Browse Source

feat: 更新menu菜单以及markdown支持a链接跳转

sunxiao 3 weeks ago
parent
commit
c0e1ca6195

+ 12 - 0
package-lock.json

@@ -20,6 +20,7 @@
         "load-awesome": "^1.1.0",
         "markdown-it": "^14.1.0",
         "markdown-it-anchor": "^9.2.0",
+        "markdown-it-attrs": "^4.3.1",
         "markdown-it-link-attributes": "^4.0.1",
         "markdown-it-math": "^4.1.1",
         "markdown-it-sub": "^2.0.0",
@@ -4980,6 +4981,17 @@
         "markdown-it": "*"
       }
     },
+    "node_modules/markdown-it-attrs": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/markdown-it-attrs/-/markdown-it-attrs-4.3.1.tgz",
+      "integrity": "sha512-/ko6cba+H6gdZ0DOw7BbNMZtfuJTRp9g/IrGIuz8lYc/EfnmWRpaR3CFPnNbVz0LDvF8Gf1hFGPqrQqq7De0rg==",
+      "engines": {
+        "node": ">=6"
+      },
+      "peerDependencies": {
+        "markdown-it": ">= 9.0.0"
+      }
+    },
     "node_modules/markdown-it-link-attributes": {
       "version": "4.0.1",
       "resolved": "https://registry.npmmirror.com/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz",

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "load-awesome": "^1.1.0",
     "markdown-it": "^14.1.0",
     "markdown-it-anchor": "^9.2.0",
+    "markdown-it-attrs": "^4.3.1",
     "markdown-it-link-attributes": "^4.0.1",
     "markdown-it-math": "^4.1.1",
     "markdown-it-sub": "^2.0.0",

BIN
src/assets/images/carbon/bg-header.png


BIN
src/assets/images/home/menu_4.png


BIN
src/assets/images/home/menu_4_hover.png


+ 12 - 3
src/assets/styles/common.scss

@@ -118,11 +118,20 @@
     justify-content: space-between;
 
     .message-inner {
-      width: 194px;
+      width: 212px;
       flex-shrink: 0;
 
-      span {
-        white-space: nowrap;
+      .message-item {
+        display: flex;
+        span:nth-child(1) {
+          flex-shrink: 0;
+        }
+        span:nth-child(2) {
+          text-overflow: ellipsis;
+          overflow: hidden;
+          word-break: break-all;
+          white-space: nowrap;
+        }
       }
     }
 

+ 39 - 0
src/assets/svgs/menu/smart-order.svg

@@ -0,0 +1,39 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="15" cy="15" r="15" fill="url(#paint0_linear_5499_1207)"/>
+<path d="M13.8327 7.54259L9.10856 8.80842C7.05656 9.35825 6.16087 10.9087 6.71043 12.9597L7.97411 17.6759C8.52518 19.7325 10.0761 20.6274 12.1281 20.0776L16.8466 18.8132C18.8986 18.2634 19.7943 16.7129 19.2447 14.6619L17.9795 9.94017C17.4356 7.88768 15.8847 6.99276 13.8327 7.54259Z" fill="url(#paint1_linear_5499_1207)"/>
+<foreignObject x="6.66496" y="6.66667" width="19.9999" height="20.0002"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(1.67px);clip-path:url(#bgblur_0_5499_1207_clip_path);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="3.33333" d="M23.1233 13.8728V13.8733V19.46C23.1233 20.6319 22.7746 21.5422 22.1571 22.1593C21.5397 22.7765 20.6289 23.125 19.4563 23.125H13.8735C12.701 23.125 11.7903 22.7765 11.1728 22.1586C10.5554 21.5407 10.2066 20.6287 10.2066 19.4533V13.8733C10.2066 12.7015 10.5553 11.7912 11.1727 11.174C11.7902 10.5569 12.701 10.2083 13.8735 10.2083H19.463C20.6356 10.2083 21.5463 10.5569 22.1629 11.1739C22.7794 11.7909 23.1265 12.701 23.1233 13.8728Z" fill="url(#paint2_linear_5499_1207)" fill-opacity="0.4" stroke="url(#paint3_linear_5499_1207)" stroke-width="0.416667"/>
+<g filter="url(#filter1_d_5499_1207)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6997 21.4006C17.3745 21.4006 18.5283 20.5206 18.5283 17.0006C18.5283 13.4806 17.3745 12.6006 16.6997 12.6006C16.0249 12.6006 14.8711 13.5399 14.8711 17.0006C14.8711 20.4613 16.0249 21.4006 16.6997 21.4006Z" stroke="white" stroke-width="0.653714" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.7405 19.3052C13.0779 19.8945 14.4466 20.441 17.6138 18.5972C20.781 16.7534 20.9959 15.2849 20.6585 14.6957C20.3211 14.1064 18.899 13.5909 15.7852 15.4037C12.6714 17.2164 12.4031 18.716 12.7405 19.3052Z" stroke="white" stroke-width="0.653714" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.741 14.6957C12.4037 15.2849 12.6186 16.7534 15.7857 18.5972C18.9529 20.441 20.3216 19.8945 20.659 19.3052C20.9964 18.716 20.7281 17.2164 17.6143 15.4037C14.5005 13.5909 13.0784 14.1064 12.741 14.6957Z" stroke="white" stroke-width="0.653714" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+<defs>
+<clipPath id="bgblur_0_5499_1207_clip_path" transform="translate(-6.66496 -6.66667)"><path d="M23.1233 13.8728V13.8733V19.46C23.1233 20.6319 22.7746 21.5422 22.1571 22.1593C21.5397 22.7765 20.6289 23.125 19.4563 23.125H13.8735C12.701 23.125 11.7903 22.7765 11.1728 22.1586C10.5554 21.5407 10.2066 20.6287 10.2066 19.4533V13.8733C10.2066 12.7015 10.5553 11.7912 11.1727 11.174C11.7902 10.5569 12.701 10.2083 13.8735 10.2083H19.463C20.6356 10.2083 21.5463 10.5569 22.1629 11.1739C22.7794 11.7909 23.1265 12.701 23.1233 13.8728Z"/>
+</clipPath><filter id="filter1_d_5499_1207" x="10.6266" y="11.1039" width="12.1462" height="12.7936" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset dy="0.5"/>
+<feGaussianBlur stdDeviation="0.835"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0.0599304 0 0 0 0 0.318001 0 0 0 0 0.0312559 0 0 0 0.2 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5499_1207"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5499_1207" result="shape"/>
+</filter>
+<linearGradient id="paint0_linear_5499_1207" x1="23" y1="6" x2="6" y2="25" gradientUnits="userSpaceOnUse">
+<stop stop-color="#78DC57"/>
+<stop offset="1" stop-color="#96CE17"/>
+</linearGradient>
+<linearGradient id="paint1_linear_5499_1207" x1="6.87283" y1="10.7754" x2="21.2995" y2="21.0464" gradientUnits="userSpaceOnUse">
+<stop stop-color="white"/>
+<stop offset="1" stop-color="white" stop-opacity="0.5"/>
+</linearGradient>
+<linearGradient id="paint2_linear_5499_1207" x1="16.665" y1="23.3333" x2="16.665" y2="10.06" gradientUnits="userSpaceOnUse">
+<stop stop-color="white"/>
+<stop offset="1" stop-color="#5E9F33" stop-opacity="0.7"/>
+</linearGradient>
+<linearGradient id="paint3_linear_5499_1207" x1="11.4858" y1="11.8523" x2="22.4382" y2="22.8047" gradientUnits="userSpaceOnUse">
+<stop stop-color="white" stop-opacity="0.7"/>
+<stop offset="1" stop-color="white"/>
+</linearGradient>
+</defs>
+</svg>

+ 2 - 0
src/components/Chat/ChatText.vue

@@ -11,6 +11,7 @@ import markdownItSub from 'markdown-it-sub';
 import markdownItSup from 'markdown-it-sup';
 import anchor from 'markdown-it-anchor';
 import toc from 'markdown-it-toc-done-right';
+import mdAttrs from 'markdown-it-attrs';
 
 const updateCatalog = inject('updateCatalog', null);
 const thinkStr = ref('');
@@ -83,6 +84,7 @@ mdi.renderer.rules.table_open = function (tokens, idx, options, env, self) { ret
 mdi.renderer.rules.table_close = function () { return '</table></div>'; };
 mdi.use(markdownItSub);
 mdi.use(markdownItSup);
+mdi.use(mdAttrs);
 
 mdi.use(anchor, {
   level: [1, 2, 3, 4, 5, 6],

+ 7 - 3
src/components/Layout/TheChatView.vue

@@ -22,6 +22,10 @@ defineProps({
   catalogData: {
     type: Array,
     default: () => []
+  },
+  isHeader: {
+    type: Boolean,
+    default: true
   }
 })
 
@@ -48,14 +52,14 @@ defineExpose({ targetScrollDom });
     <div class="catalog-wrapper" v-if="catalogData.length">
       <slot name="catalog"></slot>
     </div>
-    <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-wrapper w-full h-full flex flex-col rounded-[10px]" :style="{ padding: isHeader ? '0px' : '20px 0px 20px 0px' }">
+      <div class="chat-header flex items-center justify-between py-[24px] px-[18px] " v-if="isHeader">
         <div class="left_inner" @click="handleClickBack">
           <span v-if="isBackBtn" class="back-btn"></span>
           <span v-if="leftTitle" class="left_title">{{ leftTitle }}</span>
         </div>
         <div class="right_inner flex items-center space-x-[16px]">
-          <UserTop></UserTop>
+          <UserTop v-if="isRight"></UserTop>
         </div>
       </div>
       <main class="chat-main flex flex-1 flex-col justify-between" v-if="isChatSlot">

+ 13 - 15
src/components/Layout/TheMenu.vue

@@ -33,8 +33,6 @@ function renderLabel(val, url) {
   return url ? (<a href={url} target="_blank" class="pl-1">{val}</a>) : (<span class="">{val}</span>);
 }
 
-
-
 const updateMenOptions = () => {
   if (userStore.isCheckUser) {
     menuOptions = [
@@ -44,7 +42,7 @@ const updateMenOptions = () => {
         key: '/answer'
       },
       {
-        label: () => renderLabel('智慧办公'),
+        label: () => renderLabel('辅助办公'),
         icon: renderIcon({ name: 'menu-work' }),
         key: '/work'
       },
@@ -67,7 +65,7 @@ const updateMenOptions = () => {
         key: '/answer'
       },
       {
-        label: () => renderLabel('工艺管控'),
+        label: () => renderLabel('工艺应急决策'),
         icon: renderIcon({ name: 'menu-analyse' }),
         key: '/analyse',
         children: [
@@ -82,15 +80,10 @@ const updateMenOptions = () => {
             key: '/pymol-warn',
           },
           {
-            label: '预测预警',
+            label: '水质预测',
             icon: renderChildrenIcon({ name: 'menu-analyse-notice' }),
             key: '/forecast-warn',
           },
-          {
-            label: '智能工单',
-            icon: renderChildrenIcon({ name: 'menu-analyse-order' }),
-            key: '/work-order',
-          }
         ]
       },
       {
@@ -99,9 +92,9 @@ const updateMenOptions = () => {
         key: '/control',
         children: [
           {
-            label: '智适应碳源投加',
+            label: '智适应投药',
             icon: renderChildrenIcon({ name: 'menu-cost-drug' }),
-            key: 'medicinal',
+            key: '/medicinal',
           },
           // {
           //   label: '精准曝气',
@@ -114,19 +107,24 @@ const updateMenOptions = () => {
           //   key: 'normal-3',
           // },
           {
-            label: '碳排放管理',
+            label: '碳汇核算',
             icon: renderChildrenIcon({ name: 'menu-carbon-emission' }),
             key: '/carbon',
           },
         ]
       },
       {
-        label: () => renderLabel('智慧办公'),
+        label: '智能工单',
+        icon: renderIcon({ name: 'menu-smart-order' }),
+        key: '/work-order',
+      },
+      {
+        label: () => renderLabel('辅助办公'),
         icon: renderIcon({ name: 'menu-work' }),
         key: '/work'
       },
       {
-        label: () => renderLabel('数据分析'),
+        label: () => renderLabel('数据统计分析'),
         icon: renderIcon({ name: 'menu-data' }),
         key: '/data',
         children: [

+ 13 - 51
src/router/index.js

@@ -1,14 +1,6 @@
 import { createRouter, createWebHistory } from 'vue-router'
 
 const constantRouterMap = [
-  // {
-  //   path: '/',
-  //   name: 'Scrren',
-  //   component: () => import('@/views/screen/ScreenView.vue'),
-  //   meta: {
-  //     title: "智慧总控"
-  //   }
-  // },
   {
     path: '/',
     name: 'Scrren',
@@ -30,47 +22,17 @@ const constantRouterMap = [
     name: 'carbon',
     component: () => import('@/views/carbon/index.vue'),
     meta: {
-      title: "碳排放"
+      title: "碳汇核算"
+    }
+  },
+  {
+    path: '/water-warn-content',
+    name: 'WaterWarnContent',
+    component: () => import('@/views/single/WaterWarnContent.vue'),
+    meta: {
+      title: "报警详情"
     }
   },
-  // {
-  //   path: '/test',
-  //   name: 'TempTest',
-  //   component: () => import('@/views/screen/ScreenView2.vue'),
-  //   meta: {
-  //     title: "测试建模文件"
-  //   }
-  // },
-
-  /** 
-   * 模版筛选使用
-   * 注释时间: 2024年08月05日15:03:02
-   * 
-   * */ 
-  // {
-  //   path: '/count1',
-  //   name: 'count1',
-  //   component: () => import('@/views/count/index1.vue'),
-  //   meta: {
-  //     title: "临时统计1"
-  //   }
-  // },
-  // {
-  //   path: '/count2',
-  //   name: 'count2',
-  //   component: () => import('@/views/count/index2.vue'),
-  //   meta: {
-  //     title: "临时统计2"
-  //   }
-  // },
-  // {
-  //   path: '/count3',
-  //   name: 'count3',
-  //   component: () => import('@/views/count/index3.vue'),
-  //   meta: {
-  //     title: "临时统计3"
-  //   }
-  // },
   {
     path: '/env',
     name: 'Env',
@@ -121,7 +83,7 @@ const constantRouterMap = [
         name: 'ForecastView',
         component: () => import('@/views/analyse/ForecastView.vue'),
         meta: {
-          title: '预测预警'
+          title: '水质报警'
         }
       },
       {
@@ -129,7 +91,7 @@ const constantRouterMap = [
         name: 'WorKOrder',
         component: () => import('@/views/analyse/WorkOrder.vue'),
         meta: {
-          title: '智慧工单'
+          title: '工艺运行诊断'
         }
       },
       {
@@ -137,7 +99,7 @@ const constantRouterMap = [
         name: 'WorkView',
         component: () => import('@/views/work/WorkView.vue'),
         meta: {
-          title: '智能办公'
+          title: '辅助办公'
         }
       },
       {
@@ -145,7 +107,7 @@ const constantRouterMap = [
         name: 'MedicinalView',
         component: () => import('@/views/control/MedicinalView.vue'),
         meta: {
-          title: '智适应碳源投加'
+          title: '智适应投药'
         }
       },
       {

+ 1 - 0
src/utils/format.js

@@ -11,6 +11,7 @@ export const formatToData = ({ dataSource, warnKey, isNoUnit, statusVal }) => {
     if ( Number.isFinite(value) ) value = Number(value.toFixed(2));
     if ( key.includes("值") && !isNoUnit) value = value? value + 'mg/L' : '';
     if ( key === '状态' ) value =  statusVal;
+    if ( key === '持续时间' ) value = value + "小时"
     return { label: key, value, isWarning: warnKey === key };
   });
   return reuslt;

+ 3 - 3
src/views/analyse/ForecastView.vue

@@ -95,7 +95,7 @@ onMounted(() => {
 
 <template>
   <section class="flex items-start h-full" id="warning">
-    <TheSubMenu title="预测预警" @scrollToLower="onScrolltolower" :loading="isFetching" ref="subMenuRef">
+    <TheSubMenu title="水质预测" @scrollToLower="onScrolltolower" :loading="isFetching" ref="subMenuRef">
       <template #top>
         <div class="border-[#DAE5ED]">
           <n-tabs type="line" justify-content="space-evenly">
@@ -113,8 +113,8 @@ onMounted(() => {
     </TheSubMenu>
 
     <TheChatView ref="scrollRef" :is-footer="false">
-      <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:" :sub-title="[
-      '报警分析功能具备实时监测与预警机制,检测到异常情况推送相关工作人员确保问题及时处理',
+      <ChatWelcome title="您好,我是LibraAI工艺应急决策" card-title="常见处理方案:" :sub-title="[
+      '水质预测功能具备实时监测与预警机制,检测到异常情况推送相关工作人员确保问题及时处理',
       '报警时间为每小时警报,请大家及时处理'
     ]" v-if="!textDataSources" :card-content="recommendList" @on-click="handleWelcomeRecommend" />
       <ChatBaseCard v-show="textDataSources">

+ 2 - 13
src/views/analyse/PymolView.vue

@@ -53,17 +53,6 @@ const handleOpenContent = async ({ id, reason:title, counts }) => {
     statusVal: !!warningActive.value ? '系统关闭' : basic['状态']
   });
 
-  /**
-   * 临时修改 - 后续需要删除
-   * */ 
-   textDataSources.value = {
-    ...textDataSources.value,
-    list: textDataSources.value.list.map(item => {
-      if (item.label === '持续时间') item.value = counts;
-      return item
-    })
-  }
-
   jsTableData.value = [jsData];
   csTableData.value = [csData];
 
@@ -99,8 +88,8 @@ const handleWelcomeRecommend = question => {
 
     <TheChatView ref="scrollRef" :is-footer="false">
 
-      <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:" :sub-title="[
-        '水质报警功能对工艺过程指标实时监测,发现异常后将推送给相关人员决策方案',
+      <ChatWelcome title="您好,我是LibraAI工艺应急决策" card-title="常见处理方案:" :sub-title="[
+        '生化报警功能对工艺过程指标实时监测,发现异常后将推送给相关人员决策方案',
         '报警时间为每小时警报,请大家及时处理'
       ]"
         v-if="!textDataSources"

+ 8 - 14
src/views/analyse/WaterView.vue

@@ -369,17 +369,6 @@ const handleOpenContent = async (item) => {
     statusVal: !!warningActive.value ? '系统关闭' : basic['状态']
   });
 
-  /**
-   * 临时修改 - 后续需要删除
-   * */
-  textDataSources.value = {
-    ...textDataSources.value,
-    list: textDataSources.value.list.map(item => {
-      if (item.label === '持续时间') item.value = counts;
-      return item
-    })
-  }
-
   jsTableData.value = [jsData];
   csTableData.value = [csData];
 
@@ -570,7 +559,7 @@ const handleWelcomeRecommend = question => {
     </TheSubMenu>
 
     <TheChatView ref="scrollRef" :is-footer="false">
-      <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:" :sub-title="[
+      <ChatWelcome title="您好,我是LibraAI工艺应急决策" card-title="常见处理方案:" :sub-title="[
         '水质报警功能针对五大核心指标实时监测,发现异常后将推送给相关人员决策方案',
         '报警时间为每小时警报,请大家及时处理'
       ]" :card-content="recommendList" @on-click="handleWelcomeRecommend" v-if="!textDataSources" />
@@ -578,8 +567,13 @@ const handleWelcomeRecommend = question => {
         <div class="waring-answer-wrapper">
           <dl class="message-inner warning-info_medium ">
             <dt class="mb-[2px] font-bold text-[#1A2029]">{{ textDataSources?.title }}</dt>
-            <dd v-for="item, index in textDataSources?.list" :key="index"><span
-                :class="{ 'text-[#F44C49]': item.isWarning }">{{ item.label }}: {{ item.value }}</span></dd>
+            <dd v-for="item, index in textDataSources?.list" :key="index">
+              <span :class="['message-item', { 'text-[#F44C49]': item.isWarning }]">
+                  <span>{{ item.label }}</span>
+                  : 
+                  <span class="message-value" :title="item.value">{{ item.value }}</span>
+              </span>
+            </dd>
           </dl>
           <div class="table-inner">
             <div class="warning-table mb-[8px]">

+ 1 - 1
src/views/control/MedicinalView.vue

@@ -317,7 +317,7 @@ onMounted(async () => {
 
 <template>
   <section class="flex items-start h-full">
-    <TheChatView leftTitle="智适应碳源投加" :isChatSlot="false" :isFooter="false">
+    <TheChatView leftTitle="智适应投药" :isChatSlot="false" :isFooter="false">
       <template #control>
         <div class="control-container space-x-[12px]">
           <div class="left-section">

+ 2 - 2
src/views/screen/ScreenView.vue

@@ -122,10 +122,10 @@ onBeforeUnmount(() => {
       </div>
     </div>
     <div class="menu">
-      <RouterLink to="/" class="item item1">智慧总控</RouterLink>
+      <RouterLink to="/screen-light" class="item item1">智慧总控</RouterLink>
       <RouterLink to="/answer" class="item item2">专家问答</RouterLink>
       <RouterLink to="/water-warn" class="item item3">工艺管控</RouterLink>
-      <RouterLink to="/work" class="item item4">智能办公</RouterLink>
+      <RouterLink to="/medicinal" class="item item4">成本管控</RouterLink>
     </div>
     <div class="screen-container">
       <div class="screen-container-main">

+ 2 - 2
src/views/screen/ScreenViewBlack/index.vue

@@ -122,10 +122,10 @@ onUnmounted(() => {
         </ul>
         <ul class="menu-list justify-start">
           <li class="item mr-[20px]">
-            <RouterLink to="/water-warn"><span>智能分析</span></RouterLink>
+            <RouterLink to="/water-warn"><span>工艺管控</span></RouterLink>
           </li>
           <li class="item">
-            <RouterLink to="/work"><span>智能助手</span></RouterLink>
+            <RouterLink to="/medicinal"><span>成本管控</span></RouterLink>
           </li>
         </ul>
       </div>

+ 435 - 0
src/views/single/WaterWarnContent.vue

@@ -0,0 +1,435 @@
+<script setup lang="jsx">
+import { onMounted, ref, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { BaseTable,TheChatView } from "@/components";
+import { useInfinite, useFetchStream, useScroll } from '@/composables';
+import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
+import { CustomModal } from "@/views/analyse/components";
+import { inColumns, outColumns } from '@/views/analyse/config';
+
+import { formatToData } from "@/utils/format";
+
+import { waterApi } from '@/api/water';
+
+const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
+const { refetch, cancelFetch } = useFetchStream("/grpc/decisionStream", { methdos: 'POST' }, false);
+
+const { onRestore } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
+
+const route = useRoute();
+
+// 回答列表
+const answerResult = ref([]);
+// 获取最终回答流数据参数
+const flowParams = {
+  feedback: '',
+  category: '',
+  warningId: '',
+  simulate: '{}'
+};
+
+const answerLoading = ref(false);
+const textDataSources = ref(null);
+const warningActive = ref(0);
+
+// 进出水数据
+const jsTableData = ref([]);
+const csTableData = ref([]);
+
+const visible = ref(false);
+
+const modalData = ref({});
+
+// 菜单水数据类型
+const waterTypeValue = ref('');
+
+watch(() => waterTypeValue.value, curValue => {
+  onRestore({ warningStatus: warningActive.value, waterType: curValue });
+})
+
+const handleModelVisible = () => {
+  visible.value = true;
+}
+
+/**
+ * 报警详情
+*/
+const handleOpenContent = async (item) => {
+  const { id, category, reason: title, counts } = item;
+
+  flowParams.category = category;
+  flowParams.warningId = id;
+  flowParams.feedback = '';
+  flowParams.simulate = '{}';
+
+  answerLoading.value = false;
+
+  answerResult.value = [];
+
+  const { data } = await waterApi.getWaringDetails(id);
+  const showVal = JSON.parse(data.showVal);
+  const { basic, jsData, csData } = showVal;
+
+  try {
+    const answer = JSON.parse(data.answer);
+    const reportList = [];
+    const alertList = [];
+    let simulateObj = null;
+
+    answer.map(item => {
+      const answerObjItem = JSON.parse(item);
+      switch (answerObjItem.biz) {
+        case "DECISION_REPORT":
+          reportList.push(answerObjItem.message);
+          break
+        case "DECISION_ALERT":
+          alertList.push(answerObjItem);
+          break
+        case "DECISION_SIMULATE":
+          if (warningActive.value === 1) return;
+          const { off, on, pred } = JSON.parse(answerObjItem.message);
+          simulateObj = {
+            biz: 'DECISION_SIMULATE',
+            off,
+            on,
+            pred,
+            isDisable: false
+          }
+          modalData.value = simulateObj;
+      }
+    })
+
+    if (reportList.length) {
+      answerResult.value.push({
+        biz: 'DECISION_REPORT',
+        answer: reportList.join(""),
+        loading: false,
+        delayLoading: false
+      })
+    }
+
+    if (alertList.length) {
+      const [parseAnswer] = alertList.map(item => {
+        item.message = Object.keys(item.message).map(key => ({ ...item.message[key], isActive: null }));
+        return item;
+      })
+      answerResult.value.push({
+        biz: 'DECISION_ALERT',
+        loading: false,
+        delayLoading: false,
+        isAllSelect: false,
+        list: parseAnswer?.message
+      })
+    }
+
+    if (simulateObj) {
+      answerResult.value.push(simulateObj);
+    }
+
+  } catch (error) {
+    answerResult.value.push({
+      biz: 'DECISION_REPORT',
+      answer: data.answer,
+      loading: false,
+      delayLoading: false
+    })
+  }
+
+  cancelFetch();
+
+  basic.title = title;
+  textDataSources.value = formatToData({
+    dataSource: basic,
+    warnKey: '报警值',
+    statusVal: !!warningActive.value ? '系统关闭' : basic['状态']
+  });
+
+  jsTableData.value = [jsData];
+  csTableData.value = [csData];
+
+}
+
+// 生成流数据
+const onRegenerate = async () => {
+
+  answerLoading.value = true;
+
+  const len = answerResult.value.length ? answerResult.value.length : 0;
+
+  const tempReport = {
+    biz: 'DECISION_REPORT',
+    answer: '',
+    loading: true,
+    delayLoading: true,
+  };
+
+  let tempSimulate = null;
+
+  answerLoading.value = answerResult.value[len - 1].biz !== 'DECISION_TABLE';
+
+  const feedback = flowParams.feedback
+
+  const params = {
+    body: JSON.stringify({ ...flowParams, feedback: JSON.stringify(feedback) }),
+    errorHandler: () => { },
+    successHandler: data => {
+
+      const item = JSON.parse(data);
+
+      answerLoading.value = false;
+
+      if (item.biz === 'DECISION_REPORT') {
+        tempReport.answer += item.message;
+        tempReport.delayLoading = false;
+        answerResult.value[len] = { ...tempReport };
+      }
+
+      if (item.biz === 'DECISION_ALERT') {
+        const list = Object.keys(item.message).map(key => ({ ...item.message[key], isActive: null }));
+        answerResult.value.push({
+          biz: 'DECISION_ALERT',
+          loading: true,
+          delayLoading: false,
+          isAllSelect: false,
+          list
+        })
+      }
+
+      if (item.biz === 'DECISION_SIMULATE') {
+        const lastAnswerItem = answerResult.value[len - 1];
+        if (lastAnswerItem.biz === 'DECISION_TABLE') {
+          answerResult.value[len - 1] = {
+            ...lastAnswerItem,
+            content: JSON.parse(item.message).pred.join(", ")
+          }
+        } else {
+          const { off, on, pred } = JSON.parse(item.message);
+          tempSimulate = {
+            biz: 'DECISION_SIMULATE',
+            off,
+            on,
+            pred,
+            isDisable: false
+          }
+          modalData.value = tempSimulate;
+        }
+      }
+
+      scrollToBottomIfAtBottom();
+    }
+  }
+
+  try {
+    await refetch(params);
+
+    const answerItem = answerResult.value[answerResult.value.length - 1];
+
+    if (answerItem?.biz) {
+      answerItem.loading = false;
+      answerItem.delayLoading = false;
+
+      if (answerItem.biz === 'DECISION_TABLE') {
+        scrollToBottom()
+      }
+    }
+
+    if (tempSimulate) {
+      answerResult.value.push(tempSimulate);
+    }
+
+    setTimeout(() => {
+      scrollToBottomIfAtBottom();
+    }, 500)
+  }
+  catch (error) {
+    console.log("exist error .....", error);
+  }
+}
+
+// 回答选项点击
+const handlerAlertOptions = (item, val, index) => {
+  const { list, isAllSelect } = item;
+
+  if (isAllSelect) return;
+
+  val.isActive = index;
+
+  const isExists = list.find(({ isActive }) => isActive === null);
+
+  if (!isExists) {
+    item.isAllSelect = true;
+
+    const result = item.list
+      .map(({ id, options, isActive }) => ({ [id]: options[isActive] }))
+      .reduce((accumulator, currentValue) => {
+        Object.keys(currentValue).forEach(key => accumulator[key] = currentValue[key]);
+        return accumulator;
+      }, {});
+
+    const newResult = { ...flowParams.feedback, ...result };
+    flowParams.feedback = newResult;
+    onRegenerate();
+  }
+}
+
+// 开始预测
+const handleSendSimulate = ({ simulate, table }) => {
+  const len = answerResult.value.length;
+
+  flowParams.simulate = simulate;
+  answerResult.value[len - 1].isDisable = true;
+
+  answerResult.value.push({
+    biz: 'DECISION_TABLE',
+    loading: true,
+    delayLoading: false,
+    table,
+    isDisable: false
+  })
+
+  scrollToBottom();
+
+  onRegenerate();
+}
+
+onMounted(() => {
+  const { id,category, reason } = route.query;
+  console.log({ id,category, reason });
+  handleOpenContent({ id, category, reason });
+})
+
+</script>
+
+<template>
+  <section class="flex items-start h-full" id="warning">
+    <TheChatView ref="scrollRef" :is-footer="false" :is-header="false">
+      <ChatBaseCard v-if="textDataSources">
+        <div class="waring-answer-wrapper">
+          <dl class="message-inner warning-info_medium ">
+            <dt class="mb-[2px] font-bold text-[#1A2029]">{{ textDataSources?.title }}</dt>
+            <dd v-for="item, index in textDataSources?.list" :key="index">
+              <span :class="['message-item', { 'text-[#F44C49]': item.isWarning }]">
+                  <span>{{ item.label }}</span>
+                  : 
+                  <span class="message-value" :title="item.value">{{ item.value }}</span>
+              </span>
+            </dd>
+          </dl>
+          <div class="table-inner">
+            <div class="warning-table mb-[8px]">
+              <div class="title">
+                <span>当前进水数据:</span>
+              </div>
+              <div class="main">
+                <BaseTable :columns="inColumns" :data="jsTableData"></BaseTable>
+              </div>
+            </div>
+            <div class="warning-table">
+              <div class="title">
+                <span>当前出水数据:</span>
+              </div>
+              <div class="main">
+                <BaseTable :columns="outColumns" :data="csTableData"></BaseTable>
+              </div>
+            </div>
+          </div>
+        </div>
+      </ChatBaseCard>
+
+      <section v-for="item, index in answerResult" :key="index">
+        <template v-if="item.biz === 'DECISION_REPORT'">
+          <ChatAnswer :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false"
+            :content="item.answer"></ChatAnswer>
+        </template>
+
+        <template v-if="item.biz === 'DECISION_ALERT'">
+          <ChatBaseCard :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false">
+            <p class="mb-[15px] font-bold text-[#1A2029]">需要确定以下问题,完成决策方案:</p>
+            <ul class="radio-wrapper space-y-[14px]">
+              <li class="flex items-center" v-for="val, i in item.list" :key="i">
+                <p class="mr-[14px]">{{ val.mainContent }}</p>
+                <p class="radio-btn-group space-x-[14px]">
+                  <span v-for="option, index in val.options" :class="['radio-btn', { active: val.isActive === index }]"
+                    @click="handlerAlertOptions(item, val, index)">{{ option }}</span>
+                </p>
+              </li>
+            </ul>
+          </ChatBaseCard>
+        </template>
+
+        <template v-if="item.biz === 'DECISION_SIMULATE'">
+          <button class="
+            px-[30px] py-[10px] mb-[20px]
+            rounded-[8px] 
+            bg-white text-[13px] 
+            text-[#5E5E5E] hover:text-[#2454FF]" :disabled="item.isDisable" @click="handleModelVisible">
+            水质预测推演
+          </button>
+        </template>
+
+        <template v-if="item.biz === 'DECISION_TABLE'">
+          <ChatAnswer :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false"
+            class="reset-chart">
+            <div class="markdown-body text-[15px] break-all">
+              <strong class="block mb-[16px]">推荐指标调整:</strong>
+              <div class="custom-table-wrapper">
+                <table>
+                  <thead>
+                    <tr>
+                      <th v-for="text in item.table.header" :key="text">{{ text }}</th>
+                    </tr>
+                  </thead>
+                  <tbody class="text-center">
+                    <tr>
+                      <td v-for="text in item.table.body" :key="text">{{ text }}</td>
+                    </tr>
+                  </tbody>
+                </table>
+              </div>
+              <strong class="block mb-[16px]">预测推演结果:</strong>
+              <span>以上指标达成后,预计{{ flowParams.category }}可达到:{{ item.content }}</span>
+            </div>
+          </ChatAnswer>
+          <button class="
+            px-[30px] py-[10px] mb-[20px]
+            rounded-[8px]
+            bg-white text-[13px]
+            text-[#5E5E5E] hover:text-[#2454FF]" :disabled="item.isDisable" @click="handleModelVisible">
+            水质预测推演
+          </button>
+        </template>
+      </section>
+
+      <ChatAnswer :loading="answerLoading" :delay-loading="answerLoading" :toggleVisibleIcons="false"
+        v-show="answerLoading" loadingText="内容生成中,大概需要1分钟..."></ChatAnswer>
+
+    </TheChatView>
+  </section>
+  <CustomModal v-model:visible="visible" :current-data="modalData" @on-submit="handleSendSimulate"></CustomModal>
+</template>
+
+<style lang="scss">
+#warning {
+  width: 100vw;
+  height: 100vh;
+  background: #eef6fb;
+}
+.reset-chart {
+  .markdown-body {
+    .custom-table-wrapper {
+      width: 100%;
+      overflow: hidden;
+      padding: 10px;
+
+      table td,
+      table th {
+        white-space: normal !important;
+      }
+    }
+  }
+}
+
+.select-card {
+  padding: 15px 10px 0 10px;
+}
+</style>

+ 2 - 2
src/views/work/WorkView.vue

@@ -249,8 +249,8 @@ onUnmounted(() => {
     <TheChatView ref="scrollRef" :is-back-btn="!!chatDataSource.length" @on-click-back="handleback">
 
       <div v-show="!chatDataSource.length">
-        <ChatWelcome title="您好,我是LibraAI智能助手" :sub-title="[
-          'LibarAI智能助手模块提供撰写文章、生成报告等服务',
+        <ChatWelcome title="您好,我是LibraAI辅助办公" :sub-title="[
+          'LibarAI辅助办公模块提供撰写文章、生成报告等服务',
           '请替换问题中##的内容'
         ]" />
         <div class="grid-container">