From d7d7da5c09340eafcd2e2c672e6b2c001a4cc0be Mon Sep 17 00:00:00 2001
From: hcong <1050828145@qq.com>
Date: 星期五, 08 十一月 2024 08:38:40 +0800
Subject: [PATCH] 基础数据产品-场景清单,监管清单

---
 package-lock.json                                                             |  258 ++++
 src/views/fysp/data-product/components/CompProdTreatmentDeviceDescription.vue |   61 +
 src/views/fysp/data-product/components/CompGooderProdStage,.vue               |  106 +
 src/views/fysp/data-product/js/tableCol.js                                    |   21 
 public/test.docx                                                              |    0 
 src/components.d.ts                                                           |    8 
 src/constants/menu.js                                                         |   15 
 src/api/fysp/taskApi.js                                                       |   15 
 src/views/fysp/data-product/components/CompShowEChart.vue                     |  211 +++
 src/views/fysp/data-product/components/CompPreviewProd.vue                    |   14 
 src/views/fysp/data-product/components/DynamicTable.vue                       |  246 ++++
 src/views/fysp/data-product/ProdScenseInfo.vue                                |  637 ++++++++++
 src/views/fysp/data-product/ProdMonitorTaskInfo.vue                           |  600 ++++++++++
 src/views/fysp/data-product/components/TableColumn.vue                        |   71 +
 src/router/index.js                                                           |   18 
 src/views/fysp/data-product/components/CompSelectProdStage.vue                |   85 +
 src/views/fysp/data-product/js/htmlTransfer.js                                |  120 ++
 src/views/fysp/data-product/components/MutiableOptionScene.vue                |   84 +
 src/views/fysp/data-product/components/OptionChartProd.vue                    |   47 
 src/views/fysp/data-product/js/genChart.js                                    |  123 ++
 src/main.js                                                                   |    9 
 src/views/fysp/data-product/components/CompChangeHeaderTree.vue               |   90 +
 package.json                                                                  |    9 
 src/views/fysp/data-product/components/ComSelectMonitorTaskProdStage.vue      |  143 ++
 src/views/fysp/data-product/components/CompProdSteps.vue                      |    9 
 src/views/fysp/data-product/ProdTreatmentDeviceInfo.vue                       |   80 +
 src/views/fysp/data-product/components/CompProdGenMonitorInfoDescription.vue  |  109 +
 src/views/fysp/data-product/components/CompProdGenScenseDescription.vue       |  286 ++++
 28 files changed, 3,463 insertions(+), 12 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 8c46b4e..ae86edd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,13 +13,21 @@
         "@element-plus/icons-vue": "^2.0.10",
         "@vue-office/excel": "^1.7.11",
         "@vueuse/core": "^9.7.0",
+        "angular-expressions": "^1.4.0",
         "axios": "^1.2.1",
         "dayjs": "^1.11.13",
+        "docxtemplater": "^3.9.0",
+        "docxtemplater-image-module-free": "^1.1.1",
+        "echarts": "^4.8.0",
         "element-plus": "^2.8.3",
         "exceljs": "^4.4.0",
+        "file-saver": "^2.0.5",
+        "html2canvas": "^1.4.1",
         "js-base64": "^3.7.5",
+        "jszip-utils": "^0.1.0",
         "md5": "^2.3.0",
         "pinia": "^2.0.26",
+        "pizzip": "^3.1.7",
         "vue": "^3.2.45",
         "vue-demi": "^0.14.6",
         "vue-i18n": "^9.8.0",
@@ -31,6 +39,7 @@
         "@babel/core": "^7.20.5",
         "@babel/eslint-parser": "^7.19.1",
         "@babel/preset-env": "^7.20.2",
+        "@types/html-docx-js": "^0.3.4",
         "@typescript-eslint/eslint-plugin": "^6.10.0",
         "@typescript-eslint/parser": "^6.10.0",
         "@vitejs/plugin-vue": "^3.2.0",
@@ -2221,6 +2230,16 @@
       "license": "MIT",
       "peer": true
     },
+    "node_modules/@types/html-docx-js": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmmirror.com/@types/html-docx-js/-/html-docx-js-0.3.4.tgz",
+      "integrity": "sha512-+t/4SxNnEq+0xbSUG7LrOOfG/y12aTXBjYKtOYtigVRIIOCEmK7jh0pSUeW5ATvI934274hkzdBg57t5RDrZow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "dev": true,
@@ -2853,6 +2872,12 @@
         "ajv": "^6.9.1"
       }
     },
+    "node_modules/angular-expressions": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/angular-expressions/-/angular-expressions-1.4.0.tgz",
+      "integrity": "sha512-LmbYpeZMuMhifpk+LmF2li9FzH2kYPwF/ykwHFKSnLrk8zJ8sZUjipcmGjJoHQTPcfj/YhkUoQYVbyZ08jDdYg==",
+      "license": "Unlicense"
+    },
     "node_modules/ansi-colors": {
       "version": "4.1.3",
       "dev": true,
@@ -3191,6 +3216,15 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "license": "MIT"
+    },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
     },
     "node_modules/base64-js": {
       "version": "1.5.1",
@@ -3745,6 +3779,15 @@
         "node": "*"
       }
     },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/cssesc": {
       "version": "3.0.0",
       "dev": true,
@@ -3933,6 +3976,27 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/docxtemplater": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.9.0.tgz",
+      "integrity": "sha512-l74k7jtT/Ui3Y+KZOxaUr85HxfsHZJ3xaYzYfGmgpHavfsxvtu2pF7rU/AqERGpY59mip0gj0PxqfI5Wfcxw7Q==",
+      "license": "MIT",
+      "dependencies": {
+        "xmldom": "^0.1.27"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/docxtemplater-image-module-free": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz",
+      "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==",
+      "license": "MIT",
+      "dependencies": {
+        "xmldom": "^0.1.27"
+      }
+    },
     "node_modules/domexception": {
       "version": "4.0.0",
       "dev": true,
@@ -3991,6 +4055,15 @@
       "dependencies": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
+      }
+    },
+    "node_modules/echarts": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-4.8.0.tgz",
+      "integrity": "sha512-YwShpug8fWngj/RlgxDaYrLBoD+LsZUArrusjNPHpAF+is+gGe38xx4W848AwWMGoi745t3OXM52JedNrv+F6g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "zrender": "4.3.1"
       }
     },
     "node_modules/electron-to-chromium": {
@@ -4692,6 +4765,12 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
+      "license": "MIT"
+    },
     "node_modules/fill-range": {
       "version": "7.0.1",
       "dev": true,
@@ -5037,6 +5116,19 @@
       },
       "engines": {
         "node": ">=12"
+      }
+    },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "license": "MIT",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
       }
     },
     "node_modules/http-proxy-agent": {
@@ -5566,6 +5658,12 @@
         "readable-stream": "~2.3.6",
         "setimmediate": "^1.0.5"
       }
+    },
+    "node_modules/jszip-utils": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/jszip-utils/-/jszip-utils-0.1.0.tgz",
+      "integrity": "sha512-tBNe0o3HAf8vo0BrOYnLPnXNo5A3KsRMnkBFYjh20Y3GPYGfgyoclEMgvVchx0nnL+mherPi74yLPIusHUQpZg==",
+      "license": "(MIT OR GPL-3.0)"
     },
     "node_modules/jszip/node_modules/readable-stream": {
       "version": "2.3.8",
@@ -6431,6 +6529,21 @@
           "optional": true
         }
       }
+    },
+    "node_modules/pizzip": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmmirror.com/pizzip/-/pizzip-3.1.7.tgz",
+      "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==",
+      "license": "(MIT OR GPL-3.0)",
+      "dependencies": {
+        "pako": "^2.1.0"
+      }
+    },
+    "node_modules/pizzip/node_modules/pako": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
+      "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+      "license": "(MIT AND Zlib)"
     },
     "node_modules/postcss": {
       "version": "8.4.20",
@@ -7317,6 +7430,15 @@
       "license": "MIT",
       "peer": true
     },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "dev": true,
@@ -7798,6 +7920,15 @@
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "license": "MIT"
+    },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
     },
     "node_modules/uuid": {
       "version": "8.3.2",
@@ -8290,6 +8421,16 @@
       "version": "2.2.0",
       "license": "MIT"
     },
+    "node_modules/xmldom": {
+      "version": "0.1.31",
+      "resolved": "https://registry.npmmirror.com/xmldom/-/xmldom-0.1.31.tgz",
+      "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
+      "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0",
+      "license": "(LGPL-2.0 or MIT)",
+      "engines": {
+        "node": ">=0.1"
+      }
+    },
     "node_modules/yallist": {
       "version": "4.0.0",
       "dev": true,
@@ -8347,6 +8488,12 @@
       "engines": {
         "node": ">= 10"
       }
+    },
+    "node_modules/zrender": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-4.3.1.tgz",
+      "integrity": "sha512-CeH2TpJeCdG0TAGYoPSAcFX2ogdug1K7LIn9UO/q9HWqQ54gWhrMAlDP9AwWYMUDhrPe4VeazQ4DW3msD96nUQ==",
+      "license": "BSD-3-Clause"
     }
   },
   "dependencies": {
@@ -9727,6 +9874,15 @@
       "dev": true,
       "peer": true
     },
+    "@types/html-docx-js": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmmirror.com/@types/html-docx-js/-/html-docx-js-0.3.4.tgz",
+      "integrity": "sha512-+t/4SxNnEq+0xbSUG7LrOOfG/y12aTXBjYKtOYtigVRIIOCEmK7jh0pSUeW5ATvI934274hkzdBg57t5RDrZow==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/json-schema": {
       "version": "7.0.15",
       "dev": true
@@ -10194,6 +10350,11 @@
       "peer": true,
       "requires": {}
     },
+    "angular-expressions": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/angular-expressions/-/angular-expressions-1.4.0.tgz",
+      "integrity": "sha512-LmbYpeZMuMhifpk+LmF2li9FzH2kYPwF/ykwHFKSnLrk8zJ8sZUjipcmGjJoHQTPcfj/YhkUoQYVbyZ08jDdYg=="
+    },
     "ansi-colors": {
       "version": "4.1.3",
       "dev": true
@@ -10413,6 +10574,11 @@
     },
     "balanced-match": {
       "version": "1.0.2"
+    },
+    "base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
     },
     "base64-js": {
       "version": "1.5.1"
@@ -10745,6 +10911,14 @@
       "resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
       "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
     },
+    "css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "cssesc": {
       "version": "3.0.0",
       "dev": true
@@ -10879,6 +11053,22 @@
         "esutils": "^2.0.2"
       }
     },
+    "docxtemplater": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.9.0.tgz",
+      "integrity": "sha512-l74k7jtT/Ui3Y+KZOxaUr85HxfsHZJ3xaYzYfGmgpHavfsxvtu2pF7rU/AqERGpY59mip0gj0PxqfI5Wfcxw7Q==",
+      "requires": {
+        "xmldom": "^0.1.27"
+      }
+    },
+    "docxtemplater-image-module-free": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz",
+      "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==",
+      "requires": {
+        "xmldom": "^0.1.27"
+      }
+    },
     "domexception": {
       "version": "4.0.0",
       "dev": true,
@@ -10933,6 +11123,14 @@
       "requires": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
+      }
+    },
+    "echarts": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-4.8.0.tgz",
+      "integrity": "sha512-YwShpug8fWngj/RlgxDaYrLBoD+LsZUArrusjNPHpAF+is+gGe38xx4W848AwWMGoi745t3OXM52JedNrv+F6g==",
+      "requires": {
+        "zrender": "4.3.1"
       }
     },
     "electron-to-chromium": {
@@ -11406,6 +11604,11 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "fill-range": {
       "version": "7.0.1",
       "dev": true,
@@ -11626,6 +11829,15 @@
       "dev": true,
       "requires": {
         "whatwg-encoding": "^2.0.0"
+      }
+    },
+    "html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "requires": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
       }
     },
     "http-proxy-agent": {
@@ -11994,6 +12206,11 @@
           }
         }
       }
+    },
+    "jszip-utils": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/jszip-utils/-/jszip-utils-0.1.0.tgz",
+      "integrity": "sha512-tBNe0o3HAf8vo0BrOYnLPnXNo5A3KsRMnkBFYjh20Y3GPYGfgyoclEMgvVchx0nnL+mherPi74yLPIusHUQpZg=="
     },
     "klona": {
       "version": "2.0.5",
@@ -12544,6 +12761,21 @@
         "vue-demi": "*"
       }
     },
+    "pizzip": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmmirror.com/pizzip/-/pizzip-3.1.7.tgz",
+      "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==",
+      "requires": {
+        "pako": "^2.1.0"
+      },
+      "dependencies": {
+        "pako": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
+          "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+        }
+      }
+    },
     "postcss": {
       "version": "8.4.20",
       "requires": {
@@ -13085,6 +13317,14 @@
         "terser": "^5.14.1"
       }
     },
+    "text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "text-table": {
       "version": "0.2.0",
       "dev": true
@@ -13393,6 +13633,14 @@
     "util-deprecate": {
       "version": "1.0.2"
     },
+    "utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "requires": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "uuid": {
       "version": "8.3.2"
     },
@@ -13654,6 +13902,11 @@
     "xmlchars": {
       "version": "2.2.0"
     },
+    "xmldom": {
+      "version": "0.1.31",
+      "resolved": "https://registry.npmmirror.com/xmldom/-/xmldom-0.1.31.tgz",
+      "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
+    },
     "yallist": {
       "version": "4.0.0",
       "dev": true
@@ -13698,6 +13951,11 @@
           }
         }
       }
+    },
+    "zrender": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-4.3.1.tgz",
+      "integrity": "sha512-CeH2TpJeCdG0TAGYoPSAcFX2ogdug1K7LIn9UO/q9HWqQ54gWhrMAlDP9AwWYMUDhrPe4VeazQ4DW3msD96nUQ=="
     }
   }
 }
diff --git a/package.json b/package.json
index 7308c77..ca7301b 100644
--- a/package.json
+++ b/package.json
@@ -18,13 +18,21 @@
     "@element-plus/icons-vue": "^2.0.10",
     "@vue-office/excel": "^1.7.11",
     "@vueuse/core": "^9.7.0",
+    "angular-expressions": "^1.4.0",
     "axios": "^1.2.1",
     "dayjs": "^1.11.13",
+    "docxtemplater": "^3.9.0",
+    "docxtemplater-image-module-free": "^1.1.1",
+    "echarts": "^4.8.0",
     "element-plus": "^2.8.3",
     "exceljs": "^4.4.0",
+    "file-saver": "^2.0.5",
+    "html2canvas": "^1.4.1",
     "js-base64": "^3.7.5",
+    "jszip-utils": "^0.1.0",
     "md5": "^2.3.0",
     "pinia": "^2.0.26",
+    "pizzip": "^3.1.7",
     "vue": "^3.2.45",
     "vue-demi": "^0.14.6",
     "vue-i18n": "^9.8.0",
@@ -36,6 +44,7 @@
     "@babel/core": "^7.20.5",
     "@babel/eslint-parser": "^7.19.1",
     "@babel/preset-env": "^7.20.2",
+    "@types/html-docx-js": "^0.3.4",
     "@typescript-eslint/eslint-plugin": "^6.10.0",
     "@typescript-eslint/parser": "^6.10.0",
     "@vitejs/plugin-vue": "^3.2.0",
diff --git a/public/test.docx b/public/test.docx
new file mode 100644
index 0000000..b37c1d1
--- /dev/null
+++ b/public/test.docx
Binary files differ
diff --git a/src/api/fysp/taskApi.js b/src/api/fysp/taskApi.js
index d7f918b..98c4c90 100644
--- a/src/api/fysp/taskApi.js
+++ b/src/api/fysp/taskApi.js
@@ -22,6 +22,13 @@
     return $fysp.get('task/alltask/0').then((res) => res.data);
   },
 
+  /**
+   * 鑾峰彇椤跺眰浠诲姟鍜屾棩浠诲姟
+   */
+  getTopTaskWithDayTask() {
+    return $fysp.get('task/alltask/1').then((res) => res.data);
+  },
+
   getLastTopTask(task){
     return $fysp.post(`task/lastTask`, task).then((res) => res.data);
   },
@@ -119,9 +126,11 @@
   /** 
    * 閫氳繃鎬讳换鍔d鍜屾椂闂村尯闂磋幏鍙栧瓙浠诲姟鍒楄〃
    */
-  async getByTopTaskAndDate({startTime, endTime, sceneTypeId, topTaskId}) {
-    const params = `?startTime=${startTime}&endTime=${endTime}&sceneTypeId=${sceneTypeId}&topTaskId=${topTaskId}`;
-    return await $fysp.get(`subtask/getSubTask${params}`).then((res) => res.data);
+  async getByTopTaskAndDate({startTime = null, endTime = null, sceneTypeId = null, topTaskId}) {
+    // const params = `?startTime=${startTime}&endTime=${endTime}&sceneTypeId=${sceneTypeId}&topTaskId=${topTaskId}`;
+    return await $fysp.get(`subtask/getSubTask`, {
+      params: {startTime: startTime, endTime: endTime, sceneTypeId: sceneTypeId, topTaskId: topTaskId}
+    }).then((res) => res.data);
   },
   /** 
    * 鑾峰彇鏌愪釜鍦烘櫙鐨勫贰鏌ヤ换鍔�
diff --git a/src/components.d.ts b/src/components.d.ts
index 37e7435..124cacb 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -12,23 +12,18 @@
     BasePanelLayout: typeof import('./components/core/BasePanelLayout.vue')['default']
     CompQuickSet: typeof import('./components/search-option/CompQuickSet.vue')['default']
     Content: typeof import('./components/core/Content.vue')['default']
-    ElAffix: typeof import('element-plus/es')['ElAffix']
     ElAside: typeof import('element-plus/es')['ElAside']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     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']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElCollapse: typeof import('element-plus/es')['ElCollapse']
     ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElContainer: typeof import('element-plus/es')['ElContainer']
-    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
     ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
@@ -41,7 +36,6 @@
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
-    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']
@@ -50,7 +44,6 @@
     ElPopover: typeof import('element-plus/es')['ElPopover']
     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']
@@ -62,7 +55,6 @@
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
-    ElTooltip: typeof import('element-plus/es')['ElTooltip']
     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/constants/menu.js b/src/constants/menu.js
index 9e744ee..a1637ba 100644
--- a/src/constants/menu.js
+++ b/src/constants/menu.js
@@ -64,6 +64,21 @@
         icon: 'Document',
         name: '鍦烘櫙鎶ュ憡',
       },
+      {
+        path: '/fysp/data-product/ProdScenseInfo',
+        icon: 'Document',
+        name: '鍦烘櫙娓呭崟',
+      },
+      {
+        path: '/fysp/data-product/ProdMonitorTaskInfo',
+        icon: 'Document',
+        name: '鐩戠娓呭崟',
+      },
+      {
+        path: '/fysp/data-product/ProdTreatmentDeviceInfo',
+        icon: 'Document',
+        name: '闃叉不璁惧娓呭崟',
+      },
     ],
   },
   // {
diff --git a/src/main.js b/src/main.js
index 511d8bd..95195f2 100644
--- a/src/main.js
+++ b/src/main.js
@@ -14,6 +14,10 @@
 import 'element-plus/theme-chalk/src/message-box.scss';
 import 'element-plus/theme-chalk/src/notification.scss';
 
+// 寮曞叆echarts
+// import Echarts from 'vue-echarts'
+import * as echarts from 'echarts'
+
 // dayjs plugin
 import dayjs from 'dayjs';
 import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
@@ -29,7 +33,10 @@
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component);
 }
-
+// 浣跨敤缁勪欢
+// app.component('e-charts',Echarts)
+// 鍏ㄥ眬鎸傝浇 echarts
+app.config.globalProperties.$echarts = echarts
 app
   .use(pinia)
   .use(router)
diff --git a/src/router/index.js b/src/router/index.js
index 41e3ccd..72fa525 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -41,6 +41,24 @@
     path: '/fysp/data-product/scenereport',
     component: () => import('@/views/fysp/data-product/ProdSceneReport.vue')
   },
+  {
+    // 鍩虹浜у搧-鍦烘櫙娓呭崟
+    name: 'ProdScenseInfo',
+    path: '/fysp/data-product/ProdScenseInfo',
+    component: () => import('@/views/fysp/data-product/ProdScenseInfo.vue')
+  },
+  {
+    // 鍩虹浜у搧-鐩戠娓呭崟
+    name: 'ProdMonitorTaskInfo',
+    path: '/fysp/data-product/ProdMonitorTaskInfo',
+    component: () => import('@/views/fysp/data-product/ProdMonitorTaskInfo.vue')
+  },
+  {
+    // 鍩虹浜у搧-闃叉不璁惧娓呭崟
+    name: 'ProdTreatmentDeviceInfo',
+    path: '/fysp/data-product/ProdTreatmentDeviceInfo',
+    component: () => import('@/views/fysp/data-product/ProdTreatmentDeviceInfo.vue')
+  },
   // {
   //   //鍦烘櫙鎶ュ憡-宸ュ湴
   //   name: 'construction',
diff --git a/src/views/fysp/data-product/ProdMonitorTaskInfo.vue b/src/views/fysp/data-product/ProdMonitorTaskInfo.vue
new file mode 100644
index 0000000..120f59f
--- /dev/null
+++ b/src/views/fysp/data-product/ProdMonitorTaskInfo.vue
@@ -0,0 +1,600 @@
+<template>
+  <el-col>
+    <el-row justify="end" class="fixed-div">
+      <!-- 鎸夐挳缁� -->
+      <div class="btn-group">
+        <el-button v-if="this.activeStep.id > 0" @click="beforeStep"
+          >涓婁竴姝�</el-button
+        >
+        <el-button
+          v-if="this.activeStep.id == 0"
+          @click="genDialog = true"
+          type="primary"
+          >浜у搧鐢熸垚閰嶇疆</el-button
+        >
+        <el-button
+          v-if="this.activeStep.id == 1"
+          @click="gooderDialog = true"
+          type="primary"
+          >浜у搧浼樺寲閰嶇疆</el-button
+        >
+        <!-- <el-button type="primary" @click="chartDialog = true"
+          >鍥捐〃灞曠ず</el-button
+        > -->
+        <el-button
+          v-if="this.activeStep.id == 2"
+          type="primary"
+          @click="previewProduct"
+          >鐢熸垚</el-button
+        >
+      </div>
+    </el-row>
+  </el-col>
+  <!-- 姝ラ -->
+  <el-steps
+    :active="activeStep.id"
+    finish-status="success"
+    style=""
+    align-center
+  >
+    <el-step v-for="(s, i) in steps" :key="s.id" :title="s.name" />
+  </el-steps>
+  <div class="center-div">
+    <span class="title-input"> {{ title }} </span><br />
+  </div>
+  <div class="center-div">
+    <div
+      v-show="this.activeStep.id != 0"
+      class="main"
+      v-loading="searchLoading"
+    >
+      <span class="second-title"> {{ `涓�銆佺洃绠℃鍐礰 }} </span>
+      <CompProdGenMonitorInfoDescription
+        id="descriptionComp"
+        class="product-item-margin"
+        :table-data="tableData"
+        :location="formSearch._locations"
+        ref="prodGenDescriptionRef"
+        @generated-description="onGeneratedDescription"
+      >
+      </CompProdGenMonitorInfoDescription>
+      <template v-if="showChart">
+        <span class="second-title"> {{ `浜屻�佺粺璁¢�忚` }} </span>
+        <div class="charts product-item-margin">
+          <div class="chart-item" v-for="chart in charts" :key="chart.id">
+            <div class="chart" ref="myChart" :id="chart.id"></div>
+            <!-- <el-button
+              type="danger"
+              @click="charts = charts.filter((item) => item.id != chart.id)"
+              >鍒犻櫎</el-button
+            > -->
+          </div>
+        </div>
+      </template>
+      <span class="second-title"> {{ `涓夈�佽缁嗘竻鍗昤 }} </span>
+      <DynamicTable
+        ref="dynamicTableRef"
+        v-loading="loading"
+        :table-header="tableConfig"
+        @search="onSearch"
+        :pagination="false"
+      ></DynamicTable>
+    </div>
+    <el-empty
+      v-show="this.activeStep.id == 0"
+      style="font-size: large"
+      description="鏈厤缃骇鍝�"
+    />
+  </div>
+
+  <el-dialog title="浜у搧鐢熸垚閰嶇疆" v-model="genDialog">
+    <ComSelectMonitorTaskProdStage
+      :table-header="tableConfig"
+      @next="selectProdStageOver"
+    >
+    </ComSelectMonitorTaskProdStage>
+  </el-dialog>
+  <el-dialog title="浜у搧浼樺寲" v-model="gooderDialog" destroy-on-close>
+    <CompGooderProdStage
+      :table-header="tableConfig"
+      @selected-gooder="onselectedGooder"
+    >
+    </CompGooderProdStage>
+  </el-dialog>
+  <el-dialog title="鍥捐〃灞曠ず" v-model="chartDialog" destroy-on-close>
+    <CompShowEChart
+      @painted-chart="onPaintedChart"
+      :table-data="tableData"
+      :table-header="tableConfig"
+      ref="chartRef"
+    >
+    </CompShowEChart>
+  </el-dialog>
+</template>
+<script>
+import sceneApi from '@/api/fysp/sceneApi';
+import taskApi from '@/api/fysp/taskApi.js';
+import DynamicTable from './components/DynamicTable.vue';
+import { load } from '@amap/amap-jsapi-loader';
+import ComSelectMonitorTaskProdStage from './components/ComSelectMonitorTaskProdStage.vue';
+import CompProdGenMonitorInfoDescription from './components/CompProdGenMonitorInfoDescription.vue';
+import CompShowEChart from './components/CompShowEChart.vue';
+import htmlTransfer from './js/htmlTransfer';
+import dayjs from 'dayjs';
+import CompGooderProdStage from './components/CompGooderProdStage,.vue';
+import genChart from './js/genChart';
+export default {
+  components: {
+    DynamicTable,
+    ComSelectMonitorTaskProdStage,
+    CompProdGenMonitorInfoDescription,
+    CompShowEChart,
+    CompGooderProdStage
+  },
+  computed: {
+    title() {
+      return (
+        (this.formSearch._locations.tName ||
+          this.formSearch._locations.dName ||
+          this.formSearch._locations.cName ||
+          this.formSearch._locations.pName ||
+          '') + '鐩戠娓呭崟'
+      );
+    }
+  },
+  watch: {
+    oneValue: {
+      handler(nv, ov) {
+        console.log('master', nv);
+      },
+      immediate: true,
+      deep: true
+    }
+  },
+  data() {
+    return {
+      gooderDialog: false,
+      activeStep: {
+        id: 0,
+        name: '浜у搧閰嶇疆鐢熸垚'
+      },
+      steps: [
+        {
+          id: 0,
+          name: '浜у搧閰嶇疆鐢熸垚'
+        },
+        {
+          id: 1,
+          name: '浜у搧閰嶇疆浼樺寲'
+        },
+        {
+          id: 2,
+          name: '鐢熸垚'
+        }
+      ],
+      mainVisible: false,
+      showChart: false,
+      chartDialog: false,
+      genDialog: false,
+      oneValue: {
+        _locations: {},
+        _scenetype: []
+      },
+      charts: [],
+      chart: {},
+      loading: false,
+      tableConfig: [],
+      tableData: [],
+      formSearch: {
+        topTask: '',
+        _locations: {},
+        searchText: '',
+        _scenetype: {},
+        online: {},
+        topTasks: []
+      },
+      topTasks: []
+    };
+  },
+  mounted() {
+    this.getOptions();
+    this.genHeaders();
+  },
+  methods: {
+    genWord() {
+      let imgUrls = this.charts.map((item) => {
+        return htmlTransfer.chartToImageUrl(
+          this.$echarts.getInstanceByDom(document.getElementById(item.id))
+        );
+      });
+      const data = {
+        title: this.title,
+        description: this.description,
+        tableData: this.tableData,
+        imgUrls: imgUrls
+      };
+      const imgSize = {
+        imgUrl: [200, 200]
+      };
+      htmlTransfer.ExportBriefDataDocx(
+        '.\\test.docx', // 妯℃澘璺緞
+        data, // 鏁版嵁
+        this.title, // 鏂囦欢鍚�
+        imgSize
+      );
+    },
+    previewProduct() {
+      this.genWord();
+    },
+    onGeneratedDescription(description) {
+      this.description = description;
+    },
+    onPaintedChart(chartOption) {
+      this.showChart = true;
+      let id = `chart${this.charts.length}`;
+      this.charts.push({
+        id: id,
+        option: chartOption
+      });
+      console.log('chartOption', chartOption);
+      setTimeout(() => {
+        const dom = document.getElementById(id);
+
+        const myChart = this.$echarts.init(dom); // 鍒濆鍖杄charts瀹炰緥
+        this.chart = myChart;
+        myChart.setOption(chartOption);
+        this.chartDialog = false;
+      }, 100);
+    },
+    // 绗竴闃舵纭畾涔嬪悗鐨勫洖璋�
+    selectProdStageOver(result) {
+      this.genDialog = false;
+      this.nextStep();
+      if (result != null) {
+        this.formSearch._locations = result._locations;
+        this.formSearch.topTasks = result.topTasks;
+
+        this.$refs.dynamicTableRef.onSearch();
+      }
+      console.log('绗竴闃舵杩斿洖鍊�', result);
+    },
+    onTopTaskChange(value) {
+      this.$refs.dynamicTableRef.onSearch();
+    },
+    //鑾峰彇鏌ヨ鏉′欢
+    getOptions() {
+      taskApi.getTopTask().then((res) => {
+        const list = res.map((r) => {
+          return {
+            value: r.tguid,
+            label: r.name,
+            data: r
+          };
+        });
+        this.topTasks = list;
+        this.formSearch.topTask = list[0];
+        // this.$refs.dynamicTableRef.onSearch();
+      });
+    },
+    addSceneRegion(obj) {
+      obj.scene._region =
+        obj.scene.provincename +
+        obj.scene.cityname +
+        obj.scene.districtname +
+        obj.scene.townname +
+        obj.scene.location;
+    },
+    standardSupervisedNum(obj) {
+      obj.extension1 = obj.extension1 ? obj.extension1 : 0;
+    },
+    addSupervisedTime(monitorObj) {
+      console.log('monitorObj', monitorObj);
+      if (!('_subTasks' in monitorObj) || monitorObj._subTasks.length == 0) {
+        monitorObj._executionstarttime = '/';
+        return;
+      }
+      monitorObj._executionstarttime = monitorObj._subTasks
+        .map((item) => dayjs(item.executionstarttime).format('YYYY-MM-DD'))
+        .join(',');
+    },
+
+    onSearch(page, func) {
+      this.loading = true;
+      // 鑾峰彇褰撳墠鐩戠浠诲姟涓嬫墍鏈夊瓙浠诲姟
+      const getAllNeedSubTaskPromises = this.formSearch.topTasks.map((item) => {
+        return taskApi.getByTopTaskAndDate({ topTaskId: item.tguid });
+      });
+
+      const subTaskPromises = this.formSearch.topTasks.map((item) => {
+        return taskApi.fetchMonitorObjectVersion(item.tguid);
+      });
+      Promise.all(getAllNeedSubTaskPromises)
+        .then((results) => {
+          // 灏嗕簩缁存暟缁勫睍骞虫垚涓�缁存暟缁�
+          const subTasks = results.reduce((acc, val) => acc.concat(val), []);
+          console.log('subTasks', subTasks);
+
+          return Promise.all(subTasks);
+        })
+        .then((allSubTasks) => {
+          // 浣跨敤 Promise.all 绛夊緟鎵�鏈夌殑瀛愪换鍔¤姹傚畬鎴�
+          Promise.all(subTaskPromises)
+            .then((results) => {
+              console.log('results', results);
+              // 灏嗕簩缁存暟缁勫睍骞虫垚涓�缁存暟缁�
+              const flatResults = results.reduce(
+                (acc, val) => acc.concat(val),
+                []
+              );
+              // 鍐嶆浣跨敤 Promise.all 澶勭悊涓�缁存暟缁勪腑鐨勬瘡涓厓绱�
+              return Promise.all(flatResults);
+            })
+            .then((moniterRes) => {
+              // 淇濆瓨鏃ヤ换鍔″垪琛�
+              taskApi.getTopTaskWithDayTask().then((allTopAndDay) => {
+                let topWithDyTask = allTopAndDay.filter((item) => {
+                  return (
+                    this.formSearch.topTasks
+                      .map((top) => top.tguid)
+                      .indexOf(item.tguid) != -1
+                  );
+                });
+
+                const subTasks = [];
+                for (const r of moniterRes) {
+                  // 璧嬪�兼�讳换鍔� _topTask.daytaskList 涓烘棩浠诲姟鍒楄〃
+                  r._topTask = topWithDyTask.filter(
+                    (item) => item.tguid == r.tid
+                  )[0];
+                  r._topTask.daytaskList.forEach((dayTask) => {
+                    let dayTaskId = dayTask.tguid;
+                    let topTaskId = dayTask.tsguid;
+                    let sceneId = r.sguid;
+                    let subTasks = this.selectSubTaskBySceneIdAndTaskId(
+                      allSubTasks,
+                      sceneId,
+                      topTaskId,
+                      dayTaskId
+                    );
+                    if (subTasks) {
+                      r._subTasks = subTasks;
+                    }
+                  });
+                  // 鐢熸垚鍦烘櫙鍖哄煙鍒�
+                  this.addSceneRegion(r);
+                  // 鏍囧噯鍖栧凡鐩戠娆℃暟
+                  this.standardSupervisedNum(r);
+                  // 鐢熸垚鐩戠鏃堕棿鍒�
+                  this.addSupervisedTime(r);
+                  // r._topTask = this.formSearch.topTask.data;
+                  subTasks.push(r);
+                }
+                this.tableData = subTasks;
+                this.genSomeCharts();
+                func({
+                  data: subTasks
+                });
+                this.loading = false;
+              });
+            })
+            .catch((error) => {
+              this.loading = false;
+              console.error('Error fetching subtasks:', error);
+            });
+        });
+    },
+    genSomeCharts() {
+      console.log("this.ta", this.tableData);
+      // 娣诲姞鍥捐〃
+      this.charts = [];
+      this.showChart = true;
+      // 鐢熸垚鍖哄煙涓殑涓嬩竴绾х殑鍥捐〃
+      const l = this.formSearch._locations;
+      console.log(this.tableData);
+
+      let positionProp = '';
+      if (l.pName != null) {
+        positionProp = 'cityname';
+        if (l.cName != null) {
+          positionProp = 'districtname';
+          if (l.dName != null) {
+            positionProp = 'townname';
+            if (l.tName != null) {
+              positionProp = 'townname';
+            }
+          }
+        }
+      }
+      positionProp = 'scene.' + positionProp;
+      [
+        genChart.getChartByDataAndProp(this.tableData, 'sceneType', '绫诲瀷', 'bar'),
+        genChart.getPieChartByDataAndProp(
+          this.tableData,
+          positionProp,
+          '鎵�灞炲尯鍩�'
+        )
+      ].forEach((item) => {
+        let id = `chart${this.charts.length}`;
+        this.charts.push({
+          id: id,
+          option: item
+        });
+        setTimeout(() => {
+          this.paintChart(id, item);
+        }, 100);
+      });
+    },
+    paintChart(id, chartOption) {
+      const dom = document.getElementById(id);
+      var startX, startY, chartWidth, chartHeight;
+      const myChart = this.$echarts.init(dom); // 鍒濆鍖杄charts瀹炰緥
+      // 寮�濮嬫嫋鎷�
+      myChart.getZr().on('mousedown', function (event) {
+        if (event.target) {
+          startX = event.offsetX;
+          startY = event.offsetY;
+          chartWidth = myChart.getWidth();
+          chartHeight = myChart.getHeight();
+        }
+      });
+
+      // 鎷栧姩璋冩暣瀹藉害
+      myChart.getZr().on('mouseup', function (event) {
+        if (startX != null || startY != null) {
+          var deltaX = event.offsetX - startX;
+          var deltaY = event.offsetY - startY;
+          myChart.resize({
+            width: chartWidth + deltaX,
+            height: chartHeight + deltaY
+          });
+        }
+        startX = null;
+        startY = null;
+      });
+
+      this.chart = myChart;
+      myChart.setOption(chartOption);
+      this.chartDialog = false;
+    },
+    selectSubTaskBySceneIdAndTaskId(subTasks, sceneId, topTaskId, dayTaskId) {
+      return subTasks.filter(
+        (item) =>
+          item.scenseid == sceneId &&
+          item.tguid == topTaskId &&
+          item.tsguid == dayTaskId
+      );
+    },
+    beforeStep() {
+      let currId = this.activeStep.id;
+      this.activeStep = this.steps.filter((item) => item.id == currId - 1)[0];
+    },
+    nextStep() {
+      let currId = this.activeStep.id;
+      this.activeStep = this.steps.filter(
+        (item) => item.id == (currId + 1) % this.steps.length
+      )[0];
+    },
+    onselectedGooder(result) {
+      let copy = this.tableConfig.map((item) => item);
+      result.forEach((item) => {
+        copy = copy.filter(
+          (copyItem) => !(copyItem.id == item.id1 || copyItem.id == item.id2)
+        );
+        copy.push(item);
+        this.tableData.forEach(
+          (tableItem) =>
+            (tableItem[item.prop] = `${tableItem[item.prop1]},${
+              tableItem[item.prop2]
+            }`)
+        );
+        copy.sort((a, b) => a.id - b.id);
+      });
+      this.tableConfig = copy;
+      this.gooderDialog = false;
+      this.nextStep();
+    },
+    genHeaders() {
+      this.tableConfig = [
+        {
+          id: 1,
+          label: '鍦烘櫙鍚嶇О',
+          prop: 'scene.name',
+          width: '80'
+        },
+        {
+          id: 2,
+          label: '鍦烘櫙鎵�灞炲尯鍩�',
+          prop: 'scene._region',
+          width: '80'
+        },
+        {
+          id: 3,
+          label: '鍦烘櫙绫诲瀷',
+          prop: 'scene.type',
+          width: '20'
+        },
+        {
+          id: 4,
+          label: '璁″垝娆℃暟',
+          prop: 'monitornum',
+          width: '20'
+        },
+        {
+          id: 5,
+          label: '瀹為檯宸茬洃绠℃鏁�',
+          prop: 'extension1',
+          width: '20'
+        },
+        {
+          id: 6,
+          label: '瀹為檯鎵ц鏃ユ湡',
+          prop: '_executionstarttime',
+          width: '20'
+        }
+      ];
+    }
+  }
+};
+</script>
+<style scoped>
+.options {
+  display: flex;
+  padding: 5px;
+}
+.title-input {
+  margin-top: -20px;
+  border-radius: 6px;
+  color: #000;
+  width: 80%;
+  font-size: 1.5rem;
+  line-height: 5rem;
+  text-align: center;
+  border: none;
+}
+.center-div {
+  display: flex;
+  justify-content: center;
+}
+.btn-group {
+  /* background-color: rgb(32, 127, 211); */
+  white-space: nowrap;
+}
+.chart {
+  margin-top: 5px;
+  margin-bottom: 2px;
+  width: 100%;
+  height: 500px;
+}
+/* 鍥哄畾瀹氫綅鐨� div */
+.fixed-div {
+  position: fixed;
+  top: 7%;
+  right: 20px; /* 璺濈鍙充晶 20px */
+  width: 'auto'; /* div 鐨勫搴� */
+  background-color: #f2f2f200; /* 鑳屾櫙棰滆壊 */
+  padding: 5px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.11); /* 闃村奖鏁堟灉 */
+  z-index: 1000; /* 纭繚 div 鍦ㄩ〉闈㈠叾浠栧唴瀹逛箣涓� */
+}
+.charts {
+  display: flex;
+  flex-wrap: wrap;
+}
+.chart-item {
+  text-align: center;
+  width: 30%;
+}
+.product-item-margin {
+  margin-bottom: 5px;
+}
+.second-title {
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+.main {
+  /* text-align: center; */
+  width: 95%;
+}
+</style>
diff --git a/src/views/fysp/data-product/ProdScenseInfo.vue b/src/views/fysp/data-product/ProdScenseInfo.vue
new file mode 100644
index 0000000..ef36789
--- /dev/null
+++ b/src/views/fysp/data-product/ProdScenseInfo.vue
@@ -0,0 +1,637 @@
+<template>
+  <el-col>
+    <el-row justify="end" class="fixed-div">
+      <!-- 鎸夐挳缁� -->
+      <div class="btn-group">
+        <el-button v-if="this.activeStep.id > 0" @click="beforeStep"
+          >涓婁竴姝�</el-button
+        >
+        <el-button
+          v-if="this.activeStep.id == 0"
+          @click="genDialog = true"
+          type="primary"
+          >浜у搧鐢熸垚閰嶇疆</el-button
+        >
+        <el-button
+          v-if="this.activeStep.id == 1"
+          @click="gooderDialog = true"
+          type="primary"
+          >浜у搧浼樺寲閰嶇疆</el-button
+        >
+        <!-- <el-button type="primary" @click="chartDialog = true"
+          >鍥捐〃灞曠ず</el-button
+        > -->
+        <el-button
+          v-if="this.activeStep.id == 2"
+          type="primary"
+          @click="previewProduct"
+          >鐢熸垚</el-button
+        >
+      </div>
+    </el-row>
+  </el-col>
+  <!-- 姝ラ -->
+  <el-steps
+    :active="activeStep.id"
+    finish-status="success"
+    style=""
+    align-center
+  >
+    <el-step v-for="(s, i) in steps" :key="s.id" :title="s.name" />
+  </el-steps>
+  <div class="center-div">
+    <span class="title-input"> {{ title }} </span><br />
+  </div>
+  <div class="center-div">
+    <div
+      v-show="this.activeStep.id != 0"
+      class="main"
+      v-loading="searchLoading"
+    >
+      <span class="second-title"> {{ `涓�銆佸満鏅鍐礰 }} </span>
+      <CompProdGenScenseDescription
+        id="descriptionComp"
+        class="product-item-margin"
+        :table-data="tableData"
+        :location="formSearch._locations"
+        ref="prodGenDescriptionRef"
+        @generated-description="onGeneratedDescription"
+      >
+      </CompProdGenScenseDescription>
+      <template v-if="showChart">
+        <span class="second-title"> {{ `浜屻�佺粺璁¢�忚` }} </span>
+        <div class="charts product-item-margin">
+          <div class="chart-item" v-for="chart in charts" :key="chart.id">
+            <div class="chart" ref="myChart" :id="chart.id"></div>
+            <!-- <el-button
+              type="danger"
+              @click="charts = charts.filter((item) => item.id != chart.id)"
+              >鍒犻櫎</el-button
+            > -->
+          </div>
+        </div>
+      </template>
+      <span class="second-title"> {{ `涓夈�佽缁嗘竻鍗昤 }} </span>
+      <DynamicTable
+        class="product-item-margin"
+        ref="dynamicTableRef"
+        :table-header="tableConfig"
+        @search="onSearch"
+        :pagination="false"
+      ></DynamicTable>
+    </div>
+    <el-empty
+      v-show="this.activeStep.id == 0"
+      style="font-size: large"
+      description="鏈厤缃骇鍝�"
+    />
+  </div>
+  <el-dialog title="浜у搧鐢熸垚閰嶇疆" v-model="genDialog">
+    <CompSelectProdStage
+      :table-header="tableConfig"
+      @next="selectProdStageOver"
+    >
+    </CompSelectProdStage>
+  </el-dialog>
+  <el-dialog title="浜у搧棰勮" v-model="previewDialog" destroy-on-close>
+    <CompSelectProdStage @next="selectProdStageOver"> </CompSelectProdStage>
+  </el-dialog>
+  <el-dialog title="浜у搧浼樺寲" v-model="gooderDialog" destroy-on-close>
+    <CompGooderProdStage
+      :table-header="tableConfig"
+      @selected-gooder="onselectedGooder"
+    >
+    </CompGooderProdStage>
+  </el-dialog>
+  <el-dialog title="鍥捐〃灞曠ず" v-model="chartDialog" destroy-on-close>
+    <CompShowEChart
+      @painted-chart="onPaintedChart"
+      :table-data="tableData"
+      :table-header="tableConfig"
+      ref="chartRef"
+    >
+    </CompShowEChart>
+  </el-dialog>
+</template>
+<script>
+import DynamicTable from './components/DynamicTable.vue';
+import CompSelectProdStage from './components/CompSelectProdStage.vue';
+import sceneApi from '@/api/fysp/sceneApi';
+import taskApi from '@/api/fysp/taskApi.js';
+import CompProdGenScenseDescription from './components/CompProdGenScenseDescription.vue';
+import htmlTransfer from './js/htmlTransfer';
+import CompShowEChart from './components/CompShowEChart.vue';
+import html2canvas from 'html2canvas';
+import CompGooderProdStage from './components/CompGooderProdStage,.vue';
+import dayjs from 'dayjs';
+import genChart from './js/genChart';
+export default {
+  computed: {
+    title() {
+      return (
+        (this.formSearch._locations.tName ||
+          this.formSearch._locations.dName ||
+          this.formSearch._locations.cName ||
+          this.formSearch._locations.pName ||
+          '') + '鍦板尯鍦烘櫙娓呭崟'
+      );
+    }
+  },
+  components: {
+    DynamicTable,
+    CompSelectProdStage,
+    CompProdGenScenseDescription,
+    CompShowEChart,
+    CompGooderProdStage
+  },
+  data() {
+    return {
+      activeStep: {
+        id: 0,
+        name: '浜у搧閰嶇疆鐢熸垚'
+      },
+      steps: [
+        {
+          id: 0,
+          name: '浜у搧閰嶇疆鐢熸垚'
+        },
+        {
+          id: 1,
+          name: '浜у搧閰嶇疆浼樺寲'
+        },
+        {
+          id: 2,
+          name: '鐢熸垚'
+        }
+      ],
+      gooderDialog: false,
+      searchLoading: false,
+      description: '',
+      showChart: false,
+      chart: {},
+      charts: [],
+      chartDialog: false,
+      genDialog: false,
+      tableConfig: [],
+      tableData: [],
+      formSearch: {
+        _locations: {},
+        searchText: '',
+        _scenetypes: [],
+        _scenetype: {},
+        online: {}
+      }
+    };
+  },
+  mounted() {
+    this.genHeaders();
+  },
+  methods: {
+    beforeStep() {
+      let currId = this.activeStep.id;
+      this.activeStep = this.steps.filter((item) => item.id == currId - 1)[0];
+    },
+    nextStep() {
+      let currId = this.activeStep.id;
+      this.activeStep = this.steps.filter(
+        (item) => item.id == (currId + 1) % this.steps.length
+      )[0];
+    },
+    onselectedGooder(result) {
+      let copy = this.tableConfig.map((item) => item);
+      result.forEach((item) => {
+        copy = copy.filter(
+          (copyItem) => !(copyItem.id == item.id1 || copyItem.id == item.id2)
+        );
+        copy.push(item);
+        this.tableData.forEach(
+          (tableItem) =>
+            (tableItem[item.prop] = `${tableItem[item.prop1]},${
+              tableItem[item.prop2]
+            }`)
+        );
+        copy.sort((a, b) => a.id - b.id);
+      });
+      this.tableConfig = copy;
+      this.gooderDialog = false;
+      this.nextStep();
+    },
+    captureScreenshot() {
+      const element = document.getElementById('descriptionComp');
+
+      return html2canvas(element).then((canvas) =>
+        canvas.toDataURL('image/png')
+      );
+    },
+    onGeneratedDescription(description) {
+      this.description = description;
+    },
+    paintChart(id, chartOption) {
+      const dom = document.getElementById(id);
+      var startX, startY, chartWidth, chartHeight;
+      const myChart = this.$echarts.init(dom); // 鍒濆鍖杄charts瀹炰緥
+      // 寮�濮嬫嫋鎷�
+      myChart.getZr().on('mousedown', function (event) {
+        if (event.target) {
+          startX = event.offsetX;
+          startY = event.offsetY;
+          chartWidth = myChart.getWidth();
+          chartHeight = myChart.getHeight();
+        }
+      });
+
+      // 鎷栧姩璋冩暣瀹藉害
+      myChart.getZr().on('mouseup', function (event) {
+        if (startX != null || startY != null) {
+          var deltaX = event.offsetX - startX;
+          var deltaY = event.offsetY - startY;
+          myChart.resize({
+            width: chartWidth + deltaX,
+            height: chartHeight + deltaY
+          });
+        }
+        startX = null;
+        startY = null;
+      });
+
+      this.chart = myChart;
+      myChart.setOption(chartOption);
+      this.chartDialog = false;
+    },
+    onPaintedChart(chartOption) {
+      this.showChart = true;
+      let id = `chart${this.charts.length}`;
+      this.charts.push({
+        id: id,
+        option: chartOption
+      });
+      console.log('chartOption', chartOption);
+
+      setTimeout(() => {
+        this.paintChart(id, chartOption);
+      }, 100);
+    },
+    genWord() {
+      let imgUrls = this.charts.map((item) => {
+        return htmlTransfer.chartToImageUrl(
+          this.$echarts.getInstanceByDom(document.getElementById(item.id))
+        );
+      });
+
+      this.captureScreenshot().then((item) => {
+        const month = dayjs(new Date()).month() + 1;
+        const day = dayjs(new Date()).date();
+        const data = {
+          title: this.title,
+          month: month,
+          day: day,
+          sceneTypeImg: imgUrls[0],
+          positionImg: imgUrls[1],
+          description: this.description,
+          tableData: this.tableData,
+          imgUrls: imgUrls
+        };
+        const imgSize = {
+          imgUrl: [300, 350]
+        };
+        htmlTransfer.ExportBriefDataDocx(
+          '.\\test.docx', // 妯℃澘璺緞
+          data, // 鏁版嵁
+          this.title, // 鏂囦欢鍚�
+          imgSize
+        );
+      });
+    },
+    previewProduct() {
+      this.genWord();
+    },
+    // 绗竴闃舵纭畾涔嬪悗鐨勫洖璋�
+    selectProdStageOver(result) {
+      this.genDialog = false;
+      this.nextStep();
+      if (result != null) {
+        this.formSearch._locations = result._locations;
+        this.formSearch._scenetypes = result._scenetype;
+
+        this.$refs.dynamicTableRef.onSearch();
+      }
+    },
+    genSomeCharts() {
+      // 娣诲姞鍥捐〃
+      this.charts = [];
+      this.showChart = true;
+      // 鐢熸垚鍖哄煙涓殑涓嬩竴绾х殑鍥捐〃
+      const l = this.formSearch._locations;
+      console.log(this.tableData);
+
+      let positionProp = '';
+      if (l.pName != null) {
+        positionProp = 'cityname';
+        if (l.cName != null) {
+          positionProp = 'districtname';
+          if (l.dName != null) {
+            positionProp = 'townname';
+            if (l.tName != null) {
+              positionProp = 'townname';
+            }
+          }
+        }
+      }
+      [
+        genChart.getChartByDataAndProp(this.tableData, 'type', '绫诲瀷', 'bar'),
+        genChart.getPieChartByDataAndProp(
+          this.tableData,
+          positionProp,
+          '鎵�灞炲尯鍩�'
+        )
+      ].forEach((item) => {
+        let id = `chart${this.charts.length}`;
+        this.charts.push({
+          id: id,
+          option: item
+        });
+        setTimeout(() => {
+          this.paintChart(id, item);
+        }, 100);
+      });
+    },
+    getList(page) {
+      let promises = [];
+      if (this.formSearch._scenetypes == undefined) {
+        let obj = {};
+        this.fillPosition(obj);
+        promises.push(
+          // sceneApi.searchScene(obj, page.currentPage, page.pageSize)
+          sceneApi.searchScene(obj, 1, 9999)
+        );
+        this.addOnlineProp();
+        this.addExistTopTask();
+        return promises;
+      } else {
+        for (
+          let index = 0;
+          index < this.formSearch._scenetypes.length;
+          index++
+        ) {
+          const item = this.formSearch._scenetypes[index];
+          let obj = {
+            // online: true
+          };
+          this.fillPosition(obj);
+          obj.scensetypeid = item.value;
+          if (obj.scensetypeid == '0') obj.scensetypeid = null;
+          promises.push(sceneApi.searchScene(obj, 1, 9999));
+        }
+        this.addOnlineProp();
+        this.addExistTopTask();
+        return promises;
+      }
+    },
+    onSearch(page, func) {
+      this.searchLoading = true;
+      return Promise.all(this.getList(page)).then((results) => {
+        this.tableData = [];
+
+        results.forEach((res) => {
+          if (res.success) {
+            res.data.forEach((item) => {
+              this.tableData.push(item);
+            });
+          }
+        });
+        this.addOnlineProp();
+        this.addExistTopTask();
+        this.genSomeCharts();
+        this.searchLoading = false;
+        func({
+          data: this.tableData
+        });
+      });
+    },
+    fillPosition(area) {
+      const f = this.formSearch;
+      // 琛屾斂鍖哄垝
+      area.provincecode = f._locations.pCode;
+      area.citycode = f._locations.cCode;
+      area.districtcode = f._locations.dCode;
+      area.towncode = f._locations.tCode;
+    },
+    getArea(area) {
+      const f = this.formSearch;
+      // 鍦烘櫙绫诲瀷
+      area.scensetypeid = f._scenetype.value;
+      if (area.scensetypeid == '0') area.scensetypeid = null;
+      // 涓婁笅绾跨姸鎬�
+      area.online = f.online.value;
+      // 鏌ヨ鍏抽敭瀛�(鍦烘櫙鍚嶇О)
+      area.sceneName = f.searchText;
+    },
+    addOnlineProp() {
+      this.tableData.forEach(
+        (item) => (item._isOnline = item.extension1 == 1 ? '鏄�' : '鍚�')
+      );
+    },
+    addExistTopTask() {
+      taskApi.getTopTask().then((res) => {
+        const topTaskId = res[0].tguid;
+        taskApi.fetchMonitorObjectVersion(topTaskId).then((subTaskRes) => {
+          let alreadyExistTopTask = subTaskRes.map((item) => item.sguid);
+          this.noExistTopTaskNum = 0;
+          this.existTopTaskNum = 0;
+          this.tableData.forEach((e) => {
+            if (alreadyExistTopTask.indexOf(e.guid) == -1) {
+              e._isExistInTopTask = '涓嶅湪鐩戠璁″垝鍐�';
+            } else {
+              e._isExistInTopTask = '鍦ㄧ洃绠¤鍒掑唴';
+            }
+          });
+        });
+      });
+    },
+    genHeaders() {
+      this.tableConfig = [
+        {
+          id: 1,
+          label: '缂栧彿',
+          prop: 'index',
+          width: '80',
+          disabled: true
+        },
+        {
+          id: 2,
+          label: '鍚嶇О',
+          prop: 'name',
+          width: '400',
+          disabled: true
+        },
+        {
+          id: 3,
+          label: '绫诲瀷',
+          prop: 'type',
+          width: '130',
+          sortable: true,
+          disabled: true
+        },
+        {
+          id: 4,
+          label: '鐪�',
+          prop: 'provincename',
+          width: '90',
+          disabled: true
+        },
+        {
+          id: 5,
+          label: '甯�',
+          prop: 'cityname',
+          width: '90',
+          disabled: true
+        },
+        {
+          id: 6,
+          label: '鍖哄幙',
+          prop: 'districtname',
+          width: '90',
+          disabled: true
+        },
+        {
+          id: 7,
+          label: '琛楅亾',
+          prop: 'townname',
+          width: '110',
+          disabled: true
+        },
+        {
+          id: 8,
+          label: '鍦板潃',
+          prop: 'location',
+          width: '400',
+          disabled: true
+        },
+        {
+          id: 9,
+          label: '缁忓害',
+          prop: 'longitude',
+          width: '110',
+          merge: 10,
+          title: '缁忕含搴�',
+          disabled: true
+        },
+        {
+          id: 10,
+          label: '绾害',
+          prop: 'latitude',
+          width: '110',
+          merge: 9,
+          disabled: true
+        },
+        {
+          id: 11,
+          label: '鑱旂郴浜�',
+          prop: 'contacts',
+          width: '110',
+          disabled: true
+        },
+        {
+          id: 12,
+          label: '鑱旂郴鐢佃瘽',
+          prop: 'contactst',
+          width: '110',
+          disabled: true
+        },
+        {
+          id: 13,
+          label: '澶囨敞',
+          prop: 'remark',
+          width: '110'
+        },
+        {
+          id: 14,
+          label: '杩愯惀涓�',
+          prop: '_isOnline',
+          width: '110',
+          sortable: true,
+          disabled: true
+        },
+        {
+          id: 15,
+          label: '鏄惁瀛樺湪浜庢�讳换鍔′腑',
+          prop: '_isExistInTopTask',
+          width: '110',
+          sortable: true,
+          disabled: true
+        }
+      ];
+    }
+  }
+};
+</script>
+<style scoped>
+.options {
+  display: flex;
+  padding: 5px;
+}
+.title-input {
+  margin-top: -20px;
+  border-radius: 6px;
+  color: #000;
+  width: 80%;
+  font-size: 1.5rem;
+  line-height: 5rem;
+  text-align: center;
+  border: none;
+}
+.center-div {
+  display: flex;
+  justify-content: center;
+}
+.btn-group {
+  /* background-color: rgb(32, 127, 211); */
+  white-space: nowrap;
+}
+.chart {
+  margin-top: 5px;
+  margin-bottom: 2px;
+  width: 100%;
+  height: 500px;
+}
+.fixed-div-steps {
+  position: fixed;
+  top: 7%;
+  width: 100%; /* div 鐨勫搴� */
+  background-color: #f2f2f200; /* 鑳屾櫙棰滆壊 */
+  padding: 5px;
+  z-index: 1000; /* 纭繚 div 鍦ㄩ〉闈㈠叾浠栧唴瀹逛箣涓� */
+}
+/* 鍥哄畾瀹氫綅鐨� div */
+.fixed-div {
+  position: fixed;
+  top: 7%;
+  right: 20px; /* 璺濈鍙充晶 20px */
+  width: 'auto'; /* div 鐨勫搴� */
+  background-color: #f2f2f200; /* 鑳屾櫙棰滆壊 */
+  padding: 5px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.11); /* 闃村奖鏁堟灉 */
+  z-index: 1000; /* 纭繚 div 鍦ㄩ〉闈㈠叾浠栧唴瀹逛箣涓� */
+}
+.charts {
+  display: flex;
+  flex-wrap: wrap;
+}
+.chart-item {
+  text-align: center;
+  width: 30%;
+}
+.product-item-margin {
+  margin-bottom: 5px;
+}
+.second-title {
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+.main {
+  text-align: center;
+  width: 95%;
+}
+</style>
diff --git a/src/views/fysp/data-product/ProdTreatmentDeviceInfo.vue b/src/views/fysp/data-product/ProdTreatmentDeviceInfo.vue
new file mode 100644
index 0000000..1fefd21
--- /dev/null
+++ b/src/views/fysp/data-product/ProdTreatmentDeviceInfo.vue
@@ -0,0 +1,80 @@
+<template>
+    <el-col>
+    <el-row justify="end" class="fixed-div">
+      <div class="btn-group">
+        <el-button @click="genDialog = true" type="primary"
+          >浜у搧鐢熸垚閰嶇疆</el-button
+        >
+        <el-button type="primary" @click="chartDialog = true"
+          >鍥捐〃灞曠ず</el-button
+        >
+        <el-button type="primary" @click="previewProduct">瀵煎嚭</el-button>
+      </div>
+    </el-row>
+  </el-col>
+</template>
+<script>
+
+</script>
+<style scoped>
+.options {
+  display: flex;
+  padding: 5px;
+}
+.title-input {
+  margin-top: -20px;
+  border-radius: 6px;
+  color: #000;
+  width: 80%;
+  font-size: 1.5rem;
+  line-height: 5rem;
+  text-align: center;
+  border: none;
+}
+.center-div {
+  display: flex;
+  justify-content: center;
+}
+.btn-group {
+  /* background-color: rgb(32, 127, 211); */
+  white-space: nowrap;
+}
+.chart {
+  margin-top: 5px;
+  margin-bottom: 2px;
+  width: 100%;
+  height: 500px;
+}
+/* 鍥哄畾瀹氫綅鐨� div */
+.fixed-div {
+  position: fixed;
+  top: 7%;
+  right: 20px; /* 璺濈鍙充晶 20px */
+  width: 'auto'; /* div 鐨勫搴� */
+  background-color: #f2f2f200; /* 鑳屾櫙棰滆壊 */
+  padding: 5px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.11); /* 闃村奖鏁堟灉 */
+  z-index: 1000; /* 纭繚 div 鍦ㄩ〉闈㈠叾浠栧唴瀹逛箣涓� */
+}
+.charts {
+  display: flex;
+  flex-wrap: wrap;
+}
+.chart-item {
+  text-align: center;
+  width: 30%;
+}
+.product-item-margin {
+  margin-bottom: 5px;
+}
+.second-title {
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+.main {
+  text-align: center;
+  width: 95%;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/ComSelectMonitorTaskProdStage.vue b/src/views/fysp/data-product/components/ComSelectMonitorTaskProdStage.vue
new file mode 100644
index 0000000..de79056
--- /dev/null
+++ b/src/views/fysp/data-product/components/ComSelectMonitorTaskProdStage.vue
@@ -0,0 +1,143 @@
+<template>
+  <span class="second-title"> {{ `鍩烘湰淇℃伅` }} </span>
+  <el-form :inline="true">
+    <FYOptionLocation
+      :allOption="false"
+      :level="4"
+      v-model:value="result._locations"
+    ></FYOptionLocation>
+    <MutiableOptionScene
+      :allOption="false"
+      :type="2"
+      :multiple="true"
+      :initValue="false"
+      v-model:value="result._scenetype"
+    ></MutiableOptionScene>
+    <!-- <el-form-item label="鏈堜唤瀵规瘮">
+        <el-date-picker
+        v-model="result.months"
+        type="monthrange"
+        range-separator="-"
+        start-placeholder="Start month"
+        @change="onSelectedMonthsChange"
+        end-placeholder="End month"
+      />
+    </el-form-item> -->
+    <el-form-item label="鎬讳换鍔�">
+      <!-- <el-input v-model="formSearch.topTaskId" placeholder="鎬讳换鍔�" /> -->
+      <el-select
+        multiple
+        value-key="value"
+        v-model="result.topTasks"
+        placeholder="鎬讳换鍔�"
+        style="width: 320px; margin-left: 30px"
+      >
+        <el-option
+          v-for="s in topTasks"
+          :key="s.value"
+          :label="s.label"
+          :value="s"
+        />
+      </el-select>
+    </el-form-item>
+  </el-form>
+  <el-divider />
+  <span class="second-title"> {{ `閫夋嫨琛ㄦ牸涓渶瑕佸睍绀虹殑鍒椾俊鎭痐 }} </span>
+  <CompChangeHeaderTree :table-header="tableHeader" ref="changeHeaderRef">
+  </CompChangeHeaderTree>
+
+  <div class="center">
+    <div>
+      <el-button type="primary" @click="ok">涓嬩竴姝�</el-button>
+      <el-button type="primary" @click="cancel">鍙栨秷</el-button>
+    </div>
+  </div>
+</template>
+<script>
+import CompChangeHeaderTree from './CompChangeHeaderTree.vue';
+import MutiableOptionScene from './MutiableOptionScene.vue';
+import tableCol from '../js/tableCol';
+import taskApi from '@/api/fysp/taskApi.js';
+import dayjs from 'dayjs';
+export default {
+  mounted() {
+    this.getTopTasks();
+  },
+  props: {
+    tableHeader: {
+      type: Array,
+      default: () => []
+    }
+  },
+  components: {
+    MutiableOptionScene,
+    CompChangeHeaderTree
+  },
+  data() {
+    return {
+      result: {
+        _locations: {},
+        _scenetype: [],
+        topTasks: [],
+        months: []
+      },
+      topTasks: []
+    };
+  },
+  methods: {
+    onSelectedMonthsChange(val) {
+        // let m1 = dayjs(val[0]).format("YYYY-MM")
+        // let m2 = dayjs(val[0]).format("YYYY-MM")
+        // function
+        // this.topTasks = this.topTasks.filter(item=>{
+        //     console.log("dayjs(new Date(item.starttime))", dayjs(item.starttime).format("YYYY-MM"));
+        //     return dayjs(new Date(item.starttime)).format("YYYY-MM") == m1 || dayjs(item.starttime).format("YYYY-MM") == m2
+        // })
+    },
+    getTopTasks() {
+      taskApi.getTopTask().then((res) => {
+        const list = res.map((r) => {
+          return {
+            value: r.tguid,
+            label: r.name,
+            data: r
+          };
+        });
+        this.topTasks = list;
+        // this.$refs.dynamicTableRef.onSearch();
+      });
+    },
+    ok() {
+      let changedHeaders = this.$refs.changeHeaderRef.getChangedHeaders();
+      let copy = this.tableHeader.map((item) => item);
+      let changedHeadeArray = tableCol.treeToArray(changedHeaders);
+      tableCol.treeToArray(copy).forEach((item) => {
+        for (let index = 0; index < changedHeadeArray.length; index++) {
+          const element = changedHeadeArray[index];
+          if (element.id == item.id) {
+            item.hidden = element.hidden;
+            changedHeadeArray.splice(index, 1);
+          }
+        }
+      });
+      this.result.topTasks = this.result.topTasks.map((item) => item.data);
+      this.$emit('next', this.result);
+    },
+    cancel() {
+      this.$emit('next', null);
+    }
+  }
+};
+</script>
+<style scoped>
+.center {
+  display: flex;
+  justify-content: center;
+}
+.second-title {
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompChangeHeaderTree.vue b/src/views/fysp/data-product/components/CompChangeHeaderTree.vue
new file mode 100644
index 0000000..0085b2b
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompChangeHeaderTree.vue
@@ -0,0 +1,90 @@
+<template>
+  <!-- 鍒楀嚭琛ㄥご鐨勬爲鐘剁粨鏋� -->
+  <el-tree
+  class="tree"
+    ref="tree"
+    :props="headTreeProps"
+    :data="copyTableHeader"
+    node-key="id"
+    show-checkbox
+    :default-checked-keys="defaultCheckedKeys"
+    @check="check"
+  />
+</template>
+<script>
+import { useCloned } from '@vueuse/core';
+import tableCol from '../js/tableCol';
+export default {
+  computed: {
+    defaultCheckedKeys() {
+      if (this.copyTableHeader.length == 0) {
+        this.tableHeaderCopy()
+      }
+      let result = this.collectLabels(this.copyTableHeader);
+      console.log("resultsss", result);
+      
+      return result
+    }
+  },
+  mounted() {
+    this.tableHeaderCopy()
+  },
+  data() {
+    return {
+      copyTableHeader: [],
+      headTreeProps: {
+        children: 'children',
+        label: 'label',
+        disabled: 'disabled'
+      }
+    };
+  },
+  props: {
+    // 澶氱骇琛ㄥご鐨勬暟鎹�
+    tableHeader: {
+      type: Array,
+      required: true
+    }
+  },
+  methods: {
+    getChangedHeaders() {
+      return this.copyTableHeader
+    },
+    tableHeaderCopy() {
+      this.copyTableHeader = useCloned(this.tableHeader).cloned.value
+    },
+    collectLabels(nodes) {
+      let labels = [];
+
+      function traverseNodes(nodes) {
+        nodes.forEach((node) => {
+          if (!('hidden' in node) || !node.hidden) {
+            labels.push(node.id);
+          }
+
+          // 濡傛灉褰撳墠鑺傜偣鏈夊瓙鑺傜偣锛岄�掑綊璋冪敤閬嶅巻鍑芥暟
+          if (node.children && node.children.length > 0) {
+            traverseNodes(node.children);
+          }
+        });
+      }
+
+      traverseNodes(nodes); // 寮�濮嬮�掑綊閬嶅巻
+
+      return labels; // 杩斿洖鏀堕泦鍒扮殑 labels 鏁扮粍
+    },
+    check(checkedNode, checkedKeys, halfCheckedNodes, halfCheckedKeys) {
+      checkedNode.hidden = !checkedNode.hidden;
+      this.$refs.tree.updateKeyChildren(checkedNode.id, checkedNode);
+    },
+  }
+};
+</script>
+<style scoped>
+.tree {
+  margin-left: -16.5px;
+  display: flex !important;
+  width: 100% !important;
+  flex-flow: row wrap !important;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompGooderProdStage,.vue b/src/views/fysp/data-product/components/CompGooderProdStage,.vue
new file mode 100644
index 0000000..9aa9c12
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompGooderProdStage,.vue
@@ -0,0 +1,106 @@
+<template>
+  <div class="gooderstage-center-div">
+    <el-transfer
+      :titles="['鍙悎骞跺垪', '宸查�変腑']"
+      v-model="selected"
+      :props="{
+        key: 'id',
+        label: 'label'
+      }"
+      :data="data"
+    />
+    <el-button @click="ok" type="primary" class="ok-style">涓嬩竴姝�</el-button>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    tableHeader: {
+      type: Array,
+      default: () => []
+    }
+  },
+  mounted() {
+    this.initData();
+  },
+  data() {
+    return {
+      data: [],
+      selected: []
+    };
+  },
+  methods: {
+    ok() {
+      this.$emit(
+        'selected-gooder',
+        this.data.filter((item) => this.selected.indexOf(item.id) != -1)
+      );
+    },
+    initData() {
+      let obj = this.processElements(this.tableHeader);
+      this.selected = obj.mergedElements;
+      this.data = obj.canMergePairs;
+    },
+    processElements(elements) {
+      const canMergePairs = [];
+      const mergedElements = [];
+      const seenPairs = new Set(); // 鐢ㄤ簬璺熻釜宸插鐞嗙殑 ID 瀵�
+
+      // 鍒涘缓涓�涓敤浜庢煡鎵惧厓绱犵殑鏄犲皠锛屼互渚挎牴鎹� ID 蹇�熻闂�
+      const elementMap = new Map(elements.map((el) => [el.id, el]));
+
+      for (const element of elements) {
+        // 妫�鏌ユ槸鍚︽湁 merge 灞炴��
+        if (element.merge) {
+          const targetElement = elementMap.get(element.merge);
+          if (targetElement) {
+            // 鎵惧埌鐩爣鍏冪礌锛岀敓鎴愭柊鐨勫悎骞跺
+            const id1 = element.id;
+            const id2 = targetElement.id;
+            const prop1 = element.prop;
+            const prop2 = targetElement.prop;
+            const smallerId = Math.min(id1, id2);
+            const largerId = Math.max(id1, id2);
+            const title = element.prop + targetElement.prop;
+
+            // 鍒涘缓涓�涓敮涓�鐨� ID 瀵癸紝纭繚涓嶄細閲嶅
+            const pairKey = `${smallerId}-${largerId}`;
+            if (!seenPairs.has(pairKey)) {
+              canMergePairs.push({
+                id1,
+                id2,
+                prop1,
+                prop2,
+                id: smallerId,
+                label: `${element.label}/${targetElement.label}`,
+                prop: title,
+                merged: false
+                // children: [element, targetElement]
+              });
+              seenPairs.add(pairKey); // 鏍囪涓哄凡澶勭悊
+            }
+          }
+          // 灏嗗綋鍓嶅厓绱犳爣璁颁负宸插悎骞�
+          if (element.merged) {
+            mergedElements.push(element);
+          }
+        }
+      }
+
+      return { canMergePairs, mergedElements };
+    },
+    getSelected() {
+      return this.selected;
+    }
+  }
+};
+</script>
+<style scoped>
+.gooderstage-center-div {
+  display: block;
+  text-align: center;
+}
+.ok-style {
+  margin-top: 2px;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompPreviewProd.vue b/src/views/fysp/data-product/components/CompPreviewProd.vue
new file mode 100644
index 0000000..788fc3f
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompPreviewProd.vue
@@ -0,0 +1,14 @@
+<template>
+    <div></div>
+</template>
+<script>
+import { saveAs } from 'file-saver';
+export default {
+  methods: {
+    exportToWord() {
+
+    }
+  }
+};
+</script>
+<style scoped></style>
diff --git a/src/views/fysp/data-product/components/CompProdGenMonitorInfoDescription.vue b/src/views/fysp/data-product/components/CompProdGenMonitorInfoDescription.vue
new file mode 100644
index 0000000..3841fd2
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompProdGenMonitorInfoDescription.vue
@@ -0,0 +1,109 @@
+<template>
+  <span class="description"> {{ description }} </span>
+</template>
+<script>
+import dayjs from 'dayjs';
+export default {
+  watch: {
+    tableData: {
+      handler(nv, ov) {
+        this.buildDescription()
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  methods: {
+    // 娈甸
+    startStr() {
+      return `${'  '}`;
+    },
+    // 娈靛熬
+    endStr() {
+      return '銆�';
+    },
+    buildMonitorNum() {
+      let str = '';
+      str += this.startStr();
+      str += `褰撳墠娓呭崟鍖呮嫭${this.tableData.length}涓洃绠′换鍔;
+      str += this.endStr();
+      return str;
+    },
+    buildTopTaskNum() {
+      let topNames = Array.from(new Set(this.tableData.map((item) => item._topTask.name)))
+      let str = '';
+      str += this.startStr();
+      str += `鏈堝害浠诲姟鏈�${
+        topNames.length
+      }涓紝鍒嗗埆涓�${topNames.join(',')}`;
+      str += this.endStr();
+      return str;
+    },
+    buildScene() {
+      let scenes = Array.from(new Set(this.tableData.map(item=>item.sceneType)))
+      let str = '';
+      str += this.startStr();
+      str += `鎬诲叡${scenes.length}涓満鏅紝`;
+      str += `鍖呮嫭${scenes.join('锛�')}鍦烘櫙`;
+      str += this.endStr();
+      return str;
+    },
+    buildDescription() {
+      let result = '';
+      // 鐩戠浠诲姟鏁伴噺
+      result += this.buildMonitorNum();
+      // 娑夊強澶氬皯鎬讳换鍔�
+      result += this.buildTopTaskNum();
+      // 鍦烘櫙涓暟鍜岀被鍨�
+      result += this.buildScene();
+      this.description = result
+    },
+  },
+  computed: {
+    locationStr() {
+      let result = '';
+      if (this.location.pName) {
+        // result += this.location.pName;
+        if (this.location.cName) {
+          result += this.location.cName;
+          if (this.location.dName) {
+            result += this.location.dName;
+            if (this.location.tName) {
+              result += this.location.tName;
+            }
+          }
+        }
+      }
+      if (result == '') {
+        if (this.location.pName) {
+          result += this.location.pName;
+        }
+      }
+      return result;
+    },
+    time() {
+      if (!this.tableData || this.tableData.length == 0) {
+        return '';
+      } else {
+        return dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
+      }
+    }
+  },
+  props: {
+    location: {},
+    tableData: []
+  },
+  data() {
+    return {
+      description: ''
+    };
+  }
+};
+</script>
+<style scoped>
+.description {
+  display: block;
+  /* white-space: pre-line; */
+  width: 100%;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompProdGenScenseDescription.vue b/src/views/fysp/data-product/components/CompProdGenScenseDescription.vue
new file mode 100644
index 0000000..b9d17cf
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompProdGenScenseDescription.vue
@@ -0,0 +1,286 @@
+<template>
+  <!-- <el-descriptions :column="2" size="default" border>
+    <el-descriptions-item label="鍖哄煙">
+      {{ locationStr }}
+    </el-descriptions-item>
+    <el-descriptions-item label="鏃堕棿"> {{ time }} </el-descriptions-item>
+    <el-descriptions-item label="鎻忚堪">
+      <span class="description"> {{ description }} </span>
+    </el-descriptions-item>
+  </el-descriptions> -->
+  <span class="description"> {{ description }} </span>
+</template>
+<script>
+import taskApi from '@/api/fysp/taskApi.js';
+import dayjs from 'dayjs';
+export default {
+  watch: {
+    tableData: {
+      handler(nv, ov) {
+        if (nv == null || nv.length == 0) {
+          this.init()
+          return;
+        }
+        this.countScenseNum();
+        this.countTownScenses();
+        this.countTown();
+        this.countExistTopTask();
+        this.countOnline();
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  props: {
+    location: {},
+    tableData: []
+  },
+  computed: {
+    time() {
+      if (!this.tableData || this.tableData.length == 0) {
+        return '';
+      } else {
+        return dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
+      }
+    },
+    locationStr() {
+      let result = '';
+      if (this.location.pName) {
+        // result += this.location.pName;
+        if (this.location.cName) {
+          result += this.location.cName;
+          if (this.location.dName) {
+            result += this.location.dName;
+            if (this.location.tName) {
+              result += this.location.tName;
+            }
+          }
+        }
+      }
+      if (result == '') {
+        if (this.location.pName) {
+          result += this.location.pName;
+        }
+      }
+      return result;
+    },
+    description() {
+      let result = '';
+      if (!this.tableData || this.tableData.length == 0) {
+        return result;
+      }
+      // result += `${this.startStr()}褰撳墠娓呭崟鍖呮嫭`;
+      result += this.startStr()
+      result += `褰撳墠鍦烘櫙娓呭崟鍖呮嫭`;
+      result += this.scenses.map((item) => item.type).join(',') + '鍦烘櫙绫诲瀷锛�';
+      result += `鎬昏${this.scenseNum}绉嶅満鏅痐;
+      result += this.endStr();
+      result += `${
+        Array.from(new Set(this.tableData.map((item) => item.townname))).length
+      }涓閬擄紝`;
+      // 鍦烘櫙鍒嗗竷
+      result = this.buildTownScenses(result);
+      result = this.buildScenseDistribution(result);
+      result = this.buildOnline(result);
+      result = this.buildExistTopTask(result);
+      this.$emit('generated-description', result);
+      return result;
+    }
+  },
+  data() {
+    return {
+      scenseNum: 0,
+      scenses: [],
+      towns: [
+        // {
+        //   name: '',
+        //   scense: 0
+        // }
+      ],
+      townScenses: [],
+      existTopTaskNum: 0,
+      noExistTopTaskNum: 0,
+      online: 0,
+      noOnline: 0
+    };
+  },
+  mounted() {
+    this.countScenseNum();
+    this.countTown();
+    this.countOnline();
+    this.countExistTopTask();
+  },
+  methods: {
+    // 娈甸
+    startStr() {
+      return `${'  '}`;
+    },
+    // 娈靛熬
+    endStr() {
+      return '銆�';
+    },
+    init() {
+      this.scenseNum = 0,
+      this.scenses = [],
+      this.towns = [
+        // {
+        //   name: '',
+        //   scense: 0
+        // }
+      ],
+      this.townScenses = [],
+      this.existTopTaskNum = 0,
+      this.noExistTopTaskNum = 0,
+      this.online = 0,
+      this.noOnline = 0
+    },
+    countTownScenses() {
+      this.townScenses = [];
+      const groupedData = this.tableData.reduce((acc, current) => {
+        if (!acc[current.townname]) {
+          acc[current.townname] = {
+            type: [current.type]
+          };
+        }
+        if (acc[current.townname].type.indexOf(current.type) == -1) {
+          acc[current.townname].type.push(current.type);
+        }
+        return acc;
+      }, {});
+      for (const prop in groupedData) {
+        this.townScenses.push({
+          name: prop,
+          types: groupedData[prop].type
+        });
+      }
+    },
+    buildTownScenses(result) {
+      // result += this.startStr()
+      for (let index = 0; index < this.townScenses.length; index++) {
+        const item = this.townScenses[index];
+        if (index == 0) {
+          result += `${item.name}${item.types.length}绉嶅満鏅�(${item.types.join(
+            '锛�'
+          )})`;
+        } else {
+          result += `锛�${item.name}${item.types.length}绉嶅満鏅�(${item.types.join(
+            '锛�'
+          )})`;
+        }
+      }
+      result += this.endStr();
+      return result;
+    },
+    getDescription() {
+      let obj = {
+        description: this.description,
+        location: this.locationStr,
+        time: this.time
+      };
+      return obj;
+    },
+    buildOnline(result) {
+      // result += this.startStr()
+      result +=
+        `鍦ㄧ嚎鏁伴噺${this.online}涓紝涓嬬嚎鏁伴噺${this.noOnline}涓猔 + this.endStr();
+      return result;
+    },
+    // 鏄惁鍦ㄧ洃绠′换鍔′腑
+    buildExistTopTask(result) {
+      // result += this.startStr()
+      result +=
+        `鍦ㄧ嚎鐨�${this.online}涓満鏅腑${this.existTopTaskNum}涓満鏅湪鐩戠浠诲姟涓紝${this.noExistTopTaskNum}涓満鏅笉鍦ㄧ洃绠′换鍔′腑` +
+        this.endStr();
+      return result;
+    },
+    // 鍦烘櫙鍒嗗竷
+    buildScenseDistribution(result) {
+      // result += this.startStr()
+
+      result += `${this.tableData.length}涓満鏅紝`;
+      for (let index = 0; index < this.towns.length; index++) {
+        const item = this.towns[index];
+        if (index == 0) {
+          result += `${item.name}${item.town}涓猔;
+        } else {
+          result += `锛�${item.name}${item.town}涓猔;
+        }
+      }
+      result += this.endStr();
+      return result;
+    },
+    // 鏄惁鍦ㄧ嚎
+    countOnline() {
+      this.online = this.tableData.filter(
+        (item) => item.extension1 == 1
+      ).length;
+      this.noOnline = this.tableData.filter(
+        (item) => item.extension1 != 1
+      ).length;
+    },
+    countExistTopTask() {
+      taskApi.getTopTask().then((res) => {
+        const topTaskId = res[0].tguid;
+        taskApi.fetchMonitorObjectVersion(topTaskId).then((subTaskRes) => {
+          let alreadyExistTopTask = subTaskRes.map((item) => item.sguid);
+          this.noExistTopTaskNum = 0;
+          this.existTopTaskNum = 0;
+          this.tableData
+            .filter((item) => item.extension1 == 1)
+            .forEach((e) => {
+              if (alreadyExistTopTask.indexOf(e.guid) == -1) {
+                e._isExistInTopTask = '涓嶅湪鐩戠璁″垝鍐�';
+                this.noExistTopTaskNum++;
+              } else {
+                e._isExistInTopTask = '鍦ㄧ洃绠¤鍒掑唴';
+                this.existTopTaskNum++;
+              }
+            });
+        });
+      });
+    },
+    countScenseNum() {
+      this.scenses = [];
+      let scenseIds = this.scenses.map((item) => item.typeid);
+      this.tableData.forEach((item) => {
+        if (scenseIds.indexOf(item.typeid) == -1) {
+          this.scenses.push(item);
+          scenseIds.push(item.typeid);
+        }
+      });
+      this.scenseNum = scenseIds.length;
+    },
+    countTown() {
+      this.towns = [];
+
+      const groupedData = this.tableData.reduce((acc, current) => {
+        if (!acc[current.type]) {
+          acc[current.type] = {
+            name: current.type,
+            town: 1
+          };
+        }
+        let town = {
+          name: current.type,
+          town: acc[current.type].town + 1
+        };
+        acc[current.type] = town;
+        return acc;
+      }, {});
+      for (const prop in groupedData) {
+        this.towns.push({
+          name: prop,
+          town: groupedData[prop].town
+        });
+      }
+    }
+  }
+};
+</script>
+<style scoped>
+.description {
+  display: block;
+  white-space: pre-wrap;
+  width: 100%;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompProdSteps.vue b/src/views/fysp/data-product/components/CompProdSteps.vue
new file mode 100644
index 0000000..e8e319c
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompProdSteps.vue
@@ -0,0 +1,9 @@
+<template>
+    <div></div>
+</template>
+<script>
+
+</script>
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/fysp/data-product/components/CompProdTreatmentDeviceDescription.vue b/src/views/fysp/data-product/components/CompProdTreatmentDeviceDescription.vue
new file mode 100644
index 0000000..afb33ba
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompProdTreatmentDeviceDescription.vue
@@ -0,0 +1,61 @@
+<template>
+    <el-descriptions :column="2" size="default" border>
+      <el-descriptions-item label="鍖哄煙">
+        {{ locationStr }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鏃堕棿"> {{ time }} </el-descriptions-item>
+      <el-descriptions-item label="鎻忚堪">
+        <span class="description"> {{ description }} </span>
+      </el-descriptions-item>
+    </el-descriptions>
+  </template>
+  <script>
+  import dayjs from 'dayjs';
+  export default {
+    computed: {
+      description() {
+        let result = ''
+        return result
+      },
+      locationStr() {
+        let result = '';
+        if (this.location.pName) {
+          // result += this.location.pName;
+          if (this.location.cName) {
+            result += this.location.cName;
+            if (this.location.dName) {
+              result += this.location.dName;
+              if (this.location.tName) {
+                result += this.location.tName;
+              }
+            }
+          }
+        }
+        if (result == '') {
+          if (this.location.pName) {
+            result += this.location.pName;
+          }
+        }
+        return result;
+      },
+      time() {
+        if (!this.tableData || this.tableData.length == 0) {
+          return '';
+        } else {
+          return dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
+        }
+      }
+    },
+    props: {
+      location: {},
+      tableData: []
+    },
+    data() {
+      return {
+        description: ''
+      };
+    }
+  };
+  </script>
+  <style scoped></style>
+  
\ No newline at end of file
diff --git a/src/views/fysp/data-product/components/CompSelectProdStage.vue b/src/views/fysp/data-product/components/CompSelectProdStage.vue
new file mode 100644
index 0000000..1c2a50c
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompSelectProdStage.vue
@@ -0,0 +1,85 @@
+<template>
+  <span class="second-title"> {{ `鍩烘湰淇℃伅` }} </span>
+  <el-form :inline="true">
+    <FYOptionLocation
+      :allOption="false"
+      :level="4"
+      v-model:value="result._locations"
+    ></FYOptionLocation>
+    <MutiableOptionScene
+      :allOption="false"
+      :type="2"
+      :multiple="true"
+      :initValue="false"
+      v-model:value="result._scenetype"
+    ></MutiableOptionScene>
+  </el-form>
+  <el-divider />
+  <span class="second-title"> {{ `閫夋嫨琛ㄦ牸涓渶瑕佸睍绀虹殑鍒椾俊鎭痐 }} </span>
+  <CompChangeHeaderTree :table-header="tableHeader" ref="changeHeaderRef"> </CompChangeHeaderTree>
+
+  <div class="center">
+    <div>
+      <el-button type="primary" @click="ok">涓嬩竴姝�</el-button>
+      <el-button type="primary" @click="cancel">鍙栨秷</el-button>
+    </div>
+  </div>
+</template>
+<script>
+import CompChangeHeaderTree from './CompChangeHeaderTree.vue';
+import MutiableOptionScene from './MutiableOptionScene.vue';
+import tableCol from '../js/tableCol';
+export default {
+  mounted() {},
+  props: {
+    tableHeader: {
+      type: Array,
+      default: () => []
+    }
+  },
+  components: {
+    MutiableOptionScene,
+    CompChangeHeaderTree
+  },
+  data() {
+    return {
+      result: {
+        _locations: {},
+        _scenetype: []
+      }
+    };
+  },
+  methods: {
+    ok() {
+      let changedHeaders = this.$refs.changeHeaderRef.getChangedHeaders()
+      let copy = this.tableHeader.map(item=>item)
+      let changedHeadeArray = tableCol.treeToArray(changedHeaders)
+      tableCol.treeToArray(copy).forEach(item=>{
+        for (let index = 0; index < changedHeadeArray.length; index++) {
+          const element = changedHeadeArray[index];
+          if (element.id == item.id) {
+            item.hidden = element.hidden
+            changedHeadeArray.splice(index, 1)
+          }
+        }
+      })
+      this.$emit('next', this.result);
+    },
+    cancel() {
+      this.$emit('next', null);
+    }
+  }
+};
+</script>
+<style scoped>
+.center {
+  display: flex;
+  justify-content: center;
+}
+.second-title {
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/CompShowEChart.vue b/src/views/fysp/data-product/components/CompShowEChart.vue
new file mode 100644
index 0000000..78fa25a
--- /dev/null
+++ b/src/views/fysp/data-product/components/CompShowEChart.vue
@@ -0,0 +1,211 @@
+<template>
+  <!-- 閫夐」 -->
+  <div class="options">
+    <el-form :inline="true">
+      <!-- <el-form-item>
+        <el-option
+          v-model="charType"
+          v-for="type of charTypeOptions"
+          :key="type"
+          :value="type"
+          :label="type"
+        ></el-option>
+      </el-form-item> -->
+      <el-form-item>
+        <el-select
+          v-model="charType"
+          style="width: 150px"
+          placeholder="璇烽�夋嫨鍥捐〃绫诲瀷"
+        >
+          <el-option
+            v-for="item in charTypeOptions"
+            :key="item.value"
+            :value="item.value"
+            :label="item.label"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-select
+          v-model="selectHeader"
+          style="width: 200px"
+          @change="onSelectHeaderChange"
+          value-key="id"
+          placeholder="璇烽�夋嫨灞曠ず鐨勫垪"
+        >
+          <el-option
+            v-for="item in allHeader"
+            :key="item.id"
+            :value="item"
+            :label="item.label"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div ref="myChart" id="myChart" class="chart"></div>
+  <el-button type="primary" @click="ok()">纭畾</el-button>
+</template>
+<script>
+export default {
+  props: {
+    tableHeader: {
+      type: Array,
+      required: true
+    },
+    tableData: {
+      type: Array,
+      required: true
+    }
+  },
+  computed: {
+    allHeader() {
+      return this.treeToArray(this.tableHeader);
+    }
+  },
+  data() {
+    return {
+      selectHeader: null,
+      charType: null,
+      charTypeOptions: [
+        {
+          label: '鐩存柟鍥�',
+          value: 'bar'
+        },
+        {
+          label: '鎶樼嚎鍥�',
+          value: 'line'
+        },
+        {
+          label: '楗煎浘',
+          value: 'pie'
+        }
+      ],
+
+      charts: [],
+      chart: {}
+    };
+  },
+  mounted() {
+    // this.setChartOptions('myChart', this.header.prop, this.header.label);
+    // this.selectHeader = this.tableHeader[0]
+  },
+  methods: {
+    getChartObj() {
+      return this.chart;
+    },
+    ok() {
+      let option = this.setChartOptions(
+        'myChart',
+        this.selectHeader.prop,
+        this.selectHeader.label
+      );
+      this.$emit('painted-chart', option);
+    },
+    onSelectHeaderChange() {
+      this.setChartOptions(
+        'myChart',
+        this.selectHeader.prop,
+        this.selectHeader.label
+      );
+    },
+    // 閫掑綊鐨勮幏鍙杘bj涓殑prop灞炴��
+    getPropValueLoop(obj, prop) {
+      if (typeof prop !== 'string') {
+        return obj;
+      }
+      const props = prop.split('.');
+      let result = obj;
+      props.forEach((item) => {
+        result = result[item];
+      });
+      return result;
+    },
+    addChart(ref) {},
+    setChartOptions(refName, prop, label) {
+      let series = this.tableData.map((item) =>
+        this.getPropValueLoop(item, prop)
+      );
+
+      const dom = this.$refs[refName];
+      const myChart = this.$echarts.init(dom); // 鍒濆鍖杄charts瀹炰緥
+      this.chart = myChart;
+      const option = {
+        title: {
+          text: label //璁剧疆鎴戜滑鐨勬爣棰�
+        },
+        tooltip: {
+          trigger: 'item'
+        },
+        legend: {
+          orient: 'vertical',
+          left: 'left'
+        },
+        xAxis: {
+          type: 'category',
+          data: Array.from(new Set(series)),
+          axisLabel: {
+            rotate: 45, // 鏃嬭浆鏍囩锛岄伩鍏嶉噸鍙�
+            // 鎴栬��
+            interval: 0 // 鏄剧ず鎵�鏈夋爣绛撅紝鍙兘瀵艰嚧閲嶅彔锛屾牴鎹渶姹傝皟鏁�
+          }
+        },
+        yAxis: {
+          type: 'value'
+        },
+        series: [
+          {
+            data: Array.from(new Set(series)).map((item) =>
+              this.getCount(series, item)
+            ),
+            type: this.charType,
+            smooth: true
+          }
+        ]
+      };
+      // 璁剧疆瀹炰緥鍙傛暟
+      myChart.setOption(option);
+      return option;
+    },
+
+    getCount(array, element) {
+      let count = 0;
+      array.forEach((e) => {
+        if (e == element) {
+          count++;
+        }
+      });
+      return count;
+    },
+    treeToArray(nodes) {
+      let labels = [];
+
+      function traverseNodes(nodes) {
+        nodes.forEach((node) => {
+          labels.push(node);
+
+          // 濡傛灉褰撳墠鑺傜偣鏈夊瓙鑺傜偣锛岄�掑綊璋冪敤閬嶅巻鍑芥暟
+          if (node.children && node.children.length > 0) {
+            traverseNodes(node.children);
+          }
+        });
+      }
+
+      traverseNodes(nodes); // 寮�濮嬮�掑綊閬嶅巻
+
+      return labels; // 杩斿洖鏀堕泦鍒扮殑 labels 鏁扮粍
+    }
+  }
+};
+</script>
+<style scoped>
+.options {
+  display: flex;
+  margin: 5px;
+  margin-left: 0px;
+}
+.chart {
+  width: 'auto';
+  height: 500px;
+}
+</style>
diff --git a/src/views/fysp/data-product/components/DynamicTable.vue b/src/views/fysp/data-product/components/DynamicTable.vue
new file mode 100644
index 0000000..601fb71
--- /dev/null
+++ b/src/views/fysp/data-product/components/DynamicTable.vue
@@ -0,0 +1,246 @@
+<template>
+  <!-- 淇敼琛ㄥご -->
+  <el-col>
+    <el-row justify="end" class="btn-group">
+      <el-popover placement="bottom" :width="400" trigger="click">
+        <template #reference>
+          <!-- <el-button type="primary">琛ㄥご淇敼</el-button> -->
+        </template>
+        <!-- 鍒楀嚭琛ㄥご鐨勬爲鐘剁粨鏋� -->
+        <el-tree
+          ref="tree"
+          :props="headTreeProps"
+          :data="tableHeader"
+          node-key="id"
+          show-checkbox
+          :default-checked-keys="defaultCheckedKeys"
+          @check="check"
+        />
+      </el-popover>
+      <!-- <el-button type="primary" @click="openChart">鍥捐〃灞曠ず</el-button> -->
+    </el-row>
+  </el-col>
+
+  <el-table
+    :data="tableData"
+    border
+    :header-cell-style="headerStyle"
+    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+    row-key="id"
+    lazy
+    max-height="60vh"
+    v-loading="loading"
+    ref="tableRef"
+  >
+    <template v-for="item in tableHeader.filter((item) => !item.hidden)">
+      <table-column
+        v-if="item.children && item.children.length"
+        :key="item.id"
+        :coloumn-header="item"
+      ></table-column>
+      <el-table-column
+        v-else
+        :key="item.id + 'else'"
+        :label="item.label"
+        :prop="item.prop"
+        align="center"
+        :min-width="item.width ? item.width : '200px'"
+        :sortable="item.sortable == undefined ? false : item.sortable"
+      >
+        <!-- <template #header>
+          {{ item.label }}
+          <el-button type="primary" @click="hidden(item)">闅愯棌</el-button>
+        </template> -->
+      </el-table-column>
+    </template>
+  </el-table>
+  <el-pagination
+    v-if="pagination"
+    ref="paginationRef"
+    class="el-pagination"
+    v-model:current-page="currentPage"
+    v-model:page-size="pageSize"
+    :page-sizes="[10, 20, 50, 100]"
+    :background="true"
+    layout="total, sizes, prev, pager, next, jumper"
+    :total="total"
+  />
+
+  <el-dialog title="鍥捐〃灞曠ず" v-model="chartDialog" destroy-on-close>
+    <CompShowEChart :table-data="tableData" :table-header="tableHeader">
+    </CompShowEChart>
+  </el-dialog>
+</template>
+
+<script>
+import CompShowEChart from './CompShowEChart.vue';
+import TableColumn from './TableColumn.vue';
+import tableCol from '../js/tableCol';
+export default {
+  components: {
+    TableColumn,
+    CompShowEChart
+  },
+  mounted() {
+    // this.onSearch();
+  },
+  watch: {
+    currentPage(nValue, oValue) {
+      if (nValue != oValue) {
+        this.onSearch();
+      }
+    },
+    pageSize(nValue, oValue) {
+      if (nValue != oValue) {
+        this.onSearch();
+      }
+    }
+  },
+  computed: {
+    defaultCheckedKeys() {
+      return this.collectLabels(this.tableHeader);
+    }
+  },
+  data() {
+    return {
+      total: 0,
+      currentPage: 1,
+      pageSize: 20,
+      tableData: [],
+
+      chartDialog: false,
+      char1: {},
+      headTreeProps: {
+        children: 'children',
+        label: 'label',
+        disabled: 'noncloseable'
+      }
+    };
+  },
+  props: {
+    // 琛ㄦ牸鐨勬暟鎹�
+    // tableData: {
+    //   type: Array,
+    //   required: true
+    // },
+    pagination: {
+      type: Boolean,
+      default: false
+    },
+    // 澶氱骇琛ㄥご鐨勬暟鎹�
+    tableHeader: {
+      type: Array,
+      required: true
+    }
+  },
+  methods: {
+    // @Description: 璁剧疆琛ㄥご鏍峰紡
+    headerStyle({ row, rowIndex, columnIndex }) {
+      if (row[columnIndex].order != '' && row[columnIndex].order != null) {
+        return { 'background-color': '#F56C6C', 'color': '#000000' };
+      }
+      return {}
+    },
+    // 鏂规硶涓嶈嚜鍔ㄨЕ鍙戯紝鐢辩埗缁勪欢涓诲姩璋冪敤鑾峰彇鍒楄〃鏁版嵁
+    onSearch() {
+      this.loading = true;
+      this.$emit(
+        'search',
+        {
+          currentPage: this.currentPage,
+          pageSize: this.pageSize
+        },
+        (res) => {
+          this.tableData = res.data;
+          this.total = res.total ? res.total : 0;
+          this.loading = false;
+          // this.doLayout();
+        }
+      );
+    },
+    doLayout() {
+      this.$refs.tableRef.doLayout();
+    },
+    openChart() {
+      this.chartDialog = true;
+    },
+    check(checkedNode, checkedKeys, halfCheckedNodes, halfCheckedKeys) {
+      checkedNode.hidden = !checkedNode.hidden;
+      this.treeToArray(this.tableHeader).map((item) => {
+        if (item.hidden) {
+          console.log('闅愯棌', item.id);
+          this.$refs.tree.setCheckedKeys([item.id], false);
+        } else {
+          console.log('鏄剧ず', item.id);
+          this.$refs.tree.setCheckedKeys([item.id], true);
+        }
+      });
+    },
+    load(tree, treeNode, resolve) {
+      this.$emit('load', tree, treeNode, resolve);
+    },
+    hidden(header) {
+      header.hidden = true;
+    },
+    collectLabels(nodes) {
+      let labels = [];
+
+      function traverseNodes(nodes) {
+        nodes.forEach((node) => {
+          if (!('hidden' in node) || !node.hidden) {
+            labels.push(node.id);
+          }
+
+          // 濡傛灉褰撳墠鑺傜偣鏈夊瓙鑺傜偣锛岄�掑綊璋冪敤閬嶅巻鍑芥暟
+          if (node.children && node.children.length > 0) {
+            traverseNodes(node.children);
+          }
+        });
+      }
+
+      traverseNodes(nodes); // 寮�濮嬮�掑綊閬嶅巻
+
+      return labels; // 杩斿洖鏀堕泦鍒扮殑 labels 鏁扮粍
+    },
+    treeToArray(nodes) {
+      let labels = [];
+
+      function traverseNodes(nodes) {
+        nodes.forEach((node) => {
+          labels.push(node);
+
+          // 濡傛灉褰撳墠鑺傜偣鏈夊瓙鑺傜偣锛岄�掑綊璋冪敤閬嶅巻鍑芥暟
+          if (node.children && node.children.length > 0) {
+            traverseNodes(node.children);
+          }
+        });
+      }
+
+      traverseNodes(nodes); // 寮�濮嬮�掑綊閬嶅巻
+
+      return labels; // 杩斿洖鏀堕泦鍒扮殑 labels 鏁扮粍
+    }
+  }
+};
+</script>
+
+<style scoped>
+.btns {
+  display: flex;
+  margin: 5px;
+  margin-left: 0px;
+}
+.el-pagination {
+  background-color: var(--el-color-white);
+  padding-top: 20px;
+  border-top: 1px solid rgba(0, 0, 0, 0.096);
+  /* margin-top: 2px; */
+}
+.btn-group {
+  /* background-color: rgb(32, 127, 211); */
+  white-space: nowrap;
+}
+.table-cell-red {
+  background-color: rgb(196, 23, 23);
+}
+</style>
diff --git a/src/views/fysp/data-product/components/MutiableOptionScene.vue b/src/views/fysp/data-product/components/MutiableOptionScene.vue
new file mode 100644
index 0000000..6d3dd8d
--- /dev/null
+++ b/src/views/fysp/data-product/components/MutiableOptionScene.vue
@@ -0,0 +1,84 @@
+<template>
+  <el-form-item label="鍦烘櫙绫诲瀷" :prop="prop">
+    <el-select
+      :model-value="value"
+      @change="handleChange"
+      placeholder="鍦烘櫙绫诲瀷"
+      style="width: 350px"
+      :multiple="multiple"
+    >
+      <el-option
+        v-for="s in sceneTypes"
+        :key="s.value"
+        :label="s.label"
+        :value="s"
+      />
+    </el-select>
+  </el-form-item>
+</template>
+
+<script>
+import { enumScene } from '@/enum/scene';
+
+export default {
+  props: {
+    // 鏄惁鍙互澶氶��
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    // 鏄惁鍦ㄩ閫夐」澶勬坊鍔犫�滃叏閮ㄢ�濋�夐」
+    allOption: {
+      type: Boolean,
+      default: true
+    },
+    // 1:椋炵窘鐜绯荤粺锛�2锛氶缇界洃绠$郴缁燂紱
+    type: {
+      type: Number || String,
+      default: 1
+    },
+    // 杩斿洖缁撴灉
+    value: Object,
+    // 鏄惁榛樿杩斿洖鍒濆閫夐」
+    initValue: {
+      type: Boolean,
+      default: true
+    },
+    // form琛ㄥ崟缁戝畾灞炴�у悕
+    prop: {
+      type: String,
+      default: '_scenetype'
+    },
+    // 鍒囨崲 type 鍚庯紝褰撳墠閫夐」鏄惁娓呯┖
+    sourceInit: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['update:value'],
+  data() {
+    return {
+      // sceneTypes: enumScene(this.type, this.allOption),
+    };
+  },
+  computed: {
+    sceneTypes() {
+      if (this.sourceInit) {
+        // 褰撳洜涓簍ype鎴栬�卆llOption鍙傛暟鍙樺寲寮曡捣閫夐」鍙樻洿鏃讹紝娓呯┖褰撳墠閫夐」
+        this.handleChange();
+      }
+      return enumScene(this.type, this.allOption);
+    }
+  },
+  methods: {
+    handleChange(value) {
+      this.$emit('update:value', value);
+    }
+  },
+  mounted() {
+    if (this.initValue) {
+      this.handleChange(this.sceneTypes[0]);
+    }
+  }
+};
+</script>
diff --git a/src/views/fysp/data-product/components/OptionChartProd.vue b/src/views/fysp/data-product/components/OptionChartProd.vue
new file mode 100644
index 0000000..a83946e
--- /dev/null
+++ b/src/views/fysp/data-product/components/OptionChartProd.vue
@@ -0,0 +1,47 @@
+<template>
+  <el-form-item>
+    <el-select
+      :model-value="value"
+      style="width: 150px"
+      placeholder="璇烽�夋嫨鍥捐〃绫诲瀷"
+      :multiple="multiple"
+    >
+      <el-option
+        v-for="item in charTypeOptions"
+        :key="item.value"
+        :value="item.value"
+        :label="item.label"
+      ></el-option>
+    </el-select>
+  </el-form-item>
+</template>
+<script>
+export default {
+  methods: {
+  }, 
+  props: {
+    // 缁撴灉杩斿洖
+    value: Object,
+    // 鏄惁澶氶��
+    multiple: {
+        type: Boolean,
+        default: false
+    }
+  },
+  data() {
+    return {
+      charTypeOptions: [
+        {
+          label: '鐩存柟鍥�',
+          value: 'bar'
+        },
+        {
+          label: '鎶樼嚎鍥�',
+          value: 'line'
+        }
+      ]
+    };
+  }
+};
+</script>
+<style scoped></style>
diff --git a/src/views/fysp/data-product/components/TableColumn.vue b/src/views/fysp/data-product/components/TableColumn.vue
new file mode 100644
index 0000000..c25bfa7
--- /dev/null
+++ b/src/views/fysp/data-product/components/TableColumn.vue
@@ -0,0 +1,71 @@
+<template>
+  <el-table-column
+    :label="coloumnHeader.label"
+    :prop="coloumnHeader.label"
+    show-overflow-tooltip
+    align="center"
+    :min-width="coloumnHeader.width ? coloumnHeader.width : '200px'"
+    :sortable="
+      coloumnHeader.sortable == undefined ? false : coloumnHeader.sortable
+    "
+  >
+    <!-- <template #header>
+        {{ coloumnHeader.label }}
+      <el-button type="primary" @click="hidden1">闅愯棌</el-button>
+    </template> -->
+    <template
+      v-for="item in coloumnHeader.children.filter((item) => !item.hidden)"
+    >
+      <tableColumn
+        v-if="item.children && item.children.length"
+        :key="item.id"
+        :coloumn-header="item"
+      >
+      </tableColumn>
+      <el-table-column
+        v-else
+        :key="item.name"
+        :label="item.label"
+        :prop="item.prop"
+        align="center"
+        :min-width="item.width ? item.width : '200px'"
+        :sortable="item.sortable == undefined ? false : item.sortable"
+      >
+        <!-- <template #header>
+            {{ item.label }}
+          <el-button type="primary" @click="hidden2(item)">闅愯棌</el-button>
+        </template> -->
+      </el-table-column>
+    </template>
+  </el-table-column>
+</template>
+
+<script>
+import tableCol from '../js/tableCol';
+export default {
+  name: 'tableColumn',
+  props: {
+    // 琛ㄥご鏁版嵁淇℃伅
+    coloumnHeader: {
+      type: Object,
+      required: true
+    }
+  },
+  methods: {
+    hidden1() {
+      let header = this.coloumnHeader;
+      header.hidden = true;
+    },
+    hidden2(header) {
+      header.hidden = true;
+    },
+    sortChangeFunc(column, prop, order) {
+      console.log('tableCol', tableCol);
+
+      tableCol.sortChange(column, prop, order);
+    }
+  }
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/fysp/data-product/js/genChart.js b/src/views/fysp/data-product/js/genChart.js
new file mode 100644
index 0000000..b89f086
--- /dev/null
+++ b/src/views/fysp/data-product/js/genChart.js
@@ -0,0 +1,123 @@
+// 閫掑綊鐨勮幏鍙杘bj涓殑prop灞炴�� 瑙e喅鏈夋椂闇�瑕佸彇val.obj.prop鐨勬儏鍐�
+function getPropValueLoop(obj, prop) {
+  if (typeof prop !== 'string') {
+    return obj;
+  }
+  const props = prop.split('.');
+  let result = obj;
+  props.forEach((item) => {
+    result = result[item];
+  });
+  return result;
+}
+function getCount(array, element) {
+  let count = 0;
+  array.forEach((e) => {
+    if (e == element) {
+      count++;
+    }
+  });
+  return count;
+}
+export default {
+  getPieChartByDataAndProp(data, prop, label) {
+    let chartData = []
+    function addNewName(name) {
+      chartData.push({
+        name: name,
+        value: 1
+      })
+    }
+    function addCount(name) {
+      chartData.map(item=>{
+        if (item.name === name) {
+          item.value++
+        }
+      })
+    }
+    function hasThisName(name) {
+      for (let index = 0; index < chartData.length; index++) {
+        const element = chartData[index];
+        if (element.name === name) {
+          return true
+        }
+      }
+      return false
+    }
+
+    data
+      .map((item) => {
+        const name = getPropValueLoop(item, prop) 
+        if (hasThisName(name)) {
+          addCount(name)
+        }else {
+          addNewName(name)
+        }
+      })
+
+    return {
+      title: {
+        text: label,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'item'
+      },
+      legend: {
+        orient: 'vertical',
+        left: 'left'
+      },
+      series: [
+        {
+          type: 'pie',
+          radius: '50%',
+          data: chartData,
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowOffsetX: 0,
+              shadowColor: 'rgba(0, 0, 0, 0.5)'
+            }
+          }
+        }
+      ]
+    };
+  },
+  getChartByDataAndProp(data, prop, label, chartType = 'bar') {
+    let series = data.map((item) => getPropValueLoop(item, prop));
+    const option = {
+      title: {
+        text: label //璁剧疆鏍囬
+      },
+      tooltip: {
+        trigger: 'item'
+      },
+      legend: {
+        orient: 'vertical',
+        left: 'left'
+      },
+      xAxis: {
+        type: 'category',
+        data: Array.from(new Set(series)),
+        axisLabel: {
+          rotate: 45, // 鏃嬭浆鏍囩锛岄伩鍏嶉噸鍙�
+          // 鎴栬��
+          interval: 0 // 鏄剧ず鎵�鏈夋爣绛撅紝鍙兘瀵艰嚧閲嶅彔锛屾牴鎹渶姹傝皟鏁�
+        }
+      },
+      yAxis: {
+        type: 'value'
+      },
+      series: [
+        {
+          data: Array.from(new Set(series)).map((item) =>
+            getCount(series, item)
+          ),
+          type: chartType,
+          smooth: true
+        }
+      ]
+    };
+    return option;
+  }
+};
diff --git a/src/views/fysp/data-product/js/htmlTransfer.js b/src/views/fysp/data-product/js/htmlTransfer.js
new file mode 100644
index 0000000..c6b21fe
--- /dev/null
+++ b/src/views/fysp/data-product/js/htmlTransfer.js
@@ -0,0 +1,120 @@
+import Docxtemplater from 'docxtemplater'
+import PizZip from 'pizzip'
+import JSZipUtils from 'jszip-utils'
+import { saveAs } from 'file-saver'
+import ImageModule from 'docxtemplater-image-module-free'
+import expressions from 'angular-expressions'
+import PizZipUtils from 'pizzip/utils/index.js'
+/**
+ * 灏哹ase64鏍煎紡鐨勬暟鎹浆涓篈rrayBuffer
+ * @param {Object} dataURL base64鏍煎紡鐨勬暟鎹�
+ */
+function base64DataURLToArrayBuffer(dataURL) {
+    const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
+    if (!base64Regex.test(dataURL)) {
+        return false;
+    }
+    const stringBase64 = dataURL.replace(base64Regex, "");
+    let binaryString;
+    if (typeof window !== "undefined") {
+        binaryString = window.atob(stringBase64);
+    } else {
+        binaryString = Buffer.from(stringBase64, "base64").toString("binary");
+    }
+    const len = binaryString.length;
+    const bytes = new Uint8Array(len);
+    for (let i = 0; i < len; i++) {
+        const ascii = binaryString.charCodeAt(i);
+        bytes[i] = ascii;
+    }
+    return bytes.buffer;
+}
+export default {
+    chartToImageUrl(chart) {
+        const dataURL = chart.getDataURL({
+            pixelRatio: 5, // 鎻愰珮鍥剧墖璐ㄩ噺
+            backgroundColor: '#FFFFFF', // 璁剧疆鑳屾櫙棰滆壊
+            excludeComponents: ['toolbox'], // 鎺掗櫎宸ュ叿绠辩粍浠�
+            type: 'png' // 杈撳嚭鍥剧墖绫诲瀷涓篜NG
+        });
+        return dataURL;
+    },
+    /**
+ * 瀵煎嚭word鏂囨。锛堝甫鍥剧墖锛�
+ *
+ */
+
+ 
+ExportBriefDataDocx(tempDocxPath, data, fileName, imgSize) {
+  expressions.filters.lower = function(input) {
+    if (!input) return input
+    return input.toLowerCase()
+  }
+ 
+  JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
+    if (error) {
+      console.log(error)
+    }
+    expressions.filters.size = function(input, width, height) {
+      return {
+        data: input,
+        size: [width, height]
+      }
+    }
+    let opts = {}
+    opts = {
+      // 鍥惧儚鏄惁灞呬腑
+      centered: false
+    }
+    opts.getImage = (chartId) => {
+        //灏哹ase64鐨勬暟鎹浆涓篈rrayBuffer
+        return base64DataURLToArrayBuffer(chartId);
+    }
+    opts.getSize = function(img, tagValue, tagName) {
+      console.log("img, tagValue, tagName", img, tagValue, tagName);
+      
+      if (imgSize[tagName]) {
+        console.log("imgSize[tagName]", imgSize[tagName]);
+        return imgSize[tagName]
+      } else {
+        return [300, 300]
+      }
+    }
+    // 鍒涘缓涓�涓狫SZip瀹炰緥锛屽唴瀹逛负妯℃澘鐨勫唴瀹�
+    
+    const zip = new PizZip(content)
+    // 鍒涘缓骞跺姞杞� Docxtemplater 瀹炰緥瀵硅薄
+    // 璁剧疆妯℃澘鍙橀噺鐨勫��
+    const doc = new Docxtemplater()
+    doc.attachModule(new ImageModule(opts))
+    doc.loadZip(zip)
+    doc.setOptions({
+      nullGetter: function() { // 璁剧疆绌哄�� undefined 涓�''
+        return ''
+      }
+    })
+    doc.setData(data)
+    try {
+      // 鍛堢幇鏂囨。锛屼細灏嗗唴閮ㄦ墍鏈夊彉閲忔浛鎹㈡垚鍊硷紝
+      doc.render()
+    } catch (error) {
+      const e = {
+        message: error.message,
+        name: error.name,
+        stack: error.stack,
+        properties: error.properties
+      }
+      console.log('err', { error: e })
+      // 褰撲娇鐢╦son璁板綍鏃讹紝姝ゅ鎶涘嚭閿欒淇℃伅
+      throw error
+    }
+    // 鐢熸垚涓�涓唬琛―ocxtemplater瀵硅薄鐨剒ip鏂囦欢锛堜笉鏄竴涓湡瀹炵殑鏂囦欢锛岃�屾槸鍦ㄥ唴瀛樹腑鐨勮〃绀猴級
+    const out = doc.getZip().generate({
+      type: 'blob',
+      mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
+    })
+    // 灏嗙洰鏍囨枃浠跺璞′繚瀛樹负鐩爣绫诲瀷鐨勬枃浠讹紝骞跺懡鍚�
+    saveAs(out, fileName)
+  })
+}
+}
\ No newline at end of file
diff --git a/src/views/fysp/data-product/js/tableCol.js b/src/views/fysp/data-product/js/tableCol.js
new file mode 100644
index 0000000..244aa41
--- /dev/null
+++ b/src/views/fysp/data-product/js/tableCol.js
@@ -0,0 +1,21 @@
+
+export default {
+    treeToArray(nodes) {
+        let labels = [];
+  
+        function traverseNodes(nodes) {
+          nodes.forEach((node) => {
+            labels.push(node);
+  
+            // 濡傛灉褰撳墠鑺傜偣鏈夊瓙鑺傜偣锛岄�掑綊璋冪敤閬嶅巻鍑芥暟
+            if (node.children && node.children.length > 0) {
+              traverseNodes(node.children);
+            }
+          });
+        }
+  
+        traverseNodes(nodes); // 寮�濮嬮�掑綊閬嶅巻
+  
+        return labels; // 杩斿洖鏀堕泦鍒扮殑 labels 鏁扮粍
+      }
+}
\ No newline at end of file

--
Gitblit v1.9.3