Commit 37669d067c43a3807df848f0caf67cbeab3e1b33
1 parent
c8e84dc1
wip: add account management page
Showing
23 changed files
with
372 additions
and
16 deletions
mock/demo/system.ts
0 → 100644
1 | +import { MockMethod } from 'vite-plugin-mock'; | ||
2 | +import { resultPageSuccess } from '../_util'; | ||
3 | + | ||
4 | +const list = (() => { | ||
5 | + const result: any[] = []; | ||
6 | + for (let index = 0; index < 20; index++) { | ||
7 | + result.push({ | ||
8 | + id: `${index}`, | ||
9 | + account: '@first', | ||
10 | + email: '@email', | ||
11 | + nickname: '@cname()', | ||
12 | + role: '@first', | ||
13 | + updateTime: '@datetime', | ||
14 | + remark: '@cword(0,20)', | ||
15 | + }); | ||
16 | + } | ||
17 | + return result; | ||
18 | +})(); | ||
19 | + | ||
20 | +export default [ | ||
21 | + { | ||
22 | + url: '/api/system/getAccountList', | ||
23 | + timeout: 100, | ||
24 | + method: 'get', | ||
25 | + response: ({ query }) => { | ||
26 | + const { page = 1, pageSize = 20 } = query; | ||
27 | + return resultPageSuccess(page, pageSize, list); | ||
28 | + }, | ||
29 | + }, | ||
30 | +] as MockMethod[]; |
mock/demo/table-demo.ts
@@ -28,7 +28,7 @@ const demoList = (() => { | @@ -28,7 +28,7 @@ const demoList = (() => { | ||
28 | export default [ | 28 | export default [ |
29 | { | 29 | { |
30 | url: '/api/table/getDemoList', | 30 | url: '/api/table/getDemoList', |
31 | - timeout: 1000, | 31 | + timeout: 100, |
32 | method: 'get', | 32 | method: 'get', |
33 | response: ({ query }) => { | 33 | response: ({ query }) => { |
34 | const { page = 1, pageSize = 20 } = query; | 34 | const { page = 1, pageSize = 20 } = query; |
src/api/demo/model/systemModel.ts
0 → 100644
1 | +import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel'; | ||
2 | + | ||
3 | +export type Params = BasicPageParams & { | ||
4 | + account?: string; | ||
5 | + nickname?: string; | ||
6 | +}; | ||
7 | + | ||
8 | +export interface DemoListItem { | ||
9 | + id: string; | ||
10 | + account: string; | ||
11 | + email: string; | ||
12 | + nickname: string; | ||
13 | + role: number; | ||
14 | + updateTime: string; | ||
15 | + remark: string; | ||
16 | +} | ||
17 | + | ||
18 | +/** | ||
19 | + * @description: Request list return value | ||
20 | + */ | ||
21 | +export type DemoListGetResultModel = BasicFetchResult<DemoListItem>; |
src/api/demo/system.ts
0 → 100644
1 | +import { Params, DemoListGetResultModel } from './model/systemModel'; | ||
2 | +import { defHttp } from '/@/utils/http/axios'; | ||
3 | + | ||
4 | +enum Api { | ||
5 | + // The address does not exist | ||
6 | + AccountList = '/system/getAccountList', | ||
7 | +} | ||
8 | + | ||
9 | +export const getAccountList = (params: Params) => | ||
10 | + defHttp.get<DemoListGetResultModel>({ url: Api.AccountList, params }); |
src/components/Table/src/style/index.less
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | 4 | ||
5 | .@{prefix-cls} { | 5 | .@{prefix-cls} { |
6 | &-form-container { | 6 | &-form-container { |
7 | + width: 100%; | ||
7 | padding: 16px; | 8 | padding: 16px; |
8 | 9 | ||
9 | .ant-form { | 10 | .ant-form { |
@@ -50,8 +51,8 @@ | @@ -50,8 +51,8 @@ | ||
50 | 51 | ||
51 | // | 52 | // |
52 | .ant-table { | 53 | .ant-table { |
53 | - // width: 100%; | ||
54 | - // overflow-x: hidden; | 54 | + width: 100%; |
55 | + overflow-x: hidden; | ||
55 | // border: none; | 56 | // border: none; |
56 | 57 | ||
57 | &-title { | 58 | &-title { |
@@ -159,15 +160,15 @@ | @@ -159,15 +160,15 @@ | ||
159 | // overflow-y: hidden !important; | 160 | // overflow-y: hidden !important; |
160 | // } | 161 | // } |
161 | 162 | ||
162 | - // .ant-table-fixed { | ||
163 | - // border-bottom: none; | ||
164 | - // } | 163 | + // .ant-table-fixed { |
164 | + // border-bottom: none; | ||
165 | + // } | ||
165 | // } | 166 | // } |
166 | 167 | ||
167 | // .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th, | 168 | // .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th, |
168 | // .ant-table-tbody > tr > td { | 169 | // .ant-table-tbody > tr > td { |
169 | // word-break: break-word; | 170 | // word-break: break-word; |
170 | - // border-color: @border-color !important; | 171 | + // // border-color: @border-color !important; |
171 | // } | 172 | // } |
172 | 173 | ||
173 | .ant-table-footer { | 174 | .ant-table-footer { |
src/locales/lang/en/routes/demo/system.ts
0 → 100644
src/locales/lang/zh_CN/routes/demo/system.ts
0 → 100644
src/router/menus/modules/demo/system.ts
0 → 100644
1 | +import type { MenuModule } from '/@/router/types'; | ||
2 | +import { t } from '/@/hooks/web/useI18n'; | ||
3 | + | ||
4 | +const menu: MenuModule = { | ||
5 | + orderNo: 2000, | ||
6 | + menu: { | ||
7 | + name: t('routes.demo.system.moduleName'), | ||
8 | + path: '/system', | ||
9 | + children: [ | ||
10 | + { | ||
11 | + path: 'account', | ||
12 | + name: t('routes.demo.system.account'), | ||
13 | + }, | ||
14 | + ], | ||
15 | + }, | ||
16 | +}; | ||
17 | +export default menu; |
src/router/routes/modules/dashboard.ts
@@ -9,7 +9,7 @@ const dashboard: AppRouteModule = { | @@ -9,7 +9,7 @@ const dashboard: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/dashboard/workbench', | 10 | redirect: '/dashboard/workbench', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'bx:bx-home', | 12 | + icon: 'ion:grid-outline', |
13 | title: t('routes.dashboard.dashboard'), | 13 | title: t('routes.dashboard.dashboard'), |
14 | }, | 14 | }, |
15 | children: [ | 15 | children: [ |
src/router/routes/modules/demo/charts.ts
@@ -9,7 +9,7 @@ const charts: AppRouteModule = { | @@ -9,7 +9,7 @@ const charts: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/charts/apexChart', | 10 | redirect: '/charts/apexChart', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'vaadin:spline-area-chart', | 12 | + icon: 'ion:bar-chart-outline', |
13 | title: t('routes.demo.charts.charts'), | 13 | title: t('routes.demo.charts.charts'), |
14 | }, | 14 | }, |
15 | children: [ | 15 | children: [ |
src/router/routes/modules/demo/comp.ts
@@ -9,7 +9,7 @@ const comp: AppRouteModule = { | @@ -9,7 +9,7 @@ const comp: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/comp/basic', | 10 | redirect: '/comp/basic', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'ic:outline-settings-input-component', | 12 | + icon: 'ion:layers-outline', |
13 | title: t('routes.demo.comp.comp'), | 13 | title: t('routes.demo.comp.comp'), |
14 | }, | 14 | }, |
15 | 15 |
src/router/routes/modules/demo/feat.ts
@@ -9,7 +9,7 @@ const feat: AppRouteModule = { | @@ -9,7 +9,7 @@ const feat: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/feat/icon', | 10 | redirect: '/feat/icon', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'ic:outline-featured-play-list', | 12 | + icon: 'ion:git-compare-outline', |
13 | title: t('routes.demo.feat.feat'), | 13 | title: t('routes.demo.feat.feat'), |
14 | }, | 14 | }, |
15 | children: [ | 15 | children: [ |
src/router/routes/modules/demo/iframe.ts
@@ -10,7 +10,7 @@ const iframe: AppRouteModule = { | @@ -10,7 +10,7 @@ const iframe: AppRouteModule = { | ||
10 | component: LAYOUT, | 10 | component: LAYOUT, |
11 | redirect: '/frame/doc', | 11 | redirect: '/frame/doc', |
12 | meta: { | 12 | meta: { |
13 | - icon: 'mdi:page-next-outline', | 13 | + icon: 'ion:tv-outline', |
14 | title: t('routes.demo.iframe.frame'), | 14 | title: t('routes.demo.iframe.frame'), |
15 | }, | 15 | }, |
16 | 16 |
src/router/routes/modules/demo/level.ts
@@ -9,7 +9,7 @@ const permission: AppRouteModule = { | @@ -9,7 +9,7 @@ const permission: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/level/menu1/menu1-1/menu1-1-1', | 10 | redirect: '/level/menu1/menu1-1/menu1-1-1', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'carbon:user-role', | 12 | + icon: 'ion:menu-outline', |
13 | title: t('routes.demo.level.level'), | 13 | title: t('routes.demo.level.level'), |
14 | }, | 14 | }, |
15 | 15 |
src/router/routes/modules/demo/page.ts
@@ -12,7 +12,7 @@ const page: AppRouteModule = { | @@ -12,7 +12,7 @@ const page: AppRouteModule = { | ||
12 | component: LAYOUT, | 12 | component: LAYOUT, |
13 | redirect: '/page-demo/exception', | 13 | redirect: '/page-demo/exception', |
14 | meta: { | 14 | meta: { |
15 | - icon: 'mdi:page-next-outline', | 15 | + icon: 'ion:aperture-outline', |
16 | title: t('routes.demo.page.page'), | 16 | title: t('routes.demo.page.page'), |
17 | }, | 17 | }, |
18 | children: [ | 18 | children: [ |
src/router/routes/modules/demo/permission.ts
@@ -10,7 +10,7 @@ const permission: AppRouteModule = { | @@ -10,7 +10,7 @@ const permission: AppRouteModule = { | ||
10 | component: LAYOUT, | 10 | component: LAYOUT, |
11 | redirect: '/permission/front/page', | 11 | redirect: '/permission/front/page', |
12 | meta: { | 12 | meta: { |
13 | - icon: 'carbon:user-role', | 13 | + icon: 'ion:key-outline', |
14 | title: t('routes.demo.permission.permission'), | 14 | title: t('routes.demo.permission.permission'), |
15 | }, | 15 | }, |
16 | 16 |
src/router/routes/modules/demo/system.ts
0 → 100644
1 | +import type { AppRouteModule } from '/@/router/types'; | ||
2 | + | ||
3 | +import { LAYOUT } from '/@/router/constant'; | ||
4 | +import { t } from '/@/hooks/web/useI18n'; | ||
5 | + | ||
6 | +const system: AppRouteModule = { | ||
7 | + path: '/system', | ||
8 | + name: 'System', | ||
9 | + component: LAYOUT, | ||
10 | + redirect: '/system/account', | ||
11 | + meta: { | ||
12 | + icon: 'ion:settings-outline', | ||
13 | + title: t('routes.demo.system.moduleName'), | ||
14 | + }, | ||
15 | + children: [ | ||
16 | + { | ||
17 | + path: 'account', | ||
18 | + name: 'Account', | ||
19 | + meta: { | ||
20 | + title: t('routes.demo.system.account'), | ||
21 | + }, | ||
22 | + component: () => import('/@/views/demo/system/account/index.vue'), | ||
23 | + }, | ||
24 | + ], | ||
25 | +}; | ||
26 | + | ||
27 | +export default system; |
src/router/routes/modules/home.ts
@@ -9,7 +9,7 @@ const dashboard: AppRouteModule = { | @@ -9,7 +9,7 @@ const dashboard: AppRouteModule = { | ||
9 | component: LAYOUT, | 9 | component: LAYOUT, |
10 | redirect: '/home/welcome', | 10 | redirect: '/home/welcome', |
11 | meta: { | 11 | meta: { |
12 | - icon: 'bx:bx-home', | 12 | + icon: 'ion:home-outline', |
13 | title: t('routes.dashboard.welcome'), | 13 | title: t('routes.dashboard.welcome'), |
14 | }, | 14 | }, |
15 | children: [ | 15 | children: [ |
src/views/biz/.gitkeep deleted
100644 → 0
src/views/demo/.gitkeep deleted
100644 → 0
src/views/demo/system/account/AccountModal.vue
0 → 100644
1 | +<template> | ||
2 | + <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit"> | ||
3 | + <BasicForm @register="registerForm" /> | ||
4 | + </BasicModal> | ||
5 | +</template> | ||
6 | +<script lang="ts"> | ||
7 | + import { defineComponent, ref, computed, unref } from 'vue'; | ||
8 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
9 | + import { BasicForm, useForm } from '/@/components/Form/index'; | ||
10 | + import { accountFormSchema } from './account.data'; | ||
11 | + export default defineComponent({ | ||
12 | + name: 'AccountModal', | ||
13 | + components: { BasicModal, BasicForm }, | ||
14 | + setup() { | ||
15 | + const isUpdate = ref(true); | ||
16 | + | ||
17 | + const [registerForm, { setFieldsValue, validate }] = useForm({ | ||
18 | + labelWidth: 100, | ||
19 | + schemas: accountFormSchema, | ||
20 | + showActionButtonGroup: false, | ||
21 | + actionColOptions: { | ||
22 | + span: 23, | ||
23 | + }, | ||
24 | + }); | ||
25 | + | ||
26 | + const [registerModal, { setModalProps }] = useModalInner((data) => { | ||
27 | + isUpdate.value = !!data?.isUpdate; | ||
28 | + | ||
29 | + if (unref(isUpdate)) { | ||
30 | + setFieldsValue({ | ||
31 | + ...data.record, | ||
32 | + }); | ||
33 | + } | ||
34 | + }); | ||
35 | + | ||
36 | + const getTitle = computed(() => (!unref(isUpdate) ? '新增账号' : '编辑账号')); | ||
37 | + | ||
38 | + async function handleSubmit() { | ||
39 | + try { | ||
40 | + const values = await validate(); | ||
41 | + setModalProps({ confirmLoading: true }); | ||
42 | + // TODO custom api | ||
43 | + console.log(values); | ||
44 | + } finally { | ||
45 | + setModalProps({ confirmLoading: true }); | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + return { registerModal, registerForm, getTitle, handleSubmit }; | ||
50 | + }, | ||
51 | + }); | ||
52 | +</script> |
src/views/demo/system/account/account.data.ts
0 → 100644
1 | +import { BasicColumn } from '/@/components/Table'; | ||
2 | +import { FormSchema } from '/@/components/Table'; | ||
3 | + | ||
4 | +export const columns: BasicColumn[] = [ | ||
5 | + { | ||
6 | + title: 'ID', | ||
7 | + dataIndex: 'id', | ||
8 | + width: 80, | ||
9 | + }, | ||
10 | + { | ||
11 | + title: '用户名', | ||
12 | + dataIndex: 'account', | ||
13 | + width: 120, | ||
14 | + }, | ||
15 | + { | ||
16 | + title: '昵称', | ||
17 | + dataIndex: 'nickname', | ||
18 | + width: 120, | ||
19 | + }, | ||
20 | + { | ||
21 | + title: '邮箱', | ||
22 | + dataIndex: 'email', | ||
23 | + width: 200, | ||
24 | + }, | ||
25 | + { | ||
26 | + title: '更新时间', | ||
27 | + dataIndex: 'updateTime', | ||
28 | + width: 180, | ||
29 | + }, | ||
30 | + { | ||
31 | + title: '角色', | ||
32 | + dataIndex: 'role', | ||
33 | + width: 200, | ||
34 | + }, | ||
35 | + { | ||
36 | + title: '备注', | ||
37 | + dataIndex: 'remark', | ||
38 | + width: 200, | ||
39 | + }, | ||
40 | +]; | ||
41 | + | ||
42 | +export const searchFormSchema: FormSchema[] = [ | ||
43 | + { | ||
44 | + field: 'account', | ||
45 | + label: '用户名', | ||
46 | + component: 'Input', | ||
47 | + colProps: { span: 8 }, | ||
48 | + }, | ||
49 | + { | ||
50 | + field: 'nickname', | ||
51 | + label: '昵称', | ||
52 | + component: 'Input', | ||
53 | + colProps: { span: 8 }, | ||
54 | + }, | ||
55 | +]; | ||
56 | + | ||
57 | +export const accountFormSchema: FormSchema[] = [ | ||
58 | + { | ||
59 | + field: 'account', | ||
60 | + label: '用户名', | ||
61 | + component: 'Input', | ||
62 | + required: true, | ||
63 | + }, | ||
64 | + { | ||
65 | + field: 'nickname', | ||
66 | + label: '昵称', | ||
67 | + component: 'Input', | ||
68 | + required: true, | ||
69 | + }, | ||
70 | + { | ||
71 | + label: '邮箱', | ||
72 | + field: 'email', | ||
73 | + component: 'Input', | ||
74 | + required: true, | ||
75 | + }, | ||
76 | + // TODO | ||
77 | + { | ||
78 | + label: '角色', | ||
79 | + field: 'role', | ||
80 | + component: 'Input', | ||
81 | + required: true, | ||
82 | + }, | ||
83 | + { | ||
84 | + label: '备注', | ||
85 | + field: 'remark', | ||
86 | + component: 'InputTextArea', | ||
87 | + }, | ||
88 | +]; |
src/views/demo/system/account/index.vue
0 → 100644
1 | +<template> | ||
2 | + <div :class="[prefixCls]"> | ||
3 | + <BasicTable @register="registerTable"> | ||
4 | + <template #toolbar> | ||
5 | + <a-button type="primary" @click="handleCreateAccount"> 新增账号 </a-button> | ||
6 | + </template> | ||
7 | + <template #action="{ record }"> | ||
8 | + <TableAction | ||
9 | + :actions="[ | ||
10 | + { | ||
11 | + label: '编辑', | ||
12 | + onClick: handleEdit.bind(null, record), | ||
13 | + }, | ||
14 | + { | ||
15 | + label: '删除', | ||
16 | + color: 'error', | ||
17 | + popConfirm: { | ||
18 | + title: '是否确认删除', | ||
19 | + confirm: handleDelete.bind(null, record), | ||
20 | + }, | ||
21 | + }, | ||
22 | + ]" | ||
23 | + /> | ||
24 | + </template> | ||
25 | + </BasicTable> | ||
26 | + <AccountModal @register="registerModal" /> | ||
27 | + </div> | ||
28 | +</template> | ||
29 | +<script lang="ts"> | ||
30 | + import { defineComponent } from 'vue'; | ||
31 | + | ||
32 | + import { useDesign } from '/@/hooks/web/useDesign'; | ||
33 | + import { BasicTable, useTable, TableAction } from '/@/components/Table'; | ||
34 | + import { getAccountList } from '/@/api/demo/system'; | ||
35 | + | ||
36 | + import { useModal } from '/@/components/Modal'; | ||
37 | + import AccountModal from './AccountModal.vue'; | ||
38 | + | ||
39 | + import { columns, searchFormSchema } from './account.data'; | ||
40 | + | ||
41 | + export default defineComponent({ | ||
42 | + name: 'AccountManagement', | ||
43 | + components: { BasicTable, AccountModal, TableAction }, | ||
44 | + setup() { | ||
45 | + const { prefixCls } = useDesign('account-management'); | ||
46 | + | ||
47 | + const [registerModal, { openModal }] = useModal(); | ||
48 | + const [registerTable] = useTable({ | ||
49 | + title: '账号列表', | ||
50 | + api: getAccountList, | ||
51 | + columns, | ||
52 | + formConfig: { | ||
53 | + labelWidth: 120, | ||
54 | + schemas: searchFormSchema, | ||
55 | + }, | ||
56 | + useSearchForm: true, | ||
57 | + showTableSetting: true, | ||
58 | + actionColumn: { | ||
59 | + width: 160, | ||
60 | + title: '操作', | ||
61 | + dataIndex: 'action', | ||
62 | + slots: { customRender: 'action' }, | ||
63 | + }, | ||
64 | + }); | ||
65 | + | ||
66 | + function handleCreateAccount() { | ||
67 | + openModal(true, { | ||
68 | + isUpdate: false, | ||
69 | + }); | ||
70 | + } | ||
71 | + | ||
72 | + function handleEdit(record: Recordable) { | ||
73 | + openModal(true, { | ||
74 | + record, | ||
75 | + isUpdate: true, | ||
76 | + }); | ||
77 | + } | ||
78 | + | ||
79 | + function handleDelete(record: Recordable) { | ||
80 | + console.log(record); | ||
81 | + } | ||
82 | + | ||
83 | + return { | ||
84 | + prefixCls, | ||
85 | + registerTable, | ||
86 | + registerModal, | ||
87 | + handleCreateAccount, | ||
88 | + handleEdit, | ||
89 | + handleDelete, | ||
90 | + }; | ||
91 | + }, | ||
92 | + }); | ||
93 | +</script> | ||
94 | +<style lang="less" scoped> | ||
95 | + @prefix-cls: ~'@{namespace}-account-management'; | ||
96 | + | ||
97 | + .@{prefix-cls} { | ||
98 | + display: flex; | ||
99 | + } | ||
100 | +</style> |