Browse Source

完成同比增速页面开发

littleblue55 1 month ago
parent
commit
19e1950256
2 changed files with 386 additions and 271 deletions
  1. 22 0
      src/api/analysis/analysis.js
  2. 364 271
      src/views/growthRate/index.vue

+ 22 - 0
src/api/analysis/analysis.js

@@ -0,0 +1,22 @@
+import request from '@/utils/request'
+// 
+// /growthRate/growthRate"
+
+// 同比增速
+export function analysisGrowthRate(data) {
+  return request({
+    url: '/growthRate/growthRate/analysis',
+    method: 'post',
+    data: data
+  })
+}
+
+// 企业匹配度分析预警
+
+export function analysisMatch(data) {
+    return request({
+      url: '/match/match/list',
+      method: 'post',
+      data: data
+    })
+  }

+ 364 - 271
src/views/growthRate/index.vue

@@ -1,6 +1,9 @@
 <template>
   <!-- 同比增速 -->
-  <div :class="className" :style="{ height: '100%', width: width }">
+  <div
+    :class="className"
+    :style="{ height: '100%', width: width, padding: '15px' }"
+  >
     <div class="select-container">
       <el-form
         style="
@@ -24,86 +27,174 @@
           </el-select>
         </el-form-item>
         <el-form-item label="行业">
-          <el-select v-model="rateForm.selectedIndustry" style="width: 100%" filterable>
+          <el-select
+            v-model="rateForm.selectedIndustry"
+            style="width: 100%"
+            filterable
+          >
             <el-option
-                v-for="item in industryData"
-                :key="item.key"
-                :label="item.value"
-                :value="item.key"
-              >
-              </el-option>
+              v-for="item in industryData"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="排序方式"> </el-form-item>
+        <el-form-item label="企业分类">
+          <el-select
+            v-model="rateForm.selectEnterType"
+            style="width: 100%"
+            clearable
+          >
+            <el-option
+              v-for="item in enterTypeList"
+              :key="item.number"
+              :value="item.number"
+              :label="keyToChineseEtype(item.number)"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <!-- <el-form-item label="排序方式">
+          <el-select
+            v-model="rateForm.selectedRange"
+            style="width: 100%"
+            filterable
+          >
+            <el-option
+              v-for="key in sortOptions"
+              :key="key"
+              :label="key"
+              :value="key"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item> -->
         <el-form-item label="年度">
-          <el-select v-model="rateForm.selectedYear" style="width: 100%" filterable>
+          <el-select
+            v-model="rateForm.selectedYear"
+            style="width: 100%"
+            filterable
+          >
             <el-option
-                v-for="key in yearsOptions"
-                :key="key"
-                :label="key"
-                :value="key"
-              >
-              </el-option>
+              v-for="key in yearsOptions"
+              :key="key"
+              :label="key"
+              :value="key"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="季度">
-          <el-select v-model="rateForm.selectedSeason" style="width: 100%" filterable>
+        <el-form-item
+          label="季度"
+          v-show="
+            rateForm.selectedDataKey === 'taxableIncome' ||
+            rateForm.selectedDataKey === 'paidTax'
+          "
+        >
+          <el-select
+            v-model="rateForm.selectedSeason"
+            style="width: 100%"
+            filterable
+          >
             <el-option
-                v-for="key in seasonOptions"
-                :key="key"
-                :label="key"
-                :value="key"
-              >
-              </el-option>
+              v-for="key in seasonOptions"
+              :key="key"
+              :label="key"
+              :value="key"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="企业分类"></el-form-item>
-        <el-form-item label="起始月度">
-          <el-select v-model="rateForm.selectedStartMonth" style="width: 100%" filterable>
+        <el-form-item
+          label="起始月度"
+          v-show="
+            rateForm.selectedDataKey === 'totalIndustrialValue' ||
+            rateForm.selectedDataKey === 'powerConsume'
+          "
+        >
+          <el-select
+            v-model="rateForm.selectedStartMonth"
+            style="width: 100%"
+            filterable
+          >
             <el-option
-                v-for="key in monthOptions"
-                :key="key"
-                :label="key"
-                :value="key"
-              >
-              </el-option>
+              v-for="key in monthOptions"
+              :key="key"
+              :label="key"
+              :value="key"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="结束月度">
-          <el-select v-model="rateForm.selectedEndMonth" style="width: 100%" filterable>
+        <el-form-item
+          label="结束月度"
+          v-show="
+            rateForm.selectedDataKey === 'totalIndustrialValue' ||
+            rateForm.selectedDataKey === 'powerConsume'
+          "
+        >
+          <el-select
+            v-model="rateForm.selectedEndMonth"
+            style="width: 100%"
+            filterable
+          >
             <el-option
-                v-for="key in monthOptions"
-                :key="key"
-                :label="key"
-                :value="key"
-              >
-              </el-option>
+              v-for="key in monthOptions"
+              :key="key"
+              :label="key"
+              :value="key"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item style="margin-bottom: 0">
+        <el-form-item>
           <el-button
             type="primary"
             icon="el-icon-search"
-            @click="updateChart"
+            @click="submit"
             size="mini"
             >搜索</el-button
           >
         </el-form-item>
       </el-form>
     </div>
+    <el-button-group
+      style="margin-right: 30px; position: absolute; right: 0; z-index: 10"
+      v-show="sortButtonShow"
+    >
+      <el-button
+        size="mini"
+        :type="sortActive === 'asc' ? 'primary' : 'default'"
+        @click="changSort('asc')"
+        >升序</el-button
+      >
+      <el-button
+        size="mini"
+        :type="sortActive === 'desc' ? 'primary' : 'default'"
+        @click="changSort('desc')"
+        >降序</el-button
+      >
+      <el-button
+        size="mini"
+        :type="sortActive === 'location' ? 'primary' : 'default'"
+        @click="changSort('location')"
+        >按坐落地升序</el-button
+      >
+    </el-button-group>
     <div ref="chart" :style="{ height: height, width: width }"></div>
     <!-- littlegreen - 新增数据展示table -->
-    <el-table :data="tableData" border style="width: 100%">
+    <el-table :data="tableData" border>
+      <el-table-column prop="enterpriseName" label="企业名称"></el-table-column>
       <el-table-column
-        prop="industryName"
-        label="行业名称"
+        prop="rate"
+        label="同比增速"
         width="200"
       ></el-table-column>
-      <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="value"
-        :label="label"
+        prop="location"
+        label="坐落地"
         width="200"
       ></el-table-column>
     </el-table>
@@ -113,6 +204,8 @@
 <script>
 import * as echarts from "echarts";
 import { getYearData } from "@/api/home";
+import { listEtypeAll } from "@/api/etype/etype";
+import { analysisGrowthRate } from "@/api/analysis/analysis";
 import { listBase_data_year } from "@/api/base_data_year/base_data_year";
 import { listIndustry, listAllIndustry } from "@/api/industry/industry"; // 导入行业数据的接口
 require("echarts/theme/macarons"); // echarts theme
@@ -149,13 +242,18 @@ export default {
       seasonMap: new Map(),
       monthMap: new Map(),
       yearsOptions: [],
+      enterTypeList: [], // 企业类型列表
+      sortOptions: ["升序", "降序", "坐落地排序"],
       label: "评估指标值/万元",
       chart: null,
       chartData: [],
       industryData: [],
       industryMap: new Map(),
+      searchData: [],
       // 默认选择的字段
       selectedCode: null,
+      sortActive: "asc",
+      sortButtonShow: false,
       dataKeys: [
         "funding",
         "energyConsume",
@@ -238,264 +336,241 @@ export default {
   methods: {
     fetchData() {
       // 获取年份,获取季度、月度
-      getYearData()
-        .then((res) => {
-          this.yearsOptions = res.rows[0].years;
-          this.seasonMap = this.generateSeason(this.yearsOptions);
-          this.monthMap = this.generateMonthlyData(this.yearsOptions);
-          // console.log("monthMap", this.monthMap);
-        })
-        .catch((error) => {
-          console.error("Error fetching data:", error);
-        });
-      listAllIndustry()
-        .then((response) => {
-          // this.industryData = response.rows;
-          if (response && response?.rows) {
-              this.industryData = response.rows.map((item) => {
-                return {
-                  key: item.code,
-                  value: item.code + item.industryName,
-                };
-              });
-            }
-          // console.log("industryMap", this.industryMap);
+      Promise.all([
+        this.getYearData(),
+        this.getAllIndustry(),
+        this.getEtypeAll(),
+      ])
+        .then(() => {
+          this.initChart();
         })
         .catch((error) => {
           console.error("Error fetching data:", error);
         });
-      // listBase_data_year(this.queryParams)
-      //   .then((response) => {
-      //     this.chartData = response.rows;
-      //     this.availableYears = [
-      //       ...new Set(this.chartData.map((item) => item.year)),
-      //     ];
-      //     this.selectedYear = this.availableYears[0] || null;
-      //     this.selectedCode = [
-      //       ...new Set(this.chartData.map((item) => item.code)),
-      //     ][0];
-      //     //   this.initChart();
-      //   })
-      //   .catch((error) => {
-      //     console.error("Error fetching data:", error);
-      //   });
+    },
+    getYearData() {
+      return getYearData().then((res) => {
+        this.yearsOptions = res.rows[0].years;
+        this.seasonMap = this.generateSeason(this.yearsOptions);
+        this.monthMap = this.generateMonthlyData(this.yearsOptions);
+      });
+    },
+
+    getAllIndustry() {
+      return listAllIndustry().then((response) => {
+        if (response?.rows) {
+          this.industryData = response.rows.map((item) => ({
+            key: item.code,
+            value: item.code + item.industryName,
+          }));
+        }
+      });
+    },
 
-      // 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中
-      //       return map;
-      //     }, new Map());
-      //   })
-      //   .catch((error) => {
-      //     console.error("Error fetching data:", error);
-      //   });
+    getEtypeAll() {
+      return listEtypeAll().then((res) => {
+        this.enterTypeList = res;
+        console.log(this.enterTypeList);
+      });
     },
     initChart() {
       if (this.chart) {
         this.chart.dispose();
       }
       this.chart = echarts.init(this.$refs.chart, "macarons");
-      this.updateChart();
+      // this.updateChart();
     },
-    updateChart() {
-      console.log("updateChart", this.rateForm);
+    submit() {
+      const that = this;
+      let {
+        selectedDataKey,
+        selectedStartMonth,
+        selectedEndMonth,
+        selectedSeason,
+        selectedYear,
+        selectedIndustry,
+      } = this.rateForm;
+      // -----数据校验 start-----
+      if (!selectedIndustry) {
+        this.$message.error("请选择行业");
+        return;
+      }
+
+      if (!selectedYear) {
+        this.$message.error("请选择年度");
+        return;
+      }
+      const validateMonths = (startMonth, endMonth) => {
+        if (!startMonth) {
+          this.$message.error("请选择开始月份");
+          return false;
+        }
+        if (!endMonth) {
+          this.$message.error("请选择结束月份");
+          return false;
+        }
+        if (endMonth < startMonth) {
+          this.$message.error("结束月份不能小于开始月份");
+          return false;
+        }
+        return true;
+      };
+      if (["totalIndustrialValue", "powerConsume"].includes(selectedDataKey)) {
+        if (!validateMonths(selectedStartMonth, selectedEndMonth)) return;
+        this.rateForm.selectedSeason = null;
+      } else if (["taxableIncome", "paidTax"].includes(selectedDataKey)) {
+        this.rateForm.selectedStartMonth = null;
+        this.rateForm.selectedEndMonth = null;
+        if (!selectedSeason) {
+          this.$message.error("请选择季度");
+          return;
+        }
+      } else if (["energyConsume", "funding"].includes(selectedDataKey)) {
+        this.rateForm.selectedSeason = null; 
+        this.rateForm.selectedStartMonth = null; 
+        this.rateForm.selectedEndMonth = null; 
+      }
+      // -----数据校验 end-----
+      const propertyMapping = {
+        selectedDataKey: "analysisType",
+        selectedIndustry: "code",
+        selectedRange: "sort",
+        selectedYear: "year",
+        selectedSeason: "season",
+        selectedStartMonth: "startMonth",
+        selectedEndMonth: "endMonth",
+        selectEnterType: "typeNum",
+      };
+      const renamedRateForm = this.renameProperties(
+        this.rateForm,
+        propertyMapping
+      );
+      analysisGrowthRate(renamedRateForm)
+        .then((res) => {
+          if (res && res?.rows) {
+            that.searchData = res.rows;
+            that.updateChart(res.rows);
+          }
+          console.log(res);
+        })
+        .catch((err) => {
+          this.$message.error("请求数据失败");
+          return;
+        });
     },
-    updateChartt() {
+    updateChart(rawData) {
+      const that = this;
       if (!this.chart) {
         return;
       }
-      let filteredData = this.chartData;
-      if (this.selectedYear) {
-        filteredData = filteredData.filter(
-          (item) =>
-            item.year === this.selectedYear && item.code === this.selectedCode
-        );
+      const categories = [];
+      const seriesData = [];
+      let sortedData = [];
+      sortedData = rawData.map((item) => ({
+        ...item, // 扩展其他属性
+        rate: isNaN(parseFloat(item.rate))
+          ? null
+          : (parseFloat(item.rate) * 100).toFixed(2),
+      }));
+      // 处理数据
+      if (this.sortActive === "asc") {
+        sortedData.sort((a, b) => parseFloat(a.rate) - parseFloat(b.rate));
+      } else if (this.sortActive === "desc") {
+        sortedData.sort((a, b) => parseFloat(b.rate) - parseFloat(a.rate));
+      } else if (this.sortActive === "location") {
+        sortedData.sort((a, b) => a.location.localeCompare(b.location));
       }
-      const values = filteredData.map((item) => item[this.selectedDataKey]);
-      const minVal = 0;
-      const maxVal = Math.max(...values);
-      // console.log(filteredData, values, this.selectedDataKey, 898);
-      if (
-        this.selectedRange === null ||
-        this.selectedRange === "" ||
-        isNaN(+this.selectedRange)
-      )
-        this.selectedRange = "100";
-      else if (Number(this.selectedRange) <= 0) this.selectedRange = "100";
-
-      const binCount = Math.ceil(
-        (maxVal - minVal) / Number(this.selectedRange)
-      );
 
-      let maxNum = -1;
+      sortedData.forEach((item) => {
+        categories.push(item.enterpriseName);
 
-      // 生成区间标签和频次
-      const histogramData = [];
-      for (let i = 0; i < binCount; i++) {
-        const x0 = minVal + i * Number(this.selectedRange);
-        const x1 = Math.min(x0 + Number(this.selectedRange), maxVal);
-        const label = `${x0}-${x1}`;
-        let count = 0;
-        count += values.filter((value) => value >= x0 && value < x1).length;
-        if (x1 === maxVal) {
-          count += values.filter((value) => value === maxVal).length;
+        if (!item.rate || item.rate === "Infinity") {
+          seriesData.push({
+            value: null,
+            itemStyle: {
+              color: "#ff0000",
+            },
+          });
+        } else {
+          seriesData.push({
+            value: item.rate,
+            itemStyle: {
+              color: item.rate >= 0 ? "#5470C6" : "#91CC75",
+            },
+          });
         }
+      });
+      that.tableData = sortedData;
+      console.log(sortedData);
+      const values = seriesData.map((s) => s.value || 0);
+      const minValue = Math.min(...values); // 获取最小值
+      const maxValue = Math.max(...values); // 获取最大值
 
-        if (count > 0) {
-          if (count > maxNum) {
-            maxNum = count;
-          }
-          histogramData.push({ name: label, value: count });
-        }
-      }
+      const minAbs = Math.abs(minValue); // 最小值的绝对值
+      const maxAbs = Math.abs(maxValue); // 最大值的绝对值
 
-      const maxHistogramValue = Math.max(
-        ...histogramData.map((item) => item.value)
-      );
-      const totalHistogramValue = histogramData.reduce(
-        (sum, item) => sum + item.value,
-        0
-      );
+      // 选择较大的绝对值来设置对称的 minVal 和 maxVal
+      const maxBound = Math.max(minAbs, maxAbs);
 
-      // const quadraticData = this.generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue);
-      // littlegreen - 去掉line和scatter
+      const minVal = -maxBound; // 设置最小值为负
+      const maxVal = maxBound; // 设置最大值为正
+
+      console.log(minVal, maxVal);
+      // 配置图表
       const option = {
+        title: {
+          text: "企业同比增速分析",
+          left: "center",
+        },
         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}`;
+          trigger: "item",
+          formatter: function (params) {
+            if (!params.value && params.value !== 0) {
+              return `${params.name}<br/>同比增速:数据异常`;
+            }
+            return `${params.name}<br/>同比增速:${params.value}%`;
           },
         },
-        legend: {
-          data: [
-            `${this.selectedYear}年 ${
-              this.keyToChinese[this.selectedDataKey]
-            }企业数统计`,
-          ],
-        },
-        grid: {
-          left: "3%",
-          right: "4%",
-          bottom: "3%",
-          containLabel: true,
-        },
         xAxis: {
           type: "category",
-          name: "分布区间",
-          data: histogramData.map((item) => item.name),
+          data: categories,
           axisLabel: {
-            fontSize: 12,
-            interval: 0,
-            rotate: 30,
+            rotate: 45,
           },
+          boundaryGap: true, // 使 x 轴对称居中
         },
         yAxis: {
-          name: "企业数",
           type: "value",
-          minInterval: 1,
+          axisLabel: {
+            formatter: "{value}%",
+          },
+          min: minVal,
+          max: maxVal,
         },
         series: [
           {
-            name: `${this.selectedYear}年 ${
-              this.keyToChinese[this.selectedDataKey]
-            }企业数统计`,
+            data: seriesData,
             type: "bar",
-            barWidth: "30%",
-            itemStyle: {
-              color: "rgba(245,0,0,0.6)",
-            },
-            data: histogramData.map((item) => item.value),
+            barWidth: "60%",
             label: {
-              show: false, // 将show属性设置为false,去掉柱子上的数字
+              show: true,
+              position: "top",
+              formatter: function (params) {
+                return params.value === null ? "异常" : params.value + "%";
+              },
             },
           },
-          // {
-          //   type: "scatter",
-          //   symbol: "circle",
-          //   symbolSize: 10,
-          //   data: histogramData.map((item, index) => [index, item.value]),
-          //   itemStyle: {
-          //     color: "red",
-          //   },
-          // },
-          // {
-          //   type: "line",
-          //   data: histogramData.map((item, index) => [index, item.value]),
-          //   lineStyle: {
-          //     color: "#1a7cc8",
-          //   },
-          //   symbol: "none",
-          // },
+        ],
+        dataZoom: [
+          {
+            type: "inside",
+            start: 0,
+            end: 100,
+          },
         ],
       };
       this.chart.setOption(option);
-      const that = this;
-      // littlegreen - chart增加点击事件,点击显示对应区间的企业列表
-      this.chart.on("click", function (param) {
-        //param参数包含的内容有:
-        //param.name:X轴的值
-        //param.data:Y轴的值
-        //param.value:Y轴的值
-        //param.type:点击事件均为click
-        //param.seriesName:legend的名称
-        //param.seriesIndex:系列序号(series中当前图形是第几个图形第几个)
-        //param.dataIndex:数值序列(X轴上当前点是第几个点)
-        //alert(param.seriesName);  //legend的名称
-        // console.log(param.name); //X轴的值 0-600 1.用-分割获得确定最小值和最大值 2.筛选filteredData中this.selectedDataKey的值符合要求的
-        const result = param.name.split("-").map(Number);
-        const filteredArray = filteredData.filter((item) => {
-          const isMaxValEqual = result[1] === maxVal;
-          const lowerBoundCheck = item[that.selectedDataKey] >= result[0];
-          const upperBoundCheck = isMaxValEqual
-            ? item[that.selectedDataKey] <= result[1]
-            : item[that.selectedDataKey] < result[1];
-
-          return lowerBoundCheck && upperBoundCheck;
-        });
-        that.tableData = filteredArray.map((item) => {
-          return {
-            industryName: item.code,
-            enterpriseId: item.enterpriseName,
-            year: item.year,
-            value: item[that.selectedDataKey],
-          };
-        });
-        // console.log(filteredData, result, that.selectedDataKey, filteredArray); //获取自定义的值
-      });
-    },
-    generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue) {
-      const a = -0.01;
-      const h = binCount / 2;
-      const k = maxHistogramValue * 0.75;
-      const sideValue = (maxHistogramValue * 0.15) / 2;
-
-      const data = [];
-      for (let i = 0; i < binCount; i++) {
-        const x = i;
-        const y = a * (x - h) ** 2 + k;
-
-        // 调整两侧的值
-        if (i === 0 || i === binCount - 1) {
-          data.push([x, sideValue]);
-        } else {
-          data.push([x, y]);
-        }
-      }
-      return data;
+      this.sortButtonShow = true;
     },
     generateSeason(years) {
       const currentYear = new Date().getFullYear();
@@ -547,6 +622,21 @@ export default {
 
       return monthlyData;
     },
+    keyToChineseEtype(num) {
+      const item = this.enterTypeList.find((element) => element.number === num);
+      return item ? item.name : null;
+    },
+    renameProperties(obj, mapping) {
+      return Object.keys(mapping).reduce((accumulator, currentKey) => {
+        const newKey = mapping[currentKey];
+        accumulator[newKey] = obj[currentKey];
+        return accumulator;
+      }, {});
+    },
+    changSort(sortType) {
+      this.sortActive = sortType;
+      this.updateChart(this.searchData);
+    },
   },
   watch: {
     // littlegreen - 关闭监测数值变化
@@ -565,15 +655,18 @@ export default {
   },
   computed: {
     seasonOptions() {
-        const options = this.rateForm.selectedYear ? this.seasonMap.get(this.rateForm.selectedYear) : [];
-        return options || []; // 以防得到 undefined
+      const options = this.rateForm.selectedYear
+        ? this.seasonMap.get(this.rateForm.selectedYear)
+        : [];
+      return options || []; // 以防得到 undefined
     },
     monthOptions() {
-        const options = this.rateForm.selectedYear ? this.monthMap.get(this.rateForm.selectedYear) : [];
-        return options || []; // 以防得到 undefined
-    }
-}
-
+      const options = this.rateForm.selectedYear
+        ? this.monthMap.get(this.rateForm.selectedYear)
+        : [];
+      return options || []; // 以防得到 undefined
+    },
+  },
 };
 </script>