Pārlūkot izejas kodu

feat(智能体):标签管理接口完成,rgflow弹框

hanyang 1 gadu atpakaļ
vecāks
revīzija
fda03b44f0

+ 1 - 1
public/i18n/lang-zh.json

@@ -56,7 +56,7 @@
     "document": "文档",
     "edit": "编辑",
     "delete": "删除",
-    "online": "线",
+    "online": "线",
     "offline": "下线",
     "createUser": "创建用户",
     "addedModel": "添加了的模型",

+ 61 - 0
src/apis/intelligent.js

@@ -12,3 +12,64 @@ export function getDialogList(current, pageSize, keyword) {
     method: 'get'
   });
 }
+/**
+ * 获取标签列表
+ * @param {string} keyword 搜索关键字
+ * @param {number} labelType 标签类型 1-默认智能体
+ * @param {number} objectId 智能体ID
+ * */
+export function getLabelList(keyword, labelType, objectId = undefined) {
+  const params = {
+    keyword,
+    labelType
+  };
+  if (objectId) {
+    params.objectId = objectId;
+  }
+  return request({
+    url: `/api/label/list`,
+    method: 'get',
+    params
+  });
+}
+/**
+ * 添加标签
+ * @param {string} labelName 标签名称
+ * @param {number} labelType 标签类型 1-默认智能体
+ * */
+export function addLabel(labelName, labelType = 1) {
+  return request({
+    url: `/api/label/add_label`,
+    method: 'post',
+    data: {
+      labelName,
+      labelType
+    }
+  });
+}
+/**
+ * 删除标签
+ * @param {number} labelId 标签ID
+ * */
+export function deleteLabel(labelId) {
+  return request({
+    url: `/api/label/delete_label?labelId=${labelId}`,
+    method: 'delete'
+  });
+}
+/**
+ * 添加标签到智能体
+ * @param {Array} labelIdList 标签ID列表
+ * @param {number} objectId 智能体ID
+ * @param {number} objectId 智能体ID
+ * */
+export function addLabelToDialog(labelIdList, objectId) {
+  return request({
+    url: `/api/label/sign_label`,
+    method: 'post',
+    data: {
+      labelIdList,
+      objectId
+    }
+  });
+}

+ 2 - 0
src/base/event-bus.js

@@ -11,3 +11,5 @@ export const bus_themeChange = new EventBus('theme_change');
 
 export const bus_knowledgeSearch = new EventBus('knowledge_search');
 export const bus_intelligentSearch = new EventBus('intelligent_search');
+
+export const bus_intelligentEdit = new EventBus('intelligent_edit');

+ 9 - 4
src/components/wujie/WJExample.vue

@@ -1,9 +1,9 @@
 <template>
-  <div>项目的首页</div>
+  <!-- <div>项目的首页</div>
   <div>
     <div @click="handleChangeApp(1)">应用1</div>
     <div @click="handleChangeApp(2)">应用2</div>
-  </div>
+  </div> -->
 
   <WujieVue
     width="100%"
@@ -19,9 +19,14 @@
 <script setup>
 import { ref } from 'vue';
 
-const props = defineProps({});
+const props = defineProps({
+  url: {
+    type: String,
+    request: true
+  }
+});
 
-const url = ref('http://localhost:4001/');
+// const url = ref('http://localhost:4001/');
 
 const app1_plug = [
   {

+ 17 - 0
src/modules/dify/MessageBody.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="body-container">
+    <WujieVue width="100%" height="100%" name="sub-app" :url="defaultUrl" sync></WujieVue>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+
+const defaultUrl = ref('http://192.168.20.116');
+</script>
+<style scoped lang="css">
+.body-container {
+  margin-top: 15px;
+  height: calc(100% - 55px);
+}
+</style>

+ 15 - 0
src/modules/dify/TopSetting.vue

@@ -0,0 +1,15 @@
+<template>
+  <a-breadcrumb class="breadcrumb">
+    <a-breadcrumb-item><icon-apps /></a-breadcrumb-item>
+    <a-breadcrumb-item>智能体管理</a-breadcrumb-item>
+    <a-breadcrumb-item>智能体详情</a-breadcrumb-item>
+  </a-breadcrumb>
+</template>
+
+<script setup></script>
+
+<style lang="css" scoped>
+.breadcrumb {
+  height: 40px;
+}
+</style>

+ 23 - 28
src/modules/intelligent/LabelSelected.vue

@@ -11,8 +11,8 @@
         <icon-search />
       </template>
     </a-input>
-    <a-checkbox-group v-model="checkedList" direction="vertical">
-      <a-checkbox v-for="item in labelList" :key="item.id" :value="item.value">{{ item.name }}</a-checkbox>
+    <a-checkbox-group v-model="checkedList" direction="vertical" @change="handleToAdd">
+      <a-checkbox v-for="item in label_list" :key="item.labelId" :value="item.labelId">{{ item.labelName }}</a-checkbox>
     </a-checkbox-group>
     <div class="footer">
       <div class="label-manage" @click="toManageTags"><icon-tags /> 管理标签</div>
@@ -21,39 +21,34 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, computed, watch } from 'vue';
+import { funcDebounce } from '../../utils/utils';
 
-const emit = defineEmits(['openManageTags']);
-
-const searchText = ref('');
-const checkedList = ref([]);
-const labelList = ref([
-  {
-    id: 1,
-    name: '标签1',
-    value: '1'
-  },
-  {
-    id: 2,
-    name: '标签2',
-    value: '2'
-  },
-  {
-    id: 3,
-    name: '标签3',
-    value: '3'
+const props = defineProps({
+  labels: {
+    type: Array,
+    default: () => []
   },
-  {
-    id: 4,
-    name: '标签4',
-    value: '4'
+  addedLabels: {
+    type: Array,
+    default: () => []
   }
-]);
+});
+const emit = defineEmits(['openManageTags', 'toSearchLabels', 'addLabelToTarget']);
 
-const handleSearch = () => {};
+const searchText = ref('');
+const checkedList = ref([...props.addedLabels.map((item) => item.labelId)]);
+const label_list = computed(() => props.labels);
+
+const handleSearch = funcDebounce(() => {
+  emit('toSearchLabels', searchText.value);
+}, 300);
 const toManageTags = () => {
   emit('openManageTags');
 };
+const handleToAdd = (checkedList) => {
+  emit('addLabelToTarget', checkedList);
+};
 </script>
 <style scoped lang="css">
 .footer {

+ 42 - 27
src/modules/intelligent/ManageTags.vue

@@ -35,45 +35,50 @@
         </template>
         创建新标签
       </a-tag>
-      <a-tag v-for="tag in tags" :key="tag.name">
-        {{ tag.name }}
-        <icon-delete @click="handleDelete(tag)" style="cursor: pointer" />
+      <a-tag v-for="tag in tags" :key="tag.labelId">
+        {{ tag.labelName }} &nbsp;&nbsp;
+        <a-popconfirm type="warning" content="是否确认删除该标签?" @ok="handleDelete(tag)">
+          <icon-delete style="cursor: pointer" />
+        </a-popconfirm>
       </a-tag>
     </a-space>
   </a-modal>
 </template>
 
 <script setup>
-import { ref, computed } from 'vue';
+import { ref, computed, nextTick } from 'vue';
+import { Message } from '@arco-design/web-vue';
+import { addLabel, deleteLabel } from '../../apis/intelligent';
 
 const props = defineProps({
   visible: {
     type: Boolean,
     default: false
+  },
+  labels: {
+    type: Array,
+    default: () => []
   }
 });
-const emit = defineEmits(['update:visible']);
+const emit = defineEmits(['update:visible', 'updateLabels']);
 
-const tags = ref([
-  {
-    name: '标签1'
-  },
-  {
-    name: '标签2'
-  },
-  {
-    name: '标签3'
-  },
-  {
-    name: '标签4'
-  }
-]);
+const tags = computed(() => props.labels);
 const input_ref = ref(null);
 const show_input = ref(false);
 const input_val = ref('');
 const show_model = computed(() => props.visible);
-const handleDelete = (tag) => {
-  tags.value = tags.value.filter((t) => t.name !== tag.name);
+const handleDelete = async (tag) => {
+  try {
+    const res = await deleteLabel(tag.labelId);
+    if (res.code == 200) {
+      // 刷新标签列表
+      emit('updateLabels');
+    } else {
+      Message.error(res.msg);
+    }
+  } catch (err) {
+    console.log(err);
+  }
 };
 const handleCancel = () => {
   emit('update:visible', false);
@@ -89,16 +94,26 @@ const handleEdit = () => {
   });
 };
 
-const handleAdd = () => {
-  if (input_val.value) {
-    tags.value.push({ name: input_val.value });
-    input_val.value = '';
+const handleAdd = async () => {
+  if (input_val.value.trim().length) {
+    try {
+      const res = await addLabel(input_val.value.trim());
+      if (res.code == 200) {
+        // 刷新标签列表
+        emit('updateLabels');
+      } else {
+        Message.error(res.msg);
+      }
+      input_val.value = '';
+    } catch (err) {
+      console.log(err);
+    }
   }
   show_input.value = false;
 };
 </script>
 <style scoped lang="css">
-.arco-form-item-content-wrapper {
-  border-color: var(--color-neutral-4);
+.arco-input-wrapper {
+  background-color: var(--color-neutral-3);
 }
 </style>

+ 78 - 16
src/modules/intelligent/MessageBody.vue

@@ -9,19 +9,31 @@
         </a-switch>
       </template>
       <template #labelBtn>
-        <!-- <div class="label-btn"> -->
-        <a-dropdown ref="dropdown" v-model="show_down" class="label-btn" position="bl" :popup-max-height="false">
-          <a-button>
-            <icon-subscribe />
-            {{ $t('management.addLabelBtn') }}</a-button
+        <a-dropdown
+          ref="dropdown"
+          v-model="show_down"
+          position="bl"
+          :popup-max-height="false"
+          @popup-visible-change="changeDropdownVisible($event, item.id)"
+        >
+          <a-button v-if="!item.label.length" class="label-btn">
+            <icon-subscribe /> &nbsp;{{ $t('management.addLabelBtn') }}</a-button
+          >
+          <a-button v-else class="label-btn">
+            <icon-subscribe />&nbsp;{{ item.label.map((it) => it.labelName).join(',') }}</a-button
           >
           <template #content>
             <div class="label-add">
-              <LabelSelected @openManageTags="openManageTags"></LabelSelected>
+              <LabelSelected
+                :labels="label_list"
+                :addedLabels="item.label"
+                @openManageTags="openManageTags"
+                @toSearchLabels="toSearchLabels"
+                @addLabelToTarget="addLabelToTarget($event, item.id)"
+              ></LabelSelected>
             </div>
           </template>
         </a-dropdown>
-        <!-- </div> -->
       </template>
       <template #bottom>
         <div class="bottom">
@@ -30,7 +42,7 @@
             <icon-more :style="{ fontSize: '20px' }" />
           </div>
           <div class="tools" v-if="!item.online">
-            <icon-settings :style="{ fontSize: '20px', cursor: 'pointer' }" @click="editIntelligent" />
+            <icon-settings :style="{ fontSize: '20px', cursor: 'pointer' }" @click="editIntelligent(item)" />
             <icon-delete :style="{ fontSize: '20px', cursor: 'pointer' }" @click="deleteIntelligent" />
           </div>
         </div>
@@ -41,7 +53,7 @@
       <div v-if="scroll_bottom && pages.current >= total_pages">{{ $t('management.noMoreData') }}</div>
     </div>
   </div>
-  <ManageTags v-model:visible="visible"></ManageTags>
+  <ManageTags v-model:visible="visible" :labels="label_list" @updateLabels="updateLabels"></ManageTags>
 </template>
 
 <script setup>
@@ -51,7 +63,8 @@ import ManageTags from './ManageTags.vue';
 import { getImageIntelligent } from '../../views/manage/common';
 import LabelSelected from './LabelSelected.vue';
 import { getDialogList } from '../../apis/intelligent';
-import { bus_intelligentSearch } from '../../base/event-bus';
+import { bus_intelligentSearch, bus_intelligentEdit } from '../../base/event-bus';
+import { getLabelList, addLabelToDialog } from '../../apis/intelligent';
 
 const moke_data = ref([
   // {
@@ -72,6 +85,8 @@ const total_pages = ref(5);
 const body_container = ref(null);
 const scroll_bottom = ref(false);
 const search_text = ref('');
+const search_label = ref('');
+const label_list = ref([]);
 const dropdown = ref(null);
 const visible = ref(false);
 
@@ -109,14 +124,55 @@ const openManageTags = () => {
   visible.value = true;
 };
 
-const editIntelligent = () => {
-  // TODO:微前端执行编辑页面
-  console.log('editIntelligent');
+const editIntelligent = (item) => {
+  bus_intelligentEdit.emit(item);
 };
 const deleteIntelligent = () => {
   console.log('deleteIntelligent');
 };
 
+// 获取标签列表
+const getLabels = async () => {
+  try {
+    const res = await getLabelList(search_label.value, 1);
+    console.log(res);
+    if (res.code == 200) {
+      label_list.value = [...res.data.rows];
+    }
+  } catch (err) {
+    console.log(err);
+  }
+};
+const updateLabels = () => {
+  search_label.value = '';
+  getLabels();
+};
+const toSearchLabels = (text) => {
+  search_label.value = text;
+  getLabels();
+};
+// 添加标签到智能体
+const addLabelToTarget = async (label_id_list, target_id) => {
+  console.log(label_id_list, target_id);
+  try {
+    await addLabelToDialog(label_id_list, target_id);
+  } catch (err) {
+    console.log(err);
+  }
+};
+// 当下拉框隐藏时,重新更新对应的智能体数据
+const changeDropdownVisible = async (visible, id) => {
+  if (!visible) {
+    const res = await getLabelList(search_label.value, 1, id);
+    if (res.code == 200) {
+      moke_data.value.forEach((item) => {
+        if (item.id === id) {
+          item.label = [...res.data.rows];
+        }
+      });
+    }
+  }
+};
 bus_intelligentSearch.on((text) => {
   search_text.value = text;
   moke_data.value = [];
@@ -126,6 +182,7 @@ bus_intelligentSearch.on((text) => {
 
 onMounted(() => {
   getDialog();
+  getLabels();
   body_container.value.addEventListener('scroll', scrollHandler);
 });
 
@@ -147,13 +204,18 @@ onBeforeUnmount(() => {
   display: none;
 }
 .label-btn {
-  display: flex;
-  gap: 10px;
-  padding: 8px 16px;
+  text-align: left;
+  padding: 8px 10px;
   background-color: var(--color-neutral-2);
   color: var(--color-neutral-6);
   font-size: 12px;
   cursor: pointer;
+  width: 100%;
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-line-clamp: 1;
+  -webkit-box-orient: vertical;
+  text-overflow: ellipsis;
 }
 .label-add {
   width: 240px;

+ 12 - 3
src/modules/intelligent/ModalAddIntel.vue

@@ -39,7 +39,7 @@ const props = defineProps({
     default: false
   }
 });
-const emit = defineEmits(['update:visible']);
+const emit = defineEmits(['update:visible', 'add_intelligent', 'createRAGFLOWIntelligent']);
 
 const showModal = computed(() => props.visible);
 
@@ -51,8 +51,17 @@ const handleCancel = () => {
 
 const addNewIntelligent = (item) => {
   // TODO: 根据item中的type区分新建智能体的类型
-  emit('update:visible', false);
-  emit('add-intelligent', item);
+  if (item.type === 'RAGFLOW') {
+    // TODO: 新建RAGFLOW智能体
+    emit('update:visible', false);
+    emit('createRAGFLOWIntelligent');
+  } else if (item.type === 'DIFY_1') {
+    // TODO: 新建ARGFLOW智能体
+    emit('update:visible', false);
+    emit('add_intelligent', item);
+  } else if (item.type === 'DIFY_2') {
+  } else if (item.type === 'DIFY_3') {
+  }
 };
 </script>
 <style scoped lang="css">

+ 8 - 3
src/modules/intelligent/ModelAddItem.vue

@@ -1,6 +1,6 @@
 <template>
   <a-modal
-    v-model:visible="showModal"
+    v-model:visible="show_modal"
     :align-center="false"
     top="15vh"
     @cancel="handleCancel"
@@ -43,8 +43,13 @@ const form = ref({
 
 const emit = defineEmits(['update:show_model_item', 'select_icon']);
 
-const showModal = computed(() => props.show_model_item);
-const handleBeforeOk = (done) => {};
+const show_modal = computed(() => props.show_model_item);
+const handleBeforeOk = (done) => {
+  // TODO: 调新建接口
+  // TODO: 进入到对应的页面
+  
+  done();
+};
 const handleCancel = () => {
   form.value.name = '';
   emit('update:show_model_item', false);

+ 88 - 5
src/modules/intelligent/TopSetting.vue

@@ -18,28 +18,65 @@
       {{ $t('management.createIntelligent') }}
     </a-button>
   </a-space>
-  <ModalAddIntel v-model:visible="visible" @add-intelligent="addIntelligent"></ModalAddIntel>
+  <ModalAddIntel
+    v-model:visible="visible"
+    @add_intelligent="addIntelligent"
+    @createRAGFLOWIntelligent="createRAGFLOWIntelligent"
+  ></ModalAddIntel>
   <ModelAddItem
     v-model:show_model_item="show_model_item"
     :target_icon="target_icon"
     @select_icon="selectIcon"
   ></ModelAddItem>
   <ModelSelectIcon v-model:show_select_icon="show_select_icon" @sendIcon="sendIcon"></ModelSelectIcon>
+  <AgentConfig
+    v-model:show_dify_config="show_dify_config"
+    ref="editAgentKuai"
+    :typeAngint="add_type"
+    :formData="itemObj"
+    @queryList="queryList"
+  ></AgentConfig>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, reactive } from 'vue';
 import ModalAddIntel from './ModalAddIntel.vue';
 import ModelAddItem from './ModelAddItem.vue';
 import ModelSelectIcon from './ModalSelectIcon.vue';
+import AgentConfig from './rgflow/agentConfig.vue';
 import { funcDebounce } from '../../utils/utils';
-import { bus_intelligentSearch } from '../../base/event-bus';
+import { bus_intelligentSearch, bus_intelligentEdit } from '../../base/event-bus';
 
 const searchText = ref('');
 const visible = ref(false);
 const show_model_item = ref(false);
 const show_select_icon = ref(false);
 const target_icon = ref('../../assets/image/management/intelligent-frame1.png');
+// 当前新建智能体类型
+const target_intell_type = ref('');
+// ragflow配置
+const show_dify_config = ref(false);
+const editAgentKuai = ref(null);
+let add_type = ref('add');
+let itemObj = reactive({});
+const queryList = async (params = {}) => {
+  // setLoading(true);
+  // try {
+  //   const { data } = await querydialogList(params);
+  //   console.log(data, 'data');
+  //   agentList.value = data.map((item) => {
+  //     return {
+  //       ...item,
+  //       off: true,
+  //     };
+  //   });
+  // } catch (err) {
+  //   // you can report use errorHandler or other
+  // } finally {
+  //   setLoading(false);
+  // }
+};
+
 // 搜索
 const handleSearch = funcDebounce(
   () => {
@@ -52,9 +89,31 @@ const handleSearch = funcDebounce(
 const handleCreate = () => {
   visible.value = true;
 };
-
+// 创建Dify智能体
+const createRAGFLOWIntelligent = () => {
+  console.log('createRAGFLOWIntelligent');
+  show_dify_config.value = true;
+};
+const closeAgentConfig = () => {
+  show_dify_config.value = false;
+};
 const addIntelligent = (data) => {
-  show_model_item.value = true;
+  //根据智能体类型,打开不同的配置面板data.type
+  switch (data.type) {
+    case 'DIFY_1':
+      // TODO: 工作流助手
+      show_model_item.value = true;
+      target_intell_type.value = data.type;
+      break;
+    case 'DIFY_2':
+      // TODO: 智能Agent
+      break;
+    case 'DIFY_3':
+      // TODO: 工作流应用
+      break;
+    default:
+      console.log('暂不支持该智能体类型');
+  }
 };
 const selectIcon = () => {
   show_select_icon.value = true;
@@ -63,6 +122,30 @@ const sendIcon = (icon) => {
   console.log('sendIcon', icon);
   target_icon.value = icon;
 };
+/**
+ * 监听编辑智能体事件
+ * @param {Object} item 编辑智能体对象
+ * case 1: 编辑ragflow智能体
+ * case 2: 编辑bs智能体
+ * case 3: 编辑basic智能体
+ * case 4: 编辑Dify智能体
+ * */
+bus_intelligentEdit.on((item) => {
+  console.log('bus_intelligentEdit', item);
+  switch (item.agentType) {
+    case '1':
+      // TODO: 编辑rgflow智能体
+      // itemObj = item;
+      show_dify_config.value = true;
+      break;
+    case '4':
+      add_type.value = 'edit';
+      console.log('item1', item);
+      break;
+    default:
+      console.log('暂不支持该智能体类型');
+  }
+});
 </script>
 <style scoped lang="scss"></style>
 <style>

+ 605 - 0
src/modules/intelligent/rgflow/AgentConfig.vue

@@ -0,0 +1,605 @@
+<template>
+  <a-modal
+    v-model:visible="visible"
+    title="智能体配置"
+    @before-open="handleOpened"
+    @cancel="handleCancel"
+    :ok-loading="loading"
+    @before-ok="handleBeforeOk"
+    :footer="true"
+    title-align="start"
+    width="600px"
+  >
+    <a-row class="">
+      <a-col :span="24">
+        <a-tabs type="capsule" size="large" :active-key="activeKey" @change="handleTabChange">
+          <a-tab-pane key="1" title="助理设置">
+            <a-divider style="margin-bottom: 20px; margin-top: 0" />
+            <div style="width: 100%" :style="{ height: height }">
+              <a-form
+                ref="formRef"
+                :rules="rules"
+                :model="form"
+                @submit="handleSubmit"
+                :style="{ width: '90%', margin: '0 auto' }"
+              >
+                <a-form-item field="name" label="智能体名称">
+                  <a-input v-model="form.name" placeholder="请输入名称" />
+                </a-form-item>
+                <a-form-item label="智能体图标">
+                  <a-space direction="vertical" :style="{ width: '100%' }">
+                    <Upload
+                      v-if="avatarShow"
+                      :action="uploadAction"
+                      :limit="1"
+                      :url="form.icon ? httpUrl + form.icon : ''"
+                      @update:fileList="updateFileList"
+                      @success="handleSuccess"
+                    ></Upload>
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="空回复">
+                  <a-input v-model="form.prompt_config.empty_response" placeholder="" />
+                </a-form-item>
+                <a-form-item label="设置开场白">
+                  <a-textarea v-model="form.prompt_config.prologue" placeholder="" style="height: 100px" />
+                </a-form-item>
+                <a-form-item label="显示引文">
+                  <a-space direction="vertical">
+                    <a-switch v-model="form.prompt_config.quote" size="small" />
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="Self-RAG">
+                  <a-space direction="vertical">
+                    <a-switch v-model="form.prompt_config.self_rag" size="small" />
+                  </a-space>
+                </a-form-item>
+                <a-form-item field="kb_ids" label="知识库">
+                  <a-select v-model="form.kb_ids" placeholder="请选择 ..." multiple>
+                    <a-option v-for="item in tabs" :key="item.id" :value="item.id">
+                      {{ item.name }}
+                    </a-option>
+                  </a-select>
+                </a-form-item>
+              </a-form>
+            </div>
+          </a-tab-pane>
+          <a-tab-pane key="2" title="提示引擎">
+            <a-divider style="margin-bottom: 20px; margin-top: 0" />
+            <a-scrollbar style="width: 100%; overflow: auto" :style="{ height: height }">
+              <a-form
+                ref="formRef1"
+                :rules="rules"
+                :model="form"
+                @submit="handleSubmit"
+                :style="{ width: '90%', margin: '0 auto' }"
+              >
+                <a-form-item field="prompt_config.system" label="系统">
+                  <a-textarea v-model="form.prompt_config.system" placeholder="" style="height: 180px" />
+                </a-form-item>
+                <a-divider style="margin: 0; margin-bottom: 10px" />
+                <a-form-item label="相似度阈值">
+                  <a-slider v-model="form.similarity_threshold" :step="0.01" :min="0" :max="1" />
+                </a-form-item>
+                <a-form-item label="关键字相似度权重">
+                  <a-slider v-model="form.vector_similarity_weight" :step="0.01" :min="0" :max="1" />
+                </a-form-item>
+                <a-form-item label="Top N">
+                  <a-slider v-model="form.top_n" :min="0" :max="30" />
+                </a-form-item>
+                <a-form-item label="Rerank模型">
+                  <a-space direction="vertical" size="large">
+                    <a-select :size="'large'" v-model="form.rerank_id" placeholder="请选择 ..." allow-clear>
+                      <a-optgroup :label="index" v-for="(item, index) in rankModelList" :key="index">
+                        <a-option v-for="obj in item" :key="obj.fid" :disabled="!obj.available" :value="obj.llm_name">
+                          {{ obj.llm_name }}
+                        </a-option>
+                      </a-optgroup>
+                    </a-select>
+                  </a-space>
+                </a-form-item>
+                <a-form-item v-if="form.rerank_id" label="Top-K">
+                  <a-slider v-model="form.top_k" :min="1" :max="2048" />
+                </a-form-item>
+                <a-form-item label="变量">
+                  <div style="width: 100%; text-align: right">
+                    <a-button type="outline" @click="addVariable" style="" size="mini" shape="round"> 新建 </a-button>
+                  </div>
+                </a-form-item>
+                <a-form-item label="">
+                  <a-table :columns="columns" :data="form.prompt_config.parameters" style="width: 100%">
+                    <template #name="{ record, rowIndex }">
+                      <a-input v-model="record.name" placeholder="" />
+                      <!--                    <a-input v-model="form.prompt_config.parameters[rowIndex].key" />-->
+                    </template>
+                    <template #optional="{ record }">
+                      <a-switch v-model="record.optional" size="small" />
+                    </template>
+                    <template #action="{ record }">
+                      <a-button type="text" @click="deleteParameters(record)" style="margin-left: 0" size="small">
+                        <template #icon>
+                          <icon-delete />
+                        </template>
+                      </a-button>
+                    </template>
+                  </a-table>
+                </a-form-item>
+              </a-form>
+            </a-scrollbar>
+          </a-tab-pane>
+          <a-tab-pane key="3" title="模型设置">
+            <a-divider style="margin-bottom: 20px; margin-top: 0" />
+            <div style="width: 100%" :style="{ height: height }">
+              <a-form
+                ref="formRef2"
+                :rules="rules"
+                :model="form"
+                @submit="handleSubmit"
+                :style="{ width: '90%', margin: '0 auto' }"
+              >
+                <a-form-item field="llm_id" label="模型">
+                  <a-space direction="vertical" size="large">
+                    <a-select
+                      :size="'large'"
+                      field="llm_id"
+                      v-model="form.llm_id"
+                      style="width: 400px"
+                      placeholder="请选择"
+                    >
+                      <template v-for="(item, index) in modelList" :key="index">
+                        <a-optgroup :label="index" v-if="item.length > 0">
+                          <a-option v-for="obj in item" :key="obj.fid" :value="obj.llm_name">
+                            {{ obj.llm_name }}
+                          </a-option>
+                        </a-optgroup>
+                      </template>
+                    </a-select>
+                  </a-space>
+                </a-form-item>
+                <a-divider style="margin-bottom: 20px; margin-top: 0" />
+                <a-form-item label="自由">
+                  <a-select default-value="2" :style="{ width: '400px' }" placeholder="">
+                    <a-option value="1">即兴创作</a-option>
+                    <a-option value="2">精确</a-option>
+                    <a-option value="3">平衡</a-option>
+                  </a-select>
+                </a-form-item>
+                <a-form-item label="温度">
+                  <a-switch size="small" v-model="temperature" />
+                  <a-space direction="vertical" size="large">
+                    <a-slider
+                      :disabled="!temperature"
+                      v-model="form.llm_setting.temperature"
+                      :step="0.01"
+                      :min="0"
+                      :max="1"
+                      :style="{
+                        width: '350px',
+                        marginLeft: '20px',
+                        display: 'flex'
+                      }"
+                      show-input
+                    />
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="top P">
+                  <a-switch size="small" v-model="top_p" />
+                  <a-space direction="vertical" size="large">
+                    <a-slider
+                      :disabled="!top_p"
+                      v-model="form.llm_setting.top_p"
+                      :step="0.01"
+                      :min="0"
+                      :max="1"
+                      :style="{
+                        width: '350px',
+                        marginLeft: '20px',
+                        display: 'flex'
+                      }"
+                      show-input
+                    />
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="出席处罚">
+                  <a-switch size="small" v-model="presence_penalty" />
+                  <a-space direction="vertical" size="large">
+                    <a-slider
+                      :disabled="!presence_penalty"
+                      v-model="form.llm_setting.presence_penalty"
+                      :step="0.01"
+                      :min="0"
+                      :max="1"
+                      :style="{
+                        width: '350px',
+                        marginLeft: '20px',
+                        display: 'flex'
+                      }"
+                      show-input
+                    />
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="频率处罚">
+                  <a-switch size="small" v-model="frequency_penalty" />
+                  <a-space direction="vertical" size="large">
+                    <a-slider
+                      :disabled="!frequency_penalty"
+                      v-model="form.llm_setting.frequency_penalty"
+                      :step="0.01"
+                      :min="0"
+                      :max="1"
+                      :style="{
+                        width: '350px',
+                        marginLeft: '20px',
+                        display: 'flex'
+                      }"
+                      show-input
+                    />
+                  </a-space>
+                </a-form-item>
+                <a-form-item label="最大token数">
+                  <a-switch size="small" v-model="max_tokens" />
+                  <a-space direction="vertical" size="large">
+                    <a-slider
+                      :disabled="!max_tokens"
+                      v-model="form.llm_setting.max_tokens"
+                      :min="0"
+                      :max="2048"
+                      :style="{
+                        width: '350px',
+                        marginLeft: '20px',
+                        display: 'flex'
+                      }"
+                      show-input
+                    />
+                  </a-space>
+                </a-form-item>
+              </a-form>
+            </div>
+          </a-tab-pane>
+        </a-tabs>
+      </a-col>
+    </a-row>
+  </a-modal>
+</template>
+
+<script setup>
+import { onMounted, onBeforeMount, reactive, ref, nextTick, onUnmounted, onBeforeUnmount, computed } from 'vue';
+// import { kbdocumentupload, queryKbList, queryModelList } from '@/api/kbList';
+import useLoading from './loading';
+import Upload from './Upload.vue';
+import { Message } from '@arco-design/web-vue';
+// import { dialogSet } from '@/api/Agent';
+
+const props = defineProps(['show_dify_config', 'typeAngint', 'formData']);
+const emit = defineEmits(['queryList', 'update:show_dify_config']);
+
+const { loading, setLoading } = useLoading(true);
+const visible = computed(() => props.show_dify_config);
+const modelList = ref({});
+const rankModelList = ref({});
+let tabs = ref([]);
+const file = ref();
+const formRef = ref();
+const formRef1 = ref();
+const formRef2 = ref();
+const activeKey = ref('1');
+const form = reactive({
+  name: '',
+  icon: '',
+  language: 'English',
+  prompt_config: {
+    empty_response: '',
+    prologue: '你好! 我是你的助理,有什么可以帮到你的吗?',
+    quote: true,
+    self_rag: false,
+    system:
+      '你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。' +
+      '回答需要考虑聊天历史。\n        以下是知识库:\n        {knowledge}\n        以上是知识库。',
+    parameters: [
+      {
+        index: 0,
+        name: 'knowledge',
+        key: 'knowledge',
+        optional: false
+      }
+    ]
+  },
+  kb_ids: [],
+  llm_id: undefined,
+  llm_setting: {
+    temperature: 0.1,
+    top_p: 0.3,
+    presence_penalty: 0.4,
+    frequency_penalty: 0.7,
+    max_tokens: 512
+  },
+  similarity_threshold: 0.2,
+  vector_similarity_weight: 0.30000000000000004,
+  top_n: 8,
+  rerank_id: '',
+  top_k: 1024
+});
+let temperature = ref(true);
+let top_p = ref(true);
+let presence_penalty = ref(true);
+let frequency_penalty = ref(true);
+let max_tokens = ref(true);
+const httpUrl = localStorage.getItem('httpUrl');
+let avatarShow = ref(true);
+
+const height = ref('calc(500px)');
+
+const columns = [
+  {
+    title: '关键字',
+    dataIndex: 'name',
+    slotName: 'name'
+  },
+  {
+    title: '可选',
+    dataIndex: 'optional',
+    slotName: 'optional'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    slotName: 'action'
+  }
+];
+
+const formatter = (value) => {
+  return `${(value / 100).toFixed(2)}`;
+};
+
+const uploadAction = '/api/v1/llm/upload'; // 替换为你的上传API
+const fileList = ref([]);
+const imageUrls = ref([]);
+
+const updateFileList = (newFileList) => {
+  fileList.value = newFileList;
+  console.log(newFileList, 88);
+};
+
+const handleSuccess = (urls) => {
+  imageUrls.value = urls; // 拿到上传的图片地址
+  form.icon = urls[0];
+};
+
+const rules = {
+  name: [
+    {
+      required: true,
+      message: '名称不允许为空'
+    }
+  ],
+  'prompt_config.system': [
+    {
+      required: true,
+      message: '系统不允许为空'
+    }
+  ],
+  llm_id: [
+    {
+      required: true,
+      message: '模型不能为空'
+    }
+  ],
+  kb_ids: [
+    {
+      required: true,
+      message: '知识库不能为空'
+    }
+  ]
+};
+
+const handleSubmit = ({ values, errors }) => {
+  console.log('values:', values, '\nerrors:', errors);
+  if (!errors) {
+  }
+};
+
+const handleCancel = () => {
+  formRef.value.resetFields();
+  formRef1.value.resetFields();
+  formRef2.value.resetFields();
+  form.name = '';
+  emit('update:show_dify_config', false);
+  emit('queryList');
+};
+
+const handleOpened = (el) => {
+  // Object.assign(form,{
+  //   name: '',// 用户名
+  //   nameJoin: '',// 昵称
+  //   post: '',// 岗位
+  //   txt: '',// 备注
+  // });
+  activeKey.value = '1';
+  formRef.value.resetFields();
+  formRef1.value.resetFields();
+  formRef2.value.resetFields();
+  nextTick(() => {
+    if (props.typeAngint == 'add') {
+      form.name = '';
+      form.icon = '';
+      form.prompt_config.system =
+        '你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。' +
+        '回答需要考虑聊天历史。\n        以下是知识库:\n        {knowledge}\n        以上是知识库。';
+    }
+    // console.log(form, '新建智能体弹窗');
+    avatarShow.value = false;
+    setTimeout(() => {
+      avatarShow.value = true;
+    }, 100);
+  });
+};
+
+const handleBeforeOk = async (done) => {
+  formRef.value.validate().then((res) => {
+    // console.log('res:', res)
+  });
+  formRef1.value.validate().then((res) => {
+    // console.log('res:', res)
+  });
+  formRef2.value.validate().then((res) => {
+    // console.log('res:', res)
+  });
+  if (form.name && form.kb_ids.length > 0 && form.prompt_config.system && form.llm_id) {
+    let title = '创建成功';
+    let formNew = { ...form };
+    if (formNew.rerank_id) {
+      delete formNew.top_k;
+    }
+
+    if (formNew.rerank_id) {
+      delete formNew.top_k;
+    }
+
+    formNew.prompt_config.parameters = form.prompt_config.parameters.map((item) => {
+      return {
+        ...item,
+        key: item.name
+      };
+    });
+
+    console.log(formNew, 'formNew');
+
+    if (props.typeAngint == 'edit') {
+      formNew.dialog_id = form.id;
+      delete formNew.id;
+      delete formNew.off;
+      title = '修改成功';
+    }
+
+    if (!max_tokens.value) {
+      //formNew.llm_setting.max_tokens = '';
+      delete formNew.llm_setting.max_tokens;
+    }
+
+    setLoading(true);
+    try {
+      // const data = await dialogSet(formNew);
+      // if (data.code == 0) {
+      //   Message.success(title);
+      //   handleCancel();
+      // TODO: 刷新列表
+      // EventBus.emit('queryList');
+      // } else {
+      //   Message.error(data.msg);
+      // }
+      done(true);
+      setLoading(false);
+    } catch (err) {
+      // you can report use errorHandler or other
+      setLoading(false);
+    }
+  } else {
+    Message.warning('请填写必填项');
+    done(false);
+  }
+};
+
+const editClick = (data) => {
+  visible.value = true;
+  nextTick(() => {
+    // console.log(props.formData,'传入数据');
+    if (props.formData.llm_setting.max_tokens == '') {
+      max_tokens.value = false;
+      delete props.formData.llm_setting.max_tokens;
+    } else {
+      max_tokens.value = true;
+    }
+    Object.assign(form, props.formData);
+    console.log(form, '表单数据');
+  });
+};
+
+const addVariable = () => {
+  form.prompt_config.parameters.push({
+    index: form.prompt_config.parameters.length,
+    key: '',
+    name: '',
+    optional: true
+  });
+  nextTick(() => {
+    formRef.value.validate();
+  });
+};
+
+const deleteParameters = (record) => {
+  // console.log(record, 'record');
+  // console.log(form.prompt_config.parameters);
+  form.prompt_config.parameters = form.prompt_config.parameters.filter((item) => item.index !== record.index);
+};
+
+function handleTabChange(key) {
+  // 处理标签改变事件
+  activeKey.value = key;
+}
+
+const queryModel = async (params) => {
+  try {
+    // const data = await queryModelList(params);
+    // const arrObj = {};
+    // for (const key in data.data) {
+    //   if (Object.hasOwnProperty.call(data.data, key)) {
+    //     const element = data.data[key];
+    //     arrObj[key] = element.filter((item) => item.available === true && item.model_type === 'chat');
+    //   }
+    // }
+    // modelList.value = arrObj;
+    // rankModelList.value = {
+    //   BAAI: [data.data.BAAI[1]],
+    //   Jina: data.data.Jina,
+    //   youdao: data.data.youdao
+    // };
+  } catch (err) {
+    // you can report use errorHandler or other
+  } finally {
+  }
+};
+
+const knowledgeData = async (params = { page: 1, page_size: 20 }) => {
+  setLoading(true);
+  try {
+    // const { data } = await queryKbList(params);
+    // nextTick(() => {
+    //   tabs.value = data;
+    // });
+  } catch (err) {
+    // you can report use errorHandler or other
+  } finally {
+    setLoading(false);
+  }
+};
+
+onBeforeMount(() => {
+  queryModel({});
+  knowledgeData();
+});
+onMounted(() => {});
+
+onBeforeUnmount(() => {});
+
+defineExpose({
+  editClick
+});
+</script>
+<style scoped>
+::v-deep .arco-tabs-nav-tab-list {
+  width: 100%;
+
+  .arco-tabs-tab {
+    width: 33%;
+
+    .arco-tabs-tab-title {
+      width: 100%;
+      display: inline-block;
+      text-align: center;
+    }
+  }
+}
+::v-deep .arco-input-wrapper {
+  background-color: var(--color-neutral-2);
+}
+</style>

+ 71 - 0
src/modules/intelligent/rgflow/Upload.vue

@@ -0,0 +1,71 @@
+<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 } 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
+    }));
+  }
+});
+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>

+ 16 - 0
src/modules/intelligent/rgflow/loading.js

@@ -0,0 +1,16 @@
+import { ref } from 'vue';
+
+export default function useLoading(initValue = false) {
+  const loading = ref(initValue);
+  const setLoading = (value) => {
+    loading.value = value;
+  };
+  const toggle = () => {
+    loading.value = !loading.value;
+  };
+  return {
+    loading,
+    setLoading,
+    toggle
+  };
+}

+ 2 - 2
src/modules/knowledge/TopSetting.vue

@@ -18,12 +18,12 @@
       {{ $t('management.createKnowledgeBase') }}
     </a-button>
   </a-space>
-  <modalItem v-model:visible="visible" />
+  <ModalItem v-model:visible="visible"></ModalItem>
 </template>
 
 <script setup>
 import { ref } from 'vue';
-import modalItem from './modalItem.vue';
+import ModalItem from './ModalItem.vue';
 import { funcDebounce } from '../../utils/utils';
 import { bus_knowledgeSearch } from '../../base/event-bus';
 

+ 1 - 1
src/modules/login/FormItem.vue

@@ -159,7 +159,7 @@ const changeFormType = (e) => {
   background-color: var(--color-bg-1);
   border-color: var(--color-neutral-3);
 }
-.arco-form-item-label {
+::v-deep .arco-form-item-label-col > .arco-form-item-label {
   display: none;
 }
 </style>

+ 1 - 1
src/views/Login.vue

@@ -47,7 +47,7 @@ import FormItem from '../modules/login/FormItem.vue';
   align-items: center;
   justify-content: center;
   gap: 100px;
-  height: calc(100% - 110px);
+  height: calc(100% - 115px);
 }
 .intro {
   display: flex;

+ 15 - 0
src/views/difyDetail.vue

@@ -0,0 +1,15 @@
+<template>
+  <LayoutExperience>
+    <ContentContainer>
+      <TopSetting></TopSetting>
+      <MessageBody></MessageBody>
+    </ContentContainer>
+  </LayoutExperience>
+</template>
+
+<script setup>
+import LayoutExperience from './layout/LayoutExperience.vue';
+import ContentContainer from './layout/ContentContainer.vue';
+import TopSetting from '../modules/dify/TopSetting.vue';
+import MessageBody from '../modules/dify/MessageBody.vue';
+</script>

+ 2 - 0
src/views/manage/CardItem.vue

@@ -41,6 +41,7 @@ const props = defineProps({
   position: relative;
   padding: 10px 20px;
   background-color: var(--color-bg-2);
+  overflow: hidden;
 }
 .card-name .name {
   margin-top: 10px;
@@ -53,6 +54,7 @@ const props = defineProps({
   margin-top: 15px;
   color: var(--color-neutral-4);
 }
+
 .card-des {
   display: -webkit-box;
   overflow: hidden;

+ 4 - 4
src/views/manage/common.js

@@ -48,25 +48,25 @@ export const getDefaultIntelligent = () => {
       name: '问答助理',
       description: 'EMBEDDING,SPEECH2TEXT,MODERATION',
       labels: 'LLM,TEXT',
-      type: 'QA'
+      type: 'RAGFLOW'
     },
     {
       name: '工作流助手',
       description: 'EMBEDDING,SPEECH2TEXT,MODERATION',
       labels: 'LLM,TEXT',
-      type: 'ZS'
+      type: 'DIFY_1'
     },
     {
       name: '智能Agent',
       description: 'EMBEDDING,SPEECH2TEXT,MODERATION',
       labels: 'LLM,TEXT',
-      type: 'AG'
+      type: 'DIFY_2'
     },
     {
       name: '工作流应用',
       description: 'EMBEDDING,SPEECH2TEXT,MODERATION',
       labels: 'LLM,TEXT',
-      type: 'YY'
+      type: 'DIFY_3'
     }
   ];
 };

+ 2 - 0
src/views/routes.js

@@ -24,6 +24,8 @@ const routes = [
   { path: '/agent', component: () => import('./Agent.vue') },
   // 智能体
   { path: '/intelligent', component: () => import('./Intelligent.vue') },
+  // dify智能体详情
+  { path: '/difydetail', component: () => import('./difyDetail.vue') },
 
   // 权限管理
   {