浏览代码

AI对话窗口

zz 1 月之前
父节点
当前提交
b8cf1e5388

+ 11 - 3
src/assets/icons/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 4552577 */
-  src: url('iconfont.woff2?t=1741226706563') format('woff2'),
-       url('iconfont.woff?t=1741226706563') format('woff'),
-       url('iconfont.ttf?t=1741226706563') format('truetype');
+  src: url('iconfont.woff2?t=1743934202922') format('woff2'),
+       url('iconfont.woff?t=1743934202922') format('woff'),
+       url('iconfont.ttf?t=1743934202922') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,14 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.zj-zanxia:before {
+  content: "\e702";
+}
+
+.zj-damuzhi:before {
+  content: "\e705";
+}
+
 .zj-purchase:before {
   content: "\e700";
 }

文件差异内容过多而无法显示
+ 0 - 0
src/assets/icons/iconfont.js


+ 14 - 0
src/assets/icons/iconfont.json

@@ -5,6 +5,20 @@
   "css_prefix_text": "zj-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "3165972",
+      "name": "大拇指",
+      "font_class": "zanxia",
+      "unicode": "e702",
+      "unicode_decimal": 59138
+    },
+    {
+      "icon_id": "17537425",
+      "name": "大拇指",
+      "font_class": "damuzhi",
+      "unicode": "e705",
+      "unicode_decimal": 59141
+    },
     {
       "icon_id": "9190630",
       "name": "采购部",

二进制
src/assets/icons/iconfont.ttf


二进制
src/assets/icons/iconfont.woff


二进制
src/assets/icons/iconfont.woff2


+ 11 - 0
src/router/index.js

@@ -970,6 +970,17 @@ export const constantRoutes = [
           // nocrumb: true
         },
       },
+      {
+        path: '/test',
+        name: 'test',
+        component: () => import('@/views/test/test.vue'),
+        hidden: true,
+        meta: {
+          title: '测试',
+          keepAlive: 1,
+          canMultipleOpen: true
+        },
+      }
     ]
   }
 ];

+ 172 - 67
src/views/allcase/components/CaseQualityBox2.vue

@@ -1,83 +1,129 @@
 <template>
-<div>
 
-  <div ref="box" class="box22" :style="{width: width ? width + 'px' : '100%'}" :class="{'nocopy': $route.meta.nocopy}">
+<div>
+  <el-tabs v-model="tabsActive">
+    <el-tab-pane v-for="(item, index) in tabsArray" :key="index" :label="item.label" :name="item.name" >
+    </el-tab-pane>
+  </el-tabs>
 
-    <div class="score-box" :class="scoreLevel == '甲'? 'scoreLevel_1' : ( scoreLevel == '乙'?'scoreLevel_2': (scoreLevel == '丙'?'scoreLevel_3':'' ) ) ">
-      <div>病案评分<span class="score-f">{{ data.score  }}分</span></div>
-      <!-- <h2 class="score-dj">{{ scoreLevel }}</h2> -->
+  <div v-if="tabsActive == 1">
+    <div style="width: 420px;">
+      <div class="message" style="overflow-y: auto;max-height: 480px;height: 480px;background-color: white">
+        <div v-for="(item,index) in chatMessages" :key="index">
+          <div v-if="item.role == 'user'" class="user-message">
+            <el-avatar  style="margin-left: 10px;" src="static/img/avatar07.7b002992.png" fit="contain"></el-avatar>
+            <div class="user-content" style="line-height: 40px;background-color: #00C797;margin-left: 10px;border-radius: 5px;padding: 0 10px 0 10px;">
+              {{item.content}}
+            </div>
+          </div>
+          <div v-if="item.role == 'assistant'" class="record-message" style="display: flex;justify-content: right;margin-bottom: 20px;">
+            <div class="record-avatar" style="box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);width: 360px;margin-right: 10px;margin-left:10px; padding: 0 10px 0 10px;background-color: white;border-radius: 5px;line-height: 40px;">
+              {{item.content}}
+            </div>
+            <el-avatar  style="margin-right: 10px;" :src="require('../../../assets/images/jiqiren.png')" fit="contain"></el-avatar>
+          </div>
+        </div>
+      </div>
+      <div style="margin: 20px;border-top: 1px solid blue;border-right: 1px solid blue;border-radius: 10px;display: flex;background-color: white">
+        <div style="width: 80px;border-radius: 10px 0 0 10px;  background-color: #DDDDDD;display: flex;flex-wrap: wrap;justify-content: center;padding-bottom: 5px;">
+          <button style="width: 60px;margin-top: 5px;border-radius: 5px;border: 1px solid blue;color: blue">“ 病历</button>
+          <button style="width: 60px;margin-top: 5px;border-radius: 5px;border: 1px solid blue;color: blue">“ 检查</button>
+          <button style="width: 60px;margin-top: 5px;border-radius: 5px;border: 1px solid blue;color: blue">“ 检验</button>
+          <button style="width: 60px;margin-top: 5px;border-radius: 5px;border: 1px solid blue;color: blue">“知识库</button>
+        </div>
+        <div style="width: calc(100% - 90px)">
+          <textarea v-model="inputMessage" placeholder="请输入您的问题,帮您深度解答" style="height: 80px;border: none;outline:none; width: 100%;padding: 10px;"></textarea>
+          <div style="display: flex;margin-bottom: 10px;">
+            <el-icon class="iconfont zj-damuzhi" style="margin-left: 20px;line-height: 35px;"></el-icon>
+            <el-icon class="iconfont zj-zanxia" style="margin-left: 20px;line-height: 35px;"></el-icon>
+            <el-button @click="sendMessage" type="primary" style="margin-left: auto;height: 30px;line-height: 0;margin-right: 10px;">发送</el-button>
+          </div>
+        </div>
+      </div>
     </div>
+  </div>
+
+  <!--病案首页-->
+  <div v-else-if="tabsActive == 2">
+    <div ref="box" class="box22" :style="{width: width ? width + 'px' : '100%'}" :class="{'nocopy': $route.meta.nocopy}">
 
-    <div class="card-box" v-if="data.is_case">
-      <div class="title">
-        <span class="font-size12">智能结果更新时间:</span>
-        <span class="font-size12">{{ data.quality_time }}</span>
+      <div class="score-box" :class="scoreLevel == '甲'? 'scoreLevel_1' : ( scoreLevel == '乙'?'scoreLevel_2': (scoreLevel == '丙'?'scoreLevel_3':'' ) ) ">
+        <div>病案评分<span class="score-f">{{ data.score  }}分</span></div>
+        <!-- <h2 class="score-dj">{{ scoreLevel }}</h2> -->
       </div>
-      <el-tag type="danger" v-if="data.is_case == 1" class="font-size12">质控中</el-tag>
-      <el-tag v-if="data.is_case == 2" class="font-size12">已质控</el-tag>
-      
-    </div>   
 
+      <div class="card-box" v-if="data.is_case">
+        <div class="title">
+          <span class="font-size12">智能结果更新时间:</span>
+          <span class="font-size12">{{ data.quality_time }}</span>
+        </div>
+        <el-tag type="danger" v-if="data.is_case == 1" class="font-size12">质控中</el-tag>
+        <el-tag v-if="data.is_case == 2" class="font-size12">已质控</el-tag>
 
-    <el-scrollbar ref="scrollRef" class="scrollBox" :style="`height: ${scrollHeight};padding-bottom:60px;`">
-      <template v-for="(item, index) in tableData">
-        <!-- is_appeal: 是否申述 -->
-        <div class="list-box box-card" :key="index" v-if="is_show && (!item.is_appeal || (item.is_appeal == 1 && item.appeal_status == 2))">
-          <div class="list-score-tips-box">
-            <div class="list-left-score" :class=" item.level == 1 ? 'hover-1' : 'hover-2' ">
-              <div> {{ item.level == 1? '必改':'建议' }} </div>
-              <div>-{{ item.score }}</div>
-            </div>
-            <div class="list-right-tips">
-              <div><span class="title-color">字段:</span><span>{{ item.category }}</span></div>
-              <div class="notice-box"><span class="title-color">提示:</span><span>{{ item.notice }}</span></div>
-            </div>
-          </div>
-          <div class="list-basis-box">
-            <div class="list-basis-title" @click="clickListItem(index)">
-              <span>质控依据</span>
-              <span> >> </span>
-              <el-image class="typeImg" v-if="item.is_ai" :src="require('../../../assets/images/jiqiren.png')" fit="contain">
-              </el-image>
-              <el-image v-else class="typeImg" :src="require('../../../assets/images/kefu.png')" fit="contain">
-              </el-image>
+      </div>
+
+
+      <el-scrollbar ref="scrollRef" class="scrollBox" :style="`height: ${scrollHeight};padding-bottom:60px;`">
+        <template v-for="(item, index) in tableData">
+          <!-- is_appeal: 是否申述 -->
+          <div class="list-box box-card" :key="index" v-if="is_show && (!item.is_appeal || (item.is_appeal == 1 && item.appeal_status == 2))">
+            <div class="list-score-tips-box">
+              <div class="list-left-score" :class=" item.level == 1 ? 'hover-1' : 'hover-2' ">
+                <div> {{ item.level == 1? '必改':'建议' }} </div>
+                <div>-{{ item.score }}</div>
+              </div>
+              <div class="list-right-tips">
+                <div><span class="title-color">字段:</span><span>{{ item.category }}</span></div>
+                <div class="notice-box"><span class="title-color">提示:</span><span>{{ item.notice }}</span></div>
+              </div>
             </div>
-            <div class="list-basis-text">
-              <div class="list-basis-text-t" :class="item.show?'show':''">
-                <div v-for="(yItem, yIndex) of item.basis" :key="yIndex" style="margin-bottom: 10px;">
-                  <div v-if="item.rule_id !== 6">
-                    <span class="span-index">{{ yIndex+1 }}</span>
-                    <span v-if="item.category=='入院记录'">
+            <div class="list-basis-box">
+              <div class="list-basis-title" @click="clickListItem(index)">
+                <span>质控依据</span>
+                <span> >> </span>
+                <el-image class="typeImg" v-if="item.is_ai" :src="require('../../../assets/images/jiqiren.png')" fit="contain">
+                </el-image>
+                <el-image v-else class="typeImg" :src="require('../../../assets/images/kefu.png')" fit="contain">
+                </el-image>
+              </div>
+              <div class="list-basis-text">
+                <div class="list-basis-text-t" :class="item.show?'show':''">
+                  <div v-for="(yItem, yIndex) of item.basis" :key="yIndex" style="margin-bottom: 10px;">
+                    <div v-if="item.rule_id !== 6">
+                      <span class="span-index">{{ yIndex+1 }}</span>
+                      <span v-if="item.category=='入院记录'">
                       <span v-for="(cItem, cIndex) of yItem" :key="cIndex" @click="hightRight(cItem,292,item.JZHM)" v-html="cItem"></span>
                     </span>
-                    <span v-else>
+                      <span v-else>
                       <span v-for="(cItem, cIndex) of yItem" :key="cIndex" v-html="cItem"></span>
                     </span>
+                    </div>
+                    <div v-else>
+                      <span class="span-index">1</span>
+                      <span style="font-size: 13px;">{{ yItem[0] }}</span>
+                    </div>
                   </div>
-                  <div v-else>
-                    <span class="span-index">1</span>
-                    <span style="font-size: 13px;">{{ yItem[0] }}</span>
-                  </div>
-                </div>
-              </div>
-              <div class="list-basis-bottom-box">
-                <div class="list-basis-bottom-tips">
-                  <el-tooltip class="appeal-status-box" effect="dark" :content="item.reject_content" placement="top">
-                    <el-button type="danger" class="appeal-status-2"  v-if="item.appeal_status == 2">驳回</el-button>
-                  </el-tooltip>
                 </div>
-                <div class="list-basis-bottom-btn" v-if="data.valData != 'sl_001'">
-                  <el-button type="primary" @click="clickAppeal( item,index,2 )">申诉</el-button>
-                  <el-button @click="clickAppeal( item,index,1 )">忽略</el-button>
+                <div class="list-basis-bottom-box">
+                  <div class="list-basis-bottom-tips">
+                    <el-tooltip class="appeal-status-box" effect="dark" :content="item.reject_content" placement="top">
+                      <el-button type="danger" class="appeal-status-2"  v-if="item.appeal_status == 2">驳回</el-button>
+                    </el-tooltip>
+                  </div>
+                  <div class="list-basis-bottom-btn" v-if="data.valData != 'sl_001'">
+                    <el-button type="primary" @click="clickAppeal( item,index,2 )">申诉</el-button>
+                    <el-button @click="clickAppeal( item,index,1 )">忽略</el-button>
+                  </div>
                 </div>
               </div>
+
             </div>
-            
           </div>
-        </div>
-      </template>
-    </el-scrollbar>
+        </template>
+      </el-scrollbar>
+    </div>
   </div>
+
 </div>
 
 </template>
@@ -114,6 +160,15 @@
     },
     data(){
       return {
+        inputMessage:'',//消息输入框
+        chatMessages:[],//内容显示
+        tabsActive:'2',//tabs选择
+        tabsArray:[
+          {'name':'1','label':'病历生成'},
+          {'name':'2','label':'病案首页'},
+          {'name':'3','label':'住院病历'},
+          {'name':'4','label':'编目首页'},
+        ],
         tableData: [],
         is_show: true,
         quality_type: 2,  // 1、首页质控 2、病例质控
@@ -125,7 +180,7 @@
          * 甲>90分
          * 乙75-90分
          * 丙<75分
-         * */ 
+         * */
         let str
         const { score } = this.data
         if (score > 90) {
@@ -150,6 +205,53 @@
       this.getTableData();
     },
     methods: {
+      //发送消息
+      async sendMessage(){
+        const messages = this.inputMessage.trim();
+        if (messages){
+          //添加用户信息到聊天记录
+          this.chatMessages.push({
+            role: 'user',
+            content: messages
+          });
+
+          // 清空输入框
+          this.inputMessage = '';
+
+          //大模型请求
+          const modelResponse = await this.simulateModelResponse(messages);
+
+          // 添加模型响应消息到聊天记录
+          this.chatMessages.push({
+            role: 'assistant',
+            content: modelResponse
+          });
+        }
+      },
+      //大模型请求
+      simulateModelResponse(userMessage) {
+        //请求内容
+        const options = {
+          method: 'POST',
+          headers: {
+            Authorization: 'Bearer sk-ttnryoxayznjgquagdfwjefbkubdnezabsotuczokunwkjin',
+            'Content-Type': 'application/json'
+          },
+          // body: '{"model":"Qwen/QwQ-32B","messages":[{"role":"user","content":"当前时间"}],"stream":false,"max_tokens":512,"stop":null,"temperature":0.7,"top_p":0.7,"top_k":50,"frequency_penalty":0.5,"n":1,"response_format":{"type":"text"},"tools":[{"type":"function","function":{"description":"<string>","name":"<string>","parameters":{},"strict":false}}]}'
+        };
+        const body = {};
+        body.model = "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B";
+        body.messages = [{"role":"user","content":userMessage}];
+        options.body = JSON.stringify(body);
+
+        //请求
+        return  fetch('https://api.siliconflow.cn/v1/chat/completions', options).then(response => {
+          return response.json();
+        }).then(data => {
+          const content = data.choices[0].message.content;
+          return content;
+        }).catch(err => console.error(err));
+      },
       getTableData(){
         let data = this.data.data;
         for(let i=0; i<data.length; i++) {
@@ -174,7 +276,7 @@
         tableData[idx].show = !tableData[idx].show;
         this.is_show = false;
         this.$nextTick( () =>{
-          this.tableData = tableData; 
+          this.tableData = tableData;
           this.is_show = true;
         })
         console.log(tableData)
@@ -200,12 +302,12 @@
               type: 'success',
               message: res.msg
             });
-            
+
             setTimeout( () =>{
               tableData.splice(index,1);
               that.tableData = tableData;
             },2000)
-            
+
           }else{
             that.$message({
               type: 'error',
@@ -219,6 +321,9 @@
 </script>
 
 <style lang="scss" scoped>
+.message{border-bottom: 1px solid gray;margin-bottom: 20px;}
+.user-message{display: flex;margin-bottom: 20px;margin-top: 10px;}
+
 ::v-deep .el-scrollbar__wrap {
   overflow-x: hidden;
 }
@@ -460,7 +565,7 @@
         right: 12px;
         z-index: 999;
       }
-      
+
     }
     .list-basis-text {
       height: auto;
@@ -506,9 +611,9 @@
       }
     }
 
-    
+
 
   }
 }
 
-</style>
+</style>

+ 3 - 2
src/views/allcase/index.vue

@@ -64,7 +64,7 @@
               <div style="width:94%;display:flex;justify-content:flex-end">
                 <el-button class="btn1" type="primary" @click="funQuery">查询</el-button>
                 <el-button @click="reset">重置</el-button>
-              </div>  
+              </div>
             </el-form-item>
           </el-col>
         </el-row>
@@ -413,7 +413,7 @@ export default {
       },
       // caseSearchData: {
       //   department: ''
-      // }, 
+      // },
       caseList: [], // 缺陷问题
       // departmentList: [],
       // doctorList: [], // 医生列表
@@ -530,6 +530,7 @@ export default {
 
   },
   mounted() {
+    console.log('123');
     this.getSearchOptions();
     this.doctor_rank = JSON.parse(JSON.stringify(this.doctor_tableData)).slice(0, 10)
 

+ 83 - 0
src/views/test/test.vue

@@ -0,0 +1,83 @@
+<template>
+  <el-card style="height: 1000px;">
+    <div class="flex-1 overflow-y-auto p-4 space-y-4" ref="chatContainer">
+      <div v-for="(message, index) in chatMessages" :key="index"
+           :class="message.role === 'user' ? 'flex justify-end' : 'flex justify-start'">
+        <div :class="message.role === 'user' ? 'bg-blue-500 text-white rounded-md p-2 max-w-xs' : 'bg-gray-300 text-gray-800 rounded-md p-2 max-w-xs'">
+          {{ message.content }}
+        </div>
+      </div>
+    </div>
+    <div style="display: flex;justify-content: center;height: 300px;position: fixed ;bottom: -180px;left: 0;right: 0">
+      <el-input type="textarea" style="width: 800px;height: 300px;" placeholder="请输入" v-model="inputMessage">
+      </el-input>
+      <i class="el-icon-position" style="font-size: 28px;margin-top: 10px;margin-left: 10px;" @click="sendMessage"></i>
+    </div>
+  </el-card>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      inputMessage:'',
+      chatMessages:[],
+    };
+  },
+  mounted() {
+  },
+  methods:{
+    async sendMessage(){
+      const messages = this.inputMessage.trim();
+      if (messages){
+        //添加用户信息到聊天记录
+        this.chatMessages.push({
+          role: 'user',
+          content: messages
+        });
+
+        // 清空输入框
+        this.inputMessage = '';
+
+        //大模型请求
+        const modelResponse = await this.simulateModelResponse(messages);
+        console.log(modelResponse);
+
+        // 添加模型响应消息到聊天记录
+        this.chatMessages.push({
+          role: 'assistant',
+          content: modelResponse
+        });
+      }
+    },
+    //大模型请求
+    simulateModelResponse(userMessage) {
+      //请求内容
+      const options = {
+        method: 'POST',
+        headers: {
+          Authorization: 'Bearer sk-ttnryoxayznjgquagdfwjefbkubdnezabsotuczokunwkjin',
+          'Content-Type': 'application/json'
+        },
+       // body: '{"model":"Qwen/QwQ-32B","messages":[{"role":"user","content":"当前时间"}],"stream":false,"max_tokens":512,"stop":null,"temperature":0.7,"top_p":0.7,"top_k":50,"frequency_penalty":0.5,"n":1,"response_format":{"type":"text"},"tools":[{"type":"function","function":{"description":"<string>","name":"<string>","parameters":{},"strict":false}}]}'
+      };
+      const body = {};
+      body.model = "Qwen/QwQ-32B";
+      body.messages = [{"role":"user","content":userMessage}];
+      options.body = JSON.stringify(body);
+
+      //请求
+      return  fetch('https://api.siliconflow.cn/v1/chat/completions', options).then(response => {
+        return response.json();
+      }).then(data => {
+        const content = data.choices[0].message.reasoning_content;
+        return content;
+      }).catch(err => console.error(err));
+    },
+  },
+};
+</script>
+
+<style>
+
+</style>

部分文件因为文件数量过多而无法显示