Commit d3526e6f6307d853a5402ed8e2d3fdd7e5c1ce44

Authored by xingyu4j
1 parent fabca00f

refactor: template script

yudao-ui-admin-vue3/src/views/system/dept/index.vue
  1 +<template>
  2 + <div class="flex">
  3 + <el-card class="w-1/3 dept" :gutter="12" shadow="always">
  4 + <template #header>
  5 + <div class="card-header">
  6 + <span>部门列表</span>
  7 + <XButton
  8 + type="primary"
  9 + preIcon="ep:zoom-in"
  10 + title="新增根节点"
  11 + v-hasPermi="['system:dept:create']"
  12 + @click="handleCreate"
  13 + />
  14 + </div>
  15 + </template>
  16 + <div class="custom-tree-container">
  17 + <!-- <p>部门列表</p> -->
  18 + <!-- 操作工具栏 -->
  19 + <el-input v-model="filterText" placeholder="搜索部门" />
  20 + <el-tree
  21 + ref="treeRef"
  22 + node-key="id"
  23 + :data="deptOptions"
  24 + :props="defaultProps"
  25 + :highlight-current="true"
  26 + default-expand-all
  27 + :filter-node-method="filterNode"
  28 + :expand-on-click-node="false"
  29 + >
  30 + <template #default="{ node, data }">
  31 + <span class="custom-tree-node">
  32 + <span>{{ node.label }}</span>
  33 + <span>
  34 + <XTextButton
  35 + preIcon="ep:zoom-in"
  36 + :title="t('action.add')"
  37 + v-hasPermi="['system:dept:create']"
  38 + @click="handleCreate(data)"
  39 + />
  40 + <XTextButton
  41 + preIcon="ep:edit"
  42 + :title="t('action.edit')"
  43 + v-hasPermi="['system:dept:update']"
  44 + @click="handleUpdate(data)"
  45 + />
  46 + <XTextButton
  47 + preIcon="ep:delete"
  48 + :title="t('action.del')"
  49 + v-hasPermi="['system:dept:delete']"
  50 + @click="handleDelete(data)"
  51 + />
  52 + </span>
  53 + </span>
  54 + </template>
  55 + </el-tree>
  56 + </div>
  57 + </el-card>
  58 + <el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
  59 + <template #header>
  60 + <div class="card-header">
  61 + <span>{{ formTitle }}</span>
  62 + </div>
  63 + </template>
  64 + <div v-if="!showForm">
  65 + <span><p>请从左侧选择部门</p></span>
  66 + </div>
  67 + <div v-if="showForm">
  68 + <!-- 操作工具栏 -->
  69 + <Form ref="formRef" :schema="modelSchema" :rules="rules">
  70 + <template #parentId>
  71 + <el-tree-select
  72 + node-key="id"
  73 + v-model="deptParentId"
  74 + :props="defaultProps"
  75 + :data="deptOptions"
  76 + check-strictly
  77 + />
  78 + </template>
  79 + <template #leaderUserId>
  80 + <el-select v-model="leaderUserId">
  81 + <el-option
  82 + v-for="item in userOption"
  83 + :key="parseInt(item.id)"
  84 + :label="item.nickname"
  85 + :value="parseInt(item.id)"
  86 + />
  87 + </el-select>
  88 + </template>
  89 + </Form>
  90 + <!-- 按钮:保存 -->
  91 + <XButton
  92 + type="primary"
  93 + :title="t('action.save')"
  94 + v-hasPermi="['system:dept:update']"
  95 + :loading="loading"
  96 + @click="submitForm()"
  97 + />
  98 + <!-- 按钮:关闭 -->
  99 + <XButton :loading="loading" :title="t('dialog.close')" @click="showForm = false" />
  100 + </div>
  101 + </el-card>
  102 + </div>
  103 +</template>
1 104 <script setup lang="ts">
2 105 import { useI18n } from '@/hooks/web/useI18n'
3 106 import { ElInput, ElCard, ElTree, ElTreeSelect, ElSelect, ElOption } from 'element-plus'
... ... @@ -104,109 +207,7 @@ onMounted(async () =&gt; {
104 207 await getUserList()
105 208 })
106 209 </script>
107   -<template>
108   - <div class="flex">
109   - <el-card class="w-1/3 dept" :gutter="12" shadow="always">
110   - <template #header>
111   - <div class="card-header">
112   - <span>部门列表</span>
113   - <XButton
114   - type="primary"
115   - preIcon="ep:zoom-in"
116   - title="新增根节点"
117   - v-hasPermi="['system:dept:create']"
118   - @click="handleCreate"
119   - />
120   - </div>
121   - </template>
122   - <div class="custom-tree-container">
123   - <!-- <p>部门列表</p> -->
124   - <!-- 操作工具栏 -->
125   - <el-input v-model="filterText" placeholder="搜索部门" />
126   - <el-tree
127   - ref="treeRef"
128   - node-key="id"
129   - :data="deptOptions"
130   - :props="defaultProps"
131   - :highlight-current="true"
132   - default-expand-all
133   - :filter-node-method="filterNode"
134   - :expand-on-click-node="false"
135   - >
136   - <template #default="{ node, data }">
137   - <span class="custom-tree-node">
138   - <span>{{ node.label }}</span>
139   - <span>
140   - <XTextButton
141   - preIcon="ep:zoom-in"
142   - :title="t('action.add')"
143   - v-hasPermi="['system:dept:create']"
144   - @click="handleCreate(data)"
145   - />
146   - <XTextButton
147   - preIcon="ep:edit"
148   - :title="t('action.edit')"
149   - v-hasPermi="['system:dept:update']"
150   - @click="handleUpdate(data)"
151   - />
152   - <XTextButton
153   - preIcon="ep:delete"
154   - :title="t('action.del')"
155   - v-hasPermi="['system:dept:delete']"
156   - @click="handleDelete(data)"
157   - />
158   - </span>
159   - </span>
160   - </template>
161   - </el-tree>
162   - </div>
163   - </el-card>
164   - <el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
165   - <template #header>
166   - <div class="card-header">
167   - <span>{{ formTitle }}</span>
168   - </div>
169   - </template>
170   - <div v-if="!showForm">
171   - <span><p>请从左侧选择部门</p></span>
172   - </div>
173   - <div v-if="showForm">
174   - <!-- 操作工具栏 -->
175   - <Form ref="formRef" :schema="modelSchema" :rules="rules">
176   - <template #parentId>
177   - <el-tree-select
178   - node-key="id"
179   - v-model="deptParentId"
180   - :props="defaultProps"
181   - :data="deptOptions"
182   - check-strictly
183   - />
184   - </template>
185   - <template #leaderUserId>
186   - <el-select v-model="leaderUserId">
187   - <el-option
188   - v-for="item in userOption"
189   - :key="parseInt(item.id)"
190   - :label="item.nickname"
191   - :value="parseInt(item.id)"
192   - />
193   - </el-select>
194   - </template>
195   - </Form>
196   - <!-- 按钮:保存 -->
197   - <XButton
198   - type="primary"
199   - :title="t('action.save')"
200   - v-hasPermi="['system:dept:update']"
201   - :loading="loading"
202   - @click="submitForm()"
203   - />
204   - <!-- 按钮:关闭 -->
205   - <XButton :loading="loading" :title="t('dialog.close')" @click="showForm = false" />
206   - </div>
207   - </el-card>
208   - </div>
209   -</template>
  210 +
210 211 <style scoped>
211 212 .dept {
212 213 height: 600px;
... ...
yudao-ui-admin-vue3/src/views/system/tenantPackage/index.vue
  1 +<template>
  2 + <!-- 搜索工作区 -->
  3 + <ContentWrap>
  4 + <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  5 + </ContentWrap>
  6 + <ContentWrap>
  7 + <!-- 操作工具栏 -->
  8 + <div class="mb-10px">
  9 + <el-button type="primary" @click="handleCreate">
  10 + <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  11 + </el-button>
  12 + </div>
  13 + <!-- 列表 -->
  14 + <Table
  15 + :columns="allSchemas.tableColumns"
  16 + :selection="false"
  17 + :data="tableObject.tableList"
  18 + :loading="tableObject.loading"
  19 + :pagination="{
  20 + total: tableObject.total
  21 + }"
  22 + v-model:pageSize="tableObject.pageSize"
  23 + v-model:currentPage="tableObject.currentPage"
  24 + @register="register"
  25 + >
  26 + <template #status="{ row }">
  27 + <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
  28 + </template>
  29 + <template #createTime="{ row }">
  30 + <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
  31 + </template>
  32 + <template #action="{ row }">
  33 + <el-button link type="primary" @click="handleUpdate(row)">
  34 + <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  35 + </el-button>
  36 + <el-button link type="primary" @click="delList(row.id, false)">
  37 + <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  38 + </el-button>
  39 + </template>
  40 + </Table>
  41 + </ContentWrap>
  42 +
  43 + <XModal v-model="dialogVisible" :title="dialogTitle" maxHeight="500px" width="50%">
  44 + <!-- 对话框(添加 / 修改) -->
  45 + <Form
  46 + v-if="['create', 'update'].includes(actionType)"
  47 + :schema="allSchemas.formSchema"
  48 + :rules="rules"
  49 + ref="formRef"
  50 + >
  51 + <template #menuIds>
  52 + <el-card class="box-card">
  53 + <template #header>
  54 + <div class="card-header">
  55 + 全选/全不选:
  56 + <el-switch
  57 + v-model="treeNodeAll"
  58 + inline-prompt
  59 + active-text="是"
  60 + inactive-text="否"
  61 + @change="handleCheckedTreeNodeAll()"
  62 + />
  63 + </div>
  64 + </template>
  65 + <el-tree
  66 + ref="treeRef"
  67 + node-key="id"
  68 + show-checkbox
  69 + :props="defaultProps"
  70 + :data="menuOptions"
  71 + empty-text="加载中,请稍后"
  72 + />
  73 + </el-card>
  74 + </template>
  75 + </Form>
  76 + <!-- 操作按钮 -->
  77 + <template #footer>
  78 + <!-- 按钮:保存 -->
  79 + <XButton
  80 + v-if="['create', 'update'].includes(actionType)"
  81 + type="primary"
  82 + :title="t('action.save')"
  83 + :loading="loading"
  84 + @click="submitForm()"
  85 + />
  86 + <!-- 按钮:关闭 -->
  87 + <XButton :loading="loading" :title="t('dialog.close')" @click="dialogVisible = false" />
  88 + </template>
  89 + </XModal>
  90 +</template>
1 91 <script setup lang="ts">
2 92 import { onMounted, ref, unref } from 'vue'
3 93 import dayjs from 'dayjs'
... ... @@ -107,94 +197,3 @@ onMounted(async () =&gt; {
107 197 })
108 198 // getList()
109 199 </script>
110   -
111   -<template>
112   - <!-- 搜索工作区 -->
113   - <ContentWrap>
114   - <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
115   - </ContentWrap>
116   - <ContentWrap>
117   - <!-- 操作工具栏 -->
118   - <div class="mb-10px">
119   - <el-button type="primary" @click="handleCreate">
120   - <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
121   - </el-button>
122   - </div>
123   - <!-- 列表 -->
124   - <Table
125   - :columns="allSchemas.tableColumns"
126   - :selection="false"
127   - :data="tableObject.tableList"
128   - :loading="tableObject.loading"
129   - :pagination="{
130   - total: tableObject.total
131   - }"
132   - v-model:pageSize="tableObject.pageSize"
133   - v-model:currentPage="tableObject.currentPage"
134   - @register="register"
135   - >
136   - <template #status="{ row }">
137   - <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
138   - </template>
139   - <template #createTime="{ row }">
140   - <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
141   - </template>
142   - <template #action="{ row }">
143   - <el-button link type="primary" @click="handleUpdate(row)">
144   - <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
145   - </el-button>
146   - <el-button link type="primary" @click="delList(row.id, false)">
147   - <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
148   - </el-button>
149   - </template>
150   - </Table>
151   - </ContentWrap>
152   -
153   - <XModal v-model="dialogVisible" :title="dialogTitle" maxHeight="500px" width="50%">
154   - <!-- 对话框(添加 / 修改) -->
155   - <Form
156   - v-if="['create', 'update'].includes(actionType)"
157   - :schema="allSchemas.formSchema"
158   - :rules="rules"
159   - ref="formRef"
160   - >
161   - <template #menuIds>
162   - <el-card class="box-card">
163   - <template #header>
164   - <div class="card-header">
165   - 全选/全不选:
166   - <el-switch
167   - v-model="treeNodeAll"
168   - inline-prompt
169   - active-text="是"
170   - inactive-text="否"
171   - @change="handleCheckedTreeNodeAll()"
172   - />
173   - </div>
174   - </template>
175   - <el-tree
176   - ref="treeRef"
177   - node-key="id"
178   - show-checkbox
179   - :props="defaultProps"
180   - :data="menuOptions"
181   - empty-text="加载中,请稍后"
182   - />
183   - </el-card>
184   - </template>
185   - </Form>
186   - <!-- 操作按钮 -->
187   - <template #footer>
188   - <!-- 按钮:保存 -->
189   - <XButton
190   - v-if="['create', 'update'].includes(actionType)"
191   - type="primary"
192   - :title="t('action.save')"
193   - :loading="loading"
194   - @click="submitForm()"
195   - />
196   - <!-- 按钮:关闭 -->
197   - <XButton :loading="loading" :title="t('dialog.close')" @click="dialogVisible = false" />
198   - </template>
199   - </XModal>
200   -</template>
... ...
yudao-ui-admin-vue3/src/views/system/user/index.vue
1   -<script setup lang="ts">
2   -import { onMounted, reactive, ref, unref, watch } from 'vue'
3   -import dayjs from 'dayjs'
4   -import {
5   - ElTag,
6   - ElInput,
7   - ElCard,
8   - ElTree,
9   - ElTreeSelect,
10   - ElSelect,
11   - ElOption,
12   - ElTransfer,
13   - ElForm,
14   - ElFormItem,
15   - ElUpload,
16   - ElSwitch,
17   - ElCheckbox,
18   - UploadInstance,
19   - UploadRawFile
20   -} from 'element-plus'
21   -import { handleTree } from '@/utils/tree'
22   -import { useI18n } from '@/hooks/web/useI18n'
23   -import { useTable } from '@/hooks/web/useTable'
24   -import { FormExpose } from '@/components/Form'
25   -import type { UserVO } from '@/api/system/user/types'
26   -import type { PostVO } from '@/api/system/post'
27   -import type { PermissionAssignUserRoleReqVO } from '@/api/system/permission/types'
28   -import { listSimpleDeptApi } from '@/api/system/dept'
29   -import { listSimplePostsApi } from '@/api/system/post'
30   -import { listSimpleRolesApi } from '@/api/system/role'
31   -import { aassignUserRoleApi, listUserRolesApi } from '@/api/system/permission'
32   -import { rules, allSchemas } from './user.data'
33   -import * as UserApi from '@/api/system/user'
34   -import download from '@/utils/download'
35   -import { useRouter } from 'vue-router'
36   -import { CommonStatusEnum } from '@/utils/constants'
37   -import { getAccessToken, getTenantId } from '@/utils/auth'
38   -import { useMessage } from '@/hooks/web/useMessage'
39   -
40   -const message = useMessage()
41   -
42   -const defaultProps = {
43   - children: 'children',
44   - label: 'name',
45   - value: 'id'
46   -}
47   -const { t } = useI18n() // 国际化
48   -
49   -// ========== 列表相关 ==========
50   -const tableTitle = ref('用户列表')
51   -const { register, tableObject, methods } = useTable<UserVO>({
52   - getListApi: UserApi.getUserPageApi,
53   - delListApi: UserApi.deleteUserApi,
54   - exportListApi: UserApi.exportUserApi
55   -})
56   -const { getList, setSearchParams, delList, exportList } = methods
57   -
58   -// ========== 创建部门树结构 ==========
59   -const filterText = ref('')
60   -const deptOptions = ref<any[]>([]) // 树形结构
61   -const searchForm = ref<FormExpose>()
62   -const treeRef = ref<InstanceType<typeof ElTree>>()
63   -const getTree = async () => {
64   - const res = await listSimpleDeptApi()
65   - deptOptions.value.push(...handleTree(res))
66   -}
67   -const filterNode = (value: string, data: Tree) => {
68   - if (!value) return true
69   - return data.name.includes(value)
70   -}
71   -const handleDeptNodeClick = (data: { [key: string]: any }) => {
72   - tableObject.params = {
73   - deptId: data.id
74   - }
75   - tableTitle.value = data.name
76   - methods.getList()
77   -}
78   -const { push } = useRouter()
79   -const handleDeptEdit = () => {
80   - push('/system/dept')
81   -}
82   -watch(filterText, (val) => {
83   - treeRef.value!.filter(val)
84   -})
85   -// ========== CRUD 相关 ==========
86   -const loading = ref(false) // 遮罩层
87   -const actionType = ref('') // 操作按钮的类型
88   -const dialogVisible = ref(false) // 是否显示弹出层
89   -const dialogTitle = ref('edit') // 弹出层标题
90   -const formRef = ref<FormExpose>() // 表单 Ref
91   -const deptId = ref() // 部门ID
92   -const postIds = ref<string[]>([]) // 岗位ID
93   -const postOptions = ref<PostVO[]>([]) //岗位列表
94   -
95   -// 获取岗位列表
96   -const getPostOptions = async () => {
97   - const res = await listSimplePostsApi()
98   - postOptions.value.push(...res)
99   -}
100   -// 设置标题
101   -const setDialogTile = async (type: string) => {
102   - dialogTitle.value = t('action.' + type)
103   - actionType.value = type
104   - dialogVisible.value = true
105   -}
106   -
107   -// 新增操作
108   -const handleAdd = async () => {
109   - // 重置表单
110   - deptId.value = null
111   - postIds.value = []
112   - dialogVisible.value = true
113   - dialogTitle.value = t('action.create')
114   - actionType.value = 'create'
115   - await unref(formRef)?.getElFormRef().resetFields()
116   -}
117   -
118   -// 修改操作
119   -const handleUpdate = async (row: UserVO) => {
120   - await setDialogTile('update')
121   - unref(formRef)?.delSchema('username')
122   - unref(formRef)?.delSchema('password')
123   - // 设置数据
124   - const res = await UserApi.getUserApi(row.id)
125   - deptId.value = res.deptId
126   - postIds.value = res.postIds
127   - unref(formRef)?.setValues(res)
128   -}
129   -
130   -// 提交按钮
131   -const submitForm = async () => {
132   - loading.value = true
133   - // 提交请求
134   - try {
135   - const data = unref(formRef)?.formModel as UserVO
136   - data.deptId = deptId.value
137   - data.postIds = postIds.value
138   - if (actionType.value === 'create') {
139   - await UserApi.createUserApi(data)
140   - message.success(t('common.createSuccess'))
141   - } else {
142   - await UserApi.updateUserApi(data)
143   - message.success(t('common.updateSuccess'))
144   - }
145   - // 操作成功,重新加载列表
146   - dialogVisible.value = false
147   - await getList()
148   - } finally {
149   - loading.value = false
150   - }
151   -}
152   -// 改变用户状态操作
153   -const handleStatusChange = async (row: UserVO) => {
154   - const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
155   - message
156   - .confirm('确认要"' + text + '""' + row.username + '"用户吗?', t('common.reminder'))
157   - .then(async () => {
158   - row.status =
159   - row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.ENABLE : CommonStatusEnum.DISABLE
160   - await UserApi.updateUserStatusApi(row.id, row.status)
161   - message.success(text + '成功')
162   - await getList()
163   - })
164   - .catch(() => {
165   - row.status =
166   - row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
167   - })
168   -}
169   -// 重置密码
170   -const handleResetPwd = (row: UserVO) => {
171   - message.prompt('请输入"' + row.username + '"的新密码', t('common.reminder')).then(({ value }) => {
172   - UserApi.resetUserPwdApi(row.id, value).then(() => {
173   - message.success('修改成功,新密码是:' + value)
174   - })
175   - })
176   -}
177   -// 分配角色
178   -const roleDialogVisible = ref(false)
179   -const roleOptions = ref()
180   -const userRole = reactive({
181   - id: 0,
182   - username: '',
183   - nickname: '',
184   - roleIds: []
185   -})
186   -const handleRole = async (row: UserVO) => {
187   - userRole.id = row.id
188   - userRole.username = row.username
189   - userRole.nickname = row.nickname
190   - // 获得角色拥有的权限集合
191   - const roles = await listUserRolesApi(row.id)
192   - userRole.roleIds = roles
193   - // 获取角色列表
194   - const roleOpt = await listSimpleRolesApi()
195   - roleOptions.value = roleOpt
196   - roleDialogVisible.value = true
197   -}
198   -// 提交
199   -const submitRole = async () => {
200   - const data = ref<PermissionAssignUserRoleReqVO>({
201   - userId: userRole.id,
202   - roleIds: userRole.roleIds
203   - })
204   - await aassignUserRoleApi(data.value)
205   - message.success(t('common.updateSuccess'))
206   - roleDialogVisible.value = false
207   -}
208   -// ========== 详情相关 ==========
209   -const detailRef = ref()
210   -
211   -// 详情操作
212   -const handleDetail = async (row: UserVO) => {
213   - // 设置数据
214   - detailRef.value = row
215   - await setDialogTile('detail')
216   -}
217   -// ========== 导入相关 ==========
218   -const importDialogVisible = ref(false)
219   -const uploadDisabled = ref(false)
220   -const importDialogTitle = ref('用户导入')
221   -const updateSupport = ref(0)
222   -let updateUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/import'
223   -const uploadHeaders = ref()
224   -// 下载导入模版
225   -const handleImportTemp = async () => {
226   - const res = await UserApi.importUserTemplateApi()
227   - download.excel(res, '用户导入模版.xls')
228   -}
229   -// 文件上传之前判断
230   -const beforeExcelUpload = (file: UploadRawFile) => {
231   - const isExcel =
232   - file.type === 'application/vnd.ms-excel' ||
233   - file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
234   - const isLt5M = file.size / 1024 / 1024 < 5
235   - if (!isExcel) message.error('上传文件只能是 xls / xlsx 格式!')
236   - if (!isLt5M) message.error('上传文件大小不能超过 5MB!')
237   - return isExcel && isLt5M
238   -}
239   -// 文件上传
240   -const uploadRef = ref<UploadInstance>()
241   -const submitFileForm = () => {
242   - uploadHeaders.value = {
243   - Authorization: 'Bearer ' + getAccessToken(),
244   - 'tenant-id': getTenantId()
245   - }
246   - uploadDisabled.value = true
247   - uploadRef.value!.submit()
248   -}
249   -// 文件上传成功
250   -const handleFileSuccess = (response: any): void => {
251   - if (response.code !== 0) {
252   - message.error(response.msg)
253   - return
254   - }
255   - importDialogVisible.value = false
256   - uploadDisabled.value = false
257   - const data = response.data
258   - let text = '上传成功数量:' + data.createUsernames.length + ';'
259   - for (let username of data.createUsernames) {
260   - text += '< ' + username + ' >'
261   - }
262   - text += '更新成功数量:' + data.updateUsernames.length + ';'
263   - for (const username of data.updateUsernames) {
264   - text += '< ' + username + ' >'
265   - }
266   - text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
267   - for (const username in data.failureUsernames) {
268   - text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
269   - }
270   - message.alert(text)
271   - getList()
272   -}
273   -// 文件数超出提示
274   -const handleExceed = (): void => {
275   - message.error('最多只能上传一个文件!')
276   -}
277   -// 上传错误提示
278   -const excelUploadError = (): void => {
279   - message.error('导入数据失败,请您重新上传!')
280   -}
281   -// ========== 初始化 ==========
282   -onMounted(async () => {
283   - await getPostOptions()
284   - await getList()
285   - await getTree()
286   -})
287   -</script>
288   -
289 1 <template>
290 2 <div class="flex">
291 3 <el-card class="w-1/5 user" :gutter="12" shadow="always">
... ... @@ -546,6 +258,293 @@ onMounted(async () =&gt; {
546 258 </template>
547 259 </XModal>
548 260 </template>
  261 +<script setup lang="ts">
  262 +import { onMounted, reactive, ref, unref, watch } from 'vue'
  263 +import dayjs from 'dayjs'
  264 +import {
  265 + ElTag,
  266 + ElInput,
  267 + ElCard,
  268 + ElTree,
  269 + ElTreeSelect,
  270 + ElSelect,
  271 + ElOption,
  272 + ElTransfer,
  273 + ElForm,
  274 + ElFormItem,
  275 + ElUpload,
  276 + ElSwitch,
  277 + ElCheckbox,
  278 + UploadInstance,
  279 + UploadRawFile
  280 +} from 'element-plus'
  281 +import { handleTree } from '@/utils/tree'
  282 +import { useI18n } from '@/hooks/web/useI18n'
  283 +import { useTable } from '@/hooks/web/useTable'
  284 +import { FormExpose } from '@/components/Form'
  285 +import type { UserVO } from '@/api/system/user/types'
  286 +import type { PostVO } from '@/api/system/post'
  287 +import type { PermissionAssignUserRoleReqVO } from '@/api/system/permission/types'
  288 +import { listSimpleDeptApi } from '@/api/system/dept'
  289 +import { listSimplePostsApi } from '@/api/system/post'
  290 +import { listSimpleRolesApi } from '@/api/system/role'
  291 +import { aassignUserRoleApi, listUserRolesApi } from '@/api/system/permission'
  292 +import { rules, allSchemas } from './user.data'
  293 +import * as UserApi from '@/api/system/user'
  294 +import download from '@/utils/download'
  295 +import { useRouter } from 'vue-router'
  296 +import { CommonStatusEnum } from '@/utils/constants'
  297 +import { getAccessToken, getTenantId } from '@/utils/auth'
  298 +import { useMessage } from '@/hooks/web/useMessage'
  299 +
  300 +const message = useMessage()
  301 +
  302 +const defaultProps = {
  303 + children: 'children',
  304 + label: 'name',
  305 + value: 'id'
  306 +}
  307 +const { t } = useI18n() // 国际化
  308 +
  309 +// ========== 列表相关 ==========
  310 +const tableTitle = ref('用户列表')
  311 +const { register, tableObject, methods } = useTable<UserVO>({
  312 + getListApi: UserApi.getUserPageApi,
  313 + delListApi: UserApi.deleteUserApi,
  314 + exportListApi: UserApi.exportUserApi
  315 +})
  316 +const { getList, setSearchParams, delList, exportList } = methods
  317 +
  318 +// ========== 创建部门树结构 ==========
  319 +const filterText = ref('')
  320 +const deptOptions = ref<any[]>([]) // 树形结构
  321 +const searchForm = ref<FormExpose>()
  322 +const treeRef = ref<InstanceType<typeof ElTree>>()
  323 +const getTree = async () => {
  324 + const res = await listSimpleDeptApi()
  325 + deptOptions.value.push(...handleTree(res))
  326 +}
  327 +const filterNode = (value: string, data: Tree) => {
  328 + if (!value) return true
  329 + return data.name.includes(value)
  330 +}
  331 +const handleDeptNodeClick = (data: { [key: string]: any }) => {
  332 + tableObject.params = {
  333 + deptId: data.id
  334 + }
  335 + tableTitle.value = data.name
  336 + methods.getList()
  337 +}
  338 +const { push } = useRouter()
  339 +const handleDeptEdit = () => {
  340 + push('/system/dept')
  341 +}
  342 +watch(filterText, (val) => {
  343 + treeRef.value!.filter(val)
  344 +})
  345 +// ========== CRUD 相关 ==========
  346 +const loading = ref(false) // 遮罩层
  347 +const actionType = ref('') // 操作按钮的类型
  348 +const dialogVisible = ref(false) // 是否显示弹出层
  349 +const dialogTitle = ref('edit') // 弹出层标题
  350 +const formRef = ref<FormExpose>() // 表单 Ref
  351 +const deptId = ref() // 部门ID
  352 +const postIds = ref<string[]>([]) // 岗位ID
  353 +const postOptions = ref<PostVO[]>([]) //岗位列表
  354 +
  355 +// 获取岗位列表
  356 +const getPostOptions = async () => {
  357 + const res = await listSimplePostsApi()
  358 + postOptions.value.push(...res)
  359 +}
  360 +// 设置标题
  361 +const setDialogTile = async (type: string) => {
  362 + dialogTitle.value = t('action.' + type)
  363 + actionType.value = type
  364 + dialogVisible.value = true
  365 +}
  366 +
  367 +// 新增操作
  368 +const handleAdd = async () => {
  369 + // 重置表单
  370 + deptId.value = null
  371 + postIds.value = []
  372 + dialogVisible.value = true
  373 + dialogTitle.value = t('action.create')
  374 + actionType.value = 'create'
  375 + await unref(formRef)?.getElFormRef().resetFields()
  376 +}
  377 +
  378 +// 修改操作
  379 +const handleUpdate = async (row: UserVO) => {
  380 + await setDialogTile('update')
  381 + unref(formRef)?.delSchema('username')
  382 + unref(formRef)?.delSchema('password')
  383 + // 设置数据
  384 + const res = await UserApi.getUserApi(row.id)
  385 + deptId.value = res.deptId
  386 + postIds.value = res.postIds
  387 + unref(formRef)?.setValues(res)
  388 +}
  389 +
  390 +// 提交按钮
  391 +const submitForm = async () => {
  392 + loading.value = true
  393 + // 提交请求
  394 + try {
  395 + const data = unref(formRef)?.formModel as UserVO
  396 + data.deptId = deptId.value
  397 + data.postIds = postIds.value
  398 + if (actionType.value === 'create') {
  399 + await UserApi.createUserApi(data)
  400 + message.success(t('common.createSuccess'))
  401 + } else {
  402 + await UserApi.updateUserApi(data)
  403 + message.success(t('common.updateSuccess'))
  404 + }
  405 + // 操作成功,重新加载列表
  406 + dialogVisible.value = false
  407 + await getList()
  408 + } finally {
  409 + loading.value = false
  410 + }
  411 +}
  412 +// 改变用户状态操作
  413 +const handleStatusChange = async (row: UserVO) => {
  414 + const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
  415 + message
  416 + .confirm('确认要"' + text + '""' + row.username + '"用户吗?', t('common.reminder'))
  417 + .then(async () => {
  418 + row.status =
  419 + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.ENABLE : CommonStatusEnum.DISABLE
  420 + await UserApi.updateUserStatusApi(row.id, row.status)
  421 + message.success(text + '成功')
  422 + await getList()
  423 + })
  424 + .catch(() => {
  425 + row.status =
  426 + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
  427 + })
  428 +}
  429 +// 重置密码
  430 +const handleResetPwd = (row: UserVO) => {
  431 + message.prompt('请输入"' + row.username + '"的新密码', t('common.reminder')).then(({ value }) => {
  432 + UserApi.resetUserPwdApi(row.id, value).then(() => {
  433 + message.success('修改成功,新密码是:' + value)
  434 + })
  435 + })
  436 +}
  437 +// 分配角色
  438 +const roleDialogVisible = ref(false)
  439 +const roleOptions = ref()
  440 +const userRole = reactive({
  441 + id: 0,
  442 + username: '',
  443 + nickname: '',
  444 + roleIds: []
  445 +})
  446 +const handleRole = async (row: UserVO) => {
  447 + userRole.id = row.id
  448 + userRole.username = row.username
  449 + userRole.nickname = row.nickname
  450 + // 获得角色拥有的权限集合
  451 + const roles = await listUserRolesApi(row.id)
  452 + userRole.roleIds = roles
  453 + // 获取角色列表
  454 + const roleOpt = await listSimpleRolesApi()
  455 + roleOptions.value = roleOpt
  456 + roleDialogVisible.value = true
  457 +}
  458 +// 提交
  459 +const submitRole = async () => {
  460 + const data = ref<PermissionAssignUserRoleReqVO>({
  461 + userId: userRole.id,
  462 + roleIds: userRole.roleIds
  463 + })
  464 + await aassignUserRoleApi(data.value)
  465 + message.success(t('common.updateSuccess'))
  466 + roleDialogVisible.value = false
  467 +}
  468 +// ========== 详情相关 ==========
  469 +const detailRef = ref()
  470 +
  471 +// 详情操作
  472 +const handleDetail = async (row: UserVO) => {
  473 + // 设置数据
  474 + detailRef.value = row
  475 + await setDialogTile('detail')
  476 +}
  477 +// ========== 导入相关 ==========
  478 +const importDialogVisible = ref(false)
  479 +const uploadDisabled = ref(false)
  480 +const importDialogTitle = ref('用户导入')
  481 +const updateSupport = ref(0)
  482 +let updateUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/import'
  483 +const uploadHeaders = ref()
  484 +// 下载导入模版
  485 +const handleImportTemp = async () => {
  486 + const res = await UserApi.importUserTemplateApi()
  487 + download.excel(res, '用户导入模版.xls')
  488 +}
  489 +// 文件上传之前判断
  490 +const beforeExcelUpload = (file: UploadRawFile) => {
  491 + const isExcel =
  492 + file.type === 'application/vnd.ms-excel' ||
  493 + file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  494 + const isLt5M = file.size / 1024 / 1024 < 5
  495 + if (!isExcel) message.error('上传文件只能是 xls / xlsx 格式!')
  496 + if (!isLt5M) message.error('上传文件大小不能超过 5MB!')
  497 + return isExcel && isLt5M
  498 +}
  499 +// 文件上传
  500 +const uploadRef = ref<UploadInstance>()
  501 +const submitFileForm = () => {
  502 + uploadHeaders.value = {
  503 + Authorization: 'Bearer ' + getAccessToken(),
  504 + 'tenant-id': getTenantId()
  505 + }
  506 + uploadDisabled.value = true
  507 + uploadRef.value!.submit()
  508 +}
  509 +// 文件上传成功
  510 +const handleFileSuccess = (response: any): void => {
  511 + if (response.code !== 0) {
  512 + message.error(response.msg)
  513 + return
  514 + }
  515 + importDialogVisible.value = false
  516 + uploadDisabled.value = false
  517 + const data = response.data
  518 + let text = '上传成功数量:' + data.createUsernames.length + ';'
  519 + for (let username of data.createUsernames) {
  520 + text += '< ' + username + ' >'
  521 + }
  522 + text += '更新成功数量:' + data.updateUsernames.length + ';'
  523 + for (const username of data.updateUsernames) {
  524 + text += '< ' + username + ' >'
  525 + }
  526 + text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
  527 + for (const username in data.failureUsernames) {
  528 + text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
  529 + }
  530 + message.alert(text)
  531 + getList()
  532 +}
  533 +// 文件数超出提示
  534 +const handleExceed = (): void => {
  535 + message.error('最多只能上传一个文件!')
  536 +}
  537 +// 上传错误提示
  538 +const excelUploadError = (): void => {
  539 + message.error('导入数据失败,请您重新上传!')
  540 +}
  541 +// ========== 初始化 ==========
  542 +onMounted(async () => {
  543 + await getPostOptions()
  544 + await getList()
  545 + await getTree()
  546 +})
  547 +</script>
549 548  
550 549 <style scoped>
551 550 .user {
... ...