소스 검색

Merge branch 'master' of ssh://gitlab.fuxicarbon.com:1111/sunxiao/slibra-module-web into master

余尚辉 10 달 전
부모
커밋
319f08f380

+ 17 - 0
package-lock.json

@@ -33,6 +33,7 @@
         "@vitejs/plugin-vue-jsx": "^3.1.0",
         "@vue/tsconfig": "^0.5.1",
         "autoprefixer": "^10.4.19",
+        "dayjs": "^1.11.11",
         "npm-run-all2": "^6.1.2",
         "postcss": "^8.4.38",
         "tailwindcss": "^3.4.3",
@@ -1366,6 +1367,7 @@
     },
     "node_modules/@tsconfig/node20": {
       "version": "20.1.4",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node20/-/node20-20.1.4.tgz",
       "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==",
       "dev": true
     },
@@ -1395,6 +1397,7 @@
     },
     "node_modules/@types/node": {
       "version": "20.12.12",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.12.12.tgz",
       "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
       "dev": true,
       "dependencies": {
@@ -1417,6 +1420,7 @@
     },
     "node_modules/@vitejs/plugin-vue": {
       "version": "5.0.4",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
       "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
       "dev": true,
       "engines": {
@@ -1429,6 +1433,7 @@
     },
     "node_modules/@vitejs/plugin-vue-jsx": {
       "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz",
       "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==",
       "dev": true,
       "dependencies": {
@@ -1704,6 +1709,7 @@
     },
     "node_modules/@vue/tsconfig": {
       "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
       "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
       "dev": true
     },
@@ -2504,6 +2510,12 @@
         "date-fns": "2.x"
       }
     },
+    "node_modules/dayjs": {
+      "version": "1.11.11",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
+      "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==",
+      "dev": true
+    },
     "node_modules/de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
@@ -4903,6 +4915,7 @@
     },
     "node_modules/pinia": {
       "version": "2.1.7",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
       "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
       "dependencies": {
         "@vue/devtools-api": "^6.5.0",
@@ -6601,6 +6614,7 @@
     },
     "node_modules/typescript": {
       "version": "5.4.5",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.4.5.tgz",
       "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "devOptional": true,
       "bin": {
@@ -6775,6 +6789,7 @@
     },
     "node_modules/vite": {
       "version": "5.2.11",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.2.11.tgz",
       "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
       "dev": true,
       "dependencies": {
@@ -6914,6 +6929,7 @@
     },
     "node_modules/vue": {
       "version": "3.4.27",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.27.tgz",
       "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
       "dependencies": {
         "@vue/compiler-dom": "3.4.27",
@@ -6955,6 +6971,7 @@
     },
     "node_modules/vue-router": {
       "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.3.2.tgz",
       "integrity": "sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==",
       "dependencies": {
         "@vue/devtools-api": "^6.5.1"

+ 5 - 0
src/api/water.js

@@ -20,6 +20,11 @@ export const waterApi = {
   */
   getWaterStream: ({ data, onDownloadProgress, signal }) => http.post('/grpc/decisionStream', data, { onDownloadProgress, signal, }),
 
+  /**
+   * 获取预测
+  */
+  getWaringForecast: params => http.get('/front/bigModel/warning/forecastList/' + params),
+
   // getAnswerHistoryList: params => http.get('/front/bigModel/qa/pageList', { params }),
 
   // getAnswerHistoryDetail: params => http.get('/front/bigModel/qa/qaListBySessionId', { params }),

BIN
src/assets/images/login/bg-login.png


BIN
src/assets/images/login/bg-login1.png


+ 36 - 0
src/assets/styles/common.scss

@@ -95,6 +95,10 @@
     .message-inner {
       width: 194px;
       flex-shrink: 0;
+
+      span {
+        white-space: nowrap;
+      }
     }
 
     .table-inner {
@@ -148,6 +152,38 @@
 
 }
 
+.recode-card-item {
+  
+  &:hover, &_active {
+    border-radius: 2px;
+    background-color: #fff;
+    box-shadow: 0.5px 0.5px 4px 0px #93A1B233;
+    overflow: hidden;
+
+    .content {
+      color: #325DF3;
+    }
+
+    .del-icon {
+      display: block;
+
+      &:hover {
+        color: red;
+      }
+    }
+
+    &::before {
+      position: absolute;
+      top: 0;
+      left: 0px;
+      display: block;
+      content: " ";
+      width: 2px;
+      height: 100%;
+      background: #325DF3;
+    }
+  }
+}
 // pre code,
 // pre tt {
 //   line-height: 1.65;

+ 3 - 3
src/components/BaseInput/index.vue

@@ -13,10 +13,10 @@ const focusInput = _ => isFocusState.value = true;
 
 const blurInput = _ => isFocusState.value = false;
 
-const handleInpFocus = () => {
-  inputInstRef.value?.focus()
-}
 
+defineExpose({
+  handleInpFocus
+})
 </script>
 <template>
   <div class="chat-inp-inner border-[1px]" :class="[{ 'border-[#2454FF]': isFocusState }]">

+ 5 - 1
src/components/Chat/ChatAnswer.vue

@@ -31,6 +31,10 @@ const props = defineProps({
   toggleVisibleIcons: {
     type: Boolean,
     default: true
+  },
+  loadingText: {
+    type: String,
+    default: '内容生成中...'
   }
 })
 
@@ -60,7 +64,7 @@ const handleCopy = () => {
 </script>
 
 <template>
-  <ChatBaseCard class="answer-inner" :loading="loading" :delayLoading="delayLoading">
+  <ChatBaseCard class="answer-inner" :loading="loading" :delayLoading="delayLoading" :loadingText="loadingText">
 
     <slot></slot>
     

+ 5 - 1
src/components/Chat/ChatBaseCard.vue

@@ -36,6 +36,10 @@ const props = defineProps({
     type: Boolean,
     default: false
   },
+  loadingText: {
+    type: String,
+    default: "内容生成中..."
+  }
 })
 
 </script>
@@ -54,7 +58,7 @@ const props = defineProps({
       <div class="flex-1 pt-[4px] ml-[16px] text-[15px]">
 
         <template v-if="loading && delayLoading">
-          <p class="font-bold text-[#1A2029] leading-[24px]">内容生成中...</p>
+          <p class="font-bold text-[#1A2029] leading-[24px]">{{ loadingText }}</p>
         </template>
 
         <slot></slot>

+ 1 - 0
src/components/Chat/ChatInput.vue

@@ -63,6 +63,7 @@ const clearInpVal = () => {
 
 defineExpose({
   clearInpVal,
+  handleInpFocus,
   inpVal,
 })
 

+ 4 - 4
src/components/Layout/TheChatView.vue

@@ -52,12 +52,12 @@ defineExpose({ targetScrollDom });
 
 <template>
   <div class="flex-1 h-full chat-container">
-    <div class="chat-wrapper w-full h-full rounded-[20px]">
+    <div class="chat-wrapper w-full h-full flex flex-col rounded-[20px]">
       <div class="chat-header flex items-center justify-end py-[24px] pr-[18px] space-x-[16px]">
         <userTop></userTop>
       </div>
-      <main class="flex flex-col justify-between h-full m-auto chat-main"
-        :style="{ height: isFooter ? 'calc(100% - 212px)' : 'calc(100% - 100px)' }">
+      <!-- :style="{ minHeight: isFooter ? 'calc(100% - 310px)' : 'calc(100% - 100px)' }" -->
+      <main class="chat-main flex flex-1 flex-col justify-between m-auto">
         <div class="chat-scroll" ref="targetScrollDom">
           <!-- pb-[20px] -->
           <div class="w-[800px] m-auto ">
@@ -82,7 +82,7 @@ defineExpose({ targetScrollDom });
     background: linear-gradient(180deg, rgba(238, 253, 255, 0.5) 0%, rgba(231, 243, 252, 0.5) 100%);
 
     .chat-main {
-      height: calc(100% - 212px);
+      min-height: calc(100% - 310px);
       color: #1A2029;
 
       .chat-scroll {

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

@@ -17,7 +17,7 @@ const changeCollapse = () => appStore.toggleSubMenuCollapse();
       <div class="w-[28px] h-[28px]">
         <SvgIcon name="common-logo" size="28"></SvgIcon>
       </div>
-      <span class="block w-[70px] font-[10px]">人工智能运营体智慧决策助手</span>
+      <span class="block w-[70px] font-[10px] text-center">人工智能运营体智慧决策助手</span>
     </div>
     <!-- 图标 -->
     <div class="icon-group flex items-center justify-center"  @click="changeCollapse" v-show="!subMenuCollapse">

+ 3 - 2
src/components/Layout/TheMenu.vue

@@ -27,7 +27,7 @@ function renderChildrenIcon ({ name, size = '20' }) {
 }
 
 function renderLabel (val, url) {
-  return url ? (<a href={url} target="_blank" class="pl-1">{val}</a>) : (<span class="pl-1">{val}</span>);
+  return url ? (<a href={ url } target="_blank" class="pl-1">{ val }</a>) : (<span class="">{ val }</span>) ;
 }
 
 const menuOptions = [
@@ -59,7 +59,7 @@ const menuOptions = [
       {
         label: '预测报警',
         icon: renderChildrenIcon({ name: 'menu-analyse-notice' }),
-        key: 'key3',
+        key: 'normal',
       },
       {
         label: '智能工单',
@@ -82,6 +82,7 @@ const menuOptions = [
 
 const handleUpdateValue = (key, { url }) => {
   if (url) return window.open(url);
+  if (key == 'normal') return;
   router.push(key);
 }
 

+ 1 - 0
src/components/Layout/ThePublicLayout.vue

@@ -13,6 +13,7 @@ const initWraingListData = async () => {
 }
 
 onMounted(() => {
+  initWraingListData();
   setInterval(_ => initWraingListData(), 60 * 60 * 1000)
 })
 

+ 1 - 34
src/components/RecodeCardItem/index.vue

@@ -1,7 +1,7 @@
 <script setup>
 import { ref, computed } from 'vue';
 import { NPopconfirm, useMessage  } from 'naive-ui';
-import { chatApi } from "@/api/chat";
+
 import { SvgIcon } from '@/components';
 
 const props = defineProps({
@@ -19,12 +19,8 @@ const props = defineProps({
   }
 });
 
-const isOff = ref(false);
-
 const emit = defineEmits(['on-delete', 'on-click']);
 
-const message = useMessage();
-
 const handleCardClick = () => {
   emit('on-click', props.dataItem);
 }
@@ -57,34 +53,5 @@ const handleDelete = async () => {
     @include textLine(1);
   }
 
-  &:hover {
-    border-radius: 2px;
-    background-color: #fff;
-    box-shadow: 0.5px 0.5px 4px 0px #93A1B233;
-    overflow: hidden;
-
-    .content {
-      color: #325DF3;
-    }
-
-    .del-icon {
-      display: block;
-
-      &:hover {
-        color: red;
-      }
-    }
-
-    &::before {
-      position: absolute;
-      top: 0;
-      left: 0px;
-      display: block;
-      content: " ";
-      width: 2px;
-      height: 100%;
-      background: #325DF3;
-    }
-  }
 }
 </style>

+ 1 - 1
src/composables/useFetchStream.js

@@ -68,7 +68,7 @@ export function useFetchStream(url, options = {}, immediate = true) {
       return completeData;
     } catch (err) {
       errorHandler && errorHandler();
-      console.log("fetch err:", err);
+      console.log("fetch: cancel request success");
       error.value = err;
     } finally {
       loading.value = false;

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

@@ -148,8 +148,8 @@ const handleWelcomeRecommend = question => {
     <TheChatView ref="scrollRef" :is-footer="false">
 
       <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:" :sub-title="[
-        '报警分析功能具备实时监测与预警机制,检测到异常情况推送相关工作人员确保问题及时处理。',
-        '报警时间为每小时警报,请大家及时处理'
+        '水质报警功能对工艺过程指标实时监测,发现异常后将推送给相关人员决策方案',
+        '报警时间为每小时警报,请大家及时处理'
       ]"
         v-if="!textDataSources"
         :card-content="recommendList"

+ 138 - 56
src/views/analyse/WaterView.vue

@@ -15,11 +15,12 @@ import { useInfinite } from '@/composables/useInfinite';
 import { useRecommend } from '@/composables/useRecommend';
 import { useFetchStream } from '@/composables/useFetchStream';
 import { useScroll } from '@/composables/useScroll';
+import { SIMULATE_ENUM } from './components/config';
 
 const { recommendList } = useRecommend({type: 1});
-const { scrollRef, scrollToTop, scrollToBottomIfAtBottom } = useScroll();
+const { scrollRef, scrollToTop, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
 const { refetch, cancelFetch } = useFetchStream("/grpc/decisionStream", { methdos: 'POST' }, false);
-const { recordList, isFetching, onScrolltolower, onRestore, addHistoryRecord } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
+const { recordList, isFetching, onScrolltolower, onRestore } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
 
 const router = useRouter();
 const chatStore = useChatStore();
@@ -31,7 +32,7 @@ const flowParams = {
   feedback: '',
   category: '',
   warningId: '',
-  simulate: null
+  simulate: '{}'
 };
 
 const answerLoading = ref(false);
@@ -109,67 +110,124 @@ const columns = [
   }
 ]
 
-
-// 新建对话
-const handleCreateDialog = () => {
-  console.log("handleCreateDialog");
-}
-
-const handleLoad = () => {
-  console.log("loading")
-}
-
 const handleModelVisible = () => {
   visible.value = true;
 } 
 
+const resetConfiguration = () => {
+  /**
+   * 临时这样,后续统一处理
+   * */ 
+  textDataSources.value = '';
+  answerLoading.value = false;
+  answerResult.value = [];
+  flowParams.feedback = '';
+  flowParams.category = '';
+  flowParams.warningId = '';
+  flowParams.simulate = '{}';
+  cancelFetch();
+}
+
 /**
  * 报警详情
 */
 const handleOpenContent = async ({ id, category }) => {
 
   if ( id == flowParams.warningId ) return;
-
+  
   flowParams.category = category;
   flowParams.warningId = id;
+  flowParams.feedback = '';
+  flowParams.simulate = '{}';
+  answerLoading.value = false;
+  cancelFetch();
 
   const { data } = await waterApi.getWaringDetails(id);
+  const res = await waterApi.getWaringForecast(id);
+
   const showVal = JSON.parse(data.showVal);
   const { basic, jsData, csData } = showVal;
   const answer = JSON.parse(data.answer);
-  const [ answerStrItem ] = answer;
-  const answerObjItem = JSON.parse( answerStrItem );
-  
+ 
   cancelFetch();
 
   answerResult.value = [];
 
-  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 }
-  ]
+  const reportList = [];
+  const alertList = [];
+
+  answer.map(item => {
+
+    const answerObjItem = JSON.parse( item );
+
+    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") {
+      
+
 
-  if ( answerObjItem.biz === "DECISION_REPORT" ) {
+      // const usefulkeys = ['on', 'off'];
+      // const resultObj = {};
 
-    const answerContent = answer.map(item => {
-      const itemParse = JSON.parse(item); 
-      return itemParse.message;
-    }).join("");
+      // 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: ''
+      //     }
+      //   })
+      // })
+    }
+
+  })
+
+  if ( reportList.length ) {
+    const answerContent = reportList.join("");
     answerResult.value.push({
       biz: 'DECISION_REPORT',
       answer: answerContent,
       loading: false,
       delayLoading: false
     })
+  }
 
-  } else {
-    const [ parseAnswer ] = answer.map(item => {
+  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;
@@ -183,7 +241,28 @@ const handleOpenContent = async ({ id, category }) => {
     })
   }
 
-  console.log( basic );
+  // console.log( "reportList", reportList );
+  // console.log( "alertList", alertList );
+  // const [ answerStrItem ] = answer;
+  // const answerObjItem = JSON.parse( answerStrItem );
+
+
+
+
+  // console.log( "answerObjItem", answer, JSON.parse(answerObjItem.message) );
+  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);
 
   jsTableData.value = [jsData];
@@ -193,13 +272,11 @@ const handleOpenContent = async ({ id, category }) => {
 }
 
 const onChangeTabs = warningStatus => {
-  textDataSources.value = '';
-  answerLoading.value = false;
-  answerResult.value = [];
-  cancelFetch();
-  onRestore({ warningStatus })
+  resetConfiguration();
+  onRestore({ warningStatus });
 }
 
+// 生成流数据
 const onRegenerate = async () => {
   
   answerLoading.value = true;
@@ -215,7 +292,7 @@ const onRegenerate = async () => {
 
   let tempSimulate = null;
 
-  answerLoading.value = true;
+  answerLoading.value = answerResult.value[len -1 ].biz !== 'DECISION_TABLE';
 
   const params = {
     body: JSON.stringify(flowParams),
@@ -230,6 +307,7 @@ const onRegenerate = async () => {
 
       if (item.biz === 'DECISION_REPORT') {
         tempReport.answer += item.message;
+        tempReport.delayLoading = false;
         answerResult.value[len] = { ...tempReport };
       }
       
@@ -239,7 +317,7 @@ const onRegenerate = async () => {
         answerResult.value.push({
           biz: 'DECISION_ALERT',
           loading: true,
-          delayLoading: true,
+          delayLoading: false,
           isAllSelect: false,
           list
         })
@@ -251,7 +329,7 @@ const onRegenerate = async () => {
         if ( lastAnswerItem.biz === 'DECISION_TABLE' ) {
           answerResult.value[len - 1] = {
             ...lastAnswerItem,
-            content: JSON.parse(item.message).pred.join(",")
+            content: JSON.parse(item.message).pred.join(", ")
           }
         } else {
           const { off, on, pred } = JSON.parse(item.message);
@@ -264,7 +342,6 @@ const onRegenerate = async () => {
           }
           modalData.value = tempSimulate;
         }
-        console.log("DECISION_SIMULATE")
       }
 
       scrollToBottomIfAtBottom();
@@ -272,16 +349,24 @@ const onRegenerate = async () => {
   }
 
   try {
-    const res = await refetch(params);
-    console.log( "最终", res );
+    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);
@@ -318,12 +403,12 @@ const handleSendSimulate = ({ simulate, table }) => {
   const len = answerResult.value.length;
 
   flowParams.simulate = simulate;
-  answerResult.value[len - 1].isDisable = false;
+  answerResult.value[len - 1].isDisable = true;
 
   answerResult.value.push({
     biz: 'DECISION_TABLE',
     loading: true,
-    delayLoading: true,
+    delayLoading: false,
     table,
     isDisable: false
   })
@@ -336,7 +421,6 @@ const handleWelcomeRecommend = question => {
   chatStore.setChatQuestion(question);
   router.push('/answer');
 }
-
 </script>
 
 <template>
@@ -364,11 +448,10 @@ const handleWelcomeRecommend = question => {
     </TheSubMenu>
 
     <TheChatView ref="scrollRef" :is-footer="false">
-
       <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:"
         :sub-title="[
-          '报警分析功能具备实时监测与预警机制,检测到异常情况推送相关工作人员确保问题及时处理。',
-          '报警时间为每小时警报,请大家及时处理'
+          '水质报警功能针对五大核心指标实时监测,发现异常后将推送给相关人员决策方案',
+          '报警时间为每小时警报,请大家及时处理'
         ]" 
         :card-content="recommendList"
         @on-click="handleWelcomeRecommend"
@@ -401,7 +484,7 @@ const handleWelcomeRecommend = question => {
           </div>
         </div>
       </ChatBaseCard>
-
+  
       <section v-for="item,index in answerResult" :key="index">
         <template v-if="item.biz === 'DECISION_REPORT'">
           <ChatAnswer
@@ -411,7 +494,7 @@ const handleWelcomeRecommend = question => {
             :content="item.answer"
           ></ChatAnswer>
         </template>
-        <!-- {{ item }} -->
+        
         <template v-if="item.biz === 'DECISION_ALERT'">
           <ChatBaseCard
             :loading="item.loading"
@@ -468,7 +551,7 @@ const handleWelcomeRecommend = question => {
                 </tbody>
               </table>
               <strong class="block mb-[16px]">预测推演结果:</strong>
-              <span>出水硝酸盐:{{ item.content }}</span>
+              <span>未来三小时好氧池硝酸盐预测结果:{{ item.content }}</span>
             </div>
           </ChatAnswer>
           <button class="
@@ -489,11 +572,10 @@ const handleWelcomeRecommend = question => {
         :delay-loading="answerLoading"
         :toggleVisibleIcons="false"
         v-show="answerLoading"
+        loadingText="内容生成中,大概需要50秒..."
       ></ChatAnswer>
-
     </TheChatView>
   </section>
-
   <CustomModal
     v-model:visible="visible"
     :current-data="modalData"

+ 17 - 5
src/views/analyse/WorkOrder.vue

@@ -21,6 +21,7 @@ const switchActive = ref(false);
 
 const isLoading = ref(false);
 const inputRef = ref(null);
+const recordActive = ref(null);
 
 const currenSessionId = ref(null);
 
@@ -39,6 +40,8 @@ const handleCreateDialog = async () => {
   }
 
   currenSessionId.value = null;
+  
+  recordActive.value = null;
 
   clearChat();
 }
@@ -49,6 +52,8 @@ const handleChatDetail = async ({ sessionId }) => {
 
   controller.abort();
 
+  recordActive.value = sessionId;
+
   const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
 
   chatDataSource.value = data.map(item => ({ ...item, loading: false,  }));
@@ -171,18 +176,25 @@ onUnmounted(() => {
       </template>
 
       <div class="pr-[4px] text-[#5e5e5e]">
-        <RecodeCardItem v-for="item, index in recordList" :key="item.sessionId + index" :title="item.showVal"
-          :time="item.createTime" :data-item="item" @on-click="handleChatDetail" @on-delete="handeChatDelete" />
+        <RecodeCardItem
+          v-for="item, index in recordList" 
+          :key="item.sessionId + index"
+          :title="item.showVal"
+          :time="item.createTime"
+          :data-item="item"
+          :class="{'recode-card-item_active': recordActive === item.sessionId}"
+          @on-click="handleChatDetail"
+          @on-delete="handeChatDelete"
+        />
       </div>
     </TheSubMenu>
 
     <TheChatView ref="scrollRef" :is-footer="false">
       <ChatWelcome 
         title="您好,我是LibraAI智慧工单助手" 
-        card-title="您可以试着问我:"
         :sub-title="[
-          '基于大语言模型的智能数据分析助手,可以为您实现数据分析及数据解读',
-          '选择日期并填写关键信息为您生成日报工单'
+          '基于大语言模型的智能工单分析助手,可以为您实现数据分析及数据解读',
+          '选择日期并为您生成日报分析'
         ]"
         v-if="!chatDataSource.length"
       />

+ 25 - 11
src/views/analyse/components/CustomModal.vue

@@ -22,12 +22,26 @@ const simulateData = computed(() => {
 
   usefulkeys.forEach(key => {
     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 {
+        numVal = Number(item.value).toFixed(numDigit);
+      }
+
       return {
         ...item,
+        value: numVal,
         label: SIMULATE_ENUM[item.name],
-        inpVal: Array.isArray( item.value ) ? item.value.join() : item.value,
+        inpVal: Array.isArray( item.value ) ? item.value.join(", ") : item.value,
         errMsg: ''
       }
     })
@@ -52,15 +66,15 @@ const handleStartReport = () => {
   usefulkeys.forEach(key => {
     simulateData.value[key].forEach(item => {
       
-      simulate[item.name] = Array.isArray(item.value) ? item.inpVal.split(",") : item.inpVal;
+      simulate[item.name] = Array.isArray(item.value) ? item.inpVal.split(",").map(n => Number(n)) : item.inpVal;
 
-      table.header.push(item.label);
-      table.body.push(item.inpVal);
+      if (key === 'on') {
+        table.header.push(item.label);
+        table.body.push(item.inpVal);
+      }
     })
   })
   
-  console.log( simulateData.value );
-  console.log( simulate );
   handleCancel();
   emit('on-submit', { simulate: JSON.stringify(simulate), table })
   console.log( "result", JSON.stringify(simulate), table );
@@ -76,8 +90,8 @@ const handleStartReport = () => {
     :maskClosable="false"
   >
     <div class="modal-wrapper">
-      <p class="header mb-[16px] font-bold text-[16px] leading-[22px]">水质预测推演 - 出水硝酸盐</p>
-      <div class="content-card mb-[8px]">
+      <p class="header mb-[16px] font-bold text-[16px] leading-[22px]">水质预测-好氧硝酸盐</p>
+      <div class="content-card mb-[20px]">
         <p class="mb-[10px] font-bold text-[14px] leading-[20px]">可调参数</p>
         <ul class="inp-group grid grid-cols-2 gap-x-[24px]  gap-y-[8px]">
           <li
@@ -91,7 +105,7 @@ const handleStartReport = () => {
         </ul>
       </div>
 
-      <div class="content-card mb-[20px]">
+      <!-- <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">
@@ -101,7 +115,7 @@ const handleStartReport = () => {
             </span>
           </li>
         </ul>
-      </div>
+      </div> -->
 
       <div class="footer flex items-center justify-between">
         <p>*红色数字为建议调整数值</p>

+ 14 - 14
src/views/analyse/components/config.js

@@ -1,15 +1,15 @@
 export const SIMULATE_ENUM = {
-  COD_in: '进水COD',
-  DO_O:         '好氧池DO (多池)',
-  MLSS:         '好氧池MLSS (多池)',
-  Q_in:         '进水流量',
-  tyjyl:        '碳源药剂投加量',
-  r:            '内回流比(多池)',
-  cltjl:        '除磷药剂投加量',
-  gwnl:         '出泥量(千泥)',
-  hycxsy_all:   '好氧硝酸盐(多池)',
-  qyan_all:     '缺氧氨氮(多池)',
-  qyckxsy_all:  '缺氧硝酸盐',
-  T:            '好氧池水温',
-  pH:           '好氧池'
-}
+  COD_in:       '进水COD mg/L',
+  DO_O:         '好氧池末端DO (#1 #2) mg/L',
+  MLSS:         'MLSS (#1 #2) mg/L',
+  Q_in:         '进水流量 m3/h',
+  tyjyl:        '碳源药剂投加量 m³/h',
+  r:            '内回流比(#1 #2) %',
+  cltjl:        '除磷药剂投加量m³/h',
+  gwnl:         '干污泥量',
+  hycxsy_all:   '好氧硝酸盐(#1 #2) mg/L',
+  qyan_all:     '缺氧氨氮(#1 #2) mg/L',
+  qyckxsy_all:  '缺氧硝酸盐 mg/L',
+  T:            '水温',
+  pH:           'pH'
+}

+ 8 - 1
src/views/answer/AnswerView.vue

@@ -25,6 +25,7 @@ const switchActive = ref(false);
 
 const isLoading = ref(false);
 const inputRef = ref(null);
+const recordActive = ref(null);
 
 const currenSessionId = ref(null);
 
@@ -46,6 +47,8 @@ const handleCreateDialog = async () => {
 
   currenSessionId.value = null;
 
+  recordActive.value = null;
+
   clearChat();
 }
 
@@ -53,7 +56,10 @@ const handleCreateDialog = async () => {
 const handleChatDetail = async ({ sessionId }) => {
   isLoading.value = false;
 
+  recordActive.value = sessionId;
+
   controller.abort();
+  inputRef.value.clearInpVal();
 
   const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
 
@@ -184,6 +190,7 @@ onUnmounted(() => {
           :title="item.showVal"
           :time="item.createTime"
           :data-item="item"
+          :class="{'recode-card-item_active': recordActive === item.sessionId}"
           @on-click="handleChatDetail"
           @on-delete="handeChatDelete"
         />
@@ -194,7 +201,7 @@ onUnmounted(() => {
     <TheChatView ref="scrollRef">
       <ChatWelcome title="您好,我是LibraAI专家问答" card-title="您可以试着问我:"
         :sub-title="[
-          '期待与您一同规划和完成未来的工作。有任何重点或需讨论的事项,随时告诉我'
+          '期待与您一同规划和完成未来的工作。有任何重点或需讨论的事项,随时告诉我'
         ]" 
         :card-content="recommendList"
         v-if="!chatDataSource.length"

+ 18 - 7
src/views/helper/HelperView.vue

@@ -12,7 +12,6 @@ const ANSWER_ID_KEY = '@@id@@';
 
 let controller = new AbortController();
 
-// TODO: 如果这里的key不一样,将会在拆一层组件出来 - list
 const { recordList, isFetching, onScrolltolower, onReset, addHistoryRecord } = useInfinite('/front/bigModel/qa/pageList', { module: 2 });
 const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
 const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat();
@@ -24,6 +23,7 @@ const switchActive = ref(false);
 
 const isLoading = ref(false);
 const inputRef = ref(null);
+const recordActive = ref(null);
 
 const currenSessionId = ref(null);
 
@@ -43,6 +43,8 @@ const handleCreateDialog = async () => {
 
   inputRef.value.clearInpVal();
 
+  recordActive.value = null;
+
   currenSessionId.value = null;
 
   clearChat();
@@ -52,6 +54,10 @@ const handleCreateDialog = async () => {
 const handleChatDetail = async ({ sessionId }) => {
   isLoading.value = false;
 
+  recordActive.value = sessionId;
+  
+  inputRef.value.clearInpVal();
+
   controller.abort();
 
   const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
@@ -59,6 +65,8 @@ const handleChatDetail = async ({ sessionId }) => {
   chatDataSource.value = data.map(item => ({ ...item, loading: false,  }));
   currenSessionId.value = sessionId;
 
+
+
   scrollToBottom();
 }
 
@@ -143,7 +151,9 @@ const handleSubmit = async (question, realQuestion = '') => {
 
 // 处理推荐问题
 const handleWelcomeRecommend = ({ content }) => {
+  console.log( inputRef.value )
   inputRef.value.inpVal = content;
+  inputRef.value.handleInpFocus();
 }
 
 // 删除历史对话
@@ -179,6 +189,7 @@ onUnmounted(() => {
           :title="item.showVal"
           :time="item.createTime"
           :data-item="item"
+          :class="{'recode-card-item_active': recordActive === item.sessionId}"
           @on-click="handleChatDetail"
           @on-delete="handeChatDelete"
         />
@@ -187,10 +198,10 @@ onUnmounted(() => {
 
     <TheChatView ref="scrollRef">
       <div v-if="!chatDataSource.length">
-        <ChatWelcome title="您好,我是LibraAI专家问答" card-title="您可以试着问我:"
+        <ChatWelcome title="您好,我是LibraAI专家问答"
         :sub-title="[
-          'LibarAI智能助手模块,具有强大的文本理解和生成能力,可快速响应用户需求',
-          '提供撰写文章、生成报告等服务。'
+          'LibarAI智能助手模块提供撰写文章、生成报告等服务',
+          '请替换问题中##的内容'
         ]"
         />
         <div class="grid-container">
@@ -202,7 +213,8 @@ onUnmounted(() => {
               @click="handleWelcomeRecommend(item)"
             >
               <div class="grid-item-icon space-x-[8px]">
-                <SvgIcon name="tool-report" size="24"></SvgIcon>
+                <img :src="item.banner" alt="" class="w-[24px]">
+                <!-- <SvgIcon name="tool-report" size="24"></SvgIcon> -->
                 <h3 class="grid-item-title">{{item.title}}</h3>
               </div>
               <div class="text-[#5E5E5E] mt-[8px] text-justify">
@@ -250,7 +262,6 @@ onUnmounted(() => {
 <style scoped lang="scss">
 .grid-container{
 	position: relative;
-	height: calc(100vh - 460px);
   padding-bottom: 20px;
   margin-top: 36px;
 	overflow: hidden;
@@ -273,7 +284,6 @@ onUnmounted(() => {
 			padding: 16px;
 			margin-bottom: 16px;
 			border-radius: 10px;
-			box-shadow: 0 2px 4px rgba(0,0,0,0.08);
 			background-color: #fff;
 			-webkit-column-break-inside: avoid;
 			break-inside: avoid;
@@ -282,6 +292,7 @@ onUnmounted(() => {
 
       &:hover {
         border:1px solid #2454FF;
+        box-shadow: 0 2px 4px rgba(0,0,0,0.08);
       }
 
 			.grid-item-icon{

+ 11 - 5
src/views/login/LoginView.vue

@@ -56,17 +56,18 @@ const handleSubmit = async () => {
     <main class="main flex items-center space-x-[98px]">
       <div class="text-[#07233C] pb-[90px]">
         <div class="text-title mb-[8px] text-[36px] font-bold leading-[44px]">
-          <p class="text-[#2454FF]">人工智能运营体</p>
-          <p>全国首家落地水务大模型</p>
+          <p class="text-[#2454FF]">LibraAI</p>
+          <p>首家水务领域垂直大模型</p>
         </div>
         <p class="w-[540px] text-[18px] text-justify leading-[26px]">
-          人工智能运营体(Libra水务大模型+智能装备)赋能水务行业,为用户提供全参检测、专家问答、报警决策、预测预警、智能投药、精准曝气等多项功能,通过全参数自动化验、运维工能力提升,带动水厂提效变革
+          赋能水务行业,将海量水务数据与大模型能力相融合 <br>
+          为水务行业主体提供创新应用和全面支持
         </p>
       </div>
 
       <div class="login-form w-[442px] h-[454px] bg-white rounded-[16px]">
-        <p class="title pl-[2px] mb-[30px] text-[28px] leading-[40px] font-bold">系统登</p>
-        <div class="w-full form-inner">
+        <p class="title pl-[2px] mb-[30px] text-[28px] leading-[40px] font-bold">系统登</p>
+        <div class="form-inner w-full">
           <ul class="form-inp-list">
             <li class="inp-item-inner">
               <input type="text" placeholder="请输入用户名称" v-model="loginFormData.username">
@@ -107,6 +108,11 @@ const handleSubmit = async () => {
 
   .text-title {
     font-family: AlimamaShuHeiTi;
+    p:nth-child(1) {
+      background: linear-gradient(92.36deg, #5ABBF2 4.56%, #2454FF 20.02%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
   }
 
   .login-form {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 283 - 467
yarn.lock


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.