Commit 4628d94415c1787da8b04499e295967f15c4eef5
1 parent
cd8e924d
feat: added system management sample page
Showing
14 changed files
with
150 additions
and
68 deletions
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) => |
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 '/@/components/Table'; |
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 | ]; | ... | ... |