From c1d2051abc8ca88cd07f0d7c56c0dbf8165d5c33 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期四, 18 九月 2025 17:02:22 +0800
Subject: [PATCH] 2025.9.18 数据产品(待完成)

---
 src/views/fysp/config/device/CompDeviceMatchEdit.vue                         |    2 
 src/views/fysp/config/components/CompInfoSearchFysp.vue                      |   72 +++
 src/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue         |  104 +++++
 src/components.d.ts                                                          |    7 
 src/components/SideList.vue                                                  |  276 +++++++------
 src/components/ToolBar.vue                                                   |    2 
 src/views/HomePage.vue                                                       |   43 +
 src/views/fysp/support/JingAnSupport.vue                                     |   46 ++
 src/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue         |   98 ++++
 src/components/core/BaseContentLayout.vue                                    |   20 
 src/components/search-option/FYSearchBar.vue                                 |    2 
 src/router/index.js                                                          |   12 
 src/views/fysp/data-product/base-data-product/components/ProdDownload.vue    |   70 +++
 src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue |   60 ++
 src/components/table/FYTable.vue                                             |   44 +-
 src/api/fysp/nightConstructionApi.js                                         |   13 
 src/views/fysp/check/ProCheck.vue                                            |   16 
 src/views/fysp/config/components/CompInfoSearch.vue                          |   37 +
 src/components/SearchBar.vue                                                 |    7 
 src/views/fysp/data-product/base-data-product/ProdManage.vue                 |   19 
 src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue              |   31 +
 src/views/fysp/support/JingAnNightConstruction.vue                           |  139 +++++-
 src/views/fysp/data-product/prod-step-change.js                              |   15 
 src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue    |    8 
 24 files changed, 869 insertions(+), 274 deletions(-)

diff --git a/src/api/fysp/nightConstructionApi.js b/src/api/fysp/nightConstructionApi.js
index 371448d..babbc8e 100644
--- a/src/api/fysp/nightConstructionApi.js
+++ b/src/api/fysp/nightConstructionApi.js
@@ -13,5 +13,18 @@
         params: { cityCode, districtCode, page, perPage }
       })
       .then((res) => res.data);
+  },
+
+  /**
+   * 鏇存柊宸ュ湴澶滈棿鏂藉伐璁稿彲璇�
+   * @param {*} param0 
+   * @returns 
+   */
+  updateRecord({ recordId, userId, sceneId }) {
+    return $fysp
+      .post(`nightwork/record`, undefined, {
+        params: { recordId, userId, sceneId }
+      })
+      .then((res) => res.data);
   }
 };
diff --git a/src/components.d.ts b/src/components.d.ts
index fdde43f..3d570e1 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -20,8 +20,11 @@
     ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
     ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElCalendar: typeof import('element-plus/es')['ElCalendar']
     ElCard: typeof import('element-plus/es')['ElCard']
     ElCascader: typeof import('element-plus/es')['ElCascader']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElCollapse: typeof import('element-plus/es')['ElCollapse']
     ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
@@ -42,11 +45,13 @@
     ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
+    ElLink: typeof import('element-plus/es')['ElLink']
     ElMain: typeof import('element-plus/es')['ElMain']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
     ElRadio: typeof import('element-plus/es')['ElRadio']
@@ -54,6 +59,7 @@
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
+    ElSegmented: typeof import('element-plus/es')['ElSegmented']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSpace: typeof import('element-plus/es')['ElSpace']
     ElStep: typeof import('element-plus/es')['ElStep']
@@ -67,6 +73,7 @@
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    ElTransfer: typeof import('element-plus/es')['ElTransfer']
     ElTree: typeof import('element-plus/es')['ElTree']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     Footer: typeof import('./components/core/Footer.vue')['default']
diff --git a/src/components/SearchBar.vue b/src/components/SearchBar.vue
index c3609f4..19f48c3 100644
--- a/src/components/SearchBar.vue
+++ b/src/components/SearchBar.vue
@@ -1,6 +1,6 @@
 <template>
   <el-row class="layout">
-    <el-col :span="10">
+    <el-col :span="$slots.summary ? 10 : 24">
       <el-form :inline="true" :model="formSearch">
         <el-form-item label="鎬讳换鍔�">
           <!-- <el-input v-model="formSearch.topTaskId" placeholder="鎬讳换鍔�" /> -->
@@ -27,7 +27,7 @@
         </el-form-item>
       </el-form>
     </el-col>
-    <el-col :span="14">
+    <el-col :span="$slots.summary ? 14 : 0">
       <el-row justify="end">
         <slot name="summary"></slot>
       </el-row>
@@ -85,7 +85,8 @@
       );
       const param = {
         topTask: task ? task.data : {},
-        sceneTypeId: this.formSearch.scenetype.value
+        sceneTypeId: this.formSearch.scenetype.value,
+        sceneTypeName: this.formSearch.scenetype.label,
       };
       // console.log(param);
 
diff --git a/src/components/SideList.vue b/src/components/SideList.vue
index 846be3d..a3a795c 100644
--- a/src/components/SideList.vue
+++ b/src/components/SideList.vue
@@ -1,147 +1,153 @@
 <template>
-  <div v-if="legend" class="state-label">
-    <el-input
-      v-model="filterText"
-      icon="Search"
-      style="width: 200px"
-      placeholder="鍏抽敭瀛楃瓫閫�"
-      clearable
-    />
-    <el-tooltip placement="bottom-start" effect="dark">
-      <template #content>
-        <el-space>
-          <el-space v-for="(item, index) in stateLabels" :key="index" :size="1">
-            <el-icon :color="item.color">
-              <component :is="item.icon"></component>
-            </el-icon>
-            {{ item.name }}
-            <!-- <el-text size="small">{{ item.name }}</el-text> -->
+  <div style="padding-right: 10px;">
+    <div v-if="legend" class="state-label">
+      <el-input
+        v-model="filterText"
+        icon="Search"
+        style="width: 180px"
+        placeholder="鍏抽敭瀛楃瓫閫�"
+        clearable
+      />
+      <el-tooltip placement="bottom-start" effect="dark">
+        <template #content>
+          <el-space>
+            <el-space
+              v-for="(item, index) in stateLabels"
+              :key="index"
+              :size="1"
+            >
+              <el-icon :color="item.color">
+                <component :is="item.icon"></component>
+              </el-icon>
+              {{ item.name }}
+              <!-- <el-text size="small">{{ item.name }}</el-text> -->
+            </el-space>
           </el-space>
-        </el-space>
-        <br />
+          <br />
+          <el-space>
+            <el-space :size="1">
+              <el-icon :size="16" color="var(--el-color-success)">
+                <Avatar />
+              </el-icon>
+              姝e湪鎵ц宸℃煡
+            </el-space>
+            <el-space :size="1">
+              <el-icon :size="16" color="var(--el-color-info)">
+                <Avatar />
+              </el-icon>
+              鏈墽琛�
+            </el-space>
+          </el-space>
+        </template>
         <el-space>
-          <el-space :size="1">
-            <el-icon :size="16" color="var(--el-color-success)">
+          <el-icon class="cursor-p" :size="16" color="var(--el-color-primary)"
+            ><QuestionFilled
+          /></el-icon>
+          <el-text size="small" class="cursor-p">瀹℃牳鐘舵�佸浘渚�</el-text>
+        </el-space>
+      </el-tooltip>
+    </div>
+    <el-tree
+      ref="treeRef"
+      class="el-tree"
+      v-loading="isLoading"
+      :data="dataList"
+      :props="defaultProps"
+      @node-click="handleNodeClick"
+      :filter-node-method="filterNode"
+      default-expand-all
+      highlight-current
+      check-on-click-node
+      empty-text="鏆傛棤璁板綍"
+    >
+      <template #default="{ node, data }">
+        <slot :node="node" :data="data">
+          <div
+            :class="
+              data.selected
+                ? 'selected-tree-node custom-tree-node'
+                : 'custom-tree-node'
+            "
+          >
+            <el-icon
+              v-if="data.status == '姝e湪鎵ц'"
+              :size="16"
+              color="var(--el-color-success)"
+              style="margin-left: -16px"
+            >
               <Avatar />
             </el-icon>
-            姝e湪鎵ц宸℃煡
-          </el-space>
-          <el-space :size="1">
-            <el-icon :size="16" color="var(--el-color-info)">
+            <el-icon
+              v-if="data.status == '鏈墽琛�'"
+              :size="16"
+              color="var(--el-color-info)"
+              style="margin-left: -16px"
+            >
               <Avatar />
             </el-icon>
-            鏈墽琛�
-          </el-space>
-        </el-space>
-      </template>
-      <el-space>
-        <el-icon class="cursor-p" :size="16" color="var(--el-color-primary)"
-          ><QuestionFilled
-        /></el-icon>
-        <el-text size="small" class="cursor-p">瀹℃牳鐘舵�佸浘渚�</el-text>
-      </el-space>
-    </el-tooltip>
-  </div>
-  <el-tree
-    ref="treeRef"
-    class="el-tree"
-    v-loading="isLoading"
-    :data="dataList"
-    :props="defaultProps"
-    @node-click="handleNodeClick"
-    :filter-node-method="filterNode"
-    default-expand-all
-    highlight-current
-    check-on-click-node
-    empty-text="鏆傛棤璁板綍"
-  >
-    <template #default="{ node, data }">
-      <slot :node="node" :data="data">
-        <div
-          :class="
-            data.selected
-              ? 'selected-tree-node custom-tree-node'
-              : 'custom-tree-node'
-          "
-        >
-          <el-icon
-            v-if="data.status == '姝e湪鎵ц'"
-            :size="16"
-            color="var(--el-color-success)"
-            style="margin-left: -16px"
-          >
-            <Avatar />
-          </el-icon>
-          <el-icon
-            v-if="data.status == '鏈墽琛�'"
-            :size="16"
-            color="var(--el-color-info)"
-            style="margin-left: -16px"
-          >
-            <Avatar />
-          </el-icon>
-          <!-- <el-icon :color="stateLabels[data.type].color">
+            <!-- <el-icon :color="stateLabels[data.type].color">
             <component :is="stateLabels[data.type].icon"></component>
           </el-icon> -->
-          <el-icon
-            v-if="data.type == 0"
-            :size="16"
-            color="var(--el-color-info)"
-          >
-            <SuccessFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 1"
-            :size="16"
-            color="var(--el-color-danger)"
-          >
-            <QuestionFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 2"
-            :size="16"
-            color="var(--el-color-warning)"
-          >
-            <QuestionFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 3"
-            :size="16"
-            color="var(--el-color-danger)"
-          >
-            <WarnTriangleFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 4"
-            :size="16"
-            color="var(--el-color-danger)"
-          >
-            <WarningFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 5"
-            :size="16"
-            color="var(--el-color-warning)"
-          >
-            <WarningFilled />
-          </el-icon>
-          <el-icon
-            v-else-if="data.type == 6"
-            :size="16"
-            color="var(--el-color-success)"
-          >
-            <SuccessFilled />
-          </el-icon>
-          <!-- <el-text>{{ node.label }}</el-text> -->
-          {{ node.label }}
-          <span v-if="data.count">
-            {{ '_(' + data.count + ')' }}
-            <!-- <el-text size="small">鐐规</el-text>) -->
-          </span>
-        </div>
-      </slot>
-    </template>
-  </el-tree>
+            <el-icon
+              v-if="data.type == 0"
+              :size="16"
+              color="var(--el-color-info)"
+            >
+              <SuccessFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 1"
+              :size="16"
+              color="var(--el-color-danger)"
+            >
+              <QuestionFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 2"
+              :size="16"
+              color="var(--el-color-warning)"
+            >
+              <QuestionFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 3"
+              :size="16"
+              color="var(--el-color-danger)"
+            >
+              <WarnTriangleFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 4"
+              :size="16"
+              color="var(--el-color-danger)"
+            >
+              <WarningFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 5"
+              :size="16"
+              color="var(--el-color-warning)"
+            >
+              <WarningFilled />
+            </el-icon>
+            <el-icon
+              v-else-if="data.type == 6"
+              :size="16"
+              color="var(--el-color-success)"
+            >
+              <SuccessFilled />
+            </el-icon>
+            <!-- <el-text>{{ node.label }}</el-text> -->
+            {{ node.label }}
+            <span v-if="data.count">
+              {{ '_(' + data.count + ')' }}
+              <!-- <el-text size="small">鐐规</el-text>) -->
+            </span>
+          </div>
+        </slot>
+      </template>
+    </el-tree>
+  </div>
 </template>
 
 <script>
diff --git a/src/components/ToolBar.vue b/src/components/ToolBar.vue
index 29487a5..86d046c 100644
--- a/src/components/ToolBar.vue
+++ b/src/components/ToolBar.vue
@@ -60,7 +60,7 @@
 </script>
 <style scoped>
 .layout {
-  /* background-color: beige; */
+  background-color: white;
   height: var(--height-toolbar);
   border-bottom: 1px solid var(--el-color-info-light-7);
   box-shadow: 6px 4px 4px rgba(0, 0, 0, 0.12);
diff --git a/src/components/core/BaseContentLayout.vue b/src/components/core/BaseContentLayout.vue
index 3889435..dfd48a8 100644
--- a/src/components/core/BaseContentLayout.vue
+++ b/src/components/core/BaseContentLayout.vue
@@ -4,12 +4,12 @@
       <slot name="header"></slot>
     </el-header>
     <el-container>
-      <el-aside class="el-aside" :style="'height: ' + mainHeight">
-        <el-scrollbar :noresize="true" style="position: relative;">
+      <el-aside class="el-aside" :style="{ height: mainHeight + 'px' }">
+        <el-scrollbar :noresize="false">
           <slot name="aside"></slot>
         </el-scrollbar>
       </el-aside>
-      <el-main class="el-main" :style="'height: ' + mainHeight">
+      <el-main class="el-main" :style="{ height: mainHeight + 'px' }">
         <slot name="main"></slot>
       </el-main>
     </el-container>
@@ -18,10 +18,11 @@
 
 <script>
 export default {
+  inject: ['contentMaxHeight'],
   // 鍙充晶鎿嶄綔鐣岄潰鍩虹甯冨眬
   data() {
     return {
-      mainHeight: 'calc(100vh - 60px * 2 - var(--el-main-padding) * 2)'
+      mainHeight: this.contentMaxHeight.value
     };
   },
   methods: {
@@ -30,9 +31,10 @@
       if (this.$refs.headerRef) {
         const h1 = this.$refs.headerRef.$el.offsetHeight;
         const h = h1;
-        return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2 + 6px)`;
+        return this.contentMaxHeight.value - h;
+        // return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2 + 6px)`;
       } else {
-        return `calc(100vh - 60px * 2 - var(--el-main-padding) * 2)`;
+        return this.contentMaxHeight.value;
       }
     }
   },
@@ -58,10 +60,10 @@
 
 .el-header {
   height: initial;
-  padding: 0 0 0px 0;
+  padding: 0 0 4px 0;
   /* background-color: rgb(216, 201, 201); */
   /* border-bottom: 1px solid var(--el-color-info-light-7); */
-  margin-bottom: 4px;
+  /* margin-bottom: 4px; */
 }
 
 .el-main {
@@ -69,7 +71,7 @@
   /* background-color: whitesmoke; */
   /* height: calc(100vh - 60px * 2 - 20px * 2); */
   padding: initial;
-  padding-left: 20px;
+  padding-left: 10px;
   /* overflow: hidden; */
 }
 </style>
diff --git a/src/components/search-option/FYSearchBar.vue b/src/components/search-option/FYSearchBar.vue
index e968e58..fb3eab6 100644
--- a/src/components/search-option/FYSearchBar.vue
+++ b/src/components/search-option/FYSearchBar.vue
@@ -1,7 +1,7 @@
 <template>
   <el-form :inline="true" :size="size">
     <slot name="options"></slot>
-    <el-form-item>
+    <el-form-item v-if="$slots.options">
       <el-button
         icon="Search"
         type="primary"
diff --git a/src/components/table/FYTable.vue b/src/components/table/FYTable.vue
index b759ee0..a85de22 100644
--- a/src/components/table/FYTable.vue
+++ b/src/components/table/FYTable.vue
@@ -1,10 +1,10 @@
 <template>
   <el-row ref="searchRef">
     <FYSearchBar @search="onSearch">
-      <template #options>
+      <template #options v-if="$slots.options">
         <slot name="options"></slot>
       </template>
-      <template #buttons>
+      <template #buttons v-if="$slots.buttons">
         <slot name="buttons"></slot>
       </template>
     </FYSearchBar>
@@ -69,6 +69,7 @@
  * 浣跨敤鏃堕渶瑕佸湪<slot #options>涓坊鍔犺嚜瀹氫箟鏌ヨ閫夐」锛屽湪<slot #table-column>涓坊鍔犺嚜瀹氫箟琛ㄦ牸鍒楋紝鍚屾椂瀹炵幇瑙﹀彂鍑芥暟search
  */
 export default {
+  inject: ['contentMaxHeight'],
   props: {
     rowClassName: undefined,
     cellClassName: Function || String,
@@ -86,6 +87,11 @@
       default: () => []
     },
     totalCount: {
+      type: Number,
+      default: 0
+    },
+    // 棰濆鐨勯珮搴︼紝鐢ㄤ簬璁$畻琛ㄦ牸楂樺害
+    extraHeight: {
       type: Number,
       default: 0
     }
@@ -130,26 +136,16 @@
       if (nValue != oValue) {
         this.total = nValue;
       }
+    },
+    extraHeight: {
+      handler(nValue, oValue) {
+        if (nValue != oValue) {
+          this.tableHeight = this.calcTableHeight();
+        }
+      },
     }
   },
-  computed: {
-    cTableHeight() {
-      if (this.$refs.searchRef) {
-        const h1 = this.$refs.searchRef.$el.offsetHeight;
-        const h2 = this.$refs.paginationRef
-          ? this.$refs.paginationRef.$el.offsetHeight
-          : 0;
-        const h3 = this.$refs.expandRef.$el.offsetHeight;
-        const h4 = this.$refs.expand2Ref.offsetHeight;
-
-        const h = h1 + h2 + h3 + h4;
-        // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
-        return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
-      } else {
-        return '500';
-      }
-    }
-  },
+  computed: {},
   methods: {
     /**
      * 琛ㄦ牸鏁版嵁鏌ヨ锛屼紶閫掍袱缁勫弬鏁帮紝鍒嗛〉淇℃伅鍜屽洖璋冨嚱鏁�
@@ -186,9 +182,9 @@
       const h3 = this.$refs.expandRef.$el.offsetHeight;
       const h4 = this.$refs.expand2Ref.offsetHeight;
 
-      const h = h1 + h2 + h3 + h4;
-      // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
-      return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
+      const h = h1 + h2 + h3 + h4 + this.extraHeight;
+      return this.contentMaxHeight.value - h + 'px';
+      // return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
     },
     tableRowClassName({ row }) {
       if (this.rowClassName) {
@@ -220,7 +216,7 @@
   mounted() {
     this.tableHeight = this.calcTableHeight();
     this.onSearch();
-  },
+  }
 };
 </script>
 
diff --git a/src/router/index.js b/src/router/index.js
index 5d81136..c229675 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -93,7 +93,7 @@
                         // 鍩虹浜у搧-鍦烘櫙娓呭崟
                         path: 'scene',
                         name: 'ProdSceneInfo',
-                        meta: { keepAlive: false, key: 'ProdManage' },
+                        meta: { keepAlive: true, key: 'ProdManage' },
                         component: () =>
                           import(
                             '@/views/fysp/data-product/base-data-product/ProdSceneInfo.vue'
@@ -103,7 +103,7 @@
                         // 鍩虹浜у搧-瑙勮寖鎬ц瘎浼�
                         path: 'evaluate',
                         name: 'ProdEvaluationInfo',
-                        meta: { keepAlive: false, key: 'ProdManage' },
+                        meta: { keepAlive: true, key: 'ProdManage' },
                         component: () =>
                           import(
                             '@/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue'
@@ -113,7 +113,7 @@
                         // 鍩虹浜у搧-宸℃煡淇℃伅
                         path: 'inspection',
                         name: 'ProdInspectionInfo',
-                        meta: { keepAlive: false, key: 'ProdManage' },
+                        meta: { keepAlive: true, key: 'ProdManage' },
                         component: () =>
                           import(
                             '@/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue'
@@ -123,7 +123,7 @@
                         // 鍩虹浜у搧-鐩戞祴鏁版嵁
                         path: 'monitordata',
                         name: 'ProdMonitorDataInfo',
-                        meta: { keepAlive: false, key: 'ProdManage' },
+                        meta: { keepAlive: true, key: 'ProdManage' },
                         component: () =>
                           import(
                             '@/views/fysp/data-product/base-data-product/ProdMonitorDataInfo.vue'
@@ -323,8 +323,8 @@
 ];
 
 const router = createRouter({
-  history: createWebHistory(import.meta.env.BASE_URL),
-  // history: createWebHashHistory(),
+  // history: createWebHistory(import.meta.env.BASE_URL),
+  history: createWebHashHistory(),
   routes: routes
 });
 
diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue
index 83ce519..65add8b 100644
--- a/src/views/HomePage.vue
+++ b/src/views/HomePage.vue
@@ -1,11 +1,11 @@
 <template>
   <el-container class="el-container">
-    <el-aside class="el-aside"
-      ><SiderMenu :collapse="isCollapsed" @nav-page="navPage"></SiderMenu
-    ></el-aside>
+    <el-aside class="el-aside">
+      <SiderMenu :collapse="isCollapsed" @nav-page="navPage"></SiderMenu>
+    </el-aside>
     <el-container>
-      <el-header class="el-header"
-        ><Header
+      <el-header ref="headerRef" class="el-header">
+        <Header
           :navTitles="navTitles"
           :collapse="isCollapsed"
           @collapsed-sider="collapsedSider"
@@ -13,7 +13,13 @@
       ></el-header>
       <el-main class="el-main">
         <el-scrollbar>
-          <div class="el-main__content">
+          <div
+            class="el-main__content"
+            :style="{
+              maxHeight: contentMaxHeight + 'px',
+              padding: mainPadding + 'px'
+            }"
+          >
             <Content></Content>
             <!-- <el-backtop
                 target=".el-main .el-scrollbar__wrap"
@@ -34,11 +40,16 @@
 </template>
 
 <script>
+import { computed } from 'vue';
+
 export default {
   data() {
     return {
       isCollapsed: false,
-      navTitles: []
+      navTitles: [],
+      headerHeight: 60,
+      mainPadding: 10,
+      contentMaxHeight: NaN
     };
   },
   methods: {
@@ -48,6 +59,18 @@
     navPage(titles) {
       this.navTitles = titles;
     }
+  },
+  mounted() {
+    this.headerHeight = this.$refs.headerRef.$el.offsetHeight;
+    this.contentMaxHeight =
+      window.innerHeight - this.headerHeight - this.mainPadding * 2;
+  },
+  provide() {
+    return {
+      headerHeight: computed(() => this.headerHeight),
+      mainPadding: computed(() => this.mainPadding),
+      contentMaxHeight: computed(() => this.contentMaxHeight)
+    };
   }
 };
 </script>
@@ -74,8 +97,10 @@
 }
 
 .el-main__content {
-  padding: var(--el-main-padding) calc(var(--el-main-padding) / 2);
-  max-height: calc(100vh - 60px - var(--el-main-padding) * 2);
+  /* --main-padding: 10px; */
+  /* padding: var(--main-padding) calc(var(--main-padding) / 2); */
+  /* padding: var(--main-padding); */
+  /* max-height: calc(100vh - 60px - var(--main-padding) * 2); */
   /* background-color: aqua; */
   /* overflow: auto; */
 }
diff --git a/src/views/fysp/check/ProCheck.vue b/src/views/fysp/check/ProCheck.vue
index a7dfec4..d47f6a0 100644
--- a/src/views/fysp/check/ProCheck.vue
+++ b/src/views/fysp/check/ProCheck.vue
@@ -23,16 +23,14 @@
     <template #main>
       <el-scrollbar>
         <ToolBar
+          ref="toolBarRef"
+          class="toolbar-sticky"
           :title="curSubtask.title"
           :descriptions="proStatus"
           :buttons="buttons"
           :loading="mainLoading"
         ></ToolBar>
-        <el-scrollbar
-          v-if="curProList.length > 0"
-          class="scrollbar-inner"
-          v-loading="mainLoading"
-        >
+        <div v-if="curProList.length > 0" v-loading="mainLoading">
           <CompProblemCard
             :key="i"
             v-for="(p, i) in curProList"
@@ -43,7 +41,7 @@
             @submit="updateSubtask"
             @check="handleProblemCheck"
           ></CompProblemCard>
-        </el-scrollbar>
+        </div>
         <el-empty v-else description="鏆傛棤闂" v-loading="mainLoading" />
       </el-scrollbar>
     </template>
@@ -290,4 +288,10 @@
 .scrollbar-inner {
   height: calc(100vh - 60px * 2 - 20px * 2 - var(--height-toolbar));
 }
+
+.toolbar-sticky {
+  position: sticky;
+  z-index: 2;
+  top: 0;
+}
 </style>
diff --git a/src/views/fysp/config/device/CompInfoSearch.vue b/src/views/fysp/config/components/CompInfoSearch.vue
similarity index 80%
rename from src/views/fysp/config/device/CompInfoSearch.vue
rename to src/views/fysp/config/components/CompInfoSearch.vue
index bee891b..fc88f63 100644
--- a/src/views/fysp/config/device/CompInfoSearch.vue
+++ b/src/views/fysp/config/components/CompInfoSearch.vue
@@ -1,12 +1,12 @@
 <template>
   <!-- <div v-if="modelValue"> -->
-    <el-divider content-position="left">閫夋嫨{{ label }}</el-divider>
-    <div class="select-box">
-      <div>
-        <el-text size="small" type="info">褰撳墠閫夋嫨</el-text>
-      </div>
-      <slot name="selected" :row="modelValue"></slot>
+  <el-divider content-position="left">閫夋嫨{{ label }}</el-divider>
+  <div class="select-box">
+    <div>
+      <el-text size="small" type="info">褰撳墠閫夋嫨</el-text>
     </div>
+    <slot name="selected" :row="modelValue"></slot>
+  </div>
   <!-- </div> -->
   <el-divider content-position="left">{{ label }}妫�绱�</el-divider>
   <FYSearchBar @search="search" :loading="loading">
@@ -15,7 +15,7 @@
         label=""
         :placeholder="placeholder"
         v-model:value="searchText"
-        width="200px"
+        :width="searchTextWidth"
       ></FYOptionText>
     </template>
   </FYSearchBar>
@@ -27,6 +27,7 @@
         </div>
       </el-space>
     </el-scrollbar>
+    <el-empty v-else description="鏃犺褰�" />
   </div>
   <el-pagination
     v-if="pageShow && dataList.length > 0"
@@ -64,6 +65,10 @@
       type: String,
       default: '杈撳叆鍏抽敭瀛楁绱�'
     },
+    searchTextWidth: {
+      type: String,
+      default: '200px'
+    },
     // 鏄惁鏄剧ず鍒嗛〉
     pageShow: {
       type: Boolean,
@@ -75,7 +80,9 @@
       default: () => {
         return [10, 20, 50, 100];
       }
-    }
+    },
+    // 榛樿鎼滅储鏂囨湰
+    defaultText: String
   },
   emits: ['search', 'update:modelValue'],
   data() {
@@ -86,7 +93,19 @@
       loading: false
     };
   },
-  watch: {},
+  watch: {
+    defaultText: {
+      handler(newVal) {
+        if (newVal) {
+          this.searchText = newVal;
+          setTimeout(() => {
+            this.search();
+          }, 500);
+        }
+      },
+      immediate: true
+    }
+  },
   methods: {
     search() {
       this.loading = true;
diff --git a/src/views/fysp/config/components/CompInfoSearchFysp.vue b/src/views/fysp/config/components/CompInfoSearchFysp.vue
new file mode 100644
index 0000000..cd68241
--- /dev/null
+++ b/src/views/fysp/config/components/CompInfoSearchFysp.vue
@@ -0,0 +1,72 @@
+<template>
+  <CompInfoSearch
+    label="鐩戠鐢ㄦ埛"
+    placeholder="杈撳叆鐢ㄦ埛鍚嶇О"
+    @search="searchSVUser"
+  >
+    <template #selected="{ row }">
+      <div>
+        <el-text>缂栧彿锛歿{ row?.svUserId }}</el-text>
+      </div>
+      <el-space>
+        <el-text>鍚嶇О锛歿{ row?.svUserName }}</el-text>
+        <el-button
+          v-show="row?.svUserName"
+          type="primary"
+          icon="DocumentCopy"
+          text
+          circle
+          @click="copySVUser(row?.svUserName)"
+        />
+      </el-space>
+    </template>
+    <template #default="{ row, click }">
+      <ItemUser :item="row" @add="selectSVUser(row, click)" />
+    </template>
+  </CompInfoSearch>
+</template>
+<script setup>
+import CompInfoSearch from './CompInfoSearch.vue';
+import { useCloned } from '@vueuse/core';
+import svUserApi from '@/api/fysp/userApi';
+
+const props = defineProps({
+  // 妫�绱㈣寖鍥达紙鍖呭惈琛屾斂鍖哄垝銆佸満鏅被鍨嬶級
+  area: Object
+});
+
+// 鏌ヨ鐩戠鐢ㄦ埛
+function searchSVUser(param, callback) {
+  const { text, page, pageSize } = param;
+  const { cloned: area } = useCloned(props.area);
+  area.value.sceneName = text;
+  return svUserApi
+    .searchUser(area.value, text, page, pageSize)
+    .then((res) => {
+      if (res.success) {
+        const l = res.data.map((value) => {
+          return {
+            ...value,
+            district: value.remark
+          };
+        });
+        callback({
+          data: l,
+          total: res.head.totalCount
+        });
+      }
+    })
+    .finally(() => {
+      callback();
+    });
+}
+
+function selectSVUser(row, click) {
+  const p = {
+    svUserId: row.guid,
+    svUserName: row.realname,
+    ...row
+  };
+  click(p);
+}
+</script>
diff --git a/src/views/fysp/config/device/CompDeviceMatchEdit.vue b/src/views/fysp/config/device/CompDeviceMatchEdit.vue
index a981b43..be4f751 100644
--- a/src/views/fysp/config/device/CompDeviceMatchEdit.vue
+++ b/src/views/fysp/config/device/CompDeviceMatchEdit.vue
@@ -94,7 +94,7 @@
 
 <script>
 import { useCloned } from '@vueuse/core';
-import CompInfoSearch from './CompInfoSearch.vue';
+import CompInfoSearch from '../components/CompInfoSearch.vue';
 import tzUserApi from '@/api/fytz/userApi';
 import svUserApi from '@/api/fysp/userApi';
 import userMapApi from '@/api/fysp/userMapApi';
diff --git a/src/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue b/src/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue
index dc19bf6..c9c31d5 100644
--- a/src/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue
+++ b/src/views/fysp/data-product/base-data-product/ProdEvaluationInfo.vue
@@ -1,4 +1,98 @@
 <template>
-  1
+  <BaseProdProcess
+    v-model:active="active"
+    @onStep1="onStep1"
+    @onStep2="onStep2"
+    @onStep3="onStep3"
+    :loading="loading"
+  >
+    <template #step2="{ contentHeight }">
+      <el-table
+        id="prod-evaluation-table"
+        :data="tableData"
+        v-loading="loading"
+        :height="contentHeight + 'px'"
+        table-layout="fixed"
+        :show-overflow-tooltip="true"
+        size="small"
+        border
+      >
+        <el-table-column fixed="left" prop="index" label="缂栧彿" width="50">
+        </el-table-column>
+        <el-table-column
+          fixed="left"
+          prop="subTask.scensename"
+          label="鍚嶇О"
+          :show-overflow-tooltip="true"
+          min-width="200"
+        >
+        </el-table-column>
+        <el-table-column
+          prop="subTask.planstarttime"
+          label="宸℃煡鏃堕棿"
+          :formatter="timeFormat"
+          width="90"
+        />
+        <!-- <el-table-column prop="provincename" label="鐪�" width="90" />
+        <el-table-column prop="cityname" label="甯�" width="90" />
+        <el-table-column prop="districtname" label="鍖哄幙" width="90" /> -->
+        <el-table-column prop="evaluate.townname" label="琛楅亾" width="80" />
+        <el-table-column
+          prop="evaluate.resultscorebef"
+          label="璇勫垎"
+          width="60"
+        />
+        <el-table-column prop="scoreLevel" label="瑙勮寖鎬�" width="70" />
+        <el-table-column
+          prop="evaluate.updatedate"
+          label="鏇存柊鏃堕棿"
+          width="140"
+          :formatter="timeFormat"
+        />
+      </el-table>
+    </template>
+  </BaseProdProcess>
 </template>
-<script></script>
\ No newline at end of file
+<script setup>
+import { ref, inject } from 'vue';
+import dayjs from 'dayjs';
+import BaseProdProcess from '@/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue';
+import dataprodbaseApi from '@/api/fysp/dataprodbaseApi.js';
+import { conversionFromTable } from '@/utils/excel';
+import { useProdStepChange } from '@/views/fysp/data-product/prod-step-change.js';
+
+const { active, changeActive } = useProdStepChange();
+const loading = ref(false);
+const tableData = ref([]);
+
+function onStep1(opt) {
+  loading.value = true;
+  dataprodbaseApi
+    .fetchProdEvaluateInfo(opt)
+    .then((res) => {
+      if (res.success) {
+        tableData.value = res.data;
+      }
+      changeActive();
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+function onStep2() {
+  changeActive();
+}
+
+function onStep3(val) {
+  if (val.downloadType == '1') {
+    loading.value = true;
+    conversionFromTable('prod-evaluation-table', '瑙勮寖鎬ц瘎浼版竻鍗�');
+    loading.value = false;
+  }
+}
+
+function timeFormat(row, column, cellValue, index) {
+  return dayjs(cellValue).format('YYYY-MM-DD');
+}
+</script>
diff --git a/src/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue b/src/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue
index dc19bf6..10bd091 100644
--- a/src/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue
+++ b/src/views/fysp/data-product/base-data-product/ProdInspectionInfo.vue
@@ -1,4 +1,104 @@
 <template>
-  1
+  <BaseProdProcess
+    v-model:active="active"
+    @onStep1="onStep1"
+    @onStep2="onStep2"
+    @onStep3="onStep3"
+    :loading="loading"
+  >
+    <template #step2="{ contentHeight }">
+      <el-table
+        id="prod-inspection-table"
+        :data="tableData"
+        v-loading="loading"
+        :height="contentHeight + 'px'"
+        table-layout="fixed"
+        :show-overflow-tooltip="true"
+        size="small"
+        border
+      >
+        <el-table-column fixed="left" prop="index" label="缂栧彿" width="50">
+        </el-table-column>
+        <el-table-column
+          fixed="left"
+          prop="subTask.scensename"
+          label="鍚嶇О"
+          :show-overflow-tooltip="true"
+          min-width="200"
+        >
+        </el-table-column>
+        <el-table-column
+          prop="subTask.planstarttime"
+          label="宸℃煡鏃堕棿"
+          :formatter="timeFormat"
+          width="90"
+        />
+        <!-- <el-table-column prop="provincename" label="鐪�" width="90" />
+        <el-table-column prop="cityname" label="甯�" width="90" />
+        <el-table-column prop="districtname" label="鍖哄幙" width="90" /> -->
+        <el-table-column prop="subTask.townname" label="琛楅亾" width="80" />
+        <el-table-column
+          prop="problems.length"
+          label="闂鏁�"
+          width="60"
+        />
+        <el-table-column prop="scoreLevel" label="闂鎽樿" width="70" />
+        <el-table-column
+          prop="evaluate.resultscorebef"
+          label="鏈暣鏀规暟"
+          width="60"
+        />
+        <el-table-column prop="scoreLevel" label="鏈暣鏀归棶棰�" width="70" />
+        <el-table-column
+          prop="evaluate.updatedate"
+          label="鏇存柊鏃堕棿"
+          width="140"
+          :formatter="timeFormat"
+        />
+      </el-table>
+    </template>
+  </BaseProdProcess>
 </template>
-<script></script>
\ No newline at end of file
+<script setup>
+import { ref, inject } from 'vue';
+import dayjs from 'dayjs';
+import BaseProdProcess from '@/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue';
+import dataprodbaseApi from '@/api/fysp/dataprodbaseApi.js';
+import { conversionFromTable } from '@/utils/excel';
+import { useProdStepChange } from '@/views/fysp/data-product/prod-step-change.js';
+
+const { active, changeActive } = useProdStepChange();
+const loading = ref(false);
+const tableData = ref([]);
+
+function onStep1(opt) {
+  loading.value = true;
+  dataprodbaseApi
+    .fetchProdInspectionInfo(opt)
+    .then((res) => {
+      if (res.success) {
+        tableData.value = res.data;
+      }
+      changeActive();
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+function onStep2() {
+  changeActive();
+}
+
+function onStep3(val) {
+  if (val.downloadType == '1') {
+    loading.value = true;
+    conversionFromTable('prod-inspection-table', '鏁存敼娓呭崟');
+    loading.value = false;
+  }
+}
+
+function timeFormat(row, column, cellValue, index) {
+  return dayjs(cellValue).format('YYYY-MM-DD');
+}
+</script>
diff --git a/src/views/fysp/data-product/base-data-product/ProdManage.vue b/src/views/fysp/data-product/base-data-product/ProdManage.vue
index 0f1ff6b..e04d292 100644
--- a/src/views/fysp/data-product/base-data-product/ProdManage.vue
+++ b/src/views/fysp/data-product/base-data-product/ProdManage.vue
@@ -5,7 +5,6 @@
       default-active="scene"
       ellipsis
       mode="horizontal"
-      style="max-width: 600px; background-color: aliceblue"
     >
       <el-menu-item
         v-for="item in menu"
@@ -16,7 +15,7 @@
       >
     </el-menu>
   </el-affix>
-  <router-view v-slot="{ Component, route }" :style="'height: ' + height">
+  <router-view v-slot="{ Component, route }" :style="{ height: height + 'px' }">
     <keep-alive>
       <component
         v-if="route.meta.keepAlive"
@@ -28,14 +27,16 @@
   </router-view>
 </template>
 <script setup>
-import { ref, onMounted, provide } from 'vue';
+import { ref, onMounted, provide, inject, computed } from 'vue';
 import { useRouter, useRoute } from 'vue-router';
+
+const contentMaxHeight = inject('contentMaxHeight');
 
 const router = useRouter();
 const route = useRoute();
 
 const menuRef = ref(null);
-const height = ref('calc(100vh - 64px)');
+const height = ref(contentMaxHeight.value);
 
 const menu = ref([
   {
@@ -48,7 +49,7 @@
     path: 'evaluate'
   },
   {
-    name: '宸℃煡淇℃伅',
+    name: '鏁存敼娓呭崟',
     path: 'inspection'
   },
   {
@@ -67,7 +68,8 @@
 
 function calcTableHeight() {
   const h = menuRef.value.$el.offsetHeight;
-  return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
+  return contentMaxHeight.value - h;
+  // return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
 }
 
 onMounted(() => {
@@ -75,6 +77,9 @@
 });
 
 // 鎻愪緵缁欏唴閮ㄧ粍浠惰鍥炬渶澶ч珮搴�
-provide('viewHeight', height);
+provide(
+  'viewHeight',
+  computed(() => height.value)
+);
 </script>
 <style scoped></style>
diff --git a/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue b/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue
index 0f817a5..ef1323e 100644
--- a/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue
+++ b/src/views/fysp/data-product/base-data-product/ProdSceneInfo.vue
@@ -2,6 +2,8 @@
   <BaseProdProcess
     v-model:active="active"
     @onStep1="onStep1"
+    @onStep2="onStep2"
+    @onStep3="onStep3"
     :loading="loading"
   >
     <!-- <template #step1>
@@ -9,15 +11,16 @@
     </template> -->
     <template #step2="{ contentHeight }">
       <el-table
+        id="prod-scene-table"
         :data="tableData"
         v-loading="loading"
-        :height="viewHeight"
+        :height="contentHeight + 'px'"
         table-layout="fixed"
         :show-overflow-tooltip="true"
         size="small"
         border
       >
-        <el-table-column fixed="left" prop="index" label="缂栧彿" width="40">
+        <el-table-column fixed="left" prop="index" label="缂栧彿" width="50">
         </el-table-column>
         <el-table-column
           fixed="left"
@@ -35,6 +38,8 @@
         <el-table-column prop="districtname" label="鍖哄幙" width="90" /> -->
         <el-table-column prop="townname" label="琛楅亾" width="110" />
         <el-table-column prop="location" label="鍦板潃" width="200" />
+        <el-table-column prop="contacts" label="鑱旂郴浜�" width="70" />
+        <el-table-column prop="contactst" label="鐢佃瘽" width="96" />
         <!-- <el-table-column prop="longitude" label="缁忓害" width="110" />
         <el-table-column prop="latitude" label="绾害" width="110" /> -->
         <!-- <el-table-column
@@ -54,16 +59,12 @@
 import BaseProdProcess from '@/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue';
 import ProdQueryOpt from '@/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue';
 import dataprodbaseApi from '@/api/fysp/dataprodbaseApi.js';
+import { conversionFromTable } from '@/utils/excel';
+import { useProdStepChange } from '@/views/fysp/data-product/prod-step-change.js';
 
-const active = ref(1);
+const { active, changeActive } = useProdStepChange();
 const loading = ref(false);
 const tableData = ref([]);
-const viewHeight = inject('viewHeight');
-
-function changeActive() {
-  active.value++;
-  active.value = active.value > 3 ? 1 : active.value;
-}
 
 function onStep1(opt) {
   loading.value = true;
@@ -84,6 +85,18 @@
     });
 }
 
+function onStep2() {
+  changeActive();
+}
+
+function onStep3(val) {
+  if (val.downloadType == '1') {
+    loading.value = true;
+    conversionFromTable('prod-scene-table', '宸℃煡鍦烘櫙娓呭崟');
+    loading.value = false;
+  }
+}
+
 function timeFormat(row, column, cellValue, index) {
   return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss');
 }
diff --git a/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue b/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue
index 356a41d..8d533e7 100644
--- a/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue
+++ b/src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue
@@ -22,7 +22,7 @@
         <div
           v-show="showStep1Thumbnail"
           class="prod-thumbnail-wrapper"
-          :style="{ height: viewHeight }"
+          :style="{ height: viewHeight + 'px' }"
           @click="changeActive(1)"
         >
           <div class="prod-thumbnail">鈶犱慨鏀归�夐」</div>
@@ -38,6 +38,9 @@
         <div v-show="showStep2Content">
           <div ref="titleRef" class="prod-title">
             <el-text tag="b" size="large">鏁版嵁浜у搧棰勮</el-text>
+            <el-button type="primary" @click="$emit('onStep2')">
+              涓嬭浇鏁版嵁浜у搧
+            </el-button>
           </div>
           <slot name="step2" :contentHeight="contentHeight"></slot>
         </div>
@@ -49,7 +52,7 @@
         <div
           v-show="showStep2Thumbnail"
           class="prod-thumbnail-wrapper"
-          :style="{ height: viewHeight }"
+          :style="{ height: viewHeight + 'px' }"
           @click="changeActive(2)"
         >
           <div
@@ -74,7 +77,11 @@
             <slot name="step3"></slot>
           </template>
           <template v-else>
-            <ProdDownload></ProdDownload>
+            <ProdDownload
+              :loading="loading"
+              :queryOpt="queryOpt"
+              @submit="onDownload"
+            ></ProdDownload>
           </template>
         </div>
       </transition>
@@ -85,7 +92,7 @@
         <div
           v-show="showStep3Thumbnail"
           class="prod-thumbnail-wrapper"
-          :style="{ height: viewHeight }"
+          :style="{ height: viewHeight + 'px' }"
           @click="changeActive(3)"
         >
           <div
@@ -102,7 +109,7 @@
   </el-row>
 </template>
 <script setup>
-import { computed, inject, ref, watch, onMounted } from 'vue';
+import { computed, inject, ref, watch, onMounted, onUnmounted } from 'vue';
 import { unCalc } from '@/utils/css-util';
 import ProdQueryOpt from '@/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue';
 import ProdDownload from '@/views/fysp/data-product/base-data-product/components/ProdDownload.vue';
@@ -118,9 +125,10 @@
   }
 });
 
-const emit = defineEmits(['update:active', 'onStep1']);
+const emit = defineEmits(['update:active', 'onStep1', 'onStep2', 'onStep3']);
 
-const viewHeight = inject('viewHeight');
+const contentMaxHeight = inject('contentMaxHeight');
+const viewHeight = inject('viewHeight', contentMaxHeight.value);
 
 const btnDisabled = ref(false);
 
@@ -128,13 +136,13 @@
 const contentHeight = ref('50vh');
 
 function calContentHeight() {
-  console.log(titleRef.value.offsetHeight);
-  contentHeight.value = `calc(${unCalc(viewHeight.value)} - ${
-    titleRef.value?.offsetHeight || 0
-  }px)`;
-  console.log(contentHeight.value);
-  
+  // console.log(titleRef.value.offsetHeight);
+  contentHeight.value = viewHeight.value - (titleRef.value?.offsetHeight || 0);
+  // console.log(contentHeight.value);
 }
+
+// 鏁版嵁浜у搧鐢熸垚閫夐」
+const queryOpt = ref({});
 
 // 姝ラ寮曠敤
 const step1Ref = ref(null);
@@ -217,7 +225,11 @@
 }
 
 function onSearch(opt) {
+  queryOpt.value = opt;
   emit('onStep1', opt);
+}
+function onDownload(val) {
+  emit('onStep3', val);
 }
 function changeActive(index) {
   let isAnimate = false;
@@ -234,8 +246,21 @@
   // emit('update:active', index);
 }
 
+let resizeObserver = null;
+
 onMounted(() => {
-  calContentHeight();
+  if (titleRef.value) {
+    resizeObserver = new ResizeObserver(() => {
+      calContentHeight();
+    });
+    resizeObserver.observe(titleRef.value);
+  }
+});
+// 鍦ㄧ粍浠跺嵏杞芥椂娓呯悊
+onUnmounted(() => {
+  if (resizeObserver && titleRef.value) {
+    resizeObserver.unobserve(titleRef);
+  }
 });
 </script>
 <style scoped>
@@ -263,7 +288,10 @@
 }
 
 .prod-title {
-  padding: 10px;
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 }
 
 .prod-thumbnail-wrapper {
@@ -285,7 +313,7 @@
   text-orientation: upright;
   letter-spacing: 8px;
   font-size: 18px;
-  font-weight: 500;
+  font-weight: 600;
   border-top-left-radius: 4px;
   border-bottom-left-radius: 4px;
   cursor: pointer;
diff --git a/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue b/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue
index 54642c5..2ee4fa6 100644
--- a/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue
+++ b/src/views/fysp/data-product/base-data-product/components/ProdDownload.vue
@@ -1,2 +1,68 @@
-<template>ProdDownload</template>
-<script setup></script>
+<template>
+  <el-card shadow="never">
+    <template #header>
+      <div><el-text tag="b" size="large">鏁版嵁浜у搧涓嬭浇</el-text></div>
+    </template>
+    <el-form :inline="false" label-position="left" label-width="150px">
+      <el-form-item label="鍖哄幙">
+        <el-text>{{ queryOpt.districtName }}</el-text>
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿">
+        <el-text>{{ queryOpt.startTime }} 鑷� {{ queryOpt.endTime }}</el-text>
+      </el-form-item>
+      <el-form-item label="鍦烘櫙绫诲瀷">
+        <el-text>{{ queryOpt.sceneTypeName }}</el-text>
+      </el-form-item>
+      <el-form-item label="浜у搧褰㈠紡">
+        <el-radio-group v-model="downloadType">
+          <el-radio value="1"> Excel琛ㄥ崟 </el-radio>
+          <el-radio value="2" :disabled="true"> Word鏂囨。 </el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-row justify="end">
+        <el-button
+          type="primary"
+          size="default"
+          :loading="loading"
+          @click="submit"
+          icon="Download"
+          >涓嬭浇</el-button
+        >
+      </el-row>
+    </template>
+  </el-card>
+</template>
+<script setup>
+import { ref, computed } from 'vue';
+import dayjs from 'dayjs';
+import scene_1 from '@/assets/image/scene_1.png';
+
+const props = defineProps({
+  // 鏁版嵁浜у搧鐢熸垚閫夐」
+  queryOpt: {
+    type: Object,
+    default: () => {}
+  },
+  loading: {
+    type: Boolean,
+    default: false
+  }
+});
+const emit = defineEmits(['submit']);
+
+const downloadType = ref('1');
+
+const submit = () => {
+  emit('submit', {
+    downloadType: downloadType.value
+  });
+};
+</script>
+<style scoped>
+/* .image {
+  width: 200px;
+  height: 200px;
+} */
+</style>
diff --git a/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue b/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue
index 8f9618d..a1ac625 100644
--- a/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue
+++ b/src/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue
@@ -12,7 +12,7 @@
     >
     </SearchBar>
     <template #footer>
-      <el-row v-show="active" justify="space-around">
+      <el-row v-show="active" justify="end">
         <el-button
           type="primary"
           size="default"
@@ -49,16 +49,22 @@
 const search = (options) => {
   const opt = {
     topTaskId: options.topTask.tguid,
+    topTaskName: options.topTask.name,
     provinceCode: options.topTask.provincecode,
+    provinceName: options.topTask.provincename,
     cityCode: options.topTask.citycode,
+    cityName: options.topTask.cityname,
     districtCode: options.topTask.districtcode,
+    districtName: options.topTask.districtname,
     townCode: options.topTask.towncode,
+    townName: options.topTask.townname,
     startTime: dayjs(options.topTask.starttime).format('YYYY-MM-DD HH:mm:ss'),
     endTime: dayjs(options.topTask.endtime)
       .add(1, 'day')
       .add(-1, 'second')
       .format('YYYY-MM-DD HH:mm:ss'),
     sceneTypeId: options.sceneTypeId,
+    sceneTypeName: options.sceneTypeName,
     needCache: true
   };
   emit('submit', opt);
diff --git a/src/views/fysp/data-product/prod-step-change.js b/src/views/fysp/data-product/prod-step-change.js
new file mode 100644
index 0000000..a6630ab
--- /dev/null
+++ b/src/views/fysp/data-product/prod-step-change.js
@@ -0,0 +1,15 @@
+import { ref } from 'vue';
+/**
+ * 鏁版嵁浜у搧姝ラ鍒囨崲
+ */
+export function useProdStepChange() {
+  const active = ref(1);
+  function changeActive() {
+    active.value++;
+    active.value = active.value > 3 ? 1 : active.value;
+  }
+  return {
+    active,
+    changeActive
+  };
+}
diff --git a/src/views/fysp/support/JingAnNightConstruction.vue b/src/views/fysp/support/JingAnNightConstruction.vue
index d8c5b2e..c4066bf 100644
--- a/src/views/fysp/support/JingAnNightConstruction.vue
+++ b/src/views/fysp/support/JingAnNightConstruction.vue
@@ -1,9 +1,12 @@
 <template>
-  <FYTable @search="onSearch">
-    <template #options> </template>
-
-    <template #buttons> </template>
-
+  <FYTable
+    :data="data"
+    :total-count="total"
+    @search="onSearch"
+    :extraHeight="tabsHeaderHeight"
+  >
+    <!-- <template #options> </template>
+    <template #buttons> </template> -->
     <template #table-column>
       <el-table-column
         fixed="left"
@@ -48,40 +51,72 @@
           {{ $fm.formatYMD(row.ncCreateTime) }}
         </template>
       </el-table-column> -->
-      <el-table-column prop="ncUserId" label="鍖归厤鐢ㄦ埛" width="110">
+      <el-table-column prop="ncUserId" label="鍖归厤鐢ㄦ埛">
         <template #default="{ row }">
-          <el-text v-loading="row._loading">{{
-            row._user ? row._user.realName : '鏈尮閰�'
-          }}</el-text>
+          <el-text
+            :loading="row._loading"
+            :type="row._user ? 'primary' : 'danger'"
+            >{{ row._user ? row._user.realname : '鏈尮閰�' }}</el-text
+          >
         </template>
       </el-table-column>
-      <el-table-column fixed="right" label="鎿嶄綔" width="160">
-        <template #default="scope">
+      <el-table-column fixed="right" label="鎿嶄綔" width="80">
+        <template #default="{ row }">
           <el-button
-            :loading="scope.row.loading1"
+            :disabled="row._loading"
             type="default"
             size="small"
-            @click="itemEdit(scope)"
+            @click="itemEdit(row)"
             >缂栬緫</el-button
-          >
-          <el-button
-            :loading="scope.row.loading2"
-            :type="scope.row.extension1 != '0' ? 'danger' : 'primary'"
-            size="small"
-            @click="itemActive(scope)"
-            >{{ scope.row.extension1 != '0' ? '涓嬬嚎' : '涓婄嚎' }}</el-button
           >
         </template>
       </el-table-column>
     </template>
   </FYTable>
+  <el-dialog v-model="dialog" destroy-on-close>
+    <CompInfoSearchFysp
+      v-model="selectedSVUser"
+      :area="area"
+      :defaultText="defaultText"
+      searchTextWidth="400px"
+    />
+    <template #footer>
+      <el-button @click="dialog = false">鍙栨秷</el-button>
+      <el-button type="primary" @click="submit" :disabled="!selectedSVUser"
+        >纭畾</el-button
+      >
+    </template>
+  </el-dialog>
 </template>
 <script setup>
-import { ref } from 'vue';
+import { ref, inject, computed } from 'vue';
 import nightConstructionApi from '@/api/fysp/nightConstructionApi';
 import userApi from '@/api/fysp/userApi';
+import CompInfoSearchFysp from '@/views/fysp/config/components/CompInfoSearchFysp.vue';
+import { ElMessage } from 'element-plus';
 
+const tabsHeaderHeight = inject('tabsHeaderHeight', 0);
+
+// 澶滈棿鏂藉伐璁板綍鍙婃�绘暟
 const data = ref([]);
+const total = ref(0);
+
+// 澶滈棿鏂藉伐璁板綍鍖归厤寮圭獥
+const dialog = ref(false);
+const selectedSVUser = ref(null);
+const selectedRow = ref(null);
+const area = ref({
+  provincecode: '31',
+  provincename: '涓婃捣甯�',
+  citycode: '3100',
+  cityname: '涓婃捣甯�',
+  districtcode: '310106',
+  districtname: '闈欏畨鍖�',
+  scensetypeid: '1'
+});
+const defaultText = computed(() => {
+  return selectedRow.value?.ncItemName || undefined;
+});
 
 function onSearch(page, callback) {
   return nightConstructionApi
@@ -93,17 +128,61 @@
     })
     .then((res) => {
       if (res.success) {
-        res.data.forEach((d) => {
-          res.data._loading = true;
-          userApi.getUserById(d.ncUserId).then((res1) => {
-            res.data._user = res1;
-          });
+        data.value = res.data;
+        data.value.forEach((d) => {
+          d._loading = true;
+          if (d.ncUserId) {
+            userApi
+              .getUserById(d.ncUserId)
+              .then((res1) => {
+                d._user = res1;
+              })
+              .finally(() => {
+                d._loading = false;
+              });
+          } else {
+            d._loading = false;
+          }
         });
-        callback({
-          data: res.data,
-          total: res.head.totalCount
-        });
+        total.value = res.head.totalCount;
+        callback();
       }
     });
 }
+
+function itemEdit(row) {
+  selectedRow.value = row;
+  selectedSVUser.value = row._user;
+  dialog.value = true;
+}
+
+function submit() {
+  if (!selectedSVUser.value) {
+    return ElMessage.error('璇烽�夋嫨鐢ㄦ埛');
+  }
+  nightConstructionApi
+    .updateRecord({
+      recordId: selectedRow.value.ncId,
+      userId: selectedSVUser.value.guid,
+      sceneId: selectedSVUser.value.dguid
+    })
+    .then((res) => {
+      if (res.success) {
+        selectedRow.value.ncUserId = res.data.ncUserId;
+        selectedRow.value.ncSceneId = res.data.ncSceneId;
+        userApi
+          .getUserById(selectedRow.value.ncUserId)
+          .then((res1) => {
+            selectedRow.value._user = res1;
+          })
+          .finally(() => {
+            selectedRow.value._loading = false;
+          });
+      }
+    })
+    .finally(() => {
+      dialog.value = false;
+      selectedRow.value._user = selectedSVUser.value;
+    });
+}
 </script>
diff --git a/src/views/fysp/support/JingAnSupport.vue b/src/views/fysp/support/JingAnSupport.vue
index 60016aa..b3305ee 100644
--- a/src/views/fysp/support/JingAnSupport.vue
+++ b/src/views/fysp/support/JingAnSupport.vue
@@ -1,5 +1,5 @@
 <template>
-  <el-tabs type="border-card">
+  <el-tabs ref="tabsRef">
     <el-tab-pane label="闈欏畨澶滈棿鏂藉伐绠$悊">
       <JingAnNightConstruction></JingAnNightConstruction>
     </el-tab-pane>
@@ -12,8 +12,52 @@
   </el-tabs>
 </template>
 <script setup>
+import { ref, onMounted, provide, computed } from 'vue';
 import NewDevice from './NewDevice.vue';
 import NewConstruction from './NewConstruction.vue';
 import JingAnNightConstruction from './JingAnNightConstruction.vue';
+
+// 瀹氫箟 tabs ref
+const tabsRef = ref(null);
+const tabsHeaderHeight = ref(0);
+
+onMounted(() => {
+  // 纭繚 DOM 宸茬粡娓叉煋瀹屾垚
+  setTimeout(() => {
+    tabsHeaderHeight.value = getTabsHeaderHeight();
+  }, 0);
+});
+
+function getTabsHeaderHeight() {
+  if (tabsRef.value) {
+    // 鑾峰彇 el-tabs 缁勪欢鐨� DOM 鍏冪礌
+    const tabsElement = tabsRef.value.$el;
+
+    // Element UI 鐨� el-tabs header 閫氬父鏈� .el-tabs__header 绫诲悕
+    const headerElement = tabsElement.querySelector('.el-tabs__header');
+
+    if (headerElement) {
+      // 鑾峰彇 header 鐨� offsetHeight锛堝寘鍚� padding 鍜� border锛屼笉鍖呭惈 margin锛�
+      const offsetHeight = headerElement.offsetHeight;
+
+      // 鑾峰彇璁$畻鏍峰紡浠ヨ幏鍙� margin 鍊�
+      const computedStyle = window.getComputedStyle(headerElement);
+
+      // 瑙f瀽 margin 鍊硷紙涓婁笅宸﹀彸锛�
+      const marginTop = parseFloat(computedStyle.marginTop || 0);
+      const marginBottom = parseFloat(computedStyle.marginBottom || 0);
+      // const marginLeft = parseFloat(computedStyle.marginLeft || 0);
+      // const marginRight = parseFloat(computedStyle.marginRight || 0);
+
+      // 璁$畻鎬婚珮搴︼紙鍖呭惈鎵�鏈� padding銆乥order 鍜� margin锛�
+      const totalHeightWithMargin = offsetHeight + marginTop + marginBottom;
+
+      return totalHeightWithMargin;
+    }
+  }
+  return 0;
+}
+
+provide('tabsHeaderHeight', computed(() => tabsHeaderHeight.value));
 </script>
 <style scoped></style>

--
Gitblit v1.9.3