sunxiao преди 8 месеца
родител
ревизия
e974061ef4
променени са 4 файла, в които са добавени 140 реда и са изтрити 105 реда
  1. 8 0
      src/assets/svgs/chat/icon-close-btn.svg
  2. 123 96
      src/components/Chat/ChatInputCopy.vue
  3. 3 3
      src/views/analyse/config/index.jsx
  4. 6 6
      src/views/work/WorkView.vue

+ 8 - 0
src/assets/svgs/chat/icon-close-btn.svg

@@ -0,0 +1,8 @@
+
+<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M3.14905 3.14777L12.8508 12.8496" stroke="#838A95" stroke-linecap="round"></path>
+  <path d="M13.0016 2.99786L2.99725 13.0022" stroke="#838A95" stroke-linecap="round"></path>
+  <g opacity="0.01" style="mix-blend-mode: darken;">
+    <rect width="16" height="16" fill="white"></rect>
+  </g>
+</svg>

+ 123 - 96
src/components/Chat/ChatInputCopy.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { ref, unref, onMounted, computed, watch } from 'vue';
+import { ref, unref, onMounted, onUnmounted, computed, watch } from 'vue';
 import { useMessage, NInput, NSwitch, NPopover, NScrollbar } from 'naive-ui';
 import SvgIcon from '@/components/SvgIcon';
 
@@ -27,6 +27,9 @@ const isOpen = ref(false);
 const selectedOption = ref(null);
 const highlightedIndex = ref(0);
 
+const popoverTriggerRef = ref(null);
+const popoverInnerRef = ref(null);
+
 const agentOptions = computed(() => props.options.filter(({ tools }) => tools));
 
 const focusInput = _ => isFocusState.value = true;
@@ -65,32 +68,35 @@ const commonEmitEvent = (eventName) => {
     return message.warning('当前对话进行中');
   }
 
-  emit(eventName, val);
+  // emit(eventName, val);
+  emit(eventName, {question: val, selectedOption: selectedOption.value || {}});
 
   inpVal.value = '';
 }
 
+// 回车事件
 const handleInpEnter = (event) => {
-  console.log( !unref(isOpen) );
-  if (event.key === 'Enter' && !event.shiftKey) {
+  if (event.key === 'Enter' && !event.shiftKey && inpVal.value) {
     event.preventDefault();
-    // commonEmitEvent('onEnter');
+    commonEmitEvent('onEnter');
   }
 }
 
+// 点击事件
 const handleBtnClick = () => {
-  commonEmitEvent("onClick")
+  commonEmitEvent("onClick");
 }
 
 const clearInpVal = () => {
   inpVal.value = '';
 }
 
-// 键盘上下事件
+// 键盘事件
 const handleKeyDown = (event) => {
-
   const len = unref(agentOptions).length;
 
+  if ( !isOpen.value ) return;
+
   switch (event.key) {
     case 'ArrowUp':
       event.preventDefault();
@@ -101,6 +107,7 @@ const handleKeyDown = (event) => {
       highlightedIndex.value = (unref(highlightedIndex) + 1) % len;
       break;
     case 'Enter':
+      event.preventDefault();
       selectOption(unref(highlightedIndex));
       break;
     default:
@@ -108,15 +115,30 @@ const handleKeyDown = (event) => {
   }
 }
 
+// 处理点击空白处关闭
+const closePopoverOutside =(event) => {
+  if (!isOpen.value) return; 
+  const triggerResult = popoverTriggerRef.value.contains(event.target);
+  const innerResult = popoverInnerRef.value.contains(event.target);
+  isOpen.value = triggerResult || innerResult;
+}
+
+// 选中选项
 const selectOption = (index) => {
   selectedOption.value = agentOptions.value[index];
-  isOpen.value = false;
   highlightedIndex.value = index;
+  isOpen.value = false;
   clearInpVal();
 }
 
 onMounted(() => {
   document.addEventListener('keydown', handleKeyDown);
+  document.addEventListener('click', closePopoverOutside);
+})
+
+onUnmounted(() => {
+  document.removeEventListener('keydown', handleKeyDown);
+  document.removeEventListener('click', closePopoverOutside);
 })
 
 defineExpose({
@@ -130,21 +152,27 @@ defineExpose({
   <NPopover
     trigger="hover"
     width="trigger"
+    display-directive="show"
     content-style="padding: 0;"
     :show-arrow="false"
     :show="isOpen"
   >
     <template #trigger>
-      <div>
+      <div class="popover-trigger" ref="popoverTriggerRef">
         <div class="chat-inp-outer border-[1px]" :class="[{ 'border-[#2454FF]': isFocusState }]">
-          <div class="helper-tools py-[10px] px-[10px] bg-[#fcfcfc] space-x-[10px]" v-show="selectedOption">
-            <span>与</span>            
-            <p class="agent-name space-x-[5px]" @click="isOpen = true">
-              <img src="https://static.fuxicarbon.com/userupload/db77ffe0cef843278a23b0d2db9505fa.png" alt="">
-              <span>{{ selectedOption?.title }}</span>
-            </p>
-            <span>对话中</span>
-          </div>
+          <ul class="chat-tools-inner py-[10px] px-[10px] bg-[#fcfcfc]" v-show="selectedOption">
+            <li class="tools-tips space-x-[10px]">
+              <span>与</span>            
+              <p class="agent-name space-x-[5px]" @click="isOpen = true">
+                <img src="https://static.fuxicarbon.com/userupload/db77ffe0cef843278a23b0d2db9505fa.png" alt="">
+                <span>{{ selectedOption?.title }}</span>
+              </p>
+              <span>对话中</span>
+            </li>
+            <li class="tools-close" @click="selectedOption = null">
+              <SvgIcon name="chat-icon-close-btn"></SvgIcon>
+            </li>
+          </ul>
           <div class="chat-inp-inner">
             <div class="inp-wrapper flex-1" @click="handleInpFocus">
               <NInput 
@@ -152,7 +180,7 @@ defineExpose({
                 ref="inpRef" 
                 type="textarea" 
                 size="medium"
-                placeholder="输入您的问题或需求,Enter发送,Shift+Enter换行"
+                placeholder="输入@,召唤智能体"
                 v-model:value="inpVal" 
                 :autosize="{ minRows: 1, maxRows: 5 }"
                 @focus="focusInput"
@@ -178,56 +206,88 @@ defineExpose({
       </div>
     </template>
   
-    <div class="popover-inner">
+    <div class="popover-inner" ref="popoverInnerRef">
       <div class="header">
         <span>选择智能体</span>
-        <p class="close" @click="isOpen = false">
-          <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-            <path d="M3.14905 3.14777L12.8508 12.8496" stroke="#838A95" stroke-linecap="round"></path>
-            <path d="M13.0016 2.99786L2.99725 13.0022" stroke="#838A95" stroke-linecap="round"></path>
-            <g opacity="0.01" style="mix-blend-mode: darken;">
-              <rect width="16" height="16" fill="white"></rect>
-            </g>
-          </svg>
+        <p class="tools-close" @click="isOpen = false">
+          <SvgIcon name="chat-icon-close-btn"></SvgIcon>
         </p>
       </div>
-      <div class="content">
-        <NScrollbar style="max-height: 240px;">
-          <div class="item" v-for="item, index in agentOptions" :class="['item', { active: highlightedIndex === index }]" @click="selectOption(index)">
-            <p class="icon">
-              <img :src="item.banner" alt="">
-            </p>
-            <p class="ml-[10px] space-x-[5px] text">
-              <span class="text-[15px]">{{item.title}}</span>
-              <!-- <span class="text-[#888] text-[14px]">/span> -->
-            </p>
-          </div>
-        </NScrollbar>
-      </div>
+      <NScrollbar style="max-height: 240px;">
+        <div class="item" v-for="item, index in agentOptions" :class="['item', { active: highlightedIndex === index }]" @click="selectOption(index)">
+          <p class="icon">
+            <img :src="item.banner" alt="">
+          </p>
+          <p class="ml-[10px] space-x-[5px] text">
+            <span class="text-[15px]">{{item.title}}</span>
+            <span class="text-[#888] text-[14px]">这里可以补充个描述</span>
+          </p>
+        </div>
+      </NScrollbar>
     </div>
   </NPopover>
 </template>
 
 <style scoped lang="scss">
+.chat-inp-outer {
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0px 3px 12px 0px #97D3FF40;
+
+  .chat-tools-inner {
+    @include flex(x, center, between);
+
+    .tools-tips {
+      @include flex(x, center, start);
+      color: #666;
+      font-size: 14px;
+      
+      .agent-name {
+        @include flex(x, center, start);
+        font-weight: bold;
+        color: #333;
+        cursor: pointer;
+
+        img {
+          width: 14px;
+          height: 14px;
+        }
+      }
+    }
+  }
+
+  .chat-inp-inner {
+    position: relative;
+    @include flex(x, center, between);
+    background: #fff;
+
+    .inp-wrapper {
+      padding: 17px 0px 17px 34px;
+    }
+
+    .submit-btn {
+      @include flex(x, center, center);
+      width: 84px;
+
+      .btn {
+        @include flex(x, center, center);
+        width: 50px;
+        height: 32px;
+        border-radius: 32px;
+        transition: all .3s;
+      }
+    }
+  }
+}
+
 .popover-inner {
   .header {
     @include flex(x, center, between);
     padding-bottom: 8px;
     font-size: 14px;
     color: #666;
-    .close {
-      @include flex(x, center, center);
-      width: 28px;
-      height: 28px;
-      border-radius: 6px;
-      background: #fff;
-      cursor: pointer;
-      &:hover {
-        background: #e9eef8;
-      }
-    }
   }
-  .content {
+
     .item {
       @include flex(x, center, start);
       padding: 8px 10px;
@@ -257,51 +317,16 @@ defineExpose({
       background: #f0fafe;
     }
   }
-}
-
-.chat-inp-outer {
-  border-radius: 8px;
-  overflow: hidden;
-  box-shadow: 0px 3px 12px 0px #97D3FF40;
 
-  .helper-tools {
-    @include flex(x, center, start);
-    color: #666;
-    font-size: 14px;
-    
-    .agent-name {
-      @include flex(x, center, start);
-      font-weight: bold;
-      color: #333;
-      cursor: pointer;
-
-      img {
-        width: 14px;
-        height: 14px;
-      }
-    }
-  }
-}
-.chat-inp-inner {
-  position: relative;
-  @include flex(x, center, between);
+.tools-close {
+  @include flex(x, center, center);
+  width: 28px;
+  height: 28px;
+  border-radius: 6px;
   background: #fff;
-
-  .inp-wrapper {
-    padding: 17px 0px 17px 34px;
-  }
-
-  .submit-btn {
-    @include flex(x, center, center);
-    width: 84px;
-
-    .btn {
-      @include flex(x, center, center);
-      width: 50px;
-      height: 32px;
-      border-radius: 32px;
-      transition: all .3s;
-    }
+  cursor: pointer;
+  &:hover {
+    background: #e9eef8;
   }
 }
 
@@ -313,4 +338,6 @@ defineExpose({
   height: 30px;
   background: linear-gradient(180deg, rgba(232, 241, 250, 0) 0%, #E7F0FA 95%);
 }
+
+
 </style>

+ 3 - 3
src/views/analyse/config/index.jsx

@@ -84,7 +84,7 @@ export const outColumns = [
     render: (row) => renderRowDom({ row, key: 'COD' })
   },
   {
-    title: () => renderTooltip(h( 'span', 'xsy1(mg/L)' ), '1号好氧池硝酸盐 | 连续检测'),
+    title: () => renderTooltip(h( 'span', '#1NO₃⁻(mg/L)' ), '1号好氧池硝酸盐 | 连续检测'),
     key: 'HYC1',
     titleAlign: 'center',
     align: 'center',
@@ -93,7 +93,7 @@ export const outColumns = [
     render: (row) => renderRowDom({ row, key: 'HYC1' })
   },
   {
-    title: () => renderTooltip(h( 'span', 'xsy2(mg/L)' ), '2号好氧池硝酸盐 | 连续检测'),
+    title: () => renderTooltip(h( 'span', '#2NO₃⁻(mg/L)' ), '2号好氧池硝酸盐 | 连续检测'),
     key: 'HYC2',
     titleAlign: 'center',
     align: 'center',
@@ -111,7 +111,7 @@ export const outColumns = [
     render: (row) => renderRowDom({ row, key: 'NH3-N' })
   },
   {
-    title: () => renderTooltip(h( 'span', 'zlsy(mg/L)' ), '二沉池正磷酸盐 | 连续检测'),
+    title: () => renderTooltip(h( 'span', 'PO₄³⁻(mg/L)' ), '二沉池正磷酸盐 | 连续检测'),
     key: 'RCC',
     titleAlign: 'center',
     align: 'center',

+ 6 - 6
src/views/work/WorkView.vue

@@ -70,17 +70,18 @@ const handleChatDetail = async ({ sessionId }) => {
   scrollToBottom();
 }
 
-const onRegenerate = async ({ question, realQuestion }) => {
+const onRegenerate = async ({ question, tools }) => {
   controller = new AbortController();
 
   const sessionId = unref(currenSessionId);
+  
   const params = {
     data: {
       sessionId,
       showVal: question,
       question: question,
       module: 2,
-      tools: activeItem.value.tools,
+      tools: activeItem.value.tools || tools,
       isStrong: Number(unref(switchActive))
     },
     signal: controller.signal,
@@ -126,7 +127,7 @@ const onRegenerate = async ({ question, realQuestion }) => {
   }
 }
 // 提交问题
-const handleSubmit = async (question, realQuestion = '') => {
+const handleSubmit = async ({question, selectedOption}) => {
 
   if (unref(isExistInHistory)) {
     const { data: sessionId } = await chatApi.getChatSessionTag();
@@ -138,7 +139,6 @@ const handleSubmit = async (question, realQuestion = '') => {
   addChat({
     sessionId: unref(currenSessionId),
     question,
-    realQuestion,
     answer: '',
     loading: true,
     delayLoading: true
@@ -146,7 +146,7 @@ const handleSubmit = async (question, realQuestion = '') => {
 
   scrollToBottom();
 
-  setTimeout(() => onRegenerate({ question, realQuestion }), 2 * 1000);
+  setTimeout(() => onRegenerate({ question, tools: selectedOption?.tools || null }), 2 * 1000);
 }
 
 // 处理推荐问题
@@ -206,7 +206,7 @@ onUnmounted(() => {
     </TheSubMenu>
 
     <TheChatView ref="scrollRef" :is-back-btn="!!chatDataSource.length" @on-click-back="handleback">
-      <div v-if="!chatDataSource.length">
+      <div v-show="!chatDataSource.length">
         <ChatWelcome title="您好,我是LibraAI智能助手" :sub-title="[
           'LibarAI智能助手模块提供撰写文章、生成报告等服务',
           '请替换问题中##的内容'