| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | import type { HrmEmployeeContractApi } from '#/api/hrm/employee/contract'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { Button, message, Progress, Switch, Upload } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | importEmployeeContract, |
| | | importEmployeeContractTemplate, |
| | | } from '#/api/hrm/employee/contract'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const fileList = ref<FileType[]>([]); |
| | | const updateSupport = ref(false); |
| | | const importResult = ref<HrmEmployeeContractApi.ImportResult>(); |
| | | const importing = ref(false); |
| | | const progressPercent = ref(0); |
| | | let progressTimer: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (fileList.value.length === 0) { |
| | | message.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶'); |
| | | return; |
| | | } |
| | | if (importing.value) return; |
| | | modalApi.lock(); |
| | | importing.value = true; |
| | | startMockProgress(); |
| | | try { |
| | | importResult.value = await importEmployeeContract( |
| | | fileList.value[0] as File, |
| | | updateSupport.value, |
| | | ); |
| | | // 宿ï¼è¿åº¦æ¡èµ°æ»¡ |
| | | stopMockProgress(); |
| | | progressPercent.value = 100; |
| | | await delay(500); |
| | | message.success('导å
¥å®æ'); |
| | | emit('success'); |
| | | } finally { |
| | | stopMockProgress(); |
| | | importing.value = false; |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | /** 模æè¿åº¦æ¡ï¼è¯·æ±æé´ä» 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 importEmployeeContractTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'åå·¥åå导å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | |
| | | const createCount = computed(() => importResult.value?.createNames?.length ?? 0); |
| | | const updateCount = computed(() => importResult.value?.updateNames?.length ?? 0); |
| | | const failureCount = computed( |
| | | () => Object.keys(importResult.value?.failureNames ?? {}).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-4"> |
| | | <span class="mr-2">ååå·¥ãåååç±»åãåå¼å§æ¥æçååå·²å卿¶è¦çæ´æ°è¯¥è¡ï¼</span> |
| | | <Switch v-model:checked="updateSupport" /> |
| | | </div> |
| | | <div class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500"> |
| | | <p>ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ï¼æ¨¡æ¿ä¸âåå·¥å§å/ååç±»å/ååæéç±»åâæåç§°æä¸æå¡«åï¼ååç±»åå¦ï¼å³å¨ååãå³å¡ååï¼ã</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-blue-600">è¦çæ´æ°ï¼{{ updateCount }} 份</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.failureNames" |
| | | :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> |