Răsfoiți Sursa

feat: 联调

sunxiao 5 luni în urmă
părinte
comite
ef2b118c25

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "@element-plus/icons-vue": "2.3.1",
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "10.11.0",
+    "animate.css": "^4.1.1",
     "axios": "0.28.1",
     "echarts": "^5.5.1",
     "element-plus": "2.7.6",

+ 88 - 0
src/api/report/lab.js

@@ -0,0 +1,88 @@
+import request from '@/utils/request'
+
+// 日报 - 获取首页的统计信息
+export function getHomeCounts(type) {
+  return request({
+    url: '/business/homeCountsInfo/'+ type,
+    method: 'get'
+  })
+}
+
+// 日报 - 获取所有水厂设备
+export function getWaterFactory() {
+  return request({
+    url: '/business/waterWorkerList',
+    method: 'get'
+  })
+}
+
+// 日报 - 查询报表 - 日报table
+export function getDailyTable(params) {
+  return request({
+    url: '/business/devicePageList',
+    method: 'get',
+    params
+  })
+}
+
+// 日报 - table 弹窗数据
+export function getDrawerData(params) {
+  return request({
+    url: '/business/assayPageListByDeviceNoAndDate',
+    method: 'get',
+    params
+  })
+}
+
+// 月报 - table
+export function getMonthTable(params) {
+  return request({
+    url: '/business/assayCountListByDates',
+    method: 'get',
+    params
+  })
+}
+
+export function getContinuousAssayList (params) {
+  return request({
+    url: '/business/continuousAssayCountByDates',
+    method: 'get',
+    params
+  })
+}
+
+// 获取设备列表
+export function getDeviceList(params) {
+  return request({
+    url: '/business/deviceList',
+    method: 'get',
+    params
+  })
+}
+
+// 实验室月报 - 导出
+export function exportMonthLabData(params) {
+  return request({
+    url: '/business/exportAssayPageListByDeviceNoAndDate',
+    method: 'get',
+    params
+  })
+}
+
+// 连续检测 - 导出
+export function exportDetectionData(params) {
+  return request({
+    url: '/business/exportAssayCountListByDates',
+    method: 'get',
+    params
+  })
+}
+
+// 化验记录 - 导出
+export function exportRecordData(params) {
+  return request({
+    url: '/business/exportContinuousAssayCountByDates',
+    method: 'get',
+    params
+  })
+}

+ 1 - 1
src/assets/styles/sidebar.scss

@@ -25,7 +25,7 @@
     z-index: 1001;
     overflow: hidden;
     -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
-    box-shadow: 2px 0 6px rgba(0,21,41,.35);
+    box-shadow: 2px 0 6px rgba(0,21,41,.1);
 
     // reset element-ui css
     .horizontal-collapse-transition {

+ 4 - 1
src/layout/components/AppMain.vue

@@ -2,7 +2,10 @@
   <section class="app-main">
     <div class="app-main_inner">
       <router-view v-slot="{ Component, route }">
-        <transition name="fade-transform" mode="out-in">
+        <!-- name="fade-transform" -->
+        <transition 
+        enter-active-class="animate__animated animate__backInLeft"
+        leave-active-class="animate__animated animate__backOutRight" mode="out-in">
           <keep-alive :include="tagsViewStore.cachedViews">
             <component v-if="!route.meta.link" :is="Component" :key="route.path"/>
           </keep-alive>

+ 1 - 1
src/layout/components/TagsView/index.vue

@@ -267,7 +267,7 @@ function handleScroll() {
   width: 100%;
   background: #fff;
   border-bottom: 1px solid #d8dce5;
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+  // box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
   .tags-view-wrapper {
     .tags-view-item {
       display: inline-block;

+ 3 - 1
src/main.js

@@ -8,6 +8,7 @@ import locale from 'element-plus/es/locale/lang/zh-cn'
 
 import '@/assets/styles/index.scss';             // global css
 import '@/assets/styles/tailwind.css';           // wailwind css
+import 'animate.css';
 
 import App from './App'
 import store from './store'
@@ -16,7 +17,7 @@ import directive from './directive' // directive
 
 // 注册指令
 import plugins from './plugins' // plugins
-import { download } from '@/utils/request'
+import { download, getDownload } from '@/utils/request'
 
 // svg图标
 import 'virtual:svg-icons-register'
@@ -50,6 +51,7 @@ const app = createApp(App)
 // 全局方法挂载
 app.config.globalProperties.useDict = useDict
 app.config.globalProperties.download = download
+app.config.globalProperties.getDownload = getDownload
 app.config.globalProperties.parseTime = parseTime
 app.config.globalProperties.resetForm = resetForm
 app.config.globalProperties.handleTree = handleTree

+ 30 - 2
src/utils/request.js

@@ -17,7 +17,7 @@ const service = axios.create({
   // axios中请求配置有baseURL选项,表示请求URL公共部分
   baseURL: import.meta.env.VITE_APP_BASE_API,
   // 超时
-  timeout: 10000
+  timeout: 10 * 60 * 1000
 })
 
 // request拦截器
@@ -122,7 +122,7 @@ service.interceptors.response.use(res => {
   }
 )
 
-// 通用下载方法
+// 通用下载方法 - 原始 - post
 export function download(url, params, filename, config) {
   downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
   return service.post(url, params, {
@@ -131,6 +131,7 @@ export function download(url, params, filename, config) {
     responseType: 'blob',
     ...config
   }).then(async (data) => {
+    console.log( data );
     const isBlob = blobValidate(data);
     if (isBlob) {
       const blob = new Blob([data])
@@ -149,4 +150,31 @@ export function download(url, params, filename, config) {
   })
 }
 
+
+// 通用下载方法 - get
+export function getDownload(url, params, filename) {
+  downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
+  return service.get(url + '?' + tansParams(params), {
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+    responseType: 'blob',
+  }).then(async (data) => {
+    const isBlob = blobValidate(data);
+    if (isBlob) {
+      const blob = new Blob([data])
+      saveAs(blob, filename)
+    } else {
+      const resText = await data.text();
+      const rspObj = JSON.parse(resText);
+      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
+      ElMessage.error(errMsg);
+    }
+    downloadLoadingInstance.close();
+  }).catch((r) => {
+    console.error(r)
+    ElMessage.error('下载文件出现错误,请联系管理员!')
+    downloadLoadingInstance.close();
+  })
+}
+
+
 export default service

+ 144 - 95
src/views/report/components/RecordDrawer.vue

@@ -2,66 +2,75 @@
 import { ElMessageBox } from 'element-plus';
 import * as echarts from 'echarts';
 import { getReportLineOptions } from './echartOptions';
+import { getDrawerData } from '@/api/report/lab';
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
 
 const echartInstance = {};
 const listRefs = [];
 
 const drawerVisible = defineModel('show');
 const activeName = ref('report');
-const queryParams = ref({});
-const total = ref(100);
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10
+});
+const total = ref(0);
+const loading = ref(false);
 
-const tableData = [
-  {name1: 1, name2: 2, name3: 3}
-]
+const tableData = ref([]);
 
 const columns = [
   {
     label: '基本资料',
     children: [
-      { label: "化验时间", prop: 'name1', width: 180 },
-      { label: "化验编号", prop: 'name2', width: 180 },
-      { label: "样品名称", prop: 'name3', width: 180 }
+      { label: "化验时间", prop: 'assayTime', width: 180 },
+      { label: "样品名称", prop: 'assayTypeName', width: 180 }
     ]
   },
   {
     label: '高COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'highCodStandardVal', width: 100 },
+      { label: "化验值", prop: 'highCodResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'highCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '低COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'lowCodStandardVal', width: 100 },
+      { label: "化验值", prop: 'lowCodResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'lowCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '氨氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'anDanStandardVal', width: 100 },
+      { label: "化验值", prop: 'anDanResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'anDanDeviationRate', width: 100 }
     ]
   },
   {
     label: '总氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'tnStandardVal', width: 100 },
+      { label: "化验值", prop: 'tnResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'tnDeviationRate', width: 100 }
     ]
   },
   {
     label: '总磷',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'tpStandardVal', width: 100 },
+      { label: "化验值", prop: 'tpResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'tpDeviationRate', width: 100 }
     ]
   },
   {
@@ -75,24 +84,22 @@ const columns = [
   {
     label: '亚硝酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'xsydStandardVal', width: 100 },
+      { label: "化验值", prop: 'xsydResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'xsydDeviationRate', width: 100 }
     ]
   },
   {
     label: '正磷酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'yxsyStandardVal', width: 100 },
+      { label: "化验值", prop: 'yxsyResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'yxsyDeviationRate', width: 100 }
     ]
   }
 ]
 
-const windowResize = () => {
-  Object.keys(echartInstance).forEach(key => echartInstance[key].resize());
-}
+const windowResize = () => Object.keys(echartInstance).forEach(key => echartInstance[key].resize());
 
 const handleClose = (done) => {
   ElMessageBox.confirm('请确认是否关闭?', '提示').then(() => {
@@ -110,17 +117,49 @@ const onRadioChange = key => {
   if ( key === 'echart' ) {
     listRefs.forEach((item, index) => {
       const option = getReportLineOptions();
-      echartInstance[index] = echarts.init(item, 'light');
+      if (!echartInstance[index]) {
+        echartInstance[index] = echarts.init(item, 'light');
+      }
       echartInstance[index].setOption(option);
     });
     window.addEventListener("resize", windowResize);
   }
 }
 
-// 抽屉打开后的回调
-const onDrawerOpened = () => {
+const getList = async () => {
   
-};
+  loading.value = true;
+
+  const { deviceNo, currentDate } = props.data;
+  const { rows, total: tableTotal } = await getDrawerData({ deviceNo, timeBegin: currentDate, timeEnd: currentDate, ...queryParams.value });
+  total.value = tableTotal;
+  tableData.value = rows;
+  loading.value = false;
+}
+
+// 
+const handlePageSize = size => {
+  queryParams.value.pageSize = size;
+  getList();
+}
+
+
+const handlePageNum = size => {
+  queryParams.value.pageNum = size;
+  getList();
+}
+
+
+// 抽屉打开后的回调
+const onDrawerOpened = () => getList();
+
+const onDrawerClosed = () => {
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10
+  };
+  tableData.value = [];
+}
 </script>
 
 <template>
@@ -132,68 +171,79 @@ const onDrawerOpened = () => {
     :size="1100"
     :before-close="handleClose"
     @opened="onDrawerOpened"
+    @closed="onDrawerClosed"
   >
-    <div class="base-data-info">
-      <h4 class="title mb-[10px]">设备日报数据</h4>
-      <div class="board-list">
-        <el-row :gutter="20" >
-          <el-col :span="6">
-            <el-card>
-              <p class="sub-title">设备名称</p>
-              <p>锡林浩特化验室</p>
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card>
-              <p class="sub-title">设备编号</p>
-              <p>A0001</p>
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card>
-              <p class="sub-title">所属水厂</p>
-              <p>锡林浩特化验室</p>
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card>
-              <p class="sub-title">化验日期</p>
-              <p>2024-08-01</p>
-            </el-card>
-          </el-col>
-        </el-row>
+    <main>
+      <div class="base-data-info">
+        <h4 class="title mb-[10px]">设备日报数据</h4>
+        <div class="board-list">
+          <el-row :gutter="20" >
+            <el-col :span="6">
+              <el-card>
+                <p class="sub-title">设备名称</p>
+                <p>{{ data.deviceName }}</p>
+              </el-card>
+            </el-col>
+            <el-col :span="6">
+              <el-card>
+                <p class="sub-title">设备编号</p>
+                <p>{{ data.deviceNo }}</p>
+              </el-card>
+            </el-col>
+            <el-col :span="6">
+              <el-card>
+                <p class="sub-title">所属水厂</p>
+                <p>{{ data.worksName }}</p>
+              </el-card>
+            </el-col>
+            <el-col :span="6">
+              <el-card>
+                <p class="sub-title">化验日期</p>
+                <p>{{ data.currentDate }}</p>
+              </el-card>
+            </el-col>
+          </el-row>
+        </div>
       </div>
-    </div>
+    
+      <div class="mt-[50px]">
+        <div class="flex justify-between items-center mb-[10px]">
+          <h4 class="title">设备日报数据</h4>
+          <el-radio-group v-model="activeName" @change="onRadioChange">
+            <!-- <el-radio-button label="报表" value="report" /> -->
+            <!-- <el-radio-button label="图形" value="echart" /> -->
+          </el-radio-group>
+        </div>
   
-    <div class="mt-[50px]">
-      <div class="flex justify-between items-center mb-[10px]">
-        <h4 class="title">设备日报数据</h4>
-        <el-radio-group v-model="activeName" @change="onRadioChange">
-          <el-radio-button label="报表" value="report" />
-          <el-radio-button label="图形" value="echart" />
-        </el-radio-group>
-      </div>
-
-      <!-- table 区域 -->
-      <el-card  shadow="never" v-show="activeName === 'report'">
-        <el-table :data="tableData" style="width: 100%">
-          <el-table-column :label="col.label" align="center" v-for="col in columns">
-            <el-table-column :prop="child.prop" :label="child.label" :width="child.width" v-for="child in col.children"/>
-          </el-table-column>
-        </el-table>
-        <pagination
-          :total="total"
-          v-model:page="queryParams.pageNum"
-          v-model:limit="queryParams.pageSize"
-          @pagination="getList"
-        />
-      </el-card>
-
-      <!-- ecahrts 区域 -->
-      <div class="echart-list" v-show="activeName === 'echart'">
-        <div v-for="_, index in 8" :ref="getRiskRef"></div>
+        <!-- table 区域 -->
+        <el-card  shadow="never" v-show="activeName === 'report'">
+          <el-table :data="tableData" style="width: 100%" v-loading="loading">
+            <el-table-column :label="col.label" align="center" v-for="col in columns">
+              <el-table-column :prop="child.prop" :label="child.label" :width="child.width" v-for="child in col.children"/>
+            </el-table-column>
+          </el-table>
+          <div class="flex justify-end mt-[20px]">
+            <el-pagination
+              background
+              layout="sizes, prev, pager, next"
+              :page-sizes="[10, 20, 30, 50]"
+              :total="total"
+              v-model:current-page="queryParams.pageNum"
+              :page-size="queryParams.pageSize"
+              size="small"
+              v-if="total > 0"
+              @size-change="handlePageSize"
+              @current-change="handlePageNum"
+            />
+          </div>
+        </el-card>
+  
+        <!-- ecahrts 区域 -->
+        <div class="echart-list" v-show="activeName === 'echart'">
+          <div v-for="_, index in 8" :ref="getRiskRef"></div>
+        </div>
       </div>
-    </div>
+    </main>
   </el-drawer>
 </template>
 
@@ -215,7 +265,6 @@ const onDrawerOpened = () => {
   div {
     border-radius: 2px;
     border: 1px solid #ebeff4;
-    // background: #ebeff4;
   }
 }
 

+ 392 - 0
src/views/report/detection-month/index.vue

@@ -0,0 +1,392 @@
+<script setup>
+import { dayjs } from 'element-plus';
+import { getContinuousAssayList } from '@/api/report/lab';
+
+const { proxy } = getCurrentInstance();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  activeRadioName: 'week'
+});
+
+const tableLoading = ref(false);
+const loading = ref(false);
+
+const datePickerValue = ref(new Date());
+const tempDataValue = ref({timeBegin: dayjs(new Date()).startOf('week').format('YYYY') + '年'});
+
+const tableData = ref([]);
+const columns = ref([]);
+
+const format = computed(() => {
+  return queryParams.value.activeRadioName === 'week' ? tempDataValue.value.timeBegin + ' 第 ww 周'  : 'YYYY-MM'
+})
+
+const label = computed(() => {
+  return queryParams.value.activeRadioName === 'week' ? '选择周' : '选择月'
+})
+
+const onChangeDatePicker = (value) => {
+  const timeBegin = dayjs(value).startOf('week').format('YYYY') + '年';
+  const timeEnd = dayjs(value).endOf('week').format('MM-DD');
+  
+  tempDataValue.value = { timeBegin, timeEnd };
+}
+
+const onRadioChange = () => {
+  datePickerValue.value = new Date();
+}
+
+const getStartTimeAndEndTime = () => {
+  const { activeRadioName } = queryParams.value;
+  const timeBegin = dayjs(datePickerValue.value).startOf(activeRadioName).format('YYYY-MM-DD');
+  const timeEnd = dayjs(datePickerValue.value).endOf(activeRadioName).format('YYYY-MM-DD');
+
+  return {
+    timeBegin,
+    timeEnd
+  }
+}
+
+const initPageData = async() => {
+  tableLoading.value = true;
+  loading.value = true;
+  const dateResult = getStartTimeAndEndTime();
+  const { data } = await getContinuousAssayList({...dateResult});
+  // const data = {
+  //   "二段1池": {
+  //     "正磷酸盐": [
+  //       {
+  //         "assayName": "正磷酸盐",
+  //         "assayCounts": 1,
+  //         "minVal": 1.04,
+  //         "maxVal": 1.04,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 1,
+  //         "minVal": 8.87,
+  //         "maxVal": 8.87,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "回流一池": {
+  //     "正磷酸盐": [
+  //       {
+  //         "assayName": "正磷酸盐",
+  //         "assayCounts": 3,
+  //         "minVal": 1.85,
+  //         "maxVal": 2.16,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 5,
+  //         "minVal": 9.81,
+  //         "maxVal": 11,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "缺氧二池": {
+  //     "氨氮": [
+  //       {
+  //         "assayName": "氨氮",
+  //         "assayCounts": 2,
+  //         "minVal": 13.59,
+  //         "maxVal": 13.61,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 5,
+  //         "minVal": 0.49,
+  //         "maxVal": 0.64,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "二沉池": {
+  //     "正磷酸盐": [
+  //       {
+  //         "assayName": "正磷酸盐",
+  //         "assayCounts": 3,
+  //         "minVal": 0.39,
+  //         "maxVal": 0.8,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "回流二池": {
+  //     "正磷酸盐": [
+  //       {
+  //         "assayName": "正磷酸盐",
+  //         "assayCounts": 3,
+  //         "minVal": 2.08,
+  //         "maxVal": 2.44,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 5,
+  //         "minVal": 11.43,
+  //         "maxVal": 12.22,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "缺氧一池": {
+  //     "氨氮": [
+  //       {
+  //         "assayName": "氨氮",
+  //         "assayCounts": 2,
+  //         "minVal": 11.9,
+  //         "maxVal": 11.97,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 5,
+  //         "minVal": 0.52,
+  //         "maxVal": 0.62,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "二段2池": {
+  //     "正磷酸盐": [
+  //       {
+  //         "assayName": "正磷酸盐",
+  //         "assayCounts": 1,
+  //         "minVal": 1.55,
+  //         "maxVal": 1.55,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ],
+  //     "硝酸盐氮": [
+  //       {
+  //         "assayName": "硝酸盐氮",
+  //         "assayCounts": 1,
+  //         "minVal": 8.24,
+  //         "maxVal": 8.24,
+  //         "passedRates": "100.0%",
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   },
+  //   "total": {
+  //     "合计": [
+  //       {
+  //         "assayName": null,
+  //         "assayCounts": 37,
+  //         "minVal": null,
+  //         "maxVal": null,
+  //         "passedRates": null,
+  //         "bx": "90.00%",
+  //         "ratesDeviation": "10.00%",
+  //         "unitPrice": null,
+  //         "totalPrice": null
+  //       }
+  //     ]
+  //   }
+  // }
+
+  const whiteTableList = [
+    // { title: '检测指标', key: 'assayName' },
+    { title: '实际检测数量', key: 'assayCounts' },
+    { title: '最小值', key: 'minVal' },
+    { title: '最大值', key: 'maxVal' },
+    { title: '合格率(%)', key: 'passedRates' }, 
+    { title: '标线(%)', key: 'bx' },
+    { title: '合格率偏差', key: 'ratesDeviation' }
+  ]
+
+  const tempBodyObject = {};
+  const middleData = Object.entries(data).map(([key, value]) => {
+    const children = Object.entries(value).map(([k, v]) => {
+      const [child] = v;
+      tempBodyObject[key + '-' + k] = child;
+      return {
+        label: k, 
+        prop: key + '-' + k, 
+        width: 160
+      }
+    });
+    
+    return {
+      label: key === 'total' ? '' : key,
+      children
+    }
+  });
+  
+  if ( middleData.length ) {
+    columns.value = [{ label: '检测点位', fixed: "left", children: [{ label: '检测指标', prop: 'title', width: 150 }]}].concat(middleData);
+    tableData.value = whiteTableList.map(item => {
+      const { key } = item;
+      Object.entries(tempBodyObject).map(([k, v]) => {
+        item = {
+          ...item,
+          [k]: v[key]
+        }
+      })
+      return item;
+    });
+  } else {
+    columns.value = [];
+    tableData.value = []
+  }
+  tableLoading.value = false;
+  loading.value = false;
+}
+
+const handlerQuery = () => {
+  initPageData();
+}
+
+const handlerReset = () => {
+  datePickerValue.value = new Date();
+  queryParams.value.activeRadioName = 'week';
+  initPageData();
+}
+
+const handleExport = () => {
+  const dateResult = getStartTimeAndEndTime();
+  proxy.getDownload("/business/exportContinuousAssayCountByDates", {
+    ...dateResult,
+  }, `${new Date().getTime()}.xlsx`);
+}
+
+onMounted(() => {
+  initPageData();
+})
+</script>
+
+<template>
+  
+  <div class="lab-day-viewport space-y-[10px]">
+    <el-card shadow="never" :body-style="{ border: '0px' }">
+      <template #header>
+        <p class="space-x-[10px]">
+          <span class="font-bold">数据筛选</span>
+        </p>
+      </template>
+      <el-row class="pt-[5px]" justify="space-between" :gutter="20">
+        <el-col :span="18">
+          <div class="flex items-center space-x-[20px]">
+            <el-form-item label="统计时段">
+              <el-radio-group v-model="queryParams.activeRadioName" @change="onRadioChange">
+                <el-radio-button label="按周" value="week" />
+                <el-radio-button label="按月" value="month" />
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item :label="label">
+              <el-date-picker
+                v-model="datePickerValue"
+                style="width: 300px"
+                :editable="false"
+                :clearable="false"
+                :type="queryParams.activeRadioName"
+                :format=format
+                :placeholder="'请' + label"
+                @change="onChangeDatePicker"
+              />
+            </el-form-item>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="flex justify-end">
+            <el-button type="primary" @click="handlerQuery" :loading="loading">查询</el-button>
+            <el-button @click="handlerReset" :loading="loading">重置</el-button>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <el-card shadow="never" :body-style="{ padding: '20px' }">
+      <div class="flex justify-between items-center mb-[10px]">
+        <h4 class="font-bold">统计报表</h4>
+        <el-button
+          type="warning"
+          plain
+          icon="Download"
+          @click="handleExport"
+        >导出</el-button>
+      </div>
+      <el-table :data="tableData" style="width: 100%" v-loading="tableLoading">
+        <!-- <el-table-column prop="title" label="检测点位" width="160" fixed="left" header-align="center" /> -->
+        <el-table-column  :label="col.label" :width="col.width || 200" :fixed="col.fixed" align="center" v-for="col, index in columns">
+          <el-table-column :prop="child.prop" :label="child.label" :width="child.width" v-for="child in col.children"/>
+        </el-table-column>
+        <!-- <el-table-column prop="totalDeviationRate" label="总质控合格率" width="120" header-align="center" fixed="right" align="center"/> -->
+      </el-table>
+    </el-card>
+
+  </div>
+  
+</template>
+
+<style lang="scss" scoped></style>

+ 0 - 230
src/views/report/laboratory-day/index.vue

@@ -1,230 +0,0 @@
-<script setup>
-import { RecordDrawer } from '@/views/report/components';
-const activeName = ref('New York');
-const queryParams = ref({});
-const drawerVisible = ref(false);
-const total = ref(100);
-
-const options = [
-  {
-    value: 'Option1',
-    label: 'Option1',
-  },
-  {
-    value: 'Option2',
-    label: 'Option2',
-  },
-  {
-    value: 'Option3',
-    label: 'Option3',
-  },
-  {
-    value: 'Option4',
-    label: 'Option4',
-  },
-  {
-    value: 'Option5',
-    label: 'Option5',
-  },
-]
-
-const tableData = [
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
-  },
-  {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
-  },
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
-  },
-]
-
-// table btn 化验记录
-const handleClick = () => {
-  drawerVisible.value = true;
-}
-
-const getList = () => {
-  console.log( "queryParams.value", queryParams.value );
-}
-
-onMounted(() => {
-  // clientHeight.value = window.innerHeight;
-})
-
-</script>
-
-<template>
-  <div class="lab-day-viewport space-y-[10px]">
-    <el-card shadow="never" :body-style="{ border: '0px' }">
-      <template #header>
-        <p class="space-x-[10px]">
-          <span class="font-bold">今日数据概览</span>
-          <span class="text-[#999]">统计时间 2024-08-01 12:22:59</span>
-        </p>
-      </template>
-      <el-row class="pt-[5px]" justify="space-between" :gutter="20">
-        <el-col :span="4">
-          <el-statistic :value="268500">
-            <template #title>
-              <p class="flex items-center space-x-[4px]">
-                <el-icon :size="14" color="#2454ff"><Management /></el-icon>
-                <span>全国实验室设备总数(台)</span>
-              </p>
-            </template>
-          </el-statistic>
-        </el-col>
-        <el-col :span="4">
-          <el-statistic :value="268500">
-            <template #title>
-              <p class="flex items-center space-x-[4px]">
-                <el-icon :size="14" color="#00bd28"><Checked /></el-icon>
-                <span>今日化验中设备(台)</span>
-              </p>
-            </template>
-          </el-statistic>
-        </el-col>
-        <el-col :span="4">
-          <el-statistic title="今日待机设备(台)" :value="268500">
-            <template #title>
-              <p class="flex items-center space-x-[4px]">
-                <el-icon :size="14" color="#e5a528"><List /></el-icon>
-                <span>今日待机设备(台)</span>
-              </p>
-            </template>
-          </el-statistic>
-        </el-col>
-        <el-col :span="4">
-          <el-statistic title="今日离线设备(台)" :value="268500">
-            <template #title>
-              <p class="flex items-center space-x-[4px]">
-                <el-icon :size="14" color="#e31518"><TrendCharts /></el-icon>
-                <span>今日离线设备(台)</span>
-              </p>
-            </template>
-          </el-statistic>
-        </el-col>
-        <el-col :span="4">
-          <el-statistic title="今日质控样合格率" :value="268500">
-            <template #title>
-              <p class="flex items-center space-x-[4px]">
-                <el-icon :size="14" color="#8C40FF"><Histogram /></el-icon>
-                <span>今日质控样合格率</span>
-              </p>
-            </template>
-            <template #suffix>
-              <el-popover
-                effect="dark"
-                placement="top-start"
-                :width="200"
-                trigger="hover"
-              >
-                <template #reference>
-                  <el-icon style="margin-left: 4px" :size="14">
-                    <Warning />
-                  </el-icon>
-                </template>
-                <ul>
-                  <li>
-                    <span class="text-[12px]">今日质控样检测总数</span>
-                    <span class="font-bold text-[14px]"> 1000 </span>
-                    <span class="text-[12px]">次</span>
-                  </li>
-                  <li>
-                    <span class="text-[12px]">今日质控样合格总数</span>
-                    <span class="font-bold text-[14px]"> 1000 </span>
-                    <span class="text-[12px]">次</span>
-                  </li>
-                </ul>
-              </el-popover>
-            </template>
-          </el-statistic>
-        </el-col>
-      </el-row>
-    </el-card>
-    
-    <el-card shadow="never" :body-style="{ padding: '20px' }">
-      <div class="flex justify-between items-center mb-[10px]">
-        <el-radio-group v-model="activeName">
-          <el-radio-button label="全部设备" value="New York" />
-          <el-radio-button label="化验中" value="Washington" />
-          <el-radio-button label="待机中" value="Los Angeles" />
-          <el-radio-button label="离线中" value="Chicago" />
-        </el-radio-group>
-        <div class="space-x-[20px]">
-          <el-date-picker
-            type="date"
-            v-model="activeName"
-            placeholder="选择日期"
-            style="width: 220px"
-          />
-          <el-select
-            v-model="value"
-            placeholder="选择水厂"
-            style="width: 220px"
-          >
-            <el-option
-              v-for="item in options"
-              :key="item.value"
-              :label="item.label"
-              :value="item.value"
-            />
-          </el-select>
-        </div>
-      </div>
-      <el-table :data="tableData" style="width: 100%">
-        <el-table-column prop="date" label="设备名称" width="180" fixed/>
-        <el-table-column prop="name" label="设备编号" width="180" />
-        <el-table-column prop="address" label="所属水厂" width="180"/>
-        <el-table-column prop="address" label="当前设备状态" width="180">
-          <p class="flex items-center space-x-[4px]">
-            <span class="block w-[6px] h-[6px] rounded-full bg-[red]"></span>
-            <span>运行中</span>
-          </p>
-        </el-table-column>
-        <el-table-column prop="address" label="最近一次化验时间" width="180"/>
-        <el-table-column prop="address" label="化验开始时间" width="180"/>
-        <el-table-column prop="address" label="化验进度" width="180">
-          <el-progress :percentage="50" />
-        </el-table-column>
-        <el-table-column prop="address" label="检测轮次" width="180"/>
-        <el-table-column prop="address" label="检测总次数" width="180"/>
-        <el-table-column prop="address" label="合格率(质控样)" width="180"/>
-        <el-table-column prop="address" label="当前温度" width="180"/>
-        <el-table-column prop="address" label="当前湿度" width="180"/>
-        <el-table-column prop="address" label="废液产生总量" width="180"/>
-        <el-table-column prop="address" label="操作" fixed="right">
-          <el-button link type="primary" size="small" @click="handleClick">化验记录</el-button>
-        </el-table-column>
-      </el-table>
-      <div class="">
-        <pagination
-          v-show="total > 0"
-          :total="total"
-          v-model:page="queryParams.pageNum"
-          v-model:limit="queryParams.pageSize"
-          @pagination="getList"
-        />
-      </div>
-    </el-card>
-  </div>
-  <RecordDrawer v-model:show="drawerVisible"></RecordDrawer>
-</template>
-
-<style lang="scss">
-.lab-day-viewport {
-  font-size: 14px;
-
-  .el-statistic__number {
-    font-size: 22px;
-    font-family: D-DIN-PRO-700-Bold;
-  }
-}
-</style>

+ 91 - 60
src/views/report/laboratory-month/index.vue

@@ -1,64 +1,67 @@
 <script setup>
 import { dayjs } from 'element-plus';
-const activeRadioName = ref('week');
-const queryParams = ref({});
-const total = ref(100);
+import { getMonthTable } from '@/api/report/lab';
 
-const value1 = ref();
-const datePickerVal = ref({})
+const { proxy } = getCurrentInstance();
 
+const queryParams = ref({
+  activeRadioName: 'week'
+});
+
+const tableLoading = ref(false);
+const loading = ref(false);
+
+const datePickerValue = ref(new Date());
+const tempDataValue = ref({timeBegin: dayjs(new Date()).startOf('week').format('YYYY') + '年'});
+
+const tableData = ref([]);
 const columns = [
-  {
-    label: '用户',
-    width: 200,
-    fixed: true,
-  },
   {
     label: '高COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'highCodArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'highCodPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'highCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '低COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'lowCodArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'lowCodPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'lowCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '氨氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'anDanArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'anDanPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'anDanDeviationRate', width: 100 }
     ]
   },
   {
     label: '总氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'tnArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'tnPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'tnDeviationRate', width: 100 }
     ]
   },
   {
     label: '总磷',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'tpArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'tpPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'tpDeviationRate', width: 100 }
     ]
   },
   {
     label: '硝酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'xsydArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'xsydPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'xsydDeviationRate', width: 100 }
     ]
   },
   {
@@ -72,40 +75,74 @@ const columns = [
   {
     label: '正磷酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "质控次数", prop: 'yxsyArrayCounts', width: 100 },
+      { label: "质控合格数", prop: 'yxsyPassedCounts', width: 100 },
+      { label: "质控合格率", prop: 'yxsyDeviationRate', width: 100 }
     ]
   },
-  {
-    label: '质控合格率',
-    width: 100,
-    fixed: 'right',
-  },
 ]
 
 const format = computed(() => {
-  return activeRadioName.value === 'week' ? datePickerVal.value.startDate + ' 第 ww 周'  : 'YYYY-MM'
+  return queryParams.value.activeRadioName === 'week' ? tempDataValue.value.timeBegin + ' 第 ww 周'  : 'YYYY-MM'
 })
 
 const label = computed(() => {
-  return activeRadioName.value === 'week' ? '选择周' : '选择月'
+  return queryParams.value.activeRadioName === 'week' ? '选择周' : '选择月'
 })
 
 const onChangeDatePicker = (value) => {
-  const startDate = dayjs(value).startOf('week').format('YYYY') + '年';
-  const endDate = dayjs(value).endOf('week').format('MM-DD');
-
-  datePickerVal.value = { startDate, endDate };
+  const timeBegin = dayjs(value).startOf('week').format('YYYY') + '年';
+  const timeEnd = dayjs(value).endOf('week').format('MM-DD');
+  
+  tempDataValue.value = { timeBegin, timeEnd };
 }
 
 const onRadioChange = () => {
-  value1.value = null
+  datePickerValue.value = new Date();
+}
+
+const getStartTimeAndEndTime = () => {
+  const { activeRadioName } = queryParams.value;
+  const timeBegin = dayjs(datePickerValue.value).startOf(activeRadioName).format('YYYY-MM-DD');
+  const timeEnd = dayjs(datePickerValue.value).endOf(activeRadioName).format('YYYY-MM-DD');
+
+  return {
+    timeBegin,
+    timeEnd
+  }
+}
+
+const initPageData = async() => {
+  tableLoading.value = true;
+  loading.value = true;
+  const dateResult = getStartTimeAndEndTime();
+  const { data } = await getMonthTable({...dateResult, ...queryParams.value});
+  tableData.value = data || [];
+  tableLoading.value = false;
+  loading.value = false;
+}
+
+const handlerQuery = () => {
+  initPageData();
+}
+
+const handlerReset = () => {
+  datePickerValue.value = new Date();
+  queryParams.value.activeRadioName = 'week'
 }
 
 const handleExport = () => {
+  
+  const dateResult = getStartTimeAndEndTime();
 
+  proxy.getDownload("/business/exportAssayPageListByDeviceNoAndDate", {
+    ...dateResult,
+  }, `${new Date().getTime()}.xlsx`);
 }
+
+onMounted(() => {
+  initPageData();
+})
 </script>
 
 <template>
@@ -118,20 +155,21 @@ const handleExport = () => {
         </p>
       </template>
       <el-row class="pt-[5px]" justify="space-between" :gutter="20">
-        <el-col :span="12">
+        <el-col :span="18">
           <div class="flex items-center space-x-[20px]">
             <el-form-item label="统计时段">
-              <el-radio-group v-model="activeRadioName" @change="onRadioChange">
+              <el-radio-group v-model="queryParams.activeRadioName" @change="onRadioChange">
                 <el-radio-button label="按周" value="week" />
                 <el-radio-button label="按月" value="month" />
               </el-radio-group>
             </el-form-item>
             <el-form-item :label="label">
               <el-date-picker
-                v-model="value1"
+                v-model="datePickerValue"
                 style="width: 300px"
                 :editable="false"
-                :type="activeRadioName"
+                :clearable="false"
+                :type="queryParams.activeRadioName"
                 :format=format
                 :placeholder="'请' + label"
                 @change="onChangeDatePicker"
@@ -139,10 +177,10 @@ const handleExport = () => {
             </el-form-item>
           </div>
         </el-col>
-        <el-col :span="6" :offset="6">
+        <el-col :span="6">
           <div class="flex justify-end">
-            <el-button type="primary">查询</el-button>
-            <el-button>重置</el-button>
+            <el-button type="primary" @click="handlerQuery" :loading="loading">查询</el-button>
+            <el-button @click="handlerReset" :loading="loading">重置</el-button>
           </div>
         </el-col>
       </el-row>
@@ -158,20 +196,13 @@ const handleExport = () => {
           @click="handleExport"
         >导出</el-button>
       </div>
-      <el-table :data="tableData" style="width: 100%">
+      <el-table :data="tableData" style="width: 100%" v-loading="tableLoading">
+        <el-table-column prop="worksName" label="用户" width="200" fixed="left" header-align="center" />
         <el-table-column :label="col.label" :width="col.width" :fixed="col.fixed" align="center" v-for="col in columns">
           <el-table-column :prop="child.prop" :label="child.label" :width="child.width" v-for="child in col.children"/>
         </el-table-column>
+        <el-table-column prop="totalDeviationRate" label="总质控合格率" width="120" header-align="center" fixed="right" align="center"/>
       </el-table>
-      <div class="">
-        <pagination
-          v-show="total > 0"
-          :total="total"
-          v-model:page="queryParams.pageNum"
-          v-model:limit="queryParams.pageSize"
-          @pagination="getList"
-        />
-      </div>
     </el-card>
 
   </div>

+ 106 - 44
src/views/report/record/index.vue

@@ -1,80 +1,142 @@
 <script setup>
-const queryParams = ref({});
-const total = ref(100);
+import { getDeviceList, getDrawerData } from '@/api/report/lab';
+import { dayjs } from 'element-plus';
+
+const { proxy } = getCurrentInstance();
+
+const queryParams = ref({ 
+  deviceNo: '',
+  pageNum: 1,
+  pageSize: 10
+ });
+
+const datePieckerValue = ref([dayjs(new Date()).format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]);
+
+const total = ref(0);
+const tableData = ref([]);
+const loading = ref(false);
+const options = ref([]);
 const columns = [
   {
     label: '基本资料',
     children: [
-      { label: "化验时间", prop: 'name1', width: 180 },
-      { label: "化验编号", prop: 'name2', width: 180 },
-      { label: "样品名称", prop: 'name3', width: 180 }
+      { label: "化验时间", prop: 'assayTime', width: 180 },
+      { label: "设备名称", prop: 'deviceName', width: 180 },
+      { label: "设备编号", prop: 'deviceNo', width: 180 },
+      { label: "所属水厂", prop: 'worksName', width: 180 },
+      { label: "样品名称", prop: 'assayTypeName', width: 180 }
     ]
   },
   {
     label: '高COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'highCodStandardVal', width: 100 },
+      { label: "化验值", prop: 'highCodResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'highCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '低COD',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'lowCodStandardVal', width: 100 },
+      { label: "化验值", prop: 'lowCodResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'lowCodDeviationRate', width: 100 }
     ]
   },
   {
     label: '氨氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'anDanStandardVal', width: 100 },
+      { label: "化验值", prop: 'anDanResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'anDanDeviationRate', width: 100 }
     ]
   },
   {
     label: '总氮',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'tnStandardVal', width: 100 },
+      { label: "化验值", prop: 'tnResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'tnDeviationRate', width: 100 }
     ]
   },
   {
     label: '总磷',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'tpStandardVal', width: 100 },
+      { label: "化验值", prop: 'tpResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'tpDeviationRate', width: 100 }
     ]
   },
   {
     label: '硝酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'xsydStandardVal', width: 100 },
+      { label: "化验值", prop: 'xsydResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'xsydDeviationRate', width: 100 }
     ]
   },
   {
     label: '亚硝酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'yxsyStandardVal', width: 100 },
+      { label: "化验值", prop: 'yxsyResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'yxsyDeviationRate', width: 100 }
     ]
   },
   {
     label: '正磷酸盐',
     children: [
-      { label: "标称值", prop: 'name', width: 100 },
-      { label: "化验值", prop: 'name', width: 100 },
-      { label: "偏差度", prop: 'name', width: 100 }
+      { label: "标称值", prop: 'zlsyStandardVal', width: 100 },
+      { label: "化验值", prop: 'zlsyResultConcentration', width: 100 },
+      { label: "偏差度", prop: 'zlsyDeviationRate', width: 100 }
     ]
   }
 ]
+
+const initPageData = async () => {
+
+  loading.value = true;
+
+  const [ timeBegin, timeEnd ] = datePieckerValue.value;
+  const { total: tableTotal, rows } = await getDrawerData({ ...queryParams.value, timeBegin, timeEnd });
+  
+  tableData.value = rows;
+  total.value = tableTotal;
+  loading.value = false;
+
+}
+
+// 查询
+const handleQuery = async() => {
+  initPageData();
+}
+
+// 重置
+const handleReset = () => {
+  datePieckerValue.value = [];
+  queryParams.deviceNo.timeBegin = '';
+  queryParams.deviceNo.timeEnd = '';
+  queryParams.deviceNo.deviceNo = '';
+}
+
+// 导出
+const handleExport = () => {
+  const [ timeBegin, timeEnd ] = datePieckerValue.value;
+  
+  proxy.getDownload("/business/exportAssayCountListByDates", {
+    timeBegin, timeEnd, deviceNo: queryParams.value.deviceNo
+  }, `${new Date().getTime()}.xlsx`);
+}
+
+onMounted(async () => {
+  await getDeviceList().then(({ data }) => {
+    const [ item ] = data;
+    queryParams.value.deviceNo = item?.deviceNo;
+    options.value = data
+  });
+  
+  getDrawerData();
+})
 </script>
 
 <template>
@@ -86,17 +148,10 @@ const columns = [
         </p>
       </template>
       <el-row class="pt-[5px]" justify="space-between" :gutter="20">
-        <el-col :span="6">
-          <el-form-item label="水厂名称">
-            <el-select v-model="value" placeholder="Select">
-              <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
-            </el-select>
-          </el-form-item>
-        </el-col>
         <el-col :span="6">
           <el-form-item label="设备编号">
-            <el-select v-model="value" placeholder="Select">
-              <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+            <el-select v-model="queryParams.deviceNo" placeholder="请选择" filterable>
+              <el-option v-for="item in options" :key="item.deviceNo" :label="item.deviceName" :value="item.deviceNo" />
             </el-select>
           </el-form-item>
         </el-col>
@@ -105,14 +160,21 @@ const columns = [
             <template #label>
               <span class="font-bold">化验日期</span>
             </template>
-            <el-date-picker v-model="value1" type="daterange" range-separator="-" start-placeholder="Start date"
-              end-placeholder="End date" />
+            <el-date-picker
+              v-model="datePieckerValue"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :editable="false"
+              value-format="YYYY-MM-DD"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="6">
           <div class="flex justify-end">
-            <el-button type="primary">查询</el-button>
-            <el-button>重置</el-button>
+            <el-button type="primary" @click="handleQuery" :loading="loading">查询</el-button>
+            <el-button @click="handleReset" :loading="loading">重置</el-button>
           </div>
         </el-col>
       </el-row>
@@ -123,14 +185,14 @@ const columns = [
         <h4 class="font-bold">化验记录</h4>
         <el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
       </div>
-      <el-table :data="tableData" style="width: 100%">
+      <el-table :data="tableData" style="width: 100%" v-loading="loading">
         <el-table-column :label="col.label" :width="col.width" :fixed="col.fixed" align="center" v-for="col in columns">
           <el-table-column :prop="child.prop" :label="child.label" :width="child.width" v-for="child in col.children" />
         </el-table-column>
       </el-table>
       <div class="">
         <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-          v-model:limit="queryParams.pageSize" @pagination="getList" />
+          v-model:limit="queryParams.pageSize" @pagination="initPageData" />
       </div>
     </el-card>
   </div>

+ 280 - 0
src/views/report/report-daily/index.vue

@@ -0,0 +1,280 @@
+<script setup>
+import { RecordDrawer } from '@/views/report/components';
+import { getHomeCounts, getWaterFactory, getDailyTable } from '@/api/report/lab';
+import { dayjs } from 'element-plus';
+
+const route = useRoute();
+
+const drawerVisible = ref(false);
+const tableLoading = ref(false);
+const total = ref(0);
+const selectOptions = ref({});
+const tableData = ref([]);
+const drawerData = ref({});
+
+const deviceNameEnum = {
+  "0": "待机中",
+  "1": "离线中",
+  "2": "化验中"
+}
+
+const pageCountNums = ref({
+  deviceTotals: 0,
+  onDeviceCounts: 0,
+  standbyDeviceCounts: 0,
+  offDeviceCounts: 0,
+  passAssayRates: 0,
+  assayTotals: 0,
+  passAssayTotals: 0
+});
+
+const queryParams = ref({ 
+  assayStatus: '',
+  assayDate: dayjs().format("YYYY-MM-DD"),
+  // assayDate: '2024-08-13',
+  worksId: null,
+  type: 1,
+  pageNum: 1,
+  pageSize: 10
+ });
+
+const getDeviceClass = ({ assayStatus: key }) => {
+  const colorEnum = {
+    "0": "bg-[#FADB14]",
+    "1": "bg-[#F5222D]",
+    "2": "bg-[#52c41a]"
+  }
+  const color = colorEnum[key]
+  return 'block w-[8px] h-[8px] rounded-full ' + color;
+}
+
+const initPageData = async () => {
+  tableLoading.value = true;
+  const { total: pageTotal, rows } = await getDailyTable(queryParams.value);
+  
+  total.value = pageTotal;
+  tableData.value = rows.map(item => ({
+    ...item,
+    // passAssayRates: item.passAssayRates ? item.passAssayRates.toFixed(2) + '%' : '',
+    currentTemperature: item.currentTemperature ? item.currentTemperature + '°' : '',
+    // currentHumidity: item.currentHumidity ? item.currentHumidity + '%' : '',
+    wasteLiquidAmounts: item.wasteLiquidAmounts ? item.wasteLiquidAmounts + 'L' : '',
+  }));
+  tableLoading.value = false;
+}
+
+const handleDatePickerChange = () => initPageData();
+
+const handleSelectChange = () => initPageData();
+
+const handleRadioChange = () => initPageData();
+
+// table btn 化验记录
+const handleTableBtnClick = async (row) => {
+  const { deviceNo, deviceName, worksName } = row;
+  const currentDate = queryParams.value.assayDate;
+
+  drawerData.value = {
+    deviceName, deviceNo, worksName, currentDate
+  }
+
+  drawerVisible.value = true;
+}
+
+onMounted( async () => {
+  const { type } = route.query;
+
+  initPageData();
+
+  const { data } = await getHomeCounts(type);
+  const { data: waterFactoryData } = await getWaterFactory();
+
+  pageCountNums.value = data;
+  selectOptions.value = waterFactoryData;
+  queryParams.value.type = type;
+})
+
+</script>
+
+<template>
+  <main>
+    <div class="lab-day-viewport space-y-[10px]">
+      <el-card shadow="never" :body-style="{ border: '0px' }">
+        <template #header>
+          <p class="space-x-[10px]">
+            <span class="font-bold">今日数据概览</span>
+            <span class="text-[#999]">统计时间 2024-08-01 12:22:59</span>
+          </p>
+        </template>
+        <el-row class="pt-[5px]" justify="space-between" :gutter="20">
+          <el-col :span="4">
+            <el-statistic :value="pageCountNums.deviceTotals">
+              <template #title>
+                <p class="flex items-center space-x-[4px]">
+                  <el-icon :size="14" color="#2454ff"><Management /></el-icon>
+                  <span>全国实验室设备总数(台)</span>
+                </p>
+              </template>
+            </el-statistic>
+          </el-col>
+          <el-col :span="4">
+            <el-statistic :value="pageCountNums.onDeviceCounts">
+              <template #title>
+                <p class="flex items-center space-x-[4px]">
+                  <el-icon :size="14" color="#00bd28"><Checked /></el-icon>
+                  <span>今日化验中设备(台)</span>
+                </p>
+              </template>
+            </el-statistic>
+          </el-col>
+          <el-col :span="4">
+            <el-statistic title="今日待机设备(台)" :value="pageCountNums.standbyDeviceCounts">
+              <template #title>
+                <p class="flex items-center space-x-[4px]">
+                  <el-icon :size="14" color="#e5a528"><List /></el-icon>
+                  <span>今日待机设备(台)</span>
+                </p>
+              </template>
+            </el-statistic>
+          </el-col>
+          <el-col :span="4">
+            <el-statistic title="今日离线设备(台)" :value="pageCountNums.offDeviceCounts">
+              <template #title>
+                <p class="flex items-center space-x-[4px]">
+                  <el-icon :size="14" color="#e31518"><TrendCharts /></el-icon>
+                  <span>今日离线设备(台)</span>
+                </p>
+              </template>
+            </el-statistic>
+          </el-col>
+          <el-col :span="4">
+            <el-statistic title="今日质控样合格率" :value="pageCountNums.passAssayRates">
+              <template #title>
+                <p class="flex items-center space-x-[4px]">
+                  <el-icon :size="14" color="#8C40FF"><Histogram /></el-icon>
+                  <span>今日质控样合格率</span>
+                </p>
+              </template>
+              <template #suffix>
+                <el-popover
+                  effect="dark"
+                  placement="top-start"
+                  :width="200"
+                  trigger="hover"
+                >
+                  <template #reference>
+                    <el-icon style="margin-left: 4px" :size="14">
+                      <Warning />
+                    </el-icon>
+                  </template>
+                  <ul>
+                    <li class="space-x-[4px]">
+                      <span class="text-[12px]">今日质控样检测总数</span>
+                      <span class="font-bold text-[14px]"> {{ pageCountNums.assayTotals || 0 }} </span>
+                      <span class="text-[12px]">次</span>
+                    </li>
+                    <li class="space-x-[4px]">
+                      <span class="text-[12px]">今日质控样合格总数</span>
+                      <span class="font-bold text-[14px]"> {{ pageCountNums.passAssayTotals || 0 }} </span>
+                      <span class="text-[12px]">次</span>
+                    </li>
+                  </ul>
+                </el-popover>
+              </template>
+            </el-statistic>
+          </el-col>
+        </el-row>
+      </el-card>
+      
+      <el-card shadow="never" :body-style="{ padding: '20px' }">
+        <div class="flex justify-between items-center mb-[10px]">
+          <el-radio-group v-model="queryParams.assayStatus" @change="handleRadioChange">
+            <el-radio-button label="全部设备" value="" />
+            <el-radio-button label="待机中" value="0" />
+            <el-radio-button label="离线中" value="1" />
+            <el-radio-button label="化验中" value="2" />
+          </el-radio-group>
+          <div class="space-x-[20px]">
+            <el-date-picker
+              type="date"
+              :clearable="false"
+              v-model="queryParams.assayDate"
+              value-format="YYYY-MM-DD"
+              placeholder="选择日期"
+              style="width: 220px"
+              @change="handleDatePickerChange"
+            />
+            <el-select
+              placeholder="选择水厂"
+              style="width: 220px"
+              clearable
+              filterable
+              v-model="queryParams.worksId"
+              @change="handleSelectChange"
+            >
+              <el-option
+                v-for="item in selectOptions"
+                :key="item.worksId"
+                :label="item.worksName"
+                :value="item.worksId"
+              />
+            </el-select>
+          </div>
+        </div>
+        <el-table :data="tableData" style="width: 100%" v-loading="tableLoading">
+          <el-table-column prop="deviceName" label="设备名称" width="180" fixed/>
+          <el-table-column prop="deviceNo" label="设备编号" width="180" />
+          <el-table-column prop="worksName" label="所属水厂" width="180"/>
+          <el-table-column prop="assayStatus" label="当前设备状态" width="180">
+            <template #default="{ row }">
+              <p class="flex items-center space-x-[8px]">
+                <span :class="getDeviceClass(row)"></span>
+                <span>{{ deviceNameEnum[row.assayStatus] }}</span>
+              </p>
+            </template>
+          </el-table-column>
+          <el-table-column prop="lastAssayTime" label="最近一次化验时间" width="180"/>
+          <el-table-column prop="beginAssayTime" label="化验开始时间" width="180"/>
+          <el-table-column prop="assayProgress" label="化验进度" width="180">
+            <template #default="{ row }">
+              <el-progress :percentage="row.assayProgress || 0" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="assayCounts" label="检测轮次" width="180"/>
+          <el-table-column prop="assayTotals" label="检测总次数" width="180"/>
+          <el-table-column prop="passAssayRates" label="合格率(质控样)" width="180" />
+          <el-table-column prop="currentTemperature" label="当前温度" width="180"/>
+          <el-table-column prop="currentHumidity" label="当前湿度" width="180"/>
+          <el-table-column prop="wasteLiquidAmounts" label="废液产生总量" width="180"/>
+          <el-table-column prop="address" label="操作" fixed="right">
+            <template #default="{ row }">
+              <el-button link type="primary" size="small" @click="handleTableBtnClick(row)">化验记录</el-button>
+            </template>
+            
+          </el-table-column>
+        </el-table>
+        <div class="">
+          <pagination
+            v-show="total > 0"
+            :total="total"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            @pagination="initPageData"
+          />
+        </div>
+      </el-card>
+    </div>
+    <RecordDrawer v-model:show="drawerVisible" :data="drawerData"></RecordDrawer>
+  </main>
+</template>
+
+<style lang="scss">
+.lab-day-viewport {
+  font-size: 14px;
+
+  .el-statistic__number {
+    font-size: 22px;
+    font-family: D-DIN-PRO-700-Bold;
+  }
+}
+</style>