Explorar o código

更新PC端代码

syh %!s(int64=2) %!d(string=hai) anos
pai
achega
03876eef76
Modificáronse 33 ficheiros con 2197 adicións e 605 borrados
  1. 27 2
      README.md
  2. 2 2
      package.json
  3. 75 0
      src/api/statistics.js
  4. BIN=BIN
      src/assets/statisitcs/menu1.png
  5. BIN=BIN
      src/assets/statisitcs/menu2.png
  6. BIN=BIN
      src/assets/statisitcs/menu3.png
  7. BIN=BIN
      src/assets/statisitcs/menu4.png
  8. BIN=BIN
      src/assets/statisitcs/menu5.png
  9. BIN=BIN
      src/assets/statisitcs/menu6.png
  10. 44 36
      src/component/ApproveStatusV2.vue
  11. 1 0
      src/component/FormOption.vue
  12. 14 0
      src/router/router.config.js
  13. 144 121
      src/utils/tool.js
  14. 2 2
      src/views/home/components/FinishInfo.vue
  15. 45 2
      src/views/home/components/TableDialog.vue
  16. 267 209
      src/views/home/config/bar.js
  17. 8 2
      src/views/login/Login.vue
  18. 2 4
      src/views/monitor/check/CheckDetail.vue
  19. 11 9
      src/views/monitor/check/component/AddNodeItem.vue
  20. 4 8
      src/views/monitor/check/component/NodeItemsTable.vue
  21. 4 2
      src/views/monitor/limitApproval/Index.vue
  22. 1 2
      src/views/monitor/limitApproval/component/DataTable.vue
  23. 1 1
      src/views/random/random/RandomRuleSettingForm.vue
  24. 218 180
      src/views/random/record/component/ResourceOption.vue
  25. 8 16
      src/views/resource/ResourceEdit.vue
  26. 8 7
      src/views/resource/component/ResourceTable.vue
  27. 546 0
      src/views/statistics/data/Data.vue
  28. 67 0
      src/views/statistics/data/component/Card.vue
  29. 95 0
      src/views/statistics/data/component/ContributTable.vue
  30. 96 0
      src/views/statistics/data/component/DetailTable.vue
  31. 61 0
      src/views/statistics/data/component/Progress.vue
  32. 348 0
      src/views/statistics/data/component/SelectOption.vue
  33. 98 0
      src/views/statistics/data/component/TemplateTable.vue

+ 27 - 2
README.md

@@ -18,6 +18,31 @@ yarn build //正式环境
 
 ### 打包zip命名规范
 
-| 测试环境:app-test.zip  pc-test.zip ssj-test.zip
+```
+测试环境:app-test.zip  pc-test.zip ssj-test.zip
+```
+
+```
+正式环境:app-prod.zip  pc-prod.zip ssj-prod.zip
+```
+
+### 代码管理
+```
+master:生产代码分支
+dev:测试代码分支
+xxx: 个人开发分支
+```
 
-| 正式环境:app-prod.zip  pc-prod.zip ssj-prod.zip
+```
+如果需要修复生产问题,拉取master分支代码,新建"fix-日期"(fix-20220328),修改后合并此分支到dev,发布测试。
+```
+
+```
+合并原则:
+    1.如果是生产修复分支,需要直接部署生产,dev测试通过后,将此分支直接合并master,进行部署。不能将dev合并master。
+    2.其它按计划合并dev测试,需要部署时直接dev合并到master进行打包部署。
+```
+
+```
+生产打包后创建tag,以版本号为名,目前版本号:1.0.4
+```

+ 2 - 2
package.json

@@ -1,8 +1,8 @@
 {
   "name": "paas-manager",
-  "version": "0.0.1",
+  "version": "1.0.5",
   "private": true,
-  "description": "PAAS管理平台",
+  "description": "消防管理pc管理端",
   "scripts": {
     "serve": "vue-cli-service serve --open",
     "test": "vue-cli-service build --mode test",

+ 75 - 0
src/api/statistics.js

@@ -0,0 +1,75 @@
+import request from "@/utils/request";
+
+/**
+ * 数据总览查询
+ */
+export function getStatisticInfo(params) {
+  return request.get("/v2/api/data_statistics/info_total", { params });
+}
+
+/**
+ * 日常抽查总数
+ */
+export function getStatisticTotal(params) {
+  return request.get("/v2/api/index/statistics/job_total", { params });
+}
+
+/**
+ * 系统反馈总数
+ */
+export function getStatisticFeedbak(params) {
+  return request.get("/v2/api/data_statistics/system_feedbak", { params });
+}
+
+/**
+ * 场所类型划分
+ */
+export function getStatisticPlaceType(params) {
+  return request.get("/v2/api/index/statistics/job_by_place_type", { params });
+}
+
+/**
+ * 隐患情况统计
+ */
+export function getStatisticByItem(params) {
+  return request.get("/v2/api/data_statistics/statistic_by_item", { params });
+}
+
+/**
+ * 专项检查情况
+ */
+export function getStatisticSpecialInfo(params) {
+  return request.get("/v2/api/data_statistics/statistic_special_info", {
+    params,
+  });
+}
+
+/**
+ * 模板使用排行
+ */
+export function getStatisticTemplateInfo(params) {
+  return request.get("/v2/api/data_statistics/statistic_template_info", {
+    params,
+  });
+}
+
+/**
+ * 检查规则贡献值
+ */
+export function getStatisticContributeInfo(params) {
+  return request.get("/v2/api/data_statistics/statistic_contribute_info", {
+    params,
+  });
+}
+
+/**
+ * 检查规则贡献值详情
+ */
+export function getStatisticContributeDetail(id, params) {
+  return request.get(
+    `/v2/api/data_statistics/statistic_contribute_info/${id}`,
+    {
+      params,
+    }
+  );
+}

BIN=BIN
src/assets/statisitcs/menu1.png


BIN=BIN
src/assets/statisitcs/menu2.png


BIN=BIN
src/assets/statisitcs/menu3.png


BIN=BIN
src/assets/statisitcs/menu4.png


BIN=BIN
src/assets/statisitcs/menu5.png


BIN=BIN
src/assets/statisitcs/menu6.png


+ 44 - 36
src/component/ApproveStatusV2.vue

@@ -1,35 +1,26 @@
 <template>
     <div class="users">
-        <el-form :inline="true" :model="formData" :rules="rules" ref="formData" class="option">
-            <el-row>
-                <el-row>
-                    <el-form-item label="审批意见:" prop="status">
-                        <el-radio v-model="formData.status" label="PASS">批准</el-radio>
-                        <el-radio v-model="formData.status" label="REJECT">驳回</el-radio>
-                    </el-form-item>
-                </el-row>
-                <el-row>
-                    <el-form-item label="信息内容:" prop="reason">
-                        <el-input
-                                v-model="formData.reason"
-                                type="textarea"
-                                show-word-limit
-                                :rows="5"
-                                maxlength="300"
-                                placeholder="请输入"
-                        >
-                        </el-input>
-                    </el-form-item>
-                </el-row>
-                <el-row>
-                    <el-col :offset="10">
-                        <el-form-item>
-                            <el-button @click="cancel('formData')">取消</el-button>
-                            <el-button type="primary" :loading="loading" @click="save('formData')">确定</el-button>
-                        </el-form-item>
-                    </el-col>
-                </el-row>
-            </el-row>
+        <el-form :model="formData" :rules="rules" ref="formData" class="option">
+            <el-form-item label="审批意见:" prop="status">
+                <el-radio-group v-model="formData.status">
+                    <el-radio label="PASS">批准</el-radio>
+                    <el-radio label="REJECT">驳回</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item label="备注说明:" prop="reason" class="reason">
+                <el-input
+                    v-model="formData.reason"
+                    type="textarea"
+                    show-word-limit
+                    :rows="5"
+                    maxlength="300"
+                    placeholder="请输入">
+                </el-input>
+            </el-form-item>
+            <el-form-item class="btn">
+                <el-button @click="cancel('formData')">取消</el-button>
+                <el-button type="primary" :loading="loading" @click="save('formData')">确定</el-button>
+            </el-form-item>
         </el-form>
     </div>
 </template>
@@ -51,7 +42,7 @@ export default {
             loading: false,
             formData: {
                 status: null,
-                reason: null
+                reason: null,
             },
             rules: {
                 status: [
@@ -80,14 +71,15 @@ export default {
             this.$emit("changeApproveVisible");
         },
         save(formName) {
+            const vm = this;
             this.$refs[formName].validate((valid) => {
                 if (valid) {
                     this.loading = true;
-                    const url = handleProblemImgs(this.data.deadline_file_id);
-                    const name = this.data.enforce_org_name || '';
-                    const date = dateFormat('YYYY年mm月dd日', new Date(this.data.finish_time));
+                    const url = handleProblemImgs(vm.data.deadline_file_id);
+                    const name = vm.data.enforce_org_name || '';
+                    const date = dateFormat('YYYY年mm月dd日', new Date(vm.data.start_time));
                     const content = `根据《中华人民共和国消防法》第五十三条的规定,我${name}于${date}对你单位(场所)进行消防监督检查,即刻查看检查结果:${url}`;
-                    const params = Object.assign(this.formData, {
+                    const params = Object.assign(vm.formData, {
                         message: content
                     });
                     approveLimit(this.data.id, params).then(res => {
@@ -109,4 +101,20 @@ export default {
 
     }
 }
-</script>
+</script>
+
+<style lang="scss" scoped>
+.reason {
+    display: flex;
+    ::v-deep {
+        .el-form-item__content {
+            flex: 1;
+        }
+    }
+}
+.btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+</style>

+ 1 - 0
src/component/FormOption.vue

@@ -60,6 +60,7 @@ export default {
     padding: 16px 16px 10px;
     background-color: #FFFFFF;
     border-radius: 10px;
+    // box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.08);
     .header {
         display: flex;
         align-items: center;

+ 14 - 0
src/router/router.config.js

@@ -138,6 +138,20 @@ export const routes = [
             }
         ]
     },
+    {
+        path: '/statistics',
+        component: Layout,
+        redirect: '/statistics/data',
+        meta: {name: '统计', icon: 'el-icon-s-management'},
+        children: [
+            {
+                path: '/statistics/data',
+                name: 'Statistics',
+                meta: {name: '数据统计'},
+                component: () => import('../views/statistics/data/Data.vue')
+            },
+        ]
+    },
     {
         path: '/monitor',
         component: Layout,

+ 144 - 121
src/utils/tool.js

@@ -1,148 +1,171 @@
 const getUUID = function (len, radix) {
-    const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
-    let uuid = [], i;
-    radix = radix || chars.length;
-    if (len) {
-        for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
-    } 
-    else {
-        let r;
-        uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
-        uuid[14] = '4';
-        for (i = 0; i < 36; i++) {
-            if (!uuid[i]) {
-                r = 0 | Math.random() * 16;
-                uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
-            }
-        }
+  const chars =
+    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
+  let uuid = [],
+    i;
+  radix = radix || chars.length;
+  if (len) {
+    for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
+  } else {
+    let r;
+    uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
+    uuid[14] = "4";
+    for (i = 0; i < 36; i++) {
+      if (!uuid[i]) {
+        r = 0 | (Math.random() * 16);
+        uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
+      }
     }
-    return uuid.join('');
-}
+  }
+  return uuid.join("");
+};
 
-const getFontFamily = function() {
-    const agent = navigator.userAgent.toLowerCase()
-    const isMac = /macintosh|mac os x/i.test(navigator.userAgent)
-    if (isMac) {
-        return {
-            fontFamily: 'PingFangSC-Regular'
-        }
-    }
-    else {
-        return {
-            fontFamily: 'Microsoft YaHei'
-        }
-    }
-}
+const getFontFamily = function () {
+  const agent = navigator.userAgent.toLowerCase();
+  const isMac = /macintosh|mac os x/i.test(navigator.userAgent);
+  if (isMac) {
+    return {
+      fontFamily: "PingFangSC-Regular",
+    };
+  } else {
+    return {
+      fontFamily: "Microsoft YaHei",
+    };
+  }
+};
 
-const formatNumber = function(num) {
-    return (num + '').replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, '$1,')
-}
+const formatNumber = function (num) {
+  return (num + "").replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, "$1,");
+};
 
-const notEmpty = v => {
-    return v && v.length > 0 && v != 'null'
-}
+const notEmpty = (v) => {
+  let res = v && v != undefined && v != 'undefined' && v != 'null';
+  if (typeof v == 'string') {
+      res = res && v.trim().length > 0;
+  }
+  return res;
+};
 
-const getStr = v => {
-    if (notEmpty(v)) {
-        const s = v.trim();
-        if (notEmpty(s)) {
-            return s;
-        }
+const getStr = (v) => {
+  if (notEmpty(v)) {
+    const s = v.trim();
+    if (notEmpty(s)) {
+      return s;
     }
-    return null;
-}
+  }
+  return null;
+};
 
-const isEmpty = v => {
-    return !notEmpty(v)
-}
+const isEmpty = (v) => {
+  return !notEmpty(v);
+};
 
 function dateFormat(fmt, date) {
-    let ret;
-    const opt = {
-        "Y+": date.getFullYear().toString(),        // 年
-        "m+": (date.getMonth() + 1).toString(),     // 月
-        "d+": date.getDate().toString(),            // 日
-        "H+": date.getHours().toString(),           // 时
-        "M+": date.getMinutes().toString(),         // 分
-        "S+": date.getSeconds().toString()          // 秒
-        // 有其他格式化字符需求可以继续添加,必须转化成字符串
-    };
-    for (let k in opt) {
-        ret = new RegExp("(" + k + ")").exec(fmt);
-        if (ret) {
-            fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
-        }
+  let ret;
+  const opt = {
+    "Y+": date.getFullYear().toString(), // 年
+    "m+": (date.getMonth() + 1).toString(), // 月
+    "d+": date.getDate().toString(), // 日
+    "H+": date.getHours().toString(), // 时
+    "M+": date.getMinutes().toString(), // 分
+    "S+": date.getSeconds().toString(), // 秒
+    // 有其他格式化字符需求可以继续添加,必须转化成字符串
+  };
+  for (let k in opt) {
+    ret = new RegExp("(" + k + ")").exec(fmt);
+    if (ret) {
+      fmt = fmt.replace(
+        ret[1],
+        ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
+      );
     }
-    return fmt
+  }
+  return fmt;
 }
 
 function formatDate(str, useDefault = false) {
-    if (isEmpty(str + '')) {
-        return ''
-    }
-    const date = new Date(str)
-    if (date) {
-        const fmt = useDefault ? 'YYYY-mm-dd HH:MM:SS' : 'YYYY-mm-dd HH:MM'
-        return dateFormat(fmt, date)
-    }
-    else {
-        return str
-    }
+  if (isEmpty(str + "")) {
+    return "";
+  }
+  const date = new Date(str);
+  if (date) {
+    const fmt = useDefault ? "YYYY-mm-dd HH:MM:SS" : "YYYY-mm-dd HH:MM";
+    return dateFormat(fmt, date);
+  } else {
+    return str;
+  }
 }
 
 function formatDateMonth(str) {
-    if (isEmpty(str)) {
-        return ''
-    }
-    const date = new Date(str)
-    if (date) {
-        const fmt = 'YYYY-mm'
-        return dateFormat(fmt, date)
-    }
-    else {
-        return str
-    }
+  if (isEmpty(str)) {
+    return "";
+  }
+  const date = new Date(str);
+  if (date) {
+    const fmt = "YYYY-mm";
+    return dateFormat(fmt, date);
+  } else {
+    return str;
+  }
 }
 
-const formatResponse = function(code, msg, data) {
-    return new Promise(resolve => {
-        const res = {
-            code,
-            msg,
-            data
-        }
-        resolve(res)
-    })
-}
+const formatResponse = function (code, msg, data) {
+  return new Promise((resolve) => {
+    const res = {
+      code,
+      msg,
+      data,
+    };
+    resolve(res);
+  });
+};
 
 function handleProblemImgs(imageId) {
-    if (isEmpty(imageId)) {
-        return '';
-    }
-    let url = process.env.VUE_APP_API
-    if (process.env.NODE_ENV == 'development') {
-        url = 'http://183.66.101.90:8083'
-    }
-    return url + '/v2/api/file/' + imageId + '/open'
+  if (isEmpty(imageId)) {
+    return "";
+  }
+  let url = process.env.VUE_APP_API;
+  if (process.env.NODE_ENV == "development") {
+    url = "http://183.66.101.90:8083";
+  }
+  return url + "/v2/api/file/" + imageId + "/open";
 }
 
 export function getElementWidth(res) {
-    const clientWidth = window.innerWidth || document.documentElement.clientWidth|| document.body.clientWidth;
-    if (!clientWidth) return res;
-    let fontSize = clientWidth / 1920;
-    return res*fontSize;
+  const clientWidth =
+    window.innerWidth ||
+    document.documentElement.clientWidth ||
+    document.body.clientWidth;
+  if (!clientWidth) return res;
+  let fontSize = clientWidth / 1920;
+  return res * fontSize;
 }
 
+const compare = (property, desc = "asc") => {
+  return function (a, b) {
+    let value1 = a[property],
+      value2 = b[property];
+    if (desc === "desc") {
+      //降序
+      return value2 - value1;
+    } else {
+      //升序
+      return value1 - value2;
+    }
+  };
+};
+
 export {
-    getUUID,
-    getFontFamily,
-    formatNumber,
-    notEmpty,
-    isEmpty,
-    formatResponse,
-    dateFormat,
-    formatDate,
-    formatDateMonth,
-    handleProblemImgs,
-    getStr
-}
+  getUUID,
+  getFontFamily,
+  formatNumber,
+  notEmpty,
+  isEmpty,
+  formatResponse,
+  dateFormat,
+  formatDate,
+  formatDateMonth,
+  handleProblemImgs,
+  getStr,
+  compare
+};

+ 2 - 2
src/views/home/components/FinishInfo.vue

@@ -12,7 +12,7 @@
                     <div class="item-txt-name">完成率</div>
                 </div>
             </div>
-            <div class="item pointer" @click="onShowTableDialog(1)">
+            <div class="item pointer" @click="onShowTableDialog(2)">
                 <img class="item-icon" :src="status2" alt="" srcset="">
                 <div class="item-txt">
                     <div class="item-txt-title title-red">
@@ -21,7 +21,7 @@
                     <div class="item-txt-name">已完成</div>
                 </div>
             </div>
-            <div class="item pointer" @click="onShowTableDialog(2)">
+            <div class="item pointer" @click="onShowTableDialog(1)">
                 <img class="item-icon" :src="status3" alt="" srcset="">
                 <div class="item-txt">
                     <div class="item-txt-title title-yellow">

+ 45 - 2
src/views/home/components/TableDialog.vue

@@ -115,6 +115,9 @@ export default {
             handler(n) {
                 if (n) {
                     this.job_type = '';
+                    this.query.page = 1;
+                    this.query.size = 10;
+                    this.total = 0;
                     this.loadData();
                 }
             },
@@ -194,7 +197,7 @@ export default {
                 data.map(item => {
                     item.createTime = formatDate(item.create_time, true);
                     item.startTime = formatDate(item.start_time, true);
-                    item.objAddress = item.enforce_obj.enforce_obj_address;
+                    item.objAddress = item.enforce_obj && item.enforce_obj.enforce_obj_address || '';
                     item.result = this.parseJobResult(item);
                     item.jobStatus = this.parseJobStatus(item.job_status);
                     item.jobType = this.parseJobType(item.job_type);
@@ -204,7 +207,47 @@ export default {
             });
         },
         showOne(row) {
-            console.log('row: ', row);
+            const type = row.job_type;
+            if (type == 'NORMAL') {
+                this.$router.push({
+                    path: '/monitor/random/detail',
+                    query: {
+                        id: row.id
+                    }
+                })
+            }
+            if (type == 'SPECIAL') {
+                this.$router.push({
+                    path: '/monitor/special/detail',
+                    query: {
+                        id: row.id
+                    }
+                })
+            }
+            if (type == 'TOWNS') {
+                this.$router.push({
+                    path: '/monitor/hometown/detail',
+                    query: {
+                        id: row.id
+                    }
+                })
+            }
+            if (type == 'OTHER') {
+                this.$router.push({
+                    path: '/monitor/other/detail',
+                    query: {
+                        id: row.id
+                    }
+                })
+            }
+            if (type == 'REPORT') {
+                this.$router.push({
+                    path: '/monitor/report/detail',
+                    query: {
+                        id: row.id
+                    }
+                })
+            }
         },
     }
 }

+ 267 - 209
src/views/home/config/bar.js

@@ -1,4 +1,4 @@
-import echarts from 'echarts';
+import echarts from "echarts";
 // import { BarChart } from 'echarts/charts';
 // import {
 //     TitleComponent,
@@ -23,232 +23,290 @@ import echarts from 'echarts';
 //     LegendComponent,
 // ]);
 
-function getBarWidth(res){
-	const clientWidth = window.innerWidth || document.documentElement.clientWidth|| document.body.clientWidth;
-	if (!clientWidth) return res;
-	let fontSize = clientWidth / 1920;
-	return res*fontSize;
+function getBarWidth(res) {
+  const clientWidth =
+    window.innerWidth ||
+    document.documentElement.clientWidth ||
+    document.body.clientWidth;
+  if (!clientWidth) return res;
+  let fontSize = clientWidth / 1920;
+  return res * fontSize;
 }
 
 export function showPraise(id, data) {
-    const option = {
-        tooltip: {
-            trigger: 'item',
-            formatter: '{b}: {c}'
+  const option = {
+    tooltip: {
+      trigger: "item",
+      formatter: "{b}: {c}",
+    },
+    legend: {
+      show: false,
+    },
+    grid: {
+      left: 80,
+      top: 0,
+      right: 45,
+      bottom: 20,
+    },
+    xAxis: {
+      show: false,
+    },
+    yAxis: {
+      type: "category",
+      data: data.legendData,
+      axisLine: {
+        show: false,
+      },
+      axisTick: {
+        show: false,
+      },
+      axisLabel: {
+        color: "#333333",
+        fontSize: 12,
+        width: 80,
+        overflow: "truncate",
+        interval: 0,
+      },
+    },
+    series: [
+      {
+        name: "任务数",
+        type: "bar",
+        data: data.seriesData,
+        barWidth: getBarWidth(12),
+        label: {
+          show: true,
+          position: "right",
         },
-        legend: {
-            show: false,
+        itemStyle: {
+          color: {
+            type: "linear",
+            x: 0,
+            y: 0,
+            x2: 1,
+            y2: 1,
+            colorStops: [
+              {
+                offset: 0,
+                color: "rgba(61, 147, 255, 0.42)", // 0% 处的颜色
+              },
+              {
+                offset: 1,
+                color: "#3B90EB", // 100% 处的颜色
+              },
+            ],
+            global: false, // 缺省为 false
+          },
+          barBorderRadius: 4,
         },
-        grid: {
-            left: 80,
-            top: 0,
-            right: 45,
-            bottom: 20,
-        },
-        xAxis: {
-            show: false,
-        },
-        yAxis: {
-            type: 'category',
-            data: data.legendData,
-            axisLine: {
-                show: false,
-            },
-            axisTick: {
-                show: false,
-            },
-            axisLabel: {
-                color: '#333333',
-                fontSize: 12,
-                width: 80,
-                overflow: 'truncate',
-                interval: 0,
-            },
-        },
-        series: [{
-            name: '任务数',
-            type: 'bar',
-            data: data.seriesData,
-            barWidth: getBarWidth(12),
-            label: {
-                show: true,
-                position: "right"
-            },
-            itemStyle: {
-                color: {
-                    type: 'linear',
-                    x: 0,
-                    y: 0,
-                    x2: 1,
-                    y2: 1,
-                    colorStops: [{
-                        offset: 0,
-                        color: 'rgba(61, 147, 255, 0.42)' // 0% 处的颜色
-                    }, {
-                        offset: 1,
-                        color: '#3B90EB' // 100% 处的颜色
-                    }],
-                    global: false // 缺省为 false
-                },
-                barBorderRadius: 4,
-            }
-        }, ]
-    };
+      },
+    ],
+  };
 
-    const e = document.getElementById(id);
-    let instance = echarts.getInstanceByDom(e);
-    if (instance == null) {
-        instance = echarts.init(e);
-    }
-    instance.setOption(option);
-    setTimeout(() => {
-        window.onresize = function () {
-            instance.resize();
-        };
-    }, 1300);
+  const e = document.getElementById(id);
+  let instance = echarts.getInstanceByDom(e);
+  if (instance == null) {
+    instance = echarts.init(e);
+  }
+  instance.setOption(option);
+  setTimeout(() => {
+    window.onresize = function () {
+      instance.resize();
+    };
+  }, 1300);
 }
 
 export function showLimit(id, data) {
-    const option = {
-        legend: {
-          show: false,
-        },
-        grid: {
-            left: 30,
-            top: 20,
-            right: 10,
-            bottom: 20,
+  const option = {
+    legend: {
+      show: false,
+    },
+    grid: {
+      left: 30,
+      top: 20,
+      right: 10,
+      bottom: 20,
+    },
+    tooltip: {},
+    dataset: {
+      source: data,
+    },
+    xAxis: {
+      type: "category",
+      axisTick: {
+        show: false,
+      },
+      axisLabel: {
+        show: false,
+        color: "#666666",
+        fontSize: 10,
+        // interval: 0,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: "#888888",
+          width: 1,
         },
-        tooltip: {},
-        dataset: {
-            source: data,
+      },
+    },
+    yAxis: {
+      type: "value",
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: "#888888",
+          width: 1,
         },
-        xAxis: { 
-            type: 'category',
-            axisTick: {
-                show: false,
-            },
-            axisLabel: {
-                show: false,
-                color: '#666666',
-                fontSize: 10,
-                // interval: 0,
-            },
-            axisLine: {
-                show: true,
-                lineStyle: {
-                    color: '#888888',
-                    width: 1
-                }
-            }
+      },
+    },
+    series: [
+      {
+        type: "bar",
+        barWidth: 13,
+        itemStyle: {
+          color: "#B6CFFA",
+          borderRadius: [6, 6, 0, 0],
         },
-        yAxis: {
-            type: 'value',
-            axisLine: {
-                show: true,
-                lineStyle: {
-                    color: '#888888',
-                    width: 1
-                }
-            }
+      },
+      {
+        type: "bar",
+        barWidth: 13,
+        itemStyle: {
+          color: "#FF8162",
+          borderRadius: [6, 6, 0, 0],
         },
-        series: [
-            { 
-                type: 'bar',
-                barWidth: 13,
-                itemStyle: {
-                    color: '#B6CFFA',
-                    borderRadius: [6,6,0,0],
-                },
-            }, 
-            { 
-                type: 'bar',
-                barWidth: 13,
-                itemStyle: {
-                    color: '#FF8162',
-                    borderRadius: [6,6,0,0],
-                },
-            }
-        ],
+      },
+    ],
+  };
+
+  const e = document.getElementById(id);
+  let instance = echarts.getInstanceByDom(e);
+  if (instance == null) {
+    instance = echarts.init(e);
+  }
+  instance.setOption(option);
+  setTimeout(() => {
+    window.onresize = function () {
+      instance.resize();
     };
-    
-    const e = document.getElementById(id);
-    let instance = echarts.getInstanceByDom(e);
-    if (instance == null) {
-        instance = echarts.init(e);
-    }
-    instance.setOption(option);
-    setTimeout(() => {
-        window.onresize = function () {
-            instance.resize();
-        };
-    }, 1400);
+  }, 1400);
 }
 
 export function showFinish(id, data) {
-    const option = {
-        legend: {
-          show: false,
-        },
-        grid: {
-            left: 40,
-            top: 10,
-            right: 20,
-            bottom: 30,
+  const option = {
+    legend: {
+      show: false,
+    },
+    grid: {
+      left: 40,
+      top: 10,
+      right: 20,
+      bottom: 30,
+    },
+    tooltip: {},
+    xAxis: {
+      type: "category",
+      data: data.legendData,
+      axisTick: {
+        show: false,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: "#888888",
+          width: 1,
         },
-        tooltip: {},
-        xAxis: { 
-            type: 'category',
-            data: data.legendData,
-            axisTick: {
-                show: false,
-            },
-            axisLine: {
-                show: true,
-                lineStyle: {
-                    color: '#888888',
-                    width: 1
-                }
-            },
-            axisLabel: {
-                show: false,
-                color: '#666666',
-                fontSize: 10,
-                interval: 0,
-            },
+      },
+      axisLabel: {
+        show: false,
+        color: "#666666",
+        fontSize: 10,
+        interval: 0,
+      },
+    },
+    yAxis: {
+      type: "value",
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: "#888888",
+          width: 1,
         },
-        yAxis: {
-            type: 'value',
-            axisLine: {
-                show: true,
-                lineStyle: {
-                    color: '#888888',
-                    width: 1
-                }
-            }
+      },
+    },
+    series: [
+      {
+        type: "bar",
+        barWidth: 13,
+        itemStyle: {
+          color: "#64AFFF",
+          borderRadius: [6, 6, 0, 0],
         },
-        series: [
-            { 
-                type: 'bar',
-                barWidth: 13,
-                itemStyle: {
-                    color: '#64AFFF',
-                    borderRadius: [6,6,0,0],
-                },
-                data: data.seriesData,
-            },
-        ],
+        data: data.seriesData,
+      },
+    ],
+  };
+
+  const e = document.getElementById(id);
+  let instance = echarts.getInstanceByDom(e);
+  if (instance == null) {
+    instance = echarts.init(e);
+  }
+  instance.setOption(option);
+  setTimeout(() => {
+    window.onresize = function () {
+      instance.resize();
     };
-    
-    const e = document.getElementById(id);
-    let instance = echarts.getInstanceByDom(e);
-    if (instance == null) {
-        instance = echarts.init(e);
-    }
-    instance.setOption(option);
-    setTimeout(() => {
-        window.onresize = function () {
-            instance.resize();
-        };
-    }, 1500);
-}
+  }, 1500);
+}
+
+export function showStatistics(id, data) {
+  const option = {
+    tooltip: {
+      trigger: "axis",
+      axisPointer: {
+        type: "shadow",
+      },
+    },
+    grid: {
+      left: "3%",
+      right: "4%",
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: "value",
+      boundaryGap: [0, 0.01],
+      // data: data.yAxisData,
+    },
+    yAxis: {
+      type: "category",
+      data: data.yAxisData,
+    },
+    series: [
+      {
+        name: "Sale",
+        type: "bar",
+        backgroundStyle: {
+          borderRadius: [25, 25, 25, 25],
+        },
+        itemStyle: {
+          color: "#4086FF",
+          borderRadius: [9, 9, 9, 9],
+        },
+        data: data.seriesData,
+      },
+    ],
+  };
+
+  const e = document.getElementById(id);
+  let instance = echarts.getInstanceByDom(e);
+  if (instance == null) {
+    instance = echarts.init(e);
+  }
+  instance.setOption(option);
+  window.onresize = function () {
+    instance.resize();
+  };
+}

+ 8 - 2
src/views/login/Login.vue

@@ -115,8 +115,11 @@ export default {
                     });
                 }
                 else {
-                    this.loading = false
-                    this.$message.error(res.msg)
+                    this.loading = false;
+                    this.$message.error(res.msg);
+                    if (this.showCode) {
+                        this.getCodeImage();
+                    }
                 }
             }).catch(err => {
                 this.loading = false;
@@ -126,6 +129,9 @@ export default {
                     this.getCodeImage();
                     this.rules['random_code'] = this.random_code;
                 }
+                else if (this.showCode) {
+                    this.getCodeImage();
+                }
             });
         },
         submitForm() {

+ 2 - 4
src/views/monitor/check/CheckDetail.vue

@@ -23,10 +23,8 @@
                     </el-table-column>
                     <el-table-column prop="enforce_item" show-overflow-tooltip label="检查项">
                         <template slot-scope="scope">
-                            <span v-if="scope.row.handbook">
-                                {{ scope.row.handbook.fullName }}
-                            </span>
-                            <span v-else></span>
+                            <span v-if="scope.row.item_type==0">{{ scope.row.handbook && scope.row.handbook.fullName }}</span>
+                            <span v-else>{{scope.row.item_content}}</span>
                         </template>
                     </el-table-column>
                     <el-table-column prop="enforce_method" label="检查流程及方法" show-overflow-tooltip>

+ 11 - 9
src/views/monitor/check/component/AddNodeItem.vue

@@ -168,15 +168,17 @@ export default {
 
         this.$refs.itemForm.validate((valid) => {
           if (valid) {
-            const obj = Object.assign({}, this.formData)
-            obj.item_id = obj.id
-            obj.enforce_class = '其他',
-              obj.enforce_item = obj.item_content,
-              obj.enforce_method = ''
-            delete obj.id
-            const list = [obj]
-            this.$emit('addData', list)
-            this.resetFormData()
+            const obj = Object.assign({}, this.formData);
+            obj.item_id = obj.id;
+            obj.enforce_class = '其他';
+            obj.enforce_item = obj.item_content;
+            obj.enforce_method = '';
+            obj.item_name = obj.item_content;
+            obj.enforce_obj_class = '其他';
+            delete obj.id;
+            const list = [obj];
+            this.$emit('addData', list);
+            this.resetFormData();
           }
         })
       } else {

+ 4 - 8
src/views/monitor/check/component/NodeItemsTable.vue

@@ -17,18 +17,14 @@
             </el-table-column>
             <el-table-column prop="enforce_item" show-overflow-tooltip label="检查项">
                 <template slot-scope="scope">
-                            <span v-if="scope.row.handbook">
-                                {{ scope.row.handbook.fullName }}
-                            </span>
-                    <span v-else></span>
+                    <!-- <span>{{scope.row.item_type}}</span> -->
+                    <span v-if="scope.row.item_type == 0">{{ scope.row.handbook && scope.row.handbook.fullName }}</span>
+                    <span v-else>{{scope.row.item_content || scope.row.enforce_content}}</span>
                 </template>
             </el-table-column>
             <el-table-column prop="enforce_method" label="检查流程及方法" show-overflow-tooltip>
                 <template slot-scope="scope">
-                            <span v-if="scope.row.handbook">
-                                {{ scope.row.handbook.enforce_method }}
-                            </span>
-                    <span v-else></span>
+                    <span> {{ scope.row.handbook && scope.row.handbook.enforce_method }}</span>
                 </template>
             </el-table-column>
             <el-table-column prop="action" label="操作" align="center" width="270">

+ 4 - 2
src/views/monitor/limitApproval/Index.vue

@@ -2,7 +2,7 @@
     <div class="random">
         <select-option @query="handleQuery" @changeDialogFormVisible="changeDialogFormVisible"/>
         <data-table ref="table"  @approveStatus="approveStatus"/>
-        <el-dialog :visible.sync="approveVisible" v-if="approveData" title="审核" :width="'340px'">
+        <el-dialog :visible.sync="approveVisible" v-if="approveData" title="审核" :width="'500px'">
             <approve-status-v2 :data="approveData" @changeApproveVisible="changeApproveVisible"></approve-status-v2>
         </el-dialog>
     </div>
@@ -45,7 +45,9 @@
             changeApproveVisible() {
                 this.approveVisible = !this.approveVisible;
                 this.approveData = null;
-                this.resetForm();
+                if (this.$refs.table) {
+                    this.$refs.table.handleQuery();
+                }
             },
             resetForm() {
                 if (this.$refs.table) {

+ 1 - 2
src/views/monitor/limitApproval/component/DataTable.vue

@@ -118,7 +118,6 @@
             approveStatus(data) {
                 this.$emit("approveStatus", data)
             },
-
             resetForm(data) {
                 this.query.query = data;
                 this.query.page = 1;
@@ -133,7 +132,7 @@
                 this.handleQuery()
             },
             handleQuery() {
-                this.loading = true
+            this.loading = true
               this.query.sort = 'start_time desc';
                 if (!this.query.query) {
                     this.query.query = "enforce_result=='NOT_PASS';deadline_file_id!=null"

+ 1 - 1
src/views/random/random/RandomRuleSettingForm.vue

@@ -337,7 +337,7 @@ export default {
           for (let index in tagIds) {
             let tagId = tagIds[index]
             tasks.push(new Promise((resolve, reject) => {
-              statTag(tagId, {enforce_obj_type: type, show_recent: false}).then(res => {
+            statTag(tagId, {enforce_obj_type: type, show_recent: false,stat_date: this.timeForm.task_execute_time ? this.timeForm.task_execute_time : new Date()}).then(res => {
                 console.log(tagId, res)
                 enforce_obj_count[tagId + ''] = res.total
                 resolve(res)

+ 218 - 180
src/views/random/record/component/ResourceOption.vue

@@ -1,191 +1,229 @@
 <template>
-    <form-option title="旧检查记录">
-        <el-form :inline="true" :model="query" ref="queryForm" label-width="80px">
-            <el-form-item label="单位名称:" prop="unitName">
-                <el-input v-model="query.unitName" placeholder="请输入"></el-input>
-            </el-form-item>
-            <el-form-item label="检查人员:" prop="unitOwner">
-                <el-input v-model="query.unitOwner" placeholder="请输入"></el-input>
-            </el-form-item>
-            <el-form-item label="检查机构:" prop="org">
-                <el-cascader
-                  class="custom-cascader"
-                  v-model="query.org"
-                  :props="orgPropsNow"
-                  :options="orgOptions"
-                  @change="orgChanged">
-                </el-cascader>
-            </el-form-item>
-            <el-form-item label="检查结果:" prop="place_type">
-                <el-select v-model="query.place_type" clearable placeholder="请选择">
-                    <el-option value="" label="全部" />
-                    <el-option v-for="item in checkResult" :key="item.id" :value="item.id" :label="item.name" />
-                </el-select>
-            </el-form-item>
-            <el-button class="ml" icon="el-icon-refresh-right" @click="resetForm">重置</el-button>
-            <el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
-        </el-form>
-    </form-option>
+  <form-option title="旧检查记录">
+    <el-form :inline="true" :model="query" ref="queryForm" label-width="80px">
+      <el-form-item label="单位名称:" prop="unitName">
+        <el-input v-model="query.unitName" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="检查人员:" prop="unitOwner">
+        <el-input v-model="query.unitOwner" placeholder="请输入"></el-input>
+      </el-form-item>
+      <el-form-item label="检查机构:" prop="org">
+        <el-cascader
+          class="custom-cascader"
+          v-model="query.org"
+          :props="orgPropsNow"
+          :options="orgOptions"
+          @change="orgChanged"
+        ></el-cascader>
+      </el-form-item>
+      <el-form-item label="检查结果:" prop="place_type">
+        <el-select v-model="query.place_type" clearable placeholder="请选择">
+          <el-option value label="全部" />
+          <el-option
+            v-for="item in checkResult"
+            :key="item.id"
+            :value="item.id"
+            :label="item.name"
+          />
+        </el-select>
+      </el-form-item>
+      <el-button icon="el-icon-refresh-right" @click="resetForm">重置</el-button>
+      <el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
+      <el-upload
+        class="upload-btn"
+        :action="action"
+        multiple
+        :limit="5"
+        :on-error="handleUploadError"
+        :on-success="handleUploadSuccess"
+        ref="upload"
+        :on-progress="handleUploadProgress"
+      >
+        <el-button type="success" icon="el-icon-upload" :loading="loading">导入</el-button>
+      </el-upload>
+    </el-form>
+  </form-option>
 </template>
 
 <script>
-    import FormOption from '@/component/FormOption'
-    import {getPlaceTypes} from '@/api/resource'
-    import {getOrg, getUserByOrg} from '@/api/base'
-    import {resourceMixin} from '@/mixin/resource'
-    import { orgMixin } from '@/mixin/org';
-    import {getTags} from "@/api/random";
-    import { getStr } from '@/utils/tool';
+import FormOption from '@/component/FormOption'
+import { getPlaceTypes } from '@/api/resource'
+import { getOrg, getUserByOrg } from '@/api/base'
+import { resourceMixin } from '@/mixin/resource'
+import { orgMixin } from '@/mixin/org';
+import { getTags } from "@/api/random";
+import { getStr } from '@/utils/tool';
 
-    export default {
-        components: {
-            FormOption
+export default {
+  components: {
+    FormOption
+  },
+  mixins: [resourceMixin, orgMixin],
+  data() {
+    return {
+      action: `${process.env.VUE_APP_API}/v2/api/upload/old-job`,
+      loading: false,
+      query: {
+        unitName: '',
+        unitOwner: '',
+        unitSize: '',
+        org: '',
+        safe: '',
+        check: '',
+        place_type: '',
+        tag: '',
+      },
+      // orgOptions: [],
+      orgPropsNow: {
+        checkStrictly: true,
+        value: 'enforce_org_name',
+        label: 'enforce_org_name',
+        children: 'sub_orgs',
+      },
+      tagOptions: [],
+      tagProps: {
+        checkStrictly: true,
+        value: 'id',
+        label: 'tag_name',
+        children: 'sub_tags'
+      },
+      placeType: [],
+      checkResult: [
+        {
+          id: '合格',
+          name: '合格'
         },
-        mixins: [resourceMixin, orgMixin],
-        data() {
-            return {
-                query: {
-                    unitName: '',
-                    unitOwner: '',
-                    unitSize: '',
-                    org: '',
-                    safe: '',
-                    check: '',
-                    place_type:'',
-                    tag: '',
-                },
-                // orgOptions: [],
-                orgPropsNow: {
-                  checkStrictly: true,
-                  value: 'enforce_org_name',
-                  label: 'enforce_org_name',
-                  children: 'sub_orgs',
-                },
-                tagOptions: [],
-                tagProps: {
-                    checkStrictly: true,
-                    value: 'id',
-                    label: 'tag_name',
-                    children: 'sub_tags'
-                },
-                placeType: [],
-              checkResult: [
-                {
-                  id: '合格',
-                  name: '合格'
-                },
-                {
-                  id: '责令整改',
-                  name: '责令整改'
-                },
-                {
-                  id: '责令限期整改',
-                  name: '责令限期整改'
-                },
-                {
-                  id: '不检查(歇业)',
-                  name: '不检查(歇业)'
-                },
-                {
-                  id: '不检查(停业)',
-                  name: '不检查(停业)'
-                },
-                {
-                  id: '不检查(关门)',
-                  name: '不检查(关门)'
-                },
-                {
-                  id: '不检查(其他)',
-                  name: '不检查(其他)'
-                },
-              ]
-            }
+        {
+          id: '责令整改',
+          name: '责令整改'
         },
-        mounted() {
-            this.getPlaceTypesAll();
-            // this.getOrgs()
-            this.loadTags()
-          this.loadOrgTree()
+        {
+          id: '责令限期整改',
+          name: '责令限期整改'
         },
-        methods: {
-          orgChanged(value) {
-            console.log(value)
-            // this.query.org = data.length > 0 ? data[data.length - 1] : null
-            // this.orgUsers = []
-            // this.getOrgUsers()
-          },
-          getOrgUsers() {
-            if (this.formData.org) {
-              getUserByOrg(1, 10000, this.formData.org).then(res => {
-                if (res && res.status === 200 && res.data) {
-                  this.orgUsers = res.data
-                }
-              })
-            }
-            else {
-              this.$message.warning('请先选择执法机构')
-            }
-          },
-            async getPlaceTypesAll() {
-                const list = await getPlaceTypes();
-                if (list && list.length > 0) {
-                    this.placeType=list;
-                }
-            },
-            checkOrgLeaf(arr) {
-                if (arr && arr.length > 0) {
-                    const vm = this
-                    arr.map(item => {
-                        if (!item.children || item.children.length === 0) {
-                            item.children = undefined
-                        } else {
-                            vm.checkOrgLeaf(item.children)
-                        }
-                    })
-                }
-            },
-            // getOrgs() {
-            //     getOrg().then(res => {
-            //       console.log(res)
-            //         if (res.data) {
-            //             const data = res.data
-            //             this.checkOrgLeaf(data)
-            //             this.orgOptions = data
-            //         }
-            //     })
-            // },
-            loadTags() {
-                getTags({page: 1, size: 1000, query: 'level==1'}).then(res => {
-                    if (res) {
-                        this.tagOptions = res.data
-                    }
-                })
-            },
-            resetForm() {
-                this.$refs.queryForm.resetFields()
-                this.$emit('reset')
-            },
-            handleQuery() {
-                let res = [];
-                const name = getStr(this.query.unitName);
-                if (name) {
-                    res.push(`dwmc=="*${name}*"`);
-                }
-                const person = getStr(this.query.unitOwner);
-                if (person) {
-                    res.push(`(jcr_z==*${person}*,jcr_x==*${person}*)`);
-                }
-                if (this.query.org && this.query.org.length > 0 && this.query.org[0]) {
-                    const org = this.query.org[this.query.org.length - 1];
-                    res.push(`sjjg==${org}`);
-                }
-                const place_type = getStr(this.query.place_type)
-                if (place_type) {
-                  res.push(`jcjgzt==${place_type}`);
-                }
-                const query = res.join(';');
-                this.$emit('query', query, '');
-            }
+        {
+          id: '不检查(歇业)',
+          name: '不检查(歇业)'
+        },
+        {
+          id: '不检查(停业)',
+          name: '不检查(停业)'
+        },
+        {
+          id: '不检查(关门)',
+          name: '不检查(关门)'
+        },
+        {
+          id: '不检查(其他)',
+          name: '不检查(其他)'
+        },
+      ]
+    }
+  },
+  mounted() {
+    this.getPlaceTypesAll();
+    // this.getOrgs()
+    this.loadTags()
+    this.loadOrgTree()
+  },
+  methods: {
+    orgChanged(value) {
+      console.log(value)
+      // this.query.org = data.length > 0 ? data[data.length - 1] : null
+      // this.orgUsers = []
+      // this.getOrgUsers()
+    },
+    getOrgUsers() {
+      if (this.formData.org) {
+        getUserByOrg(1, 10000, this.formData.org).then(res => {
+          if (res && res.status === 200 && res.data) {
+            this.orgUsers = res.data
+          }
+        })
+      }
+      else {
+        this.$message.warning('请先选择执法机构')
+      }
+    },
+    async getPlaceTypesAll() {
+      const list = await getPlaceTypes();
+      if (list && list.length > 0) {
+        this.placeType = list;
+      }
+    },
+    checkOrgLeaf(arr) {
+      if (arr && arr.length > 0) {
+        const vm = this
+        arr.map(item => {
+          if (!item.children || item.children.length === 0) {
+            item.children = undefined
+          } else {
+            vm.checkOrgLeaf(item.children)
+          }
+        })
+      }
+    },
+    // getOrgs() {
+    //     getOrg().then(res => {
+    //       console.log(res)
+    //         if (res.data) {
+    //             const data = res.data
+    //             this.checkOrgLeaf(data)
+    //             this.orgOptions = data
+    //         }
+    //     })
+    // },
+    loadTags() {
+      getTags({ page: 1, size: 1000, query: 'level==1' }).then(res => {
+        if (res) {
+          this.tagOptions = res.data
         }
+      })
+    },
+    resetForm() {
+      this.$refs.queryForm.resetFields()
+      this.$emit('reset')
+    },
+    handleQuery() {
+      let res = [];
+      const name = getStr(this.query.unitName);
+      if (name) {
+        res.push(`dwmc=="*${name}*"`);
+      }
+      const person = getStr(this.query.unitOwner);
+      if (person) {
+        res.push(`(jcr_z==*${person}*,jcr_x==*${person}*)`);
+      }
+      if (this.query.org && this.query.org.length > 0 && this.query.org[0]) {
+        const org = this.query.org[this.query.org.length - 1];
+        res.push(`sjjg==${org}`);
+      }
+      const place_type = getStr(this.query.place_type)
+      if (place_type) {
+        res.push(`jcjgzt==${place_type}`);
+      }
+      const query = res.join(';');
+      this.$emit('query', query, '');
+    },
+    handleUploadError() {
+      this.loading = false
+      this.$message.error('文件上传失败,请检查文件格式');
+    },
+    handleUploadSuccess() {
+      this.loading = false
+      this.$message.success('文件上传成功!');
+      this.$refs.upload.clearFiles();
+    },
+    handleUploadProgress() {
+      this.loading = true
     }
+  }
+}
 </script>
+ 
+<style scoped>
+.upload-btn {
+  display: inline-block;
+  margin-left: 10px;
+}
+</style>

+ 8 - 16
src/views/resource/ResourceEdit.vue

@@ -72,6 +72,7 @@
         <el-col :span="10">
           <el-form-item label="所属辖区" prop="enforceOrgIds">
             <el-cascader
+              ref="cascader-org"
               v-model="formData.enforceOrgIds"
               :props="orgProps"
               :disabled="disabledEdit"
@@ -540,22 +541,12 @@ export default {
       if (!data) {
         return
       }
-      let list = this.orgOptions
-      for (let index = 0; index < data.length; index++) {
-        const item = data[index]
-        const res = list.filter(obj => item === obj.orgId)
-        if (res.length > 0) {
-          if (index < data.length - 1) {
-            list = res[0].children
-            continue
-          } else {
-            this.formData.enforce_org.enforce_org_id = res[0].orgId
-            this.formData.enforce_org.enforce_org_name = res[0].orgName
-            this.formData.enforce_org_id = res[0].orgId
-            break
-          }
-        }
-      }
+      const currData = this.$refs['cascader-org'].getCheckedNodes()[0].data
+      // this.formData.enforce_org.enforce_org_id = currData.enforce_org_id
+      // this.formData.enforce_org.enforce_org_name = currData.enforce_org_name
+      // 修改外层辖区
+      this.formData.enforce_org_id = currData.enforce_org_id
+      this.formData.enforce_org_name = currData.enforce_org_name
     },
     handlePlace (data) {
       if (data) {
@@ -651,6 +642,7 @@ export default {
           this.loading = true;
           info.hole_responsibility = this.formData.hole_responsibility ? 1 : 0;
           info.certificates = [this.formData.certificates];
+          console.log('info', info)
           if (vm.formData.id) {
             updateEnforce(vm.formData.id, info).then(() => {
               vm.$message.success('修改成功')

+ 8 - 7
src/views/resource/component/ResourceTable.vue

@@ -195,8 +195,7 @@
               pickerOptions: {
                 disabledDate(time) {
                   return time.getTime() < Date.now();
-                },
-                // selectableRange: `${(new Date()).getHours()}:${(new Date()).getMinutes()}:${(new Date()).getSeconds()}-24:00:00`
+                }
               }
             }
         },
@@ -206,7 +205,7 @@
         },
         methods: {
           blurTime(val) {
-            this.frozenTime = val.displayValue ? new Date(val.displayValue) : ''
+            this.frozenTime = val.displayValue ? moment(val.displayValue)._d : ''
             if (new Date(val.displayValue) < new Date().getTime()) {
               this.$message.error('冻结时间需要大于当前时间');
               this.$refs['date-picker'].focus()
@@ -216,8 +215,8 @@
           confirmModel() {
             let params = {
               manage_status: 1,
-              change_detail: this.frozenValue,
-              change_resonse: this.status_reason,
+              change_detail: this.status_reason,
+              change_resonse: this.frozenValue,
               blocked_end_time: this.frozenTime
             }
             if (this.manageType === 1) {
@@ -239,16 +238,18 @@
                 return
               }
               params.manage_status = 2
-              params.change_detail = ''
+              params.change_detail = this.status_reason
               params.blocked_end_time = ''
+              params.change_resonse = ''
             } else if (this.manageType === 0) {
               if (!this.status_reason) {
                 this.$message.error('请输入解冻原因');
                 return
               }
               params.manage_status = 0
-              params.change_detail = ''
+              params.change_detail = this.status_reason
               params.blocked_end_time = ''
+              params.change_resonse = ''
             }
             params = {...this.paramsData, ...params}
             updateEnforce(this.currentId, params).then(res => {

+ 546 - 0
src/views/statistics/data/Data.vue

@@ -0,0 +1,546 @@
+<template>
+    <div class="resource">
+        <select-option
+            @query="handleQuery"
+            v-bind:statisticsInfo="statisticsInfo"
+            v-bind:loading="loading"
+            v-bind:normal="normal"
+            v-bind:special="special"
+            v-bind:feedbak="feedbak"
+        />
+        <div class="wrap">
+            <div class="column">
+                <Card title="场所类型划分" width="420" height="245">
+                    <Progress :data="placeInfo.data" :maxNum="placeInfo.maxNum" ref="progress" />
+                </Card>
+                <Card title="隐患情况统计" width="420" height="244">
+                    <Progress :data="byItemfo.data" :maxNum="byItemfo.maxNum" ref="progress2" />
+                </Card>
+            </div>
+            <Card title="专项检查情况" class="card2">
+                <el-table
+                    :data="specialInfoData"
+                    style="width: 100%;"
+                    height="445"
+                    v-loading="loading"
+                    ref="table"
+                >
+                    <el-table-column
+                        prop="enforce_job_item_name"
+                        show-overflow-tooltip
+                        label="专业检查名称"
+                        align="center"
+                    />
+                    <el-table-column
+                        prop="enforce_obj_num"
+                        show-overflow-tooltip
+                        label="检查单位数"
+                        align="center"
+                    />
+                    <el-table-column prop="rate" show-overflow-tooltip label="完成率" align="center" />
+                </el-table>
+            </Card>
+            <div class="column">
+                <Card
+                    title="模版使用排行"
+                    width="420"
+                    height="269"
+                    v-bind:isMore="true"
+                    @more="handleMoreClick('template')"
+                >
+                    <el-table
+                        :data="templateInfData"
+                        style="width: 100%;"
+                        height="210"
+                        v-loading="loading"
+                        ref="table2"
+                    >
+                        <el-table-column
+                            type="index"
+                            show-overflow-tooltip
+                            label="序号"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="template_name"
+                            show-overflow-tooltip
+                            label="模版名称"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="create_org_name"
+                            show-overflow-tooltip
+                            label="创建机构"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="crate_user_name"
+                            show-overflow-tooltip
+                            label="创建人"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="use_num"
+                            show-overflow-tooltip
+                            label="使用次数"
+                            align="center"
+                        />
+                    </el-table>
+                </Card>
+                <Card
+                    title="检查规则贡献值"
+                    width="420"
+                    height="228"
+                    v-bind:isMore="true"
+                    @more="handleMoreClick('contribute')"
+                >
+                    <el-table
+                        :data="ontributeInfoData"
+                        style="width: 100%;"
+                        height="170"
+                        v-loading="loading"
+                    >
+                        <el-table-column
+                            type="index"
+                            show-overflow-tooltip
+                            label="序号"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="user_name"
+                            show-overflow-tooltip
+                            label="姓名"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="create_org_name"
+                            show-overflow-tooltip
+                            label="所属机构"
+                            align="center"
+                        />
+                        <el-table-column
+                            prop="feedbak_num"
+                            show-overflow-tooltip
+                            label="反馈次数"
+                            align="center"
+                        />
+                        <el-table-column show-overflow-tooltip label="操作" align="center">
+                            <template slot-scope="scope">
+                                <el-link
+                                    type="primary"
+                                    @click="handleOntributeDetail(scope.$index, scope.row)"
+                                >详情</el-link>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </Card>
+            </div>
+        </div>
+
+        <el-dialog
+            width="65%"
+            top="10vh"
+            v-bind:title="dialogDetailTable.title"
+            :visible.sync="dialogDetailTable.visible"
+            @close="handleDialogClose"
+        >
+            <detail-table
+                v-bind:total="dialogDetailTable.total"
+                v-bind:data="dialogDetailTable.data"
+                v-bind:loading="dialogTableLoading"
+                @nextPage="handleNextPage"
+                ref="table"
+            />
+        </el-dialog>
+
+        <el-dialog
+            width="65%"
+            top="10vh"
+            v-bind:title="dialogTemplateTable.title"
+            :visible.sync="dialogTemplateTable.visible"
+            @close="handleDialogClose"
+        >
+            <template-table
+                v-bind:data="dialogTemplateTable.data"
+                v-bind:loading="dialogTableLoading"
+                v-bind:total="dialogTemplateTable.total"
+                @nextPage="handleNextPage"
+                ref="table"
+            />
+        </el-dialog>
+
+        <el-dialog
+            width="65%"
+            top="10vh"
+            v-bind:title="dialogContributTable.title"
+            :visible.sync="dialogContributTable.visible"
+            @close="handleDialogClose"
+        >
+            <contribut-table
+                v-bind:data="dialogContributTable.data"
+                v-bind:loading="dialogTableLoading"
+                v-bind:total="dialogContributTable.total"
+                @nextPage="handleNextPage"
+                @lookDetail="handleOntributeDetail"
+                ref="table"
+            />
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import Card from './component/Card.vue'
+import SelectOption from './component/SelectOption.vue'
+import DetailTable from './component/DetailTable.vue'
+import TemplateTable from './component/TemplateTable.vue'
+import ContributTable from './component/ContributTable.vue'
+import Progress from './component/Progress.vue'
+import {
+    getStatisticInfo, getStatisticTotal, getStatisticFeedbak, getStatisticPlaceType, getStatisticByItem,
+    getStatisticSpecialInfo, getStatisticTemplateInfo, getStatisticContributeInfo, getStatisticContributeDetail
+} from '@/api/statistics'
+import { DateTime } from "luxon";
+
+export default {
+    components: {
+        SelectOption,
+        Card,
+        DetailTable,
+        TemplateTable,
+        ContributTable,
+        Progress,
+    },
+    data() {
+        return {
+            queryData: {},
+            loading: false,
+            statisticsInfo: {
+                enforce_obj_num: 0,
+                enforce_person_num: 0,
+                find_item_num: 0,
+                pass_enforce_obj_num: 0,
+                rectify_item_num: 0,
+                un_pass_enforce_obj_num: 0,
+            },
+            normal: {
+                total: 0,
+                done: 0
+            },
+            special: {
+                total: 0,
+                done: 0
+            },
+            feedbak: {
+                tempalte_num: 0,
+                contribute_num: 0
+            },
+            specialInfoData: [],
+            templateInfData: [],
+            ontributeInfoData: [],
+            dialogTableLoading: false,
+            contributDetailId: '',
+            dialogDetailTable: {
+                visible: false,
+                title: '',
+                total: 0,
+                data: []
+            },
+            dialogTemplateTable: {
+                visible: false,
+                title: '',
+                total: 0,
+                data: []
+            },
+            dialogContributTable: {
+                visible: false,
+                title: '',
+                total: 0,
+                data: []
+            },
+            timer: '',
+            timer2: '',
+            placeInfo: {
+                maxNum: 0,
+                data: []
+            },
+            byItemfo: {
+                maxNum: 0,
+                data: []
+            }
+        }
+    },
+    mounted() {
+        const end = new Date();
+        const start = new Date();
+        start.setMonth(start.getMonth() - 3);
+        this.handleQuery({
+            start_date: DateTime.fromJSDate(start).toFormat("yyyy-LL-dd"),
+            end_date: DateTime.fromJSDate(end).toFormat("yyyy-LL-dd"),
+            enforce_org_id: '',
+        })
+        this.timer = setInterval(this.autoTableScroll, 100)
+
+    },
+    methods: {
+        autoTableScroll() {
+            const table = this.$refs.table
+            const table2 = this.$refs.table2
+            const divData = table.bodyWrapper
+            const divData2 = table2.bodyWrapper
+            const progress = this.$refs.progress.$el
+            const progress2 = this.$refs.progress2.$el
+            progress.scrollTop += 3
+            if (progress.clientHeight + progress.scrollTop == progress.scrollHeight) {
+                progress.scrollTop = 0
+            }
+            progress2.scrollTop += 3
+            if (progress2.clientHeight + progress2.scrollTop == progress2.scrollHeight) {
+                progress2.scrollTop = 0
+            }
+            divData.scrollTop += 3
+            divData2.scrollTop += 3
+            if (divData.clientHeight + divData.scrollTop == divData.scrollHeight) {
+                divData.scrollTop = 0
+            }
+            if (divData2.clientHeight + divData2.scrollTop == divData2.scrollHeight) {
+                divData2.scrollTop = 0
+            }
+        },
+        resetForm() {
+            this.handleQuery()
+        },
+        handleQuery(data) {
+            console.log('data', data)
+            this.queryData = data
+            this.loading = true
+            Promise.all([getStatisticInfo({ ...data }),
+            getStatisticTotal({ ...data, job_type: 'NORMAL', }),
+            getStatisticTotal({ ...data, job_type: 'SPECIAL', }),
+            getStatisticFeedbak({ ...data }),
+            getStatisticPlaceType({ ...data }),
+            getStatisticByItem({ ...data }),
+            getStatisticSpecialInfo({ ...data }),
+            getStatisticTemplateInfo({
+                ...data,
+                page: 1,
+                size: 50,
+                sort: "create_time desc"
+            }),
+            getStatisticContributeInfo({
+                ...data,
+                page: 1,
+                size: 50,
+                sort: "create_time desc"
+            }),
+            ]).then(res => {
+                if (res[0].data) {
+                    this.statisticsInfo = res[0].data
+                }
+                if (res[1].data) {
+                    this.normal = res[1].data
+                }
+                if (res[2].data) {
+                    this.special = res[2].data
+                }
+                if (res[3].data) {
+                    this.feedbak = res[3].data
+                }
+                if (res[4].data) {
+                    this.placeInfo = {
+                        data: res[4].data,
+                        maxNum:
+                            Math.max.apply(Math, res[4].data.map(item => { return item.job_num }))
+                    }
+                }
+                if (res[5].data) {
+                    const newData = []
+                    res[5].data.map(item => {
+                        newData.push({
+                            job_num: item.enforce_job_item_num,
+                            type_name: item.enforce_job_item_name
+                        })
+                    })
+                    this.byItemfo = {
+                        data: newData,
+                        maxNum:
+                            Math.max.apply(Math, newData.map(item => { return item.job_num }))
+                    }
+                }
+                if (res[6].data) {
+                    const specialInfoDatas = []
+                    res[6].data.map(item => {
+                        specialInfoDatas.push({
+                            ...item,
+                            rate: (Math.round(Number(item.enforce_obj_done) / Number(item.enforce_obj_num) * 10000) / 100.00 + "%")
+                        })
+                    })
+                    this.specialInfoData = specialInfoDatas
+                }
+                if (res[7].data) {
+                    this.templateInfData = res[7].data
+                }
+                if (res[8].data) {
+                    this.ontributeInfoData = res[8].data
+                }
+            }).finally(() => this.loading = false)
+        },
+        handleOntributeDetail(index, data) {
+            this.contributDetailId = data.id
+            this.dialogDetailTable = {
+                visible: true,
+                data: [],
+                title: data.create_org_name,
+            }
+            this.getOntributeDetail({
+                page: 1,
+                size: 10,
+                sort: "create_time desc",
+                type: 'contribute',
+                id: data.id,
+                title: data.create_org_name
+            })
+        },
+        getOntributeDetail(params) {
+            const { page, size, sort, id, title } = params
+            this.dialogTableLoading = true
+            getStatisticContributeDetail(id, {
+                ...this.queryData,
+                page,
+                size,
+                sort,
+            }).then(res => {
+                if (res.data) {
+                    const data = res.data.map(item => {
+                        return {
+                            ...item,
+                            create_time: DateTime.fromJSDate(new Date(item.create_time)).toFormat("yyyy-LL-dd")
+
+                        }
+                    })
+                    this.dialogDetailTable = {
+                        ...this.dialogDetailTable,
+                        total: res.total,
+                        data,
+                    }
+                }
+            }).finally(() =>
+                this.dialogTableLoading = false
+            )
+        },
+        handleNextPage(params) {
+            const { type, page, size, sort } = params
+            if (type === 'contribute') {
+                this.dialogTableLoading = true
+                getStatisticContributeInfo({
+                    ...this.queryData,
+                    page,
+                    size,
+                    sort,
+                }).then(res => {
+                    if (res.data) {
+                        this.dialogContributTable = {
+                            visible: true,
+                            data: res.data,
+                            total: res.total,
+                            title: '检查规则贡献值'
+                        }
+                    }
+                }).finally(() =>
+                    this.dialogTableLoading = false
+                )
+            }
+            if (type === 'contributeDetail') {
+                this.getOntributeDetail({
+                    page,
+                    size,
+                    sort,
+                    type: 'contribute',
+                    id: this.contributDetailId,
+                })
+            }
+            if (type === 'template') {
+                this.dialogTableLoading = true
+                getStatisticTemplateInfo({
+                    ...this.queryData,
+                    page,
+                    size,
+                    sort,
+                }).then(res => {
+                    if (res.data) {
+                        this.dialogTemplateTable = {
+                            visible: true,
+                            data: res.data,
+                            total: res.total,
+                            title: '模版使用排行'
+                        }
+                    }
+                }).finally(() =>
+                    this.dialogTableLoading = false
+                )
+            }
+        },
+        handleMoreClick(type) {
+            if (type === 'contribute') {
+                this.dialogContributTable = {
+                    visible: true,
+                    data: [],
+                    total: 0,
+                    title: '检查规则贡献值'
+                }
+                this.handleNextPage({
+                    page: 1,
+                    size: 10,
+                    sort: "create_time desc",
+                    type: 'contribute'
+                })
+            }
+            if (type === 'template') {
+                this.dialogTemplateTable = {
+                    visible: true,
+                    data: [],
+                    total: 0,
+                    title: '模版使用排行'
+                }
+                this.handleNextPage({
+                    page: 1,
+                    size: 10,
+                    sort: "create_time desc",
+                    type: 'template'
+                })
+            }
+        },
+        handleDialogClose() {
+            this.$refs.table.$emit("close")
+        }
+    },
+    destroyed() {
+        clearInterval(this.timer)
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+    margin-top: 16px;
+    height: 505px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.column {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+}
+
+.card2 {
+    flex: 1;
+    min-width: 300px;
+    margin: 0 10px;
+}
+#place,
+#byItem {
+    margin-top: -60px;
+    margin-left: -10px;
+}
+</style>

+ 67 - 0
src/views/statistics/data/component/Card.vue

@@ -0,0 +1,67 @@
+<template>
+    <div
+        :class="{ 'card': true, [className]: className }"
+        :style="{ minWidth: width + 'px', height: height + 'px' }"
+    >
+        <div class="wrap">
+            <div class="header">
+                <div class="title">{{ title }}</div>
+                <el-link v-if="isMore" type="primary" @click="clickMore">查看更多</el-link>
+            </div>
+            <slot></slot>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        title: {
+            type: String
+        },
+        width: {
+            type: String
+        },
+        height: {
+            type: String
+        },
+        isMore: {
+            type: Boolean,
+            default: false
+        },
+        className: {
+            type: String
+        },
+    },
+    methods: {
+        clickMore() {
+            this.$emit('more')
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.card {
+    background: white;
+    border-radius: 10px;
+}
+
+.wrap {
+    padding: 12px 12px 0 12px;
+}
+.header {
+    margin-bottom: 15px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+.title {
+    font-size: 16px;
+    color: #1d2129;
+}
+.more {
+    font-size: 12px;
+    color: #1d68ff;
+}
+</style>

+ 95 - 0
src/views/statistics/data/component/ContributTable.vue

@@ -0,0 +1,95 @@
+<template>
+    <div>
+        <el-table :data="data" style="width: 100%;" height="500" v-loading="loading">
+            <el-table-column type="index" show-overflow-tooltip label="序号" align="center" />
+            <el-table-column prop="user_name" show-overflow-tooltip label="姓名" align="center" />
+            <el-table-column
+                prop="create_org_name"
+                show-overflow-tooltip
+                label="所属机构"
+                align="center"
+            />
+            <el-table-column prop="feedbak_num" show-overflow-tooltip label="反馈次数" align="center" />
+            <el-table-column show-overflow-tooltip label="操作" align="center">
+                <template slot-scope="scope">
+                    <el-link type="primary" @click="handleLookDetail(scope.$index, scope.row)">详情</el-link>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="custom-pagination">
+            <div class="info">
+                <span>共 {{ total }} 条记录</span>
+                <span>第 {{ query.page }} / {{ pageTotal }} 页</span>
+            </div>
+            <el-pagination
+                background
+                class="custom-page"
+                layout="prev, pager, next, sizes"
+                :page-sizes="[10, 20, 50, 100]"
+                :current-page="query.page"
+                :page-size="query.size"
+                :total="total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+            ></el-pagination>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        data: {
+            type: Array,
+        },
+        loading: {
+            type: Boolean
+        },
+        total: {
+            type: Number
+        }
+    },
+    computed: {
+        pageTotal() {
+            const page = Math.ceil(this.total / this.query.size)
+            return page > 0 ? page : 1
+        }
+    },
+    data() {
+        return {
+            query: {
+                page: 1,
+                size: 10,
+                sort: "create_time desc"
+            },
+        }
+    },
+    mounted() {
+        this.$nextTick(() => {
+            this.$on('close', () => {
+                this.query = {
+                    page: 1,
+                    size: 10,
+                    sort: "create_time desc"
+                }
+            });
+        });
+    },
+    methods: {
+        handleSizeChange(val) {
+            this.query.size = val
+            this.handleQuery()
+        },
+        handleCurrentChange(val) {
+            this.query.page = val
+            this.handleQuery()
+        },
+        handleQuery() {
+            this.$emit('nextPage', { ...this.query, type: 'contribute' })
+        },
+        handleLookDetail(index, row) {
+            this.$emit('lookDetail', index, row)
+        },
+    },
+}
+</script>

+ 96 - 0
src/views/statistics/data/component/DetailTable.vue

@@ -0,0 +1,96 @@
+<template>
+    <div>
+        <el-table :data="data" style="width: 100%;" height="500" v-loading="loading">
+            <el-table-column type="index" show-overflow-tooltip label="序号" align="center" />
+            <el-table-column prop="org_name" show-overflow-tooltip label="所属机构" align="center" />
+            <el-table-column
+                prop="comment_content"
+                show-overflow-tooltip
+                label="评论内容"
+                align="center"
+            />
+            <el-table-column prop="item_name" show-overflow-tooltip label="所属检查项" align="center" />
+            <el-table-column
+                prop="template_name"
+                show-overflow-tooltip
+                label="所属模版"
+                align="center"
+            />
+            <el-table-column prop="user_name" show-overflow-tooltip label="姓名" align="center" />
+            <el-table-column prop="phone_number" show-overflow-tooltip label="电话" align="center" />
+            <el-table-column prop="create_time" show-overflow-tooltip label="时间" align="center" />
+        </el-table>
+        <div class="custom-pagination">
+            <div class="info">
+                <span>共 {{ total }} 条记录</span>
+                <span>第 {{ query.page }} / {{ pageTotal }} 页</span>
+            </div>
+            <el-pagination
+                background
+                class="custom-page"
+                layout="prev, pager, next, sizes"
+                :page-sizes="[10, 20, 50, 100]"
+                :current-page="query.page"
+                :page-size="query.size"
+                :total="total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+            ></el-pagination>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        data: {
+            type: Array,
+        },
+        loading: {
+            type: Boolean
+        },
+        total: {
+            type: Number
+        }
+    },
+    computed: {
+        pageTotal() {
+            const page = Math.ceil(this.total / this.query.size)
+            return page > 0 ? page : 1
+        }
+    },
+    data() {
+        return {
+            query: {
+                page: 1,
+                size: 10,
+                sort: "create_time desc"
+            },
+        }
+    },
+    mounted() {
+        this.$nextTick(() => {
+            this.$on('close', () => {
+                this.query = {
+                    page: 1,
+                    size: 10,
+                    sort: "create_time desc"
+                }
+            });
+        });
+    },
+    methods: {
+        handleSizeChange(val) {
+            this.query.size = val
+            this.handleQuery()
+        },
+        handleCurrentChange(val) {
+            this.query.page = val
+            this.handleQuery()
+        },
+        handleQuery() {
+            this.$emit('nextPage', { ...this.query, type: 'contributeDetail' })
+        },
+    },
+}
+</script>

+ 61 - 0
src/views/statistics/data/component/Progress.vue

@@ -0,0 +1,61 @@
+<template>
+    <div class="items">
+        <div class="item" v-for="(item, index) in data" :key="index" ref="item">
+            <div class="title">{{ item.type_name }}</div>
+            <div
+                class="progress"
+                :style="{ width: Math.round(Number(item.job_num) / (Number(maxNum) / progressWidth)) + 'px' }"
+            />
+            <div>{{ item.job_num }}</div>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        data: {
+            type: Array,
+            require: true,
+        },
+        maxNum: {
+            type: Number,
+            require: true,
+        }
+    },
+    data() {
+        return {
+            progressWidth: 250,
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.items {
+    height: 180px;
+    overflow: auto;
+}
+.item {
+    margin-bottom: 25px;
+    display: flex;
+    align-items: center;
+    font-size: 13px;
+}
+
+.anim {
+    transition: all 0.8s;
+}
+
+.progress {
+    margin: 0 10px;
+    height: 12px;
+    background: #4086ff;
+    border-radius: 5px;
+}
+.title {
+    width: 23%;
+    color: #4e5969;
+    text-align: right;
+}
+</style>

+ 348 - 0
src/views/statistics/data/component/SelectOption.vue

@@ -0,0 +1,348 @@
+<template>
+    <form-option>
+        <el-form :inline="true" :model="formData" ref="form" label-width="90px">
+            <el-form-item label="时间选择:" prop="select_time">
+                <el-date-picker
+                    v-model="formData.select_time"
+                    unlink-panels
+                    type="monthrange"
+                    range-separator="至"
+                    start-placeholder="开始月份"
+                    end-placeholder="结束月份"
+                    :picker-options="pickerOptions"
+                ></el-date-picker>
+            </el-form-item>
+            <el-form-item label="消防机构:" prop="orgs">
+                <el-cascader
+                    class="custom-cascader"
+                    v-model="formData.orgs"
+                    :props="orgProps"
+                    :options="orgOptions"
+                    @change="handleOrgChanged"
+                ></el-cascader>
+            </el-form-item>
+            <el-button class="ml" icon="el-icon-refresh-right" @click="resetForm">重置</el-button>
+            <el-button
+                type="primary"
+                icon="el-icon-search"
+                @click="handleQuery"
+                :loading="loading"
+            >查询</el-button>
+        </el-form>
+
+        <div class="wrap">
+            <div class="info">
+                <div class="item" v-for="(item, index) in list" :key="index">
+                    <el-image class="item-icon" :src="item.icon"></el-image>
+                    <div class="column">
+                        <div class="row">
+                            <span class="row-name">{{ item.name }}</span>
+                        </div>
+                        <div class="row" :class="item.color">
+                            <span class="row-title">{{ statisticsInfo[item.mark] }}</span>
+                            <span class="row-num">个</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="cards">
+            <div class="card">
+                <div class="card-title">日常抽查</div>
+                <div class="card-wrap">
+                    <div class="card-column">
+                        <span class="row-num">
+                            {{ normal.total }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">日常抽查总数</span>
+                    </div>
+                    <div class="card-column">
+                        <span class="row-num2">
+                            {{ normal.done }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">已完成</span>
+                    </div>
+                </div>
+            </div>
+            <div class="card">
+                <div class="card-title">专项抽查</div>
+                <div class="card-wrap">
+                    <div class="card-column">
+                        <span class="row-num">
+                            {{ special.total }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">日常抽查总数</span>
+                    </div>
+                    <div class="card-column">
+                        <span class="row-num2">
+                            {{ special.done }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">已完成</span>
+                    </div>
+                    <div class="card-column">
+                        <span
+                            class="row-num3"
+                        >{{ special.done ? (Math.round(Number(special.done) / Number(special.total) * 10000) / 100.00 + "%") : '0%' }}</span>
+                        <span class="row-title">完成率</span>
+                    </div>
+                </div>
+            </div>
+            <div class="card">
+                <div class="card-title">系统反馈</div>
+                <div class="card-wrap">
+                    <div class="card-column">
+                        <span class="row-num">
+                            {{ feedbak.tempalte_num }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">模版总数量</span>
+                    </div>
+                    <div class="card-column">
+                        <span class="row-num2">
+                            {{ feedbak.contribute_num }}
+                            <span class="row-desc">个</span>
+                        </span>
+                        <span class="row-title">检查规则总数量</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </form-option>
+</template>
+
+<script>
+import FormOption from '@/component/FormOption'
+import { jobMixin } from '@/mixin/job'
+import { resourceMixin } from '@/mixin/resource'
+import { DateTime } from "luxon";
+import { orgMixin } from '@/mixin/org';
+
+export default {
+    components: {
+        FormOption,
+    },
+    mixins: [jobMixin, resourceMixin, orgMixin],
+    props: {
+        statisticsInfo: {
+            type: Object
+        },
+        loading: {
+            type: Boolean
+        },
+        normal: {
+            type: Object
+        },
+        special: {
+            type: Object
+        },
+        feedbak: {
+            type: Object
+        },
+    },
+    data() {
+        return {
+            list: [
+                {
+                    id: '10',
+                    name: '全部单位',
+                    mark: 'enforce_obj_num',
+                    icon: require('@/assets/statisitcs/menu1.png'),
+                },
+                {
+                    id: "20",
+                    name: '执法人员',
+                    mark: 'enforce_person_num',
+                    icon: require('@/assets/statisitcs/menu2.png'),
+                },
+                {
+                    id: '30',
+                    name: '单位不合格单位数',
+                    mark: 'un_pass_enforce_obj_num',
+                    icon: require('@/assets/statisitcs/menu3.png'),
+                },
+                {
+                    id: '40',
+                    name: '单位合格数',
+                    mark: 'pass_enforce_obj_num',
+                    icon: require('@/assets/statisitcs/menu4.png'),
+                },
+                {
+                    id: '40',
+                    name: '发现隐患数',
+                    mark: 'find_item_num',
+                    icon: require('@/assets/statisitcs/menu5.png'),
+                },
+                {
+                    id: '40',
+                    name: '整改隐患数',
+                    mark: 'rectify_item_num',
+                    icon: require('@/assets/statisitcs/menu6.png'),
+                }
+            ],
+            pickerOptions: {
+                shortcuts: [{
+                    text: '本月',
+                    onClick(picker) {
+                        picker.$emit('pick', [new Date(), new Date()]);
+                    }
+                }, {
+                    text: '今年至今',
+                    onClick(picker) {
+                        const end = new Date();
+                        const start = new Date(new Date().getFullYear(), 0);
+                        picker.$emit('pick', [start, end]);
+                    }
+                }, {
+                    text: '最近六个月',
+                    onClick(picker) {
+                        const end = new Date();
+                        const start = new Date();
+                        start.setMonth(start.getMonth() - 6);
+                        picker.$emit('pick', [start, end]);
+                    }
+                }]
+            },
+            formData: {
+                select_time: [new Date(new Date().setMonth(new Date().getMonth() - 3)), new Date()],
+                orgs: '',
+                org: '',
+            },
+        }
+    },
+    mounted() {
+        this.loadOrgTree()
+    },
+    methods: {
+        resetForm() {
+            this.$refs.form.resetFields()
+            this.$parent.resetForm()
+        },
+        handleOrgChanged(data) {
+            this.formData.org = data.length > 0 ? data[data.length - 1] : null
+        },
+        handleQuery() {
+            const orgs = this.formData.orgs
+            let start_date = ''
+            let end_date = ''
+            let enforce_org_id = ''
+            if (this.formData.select_time && this.formData.select_time.length === 2) {
+                start_date = DateTime.fromJSDate(this.formData.select_time[0]).toFormat("yyyy-LL-dd")
+                end_date = DateTime.fromJSDate(this.formData.select_time[1]).toFormat("yyyy-LL-dd")
+            }
+            if (orgs && orgs[orgs.length - 1] != null && orgs[orgs.length - 1] != '') {
+                enforce_org_id = orgs[orgs.length - 1];
+            }
+            this.$emit('query', {
+                start_date,
+                end_date,
+                enforce_org_id,
+            })
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+    padding: 0 15px;
+    .info {
+        width: 100%;
+        padding: 24px 0;
+        display: flex;
+        justify-content: space-between;
+        border-top: 1px solid #f2f3f5;
+        border-bottom: 1px solid #f2f3f5;
+    }
+    .item {
+        display: flex;
+    }
+    .item-icon {
+        width: 54px;
+        height: 54px;
+        margin-right: 12px;
+    }
+    .row-name {
+        font-size: 12px;
+        color: #1d2129;
+    }
+    .row-title {
+        font-size: 24px;
+        color: #1d2129;
+    }
+
+    .row-num {
+        margin-left: 10px;
+        font-size: 12px;
+        color: #4e5969;
+    }
+}
+
+.cards {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 24px;
+    margin-bottom: 25px;
+    padding: 0 30px;
+    .card {
+        display: flex;
+        flex-direction: column;
+        width: 311px;
+        height: 144px;
+        border: 1px solid #f2f3f5;
+        border-radius: 7px;
+        &:nth-child(2) {
+            .card-title {
+                background: #dccfff;
+            }
+        }
+        &:nth-child(3) {
+            .card-title {
+                background: #cfdfff;
+            }
+        }
+    }
+    .card-title {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        height: 39px;
+        font-size: 18px;
+        color: #2a2862;
+        background: #cfdfff;
+    }
+    .card-wrap {
+        flex: 1;
+        display: flex;
+        justify-content: space-around;
+        align-items: center;
+    }
+    .card-column {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    }
+
+    .row-num {
+        font-size: 22px;
+        color: #1d2129;
+    }
+    .row-title {
+        margin-top: 5px;
+        font-size: 12px;
+        color: #1d2129;
+    }
+    .row-desc {
+        margin-left: 10px;
+        color: #4e5969;
+        font-size: 12px;
+    }
+    .row-num2 {
+        font-size: 22px;
+        color: #32b757;
+    }
+}
+</style>

+ 98 - 0
src/views/statistics/data/component/TemplateTable.vue

@@ -0,0 +1,98 @@
+<template>
+    <div>
+        <el-table :data="data" style="width: 100%;" height="500" v-loading="loading">
+            <el-table-column type="index" show-overflow-tooltip label="序号" align="center" />
+            <el-table-column
+                prop="template_name"
+                show-overflow-tooltip
+                label="模版名称"
+                align="center"
+            />
+            <el-table-column
+                prop="create_org_name"
+                show-overflow-tooltip
+                label="创建机构"
+                align="center"
+            />
+            <el-table-column
+                prop="crate_user_name"
+                show-overflow-tooltip
+                label="创建人"
+                align="center"
+            />
+            <el-table-column prop="use_num" show-overflow-tooltip label="使用次数" align="center" />
+        </el-table>
+        <div class="custom-pagination">
+            <div class="info">
+                <span>共 {{ total }} 条记录</span>
+                <span>第 {{ query.page }} / {{ pageTotal }} 页</span>
+            </div>
+            <el-pagination
+                background
+                class="custom-page"
+                layout="prev, pager, next, sizes"
+                :page-sizes="[10, 20, 50, 100]"
+                :current-page="query.page"
+                :page-size="query.size"
+                :total="total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+            ></el-pagination>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        data: {
+            type: Array,
+        },
+        loading: {
+            type: Boolean
+        },
+        total: {
+            type: Number
+        }
+    },
+    computed: {
+        pageTotal() {
+            const page = Math.ceil(this.total / this.query.size)
+            return page > 0 ? page : 1
+        }
+    },
+    data() {
+        return {
+            query: {
+                page: 1,
+                size: 10,
+                sort: "create_time desc"
+            },
+        }
+    },
+    mounted() {
+        this.$nextTick(() => {
+            this.$on('close', () => {
+                this.query = {
+                    page: 1,
+                    size: 10,
+                    sort: "create_time desc"
+                }
+            });
+        });
+    },
+    methods: {
+        handleSizeChange(val) {
+            this.query.size = val
+            this.handleQuery()
+        },
+        handleCurrentChange(val) {
+            this.query.page = val
+            this.handleQuery()
+        },
+        handleQuery() {
+            this.$emit('nextPage', { ...this.query, type: 'template' })
+        },
+    },
+}
+</script>