@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,679 @@
1
+ <template>
2
+ <div class="user">
3
+ <div class="panel">
4
+ <div class="p10 log-filter">
5
+ <span class="item">
6
+ <span class="label">状态</span>
7
+ <el-select
8
+ v-model="filterLogType"
9
+ size="default"
10
+ placeholder="请选择日志类型"
11
+ >
12
+ <el-option
13
+ v-for="(item, idx) in logTypeList"
14
+ :key="idx"
15
+ :label="item.label"
16
+ :value="item.type"
17
+ ></el-option>
18
+ </el-select>
19
+ </span>
20
+ <span class="item">
21
+ <el-input
22
+ size="default"
23
+ clearable
24
+ placeholder="请输入要检索的内容"
25
+ :prefix-icon="Search"
26
+ v-model="searchWord"
27
+ >
28
+ </el-input>
29
+ </span>
30
+ <span class="item">
31
+ <el-button size="default" :icon="Refresh" @click="refreshUsers"
32
+ >刷新</el-button
33
+ >
34
+ </span>
35
+ <span class="item">
36
+ <el-button
37
+ size="warning"
38
+ :icon="Message"
39
+ @click="sendMessage(null, 0)"
40
+ >推送全局消息</el-button
41
+ >
42
+ </span>
43
+ </div>
44
+ <el-table
45
+ height="550"
46
+ stripe
47
+ border
48
+ :default-sort="{ prop: 'date', order: 'descending' }"
49
+ :data="pageUsers"
50
+ style="width: 100%"
51
+ >
52
+ <el-table-column
53
+ prop="account"
54
+ label="账号"
55
+ width="120"
56
+ ></el-table-column>
57
+ <el-table-column
58
+ prop="phone"
59
+ label="手机号"
60
+ width="100"
61
+ ></el-table-column>
62
+ <el-table-column
63
+ sortable
64
+ prop="login_time"
65
+ label="最后登录时间"
66
+ width="190"
67
+ >
68
+ <template #default="scope">{{
69
+ scope.row.login_time && formatDate(new Date(scope.row.login_time))
70
+ }}</template>
71
+ </el-table-column>
72
+ <el-table-column prop="join_time" label="注册时间" width="190">
73
+ <template #default="scope">{{
74
+ formatDate(new Date(scope.row.join_time))
75
+ }}</template>
76
+ </el-table-column>
77
+ <el-table-column
78
+ sortable
79
+ prop="login_count"
80
+ label="登录次数"
81
+ ></el-table-column>
82
+ <el-table-column
83
+ prop="open_time"
84
+ label="解封时间"
85
+ v-if="filterLogType === 1"
86
+ >
87
+ <template #default="scope">{{
88
+ scope.row.open_time && formatDate(new Date(scope.row.open_time))
89
+ }}</template>
90
+ </el-table-column>
91
+ <el-table-column
92
+ sortable
93
+ prop="fileCount"
94
+ label="收集文件数"
95
+ ></el-table-column>
96
+ <el-table-column label="占用云空间" width="200">
97
+ <template
98
+ #default="{
99
+ row: { resources, monthAgoSize, quarterAgoSize, halfYearSize, id }
100
+ }"
101
+ >
102
+ <ul class="user-oss-info">
103
+ <li>总大小:{{ resources }}</li>
104
+ <li>
105
+ 一月前:{{ monthAgoSize
106
+ }}<el-button
107
+ v-if="resources !== '0B'"
108
+ class="clear-btn"
109
+ @click="handleClearFiles(id, 'month')"
110
+ :icon="DeleteFilled"
111
+ circle
112
+ size="small"
113
+ ></el-button>
114
+ </li>
115
+ <li>
116
+ 三月前:{{ quarterAgoSize
117
+ }}<el-button
118
+ v-if="quarterAgoSize !== '0B'"
119
+ class="clear-btn"
120
+ @click="handleClearFiles(id, 'quarter')"
121
+ :icon="DeleteFilled"
122
+ circle
123
+ size="small"
124
+ ></el-button>
125
+ </li>
126
+ <li>
127
+ 半年前:{{ halfYearSize
128
+ }}<el-button
129
+ v-if="halfYearSize !== '0B'"
130
+ class="clear-btn"
131
+ @click="handleClearFiles(id, 'half')"
132
+ :icon="DeleteFilled"
133
+ circle
134
+ size="small"
135
+ ></el-button>
136
+ </li>
137
+ </ul>
138
+ </template>
139
+ </el-table-column>
140
+ <el-table-column fixed="right" label="操作" width="100">
141
+ <template #default="scope">
142
+ <div class="text-btn-list">
143
+ <el-button
144
+ @click="
145
+ handleChangeStatus(
146
+ scope.row.id,
147
+ scope.row.status,
148
+ scope.row.open_time
149
+ )
150
+ "
151
+ type="primary"
152
+ text
153
+ size="small"
154
+ >修改状态</el-button
155
+ >
156
+ <el-button
157
+ @click="handleResetPassword(scope.row.id)"
158
+ type="primary"
159
+ text
160
+ size="small"
161
+ >重置密码</el-button
162
+ >
163
+ <el-button
164
+ @click="handleBindPhone(scope.row.id)"
165
+ type="primary"
166
+ text
167
+ size="small"
168
+ >绑定手机号</el-button
169
+ >
170
+ <el-button
171
+ @click="sendMessage(scope.row.id)"
172
+ type="warning"
173
+ text
174
+ size="small"
175
+ >发送消息</el-button
176
+ >
177
+ </div>
178
+ </template>
179
+ </el-table-column>
180
+ </el-table>
181
+ <div class="flex fc p10">
182
+ <el-pagination
183
+ :current-page="pageCurrent"
184
+ @current-change="handlePageChange"
185
+ background
186
+ :page-count="pageCount"
187
+ :page-sizes="[10, 50, 100, 200]"
188
+ :page-size="pageSize"
189
+ @size-change="handleSizeChange"
190
+ :total="filterUsers.length"
191
+ layout="total, sizes, prev, pager, next, jumper"
192
+ ></el-pagination>
193
+ </div>
194
+ </div>
195
+ <!-- 消息推送弹窗 -->
196
+ <el-dialog
197
+ :fullscreen="isMobile"
198
+ center
199
+ title="消息推送"
200
+ v-model="showMessageDialog"
201
+ >
202
+ <div class="tc">
203
+ <el-input
204
+ v-model="pushMessageText"
205
+ :autosize="{ minRows: 6, maxRows: 20 }"
206
+ type="textarea"
207
+ placeholder="输入要推送的消息,支持HTML内容(推荐使用mdnice 转 markdown 转html)"
208
+ />
209
+ </div>
210
+ <template #footer>
211
+ <span class="dialog-footer">
212
+ <el-button @click="showMessageDialog = false">取 消</el-button>
213
+ <el-button type="primary" @click="sureSendMessage">确 定</el-button>
214
+ </span>
215
+ </template>
216
+ </el-dialog>
217
+ <!-- 用户状态修改弹窗 -->
218
+ <el-dialog
219
+ :fullscreen="isMobile"
220
+ center
221
+ title="状态修改"
222
+ v-model="showUserStatusDialog"
223
+ >
224
+ <div class="tc">
225
+ <el-select v-model="selectStatus" placeholder="请选择新分类">
226
+ <el-option
227
+ v-for="s in userStatusList"
228
+ :key="s.type"
229
+ :label="s.label"
230
+ :value="s.type"
231
+ ></el-option>
232
+ </el-select>
233
+ </div>
234
+ <div style="margin-top: 10px" class="tc" v-if="selectStatus === 1">
235
+ <el-date-picker
236
+ :editable="false"
237
+ v-model="openTime"
238
+ type="datetime"
239
+ placeholder="点击设置解封日期"
240
+ ></el-date-picker>
241
+ </div>
242
+ <template #footer>
243
+ <span class="dialog-footer">
244
+ <el-button @click="showUserStatusDialog = false">取 消</el-button>
245
+ <el-button type="primary" @click="handleSaveStatus">确 定</el-button>
246
+ </span>
247
+ </template>
248
+ </el-dialog>
249
+
250
+ <!-- 重置密码 -->
251
+ <el-dialog
252
+ :fullscreen="isMobile"
253
+ center
254
+ title="密码重置"
255
+ v-model="showResetPasswordDialog"
256
+ >
257
+ <div class="tc">
258
+ <el-form :model="pwdForm" label-width="80px">
259
+ <el-form-item label="新密码">
260
+ <el-input
261
+ show-word-limit
262
+ clearable
263
+ v-model="pwdForm.pwd1"
264
+ placeholder="请输入新密码"
265
+ maxlength="16"
266
+ minlength="6"
267
+ />
268
+ </el-form-item>
269
+ <el-form-item>
270
+ <el-input
271
+ show-word-limit
272
+ clearable
273
+ v-model="pwdForm.pwd2"
274
+ placeholder="请再次输入"
275
+ maxlength="16"
276
+ minlength="6"
277
+ />
278
+ </el-form-item>
279
+ </el-form>
280
+ </div>
281
+ <template #footer>
282
+ <span class="dialog-footer">
283
+ <el-button @click="showResetPasswordDialog = false">取 消</el-button>
284
+ <el-button type="primary" @click="handleSavePassword"
285
+ >确 定</el-button
286
+ >
287
+ </span>
288
+ </template>
289
+ </el-dialog>
290
+ <!-- 重绑定手机号 -->
291
+ <el-dialog
292
+ :fullscreen="isMobile"
293
+ center
294
+ title="绑定手机号"
295
+ v-model="showPhoneDialog"
296
+ >
297
+ <div class="tc">
298
+ <el-form :model="phoneForm" label-width="60px">
299
+ <el-form-item label="手机号">
300
+ <el-input
301
+ show-word-limit
302
+ clearable
303
+ v-model="phoneForm.phone"
304
+ placeholder="请输入手机号"
305
+ maxlength="11"
306
+ >
307
+ <template #append>
308
+ <!-- 获取验证码 -->
309
+ <el-button :disabled="time !== 0" @click="getCode">{{
310
+ codeText
311
+ }}</el-button>
312
+ </template>
313
+ </el-input>
314
+ </el-form-item>
315
+ <el-form-item>
316
+ <el-input
317
+ show-word-limit
318
+ clearable
319
+ v-model="phoneForm.code"
320
+ placeholder="请输入验证码"
321
+ maxlength="4"
322
+ />
323
+ </el-form-item>
324
+ </el-form>
325
+ </div>
326
+ <template #footer>
327
+ <span class="dialog-footer">
328
+ <el-button @click="showPhoneDialog = false">取 消</el-button>
329
+ <el-button type="primary" @click="handleSavePhone">确 定</el-button>
330
+ </span>
331
+ </template>
332
+ </el-dialog>
333
+ </div>
334
+ </template>
335
+ <script lang="ts" setup>
336
+ import { ElMessage, ElMessageBox } from 'element-plus'
337
+ import { computed, onMounted, reactive, ref } from 'vue'
338
+ import { useStore } from 'vuex'
339
+ import { Search, DeleteFilled, Refresh, Message } from '@element-plus/icons-vue'
340
+ import { PublicApi, SuperUserApi } from '@/apis'
341
+ import { USER_STATUS } from '@/constants'
342
+ import { formatDate } from '@/utils/stringUtil'
343
+ import { rMobilePhone, rPassword, rVerCode } from '@/utils/regExp'
344
+
345
+ const $store = useStore()
346
+ // 用户
347
+ const users: any[] = reactive([])
348
+ const refreshUsers = () => {
349
+ SuperUserApi.getUserList().then((res) => {
350
+ users.splice(0, users.length)
351
+ const d = res.data.list
352
+ users.push(...d)
353
+ })
354
+ }
355
+
356
+ // 筛选用户状态
357
+ const filterLogType = ref(USER_STATUS.NORMAL)
358
+ const searchWord = ref('')
359
+ const logTypeList = reactive([
360
+ {
361
+ label: '正常',
362
+ type: USER_STATUS.NORMAL
363
+ },
364
+ {
365
+ label: '冻结',
366
+ type: USER_STATUS.FREEZE
367
+ },
368
+ {
369
+ label: '封禁',
370
+ type: USER_STATUS.BAN
371
+ }
372
+ ])
373
+
374
+ const filterUsers = computed(() =>
375
+ users
376
+ .filter((v) => v.status === filterLogType.value)
377
+ .filter((v) => {
378
+ const { account, phone, join_time, login_count, login_time, open_time } =
379
+ v
380
+ if (searchWord.value.length === 0) return true
381
+ return `${account} ${phone} ${login_count} ${formatDate(
382
+ open_time
383
+ )} ${formatDate(login_time)} ${formatDate(join_time)}`.includes(
384
+ searchWord.value
385
+ )
386
+ })
387
+ )
388
+
389
+ // 分页
390
+ const pageSize = ref(10)
391
+ const handleSizeChange = (v: number) => {
392
+ pageSize.value = v
393
+ }
394
+ const pageCount = computed(() => {
395
+ const t = Math.ceil(filterUsers.value.length / pageSize.value)
396
+ return t
397
+ })
398
+ const pageCurrent = ref(1)
399
+ const pageUsers = computed(() => {
400
+ const start = (pageCurrent.value - 1) * pageSize.value
401
+ const end = pageCurrent.value * pageSize.value
402
+ return filterUsers.value.slice(start, end)
403
+ })
404
+ const handlePageChange = (idx: number) => {
405
+ pageCurrent.value = idx
406
+ }
407
+
408
+ // 状态修改
409
+ const showUserStatusDialog = ref(false)
410
+ const selectUserId = ref(0)
411
+ const selectStatus = ref(USER_STATUS.NORMAL)
412
+ const userStatusList = logTypeList
413
+ const openTime = ref('')
414
+ const handleChangeStatus = (
415
+ userId: number,
416
+ status: USER_STATUS,
417
+ oTime: string
418
+ ) => {
419
+ selectUserId.value = userId
420
+ selectStatus.value = status
421
+ openTime.value = oTime
422
+ showUserStatusDialog.value = true
423
+ }
424
+ const handleSaveStatus = () => {
425
+ const user = users.find((u) => u.id === selectUserId.value)
426
+ if (selectStatus.value === USER_STATUS.FREEZE) {
427
+ if (!openTime.value) {
428
+ ElMessage.warning('请设置解冻时间')
429
+ return
430
+ }
431
+ user.open_time = openTime.value
432
+ } else {
433
+ user.open_time = ''
434
+ }
435
+ user.status = selectStatus.value
436
+ showUserStatusDialog.value = false
437
+ SuperUserApi.updateUserStatus(user.id, user.status, user.open_time).then(
438
+ () => {
439
+ ElMessage.success('修改成功')
440
+ }
441
+ )
442
+ }
443
+
444
+ // 重置密码
445
+ const showResetPasswordDialog = ref(false)
446
+ const pwdForm = reactive({
447
+ pwd1: '',
448
+ pwd2: ''
449
+ })
450
+ const handleResetPassword = (userId: number) => {
451
+ selectUserId.value = userId
452
+ showResetPasswordDialog.value = true
453
+ pwdForm.pwd1 = ''
454
+ pwdForm.pwd2 = ''
455
+ }
456
+
457
+ const checkPwdForm = () => {
458
+ if (!rPassword.test(pwdForm.pwd1)) {
459
+ ElMessage.warning('密码格式不正确(6-16位 支持字母/数字/下划线)')
460
+ return false
461
+ }
462
+ if (pwdForm.pwd1 !== pwdForm.pwd2) {
463
+ ElMessage.warning('两次输入的密码不一致')
464
+ return false
465
+ }
466
+
467
+ return true
468
+ }
469
+
470
+ const handleSavePassword = () => {
471
+ if (!checkPwdForm()) return
472
+ ElMessageBox.confirm('此操作不可逆,请谨慎操作', '确定要重置用户的密码吗?', {
473
+ confirmButtonText: '确定',
474
+ cancelButtonText: '取消',
475
+ type: 'warning'
476
+ })
477
+ .then(() => {
478
+ SuperUserApi.resetPassword(selectUserId.value, pwdForm.pwd1).then(() => {
479
+ ElMessage.success('重置成功')
480
+ showResetPasswordDialog.value = false
481
+ })
482
+ })
483
+ .catch(() => {
484
+ //
485
+ })
486
+ }
487
+
488
+ // 绑定手机号
489
+ const showPhoneDialog = ref(false)
490
+ const phoneForm = reactive({
491
+ phone: '',
492
+ code: ''
493
+ })
494
+ const codeText = ref('获取验证码')
495
+ const time = ref(0)
496
+ const refreshCodeText = () => {
497
+ if (time.value === 0) {
498
+ codeText.value = '获取验证码'
499
+ return
500
+ }
501
+ codeText.value = `${time.value}s`
502
+ time.value -= 1
503
+ setTimeout(refreshCodeText, 1000)
504
+ }
505
+ const getCode = () => {
506
+ if (!rMobilePhone.test(phoneForm.phone)) {
507
+ ElMessage.warning('手机号格式不正确')
508
+ return
509
+ }
510
+ // check是否可用
511
+ PublicApi.checkPhone(phoneForm.phone)
512
+ .then(() => {
513
+ PublicApi.getCode(phoneForm.phone).then(() => {
514
+ time.value = 120
515
+ refreshCodeText()
516
+ ElMessage.success('获取成功,请注意查看手机短信')
517
+ })
518
+ })
519
+ .catch((err) => {
520
+ // TODO:编写通用方法处理失败信息弹窗回掉
521
+ const { code: c } = err
522
+ const msg = '注册失败,未知错误'
523
+ const options: any = {
524
+ 1002: '手机号已被注册',
525
+ 1006: '手机号格式不正确'
526
+ }
527
+ ElMessage.error(options[c] || msg)
528
+ })
529
+ }
530
+ const checkPhoneForm = () => {
531
+ if (!rMobilePhone.test(phoneForm.phone)) {
532
+ ElMessage.warning('手机号格式不正确')
533
+ return false
534
+ }
535
+ if (!rVerCode.test(phoneForm.code)) {
536
+ ElMessage.warning('验证码格式不正确')
537
+ return false
538
+ }
539
+
540
+ return true
541
+ }
542
+ const handleBindPhone = (id: number) => {
543
+ selectUserId.value = id
544
+ showPhoneDialog.value = true
545
+ }
546
+ const handleSavePhone = async () => {
547
+ if (!checkPhoneForm()) {
548
+ return
549
+ }
550
+ // 调用API更新,验证码 不正确判断
551
+ SuperUserApi.resetPhone(selectUserId.value, phoneForm.phone, phoneForm.code)
552
+ .then(() => {
553
+ ElMessage.success('绑定成功')
554
+ showPhoneDialog.value = false
555
+ phoneForm.code = ''
556
+ phoneForm.phone = ''
557
+ refreshUsers()
558
+ })
559
+ .catch((err) => {
560
+ const { code: c } = err
561
+ const msg = '绑定失败,未知错误'
562
+ const options: any = {
563
+ 1002: '手机号已被注册',
564
+ 1003: '验证码不正确'
565
+ }
566
+ ElMessage.error(options[c] || msg)
567
+ })
568
+ }
569
+
570
+ const handleClearFiles = (
571
+ userId: number,
572
+ type: 'month' | 'quarter' | 'half'
573
+ ) => {
574
+ const tipWords = {
575
+ month: '一个月前',
576
+ quarter: '三个月前',
577
+ half: '半年前'
578
+ }
579
+ selectUserId.value = userId
580
+ ElMessageBox.confirm('移除后这些文件将无法恢复,请谨慎操作', '删除前确认?', {
581
+ confirmButtonText: `确认删除 ${tipWords[type]}文件`
582
+ })
583
+ .then(() => {
584
+ SuperUserApi.clearOssFile(userId, type).then(() => {
585
+ ElMessage.success('清理成功')
586
+ })
587
+ })
588
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
589
+ .catch(() => {})
590
+ }
591
+
592
+ // TODO: 0 global 1 user_push
593
+ const pushMessageType = ref(1)
594
+ const showMessageDialog = ref(false)
595
+ const pushMessageText = ref('')
596
+ const sendMessage = (id: number, type = 1) => {
597
+ selectUserId.value = id
598
+ pushMessageType.value = type
599
+ showMessageDialog.value = true
600
+ }
601
+
602
+ const sureSendMessage = () => {
603
+ SuperUserApi.sendMessage(
604
+ pushMessageText.value,
605
+ pushMessageType.value,
606
+ selectUserId.value
607
+ ).then(() => {
608
+ ElMessage.success('推送成功')
609
+ // 推送成功
610
+ pushMessageText.value = ''
611
+ showMessageDialog.value = false
612
+ })
613
+ }
614
+
615
+ onMounted(() => {
616
+ refreshUsers()
617
+ })
618
+ const isMobile = computed(() => $store.getters['public/isMobile'])
619
+ </script>
620
+
621
+ <style scoped lang="scss">
622
+ @media screen and (max-width: 700px) {
623
+ .user {
624
+ margin-top: 40px !important;
625
+ }
626
+
627
+ .log-filter {
628
+ justify-content: center;
629
+ }
630
+ }
631
+
632
+ .user {
633
+ margin: 0 auto;
634
+ }
635
+
636
+ .panel {
637
+ max-width: 1256px;
638
+ padding: 1em;
639
+ background-color: #fff;
640
+ margin: 10px auto;
641
+ box-sizing: border-box;
642
+ box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
643
+ border-radius: 4px;
644
+ }
645
+
646
+ .log-filter {
647
+ display: flex;
648
+ flex-wrap: wrap;
649
+
650
+ .item {
651
+ margin-right: 10px;
652
+ margin-bottom: 10px;
653
+
654
+ .label {
655
+ margin-right: 10px;
656
+ font-size: 12px;
657
+ }
658
+ }
659
+ }
660
+
661
+ .text-btn-list {
662
+ display: flex;
663
+ flex-wrap: wrap;
664
+
665
+ button {
666
+ margin-left: 0;
667
+ }
668
+ }
669
+
670
+ .user-oss-info {
671
+ list-style: none;
672
+ li {
673
+ margin-bottom: 10px;
674
+ }
675
+ .clear-btn {
676
+ margin-left: 10px;
677
+ }
678
+ }
679
+ </style>