From 7f4fd9a7d182b8da1a7130355c718914cc086293 Mon Sep 17 00:00:00 2001
From: zhengyiming <540361168@qq.com>
Date: 星期二, 11 十一月 2025 16:41:55 +0800
Subject: [PATCH] feat: s

---
 src/views/FlexJobManage/FlexJobManage.vue               |   95 ++--
 src/views/Permission/RoleManage.vue                     |  276 ++++++++++++++
 src/views/Permission/components/AddOrEditRoleDialog.vue |  142 +++++++
 src/views/Permission/components/dialogAuthorizeV2.vue   |  356 ++++++++++++++++++
 src/views/CustomerManage/CustomerManage.vue             |   34 
 src/constants/apiEnumText.ts                            |    5 
 .eslintrc-auto-import.json                              |    1 
 src/views/EmploymentManage/TaskManageList.vue           |   36 +
 auto-imports.d.ts                                       |    2 
 src/router/index.ts                                     |  106 -----
 src/utils/common/tree.ts                                |   26 +
 src/views/UserManage/UserManageList.vue                 |   42 -
 12 files changed, 926 insertions(+), 195 deletions(-)

diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json
index ba6ef6d..4938c3b 100644
--- a/.eslintrc-auto-import.json
+++ b/.eslintrc-auto-import.json
@@ -41,6 +41,7 @@
     "EnumEnterpriseCostType": true,
     "EnumEnterpriseRealMethod": true,
     "EnumEnterpriseType": true,
+    "EnumEnterpriseTypeText": true,
     "EnumEnterpriseWalletAccess": true,
     "EnumEnterpriseWalletAccessText": true,
     "EnumEnterpriseWalletAccessTextForSettle": true,
diff --git a/auto-imports.d.ts b/auto-imports.d.ts
index 77c382f..1cea0e6 100644
--- a/auto-imports.d.ts
+++ b/auto-imports.d.ts
@@ -44,6 +44,7 @@
   const EnumEnterpriseCostType: typeof import('./src/constants/apiEnum')['EnumEnterpriseCostType']
   const EnumEnterpriseRealMethod: typeof import('./src/constants/apiEnum')['EnumEnterpriseRealMethod']
   const EnumEnterpriseType: typeof import('./src/constants/apiEnum')['EnumEnterpriseType']
+  const EnumEnterpriseTypeText: typeof import('./src/constants/apiEnumText')['EnumEnterpriseTypeText']
   const EnumEnterpriseWalletAccess: typeof import('./src/constants/apiEnum')['EnumEnterpriseWalletAccess']
   const EnumEnterpriseWalletAccessText: typeof import('./src/constants/enterpriseWallet')['EnumEnterpriseWalletAccessText']
   const EnumEnterpriseWalletAccessTextForSettle: typeof import('./src/constants/task')['EnumEnterpriseWalletAccessTextForSettle']
@@ -373,6 +374,7 @@
     readonly EnumEnterpriseCostType: UnwrapRef<typeof import('./src/constants/apiEnum')['EnumEnterpriseCostType']>
     readonly EnumEnterpriseRealMethod: UnwrapRef<typeof import('./src/constants/apiEnum')['EnumEnterpriseRealMethod']>
     readonly EnumEnterpriseType: UnwrapRef<typeof import('./src/constants/apiEnum')['EnumEnterpriseType']>
+    readonly EnumEnterpriseTypeText: UnwrapRef<typeof import('./src/constants/apiEnumText')['EnumEnterpriseTypeText']>
     readonly EnumEnterpriseWalletAccess: UnwrapRef<typeof import('./src/constants/apiEnum')['EnumEnterpriseWalletAccess']>
     readonly EnumEnterpriseWalletAccessText: UnwrapRef<typeof import('./src/constants/enterpriseWallet')['EnumEnterpriseWalletAccessText']>
     readonly EnumEnterpriseWalletAccessTextForSettle: UnwrapRef<typeof import('./src/constants/task')['EnumEnterpriseWalletAccessTextForSettle']>
diff --git a/src/constants/apiEnumText.ts b/src/constants/apiEnumText.ts
index 392c27d..bee081a 100644
--- a/src/constants/apiEnumText.ts
+++ b/src/constants/apiEnumText.ts
@@ -11,6 +11,11 @@
   [EnumUserType.Operation]: '杩愯惀',
 };
 
+export const EnumEnterpriseTypeText = {
+  [EnumEnterpriseType.Supplier]: '鎵挎徑',
+  [EnumEnterpriseType.PartyA]: '鐢叉柟',
+};
+
 export const EnumRoleWebApiDataPowerText = {
   [EnumRoleWebApiDataPower.Custom]: '鑷畾涔�',
   [EnumRoleWebApiDataPower.Creator]: '涓汉鏁版嵁',
diff --git a/src/router/index.ts b/src/router/index.ts
index 8020e3f..908eab2 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -96,84 +96,6 @@
     },
   },
   {
-    path: '/CustomerManage',
-    redirect: 'noRedirect',
-    component: Layout,
-    hidden: false,
-    alwaysShow: true,
-    meta: {
-      rank: 10010,
-      title: '瀹㈡埛绠$悊',
-      rootMenu: true,
-      icon: 'home',
-    },
-    children: [
-      {
-        path: '/CustomerManage',
-        name: 'CustomerManage',
-        hidden: false,
-        alwaysShow: true,
-        component: () => import('@/views/CustomerManage/CustomerManage.vue'),
-        meta: {
-          rank: 10011,
-          title: '瀹㈡埛绠$悊',
-          // rootMenu: true,
-          icon: 'home',
-        },
-      },
-      {
-        path: '/AddOrEditCustomer/:id?',
-        name: 'AddOrEditCustomer',
-        hidden: true,
-        alwaysShow: false,
-        component: () => import('@/views/CustomerManage/AddOrEditCustomer.vue'),
-        meta: {
-          rank: 10011,
-          title: '鏂板瀹㈡埛',
-        },
-      },
-      {
-        path: '/CustomerDetail/:id',
-        name: 'CustomerDetail',
-        hidden: true,
-        alwaysShow: false,
-        component: () => import('@/views/CustomerManage/CustomerDetail.vue'),
-        meta: {
-          rank: 10011,
-          title: '瀹㈡埛璇︽儏',
-        },
-      },
-    ],
-  },
-  {
-    path: '/FlexJobManage',
-    redirect: 'noRedirect',
-    component: Layout,
-    hidden: false,
-    alwaysShow: true,
-    meta: {
-      rank: 10010,
-      title: '鐏靛伐绠$悊',
-      rootMenu: true,
-      icon: 'home',
-    },
-    children: [
-      {
-        path: '/FlexJobManageList',
-        name: 'FlexJobManageList',
-        hidden: false,
-        alwaysShow: true,
-        component: () => import('@/views/FlexJobManage/FlexJobManage.vue'),
-        meta: {
-          rank: 10011,
-          title: '鐏靛伐绠$悊',
-          // rootMenu: true,
-          icon: 'home',
-        },
-      },
-    ],
-  },
-  {
     path: '/EmploymentManage',
     redirect: 'noRedirect',
     component: Layout,
@@ -484,34 +406,6 @@
     ],
   },
 
-  {
-    path: '/UserManage',
-    redirect: 'noRedirect',
-    component: Layout,
-    hidden: false,
-    alwaysShow: true,
-    meta: {
-      rank: 10100,
-      title: '鐢ㄦ埛绠$悊',
-      rootMenu: true,
-      icon: 'home',
-    },
-    children: [
-      {
-        path: '/UserManageList',
-        name: 'UserManageList',
-        hidden: false,
-        alwaysShow: true,
-        component: () => import('@/views/UserManage/UserManageList.vue'),
-        meta: {
-          rank: 10101,
-          title: '鐢ㄦ埛绠$悊',
-          // rootMenu: true,
-          icon: 'home',
-        },
-      },
-    ],
-  },
   {
     path: '/Login',
     name: 'Login',
diff --git a/src/utils/common/tree.ts b/src/utils/common/tree.ts
index c1f171c..8f5241f 100644
--- a/src/utils/common/tree.ts
+++ b/src/utils/common/tree.ts
@@ -1,3 +1,5 @@
+import { TreeNodeData } from 'element-plus/es/components/tree/src/tree.type';
+
 export function getTree(
   data: API.ModuleDto[],
   root?: string,
@@ -44,3 +46,27 @@
 
   return result;
 }
+
+type TreeCallback<T extends TreeNodeData, R> = (
+  data: T,
+  index: number,
+  array: T[],
+  parent?: T
+) => R;
+
+export function treeEach<T extends TreeNodeData>(
+  treeData: T[],
+  callback: TreeCallback<T, void>,
+  getChildren: (data: T) => T[],
+  parent?: T
+) {
+  for (let i = 0; i < treeData.length; i++) {
+    const data = treeData[i];
+    callback(data, i, treeData, parent);
+
+    const children = getChildren(data);
+    if (Array.isArray(children)) {
+      treeEach(children, callback, getChildren, data);
+    }
+  }
+}
diff --git a/src/views/CustomerManage/CustomerManage.vue b/src/views/CustomerManage/CustomerManage.vue
index 90acbda..b73db10 100644
--- a/src/views/CustomerManage/CustomerManage.vue
+++ b/src/views/CustomerManage/CustomerManage.vue
@@ -32,14 +32,16 @@
           </QueryFilterItem>
         </template>
         <template #btn>
-          <el-button @click="goAddOrEdit()" icon="Plus" type="primary">鏂板瀹㈡埛</el-button>
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'addBtn')"
+            @click="goAddOrEdit()"
+            icon="Plus"
+            type="primary"
+            >鏂板瀹㈡埛</el-button
+          >
         </template>
       </ProTableQueryFilterBar>
-      <ProTableV2
-        v-bind="proTableProps"
-        :columns="CustomerManageColumns"
-        :operationBtns="operationBtns"
-      >
+      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
       </ProTableV2>
     </AppContainer>
   </LoadingLayout>
@@ -69,26 +71,22 @@
   name: 'CustomerManage',
 });
 
-const operationBtns = defineOperationBtns([
-  {
-    data: {
-      enCode: 'detailBtn',
-      name: '鏌ョ湅',
-    },
+const operationBtnMap: Record<string, OperationBtnType> = {
+  detailBtn: {
     emits: {
       onClick: (role) => goDetail(role),
     },
   },
-  {
-    data: {
-      enCode: 'editBtn',
-      name: '缂栬緫',
-    },
+  editBtn: {
     emits: {
       onClick: (role) => goAddOrEdit(role),
     },
   },
-]);
+};
+
+const { checkSubModuleItemShow, column, operationBtns } = useAccess({
+  operationBtnMap,
+});
 
 const BaseState = {
   loading: true,
diff --git a/src/views/EmploymentManage/TaskManageList.vue b/src/views/EmploymentManage/TaskManageList.vue
index e015140..e3a9b20 100644
--- a/src/views/EmploymentManage/TaskManageList.vue
+++ b/src/views/EmploymentManage/TaskManageList.vue
@@ -87,6 +87,42 @@
   name: 'TaskManageList',
 });
 
+const operationBtnMap: Record<string, OperationBtnType> = {
+  // editBtn: {
+  //   emits: {
+  //     onClick: (role) => goAddOrEdit(role),
+  //   },
+  //   extraProps: {
+  //     hide: (row: API.GetTaskInfosQueryResultItem) => row.status === EnumTaskStatus.Complete,
+  //   },
+  // },
+  detailBtn: {
+    emits: {
+      onClick: (row: API.GetTaskInfosQueryResultItem) => goDetail(row),
+    },
+  },
+  // publishBtn: {
+  //   emits: {
+  //     onClick: (row: API.GetTaskInfosQueryResultItem) =>
+  //       setTaskInfoReleaseStatus(row, EnumTaskReleaseStatus.InProcess),
+  //   },
+  //   extraProps: {
+  //     hide: (row: API.GetTaskInfosQueryResultItem) =>
+  //       row.releaseStatus === EnumTaskReleaseStatus.InProcess,
+  //   },
+  // },
+  // unPublishBtn: {
+  //   emits: {
+  //     onClick: (row: API.GetTaskInfosQueryResultItem) =>
+  //       setTaskInfoReleaseStatus(row, EnumTaskReleaseStatus.Stopped),
+  //   },
+  //   extraProps: {
+  //     hide: (row: API.GetTaskInfosQueryResultItem) =>
+  //       row.releaseStatus === EnumTaskReleaseStatus.Stopped,
+  //   },
+  // },
+};
+
 const operationBtns = defineOperationBtns([
   // {
   //   data: {
diff --git a/src/views/FlexJobManage/FlexJobManage.vue b/src/views/FlexJobManage/FlexJobManage.vue
index b4522c1..67a3486 100644
--- a/src/views/FlexJobManage/FlexJobManage.vue
+++ b/src/views/FlexJobManage/FlexJobManage.vue
@@ -79,17 +79,48 @@
           </QueryFilterItem>
         </template>
         <template #btn>
-          <el-button @click="handleDownloadTemplate()" type="primary" link>妯℃澘涓嬭浇</el-button>
-          <el-button @click="handleBatchImportAdd()" type="primary">鎵归噺瀵煎叆</el-button>
-          <el-button @click="handleBatchUnSign()" type="primary">鎵归噺瑙g害</el-button>
-          <el-button @click="handleSendShotMessage()" type="primary">鐭俊鍙戦��</el-button>
-          <el-button @click="handleBatchSign()" type="primary">鎵归噺绛剧害</el-button>
-          <el-button @click="handleEnterpriseBatchSign()" type="primary">鎵归噺浼佷笟绛剧害</el-button>
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'importBtn')"
+            @click="handleDownloadTemplate()"
+            type="primary"
+            link
+            >妯℃澘涓嬭浇</el-button
+          >
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'importBtn')"
+            @click="handleBatchImportAdd()"
+            type="primary"
+            >鎵归噺瀵煎叆</el-button
+          >
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'batchUnSignBtn')"
+            @click="handleBatchUnSign()"
+            type="primary"
+            >鎵归噺瑙g害</el-button
+          >
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'sendShotBtn')"
+            @click="handleSendShotMessage()"
+            type="primary"
+            >鐭俊鍙戦��</el-button
+          >
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'batchSignBtn')"
+            @click="handleBatchSign()"
+            type="primary"
+            >鎵归噺绛剧害</el-button
+          >
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'enterpriseBatchSignBtn')"
+            @click="handleEnterpriseBatchSign()"
+            type="primary"
+            >鎵归噺浼佷笟绛剧害</el-button
+          >
         </template>
       </ProTableQueryFilterBar>
       <ProTableV2
         v-bind="proTableProps"
-        :columns="FlexJobManageColumns"
+        :columns="column"
         :operationBtns="operationBtns"
         show-column-check
         ref="proTable"
@@ -123,12 +154,10 @@
   FieldDatePicker,
   FieldRadio,
   FieldSelect,
-  defineOperationBtns,
   useFormDialog,
   UploadUserFile,
   XLSXUtils,
 } from '@bole-core/components';
-import { FlexJobManageColumns } from './constants';
 import { EnumTaskUserHireStatusText, EnumTaskUserSignContractStatusText } from '@/constants';
 import { Message } from '@bole-core/core';
 import { convertApi2FormUrlOnlyOne, downloadFileByUrl, format } from '@/utils';
@@ -145,31 +174,19 @@
   name: 'FlexJobManageList',
 });
 
-const operationBtns = defineOperationBtns([
-  {
-    data: {
-      enCode: 'editBtn',
-      name: '缂栬緫',
-    },
+const operationBtnMap: Record<string, OperationBtnType> = {
+  editBtn: {
     emits: {
       onClick: (role) => openDialog(role),
     },
   },
-  {
-    data: {
-      enCode: 'detailBtn',
-      name: '璇︽儏',
-    },
+  detailBtn: {
     emits: {
       onClick: (role: API.GetEnterpriseEmployeesQueryResultItem) =>
         handleStaffDetailEdit({ id: role.id, tabType: 'info' }),
     },
   },
-  {
-    data: {
-      enCode: 'enterpriseSignBtn',
-      name: '浼佷笟绛剧害',
-    },
+  enterpriseSignBtn: {
     emits: {
       onClick: (role) => handleEnterpriseSign(role),
     },
@@ -181,11 +198,7 @@
         ),
     },
   },
-  {
-    data: {
-      enCode: 'inviteSignBtn',
-      name: '閭�璇风绾�',
-    },
+  inviteSignBtn: {
     emits: {
       onClick: (role) => handleInviteSign(role),
     },
@@ -197,11 +210,7 @@
         ),
     },
   },
-  {
-    data: {
-      enCode: 'unSignBtn',
-      name: '瑙g害',
-    },
+  unSignBtn: {
     emits: {
       onClick: (role) => handleUnSign(role),
     },
@@ -213,17 +222,11 @@
         ),
     },
   },
-  // {
-  //   data: {
-  //     enCode: 'delBtn',
-  //     name: '鍒犻櫎',
-  //   },
-  //   props: { type: 'danger' },
-  //   emits: {
-  //     onClick: (role) => handleDelete(role),
-  //   },
-  // },
-]);
+};
+
+const { checkSubModuleItemShow, column, operationBtns } = useAccess({
+  operationBtnMap,
+});
 
 const router = useRouter();
 
diff --git a/src/views/Permission/RoleManage.vue b/src/views/Permission/RoleManage.vue
new file mode 100644
index 0000000..887b4f1
--- /dev/null
+++ b/src/views/Permission/RoleManage.vue
@@ -0,0 +1,276 @@
+<template>
+  <LoadingLayout :loading="state.loading">
+    <AppContainer>
+      <ProTableQueryFilterBar @on-reset="reset">
+        <template #query>
+          <QueryFilterItem>
+            <SearchInput
+              v-model="extraParamState.queryCondition"
+              style="width: 200px"
+              placeholder="瑙掕壊鍚嶇О"
+              @on-click-search="getList"
+              @keyup.enter="getList()"
+            >
+            </SearchInput>
+          </QueryFilterItem>
+        </template>
+        <template #btn>
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'addBtn')"
+            @click="openDialog()"
+            icon="Plus"
+            type="primary"
+            >鏂板</el-button
+          >
+        </template>
+      </ProTableQueryFilterBar>
+      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
+      </ProTableV2>
+    </AppContainer>
+    <AddOrEditRoleDialog v-bind="dialogProps" />
+    <DialogAuthorizeV2 v-bind="dialogAuthorizeProps" authorizeType="Role" />
+    <!-- <DialogMember v-model:visibleId="rowState.setMemberRoleId" /> -->
+  </LoadingLayout>
+</template>
+
+<script setup lang="ts">
+import {
+  ProTableQueryFilterBar,
+  OperationBtnType,
+  ProTableV2,
+  SearchInput,
+  LoadingLayout,
+  AppContainer,
+  QueryFilterItem,
+  useTable,
+  useFormDialog,
+  FieldRadio,
+} from '@bole-core/components';
+import { useAccess } from '@/hooks';
+import { Message } from '@bole-core/core';
+import AddOrEditRoleDialog from './components/AddOrEditRoleDialog.vue';
+import { EnumUserType } from '@/constants';
+import DialogAuthorizeV2 from './components/dialogAuthorizeV2.vue';
+import * as roleServices from '@/services/api/role';
+
+defineOptions({
+  name: 'RoleManage',
+});
+
+const operationBtnMap: Record<string, OperationBtnType> = {
+  editBtn: { emits: { onClick: (role) => openDialog(role) } },
+  delBtn: { emits: { onClick: (role) => handleDeleteRole(role) }, props: { type: 'danger' } },
+  authorize: { emits: { onClick: (role) => openAuthorizeDialog(role) } },
+  // member: { emits: { onClick: (role) => openMemberDialog(role) } },
+  disabledBtn: {
+    emits: { onClick: (role) => roleEnableOrForbid(role) },
+    props: { type: 'danger' },
+    extraProps: {
+      hide: (row) => row.isDisabled,
+    },
+  },
+  enableBtn: {
+    emits: { onClick: (role) => roleEnableOrForbid(role) },
+    extraProps: {
+      hide: (row) => !row.isDisabled,
+    },
+  },
+};
+
+const { checkSubModuleItemShow, column, operationBtns } = useAccess({
+  operationBtnMap,
+});
+
+const BaseState = {
+  loading: true,
+};
+
+const state = reactive({ ...BaseState });
+
+onMounted(async () => {
+  await getList();
+  state.loading = false;
+});
+
+const {
+  getDataSource: getList,
+  proTableProps,
+  paginationState,
+  extraParamState,
+  reset,
+} = useTable(
+  async ({ pageIndex, pageSize }, extraParamState) => {
+    try {
+      let params: API.GetRolesQuery = {
+        pageModel: {
+          rows: pageSize,
+          page: pageIndex,
+          orderInput: extraParamState.orderInput,
+        },
+        userType: AppLocalConfig.userType,
+        clientType: AppLocalConfig.clientType,
+        enterpriseType: AppLocalConfig.enterpriseType,
+        keywords: extraParamState.queryCondition,
+      };
+      let res = await roleServices.getRoles(params, {
+        showLoading: !state.loading,
+      });
+      return res;
+    } catch (error) {}
+  },
+  {
+    defaultExtraParams: {
+      queryCondition: '',
+      orderInput: [{ property: 'id', order: EnumPagedListOrder.Desc }],
+    },
+    queryKey: ['roleServices/getRoles'],
+    columnsRenderProps: {
+      dataPower: { type: 'enum', valueEnum: EnumRoleWebApiDataPowerText },
+    },
+  }
+);
+
+async function openDialog(row?: API.GetRolesQueryResultItem) {
+  try {
+    if (row) {
+      const detail = await roleServices.getRole({ id: row.id });
+      handleEdit({
+        id: row.id,
+        name: row.name,
+        remark: row.remark,
+        userType: row.userType,
+        clientType: row.clientType,
+        dataRange: row.dataPower,
+        detail: detail,
+        minLevel: row.minLevel,
+        enterpriseType: row.enterpriseType,
+      });
+    } else {
+      handleAdd({
+        userType: AppLocalConfig.userType,
+        clientType: AppLocalConfig.clientType,
+      });
+    }
+  } catch (error) {}
+}
+
+const { dialogProps, handleAdd, handleEdit, editForm, dialogState } = useFormDialog({
+  onConfirm: handleAddOrEdit,
+  defaultFormParams: {
+    id: '',
+    name: '',
+    remark: '',
+    userType: AppLocalConfig.userType,
+    clientType: AppLocalConfig.clientType,
+    dataRange: EnumRoleWebApiDataPower.All,
+    detail: null as API.GetRoleQueryResult,
+    minLevel: 1,
+    enterpriseType: AppLocalConfig.enterpriseType,
+  },
+});
+
+async function handleAddOrEdit() {
+  try {
+    const isEdit = editForm.id;
+    let params: API.SaveRoleCommand = {
+      name: editForm.name,
+      remark: editForm.remark,
+      dataPower: editForm.dataRange,
+      userType: editForm.userType,
+      clientType: editForm.clientType,
+      minLevel: editForm.minLevel,
+    };
+    if (editForm.userType === EnumUserType.Enterprise) {
+      params.enterpriseType = editForm.enterpriseType;
+    }
+    if (isEdit) {
+      params = {
+        ...editForm.detail,
+        ...params,
+      };
+    }
+    let res = await roleServices.saveRole(params);
+    if (res) {
+      Message.successMessage('鎿嶄綔鎴愬姛');
+      getList(isEdit ? paginationState.pageIndex : 1);
+    }
+  } catch (error) {}
+}
+
+async function handleDeleteRole(row: API.GetRolesQueryResultItem) {
+  try {
+    await Message.deleteMessage();
+    let params: API.DeleteRoleCommand = {
+      ids: [row.id],
+    };
+    let res = await roleServices.deleteRole(params);
+    if (res) {
+      Message.successMessage('鎿嶄綔鎴愬姛');
+      getList(paginationState.pageIndex);
+    }
+  } catch (error) {}
+}
+
+async function roleEnableOrForbid(row: API.GetRolesQueryResultItem) {
+  try {
+    await Message.tipMessage(`鏄惁${row.isDisabled ? '鍚敤' : '绂佺敤'}瑙掕壊`);
+    let res = await roleServices.setRoleIsDisabled({
+      ids: [row.id],
+      isDisabled: !row.isDisabled,
+    });
+    if (res) {
+      Message.successMessage('鎿嶄綔鎴愬姛');
+      getList(paginationState.pageIndex);
+      return !!res;
+    }
+  } catch (error) {}
+}
+
+const rowState = reactive({
+  authorizeId: '',
+  setMemberRoleId: '',
+});
+
+const {
+  dialogProps: dialogAuthorizeProps,
+  handleAdd: handleAuthorizeAdd,
+  editForm: authorizeForm,
+} = useFormDialog({
+  onConfirm: handleAuthorize,
+  defaultFormParams: {
+    detail: null as API.GetRoleQueryResult,
+  },
+});
+
+async function openAuthorizeDialog(row: API.GetRolesQueryResultItem) {
+  try {
+    const detail = await roleServices.getRole({ id: row.id });
+    handleAuthorizeAdd({
+      detail: detail,
+    });
+  } catch (error) {}
+}
+
+async function handleAuthorize(selectedMenuIds: string[]) {
+  console.log('selectedMenuIds: ', selectedMenuIds);
+  try {
+    let params: API.SaveRoleCommand = {
+      ...authorizeForm.detail,
+      menuIds: selectedMenuIds,
+      // resources: resourceIds.map((x) => ({
+      //   resourceId: x,
+      //   dataPower: EnumRoleWebApiDataPower.All,
+      // })),
+    };
+    let res = await roleServices.saveRole(params);
+    if (res) {
+      Message.successMessage('鎿嶄綔鎴愬姛');
+      getList(paginationState.pageIndex);
+    }
+  } catch (error) {}
+}
+
+// function openMemberDialog(row: API.IdentityRoleDto) {
+//   rowState.setMemberRoleId = row.id;
+// }
+</script>
diff --git a/src/views/Permission/components/AddOrEditRoleDialog.vue b/src/views/Permission/components/AddOrEditRoleDialog.vue
new file mode 100644
index 0000000..023f3bc
--- /dev/null
+++ b/src/views/Permission/components/AddOrEditRoleDialog.vue
@@ -0,0 +1,142 @@
+<template>
+  <ProDialog
+    :title="innerForm.title"
+    v-model="innerVisible"
+    @close="onDialogClose"
+    destroy-on-close
+    draggable
+  >
+    <ProForm :rules="rules" :model="innerForm" ref="dialogForm" label-width="110px">
+      <ProFormItemV2 label="瑙掕壊鍚嶇О" prop="name">
+        <ProFormText placeholder="璇疯緭鍏ヨ鑹插悕绉�" v-model.trim="innerForm.name"></ProFormText>
+      </ProFormItemV2>
+      <ProFormItemV2 label="绛夌骇" prop="minLevel">
+        <ProFormInputNumber
+          v-model="innerForm.minLevel"
+          :min="1"
+          :max="100"
+          :controls="false"
+        ></ProFormInputNumber>
+      </ProFormItemV2>
+      <!-- <ProFormItemV2
+        label="浼佷笟绫诲瀷"
+        prop="enterpriseType"
+        v-if="form.userType === EnumUserType.Enterprise"
+      >
+        <ProFormRadio
+          v-model="form.enterpriseType"
+          :value-enum="EnumEnterpriseTypeText"
+          :buttonStyle="false"
+        ></ProFormRadio>
+      </ProFormItemV2> -->
+      <ProFormItemV2 label="鏁版嵁鍙鑼冨洿" prop="dataRange">
+        <ProFormRadio
+          v-model="form.dataRange"
+          :value-enum="EnumRoleWebApiDataPowerTextForFilter"
+          :buttonStyle="false"
+        ></ProFormRadio>
+      </ProFormItemV2>
+      <ProFormItemV2 label="澶囨敞:" prop="remark">
+        <ProFormTextArea
+          v-model="innerForm.remark"
+          placeholder="璇疯緭鍏ュ娉�"
+          show-word-limit
+          :maxlength="2000"
+        ></ProFormTextArea>
+      </ProFormItemV2>
+    </ProForm>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="emit('onCancel')">鍙� 娑�</el-button>
+        <el-button type="primary" @click="handleConfirm">纭� 瀹�</el-button>
+      </span>
+    </template>
+  </ProDialog>
+</template>
+
+<script setup lang="ts">
+import { FormRules, FormInstance } from 'element-plus';
+import {
+  ProDialog,
+  ProForm,
+  ProFormItemV2,
+  ProFormText,
+  ProFormRadio,
+  ProFormTextArea,
+  ProFormInputNumber,
+} from '@bole-core/components';
+import {
+  EnumRoleWebApiDataPowerTextForFilter,
+  EnumEnterpriseTypeText,
+  EnumUserType,
+} from '@/constants';
+
+defineOptions({
+  name: 'AddOrEditRoleDialog',
+});
+
+type Props = {
+  modelValue: boolean;
+  form: {
+    id: string;
+    title?: string;
+    name: string;
+    remark: string;
+    dataRange: EnumRoleWebApiDataPower;
+    minLevel: number;
+    enterpriseType: EnumEnterpriseType;
+    userType: EnumUserType;
+  };
+};
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false,
+});
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'update:form', value: Props['form']): void;
+  (e: 'onConfirm'): void;
+  (e: 'onCancel'): void;
+}>();
+
+const dialogForm = ref<FormInstance>();
+
+const innerVisible = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(val) {
+    emit('update:modelValue', val);
+  },
+});
+
+const innerForm = computed({
+  get() {
+    return props.form;
+  },
+  set(val) {
+    emit('update:form', val);
+  },
+});
+
+const rules = reactive<FormRules>({
+  name: [{ required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur' }],
+});
+
+function onDialogClose() {
+  if (!dialogForm.value) return;
+  dialogForm.value.resetFields();
+}
+
+function handleConfirm() {
+  if (!dialogForm.value) return;
+  dialogForm.value.validate((valid) => {
+    if (valid) {
+      emit('onConfirm');
+    } else {
+      return;
+    }
+  });
+}
+</script>
diff --git a/src/views/Permission/components/dialogAuthorizeV2.vue b/src/views/Permission/components/dialogAuthorizeV2.vue
new file mode 100644
index 0000000..71bfb25
--- /dev/null
+++ b/src/views/Permission/components/dialogAuthorizeV2.vue
@@ -0,0 +1,356 @@
+<template>
+  <ProDialog
+    class="custom-dialog"
+    width="55%"
+    :title="dialogTitle"
+    v-model="visible"
+    :top="'10vh'"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+  >
+    <div class="authorize-wrapper">
+      <div class="container-wrapper left-wrapper">
+        <el-scrollbar>
+          <el-tree
+            class="companyTree"
+            :data="menusTree"
+            :props="{
+              children: 'children',
+              label: 'name',
+            }"
+            node-key="id"
+            :expand-on-click-node="false"
+            :highlight-current="true"
+            default-expand-all
+            show-checkbox
+            ref="moduleTree"
+            :default-checked-keys="defaultCheckedKeys"
+            @check="handleModuleCheck"
+          >
+            <template #default="{ node }">
+              <div class="custom-tree-node">
+                <type-tip :isMenu="node.data.type === EnumMenuType.Menu" />
+                <div class="node-text" @click="handleSelectModule(node.data)">
+                  {{ node.label }}
+                </div>
+              </div>
+            </template>
+          </el-tree>
+        </el-scrollbar>
+      </div>
+
+      <div class="container-wrapper">
+        <div class="type-wrapper">
+          {{ SubModuleTitle[SubModuleType.PageButton] }}
+        </div>
+        <el-scrollbar>
+          <el-tree
+            v-show="!!state.currentMenuId"
+            :data="[
+              {
+                name: '鍏ㄩ��',
+                id: 'pageButtonAll',
+                children: menuPageButtons,
+              },
+            ]"
+            :props="{
+              children: 'children',
+              label: 'name',
+            }"
+            node-key="id"
+            :expand-on-click-node="false"
+            :highlight-current="true"
+            default-expand-all
+            show-checkbox
+            ref="pageButtonTree"
+            :default-checked-keys="defaultCheckedKeys"
+            @check="handlePageButtonCheck"
+          >
+            <template #default="{ node }">
+              <div class="custom-tree-node">
+                <span>{{ node.label }}</span>
+              </div>
+            </template>
+          </el-tree>
+        </el-scrollbar>
+      </div>
+
+      <div class="container-wrapper">
+        <div class="type-wrapper">
+          {{ SubModuleTitle[SubModuleType.DataButton] }}
+        </div>
+        <el-scrollbar>
+          <el-tree
+            v-show="!!state.currentMenuId"
+            :data="[
+              {
+                name: '鍏ㄩ��',
+                id: 'dataButtonAll',
+                children: menuDataButtons,
+              },
+            ]"
+            :props="{
+              children: 'children',
+              label: 'name',
+            }"
+            node-key="id"
+            :expand-on-click-node="false"
+            :highlight-current="true"
+            default-expand-all
+            show-checkbox
+            ref="dataButtonTree"
+            :default-checked-keys="defaultCheckedKeys"
+            @check="handleDataButtonCheck"
+          >
+            <template #default="{ node }">
+              <div class="custom-tree-node">
+                <span>{{ node.label }}</span>
+              </div>
+            </template>
+          </el-tree>
+        </el-scrollbar>
+      </div>
+
+      <div class="container-wrapper">
+        <div class="type-wrapper">
+          {{ SubModuleTitle[SubModuleType.Column] }}
+        </div>
+        <el-scrollbar>
+          <el-tree
+            v-show="!!state.currentMenuId"
+            :data="[
+              {
+                name: '鍏ㄩ��',
+                id: 'dataColumnAll',
+                children: menuFields,
+              },
+            ]"
+            :props="{
+              children: 'children',
+              label: 'name',
+            }"
+            node-key="id"
+            :expand-on-click-node="false"
+            :highlight-current="true"
+            default-expand-all
+            show-checkbox
+            ref="dataColumnTree"
+            :default-checked-keys="defaultCheckedKeys"
+            @check="handleDataColumnCheck"
+          >
+            <template #default="{ node }">
+              <div class="custom-tree-node">
+                <span>{{ node.label }}</span>
+              </div>
+            </template>
+          </el-tree>
+        </el-scrollbar>
+      </div>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="visible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleConfirm" class="btn-submit"> 纭畾 </el-button>
+      </span>
+    </template>
+  </ProDialog>
+</template>
+
+<script setup lang="ts">
+import { Message } from '@bole-core/core';
+import { SubModuleType, SubModuleTitle, EnumMenuType } from '@/constants';
+import { getTree, treeEach } from '@/utils';
+import { TreeInstance } from 'element-plus';
+import { ProDialog } from '@bole-core/components';
+import { useMenus, useMenu } from '@/hooks';
+import data from '@iconify-icons/ep/tickets';
+
+const TypeTip = defineComponent({
+  name: 'TypeTip',
+  props: ['isMenu'],
+  render() {
+    const { isMenu } = this;
+    const tipText = isMenu ? `鑿滃崟` : '椤甸潰';
+    return h(
+      'span',
+      {
+        class: 'tip-text',
+        style: {
+          color: '#EB6100',
+        },
+      },
+      '[' + tipText + ']'
+    );
+  },
+});
+
+type Props = {
+  authorizeType: 'Role' | 'User';
+};
+
+const props = withDefaults(defineProps<Props>(), {});
+
+const visible = defineModel({ type: Boolean });
+
+type Form = {
+  title?: string;
+  detail: API.GetRoleQueryResult;
+};
+
+const form = defineModel<Form>('form');
+
+const emit = defineEmits<{
+  (e: 'onConfirm', selectedMenuIds: string[]): void;
+  (e: 'onCancel'): void;
+}>();
+
+type CheckedResourceItem = {
+  resourceId: string;
+  menuId: string;
+  resourceType: SubModuleType;
+};
+
+const state = reactive({
+  currentMenuId: '',
+  selectedMenuIds: [] as string[],
+});
+
+const defaultCheckedKeys = computed(() => Array.from(state.selectedMenuIds));
+
+watch(visible, (newVal) => {
+  if (newVal) {
+    state.currentMenuId = '';
+    const menuIds = form.value.detail?.menuIds ?? [];
+    // menuIds.forEach((id) => state.selectedMenuIds.add(id));
+    state.selectedMenuIds = [...menuIds];
+  }
+});
+
+const dialogTitle = computed(() =>
+  props.authorizeType === 'Role' ? '瑙掕壊鍔熻兘鎺堟潈' : '璐﹀彿鍔熻兘鎺堟潈'
+);
+
+const moduleTree = useTemplateRef<TreeInstance>('moduleTree');
+const pageButtonTree = useTemplateRef<TreeInstance>('pageButtonTree');
+const dataButtonTree = useTemplateRef<TreeInstance>('dataButtonTree');
+const dataColumnTree = useTemplateRef<TreeInstance>('dataColumnTree');
+
+type TreeRef = ReturnType<typeof useTemplateRef<TreeInstance>>;
+
+const { menusTree, getMenuById } = useMenus({
+  params: computed(() => ({
+    userType: form.value.detail?.userType,
+    clientType: form.value.detail?.clientType,
+    enterpriseType: form.value.detail?.enterpriseType,
+    roleId: form.value.detail?.id,
+  })),
+  enabled: computed(() => !!form.value.detail?.id),
+});
+
+const { menuFields, menuPageButtons, menuDataButtons } = useMenu({
+  params: computed(() => ({
+    id: state.currentMenuId,
+    roleId: form.value.detail?.id,
+  })),
+  enabled: computed(() => !!state.currentMenuId),
+});
+
+function handleSelectModule(menu: API.GetMenusQueryResultItem) {
+  state.currentMenuId = menu.id;
+}
+
+function handleModuleCheck(data, params) {
+  handleCheck(data, params, moduleTree);
+}
+
+function handlePageButtonCheck(data, params) {
+  handleCheck(data, params, pageButtonTree);
+}
+
+function handleDataButtonCheck(data, params) {
+  handleCheck(data, params, dataButtonTree);
+}
+
+function handleDataColumnCheck(data, params) {
+  handleCheck(data, params, dataColumnTree);
+}
+
+function handleCheck(data, params, treeRef: TreeRef) {
+  const dataMap = {};
+  treeEach(
+    [treeRef.value.store.root],
+    (node) => (dataMap[node.key] = node),
+    (node) => node.childNodes
+  );
+
+  const uncachedCheckedKeys = params.checkedKeys.filter(
+    (item) => !['pageButtonAll', 'dataButtonAll', 'dataColumnAll'].includes(item)
+  );
+  const cachedKeys = state.selectedMenuIds.filter(
+    (item) => !(item in dataMap) && !uncachedCheckedKeys.includes(item)
+  );
+  state.selectedMenuIds = cachedKeys.concat(uncachedCheckedKeys);
+}
+
+function handleConfirm() {
+  emit('onConfirm', state.selectedMenuIds);
+}
+</script>
+
+<style lang="scss" scoped>
+:deep(.custom-dialog) {
+  min-width: 900px;
+}
+
+.authorize-wrapper {
+  display: flex;
+
+  height: 500px;
+  border-bottom: 2px solid #f5f5f5;
+  background: #dddddd;
+
+  .container-wrapper {
+    width: calc(25%);
+    border-right: 2px solid #f5f5f5;
+    // margin-right: 7px;
+    background: #ffffff;
+
+    .type-wrapper {
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      padding: 10px 16px;
+      border-bottom: 2px solid #f5f5f5;
+      color: #333333;
+    }
+
+    &:last-of-type {
+      margin-right: 0px;
+    }
+
+    &.left-wrapper {
+      margin-right: 7px;
+      padding-top: 10px;
+      width: calc(35% - 7px);
+
+      :deep(.el-scrollbar) {
+        width: 100%;
+        height: 98%;
+      }
+    }
+
+    :deep(.el-scrollbar) {
+      width: 100%;
+      height: calc(98% - 43px);
+
+      .el-scrollbar__wrap {
+        overflow: auto;
+
+        .custom-tree-node {
+          display: flex;
+        }
+      }
+    }
+  }
+}
+</style>
diff --git a/src/views/UserManage/UserManageList.vue b/src/views/UserManage/UserManageList.vue
index 10d42bb..2402aa5 100644
--- a/src/views/UserManage/UserManageList.vue
+++ b/src/views/UserManage/UserManageList.vue
@@ -14,14 +14,16 @@
           </QueryFilterItem>
         </template>
         <template #btn>
-          <el-button @click="openDialog()" icon="Plus" type="primary">鏂板</el-button>
+          <el-button
+            v-if="checkSubModuleItemShow('pageButton', 'addBtn')"
+            @click="openDialog()"
+            icon="Plus"
+            type="primary"
+            >鏂板</el-button
+          >
         </template>
       </ProTableQueryFilterBar>
-      <ProTableV2
-        v-bind="proTableProps"
-        :columns="UserManageColumns"
-        :operationBtns="operationBtns"
-      >
+      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
         <template #status="{ row }">
           <FieldSwitch
             v-model="row.status"
@@ -52,11 +54,9 @@
   QueryFilterItem,
   useTable,
   useFormDialog,
-  defineOperationBtns,
   FieldSwitch,
 } from '@bole-core/components';
 import * as userServices from '@/services/api/user';
-import { UserManageColumns } from './constants';
 import { EnumUserStatus, EnumUserStatusText } from '@/constants';
 import { ModelValueType } from 'element-plus';
 import { Message } from '@bole-core/core';
@@ -68,21 +68,13 @@
   name: 'UserManageList',
 });
 
-const operationBtns = defineOperationBtns([
-  {
-    data: {
-      enCode: 'editBtn',
-      name: '缂栬緫',
-    },
+const operationBtnMap: Record<string, OperationBtnType> = {
+  editBtn: {
     emits: {
       onClick: (role) => openDialog(role),
     },
   },
-  {
-    data: {
-      enCode: 'resetPasswordBtn',
-      name: '閲嶇疆瀵嗙爜',
-    },
+  resetPasswordBtn: {
     props: {
       type: 'danger',
     },
@@ -90,11 +82,7 @@
       onClick: (role) => openResetPasswordDialog(role),
     },
   },
-  {
-    data: {
-      enCode: 'resetOperatorPasswordBtn',
-      name: '閲嶇疆鎿嶄綔瀵嗙爜',
-    },
+  resetOperatorPasswordBtn: {
     props: {
       type: 'danger',
     },
@@ -102,7 +90,11 @@
       onClick: (role) => openResetOperatorPasswordDialog(role),
     },
   },
-]);
+};
+
+const { checkSubModuleItemShow, column, operationBtns } = useAccess({
+  operationBtnMap,
+});
 
 const router = useRouter();
 const BaseState = {

--
Gitblit v1.9.1