首页,生态调查,智能告警页面线上接口对接,二级页面开发
@ -7,7 +7,7 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台'
|
||||
VITE_APP_PORT = 3000
|
||||
VITE_APP_BASE_API = '/dev-api'
|
||||
# 本地环境
|
||||
VITE_APP_BASE_URL = 'http://localhost:8093'
|
||||
# VITE_APP_BASE_URL = 'http://10.84.121.122:8093'
|
||||
# 测试环境
|
||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
|
||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
|
||||
|
||||
@ -8,3 +8,102 @@ export function getBaseWbsb(data: any) {
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 水电开发及环境监测 - 获取列表数据
|
||||
export function getVmsstbprptKendoList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 环保设施建设情况
|
||||
export function environmentalProtectionConstruction(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/operat/GetKendoListCust',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 环保设施弹框表格
|
||||
export function envOpdGetKendoListCust(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/oped/GetKendoListCust',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取基地列表
|
||||
export function getBaseList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取设施介绍列表
|
||||
export function getMsstbprptKendoList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 环保自动监测工作开展情况
|
||||
export function baseEvnmAutoMonitorGetKendoListCust(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 环保自动监测弹窗列表
|
||||
export function vmsstbprptGetKendoList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取基本信息
|
||||
export function baseMsstbprptGetKendoList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 降水量实时数据
|
||||
export function getBaseStbprpDataPPTNR(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=PPTN_R',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 气温气压实时数据
|
||||
export function getBaseStbprpDataTMPR(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=TMP_R',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 风速风向实时数据
|
||||
export function getBaseStbprpDataWNDR(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=WND_R',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
@ -547,7 +547,7 @@ export function getFlowDischargeYear(data: any) {
|
||||
}
|
||||
export function getFlowDischargeList(code: string, data: any) {
|
||||
return request({
|
||||
url: `/apo/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=${code}`,
|
||||
url: `/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=${code}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@ import request from '@/utils/request';
|
||||
//水生生态调查情况-下拉选择框
|
||||
export function wbsbGetKendoList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
|
||||
url: '/eq/wbsb/GetKendoList',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -12,7 +12,7 @@ export function wbsbGetKendoList(data: any) {
|
||||
// 水生生态调查情况-获取默认年份
|
||||
export function getDftYear(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/year/qgc/GetKendoListCust',
|
||||
url: '/wte/we/fisht/year/qgc/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -21,7 +21,7 @@ export function getDftYear(data: any) {
|
||||
// 水生生态调查情况-获取统计数据
|
||||
export function getWeFisht(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/statics/GetKendoListCust',
|
||||
url: '/wte/we/fisht/statics/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -30,7 +30,7 @@ export function getWeFisht(data: any) {
|
||||
// 水生生态调查概况-表格数据接口
|
||||
export function getWeStaticsSecond(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/statics/getWeStaticsSecond',
|
||||
url: '/wte/we/fisht/statics/getWeStaticsSecond',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -39,7 +39,7 @@ export function getWeStaticsSecond(data: any) {
|
||||
// 沿程鱼类变化-图表数据接口
|
||||
export function fishChanges2(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/stat/GetKendoListCust',
|
||||
url: '/wte/we/fisht/stat/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -48,7 +48,7 @@ export function fishChanges2(data: any) {
|
||||
// 沿程鱼类变化-详情表格接口
|
||||
export function fishDetail(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/detail/GetKendoListCust',
|
||||
url: '/wte/we/fisht/detail/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -57,7 +57,7 @@ export function fishDetail(data: any) {
|
||||
// 沿程鱼类变化-Tab数量统计接口
|
||||
export function getTabNum(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/statTab/GetKendoListCust',
|
||||
url: '/wte/we/fisht/statTab/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -66,11 +66,11 @@ export function getTabNum(data: any) {
|
||||
// 沿程鱼类变化-获取断面列表
|
||||
export function getStcdList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/vmsstbprpt/noWqDtinType0Auth/GetKendoList',
|
||||
url: '/eq/base/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: {
|
||||
filter: data,
|
||||
orders: JSON.stringify({ siteStepSort: 'asc' })
|
||||
orders: { siteStepSort: 'asc' }
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -78,7 +78,16 @@ export function getStcdList(data: any) {
|
||||
// 沿程鱼类变化-基地模式详情表格接口
|
||||
export function fishQgcDetail(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/we/fisht/qgcDetail/GetKendoListCust',
|
||||
url: '/wte/we/fisht/qgcDetail/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态调查-鱼类列表统计
|
||||
export function getFishListStat(data: any) {
|
||||
return request({
|
||||
url: '/wte/we/fishList/stat/qgc/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -87,7 +96,7 @@ export function fishQgcDetail(data: any) {
|
||||
// 陆生生态监测情况-获取统计数据
|
||||
export function getEvnmAutoMonitor(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
|
||||
url: '/wte/base/evnmAutoMonitor/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -96,7 +105,7 @@ export function getEvnmAutoMonitor(data: any) {
|
||||
// 陆生生态监测-红外相机信息表格
|
||||
export function getVmsstbprptList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||
url: '/wte/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -105,7 +114,7 @@ export function getVmsstbprptList(data: any) {
|
||||
// 陆生生态监测-重点陆生动物表格
|
||||
export function getTetSpecList(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/te/tet/spec/GetKendoListCust',
|
||||
url: '/wte/te/tet/spec/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -114,7 +123,7 @@ export function getTetSpecList(data: any) {
|
||||
// 野生动物监测-获取运行数据
|
||||
export function getWvaData(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/wva/GetKendoListCust',
|
||||
url: '/wte/wva/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -123,7 +132,7 @@ export function getWvaData(data: any) {
|
||||
// 野生动物监测-获取默认数据
|
||||
export function getWvaDftData(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/wva/default/GetKendoListCust',
|
||||
url: '/wte/wva/default/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
@ -132,7 +141,25 @@ export function getWvaDftData(data: any) {
|
||||
// 野生动物监测-获取默认年份列表
|
||||
export function getWvaDftTm(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/wva/year/GetKendoListCust',
|
||||
url: '/wte/wva/year/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态调查对比-获取断面和批次列表
|
||||
export function getFishtKendoList(data: any) {
|
||||
return request({
|
||||
url: '/wte/we/fisht/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态调查对比-获取对比柱状图数据
|
||||
export function getCompareBar(data: any) {
|
||||
return request({
|
||||
url: '/wte/we/compareBar/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
@ -39,6 +39,13 @@ export function vmsstbprptGetKendoList(queryParams: any) {
|
||||
data: queryParams
|
||||
});
|
||||
}
|
||||
export function vmsstbprptGetKendoList1(queryParams: any) {
|
||||
return request({
|
||||
url: '/eq/vmsstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data: queryParams
|
||||
});
|
||||
}
|
||||
//生态流量达标情况
|
||||
export function eqqecRateCount(queryParams: any) {
|
||||
return request({
|
||||
|
||||
@ -15,6 +15,13 @@ export function msstbprptGetKendoList(data: any) {
|
||||
data
|
||||
});
|
||||
}
|
||||
export function VDmsstbprptGetKendoList(data: any) {
|
||||
return request({
|
||||
url: '/vd/msstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
//获取鱼类介绍
|
||||
export function fishDicGetKendoList(data: any) {
|
||||
return request({
|
||||
|
||||
91
frontend/src/api/zngj/index.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// AI识别大坝环境-获取统计数据(Tab数量)
|
||||
export function getAisbdbyxList(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgcDmRun/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别大坝环境-获取饼图+表格数据
|
||||
export function getAiChartTable(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别大坝环境-获取电站列表
|
||||
export function getAiRstcd(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgc/aiRstcd/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别过鱼设施运行-获取统计数据
|
||||
export function getAiGyssList(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgcProtect/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量低于限值告警-获取统计数据
|
||||
export function getStllGaojingList(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/warn/stcd/eng/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量低于限值告警-获取表格详情数据
|
||||
export function getStllGaojingDetail(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/warn/stcd/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水质监测数据告警-获取统计数据
|
||||
export function getShuizhiGaojingList(data: any) {
|
||||
return request({
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别大坝环境-获取漂浮物/清漂日历数据
|
||||
export function getAiPfwRecord(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgc/aiPfwRecord/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别大坝环境-获取叠梁门日历数据
|
||||
export function getAiRecord(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgc/aiRecord/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI识别大坝环境-获取日历详情视频数据
|
||||
export function getAiFile(data: any) {
|
||||
return request({
|
||||
url: '/api/wmp-env-server/env/ai/com/qgc/aiFile/GetKendoListCust',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
4
frontend/src/assets/svg/diwenshuijianhuan.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg t="1686656066409" class="icon" viewBox="0 0 1125 1024" version="1.1" p-id="8027" width="200px" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 30.838 832.939 C 50.02 828.843 69.932 838.571 77.414 856.645 L 81.464 865.553 C 105.989 914.859 157.741 946.705 215.059 946.757 L 225.073 946.449 C 281.324 942.712 330.824 908.254 351.919 857.26 L 354.337 852.497 C 363.718 835.933 385.453 828.642 404.401 835.704 L 409.969 838.418 C 418.957 843.814 425.415 852.065 428.082 861.56 L 432.582 870.418 C 457.895 916.651 507.621 946.757 563.027 946.757 C 622.204 946.705 675.642 912.401 698.593 859.717 L 701.518 855.212 L 704.387 851.115 C 708.712 844.518 715.206 839.324 722.949 836.267 L 728.181 834.732 C 748.135 830.289 768.547 840.308 775.431 857.925 L 779.763 867.294 C 794.916 897.625 821.636 921.927 854.971 935.697 C 929.953 966.11 1016.355 932.114 1047.911 859.717 L 1048.08 859.205 C 1057.136 839.544 1080.423 830.892 1100.393 839.391 L 1105.624 842.104 C 1122.559 852.451 1129.395 872.075 1122.105 889.413 C 1108.341 921.238 1086.593 949.694 1058.598 972.509 C 962.24 1050.589 818.52 1038.506 737.631 945.527 L 729.193 954.744 C 723.357 960.686 717.16 966.327 710.63 971.64 C 614.497 1050.334 470.439 1038.916 388.931 946.142 L 379.481 956.279 C 357.028 978.957 329.35 996.841 298.424 1008.657 C 181.14 1053.149 48.669 997.495 2.544 884.395 L 0.969 878.917 C -3.323 860.632 6.954 842.094 25.662 834.373 Z M 544.971 0.023 C 636.098 -1.411 711.868 63.459 718.675 146.709 L 719.125 156.029 L 719.125 331.388 C 728.125 338.043 736.562 345.365 744.268 353.352 L 752.594 362.568 C 843.888 463.944 832.582 613.139 729.08 701.612 L 717.886 710.675 C 605.682 797.944 436.511 783.877 343.536 679.546 C 251.848 577.863 262.986 428.053 366.713 338.914 L 376.725 330.722 L 376.725 156.029 C 378.356 70.525 452.663 1.559 544.916 0.023 Z M 546.21 64.022 C 494.965 64.893 452.663 101.603 447.545 148.144 L 447.038 156.029 L 447.038 360.212 L 422.401 379.361 C 338.587 444.59 327.561 560.967 397.876 638.944 C 463.053 712.486 580.631 725.591 663.379 668.537 L 673.561 661.011 C 754 598.752 767.219 488.928 705.399 411.669 L 696.061 400.763 C 691.954 395.945 687.44 391.426 682.561 387.246 L 675.08 381.256 L 648.81 362.056 L 648.81 157.104 C 647.854 107.184 606.06 67.299 554.421 64.227 L 546.21 64.022 Z M 547.784 192.022 L 553.072 192.38 C 570.284 194.735 583.447 208.252 583.503 224.534 L 583.503 436.552 C 613.879 446.177 637.561 467.989 647.854 495.687 L 650.386 503.264 C 664.467 552.032 635.116 602.481 582.884 619.283 L 574.334 621.536 C 494.627 638.949 424.1 571.296 447.385 499.76 C 456.868 470.627 480.812 447.228 512.064 436.552 L 512.064 224.584 C 512.096 206.632 528.06 192.078 547.784 192.022 Z" p-id="8028" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
16
frontend/src/assets/svg/guojiashuiwenzhan.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>guojiashuiwenzhan</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="画板" transform="translate(-304.000000, -319.000000)">
|
||||
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
|
||||
<g id="ControlRates备份-2" transform="translate(121.000000, 157.000000)">
|
||||
<g id="guojiashuiwenzhan" transform="translate(6.000000, 6.000000)">
|
||||
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
|
||||
<polygon id="路径" fill="#FFFFFF" fill-rule="nonzero" points="3 14 8 0 13 14"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 931 B |
13
frontend/src/assets/svg/guojiashuizhizhan.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>guojiashuizhizhan</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="画板" transform="translate(-304.000000, -250.000000)" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
|
||||
<g id="ControlRates备份-5" transform="translate(121.000000, 88.000000)">
|
||||
<path d="M14.0127685,18.7975456 C15.186503,18.7996035 16.3093298,18.3553149 17.1170051,17.5692308 C17.9474718,16.7610359 18.4087696,15.6886498 18.405125,14.5747692 C18.405125,12.2387692 16.6849722,9.84000002 14.014102,6 C11.3352311,9.84 9.62310059,12.24 9.62310059,14.5747692 C9.62310059,15.6984616 10.087136,16.7766154 10.9111988,17.5692308 C11.7185556,18.3550038 12.8408306,18.7992648 14.014102,18.7975456 L14.0127685,18.7975456 Z M13.9807661,8.72123079 C14.3341264,9.24430771 14.5274744,9.85353848 14.5434757,10.48 C14.5434757,12.56 12.3286402,12.9107692 12.3286402,11.6307692 C12.3606426,10.8 13.4513924,9.49046156 13.9820995,8.71876924 L13.9820995,8.72123079 L13.9807661,8.72123079 Z M14.014102,21.9975385 C12.0619532,21.9975385 9.2844081,21.5175385 7.9949765,20.1095385 C8.5283505,19.8166154 9.09372692,19.5803077 9.68177174,19.4055385 C10.4084938,19.8855385 11.8952738,20.3975385 14.014102,20.3975385 C16.1315967,20.3975385 17.6183767,19.8855385 18.3784346,19.4055385 C18.9664794,19.5815385 19.5331893,19.8178462 20.0678967,20.112 C18.7464627,21.52 15.9689176,22 14.0167688,22 L14.014102,21.9975385 L14.014102,21.9975385 Z M19.9305529,13.136 C20.4612601,12.3027693 21.3853305,12.6867692 21.8520327,11.9187692 C22.4147423,11.0227692 21.2559873,10.0947693 20.3319169,10.8947692 C19.8332122,11.3501538 19.734538,12.2067692 19.9318864,13.1347692 L19.9305529,13.136 Z M7.66561805,10.8898462 C6.77221663,10.0578462 5.58279261,10.9858462 6.1455022,11.9138462 C6.64020657,12.6806154 7.53494144,12.2978462 8.06564855,13.1298462 C8.29366592,12.2067692 8.19499175,11.3489231 7.66561805,10.8935385 L7.66561805,10.8898462 Z" id="guojiashuizhizhan"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
6
frontend/src/assets/svg/liuliangjiancezhan.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg t="1686656243919" class="icon" viewBox="0 0 1024 1024" version="1.1" p-id="8195" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 1001.523 839.355 C 983.397 830.856 962.199 839.509 954.16 858.709 C 954.019 859.048 953.882 859.389 953.751 859.733 C 925.077 932.077 846.431 966.073 778.126 935.661 C 744.625 920.365 718.592 892.363 705.777 857.838 C 698.097 838.024 676.848 828.399 658.005 836.283 C 650.963 839.326 645.054 844.502 641.108 851.08 C 639.419 853.538 637.626 856.661 635.886 859.681 C 614.945 912.365 566.302 946.668 512.437 946.72 C 458.879 946.72 411.107 912.723 389.551 861.525 C 386.669 850.094 378.761 840.585 368.046 835.669 C 349.254 827.784 327.955 837.359 320.274 857.173 C 299.947 911.239 250.587 946.668 195.699 946.72 C 140.502 946.668 90.887 910.983 70.407 856.609 C 62.982 836.898 42.091 827.068 23.3 834.389 C 4.662 842.274 -4.657 864.443 2.307 884.41 C 44.293 997.509 164.824 1053.162 271.58 1008.67 C 303.882 995.076 332.235 973.558 354.016 946.105 C 428.209 1038.878 559.338 1050.295 646.792 971.602 C 655.699 963.6 663.92 954.865 671.369 945.491 C 744.999 1038.468 875.821 1050.551 963.53 972.473 C 988.773 950.048 1008.589 921.53 1021.338 889.377 C 1028.772 870.087 1020.151 848.322 1001.523 839.355" p-id="8196" fill="#ffffff"/>
|
||||
<path d="M 1001.523 647.359 C 983.397 638.86 962.199 647.513 954.16 666.713 C 954.019 667.052 953.882 667.393 953.751 667.737 C 925.077 740.081 846.431 774.077 778.126 743.665 C 744.625 728.369 718.592 700.367 705.777 665.842 C 698.097 646.028 676.848 636.403 658.005 644.287 C 650.963 647.33 645.054 652.506 641.108 659.084 C 639.419 661.542 637.626 664.665 635.886 667.685 C 614.945 720.369 566.302 754.672 512.437 754.724 C 458.879 754.724 411.107 720.727 389.551 669.529 C 386.669 658.098 378.761 648.589 368.046 643.673 C 349.254 635.788 327.955 645.363 320.274 665.177 C 299.947 719.243 250.587 754.672 195.699 754.724 C 140.502 754.672 90.887 718.987 70.407 664.613 C 62.982 644.902 42.091 635.072 23.3 642.393 C 4.662 650.278 -4.657 672.447 2.307 692.414 C 44.293 805.513 164.824 861.166 271.58 816.674 C 303.882 803.08 332.235 781.562 354.016 754.109 C 428.209 846.882 559.338 858.299 646.792 779.606 C 655.699 771.604 663.92 762.869 671.369 753.495 C 744.999 846.472 875.821 858.555 963.53 780.477 C 988.773 758.052 1008.589 729.534 1021.338 697.381 C 1028.772 678.091 1020.151 656.326 1001.523 647.359" p-id="8197" fill="#ffffff"/>
|
||||
<path d="M 486.067 0 C 493.031 0 499.892 2.918 504.705 8.089 L 523.343 28.723 L 544.95 54.168 L 557.853 69.989 L 579 97.124 L 593.849 117.246 L 609.056 138.749 L 624.314 161.379 C 632.25 173.513 640.033 186.108 647.56 198.959 L 659.644 182.831 L 710.334 116.99 L 761.024 182.78 C 776.283 202.492 790.722 222.715 804.342 243.758 C 847.608 310.829 871.418 369.349 871.418 418.347 C 871.264 508.559 799.529 582.593 710.232 582.593 C 689.034 582.593 668.143 578.343 648.789 570.203 L 646.792 572.353 C 630.3 589.037 611.36 603.108 590.623 614.08 C 563.154 628.721 532.895 637.376 501.838 639.475 L 486.169 639.987 C 424.662 639.992 365.829 614.832 323.347 570.356 C 279.817 525.2 255.64 464.834 255.964 402.116 C 255.964 331.052 291.959 242.837 362.874 139.773 L 370.555 128.714 L 385.864 107.569 L 400.662 87.96 L 414.691 70.04 L 433.636 46.745 L 449.304 28.313 L 467.43 8.192 C 471.017 4.302 475.722 1.619 480.896 0.512 L 486.067 0 Z M 486.374 83.454 L 482.79 87.806 C 459.046 116.192 436.629 145.663 415.613 176.124 C 351.353 269.511 319.967 346.463 319.967 402.168 C 319.967 449.117 337.53 492.892 369.326 525.813 C 400.815 558.324 442.034 575.988 486.169 575.988 C 512.59 575.988 537.424 569.844 560.823 557.454 C 573.112 551.003 584.684 542.811 595.232 533.186 C 565.653 502.252 549.162 461.093 549.2 418.295 C 549.2 373.65 570.193 318.969 607.469 258.145 L 597.945 241.045 C 566.996 187.231 531.229 136.336 491.085 88.984 L 486.374 83.454 Z M 710.386 221.845 L 696.407 240.481 C 688.88 250.824 680.637 262.599 672.393 275.246 C 634.247 333.766 613.204 384.658 613.204 418.295 C 613.204 454.237 631.943 487.619 662.204 505.436 C 676.694 514.089 693.386 518.594 710.232 518.594 C 763.738 518.594 807.312 473.59 807.363 418.244 C 807.363 384.453 787.701 336.07 750.529 278.471 L 741.927 265.364 L 733.376 253.025 L 717.452 231.112 L 710.386 221.845 Z" p-id="8198" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
16
frontend/src/assets/svg/qixiangzhan.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>qixiangzhan</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="画板" transform="translate(-522.000000, -250.000000)">
|
||||
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
|
||||
<g id="ControlRates备份-7" transform="translate(339.000000, 88.000000)">
|
||||
<g id="qixiangzhan" transform="translate(6.000000, 6.000000)">
|
||||
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M4.18299415,15.9992261 L4.18299412,15.9992261 C3.83124298,16.0006517 3.54495776,15.7117042 3.54355562,15.353845 C3.54355359,15.3533269 3.54355217,15.3528089 3.54355137,15.3522908 L3.54355136,15.3522926 C3.54410794,14.9944303 3.82971065,14.7047834 4.18146487,14.7053506 C4.18197411,14.7053514 4.18248336,14.7053529 4.1829926,14.7053549 L5.59022295,14.7053549 L5.59022295,12.9351129 L5.59022322,12.9351131 C4.92309208,12.6326872 4.31659847,12.2075125 3.80125929,11.6809803 C1.0497029,8.89944383 1.91133518,4.43178623 3.3446963,2.9846379 C4.47215455,1.84559646 6.52618578,2.47034106 8.41051975,3.86201396 L8.41051975,3.86201396 C8.48839992,3.9188462 8.52814235,4.0154854 8.5132465,4.11180806 L8.34609399,5.18994934 L8.21648072,5.2493013 L8.2164807,5.2493013 C8.12524474,5.29020431 8.01913074,5.2752241 7.94228952,5.2105935 C6.43563014,3.92033441 4.93530726,3.36190221 4.31286797,3.94949393 C3.55192865,4.66636217 4.38896191,6.82419779 6.18350306,8.76835318 C7.97805712,10.7125086 10.0478021,11.7073039 10.8087507,10.9899169 C11.3961948,10.4371704 11.0314528,9.02304391 10.007736,7.5129299 L10.1299935,7.28223175 L11.2843381,6.90547665 C12.4909335,8.8098996 12.9216314,10.905282 11.8438823,11.9945103 C10.9008261,12.9472373 8.90868096,13.7172702 6.87089489,13.3363793 L6.87089489,14.7061189 L9.30260071,14.7061189 L9.30260068,14.7061189 C9.65477108,14.7045498 9.94151088,14.9937268 9.94305431,15.3520141 C9.9430558,15.352361 9.94305702,15.352708 9.94305797,15.353055 L9.94305799,15.3530468 C9.94208524,15.7113356 9.65580525,16.000984 9.30363178,15.9999975 C9.30329072,15.9999975 9.30294967,15.9999975 9.30260861,15.9999975 L4.18299415,15.9992261 Z M7.50575723,7.98025697 L7.36219336,7.83523184 L7.36219336,7.83523184 C7.09216098,7.57607102 7.07976134,7.14327333 7.3344975,6.86855076 C7.35873317,6.8424136 7.38495609,6.8182604 7.41292255,6.79631553 L8.92795176,5.54347337 L8.92795181,5.54347332 C9.16901074,5.35273729 9.49064738,5.30436933 9.77563796,5.41599578 L9.77563797,5.4159958 C9.67430292,5.28150984 9.6197855,5.11656818 9.62065934,4.94711549 L9.62065934,4.94711537 C9.6137222,4.53702595 9.9339031,4.19836431 10.336956,4.18947567 L10.3369559,4.18947567 C10.7404296,4.19766401 11.0612277,4.53661816 11.0540143,4.94711537 L11.0540143,4.94711598 C11.0619159,5.35691948 10.7417826,5.69564674 10.3389754,5.70368542 C10.3383024,5.70369885 10.3376292,5.70371133 10.3369561,5.70372287 L10.3369561,5.70372287 C10.1781212,5.70409339 10.023995,5.64887913 9.90043162,5.54734298 L9.90043163,5.54734302 C10.0095216,5.8340025 9.96126958,6.15799768 9.77360851,6.39891408 L8.53353222,7.92941929 L8.53353223,7.92941928 C8.40386137,8.09512059 8.20753406,8.19233305 7.99935246,8.19392203 L7.9993525,8.19392203 C7.81323194,8.19271444 7.63535221,8.11566084 7.50550305,7.97999651 L7.50575723,7.98025697 Z M13.9666402,5.69907111 C13.9666402,3.1598353 12.0178815,1.09310774 9.62269583,1.09310774 L9.62269583,0 C12.5880746,0 15,2.55651622 15,5.69881643 L13.9666402,5.69907111 Z M12.1375932,5.6990789 L12.1375932,5.6990789 C12.141703,4.99638806 11.8773236,4.31956162 11.4002445,3.8114301 L11.4002446,3.81143013 C10.9389177,3.31261691 10.2959024,3.02930569 9.62269125,3.02824325 L9.62269125,1.93719946 C11.5813555,1.93719946 13.1737435,3.62485171 13.1737435,5.6990789 L12.1375932,5.6990789 Z" id="形状" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
9
frontend/src/assets/svg/qixidi.svg
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
4
frontend/src/assets/svg/shipinjiankongshebei.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg t="1686655935321" class="icon" viewBox="0 0 1137 1024" version="1.1" p-id="7860" width="200px" height="200" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 147.788 0 L 1095.449 263.002 L 1020.134 361.087 L 1137 392.397 L 1058.218 686.879 L 819.484 622.78 L 744.226 720.978 L 404.882 625.854 L 404.882 833.466 L 73.667 833.466 L 73.667 1024 L 0 1024 L 0 642.989 L 73.61 642.989 L 73.61 757.241 L 331.329 757.241 L 331.272 605.246 L 9.947 515.245 L 147.788 0 Z M 851.77 274.387 L 605.078 602.855 L 717.34 634.279 L 968.465 306.779 L 851.77 274.33 Z M 970 426.496 L 869.618 557.371 L 1006.72 594.145 L 1046.111 446.933 L 969.943 426.439 Z M 198.945 93.189 L 100.382 461.506 L 528.626 581.394 L 775.318 253.097 L 198.945 93.132 Z" p-id="7861" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 807 B |
1
frontend/src/assets/svg/shuizhijiancezhan.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686656318145" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8365" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M665.211904 0l0.512 307.8144c0 33.5872 7.8848 67.1744 22.2208 97.28l324.5056 460.6976 0.7168 2.048c16.4352 34.304 14.336 73.5744-6.4512 105.728a114.3296 114.3296 0 0 1-95.0784 50.432H112.507904c-38.6048 0-74.3424-18.944-95.0784-51.0976a107.4176 107.4176 0 0 1-6.4512-105.728l1.4336-2.816 307.3536-458.5472c14.336-30.8224 22.1696-64.4096 22.1696-97.9968L341.423104 0h323.7888z m-63.8464 64H405.525504l0.4096 243.8144c0 36.5568-7.168 73.216-20.7872 107.8272l-7.3216 17.1008-4.864 8.704L68.168704 896l-1.9968 5.12a42.8032 42.8032 0 0 0 1.8432 31.2832l3.2256 5.8368a47.7184 47.7184 0 0 0 33.9968 21.248l7.2704 0.512h799.1296a49.664 49.664 0 0 0 41.2672-21.0432 43.0592 43.0592 0 0 0 5.0688-37.12l-2.0992-5.2736-320.256-454.6048-5.5296-9.3696a293.376 293.376 0 0 1-27.8528-108.6464l-0.512-16.0256-0.3584-243.9168z" p-id="8366" fill="#ffffff"></path><path d="M256.072704 0h512v64H256.072704z" p-id="8367" fill="#ffffff"></path><path d="M365.231104 686.4896a83.968 83.968 0 0 1 83.6096 83.6096c0 43.008-33.1776 78.848-75.1104 83.0976l-8.4992 0.4608A83.968 83.968 0 0 1 281.672704 770.0992c0-43.008 33.1264-78.848 75.0592-83.1488l8.4992-0.4608z m245.0944-18.8416c51.5072 0 92.9792 41.472 92.9792 93.0304 0 48.4864-36.7104 88.064-83.968 92.5696l-9.0112 0.4096a92.7744 92.7744 0 0 1-93.0304-92.9792c0-48.5376 36.7616-88.064 84.0192-92.6208l9.0112-0.4096z m-245.0944 82.8416a19.968 19.968 0 0 0-19.5584 19.6096 19.968 19.968 0 0 0 19.5584 19.5584 19.968 19.968 0 0 0 19.6096-19.5584 19.968 19.968 0 0 0-19.6096-19.6096z m245.0944-18.8416a28.7744 28.7744 0 0 0-29.0304 29.0304c0 16.1792 12.8 28.9792 29.0304 28.9792 16.1792 0 28.9792-12.8 28.9792-28.9792a28.7744 28.7744 0 0 0-28.9792-29.0304zM502.242304 421.9392a116.3776 116.3776 0 0 1 116.224 116.224c0 60.928-47.616 111.0528-106.8032 115.9168l-9.4208 0.3584a116.3776 116.3776 0 0 1-116.224-116.224c0-60.928 47.0528-111.0016 106.7008-115.8656l9.5232-0.4096z m0 64c-28.7744 0-52.224 23.552-52.224 52.224 0 28.7744 23.4496 52.2752 52.224 52.2752 28.3648 0 52.224-23.7568 52.224-52.224 0-28.7744-23.5008-52.224-52.224-52.224z" p-id="8368" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
9
frontend/src/assets/svg/zengzhizhanshuizhizhan.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>zengzhizhanshuizhizhan</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="zengzhizhanshuizhizhan" transform="translate(0.750000, 0.000000)" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<path d="M23.25,0 C36.09,0 46.5,10.74525 46.5,24 C46.5,37.25475 36.09,48 23.25,48 C10.41,48 0,37.25475 0,24 C0,10.74525 10.41,0 23.25,0 Z M23.24475,6.6675 C13.74375,6.6675 6.04275,14.49225 6.04275,24.144 C6.04275,32.4705 11.775,39.432 19.44825,41.18925 L27.0405,41.18925 C34.71375,39.432 40.4475,32.4705 40.4475,24.144 C40.4475,14.4915 32.7465,6.6675 23.2455,6.6675 L23.24475,6.6675 Z M23.44275,8.361 C31.84275,8.361 38.65275,15.02025 38.65275,23.235 C38.65275,23.3325 38.64675,23.42775 38.64525,23.52525 L38.598,23.52525 C38.595,23.472 38.586,23.42025 38.5815,23.36775 C38.34075,27.62025 35.07225,30.98925 31.074,30.98925 C30.1862354,30.9895698 29.3064559,30.821605 28.48125,30.49425 L28.34475,30.5955 C26.9708322,29.6968211 25.3634685,29.2209226 23.72175,29.22675 C22.2366664,29.2236863 20.7773263,29.6144655 19.4925,30.35925 C18.35025,31.15725 16.99425,31.62225 15.53925,31.62225 C11.45925,31.62225 8.15175,27.98325 8.15175,23.49675 C8.15175,22.94175 8.202,22.40025 8.29875,21.87675 C9.00075,14.2995 15.5115,8.361 23.44275,8.361 L23.44275,8.361 Z M23.24775,11.0925 L23.24325,11.24475 L23.2395,11.0925 C23.238,11.08275 23.17875,11.18325 23.0745,11.37 L22.98825,11.52525 C22.6125,12.20775 21.86475,13.60275 21.10275,15.03075 L20.88675,15.43575 C20.2208976,16.6865315 19.5563972,17.9380322 18.89325,19.19025 C18.2049855,20.1371924 17.8354525,21.2783585 17.8379869,22.449 C17.8379869,25.482 20.25825,27.9405 23.24475,27.9405 C26.22975,27.9405 28.6500122,25.482 28.6500122,22.449 C28.6524544,21.2784908 28.2832171,20.1374264 27.5955,19.19025 C26.9682775,18.0058222 26.3397771,16.8220715 25.71,15.639 L25.278,14.829 C24.23775,12.879 23.25525,11.052 23.24775,11.0925 L23.24775,11.0925 Z M27.3,22.434 C27.30225,22.48275 27.306,22.5315 27.306,22.581 C27.306,24.768 25.764,26.54175 23.86125,26.54175 C22.623,26.54175 21.53775,25.791 20.93175,24.66225 C21.61425,25.38675 22.545,25.83375 23.57175,25.83375 C25.43475,25.83375 26.982,24.36675 27.3,22.434 L27.3,22.434 Z" id="形状"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
16
frontend/src/assets/svg/zijianshuiwenzhan.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>zijianshuiwenzhan</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="画板" transform="translate(-413.000000, -319.000000)">
|
||||
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
|
||||
<g id="ControlRates备份-3" transform="translate(230.000000, 157.000000)">
|
||||
<g id="zijianshuiwenzhan" transform="translate(6.000000, 6.000000)">
|
||||
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M9.97229576,12.7591246 C11.0763098,12.7591246 11.6579969,12.9166253 12.1726968,13.0531252 C12.6534766,13.1808752 13.0664219,13.2928757 13.9584577,13.2928757 L13.9584577,15 C12.8578363,15 12.2727566,14.8460002 11.7589154,14.7068753 C11.2781355,14.5791253 10.8643429,14.4671249 9.97230984,14.4671249 C9.08028104,14.4671249 8.66648695,14.5791249 8.1857043,14.7068753 C7.67185461,14.8425003 7.0876266,15 5.98616196,15 C4.88469309,15 4.30046086,14.8460002 3.78576092,14.7068753 C3.30498108,14.5791253 2.89118839,14.4671249 2,14.4671249 L2,12.7582428 C3.10062143,12.7582428 3.68485647,12.9131172 4.19954234,13.0522435 C4.68032218,13.1799934 5.09411486,13.2919939 5.98614788,13.2919939 C6.87817668,13.2919939 7.29197077,13.1799939 7.77275342,13.0522435 C8.28660311,12.9157434 8.87083112,12.7582428 9.97229576,12.7582428 L9.97229576,12.7591246 Z M9.99773387,10.0650032 C11.1000516,10.0650032 11.683435,10.2225068 12.1981349,10.3581308 C12.6789148,10.4867559 13.0927074,10.5987558 13.9847405,10.5987558 L13.9847405,12.3058801 C12.8832716,12.3058801 12.2990394,12.1510058 11.7843394,12.0127554 C11.3035596,11.8841304 10.8897669,11.7721305 9.9985785,11.7721305 C9.1065497,11.7721305 8.69275561,11.8841305 8.21197296,12.0127554 C7.69727583,12.1483804 7.11304782,12.3058801 6.01157192,12.3058801 C4.91095049,12.3058801 4.32671544,12.1510058 3.81202958,12.0127554 C3.33124974,11.8841304 2.91745705,11.7721305 2.02542403,11.7721305 L2.02542403,10.0658777 C3.1268929,10.0658777 3.71112513,10.2198776 4.22582508,10.3581279 C4.70660491,10.4867529 5.1203976,10.5987528 6.01158599,10.5987528 C6.90361479,10.5987528 7.31740888,10.4867528 7.79819153,10.3581279 C8.31288866,10.2225029 8.89711667,10.0650032 9.99859257,10.0650032 L9.99773387,10.0650032 Z M14,1 L14,8.84875684 L12.3456767,8.84875684 L12.3456767,2.70801044 L3.66450187,2.70801044 L3.66450187,8.84875684 L2.01017857,8.84875684 L2.01017857,1 L14,1 Z M8.88439241,4.12375034 L8.88439241,8.84875684 L7.22922448,8.84875684 L7.22922448,4.12375034 L8.88439241,4.12375034 Z" id="形状" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -3288,6 +3288,45 @@ const DwSixPointColumns: Array<any> = [
|
||||
url: ''
|
||||
}
|
||||
];
|
||||
//DW
|
||||
const DwPointColumnsyuan: Array<any> = [
|
||||
{
|
||||
name: '基本属性',
|
||||
visible: false,
|
||||
ruleTips: '',
|
||||
type: '',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '设施名称',
|
||||
filed: 'stnm',
|
||||
visible: true,
|
||||
type: 'input',
|
||||
url: ''
|
||||
},
|
||||
|
||||
{
|
||||
name: '站址',
|
||||
filed: 'stlc',
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '所属流域',
|
||||
filed: 'hbrvcdName',
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '所属电站',
|
||||
filed: 'ennm',
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
];
|
||||
//夹岩双层取水
|
||||
const DwOnePointColumns: Array<any> = [
|
||||
{
|
||||
@ -4145,8 +4184,7 @@ const EQPointColumns7: Array<any> = [
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
|
||||
}
|
||||
];
|
||||
//阶段属性
|
||||
const basicFilterColumns: Array<any> = [
|
||||
@ -4831,6 +4869,58 @@ const tableColumns5: Array<any> = [
|
||||
setting: {}
|
||||
}
|
||||
];
|
||||
//气象监测
|
||||
const MMColumns: Array<any> = [
|
||||
{
|
||||
name: '基本属性',
|
||||
visible: false,
|
||||
ruleTips: '',
|
||||
type: '',
|
||||
url: ''
|
||||
}, {
|
||||
name: '测站名称',
|
||||
filed: 'stnm',
|
||||
visible: true,
|
||||
type: 'input',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '测站类型',
|
||||
filed: 'sttpName',
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '站址',
|
||||
filed: 'addvcdName',
|
||||
visible: true,
|
||||
type: 'input',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '所属流域',
|
||||
filed: 'hbrvcdName',
|
||||
visible: true,
|
||||
type: 'select',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '所在水系',
|
||||
filed: 'addvcdName',
|
||||
visible: true,
|
||||
type: 'input',
|
||||
url: ''
|
||||
},
|
||||
{
|
||||
name: '建成日期',
|
||||
filed: 'jcdt',
|
||||
visible: true,
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
url: ''
|
||||
},
|
||||
];
|
||||
|
||||
//生态流量
|
||||
const EcologicalFlowColumns: Array<any> = [];
|
||||
@ -4873,6 +4963,7 @@ export {
|
||||
DwFivePointColumns,
|
||||
DwSixPointColumns,
|
||||
DwOnePointColumns,
|
||||
DwPointColumnsyuan,
|
||||
EQPointColumns2,
|
||||
EQPointColumns5,
|
||||
EQPointColumns3,
|
||||
@ -4886,5 +4977,6 @@ export {
|
||||
tableColumns2,
|
||||
tableColumns3,
|
||||
tableColumns4,
|
||||
tableColumns5
|
||||
tableColumns5,
|
||||
MMColumns
|
||||
};
|
||||
|
||||
@ -142,6 +142,8 @@ import {
|
||||
DwFivePointColumns,
|
||||
DwSixPointColumns,
|
||||
DwOnePointColumns,
|
||||
DwPointColumnsyuan,
|
||||
MMColumns,
|
||||
StinfoAiVidoPointColumns
|
||||
} from '../column.config';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
@ -318,6 +320,14 @@ const columnsConfig = ref([
|
||||
{
|
||||
type: 'ZQ',
|
||||
columns: FhZQPointColumns
|
||||
},
|
||||
{
|
||||
type: 'DW',
|
||||
columns: DwPointColumnsyuan
|
||||
},
|
||||
{
|
||||
type:'MM',
|
||||
columns:MMColumns
|
||||
}
|
||||
|
||||
//DW_1
|
||||
|
||||
@ -199,14 +199,14 @@ const ENGEQTabs: Array<any> = [
|
||||
].filter(Boolean);
|
||||
|
||||
// // 水电站生态流量 - 江局单独看 √
|
||||
// const ENGEQTabsJuangJu: Array<any> = [
|
||||
// {
|
||||
// name: '生态流量',
|
||||
// key: 'EcologicalFlow',
|
||||
// type: 'EcologicalFlow'
|
||||
// },
|
||||
|
||||
// ].filter(Boolean)
|
||||
const ENGEQTabsJuangJu: Array<any> = [
|
||||
{
|
||||
name: '生态流量',
|
||||
key: 'EcologicalFlow',
|
||||
type: 'EcologicalFlow',
|
||||
default: true
|
||||
}
|
||||
].filter(Boolean);
|
||||
// //生态流量泄放设施 √
|
||||
const EQTabs: Array<any> = [
|
||||
{
|
||||
@ -311,8 +311,7 @@ const FPTabs1: Array<any> = [
|
||||
key: 'basicInfo',
|
||||
type: 'basic',
|
||||
default: true, // 默认显示
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
||||
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
||||
},
|
||||
{
|
||||
name: '实时视频',
|
||||
@ -956,15 +955,23 @@ const CommonTabs: any = [
|
||||
}
|
||||
];
|
||||
|
||||
// // 气象站
|
||||
// const WeatherTabs: any = [
|
||||
// {
|
||||
// name: '基础信息',
|
||||
// key: 'basicInfo',
|
||||
// type: 'basic',
|
||||
// url: '/bbi/siteBipc/getSiteBasicInfo'
|
||||
// }
|
||||
// ]
|
||||
// 气象站
|
||||
const WeatherTabs: any = [
|
||||
{
|
||||
name: '基础信息',
|
||||
key: 'basicInfo',
|
||||
type: 'basic',
|
||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
||||
default: true // 默认显示
|
||||
},
|
||||
{
|
||||
name: '监测数据',
|
||||
key: '',
|
||||
type: '',
|
||||
url: ''
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
// const WaterQualityMonitoring: any = [
|
||||
// {
|
||||
@ -1033,6 +1040,8 @@ const handleTabs = (modaldata: any) => {
|
||||
const sttp = modaldata?.sttp ? modaldata?.sttp.toUpperCase() : '';
|
||||
console.log('sttp', sttp);
|
||||
switch (sttp) {
|
||||
case 'ENGJJ':
|
||||
return ENGEQTabsJuangJu;
|
||||
case 'ENG':
|
||||
if (modaldata?.eqtp == 'QEC') {
|
||||
return ENGEQTabs;
|
||||
@ -1223,8 +1232,8 @@ const handleTabs = (modaldata: any) => {
|
||||
// }
|
||||
// case 'WE':
|
||||
// return WETabs
|
||||
// case 'MM':
|
||||
// return WeatherTabs
|
||||
case 'MM':
|
||||
return WeatherTabs
|
||||
// case 'WARN':
|
||||
// return WaterQualityMonitoring
|
||||
// case 'AI':
|
||||
|
||||
@ -47,7 +47,6 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="title_right">
|
||||
|
||||
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
||||
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
|
||||
<img v-else src="@/assets/components/arrow-down.png" alt="" />
|
||||
@ -88,7 +87,7 @@
|
||||
:dropdown-style="{
|
||||
maxHeight: '400px',
|
||||
overflow: 'auto',
|
||||
minWidth: '200px'
|
||||
minWidth: select.minWidth ? select.minWidth : '200px'
|
||||
}"
|
||||
:options="select.options"
|
||||
:filter-option="filterOption"
|
||||
@ -190,6 +189,7 @@ interface SelectConfig {
|
||||
value?: string | undefined;
|
||||
options?: SelectProps['options'];
|
||||
width?: string;
|
||||
minWidth?: string;
|
||||
}
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
@ -248,7 +248,8 @@ const props = defineProps({
|
||||
show: false,
|
||||
value: undefined,
|
||||
options: [],
|
||||
width: ''
|
||||
width: '',
|
||||
minWidth: ''
|
||||
})
|
||||
},
|
||||
moreSelect: {
|
||||
@ -690,7 +691,6 @@ const handleTreeExpand = (keys: string[]) => {
|
||||
|
||||
// 下拉选择框事件处理
|
||||
const handleChange = (value: string) => {
|
||||
|
||||
// Handle change
|
||||
};
|
||||
|
||||
|
||||
@ -1,49 +1,29 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<!-- developStatusChart/index.vue -->
|
||||
<template>
|
||||
<div class="basic_body">
|
||||
<div ref="chartContainer" class="chart-container"></div>
|
||||
|
||||
<!-- 水电开发情况弹框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="水电开发情况"
|
||||
:width="1540"
|
||||
@ok="handleModalOk"
|
||||
@cancel="handleModalCancel"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<!-- 内容区域,供用户自定义 -->
|
||||
<!-- <p>选中类型:{{ selectedType }}</p> -->
|
||||
<SDKFQKPie :defaultTab="selectedType" />
|
||||
<!-- 用户可在此处自定义内容 -->
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { message } from 'ant-design-vue';
|
||||
import SDKFQKPie from '@/modules/shuidianhuangjingjieruMod/TwoLayer/ShuiDianKaiFQKTwoLayer.vue'
|
||||
// 定义组件名(便于调试和递归)
|
||||
import { getVmsstbprptKendoList } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
defineOptions({
|
||||
name: 'developStatusChart'
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'chart-click', params: any): void;
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// 弹框相关状态
|
||||
const modalVisible = ref(false);
|
||||
const selectedType = ref('');
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
if (chartContainer.value) {
|
||||
initChart();
|
||||
}
|
||||
});
|
||||
const dataLoading = ref(false);
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
@ -52,6 +32,154 @@ onUnmounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const transUnit = (value: number) => {
|
||||
return value * 0.1;
|
||||
};
|
||||
|
||||
const getData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
const baseFilters: any[] = [
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
||||
];
|
||||
if (baseid.value !== 'all') {
|
||||
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
|
||||
}
|
||||
|
||||
const params1 = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'ttpwr', operator: 'isnotnull' }
|
||||
]
|
||||
},
|
||||
group: [{ dir: 'desc', field: 'bldsttCcode', aggregates: [{ field: 'ttpwr', aggregate: 'sum' }] }]
|
||||
};
|
||||
|
||||
const params2 = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [...baseFilters]
|
||||
},
|
||||
group: [{ dir: 'desc', field: 'bldsttCcode' }]
|
||||
};
|
||||
|
||||
try {
|
||||
dataLoading.value = true;
|
||||
const [res1, res2] = await Promise.all([
|
||||
getVmsstbprptKendoList(params1),
|
||||
getVmsstbprptKendoList(params2)
|
||||
]);
|
||||
|
||||
const res1Data = res1?.data?.data || [];
|
||||
const res2Data = res2?.data?.data || [];
|
||||
// debugger
|
||||
|
||||
if (res1Data.length > 0 || res2Data.length > 0) {
|
||||
const ttpwrlist = res1Data.map((item: any) => ({
|
||||
key: item.key,
|
||||
ttpwr: transUnit(item.items[0].SUM_TTPWR)
|
||||
}));
|
||||
|
||||
const engTotalList = res2Data.map((item: any) => ({
|
||||
key: item.key,
|
||||
engTotal: item.count
|
||||
}));
|
||||
|
||||
const result: any = Object.values(
|
||||
[...engTotalList, ...ttpwrlist].reduce((result: any, item: any) => {
|
||||
const { key, engTotal = 0, ttpwr = 0 } = item;
|
||||
const existingItem = result[key] || { key, engTotal: 0, ttpwr: 0 };
|
||||
existingItem.bldstt = item.key == 0 ? '未建' : item.key == 1 ? '在建' : '已建';
|
||||
existingItem.engTotal += engTotal;
|
||||
existingItem.ttpwr += ttpwr;
|
||||
return { ...result, [key]: existingItem };
|
||||
}, {})
|
||||
);
|
||||
|
||||
result.sort((a: any, b: any) => {
|
||||
if (a.bldstt === '已建' && b.bldstt !== '已建') return -1;
|
||||
else if (a.bldstt !== '已建' && b.bldstt === '已建') return 1;
|
||||
else if (a.bldstt === '在建' && b.bldstt === '未建') return -1;
|
||||
else if (a.bldstt === '未建' && b.bldstt === '在建') return 1;
|
||||
else return 0;
|
||||
});
|
||||
|
||||
const legendData: string[] = [];
|
||||
const capData: { name: string; value: number }[] = [];
|
||||
const countData: { name: string; value: number }[] = [];
|
||||
|
||||
result.forEach((item: any) => {
|
||||
const name = item.bldstt;
|
||||
if (name !== '未建') {
|
||||
legendData.push(name);
|
||||
countData.push({ name, value: Number(item.engTotal) });
|
||||
capData.push({ name, value: Number(item.ttpwr).toFixed(2) });
|
||||
}
|
||||
});
|
||||
|
||||
const colors = ['#78c300', '#2196F3'];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}电站{a}: {c}',
|
||||
position: 'right'
|
||||
},
|
||||
legend: {
|
||||
data: legendData,
|
||||
left: '60%',
|
||||
orient: 'vertical',
|
||||
top: 'center',
|
||||
itemWidth: 20,
|
||||
itemHeight: 12
|
||||
},
|
||||
color: colors,
|
||||
series: [
|
||||
{
|
||||
name: '数量(座)',
|
||||
type: 'pie',
|
||||
radius: [0, '55%'],
|
||||
label: { show: false },
|
||||
labelLine: { show: false },
|
||||
data: countData,
|
||||
center: ['30%', '50%'],
|
||||
itemStyle: {
|
||||
borderRadius: 2,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '装机容量(万kW)',
|
||||
type: 'pie',
|
||||
radius: ['70%', '90%'],
|
||||
labelLine: { length: 30 },
|
||||
label: { show: false },
|
||||
data: capData,
|
||||
center: ['30%', '50%'],
|
||||
itemStyle: {
|
||||
borderRadius: 2,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption(option, true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DevelopStatusChart getData error:', error);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
|
||||
@ -64,7 +192,6 @@ const initChart = () => {
|
||||
position: 'right'
|
||||
},
|
||||
legend: {
|
||||
|
||||
data: ['已建', '在建'],
|
||||
left: '60%',
|
||||
orient: 'vertical',
|
||||
@ -72,78 +199,71 @@ const initChart = () => {
|
||||
itemWidth: 20,
|
||||
itemHeight: 12
|
||||
},
|
||||
color: ['#78c300', '#2196F3'],
|
||||
series: [
|
||||
{
|
||||
name: '装机容量',
|
||||
name: '装机容量(万kW)',
|
||||
type: 'pie',
|
||||
radius: ['70%', '90%'], // 外环
|
||||
center: ['30%', '50%'], // 整体向下移动一点
|
||||
|
||||
radius: ['70%', '90%'],
|
||||
center: ['30%', '50%'],
|
||||
itemStyle: {
|
||||
borderRadius: 2,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false // 不显示外侧标签
|
||||
},
|
||||
labelLine: {
|
||||
show: false // 不显示引导线
|
||||
},
|
||||
data: [
|
||||
{ value: 80, name: '已建', itemStyle: { color: '#78c300' } },
|
||||
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
|
||||
]
|
||||
label: { show: false },
|
||||
labelLine: { show: false },
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '数量(座)',
|
||||
type: 'pie',
|
||||
radius: ['0%', '50%'], // 中心圆
|
||||
center: ['30%', '50%'], // 与外环保持一致,整体向下移动
|
||||
radius: ['0%', '50%'],
|
||||
center: ['30%', '50%'],
|
||||
itemStyle: {
|
||||
borderRadius: 2,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 80, name: '已建', itemStyle: { color: '#78c300' } },
|
||||
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
|
||||
]
|
||||
label: { show: false },
|
||||
labelLine: { show: false },
|
||||
data: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 监听窗口大小变化,重新调整图表大小
|
||||
// 添加点击事件
|
||||
chartInstance.on('click', (params: any) => {
|
||||
emit('chart-click', {
|
||||
seriesName: params.seriesName,
|
||||
name: params.name,
|
||||
dataIndex: params.dataIndex
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
chartInstance?.resize();
|
||||
});
|
||||
};
|
||||
|
||||
// 添加图表点击事件
|
||||
chartInstance.on('click', (params: any) => {
|
||||
if (params.data && params.data.name) {
|
||||
selectedType.value = params.data.name;
|
||||
modalVisible.value = true;
|
||||
onMounted(() => {
|
||||
if (chartContainer.value) {
|
||||
initChart();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 弹框确认按钮处理
|
||||
const handleModalOk = () => {
|
||||
modalVisible.value = false;
|
||||
};
|
||||
|
||||
// 弹框取消按钮处理
|
||||
const handleModalCancel = () => {
|
||||
modalVisible.value = false;
|
||||
};
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -152,13 +272,8 @@ const handleModalCancel = () => {
|
||||
height: 100%;
|
||||
|
||||
.chart-container {
|
||||
width: 203px; // 容器宽度
|
||||
height: 100px; // 容器高度
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 10px 0;
|
||||
min-height: 100px;
|
||||
width: 203px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <div v-if="dataLoading">
|
||||
<EmptyPanel :loading="dataLoading" :data="data" />
|
||||
</div> -->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center">
|
||||
<span style="font-size: 14px; line-height: 22px; font-weight: 400; color: #000; margin-bottom: 4px">
|
||||
监测数据接入情况
|
||||
@ -11,7 +8,7 @@
|
||||
<div style="display: flex; flex-direction: column">
|
||||
<div
|
||||
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; cursor: pointer"
|
||||
|
||||
@click="handleItemClick('notAccess')"
|
||||
>
|
||||
<div>
|
||||
<span style="border-right: 2px solid #00A14D; margin-right: 6px"></span>
|
||||
@ -21,6 +18,7 @@
|
||||
</div>
|
||||
<div
|
||||
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; margin: 3px 0; cursor: pointer"
|
||||
@click="handleItemClick('partAccess')"
|
||||
>
|
||||
<div>
|
||||
<span style="border-right: 2px solid #4978AD; margin-right: 6px"></span>
|
||||
@ -30,6 +28,7 @@
|
||||
</div>
|
||||
<div
|
||||
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; cursor: pointer"
|
||||
@click="handleItemClick('isAccess')"
|
||||
>
|
||||
<div>
|
||||
<span style="border-right: 2px solid #606060; margin-right: 6px"></span>
|
||||
@ -39,16 +38,115 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { getVmsstbprptKendoList } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
defineOptions({
|
||||
name: 'EngEnvironmentData'
|
||||
});
|
||||
|
||||
// 状态定义
|
||||
const emit = defineEmits<{
|
||||
(e: 'item-click', params: any): void;
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
const data = ref<Record<string, number>>({ one: 0, two: 0, three: 0 });
|
||||
|
||||
const getData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
const baseFilters: any[] = [
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
||||
{ field: 'bldsttCcode', operator: 'in', dataType: 'string', value: ['1', '2'] }
|
||||
];
|
||||
if (baseid.value !== 'all') {
|
||||
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
|
||||
}
|
||||
|
||||
const params1 = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const params2 = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'runState', operator: 'eq', dataType: 'number', value: 4 },
|
||||
{ field: 'dtin', operator: 'eq', dataType: 'number', value: 1 }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const params3 = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] },
|
||||
{ field: 'dtinEnv', operator: 'in', dataType: 'string', value: ['2'] }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
dataLoading.value = true;
|
||||
const [res1, res2, res3] = await Promise.all([
|
||||
getVmsstbprptKendoList(params1),
|
||||
getVmsstbprptKendoList(params2),
|
||||
getVmsstbprptKendoList(params3)
|
||||
]);
|
||||
|
||||
const record = { one: 0, two: 0, three: 0 };
|
||||
const responses = [res1, res2, res3];
|
||||
responses.forEach((res, index) => {
|
||||
const total = res?.data?.total ?? 0;
|
||||
switch (index) {
|
||||
case 0:
|
||||
record.one = total;
|
||||
break;
|
||||
case 1:
|
||||
record.two = total;
|
||||
break;
|
||||
case 2:
|
||||
record.three = total;
|
||||
break;
|
||||
}
|
||||
});
|
||||
data.value = record;
|
||||
// debugger
|
||||
} catch (error) {
|
||||
console.error('EngEnvironmentData getData error:', error);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemClick = (name: string) => {
|
||||
emit('item-click', { name });
|
||||
};
|
||||
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
@ -3,15 +3,22 @@
|
||||
<div class="yancheng-yulei">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form layout="inline" class="search-form">
|
||||
<!-- 基地名称(非沿程模式显示) -->
|
||||
<a-form-item v-if="!props.ly" label="基地名称">
|
||||
<!-- 基地名称 -->
|
||||
<a-form-item label="基地名称">
|
||||
<a-select
|
||||
v-model:value="formState.baseId"
|
||||
placeholder="请选择"
|
||||
style="width: 150px"
|
||||
:options="baseOptions"
|
||||
@change="handleBaseChange"
|
||||
/>
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in baseOptions"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 所在流域 -->
|
||||
@ -20,21 +27,35 @@
|
||||
v-model:value="formState.rvcd"
|
||||
placeholder="请选择"
|
||||
style="width: 150px"
|
||||
:options="lyOptions"
|
||||
@change="handleRvcdChange"
|
||||
/>
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in lyOptions"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 断面名称(沿程模式显示) -->
|
||||
<a-form-item v-if="props.ly" label="断面名称">
|
||||
<!-- <a-form-item v-if="props.ly" label="断面名称">
|
||||
<a-select
|
||||
v-model:value="formState.stcd"
|
||||
placeholder="请选择"
|
||||
style="width: 150px"
|
||||
:options="dmOptions"
|
||||
@change="handleStcdChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in dmOptions"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item> -->
|
||||
|
||||
<!-- 年份 -->
|
||||
<a-form-item label="年份">
|
||||
@ -57,11 +78,20 @@
|
||||
</a-form>
|
||||
|
||||
<!-- Tab 切换 + 表格 -->
|
||||
<a-tabs v-model:activeKey="tabIndex" class="ylycbh-tabs" @change="handleTabChange">
|
||||
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.labelWithTooltip">
|
||||
<a-tabs
|
||||
v-model:activeKey="tabIndex"
|
||||
class="ylycbh-tabs"
|
||||
@change="handleTabChange"
|
||||
>
|
||||
<a-tab-pane
|
||||
v-for="item in tabList"
|
||||
:key="item.key"
|
||||
:tab="item.labelWithTooltip"
|
||||
>
|
||||
<div class="table-wrapper">
|
||||
<BasicTable
|
||||
v-if="tabIndex === item.key"
|
||||
<!-- 沿程模式表格 -->
|
||||
<!-- <BasicTable
|
||||
v-if="props.ly && tabIndex === item.key"
|
||||
:ref="el => setTableRef(el, item.key)"
|
||||
:columns="tableColumns"
|
||||
:scroll-y="500"
|
||||
@ -70,34 +100,46 @@
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<!-- 沿程模式:断面名称列(带电站名称分组) -->
|
||||
<template v-if="props.ly" #rsctdName="{ record }">
|
||||
<template #rsctdName="{ record }">
|
||||
{{ record.rsctdName || '-' }}
|
||||
</template>
|
||||
<template v-if="props.ly" #stcdList="{ record }">
|
||||
<template #stcdList="{ record }">
|
||||
<div v-for="st in record.stcdList" :key="st.stcd">
|
||||
<a @click="handleViewDetail(st.stcd, st.stnm)" class="text-link">
|
||||
<a
|
||||
@click="handleViewDetail(st.stcd, st.stnm)"
|
||||
class="text-link"
|
||||
>
|
||||
{{ st.stnm }}
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="props.ly" #fishList="{ record }">
|
||||
<template #fishList="{ record }">
|
||||
<a-list size="small" :data-source="record.fishList">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>{{ item }}</a-list-item>
|
||||
<template #renderItem="{ item: fish }">
|
||||
<a-list-item>{{ fish }}</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
</BasicTable> -->
|
||||
|
||||
<!-- 基地模式:调查断面列 -->
|
||||
<template v-else #stnm="{ record }">
|
||||
<a @click="handleViewDetail(record.stcd, record.stnm)" class="text-link">
|
||||
<!-- 基地模式表格 -->
|
||||
<BasicTable
|
||||
:ref="el => setTableRef(el, item.key)"
|
||||
:columns="tableColumns"
|
||||
:scroll-y="500"
|
||||
:scroll-x="800"
|
||||
:list-url="listUrl"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #stnm="{ record }">
|
||||
<a
|
||||
@click="handleViewDetail(record.stcd, record.stnm)"
|
||||
class="text-link"
|
||||
>
|
||||
{{ record.stnm }}
|
||||
</a>
|
||||
</template>
|
||||
<template v-else #name="{ record }">
|
||||
{{ record.name || '-' }}
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
@ -135,9 +177,21 @@ const props = defineProps<{
|
||||
|
||||
// ==================== Tab 配置 ====================
|
||||
const TAB_CONFIG = [
|
||||
{ key: '1', label: '外来鱼类', prompt: '通过人为行为引入的本流域原生态没有的鱼类' },
|
||||
{ key: '2', label: '本土受威胁鱼类', prompt: '流域原生态鱼类中处于近危或者濒危的鱼类' },
|
||||
{ key: '3', label: '本土无危鱼类', prompt: '分类属于本土且保护类型属于无危的鱼类' }
|
||||
{
|
||||
key: '1',
|
||||
label: '外来鱼类',
|
||||
prompt: '通过人为行为引入的本流域原生态没有的鱼类'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '本土受威胁鱼类',
|
||||
prompt: '流域原生态鱼类中处于近危或者濒危的鱼类'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '本土无危鱼类',
|
||||
prompt: '分类属于本土且保护类型属于无危的鱼类'
|
||||
}
|
||||
];
|
||||
|
||||
// ==================== 搜索表单状态 ====================
|
||||
@ -214,7 +268,7 @@ const lyColumns = [
|
||||
title: '电站名称',
|
||||
dataIndex: 'rsctdName',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
// ellipsis: true,
|
||||
slots: { customRender: 'rsctdName' }
|
||||
},
|
||||
{
|
||||
@ -222,7 +276,7 @@ const lyColumns = [
|
||||
title: '调查断面',
|
||||
dataIndex: 'stcdList',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
// ellipsis: true,
|
||||
slots: { customRender: 'stcdList' }
|
||||
},
|
||||
{
|
||||
@ -230,7 +284,7 @@ const lyColumns = [
|
||||
title: '渔获物种类',
|
||||
dataIndex: 'fishList',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
// ellipsis: true,
|
||||
slots: { customRender: 'fishList' }
|
||||
}
|
||||
];
|
||||
@ -259,10 +313,10 @@ const baseColumns = [
|
||||
];
|
||||
|
||||
/** 根据 ly 模式切换表格列 */
|
||||
const tableColumns = computed(() => (props.ly ? lyColumns : baseColumns));
|
||||
const tableColumns = computed(() => (props.ly ? baseColumns : lyColumns));
|
||||
|
||||
/** 根据 ly 模式切换表格 API */
|
||||
const listUrl = computed(() => (props.ly ? fishDetail : fishQgcDetail));
|
||||
const listUrl = computed(() => (props.ly ?fishQgcDetail : fishDetail));
|
||||
|
||||
/** 表格搜索参数 */
|
||||
const searchParams = computed(() => ({
|
||||
@ -291,22 +345,41 @@ const fetchLyList = async () => {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{ field: 'wbsType', operator: 'eq', value: 'PSB_RVCD' },
|
||||
{
|
||||
formState.value.baseId != 'all'
|
||||
? {
|
||||
field: 'fullPath',
|
||||
operator: 'contains',
|
||||
operator: 'startswith',
|
||||
dataType: 'string',
|
||||
value: formState.value.baseId + ','
|
||||
value: " formState.value.baseId+','"
|
||||
}
|
||||
]
|
||||
: null,
|
||||
{
|
||||
field: 'wbsType',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'PSB_RVCD'
|
||||
}
|
||||
].filter(Boolean)
|
||||
},
|
||||
group: [
|
||||
{ dir: 'asc', field: 'objId' },
|
||||
{ dir: 'asc', field: 'orderIndex' },
|
||||
{ dir: 'des', field: 'wbsCode' },
|
||||
{ dir: 'des', field: 'wbsName' }
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'objId'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'orderIndex'
|
||||
},
|
||||
{
|
||||
dir: 'des',
|
||||
field: 'wbsCode'
|
||||
},
|
||||
{
|
||||
dir: 'des',
|
||||
field: 'wbsName'
|
||||
}
|
||||
],
|
||||
groupResultFlat: true
|
||||
groupResultFlat: 'true'
|
||||
};
|
||||
|
||||
const res = await wbsbGetKendoList(params);
|
||||
@ -375,27 +448,45 @@ const fetchDmList = async () => {
|
||||
|
||||
// ==================== 获取 Tab 数量统计 ====================
|
||||
const getTabNumData = async () => {
|
||||
const params: any = {
|
||||
baseId: formState.value.baseId,
|
||||
rvcd: formState.value.rvcd,
|
||||
yr: dayjs(yearValue.value).format('YYYY'),
|
||||
ty: tabIndex.value
|
||||
};
|
||||
|
||||
// 沿程模式传 stcd
|
||||
if (props.ly) {
|
||||
params.stcd = formState.value.stcd;
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
formState.value.baseId != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: formState.value.baseId
|
||||
}
|
||||
|
||||
// 空值过滤
|
||||
if (!params.stcd) delete params.stcd;
|
||||
if (params.baseId === 'all') delete params.baseId;
|
||||
: null,
|
||||
{
|
||||
field: 'rvcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: formState.value.rvcd
|
||||
},
|
||||
{
|
||||
field: 'yr',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: dayjs(yearValue.value).format('YYYY')
|
||||
},
|
||||
{
|
||||
field: 'ty',
|
||||
operator: 'isnotnull',
|
||||
dataType: 'string',
|
||||
value: tabIndex.value
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await getTabNum(params);
|
||||
// getTabNum 返回格式:res.data 直接是数组 [{ty, num}, ...]
|
||||
const data = res?.data || [];
|
||||
|
||||
const data = res?.data?.data || [];
|
||||
// debugger
|
||||
const list = TAB_CONFIG.map(item => ({
|
||||
...item,
|
||||
labelWithTooltip: renderTabLabel(item.label, item.prompt)
|
||||
@ -427,12 +518,14 @@ const getTabNumData = async () => {
|
||||
/** 构建表格过滤条件 */
|
||||
const getFilter = () => {
|
||||
const filters: any[] = [
|
||||
{
|
||||
formState.value.baseId != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: formState.value.baseId
|
||||
},
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'rvcd',
|
||||
operator: 'eq',
|
||||
@ -454,14 +547,14 @@ const getFilter = () => {
|
||||
];
|
||||
|
||||
// 沿程模式传 stcd
|
||||
if (props.ly && formState.value.stcd) {
|
||||
filters.push({
|
||||
field: 'stcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: formState.value.stcd
|
||||
});
|
||||
}
|
||||
// if (props.ly && formState.value.stcd) {
|
||||
// filters.push({
|
||||
// field: 'stcd',
|
||||
// operator: 'eq',
|
||||
// dataType: 'string',
|
||||
// value: formState.value.stcd
|
||||
// });
|
||||
// }
|
||||
|
||||
return {
|
||||
logic: 'and',
|
||||
@ -516,7 +609,9 @@ const handleYearChange = () => {
|
||||
};
|
||||
|
||||
/** Tab 切换 */
|
||||
const handleTabChange = () => {
|
||||
const handleTabChange = async () => {
|
||||
// 等待新 Tab 的 BasicTable 渲染完成
|
||||
await nextTick();
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
@cancel="handleModalClose"
|
||||
>
|
||||
<Yanchengyulei
|
||||
v-if="modalVisible"
|
||||
v-if="modalVisible && JidiSelectEventStore.jidiData.length > 0"
|
||||
:tm="modalYear"
|
||||
:dataDimensionVal="baseid"
|
||||
:hbrvcd="modalHbrvcd"
|
||||
@ -525,12 +525,12 @@ const filterSelectOptions = (options: any[]) => {
|
||||
* 点击图表柱子时触发,传递当前选中的年份、流域、站点名称等参数
|
||||
*/
|
||||
const openDetail = (params: any) => {
|
||||
|
||||
// 从 chartData 中查找对应站点的 stcd
|
||||
const found = chartData.value.find((el: any) => el.stnm === params.name);
|
||||
|
||||
modalYear.value = select.value.value;
|
||||
modalHbrvcd.value = moreSelect.value.value || '';
|
||||
modalStateName.value = params.name || '';
|
||||
modalStateName.value = params.seriesName || '';
|
||||
modalSectionName.value = params.name || '';
|
||||
modalStcd.value = found?.stcd || '';
|
||||
modalVisible.value = true;
|
||||
|
||||
@ -1,130 +1,418 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<SidePanelItem title="典型设施介绍">
|
||||
<div v-if="carouselData.length > 0" class="carousel-wrapper">
|
||||
<a-carousel v-model:current="currentIndex" autoplay class="tech-carousel" :dot-style="{ bottom: '0px' }">
|
||||
<!-- -->
|
||||
<div v-for="(item, index) in carouselData" :key="index" class="carousel-item">
|
||||
<div class="image-container">
|
||||
<img :src="item.image" :alt="item.title" class="carousel-image" />
|
||||
<SidePanelItem
|
||||
title="典型设施介绍"
|
||||
:select="selectConfig"
|
||||
@update-values="handlePanelChange"
|
||||
>
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="originalMediaData.length > 0">
|
||||
<div
|
||||
class="container"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<!-- 跑马灯轨道容器 -->
|
||||
<div
|
||||
class="carousel-track"
|
||||
:class="{ 'no-transition': isTransitioning }"
|
||||
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
|
||||
>
|
||||
<!-- 遍历所有媒体项(包含克隆项) -->
|
||||
<div
|
||||
v-for="(item, index) in renderMediaData"
|
||||
:key="index"
|
||||
class="carousel-item"
|
||||
@click="handleItemClick"
|
||||
>
|
||||
<img :src="item.image" :alt="item.title" />
|
||||
<!-- 说明文字(随媒体项移动) -->
|
||||
<!-- <div class="text">{{ item.title }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</a-carousel>
|
||||
|
||||
<!-- 文字描述区域 -->
|
||||
<div v-if="carouselData[currentIndex]" class="description-container">
|
||||
<p class="item-description">{{ carouselData[currentIndex].description }}</p>
|
||||
<!-- 面板指示器(固定在底部右侧) -->
|
||||
<div class="pagination-dots-fixed">
|
||||
<span
|
||||
v-for="(dot, index) in originalMediaData"
|
||||
:key="index"
|
||||
class="dot"
|
||||
:class="{ active: getCurrentRealIndex() === index }"
|
||||
@click="goToSlide(index)"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-empty />
|
||||
|
||||
<!-- 独立的文字说明区域(随跑马灯切换而变化) -->
|
||||
<div class="description-text">
|
||||
{{ currentDescription }}
|
||||
</div>
|
||||
</template>
|
||||
<a-empty v-else description="暂无数据" />
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- 设施详情弹框 -->
|
||||
<a-modal v-model:open="modalVisible" :title="'设施详情'" width="1536px" :footer="null">
|
||||
<div v-if="currentItem" class="detail-container">
|
||||
<ArtsDetail :dataSource="currentItem" :index="getCurrentRealIndex()" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
|
||||
import { getMsstbprptKendoList } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'dianxingcuoshijieshao'
|
||||
});
|
||||
|
||||
// 当前轮播索引
|
||||
const currentIndex = ref(0);
|
||||
// 基地选择 store
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
|
||||
// 轮播图数据
|
||||
const carouselData = ref([
|
||||
{
|
||||
title: '叠梁门',
|
||||
description: '叠梁门是一种用于调节水库流量和控制水位的设施。在低温季节,可以通过控制叠梁门的开闭程度来调节流量,减少低温水体的进...',
|
||||
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png' // 替换为实际图片路径
|
||||
},
|
||||
{
|
||||
title: '环保设施',
|
||||
description: '现代化环保设施,采用先进技术,有效处理工业废水废气,实现达标排放,保护生态环境...',
|
||||
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
|
||||
},
|
||||
{
|
||||
title: '智能监控',
|
||||
description: '24 小时智能监控系统,实时监测设备运行状态,确保设施安全稳定运行...',
|
||||
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
|
||||
},
|
||||
{
|
||||
title: '水处理系统',
|
||||
description: '高效水处理系统,通过多级过滤和生物处理,实现水资源循环利用...',
|
||||
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
|
||||
// 媒体类型定义
|
||||
interface MediaItem {
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
}
|
||||
]);
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
// 弹框控制
|
||||
const modalVisible = ref(false);
|
||||
const currentItem = ref<any>(null);
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 下拉选择配置
|
||||
const selectConfig = ref({
|
||||
show: true,
|
||||
value: 'all',
|
||||
width: '142px',
|
||||
options: [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '鱼类增殖站', value: 'FB' },
|
||||
{ label: '过鱼设施', value: 'FP' },
|
||||
{ label: '低温水减缓设施', value: 'DW' }
|
||||
]
|
||||
});
|
||||
|
||||
// 当前选择的设施类型
|
||||
const selectValue = ref('all');
|
||||
|
||||
// 原始媒体数据
|
||||
const originalMediaData = ref<MediaItem[]>([]);
|
||||
|
||||
// 克隆首尾项后的渲染数组(用于无缝循环)
|
||||
const renderMediaData = ref<MediaItem[]>([]);
|
||||
|
||||
// 当前显示索引(指向renderMediaData)
|
||||
const currentIndex = ref(1);
|
||||
|
||||
// 定时器引用
|
||||
let timer: any = null;
|
||||
|
||||
// 鼠标悬停状态
|
||||
const isHovering = ref(false);
|
||||
|
||||
// 是否正在切换动画中
|
||||
const isTransitioning = ref(false);
|
||||
|
||||
// 设施类型对应的 sttpCode 列表
|
||||
const dw = ['DW', 'DW_1', 'DW_2', 'DW_3', 'DW_4', 'DW_5', 'DW_6', 'DW_9'];
|
||||
const fp = ['FP', 'FP_1', 'FP_2', 'FP_3', 'FP_4', 'FP_5'];
|
||||
|
||||
// 初始化渲染数组(克隆首尾项)
|
||||
const initRenderData = () => {
|
||||
const length = originalMediaData.value.length;
|
||||
if (length === 0) {
|
||||
renderMediaData.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
renderMediaData.value = [
|
||||
originalMediaData.value[length - 1],
|
||||
...originalMediaData.value,
|
||||
originalMediaData.value[0]
|
||||
];
|
||||
currentIndex.value = 1;
|
||||
};
|
||||
|
||||
// 启动自动轮播
|
||||
const startAutoPlay = () => {
|
||||
if (timer) clearInterval(timer);
|
||||
timer = setInterval(() => {
|
||||
if (!isHovering.value && !isTransitioning.value && originalMediaData.value.length > 0) {
|
||||
nextSlide();
|
||||
}
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
// 切换到下一张
|
||||
const nextSlide = () => {
|
||||
currentIndex.value++;
|
||||
setTimeout(() => {
|
||||
checkSeamlessJump();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 检查是否需要无缝跳转
|
||||
const checkSeamlessJump = () => {
|
||||
const realLength = originalMediaData.value.length;
|
||||
|
||||
if (currentIndex.value >= realLength + 1) {
|
||||
isTransitioning.value = true;
|
||||
currentIndex.value = 1;
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
isTransitioning.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理鼠标进入
|
||||
const handleMouseEnter = () => {
|
||||
isHovering.value = true;
|
||||
};
|
||||
|
||||
// 处理鼠标离开
|
||||
const handleMouseLeave = () => {
|
||||
isHovering.value = false;
|
||||
};
|
||||
|
||||
// 计算当前显示的描述文字
|
||||
const currentDescription = computed(() => {
|
||||
const realIndex = getCurrentRealIndex();
|
||||
return originalMediaData.value[realIndex]?.description || '';
|
||||
});
|
||||
|
||||
// 获取当前真实的索引
|
||||
const getCurrentRealIndex = () => {
|
||||
const realLength = originalMediaData.value.length;
|
||||
let realIndex = currentIndex.value - 1;
|
||||
|
||||
if (realIndex < 0) realIndex = realLength - 1;
|
||||
if (realIndex >= realLength) realIndex = 0;
|
||||
|
||||
return realIndex;
|
||||
};
|
||||
|
||||
// 跳转到指定幻灯片
|
||||
const goToSlide = (targetIndex: number) => {
|
||||
if (isTransitioning.value) return;
|
||||
currentIndex.value = targetIndex + 1;
|
||||
};
|
||||
|
||||
// 处理项目点击事件
|
||||
const handleItemClick = () => {
|
||||
currentItem.value = originalMediaData.value.map((item: any) => ({
|
||||
url: item.image || '',
|
||||
description: item.description || '',
|
||||
title: item.title || ''
|
||||
}));
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// 处理下拉选择变化
|
||||
const handlePanelChange = (payload: any) => {
|
||||
if (payload.select !== undefined && payload.select !== selectValue.value) {
|
||||
selectValue.value = payload.select;
|
||||
getData();
|
||||
}
|
||||
};
|
||||
|
||||
// 获取数据
|
||||
const getData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 根据选择确定 sttpCode 列表
|
||||
let sttpCodes: string[] = ['FB', ...dw, ...fp];
|
||||
if (selectValue.value === 'FB') {
|
||||
sttpCodes = ['FB'];
|
||||
} else if (selectValue.value === 'DW') {
|
||||
sttpCodes = [...dw];
|
||||
} else if (selectValue.value === 'FP') {
|
||||
sttpCodes = [...fp];
|
||||
}
|
||||
|
||||
const filters: any[] = [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: sttpCodes
|
||||
},
|
||||
{
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{ field: 'logo', dataType: 'string', operator: 'isnotnull' },
|
||||
{ field: 'introduce', dataType: 'string', operator: 'isnotnull' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 添加基地过滤
|
||||
if (baseid.value && baseid.value !== 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value
|
||||
});
|
||||
}
|
||||
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
},
|
||||
select: ['introduce', 'logo', 'stnm', 'precis']
|
||||
};
|
||||
|
||||
const res = await getMsstbprptKendoList(params);
|
||||
|
||||
if (res?.data?.data && Array.isArray(res.data.data)) {
|
||||
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL || '';
|
||||
originalMediaData.value = res.data.data
|
||||
.filter((item: any) => item.logo || item.introduce)
|
||||
.map((item: any) => ({
|
||||
image: item.logo ? baseUrl + '?' + item.logo + '&view=jpg' : '',
|
||||
title: item.stnm || '',
|
||||
description: item.introduce || ''
|
||||
}));
|
||||
} else {
|
||||
originalMediaData.value = [];
|
||||
}
|
||||
|
||||
initRenderData();
|
||||
} catch (error) {
|
||||
console.error('获取设施介绍数据失败:', error);
|
||||
originalMediaData.value = [];
|
||||
initRenderData();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听基地变化
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 页面加载时执行
|
||||
onMounted(() => {
|
||||
// 可以在这里加载实际的图片数据
|
||||
startAutoPlay();
|
||||
});
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.carousel-wrapper {
|
||||
width: 414px;
|
||||
:deep(.slick-slide) {
|
||||
|
||||
text-align: center;
|
||||
height: 250px;
|
||||
line-height: 160px;
|
||||
background: #364d79;
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 228px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.ant-carousel .slick-dots li {
|
||||
width: 5px !important;
|
||||
height: 5px !important;
|
||||
border-radius: 50% !important;
|
||||
background-color: #888e95;
|
||||
|
||||
}
|
||||
.ant-carousel .slick-dots li button{
|
||||
width: 5px !important;
|
||||
height: 5px !important;
|
||||
border-radius: 50% !important;
|
||||
background-color: #888e95;
|
||||
}
|
||||
.ant-carousel .slick-dots li.slick-active{
|
||||
background-color: #005293 !important;
|
||||
}
|
||||
.ant-carousel .slick-dots li.slick-active button{
|
||||
background-color: #005293 !important;
|
||||
}
|
||||
.ant-carousel .slick-dots li :hover {
|
||||
width: 5px !important;
|
||||
height: 5px !important;
|
||||
border-radius: 50% !important;
|
||||
background-color: #ffffffcc;
|
||||
|
||||
}
|
||||
.ant-carousel .slick-dots li button :hover{
|
||||
width: 5px !important;
|
||||
height: 5px !important;
|
||||
border-radius: 50% !important;
|
||||
background-color: #ffffffcc;
|
||||
}
|
||||
}
|
||||
|
||||
.description-container {
|
||||
height: 44px;
|
||||
.carousel-track {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
line-height: 22px;
|
||||
height: 100%;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
|
||||
&.no-transition {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.text {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
padding-left: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-dots-fixed {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
z-index: 10;
|
||||
|
||||
.dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background-color: #d8d8d8;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background-color: #005293;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 12px;
|
||||
min-height: 44px;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
text-indent: 0em;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -31,7 +31,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, computed, reactive,watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { msstbprptGetKendoList } from '@/api/yldc';
|
||||
import { VDmsstbprptGetKendoList } from '@/api/yldc';
|
||||
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
// 定义组件名(便于调试和递归)
|
||||
@ -84,7 +84,7 @@ const getVideoSelect = async () => {
|
||||
},
|
||||
select: ['stcd', 'stnm', 'url']
|
||||
};
|
||||
let res = await msstbprptGetKendoList(params);
|
||||
let res = await VDmsstbprptGetKendoList(params);
|
||||
let resData = res.data.data; //stnm stcd url
|
||||
select.value.options = resData.map(item => {
|
||||
return {
|
||||
|
||||
@ -0,0 +1,390 @@
|
||||
<template>
|
||||
<div class="huanbao-two-layers-container">
|
||||
<!-- 搜索表单 -->
|
||||
<div class="search-form">
|
||||
<a-space size="middle">
|
||||
<div>所属基地:</div>
|
||||
<a-select
|
||||
v-model:value="searchData.baseid"
|
||||
placeholder="请选择基地"
|
||||
style="width: 200px"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="baseOptions"
|
||||
@change="handleBaseChange"
|
||||
/>
|
||||
<div>类型:</div>
|
||||
<a-select
|
||||
v-model:value="searchData.type"
|
||||
placeholder="请选择类型"
|
||||
style="width: 200px"
|
||||
:options="typeOptions"
|
||||
@change="handleTypeChange"
|
||||
/>
|
||||
<div>建设状态:</div>
|
||||
<a-select
|
||||
v-model:value="searchData.bldstt"
|
||||
placeholder="请选择状态"
|
||||
style="width: 150px"
|
||||
:options="bldsttOptions"
|
||||
@change="handleBldsttChange"
|
||||
/>
|
||||
<a-input
|
||||
v-model:value="searchData.search"
|
||||
placeholder="搜索名称/电站"
|
||||
style="width: 200px"
|
||||
allow-clear
|
||||
@press-enter="handleSearch"
|
||||
/>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<a-tooltip title="重置">
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-wrapper">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scroll-y="500"
|
||||
:list-url="envOpdGetKendoListCust"
|
||||
:search-params="tableSearchParams"
|
||||
:transform-data="customTransform"
|
||||
row-key="stcd"
|
||||
>
|
||||
<template #stnm="{ record }">
|
||||
<a
|
||||
v-if="record.stnm && record.stnm !== '-'"
|
||||
@click="handleViewDetail(record, 'DW')"
|
||||
class="text_hocer"
|
||||
>{{ record.stnm }}</a
|
||||
>
|
||||
</template>
|
||||
<template #ennm="{ record }">
|
||||
<a
|
||||
v-if="record.ennm && record.ennm !== '-'"
|
||||
@click="handleViewDetail(record, 'ENG')"
|
||||
class="text_hocer"
|
||||
>{{ record.ennm }}</a
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { envOpdGetKendoListCust, getBaseList } from '@/api/home';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
|
||||
const modelStore = useModelStore();
|
||||
// ==================== Props 定义 ====================
|
||||
const props = defineProps<{
|
||||
data?: any;
|
||||
currentJidiInfo?: any;
|
||||
res?: any;
|
||||
}>();
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
const tableRef = ref();
|
||||
const baseList = ref<any[]>([]);
|
||||
|
||||
// 搜索数据
|
||||
const searchData = ref<any>({
|
||||
baseid: props.currentJidiInfo?.wbsCode || 'all',
|
||||
type: props.data?.type || '',
|
||||
bldstt: props.res?.bldstt || '',
|
||||
hydrodtin: props.res?.hydrodtin || '',
|
||||
search: ''
|
||||
});
|
||||
|
||||
// 基地选项
|
||||
const baseOptions = computed(() => {
|
||||
const options = [];
|
||||
baseList.value.forEach(item => {
|
||||
options.push({
|
||||
label: item.wbsName || item.basename,
|
||||
value: item.wbsCode || item.baseid
|
||||
});
|
||||
});
|
||||
return options;
|
||||
});
|
||||
|
||||
// 类型选项
|
||||
const typeOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '鱼类增殖站', value: 'FB' },
|
||||
{ label: '过鱼设施', value: 'FP' },
|
||||
{ label: '珍稀植物园', value: 'VP' },
|
||||
{ label: '动物救助站', value: 'VA' },
|
||||
{ label: '低温水减缓设施', value: 'DW' },
|
||||
{ label: '鱼类栖息地', value: 'FH' },
|
||||
{ label: '生态流量泄放设施', value: 'EQ' }
|
||||
];
|
||||
|
||||
// 建设状态选项
|
||||
const bldsttOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '未建', value: '0' },
|
||||
{ label: '在建', value: '1' },
|
||||
{ label: '已建', value: '2' }
|
||||
];
|
||||
|
||||
// ==================== 列配置 ====================
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
key: 'stnm',
|
||||
dataIndex: 'stnm',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '建设情况',
|
||||
key: 'bldsttCcodeName',
|
||||
dataIndex: 'bldsttCcodeName',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
key: 'sttpName',
|
||||
dataIndex: 'sttpName',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '接入日期',
|
||||
key: 'dtinTm',
|
||||
dataIndex: 'dtinTm',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '关联电站',
|
||||
key: 'ennm',
|
||||
dataIndex: 'ennm',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '关联基地',
|
||||
key: 'baseName',
|
||||
dataIndex: 'baseName',
|
||||
width: 150
|
||||
}
|
||||
];
|
||||
|
||||
// ==================== 表格搜索参数 ====================
|
||||
const tableSearchParams = computed(() => {
|
||||
return {
|
||||
sort: [
|
||||
{ field: 'baseStepSort', dir: 'asc' },
|
||||
{ field: 'dtin', dir: 'desc' },
|
||||
{ field: 'rvcdStepSort', dir: 'asc' },
|
||||
{ field: 'rstcdStepSort', dir: 'asc' },
|
||||
{ field: 'siteStepSort', dir: 'asc' }
|
||||
],
|
||||
select: [
|
||||
'stnm',
|
||||
'bldsttCcodeName',
|
||||
'sttpName',
|
||||
'dtinTm',
|
||||
'ennm',
|
||||
'baseName',
|
||||
'stcd',
|
||||
'sttpCode'
|
||||
],
|
||||
group: [],
|
||||
groupResultFlat: false
|
||||
};
|
||||
});
|
||||
|
||||
// 数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
// ==================== 过滤条件构建 ====================
|
||||
const buildSearchFilter = () => {
|
||||
const filters: any[] = [];
|
||||
|
||||
if (searchData.value.baseid && searchData.value.baseid !== 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
value: searchData.value.baseid
|
||||
});
|
||||
}
|
||||
|
||||
if (searchData.value.type) {
|
||||
const operator = searchData.value.type === 'EQ' ? 'contains' : 'eq';
|
||||
filters.push({
|
||||
field: 'sttpCode',
|
||||
operator: operator,
|
||||
value: searchData.value.type
|
||||
});
|
||||
}
|
||||
|
||||
if (searchData.value.bldstt !== undefined && searchData.value.bldstt !== '') {
|
||||
filters.push({
|
||||
field: 'bldsttCcode',
|
||||
operator: 'eq',
|
||||
value: searchData.value.bldstt
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
searchData.value.hydrodtin !== undefined &&
|
||||
searchData.value.hydrodtin !== ''
|
||||
) {
|
||||
filters.push({
|
||||
field: 'dtin',
|
||||
operator: 'eq',
|
||||
value: searchData.value.hydrodtin
|
||||
});
|
||||
}
|
||||
|
||||
if (searchData.value.search) {
|
||||
filters.push({
|
||||
logic: 'or',
|
||||
filters: [
|
||||
{
|
||||
field: 'baseName',
|
||||
operator: 'contains',
|
||||
value: searchData.value.search
|
||||
},
|
||||
{ field: 'stnm', operator: 'contains', value: searchData.value.search },
|
||||
{ field: 'ennm', operator: 'contains', value: searchData.value.search }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: filters.filter(
|
||||
f => f.value !== undefined && f.value !== null && f.value !== ''
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
// ==================== 获取基地列表 ====================
|
||||
const fetchBaseList = async () => {
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'wbsType',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'PSB'
|
||||
},
|
||||
{ field: 'treeLevel', operator: 'eq', dataType: 'string', value: '1' }
|
||||
]
|
||||
},
|
||||
sort: [{ field: 'orderIndex', dir: 'asc' }]
|
||||
};
|
||||
|
||||
const res = await getBaseList(params);
|
||||
if (res?.data?.data) {
|
||||
baseList.value = res.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取基地列表失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const handleBaseChange = () => {
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const handleTypeChange = () => {
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const handleBldsttChange = () => {
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
const filter = buildSearchFilter();
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
searchData.value = {
|
||||
baseid: props.currentJidiInfo?.wbsCode || 'all',
|
||||
type: props.data?.type || '',
|
||||
bldstt: '',
|
||||
hydrodtin: '',
|
||||
search: ''
|
||||
};
|
||||
const filter = buildSearchFilter();
|
||||
tableRef.value?.reset();
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
const handleViewDetail = (record: any, type: any) => {
|
||||
console.log('查看详情:', record);
|
||||
if (type == 'ENG') {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params = record;
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
} else {
|
||||
console.log('查看详情:', record);
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params = record;
|
||||
modelStore.params.sttp = record.sttpCode;
|
||||
// debugger
|
||||
modelStore.title = record.stnm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
}
|
||||
// TODO: 后续实现详情查看功能
|
||||
};
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
onMounted(() => {
|
||||
fetchBaseList();
|
||||
handleSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.huanbao-two-layers-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.text_hocer {
|
||||
color: #2f6b98;
|
||||
}
|
||||
|
||||
.text_hocer:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
</style>
|
||||
@ -1,75 +1,158 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<SidePanelItem title="环保设施情况">
|
||||
<div class="facility-grid" >
|
||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
||||
<a-spin :spinning="loading">
|
||||
<div class="facility-grid" v-if="facilities.length > 0">
|
||||
<div
|
||||
v-for="facility in facilities"
|
||||
:key="facility.type"
|
||||
class="facility-card"
|
||||
@click="handleCardClick(facility)"
|
||||
>
|
||||
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
||||
<div class="facility-icon">
|
||||
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
|
||||
<i style="color: #fff;" :class="facility.icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="facility-info">
|
||||
<div class="facility-name">{{ facility.name }}</div>
|
||||
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count }}</span><span>个</span></div>
|
||||
<div style="font-size: 16px;">
|
||||
<span class="facility-count">{{ facility.nowNum + facility.buildNum }}</span><span>个</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty v-else />
|
||||
</a-spin>
|
||||
|
||||
<!-- 二级弹框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="环保设施情况"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
>
|
||||
<HuanbaoModTwoLayers
|
||||
v-if="modalVisible"
|
||||
:data="modalData"
|
||||
:currentJidiInfo="currentJidiInfo"
|
||||
:res="modalRes"
|
||||
/>
|
||||
</a-modal>
|
||||
</SidePanelItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { environmentalProtectionConstruction } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import HuanbaoModTwoLayers from './TwoLayers/HuanbaoModTwoLayers.vue';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'huanbaoMod'
|
||||
});
|
||||
|
||||
// 基地选择 store
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const currentJidiInfo = ref<any>(null);
|
||||
|
||||
// 设施列表模板
|
||||
const facilityList = [
|
||||
{ name: '生态流量泄放设施', icon: 'icon iconfont icon-shengtailiuliang2', type: 'EQ', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
|
||||
{ name: '低温水减缓设施', icon: 'icon iconfont icon-diwenshuijianhuan', type: 'DW', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
|
||||
{ name: '栖息地', icon: 'icon iconfont icon-qixidi', type: 'FH', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 },
|
||||
{ name: '过鱼设施', icon: 'icon iconfont icon-guoyusheshi', type: 'FP', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
|
||||
{ name: '鱼类增殖站', icon: 'icon iconfont icon-yuleizengzhizhan', type: 'FB', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
|
||||
{ name: '珍稀植物园', icon: 'icon iconfont icon-zhenxizhiwuyuan', type: 'VP', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 },
|
||||
{ name: '动物救助站', icon: 'icon iconfont icon-dongwujiuzhuzhan', type: 'VA', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 }
|
||||
];
|
||||
|
||||
// 设施数据
|
||||
const facilities = ref([
|
||||
{
|
||||
name: '生态流量泄放设施',
|
||||
count: 145,
|
||||
icon: 'icon iconfont icon-shengtailiuliang2'
|
||||
},
|
||||
{
|
||||
name: '低温水减缓设施',
|
||||
count: 24,
|
||||
icon: 'icon iconfont icon-diwenshuijianhuan'
|
||||
},
|
||||
{
|
||||
name: '栖息地',
|
||||
count: 142,
|
||||
icon: 'icon iconfont icon-qixidi'
|
||||
},
|
||||
{
|
||||
name: '过鱼设施',
|
||||
count: 60,
|
||||
icon: 'icon iconfont icon-guoyusheshi'
|
||||
},
|
||||
{
|
||||
name: '鱼类增殖站',
|
||||
count: 69,
|
||||
icon: 'icon iconfont icon-yuleizengzhizhan'
|
||||
},
|
||||
{
|
||||
name: '珍稀植物园',
|
||||
count: 41,
|
||||
icon: 'icon iconfont icon-zhenxizhiwuyuan'
|
||||
},
|
||||
{
|
||||
name: '动物救助站',
|
||||
count: 4,
|
||||
icon: 'icon iconfont icon-dongwujiuzhuzhan'
|
||||
}
|
||||
]);
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
const facilities = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
// 弹框相关
|
||||
const modalVisible = ref(false);
|
||||
const modalData = ref<any>({});
|
||||
const modalRes = ref({
|
||||
bldstt: '',
|
||||
hydrodtin: '1'
|
||||
});
|
||||
|
||||
// 获取数据
|
||||
const getData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
// const params = { baseId: baseid.value, '1': '1' };
|
||||
const params ={
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "1",
|
||||
"operator": "eq",
|
||||
"dataType": "string",
|
||||
"value": "1"
|
||||
},
|
||||
baseid.value !='all'?{
|
||||
"field": "baseId",
|
||||
"operator": "eq",
|
||||
"dataType": "string",
|
||||
"value": "01"
|
||||
}:null
|
||||
].filter(Boolean)
|
||||
}
|
||||
}
|
||||
const res = await environmentalProtectionConstruction(params);
|
||||
|
||||
if (res?.data?.data) {
|
||||
const list = [...facilityList];
|
||||
list.forEach((item) => {
|
||||
const found = res.data.data.find((el: any) => item.type === el.repCode);
|
||||
if (found) {
|
||||
item.nowNum = found.count || 0;
|
||||
}
|
||||
});
|
||||
facilities.value = list;
|
||||
} else {
|
||||
facilities.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取环保设施数据失败:', error);
|
||||
facilities.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 点击卡片
|
||||
const handleCardClick = (facility: any) => {
|
||||
modalData.value = facility;
|
||||
modalRes.value = {
|
||||
bldstt: '',
|
||||
hydrodtin: ''
|
||||
};
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// 监听基地变化
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
currentJidiInfo.value = newVal;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -102,7 +185,6 @@ onMounted(() => {
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
// margin-right: 8px;
|
||||
background: #2f6b98;
|
||||
border-radius: 50%;
|
||||
|
||||
@ -119,13 +201,10 @@ onMounted(() => {
|
||||
.facility-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
// margin-bottom: 4px;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
.facility-count {
|
||||
font-size: 18px;
|
||||
color: #2f6b98;
|
||||
// font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,591 @@
|
||||
<template>
|
||||
<div class="huanbao-ej-container">
|
||||
<!-- Tab切换 -->
|
||||
<a-tabs v-model:activeKey="tabIndex" @change="handleTabChange">
|
||||
<a-tab-pane
|
||||
v-for="tab in tabs"
|
||||
:key="String(tab.orderInx)"
|
||||
:tab="tab.name"
|
||||
/>
|
||||
</a-tabs>
|
||||
<!-- 搜索表单 -->
|
||||
<div class="search-form">
|
||||
<a-space size="middle">
|
||||
<div>所属基地:</div>
|
||||
<a-select
|
||||
v-model:value="searchData.baseId"
|
||||
placeholder="请选择所属基地"
|
||||
style="width: 200px"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="jiDiList"
|
||||
@change="handleBaseChange"
|
||||
/>
|
||||
<a-input
|
||||
v-if="tabIndex !== '7'"
|
||||
v-model:value="searchData.stnm"
|
||||
placeholder="请输入测站名称"
|
||||
style="width: 200px"
|
||||
allow-clear
|
||||
/>
|
||||
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<a-tooltip title="重置">
|
||||
<a-button @click="handleReset">重置</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:scrollY="400"
|
||||
:columns="columns"
|
||||
:list-url="vmsstbprptGetKendoList"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
|
||||
<template #coenvwState="{ column, record }">
|
||||
<a-tag :color="record.coenvwState > 0 ? 'green' : '#999'">
|
||||
{{ record.coenvwState > 0 ? '在线' : '离线' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #dvtp="{ column, record }">
|
||||
{{ dvtpText(record.dvtp) }}
|
||||
</template>
|
||||
<template #stindxForEng="{ column, record }">
|
||||
{{ record.dvtp == 2 ? '生态流量' : '出库流量' }}
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<a @click="handleViewDetail(record, 'detail')" class="text-link">
|
||||
查看详情
|
||||
</a>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { vmsstbprptGetKendoList } from '@/api/home';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
|
||||
// ==================== Props 定义 ====================
|
||||
const props = defineProps<{
|
||||
baseId?: string;
|
||||
tabs?: any[];
|
||||
activeKey?: string | number;
|
||||
}>();
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
const tabIndex = ref(String(props.activeKey ?? ''));
|
||||
const searchData = ref<any>({
|
||||
baseId: props.baseId || 'all',
|
||||
stnm: null
|
||||
});
|
||||
const modelStore = useModelStore();
|
||||
const tableRef = ref();
|
||||
|
||||
// ==================== 基地列表数据 ====================
|
||||
const jiDiList: any = ref([]);
|
||||
|
||||
// ==================== 表格列定义 ====================
|
||||
const columns = computed(() => {
|
||||
// 生态流量(tabIndex=7)的列
|
||||
if (tabIndex.value === '7') {
|
||||
return [
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '所属基地',
|
||||
dataIndex: 'baseName',
|
||||
width: '150px',
|
||||
merge: true
|
||||
|
||||
},
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '电站名称',
|
||||
dataIndex: 'stnm',
|
||||
width: '176px',
|
||||
|
||||
},
|
||||
{
|
||||
key: 'dvtp',
|
||||
title: '开发方式',
|
||||
dataIndex: 'dvtp',
|
||||
width: '120px',
|
||||
|
||||
slots: { customRender: 'dvtp' }
|
||||
},
|
||||
{
|
||||
key: 'stindx',
|
||||
title: '生态流量站类型',
|
||||
dataIndex: 'stindx',
|
||||
|
||||
slots: { customRender: 'stindxForEng' }
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成时间',
|
||||
dataIndex: 'jcdt',
|
||||
width: '150px',
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
key: 'coenvwState',
|
||||
title: '监测状态',
|
||||
dataIndex: 'coenvwState',
|
||||
width: '120px',
|
||||
|
||||
slots: { customRender: 'coenvwState' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
fixed: 'right' as const,
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// 其他类型的列
|
||||
const baseColumns = [
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '所属基地',
|
||||
dataIndex: 'baseName',
|
||||
width: '150px',
|
||||
merge: true
|
||||
},
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '测站名称',
|
||||
dataIndex: 'stnm',
|
||||
width: '176px',
|
||||
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '电站',
|
||||
dataIndex: 'ennm',
|
||||
width: '150px',
|
||||
|
||||
slots: { customRender: 'ennm' }
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成时间',
|
||||
dataIndex: 'jcdt',
|
||||
width: '150px',
|
||||
sort: true
|
||||
},
|
||||
{
|
||||
key: 'stindx',
|
||||
title: '监测指标',
|
||||
dataIndex: 'stindx',
|
||||
width: '150px',
|
||||
|
||||
}
|
||||
];
|
||||
|
||||
// 气象站(tabIndex=4)不显示监测状态
|
||||
if (tabIndex.value !== '4') {
|
||||
baseColumns.push({
|
||||
key: 'coenvwState',
|
||||
title: '监测状态',
|
||||
dataIndex: 'coenvwState',
|
||||
width: '120px',
|
||||
slots: { customRender: 'coenvwState' }
|
||||
} as any);
|
||||
}
|
||||
|
||||
baseColumns.push({
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
fixed: 'right' as const,
|
||||
slots: { customRender: 'action' }
|
||||
} as any);
|
||||
|
||||
return baseColumns;
|
||||
});
|
||||
|
||||
// ==================== 搜索参数 ====================
|
||||
const searchParams = computed(() => {
|
||||
return {
|
||||
sort: [
|
||||
{
|
||||
field: 'baseStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'hbrvcd',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rstcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
}
|
||||
],
|
||||
group: [],
|
||||
select: [
|
||||
'baseName',
|
||||
'stnm',
|
||||
'ennm',
|
||||
'jcdt',
|
||||
'stindx',
|
||||
'coenvwState',
|
||||
'sttpCode',
|
||||
'stcd',
|
||||
'rstcd',
|
||||
'stCode',
|
||||
'stName',
|
||||
'dvtp'
|
||||
],
|
||||
groupResultFlat: false
|
||||
};
|
||||
});
|
||||
|
||||
// ==================== 方法 ====================
|
||||
const handleTabChange = (key: string) => {
|
||||
tabIndex.value = key;
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const handleBaseChange = () => {
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const dvtpText = (dvtp: number) => {
|
||||
if (dvtp === 2) return '引水式';
|
||||
if (dvtp === 3) return '混合式';
|
||||
return '坝后式';
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
let filters: any[] = [];
|
||||
|
||||
if (searchData.value.baseId !== 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
value: searchData.value.baseId
|
||||
});
|
||||
}
|
||||
|
||||
if (searchData.value.stnm) {
|
||||
filters.push({
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
value: searchData.value.stnm
|
||||
});
|
||||
}
|
||||
|
||||
// 根据tabIndex添加不同的过滤条件
|
||||
switch (tabIndex.value) {
|
||||
case '1': // 水温监测
|
||||
filters.push(
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ENV,ENVM,WT'
|
||||
}
|
||||
);
|
||||
break;
|
||||
case '2': // 国家水质站
|
||||
filters.push(
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ENV,ENVM,WQ'
|
||||
},
|
||||
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 1 }
|
||||
);
|
||||
break;
|
||||
case '9': // 河道水质监测
|
||||
filters.push(
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'WQ' },
|
||||
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
|
||||
);
|
||||
break;
|
||||
case '10': // 增殖站水质站
|
||||
filters.push(
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'WQFB'
|
||||
},
|
||||
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
|
||||
);
|
||||
break;
|
||||
case '4': // 气象监测
|
||||
filters.push(
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'MM'
|
||||
}
|
||||
);
|
||||
break;
|
||||
case '5': // 国家水文站
|
||||
filters.push(
|
||||
{
|
||||
logic: 'or',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ST,ZQ'
|
||||
},
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ST,ZZ'
|
||||
}
|
||||
]
|
||||
},
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{ field: 'sttpCode', operator: 'in', value: ['ZZ', 'ZQ'] },
|
||||
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 1 }
|
||||
);
|
||||
break;
|
||||
case '6': // 水文监测
|
||||
filters.push(
|
||||
{
|
||||
logic: 'or',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ST,ZQ'
|
||||
},
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'ST,ZZ'
|
||||
}
|
||||
]
|
||||
},
|
||||
{ field: 'sttpCode', operator: 'in', value: ['ZZ', 'ZQ'] },
|
||||
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
|
||||
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
|
||||
);
|
||||
break;
|
||||
case '7': // 生态流量
|
||||
filters.push(
|
||||
{
|
||||
field: 'runState',
|
||||
operator: 'eq',
|
||||
value: 4
|
||||
},
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
||||
{ field: 'DTIN', operator: 'eq', dataType: 'string', value: 1 },
|
||||
{ field: 'bldsttCcode', operator: 'eq', dataType: 'string', value: 2 }
|
||||
);
|
||||
break;
|
||||
case '8': // 视频监控
|
||||
filters.push(
|
||||
{
|
||||
field: 'sttpFullPath',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: 'VD,VD'
|
||||
},
|
||||
{ field: 'dtin', operator: 'eq', dataType: 'string', value: 1 }
|
||||
);
|
||||
break;
|
||||
case '11': // 栖息地
|
||||
filters.push({
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'FH'
|
||||
});
|
||||
break;
|
||||
}
|
||||
let filter = {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
};
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
searchData.value = {
|
||||
baseId: 'all',
|
||||
stnm: null
|
||||
};
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
// 获取sttpCode对应的详情类型
|
||||
const getSttpCode = (sttpCode: string) => {
|
||||
const codeMap: Record<string, string> = {
|
||||
DW: 'DW',
|
||||
DW_1: 'DW',
|
||||
DW_2: 'DW',
|
||||
DW_3: 'DW',
|
||||
DW_4: 'DW',
|
||||
DW_5: 'DW',
|
||||
DW_6: 'DW',
|
||||
DW_9: 'DW',
|
||||
EQS: 'EQS',
|
||||
EQ: 'EQ',
|
||||
EQ_1: 'EQ',
|
||||
EQ_2: 'EQ',
|
||||
EQ_3: 'EQ',
|
||||
EQ_4: 'EQ',
|
||||
EQ_5: 'EQ',
|
||||
EQ_6: 'EQ',
|
||||
EQ_7: 'EQ',
|
||||
FB: 'EQ',
|
||||
FPC: 'FP',
|
||||
'FPC,': 'FP',
|
||||
FP: 'FP',
|
||||
FP_1: 'FP',
|
||||
FP_2: 'FP',
|
||||
FP_3: 'FP',
|
||||
FP_4: 'FP',
|
||||
FP_5: 'FP',
|
||||
FH: 'FH',
|
||||
'FH,': 'FH',
|
||||
SG: 'SG',
|
||||
'SG,': 'SG',
|
||||
TE: 'TE',
|
||||
'TE,': 'TE',
|
||||
VA: 'VA',
|
||||
'VA,': 'VA',
|
||||
VP: 'VP',
|
||||
'VP,': 'VP',
|
||||
WE: 'WE',
|
||||
'WE,': 'WE',
|
||||
WQ: 'WQ',
|
||||
'WQ,': 'WQ',
|
||||
WQH: 'WQ',
|
||||
WT: 'WT',
|
||||
'WT,': 'WT',
|
||||
WTRV: 'WT',
|
||||
'WTRV,': 'WT',
|
||||
WTVT: 'WT',
|
||||
'WTVT,': 'WT'
|
||||
};
|
||||
return codeMap[sttpCode] || '';
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleViewDetail = (record: any, type: string) => {
|
||||
if (type === 'dz') {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.rstcd;
|
||||
} else {
|
||||
const sttpCode = record.sttpCode || '';
|
||||
|
||||
if (sttpCode === 'MM') {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'MM';
|
||||
modelStore.title = record.stnm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
} else if (sttpCode === 'ENG') {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = getSttpCode(sttpCode) || sttpCode;
|
||||
modelStore.params.eqtp = 'QEC';
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.title = record.stnm;
|
||||
} else if (sttpCode.includes('VD')) {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'VD';
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.title = record.stnm;
|
||||
} else if (sttpCode.includes('WT')) {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = getSttpCode(sttpCode) || sttpCode;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.title = record.stnm;
|
||||
modelStore.params.enfc =
|
||||
sttpCode === 'WTVT' || sttpCode === 'WTVT,' ? '1' : '0';
|
||||
} else {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = sttpCode;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.title = record.stnm;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
onMounted(() => {
|
||||
JidiSelectEventStore.jidiData?.forEach((element: any) => {
|
||||
jiDiList.value.push({
|
||||
label: element.wbsName,
|
||||
value: element.wbsCode
|
||||
});
|
||||
});
|
||||
handleSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.huanbao-ej-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.ant-table-wrapper) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.text-link {
|
||||
color: #2f6b98;
|
||||
|
||||
&:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,97 +1,185 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
|
||||
<template>
|
||||
<SidePanelItem title="环保自动监测工作开展情况">
|
||||
<a-spin :spinning="loading">
|
||||
<div class="facility-grid">
|
||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
||||
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
||||
<div
|
||||
v-for="facility in facilities"
|
||||
:key="facility.orderInx"
|
||||
class="facility-card"
|
||||
@click="handleFacilityClick(facility)"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
width: 60px;
|
||||
height: 62px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
"
|
||||
>
|
||||
<div class="facility-icon">
|
||||
<img style="width: 18px;height: 14px;" :src="facility.icon ">
|
||||
<img style="width: 18px; height: 14px" :src="facility.icon" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="facility-info">
|
||||
<div class="facility-name">{{ facility.name }}</div>
|
||||
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count }}</span><span>个</span></div>
|
||||
<div style="font-size: 16px">
|
||||
<span class="facility-count">{{ facility.count }}</span>
|
||||
<span>{{ facility.unit || '个' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- 弹框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="环保自动监测工作开展情况"
|
||||
:footer="null"
|
||||
width="1536px"
|
||||
@cancel="handleCloseModal"
|
||||
>
|
||||
<HuanbaozidongjianceEJ
|
||||
v-if="modalVisible && currentFacility"
|
||||
:activeKey="currentFacility.orderInx"
|
||||
:tabs="facilities"
|
||||
:baseId="baseid"
|
||||
/>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { baseEvnmAutoMonitorGetKendoListCust } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import HuanbaozidongjianceEJ from './huanbaozidongjianceEJ/index.vue';
|
||||
// SVG 图标资源
|
||||
import svg_guojiashuiwenzhan from '@/assets/svg/guojiashuiwenzhan.svg';
|
||||
import svg_guojiashuizhizhan from '@/assets/svg/guojiashuizhizhan.svg';
|
||||
import svg_qixiangzhan from '@/assets/svg/qixiangzhan.svg';
|
||||
import svg_zijianshuiwenzhan from '@/assets/svg/zijianshuiwenzhan.svg';
|
||||
import svg_shipinjiankongshebei from '@/assets/svg/shipinjiankongshebei.svg';
|
||||
import svg_diwenshuijianhuan from '@/assets/svg/diwenshuijianhuan.svg';
|
||||
import svg_liuliangjiancezhan from '@/assets/svg/liuliangjiancezhan.svg';
|
||||
import svg_shuizhijiancezhan from '@/assets/svg/shuizhijiancezhan.svg';
|
||||
import svg_zengzhizhanshuizhizhan from '@/assets/svg/zengzhizhanshuizhizhan.svg';
|
||||
import svg_qixidi from '@/assets/svg/qixidi.svg';
|
||||
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'huanbaoMod'
|
||||
});
|
||||
|
||||
// 设施数据
|
||||
const facilities = ref([
|
||||
{
|
||||
"name": "生态流量",
|
||||
"count": 452
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwMDEuNTIzIDgzOS4zNTVjLTE4LjEyNi04LjQ5OS0zOS4zMjQuMTU0LTQ3LjM2MyAxOS4zNTQtLjE0MS4zMzktLjI3OC42OC0uNDA5IDEuMDI0LTI4LjY3NCA3Mi4zNDQtMTA3LjMyIDEwNi4zNC0xNzUuNjI1IDc1LjkyOGExMzguNTUzIDEzOC41NTMgMCAwIDEtNzIuMzQ5LTc3LjgyM2MtNy42OC0xOS44MTQtMjguOTI5LTI5LjQzOS00Ny43NzItMjEuNTU1YTM2LjY2NSAzNi42NjUgMCAwIDAtMTYuODk3IDE0Ljc5N2MtMS42ODkgMi40NTgtMy40ODIgNS41ODEtNS4yMjIgOC42MDEtMjAuOTQxIDUyLjY4NC02OS41ODQgODYuOTg3LTEyMy40NDkgODcuMDM5LTUzLjU1OCAwLTEwMS4zMy0zMy45OTctMTIyLjg4Ni04NS4xOTVhMzguOTE0IDM4LjkxNCAwIDAgMC0yMS41MDUtMjUuODU2Yy0xOC43OTItNy44ODUtNDAuMDkxIDEuNjktNDcuNzcyIDIxLjUwNC0yMC4zMjcgNTQuMDY2LTY5LjY4NyA4OS40OTUtMTI0LjU3NSA4OS41NDctNTUuMTk3LS4wNTItMTA0LjgxMi0zNS43MzctMTI1LjI5Mi05MC4xMTEtNy40MjUtMTkuNzExLTI4LjMxNi0yOS41NDEtNDcuMTA3LTIyLjIyLTE4LjYzOCA3Ljg4NS0yNy45NTcgMzAuMDU0LTIwLjk5MyA1MC4wMjEgNDEuOTg2IDExMy4wOTkgMTYyLjUxNyAxNjguNzUyIDI2OS4yNzMgMTI0LjI2YTIwOC40MzUgMjA4LjQzNSAwIDAgMCA4Mi40MzYtNjIuNTY1Yzc0LjE5MyA5Mi43NzMgMjA1LjMyMiAxMDQuMTkgMjkyLjc3NiAyNS40OTdhMjE0LjQ2IDIxNC40NiAwIDAgMCAyNC41NzctMjYuMTExYzczLjYzIDkyLjk3NyAyMDQuNDUyIDEwNS4wNiAyOTIuMTYxIDI2Ljk4MiAyNS4yNDMtMjIuNDI1IDQ1LjA1OS01MC45NDMgNTcuODA4LTgzLjA5NiA3LjQzNC0xOS4yOS0xLjE4Ny00MS4wNTUtMTkuODE1LTUwLjAyMiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMDAxLjUyMyA2NDcuMzU5Yy0xOC4xMjYtOC40OTktMzkuMzI0LjE1NC00Ny4zNjMgMTkuMzU0LS4xNDEuMzM5LS4yNzguNjgtLjQwOSAxLjAyNC0yOC42NzQgNzIuMzQ0LTEwNy4zMiAxMDYuMzQtMTc1LjYyNSA3NS45MjhhMTM4LjU1MyAxMzguNTUzIDAgMCAxLTcyLjM0OS03Ny44MjNjLTcuNjgtMTkuODE0LTI4LjkyOS0yOS40MzktNDcuNzcyLTIxLjU1NWEzNi42NjUgMzYuNjY1IDAgMCAwLTE2Ljg5NyAxNC43OTdjLTEuNjg5IDIuNDU4LTMuNDgyIDUuNTgxLTUuMjIyIDguNjAxLTIwLjk0MSA1Mi42ODQtNjkuNTg0IDg2Ljk4Ny0xMjMuNDQ5IDg3LjAzOS01My41NTggMC0xMDEuMzMtMzMuOTk3LTEyMi44ODYtODUuMTk1YTM4LjkxNCAzOC45MTQgMCAwIDAtMjEuNTA1LTI1Ljg1NmMtMTguNzkyLTcuODg1LTQwLjA5MSAxLjY5LTQ3Ljc3MiAyMS41MDQtMjAuMzI3IDU0LjA2Ni02OS42ODcgODkuNDk1LTEyNC41NzUgODkuNTQ3LTU1LjE5Ny0uMDUyLTEwNC44MTItMzUuNzM3LTEyNS4yOTItOTAuMTExLTcuNDI1LTE5LjcxMS0yOC4zMTYtMjkuNTQxLTQ3LjEwNy0yMi4yMi0xOC42MzggNy44ODUtMjcuOTU3IDMwLjA1NC0yMC45OTMgNTAuMDIxIDQxLjk4NiAxMTMuMDk5IDE2Mi41MTcgMTY4Ljc1MiAyNjkuMjczIDEyNC4yNmEyMDguNDM1IDIwOC40MzUgMCAwIDAgODIuNDM2LTYyLjU2NWM3NC4xOTMgOTIuNzczIDIwNS4zMjIgMTA0LjE5IDI5Mi43NzYgMjUuNDk3YTIxNC40NiAyMTQuNDYgMCAwIDAgMjQuNTc3LTI2LjExMWM3My42MyA5Mi45NzcgMjA0LjQ1MiAxMDUuMDYgMjkyLjE2MSAyNi45ODIgMjUuMjQzLTIyLjQyNSA0NS4wNTktNTAuOTQzIDU3LjgwOC04My4wOTYgNy40MzQtMTkuMjktMS4xODctNDEuMDU1LTE5LjgxNS01MC4wMjIiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNNDg2LjA2NyAwYzYuOTY0IDAgMTMuODI1IDIuOTE4IDE4LjYzOCA4LjA4OWwxOC42MzggMjAuNjM0IDIxLjYwNyAyNS40NDUgMTIuOTAzIDE1LjgyMUw1NzkgOTcuMTI0bDE0Ljg0OSAyMC4xMjIgMTUuMjA3IDIxLjUwMyAxNS4yNTggMjIuNjNhODk3LjgzNCA4OTcuODM0IDAgMCAxIDIzLjI0NiAzNy41OGwxMi4wODQtMTYuMTI4IDUwLjY5LTY1Ljg0MSA1MC42OSA2NS43OWMxNS4yNTkgMTkuNzEyIDI5LjY5OCAzOS45MzUgNDMuMzE4IDYwLjk3OCA0My4yNjYgNjcuMDcxIDY3LjA3NiAxMjUuNTkxIDY3LjA3NiAxNzQuNTg5LS4xNTQgOTAuMjEyLTcxLjg4OSAxNjQuMjQ2LTE2MS4xODYgMTY0LjI0Ni0yMS4xOTggMC00Mi4wODktNC4yNS02MS40NDMtMTIuMzlsLTEuOTk3IDIuMTVhMjMwLjY2IDIzMC42NiAwIDAgMS01Ni4xNjkgNDEuNzI3IDIyMC4zNDggMjIwLjM0OCAwIDAgMS04OC43ODUgMjUuMzk1bC0xNS42NjkuNTEyYTIyNS4xNCAyMjUuMTQgMCAwIDEtMTYyLjgyMi02OS42MzEgMjQwLjYyIDI0MC42MiAwIDAgMS02Ny4zODMtMTY4LjI0YzAtNzEuMDY0IDM1Ljk5NS0xNTkuMjc5IDEwNi45MS0yNjIuMzQzbDcuNjgxLTExLjA1OSAxNS4zMDktMjEuMTQ1IDE0Ljc5OC0xOS42MDkgMTQuMDI5LTE3LjkyIDE4Ljk0NS0yMy4yOTUgMTUuNjY4LTE4LjQzMkw0NjcuNDMgOC4xOTJhMjUuNjA1IDI1LjYwNSAwIDAgMSAxMy40NjYtNy42OEw0ODYuMDY3IDBabS4zMDcgODMuNDU0LTMuNTg0IDQuMzUyYTExOTguMTA0IDExOTguMTA0IDAgMCAwLTY3LjE3NyA4OC4zMThjLTY0LjI2IDkzLjM4Ny05NS42NDYgMTcwLjMzOS05NS42NDYgMjI2LjA0NCAwIDQ2Ljk0OSAxNy41NjMgOTAuNzI0IDQ5LjM1OSAxMjMuNjQ1IDMxLjQ4OSAzMi41MTEgNzIuNzA4IDUwLjE3NSAxMTYuODQzIDUwLjE3NSAyNi40MjEgMCA1MS4yNTUtNi4xNDQgNzQuNjU0LTE4LjUzNCAxMi4yODktNi40NTEgMjMuODYxLTE0LjY0MyAzNC40MDktMjQuMjY4QTE2Ni4wMyAxNjYuMDMgMCAwIDEgNTQ5LjIgNDE4LjI5NWMwLTQ0LjY0NSAyMC45OTMtOTkuMzI2IDU4LjI2OS0xNjAuMTVsLTkuNTI0LTE3LjFhMTAyNi42MDMgMTAyNi42MDMgMCAwIDAtMTA2Ljg2LTE1Mi4wNjFsLTQuNzExLTUuNTNabTIyNC4wMTIgMTM4LjM5MS0xMy45NzkgMTguNjM2Yy03LjUyNyAxMC4zNDMtMTUuNzcgMjIuMTE4LTI0LjAxNCAzNC43NjUtMzguMTQ2IDU4LjUyLTU5LjE4OSAxMDkuNDEyLTU5LjE4OSAxNDMuMDQ5IDAgMzUuOTQyIDE4LjczOSA2OS4zMjQgNDkgODcuMTQxIDE0LjQ5IDguNjUzIDMxLjE4MiAxMy4xNTggNDguMDI4IDEzLjE1OCA1My41MDYgMCA5Ny4wOC00NS4wMDQgOTcuMTMxLTEwMC4zNSAwLTMzLjc5MS0xOS42NjItODIuMTc0LTU2LjgzNC0xMzkuNzczbC04LjYwMi0xMy4xMDctOC41NTEtMTIuMzM5LTE1LjkyNC0yMS45MTMtNy4wNjYtOS4yNjdaIiBmaWxsPSIjZmZmIi8+PC9zdmc+"
|
||||
},
|
||||
{
|
||||
"name": "水温监测",
|
||||
"count": 140
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDExMjUgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMwLjgzOCA4MzIuOTM5YzE5LjE4Mi00LjA5NiAzOS4wOTQgNS42MzIgNDYuNTc2IDIzLjcwNmw0LjA1IDguOTA4YzI0LjUyNSA0OS4zMDYgNzYuMjc3IDgxLjE1MiAxMzMuNTk1IDgxLjIwNGwxMC4wMTQtLjMwOGM1Ni4yNTEtMy43MzcgMTA1Ljc1MS0zOC4xOTUgMTI2Ljg0Ni04OS4xODlsMi40MTgtNC43NjNjOS4zODEtMTYuNTY0IDMxLjExNi0yMy44NTUgNTAuMDY0LTE2Ljc5M2w1LjU2OCAyLjcxNGM4Ljk4OCA1LjM5NiAxNS40NDYgMTMuNjQ3IDE4LjExMyAyMy4xNDJsNC41IDguODU4YzI1LjMxMyA0Ni4yMzMgNzUuMDM5IDc2LjMzOSAxMzAuNDQ1IDc2LjMzOSA1OS4xNzctLjA1MiAxMTIuNjE1LTM0LjM1NiAxMzUuNTY2LTg3LjA0bDIuOTI1LTQuNTA1IDIuODY5LTQuMDk3YzQuMzI1LTYuNTk3IDEwLjgxOS0xMS43OTEgMTguNTYyLTE0Ljg0OGw1LjIzMi0xLjUzNWMxOS45NTQtNC40NDMgNDAuMzY2IDUuNTc2IDQ3LjI1IDIzLjE5M2w0LjMzMiA5LjM2OWMxNS4xNTMgMzAuMzMxIDQxLjg3MyA1NC42MzMgNzUuMjA4IDY4LjQwMyA3NC45ODIgMzAuNDEzIDE2MS4zODQtMy41ODMgMTkyLjk0LTc1Ljk4bC4xNjktLjUxMmM5LjA1Ni0xOS42NjEgMzIuMzQzLTI4LjMxMyA1Mi4zMTMtMTkuODE0bDUuMjMxIDIuNzEzYzE2LjkzNSAxMC4zNDcgMjMuNzcxIDI5Ljk3MSAxNi40ODEgNDcuMzA5LTEzLjc2NCAzMS44MjUtMzUuNTEyIDYwLjI4MS02My41MDcgODMuMDk2LTk2LjM1OCA3OC4wOC0yNDAuMDc4IDY1Ljk5Ny0zMjAuOTY3LTI2Ljk4MmwtOC40MzggOS4yMTdhMjI1LjQ0MSAyMjUuNDQxIDAgMCAxLTE4LjU2MyAxNi44OTZjLTk2LjEzMyA3OC42OTQtMjQwLjE5MSA2Ny4yNzYtMzIxLjY5OS0yNS40OThsLTkuNDUgMTAuMTM3Yy0yMi40NTMgMjIuNjc4LTUwLjEzMSA0MC41NjItODEuMDU3IDUyLjM3OC0xMTcuMjg0IDQ0LjQ5Mi0yNDkuNzU1LTExLjE2Mi0yOTUuODgtMTI0LjI2MmwtMS41NzUtNS40NzhjLTQuMjkyLTE4LjI4NSA1Ljk4NS0zNi44MjMgMjQuNjkzLTQ0LjU0NFpNNTQ0Ljk3MS4wMjNjOTEuMTI3LTEuNDM0IDE2Ni44OTcgNjMuNDM2IDE3My43MDQgMTQ2LjY4NmwuNDUgOS4zMnYxNzUuMzU5YzkgNi42NTUgMTcuNDM3IDEzLjk3NyAyNS4xNDMgMjEuOTY0bDguMzI2IDkuMjE2YzkxLjI5NCAxMDEuMzc2IDc5Ljk4OCAyNTAuNTcxLTIzLjUxNCAzMzkuMDQ0bC0xMS4xOTQgOS4wNjNjLTExMi4yMDQgODcuMjY5LTI4MS4zNzUgNzMuMjAyLTM3NC4zNS0zMS4xMjktOTEuNjg4LTEwMS42ODMtODAuNTUtMjUxLjQ5MyAyMy4xNzctMzQwLjYzMmwxMC4wMTItOC4xOTJWMTU2LjAyOUMzNzguMzU2IDcwLjUyNSA0NTIuNjYzIDEuNTU5IDU0NC45MTYuMDIzWm0xLjIzOSA2My45OTljLTUxLjI0NS44NzEtOTMuNTQ3IDM3LjU4MS05OC42NjUgODQuMTIybC0uNTA3IDcuODg1djIwNC4xODNsLTI0LjYzNyAxOS4xNDljLTgzLjgxNCA2NS4yMjktOTQuODQgMTgxLjYwNi0yNC41MjUgMjU5LjU4MyA2NS4xNzcgNzMuNTQyIDE4Mi43NTUgODYuNjQ3IDI2NS41MDMgMjkuNTkzbDEwLjE4Mi03LjUyNmM4MC40MzktNjIuMjU5IDkzLjY1OC0xNzIuMDgzIDMxLjgzOC0yNDkuMzQybC05LjMzOC0xMC45MDZhMTIyLjE5NSAxMjIuMTk1IDAgMCAwLTEzLjUtMTMuNTE3bC03LjQ4MS01Ljk5LTI2LjI3LTE5LjJWMTU3LjEwNGMtLjk1Ni00OS45Mi00Mi43NS04OS44MDUtOTQuMzg5LTkyLjg3N2wtOC4yMTEtLjIwNVptMS41NzQgMTI4IDUuMjg4LjM1OGMxNy4yMTIgMi4zNTUgMzAuMzc1IDE1Ljg3MiAzMC40MzEgMzIuMTU0djIxMi4wMThjMzAuMzc2IDkuNjI1IDU0LjA1OCAzMS40MzcgNjQuMzUxIDU5LjEzNWwyLjUzMiA3LjU3N2MxNC4wODEgNDguNzY4LTE1LjI3IDk5LjIxNy02Ny41MDIgMTE2LjAxOWwtOC41NSAyLjI1M2MtNzkuNzA3IDE3LjQxMy0xNTAuMjM0LTUwLjI0LTEyNi45NDktMTIxLjc3NiA5LjQ4My0yOS4xMzMgMzMuNDI3LTUyLjUzMiA2NC42NzktNjMuMjA4VjIyNC41ODRjLjAzMi0xNy45NTIgMTUuOTk2LTMyLjUwNiAzNS43Mi0zMi41NjJaIiBmaWxsPSIjZmZmIi8+PC9zdmc+"
|
||||
},
|
||||
{
|
||||
"name": "水文监测",
|
||||
"count": 21
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZD0iTTkuOTcyIDEyLjc2YzEuMTA0IDAgMS42ODYuMTU3IDIuMi4yOTMuNDgxLjEyOC44OTQuMjQgMS43ODYuMjRWMTVjLTEuMSAwLTEuNjg1LS4xNTQtMi4yLS4yOTMtLjQ4LS4xMjgtLjg5NC0uMjQtMS43ODYtLjI0cy0xLjMwNi4xMTItMS43ODYuMjRjLS41MTQuMTM2LTEuMDk4LjI5My0yLjIuMjkzLTEuMTAxIDAtMS42ODYtLjE1NC0yLjItLjI5My0uNDgxLS4xMjgtLjg5NS0uMjQtMS43ODYtLjI0di0xLjcwOWMxLjEgMCAxLjY4NS4xNTUgMi4yLjI5NC40OC4xMjguODk0LjI0IDEuNzg2LjI0czEuMzA2LS4xMTIgMS43ODctLjI0Yy41MTQtLjEzNiAxLjA5OC0uMjk0IDIuMi0uMjk0di4wMDFabS4wMjYtMi42OTVjMS4xMDIgMCAxLjY4NS4xNTggMi4yLjI5My40OC4xMjkuODk1LjI0IDEuNzg3LjI0djEuNzA4Yy0xLjEwMiAwLTEuNjg2LS4xNTUtMi4yLS4yOTMtLjQ4MS0uMTI5LS44OTUtLjI0LTEuNzg2LS4yNC0uODkyIDAtMS4zMDYuMTExLTEuNzg3LjI0LS41MTUuMTM1LTEuMDk5LjI5My0yLjIuMjkzLTEuMTAxIDAtMS42ODUtLjE1NS0yLjItLjI5My0uNDgtLjEyOS0uODk1LS4yNC0xLjc4Ny0uMjR2LTEuNzA3YzEuMTAyIDAgMS42ODYuMTU0IDIuMi4yOTIuNDgyLjEyOS44OTUuMjQgMS43ODcuMjQuODkyIDAgMS4zMDUtLjExMSAxLjc4Ni0uMjQuNTE1LS4xMzUgMS4xLS4yOTMgMi4yLS4yOTNaTTE0IDF2Ny44NDloLTEuNjU0VjIuNzA4SDMuNjY1djYuMTRIMi4wMVYxSDE0Wk04Ljg4NCA0LjEyNHY0LjcyNUg3LjIzVjQuMTI0aDEuNjU1WiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9nPjwvc3ZnPg=="
|
||||
},
|
||||
{
|
||||
"name": "河道水质监测",
|
||||
"count": 56
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0ibTY2NS4yMTIgMCAuNTEyIDMwNy44MTRjMCAzMy41ODggNy44ODUgNjcuMTc1IDIyLjIyIDk3LjI4bDMyNC41MDYgNDYwLjY5OC43MTcgMi4wNDhjMTYuNDM1IDM0LjMwNCAxNC4zMzYgNzMuNTc0LTYuNDUxIDEwNS43MjhBMTE0LjMzIDExNC4zMyAwIDAgMSA5MTEuNjM4IDEwMjRoLTc5OS4xM2MtMzguNjA1IDAtNzQuMzQyLTE4Ljk0NC05NS4wNzgtNTEuMDk4YTEwNy40MTggMTA3LjQxOCAwIDAgMS02LjQ1Mi0xMDUuNzI4bDEuNDM0LTIuODE2IDMwNy4zNTQtNDU4LjU0N2MxNC4zMzYtMzAuODIyIDIyLjE3LTY0LjQxIDIyLjE3LTk3Ljk5N0wzNDEuNDIyIDBoMzIzLjc4OXptLTYzLjg0NiA2NGgtMTk1Ljg0bC40MSAyNDMuODE0YzAgMzYuNTU3LTcuMTY5IDczLjIxNi0yMC43ODggMTA3LjgyOGwtNy4zMjIgMTcuMS00Ljg2NCA4LjcwNEw2OC4xNyA4OTZsLTEuOTk3IDUuMTJhNDIuODAzIDQyLjgwMyAwIDAgMCAxLjg0MyAzMS4yODNsMy4yMjYgNS44MzdhNDcuNzE4IDQ3LjcxOCAwIDAgMCAzMy45OTcgMjEuMjQ4bDcuMjcuNTEyaDc5OS4xM2E0OS42NjQgNDkuNjY0IDAgMCAwIDQxLjI2Ny0yMS4wNDMgNDMuMDYgNDMuMDYgMCAwIDAgNS4wNjktMzcuMTJsLTIuMS01LjI3NC0zMjAuMjU2LTQ1NC42MDUtNS41My05LjM3YTI5My4zNzYgMjkzLjM3NiAwIDAgMS0yNy44NTItMTA4LjY0NmwtLjUxMi0xNi4wMjVMNjAxLjM2NiA2NHoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMjU2LjA3MyAwaDUxMnY2NGgtNTEyek0zNjUuMjMxIDY4Ni40OWE4My45NjggODMuOTY4IDAgMCAxIDgzLjYxIDgzLjYxYzAgNDMuMDA3LTMzLjE3OCA3OC44NDctNzUuMTEgODMuMDk3bC04LjUuNDZhODMuOTY4IDgzLjk2OCAwIDAgMS04My41NTgtODMuNTU4YzAtNDMuMDA4IDMzLjEyNi03OC44NDggNzUuMDU5LTgzLjE0OWw4LjUtLjQ2em0yNDUuMDk1LTE4Ljg0MmM1MS41MDcgMCA5Mi45NzkgNDEuNDcyIDkyLjk3OSA5My4wMyAwIDQ4LjQ4Ny0zNi43MSA4OC4wNjQtODMuOTY4IDkyLjU3bC05LjAxMS40MWE5Mi43NzQgOTIuNzc0IDAgMCAxLTkzLjAzLTkyLjk4YzAtNDguNTM3IDM2Ljc2LTg4LjA2NCA4NC4wMTgtOTIuNjJsOS4wMTItLjQxek0zNjUuMjMgNzUwLjQ5YTE5Ljk2OCAxOS45NjggMCAwIDAtMTkuNTU4IDE5LjYxIDE5Ljk2OCAxOS45NjggMCAwIDAgMTkuNTU4IDE5LjU1OCAxOS45NjggMTkuOTY4IDAgMCAwIDE5LjYxLTE5LjU1OSAxOS45NjggMTkuOTY4IDAgMCAwLTE5LjYxLTE5LjYxem0yNDUuMDk1LTE4Ljg0MmEyOC43NzQgMjguNzc0IDAgMCAwLTI5LjAzIDI5LjAzYzAgMTYuMTggMTIuOCAyOC45OCAyOS4wMyAyOC45OCAxNi4xNzkgMCAyOC45NzktMTIuOCAyOC45NzktMjguOThhMjguNzc0IDI4Ljc3NCAwIDAgMC0yOC45OC0yOS4wM3pNNTAyLjI0MiA0MjEuOTM5YTExNi4zNzggMTE2LjM3OCAwIDAgMSAxMTYuMjI0IDExNi4yMjRjMCA2MC45MjgtNDcuNjE2IDExMS4wNTMtMTA2LjgwMyAxMTUuOTE3bC05LjQyLjM1OGExMTYuMzc4IDExNi4zNzggMCAwIDEtMTE2LjIyNS0xMTYuMjI0YzAtNjAuOTI4IDQ3LjA1My0xMTEuMDAxIDEwNi43MDEtMTE1Ljg2NWw5LjUyMy0uNDF6bTAgNjRjLTI4Ljc3NCAwLTUyLjIyNCAyMy41NTItNTIuMjI0IDUyLjIyNCAwIDI4Ljc3NSAyMy40NSA1Mi4yNzUgNTIuMjI0IDUyLjI3NSAyOC4zNjUgMCA1Mi4yMjQtMjMuNzU2IDUyLjIyNC01Mi4yMjQgMC0yOC43NzQtMjMuNS01Mi4yMjQtNTIuMjI0LTUyLjIyNHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
|
||||
},
|
||||
{
|
||||
"name": "增殖站水质站",
|
||||
"count": 9
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI0IDBjMTIuODQgMCAyMy4yNSAxMC43NDUgMjMuMjUgMjRTMzYuODQgNDggMjQgNDggLjc1IDM3LjI1NS43NSAyNCAxMS4xNiAwIDI0IDBabS0uMDA1IDYuNjY4Yy05LjUwMSAwLTE3LjIwMiA3LjgyNC0xNy4yMDIgMTcuNDc2IDAgOC4zMjcgNS43MzIgMTUuMjg4IDEzLjQwNSAxNy4wNDVoNy41OTNjNy42NzMtMS43NTcgMTMuNDA2LTguNzE4IDEzLjQwNi0xNy4wNDUgMC05LjY1Mi03LjctMTcuNDc2LTE3LjIwMi0xNy40NzZabS4xOTggMS42OTNjOC40IDAgMTUuMjEgNi42NiAxNS4yMSAxNC44NzQgMCAuMDk4LS4wMDYuMTkzLS4wMDguMjloLS4wNDdjLS4wMDMtLjA1My0uMDEyLS4xMDUtLjAxNy0uMTU3LS4yNCA0LjI1Mi0zLjUwOSA3LjYyMS03LjUwNyA3LjYyMWE3LjAyNSA3LjAyNSAwIDAgMS0yLjU5My0uNDk1bC0uMTM2LjEwMmE4LjM5MSA4LjM5MSAwIDAgMC00LjYyMy0xLjM3IDguMzk5IDguMzk5IDAgMCAwLTQuMjMgMS4xMzMgNi44NzIgNi44NzIgMCAwIDEtMy45NTMgMS4yNjNjLTQuMDggMC03LjM4Ny0zLjYzOS03LjM4Ny04LjEyNSAwLS41NTUuMDUtMS4wOTcuMTQ3LTEuNjJDOS43NSAxNC4zIDE2LjI2IDguMzYgMjQuMTkzIDguMzZabS0uMTk1IDIuNzMxLS4wMDUuMTUzLS4wMDMtLjE1MmMtLjAwMi0uMDEtLjA2MS4wOS0uMTY1LjI3N2wtLjA4Ny4xNTVjLS4zNzUuNjgzLTEuMTIzIDIuMDc4LTEuODg1IDMuNTA2bC0uMjE2LjQwNWMtLjY2NiAxLjI1LTEuMzMgMi41MDItMS45OTQgMy43NTRhNS41MjIgNS41MjIgMCAwIDAtMS4wNTUgMy4yNTljMCAzLjAzMyAyLjQyIDUuNDkxIDUuNDA3IDUuNDkxIDIuOTg1IDAgNS40MDUtMi40NTggNS40MDUtNS40OTFhNS41MjcgNS41MjcgMCAwIDAtMS4wNTQtMy4yNTkgMTg2My4xNSAxODYzLjE1IDAgMCAwLTEuODg2LTMuNTUxbC0uNDMyLS44MWMtMS4wNC0xLjk1LTIuMDIzLTMuNzc3LTIuMDMtMy43MzdabTQuMDUyIDExLjM0MmMuMDAyLjA0OS4wMDYuMDk4LjAwNi4xNDcgMCAyLjE4Ny0xLjU0MiAzLjk2LTMuNDQ1IDMuOTYtMS4yMzggMC0yLjMyMy0uNzUtMi45My0xLjg3OS42ODMuNzI1IDEuNjE0IDEuMTcyIDIuNjQgMS4xNzIgMS44NjQgMCAzLjQxMS0xLjQ2NyAzLjcyOS0zLjRaIiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4="
|
||||
},
|
||||
{
|
||||
"name": "国家水质站",
|
||||
"count": 1722
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTguMDEzIDEyLjc5OGMxLjE3NC4wMDIgMi4yOTYtLjQ0MyAzLjEwNC0xLjIyOS44My0uODA4IDEuMjkyLTEuODggMS4yODgtMi45OTQgMC0yLjMzNi0xLjcyLTQuNzM1LTQuMzktOC41NzUtMi42OCAzLjg0LTQuMzkyIDYuMjQtNC4zOTIgOC41NzUgMCAxLjEyMy40NjQgMi4yMDIgMS4yODggMi45OTQuODA4Ljc4NiAxLjkzIDEuMjMgMy4xMDMgMS4yMjloLS4wMDFaTTcuOTggMi43MmMuMzUzLjUyMy41NDYgMS4xMzMuNTYyIDEuNzU5IDAgMi4wOC0yLjIxNCAyLjQzLTIuMjE0IDEuMTVDNi4zNiA0LjggNy40NSAzLjQ5IDcuOTgyIDIuNzJ2LjAwMmgtLjAwMVptLjAzMyAxMy4yNzdjLTEuOTUyIDAtNC43My0uNDgtNi4wMTktMS44ODhhOC41ODQgOC41ODQgMCAwIDEgMS42ODctLjcwNGMuNzI2LjQ4IDIuMjEzLjk5MiA0LjMzMi45OTIgMi4xMTggMCAzLjYwNC0uNTEyIDQuMzY0LS45OTJhOC42NSA4LjY1IDAgMCAxIDEuNjkuNzA2QzEyLjc0NiAxNS41MiA5Ljk2OCAxNiA4LjAxNyAxNmwtLjAwMy0uMDAyWm01LjkxNy04Ljg2MmMuNTMtLjgzMyAxLjQ1NC0uNDUgMS45MjEtMS4yMTcuNTYzLS44OTYtLjU5Ni0xLjgyNC0xLjUyLTEuMDI0LS40OTkuNDU1LS41OTcgMS4zMTItLjQgMi4yNGwtLjAwMS4wMDFaTTEuNjY2IDQuODljLS44OTQtLjgzMi0yLjA4My4wOTYtMS41MiAxLjAyNC40OTQuNzY3IDEuMzg5LjM4NCAxLjkyIDEuMjE2LjIyOC0uOTIzLjEyOS0xLjc4MS0uNC0yLjIzNlY0Ljg5WiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+"
|
||||
},
|
||||
{
|
||||
"name": "视频监控",
|
||||
"count": 247
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDExMzcgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE0Ny43ODggMCA5NDcuNjYxIDI2My4wMDItNzUuMzE1IDk4LjA4NUwxMTM3IDM5Mi4zOTdsLTc4Ljc4MiAyOTQuNDgyLTIzOC43MzQtNjQuMDk5LTc1LjI1OCA5OC4xOTgtMzM5LjM0NC05NS4xMjR2MjA3LjYxMkg3My42NjdWMTAyNEgwVjY0Mi45ODloNzMuNjF2MTE0LjI1MmgyNTcuNzE5bC0uMDU3LTE1MS45OTVMOS45NDcgNTE1LjI0NSAxNDcuNzg4IDBaTTg1MS43NyAyNzQuMzg3IDYwNS4wNzggNjAyLjg1NWwxMTIuMjYyIDMxLjQyNCAyNTEuMTI1LTMyNy41TDg1MS43NyAyNzQuMzNaTTk3MCA0MjYuNDk2IDg2OS42MTggNTU3LjM3MWwxMzcuMTAyIDM2Ljc3NCAzOS4zOTEtMTQ3LjIxMi03Ni4xNjgtMjAuNDk0Wk0xOTguOTQ1IDkzLjE4OWwtOTguNTYzIDM2OC4zMTcgNDI4LjI0NCAxMTkuODg4IDI0Ni42OTItMzI4LjI5N0wxOTguOTQ1IDkzLjEzMloiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
|
||||
},
|
||||
{
|
||||
"name": "气象监测",
|
||||
"count": 74058
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZD0iTTQuMTgzIDE2YS42NDIuNjQyIDAgMCAxLS42NC0uNjQ4LjY0Mi42NDIgMCAwIDEgLjY0LS42NDdINS41OXYtMS43N2E1LjkzIDUuOTMgMCAwIDEtMS43ODktMS4yNTRjLTIuNzUxLTIuNzgyLTEuODktNy4yNS0uNDU2LTguNjk2IDEuMTI3LTEuMTQgMy4xODEtLjUxNSA1LjA2Ni44NzdhLjI2LjI2IDAgMCAxIC4xMDIuMjVMOC4zNDYgNS4xOWwtLjEzLjA2YS4yNi4yNiAwIDAgMS0uMjc0LS4wNGMtMS41MDYtMS4yOS0zLjAwNy0xLjg0OC0zLjYzLTEuMjYtLjc2LjcxNi4wNzcgMi44NzQgMS44NzIgNC44MTggMS43OTQgMS45NDUgMy44NjQgMi45NCA0LjYyNSAyLjIyMi41ODctLjU1My4yMjItMS45NjctLjgwMS0zLjQ3N2wuMTIyLS4yMyAxLjE1NC0uMzc4YzEuMjA3IDEuOTA1IDEuNjM4IDQgLjU2IDUuMDktLjk0My45NTItMi45MzUgMS43MjItNC45NzMgMS4zNDF2MS4zN2gyLjQzMmEuNjQzLjY0MyAwIDAgMSAuNjQuNjQ3LjY0My42NDMgMCAwIDEtLjY0LjY0N2gtNS4xMlptMy4zMjMtOC4wMi0uMTQ0LS4xNDVhLjY5Mi42OTIgMCAwIDEgLjA1LTEuMDM5bDEuNTE2LTEuMjUzYS44NTkuODU5IDAgMCAxIC44NDgtLjEyNy43NzMuNzczIDAgMCAxLS4xNTUtLjQ2OS43NC43NCAwIDAgMSAuNzE2LS43NTguNzM5LjczOSAwIDAgMSAuNzE3Ljc1OC43MzcuNzM3IDAgMCAxLS43MTcuNzU3LjY4NS42ODUgMCAwIDEtLjQzNy0uMTU3Ljg4Ljg4IDAgMCAxLS4xMjYuODUybC0xLjI0IDEuNTNhLjY4NS42ODUgMCAwIDEtLjUzNS4yNjUuNjkuNjkgMCAwIDEtLjQ5My0uMjE0Wm02LjQ2LTIuMjhjMC0yLjU0LTEuOTQ4LTQuNjA3LTQuMzQzLTQuNjA3VjBDMTIuNTg4IDAgMTUgMi41NTcgMTUgNS42OTloLTEuMDMzWm0tMS44MjggMEEyLjczNSAyLjczNSAwIDAgMCAxMS40IDMuODFhMi40MjYgMi40MjYgMCAwIDAtMS43NzctLjc4M3YtMS4wOWMxLjk1OCAwIDMuNTUgMS42ODcgMy41NSAzLjc2MWgtMS4wMzVaIiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L2c+PC9zdmc+"
|
||||
},
|
||||
{
|
||||
"name": "国家水文站",
|
||||
"count": 1189
|
||||
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNMyAxNCA4IDBsNSAxNHoiLz48L2c+PC9zdmc+"
|
||||
interface IFacility {
|
||||
name: string;
|
||||
count: number | string;
|
||||
orderInx: number;
|
||||
icon: string;
|
||||
unit?: string | null;
|
||||
}
|
||||
]);
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
// 图标映射(orderInx → SVG 路径)
|
||||
const iconMap: Record<number, string> = {
|
||||
1: svg_diwenshuijianhuan, // 水温监测
|
||||
2: svg_guojiashuizhizhan, // 国家水质站
|
||||
4: svg_qixiangzhan, // 气象监测
|
||||
5: svg_guojiashuiwenzhan, // 国家水文站
|
||||
6: svg_zijianshuiwenzhan, // 水文监测
|
||||
7: svg_liuliangjiancezhan, // 生态流量
|
||||
8: svg_shipinjiankongshebei, // 视频监控
|
||||
9: svg_shuizhijiancezhan, // 河道水质监测
|
||||
10: svg_zengzhizhanshuizhizhan, // 增殖站水质站
|
||||
11: svg_qixidi // 栖息地
|
||||
};
|
||||
|
||||
const facilities = ref<IFacility[]>([]);
|
||||
const loading = ref(false);
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
|
||||
// 弹框相关状态
|
||||
const modalVisible = ref(false);
|
||||
const currentFacility = ref<IFacility | null>(null);
|
||||
|
||||
const fetchData = () => {
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'showIds',
|
||||
operator: 'in',
|
||||
value: ['7', '1', '6', '9', '10', '2', '8', '4', '5']
|
||||
},
|
||||
baseid.value != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
value: baseid.value
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
loading.value = true;
|
||||
baseEvnmAutoMonitorGetKendoListCust(params)
|
||||
.then((res: any) => {
|
||||
const data = res?.data?.data || [];
|
||||
facilities.value = data.map((item: any) => ({
|
||||
name: item.sttpName,
|
||||
count: item.cnt,
|
||||
orderInx: item.orderInx,
|
||||
icon: iconMap[item.orderInx] || '',
|
||||
unit: item.unit || '个'
|
||||
}));
|
||||
})
|
||||
.catch(() => {
|
||||
facilities.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 点击处理函数
|
||||
const handleFacilityClick = (facility: IFacility) => {
|
||||
currentFacility.value = facility;
|
||||
modalVisible.value = true;
|
||||
|
||||
};
|
||||
|
||||
// 关闭弹框
|
||||
const handleCloseModal = () => {
|
||||
modalVisible.value = false;
|
||||
currentFacility.value = null;
|
||||
};
|
||||
|
||||
// 监听基地变化
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
fetchData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.facility-grid {
|
||||
// width: 406px;
|
||||
flex-flow: wrap;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: -8px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||
Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
|
||||
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
}
|
||||
|
||||
.facility-card {
|
||||
@ -115,14 +203,8 @@ onMounted(() => {
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
// margin-right: 8px;
|
||||
background: #2f6b98;
|
||||
border-radius: 50%;
|
||||
|
||||
.anticon {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.facility-info {
|
||||
@ -132,13 +214,10 @@ onMounted(() => {
|
||||
.facility-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
// margin-bottom: 4px;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
.facility-count {
|
||||
font-size: 18px;
|
||||
color: #2f6b98;
|
||||
// font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@ -2,8 +2,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem title="基本情况介绍" :shrink="true">
|
||||
<a-spin :spinning="loading">
|
||||
<p v-if="title_text" class="basic_body1">{{ title_text }}</p>
|
||||
<div v-else class="zanwushujv"><a-empty /></div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
</div>
|
||||
</template>
|
||||
@ -20,6 +22,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
defineOptions({
|
||||
name: "jidiInfoMod",
|
||||
});
|
||||
const loading = ref(false);
|
||||
const initText = () => {
|
||||
const params = {
|
||||
filter: {
|
||||
@ -35,9 +38,12 @@ const initText = () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
loading.value = true;
|
||||
getBaseWbsb(params).then((res) => {
|
||||
console.log(res);
|
||||
title_text.value = res.data.data[0].introduce;
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const title_text = ref("");
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<SidePanelItem title="AI识别过鱼设施运行" :datetimePicker="datetimePicker">
|
||||
<SidePanelItem title="AI识别过鱼设施运行" :datetimePicker="datetimePicker" @update-values="handlePanelChange">
|
||||
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||
<div class="facility-grid">
|
||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
||||
<div v-for="facility in facilities" :key="facility.key" class="facility-card">
|
||||
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
||||
<div class="facility-icon">
|
||||
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
|
||||
@ -17,68 +18,141 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { getAiGyssList } from '@/api/zngj';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'zengzhizhanjiansheyunxing'
|
||||
name: 'spdzwaijc'
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
const panelDetail = ref<any>({});
|
||||
|
||||
const lyName = computed(() => baseid.value === '02' ? '升鱼斗运行' : '升鱼机运行');
|
||||
|
||||
// ==================== 日期选择器配置 ====================
|
||||
const datetimePicker = ref({
|
||||
show: true,
|
||||
value: `${new Date().getFullYear() - 1}`, // 上一年
|
||||
value: dayjs().format('YYYY-MM'),
|
||||
format: 'YYYY-MM',
|
||||
picker: 'month' as const,
|
||||
options: []
|
||||
});
|
||||
// 设施数据
|
||||
const facilities = ref([
|
||||
|
||||
// ==================== 设施数据 ====================
|
||||
const facilities = computed(() => [
|
||||
{
|
||||
key: 'gys',
|
||||
name: '赶鱼栅运行',
|
||||
count: "32",
|
||||
count: panelDetail.value?.fpGysCnt ?? 0,
|
||||
icon: 'icon iconfont icon-ganyushan'
|
||||
},
|
||||
{
|
||||
name: '升鱼斗运行',
|
||||
count: '66',
|
||||
key: 'syj',
|
||||
name: lyName.value,
|
||||
count: panelDetail.value?.fpSyjCnt ?? 0,
|
||||
icon: 'icon iconfont icon-liuliangjiancezhan1'
|
||||
},
|
||||
{
|
||||
key: 'agv',
|
||||
name: 'AGV小车',
|
||||
count: '0',
|
||||
count: panelDetail.value?.fpAgvCnt ?? 0,
|
||||
icon: 'icon iconfont icon-agvxiaoche'
|
||||
},
|
||||
{
|
||||
key: 'yych',
|
||||
name: '运鱼车运行',
|
||||
count: '0',
|
||||
count: panelDetail.value?.fpYychCnt ?? 0,
|
||||
icon: 'icon iconfont icon-yunyuche'
|
||||
},
|
||||
{
|
||||
key: 'yyc',
|
||||
name: '运鱼船出港',
|
||||
count: '32',
|
||||
count: panelDetail.value?.fpYycCnt ?? 0,
|
||||
icon: 'icon iconfont icon-yunyuchuan'
|
||||
},
|
||||
{
|
||||
key: 'ydh',
|
||||
name: '鱼道放流',
|
||||
count: '0',
|
||||
count: panelDetail.value?.fpYdhCnt ?? 0,
|
||||
icon: 'icon iconfont icon-yudaofangliu'
|
||||
}
|
||||
]);
|
||||
|
||||
// 页面加载时执行
|
||||
onMounted(() => {
|
||||
// 延迟初始化,确保容器已渲染
|
||||
// ==================== 事件处理 ====================
|
||||
const handlePanelChange = (data: any) => {
|
||||
console.log('当前所有控件状态:', data);
|
||||
if (data.datetime) {
|
||||
datetimePicker.value.value = data.datetime;
|
||||
}
|
||||
getCardData();
|
||||
};
|
||||
|
||||
});
|
||||
// ==================== 获取卡片统计数据 ====================
|
||||
const getCardData = async () => {
|
||||
if (!datetimePicker.value.value || !baseid.value) return;
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
dataLoading.value = true;
|
||||
|
||||
});
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
baseid.value !== 'all' ? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value
|
||||
} : null,
|
||||
{
|
||||
field: 'startTime',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: dayjs(datetimePicker.value.value).startOf('month').format('YYYY-MM-DD 00:00:00')
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: dayjs(datetimePicker.value.value).endOf('month').format('YYYY-MM-DD 23:59:59')
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
|
||||
const res = await getAiGyssList(params);
|
||||
panelDetail.value = res?.data ?? {};
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
panelDetail.value = {};
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 监听基地变化 ====================
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
async newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getCardData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -111,7 +185,6 @@ onUnmounted(() => {
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
// margin-right: 8px;
|
||||
background: rgb(47, 107, 152);
|
||||
border-radius: 50%;
|
||||
|
||||
@ -128,13 +201,14 @@ onUnmounted(() => {
|
||||
.facility-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
// margin-bottom: 4px;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
.facility-count {
|
||||
font-size: 18px;
|
||||
color: #2f6b98;
|
||||
// font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
min-height: 72px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div class="modal-content">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form layout="inline" class="search-form" @finish="handleSearch">
|
||||
<a-form-item label="所属基地">
|
||||
<a-select
|
||||
v-model:value="searchForm.baseId"
|
||||
placeholder="请选择基地"
|
||||
style="width: 180px"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="baseOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="监测数据状态">
|
||||
<a-select
|
||||
v-model:value="searchForm.warnState"
|
||||
placeholder="请选择状态"
|
||||
style="width: 150px"
|
||||
allow-clear
|
||||
:options="warnStateOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="测站名称">
|
||||
<a-input
|
||||
v-model:value="searchForm.stnm"
|
||||
placeholder="请输入测站名称"
|
||||
style="width: 180px"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
<!-- <a-button style="margin-left: 8px" @click="handleReset">重置</a-button> -->
|
||||
<a-tooltip title="导出(暂未实现)">
|
||||
<a-button style="margin-left: 8px">导出</a-button>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="table-wrapper">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scroll-y="420"
|
||||
:list-url="getStllGaojingDetail"
|
||||
:search-params="tableSearchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
v-if="record.warnState === 2"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="opentan(record)"
|
||||
>
|
||||
查看详情
|
||||
</a-button>
|
||||
<span v-else style="color: #999">查看详情</span>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive, watch,onMounted } from 'vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { getStllGaojingDetail } from '@/api/zngj';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
defineOptions({
|
||||
name: 'ModeGaoJing'
|
||||
});
|
||||
const modelStore = useModelStore();
|
||||
interface Props {
|
||||
baseId: string;
|
||||
warnState: string;
|
||||
warnType: string;
|
||||
sttpCode: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
baseId: '',
|
||||
warnState: '',
|
||||
warnType: 'QEC',
|
||||
sttpCode: 'ENG'
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const tableRef = ref();
|
||||
|
||||
// 基地选项
|
||||
const baseOptions = computed(() => {
|
||||
return JidiSelectEventStore.jidiData.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
});
|
||||
|
||||
// 监测数据状态选项
|
||||
const warnStateOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '正常', value: '1' },
|
||||
{ label: '告警', value: '2' },
|
||||
{ label: '无数据', value: '0' }
|
||||
];
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
baseId: props.baseId || '',
|
||||
warnState: props.warnState || '',
|
||||
stnm: ''
|
||||
});
|
||||
|
||||
// 监听 props 变化
|
||||
watch(
|
||||
() => [props.baseId, props.warnState],
|
||||
([newBaseId, newWarnState]) => {
|
||||
searchForm.baseId = newBaseId;
|
||||
searchForm.warnState = newWarnState;
|
||||
handleSearch();
|
||||
}
|
||||
);
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '电站名称',
|
||||
key: 'ennm',
|
||||
dataIndex: 'ennm',
|
||||
width: '150px',
|
||||
customRender: ({ text, record }: any) => {
|
||||
return text || record.stnm || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '测站名称',
|
||||
key: 'stnm',
|
||||
dataIndex: 'stnm',
|
||||
width: '150px'
|
||||
},
|
||||
{
|
||||
title: '所属基地',
|
||||
key: 'baseName',
|
||||
dataIndex: 'baseName',
|
||||
width: '150px'
|
||||
},
|
||||
{
|
||||
title: '监测数据状态',
|
||||
key: 'warnstateName',
|
||||
dataIndex: 'warnstateName',
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: '100px',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
];
|
||||
|
||||
// 构建搜索参数
|
||||
const tableSearchParams = computed(() => {
|
||||
return {
|
||||
sort: [
|
||||
{ field: 'baseStepSort', dir: 'asc' },
|
||||
{ field: 'rvcdStepSort', dir: 'asc' },
|
||||
{ field: 'rstcdStepSort', dir: 'asc' },
|
||||
{ field: 'siteStepSort', dir: 'asc' },
|
||||
{ field: 'stnm', dir: 'asc' }
|
||||
],
|
||||
group: [],
|
||||
select: [],
|
||||
groupResultFlat: false
|
||||
};
|
||||
});
|
||||
|
||||
// 数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
// Select 搜索过滤方法
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
const filters: any[] = [];
|
||||
|
||||
// 基地筛选
|
||||
if (searchForm.baseId && searchForm.baseId !== 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchForm.baseId
|
||||
});
|
||||
}
|
||||
|
||||
// 测站类型
|
||||
filters.push({
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: props.sttpCode
|
||||
});
|
||||
|
||||
// 告警状态
|
||||
if (searchForm.warnState !== undefined && searchForm.warnState !== '') {
|
||||
filters.push({
|
||||
field: 'warnState',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchForm.warnState
|
||||
});
|
||||
}
|
||||
|
||||
// 告警类型
|
||||
filters.push({
|
||||
field: 'warnType',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: props.warnType
|
||||
});
|
||||
|
||||
// 测站名称
|
||||
if (searchForm.stnm) {
|
||||
filters.push({
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: searchForm.stnm
|
||||
});
|
||||
}
|
||||
|
||||
const filter = {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
};
|
||||
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchForm.baseId = '';
|
||||
searchForm.warnState = '';
|
||||
searchForm.stnm = '';
|
||||
handleSearch();
|
||||
};
|
||||
const opentan = (record: any) => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.rstcd;
|
||||
};
|
||||
// 初始化加载数据
|
||||
// handleSearch();
|
||||
onMounted(() => {
|
||||
handleSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// height: 100%;
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -2,8 +2,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem title="生态流量低于限值告警情况">
|
||||
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||
<div class="body_box">
|
||||
<div class="box" v-for="(item, index) in data" :key="index">
|
||||
<div class="box" v-for="(item, index) in data" :key="index" @click="handleBoxClick(item.warnState)" style="cursor: pointer;">
|
||||
<div>
|
||||
<div :class="item.icon" :style="{background:item.background}"></div>
|
||||
<div class="title_text">{{ item.name }}</div>
|
||||
@ -11,40 +12,163 @@
|
||||
<div><span class="text_num">{{ item.num }}</span><span>座</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="isModalOpen"
|
||||
title="生态流量低于限值告警统计"
|
||||
width="90%"
|
||||
:body-style="{ height: '600px', padding: '16px' }"
|
||||
:footer="null"
|
||||
@cancel="handleModalClose"
|
||||
>
|
||||
<ModeGaoJing
|
||||
:base-id="baseid"
|
||||
:warn-state="currentWarnState"
|
||||
warn-type="QEC"
|
||||
sttp-code="ENG"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
||||
import { getStllGaojingList } from '@/api/zngj';
|
||||
import ModeGaoJing from './ModeGaoJing.vue';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: "jidiInfoMod",
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
const isModalOpen = ref(false);
|
||||
const currentWarnState = ref('');
|
||||
|
||||
const data = ref([
|
||||
{
|
||||
name:'正常',
|
||||
num:'447',
|
||||
num:'0',
|
||||
icon:'iconfont icon-shuidianzhan1 zhuangji',
|
||||
background:'#78c300',
|
||||
warnState:'1',
|
||||
},
|
||||
{
|
||||
name:'告警',
|
||||
num:'0',
|
||||
icon:'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
|
||||
background:'#e55555',
|
||||
warnState:'2',
|
||||
},
|
||||
{
|
||||
name:'无数据',
|
||||
num:'6',
|
||||
num:'0',
|
||||
icon:'iconfont icon-shuidianzhanWushujv shebeiLixian',
|
||||
background:'#8d8d8d',
|
||||
warnState:'0',
|
||||
},
|
||||
])
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => { });
|
||||
|
||||
// 点击卡片
|
||||
const handleBoxClick = (warnState: string) => {
|
||||
currentWarnState.value = warnState;
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleModalClose = () => {
|
||||
isModalOpen.value = false;
|
||||
};
|
||||
|
||||
// ==================== 获取卡片统计数据 ====================
|
||||
const getCardData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
dataLoading.value = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
baseid.value !== 'all' ? {
|
||||
field: 'base_Id',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value
|
||||
} : null
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
|
||||
const res = await getStllGaojingList(params);
|
||||
const list = res?.data?.data || [];
|
||||
|
||||
// 筛选 warnType === 'QEC' 的数据
|
||||
const qecList = list.filter((item: any) => item.warnType === 'QEC');
|
||||
|
||||
// 按 warnState 分类统计
|
||||
const result = {
|
||||
normal: 0, // warnState = 1
|
||||
abnormal: 0, // warnState = 2
|
||||
noData: 0 // warnState = 0
|
||||
};
|
||||
|
||||
qecList.forEach((item: any) => {
|
||||
if (item.warnState === 0) result.noData = item.num;
|
||||
else if (item.warnState === 1) result.normal = item.num;
|
||||
else if (item.warnState === 2) result.abnormal = item.num;
|
||||
});
|
||||
|
||||
// 更新卡片数据
|
||||
data.value = [
|
||||
{
|
||||
name: '正常',
|
||||
num: result.normal.toString(),
|
||||
icon: 'iconfont icon-shuidianzhan1 zhuangji',
|
||||
background: '#78c300',
|
||||
warnState: '1'
|
||||
},
|
||||
{
|
||||
name: '告警',
|
||||
num: result.abnormal.toString(),
|
||||
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
|
||||
background: '#e55555',
|
||||
warnState: '2'
|
||||
},
|
||||
{
|
||||
name: '无数据',
|
||||
num: result.noData.toString(),
|
||||
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
|
||||
background: '#8d8d8d',
|
||||
warnState: '0'
|
||||
}
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('获取生态流量告警数据失败:', error);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 监听基地变化 ====================
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
async newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getCardData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div class="modal-content">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form layout="inline" class="search-form" @finish="handleSearch">
|
||||
<a-form-item label="所属基地">
|
||||
<a-select
|
||||
v-model:value="searchForm.baseId"
|
||||
placeholder="请选择基地"
|
||||
style="width: 180px"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="baseOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="监测数据状态">
|
||||
<a-select
|
||||
v-model:value="searchForm.warnState"
|
||||
placeholder="请选择状态"
|
||||
style="width: 150px"
|
||||
allow-clear
|
||||
:options="warnStateOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="测站名称">
|
||||
<a-input
|
||||
v-model:value="searchForm.stnm"
|
||||
placeholder="请输入测站名称"
|
||||
style="width: 180px"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="table-wrapper">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scroll-y="420"
|
||||
:list-url="getStllGaojingDetail"
|
||||
:search-params="tableSearchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
v-if="record.warnState === 2"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="opentan(record)"
|
||||
>
|
||||
查看详情
|
||||
</a-button>
|
||||
<span v-else style="color: #999">查看详情</span>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive, watch, onMounted } from 'vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { getStllGaojingDetail } from '@/api/zngj';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
|
||||
defineOptions({
|
||||
name: 'ModeGaoJing'
|
||||
});
|
||||
const modelStore = useModelStore();
|
||||
interface Props {
|
||||
baseId: string;
|
||||
warnState: string;
|
||||
dtinType: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
baseId: '',
|
||||
warnState: '',
|
||||
dtinType: '0'
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const tableRef = ref();
|
||||
|
||||
// 基地选项
|
||||
const baseOptions = computed(() => {
|
||||
return JidiSelectEventStore.jidiData.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
});
|
||||
|
||||
// 监测数据状态选项
|
||||
const warnStateOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '正常', value: '1' },
|
||||
{ label: '告警', value: '2' },
|
||||
{ label: '无数据', value: '0' }
|
||||
];
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
baseId: props.baseId || '',
|
||||
warnState: props.warnState || '',
|
||||
stnm: ''
|
||||
});
|
||||
|
||||
// 监听 props 变化
|
||||
watch(
|
||||
() => [props.baseId, props.warnState],
|
||||
([newBaseId, newWarnState]) => {
|
||||
searchForm.baseId = newBaseId;
|
||||
searchForm.warnState = newWarnState;
|
||||
handleSearch();
|
||||
}
|
||||
);
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '电站名称',
|
||||
key: 'ennm',
|
||||
dataIndex: 'ennm',
|
||||
width: '150px',
|
||||
customRender: ({ text, record }: any) => {
|
||||
return text || record.stnm || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '测站名称',
|
||||
key: 'stnm',
|
||||
dataIndex: 'stnm',
|
||||
width: '150px'
|
||||
},
|
||||
{
|
||||
title: '所属基地',
|
||||
key: 'baseName',
|
||||
dataIndex: 'baseName',
|
||||
width: '150px'
|
||||
},
|
||||
{
|
||||
title: '监测数据状态',
|
||||
key: 'warnstateName',
|
||||
dataIndex: 'warnstateName',
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: '100px',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
];
|
||||
|
||||
// 构建搜索参数
|
||||
const tableSearchParams = computed(() => {
|
||||
return {
|
||||
sort: [
|
||||
{ field: 'baseStepSort', dir: 'asc' },
|
||||
{ field: 'rvcdStepSort', dir: 'asc' },
|
||||
{ field: 'rstcdStepSort', dir: 'asc' },
|
||||
{ field: 'siteStepSort', dir: 'asc' },
|
||||
{ field: 'stnm', dir: 'asc' }
|
||||
],
|
||||
group: [],
|
||||
select: [],
|
||||
groupResultFlat: false
|
||||
};
|
||||
});
|
||||
|
||||
// 数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
// Select 搜索过滤方法
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
const filters: any[] = [];
|
||||
|
||||
// 基地筛选
|
||||
if (searchForm.baseId && searchForm.baseId !== 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchForm.baseId
|
||||
});
|
||||
}
|
||||
|
||||
// 测站类型(固定为 WQ)
|
||||
filters.push({
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['WQ']
|
||||
});
|
||||
|
||||
// 告警状态
|
||||
if (searchForm.warnState !== undefined && searchForm.warnState !== '') {
|
||||
filters.push({
|
||||
field: 'warnState',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchForm.warnState
|
||||
});
|
||||
}
|
||||
|
||||
// dtinType
|
||||
if (props.dtinType === '') {
|
||||
filters.push({
|
||||
field: 'dtinType',
|
||||
operator: 'in',
|
||||
dataType: 'number',
|
||||
value: [0, 1]
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
field: 'dtinType',
|
||||
operator: 'eq',
|
||||
dataType: 'number',
|
||||
value: Number(props.dtinType)
|
||||
});
|
||||
}
|
||||
|
||||
// mway 固定为 2
|
||||
filters.push({
|
||||
field: 'mway',
|
||||
operator: 'eq',
|
||||
dataType: 'number',
|
||||
value: 2
|
||||
});
|
||||
|
||||
// 测站名称
|
||||
if (searchForm.stnm) {
|
||||
filters.push({
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: searchForm.stnm
|
||||
});
|
||||
}
|
||||
|
||||
const filter = {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
};
|
||||
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
const opentan = (record: any) => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.rstcd;
|
||||
};
|
||||
onMounted(() => {
|
||||
handleSearch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,9 +1,10 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem title="水质监测数据告警情况" :select="select" >
|
||||
<SidePanelItem title="水质监测数据告警情况" :select="select" @update-values="handlePanelChange">
|
||||
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||
<div class="body_box">
|
||||
<div class="box" v-for="(item, index) in data" :key="index">
|
||||
<div class="box" v-for="(item, index) in data" :key="index" @click="handleCardClick(item.warnState)" style="cursor: pointer;">
|
||||
<div>
|
||||
<div :class="item.icon" :style="{background:item.background}"></div>
|
||||
<div class="title_text">{{ item.name }}</div>
|
||||
@ -11,47 +12,225 @@
|
||||
<div><span class="text_num">{{ item.num }}</span><span>座</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="水质监测告警统计"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
@cancel="handleModalClose"
|
||||
>
|
||||
<ModeGaoJing
|
||||
v-if="modalVisible"
|
||||
:baseId="baseid"
|
||||
:warnState="currentWarnState"
|
||||
:dtinType="defaultValue"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
||||
import { getShuizhiGaojingList } from '@/api/zngj';
|
||||
import ModeGaoJing from './ModeGaoJing.vue';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: "jidiInfoMod",
|
||||
name: "shuizhijianceshuju",
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
const defaultValue = ref<string>('0');
|
||||
|
||||
// 弹框状态
|
||||
const modalVisible = ref(false);
|
||||
const currentWarnState = ref('');
|
||||
|
||||
const handleCardClick = (warnState: string) => {
|
||||
currentWarnState.value = warnState;
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
modalVisible.value = false;
|
||||
};
|
||||
|
||||
const select = ref({
|
||||
show: true,
|
||||
value: undefined,
|
||||
options: [],
|
||||
picker: undefined,
|
||||
format: undefined
|
||||
value: '0',
|
||||
options: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '国家站', value: '1' },
|
||||
{ label: '自建站', value: '0' },
|
||||
],
|
||||
width: '140px',
|
||||
minWidth: '140px',
|
||||
});
|
||||
|
||||
const data = ref([
|
||||
{
|
||||
name: '正常',
|
||||
num:'56',
|
||||
num: '0',
|
||||
warnState: '1',
|
||||
icon: 'iconfont icon-shuidianzhan1 zhuangji',
|
||||
background: '#78c300',
|
||||
},
|
||||
{
|
||||
name: '告警',
|
||||
num: '0',
|
||||
warnState: '2',
|
||||
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
|
||||
background: '#e55555',
|
||||
},
|
||||
{
|
||||
name: '无数据',
|
||||
num: '0',
|
||||
warnState: '0',
|
||||
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
|
||||
background: '#8d8d8d',
|
||||
},
|
||||
])
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => { });
|
||||
]);
|
||||
|
||||
const handlePanelChange = (payload: any) => {
|
||||
if (payload.select !== undefined) {
|
||||
defaultValue.value = payload.select;
|
||||
select.value.value = payload.select;
|
||||
}
|
||||
getCardData();
|
||||
};
|
||||
|
||||
const getCardData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
dataLoading.value = true;
|
||||
|
||||
try {
|
||||
const filters: any[] = [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['WQ'],
|
||||
},
|
||||
{
|
||||
field: 'mway',
|
||||
operator: 'eq',
|
||||
dataType: 'number',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
field: 'usfl',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1',
|
||||
},
|
||||
baseid.value !='all'? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value,
|
||||
}: null,
|
||||
].filter(Boolean);
|
||||
|
||||
if (defaultValue.value === '') {
|
||||
filters.push({
|
||||
field: 'dtinType',
|
||||
operator: 'in',
|
||||
dataType: 'number',
|
||||
value: [0, 1],
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
field: 'dtinType',
|
||||
operator: 'eq',
|
||||
dataType: 'number',
|
||||
value: Number(defaultValue.value),
|
||||
});
|
||||
}
|
||||
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters,
|
||||
},
|
||||
group: [
|
||||
{
|
||||
dir: 'desc',
|
||||
field: 'warnState',
|
||||
aggregates: [
|
||||
{
|
||||
field: 'stcd',
|
||||
aggregate: 'count',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
select: [],
|
||||
};
|
||||
|
||||
const res = await getShuizhiGaojingList(params);
|
||||
const list = res?.data?.data || [];
|
||||
// debugger
|
||||
const result = {
|
||||
normal: 0,
|
||||
abnormal: 0,
|
||||
noData: 0,
|
||||
};
|
||||
|
||||
list.forEach((item: any) => {
|
||||
const warnState = item.items?.[0]?.WARNSTATE;
|
||||
const count = item.items?.[0]?.COUNT_STCD ?? 0;
|
||||
if (warnState == 0) result.noData = count;
|
||||
else if (warnState == 1) result.normal = count;
|
||||
else if (warnState == 2) result.abnormal = count;
|
||||
});
|
||||
|
||||
data.value = [
|
||||
{
|
||||
name: '正常',
|
||||
num: result.normal.toString(),
|
||||
warnState: '1',
|
||||
icon: 'iconfont icon-shuidianzhan1 zhuangji',
|
||||
background: '#78c300',
|
||||
},
|
||||
{
|
||||
name: '告警',
|
||||
num: result.abnormal.toString(),
|
||||
warnState: '2',
|
||||
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
|
||||
background: '#e55555',
|
||||
},
|
||||
{
|
||||
name: '无数据',
|
||||
num: result.noData.toString(),
|
||||
warnState: '0',
|
||||
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
|
||||
background: '#8d8d8d',
|
||||
},
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('获取水质监测告警数据失败:', error);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
async newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getCardData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="rstcdOptions"
|
||||
allowClear
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
@ -29,9 +30,10 @@
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-spin :spinning="bodyLoading">
|
||||
<template v-if="tabList.length > 0">
|
||||
<!-- Tabs 标签页 -->
|
||||
<a-tabs
|
||||
v-if="tabList.length > 0"
|
||||
v-model:activeKey="ecoFlowType"
|
||||
class="tab-style"
|
||||
@change="fetchTableData"
|
||||
@ -77,6 +79,12 @@
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-empty
|
||||
v-else-if="!bodyLoading"
|
||||
description="暂无数据"
|
||||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -294,8 +302,6 @@ const searchParams = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
;
|
||||
|
||||
const params = ref({
|
||||
qid: props.basicId || 'all',
|
||||
rstcd: ''
|
||||
@ -309,6 +315,7 @@ const activeETime = ref('');
|
||||
const tabList = ref<any[]>([]);
|
||||
const ecoFlowType = ref<string>(props.sttpCodekey || '');
|
||||
const loading = ref<boolean>(false);
|
||||
const bodyLoading = ref<boolean>(false);
|
||||
|
||||
const jidiOptions = computed(() => {
|
||||
return [
|
||||
@ -589,6 +596,7 @@ const customTransform = (res: any) => {
|
||||
// 获取 Tab 列表数据
|
||||
const fetchTabList = async () => {
|
||||
loading.value = true;
|
||||
bodyLoading.value = true;
|
||||
try {
|
||||
const { qid, rstcd } = params.value;
|
||||
let obj = {
|
||||
@ -644,6 +652,8 @@ const fetchTabList = async () => {
|
||||
|
||||
// ecoFlowType.value = tabList.value[0].key;
|
||||
|
||||
// 等待 DOM 渲染完成(图表和表格组件需要挂载后才能操作)
|
||||
await nextTick();
|
||||
// 获取饼图数据
|
||||
fetchPieData();
|
||||
// 获取表格数据
|
||||
@ -656,6 +666,7 @@ const fetchTabList = async () => {
|
||||
tabList.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
bodyLoading.value = false;
|
||||
}
|
||||
};
|
||||
const handleView = (record: any, type: 'ENG' | 'EQ') => {
|
||||
@ -731,6 +742,17 @@ watch(ecoFlowType, () => {
|
||||
fetchPieData();
|
||||
});
|
||||
|
||||
// 监听 tabList 变化,当变为空时清理图表实例
|
||||
watch(
|
||||
() => tabList.value.length,
|
||||
(newLen) => {
|
||||
if (newLen === 0 && chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleSearch = () => {
|
||||
// 重新获取 Tab 列表
|
||||
fetchTabList();
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scrollY="480"
|
||||
:list-url="vmsstbprptGetKendoList"
|
||||
:list-url="vmsstbprptGetKendoList1"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
@ -59,7 +59,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick, h } from 'vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { vmsstbprptGetKendoList } from '@/api/stll';
|
||||
import { vmsstbprptGetKendoList1 } from '@/api/stll';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
|
||||
@ -275,7 +275,7 @@ const handleChange = async () => {
|
||||
],
|
||||
select: ['stcd', 'stnm']
|
||||
};
|
||||
let res = await vmsstbprptGetKendoList(params);
|
||||
let res = await vmsstbprptGetKendoList1(params);
|
||||
if (res?.data?.data) {
|
||||
let data = res.data.data;
|
||||
stcdOptions.value = data.map((item: any) => ({
|
||||
|
||||
@ -111,6 +111,7 @@ const props = defineProps<{
|
||||
typeKey?: string;
|
||||
time?: string;
|
||||
basicId?: string;
|
||||
dateArr?: any[];
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
@ -193,8 +194,11 @@ const searchParams = computed(() => {
|
||||
sort: baseSort
|
||||
};
|
||||
});
|
||||
// 初始化日期范围:上个月的本日到今天
|
||||
// 初始化日期范围:优先使用父组件传入的 dateArr,否则使用默认值(上个月的本日到今天)
|
||||
const initDateRange = () => {
|
||||
if (props.dateArr && props.dateArr.length === 2) {
|
||||
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
|
||||
}
|
||||
const today = dayjs();
|
||||
const lastMonthSameDay = today.subtract(1, 'month');
|
||||
return [lastMonthSameDay, today];
|
||||
|
||||
@ -77,6 +77,7 @@
|
||||
:typeKey="selectedItem.key"
|
||||
:time="mode"
|
||||
:basicId="baseid"
|
||||
:dateArr="dateArr"
|
||||
/>
|
||||
</a-modal>
|
||||
|
||||
@ -100,6 +101,7 @@ import {
|
||||
evnmAutoMonitorGetKendoListCust
|
||||
} from '@/api/stll';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
import STLLXFFS from './TwoLayer/ShengTaiLiuLiangDaBQKTwoLayer.vue';
|
||||
import ModalYkzhbzdjcgz from './TwoLayer/ModalYkzhbzdjcgz.vue';
|
||||
import HuanbaoZDJCGZKZ from '@/modules/huanbaozdjcgzkzQK/index.vue';
|
||||
@ -108,7 +110,9 @@ defineOptions({
|
||||
name: 'shengtaidabiaoMod'
|
||||
});
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const mapViewStore = useMapViewStore();
|
||||
const baseid = ref('');
|
||||
const dateArr = ref<any[]>([]);
|
||||
const obj = {
|
||||
show: true,
|
||||
value:
|
||||
@ -119,15 +123,15 @@ const mode = ref('hour');
|
||||
const dataArr: any = ref([
|
||||
{
|
||||
name: '生态环境部门要求',
|
||||
qecCnt: '41', // 座数
|
||||
qecTCnt: '55', //总座数
|
||||
qecCnt: '0', // 座数
|
||||
qecTCnt: '0', //总座数
|
||||
type: 1,
|
||||
key: 'qecInterval'
|
||||
},
|
||||
{
|
||||
name: '水利部门要求',
|
||||
qecCnt: '41', // 座数
|
||||
qecTCnt: '55', //总座数
|
||||
qecCnt: '0', // 座数
|
||||
qecTCnt: '0', //总座数
|
||||
type: 2,
|
||||
key: 'mwrInterval'
|
||||
}
|
||||
@ -181,13 +185,7 @@ const setStyle = () => {
|
||||
const dataChage = () => {
|
||||
getData();
|
||||
};
|
||||
const getData = async () => {
|
||||
// 计算时间范围:当前时间前一个月的00:00:00 到 当前时间的23:59:59
|
||||
const now = new Date();
|
||||
const oneMonthAgo = new Date(now);
|
||||
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
||||
|
||||
// 格式化日期为 YYYY-MM-DD
|
||||
const getdate = (offsetYears = 0) => {
|
||||
const formatDate = (date: Date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
@ -195,8 +193,35 @@ const getData = async () => {
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
const startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
|
||||
const endDate = `${formatDate(now)} 23:59:59`;
|
||||
let startDate: string;
|
||||
let endDate: string;
|
||||
|
||||
if (dateArr.value && dateArr.value.length === 2) {
|
||||
const start = new Date(dateArr.value[0]);
|
||||
const end = new Date(dateArr.value[1]);
|
||||
if (offsetYears !== 0) {
|
||||
start.setFullYear(start.getFullYear() + offsetYears);
|
||||
end.setFullYear(end.getFullYear() + offsetYears);
|
||||
}
|
||||
startDate = `${formatDate(start)} 00:00:00`;
|
||||
endDate = `${formatDate(end)} 23:59:59`;
|
||||
} else {
|
||||
const now = new Date();
|
||||
const oneMonthAgo = new Date(now);
|
||||
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
||||
if (offsetYears !== 0) {
|
||||
now.setFullYear(now.getFullYear() + offsetYears);
|
||||
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
|
||||
}
|
||||
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
|
||||
endDate = `${formatDate(now)} 23:59:59`;
|
||||
}
|
||||
|
||||
return { startDate, endDate };
|
||||
};
|
||||
|
||||
const getData = async () => {
|
||||
const { startDate, endDate } = getdate(0);
|
||||
|
||||
let params = {
|
||||
filter: {
|
||||
@ -279,6 +304,17 @@ watch(
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => mapViewStore.searchTimeRange,
|
||||
(newVal) => {
|
||||
|
||||
dateArr.value = newVal;
|
||||
getData();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
//统计电站范围:接入过生态流量数据的电站
|
||||
const titleData:any = ref({ cnt: 0 })
|
||||
const getcont = async () => {
|
||||
|
||||
@ -0,0 +1,386 @@
|
||||
<template>
|
||||
<div class="stlldbqk_container">
|
||||
<div class="stlldbqk_title">
|
||||
<div>选择水电站:</div>
|
||||
<a-select
|
||||
v-model:value="from.baseId"
|
||||
show-search
|
||||
placeholder=" "
|
||||
:options="jidiOptions"
|
||||
:filter-option="filterOption"
|
||||
@change="handleChange"
|
||||
style="width: 200px; margin: 0px 10px"
|
||||
></a-select>
|
||||
<a-select
|
||||
v-model:value="from.stcd"
|
||||
show-search
|
||||
placeholder="请选择电站"
|
||||
:options="stcdOptions"
|
||||
:filter-option="filterOption"
|
||||
style="width: 200px"
|
||||
></a-select>
|
||||
<a-button type="primary" style="margin: 0px 10px" @click="Search()"
|
||||
>搜索</a-button
|
||||
>
|
||||
<a-button @click="resetSearch()">重置</a-button>
|
||||
</div>
|
||||
<div class="table">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scrollY="480"
|
||||
:list-url="vmsstbprptGetKendoList1"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #dvtp="{ record }">
|
||||
{{
|
||||
record.dvtp == 2 ? '引水式' : record.dvtp == 3 ? '混合式' : '坝后式'
|
||||
}}
|
||||
</template>
|
||||
<template #stindx="{ record }">
|
||||
{{ record.dvtp == 2 ? '生态流量' : '出库流量' }}
|
||||
</template>
|
||||
<template #coenvwState="{ column, record }">
|
||||
<a-tag :color="record.coenvwState > 0 ? 'success' : ''">
|
||||
{{ record.coenvwState > 0 ? '在线' : '离线' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<a-button type="link" size="small" @click="handleView(record)"
|
||||
>查看详情</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick, h } from 'vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { vmsstbprptGetKendoList1 } from '@/api/stll';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
|
||||
const modelStore = useModelStore();
|
||||
const props = defineProps<{
|
||||
baseId?: any;
|
||||
titleData?: any;
|
||||
}>();
|
||||
const tableRef = ref();
|
||||
const columns = ref([
|
||||
{
|
||||
title: '所属基地',
|
||||
key: 'baseName',
|
||||
dataIndex: 'baseName',
|
||||
merge: true, // 启用行合并
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
title: '电站名称',
|
||||
key: 'stnm',
|
||||
dataIndex: 'stnm',
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
title: '开发方式',
|
||||
key: 'dvtp',
|
||||
dataIndex: 'dvtp',
|
||||
visible: true,
|
||||
slots: { customRender: 'dvtp' }
|
||||
},
|
||||
{
|
||||
title: '生态流量站类型',
|
||||
key: 'stindx',
|
||||
dataIndex: 'stindx',
|
||||
visible: true,
|
||||
slots: { customRender: 'stindx' }
|
||||
},
|
||||
{
|
||||
title: '建成时间',
|
||||
key: 'jcdt',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
sort: true,
|
||||
customRender: ({ record }: any) => {
|
||||
const date = record.jcdt;
|
||||
if (!date) return '-';
|
||||
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
||||
return date;
|
||||
}
|
||||
if (typeof date === 'string' && date.includes(' ')) {
|
||||
return date.split(' ')[0];
|
||||
}
|
||||
return date;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '监测状态',
|
||||
key: 'coenvwState',
|
||||
dataIndex: 'coenvwState',
|
||||
visible: true,
|
||||
slots: { customRender: 'coenvwState' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 120,
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]);
|
||||
const sort = {
|
||||
sort: [
|
||||
{
|
||||
field: 'baseStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'hbrvcd',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rstcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
}
|
||||
],
|
||||
group: [],
|
||||
select: [
|
||||
'baseName',
|
||||
'stnm',
|
||||
'ennm',
|
||||
'jcdt',
|
||||
'stindx',
|
||||
'coenvwState',
|
||||
'sttpCode',
|
||||
'stcd',
|
||||
'rstcd',
|
||||
'stCode',
|
||||
'stName',
|
||||
'dvtp'
|
||||
],
|
||||
groupResultFlat: false
|
||||
};
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
// JidiSelectEventStore.jidiData
|
||||
let jidiOptions = computed(() => {
|
||||
return [
|
||||
...JidiSelectEventStore.jidiData.map((item: any) => ({
|
||||
value: item.wbsCode,
|
||||
label: item.wbsName
|
||||
}))
|
||||
];
|
||||
});
|
||||
const from = ref({
|
||||
baseId: props.baseId || 'all',
|
||||
stcd: ''
|
||||
});
|
||||
// 动态计算搜索参数(包含排序)
|
||||
const searchParams = computed(() => {
|
||||
let baseSort;
|
||||
// 如果用户触发了排序,将其加入排序数组
|
||||
baseSort = [
|
||||
{ field: 'baseStepSort', dir: 'asc' },
|
||||
{ field: 'hbrvcd', dir: 'asc' },
|
||||
{ field: 'rstcdStepSort', dir: 'asc' },
|
||||
{ field: 'siteStepSort', dir: 'asc' }
|
||||
];
|
||||
|
||||
return {
|
||||
sort: baseSort,
|
||||
group: [],
|
||||
select: [
|
||||
'baseName',
|
||||
'stnm',
|
||||
'ennm',
|
||||
'jcdt',
|
||||
'stindx',
|
||||
'coenvwState',
|
||||
'sttpCode',
|
||||
'stcd',
|
||||
'rstcd',
|
||||
'stCode',
|
||||
'stName',
|
||||
'dvtp'
|
||||
],
|
||||
groupResultFlat: false
|
||||
};
|
||||
});
|
||||
|
||||
// 监听 props.baseId 变化,同步到 from.value.baseId
|
||||
watch(
|
||||
() => props.baseId,
|
||||
newBaseId => {
|
||||
// 只在有有效值且与当前值不同时才更新
|
||||
if (newBaseId && newBaseId !== from.value.baseId) {
|
||||
from.value.baseId = newBaseId;
|
||||
from.value.stcd = ''; // 重置电站选择
|
||||
// 异步等待 DOM 更新后再搜索
|
||||
nextTick(() => {
|
||||
handleChange(); // 先刷新电站列表
|
||||
Search(); // 再执行搜索
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: false }
|
||||
);
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
const stcdOptions = ref([]);
|
||||
const handleChange = async () => {
|
||||
try {
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
from.value.baseId != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: from.value.baseId
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'ENG'
|
||||
}
|
||||
].filter(Boolean)
|
||||
},
|
||||
sort: [
|
||||
{
|
||||
field: 'baseId',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rvcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rstcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
}
|
||||
],
|
||||
select: ['stcd', 'stnm']
|
||||
};
|
||||
let res = await vmsstbprptGetKendoList1(params);
|
||||
if (res?.data?.data) {
|
||||
let data = res.data.data;
|
||||
stcdOptions.value = data.map((item: any) => ({
|
||||
value: item.stcd,
|
||||
label: item.stnm
|
||||
}));
|
||||
} else {
|
||||
stcdOptions.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取电站列表失败:', error);
|
||||
stcdOptions.value = [];
|
||||
}
|
||||
};
|
||||
const Search = () => {
|
||||
let filters = [
|
||||
from.value.stcd
|
||||
? {
|
||||
field: 'stcd',
|
||||
operator: 'eq',
|
||||
value: from.value.stcd
|
||||
}
|
||||
: null,
|
||||
from.value.baseId != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
value: from.value.baseId
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'runState',
|
||||
operator: 'eq',
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: props.titleData?.sttpCode || 'ENG'
|
||||
},
|
||||
{
|
||||
field: 'DTIN',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
field: 'bldsttCcode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 2
|
||||
}
|
||||
].filter(Boolean);
|
||||
const filter = {
|
||||
logic: 'and',
|
||||
filters
|
||||
};
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const resetSearch = () => {
|
||||
from.value = {
|
||||
baseId: 'all',
|
||||
stcd: ''
|
||||
};
|
||||
Search();
|
||||
};
|
||||
// 自定义数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
const handleView = (record: any) => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENG';
|
||||
modelStore.title = record.stnm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.params.eqtp = 'QGC';
|
||||
modelStore.filter.stllgzlx = 'qecInterval';
|
||||
modelStore.filter.stllTmType = 'day';
|
||||
};
|
||||
onMounted(() => {
|
||||
handleChange();
|
||||
Search();
|
||||
});
|
||||
onUnmounted(() => {});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stlldbqk_container {
|
||||
width: 100%;
|
||||
.stlldbqk_title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
height: 560px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,939 @@
|
||||
<template>
|
||||
<div class="stlldbqk-container">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form layout="inline" class="search-form">
|
||||
<a-form-item label="基地选择">
|
||||
<a-select
|
||||
v-model:value="params.qid"
|
||||
placeholder="请选择基地"
|
||||
style="width: 200px"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="jidiOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="规则类型">
|
||||
<a-select
|
||||
v-model:value="params.key"
|
||||
placeholder="请选择规则类型"
|
||||
style="width: 180px"
|
||||
:options="ruleTypeOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="时间维度">
|
||||
<a-select
|
||||
v-model:value="params.timeType"
|
||||
placeholder="请选择时间维度"
|
||||
style="width: 120px"
|
||||
:options="timeTypeOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="日期范围">
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
:allow-clear="false"
|
||||
format="YYYY-MM-DD"
|
||||
style="width: 260px"
|
||||
:presets="DateSetting.RangeButton.days1"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- Tabs 标签页 -->
|
||||
<a-tabs
|
||||
v-if="tabList.length > 0"
|
||||
v-model:activeKey="ecoFlowType"
|
||||
class="tab-style"
|
||||
@change="fetchTableData"
|
||||
>
|
||||
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.label" />
|
||||
</a-tabs>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-if="!loading && tabList.length === 0" class="empty-state">
|
||||
<a-empty description="暂无数据" />
|
||||
</div>
|
||||
|
||||
<div v-show="tabList.length > 0" class="body_zhengti">
|
||||
<div class="echarts">
|
||||
<a-spin :spinning="dataLoading">
|
||||
<div v-if="pieData.length > 0" ref="chartRef" class="pie-chart"></div>
|
||||
<a-empty
|
||||
v-if="!dataLoading && pieData.length === 0"
|
||||
description="暂无数据"
|
||||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
<div class="table1">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scrollY="400"
|
||||
:list-url="
|
||||
activeTimeType == 'day' ? dayGetKendoListCust : hourGetKendoListCust
|
||||
"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #ennm="{ column, record }">
|
||||
<a-button type="link" size="small" @click="handleView(record)">{{
|
||||
record.ennm
|
||||
}}</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import dayjs from 'dayjs';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { dayGetKendoListCust, hourGetKendoListCust } from '@/api/stll';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
const modelStore = useModelStore();
|
||||
const props = defineProps<{
|
||||
options?: any[];
|
||||
typeKey?: string;
|
||||
time?: string;
|
||||
basicId?: string;
|
||||
dateArr?: any[];
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const columns = [
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '电站名称',
|
||||
dataIndex: 'ennm',
|
||||
slots: { customRender: 'action' }
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '所属基地',
|
||||
dataIndex: 'baseName'
|
||||
},
|
||||
{
|
||||
key: 'addvcdName',
|
||||
title: '行政区',
|
||||
dataIndex: 'addvcdName'
|
||||
},
|
||||
{
|
||||
key: 'hbrvcdName',
|
||||
title: '所在河段',
|
||||
dataIndex: 'hbrvcdName',
|
||||
width: '400px'
|
||||
},
|
||||
{
|
||||
key: 'qecRate',
|
||||
title: `生态流量达标率(%)`,
|
||||
dataIndex: 'qecRate',
|
||||
sort: true, // 启用排序
|
||||
width: '160px',
|
||||
customRender: ({ text }: any) => {
|
||||
if (text === null || text === undefined || text === '' || text === '-')
|
||||
return '-';
|
||||
return Number(text).toFixed(2);
|
||||
}
|
||||
}
|
||||
];
|
||||
const tableRef = ref();
|
||||
|
||||
const sort1 = ref([
|
||||
{
|
||||
field: 'baseStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rvcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'avqRate',
|
||||
dir: 'desc'
|
||||
}
|
||||
]);
|
||||
const sort2 = ref([
|
||||
{
|
||||
field: 'baseStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rvcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
}
|
||||
]);
|
||||
|
||||
// 动态计算搜索参数(包含排序)
|
||||
const searchParams = computed(() => {
|
||||
let baseSort;
|
||||
baseSort = params.value.key ? sort1.value : sort2.value;
|
||||
return {
|
||||
sort: baseSort
|
||||
};
|
||||
});
|
||||
// 初始化日期范围:优先使用父组件传入的 dateArr,否则使用默认值(上个月的本日到今天)
|
||||
const initDateRange = () => {
|
||||
if (props.dateArr && props.dateArr.length === 2) {
|
||||
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
|
||||
}
|
||||
const today = dayjs();
|
||||
const lastMonthSameDay = today.subtract(1, 'month');
|
||||
return [lastMonthSameDay, today];
|
||||
};
|
||||
|
||||
const dateRange = ref(initDateRange());
|
||||
|
||||
const params = ref({
|
||||
qid: props.basicId || 'all',
|
||||
key: props.typeKey || '',
|
||||
timeType: props.time || 'day',
|
||||
sTime: '',
|
||||
eTime: ''
|
||||
});
|
||||
|
||||
// 记录实际查询时使用的 timeType、key 和时间范围(用于 handleView 传递正确值)
|
||||
const activeTimeType = ref(props.time || 'day');
|
||||
const activeKey = ref(props.typeKey || '');
|
||||
const activeSTime = ref('');
|
||||
const activeETime = ref('');
|
||||
|
||||
// Tab 相关状态
|
||||
const tabList = ref<any[]>([]);
|
||||
const ecoFlowType = ref<string>('');
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const timeTypeOptions = [
|
||||
{ label: '逐时', value: 'hour' },
|
||||
{ label: '日', value: 'day' }
|
||||
];
|
||||
|
||||
const jidiOptions = computed(() => {
|
||||
return [
|
||||
...JidiSelectEventStore.jidiData.map((item: any) => ({
|
||||
value: item.wbsCode,
|
||||
label: item.wbsName
|
||||
}))
|
||||
];
|
||||
});
|
||||
|
||||
const ruleTypeOptions = computed(() => {
|
||||
return (
|
||||
props.options?.map((el: any) => ({
|
||||
label: el.name,
|
||||
value: el.key
|
||||
})) || []
|
||||
);
|
||||
});
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
// ==================== 图表相关 ====================
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const pieData = ref<any[]>([]); // 饼图数据源
|
||||
const dataLoading = ref<boolean>(false);
|
||||
const pieCode = ref<string[]>([]); // 用于表格过滤的选中项
|
||||
|
||||
// 颜色配置
|
||||
const themeecLists = [];
|
||||
|
||||
const jdColor = [
|
||||
'#4b79ab',
|
||||
'#78c300',
|
||||
'#9556a4',
|
||||
'#df91ab',
|
||||
'#7399c6',
|
||||
'#dbb629',
|
||||
'#56c2e3',
|
||||
'#f56a06',
|
||||
'#cdba75',
|
||||
'#76523b',
|
||||
'#df75b6',
|
||||
'#00a050',
|
||||
'#f5a644',
|
||||
'#4b79ab',
|
||||
'#78c300',
|
||||
'#9556a4',
|
||||
'#df91ab',
|
||||
'#7399c6',
|
||||
'#dbb629',
|
||||
'#56c2e3',
|
||||
'#f56a06',
|
||||
'#cdba75',
|
||||
'#76523b',
|
||||
'#df75b6',
|
||||
'#00a050',
|
||||
'#f5a644'
|
||||
];
|
||||
|
||||
// 获取饼图数据
|
||||
const fetchPieData = async () => {
|
||||
if (!ecoFlowType.value || !params.value.sTime || !params.value.eTime) return;
|
||||
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const { sTime, eTime, qid, key, timeType } = params.value;
|
||||
|
||||
// 构建过滤条件
|
||||
const filters = [
|
||||
qid != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: qid
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'dtin',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: sTime
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: eTime
|
||||
},
|
||||
key
|
||||
? {
|
||||
field: key,
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: ecoFlowType.value
|
||||
}
|
||||
: null
|
||||
].filter(Boolean);
|
||||
|
||||
// 当 key 为空时,添加额外的区间过滤条件
|
||||
if (!key) {
|
||||
filters.push(
|
||||
{
|
||||
field: 'qecInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'mwrInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'avqInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let obj: any = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
},
|
||||
group: [
|
||||
qid !== 'all' ? { dir: 'asc', field: 'rvcdStepSort' } : null,
|
||||
qid === 'all' ? { dir: 'asc', field: 'baseStepSort' } : null,
|
||||
{
|
||||
dir: 'asc',
|
||||
field: qid === 'all' ? 'baseId' : 'hbrvcd'
|
||||
},
|
||||
qid !== 'all'
|
||||
? { dir: 'asc', field: 'hbrvcdName' }
|
||||
: { dir: 'asc', field: 'baseName' }
|
||||
].filter(Boolean),
|
||||
groupResultFlat: true
|
||||
};
|
||||
|
||||
// TODO: 调用实际 API
|
||||
// const res = timeType == 'day'
|
||||
// ? await shengTaiLiuLiang.STLLTabsDay(obj)
|
||||
// : await shengTaiLiuLiang.STLLTabsHour(obj);
|
||||
|
||||
let res =
|
||||
timeType == 'day'
|
||||
? await dayGetKendoListCust(obj)
|
||||
: await hourGetKendoListCust(obj);
|
||||
|
||||
if (res?.data?.data) {
|
||||
const dataSource = res.data.data.map((el: any) => ({
|
||||
id: qid === 'all' ? el.baseId : el.hbrvcd,
|
||||
name: qid === 'all' ? el.baseName || el.key : el.hbrvcdName,
|
||||
value: qid === 'all' ? el.count_baseId : el.count_hbrvcd,
|
||||
key: qid === 'all' ? el.baseId : el.hbrvcd
|
||||
}));
|
||||
|
||||
pieData.value = dataSource;
|
||||
pieCode.value = []; // 重置选中状态
|
||||
|
||||
// 等待 DOM 更新后初始化或更新图表
|
||||
await nextTick();
|
||||
initOrUpdateChart();
|
||||
} else {
|
||||
pieData.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取饼图数据失败:', error);
|
||||
pieData.value = [];
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化或更新图表
|
||||
const initOrUpdateChart = () => {
|
||||
if (!chartRef.value) return;
|
||||
|
||||
// 如果实例不存在,初始化
|
||||
if (!chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 绑定图例选择事件
|
||||
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
|
||||
}
|
||||
|
||||
updateChart();
|
||||
};
|
||||
|
||||
// 更新图表配置
|
||||
const updateChart = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
const total =
|
||||
pieData.value.reduce((pre: number, cur: any) => pre + cur.value, 0) || 0;
|
||||
|
||||
// 生成颜色数组
|
||||
const colors = pieData.value.map((item: any, index: number) => {
|
||||
const matched = themeecLists.find((i: any) => i.name === item.name);
|
||||
return matched?.color || jdColor[index % jdColor.length];
|
||||
});
|
||||
|
||||
const option: EChartsOption = {
|
||||
color: colors,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} (座) ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 20,
|
||||
top: 80,
|
||||
type: 'scroll',
|
||||
textStyle: {
|
||||
width: 100, // 固定文本宽度
|
||||
overflow: 'truncate', // 超出部分截断
|
||||
ellipsis: '...' // 截断时显示的字符
|
||||
}
|
||||
},
|
||||
title: [
|
||||
{
|
||||
text: total.toString(),
|
||||
left: '34%',
|
||||
top: '44%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '电站总数量',
|
||||
left: '34%',
|
||||
top: '51%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '(座)',
|
||||
left: '34%',
|
||||
top: '55%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '电站总数量',
|
||||
type: 'pie',
|
||||
radius: ['31%', '40%'],
|
||||
center: ['35%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
const maxLen = 8; // 你希望显示的最大字符数
|
||||
let name = params.name;
|
||||
if (name.length > maxLen) {
|
||||
return name.substring(0, maxLen) + '...';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
data: pieData.value
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option, true);
|
||||
};
|
||||
|
||||
// 处理图例选择变化
|
||||
const handleLegendSelectChanged = (params: any) => {
|
||||
const selected = params.selected;
|
||||
const trueRivers = Object.keys(selected).filter(key => selected[key]);
|
||||
const allFalse = Object.values(selected).every(value => value === false);
|
||||
|
||||
const selectedKeys = pieData.value
|
||||
.filter((item: any) => trueRivers.includes(item.name))
|
||||
.map((item: any) => item.key);
|
||||
|
||||
pieCode.value = allFalse ? ['0'] : selectedKeys;
|
||||
|
||||
// 刷新表格
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 自定义数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
// 获取 Tab 列表数据
|
||||
const fetchTabList = async () => {
|
||||
if (!params.value.sTime || !params.value.eTime) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const { sTime, eTime, qid, key, timeType } = params.value;
|
||||
|
||||
// 构建过滤条件
|
||||
const filters = [
|
||||
qid != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: qid
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'dtin',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: sTime
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: eTime
|
||||
}
|
||||
].filter(Boolean);
|
||||
|
||||
// 当 key 为空时,添加额外的区间过滤条件
|
||||
if (!key) {
|
||||
filters.push(
|
||||
{
|
||||
field: 'qecInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'mwrInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'avqInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let obj: any = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: filters
|
||||
},
|
||||
group: key
|
||||
? [
|
||||
{
|
||||
dir: 'asc',
|
||||
field: key
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'baseStepSort'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'baseId'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'baseName'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 当 key 为空时,添加 groupResultFlat 属性
|
||||
if (!key) {
|
||||
obj.groupResultFlat = true;
|
||||
}
|
||||
|
||||
// TODO: 根据实际 API 调用替换此处逻辑
|
||||
let res =
|
||||
timeType == 'day'
|
||||
? await dayGetKendoListCust(obj)
|
||||
: await hourGetKendoListCust(obj);
|
||||
|
||||
// const res = timeType == 'day'
|
||||
// ? await shengTaiLiuLiang.STLLTabsDay(par, group) dayGetKendoListCust,hourGetKendoListCust
|
||||
// : await shengTaiLiuLiang.STLLTabsHour(par, group);
|
||||
|
||||
// 模拟数据结构(待替换为真实 API)
|
||||
// const res = {
|
||||
// data: [
|
||||
// {
|
||||
// type: 1,
|
||||
// qecCnt: 14,
|
||||
// qecTCnt: 50,
|
||||
// code: 'QEC_STLL_1_DY95',
|
||||
// desc: '环评要求_生态流量达标率所在统计区间:>=95%',
|
||||
// typeName: '环评要求',
|
||||
// key: 'QEC_STLL_1_DY95',
|
||||
// keyName: '环评要求',
|
||||
// count: 14
|
||||
// },
|
||||
// {
|
||||
// type: 2,
|
||||
// qecCnt: 13,
|
||||
// qecTCnt: 71,
|
||||
// code: 'QEC_STLL_2_DY95',
|
||||
// desc: '水利部要求_生态流量达标率所在统计区间:>=95%',
|
||||
// typeName: '水利部要求',
|
||||
// key: 'QEC_STLL_2_DY95',
|
||||
// keyName: '水利部要求',
|
||||
// count: 13
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
|
||||
if (res?.data?.data?.length > 0) {
|
||||
const resData = res.data.data;
|
||||
|
||||
if (key) {
|
||||
// 有规则类型时:过滤掉"无生态流量要求"的数据
|
||||
tabList.value = resData
|
||||
.map((el: any) => ({
|
||||
...el,
|
||||
value: el.key,
|
||||
label: `${el.keyName || el.typeName}(${el.count || el.qecCnt}座)`
|
||||
}))
|
||||
.filter((el: any) => el.key !== 'QEC_STLL_6_NOEQMN');
|
||||
|
||||
// 查找当前选中的 ecoFlowType 是否在返回数据中
|
||||
const found = resData.find(
|
||||
(el: any) => el.items?.[0] === ecoFlowType.value
|
||||
);
|
||||
|
||||
// 如果找到则保持,否则使用第一个
|
||||
ecoFlowType.value = found ? ecoFlowType.value : resData[0]?.key;
|
||||
} else {
|
||||
// 没有规则类型时:只显示"无生态流量要求"
|
||||
tabList.value = [
|
||||
{
|
||||
key: 'QEC_STLL_6_NOEQMN',
|
||||
value: 'QEC_STLL_6_NOEQMN',
|
||||
label: `无生态流量要求(${resData?.length}座)`
|
||||
}
|
||||
];
|
||||
ecoFlowType.value = 'QEC_STLL_6_NOEQMN';
|
||||
}
|
||||
|
||||
// 获取饼图数据
|
||||
fetchPieData();
|
||||
// 获取表格数据
|
||||
fetchTableData();
|
||||
} else {
|
||||
tabList.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取 Tab 列表失败:', error);
|
||||
tabList.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const handleView = (record: any) => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENGJJ';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.params.eqtp = record.eqtp;
|
||||
modelStore.filter.stllgzlx = activeKey.value;
|
||||
modelStore.filter.stllTmType = activeTimeType.value;
|
||||
modelStore.filter.rangeTm = [activeSTime.value, activeETime.value];
|
||||
};
|
||||
//获取table数据
|
||||
const fetchTableData = async () => {
|
||||
// 刷新表格
|
||||
const { sTime, eTime, qid, key, timeType } = params.value;
|
||||
const filters: any[] = [
|
||||
qid != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: qid
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: sTime
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: eTime
|
||||
},
|
||||
{
|
||||
field: 'dtin',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1'
|
||||
},
|
||||
key
|
||||
? {
|
||||
field: key,
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: ecoFlowType.value
|
||||
}
|
||||
: null
|
||||
].filter(Boolean);
|
||||
if (!key) {
|
||||
filters.push(
|
||||
{
|
||||
field: 'qecInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'mwrInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
},
|
||||
{
|
||||
field: 'avqInterval',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'QEC_STLL_6_NOEQMN'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 添加饼图选中项过滤
|
||||
if (pieCode.value.length > 0) {
|
||||
if (qid === 'all') {
|
||||
filters.push({
|
||||
field: 'baseId',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: pieCode.value
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
field: 'hbrvcd',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: pieCode.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const filter = {
|
||||
logic: 'and',
|
||||
filters
|
||||
};
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
// 监听 ecoFlowType 变化,重新获取饼图数据
|
||||
watch(ecoFlowType, () => {
|
||||
fetchPieData();
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
// 更新日期范围到 params
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
|
||||
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
|
||||
}
|
||||
|
||||
// 记录当前查询使用的 timeType、key 和时间范围
|
||||
activeTimeType.value = params.value.timeType;
|
||||
activeKey.value = params.value.key;
|
||||
activeSTime.value = params.value.sTime;
|
||||
activeETime.value = params.value.eTime;
|
||||
|
||||
// 重新获取 Tab 列表
|
||||
fetchTabList();
|
||||
};
|
||||
onMounted(() => {
|
||||
// 初始化时设置默认日期并获取数据
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
|
||||
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
|
||||
activeTimeType.value = params.value.timeType;
|
||||
activeKey.value = params.value.key;
|
||||
activeSTime.value = params.value.sTime;
|
||||
activeETime.value = params.value.eTime;
|
||||
fetchTabList();
|
||||
}
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理图表实例
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 处理窗口大小变化
|
||||
const handleResize = () => {
|
||||
chartInstance?.resize();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.stlldbqk-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
// padding: 16px;
|
||||
}
|
||||
|
||||
.body_zhengti {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
.echarts {
|
||||
width: 35%;
|
||||
height: 482px;
|
||||
position: relative;
|
||||
|
||||
.pie-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.table1 {
|
||||
width: 65%;
|
||||
height: 482px;
|
||||
}
|
||||
}
|
||||
.empty-state {
|
||||
width: 100%;
|
||||
height: 582px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
439
frontend/src/modules/shengtaidabiaoModJZ/index.vue
Normal file
@ -0,0 +1,439 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem title="生态流量达标情况" :click-action="{ show: true }">
|
||||
<!-- 使用插槽自定义点击提示的内容 -->
|
||||
<template #click-action-content>
|
||||
<div v-if="titleData.cnt" style="max-width: 300px; line-height: 1.6">
|
||||
<p>
|
||||
1、统计电站范围:接入过生态流量数据的电站
|
||||
<!-- <span
|
||||
style="color: #5989ad; cursor: pointer; "
|
||||
@click.stop="handleStationClick()"
|
||||
>
|
||||
({{titleData.cnt}})
|
||||
</span> -->
|
||||
</p>
|
||||
<p>2、当来水不足时,生态流量不小于入库流量判定为达标</p>
|
||||
<p>3、“≥95%座数”表示统计电站范围内时段达标率大于等于95%的电站数量</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="body_topOne">
|
||||
<a-radio-group v-model:value="mode" @change="dataChage">
|
||||
<a-radio-button value="hour">逐时</a-radio-button>
|
||||
<a-radio-button value="day">日</a-radio-button>
|
||||
</a-radio-group>
|
||||
<div class="title_text">≥95%座数/总座数</div>
|
||||
</div>
|
||||
<a-spin :spinning="spinning">
|
||||
<div>
|
||||
<div v-for="el in datalist" @click="handleBarClick(el)">
|
||||
<div :key="el.key">
|
||||
<div class="boy_one">
|
||||
<div style="flex: 1; white-space: nowrap; margin-right: 5px">
|
||||
{{ el.name }}
|
||||
</div>
|
||||
<img style="flex: 1" src="@/assets/components/fgx.svg" alt="" />
|
||||
</div>
|
||||
<div class="body_zhu">
|
||||
<div class="body_biao">
|
||||
<div v-for="value in allArr">
|
||||
<div :key="value" class="nei_body">
|
||||
<div
|
||||
v-if="
|
||||
value <
|
||||
(el?.key == null
|
||||
? 1
|
||||
: el.qecTCnt == 0
|
||||
? 0
|
||||
: el.qecCnt / el.qecTCnt) *
|
||||
20
|
||||
"
|
||||
class="little_body"
|
||||
:style="{ backgroundColor: el.color }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body_text">{{ el.qecCnt }}/{{ el.qecTCnt }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- 自定义弹框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="'生态流量达标情况'"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
>
|
||||
<STLLXFFS
|
||||
v-if="modalVisible"
|
||||
:options="list"
|
||||
:typeKey="selectedItem.key"
|
||||
:time="mode"
|
||||
:basicId="baseid"
|
||||
:dateArr="dateArr"
|
||||
/>
|
||||
</a-modal>
|
||||
|
||||
<!-- 环保自动监测工作开展情况弹框 -->
|
||||
<a-modal
|
||||
v-model:open="huanbaoModalVisible"
|
||||
:title="'环保自动监测工作开展情况'"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
>
|
||||
<ModalYkzhbzdjcgz v-if="huanbaoModalVisible" :baseId="baseid" :titleData = titleData />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import {
|
||||
qgcetQgcStaticData,
|
||||
evnmAutoMonitorGetKendoListCust
|
||||
} from '@/api/stll';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
import STLLXFFS from './TwoLayer/ShengTaiLiuLiangDaBQKTwoLayer.vue';
|
||||
import ModalYkzhbzdjcgz from './TwoLayer/ModalYkzhbzdjcgz.vue';
|
||||
import HuanbaoZDJCGZKZ from '@/modules/huanbaozdjcgzkzQK/index.vue';
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'shengtaidabiaoMod'
|
||||
});
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const mapViewStore = useMapViewStore();
|
||||
const baseid = ref('');
|
||||
const dateArr = ref<any[]>([]);
|
||||
const obj = {
|
||||
show: true,
|
||||
value:
|
||||
'1、统计电站范围:接入过生态流量数据的电站,2、当来水不足时,生态流量不小于入库流量判定为达标,3、“≥95%座数”表示统计电站范围内时段达标率大于等于95%的电站数量'
|
||||
};
|
||||
|
||||
const mode = ref('hour');
|
||||
const dataArr: any = ref([
|
||||
{
|
||||
name: '生态环境部门要求',
|
||||
qecCnt: '0', // 座数
|
||||
qecTCnt: '0', //总座数
|
||||
type: 1,
|
||||
key: 'qecInterval'
|
||||
},
|
||||
// {
|
||||
// name: '水利部门要求',
|
||||
// qecCnt: '0', // 座数
|
||||
// qecTCnt: '0', //总座数
|
||||
// type: 2,
|
||||
// key: 'mwrInterval'
|
||||
// }
|
||||
]);
|
||||
const list: any = ref([
|
||||
{ name: '生态环境部门要求', type: 1, color: '#77C300', key: 'qecInterval' },
|
||||
// { name: '水利部门要求', type: 2, color: '#56C3E3', key: 'mwrInterval' },
|
||||
// { name: '多年平均流量 10%', type: 3, color: '#F76B01', key: 'avqInterval' },
|
||||
// { name: '无生态流量要求', type: 4, color: '#B4B4B4', key: null }
|
||||
]);
|
||||
const datalist: any = ref([]);
|
||||
const allArr: any = ref(Array.from({ length: 20 }, (_, i) => i));
|
||||
const spinning = ref(false);
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('');
|
||||
const selectedItem = ref<any>(null);
|
||||
const huanbaoModalVisible = ref(false);
|
||||
|
||||
// 处理电站数量点击事件
|
||||
const handleStationClick = () => {
|
||||
huanbaoModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 处理进度条点击事件
|
||||
const handleBarClick = (item: any) => {
|
||||
selectedItem.value = item;
|
||||
// modalTitle.value = `${item.name} - 详细信息`;
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// 根据 type 将 list 中的数据合并到 dataArr 中
|
||||
const setStyle = () => {
|
||||
if (dataArr.value.length == 0) {
|
||||
return false;
|
||||
}
|
||||
dataArr.value.forEach((item: any) => {
|
||||
// 在 list 中找到 type 匹配的数据
|
||||
const matched = list.value.find(
|
||||
(listItem: any) => listItem.type === item.type
|
||||
);
|
||||
if (matched) {
|
||||
// 合并数据,dataArr 的属性优先
|
||||
datalist.value.push({
|
||||
...matched,
|
||||
...item
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log(datalist.value);
|
||||
};
|
||||
const dataChage = () => {
|
||||
getData();
|
||||
};
|
||||
const getdate = (offsetYears = 0) => {
|
||||
const formatDate = (date: Date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
let startDate: string;
|
||||
let endDate: string;
|
||||
|
||||
if (dateArr.value && dateArr.value.length === 2) {
|
||||
const start = new Date(dateArr.value[0]);
|
||||
const end = new Date(dateArr.value[1]);
|
||||
if (offsetYears !== 0) {
|
||||
start.setFullYear(start.getFullYear() + offsetYears);
|
||||
end.setFullYear(end.getFullYear() + offsetYears);
|
||||
}
|
||||
startDate = `${formatDate(start)} 00:00:00`;
|
||||
endDate = `${formatDate(end)} 23:59:59`;
|
||||
} else {
|
||||
const now = new Date();
|
||||
const oneMonthAgo = new Date(now);
|
||||
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
||||
if (offsetYears !== 0) {
|
||||
now.setFullYear(now.getFullYear() + offsetYears);
|
||||
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
|
||||
}
|
||||
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
|
||||
endDate = `${formatDate(now)} 23:59:59`;
|
||||
}
|
||||
|
||||
return { startDate, endDate };
|
||||
};
|
||||
|
||||
const getData = async () => {
|
||||
const { startDate, endDate } = getdate(0);
|
||||
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
baseid.value != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'dtin',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: mode.value
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: startDate
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: endDate
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
|
||||
spinning.value = true;
|
||||
try {
|
||||
let res = await qgcetQgcStaticData(params);
|
||||
// 根据API响应结构规范处理数据
|
||||
if (res.data && res.data.list) {
|
||||
const apiList = res.data.list;
|
||||
//
|
||||
// 只使用前两条数据(type: 1 和 type: 2)
|
||||
if (apiList && apiList.length > 0) {
|
||||
dataArr.value = apiList.slice(0, 2).map((item: any) => ({
|
||||
// name: item.typeName || item.desc,
|
||||
qecCnt: item.qecCnt,
|
||||
qecTCnt: item.qecTCnt,
|
||||
type: item.type
|
||||
}));
|
||||
|
||||
// 清空旧数据并重新合并样式
|
||||
datalist.value = [];
|
||||
setStyle();
|
||||
} else {
|
||||
// 无数据场景处理
|
||||
dataArr.value = [];
|
||||
datalist.value = [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取生态流量达标数据失败:', error);
|
||||
} finally {
|
||||
spinning.value = false;
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => mapViewStore.searchTimeRange,
|
||||
(newVal) => {
|
||||
|
||||
dateArr.value = newVal;
|
||||
getData();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
//统计电站范围:接入过生态流量数据的电站
|
||||
const titleData:any = ref({ cnt: 0 })
|
||||
const getcont = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "showIds",
|
||||
"operator": "in",
|
||||
"value": [
|
||||
"7"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
let res = await evnmAutoMonitorGetKendoListCust(params);
|
||||
// [
|
||||
// {
|
||||
// "cnt": 452,
|
||||
// "orderInx": 7,
|
||||
// "orderInxName": "生态流量",
|
||||
// "sttpFullPath": "ENG",
|
||||
// "sttpCode": "ENG",
|
||||
// "sttpName": "生态流量"
|
||||
// }
|
||||
// ]
|
||||
let list = res?.data?.data[0];
|
||||
if (list) {
|
||||
titleData.value = list
|
||||
}
|
||||
//
|
||||
};
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
setStyle();
|
||||
getcont()
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.body_topOne {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.title_text {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.boy_one {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
.body_zhu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.body_biao {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
border: 1px solid #ccdae7;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
padding: 5px 1px;
|
||||
overflow: hidden;
|
||||
|
||||
.nei_body {
|
||||
width: 12px;
|
||||
height: 100%;
|
||||
border: 1px solid #ccdae7;
|
||||
padding: 2px;
|
||||
margin-left: 4px;
|
||||
|
||||
.little_body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 90px;
|
||||
height: 100%;
|
||||
justify-content: space-around;
|
||||
margin-left: 6px;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-radio-group {
|
||||
// border: 3px solid #2f6b98 !important;
|
||||
// border-radius: 10px !important;
|
||||
|
||||
.ant-radio-button-wrapper-checked {
|
||||
border: 1px solid #2f6b98 !important;
|
||||
background-color: #2f6b98 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper {
|
||||
border: 2px solid #2f6b98 !important;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper-checked :before {
|
||||
background-color: #2f6b98 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -15,7 +15,7 @@
|
||||
<!-- <a-form-item label=" "> -->
|
||||
<a-input
|
||||
v-model:value="params.ennm"
|
||||
placeholder=" "
|
||||
placeholder="请输入电站名称"
|
||||
style="width: 200px; margin-right: 15px"
|
||||
/>
|
||||
<!-- </a-form-item> -->
|
||||
@ -104,7 +104,7 @@ import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
import {
|
||||
vmsstbprptGetKendoList,
|
||||
vmsstbprptGetKendoList1,
|
||||
intervalGetKendoListCust,
|
||||
eqqecRateCount,
|
||||
eqGetKendoListCust
|
||||
@ -442,7 +442,8 @@ const fetchPieData = async () => {
|
||||
};
|
||||
|
||||
// TODO: 调用实际 API - 待补充接口
|
||||
const res = await vmsstbprptGetKendoList(obj);
|
||||
const res = await vmsstbprptGetKendoList1(obj);
|
||||
// debugger
|
||||
if (res?.data?.data) {
|
||||
const dataSource = res.data.data.map((el: any) => ({
|
||||
id: isAllBase ? el.baseId : el.hbrvcd,
|
||||
@ -561,12 +562,7 @@ const updateChart = () => {
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
const maxLen = 8; // 你希望显示的最大字符数
|
||||
let name = params.name;
|
||||
if (name.length > maxLen) {
|
||||
return name.substring(0, maxLen) + '...';
|
||||
}
|
||||
return name;
|
||||
return params.value + '座';
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
|
||||
@ -0,0 +1,796 @@
|
||||
<template>
|
||||
<div class="stlldbqk-container">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form layout="inline" class="search-form">
|
||||
<a-form-item label="基地选择">
|
||||
<a-select
|
||||
v-model:value="params.qid"
|
||||
placeholder="请选择基地"
|
||||
style="width: 200px"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
:options="jidiOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label=" "> -->
|
||||
<a-input
|
||||
v-model:value="params.ennm"
|
||||
placeholder="请输入电站名称"
|
||||
style="width: 200px; margin-right: 15px"
|
||||
/>
|
||||
<!-- </a-form-item> -->
|
||||
<a-form-item label="日期范围">
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
:allow-clear="false"
|
||||
format="YYYY-MM-DD"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- Tabs 选择器(仅在 type === 'left' 时显示) -->
|
||||
<a-spin :spinning="tabsLoading" tip="加载中...">
|
||||
<a-tabs
|
||||
v-if="props.type === 'left' && tabOptions.length > 0"
|
||||
v-model:activeKey="activeTab"
|
||||
@change="handleTabChange"
|
||||
class="stll-tabs"
|
||||
>
|
||||
<a-tab-pane
|
||||
v-for="item in tabOptions"
|
||||
:key="item.qecPerformance"
|
||||
:tab="`${item.qecPerformanceName} (${
|
||||
item.qecRate !== null &&
|
||||
item.qecRate !== undefined &&
|
||||
item.qecRate !== ''
|
||||
? Number(item.qecRate).toFixed(2) + '%'
|
||||
: '-%'
|
||||
})`"
|
||||
/>
|
||||
</a-tabs>
|
||||
|
||||
<div class="body_zhengti">
|
||||
<div class="echarts">
|
||||
<a-spin :spinning="dataLoading">
|
||||
<div
|
||||
v-show="pieData.length > 0"
|
||||
ref="chartRef"
|
||||
class="pie-chart"
|
||||
></div>
|
||||
<a-empty
|
||||
v-show="!dataLoading && pieData.length === 0"
|
||||
description="暂无数据"
|
||||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
<div class="table">
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:scrollY="'450px'"
|
||||
:list-url="
|
||||
props.type == 'top'
|
||||
? intervalGetKendoListCust
|
||||
: eqGetKendoListCust
|
||||
"
|
||||
:search-params="searchParams"
|
||||
:transform-data="customTransform"
|
||||
>
|
||||
<template #ennm="{ column, record }">
|
||||
<a-button type="link" size="small" @click="handleView(record)">{{
|
||||
record.ennm
|
||||
}}</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import dayjs from 'dayjs';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
import {
|
||||
vmsstbprptGetKendoList1,
|
||||
intervalGetKendoListCust,
|
||||
eqqecRateCount,
|
||||
eqGetKendoListCust
|
||||
} from '@/api/stll';
|
||||
|
||||
const modelStore = useModelStore();
|
||||
const props = defineProps<{
|
||||
type?: string;
|
||||
basicId?: string;
|
||||
qecPerformance?: any;
|
||||
dateArr?: string[];
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const columns = [
|
||||
props.type == 'left'
|
||||
? {
|
||||
key: 'baseName',
|
||||
title: '所属基地',
|
||||
dataIndex: 'baseName',
|
||||
sort: true // 启用排序
|
||||
}
|
||||
: null,
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '电站名称',
|
||||
dataIndex: 'ennm',
|
||||
slots: { customRender: 'action' },
|
||||
sort: true // 启用排序
|
||||
},
|
||||
{
|
||||
key: 'addvcdName',
|
||||
title: '行政区',
|
||||
dataIndex: 'addvcdName',
|
||||
sort: true // 启用排序
|
||||
},
|
||||
{
|
||||
key: 'hbrvcdName',
|
||||
title: '所在河段',
|
||||
dataIndex: 'hbrvcdName',
|
||||
width: '300px'
|
||||
},
|
||||
{
|
||||
key: 'qecRate',
|
||||
title: `生态流量达标率(%)`,
|
||||
dataIndex: 'qecRate',
|
||||
sort: true, // 启用排序
|
||||
width: '160px',
|
||||
customRender: ({ text }: any) => {
|
||||
if (text === null || text === undefined || text === '' || text === '-')
|
||||
return '-';
|
||||
return Number(text).toFixed(2);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'beforeQecRate',
|
||||
title: '去年同期达标率(%)',
|
||||
dataIndex: 'beforeQecRate',
|
||||
width: '160px',
|
||||
sort: true, // 启用排序
|
||||
customRender: ({ text }: any) => {
|
||||
if (text === null || text === undefined || text === '' || text === '-')
|
||||
return '-';
|
||||
return Number(text).toFixed(2);
|
||||
}
|
||||
}
|
||||
].filter(Boolean);
|
||||
const tableRef = ref();
|
||||
|
||||
const defaultSort = ref([
|
||||
{
|
||||
field: 'baseId',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'rvcdStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'siteStepSort',
|
||||
dir: 'asc'
|
||||
},
|
||||
{
|
||||
field: 'qecRate',
|
||||
dir: 'asc'
|
||||
}
|
||||
]);
|
||||
|
||||
// 动态计算搜索参数(包含排序)
|
||||
const searchParams = computed(() => {
|
||||
let baseSort;
|
||||
|
||||
// 如果用户触发了排序,将其加入排序数组
|
||||
|
||||
// 使用默认排序
|
||||
baseSort = defaultSort.value;
|
||||
|
||||
return {
|
||||
sort: baseSort
|
||||
};
|
||||
});
|
||||
|
||||
// 初始化日期范围:上个月的本日到今天,或根据父组件传入的 dateArr
|
||||
const initDateRange = () => {
|
||||
// 如果父组件传入了 dateArr,使用它
|
||||
if (props.dateArr && props.dateArr.length === 2) {
|
||||
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
|
||||
}
|
||||
// 否则使用默认的"上个月的本日到今天"
|
||||
const today = dayjs();
|
||||
const lastMonthSameDay = today.subtract(1, 'month');
|
||||
return [lastMonthSameDay, today];
|
||||
};
|
||||
|
||||
const dateRange = ref(initDateRange());
|
||||
|
||||
// 监听父组件传入的 dateArr 变化
|
||||
watch(
|
||||
() => props.dateArr,
|
||||
newVal => {
|
||||
if (newVal && newVal.length === 2) {
|
||||
dateRange.value = [dayjs(newVal[0]), dayjs(newVal[1])];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const params = ref({
|
||||
qid: props.basicId || 'all',
|
||||
ennm: '',
|
||||
sTime: '',
|
||||
eTime: ''
|
||||
});
|
||||
|
||||
const jidiOptions = computed(() => {
|
||||
return [
|
||||
...JidiSelectEventStore.jidiData.map((item: any) => ({
|
||||
value: item.wbsCode,
|
||||
label: item.wbsName
|
||||
}))
|
||||
];
|
||||
});
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
// ==================== Tabs 相关 ====================
|
||||
const activeTab = ref<string>(props.qecPerformance || ''); // 当前选中的 tab key,默认为空字符串
|
||||
const tabOptions = ref<any[]>([]); // tabs 选项列表
|
||||
const tabsLoading = ref<boolean>(false); // tabs 数据加载状态
|
||||
|
||||
// 获取 Tabs 选项
|
||||
const fetchTabOptions = async () => {
|
||||
if (props.type !== 'left') return;
|
||||
|
||||
tabsLoading.value = true;
|
||||
try {
|
||||
const res = await eqqecRateCount({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
params.value.qid !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: params.value.qid
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'type',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'DAY'
|
||||
},
|
||||
{
|
||||
field: 'RGCP_CODE',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['1']
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value:
|
||||
params.value.sTime ||
|
||||
dayjs().subtract(1, 'month').format('YYYY-MM-DD 00:00:00')
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: params.value.eTime || dayjs().format('YYYY-MM-DD 23:59:59')
|
||||
}
|
||||
].filter(Boolean)
|
||||
},
|
||||
group: [{ dir: 'asc', field: 'qecPerformance' }]
|
||||
});
|
||||
|
||||
if (res?.data?.data && res.data.data.length > 0) {
|
||||
tabOptions.value = res.data.data;
|
||||
// 默认选中第一项
|
||||
activeTab.value = props.qecPerformance || res.data.data[0].qecPerformance;
|
||||
} else {
|
||||
tabOptions.value = [];
|
||||
activeTab.value = '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取tabs选项失败:', error);
|
||||
tabOptions.value = [];
|
||||
activeTab.value = '';
|
||||
} finally {
|
||||
tabsLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Tab 切换处理
|
||||
const handleTabChange = (key: string) => {
|
||||
activeTab.value = key;
|
||||
// 同时刷新饼图和表格
|
||||
fetchPieData();
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// ==================== 图表相关 ====================
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const pieData = ref<any[]>([]); // 饼图数据源
|
||||
const dataLoading = ref<boolean>(false);
|
||||
const pieCode = ref<string[]>([]); // 用于表格过滤的选中项
|
||||
|
||||
// 颜色配置
|
||||
const themeecLists = [];
|
||||
|
||||
const jdColor = [
|
||||
'#4b79ab',
|
||||
'#78c300',
|
||||
'#9556a4',
|
||||
'#df91ab',
|
||||
'#7399c6',
|
||||
'#dbb629',
|
||||
'#56c2e3',
|
||||
'#f56a06',
|
||||
'#cdba75',
|
||||
'#76523b',
|
||||
'#df75b6',
|
||||
'#00a050',
|
||||
'#f5a644',
|
||||
'#4b79ab',
|
||||
'#78c300',
|
||||
'#9556a4',
|
||||
'#df91ab',
|
||||
'#7399c6',
|
||||
'#dbb629',
|
||||
'#56c2e3',
|
||||
'#f56a06',
|
||||
'#cdba75',
|
||||
'#76523b',
|
||||
'#df75b6',
|
||||
'#00a050',
|
||||
'#f5a644'
|
||||
];
|
||||
|
||||
// 获取饼图数据
|
||||
const fetchPieData = async () => {
|
||||
if (!params.value.sTime || !params.value.eTime) return;
|
||||
|
||||
const isAllBase = params.value.qid === 'all';
|
||||
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const { sTime, eTime, qid, ennm } = params.value;
|
||||
let obj = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'ENG'
|
||||
},
|
||||
{
|
||||
field: 'prsc',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [1, 2, 3]
|
||||
},
|
||||
qid != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: qid
|
||||
}
|
||||
: null,
|
||||
|
||||
{
|
||||
field: 'bldstt',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [1, 10, 11]
|
||||
},
|
||||
props.type != 'left'
|
||||
? {
|
||||
field: 'dtinEnv',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [1, 2]
|
||||
}
|
||||
: {
|
||||
field: 'RGCP_CODE',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [activeTab.value]
|
||||
}
|
||||
].filter(Boolean)
|
||||
},
|
||||
group: [
|
||||
isAllBase
|
||||
? { dir: 'asc', field: 'baseStepSort' }
|
||||
: { dir: 'asc', field: 'rvcdStepSort' },
|
||||
{
|
||||
dir: 'asc',
|
||||
field: isAllBase ? 'baseId' : 'hbrvcd'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: isAllBase ? 'baseName' : 'hbrvcdName'
|
||||
}
|
||||
],
|
||||
groupResultFlat: true
|
||||
};
|
||||
|
||||
// TODO: 调用实际 API - 待补充接口
|
||||
const res = await vmsstbprptGetKendoList1(obj);
|
||||
// debugger
|
||||
if (res?.data?.data) {
|
||||
const dataSource = res.data.data.map((el: any) => ({
|
||||
id: isAllBase ? el.baseId : el.hbrvcd,
|
||||
name: isAllBase ? el.baseName : el.hbrvcdName,
|
||||
value: isAllBase ? el.count_baseId : el.count_hbrvcd,
|
||||
key: isAllBase ? el.baseId : el.hbrvcd
|
||||
}));
|
||||
|
||||
pieData.value = dataSource;
|
||||
pieCode.value = []; // 重置选中状态
|
||||
|
||||
// 等待 DOM 更新后初始化或更新图表
|
||||
await nextTick();
|
||||
initOrUpdateChart();
|
||||
} else {
|
||||
pieData.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取饼图数据失败:', error);
|
||||
pieData.value = [];
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化或更新图表
|
||||
const initOrUpdateChart = () => {
|
||||
if (!chartRef.value) return;
|
||||
|
||||
// 如果实例不存在,初始化
|
||||
if (!chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 绑定图例选择事件
|
||||
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
|
||||
}
|
||||
|
||||
updateChart();
|
||||
};
|
||||
|
||||
// 更新图表配置
|
||||
const updateChart = () => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
const total =
|
||||
pieData.value.reduce((pre: number, cur: any) => pre + cur.value, 0) || 0;
|
||||
|
||||
// 生成颜色数组
|
||||
const colors = pieData.value.map((item: any, index: number) => {
|
||||
const matched = themeecLists.find((i: any) => i.name === item.name);
|
||||
return matched?.color || jdColor[index % jdColor.length];
|
||||
});
|
||||
|
||||
const option: EChartsOption = {
|
||||
color: colors,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} (座) ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 20,
|
||||
top: 80,
|
||||
type: 'scroll',
|
||||
textStyle: {
|
||||
width: 100, // 固定文本宽度
|
||||
overflow: 'truncate', // 超出部分截断
|
||||
ellipsis: '...' // 截断时显示的字符
|
||||
}
|
||||
},
|
||||
title: [
|
||||
{
|
||||
text: total.toString(),
|
||||
left: '34%',
|
||||
top: '44%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '电站总数量',
|
||||
left: '34%',
|
||||
top: '51%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '(座)',
|
||||
left: '34%',
|
||||
top: '55%',
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '电站总数量',
|
||||
type: 'pie',
|
||||
radius: ['31%', '40%'],
|
||||
center: ['35%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
return params.value + '座';
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
data: pieData.value
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option, true);
|
||||
};
|
||||
|
||||
// 处理图例选择变化
|
||||
const handleLegendSelectChanged = (params: any) => {
|
||||
const selected = params.selected;
|
||||
const trueRivers = Object.keys(selected).filter(key => selected[key]);
|
||||
const allFalse = Object.values(selected).every(value => value === false);
|
||||
|
||||
const selectedKeys = pieData.value
|
||||
.filter((item: any) => trueRivers.includes(item.name))
|
||||
.map((item: any) => item.key);
|
||||
|
||||
pieCode.value = allFalse ? ['0'] : selectedKeys;
|
||||
|
||||
// 刷新表格
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 自定义数据转换
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
const handleView = (record: any) => {
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENGJJ';
|
||||
modelStore.title = record.ennm;
|
||||
modelStore.params.stcd = record.stcd;
|
||||
modelStore.params.eqtp = record.eqtp;
|
||||
};
|
||||
|
||||
// 获取表格数据
|
||||
const fetchTableData = async () => {
|
||||
const { sTime, eTime, qid, ennm } = params.value;
|
||||
|
||||
// TODO: 构建过滤条件 - 待补充接口参数
|
||||
const filters = [
|
||||
qid != 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: qid
|
||||
}
|
||||
: null,
|
||||
{
|
||||
field: 'dtinEnv',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [1, 2]
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: sTime
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: eTime
|
||||
},
|
||||
ennm
|
||||
? {
|
||||
field: 'ennm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: ennm
|
||||
}
|
||||
: null,
|
||||
props.type == 'left'
|
||||
? {
|
||||
field: 'rgcpCode',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: activeTab.value
|
||||
}
|
||||
: null,
|
||||
props.type == 'left'
|
||||
? {
|
||||
field: 'type',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'DAY'
|
||||
}
|
||||
: null
|
||||
].filter(Boolean);
|
||||
|
||||
const filter = {
|
||||
logic: 'and',
|
||||
filters
|
||||
};
|
||||
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
// 更新日期范围到 params
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
|
||||
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
|
||||
}
|
||||
|
||||
// 如果 type === 'left',重新获取 tabs 数据
|
||||
if (props.type === 'left') {
|
||||
fetchTabOptions().then(() => {
|
||||
// tabs 数据获取完成后,再获取饼图和表格数据
|
||||
fetchPieData();
|
||||
fetchTableData();
|
||||
});
|
||||
} else {
|
||||
// 重新获取数据
|
||||
fetchPieData();
|
||||
fetchTableData();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化时设置默认日期并获取数据
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
|
||||
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
|
||||
|
||||
// 如果 type === 'left',先获取 tabs 数据
|
||||
if (props.type === 'left') {
|
||||
fetchTabOptions().then(() => {
|
||||
// tabs 数据获取完成后,再获取饼图和表格数据
|
||||
console.log(JidiSelectEventStore.jidiData);
|
||||
fetchPieData();
|
||||
fetchTableData();
|
||||
});
|
||||
} else {
|
||||
// 自动获取图表和表格数据
|
||||
console.log(JidiSelectEventStore.jidiData);
|
||||
fetchPieData();
|
||||
fetchTableData();
|
||||
}
|
||||
}
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理图表实例
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 处理窗口大小变化
|
||||
const handleResize = () => {
|
||||
chartInstance?.resize();
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.stlldbqk-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
// padding: 16px;
|
||||
}
|
||||
|
||||
.stll-tabs {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
:deep(.ant-tabs-nav) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.body_zhengti {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
margin-top: 25px;
|
||||
.echarts {
|
||||
width: 35%;
|
||||
height: 482px;
|
||||
position: relative;
|
||||
|
||||
.pie-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.table {
|
||||
width: 65%;
|
||||
height: 482px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
675
frontend/src/modules/shengtaidabiaoTwoModJJ/index.vue
Normal file
@ -0,0 +1,675 @@
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem title="生态流量达标情况" :click-action="{ show: true }">
|
||||
<!-- 使用插槽自定义点击提示的内容 -->
|
||||
<!-- 1、时间:2026-05-11 至 2026-06-11;
|
||||
|
||||
-->
|
||||
<template #click-action-content>
|
||||
<div v-if="titleData.cnt" style="max-width: 300px; line-height: 1.6">
|
||||
<p>
|
||||
1、时间:{{ getdate().startDate1 }} 至 {{ getdate().endDate1 }};
|
||||
</p>
|
||||
<p>
|
||||
2、范围:接入过生态流量数据的电站
|
||||
<!-- <span
|
||||
style="color: #5989ad; cursor: pointer"
|
||||
@click.stop="handleStationClick()"
|
||||
>
|
||||
({{ titleData.cnt }}) </span
|
||||
>; -->
|
||||
</p>
|
||||
<p>3、达标判定:按环评批复或报告中对生态流量限值有明确要求的评判。</p>
|
||||
</div>
|
||||
</template>
|
||||
<div class="body_topOne">
|
||||
<a-radio-group v-model:value="mode" @change="getEcharts()">
|
||||
<a-radio-button value="top">按基地</a-radio-button>
|
||||
<a-radio-button value="left">按调节性能</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<a-spin :spinning="spinning">
|
||||
<div ref="chartRef" class="chart-container"></div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
</div>
|
||||
<!-- 环保自动监测工作开展情况弹框 -->
|
||||
<a-modal
|
||||
v-model:open="huanbaoModalVisible"
|
||||
:title="'环保自动监测工作开展情况'"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
>
|
||||
<ModalYkzhbzdjcgz v-if="huanbaoModalVisible" :titleData="titleData" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 生态流量达标率详情弹框 -->
|
||||
<a-modal
|
||||
v-model:open="detailModalVisible"
|
||||
:title="detailModalTitle"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
>
|
||||
<STLLXFFS
|
||||
v-if="detailModalVisible"
|
||||
:type="mode"
|
||||
:basicId="detailData[0]?.baseId"
|
||||
:qecPerformance="detailData[0].qecPerformance"
|
||||
:date-arr="dateArr"
|
||||
/>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { eqqecRateCount, evnmAutoMonitorGetKendoListCust } from '@/api/stll';
|
||||
import ModalYkzhbzdjcgz from '../shengtaidabiaoMod/TwoLayer/ModalYkzhbzdjcgz.vue';
|
||||
import STLLXFFS from './TwoLayer/STLLXFFS.vue'
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'shengtaidabiaoMod'
|
||||
});
|
||||
const mode = ref('top');
|
||||
const spinning = ref(false);
|
||||
const chartRef = ref<HTMLElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const mapViewStore = useMapViewStore();
|
||||
// 动态数据存储
|
||||
let dateArr = ref<any[]>([]);
|
||||
const chartData = ref<{
|
||||
categories: string[];
|
||||
currentData: number[];
|
||||
lastYearData: number[];
|
||||
baseIds: (string | number)[];
|
||||
qecPerformances: (string | number)[];
|
||||
}>({
|
||||
categories: [],
|
||||
currentData: [],
|
||||
lastYearData: [],
|
||||
baseIds: [],
|
||||
qecPerformances: []
|
||||
});
|
||||
const huanbaoModalVisible = ref(false);
|
||||
// 处理电站数量点击事件
|
||||
const handleStationClick = () => {
|
||||
huanbaoModalVisible.value = true;
|
||||
};
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
spinning.value = true;
|
||||
|
||||
try {
|
||||
// 先获取数据
|
||||
await getEcharts();
|
||||
|
||||
// 确保 DOM 已渲染后初始化图表
|
||||
setTimeout(() => {
|
||||
initChart();
|
||||
spinning.value = false;
|
||||
}, 50);
|
||||
} catch (error) {
|
||||
console.error('数据加载失败:', error);
|
||||
spinning.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) {
|
||||
console.error('图表容器未渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查容器尺寸
|
||||
const containerHeight = chartRef.value.offsetHeight;
|
||||
if (!containerHeight || containerHeight === 0) {
|
||||
console.warn('容器高度为 0,延迟重试');
|
||||
setTimeout(() => initChart(), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果实例存在,先销毁
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 使用动态数据
|
||||
const data = chartData.value;
|
||||
const isHorizontal = mode.value === 'top';
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: function (params: any) {
|
||||
let result = params[0].name + '<br/>';
|
||||
params.forEach((param: any) => {
|
||||
const percentage = param.value.toFixed(2) + '%';
|
||||
result +=
|
||||
param.marker +
|
||||
param.seriesName +
|
||||
' <b>' +
|
||||
percentage +
|
||||
'</b><br/>';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['当前', '去年同期']
|
||||
},
|
||||
grid: { left: 10, right: 10, bottom: 20, top: 40, containLabel: true },
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
// 横向柱形图(mode='top'): X轴是数值轴,需要缩放
|
||||
// 竖向柱状图(mode='left'): X轴是分类轴,不需要缩放;Y轴是数值轴,也不需要内部缩放
|
||||
[isHorizontal ? 'xAxisIndex' : 'xAxisIndex']: 0,
|
||||
filterMode: 'empty',
|
||||
zoomOnMouseWheel: true,
|
||||
moveOnMouseMove: false,
|
||||
moveOnMouseWheel: true,
|
||||
start: 0,
|
||||
end: 100,
|
||||
minValueSpan: 0,
|
||||
maxValueSpan: isHorizontal ? 20 : 5
|
||||
}
|
||||
],
|
||||
xAxis: isHorizontal
|
||||
? {
|
||||
type: 'value',
|
||||
min: 80,
|
||||
max: 100,
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E8E8E8',
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
formatter: '{value}'
|
||||
}
|
||||
}
|
||||
: {
|
||||
type: 'category',
|
||||
data: data.categories,
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
rotate: 0,
|
||||
margin: 10
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: isHorizontal
|
||||
? {
|
||||
type: 'category',
|
||||
data: data.categories,
|
||||
inverse: true,
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
rotate: 45,
|
||||
margin: 10
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E8E8E8',
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666',
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '当前',
|
||||
type: 'bar',
|
||||
data: data.currentData,
|
||||
itemStyle: {
|
||||
color: '#5470C6'
|
||||
},
|
||||
barWidth: isHorizontal ? 8 : 8
|
||||
// barGap: '30%'
|
||||
},
|
||||
{
|
||||
name: '去年同期',
|
||||
type: 'bar',
|
||||
data: data.lastYearData,
|
||||
itemStyle: {
|
||||
color: '#91CC75'
|
||||
},
|
||||
barWidth: isHorizontal ? 8 : 8
|
||||
// barGap: '30%'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 添加点击事件
|
||||
chartInstance.off('click');
|
||||
chartInstance.on('click', (params: any) => {
|
||||
handleChartClick(params);
|
||||
});
|
||||
|
||||
// 强制重绘,确保尺寸正确
|
||||
setTimeout(() => {
|
||||
chartInstance?.resize();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// 处理图表点击事件
|
||||
const handleChartClick = (params: any) => {
|
||||
const categoryName = params.name;
|
||||
const currentIndex = params.dataIndex;
|
||||
//
|
||||
|
||||
// 设置弹框标题
|
||||
detailModalTitle.value =
|
||||
mode.value === 'top'
|
||||
? `生态流量达标率(按基地统计) `
|
||||
: `生态流量达标率(按调节性能统计)`;
|
||||
|
||||
// 获取当前点击项的详细数据
|
||||
const baseId = mode.value === 'top' ? chartData.value.baseIds[currentIndex] : undefined;
|
||||
const qecPerformance = mode.value === 'left' ? chartData.value.qecPerformances[currentIndex] : undefined;
|
||||
//
|
||||
|
||||
const currentItem = {
|
||||
baseId: baseId,
|
||||
baseName: mode.value === 'top' ? categoryName :'',
|
||||
qecPerformance: qecPerformance,
|
||||
qecPerformanceName: mode.value === 'left' ? categoryName : '',
|
||||
qecRate: chartData.value.currentData[currentIndex],
|
||||
beforeQecRate: chartData.value.lastYearData[currentIndex]
|
||||
};
|
||||
|
||||
detailData.value = [currentItem];
|
||||
detailModalVisible.value = true;
|
||||
};
|
||||
|
||||
const getdate = (offsetYears = 0) => {
|
||||
const formatDate = (date: Date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
let startDate: string;
|
||||
let endDate: string;
|
||||
let startDate1: string;
|
||||
let endDate1: string;
|
||||
|
||||
// 如果 dateArr.value 有数据,使用数组中的日期
|
||||
if (dateArr.value && dateArr.value.length === 2) {
|
||||
|
||||
const start = new Date(dateArr.value[0]);
|
||||
const end = new Date(dateArr.value[1]);
|
||||
|
||||
// 如果 offsetYears 不为 0,表示获取去年同期的时间
|
||||
if (offsetYears !== 0) {
|
||||
start.setFullYear(start.getFullYear() + offsetYears);
|
||||
end.setFullYear(end.getFullYear() + offsetYears);
|
||||
}
|
||||
|
||||
startDate = `${formatDate(start)} 00:00:00`;
|
||||
endDate = `${formatDate(end)} 23:59:59`;
|
||||
// 提取日期部分(去除时间)
|
||||
startDate1 = formatDate(start);
|
||||
endDate1 = formatDate(end);
|
||||
// debugger
|
||||
} else {
|
||||
// 否则使用默认的"一个月前到现在"
|
||||
const now = new Date();
|
||||
const oneMonthAgo = new Date(now);
|
||||
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
|
||||
|
||||
// 如果 offsetYears 不为 0,表示获取去年同期的时间
|
||||
if (offsetYears !== 0) {
|
||||
now.setFullYear(now.getFullYear() + offsetYears);
|
||||
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
|
||||
}
|
||||
|
||||
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
|
||||
endDate = `${formatDate(now)} 23:59:59`;
|
||||
startDate1 = formatDate(oneMonthAgo);
|
||||
endDate1 = formatDate(now);
|
||||
}
|
||||
|
||||
return { startDate, endDate, startDate1, endDate1 };
|
||||
};
|
||||
const getEcharts = async () => {
|
||||
// 获取当前时间范围
|
||||
const currentDateRange = getdate(0);
|
||||
// 获取去年同期时间范围(往前推一年)
|
||||
const lastYearDateRange = getdate(-1);
|
||||
|
||||
// 构建基础 filter(不包含时间)
|
||||
const baseFilter = {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
mode.value == 'top'
|
||||
? {
|
||||
field: 'dtinEnv',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: [1, 2]
|
||||
}
|
||||
: {
|
||||
field: 'type',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: 'DAY'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 构建当前时间范围的 params
|
||||
const currentParams = {
|
||||
filter: {
|
||||
...baseFilter,
|
||||
filters: [
|
||||
...baseFilter.filters,
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: currentDateRange.startDate
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: currentDateRange.endDate
|
||||
}
|
||||
]
|
||||
},
|
||||
group:
|
||||
mode.value == 'top'
|
||||
? [
|
||||
{ dir: 'asc', field: 'basestepsort' },
|
||||
{ dir: 'asc', field: 'baseId' },
|
||||
{ dir: 'asc', field: 'baseName' }
|
||||
]
|
||||
: [{ dir: 'asc', field: 'qecPerformance' }]
|
||||
};
|
||||
|
||||
// 构建去年同期时间范围的 params
|
||||
const lastYearParams = {
|
||||
filter: {
|
||||
...baseFilter,
|
||||
filters: [
|
||||
...baseFilter.filters,
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: lastYearDateRange.startDate
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: lastYearDateRange.endDate
|
||||
}
|
||||
]
|
||||
},
|
||||
group: currentParams.group
|
||||
};
|
||||
|
||||
try {
|
||||
// 并行请求两个 API
|
||||
const [currentRes, lastYearRes]: [any, any] = await Promise.all([
|
||||
eqqecRateCount(currentParams),
|
||||
eqqecRateCount(lastYearParams)
|
||||
]);
|
||||
|
||||
// 处理当前数据
|
||||
const currentApiData = (currentRes && currentRes.data && currentRes.data.data) ? currentRes.data.data : [];
|
||||
// 处理去年同期数据
|
||||
const lastYearApiData = (lastYearRes && lastYearRes.data && lastYearRes.data) ? lastYearRes.data.data : [];
|
||||
|
||||
// 根据 mode 提取不同的数据
|
||||
if (mode.value === 'top') {
|
||||
// 按基地模式:去重处理(避免 baseId 重复)
|
||||
const uniqueMap = new Map();
|
||||
currentApiData.forEach((item: any) => {
|
||||
if (!uniqueMap.has(item.baseId)) {
|
||||
uniqueMap.set(item.baseId, item);
|
||||
}
|
||||
});
|
||||
|
||||
const uniqueData = Array.from(uniqueMap.values());
|
||||
|
||||
// 建立去年同期的数据映射(按 baseId)
|
||||
const lastYearMap = new Map();
|
||||
lastYearApiData.forEach((item: any) => {
|
||||
if (!lastYearMap.has(item.baseId)) {
|
||||
lastYearMap.set(item.baseId, item.qecRate);
|
||||
}
|
||||
});
|
||||
|
||||
chartData.value = {
|
||||
categories: uniqueData.map((item: any) => item.baseName || ''),
|
||||
currentData: uniqueData.map((item: any) =>
|
||||
item.qecRate != null ? Number(item.qecRate) : 0
|
||||
),
|
||||
lastYearData: uniqueData.map((item: any) => {
|
||||
const lastYearRate = lastYearMap.get(item.baseId);
|
||||
return lastYearRate != null ? Number(lastYearRate) : 0;
|
||||
}),
|
||||
baseIds: uniqueData.map((item: any) => item.baseId),
|
||||
qecPerformances: []
|
||||
};
|
||||
} else {
|
||||
// 按调节性能模式
|
||||
|
||||
// 建立去年同期的数据映射(按 qecPerformance)
|
||||
const lastYearMap = new Map();
|
||||
lastYearApiData.forEach((item: any) => {
|
||||
if (!lastYearMap.has(item.qecPerformance)) {
|
||||
lastYearMap.set(item.qecPerformance, item.qecRate);
|
||||
}
|
||||
});
|
||||
|
||||
chartData.value = {
|
||||
categories: currentApiData.map((item: any) => item.qecPerformanceName || ''),
|
||||
currentData: currentApiData.map((item: any) =>
|
||||
item.qecRate != null ? Number(item.qecRate) : 0
|
||||
),
|
||||
lastYearData: currentApiData.map((item: any) => {
|
||||
const lastYearRate = lastYearMap.get(item.qecPerformance);
|
||||
return lastYearRate != null ? Number(lastYearRate) : 0;
|
||||
}),
|
||||
baseIds: [],
|
||||
qecPerformances: currentApiData.map((item: any) => item.qecPerformance || '')
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API 调用失败:', error);
|
||||
// 发生错误时使用空数据
|
||||
chartData.value = {
|
||||
categories: [],
|
||||
currentData: [],
|
||||
lastYearData: [],
|
||||
baseIds: [],
|
||||
qecPerformances: []
|
||||
};
|
||||
}
|
||||
};
|
||||
//统计电站范围:接入过生态流量数据的电站
|
||||
const titleData: any = ref({ cnt: 0 });
|
||||
const getcont = async () => {
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'showIds',
|
||||
operator: 'in',
|
||||
value: ['7']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
let res = await evnmAutoMonitorGetKendoListCust(params);
|
||||
// [
|
||||
// {
|
||||
// "cnt": 452,
|
||||
// "orderInx": 7,
|
||||
// "orderInxName": "生态流量",
|
||||
// "sttpFullPath": "ENG",
|
||||
// "sttpCode": "ENG",
|
||||
// "sttpName": "生态流量"
|
||||
// }
|
||||
// ]
|
||||
let list = res?.data?.data[0];
|
||||
if (list) {
|
||||
titleData.value = list;
|
||||
}
|
||||
//
|
||||
};
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
getcont();
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', () => {
|
||||
chartInstance?.resize();
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => mapViewStore.searchTimeRange,
|
||||
(newVal, oldVal) => {
|
||||
console.log('时间范围变化:', oldVal, '->', newVal);
|
||||
dateArr.value = newVal
|
||||
// debugger
|
||||
loadData();
|
||||
// 处理你的逻辑
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 详情弹框相关
|
||||
const detailModalVisible = ref(false);
|
||||
const detailModalTitle = ref('');
|
||||
const detailData = ref<any[]>([]);
|
||||
const detailColumns = ref([
|
||||
{
|
||||
title: mode.value === 'top' ? '基地名称' : '调节性能',
|
||||
dataIndex: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
|
||||
key: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
|
||||
width: 150,
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title: '当前达标率',
|
||||
dataIndex: 'qecRate',
|
||||
key: 'qecRate',
|
||||
width: 120,
|
||||
align: 'right'
|
||||
},
|
||||
{
|
||||
title: '去年同期达标率',
|
||||
dataIndex: 'beforeQecRate',
|
||||
key: 'beforeQecRate',
|
||||
width: 150,
|
||||
align: 'right'
|
||||
}
|
||||
]);
|
||||
|
||||
// 监听 mode 变化
|
||||
watch(mode, () => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.body_topOne {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.title_text {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.ant-radio-group {
|
||||
.ant-radio-button-wrapper-checked {
|
||||
border: 1px solid #2f6b98 !important;
|
||||
background-color: #2f6b98 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper {
|
||||
border: 2px solid #2f6b98 !important;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper-checked :before {
|
||||
background-color: #2f6b98 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 449px;
|
||||
// padding: 10px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
@ -1,76 +1,98 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<!-- 水生生态调查 -->
|
||||
<template>
|
||||
<SidePanelItem title="水生生态调查" :select="select" :datetimePicker="datetimePicker">
|
||||
<div ref="chartRef" class="echarts-container"></div>
|
||||
<SidePanelItem
|
||||
title="水生生态调查"
|
||||
:moreSelect="moreSelect"
|
||||
:select="select"
|
||||
@update-values="handlePanelChange"
|
||||
>
|
||||
<a-spin :spinning="dataLoading" tip="加载中..." class="spin-wrapper">
|
||||
<div
|
||||
v-show="chartData.length > 0 && select.options.length > 0"
|
||||
ref="chartRef"
|
||||
class="chart-container"
|
||||
></div>
|
||||
<a-empty
|
||||
v-show="
|
||||
!dataLoading &&
|
||||
(chartData.length === 0 || select.options.length === 0)
|
||||
"
|
||||
:description="select.options.length === 0 ? '暂无数据' : '暂无数据'"
|
||||
class="empty-placeholder"
|
||||
/>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { wbsbGetKendoList, getDftYear, getFishListStat } from '@/api/stdc';
|
||||
import moment from 'moment';
|
||||
|
||||
// 定义组件名
|
||||
defineOptions({
|
||||
name: 'shuiShengShengTaiDiaoCha'
|
||||
});
|
||||
|
||||
// ==================== 响应式变量定义 ====================
|
||||
const chartRef = ref<HTMLDivElement>();
|
||||
let chartInstance: any = null;
|
||||
// ==================== Store ====================
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
|
||||
// ==================== 基础状态 ====================
|
||||
const baseid = ref('');
|
||||
const isInitializing = ref(false);
|
||||
const dataLoading = ref(false);
|
||||
|
||||
// ==================== 选择器状态 ====================
|
||||
const moreSelect = ref({
|
||||
show: true,
|
||||
value: undefined as string | undefined,
|
||||
options: [] as any[]
|
||||
});
|
||||
|
||||
// 选择器配置
|
||||
const select = ref({
|
||||
show: true,
|
||||
value: undefined,
|
||||
options: [],
|
||||
value: undefined as string | undefined,
|
||||
options: [] as any[],
|
||||
picker: undefined,
|
||||
format: undefined
|
||||
format: undefined,
|
||||
width: '120px'
|
||||
});
|
||||
|
||||
// 日期选择器配置
|
||||
const datetimePicker = ref({
|
||||
show: true,
|
||||
value: '2020',
|
||||
format: 'YYYY',
|
||||
picker: 'year' as const,
|
||||
options: []
|
||||
});
|
||||
const yearOptions = ref<Record<string, any[]>>({});
|
||||
|
||||
// ==================== 模拟数据 ====================
|
||||
const mockData = [
|
||||
{ name: '鱼类', value: 320 },
|
||||
{ name: '底栖动物', value: 280 },
|
||||
{ name: '浮游植物', value: 250 },
|
||||
{ name: '浮游动物', value: 220 },
|
||||
{ name: '水生植物', value: 190 },
|
||||
{ name: '两栖类', value: 150 },
|
||||
{ name: '爬行类', value: 120 },
|
||||
{ name: '鸟类', value: 100 },
|
||||
{ name: '哺乳类', value: 80 },
|
||||
{ name: '其他', value: 60 }
|
||||
];
|
||||
// ==================== 图表状态 ====================
|
||||
const chartRef = ref<HTMLDivElement>();
|
||||
let chartInstance: any = null;
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
const chartData = ref<any[]>([]);
|
||||
|
||||
// 计算总数
|
||||
const totalValue = mockData.reduce((sum, item) => sum + item.value, 0);
|
||||
// ==================== 图表相关 ====================
|
||||
|
||||
// ==================== 图表初始化函数 ====================
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) return;
|
||||
|
||||
// 检查容器尺寸
|
||||
const rect = chartRef.value.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) {
|
||||
console.warn('图表容器尺寸为0,稍后重试...');
|
||||
setTimeout(() => initChart(), 50);
|
||||
return;
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
// 初始化图表实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
updateChart();
|
||||
};
|
||||
|
||||
const updateChart = () => {
|
||||
// 如果图表实例不存在但 DOM 已就绪,先初始化
|
||||
if (!chartInstance && chartRef.value) { chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
if (!chartInstance) return;
|
||||
|
||||
const totalValue = chartData.value.reduce(
|
||||
(sum: number, item: any) => sum + (item.fcnt || 0),
|
||||
0
|
||||
);
|
||||
|
||||
// 图表配置
|
||||
const option: EChartsOption = {
|
||||
color: [
|
||||
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
|
||||
@ -80,26 +102,19 @@ const initChart = () => {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
borderColor: 'transparent',
|
||||
textStyle: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
textStyle: { color: '#ffffff' },
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'horizontal',
|
||||
bottom: '5%',
|
||||
data: mockData.map(item => item.name),
|
||||
data: chartData.value.map((item: any) => item.name),
|
||||
icon: 'rect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 14,
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
height: 8
|
||||
},
|
||||
pageTextStyle: {
|
||||
color: '#333'
|
||||
},
|
||||
textStyle: { fontSize: 12, height: 8 },
|
||||
pageTextStyle: { color: '#333' },
|
||||
pageIconColor: '#2F6B98',
|
||||
pageIconInactiveColor: '#aaa'
|
||||
},
|
||||
@ -109,20 +124,13 @@ const initChart = () => {
|
||||
top: '42%',
|
||||
textAlign: 'center',
|
||||
left: '50%',
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 13,
|
||||
fontWeight: 500
|
||||
}
|
||||
textStyle: { color: '#333', fontSize: 13, fontWeight: 500 }
|
||||
},
|
||||
{
|
||||
text: totalValue.toString(),
|
||||
left: 'center',
|
||||
top: '32%',
|
||||
textStyle: {
|
||||
color: '#2F6B98',
|
||||
fontSize: 24
|
||||
}
|
||||
textStyle: { color: '#2F6B98', fontSize: 24 }
|
||||
}
|
||||
],
|
||||
series: [
|
||||
@ -137,49 +145,317 @@ const initChart = () => {
|
||||
position: 'outside',
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 8,
|
||||
length2: 10
|
||||
},
|
||||
labelLine: { show: true, length: 8, length2: 10 },
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
label: { show: true, fontSize: 14, fontWeight: 'bold' }
|
||||
},
|
||||
data: mockData
|
||||
data: chartData.value.map((item: any) => ({
|
||||
name: item.name,
|
||||
value: item.fcnt || 0
|
||||
}))
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置配置项
|
||||
chartInstance.setOption(option);
|
||||
nextTick(() => {
|
||||
chartInstance.resize();
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 窗口resize处理 ====================
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 数据获取 ====================
|
||||
|
||||
const getYearData = async () => {
|
||||
try {
|
||||
const filter: any = {};
|
||||
if (baseid.value && baseid.value !== 'all') {
|
||||
filter.baseId = baseid.value;
|
||||
}
|
||||
|
||||
const params = {
|
||||
filter,
|
||||
group: [{ dir: 'des', field: 'yr' }]
|
||||
};
|
||||
|
||||
const res = await getDftYear(params);
|
||||
// debugger
|
||||
const data = res?.data?.data?.[0] || {};
|
||||
|
||||
if (Object.keys(data).length > 0) {
|
||||
const objYear: Record<string, any[]> = {};
|
||||
const allYears: any[] = [];
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
objYear[key] = data[key].map((item: any) => ({
|
||||
label: String(item),
|
||||
value: String(item)
|
||||
}));
|
||||
allYears.push(...data[key]);
|
||||
});
|
||||
|
||||
yearOptions.value = objYear;
|
||||
|
||||
const uniqueYears = [...new Set(allYears)].sort(
|
||||
(a: any, b: any) => Number(b) - Number(a)
|
||||
);
|
||||
const latestYear = uniqueYears[0];
|
||||
|
||||
const firstWbsCode = Object.keys(data)[0];
|
||||
select.value.options = objYear[firstWbsCode] || [];
|
||||
select.value.value = latestYear
|
||||
? String(latestYear)
|
||||
: moment().format('YYYY');
|
||||
} else {
|
||||
yearOptions.value = {};
|
||||
select.value.options = [];
|
||||
select.value.value = moment().format('YYYY');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取年份数据失败:', error);
|
||||
select.value.options = [];
|
||||
select.value.value = moment().format('YYYY');
|
||||
}
|
||||
};
|
||||
|
||||
const updateYearOptionsByBase = async (wbsCode: string) => {
|
||||
const options = yearOptions.value[wbsCode];
|
||||
if (options) {
|
||||
select.value.options = options;
|
||||
await nextTick();
|
||||
select.value.value = options[0]?.value || '';
|
||||
} else {
|
||||
select.value.options = [];
|
||||
await nextTick();
|
||||
select.value.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const getMoreSelectData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
isInitializing.value = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'wbsType',
|
||||
operator: 'eq',
|
||||
value: 'PSB_RVCD'
|
||||
},
|
||||
baseid.value !== 'all'
|
||||
? {
|
||||
field: 'objid',
|
||||
operator: 'eq',
|
||||
value: baseid.value
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
},
|
||||
group: [
|
||||
{ dir: 'asc', field: 'orderIndex' },
|
||||
{ dir: 'asc', field: 'wbsCode' },
|
||||
{ dir: 'asc', field: 'wbsName' },
|
||||
{ dir: 'asc', field: 'objid' }
|
||||
],
|
||||
groupResultFlat: true
|
||||
};
|
||||
|
||||
const res = await wbsbGetKendoList(params);
|
||||
const data = res?.data?.data || [];
|
||||
// debugger
|
||||
|
||||
if (data.length > 0) {
|
||||
const treeData = buildTreeData(data);
|
||||
const filteredData = filterSelectOptions(treeData);
|
||||
|
||||
moreSelect.value.options = filteredData;
|
||||
moreSelect.value.value = treeData?.[0]?.children?.[0]?.value;
|
||||
|
||||
await getYearData();
|
||||
if (moreSelect.value.value) {
|
||||
await updateYearOptionsByBase(moreSelect.value.value);
|
||||
}
|
||||
await getChartData();
|
||||
} else {
|
||||
moreSelect.value.options = [];
|
||||
moreSelect.value.value = undefined;
|
||||
await getYearData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取树形选择器数据失败:', error);
|
||||
moreSelect.value.options = [];
|
||||
moreSelect.value.value = undefined;
|
||||
} finally {
|
||||
isInitializing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取图表数据
|
||||
* 根据当前选中的流域(hbrvcd)调用 getFishListStat API
|
||||
*/
|
||||
const getChartData = async () => {
|
||||
if (!moreSelect.value.value) return;
|
||||
|
||||
dataLoading.value = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'hbrvcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: moreSelect.value.value
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const res = await getFishListStat(params);
|
||||
// debugger
|
||||
chartData.value = res?.data?.data || [];
|
||||
nextTick(() => {
|
||||
updateChart();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取图表数据失败:', error);
|
||||
chartData.value = [];
|
||||
nextTick(() => {
|
||||
updateChart();
|
||||
});
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 树形工具函数 ====================
|
||||
|
||||
const buildTreeData = (data: any[]) => {
|
||||
if (!data || data.length === 0) return [];
|
||||
|
||||
const jiDiListMap: Record<string, string> = {};
|
||||
JidiSelectEventStore.jidiData.forEach((item: any) => {
|
||||
jiDiListMap[item.wbsCode] = item.wbsName;
|
||||
});
|
||||
|
||||
const dataMapNameMap: Record<string, any[]> = {};
|
||||
data.forEach(item => {
|
||||
const objId = item.objid || item.objId;
|
||||
if (!dataMapNameMap[objId]) {
|
||||
dataMapNameMap[objId] = [];
|
||||
}
|
||||
const exists = dataMapNameMap[objId].some(
|
||||
existing => existing.wbsCode === item.wbsCode
|
||||
);
|
||||
if (!exists) {
|
||||
dataMapNameMap[objId].push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const treeData = Object.keys(dataMapNameMap).map(objId => ({
|
||||
title: jiDiListMap[objId] || '---',
|
||||
value: objId,
|
||||
selectable: false,
|
||||
children: dataMapNameMap[objId].map((item: any) => ({
|
||||
title: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
}));
|
||||
|
||||
treeData.sort((a, b) => {
|
||||
const numA = Number(a.value);
|
||||
const numB = Number(b.value);
|
||||
const aIsNum = !isNaN(numA);
|
||||
const bIsNum = !isNaN(numB);
|
||||
if (aIsNum && bIsNum) return numA - numB;
|
||||
if (aIsNum) return -1;
|
||||
if (bIsNum) return 1;
|
||||
return a.value.localeCompare(b.value);
|
||||
});
|
||||
|
||||
return treeData;
|
||||
};
|
||||
|
||||
const filterSelectOptions = (options: any[]) => {
|
||||
if (options?.length === 1 && options?.[0]?.children?.length === 1) {
|
||||
return options[0].children;
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
const handlePanelChange = async (data: any) => {
|
||||
if (isInitializing.value) return;
|
||||
|
||||
if (
|
||||
data.moreSelect !== undefined &&
|
||||
data.moreSelect !== moreSelect.value.value
|
||||
) {
|
||||
moreSelect.value.value = data.moreSelect;
|
||||
await updateYearOptionsByBase(data.moreSelect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.select !== undefined && data.select !== select.value.value) {
|
||||
select.value.value = data.select;
|
||||
await getChartData();
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== Watch 监听 ====================
|
||||
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
async newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
await getMoreSelectData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// ==================== 生命周期钩子 ====================
|
||||
|
||||
// 页面加载时执行
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
if (chartRef.value) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(chartRef.value);
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
});
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
@ -188,9 +464,32 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.echarts-container {
|
||||
.spin-wrapper {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 270px;
|
||||
min-height: 270px;
|
||||
}
|
||||
|
||||
.empty-placeholder {
|
||||
width: 100%;
|
||||
min-height: 270px;
|
||||
display: flex;
|
||||
background: #fff;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.ant-empty-description) {
|
||||
text-indent: 0em !important;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -2,209 +2,207 @@
|
||||
<template>
|
||||
<div class="data-table-container">
|
||||
<a-table :columns="columns" :data-source="tableData" :pagination="false" size="middle" :customRow="customRow"
|
||||
bordered class="custom-table">
|
||||
:loading="dataLoading" bordered class="custom-table">
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
||||
import { ref, watch } from 'vue';
|
||||
import { getVmsstbprptKendoList } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
// 定义组件名
|
||||
defineOptions({
|
||||
name: 'DataTable'
|
||||
});
|
||||
|
||||
// 表格列配置
|
||||
const columns: ColumnsType = [
|
||||
const emit = defineEmits<{
|
||||
(e: 'row-click', record: any): void;
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
const tableData = ref<any[]>([]);
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '基地名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
fixed: 'left',
|
||||
fixed: 'left' as const,
|
||||
width: 114.19,
|
||||
align: 'left'
|
||||
align: 'left' as const
|
||||
},
|
||||
{
|
||||
title: '装机容量(万kW)',
|
||||
key: 'capacity',
|
||||
align: 'center',
|
||||
align: 'center' as const,
|
||||
children: [
|
||||
{
|
||||
title: '总计',
|
||||
dataIndex: 'total',
|
||||
key: 'total',
|
||||
width: 76.64,
|
||||
align: 'center'
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '已建',
|
||||
dataIndex: 'built',
|
||||
key: 'built',
|
||||
dataIndex: 'buildinstall',
|
||||
key: 'buildinstall',
|
||||
width: 76.64,
|
||||
align: 'center'
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '在建',
|
||||
dataIndex: 'building',
|
||||
key: 'building',
|
||||
dataIndex: 'ubuildinstall',
|
||||
key: 'ubuildinstall',
|
||||
width: 68.25,
|
||||
align: 'center'
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '未建',
|
||||
dataIndex: 'unbuilt',
|
||||
key: 'unbuilt',
|
||||
// width: 76,
|
||||
align: 'center'
|
||||
},
|
||||
]
|
||||
dataIndex: 'nbuildinstall',
|
||||
key: 'nbuildinstall',
|
||||
align: 'center' as const
|
||||
}
|
||||
];
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{
|
||||
key: '1',
|
||||
name: '金沙江干流',
|
||||
total: '7952.00',
|
||||
built: '6370.00',
|
||||
building: '902.00',
|
||||
unbuilt: '680.00'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: '雅砻江干流',
|
||||
total: '2773.65',
|
||||
built: '1920.00',
|
||||
building: '492.00',
|
||||
unbuilt: '361.65'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: '大渡河干流',
|
||||
total: '2689.65',
|
||||
built: '1925.65',
|
||||
building: '534.00',
|
||||
unbuilt: '230.00'
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: '乌江干流',
|
||||
total: '1181.36',
|
||||
built: '1133.36',
|
||||
building: '48.00',
|
||||
unbuilt: '0.00'
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
name: '长江上游干流',
|
||||
total: '3212.65',
|
||||
built: '2523.65',
|
||||
building: '0.00',
|
||||
unbuilt: '689.00'
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
name: '湘西',
|
||||
total: '1054.32',
|
||||
built: '959.30',
|
||||
building: '38.52',
|
||||
unbuilt: '56.65'
|
||||
},
|
||||
{
|
||||
key: '7',
|
||||
name: '黄河上游干流',
|
||||
total: '2794.59',
|
||||
built: '1749.89',
|
||||
building: '0.00',
|
||||
unbuilt: '1044.65'
|
||||
},
|
||||
{
|
||||
key: '8',
|
||||
name: '黄河中游干流',
|
||||
total: '835.65',
|
||||
built: '401.65',
|
||||
building: '0.00',
|
||||
unbuilt: '434.00'
|
||||
},
|
||||
{
|
||||
key: '9',
|
||||
name: '南盘江 - 红水河',
|
||||
total: '1271.00',
|
||||
built: '1271.00',
|
||||
building: '0.00',
|
||||
unbuilt: '0.00'
|
||||
},
|
||||
{
|
||||
key: '10',
|
||||
name: '东北',
|
||||
total: '1331.95',
|
||||
built: '749.05',
|
||||
building: '0.00',
|
||||
unbuilt: '582.90'
|
||||
},
|
||||
{
|
||||
key: '11',
|
||||
name: '澜沧江干流',
|
||||
total: '2535.00',
|
||||
built: '2275.00',
|
||||
building: '260.00',
|
||||
unbuilt: '0.00'
|
||||
},
|
||||
{
|
||||
key: '12',
|
||||
name: '怒江干流',
|
||||
total: '3138.00',
|
||||
built: '0.00',
|
||||
building: '102.00',
|
||||
unbuilt: '3036.00'
|
||||
},
|
||||
{
|
||||
key: '13',
|
||||
name: '闽浙赣',
|
||||
total: '962.08',
|
||||
built: '920.68',
|
||||
building: '0.00',
|
||||
unbuilt: '41.40'
|
||||
},
|
||||
{
|
||||
key: '14',
|
||||
name: '其他',
|
||||
total: '7460.57',
|
||||
built: '7273.27',
|
||||
building: '121.90',
|
||||
unbuilt: '65.40'
|
||||
},
|
||||
{
|
||||
key: '15',
|
||||
name: '总计',
|
||||
total: '7460.57',
|
||||
built: '7273.27',
|
||||
building: '121.90',
|
||||
unbuilt: '65.40'
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
// 自定义行样式
|
||||
const customRow = (_record: any, index: number) => {
|
||||
const getData = async () => {
|
||||
if (!baseid.value) return;
|
||||
|
||||
const filters: any[] = [
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
||||
{ field: 'bldsttCcode', operator: 'isnotnull' },
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
||||
];
|
||||
if (baseid.value !== 'all') {
|
||||
filters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
|
||||
}
|
||||
|
||||
const aggregates = [{ field: 'ttpwr', aggregate: 'sum' }];
|
||||
|
||||
const group: any[] = [];
|
||||
if (baseid.value !== 'all') {
|
||||
group.push({ dir: 'asc', field: 'rvcdStepSort' });
|
||||
} else {
|
||||
group.push({ dir: 'asc', field: 'baseStepSort' });
|
||||
}
|
||||
group.push(
|
||||
{ dir: 'asc', field: baseid.value === 'all' ? 'baseId' : 'hbrvcd' },
|
||||
{ dir: 'asc', field: baseid.value === 'all' ? 'baseName' : 'hbrvcdName', aggregates },
|
||||
{ dir: 'desc', field: 'bldsttCcode', aggregates }
|
||||
);
|
||||
|
||||
const params = {
|
||||
filter: { logic: 'and', filters },
|
||||
group
|
||||
};
|
||||
|
||||
try {
|
||||
dataLoading.value = true;
|
||||
const res = await getVmsstbprptKendoList(params);
|
||||
const resData = res?.data?.data;
|
||||
if (resData && Array.isArray(resData)) {
|
||||
let newList: any = {};
|
||||
let keyList: any[] = [];
|
||||
|
||||
const list = resData.flatMap((item: any) => {
|
||||
const key = baseid.value === 'all' ? item.items[0].key : item.key;
|
||||
keyList.push(key);
|
||||
const { BASENAME, HBRVCDNAME } = baseid.value === 'all'
|
||||
? item?.items?.[0]?.items?.[0]?.items[0]?.items?.[0]
|
||||
: item?.items?.[0]?.items?.[0]?.items[0]?.items[0];
|
||||
return item.items.map((outItem: any) => {
|
||||
const itmeData = baseid.value === 'all' ? outItem.items?.[0]?.items : outItem.items[0]?.items;
|
||||
const nbuildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '0')?.items[0]?.SUM_TTPWR * 0.1 || 0;
|
||||
const ubuildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '1')?.items[0]?.SUM_TTPWR * 0.1 || 0;
|
||||
const buildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '2')?.items[0]?.SUM_TTPWR * 0.1 || 0;
|
||||
const total = nbuildinstall + ubuildinstall + buildinstall;
|
||||
return {
|
||||
key,
|
||||
nbuildinstall: nbuildinstall?.toFixed(2),
|
||||
ubuildinstall: ubuildinstall?.toFixed(2),
|
||||
buildinstall: buildinstall?.toFixed(2),
|
||||
total: total?.toFixed(2),
|
||||
name: BASENAME || HBRVCDNAME,
|
||||
bldsttCcode: outItem.key
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const result = { total: 0, buildinstall: 0, ubuildinstall: 0, nbuildinstall: 0 };
|
||||
list.forEach((e: any) => {
|
||||
result.total += Number(e.total) * 100;
|
||||
result.buildinstall += Number(e.buildinstall) * 100;
|
||||
result.ubuildinstall += Number(e.ubuildinstall) * 100;
|
||||
result.nbuildinstall += Number(e.nbuildinstall) * 100;
|
||||
if (newList[e.key]) {
|
||||
const { total, nbuildinstall, ubuildinstall, buildinstall } = newList[e.key];
|
||||
newList[e.key] = {
|
||||
...e,
|
||||
total: Number(total) + Number(e.total),
|
||||
nbuildinstall: Number(nbuildinstall) + Number(e.nbuildinstall),
|
||||
ubuildinstall: Number(ubuildinstall) + Number(e.ubuildinstall),
|
||||
buildinstall: Number(buildinstall) + Number(e.buildinstall)
|
||||
};
|
||||
} else {
|
||||
newList[e.key] = e;
|
||||
}
|
||||
});
|
||||
|
||||
const { total, buildinstall, ubuildinstall, nbuildinstall } = result;
|
||||
const arr: any[] = keyList.map((key) => newList[key]);
|
||||
tableData.value = [
|
||||
...arr,
|
||||
{
|
||||
name: '总计',
|
||||
total: (total / 100)?.toFixed(2),
|
||||
buildinstall: (buildinstall / 100)?.toFixed(2),
|
||||
ubuildinstall: (ubuildinstall / 100)?.toFixed(2),
|
||||
nbuildinstall: (nbuildinstall / 100)?.toFixed(2)
|
||||
}
|
||||
];
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DataTable getData error:', error);
|
||||
tableData.value = [];
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const customRow = (record: any, index: number) => {
|
||||
return {
|
||||
style: {
|
||||
backgroundColor: index % 2 === 1 ? '#fafafa' : '#ffffff'
|
||||
},
|
||||
onClick: () => {
|
||||
if (record.name !== '总计') {
|
||||
emit('row-click', record);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.data-table-container {
|
||||
// padding: 0;
|
||||
background: #fff;
|
||||
padding: 16px 0px;
|
||||
width: 100%;
|
||||
@ -213,7 +211,6 @@ onMounted(() => {
|
||||
.custom-table {
|
||||
:deep(.ant-table) {
|
||||
font-size: 14px;
|
||||
// border: 1px solid #d5e2ed;
|
||||
border-top: 1px solid #d5e2ed;
|
||||
|
||||
.ant-table-thead {
|
||||
@ -221,8 +218,6 @@ onMounted(() => {
|
||||
>th {
|
||||
background-color: #e5eff8 !important;
|
||||
color: #2f6b98;
|
||||
// font-weight: 600;
|
||||
// border: 1px solid #d5e2ed!important;
|
||||
padding: 0px 6px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
@ -249,7 +244,6 @@ onMounted(() => {
|
||||
.ant-table-tbody {
|
||||
>tr {
|
||||
>td {
|
||||
// border: 1px solid #d5e2ed;
|
||||
padding: 0px 6px !important;
|
||||
text-align: center;
|
||||
overflow-wrap: break-word;
|
||||
@ -257,7 +251,6 @@ onMounted(() => {
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
// font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,7 +260,6 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 移除默认的 footer 单元格样式
|
||||
.ant-table-footer::before {
|
||||
display: none;
|
||||
}
|
||||
@ -295,6 +287,5 @@ onMounted(() => {
|
||||
.ant-table-wrapper .ant-table-thead>tr>th {
|
||||
border-bottom: 1px solid #d5e2ed00 !important;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,439 @@
|
||||
<!-- HuanJingSJJRQK.vue -->
|
||||
<template>
|
||||
<div class="huanjing-sjjrqk">
|
||||
<a-tabs v-model:activeKey="activeTab" @change="handleTabChange">
|
||||
<a-tab-pane key="notAccess" tab="大中型已建在建电站" />
|
||||
<a-tab-pane key="partAccess" tab="已接入电站运行数据" />
|
||||
<a-tab-pane key="isAccess" tab="已开展全过程监测工作" />
|
||||
</a-tabs>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧饼图 -->
|
||||
<div class="chart-section">
|
||||
<div ref="chartContainer" class="chart-container"></div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧表格 -->
|
||||
<div class="table-section">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
:pagination="false"
|
||||
size="middle"
|
||||
:customRow="customRow"
|
||||
:loading="tableLoading"
|
||||
class="custom-table"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="detailModalVisible"
|
||||
:title="detailModalTitle"
|
||||
width="1200px"
|
||||
:footer="null"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<component
|
||||
v-if="detailModalVisible && detailComponent"
|
||||
:is="detailComponent"
|
||||
v-bind="detailProps"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, watch, shallowRef } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { getVmsstbprptKendoList } from '@/api/home';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
defineOptions({
|
||||
name: 'HuanJingSJJRQK'
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
dataDimensionVal: string;
|
||||
name: string;
|
||||
}>();
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const activeTab = ref(props.name || 'notAccess');
|
||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
const tableLoading = ref(false);
|
||||
const tableData = ref<any[]>([]);
|
||||
const chartFilter = ref<string>('');
|
||||
|
||||
// 详情弹窗相关
|
||||
const detailModalVisible = ref(false);
|
||||
const detailModalTitle = ref('');
|
||||
const detailComponent = shallowRef<any>(null);
|
||||
const detailProps = ref<any>({});
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref([
|
||||
{
|
||||
title: '电站名称',
|
||||
dataIndex: 'stnm',
|
||||
key: 'stnm',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
title: '接入状态',
|
||||
dataIndex: 'accessStatus',
|
||||
key: 'accessStatus',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
align: 'center',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
// 自定义行样式
|
||||
const customRow = (record: any) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
handleRowClick(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 处理表格行点击
|
||||
const handleRowClick = async (record: any) => {
|
||||
// 根据当前 Tab 打开不同的详情组件
|
||||
if (activeTab.value === 'notAccess') {
|
||||
// 大中型已建在建电站 - 打开电站详情
|
||||
detailModalTitle.value = '电站详情';
|
||||
// 这里需要动态导入对应的详情组件
|
||||
// 暂时使用占位符
|
||||
detailComponent.value = null;
|
||||
detailProps.value = {
|
||||
stcd: record.stcd,
|
||||
stnm: record.stnm
|
||||
};
|
||||
detailModalVisible.value = true;
|
||||
} else if (activeTab.value === 'partAccess') {
|
||||
// 已接入电站运行数据
|
||||
detailModalTitle.value = '运行数据详情';
|
||||
detailComponent.value = null;
|
||||
detailProps.value = {
|
||||
stcd: record.stcd,
|
||||
stnm: record.stnm
|
||||
};
|
||||
detailModalVisible.value = true;
|
||||
} else if (activeTab.value === 'isAccess') {
|
||||
// 已开展全过程监测工作
|
||||
detailModalTitle.value = '监测数据详情';
|
||||
detailComponent.value = null;
|
||||
detailProps.value = {
|
||||
stcd: record.stcd,
|
||||
stnm: record.stnm
|
||||
};
|
||||
detailModalVisible.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取图表和表格数据
|
||||
const getData = async () => {
|
||||
if (!props.dataDimensionVal) return;
|
||||
|
||||
const baseFilters: any[] = [
|
||||
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
||||
{ field: 'bldsttCcode', operator: 'in', dataType: 'string', value: ['1', '2'] }
|
||||
];
|
||||
|
||||
if (props.dataDimensionVal !== 'all') {
|
||||
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: props.dataDimensionVal });
|
||||
}
|
||||
|
||||
let params: any = {};
|
||||
|
||||
if (activeTab.value === 'notAccess') {
|
||||
// 大中型已建在建电站
|
||||
params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
||||
]
|
||||
}
|
||||
};
|
||||
} else if (activeTab.value === 'partAccess') {
|
||||
// 已接入电站运行数据
|
||||
params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'runState', operator: 'eq', dataType: 'number', value: 4 },
|
||||
{ field: 'dtin', operator: 'eq', dataType: 'number', value: 1 }
|
||||
]
|
||||
}
|
||||
};
|
||||
} else if (activeTab.value === 'isAccess') {
|
||||
// 已开展全过程监测工作
|
||||
params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
...baseFilters,
|
||||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] },
|
||||
{ field: 'dtinEnv', operator: 'in', dataType: 'string', value: ['2'] }
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
tableLoading.value = true;
|
||||
const res = await getVmsstbprptKendoList(params);
|
||||
const resData = res?.data?.data || [];
|
||||
|
||||
// 处理表格数据
|
||||
tableData.value = resData.map((item: any) => ({
|
||||
key: item.stcd,
|
||||
stcd: item.stcd,
|
||||
stnm: item.stnm,
|
||||
accessStatus: getAccessStatus(item),
|
||||
...item
|
||||
}));
|
||||
|
||||
// 更新图表
|
||||
updateChart(resData);
|
||||
} catch (error) {
|
||||
console.error('HuanJingSJJRQK getData error:', error);
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取接入状态文本
|
||||
const getAccessStatus = (item: any) => {
|
||||
if (activeTab.value === 'notAccess') {
|
||||
return '已建在建';
|
||||
} else if (activeTab.value === 'partAccess') {
|
||||
return '已接入';
|
||||
} else if (activeTab.value === 'isAccess') {
|
||||
return '已监测';
|
||||
}
|
||||
return '-';
|
||||
};
|
||||
|
||||
// 更新图表
|
||||
const updateChart = (data: any[]) => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
// 按建设状态分组统计
|
||||
const statusMap: Record<string, number> = {};
|
||||
data.forEach((item: any) => {
|
||||
const status = item.bldsttCcode === '1' ? '在建' : '已建';
|
||||
statusMap[status] = (statusMap[status] || 0) + 1;
|
||||
});
|
||||
|
||||
const chartData = Object.entries(statusMap).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'center'
|
||||
},
|
||||
color: ['#78c300', '#2196F3'],
|
||||
series: [
|
||||
{
|
||||
name: '电站数量',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
data: chartData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option, true);
|
||||
|
||||
// 添加点击事件
|
||||
chartInstance.off('click');
|
||||
chartInstance.on('click', (params: any) => {
|
||||
chartFilter.value = params.name;
|
||||
filterTableByChart(params.name);
|
||||
});
|
||||
};
|
||||
|
||||
// 根据图表筛选表格
|
||||
const filterTableByChart = (status: string) => {
|
||||
// 这里需要根据实际数据重新请求或过滤
|
||||
// 暂时简单实现:高亮显示对应状态的数据
|
||||
console.log('筛选状态:', status);
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
|
||||
chartInstance = echarts.init(chartContainer.value);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'center'
|
||||
},
|
||||
color: ['#78c300', '#2196F3'],
|
||||
series: [
|
||||
{
|
||||
name: '电站数量',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
data: [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 处理 Tab 切换
|
||||
const handleTabChange = (key: string) => {
|
||||
activeTab.value = key;
|
||||
chartFilter.value = '';
|
||||
getData();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (chartContainer.value) {
|
||||
initChart();
|
||||
}
|
||||
getData();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听 props 变化
|
||||
watch(
|
||||
() => props.dataDimensionVal,
|
||||
() => {
|
||||
getData();
|
||||
}
|
||||
);
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.huanjing-sjjrqk {
|
||||
padding: 20px;
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
|
||||
.chart-section {
|
||||
flex: 0 0 400px;
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-section {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-table {
|
||||
:deep(.ant-table) {
|
||||
font-size: 13px;
|
||||
border: 1px solid #e8e8e8;
|
||||
|
||||
.ant-table-thead {
|
||||
> tr {
|
||||
> th {
|
||||
background-color: #e5eff8 !important;
|
||||
color: #2f6b98;
|
||||
font-weight: 600;
|
||||
border: 1px solid #e8e8e8 !important;
|
||||
padding: 6px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-tbody {
|
||||
> tr {
|
||||
> td {
|
||||
border: 1px solid #e8e8e8;
|
||||
padding: 6px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #e6f7ff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -9,18 +9,50 @@
|
||||
<div>水电开发情况</div>
|
||||
<div style="color: #757575;font-size: 12px;line-height: 16px;">图释:数量(内)/装机容量(外)</div>
|
||||
<div>
|
||||
<DevelopStatusChart />
|
||||
<DevelopStatusChart @chart-click="handleChartClick" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }} -->
|
||||
<div style="flex: 1;display: flex; flex-direction: column; align-items: center;">
|
||||
<EngEnvironmentData />
|
||||
<EngEnvironmentData @item-click="handleEnvItemClick" />
|
||||
</div>
|
||||
</div>
|
||||
<DataTable />
|
||||
<DataTable @row-click="handleRowClick" />
|
||||
</div>
|
||||
<!-- <div v-else class="zanwushujv"> <a-empty /></div> -->
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- 水电开发情况弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="shuidianModalVisible"
|
||||
:title="shuidianModalTitle"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<ShuiDianKaiFQKTwoLayer
|
||||
v-if="shuidianModalVisible"
|
||||
:defaultJidiInfo="shuidianModalProps.defaultJidiInfo"
|
||||
:seriesName="shuidianModalProps.seriesName"
|
||||
:stateName="shuidianModalProps.stateName"
|
||||
:defaultTab="shuidianModalProps.defaultTab"
|
||||
/>
|
||||
</a-modal>
|
||||
|
||||
<!-- 环境监测数据接入情况弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="huanjingModalVisible"
|
||||
:title="huanjingModalTitle"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<HuanJingSJJRQK
|
||||
v-if="huanjingModalVisible"
|
||||
:dataDimensionVal="huanjingModalProps.dataDimensionVal"
|
||||
:name="huanjingModalProps.name"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -30,15 +62,75 @@ import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import DevelopStatusChart from "@/components/developStatusChart/index.vue"
|
||||
import EngEnvironmentData from "@/components/engEnvironmentData/index.vue"
|
||||
import DataTable from "./DataTable.vue"
|
||||
import ShuiDianKaiFQKTwoLayer from "./TwoLayer/ShuiDianKaiFQKTwoLayer.vue"
|
||||
import HuanJingSJJRQK from "./TwoLayer/HuanJingSJJRQK.vue"
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'shuidianhuangjingjieruMod'
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
|
||||
const prompt = ref({
|
||||
show: true,
|
||||
value: '统计大中型已建,在建水电站',
|
||||
|
||||
})
|
||||
|
||||
// 水电开发情况弹窗状态
|
||||
const shuidianModalVisible = ref(false);
|
||||
const shuidianModalTitle = ref('水电开发情况');
|
||||
const shuidianModalProps = ref<any>({});
|
||||
|
||||
// 环境监测数据接入情况弹窗状态
|
||||
const huanjingModalVisible = ref(false);
|
||||
const huanjingModalTitle = ref('环境监测数据接入情况');
|
||||
const huanjingModalProps = ref<any>({});
|
||||
|
||||
// DataTable 行点击事件
|
||||
const handleRowClick = (record: any) => {
|
||||
const currentJidiInfo = JidiSelectEventStore.selectedItem;
|
||||
shuidianModalProps.value = {
|
||||
defaultJidiInfo: {
|
||||
baseid: currentJidiInfo?.wbsCode || 'all',
|
||||
basename: currentJidiInfo?.wbsName || '全部基地'
|
||||
},
|
||||
seriesName: currentJidiInfo?.wbsCode === 'other' ? '数量(座)' : '装机容量(万kW)',
|
||||
stateName: record.bldsttCcode === '0' ? '未建' : record.bldsttCcode === '1' ? '在建' : '已建',
|
||||
defaultTab: record.bldsttCcode === '0' ? '未建' : record.bldsttCcode === '1' ? '在建' : '已建'
|
||||
};
|
||||
shuidianModalTitle.value = '水电开发情况';
|
||||
shuidianModalVisible.value = true;
|
||||
};
|
||||
|
||||
// DevelopStatusChart 饼图点击事件
|
||||
const handleChartClick = (params: any) => {
|
||||
const currentJidiInfo = JidiSelectEventStore.selectedItem;
|
||||
shuidianModalProps.value = {
|
||||
defaultJidiInfo: {
|
||||
baseid: currentJidiInfo?.wbsCode || 'all',
|
||||
basename: currentJidiInfo?.wbsName || '全部基地'
|
||||
},
|
||||
seriesName: params.seriesName,
|
||||
stateName: params.name,
|
||||
defaultTab: params.name
|
||||
};
|
||||
shuidianModalTitle.value = '水电开发情况';
|
||||
shuidianModalVisible.value = true;
|
||||
};
|
||||
|
||||
// EngEnvironmentData 指标点击事件
|
||||
const handleEnvItemClick = (params: any) => {
|
||||
const currentJidiInfo = JidiSelectEventStore.selectedItem;
|
||||
huanjingModalProps.value = {
|
||||
dataDimensionVal: currentJidiInfo?.wbsCode || 'all',
|
||||
name: params.name
|
||||
};
|
||||
huanjingModalTitle.value = '环境监测数据接入情况';
|
||||
huanjingModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => {
|
||||
|
||||
|
||||
@ -264,11 +264,12 @@ const getDcpcOptions = async () => {
|
||||
];
|
||||
|
||||
const res = await getWeStaticsSecond( { filter: params, group });
|
||||
// debugger
|
||||
|
||||
if (res?.data?.data?.length) {
|
||||
const options = res.data.data.map((item: any) => ({
|
||||
label: item.dcpc,
|
||||
value: item.dcpc
|
||||
label: item.key,
|
||||
value: item.key
|
||||
}));
|
||||
dcpcOptions.value = [{ label: '全部', value: '' }, ...options];
|
||||
} else {
|
||||
|
||||
614
frontend/src/modules/stllyanchengChangeModJJ/index.vue
Normal file
@ -0,0 +1,614 @@
|
||||
<template>
|
||||
<div>
|
||||
<SidePanelItem
|
||||
title="生态流量沿程变化"
|
||||
:iconmap="iconmap"
|
||||
:select="select"
|
||||
:datetimePicker="datetimePicker"
|
||||
@update-values="handlePanelChange"
|
||||
>
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="dataLoading" class="chart-loading">
|
||||
<a-spin tip="加载中..." />
|
||||
</div>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<a-empty
|
||||
v-if="!chartData.length && !dataLoading"
|
||||
description="暂无数据"
|
||||
class="chart-empty"
|
||||
/>
|
||||
|
||||
<!-- 图表容器(使用 display 控制,避免 v-if 销毁 DOM)-->
|
||||
<div
|
||||
ref="chartRef"
|
||||
class="chart-container"
|
||||
:style="{
|
||||
display: chartData.length && !dataLoading ? 'block' : 'none'
|
||||
}"
|
||||
></div>
|
||||
</SidePanelItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, onBeforeUnmount } from 'vue';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
// import { wbsbGetKendoList } from '@/api/sz';
|
||||
import * as echarts from 'echarts';
|
||||
import dayjs from 'dayjs';
|
||||
// TODO: 导入生态流量 API
|
||||
import { qgcHourVGetKendoListCust, wbsbGetKendoList } from '@/api/stll';
|
||||
import { useModelStore } from '@/store/modules/model';
|
||||
// 定义组件名
|
||||
defineOptions({
|
||||
name: 'StllyanchengChangeMod'
|
||||
});
|
||||
|
||||
// ==================== 响应式状态 ====================
|
||||
|
||||
// 图标提示信息
|
||||
const iconmap = ref({
|
||||
show: true,
|
||||
value:
|
||||
'入库流量是基于库容曲线和水量平衡的推算值,非实测数据。出库流量是基于机组出力特征曲线和闸门开度的推算值,非实测数据。',
|
||||
icon: 'iconfont icon-time'
|
||||
});
|
||||
|
||||
// 日期时间选择器配置
|
||||
const datetimePicker = ref({
|
||||
show: true,
|
||||
value: dayjs().format('YYYY-MM-DD HH:mm'), // 默认当前时间
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
picker: 'date',
|
||||
timeFormat: 'HH:mm',
|
||||
options: []
|
||||
});
|
||||
|
||||
// 河段选择器配置
|
||||
const select = ref({
|
||||
show: true,
|
||||
value: '',
|
||||
options: [],
|
||||
picker: undefined,
|
||||
format: undefined
|
||||
});
|
||||
const modelStore = useModelStore();
|
||||
// 业务数据状态
|
||||
const wbsCode = ref(''); // 流域编码
|
||||
const selectData = ref(''); // 选中的河段编码
|
||||
const chartData = ref<any[]>([]); // 图表数据
|
||||
const dataLoading = ref(false); // 加载状态
|
||||
const latestTime = ref(''); // 最新数据时间
|
||||
const hasTimeData = ref(false); // 是否有时间数据
|
||||
|
||||
// 图表相关
|
||||
const chartRef = ref<HTMLElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// 弹窗状态
|
||||
const modalVisible = ref(false);
|
||||
const modalData = ref<any>({
|
||||
stcd: '',
|
||||
stnm: '',
|
||||
titleName: ''
|
||||
});
|
||||
|
||||
// Store
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
|
||||
// ==================== 核心方法 ====================
|
||||
|
||||
/**
|
||||
* 获取河段列表
|
||||
*/
|
||||
const getSelect = async () => {
|
||||
if (!wbsCode.value) {
|
||||
console.warn('wbsCode 为空,无法获取河段数据');
|
||||
return;
|
||||
}
|
||||
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'objId',
|
||||
operator: 'eq',
|
||||
value: wbsCode.value
|
||||
},
|
||||
{
|
||||
field: 'wbsType',
|
||||
operator: 'eq',
|
||||
value: 'PSB_RVCD'
|
||||
}
|
||||
]
|
||||
},
|
||||
group: [
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'orderIndex'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'wbsCode'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'wbsName'
|
||||
},
|
||||
{
|
||||
dir: 'asc',
|
||||
field: 'objid'
|
||||
}
|
||||
],
|
||||
groupResultFlat: 'true'
|
||||
};
|
||||
|
||||
try {
|
||||
let res = await wbsbGetKendoList(params);
|
||||
|
||||
// 处理返回的数据
|
||||
let data;
|
||||
if (res && res.data && res.data.data) {
|
||||
if (Array.isArray(res.data.data)) {
|
||||
data = res.data.data;
|
||||
} else if (res.data.data.data && Array.isArray(res.data.data.data)) {
|
||||
data = res.data.data.data;
|
||||
} else {
|
||||
console.warn('无法解析河段数据:', res);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.warn('未获取到河段数据');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data && data.length > 0) {
|
||||
// ;
|
||||
select.value.value = data[0].wbsCode;
|
||||
selectData.value = data[0].wbsCode; // 同步选中第一个
|
||||
select.value.options = data.map(item => ({
|
||||
value: item.wbsCode,
|
||||
label: item.wbsName
|
||||
}));
|
||||
|
||||
// 获取完河段后立即加载图表数据
|
||||
await loadChartData();
|
||||
} else {
|
||||
console.warn('河段数据为空');
|
||||
select.value.value = '';
|
||||
select.value.options = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取河段数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取生态流量数据(预留 API 位置)
|
||||
*/
|
||||
const getList = async (rvcd: string, sDate: string) => {
|
||||
// TODO: 调用实际的 API
|
||||
// 参数结构参考 React 版本:
|
||||
// - baseId: 从 JidiSelectEventStore.selectedItem 获取
|
||||
// - rvcd: 河段编码
|
||||
// - tm: 时间范围(当前时间往前推1个月)
|
||||
|
||||
const currentTime = dayjs(sDate);
|
||||
const startTime = currentTime.subtract(1, 'month');
|
||||
let params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: wbsCode.value
|
||||
},
|
||||
{
|
||||
field: 'rvcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: rvcd
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: startTime.format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
{
|
||||
field: 'tm',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: currentTime.format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
]
|
||||
},
|
||||
sort: [
|
||||
{
|
||||
field: 'sort',
|
||||
dir: 'asc'
|
||||
}
|
||||
]
|
||||
};
|
||||
// TODO: 替换为实际 API 调用FAA
|
||||
const res = await qgcHourVGetKendoListCust(params);
|
||||
|
||||
// 处理返回的数据结构
|
||||
if (!res || !res.data || !res.data.data || !Array.isArray(res.data.data)) {
|
||||
console.warn('未获取到有效的生态流量数据:', res);
|
||||
return [];
|
||||
}
|
||||
|
||||
// 映射数据结构,适配图表展示需求
|
||||
return res.data.data.map((item: any) => ({
|
||||
TM: dayjs(item.tm).format('YYYY-MM-DD H时'),
|
||||
stnm: item.stnm,
|
||||
stcd: item.stcd,
|
||||
qec: item.qec, // 生态流量
|
||||
qecLimit: item.qecLimit, // 环评限值
|
||||
qi: item.qi // 入库流量
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* 加载图表数据
|
||||
*/
|
||||
const loadChartData = async () => {
|
||||
if (!selectData.value || !datetimePicker.value.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
dataLoading.value = true;
|
||||
|
||||
try {
|
||||
const data = await getList(selectData.value, datetimePicker.value.value);
|
||||
|
||||
if (data && data.length > 0) {
|
||||
chartData.value = data;
|
||||
hasTimeData.value = true;
|
||||
// 提取最新时间
|
||||
if (data[0]?.TM) {
|
||||
latestTime.value = data[0].TM;
|
||||
}
|
||||
|
||||
// 数据加载完成后渲染图表
|
||||
await nextTick();
|
||||
initChart();
|
||||
} else {
|
||||
chartData.value = [];
|
||||
hasTimeData.value = false;
|
||||
latestTime.value = '';
|
||||
// 清空图表
|
||||
if (chartInstance) {
|
||||
chartInstance.clear();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载图表数据失败:', error);
|
||||
chartData.value = [];
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化 ECharts 图表
|
||||
*/
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) {
|
||||
console.warn('图表容器未准备好');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果实例已存在,先销毁
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 准备数据
|
||||
const xData: string[] = [];
|
||||
const inData: (number | string)[] = []; // 入库流量
|
||||
const outData: (number | string)[] = []; // 生态流量
|
||||
const limitData: (number | string)[] = []; // 环评限值
|
||||
let hasValidData = false; // 标记是否有有效数据
|
||||
|
||||
chartData.value.forEach(item => {
|
||||
xData.push(item.stnm);
|
||||
const qecValue = item.qec ?? '-';
|
||||
const qiValue = item.qi ?? '-';
|
||||
const qecLimitValue = item.qecLimit ?? '-';
|
||||
|
||||
outData.push(qecValue);
|
||||
inData.push(qiValue);
|
||||
limitData.push(qecLimitValue);
|
||||
|
||||
// 检查是否有任意一个字段有有效数据
|
||||
if (qecValue !== '-' || qiValue !== '-' || qecLimitValue !== '-') {
|
||||
hasValidData = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果所有数据都为空,显示暂无数据
|
||||
if (!hasValidData && chartData.value.length > 0) {
|
||||
chartData.value = [];
|
||||
if (chartInstance) {
|
||||
chartInstance.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 图表配置
|
||||
const option: echarts.EChartsOption = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
confine: true,
|
||||
formatter: function (params: any) {
|
||||
const datum = chartData.value.find(
|
||||
(el: any) => el.stnm === params[0].name
|
||||
);
|
||||
const unit = 'm³/s'; // TODO: 根据单位配置动态获取
|
||||
|
||||
const content = params
|
||||
.map((item: any) => {
|
||||
const value = item.value ?? '-';
|
||||
return `<div>${item.marker}${item.seriesName}: <span style="float:right">${value} ${unit}</span></div>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return `<h3 style="color:white">${params[0].name}<br/>${
|
||||
datum?.TM || ''
|
||||
}</h3>${content}`;
|
||||
},
|
||||
axisPointer: {
|
||||
type: 'line'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['入库流量', '生态流量', '环评要求限值'],
|
||||
icon: 'roundRect',
|
||||
top: 10
|
||||
},
|
||||
grid: {
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
top: 70,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#505050'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#8f8f8f',
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
interval: xData.length > 12 ? 2 : 0,
|
||||
rotate: 0,
|
||||
formatter: (value: string, index: number) => {
|
||||
const shortValue =
|
||||
value.substring(0, 6) + (value.length > 6 ? '...' : '');
|
||||
if (index % 2 !== 0) {
|
||||
return `{a|${shortValue}}`;
|
||||
} else {
|
||||
return `\n{b|}${shortValue}`;
|
||||
}
|
||||
},
|
||||
rich: {
|
||||
a: {
|
||||
height: 20,
|
||||
align: 'center'
|
||||
},
|
||||
b: {
|
||||
height: 30,
|
||||
align: 'center'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '流量(m³/s)', // TODO: 根据单位配置动态获取
|
||||
minInterval: 1,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#505050'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#8f8f8f',
|
||||
type: 'solid'
|
||||
}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
xAxisIndex: 0,
|
||||
start: 0,
|
||||
end: 100,
|
||||
zoomOnMouseWheel: true,
|
||||
moveOnMouseMove: true
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '入库流量',
|
||||
data: inData,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
connectNulls: true,
|
||||
color: '#5470c6' // TODO: 根据配置动态获取
|
||||
},
|
||||
{
|
||||
name: '生态流量',
|
||||
data: outData,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
connectNulls: true,
|
||||
color: '#91cc75' // TODO: 根据配置动态获取
|
||||
},
|
||||
{
|
||||
name: '环评要求限值',
|
||||
data: limitData,
|
||||
type: 'bar',
|
||||
barMaxWidth: 10,
|
||||
color: '#fac858' // TODO: 根据配置动态获取
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 绑定点击事件
|
||||
chartInstance.on('click', handleChartClick);
|
||||
|
||||
// 自适应窗口大小
|
||||
window.addEventListener('resize', handleResize);
|
||||
};
|
||||
|
||||
/**
|
||||
* 图表点击事件
|
||||
*/
|
||||
const handleChartClick = (params: any) => {
|
||||
const datum = chartData.value.find((item: any) => item.stnm === params.name);
|
||||
if (datum) {
|
||||
const currentTime = dayjs(datetimePicker.value.value);
|
||||
const startTime = currentTime.subtract(1, 'month');
|
||||
modalVisible.value = true;
|
||||
modelStore.modalVisible = true;
|
||||
modelStore.params.sttp = 'ENGJJ';
|
||||
modelStore.title = datum.stnm;
|
||||
modelStore.params.stcd = datum.stcd;
|
||||
modelStore.params.eqtp = 'QEC';
|
||||
modelStore.filter.rangeTm = [startTime, currentTime];
|
||||
modelStore.filter.stllgzlx = 'qecInterval';
|
||||
modelStore.filter.stllTmType = 'day';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 窗口大小改变时重新调整图表
|
||||
*/
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 弹窗取消事件
|
||||
*/
|
||||
const handleModalCancel = () => {
|
||||
modalVisible.value = false;
|
||||
modalData.value = {
|
||||
stcd: '',
|
||||
stnm: '',
|
||||
titleName: ''
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 面板数据变化处理
|
||||
*/
|
||||
const handlePanelChange = async (data: any) => {
|
||||
console.log('当前所有控件状态:', data);
|
||||
|
||||
// 当选择器或日期变化时,重新加载图表数据
|
||||
if (data.select) {
|
||||
select.value.value = data.select;
|
||||
selectData.value = data.select;
|
||||
}
|
||||
|
||||
if (data.datetime) {
|
||||
datetimePicker.value.value = data.datetime;
|
||||
}
|
||||
|
||||
// 重新加载数据
|
||||
if (data.select || data.datetime) {
|
||||
await loadChartData();
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 生命周期与监听 ====================
|
||||
|
||||
// 监听流域切换
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
console.log('流域切换:', newVal);
|
||||
if (newVal?.wbsCode) {
|
||||
wbsCode.value = newVal.wbsCode;
|
||||
getSelect();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chart-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 424px;
|
||||
}
|
||||
|
||||
.chart-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 424px;
|
||||
flex-direction: column;
|
||||
/* align-content: center; */
|
||||
justify-content: center;
|
||||
text-align: center !important;
|
||||
:deep(.ant-empty-description) {
|
||||
text-align: center !important;
|
||||
text-indent: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 406px;
|
||||
height: 424px;
|
||||
}
|
||||
|
||||
.time-tip {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
color: #8e8e8e;
|
||||
padding: 8px 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
1737
frontend/src/modules/yunXingGaoJIng/Aisbdbyx/AISBTK.vue
Normal file
@ -1,8 +1,14 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<SidePanelItem title="AI识别大坝环境" :datetimePicker="datetimePicker">
|
||||
<SidePanelItem title="AI识别大坝环境" :datetimePicker="datetimePicker" @update-values="handlePanelChange">
|
||||
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||
<div class="facility-grid">
|
||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
||||
<div
|
||||
v-for="(facility, idx) in facilities"
|
||||
:key="facility.name"
|
||||
class="facility-card"
|
||||
@click="handleCardClick(String(idx + 1))"
|
||||
>
|
||||
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
||||
<div class="facility-icon">
|
||||
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
|
||||
@ -17,49 +23,166 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</SidePanelItem>
|
||||
|
||||
<!-- AI识别大坝环境详情弹框 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="AI识别大坝环境"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
@cancel="handleModalClose"
|
||||
>
|
||||
<AISBTK
|
||||
v-if="modalVisible"
|
||||
:month="datetimePicker.value"
|
||||
:tab-idx="currentTabIdx"
|
||||
:baseid="baseid"
|
||||
/>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||
import AISBTK from './AISBTK.vue';
|
||||
import { getAisbdbyxList } from '@/api/zngj';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 定义组件名(便于调试和递归)
|
||||
defineOptions({
|
||||
name: 'zengzhizhanjiansheyunxing'
|
||||
name: 'aisbdbyx'
|
||||
});
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
const dataLoading = ref(false);
|
||||
|
||||
// ==================== 弹框状态 ====================
|
||||
const modalVisible = ref(false);
|
||||
const currentTabIdx = ref('1');
|
||||
|
||||
// ==================== 日期选择器配置 ====================
|
||||
const datetimePicker = ref({
|
||||
show: true,
|
||||
value: `${new Date().getFullYear() - 1}`, // 上一年
|
||||
value: dayjs().format('YYYY-MM'),
|
||||
format: 'YYYY-MM',
|
||||
picker: 'month' as const,
|
||||
options: []
|
||||
});
|
||||
// 设施数据
|
||||
|
||||
// ==================== 设施数据 ====================
|
||||
const facilities = ref([
|
||||
{
|
||||
name: '漂浮物识别',
|
||||
count: "34",
|
||||
count: 0,
|
||||
icon: 'icon iconfont icon-piaofuwu'
|
||||
},
|
||||
{
|
||||
name: '叠梁门运行识别',
|
||||
count: '0',
|
||||
count: 0,
|
||||
icon: 'icon iconfont icon-dieliangmen1'
|
||||
},
|
||||
|
||||
]);
|
||||
|
||||
// 页面加载时执行
|
||||
onMounted(() => {
|
||||
// 延迟初始化,确保容器已渲染
|
||||
// ==================== 事件处理 ====================
|
||||
const handlePanelChange = (data: any) => {
|
||||
console.log('当前所有控件状态:', data);
|
||||
if (data.datetime) {
|
||||
datetimePicker.value.value = data.datetime;
|
||||
}
|
||||
getCardData();
|
||||
};
|
||||
|
||||
});
|
||||
const handleCardClick = (tabIdx: string) => {
|
||||
currentTabIdx.value = tabIdx;
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
const handleModalClose = () => {
|
||||
modalVisible.value = false;
|
||||
};
|
||||
|
||||
});
|
||||
// ==================== 获取卡片统计数据 ====================
|
||||
const getCardData = async () => {
|
||||
if (!datetimePicker.value.value || !baseid.value) return;
|
||||
|
||||
dataLoading.value = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
baseid.value !== 'all' ? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: baseid.value
|
||||
} : null,
|
||||
{
|
||||
field: 'startTime',
|
||||
operator: 'gte',
|
||||
dataType: 'date',
|
||||
value: dayjs(datetimePicker.value.value).startOf('month').format('YYYY-MM-DD 00:00:00')
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
operator: 'lte',
|
||||
dataType: 'date',
|
||||
value: dayjs(datetimePicker.value.value).endOf('month').format('YYYY-MM-DD 23:59:59')
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
};
|
||||
|
||||
const res = await getAisbdbyxList(params);
|
||||
const data = res?.data ?? {};
|
||||
|
||||
facilities.value = [
|
||||
{
|
||||
name: '漂浮物识别',
|
||||
count: data?.pfwCnt ?? 0,
|
||||
icon: 'icon iconfont icon-piaofuwu'
|
||||
},
|
||||
{
|
||||
name: '叠梁门运行识别',
|
||||
count: data?.dlmCnt ?? 0,
|
||||
icon: 'icon iconfont icon-dieliangmen1'
|
||||
}
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
facilities.value = [
|
||||
{
|
||||
name: '漂浮物识别',
|
||||
count: 0,
|
||||
icon: 'icon iconfont icon-piaofuwu'
|
||||
},
|
||||
{
|
||||
name: '叠梁门运行识别',
|
||||
count: 0,
|
||||
icon: 'icon iconfont icon-dieliangmen1'
|
||||
}
|
||||
];
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 监听基地变化 ====================
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
async newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
getCardData();
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -118,4 +241,8 @@ onUnmounted(() => {
|
||||
color: #2f6b98;
|
||||
// font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
min-height: 72px;
|
||||
}
|
||||
</style>
|
||||
4
frontend/src/shims.d.ts
vendored
@ -1,2 +1,6 @@
|
||||
declare module 'jsencrypt'
|
||||
declare module 'sortablejs'
|
||||
declare module '*.svg' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted,nextTick } from 'vue';
|
||||
import { wbsbGetKendoList } from '@/api/select';
|
||||
export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
||||
const jidiData: any = ref([
|
||||
@ -12,7 +12,9 @@ export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
||||
const loading = ref(false);
|
||||
const selectedItem = ref(jidiData.value[0]);
|
||||
const updataJidiData = (index: any) => {
|
||||
console.log(index);
|
||||
if(jidiData.value.length == 1){
|
||||
return
|
||||
}
|
||||
selectedItem.value = jidiData.value[index];
|
||||
jidiData.value.forEach((item: any) => {
|
||||
item.selected = false;
|
||||
|
||||
@ -65,8 +65,17 @@ watch(
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => JidiSelectEventStore.jidiData,
|
||||
(newData) => {
|
||||
if (newData.length > 1) {
|
||||
JidiSelectEventStore.updataJidiData(11);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
onMounted(() => {
|
||||
// JidiSelectEventStore.updataJidiData(11);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -82,7 +91,7 @@ onMounted(() => {
|
||||
<FishVideo />
|
||||
</RightDrawer>
|
||||
</div>
|
||||
<div class="fixedP" ref="fixedPRef">
|
||||
<div class="fixedP" ref="fixedPRef" v-if="baseid == '07'">
|
||||
<div class="zhuai" @mousedown="onMouseDown"></div>
|
||||
<SurveyQingKuang />
|
||||
<FishChangeTu />
|
||||
@ -100,13 +109,14 @@ onMounted(() => {
|
||||
padding: 0px 16px;
|
||||
border: 2px solid rgb(197, 214, 226);
|
||||
box-shadow: rgb(229, 237, 243) 0px 0px 3px 6px;
|
||||
background-color: rgba(255, 255, 255, 0.411);
|
||||
.zhuai {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
cursor: grab;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
// background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.zhuai:active {
|
||||
cursor: grabbing;
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
|
||||
import RightDrawer from "@/components/RightDrawer/index.vue";
|
||||
import SSSTJCQKTT from "@/modules/shuishengshengtaijiance/index.vue"
|
||||
import SSSTDCQK from "@/modules/shuiShengShengTaiDiaoCha/index.vue" // 水生生态监测情况模块
|
||||
// import SSSTJCQKTT from "@/modules/shuishengshengtaijiance"
|
||||
import AlongFishMod from "@/modules/alongFishMod/index.vue" //沿程鱼类变化情况
|
||||
import { ref, computed } from 'vue';
|
||||
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
|
||||
import RightDrawer from '@/components/RightDrawer/index.vue';
|
||||
import SSSTJCQKTT from '@/modules/shuishengshengtaijiance/index.vue';
|
||||
import SSSTDCQK from '@/modules/shuiShengShengTaiDiaoCha/index.vue'; // 水生生态监测情况模块
|
||||
import AlongFishMod from '@/modules/alongFishMod/index.vue'; //沿程鱼类变化情况
|
||||
import SSSTDCDB from './shuiShengShengTaiDiaoCha/SSSTDCDB.vue';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const modalVisible = ref(false);
|
||||
|
||||
const currentItem = computed(() => JidiSelectEventStore.selectedItem);
|
||||
|
||||
const handleOpenModal = () => {
|
||||
modalVisible.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -20,6 +30,55 @@ import AlongFishMod from "@/modules/alongFishMod/index.vue" //沿程鱼类变
|
||||
<AlongFishMod />
|
||||
</RightDrawer>
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="center_bottom" @click="handleOpenModal">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>水生生态调查对比</span>
|
||||
</template>
|
||||
<i class="icon iconfont icon-contrast"></i>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 水生生态调查对比弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
title="水生生态调查对比"
|
||||
width="1536px"
|
||||
:footer="null"
|
||||
destroy-on-close
|
||||
>
|
||||
<SSSTDCDB v-if="modalVisible" :current-item="currentItem" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.center {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
z-index: 999;
|
||||
.center_bottom {
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
background: #005293;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
i {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
i:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
}
|
||||
.center_bottom:hover {
|
||||
border: 1px solid #40a9ff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -0,0 +1,441 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick
|
||||
} from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import type { ECharts } from 'echarts';
|
||||
import { getFishtKendoList, getCompareBar } from '@/api/stdc';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
currentItem: any;
|
||||
}>();
|
||||
|
||||
// ==================== 搜索状态 ====================
|
||||
const weLis = ref<any[]>([]);
|
||||
const pcList = ref<any[]>([]);
|
||||
const searchParam = reactive({
|
||||
dcpc: '',
|
||||
dcpcCompare: '',
|
||||
stcd: ''
|
||||
});
|
||||
|
||||
// ==================== 图表状态 ====================
|
||||
const chartRef = ref<HTMLElement | null>(null);
|
||||
let chartInstance: ECharts | null = null;
|
||||
const chartList = ref<any[]>([]);
|
||||
const chartLoading = ref(true);
|
||||
|
||||
// ==================== 表格状态 ====================
|
||||
const tabLoading = ref(false);
|
||||
const tableRef = ref();
|
||||
const column = ref([
|
||||
{
|
||||
key: 'ftp',
|
||||
title: '鱼种类',
|
||||
dataIndex: 'ftp',
|
||||
fixed: 'left' as const,
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'genus',
|
||||
title: '科属',
|
||||
dataIndex: 'genus',
|
||||
fixed: 'left' as const,
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'fcnt',
|
||||
title: '第一批次',
|
||||
dataIndex: 'fcnt',
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'fcnt2',
|
||||
title: '对比批次',
|
||||
dataIndex: 'fcnt2',
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
}
|
||||
]);
|
||||
|
||||
// ==================== 构建表格过滤条件 ====================
|
||||
const buildTableFilter = () => {
|
||||
const filters: any[] = [];
|
||||
if (searchParam.stcd) {
|
||||
filters.push({ field: 'stcd', operator: 'eq', value: searchParam.stcd });
|
||||
}
|
||||
if (searchParam.dcpc) {
|
||||
filters.push({ field: 'dcpc', operator: 'eq', value: searchParam.dcpc });
|
||||
}
|
||||
if (searchParam.dcpcCompare) {
|
||||
filters.push({
|
||||
field: 'dcpc2',
|
||||
operator: 'eq',
|
||||
value: searchParam.dcpcCompare
|
||||
});
|
||||
}
|
||||
|
||||
// 动态更新列标题
|
||||
column.value[2].title = searchParam.dcpc || '第一批次';
|
||||
column.value[3].title = searchParam.dcpcCompare || '对比批次';
|
||||
|
||||
return {
|
||||
logic: 'and',
|
||||
filters
|
||||
};
|
||||
};
|
||||
|
||||
// ==================== 获取断面和批次列表 ====================
|
||||
const fetchPcData = async () => {
|
||||
const baseid = props.currentItem?.wbsCode || props.currentItem?.baseid;
|
||||
const params = {
|
||||
filter: {
|
||||
filters:
|
||||
baseid === 'all'
|
||||
? []
|
||||
: [
|
||||
{
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
value: baseid
|
||||
}
|
||||
]
|
||||
},
|
||||
group: [
|
||||
{ dir: 'asc', field: 'stcd' },
|
||||
{ dir: 'asc', field: 'stnm' },
|
||||
{ dir: 'asc', field: 'dcpc' }
|
||||
]
|
||||
};
|
||||
|
||||
const res = await getFishtKendoList(params);
|
||||
if (res?.data?.data?.length) {
|
||||
const options: any[] = [];
|
||||
res.data.data.forEach((el: any) => {
|
||||
if (el?.items?.[0]?.items?.length > 1) {
|
||||
options.push({
|
||||
label: el?.items?.[0]?.key,
|
||||
value: el?.key,
|
||||
dcpc: el?.items?.[0]?.items?.map((item: any) => ({
|
||||
label: item.key,
|
||||
value: item.key
|
||||
}))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
weLis.value = options;
|
||||
pcList.value = options?.[0]?.dcpc ?? [];
|
||||
|
||||
searchParam.stcd = options[0]?.value || '';
|
||||
searchParam.dcpc = options[0]?.dcpc?.[0]?.value || '';
|
||||
searchParam.dcpcCompare = options[0]?.dcpc?.[1]?.value || '';
|
||||
|
||||
tabLoading.value = true;
|
||||
} else {
|
||||
weLis.value = [];
|
||||
pcList.value = [];
|
||||
tabLoading.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 断面切换 ====================
|
||||
const handleStcdChange = (stcd: string) => {
|
||||
if (!stcd) return;
|
||||
const found = weLis.value.find((el: any) => el.value === stcd);
|
||||
if (found) {
|
||||
pcList.value = found.dcpc;
|
||||
searchParam.stcd = found.value;
|
||||
searchParam.dcpc = found.dcpc?.[0]?.value || '';
|
||||
searchParam.dcpcCompare = found.dcpc?.[1]?.value || '';
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 获取图表数据 ====================
|
||||
const fetchChartData = async () => {
|
||||
chartLoading.value = true;
|
||||
try {
|
||||
const params = {
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'stcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchParam.stcd
|
||||
},
|
||||
{
|
||||
field: 'dcpc',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchParam.dcpc
|
||||
},
|
||||
{
|
||||
field: 'dcpc2',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: searchParam.dcpcCompare
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res: any = await getCompareBar(params);
|
||||
// debugger
|
||||
if (res?.data?.data?.[0]?.dataSource) {
|
||||
chartList.value = res.data.data[0].dataSource;
|
||||
} else {
|
||||
chartList.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取图表数据失败:', error);
|
||||
chartList.value = [];
|
||||
} finally {
|
||||
chartLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 初始化图表 ====================
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) return;
|
||||
|
||||
if (!chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
if (!chartList.value || chartList.value.length < 1) {
|
||||
chartInstance.setOption({
|
||||
title: { text: '', subtext: '' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: { type: 'category', data: [] },
|
||||
yAxis: { type: 'value' },
|
||||
series: []
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const option = {
|
||||
title: { text: '', subtext: '' },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' }
|
||||
},
|
||||
legend: {
|
||||
data: [searchParam.dcpc, searchParam.dcpcCompare],
|
||||
left: 'center',
|
||||
show: true,
|
||||
itemWidth: 12,
|
||||
itemHeight: 8,
|
||||
textStyle: { fontSize: 14 }
|
||||
},
|
||||
grid: {
|
||||
top: '20%',
|
||||
left: '5%',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: { rotate: 45 },
|
||||
axisTick: { show: false },
|
||||
data: chartList.value.map((e: any) => e.ftp)
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '数量(尾)'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: searchParam.dcpc,
|
||||
type: 'bar',
|
||||
barWidth: 5,
|
||||
stack: 'dcpc',
|
||||
data: chartList.value.map((e: any) => e.fcnt)
|
||||
},
|
||||
{
|
||||
name: searchParam.dcpcCompare,
|
||||
type: 'bar',
|
||||
barWidth: 5,
|
||||
stack: 'dcpcCompare',
|
||||
data: chartList.value.map((e: any) => e.fcnt2)
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option, true);
|
||||
};
|
||||
|
||||
// ==================== 表格数据转换 ====================
|
||||
const customTransform = (res: any) => {
|
||||
return {
|
||||
records: res?.data?.data?.[0].dataSource || [],
|
||||
total: res?.data?.total || 0
|
||||
};
|
||||
};
|
||||
|
||||
// ==================== 刷新表格 ====================
|
||||
const refreshTable = () => {
|
||||
const filter = buildTableFilter();
|
||||
tableRef.value?.getList(filter);
|
||||
};
|
||||
|
||||
// ==================== 查询按钮 ====================
|
||||
const handleSearch = () => {
|
||||
fetchChartData();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
refreshTable();
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 监听批次变化自动刷新图表 ====================
|
||||
watch(
|
||||
() => [searchParam.dcpc, searchParam.dcpcCompare],
|
||||
() => {
|
||||
fetchChartData();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// ==================== 监听图表数据变化 ====================
|
||||
watch(
|
||||
() => chartList.value,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// ==================== 窗口resize ====================
|
||||
const handleResize = () => {
|
||||
chartInstance?.resize();
|
||||
};
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
onMounted(async () => {
|
||||
await fetchPcData();
|
||||
await nextTick();
|
||||
fetchChartData();
|
||||
await nextTick();
|
||||
initChart();
|
||||
await nextTick();
|
||||
refreshTable();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
chartInstance?.dispose();
|
||||
chartInstance = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ssstdcdb-page">
|
||||
<!-- 搜索表单 -->
|
||||
<div class="search-form">
|
||||
<a-form-item label="调查断面">
|
||||
<a-select
|
||||
v-model:value="searchParam.stcd"
|
||||
placeholder="请选择调查断面"
|
||||
style="width: 200px; margin-right: 12px"
|
||||
:options="weLis"
|
||||
@change="handleStcdChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="调查批次">
|
||||
<a-select
|
||||
v-model:value="searchParam.dcpc"
|
||||
placeholder="请选择调查批次"
|
||||
style="width: 200px; margin-right: 12px"
|
||||
:options="pcList"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="对比批次">
|
||||
<a-select
|
||||
v-model:value="searchParam.dcpcCompare"
|
||||
placeholder="请选择对比批次"
|
||||
style="width: 200px; margin-right: 12px"
|
||||
:options="pcList"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 图表 + 表格 -->
|
||||
<div class="content-area">
|
||||
<!-- 图表 -->
|
||||
<div class="chart-section">
|
||||
<a-spin :spinning="chartLoading">
|
||||
<div ref="chartRef" class="chart-container" style="height: 500px"></div>
|
||||
<a-empty
|
||||
v-if="!chartLoading && chartList.length === 0"
|
||||
class="chart-empty"
|
||||
description="暂无数据"
|
||||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:columns="column"
|
||||
:scroll-y="400"
|
||||
:scroll-x="300"
|
||||
:list-url="tabLoading ? getCompareBar : undefined"
|
||||
:search-params="{ sort: [] }"
|
||||
:transform-data="customTransform"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ssstdcdb-page {
|
||||
// padding: 16px;
|
||||
|
||||
.search-form {
|
||||
padding: 16px 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.chart-section {
|
||||
width: 40%;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chart-empty {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
width: 60%;
|
||||
height: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
|
||||
import RightDrawer from '@/components/RightDrawer/index.vue';
|
||||
import ShengtaidabiaoMod from '@/modules/shengtaidabiaoModJZ/index.vue'; // 模块-生态流量达标情况 生态流量达标情况
|
||||
import ShengtaidabiaoTwoMod from '@/modules/shengtaidabiaoTwoModJJ/index.vue';
|
||||
import StllyanchengChangeMod from '@/modules/stllyanchengChangeModJJ/index.vue' // 模块-生态流量沿程变化
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const baseid = ref('');
|
||||
watch(
|
||||
() => JidiSelectEventStore.selectedItem,
|
||||
newVal => {
|
||||
if (newVal && newVal.wbsCode) {
|
||||
baseid.value = newVal.wbsCode;
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="moduleContent">
|
||||
<div class="leftContent">
|
||||
<JidiSelectorMod />
|
||||
</div>
|
||||
<div class="rightContent">
|
||||
<RightDrawer>
|
||||
<ShengtaidabiaoMod />
|
||||
<ShengtaidabiaoTwoMod v-if="baseid == 'all'" />
|
||||
<StllyanchengChangeMod v-if="baseid != 'all'" />
|
||||
</RightDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss"></style>
|
||||