From cca8d423c4805665bbd48a47e4d9218b16d14ebb Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 28 三月 2025 17:42:35 +0800
Subject: [PATCH] 新增自动评估监测数据上传功能(待完成)

---
 src/api/fysp/sceneApi.js                                    |   19 ++
 src/views/fysp/evaluation/components/CompDataResultEdit.vue |  335 +++++++++++++++++++++++++++++++++++++++++++++--
 src/utils/time-util.js                                      |   46 ++++--
 3 files changed, 366 insertions(+), 34 deletions(-)

diff --git a/src/api/fysp/sceneApi.js b/src/api/fysp/sceneApi.js
index a5159c3..2d51a0c 100644
--- a/src/api/fysp/sceneApi.js
+++ b/src/api/fysp/sceneApi.js
@@ -35,21 +35,27 @@
       subScene: subScene ? JSON.stringify(subScene) : null,
       sceneDevice: sceneDevice ? sceneDevice : null
     };
-    return $fysp.post(`scense/detail/update?${params}`, rb).then((res) => res.data);
+    return $fysp
+      .post(`scense/detail/update?${params}`, rb)
+      .then((res) => res.data);
   },
 
   /**
    * 鏇存柊鍦烘櫙棰濆淇℃伅
    */
   updateSubScene(typeId, subScene) {
-    return this.updateSceneDetail(typeId, { subScene: subScene }).then((res) => res.data);
+    return this.updateSceneDetail(typeId, { subScene: subScene }).then(
+      (res) => res.data
+    );
   },
 
   /**
    * 鏇存柊鍦烘櫙璁惧淇℃伅
    */
   updateSceneDevice(typeId, sceneDevice) {
-    return this.updateSceneDetail(typeId, { sceneDevice: sceneDevice }).then((res) => res.data);
+    return this.updateSceneDetail(typeId, { sceneDevice: sceneDevice }).then(
+      (res) => res.data
+    );
   },
 
   /**
@@ -80,5 +86,12 @@
    */
   getAllScene() {
     return $fysp.get('scense/alltype').then((res) => res.data);
+  },
+
+  /**
+   * 鏌ユ壘鍦烘櫙
+   */
+  findScene(scene) {
+    return $fysp.post('scense/search', scene).then((res) => res.data);
   }
 };
diff --git a/src/utils/time-util.js b/src/utils/time-util.js
index 951116b..dec4073 100644
--- a/src/utils/time-util.js
+++ b/src/utils/time-util.js
@@ -1,39 +1,55 @@
-import dayjs from "dayjs";
+import dayjs from 'dayjs';
 
 export default {
   format(date, template) {
-    return dayjs(date).format(template)
+    return dayjs(date).format(template);
   },
 
-  formatH(date){
+  formatH(date) {
     if (date) {
-      return this.format(date, 'HH:mm:ss')
+      return this.format(date, 'HH:mm:ss');
     } else {
-      return '--:--:--'
+      return '--:--:--';
     }
   },
 
-  formatYM(date){
+  formatYM(date) {
     if (date) {
-      return this.format(date, 'YYYY-MM')
+      return this.format(date, 'YYYY-MM');
     } else {
-      return '----/--'
+      return '----/--';
     }
   },
 
-  formatYMD(date){
+  formatYMD(date) {
     if (date) {
-      return this.format(date, 'YYYY-MM-DD')
+      return this.format(date, 'YYYY-MM-DD');
     } else {
-      return '----/--/--'
+      return '----/--/--';
     }
   },
 
-  formatYMDH(date){
+  formatYMDH(date) {
     if (date) {
-      return this.format(date, 'YYYY-MM-DD HH:mm:ss')
+      return this.format(date, 'YYYY-MM-DD HH:mm:ss');
     } else {
-      return '----/--/-- --:--:--'
+      return '----/--/-- --:--:--';
     }
   },
-}
\ No newline at end of file
+
+  formatDateFromExcel(num, format) {
+    const old = num - 1;
+    const t = Math.round((old - Math.floor(old)) * 24 * 60 * 60);
+    const time = new Date(1900, 0, old, 0, 0, t);
+    const year = time.getFullYear();
+    const month = time.getMonth() + 1;
+    const date = time.getDate();
+    return (
+      year +
+      format +
+      (month < 10 ? '0' + month : month) +
+      format +
+      (date < 10 ? '0' + date : date)
+    );
+  }
+};
diff --git a/src/views/fysp/evaluation/components/CompDataResultEdit.vue b/src/views/fysp/evaluation/components/CompDataResultEdit.vue
index 8837f56..fbfaf1c 100644
--- a/src/views/fysp/evaluation/components/CompDataResultEdit.vue
+++ b/src/views/fysp/evaluation/components/CompDataResultEdit.vue
@@ -4,61 +4,245 @@
       ref="upload"
       class="upload-file"
       :limit="1"
+      accept=".xls,.xlsx"
       :on-change="handleChange"
       :on-exceed="handleExceed"
       :auto-upload="false"
     >
       <template #trigger>
-        <el-button type="primary">涓婁紶鐩戞祴鏁版嵁缁熻缁撴灉</el-button>
+        <el-button type="primary" :loading="tableLoading"
+          >涓婁紶鐩戞祴鏁版嵁缁熻缁撴灉</el-button
+        >
+      </template>
+      <template #tip>
+        <div>
+          <el-text type="danger">{{ tips }}</el-text>
+        </div>
       </template>
     </el-upload>
-    <el-text>{{ loadTxt }}</el-text>
+    <div v-if="tableLoading">
+      <el-icon class="is-loading"><Loading /></el-icon>
+      <el-text>{{ loadTxt }}</el-text>
+    </div>
   </el-row>
   <el-table
     ref="tableRef"
     :data="data"
-    v-loading="loading"
+    v-loading="tableLoading"
     table-layout="fixed"
-    :stripe="true"
+    row-key="id"
+    :expand-row-keys="expandRowKeys"
+    :row-class-name="tableRowClassName"
     size="small"
     height="60vh"
     border
   >
+    <!-- <el-table-column type="expand">
+      <template #default="{ row }">
+        {{ row.drSceneName }}
+      </template>
+    </el-table-column> -->
+    <el-table-column
+      v-if="isUploadNewFile"
+      prop="isFound"
+      label="鍚堣"
+      width="30"
+    >
+      <template #default="{ row }">
+        <el-icon class="is-loading" v-if="row.loading">
+          <Loading color="#409eff" />
+        </el-icon>
+        <el-icon v-else>
+          <Check v-if="row.isFound" />
+          <Close v-else />
+        </el-icon>
+      </template>
+    </el-table-column>
     <el-table-column
       :show-overflow-tooltip="true"
       prop="drSceneName"
       label="鍚嶇О"
       width="300"
-    />
-    <el-table-column prop="drDeviceCode" label="璁惧鍙�" width="130" />
+    >
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drSceneName"
+          @change="(e) => handleSceneNameChange(e, row)"
+        />
+        <span v-else>{{ row.drSceneName }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drDeviceCode" label="璁惧鍙�" width="130">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drDeviceCode"
+        />
+        <span v-else>{{ row.drDeviceCode }}</span>
+      </template>
+    </el-table-column>
     <el-table-column prop="drTime" label="鏃堕棿" width="100">
       <template #default="{ row }">
         <span>{{ $fm.formatYMD(row.drTime) }}</span>
       </template>
     </el-table-column>
-    <el-table-column prop="drExceedTimes" label="瓒呮爣娆℃暟" />
-    <el-table-column prop="drAvg" label="骞冲潎鍊�" />
-    <el-table-column prop="drMax" label="鏈�澶у��" />
-    <el-table-column prop="drMin" label="鏈�灏忓��" />
-    <el-table-column prop="drOverAvgPer" label="瓒呭尯鍧囧�肩櫨鍒嗘瘮" />
-    <el-table-column prop="drDataNum" label="鏁版嵁閲�" />
-    <el-table-column prop="drEffectiveRate" label="鏈夋晥鐜�" />
+    <el-table-column prop="drExceedTimes" label="瓒呮爣娆℃暟">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drExceedTimes"
+        />
+        <span v-else>{{ row.drExceedTimes }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drAvg" label="骞冲潎鍊�">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drAvg"
+        />
+        <span v-else>{{ row.drAvg }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drMax" label="鏈�澶у��">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drMax"
+        />
+        <span v-else>{{ row.drMax }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drMin" label="鏈�灏忓��">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drMin"
+        />
+        <span v-else>{{ row.drMin }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drOverAvgPer" label="瓒呭尯鍧囧�肩櫨鍒嗘瘮">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drOverAvgPer"
+        />
+        <span v-else>{{ row.drOverAvgPer }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drDataNum" label="鏁版嵁閲�">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drDataNum"
+        />
+        <span v-else>{{ row.drDataNum }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column prop="drEffectiveRate" label="鏈夋晥鐜�">
+      <template #default="{ row }">
+        <el-input
+          v-if="isUploadNewFile && !row.isFound"
+          size="small"
+          v-model="row.drEffectiveRate"
+          placeholder="鍦烘櫙鍚嶇О"
+        />
+        <span v-else>{{ row.drEffectiveRate }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column v-if="isUploadNewFile" type="expand">
+      <template #default="{ row }">
+        <div class="p-v-4">
+          <div v-if="!row.isFound" class="p-h-16">
+            <div v-if="row.notSure">
+              <el-text type="warning" size="small"
+                >鏈壘鍒拌鍦烘櫙锛屼絾鎵惧埌浜嗘湁鐩镐技鍚嶇О鐨勫満鏅紝璇风‘瀹氭槸鍝釜鍦烘櫙</el-text
+              >
+              <div class="m-t-8">
+                <el-button
+                  v-for="(v, i) in row.sourceScene"
+                  :key="v.guid"
+                  type="primary"
+                  text
+                  bg
+                  size="small"
+                  @click="handleRadioChange(v, row)"
+                >
+                  {{ v.name }}
+                </el-button>
+                <!-- <el-radio-group v-model="row.radioValue">
+                <el-radio
+                  v-for="(v, i) in row.sourceScene"
+                  :key="v.guid"
+                  :value="i"
+                  size="small"
+                  border
+                  @change="handleRadioChange(v, row)"
+                  >{{ v.name }}</el-radio
+                >
+              </el-radio-group> -->
+              </div>
+            </div>
+            <div v-else>
+              <el-text type="danger" size="small"
+                >鏈壘鍒拌鍦烘櫙锛屼篃娌℃湁浠讳綍鐩镐技鍚嶇О鐨勫満鏅紝璇蜂慨鏀瑰満鏅悕绉版垨鍘婚櫎璇ュ満鏅�</el-text
+              >
+            </div>
+          </div>
+          <div v-else class="p-h-16">
+            <el-text type="success" size="small"> 宸叉纭尮閰嶅埌璇ュ満鏅� </el-text>
+          </div>
+        </div>
+      </template>
+    </el-table-column>
   </el-table>
 </template>
 <script setup>
-import { ref, watch, onMounted } from 'vue';
+import { ref, reactive, watch, onMounted, getCurrentInstance } from 'vue';
 import { genFileId } from 'element-plus';
 import monitordataApi from '@/api/fysp/monitordataApi';
+import sceneApi from '@/api/fysp/sceneApi';
 import * as XLSX from 'xlsx';
+
+const cns = getCurrentInstance();
+const $fm = cns.appContext.config.globalProperties.$fm;
 
 const props = defineProps({
   areaInfo: { type: Object }
 });
 
 let workbook;
+const isUploadNewFile = ref(false);
 const data = ref([]);
+const expandRowKeys = ref([]);
 const upload = ref();
+const tableLoading = ref(false);
 const loadTxt = ref('');
+const tips = ref('');
+
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (row.loading) {
+    return 'loading-row';
+  } else if (row.isFound == undefined) {
+    return '';
+  } else {
+    return row.isFound
+      ? 'success-row'
+      : row.notSure
+        ? 'warning-row'
+        : 'danger-row';
+  }
+};
 
 // 鑾峰彇鍘嗗彶缁熻缁撴灉
 function fetchDustDataResult() {
@@ -75,14 +259,95 @@
 }
 
 function handleChange(uploadFile, uploadFiles) {
+  expandRowKeys.value = [];
+  tableLoading.value = true;
+  loadTxt.value = '鏂囦欢瑙f瀽涓�...';
   // console.log(uploadFile, uploadFiles);
   const fileReader = new FileReader();
   fileReader.onload = (file) => {
-    const data = file.target.result;
-    workbook = XLSX.read(data, { type: 'array' });
+    const fileData = file.target.result;
+    workbook = XLSX.read(fileData, { type: 'array' });
     console.log(workbook.SheetNames);
+    if (workbook.SheetNames.length == 0) {
+      tips.value = 'excel鏂囦欢閿欒锛屾病鏈塻heet琛ㄥ崟';
+      return;
+    }
+    const worksheet = workbook.Sheets[workbook.SheetNames[0]];
+    const tableData = XLSX.utils.sheet_to_json(worksheet);
+    data.value = tableData.map((v, i) => {
+      return reactive({
+        id: i,
+        drSceneName: v['鍚嶇О'],
+        drDeviceCode: v['璁惧鍙�'],
+        drTime: $fm.formatDateFromExcel(v['鏃堕棿'], '-'),
+        drExceedTimes: v['瓒呮爣娆℃暟'],
+        drAvg: v['骞冲潎鍊�'],
+        drMax: v['鏈�澶у��'],
+        drMin: v['鏈�灏忓��'],
+        drOverAvgPer: v['瓒呭尯鍧囧�肩櫨鍒嗘瘮'],
+        drDataNum: v['鏁版嵁閲�'],
+        drEffectiveRate: v['鏈夋晥鐜�']
+      });
+    });
+    // console.log(tableData);
+    setTimeout(() => {
+      tableLoading.value = false;
+      isUploadNewFile.value = true;
+      data.value.forEach((d) => {
+        searchScene(d);
+      });
+    }, 1000);
   };
   fileReader.readAsArrayBuffer(uploadFile.raw);
+}
+
+// 鏌ヨ浠庢枃浠朵笂浼犵殑姣忎釜鍦烘櫙鏄惁鑳藉湪绯荤粺涓壘鍒板搴旂殑鍦烘櫙淇℃伅
+function searchScene(d) {
+  d.loading = true;
+  if (!d.drSceneName) {
+    d.isFound = false;
+    d.loading = false;
+    return Promise;
+  } else {
+    sceneApi
+      .findScene({ name: d.drSceneName })
+      .then((res) => {
+        setTimeout(() => {
+          if (res.length > 0) {
+            const findRes = res.find((v) => v.name == d.drSceneName);
+            if (findRes) {
+              d.drSceneId = res[0].guid;
+              d.isFound = true;
+            } else {
+              d.isFound = false;
+              d.notSure = true;
+              expandRowKeys.value.push(d.id);
+            }
+            d.sourceScene = res;
+          } else {
+            d.isFound = false;
+            expandRowKeys.value.push(d.id);
+          }
+          d.loading = false;
+        }, 1000);
+      })
+      .finally(() => {
+        setTimeout(() => {
+          d.loading = false;
+        }, 1000);
+      });
+  }
+}
+
+function handleSceneNameChange(newName, row) {
+  searchScene(row);
+}
+
+function handleRadioChange(value, row) {
+  const scene = value;
+  row.drSceneId = scene.guid;
+  row.drSceneName = scene.name;
+  searchScene(row);
 }
 
 // 涓婁紶缁熻缁撴灉鏂囨。
@@ -102,4 +367,42 @@
 :deep(.el-text) {
   align-self: auto;
 }
+
+:deep(.el-table__expanded-cell) {
+  padding: 0;
+  /* background-color: var(--el-bg-color-page); */
+}
+
+/* :deep(.el-table__body tr>td.hover-cell) {
+  background-color: red !important;
+} */
+/* .el-table--enable-row-hover
+  .el-table__body
+  tr:hover
+  > td
+  :deep(.el-table__cell) {
+  background-color: red !important;
+} */
+</style>
+<style>
+.el-table .warning-row {
+  --el-table-tr-bg-color: var(--el-color-warning-light-5);
+}
+.el-table .success-row {
+  --el-table-tr-bg-color: var(--el-color-success-light-7);
+}
+.el-table .danger-row {
+  --el-table-tr-bg-color: var(--el-color-danger-light-5);
+}
+.el-table .loading-row {
+  color: var(--el-text-color-disabled);
+  /* --el-table-tr-bg-color: var(--el-text-color-placeholder); */
+  --el-table-tr-bg-color: var(--el-bg-color);
+}
+/* .el-table__body tr>td.hover-cell {
+  background-color: red !important;
+} */
+/* .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell {
+  background-color: unset;
+} */
 </style>

--
Gitblit v1.9.3