From e295922209fb87c6dcd68ea1560fd16c3e6d808c Mon Sep 17 00:00:00 2001
From: dongyukun <1208714201@qq.com>
Date: 星期五, 27 六月 2025 09:36:51 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/feature/ai'

---
 src/views/ai/dashboard/zhuanlu/index.vue |  338 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 274 insertions(+), 64 deletions(-)

diff --git a/src/views/ai/dashboard/zhuanlu/index.vue b/src/views/ai/dashboard/zhuanlu/index.vue
index 9feb5d0..16e7643 100644
--- a/src/views/ai/dashboard/zhuanlu/index.vue
+++ b/src/views/ai/dashboard/zhuanlu/index.vue
@@ -26,9 +26,15 @@
       <div id="tsxx">
         <div class="title"></div>
         <div class="data1-item" v-for="(item, index) in tsxxList" :key="`dynamics-${index}`">
-          <div class="content">
-            <div class="value">
-              <span>{{item.value}}</span> <span>{{item.unit}}</span>
+          <div class="content1">
+            <div class="value" v-if="item.type == 1">
+              <div class="item" v-for="(list, i) in item.lists" :key="`dynamics-${i}`">
+                <span>{{list.no}}</span><span>{{list.value}}</span>
+              </div>
+            </div>
+            <div class="value" v-else>
+              <span v-if="item.value == '进行'" style="color: #49FFD3; font-size: 14px; font-weight: bold;">{{item.value}}</span>
+              <span v-else style="color: #FFAE81; font-size: 14px; font-weight: bold;">{{item.value}}</span>
             </div>
             <div class="name">
               {{item.name}}
@@ -179,11 +185,11 @@
     <div class="gas-scheduling-right">
       <div id="ldghslyc">
         <div class="title"></div>
-        <div ref="LDGHSLYCEhartContainer" style="width: 100%; height: 180px"></div>
+        <div ref="LDGHSLYCEhartContainer" style="width: 100%; height: 140px"></div>
       </div>
       <div id="ldggrqsyc">
         <div class="title"></div>
-        <div ref="LDGGRYCEhartContainer" style="width: 100%; height: 180px"></div>
+        <div ref="LDGGRYCEhartContainer" style="width: 100%; height: 140px"></div>
       </div>
       <div id="mqhsjhxx">
         <div class="title"></div>
@@ -248,6 +254,31 @@
           <div class="item right-label"></div>
         </div>
       </div>
+      <div class="schedule-suggest">
+        <div class="result-title">
+          <span>推理结论</span><el-button @click="openSuggest" size="small" class="result-button" :icon="ArrowRight">查看更多</el-button>
+        </div>
+        <div class="result-content">
+          <div class="content-item" v-for="(item, index) in topSuggests" :key="`dynamics-${index}`">
+            <div class="time">
+              <span>{{formatDate(item.createTime, 'MM-DD HH:mm')}}</span>
+            </div>
+            <el-tooltip
+              effect="dark"
+              :content="item.content"
+              placement="top"
+              :disabled="!isOverflow"
+            >
+              <div class="content" ref="contentRef">
+                {{ item.content }}
+              </div>
+            </el-tooltip>
+          </div>
+        </div>
+        <!-- 推理结论 -->
+        <ScheduleSuggestDialog
+          ref="scheduleSuggestRef" />
+      </div>
     </div>
   </div>
 </template>
@@ -262,13 +293,16 @@
 import MessageLoading from '../components/message/MessageLoading.vue'
 import ConversationList from "../components/conversation/ConversationList.vue";
 import HistoryMessageDialog from "../components/message/HistoryMessageDialog.vue"
+import ScheduleSuggestDialog from "../components/suggest/ScheduleSuggestDialog.vue"
 import * as echarts from "echarts";
 import {formatToDateTime} from "@/utils/dateUtil";
+import { formatReasoningContent } from '@/views/ai/utils/utils'
 import {refreshToken} from "@/api/login";
 import {round} from "lodash-es";
-import {ArrowUpBold} from "@element-plus/icons-vue";
+import {ArrowRight, ArrowUpBold} from "@element-plus/icons-vue";
 import * as authUtil from "@/utils/auth";
-import HistoryMessageList from "@/views/ai/dashboard/components/message/HistoryMessageList.vue";
+import {ScheduleSuggestApi, ScheduleSuggestVO} from "@/api/ai/schedulesuggest";
+import {formatDate} from "@/utils/formatTime";
 
 const mqhsList = ref([
   {
@@ -278,7 +312,7 @@
   },
   {
     name: '转炉煤气 O 含量',
-    value: 618,
+    value: 10,
     unit: '%'
   },
   {
@@ -306,33 +340,60 @@
 const tsxxList = ref([
   {
     name: '各高炉出铁水信号',
-    value: '进行',
-    unit: ''
+    type: 1,
+    lists: [
+      {
+        no: '1#',
+        value: '不进行',
+      },
+      {
+        no: '2#',
+        value: '不进行',
+      }
+    ]
   },
   {
     name: '各高炉出铁量',
-    value: 5000,
-    unit: '吨'
+    type: 1,
+    lists: [
+      {
+        no: '1#',
+        value: '500t',
+      },
+      {
+        no: '2#',
+        value: '600t',
+      }
+    ]
   },
   {
     name: '各高炉铁水装入鱼雷罐车信号',
-    value: '进行',
-    unit: 'm³/h'
+    type: 1,
+    lists: [
+      {
+        no: '1#',
+        value: '不进行',
+      },
+      {
+        no: '2#',
+        value: '不进行',
+      }
+    ]
   },
   {
     name: '鱼雷罐车等待信号',
-    value: '进行',
-    unit: 'm³/h'
+    type: 2,
+    value: '进行'
   },
   {
     name: '铁水倒入铁水包信号',
-    value: '不进行',
-    unit: 'm³/h'
+    type: 2,
+    value: '不进行'
   },
   {
     name: '铁产量计划',
-    value: 6000,
-    unit: '吨'
+    type: 3,
+    value: '6000t',
   },
 ])
 
@@ -440,36 +501,24 @@
 
 const mqhsjhxxList = ref([
   {
-    name: '转炉总炉数\n' +
-      '日计划',
-    value: 567,
+    name: '转炉总炉数日计划',
+    value: 123,
     unit: '炉'
   },
   {
-    name: '转炉入炉铁水量\n' +
-      '日计划',
-    value: 200,
-    unit: '吨'
-  },
-  {
     name: '转炉检修计划',
-    value: '未进行',
+    value: '0',
     unit: ''
   },
   {
     name: '钢产量日计划',
-    value: 300,
-    unit: '吨'
-  },
-  {
-    name: '转炉加入废钢总量',
-    value: 500,
-    unit: '吨'
+    value: 20000,
+    unit: 't'
   },
   {
     name: '转炉实绩钢产量',
-    value: 100,
-    unit: '吨'
+    value: 20929,
+    unit: 't'
   }
 ])
 
@@ -495,20 +544,20 @@
   {
     id: 1,
     name: '1#转炉',
-    current: 20,
-    total: 30
+    current: 4,
+    total: 29
   },
   {
     id: 2,
     name: '2#转炉',
-    current: 25,
-    total: 100
+    current: 5,
+    total: 42
   },
   {
     id: 3,
     name: '3#转炉',
-    current: 4,
-    total: 29
+    current: 6,
+    total: 42
   }
 ])
 
@@ -536,6 +585,11 @@
   }
 
 ])
+
+const topSuggests = ref<ScheduleSuggestVO[]>([])
+
+const contentRef = ref([]);
+const isOverflow = ref([]);
 
 const ddtlResult = ref('')
 
@@ -725,10 +779,14 @@
     message.thinking = match[2];
     message.conclusion = match[4]
   }
+  message.thinking = formatReasoningContent(message.thinking)
   return message
 }
-
-
+/** 调度建议 */
+const scheduleSuggestRef = ref()
+const openSuggest = async () => {
+  scheduleSuggestRef.value.open()
+}
 /**
  * 消息列表
  *
@@ -736,8 +794,22 @@
  */
 const messageList = computed(() => {
   if (activeMessageList.value.length > 0) {
-    activeMessageList.value[1].thinking = dealResultAndData(activeMessageList.value[1].content)
-    return activeMessageList.value
+    // 对AI返回的消息进行格式化处理
+    const formattedList = activeMessageList.value.map(msg => {
+      if (msg.type === 'assistant') {
+        // 复制消息对象以避免修改原始数据
+        const formattedMsg = {...msg};
+        // 处理推理思路内容
+        formattedMsg.content = formatReasoningContent(msg.content);
+        return formattedMsg;
+      }
+      return msg;
+    });
+
+    // 处理调度推理结论及数据
+    formattedList[1].thinking = dealResultAndData(formattedList[1].content);
+
+    return formattedList;
   }
   // 没有消息时,如果有 systemMessage 则展示它
   if (activeConversation.value?.systemMessage) {
@@ -773,6 +845,14 @@
   }
   initLDGGRQSYCChart()
   return content
+}
+
+const getScheduleResult = (content: string) => {
+  const spliceText = content.includes("总结:") ? "总结:" : "结论:";
+  const regex = new RegExp(`^([\\s\\S]*?)${spliceText}([\\s\\S]*)$`);
+  const match = content.match(regex);
+  const result = match ? match[2].trim() : '';
+  return result
 }
 
 const extractRecoveryDetails = (text, consume, gui, totalMinutes = 60) => {
@@ -945,6 +1025,10 @@
     conversationId: activeConversationId.value,
     content: content
   } as ChatMessageVO)
+  // 保存调度建议
+  setTimeout(async () => {
+    await createSuggest()
+  }, 1000)
 }
 
 /** 真正执行【发送】消息操作 */
@@ -1108,6 +1192,31 @@
   } catch {}
 }
 
+const suggestData = ref({
+  id: undefined,
+  modelId: undefined,
+  conversationId: undefined,
+  messageId: undefined,
+  content: undefined,
+  status: undefined,
+})
+
+const createSuggest = async () => {
+  const suggestParam = suggestData.value as unknown as ScheduleSuggestVO
+  let assistantMessage = activeMessageList.value[1]
+  suggestParam.content = getScheduleResult(assistantMessage.content)
+  if(suggestParam.content != '') {
+    suggestParam.modelId = activeConversation.value.modelId
+    suggestParam.conversationId = activeConversation.value.id
+    suggestParam.messageId = assistantMessage.id
+    suggestParam.createTime = assistantMessage.createTime
+    suggestParam.status = 0
+    await ScheduleSuggestApi.createScheduleSuggest(suggestParam)
+    // 刷新首页推理结果列表
+    await getTopSuggest()
+  }
+}
+
 const LDGHSLYCEhartContainer = ref();
 
   // 生成未来60秒的时间标签(LDG回收量预测)
@@ -1148,7 +1257,7 @@
   const max = LDGMaxTotalValue()
   const schedule = modelData.value.schedule[type]
   // 返回对象格式数据,包含原始值和基准值
-  const baseline = round(max, 0) + (6 - 2 * type)
+  const baseline = round(max, 0) + (4.5 - 2 * type)
   return schedule.map(item => ({
     value: item + baseline, // 显示值 = 原始值 + 基准值
     original: item        // 原始值
@@ -1232,15 +1341,15 @@
     },
     grid: {
       left: 25,
-      right: 25,
+      right: 5,
       bottom: 10,
       top: 30,
       containLabel: true
     },
     legend: {
-      top: 10,
+      top: 5,
       right: 10,
-      data: ['1#转炉', '2#转炉', '3#转炉', '总回收量'],
+      data: ['1#转炉', '2#转炉', '3#转炉'],
       textStyle: {
         color: '#8FD6FE'
       },
@@ -1293,6 +1402,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#FF7686' // 粉色
         }
       },
@@ -1306,6 +1416,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#49FFD3' // 绿色
         }
       },
@@ -1320,6 +1431,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#FFAE81' // 橙色
         },
       },
@@ -1333,6 +1445,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: 'white'
         },
       }
@@ -1383,7 +1496,7 @@
       left: 0,
       right: 0,
       bottom: 10,
-      top: 20,
+      top: 10,
       containLabel: true
     },
     tooltip: {
@@ -1434,7 +1547,10 @@
         data: realData,
         smooth: true,
         symbol: 'none',
-        lineStyle: { color: '#95E6FF' },
+        lineStyle: {
+          color: '#95E6FF',
+          width: 1
+        },
         markLine: {
           symbol: ['none', 'none'],
           label: {
@@ -1459,6 +1575,7 @@
         lineStyle: {
           type: 'dashed',
           color: '#E76666',
+          width: 1,
           dashOffset: 5
         }
       },
@@ -1591,6 +1708,11 @@
   // 初始状态检测
   updateFullscreenStatus();
 }
+/** 查询列表 */
+const getTopSuggest = async () => {
+  const data = await ScheduleSuggestApi.getTopScheduleSuggests(5)
+  topSuggests.value = data
+}
 
 /** 初始化 **/
 onMounted(async () => {
@@ -1605,11 +1727,11 @@
   // 获取列表数据
   activeMessageListLoading.value = true
   await getMessageList()
+  await getTopSuggest()
 })
 
 // 清理监听
 onUnmounted(() => {
-  console.log('stopStream')
   const events = ['fullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange'];
   events.forEach(event => {
     document.removeEventListener(event, handleFullscreenChange);
@@ -1682,6 +1804,35 @@
           font-weight: 400;
           font-size: 12px;
           color: #C7E7FF;
+        }
+      }
+      .name {
+        height: 16px;
+        font-weight: 400;
+        font-size: 12px;
+        color: #C7E7FF;
+      }
+    }
+    .content1 {
+      margin-left: 16px;
+      .value {
+        span:nth-child(1) {
+          height: 16px;
+          font-weight: 400;
+          font-size: 12px;
+          color: #C7E7FF;
+        }
+        span:nth-child(2){
+          padding-left: 3px;
+          height: 19px;
+          font-weight: bold;
+          font-size: 14px;
+          color: #FFAE81;
+          line-height: 19px;
+        }
+        .item {
+          display: inline-block;
+          width: 45%;
         }
       }
       .name {
@@ -2229,10 +2380,10 @@
         }
       }
       .data2-item {
-        height: 2.8rem;
-        width: 45%;
+        height: 1.4rem;
+        width: 46%;
         display: inline-block;
-        margin: 6px 8px;
+        margin: 8px 8px;
         background: url("@/assets/ai/zhuanlu/data_bg3.png") no-repeat;
       }
       .content {
@@ -2241,15 +2392,13 @@
         margin-left: 10px;
 
         .name {
-          width: 95px;
-          height: 18px;
+          width: 130px;
           font-weight: 400;
           font-size: 14px;
           color: #C7E7FF;
         }
 
         .value {
-          margin-top: 10px;
           margin-left: auto;
           margin-right: 5px;
           span:nth-child(1) {
@@ -2279,13 +2428,13 @@
       .little-title {
         font-size: 14px;
         color: #8FD6FE;
-        margin: 10px;
+        margin: 5px 10px 5px 10px;
       }
       .data3-item {
         height: 5.2rem;
         width: 30%;
         display: inline-block;
-        margin: 0 6px 6px 6px;
+        margin: 0 6px 0 6px;
         background: url("@/assets/ai/zhuanlu/data_bg4.png") center/cover no-repeat;
         .name {
           font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
@@ -2355,6 +2504,67 @@
         }
       }
     }
+    .schedule-suggest {
+      .result-title {
+        margin-top: 10px;
+        background: url("@/assets/ai/zhuanlu/ddtljl_result_title.png") no-repeat;
+        height: 1.8rem;
+        font-weight: 400;
+        font-size: 14px;
+        color: #8FD6FE;
+        text-align: left;
+        font-style: normal;
+        text-transform: none;
+        span {
+          margin-left: 30px;
+        }
+        .result-button {
+          color: rgba(143, 214, 254);
+          font-weight: bold;
+          float: right;
+          margin-right: 5px;
+          background-color: rgba(0, 255, 255, 0.1);
+          border-radius: 3px;
+          padding: 0 5px;
+          border: none;
+          cursor: pointer
+        }
+        .history-button:hover {
+          color: rgba(143, 214, 254, 0.5);
+        }
+      }
+      .result-content {
+        margin-top: 5px;
+        display: inline-block;
+        font-weight: 400;
+        font-size: 14px;
+        color: rgba(130,202,255,0.89);
+        text-align: left;
+        font-style: normal;
+        text-transform: none;
+        .content-item {
+          height: 28px;
+          background: rgba(69,133,255,0.2);
+          border-radius: 2px 2px 2px 2px;
+          padding-left: 3px;
+          margin: 3px 0;
+          display: flex;
+          align-items: center;
+          overflow: hidden;
+        }
+        .time {
+          flex-shrink: 0;
+          margin-right: 12px;
+        }
+        .content {
+          width: 350px;
+          flex: 1;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+    }
   }
 }
 

--
Gitblit v1.9.3