1. 完成场景报告模块
2. 日报管理模块添加时间范围选择以及word报告生成
已修改19个文件
已添加7个文件
1125 ■■■■ 文件已修改
package-lock.json 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/单体模版.docx 补丁 | 查看 | 原始文档 | blame | 历史
public/单体模版(静安).docx 补丁 | 查看 | 原始文档 | blame | 历史
public/秋冬季空气质量攻坚工作提示模板.docx 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/analysisApi.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/image/right_triangle.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/FYImageSelectDialog.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionLocation.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionTime.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/composables/formConfirm.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/menu.js 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enum/device/monitorDevice.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/doc.js 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/DocTest.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/ArbitraryPhoto.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDevicePhono.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDevicePhoto.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/ProdDailyReport.vue 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/ProdSceneReport.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/components/CompImgInfo.vue 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/components/CompProblemTable.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/scene/CompSceneConstructionInfo.vue 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -15,12 +15,19 @@
        "@vueuse/core": "^9.7.0",
        "axios": "^1.2.1",
        "dayjs": "^1.11.13",
        "docxtemplater": "^3.50.0",
        "docxtemplater-image-module": "^3.1.0",
        "docxtemplater-image-module-free": "^1.1.1",
        "element-plus": "^2.8.3",
        "exceljs": "^4.4.0",
        "file-saver": "^2.0.5",
        "js-base64": "^3.7.5",
        "jszip": "^3.10.1",
        "jszip-utils": "^0.1.0",
        "md5": "^2.3.0",
        "open-docxtemplater-image-module": "^1.0.3",
        "pinia": "^2.0.26",
        "pizzip": "^3.1.7",
        "vue": "^3.2.45",
        "vue-demi": "^0.14.6",
        "vue-i18n": "^9.8.0",
@@ -2737,6 +2744,14 @@
        "@xtuc/long": "4.2.2"
      }
    },
    "node_modules/@xmldom/xmldom": {
      "version": "0.8.10",
      "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
      "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/@xtuc/ieee754": {
      "version": "1.2.0",
      "dev": true,
@@ -3932,6 +3947,34 @@
      },
      "engines": {
        "node": ">=6.0.0"
      }
    },
    "node_modules/docxtemplater": {
      "version": "3.50.0",
      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.50.0.tgz",
      "integrity": "sha512-6EqYbBFUcdNKVwS6G8vQ+pFOURJ7zoSvUNASIi4MPnCpkRdYDvmaOV2e1XcScMrEQV5pFZUAAbKi30Z+JTbLFA==",
      "dependencies": {
        "@xmldom/xmldom": "^0.8.10"
      },
      "engines": {
        "node": ">=0.10"
      }
    },
    "node_modules/docxtemplater-image-module": {
      "version": "3.1.0",
      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module/-/docxtemplater-image-module-3.1.0.tgz",
      "integrity": "sha512-dFzuGOhCXgZpzAO7hGJvGbjzUCvid+3L4lSjbBta+TnXuBb/55pqDQWeRZ38yMviu+QTaYreSHHdYHF1XvQlBA==",
      "deprecated": "This module has been deprecated, No new releases will be made to it.\n\nThere is an up to date paid version of the module which you can find on https://docxtemplater.com/modules/image/",
      "dependencies": {
        "xmldom": "^0.1.27"
      }
    },
    "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==",
      "dependencies": {
        "xmldom": "^0.1.27"
      }
    },
    "node_modules/domexception": {
@@ -5573,6 +5616,11 @@
        "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=="
    },
    "node_modules/jszip/node_modules/readable-stream": {
      "version": "2.3.8",
      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -6227,6 +6275,14 @@
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/open-docxtemplater-image-module": {
      "version": "1.0.3",
      "resolved": "https://registry.npmmirror.com/open-docxtemplater-image-module/-/open-docxtemplater-image-module-1.0.3.tgz",
      "integrity": "sha512-/VMRc8MU34T2/ry2ER0+/JXjc4BTuWDtdXlqnWo5ikR/DvsLVAjF6F8094dA7+i68cfDrGgP42VEXqbwyLaTdg==",
      "dependencies": {
        "xmldom": "^0.1.27"
      }
    },
    "node_modules/optionator": {
      "version": "0.9.1",
      "dev": true,
@@ -6437,6 +6493,19 @@
          "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==",
      "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=="
    },
    "node_modules/postcss": {
      "version": "8.4.20",
@@ -8296,6 +8365,15 @@
      "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",
      "engines": {
        "node": ">=0.1"
      }
    },
    "node_modules/yallist": {
      "version": "4.0.0",
      "dev": true,
@@ -10123,6 +10201,11 @@
        "@xtuc/long": "4.2.2"
      }
    },
    "@xmldom/xmldom": {
      "version": "0.8.10",
      "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
      "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="
    },
    "@xtuc/ieee754": {
      "version": "1.2.0",
      "dev": true,
@@ -10883,6 +10966,30 @@
      "dev": true,
      "requires": {
        "esutils": "^2.0.2"
      }
    },
    "docxtemplater": {
      "version": "3.50.0",
      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.50.0.tgz",
      "integrity": "sha512-6EqYbBFUcdNKVwS6G8vQ+pFOURJ7zoSvUNASIi4MPnCpkRdYDvmaOV2e1XcScMrEQV5pFZUAAbKi30Z+JTbLFA==",
      "requires": {
        "@xmldom/xmldom": "^0.8.10"
      }
    },
    "docxtemplater-image-module": {
      "version": "3.1.0",
      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module/-/docxtemplater-image-module-3.1.0.tgz",
      "integrity": "sha512-dFzuGOhCXgZpzAO7hGJvGbjzUCvid+3L4lSjbBta+TnXuBb/55pqDQWeRZ38yMviu+QTaYreSHHdYHF1XvQlBA==",
      "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": {
@@ -12006,6 +12113,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",
      "dev": true
@@ -12437,6 +12549,14 @@
        "mimic-fn": "^2.1.0"
      }
    },
    "open-docxtemplater-image-module": {
      "version": "1.0.3",
      "resolved": "https://registry.npmmirror.com/open-docxtemplater-image-module/-/open-docxtemplater-image-module-1.0.3.tgz",
      "integrity": "sha512-/VMRc8MU34T2/ry2ER0+/JXjc4BTuWDtdXlqnWo5ikR/DvsLVAjF6F8094dA7+i68cfDrGgP42VEXqbwyLaTdg==",
      "requires": {
        "xmldom": "^0.1.27"
      }
    },
    "optionator": {
      "version": "0.9.1",
      "dev": true,
@@ -12553,6 +12673,21 @@
      "requires": {
        "@vue/devtools-api": "^6.4.5",
        "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": {
@@ -13665,6 +13800,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
package.json
@@ -20,12 +20,19 @@
    "@vueuse/core": "^9.7.0",
    "axios": "^1.2.1",
    "dayjs": "^1.11.13",
    "docxtemplater": "^3.50.0",
    "docxtemplater-image-module": "^3.1.0",
    "docxtemplater-image-module-free": "^1.1.1",
    "element-plus": "^2.8.3",
    "exceljs": "^4.4.0",
    "file-saver": "^2.0.5",
    "js-base64": "^3.7.5",
    "jszip": "^3.10.1",
    "jszip-utils": "^0.1.0",
    "md5": "^2.3.0",
    "open-docxtemplater-image-module": "^1.0.3",
    "pinia": "^2.0.26",
    "pizzip": "^3.1.7",
    "vue": "^3.2.45",
    "vue-demi": "^0.14.6",
    "vue-i18n": "^9.8.0",
public/µ¥ÌåÄ£°æ.docx
Binary files differ
public/µ¥ÌåÄ£°æ£¨¾²°²£©.docx
Binary files differ
public/Çﶬ¼¾¿ÕÆøÖÊÁ¿¹¥¼á¹¤×÷Ìáʾģ°å.docx
Binary files differ
src/api/fysp/analysisApi.js
@@ -1,7 +1,7 @@
import { $fysp } from '../index';
export default {
    checkProblem(data) {
    dailyreport(data) {
        return $fysp.post(`search/dailyreport`, data).then((res) => res.data);
    },
}
src/api/index.js
@@ -1,7 +1,7 @@
import axios from 'axios';
import { ElMessage } from 'element-plus';
const debug = true;
const debug = false;
let ip1 = 'http://47.100.191.150:9005/';
let ip1_file = 'http://47.100.191.150:9005/';
src/assets/image/right_triangle.png
src/components.d.ts
@@ -12,14 +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']
    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
    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']
@@ -31,6 +35,9 @@
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDivider: typeof import('element-plus/es')['ElDivider']
    ElDrawer: typeof import('element-plus/es')['ElDrawer']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
    ElEmpty: typeof import('element-plus/es')['ElEmpty']
    ElForm: typeof import('element-plus/es')['ElForm']
    ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -38,21 +45,29 @@
    ElIcon: typeof import('element-plus/es')['ElIcon']
    ElImage: typeof import('element-plus/es')['ElImage']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
    ElLink: typeof import('element-plus/es')['ElLink']
    ElMain: typeof import('element-plus/es')['ElMain']
    ElMenu: typeof import('element-plus/es')['ElMenu']
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
    ElPopover: typeof import('element-plus/es')['ElPopover']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSegmented: typeof import('element-plus/es')['ElSegmented']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElSpace: typeof import('element-plus/es')['ElSpace']
    ElStep: typeof import('element-plus/es')['ElStep']
    ElSteps: typeof import('element-plus/es')['ElSteps']
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
@@ -60,6 +75,7 @@
    ElTag: typeof import('element-plus/es')['ElTag']
    ElText: typeof import('element-plus/es')['ElText']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTransfer: typeof import('element-plus/es')['ElTransfer']
    ElTree: typeof import('element-plus/es')['ElTree']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    Footer: typeof import('./components/core/Footer.vue')['default']
src/components/FYImageSelectDialog.vue
@@ -4,7 +4,6 @@
    @opened="$emit('update:dialogVisible', true)"
    @closed="$emit('update:dialogVisible', false)"
    width="66%"
    title="任意图片"
    destroy-on-close
  >
    <div class="main">
@@ -30,7 +29,7 @@
            v-for="item in typeList"
            :key="item.typeId"
            :label="
              item.typeName + ' (' + typeImgMap.get(activeId).length + ')'
              item.typeName + ' (' + typeImgMap.get(item.typeId).length + ')'
            "
            :name="item.typeId"
          >
src/components/search-option/FYOptionLocation.vue
@@ -7,6 +7,7 @@
      :placeholder="placeholder"
      :props="optionProps"
      style="width: 320px"
      v-bind="$attrs"
    />
  </el-form-item>
</template>
src/components/search-option/FYOptionTime.vue
@@ -8,6 +8,7 @@
      start-placeholder="选择开始时间"
      end-placeholder="选择结束时间"
      style="width: 150px"
      v-bind="$attrs"
    />
  </el-form-item>
</template>
src/composables/formConfirm.js
@@ -13,7 +13,7 @@
  },
  reset = {
    do: () => {}
  }
  },
}) {
  if (!submit.title) submit.title = '提交';
  if (!submit.msg) submit.msg = '确认是否提交?';
src/constants/menu.js
@@ -4,7 +4,15 @@
    icon: 'Connection',
    name: '账户匹配'
  }
]
];
if (import.meta.env.DEV) {
  MENU_COMMON.push({
    path: '/common/docTest',
    icon: 'Connection',
    name: '文档生成'
  });
}
const MENU_FYSP = [
  // {
@@ -26,14 +34,14 @@
      {
        path: '/fysp/task/manage',
        icon: 'CircleCheck',
        name: '监管任务',
        name: '监管任务'
      },
      {
        path: '/fysp/sceneInfo',
        icon: 'Files',
        name: '场景信息',
      },
    ],
        name: '场景信息'
      }
    ]
  },
  {
    icon: 'DataAnalysis',
@@ -42,24 +50,24 @@
      {
        path: '/fysp/data-product/profollow',
        icon: 'Document',
        name: '问题动态跟踪',
        name: '问题动态跟踪'
      },
      {
        path: '/fysp/data-product/proanalysis',
        icon: 'Document',
        name: '问题整改分析',
        name: '问题整改分析'
      },
      {
        path: '/fysp/data-product/standardjudge',
        icon: 'Document',
        name: '规范性评估',
        name: '规范性评估'
      },
      {
        path: '/fysp/data-product/dailyreport',
        icon: 'Document',
        name: '日报管理',
      },
    ],
        name: '日报管理'
      }
    ]
  },
  {
    icon: 'DataAnalysis',
@@ -68,9 +76,9 @@
      {
        path: '/fysp/data-product/scenereport',
        icon: 'Document',
        name: '场景报告',
      },
    ],
        name: '场景报告'
      }
    ]
  },
  // {
  //   path: '/changecheck',
@@ -84,14 +92,14 @@
      {
        path: '/fysp/evaluation/evalutationTask',
        icon: 'MessageBox',
        name: '评估任务',
        name: '评估任务'
      },
      {
        path: '/fysp/evaluation/evalutationRecord',
        icon: 'Tickets',
        name: '评估记录',
      },
    ],
        name: '评估记录'
      }
    ]
  },
  {
    icon: 'Setting',
@@ -100,20 +108,20 @@
      {
        path: '/fysp/config/problemType',
        icon: 'List',
        name: '监管问题',
        name: '监管问题'
      },
      {
        path: '/fysp/config/deviceMatch',
        icon: 'List',
        name: '设备匹配',
        name: '设备匹配'
      },
      {
        //评估规则管理
        path: '/fysp/config/evalutationRule',
        icon: 'List',
        name: '评估规则',
      },
    ],
        name: '评估规则'
      }
    ]
  },
  {
    path: '/fysp/support',
@@ -166,7 +174,7 @@
  //   ]
  // },
  ...MENU_COMMON
]
];
const MENU_FYTZ = [
  {
@@ -196,7 +204,7 @@
    ]
  },
  ...MENU_COMMON
]
];
const MENU_FYPW = [
  {
@@ -204,6 +212,6 @@
    icon: 'Search',
    name: '排污抽运'
  }
]
];
export { MENU_FYSP, MENU_FYTZ, MENU_FYPW, MENU_COMMON }
export { MENU_FYSP, MENU_FYTZ, MENU_FYPW, MENU_COMMON };
src/enum/device/monitorDevice.js
@@ -1,10 +1,10 @@
const dustDeviceType = [
  {
    label: '扬尘监测',
    label: '扬尘',
    value: '1',
    children: [
      {
        label: '扬尘监测',
        label: '监测设备',
        value: '1',
      },
    ],
@@ -13,11 +13,11 @@
const fumeDeviceType = [
  {
    label: '油烟监测',
    label: '油烟',
    value: '1',
    children: [
      {
        label: '油烟监测',
        label: '监测设备',
        value: '1',
      },
    ],
@@ -26,11 +26,11 @@
const vocDeviceType = [
  {
    label: 'VOC监测',
    label: 'VOC',
    value: '1',
    children: [
      {
        label: 'VOC监测',
        label: '监测设备',
        value: '1',
      },
    ],
src/router/index.js
@@ -202,6 +202,12 @@
    name: 'userMatch',
    path: '/common/userMatch',
    component: () => import('@/views/common/UserMatch.vue')
  },
  {
    //文档生成测试
    name: 'docTest',
    path: '/common/docTest',
    component: () => import('@/views/DocTest.vue')
  }
]
src/utils/doc.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
import JSZipUtils from 'jszip-utils';
import JSZip from 'jszip';
import docxtemplater from 'docxtemplater';
import ImageModule from 'docxtemplater-image-module-free';
import Pizzip from 'pizzip';
import PizZipUtils from 'pizzip/utils/dist/pizzip-utils';
import FileSaver from 'file-saver';
import fs from 'fs';
/**
 * èŽ·å–å›¾ç‰‡é…ç½®ä¿¡æ¯
 * @param {Number} horizontalHeight å›¾ç‰‡å®½åº¦å¤§äºŽé«˜åº¦æ—¶ï¼Œé™åˆ¶å…¶é«˜åº¦
 * @param {Number} verticalWidth å›¾ç‰‡å®½åº¦å°äºŽé«˜åº¦æ—¶ï¼Œé™åˆ¶å…¶å®½åº¦
 * @returns
 */
function getImageOptions(options) {
  const horizontalHeight = options ? options.horizontalHeight : undefined
  const verticalWidth = options ? options.verticalWidth : undefined
  return {
    centered: false,
    fileType: 'docx',
    getImage(tagValue) {
      // In this case tagValue will be a URL tagValue = "https://docxtemplater.com/puffin.png"
      return new Promise(function (resolve, reject) {
        JSZipUtils.getBinaryContent(tagValue, function (error, content) {
          if (error) {
            return reject(error);
          }
          return resolve(content);
        });
      });
    },
    getSize(img, tagValue, tagName) {
      return new Promise(function (resolve, reject) {
        const image = new Image();
        image.src = tagValue;
        image.onload = function () {
          let width = image.width;
          let height = image.height;
          // console.log('width height', width, height);
          if (width > height && horizontalHeight && height > horizontalHeight) {
            const scale = image.height / horizontalHeight;
            height = horizontalHeight;
            width = image.width / scale;
          } else if (width <= height && verticalWidth && width > verticalWidth) {
            const scale = image.width / verticalWidth;
            width = verticalWidth;
            height = image.height / scale;
          }
          // console.log('scale', width, height);
          resolve([width, height]);
        };
        image.onerror = function (e) {
          console.log('img, tagValue, tagName : ', img, tagValue, tagName);
          alert('An error occured while loading ' + tagValue);
          reject(e);
        };
      });
    }
  };
}
export const exportDocx = (tempDocpath, data, zipName, imageSize) => {
  return new Promise((resolve, reject) => {
    JSZipUtils.getBinaryContent(tempDocpath, (error, content) => {
      if (error) {
        reject(error);
        throw error;
      }
      const zip = new Pizzip(content);
      const imageOptions = getImageOptions(imageSize);
      let doc = new docxtemplater()
        .setOptions({ paragraphLoop: true })
        .loadZip(zip)
        .attachModule(new ImageModule(imageOptions))
        .compile();
      doc.resolveData(data).then(() => {
        try {
          doc.render();
        } catch (error) {
          console.log(error);
          throw error;
        }
        const out = doc.getZip().generate({
          type: 'blob',
          mimeType:
            'application/vnd.openxmlformats-officedocumnet.wordprocessingml.document'
        });
        FileSaver.saveAs(out, zipName);
        resolve();
      });
    });
  });
};
src/views/DocTest.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
<template>
  <div>
    <el-button type="primary" @click="export_doc">导出文档</el-button>
  </div>
</template>
<script setup>
import { exportDocx } from '@/utils/doc';
const param = {
  month: 11,
  day: 4,
  index: 5,
  hasMoreDays: false,
  endMonth: 11,
  endDay: 6,
  totalScene: 16,
  sceneNumTxt: '工地10个、码头6个',
  proNum: 23,
  changeNum: 19,
  unChangeNum: 4,
  table1: [
    {
      sIndex: 1,
      sType: '工地',
      sName: '上海电影艺术职业学院新建金山校区项目',
      sLocation: '东贤路康和路',
      sProblemList: [
        '1、渣土局部未覆盖',
        '2、散货物料露天堆放未覆盖',
        '3、工地内多条道路明显泥痕/泥泞/积尘/遗撒',
        '4、工地出入口100米道路明显积尘/遗撒/轮胎泥印'
      ],
      sTown: '张堰镇',
      sChange: '已整改'
    },
    {
      sIndex: 2,
      sType: '工地',
      sName: '上海交通大学医学院附属瑞金医院金山院区项目',
      sLocation:
        '金山区亭林镇36-05地块,亭虹路以东、林顺路以南、沈海高速以西、大亭公路以北',
      sProblemList: [
        '1、室外木材切割等木工作业',
        '2、工地内多条道路明显泥痕/泥泞/积尘/遗撒',
        '3、散货物料露天堆放未覆盖',
        '4、大面积露天裸土无防尘措施'
      ],
      sTown: '亭林镇',
      sChange: '已整改'
    },
    {
      sIndex: 3,
      sType: '工地',
      sName: '大信.中信海直华东无人机总部基地项目',
      sLocation: '东自然地块,西新泾,南黄文泾,北自然地块',
      sProblemList: ['1、出入口车轮泥痕', '2、主道路泥泞'],
      sTown: '高新区',
      sChange: '已整改'
    }
  ]
};
function export_doc() {
  exportDocx(
    '/秋冬季空气质量攻坚工作提示模板.docx',
    param,
    '工作提示.docx'
  );
}
</script>
src/views/fysp/check/components/ArbitraryPhoto.vue
@@ -27,14 +27,14 @@
          v-for="item in typeList"
          :key="item.businesstypeid"
          :label="
            item.businesstype + ' (' + typeImgMap.get(activeId).length + ')'
            item.businesstype + ' (' + typeImgMap.get(item.businesstypeid).length + ')'
          "
          :name="item.businesstypeid"
        >
        </el-tab-pane>
      </el-tabs>
      <el-empty v-if="isEmpty" description="暂无记录" />
      <el-scrollbar class="imgs">
      <el-scrollbar v-else class="imgs">
        <el-image
          v-for="(img, i) in typeImgMap.get(activeId)"
          :key="i"
src/views/fysp/check/components/CompDevicePhono.vue
@@ -29,7 +29,7 @@
        <el-tab-pane v-for="item in typeList" :label="item.label" :name="item.id"> </el-tab-pane>
      </el-tabs>
      <el-empty v-if="imgObjList.length == 0" description="暂无记录" />
      <div class="imgs">
      <el-scrollbar v-else class="imgs">
        <el-image
          v-for="(img, i) in imgObjList"
          :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']"
@@ -38,7 +38,7 @@
          lazy
          @click="onSelect(img, i)"
        />
      </div>
      </el-scrollbar>
    </div>
  </div>
</template>
@@ -186,7 +186,7 @@
  } */
.imgs {
  height: 650px;
  height: 50vh;
  width: 90%;
  min-height: 100px !important;
  /* border-style:solid;
@@ -255,7 +255,8 @@
  padding: 5px;
}
.el-dialog__body {
::v-deep .el-dialog__body {
  height: 60vh;
  padding: 10px calc(var(--el-dialog-padding-primary) + 10px) !important;
}
</style>
src/views/fysp/check/components/CompDevicePhoto.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
<template>
  <FYImageSelectDialog
    :typeList="typeList"
    :typeImgMap="typeImgMap"
    :maxSelect="1"
  ></FYImageSelectDialog>
</template>
<script setup>
import { ref, watch, computed } from 'vue';
const props = defineProps({
  // å±•示模式
  mode: {
    type: Number,
    default: 0
  },
  pics: Array
});
// typeList: [
//         { id: 0, label: '监控设备' },
//         { id: 1, label: '治理设备' },
//         { id: 2, label: '生产设备' }
//       ],
const typeList = computed(() => {
  if (props.mode == 0) {
    return [{ typeId: 0, typeName: '监控设备' }];
  } else if (props.mode == 1) {
    return [{ typeId: 1, typeName: '整改' }];
  } else {
    return [{ typeId: 1, typeName: '未指定' }];
  }
});
const typeImgMap = ref(new Map());
watch(
  () => props.pics,
  (nV, oV) => {
    nV.forEach(e => {
      if (!typeImgMap.value.has(e.topTypeId)) {
        typeImgMap.value.set(e.topTypeId, [])
      }
      typeImgMap.value.get(r.topTypeId).push({})
    });
    typeImgMap.value.set(
      1,
      nV.map((v) => {
        return { url: v };
      })
    );
  },
  { immediate: true }
);
</script>
src/views/fysp/data-product/ProdDailyReport.vue
@@ -1,46 +1,55 @@
<template>
  <!-- ä¸»å†…容 -->
  <FYSearchBar ref="searchRef" @search="search" :loading="loading">
    <template #options>
      <!-- åŒºåŽ¿ -->
      <FYOptionLocation
        :allOption="false"
        :level="3"
        :checkStrictly="false"
        v-model:value="formSearch.locations"
        style="width: 300px"
      ></FYOptionLocation>
      <!-- åœºæ™¯ç±»åž‹ -->
      <!-- <FYOptionScene
        :allOption="true"
        :type="2"
        v-model:value="formSearch.scenetype"
      ></FYOptionScene> -->
      <!-- æ—¶é—´ -->
      <FYOptionTime
        :initValue="false"
        type="datetimerange"
        v-model:value="formSearch.timeArray"
        style="width: 250px"
      ></FYOptionTime>
    </template>
    <template #buttons>
      <el-button
        icon="Download"
        type="primary"
        class=""
        :loading="docLoading"
        :disabled="!docParam"
        @click="genWord()"
      >
        ç”ŸæˆæŠ¥å‘Š
      </el-button>
      <el-button
        icon="Download"
        type="success"
        class=""
        :disabled="!docParam"
        @click="exportToExcel()"
      >
        ä¸‹è½½è¡¨æ ¼
      </el-button>
    </template>
  </FYSearchBar>
  <div class="m-task container">
    <!--头部信息-->
    <div class="h-top col-md-12">
      <div class="options">
        <!-- <input type="date" class="c-time c-check" /> -->
        <!-- æ—¶é—´ -->
        <el-form-item label="时间" :prop="prop" style="margin-left: 10px">
          <el-date-picker
            v-model="formSearch.time"
            type="date"
            placeholder="选择时间"
            start-placeholder="选择开始时间"
            end-placeholder="选择结束时间"
            style="width: 180px"
          />
        </el-form-item>
        <el-form-item label="区域" :prop="prop" style="margin-left: 10px">
          <el-select
            placeholder="Select"
            v-model="formSearch.districtCode"
            class="c-area c-check"
            style="width: 100px"
          >
            <el-option label="金山区" value="310116"></el-option>
            <el-option label="静安区" value="310106"></el-option>
            <el-option label="徐汇区" value="310104"></el-option>
            <el-option label="长宁区" value="310105"></el-option>
          </el-select>
        </el-form-item>
        <el-button
          style="margin-left: 10px"
          class="check-msg"
          @click="search"
          type="primary"
          >查询</el-button
        >
        <el-button type="primary" class="" @click="exportToExcel()">
          ç”ŸæˆæŠ¥å‘Š
        </el-button>
      </div>
      <div class="m-top">
        <span class="title-input"> {{ reportName }} </span>
        <!-- <input
@@ -52,21 +61,10 @@
    </div>
    <div class="m-msg col-md-12">
      <span
        name="desc"
        class="desc-textarea"
        rows="5"
        value=""
        cols=""
        disabled
      ></span>
      <span>{{ descMsg }}</span>
      <div class="report-table row">
        <table id="table_subtask" class="" border="1"></table>
      </div>
    </div>
    <div class="dark-screen">
      <i class="fa fa-spinner fa-spin fa-3x" style="color: white"></i>
    </div>
  </div>
</template>
@@ -77,28 +75,13 @@
import * as XLSX from 'xlsx';
import FileSaver from 'file-saver';
import analysisApi from '@/api/fysp/analysisApi.js';
import { exportDocx } from '@/utils/doc';
export default {
  name: 'DailyReport',
  computed: {
    reportName() {
      var city = '';
      switch (this.formSearch.districtCode) {
        case '310116':
          city = '金山区';
          break;
        case '310106':
          city = '静安区';
          break;
        case '310104':
          city = '徐汇区';
          break;
        case '310105':
          city = '长宁区';
          break;
      }
      let title = `${city}扬尘污染辅助监管工作日报`;
      return title;
      return `${this.formSearch.locations.dName}扬尘污染辅助监管工作日报`;
    }
  },
  data() {
@@ -235,10 +218,18 @@
        headers: [],
        data: []
      },
      loading: false,
      formSearch: {
        locations: {},
        scenetype: {},
        time: [],
        timeArray: [new Date(), new Date()],
        districtCode: '310116'
      }
      },
      descMsg: '',
      // å¯¼å‡ºæŠ¥å‘Šå‚æ•°
      docParam: undefined,
      docLoading: false
    };
  },
  watch: {
@@ -365,147 +356,98 @@
        `${this.reportName}.xlsx`
      );
    },
    downloadFile() {
      // æ–‡ä»¶æ ‡é¢˜
      let title = this.reportName;
      // è¡¨å¤´æè¿°
      let desc = document.querySelector('.desc-textarea').textContent;
      // æ—¶é—´
      let time = document
        .querySelector('.desc-textarea')
        .textContent.split(',')[0];
      // èŽ·å–æ•°æ®
      let thData = Array.from(document.getElementsByTagName('th')).map(
        (th) => th.innerText
      );
      let tdData = Array.from(document.getElementsByTagName('td')).map(
        (td) => td.innerText
      );
      let totalList = [];
      let subList = [];
      let index = 2;
      let inputStatus = true;
      for (let j = 0; j < thData.length; j++) {
        subList.push(thData[j]);
      }
      totalList.push(subList);
      subList = [];
      for (let i = 0; i < tdData.length; i++) {
        if (tdData[i] !== String(index)) {
          if (tdData[i] !== '') {
            subList.push(tdData[i]);
          } else {
            if (inputStatus) {
              let inputList = document.querySelectorAll(`.input-${subList[0]}`);
              for (let j = 0; j < inputList.length; j++) {
                subList.push(inputList[j].value);
              }
              inputStatus = false;
            } else {
              inputStatus = true;
            }
          }
        } else {
          totalList.push(subList);
          subList = [String(index)];
          index += 1;
        }
      }
      totalList.push(subList);
      // index: 0  head
      // other: data
      // title
      let fileData = {
        title: title,
        time: time,
        desc: desc,
        data: totalList
      };
      // if (fileData.title && fileData.time && fileData.desc && fileData.data.length) {
      //     fetch("http://47.100.191.150:9002/dustmonitor/report", {
      //         method: "POST",
      //         headers: {
      //             "Content-Type": "application/json"
      //         },
      //         body: JSON.stringify(fileData)
      //     })
      //     .then(response => response.json())
      //     .then(res => {
      //         if (res.status === 200) {
      //             let name = res.name.split("./")[1];
      //             setTimeout(() => {
      //                 let a = document.createElement("a");
      //                 a.href = appConfig.downloadUrl + name;
      //                 a.download = name; // è®¾ç½®ä¸‹è½½æ–‡ä»¶å
      //                 document.body.appendChild(a);
      //                 a.click();
      //                 document.body.removeChild(a);
      //             }, 1000);
      //         }
      //     })
      //     .catch(error => console.error("Error:", error));
      // }
    },
    search() {
      let sTime = dayjs(this.formSearch.time).hour(0).minute(0).second(0);
      let eTime = dayjs(this.formSearch.time).hour(23).minute(59).second(59);
      eTime = eTime.toISOString();
      sTime = sTime.toISOString();
      let sTime = dayjs(this.formSearch.timeArray[0])
        .hour(0)
        .minute(0)
        .second(0)
        .millisecond(0);
      let eTime = dayjs(this.formSearch.timeArray[1])
        .hour(23)
        .minute(59)
        .second(59)
        .millisecond(0);
      // eTime = eTime.toISOString();
      // sTime = sTime.toISOString();
      let config = {
        startTime: sTime,
        endTime: eTime,
        districtCode: this.formSearch.districtCode
        startTime: sTime.toDate(),
        endTime: eTime.toDate(),
        districtCode: this.formSearch.locations.dCode
      };
      analysisApi.checkProblem(config).then((res) => {
        this.table.headers = res.tableTitle[0];
        this.table.data = res.tableContent;
      this.loading = true;
      analysisApi
        .dailyreport(config)
        .then((res) => {
          this.table.headers = res.tableTitle[0];
          this.table.data = res.tableContent;
        this.CompTable.init(res.tableTitle[0], res.tableContent);
        this.CompTable.show('table_subtask');
          this.CompTable.init(res.tableTitle[0], res.tableContent);
          this.CompTable.show('table_subtask');
        const countMap = new Map(); //各场景数量
        let proCount = 0; //总计问题数量
        let changeCount = 0; //总计整改数量
          const countMap = new Map(); //各场景数量
          let proCount = 0; //总计问题数量
          let changeCount = 0; //总计整改数量
          const tableRows = [];
        res.tableContent.forEach((cList) => {
          const sceneType = cList[2];
          if (!(sceneType in countMap)) {
            countMap[sceneType] = 0;
          res.tableContent.forEach((cList) => {
            const sceneType = cList[2];
            if (!(sceneType in countMap)) {
              countMap[sceneType] = 0;
            }
            countMap[sceneType] += 1;
            proCount += Number(cList[10]);
            changeCount += Number(cList[13]);
            tableRows.push({
              sIndex: cList[0],
              sType: sceneType,
              sName: cList[3],
              sLocation: cList[4],
              sProblemList: cList[8].split('\n'),
              time: cList[6],
              sTown: cList[5],
              sChangeList: cList[11].split('\n')
            });
          });
          let countStr = '';
          for (const key in countMap) {
            const e = countMap[key];
            if (countStr != '') {
              countStr += '、';
            }
            countStr += `${key}${e}个`;
          }
          countMap[sceneType] += 1;
          proCount += Number(cList[10]);
          changeCount += Number(cList[13]);
        });
          this.descMsg = `共巡查${res.tableContent.length}个扬尘场景(${countStr}),共发现问题${proCount}项,督促整改了${changeCount}项。`;
        let countStr = '';
        for (const key in countMap) {
          const e = countMap[key];
          if (countStr != '') {
            countStr += '、';
          }
          countStr += `${key}${e}个`;
        }
        var descMsg = `共巡查${res.tableContent.length}个扬尘场景(${countStr}),共发现问题${proCount}项,督促整改了${changeCount}项;前期余留问题均已销项。`;
        console.log('descMsg', descMsg);
        // èŽ·å–æ‰€æœ‰ç±»åä¸º 'desc-textarea' çš„元素
        const textareas = document.querySelectorAll('.desc-textarea');
        // éåŽ†è¿™äº›å…ƒç´ å¹¶è®¾ç½®å…¶ HTML å†…容
        textareas.forEach(function (textarea) {
          textarea.innerHTML = descMsg;
        });
        // $('.desc-textarea').html('');
        // $('.desc-textarea').html(descMsg);
      });
          this.docParam = {
            month: sTime.month() + 1,
            day: sTime.date(),
            index: 1,
            hasMoreDays: !sTime.isSame(eTime, 'day'),
            endMonth: eTime.month() + 1,
            endDay: eTime.date(),
            totalScene: res.tableContent.length,
            sceneNumTxt: countStr,
            proNum: proCount,
            changeNum: changeCount,
            unChangeNum: proCount - changeCount,
            table1: tableRows
          };
        })
        .finally(() => (this.loading = false));
    },
    // ç”Ÿæˆword报告
    genWord() {
      if (this.docParam) {
        const param = this.docParam;
        exportDocx(
          '/秋冬季空气质量攻坚工作提示模板.docx',
          param,
          `秋冬季空气质量攻坚工作提示(${param.month}月${param.day}日).docx`
        );
      }
    }
  }
};
src/views/fysp/data-product/ProdSceneReport.vue
@@ -13,12 +13,13 @@
    <template #main>
      <el-scrollbar class="el-scrollbar" v-loading="mainLoading">
        <CompSceneConstructionInfo
          title="A、工地基本信息"
          title="A、基本信息"
          :scene="formScene"
          :form-info="formSubScene"
        />
        <div><el-text type="">附图片:</el-text></div>
        <CompImgInfo
          title="施工铭牌"
          v-model:title="imgTitle"
          :img-src="sceneImg.url"
          @change="anyPhotoDialog = true"
        ></CompImgInfo>
@@ -29,9 +30,9 @@
            v-for="(item, i) in deviceList"
            :key="i"
            down-title
            :title="item._deviceTypeName"
            v-model:title="item._deviceTypeName"
            :img-src="item._showStatusPic"
            @change="showDevicePhotoDialog(item)"
            @change="showDevicePhotoDialog(item, i)"
          ></CompImgInfo>
        </el-space>
        <el-divider />
@@ -41,10 +42,32 @@
            v-for="(item, i) in curProList"
            :key="i"
            :problem="item"
            @change="(value) => handleProPicSelect(value, i)"
          ></CompProblemTable>
        </el-space>
        <el-divider />
        <el-text tag="h1">D、扬尘污染防治建议</el-text>
        <div class="p-b-8">
          é’ˆå¯¹è¯¥å·¥åœ°æœ¬æœŸå·¡æŸ¥å‘现的问题,评估为扬尘污染防治
          <el-radio-group v-model="radioStandard" size="default">
            <el-radio value="规范" border>规范</el-radio>
            <el-radio value="基本规范" border>基本规范</el-radio>
            <el-radio value="不规范" border>不规范</el-radio>
            <el-radio value="严重不规范" border>严重不规范</el-radio>
          </el-radio-group>
          å·¥åœ°ï¼Œå»ºè®®è¯¥å·¥åœ°ä¸¥æ ¼æŒ‰ç…§ã€Šæ‰¬å°˜é˜²æ²»æ–¹æ¡ˆã€‹ã€ã€Šæ‰¬å°˜é˜²æ²»æ‰¿è¯ºä¹¦ã€‹ã€ã€Šæ–‡æ˜Žæ–½å·¥ç®¡ç†è§„范》、《精细化管理指标》等相关文件落实整改。
        </div>
        <el-row justify="center" style="height: 200px">
          <el-button
            icon="Download"
            type="primary"
            :loading="docLoading"
            :disabled="!sceneImg.url"
            @click="genWord()"
          >
            ç”ŸæˆæŠ¥å‘Š
          </el-button>
        </el-row>
      </el-scrollbar>
    </template>
  </BaseContentLayout>
@@ -83,14 +106,18 @@
import taskApi from '@/api/fysp/taskApi';
import sceneApi from '@/api/fysp/sceneApi';
import deviceApi from '@/api/fysp/deviceApi';
import evaluateApi from '@/api/fysp/evaluateApi';
import { formatDeviceList } from '@/model/fysp/device';
import { enumDevice, toLabel } from '@/enum/device/device';
import { exportDocx } from '@/utils/doc';
import right_triangle from '@/assets/image/right_triangle.png';
import CompSceneConstructionInfo from '@/views/fysp/scene/CompSceneConstructionInfo.vue';
import ArbitraryPhoto from '@/views/fysp/check/components/ArbitraryPhoto.vue';
import CompDevicePhono from '@/views/fysp/check/components/CompDevicePhono.vue';
import CompImgInfo from '@/views/fysp/data-product/components/CompImgInfo.vue';
import CompProblemTable from './components/CompProblemTable.vue';
import dayjs from 'dayjs';
/************************* å·¦ä¾§å·¡æŸ¥ä»»åŠ¡é€‰å• **********************************/
const curSubtask = ref({});
@@ -151,16 +178,19 @@
  });
  fetchDeviceList(s);
  fetchProblems(s);
  fetchEvaluation(s);
}
/************************* åœºæ™¯åŸºæœ¬ä¿¡æ¯ **********************************/
const imgTitle = ref('施工铭牌');
const formScene = ref({});
const formSubScene = ref({});
function fetchSceneInfo(sceneId) {
  formSubScene.value = {};
  sceneImg.value = {};
  return sceneApi.getSceneDetail(sceneId).then((res) => {
    //场景
    // if (res.data.scense) formScene = res.data.scense;
    if (res.data.scense) formScene.value = res.data.scense;
    formSubScene.value = res.data.subScene ? res.data.subScene : {};
    // if (res.data.sceneDevice) {
    //   formSceneDevice = res.data.sceneDevice;
@@ -185,12 +215,13 @@
// è®¾å¤‡å›¾ç‰‡é€‰æ‹©å¯¹è¯æ¡†
const deiveceImgDialog = ref(false);
const showDeviceImg = ref({});
const showDeviceIndex = ref(0);
const showDeviceImgList = ref([]);
// è®¾å¤‡å›¾ç‰‡åˆ—表
const deviceList = ref([]);
function showDevicePhotoDialog(device) {
function showDevicePhotoDialog(device, index) {
  showDeviceIndex.value = index;
  deiveceImgDialog.value = true;
  showDeviceImgList.value = [];
  let imgList = [];
@@ -206,7 +237,7 @@
        })
      );
    });
  console.log(imgList);
  // console.log(imgList);
  showDeviceImgList.value = imgList;
}
@@ -214,7 +245,7 @@
function handleSelectDevicePhoto(data) {
  deiveceImgDialog.value = false;
  if (data.length > 0) {
    showDeviceImg.value = { url: data[0].url };
    deviceList.value[showDeviceIndex.value]._showStatusPic = data[0]._picUrl;
  }
}
@@ -271,14 +302,119 @@
  }
}
/************************* çŽ°åœºå·¡æŸ¥æƒ…å†µï¼ˆé—®é¢˜ä¸Žæ•´æ”¹ï¼‰ **********************************/
const problemDesc = ref('');
//当前任务的问题列表
const curProList = ref([]);
const month = ref('');
const selectedProList = ref([]);
function fetchProblems(s) {
  curProList.value = [];
  taskApi.getProBySubtask(s.data.stGuid).then((res) => {
    curProList.value = res;
    // ç”Ÿæˆå·¡æŸ¥æè¿°æ–‡æœ¬
    month.value = dayjs(s.data.stPlanTime).month() + 1;
    const proCount = curProList.value.length;
    problemDesc.value = `${month.value}月巡查共计发现${proCount}个问题`;
    if (proCount > 0) {
      problemDesc.value += ':';
      curProList.value.forEach((p, i) => {
        problemDesc.value += `${i + 1}、${p.problemname};`;
      });
      problemDesc.value += '如下图所示:';
    } else {
      problemDesc.value += '。';
    }
    // ç”Ÿæˆé€‰ä¸­çš„问题和整改图片描述
    selectedProList.value = curProList.value.map((v) => {
      return {};
    });
  });
}
function handleProPicSelect(value, index) {
  selectedProList.value[index] = value;
}
/************************* æ‰¬å°˜é˜²æ²»å»ºè®® **********************************/
const radioStandard = ref('规范');
function fetchEvaluation(s) {
  evaluateApi.fetchItemEvaluation(s.data.stGuid).then((res) => {
    radioStandard.value = res.data.grade;
  });
}
/************************* ç”ŸæˆæŠ¥å‘Š **********************************/
const docLoading = ref(false);
// ç”Ÿæˆword报告
function genWord() {
  const _deviceList = [];
  for (let i = 0; i < deviceList.value.length; i += 2) {
    const d1 = deviceList.value[i];
    const d2 =
      i + 1 < deviceList.value.length ? deviceList.value[i + 1] : undefined;
    _deviceList.push({
      _showStatusPic_1: d1._showStatusPic,
      _deviceTypeName_1: d1._deviceTypeName,
      // hasPic2: d2 ? true : false,
      _showStatusPic_2: d2 ? d2._showStatusPic : right_triangle,
      _deviceTypeName_2: d2 ? d2._deviceTypeName : ''
    });
  }
  const param = {
    index: formScene.value.index,
    sceneName: formScene.value.name,
    projectType: formSubScene.value.csProjectType,
    stage: formSubScene.value.siExtension1,
    startTime: formSubScene.value.csStartTime,
    endTime: formSubScene.value.csEndTime,
    leftTime: formSubScene.value.csLeftTime,
    location: formScene.value.location,
    floorSpace: formSubScene.value.csFloorSpace,
    constructionArea: formSubScene.value.csConstructionArea,
    constructionAreaPerMonth: formSubScene.value.csConstructionAreaPerMonth,
    contacts: formScene.value.contacts,
    contactst: formScene.value.contactst,
    securityOfficer: formSubScene.value.csSecurityOfficer,
    securityOfficerTel: formSubScene.value.csSecurityOfficerTel,
    constructionUnit: formSubScene.value.csConstructionUnit,
    employerUnit: formSubScene.value.csEmployerUnit,
    sceneType: formScene.value.type,
    imgTitle: imgTitle.value,
    imgTitle_url: sceneImg.value.url,
    deviceList: _deviceList,
    problemDesc: problemDesc.value,
    problemList: selectedProList.value.map((v) => {
      return {
        month: month.value,
        ...v
      };
    }),
    standard_1: radioStandard.value == '规范',
    standard_2: radioStandard.value == '基本规范',
    standard_3: radioStandard.value == '不规范',
    standard_4: radioStandard.value == '严重规范'
  };
  for (const key in param) {
    if (param[key] == undefined) {
      param[key] = '';
    }
  }
  const date = dayjs(curSubtask.value.data.stPlanTime).format('MM月DD日');
  console.log(param);
  docLoading.value = true;
  exportDocx(
    '/单体模版.docx',
    param,
    `${param.sceneName}单体(${date}).docx`,
    {
      horizontalHeight: 368,
      verticalWidth: 266
    }
  ).finally(() => (docLoading.value = false));
}
</script>
src/views/fysp/data-product/components/CompImgInfo.vue
@@ -4,15 +4,19 @@
      <tr v-if="!downTitle">
        <td>
          <el-row justify="space-between" align="middle">
            {{ title }}
            <el-button size="small" @click="$emit('change')">{{
              btnName
            }}</el-button>
            <!-- {{ title }} -->
            <el-input
              size="small"
              :model-value="title"
              @input="handleInputChange"
              placeholder="标题"
              style="width: 150px"
            />
          </el-row>
        </td>
      </tr>
      <tr>
        <td>
        <td style="position: relative">
          <el-image
            class="image"
            :src="imgSrc"
@@ -29,15 +33,22 @@
              </div>
            </template>
          </el-image>
          <el-button class="pop-button" size="small" @click="$emit('change')">{{
            btnName
          }}</el-button>
        </td>
      </tr>
      <tr v-if="downTitle">
        <td>
          <el-row justify="space-between" align="middle">
            {{ title }}
            <el-button size="small" @click="$emit('change')">{{
              btnName
            }}</el-button>
            <!-- {{ title }} -->
            <el-input
              size="small"
              :model-value="title"
              @input="handleInputChange"
              placeholder="标题"
              style="width: 150px"
            />
          </el-row>
        </td>
      </tr>
@@ -60,7 +71,11 @@
// const unchangeImg = '../../../../assets/image/unchange.png'
const emit = defineEmits(['change']);
const emit = defineEmits(['change', 'update:title']);
function handleInputChange(value) {
  emit('update:title', value);
}
</script>
<style scoped>
.image {
@@ -96,4 +111,10 @@
  height: 100%;
  background: var(--el-fill-color-light);
}
.pop-button {
  position: absolute;
  bottom: 0;
  right: 0;
}
</style>
src/views/fysp/data-product/components/CompProblemTable.vue
@@ -74,6 +74,7 @@
  </table>
  <CompProblemPicSelect
    v-if="pics.length > 0"
    title="问题图片"
    v-model:dialog-visible="proDialog"
    mode="problem"
    :pics="pics[0].path"
@@ -82,6 +83,7 @@
  ></CompProblemPicSelect>
  <CompProblemPicSelect
    v-if="pics.length > 1"
    title="整改图片"
    v-model:dialog-visible="changeDialog"
    mode="change"
    :pics="pics[1].path"
@@ -138,13 +140,26 @@
function handleProPicSelect(imgList) {
  if (imgList && imgList.length > 0) {
    seletcedProblemPic.value = imgList[0].url;
    onChange();
  }
}
function handleChangePicSelect(imgList) {
  if (imgList && imgList.length > 0) {
    seletcedChangePic.value = imgList[0].url;
    onChange();
  }
}
function onChange() {
  const value = {
    proPic: seletcedProblemPic.value,
    changePic: seletcedChangePic.value ? seletcedChangePic.value : unchangeImg,
    location: props.problem.location,
    problemDes,
    changeDes
  };
  emit('change', value);
}
watch(
@@ -154,10 +169,23 @@
      getPics();
      problemDes.value = nV.problemname;
      changeDes.value = nV.ischanged ? '已整改' : '未整改';
      onChange();
    }
  },
  { immediate: true }
);
watch(problemDes, (nV, oV) => {
  if (nV != oV) {
    onChange();
  }
});
watch(changeDes, (nV, oV) => {
  if (nV != oV) {
    onChange();
  }
});
</script>
<style scoped>
.image {
src/views/fysp/scene/CompSceneConstructionInfo.vue
@@ -1,7 +1,7 @@
<!-- å·¥åœ°ä¸“属信息编辑 -->
<template>
  <el-form
    v-if="showStyle == 'form'"
    v-show="showStyle == 'form'"
    :inline="false"
    :model="formObj"
    ref="formRef"
@@ -164,7 +164,7 @@
  </el-form>
  <el-descriptions
    v-else-if="showStyle == 'descriptions'"
    v-show="showStyle == 'descriptions'"
    :column="2"
    :size="fontSize"
    direction="horizontal"
@@ -175,14 +175,17 @@
    </template>
    <template #extra>
      <el-button
        :disabled="!edit && !ignoreEdit"
        :size="fontSize"
        :disabled="!edit"
        type="primary"
        @click="onSubmit"
        :loading="loading"
        >提交</el-button
      >
      <el-button :size="fontSize" :disabled="!edit" @click="onReset"
      <el-button
        :size="fontSize"
        :disabled="!edit && !ignoreEdit"
        @click="onReset"
        >重置</el-button
      >
    </template>
@@ -237,7 +240,7 @@
    <el-descriptions-item label="施工地址" span="2"
      ><el-input
        clearable
        v-model="formObj.location"
        v-model="sceneObj.location"
        placeholder="施工地址"
        :size="fontSize"
    /></el-descriptions-item>
@@ -271,6 +274,25 @@
        <template #append>㎡</template>
      </el-input></el-descriptions-item
    >
    <el-descriptions-item label="项目负责人"
      ><el-input
        :size="fontSize"
        clearable
        v-model="sceneObj.contacts"
        placeholder="项目负责人"
    /></el-descriptions-item>
    <el-descriptions-item label="项目负责人电话"
      ><el-input
        :size="fontSize"
        clearable
        type="tel"
        v-model="sceneObj.contactst"
        placeholder="项目负责人电话"
      >
        <template #prepend>
          <el-icon><Iphone /></el-icon>
        </template> </el-input
    ></el-descriptions-item>
    <el-descriptions-item label="安全员"
      ><el-input
        :size="fontSize"
@@ -342,7 +364,7 @@
</template>
<script setup>
import { defineProps, defineEmits, reactive, ref, watch } from 'vue';
import { reactive, ref, watch, computed } from 'vue';
import { useDateFormat } from '@vueuse/core';
import { enumStatusNA, enumStageNA } from '@/enum/construction';
import sceneApi from '@/api/fysp/sceneApi';
@@ -366,8 +388,14 @@
  title: String
});
const fontSize = ref('small');
const emit = defineEmits(['onSubmit', 'onCancel']);
const emit = defineEmits([
  'onSubmit',
  'onCancel',
  'update:scene',
  'update:formInfo'
]);
const sceneObj = ref({});
const { formObj, formRef, edit, onSubmit, onReset } = useFormConfirm({
  submit: {
    do: submit
@@ -376,6 +404,8 @@
    do: cancel
  }
});
const ignoreEdit = computed(() => props.showStyle == 'descriptions');
const loading = ref(false);
const status = reactive(enumStatusNA());
const stage = reactive(enumStageNA());
@@ -404,7 +434,7 @@
});
// åˆ›å»ºæˆ–更新场景详情
function createOrupdateScene() {
function createOrupdateSubScene() {
  loading.value = true;
  if (formObj.value._timeRange && formObj.value._timeRange.length == 2) {
@@ -415,6 +445,8 @@
  return sceneApi
    .updateSubScene(props.sceneType, formObj.value)
    .then((res) => {
      emit('onSubmit', formObj);
      emit('update:formInfo', formObj);
      return res.data;
    })
    .finally(() => {
@@ -422,10 +454,16 @@
    });
}
function submit() {
  emit('onSubmit', formObj);
// æ›´æ–°åœºæ™¯
function updateScene() {
  return sceneApi.updateScene(sceneObj.value).then(() => {
    emit('update:scene', sceneObj);
  });
}
  return createOrupdateScene();
function submit() {
  updateScene();
  return createOrupdateSubScene();
}
function cancel() {
@@ -445,6 +483,16 @@
  },
  { deep: false, immediate: true }
);
watch(
  () => props.scene,
  (nValue) => {
    if (nValue) {
      sceneObj.value = nValue;
    }
  },
  { deep: false, immediate: true }
);
</script>
<style>