Jelajahi Sumber

feat: 水务报警版块调整

sunxiao 9 bulan lalu
induk
melakukan
b6b8492d7c

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

@@ -90,7 +90,8 @@
 
   // 回答区域卡片
   .waring-answer-wrapper {
-    @include flex(x, start, between);
+    display: flex;
+    justify-content: space-between;
 
     .message-inner {
       width: 194px;
@@ -102,7 +103,7 @@
     }
 
     .table-inner {
-      @include flex(y, end, center);
+      @include flex(y, start, between);
       padding-left: 20px;
       border-left: 1px solid #F1F1F1;
 

+ 1 - 1
src/components/Layout/TheMenu.vue

@@ -57,7 +57,7 @@ const menuOptions = [
         key: '/pymol-warn',
       },
       {
-        label: '预测警',
+        label: '预测警',
         icon: renderChildrenIcon({ name: 'menu-analyse-notice' }),
         key: '/forecast-warn',
       },

+ 21 - 13
src/components/RecodeSquareCardItem/index.vue

@@ -15,7 +15,7 @@ const emit = defineEmits(['on-click']);
 
 const warningType = computed(() => {
   const item = props.item;
-  const waterWhite = {
+  const waterEnum = {
     '0': {
       label: '报警中',
       cls: 'tips_warning'
@@ -33,7 +33,7 @@ const warningType = computed(() => {
       cls: 'tips_warning'
     }
   }
-  const earlyWaring = {
+  const earlyEnum = {
     '0': {
       label: '预警中',
       cls: 'tips_warning'
@@ -44,14 +44,13 @@ const warningType = computed(() => {
     },
   }
 
-  if ( item.type == 0 ) {
-    return waterWhite[item.status + '']
-  }
-  if ( item.type === 1 ) {
-    return waterWhite[item.status + '']
-  }
-  if ( item.type === 2 ) {
-    return earlyWaring[item.status + '']
+  switch(item.type) {
+    case 0:
+      return waterEnum[item.status + ''];
+    case 1:
+      return waterEnum[item.status + ''];
+    case 2:
+      return earlyEnum[item.status + ''];
   }
 })
 
@@ -60,7 +59,7 @@ const dataSources = computed(() => {
   if (item.type == 0) {
     return [
       { label: '报警时间', value: item.time },
-      { label: '报警值',   value: truncateDecimals(item.warningVal), type: 'wraning' },
+      { label: '报警值',  value: truncateDecimals(item.warningVal), type: 'wraning' },
       { label: '报警级别', value: item.level },
       { label: '报警次数', value: item.counts },
     ]
@@ -68,15 +67,24 @@ const dataSources = computed(() => {
   if (item.type == 1) {
     return [
       { label: '报警时间', value: item.time },
-      { label: '报警值',   value: item.warningVal, type: 'wraning' },
+      { label: '报警值', value: item.warningVal, type: 'wraning' },
       { label: '报警次数', value: item.counts },
     ]
   }
+  if (item.type == 3) {
+    return [
+      { label: '预警时间', value: item.time },
+      { label: '超标时间', value: item.warningVal },
+      { label: '现在值', value: item.counts },
+      { label: '预测值', value: item.counts, type: 'wraning' },
+      { label: '设计值', value: item.counts }
+    ]
+  }
 });
 
 const handleEmitParent = () => {
   emit('on-click', props.item)
-}
+};
 
 </script>
 

+ 14 - 0
src/utils/format.js

@@ -2,6 +2,20 @@ const formatTextData = (dataSource, whileList) => {
 
 }
 
+export const formatToData = (dataSource, warnKey) => {
+  const reuslt = {
+    title: dataSource?.title,
+    list: []
+  }
+  delete dataSource.title;
+  reuslt.list = Object.entries(dataSource).map(([key, value]) => {
+    if ( Number.isFinite(value) ) value = Number(value.toFixed(2));
+    if ( key.includes("值") ) value = value? value + 'mg/L' : '';
+    return { label: key, value, isWarning: warnKey === key };
+  });
+  return reuslt;
+}
+
 export const format = {
   textSorting(dataSource, rule) {
     const title = dataSource.title || dataSource['进水SS超标报警'];

+ 85 - 4
src/views/analyse/ForecastView.vue

@@ -1,9 +1,90 @@
 <script setup>
-  
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+import { NTabs, NTab } from 'naive-ui';
+import { useChatStore } from '@/stores/modules/chatStore';
+import { BaseTable, ChatWelcome, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
+import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
+import { format, truncateDecimals } from "@/utils/format";
+import { waterApi } from '@/api/water';
+import {useInfinite, useRecommend, useScroll} from '@/composables'
+import { columns } from './config';
+
+const { recommendList } = useRecommend({ type: 2 });
+const { scrollRef, scrollToTop } = useScroll();
+const { recordList, isFetching, onScrolltolower, onRestore } = useInfinite('/front/bigModel/warning/pageList', { type: 1, warningStatus: 0 });
+
+const router = useRouter();
+const chatStore = useChatStore();
+
+const answerResult = ref("");
+const textDataSources = ref(null);
+
+// 进出水数据
+const jsTableData = ref([]);
+const csTableData = ref([]);
+
+// 切换Tabs
+const onChangeTabs = warningStatus => {
+  answerResult.value = '';
+  textDataSources.value = '';
+  onRestore({ warningStatus })
+}
 </script>
 
 <template>
-  <div>
-    预测报警
-  </div>
+  <section class="flex items-start h-full" id="warning">
+    <TheSubMenu title="预测预警" @scrollToLower="onScrolltolower" :loading="isFetching">
+      <template #top>
+        <div class="border-[#DAE5ED]">
+          <n-tabs type="line" justify-content="space-evenly">
+            <n-tab name="oasis" tab="正在预警" @click="onChangeTabs(0)"></n-tab>
+            <n-tab name="thebeatles" tab="历史预警" @click="onChangeTabs(1)"></n-tab>
+          </n-tabs>
+        </div>
+      </template>
+      <div class="px-[12px] py-[14px] text-[#5e5e5e]">
+        <div class="grid grid-cols-1 gap-[12px]">
+          <RecodeSquareCardItem v-for="item in recordList" :key="item.id" :item="item" @on-click="handleOpenContent" />
+        </div>
+      </div>
+    </TheSubMenu>
+
+    <TheChatView ref="scrollRef" :is-footer="false">
+      <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:" :sub-title="[
+        '报警分析功能具备实时监测与预警机制,检测到异常情况推送相关工作人员确保问题及时处理',
+        '报警时间为每小时警报,请大家及时处理'
+      ]"
+        v-if="!textDataSources"
+        :card-content="recommendList"
+        @on-click="handleWelcomeRecommend"
+      />
+      <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="{'text-[#F44C49]': item.isWarning}">{{ item.label }}: {{ item.value }}</span></dd>
+          </dl>
+          <div class="table-inner">
+            <div class="warning-table mb-[8px]">
+              <div class="title">
+                <span>当前进水数据:</span>
+              </div>
+              <div class="main">
+                <BaseTable :columns="columns" :data="jsTableData"></BaseTable>
+              </div>
+            </div>
+            <div class="warning-table">
+              <div class="title">
+                <span>当前出水数据:</span>
+              </div>
+              <div class="main">
+                <BaseTable :columns="columns" :data="csTableData"></BaseTable>
+              </div>
+            </div>
+          </div>
+        </div>
+      </ChatBaseCard>
+    </TheChatView>
+  </section>
 </template>

+ 7 - 67
src/views/analyse/PymolView.vue

@@ -5,9 +5,10 @@ import { NTabs, NTab } from 'naive-ui';
 import { useChatStore } from '@/stores/modules/chatStore';
 import { BaseTable, ChatWelcome, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
 import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
-import { format, truncateDecimals } from "@/utils/format";
+import { formatToData } from "@/utils/format";
 import { waterApi } from '@/api/water';
-import {useInfinite, useRecommend, useScroll} from '@/composables'
+import { useInfinite, useRecommend, useScroll } from '@/composables'
+import { columns } from './config';
 
 const { recommendList } = useRecommend({ type: 2 });
 const { scrollRef, scrollToTop } = useScroll();
@@ -23,69 +24,6 @@ const textDataSources = ref(null);
 const jsTableData = ref([]);
 const csTableData = ref([]);
 
-const renderRowDom = ({ row, key }) => {
-  const { exceed, value } = row[key] || {};
-  const cls = exceed ? 'text-[#F44C49] font-bold' : 'text-[1A2029]'
-  return (<span class={cls}>{truncateDecimals(value)} {exceed && <i>↑</i>}</span>)
-}
-
-const columns = [
-  {
-    title: '流量(m³/h)',
-    key: 'name',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'small',
-    width: '80px',
-    render: (row) => renderRowDom({ row, key: '流量' })
-  },
-  {
-    title: 'COD(mg/L)',
-    key: 'small',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'small',
-    width: '80px',
-    render: (row) => renderRowDom({ row, key: 'COD' })
-  },
-  {
-    title: 'TN(mg/L)',
-    key: 'address',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'small',
-    width: '80px',
-    render: (row) => renderRowDom({ row, key: 'TN' })
-  },
-  {
-    title: 'NH3-N(mg/L)',
-    key: 'tags',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'small',
-    width: '80px',
-    render: (row) => renderRowDom({ row, key: 'NH3-N' })
-  },
-  {
-    title: 'TP(mg/L)',
-    key: 'COD',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'small',
-    width: '80px',
-    render: (row) => renderRowDom({ row, key: 'TP' })
-  },
-  {
-    title: 'SS(mg/L)',
-    key: '流量',
-    titleAlign: 'center',
-    align: 'center',
-    className: 'age',
-    width: '78px',
-    render: (row) => renderRowDom({ row, key: 'SS' })
-  }
-]
-
 // 切换Tabs
 const onChangeTabs = warningStatus => {
   answerResult.value = '';
@@ -98,7 +36,7 @@ const handleOpenContent = async ({ id, category }) => {
   const { data } = await waterApi.getWaringDetails(id);
   const showVal = JSON.parse(data.showVal);
   const { basic, jsData, csData } = showVal;
-  console.log( jsData );
+
   const textWhiteList = [
     { label: '报警时间', realKey: '报警时间', value: '', isWarning: false },
     { label: '报警值',   realKey: '报警值', value: 'mg/L', isWarning: true },
@@ -111,7 +49,9 @@ const handleOpenContent = async ({ id, category }) => {
 
   answerResult.value = data.answer;
 
-  textDataSources.value = format.textSorting(basic, textWhiteList);
+  textDataSources.value = formatToData(basic, '报警值');
+
+  // textDataSources.value = format.textSorting(basic, textWhiteList);
 
   jsTableData.value = [jsData];
   csTableData.value = [csData];

+ 25 - 92
src/views/analyse/WaterView.vue

@@ -3,16 +3,16 @@ import { ref } from 'vue';
 import { useRouter } from 'vue-router';
 import { NTabs, NTab } from 'naive-ui';
 import { useChatStore } from '@/stores/modules/chatStore';
-import { columns } from './config/index.jsx';
 import { BaseTable, ChatWelcome, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
+import { useInfinite, useRecommend, useFetchStream, useScroll } from '@/composables';
 import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
 import { CustomModal } from "./components";
+import { columns } from './config';
 
-import { format } from "@/utils/format";
+import { formatToData } from "@/utils/format";
 
 import { waterApi } from '@/api/water';
 
-import { useInfinite, useRecommend, useFetchStream, useScroll } from '@/composables';
 
 const { recommendList } = useRecommend({type: 1});
 const { scrollRef, scrollToTop, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
@@ -68,7 +68,7 @@ const resetConfiguration = () => {
 /**
  * 报警详情
 */
-const handleOpenContent = async ({ id, category }) => {
+const handleOpenContent = async ({ id, category, reason:title }) => {
 
   if ( id == flowParams.warningId ) return;
 
@@ -76,14 +76,14 @@ const handleOpenContent = async ({ id, category }) => {
   flowParams.warningId = id;
   flowParams.feedback = '';
   flowParams.simulate = '{}';
+
   answerLoading.value = false;
 
   const { data } = await waterApi.getWaringDetails(id);
-  // const res = await waterApi.getWaringForecast(id);
 
+  const answer = JSON.parse(data.answer);
   const showVal = JSON.parse(data.showVal);
   const { basic, jsData, csData } = showVal;
-  const answer = JSON.parse(data.answer);
  
   cancelFetch();
 
@@ -93,72 +93,23 @@ const handleOpenContent = async ({ id, category }) => {
   const alertList = [];
 
   answer.map(item => {
-
-    const answerObjItem = JSON.parse( item );
-
-    // TODO: 后面带需求确定后完善
-
-    if ( answerObjItem.biz === "DECISION_REPORT" ) {
-      reportList.push(answerObjItem.message);
-
-      // const answerContent = answer.map(item => {
-      //   const itemParse = JSON.parse(item); 
-      //   return itemParse.message;
-      // }).join("");
-
-      // answerResult.value.push({
-      //   biz: 'DECISION_REPORT',
-      //   answer: answerContent,
-      //   loading: false,
-      //   delayLoading: false
-      // })
-    }
-
-    if( answerObjItem.biz === "DECISION_ALERT" ) {
-      alertList.push(item);
-      // const [ parseAnswer ] = answer.map(item => {
-      //   const result = JSON.parse( item );
-      //   result.message = Object.keys(result.message).map(key => ({ ...result.message[key], isActive: null }));
-      //   return result;
-      // })
-
-      // answerResult.value.push({
-      //   biz: 'DECISION_ALERT',
-      //   loading: false,
-      //   delayLoading: false,
-      //   isAllSelect: false,
-      //   list: parseAnswer?.message
-      // })
-    }
-
-    if (answerObjItem.biz === "DECISION_SIMULATE") {
-      
-
-
-      // const usefulkeys = ['on', 'off'];
-      // const resultObj = {};
-
-      // usefulkeys.forEach(key => {
-      //   const tempArr = data[key];
-
-      //   resultObj[key] = tempArr.map(item => {
-      //     return {
-      //       ...item,
-      //       label: SIMULATE_ENUM[item.name],
-      //       inpVal: Array.isArray( item.value ) ? item.value.join() : item.value,
-      //       errMsg: ''
-      //     }
-      //   })
-      // })
+    const answerObjItem = JSON.parse(item);
+    switch(answerObjItem.biz) {
+      case "DECISION_REPORT":
+        reportList.push(answerObjItem.message);
+        break
+      case "DECISION_ALERT":
+        alertList.push(JSON.parse(item));
+        break
+      case "DECISION_SIMULATE":
+        // console.log("实际未返回");
     }
-
   })
 
   if ( reportList.length ) {
-    const answerContent = reportList.join("");
     answerResult.value.push({
       biz: 'DECISION_REPORT',
-      answer: answerContent,
+      answer: reportList.join(""),
       loading: false,
       delayLoading: false
     })
@@ -166,9 +117,8 @@ const handleOpenContent = async ({ id, category }) => {
 
   if ( alertList.length ) {
     const [ parseAnswer ] = alertList.map(item => {
-      const result = JSON.parse( item );
-      result.message = Object.keys(result.message).map(key => ({ ...result.message[key], isActive: null }));
-      return result;
+      item.message = Object.keys(item.message).map(key => ({ ...item.message[key], isActive: null }));
+      return item;
     })
     answerResult.value.push({
       biz: 'DECISION_ALERT',
@@ -179,25 +129,12 @@ const handleOpenContent = async ({ id, category }) => {
     })
   }
 
-  const textWhiteList = [
-    { label: '报警时间', realKey: '报警时间', value: '', isWarning: false },
-    { label: '报警值',   realKey: '报警值', value: 'mg/L', isWarning: true },
-    { label: '管控值',   realKey: '管控值', value: 'mg/L', isWarning: false },
-    { label: '标准值',   realKey: '标准值', value: 'mg/L', isWarning: false },
-    { label: '报警级别', realKey: '告警级别', value: '', isWarning: false },
-    { label: '报警次数', realKey: '报警次数', value: '', isWarning: false },
-    { label: '数据来源', realKey: '数据来源', value: '', isWarning: false },
-    { label: '状态',     realKey: '状态', value: '', isWarning: false }
-  ]
-
-  basic['数据来源'] = '在线仪表';
-
-  textDataSources.value = format.textSorting(basic, textWhiteList);
+  basic.title = title;
+  textDataSources.value = formatToData(basic, '报警值');
 
   jsTableData.value = [jsData];
   csTableData.value = [csData];
 
-  scrollToTop();
 }
 
 const onChangeTabs = warningStatus => {
@@ -221,17 +158,14 @@ const onRegenerate = async () => {
 
   let tempSimulate = null;
 
-  answerLoading.value = answerResult.value[len -1 ].biz !== 'DECISION_TABLE';
+  answerLoading.value = answerResult.value[len - 1 ].biz !== 'DECISION_TABLE';
 
   const feedback = flowParams.feedback
 
   const params = {
     body: JSON.stringify({ ...flowParams, feedback: JSON.stringify(feedback) }),
-    errorHandler: () => {
-      
-    },
+    errorHandler: () => {},
     successHandler: data => {
-
       const item = JSON.parse(data);
 
       answerLoading.value = false;
@@ -244,7 +178,6 @@ const onRegenerate = async () => {
       
       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,
@@ -256,7 +189,6 @@ const onRegenerate = async () => {
 
       if (item.biz === 'DECISION_SIMULATE') {
         const lastAnswerItem = answerResult.value[len - 1];
-
         if ( lastAnswerItem.biz === 'DECISION_TABLE' ) {
           answerResult.value[len - 1] = {
             ...lastAnswerItem,
@@ -283,6 +215,7 @@ const onRegenerate = async () => {
     await refetch(params);
 
     const answerItem = answerResult.value[answerResult.value.length - 1];
+
     if (answerItem?.biz) {
       answerItem.loading = false;
       answerItem.delayLoading = false;
@@ -291,6 +224,7 @@ const onRegenerate = async () => {
         scrollToBottom()
       }
     }
+
     if (tempSimulate) {
       answerResult.value.push(tempSimulate);
     }
@@ -514,5 +448,4 @@ const handleWelcomeRecommend = question => {
     :current-data="modalData"
     @on-submit="handleSendSimulate"
     ></CustomModal>
-
 </template>

+ 2 - 16
src/views/analyse/components/CustomModal.vue

@@ -24,13 +24,10 @@ const simulateData = computed(() => {
     const tempArr = data[key];
     resultObj[key] = tempArr.map(item => {
 
-      // MLSS   r  mlss 和内回流比是整数
-
       let numVal = '';
       const keyWhiteList = ['MLSS', 'r']
       const numDigit = keyWhiteList.includes(item.name) ? 0 : 2;
 
-
       if (Array.isArray( item.value )) {
         numVal = item.value.map(n => Number(n).toFixed(numDigit));
       } else {
@@ -75,7 +72,8 @@ const handleStartReport = () => {
     })
   })
   
-  handleCancel();
+  modelVisible.value = false;
+
   emit('on-submit', { simulate: JSON.stringify(simulate), table })
   console.log( "result", JSON.stringify(simulate), table );
 }
@@ -105,18 +103,6 @@ const handleStartReport = () => {
         </ul>
       </div>
 
-      <!-- <div class="content-card mb-[20px]">
-        <p class="mb-[10px] font-bold text-[14px] leading-[20px]">相关参数</p>
-        <ul class="grid grid-cols-2 gap-x-[24px]  gap-y-[10px]">
-          <li class="space-x-[8px]" v-for="item,index in simulateData.off" :key="index">
-            <span class="text-[#5E5E5E]">{{ item.label }}</span>
-            <span class="text-[#1A2029] font-bold">
-              {{ item.inpVal }}
-            </span>
-          </li>
-        </ul>
-      </div> -->
-
       <div class="footer flex items-center justify-between">
         <p>*红色数字为建议调整数值</p>
         <div class="btn-group space-x-[16px]">