浏览代码

feat: 工单分析报告

whh 9 月之前
父节点
当前提交
06f10a9d66

+ 8 - 0
src/api/order.js

@@ -0,0 +1,8 @@
+import http from "@/utils/request";
+
+export const orderApi = {
+  /**
+   * 获取助手列表
+   */
+  postOrderChart: data => http.post('/front/bigModel/custom/workOrder', data),
+}

+ 6 - 6
src/components/RecodeSquareCardItem/index.vue

@@ -64,7 +64,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', unit: 'mg/L' },
       { label: '报警级别', value: item.level },
       { label: '报警次数', value: item.counts },
     ]
@@ -72,7 +72,7 @@ const dataSources = computed(() => {
   if (item.type == 1) {
     return [
       { label: '报警时间', value: item.time },
-      { label: '报警值', value: Number(item?.warningVal?.toFixed(2)), type: 'wraning' },
+      { label: '报警值', value: Number(item?.warningVal?.toFixed(2)), type: 'wraning', unit: 'mg/L' },
       { label: '报警次数', value: item.counts },
     ]
   }
@@ -80,9 +80,9 @@ const dataSources = computed(() => {
     return [
       { label: '预警时间', value: item.time },
       { label: '超标时间', value: item.time },
-      { label: '现在值', value: Number(item?.warningVal?.toFixed(2)) ?? '' },
-      { label: '预测值', value: Number(item?.forecastVal?.toFixed(2)) ?? '', type: 'wraning' },
-      { label: '设计值', value: item.designVal }
+      { label: '现在值', value: Number(item?.warningVal?.toFixed(2)) ?? '', unit: 'mg/L' },
+      { label: '预测值', value: Number(item?.forecastVal?.toFixed(2)) ?? '', type: 'wraning', unit: 'mg/L' },
+      { label: '设计值', value: item.designVal, unit: 'mg/L' }
     ]
   }
 });
@@ -103,7 +103,7 @@ const handleEmitParent = () => {
         <NEllipsis class="font-bold text-[#1A2029] leading-[20px]">{{ item.reason }}</NEllipsis>
       </dt>
       <dd class="flex items-center" v-for="(item, index) in dataSources" :key="index">
-        <span>{{ item.label }}: {{ item.value }}</span>
+        <span>{{ item.label }}: {{ item.value }} {{ item.unit }}</span>
         <SvgIcon name="tool-up" class="ml-[4px]" v-if="item.type"></SvgIcon>
       </dd>
     </dl>

+ 1 - 1
src/composables/useInfinite.js

@@ -37,7 +37,7 @@ export const useInfinite = (path, props) => {
   }
 
   const onReset = async () => {
-    const { rows, total } = await initRecordData({pageNum:1, pageSize: (pageParams.value.page * pageParams.value.pageSize) + 1});
+    const { rows, total } = await initRecordData({pageNum:1, pageSize: (pageParams.value.pageNum * pageParams.value.pageSize) + 1});
 
     recordList.value = rows;
     counter.value = total;

+ 0 - 337
src/utils/echartOptions.js

@@ -1,337 +0,0 @@
-import * as echarts from 'echarts';
-let xLabel = [11, 12, 13, 14, 15, 16, 17, 18, 19]
-let goToSchool = [9.019096, 17.795139, 6.467014, 5.746528, 10.16493, 10.112847, 7.75, 6.74, 7.14]
-
-export const getAreaOptions = ({ xAxisData, seriesList }) => {
-  const series = seriesList.map((data, index) => {
-    const i = !index;
-    return {
-      name: i ? '过去' : '未来',
-      type: 'line',
-      symbol: 'circle',
-      showAllSymbol: true,
-      symbolSize: 5,
-      smooth: true,
-      lineStyle: {
-        normal: {
-          width: 2,
-          color: i ? "rgba(25,163,223,1)" : "rgba(36,175,83,1)", // 线条颜色
-        },
-        borderColor: 'rgba(0,0,0,.4)',
-      },
-      itemStyle: {
-        color: i ? "#b7f9ff" : "#fff",
-        borderColor: i ? "#2185da" : "#2ee055",
-        borderWidth: 1,
-        shadowColor: 'rgba(22, 137, 229)',
-        shadowBlur: 1
-      },
-      areaStyle: {
-        normal: {
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-            offset: 0,
-            color: i ? "rgba(25,163,223,.3)" : "rgba(48, 209, 136,.3)"
-          },
-          {
-            offset: 1,
-            color: i ? "rgba(25,163,223, 0)" : "rgba(48, 209, 136, 0)"
-          }
-          ], false),
-          shadowColor: 'rgba(25,163,223, 0.5)',
-          shadowBlur: 20
-        }
-      },
-      z: i ? 3 : 2,
-      data
-    }
-  })
-
-  return {
-    backgroundColor: '#fff',
-    tooltip: {
-      trigger: "axis",
-      show: true,
-      formatter: function (params) {
-        const [item] = params.filter(item => item.value);
-        const color = item.dataIndex <= 5 ? '#2185da' : '#2ee055'
-        return `
-          <div class="text-[12px]">
-            <p>时间:${item.name + ':00'}</p>
-            <p class="flex items-center space-x-[6px]">
-              <span class="block w-[6px] h-[6px] rounded-full bg-[${color}]"></span>
-              <span>${Number(item.value.toFixed(2))} mg/L</span>
-            </p>
-          </div>
-          `
-      },
-    },
-    grid: {
-      top: '10%',
-      left: 40,
-      right: '5%',
-      bottom: '15%',
-    },
-    xAxis: [{
-      type: 'category',
-      boundaryGap: false,
-      axisLine: {
-        show: true,
-        lineStyle: {
-          color: '#e0e6f1'
-        },
-      },
-      axisLabel: {
-        textStyle: {
-          color: '#1F2328',
-          fontSize: 12,
-        },
-        formatter: function (data) {
-          return data + ":00"
-        }
-      },
-      splitLine: {
-        show: true,
-        lineStyle: {
-          type: 'dashed',
-        },
-      },
-      axisTick: {
-        show: false,
-      },
-      data: xLabel
-    }],
-    yAxis: [{
-      name: '',
-      nameTextStyle: {
-        fontSize: 12,
-        align: 'right',
-        padding: [10, 5],
-      },
-      min: 0,
-      splitLine: {
-        show: true,
-        lineStyle: {
-          type: 'dashed',
-        },
-      },
-      axisLine: {
-        show: false,
-        lineStyle: {
-          color: "#233653"
-        }
-      },
-      axisLabel: {
-        show: true,
-        textStyle: {
-          color: '#1F2328',
-          // align: 'right',
-          padding: 0
-        },
-        formatter: function (value) {
-          if (value === 0) {
-            return value
-          }
-          return value
-        }
-      },
-      axisTick: {
-        show: false,
-      },
-    }],
-    series
-  }
-}
-
-export const areaOptions = {
-  backgroundColor: '#fff',
-  tooltip: {
-    trigger: "axis",
-    backgroundColor: "#202630",
-    borderColor: "#202630",
-    textStyle: {
-      color: "#fff",
-      fontSize: 12,
-    },
-    axisPointer: {
-      type: "line",
-      lineStyle: {
-        type: "solid",
-      },
-    },
-    formatter: function (params) {
-      return params[0].name + 'asda' + ':<br/>' + params[0].value + '万';
-    },
-  },
-
-  // tooltip: {
-  //   trigger: 'axis',
-  //   axisPointer: {
-  //     snap: true,
-  //     type: 'line',
-  //     lineStyle: {
-  //       color: '#1BD5DF', // 线的颜色
-  //       width: 3, // 线宽
-  //       type: 'solid', // 线型
-  //     },
-  //   },
-  //       formatter: function (params) {
-  //     return params[0].name + 'asda' + ':<br/>' + params[0].value + '万';
-  //   },
-  // },
-  grid: {
-    top: '10%',
-    left: 40,
-    right: '5%',
-    bottom: '15%',
-  },
-  xAxis: [{
-    type: 'category',
-    boundaryGap: false,
-    axisLine: {
-      show: true,
-      lineStyle: {
-        color: '#e0e6f1'
-      },
-    },
-    axisLabel: {
-      textStyle: {
-        color: '#1F2328',
-        fontSize: 12,
-      },
-      formatter: function (data) {
-        return data + ":00"
-      }
-    },
-    splitLine: {
-      show: true,
-      lineStyle: {
-        type: 'dashed',
-      },
-    },
-    axisTick: {
-      show: false,
-    },
-    data: xLabel
-  }],
-  yAxis: [{
-    name: '',
-    nameTextStyle: {
-      color: "#91aac4",
-      fontSize: 16,
-      padding: 10
-    },
-    min: 0,
-    splitLine: {
-      show: true,
-      lineStyle: {
-        type: 'dashed',
-      },
-    },
-    axisLine: {
-      show: false,
-      lineStyle: {
-        color: "#233653"
-      }
-
-    },
-    axisLabel: {
-      show: true,
-      textStyle: {
-        color: '#1F2328',
-        padding: 16
-      },
-      formatter: function (value) {
-        if (value === 0) {
-          return value
-        }
-        return value
-      }
-    },
-    axisTick: {
-      show: false,
-    },
-  }],
-  series: [
-    {
-      name: '上学',
-      type: 'line',
-      symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆
-      showAllSymbol: true,
-      symbolSize: 5,
-      smooth: true,
-      lineStyle: {
-        normal: {
-          width: 2,
-          color: "rgba(25,163,223,1)", // 线条颜色
-        },
-        borderColor: 'rgba(0,0,0,.4)',
-      },
-      itemStyle: {
-        color: "#b7f9ff",
-        borderColor: "#2185da",
-        borderWidth: 1,
-        shadowColor: 'rgba(22, 137, 229)',
-        shadowBlur: 1
-      },
-      areaStyle: { //区域填充样式
-        normal: {
-          //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-            offset: 0,
-            color: "rgba(25,163,223,.3)"
-          },
-          {
-            offset: 1,
-            color: "rgba(25,163,223, 0)"
-          }
-          ], false),
-          shadowColor: 'rgba(25,163,223, 0.5)', //阴影颜色
-          shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
-        }
-      },
-      z: 3,
-      data: [9.019096, 17.795139, 6.467014, 5.746528, 10.16493, 10.112847, null, null, null]
-    },
-    {
-      name: '2222222',
-      type: 'line',
-      symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆
-      showAllSymbol: true,
-      symbolSize: 5,
-      smooth: true,
-      lineStyle: {
-        normal: {
-          width: 2,
-          color: "rgba(36,175,83,1)", // 线条颜色
-        },
-        borderColor: 'rgba(0,0,0,.4)',
-      },
-      itemStyle: {
-        color: "#fff",
-        borderColor: "#2ee055",
-        borderWidth: 1,
-        shadowColor: 'rgba(22, 137, 229)',
-        shadowBlur: 1
-      },
-      areaStyle: { //区域填充样式
-        normal: {
-          //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-            offset: 0,
-            color: "rgba(48, 209, 136,.3)"
-          },
-          {
-            offset: 1,
-            color: "rgba(48, 209, 136, 0)"
-          }
-          ], false),
-          shadowColor: 'rgba(25,163,223, 0.5)', //阴影颜色
-          shadowBlur: 20
-        }
-      },
-      z: 2,
-      data: [null, null, null, null, null, 10.112847, 7.75, 6.74, 7.14]
-    }
-  ]
-};
-

+ 8 - 11
src/views/analyse/ForecastView.vue

@@ -2,10 +2,9 @@
 import { onMounted, ref } from 'vue';
 import { useRouter } from 'vue-router';
 import { NTabs, NTab } from 'naive-ui';
-// import * as G2Plot from '@antv/g2plot';
 import * as echarts from 'echarts';
-import { getAreaOptions, areaOptions } from '@/utils/echartOptions'
-// import { Area } from '@antv/g2plot';
+import { getAreaOptions } from './config/echartOptions'
+
 import { useChatStore } from '@/stores/modules/chatStore';
 import { BaseTable, ChatWelcome, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
 import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
@@ -69,6 +68,7 @@ const createLineChat = (data) => {
 
   const seriesPast = replaceArray([...yData], yData.length - 3, 3, null);
   const seriesFuture = replaceArray([...yData], 0, 5, null);
+  console.log( {xAxisData, seriesList: [seriesPast, seriesFuture]} );
   const option = getAreaOptions({xAxisData, seriesList: [seriesPast, seriesFuture]});
   chart.setOption(option);
 }
@@ -86,8 +86,6 @@ onMounted(() => {
 
 <template>
   <section class="flex items-start h-full" id="warning">
-
-
     <TheSubMenu title="预测预警" @scrollToLower="onScrolltolower" :loading="isFetching">
       <template #top>
         <div class="border-[#DAE5ED]">
@@ -98,6 +96,7 @@ onMounted(() => {
         </div>
       </template>
       <div class="px-[12px] py-[14px] text-[#5e5e5e]">
+        <p v-show="!recordList.length" class="pt-[30px] text-[12px] text-[#999] text-center">暂无预警数据</p>
         <div class="grid grid-cols-1 gap-[12px]">
           <RecodeSquareCardItem v-for="item in recordList" :key="item.id" :item="item" @on-click="handleOpenContent" />
         </div>
@@ -136,10 +135,10 @@ onMounted(() => {
           </div>
         </div>
         <div class="echart-warpper">
-          <span>{{ chartTitle }}</span>
-          <div id="chartContainer" class="w-[600px] h-[200px]" ref="echartRef"></div>
-          <ul class="flex justify-between w-[600px] text-center text-[12px] text-[#666] leading-[18px]">
-            <li class="w-[370px] pl-[40px]">
+          <span class="#1A2029 font-bold">{{ chartTitle }}</span>
+          <div id="chartContainer" class="w-[660px] h-[200px]" ref="echartRef"></div>
+          <ul class="flex justify-between w-[660px] text-center text-[12px] text-[#666] leading-[18px]">
+            <li class="w-[400px] pl-[30px]">
               <span class="inline-block w-[10px] h-[2px] mb-[2px] mr-[4px] bg-[#2185da]"></span>
               <span>过去</span>
             </li>
@@ -168,7 +167,5 @@ onMounted(() => {
   width: 100%;
   padding: 50px 0 30px 0;
   border-radius: 8px;
-  // background: #eee;
-  // border: 1px solid #ccc;
 }
 </style>

+ 1 - 0
src/views/analyse/PymolView.vue

@@ -68,6 +68,7 @@ const handleWelcomeRecommend = question => {
       </template>
 
       <div class="px-[12px] py-[14px] text-[#5e5e5e]">
+        <p v-show="!recordList.length" class="pt-[30px] text-[12px] text-[#999] text-center">暂无报警数据</p>
         <div class="grid grid-cols-1 gap-[12px]">
           <RecodeSquareCardItem v-for="item in recordList" :key="item.id" :item="item" @on-click="handleOpenContent" />
         </div>

+ 1 - 0
src/views/analyse/WaterView.vue

@@ -302,6 +302,7 @@ const handleWelcomeRecommend = question => {
       </template>
 
       <div class="px-[12px] py-[14px] text-[#5e5e5e]">
+        <p v-show="!recordList.length" class="pt-[30px] text-[12px] text-[#999] text-center">暂无报警数据</p>
         <div class="grid grid-cols-1 gap-[12px]">
           <RecodeSquareCardItem
             v-for="item in recordList"

+ 261 - 114
src/views/analyse/WorkOrder.vue

@@ -1,31 +1,71 @@
 <script setup>
-import { ref, unref, computed, onUnmounted } from 'vue';
-import { useMessage, NDatePicker, NTabs, NTab, NRadioGroup, NRadio, NCheckboxGroup, NCheckbox } from 'naive-ui';
+import { ref, unref, computed, onMounted, onUnmounted, nextTick } from 'vue';
+import { useMessage, NDatePicker, NTabs, NTab, NRadioGroup, NRadio, NCheckboxGroup, NCheckbox, NDataTable } from 'naive-ui';
 import { BaseButton, RecodeCardItem, TheSubMenu, TheChatView, ChatWelcome, SvgIcon } from '@/components';
 import { ChatAsk, ChatAnswer } from '@/components/Chat';
+import { orderApi } from "@/api/order";
 import { chatApi } from '@/api/chat';
+import { getAreaOptions, getOrderAreaOptions } from './config/echartOptions'
+import * as echarts from 'echarts';
 import dayjs from 'dayjs';
 import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
 import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
 
-
-import {useInfinite, useScroll, useChat, useRecommend} from '@/composables';
+import { useInfinite, useScroll, useChat, useRecommend } from '@/composables';
 
 const ANSWER_ID_KEY = '@@id@@';
 
-dayjs.extend(isSameOrBefore)
-dayjs.extend(isSameOrAfter)
+const arr = []
+for (let i = 0; i < 2; i++) {
+  arr.push({
+    title: '2024-06-' + (i + 1),
+    key: 'no' + i,
+    width: '150px',
+    align: 'center'
+  })
+}
+
+
+const columns = ref(arr)
+console.log(columns);
+const tableData = ref([{ no0: 1, no1: 2 }]);
 
+dayjs.extend(isSameOrBefore);
+dayjs.extend(isSameOrAfter);
+
+let chart = null;
 let controller = new AbortController();
 
 const { recordList, isFetching, onScrolltolower, onReset, addHistoryRecord } = useInfinite('/front/bigModel/qa/pageList', { module: 1 });
 const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
 const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat();
 
+const echartRef = ref([]);
 const message = useMessage();
 const reportDate = ref();
-const startDate = ref();
-const endDate = ref();
+const workOrderParams = ref({
+  timeBegin: null,
+  timeEnd: null,
+  whichWay: 1,
+  checkGroup: [],
+})
+
+/**
+ * 将他们分组,
+ *  1、进水指标:jsGroup  出水指标:csGroup  化验指标:hyGroup
+ *  2、组内的数据项和之前一样的,但是只返回勾选的子项
+ *  3、勾选的项如果没值,字段一定也要返回,可以是null or 0
+ * */
+
+// const mockData = {
+
+//   jsGroup: [{ jsCod: 1, jsNh3: null, time: '2024/06/05', .... }, {}],
+//   csGroup: [],
+//   hyGroup: []
+
+// }
+
+
 const switchActive = ref(false);
 const tabActive = ref("daily");
 const model = ref({})
@@ -51,7 +91,7 @@ const handleCreateDialog = async () => {
   }
 
   currenSessionId.value = null;
-  
+
   recordActive.value = null;
 
   clearChat();
@@ -67,13 +107,13 @@ const handleChatDetail = async ({ sessionId }) => {
 
   const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
 
-  chatDataSource.value = data.map(item => ({ ...item, loading: false,  }));
+  chatDataSource.value = data.map(item => ({ ...item, loading: false, }));
   currenSessionId.value = sessionId;
 
   scrollToBottom();
 }
 
-const onRegenerate = async ({ question, realQuestion }) => {
+const onRegenerate = async ({ question, params1 }) => {
   controller = new AbortController();
 
   const sessionId = unref(currenSessionId);
@@ -81,7 +121,7 @@ const onRegenerate = async ({ question, realQuestion }) => {
     data: {
       sessionId,
       showVal: question,
-      question: realQuestion || question,
+      question,
       module: 1,
       isStrong: Number(unref(switchActive)),
       reportDate: reportDate.value
@@ -90,11 +130,11 @@ const onRegenerate = async ({ question, realQuestion }) => {
     onDownloadProgress: ({ event }) => {
       const xhr = event.target;
       const { responseText } = xhr;
-      const [ answer ] = responseText.split(ANSWER_ID_KEY);
+      const [answer] = responseText.split(ANSWER_ID_KEY);
 
       updateChat({
         sessionId,
-        showVal:question,
+        showVal: question,
         answer,
         loading: true,
         delayLoading: false,
@@ -106,8 +146,8 @@ const onRegenerate = async ({ question, realQuestion }) => {
 
   try {
     const { data } = await chatApi.getChatStream(params);
- 
-    const [ answer, id ] = data.split(ANSWER_ID_KEY);
+
+    const [answer, id] = data.split(ANSWER_ID_KEY);
 
     updateChat({
       id,
@@ -117,10 +157,10 @@ const onRegenerate = async ({ question, realQuestion }) => {
       loading: false,
       delayLoading: false
     })
-    
+
     scrollToBottomIfAtBottom();
   }
-  catch (error){
+  catch (error) {
     console.log("取消了请求 - catch", error);
   }
   finally {
@@ -129,50 +169,117 @@ const onRegenerate = async ({ question, realQuestion }) => {
   }
 }
 // 提交问题
-const handleSubmit = async (question, realQuestion = '') => {
-  // 用于模拟 - 内容生成前置等待状态
-  
-  if (unref(isExistInHistory)) {
-    const { data: sessionId } = await chatApi.getChatSessionTag();
-    currenSessionId.value = sessionId;
-  }
-  
+const handleSubmit = async (question, params) => {
+
+  // if (unref(isExistInHistory)) {
+  //   const { data: sessionId } = await chatApi.getChatSessionTag();
+  //   currenSessionId.value = sessionId;
+  // }
+
   isLoading.value = true;
+    let option = {}
+
+  const isChart = tabActive.value == 'customDaily';
+
+  if (isChart) {
+    // const data = await createLineEchart(params);
+
+    function getRandomInt(min, max) {
+      min = Math.ceil(min);
+      max = Math.floor(max);
+      return Math.floor(Math.random() * (max - min + 1)) + min;
+    }
+    const xAxisData = []
+    const seriesList = []
+
+    for (let i = 0; i < 30; i++) {
+      xAxisData.push('06-' + (i + 1));
+    }
+
+    for (let i = 0; i < 2; i++) {
+      seriesList[i] = [];
+      for (let j = 0; j < 30; j++) {
+        seriesList[i].push(getRandomInt(1, 6))
+      }
+    }
+
+     option = getOrderAreaOptions({
+      xAxisData,
+      seriesList
+    });
+
+  }
 
   addChat({
     sessionId: unref(currenSessionId),
     showVal: question,
-    realQuestion,
     answer: '',
     loading: true,
     delayLoading: true,
+    isChart: true
   })
 
+  console.log( chatDataSource.value );
+
   scrollToBottom();
 
-  setTimeout(() => onRegenerate({ question, realQuestion }), 2 * 1000);
+  setTimeout(() => {
+    updateChat({
+      sessionId: unref(currenSessionId),
+      showVal: question,
+      answer: '',
+      loading: true,
+      delayLoading: false,
+      isChart: true
+    })
+    if(!chart) {
+      const dom = document.getElementById('chartContainer')
+      console.log( echartRef.value );
+      chart = echarts.init(dom, {width: 680, height: 300});
+    }
+    chart.setOption(option);
+    // chart.resize();
+    // onRegenerate({ question }), 2 * 1000
+  }, 2 * 1000);
 }
 
 // 处理推荐问题
-const handleCreateOrder = () => {
-  
-  if ( tabActive.value === 'customDaily' ) {
-    const startDateTime = dayjs(startDate.value);
-    const endDateTime = dayjs(endDate.value);
-    const thirtyDaysBeforeEndTime = endDateTime.subtract(30, 'day');
-    const isStartAfterThirtyDaysBeforeEnd = startDateTime.isSameOrAfter(thirtyDaysBeforeEndTime);
-    if (!isStartAfterThirtyDaysBeforeEnd) {
-      alert("错误的")
-    } else {
-      alert("可以的")
-    }
-  }
-  return
-  if ( !reportDate.value ) {
-    return message.warning('请选择日期');
+const handleCreateOrder = async () => {
+  // const { timeBegin, timeEnd, whichWay, checkGroup } = unref(workOrderParams);
+  // const startDateTime = dayjs(timeBegin);
+  // const endDateTime = dayjs(timeEnd);
+  // const params = {
+  //   timeBegin,
+  //   timeEnd,
+  //   whichWay
+  // };
+
+  let question = `请生成${reportDate.value}智慧工单分析报告`;
+
+  if (tabActive.value === 'customDaily') {
+    //   const thirtyDaysBeforeEndTime = endDateTime.subtract(30, 'day');
+    //   const isStartAfterThirtyDaysBeforeEnd = startDateTime.isSameOrAfter(thirtyDaysBeforeEndTime);
+
+    //   if (!timeBegin) return message.warning('请选择开始日期');
+    //   if (!timeEnd) return message.warning('请选择结束日期');
+    //   if (!isStartAfterThirtyDaysBeforeEnd) return message.warning('只可生成一个月区间的工单报告');
+    //   if (!checkGroup.length) return message.warning('请至少选择一项指标项');
+
+    //   checkGroup.forEach(key => params[key] = true);
+    //   // [checkGroup, csCheckGroup, hyCheckGroup].flat().forEach(key => params[key] = true);
+
+    //   question = `请成生${startDateTime.format("MM月DD日")}-${endDateTime.format("MM月DD日")}的在线仪表的日报工单`
+
+    // } else {
+    //   if (!reportDate.value) return message.warning('请选择时间');
   }
+  handleSubmit(question)
+  // handleSubmit(question, params);
+}
 
-  handleSubmit(`请生成${reportDate.value}智能工单分析报告`);
+// 创建echart
+const createLineEchart = async (params) => {
+  await orderApi.postOrderChart(params);
 }
 
 // 删除历史对话
@@ -188,22 +295,31 @@ const handleTabChange = (val) => {
 }
 
 const dateStartDisabled = (timestamp) => {
-  if (endDate.value) {
+  const { timeEnd } = workOrderParams.value;
+  if (timeEnd) {
     const currentDate = dayjs(timestamp);
-    const endDateValue = dayjs(endDate.value);
+    const endDateValue = dayjs(timeEnd);
     return !currentDate.isSameOrBefore(endDateValue);
   }
-  
 }
 
 const dateEndDisabled = (timestamp) => {
-  if (startDate.value) {
+  const { timeBegin } = workOrderParams.value;
+  if (timeBegin) {
     const currentDate = dayjs(timestamp);
-    const endDateValue = dayjs(startDate.value);
+    const endDateValue = dayjs(timeBegin);
     return !currentDate.isSameOrAfter(endDateValue);
   }
-
 }
+
+
+onMounted(async () => {
+
+  await nextTick();
+  console.log( echartRef.value );
+  // chart = echarts.init(echartRef.value, 'light');
+})
+
 onUnmounted(() => {
   controller.abort();
 })
@@ -220,45 +336,59 @@ 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"
-          :class="{'recode-card-item_active': recordActive === item.sessionId}"
-          @on-click="handleChatDetail"
-          @on-delete="handeChatDelete"
-        />
+        <p v-show="!recordList.length" class="pt-[30px] text-[12px] text-[#999] text-center">暂无工单数据</p>
+        <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智慧工单助手" 
-        :sub-title="[
-          '基于大语言模型的智能工单分析助手,可以为您实现数据分析及数据解读',
-          '选择日期并为您生成日报分析'
-        ]"
-        v-if="!chatDataSource.length"
-      />
-
-      <div class="conversation-item" v-if="chatDataSource.length">
-        <template v-for="item in chatDataSource" :key="item.id">
+      <ChatWelcome title="您好,我是LibraAI智慧工单助手" :sub-title="[
+      '基于大语言模型的智能工单分析助手,可以为您实现数据分析及数据解读',
+      '选择日期并为您生成日报分析'
+    ]" v-if="!chatDataSource.length" />
+  
+
+
+      <div class="conversation-item" v-show="chatDataSource.length">
+        <div v-for="item in chatDataSource" :key="item.id">
           <ChatAsk :content="item.showVal" :sessionId="item.sessionId"></ChatAsk>
-          <ChatAnswer
-            :id="item.id"
-            :content="item.answer"
-            :loading="item.loading"
-            :delay-loading="item.delayLoading"
-            :isSatisfied="item.isSatisfied"
-            @on-click-icon=" params => updateById(params)"
-          ></ChatAnswer>
-        </template>
+          <ChatAnswer :id="item.id" :content="item.answer" :loading="item.loading" :delay-loading="item.delayLoading"
+            :isSatisfied="item.isSatisfied" @on-click-icon="params => updateById(params)" v-if="!item.isChart">
+          </ChatAnswer>
+          <ChatAnswer content="11111111111" :loading="item.loading" :delay-loading="item.delayLoading" >
+            <div v-show="item.isChart && !item.delayLoading">
+            <div class="echart-warpper" >
+              <span class="#1A2029 font-bold">进水指标</span>
+
+              <!-- <ul class="flex justify-end w-[600px] text-center text-[12px] text-[#666] leading-[18px] space-x-[10px]">
+            <li class="">
+              <span class="inline-block w-[10px] h-[2px] mb-[2px] mr-[4px] bg-[#2185da]"></span>
+              <span>过去</span>
+            </li>
+            <li class="">
+              <span class="inline-block w-[10px] h-[2px] mb-[2px] mr-[4px] bg-[#2ee055]"></span>
+              <span>未来</span>
+            </li>
+          </ul> -->
+              <div id="chartContainer" class="w-[680px] h-[300px]" ref="echartRef" style="width: 680px; height: 300px;"></div>
+            </div>
+            <div class="w-[700px]">
+              <NDataTable :bordered="true" :single-line="false" single-column :columns="columns" :data="tableData"
+                scroll="{ x: true }" size="small"></NDataTable>
+            </div>
+            </div>
+          </ChatAnswer>
+        </div>
       </div>
 
+
+
       <div class="order-container px-[60px] py-[30px] mt-[36px] rounded-[10px] bg-[#fff]" v-if="!chatDataSource.length">
-        <div class="flex items-center justify-start space-x-[24px] pb-[20px] border-b-[1px] border-solid border-[#F1F1F1]">
+        <div
+          class="flex items-center justify-start space-x-[24px] pb-[20px] border-b-[1px] border-solid border-[#F1F1F1]">
           <n-tabs type="segment" animated style="width: 188px;" size="large" :on-update:value="handleTabChange">
             <n-tab name="daily">日报工单</n-tab>
             <n-tab name="customDaily">自定义工单</n-tab>
@@ -272,7 +402,8 @@ onUnmounted(() => {
           <div class="flex items-center justify-start text-[16px] space-x-[16px]">
             <span class="font-bold">选择时间</span>
             <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
-              <NDatePicker placeholder="选择日期" :readonly="true" v-model:formatted-value="reportDate" value-format="yyyy-MM-dd">
+              <NDatePicker placeholder="选择日期" :readonly="true" v-model:formatted-value="reportDate"
+                value-format="yyyy-MM-dd">
                 <template #date-icon>
                   <SvgIcon name="tool-arrow-bottom"></SvgIcon>
                 </template>
@@ -292,7 +423,8 @@ onUnmounted(() => {
           <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
             <span class="font-bold">选择时间</span>
             <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
-              <NDatePicker placeholder="开始日期" :readonly="true" v-model:formatted-value="startDate" value-format="yyyy-MM-dd" :is-date-disabled="dateStartDisabled">
+              <NDatePicker placeholder="开始日期" :readonly="true" v-model:formatted-value="workOrderParams.timeBegin"
+                value-format="yyyy-MM-dd" :is-date-disabled="dateStartDisabled">
                 <template #date-icon>
                   <SvgIcon name="tool-arrow-bottom"></SvgIcon>
                 </template>
@@ -300,7 +432,8 @@ onUnmounted(() => {
             </div>
             <span class="text-[16px] text-[#8F959C]">至</span>
             <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
-              <NDatePicker placeholder="结束日期" :readonly="true" v-model:formatted-value="endDate" value-format="yyyy-MM-dd" :is-date-disabled="dateEndDisabled">
+              <NDatePicker placeholder="结束日期" :readonly="true" v-model:formatted-value="workOrderParams.timeEnd"
+                value-format="yyyy-MM-dd" :is-date-disabled="dateEndDisabled">
                 <template #date-icon>
                   <SvgIcon name="tool-arrow-bottom"></SvgIcon>
                 </template>
@@ -310,48 +443,48 @@ onUnmounted(() => {
 
           <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
             <span class="font-bold">统计方式</span>
-            <n-radio-group v-model:value="model.radioGroupValue" name="radiogroup2">
-              <n-radio value="Radio 1">在线仪表</n-radio>
-              <n-radio value="Radio 2">日报</n-radio>
+            <n-radio-group v-model:value="workOrderParams.whichWay" name="radiogroup2">
+              <n-radio :value="1">在线仪表</n-radio>
+              <n-radio :value="0">日报</n-radio>
             </n-radio-group>
           </div>
 
           <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
             <span class="font-bold">进水指标</span>
-            <n-checkbox-group v-model:value="model.radioGroupValue" name="radiogroup2">
-              <n-checkbox value="Radio 1">进水水量</n-checkbox>
-              <n-checkbox value="Radio 2">COD</n-checkbox>
-              <n-checkbox value="Radio 2">总氮</n-checkbox>
-              <n-checkbox value="Radio 2">总磷</n-checkbox>
-              <n-checkbox value="Radio 2">氨氮</n-checkbox>
-              <n-checkbox value="Radio 2">SS</n-checkbox>
+            <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
+              <n-checkbox value="jsSlq">进水水量</n-checkbox>
+              <n-checkbox value="jsCod">COD</n-checkbox>
+              <n-checkbox value="jsTn">总氮</n-checkbox>
+              <n-checkbox value="jsTp">总磷</n-checkbox>
+              <n-checkbox value="jsNh3">氨氮</n-checkbox>
+              <n-checkbox value="jsSs">SS</n-checkbox>
             </n-checkbox-group>
           </div>
-          
+
           <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
             <span class="font-bold">出水指标</span>
-            <n-checkbox-group v-model:value="model.radioGroupValue" name="radiogroup2">
-              <n-checkbox value="Radio 1">出水水量</n-checkbox>
-              <n-checkbox value="Radio 2">COD</n-checkbox>
-              <n-checkbox value="Radio 2">总氮</n-checkbox>
-              <n-checkbox value="Radio 2">总磷</n-checkbox>
-              <n-checkbox value="Radio 2">氨氮</n-checkbox>
-              <n-checkbox value="Radio 2">SS</n-checkbox>
+            <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
+              <n-checkbox value="csSlqc">出水水量</n-checkbox>
+              <n-checkbox value="csCod">COD</n-checkbox>
+              <n-checkbox value="csTn">总氮</n-checkbox>
+              <n-checkbox value="csTp">总磷</n-checkbox>
+              <n-checkbox value="csNh3">氨氮</n-checkbox>
+              <n-checkbox value="csSs">SS</n-checkbox>
             </n-checkbox-group>
           </div>
 
           <div class="flex items-start justify-start mb-[20px] text-[16px] space-x-[16px]">
             <span class="font-bold">化验指标</span>
             <div class="w-[500px]">
-              <n-checkbox-group v-model:value="model.radioGroupValue" name="radiogroup2">
-              <n-checkbox value="Radio 1">1#好氧池硝酸盐</n-checkbox>
-              <n-checkbox value="Radio 2">2#好氧池硝酸盐</n-checkbox>
-              <n-checkbox value="Radio 2">1#缺氧氨氮</n-checkbox>
-              <n-checkbox value="Radio 2">2#缺氧氨氮</n-checkbox>
-              <n-checkbox value="Radio 2">1#缺氧池硝酸盐</n-checkbox>
-              <n-checkbox value="Radio 2">2#缺氧池硝酸盐</n-checkbox>
-              <n-checkbox value="Radio 2">二沉池正磷酸盐</n-checkbox>
-            </n-checkbox-group>
+              <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
+                <n-checkbox value="no3Hlj1Jqr">1#好氧池硝酸盐</n-checkbox>
+                <n-checkbox value="no3Hlj2Jqr">2#好氧池硝酸盐</n-checkbox>
+                <n-checkbox value="nh31Jqr">1#缺氧氨氮</n-checkbox>
+                <n-checkbox value="nh32Jqr">2#缺氧氨氮</n-checkbox>
+                <n-checkbox value="no3Qyc1Jqr">1#缺氧池硝酸盐</n-checkbox>
+                <n-checkbox value="no3Qyc2Jqr">2#缺氧池硝酸盐</n-checkbox>
+                <n-checkbox value="tpRccJqr">二沉池正磷酸盐</n-checkbox>
+              </n-checkbox-group>
             </div>
           </div>
 
@@ -362,11 +495,12 @@ onUnmounted(() => {
           </dl>
         </main>
 
-
         <footer class="pt-[24px]">
           <button class="btn-primary" @click="handleCreateOrder">立即生成</button>
         </footer>
       </div>
+
+
     </TheChatView>
   </section>
 </template>
@@ -412,11 +546,23 @@ onUnmounted(() => {
     background: #1D43CC;
   }
 }
+
+.echart-warpper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-flow: column;
+  width: 100%;
+  padding: 20px 0 30px 0;
+  border-radius: 8px;
+}
 </style>
 
 <style lang="scss">
 .order {
-  .n-input__input-el, .n-input__placeholder {
+
+  .n-input__input-el,
+  .n-input__placeholder {
     font-size: 14px;
   }
 }
@@ -427,6 +573,7 @@ onUnmounted(() => {
     font-weight: bold;
     color: #5E5E5E;
   }
+
   .n-tabs-tab--active {
     .n-tabs-tab__label {
       color: #1A2029;

+ 298 - 0
src/views/analyse/config/echartOptions.js

@@ -0,0 +1,298 @@
+import * as echarts from 'echarts';
+
+export const getAreaOptions = ({ xAxisData, seriesList }) => {
+  const series = seriesList.map((data, index) => {
+    const i = !index;
+    return {
+      name: i ? '过去' : '未来',
+      type: 'line',
+      symbol: 'circle',
+      showAllSymbol: true,
+      symbolSize: 5,
+      smooth: true,
+      lineStyle: {
+        normal: {
+          width: 2,
+          color: i ? "rgba(25,163,223,1)" : "rgba(36,175,83,1)", // 线条颜色
+        },
+        borderColor: 'rgba(0,0,0,.4)',
+      },
+      itemStyle: {
+        color: i ? "#b7f9ff" : "#fff",
+        borderColor: i ? "#2185da" : "#2ee055",
+        borderWidth: 1,
+        shadowColor: 'rgba(22, 137, 229)',
+        shadowBlur: 1
+      },
+      areaStyle: {
+        normal: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
+            offset: 0,
+            color: i ? "rgba(25,163,223,.3)" : "rgba(48, 209, 136,.3)"
+          },
+          {
+            offset: 1,
+            color: i ? "rgba(25,163,223, 0)" : "rgba(48, 209, 136, 0)"
+          }
+          ], false),
+          shadowColor: 'rgba(25,163,223, 0.5)',
+          shadowBlur: 20
+        }
+      },
+      z: i ? 3 : 2,
+      data
+    }
+  })
+
+  return {
+    backgroundColor: '#fff',
+    tooltip: {
+      trigger: "axis",
+      show: true,
+      formatter: function (params) {
+        const [item] = params.filter(item => item.value);
+        const color = item.dataIndex <= 5 ? '#2185da' : '#2ee055'
+        return `
+          <div class="text-[12px]">
+            <p>时间:${item.name + ':00'}</p>
+            <p class="flex items-center space-x-[6px]">
+              <span class="block w-[6px] h-[6px] rounded-full bg-[${color}]"></span>
+              <span>${Number(item.value.toFixed(2))} mg/L</span>
+            </p>
+          </div>
+          `
+      },
+    },
+    grid: {
+      top: '10',
+      left: 30,
+      right: '5%',
+      bottom: '15%',
+    },
+    xAxis: [{
+      type: 'category',
+      boundaryGap: false,
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '#e0e6f1'
+        },
+      },
+      axisLabel: {
+        textStyle: {
+          color: '#1F2328',
+          fontSize: 12,
+        },
+        formatter: function (data) {
+          return data + ":00"
+        }
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      data: xAxisData
+    }],
+    yAxis: [{
+      name: '',
+      nameTextStyle: {
+        fontSize: 12,
+        align: 'right',
+        padding: [10, 5],
+      },
+      min: 0,
+      splitLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+        },
+      },
+      axisLine: {
+        show: false,
+        lineStyle: {
+          color: "#233653"
+        }
+      },
+      axisLabel: {
+        show: true,
+        textStyle: {
+          color: '#1F2328',
+          // align: 'right',
+          padding: 0
+        },
+        formatter: function (value) {
+          if (value === 0) {
+            return value
+          }
+          return value
+        }
+      },
+      axisTick: {
+        show: false,
+      },
+    }],
+    series
+  }
+}
+
+export const getOrderAreaOptions = ({ xAxisData, seriesList }) => {
+
+  const color = [
+    '#0FFEFF',
+    '#FFCA06',
+    '#3DD784',
+    '#3D80FF',
+    '#D35AE8',
+    '#EC2E72',
+    '#FF5406',
+    '#3D3AEE',
+    '#008F07',
+    '#FF8B8B',
+  ];
+
+  const series = [];
+
+  seriesList.map((item, index) => {
+    series.push({
+      name: index == 0 ? "COD" : "FOB",
+      type: 'line',
+      smooth: true,
+      showSymbol: false,
+      yAxisIndex: 0,
+      itemStyle: {
+        opacity: 0.8,
+        color: color[index],
+      },
+      tooltip: {
+        valueFormatter: function (value) {
+          return value + '元';
+        },
+      },
+      data: item,
+    });
+  });
+
+  return {
+    series,
+
+    // label: {
+    //   show: true,
+    //   position: 'top',
+    //   color: '#a0cbff',
+    //   formatter: (e) => {
+    //     return e.value + '元';
+    //   },
+    // },
+    tooltip: {
+      trigger: 'axis',
+      // axisPointer: {
+      //   snap: true,
+      //   type: 'line',
+      //   lineStyle: {
+      //     color: '#1BD5DF', // 线的颜色
+      //     width: 3, // 线宽
+      //     type: 'solid', // 线型
+      //   },
+      // },
+    },
+    backgroundColor: '#fff',
+    grid: {
+
+      top: 30,
+      left: 30,
+      right: 10,
+      bottom: 30,
+      // x: 40,
+      // y: 10,
+      // x2: 40,
+      // y2: 30,
+      // borderWidth: 1,
+    },
+    legend: {
+      orient: 'horizontal',
+      icon: 'rect',
+      itemWidth: 10,
+      itemHeight: 2,
+      formatter: ['{a|{name}}'].join('\n'),
+      textStyle: {
+        rich: {
+          a: {
+            width: 20,
+            color: '#666',
+            fontSize: 12,
+            lineHeight: 18,
+          },
+        },
+      },
+      right: '4',
+          top: '0',
+    },
+    xAxis: [{
+      type: "category",
+      axisLabel: {
+        color: "#666",
+        fontSize: 12,
+         
+        // interval: 6,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: "rgba(127, 214, 255, .4)",
+        },
+      },
+      splitLine: {
+        show: false,
+        lineStyle: {
+          type: 'dashed',
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      boundaryGap: false,
+      data: xAxisData
+
+
+    }],
+    yAxis: [{
+      name: '',
+      nameTextStyle: {
+        fontSize: 12,
+        align: 'right',
+        padding: [10, 5],
+      },
+      min: 0,
+      splitLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+        },
+      },
+      axisLine: {
+        show: false,
+        lineStyle: {
+          color: "#233653"
+        }
+      },
+      axisLabel: {
+        show: true,
+        textStyle: {
+          color: '#666',
+          padding: 0
+        },
+        formatter: function (value) {
+          return value
+        }
+      },
+      axisTick: {
+        show: false,
+      },
+    }],
+  }
+}

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

@@ -285,4 +285,4 @@ onUnmounted(() => {
     }
   }
 }
-</style>
+</style>@/api/order