1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<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>