@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,1152 @@
1
+ <template>
2
+ <div class="files">
3
+ <!-- 筛选框 -->
4
+ <div class="panel header">
5
+ <div class="item">
6
+ <span class="label">分类</span>
7
+ <!--TODO: multiple 多选待评估 -->
8
+ <el-select
9
+ size="default"
10
+ v-model="selectCategory"
11
+ filterable
12
+ placeholder="请选择"
13
+ >
14
+ <el-option label="全部" value="all" />
15
+ <el-option label="默认" value="default" />
16
+ <el-option
17
+ v-for="item in categories"
18
+ :key="item.k"
19
+ :label="item.name"
20
+ :value="item.k"
21
+ />
22
+ <el-option label="无关联任务" value="no-task" />
23
+ <el-option label="♻️回收站♻️" value="trash" />
24
+ </el-select>
25
+ </div>
26
+ <div class="item">
27
+ <span class="label">任务</span>
28
+ <el-select
29
+ size="default"
30
+ v-model="selectTask"
31
+ filterable
32
+ placeholder="请选择"
33
+ >
34
+ <el-option label="全部" value="all" />
35
+ <el-option
36
+ v-for="item in filterTasks"
37
+ :key="item.key"
38
+ :label="item.name"
39
+ :value="item.key"
40
+ ></el-option>
41
+ </el-select>
42
+ </div>
43
+ <div class="item">
44
+ <el-button
45
+ :loading="batchDownStart"
46
+ :disabled="selectTask === 'all'"
47
+ type="primary"
48
+ size="default"
49
+ :icon="Download"
50
+ @click="handleDownloadTask"
51
+ >下载任务中的文件</el-button
52
+ >
53
+ </div>
54
+ <div class="item">
55
+ <el-input
56
+ size="default"
57
+ clearable
58
+ placeholder="请输入要检索的内容"
59
+ :prefix-icon="Search"
60
+ v-model="searchWord"
61
+ >
62
+ </el-input>
63
+ </div>
64
+ </div>
65
+ <div class="panel">
66
+ <div class="export-btns flex fac">
67
+ <el-dropdown trigger="click" @command="handleDropdownClick">
68
+ <el-button type="primary" size="default">
69
+ 批量操作<el-icon class="el-icon--right">
70
+ <arrow-down />
71
+ </el-icon>
72
+ </el-button>
73
+ <template #dropdown>
74
+ <el-dropdown-menu>
75
+ <el-dropdown-item
76
+ :disabled="selectItem.length === 0"
77
+ command="download"
78
+ >下载</el-dropdown-item
79
+ >
80
+ <el-dropdown-item
81
+ :disabled="selectItem.length === 0"
82
+ command="delete"
83
+ >删除</el-dropdown-item
84
+ >
85
+ <el-dropdown-item
86
+ :disabled="selectItem.length === 0"
87
+ command="excel"
88
+ >导出记录</el-dropdown-item
89
+ >
90
+ </el-dropdown-menu>
91
+ </template>
92
+ </el-dropdown>
93
+ <div v-show="false">
94
+ <!-- 迷惑的解决bug的手段 -->
95
+ <el-dropdown trigger="click" @command="handleDropdownClick">
96
+ <el-button
97
+ type="primary"
98
+ :disabled="selectItem.length === 0"
99
+ size="default"
100
+ >
101
+ 批量操作
102
+ <el-icon>
103
+ <ArrowDown />
104
+ </el-icon>
105
+ </el-button>
106
+ <template #dropdown>
107
+ <el-dropdown-menu>
108
+ <el-dropdown-item command="download">下载</el-dropdown-item>
109
+ <el-dropdown-item command="delete">删除</el-dropdown-item>
110
+ <el-dropdown-item command="excel">导出记录</el-dropdown-item>
111
+ </el-dropdown-menu>
112
+ </template>
113
+ </el-dropdown>
114
+ </div>
115
+ <el-button size="default" :icon="Refresh" @click="handleRefresh"
116
+ >刷新</el-button
117
+ >
118
+ <el-button
119
+ title="导出表格中所有的数据"
120
+ type="success"
121
+ size="default"
122
+ :icon="DataAnalysis"
123
+ @click="
124
+ () => {
125
+ handleExportExcel(
126
+ filterFiles,
127
+ `筛选数据导出_${formatDate(
128
+ new Date(),
129
+ 'yyyy年MM月日hh时mm分ss秒'
130
+ )}.xlsx`
131
+ )
132
+ }
133
+ "
134
+ :disabled="showFilterFiles.length === 0"
135
+ >导出记录</el-button
136
+ >
137
+ <div class="control-item">
138
+ 显示图片
139
+ <el-switch
140
+ inline-prompt
141
+ v-model="showImg"
142
+ active-color="#13ce66"
143
+ inactive-color="#ff4949"
144
+ active-text="是"
145
+ inactive-text="否"
146
+ />
147
+ </div>
148
+ <div class="control-item">
149
+ 展示原文件名
150
+ <el-switch
151
+ inline-prompt
152
+ v-model="showOriginName"
153
+ active-color="#13ce66"
154
+ inactive-color="#ff4949"
155
+ active-text="是"
156
+ inactive-text="否"
157
+ />
158
+ </div>
159
+ <div class="control-item">
160
+ 显示提交人姓名
161
+ <el-switch
162
+ inline-prompt
163
+ v-model="showPeople"
164
+ active-color="#13ce66"
165
+ inactive-color="#ff4949"
166
+ active-text="是"
167
+ inactive-text="否"
168
+ />
169
+ </div>
170
+ <div class="control-item">
171
+ ⏰ 查看下载历史
172
+ <el-switch
173
+ v-model="showHistoryPanel"
174
+ style="
175
+ --el-switch-on-color: #13ce66;
176
+ --el-switch-off-color: #ff4949;
177
+ "
178
+ />
179
+ </div>
180
+ </div>
181
+ </div>
182
+ <div
183
+ class="panel"
184
+ v-show="historyDownloadRecord.compressTask.length && !showHistoryPanel"
185
+ >
186
+ <tip style="font-size: 16px"
187
+ >正在进行归档的任务
188
+ {{ historyDownloadRecord.compressTask.length }}个</tip
189
+ >
190
+ <tip>详细归档记录点击右上角 “⏰查看下载历史”</tip>
191
+ <p
192
+ v-for="(record, idx) in historyDownloadRecord.compressTask"
193
+ :key="record.id"
194
+ class="tc"
195
+ style="margin-top: 10px"
196
+ >
197
+ {{ idx + 1 }}. {{ record.tip }}
198
+ <span
199
+ v-loading="true"
200
+ element-loading-text="..."
201
+ style="--el-loading-spinner-size: 20px"
202
+ ></span>
203
+ </p>
204
+ </div>
205
+ <div class="panel" v-show="showHistoryPanel">
206
+ <tip style="font-size: 16px"
207
+ >”❤️下面展示历史的下载记录与归档任务完成情况❤️“</tip
208
+ >
209
+ <tip>”再也不需要在页面停留等待归档完成“</tip>
210
+ <div>
211
+ <el-table
212
+ v-loading="isLoadingData"
213
+ element-loading-text="Loading..."
214
+ tooltip-effect="dark"
215
+ multipleTable
216
+ ref="multipleTable"
217
+ @selection-change="handleSelectionChange"
218
+ stripe
219
+ border
220
+ :default-sort="{ prop: 'date', order: 'descending' }"
221
+ :max-height="666"
222
+ :data="historyDownloadRecord.actions"
223
+ style="width: 100%"
224
+ >
225
+ <el-table-column prop="date" label="触发时间" width="200">
226
+ <template #default="scope">{{
227
+ formatDate(new Date(scope.row.date))
228
+ }}</template>
229
+ </el-table-column>
230
+ <el-table-column prop="tip" label="文件信息"></el-table-column>
231
+ <el-table-column prop="type" label="任务类型">
232
+ <template #default="scope">
233
+ <el-link
234
+ v-if="scope.row.type === ActionType.Compress"
235
+ type="primary"
236
+ >归档下载</el-link
237
+ >
238
+ <el-link v-else type="default">普通下载</el-link>
239
+ </template>
240
+ </el-table-column>
241
+ <el-table-column prop="size" label="大小" width="100">
242
+ <template #default="scope">
243
+ <span v-if="scope.row.status === DownloadStatus.ARCHIVE"
244
+ ><el-link type="danger">归档中...</el-link></span
245
+ >
246
+ <span v-else-if="scope.row.status !== DownloadStatus.FAIL">{{
247
+ !scope.row.size ? '未知大小' : formatSize(scope.row.size)
248
+ }}</span>
249
+ <span v-if="scope.row.status === DownloadStatus.FAIL"
250
+ ><el-link type="danger">归档失败</el-link></span
251
+ >
252
+ </template>
253
+ </el-table-column>
254
+ <el-table-column fixed="right" label="操作" width="140">
255
+ <template #default="scope">
256
+ <div
257
+ v-loading="true"
258
+ v-if="scope.row.status === DownloadStatus.ARCHIVE"
259
+ >
260
+ 归档中...
261
+ </div>
262
+ <div v-if="scope.row.status === DownloadStatus.EXPIRED">
263
+ 链接已失效
264
+ </div>
265
+ <div v-if="scope.row.status === DownloadStatus.FAIL">
266
+ 联系开发者,提供错误信息:{{ scope.row.error }}
267
+ </div>
268
+ <div v-if="scope.row.status === DownloadStatus.SUCCESS">
269
+ <el-link @click="downLoadByUrl(scope.row.url)" type="primary"
270
+ >下载</el-link
271
+ >
272
+ <el-link
273
+ type="success"
274
+ style="margin-left: 10px"
275
+ @click="copyRes(scope.row.url)"
276
+ >链接</el-link
277
+ >
278
+ <el-link
279
+ type="warning"
280
+ style="margin-left: 10px"
281
+ @click="
282
+ () => {
283
+ showLinkModel = true
284
+ downloadUrl = scope.row.url
285
+ }
286
+ "
287
+ >二维码</el-link
288
+ >
289
+ </div>
290
+ </template>
291
+ </el-table-column>
292
+ </el-table>
293
+ <div class="flex fc">
294
+ <el-pagination
295
+ small
296
+ :current-page="historyDownloadRecord.pageCurrent"
297
+ :page-count="historyDownloadRecord.pageCount"
298
+ :total="historyDownloadRecord.pageTotal"
299
+ layout="total, prev, pager, next"
300
+ @current-change="handleHistoryActionPageChange"
301
+ ></el-pagination>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ <!-- 主体内容 -->
306
+ <div class="panel">
307
+ <Tip>空间占用情况:{{ filterFileSize }} / {{ fileListSize }}</Tip>
308
+ <Tip>↑ 仅供使用者参考,应用无存储空间上限,也不收费</Tip>
309
+ <Tip
310
+ ><strong
311
+ >如果你觉得应用不错,<a
312
+ style="color: #409eff"
313
+ href="http://docs.ep.sugarat.top/praise/index.html"
314
+ target="_blank"
315
+ rel="noopener noreferrer"
316
+ >请作者喝茶 🍵</a
317
+ ></strong
318
+ >
319
+ <!-- <Praise>
320
+ <el-button style="margin:0 0 2px;" size="small" type="primary" text>Go!Go!❓</el-button>
321
+ </Praise> -->
322
+ </Tip>
323
+ <el-table
324
+ v-loading="isLoadingData"
325
+ element-loading-text="Loading..."
326
+ tooltip-effect="dark"
327
+ multipleTable
328
+ ref="multipleTable"
329
+ @selection-change="handleSelectionChange"
330
+ stripe
331
+ border
332
+ :max-height="666"
333
+ :data="showFilterFiles"
334
+ style="width: 100%"
335
+ >
336
+ <el-table-column type="selection" width="55" />
337
+ <el-table-column prop="date" label="提交时间" width="200">
338
+ <template #default="scope">{{
339
+ formatDate(new Date(scope.row.date))
340
+ }}</template>
341
+ </el-table-column>
342
+ <el-table-column
343
+ prop="task_name"
344
+ label="任务"
345
+ width="150"
346
+ ></el-table-column>
347
+ <el-table-column
348
+ prop="name"
349
+ label="文件名"
350
+ width="200"
351
+ ></el-table-column>
352
+ <template v-if="showOriginName">
353
+ <el-table-column prop="origin_name" label="原文件名" width="200">
354
+ <template #default="scope">
355
+ {{ scope.row.origin_name || '-' }}
356
+ </template>
357
+ </el-table-column>
358
+ </template>
359
+ <el-table-column prop="size" label="大小">
360
+ <template #default="scope">{{
361
+ scope.row.size === 0 ? '未知大小' : formatSize(scope.row.size)
362
+ }}</template>
363
+ </el-table-column>
364
+ <template v-if="showImg">
365
+ <el-table-column label="缩略图" width="120">
366
+ <template #default="scope">
367
+ <el-image
368
+ @switch="handleSwitchImage"
369
+ @click="handleSwitchImage(scope.$index)"
370
+ preview-teleported
371
+ :preview-src-list="previewImages"
372
+ :initial-index="scope.$index"
373
+ lazy
374
+ style="width: 100px; height: 100px"
375
+ :src="scope.row.cover"
376
+ fit="cover"
377
+ >
378
+ <template #viewer>
379
+ <div class="imageDes">{{ viewImageFilename }}</div>
380
+ </template>
381
+ <template #placeholder>
382
+ <div class="imageLoading">Loading...</div>
383
+ </template>
384
+ <template #error>
385
+ <div class="imageLoading">
386
+ 不支持
387
+ <el-icon>
388
+ <Picture />
389
+ </el-icon>
390
+ </div>
391
+ </template>
392
+ </el-image>
393
+ </template>
394
+ </el-table-column>
395
+ </template>
396
+ <template v-if="showPeople">
397
+ <el-table-column prop="people" label="姓名">
398
+ <template #default="scope">
399
+ {{ scope.row.people || '-' }}
400
+ </template>
401
+ </el-table-column>
402
+ </template>
403
+ <el-table-column fixed="right" label="操作" width="140">
404
+ <template #default="scope">
405
+ <div class="text-btns">
406
+ <el-button
407
+ @click="checkInfo(scope.row)"
408
+ type="primary"
409
+ text
410
+ size="small"
411
+ >查看提交信息</el-button
412
+ >
413
+ <el-button
414
+ @click="rewriteFilename(scope.row)"
415
+ type="primary"
416
+ text
417
+ size="small"
418
+ >修改文件名</el-button
419
+ >
420
+ <el-button
421
+ @click="downloadOne(scope.row)"
422
+ type="primary"
423
+ text
424
+ size="small"
425
+ >下载</el-button
426
+ >
427
+ <el-button
428
+ @click="handleDelete(scope.row)"
429
+ type="primary"
430
+ text
431
+ size="small"
432
+ >删除</el-button
433
+ >
434
+ </div>
435
+ </template>
436
+ </el-table-column>
437
+ </el-table>
438
+ </div>
439
+ <!-- 分页 -->
440
+ <div class="panel flex fc">
441
+ <el-pagination
442
+ :current-page="pageCurrent"
443
+ @current-change="handlePageChange"
444
+ background
445
+ :page-count="pageCount"
446
+ :page-sizes="[6, 10, 50, 100]"
447
+ :page-size="pageSize"
448
+ @size-change="handleSizeChange"
449
+ :total="filterFiles.length"
450
+ layout="total, sizes, prev, pager, next, jumper"
451
+ ></el-pagination>
452
+ </div>
453
+ <!-- 信息弹窗 -->
454
+ <el-dialog
455
+ :fullscreen="isMobile"
456
+ title="提交填写的信息"
457
+ v-model="showInfoDialog"
458
+ >
459
+ <InfosForm :infos="infos" :disabled="true" />
460
+ </el-dialog>
461
+ <LinkDialog
462
+ v-model:value="showLinkModel"
463
+ title="下载链接"
464
+ :link="downloadUrl"
465
+ ></LinkDialog>
466
+ <el-dialog
467
+ :fullscreen="isMobile"
468
+ title="修改文件名"
469
+ v-model="showRenameDialog"
470
+ >
471
+ <div>
472
+ <el-form label-width="100px" :model="renameForm">
473
+ <el-form-item label="原文件名" prop="newName">
474
+ <el-input v-model="renameForm.oldName" disabled />
475
+ </el-form-item>
476
+ <el-form-item label="新文件名" prop="newName">
477
+ <el-input v-model="renameForm.newName" placeholder="请输入新文件名">
478
+ <template #append>
479
+ {{ renameForm.suffix }}
480
+ </template>
481
+ </el-input>
482
+ </el-form-item>
483
+ <el-form-item>
484
+ <el-button type="success" @click="handleSaveNewName"
485
+ >保存</el-button
486
+ >
487
+ <el-button @click="showRenameDialog = false">取消</el-button>
488
+ </el-form-item>
489
+ </el-form>
490
+ </div>
491
+ </el-dialog>
492
+ </div>
493
+ </template>
494
+ <script lang="ts" setup>
495
+ import { ElMessage, ElMessageBox } from 'element-plus'
496
+ import { computed, onMounted, reactive, ref, watchEffect } from 'vue'
497
+ import { useStore } from 'vuex'
498
+ import LinkDialog from '@components/linkDialog.vue'
499
+ import {
500
+ ArrowDown,
501
+ Refresh,
502
+ DataAnalysis,
503
+ Download,
504
+ Search,
505
+ Picture
506
+ } from '@element-plus/icons-vue'
507
+ import { useRoute } from 'vue-router'
508
+ import {
509
+ copyRes,
510
+ formatDate,
511
+ formatSize,
512
+ getFileSuffix,
513
+ parseInfo
514
+ } from '@/utils/stringUtil'
515
+ import { ActionServiceAPI, FileApi } from '@/apis'
516
+ import { downLoadByUrl, tableItem, tableToExcel } from '@/utils/networkUtil'
517
+ import Tip from '../tasks/components/infoPanel/tip.vue'
518
+ import InfosForm from '@/components/InfosForm/index.vue'
519
+ import { DownloadStatus, ActionType, filenamePattern } from '@/constants'
520
+
521
+ const $store = useStore()
522
+ const $route = useRoute()
523
+ const showLinkModel = ref(false)
524
+ const downloadUrl = ref('')
525
+ const showImg = ref(localStorage.getItem('ep-show-images') === 'true')
526
+ const showPeople = ref(true)
527
+ const showOriginName = ref(false)
528
+ const showHistoryPanel = ref(false)
529
+ const historyDownloadRecord = reactive({
530
+ actions: [],
531
+ pageSize: 3,
532
+ /**
533
+ * 总页数
534
+ */
535
+ pageCount: 0,
536
+ pageCurrent: 1,
537
+ pageTotal: 0,
538
+ compressTask: []
539
+ })
540
+
541
+ const loadActions = () => {
542
+ // 已记录的task
543
+ const compressTask: ActionApiTypes.DownloadActionData[] = JSON.parse(
544
+ localStorage.getItem('ep_compress_task') || '[]'
545
+ )
546
+ historyDownloadRecord.compressTask = compressTask
547
+
548
+ ActionServiceAPI.getDownloadActions(
549
+ historyDownloadRecord.pageSize,
550
+ historyDownloadRecord.pageCurrent,
551
+ compressTask.map((v) => v.id)
552
+ ).then((v) => {
553
+ const { actions, sum } = v.data
554
+ const haveArchive = !!actions.find(
555
+ (v) => v.status === DownloadStatus.ARCHIVE
556
+ )
557
+
558
+ actions
559
+ .filter((v) => v.type === ActionType.Compress)
560
+ .forEach((action) => {
561
+ const existIndex = compressTask.findIndex((v) => v.id === action.id)
562
+ // 判断状态
563
+ // SUCCESS
564
+ // 存在,触发下载,从compressTask移除
565
+ if (action.status === DownloadStatus.SUCCESS && existIndex !== -1) {
566
+ // 展示弹窗
567
+ downloadUrl.value = action.url
568
+ showLinkModel.value = true
569
+ downLoadByUrl(action.url)
570
+ // ElMessage.success(`自动下载归档任务 ${action.tip}`)
571
+ compressTask.splice(existIndex, 1)
572
+ }
573
+ // Archive
574
+ // 不存在,push进compressTask
575
+ if (action.status === DownloadStatus.ARCHIVE && existIndex === -1) {
576
+ compressTask.push(action)
577
+ }
578
+
579
+ // ERROR
580
+ if (action.status === DownloadStatus.FAIL && existIndex !== -1) {
581
+ compressTask.splice(existIndex, 1)
582
+ }
583
+ })
584
+ // TODO:之后根据反馈优化
585
+ historyDownloadRecord.compressTask = compressTask
586
+ localStorage.setItem('ep_compress_task', JSON.stringify(compressTask))
587
+ if (haveArchive) {
588
+ // 递归查询
589
+ setTimeout(loadActions, 1000)
590
+ }
591
+ historyDownloadRecord.pageTotal = sum
592
+ historyDownloadRecord.actions = actions
593
+ historyDownloadRecord.pageCount = Math.ceil(
594
+ sum / historyDownloadRecord.pageSize
595
+ )
596
+ })
597
+ }
598
+ const handleHistoryActionPageChange = (v) => {
599
+ historyDownloadRecord.pageCurrent = v
600
+ loadActions()
601
+ }
602
+ // 记录导出
603
+ const handleExportExcel = (files: FileApiTypes.File[], filename?: string) => {
604
+ if (files.length === 0) {
605
+ ElMessage.warning('表格中没有可导出的内容')
606
+ return
607
+ }
608
+ const baseHeaders = ['提交时间', '任务', '文件名', '大小']
609
+ if (showOriginName.value) {
610
+ baseHeaders.push('原文件名')
611
+ }
612
+ if (showPeople.value) {
613
+ baseHeaders.push('姓名')
614
+ }
615
+ const headers: (string | tableItem)[] = baseHeaders.map((v) => ({
616
+ value: v,
617
+ row: 2
618
+ }))
619
+
620
+ const infosHeader = files.reduce((pre, value) => {
621
+ JSON.parse(value.info).forEach((i: any) => {
622
+ if (!pre.includes(i.text)) {
623
+ pre.push(i.text)
624
+ }
625
+ })
626
+ return pre
627
+ }, [])
628
+ headers.push({
629
+ value: '提交信息',
630
+ col: infosHeader.length
631
+ })
632
+
633
+ const body = files.map((v) => {
634
+ const { date, task_name: taskName, name, size, people } = v
635
+ const infoObj = JSON.parse(v.info).reduce((pre, v) => {
636
+ pre[v.text] = v.value
637
+ return pre
638
+ }, {})
639
+ const info = infosHeader.map((v) => infoObj[v] ?? '-')
640
+ const rows = [formatDate(new Date(date)), taskName, name, formatSize(size)]
641
+ if (showOriginName.value) {
642
+ rows.push(v.origin_name || '-')
643
+ }
644
+ if (showPeople.value) {
645
+ rows.push(people || '-')
646
+ }
647
+ rows.push(...info)
648
+ return rows
649
+ })
650
+ body.unshift(infosHeader)
651
+ tableToExcel(
652
+ headers,
653
+ body,
654
+ filename ||
655
+ `数据导出_${formatDate(new Date(), 'yyyy年MM月日hh时mm分ss秒')}.xlsx`
656
+ )
657
+ ElMessage.success('导出成功')
658
+ }
659
+ // 分类相关
660
+ const categories = computed(() => $store.state.category.categoryList)
661
+ const selectCategory = ref('all')
662
+ // 任务相关
663
+ const tasks = computed<TaskApiTypes.TaskItem[]>(
664
+ () => $store.state.task.taskList
665
+ )
666
+ const selectTask = ref('all')
667
+ const filterTasks = computed(() => {
668
+ if (selectCategory.value === 'all') {
669
+ return tasks.value
670
+ }
671
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
672
+ selectTask.value = 'all'
673
+ return tasks.value.filter((t) => t.category === selectCategory.value)
674
+ })
675
+ const selectTaskName = computed(() => {
676
+ const t = filterTasks.value.find((v) => v.key === selectTask.value)
677
+ return t?.name
678
+ })
679
+
680
+ watchEffect(() => {
681
+ if (
682
+ tasks.value.length &&
683
+ tasks.value.some((v) => v.key === $route.query.task)
684
+ ) {
685
+ selectTask.value = `${$route.query.task}`
686
+ }
687
+ })
688
+
689
+ const isLoadingData = ref(false)
690
+ // 提交的所有文件
691
+ const files: FileApiTypes.File[] = reactive([])
692
+ const loadFiles = () => {
693
+ isLoadingData.value = true
694
+ files.splice(0, files.length)
695
+ FileApi.getFileList().then((res) => {
696
+ files.push(...res.data.files)
697
+ isLoadingData.value = false
698
+ })
699
+ }
700
+ const multipleTable: any = ref()
701
+ const searchWord = ref('')
702
+
703
+ // 用于展示的文件
704
+ // 1. 过滤指定任务
705
+ const filterFiles = computed(() =>
706
+ files
707
+ .filter((f) => {
708
+ if (selectCategory.value === 'no-task') {
709
+ return tasks.value.every((t) => t.key !== f.task_key)
710
+ }
711
+ if (filterTasks.value.length === 0) {
712
+ return false
713
+ }
714
+
715
+ if (selectTask.value === 'all') {
716
+ return filterTasks.value.find((t) => t.key === f.task_key)
717
+ }
718
+
719
+ return selectTask.value === f.task_key
720
+ // 2. 过滤关键词(精细优化)
721
+ })
722
+ .filter((t) =>
723
+ searchWord.value
724
+ ? JSON.stringify([
725
+ formatDate(new Date(t.date)),
726
+ formatSize(t.size),
727
+ t.people,
728
+ t.name,
729
+ t.task_name,
730
+ // eslint-disable-next-line no-useless-escape
731
+ t.info
732
+ ])
733
+ .replace(/[:'"{},[\]]/g, '')
734
+ .includes(searchWord.value)
735
+ : true
736
+ )
737
+ )
738
+
739
+ /**
740
+ * 清空所有选项
741
+ */
742
+ const clearSelection = () => {
743
+ multipleTable.value.clearSelection()
744
+ }
745
+ // 多选选中的项
746
+ const selectItem: any[] = reactive([])
747
+ const handleSelectionChange = (e: any) => {
748
+ selectItem.splice(0, selectItem.length)
749
+ selectItem.push(...e)
750
+ }
751
+ const batchDownStart = ref(false)
752
+ const handleDropdownClick = (e: string) => {
753
+ const ids: number[] = selectItem.map((v) => v.id)
754
+ switch (e) {
755
+ case 'download':
756
+ if (selectItem.length === 0) {
757
+ ElMessage.warning('没有选中需要下载的内容')
758
+ return
759
+ }
760
+ if (batchDownStart.value) {
761
+ ElMessage.warning('已经有批量下载任务正在进行,请稍后再试')
762
+ return
763
+ }
764
+ FileApi.batchDownload(
765
+ ids,
766
+ `批量下载_${formatDate(new Date(), 'yyyy年MM月日hh时mm分ss秒')}`
767
+ )
768
+ .then(() => {
769
+ loadActions()
770
+ })
771
+ .catch(() => {
772
+ ElMessage.error('所选文件均已从服务器上移除')
773
+ batchDownStart.value = false
774
+ })
775
+ ElMessage.info('开始归档选中的文件,请赖心等待')
776
+ break
777
+ case 'delete':
778
+ if (selectItem.length === 0) {
779
+ ElMessage.warning('没有选中需要删除的内容')
780
+ return
781
+ }
782
+ ElMessageBox.confirm('删除后无法恢复,是否删除', '数据无价,请谨慎操作')
783
+ .then(() => {
784
+ FileApi.batchDel(ids).then(() => {
785
+ files.splice(
786
+ 0,
787
+ files.length,
788
+ ...files.filter((v) => !ids.includes(v.id))
789
+ )
790
+ ElMessage.success('删除成功')
791
+ })
792
+ })
793
+ .catch(() => {
794
+ ElMessage.info('取消')
795
+ })
796
+ break
797
+ case 'excel':
798
+ if (selectItem.length === 0) {
799
+ ElMessage.warning('没有选中需要导出的内容')
800
+ return
801
+ }
802
+ handleExportExcel(
803
+ selectItem,
804
+ `批量导出_${formatDate(new Date(), 'yyyy年MM月日hh时mm分ss秒')}.xlsx`
805
+ )
806
+ ElMessage.success('导出成功')
807
+ break
808
+ default:
809
+ break
810
+ }
811
+ clearSelection()
812
+ }
813
+ const showInfoDialog = ref(false)
814
+ const infos: any[] = reactive([])
815
+ const checkInfo = (e: any) => {
816
+ infos.splice(0, infos.length)
817
+ infos.push(...parseInfo(e.info))
818
+ showInfoDialog.value = true
819
+ }
820
+
821
+ const showRenameDialog = ref(false)
822
+ const renameForm = reactive({
823
+ oldName: '',
824
+ newName: '',
825
+ suffix: '',
826
+ id: -1
827
+ })
828
+ const rewriteFilename = (e: any) => {
829
+ const { id, name } = e
830
+ const suffix = getFileSuffix(name)
831
+ renameForm.oldName = name
832
+ renameForm.suffix = suffix
833
+ renameForm.id = id
834
+ showRenameDialog.value = true
835
+ }
836
+
837
+ const handleSaveNewName = () => {
838
+ // 文件名校验,不能有系统不支持的字符
839
+ if (filenamePattern.test(renameForm.newName)) {
840
+ ElMessage.error(`文件名不能包含${filenamePattern.source}等字符`)
841
+ filenamePattern.lastIndex = 0
842
+ return
843
+ }
844
+ FileApi.updateFilename(
845
+ renameForm.id,
846
+ `${renameForm.newName}${renameForm.suffix}`
847
+ )
848
+ .then(() => {
849
+ ElMessage.success('修改成功')
850
+ const file = files.find((v) => v.id === renameForm.id)
851
+ file.name = `${renameForm.newName}${renameForm.suffix}`
852
+ })
853
+ .catch(() => {
854
+ ElMessage.error('修改失败')
855
+ })
856
+ .finally(() => {
857
+ showRenameDialog.value = false
858
+ })
859
+ }
860
+
861
+ const downloadOne = (e: any) => {
862
+ const { id, name } = e
863
+ FileApi.getOneFileUrl(id)
864
+ .then((res) => {
865
+ const { link } = res.data
866
+ showLinkModel.value = true
867
+ downloadUrl.value = link
868
+ downLoadByUrl(link, name)
869
+ // 刷新
870
+ loadActions()
871
+ })
872
+ .catch(() => {
873
+ ElMessage.error('文件已从服务器上移除')
874
+ })
875
+ }
876
+ const handleDelete = (e: any) => {
877
+ const idx = files.findIndex((v) => v === e)
878
+ ElMessageBox.confirm('确认删除此文件吗?', '数据无价,请谨慎操作')
879
+ .then(() => {
880
+ FileApi.deleteOneFile(e.id).then(() => {
881
+ ElMessage.success('删除成功')
882
+ files.splice(idx, 1)
883
+ })
884
+ })
885
+ .catch(() => {
886
+ ElMessage.info('取消删除')
887
+ })
888
+ }
889
+
890
+ // 分页
891
+ const pageSize = ref(6)
892
+ const handleSizeChange = (v: number) => {
893
+ pageSize.value = v
894
+ }
895
+ const pageCount = computed(() => {
896
+ const t = Math.ceil(filterFiles.value.length / pageSize.value)
897
+ return t
898
+ })
899
+ // 当前页
900
+ const pageCurrent = ref(1)
901
+ const showFilterFiles = computed(() => {
902
+ const start = (pageCurrent.value - 1) * pageSize.value
903
+ const end = pageCurrent.value * pageSize.value
904
+ return filterFiles.value.slice(start, end)
905
+ })
906
+
907
+ const filterFileSize = computed(() =>
908
+ formatSize(filterFiles.value.reduce((acc, cur) => acc + cur.size, 0))
909
+ )
910
+ const fileListSize = computed(() =>
911
+ formatSize(files.reduce((acc, cur) => acc + cur.size, 0))
912
+ )
913
+ const handlePageChange = (idx: number) => {
914
+ pageCurrent.value = idx
915
+ }
916
+
917
+ // 刷新文件列表
918
+
919
+ const handleRefresh = () => {
920
+ ElMessage.success({
921
+ message: '刷新成功'
922
+ })
923
+ loadFiles()
924
+ }
925
+ const handleDownloadTask = () => {
926
+ const ids: number[] = files
927
+ .filter((f) => f.task_key === selectTask.value)
928
+ .map((v) => v.id)
929
+ if (ids.length === 0) {
930
+ ElMessage.warning('该任务中没有数据')
931
+ return
932
+ }
933
+ if (batchDownStart.value) {
934
+ ElMessage.warning('已经有批量下载任务正在进行,请稍后再试')
935
+ return
936
+ }
937
+ batchDownStart.value = true
938
+ FileApi.batchDownload(ids, selectTaskName.value)
939
+ .then(() => {
940
+ loadActions()
941
+ })
942
+ .catch(() => {
943
+ ElMessage.error('所选任务中的文件均已从服务器上移除')
944
+ })
945
+ .finally(() => {
946
+ setTimeout(() => {
947
+ batchDownStart.value = false
948
+ }, 1000)
949
+ })
950
+ ElMessage.info('开始归档选中的文件,请赖心等待')
951
+ }
952
+
953
+ const previewData = reactive<
954
+ { cover: string; preview: string; name: string; date: string; id: number }[]
955
+ >([])
956
+ const sortProps = reactive({
957
+ order: null,
958
+ prop: null
959
+ })
960
+ const viewImageFilename = ref('')
961
+ const handleFilesSortChange = (v) => {
962
+ sortProps.prop = v.prop
963
+ sortProps.order = v.order
964
+ }
965
+ const previewImages = computed(() => {
966
+ // if (!sortProps.prop) {
967
+ return previewData.map((v) => v.preview)
968
+ // }
969
+ // TODO:下面代码暂不生效,后续再支持表格排序场景
970
+ // const temp = [...previewData]
971
+ // temp.sort((a, b) => {
972
+ // if (sortProps.order === 'descending') {
973
+ // return a[sortProps.prop] - b[sortProps.prop]
974
+ // }
975
+ // return b[sortProps.prop] - a[sortProps.prop]
976
+ // })
977
+ // return temp.map((v) => v.preview)
978
+ })
979
+
980
+ const handleSwitchImage = (idx: number) => {
981
+ viewImageFilename.value = showFilterFiles.value[idx].name
982
+ }
983
+
984
+ let fetching = false
985
+ const refreshFilesCover = () => {
986
+ const ids = showFilterFiles.value.map((v) => v.id)
987
+ if (ids.length === 0 || fetching) {
988
+ return
989
+ }
990
+ fetching = true
991
+ FileApi.checkImageFilePreviewUrl(ids).then((r) => {
992
+ fetching = false
993
+ const { data } = r
994
+ if (data.length === 0 || data.length !== showFilterFiles.value.length) {
995
+ return
996
+ }
997
+ previewData.splice(0, previewData.length)
998
+ showFilterFiles.value.forEach((v, idx) => {
999
+ const { cover, preview } = data[idx]
1000
+ v.cover = cover
1001
+ previewData.push({ cover, preview, name: v.name, date: v.date, id: v.id })
1002
+ })
1003
+ })
1004
+ }
1005
+ watchEffect(() => {
1006
+ window.localStorage.setItem('ep-show-images', `${showImg.value}`)
1007
+ if (!showImg.value) {
1008
+ return
1009
+ }
1010
+ if (searchWord.value || pageCurrent.value || pageSize.value) {
1011
+ refreshFilesCover()
1012
+ return
1013
+ }
1014
+ refreshFilesCover()
1015
+ })
1016
+
1017
+ onMounted(() => {
1018
+ loadFiles()
1019
+ loadActions()
1020
+ $store.dispatch('category/getCategory')
1021
+ $store.dispatch('task/getTask')
1022
+ })
1023
+
1024
+ const isMobile = computed(() => $store.getters['public/isMobile'])
1025
+ </script>
1026
+ <style scoped lang="scss">
1027
+ .files {
1028
+ max-width: 1024px;
1029
+ margin: 0 auto;
1030
+ padding-bottom: 2em;
1031
+ }
1032
+
1033
+ @media screen and (max-width: 700px) {
1034
+ .files {
1035
+ margin-top: 70px;
1036
+ }
1037
+
1038
+ .text-btns {
1039
+ display: flex;
1040
+ flex-direction: column;
1041
+
1042
+ :deep(.el-button) {
1043
+ margin-left: 0px;
1044
+ margin-bottom: 0px;
1045
+ }
1046
+ }
1047
+
1048
+ .header {
1049
+ justify-content: center;
1050
+ }
1051
+
1052
+ .export-btns {
1053
+ display: flex;
1054
+ flex-wrap: wrap;
1055
+ justify-content: space-around;
1056
+ }
1057
+ }
1058
+
1059
+ .panel {
1060
+ padding: 1em;
1061
+ background-color: #fff;
1062
+ margin: 10px auto;
1063
+ box-sizing: border-box;
1064
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
1065
+ border-radius: 4px;
1066
+ z-index: 1;
1067
+
1068
+ .label {
1069
+ font-size: 12px;
1070
+ margin-right: 10px;
1071
+ }
1072
+ }
1073
+
1074
+ .header {
1075
+ display: flex;
1076
+ flex-wrap: wrap;
1077
+
1078
+ .item {
1079
+ margin-right: 10px;
1080
+ margin-bottom: 10px;
1081
+ }
1082
+ }
1083
+
1084
+ .el-button {
1085
+ margin-left: 10px;
1086
+ margin-bottom: 10px;
1087
+ }
1088
+
1089
+ .control-item {
1090
+ margin-left: 10px;
1091
+ margin-bottom: 10px;
1092
+ font-size: 14px;
1093
+ }
1094
+
1095
+ .imageLoading {
1096
+ display: flex;
1097
+ align-items: center;
1098
+ justify-content: center;
1099
+ height: 100%;
1100
+ }
1101
+ .imageDes {
1102
+ position: absolute;
1103
+ bottom: 80px;
1104
+ color: #fff;
1105
+ left: 50%;
1106
+ transform: translateX(-50%);
1107
+ }
1108
+
1109
+ .progress-list {
1110
+ margin-top: 10px;
1111
+
1112
+ .progress-item {
1113
+ margin-bottom: 10px;
1114
+
1115
+ .progress {
1116
+ display: flex;
1117
+ justify-content: center;
1118
+ align-items: center;
1119
+ margin-bottom: 6px;
1120
+
1121
+ .el-progress--line {
1122
+ min-width: 200px;
1123
+ width: 260px;
1124
+ }
1125
+
1126
+ .el-button {
1127
+ margin: 0 6px;
1128
+ }
1129
+ }
1130
+
1131
+ .des {
1132
+ font-size: 12px;
1133
+
1134
+ .filename {
1135
+ max-width: 200px;
1136
+ overflow: hidden;
1137
+ text-overflow: ellipsis;
1138
+ white-space: nowrap;
1139
+ word-break: keep-all;
1140
+ margin-right: 10px;
1141
+ }
1142
+
1143
+ .mimeType {
1144
+ width: 60px;
1145
+ color: #409eff;
1146
+ }
1147
+ }
1148
+
1149
+ text-align: center;
1150
+ }
1151
+ }
1152
+ </style>