fix(wms): 修复首页汇总卡展示跳转和库存选择跨页勾选
parent
a098c201e1
commit
a1b66588ec
|
|
@ -2,57 +2,101 @@
|
||||||
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictLabel } from '@vben/hooks';
|
import { getDictLabel } from '@vben/hooks';
|
||||||
|
|
||||||
|
import { Button, Card, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { getOrderSummary } from '#/api/wms/home';
|
import { getOrderSummary } from '#/api/wms/home';
|
||||||
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
||||||
|
|
||||||
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
||||||
|
|
||||||
|
interface StatusItem {
|
||||||
|
color: string;
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface OrderSummaryItem {
|
interface OrderSummaryItem {
|
||||||
color: string;
|
color: string;
|
||||||
|
routeName: string;
|
||||||
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
||||||
title: string;
|
title: string;
|
||||||
total?: number;
|
total?: number;
|
||||||
type: number;
|
type: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const orderDefinitions: OrderSummaryItem[] = [
|
const orderDefinitions: OrderSummaryItem[] = [
|
||||||
{
|
{
|
||||||
color: '#2f7df6',
|
color: '#2f7df6',
|
||||||
|
routeName: 'WmsReceiptOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.RECEIPT,
|
type: OrderTypeEnum.RECEIPT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#18a058',
|
color: '#18a058',
|
||||||
|
routeName: 'WmsShipmentOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.SHIPMENT,
|
type: OrderTypeEnum.SHIPMENT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#f59e0b',
|
color: '#f59e0b',
|
||||||
|
routeName: 'WmsMovementOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.MOVEMENT,
|
type: OrderTypeEnum.MOVEMENT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#7c3aed',
|
color: '#7c3aed',
|
||||||
|
routeName: 'WmsCheckOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.CHECK,
|
type: OrderTypeEnum.CHECK,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusList = [
|
const statusList: StatusItem[] = [
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿', value: OrderStatusEnum.PREPARE },
|
{
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成', value: OrderStatusEnum.FINISHED },
|
color: '#409eff',
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废', value: OrderStatusEnum.CANCELED },
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿',
|
||||||
|
value: OrderStatusEnum.PREPARE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#67c23a',
|
||||||
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成',
|
||||||
|
value: OrderStatusEnum.FINISHED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#909399',
|
||||||
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废',
|
||||||
|
value: OrderStatusEnum.CANCELED,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
const summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
||||||
|
|
||||||
function getStatusCount(item: OrderSummaryItem, status: number) {
|
function getStatusCount(item: OrderSummaryItem, status: number) {
|
||||||
return item.statuses?.find((row) => row.status === status)?.count;
|
return item.statuses?.find((row) => row.status === status)?.count ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusPercent(item: OrderSummaryItem, status: number) {
|
||||||
|
const total = item.total ?? 0;
|
||||||
|
if (total <= 0) {
|
||||||
|
return '0%';
|
||||||
|
}
|
||||||
|
return `${(getStatusCount(item, status) / total) * 100}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleNavigate(routeName: string) {
|
||||||
|
try {
|
||||||
|
await router.push({ name: routeName });
|
||||||
|
} catch {
|
||||||
|
message.warning('当前菜单尚未加载,请从左侧菜单进入对应页面');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load(warehouseId?: number) {
|
async function load(warehouseId?: number) {
|
||||||
|
|
@ -77,23 +121,49 @@ defineExpose({ load });
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-4 grid grid-cols-4 gap-4 max-xl:grid-cols-2 max-sm:grid-cols-1">
|
<div class="mb-4 grid grid-cols-4 gap-4 max-xl:grid-cols-2 max-sm:grid-cols-1">
|
||||||
<div
|
<Card
|
||||||
v-for="item in summaryList"
|
v-for="item in summaryList"
|
||||||
:key="item.type"
|
:key="item.type"
|
||||||
class="rounded border bg-card p-4 shadow-sm"
|
:body-style="{ padding: '16px' }"
|
||||||
|
class="h-full shadow-sm"
|
||||||
|
:loading="loading"
|
||||||
:style="{ borderTop: `3px solid ${item.color}` }"
|
:style="{ borderTop: `3px solid ${item.color}` }"
|
||||||
>
|
>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<div class="mb-3 flex items-center justify-between">
|
||||||
<span class="font-semibold">{{ item.title }}</span>
|
<div class="flex items-center gap-2 font-semibold">
|
||||||
<span v-if="loading" class="text-xs text-muted-foreground">加载中</span>
|
<span class="h-2 w-2 rounded-full" :style="{ backgroundColor: item.color }"></span>
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
<Button size="small" type="link" @click="handleNavigate(item.routeName)">
|
||||||
|
查看
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="mb-1 flex items-baseline gap-2">
|
||||||
|
<span class="text-muted-foreground">合计</span>
|
||||||
|
<span class="text-3xl font-bold leading-9">
|
||||||
|
{{ item.total?.toLocaleString() ?? 0 }}
|
||||||
|
</span>
|
||||||
|
<span class="text-muted-foreground">单</span>
|
||||||
|
</div>
|
||||||
|
<div class="my-3 flex h-2 overflow-hidden rounded-full bg-muted">
|
||||||
|
<span
|
||||||
|
v-for="status in statusList"
|
||||||
|
:key="status.value"
|
||||||
|
class="h-full"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: status.color,
|
||||||
|
width: getStatusPercent(item, status.value),
|
||||||
|
}"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 text-3xl font-bold">{{ item.total }}</div>
|
|
||||||
<div class="grid grid-cols-3 gap-2 text-sm">
|
<div class="grid grid-cols-3 gap-2 text-sm">
|
||||||
<div v-for="status in statusList" :key="status.value">
|
<div v-for="status in statusList" :key="status.value">
|
||||||
<div class="truncate text-muted-foreground">{{ status.label }}</div>
|
<div class="truncate text-muted-foreground">{{ status.label }}</div>
|
||||||
<div class="font-semibold">{{ getStatusCount(item, status.value) }}</div>
|
<div class="font-semibold" :style="{ color: status.color }">
|
||||||
|
{{ getStatusCount(item, status.value).toLocaleString() }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -2,57 +2,101 @@
|
||||||
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictLabel } from '@vben/hooks';
|
import { getDictLabel } from '@vben/hooks';
|
||||||
|
|
||||||
|
import { ElButton, ElCard, ElMessage, ElSkeleton } from 'element-plus';
|
||||||
|
|
||||||
import { getOrderSummary } from '#/api/wms/home';
|
import { getOrderSummary } from '#/api/wms/home';
|
||||||
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
||||||
|
|
||||||
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
||||||
|
|
||||||
|
interface StatusItem {
|
||||||
|
color: string;
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface OrderSummaryItem {
|
interface OrderSummaryItem {
|
||||||
color: string;
|
color: string;
|
||||||
|
routeName: string;
|
||||||
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
||||||
title: string;
|
title: string;
|
||||||
total?: number;
|
total?: number;
|
||||||
type: number;
|
type: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const orderDefinitions: OrderSummaryItem[] = [
|
const orderDefinitions: OrderSummaryItem[] = [
|
||||||
{
|
{
|
||||||
color: '#2f7df6',
|
color: '#2f7df6',
|
||||||
|
routeName: 'WmsReceiptOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.RECEIPT,
|
type: OrderTypeEnum.RECEIPT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#18a058',
|
color: '#18a058',
|
||||||
|
routeName: 'WmsShipmentOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.SHIPMENT,
|
type: OrderTypeEnum.SHIPMENT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#f59e0b',
|
color: '#f59e0b',
|
||||||
|
routeName: 'WmsMovementOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.MOVEMENT,
|
type: OrderTypeEnum.MOVEMENT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color: '#7c3aed',
|
color: '#7c3aed',
|
||||||
|
routeName: 'WmsCheckOrder',
|
||||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
||||||
type: OrderTypeEnum.CHECK,
|
type: OrderTypeEnum.CHECK,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusList = [
|
const statusList: StatusItem[] = [
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿', value: OrderStatusEnum.PREPARE },
|
{
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成', value: OrderStatusEnum.FINISHED },
|
color: '#409eff',
|
||||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废', value: OrderStatusEnum.CANCELED },
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿',
|
||||||
|
value: OrderStatusEnum.PREPARE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#67c23a',
|
||||||
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成',
|
||||||
|
value: OrderStatusEnum.FINISHED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#909399',
|
||||||
|
label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废',
|
||||||
|
value: OrderStatusEnum.CANCELED,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
const summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
||||||
|
|
||||||
function getStatusCount(item: OrderSummaryItem, status: number) {
|
function getStatusCount(item: OrderSummaryItem, status: number) {
|
||||||
return item.statuses?.find((row) => row.status === status)?.count;
|
return item.statuses?.find((row) => row.status === status)?.count ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusPercent(item: OrderSummaryItem, status: number) {
|
||||||
|
const total = item.total ?? 0;
|
||||||
|
if (total <= 0) {
|
||||||
|
return '0%';
|
||||||
|
}
|
||||||
|
return `${(getStatusCount(item, status) / total) * 100}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleNavigate(routeName: string) {
|
||||||
|
try {
|
||||||
|
await router.push({ name: routeName });
|
||||||
|
} catch {
|
||||||
|
ElMessage.warning('当前菜单尚未加载,请从左侧菜单进入对应页面');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load(warehouseId?: number) {
|
async function load(warehouseId?: number) {
|
||||||
|
|
@ -77,23 +121,56 @@ defineExpose({ load });
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-4 grid grid-cols-4 gap-4 max-xl:grid-cols-2 max-sm:grid-cols-1">
|
<div class="mb-4 grid grid-cols-4 gap-4 max-xl:grid-cols-2 max-sm:grid-cols-1">
|
||||||
<div
|
<ElCard
|
||||||
v-for="item in summaryList"
|
v-for="item in summaryList"
|
||||||
:key="item.type"
|
:key="item.type"
|
||||||
class="rounded border bg-card p-4 shadow-sm"
|
:body-style="{ padding: '16px' }"
|
||||||
|
class="h-full"
|
||||||
|
shadow="never"
|
||||||
:style="{ borderTop: `3px solid ${item.color}` }"
|
:style="{ borderTop: `3px solid ${item.color}` }"
|
||||||
>
|
>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<ElSkeleton :loading="loading" animated :rows="4">
|
||||||
<span class="font-semibold">{{ item.title }}</span>
|
<template #default>
|
||||||
<span v-if="loading" class="text-xs text-muted-foreground">加载中</span>
|
<div class="mb-3 flex items-center justify-between">
|
||||||
</div>
|
<div class="flex items-center gap-2 font-semibold">
|
||||||
<div class="mb-3 text-3xl font-bold">{{ item.total }}</div>
|
<span
|
||||||
<div class="grid grid-cols-3 gap-2 text-sm">
|
class="h-2 w-2 rounded-full"
|
||||||
<div v-for="status in statusList" :key="status.value">
|
:style="{ backgroundColor: item.color }"
|
||||||
<div class="truncate text-muted-foreground">{{ status.label }}</div>
|
></span>
|
||||||
<div class="font-semibold">{{ getStatusCount(item, status.value) }}</div>
|
{{ item.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<ElButton link type="primary" @click="handleNavigate(item.routeName)">
|
||||||
</div>
|
查看
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<div class="mb-1 flex items-baseline gap-2">
|
||||||
|
<span class="text-muted-foreground">合计</span>
|
||||||
|
<span class="text-3xl font-bold leading-9">
|
||||||
|
{{ item.total?.toLocaleString() ?? 0 }}
|
||||||
|
</span>
|
||||||
|
<span class="text-muted-foreground">单</span>
|
||||||
|
</div>
|
||||||
|
<div class="my-3 flex h-2 overflow-hidden rounded-full bg-muted">
|
||||||
|
<span
|
||||||
|
v-for="status in statusList"
|
||||||
|
:key="status.value"
|
||||||
|
class="h-full"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: status.color,
|
||||||
|
width: getStatusPercent(item, status.value),
|
||||||
|
}"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-2 text-sm">
|
||||||
|
<div v-for="status in statusList" :key="status.value">
|
||||||
|
<div class="truncate text-muted-foreground">{{ status.label }}</div>
|
||||||
|
<div class="font-semibold" :style="{ color: status.color }">
|
||||||
|
{{ getStatusCount(item, status.value).toLocaleString() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ElSkeleton>
|
||||||
|
</ElCard>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue