Explorar el Código

Merge branch 'master' of http://gitlab.smartai.com/zhupei/smartai2dian0

hanyang hace 1 año
padre
commit
45e96a1e99

+ 131 - 21
src/modules/permission/account/DeptConfigModal.vue

@@ -1,36 +1,146 @@
 <template>
-  <a-modal width="50%" v-model:visible="deptvisible" title="部门配置" @cancel="handleCancel(2)" @ok="editDeptHandleOk">
+  <a-modal width="50%" v-model:visible="visible" title="部门配置" @cancel="handleCancel" @ok="editDeptHandleOk">
     <div :style="{ display: 'flex' }">
-      <a-card :style="{ 'width': '460px', 'height': '500px', 'overflow-y': 'auto' }" title="机构" hoverable>
-        <a-tree class="tree-demo" v-model:checked-keys="checkedKeys" v-model:expanded-keys="expandKdys"
-          :checkable="true" :data="treeData" :show-line="showLine" @check="onCheck" :fieldNames="{
+      <a-card class="card-body" title="机构" hoverable>
+        <a-tree :data="dept_list" v-model:checked-keys="checked_keys" v-model:expanded-keys="expand_kdys"
+          :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
             key: 'deptId',
             title: 'deptName',
             children: 'children',
           }">
         </a-tree>
       </a-card>
-      <div :style="{ 'display': 'flex', 'flex-direction': 'column' }">
-        <a-card class="card-demo" title="用户所属部门" :style="{ 'width': '460px', 'height': '500px', 'overflow-y': 'auto' }">
-          <a-space wrap>
-            <a-tag v-for="(tag, index) of checkStrictly" :key="tag.deptId">
-              {{ tag.deptName }}
-            </a-tag>
-          </a-space>
-          <a-divider />
-          <a-space wrap>
-            部门角色:
-            <a-tag v-for="(tag, index) of checkStrictlyDeptRole" :key="tag.roleId">
-              {{ tag.roleName }}
-            </a-tag>
-          </a-space>
-        </a-card>
-      </div>
+      <a-card class="card-body" title="用户所属部门">
+        <a-space wrap>
+          <a-tag v-for="(tag, index) of check_strictly" :key="tag.deptId">
+            {{ tag.deptName }}
+          </a-tag>
+        </a-space>
+        <a-divider />
+        <div style="margin-bottom: 8px;">部门角色:</div>
+        <a-space wrap>
+          <a-tag v-for="(tag, index) of check_strictly_dept_role" :key="tag.roleId" color="arcoblue">
+            {{ tag.roleName }}
+          </a-tag>
+        </a-space>
+      </a-card>
     </div>
   </a-modal>
 </template>
 
 <script setup>
+import { ref, toRefs, onMounted } from 'vue';
+
+// 定义组件的 props 和 emits
+const props = defineProps({
+  visible: Boolean,
+  dept_list: {
+    type: Array,
+    default: () => []
+  },
+  record: {
+    type: Object,
+    default: false
+  }
+});
+
+const { visible, dept_list, record } = toRefs(props);
+
+const emit = defineEmits(['close', 'ok']);
+
+// 组件内部状态
+const checked_keys = ref([]);
+// 默认展开第一节
+const expand_kdys = ref(['0']);
+const check_strictly = ref([]);
+const check_strictly_dept_role = ref([]);
+
+// 递归取消所有子节点的选择
+const cancelChildSelection = (node, selected_nodes) => {
+  if (node.children && node.children.length > 0) {
+    node.children.forEach(child => {
+      const child_index = selected_nodes.findIndex(val => val.deptId === child.deptId);
+      if (child_index !== -1) {
+        // 取消子节点的选择
+        selected_nodes.splice(child_index, 1);
+        // 递归取消子节点的选择
+        cancelChildSelection(child, selected_nodes);
+      }
+    });
+  }
+}
+
+// 处理树形组件的勾选变化
+const onCheck = (new_checked_keys, event) => {
+  const o = { deptId: event.node.deptId, deptName: event.node.deptName };
+  if (event.checked) {
+    // 添加部门角色
+    event.node.roles.forEach((val) => {
+      check_strictly_dept_role.value.push(val);
+    });
+    // 添加部门
+    check_strictly.value.push(o);
+  } else {
+    // 移除部门
+    const dept_index = check_strictly.value.findIndex((val) => val.deptId === event.node.deptId);
+    check_strictly.value.splice(dept_index, 1);
+    // 移除部门角色
+    const role_index = check_strictly_dept_role.value.findIndex((val) => val.deptId === event.node.deptId);
+    check_strictly_dept_role.value.splice(role_index, 1);
+    // 递归取消所有子节点的选择
+    cancelChildSelection(event.node, check_strictly.value);
+  }
+};
+
+// 处理取消事件
+const handleCancel = () => {
+  visible.value = false;
+  emit('close');
+};
+
+// 处理确认事件
+const editDeptHandleOk = () => {
+  emit('ok', { checked_keys: checked_keys.value });
+};
+
+onMounted(() => {
+  // 处理传入的部门数据
+  if (record?.dept) {
+    record.dept.forEach((val) => {
+      check_strictly.value.push({
+        deptId: val.deptId,
+        deptName: val.deptName
+      });
+      checked_keys.value.push(val.deptId);
+      expand_kdys.value.push(val.deptId);
+      record.roles.forEach(
+        (r) => {
+          if (r.dept) {
+            r.dept.forEach((d) => {
+              if (d.deptId == val.deptId) {
+                check_strictly_dept_role.value.push({
+                  roleId: r.roleId,
+                  roleName: r.roleName,
+                  deptId: d.deptId,
+                });
+              }
+            });
+          }
+        }
+      );
+    });
+  }
+});
 </script>
 
-<style lang="css" scoped></style>
+<style lang="css" scoped>
+.card-body {
+  width: 460px;
+  height: 500px;
+  overflow-y: auto;
+}
+
+.card-body:first-child {
+  margin-right: 16px;
+}
+</style>

+ 4 - 4
src/modules/permission/account/EditModal.vue

@@ -1,5 +1,5 @@
 <template>
-  <a-modal v-model:visible="visible" :title="title" @ok="editHandleOk" @cancel="editHandleCancel" width="48%">
+  <a-modal v-model:visible="visible" :title="title" @ok="ok" @cancel="editHandleCancel" width="48%">
     <a-form ref="formRef" :model="edit_form" auto-label-width>
       <a-row :gutter="20">
         <a-col :span="10">
@@ -62,15 +62,15 @@ const props = defineProps({
 });
 
 // 事件发射器
-const emit = defineEmits(['save', 'cancel']);
+const emit = defineEmits(['ok', 'cancel']);
 
 const { edit_form, visible, title, roles, field_names } = toRefs(props);
 
 // 处理确认事件
-const editHandleOk = () => {
+const ok = () => {
   // 进行表单验证和提交逻辑
   // 根据需要增加表单验证逻辑
-  emit('save', edit_form);
+  emit('ok', edit_form);
 }
 
 const editHandleCancel = () => {

+ 26 - 18
src/modules/permission/account/ViewPermission.vue

@@ -1,28 +1,28 @@
 <template>
-  <a-modal width="50%" v-model:visible="visible" title="用户所有权限" okText="关闭" hide-cancel="true" @ok="handleOk">
+  <a-modal width="50%" v-model:visible="visible" title="用户权限" okText="关闭" hide-cancel="true" @ok="handleOk">
     <div :style="{ 'display': 'flex', 'flex-direction': 'column' }">
-      <a-card class="card-demo" title="用户所有权限" hoverable>
+      <a-card class="card-body" hoverable>
+        <div class="title-box"><icon-menu size="18" />菜单功能:</div>
         <a-space wrap>
-          菜单功能:
-          <a-tag v-for="(tag, index) in menu_permissions_list" :key="tag.menuId">
-            {{ tag.menuName }}
+          <a-tag v-for="item in menu_permissions_list" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
         <a-divider />
+        <div class="title-box"><icon-bookmark size="18" />知识库:</div>
         <a-space wrap>
-          知识库:
-          <a-tag v-for="(tag, index) in knowledge_permissions_list" :key="tag.knowledgeId">
-            {{ tag.knowledgeName }}
+          <a-tag v-for="item in knowledge_permissions_list" color="arcoblue" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
         <a-divider />
+        <div class="title-box"><icon-relation size="18" /> 智能体:</div>
         <a-space wrap>
-          智能体:
-          <a-tag v-for="(tag, index) in dialog_permissions_list" :key="tag.dialogId">
-            {{ tag.dialogName }}
+          <a-tag v-for="item in dialog_permissions_list" color="green" :key="item.id">
+            {{ item.name }}
           </a-tag>
-          <a-tag v-for="(tag, index) in agent_permissions_list" :key="tag.agentId">
-            {{ tag.agentName }}
+          <a-tag v-for="item in agent_permissions_list" color="green" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
       </a-card>
@@ -34,10 +34,7 @@
 import { toRefs } from 'vue';
 // 定义Props,接收外部传入的数据
 const props = defineProps({
-  visible: {
-    type: Boolean,
-    required: true
-  },
+  visible: Boolean,
   menu_permissions_list: {
     type: Array,
   },
@@ -67,7 +64,7 @@ const handleOk = () => {
 </script>
 
 <style lang="css" scoped>
-.card-demo {
+.card-body {
   width: 100%;
   margin-left: 24px;
   transition-property: all;
@@ -75,4 +72,15 @@ const handleOk = () => {
   overflow-y: auto;
   margin: 1px;
 }
+
+.title-box {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  margin-bottom: 12px;
+}
+
+.title-box>svg {
+  margin-right: 6px;
+}
 </style>

+ 183 - 22
src/modules/permission/account/index.vue

@@ -11,9 +11,9 @@
         <a-popconfirm content="确定重置该账号密码吗?" @ok="handleResetPassword(record)">
           <a-button type="text">重制密码</a-button>
         </a-popconfirm>
-        <a-button type="text" @click="handleEdit(record)">编辑</a-button>
-        <a-button type="text" @click="handleView(record)">查看权限</a-button>
-        <a-button type="text" @click="handleDeptConfig(record)">部门配置</a-button>
+        <a-button type="text" @click="handleEdit(record, true)">编辑</a-button>
+        <a-button type="text" @click="handleView(record, true)">查看权限</a-button>
+        <a-button type="text" @click="handleDeptConfig(record, true)">部门配置</a-button>
         <a-popconfirm content="确定删除该条数据吗?" type="warning" @ok="handleDelete(record)">
           <a-button type="text" status="warning">删除</a-button>
         </a-popconfirm>
@@ -21,13 +21,17 @@
     </DataTable>
   </a-card>
 
-  <EditModal :visible.sync="edit_visible" @save="editModalSave" @cancel="edit_visible = false" :title="'编辑'"
-    :edit_form="edit_form" />
+  <EditModal :visible.sync="edit_visible" :edit_form="edit_form" @ok="editModalSave" @cancel="handleEdit(null, false)"
+    :title="'编辑'" />
 
-  <ViewPermission :visible.sync="view_permission_visible" @cancel="view_permission_visible = false"
-    :view_form="view_form" :menu_permissions_list="menu_permissions_list"
+  <ViewPermission :visible.sync="view_permission_visible" @cancel="handleView(null, false)"
+    @close="handleView(null, false)" :view_form="view_form" :menu_permissions_list="menu_permissions_list"
     :knowledge_permissions_list="knowledge_permissions_list" :dialog_permissions_list="dialog_permissions_list"
     :agent_permissions_list="agent_permissions_list" />
+
+  <DeptConfigModal :visible.sync="dept_config_visible" :dept_list="dept_list" :record="current_record" @ok=""
+    @cancel="handleDeptConfig(null, false)" />
+
 </template>
 
 <script setup>
@@ -37,7 +41,9 @@ import DataTable from '../../../views/permission/DataTable.vue';
 import Action from '../../../views/permission/Action.vue';
 import EditModal from './EditModal.vue';
 import ViewPermission from './ViewPermission.vue';
+import DeptConfigModal from './DeptConfigModal.vue';
 
+const current_record = ref(null);
 // 表格相关
 const table_data = ref([
   { id: 1, name: '李雷', age: 25, status: true },
@@ -69,12 +75,159 @@ const edit_form = reactive({
   role: []
 });
 
+
+
 // 查看权限相关
 const view_permission_visible = ref(false);
-const menu_permissions_list = ref([])
-const knowledge_permissions_list = ref([])
-const dialog_permissions_list = ref([])
-const agent_permissions_list = ref([])
+const menu_permissions_list = ref([
+  { name: '智能体设置', id: 1 },
+  { name: 'root', id: 2 },
+  { name: '智能体列表', id: 3 },
+  { name: '会话记录', id: 4 },
+])
+const knowledge_permissions_list = ref([
+  { name: '知识库管理', id: 1 },
+  { name: '知识库列表', id: 2 },
+  { name: '知识库分类', id: 3 },
+])
+const dialog_permissions_list = ref([
+  { name: '对话管理', id: 1 },
+  { name: '对话列表', id: 2 },
+  { name: '对话分类', id: 3 },
+])
+const agent_permissions_list = ref([
+  { name: '智能体管理', id: 1 },
+  { name: '智能体列表', id: 2 },
+  { name: '智能体分类', id: 3 },
+])
+
+// 部门配置相关
+const dept_config_visible = ref(false);
+const dept_list = ref([
+  {
+    "address": null,
+    "children": [
+      {
+        "address": "",
+        "children": [],
+        "code": null,
+        "createTime": "Thu, 12 Sep 2024 14:56:43 GMT",
+        "deptId": "2af7cb5e-1178-4adc-8abb-9780f7543081",
+        "deptName": "成都",
+        "email": "",
+        "iconCls": null,
+        "leader": "成都",
+        "orderNum": 0,
+        "parentId": "0",
+        "parentName": "总部",
+        "phone": "成都",
+        "roles": [],
+        "status": "",
+        "updateTime": "Thu, 12 Sep 2024 14:56:43 GMT"
+      },
+      {
+        "address": null,
+        "children": [],
+        "code": null,
+        "createTime": "Sun, 22 May 2022 09:59:33 GMT",
+        "deptId": "5477d9a9-e41e-485f-bb08-697e8facef88",
+        "deptName": "南京分公司",
+        "email": "ss@ada.com",
+        "iconCls": null,
+        "leader": "dd",
+        "orderNum": 0,
+        "parentId": "0",
+        "parentName": "总部",
+        "phone": "18905189016",
+        "roles": [],
+        "status": "0",
+        "updateTime": "Tue, 12 Nov 2024 03:05:10 GMT"
+      },
+      {
+        "address": null,
+        "children": [
+          {
+            "address": "",
+            "children": [
+              {
+                "address": "",
+                "children": [],
+                "code": null,
+                "createTime": "Tue, 12 Nov 2024 03:09:57 GMT",
+                "deptId": "c6ae93de-2436-4090-b567-ec4835c74112",
+                "deptName": "成都",
+                "email": "",
+                "iconCls": null,
+                "leader": "柔柔弱弱",
+                "orderNum": 0,
+                "parentId": "f86724f0-94d9-445c-9d95-3ec2540da0f8",
+                "parentName": "太平洋派出所",
+                "phone": "1234242342",
+                "roles": [],
+                "status": "",
+                "updateTime": "Tue, 12 Nov 2024 06:58:15 GMT"
+              }
+            ],
+            "code": null,
+            "createTime": "Thu, 05 Sep 2024 03:15:13 GMT",
+            "deptId": "f86724f0-94d9-445c-9d95-3ec2540da0f8",
+            "deptName": "太平洋派出所",
+            "email": "",
+            "iconCls": null,
+            "leader": "12",
+            "orderNum": 0,
+            "parentId": "ce627e90-57d6-4ed4-a789-1f3dd467ae7d",
+            "parentName": "上海分公司",
+            "phone": "12",
+            "roles": [
+              {
+                "deptId": "f86724f0-94d9-445c-9d95-3ec2540da0f8",
+                "roleId": "19f00d46-8f1b-45b5-b7b7-6197d7b8cb33",
+                "roleName": "管理员1"
+              }
+            ],
+            "status": "0",
+            "updateTime": "Tue, 12 Nov 2024 06:58:07 GMT"
+          }
+        ],
+        "code": null,
+        "createTime": "Tue, 24 May 2022 23:54:10 GMT",
+        "deptId": "ce627e90-57d6-4ed4-a789-1f3dd467ae7d",
+        "deptName": "上海分公司",
+        "email": null,
+        "iconCls": null,
+        "leader": "jack",
+        "orderNum": 0,
+        "parentId": "0",
+        "parentName": "总部",
+        "phone": null,
+        "roles": [
+          {
+            "deptId": "ce627e90-57d6-4ed4-a789-1f3dd467ae7d",
+            "roleId": "301ca518-2ed0-4092-a2ee-e46c24a42c8a",
+            "roleName": "普通"
+          }
+        ],
+        "status": "0",
+        "updateTime": "Wed, 18 Sep 2024 09:58:16 GMT"
+      }
+    ],
+    "code": null,
+    "createTime": "Mon, 28 Nov 2016 10:34:54 GMT",
+    "deptId": "0",
+    "deptName": "总部",
+    "email": null,
+    "iconCls": "ext-icon-bricks",
+    "leader": null,
+    "orderNum": 100,
+    "parentId": "",
+    "parentName": "",
+    "phone": null,
+    "roles": [],
+    "status": "0",
+    "updateTime": "Mon, 28 Nov 2016 10:35:12 GMT"
+  }
+]);
 
 const searchClick = (value) => {
   console.log('value', value)
@@ -96,20 +249,28 @@ const handleResetPassword = (record) => {
   });
 }
 
-const handleEdit = (record) => {
-  edit_visible.value = true;
-  console.log('record', record)
-  edit_form.loginName = record.loginName;
-  edit_form.userName = record.userName;
-  edit_form.phoneNumber = record.phoneNumber;
-  edit_form.email = record.email;
-  edit_form.password = record.password;
-  edit_form.role = record.role;
+const handleEdit = (record, visible) => {
+  if (record) {
+    console.log('record', record)
+    edit_form.loginName = record.loginName;
+    edit_form.userName = record.userName;
+    edit_form.phoneNumber = record.phoneNumber;
+    edit_form.email = record.email;
+    edit_form.password = record.password;
+    edit_form.role = record.role;
+  }
+
+  edit_visible.value = visible;
 }
 
-const handleView = (record) => {
+const handleView = (record, visible) => {
   console.log('record', record)
-  view_permission_visible.value = true;
+  view_permission_visible.value = visible;
+}
+
+const handleDeptConfig = (record, visible) => {
+  current_record.value = record;
+  dept_config_visible.value = visible;
 }
 const editModalSave = () => {
   edit_visible.value = false;

+ 69 - 0
src/modules/permission/group/MembertConfigModal.vue

@@ -0,0 +1,69 @@
+<template>
+  <a-modal width="50%" v-model:visible="visible" title="权限配置" @cancel="handleCancel" @ok="ok">
+    <div class="membert-config-modal">
+      <a-transfer show-search :data="memberList" :default-value="selectedMemberList" :source-input-search-props="{
+        placeholder: '请输入用户名搜索',
+      }" :target-input-search-props="{
+        placeholder: '请输入用户名搜索',
+      }">
+        <template #source-title>可选列表</template>
+        <template #target-title>已选列表</template>
+      </a-transfer>
+    </div>
+
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, toRefs } from 'vue';
+
+// 定义组件的 props 和 emits
+const props = defineProps({
+  visible: Boolean,
+});
+const { visible } = toRefs(props);
+
+const emit = defineEmits(['cancel', 'ok']);
+
+const memberList = ref([
+  {
+    label: '张三',
+    value: 'zhangsan',
+  },
+  {
+    label: ' 李四',
+    value: 'lisi',
+  },
+  {
+    label: '张三',
+    value: 'zhangsan',
+  },
+]);
+
+const selectedMemberList = ref(['zhangsan']);
+
+const handleCancel = () => {
+  emit('close');
+};
+
+const ok = () => {
+  emit('ok', selectedMemberList.value);
+};
+</script>
+
+<style scoped>
+.membert-config-modal {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+:deep(.arco-transfer) {
+  width: 100%;
+}
+
+:deep(.arco-transfer-view) {
+  width: 45%;
+  height: 500px;
+}
+</style>

+ 0 - 0
src/modules/permission/org/index.vue


+ 116 - 0
src/modules/permission/role/DeptConfigModal.vue

@@ -0,0 +1,116 @@
+<template>
+  <a-modal width="50%" v-model:visible="visible" title="部门配置" @cancel="handleCancel" @ok="editDeptHandleOk">
+    <div :style="{ display: 'flex' }">
+      <a-card class="card-body" title="机构" hoverable>
+        <a-tree :data="dept_list" v-model:checked-keys="checked_keys" v-model:expanded-keys="expand_kdys"
+          :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
+            key: 'deptId',
+            title: 'deptName',
+            children: 'children',
+          }">
+        </a-tree>
+      </a-card>
+      <a-card class="card-body" title="角色所属部门">
+        <a-space wrap>
+          <a-tag v-for="(tag, index) of check_strictly" :key="tag.deptId">
+            {{ tag.deptName }}
+          </a-tag>
+        </a-space>
+      </a-card>
+    </div>
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, toRefs, onMounted } from 'vue';
+
+// 定义组件的 props 和 emits
+const props = defineProps({
+  visible: Boolean,
+  dept_list: {
+    type: Array,
+    default: () => []
+  },
+  record: {
+    type: Object,
+    default: false
+  }
+});
+
+const { visible, dept_list, record } = toRefs(props);
+
+const emit = defineEmits(['close', 'ok']);
+
+// 组件内部状态
+const checked_keys = ref([]);
+// 默认展开第一节
+const expand_kdys = ref(['0']);
+const check_strictly = ref([]);
+
+// 递归取消所有子节点的选择
+const cancelChildSelection = (node, selected_nodes) => {
+  if (node.children && node.children.length > 0) {
+    node.children.forEach(child => {
+      const child_index = selected_nodes.findIndex(val => val.deptId === child.deptId);
+      if (child_index !== -1) {
+        // 取消子节点的选择
+        selected_nodes.splice(child_index, 1);
+        // 递归取消子节点的选择
+        cancelChildSelection(child, selected_nodes);
+      }
+    });
+  }
+}
+
+// 处理树形组件的勾选变化
+const onCheck = (new_checked_keys, event) => {
+  const o = { deptId: event.node.deptId, deptName: event.node.deptName };
+  if (event.checked) {
+    // 添加部门
+    check_strictly.value.push(o);
+  } else {
+    // 移除部门
+    const dept_index = check_strictly.value.findIndex((val) => val.deptId === event.node.deptId);
+    check_strictly.value.splice(dept_index, 1);
+    // 递归取消所有子节点的选择
+    cancelChildSelection(event.node, check_strictly.value);
+  }
+};
+
+// 处理取消事件
+const handleCancel = () => {
+  visible.value = false;
+  emit('close');
+};
+
+// 处理确认事件
+const editDeptHandleOk = () => {
+  emit('ok', { checked_keys: checked_keys.value });
+};
+
+onMounted(() => {
+  // 处理传入的部门数据
+  if (record?.dept) {
+    record.dept.forEach((val) => {
+      check_strictly.value.push({
+        deptId: val.deptId,
+        deptName: val.deptName
+      });
+      checked_keys.value.push(val.deptId);
+      expand_kdys.value.push(val.deptId);
+    });
+  }
+});
+</script>
+
+<style lang="css" scoped>
+.card-body {
+  width: 460px;
+  height: 500px;
+  overflow-y: auto;
+}
+
+.card-body:first-child {
+  margin-right: 16px;
+}
+</style>

+ 140 - 0
src/modules/permission/role/PermissionConfigModal.vue

@@ -0,0 +1,140 @@
+<template>
+  <a-modal width="50%" v-model:visible="visible" title="权限配置" @cancel="handleCancel" @ok="editDeptHandleOk">
+    <a-tabs :active-key="selected_tab_key" @change="handleTabChange">
+      <a-tab-pane v-for="tab in tabs" :key="tab.key">
+        <template #title>
+          <component :is="tab.icon" /> {{ tab.title }}
+        </template>
+      </a-tab-pane>
+    </a-tabs>
+    <div style="display: flex;">
+      <a-card class="card-body" :title="getCurrentTabTitle" hoverable>
+        <a-tree :data="dept_list" v-model:checked-keys="checked_keys" v-model:expanded-keys="expand_kdys"
+          :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
+            key: 'deptId',
+            title: 'deptName',
+            children: 'children',
+          }">
+        </a-tree>
+      </a-card>
+      <a-card class="card-body" title="角色所有权限">
+        <div style="margin-bottom: 8px;">菜单功能:</div>
+        <a-space wrap>
+          <a-tag v-for="(tag, index) of check_strictly" :key="tag.deptId">
+            {{ tag.deptName }}
+          </a-tag>
+        </a-space>
+      </a-card>
+    </div>
+  </a-modal>
+</template>
+
+<script setup>
+import { ref, toRefs, onMounted, computed } from 'vue';
+
+// 定义组件的 props 和 emits
+const props = defineProps({
+  visible: Boolean,
+  dept_list: {
+    type: Array,
+    default: () => []
+  },
+  record: Object
+});
+
+const { visible, dept_list, record } = toRefs(props);
+
+const emit = defineEmits(['getActiveTab', 'close', 'ok']);
+
+const tabs = [
+  { key: "1", icon: "icon-menu", title: "菜单" },
+  { key: "2", icon: "icon-storage", title: "知识库" },
+  { key: "3", icon: "icon-robot", title: "智能体" },
+  { key: "4", icon: "icon-relation", title: "模型" }
+]
+const selected_tab_key = ref(tabs[0].key);
+
+// 组件内部状态
+const checked_keys = ref([]);
+// 默认展开第一节
+const expand_kdys = ref(['0']);
+const check_strictly = ref([]);
+
+// 递归取消所有子节点的选择
+const cancelChildSelection = (node, selectedNodes) => {
+  if (node.children && node.children.length > 0) {
+    node.children.forEach(child => {
+      const childIndex = selectedNodes.findIndex(val => val.deptId === child.deptId);
+      if (childIndex !== -1) {
+        // 取消子节点的选择
+        selectedNodes.splice(childIndex, 1);
+        // 递归取消子节点的选择
+        cancelChildSelection(child, selectedNodes);
+      }
+    });
+  }
+}
+
+// 处理树形组件的勾选变化
+const onCheck = (newCheckedKeys, event) => {
+  const o = { deptId: event.node.deptId, deptName: event.node.deptName };
+  if (event.checked) {
+    // 添加部门
+    check_strictly.value.push(o);
+  } else {
+    // 移除部门
+    const deptIndex = check_strictly.value.findIndex((val) => val.deptId === event.node.deptId);
+    check_strictly.value.splice(deptIndex, 1);
+    // 递归取消所有子节点的选择
+    cancelChildSelection(event.node, check_strictly.value);
+  }
+};
+
+const handleTabChange = (key) => {
+  const currentTab = tabs.find(tab => tab.key === key);
+  emit('getActiveTab', currentTab);
+  selected_tab_key.value = key;
+};
+// 处理取消事件
+const handleCancel = () => {
+  visible.value = false;
+  emit('close');
+};
+
+// 处理确认事件
+const editDeptHandleOk = () => {
+  emit('ok', { checkedKeys: checked_keys.value });
+};
+
+const getCurrentTabTitle = computed(() => {
+  const currentTab = tabs.find(tab => tab.key === selected_tab_key.value);
+  return currentTab ? currentTab.title : '菜单';
+});
+
+onMounted(() => {
+  // 处理传入的部门数据
+  if (record?.dept) {
+    record.dept.forEach((val) => {
+      check_strictly.value.push({
+        deptId: val.deptId,
+        deptName: val.deptName
+      });
+      checked_keys.value.push(val.deptId);
+      expand_kdys.value.push(val.deptId);
+    });
+  }
+
+});
+</script>
+
+<style lang="css" scoped>
+.card-body {
+  width: 460px;
+  height: 500px;
+  overflow-y: auto;
+}
+
+.card-body:first-child {
+  margin-right: 16px;
+}
+</style>

+ 0 - 0
src/modules/permission/role/index.vue