Переглянути джерело

feat: added system management sample page

Vben 4 роки тому
батько
коміт
4628d94415

+ 1 - 2
CHANGELOG.zh_CN.md

@@ -8,12 +8,11 @@
 
 - axios 支持 form-data 格式请求
 - 新增图标选择器组件(支持本地和在线方式)
-- 新增修改密码界面
-- 新增部门管理示例界面
 - 新增 WebSocket 示例和服务脚本
 - Tree 组件新增 `renderIcon` 属性用于控制层级图标显示
 - Tree->actionItem 新增 show 属性,用于动态控制按钮显示
 - Tree 新增工具栏/title/搜索功能
+- 新增部门管理/修改密码/账号管理/角色管理/菜单管理示例界面
 
 ### ⚡ Performance Improvements
 

+ 9 - 1
mock/demo/system.ts

@@ -116,7 +116,7 @@ export default [
     },
   },
   {
-    url: '/api/system/getRoleList',
+    url: '/api/system/getRoleListByPage',
     timeout: 100,
     method: 'get',
     response: ({ query }) => {
@@ -125,6 +125,14 @@ export default [
     },
   },
   {
+    url: '/api/system/getAllRoleList',
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(roleList);
+    },
+  },
+  {
     url: '/api/system/getDeptList',
     timeout: 100,
     method: 'get',

+ 7 - 2
src/api/demo/model/systemModel.ts

@@ -4,11 +4,14 @@ export type AccountParams = BasicPageParams & {
   account?: string;
   nickname?: string;
 };
-export type RoleParams = BasicPageParams & {
+
+export type RoleParams = {
   roleName?: string;
   status?: string;
 };
 
+export type RolePageParams = BasicPageParams & RoleParams;
+
 export type DeptParams = {
   deptName?: string;
   status?: string;
@@ -66,4 +69,6 @@ export type DeptListGetResultModel = BasicFetchResult<DeptListItem>;
 
 export type MenuListGetResultModel = BasicFetchResult<MenuListItem>;
 
-export type RoleListGetResultModel = BasicFetchResult<RoleListItem>;
+export type RolePageListGetResultModel = BasicFetchResult<RoleListItem>;
+
+export type RoleListGetResultModel = RoleListItem[];

+ 9 - 3
src/api/demo/system.ts

@@ -3,9 +3,11 @@ import {
   DeptListItem,
   MenuParams,
   RoleParams,
+  RolePageParams,
   MenuListGetResultModel,
   DeptListGetResultModel,
   AccountListGetResultModel,
+  RolePageListGetResultModel,
   RoleListGetResultModel,
 } from './model/systemModel';
 import { defHttp } from '/@/utils/http/axios';
@@ -14,7 +16,8 @@ enum Api {
   AccountList = '/system/getAccountList',
   DeptList = '/system/getDeptList',
   MenuList = '/system/getMenuList',
-  RoleList = '/system/getRoleList',
+  RolePageList = '/system/getRoleListByPage',
+  GetAllRoleList = '/system/getAllRoleList',
 }
 
 export const getAccountList = (params: AccountParams) =>
@@ -26,5 +29,8 @@ export const getDeptList = (params?: DeptListItem) =>
 export const getMenuList = (params?: MenuParams) =>
   defHttp.get<MenuListGetResultModel>({ url: Api.MenuList, params });
 
-export const getRoleList = (params?: RoleParams) =>
-  defHttp.get<RoleListGetResultModel>({ url: Api.RoleList, params });
+export const getRoleListByPage = (params?: RolePageParams) =>
+  defHttp.get<RolePageListGetResultModel>({ url: Api.RolePageList, params });
+
+export const getAllRoleList = (params?: RoleParams) =>
+  defHttp.get<RoleListGetResultModel>({ url: Api.GetAllRoleList, params });

+ 3 - 2
src/components/Form/src/components/FormItem.tsx

@@ -311,11 +311,12 @@ export default defineComponent({
       const realColProps = { ...baseColProps, ...colProps };
       const { isIfShow, isShow } = getShow();
 
+      const values = unref(getValues);
       const getContent = () => {
         return colSlot
-          ? getSlot(slots, colSlot, unref(getValues))
+          ? getSlot(slots, colSlot, values)
           : renderColContent
-          ? renderColContent(unref(getValues))
+          ? renderColContent(values)
           : renderItem();
       };
 

+ 9 - 5
src/components/Tree/src/TreeHeader.vue

@@ -10,9 +10,12 @@
         <Icon icon="ion:ellipsis-vertical" />
         <template #overlay>
           <Menu @click="handleMenuClick">
-            <MenuItem v-for="item in toolbarList" :key="item.value">
-              {{ item.label }}
-            </MenuItem>
+            <template v-for="item in toolbarList" :key="item.value">
+              <MenuItem v-bind="{ key: item.value }">
+                {{ item.label }}
+              </MenuItem>
+              <MenuDivider v-if="item.divider" />
+            </template>
           </Menu>
         </template>
       </Dropdown>
@@ -46,6 +49,7 @@
       Dropdown,
       Menu,
       MenuItem: Menu.Item,
+      MenuDivider: Menu.Divider,
       InputSearch: Input.Search,
     },
     props: {
@@ -64,9 +68,9 @@
       const { t } = useI18n();
       const toolbarList = ref([
         { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
-        { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL },
+        { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL, divider: true },
         { label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL },
-        { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL },
+        { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL, divider: true },
         { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
         { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
       ]);

+ 19 - 7
src/components/Tree/src/index.vue

@@ -1,7 +1,7 @@
 <script lang="tsx">
   import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
 
-  import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw } from 'vue';
+  import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw, watch } from 'vue';
   import { Tree } from 'ant-design-vue';
   import { TreeIcon } from './TreeIcon';
   import TreeHeader from './TreeHeader.vue';
@@ -27,6 +27,7 @@
   }
   export default defineComponent({
     name: 'BasicTree',
+    inheritAttrs: false,
     props: basicProps,
     emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change'],
     setup(props, { attrs, slots, emit }) {
@@ -89,8 +90,9 @@
           },
           onCheck: (v: CheckKeys) => {
             state.checkedKeys = v;
-            emit('change', v);
-            emit('update:value', v);
+            const rawVal = toRaw(v);
+            emit('change', rawVal);
+            emit('update:value', rawVal);
           },
           onRightClick: handleRightClick,
         };
@@ -191,11 +193,21 @@
         state.checkedKeys = props.checkedKeys;
       });
 
-      watchEffect(() => {
-        if (props.value) {
-          state.checkedKeys = props.value;
+      watch(
+        () => props.value,
+        () => {
+          state.checkedKeys = toRaw(props.value || []);
         }
-      });
+      );
+
+      // watchEffect(() => {
+      //   console.log('======================');
+      //   console.log(props.value);
+      //   console.log('======================');
+      //   if (props.value) {
+      //     state.checkedKeys = props.value;
+      //   }
+      // });
 
       watchEffect(() => {
         state.checkStrictly = props.checkStrictly;

+ 1 - 17
src/router/menus/modules/demo/system.ts

@@ -7,45 +7,29 @@ const menu: MenuModule = {
     name: t('routes.demo.system.moduleName'),
     path: '/system',
     tag: {
-      dot: true,
+      content: 'new',
     },
     children: [
       {
         path: 'account',
         name: t('routes.demo.system.account'),
-        tag: {
-          dot: true,
-          type: 'warn',
-        },
       },
       {
         path: 'role',
         name: t('routes.demo.system.role'),
-        tag: {
-          dot: true,
-        },
       },
       {
         path: 'menu',
         name: t('routes.demo.system.menu'),
-        tag: {
-          dot: true,
-        },
       },
       {
         path: 'dept',
         name: t('routes.demo.system.dept'),
-        tag: {
-          dot: true,
-        },
       },
 
       {
         path: 'changePassword',
         name: t('routes.demo.system.password'),
-        tag: {
-          dot: true,
-        },
       },
     ],
   },

+ 19 - 3
src/views/demo/system/account/AccountModal.vue

@@ -8,13 +8,16 @@
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import { BasicForm, useForm } from '/@/components/Form/index';
   import { accountFormSchema } from './account.data';
+  import { getDeptList } from '/@/api/demo/system';
+
   export default defineComponent({
     name: 'AccountModal',
     components: { BasicModal, BasicForm },
-    setup() {
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
       const isUpdate = ref(true);
 
-      const [registerForm, { setFieldsValue, validate }] = useForm({
+      const [registerForm, { setFieldsValue, updateSchema, validate }] = useForm({
         labelWidth: 100,
         schemas: accountFormSchema,
         showActionButtonGroup: false,
@@ -23,7 +26,7 @@
         },
       });
 
-      const [registerModal, { setModalProps }] = useModalInner((data) => {
+      const [registerModal, { setModalProps }] = useModalInner(async (data) => {
         setModalProps({ confirmLoading: false });
         isUpdate.value = !!data?.isUpdate;
 
@@ -32,6 +35,18 @@
             ...data.record,
           });
         }
+
+        const treeData = await getDeptList();
+        updateSchema([
+          {
+            field: 'pwd',
+            show: !unref(isUpdate),
+          },
+          {
+            field: 'dept',
+            componentProps: { treeData },
+          },
+        ]);
       });
 
       const getTitle = computed(() => (!unref(isUpdate) ? '新增账号' : '编辑账号'));
@@ -42,6 +57,7 @@
           setModalProps({ confirmLoading: true });
           // TODO custom api
           console.log(values);
+          emit('success');
         } finally {
           setModalProps({ confirmLoading: false });
         }

+ 35 - 7
src/views/demo/system/account/account.data.ts

@@ -1,3 +1,4 @@
+import { getAllRoleList } from '/@/api/demo/system';
 import { BasicColumn } from '/@/components/Table';
 import { FormSchema } from '/@/components/Table';
 
@@ -56,24 +57,51 @@ export const accountFormSchema: FormSchema[] = [
     required: true,
   },
   {
+    field: 'pwd',
+    label: '密码',
+    component: 'InputPassword',
+    required: true,
+    show: true,
+  },
+  {
+    label: '角色',
+    field: 'role',
+    component: 'ApiSelect',
+    componentProps: {
+      api: getAllRoleList,
+      labelField: 'roleName',
+      valueField: 'roleValue',
+    },
+    required: true,
+  },
+  {
+    field: 'dept',
+    label: '所属部门',
+    component: 'TreeSelect',
+    componentProps: {
+      replaceFields: {
+        title: 'deptName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+    required: true,
+  },
+  {
     field: 'nickname',
     label: '昵称',
     component: 'Input',
     required: true,
   },
+
   {
     label: '邮箱',
     field: 'email',
     component: 'Input',
     required: true,
   },
-  // TODO
-  {
-    label: '角色',
-    field: 'role',
-    component: 'Input',
-    required: true,
-  },
+
   {
     label: '备注',
     field: 'remark',

+ 10 - 5
src/views/demo/system/account/index.vue

@@ -2,7 +2,7 @@
   <div :class="[prefixCls]">
     <BasicTable @register="registerTable">
       <template #toolbar>
-        <a-button type="primary" @click="handleCreateAccount"> 新增账号 </a-button>
+        <a-button type="primary" @click="handleCreate"> 新增账号 </a-button>
       </template>
       <template #action="{ record }">
         <TableAction
@@ -23,7 +23,7 @@
         />
       </template>
     </BasicTable>
-    <AccountModal @register="registerModal" />
+    <AccountModal @register="registerModal" @success="handleSuccess" />
   </div>
 </template>
 <script lang="ts">
@@ -45,7 +45,7 @@
       const { prefixCls } = useDesign('account-management');
 
       const [registerModal, { openModal }] = useModal();
-      const [registerTable] = useTable({
+      const [registerTable, { reload }] = useTable({
         title: '账号列表',
         api: getAccountList,
         columns,
@@ -64,7 +64,7 @@
         },
       });
 
-      function handleCreateAccount() {
+      function handleCreate() {
         openModal(true, {
           isUpdate: false,
         });
@@ -81,13 +81,18 @@
         console.log(record);
       }
 
+      function handleSuccess() {
+        reload();
+      }
+
       return {
         prefixCls,
         registerTable,
         registerModal,
-        handleCreateAccount,
+        handleCreate,
         handleEdit,
         handleDelete,
+        handleSuccess,
       };
     },
   });

+ 24 - 9
src/views/demo/system/role/RoleDrawer.vue

@@ -7,7 +7,18 @@
     width="500px"
     @ok="handleSubmit"
   >
-    <BasicForm @register="registerForm" />
+    <BasicForm @register="registerForm">
+      <template #menu="{ model, field }">
+        <BasicTree
+          v-model:value="model[field]"
+          :treeData="treeData"
+          :replaceFields="{ title: 'menuName', key: 'id' }"
+          checkable
+          toolbar
+          title="菜单分配"
+        />
+      </template>
+    </BasicForm>
   </BasicDrawer>
 </template>
 <script lang="ts">
@@ -15,17 +26,19 @@
   import { BasicForm, useForm } from '/@/components/Form/index';
   import { formSchema } from './role.data';
   import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicTree, TreeItem } from '/@/components/Tree';
 
   import { getMenuList } from '/@/api/demo/system';
 
   export default defineComponent({
     name: 'RoleDrawer',
-    components: { BasicDrawer, BasicForm },
+    components: { BasicDrawer, BasicForm, BasicTree },
     emits: ['success', 'register'],
     setup(_, { emit }) {
       const isUpdate = ref(true);
+      const treeData = ref<TreeItem[]>([]);
 
-      const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
+      const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
         labelWidth: 90,
         schemas: formSchema,
         showActionButtonGroup: false,
@@ -41,11 +54,7 @@
             ...data.record,
           });
         }
-        const treeData = await getMenuList();
-        updateSchema({
-          field: 'parentMenu',
-          componentProps: { treeData },
-        });
+        treeData.value = ((await getMenuList()) as any) as TreeItem[];
       });
 
       const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
@@ -63,7 +72,13 @@
         }
       }
 
-      return { registerDrawer, registerForm, getTitle, handleSubmit };
+      return {
+        registerDrawer,
+        registerForm,
+        getTitle,
+        handleSubmit,
+        treeData,
+      };
     },
   });
 </script>

+ 2 - 2
src/views/demo/system/role/index.vue

@@ -30,7 +30,7 @@
   import { defineComponent } from 'vue';
 
   import { BasicTable, useTable, TableAction } from '/@/components/Table';
-  import { getRoleList } from '/@/api/demo/system';
+  import { getRoleListByPage } from '/@/api/demo/system';
 
   import { useDrawer } from '/@/components/Drawer';
   import RoleDrawer from './RoleDrawer.vue';
@@ -44,7 +44,7 @@
       const [registerDrawer, { openDrawer }] = useDrawer();
       const [registerTable, { reload }] = useTable({
         title: '角色列表',
-        api: getRoleList,
+        api: getRoleListByPage,
         columns,
         formConfig: {
           labelWidth: 120,

+ 2 - 3
src/views/demo/system/role/role.data.ts

@@ -2,7 +2,6 @@ import { BasicColumn } from '/@/components/Table';
 import { FormSchema } from '/@/components/Table';
 import { h } from 'vue';
 import { Tag } from 'ant-design-vue';
-
 export const columns: BasicColumn[] = [
   {
     title: '角色名称',
@@ -94,9 +93,9 @@ export const formSchema: FormSchema[] = [
     component: 'InputTextArea',
   },
   {
-    label: '菜单分配',
+    label: ' ',
     field: 'menu',
     slot: 'menu',
-    component: 'Render',
+    component: 'Input',
   },
 ];