| | |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Button, DatePicker, Input, InputNumber, message } from 'ant-design-vue'; |
| | | import type { TableColumnsType } from 'ant-design-vue'; |
| | | import { Button, DatePicker, Input, InputNumber, message, Table } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createPurchasePlan, |
| | | getPurchasePlan, |
| | | getStockAlertSuggestItems, |
| | | updatePurchasePlan, |
| | | } from '#/api/erp/purchase/plan'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | import { DEMAND_SOURCE, useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<ErpPurchasePlanApi.PurchasePlan>(); |
| | | const formType = ref<FormType>('create'); |
| | | const items = ref<ErpPurchasePlanApi.PurchasePlanItem[]>([]); |
| | | const generating = ref(false); // 正在按库存预警生成建议明细 |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | |
| | | } else { |
| | | return '采购计划详情'; |
| | | } |
| | | }); |
| | | |
| | | const totalCount = computed(() => |
| | | items.value.reduce((sum, item) => sum + (item.count || 0), 0), |
| | | ); |
| | | |
| | | const itemColumns = computed<TableColumnsType>(() => { |
| | | const columns: TableColumnsType = [ |
| | | { title: '序号', dataIndex: 'index', width: 50, align: 'center' }, |
| | | { title: '产品', dataIndex: 'productId', minWidth: 200 }, |
| | | { title: '单位', dataIndex: 'productUnitName', width: 90, align: 'center' }, |
| | | { title: '数量', dataIndex: 'count', width: 130, align: 'right' }, |
| | | { title: '需求日期', dataIndex: 'demandTime', width: 160 }, |
| | | { title: '备注', dataIndex: 'remark', minWidth: 160 }, |
| | | ]; |
| | | if (formType.value !== 'detail') { |
| | | columns.push({ |
| | | title: '操作', |
| | | dataIndex: 'action', |
| | | width: 70, |
| | | align: 'center', |
| | | fixed: 'right', |
| | | }); |
| | | } |
| | | return columns; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | |
| | | productUnitId: undefined as unknown as number, |
| | | productUnitName: '', |
| | | count: 1, |
| | | demandTime: undefined, |
| | | demandTime: dayjs().format('YYYY-MM-DD'), |
| | | remark: '', |
| | | }); |
| | | } |
| | |
| | | row.productName = item?.name; |
| | | row.productUnitId = item?.unitMeasureId; |
| | | row.productUnitName = item?.unitMeasureName; |
| | | } |
| | | |
| | | /** 按库存预警生成建议明细并回填 */ |
| | | async function handleGenerateSuggest() { |
| | | if (generating.value) { |
| | | return; |
| | | } |
| | | generating.value = true; |
| | | try { |
| | | const suggestItems = await getStockAlertSuggestItems(); |
| | | if (!suggestItems || suggestItems.length === 0) { |
| | | message.warning('当前没有需要补货的物料,建议采购量为空'); |
| | | return; |
| | | } |
| | | items.value = suggestItems.map((item) => ({ ...item })); |
| | | await formApi.setFieldValue('demandSource', DEMAND_SOURCE.STOCK_ALERT); |
| | | message.success(`已生成 ${suggestItems.length} 条建议明细,请核对后保存`); |
| | | } finally { |
| | | generating.value = false; |
| | | } |
| | | } |
| | | |
| | | /** 创建或更新采购计划 */ |
| | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-3/4" |
| | | class="w-[1100px]" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3"> |
| | | <template #items> |
| | | <div class="mt-2"> |
| | | <div v-if="formType !== 'detail'" class="mb-2"> |
| | | <Button type="dashed" @click="handleAddItem">+ 添加明细</Button> |
| | | <div class="mb-2 flex items-center justify-between"> |
| | | <span class="text-sm font-medium text-muted-foreground"> |
| | | 计划明细(共 {{ items.length }} 条,合计数量 {{ totalCount }}) |
| | | </span> |
| | | <div v-if="formType !== 'detail'" class="flex space-x-2"> |
| | | <Button |
| | | type="primary" |
| | | ghost |
| | | :loading="generating" |
| | | @click="handleGenerateSuggest" |
| | | > |
| | | 按库存预警生成建议明细 |
| | | </Button> |
| | | <Button type="dashed" @click="handleAddItem">+ 添加明细</Button> |
| | | </div> |
| | | </div> |
| | | <table class="w-full border-collapse border border-gray-200 text-sm"> |
| | | <thead> |
| | | <tr class="bg-gray-100"> |
| | | <th class="border border-gray-200 px-2 py-1 w-12">序号</th> |
| | | <th class="border border-gray-200 px-2 py-1">产品</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-20">单位</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-32">数量</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-40">需求日期</th> |
| | | <th class="border border-gray-200 px-2 py-1">备注</th> |
| | | <th |
| | | <Table |
| | | :row-key="(record, index) => record.id ?? index ?? 0" |
| | | :columns="itemColumns" |
| | | :data-source="items" |
| | | :pagination="false" |
| | | :locale="{ emptyText: '暂无明细,请添加' }" |
| | | :scroll="{ x: 800, y: 320 }" |
| | | size="small" |
| | | bordered |
| | | > |
| | | <template #bodyCell="{ column, record, index }"> |
| | | <!-- 序号 --> |
| | | <template v-if="column.dataIndex === 'index'"> |
| | | <span class="block text-center">{{ index + 1 }}</span> |
| | | </template> |
| | | <!-- 产品 --> |
| | | <template v-else-if="column.dataIndex === 'productId'"> |
| | | <MdmItemSelect |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 w-16" |
| | | > |
| | | 操作 |
| | | </th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="(item, index) in items" :key="index"> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ index + 1 }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <MdmItemSelect |
| | | v-if="formType !== 'detail'" |
| | | :model-value="item.productId" |
| | | placeholder="请选择产品" |
| | | @change="handleItemSelect(index, $event)" |
| | | /> |
| | | <span v-else>{{ item.productName }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ item.productUnitName || '-' }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <InputNumber |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.count" |
| | | class="!w-full" |
| | | :min="0.01" |
| | | :precision="2" |
| | | /> |
| | | <span v-else>{{ item.count }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <DatePicker |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.demandTime" |
| | | class="!w-full" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | <span v-else>{{ item.demandTime || '-' }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <Input |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.remark" |
| | | placeholder="备注" |
| | | /> |
| | | <span v-else>{{ item.remark || '-' }}</span> |
| | | </td> |
| | | <td |
| | | :model-value="record.productId" |
| | | placeholder="请选择产品" |
| | | @change="handleItemSelect(index, $event)" |
| | | /> |
| | | <span v-else>{{ record.productName || '-' }}</span> |
| | | </template> |
| | | <!-- 单位 --> |
| | | <template v-else-if="column.dataIndex === 'productUnitName'"> |
| | | <span>{{ record.productUnitName || '-' }}</span> |
| | | </template> |
| | | <!-- 数量 --> |
| | | <template v-else-if="column.dataIndex === 'count'"> |
| | | <InputNumber |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 text-center" |
| | | > |
| | | <Button type="link" danger @click="handleRemoveItem(index)"> |
| | | 删除 |
| | | </Button> |
| | | </td> |
| | | </tr> |
| | | <tr v-if="!items.length"> |
| | | <td |
| | | :colspan="formType === 'detail' ? 6 : 7" |
| | | class="border border-gray-200 px-2 py-4 text-center text-gray-400" |
| | | > |
| | | 暂无明细,请添加 |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | v-model:value="record.count" |
| | | class="!w-full" |
| | | :min="0.01" |
| | | :precision="2" |
| | | /> |
| | | <span v-else>{{ record.count }}</span> |
| | | </template> |
| | | <!-- 需求日期 --> |
| | | <template v-else-if="column.dataIndex === 'demandTime'"> |
| | | <DatePicker |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="record.demandTime" |
| | | class="!w-full" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | <span v-else>{{ record.demandTime || '-' }}</span> |
| | | </template> |
| | | <!-- 备注 --> |
| | | <template v-else-if="column.dataIndex === 'remark'"> |
| | | <Input |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="record.remark" |
| | | placeholder="备注" |
| | | /> |
| | | <span v-else>{{ record.remark || '-' }}</span> |
| | | </template> |
| | | <!-- 操作 --> |
| | | <template v-else-if="column.dataIndex === 'action'"> |
| | | <Button type="link" danger @click="handleRemoveItem(index)"> |
| | | 删除 |
| | | </Button> |
| | | </template> |
| | | </template> |
| | | </Table> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | </Modal> |
| | | </template> |
| | | </template> |