Procházet zdrojové kódy

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

zhupei@smartai.com před 1 rokem
rodič
revize
92f55c9956
27 změnil soubory, kde provedl 1634 přidání a 652 odebrání
  1. 729 140
      package-lock.json
  2. 11 2
      src/apis/permission/account.js
  3. 36 2
      src/apis/permission/resource.js
  4. 7 2
      src/apis/rgflow-dify.js
  5. 7 1
      src/modules/knowledge/rgflow/MessageDetail.vue
  6. 2 1
      src/modules/knowledge/rgflow/knowledgeLib/add.vue
  7. 1 1
      src/modules/knowledge/rgflow/knowledgeLib/addDetails.vue
  8. 1 1
      src/modules/knowledge/rgflow/knowledgeLib/addKnow.vue
  9. 78 0
      src/modules/knowledge/rgflow/knowledgeLib/components/Upload.vue
  10. 226 195
      src/modules/knowledge/rgflow/knowledgeLib/config.vue
  11. 3 1
      src/modules/knowledge/rgflow/knowledgeLib/edit.vue
  12. 1 1
      src/modules/knowledge/rgflow/knowledgeLib/eidtDetails.vue
  13. 45 34
      src/modules/knowledge/rgflow/knowledgeLib/index.vue
  14. 109 11
      src/modules/knowledge/rgflow/knowledgeLib/test.vue
  15. 5 5
      src/modules/knowledge/rgflow/knowledgeLib/tool.vue
  16. 1 1
      src/modules/knowledge/rgflow/knowledgeLib/utils/index.js
  17. 25 11
      src/modules/permission/TreeParts.vue
  18. 22 14
      src/modules/permission/account/DeptConfigModal.vue
  19. 24 6
      src/modules/permission/account/index.vue
  20. 7 7
      src/modules/permission/group/PermissionConfigModal.vue
  21. 5 7
      src/modules/permission/org/index.vue
  22. 28 7
      src/modules/permission/resource/ResourceForm.vue
  23. 174 0
      src/modules/permission/resource/index.vue
  24. 10 11
      src/modules/permission/role/DeptConfigModal.vue
  25. 23 142
      src/modules/permission/role/PermissionConfigModal.vue
  26. 50 48
      src/modules/permission/role/index.vue
  27. 4 1
      src/views/PermissionResource.vue

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 729 - 140
package-lock.json


+ 11 - 2
src/apis/permission/account.js

@@ -9,8 +9,8 @@ export function QueryUserList(params) {
     params
   });
 }
-// 获取用户详情
-export function QueryUserDetail(userId) {
+// 获取用户机构详情
+export function QueryUserDeptDetail(userId) {
   return request({
     url: `${prefix}/dept`,
     method: 'get',
@@ -20,6 +20,15 @@ export function QueryUserDetail(userId) {
   });
 }
 
+// 修改用户部门
+export function UpdateUserDept(data) {
+  return request({
+    url: `${prefix}/dept`,
+    method: 'put',
+    data
+  });
+}
+
 // 新增用户
 export function AddUser(data) {
   return request({

+ 36 - 2
src/apis/permission/resource.js

@@ -2,10 +2,44 @@ import request from '../../base/ajax';
 
 const prefix = '/api/menu';
 
-// 查询角色列表
-export function QueryMenuTree() {
+// 查询菜单列表
+export function QueryResourceTree() {
   return request({
     url: `${prefix}/tree_select`,
     method: 'get'
   });
 }
+
+// 查询菜单详情
+export function QueryResourceDetail(menuId) {
+  return request({
+    url: `${prefix}/${menuId}`,
+    method: 'get'
+  });
+}
+
+// 修改菜单
+export function UpdateResource(data) {
+  return request({
+    url: `${prefix}/edit_menu`,
+    method: 'put',
+    data
+  });
+}
+
+// 新增菜单
+export function CreateResource(data) {
+  return request({
+    url: `${prefix}/add_menu`,
+    method: 'post',
+    data
+  });
+}
+
+// 删除菜单
+export function DeleteResource(menuId) {
+  return request({
+    url: `${prefix}/${menuId}`,
+    method: 'delete'
+  });
+}

+ 7 - 2
src/apis/rgflow-dify.js

@@ -34,14 +34,19 @@ export function createRgFlow(data) {
 }
 
 // 头像上传
-export function kbdocumentupload(params) {
+export function kbdocumentupload(data) {
   const config = {
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded'
       // token: token,
     }
   };
-  return axios.post('/api/v1/document/upload', params, config);
+  return request({
+    url: `/rgflow/v1/document/upload`,
+    method: 'post',
+    data,
+    headers: config.headers
+  });
 }
 // 查询模型列表
 export function queryModelList() {

+ 7 - 1
src/modules/knowledge/rgflow/MessageDetail.vue

@@ -1,11 +1,17 @@
 <template>
   <div class="message-detail">
-    <knowledgeLib></knowledgeLib>
+    <knowledgeLib :kb_id="id"></knowledgeLib>
   </div>
 </template>
 
 <script setup>
 import knowledgeLib from './knowledgeLib/index.vue';
+const props = defineProps({
+  id: {
+    type: String,
+    required: true
+  }
+});
 </script>
 <style scoped lang="css">
 .message-detail {

+ 2 - 1
src/modules/knowledge/rgflow/knowledgeLib/add.vue

@@ -64,12 +64,13 @@ const acceptNameList = computed(() => {
 // 上传文件
 const submitForm = async (done) => {
   if (files.value && files.value.length > 0) {
+    console.log(files.value, props.kbobj, 11);
     files.value.map((item) => {
       const formData = new FormData();
       formData.append('file', item.file);
       formData.append('kb_id', props.kbobj.id);
       kbdocumentupload(formData).then((res) => {
-        if (res.code == 0) {
+        if (res.retcode == 0) {
           emit('changeFetchData');
           Message.success('上传成功');
           handleCancel();

+ 1 - 1
src/modules/knowledge/rgflow/knowledgeLib/addDetails.vue

@@ -86,7 +86,7 @@ const handleSubmit = async ({ values, errors }) => {
       important_kwd: values.important_kwd,
       doc_id: props.item.id
     });
-    if (res.code === 0) {
+    if (res.retcode === 0) {
       visible.value = false;
       emit('canplaythrough');
     }

+ 1 - 1
src/modules/knowledge/rgflow/knowledgeLib/addKnow.vue

@@ -53,7 +53,7 @@ const handleSubmit = async ({ values, errors }) => {
   let data = await kbcreate({
     name: values.name
   });
-  if (data.code == 0) {
+  if (data.retcode == 0) {
     visible.value = false;
     Message.success('创建成功');
     // emit('getKbdetail',data.data.kb_id);

+ 78 - 0
src/modules/knowledge/rgflow/knowledgeLib/components/Upload.vue

@@ -0,0 +1,78 @@
+<template>
+  <a-upload
+    v-model:fileList="fileList"
+    list-type="picture-card"
+    :limit="limit"
+    :action="action"
+    @change="handleChange"
+    @before-remove="beforeRemove"
+    image-preview
+  />
+</template>
+
+<script setup>
+import { computed, ref, onMounted, watch, watchEffect } from 'vue';
+const props = defineProps({
+  limit: {
+    type: Number,
+    default: 1
+  },
+  action: String, // 上传的服务器地址
+  url: String //回显的文件地址
+});
+
+const emit = defineEmits(['update:fileList', 'success', 'handleRemove']);
+const urls = computed(() => props.url);
+const fileList = ref([]);
+
+watch(
+  () => props.url,
+  (newVal) => {
+    if (newVal) {
+      fileList.value = newVal.split(',').map((item) => ({
+        uid: item,
+        name: item,
+        status: 'done',
+        url: item
+      }));
+    }
+  },
+  {
+    deep: true // 开启深度监听
+  }
+);
+
+onMounted(() => {
+  if (urls.value) {
+    fileList.value = urls.value.split(',').map((item) => ({
+      uid: item,
+      name: item,
+      status: 'done',
+      url: item
+    }));
+  }
+
+  // console.log(
+  //   window.location.origin,
+  //   import.meta.env.VITE_API_BASE_URL,
+  //   8988
+  // );
+});
+
+// console.log(urls.value, 8988);
+const beforeRemove = (file) => {
+  emit('handleRemove');
+  fileList.value = [];
+};
+
+const handleChange = (fileList) => {
+  emit('update:fileList', fileList);
+  const successFiles = fileList.filter((item) => item.status === 'done');
+  if (successFiles.length > 0) {
+    emit(
+      'success',
+      successFiles.map((item) => item.response.data)
+    );
+  }
+};
+</script>

+ 226 - 195
src/modules/knowledge/rgflow/knowledgeLib/config.vue

@@ -1,14 +1,20 @@
 <template>
   <div ref="scrollContainer" class="main-container">
-    <div style="position: absolute;top: 0;left: 0;width: 100%;padding: 0 20px">
+    <div style="position: absolute; top: 0; left: 0; width: 100%; padding: 0 20px">
       <h4 style="margin-bottom: 10px"></h4>
-      <div style="color: #666666;">在这里更新您的知识库详细信息,尤其是解析方法。</div>
+      <div style="color: #666666">在这里更新您的知识库详细信息,尤其是解析方法。</div>
       <a-divider style="margin-top: 20px" />
     </div>
     <div class="main-container-lf">
       <div class="main-container-lf-down">
-        <a-form ref="formRef" :rules="rules" :model="form" :style="{width:'100%'}" layout="vertical"
-                @submit="handleSubmit">
+        <a-form
+          ref="formRef"
+          :rules="rules"
+          :model="form"
+          :style="{ width: '100%' }"
+          layout="vertical"
+          @submit="handleSubmit"
+        >
           <a-form-item field="name" label="知识库名称">
             <a-input v-model="form.name" placeholder="请输入" />
           </a-form-item>
@@ -25,16 +31,21 @@
             </a-space>
           </a-form-item>
           <a-form-item field="section" label="描述">
-            <a-textarea placeholder=""  v-model="form.description" allow-clear />
+            <a-textarea placeholder="" v-model="form.description" allow-clear />
           </a-form-item>
-<!--          <a-form-item field="section" label="语言" :rules="[{match:/section one/,message:'请选择'}]">-->
-<!--            <a-select v-model="form.section" placeholder="请选择" allow-clear>-->
-<!--              <a-option value="0">中文</a-option>-->
-<!--              <a-option value="1">英文</a-option>-->
-<!--            </a-select>-->
-<!--          </a-form-item>-->
+          <!--          <a-form-item field="section" label="语言" :rules="[{match:/section one/,message:'请选择'}]">-->
+          <!--            <a-select v-model="form.section" placeholder="请选择" allow-clear>-->
+          <!--              <a-option value="0">中文</a-option>-->
+          <!--              <a-option value="1">英文</a-option>-->
+          <!--            </a-select>-->
+          <!--          </a-form-item>-->
           <a-form-item field="embd_id" label="嵌入模型">
-            <a-select :size="'large'" v-model="form.embd_id" placeholder="请选择 ..." :disabled="kbObj.chunk_num && kbObj.token_num">
+            <a-select
+              :size="'large'"
+              v-model="form.embd_id"
+              placeholder="请选择 ..."
+              :disabled="kbObj.chunk_num && kbObj.token_num"
+            >
               <!-- <a-optgroup  :label="index" v-for="(item,index) in mobileData" :key=index>
                 <a-option
                   v-for="(obj) in item"
@@ -44,24 +55,20 @@
                   {{obj.llm_name}}
                 </a-option>
               </a-optgroup> -->
-              <a-option
-                  v-for="(item,index) in mobileData"
-                  :key='item.name'
-                  :value="item.name"
-                >
-                  {{item.name}}
-                </a-option>
+              <a-option v-for="(item, index) in mobileData" :key="item.name" :value="item.name">
+                {{ item.name }}
+              </a-option>
             </a-select>
           </a-form-item>
           <a-form-item field="parser_id" label="解析方法">
-            <a-select v-model="form.parser_id" placeholder="请选择" :disabled="!!(kbObj.chunk_num  && kbObj.token_num)" >
+            <a-select v-model="form.parser_id" placeholder="请选择" :disabled="!!(kbObj.chunk_num && kbObj.token_num)">
               <a-option v-for="item in parser_ids" :key="item.value" :label="item.name" :value="item.value"></a-option>
             </a-select>
           </a-form-item>
           <a-form-item field="slider" label="块Token数">
-            <a-slider v-model="form.chunk_token_num" :min="0" :max="2048"  show-input />
+            <a-slider v-model="form.chunk_token_num" :min="0" :max="2048" show-input />
           </a-form-item>
-          <a-form-item  label="布局识别" >
+          <a-form-item label="布局识别">
             <a-space direction="vertical">
               <a-switch v-model="form.layout_recognize" size="small" />
             </a-space>
@@ -73,28 +80,35 @@
             <a-form-item field="prompt" label="提示词">
               <a-textarea
                 v-model="form.prompt"
-                style="height: 10rem;border: 1px solid  var(--color-fill-3);border-radius: 4px" placeholder="请填写提示词"  />
+                style="height: 10rem; border: 1px solid var(--color-fill-3); border-radius: 4px"
+                placeholder="请填写提示词"
+              />
             </a-form-item>
             <a-form-item field="slider" label="最大token数">
-              <a-slider v-model="form.max_token" :min="0" :max="2048" show-input/>
+              <a-slider v-model="form.max_token" :min="0" :max="2048" show-input />
             </a-form-item>
-            <a-form-item field="slider" label="阈值" >
-              <a-slider v-model="form.threshold"  :step="0.01" :min="0" :max="1"  show-input/>
+            <a-form-item field="slider" label="阈值">
+              <a-slider v-model="form.threshold" :step="0.01" :min="0" :max="1" show-input />
             </a-form-item>
             <a-form-item field="slider" label="最大聚类数">
-              <a-slider v-model="form.max_cluster" :min="1" :max="1024" show-input/>
+              <a-slider v-model="form.max_cluster" :min="1" :max="1024" show-input />
             </a-form-item>
             <a-form-item field="slider" label="随机种子" v-model="form.random_seed">
-              <a-input-number v-model="form.random_seed" :style="{width:'300px',marginRight:'1rem',borderRadius:'4px'}"
-                              placeholder="请输入" class="input-demo" :min="10" :max="100" />
+              <a-input-number
+                v-model="form.random_seed"
+                :style="{ width: '300px', marginRight: '1rem', borderRadius: '4px' }"
+                placeholder="请输入"
+                class="input-demo"
+                :min="10"
+                :max="100"
+              />
               <a-button type="primary" @click="randomNumber">
                 <icon-plus />
               </a-button>
             </a-form-item>
-
           </div>
           <a-form-item>
-            <div style="width: 100%;text-align: right">
+            <div style="width: 100%; text-align: right">
               <a-space>
                 <a-button @click="cancelConfig">取消</a-button>
                 <a-button :loading="loading" type="primary" html-type="submit">保存</a-button>
@@ -105,70 +119,104 @@
       </div>
     </div>
     <div class="main-container-rt">
-      <section data-inspector-line="34" data-inspector-column="4"
-               data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
-               class="categoryPanelWrapper___yI4rY"><h4
-        class="ant-typography topTitle___tNiB9 css-dev-only-do-not-override-1wxecgq" data-inspector-line="37"
-        data-inspector-column="10"
-        data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">"General"
-        分块方法说明</h4>
-        <p data-inspector-line="40" data-inspector-column="10"
-           data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"><p>
-          支持的文件格式为<b>DOCX、EXCEL、PPT、IMAGE、PDF、TXT</b>。</p>
-          <p>此方法将简单的方法应用于块文件:</p>
-          <p>
-          </p>
-          <li>系统将使用视觉检测模型将连续文本分割成多个片段。</li>
-          <li>接下来,这些连续的片段被合并成Token数不超过“Token数”的块。</li>
-          <p></p></p>
-        <h4 class="ant-typography css-dev-only-do-not-override-1wxecgq" data-inspector-line="45"
-            data-inspector-column="10"
-            data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">
-          "General" 示例</h4><span class="ant-typography css-dev-only-do-not-override-1wxecgq" data-inspector-line="48"
-                                   data-inspector-column="10"
-                                   data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">提出以下屏幕截图以促进理解。</span>
-        <div data-inspector-line="49" data-inspector-column="10"
-             data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
-             class="ant-row imageRow___C3Xpb css-dev-only-do-not-override-1wxecgq"
-             style="margin-left: -5px; margin-right: -5px; row-gap: 10px;display: flex; margin-top: 20px;width: 100%">
+      <section
+        data-inspector-line="34"
+        data-inspector-column="4"
+        data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+        class="categoryPanelWrapper___yI4rY"
+      >
+        <h4
+          class="ant-typography topTitle___tNiB9 css-dev-only-do-not-override-1wxecgq"
+          data-inspector-line="37"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+        >
+          "General" 分块方法说明
+        </h4>
+        < data-inspector-line="40" data-inspector-column="10"
+        data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">
+        <p>支持的文件格式为<b>DOCX、EXCEL、PPT、IMAGE、PDF、TXT</b>。</p>
+        <p>此方法将简单的方法应用于块文件:</p>
+        <p></p>
+        <li>系统将使用视觉检测模型将连续文本分割成多个片段。</li>
+        <li>接下来,这些连续的片段被合并成Token数不超过“Token数”的块。</li>
+        <p></p>
+        <h4
+          class="ant-typography css-dev-only-do-not-override-1wxecgq"
+          data-inspector-line="45"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+        >
+          "General" 示例
+        </h4>
+        <span
+          class="ant-typography css-dev-only-do-not-override-1wxecgq"
+          data-inspector-line="48"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+          >提出以下屏幕截图以促进理解。</span
+        >
+        <div
+          data-inspector-line="49"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+          class="ant-row imageRow___C3Xpb css-dev-only-do-not-override-1wxecgq"
+          style="margin-left: -5px; margin-right: -5px; row-gap: 10px; display: flex; margin-top: 20px; width: 100%"
+        >
           <img
-            data-inspector-line="32" data-inspector-column="8"
-            data-inspector-relative-path="src\components\svg-icon.tsx" :src="step1" alt=""
-            width="50%">
+            data-inspector-line="32"
+            data-inspector-column="8"
+            data-inspector-relative-path="src\components\svg-icon.tsx"
+            :src="step1"
+            alt=""
+            width="50%"
+          />
           <img
-            data-inspector-line="32" data-inspector-column="8"
-            data-inspector-relative-path="src\components\svg-icon.tsx" :src="step2" alt=""
-            width="50%">
+            data-inspector-line="32"
+            data-inspector-column="8"
+            data-inspector-relative-path="src\components\svg-icon.tsx"
+            :src="step2"
+            alt=""
+            width="50%"
+          />
         </div>
-        <h4 class="ant-typography css-dev-only-do-not-override-1wxecgq" data-inspector-line="60"
-            data-inspector-column="10"
-            data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">
-          General 对话示例</h4>
-        <div class="ant-divider css-dev-only-do-not-override-1wxecgq ant-divider-horizontal" data-inspector-line="63"
-             data-inspector-column="10"
-             data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
-             role="separator"></div>
+        <h4
+          class="ant-typography css-dev-only-do-not-override-1wxecgq"
+          data-inspector-line="60"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+        >
+          General 对话示例
+        </h4>
+        <div
+          class="ant-divider css-dev-only-do-not-override-1wxecgq ant-divider-horizontal"
+          data-inspector-line="63"
+          data-inspector-column="10"
+          data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
+          role="separator"
+        ></div>
         <a-divider style="margin-top: 20px" />
       </section>
     </div>
   </div>
 </template>
 
-<script  setup>
-import { onMounted, onBeforeMount, reactive, ref, nextTick } from "vue";
-import { Message } from "@arco-design/web-vue";
-import { queryModelList,kbUpdate,getKnowledgeBaseModel } from '../../../../apis/rgflow-dify';
-import message from "@arco-design/web-vue/es/message";
-import useLoading from "./loading";
-import step1 from "../../../../assets/image/management/step1.png";
-import step2 from "../../../../assets/image/management/step2.png";
-const { loading,setLoading } = useLoading(true);
+<script setup>
+import { onMounted, onBeforeMount, reactive, ref, nextTick } from 'vue';
+import Upload from './components/Upload.vue';
+import { Message } from '@arco-design/web-vue';
+import { queryModelList, kbUpdate, getKnowledgeBaseModel } from '../../../../apis/rgflow-dify';
+import message from '@arco-design/web-vue/es/message';
+import useLoading from './loading';
+import step1 from '../../../../assets/image/management/step1.png';
+import step2 from '../../../../assets/image/management/step2.png';
+const { loading, setLoading } = useLoading(true);
 const scrollContainer = ref();
-const props =  defineProps(['kbtenantInfo'])
-const emit =  defineEmits(['cancleConfig','saveConfig'])
+const props = defineProps(['kbtenantInfo']);
+const emit = defineEmits(['cancleConfig', 'saveConfig']);
 // 解析方法列表
-let parser_ids = props.kbtenantInfo.parser_ids.split(",").map((item) => {
-  const [value1, value2] = item.split(":");
+let parser_ids = props.kbtenantInfo.parser_ids.split(',').map((item) => {
+  const [value1, value2] = item.split(':');
   return {
     name: value2,
     value: value1
@@ -180,7 +228,7 @@ let avatarShow = ref(true);
 
 const form = reactive({
   name: '',
-  avatar: '',//头像
+  avatar: '', //头像
   max_token: 600,
   threshold: 0.32,
   max_cluster: 233,
@@ -188,13 +236,12 @@ const form = reactive({
   chunk_token_num: 384,
   use_raptor: false,
   pages: {},
-  prompt: "请总结以下段落。 小心数字,不要编造。 段落如下:\n" +
-    "      {cluster_content}\n" +
-    "以上就是你需要总结的内容。",
-  description: "",
-  language: "English",
-  permission: "",
-  embd_id: "",
+  prompt:
+    '请总结以下段落。 小心数字,不要编造。 段落如下:\n' + '      {cluster_content}\n' + '以上就是你需要总结的内容。',
+  description: '',
+  language: 'English',
+  permission: '',
+  embd_id: '',
   parser_id: '',
   layout_recognize: true
 });
@@ -203,32 +250,32 @@ const rules = {
   name: [
     {
       required: true,
-      message:'知识库名称不允许为空',
-    },
+      message: '知识库名称不允许为空'
+    }
   ],
   embd_id: [
     {
       required: true,
-      message:'请选择',
-    },
+      message: '请选择'
+    }
   ],
   parser_id: [
     {
       required: true,
-      message:'请选择',
-    },
+      message: '请选择'
+    }
   ],
   prompt: [
     {
       required: true,
-      message:'提示词不允许为空',
-    },
-  ],
-}
+      message: '提示词不允许为空'
+    }
+  ]
+};
 let visible = ref(false);
 const formRef = ref();
 const modelList = ref({});
-const mobileData= ref([])
+const mobileData = ref([]);
 const file = ref();
 
 const uploadAction = '/api/v1/llm/upload'; // 替换为你的上传API
@@ -246,59 +293,52 @@ const handleSuccess = (urls) => {
   form.avatar = urls[0];
 };
 
-
 const handleSubmit = async ({ values, errors }) => {
-  if (errors){
+  if (errors) {
     return;
   }
   let parms = {
-      name: form.name,
-      avatar: form.avatar,
-      description: form.description,
-      language: form.language,
-      permission: form.permission,
-      embd_id: form.embd_id,
-      parser_id: form.parser_id,
-      parser_config: {
-        chunk_token_num: form.chunk_token_num,
-        layout_recognize: form.layout_recognize,
-      },
-      kb_id: kbObj.id,
-   }
-   if(form.use_raptor){
-     parms.parser_config.raptor = {
-       use_raptor: form.use_raptor,
-       prompt: form.prompt,
-       max_token: form.max_token,
-       threshold: form.threshold,
-       max_cluster: form.max_cluster,
-       random_seed: form.random_seed,
-     };
-   }
-   try {
-     setLoading(true)
+    name: form.name,
+    avatar: form.avatar,
+    description: form.description,
+    language: form.language,
+    permission: form.permission,
+    embd_id: form.embd_id,
+    parser_id: form.parser_id,
+    parser_config: {
+      chunk_token_num: form.chunk_token_num,
+      layout_recognize: form.layout_recognize
+    },
+    kb_id: kbObj.id
+  };
+  if (form.use_raptor) {
+    parms.parser_config.raptor = {
+      use_raptor: form.use_raptor,
+      prompt: form.prompt,
+      max_token: form.max_token,
+      threshold: form.threshold,
+      max_cluster: form.max_cluster,
+      random_seed: form.random_seed
+    };
+  }
+  try {
+    setLoading(true);
     const data = await kbUpdate(parms);
-    if(data.code == '0'){
-      Message.success("配置成功");
+    if (data.retcode == '0') {
+      Message.success('配置成功');
       // window.location.reload();
-      emit('saveConfig')
-
+      emit('saveConfig');
     }
-    setLoading(false)
-
+    setLoading(false);
   } catch (err) {
     Message.error(err.message);
-     setLoading(false)
+    setLoading(false);
   }
-
-
-
 };
 
 const cancelConfig = () => {
-    emit('cancleConfig')
-}
-
+  emit('cancleConfig');
+};
 
 function randomNumber() {
   // 生成一个介于min和max之间的随机整数(包含min和max)
@@ -308,109 +348,97 @@ function randomNumber() {
 }
 
 const handleBeforeOk = (done) => {
-  formRef.value.validate().then(res => {
-    console.log("form:", form);
+  formRef.value.validate().then((res) => {
+    console.log('form:', form);
     if (!form.name) {
       done(false);
     } else {
-      console.log("请求数据");
-
+      console.log('请求数据');
     }
   });
 };
-const handleOpened =(el) => {
-  Object.assign(form,{
-
-  });
+const handleOpened = (el) => {
+  Object.assign(form, {});
   // formRef.value.resetFields();
-}
+};
 
 const queryModel = async (params) => {
   try {
     const data = await getKnowledgeBaseModel(params);
     // const res =await queryModelList(params)
     // console.log(data.data, '大模型列表');
-    modelList.value = data.data
-    for(let key in data.data) {
-      if (data.data.hasOwnProperty(key)) { // 确保是对象自有属性,不是原型链上的属性
-        if(data.data[key].llm.length > 0){
-          mobileData.value= data.data[key].llm.filter(item => item.type == "embedding")
+    modelList.value = data.data;
+    for (let key in data.data) {
+      if (data.data.hasOwnProperty(key)) {
+        // 确保是对象自有属性,不是原型链上的属性
+        if (data.data[key].llm.length > 0) {
+          mobileData.value = data.data[key].llm.filter((item) => item.type == 'embedding');
         }
       }
-
     }
-
-
   } catch (err) {
     // you can report use errorHandler or other
   } finally {
-
   }
 };
 
 const changekbObj = (value) => {
-
-   formRef.value.resetFields();
-  nextTick(()=>{
+  formRef.value.resetFields();
+  nextTick(() => {
     kbObj = value;
     console.log(kbObj, 'kbObj');
     let configObj = {
       name: kbObj.name,
       avatar: kbObj.avatar,
-      pages:  kbObj.parser_config.pages,
-      chunk_token_num:  kbObj.parser_config.chunk_token_num,
+      pages: kbObj.parser_config.pages,
+      chunk_token_num: kbObj.parser_config.chunk_token_num,
       description: kbObj.description,
-      language: "English",
+      language: 'English',
       permission: kbObj.permission,
       embd_id: kbObj.embd_id || props.kbtenantInfo.embd_id,
       parser_id: kbObj.parser_id,
-      layout_recognize: true,
-    }
-    let raptor = {}
-    if(kbObj.parser_config && kbObj.parser_config.raptor){
+      layout_recognize: true
+    };
+    let raptor = {};
+    if (kbObj.parser_config && kbObj.parser_config.raptor) {
       raptor = kbObj.parser_config.raptor;
-      Object.assign(form,{
+      Object.assign(form, {
         ...configObj,
         ...raptor,
-        use_raptor: kbObj.parser_config.raptor.use_raptor,
+        use_raptor: kbObj.parser_config.raptor.use_raptor
       });
-    }else{
-      Object.assign(form,{
+    } else {
+      Object.assign(form, {
         ...configObj,
-        use_raptor: false,
+        use_raptor: false
       });
     }
     imageUrls.value = [kbObj.avatar];
-    console.log(form,'配置页面表单信息');
+    console.log(form, '配置页面表单信息');
     avatarShow.value = false;
     setTimeout(() => {
       avatarShow.value = true;
-    },100);
+    }, 100);
     scrollContainer.value.scrollTop = 0;
-  })
-}
+  });
+};
 
 defineExpose({
   changekbObj
-})
-
-
-onBeforeMount(()=>{
-  setLoading(false)
-  queryModel({})
-})
-onMounted(()=>{
-
-
-})
+});
 
+onBeforeMount(() => {
+  setLoading(false);
+  queryModel({});
+});
+onMounted(() => {});
 </script>
 
-<script >
-import { IconEdit, IconPlus } from "@arco-design/web-vue/es/icon";
+<script>
+import { IconEdit, IconPlus } from '@arco-design/web-vue/es/icon';
 
 export default {
-  name: "config",
+  name: 'config',
   components: { IconPlus, IconEdit },
   methods: {}
 };
@@ -428,7 +456,7 @@ export default {
   padding-top: 80px;
 
   .main-container-lf {
-    width:calc(40% - 40px);
+    width: calc(40% - 40px);
     border-radius: 10px;
     .main-container-lf-top {
       width: 100%;
@@ -483,4 +511,7 @@ export default {
     }
   }
 }
+.arco-form .arco-slider {
+  display: flex;
+}
 </style>

+ 3 - 1
src/modules/knowledge/rgflow/knowledgeLib/edit.vue

@@ -59,10 +59,12 @@ const handleSubmit = async ({ values, errors }) => {
     doc_id: props.item.id,
     name: form.name
   });
-  if (data.code == 0) {
+  if (data.retcode == 0) {
     Message.success('操作成功');
     visible.value = false;
     emit('upTabdateItem', props.item.id, form.name);
+  } else {
+    Message.error('操作失败');
   }
 };
 

+ 1 - 1
src/modules/knowledge/rgflow/knowledgeLib/eidtDetails.vue

@@ -92,7 +92,7 @@ const handleSubmit = async ({ values, errors }) => {
       doc_id: props.item.doc_id,
       chunk_id: props.item.chunk_id
     });
-    if (res.code === 0) {
+    if (res.retcode === 0) {
       visible.value = false;
       emit('canplaythrough');
     }

+ 45 - 34
src/modules/knowledge/rgflow/knowledgeLib/index.vue

@@ -35,12 +35,14 @@
       <div class="rt-container" v-if="tabs.length > 0">
         <a-tabs :active-key="activeKey" @change="handleTabChange">
           <a-tab-pane key="1" title="数据集">
-            <a-scrollbar
-              class="rt-container-main"
-              :style="{ height: documentHeight + 70 + 'px' }"
-              style="overflow: auto"
-            >
-              <a-card ref="account" class="general-card" title="" style="padding-top: 20px">
+            <a-scrollbar class="rt-container-main" :style="{ height: documentHeight + 'px' }" style="overflow: auto">
+              <!-- <a-scrollbar class="rt-container-main" style="overflow: auto"> -->
+              <a-card
+                ref="account"
+                class="general-card"
+                title=""
+                style="height: calc(100% - 25px); overflow-y: auto; padding-top: 20px"
+              >
                 <a-row>
                   <a-col :flex="1" :span="12">
                     <a-form
@@ -341,20 +343,12 @@
             </a-scrollbar>
           </a-tab-pane>
           <a-tab-pane key="2" title="测试" v-if="getAut('/kb/test')">
-            <a-scrollbar
-              class="rt-container-main"
-              :style="{ height: documentHeight + 70 + 'px' }"
-              style="overflow: auto"
-            >
+            <a-scrollbar class="rt-container-main" :style="{ height: documentHeight + 'px' }" style="overflow: hidden">
               <test ref="testForm"></test>
             </a-scrollbar>
           </a-tab-pane>
           <a-tab-pane key="3" title="配置" v-if="getAut('/kb/update')">
-            <a-scrollbar
-              class="rt-container-main"
-              :style="{ height: documentHeight + 70 + 'px' }"
-              style="overflow: auto"
-            >
+            <a-scrollbar class="rt-container-main" :style="{ height: documentHeight + 'px' }" style="overflow: auto">
               <config
                 ref="configForm"
                 :kbtenantInfo="kbtenantInfo"
@@ -421,6 +415,12 @@ import { documentHeight, parseTime } from './utils';
 
 // let modelStore = userModelState();
 // const httpUrl = modelStore.hrefUrl;
+const props = defineProps({
+  kb_id: {
+    type: String,
+    default: ''
+  }
+});
 
 const account = ref(null);
 const generateFormModel = () => {
@@ -476,8 +476,8 @@ let kbtenantInfo = reactive({
 let tabs = ref([]);
 let timer = null; // 定时器
 
-const selectTab = (index, item) => {
-  selectedTab.value = index;
+const selectTab = (item) => {
+  // selectedTab.value = index;
   checkedArr.value = [];
   // console.log(item.id);
   kbobj = item;
@@ -489,7 +489,14 @@ const selectTab = (index, item) => {
   });
   getKbdetail(kbobj.id);
 };
-
+const toTargetKnowledge = () => {
+  if (tabs.value.length) {
+    const index = tabs.value.findIndex((item) => item.id == props.kb_id);
+    if (index !== -1) {
+      selectTab(tabs.value[index]);
+    }
+  }
+};
 const getAut = (val) => {
   // const roleData = JSON.parse(getUserResources());
   // const roleBtn = roleData.map((item: any) => item.component);
@@ -604,7 +611,8 @@ const fetchData = async (params) => {
   setLoading(true);
   try {
     const data = await queryKbDocumentList(params);
-    if (data.code == '0') {
+    console.log(data, 'data');
+    if (data.retcode == '0') {
       parser_ids = kbtenantInfo.parser_ids.split(',').reduce((acc, pair) => {
         const [key, value] = pair.split(':');
         acc[key] = value;
@@ -639,7 +647,7 @@ const fetchData = async (params) => {
 const documentList = async () => {
   let params = { ...basePagination, kb_id: kbobj.id };
   const data = await queryKbDocumentList(params);
-  if (data.code == '0') {
+  if (data.retcode == '0') {
     parser_ids = kbtenantInfo.parser_ids.split(',').reduce((acc, pair) => {
       const [key, value] = pair.split(':');
       acc[key] = value;
@@ -695,15 +703,17 @@ const knowledgeData = async (params = { page: 1, page_size: 10 }) => {
     tabs.value = data;
     // console.log(tabs.value, 'tabs');
     if (tabs.value.length > 0 && tabs.value[0]) {
-      kbobj = tabs.value[0];
-      kbId = kbobj.id;
-      activeKey.value = '1';
-      basePagination.page = 1;
+      // kbobj = tabs.value[0];
+      // kbId = kbobj.id;
+      // activeKey.value = '1';
+      // basePagination.page = 1;
       fetchData({
-        kb_id: kbobj.id,
+        kb_id: props.kb_id,
         ...basePagination
       });
-      getKbdetail(kbobj.id);
+      toTargetKnowledge();
+      // getKbdetail(kbobj.id);
+      getKbdetail(props.kb_id);
     } else {
     }
     // renderData.value = data.list;
@@ -722,6 +732,7 @@ const getKbdetail = async (kb_id) => {
   const data = await queryKbdetail({
     kb_id: kb_id
   });
+  console.log('targetID', kb_id);
   kbdetail = data.data;
   testForm.value.changekbid(kbdetail.id);
   configForm.value.changekbObj(kbdetail);
@@ -759,7 +770,7 @@ const deleteKnowledge = (id) => {
       let data = await deleteKnow({
         kb_id: id
       });
-      if (data.code == 0) {
+      if (data.retcode == 0) {
         Message.success('删除成功');
         knowledgeData();
       }
@@ -825,7 +836,7 @@ const addBlock = async (type) => {
       doc_ids: checkedArr.value,
       run: type
     });
-    if (data.code == 0) {
+    if (data.retcode == 0) {
       checkedArr.value.forEach(async (item) => {
         tabs.value.forEach(async (items) => {
           if (items.id == item) {
@@ -848,7 +859,7 @@ const addBlock = async (type) => {
       hideTitle: true,
       onOk: async () => {
         let data = await kbdocumentrm({ doc_id: checkedArr.value });
-        if (data.code == 0) {
+        if (data.retcode == 0) {
           Message.success('删除成功');
           getKbList();
         }
@@ -908,7 +919,7 @@ function upTabdateItem(id, newName) {
 const deleteItem = async (row) => {
   console.log(row);
   let data = await kbdocumentrm({ doc_id: row.id });
-  if (data.code == 0) {
+  if (data.retcode == 0) {
     Message.success('删除成功');
     // console.log(kbobj, 'kbobj');
     fetchData({
@@ -952,7 +963,7 @@ const handleChangeStatus = async (row) => {
     doc_id: row.id,
     status: row.status
   });
-  if (data.code == 0) {
+  if (data.retcode == 0) {
     Message.success('操作成功');
   }
 };
@@ -973,7 +984,7 @@ const run = async (row) => {
     doc_ids: [row.id],
     run: run
   });
-  if (data.code == 0) {
+  if (data.retcode == 0) {
     // 定时器查询当前列表
     fetchData({
       kb_id: tabs.value[selectedTab.value].id,
@@ -1091,7 +1102,7 @@ onBeforeUnmount(() => {
     .rt-container {
       position: relative;
       //background: #626aea;
-      margin-left: 20px;
+      /* margin-left: 20px; */
       /* width: calc(100% - 200px); */
       overflow: hidden;
 

+ 109 - 11
src/modules/knowledge/rgflow/knowledgeLib/test.vue

@@ -54,23 +54,52 @@
       </div>
     </div>
     <div class="main-container-rt">
-      <a-collapse style="width: 96%; margin-top: 1rem; margin-left: 2%">
-        <a-collapse-item header="0/0 选定的文件" key="3">
-          <template #extra>
-            <a-tag size="small">命中数</a-tag>
-            <a-tag size="small">看法</a-tag>
-          </template>
+      <a-collapse style="width: 96%; margin-top: 1rem; margin-left: 2%" @change="handleCollapseChange">
+        <a-collapse-item :header="check_count + '/' + doc_total + '选定的文件'" key="3">
+          <a-list :bordered="false" :pagination-props="doc_pagination_props" style="background: var(--color-bg-1)">
+            <a-list-item v-for="doc in docList" :key="doc.doc_id" style="border-left: none; border-right: none">
+              <a-list-item-meta :title="doc.doc_name">
+                <template #avatar>
+                  <a-checkbox v-model="doc.checked"></a-checkbox>
+                </template>
+              </a-list-item-meta>
+              <template #actions>
+                {{ doc.count }}
+                <icon-eye @click="handlePreview(doc.doc_id)" />
+              </template>
+            </a-list-item>
+          </a-list>
           <div>
-            <a-empty />
+            <!-- <a-empty /> -->
           </div>
         </a-collapse-item>
       </a-collapse>
+      <div
+        class="main-container-rt-down"
+        :style="collapse_status ? 'height: calc(100% - 350px);overflow-y: auto' : 'height: calc(100% - 140px)'"
+      >
+        <a-card :bordered="false" v-for="item in chunks" :key="item.chunk_id">
+          <template #title>
+            <div class="main-container-rt-down-title">
+              <div>{{ (item.similarity * 100).toFixed(2) }}&nbsp;混合相似度</div>
+              <div>{{ (item.term_similarity * 100).toFixed(2) }}&nbsp;关键词相似度</div>
+              <div>{{ (item.vector_similarity * 100).toFixed(2) }}&nbsp;向量相似度</div>
+            </div>
+          </template>
+          <div class="main-container-rt-down-content">
+            {{ item.content_with_weight }}
+          </div>
+        </a-card>
+      </div>
+      <div class="footer-page">
+        <a-pagination :total="chunk_total" show-total />
+      </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { onMounted, onBeforeMount, reactive, ref, watch } from 'vue';
+import { onMounted, onBeforeMount, reactive, ref, watch, computed } from 'vue';
 import { Message } from '@arco-design/web-vue';
 import { kbretrievalTest, queryModelList } from '../../../../apis/rgflow-dify';
 // const props = defineProps(['kbId'])
@@ -95,11 +124,28 @@ let form = reactive({
   top_k: 1024,
   vector_similarity_weight: 0.7
 });
+const chunks = ref([]);
+const all_chunks = ref([]);
+const docList = ref([]);
+
+const doc_total = ref(0);
+const chunk_total = ref(0);
+const collapse_status = ref(false);
+const check_count = computed(() => {
+  return docList.value.filter((item) => item.checked).length || 0;
+}); // 选中的文件数
+const doc_pagination_props = reactive({
+  defaultPageSize: 3,
+  total: doc_total.value
+});
 
 const formatter = (value) => {
   // return String(Math.round(value / 100))
 };
 
+const handleCollapseChange = (key) => {
+  key.length ? (collapse_status.value = true) : (collapse_status.value = false);
+};
 const handleSubmit = async ({ values, errors }) => {
   // console.log(props.kbobj, 'props.kbobj');
   if (!form.question) {
@@ -117,8 +163,20 @@ const handleSubmit = async ({ values, errors }) => {
   try {
     loading.value = true;
     const data = await kbretrievalTest(formObj);
-    if (data.code == 0) {
+    if (data.retcode == 0) {
       Message.success('测试成功');
+      all_chunks.value = data.data.chunks;
+      docList.value = data.data.doc_aggs?.map((item) => {
+        return {
+          ...item,
+          checked: false
+        };
+      });
+      docList.value[0].checked = true;
+      chunks.value = all_chunks.value.filter((item) => item.doc_id == docList.value[0].doc_id);
+
+      doc_total.value = data.data.doc_aggs.length;
+      chunk_total.value = data.data.chunks.length;
     } else {
       Message.error('测试失败');
     }
@@ -160,6 +218,11 @@ const queryModel = async (params) => {
   } finally {
   }
 };
+const handlePreview = (doc_id) => {
+  window.open(`${window.RGFLOW_BASE_URL}/document/${doc_id}?ext=docx&prefix=document`);
+};
+
+// watch(()=>)
 
 defineExpose({
   changekbid
@@ -185,10 +248,9 @@ export default {
   .main-container-lf {
     width: 30%;
     height: 100%;
-    //border: 1px solid var(--color-fill-3);
     background: var(--color-bg-2);
     border-radius: 10px;
-    overflow: auto;
+    overflow: hidden;
     .main-container-lf-top {
       width: 100%;
       height: 60px;
@@ -229,12 +291,48 @@ export default {
     }
   }
   .main-container-rt {
+    position: relative;
     width: 69%;
     height: 100%;
+    overflow: hidden;
     //background: #626aea;
     //border: 1px solid var(--color-fill-3);
     background: var(--color-bg-2);
     border-radius: 10px;
+    .main-container-rt-down {
+      padding: 25px;
+      .main-container-rt-down-title {
+        display: flex;
+        gap: 10px;
+        font-size: 14px;
+      }
+      .main-container-rt-down-content {
+        max-height: 300px;
+        position: relative;
+        padding: 20px 0;
+        font-size: 16px;
+        .footer-page {
+          position: absolute;
+          bottom: 10px;
+          left: 10px;
+        }
+      }
+    }
+    .footer-page {
+      padding-top: 10px;
+      position: absolute;
+      bottom: 20px;
+      left: 20px;
+      width: 100%;
+      background: var(--color-bg-2);
+    }
   }
 }
+
+:deep .arco-collapse-item-content {
+  padding: 0;
+}
+:deep .arco-collapse-item-content-box {
+  padding: 0;
+}
 </style>

+ 5 - 5
src/modules/knowledge/rgflow/knowledgeLib/tool.vue

@@ -365,7 +365,7 @@ const handleSubmit = async ({ values, errors }) => {
         pages: []
       }
     });
-    if (data.code == 0) {
+    if (data.retcode == 0) {
       Message.success('配置成功');
     } else {
       Message.error('配置失败');
@@ -450,7 +450,7 @@ const addBlock = (type) => {
       doc_id: props.item.id
     };
     chunkSwitch(params).then((res) => {
-      if (res.code == 0) {
+      if (res.retcode == 0) {
         Message.success('操作成功');
         getList();
         checkedAll.value = false;
@@ -488,7 +488,7 @@ const getList = async () => {
     params.available_int = available_int.value;
   }
   await queryChunkList(params).then((res) => {
-    if (res.code == 0) {
+    if (res.retcode == 0) {
       listData.value = res.data.chunks;
       isLoading.value = false;
       listData.value = listData.value.map((item) => {
@@ -512,7 +512,7 @@ const updateChunkStatus = (val) => {
   };
   chunkSwitch(params).then((res) => {
     console.log(res);
-    if (res.code == 0) {
+    if (res.retcode == 0) {
       Message.success('更新成功');
       getList();
     } else {
@@ -529,7 +529,7 @@ const deleteChunk = () => {
   };
   chunkRm(params).then((res) => {
     console.log(res);
-    if (res.code == 0) {
+    if (res.retcode == 0) {
       Message.success('删除成功');
       getList();
     } else {

+ 1 - 1
src/modules/knowledge/rgflow/knowledgeLib/utils/index.js

@@ -1,4 +1,4 @@
-export const documentHeight = window.innerHeight - 320;
+export const documentHeight = window.innerHeight - 185;
 /**
  * @description 格式化时间
  * @param time

+ 25 - 11
src/modules/permission/TreeParts.vue

@@ -1,21 +1,19 @@
 <template>
-  <a-card title="机构" :bordered="false" :header-style="{ textAlign: 'left' }" class="tree-card">
+  <a-card :title="title" :bordered="false" :header-style="{ textAlign: 'left' }" class="tree-card">
     <template #title>
-      <span>机构</span>
-      <a-tooltip content="点击后可通过拖拽更改机构结构,再次点击来关闭" background-color="#165DFF">
+      <span>{{ title }}</span>
+      <a-tooltip v-if="showLock" content="点击后可通过拖拽更改结构,再次点击来关闭" background-color="#165DFF">
         <a-button :type="is_lock ? 'secondary' : 'outline'" style="float: right;" @click="toggleDraggable"
           v-has-permi="'/base/syorganization!update'">
           <component :is="is_lock ? 'IconUnlock' : 'IconLock'" />
         </a-button>
       </a-tooltip>
     </template>
-    <a-tree :draggable="is_lock" blockNode :data="treeData" :show-line="true" :default-expand-all="true" :fieldNames="{
-      key: 'deptId',
-      title: 'deptName',
-      children: 'children',
-    }" @drop="onDrop" @select="nodeClick">
+    <a-tree :draggable="is_lock" blockNode :data="treeData" :show-line="true" ref="treeRef" :fieldNames="fieldNames"
+      @drop="onDrop" @select="nodeClick">
       <template #extra="nodeData">
         <IconPlus class="icon-plus" @click="() => onAddBtnClick(nodeData)" />
+
         <a-popconfirm v-if="nodeData.parentId" content="请确认是否删除?" type="success" @ok="() => onDeleteBtnClick(nodeData)">
           <IconDelete class="icon-delete" />
         </a-popconfirm>
@@ -25,18 +23,27 @@
 </template>
 
 <script setup>
-import { ref, toRefs } from 'vue';
+import { onMounted, ref, toRefs, nextTick } from 'vue';
 const props = defineProps({
+  title: String,
+  showLock: {
+    type: Boolean,
+    default: false,
+  },
   treeData: {
     type: Array,
     default: () => [],
   },
+  fieldNames: {
+    type: Object,
+    default: () => ({ label: 'label', key: 'id', children: 'children' }),
+  },
 });
 
-const { treeData } = toRefs(props);
+const { treeData, title } = toRefs(props);
 
 const emit = defineEmits(['nodeClick', 'onAddBtnClick', 'onDrop', 'onDeleteBtnClick']);
-
+const treeRef = ref(null);
 let is_lock = ref(false);
 
 const toggleDraggable = () => {
@@ -57,6 +64,13 @@ const onDeleteBtnClick = (nodeData) => {
 const onDrop = ({ dragNode, dropNode, dropPosition }) => {
   emit('drop', { dragNode, dropNode, dropPosition });
 };
+
+onMounted(() => {
+  //TODO: 展开所有节点
+  nextTick(() => {
+    treeRef.value.expandAll();
+  });
+});
 </script>
 
 <style lang="css" scoped>

+ 22 - 14
src/modules/permission/account/DeptConfigModal.vue

@@ -2,7 +2,7 @@
   <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"
+        <a-tree :data="dept_list" v-model:checked-keys="checked_keys" v-model:expanded-keys="expand_keys"
           :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
             key: 'deptId',
             title: 'deptName',
@@ -12,15 +12,15 @@
       </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 v-for="item of check_strictly" :key="item.deptId">
+            {{ item.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 v-for="item of check_strictly_dept_role" :key="item.roleId" color="arcoblue">
+            {{ item.roleName }}
           </a-tag>
         </a-space>
       </a-card>
@@ -51,7 +51,7 @@ const emit = defineEmits(['close', 'ok']);
 // 组件内部状态
 const checked_keys = ref([]);
 // 默认展开第一节
-const expand_kdys = ref(['0']);
+const expand_keys = ref(['0']);
 const check_strictly = ref([]);
 const check_strictly_dept_role = ref([]);
 
@@ -69,14 +69,18 @@ const cancelChildSelection = (node, selected_nodes) => {
     });
   }
 }
-
+// FIXME:选择和取消选择的bug
 // 处理树形组件的勾选变化
 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);
+      // 检查是否已经存在相同的 roleId,如果不存在则添加
+      const exists = check_strictly_dept_role.value.some((role) => role.roleId === val.roleId);
+      if (!exists) {
+        check_strictly_dept_role.value.push(val);
+      }
     });
     // 添加部门
     check_strictly.value.push(o);
@@ -100,11 +104,11 @@ const handleCancel = () => {
 
 // 处理确认事件
 const editDeptHandleOk = () => {
-  emit('ok', { checked_keys: checked_keys.value });
+  emit('ok', checked_keys.value);
 };
 
 watch(() => props.dept_detail, (newVal) => {
-  if (newVal) {
+  if (newVal.length > 0) {
     newVal.forEach(val => {
       if (!checked_keys.value.includes(val.deptId)) {
         check_strictly.value.push({
@@ -112,11 +116,12 @@ watch(() => props.dept_detail, (newVal) => {
           deptName: val.deptName
         });
         checked_keys.value.push(val.deptId);
-        expand_kdys.value.push(val.deptId);
+        expand_keys.value.push(val.deptId);
       }
 
       val.roles.forEach((role) => {
-        if (!check_strictly_dept_role.value.some(item => item.roleId === role.roleId && item.deptId === val.deptId)) {
+        // 判断是否存在同样的 roleId,如果不存在则添加
+        if (!check_strictly_dept_role.value.some(item => item.roleId === role.roleId)) {
           check_strictly_dept_role.value.push({
             roleId: role.roleId,
             roleName: role.roleName,
@@ -125,8 +130,11 @@ watch(() => props.dept_detail, (newVal) => {
         }
       });
     });
-
-
+  } else {
+    checked_keys.value = [];
+    expand_keys.value = [];
+    check_strictly.value = [];
+    check_strictly_dept_role.value = [];
   }
 });
 </script>

+ 24 - 6
src/modules/permission/account/index.vue

@@ -31,8 +31,8 @@
     :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" :dept_detail="dept_detail" @ok=""
-    @cancel="handleDeptConfig(null, false)" />
+  <DeptConfigModal :visible.sync="dept_config_visible" :dept_list="dept_list" :dept_detail="dept_detail"
+    @ok="handleDeptConfigSave" @cancel="handleDeptConfig(null, false)" />
 
 </template>
 
@@ -47,7 +47,7 @@ import ViewPermission from './ViewPermission.vue';
 import DeptConfigModal from './DeptConfigModal.vue';
 
 import {
-  QueryUserList, QueryUserDetail, AddUser,
+  QueryUserList, QueryUserDeptDetail, UpdateUserDept, AddUser,
   UpdateUser, QueryUserPermission, UserStatusSwitch, ResetPassword, DeleteUser
 } from '../../../apis/permission/account';
 import { QueryAllRoleList, QueryDeptTree } from '../../../apis/permission/common';
@@ -107,6 +107,9 @@ const getUserList = async () => {
   if (code === 200) {
     // 处理部门名称和角色名称
     for (const user of data.rows) {
+      user.deptName = ''; // 初始化 deptName
+      user.roleName = ''; // 初始化 roleName
+
       if (user.dept) {
         for (let i = 0; i < user.dept.length; i++) {
           const d = user.dept[i];
@@ -120,9 +123,9 @@ const getUserList = async () => {
           user.roleName += r.roleName + (i < user.roles.length - 1 ? ', ' : '');
         }
       }
-
     }
 
+
     pagination_props.value.total = data.total;
     table_data.value = data.rows;
 
@@ -245,8 +248,8 @@ const handleView = async (record, visible) => {
 // 部门配置
 const handleDeptConfig = async (record, visible) => {
   if (record) {
-    // const { code, data } = await QueryUserDetail(record.userId);
-    const { code, data: { rows } } = await QueryUserDetail(77);
+    edit_form.userId = record.userId;
+    const { code, data: { rows } } = await QueryUserDeptDetail(record.userId);
     if (code === 200) {
       dept_detail.value = rows;
     }
@@ -254,6 +257,21 @@ const handleDeptConfig = async (record, visible) => {
   dept_config_visible.value = visible;
 }
 
+const handleDeptConfigSave = async (checked_keys) => {
+  dept_config_visible.value = false;
+  const data = {
+    userId: edit_form.userId,
+    depts: checked_keys,
+  }
+  const { code } = await UpdateUserDept(data);
+  if (code === 200) {
+    Message.success({
+      title: '部门配置',
+      content: '部门配置成功',
+    });
+    getUserList();
+  }
+}
 // 删除用户
 const handleDelete = async (userId) => {
   const { code } = await DeleteUser(userId);

+ 7 - 7
src/modules/permission/group/PermissionConfigModal.vue

@@ -35,24 +35,24 @@
 
         <div style="margin: 8px 0;">知识库功能:</div>
         <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_knowledge_list" :key="tag.id">
-            {{ tag.name }}
+          <a-tag v-for="item of checked_knowledge_list" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
         <a-divider />
 
         <div style="margin: 8px 0;">智能体功能:</div>
         <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_agents_list" :key="tag.id">
-            {{ tag.name }}
+          <a-tag v-for="item of checked_agents_list" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
         <a-divider />
 
         <div style="margin: 8px 0;">模型功能:</div>
         <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_model_list" :key="tag.id">
-            {{ tag.name }}
+          <a-tag v-for="item of checked_model_list" :key="item.id">
+            {{ item.name }}
           </a-tag>
         </a-space>
       </a-card>
@@ -94,7 +94,7 @@ const selected_tab_key = ref(tabs[0].key);
 
 // 组件内部状态
 // 默认展开第一节
-const expand_kdys = ref(['0']);
+const expand_keys = ref(['0']);
 
 const checked_knowledge = ref([]);
 const checked_knowledge_list = ref([]);

+ 5 - 7
src/modules/permission/org/index.vue

@@ -1,13 +1,11 @@
 <template>
   <div class="organization-container">
     <div class="left">
-      <TreeParts
-        :treeData="org_tree_data"
-        @nodeClick="onNodeClick"
-        @addBtnClick="onAddBtnClick"
-        @deleteBtnClick="onDeleteBtnClick"
-        @drop="onNodeDrop"
-      />
+      <TreeParts :treeData="org_tree_data" :title="'机构'" :showLock="true" :fieldNames="{
+        key: 'deptId',
+        title: 'deptName',
+        children: 'children',
+      }" @nodeClick="onNodeClick" @addBtnClick="onAddBtnClick" @deleteBtnClick="onDeleteBtnClick" @drop="onNodeDrop" />
     </div>
     <div class="right">
       <a-card title="详情" style="height: 100%">

+ 28 - 7
src/modules/permission/resource/ResourceForm.vue

@@ -10,7 +10,7 @@
       :rules="[{ required: true, message: '资源名称必填' }, { maxLength: 50, message: '长度不超过50' }]">
       <a-input v-model="resource_form.menuName" />
     </a-form-item>
-    <a-form-item field="menuName" label="资源图标">
+    <a-form-item field="icon" label="资源图标">
       <Upload :action="uploadAction" :limit="1" :url="resource_form.icon" @update:fileList="updateFileList"
         @success="handleSuccess"></Upload>
     </a-form-item>
@@ -38,7 +38,7 @@
 </template>
 
 <script setup>
-import { toRefs } from 'vue';
+import { ref, toRefs } from 'vue';
 import Upload from './Upload.vue';
 
 const uploadAction = '/api/v1/llm/upload'; // 替换为你的上传API
@@ -52,15 +52,36 @@ const props = defineProps({
     required: true,
     default: () => ({
       parentName: '',
-      status: 0,
-      deptName: '',
-      leader: '',
-      phone: '',
-      address: ''
+      status: '0',
+      menuName: '',
+      icon: '',
+      menuType: '0',
+      description: '',
+      perms: '',
+      component: '',
+
+      // 多余的参数
+      orderNum: 0,
+      path: ''
     })
   }
 });
 
+const options = [
+  {
+    value: '3',
+    label: '目录',
+  },
+  {
+    value: '0',
+    label: '菜单',
+  },
+  {
+    value: '1',
+    label: '按钮',
+  },
+];
+
 // 将 props 解构
 const { resource_form } = toRefs(props);
 

+ 174 - 0
src/modules/permission/resource/index.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="organization-container">
+    <div class="left">
+      <TreeParts :treeData="resourcetree_data" :title="'资源'" :fieldNames="{
+        key: 'menuId',
+        title: 'menuName',
+        children: 'children',
+      }" @nodeClick="onNodeClick" @addBtnClick="onAddBtnClick" @deleteBtnClick="onDeleteBtnClick" />
+    </div>
+
+    <div class="right">
+      <a-card title="详情" style="height: 100%;">
+        <ResourceForm :resource_form="resource_form" @save="orgFromSave" @reset="orgFromReset">
+          <template #actions>
+            <a-button @click="orgFromSave">保存</a-button>
+            <a-button @click="orgFromReset">重置</a-button>
+          </template>
+        </ResourceForm>
+      </a-card>
+    </div>
+
+    <Modal :visible.sync="resourcevisible" @ok="orgFromSave" @cancel="orgFromCancel" :title="'新增'">
+      <ResourceForm :resource_form="resource_form"></ResourceForm>
+    </Modal>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { Message } from '@arco-design/web-vue';
+import TreeParts from '../TreeParts.vue';
+import ResourceForm from './ResourceForm.vue';
+import Modal from '../Modal.vue';
+
+import {
+  QueryResourceTree, QueryResourceDetail, UpdateResource, CreateResource, DeleteResource
+} from '../../../apis/permission/resource';
+
+const resourcevisible = ref(false);
+const is_edit_mode = ref(true);
+const resourcetree_data = ref([]);
+
+const resource_form = reactive({
+  parentName: '',
+  status: 0,
+  menuName: '',
+  icon: '',
+  menuType: '0',
+  description: '',
+  perms: '',
+  component: '',
+});
+
+const getResourceTree = async () => {
+  const { code, data: { rows } } = await QueryResourceTree();
+  if (code == 200) {
+    resourcetree_data.value = rows;
+  }
+}
+
+
+// 树节点操作相关
+const onNodeClick = async (id) => {
+  const { data: { data } } = await QueryResourceDetail(id);
+  resource_form.resourceId = id;
+
+  resource_form.parentId = data.parentId;
+  resource_form.parentName = data.parentName;
+  resource_form.menuName = data.menuName;
+  resource_form.status = data.status;
+  resource_form.icon = data.icon;
+  resource_form.menuType = data.menuType;
+  resource_form.description = data.description;
+  resource_form.perms = data.perms;
+  resource_form.component = data.component;
+
+  // 多余的参数
+  resource_form.orderNum = 0;
+  resource_form.path = '';
+}
+const onAddBtnClick = (nodeData) => {
+  resource_form.resourceId = '';
+
+  resource_form.parentId = nodeData.menuId
+  resource_form.parentName = nodeData.menuName;
+  resource_form.menuName = '';
+  resource_form.status = '0';
+  resource_form.icon = '';
+  resource_form.menuType = '0';
+  resource_form.description = '';
+  resource_form.perms = '';
+  resource_form.component = '';
+
+  // 多余的参数
+  resource_form.orderNum = 0;
+  resource_form.path = '';
+
+  resourcevisible.value = true;
+  is_edit_mode.value = false;
+}
+
+const onDeleteBtnClick = async ({ menuId }) => {
+  const { code } = await DeleteResource(menuId);
+  if (code === 200) {
+    Message.success({
+      title: '删除',
+      content: '删除成功',
+    });
+  }
+  getResourceTree();
+}
+
+const orgFromSave = async () => {
+  const data = {
+    ...resource_form,
+    menuId: (is_edit_mode.value && resource_form.resourceId) ? resource_form.resourceId[0] : null
+  }
+
+  const apiCall = is_edit_mode.value ? UpdateResource : CreateResource;
+  const { code } = await apiCall(data);
+
+  if (code === 200) {
+    const action = is_edit_mode.value ? '编辑' : '新增';
+    Message.success({
+      title: `${action}账号`,
+      content: `${action}成功`,
+    });
+
+    getResourceTree()
+    resourcevisible.value = false;
+  }
+}
+
+const orgFromReset = () => {
+  resource_form.resourceId = null;
+  resource_form.parentName = '';
+  resource_form.status = 0;
+  resource_form.resourceName = '';
+  resource_form.leader = '';
+  resource_form.orderNum = '';
+  resource_form.address = '';
+}
+
+const orgFromCancel = () => {
+  is_edit_mode.value = true;
+  resourcevisible.value = false;
+}
+onMounted(() => {
+  getResourceTree();
+})
+</script>
+
+<style lang="css" scoped>
+.organization-container {
+  display: flex;
+  justify-content: space-between;
+  height: calc(100% - 40px);
+  width: 100%;
+}
+
+.left {
+  width: 30%;
+  height: 100%;
+  background-color: #fff;
+  /* TODO优化 使用滚动组件 */
+  overflow-y: auto;
+}
+
+.right {
+  width: 69%;
+  height: 100%;
+  background-color: #fff;
+}
+</style>

+ 10 - 11
src/modules/permission/role/DeptConfigModal.vue

@@ -2,7 +2,7 @@
   <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"
+        <a-tree :data="dept_list" v-model:checked-keys="checked_keys" v-model:expanded-keys="expand_keys"
           :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
             key: 'deptId',
             title: 'deptName',
@@ -12,8 +12,8 @@
       </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 v-for="item of check_strictly" :key="item.deptId">
+            {{ item.deptName }}
           </a-tag>
         </a-space>
       </a-card>
@@ -22,7 +22,7 @@
 </template>
 
 <script setup>
-import { ref, toRefs, onMounted } from 'vue';
+import { ref, toRefs, watch } from 'vue';
 
 // 定义组件的 props 和 emits
 const props = defineProps({
@@ -44,7 +44,7 @@ const emit = defineEmits(['close', 'ok']);
 // 组件内部状态
 const checked_keys = ref([]);
 // 默认展开第一节
-const expand_kdys = ref(['0']);
+const expand_keys = ref(['0']);
 const check_strictly = ref([]);
 
 // 递归取消所有子节点的选择
@@ -85,19 +85,18 @@ const handleCancel = () => {
 
 // 处理确认事件
 const editDeptHandleOk = () => {
-  emit('ok', { checked_keys: checked_keys.value });
+  emit('ok', checked_keys.value);
 };
 
-onMounted(() => {
-  // 处理传入的部门数据
-  if (record?.dept) {
-    record.dept.forEach((val) => {
+watch(() => props.record, (newVal) => {
+  if (newVal?.dept) {
+    newVal.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);
+      expand_keys.value.push(val.deptId);
     });
   }
 });

+ 23 - 142
src/modules/permission/role/PermissionConfigModal.vue

@@ -1,81 +1,30 @@
 <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 v-if="selected_tab_key === '1'" :data="menu_list" v-model="checked_menu"
-          v-model:expanded-keys="expand_kdys" :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
+        <a-tree :data="menu_list" v-model:checked-keys="checked_menu" v-model:expanded-keys="expand_keys"
+          :checkable="true" :show-line="true" @check="onCheck" :fieldNames="{
             key: 'menuId',
             title: 'menuName',
             children: 'children',
           }">
         </a-tree>
 
-        <a-checkbox-group v-if="selected_tab_key === '2'" v-model="checked_knowledge" @change="onCheckKnowledge">
-          <a-checkbox v-for="(knowledg, index) of knowledge_list" :value="knowledg.id" :lable="knowledg.name"
-            style="width:150px">
-            {{ knowledg.name }}
-          </a-checkbox>
-        </a-checkbox-group>
-
-        <a-checkbox-group v-if="selected_tab_key === '3'" v-model="checked_agents" @change="onCheckAgents">
-          <a-checkbox v-for="(agents, index) of agents_list" :value="agents.id" :lable="agents.name"
-            style="width:150px">
-            {{ agents.name }}
-          </a-checkbox>
-        </a-checkbox-group>
-
-        <a-checkbox-group v-if="selected_tab_key === '4'" v-model="checked_model" @change="onCheckModel">
-          <a-checkbox v-for="(model, index) of model_list" :value="model.id" :lable="model.name" style="width:150px">
-            {{ model.name }}
-          </a-checkbox>
-        </a-checkbox-group>
-
       </a-card>
-      <a-card class="card-body" title="角色所有权限">
+      <a-card class="card-body" title="角色菜单权限">
         <div style="margin: 8px 0;">菜单功能:</div>
         <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_menu_list" :key="tag.menuId">
+          <a-tag v-for="tag of checked_menu_list" :key="tag.menuId">
             {{ tag.menuName }}
           </a-tag>
         </a-space>
-        <a-divider />
-
-        <div style="margin: 8px 0;">知识库功能:</div>
-        <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_knowledge_list" :key="tag.id">
-            {{ tag.name }}
-          </a-tag>
-        </a-space>
-        <a-divider />
-
-        <div style="margin: 8px 0;">智能体功能:</div>
-        <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_agents_list" :key="tag.id">
-            {{ tag.name }}
-          </a-tag>
-        </a-space>
-        <a-divider />
-
-        <div style="margin: 8px 0;">模型功能:</div>
-        <a-space wrap>
-          <a-tag v-for="(tag, index) of checked_model_list" :key="tag.id">
-            {{ tag.name }}
-          </a-tag>
-        </a-space>
       </a-card>
     </div>
   </a-modal>
 </template>
 
 <script setup>
-import { ref, toRefs, onMounted, computed } from 'vue';
+import { ref, toRefs, watch } from 'vue';
 
 // 定义组件的 props 和 emits
 const props = defineProps({
@@ -84,45 +33,20 @@ const props = defineProps({
     type: Array,
     default: () => []
   },
-  knowledge_list: {
-    type: Array,
-    default: () => []
-  },
-  agents_list: {
-    type: Array,
-    default: () => []
-  },
-  model_list: {
-    type: Array,
-    default: () => []
-  },
   record: Object
 });
 
-const { visible, menu_list, knowledge_list, agents_list, model_list, record } = toRefs(props);
+const { visible, menu_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 expand_kdys = ref(['0']);
+const expand_keys = ref(['0']);
 
 const checked_menu = ref([]);
 const checked_menu_list = ref([]);
-const checked_knowledge = ref([]);
-const checked_knowledge_list = ref([]);
-const checked_agents = ref([]);
-const checked_agents_list = ref([])
-const checked_model = ref([]);
-const checked_model_list = ref([])
 
 
 // 递归取消所有子节点的选择
@@ -183,39 +107,6 @@ const onCheck = (newCheckedKeys, event) => {
   }
 };
 
-const onCheckboxCheck = (newCheckedKeys, event, collection) => {
-  let o = {
-    id: event.target.value,
-    name: event.target.labels[0].innerText,
-  };
-  if (event.target.checked) {
-    collection.value.push(o);
-  } else {
-    collection.value.forEach((val, idx, array) => {
-      if (val['id'] == event.target.value) {
-        collection.value.splice(idx, 1);
-        return true;
-      }
-    });
-  }
-};
-
-const onCheckKnowledge = (newCheckedKeys, event) => {
-  onCheckboxCheck(newCheckedKeys, event, checked_knowledge_list);
-
-};
-const onCheckAgents = (newCheckedKeys, event) => {
-  onCheckboxCheck(newCheckedKeys, event, checked_agents_list);
-};
-const onCheckModel = (newCheckedKeys, event) => {
-  onCheckboxCheck(newCheckedKeys, event, checked_model_list);
-};
-
-const handleTabChange = (key) => {
-  const currentTab = tabs.find(tab => tab.key === key);
-  emit('getActiveTab', currentTab);
-  selected_tab_key.value = key;
-};
 // 处理取消事件
 const handleCancel = () => {
   emit('cancel');
@@ -223,35 +114,25 @@ const handleCancel = () => {
 
 // 处理确认事件
 const editDeptHandleOk = () => {
-  emit('ok', {
-    checkedKeys: {
-      menu: checked_menu.value,
-      knowledge: checked_knowledge.value,
-      agents: checked_agents.value,
-      model: checked_model.value
-    }
-  });
+  emit('ok', checked_menu.value);
 };
 
-const getCurrentTabTitle = computed(() => {
-  const currentTab = tabs.find(tab => tab.key === selected_tab_key.value);
-  return currentTab ? currentTab.title : '菜单';
-});
-
-onMounted(() => {
-  // 处理传入的部门数据
-  // TODO功能 回显
-  // if (record?.dept) {
-  //   record.dept.forEach((val) => {
-  //     checked_menu_list.value.push({
-  //       menuId: val.menuId,
-  //       menuName: val.menuName
-  //     });
-  //     checked_menu.value.push(val.menuId);
-  //     expand_kdys.value.push(val.menuId);
-  //   });
-  // }
 
+watch(() => props.record, (newVal) => {
+  if (newVal?.resources) {
+    newVal.resources.forEach((val) => {
+      checked_menu_list.value.push({
+        menuId: val.menuId,
+        menuName: val.menuName
+      });
+      checked_menu.value.push(val.menuId);
+      expand_keys.value.push(val.menuId);
+    });
+  } else {
+    checked_menu_list.value = [];
+    checked_menu.value = [];
+    expand_keys.value = ['0'];
+  }
 });
 </script>
 

+ 50 - 48
src/modules/permission/role/index.vue

@@ -23,13 +23,11 @@
     <RoleForm :edit_form="edit_form" :is_edit_mode="is_edit_mode" />
   </Modal>
 
-  <!-- TODO -->
-  <PermissionConfigModal :visible.sync="permission_config_visible" :menu_list="menu_list"
-    :knowledge_list="knowledge_list" :agents_list="agents_list" @getActiveTab="handlePermissionModalTab"
+  <PermissionConfigModal :visible.sync="permission_config_visible" :menu_list="menu_list" :record="current_record"
     @ok="handlePermissionConfigSave" @cancel="handlePermissionConfig(null, false)" />
 
-  <DeptConfigModal :visible.sync="dept_config_visible" :dept_list="dept_list" :record="current_record" @ok=""
-    @cancel="handleDeptConfig(null, false)" />
+  <DeptConfigModal :visible.sync="dept_config_visible" :dept_list="dept_list" :record="current_record"
+    @ok="handleDeptConfigSave" @cancel="handleDeptConfig(null, false)" />
 
 </template>
 
@@ -45,7 +43,7 @@ import DeptConfigModal from './DeptConfigModal.vue';
 
 import { QueryRoleList, RoleStatusSwitch, DeleteRole, CreateRole, UpdateRole } from '../../../apis/permission/role';
 import { QueryMenuTree } from '../../../apis/permission/resource';
-import { QueryAllKnowledgeList, QueryAllDialogList } from '../../../apis/permission/common';
+import { QueryDeptTree } from '../../../apis/permission/common';
 
 const keywords = ref('');
 // 表格相关
@@ -75,8 +73,7 @@ const edit_form = reactive({
 // 权限配置相关
 const permission_config_visible = ref(false);
 const menu_list = ref([]);
-const knowledge_list = ref([]);
-const agents_list = ref([]);
+
 
 // 部门配置相关
 const dept_config_visible = ref(false);
@@ -287,39 +284,32 @@ const handleEdit = (record, visible) => {
 const handlePermissionConfig = async (record, visible) => {
   if (record) {
     edit_form.roleId = record.roleId;
-    getConfigList('1');
+    current_record.value = record;
   }
   permission_config_visible.value = visible;
 }
 
-const handlePermissionModalTab = (tab) => {
-  if (tab.key) getConfigList(tab.key);
-}
-
-const handlePermissionConfigSave = async (data) => {
+const handlePermissionConfigSave = async (checked_menu) => {
   permission_config_visible.value = false;
-  console.log('data', data)
-  const { menu, knowledge, agents, model } = data.checkedKeys;
-  // TODO询问 接口
-  // const params = {
-  //   roleId: edit_form.roleId,
-  //   menuIds: menu_list.value.map((item) => item.menuId).join(','),
-  //   knowledgeIds: knowledge_list.value.map((item) => item.knowledgeId).join(','),
-  //   agentIds: agents_list.value.map((item) => item.agentId).join(','),
-  // };
-  // const { code } = await UpdateRole(params);
-  // if (code === 200) {
-  //   Message.success({
-  //     title: '权限配置',
-  //     content: '权限配置成功',
-  //   });
-  // }
+  const params = {
+    roleId: edit_form.roleId,
+    menuIds: checked_menu,
+    editType: 2,
+  };
+  const { code } = await UpdateRole(params);
+  if (code === 200) {
+    Message.success({
+      title: '权限配置',
+      content: '权限配置成功',
+    });
+  }
 }
 
 // 部门配置
 const handleDeptConfig = async (record, visible) => {
   if (record) {
-    // current_record.value = record;
+    edit_form.roleId = record.roleId;
+    current_record.value = record;
     // const { code, data: { rows } } = await QueryUserDept(record.roleId);
     // if (code === 200) {
     //   console.log('rows', rows)
@@ -329,6 +319,22 @@ const handleDeptConfig = async (record, visible) => {
   dept_config_visible.value = visible;
 }
 
+const handleDeptConfigSave = async (dept_ids) => {
+  dept_config_visible.value = false;
+  const params = {
+    roleId: edit_form.roleId,
+    depts: dept_ids,
+    editType: 3,
+  };
+  const { code } = await UpdateRole(params);
+  if (code === 200) {
+    Message.success({
+      title: '部门配置',
+      content: '部门配置成功',
+    });
+  }
+}
+
 // 删除用户
 const handleDelete = async (roleId) => {
   const { code } = await DeleteRole(roleId);
@@ -366,29 +372,25 @@ const tableSizeChange = (size) => {
   table_size.value = size;
 }
 
-const getConfigList = async (tabsKey) => {
-  if (tabsKey === '1') {
-    const { code, data: { rows } } = await QueryMenuTree();
-    if (code === 200) {
-      menu_list.value = rows;
-    }
-  }
-  if (tabsKey === '2') {
-    const { code, data: { rows } } = await QueryAllKnowledgeList();
-    if (code === 200) {
-      knowledge_list.value = rows;
-    }
+// 获取菜单树
+const getMenuTree = async () => {
+  const { code, data: { rows } } = await QueryMenuTree();
+  if (code === 200) {
+    menu_list.value = rows;
   }
-  if (tabsKey === '3') {
-    const { code, data: { rows } } = await QueryAllDialogList();
-    if (code === 200) {
-      agents_list.value = rows;
-    }
+}
+// 获取部门树
+const getDeptTree = async () => {
+  const { code, data: { rows } } = await QueryDeptTree();
+  if (code == 200) {
+    dept_list.value = rows;
   }
 }
 
 onMounted(() => {
   gerRoleList()
+  getMenuTree()
+  getDeptTree()
 })
 </script>
 

+ 4 - 1
src/views/PermissionResource.vue

@@ -1,11 +1,14 @@
 <template>
   <ContentContainer>
     <BreadCrumbMenu :current_label="'资源'" />
-    <div class="permission-resource">权限管理: 资源</div>
+
+    <ResourcePage />
   </ContentContainer>
 </template>
 
 <script setup>
 import ContentContainer from './layout/ContentContainer.vue';
 import BreadCrumbMenu from './common/BreadCrumbMenu.vue';
+
+import ResourcePage from '../modules/permission/resource/index.vue';
 </script>

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů