fix(wms): 修复首页汇总卡展示跳转和库存选择跨页勾选
parent
a098c201e1
commit
a1b66588ec
|
|
@ -2,57 +2,101 @@
|
|||
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictLabel } from '@vben/hooks';
|
||||
|
||||
import { Button, Card, message } from 'ant-design-vue';
|
||||
|
||||
import { getOrderSummary } from '#/api/wms/home';
|
||||
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
||||
|
||||
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
||||
|
||||
interface StatusItem {
|
||||
color: string;
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface OrderSummaryItem {
|
||||
color: string;
|
||||
routeName: string;
|
||||
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
||||
title: string;
|
||||
total?: number;
|
||||
type: number;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const orderDefinitions: OrderSummaryItem[] = [
|
||||
{
|
||||
color: '#2f7df6',
|
||||
routeName: 'WmsReceiptOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.RECEIPT,
|
||||
},
|
||||
{
|
||||
color: '#18a058',
|
||||
routeName: 'WmsShipmentOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.SHIPMENT,
|
||||
},
|
||||
{
|
||||
color: '#f59e0b',
|
||||
routeName: 'WmsMovementOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.MOVEMENT,
|
||||
},
|
||||
{
|
||||
color: '#7c3aed',
|
||||
routeName: 'WmsCheckOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.CHECK,
|
||||
},
|
||||
];
|
||||
|
||||
const statusList = [
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿', value: OrderStatusEnum.PREPARE },
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成', value: OrderStatusEnum.FINISHED },
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废', value: OrderStatusEnum.CANCELED },
|
||||
const statusList: StatusItem[] = [
|
||||
{
|
||||
color: '#409eff',
|
||||
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 summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
||||
|
||||
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) {
|
||||
|
|
@ -77,23 +121,49 @@ defineExpose({ load });
|
|||
|
||||
<template>
|
||||
<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"
|
||||
: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}` }"
|
||||
>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<span class="font-semibold">{{ item.title }}</span>
|
||||
<span v-if="loading" class="text-xs text-muted-foreground">加载中</span>
|
||||
<div class="flex items-center gap-2 font-semibold">
|
||||
<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 class="mb-3 text-3xl font-bold">{{ item.total }}</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">{{ getStatusCount(item, status.value) }}</div>
|
||||
<div class="font-semibold" :style="{ color: status.color }">
|
||||
{{ getStatusCount(item, status.value).toLocaleString() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -2,57 +2,101 @@
|
|||
import type { WmsHomeStatisticsApi } from '#/api/wms/home';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictLabel } from '@vben/hooks';
|
||||
|
||||
import { ElButton, ElCard, ElMessage, ElSkeleton } from 'element-plus';
|
||||
|
||||
import { getOrderSummary } from '#/api/wms/home';
|
||||
import { OrderStatusEnum, OrderTypeEnum } from '#/views/wms/utils/constants';
|
||||
|
||||
defineOptions({ name: 'WmsHomeOrderSummaryCards' });
|
||||
|
||||
interface StatusItem {
|
||||
color: string;
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface OrderSummaryItem {
|
||||
color: string;
|
||||
routeName: string;
|
||||
statuses?: WmsHomeStatisticsApi.OrderStatus[];
|
||||
title: string;
|
||||
total?: number;
|
||||
type: number;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const orderDefinitions: OrderSummaryItem[] = [
|
||||
{
|
||||
color: '#2f7df6',
|
||||
routeName: 'WmsReceiptOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.RECEIPT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.RECEIPT,
|
||||
},
|
||||
{
|
||||
color: '#18a058',
|
||||
routeName: 'WmsShipmentOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.SHIPMENT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.SHIPMENT,
|
||||
},
|
||||
{
|
||||
color: '#f59e0b',
|
||||
routeName: 'WmsMovementOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.MOVEMENT).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.MOVEMENT,
|
||||
},
|
||||
{
|
||||
color: '#7c3aed',
|
||||
routeName: 'WmsCheckOrder',
|
||||
title: getDictLabel(DICT_TYPE.WMS_ORDER_TYPE, OrderTypeEnum.CHECK).replace(/单$/, ''),
|
||||
type: OrderTypeEnum.CHECK,
|
||||
},
|
||||
];
|
||||
|
||||
const statusList = [
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.PREPARE) || '草稿', value: OrderStatusEnum.PREPARE },
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.FINISHED) || '已完成', value: OrderStatusEnum.FINISHED },
|
||||
{ label: getDictLabel(DICT_TYPE.WMS_ORDER_STATUS, OrderStatusEnum.CANCELED) || '已作废', value: OrderStatusEnum.CANCELED },
|
||||
const statusList: StatusItem[] = [
|
||||
{
|
||||
color: '#409eff',
|
||||
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 summaryList = ref<OrderSummaryItem[]>(orderDefinitions);
|
||||
|
||||
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) {
|
||||
|
|
@ -77,23 +121,56 @@ defineExpose({ load });
|
|||
|
||||
<template>
|
||||
<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"
|
||||
: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}` }"
|
||||
>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<span class="font-semibold">{{ item.title }}</span>
|
||||
<span v-if="loading" class="text-xs text-muted-foreground">加载中</span>
|
||||
</div>
|
||||
<div class="mb-3 text-3xl font-bold">{{ item.total }}</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">{{ getStatusCount(item, status.value) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ElSkeleton :loading="loading" animated :rows="4">
|
||||
<template #default>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 font-semibold">
|
||||
<span
|
||||
class="h-2 w-2 rounded-full"
|
||||
:style="{ backgroundColor: item.color }"
|
||||
></span>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<ElButton link type="primary" @click="handleNavigate(item.routeName)">
|
||||
查看
|
||||
</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>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Reference in New Issue