admin-vben/apps/web-antdv-next/src/views/wms/inventory/index/index.vue

191 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts" setup>
import type { InventoryDimension } from './data';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { WmsInventoryApi } from '#/api/wms/inventory';
import { ref } from 'vue';
import { DocAlert, Page } from '@vben/common-ui';
import { Checkbox } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getInventoryPage } from '#/api/wms/inventory';
import { formatQuantity } from '#/views/wms/utils/format';
import { INVENTORY_DIMENSION, useGridColumns, useGridFormSchema } from './data';
interface InventoryRow extends WmsInventoryApi.Inventory {
skuWarehouseId?: string;
warehouseItemId?: string;
}
defineOptions({ name: 'WmsInventory' });
const currentRows = ref<InventoryRow[]>([]);
const currentDimension = ref<InventoryDimension>(INVENTORY_DIMENSION.WAREHOUSE);
const filterZero = ref(false);
/** 补充合并单元格需要的复合 key */
function buildRows(items: WmsInventoryApi.Inventory[]) {
return items.map((item) => ({
...item,
skuWarehouseId: `${item.skuId || 0}-${item.warehouseId || 0}`,
warehouseItemId: `${item.warehouseId || 0}-${item.itemId || 0}`,
}));
}
/** 切换统计维度时同步表单、表头和查询参数 */
async function handleDimensionChange(dimension: InventoryDimension) {
// spanMethod 依赖当前维度判断合并字段,先更新本地状态。
currentDimension.value = dimension;
// 表头结构需要立即跟随维度切换,避免请求完成前仍显示旧列。
await gridApi.grid.reloadColumn(useGridColumns(dimension) || []);
// VXE proxy 查询会合并最近提交值,这里显式写入新 type 并刷新提交快照。
await gridApi.formApi.setFieldValue('type', dimension);
const formValues = await gridApi.formApi.getValues();
gridApi.formApi.setLatestSubmissionValues(formValues);
// 带上最新表单值重新加载,确保本次请求使用切换后的统计维度。
await gridApi.reload(formValues);
}
/** 切换零库存过滤时按当前搜索条件重新查询 */
function handleFilterZeroChange() {
gridApi.query();
}
/** 按列字段读取行值,兼容 VXE 的 field/property */
function getRowPropertyValue(row: InventoryRow | undefined, property: string) {
return row?.[property as keyof InventoryRow];
}
/** 根据统计维度返回需要合并单元格的字段 */
function getRowSpanProperties() {
if (currentDimension.value === INVENTORY_DIMENSION.ITEM) {
return ['itemId', 'skuId', 'skuWarehouseId'];
}
return ['warehouseId', 'warehouseItemId'];
}
/** 合并库存统计的维度单元格 */
function handleSpanMethod({
column,
rowIndex,
}: {
column: { field?: string; property?: string };
rowIndex: number;
}) {
const property = column.field || column.property;
if (!property || !getRowSpanProperties().includes(property)) {
return { colspan: 1, rowspan: 1 };
}
const row = currentRows.value[rowIndex];
if (
rowIndex > 0 &&
getRowPropertyValue(currentRows.value[rowIndex - 1], property) ===
getRowPropertyValue(row, property)
) {
return { colspan: 0, rowspan: 0 };
}
let rowspan = 1;
for (let index = rowIndex + 1; index < currentRows.value.length; index += 1) {
if (
getRowPropertyValue(currentRows.value[index], property) !==
getRowPropertyValue(row, property)
) {
break;
}
rowspan += 1;
}
return { colspan: 1, rowspan };
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(handleDimensionChange),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
// 维度切换会通过 reload(formValues) 传入新 type刷新场景用当前维度兜底。
const nextDimension = (formValues.type ||
currentDimension.value ||
INVENTORY_DIMENSION.WAREHOUSE) as InventoryDimension;
currentDimension.value = nextDimension;
await gridApi.grid.reloadColumn(useGridColumns(nextDimension) || []);
// type 是后端聚合维度参数,普通搜索参数里移除,避免重复透传旧值。
const queryParams = { ...formValues };
Reflect.deleteProperty(queryParams, 'type');
const data = await getInventoryPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...queryParams,
type: nextDimension,
onlyPositiveQuantity: filterZero.value ? true : undefined,
});
// spanMethod 依赖当前页数据计算 rowspan需要保存构造后的行。
currentRows.value = buildRows(data.list || []);
return {
...data,
list: currentRows.value,
};
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
spanMethod: handleSpanMethod,
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<InventoryRow>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【库存】库存记录、流水、统计"
url="https://doc.iocoder.cn/wms/inventory/"
/>
</template>
<Grid table-title="">
<template #toolbar-tools>
<Checkbox v-model:checked="filterZero" @change="handleFilterZeroChange">
0
</Checkbox>
</template>
<template #warehouseName="{ row }">
{{ row.warehouseName || '-' }}
</template>
<template #itemInfo="{ row }">
<div>{{ row.itemName || '-' }}</div>
<div v-if="row.itemCode" class="text-xs text-gray-500">
商品编号:{{ row.itemCode }}
</div>
</template>
<template #skuInfo="{ row }">
<div>{{ row.skuName || '-' }}</div>
<div v-if="row.skuCode" class="text-xs text-gray-500">
规格编号:{{ row.skuCode }}
</div>
</template>
<template #quantity="{ row }">
{{ formatQuantity(row.quantity) }}
</template>
</Grid>
</Page>
</template>