智慧装备添加锚点,右侧一级二级展示,以及点击锚点弹框

This commit is contained in:
扈兆增 2026-09-09 14:10:59 +08:00
parent 77ef3f72aa
commit f51843a2f9
33 changed files with 3222 additions and 506 deletions

View File

@ -549,3 +549,28 @@ export function getFlowDischargeList(code: string, data: any) {
data data
}); });
} }
// 智慧装备 - 动物 鸟类
export function getAnimalList(data: any) {
return request({
url: '/wte/anml/run/GetKendoListCust',
method: 'post',
data
});
}
// 智慧装备 - 钓鱼、违章监测、漂浮物识别装置、叠梁门运行识别装置
export function getWarningList(data: any) {
return request({
url: '/warn/ai/com/GetKendoListCust',
method: 'post',
data
});
}
// 智慧装备 - 鱼类调查装置 鱼类智能识别装置 智能分拣装置
export function getFishList(data: any) {
return request({
url: '/wte/fp/ai/GetKendoListCust',
method: 'post',
data
});
}

View File

@ -69,7 +69,15 @@ export function stbprpGetStbprpYsByStcd(data: any) {
return request({ return request({
url: '/wq/stbprp/GetStbprpYsByStcd', url: '/wq/stbprp/GetStbprpYsByStcd',
method: 'get', method: 'get',
params:data params: data
});
}
//水质监测工作开展情况 - wte接口
export function vmsstbprptGetKendoListWte(data: any) {
return request({
url: '/wte/vmsstbprpt/GetKendoList',
method: 'post',
data
}); });
} }
//表格数据获取函数 //表格数据获取函数
@ -86,4 +94,12 @@ export function NodataGetKendoListCust(data: any) {
method: 'post', method: 'post',
data data
}); });
} }
// 各基地社保统计
export function imeBaseDeviceStat(data: any) {
return request({
url: '/ime/base/device/stat',
method: 'post',
data
});
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_9_">
<g id="map-qixidi_9_">
<path id="路径_20_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
C10.3,23,10,23,9.7,22.9c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10.1C0,4.5,4.5,0,10,0z"/>
<path id="路径_19_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L7.9,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2L12,19l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<path class="st2" d="M14.3,11H5.9v1.9h3l0.1,0l1.2,0.5l1.1-0.5l0.1,0h3V11z M14.3,8H5.9v1.5h3l0.1,0l1.2,0.5l1.1-0.5l0.1,0h3
C14.3,9.5,14.3,8,14.3,8z M14.3,5.5c0-0.4,0.3-0.8,0.7-0.8c0.1,0,0.2,0,0.3,0.1c0.1,0,0.1,0.1,0.1,0.2v10.5c0,0.1,0,0.1-0.1,0.2
c-0.1,0-0.2,0.1-0.3,0.1c-0.4,0-0.7-0.3-0.7-0.8v-0.6H5.9V15c0,0.4-0.3,0.8-0.7,0.8c-0.1,0-0.2,0-0.3-0.1c-0.1,0-0.1-0.1-0.1-0.2
V5c0-0.1,0-0.1,0.1-0.2c0.1,0,0.2-0.1,0.3-0.1c0.4,0,0.7,0.3,0.7,0.8v0.9h3l0.1,0L10.1,7l1.1-0.6l0.1,0h3V5.5L14.3,5.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_12_">
<g id="map-qixidi_12_">
<path id="路径_26_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
C10.3,23,10,23,9.7,22.9c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10.1C0,4.5,4.5,0,10,0z"/>
<path id="路径_25_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<g>
<path class="st2" d="M7.8,10.6c0-0.5-0.4-0.9-0.7-1.2C7,9.3,6.9,9.1,6.8,9C6.7,8.8,6.5,8.6,6.4,8.6c0-0.2-0.1-0.4-0.2-0.7
C6,8.1,5.9,8.2,5.9,8.4h0c0,0.1,0,0.2,0,0.3c-0.4,0.2-0.5,0.9-0.5,1.6c0,0.9,0.7,1.5,1.2,1.5C7.3,11.8,7.8,11.3,7.8,10.6z
M6.1,10.3c0-0.5,0-0.8,0.1-0.9l0,0c0.2,0.2,0.3,0.4,0.4,0.5c0.3,0.3,0.5,0.5,0.5,0.7c0,0.2,0,0.3-0.1,0.4
c-0.1,0.1-0.2,0.1-0.4,0.1c-0.1,0-0.2-0.1-0.3-0.2C6.2,10.8,6.1,10.6,6.1,10.3z"/>
<path class="st2" d="M8.6,9.6c0.7,0,1.2-0.5,1.2-1.2c0-0.7-0.3-1.5-0.5-1.9C9.2,6.2,9.1,6.1,8.9,6c0,0,0,0,0,0
c0-0.2-0.1-0.4-0.2-0.7C8.5,5.4,8.4,5.6,8.4,5.7c0,0.1,0,0.2,0,0.2c-0.7,0.2-1,1.2-1,1.9C7.4,8.8,7.8,9.6,8.6,9.6z M8.3,7
c0.1-0.2,0.2-0.3,0.2-0.3c0.1,0,0.1,0,0.1,0c0,0,0,0,0,0.1C8.8,7,8.9,7.2,9,7.5C9,7.8,9.1,8.1,9.1,8.4c0,0.2,0,0.3-0.1,0.4
C8.9,8.8,8.7,8.9,8.6,8.9c-0.1,0-0.2-0.1-0.3-0.2C8.2,8.5,8.1,8.2,8.1,7.9C8.1,7.6,8.2,7.3,8.3,7z"/>
<path class="st2" d="M11.8,9.6c0.7,0,1.2-1,1.2-1.7c0-0.7-0.5-1.9-1.2-1.9c0-0.1,0-0.2,0-0.2c0-0.2-0.1-0.3-0.2-0.5
c-0.1,0.3-0.2,0.6-0.2,0.8c-0.2,0.1-0.4,0.3-0.5,0.6c-0.2,0.4-0.5,1-0.5,1.7C10.3,9.1,11.1,9.6,11.8,9.6z M11.2,7.7
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.2,0.2-0.3,0.2-0.3c0,0,0,0,0.1,0c0,0,0,0,0,0C11.9,6.8,12,6.9,12.1,7c0.2,0.3,0.3,0.6,0.3,0.9
c0,0.3-0.1,0.5-0.2,0.7c-0.1,0.2-0.2,0.2-0.3,0.2c-0.2,0-0.4-0.1-0.6-0.2c-0.1-0.1-0.2-0.2-0.2-0.3C11.1,8.2,11.1,7.9,11.2,7.7z"
/>
<path class="st2" d="M14.4,8.7c0-0.1,0-0.2,0-0.3h0c0-0.2-0.1-0.3-0.2-0.5c-0.1,0.3-0.2,0.5-0.2,0.8c-0.1,0.1-0.3,0.2-0.5,0.4
c-0.2,0.2-1,1-0.7,1.7c0.2,0.7,0.7,1,1.2,1c0.5,0,1.2-1,1.2-1.7C15.1,9.5,14.7,8.9,14.4,8.7z M14,10.8C14,10.9,13.9,11,13.8,11
l0,0c-0.2,0-0.4-0.2-0.5-0.5c-0.1-0.2,0.1-0.5,0.6-1h0C14,9.5,14,9.5,14.1,9.4c0,0,0,0,0.1,0.1c0.1,0.2,0.2,0.4,0.2,0.6
C14.3,10.3,14.2,10.5,14,10.8z"/>
<path class="st2" d="M13.1,11.9c-0.1-0.1-0.2-0.1-0.2-0.2c-0.7-1.2-1.2-2.2-2.7-2.2c-1,0-1.5,0.5-1.7,1c-0.2,0.3-0.5,1-1.5,1.7
c-1.2,1.2-0.5,2.4,0.5,2.7c1.3,0.2,1.9-0.1,2.3-0.3c0.2-0.1,0.4-0.2,0.7-0.2c0.2,0,0.5,0.1,0.8,0.2c0.4,0.1,0.7,0.3,1.1,0.3h0
c0.6,0,1.4,0,1.8-1C14.5,13,13.5,12.3,13.1,11.9z M13.2,13.6c-0.1,0.2-0.2,0.3-0.3,0.3c-0.1,0-0.3,0.1-0.6,0.1h0
c-0.1,0-0.4-0.1-0.8-0.2c-0.5-0.2-0.9-0.3-1.2-0.3c-0.3,0-0.7,0.1-1.1,0.3C9,13.9,8.8,13.9,8.6,14h0C8.3,14,8,14,7.5,14
c-0.2-0.1-0.3-0.2-0.3-0.3c-0.1-0.2,0-0.4,0.3-0.7c0.8-0.6,1.3-1.2,1.7-2v0c0.1-0.3,0.4-0.4,0.9-0.4c0.4,0,0.7,0.1,0.9,0.3
c0.2,0.2,0.5,0.6,0.9,1.2l0.1,0.2l0.1,0.1l0.1,0.1c0.1,0.1,0.2,0.2,0.3,0.3C13,13.1,13.2,13.4,13.2,13.6z"/>
<path class="st2" d="M5.6,7H4.7V5.6c0-0.4,0.3-0.7,0.7-0.7h1.4v0.8H5.7c-0.1,0-0.1,0.1-0.1,0.1V7z M15,4.9h-1.4v0.8h1.2
c0.1,0,0.1,0.1,0.1,0.1V7h0.8V5.6C15.7,5.2,15.4,4.9,15,4.9z M5.6,14.9v-1.2H4.7v1.4c0,0.4,0.3,0.7,0.7,0.7h1.4V15H5.7
C5.6,15,5.6,15,5.6,14.9z M14.9,14.9c0,0.1-0.1,0.1-0.1,0.1h-1.2v0.8H15c0.4,0,0.7-0.3,0.7-0.7v-1.4h-0.8V14.9z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_5_">
<g id="map-qixidi_5_">
<path id="路径_12_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
C10.3,23,10,23,9.8,22.9c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10C0,4.5,4.5,0,10,0z"/>
<path id="路径_11_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<g>
<path class="st2" d="M16.2,11.4H14c-0.3,0-0.7,0.2-0.9,0.4l-0.9,0.9l-0.5-0.5c-0.9-0.9-2-1.3-3.1-1.3c-1.1,0-2.3,0.4-3.1,1.3
l-1.2,1.2l1.2,1.2c0.9,0.9,2,1.3,3.1,1.3c1.2,0,2.3-0.5,3.1-1.3l0.5-0.5l0.9,0.9c0.2,0.2,0.6,0.4,0.9,0.4h2.3l-0.5-2L16.2,11.4z
M11,13.9c-0.6,0.6-1.5,1-2.4,1c-0.9,0-1.8-0.4-2.4-1l-0.5-0.5l0.5-0.5c0.6-0.6,1.5-1,2.4-1c0.9,0,1.8,0.4,2.4,1l0.5,0.5L11,13.9
L11,13.9z M15,14.4h-1c0,0-0.1,0-0.2-0.1l-0.9-0.9l0.9-0.9c0,0,0.1-0.1,0.2-0.1l1,0l-0.3,1L15,14.4z"/>
<path class="st2" d="M8,13.4c0,0.3-0.2,0.5-0.5,0.5c0,0,0,0,0,0c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0,0c0-0.3,0.2-0.5,0.5-0.5
c0,0,0,0,0,0C7.8,12.9,8,13.1,8,13.4C8,13.4,8,13.4,8,13.4z M7,8.6h0.8c0,0.4-0.3,0.7-0.8,0.7c-0.4,0-0.7-0.3-0.7-0.7V6.8
c0.6-0.2,1-0.8,1-1.4c0-0.8-0.7-1.5-1.5-1.5S4.2,4.6,4.2,5.4c0,0.7,0.4,1.2,1,1.4v1.8c0,1,0.8,1.8,1.7,1.8c1,0,1.8-0.8,1.8-1.7
V6.9L7,8.6z M5.7,5.9c-0.3,0-0.5-0.2-0.5-0.5c0-0.3,0.2-0.5,0.5-0.5c0,0,0,0,0,0c0.3,0,0.5,0.2,0.5,0.5C6.2,5.7,6,5.9,5.7,5.9z"
/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_4_">
<g id="map-qixidi_4_">
<path id="路径_10_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
c-0.1,0.2-0.5,0.3-0.7,0.2c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10C0,4.5,4.5,0,10,0z"/>
<path id="路径_9_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,18.9c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<g>
<path class="st2" d="M8.8,15.9c-0.1,0-0.2,0-0.2-0.1l-0.1-0.1c-0.1-0.1-0.1-0.3,0-0.5s0.3-0.1,0.5,0L9,15.3
c0.1,0.1,0.1,0.3,0,0.5C8.9,15.9,8.8,15.9,8.8,15.9z M7.8,15c-0.1,0-0.2,0-0.2-0.1l-0.2-0.2c-0.1-0.1-0.1-0.3,0-0.5
c0.1-0.1,0.3-0.1,0.5,0l0,0l0.2,0.2c0.1,0.1,0.1,0.3,0,0.5C8,14.9,7.9,15,7.8,15L7.8,15z M7,13.3L7,13.3c-0.2,0-0.3-0.2-0.3-0.3
c0-0.1,0-0.1,0-0.2c0-0.2,0.2-0.3,0.4-0.3c0.2,0,0.3,0.2,0.3,0.4c0,0,0,0.1,0,0.1C7.3,13.2,7.2,13.3,7,13.3L7,13.3z M7.5,12.1
c-0.2,0-0.3-0.1-0.3-0.3c0-0.1,0-0.1,0.1-0.2c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.3-0.1,0.5,0c0.1,0.1,0.1,0.3,0,0.4
c-0.1,0.1-0.1,0.1-0.2,0.2C7.7,12.1,7.6,12.1,7.5,12.1L7.5,12.1z M8.4,10.5L8.4,10.5c-0.2,0-0.3-0.2-0.3-0.3V10
c0-0.2,0.1-0.3,0.3-0.3c0.2,0,0.3,0.1,0.3,0.3l0,0v0.2C8.7,10.3,8.6,10.5,8.4,10.5L8.4,10.5z M8.1,9.2C8,9.2,7.9,9.2,7.8,9.1
C7.8,9,7.7,9,7.6,8.9c-0.1-0.1-0.1-0.3,0-0.5c0.1-0.1,0.3-0.1,0.5,0c0,0,0,0,0,0c0.1,0.1,0.2,0.2,0.2,0.3
c0.1,0.1,0.1,0.3-0.1,0.4C8.2,9.2,8.1,9.2,8.1,9.2z M7.1,7.6c-0.2,0-0.3-0.1-0.3-0.3c0-0.1,0-0.1,0-0.2C6.7,7,6.8,6.8,7,6.8
c0.2,0,0.3,0.1,0.4,0.3c0,0,0,0,0,0c0,0,0,0.1,0,0.1C7.4,7.4,7.3,7.6,7.1,7.6C7.1,7.6,7.1,7.6,7.1,7.6L7.1,7.6z M7.3,6.4
c-0.2,0-0.3-0.1-0.3-0.3c0-0.1,0-0.1,0-0.2C7,5.8,7.1,5.7,7.2,5.6c0.1-0.1,0.3-0.2,0.5-0.1C7.8,5.6,7.8,5.8,7.7,6
C7.6,6.1,7.6,6.1,7.5,6.2C7.5,6.3,7.4,6.4,7.3,6.4L7.3,6.4z M8.6,5C8.4,5,8.3,4.9,8.3,4.7c0-0.1,0-0.2,0.1-0.2l0.1-0.1
C8.7,4.2,8.9,4.3,9,4.4c0.1,0.1,0.1,0.3,0,0.4L8.8,4.9C8.8,5,8.7,5,8.6,5L8.6,5z M13.2,16.1c-0.2,0-0.3-0.1-0.3-0.3c0,0,0,0,0,0
c0-0.2,0.1-0.3,0.3-0.3c0.1,0,0.2,0,0.2,0.1c0.1,0.1,0.1,0.3,0,0.5C13.3,16.1,13.2,16.1,13.2,16.1z M12.3,15.3
c-0.1,0-0.2,0-0.2-0.1l-0.6-0.6c0,0-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.3,0-0.5c0,0,0,0,0,0c0.1-0.1,0.3-0.1,0.5,0
c0,0,0.1,0.1,0.1,0.1l0.6,0.6c0.1,0.1,0.1,0.3,0,0.5C12.5,15.3,12.4,15.3,12.3,15.3L12.3,15.3z M11.2,13.4
c-0.2,0-0.3-0.1-0.3-0.3c0-0.2,0.1-0.3,0.3-0.3c0.2,0,0.3,0.1,0.3,0.3C11.5,13.3,11.4,13.4,11.2,13.4L11.2,13.4L11.2,13.4z
M11.5,12.4c-0.1,0-0.1,0-0.2,0c-0.2-0.1-0.2-0.3-0.1-0.4c0.1-0.2,0.3-0.4,0.5-0.6c0.1-0.1,0.1-0.1,0.2-0.2
c0.1-0.1,0.3-0.1,0.5,0c0.1,0.1,0.1,0.3,0,0.5c-0.1,0.1-0.2,0.2-0.2,0.2c-0.1,0.1-0.3,0.3-0.4,0.4C11.7,12.3,11.6,12.4,11.5,12.4
z M12.6,10.5L12.6,10.5c-0.1,0-0.2,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2c0-0.2,0.2-0.3,0.3-0.3c0.2,0,0.3,0.2,0.3,0.3
C12.9,10.3,12.7,10.5,12.6,10.5z M12.3,9.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.2-0.3-0.3-0.4l-0.2-0.2l-0.1-0.1
c-0.1-0.1-0.1-0.3,0-0.5c0.1-0.1,0.3-0.1,0.5,0L12,8.2l0.2,0.2c0.2,0.2,0.3,0.3,0.4,0.5c0.1,0.2,0,0.4-0.1,0.4c0,0,0,0,0,0
C12.4,9.4,12.4,9.4,12.3,9.4L12.3,9.4z M11.2,7.5c-0.2,0-0.3-0.1-0.3-0.3c0-0.1,0-0.2,0.1-0.2c0.1-0.1,0.1-0.1,0.2-0.1
c0.2,0,0.3,0.1,0.4,0.3C11.5,7.3,11.4,7.5,11.2,7.5C11.2,7.5,11.2,7.5,11.2,7.5L11.2,7.5L11.2,7.5z M11.4,6.4c-0.1,0-0.1,0-0.2,0
c-0.2-0.1-0.2-0.3-0.1-0.4c0.1-0.3,0.3-0.5,0.5-0.7l0.2-0.2C12,5,12.2,5,12.3,5.1c0.1,0.1,0.1,0.3,0,0.5l-0.2,0.2
c-0.2,0.1-0.3,0.3-0.4,0.5C11.7,6.3,11.5,6.4,11.4,6.4L11.4,6.4z M13,4.8c-0.1,0-0.2,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2
c0-0.1,0-0.2,0.1-0.2c0.1-0.1,0.3-0.1,0.5,0c0.1,0.1,0.1,0.3,0,0.5C13.2,4.8,13.1,4.8,13,4.8L13,4.8z"/>
<path class="st2" d="M13.9,14.1c-0.6-0.6-0.6-1.5,0-2.1c0,0,0.1-0.1,0.1-0.1c1-0.9,1.2-2.5,0.3-3.5c0-0.1-0.1-0.1-0.1-0.2
l-0.2-0.2c-0.6-0.6-0.6-1.5,0-2.1c0,0,0.1-0.1,0.1-0.1l1.9-1.7h-1.5l-1.1,0.9c-1,0.9-1.1,2.5-0.2,3.5c0,0,0.1,0.1,0.1,0.1
l0.2,0.2c0.6,0.6,0.6,1.5,0,2.1c0,0-0.1,0.1-0.1,0.1c-1,0.9-1.2,2.5-0.3,3.5c0,0,0.1,0.1,0.1,0.1l1.4,1.4h1.4L13.9,14.1z M5.8,13
c0-0.4,0.2-0.8,0.5-1.1c1-0.9,1.1-2.5,0.3-3.5c0-0.1-0.1-0.1-0.1-0.1L6.2,8.1c-0.6-0.6-0.6-1.5,0-2.1c0,0,0.1-0.1,0.1-0.1
l1.9-1.7H6.8L5.7,5.1C4.6,6,4.5,7.6,5.4,8.6c0,0,0.1,0.1,0.1,0.1l0.2,0.2c0.6,0.6,0.6,1.5,0,2.1c0,0-0.1,0.1-0.1,0.1
c-1,0.9-1.2,2.5-0.3,3.5c0,0,0.1,0.1,0.1,0.1l1.4,1.4h1.4l-2.1-2.1C5.9,13.8,5.8,13.4,5.8,13z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_13_">
<g id="map-qixidi_13_">
<path id="路径_28_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
C10.3,23,10,23,9.7,22.9c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10.1C0,4.5,4.5,0,10,0z"/>
<path id="路径_27_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<path class="st2" d="M5.5,14.6h1.2v0.8H5.3c-0.4,0-0.7-0.3-0.7-0.7v-1.4h0.8v1.2C5.4,14.5,5.5,14.6,5.5,14.6z M5.4,5.4
c0-0.1,0.1-0.1,0.1-0.1h1.2V4.5H5.3c-0.4,0-0.7,0.3-0.7,0.7v1.4h0.8V5.4z M14.8,4.5h-1.4v0.8h1.2c0.1,0,0.1,0.1,0.1,0.1v1.2h0.8
V5.2C15.6,4.8,15.2,4.5,14.8,4.5z M14.7,14.5c0,0.1-0.1,0.1-0.1,0.1h-1.2v0.8h1.4c0.4,0,0.7-0.3,0.7-0.7v-1.4h-0.8V14.5z"/>
<path class="st2" d="M14.9,8.1c0,0.1-0.1,0.2-0.2,0.3l-0.9,0.5c-0.2,0.1-0.3,0-0.4-0.1c-0.1-0.2,0-0.3,0.1-0.4L14,8.1l-0.4-0.2
c-0.1,0-0.1-0.1-0.1-0.2c-0.2-0.5-0.5-0.7-1-0.8c-0.5,0-0.9,0.2-1.2,0.6l-0.2,0.3c0,0,0,0.1,0,0.1c0.8,2.1,0.1,3.3-0.8,3.8
c-0.3,0.1-0.6,0.2-0.9,0.2c-0.7,0-1.4-0.4-1.7-1.1c-0.1-0.2,0-0.3,0.1-0.4c0.2-0.1,0.3,0,0.4,0.1c0.4,0.7,1.2,0.8,1.7,0.6
c0.6-0.3,1.1-1.1,0.7-2.5c-0.3,0.4-0.7,0.7-1.3,1C9.1,9.7,9,9.7,8.9,9.8c-0.8,0.4-1.6,0.8-2.4-0.2L6.3,9.4
C6.2,9.3,5.8,8.8,5.6,8.7c0,0.1,0,0.2,0,0.4c0,0.8,0.9,1.6,1.6,1.8c0.1,0,0.2,0.1,0.2,0.3c0.3,1.6,1.7,2.3,3,2.1
c1.3-0.1,2.9-1.2,2.9-3.8c0-0.2,0.1-0.3,0.3-0.3c0.2,0,0.3,0.1,0.3,0.3c0,3-1.9,4.3-3.5,4.4c-0.1,0-0.2,0-0.3,0
c-0.2,0-0.4,0-0.6,0l-0.2,0.6h0.3c0.2,0,0.3,0.1,0.3,0.3c0,0.2-0.1,0.3-0.3,0.3H9c-0.1,0-0.2,0-0.3-0.1c-0.1-0.1-0.1-0.2-0.1-0.3
l0.3-1c-1-0.3-1.8-1.1-2.1-2.3c-0.9-0.3-1.9-1.3-1.9-2.3c0-0.4,0-0.9,0.4-1c0.5-0.2,1,0.5,1.5,1L7,9.1c0.5,0.6,0.9,0.5,1.7,0.1
C8.7,9.1,8.9,9.1,9,9c0.9-0.4,1.1-0.8,1.6-1.5l0.2-0.3c0.4-0.6,1.1-1,1.8-0.9c0.6,0.1,1.2,0.4,1.5,1.1l0.8,0.4
C14.9,7.9,14.9,8,14.9,8.1z M12.2,7.6c-0.1,0.1-0.1,0.2-0.1,0.3c0,0.1,0,0.2,0.1,0.3c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1
c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1-0.1-0.2-0.1-0.3C12.6,7.4,12.4,7.4,12.2,7.6z M11.3,14.5H11l0-0.1c0-0.2-0.1-0.3-0.2-0.4
c-0.2,0-0.3,0.1-0.4,0.2l-0.1,0.5c0,0.1,0,0.2,0.1,0.3c0.1,0.1,0.2,0.1,0.3,0.1h0.7c0.2,0,0.3-0.1,0.3-0.3
C11.6,14.6,11.5,14.5,11.3,14.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_10_">
<g id="map-qixidi_10_">
<path id="路径_22_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
c-0.1,0.2-0.5,0.3-0.7,0.2c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10C0,4.5,4.5,0,10,0z"/>
<path id="路径_21_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,18.9c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<path class="st2" d="M4.2,9.3c0-0.3,0.2-0.6,0.5-0.6h11.1c0.3,0,0.5,0.3,0.5,0.6c0,0.3-0.2,0.6-0.5,0.6H4.6
C4.4,9.9,4.2,9.7,4.2,9.3z M16.2,11.1C16.2,11.1,16.2,11.1,16.2,11.1c0-0.2-0.2-0.4-0.4-0.3c0,0-1,0.9-2.3,0.9
c-1,0-1.8-0.9-2.7-0.9c-1.4,0-2.3,0.9-3.2,0.9c-0.9,0-2.3-1.2-2.3-1.2c-0.2-0.2-0.3,0.1-0.4,0.2c0,0,0,0.1,0,0.1v0.4
c0,0.1,0.1,0.3,0.2,0.4c0,0,1.4,1.2,2.5,1.2c0.9,0,1.9-0.9,3.2-0.9c1,0,1.6,0.9,2.7,0.9c1.2,0,2.3-0.9,2.3-0.9
c0.3-0.1,0.4-0.4,0.4-0.5v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0
C16.2,11.2,16.2,11.2,16.2,11.1L16.2,11.1C16.2,11.2,16.2,11.2,16.2,11.1L16.2,11.1C16.2,11.1,16.2,11.1,16.2,11.1L16.2,11.1
L16.2,11.1z M16.2,13.5C16.2,13.5,16.2,13.5,16.2,13.5c0-0.2-0.2-0.4-0.4-0.3c0,0-1,0.9-2.3,0.9c-1,0-1.8-0.9-2.7-0.9
c-1.4,0-2.3,0.9-3.2,0.9c-0.9,0-2.3-1.2-2.3-1.2c-0.2-0.2-0.3,0.1-0.4,0.2c0,0,0,0.1,0,0.1v0.4c0,0.1,0.1,0.3,0.2,0.4
c0,0,1.4,1.2,2.5,1.2c0.9,0,1.9-0.9,3.2-0.9c1,0,1.6,0.9,2.7,0.9c1.2,0,2.3-0.9,2.3-0.9c0.3-0.1,0.4-0.4,0.4-0.5v0c0,0,0,0,0,0v0
c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0c0,0,0,0,0,0v0C16.2,13.6,16.2,13.6,16.2,13.5L16.2,13.5
C16.2,13.6,16.2,13.6,16.2,13.5L16.2,13.5C16.2,13.6,16.2,13.5,16.2,13.5L16.2,13.5L16.2,13.5z M6.7,4.5l1.5,1.5L6.7,7.6L5.1,6.1
L6.7,4.5z M10.1,4.5l1.5,1.5l-1.5,1.5L8.6,6.1L10.1,4.5z M13.6,4.5l1.5,1.5l-1.5,1.5L12,6.1L13.6,4.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_1_">
<g id="map-qixidi_1_">
<path id="路径_4_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
C10.3,23,10,23,9.7,22.9c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10.1C0,4.5,4.5,0,10,0z"/>
<path id="路径_3_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2L12,19l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1,10,1z"/>
<g>
<path class="st2" d="M16.4,12.8L16.4,12.8c-0.7-0.8-1.5-1.1-2.4-1c-0.2,0-0.4,0.1-0.6,0.1c-0.1-0.7-0.1-1.3-0.3-1.8
C13.5,9.1,13.5,8,13.1,7c-0.2-0.5-0.5-0.9-0.8-1.3C12.2,5.4,12,5.2,11.8,5L11.8,5c0,0-0.1-0.1-0.2-0.1c-0.4-0.1-0.8,0-1.3,0.2
c-0.2,0.1-0.4,0.2-0.6,0.4L9.6,5.5c0,0,0,0,0,0c-1.3-0.6-2.6-0.7-4-0.6c-0.2,0-0.4,0.2-0.4,0.4l0,0.5L5,5.7c0,0,0,0,0,0
C4.8,5.7,4.6,5.8,4.5,6l0,0c-0.1,1.5,0.1,3.1,0.9,4.4c0.1,0.1,0.1,0.2,0.2,0.3c-0.1,0.4,0,0.9,0,1.3c0.1,0.5,0.3,1,0.5,1.5
c0.2,0.3,0.3,0.4,0.4,0.6c0.3,0.3,0.8,0.1,0.7-0.3l0,0c0-0.2,0-0.4,0.1-0.6c0.1-0.2,0.2-0.3,0.3-0.4c1,0.6,2.3,0.9,3.9,1.1
c0,0.2-0.1,0.4-0.1,0.6c-0.1,0.9,0.2,1.8,1,2.4c0.2,0.1,0.4,0.1,0.6,0c0.1-0.1,0.1-0.1,0.1-0.2l0-0.1l0-0.1l0,0
c0-0.2,0.1-0.4,0.1-0.6c0.1-0.3,0.2-0.6,0.3-0.9c0.1-0.2,0.2-0.3,0.3-0.4c0.1-0.1,0.2-0.2,0.4-0.3c0.2-0.1,0.6-0.3,0.9-0.3
c0.2-0.1,0.4-0.1,0.6-0.1l0.1,0c0,0,0.1,0,0.1,0c0.2,0,0.4-0.2,0.4-0.5C16.5,13,16.4,12.9,16.4,12.8z M12.5,8.6
C12.6,8.5,12.6,8.5,12.5,8.6c0,0,0,0.1,0,0.2C12.6,8.7,12.5,8.6,12.5,8.6C12.5,8.6,12.5,8.6,12.5,8.6z M12.4,7.4L12.4,7.4
c0.1,0.1,0.1,0.2,0.1,0.3c0,0-0.1,0-0.1,0.1c-0.1,0-0.1,0.1-0.2,0.1c0,0,0,0,0,0C12.1,7.8,12,7.7,12,7.6l0,0c0,0,0.1,0,0.1-0.1
L12.4,7.4z M11.8,6.5l0.1,0c0,0.1,0.1,0.1,0.1,0.2c-0.1,0-0.2,0.1-0.3,0.1l-0.1,0.1l-0.1,0.1c-0.1-0.1-0.1-0.1-0.2-0.2
C11.5,6.7,11.7,6.6,11.8,6.5z M10.6,5.8c0.2-0.1,0.4-0.2,0.6-0.2l0.1,0l0,0c0,0,0.1,0.1,0.1,0.1c-0.2,0.1-0.3,0.1-0.5,0.2
c-0.1,0-0.2,0.1-0.3,0.1c-0.1-0.1-0.2-0.1-0.3-0.2C10.5,5.9,10.5,5.8,10.6,5.8z M6.7,12.6L6.7,12.6L6.7,12.6
c-0.1-0.1-0.2-0.3-0.3-0.5l0-0.1c0-0.1,0-0.1,0-0.2C6.6,11.9,6.8,12,7,12.2C6.9,12.3,6.8,12.4,6.7,12.6z M11.9,12.9
c-2.8-0.2-4.7-1.2-5.7-2.7l-0.1-0.1C6,9.8,5.9,9.6,5.8,9.4C7.7,9,8.7,7.9,9,6.6l0-0.1L8.2,6.3C8,7.4,7.2,8.3,5.5,8.6
C5.4,8,5.3,7.4,5.3,6.7l0-0.1l0.3,0.1c0,0,0.1,0,0.1,0c0.2,0,0.4-0.2,0.4-0.4v0l0-0.6l0,0c1.1-0.1,2.3,0.1,3.4,0.7l0.1,0.1
c1.7,1,2.7,2.8,2.9,5.8l0,0.2L12,12.9L11.9,12.9z M15,12.9c-0.3,0.1-0.7,0.2-0.9,0.4L14,13.3c-0.2,0.1-0.4,0.3-0.6,0.4
c-0.2,0.2-0.3,0.4-0.4,0.6c-0.1,0.3-0.3,0.6-0.4,0.9l0,0.1l0,0.1l0-0.1c-0.1-0.3-0.2-0.6-0.2-1l0-0.1c0-0.2,0.1-0.4,0.1-0.6
l0-0.1c0,0,0-0.1,0-0.1l0.7-0.7c0.3-0.1,0.5-0.2,0.8-0.2C14.4,12.6,14.7,12.7,15,12.9L15,12.9L15,12.9z"/>
<polygon class="st2" points="9,6.5 9,6.5 9,6.5 "/>
<path class="st2" d="M6.6,6.1c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0c0.2-0.2,0.2-0.5,0-0.7C7.1,5.9,6.8,5.9,6.6,6.1z
M7.2,6.7C7,6.8,6.9,6.8,6.7,6.7c-0.1-0.1-0.1-0.3,0-0.4c0.1-0.1,0.3-0.1,0.4,0C7.3,6.4,7.3,6.6,7.2,6.7z"/>
<path class="st2" d="M9.9,9c-0.2,0-0.4,0.1-0.4,0.4c0,0.4-0.2,0.5-0.4,0.5l-0.1,0c0,0,0,0,0,0l0,0c-0.2,0-0.4,0.1-0.5,0.4
c0,0.2,0.1,0.4,0.4,0.5c0.1,0,0.2,0,0.3,0c0.5-0.1,0.9-0.5,1-1.2C10.3,9.2,10.1,9,9.9,9z"/>
<path class="st2" d="M8.3,8.9C8.1,8.9,7.8,9,7.8,9.2c0,0,0,0,0,0C7.7,9.6,7.6,9.7,7.4,9.7c0,0-0.1,0-0.1,0l0,0
C7,9.6,6.8,9.8,6.8,10c-0.1,0.2,0.1,0.4,0.3,0.5c0.1,0,0.3,0,0.4,0c0.5-0.1,1-0.4,1.1-1.1C8.6,9.2,8.5,9,8.3,8.9z"/>
<path class="st2" d="M9,9c0.7-0.2,1-0.6,1.1-1.1c0-0.2,0-0.3,0-0.4l0,0c-0.1-0.2-0.3-0.4-0.5-0.3c-0.2,0-0.3,0.2-0.3,0.4l0,0
c0,0,0,0.1,0,0.1c0,0.2-0.1,0.3-0.5,0.4C8.6,8.2,8.5,8.4,8.6,8.7C8.6,8.9,8.8,9,9,9z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_8_">
<g id="map-qixidi_8_">
<path id="路径_18_" class="st0" d="M10,0.1c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
c-0.1,0.2-0.5,0.3-0.7,0.2c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.7,0,10.1C0,4.5,4.5,0.1,10,0.1z"/>
<path id="路径_17_" class="st1" d="M10,1.1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,19c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5.1,15,1.1,10,1.1z"/>
<g>
<path class="st2" d="M15.1,11.1c0,0-0.2-0.2-0.2-0.6c0-0.2-0.1-0.3-0.1-0.5c0-0.1,0.1-0.3,0.1-0.5C15,9.2,15.1,9,15.1,9
c0.1-0.1,0.2-0.2,0.3-0.3c0.2-0.3,0.2-0.6,0.1-0.7c-0.1-0.2-0.3-0.5-0.7-0.5c-0.1,0-0.2,0-0.3,0c-0.3,0.1-0.7,0.2-0.9,0.3
c-0.3,0.1-0.6,0.3-0.7,0.5c0,0,0,0-0.1,0.1c-0.3-0.2-0.5-0.4-0.8-0.6c0.1-0.4,0.2-0.7,0.1-1.1c0,0,0-0.1,0-0.1
c0-0.1-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.2-0.3-0.2c0,0,0,0-0.1,0c-0.2,0-0.3-0.1-0.5-0.1c-0.3,0-0.7,0.1-1.1,0.2
C9.6,6.5,9.3,6.6,9,6.8C8.9,6.9,8.8,7,8.6,7.1c-0.3,0-0.5,0.1-0.8,0.1C7.3,7.3,6.8,7.5,6.4,7.7C6,7.9,5.7,8.1,5.4,8.4
C5.1,8.7,4.8,9,4.7,9.4L4.6,9.8L4.8,10c0,0,0,0,0,0.1l-0.2,0.2l0.1,0.4c0.1,0.2,0.2,0.5,0.4,0.7c0.2,0.2,0.4,0.4,0.6,0.6
c0.4,0.3,1,0.6,1.7,0.8l0.3,0.1l0.1,0l0,0c0.2,0,0.5,0.1,0.8,0.1c0.1,0.1,0.2,0.1,0.3,0.2c0.3,0.2,0.6,0.4,0.9,0.5
c0.4,0.2,0.7,0.2,1.1,0.2c0.2,0,0.3,0,0.5-0.1l0,0c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.2-0.3,0.2-0.4c0,0,0,0,0,0l-0.1,0l0.1,0
c0.1-0.3,0-0.6-0.1-0.9c0.3-0.2,0.6-0.4,0.9-0.7c0,0,0,0,0.1,0.1c0.2,0.2,0.5,0.4,0.7,0.5c0.4,0.2,0.9,0.3,0.9,0.3
c0.1,0,0.2,0,0.3,0c0.4,0,0.6-0.2,0.7-0.5c0-0.1,0.1-0.4-0.1-0.7C15.3,11.3,15.2,11.2,15.1,11.1z M10.9,6.9c0.1,0,0.2,0,0.3,0
c0.1,0,0.1,0,0.1,0.1c0,0.2,0,0.3-0.1,0.5c-0.4-0.2-0.9-0.3-1.3-0.4C10.3,7,10.6,6.9,10.9,6.9z M7.5,12c-1-0.3-1.8-0.8-2.1-1.5
L5.9,10L5.4,9.6c0.3-0.6,1.1-1.2,2.1-1.5C8,8.6,8.2,9.3,8.2,10C8.2,10.7,7.9,11.4,7.5,12z M11.1,13.1c-0.1,0-0.2,0-0.3,0
c-0.2,0-0.5-0.1-0.8-0.2c0.4-0.1,0.8-0.2,1.2-0.4C11.2,12.7,11.2,12.9,11.1,13.1C11.2,13.1,11.2,13.1,11.1,13.1z M11.3,13.8
C11.3,13.8,11.3,13.8,11.3,13.8L11.3,13.8L11.3,13.8z M13.2,10.6C13.2,10.6,13.2,10.5,13.2,10.6c0-0.1,0-0.1,0-0.1l0-0.3
l-0.2,0.2c-0.3,0.3-0.9,0.8-1.5,1.2c-0.2,0.1-0.3,0.2-0.4,0.2c-0.6,0.3-1.1,0.4-1.7,0.4c-0.2,0-0.3,0-0.5,0c-0.2,0-0.3,0-0.5-0.1
c0.1-0.2,0.2-0.4,0.3-0.6c0.2-0.5,0.3-1,0.3-1.5c0-0.5-0.1-0.9-0.2-1.3c0-0.1,0-0.1-0.1-0.2C8.6,8.3,8.5,8.1,8.4,7.9
c0.2,0,0.3,0,0.5,0c0.1,0,0.3,0,0.4,0c0.5,0,1.1,0.1,1.6,0.4c0.2,0.1,0.4,0.2,0.6,0.3c0.5,0.3,1,0.7,1.5,1.2l0.2,0.2l0-0.3
c0,0,0-0.1,0-0.1c0-0.7,1.2-1.1,1.5-1.1c0,0,0,0,0,0C14.2,8.8,14.1,9.7,14,10l0,0l0,0.1c0,0.3,0.2,1.2,0.6,1.6c0,0,0,0,0,0
C14.3,11.7,13.2,11.3,13.2,10.6z"/>
<path class="st2" d="M6.9,9.3c-0.3,0-0.5,0.3-0.5,0.6c0,0.3,0.2,0.6,0.5,0.6s0.5-0.3,0.5-0.6C7.4,9.6,7.2,9.3,6.9,9.3z"/>
<path class="st2" d="M5.5,14.6h1.2v0.8H5.3c-0.4,0-0.7-0.3-0.7-0.7v-1.4h0.8v1.2C5.4,14.6,5.5,14.6,5.5,14.6z M5.4,5.5
c0-0.1,0.1-0.1,0.1-0.1h1.2V4.5H5.3c-0.4,0-0.7,0.3-0.7,0.7v1.4h0.8V5.5z M14.8,4.5h-1.4v0.8h1.2c0.1,0,0.1,0.1,0.1,0.1v1.2h0.8
V5.2C15.6,4.8,15.2,4.5,14.8,4.5z M14.7,14.5c0,0.1-0.1,0.1-0.1,0.1h-1.2v0.8h1.4c0.4,0,0.7-0.3,0.7-0.7v-1.4h-0.8V14.5z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="20" height="23" version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 23" style="enable-background:new 0 0 20 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0AC266;}
.st1{fill:#00A050;}
.st2{fill:#FFFFFF;}
</style>
<title>map-qixidi</title>
<g id="页面-1_6_">
<g id="map-qixidi_6_">
<path id="路径_14_" class="st0" d="M10,0c5.5,0,10,4.5,10,10c0,4.6-3.1,8.4-7.3,9.6c-0.6,0.6-1.2,1.3-1.7,2l-0.6,1
c-0.1,0.2-0.5,0.3-0.7,0.2c-0.1,0-0.1-0.1-0.2-0.2l-0.6-1c-0.5-0.8-1-1.4-1.7-2C3.1,18.5,0,14.6,0,10C0,4.5,4.5,0,10,0z"/>
<path id="路径_13_" class="st1" d="M10,1c-5,0-9,4-9,9c0,4,2.6,7.4,6.3,8.6l0.5,0.1L8,18.9c0.6,0.6,1.2,1.2,1.6,1.9l0.2,0.4
l0.2,0.3l0.2-0.3c0.4-0.7,1-1.4,1.6-2l0.3-0.3l0.2-0.2l0.2-0.1c3.8-1.1,6.5-4.6,6.5-8.7C19,5,15,1,10,1z"/>
<path class="st2" d="M11.3,11.8H8.2V8.4C8.2,8.2,8.4,8,8.7,8h3.4v3.5c0,0.2-0.2,0.4-0.4,0.4L11.3,11.8L11.3,11.8L11.3,11.8z"/>
<path class="st2" d="M9.3,14.4c0,0.1,0,0.2-0.1,0.3c-0.1,0.1-0.2,0.1-0.3,0.1c0,0,0,0,0,0H7.2c-1,0-1.9-0.8-1.9-1.9v-1.7
c0-0.2,0.2-0.4,0.4-0.4s0.4,0.2,0.4,0.4v1.7c0,0.6,0.5,1,1,1h1.7C9.1,13.9,9.3,14.1,9.3,14.4z M14.7,10.8L14.7,10.8
c-0.2,0-0.4,0.2-0.4,0.4v1.7c0,0.6-0.5,1-1,1h-1.7c-0.2,0-0.4,0.2-0.4,0.4s0.2,0.4,0.4,0.4h1.7c1,0,1.9-0.8,1.9-1.9v-1.7
c0-0.1,0-0.2-0.1-0.3C14.9,10.8,14.8,10.8,14.7,10.8z M13.2,5h-1.7c-0.2,0-0.4,0.2-0.4,0.4c0,0.2,0.2,0.4,0.4,0.4h1.7
c0.6,0,1,0.5,1,1v1.7c0,0.2,0.2,0.4,0.4,0.4s0.4-0.2,0.4-0.4V6.9C15.1,5.9,14.2,5,13.2,5z M8.8,5H7.2c-1,0-1.9,0.8-1.9,1.9v1.7
c0,0.2,0.2,0.4,0.4,0.4l0,0l0,0c0.2,0,0.4-0.2,0.4-0.4V6.9c0-0.6,0.5-1,1-1h1.7c0.2,0,0.4-0.2,0.4-0.4S9.1,5,8.8,5z"/>
<path class="st2" d="M14,9.5h0.8v0.8H14V9.5z M13.4,9.5h0.4v0.8h-0.4V9.5z M17,9.7l-0.9-0.8c-0.2-0.2-0.5,0-0.5,0.2v0.4h-0.6v0.8
h0.6v0.4c0,0.2,0.3,0.4,0.5,0.2l0.9-0.8C17.1,10,17.1,9.8,17,9.7L17,9.7z M5.3,9.5h0.8v0.8H5.3V9.5z M6.4,9.5h0.4v0.8H6.4V9.5z
M4.5,9.1c0-0.2-0.3-0.4-0.5-0.2L3.2,9.7c-0.1,0.1-0.1,0.3,0,0.4c0,0,0,0,0,0l0.9,0.8c0.2,0.2,0.5,0,0.5-0.2v-0.4h0.6V9.5H4.5
L4.5,9.1L4.5,9.1z M9.7,13.8h0.8v0.8H9.7V13.8z M9.7,13.1h0.8v0.4H9.7V13.1z M10.9,15.4h-0.4v-0.6H9.7v0.6H9.3
c-0.2,0-0.4,0.3-0.2,0.5l0.8,0.9c0.1,0.1,0.3,0.1,0.4,0c0,0,0,0,0,0l0.8-0.9C11.3,15.7,11.2,15.4,10.9,15.4L10.9,15.4z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -2741,8 +2741,8 @@ const StinfoAiVidoPointColumns: Array<any> = [
url: '' url: ''
}, },
{ {
name: '监控内容类型', name: '监控方式',
filed: 'sttpName', filed: 'mwayName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '' url: ''

View File

@ -344,7 +344,15 @@ const columnsConfig = ref([
{ {
type: 'MM', type: 'MM',
columns: MMColumns columns: MMColumns
} },
{ type: 'AI_FISH', columns: StinfoAiVidoPointColumns },
{ type: 'AI_ANML', columns: StinfoAiVidoPointColumns },
{ type: 'AI_BIRD', columns: StinfoAiVidoPointColumns },
{ type: 'AI_RIV', columns: StinfoAiVidoPointColumns },
{ type: 'AI_FISHING', columns: StinfoAiVidoPointColumns },
{ type: 'AI_5001', columns: StinfoAiVidoPointColumns },
{ type: 'AI_5005', columns: StinfoAiVidoPointColumns },
{ type: 'AI_SORTER', columns: StinfoAiVidoPointColumns }
//DW_1 //DW_1
]); ]);

View File

@ -0,0 +1,217 @@
<template>
<div>
<div class="mb-2">
时间
<a-range-picker
v-model:value="searchParams"
type="daterange"
:allow-clear="false"
value-format="YYYY-MM-DD HH:mm:ss"
:presets="DateSetting.RangeButton.year1"
/>
<a-button class="ml-2" type="primary" @click="initData">查询</a-button>
</div>
<BasicTable
ref="tableRef"
:scrollY="500"
:columns="columns"
:searchParams="{
sort: [{ field: 'tm', dir: 'desc' }]
}"
:list-url="currentApi"
:paginationConfig="{
showSizeChanger: false,
showQuickJumper: false
}"
/>
<!-- 附件/图片/视频 展示弹窗fid 含多个时左侧有列表可切换展示 -->
<CommonAttachmentModal
v-model:open="attachmentOpen"
:fid="attachmentFid"
:title="attachmentTitle"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch, computed, h, nextTick } from 'vue';
import { Button } from 'ant-design-vue';
import BasicTable from '@/components/BasicTable/index.vue';
import CommonAttachmentModal from './CommonAttachmentModal.vue';
import { getAnimalList, getWarningList, getFishList } from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model';
import { DateSetting } from '@/utils/enumeration';
import dayjs from 'dayjs';
const modelStore = useModelStore();
const tableRef = ref();
const searchParams = ref([
dayjs().subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss'),
dayjs().format('YYYY-MM-DD HH:mm:ss')
]);
const props = defineProps({
isActive: { type: Boolean, default: false }
});
// ==================== sttpCode ====================
const sttpCode = computed(() => modelStore.params.sttpCode || '');
const currentApi = computed(() => {
const code = sttpCode.value;
if (code === 'AI_ANML' || code === 'AI_BIRD') return getAnimalList;
if (
code === 'AI_FISHING' ||
code === 'AI_RIV' ||
code === 'AI_5001' ||
code === 'AI_5005'
)
return getWarningList;
// AI_FISH / AI_SORTER / FPRD
return getFishList;
});
const isAnimalType = () =>
sttpCode.value === 'AI_ANML' || sttpCode.value === 'AI_BIRD';
const isWarningType = () =>
['AI_FISHING', 'AI_RIV', 'AI_5001', 'AI_5005'].includes(sttpCode.value);
// ==================== ====================
const attachmentOpen = ref(false);
const attachmentFid = ref('');
const attachmentTitle = ref('查看附件');
const openAttachment = (fid: string, title: string) => {
const value = String(fid || '').trim();
if (!value || value === '-' || value === 'null' || value === 'undefined')
return;
attachmentFid.value = value;
attachmentTitle.value = title;
attachmentOpen.value = true;
};
//
const renderMediaButton = (fidKey: string, label: string) => ({
title: label,
dataIndex: fidKey,
width: 110,
customRender: ({ text }: any) => {
const hasValidFid =
text &&
text !== '-' &&
String(text).trim() !== '' &&
text !== 'null' &&
text !== 'undefined';
return h(
Button,
{
type: 'link',
disabled: !hasValidFid,
size: 'small',
style: {
color: !hasValidFid ? '#ccc' : '#1890ff',
cursor: !hasValidFid ? 'not-allowed' : 'pointer'
},
onClick: () => {
if (hasValidFid) {
openAttachment(text, label);
}
}
},
() => label
);
}
});
const renderTimeColumn = () => ({
title: '时间',
dataIndex: 'tm',
width: 160,
sort: true,
customRender: ({ text }: any) =>
text && text !== '-' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '-'
});
// ==================== ====================
// / / getFishList
const fishColumns = [
renderTimeColumn(),
{ title: '鱼种类', dataIndex: 'ftp', width: 130 },
{ title: '游向', dataIndex: 'directionName', width: 130, sort: true },
{ title: '鱼长度', dataIndex: 'length', width: 100 },
{ title: '数量', dataIndex: 'fcnt', width: 100 },
renderMediaButton('firstimgurl', '查看图片'),
renderMediaButton('videourl', '查看视频')
];
// / getAnimalList
const animalColumns = [
{ title: '记录时间', dataIndex: 'tm', width: 140, sort: true },
{ title: '拍摄时间', dataIndex: 'shootTime', width: 140, sort: true },
{ title: '识别时间', dataIndex: 'recognizeTime', width: 140, sort: true },
{ title: '动物名称', dataIndex: 'animalName', width: 140, sort: true },
{ title: '识别描述', dataIndex: 'behavior', width: 280 },
{ title: '备注', dataIndex: 'description', width: 200 },
renderMediaButton('imgPath', '查看图片'),
renderMediaButton('flpth', '查看视频')
];
// / / / getWarningList fid
const warningColumns = [
renderTimeColumn(),
{ title: 'AI识别值', dataIndex: 'aiVal', width: 180 },
{ title: '备注', dataIndex: 'remark' },
renderMediaButton('fid', '操作')
];
// ==================== ====================
const columns = computed(() => {
if (isAnimalType()) return animalColumns;
if (isWarningType()) return warningColumns;
return fishColumns;
});
const hasLoaded = ref(false);
const initData = async () => {
if (hasLoaded.value) return;
hasLoaded.value = true;
const filter = {
logic: 'and',
filters: [
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: searchParams.value[0]
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: searchParams.value[1]
},
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: modelStore.params.stcd
}
]
};
// BasicTable immediate watch
await nextTick();
tableRef.value?.getList(filter);
};
// tab
watch(
() => props.isActive,
active => {
if (active) initData();
},
{ immediate: true, deep: true }
);
</script>
<style scoped>
.fish-facility-monitor-data {
padding: 20px;
}
</style>

View File

@ -121,6 +121,12 @@
:is-active="currentActiveKey === 'DZGJ'" :is-active="currentActiveKey === 'DZGJ'"
:tabs-items="getTabChildren('DZGJ')" :tabs-items="getTabChildren('DZGJ')"
/> />
<!-- 检测数据 - 智慧装备 -->
<ZhzbMonitorInfo
v-show="currentActiveKey === 'zhzbMonitorInfo'"
:is-active="currentActiveKey === 'zhzbMonitorInfo'"
/>
<!-- 生态流量泄放设施 - 监测数据 --> <!-- 生态流量泄放设施 - 监测数据 -->
<FlowDischarge <FlowDischarge
v-show="currentActiveKey === 'flowDischarge'" v-show="currentActiveKey === 'flowDischarge'"
@ -284,6 +290,7 @@ import DZGJ from './components/DZGJ.vue'; // 水电站告警情况
import FishZHFX from './components/FishZHFX.vue'; // import FishZHFX from './components/FishZHFX.vue'; //
import EcologicalFlow from './components/EcologicalFlow.vue'; // import EcologicalFlow from './components/EcologicalFlow.vue'; //
import Attachment from './components/Attachment.vue'; // / import Attachment from './components/Attachment.vue'; // /
import ZhzbMonitorInfo from './components/zhzbMonitorInfo.vue'; // -
import WaterTemperatureRep from './components/WaterTemperatureRep.vue'; // import WaterTemperatureRep from './components/WaterTemperatureRep.vue'; //
import { useModelStore } from '@/store/modules/model'; import { useModelStore } from '@/store/modules/model';
import { useUiStore } from '@/store/modules/ui'; import { useUiStore } from '@/store/modules/ui';
@ -888,7 +895,10 @@ const handleClose = () => {
eqtp: '', eqtp: '',
baseId: '', baseId: '',
sttpCode: '', sttpCode: '',
sttpMap: '' sttpMap: '',
dtinType: '',
bldstt: '',
bldsttCcode: ''
}; };
// , // ,
modelStore.showStcdSelector = false; modelStore.showStcdSelector = false;
@ -904,6 +914,7 @@ const handleClose = () => {
modelStore.aiSites = []; modelStore.aiSites = [];
modelStore.wtConfig = {}; modelStore.wtConfig = {};
modelStore.filter = { modelStore.filter = {
year: '',
stllgzlx: '', // - stllgzlx: '', // -
stllTmType: '', // - stllTmType: '', // -
tm: '', tm: '',

View File

@ -728,6 +728,17 @@ const DEVICETABS: Array<any> = [
type: 'basic', type: 'basic',
url: '/eng/base/msstbprpt/getStcdInfo', url: '/eng/base/msstbprpt/getStcdInfo',
default: true // 默认显示 default: true // 默认显示
},
{
name: '监测数据',
key: 'zhzbMonitorInfo',
type: 'zhzbMonitorInfo'
},
{
name: '实时视频',
key: 'videoInfo',
type: 'video',
url: '/video/dataStcdFrame/getVideoMonitorList'
} }
]; ];
// //电站告警 // //电站告警
@ -1232,6 +1243,16 @@ const handleTabs = (modaldata: any) => {
// return WETabs // return WETabs
case 'MM': case 'MM':
return WeatherTabs; return WeatherTabs;
case 'AI_FISH': // 智慧装备
case 'AI_ANML':
case 'AI_BIRD':
case 'AI_RIV':
case 'AI_FISHING':
case 'AI_5001':
case 'AI_5005':
case 'AI_SORTER':
return DEVICETABS;
// case 'WARN': // case 'WARN':
// return WaterQualityMonitoring // return WaterQualityMonitoring
// case 'AI': // case 'AI':

View File

@ -2013,10 +2013,10 @@ export class MapOl implements MapInterface {
} }
/** /**
* *
* @returns * @returns
*/ */
private getCurrentMaskTargetLayers(): TileLayer[] { private getCurrentMaskTargetLayers(): (TileLayer | VectorLayer<any>)[] {
const layers: TileLayer[] = []; const layers: (TileLayer | VectorLayer<any>)[] = [];
const baseLayer = this.layerRegistry.get(this.REGISTRY_KEY); const baseLayer = this.layerRegistry.get(this.REGISTRY_KEY);
if (baseLayer instanceof TileLayer) { if (baseLayer instanceof TileLayer) {
@ -2033,6 +2033,19 @@ export class MapOl implements MapInterface {
} }
} }
// 遍历所有图层注册表,收集所有可见的 TileLayer 和 VectorLayer
// 这会自动包括栖息地图层、自然保护区图层等已勾选显示的基础图层
this.layerRegistry.forEach((layerInstance, layerKey) => {
if (
(layerInstance instanceof TileLayer ||
layerInstance instanceof VectorLayer) &&
layerInstance.getVisible() &&
!layers.includes(layerInstance)
) {
layers.push(layerInstance);
}
});
return layers; return layers;
} }
/** /**
@ -2173,6 +2186,23 @@ export class MapOl implements MapInterface {
if (this.isBatchPopupMode) { if (this.isBatchPopupMode) {
this.requestUpdateBatchPopups(true); this.requestUpdateBatchPopups(true);
} }
// 如果当前处于基地裁切状态且图层显示,确保裁切事件已绑定
if (
this.currentClipGeoJson &&
checked &&
(layerInstance instanceof TileLayer ||
layerInstance instanceof VectorLayer)
) {
// 检查该图层是否已经绑定过裁切事件,如果没有则追加
const hasBinding =
this.regionMaskManager.hasLayerBinding(layerInstance);
if (!hasBinding) {
this.regionMaskManager.appendMaskToLayer(
layerInstance,
this.currentClipGeoJson
);
}
}
} else { } else {
console.warn( console.warn(
`未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`, `未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`,

View File

@ -1,11 +1,18 @@
import OlMap from 'ol/Map'; import OlMap from 'ol/Map';
import View from 'ol/View'; import View from 'ol/View';
import TileLayer from 'ol/layer/Tile'; import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import GeoJSON from 'ol/format/GeoJSON'; import GeoJSON from 'ol/format/GeoJSON';
import VectorSource from 'ol/source/Vector'; import VectorSource from 'ol/source/Vector';
import { fromLonLat } from 'ol/proj'; import { fromLonLat } from 'ol/proj';
import { PointLayerManager } from './point-layer-manager'; import { PointLayerManager } from './point-layer-manager';
type MaskBinding = {
layer: TileLayer | VectorLayer<any>;
prerender: (event: any) => void;
postrender: (event: any) => void;
};
type RegionMaskManagerOptions = { type RegionMaskManagerOptions = {
map: OlMap | null; map: OlMap | null;
view: View | null; view: View | null;
@ -20,11 +27,7 @@ export class RegionMaskManager {
private pointLayerManager: PointLayerManager; private pointLayerManager: PointLayerManager;
private defaultCenter: [number, number]; private defaultCenter: [number, number];
private defaultZoom: number; private defaultZoom: number;
private maskBindings: Array<{ private maskBindings: Array<MaskBinding> = [];
layer: TileLayer;
prerender: (event: any) => void;
postrender: (event: any) => void;
}> = [];
constructor(options: RegionMaskManagerOptions) { constructor(options: RegionMaskManagerOptions) {
this.map = options.map; this.map = options.map;
@ -76,7 +79,10 @@ export class RegionMaskManager {
return; return;
} }
feature.set('_regionVisible', this.isPointInPolygon(lon, lat, regionCoords)); feature.set(
'_regionVisible',
this.isPointInPolygon(lon, lat, regionCoords)
);
feature.changed(); feature.changed();
}); });
} }
@ -114,12 +120,18 @@ export class RegionMaskManager {
} }
// 备注:把 GeoJSON 边界绑定到底图 prerender/postrender 事件,实现区域裁切遮罩。 // 备注:把 GeoJSON 边界绑定到底图 prerender/postrender 事件,实现区域裁切遮罩。
applyMapMask(rasterLayer: TileLayer, clipGeoJson: any): void { applyMapMask(
rasterLayer: TileLayer | VectorLayer<any>,
clipGeoJson: any
): void {
this.applyMapMaskToLayers(rasterLayer ? [rasterLayer] : [], clipGeoJson); this.applyMapMaskToLayers(rasterLayer ? [rasterLayer] : [], clipGeoJson);
} }
// 备注:把 GeoJSON 边界同时绑定到多个底图图层,保证多底图叠加场景下裁切边界保持一致。 // 备注:把 GeoJSON 边界同时绑定到多个底图图层和矢量图层,保证多图层叠加场景下裁切边界保持一致。
applyMapMaskToLayers(rasterLayers: TileLayer[] = [], clipGeoJson: any): void { applyMapMaskToLayers(
rasterLayers: (TileLayer | VectorLayer<any>)[] = [],
clipGeoJson: any
): void {
if (!clipGeoJson) return; if (!clipGeoJson) return;
const targetLayers = rasterLayers.filter(Boolean); const targetLayers = rasterLayers.filter(Boolean);
@ -162,7 +174,9 @@ export class RegionMaskManager {
} }
} }
} else if (geometry.getType() === 'MultiPolygon') { } else if (geometry.getType() === 'MultiPolygon') {
const polygons = (geometry as any).getCoordinates() as number[][][][]; const polygons = (
geometry as any
).getCoordinates() as number[][][][];
for (const polygonRings of polygons) { for (const polygonRings of polygons) {
for (const ring of polygonRings) { for (const ring of polygonRings) {
if (this.drawRingToContext(context, ring, frameState)) { if (this.drawRingToContext(context, ring, frameState)) {
@ -208,6 +222,93 @@ export class RegionMaskManager {
}); });
} }
// 备注:给单个图层追加裁切,不破坏已有裁切绑定。用于新图层动态加入时复用当前裁切边界。
appendMaskToLayer(
layer: TileLayer | VectorLayer<any>,
clipGeoJson: any
): void {
if (!clipGeoJson || !layer) return;
// 避免重复绑定
if (this.hasLayerBinding(layer)) return;
const features = new GeoJSON().readFeatures(clipGeoJson, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
if (!features.length) return;
const maskPrerender = (event: any) => {
const context = event.context;
const frameState = event.frameState;
if (!context || !frameState || !this.map) return;
context.save();
context.setTransform(1, 0, 0, 1, 0, 0);
context.beginPath();
let hasValidPath = false;
for (const feature of features) {
const geometry = feature.getGeometry();
if (!geometry) continue;
if (geometry.getType() === 'Polygon') {
const rings = (geometry as any).getCoordinates() as number[][][];
for (const ring of rings) {
if (this.drawRingToContext(context, ring, frameState)) {
hasValidPath = true;
}
}
} else if (geometry.getType() === 'MultiPolygon') {
const polygons = (geometry as any).getCoordinates() as number[][][][];
for (const polygonRings of polygons) {
for (const ring of polygonRings) {
if (this.drawRingToContext(context, ring, frameState)) {
hasValidPath = true;
}
}
}
}
}
if (!hasValidPath) return;
context.save();
context.lineWidth = 6;
context.strokeStyle = '#6D64DF';
context.stroke();
context.clip();
context.strokeStyle = '#CCC9F4';
context.lineWidth = 5;
context.stroke();
context.clip();
};
const maskPostrender = (event: any) => {
if (event.context) {
event.context.restore();
event.context.restore();
}
};
this.maskBindings.push({
layer,
prerender: maskPrerender,
postrender: maskPostrender
});
layer.on('prerender', maskPrerender);
layer.on('postrender', maskPostrender);
layer.changed();
}
// 备注:检查某个图层是否已经绑定了裁切事件,避免重复绑定。
hasLayerBinding(layer: TileLayer | VectorLayer<any>): boolean {
return this.maskBindings.some(binding => binding.layer === layer);
}
// 备注:清除当前底图遮罩并按需恢复默认视野,供切换基地和异常兜底复用。 // 备注:清除当前底图遮罩并按需恢复默认视野,供切换基地和异常兜底复用。
clearMapMask(resetView = true): void { clearMapMask(resetView = true): void {
const maskedLayers = this.maskBindings.map(binding => binding.layer); const maskedLayers = this.maskBindings.map(binding => binding.layer);
@ -308,7 +409,9 @@ export class RegionMaskManager {
continue; continue;
} }
const cssPixel = this.map?.getPixelFromCoordinate(coord as [number, number]); const cssPixel = this.map?.getPixelFromCoordinate(
coord as [number, number]
);
if (!cssPixel) continue; if (!cssPixel) continue;
const canvasX = cssPixel[0] * pixelRatio; const canvasX = cssPixel[0] * pixelRatio;

View File

@ -1,8 +1,5 @@
<template> <template>
<div <div class="map-filter-container" v-show="!uiStore.isRoaming">
class="map-filter-container"
v-show="!uiStore.isRoaming && !uiStore.isSmartEquipment"
>
<div class="toolbar"> <div class="toolbar">
<a-form :model="formModel"> <a-form :model="formModel">
<a-row :gutter="10"> <a-row :gutter="10">
@ -91,6 +88,49 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col v-if="uiStore.isSmartEquipment">
<a-form-item label="" name="">
<a-select
v-model:value="power"
:loading="powerLoading"
show-search
:filter-option="filterOption"
style="width: 140px"
placeholder="请选择电站"
allow-clear
@change="getstcd"
>
<a-select-option
v-for="item in stationOptions"
:key="item.stcd"
:value="item.stcd"
:title="item.ennm"
>{{ item.ennm }}</a-select-option
>
</a-select>
</a-form-item>
</a-col>
<a-col v-if="uiStore.isSmartEquipment">
<a-form-item label="" name="">
<a-select
v-model:value="equipmentTypeValue"
show-search
:filter-option="filterOption"
style="width: 160px"
placeholder="请选择装备类型"
allow-clear
@change="handleEquipmentTypeChange"
>
<a-select-option
v-for="item in equipmentTypeOptions"
:key="item.code"
:value="item.code"
:title="item.name"
>{{ item.name }}</a-select-option
>
</a-select>
</a-form-item>
</a-col>
<a-col> <a-col>
<a-form-item label="" name="anchorPointSelect"> <a-form-item label="" name="anchorPointSelect">
<a-select <a-select
@ -139,6 +179,8 @@ import { useMapStore } from '@/store/modules/map';
import { useModelStore } from '@/store/modules/model'; import { useModelStore } from '@/store/modules/model';
import { DateSetting } from '@/utils/enumeration'; import { DateSetting } from '@/utils/enumeration';
import { getFishList, getFishPointList } from '@/api/map'; import { getFishList, getFishPointList } from '@/api/map';
import { getEngInfoDropdown } from '@/api/select';
import { vmsstbprptGetKendoListWte } from '@/api/sz';
const route = useRoute(); const route = useRoute();
const mapOrchestrator = useMapOrchestrator(); const mapOrchestrator = useMapOrchestrator();
@ -169,6 +211,19 @@ const DRAWER_STTP_CODES = [
'DW', 'DW',
'WE', 'WE',
'WVA', 'WVA',
'FPRD',
''
];
// - sttpCode
const SmartEquipmentCodes = [
'AI_FISH',
'AI_ANML',
'AI_BIRD',
'AI_RIV',
'AI_FISHING',
'AI_5001',
'AI_5005',
'AI_SORTER',
'FPRD' 'FPRD'
]; ];
const siteRangePicker = [ const siteRangePicker = [
@ -203,6 +258,31 @@ const searchTimeRange = ref([
dayjs(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss') dayjs(mapViewStore.searchTimeRange[1]).format('YYYY-MM-DD HH:mm:ss')
]); ]);
// -
const power = ref<string | undefined>(undefined);
const powerLoading = ref(false);
const stationOptions = ref<any[]>([]);
const equipmentOptions = ref<any[]>([]);
const equipmentLoading = ref(false);
const equipmentTypeValue = ref<string | undefined>(undefined);
const sttpCodeToName: Record<string, string> = {
FPRD: '鱼类调查装置',
AI_FISH: '鱼类智能识别装置',
AI_ANML: '动物智能识别装置',
AI_BIRD: '鸟类智能识别装置',
AI_RIV: '河道违章智能识别装置',
AI_SORTER: '智能分拣装置',
AI_FISHING: '钓鱼',
AI_5001: '漂浮物识别装置',
AI_5005: '叠梁门运行识别装置'
};
const equipmentTypeOptions = computed(() => {
return SmartEquipmentCodes.map(code => ({
code,
name: sttpCodeToName[code] || code
}));
});
// //
const isMenu = (menuPath: string) => { const isMenu = (menuPath: string) => {
const path = route.path; const path = route.path;
@ -217,7 +297,9 @@ const isFishSurveyMode = computed(() => {
}); });
const anchorPointLoading = computed(() => { const anchorPointLoading = computed(() => {
return isFishSurveyMode.value ? fishPointLoading.value : false; if (isFishSurveyMode.value) return fishPointLoading.value;
if (uiStore.isSmartEquipment) return equipmentLoading.value;
return false;
}); });
const currentFishBaseId = computed(() => { const currentFishBaseId = computed(() => {
@ -243,6 +325,7 @@ const anchorPointSelectValue = computed({
watch(anchorPointSelectValue, value => { watch(anchorPointSelectValue, value => {
if (!value) return; if (!value) return;
const matched = anchorPointOptions.value.find(item => item.stcd === value); const matched = anchorPointOptions.value.find(item => item.stcd === value);
console.log(matched);
if (matched) { if (matched) {
modelStore.selectedAnchorPoint = matched; modelStore.selectedAnchorPoint = matched;
} }
@ -262,6 +345,7 @@ watch(
anchorPointSelect: null, anchorPointSelect: null,
fishSurveyZhuanZhi: false fishSurveyZhuanZhi: false
}; };
equipmentTypeValue.value = undefined;
uiStore.setIsSearchActive(false); uiStore.setIsSearchActive(false);
uiStore.setGlobalDrawerOpen(false); uiStore.setGlobalDrawerOpen(false);
setTimeout(() => { setTimeout(() => {
@ -364,9 +448,17 @@ const fetchFishSurveyOptions = async (
fishPointOptions.value = nextFishPointOptions; fishPointOptions.value = nextFishPointOptions;
fishSpeciesOptions.value = nextFishSpeciesOptions; fishSpeciesOptions.value = nextFishSpeciesOptions;
// fishSurveyZhuanZhiParams.value.value = nextFishSpeciesOptions.map( if (nextFishSpeciesOptions.length === 0) {
// item => item.value fishSurveyZhuanZhiParams.value.value = [];
// ); fishSurveyZhuanZhiParams.value.anchorPointSelect = null;
modelStore.selectedFishSpecies = [];
modelStore.globalDrawerMode = 'basicInfo';
uiStore.setIsSearchActive(false);
uiStore.setGlobalDrawerOpen(false);
setTimeout(() => {
uiStore.setDrawerOpen(true);
}, 300);
}
syncFishAnchorPointSelection(nextFishPointOptions); syncFishAnchorPointSelection(nextFishPointOptions);
} finally { } finally {
fishPointLoading.value = false; fishPointLoading.value = false;
@ -573,6 +665,16 @@ const anchorPointOptions = computed(() => {
return filteredFishPointOptions.value; return filteredFishPointOptions.value;
} }
if (uiStore.isSmartEquipment && equipmentOptions.value.length > 0) {
if (equipmentTypeValue.value) {
return equipmentOptions.value.filter(
(item: any) =>
String(item.sttpCode || item.sttp || '') === equipmentTypeValue.value
);
}
return equipmentOptions.value;
}
const pointData = mapDataStore.pointData || []; const pointData = mapDataStore.pointData || [];
const checkedLayerKeys = new Set<string>( const checkedLayerKeys = new Set<string>(
mapViewStore.getCheckedLayerKeys() || [] mapViewStore.getCheckedLayerKeys() || []
@ -628,6 +730,82 @@ const filterAnchorPoint = (input: string, option: any) => {
return stnm.includes(keyword) || baseName.includes(keyword); return stnm.includes(keyword) || baseName.includes(keyword);
}; };
const filterOption = (inputValue: string, option: any) => {
const label = option.title || '';
const keyword = inputValue || '';
return label.indexOf(keyword) !== -1;
};
const getDataList = async () => {
powerLoading.value = true;
power.value = undefined;
stationOptions.value = [];
const baseId =
jidiSelectEventStore.selectedItem?.wbsCode === 'all'
? ''
: jidiSelectEventStore.selectedItem?.wbsCode || '';
const res = await getEngInfoDropdown({
baseId
});
if (res.data) {
powerLoading.value = false;
stationOptions.value = res.data;
}
getstcd();
};
const getstcd = async () => {
equipmentLoading.value = true;
const baseId = jidiSelectEventStore.selectedItem?.wbsCode || '';
const params = {
filter: {
logic: 'and',
filters: [
baseId !== 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseId
}
: null,
power.value
? {
field: 'rstcd',
operator: 'eq',
dataType: 'string',
value: power.value
}
: null,
{
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: SmartEquipmentCodes
}
].filter(Boolean)
},
groupResultFlat: true
};
const res = await vmsstbprptGetKendoListWte(params);
if (res.data?.data?.length > 0) {
equipmentOptions.value = res.data.data;
} else {
equipmentOptions.value = [];
}
equipmentLoading.value = false;
};
const handleEquipmentTypeChange = (value: string | undefined) => {
//
formModel.value.anchorPointSelect = null;
uiStore.setIsSearchActive(false);
uiStore.setGlobalDrawerOpen(false);
setTimeout(() => {
uiStore.setDrawerOpen(true);
}, 300);
};
// //
watch( watch(
() => currentFishBaseId.value, () => currentFishBaseId.value,
@ -646,11 +824,39 @@ watch(
{ immediate: true } { immediate: true }
); );
//
watch(
() => currentFishBaseId.value,
() => {
if (uiStore.isSmartEquipment) {
getDataList();
}
},
{ immediate: true, deep: true }
);
//
watch(
() => uiStore.isSmartEquipment,
newVal => {
if (newVal) {
getDataList();
} else {
equipmentTypeValue.value = undefined;
}
}
);
watch( watch(
() => formModel.value.fishSurveyZhuanZhi, () => formModel.value.fishSurveyZhuanZhi,
async checked => { async checked => {
if (!checked) { if (!checked) {
resetFishSurveyState(); resetFishSurveyState();
uiStore.setIsSearchActive(false);
uiStore.setGlobalDrawerOpen(false);
setTimeout(() => {
uiStore.setDrawerOpen(true);
}, 300);
return; return;
} }
@ -739,7 +945,10 @@ const handleAnchorPointChange = (value: string) => {
matched?.sttpCode || matched?.sttp || '' matched?.sttpCode || matched?.sttp || ''
).toUpperCase(); ).toUpperCase();
// 退 // 退
if (!DRAWER_STTP_CODES.includes(sttpCode)) { if (
!DRAWER_STTP_CODES.includes(sttpCode) &&
!SmartEquipmentCodes.includes(sttpCode)
) {
uiStore.setIsSearchActive(false); uiStore.setIsSearchActive(false);
uiStore.setGlobalDrawerOpen(false); uiStore.setGlobalDrawerOpen(false);
setTimeout(() => { setTimeout(() => {

View File

@ -40,7 +40,6 @@ const isZhiNengPeiTu = computed(() => {
<!-- 物种介绍模式鱼种下拉选择时展示动物/植物介绍 + 监测表格否则展示基础信息 + 监测表格 --> <!-- 物种介绍模式鱼种下拉选择时展示动物/植物介绍 + 监测表格否则展示基础信息 + 监测表格 -->
<template v-if="modelStore.globalDrawerMode === 'speciesIntro'"> <template v-if="modelStore.globalDrawerMode === 'speciesIntro'">
<speciesIntroMod /> <speciesIntroMod />
<monitoringTableMod />
</template> </template>
<template v-else> <template v-else>
<basicInfoMod /> <basicInfoMod />

View File

@ -1,77 +1,253 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="各基地设备统计"> <SidePanelItem title="各基地设备统计">
<div ref="chartRef" style="width: 100%; height: calc(100vh - 520px)"></div> <a-spin :spinning="loading" wrapperClassName="chart-spin">
<div
ref="chartRef"
style="width: 100%; height: calc(100vh - 520px)"
></div>
</a-spin>
<!-- 弹框 -->
<a-modal
v-model:open="modalVisible"
:title="modalTitle"
:footer="null"
width="1536px"
@cancel="handleCloseModal"
>
<ZHZB v-if="currentType" :baseId="currentBaseId" :type="currentType" />
</a-modal>
</SidePanelItem> </SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import dayjs from 'dayjs'; import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'; import { imeBaseDeviceStat } from '@/api/sz';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import ZHZB from '@/modules/ZHZB/index.vue';
import { useDraggable } from '@/utils/drag';
const loading = ref(false);
const baseid = ref('all');
const jidiSelectEventStore = useJidiSelectEventStore();
//
const modalVisible = ref(false);
const currentType = ref<string | null>(null);
const currentBaseId = ref<string>('');
const currentBaseName = ref<string>('');
useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
const modalTitle = computed(() => {
const typeName =
deviceList.find(d => d.code === currentType.value)?.name || '';
return `${currentBaseName.value}${typeName ? ' - ' + typeName : ''}`;
});
// baseId
const chartData = ref<any[]>([]);
// //
const chartRef = ref(null); const chartRef = ref(null);
let chartInstance = null; let chartInstance = null;
// EquipmentStatistics facilities
const deviceList = [
{ code: 'FPRD', name: '鱼类调查装置' },
{ code: 'AI_FISH', name: '鱼类智能识别装置' },
{ code: 'AI_ANML', name: '动物智能识别装置' },
{ code: 'AI_BIRD', name: '鸟类智能识别装置' },
{ code: 'AI_RIV', name: '河道违章智能识别装置' },
{ code: 'AI_SORTER', name: '智能分拣装置' },
{ code: 'AI_FISHING', name: '钓鱼' },
{ code: 'AI_5001', name: '漂浮物识别装置' },
{ code: 'AI_5005', name: '叠梁门运行识别装置' }
];
const chartColors = [
'#5B8FF9',
'#5AD8A6',
'#5D7092',
'#F6BD16',
'#E86452',
'#6DC8EC',
'#FF9845',
'#1E9493',
'#FF99C3'
];
// //
const initChart = () => { const initChart = () => {
chartInstance = echarts.init(chartRef.value); chartInstance = echarts.init(chartRef.value);
const option = { chartInstance.on('click', handleChartClick);
color: ['#5B8FF9', '#5AD8A6', '#5D7092'], };
tooltip: { trigger: 'axis' },
legend: { //
data: [ const handleChartClick = (params: any) => {
{ name: '鱼类调查装置' }, if (!params || !params.seriesName) return;
{ name: '鱼类智能识别装置' }, //
{ name: '动物智能识别装置' }, if (params.seriesName === 'total') return;
{ name: '鸟类智能识别装置' }, let typeCode: string | undefined;
{ name: '河道违章智能识别装置' }, let baseName: string;
{ name: '智能分拣装置' }, if (params.seriesType === 'pie') {
{ name: '钓鱼' }, // name seriesName
{ name: '漂浮物识别装置' }, typeCode = deviceList.find(d => d.name === params.name)?.code;
{ name: '叠梁门运行识别装置' } baseName = params.seriesName;
] } else {
}, // seriesName name
grid: { left: 0, right: 30, top: 60, bottom: 40, containLabel: true }, typeCode = deviceList.find(d => d.name === params.seriesName)?.code;
xAxis: { baseName = params.name;
type: 'value' }
}, if (!typeCode) return;
yAxis: { // baseId
type: 'category', const base = chartData.value.find(item => item.baseName === baseName);
// offset: -60, currentType.value = typeCode;
data: [ currentBaseId.value = base?.baseId || '';
'金沙江干流', currentBaseName.value = baseName || '';
'雅磨江干流', modalVisible.value = true;
'大渡河干流', };
'乌江干流',
'长江上游干流', //
'湘西', const handleCloseModal = () => {
'黄河上游干流', modalVisible.value = false;
'黄河中游干流', currentType.value = null;
'南盘江-红水河', currentBaseId.value = '';
'雅鲁藏布江干流' currentBaseName.value = '';
] };
},
series: [ //
const buildBarOption = (data: any[]) => {
const baseNames = data.map(item => item.baseName).reverse();
const series = deviceList.map(device => ({
name: device.name,
type: 'bar',
stack: 'total',
data: data.map(item => item.sttpCountMap[device.code] || 0).reverse()
}));
//
const totals = data
.map(item =>
deviceList.reduce((sum, d) => sum + (item.sttpCountMap[d.code] || 0), 0)
)
.reverse();
return {
color: chartColors,
tooltip: { trigger: 'item', confine: true },
legend: { data: deviceList.map(d => ({ name: d.name })), type: 'scroll' },
grid: { left: 10, right: 30, top: 50, bottom: 20, containLabel: true },
xAxis: { type: 'value', minInterval: 1 },
yAxis: { type: 'category', data: baseNames },
dataZoom: [
{ {
name: '鱼类调查装置', type: 'inside',
type: 'bar', xAxisIndex: 0,
stack: 'total', throttle: 50,
data: [12.3, 10, 8.8, 10, 7.2, 6, 15, 11, 16, 8.6, 18, 15, 20.2] zoomOnMouseWheel: true,
}, moveOnMouseMove: true,
{ moveOnMouseWheel: false,
name: '鱼类智能识别装置', start: 0,
type: 'bar', end: 100
stack: 'total',
data: [12.3, 10, 8.8, 10, 7.2, 6, 15, 11, 16, 8.6, 18, 15, 20.2]
} }
], ],
markLine: { series: [
silent: true, ...series.map(s => ({
data: [{ xAxis: '05-14', lineStyle: { color: 'red', type: 'dashed' } }] ...s,
} barMaxWidth: 30,
emphasis: {
itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.3)' }
}
})),
{
name: 'total',
type: 'bar',
stack: 'totalLabel',
barGap: '-100%',
data: totals,
barMaxWidth: 30,
itemStyle: { color: 'transparent' },
label: {
show: true,
position: 'right',
color: '#333',
fontSize: 12,
formatter: (params: any) => {
if (params.value === 0) return '';
return params.value;
}
},
silent: true,
tooltip: { show: false }
}
]
}; };
chartInstance.setOption(option); };
//
const buildPieOption = (data: any[]) => {
const base = data[0];
const pieData = deviceList
.map(d => ({ name: d.name, value: base.sttpCountMap[d.code] || 0 }))
.filter(item => item.value > 0);
return {
color: chartColors,
tooltip: { trigger: 'item' },
legend: {
type: 'scroll',
orient: 'horizontal',
top: 0,
left: 'center'
},
graphic: [
{
type: 'text',
left: 'center',
bottom: 10,
style: { text: base.baseName, fill: '#333', fontSize: 14 }
}
],
series: [
{
name: base.baseName,
type: 'pie',
radius: ['36%', '54%'],
center: ['50%', '50%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 6,
borderColor: '#fff',
borderWidth: 2
},
label: { show: true, formatter: '{b}: {c}' },
emphasis: { label: { show: true, fontWeight: 'bold' } },
data: pieData
}
]
};
};
//
const updateChart = (data: any[]) => {
chartData.value = data || [];
if (!chartInstance) return;
if (!data.length) {
chartInstance.clear();
chartInstance.setOption({
graphic: {
type: 'text',
left: 'center',
top: 'center',
style: { text: '暂无数据', fill: '#999', fontSize: 14 }
}
});
return;
}
//
const isSingleBase = baseid.value !== 'all' && data.length === 1;
const option = isSingleBase ? buildPieOption(data) : buildBarOption(data);
chartInstance.setOption(option, true);
// / 0 resize ~1s
const doResize = () => chartInstance?.resize();
requestAnimationFrame(doResize);
setTimeout(doResize, 100);
setTimeout(doResize, 400);
}; };
// 0ECharts 100px // 0ECharts 100px
@ -79,6 +255,58 @@ let resizeObserver = null;
const handleResize = () => { const handleResize = () => {
chartInstance?.resize(); chartInstance?.resize();
}; };
const getDataList = async () => {
loading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
baseid.value !== 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
}
: null,
{
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: [
'AI_FISH',
'AI_ANML',
'AI_BIRD',
'AI_RIV',
'AI_FISHING',
'AI_5001',
'AI_5005',
'AI_SORTER',
'FPRD'
]
}
].filter(Boolean)
}
};
const res = await imeBaseDeviceStat(params);
updateChart(res.data.data);
} catch (error) {
console.error('获取设备统计数据失败:', error);
} finally {
loading.value = false;
}
};
watch(
() => jidiSelectEventStore.selectedItem,
newVal => {
baseid.value = newVal.wbsCode;
getDataList();
},
{ deep: true }
);
onMounted(() => { onMounted(() => {
initChart(); initChart();
@ -86,6 +314,9 @@ onMounted(() => {
if (chartRef.value) { if (chartRef.value) {
resizeObserver.observe(chartRef.value); resizeObserver.observe(chartRef.value);
} }
// 'all'
baseid.value = jidiSelectEventStore.selectedItem?.wbsCode || 'all';
getDataList();
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -7,6 +7,7 @@
v-for="facility in facilities" v-for="facility in facilities"
:key="facility.name" :key="facility.name"
class="facility-card" class="facility-card"
@click="handleFacilityClick(facility)"
> >
<div <div
style=" style="
@ -32,12 +33,31 @@
</div> </div>
</div> </div>
</a-spin> </a-spin>
<!-- 弹框 -->
<a-modal
v-model:open="modalVisible"
title="设备统计"
:footer="null"
width="1536px"
@cancel="handleCloseModal"
>
<ZHZB
v-if="currentFacility"
:baseId="baseid"
:type="currentFacility.code"
/>
</a-modal>
</SidePanelItem> </SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { vmsstbprptGetKendoListWte } from '@/api/sz';
import ZHZB from '@/modules/ZHZB/index.vue';
import { useDraggable } from '@/utils/drag';
import equipment1 from '@/assets/svg/equipment1.svg'; import equipment1 from '@/assets/svg/equipment1.svg';
import equipment2 from '@/assets/svg/equipment2.svg'; import equipment2 from '@/assets/svg/equipment2.svg';
import equipment3 from '@/assets/svg/equipment3.svg'; import equipment3 from '@/assets/svg/equipment3.svg';
@ -48,12 +68,33 @@ import equipment7 from '@/assets/svg/equipment7.svg';
import equipment8 from '@/assets/svg/equipment8.svg'; import equipment8 from '@/assets/svg/equipment8.svg';
import equipment9 from '@/assets/svg/equipment9.svg'; import equipment9 from '@/assets/svg/equipment9.svg';
const jidiSelectEventStore = useJidiSelectEventStore();
// Loading // Loading
const loading = ref(false); const loading = ref(false);
// ID
const baseid = ref('');
//
const modalVisible = ref(false);
const currentFacility = ref<any>(null);
useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
//
const handleFacilityClick = (facility: any) => {
currentFacility.value = facility;
modalVisible.value = true;
};
//
const handleCloseModal = () => {
modalVisible.value = false;
currentFacility.value = null;
};
// //
const facilities = ref([ const facilities = ref([
{ {
name: '鱼类调查装置', name: '鱼类调查装置',
code: 'FPRD',
count: 0, count: 0,
icon: equipment1, icon: equipment1,
key: '26' key: '26'
@ -61,52 +102,155 @@ const facilities = ref([
{ {
name: '鱼类智能识别装置', name: '鱼类智能识别装置',
count: 0, count: 0,
code: 'AI_FISH',
icon: equipment2, icon: equipment2,
key: '27' key: '27'
}, },
{ {
name: '动物智能识别装置', name: '动物智能识别装置',
count: 0, count: 0,
code: 'AI_ANML',
icon: equipment3, icon: equipment3,
key: '28' key: '28'
}, },
{ {
name: '鸟类智能识别装置', name: '鸟类智能识别装置',
count: 0, count: 0,
code: 'AI_BIRD',
icon: equipment4, icon: equipment4,
key: '29' key: '29'
}, },
{ {
name: '河道违章智能识别装置', name: '河道违章智能识别装置',
count: 0, count: 0,
code: 'AI_RIV',
icon: equipment5, icon: equipment5,
key: '29' key: '29'
}, },
{ {
name: '智能分拣装置', name: '智能分拣装置',
count: 0, count: 0,
code: 'AI_SORTER',
icon: equipment6, icon: equipment6,
key: '29' key: '29'
}, },
{ {
name: '钓鱼', name: '钓鱼',
count: 0, count: 0,
code: 'AI_FISHING',
icon: equipment7, icon: equipment7,
key: '30' key: '30'
}, },
{ {
name: '漂浮物识别装置', name: '漂浮物识别装置',
count: 0, count: 0,
code: 'AI_5001',
icon: equipment8, icon: equipment8,
key: '30' key: '30'
}, },
{ {
name: '叠梁门运行识别装置', name: '叠梁门运行识别装置',
count: 0, count: 0,
code: 'AI_5005',
icon: equipment9, icon: equipment9,
key: '31' key: '31'
} }
]); ]);
// sttpCode
const sttpCodeToNamePattern: Record<string, string> = {
AI_FISH: '鱼类',
AI_ANML: '动物',
AI_BIRD: '鸟类',
AI_RIV: '河道',
AI_FISHING: '钓鱼',
AI_5001: '漂浮物识别装置',
AI_5005: '叠梁门运行识别装置',
AI_SORTER: '智能分拣装置',
FPRD: '鱼类调查装置'
};
//
const getDataList = async () => {
loading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
baseid.value !== 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
}
: null,
{
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: [
'AI_FISH',
'AI_ANML',
'AI_BIRD',
'AI_RIV',
'AI_FISHING',
'AI_5001',
'AI_5005',
'AI_SORTER',
'FPRD'
]
}
].filter(Boolean)
},
group: [
{
dir: 'desc',
field: 'sttpCode'
}
],
groupResultFlat: true
};
const res = await vmsstbprptGetKendoListWte(params);
if (res.data) {
const responseData = res?.data?.data || res?.data;
// count 0
const finalData = facilities.value.map(item => ({ ...item, count: 0 }));
if (Array.isArray(responseData)) {
responseData.forEach((item: any) => {
const pattern = sttpCodeToNamePattern[item.sttpCode];
if (pattern) {
// pattern count
const matched = finalData.filter(f => f.code === item.sttpCode);
if (matched.length > 0) {
matched.forEach(f => {
f.count = item.count_sttpCode;
});
}
}
});
}
facilities.value = finalData;
}
} catch (error) {
console.error('获取设备统计数据失败:', error);
} finally {
loading.value = false;
}
};
watch(
() => jidiSelectEventStore.selectedItem,
newVal => {
baseid.value = newVal.wbsCode;
getDataList();
},
{ deep: true, immediate: true }
);
onMounted(() => {});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,430 @@
<template>
<div class="zhzb-container">
<!-- 搜索表单 -->
<div class="search-form">
<a-space size="middle">
<div>选择水电站:</div>
<a-select
v-model:value="searchData.stcd.dataDimensionData"
placeholder="请选择水电基地"
style="width: 200px"
show-search
:filter-option="filterOption"
:options="hydropowerStationOptions"
@change="handleHydropowerStationChange"
/>
<a-select
v-model:value="searchData.stnm"
placeholder="请选择电站"
style="width: 200px"
allow-clear
show-search
:filter-option="filterOption"
:options="stationOptions"
@change="handleStationChange"
/>
<a-select
v-model:value="searchData.type"
placeholder="请选择装备类型"
style="width: 160px"
allow-clear
:options="equipmentTypeOptions"
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-tooltip title="重置">
<a-button @click="handleReset">重置</a-button>
</a-tooltip>
</a-space>
</div>
<!-- 数据表格 -->
<a-table
:columns="columns"
:data-source="tableData"
:loading="loading"
:pagination="pagination"
:scroll="{ y: tableHeight }"
row-key="stcd"
@change="handleTableChange"
>
<template #bodyCell="{ column, record }">
<!-- 操作 -->
<template v-if="column.key === 'action'">
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
</template>
</template>
</a-table>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue';
import { wbsbGetKendoList, getVmsstbprpt } from '@/api/sw';
import { vmsstbprptGetKendoListWte } from '@/api/sz';
import { useModelStore } from '@/store/modules/model';
const modelStore = useModelStore();
// - sttpCode
const SmartEquipmentCodes = [
'AI_FISH',
'AI_ANML',
'AI_BIRD',
'AI_RIV',
'AI_FISHING',
'AI_5001',
'AI_5005',
'AI_SORTER',
'FPRD'
];
const sttpCodeToName: Record<string, string> = {
FPRD: '鱼类调查装置',
AI_FISH: '鱼类智能识别装置',
AI_ANML: '动物智能识别装置',
AI_BIRD: '鸟类智能识别装置',
AI_RIV: '河道违章智能识别装置',
AI_SORTER: '智能分拣装置',
AI_FISHING: '钓鱼',
AI_5001: '漂浮物识别装置',
AI_5005: '叠梁门运行识别装置'
};
const equipmentTypeOptions = computed(() => {
return Object.keys(sttpCodeToName).map(code => ({
label: sttpCodeToName[code] || code,
value: code
}));
});
// ==================== Props ====================
const props = defineProps<{
baseId?: string;
type?: string;
}>();
// ==================== ====================
const loading = ref(false);
const tableData = ref<any[]>([]);
const pagination = ref({
current: 1,
pageSize: 20,
total: 0,
size: 'small',
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`,
pageSizeOptions: ['10', '20', '50', '100']
});
const tableHeight = 'calc(48vh)';
//
const hydropowerStationList = ref<any[]>([]);
const stationDataList: any = ref([]);
// a-select options
const hydropowerStationOptions = computed(() => {
return hydropowerStationList.value.map(station => ({
label: station.wbsName,
value: station.wbsCode
}));
});
const stationOptions = computed(() => {
return stationDataList.value.map(station => ({
label: station.stnm,
value: station.stcd
}));
});
const searchData = ref<any>({
stcd: {
dataDimensionType: 'hyBase',
stcdId: '',
dataDimensionData: props.baseId || 'all'
},
stnm: null,
type: props.type || null
});
// ==================== ====================
const columns = computed(() => {
return [
{
title: '监控点名称',
dataIndex: 'stnm',
key: 'stnm'
},
{
title: '基地名称',
dataIndex: 'baseName',
key: 'baseName'
},
{
title: '站址',
dataIndex: 'stlc',
key: 'stlc'
},
{
title: '操作',
key: 'action'
}
];
});
// ==================== ====================
const buildSearchFilter = () => {
const filters: any[] = [];
// ID
if (
searchData.value?.stcd?.dataDimensionData &&
searchData.value.stcd.dataDimensionData !== 'all'
) {
filters.push({
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: searchData.value.stcd.dataDimensionData
});
}
//
if (searchData.value?.stnm) {
filters.push({
field: 'rstcd',
operator: 'eq',
dataType: 'string',
value: searchData.value.stnm
});
}
//
if (searchData.value?.type) {
filters.push({
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: searchData.value.type
});
} else {
filters.push({
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: SmartEquipmentCodes
});
}
return {
logic: 'and',
filters: filters.filter(
f => f.value !== undefined && f.value !== null && f.value !== ''
)
};
};
// ==================== API ====================
const fetchData = async () => {
loading.value = true;
try {
const filter = buildSearchFilter();
const params = {
take: pagination.value.pageSize,
skip: pagination.value.current,
filter: filter,
groupResultFlat: true
};
const res = await vmsstbprptGetKendoListWte(params);
//
const records = res?.data?.data || res?.data;
console.log(res);
const total = res?.data.total || 0;
tableData.value = records || [];
pagination.value.total = total;
} catch (error) {
console.error('获取设备统计数据失败:', error);
tableData.value = [];
pagination.value.total = 0;
} finally {
loading.value = false;
}
};
const getselect = 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 wbsbGetKendoList(params);
//
if (res?.data?.data && Array.isArray(res.data.data)) {
hydropowerStationList.value = res.data.data;
getselectTwo(searchData.value.stcd.dataDimensionData);
} else {
hydropowerStationList.value = [];
}
} catch (error) {
console.error('获取水电基地列表失败:', error);
hydropowerStationList.value = [];
}
};
const getselectTwo = async (code: any) => {
const params = {
filter: {
logic: 'and',
filters: [
code != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: code
}
: null,
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'ENG'
}
].filter(Boolean)
},
select: ['stcd', 'stnm']
};
const res = await getVmsstbprpt(params);
stationDataList.value = res?.data?.data || res?.data;
};
// ==================== ====================
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const handleHydropowerStationChange = (value: string) => {
//
searchData.value.stnm = null;
searchData.value.stcd.stcdId = '';
//
getselectTwo(value);
};
const handleStationChange = (value: string | undefined) => {
if (value) {
searchData.value.stcd.stcdId = value;
} else {
searchData.value.stcd.stcdId = '';
}
};
const handleSearch = () => {
pagination.value.current = 1;
fetchData();
};
const handleReset = () => {
searchData.value = {
stcd: {
dataDimensionType: 'hyBase',
stcdId: '',
dataDimensionData: props.baseId || 'all'
},
stnm: null,
type: props.type || null
};
pagination.value.current = 1;
fetchData();
};
const handleTableChange = (pag: any) => {
pagination.value.current = pag.current;
pagination.value.pageSize = pag.pageSize;
fetchData();
};
const handleViewDetail = (record: any) => {
modelStore.modalVisible = true;
modelStore.params = record;
modelStore.title = record.stnm;
};
// ==================== Props ====================
watch(
() => props.baseId,
newVal => {
if (newVal && newVal !== searchData.value.stcd.dataDimensionData) {
searchData.value.stcd.dataDimensionData = newVal;
getselectTwo(newVal);
fetchData();
}
}
);
watch(
() => props.type,
newVal => {
searchData.value.type = newVal || null;
fetchData();
}
);
// ==================== ====================
onMounted(() => {
fetchData();
getselect();
});
</script>
<style scoped lang="scss">
.zhzb-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_hocer {
color: #2f6b98;
}
.text_hocer:hover {
color: #40a9ff;
}
</style>

View File

@ -423,18 +423,18 @@ const FPRDInfo = [
}, },
{ {
label: '监控点类型', label: '监控点类型',
value: 'stindx', value: 'sttpName',
col: 24
},
{
label: '监控方式',
value: 'mwayName',
col: 24 col: 24
}, },
{ {
label: '监控点地址', label: '监控点地址',
value: 'stlc', value: 'stlc',
col: 24 col: 24
},
{
label: '所属流域',
value: 'hbrvcdName',
col: 24
} }
]; ];
export { export {

View File

@ -58,7 +58,7 @@ import { getStcdDetail } from '@/api/mapModal';
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL; const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
const modelStore = useModelStore(); const modelStore = useModelStore();
const info = ref({}); const info: any = ref({});
const formDiv = ref(null); const formDiv = ref(null);
const logo = ref(''); const logo = ref('');
const loading = ref(false); const loading = ref(false);
@ -82,7 +82,15 @@ const moduleMap = {
AIVDInfo: AIVDInfo, AIVDInfo: AIVDInfo,
WEInfo: WEInfo, WEInfo: WEInfo,
WVAInfo: WVAInfo, WVAInfo: WVAInfo,
FPRDInfo: FPRDInfo FPRDInfo: FPRDInfo,
AI_FISHInfo: FPRDInfo,
AI_ANMLInfo: FPRDInfo,
AI_BIRDInfo: FPRDInfo,
AI_RIVInfo: FPRDInfo,
AI_FISHINGInfo: FPRDInfo,
AI_5001Info: FPRDInfo,
AI_5005Info: FPRDInfo,
AI_SORTERInfo: FPRDInfo
} as const; } as const;
watch( watch(
() => modelStore.selectedAnchorPoint, () => modelStore.selectedAnchorPoint,
@ -104,12 +112,16 @@ watch(
if (data.sttpCode == 'ENG') { if (data.sttpCode == 'ENG') {
data.sttp = data.sttpCode; data.sttp = data.sttpCode;
} }
if (data.sttp && data.sttp.includes('_')) { if (
data.sttp &&
data.sttp.includes('_') &&
!data.sttp.startsWith('AI_')
) {
const code = data.sttp.split('_')[0]; const code = data.sttp.split('_')[0];
formDiv.value = moduleMap[code + 'Info'] || null; formDiv.value = moduleMap[code + 'Info'] || null;
// debugger // debugger
} else { } else {
// 线 // AI_线 sttp
formDiv.value = moduleMap[data.sttp + 'Info']; formDiv.value = moduleMap[data.sttp + 'Info'];
} }

View File

@ -0,0 +1,509 @@
<template>
<SidePanelItem title="监测数据" :shrink="false">
<template #title-right-content>
<a-range-picker
class="w-[220px]"
v-model:value="dateRange"
format="YYYY-MM-DD"
:show-time="showTimeConfig"
:allow-clear="false"
:presets="DateSetting.RangeButton.month1"
:disabled-date="disabledDate"
size="small"
@change="handleSearch"
/>
</template>
<a-spin :spinning="loading" tip="加载中...">
<a-carousel
v-if="mediaList.length > 0"
ref="carouselRef"
arrows
class="media-carousel"
@beforeChange="onBeforeSlideChange"
@afterChange="onAfterSlideChange"
>
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
<div v-for="(item, index) in mediaList" :key="index" class="slide">
<div class="media-wrapper">
<a-image
v-if="item.imageUrl"
:src="item.imageUrl"
class="media-image"
:preview="{
visible: previewVisible === index,
onVisibleChange: (v: boolean) => {
previewVisible = v ? index : -1;
}
}"
@click="previewVisible = index"
/>
<!-- 视频仅挂载当前活跃项其余显示占位避免多个视频同时拉流/一起报错重试导致卡顿 -->
<LiveVideoBox
v-else-if="item.videoUrl && index === activeIndex"
:videoData="{ url: item.videoUrl }"
:showTitle="false"
height="100%"
/>
<div v-else-if="item.videoUrl" class="video-placeholder">
<i class="iconfont icon-jiankong"></i>
</div>
</div>
<div class="media-overlay">
<span class="overlay-time">{{ item.tm }}</span>
<a-button
type="primary"
class="overlay-btn"
size="small"
@click="handleDetail(item)"
>
详细信息
</a-button>
</div>
</div>
</a-carousel>
<a-empty v-else description="暂无数据" />
</a-spin>
</SidePanelItem>
</template>
<script lang="ts" setup>
import dayjs, { Dayjs } from 'dayjs';
import { ref, watch, nextTick } from 'vue';
import { useModelStore } from '@/store/modules/model';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { DateSetting } from '@/utils/enumeration';
import {
getAnimalList,
getWarningList,
getFishList,
getIdUrl
} from '@/api/mapModal';
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
const props = defineProps({
sttpValue: {
type: String,
default: ''
}
});
const modelStore = useModelStore();
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
const loading = ref(false);
const mediaList = ref<
{ tm: string; imageUrl: string; videoUrl: string; raw: any }[]
>([]);
const previewVisible = ref(-1);
//
const activeIndex = ref(0);
// ==================== ====================
const carouselRef = ref<any>(null);
let slideTimer: ReturnType<typeof setTimeout> | null = null;
const SLIDE_INTERVAL_IMAGE = 5000;
const SLIDE_TIMEOUT_VIDEO_ERROR = 15000;
const clearSlideTimer = () => {
if (slideTimer !== null) {
clearTimeout(slideTimer);
slideTimer = null;
}
};
const advanceToNext = () => {
carouselRef.value?.next();
};
const handleVideoPlay = () => {
//
clearSlideTimer();
};
const handleVideoError = () => {
//
clearSlideTimer();
slideTimer = setTimeout(advanceToNext, SLIDE_TIMEOUT_VIDEO_ERROR);
};
const setupAutoAdvance = () => {
clearSlideTimer();
if (mediaList.value.length <= 1) return;
try {
const el = carouselRef.value?.$el;
if (!el) return;
const activeSlide = el.querySelector('.slick-active');
if (!activeSlide) return;
const video = activeSlide.querySelector('video.video-js');
if (video) {
// ended
video.addEventListener('ended', advanceToNext);
video.addEventListener('play', handleVideoPlay);
video.addEventListener('error', handleVideoError);
} else {
//
slideTimer = setTimeout(advanceToNext, SLIDE_INTERVAL_IMAGE);
}
} catch (e) {
// ignore
}
};
const cleanupAutoAdvance = () => {
clearSlideTimer();
try {
const el = carouselRef.value?.$el;
if (el) {
const activeSlide = el.querySelector('.slick-active');
if (activeSlide) {
const video = activeSlide.querySelector('video');
if (video) {
video.removeEventListener('ended', advanceToNext);
video.removeEventListener('play', handleVideoPlay);
video.removeEventListener('error', handleVideoError);
}
}
}
} catch (e) {
// ignore
}
};
const onBeforeSlideChange = (current: number, next: number) => {
cleanupAutoAdvance();
//
if (typeof next === 'number') {
activeIndex.value = next;
}
};
const onAfterSlideChange = (current: number) => {
if (typeof current === 'number') {
activeIndex.value = current;
}
nextTick(setupAutoAdvance);
};
//
watch(mediaList, newVal => {
if (newVal.length > 0) {
activeIndex.value = 0;
nextTick(setupAutoAdvance);
} else {
cleanupAutoAdvance();
}
});
// ==================== ====================
const showTimeConfig = {
format: 'HH:mm',
hourStep: 1,
minuteStep: 5,
secondStep: 60
};
const disabledDate = (current: Dayjs) =>
current && current.isAfter(dayjs(), 'day');
const initDateRange = (): [Dayjs, Dayjs] => {
return [dayjs().subtract(1, 'month'), dayjs()];
};
const dateRange = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
// sttpValue
const getApiBySttp = () => {
const sttp = props.sttpValue;
if (sttp === 'AI_ANML' || sttp === 'AI_BIRD') return getAnimalList;
if (
sttp === 'AI_FISHING' ||
sttp === 'AI_RIV' ||
sttp === 'AI_5001' ||
sttp === 'AI_5005'
)
return getWarningList;
return getFishList;
};
// getWarningList
const isWarningType = () => {
const sttp = props.sttpValue;
return (
sttp === 'AI_FISHING' ||
sttp === 'AI_RIV' ||
sttp === 'AI_5001' ||
sttp === 'AI_5005'
);
};
// getAnimalList
const isAnimalType = () => {
const sttp = props.sttpValue;
return sttp === 'AI_ANML' || sttp === 'AI_BIRD';
};
// raw mediaItem
const toMediaItem = (raw: any, imageUrl?: string, videoUrl?: string) => ({
tm: raw.tm ? dayjs(raw.tm).format('YYYY-MM-DD HH:mm:ss') : '-',
imageUrl: imageUrl || '',
videoUrl: videoUrl || '',
raw
});
//
const fetchData = async () => {
const stcd = modelStore.selectedAnchorPoint?.stcd;
if (!dateRange.value || !stcd) return;
loading.value = true;
mediaList.value = [];
previewVisible.value = -1;
try {
const api = getApiBySttp();
const params = {
filter: {
logic: 'and',
filters: [
{ field: 'stcd', operator: 'eq', dataType: 'string', value: stcd },
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dateRange.value[0].format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dateRange.value[1].format('YYYY-MM-DD HH:mm:ss')
}
]
},
sort: [{ field: 'tm', dir: 'asc' }]
};
const res = await api(params);
const rawData = res?.data?.data || res?.data?.records || [];
if (isWarningType()) {
// getWarningListfid getIdUrl
const fids = rawData.map((item: any) => item.fid).filter(Boolean);
if (fids.length === 0) {
mediaList.value = rawData.map((item: any) => toMediaItem(item));
return;
}
const idRes: any = await getIdUrl({ id: fids.join(',') });
const urlMap = idRes?.data || {};
mediaList.value = rawData.flatMap((item: any) => {
const fidStr = item.fid;
if (!fidStr) return [toMediaItem(item)];
// fid ID
const fidList = String(fidStr).split(',').filter(Boolean);
if (fidList.length === 0) return [toMediaItem(item)];
return fidList.map((fid: string) => {
const fidKey = fid.trim();
const fileInfo = urlMap[fidKey];
if (!fileInfo) return toMediaItem(item);
const ext = String(fileInfo.ext || '').toLowerCase();
const isImage = [
'.jpg',
'.jpeg',
'.png',
'.gif',
'.webp',
'.bmp'
].includes(ext);
const isVideo = ['.mp4', '.mov', '.avi', '.wmv', '.m3u8'].includes(
ext
);
const fullpath = fidKey ? `${baseUrl}/?${fidKey}` : '';
return toMediaItem(
item,
isImage ? fullpath : '',
isVideo ? fullpath : ''
);
});
});
} else if (isAnimalType()) {
// getAnimalListimgPath, flpth
mediaList.value = rawData.map((item: any) => {
const imgPath = item.imgPath || '';
const flpth = item.flpth || '';
const imageUrl = imgPath
? imgPath.startsWith('http')
? imgPath
: `${baseUrl}/?${imgPath}`
: '';
const videoUrl = flpth
? flpth.startsWith('http')
? flpth
: `${baseUrl}/?${flpth}`
: '';
return toMediaItem(item, imageUrl, videoUrl);
});
} else {
// getFishListfirstimgurl, videourl
mediaList.value = rawData.map((item: any) => {
const firstimgurl = item.firstimgurl || '';
const videourl = item.videourl || '';
const imageUrl = firstimgurl
? firstimgurl.startsWith('http')
? firstimgurl
: `${baseUrl}/?${firstimgurl}`
: '';
const videoUrl = videourl
? videourl.startsWith('http')
? videourl
: `${baseUrl}/?${videourl}`
: '';
return toMediaItem(item, imageUrl, videoUrl);
});
}
} catch (error) {
console.error('获取数据失败:', error);
} finally {
loading.value = false;
}
};
//
const handleDetail = (item: any) => {
console.log('详情', item);
};
// change
const handleSearch = () => fetchData();
watch(
() => modelStore.selectedAnchorPoint?.stcd,
(newStcd, oldStcd) => {
if (!newStcd) return;
if (newStcd !== oldStcd) {
dateRange.value = initDateRange();
fetchData();
}
},
{ immediate: true }
);
//
watch(
() => modelStore.filter.rangeTm,
newRange => {
if (Array.isArray(newRange) && newRange.length > 0) {
dateRange.value = [dayjs(newRange[0]), dayjs(newRange[1])];
}
},
{ deep: true }
);
</script>
<style lang="scss" scoped>
.media-carousel {
width: 400px;
height: 250px;
:deep(.slick-slide) {
height: 250px;
overflow: hidden;
}
:deep(.slick-slide > div) {
height: 100%;
}
:deep(.slick-arrow.custom-slick-arrow) {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
transition: ease all 0.3s;
opacity: 0.5;
z-index: 1;
}
:deep(.slick-arrow.custom-slick-arrow:before) {
display: none;
}
:deep(.slick-arrow.custom-slick-arrow:hover) {
color: #fff;
opacity: 0.5;
}
}
.slide {
position: relative;
height: 250px;
background: #000;
}
.video-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #000;
color: #666;
i {
font-size: 48px;
opacity: 0.5;
}
}
.media-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.media-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
cursor: zoom-in;
}
}
.media-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 36px;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
z-index: 10;
.overlay-time {
color: #fff;
font-size: 13px;
}
.overlay-btn {
font-size: 12px;
}
}
</style>

View File

@ -26,12 +26,19 @@
<FlowDischarge v-if="sttpValue == 'EQ'" /> <FlowDischarge v-if="sttpValue == 'EQ'" />
<!-- 鱼类增殖站 --> <!-- 鱼类增殖站 -->
<NormalOperationData v-if="sttpValue == 'FB'" /> <NormalOperationData v-if="sttpValue == 'FB'" />
<!-- 智慧装备 -->
<SmartEquipment
:sttpValue="sttpValue"
v-if="SmartEquipmentCodes.includes(sttpValue)"
/>
<!-- 实时视频 --> <!-- 实时视频 -->
<VideoInfo <VideoInfo
title="实时视频" title="实时视频"
placeholder="请选择视频" placeholder="请选择视频"
v-if="sttpValue == 'FB'" v-if="sttpValue == 'FB' || SmartEquipmentCodes.includes(sttpValue)"
/> />
<!-- 监测数据 -->
<MonitorData v-if="sttpValue == 'WE'" />
<!-- 生态调查 --> <!-- 生态调查 -->
<!-- <WaterTemperature v-if="sttpValue == 'WE'" /> --> <!-- <WaterTemperature v-if="sttpValue == 'WE'" /> -->
@ -54,10 +61,24 @@ import Animal from '@/modules/rightSearchDrawer/monitoringTable/components/Anima
import FlowDischarge from '@/modules/rightSearchDrawer/monitoringTable/components/FlowDischarge.vue'; import FlowDischarge from '@/modules/rightSearchDrawer/monitoringTable/components/FlowDischarge.vue';
import NormalOperationData from '@/modules/rightSearchDrawer/monitoringTable/components/NormalOperationData.vue'; import NormalOperationData from '@/modules/rightSearchDrawer/monitoringTable/components/NormalOperationData.vue';
import VideoInfo from '@/modules/rightSearchDrawer/monitoringTable/components/VideoInfo.vue'; import VideoInfo from '@/modules/rightSearchDrawer/monitoringTable/components/VideoInfo.vue';
import SmartEquipment from '@/modules/rightSearchDrawer/monitoringTable/components/SmartEquipment.vue';
import MonitorData from '@/modules/rightSearchDrawer/speciesIntro/monitorData.vue';
const modelStore = useModelStore(); const modelStore = useModelStore();
const sttpValue = ref(''); const sttpValue = ref('');
// - sttpCode
const SmartEquipmentCodes = [
'AI_FISH',
'AI_ANML',
'AI_BIRD',
'AI_RIV',
'AI_FISHING',
'AI_5001',
'AI_5005',
'AI_SORTER',
'FPRD'
];
// //
watch( watch(
() => modelStore.selectedAnchorPoint, () => modelStore.selectedAnchorPoint,

View File

@ -0,0 +1,257 @@
<template>
<SidePanelItem :title="panelTitle" :shrink="false">
<template #title-right-content>
<a-select
v-model:value="currentValue"
size="small"
style="width: 110px"
placeholder="请选择"
:options="speciesOptions"
/>
</template>
<a-empty v-if="!currentValue" description="请先在上方选择物种" />
<a-spin v-else :spinning="loading" class="species-spin">
<div class="species-card">
<!-- 上方图片轮播 -->
<div
class="species-image"
v-if="currentSpeciesData.imageList.length > 0"
>
<a-carousel
arrows
v-if="currentSpeciesData.imageList.length > 1"
class="species-carousel"
>
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
<div
v-for="(img, index) in currentSpeciesData.imageList"
:key="index"
>
<a-image :src="img" height="300px" class="species-image-img" />
</div>
</a-carousel>
</div>
<div v-else class="species-image species-image--empty">
<span>暂无图片</span>
</div>
<!-- 下方文字描述 -->
<div class="species-info">
<div class="species-name">
{{ currentSpeciesData.name || currentLabel }}
</div>
<div class="species-desc">
{{ currentSpeciesData.shapedesc || '暂无描述' }}
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useModelStore } from '@/store/modules/model';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getFishResourceZYList } from '@/api/DataQueryMenuModule';
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
const modelStore = useModelStore();
const route = useRoute();
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
//
const panelTitle = computed(() => {
if (route.path.includes('shengTaiDiaoCha/shuiShengShengTaiDiaoCha')) {
return '鱼类介绍';
}
if (route.path.includes('shengTaiDiaoCha/luShengShengTaiDiaoCha')) {
return '动物介绍';
}
return '物种介绍';
});
// =
const speciesOptions = computed(() =>
(modelStore.selectedFishSpecies || []).map(item => ({
label: item.label,
value: item.value
}))
);
const currentValue = ref<string | undefined>();
const loading = ref(false);
const currentSpeciesData = ref<{
name: string;
imageList: string[];
shapedesc: string;
}>({
name: '',
imageList: [],
shapedesc: ''
});
// label
const currentLabel = computed(() => {
const matched = speciesOptions.value.find(
item => item.value === currentValue.value
);
return matched?.label || '';
});
//
const fetchSpeciesData = async (ftp: string) => {
if (!ftp) return;
loading.value = true;
try {
const res = await getFishResourceZYList({
filter: {
logic: 'and',
filters: [
{ field: 'id', operator: 'eq', dataType: 'string', value: ftp }
]
}
});
const data = res?.data?.data?.[0];
if (data) {
let imageList: string[] = [];
if (data.inffile && data.inffile !== '-') {
imageList = data.inffile
.split(',')
.map((item: string) => item.trim())
.filter((item: string) => item && item !== '-' && item.length > 0)
.map((item: string) => `${baseUrl}/?${item}&view=jpg`);
}
currentSpeciesData.value = {
name: data.name || '',
imageList,
shapedesc: data.shapedesc || ''
};
} else {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
}
} catch {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
} finally {
loading.value = false;
}
};
//
watch(
() => modelStore.selectedFishSpecies,
list => {
const items = list || [];
//
if (
currentValue.value &&
items.some(item => item.value === currentValue.value)
) {
return;
}
//
const firstValue = items[0]?.value;
currentValue.value = firstValue;
if (firstValue) {
fetchSpeciesData(firstValue);
} else {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
}
},
{ immediate: true, deep: true }
);
//
watch(currentValue, (newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
fetchSpeciesData(newVal);
}
});
</script>
<style lang="scss" scoped>
.species-spin {
display: block;
}
.species-card {
border: 1px solid #e5edf3;
border-radius: 4px;
overflow: hidden;
}
.species-image {
width: 100%;
overflow: hidden;
background: #f5f5f5;
}
.species-image--empty {
display: flex;
align-items: center;
justify-content: center;
color: #bfbfbf;
font-size: 14px;
}
.species-carousel {
height: 300px;
}
.species-image-img {
width: 100%;
object-fit: cover;
}
.species-info {
padding: 10px 12px 12px;
}
.species-name {
font-size: 15px;
font-weight: 500;
color: #2f6b98;
margin-bottom: 6px;
}
.species-desc {
font-size: 13px;
line-height: 22px;
color: #595959;
word-break: break-all;
white-space: pre-wrap;
}
:deep(.slick-slide) {
text-align: center;
overflow: hidden;
}
:deep(.slick-arrow.custom-slick-arrow) {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
transition: ease all 0.3s;
opacity: 0.5;
z-index: 1;
}
:deep(.slick-arrow.custom-slick-arrow:before) {
display: none;
}
:deep(.slick-arrow.custom-slick-arrow:hover) {
color: #fff;
opacity: 0.5;
}
</style>

View File

@ -1,128 +1,13 @@
<!--
物种介绍动物/植物介绍面板
- 面板标题根据当前生态调查菜单动态展示水生生态调查鱼类介绍陆生生态调查动物介绍
- 右上角下拉框title-right-content 插槽选项为上方鱼种/动物种多选框已选中的数据默认展示第一条
- 主体区域只展示当前选中物种上方图片下方文字描述
- 数据来自 modelStore.selectedFishSpecies预留 image / intro 字段后续对接真实数据
-->
<template> <template>
<SidePanelItem :title="panelTitle" :shrink="false"> <div>
<template #title-right-content> <Fish />
<a-select <MonitorData />
v-model:value="currentValue" </div>
size="small"
style="width: 110px"
placeholder="请选择"
:options="speciesOptions"
/>
</template>
<a-empty v-if="!currentSpecies" description="请先在上方选择物种" />
<div v-else class="species-card">
<!-- 上方图片 -->
<a-image
v-if="currentSpecies.image"
class="species-image"
:src="currentSpecies.image"
:preview="false"
/>
<div v-else class="species-image species-image--empty">
<span>暂无图片</span>
</div>
<!-- 下方文字描述 -->
<div class="species-info">
<div class="species-name">{{ currentSpecies.label }}</div>
<div class="species-desc">
{{ currentSpecies.intro || '暂无描述' }}
</div>
</div>
</div>
</SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from 'vue'; import Fish from '@/modules/rightSearchDrawer/speciesIntro/fish.vue';
import { useRoute } from 'vue-router'; import MonitorData from '@/modules/rightSearchDrawer/speciesIntro/monitorData.vue';
import { useModelStore } from '@/store/modules/model';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
const modelStore = useModelStore();
const route = useRoute();
//
const panelTitle = computed(() => {
if (route.path.includes('shengTaiDiaoCha/shuiShengShengTaiDiaoCha')) {
return '鱼类介绍';
}
if (route.path.includes('shengTaiDiaoCha/luShengShengTaiDiaoCha')) {
return '动物介绍';
}
return '物种介绍';
});
// =
const speciesOptions = computed(() =>
(modelStore.selectedFishSpecies || []).map(item => ({
label: item.label,
value: item.value
}))
);
const currentValue = ref<string | undefined>();
//
const currentSpecies = computed(() =>
speciesOptions.value.find(item => item.value === currentValue.value)
);
//
watch(
() => modelStore.selectedFishSpecies,
list => {
currentValue.value = (list || [])[0]?.value;
},
{ immediate: true, deep: true }
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
.species-card {
border: 1px solid #e5edf3;
border-radius: 4px;
overflow: hidden;
}
.species-image {
display: block;
width: 100%;
height: 200px;
object-fit: cover;
background: #f5f5f5;
}
.species-image--empty {
display: flex;
align-items: center;
justify-content: center;
color: #bfbfbf;
font-size: 14px;
}
.species-info {
padding: 10px 12px 12px;
}
.species-name {
font-size: 15px;
font-weight: 500;
color: #2f6b98;
margin-bottom: 6px;
}
.species-desc {
font-size: 13px;
line-height: 22px;
color: #595959;
word-break: break-all;
white-space: pre-wrap;
}
</style>

View File

@ -0,0 +1,236 @@
<template>
<SidePanelItem title="监测数据" :shrink="false">
<template #title-right-content>
<a-select
v-model:value="currentValue"
size="small"
style="width: 80px"
placeholder="请选择"
:options="speciesOptions"
/>
<a-select
v-model:value="currentValue"
size="small"
style="width: 80px"
placeholder="请选择"
:options="speciesOptions"
/>
<a-date-picker
class="w-[70px]"
v-model:value="dateRange"
picker="year"
format="YYYY"
value-format="YYYY"
:allowClear="false"
size="small"
@change="handleSearch"
/>
</template>
<div class="w-full h-[300px] bg-[#00000050]"></div>
</SidePanelItem>
</template>
<script lang="ts" setup>
import dayjs from 'dayjs';
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useModelStore } from '@/store/modules/model';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getFishResourceZYList } from '@/api/DataQueryMenuModule';
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
const modelStore = useModelStore();
const route = useRoute();
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
const dateRange = ref('');
//
const panelTitle = computed(() => {
if (route.path.includes('shengTaiDiaoCha/shuiShengShengTaiDiaoCha')) {
return '鱼类介绍';
}
if (route.path.includes('shengTaiDiaoCha/luShengShengTaiDiaoCha')) {
return '动物介绍';
}
return '物种介绍';
});
// =
const speciesOptions = computed(() =>
(modelStore.selectedFishSpecies || []).map(item => ({
label: item.label,
value: item.value
}))
);
const currentValue = ref<string | undefined>();
const loading = ref(false);
const currentSpeciesData = ref<{
name: string;
imageList: string[];
shapedesc: string;
}>({
name: '',
imageList: [],
shapedesc: ''
});
// label
const currentLabel = computed(() => {
const matched = speciesOptions.value.find(
item => item.value === currentValue.value
);
return matched?.label || '';
});
const handleSearch = () => {
console.log(dateRange.value);
};
//
const fetchSpeciesData = async (ftp: string) => {
if (!ftp) return;
loading.value = true;
try {
const res = await getFishResourceZYList({
filter: {
logic: 'and',
filters: [
{ field: 'id', operator: 'eq', dataType: 'string', value: ftp }
]
}
});
const data = res?.data?.data?.[0];
if (data) {
let imageList: string[] = [];
if (data.inffile && data.inffile !== '-') {
imageList = data.inffile
.split(',')
.map((item: string) => item.trim())
.filter((item: string) => item && item !== '-' && item.length > 0)
.map((item: string) => `${baseUrl}/?${item}&view=jpg`);
}
currentSpeciesData.value = {
name: data.name || '',
imageList,
shapedesc: data.shapedesc || ''
};
} else {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
}
} catch {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
} finally {
loading.value = false;
}
};
//
watch(
() => modelStore.selectedFishSpecies,
list => {
const items = list || [];
//
if (
currentValue.value &&
items.some(item => item.value === currentValue.value)
) {
return;
}
//
const firstValue = items[0]?.value;
currentValue.value = firstValue;
if (firstValue) {
fetchSpeciesData(firstValue);
} else {
currentSpeciesData.value = { name: '', imageList: [], shapedesc: '' };
}
},
{ immediate: true, deep: true }
);
//
watch(currentValue, (newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
fetchSpeciesData(newVal);
}
});
</script>
<style lang="scss" scoped>
.species-spin {
display: block;
}
.species-card {
border: 1px solid #e5edf3;
border-radius: 4px;
overflow: hidden;
}
.species-image {
width: 100%;
overflow: hidden;
background: #f5f5f5;
}
.species-image--empty {
display: flex;
align-items: center;
justify-content: center;
color: #bfbfbf;
font-size: 14px;
}
.species-carousel {
height: 300px;
}
.species-image-img {
width: 100%;
object-fit: cover;
}
.species-info {
padding: 10px 12px 12px;
}
.species-name {
font-size: 15px;
font-weight: 500;
color: #2f6b98;
margin-bottom: 6px;
}
.species-desc {
font-size: 13px;
line-height: 22px;
color: #595959;
word-break: break-all;
white-space: pre-wrap;
}
:deep(.slick-slide) {
text-align: center;
overflow: hidden;
}
:deep(.slick-arrow.custom-slick-arrow) {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
transition: ease all 0.3s;
opacity: 0.5;
z-index: 1;
}
:deep(.slick-arrow.custom-slick-arrow:before) {
display: none;
}
:deep(.slick-arrow.custom-slick-arrow:hover) {
color: #fff;
opacity: 0.5;
}
</style>

View File

@ -469,12 +469,7 @@ const getselectTwo = async (code: any) => {
dir: 'asc' dir: 'asc'
} }
] ]
: [ : [],
{
field: 'ttpwr',
dir: 'desc'
}
],
select: ['stcd', 'stnm'] select: ['stcd', 'stnm']
}; };
const res = await getVmsstbprpt(params); const res = await getVmsstbprpt(params);

View File

@ -1,328 +1,356 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="水质监测工作开展情况"> <SidePanelItem title="水质监测工作开展情况">
<a-spin :spinning="loading" tip="加载中..."> <a-spin :spinning="loading" tip="加载中...">
<div class="facility-grid"> <div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card" <div
@click="handleFacilityClick(facility)"> v-for="facility in facilities"
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;"> :key="facility.name"
<div class="facility-icon"> class="facility-card"
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i> @click="handleFacilityClick(facility)"
</div> >
</div> <div
style="
<div class="facility-info"> width: 60px;
<div class="facility-name">{{ facility.name }}</div> height: 62px;
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count display: flex;
}}</span><span></span></div> align-items: center;
</div> justify-content: center;
</div> "
>
<div class="facility-icon">
<i
style="color: #fff"
:class="facility.icon"
type="icon-shengtailiuliang2"
></i>
</div> </div>
</a-spin> </div>
</SidePanelItem>
<!-- 弹框 --> <div class="facility-info">
<a-modal v-model:open="modalVisible" :title="'水质监测工作开展情况'" :footer="null" width="1536px" @cancel="handleCloseModal"> <div class="facility-name">{{ facility.name }}</div>
<ShuiwenjiancegongzuoEJ v-if="currentFacility" :activeKey="currentFacility.key" :tabs="facilities" <div style="font-size: 16px">
:baseId="baseid" /> <span class="facility-count">{{ facility.count }}</span
</a-modal> ><span></span>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
<!-- 弹框 -->
<a-modal
v-model:open="modalVisible"
:title="'水质监测工作开展情况'"
:footer="null"
width="1536px"
@cancel="handleCloseModal"
>
<ShuiwenjiancegongzuoEJ
v-if="currentFacility"
:activeKey="currentFacility.key"
:tabs="facilities"
:baseId="baseid"
/>
</a-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { msstbprptGetKendoList } from '@/api/sz' import { msstbprptGetKendoList } from '@/api/sz';
import ShuiwenjiancegongzuoEJ from "./shuiwenjiancegongzuoEJ.vue" import ShuiwenjiancegongzuoEJ from './shuiwenjiancegongzuoEJ.vue';
import { useDraggable } from '@/utils/drag'; import { useDraggable } from '@/utils/drag';
// 便 // 便
defineOptions({ defineOptions({
name: 'shuizhijiancegongzuoQK' name: 'shuizhijiancegongzuoQK'
}); });
const JidiSelectEventStore = useJidiSelectEventStore(); const JidiSelectEventStore = useJidiSelectEventStore();
// loading // loading
const loading = ref(false); const loading = ref(false);
// //
const facilities = ref([ const facilities = ref([
{ {
name: '河道水质站', name: '河道水质站',
count: 0, count: 0,
icon: 'icon iconfont icon-qixidi', icon: 'icon iconfont icon-qixidi',
key: '0' key: '0'
}, },
{ {
icon: 'icon iconfont icon-yuleizengzhizhan', icon: 'icon iconfont icon-yuleizengzhizhan',
name: '增殖站水质站', name: '增殖站水质站',
count: '0', count: '0',
key: '3' key: '3'
}, },
{ {
name: '国家水质站', name: '国家水质站',
count: 0, count: 0,
icon: 'icon iconfont icon-diwenshuijianhuan', icon: 'icon iconfont icon-diwenshuijianhuan',
key: '1' key: '1'
}, },
{ {
name: '人工水质监测断面', name: '人工水质监测断面',
count: 0, count: 0,
icon: 'icon iconfont icon-shuiwen-line', icon: 'icon iconfont icon-shuiwen-line',
key: '2' key: '2'
}, }
]); ]);
// //
const modalVisible = ref(false); const modalVisible = ref(false);
const currentFacility = ref<any>(null); const currentFacility = ref<any>(null);
const baseid = ref("") const baseid = ref('');
useDraggable(modalVisible, { boundary: true, resetOnOpen: true }); useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
// //
const getDataList = async () => { const getDataList = async () => {
loading.value = true; loading.value = true;
try { try {
let data1 = { let data1 = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
baseid.value != "all" ? { baseid.value != 'all'
"field": "baseId", ? {
"operator": "eq", field: 'baseId',
"dataType": "string", operator: 'eq',
"value": baseid.value dataType: 'string',
} : null, value: baseid.value
{ }
"field": "isDeleted", : null,
"operator": "eq", {
"value": 0 field: 'isDeleted',
}, operator: 'eq',
{ value: 0
"field": "sttpCode", },
"operator": "eq", {
"value": "WQ" field: 'sttpCode',
}, operator: 'eq',
{ value: 'WQ'
"logic": "or", },
"filters": [ {
{ logic: 'or',
"logic": "and", filters: [
"filters": [ {
{ logic: 'and',
"field": "mway", filters: [
"operator": "eq", {
"value": 2 field: 'mway',
}, operator: 'eq',
{ value: 2
"field": "dtinType", },
"operator": "eq", {
"value": 0 field: 'dtinType',
} operator: 'eq',
] value: 0
}, }
{ ]
"logic": "and", },
"filters": [ {
{ logic: 'and',
"field": "mway", filters: [
"operator": "eq", {
"value": 1 field: 'mway',
}, operator: 'eq',
{ value: 1
"field": "dtinType", },
"operator": "eq", {
"value": 2 field: 'dtinType',
} operator: 'eq',
] value: 2
}, }
{ ]
"logic": "and", },
"filters": [ {
{ logic: 'and',
"field": "mway", filters: [
"operator": "eq", {
"value": 2 field: 'mway',
}, operator: 'eq',
{ value: 2
"field": "dtinType", },
"operator": "eq", {
"value": 1 field: 'dtinType',
} operator: 'eq',
] value: 1
} }
] ]
} }
].filter(Boolean)
},
"group": [
{
"dir": "asc",
"field": "dtinType"
}
] ]
}
].filter(Boolean)
},
group: [
{
dir: 'asc',
field: 'dtinType'
} }
let data2 = { ]
"filter": { };
"logic": "and", let data2 = {
"filters": [ filter: {
baseid.value != "all" ? { logic: 'and',
"field": "baseId", filters: [
"operator": "eq", baseid.value != 'all'
"dataType": "string", ? {
"value": baseid.value field: 'baseId',
} : null, operator: 'eq',
{ dataType: 'string',
"field": "isDeleted", value: baseid.value
"operator": "eq", }
"value": 0 : null,
}, {
{ field: 'isDeleted',
"field": "sttpCode", operator: 'eq',
"operator": "eq", value: 0
"value": "WQFB" },
}, {
{ field: 'sttpCode',
"filters": [ operator: 'eq',
{ value: 'WQFB'
"logic": "and", },
"filters": [ {
{ filters: [
"field": "mway", {
"operator": "eq", logic: 'and',
"value": 2 filters: [
}, {
{ field: 'mway',
"field": "dtinType", operator: 'eq',
"operator": "eq", value: 2
"value": 0 },
} {
] field: 'dtinType',
} operator: 'eq',
] value: 0
} }
].filter(Boolean) ]
}, }
"group": [
{
"dir": "asc",
"field": "dtinType"
}
] ]
}
].filter(Boolean)
},
group: [
{
dir: 'asc',
field: 'dtinType'
} }
// const params = [data1, data2] ]
const res1 = await msstbprptGetKendoList(data1); };
// // const params = [data1, data2]
let finalData = facilities.value.map((item) => ({ ...item, count: 0 })) const res1 = await msstbprptGetKendoList(data1);
if (res1.data) { //
const { data } = res1 let finalData = facilities.value.map(item => ({ ...item, count: 0 }));
data.data.forEach((item: any, index: number) => { if (res1.data) {
const target = finalData.find((n) => n.key == item.key) const { data } = res1;
if (target) { data.data.forEach((item: any, index: number) => {
target.count = item.count || 0 const target = finalData.find(n => n.key == item.key);
} if (target) {
}) target.count = item.count || 0;
} }
console.log(finalData) });
// /
const res2 = await msstbprptGetKendoList(data2);
if (res2.data) {
const responseData = res2?.data?.data
const target1 = finalData.find((n) => n.key == '3')
if (target1) {
target1.count = responseData[0]?.count || 0
}
}
facilities.value = finalData
console.log(finalData)
//
} catch (error) {
console.error('获取数据失败:', error);
} finally {
loading.value = false;
} }
} console.log(finalData);
// /
const res2 = await msstbprptGetKendoList(data2);
if (res2.data) {
const responseData = res2?.data?.data;
const target1 = finalData.find(n => n.key == '3');
if (target1) {
target1.count = responseData[0]?.count || 0;
}
}
facilities.value = finalData;
console.log(finalData);
//
} catch (error) {
console.error('获取数据失败:', error);
} finally {
loading.value = false;
}
};
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getDataList() getDataList();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
// //
const handleFacilityClick = (facility: any) => { const handleFacilityClick = (facility: any) => {
// console.log(facility); // console.log(facility);
currentFacility.value = facility; currentFacility.value = facility;
modalVisible.value = true; modalVisible.value = true;
}; };
// //
const handleCloseModal = () => { const handleCloseModal = () => {
modalVisible.value = false; modalVisible.value = false;
currentFacility.value = null; currentFacility.value = null;
}; };
// //
onMounted(() => { onMounted(() => {});
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.facility-grid { .facility-grid {
width: 406px; width: 406px;
flex-flow: wrap; flex-flow: wrap;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
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 { .facility-card {
width: 200px; width: 200px;
height: 70px; height: 70px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin: 4px 0px; margin: 4px 0px;
background: #fff; background: #fff;
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
border-radius: 2px; border-radius: 2px;
transition: all 0.3s; transition: all 0.3s;
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
} }
.facility-icon { .facility-icon {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 40px; width: 40px;
height: 40px; height: 40px;
// margin-right: 8px; // margin-right: 8px;
background: #5389b5; background: #2f6b98;
border-radius: 50%; border-radius: 50%;
.anticon { .anticon {
font-size: 24px; font-size: 24px;
color: #fff; color: #fff;
} }
} }
.facility-info { .facility-info {
flex: 1; flex: 1;
} }
.facility-name { .facility-name {
font-size: 16px; font-size: 16px;
color: #333; color: #333;
// margin-bottom: 4px; // margin-bottom: 4px;
// font-weight: 500; // font-weight: 500;
} }
.facility-count { .facility-count {
font-size: 18px; font-size: 18px;
color: #2f6b98; color: #2f6b98;
// font-weight: 600; // font-weight: 600;
} }
</style> </style>

View File

@ -1,20 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'; import { onMounted, onUnmounted, ref, watch } from 'vue';
import { useUiStore } from '@/store/modules/ui'; import { useUiStore } from '@/store/modules/ui';
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue'; import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
import RightDrawer from '@/components/RightDrawer/index.vue'; import RightDrawer from '@/components/RightDrawer/index.vue';
import EquipmentStatistics from '@/modules/EquipmentStatistics/index.vue'; // import EquipmentStatistics from '@/modules/EquipmentStatistics/index.vue'; //
import BaseStatistics from '@/modules/BaseStatistics/index.vue'; // import BaseStatistics from '@/modules/BaseStatistics/index.vue'; //
const uiStore = useUiStore(); const uiStore = useUiStore();
onMounted(() => { onMounted(() => {
uiStore.isSmartEquipment = true; uiStore.isSmartEquipment = true;
}); });
onUnmounted(() => { onUnmounted(() => {
uiStore.isSmartEquipment = false; uiStore.isSmartEquipment = false;
}); });
const filter2 = ref('jinshajiang');
const filter3 = ref('lianghekou');
const filter4 = ref('station');
</script> </script>
<template> <template>
@ -22,19 +20,6 @@ const filter4 = ref('station');
<div class="leftContent"> <div class="leftContent">
<JidiSelectorMod /> <JidiSelectorMod />
</div> </div>
<div class="selector">
<div style="display: flex; gap: 12px; align-items: center">
<a-select v-model:value="filter2" style="width: 160px">
<a-select-option value="jinshajiang">金沙江干流</a-select-option>
</a-select>
<a-select v-model:value="filter3" style="width: 140px">
<a-select-option value="lianghekou">两河口</a-select-option>
</a-select>
<a-select v-model:value="filter4" style="width: 200px">
<a-select-option value="station">两河口库尾水温站</a-select-option>
</a-select>
</div>
</div>
<div class="rightContent"> <div class="rightContent">
<RightDrawer> <RightDrawer>
<EquipmentStatistics /> <EquipmentStatistics />