diff --git a/src/api/erp/stock/stock/index.ts b/src/api/erp/stock/stock/index.ts index 6ee947b1..7ed7d128 100644 --- a/src/api/erp/stock/stock/index.ts +++ b/src/api/erp/stock/stock/index.ts @@ -1,6 +1,49 @@ import request from '@/config/axios' // ERP 产品库存 VO +export interface StockUnitStockVO { + unitId?: number + unitName?: string + count?: number + stockDisplay?: string +} + +export interface ProductStockCardVO { + skuCount?: number + stockDisplay?: string + todayFinishedInDisplay?: string +} + +export interface UnitGroupedStockCardVO { + skuCount?: number + stockDisplay?: string + unitStocks?: StockUnitStockVO[] +} + +export interface TodayInCardVO { + orderCount?: number + itemCount?: number + productDisplay?: string + materialDisplay?: string + sparePartDisplay?: string +} + +export interface TodayOutCardVO { + orderCount?: number + itemCount?: number + materialDisplay?: string + sparePartDisplay?: string + productDisplay?: string +} + +export interface StockStatisticsCardsRespVO { + productStock?: ProductStockCardVO + materialStock?: UnitGroupedStockCardVO + sparePartStock?: UnitGroupedStockCardVO + todayIn?: TodayInCardVO + todayOut?: TodayOutCardVO +} + export interface StockVO { // 编号 id: number @@ -49,6 +92,11 @@ export interface StockVO { // ERP 产品库存 API export const StockApi = { + // 查询库存统计卡片 + getStockStatisticsCards: async () => { + return await request.get({ url: `/erp/stock/statistics-cards` }) + }, + // 查询产品库存分页 getStockPage: async (params: any) => { return await request.get({ url: `/erp/stock/page`, params }) diff --git a/src/locales/en.ts b/src/locales/en.ts index eea88971..196843b3 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -416,6 +416,22 @@ export default { latestChangeTime: 'Latest Change Time', stockIn: 'Inbound', stockOut: 'Outbound', + cardLabels: { + sku: 'SKU', + stockSummary: 'Stock Summary', + todayFinishedIn: 'Completed Inbound Today', + materialSku: 'Materials', + sparePartSku: 'Spare Parts', + inboundOrders: 'Inbound Orders', + outboundOrders: 'Outbound Orders', + detailLines: 'Detail Lines', + productIn: 'Finished Goods', + materialIn: 'Raw Materials', + sparePartIn: 'Spare Parts', + materialOut: 'Raw Material Issue', + sparePartOut: 'Spare Parts Issue', + productOut: 'Finished Goods Shipment' + }, cards: { finishedGoods: { title: 'Finished Goods Stock', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index d8b82ae5..9d87b515 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -416,6 +416,22 @@ export default { latestChangeTime: '最近变动时间', stockIn: '入库', stockOut: '出库', + cardLabels: { + sku: 'SKU', + stockSummary: '库存摘要', + todayFinishedIn: '今日完工入库', + materialSku: '物料', + sparePartSku: '备件', + inboundOrders: '入库单', + outboundOrders: '出库单', + detailLines: '明细行', + productIn: '产成品', + materialIn: '原材料', + sparePartIn: '备件', + materialOut: '原材料领用', + sparePartOut: '备件领用', + productOut: '产成品发货' + }, cards: { finishedGoods: { title: '产成品库存', diff --git a/src/views/erp/stock/overview/index.vue b/src/views/erp/stock/overview/index.vue index e396886f..c4fe89e6 100644 --- a/src/views/erp/stock/overview/index.vue +++ b/src/views/erp/stock/overview/index.vue @@ -38,7 +38,7 @@ -
+
@@ -177,7 +177,7 @@ import { DICT_TYPE, getDictObj, getIntDictOptions } from '@/utils/dict' import { isHexColor } from '@/utils/color' import { formatDate } from '@/utils/formatTime' -import { StockApi, StockVO } from '@/api/erp/stock/stock' +import { StockApi, type StockStatisticsCardsRespVO, type StockVO } from '@/api/erp/stock/stock' import { StockRecordApi, StockRecordVO } from '@/api/erp/stock/record' import { WarehouseApi, WarehouseVO } from '@/api/erp/stock/warehouse' import { useDictStoreWithOut } from '@/store/modules/dict' @@ -204,10 +204,12 @@ const queryParams = reactive<{ dateRange: [] }) +const cardsLoading = ref(false) const stockLoading = ref(false) const recordLoading = ref(false) const stockList = ref([]) const recordList = ref([]) +const statisticsCards = ref() const stockTotal = ref(0) const stockQuery = reactive({ pageNo: 1, @@ -218,79 +220,78 @@ const recordQuery = reactive({ pageSize: 9 }) -const summaryCards = computed(() => [ - { - title: t('ErpStock.Overview.cards.finishedGoods.title'), - icon: 'ep:box', - color: 'blue', - lines: [ - t('ErpStock.Overview.cards.finishedGoods.line1'), - t('ErpStock.Overview.cards.finishedGoods.line2'), - t('ErpStock.Overview.cards.finishedGoods.line3') - ], - tip: t('ErpStock.Overview.cards.finishedGoods.tip') - }, - { - title: t('ErpStock.Overview.cards.rawMaterial.title'), - icon: 'ep:collection', - color: 'green', - lines: [ - t('ErpStock.Overview.cards.rawMaterial.line1'), - t('ErpStock.Overview.cards.rawMaterial.line2'), - t('ErpStock.Overview.cards.rawMaterial.line3'), - t('ErpStock.Overview.cards.rawMaterial.line4') - ], - tip: t('ErpStock.Overview.cards.rawMaterial.tip') - }, - { - title: t('ErpStock.Overview.cards.sparePart.title'), - icon: 'ep:setting', - color: 'orange', - lines: [ - t('ErpStock.Overview.cards.sparePart.line1'), - t('ErpStock.Overview.cards.sparePart.line2'), - t('ErpStock.Overview.cards.sparePart.line3'), - t('ErpStock.Overview.cards.sparePart.line4') - ], - tip: t('ErpStock.Overview.cards.sparePart.tip') - }, - { - title: t('ErpStock.Overview.cards.todayIn.title'), - icon: 'ep:download', - color: 'blue', - lines: [ - t('ErpStock.Overview.cards.todayIn.line1'), - t('ErpStock.Overview.cards.todayIn.line2'), - t('ErpStock.Overview.cards.todayIn.line3'), - t('ErpStock.Overview.cards.todayIn.line4'), - t('ErpStock.Overview.cards.todayIn.line5') - ] - }, - { - title: t('ErpStock.Overview.cards.todayOut.title'), - icon: 'ep:upload', - color: 'green', - lines: [ - t('ErpStock.Overview.cards.todayOut.line1'), - t('ErpStock.Overview.cards.todayOut.line2'), - t('ErpStock.Overview.cards.todayOut.line3'), - t('ErpStock.Overview.cards.todayOut.line4'), - t('ErpStock.Overview.cards.todayOut.line5') - ] - }, - { - title: t('ErpStock.Overview.cards.warning.title'), - icon: 'ep:warning-filled', - color: 'red', - lines: [ - t('ErpStock.Overview.cards.warning.line1'), - t('ErpStock.Overview.cards.warning.line2'), - t('ErpStock.Overview.cards.warning.line3'), - t('ErpStock.Overview.cards.warning.line4') - ] - } -]) +const displayValue = (value: unknown) => { + return value === undefined || value === null || value === '' ? '-' : value +} +const formatCardLine = (label: string, value: unknown) => `${label}: ${displayValue(value)}` + +const summaryCards = computed(() => { + const data: StockStatisticsCardsRespVO = statisticsCards.value || {} + const productStock = data.productStock || {} + const materialStock = data.materialStock || {} + const sparePartStock = data.sparePartStock || {} + const todayIn = data.todayIn || {} + const todayOut = data.todayOut || {} + + return [ + { + title: t('ErpStock.Overview.cards.finishedGoods.title'), + icon: 'ep:box', + color: 'blue', + lines: [ + formatCardLine(t('ErpStock.Overview.cardLabels.sku'), productStock.skuCount), + formatCardLine(t('ErpStock.Overview.cardLabels.stockSummary'), productStock.stockDisplay), + formatCardLine(t('ErpStock.Overview.cardLabels.todayFinishedIn'), productStock.todayFinishedInDisplay) + ], + tip: t('ErpStock.Overview.cards.finishedGoods.tip') + }, + { + title: t('ErpStock.Overview.cards.rawMaterial.title'), + icon: 'ep:collection', + color: 'green', + lines: [ + formatCardLine(t('ErpStock.Overview.cardLabels.materialSku'), materialStock.skuCount), + formatCardLine(t('ErpStock.Overview.cardLabels.stockSummary'), materialStock.stockDisplay) + ], + tip: t('ErpStock.Overview.cards.rawMaterial.tip') + }, + { + title: t('ErpStock.Overview.cards.sparePart.title'), + icon: 'ep:setting', + color: 'orange', + lines: [ + formatCardLine(t('ErpStock.Overview.cardLabels.sparePartSku'), sparePartStock.skuCount), + formatCardLine(t('ErpStock.Overview.cardLabels.stockSummary'), sparePartStock.stockDisplay) + ], + tip: t('ErpStock.Overview.cards.sparePart.tip') + }, + { + title: t('ErpStock.Overview.cards.todayIn.title'), + icon: 'ep:download', + color: 'blue', + lines: [ + formatCardLine(t('ErpStock.Overview.cardLabels.inboundOrders'), todayIn.orderCount), + formatCardLine(t('ErpStock.Overview.cardLabels.detailLines'), todayIn.itemCount), + formatCardLine(t('ErpStock.Overview.cardLabels.productIn'), todayIn.productDisplay), + formatCardLine(t('ErpStock.Overview.cardLabels.materialIn'), todayIn.materialDisplay), + formatCardLine(t('ErpStock.Overview.cardLabels.sparePartIn'), todayIn.sparePartDisplay) + ] + }, + { + title: t('ErpStock.Overview.cards.todayOut.title'), + icon: 'ep:upload', + color: 'green', + lines: [ + formatCardLine(t('ErpStock.Overview.cardLabels.outboundOrders'), todayOut.orderCount), + formatCardLine(t('ErpStock.Overview.cardLabels.detailLines'), todayOut.itemCount), + formatCardLine(t('ErpStock.Overview.cardLabels.materialOut'), todayOut.materialDisplay), + formatCardLine(t('ErpStock.Overview.cardLabels.sparePartOut'), todayOut.sparePartDisplay), + formatCardLine(t('ErpStock.Overview.cardLabels.productOut'), todayOut.productDisplay) + ] + } + ] +}) const areaOptions = computed(() => { const areaMap = new Map() warehouseList.value.forEach((warehouse) => { @@ -323,6 +324,14 @@ const buildBaseQueryParams = () => { }) } +const getStatisticsCards = async () => { + cardsLoading.value = true + try { + statisticsCards.value = await StockApi.getStockStatisticsCards() + } finally { + cardsLoading.value = false + } +} const getStockList = async () => { stockLoading.value = true try { @@ -357,7 +366,7 @@ const getRecordList = async () => { } const getList = async () => { - await Promise.all([getStockList(), getRecordList()]) + await Promise.all([getStatisticsCards(), getStockList(), getRecordList()]) } const handleQuery = () => { @@ -476,7 +485,7 @@ onMounted(async () => { .stock-overview__cards { display: grid; - grid-template-columns: repeat(6, minmax(0, 1fr)); + grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; margin-bottom: 15px; }