6 Revize 1b52545823 ... 53e8f4451f

Autor SHA1 Zpráva Datum
  littleblue55 53e8f4451f 修改正态分布部分,解决精确度问题 před 4 týdny
  littleblue55 586b559504 修改获取预警的markArea před 4 týdny
  littleblue55 5140750738 新增招商 před 4 týdny
  littleblue55 d45f3fcaba 首页修改图表的lagend,增加企业效益监测评估 před 4 týdny
  littleblue55 ab5c1aebf7 修改企业运行数据的表单 před 4 týdny
  littleblue55 36c1d44ae2 增加获取全部列表的接口和decimal.js před 4 týdny

+ 2 - 1
package.json

@@ -38,9 +38,10 @@
   },
   "dependencies": {
     "@riophae/vue-treeselect": "0.4.0",
-    "axios": "0.28.1",
+    "axios": "^0.28.1",
     "clipboard": "2.0.8",
     "core-js": "3.37.1",
+    "decimal.js": "^10.4.3",
     "echarts": "5.4.0",
     "echarts-stat": "^1.2.0",
     "element-ui": "2.15.14",

+ 10 - 0
src/api/benefit.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 获取柱状图数据
+export function benefitList(data) {
+    return request({
+        url: '/benefit/benefit/list',
+        method: 'post',
+        data: data
+    })
+}

+ 7 - 0
src/api/enterprise/enterprise.js

@@ -42,3 +42,10 @@ export function delEnterprise(id) {
     method: 'delete'
   })
 }
+
+export function listAllEnterprise() {
+  return request({
+    url: '/enterprise/enterprise/alllist',
+    method: 'get'
+  })
+}

+ 7 - 0
src/api/industry/industry.js

@@ -42,3 +42,10 @@ export function delIndustry(id) {
     method: 'delete'
   })
 }
+
+export function listAllIndustry() {
+  return request({
+    url: '/industry/industry/alllist',
+    method: 'get'
+  })
+}

+ 9 - 0
src/api/industry_run/industry_run.js

@@ -42,3 +42,12 @@ export function delIndustry_run(enterpriseName) {
     method: 'delete'
   })
 }
+
+// 查询industry_run全部列表
+export function listAllIndustry_run(query) {
+  return request({
+    url: '/industry_run/industry_run/alllist',
+    method: 'get',
+    params: query
+  })
+}

+ 0 - 1
src/main.js

@@ -37,7 +37,6 @@ import DictTag from '@/components/DictTag'
 import VueMeta from 'vue-meta'
 // 字典数据组件
 import DictData from '@/components/DictData'
-
 // 全局方法挂载
 Vue.prototype.getDicts = getDicts
 Vue.prototype.getConfigKey = getConfigKey

+ 347 - 0
src/views/benefit/index.vue

@@ -0,0 +1,347 @@
+<template>
+  <!-- littlegreen - 企业效益监测评估 - 差对接查询接口 -->
+  <div :class="className" :style="{ width: width, padding: '20px' }">
+    <div class="select-container">
+      <el-form
+        style="
+          display: grid;
+          grid-template-columns: 1fr 1.3fr 1fr;
+          align-items: center;
+          grid-gap: 20px;
+        "
+        label-width="100px"
+      >
+        <el-form-item label="行业代码" style="margin-bottom: 0">
+          <el-select
+            v-model="form.code"
+            style="width: 100%"
+            filterable
+            @change="codeChange"
+          >
+            <el-option
+              v-for="item in selectedIndustryArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="企业" style="margin-bottom: 0">
+          <el-select
+            v-model="form.enterpriseName"
+            style="width: 100%"
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="item in selectedEnterpriseArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="年度" style="margin-bottom: 0">
+          <el-select v-model="form.year" style="width: 100%">
+            <el-option
+              v-for="item in selectedYearArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="季度" style="margin-bottom: 0">
+          <el-select v-model="form.season" style="width: 100%" clearable>
+            <el-option
+              v-for="item in selectedSeasonArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="分析方式" style="margin-bottom: 0">
+          <el-select v-model="form.mode" style="width: 100%">
+            <el-option
+              v-for="item in selectedAnalyseArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item style="margin-bottom: 0">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            size="mini"
+            @click="submit"
+            >搜索</el-button
+          ><el-button icon="el-icon-refresh" size="mini" @click="resetForm"
+            >重置</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+    <el-table
+      :data="paginatedData"
+      border
+      style="width: 100%; margin-top: 30px"
+    >
+      <el-table-column
+        prop="enterpriseName"
+        label="企业名称"
+        width="200"
+        sortable
+      ></el-table-column>
+      <el-table-column
+        prop="code"
+        label="所属行业"
+        width="100"
+      ></el-table-column>
+      <el-table-column prop="year" label="年度" width="100"></el-table-column>
+      <el-table-column
+        prop="season"
+        label="季度"
+        width="100"
+        sortable
+      ></el-table-column>
+      <el-table-column
+        v-if="mode == 'trendAnalysis'"
+        prop="trendAnalysis"
+        label="趋势分析情况"
+        width="300"
+      ></el-table-column>
+      <el-table-column
+        v-if="mode == 'avgAnalysis'"
+        prop="avgAnalysis"
+        label="平均水平分析情况"
+        width="350"
+      ></el-table-column>
+      <el-table-column
+        prop="operationalEvaluate"
+        label="企业运营评估结果"
+      ></el-table-column>
+    </el-table>
+    <el-pagination
+      @current-change="handlePageChange"
+      :current-page="currentPage"
+      :page-size="pageSize"
+      :total="totalItems"
+      hide-on-single-page
+      background
+      layout="total, prev, pager, next, jumper"
+      style="float: right; margin-top: 20px; margin-bottom: 20px"
+    >
+    </el-pagination>
+    <!-- <div ref="chart" :style="{ height: height, width: width }"></div> -->
+  </div>
+</template>
+
+<script>
+import { listAllEnterprise } from "@/api/enterprise/enterprise";
+import { listAllIndustry } from "@/api/industry/industry";
+import { getIndexData } from "@/api/home";
+import { benefitList } from "@/api/benefit";
+export default {
+  props: {
+    className: {
+      type: String,
+      default: "chart",
+    },
+    width: {
+      type: String,
+      default: "100%",
+    },
+    height: {
+      type: String,
+      default: "400px",
+    },
+  },
+  data() {
+    return {
+      selectedEnterpriseArray: [],
+      EnterpriseArray: [],
+      selectedYearArray: [],
+      selectedIndustryArray: [],
+      mode: "trendAnalysis",
+      currentPage: 1,
+      pageSize: 5,
+      form: {
+        enterpriseName: null,
+        year: null,
+        code: null,
+        season: "1",
+        mode: "trendAnalysis",
+      },
+      selectedAnalyseArray: [
+        {
+          key: "trendAnalysis",
+          value: "趋势分析",
+        },
+        {
+          key: "avgAnalysis",
+          value: "平均水平分析",
+        },
+      ],
+      selectedSeasonArray: [
+        {
+          key: "1",
+          value: "1季度",
+        },
+        {
+          key: "2",
+          value: "2季度",
+        },
+        {
+          key: "3",
+          value: "3季度",
+        },
+        {
+          key: "4",
+          value: "4季度",
+        },
+      ],
+      tableData: [],
+    };
+  },
+  computed: {
+    totalItems() {
+      return this.tableData.length;
+    },
+    paginatedData() {
+      const start = (this.currentPage - 1) * this.pageSize;
+      return this.tableData.slice(start, start + this.pageSize);
+    },
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+    handlePageChange(newPage) {
+      this.currentPage = newPage;
+    },
+    async init() {
+      const that = this;
+      // 获取所有的行业
+      await listAllIndustry()
+        .then((res) => {
+          if (res && res?.rows) {
+            that.selectedIndustryArray = res.rows.map((item) => {
+              return {
+                key: item.code,
+                value: item.code + item.industryName,
+              };
+            });
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+      // 获取所有的企业
+      await listAllEnterprise()
+        .then((res) => {
+          if (res && res?.rows) {
+            that.EnterpriseArray = res.rows.map((item) => {
+              return {
+                key: item.id,
+                value: item.enterpriseName,
+                code: item.code,
+              };
+            });
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+      // 获取有的年份
+      await getIndexData({
+        year: 0,
+      })
+        .then((res) => {
+          if (res && res?.rows) {
+            that.selectedYearArray = res.rows[0].years.map((v) => {
+              return {
+                key: v,
+                value: v,
+              };
+            });
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+      // 第一次的时候初始化from
+      this.resetForm();
+    },
+    submit() {
+      benefitList(this.form)
+        .then((res) => {
+          if (res && res?.rows) {
+            this.mode = this.form.mode;
+            this.tableData = res.rows;
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+    },
+    resetForm() {
+      // 过滤对应行业的的企业出来
+      this.selectedEnterpriseArray = this.EnterpriseArray.filter(
+        (item) => item.code == this.selectedIndustryArray[0].key
+      );
+      // 重置form表单
+      this.form = {
+        year: this.selectedYearArray[0].key,
+        enterpriseName: null,
+        code: this.selectedIndustryArray[0].key,
+        season: null,
+        mode: this.selectedAnalyseArray[0].key,
+      };
+      this.submit();
+    },
+    codeChange(e) {
+      // 当选择的行业变化时,过滤对应行业的的企业出来
+      this.selectedEnterpriseArray = this.EnterpriseArray.filter(
+        (item) => item.code == e
+      );
+      this.form.enterpriseName = null;
+    },
+  },
+};
+</script>
+
+<style scoped>
+.select-container {
+  margin: 10px 5px;
+  font-size: 16px;
+}
+
+.select-container select {
+  padding: 10px 20px;
+  margin-right: 10px;
+  border: 1px solid #121315;
+  border-radius: 4px;
+  background-color: white;
+  color: #121315;
+  font-size: 16px;
+  cursor: pointer;
+  outline: none;
+}
+
+.select-container select:hover {
+  border-color: #1a7cc8;
+}
+
+.select-container select:focus {
+  border-color: #121315;
+  box-shadow: 0 0 0 2px rgba(64, 159, 255, 0.2);
+}
+</style>

+ 26 - 10
src/views/dashboard/sandianpredict.vue

@@ -65,8 +65,12 @@
             {{ selectedModel }}
           </div>
         </el-form-item>
-        <el-form-item  style="margin-bottom: 0">
-          <el-button type="primary" icon="el-icon-search" @click="updateChart" size="mini"
+        <el-form-item style="margin-bottom: 0">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            @click="updateChart"
+            size="mini"
             >搜索</el-button
           >
         </el-form-item>
@@ -82,7 +86,7 @@ import { listPredict } from "@/api/predict/predict";
 import { listBase_data_month } from "@/api/base_data_month/base_data_month";
 require("echarts/theme/macarons");
 import resize from "./mixins/resize";
-
+import { Decimal } from "decimal.js";
 export default {
   mixins: [resize],
   props: {
@@ -350,23 +354,24 @@ export default {
           ]);
           break;
       }
-      // littlegreen - 获取预警的markArae
+      // littlegreen - 获取预警的markArea
       function getErrorArray(flag, yValues, monthYValues, xValues) {
         if (flag == 0) {
           return [];
         }
         const errorArray = [];
+        const error = new Decimal(that.errorVal);
         yValues.forEach((yValue) => {
-          const yVal = yValue[0];
+          const yVal = new Decimal(yValue[0]);
           const month = yValue[1];
           // 在 base_data_monthYValues 中找到相应的月
           const baseData = monthYValues.find((base) => base[1] === month);
           if (baseData) {
-            const baseVal = baseData[0];
+            const baseVal = new Decimal(baseData[0]);
             // 计算差值
-            const difference = Math.abs(baseVal - yVal);
+            const difference = yVal.minus(baseVal);
             // 如果差值大于10,添加该月到新数组
-            if (difference > that.errorVal) {
+            if (difference.gt(error)) {
               // 10-90 80分成xValues.length份
               errorArray.push([
                 {
@@ -388,7 +393,18 @@ export default {
         });
         return errorArray;
       }
-
+      // console.log(yValues, base_data_monthYValues);
+      // base_data_monthYValues = [
+      //   [1096, "2"],
+      //   [563, "3"],
+      //   [1053, "4"],
+      //   [737, "5"],
+      //   [1043, "6"],
+      //   [980, "7"],
+      //   [1428, "8"],
+      //   [1098, "9"],
+      // ];
+      // yValues = [[823.04, "10"],[1000, "9"]];
       this.chart.setOption(
         {
           legend: {
@@ -443,8 +459,8 @@ export default {
                 },
                 data: getErrorArray(
                   flag,
-                  yValues,
                   base_data_monthYValues,
+                  yValues,
                   xValues
                 ),
               },

+ 10 - 8
src/views/dashboard/sandianpredictseason.vue

@@ -82,6 +82,7 @@ import { listPredict } from "@/api/predict_season/predict_season";
 import { listBase_data_season } from "@/api/base_data_season/base_data_season";
 require("echarts/theme/macarons");
 import resize from "./mixins/resize";
+import { Decimal } from "decimal.js";
 
 export default {
   mixins: [resize],
@@ -214,7 +215,7 @@ export default {
           // 获取实际数据
           listBase_data_season(this.queryParams).then((response) => {
             this.base_data_seasonData = response.rows;
-            console.log("vvv" + this.base_data_seasonData.length);
+            // console.log("vvv" + this.base_data_seasonData.length);
             this.initChart();
           });
         })
@@ -332,24 +333,25 @@ export default {
           ]);
           break;
       }
-      // littlegreen - 获取预警的markArae
+      // littlegreen - 获取预警的markArea
       function getErrorArray(flag, yValues, monthYValues, xValues) {
         if (flag == 0) {
           return [];
         }
         const errorArray = [];
+        const error = new Decimal(that.errorVal);
+        // console.log(yValues, monthYValues,"实际和预测")
         yValues.forEach((yValue) => {
-          const yVal = yValue[0];
+          const yVal = new Decimal(yValue[0]);
           const month = yValue[1];
-          console.log(yVal,month)
           // 在 base_data_monthYValues 中找到相应的月
           const baseData = monthYValues.find((base) => base[1] === month);
           if (baseData) {
-            const baseVal = baseData[0];
+            const baseVal = new Decimal(baseData[0]);
             // 计算差值
-            const difference = Math.abs(baseVal - yVal);
+            const difference = yVal.minus(baseVal);
             // 如果差值大于10,添加该月到新数组
-            if (difference > that.errorVal) {
+            if (difference.gt(error)) {
               // 10-90 80分成xValues.length份
               errorArray.push([
                 {
@@ -447,8 +449,8 @@ export default {
               },
               data: getErrorArray(
                 flag,
-                yValues,
                 base_data_seasonYValues,
+                yValues,
                 xValues
               ),
             },

+ 23 - 17
src/views/dashboard/sandianpredictyear.vue

@@ -65,9 +65,14 @@
             {{ selectedModel }}
           </div>
         </el-form-item>
-        <el-form-item  style="margin-bottom: 0">
-          <el-button type="primary" icon="el-icon-search" @click="updateChart" size="mini"
-            >搜索</el-button>
+        <el-form-item style="margin-bottom: 0">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            @click="updateChart"
+            size="mini"
+            >搜索</el-button
+          >
         </el-form-item>
       </el-form>
     </div>
@@ -82,7 +87,7 @@ import { listBase_data_year } from "@/api/base_data_year/base_data_year";
 require("echarts/theme/macarons");
 import resize from "./mixins/resize";
 import basicInfoFormVue from '../tool/gen/basicInfoForm.vue';
-
+import { Decimal } from "decimal.js";
 export default {
   mixins: [resize],
   props: {
@@ -343,23 +348,24 @@ export default {
           break;
       }
 
-      // littlegreen - 获取预警的markArae
-      function getErrorArray(flag, yValues, monthYValues, xValues) {
+      // littlegreen - 获取预警的markArea
+      function getErrorArray(flag, monthYValues, yValues, xValues) {
+        // console.log(yValues, monthYValues,flag)
         if (flag == 0) {
           return [];
         }
         const errorArray = [];
-        yValues.forEach((yValue) => {
-          const yVal = yValue[0];
-          const month = yValue[1];
-          // 在 base_data_monthYValues 中找到相应的月
-          const baseData = monthYValues.find((base) => base[1] === month);
+        const error = new Decimal(that.errorVal);
+        monthYValues.forEach(monthYValue => {
+          const yVal = new Decimal(monthYValue[0]);
+          const month = monthYValue[1];
+          const baseData = yValues.find((base) => base[1] === month);
           if (baseData) {
-            const baseVal = baseData[0];
-            // 计算差值
-            const difference = Math.abs(baseVal - yVal);
-            // 如果差值大于10,添加该月到新数组
-            if (difference > that.errorVal) {
+            const baseVal = new Decimal(baseData[0]);
+            // console.log(yVal,baseVal)
+            const difference = yVal.minus(baseVal);
+            // console.log(difference.toNumber(),error)
+            if (difference.gt(error)) {
               errorArray.push([
                 {
                   x: 10 + (80 / (xValues.length)) * (xValues.indexOf(month+"年")) + "%",
@@ -456,7 +462,7 @@ export default {
               itemStyle: {
                 color: "rgba(255, 173, 177, 0.4)",
               },
-              data: getErrorArray(flag, yValues, base_data_yearYValues, xValues),
+              data: getErrorArray(flag, base_data_yearYValues, yValues, xValues),
             },
             // itemStyle: {
             //   // color: "rgba(245,0,0,0.6)",

+ 162 - 177
src/views/dashboard/sandianscore.vue

@@ -49,8 +49,8 @@
         </el-form-item>
         <el-form-item label="当前行业:" style="margin-bottom: 0">
           <div style="display: inline; padding-left: 10px">
-          {{ industryMap.get(selectedCode) }}
-        </div>
+            {{ industryMap.get(selectedCode) }}
+          </div>
         </el-form-item>
         <!-- <el-form-item label="数据分布区间"  style="margin-bottom:0"
           ><el-input
@@ -59,10 +59,15 @@
             clearable
             style="width: 240px"
         /></el-form-item> -->
-        
+
         <el-form-item style="margin-bottom: 0">
-          <el-button type="primary" icon="el-icon-search" @click="updateChart"  size="mini"
-            >搜索</el-button>
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            @click="updateChart"
+            size="mini"
+            >搜索</el-button
+          >
         </el-form-item>
       </el-form>
     </div>
@@ -95,6 +100,7 @@ import { listScore } from "@/api/score/score"; // 导入得分数据的接口
 import { listIndustry } from "@/api/industry/industry"; // 导入行业数据的接口
 require("echarts/theme/macarons"); // echarts theme
 import resize from "./mixins/resize";
+import { Decimal } from "decimal.js";
 
 export default {
   mixins: [resize],
@@ -235,7 +241,6 @@ export default {
       listIndustry(this.industryQueryParams)
         .then((response) => {
           this.industryData = response.rows;
-          // console.log("industry_info" + this.industryData);
           this.industryMap = this.industryData.reduce((map, item) => {
             const key = `${item.code}`; // 键是行业代码
             map.set(key, item.industryName); // 将键和对应的行业名称存储到Map中
@@ -262,7 +267,6 @@ export default {
       if (flag == 2) {
         return;
       }
-      // console.log(flag);
       let filteredData = this.chartData;
       if (this.selectedYear) {
         filteredData = filteredData.filter(
@@ -271,18 +275,8 @@ export default {
         );
       }
       const values = filteredData.map((item) => item[this.selectedDataKey]);
-      console.log(filteredData, values, "filteredData")
       const minVal = Math.min(...values).toFixed(2);
       const maxVal = Math.max(...values).toFixed(2);
-      // console.log(minVal, maxVal)
-      // console.log(
-      //   filteredData,
-      //   "filteredData",
-      //   this.selectedDataKey,
-      //   values,
-      //   "values"
-      // );
-      // console.log(filteredData, "filteredData");
       if (
         this.selectedRange === null ||
         this.selectedRange === "" ||
@@ -306,7 +300,6 @@ export default {
 
         // 从最接近 x1 的更小的值开始
         let current = x1;
-        // console.log(current)
         while (current < x2) {
           let next = parseFloat((current + interval).toFixed(2));
 
@@ -330,36 +323,6 @@ export default {
 
         return result;
       }
-      // function generateRangeArray(x1, x2, interval) {
-      //   x1 = parseFloat(x1);
-      //   x2 = parseFloat(x2);
-      //   interval = parseFloat(interval);
-      //   const result = [];
-      //   let current = x1 - (x2 - x1);
-      //   while (current < x2) {
-      //     let next;
-      //     if (current == x1) {
-      //       current = Math.floor(current * 10) / 10;
-      //     }
-      //     next = parseFloat((current + interval).toFixed(2));
-      //     // 保留两位小数
-      //     result.push({
-      //       name: `${current}-${next}`,
-      //       min: current,
-      //       max: next,
-      //     });
-      //     current = next;
-      //   }
-      //   // 最后一个区间
-      //   if (current <= x2) {
-      //     result.push({
-      //       name: `${current}-${x2}`,
-      //       min: current,
-      //       max: x2,
-      //     });
-      //   }
-      //   return result;
-      // }
       // 生成区间标签和频次
       const histogramData = [];
       let result = generateRangeArray(minVal, maxVal, 0.1);
@@ -369,7 +332,6 @@ export default {
         count += values.filter(
           (value) => value >= item.min && value < item.max
         ).length;
-        // console.log(item.max, maxVal)
         if (item.max == maxVal) {
           count += values.filter((value) => value === item.max).length;
         }
@@ -383,83 +345,147 @@ export default {
         (sum, item) => sum + item.value,
         0
       );
+      //---------------------------修改后正态分布-----------------------------
+      // 获取基础数据:最大值,最小值,平均值,标准差
+      function getBebeQ(numbers, digit = 2) {
 
-      //--------------------------正态分布----------------------------------
-      // 计算均值
-      const mean = values.reduce((acc, val) => acc + val, 0) / values.length;
-      // console.log(mean.toFixed(2),"mean")
-      // 计算标准差
-      const stdDev = Math.sqrt(
-        values.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) /
-          values.length
-      );
+        let sum = numbers.reduce(
+          (acc, num) => acc.add(new Decimal(num)),
+          new Decimal(0)
+        );
+
+        let max = Math.max.apply(null, numbers);
+        let min = Math.min.apply(null, numbers);
+
+        // 平均值
+        let mean = sum.dividedBy(numbers.length);
+
+        // 计算每个数与均值的差的平方
+        const squaredDiffs = numbers.map((num) => {
+          const diff = new Decimal(num).minus(mean);
+          return diff.pow(2);
+        });
+
+        // 计算平方差的均值
+        const sumOfSquaredDiffs = squaredDiffs.reduce(
+          (acc, diff) => acc.add(diff),
+          new Decimal(0)
+        );
+
+        const variance = sumOfSquaredDiffs.dividedBy(numbers.length);
+
+        // 开平方得到标准差
+        const standardDeviation = variance.sqrt();
+
+        // 向上取整到最近的 0.1
+        const ceilingRoundedMax = Math.ceil(max * 10) / 10;
+
+        // 向下取整到最近的 0.1
+        const floorRoundedMin = Math.floor(min * 10) / 10;
+
+        return {
+          max: ceilingRoundedMax,
+          min: floorRoundedMin,
+          avg: parseFloat(mean.toNumber().toFixed(digit)) || 0,
+          stdDev: parseFloat(standardDeviation.toNumber().toFixed(digit)),
+        };
+      }
+      // 计算 z-score 的基础数据
+      const scoreBasic = getBebeQ(values);
 
-      // 创建间隔为0.1的区间
-      const center = mean.toFixed(2)
-      const interval = 0.1;
-      let minLimit = Math.floor(Math.min(...values) * 10) / 10; // 向下取整到最近的0.1
-      let maxLimit = Math.ceil(Math.max(...values) * 10) / 10; // 向上取整到最近的0.1
-      console.log("中心", center,minLimit,maxLimit) 
-      if(center-minLimit != maxLimit-center){
-        minLimit = Math.floor(minLimit - (maxLimit-center - (center-minLimit)))
+      const center = new Decimal(scoreBasic.avg); // 确保 center 是 Decimal 类型
+      const interval = new Decimal(0.1);
+
+      // 使用 Decimal 来处理 minLimit 和 maxLimit
+      let minLimit = new Decimal(scoreBasic.min); // 向下取整到最近的0.1
+      let maxLimit = new Decimal(scoreBasic.max); // 向上取整到最近的0.1
+
+      // 确保 minLimit 和 maxLimit 关于 center 对称
+      if (!center.minus(minLimit).equals(maxLimit.minus(center))) {
+        const distanceToCenter = center
+          .minus(minLimit)
+          .gt(maxLimit.minus(center))
+          ? center.minus(minLimit)
+          : maxLimit.minus(center);
+
+        maxLimit = center.plus(distanceToCenter); // 更新 maxLimit 保持对称
+        minLimit = center.minus(distanceToCenter); // 更新 minLimit 保持对称
       }
+
       const intervals = [];
-      console.log(minLimit,maxLimit, interval)
-      // 乘以1000 是将小数变成整数去计算,小数计算会有误差
-      for (let i = minLimit * 1000 ; i <= maxLimit*1000 ; i += (interval*1000)) {
-        intervals.push(parseFloat((i/1000).toFixed(2)))
+      // // 乘以1000 是将小数变成整数去计算,小数计算会有误差
+      for (
+        let i = minLimit * 1000;
+        i <= maxLimit * 1000;
+        i += interval * 1000
+      ) {
+        intervals.push(parseFloat((i / 1000).toFixed(2)));
       }
-      // 计算每个间隔的频次
+      // // 计算每个间隔的频次
       const frequency = new Array(intervals.length - 1).fill(0); // 初始化频次数组
 
-      let intervalArr = intervals.slice(0,-1)
+      let intervalArr = intervals.slice(0, -1);
       const intervalStrings = [];
       for (let i = 0; i < intervalArr.length; i++) {
-          const start = intervalArr[i].toFixed(1); // 保留一位小数
-          const end = ((intervalArr[i]*1000 + interval*1000)/1000).toFixed(1); // 保留一位小数
-          intervalStrings.push({
-            name: `${start}-${end}`,
-            max: end,
-            min: start
-          }); // 构建区间字符串
+        const start = intervalArr[i].toFixed(1); // 保留一位小数
+        const end = ((intervalArr[i] * 1000 + interval * 1000) / 1000).toFixed(
+          1
+        ); // 保留一位小数
+        intervalStrings.push({
+          name: `${start}-${end}`,
+          max: end,
+          min: start,
+        }); // 构建区间字符串
       }
-      console.log(intervals,intervalArr)
+
       values.forEach((value) => {
-        for (let j = 0; j < intervalStrings.length - 1; j++) {
-          // console.log(value)
+        for (let j = 0; j < intervalStrings.length; j++) {
           // 保证包含右边界
-          // console.log(intervals[j].min, intervals[j].max)
-          if (value >= intervalStrings[j].min && value < intervalStrings[j].max) {
-            // console.log(intervals[j],intervals[j + 1])
+          let v = parseFloat(value) * 1000;
+          if (
+            v >= parseFloat(intervalStrings[j].min) * 1000 &&
+            v < parseFloat(intervalStrings[j].max) * 1000
+          ) {
             frequency[j]++;
             break; // 找到对应区间后可以跳出循环
           }
         }
       });
 
-      // 确保处理最大值等于maxLimit的情况
-      if (values.some((value) => value == maxLimit)) {
+      // // 确保处理最大值等于maxLimit的情况
+      if (
+        values.some(
+          (value) => parseFloat(value) * 1000 == parseFloat(maxLimit) * 1000
+        )
+      ) {
         frequency[frequency.length - 1]++;
       }
-
-      // 计算正态分布值
+      // 1.计算正态分布值
       const normalDistributionValues = intervals
         .slice(0, -1)
         .map((intervalStart, index) => {
           const intervalEnd = intervals[index + 1];
-          const midpoint = (intervalStart + intervalEnd) / 2; // 每个区间的中点
-          return (
-            (1 / (stdDev * Math.sqrt(2 * Math.PI))) *
-            Math.exp(-0.5 * Math.pow((midpoint - mean) / stdDev, 2))
+          const midpoint = (intervalStart + intervalEnd) / 2;
+
+          // 使用 Decimal 进行更高精度的计算
+          const midPointDecimal = new Decimal(midpoint);
+          const avgDecimal = new Decimal(scoreBasic.avg);
+          const stdDevDecimal = new Decimal(scoreBasic.stdDev);
+
+          const exponent = midPointDecimal
+            .minus(avgDecimal)
+            .dividedBy(stdDevDecimal)
+            .pow(2)
+            .negated()
+            .dividedBy(new Decimal(2));
+          const exponentValue = new Decimal(Math.exp(exponent.toNumber())); // 取自然指数
+          const coefficient = new Decimal(1).dividedBy(
+            stdDevDecimal.times(new Decimal(Math.sqrt(2 * Math.PI)))
           );
+
+          return coefficient.times(exponentValue).toNumber(); // 返回计算结果
         });
-      // 输出结果
-      // console.log("Intervals:", intervals.slice(0, -1)); // 显示间隔
-      // console.log("Frequencies:", frequency);
-      // console.log("Normal Distribution Values:", normalDistributionValues);
-      //---------------------------------正态分布end---------------------------
-      // const quadraticData = this.generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue);
-      // littlegreen - 去掉line和scatter,增加x轴拖动,根据检测区间修改柱状的颜色
+      //----------------------------修改后正态分布end-----------------------------
       const option = {
         color: ["rgba(245,0,0,0.6)"],
         dataZoom: [
@@ -491,7 +517,7 @@ export default {
             `${this.selectedYear}年 ${
               this.keyToChinese[this.selectedDataKey]
             }企业数统计`,
-            "正态分布"
+            "正态分布",
           ],
         },
         grid: {
@@ -504,23 +530,25 @@ export default {
           type: "category",
           name: "分布区间",
           // data: histogramData.map((item) => item.name),
-          data: intervalStrings.map(item=>item.name),
+          data: intervalStrings.map((item) => item.name),
           axisLabel: {
             fontSize: 12,
-            interval: 0,
-            rotate: 20,
+            // interval: 0,
+            rotate: 0,
             // padding: [0, 0, 0, -200]
           },
         },
-        yAxis: [{
-          name: "企业数",
-          type: "value",
-          minInterval: 1,
-        },{
-          name: '正态分布',
-          type: 'value'
-
-        }],
+        yAxis: [
+          {
+            name: "企业数",
+            type: "value",
+            minInterval: 1,
+          },
+          {
+            name: "正态分布",
+            type: "value",
+          },
+        ],
         series: [
           {
             name: `${this.selectedYear}年 ${
@@ -534,16 +562,13 @@ export default {
                 color: function (params) {
                   const value = params.dataIndex; // 获取当前柱子在数据中的索引
                   if (flag == 1) {
-                    let min = parseFloat(intervalStrings[value].min)*1000
-                    let max = parseFloat(intervalStrings[value].max)*1000
-                    const detectMin = parseFloat(that.detectMin)*1000
-                    const detectMax = parseFloat(that.detectMax)*1000
-                    if (
-                      min >= detectMin &&
-                      max <= detectMax
-                    ) {
+                    let min = parseFloat(intervalStrings[value].min) * 1000;
+                    let max = parseFloat(intervalStrings[value].max) * 1000;
+                    const detectMin = parseFloat(that.detectMin) * 1000;
+                    const detectMax = parseFloat(that.detectMax) * 1000;
+                    if (min >= detectMin && max <= detectMax) {
                       return "#ffcc00"; // 变更颜色为黄色
-                    }else if(min == detectMax){
+                    } else if (min == detectMax) {
                       return "#ffcc00";
                     }
                     return "rgba(245,0,0,0.6)";
@@ -560,7 +585,7 @@ export default {
             },
           },
           {
-            name:"正态分布",
+            name: "正态分布",
             yAxisIndex: 1,
             type: "line",
             data: normalDistributionValues,
@@ -574,21 +599,24 @@ export default {
       this.isChartShow = true;
       this.chart.setOption(option);
       this.chart.on("click", function (param) {
-        // console.log(param.dataIndex, intervalStrings)
-        if(typeof param.dataIndex === "number" &&
+        if (
+          typeof param.dataIndex === "number" &&
           param.dataIndex >= 0 &&
           param.dataIndex < intervalStrings.length
-        ){
+        ) {
           let dataIndexValue = intervalStrings[param.dataIndex];
-          const filteredArray = filteredData.filter((item)=>{
-            const isMaxValEqual = parseFloat(dataIndexValue.max) == parseFloat(maxVal);
-            const lowerBoundCheck = item[that.selectedDataKey] >= dataIndexValue.min;
-            const upperBoundCheck  = isMaxValEqual
-              ? item[that.selectedDataKey] <= dataIndexValue.max
-              : item[that.selectedDataKey] < dataIndexValue.max;
+          const filteredArray = filteredData.filter((item) => {
+            let dmax = new Decimal(dataIndexValue.max);
+            let dmin = new Decimal(dataIndexValue.min);
+            // maxLimit
+            let v = new Decimal(item[that.selectedDataKey]);
+            const isMaxValEqual = dmax.equals(maxLimit);
+            const lowerBoundCheck = v.greaterThan(dmin) || v.equals(dmin);
+            const upperBoundCheck = isMaxValEqual
+              ? v.lessThan(dmax) || v.equals(dmax)
+              : v.lessThan(dmax);
             return lowerBoundCheck && upperBoundCheck;
-          })
-          // console.log(filteredData,filteredArray,that.selectedDataKey)
+          });
           that.tableData = filteredArray.map((item) => {
             return {
               industryName: item.code,
@@ -599,49 +627,6 @@ export default {
             };
           });
         }
-        // if (
-        //   typeof param.dataIndex === "number" &&
-        //   param.dataIndex >= 0 &&
-        //   param.dataIndex < intervalStrings.length
-        // ) {
-        //   let dataIndexValue = intervalStrings[param.dataIndex];
-        //   const filteredArray = filteredData.filter((item) => {
-        //     const isMaxValEqual = intervalStrings[1] === maxVal;
-        //     const lowerBoundCheck =
-        //       item[that.selectedDataKey] >= dataIndexValue.min;
-        //     const upperBoundCheck = isMaxValEqual
-        //       ? item[that.selectedDataKey] <= dataIndexValue.max
-        //       : item[that.selectedDataKey] < dataIndexValue.max;
-
-        //     return lowerBoundCheck && upperBoundCheck;
-        //   });
-        //   that.tableData = filteredArray.map((item) => {
-        //     return {
-        //       industryName: item.code,
-        //       enterpriseId: item.enterpriseName,
-        //       year: item.year,
-        //       value: item[that.selectedDataKey],
-        //       level: "高",
-        //     };
-        //   });
-        // }
-        // const filteredArray = filteredData.filter((item) => {
-        //     const isMaxValEqual = result[1] === maxVal;
-        //     const lowerBoundCheck = item[that.selectedDataKey] >= dataIndexValue.min;
-        //     const upperBoundCheck = isMaxValEqual
-        //         ? item[that.selectedDataKey] <= dataIndexValue.max
-        //         : item[that.selectedDataKey] < dataIndexValue.max;
-
-        //     return lowerBoundCheck && upperBoundCheck;
-        // })
-        // that.tableData = filteredArray.map(item => {
-        //   return {
-        //     industryName: item.code,
-        //     enterpriseId: item.enterpriseName,
-        //     year: item.year,
-        //     value: item[that.selectedDataKey]
-        //   }
-        // })
       });
     },
     generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue) {

+ 3 - 3
src/views/index.vue

@@ -231,7 +231,7 @@ export default {
         this.chart.dispose();
       }
       this.chart = echarts.init(this.$refs.chart);
-      console.log(this.chartData);
+      // console.log(this.chartData);
       // 饼图
       this.chart.setOption(
         {
@@ -269,7 +269,7 @@ export default {
           yAxis: {
             type: "category",
             // inverse: true,
-            data: this.chartData.map((item) => item.industryName),
+            data: this.chartData.map((item) => item.code+item.industryName),
           },
           series: [
             {
@@ -378,7 +378,7 @@ export default {
       let chartArray = this.pieChartData.map((item) => {
         return {
           value: item.number,
-          name: item.industryName,
+          name: (item.code=="others"?"":item.code)+item.industryName,
         };
       });
       this.piechart = echarts.init(this.$refs.piechart);

+ 17 - 65
src/views/industry_run/index.vue

@@ -25,66 +25,18 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="工业总产值" prop="totalIndustrialValue">
-        <el-input
-          v-model="queryParams.totalIndustrialValue"
-          placeholder="请输入工业总产值"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="应税收入" prop="taxableIncome">
-        <el-input
-          v-model="queryParams.taxableIncome"
-          placeholder="请输入应税收入"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="实缴税金" prop="paidTax">
-        <el-input
-          v-model="queryParams.paidTax"
-          placeholder="请输入实缴税金"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="研发经费" prop="funding">
-        <el-input
-          v-model="queryParams.funding"
-          placeholder="请输入研发经费"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="能源消费量" prop="energyConsume">
-        <el-input
-          v-model="queryParams.energyConsume"
-          placeholder="请输入能源消费量"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="用电量" prop="powerConsume">
-        <el-input
-          v-model="queryParams.powerConsume"
-          placeholder="请输入用电量"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="年份" prop="year">
+      <el-form-item label="年度" prop="year">
         <el-input
           v-model="queryParams.year"
-          placeholder="请输入年"
+          placeholder="请输入年度"
           clearable
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="季" prop="season">
+      <el-form-item label="季度" prop="season">
         <el-input
           v-model="queryParams.season"
-          placeholder="请输入季"
+          placeholder="请输入季度"
           clearable
           @keyup.enter.native="handleQuery"
         />
@@ -104,7 +56,7 @@
     </el-form>
 
     <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
+      <!-- <el-col :span="1.5">
         <el-button
           type="primary"
           plain
@@ -135,7 +87,7 @@
           @click="handleDelete"
           v-hasPermi="['industry_run:industry_run:remove']"
         >删除</el-button>
-      </el-col>
+      </el-col> -->
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -154,16 +106,16 @@
       <el-table-column label="企业名称" align="center" prop="enterpriseName" />
       <el-table-column label="坐落地" align="center" prop="location" />
       <el-table-column label="行业代码" align="center" prop="code" />
-      <el-table-column label="工业总产值" align="center" prop="totalIndustrialValue" />
-      <el-table-column label="应税收入" align="center" prop="taxableIncome" />
-      <el-table-column label="实缴税金" align="center" prop="paidTax" />
-      <el-table-column label="研发经费" align="center" prop="funding" />
-      <el-table-column label="能源消费量" align="center" prop="energyConsume" />
-      <el-table-column label="用电量" align="center" prop="powerConsume" />
-      <el-table-column label="年份" align="center" prop="year" />
-      <el-table-column label="季节" align="center" prop="season" />
+      <el-table-column label="工业总产值/万元" align="center" prop="totalIndustrialValue" />
+      <el-table-column label="电力消耗/万KW·h" align="center" prop="powerConsume" />
       <el-table-column label="月份" align="center" prop="month" />
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column label="应税收入/万元" align="center" prop="taxableIncome" />
+      <el-table-column label="实缴税金/万元" align="center" prop="paidTax" />
+      <el-table-column label="季度" align="center" prop="season" />
+      <el-table-column label="能源消耗/万m³" align="center" prop="energyConsume" />
+      <el-table-column label="研发投入/万元" align="center" prop="funding" />
+      <el-table-column label="年度" align="center" prop="year" />
+      <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -180,7 +132,7 @@
             v-hasPermi="['industry_run:industry_run:remove']"
           >删除</el-button>
         </template>
-      </el-table-column>
+      </el-table-column> -->
     </el-table>
     
     <pagination
@@ -245,7 +197,7 @@
         <el-form-item label="用电量" prop="powerConsume">
           <el-input v-model="form.powerConsume" placeholder="请输入用电量" />
         </el-form-item>
-        <el-form-item label="季" prop="season">
+        <el-form-item label="季" prop="season">
           <el-input v-model="form.season" placeholder="请输入季节" />
         </el-form-item>
       </el-form>

+ 637 - 0
src/views/investment/index.vue

@@ -0,0 +1,637 @@
+<template>
+  <!-- 招商 - littlegreen - 补充form和label -->
+  <div
+    :class="className"
+    :style="{ height: '100%', width: width, padding: '20px' }"
+  >
+    <div class="select-container">
+      <el-form
+        :model="form"
+        :rules="rules"
+        ref="ruleForm"
+        style="
+          display: grid;
+          grid-template-columns: 1fr 1fr;
+          align-items: center;
+          grid-gap: 20px;
+        "
+      >
+        <el-form-item
+          label="新增企业月均工业产值"
+          label-width="200px"
+          prop="newMonthValue"
+        >
+          <div style="display: flex; align-items: cente">
+            <el-input
+              v-model.number="form.newMonthValue"
+              placeholder="请输入企业月均工业产值"
+              clearable
+            />
+            <p style="flex: 0 0 auto; margin: 0 10px">万元</p>
+          </div>
+        </el-form-item>
+        <el-form-item label="行业代码" label-width="100px" prop="selectedCode">
+          <el-select v-model="form.selectedCode" style="width: 100%" filterable>
+            <el-option
+              v-for="item in selectedIndustryArray"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option> </el-select
+        ></el-form-item>
+        <el-form-item
+          label="新增企业年实缴税金"
+          label-width="200px"
+          prop="newYearTax"
+        >
+          <!-- <el-input
+            v-model="form.newYearTax"
+            placeholder="请输入企业年实缴税金"
+            clearable
+          /> -->
+          <div style="display: flex; align-items: cente">
+            <el-input
+              v-model.number="form.newYearTax"
+              placeholder="请输入企业年实缴税金"
+              clearable
+            />
+            <p style="flex: 0 0 auto; margin: 0 10px">万元</p>
+          </div>
+        </el-form-item>
+        <el-form-item label-width="100px">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            size="mini"
+            @click="submit"
+            >搜索</el-button
+          >
+          <el-button icon="el-icon-refresh" size="mini" @click="resetForm"
+            >重置</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+    <div ref="chart" :style="{ height: height, width: width }"></div>
+    <el-table :data="tableData" border style="width: 100%">
+      <el-table-column prop="year" label="年度" width="200"> </el-table-column>
+      <el-table-column prop="enterpriseId" label="企业ID"></el-table-column>
+      <el-table-column
+        prop="totalIndustrialValue"
+        label="企业月均工业产值"
+        width="200"
+      ></el-table-column>
+      <el-table-column
+        prop="value"
+        label="Z-score得分"
+        width="200"
+      ></el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+import { listAllIndustry } from "@/api/industry/industry"; // 导入行业数据的接口
+import { listAllIndustry_run } from "@/api/industry_run/industry_run"; // 导入行业数据的接口
+require("echarts/theme/macarons"); // echarts theme
+import resize from "@/views/dashboard/mixins/resize";
+import { Decimal } from "decimal.js";
+
+export default {
+  mixins: [resize],
+  props: {
+    className: {
+      type: String,
+      default: "chart",
+    },
+    width: {
+      type: String,
+      default: "100%",
+    },
+    height: {
+      type: String,
+      default: "400px",
+    },
+  },
+  data() {
+    return {
+      chart: null,
+      chartData: [],
+      industryData: [],
+      industryMap: new Map(),
+      selectedDataKey: "totalIndustrialValueScore", // 默认选择的字段
+      form: {
+        selectedCode: null,
+        newMonthValue: null,
+        newYearTax: null,
+      },
+      rules: {
+        selectedCode: [
+          { required: true, message: "请选择行业", trigger: "blur" },
+        ],
+        newMonthValue: [
+          {
+            required: true,
+            message: "请输入企业月均工业产值",
+            trigger: "blur",
+          },
+          { type: "number", message: "企业月均工业产值必须为数字" },
+        ],
+        newYearTax: [
+          {
+            type: "number",
+            message: "企业年实缴税金必须为数字",
+            trigger: "blur",
+          },
+        ],
+      },
+      selectedIndustryArray: [],
+      // dataKeys: [
+      //   "totalIndustrialValueScore",
+      //   // 'gdpScore',
+      //   "taxableIncomeScore",
+      //   "paidTaxScore",
+      //   "fundingScore",
+      //   "energyConsumeScore",
+      //   "powerConsumeScore",
+      //   // 其他得分字段...
+      // ], // 可选项
+      // keyToChinese: {
+      //   mainBusinessScore: "主营业务活动得分",
+      //   landAreaScore: "用地面积得分",
+      //   totalIndustrialValueScore: "工业总产值得分",
+      //   gdpScore: "工业增加值得分",
+      //   taxableIncomeScore: "应税收入得分",
+      //   paidTaxScore: "实缴税金得分",
+      //   mainBusinessIncomeScore: "主营业务收入得分",
+      //   employeeNumberScore: "从业人员数得分",
+      //   profitScore: "利润总额得分",
+      //   ownerEquityScore: "所有者权益得分",
+      //   fundingScore: "研发经费得分",
+      //   energyConsumeScore: "能源消费量得分",
+      //   powerConsumeScore: "电力消费量得分",
+      // },
+      // keyToLevel: {
+      //   totalIndustrialValueScore: "totalIndustrialValueLevel",
+      //   taxableIncomeScore: "taxableIncomeLevel",
+      //   paidTaxScore: "paidTaxLevel",
+      //   fundingScore: "fundingLevel",
+      //   energyConsumeScore: "energyConsumeLevel",
+      //   powerConsumeScore: "powerConsumeLevel",
+      // },
+      selectedYear: null,
+      // queryParams: {
+      //   pageNum: 1,
+      //   pageSize: 2000000, // 默认 2000000 条(全部一次性数据)
+      //   enterpriseName: null,
+      //   location: null,
+      //   code: null,
+      //   mainBusiness: null,
+      //   landArea: null,
+      //   totalIndustrialValue: null,
+      //   gdp: null,
+      //   taxableIncome: null,
+      //   paidTax: null,
+      //   mainBusinessIncome: null,
+      //   employeeNumber: null,
+      //   profit: null,
+      //   ownerEquity: null,
+      //   funding: null,
+      //   energyConsume: null,
+      //   year: null,
+      //   month: null,
+      // },
+      // industryQueryParams: {
+      //   pageNum: 1,
+      //   pageSize: 2000000,
+      //   industryName: null,
+      //   code: null,
+      // }, //  查询行业信息
+      // availableYears: [],
+      selectedRange: 0.1, // 不能为 0, - littlegreen - 固定0.1
+      // littlegreen - 检测区间 和 表格数据
+      detectMin: null,
+      detectMax: null,
+      tableData: [],
+      scoreData: [],
+    };
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.fetchData();
+    });
+  },
+  beforeDestroy() {
+    if (this.chart) {
+      this.chart.dispose();
+    }
+  },
+  methods: {
+    // 拉取初始数据
+    fetchData() {
+      const that = this;
+      // 获得所有行业
+      that.selectedYear = new Date().getUTCFullYear() + "";
+      listAllIndustry()
+        .then((res) => {
+          if (res && res?.rows) {
+            that.selectedIndustryArray = res.rows.map((item) => {
+              return {
+                key: item.code,
+                value: item.code + item.industryName,
+              };
+            });
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+    },
+    // 提交表单
+    submit() {
+      this.$refs.ruleForm.validate((valid) => {
+        if (valid) {
+          this.handleData();
+        } else {
+          console.error("error submit!!");
+          return false;
+        }
+      });
+    },
+    // 处理数据
+    handleData() {
+      // 1.拿当前年份和code的企业运行数据过来
+      // 2.计算每个企业的z-score
+      // 3.画图
+      const that = this;
+      listAllIndustry_run({
+        code: that.form.selectedCode,
+        year: that.selectedYear,
+      })
+        .then((res) => {
+          if (res && res.rows) {
+            that.scoreData = res.rows;
+            that.initChart();
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching data:", error);
+        });
+    },
+    initChart() {
+      if (this.chart) {
+        this.chart.dispose();
+      }
+      this.chart = echarts.init(this.$refs.chart, "macarons");
+      this.updateChart();
+    },
+    updateChart() {
+      const that = this;
+      if (!this.chart) {
+        return;
+      }
+      let newMonthValue = parseFloat(that.form.newMonthValue);
+      let total = [parseFloat(newMonthValue)]; //总值
+      const result = Object.values(
+        that.scoreData.reduce((acc, item) => {
+          const key = item.enterpriseName; // 根据企业名称分组
+          if (!acc[key]) {
+            acc[key] = { name: key, data: [], totalValue: 0, count: 0 }; // 创建对象并初始化
+          }
+          acc[key].data.push(item); // 添加到对应的类别中
+          acc[key].totalValue += item.totalIndustrialValue; // 累加 totalIndustrialValue
+          acc[key].count++; // 企业数 +1
+          return acc;
+        }, {})
+      ).map(({ name, data, totalValue, count }) => {
+        const totalIndustrialValue = totalValue / count; // 计算均值
+        total.push(totalIndustrialValue);
+        return { name, data, totalIndustrialValue }; // 返回结果
+      });
+      result.push({
+        name: "新企业",
+        data: [],
+        totalIndustrialValue: newMonthValue,
+      });
+      // 获取基础数据:最大值,最小值,平均值,标准差
+      function getBebeQ(numbers, digit = 2) {
+        let sum = numbers.reduce(
+          (acc, num) => acc.add(new Decimal(num)),
+          new Decimal(0)
+        );
+
+        let max = Math.max.apply(null, numbers);
+        let min = Math.min.apply(null, numbers);
+
+        // 平均值
+        let mean = sum.dividedBy(numbers.length);
+
+        // 计算每个数与均值的差的平方
+        const squaredDiffs = numbers.map((num) => {
+          const diff = new Decimal(num).minus(mean);
+          return diff.pow(2);
+        });
+
+        // 计算平方差的均值
+        const sumOfSquaredDiffs = squaredDiffs.reduce(
+          (acc, diff) => acc.add(diff),
+          new Decimal(0)
+        );
+
+        const variance = sumOfSquaredDiffs.dividedBy(numbers.length);
+
+        // 开平方得到标准差
+        const standardDeviation = variance.sqrt();
+
+        // 向上取整到最近的 0.1
+        const ceilingRoundedMax = Math.ceil(max * 10) / 10;
+
+        // 向下取整到最近的 0.1
+        const floorRoundedMin = Math.floor(min * 10) / 10;
+
+        return {
+          max: ceilingRoundedMax,
+          min: floorRoundedMin,
+          avg: parseFloat(mean.toNumber().toFixed(digit)) || 0,
+          stdDev: parseFloat(standardDeviation.toNumber().toFixed(digit)),
+        };
+      }
+
+      // 示例数据
+      const betaData = getBebeQ(total);
+      let newZScore;
+      const values = result.map((item) => {
+        let num = item.totalIndustrialValue;
+        const zScore = new Decimal(num)
+          .minus(betaData.avg)
+          .dividedBy(betaData.stdDev);
+        let scoreNum = zScore.toNumber();
+        if (new Decimal(num).equals(new Decimal(that.form.newMonthValue))) {
+          newZScore = scoreNum;
+        }
+        item.score = scoreNum;
+        return scoreNum;
+      });
+      // 计算 z-score 的基础数据
+      const scoreBasic = getBebeQ(values);
+      const center = new Decimal(scoreBasic.avg); // 确保 center 是 Decimal 类型
+      const interval = new Decimal(0.1);
+
+      // 使用 Decimal 来处理 minLimit 和 maxLimit
+      let minLimit = new Decimal(scoreBasic.min); // 向下取整到最近的0.1
+      let maxLimit = new Decimal(scoreBasic.max); // 向上取整到最近的0.1
+
+      // 确保 minLimit 和 maxLimit 关于 center 对称
+      if (!center.minus(minLimit).equals(maxLimit.minus(center))) {
+        const distanceToCenter = center
+          .minus(minLimit)
+          .gt(maxLimit.minus(center))
+          ? center.minus(minLimit)
+          : maxLimit.minus(center);
+
+        maxLimit = center.plus(distanceToCenter); // 更新 maxLimit 保持对称
+        minLimit = center.minus(distanceToCenter); // 更新 minLimit 保持对称
+      }
+
+      const intervals = [];
+      // 乘以1000 是将小数变成整数去计算,小数计算会有误差
+      for (
+        let i = minLimit * 1000;
+        i <= maxLimit * 1000;
+        i += interval * 1000
+      ) {
+        intervals.push(parseFloat((i / 1000).toFixed(2)));
+      }
+      // // 计算每个间隔的频次
+      const frequency = new Array(intervals.length - 1).fill(0); // 初始化频次数组
+
+      let intervalArr = intervals.slice(0, -1);
+      const intervalStrings = [];
+      for (let i = 0; i < intervalArr.length; i++) {
+        const start = intervalArr[i].toFixed(1); // 保留一位小数
+        const end = ((intervalArr[i] * 1000 + interval * 1000) / 1000).toFixed(
+          1
+        ); // 保留一位小数
+        intervalStrings.push({
+          name: `${start}-${end}`,
+          max: end,
+          min: start,
+        }); // 构建区间字符串
+      }
+
+      values.forEach((value) => {
+        for (let j = 0; j < intervalStrings.length; j++) {
+          // 保证包含右边界
+          let v = parseFloat(value) * 1000;
+          if (
+            v >= parseFloat(intervalStrings[j].min) * 1000 &&
+            v < parseFloat(intervalStrings[j].max) * 1000
+          ) {
+            frequency[j]++;
+            break; // 找到对应区间后可以跳出循环
+          }
+        }
+      });
+
+      // // 确保处理最大值等于maxLimit的情况
+      if (
+        values.some(
+          (value) => parseFloat(value) * 1000 == parseFloat(maxLimit) * 1000
+        )
+      ) {
+        frequency[frequency.length - 1]++;
+      }
+      // 1.计算正态分布值
+      const normalDistributionValues = intervals
+        .slice(0, -1)
+        .map((intervalStart, index) => {
+          const intervalEnd = intervals[index + 1];
+          const midpoint = (intervalStart + intervalEnd) / 2;
+
+          // 使用 Decimal 进行更高精度的计算
+          const midPointDecimal = new Decimal(midpoint);
+          const avgDecimal = new Decimal(scoreBasic.avg);
+          const stdDevDecimal = new Decimal(scoreBasic.stdDev);
+
+          const exponent = midPointDecimal
+            .minus(avgDecimal)
+            .dividedBy(stdDevDecimal)
+            .pow(2)
+            .negated()
+            .dividedBy(new Decimal(2));
+          const exponentValue = new Decimal(Math.exp(exponent.toNumber())); // 取自然指数
+          const coefficient = new Decimal(1).dividedBy(
+            stdDevDecimal.times(new Decimal(Math.sqrt(2 * Math.PI)))
+          );
+
+          return coefficient.times(exponentValue).toNumber(); // 返回计算结果
+        });
+      const option = {
+        color: ["rgba(245,0,0,0.6)"],
+        dataZoom: [
+          {
+            type: "inside", // 内置于坐标系中
+            start: 0,
+            end: 100,
+            xAxisIndex: [0],
+          },
+        ],
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            type: "shadow",
+          },
+          formatter: (params) => {
+            // 使用箭头函数来保持 this 的上下文
+            const seriesName = params[0].name; // 系列名称
+            const dataIndex = params[0].dataIndex; // 数据索引
+            const value = params[0].data; // 实际的数据值
+            const xAxisLabel = params[0].axisValueLabel; // x轴的标签
+            const yAxisLabel = params[0].seriesName;
+            // 构建自定义的 tooltip 内容,包含年份
+            return `${xAxisLabel}</br> ${yAxisLabel}: ${value} `;
+          },
+        },
+        legend: {
+          data: [`${that.selectedYear}工业生产总值企业数统计`, "正态分布"],
+        },
+        grid: {
+          left: "3%",
+          right: "4%",
+          bottom: "50px",
+          containLabel: true,
+        },
+        xAxis: {
+          type: "category",
+          name: "分布区间",
+          // data: histogramData.map((item) => item.name),
+          data: intervalStrings.map((item) => item.name),
+          axisLabel: {
+            fontSize: 12,
+            rotate: 0,
+            // padding: [0, 0, 0, -200]
+          },
+        },
+        yAxis: [
+          {
+            name: "企业数",
+            type: "value",
+            minInterval: 1,
+          },
+          {
+            name: "正态分布",
+            type: "value",
+          },
+        ],
+        series: [
+          {
+            name: `${that.selectedYear}工业生产总值企业数统计`,
+            type: "bar",
+            barWidth: "30%",
+            itemStyle: {
+              // color: "rgba(245,0,0,0.6)",
+              normal: {
+                color: function (params) {
+                  const value = params.dataIndex; // 获取当前柱子在数据中的索引
+                  let min = parseFloat(intervalStrings[value].min) * 1000;
+                  let max = parseFloat(intervalStrings[value].max) * 1000;
+                  if (newZScore * 1000 >= min && newZScore * 1000 < max) {
+                    return "#ffcc00";
+                  } else if (
+                    newZScore * 1000 == maxLimit * 1000 &&
+                    max == maxLimit * 1000
+                  ) {
+                    return "#ffcc00";
+                  }
+                  return "rgba(245,0,0,0.6)";
+                  // 默认颜色
+                },
+              },
+            },
+            // data: histogramData.map((item) => item.value),
+            data: frequency,
+            label: {
+              show: false, // 将show属性设置为false,去掉柱子上的数字
+            },
+          },
+          {
+            name: "正态分布",
+            yAxisIndex: 1,
+            type: "line",
+            data: normalDistributionValues,
+            lineStyle: {
+              color: "#1a7cc8",
+            },
+            symbol: "none",
+          },
+        ],
+      };
+      this.chart.setOption(option);
+      this.chart.on("click", function (param) {
+        if (
+          typeof param.dataIndex === "number" &&
+          param.dataIndex >= 0 &&
+          param.dataIndex < intervalStrings.length
+        ) {
+          let dataIndexValue = intervalStrings[param.dataIndex];
+          const filteredArray = result.filter((item) => {
+            let score = new Decimal(item.score);
+            let max = new Decimal(dataIndexValue.max);
+            let min = new Decimal(dataIndexValue.min);
+            let maxL = new Decimal(maxLimit);
+            const isMaxValEqual = max.equals(maxL);
+            const lowerBoundCheck = score.greaterThan(min) || score.equals(min);
+            const upperBoundCheck = isMaxValEqual
+              ? score.lessThan(max) || score.equals(max)
+              : score.lessThan(max);
+            return lowerBoundCheck && upperBoundCheck;
+          });
+          that.tableData = filteredArray.map((item) => {
+            return {
+              totalIndustrialValue: item.totalIndustrialValue.toFixed(2),
+              enterpriseId: item.name,
+              year: that.selectedYear,
+              value: item.score.toFixed(2)
+            };
+          });
+        }
+      });
+    },
+    // 重置表单
+    resetForm() {
+      this.$refs.ruleForm.resetFields();
+    },
+  },
+  watch: {},
+};
+</script>
+
+<style scoped>
+.select-container {
+  margin: 10px 0;
+  font-size: 16px; /* 调整字号大小 */
+}
+
+.select-container select {
+  padding: 10px 20px; /* 增加内边距 */
+  margin-right: 10px; /* 增加右边距 */
+  border: 1px solid #121315; /* 蓝色边框 */
+  border-radius: 4px; /* 圆角边框 */
+  background-color: white; /* 背景色 */
+  color: #121315; /* 文字颜色 */
+  font-size: 16px; /* 调整字号大小 */
+  cursor: pointer; /* 鼠标悬停时的指针样式 */
+  outline: none; /* 移除焦点时的轮廓 */
+}
+
+.select-container select:hover {
+  border-color: #1a7cc8; /* 鼠标悬停时的边框颜色 */
+}
+
+.select-container select:focus {
+  border-color: #121315; /* 焦点时的边框颜色 */
+  box-shadow: 0 0 0 2px rgba(64, 159, 255, 0.2); /* 焦点时的阴影效果 */
+}
+</style>