feat: 发票导入、质检批量导入与检测台账、回款必填、报价单号提示、退货物料搜索
- CRM:发票批量导入弹窗、合同导入模板、回款必填、报价单号提示
- MES:IQC/IPQC/OQC/RQC 批量导入弹窗与接口、检测结果台账页面
- WLS:退货申请明细物料下拉支持搜索
| | |
| | | `/crm/invoice/simple-list?contractId=${contractId}`, |
| | | ); |
| | | } |
| | | |
| | | /** å¼ç¥¨å¯¼å
¥ç»æ */ |
| | | export interface InvoiceImportResult { |
| | | /** å建æåçå¼ç¥¨ç¼å· */ |
| | | createList?: string[]; |
| | | /** 导å
¥å¤±è´¥çéåï¼key 为ãå票å·ç xxxãæã第 N è¡ã */ |
| | | failureList?: Record<string, string>; |
| | | } |
| | | |
| | | /** ä¸è½½å¼ç¥¨å¯¼å
¥æ¨¡æ¿ */ |
| | | export function importInvoiceTemplate() { |
| | | return requestClient.download('/crm/invoice/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥å¼ç¥¨ */ |
| | | export function importInvoice(file: File) { |
| | | return requestClient.upload<InvoiceImportResult>('/crm/invoice/import', { |
| | | file, |
| | | }); |
| | | } |
| | |
| | | majorQuantity?: number; // 严éç¼ºé·æ°é |
| | | minorQuantity?: number; // è½»å¾®ç¼ºé·æ°é |
| | | checkResult?: number; // æ£æµç»æ |
| | | indicatorResultSummary?: string; // æ£æµç»æï¼æ£éªé¡¹å®æµå¼æ±æ»ï¼å¦ã温度:15ï¼çµé»:1ãï¼ |
| | | inspectDate?: number; // æ£æµæ¥æ |
| | | inspectorUserId?: number; // æ£æµäººåç¨æ· ID |
| | | inspectorNickname?: string; // æ£æµäººåæµç§°ï¼å
³èæ¥è¯¢ï¼ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** è¿ç¨æ£éªå导å
¥ç»æ */ |
| | | export interface ImportResult { |
| | | createList?: string[]; // æ°å¢æåçæ£éªåç¼å· |
| | | failureList?: Record<string, string>; // 导å
¥å¤±è´¥æç»ï¼key ä¸ºãæ£éªåç¼å· Xãæã第 N è¡ã |
| | | } |
| | | } |
| | | |
| | |
| | | export function exportIpqc(params: any) { |
| | | return requestClient.download('/mes/qc/ipqc/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸è½½è¿ç¨æ£éªå导å
¥æ¨¡æ¿ */ |
| | | export function importIpqcTemplate() { |
| | | return requestClient.download('/mes/qc/ipqc/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥è¿ç¨æ£éªå */ |
| | | export function importIpqc(file: File) { |
| | | return requestClient.upload<MesQcIpqcApi.ImportResult>('/mes/qc/ipqc/import', { |
| | | file, |
| | | }); |
| | | } |
| | |
| | | majorQuantity?: number; // 严éç¼ºé·æ°é |
| | | minorQuantity?: number; // è½»å¾®ç¼ºé·æ°é |
| | | checkResult?: number; // æ£æµç»æ |
| | | indicatorResultSummary?: string; // æ£æµç»æï¼æ£éªé¡¹å®æµå¼æ±æ»ï¼å¦ã温度:15ï¼çµé»:1ãï¼ |
| | | receiveDate?: number; // æ¥ææ¥æ |
| | | inspectDate?: number; // æ£æµæ¥æ |
| | | inspector?: string; // æ£æµäººåï¼æµç§°ï¼ |
| | |
| | | inspectorNickname?: string; // æ£æµäººåæµç§°ï¼å
³èæ¥è¯¢ï¼ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** æ¥ææ£éªå导å
¥ç»æ */ |
| | | export interface ImportResult { |
| | | createList?: string[]; // æ°å¢æåçæ£éªåç¼å· |
| | | failureList?: Record<string, string>; // 导å
¥å¤±è´¥æç»ï¼key ä¸ºãæ£éªåç¼å· Xãæã第 N è¡ã |
| | | } |
| | | } |
| | | |
| | |
| | | export function exportIqc(params: any) { |
| | | return requestClient.download('/mes/qc/iqc/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸è½½æ¥ææ£éªå导å
¥æ¨¡æ¿ */ |
| | | export function importIqcTemplate() { |
| | | return requestClient.download('/mes/qc/iqc/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥æ¥ææ£éªå */ |
| | | export function importIqc(file: File) { |
| | | return requestClient.upload<MesQcIqcApi.ImportResult>('/mes/qc/iqc/import', { |
| | | file, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesQcInspectionLedgerApi { |
| | | /** MES æ£æµç»æå°è´¦ï¼ä¸è¡ä¸ä¸ªæ£æµé¡¹ç»æï¼æ£éªåæªå½å
¥æ£æµé¡¹æ¶è¯¥ç»åæ®µä¸ºç©ºï¼ */ |
| | | export interface Ledger { |
| | | qcType?: number; // åç±»ï¼æ£éªç±»åï¼ï¼1 æ¥ææ£éª / 2 è¿ç¨æ£éª / 3 åºè´§æ£éª / 4 éè´§æ£éª |
| | | qcId?: number; // æ£éªå ID |
| | | code?: string; // æ£éªåç¼å· |
| | | name?: string; // æ£éªååç§° |
| | | status?: number; // åæ®ç¶æ |
| | | itemId?: number; // ç©æç¼å· |
| | | itemCode?: string; // ç©æç¼ç |
| | | itemName?: string; // ç©æåç§° |
| | | itemSpecification?: string; // è§æ ¼åå· |
| | | unitName?: string; // 计éåä½åç§° |
| | | batchNo?: string; // æ¹æ¬¡å· |
| | | checkQuantity?: number; // æ£æµæ°é |
| | | qualifiedQuantity?: number; // åæ ¼æ°é |
| | | unqualifiedQuantity?: number; // ä¸åæ ¼æ°é |
| | | checkResult?: number; // æ£æµç»æ |
| | | inspectDate?: string; // æ£æµæ¥æ |
| | | inspectorUserId?: number; // æ£æµäººåç¼å· |
| | | inspectorName?: string; // æ£æµäººåæµç§° |
| | | indicatorId?: number; // æ£æµé¡¹ç¼å· |
| | | indicatorName?: string; // æ£æµé¡¹åç§° |
| | | indicatorValue?: string; // æ£æµå¼ï¼å®æµå¼ï¼ |
| | | indicatorRemark?: string; // æ£æµé¡¹å¤æ³¨ |
| | | judgementBasis?: string; // å¤å®ä¾æ®ï¼å¦ 10~20ãâ¤20ãâ¥10ãæ åå¼ 15 |
| | | remark?: string; // æ£éªå夿³¨ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢æ£æµç»æå°è´¦å页ï¼åç±»æ£éªåç»ä¸æ¥è¯¢ï¼ */ |
| | | export function getInspectionLedgerPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesQcInspectionLedgerApi.Ledger>>( |
| | | '/mes/qc/inspection-ledger/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** å¯¼åºæ£æµç»æå°è´¦ */ |
| | | export function exportInspectionLedger(params: Record<string, unknown>) { |
| | | return requestClient.download('/mes/qc/inspection-ledger/export-excel', { |
| | | params, |
| | | }); |
| | | } |
| | |
| | | majorQuantity?: number; // 严éç¼ºé·æ°é |
| | | minorQuantity?: number; // è½»å¾®ç¼ºé·æ°é |
| | | checkResult?: number; // æ£æµç»æ |
| | | indicatorResultSummary?: string; // æ£æµç»æï¼æ£éªé¡¹å®æµå¼æ±æ»ï¼å¦ã温度:15ï¼çµé»:1ãï¼ |
| | | outDate?: number; // åºè´§æ¥æ |
| | | inspectDate?: number; // æ£æµæ¥æ |
| | | inspectorUserId?: number; // æ£æµäººåç¨æ· ID |
| | | inspectorNickname?: string; // æ£æµäººåæµç§°ï¼å
³èæ¥è¯¢ï¼ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | |
| | | /** åºè´§æ£éªå导å
¥ç»æ */ |
| | | export interface ImportResult { |
| | | createList?: string[]; // æ°å¢æåçæ£éªåç¼å· |
| | | failureList?: Record<string, string>; // 导å
¥å¤±è´¥æç»ï¼key ä¸ºãæ£éªåç¼å· Xãæã第 N è¡ã |
| | | } |
| | | } |
| | | |
| | |
| | | export function exportOqc(params: any) { |
| | | return requestClient.download('/mes/qc/oqc/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸è½½åºè´§æ£éªå导å
¥æ¨¡æ¿ */ |
| | | export function importOqcTemplate() { |
| | | return requestClient.download('/mes/qc/oqc/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥åºè´§æ£éªå */ |
| | | export function importOqc(file: File) { |
| | | return requestClient.upload<MesQcOqcApi.ImportResult>('/mes/qc/oqc/import', { |
| | | file, |
| | | }); |
| | | } |
| | |
| | | qualifiedQuantity?: number; // åæ ¼åæ°é |
| | | unqualifiedQuantity?: number; // ä¸åæ ¼æ°é |
| | | checkResult?: number; // æ£æµç»æ |
| | | indicatorResultSummary?: string; // æ£æµç»æï¼æ£éªé¡¹å®æµå¼æ±æ»ï¼å¦ã温度:15ï¼çµé»:1ãï¼ |
| | | inspectDate?: number; // æ£æµæ¥æ |
| | | inspectorUserId?: number; // æ£æµäººåç¨æ· ID |
| | | inspectorNickname?: string; // æ£æµäººåæµç§°ï¼å
³èæ¥è¯¢ï¼ |
| | |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import ImportForm from './modules/import-form.vue'; |
| | | |
| | | const { push } = useRouter(); |
| | | const sceneType = ref('1'); |
| | |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: ImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æå¼å¯¼å
¥å¼ç¥¨å¼¹çª */ |
| | | function handleImport() { |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid> |
| | | <template #toolbar-actions> |
| | | <Tabs class="w-full" @change="handleChangeSceneType"> |
| | |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: '导å
¥', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['crm:invoice:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | import type { InvoiceImportResult } from '#/api/crm/invoice'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { Button, message, Progress, Upload } from 'ant-design-vue'; |
| | | |
| | | import { importInvoice, importInvoiceTemplate } from '#/api/crm/invoice'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const fileList = ref<FileType[]>([]); |
| | | const importResult = ref<InvoiceImportResult>(); |
| | | const importing = ref(false); |
| | | const progressPercent = ref(0); |
| | | let progressTimer: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | let shouldClose = false; |
| | | if (fileList.value.length === 0) { |
| | | message.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶'); |
| | | return; |
| | | } |
| | | if (importing.value) return; |
| | | modalApi.lock(); |
| | | importing.value = true; |
| | | startMockProgress(); |
| | | try { |
| | | importResult.value = await importInvoice(fileList.value[0] as File); |
| | | // 宿ï¼è¿åº¦æ¡èµ°æ»¡ |
| | | stopMockProgress(); |
| | | progressPercent.value = 100; |
| | | await delay(300); |
| | | emit('success'); |
| | | const failedCount = Object.keys( |
| | | importResult.value?.failureList ?? {}, |
| | | ).length; |
| | | if (failedCount > 0) { |
| | | message.warning( |
| | | `导å
¥å®æï¼æ°å¢ ${createCount.value} æ¡ï¼å¤±è´¥ ${failedCount} æ¡ï¼è¯·æ¥ç䏿¹å¤±è´¥æç»`, |
| | | ); |
| | | } else { |
| | | message.success('导å
¥å®æ'); |
| | | shouldClose = true; |
| | | } |
| | | } finally { |
| | | stopMockProgress(); |
| | | importing.value = false; |
| | | modalApi.unlock(); |
| | | if (shouldClose) { |
| | | await modalApi.close(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | /** 模æè¿åº¦æ¡ï¼è¯·æ±æé´ä» 0 èµ°å° 90% */ |
| | | function startMockProgress() { |
| | | progressPercent.value = 0; |
| | | progressTimer = setInterval(() => { |
| | | if (progressPercent.value < 90) { |
| | | progressPercent.value = Math.min( |
| | | 90, |
| | | progressPercent.value + Math.random() * 12, |
| | | ); |
| | | } |
| | | }, 300); |
| | | } |
| | | |
| | | function stopMockProgress() { |
| | | if (progressTimer) { |
| | | clearInterval(progressTimer); |
| | | progressTimer = undefined; |
| | | } |
| | | } |
| | | |
| | | function delay(ms: number) { |
| | | return new Promise((resolve) => setTimeout(resolve, ms)); |
| | | } |
| | | |
| | | function beforeUpload(file: FileType) { |
| | | fileList.value = [file]; |
| | | importResult.value = undefined; |
| | | return false; |
| | | } |
| | | |
| | | function handleRemove() { |
| | | fileList.value = []; |
| | | importResult.value = undefined; |
| | | } |
| | | |
| | | async function handleDownloadTemplate() { |
| | | const data = await importInvoiceTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'å¼ç¥¨å¯¼å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | |
| | | const createCount = computed(() => importResult.value?.createList?.length ?? 0); |
| | | const failureCount = computed( |
| | | () => Object.keys(importResult.value?.failureList ?? {}).length, |
| | | ); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="导å
¥å¼ç¥¨" class="w-[720px]"> |
| | | <div class="mx-4"> |
| | | <Upload |
| | | :max-count="1" |
| | | accept=".xls,.xlsx" |
| | | :file-list="fileList" |
| | | :before-upload="beforeUpload" |
| | | @remove="handleRemove" |
| | | > |
| | | <Button type="primary">éæ© Excel æä»¶</Button> |
| | | </Upload> |
| | | <div class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500"> |
| | | <p>ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ï¼å¿
å¡«åï¼ååç¼å·ãå票类åãå¼ç¥¨éé¢(å
)ãâååç¼å·âéä¸ç³»ç»ä¸å·²å®¡æ ¸éè¿çååç¼å·ä¸è´ï¼ç¼å·éå®å
¨ä¸è´ï¼ï¼å®¢æ·ç±ååèªå¨å¸¦å
¥ï¼æ éå¡«åï¼âå票类åâæåå
¸åç§°å¡«åï¼å¢å¼ç¨ä¸ç¨å票 / å¢å¼ç¨æ®éå票 / çµåå票ï¼ï¼âå¼ç¥¨éé¢(å
)âéå¤§äº 0 ä¸ä¸è¶
è¿è¯¥ååå©ä½å¯å¼ç¥¨éé¢ã</p> |
| | | <p>âå¼ç¥¨ç¼å·âç空åç±ç³»ç»èªå¨çæï¼å¡«åæ¶ä¸å¯ä¸ç³»ç»ä¸å·²æå¼ç¥¨ç¼å·éå¤ï¼âå¼ç¥¨æ¥æâæ ¼å¼ä¸º 2026-01-01 10:00:00 æ 2026-01-01ï¼ç空åå½åæ¶é´ã</p> |
| | | <p>éè¡å¯¼å
¥ï¼å¤±è´¥è¡ä¸å½±åæåè¡ï¼å¯¼å
¥çå¼ç¥¨è´è´£äºº = å½åç»å½ç¨æ·ï¼ç¶æä¸ºâæªæäº¤âï¼å¯å¨å表ä¸ç»§ç»ç¼è¾ãä¸ä¼ å票éä»¶åæäº¤å®¡æ¹ã</p> |
| | | </div> |
| | | <!-- 导å
¥è¿åº¦æ¡ --> |
| | | <div v-if="importing" class="mt-4"> |
| | | <div class="mb-2 text-sm text-gray-600"> |
| | | æ£å¨å¯¼å
¥å¼ç¥¨æ°æ®ï¼è¯·ç¨å... |
| | | </div> |
| | | <Progress :percent="progressPercent" status="active" /> |
| | | </div> |
| | | <!-- 导å
¥ç»æ --> |
| | | <div |
| | | v-if="importResult" |
| | | class="mt-4 rounded-md border border-gray-200 bg-gray-50 p-3" |
| | | > |
| | | <div class="mb-2 font-medium">导å
¥ç»æ</div> |
| | | <div class="mb-2 flex gap-4"> |
| | | <span class="text-green-600">æ°å¢ï¼{{ createCount }} æ¡</span> |
| | | <span class="text-red-600">失败ï¼{{ failureCount }} æ¡</span> |
| | | </div> |
| | | <div v-if="failureCount > 0" class="max-h-40 overflow-y-auto"> |
| | | <div |
| | | v-for="(reason, name) in importResult.failureList" |
| | | :key="name" |
| | | class="text-sm leading-6 text-red-500" |
| | | > |
| | | {{ name }}ï¼{{ reason }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #prepend-footer> |
| | | <Button @click="handleDownloadTemplate">ä¸è½½å¯¼å
¥æ¨¡æ¿</Button> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | |
| | | fieldName: 'invoiceId', |
| | | label: 'å
³èå¼ç¥¨', |
| | | component: 'Select', |
| | | rules: 'required', |
| | | help: 'å¯ä¸éï¼ä¸é表示该ç¬å款æä¸å
³èå¼ç¥¨', |
| | | dependencies: { |
| | | triggerFields: ['contractId', 'invoiceId'], |
| | | disabled: (values) => !values.contractId || !!values.id, |
| | |
| | | } |
| | | const invoices = await getInvoiceSimpleList(values.contractId); |
| | | return { |
| | | allowClear: true, |
| | | options: invoices.map((item) => { |
| | | const remain = item.remainingInvoicePrice ?? item.price ?? 0; |
| | | return { |
| | |
| | | value: item.id, |
| | | }; |
| | | }), |
| | | placeholder: 'è¯·éæ©å
³èå¼ç¥¨', |
| | | placeholder: 'è¯·éæ©å
³èå¼ç¥¨ï¼å¯ä¸éï¼', |
| | | onChange: (value: number) => { |
| | | form.setFieldValue('planId', undefined); |
| | | form.setFieldValue('returnTime', undefined); |
| | | form.setFieldValue('returnType', undefined); |
| | | form.setFieldValue('price', undefined); |
| | | const invoice = invoices.find((item) => item.id === value); |
| | | if (invoice) { |
| | | form.setFieldValue( |
| | |
| | | fieldName: 'planId', |
| | | label: '忬¾ææ°', |
| | | component: 'Select', |
| | | rules: 'required', |
| | | help: 'æ¤åæ°å¯å¨å款计åé
ç½®', |
| | | help: 'æ¤åæ°å¯å¨å款计åé
ç½®ï¼å¯ä¸éï¼ä¸éåä¸å ç¨åæ¬¾è®¡åææ°', |
| | | dependencies: { |
| | | triggerFields: ['contractId'], |
| | | disabled: (values) => !values.contractId || values.id, |
| | |
| | | values.contractId, |
| | | ); |
| | | return { |
| | | allowClear: true, |
| | | options: plans.map((item) => ({ |
| | | disabled: !!item.receivableId, |
| | | label: `第 ${item.period} æ`, |
| | | value: item.id, |
| | | })), |
| | | placeholder: 'è¯·éæ©åæ¬¾ææ°', |
| | | placeholder: 'è¯·éæ©åæ¬¾ææ°ï¼å¯ä¸éï¼', |
| | | onChange: async (value: any) => { |
| | | if (!value) { |
| | | form.setFieldValue('returnTime', undefined); |
| | |
| | | <Alert |
| | | v-if="showInvoiceHint" |
| | | type="warning" |
| | | message="请å
宿å¼ç¥¨å¹¶ä¸ä¼ å票éä»¶ï¼åè¿è¡å款æä½" |
| | | message="å¦éå
³èå¼ç¥¨ï¼è¯·å
宿å¼ç¥¨å¹¶ä¸ä¼ å票éä»¶ï¼åè¿è¡å款æä½ï¼ãå
³èå¼ç¥¨ãã忬¾ææ°ãå为éå¡«ï¼ä¸éå¯ç´æ¥å¡«å忬¾ä¿¡æ¯" |
| | | show-icon |
| | | class="mb-4" |
| | | /> |
| | |
| | | fieldName: 'no', |
| | | label: 'æ¥ä»·åå·', |
| | | component: 'Input', |
| | | help: 'ç空ç±ç³»ç»èªå¨çæï¼å¡«åå使ç¨è¯¥ç¼å·ï¼éå
¨åºå¯ä¸', |
| | | componentProps: { |
| | | placeholder: 'ä¿åæ¶èªå¨çæ', |
| | | disabled: true, |
| | | placeholder: 'æ¥ä»·åå·èªå®ä¹', |
| | | maxLength: 32, |
| | | }, |
| | | }, |
| | | { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message, Progress, Upload } from 'ant-design-vue'; |
| | | |
| | | /** 导å
¥ç»æï¼IQC/IPQC/OQC ä¸ä¸ªå¯¼å
¥æ¥å£è¿åç»æä¸è´ï¼ */ |
| | | interface ImportResult { |
| | | createList?: string[]; |
| | | failureList?: Record<string, string>; |
| | | } |
| | | |
| | | interface ImportModalData { |
| | | /** å¼¹çªæ é¢ */ |
| | | title?: string; |
| | | /** 模æ¿ä¸è½½å½æ°ï¼requestClient.download(...) è¿å Blob */ |
| | | templateApi: () => Promise<Blob>; |
| | | /** 导å
¥å½æ°ï¼multipart ä¸ä¼ Excelï¼ */ |
| | | importApi: (file: File) => Promise<ImportResult>; |
| | | /** ä¸è½½çæ¨¡æ¿æä»¶å */ |
| | | fileName: string; |
| | | /** ç°è²æç¤ºåºçè¯´ææ®µè½ */ |
| | | hints?: string[]; |
| | | } |
| | | |
| | | defineOptions({ name: 'QcImportFormModal' }); |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const modalData = ref<ImportModalData>(); |
| | | const title = ref('æ¹é导å
¥'); |
| | | const hints = ref<string[]>([]); |
| | | const fileList = ref<FileType[]>([]); |
| | | const importResult = ref<ImportResult>(); |
| | | const importing = ref(false); |
| | | const progressPercent = ref(0); |
| | | let progressTimer: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | const createCount = computed(() => importResult.value?.createList?.length ?? 0); |
| | | const failureCount = computed( |
| | | () => Object.keys(importResult.value?.failureList ?? {}).length, |
| | | ); |
| | | |
| | | /** 模æè¿åº¦æ¡ï¼è¯·æ±æé´ä» 0 èµ°å° 90% */ |
| | | function startMockProgress() { |
| | | progressPercent.value = 0; |
| | | progressTimer = setInterval(() => { |
| | | if (progressPercent.value < 90) { |
| | | progressPercent.value = Math.min( |
| | | 90, |
| | | progressPercent.value + Math.random() * 12, |
| | | ); |
| | | } |
| | | }, 300); |
| | | } |
| | | |
| | | function stopMockProgress() { |
| | | if (progressTimer) { |
| | | clearInterval(progressTimer); |
| | | progressTimer = undefined; |
| | | } |
| | | } |
| | | |
| | | function delay(ms: number) { |
| | | return new Promise((resolve) => setTimeout(resolve, ms)); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const data = modalData.value; |
| | | if (!data) { |
| | | return; |
| | | } |
| | | if (fileList.value.length === 0) { |
| | | message.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶'); |
| | | return; |
| | | } |
| | | if (importing.value) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | importing.value = true; |
| | | startMockProgress(); |
| | | let shouldClose = false; |
| | | try { |
| | | importResult.value = await data.importApi(fileList.value[0] as File); |
| | | stopMockProgress(); |
| | | progressPercent.value = 100; |
| | | await delay(300); |
| | | emit('success'); |
| | | if (failureCount.value > 0) { |
| | | message.warning( |
| | | `导å
¥å®æï¼æ°å¢ ${createCount.value} æ¡ï¼å¤±è´¥ ${failureCount.value} æ¡ï¼è¯·æ¥ç䏿¹å¤±è´¥æç»`, |
| | | ); |
| | | } else { |
| | | message.success('导å
¥å®æ'); |
| | | shouldClose = true; |
| | | } |
| | | } finally { |
| | | stopMockProgress(); |
| | | importing.value = false; |
| | | modalApi.unlock(); |
| | | if (shouldClose) { |
| | | await modalApi.close(); |
| | | } |
| | | } |
| | | }, |
| | | onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | modalData.value = undefined; |
| | | fileList.value = []; |
| | | importResult.value = undefined; |
| | | importing.value = false; |
| | | progressPercent.value = 0; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<ImportModalData>(); |
| | | modalData.value = data; |
| | | title.value = data?.title ?? 'æ¹é导å
¥'; |
| | | hints.value = data?.hints ?? []; |
| | | modalApi.setState({ confirmText: '导å
¥' }); |
| | | }, |
| | | }); |
| | | |
| | | /** ä¸ä¼ åï¼åªä¿çä¸ä¸ªæä»¶ï¼äº¤ç± onConfirm ç»ä¸æäº¤ */ |
| | | function beforeUpload(file: FileType) { |
| | | fileList.value = [file]; |
| | | importResult.value = undefined; |
| | | return false; |
| | | } |
| | | |
| | | function handleRemove() { |
| | | fileList.value = []; |
| | | importResult.value = undefined; |
| | | } |
| | | |
| | | async function handleDownloadTemplate() { |
| | | const data = modalData.value; |
| | | if (!data) { |
| | | return; |
| | | } |
| | | const blob = await data.templateApi(); |
| | | downloadFileFromBlobPart({ fileName: data.fileName, source: blob }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="title" class="w-[720px]"> |
| | | <div class="mx-4"> |
| | | <Upload |
| | | :max-count="1" |
| | | accept=".xls,.xlsx" |
| | | :file-list="fileList" |
| | | :before-upload="beforeUpload" |
| | | @remove="handleRemove" |
| | | > |
| | | <Button type="primary">éæ© Excel æä»¶</Button> |
| | | </Upload> |
| | | <div |
| | | v-if="hints.length > 0" |
| | | class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500" |
| | | > |
| | | <p v-for="(hint, index) in hints" :key="index">{{ hint }}</p> |
| | | </div> |
| | | <!-- 导å
¥è¿åº¦æ¡ --> |
| | | <div v-if="importing" class="mt-4"> |
| | | <div class="mb-2 text-sm text-gray-600"> |
| | | æ£å¨å¯¼å
¥æ£éªåæ°æ®ï¼è¯·ç¨å... |
| | | </div> |
| | | <Progress :percent="progressPercent" status="active" /> |
| | | </div> |
| | | <!-- 导å
¥ç»æ --> |
| | | <div |
| | | v-if="importResult" |
| | | class="mt-4 rounded-md border border-gray-200 bg-gray-50 p-3" |
| | | > |
| | | <div class="mb-2 font-medium">导å
¥ç»æ</div> |
| | | <div class="mb-2 flex gap-4"> |
| | | <span class="text-green-600">æ°å¢ï¼{{ createCount }} æ¡</span> |
| | | <span class="text-red-600">失败ï¼{{ failureCount }} æ¡</span> |
| | | </div> |
| | | <div v-if="createCount > 0" class="text-sm text-gray-600"> |
| | | æ°å¢æ£éªå为ãå¾
æ£éªãç¶æï¼è¯·å¨å表ä¸ç¼è¾å¹¶å½å
¥æ£æµç»æ |
| | | </div> |
| | | <div v-if="failureCount > 0" class="mt-2 max-h-40 overflow-y-auto"> |
| | | <div |
| | | v-for="(reason, name) in importResult.failureList" |
| | | :key="name" |
| | | class="text-sm leading-6 text-red-500" |
| | | > |
| | | {{ name }}ï¼{{ reason }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #prepend-footer> |
| | | <Button @click="handleDownloadTemplate">ä¸è½½å¯¼å
¥æ¨¡æ¿</Button> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'indicatorResultSummary', |
| | | title: 'æ£æµç»æï¼æ£éªé¡¹ï¼', |
| | | minWidth: 200, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'inspectDate', |
| | | title: 'æ£æµæ¥æ', |
| | | width: 120, |
| | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteIpqc, exportIpqc, getIpqcPage } from '#/api/mes/qc/ipqc'; |
| | | import { |
| | | deleteIpqc, |
| | | exportIpqc, |
| | | getIpqcPage, |
| | | importIpqc, |
| | | importIpqcTemplate, |
| | | } from '#/api/mes/qc/ipqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue'; |
| | | import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: QcImportFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ¹é导å
¥è¿ç¨æ£éªå */ |
| | | function handleImport() { |
| | | importModalApi |
| | | .setData({ |
| | | title: 'æ¹é导å
¥è¿ç¨æ£éªå', |
| | | templateApi: importIpqcTemplate, |
| | | importApi: importIpqc, |
| | | fileName: 'è¿ç¨æ£éªå导å
¥æ¨¡æ¿.xls', |
| | | hints: [ |
| | | 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ï¼å¿
å¡«åï¼æ£éªååç§°ãæ£éªç±»åãç产工åå·ãå·¥ä½åç§°ãåæ ¼åæ°éãä¸åæ ¼åæ°éã', |
| | | 'ã产åç©æç±ç产工åèªå¨æ¨å¯¼ãï¼æ¨¡æ¿ä¸å¡«ç©æï¼ãæ£éªç±»åãæåå
¸åç§°å¡«åï¼èªå¶å·¥åºæ£éª / 馿£ / å·¡æ£ / èªæ£ / æåæ£éªï¼ã', |
| | | 'ãç产工åå·ãéä¸ãç产管ç - ç产工åãä¸çå·¥åå·ä¸è´ï¼ãå·¥ä½åç§°ãéä¸ãåºç¡æ°æ® - å·¥ä½ç®¡çãä¸çåç§°ä¸è´ã', |
| | | 'åæ ¼åæ°é + ä¸åæ ¼åæ°é å¿
é¡»çäºãæ£æµæ°éãï¼ç空æ¶èªå¨å两è
ä¹åï¼ï¼å·¥åº + æåº + å
¶ä»åºå ä¹åä¸å¾è¶
è¿ä¸åæ ¼åæ°éã', |
| | | 'ãè´¨æ£æ¹æ¡ãå¡«æ¹æ¡åç§°ï¼ç空åæç©æèªå¨å¹é
IPQC æ¹æ¡ï¼ãæ£æµäººåãç空åå½åç»å½ç¨æ·ï¼ãæ£æµæ¥æãå¯ç空ã', |
| | | 'éè¡å¯¼å
¥ï¼å¤±è´¥è¡ä¸å½±åæåè¡ï¼å¯¼å
¥åçæ£éªå为ãå¾
æ£éªãç¶æï¼å¯å¨å表ä¸ç»§ç»ç¼è¾ãå½å
¥æ£æµç»æã', |
| | | ], |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | /** å建è¿ç¨æ£éªå */ |
| | |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <ReportModal /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="è¿ç¨æ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹é导å
¥', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['mes:qc-ipqc:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'indicatorResultSummary', |
| | | title: 'æ£æµç»æï¼æ£éªé¡¹ï¼', |
| | | minWidth: 200, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'receiveDate', |
| | | title: 'æ¥ææ¥æ', |
| | | width: 120, |
| | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteIqc, exportIqc, getIqcPage } from '#/api/mes/qc/iqc'; |
| | | import { |
| | | deleteIqc, |
| | | exportIqc, |
| | | getIqcPage, |
| | | importIqc, |
| | | importIqcTemplate, |
| | | } from '#/api/mes/qc/iqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue'; |
| | | import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: QcImportFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ¹é导å
¥æ¥ææ£éªå */ |
| | | function handleImport() { |
| | | importModalApi |
| | | .setData({ |
| | | title: 'æ¹é导å
¥æ¥ææ£éªå', |
| | | templateApi: importIqcTemplate, |
| | | importApi: importIqc, |
| | | fileName: 'æ¥ææ£éªå导å
¥æ¨¡æ¿.xls', |
| | | hints: [ |
| | | 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ï¼å¿
å¡«åï¼æ£éªååç§°ã产åç©æç¼ç ãåæ ¼åæ°éãä¸åæ ¼åæ°éãæ£æµæ¥æãæ¥ææ¥æã', |
| | | 'ã产åç©æç¼ç ãéä¸ãç©æç®¡çãä¸çç¼ç å®å
¨ä¸è´ï¼ãä¾åºååç§°ãéä¸ãä¾åºå管çãä¸çåç§°ä¸è´ï¼ç空åè¯¥åæ ä¾åºåã', |
| | | 'åæ ¼åæ°é + ä¸åæ ¼åæ°é å¿
é¡»çäºãæ¬æ¬¡æ¥æ¶æ°éãï¼ç空æ¶èªå¨å两è
ä¹åï¼ï¼ãæ£æµç»æãæåå
¸åç§°å¡«åï¼æ ¡éªéè¿ / æ ¡éªä¸éè¿ï¼ã', |
| | | 'ãè´¨æ£æ¹æ¡ãå¡«æ¹æ¡åç§°ï¼ç空åæç©æèªå¨å¹é
IQC æ¹æ¡ï¼ãæ£æµäººåãç空åå½åç»å½ç¨æ·ï¼æ¥ææ¯æ 2026-01-01 10:00:00 æ 2026-01-01ã', |
| | | 'éè¡å¯¼å
¥ï¼å¤±è´¥è¡ä¸å½±åæåè¡ï¼å¯¼å
¥åçæ£éªå为ãå¾
æ£éªãç¶æï¼å¯å¨å表ä¸ç»§ç»ç¼è¾ãå½å
¥æ£æµç»æã', |
| | | ], |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | /** åå»ºæ¥ææ£éªå */ |
| | |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <ReportModal /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="æ¥ææ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹é导å
¥', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['mes:qc-iqc:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesQcInspectionLedgerApi } from '#/api/mes/qc/ledger'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'qcType', |
| | | label: 'åç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_QC_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©åç±»', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'inspectDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | ...getRangePickerDefaultProps(), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'æ£éªåç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ£éªåç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产åç©æ', |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©äº§åç©æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'batchNo', |
| | | label: 'æ¹æ¬¡å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¹æ¬¡å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'checkResult', |
| | | label: 'æ£æµç»æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_QC_CHECK_RESULT, 'number'), |
| | | placeholder: 'è¯·éæ©æ£æµç»æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'inspectorUserId', |
| | | label: 'æ£æµäººå', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: getSimpleUserList, |
| | | labelField: 'nickname', |
| | | placeholder: 'è¯·éæ©æ£æµäººå', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µï¼ä¸è¡ä¸ä¸ªæ£æµé¡¹ç»æï¼ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesQcInspectionLedgerApi.Ledger>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'qcType', |
| | | title: 'åç±»', |
| | | width: 130, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_QC_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'code', |
| | | title: 'æ£éªåç¼å·', |
| | | width: 170, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'æ£éªååç§°', |
| | | minWidth: 180, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'itemCode', |
| | | title: 'ç©æç¼ç ', |
| | | width: 130, |
| | | }, |
| | | { |
| | | field: 'itemName', |
| | | title: 'ç©æåç§°', |
| | | minWidth: 150, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'itemSpecification', |
| | | title: 'è§æ ¼åå·', |
| | | minWidth: 140, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'batchNo', |
| | | title: 'æ¹æ¬¡å·', |
| | | width: 130, |
| | | }, |
| | | { |
| | | field: 'indicatorName', |
| | | title: 'æ£æµé¡¹', |
| | | minWidth: 140, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'indicatorValue', |
| | | title: 'æ£æµå¼', |
| | | minWidth: 120, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'judgementBasis', |
| | | title: 'å¤å®ä¾æ®', |
| | | minWidth: 120, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'checkResult', |
| | | title: 'æ£æµç»æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_QC_CHECK_RESULT }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'checkQuantity', |
| | | title: 'æ£æµæ°é', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'qualifiedQuantity', |
| | | title: 'åæ ¼æ°é', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'unqualifiedQuantity', |
| | | title: 'ä¸åæ ¼æ°é', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'inspectorName', |
| | | title: 'æ£æµäººå', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'inspectDate', |
| | | title: 'æ£æµæ¥æ', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'åæ®ç¶æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_ORDER_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | title: '夿³¨', |
| | | minWidth: 160, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesQcInspectionLedgerApi } from '#/api/mes/qc/ledger'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | exportInspectionLedger, |
| | | getInspectionLedgerPage, |
| | | } from '#/api/mes/qc/ledger'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | |
| | | /** å¯¼åºæ£æµç»æå°è´¦ï¼ä¸å表å
±ç¨åä¸å¥çéæ¡ä»¶ï¼ */ |
| | | async function handleExport() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | const data = await exportInspectionLedger(formValues); |
| | | downloadFileFromBlobPart({ fileName: 'æ£æµç»æå°è´¦.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getInspectionLedgerPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | // å°è´¦è¡æ å¯ä¸ idï¼å䏿£éªåå¤è¡ qcId éå¤ï¼ï¼ä¸è®¾ keyFieldï¼ç± vxe å
é¨çæè¡æ è¯ |
| | | rowConfig: { |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesQcInspectionLedgerApi.Ledger>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Grid table-title="æ£æµç»æå°è´¦"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:qc-inspection-ledger:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'indicatorResultSummary', |
| | | title: 'æ£æµç»æï¼æ£éªé¡¹ï¼', |
| | | minWidth: 200, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'outDate', |
| | | title: 'åºè´§æ¥æ', |
| | | width: 120, |
| | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteOqc, exportOqc, getOqcPage } from '#/api/mes/qc/oqc'; |
| | | import { |
| | | deleteOqc, |
| | | exportOqc, |
| | | getOqcPage, |
| | | importOqc, |
| | | importOqcTemplate, |
| | | } from '#/api/mes/qc/oqc'; |
| | | import { $t } from '#/locales'; |
| | | import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue'; |
| | | import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue'; |
| | | import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: QcImportFormModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ¹é导å
¥åºè´§æ£éªå */ |
| | | function handleImport() { |
| | | importModalApi |
| | | .setData({ |
| | | title: 'æ¹é导å
¥åºè´§æ£éªå', |
| | | templateApi: importOqcTemplate, |
| | | importApi: importOqc, |
| | | fileName: 'åºè´§æ£éªå导å
¥æ¨¡æ¿.xls', |
| | | hints: [ |
| | | 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ï¼å¿
å¡«åï¼æ£éªååç§°ã产åç©æç¼ç ã客æ·åç§°ãæ¬æ¬¡åºè´§æ°éãåæ ¼åæ°éãä¸åæ ¼åæ°éãæ£æµæ¥æãåºè´§æ¥æã', |
| | | 'ã产åç©æç¼ç ãéä¸ãç©æç®¡çãä¸çç¼ç å®å
¨ä¸è´ï¼ã客æ·åç§°ãéä¸ãCRM 客æ·ç®¡çãä¸çåç§°ä¸è´ï¼ä¸å¿
é¡»å¨å½åç»å½ç¨æ·çå®¢æ·æ°æ®æéèå´å
ã', |
| | | 'åæ ¼åæ°é + ä¸åæ ¼åæ°é å¿
é¡»çäºãæ£æµæ°éãï¼ç空æ¶èªå¨å两è
ä¹åï¼ï¼ãæ£æµç»æãæåå
¸åç§°å¡«åï¼æ ¡éªéè¿ / æ ¡éªä¸éè¿ï¼ã', |
| | | 'ãè´¨æ£æ¹æ¡ãå¡«æ¹æ¡åç§°ï¼ç空åæç©æèªå¨å¹é
OQC æ¹æ¡ï¼ãæ£æµäººåãç空åå½åç»å½ç¨æ·ï¼æ¥ææ¯æ 2026-01-01 10:00:00 æ 2026-01-01ã', |
| | | 'éè¡å¯¼å
¥ï¼å¤±è´¥è¡ä¸å½±åæåè¡ï¼å¯¼å
¥åçæ£éªå为ãå¾
æ£éªãç¶æï¼å¯å¨å表ä¸ç»§ç»ç¼è¾ãå½å
¥æ£æµç»æã', |
| | | ], |
| | | }) |
| | | .open(); |
| | | } |
| | | |
| | | /** å建åºè´§æ£éªå */ |
| | |
| | | <FormModal @success="handleRefresh" /> |
| | | <ExportRangeModal /> |
| | | <ReportModal /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="åºè´§æ£éªåå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹é导å
¥', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['mes:qc-oqc:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'indicatorResultSummary', |
| | | title: 'æ£æµç»æï¼æ£éªé¡¹ï¼', |
| | | minWidth: 200, |
| | | showOverflow: 'tooltip', |
| | | }, |
| | | { |
| | | field: 'inspectDate', |
| | | title: 'æ£æµæ¥æ', |
| | | width: 160, |
| | |
| | | label: `${item.productName || ''}ï¼${item.productBarCode || ''}ï¼`, |
| | | value: item.productId, |
| | | })), |
| | | showSearch: true, |
| | | onChange: (value: number | undefined) => { |
| | | const selected = items.find((i) => i.productId === value); |
| | | onItemChange?.(selected); |