liu
3 天以前 08b86cccfc8227ab065ee913f809609ef708486d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<script lang="ts" setup>
import type { FileType } from 'ant-design-vue/es/upload/interface';
import type { HrmEmployeeApi } from '#/api/hrm/employee';
 
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 { importEmployee, importEmployeeTemplate } from '#/api/hrm/employee';
 
const emit = defineEmits(['success']);
 
const fileList = ref<FileType[]>([]);
const updateSupport = ref(false);
const importResult = ref<HrmEmployeeApi.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 importEmployee(
        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 importEmployeeTemplate();
  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-[640px]">
    <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 text-gray-500">
        仅允许导入 xls、xlsx 格式文件;模板中"部门名称/岗位名称/用户角色"填写名称,角色多个用逗号分隔
      </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>