feat(system): 新增部门管理和岗位管理的 Excel 导入功能
- 岗位管理 (Post):
- 新增 `import-form.vue` 导入弹窗组件,包含文件上传及是否覆盖选项。
- 在 `post/data.ts` 补充导入表单相关的 Schema 定义。
- 完善 `post/index.ts` API 接口,增加获取导入模板和上传数据的方法。
- 列表页 `index.vue` 增加导入按钮并接入导入组件。
- 部门管理 (Dept):
- 新增 `import-form.vue` 导入弹窗组件。
- 增加细粒度权限控制,导入按钮配置 `auth: ['system:dept:import']` 权限标识。
- 优化弹窗 UI 布局,添加了关于“上级部门填写全路径,为空即为顶级部门”的下置提示文案,并修复了表单标签与上传按钮的垂直对齐问题。
- 增强异常处理逻辑,针对后端返回的 `failureDeptNames` (部分导入失败数据),引入 `notification.warning` 实现导入结果的详细报错展示。
- 完善 `dept/data.ts` 补充导入 Schema,并在 `dept/index.ts` 补齐导入相关 API。
| | |
| | | export async function deleteDeptList(ids: number[]) { |
| | | return requestClient.delete(`/system/dept/delete-list?ids=${ids.join(',')}`); |
| | | } |
| | | |
| | | /** ä¸è½½é¨é¨å¯¼å
¥æ¨¡æ¿ */ |
| | | export function importDeptTemplate() { |
| | | return requestClient.download('/system/dept/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥é¨é¨ */ |
| | | export function importDept(file: File, updateSupport: boolean) { |
| | | return requestClient.upload('/system/dept/import', { |
| | | file, |
| | | updateSupport, |
| | | }); |
| | | } |
| | |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** ä¸è½½å²ä½å¯¼å
¥æ¨¡æ¿ */ |
| | | export function importPostTemplate() { |
| | | return requestClient.download('/system/post/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥å²ä½ */ |
| | | export function importPost(file: File, updateSupport: boolean) { |
| | | return requestClient.upload('/system/post/import', { |
| | | file, |
| | | updateSupport, |
| | | }); |
| | | } |
| | |
| | | } from "#/api/mes/md/item"; |
| | | import { $t } from "#/locales"; |
| | | import { MdItemTypeTree } from "#/views/mes/md/item/type/components"; |
| | | import { PrinterLabel } from "#/views/wls/barcode/components"; |
| | | // import { PrinterLabel } from "#/views/wls/barcode/components"; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from "./data"; |
| | | import Form from "./modules/form.vue"; |
| | |
| | | }, |
| | | }, |
| | | ]" /> |
| | | <PrinterLabel :biz-id="row.id" |
| | | <!-- <PrinterLabel :biz-id="row.id" |
| | | :biz-code="row.code" |
| | | biz-type="ITEM" /> |
| | | biz-type="ITEM" /> --> |
| | | </div> |
| | | </template> |
| | | </Grid> |
| | |
| | | <Tabs.TabPane key="substitute" tab="æ¿ä»£å"> |
| | | <Empty description="æ¿ä»£åï¼å¾
å®ç°ï¼" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="sip" tab="SIP"> |
| | | <Tabs.TabPane key="sip" tab="æ£éªæå¯¼ä¹¦(SIP)"> |
| | | <ProductSipForm :form-type="formType" :item-id="formData.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="sop" tab="SOP"> |
| | | <Tabs.TabPane key="sop" tab="ä½ä¸æå¯¼ä¹¦(SOP)"> |
| | | <ProductSopForm :form-type="formType" :item-id="formData.id" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | |
| | | ); |
| | | |
| | | const isReadOnly = computed(() => props.formType === 'detail'); // æ¯å¦åªè¯» |
| | | const title = computed(() => props.kind); // å½ååªä½ç±»åæ é¢ |
| | | const title = computed(() => |
| | | props.kind === 'SIP' ? 'æ£éªæå¯¼ä¹¦(SIP)' : 'ä½ä¸æå¯¼ä¹¦(SOP)', |
| | | ); |
| | | |
| | | /** å°å®æ´ URL 转为ç¸å¯¹è·¯å¾ï¼ç¡®ä¿èµ°å端代ç */ |
| | | function resolveImageUrl(url?: string): string { |
| | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { BarcodeBizTypeEnum } from '@vben/constants'; |
| | | // import { BarcodeBizTypeEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | |
| | | getWorkstationPage, |
| | | } from '#/api/mes/md/workstation'; |
| | | import { $t } from '#/locales'; |
| | | import { BarcodeDetail } from '#/views/wls/barcode/components'; |
| | | // import { BarcodeDetail } from '#/views/wls/barcode/components'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | |
| | | } |
| | | |
| | | /** æ¥çå·¥ä½ç«æ¡ç */ |
| | | function handleBarcode(row: MesMdWorkstationApi.Workstation) { |
| | | if (!row.id) { |
| | | return; |
| | | } |
| | | barcodeDetailRef.value?.openByBusiness( |
| | | row.id, |
| | | BarcodeBizTypeEnum.WORKSTATION, |
| | | row.code, |
| | | row.name, |
| | | ); |
| | | } |
| | | // function handleBarcode(row: MesMdWorkstationApi.Workstation) { |
| | | // if (!row.id) { |
| | | // return; |
| | | // } |
| | | // barcodeDetailRef.value?.openByBusiness( |
| | | // row.id, |
| | | // BarcodeBizTypeEnum.WORKSTATION, |
| | | // row.code, |
| | | // row.name, |
| | | // ); |
| | | // } |
| | | |
| | | /** 导åºå·¥ä½ç« */ |
| | | async function handleExport() { |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <!-- <BarcodeDetail ref="barcodeDetailRef" /> --> |
| | | |
| | | <Grid table-title="å·¥ä½ç«å表"> |
| | | <template #toolbar-tools> |
| | |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æ¡ç ', |
| | | type: 'link', |
| | | auth: ['mes:md-workstation:query'], |
| | | onClick: handleBarcode.bind(null, row), |
| | | }, |
| | | // { |
| | | // label: 'æ¡ç ', |
| | | // type: 'link', |
| | | // auth: ['mes:md-workstation:query'], |
| | | // onClick: handleBarcode.bind(null, row), |
| | | // }, |
| | | ]" |
| | | /> |
| | | </template> |
| | |
| | | import { computed, ref } from "vue"; |
| | | |
| | | import { useVbenModal } from "@vben/common-ui"; |
| | | import { BarcodeBizTypeEnum } from "@vben/constants"; |
| | | // import { BarcodeBizTypeEnum } from "@vben/constants"; |
| | | |
| | | import { Button, message, Tabs } from "ant-design-vue"; |
| | | |
| | |
| | | updateWorkstation, |
| | | } from "#/api/mes/md/workstation"; |
| | | import { $t } from "#/locales"; |
| | | import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | // import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | |
| | | import { useFormSchema } from "../data"; |
| | | import MachineList from "./machine-list.vue"; |
| | |
| | | const formType = ref<FormType>("create"); // è¡¨åæ¨¡å¼ |
| | | const subTabsName = ref("machine"); // å½åèµæºé¡µç¾ |
| | | const formData = ref<MesMdWorkstationApi.Workstation>(); |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | |
| | | const isDetail = computed(() => formType.value === "detail"); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | |
| | | }); |
| | | |
| | | /** æ¥çå·¥ä½ç«æ¡ç */ |
| | | function handleBarcode() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | barcodeDetailRef.value?.openByBusiness( |
| | | formData.value.id, |
| | | BarcodeBizTypeEnum.WORKSTATION, |
| | | formData.value.code, |
| | | formData.value.name |
| | | ); |
| | | } |
| | | // function handleBarcode() { |
| | | // if (!formData.value?.id) { |
| | | // return; |
| | | // } |
| | | // barcodeDetailRef.value?.openByBusiness( |
| | | // formData.value.id, |
| | | // BarcodeBizTypeEnum.WORKSTATION, |
| | | // formData.value.code, |
| | | // formData.value.name |
| | | // ); |
| | | // } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | |
| | | </Tabs> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| | | <Button v-if="isDetail && formData?.id" |
| | | <!-- <Button v-if="isDetail && formData?.id" |
| | | type="primary" |
| | | @click="handleBarcode"> |
| | | æ¥çæ¡ç |
| | | </Button> |
| | | </Button> --> |
| | | </div> |
| | | </template> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <!-- <BarcodeDetail ref="barcodeDetailRef" /> --> |
| | | </Modal> |
| | | </template> |
| | |
| | | import { computed, ref } from "vue"; |
| | | |
| | | import { useVbenModal } from "@vben/common-ui"; |
| | | import { BarcodeBizTypeEnum } from "@vben/constants"; |
| | | // import { BarcodeBizTypeEnum } from "@vben/constants"; |
| | | |
| | | import { Button, message } from "ant-design-vue"; |
| | | |
| | |
| | | updateWorkshop, |
| | | } from "#/api/mes/md/workstation/workshop"; |
| | | import { $t } from "#/locales"; |
| | | import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | // import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | |
| | | import { useFormSchema } from "../data"; |
| | | |
| | | const emit = defineEmits(["success"]); |
| | | const formType = ref<FormType>("create"); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<MesMdWorkshopApi.Workshop>(); |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | |
| | | const isDetail = computed(() => formType.value === "detail"); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | |
| | | }); |
| | | |
| | | /** æ¥çè½¦é´æ¡ç */ |
| | | function handleBarcode() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | barcodeDetailRef.value?.openByBusiness( |
| | | formData.value.id, |
| | | BarcodeBizTypeEnum.WORKSHOP, |
| | | formData.value.code, |
| | | formData.value.name |
| | | ); |
| | | } |
| | | // function handleBarcode() { |
| | | // if (!formData.value?.id) { |
| | | // return; |
| | | // } |
| | | // barcodeDetailRef.value?.openByBusiness( |
| | | // formData.value.id, |
| | | // BarcodeBizTypeEnum.WORKSHOP, |
| | | // formData.value.code, |
| | | // formData.value.name |
| | | // ); |
| | | // } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | |
| | | <template v-if="isDetail && formData?.id" |
| | | #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| | | <Button @click="handleBarcode">æ¥çæ¡ç </Button> |
| | | <!-- <Button @click="handleBarcode">æ¥çæ¡ç </Button> --> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <!-- <BarcodeDetail ref="barcodeDetailRef" /> --> |
| | | </template> |
| | |
| | | |
| | | import { Page, useVbenModal } from "@vben/common-ui"; |
| | | import { |
| | | BarcodeBizTypeEnum, |
| | | MesProWorkOrderStatusEnum, |
| | | MesProWorkOrderTypeEnum, |
| | | } from "@vben/constants"; |
| | |
| | | } from "#/api/mes/pro/workorder"; |
| | | import { $t } from "#/locales"; |
| | | import ArchiveDetail from '#/views/mes/pd/archive/modules/detail.vue'; |
| | | import { BarcodeDetail } from '#/views/wls/barcode/components'; |
| | | // import { BarcodeDetail } from '#/views/wls/barcode/components'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from "./data"; |
| | | import Form from "./modules/form.vue"; |
| | |
| | | }); |
| | | |
| | | const archiveDetailRef = ref<InstanceType<typeof ArchiveDetail>>(); |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç 详æ
å¼¹çª |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | |
| | | } |
| | | |
| | | /** æ¥çè®¢åæ¡ç */ |
| | | function handleBarcode(row: MesProWorkOrderApi.WorkOrder) { |
| | | barcodeDetailRef.value?.openByBusiness( |
| | | row.id!, |
| | | BarcodeBizTypeEnum.WORKORDER, |
| | | row.code, |
| | | row.name |
| | | ); |
| | | } |
| | | // function handleBarcode(row: MesProWorkOrderApi.WorkOrder) { |
| | | // barcodeDetailRef.value?.openByBusiness( |
| | | // row.id!, |
| | | // BarcodeBizTypeEnum.WORKORDER, |
| | | // row.code, |
| | | // row.name |
| | | // ); |
| | | // } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | |
| | | confirm: handleCancel.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'æ¡ç ', |
| | | type: 'link', |
| | | auth: ['mes:pro-work-order:query'], |
| | | onClick: handleBarcode.bind(null, row), |
| | | }, |
| | | // { |
| | | // label: 'æ¡ç ', |
| | | // type: 'link', |
| | | // auth: ['mes:pro-work-order:query'], |
| | | // onClick: handleBarcode.bind(null, row), |
| | | // }, |
| | | { |
| | | label: '彿¡£', |
| | | type: 'link', |
| | |
| | | ]" /> |
| | | </template> |
| | | </Grid> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <!-- <BarcodeDetail ref="barcodeDetailRef" /> --> |
| | | <ArchiveDetail ref="archiveDetailRef" /> |
| | | </Page> |
| | | </template> |
| | |
| | | |
| | | import { confirm, useVbenModal } from "@vben/common-ui"; |
| | | import { |
| | | BarcodeBizTypeEnum, |
| | | MesProWorkOrderStatusEnum, |
| | | } from "@vben/constants"; |
| | | |
| | |
| | | } from "#/api/mes/pro/workorder"; |
| | | import { batchSaveWorkOrderProcess, getWorkOrderProcessListByWorkOrderId } from "#/api/mes/pro/workorder/process"; |
| | | import { $t } from "#/locales"; |
| | | import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | // import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | |
| | | import { useFormSchema } from "../data"; |
| | | import PredictDelivery from "./ai/predict-delivery.vue"; |
| | |
| | | const formData = ref<MesProWorkOrderApi.WorkOrder>(); |
| | | const originalSnapshot = ref(""); |
| | | const subTabsName = ref("process"); |
| | | const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); |
| | | // const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); |
| | | const processListRef = ref<InstanceType<typeof ProcessList>>(); |
| | | const currentProductId = ref<number>(); // å½åéä¸ç产å IDï¼ç¨äºå·¥åºå è½½ï¼ |
| | | const tempProcessList = ref<MesProWorkOrderProcessApi.WorkOrderProcess[]>([]); // 临æ¶å·¥åºå表 |
| | |
| | | } |
| | | |
| | | /** æ¥çè®¢åæ¡ç */ |
| | | function handleBarcode() { |
| | | if (!formData.value?.id) { |
| | | return; |
| | | } |
| | | barcodeDetailRef.value?.openByBusiness( |
| | | formData.value.id, |
| | | BarcodeBizTypeEnum.WORKORDER, |
| | | formData.value.code, |
| | | formData.value.name |
| | | ); |
| | | } |
| | | // function handleBarcode() { |
| | | // if (!formData.value?.id) { |
| | | // return; |
| | | // } |
| | | // barcodeDetailRef.value?.openByBusiness( |
| | | // formData.value.id, |
| | | // BarcodeBizTypeEnum.WORKORDER, |
| | | // formData.value.code, |
| | | // formData.value.name |
| | | // ); |
| | | // } |
| | | |
| | | /** å·¥åºå表åååè° */ |
| | | function handleProcessListChange(list: MesProWorkOrderProcessApi.WorkOrderProcess[]) { |
| | |
| | | @click="handleSave"> |
| | | ä¿åä¿®æ¹ |
| | | </Button> |
| | | <Button v-if="formType === 'detail' && formData?.id" |
| | | <!-- <Button v-if="formType === 'detail' && formData?.id" |
| | | @click="handleBarcode"> |
| | | æ¥çæ¡ç |
| | | </Button> |
| | | </Button> --> |
| | | <Button @click="modalApi.close()">åæ¶</Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | <BarcodeDetail ref="barcodeDetailRef" /> |
| | | <!-- <BarcodeDetail ref="barcodeDetailRef" /> --> |
| | | </template> |
| | |
| | | ]; |
| | | } |
| | | |
| | | /** é¨é¨å¯¼å
¥ç表å */ |
| | | export function useImportFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'file', |
| | | label: 'é¨é¨æ°æ®', |
| | | component: 'Upload', |
| | | rules: 'required', |
| | | help: 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶', |
| | | }, |
| | | { |
| | | fieldName: 'updateSupport', |
| | | label: 'æ¯å¦è¦ç', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | help: 'æ¯å¦æ´æ°å·²ç»åå¨çé¨é¨æ°æ®', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<SystemDeptApi.Dept>['columns'] { |
| | | return [ |
| | |
| | | |
| | | import { useGridColumns } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import ImportForm from './modules/import-form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: ImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | /** å建é¨é¨ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** 导å
¥é¨é¨ */ |
| | | function handleImport() { |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** æ·»å ä¸çº§é¨é¨ */ |
| | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="é¨é¨å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.import', ['é¨é¨']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['system:dept:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: isExpanded ? 'æ¶ç¼©' : 'å±å¼', |
| | | type: 'primary', |
| | | onClick: handleExpand, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { h } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message, notification, Upload } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { importDept, importDeptTemplate } from '#/api/system/dept'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useImportFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 120, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useImportFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = await formApi.getValues(); |
| | | try { |
| | | const response = (await importDept(data.file, data.updateSupport)) as any; |
| | | // å
³éå¹¶æç¤º |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | |
| | | const resultData = response?.data || response || {}; |
| | | const failureNames = resultData.failureDeptNames || {}; |
| | | |
| | | if (Object.keys(failureNames).length > 0) { |
| | | const errorNodes = Object.entries(failureNames).map(([name, reason]) => { |
| | | return h('div', `${name}: ${reason}`); |
| | | }); |
| | | |
| | | notification.warning({ |
| | | message: '导å
¥å®æï¼é¨åé¨é¨å¯¼å
¥å¤±è´¥', |
| | | description: () => h('div', errorNodes), |
| | | duration: null, |
| | | }); |
| | | } else { |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | /** ä¸ä¼ å */ |
| | | function beforeUpload(file: FileType) { |
| | | formApi.setFieldValue('file', file); |
| | | return false; |
| | | } |
| | | |
| | | /** ä¸è½½æ¨¡ç */ |
| | | async function handleDownload() { |
| | | const data = await importDeptTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'é¨é¨å¯¼å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="导å
¥é¨é¨" class="w-1/3"> |
| | | <Form class="mx-4"> |
| | | <template #file> |
| | | <div class="w-full"> |
| | | <Upload |
| | | :max-count="1" |
| | | accept=".xls,.xlsx" |
| | | :before-upload="beforeUpload" |
| | | > |
| | | <Button type="primary"> éæ© Excel æä»¶ </Button> |
| | | </Upload> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | <div class="mx-4 mt-2 mb-4 text-gray-500 text-xs"> |
| | | æç¤ºï¼æ¨¡æ¿éçâä¸çº§é¨é¨â请填åå
¨è·¯å¾ï¼å¦:æ»å
¬å¸/å京åå
¬å¸ï¼ |
| | | <div>ä¸çº§é¨é¨ç©ºçå³ä¸ºé¡¶çº§é¨é¨ã</div> |
| | | </div> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| | | <Button @click="handleDownload"> ä¸è½½å¯¼å
¥æ¨¡æ¿ </Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | |
| | | ]; |
| | | } |
| | | |
| | | /** å²ä½å¯¼å
¥ç表å */ |
| | | export function useImportFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'file', |
| | | label: 'å²ä½æ°æ®', |
| | | component: 'Upload', |
| | | rules: 'required', |
| | | help: 'ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶', |
| | | }, |
| | | { |
| | | fieldName: 'updateSupport', |
| | | label: 'æ¯å¦è¦ç', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ¯', |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | rules: z.boolean().default(false), |
| | | help: 'æ¯å¦æ´æ°å·²ç»åå¨çå²ä½æ°æ®', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import ImportForm from './modules/import-form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: ImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | /** å建å²ä½ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** 导å
¥å²ä½ */ |
| | | function handleImport() { |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** ç¼è¾å²ä½ */ |
| | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="å²ä½å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.import'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['system:post:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.deleteBatch'), |
| | | type: 'primary', |
| | | danger: true, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message, Upload } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { importPost, importPostTemplate } from '#/api/system/post'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useImportFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 120, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useImportFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = await formApi.getValues(); |
| | | try { |
| | | await importPost(data.file, data.updateSupport); |
| | | // å
³éå¹¶æç¤º |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | /** ä¸ä¼ å */ |
| | | function beforeUpload(file: FileType) { |
| | | formApi.setFieldValue('file', file); |
| | | return false; |
| | | } |
| | | |
| | | /** ä¸è½½æ¨¡ç */ |
| | | async function handleDownload() { |
| | | const data = await importPostTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'å²ä½å¯¼å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="导å
¥å²ä½" class="w-1/3"> |
| | | <Form class="mx-4"> |
| | | <template #file> |
| | | <div class="w-full"> |
| | | <Upload |
| | | :max-count="1" |
| | | accept=".xls,.xlsx" |
| | | :before-upload="beforeUpload" |
| | | > |
| | | <Button type="primary"> éæ© Excel æä»¶ </Button> |
| | | </Upload> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| | | <Button @click="handleDownload"> ä¸è½½å¯¼å
¥æ¨¡æ¿ </Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | </template> |