| | |
| | | id?: number; // 颿åç¼å· |
| | | code?: string; // 颿åç¼å· |
| | | name?: string; // 颿ååç§° |
| | | taskId?: number; // çäº§ä»»å¡ ID |
| | | taskCode?: string; // ç产任å¡ç¼ç |
| | | taskName?: string; // ç产任å¡åç§° |
| | | workstationId?: number; // å·¥ä½ç«ç¼å· |
| | | workstationCode?: string; // å·¥ä½ç«ç¼ç |
| | | workstationName?: string; // å·¥ä½ç«åç§° |
| | |
| | | id?: number; // éæåç¼å· |
| | | code?: string; // éæåç¼å· |
| | | name?: string; // éæååç§° |
| | | taskId?: number; // çäº§ä»»å¡ ID |
| | | taskCode?: string; // ç产任å¡ç¼ç |
| | | taskName?: string; // ç产任å¡åç§° |
| | | workstationId?: number; // å·¥ä½ç«ç¼å· |
| | | workstationName?: string; // å·¥ä½ç«åç§° |
| | | workOrderId?: number; // ç产订åç¼å· |
| | |
| | | import type { VbenFormSchema } from "#/adapter/form"; |
| | | import type { VbenFormApi, VbenFormSchema } from "#/adapter/form"; |
| | | import type { VxeTableGridOptions } from "#/adapter/vxe-table"; |
| | | |
| | | import { h } from 'vue'; |
| | | |
| | | import { CommonStatusEnum, DICT_TYPE } from "#/packages/constants/src"; |
| | | import { getDictOptions } from "#/packages/effects/hooks/src"; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { MesAutoCodeRuleCode } from '@vben/constants'; |
| | | import { getItemTypeSimpleList } from '#/api/mes/md/item/type'; |
| | | import { getUnitPage } from '#/api/mdm/unit'; |
| | | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | // ç¼ååä½å表 |
| | | let unitListCache: any[] | null = null; |
| | |
| | | } |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | component: "Input", |
| | |
| | | fieldName: "code", |
| | | label: "ç©æç¼ç ", |
| | | componentProps: { |
| | | maxLength: 64, |
| | | placeholder: "请è¾å
¥ç©æç¼ç ", |
| | | }, |
| | | rules: "required", |
| | | rules: z.string().min(1, 'ç©æç¼ç ä¸è½ä¸ºç©º').max(64), |
| | | suffix: () => |
| | | h( |
| | | Button, |
| | | { |
| | | type: 'default', |
| | | onClick: async () => { |
| | | const code = await generateAutoCode( |
| | | MesAutoCodeRuleCode.MD_ITEM_CODE, |
| | | ); |
| | | await formApi?.setFieldValue('code', code); |
| | | }, |
| | | }, |
| | | { default: () => 'çæ' }, |
| | | ), |
| | | }, |
| | | { |
| | | component: "Input", |
| | |
| | | labelWidth: 110, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | |
| | | return; |
| | | } |
| | | const data = modalApi.getData<MdmItemApi.Item>(); |
| | | // 设置 schemaï¼formApi å·²åå§åï¼ |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | if (data?.id) { |
| | | // ç¼è¾æ¨¡å¼ |
| | | modalApi.lock(); |
| | |
| | | <template #toolbar-tools> |
| | | <TableAction :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['ç©æ']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:md-item:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.import'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue'; |
| | | import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | import { computed, h, markRaw, ref } from 'vue'; |
| | | |
| | | import { |
| | | DICT_TYPE, |
| | | MesAutoCodeRuleCode, |
| | | MesProWorkOrderStatusEnum, |
| | | MesWmProductIssueStatusEnum, |
| | | } from '@vben/constants'; |
| | | |
| | | import { Button, Divider, message, Popconfirm } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { confirm, useVbenModal } from '@vben/common-ui'; |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { |
| | | checkProductIssueQuantity, |
| | | getProductIssue, |
| | | stockProductIssue, |
| | | submitProductIssue, |
| | | updateProductIssue, |
| | | } from '#/api/mes/wm/productissue'; |
| | | import { |
| | | deleteProductIssueLine, |
| | | getProductIssueLinePage, |
| | | } from '#/api/mes/wm/productissue/line'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | |
| | | import ProductIssueLineFormModal from './ProductIssueLineFormModal.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmProductIssueApi.ProductIssue>(); |
| | | |
| | | const canSubmit = computed( |
| | | () => formData.value?.status === MesWmProductIssueStatusEnum.PREPARE, |
| | | ); |
| | | |
| | | /** æ¯å¦å¯æ§è¡æ£è´§ï¼å®¡æ¹ä¸ç¶æï¼ */ |
| | | const canStock = computed( |
| | | () => formData.value?.status === MesWmProductIssueStatusEnum.APPROVING, |
| | | ); |
| | | |
| | | /** æå»ºç¼è¾è¡¨å schema */ |
| | | function buildFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: '颿åç¼å·', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥é¢æåç¼å·' }, |
| | | rules: 'required', |
| | | suffix: () => |
| | | h( |
| | | Button, |
| | | { |
| | | type: 'default', |
| | | size: 'small', |
| | | onClick: async () => { |
| | | const code = await generateAutoCode( |
| | | MesAutoCodeRuleCode.WM_PRODUCT_ISSUE_CODE, |
| | | ); |
| | | await formApi.setFieldValue('code', code); |
| | | }, |
| | | }, |
| | | { default: () => 'çæ' }, |
| | | ), |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '颿ååç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥é¢æååç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'requiredTime', |
| | | label: 'éæ±æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | placeholder: 'è¯·éæ©éæ±æ¶é´', |
| | | showTime: true, |
| | | valueFormat: 'x', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workOrderId', |
| | | label: 'ç产订å', |
| | | component: markRaw(ProWorkOrderSelect), |
| | | componentProps: { |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workstationId', |
| | | label: 'å·¥ä½ç«', |
| | | component: markRaw(MdWorkstationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©å·¥ä½ç«' }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: buildFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | // --- ç©æè¡å表 --- |
| | | const lineGridColumns: VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>['columns'] = [ |
| | | { field: 'itemCode', title: 'ç©æç¼ç ', minWidth: 120 }, |
| | | { field: 'itemName', title: 'ç©æåç§°', minWidth: 140 }, |
| | | { field: 'specification', title: 'è§æ ¼åå·', minWidth: 120 }, |
| | | { field: 'unitMeasureName', title: 'åä½', width: 80 }, |
| | | { field: 'quantity', title: '颿æ°é', width: 100 }, |
| | | { title: 'æä½', width: 140, fixed: 'right', slots: { default: 'lineActions' } }, |
| | | ]; |
| | | |
| | | const [LineGrid, lineGridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: lineGridColumns, |
| | | height: 260, |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }) => { |
| | | if (!formData.value?.id) return { list: [], total: 0 }; |
| | | return await getProductIssueLinePage({ |
| | | issueId: formData.value.id, |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true }, |
| | | pagerConfig: { pageSize: 10 }, |
| | | } as VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>, |
| | | }); |
| | | |
| | | /** æ·»å ç©æè¡ */ |
| | | function handleCreateLine() { |
| | | lineFormModalApi.setData({ issueId: formData.value?.id }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ç©æè¡ */ |
| | | function handleEditLine(row: MesWmProductIssueLineApi.ProductIssueLine) { |
| | | lineFormModalApi.setData({ id: row.id, issueId: formData.value?.id }).open(); |
| | | } |
| | | |
| | | /** å é¤ç©æè¡ */ |
| | | async function handleDeleteLine(row: MesWmProductIssueLineApi.ProductIssueLine) { |
| | | try { |
| | | await deleteProductIssueLine(row.id!); |
| | | message.success('å 餿å'); |
| | | lineGridApi.query(); |
| | | } catch { |
| | | message.error('å é¤å¤±è´¥'); |
| | | } |
| | | } |
| | | |
| | | /** ç©æè¡æä½æå */ |
| | | function onLineSuccess() { |
| | | lineGridApi.query(); |
| | | } |
| | | |
| | | const [LineFormModal, lineFormModalApi] = useVbenModal({ |
| | | connectedComponent: ProductIssueLineFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æäº¤é¢æå */ |
| | | async function handleSubmit() { |
| | | if (!formData.value?.id) return; |
| | | modalApi.lock(); |
| | | try { |
| | | // å
ä¿å表å |
| | | const data = |
| | | (await formApi.getValues()) as MesWmProductIssueApi.ProductIssue; |
| | | await updateProductIssue({ ...formData.value, ...data }); |
| | | // åæäº¤ |
| | | await submitProductIssue(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡æ£è´§ï¼é¢ææ°é䏿£è´§æ°éä¸ä¸è´æ¶äºæ¬¡ç¡®è®¤ */ |
| | | async function handleStock() { |
| | | if (!formData.value?.id) return; |
| | | const quantityMatch = await checkProductIssueQuantity(formData.value.id); |
| | | if (!quantityMatch) { |
| | | try { |
| | | await confirm('颿æ°é䏿£è´§æ°éä¸ä¸è´ï¼ç¡®è®¤æ§è¡æ£è´§ï¼'); |
| | | } catch { |
| | | return; |
| | | } |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await stockProductIssue(formData.value.id); |
| | | message.success('æ£è´§æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | try { |
| | | const data = |
| | | (await formApi.getValues()) as MesWmProductIssueApi.ProductIssue; |
| | | await updateProductIssue({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | emit('success'); |
| | | message.success('ä¿åæå'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getProductIssue(data.id); |
| | | await formApi.setValues(formData.value); |
| | | // å è½½åå·æ°ç©æè¡ |
| | | lineGridApi.query(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="ç¼è¾é¢æå" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | <!-- ç©æä¿¡æ¯ --> |
| | | <template v-if="formData?.id"> |
| | | <Divider style="margin: 12px 0"> |
| | | <span style="font-weight: 600; color: #1890ff;">ç©æä¿¡æ¯</span> |
| | | </Divider> |
| | | <div class="mx-4" style="height: 300px"> |
| | | <LineGrid table-title="ç©æå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ·»å ç©æ', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | onClick: handleCreateLine, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #lineActions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç¼è¾', |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | onClick: handleEditLine.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'å é¤', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | popConfirm: { |
| | | title: `确认å é¤ç©æ ${row.itemName}ï¼`, |
| | | confirm: handleDeleteLine.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </LineGrid> |
| | | </div> |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | | title="确认æäº¤è¯¥é¢æåï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Button v-if="canStock" type="primary" @click="handleStock"> |
| | | æ§è¡æ£è´§ |
| | | </Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | <LineFormModal @success="onLineSuccess" /> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line'; |
| | | |
| | | import { computed, markRaw, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createProductIssueLine, |
| | | getProductIssueLine, |
| | | updateProductIssueLine, |
| | | } from '#/api/mes/wm/productissue/line'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmProductIssueLineApi.ProductIssueLine>(); |
| | | const issueId = ref<number>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id ? 'ç¼è¾ç©æ' : 'æ·»å ç©æ'; |
| | | }); |
| | | |
| | | /** ç©æè¡è¡¨å schema */ |
| | | function buildLineFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产åç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©äº§åç©æ' }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: '颿æ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | placeholder: '请è¾å
¥é¢ææ°é', |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { componentProps: { class: 'w-full' }, formItemClass: 'col-span-1', labelWidth: 90 }, |
| | | layout: 'horizontal', |
| | | schema: buildLineFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesWmProductIssueLineApi.ProductIssueLine; |
| | | data.issueId = issueId.value; |
| | | try { |
| | | if (formData.value?.id) { |
| | | await updateProductIssueLine({ ...data, id: formData.value.id }); |
| | | } else { |
| | | await createProductIssueLine(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æä½æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id?: number; issueId: number }>(); |
| | | issueId.value = data.issueId; |
| | | if (!data.id) { |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getProductIssueLine(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue'; |
| | | |
| | | import { h, markRaw, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'; |
| | | import { computed, h, markRaw, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'; |
| | | |
| | | import { |
| | | DICT_TYPE, |
| | |
| | | MesWmProductIssueStatusEnum, |
| | | } from '@vben/constants'; |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { Button, Divider, message, Popconfirm, Tag } from 'ant-design-vue'; |
| | | import { Button, Divider, Empty, message, Popconfirm, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { |
| | |
| | | } from '#/api/mes/wm/productissue'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ProductIssueFormModal from './ProductIssueFormModal.vue'; |
| | | |
| | | defineOptions({ name: 'WorkbenchProductIssueTab' }); |
| | | |
| | |
| | | workstationId?: number; |
| | | /** å½åå·¥ä½ç«åç§° */ |
| | | workstationName?: string; |
| | | /** å½åéä¸çä»»å¡ ID */ |
| | | taskId?: number; |
| | | /** å½åéä¸çä»»å¡ç¼ç */ |
| | | taskCode?: string; |
| | | /** å½åéä¸çä»»å¡åç§° */ |
| | | taskName?: string; |
| | | /** å½åéä¸çä»»å¡å
³èå·¥å ID */ |
| | | workOrderId?: number; |
| | | }>(); |
| | | |
| | | // --- 表åç¶æ --- |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'taskId', |
| | | label: 'ç产任å¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ä»éä¸ä»»å¡èªå¨å¸¦å
¥', |
| | | disabled: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'workOrderId', |
| | | label: 'ç产订å', |
| | | component: markRaw(ProWorkOrderSelect), |
| | | componentProps: { |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | disabled: true, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'workstationId', |
| | | label: 'å·¥ä½ç«', |
| | | component: markRaw(MdWorkstationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©å·¥ä½ç«' }, |
| | | componentProps: { placeholder: 'ä»éä¸ä»»å¡èªå¨å¸¦å
¥', disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'requiredTime', |
| | |
| | | isCreating.value = true; |
| | | editingId.value = undefined; |
| | | await formApi.resetForm(); |
| | | // é¢å¡«å·¥ä½ç« |
| | | // é¢å¡«ä»»å¡ä¿¡æ¯ |
| | | if (props.taskId) { |
| | | await formApi.setFieldValue('taskId', props.taskId); |
| | | } |
| | | if (props.workOrderId) { |
| | | await formApi.setFieldValue('workOrderId', props.workOrderId); |
| | | } |
| | | if (props.workstationId) { |
| | | await formApi.setFieldValue('workstationId', props.workstationId); |
| | | } |
| | |
| | | formApi.resetForm(); |
| | | } |
| | | |
| | | /** ç¼è¾é¢æåï¼æå¼ç¼è¾å¼¹çªï¼ä»
èç¨¿ç¶æå¯ç¼è¾ï¼ */ |
| | | function handleEditRow(row: MesWmProductIssueApi.ProductIssue) { |
| | | editFormModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤ */ |
| | | async function handleSubmitRow(row: MesWmProductIssueApi.ProductIssue) { |
| | | await submitProductIssue(row.id!); |
| | |
| | | const gridColumns: VxeTableGridOptions<MesWmProductIssueApi.ProductIssue>['columns'] = [ |
| | | { field: 'code', title: '颿åç¼å·', minWidth: 150 }, |
| | | { field: 'name', title: '颿ååç§°', minWidth: 130 }, |
| | | { |
| | | field: 'taskCode', |
| | | title: 'ç产任å¡', |
| | | minWidth: 130, |
| | | formatter: ({ row }) => row.taskCode || props.taskCode || '', |
| | | }, |
| | | { field: 'workOrderCode', title: 'ç产订å', minWidth: 130 }, |
| | | { |
| | | field: 'requiredTime', |
| | |
| | | props: { type: DICT_TYPE.MES_WM_PRODUCT_ISSUE_STATUS }, |
| | | }, |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | { title: 'æä½', width: 260, fixed: 'right', slots: { default: 'actions' } }, |
| | | ]; |
| | | |
| | | const gridHeight = ref(300); |
| | |
| | | return await getProductIssuePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | workstationId: props.workstationId, |
| | | taskId: props.taskId, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | |
| | | resizeObserver = null; |
| | | }); |
| | | |
| | | // å·¥ä½ç«ååæ¶å·æ°å表 |
| | | // 任塿工ä½ç«ååæ¶å·æ°å表 |
| | | watch( |
| | | () => props.workstationId, |
| | | () => [props.taskId, props.workstationId], |
| | | () => gridApi.query(), |
| | | ); |
| | | |
| | | /** æ¯å¦å·²éä¸å·¥ä½ç«åä»»å¡ï¼æªé䏿¶ä¸å±ç¤ºè¡¨æ ¼ */ |
| | | const isReady = computed(() => !!props.taskId && !!props.workstationId); |
| | | |
| | | /** ç¼è¾å¼¹çª */ |
| | | const [EditFormModal, editFormModalApi] = useVbenModal({ |
| | | connectedComponent: ProductIssueFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** ç¼è¾å¼¹çªæä½æååå·æ°å表 */ |
| | | function onEditSuccess() { |
| | | gridApi.query(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="issue-tab"> |
| | | <!-- é¡¶é¨ï¼è¡¨å / æ°å»ºæé® --> |
| | | <div class="issue-tab__header"> |
| | | <div class="issue-tab__header-left"> |
| | | <IconifyIcon icon="lucide:clipboard-list" class="issue-tab__icon" /> |
| | | <span class="issue-tab__title">颿å</span> |
| | | <Tag v-if="isCreating" color="blue">æ°å»ºä¸</Tag> |
| | | <!-- æªéä¸å·¥ä½ç«æä»»å¡æ¶æ¾ç¤ºæç¤º --> |
| | | <div v-if="!isReady" class="issue-tab__empty"> |
| | | <Empty |
| | | :image="Empty.PRESENTED_IMAGE_SIMPLE" |
| | | description="请å
å¨å·¦ä¾§éæ©å·¥ä½ç«åä»»å¡" |
| | | /> |
| | | </div> |
| | | <template v-else> |
| | | <!-- é¡¶é¨ï¼è¡¨å / æ°å»ºæé® --> |
| | | <div class="issue-tab__header"> |
| | | <div class="issue-tab__header-left"> |
| | | <IconifyIcon icon="lucide:clipboard-list" class="issue-tab__icon" /> |
| | | <span class="issue-tab__title">颿å</span> |
| | | <Tag v-if="isCreating" color="blue">æ°å»ºä¸</Tag> |
| | | </div> |
| | | <div class="issue-tab__header-actions"> |
| | | <template v-if="!isCreating"> |
| | | <Button type="primary" size="small" @click="handleCreate"> |
| | | <template #icon><IconifyIcon icon="lucide:plus" /></template> |
| | | æ°å»ºé¢æå |
| | | </Button> |
| | | </template> |
| | | <template v-else> |
| | | <Button type="primary" size="small" @click="handleSave"> |
| | | <template #icon><IconifyIcon icon="lucide:save" /></template> |
| | | ä¿å |
| | | </Button> |
| | | <Button size="small" @click="handleCancelEdit">åæ¶</Button> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | <div class="issue-tab__header-actions"> |
| | | <template v-if="!isCreating"> |
| | | <Button type="primary" size="small" @click="handleCreate"> |
| | | <template #icon><IconifyIcon icon="lucide:plus" /></template> |
| | | æ°å»ºé¢æå |
| | | </Button> |
| | | </template> |
| | | <template v-else> |
| | | <Button type="primary" size="small" @click="handleSave"> |
| | | <template #icon><IconifyIcon icon="lucide:save" /></template> |
| | | ä¿å |
| | | </Button> |
| | | <Button size="small" @click="handleCancelEdit">åæ¶</Button> |
| | | </template> |
| | | |
| | | <!-- 表ååºåï¼ä»
æ°å»ºæ¶å±ç¤ºï¼ --> |
| | | <div v-if="isCreating" class="issue-tab__form"> |
| | | <Form /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 表ååºåï¼ä»
æ°å»ºæ¶å±ç¤ºï¼ --> |
| | | <div v-if="isCreating" class="issue-tab__form"> |
| | | <Form /> |
| | | </div> |
| | | <Divider style="margin: 12px 0" /> |
| | | |
| | | <Divider style="margin: 12px 0" /> |
| | | <!-- åºé¨ï¼é¢æè®°å½å表 --> |
| | | <div ref="listRef" class="issue-tab__list"> |
| | | <Grid table-title="é¢æè®°å½"> |
| | | <template #actions="{ row }"> |
| | | <Button |
| | | v-if="row.status === MesWmProductIssueStatusEnum.PREPARE" |
| | | type="link" |
| | | size="small" |
| | | @click="handleEditRow(row)" |
| | | > |
| | | ç¼è¾ |
| | | </Button> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmProductIssueStatusEnum.PREPARE" |
| | | title="确认æäº¤ï¼æäº¤åä¸è½ä¿®æ¹" |
| | | @confirm="handleSubmitRow(row)" |
| | | > |
| | | <Button type="link" size="small">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmProductIssueStatusEnum.PREPARE" |
| | | :title="`确认å é¤ ${row.code}ï¼`" |
| | | @confirm="handleDeleteRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>å é¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if=" |
| | | row.status === MesWmProductIssueStatusEnum.APPROVING || |
| | | row.status === MesWmProductIssueStatusEnum.APPROVED |
| | | " |
| | | title="ç¡®è®¤åæ¶ï¼åæ¶åä¸å¯æ¢å¤" |
| | | @confirm="handleCancelRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>åæ¶</Button> |
| | | </Popconfirm> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | |
| | | <!-- åºé¨ï¼é¢æè®°å½å表 --> |
| | | <div ref="listRef" class="issue-tab__list"> |
| | | <Grid table-title="é¢æè®°å½"> |
| | | <template #actions="{ row }"> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmProductIssueStatusEnum.PREPARE" |
| | | title="确认æäº¤ï¼æäº¤åä¸è½ä¿®æ¹" |
| | | @confirm="handleSubmitRow(row)" |
| | | > |
| | | <Button type="link" size="small">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmProductIssueStatusEnum.PREPARE" |
| | | :title="`确认å é¤ ${row.code}ï¼`" |
| | | @confirm="handleDeleteRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>å é¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if=" |
| | | row.status === MesWmProductIssueStatusEnum.APPROVING || |
| | | row.status === MesWmProductIssueStatusEnum.APPROVED |
| | | " |
| | | title="ç¡®è®¤åæ¶ï¼åæ¶åä¸å¯æ¢å¤" |
| | | @confirm="handleCancelRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>åæ¶</Button> |
| | | </Popconfirm> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- ç¼è¾å¼¹çª --> |
| | | <EditFormModal @success="onEditSuccess" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | min-height: 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .issue-tab__empty { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmReturnIssueApi } from '#/api/mes/wm/returnissue'; |
| | | import type { MesWmReturnIssueLineApi } from '#/api/mes/wm/returnissue/line'; |
| | | |
| | | import { computed, h, markRaw, ref } from 'vue'; |
| | | |
| | | import { |
| | | DICT_TYPE, |
| | | MesAutoCodeRuleCode, |
| | | MesProWorkOrderStatusEnum, |
| | | MesWmReturnIssueStatusEnum, |
| | | } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { Button, Divider, message, Popconfirm } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { |
| | | getReturnIssue, |
| | | submitReturnIssue, |
| | | updateReturnIssue, |
| | | } from '#/api/mes/wm/returnissue'; |
| | | import { |
| | | deleteReturnIssueLine, |
| | | getReturnIssueLinePage, |
| | | } from '#/api/mes/wm/returnissue/line'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | |
| | | import ReturnIssueLineFormModal from './ReturnIssueLineFormModal.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmReturnIssueApi.ReturnIssue>(); |
| | | |
| | | const canSubmit = computed( |
| | | () => formData.value?.status === MesWmReturnIssueStatusEnum.PREPARE, |
| | | ); |
| | | |
| | | /** æå»ºç¼è¾è¡¨å schema */ |
| | | function buildFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'éæåç¼å·', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥éæåç¼å·' }, |
| | | rules: 'required', |
| | | suffix: () => |
| | | h( |
| | | Button, |
| | | { |
| | | type: 'default', |
| | | size: 'small', |
| | | onClick: async () => { |
| | | const code = await generateAutoCode( |
| | | MesAutoCodeRuleCode.WM_RETURN_ISSUE_CODE, |
| | | ); |
| | | await formApi.setFieldValue('code', code); |
| | | }, |
| | | }, |
| | | { default: () => 'çæ' }, |
| | | ), |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'éæååç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥éæååç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'type', |
| | | label: 'éæç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_WM_RETURN_ISSUE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©éæç±»å', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'workOrderId', |
| | | label: 'ç产订å', |
| | | component: markRaw(ProWorkOrderSelect), |
| | | componentProps: { |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workstationId', |
| | | label: 'å·¥ä½ç«', |
| | | component: markRaw(MdWorkstationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©å·¥ä½ç«' }, |
| | | }, |
| | | { |
| | | fieldName: 'returnDate', |
| | | label: 'éææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | placeholder: 'è¯·éæ©éææ¥æ', |
| | | showTime: true, |
| | | valueFormat: 'x', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: buildFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | // --- ç©æè¡å表 --- |
| | | const lineGridColumns: VxeTableGridOptions<MesWmReturnIssueLineApi.ReturnIssueLine>['columns'] = [ |
| | | { field: 'itemCode', title: 'ç©æç¼ç ', minWidth: 120 }, |
| | | { field: 'itemName', title: 'ç©æåç§°', minWidth: 140 }, |
| | | { field: 'specification', title: 'è§æ ¼åå·', minWidth: 120 }, |
| | | { field: 'unitMeasureName', title: 'åä½', width: 80 }, |
| | | { field: 'quantity', title: 'éææ°é', width: 100 }, |
| | | { field: 'batchCode', title: 'æ¹æ¬¡å·', minWidth: 120 }, |
| | | { title: 'æä½', width: 140, fixed: 'right', slots: { default: 'lineActions' } }, |
| | | ]; |
| | | |
| | | const [LineGrid, lineGridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: lineGridColumns, |
| | | height: 260, |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }) => { |
| | | if (!formData.value?.id) return { list: [], total: 0 }; |
| | | return await getReturnIssueLinePage({ |
| | | issueId: formData.value.id, |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true }, |
| | | pagerConfig: { pageSize: 10 }, |
| | | } as VxeTableGridOptions<MesWmReturnIssueLineApi.ReturnIssueLine>, |
| | | }); |
| | | |
| | | /** æ·»å ç©æè¡ */ |
| | | function handleCreateLine() { |
| | | lineFormModalApi.setData({ issueId: formData.value?.id }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ç©æè¡ */ |
| | | function handleEditLine(row: MesWmReturnIssueLineApi.ReturnIssueLine) { |
| | | lineFormModalApi.setData({ id: row.id, issueId: formData.value?.id }).open(); |
| | | } |
| | | |
| | | /** å é¤ç©æè¡ */ |
| | | async function handleDeleteLine(row: MesWmReturnIssueLineApi.ReturnIssueLine) { |
| | | try { |
| | | await deleteReturnIssueLine(row.id!); |
| | | message.success('å 餿å'); |
| | | lineGridApi.query(); |
| | | } catch { |
| | | message.error('å é¤å¤±è´¥'); |
| | | } |
| | | } |
| | | |
| | | /** ç©æè¡æä½æå */ |
| | | function onLineSuccess() { |
| | | lineGridApi.query(); |
| | | } |
| | | |
| | | const [LineFormModal, lineFormModalApi] = useVbenModal({ |
| | | connectedComponent: ReturnIssueLineFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æäº¤éæå */ |
| | | async function handleSubmit() { |
| | | if (!formData.value?.id) return; |
| | | modalApi.lock(); |
| | | try { |
| | | // å
ä¿å表å |
| | | const data = |
| | | (await formApi.getValues()) as MesWmReturnIssueApi.ReturnIssue; |
| | | await updateReturnIssue({ ...formData.value, ...data }); |
| | | // åæäº¤ |
| | | await submitReturnIssue(formData.value.id); |
| | | message.success('æäº¤æå'); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | try { |
| | | const data = |
| | | (await formApi.getValues()) as MesWmReturnIssueApi.ReturnIssue; |
| | | await updateReturnIssue({ ...formData.value, ...data }); |
| | | formData.value = { ...formData.value, ...data }; |
| | | emit('success'); |
| | | message.success('ä¿åæå'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getReturnIssue(data.id); |
| | | await formApi.setValues(formData.value); |
| | | // å è½½åå·æ°ç©æè¡ |
| | | lineGridApi.query(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="ç¼è¾éæå" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | <!-- ç©æä¿¡æ¯ --> |
| | | <template v-if="formData?.id"> |
| | | <Divider style="margin: 12px 0"> |
| | | <span style="font-weight: 600; color: #fa8c16;">ç©æä¿¡æ¯</span> |
| | | </Divider> |
| | | <div class="mx-4" style="height: 300px"> |
| | | <LineGrid table-title="ç©æå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ·»å ç©æ', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | onClick: handleCreateLine, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #lineActions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç¼è¾', |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | onClick: handleEditLine.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'å é¤', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | popConfirm: { |
| | | title: `确认å é¤ç©æ ${row.itemName}ï¼`, |
| | | confirm: handleDeleteLine.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </LineGrid> |
| | | </div> |
| | | </template> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center gap-2"> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | | title="确认æäº¤è¯¥éæåï¼ãæäº¤åå°ä¸è½ä¿®æ¹ã" |
| | | @confirm="handleSubmit" |
| | | > |
| | | <Button type="primary">æäº¤</Button> |
| | | </Popconfirm> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | <LineFormModal @success="onLineSuccess" /> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmReturnIssueLineApi } from '#/api/mes/wm/returnissue/line'; |
| | | |
| | | import { computed, 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 { |
| | | createReturnIssueLine, |
| | | getReturnIssueLine, |
| | | updateReturnIssueLine, |
| | | } from '#/api/mes/wm/returnissue/line'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesWmReturnIssueLineApi.ReturnIssueLine>(); |
| | | const issueId = ref<number>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id ? 'ç¼è¾ç©æ' : 'æ·»å ç©æ'; |
| | | }); |
| | | |
| | | /** ç©æè¡è¡¨å schema */ |
| | | function buildLineFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'materialStockId', |
| | | label: 'åºåè®°å½', |
| | | component: markRaw(WmMaterialStockSelect), |
| | | componentProps: { |
| | | onChange: async (stock?: MesWmMaterialStockApi.MaterialStock) => { |
| | | await formApi.setValues({ |
| | | batchCode: stock?.batchCode, |
| | | batchId: stock?.batchId, |
| | | itemId: stock?.itemId, |
| | | quantity: stock?.quantity, |
| | | quantityMax: stock?.quantity, |
| | | }); |
| | | }, |
| | | virtualFilter: 'only', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'quantityMax', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'éææ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | placeholder: '请è¾å
¥éææ°é', |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | dependencies: { |
| | | triggerFields: ['quantityMax'], |
| | | componentProps: (values) => ({ |
| | | class: '!w-full', |
| | | max: values.quantityMax, |
| | | min: 0, |
| | | placeholder: '请è¾å
¥éææ°é', |
| | | precision: 2, |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'rqcCheckFlag', |
| | | label: 'éè¦è´¨æ£', |
| | | component: 'Switch', |
| | | rules: z.boolean().default(false), |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { disabled: true, placeholder: 'éæ©åºååèªå¨å¸¦åº' }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { componentProps: { class: 'w-full' }, formItemClass: 'col-span-1', labelWidth: 90 }, |
| | | layout: 'horizontal', |
| | | schema: buildLineFormSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesWmReturnIssueLineApi.ReturnIssueLine; |
| | | data.issueId = issueId.value; |
| | | try { |
| | | if (formData.value?.id) { |
| | | await updateReturnIssueLine({ ...data, id: formData.value.id }); |
| | | } else { |
| | | await createReturnIssueLine(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æä½æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id?: number; issueId: number }>(); |
| | | issueId.value = data.issueId; |
| | | if (!data.id) { |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getReturnIssueLine(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmReturnIssueApi } from '#/api/mes/wm/returnissue'; |
| | | |
| | | import { h, markRaw, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'; |
| | | import { computed, h, markRaw, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'; |
| | | |
| | | import { |
| | | DICT_TYPE, |
| | |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { Button, Divider, message, Popconfirm, Tag } from 'ant-design-vue'; |
| | | import { Button, Divider, Empty, message, Popconfirm, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { |
| | |
| | | } from '#/api/mes/wm/returnissue'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import ReturnIssueFormModal from './ReturnIssueFormModal.vue'; |
| | | |
| | | defineOptions({ name: 'WorkbenchReturnIssueTab' }); |
| | | |
| | |
| | | workstationId?: number; |
| | | /** å½åå·¥ä½ç«åç§° */ |
| | | workstationName?: string; |
| | | /** å½åéä¸çä»»å¡ ID */ |
| | | taskId?: number; |
| | | /** å½åéä¸çä»»å¡ç¼ç */ |
| | | taskCode?: string; |
| | | /** å½åéä¸çä»»å¡åç§° */ |
| | | taskName?: string; |
| | | /** å½åéä¸çä»»å¡å
³èå·¥å ID */ |
| | | workOrderId?: number; |
| | | }>(); |
| | | |
| | | // --- 表åç¶æ --- |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'taskId', |
| | | label: 'ç产任å¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ä»éä¸ä»»å¡èªå¨å¸¦å
¥', |
| | | disabled: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'workOrderId', |
| | | label: 'ç产订å', |
| | | component: markRaw(ProWorkOrderSelect), |
| | | componentProps: { |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | disabled: true, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'workstationId', |
| | | label: 'å·¥ä½ç«', |
| | | component: markRaw(MdWorkstationSelect), |
| | | componentProps: { placeholder: 'è¯·éæ©å·¥ä½ç«' }, |
| | | componentProps: { placeholder: 'ä»éä¸ä»»å¡èªå¨å¸¦å
¥', disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'returnDate', |
| | |
| | | isCreating.value = true; |
| | | editingId.value = undefined; |
| | | await formApi.resetForm(); |
| | | // é¢å¡«ä»»å¡ä¿¡æ¯ |
| | | if (props.taskId) { |
| | | await formApi.setFieldValue('taskId', props.taskId); |
| | | } |
| | | if (props.workOrderId) { |
| | | await formApi.setFieldValue('workOrderId', props.workOrderId); |
| | | } |
| | | if (props.workstationId) { |
| | | await formApi.setFieldValue('workstationId', props.workstationId); |
| | | } |
| | |
| | | isCreating.value = false; |
| | | editingId.value = undefined; |
| | | formApi.resetForm(); |
| | | } |
| | | |
| | | /** ç¼è¾éæåï¼æå¼ç¼è¾å¼¹çªï¼ä»
èç¨¿ç¶æå¯ç¼è¾ï¼ */ |
| | | function handleEditRow(row: MesWmReturnIssueApi.ReturnIssue) { |
| | | editFormModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | /** æäº¤ */ |
| | |
| | | props: { type: DICT_TYPE.MES_WM_RETURN_ISSUE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'taskCode', |
| | | title: 'ç产任å¡', |
| | | minWidth: 130, |
| | | formatter: ({ row }) => row.taskCode || props.taskCode || '', |
| | | }, |
| | | { field: 'workOrderCode', title: 'ç产订å', minWidth: 130 }, |
| | | { |
| | | field: 'returnDate', |
| | |
| | | props: { type: DICT_TYPE.MES_WM_RETURN_ISSUE_STATUS }, |
| | | }, |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | { title: 'æä½', width: 260, fixed: 'right', slots: { default: 'actions' } }, |
| | | ]; |
| | | |
| | | const gridHeight = ref(300); |
| | |
| | | return await getReturnIssuePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | workstationId: props.workstationId, |
| | | taskId: props.taskId, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | |
| | | resizeObserver = null; |
| | | }); |
| | | |
| | | // å·¥ä½ç«ååæ¶å·æ°å表 |
| | | // 任塿工ä½ç«ååæ¶å·æ°å表 |
| | | watch( |
| | | () => props.workstationId, |
| | | () => [props.taskId, props.workstationId], |
| | | () => gridApi.query(), |
| | | ); |
| | | |
| | | /** æ¯å¦å·²éä¸å·¥ä½ç«åä»»å¡ï¼æªé䏿¶ä¸å±ç¤ºè¡¨æ ¼ */ |
| | | const isReady = computed(() => !!props.taskId && !!props.workstationId); |
| | | |
| | | /** ç¼è¾å¼¹çª */ |
| | | const [EditFormModal, editFormModalApi] = useVbenModal({ |
| | | connectedComponent: ReturnIssueFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** ç¼è¾å¼¹çªæä½æååå·æ°å表 */ |
| | | function onEditSuccess() { |
| | | gridApi.query(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="issue-tab"> |
| | | <!-- é¡¶é¨ï¼è¡¨å / æ°å»ºæé® --> |
| | | <div class="issue-tab__header"> |
| | | <div class="issue-tab__header-left"> |
| | | <IconifyIcon icon="lucide:rotate-ccw" class="issue-tab__icon" /> |
| | | <span class="issue-tab__title">éæå</span> |
| | | <Tag v-if="isCreating" color="orange">æ°å»ºä¸</Tag> |
| | | <!-- æªéä¸å·¥ä½ç«æä»»å¡æ¶æ¾ç¤ºæç¤º --> |
| | | <div v-if="!isReady" class="issue-tab__empty"> |
| | | <Empty |
| | | :image="Empty.PRESENTED_IMAGE_SIMPLE" |
| | | description="请å
å¨å·¦ä¾§éæ©å·¥ä½ç«åä»»å¡" |
| | | /> |
| | | </div> |
| | | <template v-else> |
| | | <!-- é¡¶é¨ï¼è¡¨å / æ°å»ºæé® --> |
| | | <div class="issue-tab__header"> |
| | | <div class="issue-tab__header-left"> |
| | | <IconifyIcon icon="lucide:rotate-ccw" class="issue-tab__icon" /> |
| | | <span class="issue-tab__title">éæå</span> |
| | | <Tag v-if="isCreating" color="orange">æ°å»ºä¸</Tag> |
| | | </div> |
| | | <div class="issue-tab__header-actions"> |
| | | <template v-if="!isCreating"> |
| | | <Button type="primary" size="small" @click="handleCreate"> |
| | | <template #icon><IconifyIcon icon="lucide:plus" /></template> |
| | | æ°å»ºéæå |
| | | </Button> |
| | | </template> |
| | | <template v-else> |
| | | <Button type="primary" size="small" @click="handleSave"> |
| | | <template #icon><IconifyIcon icon="lucide:save" /></template> |
| | | ä¿å |
| | | </Button> |
| | | <Button size="small" @click="handleCancelEdit">åæ¶</Button> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | <div class="issue-tab__header-actions"> |
| | | <template v-if="!isCreating"> |
| | | <Button type="primary" size="small" @click="handleCreate"> |
| | | <template #icon><IconifyIcon icon="lucide:plus" /></template> |
| | | æ°å»ºéæå |
| | | </Button> |
| | | </template> |
| | | <template v-else> |
| | | <Button type="primary" size="small" @click="handleSave"> |
| | | <template #icon><IconifyIcon icon="lucide:save" /></template> |
| | | ä¿å |
| | | </Button> |
| | | <Button size="small" @click="handleCancelEdit">åæ¶</Button> |
| | | </template> |
| | | |
| | | <!-- 表ååºåï¼ä»
æ°å»ºæ¶å±ç¤ºï¼ --> |
| | | <div v-if="isCreating" class="issue-tab__form"> |
| | | <Form /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 表ååºåï¼ä»
æ°å»ºæ¶å±ç¤ºï¼ --> |
| | | <div v-if="isCreating" class="issue-tab__form"> |
| | | <Form /> |
| | | </div> |
| | | <Divider style="margin: 12px 0" /> |
| | | |
| | | <Divider style="margin: 12px 0" /> |
| | | <!-- åºé¨ï¼éæè®°å½å表 --> |
| | | <div ref="listRef" class="issue-tab__list"> |
| | | <Grid table-title="éæè®°å½"> |
| | | <template #actions="{ row }"> |
| | | <Button |
| | | v-if="row.status === MesWmReturnIssueStatusEnum.PREPARE" |
| | | type="link" |
| | | size="small" |
| | | @click="handleEditRow(row)" |
| | | > |
| | | ç¼è¾ |
| | | </Button> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmReturnIssueStatusEnum.PREPARE" |
| | | title="确认æäº¤ï¼æäº¤åä¸è½ä¿®æ¹" |
| | | @confirm="handleSubmitRow(row)" |
| | | > |
| | | <Button type="link" size="small">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmReturnIssueStatusEnum.PREPARE" |
| | | :title="`确认å é¤ ${row.code}ï¼`" |
| | | @confirm="handleDeleteRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>å é¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if=" |
| | | row.status === MesWmReturnIssueStatusEnum.CONFIRMED || |
| | | row.status === MesWmReturnIssueStatusEnum.APPROVING || |
| | | row.status === MesWmReturnIssueStatusEnum.APPROVED |
| | | " |
| | | title="ç¡®è®¤åæ¶ï¼åæ¶åä¸å¯æ¢å¤" |
| | | @confirm="handleCancelRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>åæ¶</Button> |
| | | </Popconfirm> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | |
| | | <!-- åºé¨ï¼éæè®°å½å表 --> |
| | | <div ref="listRef" class="issue-tab__list"> |
| | | <Grid table-title="éæè®°å½"> |
| | | <template #actions="{ row }"> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmReturnIssueStatusEnum.PREPARE" |
| | | title="确认æäº¤ï¼æäº¤åä¸è½ä¿®æ¹" |
| | | @confirm="handleSubmitRow(row)" |
| | | > |
| | | <Button type="link" size="small">æäº¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if="row.status === MesWmReturnIssueStatusEnum.PREPARE" |
| | | :title="`确认å é¤ ${row.code}ï¼`" |
| | | @confirm="handleDeleteRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>å é¤</Button> |
| | | </Popconfirm> |
| | | <Popconfirm |
| | | v-if=" |
| | | row.status === MesWmReturnIssueStatusEnum.CONFIRMED || |
| | | row.status === MesWmReturnIssueStatusEnum.APPROVING || |
| | | row.status === MesWmReturnIssueStatusEnum.APPROVED |
| | | " |
| | | title="ç¡®è®¤åæ¶ï¼åæ¶åä¸å¯æ¢å¤" |
| | | @confirm="handleCancelRow(row)" |
| | | > |
| | | <Button type="link" size="small" danger>åæ¶</Button> |
| | | </Popconfirm> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- ç¼è¾å¼¹çª --> |
| | | <EditFormModal @success="onEditSuccess" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | min-height: 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .issue-tab__empty { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| | |
| | | /> |
| | | <ProductIssueTab |
| | | v-else-if="activeTab === 'product-issue'" |
| | | :task-id="selectedTask?.id" |
| | | :task-code="selectedTask?.code" |
| | | :task-name="selectedTask?.name" |
| | | :work-order-id="selectedTask?.workOrderId" |
| | | :workstation-id="workstationId" |
| | | :workstation-name="workstationName" |
| | | /> |
| | | <ReturnIssueTab |
| | | v-else-if="activeTab === 'return-issue'" |
| | | :task-id="selectedTask?.id" |
| | | :task-code="selectedTask?.code" |
| | | :task-name="selectedTask?.name" |
| | | :work-order-id="selectedTask?.workOrderId" |
| | | :workstation-id="workstationId" |
| | | :workstation-name="workstationName" |
| | | /> |