create-young-proj 0.6.1 → 0.7.0

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 (75) hide show
  1. package/README.md +5 -3
  2. package/dist/index.mjs +9 -9
  3. package/package.json +1 -1
  4. package/template-nuxt-admin/Dockerfile +41 -0
  5. package/template-nuxt-admin/README.md +35 -0
  6. package/template-nuxt-admin/app.vue +41 -0
  7. package/template-nuxt-admin/boot.mjs +16 -0
  8. package/template-nuxt-admin/components/ScreenFull.vue +18 -0
  9. package/template-nuxt-admin/components/TopSearch.vue +73 -0
  10. package/template-nuxt-admin/components/TopUser.vue +69 -0
  11. package/template-nuxt-admin/components/YoungChangePassword.vue +87 -0
  12. package/template-nuxt-admin/components/YoungCodeInput.vue +60 -0
  13. package/template-nuxt-admin/components/YoungLink.vue +23 -0
  14. package/template-nuxt-admin/components/YoungLoading.vue +39 -0
  15. package/template-nuxt-admin/components/layout/Footer.vue +29 -0
  16. package/template-nuxt-admin/components/layout/Logo.vue +52 -0
  17. package/template-nuxt-admin/components/layout/Main.vue +41 -0
  18. package/template-nuxt-admin/components/layout/NavBar.vue +77 -0
  19. package/template-nuxt-admin/components/layout/SideBar.vue +90 -0
  20. package/template-nuxt-admin/components/layout/SubMenu.vue +46 -0
  21. package/template-nuxt-admin/components/layout/TabsBar.vue +183 -0
  22. package/template-nuxt-admin/composables/api.ts +104 -0
  23. package/template-nuxt-admin/composables/apis/delete.ts +37 -0
  24. package/template-nuxt-admin/composables/apis/get.ts +83 -0
  25. package/template-nuxt-admin/composables/apis/index.ts +10 -0
  26. package/template-nuxt-admin/composables/apis/patch.ts +74 -0
  27. package/template-nuxt-admin/composables/apis/post.ts +85 -0
  28. package/template-nuxt-admin/composables/config.ts +13 -0
  29. package/template-nuxt-admin/composables/icon.ts +27 -0
  30. package/template-nuxt-admin/composables/nav.ts +60 -0
  31. package/template-nuxt-admin/composables/tags.ts +108 -0
  32. package/template-nuxt-admin/composables/user.ts +29 -0
  33. package/template-nuxt-admin/config/.devrc +1 -0
  34. package/template-nuxt-admin/config/.onlinerc +1 -0
  35. package/template-nuxt-admin/config/.testrc +1 -0
  36. package/template-nuxt-admin/env.d.ts +47 -0
  37. package/template-nuxt-admin/error.vue +53 -0
  38. package/template-nuxt-admin/layouts/blank.vue +9 -0
  39. package/template-nuxt-admin/layouts/default.vue +124 -0
  40. package/template-nuxt-admin/middleware/auth.global.ts +39 -0
  41. package/template-nuxt-admin/nuxt.config.ts +101 -0
  42. package/template-nuxt-admin/package.json +44 -0
  43. package/template-nuxt-admin/pages/home/[id].vue +28 -0
  44. package/template-nuxt-admin/pages/index.vue +20 -0
  45. package/template-nuxt-admin/pages/login.vue +179 -0
  46. package/template-nuxt-admin/pages/system/api.vue +166 -0
  47. package/template-nuxt-admin/pages/system/hooks/useRole.ts +336 -0
  48. package/template-nuxt-admin/pages/system/menuList.vue +329 -0
  49. package/template-nuxt-admin/pages/system/role.vue +117 -0
  50. package/template-nuxt-admin/pages/system/user.vue +214 -0
  51. package/template-nuxt-admin/plugins/directive.ts +26 -0
  52. package/template-nuxt-admin/public/default_avatar.svg +1 -0
  53. package/template-nuxt-admin/public/favicon.ico +0 -0
  54. package/template-nuxt-admin/public/image_placeholder.svg +15 -0
  55. package/template-nuxt-admin/public/tabbar_bg.png +0 -0
  56. package/template-nuxt-admin/rome.json +26 -0
  57. package/template-nuxt-admin/server/api/[...all].ts +10 -0
  58. package/template-nuxt-admin/server/plugins/env.ts +89 -0
  59. package/template-nuxt-admin/server/routes/get/env.ts +13 -0
  60. package/template-nuxt-admin/server/tsconfig.json +3 -0
  61. package/template-nuxt-admin/server/utils/index.ts +35 -0
  62. package/template-nuxt-admin/styles/element.scss +30 -0
  63. package/template-nuxt-admin/styles/index.scss +59 -0
  64. package/template-nuxt-admin/styles/variable.scss +103 -0
  65. package/template-nuxt-admin/tsconfig.json +7 -0
  66. package/template-nuxt-admin/typings/global.d.ts +16 -0
  67. package/template-nuxt-admin/typings/system.d.ts +66 -0
  68. package/template-nuxt-admin/typings/user.d.ts +19 -0
  69. package/template-nuxt-admin/uno.config.ts +40 -0
  70. package/template-nuxt-admin/utils/tool.ts +207 -0
  71. package/template-nuxt-admin/yarn.lock +6849 -0
  72. package/template-uni-app/_npmrc +2 -0
  73. package/template-uni-app/src/layouts/tabbar.vue +6 -6
  74. package/template-uni-app/src/pages/index.vue +16 -12
  75. package/template-uni-app/src/pages/my.vue +7 -11
@@ -0,0 +1,329 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-07-25 16:45:17
4
+ * @LastEditTime: 2023-07-31 14:21:34
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { YoungTablePro, useFormMode, YoungDialog, YoungSelect } from '@bluesyoung/ui-vue3-element-plus';
9
+ import type { TableHeadItem } from '@bluesyoung/ui-vue3-element-plus';
10
+ import { deepClone, isArray } from '@bluesyoung/utils';
11
+ import { ElButton } from 'element-plus';
12
+
13
+ definePageMeta({
14
+ title: '菜单管理'
15
+ });
16
+
17
+ const FORM_TEMP: NavArrItem = {
18
+ breadcrumb: 0,
19
+ component: '',
20
+ createdAt: '',
21
+ creator: '',
22
+ icon: '',
23
+ id: 0,
24
+ name: '',
25
+ not_dev: 0,
26
+ parentId: 0,
27
+ path: '',
28
+ permission: '',
29
+ redirect: '',
30
+ sort: 0,
31
+ status: 1,
32
+ title: '',
33
+ updatedAt: '',
34
+ visible: 1
35
+ };
36
+ // key 的类型必须为 string 才会生效!!!
37
+ const expandKeys = ref<Set<string>>(new Set());
38
+
39
+ let tempArr: string[] = [];
40
+ const getFatherAndSon = (arr: NavArrItem[], num?: number): string[] => {
41
+ if (num === 1) {
42
+ tempArr = [];
43
+ }
44
+ for (const item of arr) {
45
+ tempArr.push(item.id + '');
46
+ if (item.children && isArray(item.children) && item.children.length > 0) {
47
+ getFatherAndSon(deepClone(item.children));
48
+ }
49
+ }
50
+ return tempArr;
51
+ };
52
+ const expandChange = (...args: any) => {
53
+ const [row, isOpen] = args as [NavArrItem, boolean];
54
+ const autoid = row.id;
55
+ if (isOpen) {
56
+ expandKeys.value.add(autoid + '');
57
+ } else {
58
+ const allSub = getFatherAndSon([row], 1);
59
+ allSub.forEach((v) => expandKeys.value.delete(v));
60
+ }
61
+ };
62
+ const tableData = ref<NavArrItem[]>([]);
63
+
64
+ const topMenuOption = ref<Partial<NavArrItem>[]>([]);
65
+
66
+ /**
67
+ * 生成节点映射
68
+ */
69
+ const nodeMap = new Map<string | number, NavArrItem>();
70
+ const generateNodeMap = (list: NavArrItem[]) => {
71
+ for (const node of list) {
72
+ nodeMap.set(node.id, node);
73
+ if (node.children && node.children?.length > 0) {
74
+ generateNodeMap(node.children);
75
+ }
76
+ }
77
+ };
78
+
79
+ /**
80
+ * 获取节点列表
81
+ */
82
+ const getList = async () => {
83
+ const list = Object.values(await apis.get.getMenuList());
84
+ generateNodeMap(list);
85
+
86
+ tableData.value = list;
87
+
88
+ topMenuOption.value = [
89
+ { title: '顶级目录', children: list, id: 0 }
90
+ ];
91
+ };
92
+
93
+ const {
94
+ isAdd,
95
+ isEdit,
96
+ form,
97
+ edit,
98
+ del,
99
+ sure,
100
+ clear,
101
+ formRef,
102
+ validForm
103
+ } = useFormMode<NavArrItem>(FORM_TEMP, {
104
+ addCbk: async () => {
105
+ const res = await validForm() as boolean;
106
+ if (res) {
107
+ const v = deepClone(form.value);
108
+ await apis.post.addMenuItem(v);
109
+ ElMessage.success('菜单添加成功!');
110
+ }
111
+ return res;
112
+ },
113
+ modCbk: async () => {
114
+ const res = await validForm() as boolean;
115
+ if (res) {
116
+ const v = deepClone(form.value);
117
+ await apis.patch.changeMenuItem(v);
118
+ ElMessage.success('菜单修改成功!');
119
+ }
120
+ return res;
121
+ },
122
+ delCbk: async (nav: NavArrItem) => {
123
+ apis.delete.deleteMenu(nav.id.toString());
124
+ expandKeys.value.delete(nav.id.toString());
125
+ ElMessage.success('节点删除成功!');
126
+ // 框架有 bug,视图更新不及时
127
+ location.reload();
128
+ },
129
+ cgEffect: async () => {
130
+ await getList();
131
+ }
132
+ });
133
+
134
+ /**
135
+ * 添加节点
136
+ */
137
+ const add = () => {
138
+ form.value.parentId = 0;
139
+ isAdd.value = true;
140
+ };
141
+
142
+ const tableHead: TableHeadItem<NavArrItem>[] = [
143
+ {
144
+ prop: 'title', label: '标题', render: (row) => h('div', { class: 'inline-flex items-center justify-center' }, [
145
+ h('div', { class: row.icon }),
146
+ h('div', row.title)
147
+ ])
148
+ },
149
+ {
150
+ prop: 'sort', label: '同级排序', aligin: 'center', render: (row, i) => h('div', {
151
+ class: 'flex text-2xl justify-center'
152
+ }, [
153
+ h('div', {
154
+ class: 'i-ic-round-keyboard-arrow-up cursor-pointer',
155
+ title: '向上',
156
+ onClick: async () => {
157
+ const arr = nodeMap.get(row.parentId.toString())?.children || [];
158
+ const index = arr.findIndex((m) => m.id === row.id);
159
+ if (index <= 0) {
160
+ return ElMessage.warning('已经到最前面啦!');
161
+ } else {
162
+ const pre = arr[index - 1];
163
+
164
+ const num = row.sort;
165
+ const preNum = pre.sort;
166
+
167
+ if (num !== preNum) {
168
+ row.sort = preNum;
169
+ pre.sort = num;
170
+ } else {
171
+ row.sort = num - 1;
172
+ }
173
+ await Promise.all([
174
+ apis.patch.changeMenuItem(row),
175
+ apis.patch.changeMenuItem(pre),
176
+ ]);
177
+ ElMessage.success('顺序修改成功!');
178
+ getList();
179
+ }
180
+ }
181
+ }),
182
+ h('div', {
183
+ class: 'i-ic-round-keyboard-arrow-down cursor-pointer',
184
+ title: '向下',
185
+ onClick: async () => {
186
+ const arr = nodeMap.get(row.parentId.toString())?.children || [];
187
+ const index = arr.findIndex((m) => m.id === row.id);
188
+ if (index === arr.length - 1) {
189
+ return ElMessage.warning('已经到最后面啦!');
190
+ } else {
191
+ const next = arr[index + 1];
192
+
193
+ const num = row.sort;
194
+ const nextNum = next.sort;
195
+
196
+ if (num !== nextNum) {
197
+ row.sort = nextNum;
198
+ next.sort = num;
199
+ } else {
200
+ row.sort = num + 1;
201
+ }
202
+ await Promise.all([
203
+ apis.patch.changeMenuItem(row),
204
+ apis.patch.changeMenuItem(next),
205
+ ]);
206
+ ElMessage.success('顺序修改成功!');
207
+ getList();
208
+ }
209
+ }
210
+ }),
211
+ ])
212
+ },
213
+ { prop: 'component', label: '页面路径' },
214
+ {
215
+ prop: 'visible', label: '隐藏/显示', render: (row) => h(YoungSelect, {
216
+ modelValue: row.visible,
217
+ options: [{ label: '显示', value: 1 }, { label: '隐藏', value: 0 }],
218
+ 'onUpdate:modelValue': async (e: number) => {
219
+ // 状态未修改
220
+ if (e === row.visible) {
221
+ return;
222
+ }
223
+
224
+ row.visible = e;
225
+ await apis.patch.changeMenuItem({ ...row, visible: e });
226
+ ElMessage.success('菜单修改成功!');
227
+ }
228
+ })
229
+ },
230
+ { prop: 'creator', label: '创建时间' },
231
+ {
232
+ prop: 'id', label: '操作', fixed: 'right', width: '180', render: (row) => h('div', [
233
+ h(ElButton, {
234
+ text: true,
235
+ class: '!p-0',
236
+ type: 'primary',
237
+ onClick: () => edit(row),
238
+ }, {
239
+ default: () => h('div', '编辑')
240
+ }),
241
+ h(ElButton, {
242
+ type: 'primary',
243
+ class: '!p-0',
244
+ text: true,
245
+ onClick: () => {
246
+ form.value.parentId = row.id;
247
+ isAdd.value = true;
248
+ },
249
+ }, {
250
+ default: () => h('div', '添加子节点')
251
+ }),
252
+ h(ElButton, {
253
+ text: true,
254
+ class: '!p-0',
255
+ type: 'danger',
256
+ onClick: () => del(row),
257
+ }, {
258
+ default: () => h('div', '删除')
259
+ })
260
+ ])
261
+ }
262
+ ];
263
+
264
+ getList();
265
+ </script>
266
+
267
+ <template>
268
+ <ElCard>
269
+ <div>
270
+ <ElButton type="primary" plain @click="add">新建菜单</ElButton>
271
+ </div>
272
+ <br />
273
+ <!-- 节点列表 -->
274
+ <YoungTablePro :table-data="tableData" :table-head="tableHead" :tree-props="{ children: 'children' }" row-key="id"
275
+ :expand-row-keys="[...expandKeys]" @expand-change="expandChange" />
276
+ <!-- 添加 / 编辑 -->
277
+ <YoungDialog width="370px" :is-add="isAdd" :diff-form="form" :is-edit="isEdit" @sure="sure" @clear="clear">
278
+ <template #body>
279
+ <ElForm ref="formRef" :model="form" label-width="80px" :label-position="WindowSize['lt-lg'] ? 'top' : 'left'">
280
+ <ElFormItem label="上级目录">
281
+ <ElCascader :model-value="form.parentId" :props="{
282
+ label: 'title',
283
+ value: 'id',
284
+ checkStrictly: true,
285
+ multiple: false
286
+ }" :options="topMenuOption" :show-all-levels="false"
287
+ @update:model-value="(e: any) => form.parentId = e[e.length - 1]" />
288
+ </ElFormItem>
289
+ <ElFormItem label="英文名称" prop="name" :rules="{ required: true, message: '请输入英文名', trigger: 'blur' }">
290
+ <ElInput v-model="form.name" placeholder="请输入英文名" />
291
+ </ElFormItem>
292
+ <ElFormItem label="标题" prop="title" :rules="{ required: true, message: '请输入页面标题', trigger: 'blur' }">
293
+ <ElInput v-model="form.title" placeholder="请输入页面标题" />
294
+ </ElFormItem>
295
+ <ElFormItem label="图标">
296
+ <ElSelect v-model="form.icon" class="select_icon" placeholder="请选择图标">
297
+ <template #prefix>
298
+ <div :class="form.icon" />
299
+ </template>
300
+ <ElOption v-for="(item, index) in MenuIconList" :key="index + 'sfjhaeoir'" :value="item.label">
301
+ <div :class="item.label" />
302
+ </ElOption>
303
+ </ElSelect>
304
+ </ElFormItem>
305
+ <ElFormItem label="排序">
306
+ <ElInput v-model.number="form.sort" />
307
+ </ElFormItem>
308
+ <ElFormItem label="是否显示">
309
+ <ElRadioGroup v-model="form.visible">
310
+ <ElRadio :label="1">显示</ElRadio>
311
+ <ElRadio :label="0">隐藏</ElRadio>
312
+ </ElRadioGroup>
313
+ </ElFormItem>
314
+ <ElFormItem label="路径" prop="component" :rules="form.parentId === 0 ? {} :
315
+ { message: '请输入合法的路径, eg: /path/page', trigger: 'blur', validator: (_: any, v: string) => v.trim() === '' || /\/(.*)\/(.*)/.test(v) }
316
+ ">
317
+ <ElInput v-model="form.component" />
318
+ </ElFormItem>
319
+ </ElForm>
320
+ </template>
321
+ </YoungDialog>
322
+ </ElCard>
323
+ </template>
324
+
325
+ <style lang="scss" scoped>
326
+ :deep(.select_icon .el-input__inner) {
327
+ opacity: 0 !important;
328
+ }
329
+ </style>
@@ -0,0 +1,117 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-07-25 16:45:39
4
+ * @LastEditTime: 2023-07-31 11:05:49
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { YoungTablePro, YoungDialog, YoungPagination, YoungSearchForm } from '@bluesyoung/ui-vue3-element-plus';
9
+ import { useRoleApi, useRoleBase, useRoleMenu } from './hooks/useRole';
10
+
11
+ definePageMeta({
12
+ title: '角色管理'
13
+ });
14
+
15
+ const {
16
+ query,
17
+ queryScheme,
18
+ reset,
19
+ getList,
20
+ tableHead,
21
+ tableData,
22
+ baseFormRef,
23
+ base
24
+ } = useRoleBase();
25
+
26
+ const { showPriority, menu } = useRoleMenu();
27
+
28
+ const { showApi, api } = useRoleApi();
29
+
30
+ getList();
31
+ </script>
32
+
33
+ <template>
34
+ <ElCard>
35
+ <YoungSearchForm v-model="query" :search-scheme="queryScheme" :on-search="getList" :on-reset="reset">
36
+ <template #btns>
37
+ <ElButton type="success" @click="base.isAdd = true">添加角色</ElButton>
38
+ </template>
39
+ </YoungSearchForm>
40
+ </ElCard>
41
+
42
+ <br />
43
+
44
+ <ElCard>
45
+ <YoungTablePro :table-data="tableData" :table-head="tableHead">
46
+ <template #operate>
47
+ <ElTableColumn label="操作" width="300px" fixed="right">
48
+ <template #default="scope">
49
+ <ElButton type="primary" link @click="base.edit(scope.row)">信息编辑</ElButton>
50
+ <ElButton type="primary" link @click="menu.edit(scope.row)">菜单编辑</ElButton>
51
+ <ElButton type="primary" link @click="api.edit(scope.row)">接口编辑</ElButton>
52
+ <ElButton type="danger" link @click="base.del(scope.row)">删除</ElButton>
53
+ </template>
54
+ </ElTableColumn>
55
+ </template>
56
+ </YoungTablePro>
57
+ <YoungPagination v-model:page="query.pageNum" v-model:limit="query.pageSize" :total="query.total"
58
+ @page-change="getList" />
59
+ </ElCard>
60
+ <!-- 基础信息编辑 -->
61
+ <YoungDialog :is-add="base.isAdd" :diff-form="base.form" :is-edit="base.isEdit" width="520px" @sure="base.sure"
62
+ @clear="base.clear">
63
+ <template #body>
64
+ <ElForm ref="baseFormRef" :model="base.form" label-width="120px"
65
+ :label-position="WindowSize['lt-lg'] ? 'top' : 'left'">
66
+ <ElFormItem label="角色名称(中文)" prop="name" :rules="[{ required: true, message: '请填写角色名称', trigger: 'blur' }]">
67
+ <ElInput v-model="base.form.name" class="!w-300px" />
68
+ </ElFormItem>
69
+ <ElFormItem label="关键字(英文)" prop="keyword" :rules="[{ required: true, message: '请填写关键字', trigger: 'blur' }]">
70
+ <ElInput v-model="base.form.keyword" class="!w-300px" />
71
+ </ElFormItem>
72
+ <ElFormItem label="角色描述">
73
+ <ElInput v-model="base.form.desc" class="!w-300px" />
74
+ </ElFormItem>
75
+ </ElForm>
76
+ </template>
77
+ </YoungDialog>
78
+ <!-- 菜单编辑 -->
79
+ <YoungDialog real-title="菜单编辑" :diff-form="menu.checkMap" :is-edit="showPriority" top="5vh" width="1200px"
80
+ @sure="menu.sure" @clear="menu.clear">
81
+ <template #body>
82
+ <ElTable :data="menu.tableData" row-key="id" :default-expand-all="false" class="max-h-600px !overflow-y-auto">
83
+ <ElTableColumn prop="name" label="节点名称" width="320px">
84
+ <template #default="scope">
85
+ <ElCheckbox v-model="menu.checkMap[scope.row.id]" @change="menu.selectChange(scope.row)">{{
86
+ scope.row.title
87
+ }}</ElCheckbox>
88
+ </template>
89
+ </ElTableColumn>
90
+ <ElTableColumn v-for="(item, index) in menu.tableHead" v-bind="item" :key="index + 'fsdjhfaer'" />
91
+ <ElTableColumn prop="name" label="隐藏/显示">
92
+ <template #default="scope">
93
+ <ElSwitch v-model="scope.row.visible" :active-value="1" :inactive-value="0" active-color="#409EFF"
94
+ inactive-color="#909399" disabled />
95
+ </template>
96
+ </ElTableColumn>
97
+ </ElTable>
98
+ </template>
99
+ </YoungDialog>
100
+ <!-- 接口编辑 -->
101
+ <YoungDialog real-title="接口编辑" :diff-form="api.checkMap" :is-edit="showApi" top="5vh" width="1200px" @sure="api.sure"
102
+ @clear="api.clear">
103
+ <template #body>
104
+ <ElTable :data="api.tableData" row-key="id" :default-expand-all="false" class="max-h-600px !overflow-y-auto">
105
+ <ElTableColumn prop="name" width="100px">
106
+ <template #header>
107
+ <ElCheckbox v-model="api.isAll" @change="api.changeAll">全选</ElCheckbox>
108
+ </template>
109
+ <template #default="scope">
110
+ <ElCheckbox v-model="api.checkMap[scope.row.id]">{{ scope.row.title }}</ElCheckbox>
111
+ </template>
112
+ </ElTableColumn>
113
+ <ElTableColumn v-for="(item, index) in api.tableHead" v-bind="item" :key="index + 'fsdjhfaer'" />
114
+ </ElTable>
115
+ </template>
116
+ </YoungDialog>
117
+ </template>
@@ -0,0 +1,214 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-07-25 16:46:00
4
+ * @LastEditTime: 2023-07-31 14:39:16
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { useFormMode, YoungDialog, YoungTablePro, YoungPagination, YoungSelect, useQuery, YoungSearchForm } from '@bluesyoung/ui-vue3-element-plus';
9
+ import type { TableDataItem, TableHeadItem, SelectOptionItem, YoungSearchScheme } from '@bluesyoung/ui-vue3-element-plus';
10
+ import { deepClone } from '@bluesyoung/utils';
11
+ import { ElButton } from 'element-plus';
12
+
13
+ definePageMeta({
14
+ title: '用户管理'
15
+ });
16
+
17
+ interface Form extends UserItem { op?: any; };
18
+ const FORM_TEMP: Form = {
19
+ id: 0,
20
+ username: '',
21
+ nickname: '',
22
+ mobile: '',
23
+ roleId: 1,
24
+ status: 1,
25
+ initPassword: ''
26
+ };
27
+ const {
28
+ isAdd,
29
+ isEdit,
30
+ edit,
31
+ del,
32
+ sure,
33
+ clear,
34
+ form,
35
+ formRef,
36
+ validForm
37
+ } = useFormMode<Form>(FORM_TEMP, {
38
+ addCbk: async () => {
39
+ const res = await validForm() as boolean;
40
+ if (res) {
41
+ const v = deepClone(form.value);
42
+ await apis.post.addUserItem(v);
43
+ ElMessage.success('新增成功!');
44
+ }
45
+ return res;
46
+ },
47
+ modCbk: async () => {
48
+ const res = await validForm() as boolean;
49
+ if (res) {
50
+ const v = deepClone(form.value);
51
+ await apis.patch.changeUserItem(v);
52
+ ElMessage.success('修改成功!');
53
+ }
54
+ return res;
55
+ },
56
+ delCbk: async (row) => {
57
+ await apis.delete.deleteUser(row.id.toString());
58
+ ElMessage.success('删除成功!');
59
+ query.value.pageNum = 1;
60
+ },
61
+ cgEffect: () => getList(),
62
+ });
63
+
64
+ const tableHead: TableHeadItem<Form>[] = [
65
+ { prop: 'id', label: '用户ID' },
66
+ { prop: 'username', label: '用户名' },
67
+ { prop: 'mobile', label: '手机号' },
68
+ { prop: 'role_name', label: '角色名称' },
69
+ { prop: 'creator', label: '创建信息' },
70
+ {
71
+ prop: 'op', label: '操作', width: '300px', fixed: 'right', render: (row) => h('div', [
72
+ hasPermission('/access/system/update/user') && h(ElButton, {
73
+ type: 'primary',
74
+ text: true,
75
+ class: '!p-0',
76
+ onClick: () => edit(row)
77
+ }, {
78
+ default: () => '编辑'
79
+ }),
80
+ hasPermission('/access/system/update/user') && h(ElButton, {
81
+ type: 'warning',
82
+ text: true,
83
+ class: '!p-0',
84
+ onClick: () => changePwd(row)
85
+ }, {
86
+ default: () => '修改密码'
87
+ }),
88
+ hasPermission('/access/system/del/user') && h(ElButton, {
89
+ type: 'danger',
90
+ text: true,
91
+ class: '!p-0',
92
+ onClick: () => del(row)
93
+ }, {
94
+ default: () => '删除'
95
+ })
96
+ ])
97
+ }
98
+ ];
99
+ const tableData = ref<TableDataItem<Form>[]>([]);
100
+
101
+ const getList = async () => {
102
+ const { list: role_list } = await apis.get.getRoleList({ noPagination: true });
103
+ roleList.value = (role_list || []).map((item: RoleItem) => {
104
+ return {
105
+ label: item.name,
106
+ value: item.id
107
+ };
108
+ });
109
+
110
+ const { list, pageNum, pageSize, total } = await apis.get.getUserList(query.value);
111
+ tableData.value = deepClone(list || []);
112
+
113
+ query.value.pageNum = +pageNum || 1;
114
+ query.value.pageSize = +pageSize || 50;
115
+ query.value.total = +total || 0;
116
+ };
117
+
118
+ const changePwd = (e: UserItem) => {
119
+ ElMessageBox.prompt('请输入新密码').then(async ({ value }) => {
120
+ value = value.trim()
121
+ if (value) {
122
+ await apis.patch.changeUserItem({ id: e.id, newPassword: value });
123
+ ElMessage.success('修改成功!');
124
+ }
125
+ }).catch(() => null);
126
+ };
127
+
128
+ interface Query extends BaseQuery {
129
+ username: string;
130
+ mobile: string;
131
+ status: 0 | 1;
132
+ }
133
+
134
+ const { query, reset } = useQuery<Query>(
135
+ {
136
+ pageNum: 1,
137
+ pageSize: 10,
138
+ total: 0,
139
+ username: '',
140
+ mobile: '',
141
+ status: 1
142
+ },
143
+ getList,
144
+ );
145
+ const queryScheme: YoungSearchScheme<Query> = {
146
+ username: {
147
+ type: 'input',
148
+ tip: '用户名',
149
+ attrs: {
150
+ placeholder: '请输入用户名',
151
+ },
152
+ },
153
+ mobile: {
154
+ type: 'input',
155
+ tip: '手机号',
156
+ attrs: {
157
+ placeholder: '请输入手机号',
158
+ maxlength: 11
159
+ }
160
+ },
161
+ status: {
162
+ type: 'select',
163
+ tip: '状态',
164
+ attrs: {
165
+ placeholder: '用户状态',
166
+ },
167
+ options: [{ label: '禁用', value: 0 }, { label: '启用', value: 1 }],
168
+ }
169
+ };
170
+
171
+ const roleList = ref<SelectOptionItem<number>[]>([]);
172
+
173
+ getList();
174
+ </script>
175
+ <template>
176
+ <ElCard>
177
+ <YoungSearchForm v-model="query" :search-scheme="queryScheme" :on-search="getList" :on-reset="reset">
178
+ <template #btns>
179
+ <ElButton v-permission="'/access/system/create/user'" type="success" @click="isAdd = true">添加用户</ElButton>
180
+ </template>
181
+ </YoungSearchForm>
182
+ </ElCard>
183
+
184
+ <br />
185
+
186
+ <ElCard>
187
+ <YoungTablePro :table-data="tableData" :table-head="tableHead" />
188
+ <YoungPagination v-model:page="query.pageNum" v-model:limit="query.pageSize" :total="query.total"
189
+ @page-change="getList" />
190
+ </ElCard>
191
+
192
+ <YoungDialog :is-add="isAdd" :diff-form="form" :is-edit="isEdit" width="520px" @sure="sure" @clear="clear">
193
+ <template #body>
194
+ <ElForm ref="formRef" :model="form" label-width="100px" :label-position="WindowSize['lt-lg'] ? 'top' : 'left'">
195
+ <ElFormItem label="用户名" prop="username" :rules="{ required: true, message: '请输用户名', trigger: 'blur' }">
196
+ <ElInput v-model="form.username" class="!w-300px" />
197
+ </ElFormItem>
198
+ <ElFormItem label="昵称" prop="nickname" :rules="{ required: true, message: '请输昵称(用于右上角展示)', trigger: 'blur' }">
199
+ <ElInput v-model="form.nickname" class="!w-300px" />
200
+ </ElFormItem>
201
+ <ElFormItem label="手机号" prop="mobile" :rules="{ required: true, message: '请输手机号', trigger: 'blur' }">
202
+ <ElInput v-model="form.mobile" :maxlength="11" class="!w-300px" />
203
+ </ElFormItem>
204
+ <ElFormItem label="角色">
205
+ <YoungSelect v-model="form.roleId" placeholder="请选择角色" :options="roleList" />
206
+ </ElFormItem>
207
+ <ElFormItem v-if="isAdd" label="初始密码" prop="initPassword"
208
+ :rules="{ required: true, message: '请输初始密码', trigger: 'blur' }">
209
+ <ElInput v-model="form.initPassword" class="!w-300px" />
210
+ </ElFormItem>
211
+ </ElForm>
212
+ </template>
213
+ </YoungDialog>
214
+ </template>
@@ -0,0 +1,26 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-07-31 14:04:44
4
+ * @LastEditTime: 2023-07-31 14:33:10
5
+ * @Description:
6
+ */
7
+ import { isString } from '@bluesyoung/utils';
8
+
9
+ export default defineNuxtPlugin((nuxtApp) => {
10
+ /**
11
+ * 鉴自指令定义的zdy
12
+ */
13
+ nuxtApp.vueApp.directive('permission', {
14
+ mounted(el, binding) {
15
+ const { value } = binding;
16
+
17
+ if (isString(value) && value) {
18
+ if (!hasPermission(value as unknown as string)) {
19
+ el.parentNode && el.parentNode.removeChild(el);
20
+ }
21
+ } else {
22
+ throw new Error(`need value! Like v-permission="'/access/get/list'"`);
23
+ }
24
+ },
25
+ });
26
+ });
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M28.717511 509.428622c0.068267 263.850667 213.8112 477.684622 477.411556 477.639111 263.600356-0.045511 477.275022-213.970489 477.229511-477.843911 0-263.850667-213.720178-477.752889-477.320534-477.752889-126.611911 0-248.035556 50.3808-337.555911 140.014934a478.094222 478.094222 0 0 0-139.764622 337.92z" fill="#FFFFFF" /><path d="M856.814933 833.9456c-12.811378-67.1744-130.753422-131.390578-236.612266-166.479644-5.165511-1.729422-37.387378-16.201956-17.2032-77.459912 52.747378-54.0672 92.728889-141.175467 92.728889-226.941155 0-131.754667-87.699911-200.886044-189.622045-200.886045-101.944889 0-189.394489 69.063111-189.394489 200.886045 0 86.038756 40.004267 173.488356 92.842667 227.487289 20.593778 53.976178-16.224711 74.092089-23.984356 76.777244-100.693333 36.340622-217.679644 100.784356-230.559289 166.479645a478.685867 478.685867 0 0 1-55.591822-574.350223 477.639111 477.639111 0 0 1 536.9856-209.942755C842.183111 107.633778 984.314311 295.5264 984.450844 509.5424a475.363556 475.363556 0 0 1-127.635911 324.4032" fill="#B8D4FF" /></svg>