Răsfoiți Sursa

feat: 合并input代码

sunxiao 4 săptămâni în urmă
părinte
comite
effcf14b72

+ 1 - 1
src/assets/styles/github-markdown-light.scss

@@ -547,7 +547,7 @@
   margin-top: 16px;
   margin-top: 16px;
 }
 }
 .markdown-body p, .markdown-body li {
 .markdown-body p, .markdown-body li {
-  font-size: 14px;
+  font-size: 16px;
 }
 }
 
 
 .markdown-body li+li {
 .markdown-body li+li {

+ 11 - 0
src/assets/svgs/chat/icon-arrow-down.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
+                        <g clip-path="url(#clip0_5397_2138)">
+                        <path d="M8.00004 14.6668C9.84097 14.6668 11.5076 13.9206 12.7141 12.7142C13.9205 11.5078 14.6667 9.8411 14.6667 8.00016C14.6667 6.15923 13.9205 4.49256 12.7141 3.28612C11.5076 2.07969 9.84097 1.3335 8.00004 1.3335C6.15911 1.3335 4.49244 2.07969 3.28599 3.28612C2.07957 4.49256 1.33337 6.15923 1.33337 8.00016C1.33337 9.8411 2.07957 11.5078 3.28599 12.7142C4.49244 13.9206 6.15911 14.6668 8.00004 14.6668Z" fill="#1A2029" stroke="#1A2029" stroke-width="1.33333" stroke-linejoin="round"/>
+                        <path d="M5.33337 8L7.33337 10L11.3334 6" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+                        </g>
+                        <defs>
+                        <clipPath id="clip0_5397_2138">
+                        <rect width="16" height="16" fill="white"/>
+                        </clipPath>
+                        </defs>
+                      </svg>

+ 3 - 0
src/assets/svgs/chat/icon-deep.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
+  <path d="M6.82151 2.10744C6.50895 2.42 6.33336 2.84393 6.33336 3.28595C6.33336 3.72798 6.50895 4.15191 6.82151 4.46447L11.5356 9.17851C11.8481 9.49107 12.272 9.66667 12.7141 9.66667C13.1561 9.66667 13.58 9.49107 13.8926 9.17851C14.2051 8.86595 14.3807 8.44203 14.3807 8C14.3807 7.55797 14.2051 7.13405 13.8926 6.82149C13.58 6.50893 13.1561 6.33333 12.7141 6.33333C12.272 6.33333 11.8481 6.50893 11.5356 6.82149L6.82151 11.5355C6.50895 11.8481 6.33336 12.272 6.33336 12.714C6.33336 13.1561 6.50895 13.58 6.82151 13.8926C7.13407 14.2051 7.558 14.3807 8.00002 14.3807C8.44205 14.3807 8.86597 14.2051 9.17853 13.8926C9.4911 13.58 9.66669 13.1561 9.66669 12.714C9.66669 12.272 9.4911 11.8481 9.17853 11.5355L4.46449 6.82149C4.15193 6.50893 3.72801 6.33333 3.28598 6.33333C2.84395 6.33333 2.42003 6.50893 2.10747 6.82149C1.79491 7.13405 1.61931 7.55797 1.61931 8C1.61931 8.44203 1.79491 8.86595 2.10747 9.17851C2.42003 9.49107 2.84395 9.66667 3.28598 9.66667C3.72801 9.66667 4.15193 9.49107 4.46449 9.17851L9.17853 4.46447C9.4911 4.15191 9.66669 3.72798 9.66669 3.28595C9.66669 2.84393 9.4911 2.42 9.17853 2.10744C8.86597 1.79488 8.44205 1.61929 8.00002 1.61929C7.558 1.61929 7.13407 1.79488 6.82151 2.10744Z" stroke="#2E5CFF" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 14 - 0
src/assets/svgs/chat/icon-internet-active.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
+<g clip-path="url(#clip0_5450_2335)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00004 14.6668C11.6819 14.6668 14.6667 11.6821 14.6667 8.00016C14.6667 4.31826 11.6819 1.3335 8.00004 1.3335C4.31814 1.3335 1.33337 4.31826 1.33337 8.00016C1.33337 11.6821 4.31814 14.6668 8.00004 14.6668Z" stroke="#2E5CFF" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M1.33337 8H14.6667" stroke="#2E5CFF" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00004 14.6668C9.47281 14.6668 10.6667 11.6821 10.6667 8.00016C10.6667 4.31826 9.47281 1.3335 8.00004 1.3335C6.52727 1.3335 5.33337 4.31826 5.33337 8.00016C5.33337 11.6821 6.52727 14.6668 8.00004 14.6668Z" stroke="#2E5CFF" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M3.28601 3.38037C4.49244 4.5868 6.15911 5.333 8.00004 5.333C9.84101 5.333 11.5077 4.5868 12.7141 3.38037" stroke="#2E5CFF" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12.7141 12.6191C11.5077 11.4127 9.84101 10.6665 8.00004 10.6665C6.15911 10.6665 4.49244 11.4127 3.28601 12.6191" stroke="#2E5CFF" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+<defs>
+<clipPath id="clip0_5450_2335">
+<rect width="16" height="16" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 14 - 0
src/assets/svgs/chat/icon-internet.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
+  <g clip-path="url(#clip0_5397_2123)">
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M8.00004 14.6668C11.6819 14.6668 14.6667 11.6821 14.6667 8.00016C14.6667 4.31826 11.6819 1.3335 8.00004 1.3335C4.31814 1.3335 1.33337 4.31826 1.33337 8.00016C1.33337 11.6821 4.31814 14.6668 8.00004 14.6668Z" stroke="#1A2029" stroke-linecap="round" stroke-linejoin="round"/>
+    <path d="M1.33337 8H14.6667" stroke="#1A2029" stroke-linecap="round" stroke-linejoin="round"/>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M8.00004 14.6668C9.47281 14.6668 10.6667 11.6821 10.6667 8.00016C10.6667 4.31826 9.47281 1.3335 8.00004 1.3335C6.52727 1.3335 5.33337 4.31826 5.33337 8.00016C5.33337 11.6821 6.52727 14.6668 8.00004 14.6668Z" stroke="#1A2029" stroke-linecap="round" stroke-linejoin="round"/>
+    <path d="M3.28601 3.38037C4.49244 4.5868 6.15911 5.333 8.00004 5.333C9.84101 5.333 11.5077 4.5868 12.7141 3.38037" stroke="#1A2029" stroke-linecap="round" stroke-linejoin="round"/>
+    <path d="M12.7141 12.6191C11.5077 11.4127 9.84101 10.6665 8.00004 10.6665C6.15911 10.6665 4.49244 11.4127 3.28601 12.6191" stroke="#1A2029" stroke-linecap="round" stroke-linejoin="round"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_5397_2123">
+      <rect width="16" height="16" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>

+ 210 - 28
src/components/Chat/ChatAgentInput.vue

@@ -1,6 +1,6 @@
 <script setup>
 <script setup>
 import { ref, unref, onMounted, onUnmounted, computed, watch } from 'vue';
 import { ref, unref, onMounted, onUnmounted, computed, watch } from 'vue';
-import { useMessage, NInput, NSwitch, NPopover, NScrollbar, NUpload, NTooltip, NProgress } from 'naive-ui';
+import { useMessage, NInput, NPopover, NScrollbar, NUpload, NTooltip, NProgress, NButton } from 'naive-ui';
 import { useUserStore } from '@/stores/modules/userStore';
 import { useUserStore } from '@/stores/modules/userStore';
 import { baseURL } from '@/utils/request';
 import { baseURL } from '@/utils/request';
 import { getFormatYesterDay } from '@/utils/format';
 import { getFormatYesterDay } from '@/utils/format';
@@ -24,8 +24,14 @@ const MAX_NUM = 5;
 const useStore = useUserStore();
 const useStore = useUserStore();
 
 
 const modelLoading = defineModel('loading');
 const modelLoading = defineModel('loading');
+const modelOnline = defineModel('online');
 const switchStatus = defineModel('switch');
 const switchStatus = defineModel('switch');
 
 
+const selectOptions = [
+  { title: 'LibraAl', subTitle: '水务行业专家', value: 0 },
+  { title: 'Deepseek', subTitle: '适合深度思考', value: 1 },
+]
+
 const message = useMessage();
 const message = useMessage();
 
 
 const inpVal = ref('');
 const inpVal = ref('');
@@ -65,7 +71,15 @@ watch(inpVal, (curVal) => {
 })
 })
 
 
 watch(() => props.activeItem, (curVal) => {
 watch(() => props.activeItem, (curVal) => {
-  selectedOption.value = curVal?.tools ? curVal :  null;
+  // console.log(curVal);
+  selectedOption.value = curVal?.tools ? curVal : null;
+  // TODO: 这里后续大概率要删除
+  if ( curVal?.tools ) {
+    // 选中智能体,没有联网搜索
+    modelOnline.value = false;
+    // 选中智能体,需要使用默认的libraAi
+    switchStatus.value = 0;
+  }
 })
 })
 
 
 const handleInpFocus = () => {
 const handleInpFocus = () => {
@@ -197,6 +211,10 @@ const selectOption = (index) => {
   highlightedIndex.value = index;
   highlightedIndex.value = index;
   isOpen.value = false;
   isOpen.value = false;
   inpVal.value = selectedOption.value.content;
   inpVal.value = selectedOption.value.content;
+  // 选中智能体,没有联网搜索
+  modelOnline.value = false;
+  // 选中智能体,需要使用默认的libraAi
+  switchStatus.value = 0;
 }
 }
 
 
 const beforeUpload = ({ file }) => {
 const beforeUpload = ({ file }) => {
@@ -262,6 +280,12 @@ const clearFileList = () => {
   uploadFileList.value = [];
   uploadFileList.value = [];
 }
 }
 
 
+// 切换智能体
+const onChangeAgent = ({ value }) => {
+  switchStatus.value = value;
+  modelOnline.value = false;
+}
+
 onMounted(async () => {
 onMounted(async () => {
   const { data } = await helperApi.getHelperList();
   const { data } = await helperApi.getHelperList();
   
   
@@ -299,7 +323,7 @@ defineExpose({
         <div class="chat-inp-outer border-[1px]" :class="[{ 'border-[#2454FF]': isFocusState }]">
         <div class="chat-inp-outer border-[1px]" :class="[{ 'border-[#2454FF]': isFocusState }]">
           <ul class="chat-tools-inner py-[10px] px-[10px] bg-[#fcfcfc]" v-show="selectedOption">
           <ul class="chat-tools-inner py-[10px] px-[10px] bg-[#fcfcfc]" v-show="selectedOption">
             <li class="tools-tips space-x-[10px]">
             <li class="tools-tips space-x-[10px]">
-              <span>与</span>            
+              <span>与</span>       
               <p class="agent-name space-x-[5px]" @click="isOpen = true">
               <p class="agent-name space-x-[5px]" @click="isOpen = true">
                 <img src="https://static.fuxicarbon.com/userupload/db77ffe0cef843278a23b0d2db9505fa.png" alt="">
                 <img src="https://static.fuxicarbon.com/userupload/db77ffe0cef843278a23b0d2db9505fa.png" alt="">
                 <span>{{ selectedOption?.title }}</span>
                 <span>{{ selectedOption?.title }}</span>
@@ -358,34 +382,91 @@ defineExpose({
                   </NTooltip>
                   </NTooltip>
                 </NUpload>
                 </NUpload>
               </div>
               </div>
-              <NInput 
-                class="flex-1"
-                ref="inpRef" 
-                type="textarea" 
-                size="medium"
-                placeholder="输入@,召唤智能体"
-                v-model:value="inpVal" 
-                :autosize="{ minRows: 1, maxRows: 5 }"
-                @focus="focusInput"
-                @blur="blurInput"
-                @keypress="handleInpEnter"
-              />
+              <div class="w-full ml-[15px]">
+                <NInput 
+                  class="flex-1"
+                  ref="inpRef" 
+                  type="textarea" 
+                  size="medium"
+                  placeholder="输入@,召唤智能体"
+                  v-model:value="inpVal" 
+                  :autosize="{ minRows: 1, maxRows: 4 }"
+                  @focus="focusInput"
+                  @blur="blurInput"
+                  @keypress="handleInpEnter"
+                />
+              </div>
             </div>
             </div>
-            <div class="submit-btn">
-              <button class="btn bg-[#1A2029] hover:bg-[#3C4148]" @click="handleBtnClick">
-                <SvgIcon name="tool-send-plane" size="22" v-show="!modelLoading"></SvgIcon>
-                <div style="color: #fff" class="la-ball-running-dots la-sm" v-show="modelLoading">
-                  <div v-for="item in 5" :key="item"></div>
+
+            <div class="option-wrapper">
+              <div class="option-list space-x-[10px]">
+                <n-popover 
+                  trigger="focus" 
+                  to=".option-wrapper" 
+                  raw 
+                  :show-arrow="false"
+                  placement="top-start"
+                  :width="150"
+                  content-class="content-class"
+                  content-style="padding: 0;"
+                  isOpen
+                  :disabled="selectedOption"
+                >
+                  <template #trigger>
+                    <n-button text>
+                      <div class="switch-agent option">
+                        <span>{{ selectOptions[switchStatus]?.title }}</span>
+                        <i class="n-base-icon n-base-suffix__arrow"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.14645 5.64645C3.34171 5.45118 3.65829 5.45118 3.85355 5.64645L8 9.79289L12.1464 5.64645C12.3417 5.45118 12.6583 5.45118 12.8536 5.64645C13.0488 5.84171 13.0488 6.15829 12.8536 6.35355L8.35355 10.8536C8.15829 11.0488 7.84171 11.0488 7.64645 10.8536L3.14645 6.35355C2.95118 6.15829 2.95118 5.84171 3.14645 5.64645Z" fill="currentColor"></path></svg></i>
+                      </div>
+                    </n-button>
+                  </template>
+                  <ul class="select-agent-options space-y-[8px]">
+                    <li class="agent-option-item" v-for="item in selectOptions" :key="item.value" @click="onChangeAgent(item)">
+                      <p class="">
+                        <span class="agent-title">{{ item.title }}</span>
+                        <span class="agent-sub-title">{{ item.subTitle }}</span>
+                      </p>
+                      <SvgIcon name="chat-icon-arrow-down" v-show="switchStatus == item.value"></SvgIcon>
+                    </li>
+                  </ul>
+                </n-popover>
+
+                <div 
+                  class="internet-agent option space-x-[4px]"
+                  :class="{'agent-active': modelOnline}"
+                  @click="modelOnline = !modelOnline"
+                  v-show="switchStatus == 1"
+                >
+                  <span class="icon"></span>
+                  <span>联网搜索</span>
+                  <span class="circle"></span>
                 </div>
                 </div>
-              </button>
+
+                <!-- <div class="internet-agent  option space-x-[4px]">
+                  <SvgIcon name="chat-icon-deep"></SvgIcon>
+                  <span>深度思考(T1)</span>
+                  <span class="circle"></span>
+                </div> -->
+              </div>
+
+              <div class="submit-btn">
+                <button class="btn bg-[#1A2029] hover:bg-[#3C4148]" @click="handleBtnClick">
+                  <SvgIcon name="tool-send-plane" size="22" v-show="!modelLoading"></SvgIcon>
+                  <div style="color: #fff" class="la-ball-running-dots la-sm" v-show="modelLoading">
+                    <div v-for="item in 5" :key="item"></div>
+                  </div>
+                </button>
+              </div>
             </div>
             </div>
+
           </div>
           </div>
         </div>
         </div>
-        <div class="switch-inner pt-[8px] flex justify-between items-center">
-          <div class="space-x-[6px]">
+
+        <div class="switch-inner pt-[8px] flex justify-center items-center">
+          <!-- <div class="space-x-[6px]">
             <NSwitch size="small" v-model:value="switchStatus"></NSwitch>
             <NSwitch size="small" v-model:value="switchStatus"></NSwitch>
             <span class="text-[12px] text-[#9E9E9E]">使用deepseek R1</span>
             <span class="text-[12px] text-[#9E9E9E]">使用deepseek R1</span>
-          </div>
+          </div> -->
           <div>
           <div>
             <TheArchival></TheArchival>
             <TheArchival></TheArchival>
           </div>
           </div>
@@ -419,10 +500,12 @@ defineExpose({
 <style scoped lang="scss">
 <style scoped lang="scss">
 .chat-inp-outer {
 .chat-inp-outer {
   border-radius: 8px;
   border-radius: 8px;
-  overflow: hidden;
   box-shadow: 0px 3px 12px 0px #97D3FF40;
   box-shadow: 0px 3px 12px 0px #97D3FF40;
 
 
   .chat-tools-inner {
   .chat-tools-inner {
+    border-top-left-radius: 8px;
+    border-top-right-radius: 8px;
+    margin-bottom: -3px;
     @include flex(x, center, between);
     @include flex(x, center, between);
 
 
     .tools-tips {
     .tools-tips {
@@ -446,12 +529,13 @@ defineExpose({
 
 
   .chat-inp-inner {
   .chat-inp-inner {
     position: relative;
     position: relative;
-    @include flex(x, center, between);
+    border-radius: 8px;
+    padding: 18px 16px 14px 16px;
     background: #fff;
     background: #fff;
 
 
     .inp-wrapper {
     .inp-wrapper {
       @include flex(x, start, center);
       @include flex(x, start, center);
-      padding: 17px 0px 17px 17px;
+      margin-bottom: 14px;
 
 
       .upload-inner {
       .upload-inner {
         width: 30px;
         width: 30px;
@@ -471,9 +555,69 @@ defineExpose({
       }
       }
     }
     }
 
 
+    .option-wrapper {
+      @include flex(x, center, between);
+
+      .option-list {
+        @include flex(x, center, between);
+        .option {
+          @include flex(x, center, between);
+          height: 32px;
+          padding: 0 8px;
+          border: 1px solid #f5f5f5;
+          border-radius: 8px;
+          font-size: 14px;
+          font-weight: bold;
+          color: #1A2029;
+          cursor: pointer;
+          .icon {
+            display: block;
+            width: 16px;
+            height: 16px;
+          }
+          .circle {
+            width: 6px;
+            height: 6px;
+            border-radius: 100%;
+            background: #BDBDBD;
+          }
+        }
+        .switch-agent {
+          width: 110px;
+          background: #f5f5f5;
+        }
+
+        .internet-agent {
+          transition: all 0.3s ease;
+          .icon {
+            background: url("@/assets/svgs/chat/icon-internet.svg");
+          }
+          &:hover {
+            color: #2E5CFF;
+            .circle {
+              background: #2E5CFF;
+            }
+            .icon {
+              background: url("@/assets/svgs/chat/icon-internet-active.svg");
+            }
+          }
+        }
+
+        .agent-active {
+          background: #eaeff8;
+          color: #2E5CFF;
+          .circle {
+            background: #2E5CFF;
+          }
+          .icon {
+            background: url("@/assets/svgs/chat/icon-internet-active.svg");
+          }
+        }
+      }
+    }
+
     .submit-btn {
     .submit-btn {
       @include flex(x, center, center);
       @include flex(x, center, center);
-      width: 84px;
 
 
       .btn {
       .btn {
         @include flex(x, center, center);
         @include flex(x, center, center);
@@ -615,6 +759,44 @@ defineExpose({
   height: 30px;
   height: 30px;
   background: linear-gradient(180deg, rgba(232, 241, 250, 0) 0%, #E7F0FA 95%);
   background: linear-gradient(180deg, rgba(232, 241, 250, 0) 0%, #E7F0FA 95%);
 }
 }
+</style>
+
+<style lang="scss">
+.chat-inp-outer {
+  .n-input-wrapper {
+    padding: 0;
+  }
+}
 
 
+.option-wrapper {
+  .n-popover {
+    border-radius: 8px !important;
+    box-shadow: none;
+  }
+}
 
 
+.select-agent-options {
+  width: 150px;
+  padding: 12px 19px;
+  border: 1px solid #ddd;
+  border-radius: 8px;
+  background: #fff;
+
+  .agent-option-item {
+    @include flex(x, center, between);
+    cursor: pointer;
+    .agent-title, .agent-sub-title {
+      display: block;
+    }
+    .agent-title {
+      font-size: 14px;
+      font-weight: bold;
+      color: #1A2029;
+    }
+    .agent-sub-title {
+      font-size: 12px;
+      color: #666;
+    }
+  }
+}
 </style>
 </style>

+ 139 - 24
src/components/Chat/ChatText.vue

@@ -1,5 +1,6 @@
 <script setup lang="jsx">
 <script setup lang="jsx">
 import { computed, ref, inject, watchEffect } from 'vue';
 import { computed, ref, inject, watchEffect } from 'vue';
+import { NCollapse, NCollapseItem } from 'naive-ui';
 import MarkdownIt from 'markdown-it';
 import MarkdownIt from 'markdown-it';
 import hljs from 'highlight.js';
 import hljs from 'highlight.js';
 import mila from 'markdown-it-link-attributes';
 import mila from 'markdown-it-link-attributes';
@@ -11,7 +12,8 @@ import markdownItSup from 'markdown-it-sup';
 import anchor from 'markdown-it-anchor';
 import anchor from 'markdown-it-anchor';
 import toc from 'markdown-it-toc-done-right';
 import toc from 'markdown-it-toc-done-right';
 
 
-const updateCatalog = inject('updateCatalog');
+const updateCatalog = inject('updateCatalog', null);
+const thinkStr = ref('');
 
 
 const props = defineProps({
 const props = defineProps({
   content: {
   content: {
@@ -29,6 +31,32 @@ const highlightBlock = (str, lang) => {
   `
   `
 }
 }
 
 
+let buffer = '';
+
+const handleThinkContent = (content) => {
+  thinkStr.value = content
+  console.log('Extracted think content:', content);
+};
+
+const processStreamData = (chunk) => {
+  // 将新接收到的数据追加到缓冲区
+  buffer += chunk;
+
+  // 使用非贪婪匹配尽可能快地找到<think>...</think>的内容
+  const regex = /<think>(.*?)<\/think>/gs;
+
+  let match;
+  while ((match = regex.exec(buffer)) !== null) {
+    const thinkContent = match[1];
+    // 假设你想对<think>内的内容做特殊处理
+    handleThinkContent(thinkContent);
+
+    // 移除已处理的部分
+    buffer = buffer.slice(match.index + match[0].length);
+  }
+
+
+}
 const mdi = new MarkdownIt({
 const mdi = new MarkdownIt({
   html: true,
   html: true,
   linkify: true,
   linkify: true,
@@ -72,9 +100,9 @@ mdi.use(toc, {
   callback: (_, ast) => {
   callback: (_, ast) => {
     setTimeout(_ => {
     setTimeout(_ => {
       const domExists = document.querySelector('.table-of-contents');
       const domExists = document.querySelector('.table-of-contents');
-      if ( updateCatalog ) {
-        if ( domExists ) {
-          const { c:content } = ast;
+      if (updateCatalog) {
+        if (domExists) {
+          const { c: content } = ast;
           updateCatalog(content);
           updateCatalog(content);
         } else {
         } else {
           updateCatalog([]);
           updateCatalog([]);
@@ -90,24 +118,7 @@ const transformText = (text) => {
   });
   });
 }
 }
 
 
-mdi.renderer.rules.echart_block = (tokens, idx, options, env, self) => {
-  const token = tokens[idx];
-  const optionsString = token.content.trim();
-  try {
-    const optionsObj = JSON.parse(optionsString);
-
-    echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
-
-    return `<div id="chart-${idx}"></div>`;
-  } catch (e) {
-    console.error('Invalid ECharts options:', optionsString);
-    return '';
-  }
-};
-
-mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLine, silent) {
-  const startStr = '@@@echarts_b';
-  const endStr = '@@@echarts_e';
+const extractRuler = (startStr, endStr, state, startLine, endLine) => {
 
 
   if (startLine + 1 < state.lineMax && state.src.slice(state.bMarks[startLine] + state.tShift[startLine], state.eMarks[startLine]) === startStr) {
   if (startLine + 1 < state.lineMax && state.src.slice(state.bMarks[startLine] + state.tShift[startLine], state.eMarks[startLine]) === startStr) {
 
 
@@ -135,8 +146,52 @@ mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLi
   }
   }
 
 
   return false;
   return false;
+}
+
+// echart
+mdi.renderer.rules.echart_block = (tokens, idx, options, env, self) => {
+  const token = tokens[idx];
+  const optionsString = token.content.trim();
+  try {
+    const optionsObj = JSON.parse(optionsString);
+
+    echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
+
+    return `<div id="chart-${idx}"></div>`;
+  } catch (e) {
+    console.error('Invalid ECharts options:', optionsString);
+    return '';
+  }
+};
+
+mdi.block.ruler.after('fence', 'echart_block', function (state, startLine, endLine) {
+  const startStr = '@@@echarts_b';
+  const endStr = '@@@echarts_e';
+  return extractRuler(startStr, endStr, state, startLine, endLine);
 });
 });
 
 
+// think tag
+// mdi.renderer.rules.think_tag = (tokens, idx, options, env, self) => {
+//   const token = tokens[idx];
+//   const optionsString = token.content.trim();
+//   try {
+//     // const optionsObj = JSON.parse(optionsString);
+//     // echartOptions[`chart-${idx}`] = { id: idx, code: optionsString, option: optionsObj, isExecute: false }
+//     thinkStr.value = optionsString;
+
+//     return `<div class="testAA">${optionsString}</div>`;
+//   } catch (e) {
+//     console.error('Invalid:', e);
+//     return '';
+//   }
+// };
+
+// mdi.block.ruler.after('fence', 'think_tag', function (state, startLine, endLine) {
+//   const startStr = '<think>';
+//   const endStr = '</think>';
+//   return extractRuler(startStr, endStr, state, startLine, endLine);
+// });
+
 const updateCharts = () => {
 const updateCharts = () => {
   Object.keys(echartOptions).forEach((key) => {
   Object.keys(echartOptions).forEach((key) => {
     if (echartInstance[key]) return;
     if (echartInstance[key]) return;
@@ -171,8 +226,40 @@ function splitStringByChartDiv(str) {
   return result;
   return result;
 }
 }
 
 
+const processedValue = (value) => {
+	return value.replace(/<think>([\s\S]*?)<\/think>/g, (_, content) => {
+	    const lines = content.trim().split('\n');
+	    const quotedLines = lines.map((line) => `> ${line}`).join('\n');
+	    return quotedLines;
+	  });
+};
+
+const thinkParser = (content) => {
+  const regex = /<think>(.*?)<\/think>/gs;
+  const thinkTagRegex = /<think>/;
+  const match = regex.exec(content);
+  if ( match !== null ) {
+    return match[1]
+  }
+  if (thinkTagRegex.test(content)) {
+    return content.slice(7);
+  }
+}
+
+
 watchEffect(() => {
 watchEffect(() => {
-  const value = mdi.render(props.content);
+  const { content } = props;
+  const thinkValue = thinkParser(content);
+  // const tempStr = !thinkValue ? content : content.replace(thinkValue, '')
+
+  const tempStr = !thinkValue ? content : content.replace(thinkValue, '')
+  
+  // processStreamData(content);
+
+  thinkStr.value = thinkValue && mdi.render(thinkValue || '');
+
+  const value = mdi.render(tempStr);
+  // const value = mdi.render(processedValue(content));
 
 
   const result = splitStringByChartDiv(value)
   const result = splitStringByChartDiv(value)
 
 
@@ -181,6 +268,8 @@ watchEffect(() => {
   updateCharts();
   updateCharts();
 })
 })
 
 
+
+
 // 原本的逻辑,暂时不用
 // 原本的逻辑,暂时不用
 const text = computed(() => {
 const text = computed(() => {
   let value = props.content ?? ""
   let value = props.content ?? ""
@@ -190,11 +279,15 @@ const text = computed(() => {
     return mdi.render(value)
     return mdi.render(value)
   return value
   return value
 })
 })
-
 </script>
 </script>
 
 
 <template>
 <template>
   <div class="markdown-body text-[15px] break-all" v-if="content">
   <div class="markdown-body text-[15px] break-all" v-if="content">
+    <n-collapse arrow-placement="right" display-directive="show" class="collapse-wrapper" :default-expanded-names="['thinkTag']" v-if="thinkStr">
+      <n-collapse-item title="已深度思考" name="thinkTag">
+        <div class="think-container" v-html="thinkStr"></div>
+      </n-collapse-item>
+    </n-collapse>
     <div v-for="item, index in markdownText" :key="index" v-html="item"></div>
     <div v-for="item, index in markdownText" :key="index" v-html="item"></div>
   </div>
   </div>
 </template>
 </template>
@@ -207,6 +300,28 @@ const text = computed(() => {
 .markdown-body {
 .markdown-body {
   word-break: break-word;
   word-break: break-word;
 
 
+  .collapse-wrapper {
+    margin-bottom: 20px;
+
+    .n-collapse-item__header-main {
+      font-size: 14px;
+      color: rgba(0, 0, 0, 0.6) !important;
+    }
+  }
+
+  .think-container {
+    border-left: 2px solid rgba(0, 0, 0, 0.08);
+    padding: 4px 0px 4px 12px;
+    margin-bottom: 8px;
+    font-size: 14px;
+    line-height: 26px;
+    color: rgba(0, 0, 0, 0.6);
+    p, li, span {
+      font-size: 14px;
+    }
+  }
+
+
   .custom-table-wrapper {
   .custom-table-wrapper {
     width: 760px;
     width: 760px;
     padding: 10px 10px 4px 10px;
     padding: 10px 10px 4px 10px;

+ 6 - 4
src/components/Layout/TheArchival.vue

@@ -1,11 +1,9 @@
 <script setup>
 <script setup>
 import { NPopover } from 'naive-ui';
 import { NPopover } from 'naive-ui';
-
-
 </script>
 </script>
 
 
-<template >
-  <div class="flex space-x-[20px] text-[12px] text-[#1a2029e5]">
+<template>
+  <div class="flex space-x-[10px] text-[12px] text-[#B0B7C0]">
     <div>
     <div>
       以上内容均由LibraAI生成,仅供参考 
       以上内容均由LibraAI生成,仅供参考 
     </div>
     </div>
@@ -35,6 +33,7 @@ import { NPopover } from 'naive-ui';
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
   .archival {
   .archival {
+    transition: all 0.3s;
     cursor: pointer;
     cursor: pointer;
     &::after {
     &::after {
       content: " ";
       content: " ";
@@ -46,5 +45,8 @@ import { NPopover } from 'naive-ui';
       border-left:4px solid transparent;
       border-left:4px solid transparent;
       border-bottom:4px solid #b0b7c0;
       border-bottom:4px solid #b0b7c0;
     }
     }
+    &:hover {
+      color: #666;
+    }
   }
   }
 </style>
 </style>

+ 2 - 5
src/components/Layout/TheChatView.vue

@@ -48,7 +48,7 @@ defineExpose({ targetScrollDom });
     <div class="catalog-wrapper" v-if="catalogData.length">
     <div class="catalog-wrapper" v-if="catalogData.length">
       <slot name="catalog"></slot>
       <slot name="catalog"></slot>
     </div>
     </div>
-    <div class="chat-wrapper w-full h-full flex flex-col rounded-[20px]">
+    <div class="chat-wrapper w-full h-full flex flex-col rounded-[10px]">
       <div class="chat-header flex items-center justify-between py-[24px] px-[18px] ">
       <div class="chat-header flex items-center justify-between py-[24px] px-[18px] ">
         <div class="left_inner" @click="handleClickBack">
         <div class="left_inner" @click="handleClickBack">
           <span v-if="isBackBtn" class="back-btn"></span>
           <span v-if="isBackBtn" class="back-btn"></span>
@@ -68,7 +68,7 @@ defineExpose({ targetScrollDom });
       <main class="control-main" v-if="!isChatSlot">
       <main class="control-main" v-if="!isChatSlot">
         <slot name="control"></slot>
         <slot name="control"></slot>
       </main>
       </main>
-      <footer class="chat-footer relative w-[900px] m-auto pb-[30px]" v-if="isFooter">
+      <footer class="chat-footer relative w-[900px] m-auto pb-[10px]" v-if="isFooter">
         <slot name="footer" />
         <slot name="footer" />
       </footer>
       </footer>
     </div>
     </div>
@@ -80,9 +80,6 @@ defineExpose({ targetScrollDom });
   position: relative;
   position: relative;
   padding: 20px 20px 20px 0;
   padding: 20px 20px 20px 0;
   overflow: hidden;
   overflow: hidden;
-  
-  .catalog-wrapper {
-  }
 
 
   .chat-header {
   .chat-header {
     .left_inner {
     .left_inner {

+ 1 - 1
src/components/Layout/TheControlView.vue

@@ -62,7 +62,7 @@ defineExpose({ targetScrollDom });
       <main class="control-main" v-if="!isChatSlot">
       <main class="control-main" v-if="!isChatSlot">
         <slot name="control"></slot>
         <slot name="control"></slot>
       </main>
       </main>
-      <footer class="chat-footer relative w-[900px] m-auto pb-[30px]" v-if="isFooter">
+      <footer class="chat-footer relative w-[900px] m-auto pb-[10px]" v-if="isFooter">
         <slot name="footer" />
         <slot name="footer" />
       </footer>
       </footer>
     </div>
     </div>

+ 5 - 2
src/views/answer/AnswerView.vue

@@ -21,7 +21,8 @@ const { recommendList } = useRecommend({ type: 0 });
 
 
 const message = useMessage();
 const message = useMessage();
 
 
-const switchActive = ref(false);
+const onlineSearch = ref(false);
+const switchActive = ref(0);
 const activeItem = ref({});
 const activeItem = ref({});
 
 
 const isLoading = ref(false);
 const isLoading = ref(false);
@@ -107,7 +108,6 @@ const onRegenerate = async ({ showVal, question, realQuestion, tools, uploadFile
     const [ fileItem ] = uploadFileList;
     const [ fileItem ] = uploadFileList;
     fileQuestionStr = `file:${fileItem.name + fileItem.originSuffix}||${fileItem.url}||${question}`
     fileQuestionStr = `file:${fileItem.name + fileItem.originSuffix}||${fileItem.url}||${question}`
   }
   }
-
   const params = {
   const params = {
     data: {
     data: {
       sessionId,
       sessionId,
@@ -117,6 +117,8 @@ const onRegenerate = async ({ showVal, question, realQuestion, tools, uploadFile
       modelType: Number(unref(switchActive)),
       modelType: Number(unref(switchActive)),
       isStrong: Number(unref(switchActive)),
       isStrong: Number(unref(switchActive)),
       tools: tools ? tools : unref(switchActive) ? 'DEEPSEEK' : null,
       tools: tools ? tools : unref(switchActive) ? 'DEEPSEEK' : null,
+      onlineSearch: Boolean(unref(onlineSearch)),
+      // tools,
       prompt: null
       prompt: null
       // TODO: 后续大概率需要删除
       // TODO: 后续大概率需要删除
       // topP: 0.9,
       // topP: 0.9,
@@ -281,6 +283,7 @@ onUnmounted(() => {
           ref="inputRef"
           ref="inputRef"
           v-model:loading="isLoading"
           v-model:loading="isLoading"
           v-model:switch="switchActive"
           v-model:switch="switchActive"
+          v-model:online="onlineSearch"
           @on-click="handleSubmit"
           @on-click="handleSubmit"
           @on-enter="handleSubmit"
           @on-enter="handleSubmit"
         ></ChatAgentInput>
         ></ChatAgentInput>

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

@@ -20,7 +20,8 @@ const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat()
 const helperList = ref([]);
 const helperList = ref([]);
 const message = useMessage();
 const message = useMessage();
 
 
-const switchActive = ref(false);
+const switchActive = ref(0);
+const onlineSearch = ref(false);
 
 
 const isLoading = ref(false);
 const isLoading = ref(false);
 const inputRef = ref(null);
 const inputRef = ref(null);
@@ -111,11 +112,12 @@ const onRegenerate = async ({ showVal, question, tools, uploadFileList }) => {
   const params = {
   const params = {
     data: {
     data: {
       sessionId,
       sessionId,
-      showVal,                        // 展示问题
+      showVal,                                  // 展示问题
       question: fileQuestionStr || question,    // 给大模型的问题
       question: fileQuestionStr || question,    // 给大模型的问题
       module: 2,
       module: 2,
       tools: activeItem.value.tools || tools,
       tools: activeItem.value.tools || tools,
-      isStrong: Number(unref(switchActive))
+      isStrong: Number(unref(switchActive)),
+      onlineSearch: Boolean(unref(onlineSearch)),
     },
     },
     signal: controller.signal,
     signal: controller.signal,
     onDownloadProgress: ({ event }) => {
     onDownloadProgress: ({ event }) => {
@@ -287,6 +289,7 @@ onUnmounted(() => {
           ref="inputRef"
           ref="inputRef"
           v-model:loading="isLoading"
           v-model:loading="isLoading"
           v-model:switch="switchActive"
           v-model:switch="switchActive"
+          v-model:online="onlineSearch"
           @on-click="handleSubmit"
           @on-click="handleSubmit"
           @on-enter="handleSubmit"
           @on-enter="handleSubmit"
         ></ChatAgentInput>
         ></ChatAgentInput>