| | |
| | | return requestClient.post<number>('/mes/wm/material-stock/adjust-out', data); |
| | | } |
| | | |
| | | /** åºåç§»åº */ |
| | | export interface MaterialStockMoveParams { |
| | | materialStockId: number; // åºåè®°å½ID |
| | | toWarehouseId: number; // ç®æ ä»åºID |
| | | toLocationId: number; // ç®æ åºåºID |
| | | toAreaId: number; // ç®æ åºä½ID |
| | | quantity: number; // ç§»åºæ°é |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** åºåç§»åº */ |
| | | export function moveMaterialStock(data: MaterialStockMoveParams) { |
| | | return requestClient.post<boolean>('/mes/wm/material-stock/move', data); |
| | | } |
| | | |
| | | /** åºåè°æ´ï¼æ£æ°=å
¥åºï¼è´æ°=åºåºï¼- æ§æ¥å£ï¼ä¿çå
¼å®¹ */ |
| | | export interface MaterialStockAdjustParams { |
| | | itemId: number; // ç©æID |
| | |
| | | clientCode?: string; // 客æ·ç¼ç |
| | | clientName?: string; // 客æ·åç§° |
| | | salesOrderCode?: string; // éå®è®¢åç¼å· |
| | | saleOrderId?: number; // éå®è®¢åID |
| | | salesDate?: number; // åºåºæ¥æ |
| | | contactName?: string; // æ¶è´§äºº |
| | | contactTelephone?: string; // èç³»æ¹å¼ |
| | |
| | | clientCode?: string; // 客æ·ç¼ç |
| | | clientName?: string; // 客æ·åç§° |
| | | salesOrderCode?: string; // éå®è®¢åç¼å· |
| | | saleOrderId?: number; // éå®è®¢åID |
| | | salesDate?: number; // åºåºæ¥æ |
| | | contactName?: string; // æ¶è´§äºº |
| | | contactTelephone?: string; // èç³»æ¹å¼ |
| | |
| | | let unitListPromise: Promise<any[]> | null = null; |
| | | |
| | | /** å°æå¹³åç±»å表转为 TreeSelect æéçæ å½¢ç»æ */ |
| | | function buildCategoryTree(list: any[]): any[] { |
| | | export function buildCategoryTree(list: any[]): any[] { |
| | | const map = new Map<number, any>(); |
| | | const roots: any[] = []; |
| | | for (const item of list) { |
| | |
| | | } |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] { |
| | | export function useFormSchema(formApi?: VbenFormApi, categoryTreeData: any[] = []): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | component: "Input", |
| | |
| | | rules: "required", |
| | | }, |
| | | { |
| | | component: "ApiTreeSelect", |
| | | component: "TreeSelect", |
| | | fieldName: "categoryId", |
| | | label: "ç©æåç±»", |
| | | rules: "selectRequired", |
| | |
| | | placeholder: "è¯·éæ©ç©æåç±»", |
| | | allowClear: true, |
| | | treeDefaultExpandAll: true, |
| | | api: async () => { |
| | | const res = await getItemTypeSimpleList(); |
| | | return buildCategoryTree(res || []); |
| | | }, |
| | | treeData: categoryTreeData, |
| | | fieldNames: { label: 'label', value: 'value', children: 'children' }, |
| | | }, |
| | | }, |
| | | { |
| | |
| | | import { createItem, getItem, updateItem } from '#/api/mdm/item'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema, clearUnitListCache } from '../data'; |
| | | import { useFormSchema, clearUnitListCache, buildCategoryTree } from '../data'; |
| | | import { getItemTypeSimpleList } from '#/api/mes/md/item/type'; |
| | | |
| | | defineOptions({ name: "ItemForm" }); |
| | | |
| | |
| | | const data = modalApi.getData<MdmItemApi.Item>(); |
| | | // æ¸
é¤åä½ç¼åï¼ç¡®ä¿è·åææ°æ°æ® |
| | | clearUnitListCache(); |
| | | // å è½½ææ°åç±»æ°æ® |
| | | const categoryRes = await getItemTypeSimpleList(); |
| | | const categoryTreeData = buildCategoryTree(categoryRes || []); |
| | | // 设置 schemaï¼formApi å·²åå§åï¼ |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | formApi.setState({ schema: useFormSchema(formApi, categoryTreeData) }); |
| | | if (data?.id) { |
| | | // ç¼è¾æ¨¡å¼ |
| | | modalApi.lock(); |
| | |
| | | type: 'link', |
| | | icon: 'ant-design:file-protect-outlined', |
| | | auth: ['mes:wm-sales-notice:create'], |
| | | ifShow: () => row.status === 20 && (row.canCreateSalesNotice || row.productionStatus === 1), |
| | | ifShow: () => row.status === 20 && row.canCreateSalesNotice, |
| | | onClick: handleGenerateNotice.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | if (formType.value === 'create') { |
| | | const id = await createItem(data); |
| | | formData.value = { ...data, id }; |
| | | await formApi.setFieldValue('id', id); |
| | | formType.value = 'update'; |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } else { |
| | | await updateItem(data); |
| | | formData.value = { ...formData.value, ...data }; |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | |
| | | await updateFeedback(data); |
| | | formData.value = { ...formData.value, ...data }; |
| | | message.success($t('common.updateSuccess')); |
| | | await modalApi.close(); |
| | | } |
| | | // å·æ°å¿«ç
§ï¼å·²å¯¹é½æ°éå¹¶è¡¥ idï¼ï¼åç» submit ç¨äºå¤ææ¯å¦éè¦åä¿å |
| | | originalSnapshot.value = JSON.stringify(data); |
| | |
| | | * 1. æè®¢ååç»å±ç¤ºç产任å¡ï¼è®¢å为 project è¡ï¼ä»»å¡ä¸ºåè¡ |
| | | * 2. æ¯æåªè¯»é¢è§åææ½ç¼è¾ä¸¤ç§æ¨¡å¼ |
| | | * 3. ææ½å触å taskUpdate äºä»¶ï¼éç¥ç¶ç»ä»¶æ¹éä¿å |
| | | * 4. æ¶é´å»åº¦ï¼å¨ â æ¥ â 8 å°æ¶ï¼1 工使¥ = 8 å°æ¶ï¼ |
| | | * 4. æ¶é´å»åº¦ï¼å¨ â æ¥ â å°æ¶ |
| | | */ |
| | | defineOptions({ name: 'GanttChart', inheritAttrs: false }); |
| | | |
| | |
| | | |
| | | gantt.config.readonly = props.readonly; |
| | | gantt.config.date_format = '%Y-%m-%d %H:%i:%s'; |
| | | gantt.config.duration_unit = 'hour'; // æç»æ¶é´åä½ä¸ºå°æ¶ï¼é
å duration_step å®ç°å·¥ä½æ¥åä½ |
| | | gantt.config.duration_step = 8; // 1 工使¥ = 8 å°æ¶ |
| | | gantt.config.duration_unit = 'hour'; // æç»æ¶é´åä½ä¸ºå°æ¶ |
| | | gantt.config.duration_step = 1; // duration åæ®µå³ä¸ºå®é
å°æ¶æ° |
| | | gantt.config.row_height = 36; |
| | | gantt.config.bar_height = 24; |
| | | gantt.config.fit_tasks = true; |
| | |
| | | gantt.config.buttons_left = ['gantt_save_btn']; |
| | | gantt.config.buttons_right = ['gantt_cancel_btn']; |
| | | |
| | | // æ¶é´å»åº¦ï¼å¨ > æ¥ > 8 å°æ¶ |
| | | // æ¶é´å»åº¦ï¼å¨ > æ¥ > å°æ¶ |
| | | const weekScaleTemplate = (date: Date) => { |
| | | const dateToStr = gantt.date.date_to_str('%M %d'); |
| | | const endDate = gantt.date.add(gantt.date.add(date, 1, 'week'), -1, 'day'); |
| | |
| | | gantt.config.scales = [ |
| | | { unit: 'week', step: 1, format: weekScaleTemplate }, |
| | | { unit: 'day', step: 1, format: dayTemplate, css: daysStyle }, |
| | | { unit: 'hour', step: 8, format: '%H:%i' }, |
| | | { unit: 'hour', step: 1, format: '%H:%i' }, |
| | | ]; |
| | | gantt.config.scale_height = 50; |
| | | gantt.config.show_task_cells = true; |
| | |
| | | }, |
| | | { |
| | | field: 'duration', |
| | | title: 'ç产æ¶é¿', |
| | | width: 80, |
| | | title: 'ç产æ¶é¿(h)', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'endTime', |
| | |
| | | }, |
| | | { |
| | | fieldName: 'duration', |
| | | label: 'ç产æ¶é¿', |
| | | label: 'ç产æ¶é¿(h)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | |
| | | ]; |
| | | } |
| | | |
| | | /** 计ç®ç»ææ¶é´ï¼å¼å§æ¶é´ + ç产æ¶é¿ à 8 å°æ¶ */ |
| | | /** 计ç®ç»ææ¶é´ï¼å¼å§æ¶é´ + ç产æ¶é¿ï¼å°æ¶ï¼ */ |
| | | async function recalcEndTime(formApi?: VbenFormApi) { |
| | | if (!formApi) { |
| | | return; |
| | |
| | | const start = Number(values.startTime); |
| | | await formApi.setFieldValue( |
| | | 'endTime', |
| | | start + values.duration * 8 * 3600 * 1000, |
| | | start + values.duration * 3600 * 1000, |
| | | ); |
| | | } |
| | | } |
| | |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { field: 'duration', title: 'ç产æ¶é¿', width: 100 }, |
| | | { field: 'duration', title: 'ç产æ¶é¿(h)', width: 120 }, |
| | | { |
| | | field: 'endTime', |
| | | title: 'é¢è®¡å®ææ¶é´', |
| | |
| | | label: '宿', |
| | | type: 'link', |
| | | auth: ['mes:pro-work-order:update'], |
| | | ifShow: row.status === MesProWorkOrderStatusEnum.CONFIRMED, |
| | | ifShow: |
| | | row.status === MesProWorkOrderStatusEnum.CONFIRMED && |
| | | !!row.quantityProduced, |
| | | onClick: handleFinish.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | |
| | | import { computed, ref } from "vue"; |
| | | |
| | | import { useVbenModal } from "@vben/common-ui"; |
| | | import { confirm, useVbenModal } from "@vben/common-ui"; |
| | | import { |
| | | BarcodeBizTypeEnum, |
| | | MesProWorkOrderStatusEnum, |
| | | } from "@vben/constants"; |
| | | |
| | | import { Button, message, Popconfirm, Tabs } from "ant-design-vue"; |
| | | import { Button, message, Tabs } from "ant-design-vue"; |
| | | |
| | | import { useVbenForm } from "#/adapter/form"; |
| | | import { |
| | |
| | | wrapperClass: "grid-cols-3", |
| | | }); |
| | | |
| | | /** éæ°æè½½ schema å¹¶æè¡¨åææ§å¶åºé¨ç¡®è®¤æé® */ |
| | | /** éæ°æè½½ schema å¹¶æè¡¨åææ§å¶åºé¨æé® */ |
| | | function applySchema() { |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | modalApi.setState({ showConfirmButton: isEditable.value }); |
| | | const showBtn = isEditable.value || isConfirm.value || isFinish.value; |
| | | modalApi.setState({ |
| | | showConfirmButton: showBtn, |
| | | confirmText: isConfirm.value || canConfirm.value |
| | | ? '确认' |
| | | : isFinish.value |
| | | ? '宿' |
| | | : undefined, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥çè®¢åæ¡ç */ |
| | |
| | | formData.value.code, |
| | | formData.value.name |
| | | ); |
| | | } |
| | | |
| | | /** 确认订åï¼ç¼è¾ææä¿®æ¹æ¶å
ä¿åï¼å确认 */ |
| | | async function handleConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | if (isEditable.value) { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = |
| | | (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | | } |
| | | await confirmWorkOrder(formData.value.id); |
| | | message.success("订å已确认"); |
| | | await modalApi.close(); |
| | | emit("success"); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** å®æè®¢å */ |
| | | async function handleFinish() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await finishWorkOrder(formData.value.id); |
| | | message.success("订å已宿"); |
| | | await modalApi.close(); |
| | | emit("success"); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** ä» BOM ç©æè¡é¢å¡«å订åï¼å¤ç¨å½å订åä¸ä¸æï¼è¿å
¥æ°å¢æ */ |
| | |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | // ---- ç¡®è®¤è®¢åæ¨¡å¼ ---- |
| | | if (isConfirm.value || canConfirm.value) { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid || !formData.value?.id) return; |
| | | try { |
| | | await confirm('确认è¦å®æè®¢åç¼å¶åï¼ç¡®è®¤åå°ä¸è½æ´æ¹ã'); |
| | | } catch { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | // ç¼è¾ææä¿®æ¹æ¶å
ä¿å |
| | | if (isEditable.value) { |
| | | const current = JSON.stringify(await formApi.getValues()); |
| | | if (current !== originalSnapshot.value) { |
| | | const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | originalSnapshot.value = current; |
| | | } |
| | | } |
| | | await confirmWorkOrder(formData.value.id); |
| | | message.success('订å已确认'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | return; |
| | | } |
| | | // ---- å®æè®¢åæ¨¡å¼ ---- |
| | | if (isFinish.value) { |
| | | if (!formData.value?.id) return; |
| | | try { |
| | | await confirm('确认è¦å®æè¯¥è®¢ååï¼'); |
| | | } catch { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await finishWorkOrder(formData.value.id); |
| | | message.success('订å已宿'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | return; |
| | | } |
| | | // ---- ä¿åï¼æ°å¢ / ç¼è¾ï¼---- |
| | | if (!isEditable.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder; |
| | | try { |
| | | if (formData.value?.id) { |
| | | await updateWorkOrder({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | emit("success"); |
| | | message.success($t("ui.actionMessage.operationSuccess")); |
| | | await modalApi.close(); |
| | | } else { |
| | | const id = await createWorkOrder(data); |
| | | formData.value = { |
| | |
| | | status: MesProWorkOrderStatusEnum.PREPARE, |
| | | }; |
| | | formType.value = "update"; |
| | | // å建æåå忢ç¼è¾æï¼éæ schema 让ã订åç¶æãåæ®µæ¾ç° |
| | | applySchema(); |
| | | await formApi.setValues(formData.value); |
| | | emit("success"); |
| | | } |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit("success"); |
| | | message.success($t("ui.actionMessage.operationSuccess")); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | |
| | | </Tabs> |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm v-if="canConfirm || isConfirm" |
| | | title="确认è¦å®æè®¢åç¼å¶åï¼ç¡®è®¤åå°ä¸è½æ´æ¹ã" |
| | | @confirm="handleConfirm"> |
| | | <Button type="primary">确认</Button> |
| | | </Popconfirm> |
| | | <Popconfirm v-if="isFinish" |
| | | title="确认è¦å®æè¯¥è®¢ååï¼" |
| | | @confirm="handleFinish"> |
| | | <Button type="primary">宿</Button> |
| | | </Popconfirm> |
| | | <Button v-if="formType === 'detail' && formData?.id" |
| | | @click="handleBarcode"> |
| | | æ¥çæ¡ç |
| | | </Button> |
| | | </div> |
| | | <Button v-if="formType === 'detail' && formData?.id" |
| | | @click="handleBarcode"> |
| | | æ¥çæ¡ç |
| | | </Button> |
| | | </template> |
| | | </Modal> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | |
| | | } else { |
| | | await updateRoute(data); |
| | | formData.value = { ...formData.value, ...data }; |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | // æ°å¢ï¼ä¿æå¼¹çªæå¼ï¼åæ¢å°ç¼è¾æ¨¡å¼ç»§ç»ç»´æ¤ |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | |
| | | if (formData.value?.id) { |
| | | await updateItemReceipt({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | } else { |
| | | const id = await createItemReceipt(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmItemReceiptStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | // æ°å¢ï¼ä¿æå¼¹çªæå¼ï¼åæ¢å°ç¼è¾æ¨¡å¼ |
| | | const id = await createItemReceipt(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmItemReceiptStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | |
| | | export { default as WmMaterialStockSelectDialog } from './select-dialog.vue'; |
| | | export { default as WmMaterialStockSelect } from './select.vue'; |
| | | export { default as WmStockAdjustModal } from './stock-adjust-modal.vue'; |
| | | export { default as WmStockMoveModal } from './move-modal.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | import { markRaw, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { moveMaterialStock } from '#/api/mes/wm/materialstock'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | | WmWarehouseLocationSelect, |
| | | WmWarehouseSelect, |
| | | } from '#/views/wls/warehouse/components'; |
| | | |
| | | defineOptions({ name: 'WmStockMoveModal' }); |
| | | |
| | | const emit = defineEmits<{ success: [] }>(); |
| | | |
| | | const currentRow = ref<MesWmMaterialStockApi.MaterialStock>(); |
| | | |
| | | function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemCode', |
| | | label: 'ç©æç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'itemName', |
| | | label: 'ç©æåç§°', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'warehouseName', |
| | | label: 'å½åä»åº', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'locationName', |
| | | label: 'å½ååºåº', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'areaName', |
| | | label: 'å½ååºä½', |
| | | component: 'Input', |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'å½åæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { disabled: true, class: '!w-full', precision: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'toWarehouseId', |
| | | label: 'ç®æ ä»åº', |
| | | component: markRaw(WmWarehouseSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç®æ ä»åº', |
| | | onChange: async () => { |
| | | await formApi.setFieldValue('toLocationId', undefined); |
| | | await formApi.setFieldValue('toAreaId', undefined); |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'toLocationId', |
| | | label: 'ç®æ åºåº', |
| | | component: markRaw(WmWarehouseLocationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©ç®æ åºåº' }, |
| | | dependencies: { |
| | | triggerFields: ['toWarehouseId'], |
| | | componentProps: (values) => ({ |
| | | warehouseId: values.toWarehouseId, |
| | | placeholder: 'è¯·éæ©ç®æ åºåº', |
| | | onChange: async () => { |
| | | await formApi.setFieldValue('toAreaId', undefined); |
| | | }, |
| | | }), |
| | | trigger: (values, api) => { |
| | | if (values.toLocationId !== undefined) { |
| | | void api.setFieldValue('toLocationId', undefined); |
| | | void api.setFieldValue('toAreaId', undefined); |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'toAreaId', |
| | | label: 'ç®æ åºä½', |
| | | component: markRaw(WmWarehouseAreaSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©ç®æ åºä½' }, |
| | | dependencies: { |
| | | triggerFields: ['toLocationId'], |
| | | componentProps: (values) => ({ |
| | | locationId: values.toLocationId, |
| | | placeholder: 'è¯·éæ©ç®æ åºä½', |
| | | }), |
| | | trigger: (values, api) => { |
| | | if (values.toAreaId !== undefined) { |
| | | void api.setFieldValue('toAreaId', undefined); |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'moveQuantity', |
| | | label: 'ç§»åºæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0.01, |
| | | placeholder: '请è¾å
¥ç§»åºæ°é', |
| | | precision: 2, |
| | | }, |
| | | rules: z.number().positive('ç§»åºæ°éå¿
须大äº0'), |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 3 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | |
| | | const row = currentRow.value; |
| | | if (!row?.id) return; |
| | | |
| | | const values = await formApi.getValues(); |
| | | const moveQuantity = values.moveQuantity as number; |
| | | const quantity = (row.quantity || 0) as number; |
| | | const availableQuantity = (row.availableQuantity || 0) as number; |
| | | |
| | | if (moveQuantity > quantity) { |
| | | message.error(`ç§»åºæ°éä¸è½è¶
è¿å½ååºåæ°éï¼${quantity.toFixed(2)}ï¼`); |
| | | return; |
| | | } |
| | | |
| | | if (moveQuantity > availableQuantity) { |
| | | message.error(`ç§»åºæ°éä¸è½è¶
è¿å¯ç¨éï¼${availableQuantity.toFixed(2)}ï¼ï¼å¯ç¨é = å¨åºæ°é - å»ç»æ°é - å ç¨é`); |
| | | return; |
| | | } |
| | | |
| | | if (row.warehouseId === values.toWarehouseId && row.locationId === values.toLocationId && row.areaId === values.toAreaId) { |
| | | message.error('ç®æ åºä½ä¸å½ååºä½ç¸åï¼ä¸å
许移åº'); |
| | | return; |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | try { |
| | | await moveMaterialStock({ |
| | | materialStockId: row.id, |
| | | quantity: moveQuantity, |
| | | remark: values.remark, |
| | | toAreaId: values.toAreaId, |
| | | toLocationId: values.toLocationId, |
| | | toWarehouseId: values.toWarehouseId, |
| | | }); |
| | | message.success('ç§»åºæå'); |
| | | emit('success'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | currentRow.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<MesWmMaterialStockApi.MaterialStock>(); |
| | | if (!data?.id) return; |
| | | currentRow.value = data; |
| | | |
| | | if (data.frozen) { |
| | | message.warning('该åºåè®°å½å·²å»ç»ï¼ä¸å
许移åº'); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | |
| | | await formApi.setValues({ |
| | | areaName: data.areaName, |
| | | batchCode: data.batchCode, |
| | | itemCode: data.itemCode, |
| | | itemName: data.itemName, |
| | | locationName: data.locationName, |
| | | quantity: data.quantity, |
| | | warehouseName: data.warehouseName, |
| | | }); |
| | | }, |
| | | title: 'åºåç§»åº', |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | // é»è®¤æ¥æªå»ç» |
| | | frozen: false, |
| | | itemTypeId: searchItemTypeId.value, |
| | | itemId: formValues.mdmItemId ?? props.mdmItemId, |
| | | itemId: props.mdmItemId, |
| | | warehouseId: formValues.warehouseId ?? props.warehouseId, |
| | | virtualFilter: |
| | | props.virtualFilter === 'all' ? undefined : props.virtualFilter, |
| | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | newFrozen: boolean, |
| | | row: MesWmMaterialStockApi.MaterialStock, |
| | | ) => Promise<boolean | undefined>, |
| | | onMove?: (row: MesWmMaterialStockApi.MaterialStock) => void, |
| | | ): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] { |
| | | const { hasAccessByCodes } = useAccess(); |
| | | return [ |
| | |
| | | }, |
| | | }, |
| | | }, |
| | | ...(onMove |
| | | ? [ |
| | | { |
| | | title: 'æä½', |
| | | width: 80, |
| | | fixed: 'right' as const, |
| | | slots: { default: 'actions' } as const, |
| | | }, |
| | | ] |
| | | : []), |
| | | ]; |
| | | } |
| | | |
| | |
| | | export function useSelectGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemId', |
| | | fieldName: 'itemName', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç©æ', |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥ç©æåç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'vendorId', |
| | | fieldName: 'vendorName', |
| | | label: 'ä¾åºå', |
| | | component: markRaw(MdVendorSelect), |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥ä¾åºååç§°', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | import { MdItemTypeTree } from '#/views/mes/md/item/type/components'; |
| | | import { WmBatchDetail } from '#/views/wls/batch/components'; |
| | | import AreaForm from '#/views/wls/warehouse/area/modules/form.vue'; |
| | | import { WmStockAdjustModal } from './components'; |
| | | import { WmStockAdjustModal, WmStockMoveModal } from './components'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | |
| | |
| | | |
| | | const [StockAdjustModal, stockAdjustModalApi] = useVbenModal({ |
| | | connectedComponent: WmStockAdjustModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [StockMoveModal, stockMoveModalApi] = useVbenModal({ |
| | | connectedComponent: WmStockMoveModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æå¼ç§»åºå¼¹çª */ |
| | | function handleOpenStockMove(row: MesWmMaterialStockApi.MaterialStock) { |
| | | stockMoveModalApi.setData(row).open(); |
| | | } |
| | | |
| | | /** ç§»åºæååè° */ |
| | | function handleStockMoveSuccess() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æå¼æ¹æ¬¡è¯¦æ
å¼¹çª */ |
| | | function handleOpenBatchDetail(row: MesWmMaterialStockApi.MaterialStock) { |
| | | if (!row.batchId) { |
| | |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(handleFrozenChange), |
| | | columns: useGridColumns(handleFrozenChange, handleOpenStockMove), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | |
| | | <Page auto-content-height><AreaModal /> |
| | | <WmBatchDetail ref="batchDetailRef" /> |
| | | <StockAdjustModal @success="handleStockAdjustSuccess" /> |
| | | <StockMoveModal @success="handleStockMoveSuccess" /> |
| | | |
| | | <div class="flex h-full w-full"> |
| | | <!-- å·¦ä¾§ç©æåç±»æ --> |
| | |
| | | </Button> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç§»åº', |
| | | type: 'link', |
| | | auth: ['mes:wm-material-stock:move'], |
| | | ifShow: !row.frozen, |
| | | onClick: handleOpenStockMove.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </div> |
| | |
| | | if (formData.value?.id) { |
| | | await updateProductIssue({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | } else { |
| | | const id = await createProductIssue(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmProductIssueStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | // æ°å¢ï¼ä¿æå¼¹çªæå¼ï¼åæ¢å°ç¼è¾æ¨¡å¼ |
| | | const id = await createProductIssue(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmProductIssueStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: 'éå®è®¢åç¼å·', |
| | | component: markRaw(ErpSaleOrderSelect), |
| | |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['salesOrderCode'], |
| | | if: () => formType === 'create', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | label: 'éå®è®¢åç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'éå®è®¢åç¼å·', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | if: () => formType !== 'create', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salesDate', |
| | |
| | | if (formData.value?.id) { |
| | | await updateReturnIssue({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | } else { |
| | | const id = await createReturnIssue(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmReturnIssueStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | // æ°å¢ï¼ä¿æå¼¹çªæå¼ï¼åæ¢å°ç¼è¾æ¨¡å¼ |
| | | const id = await createReturnIssue(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmReturnIssueStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | |
| | | if (formData.value?.id) { |
| | | await updateSalesNotice({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | } else { |
| | | const id = await createSalesNotice(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmSalesNoticeStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | // æ°å¢ï¼ä¿æå¼¹çªæå¼ï¼åæ¢å°ç¼è¾æ¨¡å¼ |
| | | const id = await createSalesNotice(data); |
| | | formData.value = { |
| | | ...data, |
| | | id, |
| | | status: MesWmSalesNoticeStatusEnum.PREPARE, |
| | | }; |
| | | await formApi.setFieldValue('id', id); |
| | | await formApi.setFieldValue('status', formData.value.status); |
| | | formType.value = 'update'; |
| | | originalSnapshot.value = JSON.stringify(await formApi.getValues()); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |