@sugarat/easypicker2-client 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.env +6 -0
  2. package/.env.production +3 -0
  3. package/.env.test +4 -0
  4. package/.eslintignore +0 -0
  5. package/.eslintrc.json +57 -0
  6. package/.github/workflows/main.yml +61 -0
  7. package/.prettierrc.js +9 -0
  8. package/LICENSE +21 -0
  9. package/README.md +86 -0
  10. package/auto-imports.d.ts +6 -0
  11. package/components.d.ts +56 -0
  12. package/docker/ep_backup/easypicker2.sql +214 -0
  13. package/docker/ep_backup/mongodb/easypicker2/action.bson +0 -0
  14. package/docker/ep_backup/mongodb/easypicker2/action.metadata.json +1 -0
  15. package/docker/ep_backup/mongodb/easypicker2/log.bson +0 -0
  16. package/docker/ep_backup/mongodb/easypicker2/log.metadata.json +1 -0
  17. package/docker/ep_backup/user-config.json +176 -0
  18. package/docs/.env +1 -0
  19. package/docs/.env.production +2 -0
  20. package/docs/.vitepress/config.ts +204 -0
  21. package/docs/.vitepress/theme/bg.png +0 -0
  22. package/docs/.vitepress/theme/index.scss +41 -0
  23. package/docs/.vitepress/theme/index.ts +5 -0
  24. package/docs/author.md +24 -0
  25. package/docs/auto-imports.d.ts +6 -0
  26. package/docs/components.d.ts +17 -0
  27. package/docs/deploy/design/api.md +3 -0
  28. package/docs/deploy/design/db.md +3 -0
  29. package/docs/deploy/design/index.md +3 -0
  30. package/docs/deploy/design/shell.md +9 -0
  31. package/docs/deploy/faq.md +86 -0
  32. package/docs/deploy/index.md +9 -0
  33. package/docs/deploy/local.md +275 -0
  34. package/docs/deploy/online-new.md +610 -0
  35. package/docs/deploy/online.md +683 -0
  36. package/docs/deploy/qiniu.md +183 -0
  37. package/docs/index.md +40 -0
  38. package/docs/introduction/about/code.md +26 -0
  39. package/docs/introduction/about/index.md +33 -0
  40. package/docs/introduction/feature/index.md +3 -0
  41. package/docs/plan/log.md +333 -0
  42. package/docs/plan/todo.md +127 -0
  43. package/docs/plan/wish.md +29 -0
  44. package/docs/praise/index.md +45 -0
  45. package/docs/public/favicon.ico +0 -0
  46. package/docs/public/logo.png +0 -0
  47. package/docs/public/robots.txt +2 -0
  48. package/docs/src/apis/ajax.ts +66 -0
  49. package/docs/src/apis/index.ts +1 -0
  50. package/docs/src/apis/modules/wish.ts +20 -0
  51. package/docs/src/components/Avatar.vue +60 -0
  52. package/docs/src/components/Home.vue +85 -0
  53. package/docs/src/components/Picture.vue +13 -0
  54. package/docs/src/components/Praise.vue +52 -0
  55. package/docs/src/components/WishBtn.vue +98 -0
  56. package/docs/src/components/WishPanel.vue +170 -0
  57. package/docs/src/components/callme/index.vue +72 -0
  58. package/docs/vite.config.ts +42 -0
  59. package/index.html +127 -0
  60. package/package.json +52 -0
  61. package/public/favicon.ico +0 -0
  62. package/public/logo.png +0 -0
  63. package/scripts/deploy/docs.mjs +24 -0
  64. package/scripts/deploy/prod.mjs +24 -0
  65. package/scripts/deploy/test.mjs +26 -0
  66. package/src/@types/ajax.d.ts +5 -0
  67. package/src/@types/api.d.ts +305 -0
  68. package/src/@types/lib.d.ts +26 -0
  69. package/src/@types/page.d.ts +18 -0
  70. package/src/App.vue +36 -0
  71. package/src/apis/ajax.ts +70 -0
  72. package/src/apis/index.ts +20 -0
  73. package/src/apis/modules/action.ts +17 -0
  74. package/src/apis/modules/category.ts +20 -0
  75. package/src/apis/modules/config.ts +19 -0
  76. package/src/apis/modules/file.ts +150 -0
  77. package/src/apis/modules/people.ts +81 -0
  78. package/src/apis/modules/public.ts +49 -0
  79. package/src/apis/modules/super/overview.ts +56 -0
  80. package/src/apis/modules/super/user.ts +62 -0
  81. package/src/apis/modules/task.ts +67 -0
  82. package/src/apis/modules/user.ts +56 -0
  83. package/src/apis/modules/wish.ts +31 -0
  84. package/src/assets/i/EasyPicker.png +0 -0
  85. package/src/assets/logo.png +0 -0
  86. package/src/assets/styles/app.css +69 -0
  87. package/src/components/HomeFooter/index.vue +134 -0
  88. package/src/components/HomeHeader/index.vue +156 -0
  89. package/src/components/InfosForm/index.vue +73 -0
  90. package/src/components/MessageList/index.vue +155 -0
  91. package/src/components/MessagePanel/index.vue +42 -0
  92. package/src/components/Praise/index.vue +102 -0
  93. package/src/components/QrCode.vue +44 -0
  94. package/src/components/linkDialog.vue +104 -0
  95. package/src/components/loginPanel.vue +92 -0
  96. package/src/constants/index.ts +83 -0
  97. package/src/env.d.ts +8 -0
  98. package/src/main.ts +19 -0
  99. package/src/pages/404/index.vue +59 -0
  100. package/src/pages/about/index.vue +152 -0
  101. package/src/pages/callme/index.vue +155 -0
  102. package/src/pages/dashboard/config/index.vue +264 -0
  103. package/src/pages/dashboard/files/index.vue +1152 -0
  104. package/src/pages/dashboard/index.vue +335 -0
  105. package/src/pages/dashboard/manage/config/index.vue +97 -0
  106. package/src/pages/dashboard/manage/index.vue +105 -0
  107. package/src/pages/dashboard/manage/overview/index.vue +488 -0
  108. package/src/pages/dashboard/manage/user/index.vue +679 -0
  109. package/src/pages/dashboard/manage/wish/index.vue +257 -0
  110. package/src/pages/dashboard/tasks/components/CategoryPanel.vue +208 -0
  111. package/src/pages/dashboard/tasks/components/CreateTask.vue +93 -0
  112. package/src/pages/dashboard/tasks/components/TaskInfo.vue +129 -0
  113. package/src/pages/dashboard/tasks/components/infoPanel/ddl.vue +96 -0
  114. package/src/pages/dashboard/tasks/components/infoPanel/file.vue +175 -0
  115. package/src/pages/dashboard/tasks/components/infoPanel/info.vue +477 -0
  116. package/src/pages/dashboard/tasks/components/infoPanel/people.vue +567 -0
  117. package/src/pages/dashboard/tasks/components/infoPanel/template.vue +146 -0
  118. package/src/pages/dashboard/tasks/components/infoPanel/tip.vue +55 -0
  119. package/src/pages/dashboard/tasks/components/infoPanel/tipInfo.vue +196 -0
  120. package/src/pages/dashboard/tasks/index.vue +302 -0
  121. package/src/pages/dashboard/tasks/public.ts +32 -0
  122. package/src/pages/disabled/index.vue +47 -0
  123. package/src/pages/feedback/index.vue +5 -0
  124. package/src/pages/home/index.vue +72 -0
  125. package/src/pages/login/index.vue +270 -0
  126. package/src/pages/register/index.vue +211 -0
  127. package/src/pages/reset/index.vue +186 -0
  128. package/src/pages/task/index.vue +897 -0
  129. package/src/pages/wish/index.vue +152 -0
  130. package/src/router/Interceptor/index.ts +112 -0
  131. package/src/router/index.ts +13 -0
  132. package/src/router/routes/index.ts +197 -0
  133. package/src/shims-vue.d.ts +6 -0
  134. package/src/store/index.ts +17 -0
  135. package/src/store/modules/category.ts +44 -0
  136. package/src/store/modules/public.ts +27 -0
  137. package/src/store/modules/task.ts +55 -0
  138. package/src/store/modules/user.ts +57 -0
  139. package/src/utils/elementUI.ts +8 -0
  140. package/src/utils/networkUtil.ts +236 -0
  141. package/src/utils/other.ts +25 -0
  142. package/src/utils/regExp.ts +11 -0
  143. package/src/utils/stringUtil.ts +242 -0
  144. package/tsconfig.json +24 -0
  145. package/vite.config.ts +55 -0
@@ -0,0 +1,567 @@
1
+ <template>
2
+ <div class="tc info-panel">
3
+ <tip
4
+ :imgs="[
5
+ 'https://img.cdn.sugarat.top/mdImg/MTY1MDE4MzEwOTEzOQ==650183109139',
6
+ 'https://img.cdn.sugarat.top/mdImg/MTY1MTQ5NjY3MTUyMw==651496671523'
7
+ ]"
8
+ >只有名单里的成员,才可提交文件</tip
9
+ >
10
+ <el-button
11
+ @click="updateLimitPeople(true)"
12
+ v-if="!people"
13
+ size="default"
14
+ round
15
+ type="success"
16
+ >开启</el-button
17
+ >
18
+ <el-button
19
+ @click="updateLimitPeople(false)"
20
+ v-if="people"
21
+ size="default"
22
+ round
23
+ type="danger"
24
+ >关闭</el-button
25
+ >
26
+ <el-button
27
+ @click="checkPeople"
28
+ v-if="people"
29
+ round
30
+ size="default"
31
+ type="primary"
32
+ >查看提交情况</el-button
33
+ >
34
+ <div class="upload-people" v-if="people">
35
+ <el-radio-group v-model="activeTab" size="small">
36
+ <el-radio-button label="文件导入" />
37
+ <el-radio-button label="任务导入" />
38
+ <el-radio-button label="手动添加" />
39
+ </el-radio-group>
40
+ <div class="import-people-wrapper">
41
+ <div v-show="activeTab === '文件导入'">
42
+ <el-upload
43
+ accept="text/plain"
44
+ action=""
45
+ class="upload-demo"
46
+ ref="peopleUpload"
47
+ :on-change="handleChangeFile"
48
+ :on-exceed="handleExceedFile"
49
+ :on-remove="clearFiles"
50
+ :auto-upload="false"
51
+ :limit="1"
52
+ v-model:file-list="peopleFileList"
53
+ >
54
+ <template #trigger>
55
+ <el-button size="small" type="primary">选择文件</el-button>
56
+ </template>
57
+ <el-button
58
+ @click="submitUploadPeople"
59
+ style="margin-left: 10px"
60
+ size="small"
61
+ type="success"
62
+ :disabled="!peopleFileList.length"
63
+ >确定上传</el-button
64
+ >
65
+ <template #tip>
66
+ <div class="el-upload__tip">
67
+ <tip
68
+ :imgs="[
69
+ 'https://img.cdn.sugarat.top/mdImg/MTY1MDE4Mjk2NjUxMA==650182966510'
70
+ ]"
71
+ >只能上传 .txt 文本文件,每行一个名字</tip
72
+ >
73
+ <tip>如名字有特殊字符,建议去除</tip>
74
+ <tip>上传文件导入的方式,为追加导入,不会覆盖已存在数据</tip>
75
+ </div>
76
+ </template>
77
+ </el-upload>
78
+ </div>
79
+ <div v-show="activeTab === '任务导入'">
80
+ <!-- 从其它任务导入 -->
81
+ <el-button size="small" type="success" @click="openImportPanel"
82
+ >选择任务</el-button
83
+ >
84
+ <div class="p10">
85
+ <tip>支持从已有的任务直接导入名单</tip>
86
+ </div>
87
+ </div>
88
+ <div v-show="activeTab === '手动添加'">
89
+ <div style="max-width: 300px; margin: 0 auto">
90
+ <el-input
91
+ :disabled="importStatus"
92
+ v-model="userInputName"
93
+ placeholder="请输入姓名"
94
+ >
95
+ <template #append>
96
+ <el-button @click="handAddName"> 确定 </el-button>
97
+ </template>
98
+ </el-input>
99
+ </div>
100
+ <div class="p10">
101
+ <tip>会自动判重,不会重复添加</tip>
102
+ <tip>大量名单优先推荐使用文件导入</tip>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ <el-dialog :fullscreen="isMobile" title="提交情况" v-model="showPeopleList">
108
+ <!-- 上部分的筛选菜单 -->
109
+ <div class="nav">
110
+ <div class="item">
111
+ <el-button
112
+ :disabled="peopleList.length === 0"
113
+ type="success"
114
+ size="default"
115
+ @click="handleExportExcel"
116
+ >导出记录</el-button
117
+ >
118
+ </div>
119
+ <div class="item">
120
+ <el-select
121
+ size="default"
122
+ v-model="selectSubmitStatus"
123
+ placeholder="状态筛选"
124
+ >
125
+ <el-option label="全部" value="all" />
126
+ <el-option label="已提交" :value="1" />
127
+ <el-option label="未提交" :value="0" />
128
+ </el-select>
129
+ </div>
130
+ <div class="item">
131
+ <el-input
132
+ size="default"
133
+ placeholder="输入要查询的姓名"
134
+ v-model="searchName"
135
+ ></el-input>
136
+ </div>
137
+ <div class="item">
138
+ <el-button type="primary" size="default" @click="handleCheckMore"
139
+ >{{ checkMore ? '隐藏' : '显示' }}详细提交情况
140
+ </el-button>
141
+ </div>
142
+ </div>
143
+ <!-- 概况信息 -->
144
+ <div class="tc p10">
145
+ <span>共: {{ peopleSubmitData.length }} 条数据</span>,
146
+ <span
147
+ >已提交: {{ peopleSubmitData.filter((v) => v.status).length }}</span
148
+ >,
149
+ <span
150
+ >未提交: {{ peopleSubmitData.filter((v) => !v.status).length }}</span
151
+ >
152
+ </div>
153
+ <div class="tc p10">
154
+ <tip>"提交次数" 用户实际的提交次数</tip>
155
+ <tip>"现存数量" 还存在于服务器上的文件数 (不包含删除) --- 慢查询</tip>
156
+ <tip>"提交数量" 用户实际提交的文件数 (不包含撤回) --- 慢查询</tip>
157
+ </div>
158
+ <!-- 数据部分 -->
159
+ <el-table
160
+ v-loading="isLoadingPeopleData"
161
+ element-loading-text="Loading..."
162
+ stripe
163
+ border
164
+ :data="peopleSubmitData"
165
+ height="460px"
166
+ >
167
+ <el-table-column label="序号" width="60">
168
+ <template #default="scope">
169
+ <div style="text-align: center">{{ scope.$index + 1 }}</div>
170
+ </template>
171
+ </el-table-column>
172
+ <el-table-column property="name" label="姓名"></el-table-column>
173
+ <el-table-column label="提交状态" width="100">
174
+ <template #default="scope">
175
+ <span class="submit-ok" v-if="scope.row.status">已提交</span>
176
+ <span class="submit-fail" v-else>未提交</span>
177
+ </template>
178
+ </el-table-column>
179
+ <el-table-column
180
+ property="count"
181
+ label="提交次数"
182
+ width="94"
183
+ ></el-table-column>
184
+ <el-table-column
185
+ sortable
186
+ property="lastDate"
187
+ label="最后操作时间"
188
+ width="120"
189
+ ></el-table-column>
190
+ <template v-if="checkMore">
191
+ <el-table-column
192
+ property="fileCount"
193
+ label="现存数量"
194
+ width="94"
195
+ ></el-table-column>
196
+ <el-table-column
197
+ sortable
198
+ property="submitCount"
199
+ label="提交数量"
200
+ width="120"
201
+ ></el-table-column>
202
+ </template>
203
+ <el-table-column label="操作" width="100">
204
+ <template #default="scope">
205
+ <el-button
206
+ @click="handleDeletePeople(scope.row)"
207
+ type="primary"
208
+ text
209
+ size="small"
210
+ >删除</el-button
211
+ >
212
+ </template>
213
+ </el-table-column>
214
+ </el-table>
215
+ </el-dialog>
216
+ <el-dialog
217
+ :fullscreen="isMobile"
218
+ title="人员列表导入"
219
+ v-model="showImportPanel"
220
+ >
221
+ <el-form
222
+ :model="importPanelInfo"
223
+ label-width="100px"
224
+ label-position="right"
225
+ >
226
+ <el-form-item label="任务">
227
+ <el-select
228
+ filterable
229
+ v-model="importPanelInfo.taskValue"
230
+ placeholder="请选择"
231
+ no-data-text="无可用任务"
232
+ >
233
+ <el-option
234
+ v-for="t in importPanelInfo.taskList"
235
+ :key="t.taskKey"
236
+ :label="t.name"
237
+ :value="t.taskKey"
238
+ >
239
+ </el-option>
240
+ </el-select>
241
+ </el-form-item>
242
+ <tip>{{ ImportTaskTipMsg }}</tip>
243
+ <el-form-item label="任务">
244
+ <el-radio-group v-model="importPanelInfo.type">
245
+ <el-radio label="override">覆盖导入</el-radio>
246
+ <el-radio label="add">追加导入</el-radio>
247
+ </el-radio-group>
248
+ </el-form-item>
249
+ <tip>{{
250
+ importPanelInfo.type === 'override'
251
+ ? '“覆盖导入” 将会覆盖原来的数据'
252
+ : '“追加导入” 将只会导入不存在数据'
253
+ }}</tip>
254
+ </el-form>
255
+ <template #footer>
256
+ <span class="dialog-footer">
257
+ <el-button @click="showImportPanel = false">取 消</el-button>
258
+ <el-button
259
+ :disabled="!importPanelInfo.taskValue"
260
+ type="primary"
261
+ @click="handleSaveImportInfo"
262
+ >确 定</el-button
263
+ >
264
+ </span>
265
+ </template>
266
+ </el-dialog>
267
+ </div>
268
+ </template>
269
+ <script lang="ts" setup>
270
+ import { ElMessage, ElMessageBox, UploadUserFile } from 'element-plus'
271
+ import { computed, reactive, ref, watchEffect } from 'vue'
272
+ import { useStore } from 'vuex'
273
+ import { PeopleApi } from '@/apis'
274
+ import { uploadFile, tableToExcel } from '@/utils/networkUtil'
275
+ import { formatDate } from '@/utils/stringUtil'
276
+ import { updateTaskInfo } from '../../public'
277
+ import Tip from './tip.vue'
278
+
279
+ const props = defineProps({
280
+ value: {
281
+ type: Number,
282
+ defalut: 0
283
+ },
284
+ k: {
285
+ type: String,
286
+ default: ''
287
+ },
288
+ name: {
289
+ type: String,
290
+ default: ''
291
+ }
292
+ })
293
+
294
+ const activeTab = ref('手动添加')
295
+ const userInputName = ref('')
296
+ const importStatus = ref(false)
297
+
298
+ const handAddName = () => {
299
+ if (!userInputName.value) {
300
+ return
301
+ }
302
+ // TODO:掉接口导入
303
+ importStatus.value = true
304
+ PeopleApi.addPeopleByUser(userInputName.value, props.k)
305
+ .then((v) => {
306
+ ElMessage.success(`添加 ${userInputName.value} 成功`)
307
+ })
308
+ .catch(() => {
309
+ ElMessage.error(`${userInputName.value} 已存在`)
310
+ })
311
+ .finally(() => {
312
+ importStatus.value = false
313
+ userInputName.value = ''
314
+ })
315
+ }
316
+ const checkMore = ref(false)
317
+
318
+ const people = ref(0)
319
+ watchEffect(() => {
320
+ people.value = props.value as number
321
+ })
322
+ // 限制提交人员
323
+ const updateLimitPeople = (limit: boolean) => {
324
+ updateTaskInfo(props.k, {
325
+ people: +limit
326
+ })
327
+ people.value = +limit
328
+ }
329
+
330
+ // 查看提交情况
331
+ const showPeopleList = ref(false)
332
+ const peopleList: any = reactive([])
333
+ const selectSubmitStatus = ref('all')
334
+ const searchName = ref('')
335
+ const filterPeopleBySearchWord = computed(() => {
336
+ if (!searchName.value) {
337
+ return peopleList
338
+ }
339
+ return peopleList.filter((v) => v.name.includes(searchName.value))
340
+ })
341
+ const peopleSubmitData = computed(() => {
342
+ if (selectSubmitStatus.value === 'all') {
343
+ return filterPeopleBySearchWord.value
344
+ }
345
+ return filterPeopleBySearchWord.value.filter(
346
+ (p) => p.status === selectSubmitStatus.value
347
+ )
348
+ })
349
+ const isLoadingPeopleData = ref(false)
350
+ const refreshSubmitData = () => {
351
+ isLoadingPeopleData.value = true
352
+ PeopleApi.getPeople(props.k, `${+checkMore.value}`).then((res) => {
353
+ peopleList.splice(0, peopleList.length)
354
+ peopleList.push(...res.data.people)
355
+ peopleList.forEach((p) => {
356
+ if (!p.status && p.count === 0) {
357
+ p.lastDate = '暂无记录'
358
+ } else {
359
+ p.lastDate = formatDate(new Date(p.lastDate), 'yyyy-MM-dd hh:mm:ss')
360
+ }
361
+ })
362
+ isLoadingPeopleData.value = false
363
+ })
364
+ }
365
+ const handleCheckMore = () => {
366
+ checkMore.value = !checkMore.value
367
+ if (checkMore.value) {
368
+ refreshSubmitData()
369
+ }
370
+ }
371
+ const checkPeople = () => {
372
+ showPeopleList.value = true
373
+ // 默认不展示提交数量
374
+ checkMore.value = false
375
+ refreshSubmitData()
376
+ }
377
+ const handleDeletePeople = (item: any) => {
378
+ ElMessageBox.confirm('确认删除此人员吗', '数据无价,请谨慎操作')
379
+ .then(() => {
380
+ PeopleApi.deletePeople(props.k, item.id).then(() => {
381
+ ElMessage.success('删除成功')
382
+ peopleList.splice(
383
+ peopleList.findIndex((v) => v.id === item.id),
384
+ 1
385
+ )
386
+ })
387
+ })
388
+ .catch(() => {
389
+ ElMessage.info('取消删除')
390
+ })
391
+ }
392
+ // 文件上传
393
+ const peopleFileList = ref<UploadUserFile[]>([])
394
+ const peopleUpload = ref()
395
+ // 超出选择的文件个数
396
+ const handleExceedFile = () => {
397
+ ElMessage.error('只能选择一个文件,可删除后重新选择')
398
+ }
399
+ // 清空文件
400
+ const clearFiles = () => {
401
+ peopleFileList.value.splice(0, peopleFileList.value.length)
402
+ peopleUpload.value.clearFiles()
403
+ }
404
+ // 开始上传
405
+ const submitUploadPeople = () => {
406
+ peopleFileList.value.forEach((file) => {
407
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
408
+ // @ts-ignore
409
+ uploadFile(
410
+ file.raw,
411
+ `${import.meta.env.VITE_APP_AXIOS_BASE_URL}public/upload`,
412
+ {
413
+ success: (e: any) => {
414
+ const { name, type } = e.data
415
+ PeopleApi.importPeople(props.k, name, type).then((res) => {
416
+ const { success, fail } = res.data
417
+ ElMessage.success(`导入完成:${success}成功,${fail.length}失败`)
418
+ if (fail.length > 0) {
419
+ setTimeout(() => {
420
+ ElMessage.info('自动开始下载未成功导入名单')
421
+ tableToExcel(
422
+ ['未成功导入名单'],
423
+ fail.map((v: string) => [v]),
424
+ `${props.name}_导入失败名单_${formatDate(
425
+ new Date(),
426
+ 'yyyy年MM月日hh时mm分ss秒'
427
+ )}.xlsx`
428
+ )
429
+ }, 1000)
430
+ }
431
+ clearFiles()
432
+ })
433
+ }
434
+ }
435
+ )
436
+ })
437
+ }
438
+ // 添加文件
439
+ const handleChangeFile = (file: any) => {
440
+ if (file.raw.type !== 'text/plain') {
441
+ ElMessage.warning({
442
+ message: '只支持txt文件',
443
+ zIndex: 4000
444
+ })
445
+ clearFiles()
446
+ }
447
+ }
448
+ const handleExportExcel = () => {
449
+ if (peopleSubmitData.value.length === 0) {
450
+ ElMessage.warning('表格中没有可导出数据')
451
+ return
452
+ }
453
+ const headers = [
454
+ '姓名',
455
+ '提交状态',
456
+ '提交数量',
457
+ '最后操作时间',
458
+ ...(checkMore.value ? ['现存数量', '提交次数'] : [])
459
+ ]
460
+ const body = peopleSubmitData.value.map((v) => {
461
+ const { name, status, lastDate, submitCount, fileCount, count } = v
462
+ return [
463
+ name,
464
+ status ? '✔' : 'x',
465
+ submitCount,
466
+ status ? formatDate(new Date(lastDate)) : '',
467
+ ...(checkMore.value ? [fileCount, count] : [])
468
+ ]
469
+ })
470
+ tableToExcel(
471
+ headers,
472
+ body,
473
+ `${props.name}_提交情况_${formatDate(
474
+ new Date(),
475
+ 'yyyy年MM月日hh时mm分ss秒'
476
+ )}.xlsx`
477
+ )
478
+ ElMessage.success('导出成功')
479
+ }
480
+ const $store = useStore()
481
+ const isMobile = computed(() => $store.getters['public/isMobile'])
482
+ const importPanelInfo = reactive({
483
+ taskList: [],
484
+ type: 'override',
485
+ taskValue: ''
486
+ })
487
+ const showImportPanel = ref(false)
488
+ const openImportPanel = async () => {
489
+ const taskKey = props.k
490
+ // 通过任务Key获取可用任务列表,与概况信息
491
+ const { data } = await PeopleApi.getUsefulTemplate(taskKey)
492
+ importPanelInfo.taskList = data
493
+ importPanelInfo.taskValue = data[0]?.taskKey || ''
494
+ showImportPanel.value = true
495
+ }
496
+ const ImportTaskTipMsg = computed(() => {
497
+ const { taskList, taskValue } = importPanelInfo
498
+ const task = taskList.find((v) => v.taskKey === taskValue)
499
+ if (!task) {
500
+ return '无可用任务'
501
+ }
502
+ return `${task.count} 条数据`
503
+ })
504
+ const handleSaveImportInfo = () => {
505
+ PeopleApi.importPeopleFromTpl(
506
+ props.k,
507
+ importPanelInfo.taskValue,
508
+ importPanelInfo.type
509
+ ).then((res) => {
510
+ showImportPanel.value = false
511
+ const { success, fail } = res.data
512
+ ElMessage.success(`导入成功${success}条,失败${fail.length}条`)
513
+
514
+ if (fail.length > 0) {
515
+ setTimeout(() => {
516
+ ElMessage.info('自动开始下载未成功导入名单')
517
+ tableToExcel(
518
+ ['未成功导入名单'],
519
+ fail.map((v: string) => [v]),
520
+ `${props.name}_导入失败名单_${formatDate(
521
+ new Date(),
522
+ 'yyyy年MM月日hh时mm分ss秒'
523
+ )}.xlsx`
524
+ )
525
+ }, 1000)
526
+ }
527
+ })
528
+ }
529
+
530
+ const importPanelFlexStyle = computed(() => (isMobile.value ? '0 0 auto' : 0.5))
531
+ </script>
532
+ <style scoped>
533
+ .upload-people {
534
+ padding: 10px;
535
+ }
536
+
537
+ .import-people-wrapper {
538
+ padding: 10px 0;
539
+ }
540
+ .submit-ok {
541
+ color: #67c23a;
542
+ }
543
+
544
+ .submit-fail {
545
+ color: #f56c6c;
546
+ }
547
+
548
+ .nav {
549
+ display: flex;
550
+ flex-wrap: wrap;
551
+ justify-content: center;
552
+ padding-bottom: 5px;
553
+ }
554
+
555
+ .nav .item {
556
+ margin-left: 10px;
557
+ margin-top: 5px;
558
+ }
559
+
560
+ .info-panel :deep(.el-form-item__label) {
561
+ flex: v-bind(importPanelFlexStyle);
562
+ }
563
+
564
+ .info-panel :deep(.el-upload-list__item-name) {
565
+ justify-content: center;
566
+ }
567
+ </style>
@@ -0,0 +1,146 @@
1
+ <template>
2
+ <div class="tc info-panel">
3
+ <tip
4
+ :imgs="[
5
+ 'https://img.cdn.sugarat.top/mdImg/MTY1MDE4MjY3MjUxNw==650182672517'
6
+ ]"
7
+ >设置的模板文件,可供用户在提交页下载。</tip
8
+ >
9
+ <el-button
10
+ v-if="template"
11
+ :disabled="!template"
12
+ @click="deleteTemplate"
13
+ size="default"
14
+ round
15
+ type="danger"
16
+ >删除</el-button
17
+ >
18
+ <div class="p10">{{ template || '尚未设置模板文件' }}</div>
19
+ <div class="upload-file" v-if="!template">
20
+ <el-upload
21
+ action=""
22
+ ref="elUpload"
23
+ :on-exceed="handleExceedFile"
24
+ :on-remove="clearFiles"
25
+ :auto-upload="false"
26
+ :limit="1"
27
+ v-model:file-list="fileList"
28
+ >
29
+ <template #trigger>
30
+ <el-button size="small" type="primary">选取文件</el-button>
31
+ </template>
32
+ <el-button
33
+ @click="submitUploadPeople"
34
+ style="margin-left: 10px"
35
+ size="small"
36
+ type="success"
37
+ >设为模板</el-button
38
+ >
39
+ <template #tip>
40
+ <div class="el-upload__tip">选择模板文件,然后点击上传</div>
41
+ </template>
42
+ </el-upload>
43
+ </div>
44
+ </div>
45
+ </template>
46
+ <script lang="ts">
47
+ import { ElMessage, UploadUserFile } from 'element-plus'
48
+ import { defineComponent, ref, watchEffect } from 'vue'
49
+ import { FileApi } from '@/apis'
50
+ import { qiniuUpload } from '@/utils/networkUtil'
51
+ import { updateTaskInfo } from '../../public'
52
+ import Tip from './tip.vue'
53
+
54
+ export default defineComponent({
55
+ name: 'templatePanel',
56
+ props: {
57
+ value: {
58
+ type: String,
59
+ default: ''
60
+ },
61
+ k: {
62
+ type: String,
63
+ default: ''
64
+ }
65
+ },
66
+ setup(props) {
67
+ const template = ref()
68
+ watchEffect(() => {
69
+ if (props.value) {
70
+ template.value = props.value
71
+ } else {
72
+ template.value = ''
73
+ }
74
+ })
75
+ const percentage = ref(0)
76
+ // 删除模板
77
+ const deleteTemplate = () => {
78
+ if (template.value) {
79
+ // 移除文件,避免空间被长时间占用
80
+ updateTaskInfo(props.k, { template: '' })
81
+ template.value = ''
82
+ percentage.value = 0
83
+ }
84
+ }
85
+ // 文件上传
86
+ const fileList = ref<UploadUserFile[]>([])
87
+ const elUpload = ref()
88
+ // 超出选择的文件个数
89
+ const handleExceedFile = () => {
90
+ ElMessage.error('只能选择一个文件,可删除后重新选择')
91
+ }
92
+ // 清空文件
93
+ const clearFiles = () => {
94
+ elUpload.value.clearFiles()
95
+ }
96
+ // 开始上传
97
+ const submitUploadPeople = () => {
98
+ fileList.value.forEach((file) => {
99
+ if (!props.k) {
100
+ return
101
+ }
102
+ const { name } = file
103
+ const key = `easypicker2/${props.k}_template/${name}`
104
+ if (file.status === 'ready') {
105
+ file.status = 'uploading'
106
+ // qiniu上传
107
+ FileApi.getUploadToken().then((res) => {
108
+ qiniuUpload(res.data.token, file.raw, key, {
109
+ success() {
110
+ ElMessage.success('上传成功')
111
+ updateTaskInfo(props.k, { template: name })
112
+ // 清理上传完成的
113
+ clearFiles()
114
+ template.value = name
115
+ file.status = 'success'
116
+ // hash,key
117
+ // console.log(data)
118
+ },
119
+ process(per: number) {
120
+ file.percentage = ~~per
121
+ }
122
+ })
123
+ })
124
+ }
125
+ })
126
+ }
127
+ return {
128
+ template,
129
+ deleteTemplate,
130
+ fileList,
131
+ handleExceedFile,
132
+ clearFiles,
133
+ submitUploadPeople,
134
+ elUpload,
135
+ percentage
136
+ }
137
+ },
138
+ components: { Tip }
139
+ })
140
+ </script>
141
+
142
+ <style lang="scss" scoped>
143
+ .info-panel :deep(.el-upload-list__item-name) {
144
+ justify-content: center;
145
+ }
146
+ </style>