Commit 7db04cf06679855672c57fcd3c4e3b4ee2761b92

Authored by xingyu
1 parent 09e61f7e

refactor: user

yudao-ui-admin-vue3/src/api/system/user/index.ts
1 1 import request from '@/config/axios'
2   -import type { UserVO } from './types'
  2 +export type UserVO = {
  3 + id: number
  4 + username: string
  5 + nickname: string
  6 + deptId: number
  7 + postIds: string[]
  8 + email: string
  9 + mobile: string
  10 + sex: number
  11 + avatar: string
  12 + loginIp: string
  13 + status: number
  14 + remark: string
  15 + loginDate: string
  16 + createTime: string
  17 +}
  18 +
  19 +export interface UserPageReqVO extends PageParam {
  20 + deptId?: number
  21 + username?: string
  22 + mobile?: string
  23 + status?: number
  24 + createTime?: string[]
  25 +}
  26 +
  27 +export interface UserExportReqVO {
  28 + code?: string
  29 + name?: string
  30 + status?: number
  31 + createTime?: string[]
  32 +}
3 33  
4 34 // 查询用户管理列表
5   -export const getUserPageApi = (params) => {
  35 +export const getUserPageApi = (params: UserPageReqVO) => {
6 36 return request.get({ url: '/system/user/page', params })
7 37 }
8 38  
... ... @@ -27,7 +57,7 @@ export const deleteUserApi = (id: number) => {
27 57 }
28 58  
29 59 // 导出用户
30   -export const exportUserApi = (params) => {
  60 +export const exportUserApi = (params: UserExportReqVO) => {
31 61 return request.download({ url: '/system/user/export', params })
32 62 }
33 63  
... ...
yudao-ui-admin-vue3/src/api/system/user/types.ts deleted
1   -export type UserVO = {
2   - id: number
3   - username: string
4   - nickname: string
5   - deptId: number
6   - postIds: string[]
7   - email: string
8   - mobile: string
9   - sex: number
10   - avatar: string
11   - loginIp: string
12   - status: number
13   - remark: string
14   - loginDate: string
15   - createTime: string
16   -}
yudao-ui-admin-vue3/src/views/system/user/index.vue
... ... @@ -20,56 +20,38 @@
20 20 @node-click="handleDeptNodeClick"
21 21 />
22 22 </el-card>
23   - <!-- 搜索工作区 -->
24 23 <el-card class="w-4/5 user" style="margin-left: 10px" :gutter="12" shadow="hover">
25 24 <template #header>
26 25 <div class="card-header">
27 26 <span>{{ tableTitle }}</span>
28 27 </div>
29 28 </template>
30   - <Search
31   - :schema="allSchemas.searchSchema"
32   - @search="setSearchParams"
33   - @reset="setSearchParams"
34   - ref="searchForm"
35   - />
36   - <!-- 操作工具栏 -->
37   - <div class="mb-10px">
38   - <XButton
39   - type="primary"
40   - preIcon="ep:zoom-in"
41   - :title="t('action.add')"
42   - v-hasPermi="['system:user:create']"
43   - @click="handleCreate()"
44   - />
45   - <XButton
46   - type="warning"
47   - preIcon="ep:upload"
48   - :title="t('action.import')"
49   - v-hasPermi="['system:user:import']"
50   - @click="importDialogVisible = true"
51   - />
52   - <XButton
53   - type="warning"
54   - preIcon="ep:download"
55   - :title="t('action.export')"
56   - v-hasPermi="['system:user:export']"
57   - @click="exportList('用户数据.xls')"
58   - />
59   - </div>
60 29 <!-- 列表 -->
61   - <Table
62   - :columns="allSchemas.tableColumns"
63   - :selection="false"
64   - :data="tableObject.tableList"
65   - :loading="tableObject.loading"
66   - :pagination="{
67   - total: tableObject.total
68   - }"
69   - v-model:pageSize="tableObject.pageSize"
70   - v-model:currentPage="tableObject.currentPage"
71   - @register="register"
72   - >
  30 + <vxe-grid ref="xGrid" v-bind="gridOptions" class="xtable-scrollbar">
  31 + <!-- 操作:新增 -->
  32 + <template #toolbar_buttons>
  33 + <XButton
  34 + type="primary"
  35 + preIcon="ep:zoom-in"
  36 + :title="t('action.add')"
  37 + v-hasPermi="['system:user:create']"
  38 + @click="handleCreate()"
  39 + />
  40 + <XButton
  41 + type="warning"
  42 + preIcon="ep:upload"
  43 + :title="t('action.import')"
  44 + v-hasPermi="['system:user:import']"
  45 + @click="importDialogVisible = true"
  46 + />
  47 + <XButton
  48 + type="warning"
  49 + preIcon="ep:download"
  50 + :title="t('action.export')"
  51 + v-hasPermi="['system:user:export']"
  52 + @click="exportList('用户数据.xls')"
  53 + />
  54 + </template>
73 55 <template #status="{ row }">
74 56 <el-switch
75 57 v-model="row.status"
... ... @@ -78,10 +60,7 @@
78 60 @change="handleStatusChange(row)"
79 61 />
80 62 </template>
81   - <template #loginDate="{ row }">
82   - <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
83   - </template>
84   - <template #action="{ row }">
  63 + <template #actionbtns_default="{ row }">
85 64 <XTextButton
86 65 preIcon="ep:edit"
87 66 :title="t('action.edit')"
... ... @@ -112,10 +91,10 @@
112 91 preIcon="ep:delete"
113 92 :title="t('action.del')"
114 93 v-hasPermi="['system:user:delete']"
115   - @click="delList(row.id, false)"
  94 + @click="handleDelete(row.id)"
116 95 />
117 96 </template>
118   - </Table>
  97 + </vxe-grid>
119 98 </el-card>
120 99 </div>
121 100 <XModal v-model="dialogVisible" :title="dialogTitle">
... ... @@ -150,7 +129,7 @@
150 129 <Descriptions
151 130 v-if="actionType === 'detail'"
152 131 :schema="allSchemas.detailSchema"
153   - :data="detailRef"
  132 + :data="detailData"
154 133 >
155 134 <template #deptId="{ row }">
156 135 <span>{{ row.dept?.name }}</span>
... ... @@ -200,10 +179,16 @@
200 179 </el-form>
201 180 <!-- 操作按钮 -->
202 181 <template #footer>
203   - <el-button type="primary" :loading="loading" @click="submitRole">
204   - {{ t('action.save') }}
205   - </el-button>
206   - <el-button @click="roleDialogVisible = false">{{ t('dialog.close') }}</el-button>
  182 + <!-- 按钮:保存 -->
  183 + <XButton
  184 + v-if="['create', 'update'].includes(actionType)"
  185 + type="primary"
  186 + :title="t('action.save')"
  187 + :loading="loading"
  188 + @click="submitRole()"
  189 + />
  190 + <!-- 按钮:关闭 -->
  191 + <XButton :title="t('dialog.close')" @click="roleDialogVisible = false" />
207 192 </template>
208 193 </XModal>
209 194 <!-- 导入 -->
... ... @@ -244,17 +229,20 @@
244 229 </el-form-item>
245 230 </el-form>
246 231 <template #footer>
247   - <el-button type="primary" @click="submitFileForm">
248   - <Icon icon="ep:upload-filled" />
249   - {{ t('action.save') }}
250   - </el-button>
251   - <el-button @click="importDialogVisible = false">{{ t('dialog.close') }}</el-button>
  232 + <!-- 按钮:保存 -->
  233 + <XButton
  234 + type="warning"
  235 + preIcon="ep:upload-filled"
  236 + :title="t('action.save')"
  237 + @click="submitFileForm()"
  238 + />
  239 + <!-- 按钮:关闭 -->
  240 + <XButton :title="t('dialog.close')" @click="importDialogVisible = false" />
252 241 </template>
253 242 </XModal>
254 243 </template>
255 244 <script setup lang="ts">
256   -import { nextTick, onMounted, reactive, ref, unref, watch } from 'vue'
257   -import dayjs from 'dayjs'
  245 +import { onMounted, reactive, ref, unref, watch } from 'vue'
258 246 import {
259 247 ElTag,
260 248 ElInput,
... ... @@ -274,9 +262,7 @@ import {
274 262 } from 'element-plus'
275 263 import { handleTree } from '@/utils/tree'
276 264 import { useI18n } from '@/hooks/web/useI18n'
277   -import { useTable } from '@/hooks/web/useTable'
278 265 import { FormExpose } from '@/components/Form'
279   -import type { UserVO } from '@/api/system/user/types'
280 266 import type { PostVO } from '@/api/system/post'
281 267 import type { PermissionAssignUserRoleReqVO } from '@/api/system/permission/types'
282 268 import { listSimpleDeptApi } from '@/api/system/dept'
... ... @@ -290,29 +276,31 @@ import { useRouter } from &#39;vue-router&#39;
290 276 import { CommonStatusEnum } from '@/utils/constants'
291 277 import { getAccessToken, getTenantId } from '@/utils/auth'
292 278 import { useMessage } from '@/hooks/web/useMessage'
  279 +import { VxeGridInstance } from 'vxe-table'
  280 +import { useVxeGrid } from '@/hooks/web/useVxeGrid'
293 281  
294   -const message = useMessage()
  282 +const { t } = useI18n() // 国际化
  283 +const message = useMessage() // 消息弹窗
295 284  
296 285 const defaultProps = {
297 286 children: 'children',
298 287 label: 'name',
299 288 value: 'id'
300 289 }
301   -const { t } = useI18n() // 国际化
302 290  
303 291 // ========== 列表相关 ==========
304 292 const tableTitle = ref('用户列表')
305   -const { register, tableObject, methods } = useTable<UserVO>({
  293 +// 列表相关的变量
  294 +const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref
  295 +const { gridOptions, reloadList, delList, exportList, getSearchData } = useVxeGrid<UserApi.UserVO>({
  296 + allSchemas: allSchemas,
306 297 getListApi: UserApi.getUserPageApi,
307 298 delListApi: UserApi.deleteUserApi,
308 299 exportListApi: UserApi.exportUserApi
309 300 })
310   -const { getList, setSearchParams, delList, exportList } = methods
311   -
312 301 // ========== 创建部门树结构 ==========
313 302 const filterText = ref('')
314 303 const deptOptions = ref<any[]>([]) // 树形结构
315   -const searchForm = ref<FormExpose>()
316 304 const treeRef = ref<InstanceType<typeof ElTree>>()
317 305 const getTree = async () => {
318 306 const res = await listSimpleDeptApi()
... ... @@ -322,12 +310,14 @@ const filterNode = (value: string, data: Tree) =&gt; {
322 310 if (!value) return true
323 311 return data.name.includes(value)
324 312 }
325   -const handleDeptNodeClick = (data: { [key: string]: any }) => {
326   - tableObject.params = {
327   - deptId: data.id
328   - }
329   - tableTitle.value = data.name
330   - methods.getList()
  313 +const handleDeptNodeClick = async (row: { [key: string]: any }) => {
  314 + tableTitle.value = row.name
  315 + console.log(getSearchData(xGrid))
  316 + // gridOptions.formConfig?.data.push({
  317 + // deptId: row.id
  318 + // })
  319 + // TODO 查询
  320 + await reloadList(xGrid)
331 321 }
332 322 const { push } = useRouter()
333 323 const handleDeptEdit = () => {
... ... @@ -366,8 +356,6 @@ const handleCreate = async () =&gt; {
366 356 dialogVisible.value = true
367 357 dialogTitle.value = t('action.create')
368 358 actionType.value = 'create'
369   - await nextTick()
370   - unref(formRef)?.getElFormRef().resetFields()
371 359 }
372 360  
373 361 // 修改操作
... ... @@ -381,13 +369,24 @@ const handleUpdate = async (rowId: number) =&gt; {
381 369 postIds.value = res.postIds
382 370 unref(formRef)?.setValues(res)
383 371 }
  372 +const detailData = ref()
384 373  
  374 +// 详情操作
  375 +const handleDetail = async (row: UserApi.UserVO) => {
  376 + // 设置数据
  377 + detailData.value = row
  378 + await setDialogTile('detail')
  379 +}
  380 +// 删除操作
  381 +const handleDelete = async (rowId: number) => {
  382 + await delList(xGrid, rowId)
  383 +}
385 384 // 提交按钮
386 385 const submitForm = async () => {
387 386 loading.value = true
388 387 // 提交请求
389 388 try {
390   - const data = unref(formRef)?.formModel as UserVO
  389 + const data = unref(formRef)?.formModel as UserApi.UserVO
391 390 data.deptId = deptId.value
392 391 data.postIds = postIds.value
393 392 if (actionType.value === 'create') {
... ... @@ -397,15 +396,15 @@ const submitForm = async () =&gt; {
397 396 await UserApi.updateUserApi(data)
398 397 message.success(t('common.updateSuccess'))
399 398 }
400   - // 操作成功,重新加载列表
401 399 dialogVisible.value = false
402   - await getList()
403 400 } finally {
404 401 loading.value = false
  402 + // 刷新列表
  403 + reloadList(xGrid)
405 404 }
406 405 }
407 406 // 改变用户状态操作
408   -const handleStatusChange = async (row: UserVO) => {
  407 +const handleStatusChange = async (row: UserApi.UserVO) => {
409 408 const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
410 409 message
411 410 .confirm('确认要"' + text + '""' + row.username + '"用户吗?', t('common.reminder'))
... ... @@ -414,7 +413,8 @@ const handleStatusChange = async (row: UserVO) =&gt; {
414 413 row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.ENABLE : CommonStatusEnum.DISABLE
415 414 await UserApi.updateUserStatusApi(row.id, row.status)
416 415 message.success(text + '成功')
417   - await getList()
  416 + // 刷新列表
  417 + reloadList(xGrid)
418 418 })
419 419 .catch(() => {
420 420 row.status =
... ... @@ -422,7 +422,7 @@ const handleStatusChange = async (row: UserVO) =&gt; {
422 422 })
423 423 }
424 424 // 重置密码
425   -const handleResetPwd = (row: UserVO) => {
  425 +const handleResetPwd = (row: UserApi.UserVO) => {
426 426 message.prompt('请输入"' + row.username + '"的新密码', t('common.reminder')).then(({ value }) => {
427 427 UserApi.resetUserPwdApi(row.id, value).then(() => {
428 428 message.success('修改成功,新密码是:' + value)
... ... @@ -438,7 +438,7 @@ const userRole = reactive({
438 438 nickname: '',
439 439 roleIds: []
440 440 })
441   -const handleRole = async (row: UserVO) => {
  441 +const handleRole = async (row: UserApi.UserVO) => {
442 442 userRole.id = row.id
443 443 userRole.username = row.username
444 444 userRole.nickname = row.nickname
... ... @@ -460,15 +460,6 @@ const submitRole = async () =&gt; {
460 460 message.success(t('common.updateSuccess'))
461 461 roleDialogVisible.value = false
462 462 }
463   -// ========== 详情相关 ==========
464   -const detailRef = ref()
465   -
466   -// 详情操作
467   -const handleDetail = async (row: UserVO) => {
468   - // 设置数据
469   - detailRef.value = row
470   - await setDialogTile('detail')
471   -}
472 463 // ========== 导入相关 ==========
473 464 const importDialogVisible = ref(false)
474 465 const uploadDisabled = ref(false)
... ... @@ -523,7 +514,7 @@ const handleFileSuccess = (response: any): void =&gt; {
523 514 text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
524 515 }
525 516 message.alert(text)
526   - getList()
  517 + reloadList(xGrid)
527 518 }
528 519 // 文件数超出提示
529 520 const handleExceed = (): void => {
... ... @@ -536,7 +527,6 @@ const excelUploadError = (): void =&gt; {
536 527 // ========== 初始化 ==========
537 528 onMounted(async () => {
538 529 await getPostOptions()
539   - await getList()
540 530 await getTree()
541 531 })
542 532 </script>
... ...
yudao-ui-admin-vue3/src/views/system/user/user.data.ts
... ... @@ -2,7 +2,7 @@ import { reactive } from &#39;vue&#39;
2 2 import { required } from '@/utils/formRules'
3 3 import { useI18n } from '@/hooks/web/useI18n'
4 4 import { DICT_TYPE } from '@/utils/dict'
5   -import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  5 +import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
6 6 // 国际化
7 7 const { t } = useI18n()
8 8 // 表单校验
... ... @@ -21,157 +21,86 @@ export const rules = reactive({
21 21 ]
22 22 })
23 23 // crudSchemas
24   -const crudSchemas = reactive<CrudSchema[]>([
25   - {
26   - label: t('common.index'),
27   - field: 'id',
28   - type: 'index',
29   - form: {
30   - show: false
31   - },
32   - detail: {
33   - show: false
34   - }
35   - },
36   - {
37   - label: '用户账号',
38   - field: 'username',
39   - search: {
40   - show: true
41   - }
42   - },
43   - {
44   - label: '用户密码',
45   - field: 'password',
46   - form: {
47   - component: 'InputPassword'
  24 +const crudSchemas = reactive<VxeCrudSchema>({
  25 + primaryKey: 'id',
  26 + primaryType: 'seq',
  27 + action: true,
  28 + actionWidth: '400px',
  29 + columns: [
  30 + {
  31 + title: '用户账号',
  32 + field: 'username',
  33 + isSearch: true
48 34 },
49   - detail: {
50   - show: false
51   - }
52   - },
53   - {
54   - label: '用户昵称',
55   - field: 'nickname'
56   - },
57   - {
58   - label: '用户邮箱',
59   - field: 'email'
60   - },
61   - {
62   - label: '手机号码',
63   - field: 'mobile',
64   - search: {
65   - show: true
66   - }
67   - },
68   - {
69   - label: '部门',
70   - field: 'deptId',
71   - table: {
72   - show: false
73   - }
74   - },
75   - {
76   - label: '岗位',
77   - field: 'postIds',
78   - table: {
79   - show: false
80   - }
81   - },
82   - {
83   - label: t('common.status'),
84   - field: 'status',
85   - dictType: DICT_TYPE.COMMON_STATUS,
86   - search: {
87   - show: true
88   - }
89   - },
90   - {
91   - label: '最后登录时间',
92   - field: 'loginDate',
93   - form: {
94   - show: false
95   - }
96   - },
97   - {
98   - label: '最后登录IP',
99   - field: 'loginIp',
100   - table: {
101   - show: false
  35 + {
  36 + title: '用户密码',
  37 + field: 'password',
  38 + form: {
  39 + component: 'InputPassword'
  40 + },
  41 + isDetail: false
102 42 },
103   - form: {
104   - show: false
105   - }
106   - },
107   - {
108   - label: t('form.remark'),
109   - field: 'remark',
110   - table: {
111   - show: false
112   - }
113   - },
114   - {
115   - label: t('common.createTime'),
116   - field: 'createTime',
117   - table: {
118   - show: false
  43 + {
  44 + title: '用户昵称',
  45 + field: 'nickname'
119 46 },
120   - form: {
121   - show: false
  47 + {
  48 + title: '用户邮箱',
  49 + field: 'email'
122 50 },
123   - detail: {
124   - show: false
  51 + {
  52 + title: '手机号码',
  53 + field: 'mobile',
  54 + isSearch: true
125 55 },
126   - search: {
127   - show: true,
128   - component: 'DatePicker',
129   - componentProps: {
130   - type: 'datetimerange',
131   - valueFormat: 'YYYY-MM-DD HH:mm:ss',
132   - defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)],
133   - shortcuts: [
134   - {
135   - text: '近一周',
136   - value: () => {
137   - const end = new Date()
138   - const start = new Date()
139   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
140   - return [start, end]
141   - }
142   - },
143   - {
144   - text: '近一个月',
145   - value: () => {
146   - const end = new Date()
147   - const start = new Date()
148   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
149   - return [start, end]
150   - }
151   - },
152   - {
153   - text: '近三个月',
154   - value: () => {
155   - const end = new Date()
156   - const start = new Date()
157   - start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
158   - return [start, end]
159   - }
160   - }
161   - ]
  56 + {
  57 + title: '部门',
  58 + field: 'deptId',
  59 + isTable: false,
  60 + search: {
  61 + visible: false
162 62 }
163   - }
164   - },
165   - {
166   - field: 'action',
167   - width: '400px',
168   - label: t('table.action'),
169   - form: {
170   - show: false
171 63 },
172   - detail: {
173   - show: false
  64 + {
  65 + title: '岗位',
  66 + field: 'postIds',
  67 + isTable: false
  68 + },
  69 + {
  70 + title: t('common.status'),
  71 + field: 'status',
  72 + dictType: DICT_TYPE.COMMON_STATUS,
  73 + isSearch: true
  74 + },
  75 + {
  76 + title: '最后登录时间',
  77 + field: 'loginDate',
  78 + isForm: false
  79 + },
  80 + {
  81 + title: '最后登录IP',
  82 + field: 'loginIp',
  83 + isTable: false,
  84 + isForm: false
  85 + },
  86 + {
  87 + title: t('form.remark'),
  88 + field: 'remark',
  89 + isTable: false
  90 + },
  91 + {
  92 + title: t('common.createTime'),
  93 + field: 'createTime',
  94 + formatter: 'formatDate',
  95 + isTable: false,
  96 + isForm: false,
  97 + search: {
  98 + show: true,
  99 + itemRender: {
  100 + name: 'XDataTimePicker'
  101 + }
  102 + }
174 103 }
175   - }
176   -])
177   -export const { allSchemas } = useCrudSchemas(crudSchemas)
  104 + ]
  105 +})
  106 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...