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 |  392 +++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 315 insertions(+), 77 deletions(-)

diff --git a/src/views/ai/dashboard/zhuanlu/index.vue b/src/views/ai/dashboard/zhuanlu/index.vue
index 0d0be46..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}}
@@ -62,7 +68,7 @@
 
     <div class="gas-scheduling-center">
       <div class="mode-switch">
-        <el-radio-group v-model="tabPosition" class="custom-radio-group">
+        <el-radio-group v-model="tabPosition" @change="handleChange" class="custom-radio-group">
           <el-radio-button label="model">大模型模式</el-radio-button>
           <el-radio-button label="conversation">对话模式</el-radio-button>
         </el-radio-group>
@@ -164,23 +170,26 @@
         <!-- 历史建议 -->
         <HistoryMessageDialog
           ref="historyMessageRef"
-          :conversation="activeConversation"
+          :parentMethod="queryHistoryMessage"
+          @gotoManualMethod="gotoManual"
         />
       </div>
 
       <div v-else>
-        <NormalConversation />
+        <NormalConversation
+          :data="defaultMessage"
+        />
       </div>
     </div>
 
     <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>
@@ -245,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>
@@ -259,12 +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 {ScheduleSuggestApi, ScheduleSuggestVO} from "@/api/ai/schedulesuggest";
+import {formatDate} from "@/utils/formatTime";
 
 const mqhsList = ref([
   {
@@ -274,7 +312,7 @@
   },
   {
     name: '转炉煤气 O 含量',
-    value: 618,
+    value: 10,
     unit: '%'
   },
   {
@@ -302,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',
   },
 ])
 
@@ -436,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'
   }
 ])
 
@@ -491,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
   }
 ])
 
@@ -533,6 +586,11 @@
 
 ])
 
+const topSuggests = ref<ScheduleSuggestVO[]>([])
+
+const contentRef = ref([]);
+const isOverflow = ref([]);
+
 const ddtlResult = ref('')
 
 const route = useRoute() // 路由
@@ -548,6 +606,7 @@
 const messageRef = ref()
 const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表
 const activeHistoryMessageList = ref<ChatMessageVO[]>([]) // 历史建议列表
+const activeHistoryMessageTotal = ref(0) // 历史建议总数
 const activeMessageListLoading = ref<boolean>(false) // activeMessageList 是否正在加载中
 const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 定时器。如果加载速度很快,就不进入加载中
 // 消息滚动
@@ -573,8 +632,30 @@
 const historyMessageRef = ref()
 const openHistoryMessage = async () => {
   // 刷新 message 列表
-  await getHistoryMessageList()
-  historyMessageRef.value.open(activeHistoryMessageList.value, activeConversation.value)
+  let resDate = await historyMessageRef.value.dealDate()
+  await getHistoryMessageList(resDate)
+  historyMessageRef.value.open(activeHistoryMessageList.value, activeConversation.value, activeHistoryMessageTotal.value)
+}
+
+const queryHistoryMessage = async (queryParams: ChatMessageVO) => {
+  return await getHistoryMessageList(queryParams)
+}
+
+//切换对话模式判断
+const handleChange = async () => {
+  // 对话进行中,不允许切换
+  if (conversationInProgress.value) {
+    message.alert('对话中,不允许切换!')
+    return false
+  }
+}
+
+// 默认选中消息
+const defaultMessage = ref<ChatMessageVO>()
+
+const gotoManual = async (item: ChatMessageVO) => {
+  defaultMessage.value = item
+  tabPosition.value = 'conversation'
 }
 
 // =========== 【聊天对话】相关 ===========
@@ -671,22 +752,23 @@
 }
 
 /** 获取消息 message 列表 */
-const getHistoryMessageList = async () => {
+const getHistoryMessageList = async (params: any) => {
   if (activeConversationId.value === null) {
     return
   }
+  params.conversationId = activeConversationId.value
   // 获取消息列表
-  activeHistoryMessageList.value = await ChatMessageApi.getChatMessageListByConversationId(
-    activeConversationId.value
-  )
-  if (activeHistoryMessageList.value.length > 0) {
+  let pageResult = await ChatMessageApi.getChatMessagePageListByConversationId(params)
+  activeHistoryMessageList.value = pageResult.list
+  activeHistoryMessageTotal.value = pageResult.total
+  if (activeHistoryMessageList.value != null && activeHistoryMessageList.value.length > 0) {
     activeHistoryMessageList.value.forEach((message: ChatMessageVO) => {
       if(message.type != 'user') {
         dealResult(message)
       }
     })
-    return activeHistoryMessageList.value
   }
+  return pageResult
 }
 //处理调度推理结论
 const dealResult = (message: any) => {
@@ -697,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()
+}
 /**
  * 消息列表
  *
@@ -708,9 +794,22 @@
  */
 const messageList = computed(() => {
   if (activeMessageList.value.length > 0) {
-    activeMessageList.value[1].thinking = dealResultAndData(activeMessageList.value[1].content)
-    console.log(activeMessageList.value)
-    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) {
@@ -746,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) => {
@@ -918,6 +1025,10 @@
     conversationId: activeConversationId.value,
     content: content
   } as ChatMessageVO)
+  // 保存调度建议
+  setTimeout(async () => {
+    await createSuggest()
+  }, 1000)
 }
 
 /** 真正执行【发送】消息操作 */
@@ -1081,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回收量预测)
@@ -1121,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        // 原始值
@@ -1143,11 +1279,11 @@
   let returnValue = 0;
   if(type == 'max') {
     returnValue = computed(() => {
-      return Math.max(...tank) + 20
+      return Number((Math.max(...tank) + 20).toFixed(0))
     })
   } else if(type == 'min') {
     returnValue = computed(() => {
-      return Math.min(...tank) - 60
+      return Number((Math.min(...tank) - 60).toFixed(0))
     })
   } else if(type == 'average') {
     returnValue = computed(() => {
@@ -1155,7 +1291,7 @@
       tank.forEach((item) => {
         sum += item[0]
       })
-      return (sum / tank.length).toFixed(0);
+      return Number((sum / tank.length).toFixed(0));
     })
   }
   return returnValue.value
@@ -1205,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'
       },
@@ -1266,6 +1402,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#FF7686' // 粉色
         }
       },
@@ -1279,6 +1416,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#49FFD3' // 绿色
         }
       },
@@ -1293,6 +1431,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: '#FFAE81' // 橙色
         },
       },
@@ -1306,6 +1445,7 @@
           focus: 'series'
         },
         lineStyle: {
+          width: 1,
           color: 'white'
         },
       }
@@ -1356,7 +1496,7 @@
       left: 0,
       right: 0,
       bottom: 10,
-      top: 20,
+      top: 10,
       containLabel: true
     },
     tooltip: {
@@ -1407,7 +1547,10 @@
         data: realData,
         smooth: true,
         symbol: 'none',
-        lineStyle: { color: '#95E6FF' },
+        lineStyle: {
+          color: '#95E6FF',
+          width: 1
+        },
         markLine: {
           symbol: ['none', 'none'],
           label: {
@@ -1432,6 +1575,7 @@
         lineStyle: {
           type: 'dashed',
           color: '#E76666',
+          width: 1,
           dashOffset: 5
         }
       },
@@ -1564,6 +1708,11 @@
   // 初始状态检测
   updateFullscreenStatus();
 }
+/** 查询列表 */
+const getTopSuggest = async () => {
+  const data = await ScheduleSuggestApi.getTopScheduleSuggests(5)
+  topSuggests.value = data
+}
 
 /** 初始化 **/
 onMounted(async () => {
@@ -1578,6 +1727,7 @@
   // 获取列表数据
   activeMessageListLoading.value = true
   await getMessageList()
+  await getTopSuggest()
 })
 
 // 清理监听
@@ -1654,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 {
@@ -2201,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 {
@@ -2213,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) {
@@ -2251,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;
@@ -2327,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