2
0

6 Incheckningar 1b52545823 ... 53e8f4451f

Upphovsman SHA1 Meddelande Datum
  littleblue55 53e8f4451f 修改正态分布部分,解决精确度问题 10 månader sedan
  littleblue55 586b559504 修改获取预警的markArea 10 månader sedan
  littleblue55 5140750738 新增招商 10 månader sedan
  littleblue55 d45f3fcaba 首页修改图表的lagend,增加企业效益监测评估 10 månader sedan
  littleblue55 ab5c1aebf7 修改企业运行数据的表单 10 månader sedan
  littleblue55 36c1d44ae2 增加获取全部列表的接口和decimal.js 10 månader sedan

+ 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>