Commit 4628d94415c1787da8b04499e295967f15c4eef5

Authored by Vben
1 parent cd8e924d

feat: added system management sample page

CHANGELOG.zh_CN.md
... ... @@ -8,12 +8,11 @@
8 8  
9 9 - axios 支持 form-data 格式请求
10 10 - 新增图标选择器组件(支持本地和在线方式)
11   -- 新增修改密码界面
12   -- 新增部门管理示例界面
13 11 - 新增 WebSocket 示例和服务脚本
14 12 - Tree 组件新增 `renderIcon` 属性用于控制层级图标显示
15 13 - Tree->actionItem 新增 show 属性,用于动态控制按钮显示
16 14 - Tree 新增工具栏/title/搜索功能
  15 +- 新增部门管理/修改密码/账号管理/角色管理/菜单管理示例界面
17 16  
18 17 ### ⚡ Performance Improvements
19 18  
... ...
mock/demo/system.ts
... ... @@ -116,7 +116,7 @@ export default [
116 116 },
117 117 },
118 118 {
119   - url: '/api/system/getRoleList',
  119 + url: '/api/system/getRoleListByPage',
120 120 timeout: 100,
121 121 method: 'get',
122 122 response: ({ query }) => {
... ... @@ -125,6 +125,14 @@ export default [
125 125 },
126 126 },
127 127 {
  128 + url: '/api/system/getAllRoleList',
  129 + timeout: 100,
  130 + method: 'get',
  131 + response: () => {
  132 + return resultSuccess(roleList);
  133 + },
  134 + },
  135 + {
128 136 url: '/api/system/getDeptList',
129 137 timeout: 100,
130 138 method: 'get',
... ...
src/api/demo/model/systemModel.ts
... ... @@ -4,11 +4,14 @@ export type AccountParams = BasicPageParams & {
4 4 account?: string;
5 5 nickname?: string;
6 6 };
7   -export type RoleParams = BasicPageParams & {
  7 +
  8 +export type RoleParams = {
8 9 roleName?: string;
9 10 status?: string;
10 11 };
11 12  
  13 +export type RolePageParams = BasicPageParams & RoleParams;
  14 +
12 15 export type DeptParams = {
13 16 deptName?: string;
14 17 status?: string;
... ... @@ -66,4 +69,6 @@ export type DeptListGetResultModel = BasicFetchResult<DeptListItem>;
66 69  
67 70 export type MenuListGetResultModel = BasicFetchResult<MenuListItem>;
68 71  
69   -export type RoleListGetResultModel = BasicFetchResult<RoleListItem>;
  72 +export type RolePageListGetResultModel = BasicFetchResult<RoleListItem>;
  73 +
  74 +export type RoleListGetResultModel = RoleListItem[];
... ...
src/api/demo/system.ts
... ... @@ -3,9 +3,11 @@ import {
3 3 DeptListItem,
4 4 MenuParams,
5 5 RoleParams,
  6 + RolePageParams,
6 7 MenuListGetResultModel,
7 8 DeptListGetResultModel,
8 9 AccountListGetResultModel,
  10 + RolePageListGetResultModel,
9 11 RoleListGetResultModel,
10 12 } from './model/systemModel';
11 13 import { defHttp } from '/@/utils/http/axios';
... ... @@ -14,7 +16,8 @@ enum Api {
14 16 AccountList = '/system/getAccountList',
15 17 DeptList = '/system/getDeptList',
16 18 MenuList = '/system/getMenuList',
17   - RoleList = '/system/getRoleList',
  19 + RolePageList = '/system/getRoleListByPage',
  20 + GetAllRoleList = '/system/getAllRoleList',
18 21 }
19 22  
20 23 export const getAccountList = (params: AccountParams) =>
... ... @@ -26,5 +29,8 @@ export const getDeptList = (params?: DeptListItem) =&gt;
26 29 export const getMenuList = (params?: MenuParams) =>
27 30 defHttp.get<MenuListGetResultModel>({ url: Api.MenuList, params });
28 31  
29   -export const getRoleList = (params?: RoleParams) =>
30   - defHttp.get<RoleListGetResultModel>({ url: Api.RoleList, params });
  32 +export const getRoleListByPage = (params?: RolePageParams) =>
  33 + defHttp.get<RolePageListGetResultModel>({ url: Api.RolePageList, params });
  34 +
  35 +export const getAllRoleList = (params?: RoleParams) =>
  36 + defHttp.get<RoleListGetResultModel>({ url: Api.GetAllRoleList, params });
... ...
src/components/Form/src/components/FormItem.tsx
... ... @@ -311,11 +311,12 @@ export default defineComponent({
311 311 const realColProps = { ...baseColProps, ...colProps };
312 312 const { isIfShow, isShow } = getShow();
313 313  
  314 + const values = unref(getValues);
314 315 const getContent = () => {
315 316 return colSlot
316   - ? getSlot(slots, colSlot, unref(getValues))
  317 + ? getSlot(slots, colSlot, values)
317 318 : renderColContent
318   - ? renderColContent(unref(getValues))
  319 + ? renderColContent(values)
319 320 : renderItem();
320 321 };
321 322  
... ...
src/components/Tree/src/TreeHeader.vue
... ... @@ -10,9 +10,12 @@
10 10 <Icon icon="ion:ellipsis-vertical" />
11 11 <template #overlay>
12 12 <Menu @click="handleMenuClick">
13   - <MenuItem v-for="item in toolbarList" :key="item.value">
14   - {{ item.label }}
15   - </MenuItem>
  13 + <template v-for="item in toolbarList" :key="item.value">
  14 + <MenuItem v-bind="{ key: item.value }">
  15 + {{ item.label }}
  16 + </MenuItem>
  17 + <MenuDivider v-if="item.divider" />
  18 + </template>
16 19 </Menu>
17 20 </template>
18 21 </Dropdown>
... ... @@ -46,6 +49,7 @@
46 49 Dropdown,
47 50 Menu,
48 51 MenuItem: Menu.Item,
  52 + MenuDivider: Menu.Divider,
49 53 InputSearch: Input.Search,
50 54 },
51 55 props: {
... ... @@ -64,9 +68,9 @@
64 68 const { t } = useI18n();
65 69 const toolbarList = ref([
66 70 { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
67   - { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL },
  71 + { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL, divider: true },
68 72 { label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL },
69   - { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL },
  73 + { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL, divider: true },
70 74 { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
71 75 { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
72 76 ]);
... ...
src/components/Tree/src/index.vue
1 1 <script lang="tsx">
2 2 import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
3 3  
4   - import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw } from 'vue';
  4 + import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw, watch } from 'vue';
5 5 import { Tree } from 'ant-design-vue';
6 6 import { TreeIcon } from './TreeIcon';
7 7 import TreeHeader from './TreeHeader.vue';
... ... @@ -27,6 +27,7 @@
27 27 }
28 28 export default defineComponent({
29 29 name: 'BasicTree',
  30 + inheritAttrs: false,
30 31 props: basicProps,
31 32 emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change'],
32 33 setup(props, { attrs, slots, emit }) {
... ... @@ -89,8 +90,9 @@
89 90 },
90 91 onCheck: (v: CheckKeys) => {
91 92 state.checkedKeys = v;
92   - emit('change', v);
93   - emit('update:value', v);
  93 + const rawVal = toRaw(v);
  94 + emit('change', rawVal);
  95 + emit('update:value', rawVal);
94 96 },
95 97 onRightClick: handleRightClick,
96 98 };
... ... @@ -191,11 +193,21 @@
191 193 state.checkedKeys = props.checkedKeys;
192 194 });
193 195  
194   - watchEffect(() => {
195   - if (props.value) {
196   - state.checkedKeys = props.value;
  196 + watch(
  197 + () => props.value,
  198 + () => {
  199 + state.checkedKeys = toRaw(props.value || []);
197 200 }
198   - });
  201 + );
  202 +
  203 + // watchEffect(() => {
  204 + // console.log('======================');
  205 + // console.log(props.value);
  206 + // console.log('======================');
  207 + // if (props.value) {
  208 + // state.checkedKeys = props.value;
  209 + // }
  210 + // });
199 211  
200 212 watchEffect(() => {
201 213 state.checkStrictly = props.checkStrictly;
... ...
src/router/menus/modules/demo/system.ts
... ... @@ -7,45 +7,29 @@ const menu: MenuModule = {
7 7 name: t('routes.demo.system.moduleName'),
8 8 path: '/system',
9 9 tag: {
10   - dot: true,
  10 + content: 'new',
11 11 },
12 12 children: [
13 13 {
14 14 path: 'account',
15 15 name: t('routes.demo.system.account'),
16   - tag: {
17   - dot: true,
18   - type: 'warn',
19   - },
20 16 },
21 17 {
22 18 path: 'role',
23 19 name: t('routes.demo.system.role'),
24   - tag: {
25   - dot: true,
26   - },
27 20 },
28 21 {
29 22 path: 'menu',
30 23 name: t('routes.demo.system.menu'),
31   - tag: {
32   - dot: true,
33   - },
34 24 },
35 25 {
36 26 path: 'dept',
37 27 name: t('routes.demo.system.dept'),
38   - tag: {
39   - dot: true,
40   - },
41 28 },
42 29  
43 30 {
44 31 path: 'changePassword',
45 32 name: t('routes.demo.system.password'),
46   - tag: {
47   - dot: true,
48   - },
49 33 },
50 34 ],
51 35 },
... ...
src/views/demo/system/account/AccountModal.vue
... ... @@ -8,13 +8,16 @@
8 8 import { BasicModal, useModalInner } from '/@/components/Modal';
9 9 import { BasicForm, useForm } from '/@/components/Form/index';
10 10 import { accountFormSchema } from './account.data';
  11 + import { getDeptList } from '/@/api/demo/system';
  12 +
11 13 export default defineComponent({
12 14 name: 'AccountModal',
13 15 components: { BasicModal, BasicForm },
14   - setup() {
  16 + emits: ['success', 'register'],
  17 + setup(_, { emit }) {
15 18 const isUpdate = ref(true);
16 19  
17   - const [registerForm, { setFieldsValue, validate }] = useForm({
  20 + const [registerForm, { setFieldsValue, updateSchema, validate }] = useForm({
18 21 labelWidth: 100,
19 22 schemas: accountFormSchema,
20 23 showActionButtonGroup: false,
... ... @@ -23,7 +26,7 @@
23 26 },
24 27 });
25 28  
26   - const [registerModal, { setModalProps }] = useModalInner((data) => {
  29 + const [registerModal, { setModalProps }] = useModalInner(async (data) => {
27 30 setModalProps({ confirmLoading: false });
28 31 isUpdate.value = !!data?.isUpdate;
29 32  
... ... @@ -32,6 +35,18 @@
32 35 ...data.record,
33 36 });
34 37 }
  38 +
  39 + const treeData = await getDeptList();
  40 + updateSchema([
  41 + {
  42 + field: 'pwd',
  43 + show: !unref(isUpdate),
  44 + },
  45 + {
  46 + field: 'dept',
  47 + componentProps: { treeData },
  48 + },
  49 + ]);
35 50 });
36 51  
37 52 const getTitle = computed(() => (!unref(isUpdate) ? '新增账号' : '编辑账号'));
... ... @@ -42,6 +57,7 @@
42 57 setModalProps({ confirmLoading: true });
43 58 // TODO custom api
44 59 console.log(values);
  60 + emit('success');
45 61 } finally {
46 62 setModalProps({ confirmLoading: false });
47 63 }
... ...
src/views/demo/system/account/account.data.ts
  1 +import { getAllRoleList } from '/@/api/demo/system';
1 2 import { BasicColumn } from '/@/components/Table';
2 3 import { FormSchema } from '/@/components/Table';
3 4  
... ... @@ -56,24 +57,51 @@ export const accountFormSchema: FormSchema[] = [
56 57 required: true,
57 58 },
58 59 {
  60 + field: 'pwd',
  61 + label: '密码',
  62 + component: 'InputPassword',
  63 + required: true,
  64 + show: true,
  65 + },
  66 + {
  67 + label: '角色',
  68 + field: 'role',
  69 + component: 'ApiSelect',
  70 + componentProps: {
  71 + api: getAllRoleList,
  72 + labelField: 'roleName',
  73 + valueField: 'roleValue',
  74 + },
  75 + required: true,
  76 + },
  77 + {
  78 + field: 'dept',
  79 + label: '所属部门',
  80 + component: 'TreeSelect',
  81 + componentProps: {
  82 + replaceFields: {
  83 + title: 'deptName',
  84 + key: 'id',
  85 + value: 'id',
  86 + },
  87 + getPopupContainer: () => document.body,
  88 + },
  89 + required: true,
  90 + },
  91 + {
59 92 field: 'nickname',
60 93 label: '昵称',
61 94 component: 'Input',
62 95 required: true,
63 96 },
  97 +
64 98 {
65 99 label: '邮箱',
66 100 field: 'email',
67 101 component: 'Input',
68 102 required: true,
69 103 },
70   - // TODO
71   - {
72   - label: '角色',
73   - field: 'role',
74   - component: 'Input',
75   - required: true,
76   - },
  104 +
77 105 {
78 106 label: '备注',
79 107 field: 'remark',
... ...
src/views/demo/system/account/index.vue
... ... @@ -2,7 +2,7 @@
2 2 <div :class="[prefixCls]">
3 3 <BasicTable @register="registerTable">
4 4 <template #toolbar>
5   - <a-button type="primary" @click="handleCreateAccount"> 新增账号 </a-button>
  5 + <a-button type="primary" @click="handleCreate"> 新增账号 </a-button>
6 6 </template>
7 7 <template #action="{ record }">
8 8 <TableAction
... ... @@ -23,7 +23,7 @@
23 23 />
24 24 </template>
25 25 </BasicTable>
26   - <AccountModal @register="registerModal" />
  26 + <AccountModal @register="registerModal" @success="handleSuccess" />
27 27 </div>
28 28 </template>
29 29 <script lang="ts">
... ... @@ -45,7 +45,7 @@
45 45 const { prefixCls } = useDesign('account-management');
46 46  
47 47 const [registerModal, { openModal }] = useModal();
48   - const [registerTable] = useTable({
  48 + const [registerTable, { reload }] = useTable({
49 49 title: '账号列表',
50 50 api: getAccountList,
51 51 columns,
... ... @@ -64,7 +64,7 @@
64 64 },
65 65 });
66 66  
67   - function handleCreateAccount() {
  67 + function handleCreate() {
68 68 openModal(true, {
69 69 isUpdate: false,
70 70 });
... ... @@ -81,13 +81,18 @@
81 81 console.log(record);
82 82 }
83 83  
  84 + function handleSuccess() {
  85 + reload();
  86 + }
  87 +
84 88 return {
85 89 prefixCls,
86 90 registerTable,
87 91 registerModal,
88   - handleCreateAccount,
  92 + handleCreate,
89 93 handleEdit,
90 94 handleDelete,
  95 + handleSuccess,
91 96 };
92 97 },
93 98 });
... ...
src/views/demo/system/role/RoleDrawer.vue
... ... @@ -7,7 +7,18 @@
7 7 width="500px"
8 8 @ok="handleSubmit"
9 9 >
10   - <BasicForm @register="registerForm" />
  10 + <BasicForm @register="registerForm">
  11 + <template #menu="{ model, field }">
  12 + <BasicTree
  13 + v-model:value="model[field]"
  14 + :treeData="treeData"
  15 + :replaceFields="{ title: 'menuName', key: 'id' }"
  16 + checkable
  17 + toolbar
  18 + title="菜单分配"
  19 + />
  20 + </template>
  21 + </BasicForm>
11 22 </BasicDrawer>
12 23 </template>
13 24 <script lang="ts">
... ... @@ -15,17 +26,19 @@
15 26 import { BasicForm, useForm } from '/@/components/Form/index';
16 27 import { formSchema } from './role.data';
17 28 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  29 + import { BasicTree, TreeItem } from '/@/components/Tree';
18 30  
19 31 import { getMenuList } from '/@/api/demo/system';
20 32  
21 33 export default defineComponent({
22 34 name: 'RoleDrawer',
23   - components: { BasicDrawer, BasicForm },
  35 + components: { BasicDrawer, BasicForm, BasicTree },
24 36 emits: ['success', 'register'],
25 37 setup(_, { emit }) {
26 38 const isUpdate = ref(true);
  39 + const treeData = ref<TreeItem[]>([]);
27 40  
28   - const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
  41 + const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
29 42 labelWidth: 90,
30 43 schemas: formSchema,
31 44 showActionButtonGroup: false,
... ... @@ -41,11 +54,7 @@
41 54 ...data.record,
42 55 });
43 56 }
44   - const treeData = await getMenuList();
45   - updateSchema({
46   - field: 'parentMenu',
47   - componentProps: { treeData },
48   - });
  57 + treeData.value = ((await getMenuList()) as any) as TreeItem[];
49 58 });
50 59  
51 60 const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
... ... @@ -63,7 +72,13 @@
63 72 }
64 73 }
65 74  
66   - return { registerDrawer, registerForm, getTitle, handleSubmit };
  75 + return {
  76 + registerDrawer,
  77 + registerForm,
  78 + getTitle,
  79 + handleSubmit,
  80 + treeData,
  81 + };
67 82 },
68 83 });
69 84 </script>
... ...
src/views/demo/system/role/index.vue
... ... @@ -30,7 +30,7 @@
30 30 import { defineComponent } from 'vue';
31 31  
32 32 import { BasicTable, useTable, TableAction } from '/@/components/Table';
33   - import { getRoleList } from '/@/api/demo/system';
  33 + import { getRoleListByPage } from '/@/api/demo/system';
34 34  
35 35 import { useDrawer } from '/@/components/Drawer';
36 36 import RoleDrawer from './RoleDrawer.vue';
... ... @@ -44,7 +44,7 @@
44 44 const [registerDrawer, { openDrawer }] = useDrawer();
45 45 const [registerTable, { reload }] = useTable({
46 46 title: '角色列表',
47   - api: getRoleList,
  47 + api: getRoleListByPage,
48 48 columns,
49 49 formConfig: {
50 50 labelWidth: 120,
... ...
src/views/demo/system/role/role.data.ts
... ... @@ -2,7 +2,6 @@ import { BasicColumn } from &#39;/@/components/Table&#39;;
2 2 import { FormSchema } from '/@/components/Table';
3 3 import { h } from 'vue';
4 4 import { Tag } from 'ant-design-vue';
5   -
6 5 export const columns: BasicColumn[] = [
7 6 {
8 7 title: '角色名称',
... ... @@ -94,9 +93,9 @@ export const formSchema: FormSchema[] = [
94 93 component: 'InputTextArea',
95 94 },
96 95 {
97   - label: '菜单分配',
  96 + label: ' ',
98 97 field: 'menu',
99 98 slot: 'menu',
100   - component: 'Render',
  99 + component: 'Input',
101 100 },
102 101 ];
... ...