<script lang="ts" setup>
|
import type { MdmItemModelApi } from '#/api/mdm/item-model';
|
|
import { ref } from 'vue';
|
|
import {
|
Button,
|
Divider,
|
Empty,
|
Input,
|
InputNumber,
|
Popconfirm,
|
Select,
|
Spin,
|
Table,
|
} from 'ant-design-vue';
|
|
import {
|
getItemModelListByItem,
|
saveItemModelList,
|
} from '#/api/mdm/item-model';
|
import { getUnitPage } from '#/api/mdm/unit';
|
|
/** 行内编辑需要稳定的行标识,故在接口数据上补一个本地 _key */
|
type ModelRow = MdmItemModelApi.ItemModel & { _key: number };
|
|
const rows = ref<ModelRow[]>([]);
|
const units = ref<{ label: string; value: number }[]>([]);
|
const loading = ref(false);
|
let rowKey = 0;
|
|
const columns = [
|
{ title: '型号名称', dataIndex: 'model', width: 200 },
|
{ title: '型号计量单位', dataIndex: 'unitMeasureId', width: 170 },
|
{ title: '换算比率', dataIndex: 'unitMeasureRate', width: 200 },
|
{ title: '备注', dataIndex: 'remark' },
|
{ title: '操作', dataIndex: 'action', width: 70, align: 'center' as const },
|
];
|
|
/** 加载计量单位下拉选项 */
|
async function loadUnits() {
|
if (units.value.length > 0) {
|
return;
|
}
|
const res = await getUnitPage({ pageNo: 1, pageSize: 100, status: 0 });
|
units.value = (res.list || []).map((unit) => ({
|
label: unit.name,
|
value: unit.id!,
|
}));
|
}
|
|
/** 加载指定物料已配置的型号 */
|
async function load(itemId?: number) {
|
if (!itemId) {
|
reset();
|
return;
|
}
|
loading.value = true;
|
try {
|
await loadUnits();
|
const list = await getItemModelListByItem(itemId);
|
rows.value = (list || []).map((model) => ({
|
...model,
|
_key: ++rowKey,
|
}));
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
/** 新增一行型号 */
|
async function handleAdd() {
|
await loadUnits();
|
rows.value.push({
|
_key: ++rowKey,
|
model: '',
|
unitMeasureId: undefined,
|
unitMeasureRate: undefined,
|
status: 0,
|
remark: '',
|
});
|
}
|
|
/** 删除某行型号 */
|
function handleRemove(key: number) {
|
rows.value = rows.value.filter((row) => row._key !== key);
|
}
|
|
/** 清空 */
|
function reset() {
|
rows.value = [];
|
}
|
|
/** 型号计量单位变化时,同步单位名称 */
|
function handleUnitChange(row: ModelRow, value: unknown) {
|
row.unitMeasureName = units.value.find((u) => u.value === value)?.label;
|
}
|
|
/** 单位下拉支持按名称搜索 */
|
function filterUnitOption(input: string, option: { label?: string }) {
|
return String(option?.label ?? '')
|
.toLowerCase()
|
.includes(input.toLowerCase());
|
}
|
|
/** 校验型号列表,返回第一处问题;无问题返回 undefined */
|
function validate(): string | undefined {
|
const names = new Set<string>();
|
for (const [index, row] of rows.value.entries()) {
|
const label = `第 ${index + 1} 行`;
|
const name = row.model?.trim();
|
if (!name) {
|
return `${label}的型号名称不能为空,请填写型号名称`;
|
}
|
if (names.has(name)) {
|
return `${label}的型号名称【${name}】与前面填写重复,同一物料下型号名称不能重名,请修改`;
|
}
|
names.add(name);
|
if (row.unitMeasureId == null) {
|
return `${label}型号【${name}】还未选择型号计量单位,请选择该型号实际收发货使用的单位`;
|
}
|
if (row.unitMeasureRate == null || row.unitMeasureRate <= 0) {
|
return `${label}型号【${name}】的换算比率未填写或不大于 0,请填写 1 个型号计量单位对应多少个物料主单位`;
|
}
|
}
|
return undefined;
|
}
|
|
/** 全量覆盖保存型号列表 */
|
async function save(itemId: number) {
|
const payload = rows.value.map((row) => ({
|
id: row.id,
|
itemId,
|
model: row.model!.trim(),
|
unitMeasureId: row.unitMeasureId,
|
unitMeasureRate: row.unitMeasureRate,
|
status: row.status ?? 0,
|
remark: row.remark,
|
}));
|
await saveItemModelList(itemId, payload);
|
}
|
|
defineExpose({ load, reset, validate, save });
|
</script>
|
|
<template>
|
<div class="mx-4 mt-2">
|
<Divider orientation="left">型号配置(主辅单位换算)</Divider>
|
<p class="mb-3 text-xs text-gray-400">
|
同一物料不同型号可使用不同的计量单位与换算比率。换算比率含义:1 个型号计量单位 = N 个物料主单位(例:型号
|
「4米/根」的计量单位填「根」、换算比率填 4,表示 1 根 = 4 米)。出入库时可按型号选择这些单位录入,系统自动换算为物料主单位数量记账。
|
</p>
|
<Button class="mb-3" type="primary" @click="handleAdd">添加型号</Button>
|
<Spin :spinning="loading">
|
<div>
|
<Table
|
v-if="rows.length > 0"
|
:columns="columns"
|
:data-source="rows"
|
:pagination="false"
|
row-key="_key"
|
size="small"
|
bordered
|
>
|
<template #bodyCell="{ column, record }">
|
<template v-if="column.dataIndex === 'model'">
|
<Input
|
v-model:value="record.model"
|
:maxlength="128"
|
placeholder="如:4米/根"
|
/>
|
</template>
|
<template v-else-if="column.dataIndex === 'unitMeasureId'">
|
<Select
|
v-model:value="record.unitMeasureId"
|
:filter-option="filterUnitOption"
|
:options="units"
|
allow-clear
|
class="w-full"
|
placeholder="如:根"
|
show-search
|
@change="(value) => handleUnitChange(record, value)"
|
/>
|
</template>
|
<template v-else-if="column.dataIndex === 'unitMeasureRate'">
|
<InputNumber
|
v-model:value="record.unitMeasureRate"
|
:min="0"
|
:precision="6"
|
class="!w-full"
|
placeholder="1 型号单位 = N 主单位"
|
/>
|
</template>
|
<template v-else-if="column.dataIndex === 'remark'">
|
<Input v-model:value="record.remark" :maxlength="500" />
|
</template>
|
<template v-else-if="column.dataIndex === 'action'">
|
<Popconfirm
|
title="确认删除该型号吗?"
|
@confirm="handleRemove(record._key)"
|
>
|
<Button danger size="small" type="link">删除</Button>
|
</Popconfirm>
|
</template>
|
</template>
|
</Table>
|
<Empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无型号,如需按辅单位(如根、袋、箱)收发货,请添加型号" />
|
</div>
|
</Spin>
|
</div>
|
</template>
|