Merge branch 'main' into dev-tw

This commit is contained in:
tangwei 2026-07-02 11:00:01 +08:00
commit ef7048e9ff
89 changed files with 13991 additions and 1084 deletions

View File

@ -77,6 +77,9 @@ public class SecurityConfig {
.requestMatchers("/fpr/**").permitAll()
.requestMatchers("/fh/**").permitAll()
.requestMatchers("/data/**").permitAll()
.requestMatchers("/mapLayer/**").permitAll()
.requestMatchers("/mapmodule/**").permitAll()
.requestMatchers("/mapLegend/**").permitAll()
.requestMatchers("/sms/**").permitAll()
.requestMatchers(HttpMethod.GET, "/").permitAll()
.requestMatchers(HttpMethod.GET,

View File

@ -181,4 +181,20 @@ public class SwaggerConfig {
.packagesToScan("com.yfd.platform.qgc_eng.eq.controller")
.build();
}
@Bean
public GroupedOpenApi groupSysApi() {
return GroupedOpenApi.builder()
.group("5. 图层管理")
.packagesToScan("com.yfd.platform.qgc_sys.mapLayer.controller")
.build();
}
@Bean
public GroupedOpenApi groupSysMcfgApi() {
return GroupedOpenApi.builder()
.group("5.1 图例结构管理")
.packagesToScan("com.yfd.platform.qgc_sys.mapcfg.controller")
.build();
}
}

View File

@ -0,0 +1,90 @@
package com.yfd.platform.qgc_sys.mapLayer.controller;
import cn.hutool.core.util.StrUtil;
import com.yfd.platform.common.DataSourceResult;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerAo;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerBizService;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 地图图层管理
*
* @author migration
*/
@RestController
@RequestMapping("/mapLayer")
@Tag(name = "地图图层管理")
public class MapLayerController {
@Resource
private IMapLayerBizService mapLayerBizService;
@Resource
private IMapLayerService layerService;
/**
* 地图图层管理图层树(所有)
*/
@Operation(summary = "地图图层管理图层树(所有)")
@PostMapping("/getAllMapLayerTree")
public ResponseResult getAllMapLayerTree(@RequestBody MapLayerAo flag) {
List<MsMaplayerB> tree = mapLayerBizService.getMapLayerTree(flag);
// 构建 Kendo 数据源结果
DataSourceResult<MsMaplayerB> dataSourceResult = new DataSourceResult<>();
dataSourceResult.setData(tree);
// 计算总节点数
long total = countTreeNodes(tree);
dataSourceResult.setTotal(tree.size());
return ResponseResult.successData(dataSourceResult);
}
private long countTreeNodes(List<MsMaplayerB> nodes) {
if (nodes == null) return 0;
long count = 0;
for (MsMaplayerB node : nodes) {
count++; // 当前节点
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
count += countTreeNodes(node.getChildren());
}
}
return count;
}
/**
* 图层新增修改
*/
@Operation(summary = "图层新增修改")
@PostMapping("/save")
public ResponseResult addLayer(@RequestBody MsMaplayerB layer) {
if (layer == null) {
return ResponseResult.error("图层信息不能为空.");
}
if (StrUtil.isBlank(layer.getCode())) {
throw new BizException("图层编码(code)不能为空.");
}
if (StrUtil.isBlank(layer.getTitle())) {
throw new BizException("图层名称(title)不能为空.");
}
layerService.saveOrUpdate(layer);
return ResponseResult.success("保存成功.");
}
/**
* 图层删除
*/
@Operation(summary = "图层删除")
@PostMapping("/delete")
public ResponseResult deleteLayer(@RequestBody List<String> ids) {
layerService.delelteByIds(ids);
return ResponseResult.success("删除成功.");
}
}

View File

@ -0,0 +1,209 @@
package com.yfd.platform.qgc_sys.mapLayer.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.common.DataSourceRequest;
import com.yfd.platform.common.DataSourceResult;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplayerBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerService;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendService;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendBizService;
import com.yfd.platform.utils.DataSourceRequestUtil;
import com.yfd.platform.utils.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 地图图例管理
*
* @author migration
*/
@RestController
@RequestMapping("/mapLegend")
@Tag(name = "地图图例管理")
public class MapLegendController {
@Resource
private IMapLegendService mapLegendService;
@Resource
private IMapLegendBizService mapLegendBizService;
@Resource
private MsMaplayerBMapper msMaplayerBMapper; // 新增注入
/**
* 条件过滤数据列表Kendo UI 数据源
*/
@Operation(summary = "条件过滤数据列表")
@PostMapping("/GetKendoList")
public ResponseResult getKendoList(@RequestBody DataSourceRequest dataSourceRequest) {
// 构建查询条件
QueryWrapper<MsMaplegendB> wrapper = DataSourceRequestUtil.buildQueryWrapper(
dataSourceRequest, MsMaplegendB.class);
// 执行分页查询
Page<MsMaplegendB> page = DataSourceRequestUtil.getPage(dataSourceRequest);
Page<MsMaplegendB> resultPage;
if (page != null) {
resultPage = mapLegendService.page(page, wrapper);
} else {
List<MsMaplegendB> list = mapLegendService.list(wrapper);
resultPage = new Page<>();
resultPage.setRecords(list);
resultPage.setTotal(list.size());
}
List<MsMaplegendB> records = resultPage.getRecords();
if (records != null && !records.isEmpty()) {
// ---------- 2. 批量获取父级信息用于 parentCodeparentName----------
Set<String> parentIds = records.stream()
.filter(e -> e.getDataType() != null && e.getDataType() == 2) // 只处理图例数据
.map(MsMaplegendB::getParentId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<String, MsMaplegendB> parentMap = new HashMap<>();
if (!parentIds.isEmpty()) {
List<MsMaplegendB> parents = mapLegendService.listByIds(parentIds);
parentMap = parents.stream()
.collect(Collectors.toMap(MsMaplegendB::getId, Function.identity()));
}
// ---------- 3. 批量获取图层名称根据 layerCode MS_MAPLAYER_B----------
Set<String> layerCodes = records.stream()
.map(MsMaplegendB::getLayerCode)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<String, String> layerCodeNameMap = new HashMap<>();
if (!layerCodes.isEmpty()) {
// 使用 Mapper 查询
List<MsMaplayerB> layerList = msMaplayerBMapper.selectList(
new LambdaQueryWrapper<MsMaplayerB>()
.in(MsMaplayerB::getCode, layerCodes)
);
layerCodeNameMap = layerList.stream()
.collect(Collectors.toMap(MsMaplayerB::getCode, MsMaplayerB::getTitle));
}
// ---------- 4. 遍历记录填充三个额外字段 ----------
for (MsMaplegendB entity : records) {
// 4.1 填充 parentCode / parentName dataType=2
if (entity.getDataType() != null && entity.getDataType() == 2) {
String parentId = entity.getParentId();
if (parentId != null && parentMap.containsKey(parentId)) {
MsMaplegendB parent = parentMap.get(parentId);
entity.setParentCode(parent.getCode());
entity.setParentName(parent.getName());
} else {
entity.setParentCode(null);
entity.setParentName(null);
}
} else {
// dataType=2 的记录这两个字段置空
entity.setParentCode(null);
entity.setParentName(null);
}
// 4.2 填充 layerCodeName所有记录均可填充如果图层不存在则置空
String layerCode = entity.getLayerCode();
if (layerCode != null && layerCodeNameMap.containsKey(layerCode)) {
entity.setLayerCodeName(layerCodeNameMap.get(layerCode));
} else {
entity.setLayerCodeName(null);
}
}
}
// 构建 Kendo 数据源结果
DataSourceResult<MsMaplegendB> dataSourceResult = new DataSourceResult<>();
dataSourceResult.setData(resultPage.getRecords());
dataSourceResult.setTotal(resultPage.getTotal());
return ResponseResult.successData(dataSourceResult);
}
/**
* 图例新增修改
*/
@Operation(summary = "图例新增修改")
@PostMapping("/save")
public ResponseResult addLayer(@RequestBody MsMaplegendB legend) {
if (legend == null) {
return ResponseResult.error("图例信息不能为空.");
}
if (StrUtil.isBlank(legend.getCode())) {
throw new BizException("图例类型(code: pointLayer=用于锚点baseLayer=地图GIS)不能为空.");
}
if (StrUtil.isBlank(legend.getName())) {
throw new BizException("图例名称(name)不能为空.");
}
if (StrUtil.isBlank(legend.getNameEn())) {
throw new BizException("图例编码(nameEn)不能为空.");
}
if (legend.getDataType() == null) {
throw new BizException("图例数据类型(dataType: 1=结构,2=数据)不能为空.");
}
if (legend.getDataType() == 2 && StrUtil.isBlank(legend.getLayerCode())) {
throw new BizException("关联的图层编码(layerCode)不能为空.");
}
if (StrUtil.isNotBlank(legend.getName())){
if(StrUtil.isBlank(legend.getGroupName())){
legend.setGroupName(legend.getName());
}
if(StrUtil.isBlank(legend.getParentName())){
legend.setGroupName(legend.getName());
}
}
legend.setRecordUser(SecurityUtils.getUserId());
mapLegendService.saveOrUpdate(legend);
return ResponseResult.success("保存成功.");
}
/**
* 图例删除
*/
@Operation(summary = "图例删除")
@PostMapping("/delete")
public ResponseResult deleteLayer(@RequestBody List<String> ids) {
mapLegendService.delelteByIds(ids);
return ResponseResult.success("删除成功.");
}
/**
* 地图图例管理图层树(下拉选)
*/
@Operation(summary = "地图图例管理图层树(下拉选)")
@GetMapping("/getAllMapLegendTree")
public ResponseResult getAllMapLayerTree(Integer flag) {
List<MapLegendVo> tree = mapLegendBizService.getMapLegendTree(flag);
return ResponseResult.successData(tree);
}
/**
* 地图图例列表
*/
@Operation(summary = "地图图例列表")
@GetMapping("/getModuleMapLegendList")
public ResponseResult getModuleMapLegendList(@RequestParam(required = false) String moduleId) {
List<MapLegendVo> list = mapLegendBizService.getModuleMapLegendList(moduleId);
return ResponseResult.successData(list);
}
}

View File

@ -0,0 +1,191 @@
package com.yfd.platform.qgc_sys.mapLayer.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 地图图层表
*
* @author migration
*/
@Data
@TableName("MS_MAPLAYER_B")
public class MsMaplayerB implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/** 类型 */
@TableField("TYPE")
private String type;
/** 标识字段 */
@TableField("KEY")
private String key;
/** 编码 */
@TableField("CODE")
private String code;
/** 名称 */
@TableField("TITLE")
private String title;
/** 名称英文 */
@TableField("TITLE_EN")
private String titleEn;
/** 所属父节点 */
@TableField("PARENT_ID")
private String parentId;
/** 树全路径 */
@TableField("FULL_PATH")
private String fullPath;
/** 层级 */
@TableField("TREE_LEVEL")
private Integer treeLevel;
/** 是否有子集0=否 1=有 */
@TableField("HAS_CHILDREN")
private Integer hasChildren;
/** 对应锚点接口 */
@TableField("URL")
private String url;
/** 对应三维锚点接口 */
@TableField("URL_THD")
private String urlThd;
/** 参数设置 */
@TableField("OPTIONS")
private String options;
/** 访问令牌 */
@TableField("TOKEN")
private String token;
/** 用户名 */
@TableField("USERNAME")
private String username;
/** 密码 */
@TableField("PASSWORD")
private String password;
/** 地图中心点经度 */
@TableField("CENTER_LGTD")
private BigDecimal centerLgtd;
/** 地图中心点纬度 */
@TableField("CENTER_LTTD")
private BigDecimal centerLttd;
/** 默认缩放级别 */
@TableField("DEFAULT_ZOOM")
private String defaultZoom;
/** 所属国家 */
@TableField("COUNTRY")
private String country;
/** 图标 */
@TableField("ICON")
private String icon;
/** 是否可选择0=否 1=是 */
@TableField("CHECKABLE")
private Integer checkable;
/** 图层上下层层级关系 */
@TableField("Z_INDEX")
private String zIndex;
/** 各类站点图层对应类型 */
@TableField("STTP_TYPE")
private String sttpType;
/** 图层展示数据格式 */
@TableField("LAYERS")
private String layers;
/** 图层透明度 */
@TableField("OPACITY")
private BigDecimal opacity;
/** 额外参数 */
@TableField("PARAMJSON")
private String paramJson;
/** 备注 */
@TableField("DESCRIPTION")
private String description;
/** 是否启用0=禁用 1=启用 */
@TableField("ENABLE")
private Integer enable;
/** 排序 */
@TableField("ORDER_INDEX")
private Integer orderIndex;
/** 过滤内容 */
@TableField("FILTER_CONTENT")
private String filterContent;
/** 创建人 */
@TableField("RECORD_USER")
private String recordUser;
/** 创建时间 */
@TableField("RECORD_TIME")
private Date recordTime;
/** 更新人 */
@TableField("MODIFY_USER")
private String modifyUser;
/** 更新时间 */
@TableField("MODIFY_TIME")
private Date modifyTime;
/** 是否已删除0=未删除 1=已删除 */
@TableField("IS_DELETED")
private Integer isDeleted;
/** 删除人 */
@TableField("DELETE_USER")
private String deleteUser;
/** 删除时间 */
@TableField("DELETE_TIME")
private Date deleteTime;
/** 数据类型:1=结构,2=数据 */
@TableField("DATA_TYPE")
private Integer dataType;
/** 标签层级 */
@TableField("LABEL_LEVEL")
private Integer labelLevel;
/** 子级集合(非数据库字段) */
@TableField(exist = false)
private List<MsMaplayerB> children;
/** 创建人名称(非数据库字段) */
@TableField(exist = false)
private String displayRecordUser;
}

View File

@ -0,0 +1,140 @@
package com.yfd.platform.qgc_sys.mapLayer.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 地图图例表
*
* @author migration
*/
@Data
@TableName("MS_MAPLEGEND_B")
public class MsMaplegendB implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_UUID)
private String id;
@TableField("NAME")
private String name;
@TableField("NAME_EN")
private String nameEn;
@TableField("CODE")
private String code;
@TableField("ICON")
private String icon;
@TableField("GROUP_NAME")
private String groupName;
@TableField("PARENT_NAME")
private String parentName;
@TableField("LAYER_CODE")
private String layerCode;
@TableField("MIN_ZOOM")
private Integer minZoom;
@TableField("MAX_ZOOM")
private Integer maxZoom;
@TableField("MIN_HEIGHT_THD")
private BigDecimal minHeightThd;
@TableField("MAX_HEIGHT_THD")
private BigDecimal maxHeightThd;
@TableField("DESCRIPTION")
private String description;
@TableField("ENABLE")
private Integer enable;
@TableField("INTERNAL")
private Integer internal;
@TableField("ORDER_INDEX")
private Integer orderIndex;
@TableField("FILTER_CONTENT")
private String filterContent;
@TableField("RECORD_USER")
private String recordUser;
@TableField("RECORD_TIME")
private Date recordTime;
@TableField("MODIFY_USER")
private String modifyUser;
@TableField("MODIFY_TIME")
private Date modifyTime;
@TableField("IS_DELETED")
private Integer isDeleted;
@TableField("DELETE_USER")
private String deleteUser;
@TableField("DELETE_TIME")
private Date deleteTime;
@TableField("IF_SHOW")
private Integer ifShow;
@TableField("EXPRESSION")
private String expression;
@TableField("EXT")
private String ext;
@TableField("DATA_TYPE")
private Integer dataType;
@TableField("PARENT_ID")
private String parentId;
@TableField("FULL_PATH")
private String fullPath;
@TableField("TREE_LEVEL")
private Integer treeLevel;
@TableField("HAS_CHILDREN")
private Integer hasChildren;
@TableField("COLOR")
private String color;
@TableField("SHAPE")
private String shape;
@TableField("MIN_VAL")
private BigDecimal minVal;
@TableField("MAX_VAL")
private BigDecimal maxVal;
@TableField("IS_FILTER")
private Integer isFilter;
@TableField(exist = false)
private String layerCodeName;
@TableField(exist = false)
private String parentCode;
}

View File

@ -0,0 +1,98 @@
package com.yfd.platform.qgc_sys.mapLayer.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 地图与菜单映射表
*
* @author migration
*/
@Data
@TableName("MS_MAPMODULE_B")
public class MsMapmoduleB implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_UUID)
private String id;
@TableField("MODULE_ID")
private String moduleId;
@TableField("RES_TYPE")
private String resType;
@TableField("RES_ID")
private String resId;
@TableField("CHILD_NODE")
private Integer childNode;
@TableField("MULTI_SELECT")
private Integer multiSelect;
@TableField("CAN_BE_CHECKED")
private Integer canBeChecked;
@TableField("CHECKED")
private Integer checked;
@TableField("DESCRIPTION")
private String description;
@TableField("ENABLE")
private Integer enable;
@TableField("ORDER_INDEX")
private Integer orderIndex;
@TableField("FILTER_CONTENT")
private String filterContent;
@TableField("RECORD_USER")
private String recordUser;
@TableField("RECORD_TIME")
private Date recordTime;
@TableField("MODIFY_USER")
private String modifyUser;
@TableField("MODIFY_TIME")
private Date modifyTime;
@TableField("IS_DELETED")
private Integer isDeleted;
@TableField("DELETE_USER")
private String deleteUser;
@TableField("DELETE_TIME")
private Date deleteTime;
@TableField("TEMPLATE_ID")
private String templateId;
@TableField("SYSTEM_ID")
private String systemId;
/**
* 归属的系统ID非数据库字段用于字典翻译
*/
@TableField(exist = false)
private String systemName;
/**
* 菜单名称非数据库字段用于字典翻译
*/
@TableField(exist = false)
private String moduleName;
}

View File

@ -0,0 +1,186 @@
package com.yfd.platform.qgc_sys.mapLayer.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 地图工具箱表
*
* @author migration
*/
@Data
@TableName("MS_MAPTOOLBOX_B")
public class MsMaptoolboxB implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/**
* 名称
*/
@TableField("NAME")
private String name;
/**
* 名称英文
*/
@TableField("NAME_EN")
private String nameEn;
/**
* 编码
*/
@TableField("CODE")
private String code;
/**
* 地图模式
*/
@TableField("MAP_MODEL")
private String mapModel;
/**
* 所属父节点
*/
@TableField("PATENT_ID")
private String patentId;
/**
* 树全路径
*/
@TableField("FULL_PATH")
private String fullPath;
/**
* 层级
*/
@TableField("TREE_LEVEL")
private Integer treeLevel;
/**
* 是否有子集
*/
@TableField("HAS_CHILDREN")
private Integer hasChildren;
/**
* 图标
*/
@TableField("ICON")
private String icon;
/**
* 备注
*/
@TableField("DESCRIPTION")
private String description;
/**
* 是否启用
*/
@TableField("ENABLE")
private Integer enable;
/**
* 系统内置项
*/
@TableField("INTERNAL")
private Integer internal;
/**
* 排序
*/
@TableField("ORDER_INDEX")
private Integer orderIndex;
/**
* 过滤内容
*/
@TableField("FILTER_CONTENT")
private String filterContent;
/**
* 创建人
*/
@TableField("RECORD_USER")
private String recordUser;
/**
* 创建时间
*/
@TableField("RECORD_TIME")
private Date recordTime;
/**
* 更新人
*/
@TableField("MODIFY_USER")
private String modifyUser;
/**
* 更新时间
*/
@TableField("MODIFY_TIME")
private Date modifyTime;
/**
* 是否已删除
*/
@TableField("IS_DELETED")
private Integer isDeleted;
/**
* 删除人
*/
@TableField("DELETE_USER")
private String deleteUser;
/**
* 删除时间
*/
@TableField("DELETE_TIME")
private Date deleteTime;
/**
* 类型0-功能区 1-地形 2-图例
*/
@TableField("TOOL_TYPE")
private Integer toolType;
/**
* 归属的系统ID非数据库字段来自MS_MAPMODULE_B关联
*/
@TableField(exist = false)
private String systemId;
/**
* 是否默认勾选非数据库字段来自MS_MAPMODULE_B关联
*/
@TableField(exist = false)
private Integer checked;
/**
* 是否可勾选非数据库字段来自MS_MAPMODULE_B关联
*/
@TableField(exist = false)
private Integer canBeChecked;
/**
* 是否多选非数据库字段来自MS_MAPMODULE_B关联
*/
@TableField(exist = false)
private Integer multiSelect;
/** 子级集合(非数据库字段) */
@TableField(exist = false)
private List<MsMaptoolboxB> children;
}

View File

@ -0,0 +1,15 @@
package com.yfd.platform.qgc_sys.mapLayer.entity.vo;
import lombok.Data;
/**
* 地图图层查询参数
*
* @author migration
*/
@Data
public class MapLayerAo {
/** 图层名称(模糊查询) */
private String title;
}

View File

@ -0,0 +1,80 @@
package com.yfd.platform.qgc_sys.mapLayer.entity.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 地图图层管理封装VO
*
* @author migration
*/
@Data
@Schema(description = "地图图层管理封装VO")
public class MapLayerVo {
@Schema(description = "主键")
private String id;
@Schema(description = "父id")
private String parentId;
@Schema(description = "标题名称")
private String title;
@Schema(description = "图层标识")
@JsonProperty(value = "key")
private String code;
@Schema(description = "标签层级")
@JsonProperty(value = "labelLevel")
private Integer labelLevel;
@Schema(description = "是否可以选择0=否 1=是")
private Integer checkable;
@Schema(description = "图层分类")
private String type;
@Schema(description = "图层上下层层级关系")
@JsonProperty(value = "zIndex")
private String zIndex;
@Schema(description = "各类站点图层对应类型")
private String sttpType;
@Schema(description = "图层展示数据格式")
private String layers;
@Schema(description = "url地址")
private String url;
@Schema(description = "3D url地址")
private String urlThd;
@Schema(description = "图层透明度")
private BigDecimal opacity;
@Schema(description = "子级集合")
private List<MapLayerVo> children;
@Schema(description = "参数json")
private String paramJson;
@Schema(description = "排序")
private String orderIndex;
@Schema(description = "是否默认勾选0=否 1=是")
private Integer checked;
@Schema(description = "是否可勾选0=否 1=是")
private Integer canBeChecked;
@Schema(description = "是否多选0=否 1=是")
private Integer multiSelect;
@Schema(description = "参数设置steady=静态 dynamic=动态")
private String options;
}

View File

@ -0,0 +1,97 @@
package com.yfd.platform.qgc_sys.mapLayer.entity.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 地图图例VO
*
* @author migration
*/
@Data
@Schema(description = "地图图例VO")
public class MapLegendVo {
@Schema(description = "图例id")
private String id;
@Schema(description = "父级id")
private String parentId;
@Schema(description = "分组名")
private String groupName;
@Schema(description = "层级名称")
private String parentName;
@Schema(description = "图例名")
private String name;
@Schema(description = "图例code")
private String code;
@Schema(description = "图例icon")
private String icon;
@Schema(description = "图例英文(对应描点数据中的状态)")
private String nameEn;
@Schema(description = "图层code")
private String layerCode;
@Schema(description = "是否多选;0=否1=是")
private Integer multiSelect;
@Schema(description = "是否默认勾选;0=否1=是")
private Integer checked;
@Schema(description = "是否可勾选;0=否1=是")
private Integer canBeChecked;
@Schema(description = "子排序")
private Integer orderIndex;
@Schema(description = "父排序")
private Integer pSort;
@Schema(description = "最小缩放等级")
private Integer minZoom;
@Schema(description = "最大缩放等级")
private Integer maxZoom;
@Schema(description = "最小高程-三维")
private BigDecimal minHeightThd;
@Schema(description = "最大高程-三维")
private BigDecimal maxHeightThd;
@Schema(description = "是否显示;0=否1=是")
private Integer ifShow;
@Schema(description = "是否过滤;0=否1=是")
private Integer isFilter;
@JsonProperty(value = "color")
@Schema(description = "图例颜色")
private String color;
@JsonProperty(value = "shape")
@Schema(description = "图例形状")
private String shape;
@JsonProperty(value = "minVal")
@Schema(description = "区间最小值")
private BigDecimal minVal;
@JsonProperty(value = "maxVal")
@Schema(description = "区间最大值")
private BigDecimal maxVal;
@Schema(description = "子级")
private List<MapLegendVo> childrenList;
}

View File

@ -0,0 +1,14 @@
package com.yfd.platform.qgc_sys.mapLayer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import org.apache.ibatis.annotations.Mapper;
/**
* 地图图层表 Mapper 接口
*
* @author migration
*/
@Mapper
public interface MsMaplayerBMapper extends BaseMapper<MsMaplayerB> {
}

View File

@ -0,0 +1,14 @@
package com.yfd.platform.qgc_sys.mapLayer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import org.apache.ibatis.annotations.Mapper;
/**
* 地图图例表 Mapper 接口
*
* @author migration
*/
@Mapper
public interface MsMaplegendBMapper extends BaseMapper<MsMaplegendB> {
}

View File

@ -0,0 +1,114 @@
package com.yfd.platform.qgc_sys.mapLayer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaptoolboxB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 地图与菜单映射表 Mapper 接口
*
* @author migration
*/
@Mapper
public interface MsMapmoduleBMapper extends BaseMapper<MsMapmoduleB> {
/**
* 查询地图图层树关联MS_MAPMODULE_B与MS_MAPLAYER_B
*/
@Select("<script>"
+ "SELECT DISTINCT BML.ID, BML.PARENT_ID AS parentId, BML.TITLE, BML.CODE, BML.LABEL_LEVEL AS labelLevel, "
+ "BML.CHECKABLE, BML.TYPE, BML.Z_INDEX AS zIndex, BML.STTP_TYPE AS sttpType, BML.LAYERS, "
+ "BML.PARAMJSON AS paramJson, BML.URL, BML.URL_THD AS urlThd, BML.OPACITY, BML.SYSTEM_ID AS systemId, "
+ "BML.OPTIONS, BMM.MULTI_SELECT AS multiSelect, BMM.CAN_BE_CHECKED AS canBeChecked, "
+ "CASE WHEN BMM.CHECKED IS NULL THEN 0 ELSE BMM.CHECKED END AS checked, BML.ORDER_INDEX "
+ "FROM MS_MAPLAYER_B BML LEFT JOIN MS_MAPMODULE_B BMM ON BML.ID = BMM.RES_ID "
+ "<if test=\"moduleId != null and moduleId != ''\"> AND BMM.MODULE_ID = #{moduleId}</if>"
+ "<if test=\"sid != null and sid != ''\"> AND BMM.SYSTEM_ID = #{sid}</if>"
+ "<choose>"
+ "<when test=\"templateId != null and templateId != ''\"> AND BMM.TEMPLATE_ID = #{templateId}</when>"
+ "<otherwise> AND (BMM.TEMPLATE_ID IS NULL OR BMM.TEMPLATE_ID = '00000000-0000-0000-0000-000000000000')</otherwise>"
+ "</choose>"
+ "WHERE BML.ENABLE = 1 AND (BML.TYPE NOT IN ('YXDT','DXDT','SLDT') OR BML.TYPE IS NULL) "
+ "ORDER BY BML.ORDER_INDEX"
+ "</script>")
List<MapLayerVo> getMapLayerTree(@Param("moduleId") String moduleId, @Param("sid") String sid,
@Param("templateId") String templateId);
/**
* 查询地图工具箱关联MS_MAPMODULE_B与MS_MAPTOOLBOX_B
*/
@Select("<script>"
+ "SELECT DISTINCT MTB.ID, MTB.NAME, MTB.NAME_EN AS nameEn, MTB.MAP_MODEL AS mapModel, "
+ "MTB.FULL_PATH AS fullPath, MTB.TREE_LEVEL AS treeLevel, MTB.HAS_CHILDREN AS hasChildren, "
+ "MTB.PATENT_ID AS patentId, MTB.ICON, MTB.DESCRIPTION, MTB.ENABLE, MTB.INTERNAL, "
+ "MTB.ORDER_INDEX AS orderIndex, MTB.RECORD_USER AS recordUser, MTB.RECORD_TIME AS recordTime, "
+ "MTB.IS_DELETED AS isDeleted, MTB.TOOL_TYPE AS toolType, "
+ "BMM.MULTI_SELECT AS multiSelect, BMM.CAN_BE_CHECKED AS canBeChecked, "
+ "CASE WHEN BMM.CHECKED IS NULL AND MTB.NAME_EN NOT IN ('left','right','bottom') THEN 0 ELSE BMM.CHECKED END AS checked, "
+ "BMM.SYSTEM_ID AS systemId, MTB.ORDER_INDEX "
+ "FROM MS_MAPTOOLBOX_B MTB LEFT JOIN MS_MAPMODULE_B BMM ON MTB.ID = BMM.RES_ID "
+ "<if test=\"moduleId != null and moduleId != ''\"> AND BMM.MODULE_ID = #{moduleId}</if>"
+ "<if test=\"sid != null and sid != ''\"> AND BMM.SYSTEM_ID = #{sid}</if>"
+ "<choose>"
+ "<when test=\"templateId != null and templateId != ''\"> AND BMM.TEMPLATE_ID = #{templateId}</when>"
+ "<otherwise> AND (BMM.TEMPLATE_ID IS NULL OR BMM.TEMPLATE_ID = '00000000-0000-0000-0000-000000000000')</otherwise>"
+ "</choose>"
+ "WHERE MTB.ENABLE = 1 ORDER BY MTB.ORDER_INDEX"
+ "</script>")
List<MsMaptoolboxB> getMapToolBox(@Param("moduleId") String moduleId, @Param("sid") String sid,
@Param("templateId") String templateId);
/**
* 查询模块关联的图例列表关联MS_MAPMODULE_B与MS_MAPLEGEND_B
*/
@Select("<script>"
+ "SELECT DISTINCT BML.PARENT_NAME AS parentName, BML.GROUP_NAME AS groupName, BML.NAME, BML.CODE, "
+ "BML.ICON, BML.NAME_EN AS nameEn, BMM.MULTI_SELECT AS multiSelect, BMM.CHECKED AS checked, "
+ "BMM.CAN_BE_CHECKED AS canBeChecked, BML.ORDER_INDEX, BMM.ORDER_INDEX AS pSort, "
+ "BML.LAYER_CODE AS layerCode, BML.MIN_ZOOM AS minZoom, BML.MAX_ZOOM AS maxZoom, "
+ "BML.MIN_HEIGHT_THD AS minHeightThd, BML.MAX_HEIGHT_THD AS maxHeightThd, "
+ "BMM.ORDER_INDEX, BML.ORDER_INDEX "
+ "FROM MS_MAPLEGEND_B BML LEFT JOIN MS_MAPMODULE_B BMM ON BML.ID = BMM.RES_ID AND BMM.IS_DELETED = 0 "
+ "WHERE BMM.RES_TYPE = 'legend' AND BML.ENABLE = 1 AND BML.IS_DELETED = 0 "
+ "<if test=\"moduleId != null and moduleId != ''\"> AND BMM.MODULE_ID = #{moduleId}</if>"
+ "<if test=\"sid != null and sid != ''\"> AND BMM.SYSTEM_ID = #{sid}</if>"
+ "<choose>"
+ "<when test=\"templateId != null and templateId != ''\"> AND BMM.TEMPLATE_ID = #{templateId}</when>"
+ "<otherwise> AND (BMM.TEMPLATE_ID IS NULL OR BMM.TEMPLATE_ID = '00000000-0000-0000-0000-000000000000')</otherwise>"
+ "</choose>"
+ "ORDER BY BMM.ORDER_INDEX, BML.ORDER_INDEX"
+ "</script>")
List<MapLegendVo> getModuleMapLegendList(@Param("moduleId") String moduleId, @Param("sid") String sid,
@Param("templateId") String templateId);
/**
* 查询模块关联的图例列表单参数版本用于地图图例管理页面
*/
@Select("<script>"
+ "SELECT BML.ID, BML.PARENT_ID AS parentId, BML.GROUP_NAME AS groupName, BML.PARENT_NAME AS parentName, "
+ "BML.NAME, BML.CODE, BML.ICON, BML.NAME_EN AS nameEn, BML.LAYER_CODE AS layerCode, "
+ "BML.MIN_ZOOM AS minZoom, BML.MAX_ZOOM AS maxZoom, "
+ "BML.MIN_HEIGHT_THD AS minHeightThd, BML.MAX_HEIGHT_THD AS maxHeightThd, "
+ "BML.IF_SHOW AS ifShow, BML.IS_FILTER AS isFilter, BML.COLOR, BML.SHAPE, BML.MIN_VAL AS minVal, BML.MAX_VAL AS maxVal, "
+ "BML.ORDER_INDEX AS orderIndex, "
+ "BMM.MULTI_SELECT AS multiSelect, BMM.CHECKED AS checked, BMM.CAN_BE_CHECKED AS canBeChecked, "
+ "BMM.ORDER_INDEX AS pSort "
+ "FROM MS_MAPLEGEND_B BML LEFT JOIN MS_MAPMODULE_B BMM ON BML.ID = BMM.RES_ID "
+ "WHERE BMM.RES_TYPE = 'legend' AND BML.IS_DELETED = 0 AND BML.ENABLE = 1 "
+ "<choose>"
+ "<when test=\"moduleId != null and moduleId != ''\"> AND BMM.MODULE_ID = #{moduleId}</when>"
+ "<otherwise> AND BMM.MODULE_ID = 'common'</otherwise>"
+ "</choose>"
+ "ORDER BY BMM.ORDER_INDEX, BML.ORDER_INDEX"
+ "</script>")
List<MapLegendVo> getModuleMapLegendList(@Param("moduleId") String moduleId);
}

View File

@ -0,0 +1,15 @@
package com.yfd.platform.qgc_sys.mapLayer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaptoolboxB;
import org.apache.ibatis.annotations.Mapper;
/**
* 地图工具箱表 Mapper 接口
*
* @author migration
*/
@Mapper
public interface MsMaptoolboxBMapper extends BaseMapper<MsMaptoolboxB> {
}

View File

@ -0,0 +1,34 @@
package com.yfd.platform.qgc_sys.mapLayer.service;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerAo;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo;
import java.util.List;
/**
* 地图图层业务服务接口
*
* @author migration
*/
public interface IMapLayerBizService {
/**
* 获取所有地图图层树
*
* @param flag 查询参数
* @return 图层树列表
*/
List<MsMaplayerB> getMapLayerTree(MapLayerAo flag);
/**
* 根据模块ID系统ID模板ID获取地图图层树关联MS_MAPMODULE_B
*
* @param moduleId 模块ID
* @param systemId 系统ID
* @param templateId 模板ID
* @return 图层树列表
*/
List<MapLayerVo> getMapLayerTree(String moduleId, String systemId, String templateId);
}

View File

@ -0,0 +1,28 @@
package com.yfd.platform.qgc_sys.mapLayer.service;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import java.util.List;
/**
* 地图图层基础服务接口
*
* @author migration
*/
public interface IMapLayerService {
/**
* 新增或修改图层
*
* @param layer 图层实体
* @return 是否成功
*/
boolean saveOrUpdate(MsMaplayerB layer);
/**
* 根据ID列表删除图层
*
* @param ids 图层ID列表
*/
void delelteByIds(List<String> ids);
}

View File

@ -0,0 +1,42 @@
package com.yfd.platform.qgc_sys.mapLayer.service;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import java.util.List;
/**
* 地图图例业务服务接口
*
* @author migration
*/
public interface IMapLegendBizService {
/**
* 获取地图图例树下拉选
*
* @param flag 数据类型过滤null=全部, 1=结构, 2=数据
* @return 图例树
*/
List<MapLegendVo> getMapLegendTree(Integer flag);
/**
* 根据模块ID系统ID模板ID获取模块关联的图例列表
*
* @param moduleId 模块ID
* @param systemId 系统ID
* @param templateId 模板ID
* @return 图例列表
*/
List<MapLegendVo> getModuleMapLegendList(String moduleId, String systemId, String templateId);
/**
* 根据模块ID获取模块关联的图例列表单参数版本
*
* @param moduleId 模块ID
* @return 图例列表
*/
List<MapLegendVo> getModuleMapLegendList(String moduleId);
}

View File

@ -0,0 +1,22 @@
package com.yfd.platform.qgc_sys.mapLayer.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import java.util.List;
/**
* 地图图例基础服务接口
*
* @author migration
*/
public interface IMapLegendService extends IService<MsMaplegendB> {
/**
* 根据ID列表删除图例
*
* @param ids 图例ID列表
*/
void delelteByIds(List<String> ids);
}

View File

@ -0,0 +1,214 @@
package com.yfd.platform.qgc_sys.mapLayer.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerAo;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplayerBMapper;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerBizService;
import com.yfd.platform.system.domain.SysDictionary;
import com.yfd.platform.system.domain.SysDictionaryItems;
import com.yfd.platform.system.domain.SysUser;
import com.yfd.platform.system.mapper.SysDictionaryItemsMapper;
import com.yfd.platform.system.mapper.SysDictionaryMapper;
import com.yfd.platform.system.mapper.SysUserMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 地图图层业务服务实现
*
* @author migration
*/
@Service
public class MapLayerBizServiceImpl implements IMapLayerBizService {
@Resource
private MsMaplayerBMapper msMaplayerBMapper;
@Resource
private SysDictionaryMapper sysDictionaryMapper;
@Resource
private SysDictionaryItemsMapper sysDictionaryItemsMapper;
@Resource
private SysUserMapper sysUserMapper;
@Resource
private MsMapmoduleBMapper msMapmoduleBMapper;
@Override
public List<MsMaplayerB> getMapLayerTree(MapLayerAo flag) {
// 1. 查询字典 "DTZJLX" 获取需要排除的图层类型
Set<String> excludeTypes = getExcludeTypes();
// 2. 查询所有未删除的图层数据排除特殊类型支持按名称模糊查询
LambdaQueryWrapper<MsMaplayerB> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(MsMaplayerB::getIsDeleted, 0);
// 排除特殊类型
if (!excludeTypes.isEmpty()) {
wrapper.and(w -> w.notIn(MsMaplayerB::getType, excludeTypes).or().isNull(MsMaplayerB::getType));
}
// 按名称模糊查询
if (StrUtil.isNotBlank(flag.getTitle())) {
wrapper.like(MsMaplayerB::getTitle, flag.getTitle());
}
List<MsMaplayerB> mapLayers = msMaplayerBMapper.selectList(wrapper);
// 3. 当按名称过滤时补充缺失的父级节点
if (StrUtil.isNotBlank(flag.getTitle()) && !mapLayers.isEmpty()) {
Set<String> hasKeys = mapLayers.stream()
.filter(it -> StrUtil.isNotBlank(it.getKey()))
.map(MsMaplayerB::getKey)
.collect(Collectors.toSet());
Set<String> idList = new HashSet<>();
for (MsMaplayerB layer : mapLayers) {
if (StrUtil.isNotBlank(layer.getFullPath()) && StrUtil.isNotBlank(layer.getKey())) {
String[] ids = layer.getFullPath().replace(layer.getKey() + ",", "").split(",");
for (String id : ids) {
if (StrUtil.isNotBlank(id) && !hasKeys.contains(id)) {
idList.add(id);
}
}
}
}
if (!idList.isEmpty()) {
LambdaQueryWrapper<MsMaplayerB> parentWrapper = new LambdaQueryWrapper<>();
parentWrapper.eq(MsMaplayerB::getIsDeleted, 0)
.in(MsMaplayerB::getKey, idList);
List<MsMaplayerB> parentLayers = msMaplayerBMapper.selectList(parentWrapper);
if (!parentLayers.isEmpty()) {
mapLayers.addAll(parentLayers);
}
}
}
// 4. 填充创建人名称
if (!mapLayers.isEmpty()) {
LambdaQueryWrapper<SysUser> userWrapper = new LambdaQueryWrapper<>();
List<SysUser> users = sysUserMapper.selectList(userWrapper);
Map<String, String> userMap = users.stream()
.collect(Collectors.toMap(SysUser::getId, SysUser::getNickname, (v1, v2) -> v1));
for (MsMaplayerB layer : mapLayers) {
if (StrUtil.isNotBlank(layer.getRecordUser())) {
layer.setDisplayRecordUser(userMap.get(layer.getRecordUser()));
}
}
}
// 5. 构建树结构取第一级
List<MsMaplayerB> parents = mapLayers.stream()
.filter(p -> StrUtil.isBlank(p.getParentId()))
.sorted(Comparator.comparing(MsMaplayerB::getRecordTime, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
// 通过父级id分组子节点
Map<String, List<MsMaplayerB>> childrenMap = mapLayers.stream()
.filter(p -> StrUtil.isNotBlank(p.getParentId()))
.collect(Collectors.groupingBy(MsMaplayerB::getParentId));
// 递归构建树
convertTree(parents, childrenMap);
return parents;
}
/**
* 查询字典 "DTZJLX" 中需要排除的图层类型值
*/
private Set<String> getExcludeTypes() {
Set<String> excludeTypes = new HashSet<>();
try {
LambdaQueryWrapper<SysDictionary> dictWrapper = new LambdaQueryWrapper<>();
dictWrapper.eq(SysDictionary::getDictCode, "DTZJLX");
SysDictionary dictionary = sysDictionaryMapper.selectOne(dictWrapper);
if (dictionary != null) {
LambdaQueryWrapper<SysDictionaryItems> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.eq(SysDictionaryItems::getDictId, dictionary.getId());
List<SysDictionaryItems> items = sysDictionaryItemsMapper.selectList(itemWrapper);
if (items != null) {
excludeTypes = items.stream()
.map(SysDictionaryItems::getDictName)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
}
}
} catch (Exception e) {
// 字典查询失败时不中断主流程
}
return excludeTypes;
}
/**
* 递归转换图层数据为树结构
*/
private void convertTree(List<MsMaplayerB> parents, Map<String, List<MsMaplayerB>> childrenMap) {
if (parents == null || parents.isEmpty()) {
return;
}
for (MsMaplayerB parent : parents) {
String id = parent.getId();
List<MsMaplayerB> children = childrenMap.get(id);
if (children != null && !children.isEmpty()) {
parent.setChildren(children);
convertTree(children, childrenMap);
}else {
// 当没有子节点时显式设置为空列表保证 JSON 输出为 [] 而不是 null
parent.setChildren(new ArrayList<>());
}
}
}
@Override
public List<MapLayerVo> getMapLayerTree(String moduleId, String systemId, String templateId) {
// 通过 JOIN 查询 MS_MAPLAYER_B MS_MAPMODULE_B
List<MapLayerVo> allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId);
if (allLayers == null || allLayers.isEmpty()) {
return new ArrayList<>();
}
// 取第一级
List<MapLayerVo> parents = allLayers.stream()
.filter(p -> StrUtil.isBlank(p.getParentId()))
.collect(Collectors.toList());
// 通过父级id分组子节点
Map<String, List<MapLayerVo>> childrenMap = allLayers.stream()
.filter(p -> StrUtil.isNotBlank(p.getParentId()))
.collect(Collectors.groupingBy(MapLayerVo::getParentId));
// 递归构建树
convertLayerVoTree(parents, childrenMap);
return parents;
}
/**
* 递归转换图层VO为树结构
*/
private void convertLayerVoTree(List<MapLayerVo> parents, Map<String, List<MapLayerVo>> childrenMap) {
if (parents == null || parents.isEmpty()) {
return;
}
for (MapLayerVo parent : parents) {
String id = parent.getId();
List<MapLayerVo> children = childrenMap.get(id);
if (children != null && !children.isEmpty()) {
parent.setChildren(children);
convertLayerVoTree(children, childrenMap);
}
}
}
}

View File

@ -0,0 +1,256 @@
package com.yfd.platform.qgc_sys.mapLayer.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplegendBMapper;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplayerBMapper;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerService;
import com.yfd.platform.utils.SecurityUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 地图图层基础服务实现
*
* @author migration
*/
@Service
public class MapLayerServiceImpl implements IMapLayerService {
@Resource
private MsMaplayerBMapper msMaplayerBMapper;
@Resource
private MsMaplegendBMapper msMaplegendBMapper;
@Resource
private MsMapmoduleBMapper msMapmoduleBMapper;
@Override
public boolean saveOrUpdate(MsMaplayerB layer) {
// 校验编码唯一性
if (hasSameCode(layer)) {
throw new BizException("存在相同编码的数据");
}
// 当type等于SLDT/DXDT/YXDT校验地图json格式
String paramJson = layer.getParamJson();
String type = layer.getType();
if (("SLDT".equals(type) || "DXDT".equals(type) || "YXDT".equals(type))
&& StrUtil.isNotBlank(paramJson)) {
try {
JSONUtil.toBean(paramJson, HashMap.class);
} catch (Exception e) {
throw new BizException("请检查地图json格式");
}
}
if (StrUtil.isNotBlank(layer.getId())) {
// 修改
msMaplayerBMapper.updateById(layer);
// 判断是否启用且有子级则子数据都要设置为启用状态
if (layer.getEnable() != null && layer.getEnable() == 1
&& layer.getHasChildren() != null && layer.getHasChildren() == 1) {
LambdaQueryWrapper<MsMaplayerB> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(MsMaplayerB::getIsDeleted, 0)
.like(MsMaplayerB::getFullPath, layer.getId() + ",");
List<MsMaplayerB> children = msMaplayerBMapper.selectList(wrapper);
for (MsMaplayerB child : children) {
MsMaplayerB temp = new MsMaplayerB();
temp.setId(child.getId());
temp.setEnable(1);
msMaplayerBMapper.updateById(temp);
}
}
} else {
// 新增
String id = IdUtil.fastSimpleUUID();
layer.setId(id);
// 获取同级子节点计算最大排序号
List<MsMaplayerB> subList = getSubList(layer.getParentId());
Integer maxOrderIndex = getMaxOrderIndex(subList);
layer.setOrderIndex(maxOrderIndex + 1);
MsMaplayerB parentLayer = null;
if (StrUtil.isNotBlank(layer.getParentId())) {
// 获取父级信息
parentLayer = msMaplayerBMapper.selectById(layer.getParentId());
if (parentLayer == null) {
throw new BizException("获取父级图层信息失败.请检查父级图层(parentId)是否存在.");
}
// 设置key
layer.setKey(parentLayer.getKey() + "-" + layer.getOrderIndex());
// 设置fullPath
layer.setFullPath(parentLayer.getFullPath() + layer.getKey() + ",");
// 设置treeLevel
layer.setTreeLevel(parentLayer.getTreeLevel() + 1);
// 设置hasChildren
layer.setHasChildren(0);
} else {
// 无父级则为创建的顶级图层
layer.setTreeLevel(1);
layer.setKey("0-" + layer.getOrderIndex());
layer.setFullPath(layer.getKey() + ",");
layer.setHasChildren(0);
}
// 设置可选
layer.setCheckable(0);
// 设置不可用
layer.setEnable(0);
String userId = getCurrentUserId();
layer.setRecordUser(userId);
layer.setModifyUser(userId);
layer.setRecordTime(new Date());
msMaplayerBMapper.insert(layer);
// 新增完成后设置父级hasChildren为1
if (parentLayer != null) {
MsMaplayerB temp = new MsMaplayerB();
temp.setId(parentLayer.getId());
temp.setHasChildren(1);
msMaplayerBMapper.updateById(temp);
}
}
return true;
}
@Override
public void delelteByIds(List<String> ids) {
if (ids == null || ids.isEmpty()) {
return;
}
for (String id : ids) {
// 根据id查询是否有子级数据
MsMaplayerB layer = msMaplayerBMapper.selectById(id);
if (layer == null) {
continue;
}
LambdaQueryWrapper<MsMaplayerB> childWrapper = new LambdaQueryWrapper<>();
childWrapper.eq(MsMaplayerB::getParentId, id);
List<MsMaplayerB> children = msMaplayerBMapper.selectList(childWrapper);
if (!children.isEmpty()) {
throw new BizException("有子级图层,无法删除.");
}
// 判断是否启用
if (layer.getEnable() != null && layer.getEnable() == 1) {
throw new BizException("无法删除已启用的图层.");
}
// 判断是否已经关联了图例
QueryWrapper<MsMaplegendB> legendWrapper = new QueryWrapper<>();
legendWrapper.eq("LAYER_CODE", layer.getCode());
legendWrapper.eq("IS_DELETED", 0);
List<MsMaplegendB> legendList = msMaplegendBMapper.selectList(legendWrapper);
if (!legendList.isEmpty()) {
throw new BizException("图层关联了图例,无法删除.");
}
// 判断父级是否存在
if (StrUtil.isBlank(layer.getParentId())) {
// 逻辑删除
msMaplayerBMapper.deleteById(id);
} else {
MsMaplayerB parent = msMaplayerBMapper.selectById(layer.getParentId());
// 逻辑删除
msMaplayerBMapper.deleteById(id);
// 判断父级下是否只有一个子级如果是当前图层则设置父级hasChildren为0
if (parent != null) {
LambdaQueryWrapper<MsMaplayerB> siblingWrapper = new LambdaQueryWrapper<>();
siblingWrapper.eq(MsMaplayerB::getParentId, parent.getId());
List<MsMaplayerB> siblings = msMaplayerBMapper.selectList(siblingWrapper);
if (siblings.size() == 1 && id.equals(siblings.get(0).getId())) {
MsMaplayerB temp = new MsMaplayerB();
temp.setId(parent.getId());
temp.setHasChildren(0);
msMaplayerBMapper.updateById(temp);
}
}
}
// 判断是否关联菜单删除关联
QueryWrapper<MsMapmoduleB> moduleWrapper = new QueryWrapper<>();
moduleWrapper.eq("RES_TYPE", "layer");
moduleWrapper.eq("IS_DELETED", 0);
moduleWrapper.eq("RES_ID", id);
List<MsMapmoduleB> moduleList = msMapmoduleBMapper.selectList(moduleWrapper);
if (!moduleList.isEmpty()) {
Set<String> moduleIds = moduleList.stream()
.map(MsMapmoduleB::getId)
.collect(Collectors.toSet());
msMapmoduleBMapper.deleteBatchIds(moduleIds);
}
}
}
/**
* 获取当前用户ID
*/
private String getCurrentUserId() {
try {
return SecurityUtils.getUserId();
} catch (Exception e) {
return null;
}
}
/**
* 根据父级编码查询子一级数据
*/
private List<MsMaplayerB> getSubList(String parentId) {
LambdaQueryWrapper<MsMaplayerB> wrapper = new LambdaQueryWrapper<>();
if (StrUtil.isNotBlank(parentId)) {
wrapper.eq(MsMaplayerB::getParentId, parentId);
} else {
wrapper.eq(MsMaplayerB::getTreeLevel, 1);
}
wrapper.orderByAsc(MsMaplayerB::getOrderIndex);
return msMaplayerBMapper.selectList(wrapper);
}
/**
* 获取最大排序号
*/
private Integer getMaxOrderIndex(List<MsMaplayerB> list) {
if (list.isEmpty()) {
return 0;
}
return list.stream()
.sorted(Comparator.comparing(MsMaplayerB::getOrderIndex, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList())
.get(0)
.getOrderIndex();
}
/**
* 判断同级下是否有编码一样的数据
*/
private boolean hasSameCode(MsMaplayerB layer) {
LambdaQueryWrapper<MsMaplayerB> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(MsMaplayerB::getCode, layer.getCode())
.eq(MsMaplayerB::getIsDeleted, 0);
List<MsMaplayerB> list = msMaplayerBMapper.selectList(wrapper);
if (list.isEmpty()) {
return false;
}
return !list.get(0).getId().equals(layer.getId());
}
}

View File

@ -0,0 +1,212 @@
package com.yfd.platform.qgc_sys.mapLayer.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendBizService;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendService;
import jakarta.annotation.Resource;
import cn.hutool.core.collection.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 地图图例业务服务实现类
*
* @author migration
*/
@Service
public class MapLegendBizServiceImpl implements IMapLegendBizService {
@Resource
private IMapLegendService legendService;
@Resource
private MsMapmoduleBMapper msMapmoduleBMapper;
/**
* 数据类型枚举1=结构
*/
private static final int DATA_TYPE_STRUCT = 1;
/**
* 数据类型枚举2=数据
*/
private static final int DATA_TYPE_DATA = 2;
@Override
public List<MapLegendVo> getMapLegendTree(Integer flag) {
// 查询所有数据
LambdaQueryWrapper<MsMaplegendB> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(MsMaplegendB::getIsDeleted, 0);
if (flag != null) {
if (flag == DATA_TYPE_STRUCT) {
wrapper.eq(MsMaplegendB::getDataType, DATA_TYPE_STRUCT);
} else if (flag == DATA_TYPE_DATA) {
wrapper.eq(MsMaplegendB::getDataType, DATA_TYPE_DATA);
}
}
List<MsMaplegendB> legendList = legendService.list(wrapper);
// 转换为VO
List<MapLegendVo> list = new ArrayList<>();
for (MsMaplegendB legend : legendList) {
MapLegendVo vo = new MapLegendVo();
BeanUtil.copyProperties(legend, vo);
list.add(vo);
}
// 取第一级
List<MapLegendVo> parents = list.stream()
.filter(p -> StringUtils.isBlank(p.getParentId()))
.sorted(Comparator.comparing(MapLegendVo::getOrderIndex, Comparator.nullsLast(Integer::compareTo)))
.collect(Collectors.toList());
// 通过父级id分组
Map<String, List<MapLegendVo>> mapList = list.stream()
.filter(p -> StringUtils.isNotBlank(p.getParentId()))
.collect(Collectors.groupingBy(MapLegendVo::getParentId));
// 递归转换数据为树结构
convertTree(parents, mapList);
return parents;
}
/**
* 递归转换树结构
*/
private void convertTree(List<MapLegendVo> parents, Map<String, List<MapLegendVo>> subList) {
if (parents == null || parents.isEmpty()) {
return;
}
for (MapLegendVo p : parents) {
String id = p.getId();
for (Map.Entry<String, List<MapLegendVo>> entry : subList.entrySet()) {
String pid = entry.getKey();
List<MapLegendVo> children = entry.getValue();
if (id.equals(pid)) {
p.setChildrenList(children);
convertTree(children, subList);
}
}
}
}
@Override
public List<MapLegendVo> getModuleMapLegendList(String moduleId, String systemId, String templateId) {
List<MapLegendVo> list = new ArrayList<>();
// 通过 JOIN 查询 MS_MAPLEGEND_B MS_MAPMODULE_B
List<MapLegendVo> bbiMapLegendList = msMapmoduleBMapper.getModuleMapLegendList(moduleId, systemId, templateId);
if (bbiMapLegendList == null || bbiMapLegendList.isEmpty()) {
return list;
}
// 按父级分组
Map<String, List<MapLegendVo>> collect = bbiMapLegendList.stream()
.collect(Collectors.groupingBy(MapLegendVo::getParentName));
// 遍历
collect.forEach((str, mapLegendVoList) -> {
MapLegendVo mapLegendVo = new MapLegendVo();
mapLegendVo.setName(str);
mapLegendVo.setPSort(mapLegendVoList.get(0).getPSort());
// 按子级分组名分组
Map<String, List<MapLegendVo>> collect1 = mapLegendVoList.stream()
.collect(Collectors.groupingBy(MapLegendVo::getGroupName));
List<MapLegendVo> childrenList = new ArrayList<>();
for (String groupNameStr : collect1.keySet()) {
List<MapLegendVo> list1 = collect1.get(groupNameStr);
// 如果子级和父级名称相同则不用子级
if (groupNameStr.equalsIgnoreCase(str)) {
childrenList = list1;
} else {
MapLegendVo mapLegendVoChild = new MapLegendVo();
mapLegendVoChild.setName(groupNameStr);
if (null != list1 && list1.size() > 0) {
mapLegendVoChild.setPSort(list1.get(0).getPSort());
}
mapLegendVoChild.setChildrenList(list1);
childrenList.add(mapLegendVoChild);
}
}
List<MapLegendVo> collect2 = childrenList.stream()
.sorted(Comparator.comparing(MapLegendVo::getPSort, Comparator.nullsLast(Integer::compareTo)))
.collect(Collectors.toList());
mapLegendVo.setChildrenList(collect2);
list.add(mapLegendVo);
});
return list.stream()
.sorted(Comparator.comparing(MapLegendVo::getPSort, Comparator.nullsLast(Integer::compareTo)))
.collect(Collectors.toList());
}
@Override
public List<MapLegendVo> getModuleMapLegendList(String moduleId) {
List<MapLegendVo> list = new ArrayList<>();
// 通过 JOIN 查询 MS_MAPLEGEND_B MS_MAPMODULE_B单参数版本
List<MapLegendVo> bbiMapLegendList = msMapmoduleBMapper.getModuleMapLegendList(moduleId);
if (bbiMapLegendList == null || bbiMapLegendList.isEmpty()) {
return list;
}
// 收集父级ID查询父级图例的layerCode
Set<String> parentIdList = bbiMapLegendList.stream()
.map(MapLegendVo::getParentId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<String, String> layerCodeMap = new HashMap<>();
if (CollectionUtil.isNotEmpty(parentIdList)) {
List<MsMaplegendB> parentLegendList = legendService.lambdaQuery()
.in(MsMaplegendB::getId, parentIdList)
.list();
layerCodeMap = parentLegendList.stream()
.filter(item -> item.getLayerCode() != null)
.collect(Collectors.toMap(MsMaplegendB::getId, MsMaplegendB::getLayerCode, (v1, v2) -> v1));
}
// 按父级分组
Map<String, List<MapLegendVo>> collect = bbiMapLegendList.stream()
.collect(Collectors.groupingBy(MapLegendVo::getParentName));
// 遍历
Map<String, String> finalLayerCodeMap = layerCodeMap;
collect.forEach((str, mapLegendVoList) -> {
MapLegendVo mapLegendVo = new MapLegendVo();
mapLegendVo.setName(str);
mapLegendVo.setPSort(mapLegendVoList.get(0).getPSort());
mapLegendVo.setLayerCode(finalLayerCodeMap.get(mapLegendVoList.get(0).getParentId()));
// 按子级分组名分组
Map<String, List<MapLegendVo>> collect1 = mapLegendVoList.stream()
.collect(Collectors.groupingBy(MapLegendVo::getGroupName));
List<MapLegendVo> childrenList = new ArrayList<>();
for (String groupNameStr : collect1.keySet()) {
List<MapLegendVo> list1 = collect1.get(groupNameStr);
// 如果子级和父级名称相同则不用子级
if (groupNameStr.equalsIgnoreCase(str)) {
childrenList = list1;
} else {
MapLegendVo mapLegendVoChild = new MapLegendVo();
mapLegendVoChild.setName(groupNameStr);
if (null != list1 && list1.size() > 0) {
mapLegendVoChild.setPSort(list1.get(0).getPSort());
}
mapLegendVoChild.setChildrenList(list1);
childrenList.add(mapLegendVoChild);
}
}
List<MapLegendVo> collect2 = childrenList.stream()
.sorted(Comparator.comparing(MapLegendVo::getPSort, Comparator.nullsLast(Integer::compareTo)))
.collect(Collectors.toList());
mapLegendVo.setChildrenList(collect2);
list.add(mapLegendVo);
});
return list.stream()
.sorted(Comparator.comparing(MapLegendVo::getPSort, Comparator.nullsLast(Integer::compareTo)))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,97 @@
package com.yfd.platform.qgc_sys.mapLayer.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplegendB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplegendBMapper;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 地图图例基础服务实现
*
* @author migration
*/
@Service
public class MapLegendServiceImpl extends ServiceImpl<MsMaplegendBMapper, MsMaplegendB>
implements IMapLegendService {
@Resource
private MsMapmoduleBMapper msMapmoduleBMapper;
@Override
public void delelteByIds(List<String> ids) {
if (CollectionUtil.isEmpty(ids)) {
return;
}
for (String id : ids) {
// 根据id查询是否有数据或者子级数据
MsMaplegendB legend = this.getById(id);
if (legend == null) {
continue;
}
LambdaQueryWrapper<MsMaplegendB> childWrapper = new LambdaQueryWrapper<>();
childWrapper.eq(MsMaplegendB::getParentId, id)
.eq(MsMaplegendB::getIsDeleted, 0);
List<MsMaplegendB> children = this.list(childWrapper);
if (!children.isEmpty()) {
throw new BizException("有子级图例,无法删除.");
}
// 判断是否内置的数据
if (legend.getInternal() != null && legend.getInternal() == 1) {
throw new BizException("无法删除系统内置的图例.");
}
// 判断是否启用
if (legend.getEnable() != null && legend.getEnable() == 1) {
throw new BizException("无法删除已启用的图例.");
}
// 判断图例是否关联了菜单
QueryWrapper<MsMapmoduleB> moduleWrapper = new QueryWrapper<>();
moduleWrapper.eq("IS_DELETED", 0);
moduleWrapper.eq("RES_TYPE", "legend");
moduleWrapper.eq("RES_ID", id);
List<MsMapmoduleB> moduleList = msMapmoduleBMapper.selectList(moduleWrapper);
if (!moduleList.isEmpty()) {
throw new BizException("无法删除:图例关联了菜单.");
}
// 判断父级是否存在
if (StrUtil.isBlank(legend.getParentId())) {
this.removeById(id); // 逻辑删除
return;
}
// 删除数据
this.removeById(id); // 逻辑删除
if (StrUtil.isNotBlank(legend.getParentId())) {
// 判断父级下是否只有一个子级,如果只有一个子级并且是当前图例,则需要设置父级的hasChildren为0
MsMaplegendB parent = this.getById(legend.getParentId());
if (parent != null) {
LambdaQueryWrapper<MsMaplegendB> siblingWrapper = new LambdaQueryWrapper<>();
siblingWrapper.eq(MsMaplegendB::getParentId, parent.getId());
List<MsMaplegendB> siblings = this.list(siblingWrapper);
if (siblings.size() == 1 && id.equals(siblings.get(0).getId())) {
MsMaplegendB updateParent = new MsMaplegendB();
updateParent.setId(parent.getId());
updateParent.setHasChildren(0);
this.updateById(updateParent);
}
}
}
}
}
}

View File

@ -0,0 +1,162 @@
package com.yfd.platform.qgc_sys.mapcfg.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.common.DataSourceLoadOptionsBase;
import com.yfd.platform.common.DataSourceRequest;
import com.yfd.platform.common.DataSourceResult;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapcfg.service.IMapmoduleBService;
import com.yfd.platform.qgc_sys.mapcfg.entity.MapmoduleVo;
import com.yfd.platform.system.domain.SysMenu;
import com.yfd.platform.system.mapper.SysMenuMapper;
import com.yfd.platform.utils.DataSourceRequestUtil;
import com.yfd.platform.utils.QgcQueryWrapperUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* 地图菜单配置控制层
*
* @author migration
*/
@RestController
@RequestMapping("/mapmodule")
@Tag(name = "地图菜单配置")
public class MapmoduleBController {
@Resource
private IMapmoduleBService mapmoduleBService;
@Resource
private SysMenuMapper sysMenuMapper;
/**
* 条件过滤数据列表Kendo UI 数据源
*/
@Operation(summary = "条件过滤数据列表")
@PostMapping("/GetKendoList")
public ResponseResult getKendoList(@RequestBody DataSourceRequest dataSourceRequest) {
// 1. 构建查询条件
QueryWrapper<MsMapmoduleB> wrapper = DataSourceRequestUtil.buildQueryWrapper(
dataSourceRequest, MsMapmoduleB.class);
// 2. 分页参数处理 getMsstbprptList 完全一致
DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest();
Page<MsMapmoduleB> page = loadOptions == null ? null
: (Page<MsMapmoduleB>) QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake());
// 3. 执行分页查询
Page<MsMapmoduleB> resultPage;
if (page != null) {
resultPage = mapmoduleBService.page(page, wrapper);
} else {
List<MsMapmoduleB> list = mapmoduleBService.list(wrapper);
resultPage = new Page<>();
resultPage.setRecords(list);
resultPage.setTotal(list.size());
}
// 4. 构建 Kendo 数据源结果
DataSourceResult<MsMapmoduleB> dataSourceResult = new DataSourceResult<>();
List<MsMapmoduleB> dataList = resultPage.getRecords();
dataSourceResult.setData(dataList);
dataSourceResult.setTotal(resultPage.getTotal());
// 5. 翻译与额外字段填充原有逻辑保持不变
if (dataList != null && !dataList.isEmpty()) {
mapmoduleBService.translateDict(dataList);
Set<String> needQueryIds = dataList.stream()
.map(MsMapmoduleB::getModuleId)
.filter(id -> id != null && !"tycd".equals(id))
.collect(Collectors.toSet());
Map<String, String> menuNameMap = new HashMap<>();
if (!needQueryIds.isEmpty()) {
List<SysMenu> menus = sysMenuMapper.selectList(
new LambdaQueryWrapper<SysMenu>()
.in(SysMenu::getId, needQueryIds)
);
menuNameMap = menus.stream()
.collect(Collectors.toMap(
menu -> String.valueOf(menu.getId()),
SysMenu::getName
));
}
for (MsMapmoduleB entity : dataList) {
// 填充 moduleName
String moduleId = entity.getModuleId();
if (moduleId == null) {
entity.setModuleName(null);
} else if ("tycd".equals(moduleId)) {
entity.setModuleName("通用菜单");
entity.setChecked(1);
} else {
entity.setModuleName(menuNameMap.get(moduleId));
}
// 特殊 systemName
if ("qgc".equals(entity.getSystemId())) {
entity.setSystemName("qgc");
}
}
}
return ResponseResult.successData(dataSourceResult);
}
/**
* 地图图例关联系统及菜单
*/
@Operation(summary = "地图图例关联系统及菜单")
@PostMapping("/save")
public ResponseResult saveOrUpdate(@RequestBody MsMapmoduleB mapmoduleB) {
if (mapmoduleB == null) {
throw new BizException("对象不能为空.");
}
if (StrUtil.isBlank(mapmoduleB.getModuleId())) {
throw new BizException("关联菜单(moduleId)不能为空.");
}
if (StrUtil.isBlank(mapmoduleB.getSystemId())) {
throw new BizException("关联系统(systemId)不能为空.");
}
if (StrUtil.isBlank(mapmoduleB.getResType())) {
throw new BizException("资源类型(resType: layer=图层,legend=图例,mapbox=地图)不能为空");
}
if (StrUtil.isBlank(mapmoduleB.getResId())) {
throw new BizException("资源(resId)不能为空.");
}
return ResponseResult.successData(mapmoduleBService.saveOrUpdate(mapmoduleB));
}
/**
* 图例与系统菜单关联关系删除
*/
@Operation(summary = "图例与系统菜单关联关系删除")
@PostMapping("/delete")
public ResponseResult deleteData(@RequestBody List<String> ids) {
mapmoduleBService.delelteByIds(ids);
return ResponseResult.success("删除成功.");
}
/**
* 地图图层管理数据
*/
@Operation(summary = "地图图层管理数据")
@PostMapping("/getMapData")
public ResponseResult getMapData(@RequestBody MsMapmoduleB mapmoduleB) {
MapmoduleVo result = mapmoduleBService.getMapData(mapmoduleB);
return ResponseResult.successData(result);
}
}

View File

@ -0,0 +1,47 @@
package com.yfd.platform.qgc_sys.mapcfg.entity;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaptoolboxB;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 地图与菜单查询返回实体类
*
* @author migration
*/
@Data
@Schema(description = "地图菜单配置实体类")
public class MapmoduleVo {
/** GIS地图多功能区 */
@Schema(description = "GIS地图多功能区")
private List<MsMaptoolboxB> maptoolbox;
/** GIS默认数据加载 - 地形 */
@Schema(description = "GIS默认数据加载 - 地形")
private List<MsMaptoolboxB> terrain;
/** GIS默认数据加载 - 图例 */
@Schema(description = "GIS默认数据加载 - 图例")
private List<MsMaptoolboxB> legend;
/** GIS默认数据加载 - 内容模块 */
@Schema(description = "GIS默认数据加载 - 内容模块")
private List<MsMaptoolboxB> content;
/** GIS默认数据加载 - 环保监测 */
@Schema(description = "GIS默认数据加载 - 环保监测")
private List<MsMaptoolboxB> hbcj;
/** GIS图层区 */
@Schema(description = "GIS图层区")
private List<MapLayerVo> mapLayerVos;
/** 地图图例 */
@Schema(description = "地图图例")
private List<MapLegendVo> mapLegendVos;
}

View File

@ -0,0 +1,45 @@
package com.yfd.platform.qgc_sys.mapcfg.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapcfg.entity.MapmoduleVo;
import java.util.List;
/**
* 地图菜单配置服务接口
*
* @author migration
*/
public interface IMapmoduleBService extends IService<MsMapmoduleB> {
/**
* 新增或修改
*
* @param entity 实体
* @return 是否成功
*/
boolean saveOrUpdate(MsMapmoduleB entity);
/**
* 翻译字典值菜单名称
*
* @param list 数据列表
*/
void translateDict(List<MsMapmoduleB> list);
/**
* 根据ID列表删除关联关系
*
* @param ids ID列表
*/
void delelteByIds(List<String> ids);
/**
* 获取地图数据图层树图例工具箱
*
* @param mapmoduleB 查询参数
* @return 地图数据
*/
MapmoduleVo getMapData(MsMapmoduleB mapmoduleB);
}

View File

@ -0,0 +1,183 @@
package com.yfd.platform.qgc_sys.mapcfg.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB;
import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaptoolboxB;
import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerBizService;
import com.yfd.platform.qgc_sys.mapLayer.service.IMapLegendBizService;
import com.yfd.platform.qgc_sys.mapcfg.service.IMapmoduleBService;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo;
import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLegendVo;
import com.yfd.platform.qgc_sys.mapcfg.entity.MapmoduleVo;
import com.yfd.platform.system.domain.SysMenu;
import com.yfd.platform.system.mapper.SysMenuMapper;
import com.yfd.platform.utils.SecurityUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 地图菜单配置服务实现类
*
* @author migration
*/
@Service
public class MapmoduleBServiceImpl extends ServiceImpl<MsMapmoduleBMapper, MsMapmoduleB>
implements IMapmoduleBService {
@Resource
private SysMenuMapper sysMenuMapper;
@Resource
private IMapLayerBizService mapLayerBizService;
@Resource
private IMapLegendBizService mapLegendBizService;
@Override
public boolean saveOrUpdate(MsMapmoduleB entity) {
if (StrUtil.isBlank(entity.getId())) {
// 新增
entity.setRecordUser(SecurityUtils.getUserId());
this.baseMapper.insert(entity);
} else {
// 修改
this.baseMapper.updateById(entity);
}
return true;
}
@Override
public void delelteByIds(List<String> ids) {
if (ids != null) {
for (String id : ids) {
this.baseMapper.deleteById(id);
}
}
}
@Override
public void translateDict(List<MsMapmoduleB> list) {
if (list == null || list.isEmpty()) {
return;
}
// 收集所有 moduleId
List<String> moduleIds = list.stream()
.filter(it -> StrUtil.isNotBlank(it.getModuleId()))
.map(MsMapmoduleB::getModuleId)
.distinct()
.collect(Collectors.toList());
if (moduleIds.isEmpty()) {
return;
}
// 查询本地菜单表获取名称
LambdaQueryWrapper<SysMenu> menuWrapper = new LambdaQueryWrapper<>();
menuWrapper.in(SysMenu::getId, moduleIds);
List<SysMenu> menus = sysMenuMapper.selectList(menuWrapper);
Map<String, String> menuMap = new HashMap<>();
menuMap.put("common", "通用菜单");
for (SysMenu menu : menus) {
menuMap.put(menu.getId(), menu.getName());
}
// 设置菜单名称
for (MsMapmoduleB item : list) {
if (StrUtil.isNotBlank(item.getModuleId())) {
String name = menuMap.get(item.getModuleId());
if (name != null) {
item.setModuleName(name);
}
}
}
}
@Override
public MapmoduleVo getMapData(MsMapmoduleB mapmoduleB) {
// 模板ID暂时不使用MS_TEMPLATE_B表直接取传入的templateId
String templateId = mapmoduleB.getTemplateId();
// ========== 以下代码预留后续启用MS_TEMPLATE_B表时使用 ==========
// String templateId = null;
// // getDescription用于区分是在模块页面中还是在系统配置中调用该接口
// if (StrUtil.isBlank(mapmoduleB.getTemplateId()) && StrUtil.isNotBlank(mapmoduleB.getDescription())) {
// // 如果用户没有该模块的模板权限,但该模块有指定默认模板则返回默认模板数据
// LambdaQueryWrapper<MsTemplateB> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// lambdaQueryWrapper.eq(MsTemplateB::getIsDefault, 1);
// lambdaQueryWrapper.eq(MsTemplateB::getIsDeleted, 0);
// lambdaQueryWrapper.eq(MsTemplateB::getModuleId, mapmoduleB.getModuleId());
// MsTemplateB msTemplateB = msTemplateBMapper.selectOne(lambdaQueryWrapper);
// if (msTemplateB != null) {
// templateId = msTemplateB.getId();
// }
// } else {
// templateId = mapmoduleB.getTemplateId();
// }
// ========== 预留代码结束 ==========
MapmoduleVo mapmoduleVo = new MapmoduleVo();
// 1. GIS图层区
List<MapLayerVo> layer = mapLayerBizService.getMapLayerTree(
mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId);
mapmoduleVo.setMapLayerVos(layer);
// 2. 地图图例
List<MapLegendVo> legend = mapLegendBizService.getModuleMapLegendList(
mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId);
mapmoduleVo.setMapLegendVos(legend);
// 3. 地图工具箱
List<MsMaptoolboxB> mapToolBox = baseMapper.getMapToolBox(
mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId);
// 按toolType分组
Map<Integer, List<MsMaptoolboxB>> groupedByType = mapToolBox.stream()
.collect(Collectors.groupingBy(MsMaptoolboxB::getToolType));
// 图例toolType=2
mapmoduleVo.setLegend(groupedByType.get(2));
// 内容模块toolType=3
List<MsMaptoolboxB> contentLayerTree = getContentLayerTree(groupedByType);
mapmoduleVo.setContent(contentLayerTree);
// 环保监测toolType=4
mapmoduleVo.setHbcj(groupedByType.get(4));
return mapmoduleVo;
}
/**
* 获取内容模块图层树
*/
private List<MsMaptoolboxB> getContentLayerTree(Map<Integer, List<MsMaptoolboxB>> groupedByType) {
List<MsMaptoolboxB> msMaptoolboxBList = new java.util.ArrayList<>();
List<MsMaptoolboxB> contentToolBoxList = groupedByType.get(3);
if (contentToolBoxList != null) {
for (MsMaptoolboxB msMaptoolboxB : contentToolBoxList) {
if (StrUtil.isNotBlank(msMaptoolboxB.getPatentId())) {
// 如果内容模块没有进行配置则默认全部勾选
if (msMaptoolboxB.getChecked() == null) {
msMaptoolboxB.setChecked(1);
}
msMaptoolboxBList.add(msMaptoolboxB);
}
}
}
return msMaptoolboxBList;
}
}

View File

@ -98,3 +98,9 @@ task:
keep-alive-seconds: 60
# 队列容量
queue-capacity: 50
attachment:
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/delete}

View File

@ -7,7 +7,7 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/dev-api'
# 本地环境
VITE_APP_BASE_URL = 'http://localhost:8093'
# VITE_APP_BASE_URL = 'http://10.84.121.122:8093'
# 测试环境
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'

View File

@ -8,3 +8,102 @@ export function getBaseWbsb(data: any) {
data: data
});
}
// 水电开发及环境监测 - 获取列表数据
export function getVmsstbprptKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
method: 'post',
data: data
});
}
// 环保设施建设情况
export function environmentalProtectionConstruction(data: any) {
return request({
url: '/api/wmp-env-server/env/operat/GetKendoListCust',
method: 'post',
data: data
});
}
// 环保设施弹框表格
export function envOpdGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/env/oped/GetKendoListCust',
method: 'post',
data: data
});
}
// 获取基地列表
export function getBaseList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
method: 'post',
data: data
});
}
// 获取设施介绍列表
export function getMsstbprptKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
method: 'post',
data: data
});
}
// 环保自动监测工作开展情况
export function baseEvnmAutoMonitorGetKendoListCust(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
method: 'post',
data: data
});
}
// 环保自动监测弹窗列表
export function vmsstbprptGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
method: 'post',
data: data
});
}
// 获取基本信息
export function baseMsstbprptGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
method: 'post',
data: data
});
}
// 降水量实时数据
export function getBaseStbprpDataPPTNR(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=PPTN_R',
method: 'post',
data: data
});
}
// 气温气压实时数据
export function getBaseStbprpDataTMPR(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=TMP_R',
method: 'post',
data: data
});
}
// 风速风向实时数据
export function getBaseStbprpDataWNDR(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=WND_R',
method: 'post',
data: data
});
}

View File

@ -547,7 +547,7 @@ export function getFlowDischargeYear(data: any) {
}
export function getFlowDischargeList(code: string, data: any) {
return request({
url: `/apo/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=${code}`,
url: `/api/dec-lygk-base-server/base/stbprpData/GetKendoListCust?tbCode=${code}`,
method: 'post',
data
});

View File

@ -3,7 +3,7 @@ import request from '@/utils/request';
//水生生态调查情况-下拉选择框
export function wbsbGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
url: '/eq/wbsb/GetKendoList',
method: 'post',
data
});
@ -12,7 +12,7 @@ export function wbsbGetKendoList(data: any) {
// 水生生态调查情况-获取默认年份
export function getDftYear(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/year/qgc/GetKendoListCust',
url: '/wte/we/fisht/year/qgc/GetKendoListCust',
method: 'post',
data
});
@ -21,7 +21,7 @@ export function getDftYear(data: any) {
// 水生生态调查情况-获取统计数据
export function getWeFisht(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/statics/GetKendoListCust',
url: '/wte/we/fisht/statics/GetKendoListCust',
method: 'post',
data
});
@ -30,7 +30,7 @@ export function getWeFisht(data: any) {
// 水生生态调查概况-表格数据接口
export function getWeStaticsSecond(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/statics/getWeStaticsSecond',
url: '/wte/we/fisht/statics/getWeStaticsSecond',
method: 'post',
data
});
@ -39,7 +39,7 @@ export function getWeStaticsSecond(data: any) {
// 沿程鱼类变化-图表数据接口
export function fishChanges2(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/stat/GetKendoListCust',
url: '/wte/we/fisht/stat/GetKendoListCust',
method: 'post',
data
});
@ -48,7 +48,7 @@ export function fishChanges2(data: any) {
// 沿程鱼类变化-详情表格接口
export function fishDetail(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/detail/GetKendoListCust',
url: '/wte/we/fisht/detail/GetKendoListCust',
method: 'post',
data
});
@ -57,7 +57,7 @@ export function fishDetail(data: any) {
// 沿程鱼类变化-Tab数量统计接口
export function getTabNum(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/statTab/GetKendoListCust',
url: '/wte/we/fisht/statTab/GetKendoListCust',
method: 'post',
data
});
@ -66,11 +66,11 @@ export function getTabNum(data: any) {
// 沿程鱼类变化-获取断面列表
export function getStcdList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/noWqDtinType0Auth/GetKendoList',
url: '/eq/base/vmsstbprpt/GetKendoList',
method: 'post',
data: {
filter: data,
orders: JSON.stringify({ siteStepSort: 'asc' })
orders: { siteStepSort: 'asc' }
}
});
}
@ -78,7 +78,16 @@ export function getStcdList(data: any) {
// 沿程鱼类变化-基地模式详情表格接口
export function fishQgcDetail(data: any) {
return request({
url: '/api/wmp-env-server/env/we/fisht/qgcDetail/GetKendoListCust',
url: '/wte/we/fisht/qgcDetail/GetKendoListCust',
method: 'post',
data
});
}
// 水生生态调查-鱼类列表统计
export function getFishListStat(data: any) {
return request({
url: '/wte/we/fishList/stat/qgc/GetKendoListCust',
method: 'post',
data
});
@ -87,7 +96,7 @@ export function fishQgcDetail(data: any) {
// 陆生生态监测情况-获取统计数据
export function getEvnmAutoMonitor(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
url: '/wte/base/evnmAutoMonitor/GetKendoListCust',
method: 'post',
data
});
@ -96,7 +105,7 @@ export function getEvnmAutoMonitor(data: any) {
// 陆生生态监测-红外相机信息表格
export function getVmsstbprptList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
url: '/wte/vmsstbprpt/GetKendoList',
method: 'post',
data
});
@ -105,7 +114,7 @@ export function getVmsstbprptList(data: any) {
// 陆生生态监测-重点陆生动物表格
export function getTetSpecList(data: any) {
return request({
url: '/api/wmp-env-server/te/tet/spec/GetKendoListCust',
url: '/wte/te/tet/spec/GetKendoListCust',
method: 'post',
data
});
@ -114,7 +123,7 @@ export function getTetSpecList(data: any) {
// 野生动物监测-获取运行数据
export function getWvaData(data: any) {
return request({
url: '/api/wmp-env-server/env/wva/GetKendoListCust',
url: '/wte/wva/GetKendoListCust',
method: 'post',
data
});
@ -123,7 +132,7 @@ export function getWvaData(data: any) {
// 野生动物监测-获取默认数据
export function getWvaDftData(data: any) {
return request({
url: '/api/wmp-env-server/env/wva/default/GetKendoListCust',
url: '/wte/wva/default/GetKendoListCust',
method: 'post',
data
});
@ -132,7 +141,25 @@ export function getWvaDftData(data: any) {
// 野生动物监测-获取默认年份列表
export function getWvaDftTm(data: any) {
return request({
url: '/api/wmp-env-server/env/wva/year/GetKendoListCust',
url: '/wte/wva/year/GetKendoListCust',
method: 'post',
data
});
}
// 水生生态调查对比-获取断面和批次列表
export function getFishtKendoList(data: any) {
return request({
url: '/wte/we/fisht/GetKendoListCust',
method: 'post',
data
});
}
// 水生生态调查对比-获取对比柱状图数据
export function getCompareBar(data: any) {
return request({
url: '/wte/we/compareBar/GetKendoListCust',
method: 'post',
data
});

View File

@ -39,6 +39,13 @@ export function vmsstbprptGetKendoList(queryParams: any) {
data: queryParams
});
}
export function vmsstbprptGetKendoList1(queryParams: any) {
return request({
url: '/eq/vmsstbprpt/GetKendoList',
method: 'post',
data: queryParams
});
}
//生态流量达标情况
export function eqqecRateCount(queryParams: any) {
return request({

View File

@ -15,6 +15,13 @@ export function msstbprptGetKendoList(data: any) {
data
});
}
export function VDmsstbprptGetKendoList(data: any) {
return request({
url: '/vd/msstbprpt/GetKendoList',
method: 'post',
data
});
}
//获取鱼类介绍
export function fishDicGetKendoList(data: any) {
return request({

View File

@ -0,0 +1,91 @@
import request from '@/utils/request';
// AI识别大坝环境-获取统计数据Tab数量
export function getAisbdbyxList(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgcDmRun/GetKendoListCust',
method: 'post',
data
});
}
// AI识别大坝环境-获取饼图+表格数据
export function getAiChartTable(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/GetKendoListCust',
method: 'post',
data
});
}
// AI识别大坝环境-获取电站列表
export function getAiRstcd(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgc/aiRstcd/GetKendoListCust',
method: 'post',
data
});
}
// AI识别过鱼设施运行-获取统计数据
export function getAiGyssList(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgcProtect/GetKendoListCust',
method: 'post',
data
});
}
// 生态流量低于限值告警-获取统计数据
export function getStllGaojingList(data: any) {
return request({
url: '/api/wmp-env-server/env/warn/stcd/eng/GetKendoListCust',
method: 'post',
data
});
}
// 生态流量低于限值告警-获取表格详情数据
export function getStllGaojingDetail(data: any) {
return request({
url: '/api/wmp-env-server/env/warn/stcd/GetKendoListCust',
method: 'post',
data
});
}
// 水质监测数据告警-获取统计数据
export function getShuizhiGaojingList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
method: 'post',
data
});
}
// AI识别大坝环境-获取漂浮物/清漂日历数据
export function getAiPfwRecord(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgc/aiPfwRecord/GetKendoListCust',
method: 'post',
data
});
}
// AI识别大坝环境-获取叠梁门日历数据
export function getAiRecord(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgc/aiRecord/GetKendoListCust',
method: 'post',
data
});
}
// AI识别大坝环境-获取日历详情视频数据
export function getAiFile(data: any) {
return request({
url: '/api/wmp-env-server/env/ai/com/qgc/aiFile/GetKendoListCust',
method: 'post',
data
});
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg t="1686656066409" class="icon" viewBox="0 0 1125 1024" version="1.1" p-id="8027" width="200px" height="200" xmlns="http://www.w3.org/2000/svg">
<path d="M 30.838 832.939 C 50.02 828.843 69.932 838.571 77.414 856.645 L 81.464 865.553 C 105.989 914.859 157.741 946.705 215.059 946.757 L 225.073 946.449 C 281.324 942.712 330.824 908.254 351.919 857.26 L 354.337 852.497 C 363.718 835.933 385.453 828.642 404.401 835.704 L 409.969 838.418 C 418.957 843.814 425.415 852.065 428.082 861.56 L 432.582 870.418 C 457.895 916.651 507.621 946.757 563.027 946.757 C 622.204 946.705 675.642 912.401 698.593 859.717 L 701.518 855.212 L 704.387 851.115 C 708.712 844.518 715.206 839.324 722.949 836.267 L 728.181 834.732 C 748.135 830.289 768.547 840.308 775.431 857.925 L 779.763 867.294 C 794.916 897.625 821.636 921.927 854.971 935.697 C 929.953 966.11 1016.355 932.114 1047.911 859.717 L 1048.08 859.205 C 1057.136 839.544 1080.423 830.892 1100.393 839.391 L 1105.624 842.104 C 1122.559 852.451 1129.395 872.075 1122.105 889.413 C 1108.341 921.238 1086.593 949.694 1058.598 972.509 C 962.24 1050.589 818.52 1038.506 737.631 945.527 L 729.193 954.744 C 723.357 960.686 717.16 966.327 710.63 971.64 C 614.497 1050.334 470.439 1038.916 388.931 946.142 L 379.481 956.279 C 357.028 978.957 329.35 996.841 298.424 1008.657 C 181.14 1053.149 48.669 997.495 2.544 884.395 L 0.969 878.917 C -3.323 860.632 6.954 842.094 25.662 834.373 Z M 544.971 0.023 C 636.098 -1.411 711.868 63.459 718.675 146.709 L 719.125 156.029 L 719.125 331.388 C 728.125 338.043 736.562 345.365 744.268 353.352 L 752.594 362.568 C 843.888 463.944 832.582 613.139 729.08 701.612 L 717.886 710.675 C 605.682 797.944 436.511 783.877 343.536 679.546 C 251.848 577.863 262.986 428.053 366.713 338.914 L 376.725 330.722 L 376.725 156.029 C 378.356 70.525 452.663 1.559 544.916 0.023 Z M 546.21 64.022 C 494.965 64.893 452.663 101.603 447.545 148.144 L 447.038 156.029 L 447.038 360.212 L 422.401 379.361 C 338.587 444.59 327.561 560.967 397.876 638.944 C 463.053 712.486 580.631 725.591 663.379 668.537 L 673.561 661.011 C 754 598.752 767.219 488.928 705.399 411.669 L 696.061 400.763 C 691.954 395.945 687.44 391.426 682.561 387.246 L 675.08 381.256 L 648.81 362.056 L 648.81 157.104 C 647.854 107.184 606.06 67.299 554.421 64.227 L 546.21 64.022 Z M 547.784 192.022 L 553.072 192.38 C 570.284 194.735 583.447 208.252 583.503 224.534 L 583.503 436.552 C 613.879 446.177 637.561 467.989 647.854 495.687 L 650.386 503.264 C 664.467 552.032 635.116 602.481 582.884 619.283 L 574.334 621.536 C 494.627 638.949 424.1 571.296 447.385 499.76 C 456.868 470.627 480.812 447.228 512.064 436.552 L 512.064 224.584 C 512.096 206.632 528.06 192.078 547.784 192.022 Z" p-id="8028" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>guojiashuiwenzhan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="画板" transform="translate(-304.000000, -319.000000)">
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
<g id="ControlRates备份-2" transform="translate(121.000000, 157.000000)">
<g id="guojiashuiwenzhan" transform="translate(6.000000, 6.000000)">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<polygon id="路径" fill="#FFFFFF" fill-rule="nonzero" points="3 14 8 0 13 14"></polygon>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 931 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>guojiashuizhizhan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="画板" transform="translate(-304.000000, -250.000000)" fill="#FFFFFF" fill-rule="nonzero">
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
<g id="ControlRates备份-5" transform="translate(121.000000, 88.000000)">
<path d="M14.0127685,18.7975456 C15.186503,18.7996035 16.3093298,18.3553149 17.1170051,17.5692308 C17.9474718,16.7610359 18.4087696,15.6886498 18.405125,14.5747692 C18.405125,12.2387692 16.6849722,9.84000002 14.014102,6 C11.3352311,9.84 9.62310059,12.24 9.62310059,14.5747692 C9.62310059,15.6984616 10.087136,16.7766154 10.9111988,17.5692308 C11.7185556,18.3550038 12.8408306,18.7992648 14.014102,18.7975456 L14.0127685,18.7975456 Z M13.9807661,8.72123079 C14.3341264,9.24430771 14.5274744,9.85353848 14.5434757,10.48 C14.5434757,12.56 12.3286402,12.9107692 12.3286402,11.6307692 C12.3606426,10.8 13.4513924,9.49046156 13.9820995,8.71876924 L13.9820995,8.72123079 L13.9807661,8.72123079 Z M14.014102,21.9975385 C12.0619532,21.9975385 9.2844081,21.5175385 7.9949765,20.1095385 C8.5283505,19.8166154 9.09372692,19.5803077 9.68177174,19.4055385 C10.4084938,19.8855385 11.8952738,20.3975385 14.014102,20.3975385 C16.1315967,20.3975385 17.6183767,19.8855385 18.3784346,19.4055385 C18.9664794,19.5815385 19.5331893,19.8178462 20.0678967,20.112 C18.7464627,21.52 15.9689176,22 14.0167688,22 L14.014102,21.9975385 L14.014102,21.9975385 Z M19.9305529,13.136 C20.4612601,12.3027693 21.3853305,12.6867692 21.8520327,11.9187692 C22.4147423,11.0227692 21.2559873,10.0947693 20.3319169,10.8947692 C19.8332122,11.3501538 19.734538,12.2067692 19.9318864,13.1347692 L19.9305529,13.136 Z M7.66561805,10.8898462 C6.77221663,10.0578462 5.58279261,10.9858462 6.1455022,11.9138462 C6.64020657,12.6806154 7.53494144,12.2978462 8.06564855,13.1298462 C8.29366592,12.2067692 8.19499175,11.3489231 7.66561805,10.8935385 L7.66561805,10.8898462 Z" id="guojiashuizhizhan"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg t="1686656243919" class="icon" viewBox="0 0 1024 1024" version="1.1" p-id="8195" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<path d="M 1001.523 839.355 C 983.397 830.856 962.199 839.509 954.16 858.709 C 954.019 859.048 953.882 859.389 953.751 859.733 C 925.077 932.077 846.431 966.073 778.126 935.661 C 744.625 920.365 718.592 892.363 705.777 857.838 C 698.097 838.024 676.848 828.399 658.005 836.283 C 650.963 839.326 645.054 844.502 641.108 851.08 C 639.419 853.538 637.626 856.661 635.886 859.681 C 614.945 912.365 566.302 946.668 512.437 946.72 C 458.879 946.72 411.107 912.723 389.551 861.525 C 386.669 850.094 378.761 840.585 368.046 835.669 C 349.254 827.784 327.955 837.359 320.274 857.173 C 299.947 911.239 250.587 946.668 195.699 946.72 C 140.502 946.668 90.887 910.983 70.407 856.609 C 62.982 836.898 42.091 827.068 23.3 834.389 C 4.662 842.274 -4.657 864.443 2.307 884.41 C 44.293 997.509 164.824 1053.162 271.58 1008.67 C 303.882 995.076 332.235 973.558 354.016 946.105 C 428.209 1038.878 559.338 1050.295 646.792 971.602 C 655.699 963.6 663.92 954.865 671.369 945.491 C 744.999 1038.468 875.821 1050.551 963.53 972.473 C 988.773 950.048 1008.589 921.53 1021.338 889.377 C 1028.772 870.087 1020.151 848.322 1001.523 839.355" p-id="8196" fill="#ffffff"/>
<path d="M 1001.523 647.359 C 983.397 638.86 962.199 647.513 954.16 666.713 C 954.019 667.052 953.882 667.393 953.751 667.737 C 925.077 740.081 846.431 774.077 778.126 743.665 C 744.625 728.369 718.592 700.367 705.777 665.842 C 698.097 646.028 676.848 636.403 658.005 644.287 C 650.963 647.33 645.054 652.506 641.108 659.084 C 639.419 661.542 637.626 664.665 635.886 667.685 C 614.945 720.369 566.302 754.672 512.437 754.724 C 458.879 754.724 411.107 720.727 389.551 669.529 C 386.669 658.098 378.761 648.589 368.046 643.673 C 349.254 635.788 327.955 645.363 320.274 665.177 C 299.947 719.243 250.587 754.672 195.699 754.724 C 140.502 754.672 90.887 718.987 70.407 664.613 C 62.982 644.902 42.091 635.072 23.3 642.393 C 4.662 650.278 -4.657 672.447 2.307 692.414 C 44.293 805.513 164.824 861.166 271.58 816.674 C 303.882 803.08 332.235 781.562 354.016 754.109 C 428.209 846.882 559.338 858.299 646.792 779.606 C 655.699 771.604 663.92 762.869 671.369 753.495 C 744.999 846.472 875.821 858.555 963.53 780.477 C 988.773 758.052 1008.589 729.534 1021.338 697.381 C 1028.772 678.091 1020.151 656.326 1001.523 647.359" p-id="8197" fill="#ffffff"/>
<path d="M 486.067 0 C 493.031 0 499.892 2.918 504.705 8.089 L 523.343 28.723 L 544.95 54.168 L 557.853 69.989 L 579 97.124 L 593.849 117.246 L 609.056 138.749 L 624.314 161.379 C 632.25 173.513 640.033 186.108 647.56 198.959 L 659.644 182.831 L 710.334 116.99 L 761.024 182.78 C 776.283 202.492 790.722 222.715 804.342 243.758 C 847.608 310.829 871.418 369.349 871.418 418.347 C 871.264 508.559 799.529 582.593 710.232 582.593 C 689.034 582.593 668.143 578.343 648.789 570.203 L 646.792 572.353 C 630.3 589.037 611.36 603.108 590.623 614.08 C 563.154 628.721 532.895 637.376 501.838 639.475 L 486.169 639.987 C 424.662 639.992 365.829 614.832 323.347 570.356 C 279.817 525.2 255.64 464.834 255.964 402.116 C 255.964 331.052 291.959 242.837 362.874 139.773 L 370.555 128.714 L 385.864 107.569 L 400.662 87.96 L 414.691 70.04 L 433.636 46.745 L 449.304 28.313 L 467.43 8.192 C 471.017 4.302 475.722 1.619 480.896 0.512 L 486.067 0 Z M 486.374 83.454 L 482.79 87.806 C 459.046 116.192 436.629 145.663 415.613 176.124 C 351.353 269.511 319.967 346.463 319.967 402.168 C 319.967 449.117 337.53 492.892 369.326 525.813 C 400.815 558.324 442.034 575.988 486.169 575.988 C 512.59 575.988 537.424 569.844 560.823 557.454 C 573.112 551.003 584.684 542.811 595.232 533.186 C 565.653 502.252 549.162 461.093 549.2 418.295 C 549.2 373.65 570.193 318.969 607.469 258.145 L 597.945 241.045 C 566.996 187.231 531.229 136.336 491.085 88.984 L 486.374 83.454 Z M 710.386 221.845 L 696.407 240.481 C 688.88 250.824 680.637 262.599 672.393 275.246 C 634.247 333.766 613.204 384.658 613.204 418.295 C 613.204 454.237 631.943 487.619 662.204 505.436 C 676.694 514.089 693.386 518.594 710.232 518.594 C 763.738 518.594 807.312 473.59 807.363 418.244 C 807.363 384.453 787.701 336.07 750.529 278.471 L 741.927 265.364 L 733.376 253.025 L 717.452 231.112 L 710.386 221.845 Z" p-id="8198" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>qixiangzhan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="画板" transform="translate(-522.000000, -250.000000)">
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
<g id="ControlRates备份-7" transform="translate(339.000000, 88.000000)">
<g id="qixiangzhan" transform="translate(6.000000, 6.000000)">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<path d="M4.18299415,15.9992261 L4.18299412,15.9992261 C3.83124298,16.0006517 3.54495776,15.7117042 3.54355562,15.353845 C3.54355359,15.3533269 3.54355217,15.3528089 3.54355137,15.3522908 L3.54355136,15.3522926 C3.54410794,14.9944303 3.82971065,14.7047834 4.18146487,14.7053506 C4.18197411,14.7053514 4.18248336,14.7053529 4.1829926,14.7053549 L5.59022295,14.7053549 L5.59022295,12.9351129 L5.59022322,12.9351131 C4.92309208,12.6326872 4.31659847,12.2075125 3.80125929,11.6809803 C1.0497029,8.89944383 1.91133518,4.43178623 3.3446963,2.9846379 C4.47215455,1.84559646 6.52618578,2.47034106 8.41051975,3.86201396 L8.41051975,3.86201396 C8.48839992,3.9188462 8.52814235,4.0154854 8.5132465,4.11180806 L8.34609399,5.18994934 L8.21648072,5.2493013 L8.2164807,5.2493013 C8.12524474,5.29020431 8.01913074,5.2752241 7.94228952,5.2105935 C6.43563014,3.92033441 4.93530726,3.36190221 4.31286797,3.94949393 C3.55192865,4.66636217 4.38896191,6.82419779 6.18350306,8.76835318 C7.97805712,10.7125086 10.0478021,11.7073039 10.8087507,10.9899169 C11.3961948,10.4371704 11.0314528,9.02304391 10.007736,7.5129299 L10.1299935,7.28223175 L11.2843381,6.90547665 C12.4909335,8.8098996 12.9216314,10.905282 11.8438823,11.9945103 C10.9008261,12.9472373 8.90868096,13.7172702 6.87089489,13.3363793 L6.87089489,14.7061189 L9.30260071,14.7061189 L9.30260068,14.7061189 C9.65477108,14.7045498 9.94151088,14.9937268 9.94305431,15.3520141 C9.9430558,15.352361 9.94305702,15.352708 9.94305797,15.353055 L9.94305799,15.3530468 C9.94208524,15.7113356 9.65580525,16.000984 9.30363178,15.9999975 C9.30329072,15.9999975 9.30294967,15.9999975 9.30260861,15.9999975 L4.18299415,15.9992261 Z M7.50575723,7.98025697 L7.36219336,7.83523184 L7.36219336,7.83523184 C7.09216098,7.57607102 7.07976134,7.14327333 7.3344975,6.86855076 C7.35873317,6.8424136 7.38495609,6.8182604 7.41292255,6.79631553 L8.92795176,5.54347337 L8.92795181,5.54347332 C9.16901074,5.35273729 9.49064738,5.30436933 9.77563796,5.41599578 L9.77563797,5.4159958 C9.67430292,5.28150984 9.6197855,5.11656818 9.62065934,4.94711549 L9.62065934,4.94711537 C9.6137222,4.53702595 9.9339031,4.19836431 10.336956,4.18947567 L10.3369559,4.18947567 C10.7404296,4.19766401 11.0612277,4.53661816 11.0540143,4.94711537 L11.0540143,4.94711598 C11.0619159,5.35691948 10.7417826,5.69564674 10.3389754,5.70368542 C10.3383024,5.70369885 10.3376292,5.70371133 10.3369561,5.70372287 L10.3369561,5.70372287 C10.1781212,5.70409339 10.023995,5.64887913 9.90043162,5.54734298 L9.90043163,5.54734302 C10.0095216,5.8340025 9.96126958,6.15799768 9.77360851,6.39891408 L8.53353222,7.92941929 L8.53353223,7.92941928 C8.40386137,8.09512059 8.20753406,8.19233305 7.99935246,8.19392203 L7.9993525,8.19392203 C7.81323194,8.19271444 7.63535221,8.11566084 7.50550305,7.97999651 L7.50575723,7.98025697 Z M13.9666402,5.69907111 C13.9666402,3.1598353 12.0178815,1.09310774 9.62269583,1.09310774 L9.62269583,0 C12.5880746,0 15,2.55651622 15,5.69881643 L13.9666402,5.69907111 Z M12.1375932,5.6990789 L12.1375932,5.6990789 C12.141703,4.99638806 11.8773236,4.31956162 11.4002445,3.8114301 L11.4002446,3.81143013 C10.9389177,3.31261691 10.2959024,3.02930569 9.62269125,3.02824325 L9.62269125,1.93719946 C11.5813555,1.93719946 13.1737435,3.62485171 13.1737435,5.6990789 L12.1375932,5.6990789 Z" id="形状" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg t="1686655935321" class="icon" viewBox="0 0 1137 1024" version="1.1" p-id="7860" width="200px" height="200" xmlns="http://www.w3.org/2000/svg">
<path d="M 147.788 0 L 1095.449 263.002 L 1020.134 361.087 L 1137 392.397 L 1058.218 686.879 L 819.484 622.78 L 744.226 720.978 L 404.882 625.854 L 404.882 833.466 L 73.667 833.466 L 73.667 1024 L 0 1024 L 0 642.989 L 73.61 642.989 L 73.61 757.241 L 331.329 757.241 L 331.272 605.246 L 9.947 515.245 L 147.788 0 Z M 851.77 274.387 L 605.078 602.855 L 717.34 634.279 L 968.465 306.779 L 851.77 274.33 Z M 970 426.496 L 869.618 557.371 L 1006.72 594.145 L 1046.111 446.933 L 969.943 426.439 Z M 198.945 93.189 L 100.382 461.506 L 528.626 581.394 L 775.318 253.097 L 198.945 93.132 Z" p-id="7861" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 807 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686656318145" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8365" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M665.211904 0l0.512 307.8144c0 33.5872 7.8848 67.1744 22.2208 97.28l324.5056 460.6976 0.7168 2.048c16.4352 34.304 14.336 73.5744-6.4512 105.728a114.3296 114.3296 0 0 1-95.0784 50.432H112.507904c-38.6048 0-74.3424-18.944-95.0784-51.0976a107.4176 107.4176 0 0 1-6.4512-105.728l1.4336-2.816 307.3536-458.5472c14.336-30.8224 22.1696-64.4096 22.1696-97.9968L341.423104 0h323.7888z m-63.8464 64H405.525504l0.4096 243.8144c0 36.5568-7.168 73.216-20.7872 107.8272l-7.3216 17.1008-4.864 8.704L68.168704 896l-1.9968 5.12a42.8032 42.8032 0 0 0 1.8432 31.2832l3.2256 5.8368a47.7184 47.7184 0 0 0 33.9968 21.248l7.2704 0.512h799.1296a49.664 49.664 0 0 0 41.2672-21.0432 43.0592 43.0592 0 0 0 5.0688-37.12l-2.0992-5.2736-320.256-454.6048-5.5296-9.3696a293.376 293.376 0 0 1-27.8528-108.6464l-0.512-16.0256-0.3584-243.9168z" p-id="8366" fill="#ffffff"></path><path d="M256.072704 0h512v64H256.072704z" p-id="8367" fill="#ffffff"></path><path d="M365.231104 686.4896a83.968 83.968 0 0 1 83.6096 83.6096c0 43.008-33.1776 78.848-75.1104 83.0976l-8.4992 0.4608A83.968 83.968 0 0 1 281.672704 770.0992c0-43.008 33.1264-78.848 75.0592-83.1488l8.4992-0.4608z m245.0944-18.8416c51.5072 0 92.9792 41.472 92.9792 93.0304 0 48.4864-36.7104 88.064-83.968 92.5696l-9.0112 0.4096a92.7744 92.7744 0 0 1-93.0304-92.9792c0-48.5376 36.7616-88.064 84.0192-92.6208l9.0112-0.4096z m-245.0944 82.8416a19.968 19.968 0 0 0-19.5584 19.6096 19.968 19.968 0 0 0 19.5584 19.5584 19.968 19.968 0 0 0 19.6096-19.5584 19.968 19.968 0 0 0-19.6096-19.6096z m245.0944-18.8416a28.7744 28.7744 0 0 0-29.0304 29.0304c0 16.1792 12.8 28.9792 29.0304 28.9792 16.1792 0 28.9792-12.8 28.9792-28.9792a28.7744 28.7744 0 0 0-28.9792-29.0304zM502.242304 421.9392a116.3776 116.3776 0 0 1 116.224 116.224c0 60.928-47.616 111.0528-106.8032 115.9168l-9.4208 0.3584a116.3776 116.3776 0 0 1-116.224-116.224c0-60.928 47.0528-111.0016 106.7008-115.8656l9.5232-0.4096z m0 64c-28.7744 0-52.224 23.552-52.224 52.224 0 28.7744 23.4496 52.2752 52.224 52.2752 28.3648 0 52.224-23.7568 52.224-52.224 0-28.7744-23.5008-52.224-52.224-52.224z" p-id="8368" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>zengzhizhanshuizhizhan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="zengzhizhanshuizhizhan" transform="translate(0.750000, 0.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M23.25,0 C36.09,0 46.5,10.74525 46.5,24 C46.5,37.25475 36.09,48 23.25,48 C10.41,48 0,37.25475 0,24 C0,10.74525 10.41,0 23.25,0 Z M23.24475,6.6675 C13.74375,6.6675 6.04275,14.49225 6.04275,24.144 C6.04275,32.4705 11.775,39.432 19.44825,41.18925 L27.0405,41.18925 C34.71375,39.432 40.4475,32.4705 40.4475,24.144 C40.4475,14.4915 32.7465,6.6675 23.2455,6.6675 L23.24475,6.6675 Z M23.44275,8.361 C31.84275,8.361 38.65275,15.02025 38.65275,23.235 C38.65275,23.3325 38.64675,23.42775 38.64525,23.52525 L38.598,23.52525 C38.595,23.472 38.586,23.42025 38.5815,23.36775 C38.34075,27.62025 35.07225,30.98925 31.074,30.98925 C30.1862354,30.9895698 29.3064559,30.821605 28.48125,30.49425 L28.34475,30.5955 C26.9708322,29.6968211 25.3634685,29.2209226 23.72175,29.22675 C22.2366664,29.2236863 20.7773263,29.6144655 19.4925,30.35925 C18.35025,31.15725 16.99425,31.62225 15.53925,31.62225 C11.45925,31.62225 8.15175,27.98325 8.15175,23.49675 C8.15175,22.94175 8.202,22.40025 8.29875,21.87675 C9.00075,14.2995 15.5115,8.361 23.44275,8.361 L23.44275,8.361 Z M23.24775,11.0925 L23.24325,11.24475 L23.2395,11.0925 C23.238,11.08275 23.17875,11.18325 23.0745,11.37 L22.98825,11.52525 C22.6125,12.20775 21.86475,13.60275 21.10275,15.03075 L20.88675,15.43575 C20.2208976,16.6865315 19.5563972,17.9380322 18.89325,19.19025 C18.2049855,20.1371924 17.8354525,21.2783585 17.8379869,22.449 C17.8379869,25.482 20.25825,27.9405 23.24475,27.9405 C26.22975,27.9405 28.6500122,25.482 28.6500122,22.449 C28.6524544,21.2784908 28.2832171,20.1374264 27.5955,19.19025 C26.9682775,18.0058222 26.3397771,16.8220715 25.71,15.639 L25.278,14.829 C24.23775,12.879 23.25525,11.052 23.24775,11.0925 L23.24775,11.0925 Z M27.3,22.434 C27.30225,22.48275 27.306,22.5315 27.306,22.581 C27.306,24.768 25.764,26.54175 23.86125,26.54175 C22.623,26.54175 21.53775,25.791 20.93175,24.66225 C21.61425,25.38675 22.545,25.83375 23.57175,25.83375 C25.43475,25.83375 26.982,24.36675 27.3,22.434 L27.3,22.434 Z" id="形状"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>zijianshuiwenzhan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="画板" transform="translate(-413.000000, -319.000000)">
<g id="BGSM备份-11" transform="translate(177.000000, 156.000000)">
<g id="ControlRates备份-3" transform="translate(230.000000, 157.000000)">
<g id="zijianshuiwenzhan" transform="translate(6.000000, 6.000000)">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<path d="M9.97229576,12.7591246 C11.0763098,12.7591246 11.6579969,12.9166253 12.1726968,13.0531252 C12.6534766,13.1808752 13.0664219,13.2928757 13.9584577,13.2928757 L13.9584577,15 C12.8578363,15 12.2727566,14.8460002 11.7589154,14.7068753 C11.2781355,14.5791253 10.8643429,14.4671249 9.97230984,14.4671249 C9.08028104,14.4671249 8.66648695,14.5791249 8.1857043,14.7068753 C7.67185461,14.8425003 7.0876266,15 5.98616196,15 C4.88469309,15 4.30046086,14.8460002 3.78576092,14.7068753 C3.30498108,14.5791253 2.89118839,14.4671249 2,14.4671249 L2,12.7582428 C3.10062143,12.7582428 3.68485647,12.9131172 4.19954234,13.0522435 C4.68032218,13.1799934 5.09411486,13.2919939 5.98614788,13.2919939 C6.87817668,13.2919939 7.29197077,13.1799939 7.77275342,13.0522435 C8.28660311,12.9157434 8.87083112,12.7582428 9.97229576,12.7582428 L9.97229576,12.7591246 Z M9.99773387,10.0650032 C11.1000516,10.0650032 11.683435,10.2225068 12.1981349,10.3581308 C12.6789148,10.4867559 13.0927074,10.5987558 13.9847405,10.5987558 L13.9847405,12.3058801 C12.8832716,12.3058801 12.2990394,12.1510058 11.7843394,12.0127554 C11.3035596,11.8841304 10.8897669,11.7721305 9.9985785,11.7721305 C9.1065497,11.7721305 8.69275561,11.8841305 8.21197296,12.0127554 C7.69727583,12.1483804 7.11304782,12.3058801 6.01157192,12.3058801 C4.91095049,12.3058801 4.32671544,12.1510058 3.81202958,12.0127554 C3.33124974,11.8841304 2.91745705,11.7721305 2.02542403,11.7721305 L2.02542403,10.0658777 C3.1268929,10.0658777 3.71112513,10.2198776 4.22582508,10.3581279 C4.70660491,10.4867529 5.1203976,10.5987528 6.01158599,10.5987528 C6.90361479,10.5987528 7.31740888,10.4867528 7.79819153,10.3581279 C8.31288866,10.2225029 8.89711667,10.0650032 9.99859257,10.0650032 L9.99773387,10.0650032 Z M14,1 L14,8.84875684 L12.3456767,8.84875684 L12.3456767,2.70801044 L3.66450187,2.70801044 L3.66450187,8.84875684 L2.01017857,8.84875684 L2.01017857,1 L14,1 Z M8.88439241,4.12375034 L8.88439241,8.84875684 L7.22922448,8.84875684 L7.22922448,4.12375034 L8.88439241,4.12375034 Z" id="形状" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -3288,6 +3288,45 @@ const DwSixPointColumns: Array<any> = [
url: ''
}
];
//DW
const DwPointColumnsyuan: Array<any> = [
{
name: '基本属性',
visible: false,
ruleTips: '',
type: '',
url: ''
},
{
name: '设施名称',
filed: 'stnm',
visible: true,
type: 'input',
url: ''
},
{
name: '站址',
filed: 'stlc',
visible: true,
type: 'select',
url: ''
},
{
name: '所属流域',
filed: 'hbrvcdName',
visible: true,
type: 'select',
url: ''
},
{
name: '所属电站',
filed: 'ennm',
visible: true,
type: 'select',
url: ''
},
];
//夹岩双层取水
const DwOnePointColumns: Array<any> = [
{
@ -4145,8 +4184,7 @@ const EQPointColumns7: Array<any> = [
visible: true,
type: 'select',
url: ''
},
}
];
//阶段属性
const basicFilterColumns: Array<any> = [
@ -4831,6 +4869,58 @@ const tableColumns5: Array<any> = [
setting: {}
}
];
//气象监测
const MMColumns: Array<any> = [
{
name: '基本属性',
visible: false,
ruleTips: '',
type: '',
url: ''
}, {
name: '测站名称',
filed: 'stnm',
visible: true,
type: 'input',
url: ''
},
{
name: '测站类型',
filed: 'sttpName',
visible: true,
type: 'select',
url: ''
},
{
name: '站址',
filed: 'addvcdName',
visible: true,
type: 'input',
url: ''
},
{
name: '所属流域',
filed: 'hbrvcdName',
visible: true,
type: 'select',
url: ''
},
{
name: '所在水系',
filed: 'addvcdName',
visible: true,
type: 'input',
url: ''
},
{
name: '建成日期',
filed: 'jcdt',
visible: true,
type: 'date',
format: 'YYYY-MM-DD',
url: ''
},
];
//生态流量
const EcologicalFlowColumns: Array<any> = [];
@ -4873,6 +4963,7 @@ export {
DwFivePointColumns,
DwSixPointColumns,
DwOnePointColumns,
DwPointColumnsyuan,
EQPointColumns2,
EQPointColumns5,
EQPointColumns3,
@ -4886,5 +4977,6 @@ export {
tableColumns2,
tableColumns3,
tableColumns4,
tableColumns5
tableColumns5,
MMColumns
};

View File

@ -142,6 +142,8 @@ import {
DwFivePointColumns,
DwSixPointColumns,
DwOnePointColumns,
DwPointColumnsyuan,
MMColumns,
StinfoAiVidoPointColumns
} from '../column.config';
import { useModelStore } from '@/store/modules/model';
@ -318,6 +320,14 @@ const columnsConfig = ref([
{
type: 'ZQ',
columns: FhZQPointColumns
},
{
type: 'DW',
columns: DwPointColumnsyuan
},
{
type:'MM',
columns:MMColumns
}
//DW_1

View File

@ -199,14 +199,14 @@ const ENGEQTabs: Array<any> = [
].filter(Boolean);
// // 水电站生态流量 - 江局单独看 √
// const ENGEQTabsJuangJu: Array<any> = [
// {
// name: '生态流量',
// key: 'EcologicalFlow',
// type: 'EcologicalFlow'
// },
// ].filter(Boolean)
const ENGEQTabsJuangJu: Array<any> = [
{
name: '生态流量',
key: 'EcologicalFlow',
type: 'EcologicalFlow',
default: true
}
].filter(Boolean);
// //生态流量泄放设施 √
const EQTabs: Array<any> = [
{
@ -310,9 +310,8 @@ const FPTabs1: Array<any> = [
name: '基础信息',
key: 'basicInfo',
type: 'basic',
default: true, // 默认显示
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true, // 默认显示
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
},
{
name: '实时视频',
@ -956,15 +955,23 @@ const CommonTabs: any = [
}
];
// // 气象站
// const WeatherTabs: any = [
// {
// name: '基础信息',
// key: 'basicInfo',
// type: 'basic',
// url: '/bbi/siteBipc/getSiteBasicInfo'
// }
// ]
// 气象站
const WeatherTabs: any = [
{
name: '基础信息',
key: 'basicInfo',
type: 'basic',
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true // 默认显示
},
{
name: '监测数据',
key: '',
type: '',
url: ''
},
]
// const WaterQualityMonitoring: any = [
// {
@ -1033,6 +1040,8 @@ const handleTabs = (modaldata: any) => {
const sttp = modaldata?.sttp ? modaldata?.sttp.toUpperCase() : '';
console.log('sttp', sttp);
switch (sttp) {
case 'ENGJJ':
return ENGEQTabsJuangJu;
case 'ENG':
if (modaldata?.eqtp == 'QEC') {
return ENGEQTabs;
@ -1223,8 +1232,8 @@ const handleTabs = (modaldata: any) => {
// }
// case 'WE':
// return WETabs
// case 'MM':
// return WeatherTabs
case 'MM':
return WeatherTabs
// case 'WARN':
// return WaterQualityMonitoring
// case 'AI':

View File

@ -47,7 +47,6 @@
</span>
</div>
<div class="title_right">
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
<img v-else src="@/assets/components/arrow-down.png" alt="" />
@ -78,7 +77,7 @@
>
</a-tree-select>
</div>
<div v-if="select.show">
<div v-if="select.show">
<a-select
v-model:value="selectValue"
show-search
@ -88,14 +87,14 @@
:dropdown-style="{
maxHeight: '400px',
overflow: 'auto',
minWidth: '200px'
minWidth: select.minWidth ? select.minWidth : '200px'
}"
:options="select.options"
:filter-option="filterOption"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
@select="handleTreeSelect1"
@select="handleTreeSelect1"
></a-select>
</div>
<div v-if="datetimePicker.show">
@ -190,6 +189,7 @@ interface SelectConfig {
value?: string | undefined;
options?: SelectProps['options'];
width?: string;
minWidth?: string;
}
// 便
@ -248,7 +248,8 @@ const props = defineProps({
show: false,
value: undefined,
options: [],
width: ''
width: '',
minWidth: ''
})
},
moreSelect: {
@ -690,7 +691,6 @@ const handleTreeExpand = (keys: string[]) => {
//
const handleChange = (value: string) => {
// Handle change
};

View File

@ -1,49 +1,29 @@
<!-- SidePanelItem.vue -->
<!-- developStatusChart/index.vue -->
<template>
<div class="basic_body">
<div ref="chartContainer" class="chart-container"></div>
<!-- 水电开发情况弹框 -->
<a-modal
v-model:open="modalVisible"
title="水电开发情况"
:width="1540"
@ok="handleModalOk"
@cancel="handleModalCancel"
>
<div class="modal-content">
<!-- 内容区域供用户自定义 -->
<!-- <p>选中类型{{ selectedType }}</p> -->
<SDKFQKPie :defaultTab="selectedType" />
<!-- 用户可在此处自定义内容 -->
</div>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ref, onMounted, onUnmounted, watch } from 'vue';
import * as echarts from 'echarts';
import { message } from 'ant-design-vue';
import SDKFQKPie from '@/modules/shuidianhuangjingjieruMod/TwoLayer/ShuiDianKaiFQKTwoLayer.vue'
// 便
import { getVmsstbprptKendoList } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
defineOptions({
name: 'developStatusChart'
});
const emit = defineEmits<{
(e: 'chart-click', params: any): void;
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const chartContainer = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
//
const modalVisible = ref(false);
const selectedType = ref('');
//
onMounted(() => {
if (chartContainer.value) {
initChart();
}
});
const dataLoading = ref(false);
onUnmounted(() => {
if (chartInstance) {
@ -52,98 +32,238 @@ onUnmounted(() => {
}
});
const transUnit = (value: number) => {
return value * 0.1;
};
const getData = async () => {
if (!baseid.value) return;
const baseFilters: any[] = [
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
];
if (baseid.value !== 'all') {
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
}
const params1 = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'ttpwr', operator: 'isnotnull' }
]
},
group: [{ dir: 'desc', field: 'bldsttCcode', aggregates: [{ field: 'ttpwr', aggregate: 'sum' }] }]
};
const params2 = {
filter: {
logic: 'and',
filters: [...baseFilters]
},
group: [{ dir: 'desc', field: 'bldsttCcode' }]
};
try {
dataLoading.value = true;
const [res1, res2] = await Promise.all([
getVmsstbprptKendoList(params1),
getVmsstbprptKendoList(params2)
]);
const res1Data = res1?.data?.data || [];
const res2Data = res2?.data?.data || [];
// debugger
if (res1Data.length > 0 || res2Data.length > 0) {
const ttpwrlist = res1Data.map((item: any) => ({
key: item.key,
ttpwr: transUnit(item.items[0].SUM_TTPWR)
}));
const engTotalList = res2Data.map((item: any) => ({
key: item.key,
engTotal: item.count
}));
const result: any = Object.values(
[...engTotalList, ...ttpwrlist].reduce((result: any, item: any) => {
const { key, engTotal = 0, ttpwr = 0 } = item;
const existingItem = result[key] || { key, engTotal: 0, ttpwr: 0 };
existingItem.bldstt = item.key == 0 ? '未建' : item.key == 1 ? '在建' : '已建';
existingItem.engTotal += engTotal;
existingItem.ttpwr += ttpwr;
return { ...result, [key]: existingItem };
}, {})
);
result.sort((a: any, b: any) => {
if (a.bldstt === '已建' && b.bldstt !== '已建') return -1;
else if (a.bldstt !== '已建' && b.bldstt === '已建') return 1;
else if (a.bldstt === '在建' && b.bldstt === '未建') return -1;
else if (a.bldstt === '未建' && b.bldstt === '在建') return 1;
else return 0;
});
const legendData: string[] = [];
const capData: { name: string; value: number }[] = [];
const countData: { name: string; value: number }[] = [];
result.forEach((item: any) => {
const name = item.bldstt;
if (name !== '未建') {
legendData.push(name);
countData.push({ name, value: Number(item.engTotal) });
capData.push({ name, value: Number(item.ttpwr).toFixed(2) });
}
});
const colors = ['#78c300', '#2196F3'];
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}电站{a}: {c}',
position: 'right'
},
legend: {
data: legendData,
left: '60%',
orient: 'vertical',
top: 'center',
itemWidth: 20,
itemHeight: 12
},
color: colors,
series: [
{
name: '数量(座)',
type: 'pie',
radius: [0, '55%'],
label: { show: false },
labelLine: { show: false },
data: countData,
center: ['30%', '50%'],
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
}
},
{
name: '装机容量(万kW)',
type: 'pie',
radius: ['70%', '90%'],
labelLine: { length: 30 },
label: { show: false },
data: capData,
center: ['30%', '50%'],
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
}
}
]
};
if (chartInstance) {
chartInstance.setOption(option, true);
}
}
} catch (error) {
console.error('DevelopStatusChart getData error:', error);
} finally {
dataLoading.value = false;
}
};
const initChart = () => {
if (!chartContainer.value) return;
chartInstance = echarts.init(chartContainer.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} {b}: {c} ({d}%)',
position: 'right'
},
legend: {
data: ['已建', '在建'],
left: '60%',
orient: 'vertical',
top: 'center',
itemWidth: 20,
itemHeight: 12
tooltip: {
trigger: 'item',
formatter: '{a} {b}: {c} ({d}%)',
position: 'right'
},
legend: {
data: ['已建', '在建'],
left: '60%',
orient: 'vertical',
top: 'center',
itemWidth: 20,
itemHeight: 12
},
color: ['#78c300', '#2196F3'],
series: [
{
name: '装机容量',
name: '装机容量(万kW)',
type: 'pie',
radius: ['70%', '90%'], //
center: ['30%', '50%'], //
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false //
radius: ['70%', '90%'],
center: ['30%', '50%'],
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
labelLine: {
show: false // 线
},
data: [
{ value: 80, name: '已建', itemStyle: { color: '#78c300' } },
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
]
label: { show: false },
labelLine: { show: false },
data: []
},
{
name: '数量(座)',
type: 'pie',
radius: ['0%', '50%'], //
center: ['30%', '50%'], //
radius: ['0%', '50%'],
center: ['30%', '50%'],
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
labelLine: {
show: false
},
data: [
{ value: 80, name: '已建', itemStyle: { color: '#78c300' } },
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
]
label: { show: false },
labelLine: { show: false },
data: []
}
]
};
chartInstance.setOption(option);
//
//
chartInstance.on('click', (params: any) => {
emit('chart-click', {
seriesName: params.seriesName,
name: params.name,
dataIndex: params.dataIndex
});
});
window.addEventListener('resize', () => {
chartInstance?.resize();
});
};
//
chartInstance.on('click', (params: any) => {
if (params.data && params.data.name) {
selectedType.value = params.data.name;
modalVisible.value = true;
onMounted(() => {
if (chartContainer.value) {
initChart();
}
});
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getData();
}
});
};
//
const handleModalOk = () => {
modalVisible.value = false;
};
//
const handleModalCancel = () => {
modalVisible.value = false;
};
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
@ -152,13 +272,8 @@ const handleModalCancel = () => {
height: 100%;
.chart-container {
width: 203px; //
height: 100px; //
}
.modal-content {
padding: 10px 0;
min-height: 100px;
width: 203px;
height: 100px;
}
}
</style>

View File

@ -1,9 +1,6 @@
<template>
<div>
<!-- <div v-if="dataLoading">
<EmptyPanel :loading="dataLoading" :data="data" />
</div> -->
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center">
<div style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center">
<span style="font-size: 14px; line-height: 22px; font-weight: 400; color: #000; margin-bottom: 4px">
监测数据接入情况
</span>
@ -11,7 +8,7 @@
<div style="display: flex; flex-direction: column">
<div
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; cursor: pointer"
@click="handleItemClick('notAccess')"
>
<div>
<span style="border-right: 2px solid #00A14D; margin-right: 6px"></span>
@ -21,6 +18,7 @@
</div>
<div
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; margin: 3px 0; cursor: pointer"
@click="handleItemClick('partAccess')"
>
<div>
<span style="border-right: 2px solid #4978AD; margin-right: 6px"></span>
@ -30,6 +28,7 @@
</div>
<div
style="width: 190px; height: 36px; border: 1px solid #EDF2F8; padding: 4px; display: flex; justify-content: space-between; cursor: pointer"
@click="handleItemClick('isAccess')"
>
<div>
<span style="border-right: 2px solid #606060; margin-right: 6px"></span>
@ -39,16 +38,115 @@
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ref, watch } from 'vue';
import { getVmsstbprptKendoList } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
defineOptions({
name: 'EngEnvironmentData'
});
//
const emit = defineEmits<{
(e: 'item-click', params: any): void;
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
const data = ref<Record<string, number>>({ one: 0, two: 0, three: 0 });
const getData = async () => {
if (!baseid.value) return;
const baseFilters: any[] = [
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
{ field: 'bldsttCcode', operator: 'in', dataType: 'string', value: ['1', '2'] }
];
if (baseid.value !== 'all') {
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
}
const params1 = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
]
}
};
const params2 = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'runState', operator: 'eq', dataType: 'number', value: 4 },
{ field: 'dtin', operator: 'eq', dataType: 'number', value: 1 }
]
}
};
const params3 = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] },
{ field: 'dtinEnv', operator: 'in', dataType: 'string', value: ['2'] }
]
}
};
try {
dataLoading.value = true;
const [res1, res2, res3] = await Promise.all([
getVmsstbprptKendoList(params1),
getVmsstbprptKendoList(params2),
getVmsstbprptKendoList(params3)
]);
const record = { one: 0, two: 0, three: 0 };
const responses = [res1, res2, res3];
responses.forEach((res, index) => {
const total = res?.data?.total ?? 0;
switch (index) {
case 0:
record.one = total;
break;
case 1:
record.two = total;
break;
case 2:
record.three = total;
break;
}
});
data.value = record;
// debugger
} catch (error) {
console.error('EngEnvironmentData getData error:', error);
} finally {
dataLoading.value = false;
}
};
const handleItemClick = (name: string) => {
emit('item-click', { name });
};
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getData();
}
},
{ deep: true, immediate: true }
);
</script>

View File

@ -3,15 +3,22 @@
<div class="yancheng-yulei">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form">
<!-- 基地名称非沿程模式显示 -->
<a-form-item v-if="!props.ly" label="基地名称">
<!-- 基地名称 -->
<a-form-item label="基地名称">
<a-select
v-model:value="formState.baseId"
placeholder="请选择"
style="width: 150px"
:options="baseOptions"
@change="handleBaseChange"
/>
>
<a-select-option
v-for="opt in baseOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 所在流域 -->
@ -20,21 +27,35 @@
v-model:value="formState.rvcd"
placeholder="请选择"
style="width: 150px"
:options="lyOptions"
@change="handleRvcdChange"
/>
>
<a-select-option
v-for="opt in lyOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 断面名称沿程模式显示 -->
<a-form-item v-if="props.ly" label="断面名称">
<!-- <a-form-item v-if="props.ly" label="断面名称">
<a-select
v-model:value="formState.stcd"
placeholder="请选择"
style="width: 150px"
:options="dmOptions"
@change="handleStcdChange"
/>
</a-form-item>
>
<a-select-option
v-for="opt in dmOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item> -->
<!-- 年份 -->
<a-form-item label="年份">
@ -57,11 +78,20 @@
</a-form>
<!-- Tab 切换 + 表格 -->
<a-tabs v-model:activeKey="tabIndex" class="ylycbh-tabs" @change="handleTabChange">
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.labelWithTooltip">
<a-tabs
v-model:activeKey="tabIndex"
class="ylycbh-tabs"
@change="handleTabChange"
>
<a-tab-pane
v-for="item in tabList"
:key="item.key"
:tab="item.labelWithTooltip"
>
<div class="table-wrapper">
<BasicTable
v-if="tabIndex === item.key"
<!-- 沿程模式表格 -->
<!-- <BasicTable
v-if="props.ly && tabIndex === item.key"
:ref="el => setTableRef(el, item.key)"
:columns="tableColumns"
:scroll-y="500"
@ -70,34 +100,46 @@
:search-params="searchParams"
:transform-data="customTransform"
>
<!-- 沿程模式断面名称列带电站名称分组 -->
<template v-if="props.ly" #rsctdName="{ record }">
<template #rsctdName="{ record }">
{{ record.rsctdName || '-' }}
</template>
<template v-if="props.ly" #stcdList="{ record }">
<template #stcdList="{ record }">
<div v-for="st in record.stcdList" :key="st.stcd">
<a @click="handleViewDetail(st.stcd, st.stnm)" class="text-link">
<a
@click="handleViewDetail(st.stcd, st.stnm)"
class="text-link"
>
{{ st.stnm }}
</a>
</div>
</template>
<template v-if="props.ly" #fishList="{ record }">
<template #fishList="{ record }">
<a-list size="small" :data-source="record.fishList">
<template #renderItem="{ item }">
<a-list-item>{{ item }}</a-list-item>
<template #renderItem="{ item: fish }">
<a-list-item>{{ fish }}</a-list-item>
</template>
</a-list>
</template>
</BasicTable> -->
<!-- 基地模式调查断面列 -->
<template v-else #stnm="{ record }">
<a @click="handleViewDetail(record.stcd, record.stnm)" class="text-link">
<!-- 基地模式表格 -->
<BasicTable
:ref="el => setTableRef(el, item.key)"
:columns="tableColumns"
:scroll-y="500"
:scroll-x="800"
:list-url="listUrl"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #stnm="{ record }">
<a
@click="handleViewDetail(record.stcd, record.stnm)"
class="text-link"
>
{{ record.stnm }}
</a>
</template>
<template v-else #name="{ record }">
{{ record.name || '-' }}
</template>
</BasicTable>
</div>
</a-tab-pane>
@ -123,21 +165,33 @@ const modelStore = useModelStore();
// ==================== Props ====================
const props = defineProps<{
tm: string; // "2024"
tm: string; // "2024"
dataDimensionVal: string; // ID
hbrvcd: string; //
jdList: any[]; //
stateName: string; // ""
sectionName: string; //
stcd: string; //
ly: boolean; // 沿
hbrvcd: string; //
jdList: any[]; //
stateName: string; // ""
sectionName: string; //
stcd: string; //
ly: boolean; // 沿
}>();
// ==================== Tab ====================
const TAB_CONFIG = [
{ key: '1', label: '外来鱼类', prompt: '通过人为行为引入的本流域原生态没有的鱼类' },
{ key: '2', label: '本土受威胁鱼类', prompt: '流域原生态鱼类中处于近危或者濒危的鱼类' },
{ key: '3', label: '本土无危鱼类', prompt: '分类属于本土且保护类型属于无危的鱼类' }
{
key: '1',
label: '外来鱼类',
prompt: '通过人为行为引入的本流域原生态没有的鱼类'
},
{
key: '2',
label: '本土受威胁鱼类',
prompt: '流域原生态鱼类中处于近危或者濒危的鱼类'
},
{
key: '3',
label: '本土无危鱼类',
prompt: '分类属于本土且保护类型属于无危的鱼类'
}
];
// ==================== ====================
@ -214,7 +268,7 @@ const lyColumns = [
title: '电站名称',
dataIndex: 'rsctdName',
width: 200,
ellipsis: true,
// ellipsis: true,
slots: { customRender: 'rsctdName' }
},
{
@ -222,7 +276,7 @@ const lyColumns = [
title: '调查断面',
dataIndex: 'stcdList',
width: 200,
ellipsis: true,
// ellipsis: true,
slots: { customRender: 'stcdList' }
},
{
@ -230,7 +284,7 @@ const lyColumns = [
title: '渔获物种类',
dataIndex: 'fishList',
width: 300,
ellipsis: true,
// ellipsis: true,
slots: { customRender: 'fishList' }
}
];
@ -259,10 +313,10 @@ const baseColumns = [
];
/** 根据 ly 模式切换表格列 */
const tableColumns = computed(() => (props.ly ? lyColumns : baseColumns));
const tableColumns = computed(() => (props.ly ? baseColumns : lyColumns));
/** 根据 ly 模式切换表格 API */
const listUrl = computed(() => (props.ly ? fishDetail : fishQgcDetail));
const listUrl = computed(() => (props.ly ?fishQgcDetail : fishDetail));
/** 表格搜索参数 */
const searchParams = computed(() => ({
@ -291,22 +345,41 @@ const fetchLyList = async () => {
filter: {
logic: 'and',
filters: [
{ field: 'wbsType', operator: 'eq', value: 'PSB_RVCD' },
formState.value.baseId != 'all'
? {
field: 'fullPath',
operator: 'startswith',
dataType: 'string',
value: " formState.value.baseId+','"
}
: null,
{
field: 'fullPath',
operator: 'contains',
field: 'wbsType',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId + ','
value: 'PSB_RVCD'
}
]
].filter(Boolean)
},
group: [
{ dir: 'asc', field: 'objId' },
{ dir: 'asc', field: 'orderIndex' },
{ dir: 'des', field: 'wbsCode' },
{ dir: 'des', field: 'wbsName' }
{
dir: 'asc',
field: 'objId'
},
{
dir: 'asc',
field: 'orderIndex'
},
{
dir: 'des',
field: 'wbsCode'
},
{
dir: 'des',
field: 'wbsName'
}
],
groupResultFlat: true
groupResultFlat: 'true'
};
const res = await wbsbGetKendoList(params);
@ -375,27 +448,45 @@ const fetchDmList = async () => {
// ==================== Tab ====================
const getTabNumData = async () => {
const params: any = {
baseId: formState.value.baseId,
rvcd: formState.value.rvcd,
yr: dayjs(yearValue.value).format('YYYY'),
ty: tabIndex.value
let params = {
filter: {
logic: 'and',
filters: [
formState.value.baseId != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId
}
: null,
{
field: 'rvcd',
operator: 'eq',
dataType: 'string',
value: formState.value.rvcd
},
{
field: 'yr',
operator: 'eq',
dataType: 'string',
value: dayjs(yearValue.value).format('YYYY')
},
{
field: 'ty',
operator: 'isnotnull',
dataType: 'string',
value: tabIndex.value
}
].filter(Boolean)
}
};
// 沿 stcd
if (props.ly) {
params.stcd = formState.value.stcd;
}
//
if (!params.stcd) delete params.stcd;
if (params.baseId === 'all') delete params.baseId;
try {
const res = await getTabNum(params);
// getTabNum res.data [{ty, num}, ...]
const data = res?.data || [];
const data = res?.data?.data || [];
// debugger
const list = TAB_CONFIG.map(item => ({
...item,
labelWithTooltip: renderTabLabel(item.label, item.prompt)
@ -427,12 +518,14 @@ const getTabNumData = async () => {
/** 构建表格过滤条件 */
const getFilter = () => {
const filters: any[] = [
{
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId
},
formState.value.baseId != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId
}
: null,
{
field: 'rvcd',
operator: 'eq',
@ -454,14 +547,14 @@ const getFilter = () => {
];
// 沿 stcd
if (props.ly && formState.value.stcd) {
filters.push({
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: formState.value.stcd
});
}
// if (props.ly && formState.value.stcd) {
// filters.push({
// field: 'stcd',
// operator: 'eq',
// dataType: 'string',
// value: formState.value.stcd
// });
// }
return {
logic: 'and',
@ -516,7 +609,9 @@ const handleYearChange = () => {
};
/** Tab 切换 */
const handleTabChange = () => {
const handleTabChange = async () => {
// Tab BasicTable
await nextTick();
handleSearch();
};

View File

@ -32,7 +32,7 @@
@cancel="handleModalClose"
>
<Yanchengyulei
v-if="modalVisible"
v-if="modalVisible && JidiSelectEventStore.jidiData.length > 0"
:tm="modalYear"
:dataDimensionVal="baseid"
:hbrvcd="modalHbrvcd"
@ -525,12 +525,12 @@ const filterSelectOptions = (options: any[]) => {
* 点击图表柱子时触发传递当前选中的年份流域站点名称等参数
*/
const openDetail = (params: any) => {
// chartData stcd
const found = chartData.value.find((el: any) => el.stnm === params.name);
modalYear.value = select.value.value;
modalHbrvcd.value = moreSelect.value.value || '';
modalStateName.value = params.name || '';
modalStateName.value = params.seriesName || '';
modalSectionName.value = params.name || '';
modalStcd.value = found?.stcd || '';
modalVisible.value = true;

View File

@ -1,130 +1,418 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="典型设施介绍">
<div v-if="carouselData.length > 0" class="carousel-wrapper">
<a-carousel v-model:current="currentIndex" autoplay class="tech-carousel" :dot-style="{ bottom: '0px' }">
<!-- -->
<div v-for="(item, index) in carouselData" :key="index" class="carousel-item">
<div class="image-container">
<img :src="item.image" :alt="item.title" class="carousel-image" />
</div>
</div>
</a-carousel>
<!-- 文字描述区域 -->
<div v-if="carouselData[currentIndex]" class="description-container">
<p class="item-description">{{ carouselData[currentIndex].description }}</p>
<SidePanelItem
title="典型设施介绍"
:select="selectConfig"
@update-values="handlePanelChange"
>
<a-spin :spinning="loading">
<template v-if="originalMediaData.length > 0">
<div
class="container"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<!-- 跑马灯轨道容器 -->
<div
class="carousel-track"
:class="{ 'no-transition': isTransitioning }"
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
>
<!-- 遍历所有媒体项包含克隆项 -->
<div
v-for="(item, index) in renderMediaData"
:key="index"
class="carousel-item"
@click="handleItemClick"
>
<img :src="item.image" :alt="item.title" />
<!-- 说明文字随媒体项移动 -->
<!-- <div class="text">{{ item.title }}</div> -->
</div>
</div>
<!-- 面板指示器固定在底部右侧 -->
<div class="pagination-dots-fixed">
<span
v-for="(dot, index) in originalMediaData"
:key="index"
class="dot"
:class="{ active: getCurrentRealIndex() === index }"
@click="goToSlide(index)"
></span>
</div>
</div>
<div v-else>
<a-empty />
<!-- 独立的文字说明区域随跑马灯切换而变化 -->
<div class="description-text">
{{ currentDescription }}
</div>
</SidePanelItem>
</template>
<a-empty v-else description="暂无数据" />
</a-spin>
</SidePanelItem>
<!-- 设施详情弹框 -->
<a-modal v-model:open="modalVisible" :title="'设施详情'" width="1536px" :footer="null">
<div v-if="currentItem" class="detail-container">
<ArtsDetail :dataSource="currentItem" :index="getCurrentRealIndex()" />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
import { getMsstbprptKendoList } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
// 便
defineOptions({
name: 'dianxingcuoshijieshao'
name: 'dianxingcuoshijieshao'
});
//
const currentIndex = ref(0);
// store
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
//
const carouselData = ref([
{
title: '叠梁门',
description: '叠梁门是一种用于调节水库流量和控制水位的设施。在低温季节,可以通过控制叠梁门的开闭程度来调节流量,减少低温水体的进...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png' //
},
{
title: '环保设施',
description: '现代化环保设施,采用先进技术,有效处理工业废水废气,实现达标排放,保护生态环境...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
},
{
title: '智能监控',
description: '24 小时智能监控系统,实时监测设备运行状态,确保设施安全稳定运行...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
},
{
title: '水处理系统',
description: '高效水处理系统,通过多级过滤和生物处理,实现水资源循环利用...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
//
interface MediaItem {
title: string;
description: string;
image: string;
}
//
const modalVisible = ref(false);
const currentItem = ref<any>(null);
//
const loading = ref(false);
//
const selectConfig = ref({
show: true,
value: 'all',
width: '142px',
options: [
{ label: '全部', value: 'all' },
{ label: '鱼类增殖站', value: 'FB' },
{ label: '过鱼设施', value: 'FP' },
{ label: '低温水减缓设施', value: 'DW' }
]
});
//
const selectValue = ref('all');
//
const originalMediaData = ref<MediaItem[]>([]);
//
const renderMediaData = ref<MediaItem[]>([]);
// renderMediaData
const currentIndex = ref(1);
//
let timer: any = null;
//
const isHovering = ref(false);
//
const isTransitioning = ref(false);
// sttpCode
const dw = ['DW', 'DW_1', 'DW_2', 'DW_3', 'DW_4', 'DW_5', 'DW_6', 'DW_9'];
const fp = ['FP', 'FP_1', 'FP_2', 'FP_3', 'FP_4', 'FP_5'];
//
const initRenderData = () => {
const length = originalMediaData.value.length;
if (length === 0) {
renderMediaData.value = [];
return;
}
renderMediaData.value = [
originalMediaData.value[length - 1],
...originalMediaData.value,
originalMediaData.value[0]
];
currentIndex.value = 1;
};
//
const startAutoPlay = () => {
if (timer) clearInterval(timer);
timer = setInterval(() => {
if (!isHovering.value && !isTransitioning.value && originalMediaData.value.length > 0) {
nextSlide();
}
]);
}, 4000);
};
//
//
const nextSlide = () => {
currentIndex.value++;
setTimeout(() => {
checkSeamlessJump();
}, 500);
};
//
const checkSeamlessJump = () => {
const realLength = originalMediaData.value.length;
if (currentIndex.value >= realLength + 1) {
isTransitioning.value = true;
currentIndex.value = 1;
requestAnimationFrame(() => {
requestAnimationFrame(() => {
isTransitioning.value = false;
});
});
}
};
//
const handleMouseEnter = () => {
isHovering.value = true;
};
//
const handleMouseLeave = () => {
isHovering.value = false;
};
//
const currentDescription = computed(() => {
const realIndex = getCurrentRealIndex();
return originalMediaData.value[realIndex]?.description || '';
});
//
const getCurrentRealIndex = () => {
const realLength = originalMediaData.value.length;
let realIndex = currentIndex.value - 1;
if (realIndex < 0) realIndex = realLength - 1;
if (realIndex >= realLength) realIndex = 0;
return realIndex;
};
//
const goToSlide = (targetIndex: number) => {
if (isTransitioning.value) return;
currentIndex.value = targetIndex + 1;
};
//
const handleItemClick = () => {
currentItem.value = originalMediaData.value.map((item: any) => ({
url: item.image || '',
description: item.description || '',
title: item.title || ''
}));
modalVisible.value = true;
};
//
const handlePanelChange = (payload: any) => {
if (payload.select !== undefined && payload.select !== selectValue.value) {
selectValue.value = payload.select;
getData();
}
};
//
const getData = async () => {
loading.value = true;
try {
// sttpCode
let sttpCodes: string[] = ['FB', ...dw, ...fp];
if (selectValue.value === 'FB') {
sttpCodes = ['FB'];
} else if (selectValue.value === 'DW') {
sttpCodes = [...dw];
} else if (selectValue.value === 'FP') {
sttpCodes = [...fp];
}
const filters: any[] = [
{
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: sttpCodes
},
{
logic: 'and',
filters: [
{ field: 'logo', dataType: 'string', operator: 'isnotnull' },
{ field: 'introduce', dataType: 'string', operator: 'isnotnull' }
]
}
];
//
if (baseid.value && baseid.value !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
});
}
const params = {
filter: {
logic: 'and',
filters: filters
},
select: ['introduce', 'logo', 'stnm', 'precis']
};
const res = await getMsstbprptKendoList(params);
if (res?.data?.data && Array.isArray(res.data.data)) {
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL || '';
originalMediaData.value = res.data.data
.filter((item: any) => item.logo || item.introduce)
.map((item: any) => ({
image: item.logo ? baseUrl + '?' + item.logo + '&view=jpg' : '',
title: item.stnm || '',
description: item.introduce || ''
}));
} else {
originalMediaData.value = [];
}
initRenderData();
} catch (error) {
console.error('获取设施介绍数据失败:', error);
originalMediaData.value = [];
initRenderData();
} finally {
loading.value = false;
}
};
//
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getData();
}
},
{ deep: true, immediate: true }
);
//
onMounted(() => {
//
startAutoPlay();
});
//
onUnmounted(() => {
if (timer) clearInterval(timer);
});
</script>
<style lang="scss" scoped>
.carousel-wrapper {
width: 414px;
:deep(.slick-slide) {
.container {
width: 100%;
height: 228px;
position: relative;
overflow: hidden;
text-align: center;
height: 250px;
line-height: 160px;
background: #364d79;
overflow: hidden;
.carousel-track {
display: flex;
width: 100%;
height: 100%;
transition: transform 0.5s ease-in-out;
&.no-transition {
transition: none;
}
:deep() {
.ant-carousel .slick-dots li {
width: 5px !important;
height: 5px !important;
border-radius: 50% !important;
background-color: #888e95;
.carousel-item {
min-width: 100%;
height: 100%;
position: relative;
flex-shrink: 0;
cursor: pointer;
}
.ant-carousel .slick-dots li button{
width: 5px !important;
height: 5px !important;
border-radius: 50% !important;
background-color: #888e95;
}
.ant-carousel .slick-dots li.slick-active{
background-color: #005293 !important;
}
.ant-carousel .slick-dots li.slick-active button{
background-color: #005293 !important;
}
.ant-carousel .slick-dots li :hover {
width: 5px !important;
height: 5px !important;
border-radius: 50% !important;
background-color: #ffffffcc;
}
.ant-carousel .slick-dots li button :hover{
width: 5px !important;
height: 5px !important;
border-radius: 50% !important;
background-color: #ffffffcc;
}
}
.description-container {
height: 44px;
img {
width: 100%;
margin-top: 6px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-indent: 0em;
}
height: 100%;
object-fit: cover;
}
.text {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 28px;
line-height: 28px;
background: rgba(0, 0, 0, 0.5);
color: #fff;
padding-left: 12px;
font-size: 14px;
}
}
}
.pagination-dots-fixed {
position: absolute;
bottom: 10px;
right: 10px;
display: flex;
gap: 6px;
z-index: 10;
.dot {
width: 5px;
height: 5px;
border-radius: 50%;
background-color: #d8d8d8;
cursor: pointer;
transition: background-color 0.3s ease;
&.active {
background-color: #005293;
}
&:hover {
opacity: 0.8;
}
}
}
}
.description-text {
font-size: 14px;
line-height: 1.5;
transition: all 0.3s ease;
margin-top: 12px;
min-height: 44px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
}
.detail-container {
width: 100%;
height: 100%;
}
</style>

View File

@ -31,7 +31,7 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, computed, reactive,watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { msstbprptGetKendoList } from '@/api/yldc';
import { VDmsstbprptGetKendoList } from '@/api/yldc';
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
// 便
@ -84,7 +84,7 @@ const getVideoSelect = async () => {
},
select: ['stcd', 'stnm', 'url']
};
let res = await msstbprptGetKendoList(params);
let res = await VDmsstbprptGetKendoList(params);
let resData = res.data.data; //stnm stcd url
select.value.options = resData.map(item => {
return {

View File

@ -0,0 +1,390 @@
<template>
<div class="huanbao-two-layers-container">
<!-- 搜索表单 -->
<div class="search-form">
<a-space size="middle">
<div>所属基地:</div>
<a-select
v-model:value="searchData.baseid"
placeholder="请选择基地"
style="width: 200px"
show-search
:filter-option="filterOption"
:options="baseOptions"
@change="handleBaseChange"
/>
<div>类型:</div>
<a-select
v-model:value="searchData.type"
placeholder="请选择类型"
style="width: 200px"
:options="typeOptions"
@change="handleTypeChange"
/>
<div>建设状态:</div>
<a-select
v-model:value="searchData.bldstt"
placeholder="请选择状态"
style="width: 150px"
:options="bldsttOptions"
@change="handleBldsttChange"
/>
<a-input
v-model:value="searchData.search"
placeholder="搜索名称/电站"
style="width: 200px"
allow-clear
@press-enter="handleSearch"
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-tooltip title="重置">
<a-button @click="handleReset">重置</a-button>
</a-tooltip>
</a-space>
</div>
<!-- 数据表格 -->
<div class="table-wrapper">
<BasicTable
ref="tableRef"
:columns="columns"
:scroll-y="500"
:list-url="envOpdGetKendoListCust"
:search-params="tableSearchParams"
:transform-data="customTransform"
row-key="stcd"
>
<template #stnm="{ record }">
<a
v-if="record.stnm && record.stnm !== '-'"
@click="handleViewDetail(record, 'DW')"
class="text_hocer"
>{{ record.stnm }}</a
>
</template>
<template #ennm="{ record }">
<a
v-if="record.ennm && record.ennm !== '-'"
@click="handleViewDetail(record, 'ENG')"
class="text_hocer"
>{{ record.ennm }}</a
>
</template>
</BasicTable>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { envOpdGetKendoListCust, getBaseList } from '@/api/home';
import { useModelStore } from '@/store/modules/model';
const modelStore = useModelStore();
// ==================== Props ====================
const props = defineProps<{
data?: any;
currentJidiInfo?: any;
res?: any;
}>();
// ==================== ====================
const tableRef = ref();
const baseList = ref<any[]>([]);
//
const searchData = ref<any>({
baseid: props.currentJidiInfo?.wbsCode || 'all',
type: props.data?.type || '',
bldstt: props.res?.bldstt || '',
hydrodtin: props.res?.hydrodtin || '',
search: ''
});
//
const baseOptions = computed(() => {
const options = [];
baseList.value.forEach(item => {
options.push({
label: item.wbsName || item.basename,
value: item.wbsCode || item.baseid
});
});
return options;
});
//
const typeOptions = [
{ label: '全部', value: '' },
{ label: '鱼类增殖站', value: 'FB' },
{ label: '过鱼设施', value: 'FP' },
{ label: '珍稀植物园', value: 'VP' },
{ label: '动物救助站', value: 'VA' },
{ label: '低温水减缓设施', value: 'DW' },
{ label: '鱼类栖息地', value: 'FH' },
{ label: '生态流量泄放设施', value: 'EQ' }
];
//
const bldsttOptions = [
{ label: '全部', value: '' },
{ label: '未建', value: '0' },
{ label: '在建', value: '1' },
{ label: '已建', value: '2' }
];
// ==================== ====================
const columns = [
{
title: '名称',
key: 'stnm',
dataIndex: 'stnm',
width: 150
},
{
title: '建设情况',
key: 'bldsttCcodeName',
dataIndex: 'bldsttCcodeName',
width: 120
},
{
title: '类型',
key: 'sttpName',
dataIndex: 'sttpName',
width: 120
},
{
title: '接入日期',
key: 'dtinTm',
dataIndex: 'dtinTm',
width: 120
},
{
title: '关联电站',
key: 'ennm',
dataIndex: 'ennm',
width: 150
},
{
title: '关联基地',
key: 'baseName',
dataIndex: 'baseName',
width: 150
}
];
// ==================== ====================
const tableSearchParams = computed(() => {
return {
sort: [
{ field: 'baseStepSort', dir: 'asc' },
{ field: 'dtin', dir: 'desc' },
{ field: 'rvcdStepSort', dir: 'asc' },
{ field: 'rstcdStepSort', dir: 'asc' },
{ field: 'siteStepSort', dir: 'asc' }
],
select: [
'stnm',
'bldsttCcodeName',
'sttpName',
'dtinTm',
'ennm',
'baseName',
'stcd',
'sttpCode'
],
group: [],
groupResultFlat: false
};
});
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// ==================== ====================
const buildSearchFilter = () => {
const filters: any[] = [];
if (searchData.value.baseid && searchData.value.baseid !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
value: searchData.value.baseid
});
}
if (searchData.value.type) {
const operator = searchData.value.type === 'EQ' ? 'contains' : 'eq';
filters.push({
field: 'sttpCode',
operator: operator,
value: searchData.value.type
});
}
if (searchData.value.bldstt !== undefined && searchData.value.bldstt !== '') {
filters.push({
field: 'bldsttCcode',
operator: 'eq',
value: searchData.value.bldstt
});
}
if (
searchData.value.hydrodtin !== undefined &&
searchData.value.hydrodtin !== ''
) {
filters.push({
field: 'dtin',
operator: 'eq',
value: searchData.value.hydrodtin
});
}
if (searchData.value.search) {
filters.push({
logic: 'or',
filters: [
{
field: 'baseName',
operator: 'contains',
value: searchData.value.search
},
{ field: 'stnm', operator: 'contains', value: searchData.value.search },
{ field: 'ennm', operator: 'contains', value: searchData.value.search }
]
});
}
return {
logic: 'and',
filters: filters.filter(
f => f.value !== undefined && f.value !== null && f.value !== ''
)
};
};
// ==================== ====================
const fetchBaseList = async () => {
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'wbsType',
operator: 'eq',
dataType: 'string',
value: 'PSB'
},
{ field: 'treeLevel', operator: 'eq', dataType: 'string', value: '1' }
]
},
sort: [{ field: 'orderIndex', dir: 'asc' }]
};
const res = await getBaseList(params);
if (res?.data?.data) {
baseList.value = res.data.data;
}
} catch (error) {
console.error('获取基地列表失败:', error);
}
};
// ==================== ====================
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const handleBaseChange = () => {
handleSearch();
};
const handleTypeChange = () => {
handleSearch();
};
const handleBldsttChange = () => {
handleSearch();
};
const handleSearch = () => {
const filter = buildSearchFilter();
tableRef.value?.getList(filter);
};
const handleReset = () => {
searchData.value = {
baseid: props.currentJidiInfo?.wbsCode || 'all',
type: props.data?.type || '',
bldstt: '',
hydrodtin: '',
search: ''
};
const filter = buildSearchFilter();
tableRef.value?.reset();
tableRef.value?.getList(filter);
};
const handleViewDetail = (record: any, type: any) => {
console.log('查看详情:', record);
if (type == 'ENG') {
modelStore.modalVisible = true;
modelStore.params = record;
modelStore.params.sttp = 'ENG';
modelStore.title = record.ennm;
modelStore.params.stcd = record.stcd;
} else {
console.log('查看详情:', record);
modelStore.modalVisible = true;
modelStore.params = record;
modelStore.params.sttp = record.sttpCode;
// debugger
modelStore.title = record.stnm;
modelStore.params.stcd = record.stcd;
}
// TODO:
};
// ==================== ====================
onMounted(() => {
fetchBaseList();
handleSearch();
});
</script>
<style scoped lang="scss">
.huanbao-two-layers-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
.search-form {
margin-bottom: 16px;
border-radius: 4px;
}
.table-wrapper {
flex: 1;
overflow: hidden;
}
}
.text_hocer {
color: #2f6b98;
}
.text_hocer:hover {
color: #40a9ff;
}
</style>

View File

@ -1,131 +1,210 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="环保设施情况">
<div class="facility-grid" >
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
</div>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count }}</span><span></span></div>
</div>
</div>
<SidePanelItem title="环保设施情况">
<a-spin :spinning="loading">
<div class="facility-grid" v-if="facilities.length > 0">
<div
v-for="facility in facilities"
:key="facility.type"
class="facility-card"
@click="handleCardClick(facility)"
>
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon"></i>
</div>
</SidePanelItem>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 16px;">
<span class="facility-count">{{ facility.nowNum + facility.buildNum }}</span><span></span>
</div>
</div>
</div>
</div>
<a-empty v-else />
</a-spin>
<!-- 二级弹框 -->
<a-modal
v-model:open="modalVisible"
title="环保设施情况"
width="1536px"
:footer="null"
>
<HuanbaoModTwoLayers
v-if="modalVisible"
:data="modalData"
:currentJidiInfo="currentJidiInfo"
:res="modalRes"
/>
</a-modal>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { environmentalProtectionConstruction } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import HuanbaoModTwoLayers from './TwoLayers/HuanbaoModTwoLayers.vue';
// 便
defineOptions({
name: 'huanbaoMod'
name: 'huanbaoMod'
});
// store
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const currentJidiInfo = ref<any>(null);
//
const facilityList = [
{ name: '生态流量泄放设施', icon: 'icon iconfont icon-shengtailiuliang2', type: 'EQ', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
{ name: '低温水减缓设施', icon: 'icon iconfont icon-diwenshuijianhuan', type: 'DW', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
{ name: '栖息地', icon: 'icon iconfont icon-qixidi', type: 'FH', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 },
{ name: '过鱼设施', icon: 'icon iconfont icon-guoyusheshi', type: 'FP', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
{ name: '鱼类增殖站', icon: 'icon iconfont icon-yuleizengzhizhan', type: 'FB', accessNum: 0, buildNum: 0, nowNum: 0, totalNum: 1 },
{ name: '珍稀植物园', icon: 'icon iconfont icon-zhenxizhiwuyuan', type: 'VP', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 },
{ name: '动物救助站', icon: 'icon iconfont icon-dongwujiuzhuzhan', type: 'VA', accessNum: 0, buildNum: 0, totalNum: 1, nowNum: 0 }
];
//
const facilities = ref([
{
name: '生态流量泄放设施',
count: 145,
icon: 'icon iconfont icon-shengtailiuliang2'
},
{
name: '低温水减缓设施',
count: 24,
icon: 'icon iconfont icon-diwenshuijianhuan'
},
{
name: '栖息地',
count: 142,
icon: 'icon iconfont icon-qixidi'
},
{
name: '过鱼设施',
count: 60,
icon: 'icon iconfont icon-guoyusheshi'
},
{
name: '鱼类增殖站',
count: 69,
icon: 'icon iconfont icon-yuleizengzhizhan'
},
{
name: '珍稀植物园',
count: 41,
icon: 'icon iconfont icon-zhenxizhiwuyuan'
},
{
name: '动物救助站',
count: 4,
icon: 'icon iconfont icon-dongwujiuzhuzhan'
}
]);
//
onMounted(() => {
const facilities = ref<any[]>([]);
const loading = ref(false);
//
const modalVisible = ref(false);
const modalData = ref<any>({});
const modalRes = ref({
bldstt: '',
hydrodtin: '1'
});
//
const getData = async () => {
if (!baseid.value) return;
loading.value = true;
try {
// const params = { baseId: baseid.value, '1': '1' };
const params ={
"filter": {
"logic": "and",
"filters": [
{
"field": "1",
"operator": "eq",
"dataType": "string",
"value": "1"
},
baseid.value !='all'?{
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": "01"
}:null
].filter(Boolean)
}
}
const res = await environmentalProtectionConstruction(params);
if (res?.data?.data) {
const list = [...facilityList];
list.forEach((item) => {
const found = res.data.data.find((el: any) => item.type === el.repCode);
if (found) {
item.nowNum = found.count || 0;
}
});
facilities.value = list;
} else {
facilities.value = [];
}
} catch (error) {
console.error('获取环保设施数据失败:', error);
facilities.value = [];
} finally {
loading.value = false;
}
};
//
const handleCardClick = (facility: any) => {
modalData.value = facility;
modalRes.value = {
bldstt: '',
hydrodtin: ''
};
modalVisible.value = true;
};
//
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
currentJidiInfo.value = newVal;
getData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
.facility-grid {
width: 406px;
flex-flow: wrap;
display: flex;
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;
width: 406px;
flex-flow: wrap;
display: flex;
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;
}
.facility-card {
width: 200px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0px;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
transition: all 0.3s;
cursor: pointer;
box-sizing: border-box;
width: 200px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0px;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
transition: all 0.3s;
cursor: pointer;
box-sizing: border-box;
}
.facility-icon {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
// margin-right: 8px;
background: #2f6b98;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: #2f6b98;
border-radius: 50%;
.anticon {
font-size: 24px;
color: #fff;
}
.anticon {
font-size: 24px;
color: #fff;
}
}
.facility-info {
flex: 1;
flex: 1;
}
.facility-name {
font-size: 16px;
color: #333;
// margin-bottom: 4px;
// font-weight: 500;
font-size: 16px;
color: #333;
}
.facility-count {
font-size: 18px;
color: #2f6b98;
// font-weight: 600;
font-size: 18px;
color: #2f6b98;
}
</style>

View File

@ -0,0 +1,591 @@
<template>
<div class="huanbao-ej-container">
<!-- Tab切换 -->
<a-tabs v-model:activeKey="tabIndex" @change="handleTabChange">
<a-tab-pane
v-for="tab in tabs"
:key="String(tab.orderInx)"
:tab="tab.name"
/>
</a-tabs>
<!-- 搜索表单 -->
<div class="search-form">
<a-space size="middle">
<div>所属基地:</div>
<a-select
v-model:value="searchData.baseId"
placeholder="请选择所属基地"
style="width: 200px"
show-search
:filter-option="filterOption"
:options="jiDiList"
@change="handleBaseChange"
/>
<a-input
v-if="tabIndex !== '7'"
v-model:value="searchData.stnm"
placeholder="请输入测站名称"
style="width: 200px"
allow-clear
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-tooltip title="重置">
<a-button @click="handleReset">重置</a-button>
</a-tooltip>
</a-space>
</div>
<!-- 列表 -->
<BasicTable
ref="tableRef"
:scrollY="400"
:columns="columns"
:list-url="vmsstbprptGetKendoList"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #coenvwState="{ column, record }">
<a-tag :color="record.coenvwState > 0 ? 'green' : '#999'">
{{ record.coenvwState > 0 ? '在线' : '离线' }}
</a-tag>
</template>
<template #dvtp="{ column, record }">
{{ dvtpText(record.dvtp) }}
</template>
<template #stindxForEng="{ column, record }">
{{ record.dvtp == 2 ? '生态流量' : '出库流量' }}
</template>
<template #action="{ column, record }">
<a @click="handleViewDetail(record, 'detail')" class="text-link">
查看详情
</a>
</template>
</BasicTable>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { vmsstbprptGetKendoList } from '@/api/home';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
const JidiSelectEventStore = useJidiSelectEventStore();
// ==================== Props ====================
const props = defineProps<{
baseId?: string;
tabs?: any[];
activeKey?: string | number;
}>();
// ==================== ====================
const tabIndex = ref(String(props.activeKey ?? ''));
const searchData = ref<any>({
baseId: props.baseId || 'all',
stnm: null
});
const modelStore = useModelStore();
const tableRef = ref();
// ==================== ====================
const jiDiList: any = ref([]);
// ==================== ====================
const columns = computed(() => {
// (tabIndex=7)
if (tabIndex.value === '7') {
return [
{
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName',
width: '150px',
merge: true
},
{
key: 'stnm',
title: '电站名称',
dataIndex: 'stnm',
width: '176px',
},
{
key: 'dvtp',
title: '开发方式',
dataIndex: 'dvtp',
width: '120px',
slots: { customRender: 'dvtp' }
},
{
key: 'stindx',
title: '生态流量站类型',
dataIndex: 'stindx',
slots: { customRender: 'stindxForEng' }
},
{
key: 'jcdt',
title: '建成时间',
dataIndex: 'jcdt',
width: '150px',
sort: true
},
{
key: 'coenvwState',
title: '监测状态',
dataIndex: 'coenvwState',
width: '120px',
slots: { customRender: 'coenvwState' }
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const,
slots: { customRender: 'action' }
}
];
}
//
const baseColumns = [
{
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName',
width: '150px',
merge: true
},
{
key: 'stnm',
title: '测站名称',
dataIndex: 'stnm',
width: '176px',
},
{
key: 'ennm',
title: '电站',
dataIndex: 'ennm',
width: '150px',
slots: { customRender: 'ennm' }
},
{
key: 'jcdt',
title: '建成时间',
dataIndex: 'jcdt',
width: '150px',
sort: true
},
{
key: 'stindx',
title: '监测指标',
dataIndex: 'stindx',
width: '150px',
}
];
// (tabIndex=4)
if (tabIndex.value !== '4') {
baseColumns.push({
key: 'coenvwState',
title: '监测状态',
dataIndex: 'coenvwState',
width: '120px',
slots: { customRender: 'coenvwState' }
} as any);
}
baseColumns.push({
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const,
slots: { customRender: 'action' }
} as any);
return baseColumns;
});
// ==================== ====================
const searchParams = computed(() => {
return {
sort: [
{
field: 'baseStepSort',
dir: 'asc'
},
{
field: 'hbrvcd',
dir: 'asc'
},
{
field: 'rstcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
}
],
group: [],
select: [
'baseName',
'stnm',
'ennm',
'jcdt',
'stindx',
'coenvwState',
'sttpCode',
'stcd',
'rstcd',
'stCode',
'stName',
'dvtp'
],
groupResultFlat: false
};
});
// ==================== ====================
const handleTabChange = (key: string) => {
tabIndex.value = key;
handleSearch();
};
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const handleBaseChange = () => {
handleSearch();
};
const dvtpText = (dvtp: number) => {
if (dvtp === 2) return '引水式';
if (dvtp === 3) return '混合式';
return '坝后式';
};
const handleSearch = () => {
let filters: any[] = [];
if (searchData.value.baseId !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
value: searchData.value.baseId
});
}
if (searchData.value.stnm) {
filters.push({
field: 'stnm',
operator: 'contains',
value: searchData.value.stnm
});
}
// tabIndex
switch (tabIndex.value) {
case '1': //
filters.push(
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ENV,ENVM,WT'
}
);
break;
case '2': //
filters.push(
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ENV,ENVM,WQ'
},
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 1 }
);
break;
case '9': //
filters.push(
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'WQ' },
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
);
break;
case '10': //
filters.push(
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'WQFB'
},
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
);
break;
case '4': //
filters.push(
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'MM'
}
);
break;
case '5': //
filters.push(
{
logic: 'or',
filters: [
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ST,ZQ'
},
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ST,ZZ'
}
]
},
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{ field: 'sttpCode', operator: 'in', value: ['ZZ', 'ZQ'] },
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 1 }
);
break;
case '6': //
filters.push(
{
logic: 'or',
filters: [
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ST,ZQ'
},
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'ST,ZZ'
}
]
},
{ field: 'sttpCode', operator: 'in', value: ['ZZ', 'ZQ'] },
{ field: 'mway', operator: 'eq', dataType: 'string', value: 2 },
{ field: 'dtinType', operator: 'eq', dataType: 'string', value: 0 }
);
break;
case '7': //
filters.push(
{
field: 'runState',
operator: 'eq',
value: 4
},
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
{ field: 'DTIN', operator: 'eq', dataType: 'string', value: 1 },
{ field: 'bldsttCcode', operator: 'eq', dataType: 'string', value: 2 }
);
break;
case '8': //
filters.push(
{
field: 'sttpFullPath',
operator: 'contains',
dataType: 'string',
value: 'VD,VD'
},
{ field: 'dtin', operator: 'eq', dataType: 'string', value: 1 }
);
break;
case '11': //
filters.push({
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'FH'
});
break;
}
let filter = {
logic: 'and',
filters: filters
};
tableRef.value?.getList(filter);
};
const handleReset = () => {
searchData.value = {
baseId: 'all',
stnm: null
};
handleSearch();
};
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// sttpCode
const getSttpCode = (sttpCode: string) => {
const codeMap: Record<string, string> = {
DW: 'DW',
DW_1: 'DW',
DW_2: 'DW',
DW_3: 'DW',
DW_4: 'DW',
DW_5: 'DW',
DW_6: 'DW',
DW_9: 'DW',
EQS: 'EQS',
EQ: 'EQ',
EQ_1: 'EQ',
EQ_2: 'EQ',
EQ_3: 'EQ',
EQ_4: 'EQ',
EQ_5: 'EQ',
EQ_6: 'EQ',
EQ_7: 'EQ',
FB: 'EQ',
FPC: 'FP',
'FPC,': 'FP',
FP: 'FP',
FP_1: 'FP',
FP_2: 'FP',
FP_3: 'FP',
FP_4: 'FP',
FP_5: 'FP',
FH: 'FH',
'FH,': 'FH',
SG: 'SG',
'SG,': 'SG',
TE: 'TE',
'TE,': 'TE',
VA: 'VA',
'VA,': 'VA',
VP: 'VP',
'VP,': 'VP',
WE: 'WE',
'WE,': 'WE',
WQ: 'WQ',
'WQ,': 'WQ',
WQH: 'WQ',
WT: 'WT',
'WT,': 'WT',
WTRV: 'WT',
'WTRV,': 'WT',
WTVT: 'WT',
'WTVT,': 'WT'
};
return codeMap[sttpCode] || '';
};
//
const handleViewDetail = (record: any, type: string) => {
if (type === 'dz') {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENG';
modelStore.title = record.ennm;
modelStore.params.stcd = record.rstcd;
} else {
const sttpCode = record.sttpCode || '';
if (sttpCode === 'MM') {
modelStore.modalVisible = true;
modelStore.params.sttp = 'MM';
modelStore.title = record.stnm;
modelStore.params.stcd = record.stcd;
} else if (sttpCode === 'ENG') {
modelStore.modalVisible = true;
modelStore.params.sttp = getSttpCode(sttpCode) || sttpCode;
modelStore.params.eqtp = 'QEC';
modelStore.params.stcd = record.stcd;
modelStore.title = record.stnm;
} else if (sttpCode.includes('VD')) {
modelStore.modalVisible = true;
modelStore.params.sttp = 'VD';
modelStore.params.stcd = record.stcd;
modelStore.title = record.stnm;
} else if (sttpCode.includes('WT')) {
modelStore.modalVisible = true;
modelStore.params.sttp = getSttpCode(sttpCode) || sttpCode;
modelStore.params.stcd = record.stcd;
modelStore.title = record.stnm;
modelStore.params.enfc =
sttpCode === 'WTVT' || sttpCode === 'WTVT,' ? '1' : '0';
} else {
modelStore.modalVisible = true;
modelStore.params.sttp = sttpCode;
modelStore.params.stcd = record.stcd;
modelStore.title = record.stnm;
}
}
};
// ==================== ====================
onMounted(() => {
JidiSelectEventStore.jidiData?.forEach((element: any) => {
jiDiList.value.push({
label: element.wbsName,
value: element.wbsCode
});
});
handleSearch();
});
</script>
<style scoped lang="scss">
.huanbao-ej-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
.search-form {
margin-bottom: 16px;
border-radius: 4px;
}
:deep(.ant-table-wrapper) {
flex: 1;
overflow: hidden;
}
}
.text-link {
color: #2f6b98;
&:hover {
color: #40a9ff;
}
}
</style>

View File

@ -1,144 +1,223 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="环保自动监测工作开展情况">
<div class="facility-grid" >
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<img style="width: 18px;height: 14px;" :src="facility.icon ">
</div>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count }}</span><span></span></div>
</div>
</div>
<SidePanelItem title="环保自动监测工作开展情况">
<a-spin :spinning="loading">
<div class="facility-grid">
<div
v-for="facility in facilities"
:key="facility.orderInx"
class="facility-card"
@click="handleFacilityClick(facility)"
>
<div
style="
width: 60px;
height: 62px;
display: flex;
align-items: center;
justify-content: center;
"
>
<div class="facility-icon">
<img style="width: 18px; height: 14px" :src="facility.icon" />
</div>
</SidePanelItem>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 16px">
<span class="facility-count">{{ facility.count }}</span>
<span>{{ facility.unit || '个' }}</span>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
<!-- 弹框 -->
<a-modal
v-model:open="modalVisible"
title="环保自动监测工作开展情况"
:footer="null"
width="1536px"
@cancel="handleCloseModal"
>
<HuanbaozidongjianceEJ
v-if="modalVisible && currentFacility"
:activeKey="currentFacility.orderInx"
:tabs="facilities"
:baseId="baseid"
/>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { baseEvnmAutoMonitorGetKendoListCust } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import HuanbaozidongjianceEJ from './huanbaozidongjianceEJ/index.vue';
// SVG
import svg_guojiashuiwenzhan from '@/assets/svg/guojiashuiwenzhan.svg';
import svg_guojiashuizhizhan from '@/assets/svg/guojiashuizhizhan.svg';
import svg_qixiangzhan from '@/assets/svg/qixiangzhan.svg';
import svg_zijianshuiwenzhan from '@/assets/svg/zijianshuiwenzhan.svg';
import svg_shipinjiankongshebei from '@/assets/svg/shipinjiankongshebei.svg';
import svg_diwenshuijianhuan from '@/assets/svg/diwenshuijianhuan.svg';
import svg_liuliangjiancezhan from '@/assets/svg/liuliangjiancezhan.svg';
import svg_shuizhijiancezhan from '@/assets/svg/shuizhijiancezhan.svg';
import svg_zengzhizhanshuizhizhan from '@/assets/svg/zengzhizhanshuizhizhan.svg';
import svg_qixidi from '@/assets/svg/qixidi.svg';
// 便
defineOptions({
name: 'huanbaoMod'
name: 'huanbaoMod'
});
//
const facilities = ref([
{
"name": "生态流量",
"count": 452
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwMDEuNTIzIDgzOS4zNTVjLTE4LjEyNi04LjQ5OS0zOS4zMjQuMTU0LTQ3LjM2MyAxOS4zNTQtLjE0MS4zMzktLjI3OC42OC0uNDA5IDEuMDI0LTI4LjY3NCA3Mi4zNDQtMTA3LjMyIDEwNi4zNC0xNzUuNjI1IDc1LjkyOGExMzguNTUzIDEzOC41NTMgMCAwIDEtNzIuMzQ5LTc3LjgyM2MtNy42OC0xOS44MTQtMjguOTI5LTI5LjQzOS00Ny43NzItMjEuNTU1YTM2LjY2NSAzNi42NjUgMCAwIDAtMTYuODk3IDE0Ljc5N2MtMS42ODkgMi40NTgtMy40ODIgNS41ODEtNS4yMjIgOC42MDEtMjAuOTQxIDUyLjY4NC02OS41ODQgODYuOTg3LTEyMy40NDkgODcuMDM5LTUzLjU1OCAwLTEwMS4zMy0zMy45OTctMTIyLjg4Ni04NS4xOTVhMzguOTE0IDM4LjkxNCAwIDAgMC0yMS41MDUtMjUuODU2Yy0xOC43OTItNy44ODUtNDAuMDkxIDEuNjktNDcuNzcyIDIxLjUwNC0yMC4zMjcgNTQuMDY2LTY5LjY4NyA4OS40OTUtMTI0LjU3NSA4OS41NDctNTUuMTk3LS4wNTItMTA0LjgxMi0zNS43MzctMTI1LjI5Mi05MC4xMTEtNy40MjUtMTkuNzExLTI4LjMxNi0yOS41NDEtNDcuMTA3LTIyLjIyLTE4LjYzOCA3Ljg4NS0yNy45NTcgMzAuMDU0LTIwLjk5MyA1MC4wMjEgNDEuOTg2IDExMy4wOTkgMTYyLjUxNyAxNjguNzUyIDI2OS4yNzMgMTI0LjI2YTIwOC40MzUgMjA4LjQzNSAwIDAgMCA4Mi40MzYtNjIuNTY1Yzc0LjE5MyA5Mi43NzMgMjA1LjMyMiAxMDQuMTkgMjkyLjc3NiAyNS40OTdhMjE0LjQ2IDIxNC40NiAwIDAgMCAyNC41NzctMjYuMTExYzczLjYzIDkyLjk3NyAyMDQuNDUyIDEwNS4wNiAyOTIuMTYxIDI2Ljk4MiAyNS4yNDMtMjIuNDI1IDQ1LjA1OS01MC45NDMgNTcuODA4LTgzLjA5NiA3LjQzNC0xOS4yOS0xLjE4Ny00MS4wNTUtMTkuODE1LTUwLjAyMiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMDAxLjUyMyA2NDcuMzU5Yy0xOC4xMjYtOC40OTktMzkuMzI0LjE1NC00Ny4zNjMgMTkuMzU0LS4xNDEuMzM5LS4yNzguNjgtLjQwOSAxLjAyNC0yOC42NzQgNzIuMzQ0LTEwNy4zMiAxMDYuMzQtMTc1LjYyNSA3NS45MjhhMTM4LjU1MyAxMzguNTUzIDAgMCAxLTcyLjM0OS03Ny44MjNjLTcuNjgtMTkuODE0LTI4LjkyOS0yOS40MzktNDcuNzcyLTIxLjU1NWEzNi42NjUgMzYuNjY1IDAgMCAwLTE2Ljg5NyAxNC43OTdjLTEuNjg5IDIuNDU4LTMuNDgyIDUuNTgxLTUuMjIyIDguNjAxLTIwLjk0MSA1Mi42ODQtNjkuNTg0IDg2Ljk4Ny0xMjMuNDQ5IDg3LjAzOS01My41NTggMC0xMDEuMzMtMzMuOTk3LTEyMi44ODYtODUuMTk1YTM4LjkxNCAzOC45MTQgMCAwIDAtMjEuNTA1LTI1Ljg1NmMtMTguNzkyLTcuODg1LTQwLjA5MSAxLjY5LTQ3Ljc3MiAyMS41MDQtMjAuMzI3IDU0LjA2Ni02OS42ODcgODkuNDk1LTEyNC41NzUgODkuNTQ3LTU1LjE5Ny0uMDUyLTEwNC44MTItMzUuNzM3LTEyNS4yOTItOTAuMTExLTcuNDI1LTE5LjcxMS0yOC4zMTYtMjkuNTQxLTQ3LjEwNy0yMi4yMi0xOC42MzggNy44ODUtMjcuOTU3IDMwLjA1NC0yMC45OTMgNTAuMDIxIDQxLjk4NiAxMTMuMDk5IDE2Mi41MTcgMTY4Ljc1MiAyNjkuMjczIDEyNC4yNmEyMDguNDM1IDIwOC40MzUgMCAwIDAgODIuNDM2LTYyLjU2NWM3NC4xOTMgOTIuNzczIDIwNS4zMjIgMTA0LjE5IDI5Mi43NzYgMjUuNDk3YTIxNC40NiAyMTQuNDYgMCAwIDAgMjQuNTc3LTI2LjExMWM3My42MyA5Mi45NzcgMjA0LjQ1MiAxMDUuMDYgMjkyLjE2MSAyNi45ODIgMjUuMjQzLTIyLjQyNSA0NS4wNTktNTAuOTQzIDU3LjgwOC04My4wOTYgNy40MzQtMTkuMjktMS4xODctNDEuMDU1LTE5LjgxNS01MC4wMjIiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNNDg2LjA2NyAwYzYuOTY0IDAgMTMuODI1IDIuOTE4IDE4LjYzOCA4LjA4OWwxOC42MzggMjAuNjM0IDIxLjYwNyAyNS40NDUgMTIuOTAzIDE1LjgyMUw1NzkgOTcuMTI0bDE0Ljg0OSAyMC4xMjIgMTUuMjA3IDIxLjUwMyAxNS4yNTggMjIuNjNhODk3LjgzNCA4OTcuODM0IDAgMCAxIDIzLjI0NiAzNy41OGwxMi4wODQtMTYuMTI4IDUwLjY5LTY1Ljg0MSA1MC42OSA2NS43OWMxNS4yNTkgMTkuNzEyIDI5LjY5OCAzOS45MzUgNDMuMzE4IDYwLjk3OCA0My4yNjYgNjcuMDcxIDY3LjA3NiAxMjUuNTkxIDY3LjA3NiAxNzQuNTg5LS4xNTQgOTAuMjEyLTcxLjg4OSAxNjQuMjQ2LTE2MS4xODYgMTY0LjI0Ni0yMS4xOTggMC00Mi4wODktNC4yNS02MS40NDMtMTIuMzlsLTEuOTk3IDIuMTVhMjMwLjY2IDIzMC42NiAwIDAgMS01Ni4xNjkgNDEuNzI3IDIyMC4zNDggMjIwLjM0OCAwIDAgMS04OC43ODUgMjUuMzk1bC0xNS42NjkuNTEyYTIyNS4xNCAyMjUuMTQgMCAwIDEtMTYyLjgyMi02OS42MzEgMjQwLjYyIDI0MC42MiAwIDAgMS02Ny4zODMtMTY4LjI0YzAtNzEuMDY0IDM1Ljk5NS0xNTkuMjc5IDEwNi45MS0yNjIuMzQzbDcuNjgxLTExLjA1OSAxNS4zMDktMjEuMTQ1IDE0Ljc5OC0xOS42MDkgMTQuMDI5LTE3LjkyIDE4Ljk0NS0yMy4yOTUgMTUuNjY4LTE4LjQzMkw0NjcuNDMgOC4xOTJhMjUuNjA1IDI1LjYwNSAwIDAgMSAxMy40NjYtNy42OEw0ODYuMDY3IDBabS4zMDcgODMuNDU0LTMuNTg0IDQuMzUyYTExOTguMTA0IDExOTguMTA0IDAgMCAwLTY3LjE3NyA4OC4zMThjLTY0LjI2IDkzLjM4Ny05NS42NDYgMTcwLjMzOS05NS42NDYgMjI2LjA0NCAwIDQ2Ljk0OSAxNy41NjMgOTAuNzI0IDQ5LjM1OSAxMjMuNjQ1IDMxLjQ4OSAzMi41MTEgNzIuNzA4IDUwLjE3NSAxMTYuODQzIDUwLjE3NSAyNi40MjEgMCA1MS4yNTUtNi4xNDQgNzQuNjU0LTE4LjUzNCAxMi4yODktNi40NTEgMjMuODYxLTE0LjY0MyAzNC40MDktMjQuMjY4QTE2Ni4wMyAxNjYuMDMgMCAwIDEgNTQ5LjIgNDE4LjI5NWMwLTQ0LjY0NSAyMC45OTMtOTkuMzI2IDU4LjI2OS0xNjAuMTVsLTkuNTI0LTE3LjFhMTAyNi42MDMgMTAyNi42MDMgMCAwIDAtMTA2Ljg2LTE1Mi4wNjFsLTQuNzExLTUuNTNabTIyNC4wMTIgMTM4LjM5MS0xMy45NzkgMTguNjM2Yy03LjUyNyAxMC4zNDMtMTUuNzcgMjIuMTE4LTI0LjAxNCAzNC43NjUtMzguMTQ2IDU4LjUyLTU5LjE4OSAxMDkuNDEyLTU5LjE4OSAxNDMuMDQ5IDAgMzUuOTQyIDE4LjczOSA2OS4zMjQgNDkgODcuMTQxIDE0LjQ5IDguNjUzIDMxLjE4MiAxMy4xNTggNDguMDI4IDEzLjE1OCA1My41MDYgMCA5Ny4wOC00NS4wMDQgOTcuMTMxLTEwMC4zNSAwLTMzLjc5MS0xOS42NjItODIuMTc0LTU2LjgzNC0xMzkuNzczbC04LjYwMi0xMy4xMDctOC41NTEtMTIuMzM5LTE1LjkyNC0yMS45MTMtNy4wNjYtOS4yNjdaIiBmaWxsPSIjZmZmIi8+PC9zdmc+"
},
{
"name": "水温监测",
"count": 140
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDExMjUgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMwLjgzOCA4MzIuOTM5YzE5LjE4Mi00LjA5NiAzOS4wOTQgNS42MzIgNDYuNTc2IDIzLjcwNmw0LjA1IDguOTA4YzI0LjUyNSA0OS4zMDYgNzYuMjc3IDgxLjE1MiAxMzMuNTk1IDgxLjIwNGwxMC4wMTQtLjMwOGM1Ni4yNTEtMy43MzcgMTA1Ljc1MS0zOC4xOTUgMTI2Ljg0Ni04OS4xODlsMi40MTgtNC43NjNjOS4zODEtMTYuNTY0IDMxLjExNi0yMy44NTUgNTAuMDY0LTE2Ljc5M2w1LjU2OCAyLjcxNGM4Ljk4OCA1LjM5NiAxNS40NDYgMTMuNjQ3IDE4LjExMyAyMy4xNDJsNC41IDguODU4YzI1LjMxMyA0Ni4yMzMgNzUuMDM5IDc2LjMzOSAxMzAuNDQ1IDc2LjMzOSA1OS4xNzctLjA1MiAxMTIuNjE1LTM0LjM1NiAxMzUuNTY2LTg3LjA0bDIuOTI1LTQuNTA1IDIuODY5LTQuMDk3YzQuMzI1LTYuNTk3IDEwLjgxOS0xMS43OTEgMTguNTYyLTE0Ljg0OGw1LjIzMi0xLjUzNWMxOS45NTQtNC40NDMgNDAuMzY2IDUuNTc2IDQ3LjI1IDIzLjE5M2w0LjMzMiA5LjM2OWMxNS4xNTMgMzAuMzMxIDQxLjg3MyA1NC42MzMgNzUuMjA4IDY4LjQwMyA3NC45ODIgMzAuNDEzIDE2MS4zODQtMy41ODMgMTkyLjk0LTc1Ljk4bC4xNjktLjUxMmM5LjA1Ni0xOS42NjEgMzIuMzQzLTI4LjMxMyA1Mi4zMTMtMTkuODE0bDUuMjMxIDIuNzEzYzE2LjkzNSAxMC4zNDcgMjMuNzcxIDI5Ljk3MSAxNi40ODEgNDcuMzA5LTEzLjc2NCAzMS44MjUtMzUuNTEyIDYwLjI4MS02My41MDcgODMuMDk2LTk2LjM1OCA3OC4wOC0yNDAuMDc4IDY1Ljk5Ny0zMjAuOTY3LTI2Ljk4MmwtOC40MzggOS4yMTdhMjI1LjQ0MSAyMjUuNDQxIDAgMCAxLTE4LjU2MyAxNi44OTZjLTk2LjEzMyA3OC42OTQtMjQwLjE5MSA2Ny4yNzYtMzIxLjY5OS0yNS40OThsLTkuNDUgMTAuMTM3Yy0yMi40NTMgMjIuNjc4LTUwLjEzMSA0MC41NjItODEuMDU3IDUyLjM3OC0xMTcuMjg0IDQ0LjQ5Mi0yNDkuNzU1LTExLjE2Mi0yOTUuODgtMTI0LjI2MmwtMS41NzUtNS40NzhjLTQuMjkyLTE4LjI4NSA1Ljk4NS0zNi44MjMgMjQuNjkzLTQ0LjU0NFpNNTQ0Ljk3MS4wMjNjOTEuMTI3LTEuNDM0IDE2Ni44OTcgNjMuNDM2IDE3My43MDQgMTQ2LjY4NmwuNDUgOS4zMnYxNzUuMzU5YzkgNi42NTUgMTcuNDM3IDEzLjk3NyAyNS4xNDMgMjEuOTY0bDguMzI2IDkuMjE2YzkxLjI5NCAxMDEuMzc2IDc5Ljk4OCAyNTAuNTcxLTIzLjUxNCAzMzkuMDQ0bC0xMS4xOTQgOS4wNjNjLTExMi4yMDQgODcuMjY5LTI4MS4zNzUgNzMuMjAyLTM3NC4zNS0zMS4xMjktOTEuNjg4LTEwMS42ODMtODAuNTUtMjUxLjQ5MyAyMy4xNzctMzQwLjYzMmwxMC4wMTItOC4xOTJWMTU2LjAyOUMzNzguMzU2IDcwLjUyNSA0NTIuNjYzIDEuNTU5IDU0NC45MTYuMDIzWm0xLjIzOSA2My45OTljLTUxLjI0NS44NzEtOTMuNTQ3IDM3LjU4MS05OC42NjUgODQuMTIybC0uNTA3IDcuODg1djIwNC4xODNsLTI0LjYzNyAxOS4xNDljLTgzLjgxNCA2NS4yMjktOTQuODQgMTgxLjYwNi0yNC41MjUgMjU5LjU4MyA2NS4xNzcgNzMuNTQyIDE4Mi43NTUgODYuNjQ3IDI2NS41MDMgMjkuNTkzbDEwLjE4Mi03LjUyNmM4MC40MzktNjIuMjU5IDkzLjY1OC0xNzIuMDgzIDMxLjgzOC0yNDkuMzQybC05LjMzOC0xMC45MDZhMTIyLjE5NSAxMjIuMTk1IDAgMCAwLTEzLjUtMTMuNTE3bC03LjQ4MS01Ljk5LTI2LjI3LTE5LjJWMTU3LjEwNGMtLjk1Ni00OS45Mi00Mi43NS04OS44MDUtOTQuMzg5LTkyLjg3N2wtOC4yMTEtLjIwNVptMS41NzQgMTI4IDUuMjg4LjM1OGMxNy4yMTIgMi4zNTUgMzAuMzc1IDE1Ljg3MiAzMC40MzEgMzIuMTU0djIxMi4wMThjMzAuMzc2IDkuNjI1IDU0LjA1OCAzMS40MzcgNjQuMzUxIDU5LjEzNWwyLjUzMiA3LjU3N2MxNC4wODEgNDguNzY4LTE1LjI3IDk5LjIxNy02Ny41MDIgMTE2LjAxOWwtOC41NSAyLjI1M2MtNzkuNzA3IDE3LjQxMy0xNTAuMjM0LTUwLjI0LTEyNi45NDktMTIxLjc3NiA5LjQ4My0yOS4xMzMgMzMuNDI3LTUyLjUzMiA2NC42NzktNjMuMjA4VjIyNC41ODRjLjAzMi0xNy45NTIgMTUuOTk2LTMyLjUwNiAzNS43Mi0zMi41NjJaIiBmaWxsPSIjZmZmIi8+PC9zdmc+"
},
{
"name": "水文监测",
"count": 21
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZD0iTTkuOTcyIDEyLjc2YzEuMTA0IDAgMS42ODYuMTU3IDIuMi4yOTMuNDgxLjEyOC44OTQuMjQgMS43ODYuMjRWMTVjLTEuMSAwLTEuNjg1LS4xNTQtMi4yLS4yOTMtLjQ4LS4xMjgtLjg5NC0uMjQtMS43ODYtLjI0cy0xLjMwNi4xMTItMS43ODYuMjRjLS41MTQuMTM2LTEuMDk4LjI5My0yLjIuMjkzLTEuMTAxIDAtMS42ODYtLjE1NC0yLjItLjI5My0uNDgxLS4xMjgtLjg5NS0uMjQtMS43ODYtLjI0di0xLjcwOWMxLjEgMCAxLjY4NS4xNTUgMi4yLjI5NC40OC4xMjguODk0LjI0IDEuNzg2LjI0czEuMzA2LS4xMTIgMS43ODctLjI0Yy41MTQtLjEzNiAxLjA5OC0uMjk0IDIuMi0uMjk0di4wMDFabS4wMjYtMi42OTVjMS4xMDIgMCAxLjY4NS4xNTggMi4yLjI5My40OC4xMjkuODk1LjI0IDEuNzg3LjI0djEuNzA4Yy0xLjEwMiAwLTEuNjg2LS4xNTUtMi4yLS4yOTMtLjQ4MS0uMTI5LS44OTUtLjI0LTEuNzg2LS4yNC0uODkyIDAtMS4zMDYuMTExLTEuNzg3LjI0LS41MTUuMTM1LTEuMDk5LjI5My0yLjIuMjkzLTEuMTAxIDAtMS42ODUtLjE1NS0yLjItLjI5My0uNDgtLjEyOS0uODk1LS4yNC0xLjc4Ny0uMjR2LTEuNzA3YzEuMTAyIDAgMS42ODYuMTU0IDIuMi4yOTIuNDgyLjEyOS44OTUuMjQgMS43ODcuMjQuODkyIDAgMS4zMDUtLjExMSAxLjc4Ni0uMjQuNTE1LS4xMzUgMS4xLS4yOTMgMi4yLS4yOTNaTTE0IDF2Ny44NDloLTEuNjU0VjIuNzA4SDMuNjY1djYuMTRIMi4wMVYxSDE0Wk04Ljg4NCA0LjEyNHY0LjcyNUg3LjIzVjQuMTI0aDEuNjU1WiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9nPjwvc3ZnPg=="
},
{
"name": "河道水质监测",
"count": 56
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0ibTY2NS4yMTIgMCAuNTEyIDMwNy44MTRjMCAzMy41ODggNy44ODUgNjcuMTc1IDIyLjIyIDk3LjI4bDMyNC41MDYgNDYwLjY5OC43MTcgMi4wNDhjMTYuNDM1IDM0LjMwNCAxNC4zMzYgNzMuNTc0LTYuNDUxIDEwNS43MjhBMTE0LjMzIDExNC4zMyAwIDAgMSA5MTEuNjM4IDEwMjRoLTc5OS4xM2MtMzguNjA1IDAtNzQuMzQyLTE4Ljk0NC05NS4wNzgtNTEuMDk4YTEwNy40MTggMTA3LjQxOCAwIDAgMS02LjQ1Mi0xMDUuNzI4bDEuNDM0LTIuODE2IDMwNy4zNTQtNDU4LjU0N2MxNC4zMzYtMzAuODIyIDIyLjE3LTY0LjQxIDIyLjE3LTk3Ljk5N0wzNDEuNDIyIDBoMzIzLjc4OXptLTYzLjg0NiA2NGgtMTk1Ljg0bC40MSAyNDMuODE0YzAgMzYuNTU3LTcuMTY5IDczLjIxNi0yMC43ODggMTA3LjgyOGwtNy4zMjIgMTcuMS00Ljg2NCA4LjcwNEw2OC4xNyA4OTZsLTEuOTk3IDUuMTJhNDIuODAzIDQyLjgwMyAwIDAgMCAxLjg0MyAzMS4yODNsMy4yMjYgNS44MzdhNDcuNzE4IDQ3LjcxOCAwIDAgMCAzMy45OTcgMjEuMjQ4bDcuMjcuNTEyaDc5OS4xM2E0OS42NjQgNDkuNjY0IDAgMCAwIDQxLjI2Ny0yMS4wNDMgNDMuMDYgNDMuMDYgMCAwIDAgNS4wNjktMzcuMTJsLTIuMS01LjI3NC0zMjAuMjU2LTQ1NC42MDUtNS41My05LjM3YTI5My4zNzYgMjkzLjM3NiAwIDAgMS0yNy44NTItMTA4LjY0NmwtLjUxMi0xNi4wMjVMNjAxLjM2NiA2NHoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMjU2LjA3MyAwaDUxMnY2NGgtNTEyek0zNjUuMjMxIDY4Ni40OWE4My45NjggODMuOTY4IDAgMCAxIDgzLjYxIDgzLjYxYzAgNDMuMDA3LTMzLjE3OCA3OC44NDctNzUuMTEgODMuMDk3bC04LjUuNDZhODMuOTY4IDgzLjk2OCAwIDAgMS04My41NTgtODMuNTU4YzAtNDMuMDA4IDMzLjEyNi03OC44NDggNzUuMDU5LTgzLjE0OWw4LjUtLjQ2em0yNDUuMDk1LTE4Ljg0MmM1MS41MDcgMCA5Mi45NzkgNDEuNDcyIDkyLjk3OSA5My4wMyAwIDQ4LjQ4Ny0zNi43MSA4OC4wNjQtODMuOTY4IDkyLjU3bC05LjAxMS40MWE5Mi43NzQgOTIuNzc0IDAgMCAxLTkzLjAzLTkyLjk4YzAtNDguNTM3IDM2Ljc2LTg4LjA2NCA4NC4wMTgtOTIuNjJsOS4wMTItLjQxek0zNjUuMjMgNzUwLjQ5YTE5Ljk2OCAxOS45NjggMCAwIDAtMTkuNTU4IDE5LjYxIDE5Ljk2OCAxOS45NjggMCAwIDAgMTkuNTU4IDE5LjU1OCAxOS45NjggMTkuOTY4IDAgMCAwIDE5LjYxLTE5LjU1OSAxOS45NjggMTkuOTY4IDAgMCAwLTE5LjYxLTE5LjYxem0yNDUuMDk1LTE4Ljg0MmEyOC43NzQgMjguNzc0IDAgMCAwLTI5LjAzIDI5LjAzYzAgMTYuMTggMTIuOCAyOC45OCAyOS4wMyAyOC45OCAxNi4xNzkgMCAyOC45NzktMTIuOCAyOC45NzktMjguOThhMjguNzc0IDI4Ljc3NCAwIDAgMC0yOC45OC0yOS4wM3pNNTAyLjI0MiA0MjEuOTM5YTExNi4zNzggMTE2LjM3OCAwIDAgMSAxMTYuMjI0IDExNi4yMjRjMCA2MC45MjgtNDcuNjE2IDExMS4wNTMtMTA2LjgwMyAxMTUuOTE3bC05LjQyLjM1OGExMTYuMzc4IDExNi4zNzggMCAwIDEtMTE2LjIyNS0xMTYuMjI0YzAtNjAuOTI4IDQ3LjA1My0xMTEuMDAxIDEwNi43MDEtMTE1Ljg2NWw5LjUyMy0uNDF6bTAgNjRjLTI4Ljc3NCAwLTUyLjIyNCAyMy41NTItNTIuMjI0IDUyLjIyNCAwIDI4Ljc3NSAyMy40NSA1Mi4yNzUgNTIuMjI0IDUyLjI3NSAyOC4zNjUgMCA1Mi4yMjQtMjMuNzU2IDUyLjIyNC01Mi4yMjQgMC0yOC43NzQtMjMuNS01Mi4yMjQtNTIuMjI0LTUyLjIyNHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
},
{
"name": "增殖站水质站",
"count": 9
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI0IDBjMTIuODQgMCAyMy4yNSAxMC43NDUgMjMuMjUgMjRTMzYuODQgNDggMjQgNDggLjc1IDM3LjI1NS43NSAyNCAxMS4xNiAwIDI0IDBabS0uMDA1IDYuNjY4Yy05LjUwMSAwLTE3LjIwMiA3LjgyNC0xNy4yMDIgMTcuNDc2IDAgOC4zMjcgNS43MzIgMTUuMjg4IDEzLjQwNSAxNy4wNDVoNy41OTNjNy42NzMtMS43NTcgMTMuNDA2LTguNzE4IDEzLjQwNi0xNy4wNDUgMC05LjY1Mi03LjctMTcuNDc2LTE3LjIwMi0xNy40NzZabS4xOTggMS42OTNjOC40IDAgMTUuMjEgNi42NiAxNS4yMSAxNC44NzQgMCAuMDk4LS4wMDYuMTkzLS4wMDguMjloLS4wNDdjLS4wMDMtLjA1My0uMDEyLS4xMDUtLjAxNy0uMTU3LS4yNCA0LjI1Mi0zLjUwOSA3LjYyMS03LjUwNyA3LjYyMWE3LjAyNSA3LjAyNSAwIDAgMS0yLjU5My0uNDk1bC0uMTM2LjEwMmE4LjM5MSA4LjM5MSAwIDAgMC00LjYyMy0xLjM3IDguMzk5IDguMzk5IDAgMCAwLTQuMjMgMS4xMzMgNi44NzIgNi44NzIgMCAwIDEtMy45NTMgMS4yNjNjLTQuMDggMC03LjM4Ny0zLjYzOS03LjM4Ny04LjEyNSAwLS41NTUuMDUtMS4wOTcuMTQ3LTEuNjJDOS43NSAxNC4zIDE2LjI2IDguMzYgMjQuMTkzIDguMzZabS0uMTk1IDIuNzMxLS4wMDUuMTUzLS4wMDMtLjE1MmMtLjAwMi0uMDEtLjA2MS4wOS0uMTY1LjI3N2wtLjA4Ny4xNTVjLS4zNzUuNjgzLTEuMTIzIDIuMDc4LTEuODg1IDMuNTA2bC0uMjE2LjQwNWMtLjY2NiAxLjI1LTEuMzMgMi41MDItMS45OTQgMy43NTRhNS41MjIgNS41MjIgMCAwIDAtMS4wNTUgMy4yNTljMCAzLjAzMyAyLjQyIDUuNDkxIDUuNDA3IDUuNDkxIDIuOTg1IDAgNS40MDUtMi40NTggNS40MDUtNS40OTFhNS41MjcgNS41MjcgMCAwIDAtMS4wNTQtMy4yNTkgMTg2My4xNSAxODYzLjE1IDAgMCAwLTEuODg2LTMuNTUxbC0uNDMyLS44MWMtMS4wNC0xLjk1LTIuMDIzLTMuNzc3LTIuMDMtMy43MzdabTQuMDUyIDExLjM0MmMuMDAyLjA0OS4wMDYuMDk4LjAwNi4xNDcgMCAyLjE4Ny0xLjU0MiAzLjk2LTMuNDQ1IDMuOTYtMS4yMzggMC0yLjMyMy0uNzUtMi45My0xLjg3OS42ODMuNzI1IDEuNjE0IDEuMTcyIDIuNjQgMS4xNzIgMS44NjQgMCAzLjQxMS0xLjQ2NyAzLjcyOS0zLjRaIiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4="
},
{
"name": "国家水质站",
"count": 1722
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTguMDEzIDEyLjc5OGMxLjE3NC4wMDIgMi4yOTYtLjQ0MyAzLjEwNC0xLjIyOS44My0uODA4IDEuMjkyLTEuODggMS4yODgtMi45OTQgMC0yLjMzNi0xLjcyLTQuNzM1LTQuMzktOC41NzUtMi42OCAzLjg0LTQuMzkyIDYuMjQtNC4zOTIgOC41NzUgMCAxLjEyMy40NjQgMi4yMDIgMS4yODggMi45OTQuODA4Ljc4NiAxLjkzIDEuMjMgMy4xMDMgMS4yMjloLS4wMDFaTTcuOTggMi43MmMuMzUzLjUyMy41NDYgMS4xMzMuNTYyIDEuNzU5IDAgMi4wOC0yLjIxNCAyLjQzLTIuMjE0IDEuMTVDNi4zNiA0LjggNy40NSAzLjQ5IDcuOTgyIDIuNzJ2LjAwMmgtLjAwMVptLjAzMyAxMy4yNzdjLTEuOTUyIDAtNC43My0uNDgtNi4wMTktMS44ODhhOC41ODQgOC41ODQgMCAwIDEgMS42ODctLjcwNGMuNzI2LjQ4IDIuMjEzLjk5MiA0LjMzMi45OTIgMi4xMTggMCAzLjYwNC0uNTEyIDQuMzY0LS45OTJhOC42NSA4LjY1IDAgMCAxIDEuNjkuNzA2QzEyLjc0NiAxNS41MiA5Ljk2OCAxNiA4LjAxNyAxNmwtLjAwMy0uMDAyWm01LjkxNy04Ljg2MmMuNTMtLjgzMyAxLjQ1NC0uNDUgMS45MjEtMS4yMTcuNTYzLS44OTYtLjU5Ni0xLjgyNC0xLjUyLTEuMDI0LS40OTkuNDU1LS41OTcgMS4zMTItLjQgMi4yNGwtLjAwMS4wMDFaTTEuNjY2IDQuODljLS44OTQtLjgzMi0yLjA4My4wOTYtMS41MiAxLjAyNC40OTQuNzY3IDEuMzg5LjM4NCAxLjkyIDEuMjE2LjIyOC0uOTIzLjEyOS0xLjc4MS0uNC0yLjIzNlY0Ljg5WiIgZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+"
},
{
"name": "视频监控",
"count": 247
,"icon": "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDExMzcgMTAyNCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE0Ny43ODggMCA5NDcuNjYxIDI2My4wMDItNzUuMzE1IDk4LjA4NUwxMTM3IDM5Mi4zOTdsLTc4Ljc4MiAyOTQuNDgyLTIzOC43MzQtNjQuMDk5LTc1LjI1OCA5OC4xOTgtMzM5LjM0NC05NS4xMjR2MjA3LjYxMkg3My42NjdWMTAyNEgwVjY0Mi45ODloNzMuNjF2MTE0LjI1MmgyNTcuNzE5bC0uMDU3LTE1MS45OTVMOS45NDcgNTE1LjI0NSAxNDcuNzg4IDBaTTg1MS43NyAyNzQuMzg3IDYwNS4wNzggNjAyLjg1NWwxMTIuMjYyIDMxLjQyNCAyNTEuMTI1LTMyNy41TDg1MS43NyAyNzQuMzNaTTk3MCA0MjYuNDk2IDg2OS42MTggNTU3LjM3MWwxMzcuMTAyIDM2Ljc3NCAzOS4zOTEtMTQ3LjIxMi03Ni4xNjgtMjAuNDk0Wk0xOTguOTQ1IDkzLjE4OWwtOTguNTYzIDM2OC4zMTcgNDI4LjI0NCAxMTkuODg4IDI0Ni42OTItMzI4LjI5N0wxOTguOTQ1IDkzLjEzMloiIGZpbGw9IiNmZmYiLz48L3N2Zz4="
},
{
"name": "气象监测",
"count": 74058
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZD0iTTQuMTgzIDE2YS42NDIuNjQyIDAgMCAxLS42NC0uNjQ4LjY0Mi42NDIgMCAwIDEgLjY0LS42NDdINS41OXYtMS43N2E1LjkzIDUuOTMgMCAwIDEtMS43ODktMS4yNTRjLTIuNzUxLTIuNzgyLTEuODktNy4yNS0uNDU2LTguNjk2IDEuMTI3LTEuMTQgMy4xODEtLjUxNSA1LjA2Ni44NzdhLjI2LjI2IDAgMCAxIC4xMDIuMjVMOC4zNDYgNS4xOWwtLjEzLjA2YS4yNi4yNiAwIDAgMS0uMjc0LS4wNGMtMS41MDYtMS4yOS0zLjAwNy0xLjg0OC0zLjYzLTEuMjYtLjc2LjcxNi4wNzcgMi44NzQgMS44NzIgNC44MTggMS43OTQgMS45NDUgMy44NjQgMi45NCA0LjYyNSAyLjIyMi41ODctLjU1My4yMjItMS45NjctLjgwMS0zLjQ3N2wuMTIyLS4yMyAxLjE1NC0uMzc4YzEuMjA3IDEuOTA1IDEuNjM4IDQgLjU2IDUuMDktLjk0My45NTItMi45MzUgMS43MjItNC45NzMgMS4zNDF2MS4zN2gyLjQzMmEuNjQzLjY0MyAwIDAgMSAuNjQuNjQ3LjY0My42NDMgMCAwIDEtLjY0LjY0N2gtNS4xMlptMy4zMjMtOC4wMi0uMTQ0LS4xNDVhLjY5Mi42OTIgMCAwIDEgLjA1LTEuMDM5bDEuNTE2LTEuMjUzYS44NTkuODU5IDAgMCAxIC44NDgtLjEyNy43NzMuNzczIDAgMCAxLS4xNTUtLjQ2OS43NC43NCAwIDAgMSAuNzE2LS43NTguNzM5LjczOSAwIDAgMSAuNzE3Ljc1OC43MzcuNzM3IDAgMCAxLS43MTcuNzU3LjY4NS42ODUgMCAwIDEtLjQzNy0uMTU3Ljg4Ljg4IDAgMCAxLS4xMjYuODUybC0xLjI0IDEuNTNhLjY4NS42ODUgMCAwIDEtLjUzNS4yNjUuNjkuNjkgMCAwIDEtLjQ5My0uMjE0Wm02LjQ2LTIuMjhjMC0yLjU0LTEuOTQ4LTQuNjA3LTQuMzQzLTQuNjA3VjBDMTIuNTg4IDAgMTUgMi41NTcgMTUgNS42OTloLTEuMDMzWm0tMS44MjggMEEyLjczNSAyLjczNSAwIDAgMCAxMS40IDMuODFhMi40MjYgMi40MjYgMCAwIDAtMS43NzctLjc4M3YtMS4wOWMxLjk1OCAwIDMuNTUgMS42ODcgMy41NSAzLjc2MWgtMS4wMzVaIiBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L2c+PC9zdmc+"
},
{
"name": "国家水文站",
"count": 1189
,"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwaDE2djE2SDB6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNMyAxNCA4IDBsNSAxNHoiLz48L2c+PC9zdmc+"
interface IFacility {
name: string;
count: number | string;
orderInx: number;
icon: string;
unit?: string | null;
}
// orderInx SVG
const iconMap: Record<number, string> = {
1: svg_diwenshuijianhuan, //
2: svg_guojiashuizhizhan, //
4: svg_qixiangzhan, //
5: svg_guojiashuiwenzhan, //
6: svg_zijianshuiwenzhan, //
7: svg_liuliangjiancezhan, //
8: svg_shipinjiankongshebei, //
9: svg_shuizhijiancezhan, //
10: svg_zengzhizhanshuizhizhan, //
11: svg_qixidi //
};
const facilities = ref<IFacility[]>([]);
const loading = ref(false);
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
//
const modalVisible = ref(false);
const currentFacility = ref<IFacility | null>(null);
const fetchData = () => {
let params = {
filter: {
logic: 'and',
filters: [
{
field: 'showIds',
operator: 'in',
value: ['7', '1', '6', '9', '10', '2', '8', '4', '5']
},
baseid.value != 'all'
? {
field: 'baseId',
operator: 'eq',
value: baseid.value
}
: null
].filter(Boolean)
}
]);
};
loading.value = true;
baseEvnmAutoMonitorGetKendoListCust(params)
.then((res: any) => {
const data = res?.data?.data || [];
facilities.value = data.map((item: any) => ({
name: item.sttpName,
count: item.cnt,
orderInx: item.orderInx,
icon: iconMap[item.orderInx] || '',
unit: item.unit || '个'
}));
})
.catch(() => {
facilities.value = [];
})
.finally(() => {
loading.value = false;
});
};
//
onMounted(() => {
//
const handleFacilityClick = (facility: IFacility) => {
currentFacility.value = facility;
modalVisible.value = true;
});
};
//
const handleCloseModal = () => {
modalVisible.value = false;
currentFacility.value = null;
};
//
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
fetchData();
}
},
{ deep: true, immediate: true }
);
onMounted(() => {});
</script>
<style lang="scss" scoped>
.facility-grid {
// width: 406px;
flex-flow: wrap;
display: flex;
justify-content: space-between;
margin-top: -8px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
flex-flow: wrap;
display: flex;
justify-content: space-between;
margin-top: -8px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
}
.facility-card {
width: 200px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0px;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
transition: all 0.3s;
cursor: pointer;
box-sizing: border-box;
width: 200px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0px;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
transition: all 0.3s;
cursor: pointer;
box-sizing: border-box;
}
.facility-icon {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
// margin-right: 8px;
background: #2f6b98;
border-radius: 50%;
.anticon {
font-size: 24px;
color: #fff;
}
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: #2f6b98;
border-radius: 50%;
}
.facility-info {
flex: 1;
flex: 1;
}
.facility-name {
font-size: 16px;
color: #333;
// margin-bottom: 4px;
// font-weight: 500;
font-size: 16px;
color: #333;
}
.facility-count {
font-size: 18px;
color: #2f6b98;
// font-weight: 600;
font-size: 18px;
color: #2f6b98;
}
</style>

View File

@ -2,8 +2,10 @@
<template>
<div>
<SidePanelItem title="基本情况介绍" :shrink="true">
<p v-if="title_text" class="basic_body1">{{ title_text }}</p>
<div v-else class="zanwushujv"><a-empty /></div>
<a-spin :spinning="loading">
<p v-if="title_text" class="basic_body1">{{ title_text }}</p>
<div v-else class="zanwushujv"><a-empty /></div>
</a-spin>
</SidePanelItem>
</div>
</template>
@ -20,6 +22,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
defineOptions({
name: "jidiInfoMod",
});
const loading = ref(false);
const initText = () => {
const params = {
filter: {
@ -35,9 +38,12 @@ const initText = () => {
],
},
};
loading.value = true;
getBaseWbsb(params).then((res) => {
console.log(res);
title_text.value = res.data.data[0].introduce;
}).finally(() => {
loading.value = false;
});
};
const title_text = ref("");

View File

@ -1,84 +1,158 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="AI识别过鱼设施运行" :datetimePicker="datetimePicker">
<div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
<SidePanelItem title="AI识别过鱼设施运行" :datetimePicker="datetimePicker" @update-values="handlePanelChange">
<a-spin :spinning="dataLoading" tip="加载中...">
<div class="facility-grid">
<div v-for="facility in facilities" :key="facility.key" class="facility-card">
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
</div>
</div>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 14px;"> <span class="facility-count">{{ facility.count }}</span>
<span></span>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 14px;"> <span class="facility-count">{{ facility.count }}</span>
<span></span>
</div>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { ref, computed, watch } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getAiGyssList } from '@/api/zngj';
import dayjs from 'dayjs';
// 便
defineOptions({
name: 'zengzhizhanjiansheyunxing'
name: 'spdzwaijc'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
const panelDetail = ref<any>({});
const lyName = computed(() => baseid.value === '02' ? '升鱼斗运行' : '升鱼机运行');
// ==================== ====================
const datetimePicker = ref({
show: true,
value: `${new Date().getFullYear() - 1}`, //
value: dayjs().format('YYYY-MM'),
format: 'YYYY-MM',
picker: 'month' as const,
options: []
});
//
const facilities = ref([
// ==================== ====================
const facilities = computed(() => [
{
key: 'gys',
name: '赶鱼栅运行',
count: "32",
count: panelDetail.value?.fpGysCnt ?? 0,
icon: 'icon iconfont icon-ganyushan'
},
{
name: '升鱼斗运行',
count: '66',
key: 'syj',
name: lyName.value,
count: panelDetail.value?.fpSyjCnt ?? 0,
icon: 'icon iconfont icon-liuliangjiancezhan1'
},
{
key: 'agv',
name: 'AGV小车',
count: '0',
count: panelDetail.value?.fpAgvCnt ?? 0,
icon: 'icon iconfont icon-agvxiaoche'
},
{
key: 'yych',
name: '运鱼车运行',
count: '0',
count: panelDetail.value?.fpYychCnt ?? 0,
icon: 'icon iconfont icon-yunyuche'
},
{
key: 'yyc',
name: '运鱼船出港',
count: '32',
count: panelDetail.value?.fpYycCnt ?? 0,
icon: 'icon iconfont icon-yunyuchuan'
},
{
key: 'ydh',
name: '鱼道放流',
count: '0',
count: panelDetail.value?.fpYdhCnt ?? 0,
icon: 'icon iconfont icon-yudaofangliu'
}
]);
//
onMounted(() => {
//
// ==================== ====================
const handlePanelChange = (data: any) => {
console.log('当前所有控件状态:', data);
if (data.datetime) {
datetimePicker.value.value = data.datetime;
}
getCardData();
};
});
// ==================== ====================
const getCardData = async () => {
if (!datetimePicker.value.value || !baseid.value) return;
//
onUnmounted(() => {
dataLoading.value = true;
});
try {
const params = {
filter: {
logic: 'and',
filters: [
baseid.value !== 'all' ? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
} : null,
{
field: 'startTime',
operator: 'gte',
dataType: 'date',
value: dayjs(datetimePicker.value.value).startOf('month').format('YYYY-MM-DD 00:00:00')
},
{
field: 'endTime',
operator: 'lte',
dataType: 'date',
value: dayjs(datetimePicker.value.value).endOf('month').format('YYYY-MM-DD 23:59:59')
}
].filter(Boolean)
}
};
const res = await getAiGyssList(params);
panelDetail.value = res?.data ?? {};
} catch (error) {
console.error('获取统计数据失败:', error);
panelDetail.value = {};
} finally {
dataLoading.value = false;
}
};
// ==================== ====================
watch(
() => JidiSelectEventStore.selectedItem,
async newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getCardData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
@ -111,7 +185,6 @@ onUnmounted(() => {
justify-content: center;
width: 40px;
height: 40px;
// margin-right: 8px;
background: rgb(47, 107, 152);
border-radius: 50%;
@ -128,13 +201,14 @@ onUnmounted(() => {
.facility-name {
font-size: 16px;
color: #333;
// margin-bottom: 4px;
// font-weight: 500;
}
.facility-count {
font-size: 18px;
color: #2f6b98;
// font-weight: 600;
}
:deep(.ant-spin-nested-loading) {
min-height: 72px;
}
</style>

View File

@ -0,0 +1,289 @@
<template>
<div class="modal-content">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form" @finish="handleSearch">
<a-form-item label="所属基地">
<a-select
v-model:value="searchForm.baseId"
placeholder="请选择基地"
style="width: 180px"
allow-clear
show-search
:filter-option="filterOption"
:options="baseOptions"
/>
</a-form-item>
<a-form-item label="监测数据状态">
<a-select
v-model:value="searchForm.warnState"
placeholder="请选择状态"
style="width: 150px"
allow-clear
:options="warnStateOptions"
/>
</a-form-item>
<a-form-item label="测站名称">
<a-input
v-model:value="searchForm.stnm"
placeholder="请输入测站名称"
style="width: 180px"
allow-clear
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
<!-- <a-button style="margin-left: 8px" @click="handleReset">重置</a-button> -->
<a-tooltip title="导出(暂未实现)">
<a-button style="margin-left: 8px">导出</a-button>
</a-tooltip>
</a-form-item>
</a-form>
<!-- 表格 -->
<div class="table-wrapper">
<BasicTable
ref="tableRef"
:columns="columns"
:scroll-y="420"
:list-url="getStllGaojingDetail"
:search-params="tableSearchParams"
:transform-data="customTransform"
>
<template #action="{ record }">
<a-button
v-if="record.warnState === 2"
type="link"
size="small"
@click="opentan(record)"
>
查看详情
</a-button>
<span v-else style="color: #999">查看详情</span>
</template>
</BasicTable>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive, watch,onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { getStllGaojingDetail } from '@/api/zngj';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useModelStore } from '@/store/modules/model';
defineOptions({
name: 'ModeGaoJing'
});
const modelStore = useModelStore();
interface Props {
baseId: string;
warnState: string;
warnType: string;
sttpCode: string;
}
const props = withDefaults(defineProps<Props>(), {
baseId: '',
warnState: '',
warnType: 'QEC',
sttpCode: 'ENG'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const tableRef = ref();
//
const baseOptions = computed(() => {
return JidiSelectEventStore.jidiData.map((item: any) => ({
label: item.wbsName,
value: item.wbsCode
}));
});
//
const warnStateOptions = [
{ label: '全部', value: '' },
{ label: '正常', value: '1' },
{ label: '告警', value: '2' },
{ label: '无数据', value: '0' }
];
//
const searchForm = reactive({
baseId: props.baseId || '',
warnState: props.warnState || '',
stnm: ''
});
// props
watch(
() => [props.baseId, props.warnState],
([newBaseId, newWarnState]) => {
searchForm.baseId = newBaseId;
searchForm.warnState = newWarnState;
handleSearch();
}
);
//
const columns = [
{
title: '电站名称',
key: 'ennm',
dataIndex: 'ennm',
width: '150px',
customRender: ({ text, record }: any) => {
return text || record.stnm || '-';
}
},
{
title: '测站名称',
key: 'stnm',
dataIndex: 'stnm',
width: '150px'
},
{
title: '所属基地',
key: 'baseName',
dataIndex: 'baseName',
width: '150px'
},
{
title: '监测数据状态',
key: 'warnstateName',
dataIndex: 'warnstateName',
width: '120px'
},
{
title: '操作',
key: 'action',
width: '100px',
slots: { customRender: 'action' }
}
];
//
const tableSearchParams = computed(() => {
return {
sort: [
{ field: 'baseStepSort', dir: 'asc' },
{ field: 'rvcdStepSort', dir: 'asc' },
{ field: 'rstcdStepSort', dir: 'asc' },
{ field: 'siteStepSort', dir: 'asc' },
{ field: 'stnm', dir: 'asc' }
],
group: [],
select: [],
groupResultFlat: false
};
});
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// Select
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
//
const handleSearch = () => {
const filters: any[] = [];
//
if (searchForm.baseId && searchForm.baseId !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: searchForm.baseId
});
}
//
filters.push({
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: props.sttpCode
});
//
if (searchForm.warnState !== undefined && searchForm.warnState !== '') {
filters.push({
field: 'warnState',
operator: 'eq',
dataType: 'string',
value: searchForm.warnState
});
}
//
filters.push({
field: 'warnType',
operator: 'eq',
dataType: 'string',
value: props.warnType
});
//
if (searchForm.stnm) {
filters.push({
field: 'stnm',
operator: 'contains',
dataType: 'string',
value: searchForm.stnm
});
}
const filter = {
logic: 'and',
filters: filters
};
tableRef.value?.getList(filter);
};
//
const handleReset = () => {
searchForm.baseId = '';
searchForm.warnState = '';
searchForm.stnm = '';
handleSearch();
};
const opentan = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENG';
modelStore.title = record.ennm;
modelStore.params.stcd = record.rstcd;
};
//
// handleSearch();
onMounted(() => {
handleSearch();
});
</script>
<style lang="scss" scoped>
.modal-content {
display: flex;
flex-direction: column;
// height: 100%;
.search-form {
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.table-wrapper {
flex: 1;
}
}
</style>

View File

@ -2,49 +2,173 @@
<template>
<div>
<SidePanelItem title="生态流量低于限值告警情况">
<div class="body_box">
<div class="box" v-for="(item, index) in data" :key="index">
<div>
<div :class="item.icon" :style="{background:item.background}"></div>
<div class="title_text">{{ item.name }}</div>
<a-spin :spinning="dataLoading" tip="加载中...">
<div class="body_box">
<div class="box" v-for="(item, index) in data" :key="index" @click="handleBoxClick(item.warnState)" style="cursor: pointer;">
<div>
<div :class="item.icon" :style="{background:item.background}"></div>
<div class="title_text">{{ item.name }}</div>
</div>
<div><span class="text_num">{{ item.num }}</span><span></span></div>
</div>
<div><span class="text_num">{{ item.num }}</span><span></span></div>
</div>
</div>
</a-spin>
</SidePanelItem>
<!-- 详情弹窗 -->
<a-modal
v-model:open="isModalOpen"
title="生态流量低于限值告警统计"
width="90%"
:body-style="{ height: '600px', padding: '16px' }"
:footer="null"
@cancel="handleModalClose"
>
<ModeGaoJing
:base-id="baseid"
:warn-state="currentWarnState"
warn-type="QEC"
sttp-code="ENG"
/>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from "vue";
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import { getStllGaojingList } from '@/api/zngj';
import ModeGaoJing from './ModeGaoJing.vue';
// 便
defineOptions({
name: "jidiInfoMod",
});
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
const isModalOpen = ref(false);
const currentWarnState = ref('');
const data = ref([
{
name:'正常',
num:'447',
num:'0',
icon:'iconfont icon-shuidianzhan1 zhuangji',
background:'#78c300',
warnState:'1',
},
{
name:'告警',
num:'0',
icon:'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background:'#e55555',
warnState:'2',
},
{
name:'无数据',
num:'6',
num:'0',
icon:'iconfont icon-shuidianzhanWushujv shebeiLixian',
background:'#8d8d8d',
warnState:'0',
},
])
//
onMounted(() => { });
//
const handleBoxClick = (warnState: string) => {
currentWarnState.value = warnState;
isModalOpen.value = true;
};
//
const handleModalClose = () => {
isModalOpen.value = false;
};
// ==================== ====================
const getCardData = async () => {
if (!baseid.value) return;
dataLoading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
baseid.value !== 'all' ? {
field: 'base_Id',
operator: 'eq',
dataType: 'string',
value: baseid.value
} : null
].filter(Boolean)
}
};
const res = await getStllGaojingList(params);
const list = res?.data?.data || [];
// warnType === 'QEC'
const qecList = list.filter((item: any) => item.warnType === 'QEC');
// warnState
const result = {
normal: 0, // warnState = 1
abnormal: 0, // warnState = 2
noData: 0 // warnState = 0
};
qecList.forEach((item: any) => {
if (item.warnState === 0) result.noData = item.num;
else if (item.warnState === 1) result.normal = item.num;
else if (item.warnState === 2) result.abnormal = item.num;
});
//
data.value = [
{
name: '正常',
num: result.normal.toString(),
icon: 'iconfont icon-shuidianzhan1 zhuangji',
background: '#78c300',
warnState: '1'
},
{
name: '告警',
num: result.abnormal.toString(),
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background: '#e55555',
warnState: '2'
},
{
name: '无数据',
num: result.noData.toString(),
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
background: '#8d8d8d',
warnState: '0'
}
];
} catch (error) {
console.error('获取生态流量告警数据失败:', error);
} finally {
dataLoading.value = false;
}
};
// ==================== ====================
watch(
() => JidiSelectEventStore.selectedItem,
async newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getCardData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss">

View File

@ -0,0 +1,289 @@
<template>
<div class="modal-content">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form" @finish="handleSearch">
<a-form-item label="所属基地">
<a-select
v-model:value="searchForm.baseId"
placeholder="请选择基地"
style="width: 180px"
allow-clear
show-search
:filter-option="filterOption"
:options="baseOptions"
/>
</a-form-item>
<a-form-item label="监测数据状态">
<a-select
v-model:value="searchForm.warnState"
placeholder="请选择状态"
style="width: 150px"
allow-clear
:options="warnStateOptions"
/>
</a-form-item>
<a-form-item label="测站名称">
<a-input
v-model:value="searchForm.stnm"
placeholder="请输入测站名称"
style="width: 180px"
allow-clear
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
</a-form-item>
</a-form>
<!-- 表格 -->
<div class="table-wrapper">
<BasicTable
ref="tableRef"
:columns="columns"
:scroll-y="420"
:list-url="getStllGaojingDetail"
:search-params="tableSearchParams"
:transform-data="customTransform"
>
<template #action="{ record }">
<a-button
v-if="record.warnState === 2"
type="link"
size="small"
@click="opentan(record)"
>
查看详情
</a-button>
<span v-else style="color: #999">查看详情</span>
</template>
</BasicTable>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive, watch, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { getStllGaojingDetail } from '@/api/zngj';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useModelStore } from '@/store/modules/model';
defineOptions({
name: 'ModeGaoJing'
});
const modelStore = useModelStore();
interface Props {
baseId: string;
warnState: string;
dtinType: string;
}
const props = withDefaults(defineProps<Props>(), {
baseId: '',
warnState: '',
dtinType: '0'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const tableRef = ref();
//
const baseOptions = computed(() => {
return JidiSelectEventStore.jidiData.map((item: any) => ({
label: item.wbsName,
value: item.wbsCode
}));
});
//
const warnStateOptions = [
{ label: '全部', value: '' },
{ label: '正常', value: '1' },
{ label: '告警', value: '2' },
{ label: '无数据', value: '0' }
];
//
const searchForm = reactive({
baseId: props.baseId || '',
warnState: props.warnState || '',
stnm: ''
});
// props
watch(
() => [props.baseId, props.warnState],
([newBaseId, newWarnState]) => {
searchForm.baseId = newBaseId;
searchForm.warnState = newWarnState;
handleSearch();
}
);
//
const columns = [
{
title: '电站名称',
key: 'ennm',
dataIndex: 'ennm',
width: '150px',
customRender: ({ text, record }: any) => {
return text || record.stnm || '-';
}
},
{
title: '测站名称',
key: 'stnm',
dataIndex: 'stnm',
width: '150px'
},
{
title: '所属基地',
key: 'baseName',
dataIndex: 'baseName',
width: '150px'
},
{
title: '监测数据状态',
key: 'warnstateName',
dataIndex: 'warnstateName',
width: '120px'
},
{
title: '操作',
key: 'action',
width: '100px',
slots: { customRender: 'action' }
}
];
//
const tableSearchParams = computed(() => {
return {
sort: [
{ field: 'baseStepSort', dir: 'asc' },
{ field: 'rvcdStepSort', dir: 'asc' },
{ field: 'rstcdStepSort', dir: 'asc' },
{ field: 'siteStepSort', dir: 'asc' },
{ field: 'stnm', dir: 'asc' }
],
group: [],
select: [],
groupResultFlat: false
};
});
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// Select
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
//
const handleSearch = () => {
const filters: any[] = [];
//
if (searchForm.baseId && searchForm.baseId !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: searchForm.baseId
});
}
// WQ
filters.push({
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: ['WQ']
});
//
if (searchForm.warnState !== undefined && searchForm.warnState !== '') {
filters.push({
field: 'warnState',
operator: 'eq',
dataType: 'string',
value: searchForm.warnState
});
}
// dtinType
if (props.dtinType === '') {
filters.push({
field: 'dtinType',
operator: 'in',
dataType: 'number',
value: [0, 1]
});
} else {
filters.push({
field: 'dtinType',
operator: 'eq',
dataType: 'number',
value: Number(props.dtinType)
});
}
// mway 2
filters.push({
field: 'mway',
operator: 'eq',
dataType: 'number',
value: 2
});
//
if (searchForm.stnm) {
filters.push({
field: 'stnm',
operator: 'contains',
dataType: 'string',
value: searchForm.stnm
});
}
const filter = {
logic: 'and',
filters: filters
};
tableRef.value?.getList(filter);
};
const opentan = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENG';
modelStore.title = record.ennm;
modelStore.params.stcd = record.rstcd;
};
onMounted(() => {
handleSearch();
});
</script>
<style lang="scss" scoped>
.modal-content {
display: flex;
flex-direction: column;
.search-form {
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.table-wrapper {
flex: 1;
}
}
</style>

View File

@ -1,57 +1,236 @@
<!-- SidePanelItem.vue -->
<template>
<div>
<SidePanelItem title="水质监测数据告警情况" :select="select" >
<div class="body_box">
<div class="box" v-for="(item, index) in data" :key="index">
<div>
<div :class="item.icon" :style="{background:item.background}"></div>
<div class="title_text">{{ item.name }}</div>
<SidePanelItem title="水质监测数据告警情况" :select="select" @update-values="handlePanelChange">
<a-spin :spinning="dataLoading" tip="加载中...">
<div class="body_box">
<div class="box" v-for="(item, index) in data" :key="index" @click="handleCardClick(item.warnState)" style="cursor: pointer;">
<div>
<div :class="item.icon" :style="{background:item.background}"></div>
<div class="title_text">{{ item.name }}</div>
</div>
<div><span class="text_num">{{ item.num }}</span><span></span></div>
</div>
<div><span class="text_num">{{ item.num }}</span><span></span></div>
</div>
</div>
</a-spin>
</SidePanelItem>
<a-modal
v-model:open="modalVisible"
title="水质监测告警统计"
width="1536px"
:footer="null"
@cancel="handleModalClose"
>
<ModeGaoJing
v-if="modalVisible"
:baseId="baseid"
:warnState="currentWarnState"
:dtinType="defaultValue"
/>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from "vue";
import { ref, watch } from "vue";
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import { getShuizhiGaojingList } from '@/api/zngj';
import ModeGaoJing from './ModeGaoJing.vue';
// 便
defineOptions({
name: "jidiInfoMod",
name: "shuizhijianceshuju",
});
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
const defaultValue = ref<string>('0');
//
const modalVisible = ref(false);
const currentWarnState = ref('');
const handleCardClick = (warnState: string) => {
currentWarnState.value = warnState;
modalVisible.value = true;
};
const handleModalClose = () => {
modalVisible.value = false;
};
const select = ref({
show: true,
value: undefined,
options: [],
picker: undefined,
format: undefined
value: '0',
options: [
{ label: '全部', value: '' },
{ label: '国家站', value: '1' },
{ label: '自建站', value: '0' },
],
width: '140px',
minWidth: '140px',
});
const data = ref([
{
name:'正常',
num:'56',
icon:'iconfont icon-shuidianzhan1 zhuangji',
background:'#78c300',
name: '正常',
num: '0',
warnState: '1',
icon: 'iconfont icon-shuidianzhan1 zhuangji',
background: '#78c300',
},
{
name:'告警',
num:'0',
icon:'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background:'#e55555',
{
name: '告警',
num: '0',
warnState: '2',
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background: '#e55555',
},
{
name:'无数据',
num:'0',
icon:'iconfont icon-shuidianzhanWushujv shebeiLixian',
background:'#8d8d8d',
{
name: '无数据',
num: '0',
warnState: '0',
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
background: '#8d8d8d',
},
])
//
onMounted(() => { });
]);
const handlePanelChange = (payload: any) => {
if (payload.select !== undefined) {
defaultValue.value = payload.select;
select.value.value = payload.select;
}
getCardData();
};
const getCardData = async () => {
if (!baseid.value) return;
dataLoading.value = true;
try {
const filters: any[] = [
{
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: ['WQ'],
},
{
field: 'mway',
operator: 'eq',
dataType: 'number',
value: 2,
},
{
field: 'usfl',
operator: 'eq',
dataType: 'string',
value: '1',
},
baseid.value !='all'? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value,
}: null,
].filter(Boolean);
if (defaultValue.value === '') {
filters.push({
field: 'dtinType',
operator: 'in',
dataType: 'number',
value: [0, 1],
});
} else {
filters.push({
field: 'dtinType',
operator: 'eq',
dataType: 'number',
value: Number(defaultValue.value),
});
}
const params = {
filter: {
logic: 'and',
filters,
},
group: [
{
dir: 'desc',
field: 'warnState',
aggregates: [
{
field: 'stcd',
aggregate: 'count',
},
],
},
],
select: [],
};
const res = await getShuizhiGaojingList(params);
const list = res?.data?.data || [];
// debugger
const result = {
normal: 0,
abnormal: 0,
noData: 0,
};
list.forEach((item: any) => {
const warnState = item.items?.[0]?.WARNSTATE;
const count = item.items?.[0]?.COUNT_STCD ?? 0;
if (warnState == 0) result.noData = count;
else if (warnState == 1) result.normal = count;
else if (warnState == 2) result.abnormal = count;
});
data.value = [
{
name: '正常',
num: result.normal.toString(),
warnState: '1',
icon: 'iconfont icon-shuidianzhan1 zhuangji',
background: '#78c300',
},
{
name: '告警',
num: result.abnormal.toString(),
warnState: '2',
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background: '#e55555',
},
{
name: '无数据',
num: result.noData.toString(),
warnState: '0',
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
background: '#8d8d8d',
},
];
} catch (error) {
console.error('获取水质监测告警数据失败:', error);
} finally {
dataLoading.value = false;
}
};
watch(
() => JidiSelectEventStore.selectedItem,
async newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getCardData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss">

View File

@ -19,6 +19,7 @@
show-search
:filter-option="filterOption"
:options="rstcdOptions"
allowClear
/>
</a-form-item>
@ -29,54 +30,61 @@
</a-form-item>
</a-form>
<!-- Tabs 标签页 -->
<a-tabs
v-if="tabList.length > 0"
v-model:activeKey="ecoFlowType"
class="tab-style"
@change="fetchTableData"
>
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.label" />
</a-tabs>
<div class="body_zhengti">
<div class="echarts">
<a-spin :spinning="dataLoading">
<div v-if="pieData.length > 0" ref="chartRef" class="pie-chart"></div>
<a-empty
v-if="!dataLoading && pieData.length === 0"
description="暂无数据"
/>
</a-spin>
</div>
<div class="table_1">
<BasicTable
ref="tableRef"
:columns="columns"
:scrollY="380"
:scrollX="100"
:list-url="vmsstbprptGetKendoList"
:search-params="searchParams"
:transform-data="customTransform"
<a-spin :spinning="bodyLoading">
<template v-if="tabList.length > 0">
<!-- Tabs 标签页 -->
<a-tabs
v-model:activeKey="ecoFlowType"
class="tab-style"
@change="fetchTableData"
>
<template #stnm="{ record }">
<a-button
type="link"
size="small"
@click="handleView(record, 'EQ')"
>{{ record.stnm }}</a-button
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.label" />
</a-tabs>
<div class="body_zhengti">
<div class="echarts">
<a-spin :spinning="dataLoading">
<div v-if="pieData.length > 0" ref="chartRef" class="pie-chart"></div>
<a-empty
v-if="!dataLoading && pieData.length === 0"
description="暂无数据"
/>
</a-spin>
</div>
<div class="table_1">
<BasicTable
ref="tableRef"
:columns="columns"
:scrollY="380"
:scrollX="100"
:list-url="vmsstbprptGetKendoList"
:search-params="searchParams"
:transform-data="customTransform"
>
</template>
<template #ennm="{ record }">
<a-button
type="link"
size="small"
@click="handleView(record, 'ENG')"
>{{ record.ennm }}</a-button
>
</template>
</BasicTable>
</div>
</div>
<template #stnm="{ record }">
<a-button
type="link"
size="small"
@click="handleView(record, 'EQ')"
>{{ record.stnm }}</a-button
>
</template>
<template #ennm="{ record }">
<a-button
type="link"
size="small"
@click="handleView(record, 'ENG')"
>{{ record.ennm }}</a-button
>
</template>
</BasicTable>
</div>
</div>
</template>
<a-empty
v-else-if="!bodyLoading"
description="暂无数据"
/>
</a-spin>
</div>
</template>
@ -294,8 +302,6 @@ const searchParams = computed(() => {
};
});
;
const params = ref({
qid: props.basicId || 'all',
rstcd: ''
@ -309,6 +315,7 @@ const activeETime = ref('');
const tabList = ref<any[]>([]);
const ecoFlowType = ref<string>(props.sttpCodekey || '');
const loading = ref<boolean>(false);
const bodyLoading = ref<boolean>(false);
const jidiOptions = computed(() => {
return [
@ -589,6 +596,7 @@ const customTransform = (res: any) => {
// Tab
const fetchTabList = async () => {
loading.value = true;
bodyLoading.value = true;
try {
const { qid, rstcd } = params.value;
let obj = {
@ -644,6 +652,8 @@ const fetchTabList = async () => {
// ecoFlowType.value = tabList.value[0].key;
// DOM
await nextTick();
//
fetchPieData();
//
@ -656,6 +666,7 @@ const fetchTabList = async () => {
tabList.value = [];
} finally {
loading.value = false;
bodyLoading.value = false;
}
};
const handleView = (record: any, type: 'ENG' | 'EQ') => {
@ -731,6 +742,17 @@ watch(ecoFlowType, () => {
fetchPieData();
});
// tabList
watch(
() => tabList.value.length,
(newLen) => {
if (newLen === 0 && chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
}
);
const handleSearch = () => {
// Tab
fetchTabList();

View File

@ -29,7 +29,7 @@
ref="tableRef"
:columns="columns"
:scrollY="480"
:list-url="vmsstbprptGetKendoList"
:list-url="vmsstbprptGetKendoList1"
:search-params="searchParams"
:transform-data="customTransform"
>
@ -59,7 +59,7 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick, h } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { vmsstbprptGetKendoList } from '@/api/stll';
import { vmsstbprptGetKendoList1 } from '@/api/stll';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
@ -275,7 +275,7 @@ const handleChange = async () => {
],
select: ['stcd', 'stnm']
};
let res = await vmsstbprptGetKendoList(params);
let res = await vmsstbprptGetKendoList1(params);
if (res?.data?.data) {
let data = res.data.data;
stcdOptions.value = data.map((item: any) => ({

View File

@ -111,6 +111,7 @@ const props = defineProps<{
typeKey?: string;
time?: string;
basicId?: string;
dateArr?: any[];
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
@ -193,8 +194,11 @@ const searchParams = computed(() => {
sort: baseSort
};
});
//
// 使 dateArr使
const initDateRange = () => {
if (props.dateArr && props.dateArr.length === 2) {
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
}
const today = dayjs();
const lastMonthSameDay = today.subtract(1, 'month');
return [lastMonthSameDay, today];

View File

@ -77,6 +77,7 @@
:typeKey="selectedItem.key"
:time="mode"
:basicId="baseid"
:dateArr="dateArr"
/>
</a-modal>
@ -100,6 +101,7 @@ import {
evnmAutoMonitorGetKendoListCust
} from '@/api/stll';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
import STLLXFFS from './TwoLayer/ShengTaiLiuLiangDaBQKTwoLayer.vue';
import ModalYkzhbzdjcgz from './TwoLayer/ModalYkzhbzdjcgz.vue';
import HuanbaoZDJCGZKZ from '@/modules/huanbaozdjcgzkzQK/index.vue';
@ -108,7 +110,9 @@ defineOptions({
name: 'shengtaidabiaoMod'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const mapViewStore = useMapViewStore();
const baseid = ref('');
const dateArr = ref<any[]>([]);
const obj = {
show: true,
value:
@ -119,15 +123,15 @@ const mode = ref('hour');
const dataArr: any = ref([
{
name: '生态环境部门要求',
qecCnt: '41', //
qecTCnt: '55', //
qecCnt: '0', //
qecTCnt: '0', //
type: 1,
key: 'qecInterval'
},
{
name: '水利部门要求',
qecCnt: '41', //
qecTCnt: '55', //
qecCnt: '0', //
qecTCnt: '0', //
type: 2,
key: 'mwrInterval'
}
@ -181,13 +185,7 @@ const setStyle = () => {
const dataChage = () => {
getData();
};
const getData = async () => {
// 00:00:00 23:59:59
const now = new Date();
const oneMonthAgo = new Date(now);
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
// YYYY-MM-DD
const getdate = (offsetYears = 0) => {
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
@ -195,8 +193,35 @@ const getData = async () => {
return `${year}-${month}-${day}`;
};
const startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
const endDate = `${formatDate(now)} 23:59:59`;
let startDate: string;
let endDate: string;
if (dateArr.value && dateArr.value.length === 2) {
const start = new Date(dateArr.value[0]);
const end = new Date(dateArr.value[1]);
if (offsetYears !== 0) {
start.setFullYear(start.getFullYear() + offsetYears);
end.setFullYear(end.getFullYear() + offsetYears);
}
startDate = `${formatDate(start)} 00:00:00`;
endDate = `${formatDate(end)} 23:59:59`;
} else {
const now = new Date();
const oneMonthAgo = new Date(now);
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
if (offsetYears !== 0) {
now.setFullYear(now.getFullYear() + offsetYears);
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
}
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
endDate = `${formatDate(now)} 23:59:59`;
}
return { startDate, endDate };
};
const getData = async () => {
const { startDate, endDate } = getdate(0);
let params = {
filter: {
@ -279,6 +304,17 @@ watch(
},
{ deep: true, immediate: true }
);
watch(
() => mapViewStore.searchTimeRange,
(newVal) => {
dateArr.value = newVal;
getData();
},
{ deep: true }
);
//
const titleData:any = ref({ cnt: 0 })
const getcont = async () => {

View File

@ -0,0 +1,386 @@
<template>
<div class="stlldbqk_container">
<div class="stlldbqk_title">
<div>选择水电站:</div>
<a-select
v-model:value="from.baseId"
show-search
placeholder=" "
:options="jidiOptions"
:filter-option="filterOption"
@change="handleChange"
style="width: 200px; margin: 0px 10px"
></a-select>
<a-select
v-model:value="from.stcd"
show-search
placeholder="请选择电站"
:options="stcdOptions"
:filter-option="filterOption"
style="width: 200px"
></a-select>
<a-button type="primary" style="margin: 0px 10px" @click="Search()"
>搜索</a-button
>
<a-button @click="resetSearch()">重置</a-button>
</div>
<div class="table">
<BasicTable
ref="tableRef"
:columns="columns"
:scrollY="480"
:list-url="vmsstbprptGetKendoList1"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #dvtp="{ record }">
{{
record.dvtp == 2 ? '引水式' : record.dvtp == 3 ? '混合式' : '坝后式'
}}
</template>
<template #stindx="{ record }">
{{ record.dvtp == 2 ? '生态流量' : '出库流量' }}
</template>
<template #coenvwState="{ column, record }">
<a-tag :color="record.coenvwState > 0 ? 'success' : ''">
{{ record.coenvwState > 0 ? '在线' : '离线' }}
</a-tag>
</template>
<template #action="{ column, record }">
<a-button type="link" size="small" @click="handleView(record)"
>查看详情</a-button
>
</template>
</BasicTable>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick, h } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { vmsstbprptGetKendoList1 } from '@/api/stll';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
const modelStore = useModelStore();
const props = defineProps<{
baseId?: any;
titleData?: any;
}>();
const tableRef = ref();
const columns = ref([
{
title: '所属基地',
key: 'baseName',
dataIndex: 'baseName',
merge: true, //
visible: true
},
{
title: '电站名称',
key: 'stnm',
dataIndex: 'stnm',
visible: true
},
{
title: '开发方式',
key: 'dvtp',
dataIndex: 'dvtp',
visible: true,
slots: { customRender: 'dvtp' }
},
{
title: '生态流量站类型',
key: 'stindx',
dataIndex: 'stindx',
visible: true,
slots: { customRender: 'stindx' }
},
{
title: '建成时间',
key: 'jcdt',
dataIndex: 'jcdt',
visible: true,
sort: true,
customRender: ({ record }: any) => {
const date = record.jcdt;
if (!date) return '-';
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
return date;
}
if (typeof date === 'string' && date.includes(' ')) {
return date.split(' ')[0];
}
return date;
}
},
{
title: '监测状态',
key: 'coenvwState',
dataIndex: 'coenvwState',
visible: true,
slots: { customRender: 'coenvwState' }
},
{
title: '操作',
key: 'action',
width: 120,
slots: { customRender: 'action' }
}
]);
const sort = {
sort: [
{
field: 'baseStepSort',
dir: 'asc'
},
{
field: 'hbrvcd',
dir: 'asc'
},
{
field: 'rstcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
}
],
group: [],
select: [
'baseName',
'stnm',
'ennm',
'jcdt',
'stindx',
'coenvwState',
'sttpCode',
'stcd',
'rstcd',
'stCode',
'stName',
'dvtp'
],
groupResultFlat: false
};
const JidiSelectEventStore = useJidiSelectEventStore();
// JidiSelectEventStore.jidiData
let jidiOptions = computed(() => {
return [
...JidiSelectEventStore.jidiData.map((item: any) => ({
value: item.wbsCode,
label: item.wbsName
}))
];
});
const from = ref({
baseId: props.baseId || 'all',
stcd: ''
});
//
const searchParams = computed(() => {
let baseSort;
//
baseSort = [
{ field: 'baseStepSort', dir: 'asc' },
{ field: 'hbrvcd', dir: 'asc' },
{ field: 'rstcdStepSort', dir: 'asc' },
{ field: 'siteStepSort', dir: 'asc' }
];
return {
sort: baseSort,
group: [],
select: [
'baseName',
'stnm',
'ennm',
'jcdt',
'stindx',
'coenvwState',
'sttpCode',
'stcd',
'rstcd',
'stCode',
'stName',
'dvtp'
],
groupResultFlat: false
};
});
// props.baseId from.value.baseId
watch(
() => props.baseId,
newBaseId => {
//
if (newBaseId && newBaseId !== from.value.baseId) {
from.value.baseId = newBaseId;
from.value.stcd = ''; //
// DOM
nextTick(() => {
handleChange(); //
Search(); //
});
}
},
{ immediate: false }
);
const filterOption = (input: string, option: any) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const stcdOptions = ref([]);
const handleChange = async () => {
try {
let params = {
filter: {
logic: 'and',
filters: [
from.value.baseId != 'all'
? {
field: 'baseId',
operator: 'contains',
dataType: 'string',
value: from.value.baseId
}
: null,
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'ENG'
}
].filter(Boolean)
},
sort: [
{
field: 'baseId',
dir: 'asc'
},
{
field: 'rvcdStepSort',
dir: 'asc'
},
{
field: 'rstcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
}
],
select: ['stcd', 'stnm']
};
let res = await vmsstbprptGetKendoList1(params);
if (res?.data?.data) {
let data = res.data.data;
stcdOptions.value = data.map((item: any) => ({
value: item.stcd,
label: item.stnm
}));
} else {
stcdOptions.value = [];
}
} catch (error) {
console.error('获取电站列表失败:', error);
stcdOptions.value = [];
}
};
const Search = () => {
let filters = [
from.value.stcd
? {
field: 'stcd',
operator: 'eq',
value: from.value.stcd
}
: null,
from.value.baseId != 'all'
? {
field: 'baseId',
operator: 'eq',
value: from.value.baseId
}
: null,
{
field: 'runState',
operator: 'eq',
value: 4
},
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: props.titleData?.sttpCode || 'ENG'
},
{
field: 'DTIN',
operator: 'eq',
dataType: 'string',
value: 1
},
{
field: 'bldsttCcode',
operator: 'eq',
dataType: 'string',
value: 2
}
].filter(Boolean);
const filter = {
logic: 'and',
filters
};
tableRef.value?.getList(filter);
};
const resetSearch = () => {
from.value = {
baseId: 'all',
stcd: ''
};
Search();
};
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
const handleView = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENG';
modelStore.title = record.stnm;
modelStore.params.stcd = record.stcd;
modelStore.params.eqtp = 'QGC';
modelStore.filter.stllgzlx = 'qecInterval';
modelStore.filter.stllTmType = 'day';
};
onMounted(() => {
handleChange();
Search();
});
onUnmounted(() => {});
</script>
<style scoped lang="scss">
.stlldbqk_container {
width: 100%;
.stlldbqk_title {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 10px;
}
.table {
width: 100%;
height: 560px;
}
}
</style>

View File

@ -0,0 +1,939 @@
<template>
<div class="stlldbqk-container">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form">
<a-form-item label="基地选择">
<a-select
v-model:value="params.qid"
placeholder="请选择基地"
style="width: 200px"
show-search
:filter-option="filterOption"
:options="jidiOptions"
/>
</a-form-item>
<a-form-item label="规则类型">
<a-select
v-model:value="params.key"
placeholder="请选择规则类型"
style="width: 180px"
:options="ruleTypeOptions"
/>
</a-form-item>
<a-form-item label="时间维度">
<a-select
v-model:value="params.timeType"
placeholder="请选择时间维度"
style="width: 120px"
:options="timeTypeOptions"
/>
</a-form-item>
<a-form-item label="日期范围">
<a-range-picker
v-model:value="dateRange"
:allow-clear="false"
format="YYYY-MM-DD"
style="width: 260px"
:presets="DateSetting.RangeButton.days1"
/>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">查询</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- Tabs 标签页 -->
<a-tabs
v-if="tabList.length > 0"
v-model:activeKey="ecoFlowType"
class="tab-style"
@change="fetchTableData"
>
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.label" />
</a-tabs>
<!-- 空状态 -->
<div v-if="!loading && tabList.length === 0" class="empty-state">
<a-empty description="暂无数据" />
</div>
<div v-show="tabList.length > 0" class="body_zhengti">
<div class="echarts">
<a-spin :spinning="dataLoading">
<div v-if="pieData.length > 0" ref="chartRef" class="pie-chart"></div>
<a-empty
v-if="!dataLoading && pieData.length === 0"
description="暂无数据"
/>
</a-spin>
</div>
<div class="table1">
<BasicTable
ref="tableRef"
:columns="columns"
:scrollY="400"
:list-url="
activeTimeType == 'day' ? dayGetKendoListCust : hourGetKendoListCust
"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #ennm="{ column, record }">
<a-button type="link" size="small" @click="handleView(record)">{{
record.ennm
}}</a-button>
</template>
</BasicTable>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
import dayjs from 'dayjs';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { dayGetKendoListCust, hourGetKendoListCust } from '@/api/stll';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
import { DateSetting } from '@/utils/enumeration';
const modelStore = useModelStore();
const props = defineProps<{
options?: any[];
typeKey?: string;
time?: string;
basicId?: string;
dateArr?: any[];
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const columns = [
{
key: 'ennm',
title: '电站名称',
dataIndex: 'ennm',
slots: { customRender: 'action' }
},
{
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName'
},
{
key: 'addvcdName',
title: '行政区',
dataIndex: 'addvcdName'
},
{
key: 'hbrvcdName',
title: '所在河段',
dataIndex: 'hbrvcdName',
width: '400px'
},
{
key: 'qecRate',
title: `生态流量达标率(%)`,
dataIndex: 'qecRate',
sort: true, //
width: '160px',
customRender: ({ text }: any) => {
if (text === null || text === undefined || text === '' || text === '-')
return '-';
return Number(text).toFixed(2);
}
}
];
const tableRef = ref();
const sort1 = ref([
{
field: 'baseStepSort',
dir: 'asc'
},
{
field: 'rvcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
},
{
field: 'avqRate',
dir: 'desc'
}
]);
const sort2 = ref([
{
field: 'baseStepSort',
dir: 'asc'
},
{
field: 'rvcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
}
]);
//
const searchParams = computed(() => {
let baseSort;
baseSort = params.value.key ? sort1.value : sort2.value;
return {
sort: baseSort
};
});
// 使 dateArr使
const initDateRange = () => {
if (props.dateArr && props.dateArr.length === 2) {
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
}
const today = dayjs();
const lastMonthSameDay = today.subtract(1, 'month');
return [lastMonthSameDay, today];
};
const dateRange = ref(initDateRange());
const params = ref({
qid: props.basicId || 'all',
key: props.typeKey || '',
timeType: props.time || 'day',
sTime: '',
eTime: ''
});
// 使 timeTypekey handleView
const activeTimeType = ref(props.time || 'day');
const activeKey = ref(props.typeKey || '');
const activeSTime = ref('');
const activeETime = ref('');
// Tab
const tabList = ref<any[]>([]);
const ecoFlowType = ref<string>('');
const loading = ref<boolean>(false);
const timeTypeOptions = [
{ label: '逐时', value: 'hour' },
{ label: '日', value: 'day' }
];
const jidiOptions = computed(() => {
return [
...JidiSelectEventStore.jidiData.map((item: any) => ({
value: item.wbsCode,
label: item.wbsName
}))
];
});
const ruleTypeOptions = computed(() => {
return (
props.options?.map((el: any) => ({
label: el.name,
value: el.key
})) || []
);
});
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
// ==================== ====================
const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const pieData = ref<any[]>([]); //
const dataLoading = ref<boolean>(false);
const pieCode = ref<string[]>([]); //
//
const themeecLists = [];
const jdColor = [
'#4b79ab',
'#78c300',
'#9556a4',
'#df91ab',
'#7399c6',
'#dbb629',
'#56c2e3',
'#f56a06',
'#cdba75',
'#76523b',
'#df75b6',
'#00a050',
'#f5a644',
'#4b79ab',
'#78c300',
'#9556a4',
'#df91ab',
'#7399c6',
'#dbb629',
'#56c2e3',
'#f56a06',
'#cdba75',
'#76523b',
'#df75b6',
'#00a050',
'#f5a644'
];
//
const fetchPieData = async () => {
if (!ecoFlowType.value || !params.value.sTime || !params.value.eTime) return;
dataLoading.value = true;
try {
const { sTime, eTime, qid, key, timeType } = params.value;
//
const filters = [
qid != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: qid
}
: null,
{
field: 'dtin',
operator: 'eq',
dataType: 'string',
value: '1'
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: sTime
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: eTime
},
key
? {
field: key,
operator: 'eq',
dataType: 'string',
value: ecoFlowType.value
}
: null
].filter(Boolean);
// key
if (!key) {
filters.push(
{
field: 'qecInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'mwrInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'avqInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
}
);
}
let obj: any = {
filter: {
logic: 'and',
filters: filters
},
group: [
qid !== 'all' ? { dir: 'asc', field: 'rvcdStepSort' } : null,
qid === 'all' ? { dir: 'asc', field: 'baseStepSort' } : null,
{
dir: 'asc',
field: qid === 'all' ? 'baseId' : 'hbrvcd'
},
qid !== 'all'
? { dir: 'asc', field: 'hbrvcdName' }
: { dir: 'asc', field: 'baseName' }
].filter(Boolean),
groupResultFlat: true
};
// TODO: API
// const res = timeType == 'day'
// ? await shengTaiLiuLiang.STLLTabsDay(obj)
// : await shengTaiLiuLiang.STLLTabsHour(obj);
let res =
timeType == 'day'
? await dayGetKendoListCust(obj)
: await hourGetKendoListCust(obj);
if (res?.data?.data) {
const dataSource = res.data.data.map((el: any) => ({
id: qid === 'all' ? el.baseId : el.hbrvcd,
name: qid === 'all' ? el.baseName || el.key : el.hbrvcdName,
value: qid === 'all' ? el.count_baseId : el.count_hbrvcd,
key: qid === 'all' ? el.baseId : el.hbrvcd
}));
pieData.value = dataSource;
pieCode.value = []; //
// DOM
await nextTick();
initOrUpdateChart();
} else {
pieData.value = [];
}
} catch (error) {
console.error('获取饼图数据失败:', error);
pieData.value = [];
} finally {
dataLoading.value = false;
}
};
//
const initOrUpdateChart = () => {
if (!chartRef.value) return;
//
if (!chartInstance) {
chartInstance = echarts.init(chartRef.value);
//
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
}
updateChart();
};
//
const updateChart = () => {
if (!chartInstance) return;
const total =
pieData.value.reduce((pre: number, cur: any) => pre + cur.value, 0) || 0;
//
const colors = pieData.value.map((item: any, index: number) => {
const matched = themeecLists.find((i: any) => i.name === item.name);
return matched?.color || jdColor[index % jdColor.length];
});
const option: EChartsOption = {
color: colors,
tooltip: {
trigger: 'item',
formatter: '{b}: {c} (座) ({d}%)'
},
legend: {
orient: 'vertical',
right: 20,
top: 80,
type: 'scroll',
textStyle: {
width: 100, //
overflow: 'truncate', //
ellipsis: '...' //
}
},
title: [
{
text: total.toString(),
left: '34%',
top: '44%',
textAlign: 'center',
textStyle: {
fontSize: 24,
fontWeight: 'bold',
color: '#333'
}
},
{
text: '电站总数量',
left: '34%',
top: '51%',
textAlign: 'center',
textStyle: {
fontSize: 14,
color: '#666'
}
},
{
text: '(座)',
left: '34%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: 12,
color: '#999'
}
}
],
series: [
{
name: '电站总数量',
type: 'pie',
radius: ['31%', '40%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: function (params) {
const maxLen = 8; //
let name = params.name;
if (name.length > maxLen) {
return name.substring(0, maxLen) + '...';
}
return name;
}
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
},
data: pieData.value
}
]
};
chartInstance.setOption(option, true);
};
//
const handleLegendSelectChanged = (params: any) => {
const selected = params.selected;
const trueRivers = Object.keys(selected).filter(key => selected[key]);
const allFalse = Object.values(selected).every(value => value === false);
const selectedKeys = pieData.value
.filter((item: any) => trueRivers.includes(item.name))
.map((item: any) => item.key);
pieCode.value = allFalse ? ['0'] : selectedKeys;
//
fetchTableData();
};
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// Tab
const fetchTabList = async () => {
if (!params.value.sTime || !params.value.eTime) return;
loading.value = true;
try {
const { sTime, eTime, qid, key, timeType } = params.value;
//
const filters = [
qid != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: qid
}
: null,
{
field: 'dtin',
operator: 'eq',
dataType: 'string',
value: '1'
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: sTime
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: eTime
}
].filter(Boolean);
// key
if (!key) {
filters.push(
{
field: 'qecInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'mwrInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'avqInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
}
);
}
let obj: any = {
filter: {
logic: 'and',
filters: filters
},
group: key
? [
{
dir: 'asc',
field: key
}
]
: [
{
dir: 'asc',
field: 'baseStepSort'
},
{
dir: 'asc',
field: 'baseId'
},
{
dir: 'asc',
field: 'baseName'
}
]
};
// key groupResultFlat
if (!key) {
obj.groupResultFlat = true;
}
// TODO: API
let res =
timeType == 'day'
? await dayGetKendoListCust(obj)
: await hourGetKendoListCust(obj);
// const res = timeType == 'day'
// ? await shengTaiLiuLiang.STLLTabsDay(par, group) dayGetKendoListCust,hourGetKendoListCust
// : await shengTaiLiuLiang.STLLTabsHour(par, group);
// API
// const res = {
// data: [
// {
// type: 1,
// qecCnt: 14,
// qecTCnt: 50,
// code: 'QEC_STLL_1_DY95',
// desc: '_:>=95%',
// typeName: '',
// key: 'QEC_STLL_1_DY95',
// keyName: '',
// count: 14
// },
// {
// type: 2,
// qecCnt: 13,
// qecTCnt: 71,
// code: 'QEC_STLL_2_DY95',
// desc: '_:>=95%',
// typeName: '',
// key: 'QEC_STLL_2_DY95',
// keyName: '',
// count: 13
// }
// ]
// };
if (res?.data?.data?.length > 0) {
const resData = res.data.data;
if (key) {
// ""
tabList.value = resData
.map((el: any) => ({
...el,
value: el.key,
label: `${el.keyName || el.typeName}(${el.count || el.qecCnt}座)`
}))
.filter((el: any) => el.key !== 'QEC_STLL_6_NOEQMN');
// ecoFlowType
const found = resData.find(
(el: any) => el.items?.[0] === ecoFlowType.value
);
// 使
ecoFlowType.value = found ? ecoFlowType.value : resData[0]?.key;
} else {
// ""
tabList.value = [
{
key: 'QEC_STLL_6_NOEQMN',
value: 'QEC_STLL_6_NOEQMN',
label: `无生态流量要求(${resData?.length}座)`
}
];
ecoFlowType.value = 'QEC_STLL_6_NOEQMN';
}
//
fetchPieData();
//
fetchTableData();
} else {
tabList.value = [];
}
} catch (error) {
console.error('获取 Tab 列表失败:', error);
tabList.value = [];
} finally {
loading.value = false;
}
};
const handleView = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENGJJ';
modelStore.title = record.ennm;
modelStore.params.stcd = record.stcd;
modelStore.params.eqtp = record.eqtp;
modelStore.filter.stllgzlx = activeKey.value;
modelStore.filter.stllTmType = activeTimeType.value;
modelStore.filter.rangeTm = [activeSTime.value, activeETime.value];
};
//table
const fetchTableData = async () => {
//
const { sTime, eTime, qid, key, timeType } = params.value;
const filters: any[] = [
qid != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: qid
}
: null,
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: sTime
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: eTime
},
{
field: 'dtin',
operator: 'eq',
dataType: 'string',
value: '1'
},
key
? {
field: key,
operator: 'eq',
dataType: 'string',
value: ecoFlowType.value
}
: null
].filter(Boolean);
if (!key) {
filters.push(
{
field: 'qecInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'mwrInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
},
{
field: 'avqInterval',
operator: 'eq',
dataType: 'string',
value: 'QEC_STLL_6_NOEQMN'
}
);
}
//
if (pieCode.value.length > 0) {
if (qid === 'all') {
filters.push({
field: 'baseId',
operator: 'in',
dataType: 'string',
value: pieCode.value
});
} else {
filters.push({
field: 'hbrvcd',
operator: 'in',
dataType: 'string',
value: pieCode.value
});
}
}
const filter = {
logic: 'and',
filters
};
tableRef.value?.getList(filter);
};
// ecoFlowType
watch(ecoFlowType, () => {
fetchPieData();
});
const handleSearch = () => {
// params
if (dateRange.value && dateRange.value.length === 2) {
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
}
// 使 timeTypekey
activeTimeType.value = params.value.timeType;
activeKey.value = params.value.key;
activeSTime.value = params.value.sTime;
activeETime.value = params.value.eTime;
// Tab
fetchTabList();
};
onMounted(() => {
//
if (dateRange.value && dateRange.value.length === 2) {
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
activeTimeType.value = params.value.timeType;
activeKey.value = params.value.key;
activeSTime.value = params.value.sTime;
activeETime.value = params.value.eTime;
fetchTabList();
}
//
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
//
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
});
//
const handleResize = () => {
chartInstance?.resize();
};
</script>
<style scoped lang="scss">
.stlldbqk-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
// padding: 16px;
}
.body_zhengti {
width: 100%;
display: flex;
justify-content: space-between;
flex: 1;
.echarts {
width: 35%;
height: 482px;
position: relative;
.pie-chart {
width: 100%;
height: 100%;
}
:deep(.ant-spin-nested-loading) {
width: 100%;
height: 100%;
}
:deep(.ant-spin-container) {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
.table1 {
width: 65%;
height: 482px;
}
}
.empty-state {
width: 100%;
height: 582px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>

View File

@ -0,0 +1,439 @@
<!-- SidePanelItem.vue -->
<template>
<div>
<SidePanelItem title="生态流量达标情况" :click-action="{ show: true }">
<!-- 使用插槽自定义点击提示的内容 -->
<template #click-action-content>
<div v-if="titleData.cnt" style="max-width: 300px; line-height: 1.6">
<p>
1统计电站范围接入过生态流量数据的电站
<!-- <span
style="color: #5989ad; cursor: pointer; "
@click.stop="handleStationClick()"
>
({{titleData.cnt}})
</span> -->
</p>
<p>2当来水不足时生态流量不小于入库流量判定为达标</p>
<p>395%座数表示统计电站范围内时段达标率大于等于95%的电站数量</p>
</div>
</template>
<div class="body_topOne">
<a-radio-group v-model:value="mode" @change="dataChage">
<a-radio-button value="hour">逐时</a-radio-button>
<a-radio-button value="day"></a-radio-button>
</a-radio-group>
<div class="title_text">95%座数/总座数</div>
</div>
<a-spin :spinning="spinning">
<div>
<div v-for="el in datalist" @click="handleBarClick(el)">
<div :key="el.key">
<div class="boy_one">
<div style="flex: 1; white-space: nowrap; margin-right: 5px">
{{ el.name }}
</div>
<img style="flex: 1" src="@/assets/components/fgx.svg" alt="" />
</div>
<div class="body_zhu">
<div class="body_biao">
<div v-for="value in allArr">
<div :key="value" class="nei_body">
<div
v-if="
value <
(el?.key == null
? 1
: el.qecTCnt == 0
? 0
: el.qecCnt / el.qecTCnt) *
20
"
class="little_body"
:style="{ backgroundColor: el.color }"
></div>
</div>
</div>
</div>
<div class="body_text">{{ el.qecCnt }}/{{ el.qecTCnt }}</div>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
<!-- 自定义弹框 -->
<a-modal
v-model:open="modalVisible"
:title="'生态流量达标情况'"
width="1536px"
:footer="null"
>
<STLLXFFS
v-if="modalVisible"
:options="list"
:typeKey="selectedItem.key"
:time="mode"
:basicId="baseid"
:dateArr="dateArr"
/>
</a-modal>
<!-- 环保自动监测工作开展情况弹框 -->
<a-modal
v-model:open="huanbaoModalVisible"
:title="'环保自动监测工作开展情况'"
width="1536px"
:footer="null"
>
<ModalYkzhbzdjcgz v-if="huanbaoModalVisible" :baseId="baseid" :titleData = titleData />
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import {
qgcetQgcStaticData,
evnmAutoMonitorGetKendoListCust
} from '@/api/stll';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
import STLLXFFS from './TwoLayer/ShengTaiLiuLiangDaBQKTwoLayer.vue';
import ModalYkzhbzdjcgz from './TwoLayer/ModalYkzhbzdjcgz.vue';
import HuanbaoZDJCGZKZ from '@/modules/huanbaozdjcgzkzQK/index.vue';
// 便
defineOptions({
name: 'shengtaidabiaoMod'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const mapViewStore = useMapViewStore();
const baseid = ref('');
const dateArr = ref<any[]>([]);
const obj = {
show: true,
value:
'1、统计电站范围接入过生态流量数据的电站,2、当来水不足时生态流量不小于入库流量判定为达标,3、“≥95%座数”表示统计电站范围内时段达标率大于等于95%的电站数量'
};
const mode = ref('hour');
const dataArr: any = ref([
{
name: '生态环境部门要求',
qecCnt: '0', //
qecTCnt: '0', //
type: 1,
key: 'qecInterval'
},
// {
// name: '',
// qecCnt: '0', //
// qecTCnt: '0', //
// type: 2,
// key: 'mwrInterval'
// }
]);
const list: any = ref([
{ name: '生态环境部门要求', type: 1, color: '#77C300', key: 'qecInterval' },
// { name: '', type: 2, color: '#56C3E3', key: 'mwrInterval' },
// { name: ' 10%', type: 3, color: '#F76B01', key: 'avqInterval' },
// { name: '', type: 4, color: '#B4B4B4', key: null }
]);
const datalist: any = ref([]);
const allArr: any = ref(Array.from({ length: 20 }, (_, i) => i));
const spinning = ref(false);
const modalVisible = ref(false);
const modalTitle = ref('');
const selectedItem = ref<any>(null);
const huanbaoModalVisible = ref(false);
//
const handleStationClick = () => {
huanbaoModalVisible.value = true;
};
//
const handleBarClick = (item: any) => {
selectedItem.value = item;
// modalTitle.value = `${item.name} - `;
modalVisible.value = true;
};
// type list dataArr
const setStyle = () => {
if (dataArr.value.length == 0) {
return false;
}
dataArr.value.forEach((item: any) => {
// list type
const matched = list.value.find(
(listItem: any) => listItem.type === item.type
);
if (matched) {
// dataArr
datalist.value.push({
...matched,
...item
});
}
});
console.log(datalist.value);
};
const dataChage = () => {
getData();
};
const getdate = (offsetYears = 0) => {
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
let startDate: string;
let endDate: string;
if (dateArr.value && dateArr.value.length === 2) {
const start = new Date(dateArr.value[0]);
const end = new Date(dateArr.value[1]);
if (offsetYears !== 0) {
start.setFullYear(start.getFullYear() + offsetYears);
end.setFullYear(end.getFullYear() + offsetYears);
}
startDate = `${formatDate(start)} 00:00:00`;
endDate = `${formatDate(end)} 23:59:59`;
} else {
const now = new Date();
const oneMonthAgo = new Date(now);
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
if (offsetYears !== 0) {
now.setFullYear(now.getFullYear() + offsetYears);
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
}
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
endDate = `${formatDate(now)} 23:59:59`;
}
return { startDate, endDate };
};
const getData = async () => {
const { startDate, endDate } = getdate(0);
let params = {
filter: {
logic: 'and',
filters: [
baseid.value != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
}
: null,
{
field: 'dtin',
operator: 'eq',
dataType: 'string',
value: '1'
},
{
field: 'type',
operator: 'eq',
dataType: 'string',
value: mode.value
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: startDate
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: endDate
}
].filter(Boolean)
}
};
spinning.value = true;
try {
let res = await qgcetQgcStaticData(params);
// API
if (res.data && res.data.list) {
const apiList = res.data.list;
//
// 使type: 1 type: 2
if (apiList && apiList.length > 0) {
dataArr.value = apiList.slice(0, 2).map((item: any) => ({
// name: item.typeName || item.desc,
qecCnt: item.qecCnt,
qecTCnt: item.qecTCnt,
type: item.type
}));
//
datalist.value = [];
setStyle();
} else {
//
dataArr.value = [];
datalist.value = [];
}
}
} catch (error) {
console.error('获取生态流量达标数据失败:', error);
} finally {
spinning.value = false;
}
};
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getData();
}
},
{ deep: true, immediate: true }
);
watch(
() => mapViewStore.searchTimeRange,
(newVal) => {
dateArr.value = newVal;
getData();
},
{ deep: true }
);
//
const titleData:any = ref({ cnt: 0 })
const getcont = async () => {
let params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "showIds",
"operator": "in",
"value": [
"7"
]
}
]
}
}
let res = await evnmAutoMonitorGetKendoListCust(params);
// [
// {
// "cnt": 452,
// "orderInx": 7,
// "orderInxName": "",
// "sttpFullPath": "ENG",
// "sttpCode": "ENG",
// "sttpName": ""
// }
// ]
let list = res?.data?.data[0];
if (list) {
titleData.value = list
}
//
};
//
onMounted(() => {
setStyle();
getcont()
});
</script>
<style lang="scss" scoped>
.body_topOne {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.title_text {
font-size: 16px;
}
}
.boy_one {
display: flex;
align-items: center;
margin: 5px 0px;
}
.body_zhu {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: all 0.3s ease;
.body_biao {
width: 100%;
height: 52px;
border: 1px solid #ccdae7;
border-radius: 2px;
display: flex;
padding: 5px 1px;
overflow: hidden;
.nei_body {
width: 12px;
height: 100%;
border: 1px solid #ccdae7;
padding: 2px;
margin-left: 4px;
.little_body {
width: 100%;
height: 100%;
}
}
}
.body_text {
display: flex;
flex-direction: column;
width: 90px;
height: 100%;
justify-content: space-around;
margin-left: 6px;
align-items: center;
font-size: 18px;
font-weight: 500;
}
}
.ant-radio-group {
// border: 3px solid #2f6b98 !important;
// border-radius: 10px !important;
.ant-radio-button-wrapper-checked {
border: 1px solid #2f6b98 !important;
background-color: #2f6b98 !important;
color: #fff !important;
}
.ant-radio-button-wrapper {
border: 2px solid #2f6b98 !important;
}
.ant-radio-button-wrapper-checked :before {
background-color: #2f6b98 !important;
}
}
</style>

View File

@ -15,7 +15,7 @@
<!-- <a-form-item label=" "> -->
<a-input
v-model:value="params.ennm"
placeholder=" "
placeholder="请输入电站名称"
style="width: 200px; margin-right: 15px"
/>
<!-- </a-form-item> -->
@ -104,7 +104,7 @@ import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
import {
vmsstbprptGetKendoList,
vmsstbprptGetKendoList1,
intervalGetKendoListCust,
eqqecRateCount,
eqGetKendoListCust
@ -442,7 +442,8 @@ const fetchPieData = async () => {
};
// TODO: API -
const res = await vmsstbprptGetKendoList(obj);
const res = await vmsstbprptGetKendoList1(obj);
// debugger
if (res?.data?.data) {
const dataSource = res.data.data.map((el: any) => ({
id: isAllBase ? el.baseId : el.hbrvcd,
@ -561,12 +562,7 @@ const updateChart = () => {
label: {
show: true,
formatter: function (params) {
const maxLen = 8; //
let name = params.name;
if (name.length > maxLen) {
return name.substring(0, maxLen) + '...';
}
return name;
return params.value + '座';
}
},
emphasis: {

View File

@ -0,0 +1,796 @@
<template>
<div class="stlldbqk-container">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form">
<a-form-item label="基地选择">
<a-select
v-model:value="params.qid"
placeholder="请选择基地"
style="width: 200px"
show-search
:filter-option="filterOption"
:options="jidiOptions"
/>
</a-form-item>
<!-- <a-form-item label=" "> -->
<a-input
v-model:value="params.ennm"
placeholder="请输入电站名称"
style="width: 200px; margin-right: 15px"
/>
<!-- </a-form-item> -->
<a-form-item label="日期范围">
<a-range-picker
v-model:value="dateRange"
:allow-clear="false"
format="YYYY-MM-DD"
style="width: 360px"
/>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">查询</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- Tabs 选择器仅在 type === 'left' 时显示 -->
<a-spin :spinning="tabsLoading" tip="加载中...">
<a-tabs
v-if="props.type === 'left' && tabOptions.length > 0"
v-model:activeKey="activeTab"
@change="handleTabChange"
class="stll-tabs"
>
<a-tab-pane
v-for="item in tabOptions"
:key="item.qecPerformance"
:tab="`${item.qecPerformanceName} (${
item.qecRate !== null &&
item.qecRate !== undefined &&
item.qecRate !== ''
? Number(item.qecRate).toFixed(2) + '%'
: '-%'
})`"
/>
</a-tabs>
<div class="body_zhengti">
<div class="echarts">
<a-spin :spinning="dataLoading">
<div
v-show="pieData.length > 0"
ref="chartRef"
class="pie-chart"
></div>
<a-empty
v-show="!dataLoading && pieData.length === 0"
description="暂无数据"
/>
</a-spin>
</div>
<div class="table">
<BasicTable
ref="tableRef"
:columns="columns"
:scrollY="'450px'"
:list-url="
props.type == 'top'
? intervalGetKendoListCust
: eqGetKendoListCust
"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #ennm="{ column, record }">
<a-button type="link" size="small" @click="handleView(record)">{{
record.ennm
}}</a-button>
</template>
</BasicTable>
</div>
</div>
</a-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
import dayjs from 'dayjs';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
import {
vmsstbprptGetKendoList1,
intervalGetKendoListCust,
eqqecRateCount,
eqGetKendoListCust
} from '@/api/stll';
const modelStore = useModelStore();
const props = defineProps<{
type?: string;
basicId?: string;
qecPerformance?: any;
dateArr?: string[];
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const columns = [
props.type == 'left'
? {
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName',
sort: true //
}
: null,
{
key: 'ennm',
title: '电站名称',
dataIndex: 'ennm',
slots: { customRender: 'action' },
sort: true //
},
{
key: 'addvcdName',
title: '行政区',
dataIndex: 'addvcdName',
sort: true //
},
{
key: 'hbrvcdName',
title: '所在河段',
dataIndex: 'hbrvcdName',
width: '300px'
},
{
key: 'qecRate',
title: `生态流量达标率(%)`,
dataIndex: 'qecRate',
sort: true, //
width: '160px',
customRender: ({ text }: any) => {
if (text === null || text === undefined || text === '' || text === '-')
return '-';
return Number(text).toFixed(2);
}
},
{
key: 'beforeQecRate',
title: '去年同期达标率(%)',
dataIndex: 'beforeQecRate',
width: '160px',
sort: true, //
customRender: ({ text }: any) => {
if (text === null || text === undefined || text === '' || text === '-')
return '-';
return Number(text).toFixed(2);
}
}
].filter(Boolean);
const tableRef = ref();
const defaultSort = ref([
{
field: 'baseId',
dir: 'asc'
},
{
field: 'rvcdStepSort',
dir: 'asc'
},
{
field: 'siteStepSort',
dir: 'asc'
},
{
field: 'qecRate',
dir: 'asc'
}
]);
//
const searchParams = computed(() => {
let baseSort;
//
// 使
baseSort = defaultSort.value;
return {
sort: baseSort
};
});
// dateArr
const initDateRange = () => {
// dateArr使
if (props.dateArr && props.dateArr.length === 2) {
return [dayjs(props.dateArr[0]), dayjs(props.dateArr[1])];
}
// 使""
const today = dayjs();
const lastMonthSameDay = today.subtract(1, 'month');
return [lastMonthSameDay, today];
};
const dateRange = ref(initDateRange());
// dateArr
watch(
() => props.dateArr,
newVal => {
if (newVal && newVal.length === 2) {
dateRange.value = [dayjs(newVal[0]), dayjs(newVal[1])];
}
},
{ deep: true }
);
const params = ref({
qid: props.basicId || 'all',
ennm: '',
sTime: '',
eTime: ''
});
const jidiOptions = computed(() => {
return [
...JidiSelectEventStore.jidiData.map((item: any) => ({
value: item.wbsCode,
label: item.wbsName
}))
];
});
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
// ==================== Tabs ====================
const activeTab = ref<string>(props.qecPerformance || ''); // tab key
const tabOptions = ref<any[]>([]); // tabs
const tabsLoading = ref<boolean>(false); // tabs
// Tabs
const fetchTabOptions = async () => {
if (props.type !== 'left') return;
tabsLoading.value = true;
try {
const res = await eqqecRateCount({
filter: {
logic: 'and',
filters: [
params.value.qid !== 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: params.value.qid
}
: null,
{
field: 'type',
operator: 'eq',
dataType: 'string',
value: 'DAY'
},
{
field: 'RGCP_CODE',
operator: 'in',
dataType: 'string',
value: ['1']
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value:
params.value.sTime ||
dayjs().subtract(1, 'month').format('YYYY-MM-DD 00:00:00')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: params.value.eTime || dayjs().format('YYYY-MM-DD 23:59:59')
}
].filter(Boolean)
},
group: [{ dir: 'asc', field: 'qecPerformance' }]
});
if (res?.data?.data && res.data.data.length > 0) {
tabOptions.value = res.data.data;
//
activeTab.value = props.qecPerformance || res.data.data[0].qecPerformance;
} else {
tabOptions.value = [];
activeTab.value = '';
}
} catch (error) {
console.error('获取tabs选项失败:', error);
tabOptions.value = [];
activeTab.value = '';
} finally {
tabsLoading.value = false;
}
};
// Tab
const handleTabChange = (key: string) => {
activeTab.value = key;
//
fetchPieData();
fetchTableData();
};
// ==================== ====================
const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const pieData = ref<any[]>([]); //
const dataLoading = ref<boolean>(false);
const pieCode = ref<string[]>([]); //
//
const themeecLists = [];
const jdColor = [
'#4b79ab',
'#78c300',
'#9556a4',
'#df91ab',
'#7399c6',
'#dbb629',
'#56c2e3',
'#f56a06',
'#cdba75',
'#76523b',
'#df75b6',
'#00a050',
'#f5a644',
'#4b79ab',
'#78c300',
'#9556a4',
'#df91ab',
'#7399c6',
'#dbb629',
'#56c2e3',
'#f56a06',
'#cdba75',
'#76523b',
'#df75b6',
'#00a050',
'#f5a644'
];
//
const fetchPieData = async () => {
if (!params.value.sTime || !params.value.eTime) return;
const isAllBase = params.value.qid === 'all';
dataLoading.value = true;
try {
const { sTime, eTime, qid, ennm } = params.value;
let obj = {
filter: {
logic: 'and',
filters: [
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'ENG'
},
{
field: 'prsc',
operator: 'in',
dataType: 'string',
value: [1, 2, 3]
},
qid != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: qid
}
: null,
{
field: 'bldstt',
operator: 'in',
dataType: 'string',
value: [1, 10, 11]
},
props.type != 'left'
? {
field: 'dtinEnv',
operator: 'in',
dataType: 'string',
value: [1, 2]
}
: {
field: 'RGCP_CODE',
operator: 'in',
dataType: 'string',
value: [activeTab.value]
}
].filter(Boolean)
},
group: [
isAllBase
? { dir: 'asc', field: 'baseStepSort' }
: { dir: 'asc', field: 'rvcdStepSort' },
{
dir: 'asc',
field: isAllBase ? 'baseId' : 'hbrvcd'
},
{
dir: 'asc',
field: isAllBase ? 'baseName' : 'hbrvcdName'
}
],
groupResultFlat: true
};
// TODO: API -
const res = await vmsstbprptGetKendoList1(obj);
// debugger
if (res?.data?.data) {
const dataSource = res.data.data.map((el: any) => ({
id: isAllBase ? el.baseId : el.hbrvcd,
name: isAllBase ? el.baseName : el.hbrvcdName,
value: isAllBase ? el.count_baseId : el.count_hbrvcd,
key: isAllBase ? el.baseId : el.hbrvcd
}));
pieData.value = dataSource;
pieCode.value = []; //
// DOM
await nextTick();
initOrUpdateChart();
} else {
pieData.value = [];
}
} catch (error) {
console.error('获取饼图数据失败:', error);
pieData.value = [];
} finally {
dataLoading.value = false;
}
};
//
const initOrUpdateChart = () => {
if (!chartRef.value) return;
//
if (!chartInstance) {
chartInstance = echarts.init(chartRef.value);
//
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
}
updateChart();
};
//
const updateChart = () => {
if (!chartInstance) return;
const total =
pieData.value.reduce((pre: number, cur: any) => pre + cur.value, 0) || 0;
//
const colors = pieData.value.map((item: any, index: number) => {
const matched = themeecLists.find((i: any) => i.name === item.name);
return matched?.color || jdColor[index % jdColor.length];
});
const option: EChartsOption = {
color: colors,
tooltip: {
trigger: 'item',
formatter: '{b}: {c} (座) ({d}%)'
},
legend: {
orient: 'vertical',
right: 20,
top: 80,
type: 'scroll',
textStyle: {
width: 100, //
overflow: 'truncate', //
ellipsis: '...' //
}
},
title: [
{
text: total.toString(),
left: '34%',
top: '44%',
textAlign: 'center',
textStyle: {
fontSize: 24,
fontWeight: 'bold',
color: '#333'
}
},
{
text: '电站总数量',
left: '34%',
top: '51%',
textAlign: 'center',
textStyle: {
fontSize: 14,
color: '#666'
}
},
{
text: '(座)',
left: '34%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: 12,
color: '#999'
}
}
],
series: [
{
name: '电站总数量',
type: 'pie',
radius: ['31%', '40%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: function (params) {
return params.value + '座';
}
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
},
data: pieData.value
}
]
};
chartInstance.setOption(option, true);
};
//
const handleLegendSelectChanged = (params: any) => {
const selected = params.selected;
const trueRivers = Object.keys(selected).filter(key => selected[key]);
const allFalse = Object.values(selected).every(value => value === false);
const selectedKeys = pieData.value
.filter((item: any) => trueRivers.includes(item.name))
.map((item: any) => item.key);
pieCode.value = allFalse ? ['0'] : selectedKeys;
//
fetchTableData();
};
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
const handleView = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENGJJ';
modelStore.title = record.ennm;
modelStore.params.stcd = record.stcd;
modelStore.params.eqtp = record.eqtp;
};
//
const fetchTableData = async () => {
const { sTime, eTime, qid, ennm } = params.value;
// TODO: -
const filters = [
qid != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: qid
}
: null,
{
field: 'dtinEnv',
operator: 'in',
dataType: 'string',
value: [1, 2]
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: sTime
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: eTime
},
ennm
? {
field: 'ennm',
operator: 'contains',
dataType: 'string',
value: ennm
}
: null,
props.type == 'left'
? {
field: 'rgcpCode',
operator: 'eq',
dataType: 'string',
value: activeTab.value
}
: null,
props.type == 'left'
? {
field: 'type',
operator: 'eq',
dataType: 'string',
value: 'DAY'
}
: null
].filter(Boolean);
const filter = {
logic: 'and',
filters
};
tableRef.value?.getList(filter);
};
const handleSearch = () => {
// params
if (dateRange.value && dateRange.value.length === 2) {
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
}
// type === 'left' tabs
if (props.type === 'left') {
fetchTabOptions().then(() => {
// tabs
fetchPieData();
fetchTableData();
});
} else {
//
fetchPieData();
fetchTableData();
}
};
onMounted(() => {
//
if (dateRange.value && dateRange.value.length === 2) {
params.value.sTime = dateRange.value[0].format('YYYY-MM-DD 00:00:00');
params.value.eTime = dateRange.value[1].format('YYYY-MM-DD 23:59:59');
// type === 'left' tabs
if (props.type === 'left') {
fetchTabOptions().then(() => {
// tabs
console.log(JidiSelectEventStore.jidiData);
fetchPieData();
fetchTableData();
});
} else {
//
console.log(JidiSelectEventStore.jidiData);
fetchPieData();
fetchTableData();
}
}
//
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
//
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
});
//
const handleResize = () => {
chartInstance?.resize();
};
</script>
<style scoped lang="scss">
.stlldbqk-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
// padding: 16px;
}
.stll-tabs {
margin-top: 16px;
margin-bottom: 16px;
:deep(.ant-tabs-nav) {
margin-bottom: 0;
}
}
.body_zhengti {
width: 100%;
display: flex;
justify-content: space-between;
flex: 1;
margin-top: 25px;
.echarts {
width: 35%;
height: 482px;
position: relative;
.pie-chart {
width: 100%;
height: 100%;
}
:deep(.ant-spin-nested-loading) {
width: 100%;
height: 100%;
}
:deep(.ant-spin-container) {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
.table {
width: 65%;
height: 482px;
}
}
</style>

View File

@ -0,0 +1,675 @@
<template>
<div>
<SidePanelItem title="生态流量达标情况" :click-action="{ show: true }">
<!-- 使用插槽自定义点击提示的内容 -->
<!-- 1时间2026-05-11 2026-06-11
-->
<template #click-action-content>
<div v-if="titleData.cnt" style="max-width: 300px; line-height: 1.6">
<p>
1时间{{ getdate().startDate1 }} {{ getdate().endDate1 }}
</p>
<p>
2范围接入过生态流量数据的电站
<!-- <span
style="color: #5989ad; cursor: pointer"
@click.stop="handleStationClick()"
>
({{ titleData.cnt }}) </span
> -->
</p>
<p>3达标判定按环评批复或报告中对生态流量限值有明确要求的评判</p>
</div>
</template>
<div class="body_topOne">
<a-radio-group v-model:value="mode" @change="getEcharts()">
<a-radio-button value="top">按基地</a-radio-button>
<a-radio-button value="left">按调节性能</a-radio-button>
</a-radio-group>
</div>
<a-spin :spinning="spinning">
<div ref="chartRef" class="chart-container"></div>
</a-spin>
</SidePanelItem>
</div>
<!-- 环保自动监测工作开展情况弹框 -->
<a-modal
v-model:open="huanbaoModalVisible"
:title="'环保自动监测工作开展情况'"
width="1536px"
:footer="null"
>
<ModalYkzhbzdjcgz v-if="huanbaoModalVisible" :titleData="titleData" />
</a-modal>
<!-- 生态流量达标率详情弹框 -->
<a-modal
v-model:open="detailModalVisible"
:title="detailModalTitle"
width="1536px"
:footer="null"
>
<STLLXFFS
v-if="detailModalVisible"
:type="mode"
:basicId="detailData[0]?.baseId"
:qecPerformance="detailData[0].qecPerformance"
:date-arr="dateArr"
/>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import * as echarts from 'echarts';
import { eqqecRateCount, evnmAutoMonitorGetKendoListCust } from '@/api/stll';
import ModalYkzhbzdjcgz from '../shengtaidabiaoMod/TwoLayer/ModalYkzhbzdjcgz.vue';
import STLLXFFS from './TwoLayer/STLLXFFS.vue'
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
// 便
defineOptions({
name: 'shengtaidabiaoMod'
});
const mode = ref('top');
const spinning = ref(false);
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const mapViewStore = useMapViewStore();
//
let dateArr = ref<any[]>([]);
const chartData = ref<{
categories: string[];
currentData: number[];
lastYearData: number[];
baseIds: (string | number)[];
qecPerformances: (string | number)[];
}>({
categories: [],
currentData: [],
lastYearData: [],
baseIds: [],
qecPerformances: []
});
const huanbaoModalVisible = ref(false);
//
const handleStationClick = () => {
huanbaoModalVisible.value = true;
};
//
const loadData = async () => {
spinning.value = true;
try {
//
await getEcharts();
// DOM
setTimeout(() => {
initChart();
spinning.value = false;
}, 50);
} catch (error) {
console.error('数据加载失败:', error);
spinning.value = false;
}
};
//
const initChart = () => {
if (!chartRef.value) {
console.error('图表容器未渲染');
return;
}
//
const containerHeight = chartRef.value.offsetHeight;
if (!containerHeight || containerHeight === 0) {
console.warn('容器高度为 0延迟重试');
setTimeout(() => initChart(), 50);
return;
}
//
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(chartRef.value);
// 使
const data = chartData.value;
const isHorizontal = mode.value === 'top';
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params: any) {
let result = params[0].name + '<br/>';
params.forEach((param: any) => {
const percentage = param.value.toFixed(2) + '%';
result +=
param.marker +
param.seriesName +
'&nbsp;&nbsp;<b>' +
percentage +
'</b><br/>';
});
return result;
}
},
legend: {
data: ['当前', '去年同期']
},
grid: { left: 10, right: 10, bottom: 20, top: 40, containLabel: true },
dataZoom: [
{
type: 'inside',
// (mode='top'): X
// (mode='left'): XY
[isHorizontal ? 'xAxisIndex' : 'xAxisIndex']: 0,
filterMode: 'empty',
zoomOnMouseWheel: true,
moveOnMouseMove: false,
moveOnMouseWheel: true,
start: 0,
end: 100,
minValueSpan: 0,
maxValueSpan: isHorizontal ? 20 : 5
}
],
xAxis: isHorizontal
? {
type: 'value',
min: 80,
max: 100,
splitLine: {
show: true,
lineStyle: {
color: '#E8E8E8',
type: 'solid'
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
}
}
: {
type: 'category',
data: data.categories,
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0,
rotate: 0,
margin: 10
},
axisLine: {
show: true,
lineStyle: {
color: '#666'
}
},
axisTick: {
show: true,
lineStyle: {
color: '#666'
}
},
splitLine: {
show: false
}
},
yAxis: isHorizontal
? {
type: 'category',
data: data.categories,
inverse: true,
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0,
rotate: 45,
margin: 10
},
axisLine: {
show: true,
lineStyle: {
color: '#666'
}
},
axisTick: {
show: true,
lineStyle: {
color: '#666'
}
},
splitLine: {
show: false
}
}
: {
type: 'value',
min: 0,
max: 100,
splitLine: {
show: true,
lineStyle: {
color: '#E8E8E8',
type: 'solid'
}
},
axisLabel: {
color: '#666',
formatter: '{value}'
}
},
series: [
{
name: '当前',
type: 'bar',
data: data.currentData,
itemStyle: {
color: '#5470C6'
},
barWidth: isHorizontal ? 8 : 8
// barGap: '30%'
},
{
name: '去年同期',
type: 'bar',
data: data.lastYearData,
itemStyle: {
color: '#91CC75'
},
barWidth: isHorizontal ? 8 : 8
// barGap: '30%'
}
]
};
chartInstance.setOption(option);
//
chartInstance.off('click');
chartInstance.on('click', (params: any) => {
handleChartClick(params);
});
//
setTimeout(() => {
chartInstance?.resize();
}, 0);
};
//
const handleChartClick = (params: any) => {
const categoryName = params.name;
const currentIndex = params.dataIndex;
//
//
detailModalTitle.value =
mode.value === 'top'
? `生态流量达标率(按基地统计) `
: `生态流量达标率(按调节性能统计)`;
//
const baseId = mode.value === 'top' ? chartData.value.baseIds[currentIndex] : undefined;
const qecPerformance = mode.value === 'left' ? chartData.value.qecPerformances[currentIndex] : undefined;
//
const currentItem = {
baseId: baseId,
baseName: mode.value === 'top' ? categoryName :'',
qecPerformance: qecPerformance,
qecPerformanceName: mode.value === 'left' ? categoryName : '',
qecRate: chartData.value.currentData[currentIndex],
beforeQecRate: chartData.value.lastYearData[currentIndex]
};
detailData.value = [currentItem];
detailModalVisible.value = true;
};
const getdate = (offsetYears = 0) => {
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
let startDate: string;
let endDate: string;
let startDate1: string;
let endDate1: string;
// dateArr.value 使
if (dateArr.value && dateArr.value.length === 2) {
const start = new Date(dateArr.value[0]);
const end = new Date(dateArr.value[1]);
// offsetYears 0
if (offsetYears !== 0) {
start.setFullYear(start.getFullYear() + offsetYears);
end.setFullYear(end.getFullYear() + offsetYears);
}
startDate = `${formatDate(start)} 00:00:00`;
endDate = `${formatDate(end)} 23:59:59`;
//
startDate1 = formatDate(start);
endDate1 = formatDate(end);
// debugger
} else {
// 使""
const now = new Date();
const oneMonthAgo = new Date(now);
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
// offsetYears 0
if (offsetYears !== 0) {
now.setFullYear(now.getFullYear() + offsetYears);
oneMonthAgo.setFullYear(oneMonthAgo.getFullYear() + offsetYears);
}
startDate = `${formatDate(oneMonthAgo)} 00:00:00`;
endDate = `${formatDate(now)} 23:59:59`;
startDate1 = formatDate(oneMonthAgo);
endDate1 = formatDate(now);
}
return { startDate, endDate, startDate1, endDate1 };
};
const getEcharts = async () => {
//
const currentDateRange = getdate(0);
//
const lastYearDateRange = getdate(-1);
// filter
const baseFilter = {
logic: 'and',
filters: [
mode.value == 'top'
? {
field: 'dtinEnv',
operator: 'in',
dataType: 'string',
value: [1, 2]
}
: {
field: 'type',
operator: 'eq',
dataType: 'string',
value: 'DAY'
}
]
};
// params
const currentParams = {
filter: {
...baseFilter,
filters: [
...baseFilter.filters,
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: currentDateRange.startDate
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: currentDateRange.endDate
}
]
},
group:
mode.value == 'top'
? [
{ dir: 'asc', field: 'basestepsort' },
{ dir: 'asc', field: 'baseId' },
{ dir: 'asc', field: 'baseName' }
]
: [{ dir: 'asc', field: 'qecPerformance' }]
};
// params
const lastYearParams = {
filter: {
...baseFilter,
filters: [
...baseFilter.filters,
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: lastYearDateRange.startDate
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: lastYearDateRange.endDate
}
]
},
group: currentParams.group
};
try {
// API
const [currentRes, lastYearRes]: [any, any] = await Promise.all([
eqqecRateCount(currentParams),
eqqecRateCount(lastYearParams)
]);
//
const currentApiData = (currentRes && currentRes.data && currentRes.data.data) ? currentRes.data.data : [];
//
const lastYearApiData = (lastYearRes && lastYearRes.data && lastYearRes.data) ? lastYearRes.data.data : [];
// mode
if (mode.value === 'top') {
// baseId
const uniqueMap = new Map();
currentApiData.forEach((item: any) => {
if (!uniqueMap.has(item.baseId)) {
uniqueMap.set(item.baseId, item);
}
});
const uniqueData = Array.from(uniqueMap.values());
// baseId
const lastYearMap = new Map();
lastYearApiData.forEach((item: any) => {
if (!lastYearMap.has(item.baseId)) {
lastYearMap.set(item.baseId, item.qecRate);
}
});
chartData.value = {
categories: uniqueData.map((item: any) => item.baseName || ''),
currentData: uniqueData.map((item: any) =>
item.qecRate != null ? Number(item.qecRate) : 0
),
lastYearData: uniqueData.map((item: any) => {
const lastYearRate = lastYearMap.get(item.baseId);
return lastYearRate != null ? Number(lastYearRate) : 0;
}),
baseIds: uniqueData.map((item: any) => item.baseId),
qecPerformances: []
};
} else {
//
// qecPerformance
const lastYearMap = new Map();
lastYearApiData.forEach((item: any) => {
if (!lastYearMap.has(item.qecPerformance)) {
lastYearMap.set(item.qecPerformance, item.qecRate);
}
});
chartData.value = {
categories: currentApiData.map((item: any) => item.qecPerformanceName || ''),
currentData: currentApiData.map((item: any) =>
item.qecRate != null ? Number(item.qecRate) : 0
),
lastYearData: currentApiData.map((item: any) => {
const lastYearRate = lastYearMap.get(item.qecPerformance);
return lastYearRate != null ? Number(lastYearRate) : 0;
}),
baseIds: [],
qecPerformances: currentApiData.map((item: any) => item.qecPerformance || '')
};
}
} catch (error) {
console.error('API 调用失败:', error);
// 使
chartData.value = {
categories: [],
currentData: [],
lastYearData: [],
baseIds: [],
qecPerformances: []
};
}
};
//
const titleData: any = ref({ cnt: 0 });
const getcont = async () => {
let params = {
filter: {
logic: 'and',
filters: [
{
field: 'showIds',
operator: 'in',
value: ['7']
}
]
}
};
let res = await evnmAutoMonitorGetKendoListCust(params);
// [
// {
// "cnt": 452,
// "orderInx": 7,
// "orderInxName": "",
// "sttpFullPath": "ENG",
// "sttpCode": "ENG",
// "sttpName": ""
// }
// ]
let list = res?.data?.data[0];
if (list) {
titleData.value = list;
}
//
};
//
onMounted(() => {
loadData();
getcont();
//
window.addEventListener('resize', () => {
chartInstance?.resize();
});
});
watch(
() => mapViewStore.searchTimeRange,
(newVal, oldVal) => {
console.log('时间范围变化:', oldVal, '->', newVal);
dateArr.value = newVal
// debugger
loadData();
//
},
{ deep: true }
);
//
const detailModalVisible = ref(false);
const detailModalTitle = ref('');
const detailData = ref<any[]>([]);
const detailColumns = ref([
{
title: mode.value === 'top' ? '基地名称' : '调节性能',
dataIndex: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
key: mode.value === 'top' ? 'baseName' : 'qecPerformanceName',
width: 150,
fixed: 'left'
},
{
title: '当前达标率',
dataIndex: 'qecRate',
key: 'qecRate',
width: 120,
align: 'right'
},
{
title: '去年同期达标率',
dataIndex: 'beforeQecRate',
key: 'beforeQecRate',
width: 150,
align: 'right'
}
]);
// mode
watch(mode, () => {
loadData();
});
</script>
<style lang="scss" scoped>
.body_topOne {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.title_text {
font-size: 16px;
}
}
.ant-radio-group {
.ant-radio-button-wrapper-checked {
border: 1px solid #2f6b98 !important;
background-color: #2f6b98 !important;
color: #fff !important;
}
.ant-radio-button-wrapper {
border: 2px solid #2f6b98 !important;
}
.ant-radio-button-wrapper-checked :before {
background-color: #2f6b98 !important;
}
}
.chart-container {
width: 100%;
height: 449px;
// padding: 10px;
}
.detail-content {
padding: 16px 0;
}
</style>

View File

@ -1,196 +1,495 @@
<!-- SidePanelItem.vue -->
<!-- 水生生态调查 -->
<template>
<SidePanelItem title="水生生态调查" :select="select" :datetimePicker="datetimePicker">
<div ref="chartRef" class="echarts-container"></div>
</SidePanelItem>
<SidePanelItem
title="水生生态调查"
:moreSelect="moreSelect"
:select="select"
@update-values="handlePanelChange"
>
<a-spin :spinning="dataLoading" tip="加载中..." class="spin-wrapper">
<div
v-show="chartData.length > 0 && select.options.length > 0"
ref="chartRef"
class="chart-container"
></div>
<a-empty
v-show="
!dataLoading &&
(chartData.length === 0 || select.options.length === 0)
"
:description="select.options.length === 0 ? '暂无数据' : '暂无数据'"
class="empty-placeholder"
/>
</a-spin>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { wbsbGetKendoList, getDftYear, getFishListStat } from '@/api/stdc';
import moment from 'moment';
//
defineOptions({
name: 'shuiShengShengTaiDiaoCha'
name: 'shuiShengShengTaiDiaoCha'
});
// ==================== ====================
// ==================== Store ====================
const JidiSelectEventStore = useJidiSelectEventStore();
// ==================== ====================
const baseid = ref('');
const isInitializing = ref(false);
const dataLoading = ref(false);
// ==================== ====================
const moreSelect = ref({
show: true,
value: undefined as string | undefined,
options: [] as any[]
});
const select = ref({
show: true,
value: undefined as string | undefined,
options: [] as any[],
picker: undefined,
format: undefined,
width: '120px'
});
const yearOptions = ref<Record<string, any[]>>({});
// ==================== ====================
const chartRef = ref<HTMLDivElement>();
let chartInstance: any = null;
let resizeObserver: ResizeObserver | null = null;
const chartData = ref<any[]>([]);
//
const select = ref({
show: true,
value: undefined,
options: [],
picker: undefined,
format: undefined
});
// ==================== ====================
//
const datetimePicker = ref({
show: true,
value: '2020',
format: 'YYYY',
picker: 'year' as const,
options: []
});
// ==================== ====================
const mockData = [
{ name: '鱼类', value: 320 },
{ name: '底栖动物', value: 280 },
{ name: '浮游植物', value: 250 },
{ name: '浮游动物', value: 220 },
{ name: '水生植物', value: 190 },
{ name: '两栖类', value: 150 },
{ name: '爬行类', value: 120 },
{ name: '鸟类', value: 100 },
{ name: '哺乳类', value: 80 },
{ name: '其他', value: 60 }
];
//
const totalValue = mockData.reduce((sum, item) => sum + item.value, 0);
// ==================== ====================
const initChart = () => {
if (!chartRef.value) return;
if (!chartRef.value) return;
//
const rect = chartRef.value.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
console.warn('图表容器尺寸为0稍后重试...');
setTimeout(() => initChart(), 50);
return;
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(chartRef.value);
updateChart();
};
const updateChart = () => {
// DOM
if (!chartInstance && chartRef.value) { chartInstance = echarts.init(chartRef.value);
}
if (!chartInstance) return;
const totalValue = chartData.value.reduce(
(sum: number, item: any) => sum + (item.fcnt || 0),
0
);
const option: EChartsOption = {
color: [
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
'#3ba272', '#fc8452', '#9a60b4', '#ea7ccc', '#8dd3a7'
],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderColor: 'transparent',
textStyle: { color: '#ffffff' },
formatter: '{b}: {c} ({d}%)'
},
legend: {
type: 'scroll',
orient: 'horizontal',
bottom: '5%',
data: chartData.value.map((item: any) => item.name),
icon: 'rect',
itemWidth: 14,
itemHeight: 14,
textStyle: { fontSize: 12, height: 8 },
pageTextStyle: { color: '#333' },
pageIconColor: '#2F6B98',
pageIconInactiveColor: '#aaa'
},
title: [
{
text: `(类)`,
top: '42%',
textAlign: 'center',
left: '50%',
textStyle: { color: '#333', fontSize: 13, fontWeight: 500 }
},
{
text: totalValue.toString(),
left: 'center',
top: '32%',
textStyle: { color: '#2F6B98', fontSize: 24 }
}
],
series: [
{
name: '生态类别',
type: 'pie',
radius: ['35%', '65%'],
center: ['50%', '40%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'outside',
formatter: '{b}: {c}'
},
labelLine: { show: true, length: 8, length2: 10 },
emphasis: {
label: { show: true, fontSize: 14, fontWeight: 'bold' }
},
data: chartData.value.map((item: any) => ({
name: item.name,
value: item.fcnt || 0
}))
}
]
};
chartInstance.setOption(option);
nextTick(() => {
chartInstance.resize();
});
};
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
// ==================== ====================
const getYearData = async () => {
try {
const filter: any = {};
if (baseid.value && baseid.value !== 'all') {
filter.baseId = baseid.value;
}
//
chartInstance = echarts.init(chartRef.value);
//
const option: EChartsOption = {
color: [
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
'#3ba272', '#fc8452', '#9a60b4', '#ea7ccc', '#8dd3a7'
],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderColor: 'transparent',
textStyle: {
color: '#ffffff'
},
formatter: '{b}: {c} ({d}%)'
},
legend: {
type: 'scroll',
orient: 'horizontal',
bottom: '5%',
data: mockData.map(item => item.name),
icon: 'rect',
itemWidth: 14,
itemHeight: 14,
textStyle: {
fontSize: 12,
height: 8
},
pageTextStyle: {
color: '#333'
},
pageIconColor: '#2F6B98',
pageIconInactiveColor: '#aaa'
},
title: [
{
text: `(类)`,
top: '42%',
textAlign: 'center',
left: '50%',
textStyle: {
color: '#333',
fontSize: 13,
fontWeight: 500
}
},
{
text: totalValue.toString(),
left: 'center',
top: '32%',
textStyle: {
color: '#2F6B98',
fontSize: 24
}
}
],
series: [
{
name: '生态类别',
type: 'pie',
radius: ['35%', '65%'],
center: ['50%', '40%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'outside',
formatter: '{b}: {c}'
},
labelLine: {
show: true,
length: 8,
length2: 10
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
},
data: mockData
}
]
const params = {
filter,
group: [{ dir: 'des', field: 'yr' }]
};
//
chartInstance.setOption(option);
const res = await getDftYear(params);
// debugger
const data = res?.data?.data?.[0] || {};
if (Object.keys(data).length > 0) {
const objYear: Record<string, any[]> = {};
const allYears: any[] = [];
Object.keys(data).forEach(key => {
objYear[key] = data[key].map((item: any) => ({
label: String(item),
value: String(item)
}));
allYears.push(...data[key]);
});
yearOptions.value = objYear;
const uniqueYears = [...new Set(allYears)].sort(
(a: any, b: any) => Number(b) - Number(a)
);
const latestYear = uniqueYears[0];
const firstWbsCode = Object.keys(data)[0];
select.value.options = objYear[firstWbsCode] || [];
select.value.value = latestYear
? String(latestYear)
: moment().format('YYYY');
} else {
yearOptions.value = {};
select.value.options = [];
select.value.value = moment().format('YYYY');
}
} catch (error) {
console.error('获取年份数据失败:', error);
select.value.options = [];
select.value.value = moment().format('YYYY');
}
};
// ==================== resize ====================
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
const updateYearOptionsByBase = async (wbsCode: string) => {
const options = yearOptions.value[wbsCode];
if (options) {
select.value.options = options;
await nextTick();
select.value.value = options[0]?.value || '';
} else {
select.value.options = [];
await nextTick();
select.value.value = '';
}
};
const getMoreSelectData = async () => {
if (!baseid.value) return;
isInitializing.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'wbsType',
operator: 'eq',
value: 'PSB_RVCD'
},
baseid.value !== 'all'
? {
field: 'objid',
operator: 'eq',
value: baseid.value
}
: null
].filter(Boolean)
},
group: [
{ dir: 'asc', field: 'orderIndex' },
{ dir: 'asc', field: 'wbsCode' },
{ dir: 'asc', field: 'wbsName' },
{ dir: 'asc', field: 'objid' }
],
groupResultFlat: true
};
const res = await wbsbGetKendoList(params);
const data = res?.data?.data || [];
// debugger
if (data.length > 0) {
const treeData = buildTreeData(data);
const filteredData = filterSelectOptions(treeData);
moreSelect.value.options = filteredData;
moreSelect.value.value = treeData?.[0]?.children?.[0]?.value;
await getYearData();
if (moreSelect.value.value) {
await updateYearOptionsByBase(moreSelect.value.value);
}
await getChartData();
} else {
moreSelect.value.options = [];
moreSelect.value.value = undefined;
await getYearData();
}
} catch (error) {
console.error('获取树形选择器数据失败:', error);
moreSelect.value.options = [];
moreSelect.value.value = undefined;
} finally {
isInitializing.value = false;
}
};
/**
* 获取图表数据
* 根据当前选中的流域(hbrvcd)调用 getFishListStat API
*/
const getChartData = async () => {
if (!moreSelect.value.value) return;
dataLoading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'hbrvcd',
operator: 'eq',
dataType: 'string',
value: moreSelect.value.value
}
]
}
};
const res = await getFishListStat(params);
// debugger
chartData.value = res?.data?.data || [];
nextTick(() => {
updateChart();
});
} catch (error) {
console.error('获取图表数据失败:', error);
chartData.value = [];
nextTick(() => {
updateChart();
});
} finally {
dataLoading.value = false;
}
};
// ==================== ====================
const buildTreeData = (data: any[]) => {
if (!data || data.length === 0) return [];
const jiDiListMap: Record<string, string> = {};
JidiSelectEventStore.jidiData.forEach((item: any) => {
jiDiListMap[item.wbsCode] = item.wbsName;
});
const dataMapNameMap: Record<string, any[]> = {};
data.forEach(item => {
const objId = item.objid || item.objId;
if (!dataMapNameMap[objId]) {
dataMapNameMap[objId] = [];
}
const exists = dataMapNameMap[objId].some(
existing => existing.wbsCode === item.wbsCode
);
if (!exists) {
dataMapNameMap[objId].push(item);
}
});
const treeData = Object.keys(dataMapNameMap).map(objId => ({
title: jiDiListMap[objId] || '---',
value: objId,
selectable: false,
children: dataMapNameMap[objId].map((item: any) => ({
title: item.wbsName,
value: item.wbsCode
}))
}));
treeData.sort((a, b) => {
const numA = Number(a.value);
const numB = Number(b.value);
const aIsNum = !isNaN(numA);
const bIsNum = !isNaN(numB);
if (aIsNum && bIsNum) return numA - numB;
if (aIsNum) return -1;
if (bIsNum) return 1;
return a.value.localeCompare(b.value);
});
return treeData;
};
const filterSelectOptions = (options: any[]) => {
if (options?.length === 1 && options?.[0]?.children?.length === 1) {
return options[0].children;
}
return options;
};
// ==================== ====================
const handlePanelChange = async (data: any) => {
if (isInitializing.value) return;
if (
data.moreSelect !== undefined &&
data.moreSelect !== moreSelect.value.value
) {
moreSelect.value.value = data.moreSelect;
await updateYearOptionsByBase(data.moreSelect);
return;
}
if (data.select !== undefined && data.select !== select.value.value) {
select.value.value = data.select;
await getChartData();
}
};
// ==================== Watch ====================
watch(
() => JidiSelectEventStore.selectedItem,
async newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
await getMoreSelectData();
}
},
{ deep: true, immediate: true }
);
// ==================== ====================
//
onMounted(() => {
nextTick(() => {
setTimeout(() => {
initChart();
window.addEventListener('resize', handleResize);
}, 50);
});
nextTick(() => {
setTimeout(() => {
initChart();
window.addEventListener('resize', handleResize);
if (chartRef.value) {
resizeObserver = new ResizeObserver(() => {
if (chartInstance) {
chartInstance.resize();
}
});
resizeObserver.observe(chartRef.value);
}
}, 50);
});
});
//
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
});
</script>
<style lang="scss" scoped>
.echarts-container {
width: 100%;
height: 270px;
min-height: 270px;
.spin-wrapper {
width: 100%;
display: block;
}
.chart-container {
width: 100%;
height: 270px;
min-height: 270px;
}
.empty-placeholder {
width: 100%;
min-height: 270px;
display: flex;
background: #fff;
flex-direction: column;
justify-content: center;
align-items: center;
}
:deep(.ant-empty-description) {
text-indent: 0em !important;
}
:deep(.ant-spin-container) {
width: 100%;
}
</style>

View File

@ -2,209 +2,207 @@
<template>
<div class="data-table-container">
<a-table :columns="columns" :data-source="tableData" :pagination="false" size="middle" :customRow="customRow"
bordered class="custom-table">
:loading="dataLoading" bordered class="custom-table">
</a-table>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
import { ref, watch } from 'vue';
import { getVmsstbprptKendoList } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
//
defineOptions({
name: 'DataTable'
});
//
const columns: ColumnsType = [
const emit = defineEmits<{
(e: 'row-click', record: any): void;
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
const tableData = ref<any[]>([]);
const columns = ref([
{
title: '基地名称',
dataIndex: 'name',
key: 'name',
fixed: 'left',
fixed: 'left' as const,
width: 114.19,
align: 'left'
align: 'left' as const
},
{
title: '装机容量(万kW)',
key: 'capacity',
align: 'center',
align: 'center' as const,
children: [
{
title: '总计',
dataIndex: 'total',
key: 'total',
width: 76.64,
align: 'center'
align: 'center' as const
},
{
title: '已建',
dataIndex: 'built',
key: 'built',
dataIndex: 'buildinstall',
key: 'buildinstall',
width: 76.64,
align: 'center'
align: 'center' as const
},
{
title: '在建',
dataIndex: 'building',
key: 'building',
dataIndex: 'ubuildinstall',
key: 'ubuildinstall',
width: 68.25,
align: 'center'
align: 'center' as const
},
{
title: '未建',
dataIndex: 'unbuilt',
key: 'unbuilt',
// width: 76,
align: 'center'
},
dataIndex: 'nbuildinstall',
key: 'nbuildinstall',
align: 'center' as const
}
]
}
];
//
const tableData = ref([
{
key: '1',
name: '金沙江干流',
total: '7952.00',
built: '6370.00',
building: '902.00',
unbuilt: '680.00'
},
{
key: '2',
name: '雅砻江干流',
total: '2773.65',
built: '1920.00',
building: '492.00',
unbuilt: '361.65'
},
{
key: '3',
name: '大渡河干流',
total: '2689.65',
built: '1925.65',
building: '534.00',
unbuilt: '230.00'
},
{
key: '4',
name: '乌江干流',
total: '1181.36',
built: '1133.36',
building: '48.00',
unbuilt: '0.00'
},
{
key: '5',
name: '长江上游干流',
total: '3212.65',
built: '2523.65',
building: '0.00',
unbuilt: '689.00'
},
{
key: '6',
name: '湘西',
total: '1054.32',
built: '959.30',
building: '38.52',
unbuilt: '56.65'
},
{
key: '7',
name: '黄河上游干流',
total: '2794.59',
built: '1749.89',
building: '0.00',
unbuilt: '1044.65'
},
{
key: '8',
name: '黄河中游干流',
total: '835.65',
built: '401.65',
building: '0.00',
unbuilt: '434.00'
},
{
key: '9',
name: '南盘江 - 红水河',
total: '1271.00',
built: '1271.00',
building: '0.00',
unbuilt: '0.00'
},
{
key: '10',
name: '东北',
total: '1331.95',
built: '749.05',
building: '0.00',
unbuilt: '582.90'
},
{
key: '11',
name: '澜沧江干流',
total: '2535.00',
built: '2275.00',
building: '260.00',
unbuilt: '0.00'
},
{
key: '12',
name: '怒江干流',
total: '3138.00',
built: '0.00',
building: '102.00',
unbuilt: '3036.00'
},
{
key: '13',
name: '闽浙赣',
total: '962.08',
built: '920.68',
building: '0.00',
unbuilt: '41.40'
},
{
key: '14',
name: '其他',
total: '7460.57',
built: '7273.27',
building: '121.90',
unbuilt: '65.40'
},
{
key: '15',
name: '总计',
total: '7460.57',
built: '7273.27',
building: '121.90',
unbuilt: '65.40'
}
]);
//
const customRow = (_record: any, index: number) => {
const getData = async () => {
if (!baseid.value) return;
const filters: any[] = [
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
{ field: 'bldsttCcode', operator: 'isnotnull' },
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
];
if (baseid.value !== 'all') {
filters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
}
const aggregates = [{ field: 'ttpwr', aggregate: 'sum' }];
const group: any[] = [];
if (baseid.value !== 'all') {
group.push({ dir: 'asc', field: 'rvcdStepSort' });
} else {
group.push({ dir: 'asc', field: 'baseStepSort' });
}
group.push(
{ dir: 'asc', field: baseid.value === 'all' ? 'baseId' : 'hbrvcd' },
{ dir: 'asc', field: baseid.value === 'all' ? 'baseName' : 'hbrvcdName', aggregates },
{ dir: 'desc', field: 'bldsttCcode', aggregates }
);
const params = {
filter: { logic: 'and', filters },
group
};
try {
dataLoading.value = true;
const res = await getVmsstbprptKendoList(params);
const resData = res?.data?.data;
if (resData && Array.isArray(resData)) {
let newList: any = {};
let keyList: any[] = [];
const list = resData.flatMap((item: any) => {
const key = baseid.value === 'all' ? item.items[0].key : item.key;
keyList.push(key);
const { BASENAME, HBRVCDNAME } = baseid.value === 'all'
? item?.items?.[0]?.items?.[0]?.items[0]?.items?.[0]
: item?.items?.[0]?.items?.[0]?.items[0]?.items[0];
return item.items.map((outItem: any) => {
const itmeData = baseid.value === 'all' ? outItem.items?.[0]?.items : outItem.items[0]?.items;
const nbuildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '0')?.items[0]?.SUM_TTPWR * 0.1 || 0;
const ubuildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '1')?.items[0]?.SUM_TTPWR * 0.1 || 0;
const buildinstall = itmeData.find((x: any) => x.items[0]?.BLDSTTCCODE == '2')?.items[0]?.SUM_TTPWR * 0.1 || 0;
const total = nbuildinstall + ubuildinstall + buildinstall;
return {
key,
nbuildinstall: nbuildinstall?.toFixed(2),
ubuildinstall: ubuildinstall?.toFixed(2),
buildinstall: buildinstall?.toFixed(2),
total: total?.toFixed(2),
name: BASENAME || HBRVCDNAME,
bldsttCcode: outItem.key
};
});
});
const result = { total: 0, buildinstall: 0, ubuildinstall: 0, nbuildinstall: 0 };
list.forEach((e: any) => {
result.total += Number(e.total) * 100;
result.buildinstall += Number(e.buildinstall) * 100;
result.ubuildinstall += Number(e.ubuildinstall) * 100;
result.nbuildinstall += Number(e.nbuildinstall) * 100;
if (newList[e.key]) {
const { total, nbuildinstall, ubuildinstall, buildinstall } = newList[e.key];
newList[e.key] = {
...e,
total: Number(total) + Number(e.total),
nbuildinstall: Number(nbuildinstall) + Number(e.nbuildinstall),
ubuildinstall: Number(ubuildinstall) + Number(e.ubuildinstall),
buildinstall: Number(buildinstall) + Number(e.buildinstall)
};
} else {
newList[e.key] = e;
}
});
const { total, buildinstall, ubuildinstall, nbuildinstall } = result;
const arr: any[] = keyList.map((key) => newList[key]);
tableData.value = [
...arr,
{
name: '总计',
total: (total / 100)?.toFixed(2),
buildinstall: (buildinstall / 100)?.toFixed(2),
ubuildinstall: (ubuildinstall / 100)?.toFixed(2),
nbuildinstall: (nbuildinstall / 100)?.toFixed(2)
}
];
} else {
tableData.value = [];
}
} catch (error) {
console.error('DataTable getData error:', error);
tableData.value = [];
} finally {
dataLoading.value = false;
}
};
const customRow = (record: any, index: number) => {
return {
style: {
backgroundColor: index % 2 === 1 ? '#fafafa' : '#ffffff'
},
onClick: () => {
if (record.name !== '总计') {
emit('row-click', record);
}
}
};
};
//
onMounted(() => {
});
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
.data-table-container {
// padding: 0;
background: #fff;
padding: 16px 0px;
width: 100%;
@ -213,7 +211,6 @@ onMounted(() => {
.custom-table {
:deep(.ant-table) {
font-size: 14px;
// border: 1px solid #d5e2ed;
border-top: 1px solid #d5e2ed;
.ant-table-thead {
@ -221,8 +218,6 @@ onMounted(() => {
>th {
background-color: #e5eff8 !important;
color: #2f6b98;
// font-weight: 600;
// border: 1px solid #d5e2ed!important;
padding: 0px 6px;
text-align: center;
font-weight: 600;
@ -249,7 +244,6 @@ onMounted(() => {
.ant-table-tbody {
>tr {
>td {
// border: 1px solid #d5e2ed;
padding: 0px 6px !important;
text-align: center;
overflow-wrap: break-word;
@ -257,7 +251,6 @@ onMounted(() => {
&:first-child {
text-align: left;
// font-weight: 500;
}
}
@ -267,7 +260,6 @@ onMounted(() => {
}
}
// footer
.ant-table-footer::before {
display: none;
}
@ -295,6 +287,5 @@ onMounted(() => {
.ant-table-wrapper .ant-table-thead>tr>th {
border-bottom: 1px solid #d5e2ed00 !important;
}
}
</style>

View File

@ -0,0 +1,439 @@
<!-- HuanJingSJJRQK.vue -->
<template>
<div class="huanjing-sjjrqk">
<a-tabs v-model:activeKey="activeTab" @change="handleTabChange">
<a-tab-pane key="notAccess" tab="大中型已建在建电站" />
<a-tab-pane key="partAccess" tab="已接入电站运行数据" />
<a-tab-pane key="isAccess" tab="已开展全过程监测工作" />
</a-tabs>
<div class="content-wrapper">
<!-- 左侧饼图 -->
<div class="chart-section">
<div ref="chartContainer" class="chart-container"></div>
</div>
<!-- 右侧表格 -->
<div class="table-section">
<a-table
:columns="columns"
:data-source="tableData"
:pagination="false"
size="middle"
:customRow="customRow"
:loading="tableLoading"
class="custom-table"
/>
</div>
</div>
<!-- 详情弹窗 -->
<a-modal
v-model:open="detailModalVisible"
:title="detailModalTitle"
width="1200px"
:footer="null"
:destroy-on-close="true"
>
<component
v-if="detailModalVisible && detailComponent"
:is="detailComponent"
v-bind="detailProps"
/>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, shallowRef } from 'vue';
import * as echarts from 'echarts';
import { getVmsstbprptKendoList } from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
defineOptions({
name: 'HuanJingSJJRQK'
});
const props = defineProps<{
dataDimensionVal: string;
name: string;
}>();
const JidiSelectEventStore = useJidiSelectEventStore();
const activeTab = ref(props.name || 'notAccess');
const chartContainer = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const tableLoading = ref(false);
const tableData = ref<any[]>([]);
const chartFilter = ref<string>('');
//
const detailModalVisible = ref(false);
const detailModalTitle = ref('');
const detailComponent = shallowRef<any>(null);
const detailProps = ref<any>({});
//
const columns = ref([
{
title: '电站名称',
dataIndex: 'stnm',
key: 'stnm',
align: 'left'
},
{
title: '接入状态',
dataIndex: 'accessStatus',
key: 'accessStatus',
align: 'center'
},
{
title: '操作',
key: 'action',
align: 'center',
width: 120
}
]);
//
const customRow = (record: any) => {
return {
onClick: () => {
handleRowClick(record);
}
};
};
//
const handleRowClick = async (record: any) => {
// Tab
if (activeTab.value === 'notAccess') {
// -
detailModalTitle.value = '电站详情';
//
// 使
detailComponent.value = null;
detailProps.value = {
stcd: record.stcd,
stnm: record.stnm
};
detailModalVisible.value = true;
} else if (activeTab.value === 'partAccess') {
//
detailModalTitle.value = '运行数据详情';
detailComponent.value = null;
detailProps.value = {
stcd: record.stcd,
stnm: record.stnm
};
detailModalVisible.value = true;
} else if (activeTab.value === 'isAccess') {
//
detailModalTitle.value = '监测数据详情';
detailComponent.value = null;
detailProps.value = {
stcd: record.stcd,
stnm: record.stnm
};
detailModalVisible.value = true;
}
};
//
const getData = async () => {
if (!props.dataDimensionVal) return;
const baseFilters: any[] = [
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
{ field: 'bldsttCcode', operator: 'in', dataType: 'string', value: ['1', '2'] }
];
if (props.dataDimensionVal !== 'all') {
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: props.dataDimensionVal });
}
let params: any = {};
if (activeTab.value === 'notAccess') {
//
params = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
]
}
};
} else if (activeTab.value === 'partAccess') {
//
params = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'runState', operator: 'eq', dataType: 'number', value: 4 },
{ field: 'dtin', operator: 'eq', dataType: 'number', value: 1 }
]
}
};
} else if (activeTab.value === 'isAccess') {
//
params = {
filter: {
logic: 'and',
filters: [
...baseFilters,
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] },
{ field: 'dtinEnv', operator: 'in', dataType: 'string', value: ['2'] }
]
}
};
}
try {
tableLoading.value = true;
const res = await getVmsstbprptKendoList(params);
const resData = res?.data?.data || [];
//
tableData.value = resData.map((item: any) => ({
key: item.stcd,
stcd: item.stcd,
stnm: item.stnm,
accessStatus: getAccessStatus(item),
...item
}));
//
updateChart(resData);
} catch (error) {
console.error('HuanJingSJJRQK getData error:', error);
} finally {
tableLoading.value = false;
}
};
//
const getAccessStatus = (item: any) => {
if (activeTab.value === 'notAccess') {
return '已建在建';
} else if (activeTab.value === 'partAccess') {
return '已接入';
} else if (activeTab.value === 'isAccess') {
return '已监测';
}
return '-';
};
//
const updateChart = (data: any[]) => {
if (!chartInstance) return;
//
const statusMap: Record<string, number> = {};
data.forEach((item: any) => {
const status = item.bldsttCcode === '1' ? '在建' : '已建';
statusMap[status] = (statusMap[status] || 0) + 1;
});
const chartData = Object.entries(statusMap).map(([name, value]) => ({
name,
value
}));
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
color: ['#78c300', '#2196F3'],
series: [
{
name: '电站数量',
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '50%'],
data: chartData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
label: {
show: true,
formatter: '{b}: {c}'
}
}
]
};
chartInstance.setOption(option, true);
//
chartInstance.off('click');
chartInstance.on('click', (params: any) => {
chartFilter.value = params.name;
filterTableByChart(params.name);
});
};
//
const filterTableByChart = (status: string) => {
//
//
console.log('筛选状态:', status);
};
//
const initChart = () => {
if (!chartContainer.value) return;
chartInstance = echarts.init(chartContainer.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
color: ['#78c300', '#2196F3'],
series: [
{
name: '电站数量',
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '50%'],
data: [],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
label: {
show: true,
formatter: '{b}: {c}'
}
}
]
};
chartInstance.setOption(option);
};
// Tab
const handleTabChange = (key: string) => {
activeTab.value = key;
chartFilter.value = '';
getData();
};
onMounted(() => {
if (chartContainer.value) {
initChart();
}
getData();
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
});
// props
watch(
() => props.dataDimensionVal,
() => {
getData();
}
);
//
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style lang="scss" scoped>
.huanjing-sjjrqk {
padding: 20px;
.content-wrapper {
display: flex;
gap: 20px;
margin-top: 20px;
.chart-section {
flex: 0 0 400px;
.chart-container {
width: 100%;
height: 400px;
}
}
.table-section {
flex: 1;
}
}
.custom-table {
:deep(.ant-table) {
font-size: 13px;
border: 1px solid #e8e8e8;
.ant-table-thead {
> tr {
> th {
background-color: #e5eff8 !important;
color: #2f6b98;
font-weight: 600;
border: 1px solid #e8e8e8 !important;
padding: 6px 8px;
text-align: center;
}
}
}
.ant-table-tbody {
> tr {
> td {
border: 1px solid #e8e8e8;
padding: 6px 8px;
text-align: center;
}
&:hover {
background-color: #e6f7ff !important;
}
}
}
}
}
}
</style>

View File

@ -9,18 +9,50 @@
<div>水电开发情况</div>
<div style="color: #757575;font-size: 12px;line-height: 16px;">图释数量/装机容量</div>
<div>
<DevelopStatusChart />
<DevelopStatusChart @chart-click="handleChartClick" />
</div>
</div>
<!-- style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }} -->
<div style="flex: 1;display: flex; flex-direction: column; align-items: center;">
<EngEnvironmentData />
<EngEnvironmentData @item-click="handleEnvItemClick" />
</div>
</div>
<DataTable />
<DataTable @row-click="handleRowClick" />
</div>
<!-- <div v-else class="zanwushujv"> <a-empty /></div> -->
</SidePanelItem>
<!-- 水电开发情况弹窗 -->
<a-modal
v-model:open="shuidianModalVisible"
:title="shuidianModalTitle"
width="1536px"
:footer="null"
:destroy-on-close="true"
>
<ShuiDianKaiFQKTwoLayer
v-if="shuidianModalVisible"
:defaultJidiInfo="shuidianModalProps.defaultJidiInfo"
:seriesName="shuidianModalProps.seriesName"
:stateName="shuidianModalProps.stateName"
:defaultTab="shuidianModalProps.defaultTab"
/>
</a-modal>
<!-- 环境监测数据接入情况弹窗 -->
<a-modal
v-model:open="huanjingModalVisible"
:title="huanjingModalTitle"
width="1536px"
:footer="null"
:destroy-on-close="true"
>
<HuanJingSJJRQK
v-if="huanjingModalVisible"
:dataDimensionVal="huanjingModalProps.dataDimensionVal"
:name="huanjingModalProps.name"
/>
</a-modal>
</div>
</template>
@ -30,15 +62,75 @@ import SidePanelItem from '@/components/SidePanelItem/index.vue';
import DevelopStatusChart from "@/components/developStatusChart/index.vue"
import EngEnvironmentData from "@/components/engEnvironmentData/index.vue"
import DataTable from "./DataTable.vue"
import ShuiDianKaiFQKTwoLayer from "./TwoLayer/ShuiDianKaiFQKTwoLayer.vue"
import HuanJingSJJRQK from "./TwoLayer/HuanJingSJJRQK.vue"
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
// 便
defineOptions({
name: 'shuidianhuangjingjieruMod'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const prompt = ref({
show: true,
value: '统计大中型已建,在建水电站',
})
//
const shuidianModalVisible = ref(false);
const shuidianModalTitle = ref('水电开发情况');
const shuidianModalProps = ref<any>({});
//
const huanjingModalVisible = ref(false);
const huanjingModalTitle = ref('环境监测数据接入情况');
const huanjingModalProps = ref<any>({});
// DataTable
const handleRowClick = (record: any) => {
const currentJidiInfo = JidiSelectEventStore.selectedItem;
shuidianModalProps.value = {
defaultJidiInfo: {
baseid: currentJidiInfo?.wbsCode || 'all',
basename: currentJidiInfo?.wbsName || '全部基地'
},
seriesName: currentJidiInfo?.wbsCode === 'other' ? '数量(座)' : '装机容量(万kW)',
stateName: record.bldsttCcode === '0' ? '未建' : record.bldsttCcode === '1' ? '在建' : '已建',
defaultTab: record.bldsttCcode === '0' ? '未建' : record.bldsttCcode === '1' ? '在建' : '已建'
};
shuidianModalTitle.value = '水电开发情况';
shuidianModalVisible.value = true;
};
// DevelopStatusChart
const handleChartClick = (params: any) => {
const currentJidiInfo = JidiSelectEventStore.selectedItem;
shuidianModalProps.value = {
defaultJidiInfo: {
baseid: currentJidiInfo?.wbsCode || 'all',
basename: currentJidiInfo?.wbsName || '全部基地'
},
seriesName: params.seriesName,
stateName: params.name,
defaultTab: params.name
};
shuidianModalTitle.value = '水电开发情况';
shuidianModalVisible.value = true;
};
// EngEnvironmentData
const handleEnvItemClick = (params: any) => {
const currentJidiInfo = JidiSelectEventStore.selectedItem;
huanjingModalProps.value = {
dataDimensionVal: currentJidiInfo?.wbsCode || 'all',
name: params.name
};
huanjingModalTitle.value = '环境监测数据接入情况';
huanjingModalVisible.value = true;
};
//
onMounted(() => {

View File

@ -264,11 +264,12 @@ const getDcpcOptions = async () => {
];
const res = await getWeStaticsSecond( { filter: params, group });
// debugger
if (res?.data?.data?.length) {
const options = res.data.data.map((item: any) => ({
label: item.dcpc,
value: item.dcpc
label: item.key,
value: item.key
}));
dcpcOptions.value = [{ label: '全部', value: '' }, ...options];
} else {

View File

@ -0,0 +1,614 @@
<template>
<div>
<SidePanelItem
title="生态流量沿程变化"
:iconmap="iconmap"
:select="select"
:datetimePicker="datetimePicker"
@update-values="handlePanelChange"
>
<!-- 加载状态 -->
<div v-if="dataLoading" class="chart-loading">
<a-spin tip="加载中..." />
</div>
<!-- 无数据提示 -->
<a-empty
v-if="!chartData.length && !dataLoading"
description="暂无数据"
class="chart-empty"
/>
<!-- 图表容器使用 display 控制避免 v-if 销毁 DOM-->
<div
ref="chartRef"
class="chart-container"
:style="{
display: chartData.length && !dataLoading ? 'block' : 'none'
}"
></div>
</SidePanelItem>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick, onBeforeUnmount } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
// import { wbsbGetKendoList } from '@/api/sz';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
// TODO: API
import { qgcHourVGetKendoListCust, wbsbGetKendoList } from '@/api/stll';
import { useModelStore } from '@/store/modules/model';
//
defineOptions({
name: 'StllyanchengChangeMod'
});
// ==================== ====================
//
const iconmap = ref({
show: true,
value:
'入库流量是基于库容曲线和水量平衡的推算值,非实测数据。出库流量是基于机组出力特征曲线和闸门开度的推算值,非实测数据。',
icon: 'iconfont icon-time'
});
//
const datetimePicker = ref({
show: true,
value: dayjs().format('YYYY-MM-DD HH:mm'), //
format: 'YYYY-MM-DD HH:mm',
picker: 'date',
timeFormat: 'HH:mm',
options: []
});
//
const select = ref({
show: true,
value: '',
options: [],
picker: undefined,
format: undefined
});
const modelStore = useModelStore();
//
const wbsCode = ref(''); //
const selectData = ref(''); //
const chartData = ref<any[]>([]); //
const dataLoading = ref(false); //
const latestTime = ref(''); //
const hasTimeData = ref(false); //
//
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
//
const modalVisible = ref(false);
const modalData = ref<any>({
stcd: '',
stnm: '',
titleName: ''
});
// Store
const JidiSelectEventStore = useJidiSelectEventStore();
// ==================== ====================
/**
* 获取河段列表
*/
const getSelect = async () => {
if (!wbsCode.value) {
console.warn('wbsCode 为空,无法获取河段数据');
return;
}
let params = {
filter: {
logic: 'and',
filters: [
{
field: 'objId',
operator: 'eq',
value: wbsCode.value
},
{
field: 'wbsType',
operator: 'eq',
value: 'PSB_RVCD'
}
]
},
group: [
{
dir: 'asc',
field: 'orderIndex'
},
{
dir: 'asc',
field: 'wbsCode'
},
{
dir: 'asc',
field: 'wbsName'
},
{
dir: 'asc',
field: 'objid'
}
],
groupResultFlat: 'true'
};
try {
let res = await wbsbGetKendoList(params);
//
let data;
if (res && res.data && res.data.data) {
if (Array.isArray(res.data.data)) {
data = res.data.data;
} else if (res.data.data.data && Array.isArray(res.data.data.data)) {
data = res.data.data.data;
} else {
console.warn('无法解析河段数据:', res);
return;
}
} else {
console.warn('未获取到河段数据');
return;
}
if (data && data.length > 0) {
// ;
select.value.value = data[0].wbsCode;
selectData.value = data[0].wbsCode; //
select.value.options = data.map(item => ({
value: item.wbsCode,
label: item.wbsName
}));
//
await loadChartData();
} else {
console.warn('河段数据为空');
select.value.value = '';
select.value.options = [];
}
} catch (error) {
console.error('获取河段数据失败:', error);
}
};
/**
* 获取生态流量数据预留 API 位置
*/
const getList = async (rvcd: string, sDate: string) => {
// TODO: API
// React
// - baseId: JidiSelectEventStore.selectedItem
// - rvcd:
// - tm: 1
const currentTime = dayjs(sDate);
const startTime = currentTime.subtract(1, 'month');
let params = {
filter: {
logic: 'and',
filters: [
{
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: wbsCode.value
},
{
field: 'rvcd',
operator: 'eq',
dataType: 'string',
value: rvcd
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: startTime.format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: currentTime.format('YYYY-MM-DD HH:mm:ss')
}
]
},
sort: [
{
field: 'sort',
dir: 'asc'
}
]
};
// TODO: API FAA
const res = await qgcHourVGetKendoListCust(params);
//
if (!res || !res.data || !res.data.data || !Array.isArray(res.data.data)) {
console.warn('未获取到有效的生态流量数据:', res);
return [];
}
//
return res.data.data.map((item: any) => ({
TM: dayjs(item.tm).format('YYYY-MM-DD H时'),
stnm: item.stnm,
stcd: item.stcd,
qec: item.qec, //
qecLimit: item.qecLimit, //
qi: item.qi //
}));
};
/**
* 加载图表数据
*/
const loadChartData = async () => {
if (!selectData.value || !datetimePicker.value.value) {
return;
}
dataLoading.value = true;
try {
const data = await getList(selectData.value, datetimePicker.value.value);
if (data && data.length > 0) {
chartData.value = data;
hasTimeData.value = true;
//
if (data[0]?.TM) {
latestTime.value = data[0].TM;
}
//
await nextTick();
initChart();
} else {
chartData.value = [];
hasTimeData.value = false;
latestTime.value = '';
//
if (chartInstance) {
chartInstance.clear();
}
}
} catch (error) {
console.error('加载图表数据失败:', error);
chartData.value = [];
} finally {
dataLoading.value = false;
}
};
/**
* 初始化 ECharts 图表
*/
const initChart = () => {
if (!chartRef.value) {
console.warn('图表容器未准备好');
return;
}
//
if (chartInstance) {
chartInstance.dispose();
}
//
chartInstance = echarts.init(chartRef.value);
//
const xData: string[] = [];
const inData: (number | string)[] = []; //
const outData: (number | string)[] = []; //
const limitData: (number | string)[] = []; //
let hasValidData = false; //
chartData.value.forEach(item => {
xData.push(item.stnm);
const qecValue = item.qec ?? '-';
const qiValue = item.qi ?? '-';
const qecLimitValue = item.qecLimit ?? '-';
outData.push(qecValue);
inData.push(qiValue);
limitData.push(qecLimitValue);
//
if (qecValue !== '-' || qiValue !== '-' || qecLimitValue !== '-') {
hasValidData = true;
}
});
//
if (!hasValidData && chartData.value.length > 0) {
chartData.value = [];
if (chartInstance) {
chartInstance.clear();
}
return;
}
//
const option: echarts.EChartsOption = {
tooltip: {
trigger: 'axis',
confine: true,
formatter: function (params: any) {
const datum = chartData.value.find(
(el: any) => el.stnm === params[0].name
);
const unit = 'm³/s'; // TODO:
const content = params
.map((item: any) => {
const value = item.value ?? '-';
return `<div>${item.marker}${item.seriesName}: &nbsp;&nbsp;&nbsp;&nbsp;<span style="float:right">${value} ${unit}</span></div>`;
})
.join('');
return `<h3 style="color:white">${params[0].name}<br/>${
datum?.TM || ''
}</h3>${content}`;
},
axisPointer: {
type: 'line'
}
},
legend: {
data: ['入库流量', '生态流量', '环评要求限值'],
icon: 'roundRect',
top: 10
},
grid: {
left: 10,
right: 10,
bottom: 10,
top: 70,
containLabel: true
},
xAxis: {
type: 'category',
data: xData,
axisLine: {
lineStyle: {
color: '#505050'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#8f8f8f',
type: 'solid'
}
},
axisLabel: {
interval: xData.length > 12 ? 2 : 0,
rotate: 0,
formatter: (value: string, index: number) => {
const shortValue =
value.substring(0, 6) + (value.length > 6 ? '...' : '');
if (index % 2 !== 0) {
return `{a|${shortValue}}`;
} else {
return `\n{b|}${shortValue}`;
}
},
rich: {
a: {
height: 20,
align: 'center'
},
b: {
height: 30,
align: 'center'
}
}
}
},
yAxis: {
type: 'value',
name: '流量(m³/s)', // TODO:
minInterval: 1,
axisLine: {
lineStyle: {
color: '#505050'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#8f8f8f',
type: 'solid'
}
}
},
dataZoom: [
{
type: 'inside',
xAxisIndex: 0,
start: 0,
end: 100,
zoomOnMouseWheel: true,
moveOnMouseMove: true
}
],
series: [
{
name: '入库流量',
data: inData,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
connectNulls: true,
color: '#5470c6' // TODO:
},
{
name: '生态流量',
data: outData,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
connectNulls: true,
color: '#91cc75' // TODO:
},
{
name: '环评要求限值',
data: limitData,
type: 'bar',
barMaxWidth: 10,
color: '#fac858' // TODO:
}
]
};
chartInstance.setOption(option);
//
chartInstance.on('click', handleChartClick);
//
window.addEventListener('resize', handleResize);
};
/**
* 图表点击事件
*/
const handleChartClick = (params: any) => {
const datum = chartData.value.find((item: any) => item.stnm === params.name);
if (datum) {
const currentTime = dayjs(datetimePicker.value.value);
const startTime = currentTime.subtract(1, 'month');
modalVisible.value = true;
modelStore.modalVisible = true;
modelStore.params.sttp = 'ENGJJ';
modelStore.title = datum.stnm;
modelStore.params.stcd = datum.stcd;
modelStore.params.eqtp = 'QEC';
modelStore.filter.rangeTm = [startTime, currentTime];
modelStore.filter.stllgzlx = 'qecInterval';
modelStore.filter.stllTmType = 'day';
}
};
/**
* 窗口大小改变时重新调整图表
*/
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
/**
* 弹窗取消事件
*/
const handleModalCancel = () => {
modalVisible.value = false;
modalData.value = {
stcd: '',
stnm: '',
titleName: ''
};
};
/**
* 面板数据变化处理
*/
const handlePanelChange = async (data: any) => {
console.log('当前所有控件状态:', data);
//
if (data.select) {
select.value.value = data.select;
selectData.value = data.select;
}
if (data.datetime) {
datetimePicker.value.value = data.datetime;
}
//
if (data.select || data.datetime) {
await loadChartData();
}
};
// ==================== ====================
//
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
console.log('流域切换:', newVal);
if (newVal?.wbsCode) {
wbsCode.value = newVal.wbsCode;
getSelect();
}
},
{ deep: true, immediate: true }
);
//
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
});
</script>
<style lang="scss" scoped>
.chart-loading {
display: flex;
justify-content: center;
align-items: center;
height: 424px;
}
.chart-empty {
display: flex;
align-items: center;
height: 424px;
flex-direction: column;
/* align-content: center; */
justify-content: center;
text-align: center !important;
:deep(.ant-empty-description) {
text-align: center !important;
text-indent: 0;
width: 100%;
}
}
.chart-container {
width: 406px;
height: 424px;
}
.time-tip {
text-align: right;
font-size: 12px;
color: #8e8e8e;
padding: 8px 0;
margin-top: 4px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +1,188 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="AI识别大坝环境" :datetimePicker="datetimePicker">
<div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
<SidePanelItem title="AI识别大坝环境" :datetimePicker="datetimePicker" @update-values="handlePanelChange">
<a-spin :spinning="dataLoading" tip="加载中...">
<div class="facility-grid">
<div
v-for="(facility, idx) in facilities"
:key="facility.name"
class="facility-card"
@click="handleCardClick(String(idx + 1))"
>
<div style="width: 70px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
</div>
</div>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 14px;"> <span class="facility-count">{{ facility.count }}</span>
<span></span>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 14px;"> <span class="facility-count">{{ facility.count }}</span>
<span></span>
</div>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
<!-- AI识别大坝环境详情弹框 -->
<a-modal
v-model:open="modalVisible"
title="AI识别大坝环境"
width="1536px"
:footer="null"
@cancel="handleModalClose"
>
<AISBTK
v-if="modalVisible"
:month="datetimePicker.value"
:tab-idx="currentTabIdx"
:baseid="baseid"
/>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { ref, watch } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import AISBTK from './AISBTK.vue';
import { getAisbdbyxList } from '@/api/zngj';
import dayjs from 'dayjs';
// 便
defineOptions({
name: 'zengzhizhanjiansheyunxing'
name: 'aisbdbyx'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
const dataLoading = ref(false);
// ==================== ====================
const modalVisible = ref(false);
const currentTabIdx = ref('1');
// ==================== ====================
const datetimePicker = ref({
show: true,
value: `${new Date().getFullYear() - 1}`, //
value: dayjs().format('YYYY-MM'),
format: 'YYYY-MM',
picker: 'month' as const,
options: []
});
//
// ==================== ====================
const facilities = ref([
{
name: '漂浮物识别',
count: "34",
count: 0,
icon: 'icon iconfont icon-piaofuwu'
},
{
name: '叠梁门运行识别',
count: '0',
count: 0,
icon: 'icon iconfont icon-dieliangmen1'
},
]);
//
onMounted(() => {
//
// ==================== ====================
const handlePanelChange = (data: any) => {
console.log('当前所有控件状态:', data);
if (data.datetime) {
datetimePicker.value.value = data.datetime;
}
getCardData();
};
});
const handleCardClick = (tabIdx: string) => {
currentTabIdx.value = tabIdx;
modalVisible.value = true;
};
//
onUnmounted(() => {
const handleModalClose = () => {
modalVisible.value = false;
};
});
// ==================== ====================
const getCardData = async () => {
if (!datetimePicker.value.value || !baseid.value) return;
dataLoading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
baseid.value !== 'all' ? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
} : null,
{
field: 'startTime',
operator: 'gte',
dataType: 'date',
value: dayjs(datetimePicker.value.value).startOf('month').format('YYYY-MM-DD 00:00:00')
},
{
field: 'endTime',
operator: 'lte',
dataType: 'date',
value: dayjs(datetimePicker.value.value).endOf('month').format('YYYY-MM-DD 23:59:59')
}
].filter(Boolean)
}
};
const res = await getAisbdbyxList(params);
const data = res?.data ?? {};
facilities.value = [
{
name: '漂浮物识别',
count: data?.pfwCnt ?? 0,
icon: 'icon iconfont icon-piaofuwu'
},
{
name: '叠梁门运行识别',
count: data?.dlmCnt ?? 0,
icon: 'icon iconfont icon-dieliangmen1'
}
];
} catch (error) {
console.error('获取统计数据失败:', error);
facilities.value = [
{
name: '漂浮物识别',
count: 0,
icon: 'icon iconfont icon-piaofuwu'
},
{
name: '叠梁门运行识别',
count: 0,
icon: 'icon iconfont icon-dieliangmen1'
}
];
} finally {
dataLoading.value = false;
}
};
// ==================== ====================
watch(
() => JidiSelectEventStore.selectedItem,
async newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
getCardData();
}
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>
@ -118,4 +241,8 @@ onUnmounted(() => {
color: #2f6b98;
// font-weight: 600;
}
:deep(.ant-spin-nested-loading) {
min-height: 72px;
}
</style>

View File

@ -1,2 +1,6 @@
declare module 'jsencrypt'
declare module 'sortablejs'
declare module '*.svg' {
const content: string
export default content
}

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import { ref, onMounted } from 'vue';
import { ref, onMounted,nextTick } from 'vue';
import { wbsbGetKendoList } from '@/api/select';
export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
const jidiData: any = ref([
@ -12,7 +12,9 @@ export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
const loading = ref(false);
const selectedItem = ref(jidiData.value[0]);
const updataJidiData = (index: any) => {
console.log(index);
if(jidiData.value.length == 1){
return
}
selectedItem.value = jidiData.value[index];
jidiData.value.forEach((item: any) => {
item.selected = false;

View File

@ -54,7 +54,7 @@ const onMouseUp = () => {
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
if (newVal.wbsCode == '07') {
uiStore.setDrawerOpen(true);
@ -65,8 +65,17 @@ watch(
},
{ deep: true, immediate: true }
);
watch(
() => JidiSelectEventStore.jidiData,
(newData) => {
if (newData.length > 1) {
JidiSelectEventStore.updataJidiData(11);
}
},
{ immediate: true }
);
onMounted(() => {
JidiSelectEventStore.updataJidiData(11);
// JidiSelectEventStore.updataJidiData(11);
});
</script>
@ -82,7 +91,7 @@ onMounted(() => {
<FishVideo />
</RightDrawer>
</div>
<div class="fixedP" ref="fixedPRef">
<div class="fixedP" ref="fixedPRef" v-if="baseid == '07'">
<div class="zhuai" @mousedown="onMouseDown"></div>
<SurveyQingKuang />
<FishChangeTu />
@ -100,13 +109,14 @@ onMounted(() => {
padding: 0px 16px;
border: 2px solid rgb(197, 214, 226);
box-shadow: rgb(229, 237, 243) 0px 0px 3px 6px;
background-color: rgba(255, 255, 255, 0.411);
.zhuai {
width: 100%;
height: 16px;
cursor: grab;
cursor: grab;
user-select: none;
background-color: rgba(0, 0, 0, 0.5);
// background-color: rgba(0, 0, 0, 0.5);
}
.zhuai:active {
cursor: grabbing;

View File

@ -1,11 +1,21 @@
<script setup lang="ts">
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
import RightDrawer from "@/components/RightDrawer/index.vue";
import SSSTJCQKTT from "@/modules/shuishengshengtaijiance/index.vue"
import SSSTDCQK from "@/modules/shuiShengShengTaiDiaoCha/index.vue" //
// import SSSTJCQKTT from "@/modules/shuishengshengtaijiance"
import AlongFishMod from "@/modules/alongFishMod/index.vue" //沿
import { ref, computed } from 'vue';
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
import RightDrawer from '@/components/RightDrawer/index.vue';
import SSSTJCQKTT from '@/modules/shuishengshengtaijiance/index.vue';
import SSSTDCQK from '@/modules/shuiShengShengTaiDiaoCha/index.vue'; //
import AlongFishMod from '@/modules/alongFishMod/index.vue'; //沿
import SSSTDCDB from './shuiShengShengTaiDiaoCha/SSSTDCDB.vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
const JidiSelectEventStore = useJidiSelectEventStore();
const modalVisible = ref(false);
const currentItem = computed(() => JidiSelectEventStore.selectedItem);
const handleOpenModal = () => {
modalVisible.value = true;
};
</script>
<template>
@ -17,9 +27,58 @@ import AlongFishMod from "@/modules/alongFishMod/index.vue" //沿程鱼类变
<RightDrawer>
<SSSTJCQKTT />
<SSSTDCQK />
<AlongFishMod />
<AlongFishMod />
</RightDrawer>
</div>
<div class="center">
<div class="center_bottom" @click="handleOpenModal">
<a-tooltip placement="top">
<template #title>
<span>水生生态调查对比</span>
</template>
<i class="icon iconfont icon-contrast"></i>
</a-tooltip>
</div>
</div>
<!-- 水生生态调查对比弹窗 -->
<a-modal
v-model:open="modalVisible"
title="水生生态调查对比"
width="1536px"
:footer="null"
destroy-on-close
>
<SSSTDCDB v-if="modalVisible" :current-item="currentItem" />
</a-modal>
</div>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.center {
position: fixed;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
z-index: 999;
.center_bottom {
width: 48px;
height: 32px;
background: #005293;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
pointer-events: all;
i {
font-size: 16px;
color: #fff;
}
i:hover {
color: #40a9ff;
}
}
.center_bottom:hover {
border: 1px solid #40a9ff;
}
}
</style>

View File

@ -0,0 +1,441 @@
<script setup lang="ts">
import {
ref,
reactive,
watch,
onMounted,
onBeforeUnmount,
nextTick
} from 'vue';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import { getFishtKendoList, getCompareBar } from '@/api/stdc';
import BasicTable from '@/components/BasicTable/index.vue';
const props = defineProps<{
currentItem: any;
}>();
// ==================== ====================
const weLis = ref<any[]>([]);
const pcList = ref<any[]>([]);
const searchParam = reactive({
dcpc: '',
dcpcCompare: '',
stcd: ''
});
// ==================== ====================
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
const chartList = ref<any[]>([]);
const chartLoading = ref(true);
// ==================== ====================
const tabLoading = ref(false);
const tableRef = ref();
const column = ref([
{
key: 'ftp',
title: '鱼种类',
dataIndex: 'ftp',
fixed: 'left' as const,
width: 150,
ellipsis: true
},
{
key: 'genus',
title: '科属',
dataIndex: 'genus',
fixed: 'left' as const,
width: 150,
ellipsis: true
},
{
key: 'fcnt',
title: '第一批次',
dataIndex: 'fcnt',
width: 150,
ellipsis: true
},
{
key: 'fcnt2',
title: '对比批次',
dataIndex: 'fcnt2',
width: 150,
ellipsis: true
}
]);
// ==================== ====================
const buildTableFilter = () => {
const filters: any[] = [];
if (searchParam.stcd) {
filters.push({ field: 'stcd', operator: 'eq', value: searchParam.stcd });
}
if (searchParam.dcpc) {
filters.push({ field: 'dcpc', operator: 'eq', value: searchParam.dcpc });
}
if (searchParam.dcpcCompare) {
filters.push({
field: 'dcpc2',
operator: 'eq',
value: searchParam.dcpcCompare
});
}
//
column.value[2].title = searchParam.dcpc || '第一批次';
column.value[3].title = searchParam.dcpcCompare || '对比批次';
return {
logic: 'and',
filters
};
};
// ==================== ====================
const fetchPcData = async () => {
const baseid = props.currentItem?.wbsCode || props.currentItem?.baseid;
const params = {
filter: {
filters:
baseid === 'all'
? []
: [
{
field: 'baseId',
operator: 'eq',
value: baseid
}
]
},
group: [
{ dir: 'asc', field: 'stcd' },
{ dir: 'asc', field: 'stnm' },
{ dir: 'asc', field: 'dcpc' }
]
};
const res = await getFishtKendoList(params);
if (res?.data?.data?.length) {
const options: any[] = [];
res.data.data.forEach((el: any) => {
if (el?.items?.[0]?.items?.length > 1) {
options.push({
label: el?.items?.[0]?.key,
value: el?.key,
dcpc: el?.items?.[0]?.items?.map((item: any) => ({
label: item.key,
value: item.key
}))
});
}
});
weLis.value = options;
pcList.value = options?.[0]?.dcpc ?? [];
searchParam.stcd = options[0]?.value || '';
searchParam.dcpc = options[0]?.dcpc?.[0]?.value || '';
searchParam.dcpcCompare = options[0]?.dcpc?.[1]?.value || '';
tabLoading.value = true;
} else {
weLis.value = [];
pcList.value = [];
tabLoading.value = true;
}
};
// ==================== ====================
const handleStcdChange = (stcd: string) => {
if (!stcd) return;
const found = weLis.value.find((el: any) => el.value === stcd);
if (found) {
pcList.value = found.dcpc;
searchParam.stcd = found.value;
searchParam.dcpc = found.dcpc?.[0]?.value || '';
searchParam.dcpcCompare = found.dcpc?.[1]?.value || '';
}
};
// ==================== ====================
const fetchChartData = async () => {
chartLoading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: searchParam.stcd
},
{
field: 'dcpc',
operator: 'eq',
dataType: 'string',
value: searchParam.dcpc
},
{
field: 'dcpc2',
operator: 'eq',
dataType: 'string',
value: searchParam.dcpcCompare
}
]
}
};
const res: any = await getCompareBar(params);
// debugger
if (res?.data?.data?.[0]?.dataSource) {
chartList.value = res.data.data[0].dataSource;
} else {
chartList.value = [];
}
} catch (error) {
console.error('获取图表数据失败:', error);
chartList.value = [];
} finally {
chartLoading.value = false;
}
};
// ==================== ====================
const initChart = () => {
if (!chartRef.value) return;
if (!chartInstance) {
chartInstance = echarts.init(chartRef.value);
}
if (!chartList.value || chartList.value.length < 1) {
chartInstance.setOption({
title: { text: '', subtext: '' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: [] },
yAxis: { type: 'value' },
series: []
});
return;
}
const option = {
title: { text: '', subtext: '' },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
legend: {
data: [searchParam.dcpc, searchParam.dcpcCompare],
left: 'center',
show: true,
itemWidth: 12,
itemHeight: 8,
textStyle: { fontSize: 14 }
},
grid: {
top: '20%',
left: '5%',
right: 0,
bottom: 0,
containLabel: true
},
xAxis: {
type: 'category',
axisLabel: { rotate: 45 },
axisTick: { show: false },
data: chartList.value.map((e: any) => e.ftp)
},
yAxis: {
type: 'value',
name: '数量(尾)'
},
series: [
{
name: searchParam.dcpc,
type: 'bar',
barWidth: 5,
stack: 'dcpc',
data: chartList.value.map((e: any) => e.fcnt)
},
{
name: searchParam.dcpcCompare,
type: 'bar',
barWidth: 5,
stack: 'dcpcCompare',
data: chartList.value.map((e: any) => e.fcnt2)
}
]
};
chartInstance.setOption(option, true);
};
// ==================== ====================
const customTransform = (res: any) => {
return {
records: res?.data?.data?.[0].dataSource || [],
total: res?.data?.total || 0
};
};
// ==================== ====================
const refreshTable = () => {
const filter = buildTableFilter();
tableRef.value?.getList(filter);
};
// ==================== ====================
const handleSearch = () => {
fetchChartData();
nextTick(() => {
initChart();
refreshTable();
});
};
// ==================== ====================
watch(
() => [searchParam.dcpc, searchParam.dcpcCompare],
() => {
fetchChartData();
nextTick(() => {
initChart();
});
}
);
// ==================== ====================
watch(
() => chartList.value,
() => {
nextTick(() => {
initChart();
});
}
);
// ==================== resize ====================
const handleResize = () => {
chartInstance?.resize();
};
// ==================== ====================
onMounted(async () => {
await fetchPcData();
await nextTick();
fetchChartData();
await nextTick();
initChart();
await nextTick();
refreshTable();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
chartInstance = null;
});
</script>
<template>
<div class="ssstdcdb-page">
<!-- 搜索表单 -->
<div class="search-form">
<a-form-item label="调查断面">
<a-select
v-model:value="searchParam.stcd"
placeholder="请选择调查断面"
style="width: 200px; margin-right: 12px"
:options="weLis"
@change="handleStcdChange"
/>
</a-form-item>
<a-form-item label="调查批次">
<a-select
v-model:value="searchParam.dcpc"
placeholder="请选择调查批次"
style="width: 200px; margin-right: 12px"
:options="pcList"
/>
</a-form-item>
<a-form-item label="对比批次">
<a-select
v-model:value="searchParam.dcpcCompare"
placeholder="请选择对比批次"
style="width: 200px; margin-right: 12px"
:options="pcList"
/>
</a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
</div>
<!-- 图表 + 表格 -->
<div class="content-area">
<!-- 图表 -->
<div class="chart-section">
<a-spin :spinning="chartLoading">
<div ref="chartRef" class="chart-container" style="height: 500px"></div>
<a-empty
v-if="!chartLoading && chartList.length === 0"
class="chart-empty"
description="暂无数据"
/>
</a-spin>
</div>
<!-- 表格 -->
<BasicTable
ref="tableRef"
:columns="column"
:scroll-y="400"
:scroll-x="300"
:list-url="tabLoading ? getCompareBar : undefined"
:search-params="{ sort: [] }"
:transform-data="customTransform"
/>
</div>
</div>
</template>
<style scoped lang="scss">
.ssstdcdb-page {
// padding: 16px;
.search-form {
padding: 16px 0;
display: flex;
}
.content-area {
display: flex;
gap: 16px;
.chart-section {
width: 40%;
height: 500px;
position: relative;
}
.chart-empty {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
.table-section {
width: 60%;
height: 500px;
}
}
}
</style>

View File

@ -0,0 +1,36 @@
<script setup lang="ts">
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
import RightDrawer from '@/components/RightDrawer/index.vue';
import ShengtaidabiaoMod from '@/modules/shengtaidabiaoModJZ/index.vue'; // -
import ShengtaidabiaoTwoMod from '@/modules/shengtaidabiaoTwoModJJ/index.vue';
import StllyanchengChangeMod from '@/modules/stllyanchengChangeModJJ/index.vue' // -沿
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { ref, onMounted, watch } from 'vue';
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
watch(
() => JidiSelectEventStore.selectedItem,
newVal => {
if (newVal && newVal.wbsCode) {
baseid.value = newVal.wbsCode;
}
},
{ deep: true, immediate: true }
);
</script>
<template>
<div class="moduleContent">
<div class="leftContent">
<JidiSelectorMod />
</div>
<div class="rightContent">
<RightDrawer>
<ShengtaidabiaoMod />
<ShengtaidabiaoTwoMod v-if="baseid == 'all'" />
<StllyanchengChangeMod v-if="baseid != 'all'" />
</RightDrawer>
</div>
</div>
</template>
<style scoped lang="scss"></style>