zhifangbasedata.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. <template>
  2. <!-- 行业水平分布 - littlegreen - 补充form和label -->
  3. <div :class="className" :style="{ height: '100%', width: width }">
  4. <div class="select-container">
  5. <el-form
  6. style="
  7. display: grid;
  8. grid-template-columns: 1fr 1fr 1fr;
  9. align-items: center;
  10. grid-gap: 20px;
  11. "
  12. label-width="100px"
  13. >
  14. <el-form-item label="评估指标" style="margin-bottom: 0">
  15. <el-select v-model="selectedDataKey" style="width: 100%">
  16. <el-option
  17. v-for="key in dataKeys"
  18. :key="key"
  19. :value="key"
  20. :label="keyToChinese[key]"
  21. >
  22. {{ keyToChinese[key] }}
  23. </el-option>
  24. </el-select>
  25. </el-form-item>
  26. <el-form-item label="年度" style="margin-bottom: 0">
  27. <el-select v-model="selectedYear" style="width: 100%" >
  28. <el-option v-for="year in availableYears" :key="year" :value="year">
  29. {{ year }}
  30. </el-option>
  31. </el-select>
  32. </el-form-item>
  33. <el-form-item label="行业代码" style="margin-bottom: 0">
  34. <el-input
  35. v-model="selectedCode"
  36. placeholder="请输入行业代码"
  37. clearable
  38. style="width: 100%"
  39. />
  40. </el-form-item>
  41. <el-form-item label="数据分布区间" style="margin-bottom: 0">
  42. <el-input
  43. v-model="selectedRange"
  44. placeholder="请设置数据分布区间"
  45. clearable
  46. style="width: 100%"
  47. />
  48. </el-form-item>
  49. <el-form-item label="当前行业:" style="margin-bottom: 0">
  50. <div>
  51. {{ industryMap.get(selectedCode) }}
  52. </div>
  53. </el-form-item>
  54. <el-form-item style="margin-bottom: 0">
  55. <el-button type="primary" icon="el-icon-search" @click="updateChart" size="mini"
  56. >搜索</el-button
  57. >
  58. </el-form-item>
  59. </el-form>
  60. <!-- <el-select v-model="selectedDataKey" @change="updateChart">
  61. <el-option
  62. v-for="key in dataKeys"
  63. :key="key"
  64. :value="key"
  65. :label="keyToChinese[key]"
  66. >
  67. {{ keyToChinese[key] }}
  68. </el-option>
  69. </el-select>
  70. <el-select v-model="selectedYear" @change="updateChart">
  71. <el-option v-for="year in availableYears" :key="year" :value="year">
  72. {{ year }}
  73. </el-option>
  74. </el-select>
  75. <el-input
  76. v-model="selectedCode"
  77. placeholder="请输入行业代码"
  78. clearable
  79. style="width: 180px"
  80. />
  81. <el-input
  82. v-model="selectedRange"
  83. placeholder="请设置数据分布区间"
  84. clearable
  85. style="width: 180px"
  86. /> -->
  87. <!-- <div style="display: inline; padding-left: 10px">
  88. </div> -->
  89. </div>
  90. <div ref="chart" :style="{ height: height, width: width }"></div>
  91. <!-- littlegreen - 新增数据展示table -->
  92. <el-table :data="tableData" border style="width: 100%">
  93. <el-table-column
  94. prop="industryName"
  95. label="行业名称"
  96. width="200"
  97. ></el-table-column>
  98. <el-table-column prop="year" label="年度" width="200"> </el-table-column>
  99. <el-table-column prop="enterpriseId" label="企业ID"></el-table-column>
  100. <el-table-column
  101. prop="value"
  102. :label="label"
  103. width="200"
  104. ></el-table-column>
  105. </el-table>
  106. </div>
  107. </template>
  108. <script>
  109. import * as echarts from "echarts";
  110. import { listBase_data_year } from "@/api/base_data_year/base_data_year";
  111. import { listIndustry } from "@/api/industry/industry"; // 导入行业数据的接口
  112. require("echarts/theme/macarons"); // echarts theme
  113. import resize from "./mixins/resize";
  114. export default {
  115. mixins: [resize],
  116. props: {
  117. className: {
  118. type: String,
  119. default: "chart",
  120. },
  121. width: {
  122. type: String,
  123. default: "100%",
  124. },
  125. height: {
  126. type: String,
  127. default: "400px",
  128. },
  129. },
  130. data() {
  131. return {
  132. label: "评估指标值/万元",
  133. chart: null,
  134. chartData: [],
  135. industryData: [],
  136. industryMap: new Map(),
  137. selectedDataKey: "funding", // 默认选择的字段
  138. selectedCode: null,
  139. dataKeys: [
  140. "funding",
  141. "energyConsume",
  142. "paidTax",
  143. "taxableIncome",
  144. "totalIndustrialValue",
  145. "powerConsume",
  146. ], // 可选项
  147. keyToChinese: {
  148. landArea: "用地面积",
  149. totalIndustrialValue: "工业总产值",
  150. gdp: "工业增加值",
  151. taxableIncome: "应税收入",
  152. paidTax: "实缴税金",
  153. mainBusinessIncome: "主营业务收入",
  154. employeeNumber: "从业人员数",
  155. profit: "利润总额",
  156. ownerEquity: "所有者权益",
  157. funding: "研发经费",
  158. energyConsume: "能源消费量",
  159. powerConsume: "电力消费量",
  160. },
  161. keyToUnit: {
  162. landArea: "/亩",
  163. totalIndustrialValue: "/万元",
  164. gdp: "/万元",
  165. taxableIncome: "/万元",
  166. paidTax: "/万元",
  167. mainBusinessIncome: "/万元",
  168. employeeNumber: "/人",
  169. profit: "/万元",
  170. ownerEquity: "",
  171. funding: "/万元",
  172. energyConsume: "/万m³",
  173. powerConsume: "/万KW·h",
  174. },
  175. selectedYear: null,
  176. queryParams: {
  177. pageNum: 1,
  178. pageSize: 2000000, // 默认 2000000 条(全部一次性数据)
  179. enterpriseName: null,
  180. location: null,
  181. code: null,
  182. mainBusiness: null,
  183. landArea: null,
  184. totalIndustrialValue: null,
  185. gdp: null,
  186. taxableIncome: null,
  187. paidTax: null,
  188. mainBusinessIncome: null,
  189. employeeNumber: null,
  190. profit: null,
  191. ownerEquity: null,
  192. funding: null,
  193. energyConsume: null,
  194. year: null,
  195. month: null,
  196. },
  197. industryQueryParams: {
  198. pageNum: 1,
  199. pageSize: 2000000,
  200. industryName: null,
  201. code: null,
  202. }, // 查询行业信息
  203. availableYears: [],
  204. selectedRange: 300, // 不能为 0
  205. tableData: [],
  206. };
  207. },
  208. mounted() {
  209. this.$nextTick(() => {
  210. this.fetchData();
  211. });
  212. },
  213. beforeDestroy() {
  214. if (this.chart) {
  215. this.chart.dispose();
  216. }
  217. },
  218. methods: {
  219. fetchData() {
  220. listBase_data_year(this.queryParams)
  221. .then((response) => {
  222. this.chartData = response.rows;
  223. this.availableYears = [
  224. ...new Set(this.chartData.map((item) => item.year)),
  225. ];
  226. this.selectedYear = this.availableYears[0] || null;
  227. this.selectedCode = [
  228. ...new Set(this.chartData.map((item) => item.code)),
  229. ][0];
  230. this.initChart();
  231. })
  232. .catch((error) => {
  233. console.error("Error fetching data:", error);
  234. });
  235. listIndustry(this.industryQueryParams)
  236. .then((response) => {
  237. this.industryData = response.rows;
  238. // console.log("industry_info" + this.industryData);
  239. this.industryMap = this.industryData.reduce((map, item) => {
  240. const key = `${item.code}`; // 键是行业代码
  241. map.set(key, item.industryName); // 将键和对应的行业名称存储到Map中
  242. return map;
  243. }, new Map());
  244. })
  245. .catch((error) => {
  246. console.error("Error fetching data:", error);
  247. });
  248. },
  249. initChart() {
  250. if (this.chart) {
  251. this.chart.dispose();
  252. }
  253. this.chart = echarts.init(this.$refs.chart, "macarons");
  254. this.updateChart();
  255. },
  256. updateChart() {
  257. if (!this.chart) {
  258. return;
  259. }
  260. let filteredData = this.chartData;
  261. if (this.selectedYear) {
  262. filteredData = filteredData.filter(
  263. (item) =>
  264. item.year === this.selectedYear && item.code === this.selectedCode
  265. );
  266. }
  267. const values = filteredData.map((item) => item[this.selectedDataKey]);
  268. const minVal = 0;
  269. const maxVal = Math.max(...values);
  270. console.log(filteredData, values, this.selectedDataKey, 898);
  271. if (
  272. this.selectedRange === null ||
  273. this.selectedRange === "" ||
  274. isNaN(+this.selectedRange)
  275. )
  276. this.selectedRange = "100";
  277. else if (Number(this.selectedRange) <= 0) this.selectedRange = "100";
  278. const binCount = Math.ceil(
  279. (maxVal - minVal) / Number(this.selectedRange)
  280. );
  281. let maxNum = -1;
  282. // 生成区间标签和频次
  283. const histogramData = [];
  284. for (let i = 0; i < binCount; i++) {
  285. const x0 = minVal + i * Number(this.selectedRange);
  286. const x1 = Math.min(x0 + Number(this.selectedRange), maxVal);
  287. const label = `${x0}-${x1}`;
  288. let count = 0;
  289. count += values.filter((value) => value >= x0 && value < x1).length;
  290. if (x1 === maxVal) {
  291. count += values.filter((value) => value === maxVal).length;
  292. }
  293. if (count > 0) {
  294. if (count > maxNum) {
  295. maxNum = count;
  296. }
  297. histogramData.push({ name: label, value: count });
  298. }
  299. }
  300. const maxHistogramValue = Math.max(
  301. ...histogramData.map((item) => item.value)
  302. );
  303. const totalHistogramValue = histogramData.reduce(
  304. (sum, item) => sum + item.value,
  305. 0
  306. );
  307. // const quadraticData = this.generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue);
  308. // littlegreen - 去掉line和scatter
  309. const option = {
  310. tooltip: {
  311. trigger: "axis",
  312. axisPointer: {
  313. type: "shadow",
  314. },
  315. formatter: (params) => {
  316. // 使用箭头函数来保持 this 的上下文
  317. const seriesName = params[0].name; // 系列名称
  318. const dataIndex = params[0].dataIndex; // 数据索引
  319. const value = params[0].data; // 实际的数据值
  320. const xAxisLabel = params[0].axisValueLabel; // x轴的标签
  321. const yAxisLabel = params[0].seriesName;
  322. // 构建自定义的 tooltip 内容,包含年份
  323. return `${xAxisLabel}<br> ${yAxisLabel}: ${value}`;
  324. },
  325. },
  326. legend: {
  327. data: [
  328. `${this.selectedYear}年 ${
  329. this.keyToChinese[this.selectedDataKey]
  330. }企业数统计`,
  331. ],
  332. },
  333. grid: {
  334. left: "3%",
  335. right: "4%",
  336. bottom: "3%",
  337. containLabel: true,
  338. },
  339. xAxis: {
  340. type: "category",
  341. name: "分布区间",
  342. data: histogramData.map((item) => item.name),
  343. axisLabel: {
  344. fontSize: 12,
  345. interval: 0,
  346. rotate: 30,
  347. },
  348. },
  349. yAxis: {
  350. name: "企业数",
  351. type: "value",
  352. minInterval: 1,
  353. },
  354. series: [
  355. {
  356. name: `${this.selectedYear}年 ${
  357. this.keyToChinese[this.selectedDataKey]
  358. }企业数统计`,
  359. type: "bar",
  360. barWidth: "30%",
  361. itemStyle: {
  362. color: "rgba(245,0,0,0.6)",
  363. },
  364. data: histogramData.map((item) => item.value),
  365. label: {
  366. show: false, // 将show属性设置为false,去掉柱子上的数字
  367. },
  368. },
  369. // {
  370. // type: "scatter",
  371. // symbol: "circle",
  372. // symbolSize: 10,
  373. // data: histogramData.map((item, index) => [index, item.value]),
  374. // itemStyle: {
  375. // color: "red",
  376. // },
  377. // },
  378. // {
  379. // type: "line",
  380. // data: histogramData.map((item, index) => [index, item.value]),
  381. // lineStyle: {
  382. // color: "#1a7cc8",
  383. // },
  384. // symbol: "none",
  385. // },
  386. ],
  387. };
  388. this.chart.setOption(option);
  389. const that = this;
  390. // littlegreen - chart增加点击事件,点击显示对应区间的企业列表
  391. this.chart.on("click", function (param) {
  392. //param参数包含的内容有:
  393. //param.name:X轴的值
  394. //param.data:Y轴的值
  395. //param.value:Y轴的值
  396. //param.type:点击事件均为click
  397. //param.seriesName:legend的名称
  398. //param.seriesIndex:系列序号(series中当前图形是第几个图形第几个)
  399. //param.dataIndex:数值序列(X轴上当前点是第几个点)
  400. //alert(param.seriesName); //legend的名称
  401. // console.log(param.name); //X轴的值 0-600 1.用-分割获得确定最小值和最大值 2.筛选filteredData中this.selectedDataKey的值符合要求的
  402. const result = param.name.split("-").map(Number);
  403. const filteredArray = filteredData.filter((item) => {
  404. const isMaxValEqual = result[1] === maxVal;
  405. const lowerBoundCheck = item[that.selectedDataKey] >= result[0];
  406. const upperBoundCheck = isMaxValEqual
  407. ? item[that.selectedDataKey] <= result[1]
  408. : item[that.selectedDataKey] < result[1];
  409. return lowerBoundCheck && upperBoundCheck;
  410. });
  411. that.tableData = filteredArray.map((item) => {
  412. return {
  413. industryName: item.code,
  414. enterpriseId: item.enterpriseName,
  415. year: item.year,
  416. value: item[that.selectedDataKey],
  417. };
  418. });
  419. // console.log(filteredData, result, that.selectedDataKey, filteredArray); //获取自定义的值
  420. });
  421. },
  422. generateQuadraticData(binCount, maxHistogramValue, totalHistogramValue) {
  423. const a = -0.01;
  424. const h = binCount / 2;
  425. const k = maxHistogramValue * 0.75;
  426. const sideValue = (maxHistogramValue * 0.15) / 2;
  427. const data = [];
  428. for (let i = 0; i < binCount; i++) {
  429. const x = i;
  430. const y = a * (x - h) ** 2 + k;
  431. // 调整两侧的值
  432. if (i === 0 || i === binCount - 1) {
  433. data.push([x, sideValue]);
  434. } else {
  435. data.push([x, y]);
  436. }
  437. }
  438. return data;
  439. },
  440. },
  441. watch: {
  442. // littlegreen - 关闭监测数值变化
  443. selectedDataKey() {
  444. this.label = "评估指标值"+this.keyToUnit[this.selectedDataKey]
  445. },
  446. // selectedYear() {
  447. // this.updateChart();
  448. // },
  449. // selectedRange() {
  450. // setTimeout(this.updateChart, 2500);
  451. // },
  452. // selectedCode() {
  453. // this.updateChart();
  454. // },
  455. },
  456. };
  457. </script>
  458. <style scoped>
  459. .select-container {
  460. margin: 10px 0;
  461. font-size: 16px; /* 调整字号大小 */
  462. }
  463. .select-container select {
  464. padding: 10px 20px; /* 增加内边距 */
  465. margin-right: 10px; /* 增加右边距 */
  466. border: 1px solid #121315; /* 蓝色边框 */
  467. border-radius: 4px; /* 圆角边框 */
  468. background-color: white; /* 背景色 */
  469. color: #121315; /* 文字颜色 */
  470. font-size: 16px; /* 调整字号大小 */
  471. cursor: pointer; /* 鼠标悬停时的指针样式 */
  472. outline: none; /* 移除焦点时的轮廓 */
  473. }
  474. .select-container select:hover {
  475. border-color: #1a7cc8; /* 鼠标悬停时的边框颜色 */
  476. }
  477. .select-container select:focus {
  478. border-color: #121315; /* 焦点时的边框颜色 */
  479. box-shadow: 0 0 0 2px rgba(64, 159, 255, 0.2); /* 焦点时的阴影效果 */
  480. }
  481. </style>