Browse Source

feat: 智慧总控改版

sunxiao 3 months ago
parent
commit
ef3b58d31f

+ 2 - 2
.env.development

@@ -1,5 +1,5 @@
 # 请求地址
-VITE_BASE_URL=http://192.168.40.18:8080
-# VITE_BASE_URL=http://192.168.9.54:8080
+# VITE_BASE_URL=http://chat.sequoialibra.com:81/apiServe/
+VITE_BASE_URL=http://192.168.40.21:8080/
 # 请求前缀
 VITE_BASE_PREFIX='' 

+ 10 - 1
src/api/screen.js

@@ -41,5 +41,14 @@ export const screenApi = {
     */
   getFee: () => http.get('/front/bigModel/daily/recentlyOneFeeInfo'),
 
- 
+  /**
+    * 水质数据 - 近7天的进出水
+    */
+  getIndustryDataByDays: (day = 7) => http.get('/front/getIndustryDataByDays?days=' + day),
+  
+  /**
+    * 联系检测数据 - 近7天
+    */
+  getContinueDataByDays: (day = 7) => http.get('/front/getRobotDataByDays?days=' + day),
+  
 }

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


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


BIN
src/assets/images/workbench/bg-square-line.png


+ 3 - 0
src/assets/images/workbench/bg-square-line.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="483" height="64" viewBox="0 0 483 64" fill="none">
+<path opacity="0.4" d="M1 1H482M1 1V63M1 1L5 5.07647M482 1C482 1 482 33.3202 482 63M482 1L478 5M482 63C275.803 63 1 63 1 63M482 63C480.438 61.4379 478 59 478 59M1 63L5 59M5 5.07647L478 5M5 5.07647V59M478 5V59M478 59H5" stroke="#00A0E9" stroke-width="0.4"/>
+</svg>

+ 2 - 2
src/components/Layout/TheArchival.vue

@@ -5,7 +5,7 @@ import { NPopover } from 'naive-ui';
 </script>
 
 <template >
-  <div class="flex space-x-[20px] text-[12px] text-[#9E9E9E]">
+  <div class="flex space-x-[20px] text-[12px] text-[#1a2029e5]">
     <div>
       以上内容均由LibraAI生成,仅供参考 
     </div>
@@ -18,7 +18,7 @@ import { NPopover } from 'naive-ui';
       <ul class="text-[#606266]">
         <li>
           <span class="inline-block w-[60px]">网信算备</span>
-          <span>申请中</span>
+          <span>110106889381501240013号</span>
         </li>
         <li>
           <span class="inline-block w-[60px]">京ICP备</span>

+ 5 - 0
src/utils/format.js

@@ -84,4 +84,9 @@ export const getFormatYesterDay = ( data ) => {
     }
     return item;
   })
+}
+
+// 格式化小数
+export const formatDecimals = ( num, digits = 2 ) => {
+  return !(typeof num !== 'number' || isNaN(num)) ? Number(Number(num).toFixed(digits)) : ''
 }

+ 37 - 26
src/views/screen/ScreenView.vue

@@ -1,6 +1,12 @@
 <script setup>
 import { ref, onBeforeUnmount, onMounted } from "vue";
+import autofit from 'autofit.js'
+
 import { userTop } from '@/components';
+import WaterYield from "./components/WaterYield";
+import WaterQuality from "./components/WaterQuality";
+import ContinueData from "./components/ContinueData";
+
 import shuizhi from "./components/shuizhi";
 import dataBox from "./components/dataBox";
 import liuliang from "./components/liuliang";
@@ -40,6 +46,7 @@ const getLeastShortReport = () => {
 // 获取大屏分析数据
 const getRealTimeData = () => {
   screenApi.realTimeData().then(res => {
+    console.log(res.data);
     screenData.value = res.data
     dataTime.value = `更新时间:${res.data.testHour}`
     time.value = dayjs(res.data.timestamp).format('HH:mm:ss')
@@ -65,16 +72,23 @@ const init = () => {
   getLeastShortReport()
 }
 
-init()
 
 function changeSize () {
   // 基于设计稿 1920px 宽度的计算
   document.documentElement.style.fontSize = (document.documentElement.clientWidth * 10) / 1920 + 'px';
 }
 
+
 changeSize();
+init()
 
 onMounted(() => {
+  // autofit.init({
+  //   dw: 1920,
+  //   dh: 1080,
+  //   el:"#screen-view",
+  //   resize: true,
+  // })
 
   window.addEventListener('resize', changeSize, false);
 
@@ -96,12 +110,12 @@ onBeforeUnmount(() => {
 <template>
   <div class="screen-view" id="screen-view">
     <div class="screen-view-top">
-      <div class="left space-x-[1rem]">
+      <div class="left">
         <span class="time">{{ time }}</span>
         <span class="line"></span>
-        <div class="data">
+        <div class="date">
           <div>{{ dayjs(screenData.timestamp).format("dddd") }}</div>
-          <div class="text-[12px]">{{ dayjs(screenData.timestamp).format('YYYY-MM-DD') }}</div>
+          <div class="text-[1.2rem]">{{ dayjs(screenData.timestamp).format('YYYY-MM-DD') }}</div>
         </div>
       </div>
       <div class="chat-header flex items-center justify-end pr-[18px] space-x-[16px]">
@@ -117,15 +131,18 @@ onBeforeUnmount(() => {
     <div class="screen-container">
       <div class="screen-container-main">
         <div class="left">
-          <shuizhi :screenData="screenData"></shuizhi>
+          <WaterYield :screenData="screenData"></WaterYield>
+          <WaterQuality :screenData="screenData"></WaterQuality>
+          <!-- <shuizhi :screenData="screenData"></shuizhi>
           <EchartBar :screenData="screenData"></EchartBar>
-          <liuliang :screenData="screenData"></liuliang>
+          <liuliang :screenData="screenData"></liuliang> -->
         </div>
         <div class="middle">
           <middleBox :dataTime="dataTime"></middleBox>
         </div>
         <div class="right">
-          <ControlHelper></ControlHelper>
+          <ContinueData :screenData="screenData"></ContinueData>
+          <!-- <ControlHelper></ControlHelper> -->
           <dataBox :reportData="reportData"></dataBox>
           <gongyi :gongyiData="gongyiData"></gongyi>
         </div>
@@ -139,19 +156,23 @@ onBeforeUnmount(() => {
   position: relative;
   display: flex;
   justify-content: space-around;
+  // justify-content: flex-start;
   flex-flow: column;
   width: 100vw;
   height: 100vh;
+  min-width: 1200px;
+  min-height: 700px;
   background: url(@/assets/images/home/home_bg.png) center center no-repeat;
   background-size: 100% 100%;
   overflow: hidden;
 
-  &-top {
+  .screen-view-top {
+    flex-shrink: 0;
     display: flex;
     align-items: end;
     justify-content: space-between;
-    padding: 0 5rem;
     height: 9rem;
+    padding: 0 5rem;
 
     .left {
       display: flex;
@@ -160,29 +181,32 @@ onBeforeUnmount(() => {
     }
 
     .time {
+      width: 14rem;
       font-size: 3.6rem;
       font-weight: bold;
       color: #333;
     }
 
     .line {
-      margin: 0 1rem 0 1.5rem;
+      margin: 0 1rem 0 1rem;
       display: inline-block;
       background: #767C82;
       height: 2.8rem;
       width: 0.2rem;
     }
 
-    .data {
+    .date {
+      text-align: center;
       color: #415B73;
     }
   }
 
   .menu {
     display: flex;
-    align-content: center;
+    align-items: center;
     justify-content: center;
     flex-shrink: 0;
+    height: 6.8rem;
 
     .item {
       width: 21.8rem;
@@ -226,27 +250,15 @@ onBeforeUnmount(() => {
         background-size: 100% 100%;
       }
     }
-
-  }
-
-  .water-work-inner {
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
-    width: 150rem;
-    height: 80rem;
-    // background: url("@assets/images/home/water-work.png") no-repeat;
-    background: red;
   }
 
   .screen-container {
     position: relative;
     display: flex;
     flex-flow: column;
-    height: 80rem;
     padding: 0 6rem;
     z-index: 2;
+    // height: 80rem;
 
     &-main {
       position: relative;
@@ -288,7 +300,6 @@ onBeforeUnmount(() => {
 <style lang="scss">
 .home-box {
   width: 54rem;
-  // width: 540px;
   border-radius: 0.8rem;
   // border: 1px solid #fff;
   // margin-bottom: 2rem;

+ 332 - 0
src/views/screen/ScreenView1.vue

@@ -0,0 +1,332 @@
+<script setup>
+import { ref, onBeforeUnmount, onMounted } from "vue";
+import { userTop } from '@/components';
+import shuizhi from "./components/shuizhi";
+import dataBox from "./components/dataBox";
+import liuliang from "./components/liuliang";
+import gongyi from "./components/gongyi";
+import middleBox from "./components/middleBox";
+import EchartBar from "./components/EchartBar";
+import ControlHelper from "./components/ControlHelper";
+import { screenApi } from "@/api/screen"
+import dayjs from 'dayjs';
+import 'dayjs/locale/zh-cn';
+
+dayjs.locale('zh-cn'); // 设置day.js使用中文/ 导入中文语言包
+
+const gongyiData = ref([])
+const reportData = ref({})
+const screenData = ref({})
+const time = ref('')
+let timer = null
+let timeHour = null
+
+const dataTime = ref("");
+
+// 获取工艺管控 助手
+const getWarningList = () => {
+  screenApi.warningList().then(res => {
+    gongyiData.value = res.data || []
+  })
+}
+
+// 获取数据费助手
+const getLeastShortReport = () => {
+  screenApi.getLeastShortReport().then(res => {
+    reportData.value = res.data
+  })
+}
+
+// 获取大屏分析数据
+const getRealTimeData = () => {
+  screenApi.realTimeData().then(res => {
+    screenData.value = res.data
+    dataTime.value = `更新时间:${res.data.testHour}`
+    time.value = dayjs(res.data.timestamp).format('HH:mm:ss')
+    timeHour = setInterval(() => {
+      time.value = updateCurrentTime(res.data.timestamp)
+    }, 1000)
+  })
+}
+
+const updateCurrentTime = (timestamp) => {
+  // 计算从初始时间戳到现在经过的时间
+  const now = dayjs();
+  const initialTime = dayjs(timestamp);
+  const elapsedTime = now.diff(initialTime);
+
+  // 使用初始时间戳加上经过的时间来更新当前时间
+  const currentTime = initialTime.add(elapsedTime, 'millisecond');
+  return currentTime.format('HH:mm:ss');
+}
+const init = () => {
+  getRealTimeData()
+  getWarningList()
+  getLeastShortReport()
+}
+
+init()
+
+function changeSize () {
+  // 基于设计稿 1920px 宽度的计算
+  document.documentElement.style.fontSize = (document.documentElement.clientWidth * 10) / 1920 + 'px';
+}
+
+changeSize();
+
+onMounted(() => {
+
+  window.addEventListener('resize', changeSize, false);
+
+  //每5s刷新数据
+  timer = setInterval(() => {
+    init()
+  }, 1000 * 60 * 60)
+})
+
+onBeforeUnmount(() => {
+  document.documentElement.style.fontSize = '';
+  clearInterval(timer)
+  clearInterval(timeHour)
+  timer = null;
+  timeHour = null
+})
+
+</script>
+<template>
+  <div class="screen-view" id="screen-view">
+    <div class="screen-view-top">
+      <div class="left space-x-[1rem]">
+        <span class="time">{{ time }}</span>
+        <span class="line"></span>
+        <div class="data">
+          <div>{{ dayjs(screenData.timestamp).format("dddd") }}</div>
+          <div class="text-[12px]">{{ dayjs(screenData.timestamp).format('YYYY-MM-DD') }}</div>
+        </div>
+      </div>
+      <div class="chat-header flex items-center justify-end pr-[18px] space-x-[16px]">
+        <userTop></userTop>
+      </div>
+    </div>
+    <div class="menu">
+      <RouterLink to="/" class="item item1">智慧总控</RouterLink>
+      <RouterLink to="/answer" class="item item2">专家问答</RouterLink>
+      <RouterLink to="/water-warn" class="item item3">工艺管控</RouterLink>
+      <RouterLink to="/work" class="item item4">智能办公</RouterLink>
+    </div>
+    <div class="screen-container">
+      <div class="screen-container-main">
+        <div class="left">
+          <shuizhi :screenData="screenData"></shuizhi>
+          <EchartBar :screenData="screenData"></EchartBar>
+          <liuliang :screenData="screenData"></liuliang>
+        </div>
+        <div class="middle">
+          <middleBox :dataTime="dataTime"></middleBox>
+        </div>
+        <div class="right">
+          <ControlHelper></ControlHelper>
+          <dataBox :reportData="reportData"></dataBox>
+          <gongyi :gongyiData="gongyiData"></gongyi>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.screen-view {
+  position: relative;
+  display: flex;
+  justify-content: space-around;
+  flex-flow: column;
+  width: 100vw;
+  height: 100vh;
+  background: url(@/assets/images/home/home_bg.png) center center no-repeat;
+  background-size: 100% 100%;
+  overflow: hidden;
+
+  &-top {
+    display: flex;
+    align-items: end;
+    justify-content: space-between;
+    padding: 0 5rem;
+    height: 9rem;
+
+    .left {
+      display: flex;
+      align-items: center;
+      font-size: 1.6rem;
+    }
+
+    .time {
+      font-size: 3.6rem;
+      font-weight: bold;
+      color: #333;
+    }
+
+    .line {
+      margin: 0 1rem 0 1.5rem;
+      display: inline-block;
+      background: #767C82;
+      height: 2.8rem;
+      width: 0.2rem;
+    }
+
+    .data {
+      color: #415B73;
+    }
+  }
+
+  .menu {
+    display: flex;
+    align-content: center;
+    justify-content: center;
+    flex-shrink: 0;
+
+    .item {
+      width: 21.8rem;
+      height: 5.2rem;
+      display: inline-block;
+      font-size: 0;
+    }
+
+    .item1 {
+      background: url('@/assets/images/home/menu_1_hover.png') no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .item2 {
+      background: url('@/assets/images/home/menu_2.png') no-repeat;
+      background-size: 100% 100%;
+      margin-right: 20rem;
+
+      &:hover {
+        background: url('@/assets/images/home/menu_2_hover.png') no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+
+    .item3 {
+      background: url('@/assets/images/home/menu_3.png') no-repeat;
+      background-size: 100% 100%;
+
+      &:hover {
+        background: url('@/assets/images/home/menu_3_hover.png') no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+
+    .item4 {
+      background: url('@/assets/images/home/menu_4.png') no-repeat;
+      background-size: 100% 100%;
+
+      &:hover {
+        background: url('@/assets/images/home/menu_4_hover.png') no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+
+  }
+
+  .water-work-inner {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 150rem;
+    height: 80rem;
+    // background: url("@assets/images/home/water-work.png") no-repeat;
+    background: red;
+  }
+
+  .screen-container {
+    position: relative;
+    display: flex;
+    flex-flow: column;
+    height: 80rem;
+    padding: 0 6rem;
+    z-index: 2;
+
+    &-main {
+      position: relative;
+      height: 100%;
+      display: flex;
+      justify-content: space-between;
+
+      .left {
+        display: flex;
+        flex-flow: column;
+      }
+
+      .middle {
+        position: relative;
+        flex: 1;
+      }
+    }
+  }
+
+  .img-card {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    width: 79rem;
+    height: 59rem;
+  }
+}
+
+@media screen and (max-width: 1440px) {
+  .screen-view {
+    justify-content: flex-start;
+    // background: url(@/assets/images/home/home_bg.png) center top no-repeat;
+    // background-size: contain;
+  }
+}
+</style>
+
+<style lang="scss">
+.home-box {
+  width: 54rem;
+  // width: 540px;
+  border-radius: 0.8rem;
+  // border: 1px solid #fff;
+  // margin-bottom: 2rem;
+
+  &-top {
+    padding: 0.7rem 2.4rem 0.7rem 1.2rem;
+    display: flex;
+    justify-content: space-between;
+    position: relative;
+    align-items: center;
+
+    &::after {
+      content: '';
+      height: 0.2rem;
+      background: url('@/assets/images/home/line.png') no-repeat;
+      background-size: 100% 100%;
+      width: 100%;
+      position: absolute;
+      left: 1.2rem;
+      bottom: 0;
+    }
+
+    .title {
+      font-size: 1.8rem;
+      font-weight: bold;
+      display: flex;
+      align-items: center;
+
+      &::before {
+        content: '';
+        width: 2rem;
+        height: 2rem;
+        background: url('@/assets/images/home/mark.png') no-repeat;
+        background-size: cover;
+        margin-right: 0.8rem;
+        display: inline-block;
+      }
+    }
+  }
+}
+</style>

+ 132 - 0
src/views/screen/components-back/ControlHelper.vue

@@ -0,0 +1,132 @@
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import { screenApi } from '@/api/screen';
+import * as echarts from 'echarts';
+import dayjs from 'dayjs';
+import "echarts-gl";
+import { getPieOptions } from '../config/echartOption';
+
+let echart = null;
+
+const echartRef = ref(null);
+const legendData = ref([]);
+const dataSource = ref({});
+
+const windowResize = () => echart.resize();
+
+onMounted(async () => {
+
+  const res = await screenApi.getFee();
+  const { DF, XNJF, CLF, TYF, QTYJF, testDate } = res.data;
+  const data = [
+    { name: '电费', value: DF, color: "#00A0E9"},
+    { name: '絮凝剂费', value: XNJF, color: "#00AB84"},
+    { name: '除磷费', value: CLF, color: "#C6A3FF"},
+    { name: '碳源费', value: TYF, color: "#E9D0A1"},
+    { name:'其他药剂费', value: QTYJF, color: "#45D3F2"}
+  ]
+
+  legendData.value = data
+
+  dataSource.value = {
+    ...res.data,
+    dateTime: dayjs(testDate).format('YYYY-MM-DD')
+  }
+
+  echart = echarts.init(echartRef.value, 'light');
+
+  echart.setOption( getPieOptions(data) );
+
+  window.addEventListener("resize", windowResize);
+})
+
+onUnmounted(() => {
+  window.removeEventListener("resize", windowResize);
+  echart && echart.dispose();
+})
+
+</script>
+
+<template>
+  <div class="home-box">
+    <div class="home-box-top">
+      <div class="title">成本管控助手</div>
+      <div>
+        <span class="text-[#1A2029] text-[1.4rem]">{{ dataSource.dateTime }}</span>
+      </div>
+    </div>
+    <div class="main">
+      <ul class="flex justify-between items-center">
+        <li class="text-[1.4rem] font-bold text-[#1A2029]">成本指标</li>
+        <li class="text-[1.2rem] text-[#415B73] space-x-[2rem]">
+          <span>直接成本:¥{{ dataSource.ZJCB }}元/天</span>
+          <span>单位成本:¥{{dataSource.DWCB}}元/千吨</span>
+        </li>
+      </ul>
+      <div class="echart_inner">
+        <div class="cost-chart flex items-center">
+          <div id="pieEchart" class="w-[16rem] h-[16rem]" ref="echartRef"></div>
+        </div>
+        <ul class="cost-num">
+          <li class="cost-item space-y-[0.4rem]" v-for="item in legendData" :key="item.value">
+            <p class="flex items-center space-x-[0.5rem]">
+              <span class="square-icon" :style="{borderColor: item.color}"><i :style="{background: item.color}"></i></span>
+              <span>{{ item.name }}</span>
+            </p>
+            <span>{{ item.value }} 元</span>
+          </li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.main {
+  padding: 1.8rem 2.5rem 1.6rem 4rem;
+
+  .echart_inner {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: 12rem;
+
+    .cost-chart {
+      width: 19rem;
+      height: 12rem;
+      display: flex;
+      justify-content: center;
+      margin-right: 3rem;
+      background: url("@/assets/images/home/bg-helper-round.png") no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .cost-num {
+      flex: 1;
+      font-size: 1.2rem;
+      color: #415B73;
+      
+      .cost-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .square-icon {
+          display: inline-block;
+          padding: 0.1rem;
+          border-width: 0.2rem;
+          border-style: solid;
+          background: #fff;
+
+          i {
+            display: block;
+            width: 0.8rem;
+            height: 0.8rem;
+          }
+        }
+      }
+    }
+  }
+
+}
+</style>

+ 99 - 0
src/views/screen/components-back/EchartBar.vue

@@ -0,0 +1,99 @@
+<script setup>
+import { ref, watch, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import { getBarOptions } from '../config/echartOption';
+
+let chart = null;
+const echartRef = ref(null);
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  }
+})
+
+watch(() => props.screenData, (currentVal) => {
+  const { no3Hlj1Jqr, no3Hlj2Jqr, nh31Jqr, nh32Jqr, tpRccJqr } = currentVal;
+  const options = [ no3Hlj1Jqr, no3Hlj2Jqr, nh31Jqr, nh32Jqr, tpRccJqr ].map(item => {
+    return item ? Number(item.toFixed(2)) : 0;
+  })
+  chart.setOption( getBarOptions(options) )
+})
+
+const windowResize = () => {
+  chart.resize();
+}
+
+onMounted(() => {
+  chart = echarts.init(echartRef.value, 'light');
+
+  window.addEventListener("resize", windowResize);
+})
+
+onUnmounted(() => {
+  window.removeEventListener("resize", windowResize);
+})
+</script>
+
+<template>
+  <div class="home-box echart-box">
+    <div class="home-box-top">
+      <div class="title">连续检测</div>
+    </div>
+    <div class="main">
+      <ul class="legeng_inner space-x-[3rem]">
+        <li class="item space-x-[0.6rem]">
+          <span class="square-icon border-[#FF6737]"><i class="bg-[#FF6737]"></i></span>
+          <span>当前值</span>
+        </li>
+        <li class="item space-x-[0.6rem]">
+          <span class="square-icon border-[#00AB84]"><i class="bg-[#00AB84]"></i></span>
+          <span>范围值</span>
+        </li>
+      </ul>
+      <div class="echart_inner">
+        <div class="echart w-full h-full" ref="echartRef"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.echart-box {
+  min-height: 30rem;
+  .echart_inner {
+    height: calc(100% - 5.6rem);
+  }
+}
+.main {
+  height: 100%;
+  padding: 1.8rem 1.8rem 1.6rem;
+
+  .legeng_inner {
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    color: #415B73;
+    .item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 1.2rem;
+      .square-icon {
+        display: inline-block;
+        padding: 0.1rem;
+        border-width: 0.2rem;
+        border-style: solid;
+        background: #fff;
+        i {
+          display: block;
+          width: 0.8rem;
+          height: 0.8rem;
+        }
+      }
+    }
+  }
+
+}
+</style>

+ 134 - 0
src/views/screen/components-back/cicle.vue

@@ -0,0 +1,134 @@
+<template>
+  <div :class='["gauge", errorStatus ? "warning-wrap" : ""]'>
+    <div class="gauge__body"></div>
+    <svg id="progress-bar" width="100%" height="100%" viewBox="0 0 200 120" preserveAspectRatio="xMidYMid meet">
+      <defs>
+        <linearGradient :id="`grad1-${errorStatus}`" x1="0%" y1="0%" x2="100%" y2="0%">
+          <stop offset="0%" :style="`stop-color: ${errorStatus ? '#FF6737' : '#00BAF5'}; stop-opacity: 1`" />
+          <stop offset="100%" :style="`stop-color: ${errorStatus ? '#F5BF00' : '#02E1A2'}; stop-opacity: 1`" />
+        </linearGradient>
+      </defs>
+      <path d="M 20,100 A 80,80 0 0,1 180,100" stroke="#00557f" stroke-width="16" fill="none" stroke-linecap="round" />
+      <path id="progress" d="M 20,100 A 80,80 0 0,1 180,100"
+        :stroke="`${progressValue > 0 ? `url(#grad1-${errorStatus})` : '#00557f'}`" stroke-width="16" fill="none"
+        :stroke-dasharray="strokeDashArray" stroke-linecap="round" />
+    </svg>
+    <span class="img" :style="{ transform: `translate(-50%, 50%) rotate(${angle}deg)` }"></span>
+    <div class="text">{{ valueNumber }}</div>
+  </div>
+</template>
+
+<script setup>
+import { computed } from "vue";
+
+const props = defineProps({
+  progress: {
+    type: Number,
+    default: 0,
+  },
+  value: {
+    type: Number,
+    default: 0,
+  },
+  levelVal: {
+    type: Number,
+    default: 0,
+  },
+});
+
+const angle = computed(() => {
+  if (!props.value) {
+    return 0;
+  }
+  return props.value / props.levelVal > 1 ? 170 : parseInt((props.value / props.levelVal) * 180);
+});
+
+const strokeDashArray = computed(() => {
+  const radius = 80;
+  const circumference = Math.PI * radius;
+  const offset = circumference * (1 - progressValue.value / 100);
+  return `${circumference - offset}, ${circumference}`;
+});
+
+const valueNumber = computed(() => {
+  return props.value ? Number(props.value).toFixed(2) : 0;
+});
+
+const progressValue = computed(() => {
+  return props.value / props.levelVal > 1 ? 100 : parseInt((props.value / props.levelVal) * 100);
+});
+
+const errorStatus = computed(() => {
+  return parseInt((props.value / props.levelVal) * 100) >= 100;
+});
+</script>
+
+<style scoped lang="scss">
+.gauge {
+  width: 10.8rem;
+  height: 5.4rem;
+  overflow: hidden;
+  position: relative;
+  z-index: 2;
+
+  .gauge__body {
+    width: 9rem;
+    height: 9rem;
+    border-radius: 50%;
+    border: 0.1rem solid rgba(2, 225, 162, 1);
+    padding: 0.3rem;
+    position: absolute;
+    z-index: -1;
+    margin-left: 0.9rem;
+    margin-top: 0.4rem;
+  }
+
+  &.warning-wrap {
+    .gauge__body {
+      border: 0.1rem solid #FF9237;
+    }
+
+    .text {
+      background: linear-gradient(-180.71deg, #FF9237 30.93%, rgba(50, 128, 255, 0) 80%);
+    }
+
+    .img {
+      background: url('@/assets/images/home/waring_arrow.png') no-repeat;
+      background-size: cover;
+    }
+  }
+
+  #progress-bar {
+    margin-top: 0.5rem;
+  }
+
+  .text {
+    width: 4.5rem;
+    height: 4.5rem;
+    border-radius: 50%;
+    background: linear-gradient(-180.71deg, #02E1A2 8.93%, rgba(50, 128, 255, 0.8) 60%);
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    transform: translate(-50%, 50%);
+    text-align: center;
+    font-size: 1.2rem;
+    color: #fff;
+    font-weight: bold;
+    padding-top: 0.4rem;
+    z-index: 4;
+  }
+
+  .img {
+    width: 6.4rem;
+    height: 4.5rem;
+    background: url('@/assets/images/home/arrow.png') no-repeat;
+    background-size: cover;
+    position: absolute;
+    left: 50%;
+    bottom: 0.4rem;
+    transform-origin: center center;
+    z-index: -1;
+  }
+}
+</style>

+ 67 - 0
src/views/screen/components-back/csBox.vue

@@ -0,0 +1,67 @@
+<template>
+  <ul class="box-wrap">
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.cscodBzz" :value="screenData.csCod"></cicle>
+      </div>
+      <div class="name">COD</div>
+      <div class="value">标准值:&lt;{{ screenData.cscodBzz || 0 }}mg/L</div>
+    </li>
+
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.csadBzz" :value="screenData.csNh3"></cicle>
+      </div>
+      <div class="name">NH3-N</div>
+      <div class="value">标准值:&lt;{{ screenData.csadBzz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.cszzBzz" :value="screenData.csTn"></cicle>
+      </div>
+      <div class="name">TN</div>
+      <div class="value">标准值:&lt;{{ screenData.cszzBzz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.cszlBzz" :value="screenData.csTp"></cicle>
+      </div>
+      <div class="name">TP</div>
+      <div class="value">标准值:&lt;{{ screenData.cszlBzz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.csssBzz" :value="screenData.csSs"></cicle>
+      </div>
+      <div class="name">SS</div>
+      <div class="value">标准值:&lt;{{ screenData.csssBzz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="9" :value="screenData.csPh"></cicle>
+      </div>
+      <div class="name">PH值</div>
+      <div class="value">标准值:6~9</div>
+    </li>
+    <!-- <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="35" :value="screenData.csSw"></cicle>
+      </div>
+      <div class="name">温度</div>
+      <div class="value">标准值:15-35℃</div>
+    </li> -->
+  </ul>
+</template>
+
+<script setup>
+import cicle from "./cicle.vue";
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    required: true,
+    default: 0,
+  }
+})
+
+</script>

+ 108 - 0
src/views/screen/components-back/dataBox.vue

@@ -0,0 +1,108 @@
+<script setup>
+import ChatText from '@/components/Chat/ChatText.vue';
+defineProps({
+  reportData: {
+    type: Object,
+    default: {}
+  },
+})
+const content = '① 因房地产市场并不十分活跃和顺利运转,因此难以得到可靠的数据。 ② 需运用大量统计和计量经济学方法收集和处理大量数据。③ 环境变量可能难以度量。④ 价值评估的结果依赖于函数形式和估算技术,因此函数的界定很重要。 ⑤ 财产的价格可能会反映人们对未来房地产市场的期望。① 因房地产市场并不十分活跃和顺利运转,因此难以得到可靠的数据。 ② 需运用大量统计和计量经济学方法收集和处理大量数据。③ 环境变量可能难以度量。④ 价值评估的结果依赖于函数形式和估算技术,因此函数的界定很重要。 ⑤ 财产的价格可能会反映人们对未来房地产市场的期望。① 因房地产市场并不十分活跃和顺利运转,因此难以得到可靠的数据。 ② 需运用大量统计和计量经济学方法收集和处理大量数据。③ 环境变量可能难以度量。④ 价值评估的结果依赖于函数形式和估算技术,因此函数的界定很重要。 ⑤ 财产的价格可能会反映人们对未来房地产市场的期望。'
+</script>
+
+<template>
+  <div class="home-box data-box">
+    <div class="home-box-top">
+      <div class="title">数据分析</div>
+      <div class="flex items-center space-x-[0.4rem] text-[#333333]">
+        <RouterLink to="/work-order" class="flex items-center space-x-[0.4rem] text-[1.6rem]">
+          <span>更多</span>
+          <svg width="1.5rem" height="2rem" viewBox="0 0 15 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <path fill-rule="evenodd" clip-rule="evenodd"
+              d="M14.5303 10L4.53033 0L2.84959 1.68074L11.1688 10L2.84959 18.3193L4.53033 20L14.5303 10ZM7.39954 10L2.1534 4.75386L0.472656 6.4346L4.03805 10L0.472656 13.5654L2.1534 15.2461L7.39954 10Z"
+              fill="url(#paint0_linear_1909_2940)" />
+            <defs>
+              <linearGradient id="paint0_linear_1909_2940" x1="14.5303" y1="10" x2="-0.817942" y2="10.5941"
+                gradientUnits="userSpaceOnUse">
+                <stop stop-color="#1872D8" />
+                <stop offset="0.43028" stop-color="#08AEF1" />
+                <stop offset="1" stop-color="white" />
+              </linearGradient>
+            </defs>
+          </svg>
+        </RouterLink>
+      </div>
+    </div>
+    <div class="data-box-main">
+      <div class="content">
+        <h5 class="title">{{ reportData.showVal }}</h5>
+        <div class="markdown-inner">
+          <ChatText :content="reportData.answer" class="html-box"></ChatText>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.data-box {
+
+  &-main {
+    padding: 1.8rem 2.5rem 1.6rem 4rem;
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+
+    .content {
+      .markdown-inner {
+        height: 20rem;
+        overflow-y: scroll;
+
+        &::-webkit-scrollbar-track {
+          background-color: rgba(255, 255, 255, 0.3);
+        }
+      }
+
+      // .markdown-body{
+      //   &::-webkit-scrollbar {
+      //     width: 0px !important;
+      //   }
+      // }
+      .title {
+        margin-bottom: 1.6rem;
+        font-size: 1.6rem;
+        font-weight: bold;
+      }
+
+      .html-box {
+        // height: 22.6rem;
+        // overflow-y: auto;
+        font-size: 1.4rem;
+      }
+
+      .markdown-body {
+        background: initial;
+      }
+    }
+
+    .btn {
+      width: 12.4rem;
+      height: 4.4rem;
+      background: linear-gradient(270deg, #59CCFA 0%, #3C97F7 100%);
+      font-size: 1.6rem;
+      font-weight: 500;
+      color: #fff;
+      border-radius: 0.4rem;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-top: 2rem;
+    }
+  }
+}
+</style>
+
+<style >
+.data-box .data-box-main .content .markdown-body .custom-table-wrapper {
+  width: 45rem !important;
+}
+</style>

+ 113 - 0
src/views/screen/components-back/gongyi.vue

@@ -0,0 +1,113 @@
+<script setup>
+import { useRouter } from 'vue-router';
+defineProps({
+  gongyiData: {
+    type: Array,
+    default: []
+  },
+})
+const router = useRouter()
+const types = ['水质报警', '生化报警', '预测报警']
+const toGongyi = (item) => {
+  router.push('water-warn');
+}
+
+</script>
+
+<template>
+  <div class="home-box gongyi">
+    <div class="home-box-top">
+      <div class="title">工艺管控助手</div>
+      <div class="flex items-center space-x-[0.4rem] text-[#333333]">
+        <RouterLink to="/water-warn" class="flex items-center space-x-[0.4rem] text-[1.6rem]">
+          <span>更多</span>
+          <svg width="1.5rem" height="2rem" viewBox="0 0 15 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <path fill-rule="evenodd" clip-rule="evenodd" d="M14.5303 10L4.53033 0L2.84959 1.68074L11.1688 10L2.84959 18.3193L4.53033 20L14.5303 10ZM7.39954 10L2.1534 4.75386L0.472656 6.4346L4.03805 10L0.472656 13.5654L2.1534 15.2461L7.39954 10Z" fill="url(#paint0_linear_1909_2940)"/>
+            <defs>
+            <linearGradient id="paint0_linear_1909_2940" x1="14.5303" y1="10" x2="-0.817942" y2="10.5941" gradientUnits="userSpaceOnUse">
+            <stop stop-color="#1872D8"/>
+            <stop offset="0.43028" stop-color="#08AEF1"/>
+            <stop offset="1" stop-color="white"/>
+            </linearGradient>
+            </defs>
+          </svg>
+        </RouterLink>
+      </div>
+    </div>
+    <div class="gongyi-wrap">
+      <div class="gongyi-wrap-top gongyi-wrap-item">
+        <span>告警类型</span>
+        <span>告警信息</span>
+        <span>触发时间</span>
+      </div>
+      <ul class="gongyi-wrap-list">
+        <li class="gongyi-wrap-item" v-for="item in gongyiData" @click="toGongyi">
+          <span>{{ types[item.type] }}</span>
+          <span>{{ item.reason }}</span>
+          <span>{{ item.time }}</span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.gongyi {
+  // height: 36.4rem;
+  // margin-top: 2rem;
+
+  .gongyi-wrap {
+    padding: 1.2rem;
+
+    &-top {
+      background: rgba(10, 139, 207, 0.12);
+      font-size: 1.4rem;
+      color: #1A2029;
+      font-weight: bold;
+    }
+
+    &-list {
+      height: 12.5rem;
+      overflow-y: auto;
+      
+      &::-webkit-scrollbar {
+          width: 0px !important;
+        }
+    }
+
+    &-item {
+      margin-bottom: 1rem;
+      padding: 1rem 1.2rem;
+      display: flex;
+      line-height: 1.4rem;
+      background: rgba(10, 139, 207, 0.06);
+      justify-content: space-between;
+      align-items: center;
+      color: rgba(65, 91, 115, 1);
+      cursor: pointer;
+      font-size: 1.4rem;
+
+      >span {
+        flex: 1;
+      }
+
+      >span:nth-child(2) {
+        text-align: center;
+      }
+
+      >span:last-child {
+        text-align: right;
+      }
+    }
+
+    .warning,
+    &-item:hover {
+      color: #F0727A;
+    }
+
+    &-top:hover {
+      color: #1A2029;
+    }
+  }
+}
+</style>

+ 68 - 0
src/views/screen/components-back/jsBox.vue

@@ -0,0 +1,68 @@
+<template>
+
+  <ul class="box-wrap">
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.jscodSjz" :value="screenData.jsCod"></cicle>
+      </div>
+      <div class="name">COD</div>
+      <div class="value">标准值:&lt;{{ screenData.jscodSjz }}mg/L</div>
+    </li>
+
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.jsadSjz" :value="screenData.jsNh3"></cicle>
+      </div>
+      <div class="name">NH3-N</div>
+      <div class="value">标准值:&lt;{{ screenData.jsadSjz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.jszdSjz" :value="screenData.jsTn"></cicle>
+      </div>
+      <div class="name">TN</div>
+      <div class="value">标准值:&lt;{{ screenData.jszdSjz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.jszlSjz" :value="screenData.jsTp"></cicle>
+      </div>
+      <div class="name">TP</div>
+      <div class="value">标准值:&lt;{{ screenData.jszlSjz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="screenData.jsssSjz" :value="screenData.jsSs"></cicle>
+      </div>
+      <div class="name">SS</div>
+      <div class="value">标准值:&lt;{{ screenData.jsssSjz }}mg/L</div>
+    </li>
+    <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="9" :value="screenData.jsPh"></cicle>
+      </div>
+      <div class="name">PH值</div>
+      <div class="value">标准值:6~9</div>
+    </li>
+    <!-- <li class="box-item">
+      <div class="cicle">
+        <cicle :levelVal="35" :value="screenData.jsSw"></cicle>
+      </div>
+      <div class="name">温度</div>
+      <div class="value">标准值:15-35℃</div>
+    </li> -->
+  </ul>
+</template>
+
+<script setup>
+import cicle from "./cicle.vue";
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    required: true,
+    default: 0,
+  }
+})
+
+</script>

+ 191 - 0
src/views/screen/components-back/liuliang.vue

@@ -0,0 +1,191 @@
+<script setup>
+import { ref } from 'vue';
+
+defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  },
+})
+
+const index = ref(0)
+const changeTab = (i) => {
+  index.value = i
+}
+
+const showHBarrow = (value) => {
+  const str = value > 0 ? '↑' : '↓'
+  const num = value ? (value * 100).toFixed(2) : 0
+  return str + num
+}
+
+const showVal = (value) => {
+  return value ? Number(value).toFixed(2) : 0
+}
+
+const getStyle = (value) => {
+  return {
+    color: value > 0 ? '#F0727A' : '#38c348'
+  }
+}
+</script>
+
+<template>
+  <div class="home-box liuliang">
+    <div class="home-box-top">
+      <div class="title">流量分析</div>
+      <div class="right">
+        <span :class="['btn', index == 0 ? 'action' : '']" @click="changeTab(0)">进水</span>
+        <span :class="['btn', index == 1 ? 'action' : '']" @click="changeTab(1)">出水</span>
+      </div>
+    </div>
+    <div class="box-wrap">
+      <ul class="box-wrap-list" v-show="index === 0">
+        <li class="box-wrap-item">
+          <div class="dess">瞬时 m³/h</div>
+          <div class="num">{{ showVal(screenData.jsSlq) }}</div>
+          <div class="footer" :style="getStyle(screenData.ssJsHb)">{{ showHBarrow(screenData.ssJsHb) }}%</div>
+        </li>
+        <li class="box-wrap-item">
+          <div class="dess">昨日累计 m³/d</div>
+          <div class="num">{{ showVal(screenData.zrZJSL) }}</div>
+          <div class="footer" :style="getStyle(screenData.zrZJsHb)">{{ showHBarrow(screenData.zrZJsHb) }}%</div>
+        </li>
+        <li class="box-wrap-item">
+          <div class="dess">周均值 m³/d</div>
+          <div class="num">{{ showVal(screenData.szZJSAvg) }}</div>
+          <div class="footer" :style="getStyle(screenData.szZJSHb)">{{ showHBarrow(screenData.szZJSHb) }}%</div>
+        </li>
+      </ul>
+      <ul class="box-wrap-list" v-show="index === 1">
+        <li class="box-wrap-item">
+          <div class="dess">瞬时 m³/h</div>
+          <div class="num">{{ showVal(screenData.csSlqc) }}</div>
+          <div class="footer" :style="getStyle(screenData.ssCsHb)">{{ showHBarrow(screenData.ssCsHb) }}%</div>
+        </li>
+        <li class="box-wrap-item">
+          <div class="dess">昨日累计 m³/d</div>
+          <div class="num">{{ showVal(screenData.zrZCSL) }}</div>
+          <div class="footer" :style="getStyle(screenData.zrZCsHb)">{{ showHBarrow(screenData.zrZCsHb) }}%</div>
+        </li>
+        <li class="box-wrap-item">
+          <div class="dess">周均值 m³/d</div>
+          <div class="num">{{ showVal(screenData.szZCSAvg) }}</div>
+          <div class="footer" :style="getStyle(screenData.szZCSHb)">{{ showHBarrow(screenData.szZCSHb) }}%</div>
+        </li>
+      </ul>
+    </div>
+
+
+  </div>
+</template>
+
+<style scoped lang="scss">
+.liuliang {
+  height: 15rem;
+  // margin-top: 2rem;
+
+  .box-wrap {
+    // margin-top: 3rem;
+    // padding: 0 3rem;
+
+    &-title {
+      font-size: 1.6rem;
+      font-weight: 600;
+      color: #1A2029;
+      margin-bottom: 1.6rem;
+      line-height: 1.6rem;
+    }
+
+    &-list {
+      padding: 1.8rem 1.8rem 1.6rem 1.8rem;
+      display: flex;
+    }
+
+    &-item:nth-child(2) {
+      background: url(@/assets/images/home/liuliang2.png) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    &-item:nth-child(3) {
+      background: url(@/assets/images/home/liuliang3.png) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    &-item {
+      flex: 1;
+      margin-right: 0.8rem;
+      width: 15.4rem;
+      height: 7.6rem;
+      background: url(@/assets/images/home/liuliang1.png) no-repeat;
+      background-size: 100% 100%;
+      padding-left: 5.8rem;
+      font-size: 1.2rem;
+      color: #415B73;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      line-height: 1;
+
+      &:last-child {
+        margin-right: 0;
+      }
+
+      .num {
+        color: #1A2029;
+        font-size: 1.4rem;
+        font-weight: bold;
+        margin: 0.8rem 0 0.2rem;
+      }
+
+      .footer {
+        color: #F0727A;
+
+        &::before {
+          content: '环比';
+          color: #7395B3;
+          margin-right: 1.5rem;
+          display: inline-block;
+          scale: 0.8;
+        }
+      }
+    }
+  }
+  
+  .right {
+    .btn {
+      width: 6.2rem;
+      height: 2.8rem;
+      background: rgba(238, 249, 255, 1);
+      border: 0.1rem solid rgba(122, 215, 249, 1);
+      float: left;
+      border-radius: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      color: rgba(84, 194, 248, 1);
+      font-size: 1.4rem;
+
+      &:first-child {
+        border-right: 0;
+        border-top-left-radius: 0.4rem;
+        border-bottom-left-radius: 0.4rem;
+      }
+
+      &:last-child {
+        border-left: 0;
+        border-top-right-radius: 0.4rem;
+        border-bottom-right-radius: 0.4rem;
+      }
+    }
+
+    .action,
+    .btn:hover {
+      background: linear-gradient(270deg, #59CCFA 0%, #3C97F7 100%);
+      border: none;
+      color: #fff;
+    }
+  }
+}
+</style>

+ 74 - 0
src/views/screen/components-back/middleBox.vue

@@ -0,0 +1,74 @@
+<script setup>
+import { onMounted } from "vue";
+// import motor3d from '../3d/renderModel.js';
+
+
+defineProps({
+  dataTime: ''
+})
+
+onMounted(() => {
+  // new motor3d('#scene') 
+})
+
+</script>
+
+<template>
+  <div class="send-ask">
+    <!-- <div id="scene" class="3d-scene"></div> -->
+    <RouterLink class="send-ask-button" to="/answer" :data-time="dataTime">输入您的问题或需求</RouterLink>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.send-ask {
+  margin: 0 2rem;
+  position: relative;
+  height: 100%;
+  
+  #scene {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    width: 116rem;
+    height: calc(100% - 6rem);
+    z-index: 1;
+  }
+
+  &-button {
+    position: absolute;
+    width: 100%;
+    height: 6rem;
+    border-radius: 0.8rem;
+    box-shadow: 0 0.3rem 1.2rem 0 #97D3FF40;
+    background: #FFFFFF;
+    font-size: 1.5rem;
+    color: #9E9E9E99;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 1.7rem 0 3.4rem;
+    bottom: 4.7rem;
+    z-index: 2;
+
+    &::before {
+      content: attr(data-time);
+      display: block;
+      position: absolute;
+      right: 0;
+      bottom: 6.8rem;
+      color: #9E9E9E;
+      font-size: 1.2rem;
+    }
+
+    &::after {
+      content: '';
+      background: url(@/assets/images/home/send.png) no-repeat;
+      background-size: contain;
+      width: 5.6rem;
+      height: 3.6rem;
+    }
+  }
+}
+</style>

+ 114 - 0
src/views/screen/components-back/shuizhi.vue

@@ -0,0 +1,114 @@
+<script setup>
+import { ref } from "vue";
+import jsBox from "./jsBox.vue";
+import csBox from "./csBox.vue";
+const index = ref(0)
+const changeTab = (i) => {
+  index.value = i
+}
+defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  },
+})
+</script>
+
+<template>
+  <div class="home-box shuizhi">
+    <div class="home-box-top">
+      <div class="title">水质分析</div>
+      <div class="right">
+        <span :class="['btn', index == 0 ? 'action' : '']" @click="changeTab(0)">进水</span>
+        <span :class="['btn', index == 1 ? 'action' : '']" @click="changeTab(1)">出水</span>
+      </div>
+    </div>
+    <div class="main">
+      <jsBox :screenData="screenData" v-if="index == 0"></jsBox>
+      <csBox :screenData="screenData" v-else="index == 1"></csBox>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.shuizhi {
+  height: 31rem;
+  .right {
+    .btn {
+      width: 6.2rem;
+      height: 2.8rem;
+      background: rgba(238, 249, 255, 1);
+      border: 0.1rem solid rgba(122, 215, 249, 1);
+      float: left;
+      border-radius: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      color: rgba(84, 194, 248, 1);
+      font-size: 1.4rem;
+
+      &:first-child {
+        border-right: 0;
+        border-top-left-radius: 0.4rem;
+        border-bottom-left-radius: 0.4rem;
+      }
+
+      &:last-child {
+        border-left: 0;
+        border-top-right-radius: 0.4rem;
+        border-bottom-right-radius: 0.4rem;
+      }
+    }
+
+    .action,
+    .btn:hover {
+      background: linear-gradient(270deg, #59CCFA 0%, #3C97F7 100%);
+      border: none;
+      color: #fff;
+    }
+  }
+
+  :deep(.main) {
+    padding: 1.8rem 1.8rem 1.6rem;
+
+    .box-wrap {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      grid-gap: 1rem 6rem;
+    }
+
+    .box-item {
+      height: 10.8rem;
+      display: flex;
+      align-items: center;
+      flex-direction: column;
+      overflow: hidden;
+
+      .cicle {
+        height: 5.2rem;
+        margin-bottom: 1rem;
+      }
+
+      .name {
+        color: #0A284E;
+        font-size: 1.4rem;
+        line-height: 2rem;
+        margin-bottom: 0.2rem;
+        font-weight: bold;
+      }
+
+      .value {
+        font-size: 1.2rem;
+        line-height: 1.8rem;
+        color: #466993;
+        width: 100%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        text-align: center;
+      }
+    }
+  }
+}
+</style>

+ 300 - 0
src/views/screen/components/ContinueData.vue

@@ -0,0 +1,300 @@
+<script setup>
+import { onMounted, onUnmounted, watch, ref } from 'vue';
+import * as echarts from 'echarts';
+import { formatDecimals } from "@/utils/format";
+import { screenApi } from '@/api/screen';
+import dayjs from 'dayjs';
+import LayoutCard from './LayoutCard.vue';
+import { getBarOptions } from '../config/echartOption';
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  }
+})
+
+let barOption = {};
+let lineOption = {};
+let timer = null;
+let chart = null;
+const activeIndex = ref(0);
+const echartRef = ref(null);
+
+
+const windowResize = () => {
+  clearTimeout(timer);
+  timer = setTimeout(() => chart.resize(), 100);
+};
+
+watch(() => props.screenData, (currentVal) => {
+  const { no3Hlj1Jqr, no3Hlj2Jqr, nh31Jqr, nh32Jqr, tpRccJqr } = currentVal;
+  const options = [ no3Hlj1Jqr, no3Hlj2Jqr, nh31Jqr, nh32Jqr, tpRccJqr ].map(item => {
+    return item ? Number(item.toFixed(2)) : 0;
+  })
+  barOption = options;
+  chart.setOption( getBarOptions(options) )
+})
+
+const getLineOptions = ({xAxisData, seriesData}) => {
+  const colors = ['#F7931E', '#2454FF', '#00FFFF', '#00FF00', '#3DB0F1', '#F4CF35',];
+  const series = Object.keys(seriesData).map(((key, index) => {
+    const item = seriesData[key];
+    const color = colors[index];
+    return {
+      name: key,
+      type: "line",
+      smooth: true,
+      symbol: 'none',
+      areaStyle: {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          {
+            offset: 0,
+            color,
+          },
+          {
+            offset: 1,
+            color: "rgba(255, 255, 255, 0.3)",
+          },
+        ]),
+      },
+      itemStyle: { color },
+      lineStyle: {
+        width: '2',
+        color
+      },
+      data: item
+    }
+  }));
+
+  const option = {
+    grid: {
+      bottom: '4%',
+      top: "23%",
+      left: "5%",
+      right: "2%",
+      containLabel: true,
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    legend: [
+      {
+        show: true,
+        orient: 'horizontal',
+        icon: 'circle',
+        itemWidth: 8,
+        y: 'top',
+        x: 'right',
+        textStyle: {fontSize: '1.2rem', color: '#415B73'},
+        data: ['1#好氧池硝酸盐', '1#缺氧氨氮', '二沉池正磷酸盐']  
+      },
+      {
+      show: true,
+      orient: 'horizontal',
+      icon: 'circle',
+      itemWidth: 8,
+      y: '8%',
+      x: 'right',
+      textStyle: {fontSize: '1.2rem', color: '#415B73'},
+      data: ['2#好氧池硝酸盐', '2#缺氧氨氮',]  
+    }
+    ],
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      axisLabel: {
+        interval: 'auto',
+        show: true,
+        fontSize: '1.1rem',
+        color: "#0A284E",
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+          show: true,
+          color: "#c1d3e6",
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      interval: 0, // 0 表示强制显示所有标签,'auto' 表示自动间隔
+      // boundaryGap: true,
+    },
+    yAxis: {
+      splitNumber: 4,
+      type: "value",
+      axisLabel: {
+        show: true,
+        fontSize: '1.2rem',
+        color: "#7395B3"
+      },
+      axisLine: {
+        show: false,
+        lineStyle: {
+          color: "#BDD4E8",
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      splitLine: {
+        lineStyle: {
+          color: "#BDD4E8",
+          type: "dashed",
+        },
+      }
+    },
+    series
+  };
+
+  return option;
+}
+
+const onChangeTab = (index) => {
+  activeIndex.value = index;
+  if (index == 0) {
+    chart.setOption( getBarOptions(barOption), {notMerge: true} )
+  } else {
+    chart.setOption( getLineOptions(lineOption), {notMerge: true} )
+  }
+  setTimeout(() => chart.resize());
+}
+
+onMounted(() => {
+  screenApi.getContinueDataByDays().then(({ data }) => {
+    const seriesData = { '1#好氧池硝酸盐': [], '2#好氧池硝酸盐': [], '1#缺氧氨氮': [], '2#缺氧氨氮': [], '二沉池正磷酸盐': [] };
+    const xAxisData = data.map(item => dayjs(item.testHour).format('MM/DD HH'));
+    data.forEach(({ no3Hlj1Jqr, no3Hlj2Jqr, nh31Jqr, nh32Jqr, tpRccJqr }) => {
+      seriesData['1#好氧池硝酸盐'].push(formatDecimals(no3Hlj1Jqr));
+      seriesData['2#好氧池硝酸盐'].push(formatDecimals(no3Hlj2Jqr));
+      seriesData['1#缺氧氨氮'].push(formatDecimals(nh31Jqr));
+      seriesData['2#缺氧氨氮'].push(formatDecimals(nh32Jqr));
+      seriesData['二沉池正磷酸盐'].push(formatDecimals(tpRccJqr));
+    })
+    lineOption = {xAxisData, seriesData};
+  })
+
+  chart = echarts.init(echartRef.value, 'light');
+  window.addEventListener("resize", windowResize);
+})
+
+onUnmounted(() => {
+  window.removeEventListener("resize", windowResize);
+})
+</script>
+
+<template>
+  <LayoutCard title="连续检测数据">
+    <template #headerRight>
+      <ul>
+        <li :class="['btn', activeIndex == 0 ? 'action' : '']" @click="onChangeTab(0)">进水</li>
+        <li :class="['btn', activeIndex == 1 ? 'action' : '']" @click="onChangeTab(1)">出水</li>
+      </ul>
+    </template>
+    <div class="main-container">
+      <div class="echart-card">
+        <div class="title" v-show="activeIndex == 0">
+          <span></span>
+          <ul class="tabs space-x-[1.8rem]">
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#FF6737]"><i class="bg-[#FF6737]"></i></span>
+              <span>当前值</span>
+            </li>
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#00AB84]"><i class="bg-[#00AB84]"></i></span>
+              <span>标准值</span>
+            </li>
+          </ul>
+        </div>
+        <div class="echart-inner" ref="echartRef" ></div>
+        <!-- :style="{ height: activeIndex === 0 ? '10rem' : '20rem' }" -->
+      </div>
+    </div>
+  </LayoutCard>
+</template>
+
+<style lang="scss" scoped>
+.main-container {
+  width: 100%;
+  height: 22rem;
+  .echart-card {
+    display: flex;
+    flex-flow: column;
+    height: 100%;
+    color: #415B73;
+    font-size: 1.2rem;
+    font-weight: bold;
+
+    .title {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding:0rem 0 1.2rem 0;
+
+      .tabs {
+        display: flex;
+
+        .item {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 1.2rem;
+
+          .square-icon {
+            display: inline-block;
+            padding: 1px;
+            border-width: 1px;
+            border-style: solid;
+            background: #fff;
+
+            i {
+              display: block;
+              width: 0.8rem;
+              height: 0.8rem;
+            }
+          }
+        }
+      }
+    }
+    .echart-inner {
+      flex: 1;
+    }
+  }
+}
+
+.btn {
+    width: 6.2rem;
+    height: 2.8rem;
+    background: rgba(238, 249, 255, 1);
+    border: 0.1rem solid rgba(122, 215, 249, 1);
+    float: left;
+    border-radius: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    color: rgba(84, 194, 248, 1);
+    font-size: 1.4rem;
+
+    &:first-child {
+      border-right: 0;
+      border-top-left-radius: 0.4rem;
+      border-bottom-left-radius: 0.4rem;
+    }
+
+    &:last-child {
+      border-left: 0;
+      border-top-right-radius: 0.4rem;
+      border-bottom-right-radius: 0.4rem;
+    }
+  }
+  .action, .btn:hover {
+    background: linear-gradient(270deg, #59CCFA 0%, #3C97F7 100%);
+    border: none;
+    color: #fff;
+  }
+</style>

+ 65 - 0
src/views/screen/components/LayoutCard.vue

@@ -0,0 +1,65 @@
+<script setup>
+defineProps({
+  title: {
+    type: String,
+    default: ''
+  }
+})
+</script>
+
+<template>
+  <section class="layout-card">
+    <div class="header">
+      <div class="header-left">{{ title }}</div>
+      <div class="header-right">
+        <slot name="headerRight"></slot>
+      </div>
+    </div>
+    <div class="layout-main">
+      <slot></slot>
+    </div>
+  </section>
+</template>
+
+<style lang="scss" scoped>
+.layout-card {
+  width: 54rem;
+  .header {
+    padding: .7rem 2.4rem .7rem 1.2rem;
+    display: flex;
+    justify-content: space-between;
+    position: relative;
+    align-items: center;
+    &::after {
+      content: '';
+      height: 0.2rem;
+      background: url('@/assets/images/home/line.png') no-repeat;
+      background-size: 100% 100%;
+      width: 100%;
+      position: absolute;
+      left: 1.2rem;
+      bottom: 0;
+    }
+
+    .header-left {
+      font-size: 1.8rem;
+      display: flex;
+      align-items: center;
+      font-family: YouSheBiaoTiHei;
+      color: #1A2029;
+      &::before {
+        content: '';
+        width: 2rem;
+        height: 2rem;
+        background: url('@/assets/images/home/mark.png') no-repeat;
+        background-size: cover;
+        margin-right: 0.8rem;
+        display: inline-block;
+      }
+    }
+  }
+  .layout-main {
+    padding: 1.8rem 1rem 1.2rem 1.2rem;
+  }
+}
+</style>

+ 388 - 0
src/views/screen/components/WaterQuality.vue

@@ -0,0 +1,388 @@
+<script setup>
+import { onMounted, onUnmounted, watchEffect, ref } from 'vue';
+import { NTooltip } from 'naive-ui';
+import dayjs from 'dayjs';
+import { formatDecimals } from "@/utils/format";
+import { screenApi } from '@/api/screen'
+import LayoutCard from './LayoutCard.vue';
+
+import * as echarts from 'echarts';
+
+let timer = null;
+let echartJs = null;
+let echartCs = null;
+const echartJsRef = ref(null);
+const echartCsRef = ref(null);
+const inWater = ref({});
+const outWater = ref({});
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  }
+})
+const createEchart = ({ xAxisData, seriesData, chart, title }) => {
+  const colors = ['#F7931E', '#2454FF', '#00FFFF', '#00FF00', '#3DB0F1', '#F4CF35',];
+
+  const series = Object.keys(seriesData).map(((key, index) => {
+    const item = seriesData[key];
+    const color = colors[index];
+    return {
+      name: key,
+      type: "line",
+      smooth: true,
+      symbol: 'none',
+      areaStyle: {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          {
+            offset: 0,
+            color,
+          },
+          {
+            offset: 1,
+            color: "rgba(255, 255, 255, 0.3)",
+          },
+        ]),
+      },
+      itemStyle: { color },
+      lineStyle: {
+        width: '2',
+        color
+      },
+      data: item
+    }
+  }));
+
+  const option = {
+    grid: {
+      bottom: "4%",
+      top: "26%",
+      left: '4%',
+      right: "2%",
+      containLabel: true,
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    title: {
+      text: title,
+      left: '0',
+      textStyle: {
+        fontSize: '1.2rem',
+        color: '#415B73'
+      },
+    },
+    legend: {
+      orient: 'horizontal',
+      icon: 'circle',
+      itemWidth: 8,
+      y: 'top',
+      x: 'right',
+      textStyle: {fontSize: '1.2rem', color: '#415B73'}
+    },
+    xAxis: {
+      type: "category",
+      data: xAxisData,
+      axisLabel: {
+        show: true,
+        fontSize: '1.2rem',
+        color: "#0A284E",
+
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+          show: true,
+          color: "#c1d3e6",
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      boundaryGap: true,
+    },
+    yAxis: {
+      splitNumber: 2,
+      type: "value",
+      axisLabel: {
+        show: true,
+        fontSize: '1.2rem',
+        color: "#7395B3"
+      },
+      axisLine: {
+        show: false,
+        lineStyle: {
+          color: "#BDD4E8",
+        },
+      },
+      axisTick: {
+        show: false,
+      },
+      splitLine: {
+        lineStyle: {
+          color: "#BDD4E8",
+          type: "dashed",
+        },
+      }
+    },
+    series
+  };
+  chart && chart.setOption(option);
+};
+
+watchEffect(() => {
+  const {
+    jsCod, jsNh3, jsTn, jsTp, jsSs, jsPh,
+    jscodSjz, jsadSjz, jszdSjz, jszlSjz, jsssSjz,
+    csCod, csNh3, csTn, csTp, csSs, csPh,
+    cscodBzz, csadBzz, cszzBzz, cszlBzz, csssBzz
+  } = props.screenData;
+
+  inWater.value = [
+    { label: 'COD', standardValue: formatDecimals(jsCod), currentValue: formatDecimals(jscodSjz) },
+    { label: 'NH3-N', standardValue: formatDecimals(jsNh3), currentValue: formatDecimals(jsadSjz) },
+    { label: 'TN', standardValue: formatDecimals(jsTn), currentValue: formatDecimals(jszdSjz) },
+    { label: 'TP', standardValue: formatDecimals(jsTp), currentValue: formatDecimals(jszlSjz) },
+    { label: 'SS', standardValue: formatDecimals(jsSs), currentValue: formatDecimals(jsssSjz) },
+    { label: 'PH值', standardValue: '6-9', currentValue: formatDecimals(jsPh) }
+  ],
+  outWater.value = [
+    { label: 'COD', standardValue: formatDecimals(csCod), currentValue: formatDecimals(cscodBzz) },
+    { label: 'NH3-N', standardValue: formatDecimals(csNh3), currentValue: formatDecimals(csadBzz) },
+    { label: 'TN', standardValue: formatDecimals(csTn), currentValue: formatDecimals(cszzBzz) },
+    { label: 'TP', standardValue: formatDecimals(csTp), currentValue: formatDecimals(cszlBzz) },
+    { label: 'SS', standardValue: formatDecimals(csSs), currentValue: formatDecimals(csssBzz) },
+    { label: 'PH值', standardValue: '6-9', currentValue: formatDecimals(csPh) }
+  ]
+})
+
+const windowResize = () => {
+  clearTimeout(timer);
+  timer = setTimeout(() => {
+    echartJs.resize();
+    echartCs.resize();
+  }, 100);
+};
+
+onMounted(() => {
+  echartJs = echarts.init(echartJsRef.value, 'light');
+  echartCs = echarts.init(echartCsRef.value, 'light');
+  window.addEventListener("resize", windowResize);
+
+  screenApi.getIndustryDataByDays().then(res => {
+    const seriesJsData = { 'COD': [], 'NH3-N': [], 'TN': [], 'TP': [], 'SS': [], 'PH值': [] };
+    const seriesCsData = { 'COD': [], 'NH3-N': [], 'TN': [], 'TP': [], 'SS': [], 'PH值': [] };
+
+    const xAxisData = res.data.map(item => dayjs(item.testHour).format('MM/DD HH'));
+
+    res.data.forEach(item => {
+      seriesJsData['COD'].push(formatDecimals(item.jsCod));
+      seriesJsData['NH3-N'].push(formatDecimals(item.jsNh3));
+      seriesJsData['TN'].push(formatDecimals(item.jsTn));
+      seriesJsData['TP'].push(formatDecimals(item.jsTp));
+      seriesJsData['SS'].push(formatDecimals(item.jsSs));
+      seriesJsData['PH值'].push(formatDecimals(item.jsPh));
+
+      seriesCsData['COD'].push(formatDecimals(item.csCod));
+      seriesCsData['NH3-N'].push(formatDecimals(item.csNh3));
+      seriesCsData['TN'].push(formatDecimals(item.csTn));
+      seriesCsData['TP'].push(formatDecimals(item.csTp));
+      seriesCsData['SS'].push(formatDecimals(item.csSs));
+      seriesCsData['PH值'].push(formatDecimals(item.csPh));
+    });
+
+    createEchart({ xAxisData, seriesData: seriesJsData, chart: echartJs, title: '进水 · 近7日趋势' });
+    createEchart({ xAxisData, seriesData: seriesCsData, chart: echartCs, title: '出水 · 近7日趋势' });
+  })
+
+})
+
+onUnmounted(() => {
+  window.removeEventListener("resize", windowResize);
+})
+</script>
+
+<template>
+  <LayoutCard title="水质数据">
+    <div class="main-container">
+      <div class="count-card mb-[1rem]">
+        <div class="count-num_inner">
+          <p class="title">今日<br>进水</p>
+          <ul class="num-group flex space-x-[0.4rem]">
+            <template v-for="item, index in inWater" :key="item.label">
+              <n-tooltip placement="top" trigger="hover">
+                <template #trigger>
+                  <li class="item">
+                    <span class="label">{{ item.label }}</span>
+                    <span class="num">{{ item.currentValue }} <i v-if="index != inWater.length - 1">mg/L</i></span>
+                  </li>
+                </template>
+                <ul class="text-bold text-[#fff] text-[1.2rem] leading-[1.8rem]">
+                  <li>当前值:{{ item.currentValue }} <i v-if="index != inWater.length - 1">mg/L</i></li>
+                  <li>标准值:{{ item.standardValue }} <i v-if="index != inWater.length - 1">mg/L</i></li>
+                </ul>
+              </n-tooltip>
+            </template>
+          </ul>
+        </div>
+      </div>
+      <div class="count-card">
+        <div class="count-num_inner">
+          <p class="title">今日<br>出水</p>
+          <ul class="num-group flex space-x-[0.4rem]">
+            <template v-for="item, index in outWater" :key="item.label">
+              <n-tooltip placement="top" trigger="hover">
+                <template #trigger>
+                  <li class="item">
+                    <span class="label">{{ item.label }}</span>
+                    <span class="num">{{ item.currentValue }} <i v-if="index != inWater.length - 1">mg/L</i></span>
+                  </li>
+                </template>
+                <ul class="text-bold text-[#fff] text-[1.2rem] leading-[1.8rem]">
+                  <li>当前值:{{ item.currentValue }} <i v-if="index != inWater.length - 1">mg/L</i></li>
+                  <li>标准值:{{ item.standardValue }} <i v-if="index != inWater.length - 1">mg/L</i></li>
+                </ul>
+              </n-tooltip>
+            </template>
+          </ul>
+        </div>
+      </div>
+      <div class="echart-card">
+        <!-- <div class="title">
+          <span>进出水·流量分析</span>
+          <ul class="tabs space-x-[1.8rem]">
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#9fcbf2]"><i class="bg-[#5faeeb]"></i></span>
+              <span>出水</span>
+            </li>
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#f4e28c]"><i class="bg-[#eed056]"></i></span>
+              <span>出水</span>
+            </li>
+          </ul>
+        </div> -->
+        <div class="echart-inner mb-[1.4rem]" ref="echartJsRef"></div>
+        <div class="echart-inner" ref="echartCsRef"></div>
+      </div>
+    </div>
+  </LayoutCard>
+</template>
+
+<style lang="scss" scoped>
+.main-container {
+  width: 100%;
+
+  .count-card {
+    width: 100%;
+    height: 6.2rem;
+    padding: 4px;
+    background: url('@/assets/images/workbench/bg-square-line.png') center center no-repeat;
+    background-size: 100% 100%;
+
+    .count-num_inner {
+      width: 100%;
+      height: 100%;
+      padding: 0.6rem 0.6rem;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      .title {
+        flex-shrink: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 4.8rem;
+        height: 100%;
+        color: #3C97F7;
+        text-align: center;
+        font-family: "DingTalk JinBuTi";
+        font-size: 1.2rem;
+        letter-spacing: 0.12px;
+        line-height: 1.6rem;
+        border-radius: 2px;
+        background: linear-gradient(180deg, #99D7FF 0%, rgba(255, 255, 255, 0.00) 100%);
+      }
+
+      .num-group {
+        display: grid;
+        grid-template-columns: repeat(6, minmax(5.8rem, 1fr));
+        gap: 1rem;
+        width: 100%;
+        height: 100%;
+        padding-left: 0.9rem;
+        cursor: pointer;
+
+        .item {
+          @include flex(y, center, center);
+          text-align: center;
+
+          .label {
+            color: #0A284E;
+            font-size: 1.2rem;
+            font-weight: bold;
+            line-height: 1.6rem;
+          }
+
+          .num {
+            color: #415B73;
+            text-align: center;
+            font-size: 1.0rem;
+            line-height: 1.4rem;
+          }
+
+          &:hover {
+            border-radius: 2px;
+            background: #fff;
+          }
+        }
+      }
+    }
+  }
+
+  .echart-card {
+    color: #415B73;
+    font-size: 1.2rem;
+    font-weight: bold;
+    padding-top: 1.4rem;
+
+    .title {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 1.4rem 0 1.2rem 0;
+
+      .tabs {
+        display: flex;
+
+        .item {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 1.2rem;
+
+          .square-icon {
+            display: inline-block;
+            padding: 1px;
+            border-width: 1px;
+            border-style: solid;
+            background: #fff;
+
+            i {
+              display: block;
+              width: 0.8rem;
+              height: 0.8rem;
+            }
+          }
+        }
+      }
+    }
+
+    .echart-inner {
+      height: 14rem;
+    }
+  }
+}
+</style>

+ 335 - 0
src/views/screen/components/WaterYield.vue

@@ -0,0 +1,335 @@
+<script setup>
+import { onMounted, onUnmounted, watchEffect, ref, computed } from 'vue';
+import * as echarts from 'echarts';
+import { formatDecimals } from "@/utils/format";
+import LayoutCard from './LayoutCard.vue';
+
+const props = defineProps({
+  screenData: {
+    type: Object,
+    default: []
+  }
+})
+
+let timer = null;
+let chart = null;
+const echartRef = ref(null);
+const outWaterData = ref([]);
+const inWaterData = ref([]);
+const totalNum = ref(0)
+
+const waterYield = computed(() => {
+  if ( !totalNum.value ) return [0];
+  return totalNum.value.toFixed(0).split('');
+})
+
+const initEchart = () => {
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+    },
+    grid: {
+      bottom: "20%",
+      top: "6%",
+      left: "12%",
+      right: "0%",
+    },
+    xAxis: [
+      {
+        type: "category",
+        data: ['瞬时 m³/h', '昨日累计 m³/h', '周均值 m³/h'],
+        axisLabel: {
+          interval: 0,
+          show: true,
+          fontSize: '1.2rem',
+          color: "#0A284E",
+        },
+        axisLine: {
+          show: true,
+          lineStyle: {
+            type: 'dashed',
+            show: true,
+            color: "#c1d3e6",
+          },
+        },
+        axisTick: {
+          show: false,
+        },
+      },
+    ],
+    yAxis: [
+      {
+        splitNumber: 2,
+        type: "value",
+        axisLabel: {
+          show: true,
+          fontSize: '1.2rem',
+          color: "#7395B3",
+
+        },
+        axisLine: {
+          show: false,
+          lineStyle: {
+            color: "#BDD4E8",
+          },
+        },
+        axisTick: {
+          show: false,
+        },
+        splitLine: {
+          lineStyle: {
+            color: "#BDD4E8",
+            type: "dashed",
+          },
+        },
+      },
+      {
+         type: "category",
+         show: false,
+         data: outWaterData.value,
+      },
+    ],
+    series: [
+      {
+        name: "出水",
+        type: "bar",
+        showBackground: true,
+        itemStyle: {
+          borderRadius: [2, 2, 0, 0],
+          color: {
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [
+              {
+                offset: 0,
+                color: "rgba(95, 173, 235, 1)",
+              },
+              {
+                offset: 1,
+                color: "rgba(217, 237, 250, 0.6)",
+              },
+            ],
+          },
+        },
+        barWidth: 8,
+        data: outWaterData.value,
+      },
+      {
+        name: "进水",
+        type: "bar",
+        showBackground: true,
+        itemStyle: {
+          color: {
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [
+              {
+                offset: 0,
+                color: "rgba(238, 208, 85, 1)",
+              },
+              {
+                offset: 1,
+                color: "rgba(251, 251, 211, 0.6)",
+              },
+            ],
+          },
+        },
+        barWidth: 8,
+        data: inWaterData.value,
+      },
+    ]
+  }
+  chart && chart.setOption(option)
+}
+
+const windowResize = () => {
+  clearTimeout(timer);
+  timer = setTimeout(() => chart.resize(), 100);
+};
+
+watchEffect(() => {
+  const { jsSlq, zrZJSL, szZJSAvg, csSlqc, zrZCSL, szZCSAvg, totalTodayCSL } = props.screenData;
+  inWaterData.value = [formatDecimals(jsSlq), formatDecimals(zrZJSL), formatDecimals(szZJSAvg)];
+  outWaterData.value = [formatDecimals(csSlqc), formatDecimals(zrZCSL), formatDecimals(szZCSAvg)];
+  totalNum.value = totalTodayCSL;
+  initEchart();
+})
+
+onMounted(() => {
+  chart = echarts.init(echartRef.value, 'light');
+  window.addEventListener("resize", windowResize);
+})
+
+onUnmounted(() => {
+  window.removeEventListener("resize", windowResize);
+})
+
+</script>
+
+<template>
+  <LayoutCard title="水量数据">
+    <div class="main-container">
+      <div class="count-card">
+        <div class="count-num_inner">
+          <span class="count-title">今日累计出水水量</span>
+          <div class="count-value">
+            <ul class="num-group flex space-x-[0.4rem]">
+              <li class="item" v-for="item, index in waterYield" :key="index"><span class="num">{{ item }}</span></li>
+            </ul>
+            <span class="unit">m³</span>
+          </div>
+        </div>
+      </div>
+      <div class="echart-card">
+        <div class="title">
+          <span>进出水·流量分析</span>
+          <ul class="tabs space-x-[1.8rem]">
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#9fcbf2]"><i class="bg-[#5faeeb]"></i></span>
+              <span>出水</span>
+            </li>
+            <li class="item space-x-[0.6rem]">
+              <span class="square-icon border-[#f4e28c]"><i class="bg-[#eed056]"></i></span>
+              <span>出水</span>
+            </li>
+          </ul>
+        </div>
+        <div class="echart-inner" ref="echartRef"></div>
+      </div>
+    </div>
+  </LayoutCard>
+</template>
+
+<style lang="scss" scoped>
+.main-container {
+  width: 100%;
+  height: 22rem;
+
+  .count-card {
+    width: 100%;
+    height: 6.2rem;
+    padding: 4px;
+    background: url('@/assets/images/workbench/bg-square-line.png') center center no-repeat;
+    background-size: 100% 100%;
+
+    .count-num_inner {
+      width: 100%;
+      height: 100%;
+      padding: 0 2rem;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      background: linear-gradient(180deg, #91D4F6 0%, rgba(145, 212, 246, 0.00) 68.75%);
+
+      .count-title {
+        color: #1A2029;
+        text-align: center;
+        font-size: 1.4rem;
+        font-weight: bold;
+        letter-spacing: 0.14px;
+      }
+
+      .count-value {
+        display: flex;
+        align-items: flex-end;
+
+        .num-group {
+          display: flex;
+          align-items: center;
+          color: #FFF;
+          text-align: center;
+          font-family: D-DIN-PRO-700-Bold;
+          font-size: 3rem;
+          font-weight: bold;
+          letter-spacing: 0.3px;
+
+          .item {
+            position: relative;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 2.8rem;
+            height: 3.8rem;
+            border-radius: 4px 4px 4px 4px;
+            background: #377BF8;
+
+            .num {
+              position: relative;
+              z-index: 1;
+            }
+
+            &::before {
+              position: absolute;
+              top: 50%;
+              left: 0;
+              content: ' ';
+              width: 100%;
+              height: 1px;
+              background: #3a6ef6;
+            }
+          }
+        }
+
+        .unit {
+          margin-left: 0.8rem;
+          color: #4C8BFF;
+          text-align: center;
+          font-family: D-DIN-PRO;
+          font-size: 2rem;
+          font-weight: bold;
+        }
+      }
+    }
+  }
+
+  .echart-card {
+    display: flex;
+    flex-direction: column;
+    // flex: 1;
+    height: calc(100% - 6.2rem);
+    color: #415B73;
+    font-size: 1.2rem;
+    font-weight: bold;
+
+    .title {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 1.4rem 0 1.2rem 0;
+
+      .tabs {
+        display: flex;
+
+        .item {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 1.2rem;
+
+          .square-icon {
+            display: inline-block;
+            padding: 1px;
+            border-width: 1px;
+            border-style: solid;
+            background: #fff;
+
+            i {
+              display: block;
+              width: 0.8rem;
+              height: 0.8rem;
+            }
+          }
+        }
+      }
+    }
+    .echart-inner {
+      flex: 1;
+    }
+  }
+}
+</style>

+ 8 - 2
src/views/screen/components/dataBox.vue

@@ -45,7 +45,13 @@ const content = '① 因房地产市场并不十分活跃和顺利运转,因
 
 <style scoped lang="scss">
 .data-box {
-
+  .home-box-top {
+    .title {
+      font-weight: 400;
+      font-family: YouSheBiaoTiHei;
+      color: #1A2029;
+    }
+  }
   &-main {
     padding: 1.8rem 2.5rem 1.6rem 4rem;
     display: flex;
@@ -54,7 +60,7 @@ const content = '① 因房地产市场并不十分活跃和顺利运转,因
 
     .content {
       .markdown-inner {
-        height: 20rem;
+        height: 16rem;
         overflow-y: scroll;
 
         &::-webkit-scrollbar-track {

+ 16 - 2
src/views/screen/components/gongyi.vue

@@ -55,7 +55,14 @@ const toGongyi = (item) => {
 .gongyi {
   // height: 36.4rem;
   // margin-top: 2rem;
-
+  
+  .home-box-top {
+    .title {
+      font-weight: 400;
+      font-family: YouSheBiaoTiHei;
+      color: #1A2029;
+    }
+  }
   .gongyi-wrap {
     padding: 1.2rem;
 
@@ -67,7 +74,7 @@ const toGongyi = (item) => {
     }
 
     &-list {
-      height: 12rem;
+      height: 13rem;
       overflow-y: auto;
       
       &::-webkit-scrollbar {
@@ -87,6 +94,13 @@ const toGongyi = (item) => {
       cursor: pointer;
       font-size: 1.4rem;
 
+      span {
+        text-overflow: ellipsis;
+        overflow: hidden;
+        word-break: break-all;
+        white-space: nowrap;
+      }
+
       >span {
         flex: 1;
       }

+ 1 - 1
src/views/screen/components/middleBox.vue

@@ -49,7 +49,7 @@ onMounted(() => {
     align-items: center;
     justify-content: space-between;
     padding: 0 1.7rem 0 3.4rem;
-    bottom: 4.7rem;
+    bottom: 2rem;
     z-index: 2;
 
     &::before {

+ 19 - 17
src/views/screen/config/echartOption.js

@@ -7,11 +7,18 @@ export const getBarOptions = (currentData = []) => {
       trigger: 'axis',
     },
     grid: {
-      // containLabel: true,
-      bottom: "10%",
-      top: "6%",
-      left: "10%",
+      bottom: '4%',
+      top: "18%",
+      left: "5%",
       right: "2%",
+      containLabel: true,
+      // bottom: "4%",
+      // top: "6%",
+      // left: "10%",
+      // right: "2%",
+    },
+    legend: {
+      show: false,
     },
     xAxis: [
       {
@@ -20,7 +27,7 @@ export const getBarOptions = (currentData = []) => {
         axisLabel: {
           interval: 0,
           show: true,
-          fontSize: '1.1rem',
+          fontSize: '1.2rem',
           color: "#0A284E",
         },
         axisLine: {
@@ -34,25 +41,20 @@ export const getBarOptions = (currentData = []) => {
           show: false,
         },
       },
-      //背景柱体,不显示数据在图表中,只显示背景色
-      // {
-      //   type: "category",
-      //   show: false,
-      //   data: myData6,
-      // },
     ],
     yAxis: [
       {
+        splitNumber: 4,
         type: "value",
         axisLabel: {
           show: true,
-          fontSize: '1.1rem',
-          color: "#7395B3",
+          fontSize: '1.2rem',
+          color: "#7395B3"
         },
         axisLine: {
           show: false,
           lineStyle: {
-            color: "#7FD6FF",
+            color: "#BDD4E8",
           },
         },
         axisTick: {
@@ -63,7 +65,7 @@ export const getBarOptions = (currentData = []) => {
             color: "#BDD4E8",
             type: "dashed",
           },
-        },
+        }
       },
     ],
     series: [
@@ -93,7 +95,7 @@ export const getBarOptions = (currentData = []) => {
         data: currentData,
       },
       {
-        name: "范围值",
+        name: "标准值",
         type: "bar",
         itemStyle: {
           color: {
@@ -135,7 +137,7 @@ export const getBarOptions = (currentData = []) => {
         symbolClip: false,
         symbolSize: [7, 2],
         symbolPosition: 'end',
-        symbolOffset: [-4, 0],
+        symbolOffset: [-5, 0],
         data: currentData,
         width: 1,
         z: 0,