诚发五金:按需求收敛页面,去掉无入口的工序/库区库位强依赖
- 工单表单移除「工序」页签,不再要求先维护工序主数据
- 工作站表单去掉工序必选与工具页签
- 排产/任务列表按单工序展示,路线与工序可空
- 库区、库位选择改为可手填新建
- 仓库页去掉已无菜单的库位跳转链接
- MPS「查看工单」跳转路径修正为 /mes/pro/work-order
- 物料管理隐藏需求外的智能体导入
Co-Authored-By: Claude Code <noreply@anthropic.com>
| | |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { isAiEnable } from '@vben/hooks'; |
| | | |
| | | import { confirm, Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '#/packages/utils/src'; |
| | | |
| | |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import AgentImport from './modules/agent-import.vue'; |
| | | import Form from './modules/form.vue'; |
| | | import ImportForm from './modules/import-form.vue'; |
| | | |
| | |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: ImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [AgentImportModal, agentImportModalApi] = useVbenModal({ |
| | | connectedComponent: AgentImport, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** 智能体导入物料 */ |
| | | function handleAgentImport() { |
| | | agentImportModalApi.open(); |
| | | } |
| | | |
| | | /** 工具栏按钮:智能体导入受 AI 总开关控制 */ |
| | | /** 工具栏按钮 */ |
| | | const toolbarActions = computed(() => [ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['物料']), |
| | |
| | | auth: ['mdm:item:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | ...(isAiEnable() |
| | | ? [ |
| | | { |
| | | label: '智能体导入', |
| | | type: 'primary' as const, |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['mdm:item:import'], |
| | | onClick: handleAgentImport, |
| | | }, |
| | | ] |
| | | : []), |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary' as const, |
| | |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <AgentImportModal @success="handleRefresh" /> |
| | | <Grid table-title="物料列表"> |
| | | <template #toolbar-tools> |
| | | <TableAction :actions="toolbarActions" /> |
| | |
| | | import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; |
| | | import { getWarehouseAreaSimpleList } from '#/api/mes/wm/warehouse/area'; |
| | | import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location'; |
| | | import { ProProcessSelect } from '#/views/mes/process-design/process/components'; |
| | | |
| | | import { MdWorkshopSelect } from './components'; |
| | | |
| | |
| | | componentProps: { |
| | | placeholder: '请输入工作站地点', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'processId', |
| | | label: '所属工序', |
| | | component: markRaw(ProProcessSelect), |
| | | componentProps: { |
| | | placeholder: '请选择所属工序', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'warehouseId', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'processId', |
| | | label: '所属工序', |
| | | component: markRaw(ProProcessSelect), |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请选择工序', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '状态', |
| | | component: 'Select', |
| | |
| | | width: 140, |
| | | }, |
| | | { |
| | | field: 'processName', |
| | | title: '所属工序', |
| | | width: 140, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: '状态', |
| | | width: 120, |
| | |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请输入工作站编码', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'processId', |
| | | label: '所属工序', |
| | | component: markRaw(ProProcessSelect), |
| | | componentProps: { |
| | | placeholder: '请选择工序', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | |
| | | import { useFormSchema } from "../data"; |
| | | import MachineList from "./machine-list.vue"; |
| | | import ToolList from "./tool-list.vue"; |
| | | import WorkerList from "./worker-list.vue"; |
| | | |
| | | const emit = defineEmits(["success"]); |
| | |
| | | tab="设备资源"> |
| | | <MachineList :form-type="formType" |
| | | :workstation-id="formData.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="tool" |
| | | tab="工装夹具"> |
| | | <ToolList :form-type="formType" |
| | | :workstation-id="formData.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="worker" |
| | | tab="人力资源"> |
| | |
| | | function handleGoToWorkOrder(row: MesProMpsApi.Mps) { |
| | | if (row.workOrderId) { |
| | | router.push({ |
| | | path: '/mes/pro/workorder', |
| | | path: '/mes/pro/work-order', |
| | | query: { id: row.workOrderId }, |
| | | }); |
| | | } |
| | |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** 加载工单工序列表(仅使用工单工序,不回退到工艺路线模板) */ |
| | | /** 加载工单工序列表;未配置工序时按单工序生产处理 */ |
| | | async function loadRouteProcesses(workOrderId: number, productId: number) { |
| | | const woProcesses = await getWorkOrderProcessListByWorkOrderId(workOrderId); |
| | | if (!woProcesses || woProcesses.length === 0) { |
| | | message.warning('当前订单未配置工序,请先在工单中维护工序信息'); |
| | | return; |
| | | } |
| | | // 确定工艺路线ID:工单路线 → 产品路线 |
| | | // 确定工艺路线ID:有工序时按产品路线带出;未配置工序时按单工序生产 |
| | | let tempRouteId = 0; |
| | | if (workOrder.value?.routeId) { |
| | | tempRouteId = workOrder.value.routeId; |
| | | } else { |
| | | if (woProcesses && woProcesses.length > 0) { |
| | | const processes = await getRouteProcessListByProduct(productId); |
| | | tempRouteId = processes?.[0]?.routeId ?? 0; |
| | | } |
| | | currentRouteId.value = tempRouteId; |
| | | |
| | | routeProcessList.value = [...woProcesses].toSorted( |
| | | routeProcessList.value = [...(woProcesses ?? [])].toSorted( |
| | | (a, b) => (a.sort ?? 0) - (b.sort ?? 0), |
| | | ); |
| | | } |
| | |
| | | <Modal :title="getTitle" class="w-4/5"> |
| | | <Form class="mx-4" /> |
| | | <!-- 工序步骤导航 + 当前工序任务列表 --> |
| | | <template v-if="routeProcessList.length > 0 && workOrder?.id"> |
| | | <Steps |
| | | v-model:current="activeProcessStep" |
| | | class="my-4 px-4" |
| | | size="small" |
| | | type="navigation" |
| | | > |
| | | <Steps.Step |
| | | v-for="rp in routeProcessList" |
| | | :key="rp.processId" |
| | | :title="rp.processName" |
| | | :description="rp.paramTemplateId ? `参数模板 #${rp.paramTemplateId}` : '未配置参数模板'" |
| | | /> |
| | | </Steps> |
| | | <template v-for="(rp, index) in routeProcessList" :key="rp.processId"> |
| | | <Card |
| | | v-if="activeProcessStep === index" |
| | | class="mx-4" |
| | | <template v-if="workOrder?.id"> |
| | | <template v-if="routeProcessList.length > 0"> |
| | | <Steps |
| | | v-model:current="activeProcessStep" |
| | | class="my-4 px-4" |
| | | size="small" |
| | | type="navigation" |
| | | > |
| | | <TaskList |
| | | :color-code="rp.colorCode" |
| | | :disabled="isReadonly" |
| | | :item-id="rp.outputItemId || workOrder.productId" |
| | | :process-id="rp.processId!" |
| | | :route-id="currentRouteId" |
| | | :work-order-id="workOrder.id!" |
| | | :work-order-quantity="workOrder.quantity" |
| | | <Steps.Step |
| | | v-for="rp in routeProcessList" |
| | | :key="rp.processId" |
| | | :title="rp.processName" |
| | | :description="rp.paramTemplateId ? `参数模板 #${rp.paramTemplateId}` : '未配置参数模板'" |
| | | /> |
| | | </Card> |
| | | </Steps> |
| | | <template v-for="(rp, index) in routeProcessList" :key="rp.processId"> |
| | | <Card |
| | | v-if="activeProcessStep === index" |
| | | class="mx-4" |
| | | > |
| | | <TaskList |
| | | :color-code="rp.colorCode" |
| | | :disabled="isReadonly" |
| | | :item-id="rp.outputItemId || workOrder.productId" |
| | | :process-id="rp.processId!" |
| | | :route-id="currentRouteId" |
| | | :work-order-id="workOrder.id!" |
| | | :work-order-quantity="workOrder.quantity" |
| | | /> |
| | | </Card> |
| | | </template> |
| | | </template> |
| | | <!-- 未配置工序/工艺路线:整张工单按单工序排产 --> |
| | | <Card v-else class="mx-4"> |
| | | <TaskList |
| | | :disabled="isReadonly" |
| | | :item-id="workOrder.productId" |
| | | :route-id="currentRouteId || undefined" |
| | | :work-order-id="workOrder.id!" |
| | | :work-order-quantity="workOrder.quantity" |
| | | /> |
| | | </Card> |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <template v-if="formType === 'schedule'"> |
| | | <Popconfirm |
| | | v-if="routeProcessList.length > 0" |
| | | title="确认要完成该订单吗?完成后订单下所有任务将标记为已完成。" |
| | | @confirm="handleFinish" |
| | | > |
| | | <Button type="primary">完成</Button> |
| | | </Popconfirm> |
| | | <span v-else class="text-gray-400 text-sm"> |
| | | 当前订单未配置工序,请先在工单中维护工序信息后再完成 |
| | | </span> |
| | | </template> |
| | | </div> |
| | | </template> |
| | |
| | | colorCode?: string; |
| | | disabled?: boolean; |
| | | itemId?: number; |
| | | processId: number; |
| | | routeId: number; |
| | | processId?: number; |
| | | routeId?: number; |
| | | workOrderId: number; |
| | | workOrderQuantity?: number; |
| | | }>(); |
| | |
| | | if (!props.workOrderId) { |
| | | return { list: [], total: 0 }; |
| | | } |
| | | return await getTaskPage({ |
| | | const params: { |
| | | pageNo: number; |
| | | pageSize: number; |
| | | processId?: number; |
| | | routeId?: number; |
| | | workOrderId: number; |
| | | } = { |
| | | pageNo: 1, |
| | | pageSize: 100, |
| | | processId: props.processId, |
| | | routeId: props.routeId, |
| | | workOrderId: props.workOrderId, |
| | | }); |
| | | }; |
| | | if (props.processId != null) { |
| | | params.processId = props.processId; |
| | | } |
| | | if (props.routeId != null) { |
| | | params.routeId = props.routeId; |
| | | } |
| | | return await getTaskPage(params); |
| | | }, |
| | | }, |
| | | }, |
| | |
| | | import type { FormType } from "../data"; |
| | | |
| | | import type { MesProWorkOrderApi } from "#/api/mes/pro/workorder"; |
| | | import type { MesProWorkOrderProcessApi } from "#/api/mes/pro/workorder/process"; |
| | | |
| | | import { computed, ref } from "vue"; |
| | | |
| | |
| | | getWorkOrder, |
| | | updateWorkOrder, |
| | | } from "#/api/mes/pro/workorder"; |
| | | import { batchSaveWorkOrderProcess, getWorkOrderProcessListByWorkOrderId } from "#/api/mes/pro/workorder/process"; |
| | | import { $t } from "#/locales"; |
| | | // import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | |
| | |
| | | |
| | | import ItemList from "./item-list.vue"; |
| | | import MaterialPreview from "./material-preview.vue"; |
| | | import ProcessList from "./process-list.vue"; |
| | | |
| | | const emit = defineEmits(["success"]); |
| | | const formType = ref<FormType>("create"); |
| | | const formData = ref<MesProWorkOrderApi.WorkOrder>(); |
| | | const originalSnapshot = ref(""); |
| | | const subTabsName = ref("process"); |
| | | const subTabsName = ref("item"); |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); |
| | | const processListRef = ref<InstanceType<typeof ProcessList>>(); |
| | | const currentProductId = ref<number>(); // 当前选中的产品 ID(用于工序加载) |
| | | const tempProcessList = ref<MesProWorkOrderProcessApi.WorkOrderProcess[]>([]); // 临时工序列表 |
| | | const previewParams = ref<{ productId?: number; quantity?: number }>({}); // 辅料预览参数 |
| | | |
| | | const isEditable = computed(() => |
| | |
| | | if (fieldsChanged.includes('factoryId')) { |
| | | await formApi.setFieldValue('workshopId', undefined); |
| | | } |
| | | // 产品变更时,触发工序加载 |
| | | if (isCreateMode.value && fieldsChanged.includes('productId')) { |
| | | const newProductId = values.productId; |
| | | if (newProductId && newProductId !== currentProductId.value) { |
| | | currentProductId.value = newProductId; |
| | | } |
| | | } |
| | | // 同步辅料预览参数(产品 / 数量变化时自动重新计算辅料与库存) |
| | | if ( |
| | | fieldsChanged.includes('productId') || |
| | |
| | | data.feedbackRole = data.feedbackRole.join(','); |
| | | } |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | if (tempProcessList.value.length > 0) { |
| | | const processList = tempProcessList.value.map(item => ({ |
| | | ...item, |
| | | workOrderId: formData.value!.id, |
| | | })); |
| | | await batchSaveWorkOrderProcess(processList); |
| | | } |
| | | formData.value = { ...formData.value, ...data }; |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | |
| | | }); |
| | | } |
| | | |
| | | /** 工序列表变化回调 */ |
| | | function handleProcessListChange(list: MesProWorkOrderProcessApi.WorkOrderProcess[]) { |
| | | tempProcessList.value = list; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | // ---- 确认订单模式 ---- |
| | |
| | | originalSnapshot.value = current; |
| | | } |
| | | } |
| | | // 2. 批量保存工序(如果有) |
| | | if (tempProcessList.value.length > 0) { |
| | | const processList = tempProcessList.value.map(item => ({ |
| | | ...item, |
| | | workOrderId: formData.value!.id, |
| | | })); |
| | | await batchSaveWorkOrderProcess(processList); |
| | | } |
| | | // 3. 校验是否有工序 |
| | | const processes = await getWorkOrderProcessListByWorkOrderId(formData.value.id); |
| | | if (!processes || processes.length === 0) { |
| | | message.warning('当前订单未配置工序,请先在工单中维护工序信息后再确认'); |
| | | return; |
| | | } |
| | | // 4. 确认工单 |
| | | // 2. 确认工单(未配置工序时按单工序生产) |
| | | await confirmWorkOrder(formData.value.id); |
| | | message.success('订单已确认'); |
| | | await modalApi.close(); |
| | |
| | | // ---- 完成订单模式 ---- |
| | | if (isFinish.value) { |
| | | if (!formData.value?.id) return; |
| | | // 校验是否有工序 |
| | | const processes = await getWorkOrderProcessListByWorkOrderId(formData.value.id); |
| | | if (!processes || processes.length === 0) { |
| | | message.warning('当前订单未配置工序,请先在工单中维护工序信息后再完成'); |
| | | return; |
| | | } |
| | | try { |
| | | await confirm('确认要完成该订单吗?'); |
| | | } catch { |
| | |
| | | if (formData.value?.id) { |
| | | // 编辑模式 |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | // 批量保存工序 |
| | | if (tempProcessList.value.length > 0) { |
| | | const processList = tempProcessList.value.map(item => ({ |
| | | ...item, |
| | | workOrderId: formData.value!.id, |
| | | })); |
| | | await batchSaveWorkOrderProcess(processList); |
| | | } |
| | | formData.value = { ...formData.value, ...data }; |
| | | emit("success"); |
| | | message.success($t("ui.actionMessage.operationSuccess")); |
| | |
| | | const insufficient = await checkMaterialStock(data); |
| | | if (!insufficient) return; |
| | | const id = await createWorkOrder(data); |
| | | // 批量保存工序 |
| | | if (tempProcessList.value.length > 0) { |
| | | const processList = tempProcessList.value.map(item => ({ |
| | | ...item, |
| | | workOrderId: id, |
| | | })); |
| | | await batchSaveWorkOrderProcess(processList); |
| | | } |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | originalSnapshot.value = ""; |
| | | currentProductId.value = undefined; |
| | | tempProcessList.value = []; |
| | | previewParams.value = {}; |
| | | return; |
| | | } |
| | |
| | | parentRow?: MesProWorkOrderApi.WorkOrder; |
| | | }>(); |
| | | formType.value = data.formType; |
| | | subTabsName.value = "process"; |
| | | tempProcessList.value = []; |
| | | subTabsName.value = "item"; |
| | | applySchema(); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | |
| | | formValues.feedbackRole = formValues.feedbackRole.split(','); |
| | | } |
| | | await formApi.setValues(formValues); |
| | | currentProductId.value = formData.value.productId; |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | |
| | | class="w-3/5" |
| | | content-class="!max-h-[80vh] overflow-y-auto"> |
| | | <Form class="mx-4" /> |
| | | <!-- 工序 / 物料需求 Tab --> |
| | | <!-- 物料需求(辅料明细)--> |
| | | <template v-if="showTabs"> |
| | | <Tabs v-model:active-key="subTabsName" |
| | | class="mx-4"> |
| | | <Tabs.TabPane key="process" |
| | | tab="工序" |
| | | forceRender> |
| | | <p class="mb-3 text-xs text-gray-400 leading-relaxed"> |
| | | 新增模式会自动从所选产品的工艺路线带出工序,点击行首手柄可拖拽调整顺序;「关键工序」需重点管控、「质检工序」由质检人员检验、勾选「倒冲」的工序在报工后按投料用量自动扣减物料库存。每道工序可点「新增物料」配置投料明细。 |
| | | </p> |
| | | <ProcessList |
| | | ref="processListRef" |
| | | :form-type="formType" |
| | | :work-order="formData || {}" |
| | | :work-order-id="formData?.id" |
| | | :product-id="currentProductId" |
| | | @process-list-change="handleProcessListChange" |
| | | /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="item" |
| | | tab="物料需求"> |
| | | <p class="mb-3 text-xs text-gray-400 leading-relaxed"> |
| | | 物料需求按各工序「投料明细」×订单数量自动汇总生成,仅供查看;如需调整,请回到「工序」页签维护投料。新增工单时会按所选产品与数量预先计算辅料用量及库存情况。 |
| | | 新增工单时会按所选产品与数量预先计算辅料用量及库存情况;工单生成后,物料需求按产品用量自动汇总,仅供查看。 |
| | | </p> |
| | | <ItemList v-if="formData?.id" |
| | | :work-order-id="formData.id" /> |
| | |
| | | |
| | | import { Select, SelectOption, Tooltip } from 'ant-design-vue'; |
| | | |
| | | import { getWarehouseAreaSimpleList } from '#/api/mes/wm/warehouse/area'; |
| | | import { |
| | | createWarehouseArea, |
| | | getWarehouseAreaSimpleList, |
| | | } from '#/api/mes/wm/warehouse/area'; |
| | | |
| | | defineOptions({ name: 'WmWarehouseAreaSelect', inheritAttrs: false }); |
| | | |
| | |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | |
| | | const CREATE_VALUE = '__CREATE_AREA__'; |
| | | |
| | | const attrs = useAttrs(); |
| | | const allList = ref<MesWmWarehouseAreaApi.WarehouseArea[]>([]); |
| | | const selectedItem = ref<MesWmWarehouseAreaApi.WarehouseArea>(); |
| | | const searchText = ref(''); |
| | | |
| | | const selectValue = computed({ |
| | | get: () => props.modelValue, |
| | | set: (val) => emit('update:modelValue', val), |
| | | }); |
| | | |
| | | /** 选中变化 */ |
| | | function handleChange(val: any) { |
| | | /** 输入内容尚未存在于列表时,允许直接新建 */ |
| | | const canCreate = computed(() => { |
| | | const keyword = searchText.value.trim(); |
| | | if (!keyword || !props.locationId) { |
| | | return false; |
| | | } |
| | | return !allList.value.some((o) => o.name === keyword); |
| | | }); |
| | | |
| | | /** 选中变化;选中“新增”项时按输入名称直接建档 */ |
| | | async function handleChange(val: any) { |
| | | if (val === CREATE_VALUE) { |
| | | await handleCreate(); |
| | | return; |
| | | } |
| | | const item = allList.value.find((o) => o.id === val); |
| | | selectedItem.value = item; |
| | | emit('change', item); |
| | | } |
| | | |
| | | /** 前端过滤(name + code) */ |
| | | /** 输入名称直接新建库位并选中 */ |
| | | async function handleCreate() { |
| | | const name = searchText.value.trim(); |
| | | if (!name || !props.locationId) { |
| | | return; |
| | | } |
| | | const id = await createWarehouseArea({ |
| | | allowBatchMixing: true, |
| | | allowItemMixing: true, |
| | | code: `KW${Date.now()}`, |
| | | frozen: false, |
| | | locationId: props.locationId, |
| | | name, |
| | | status: 0, |
| | | }); |
| | | allList.value = await getWarehouseAreaSimpleList(props.locationId); |
| | | const item = allList.value.find((o) => o.id === id); |
| | | selectedItem.value = item; |
| | | searchText.value = ''; |
| | | emit('update:modelValue', id); |
| | | emit('change', item); |
| | | } |
| | | |
| | | /** 前端过滤(name + code),始终保留“新增”项 */ |
| | | function filterOption(input: string, option: any) { |
| | | if (option.value === CREATE_VALUE) { |
| | | return true; |
| | | } |
| | | const item = allList.value.find((o) => o.id === option.value); |
| | | if (!item) { |
| | | return false; |
| | |
| | | |
| | | /** 库区变更时清空已选库位并重新加载列表 */ |
| | | watchEffect(async () => { |
| | | searchText.value = ''; |
| | | if (!props.locationId) { |
| | | allList.value = []; |
| | | return; |
| | |
| | | :popup-match-select-width="false" |
| | | show-search |
| | | @change="handleChange" |
| | | @search="searchText = $event" |
| | | > |
| | | <SelectOption v-for="item in allList" :key="item.id" :value="item.id"> |
| | | {{ item.name }} |
| | | </SelectOption> |
| | | <SelectOption v-if="canCreate" :value="CREATE_VALUE"> |
| | | 新增库位“{{ searchText.trim() }}” |
| | | </SelectOption> |
| | | </Select> |
| | | </Tooltip> |
| | | </template> |
| | |
| | | |
| | | import { Select, SelectOption, Tooltip } from 'ant-design-vue'; |
| | | |
| | | import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location'; |
| | | import { |
| | | createWarehouseLocation, |
| | | getWarehouseLocationSimpleList, |
| | | } from '#/api/mes/wm/warehouse/location'; |
| | | |
| | | defineOptions({ name: 'WmWarehouseLocationSelect', inheritAttrs: false }); |
| | | |
| | |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | |
| | | const CREATE_VALUE = '__CREATE_LOCATION__'; |
| | | |
| | | const attrs = useAttrs(); |
| | | const allList = ref<MesWmWarehouseLocationApi.WarehouseLocation[]>([]); |
| | | const selectedItem = ref<MesWmWarehouseLocationApi.WarehouseLocation>(); |
| | | const searchText = ref(''); |
| | | |
| | | const selectValue = computed({ |
| | | get: () => props.modelValue, |
| | | set: (val) => emit('update:modelValue', val), |
| | | }); |
| | | |
| | | /** 选中变化 */ |
| | | function handleChange(val: any) { |
| | | /** 输入内容尚未存在于列表时,允许直接新建 */ |
| | | const canCreate = computed(() => { |
| | | const keyword = searchText.value.trim(); |
| | | if (!keyword || !props.warehouseId) { |
| | | return false; |
| | | } |
| | | return !allList.value.some((o) => o.name === keyword); |
| | | }); |
| | | |
| | | /** 选中变化;选中“新增”项时按输入名称直接建档 */ |
| | | async function handleChange(val: any) { |
| | | if (val === CREATE_VALUE) { |
| | | await handleCreate(); |
| | | return; |
| | | } |
| | | const item = allList.value.find((o) => o.id === val); |
| | | selectedItem.value = item; |
| | | emit('change', item); |
| | | } |
| | | |
| | | /** 前端过滤(name + code) */ |
| | | /** 输入名称直接新建库区并选中 */ |
| | | async function handleCreate() { |
| | | const name = searchText.value.trim(); |
| | | if (!name || !props.warehouseId) { |
| | | return; |
| | | } |
| | | const id = await createWarehouseLocation({ |
| | | code: `ZD${Date.now()}`, |
| | | frozen: false, |
| | | name, |
| | | warehouseId: props.warehouseId, |
| | | }); |
| | | allList.value = await getWarehouseLocationSimpleList(props.warehouseId); |
| | | const item = allList.value.find((o) => o.id === id); |
| | | selectedItem.value = item; |
| | | searchText.value = ''; |
| | | emit('update:modelValue', id); |
| | | emit('change', item); |
| | | } |
| | | |
| | | /** 前端过滤(name + code),始终保留“新增”项 */ |
| | | function filterOption(input: string, option: any) { |
| | | if (option.value === CREATE_VALUE) { |
| | | return true; |
| | | } |
| | | const item = allList.value.find((o) => o.id === option.value); |
| | | if (!item) { |
| | | return false; |
| | |
| | | |
| | | /** 仓库变更时清空已选库区并重新加载列表 */ |
| | | watchEffect(async () => { |
| | | searchText.value = ''; |
| | | if (!props.warehouseId) { |
| | | allList.value = []; |
| | | return; |
| | |
| | | :popup-match-select-width="false" |
| | | show-search |
| | | @change="handleChange" |
| | | @search="searchText = $event" |
| | | > |
| | | <SelectOption v-for="item in allList" :key="item.id" :value="item.id"> |
| | | {{ item.name }} |
| | | </SelectOption> |
| | | <SelectOption v-if="canCreate" :value="CREATE_VALUE"> |
| | | 新增库区“{{ searchText.trim() }}” |
| | | </SelectOption> |
| | | </Select> |
| | | </Tooltip> |
| | | </template> |
| | |
| | | import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { BarcodeBizTypeEnum } from '@vben/constants'; |
| | |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'WlsWarehouse' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 跳转库区列表 */ |
| | | function handleOpenLocation(row: MesWmWarehouseApi.Warehouse) { |
| | | router.push({ |
| | | path: '/wls/location', |
| | | query: { warehouseId: String(row.id) }, |
| | | }); |
| | | } |
| | | |
| | | /** 查看仓库条码 */ |
| | |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: '库区', |
| | | type: 'link', |
| | | auth: ['mes:wm-warehouse:query'], |
| | | onClick: handleOpenLocation.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |