Explorar el Código

深度思考对话版

liuhj hace 3 meses
padre
commit
1a7197a32c
Se han modificado 1 ficheros con 311 adiciones y 10 borrados
  1. 311 10
      src/components/Resource/VisitSchedule.vue

+ 311 - 10
src/components/Resource/VisitSchedule.vue

@@ -1,13 +1,61 @@
 <template>
     <div class="VisitSchedule-all" v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="耗时较长请耐心等待">
-        <el-dialog :before-close="handleBeforeClose" :close-on-click-modal="false" title="深度思考" :visible.sync="RequestDocumentVisible" @close="handleDialogClose">
+        <el-dialog top="2vh" :before-close="handleBeforeClose" :close-on-click-modal="false" title="深度思考" :visible.sync="RequestDocumentVisible" @close="handleDialogClose">
             <div>
-                <div class="RequestDocument-box" style="white-space:pre-line;height: 500px;overflow-y: auto;"
-                    v-html="RequestDocument"></div>
-                <div class="RequestDocument-btn">
+                <div class="RequestDocument-box" style="white-space:pre-line;height: 654px;overflow-y: auto;" @scroll="handleScroll">
+                    <div v-for="(item,index) in RequestDocumentArr" :key="index">
+                        <div v-if="item.role==='assistant'" class="RequestDocument-assistant"
+                        v-html="item.content===''?RequestDocument:item.content"></div>
+                        <div v-else style="text-align: right;white-space: normal;">
+                            <span class="RequestDocument-usre">
+                                {{ item.content }}
+                            </span>
+                        </div>
+                    </div>
+                </div>
+                <div class="RequestDocument-itp">
+                    <textarea 
+                    v-model="RequestDocumentitp" 
+                    class="native-textarea" 
+                    placeholder="与AI对话"
+                        rows="3">
+                    </textarea>
+                    <div style="text-align: right;display: flex;justify-content: end;align-items: center;">
+                        <el-tooltip @click="BusinessInvitationInstructionsFileDown" class="item" effect="dark" content="下载请示文件" placement="top">
+                            <div class="Dialogue-top" style="line-height: 40px;margin-right: 6px;">
+                                <i class="el-icon-download" style="color: #fff;font-size: 24px;font-weight: 600;"></i>
+                            </div>
+                        </el-tooltip>
+                        <el-tooltip v-if="RequestDocumentitp === ''" class="item" effect="dark" content="输入你的问题!"
+                            placement="top">
+                            <div class="Dialogue-top" style="background-color: #78baff;">
+                                <i class="el-icon-top" style="color: #fff;font-size: 24px;font-weight: 600;"></i>
+                            </div>
+                        </el-tooltip>
+                        <div v-else>
+                            <el-tooltip v-if="isStreaming" class="item" effect="dark" content="停止生成!"
+                                placement="top">
+                                <div @click="cancelStream" class="Dialogue-top" style="background-color: #78baff;">
+                                    <i class="el-icon-loading"
+                                        style="color: #fff;font-size: 24px;font-weight: 600;"></i>
+                                </div>
+                            </el-tooltip>
+                            <!-- <div v-else class="Dialogue-top">
+                                <i class="el-icon-top" style="color: #fff;font-size: 24px;font-weight: 600;"></i>
+                            </div> -->
+                            <div v-else @click="UserDialogue" class="Dialogue-top">
+                                <i class="el-icon-top" style="color: #fff;font-size: 24px;font-weight: 600;"></i>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <!-- <div class="RequestDocument-box" style="white-space:pre-line;height: 500px;overflow-y: auto;"
+                    v-html="RequestDocument"></div> -->
+                <!-- <div class="RequestDocument-btn">
                     <el-button @click="BusinessInvitationInstructionsFileDown" type="primary">下载请示文件</el-button>
+                    <el-button @click="DialogueData" type="primary">缓存</el-button>
                     <el-button @click="cancelStream" type="primary">取消生成</el-button>
-                </div>
+                </div> -->
             </div>
         </el-dialog>
         <el-dialog :close-on-click-modal="false" width="400px" title="单位" :visible.sync="UnitVisible">
@@ -114,6 +162,12 @@ export default {
             unittwo: '',
             RequestDocumentVisible: false,
             RequestDocument: '',
+            RequestDocumentArr:[
+                {
+                    role: "assistant",
+                    content: ""
+                },
+            ],
             typewriterText: '',
             typewriterTimer: null,
             typewriterQueue: [],
@@ -121,9 +175,11 @@ export default {
             isStreaming: false,      // 是否正在流式传输
             autoScrollEnabled: true,
             streamController: null,    // 保存控制器,用于中断
+            streamControllers: null,    // 保存控制器,用于中断
             downbtn:true,
             downbtns:false,
-            ContentText:''
+            ContentText:'',
+            RequestDocumentitp:'',
         }
     },
     computed: {
@@ -146,6 +202,25 @@ export default {
     },
 
     methods: {
+        //对话数据缓存一下不重复访问接口
+        DialogueData(){
+            // this.RequestDocumentArr.push(
+            //     {
+            //         role: "assistant",
+            //         content: localStorage.getItem('RequestDocument')
+            //     },
+            // )
+            
+            // localStorage.setItem("RequestDocument",this.RequestDocument)
+        },
+        //马克荡
+        displayContents(val) {
+            // 如果需要 Markdown 渲染
+            if (this.$marked && typeof this.$marked === 'function') {
+                return this.$marked(val);
+            }
+            return (val).replace(/\n/g, '<br/>');
+        },
         //报批行程初始化
         ApprovalJourneyInit() {
             this.groupInfo = {};
@@ -299,6 +374,32 @@ export default {
                 this.autoScrollEnabled = false;
             }
         },
+        //滚动到最底部
+        RollingBelow(text){
+            let index = 0;
+            const fullText = text;
+            if (index < fullText.length) {
+                this.typewriterText += fullText[index];
+                index++;
+            }
+            this.RequestDocument+= text;
+        },
+        /**
+        * 滚动事件处理 - 用户滚动时判断是否启用自动滚动
+        */
+        handleScroll(e) {
+            const container = e.target;
+            // 判断是否滚动到底部(允许50px误差)
+            const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 50;
+
+            if (isAtBottom) {
+                // 滚动到底部时,恢复自动滚动
+                this.autoScrollEnabled = true;
+            } else {
+                // 离开底部时,禁用自动滚动
+                this.autoScrollEnabled = false;
+            }
+        },
 
         /**
          * 打字机效果
@@ -315,6 +416,7 @@ export default {
 
             const type = () => {
                 if (index < fullText.length) {
+                    
                     this.typewriterText += fullText[index];
                     index++;
                     this.typewriterTimer = setTimeout(type, speed);
@@ -353,14 +455,117 @@ export default {
          */
         onStreamComplete() {
             console.log('✅ 流式传输已完成');
+            for (let i = 0; i < this.RequestDocumentArr.length; i++) {
+                if (this.RequestDocumentArr[i].content=='') {
+                    this.RequestDocumentArr[i].content=this.RequestDocument;
+                }
+            }
+            // this.RequestDocumentArr[0].content=this.RequestDocument;
             this.isStreaming = false;
         },
 
+
+        //用户对话
+        UserDialogue(){
+            this.RequestDocumentArr.push(
+                {
+                    role: "user",
+                    content: this.RequestDocumentitp
+                },
+            )
+            this.RequestDocumentitp='';
+            this.deepseekchat();
+        },
+        //对话接口
+        deepseekchat(){
+            // 重置状态
+            this.resetTypewriter();
+            this.isStreaming = true;
+            
+            this.fullscreenLoading = true;
+            // 绑定滚动事件
+            this.$nextTick(() => {
+                const container = document.querySelector('.RequestDocument-box');
+                if (container) {
+                    container.addEventListener('scroll', this.handleScroll);
+                }
+            });
+            const url = "http://132.232.92.186:8888/api/AITest/deepseek-chat-stream-with-history";
+            const that = this;
+            this.ContentText = '';
+            var biaoshi=true;
+            // 发起流式请求,传入完成回调
+            this.streamControllers = streamRequestPOST(
+                url,
+                { 
+                    messages: that.RequestDocumentArr,
+                    model:'deepseek-reasoner',
+                    temperature:0.7,
+                    maxTokens:60000
+                },
+                (data) => {
+                    if(biaoshi){
+                        that.RequestDocumentArr.push(
+                            {
+                                role: "assistant",
+                                content: ""
+                            },
+                        )
+                        biaoshi=false;
+                    }
+                    // that.RequestDocumentVisible = true;
+                    that.fullscreenLoading = false;
+                    // 处理流式返回的数据
+                    let newText = '';
+                    if (typeof data === 'string') {
+                        newText = data;
+                    } else if (data.text) {
+                        newText = data.text;
+                    } else if (data.content) {
+                        newText = data.content;
+                    } else {
+                        newText = JSON.stringify(data);
+                    }
+                    if (newText) {
+                        // that.startTypewriter(newText, 0);
+                        that.RollingBelow(newText);
+                    }
+                    if (data.phase=='content') {
+                        that.ContentText += data.text;
+                    }
+                    if (data.phase=='success') {
+                        
+                    }
+                },(error) => {
+                    // 错误处理
+                    console.error('流式请求失败:', error);
+                    that.isStreaming = false;
+                    that.$message.error('深度思考链接中断或链接失效,请关闭弹窗界面后,再重试!');
+                },(res) => {
+                    // 完成回调 - 流式传输真正结束时触发
+                    if (that.RequestDocumentVisible==false&&that.fullscreenLoading == true) {
+                       that.$message.error('没有信息无法生成!');
+                    }
+                    that.fullscreenLoading = false;
+                    that.onStreamComplete();
+                    // setTimeout(() => {
+                    // }, 5000);
+                }
+            );
+        },
         /**
          * 流式接收并显示请示文件(带完成回调)
          */
         BusinessInvitationInstructionsFileContentStream() {
             // 重置状态
+            this.RequestDocumentArr=[
+                {
+                    role: "assistant",
+                    content: ""
+                },
+            ];
+            this.RequestDocument="";
+            this.RequestDocumentitp="";
             this.resetTypewriter();
             this.isStreaming = true;
             
@@ -372,7 +577,6 @@ export default {
                     container.addEventListener('scroll', this.handleScroll);
                 }
             });
-
             const url = "http://132.232.92.186:8888/api/Groups/BusinessInvitationInstructionsFileContentStream";
             const that = this;
             this.ContentText = '';
@@ -395,7 +599,9 @@ export default {
                         newText = JSON.stringify(data);
                     }
                     if (newText) {
-                        that.startTypewriter(newText, 0);
+                        // that.RequestDocument+= newText;
+                        // that.startTypewriter(newText, 0);
+                        that.RollingBelow(newText);
                     }
                     if (data.phase=='content') {
                         that.ContentText += data.text;
@@ -412,10 +618,11 @@ export default {
                     }
                     that.fullscreenLoading = false;
                     that.onStreamComplete();
+                    // setTimeout(() => {
+                    // }, 5000);
                 }
             );
         },
-
         /**
          * 手动中断流式请求
          */
@@ -425,14 +632,37 @@ export default {
                 cancelButtonText: '取消',
                 type: 'warning'
             }).then(() => {
-                if (this.streamController) {
+                if (this.streamController||this.streamControllers) {
                     this.RequestDocumentVisible = false;
                     this.streamController.abort();
+                    this.streamControllers.abort();
                     this.isStreaming = false;
                     this.$message.info('已停止生成');
+                    this.RequestDocumentArr=[
+                        {
+                            role: "assistant",
+                            content: ""
+                        },
+                    ];
+                    this.RequestDocument="";
                 }else {
                     this.$message.warning('没有正在生成的数据数据已生成完毕');
                 }
+                // if (this.streamControllers) {
+                //     this.RequestDocumentVisible = false;
+                //     this.streamControllers.abort();
+                //     this.isStreaming = false;
+                //     this.RequestDocumentArr=[
+                //         {
+                //             role: "assistant",
+                //             content: ""
+                //         },
+                //     ];
+                //     this.RequestDocument="";
+                //     this.$message.info('已停止生成');
+                // }else {
+                //     this.$message.warning('没有正在生成的数据数据已生成完毕');
+                // }
             }).catch(() => {});
         },
 
@@ -451,7 +681,26 @@ export default {
                     // 中断进行中的请求
                     if (this.streamController) {
                         this.streamController.abort();
+                        this.RequestDocumentArr=[
+                            {
+                                role: "assistant",
+                                content: ""
+                            },
+                        ];
+                        this.RequestDocument="";
                     }
+                    if (this.streamControllers) {
+                        this.streamControllers.abort();
+                        this.RequestDocumentArr=[
+                            {
+                                role: "assistant",
+                                content: ""
+                            },
+                        ];
+                        this.RequestDocument="";
+                    }
+                    this.RequestDocumentArr=[];
+                    this.RequestDocument="";
                     done(); // 执行关闭
                 }).catch(() => {
                     // 用户取消关闭,不做任何操作
@@ -767,6 +1016,9 @@ export default {
         if (this.streamController) {
             this.streamController.abort();
         }
+        if (this.streamControllers) {
+            this.streamControllers.abort();
+        }
         if (this.typewriterTimer) {
             clearTimeout(this.typewriterTimer);
         }
@@ -850,4 +1102,53 @@ export default {
 .RequestDocument-btn{
     text-align: right;
 }
+.RequestDocument-assistant{
+    background-color: #f0f2f3;
+    padding: 10px;
+    border-radius: 10px;
+    margin-bottom: 30px;
+}
+.RequestDocument-usre{
+    background-color: #EDF3FE;
+    padding: 10px;
+    border-radius: 10px;
+    margin-bottom: 30px;
+    display: inline-block;
+}
+.RequestDocument-itp{
+    padding: 10px;
+    border-radius: 10px;
+    box-shadow: 0 4px 10px rgba(0, 0, 0, .2), 0 2px 4px rgba(0, 0, 0, .04);
+}
+.native-textarea {
+  /* 完全无样式,或按需添加 */
+  border: none;
+  background: transparent;
+  outline: none;
+  padding: 0;
+  margin: 0;
+  font: inherit;
+  width: 100%;
+  resize: none;
+  /* 继承父级样式 */
+  color: inherit;
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+
+/* 可选:自定义滚动条 */
+.native-textarea::-webkit-scrollbar {
+  width: 6px;
+}
+.Dialogue-top{
+    background-color: #409EFF;
+    width: 35px;
+    height: 35px;
+    display: inline-block;
+    border-radius: 50%;
+    text-align: center;
+    line-height: 44px;
+    cursor: pointer;
+}
 </style>