This commit is contained in:
jingna 2025-07-02 13:42:32 +08:00
commit a4ec4eb0d4
193 changed files with 8922 additions and 2174 deletions

View File

@ -160,6 +160,16 @@
<artifactId>jcommander</artifactId>
<version>1.82</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -17,6 +17,7 @@ import jakarta.annotation.Resource;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -34,7 +35,7 @@ public class ChartDataController {
@Operation(summary = "获取图表数据")
@OperationLog(type = "01", module = "数据可视化", description = "获取图表数据")
@PostMapping("getData")
public ResponseResult getData(ChartViewDTO chartViewDTO) throws Exception {
public ResponseResult getData(@RequestBody ChartViewDTO chartViewDTO) throws Exception {
try {
// 从模板数据获取
DatasetUtils.viewDecode(chartViewDTO);

View File

@ -8,11 +8,13 @@ import com.stdproject.config.WebConfig;
import com.stdproject.entity.AppOptLog;
import com.stdproject.entity.LoginUser;
import com.stdproject.entity.User;
import com.stdproject.entity.vo.CurIpVO;
import com.stdproject.service.IAppOptLogService;
import com.stdproject.service.IUserService;
import com.stdproject.utils.JwtUtils;
import com.stdproject.utils.RsaUtils;
import com.stdproject.utils.commonUtils;
import io.gisbi.utils.IPUtils;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
@ -276,4 +278,13 @@ private Long jwtExpirationMs;
sysLogService.save(sysLog);
return ResponseResult.success();
}
@GetMapping("/ipInfo")
public CurIpVO ipInfo() {
CurIpVO curIpVO = new CurIpVO();
curIpVO.setAccount("admin");
curIpVO.setName("管理员");
curIpVO.setIp(IPUtils.get());
return curIpVO;
}
}

View File

@ -0,0 +1,68 @@
package com.stdproject.controller;
import com.stdproject.common.OperationLog;
import com.stdproject.config.ResponseResult;
import com.stdproject.entity.dto.LinkageInfoDTO;
import com.stdproject.entity.dto.VisualizationLinkJumpDTO;
import com.stdproject.entity.dto.VisualizationLinkJumpInfoDTO;
import com.stdproject.mapper.ExtVisualizationLinkJumpMapper;
import com.stdproject.mapper.ExtVisualizationLinkageMapper;
import com.stdproject.response.VisualizationLinkJumpBaseResponse;
import io.gisbi.constant.CommonConstants;
import io.gisbi.utils.AuthUtils;
import io.gisbi.utils.ModelUtils;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/linkJump")
public class VisualizationLinkJumpController {
@Resource
private ExtVisualizationLinkJumpMapper extVisualizationLinkJumpMapper;
@Operation(summary = "根据可视化资源ID查询跳转信息")
@OperationLog(type = "01", module = "数据可视化", description = "根据可视化资源ID查询跳转信息")
@GetMapping("/queryVisualizationJumpInfo/{dvId}/{resourceTable}")
public ResponseResult queryVisualizationJumpInfo(@PathVariable Long dvId,@PathVariable String resourceTable) throws Exception {
Map<String, VisualizationLinkJumpInfoDTO> resultBase = new HashMap<>();
List<VisualizationLinkJumpDTO> resultLinkJumpList = null;
// String userid=com.stdproject.utils.AuthUtils.getUserId();
String userid="1922852335260831746";
if (CommonConstants.RESOURCE_TABLE.SNAPSHOT.equals(resourceTable)) {
resultLinkJumpList = extVisualizationLinkJumpMapper.queryWithDvIdSnapshot(dvId, Long.parseLong(userid), false);
} else {
resultLinkJumpList = extVisualizationLinkJumpMapper.queryWithDvId(dvId,Long.parseLong(userid), false);
}
Optional.ofNullable(resultLinkJumpList).orElse(new ArrayList<>()).forEach(resultLinkJump -> {
if (resultLinkJump.getChecked()) {
Long sourceViewId = resultLinkJump.getSourceViewId();
Optional.ofNullable(resultLinkJump.getLinkJumpInfoArray()).orElse(new ArrayList<>()).forEach(linkJumpInfo -> {
if (linkJumpInfo.getChecked()) {
String sourceJumpInfo = sourceViewId + "#" + linkJumpInfo.getSourceFieldId();
// 内部仪表板跳转 需要设置好仪表板ID
if ("inner".equals(linkJumpInfo.getLinkType())) {
if (linkJumpInfo.getTargetDvId() != null) {
resultBase.put(sourceJumpInfo, linkJumpInfo);
}
} else {
// 外部跳转
resultBase.put(sourceJumpInfo, linkJumpInfo);
}
}
});
}
});
VisualizationLinkJumpBaseResponse result = new VisualizationLinkJumpBaseResponse(resultBase, null);
return ResponseResult.successData(result);
}
}

View File

@ -0,0 +1,56 @@
package com.stdproject.controller;
import com.stdproject.common.OperationLog;
import com.stdproject.config.ResponseResult;
import com.stdproject.entity.dto.LinkageInfoDTO;
import com.stdproject.mapper.ExtVisualizationLinkageMapper;
import com.stdproject.service.manage.ChartDataManage;
import com.stdproject.utils.DatasetUtils;
import io.gisbi.constant.CommonConstants;
import io.gisbi.exception.DEException;
import io.gisbi.extensions.view.dto.ChartViewDTO;
import io.gisbi.result.ResultCode;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/linkage")
public class VisualizationLinkageController {
@Resource
private ExtVisualizationLinkageMapper extVisualizationLinkageMapper;
@Operation(summary = "根据资源ID查询联动信息")
@OperationLog(type = "01", module = "数据可视化", description = "根据资源ID查询联动信息")
@GetMapping("/getVisualizationAllLinkageInfo/{dvId}/{resourceTable}")
public ResponseResult getVisualizationAllLinkageInfo(@PathVariable Long dvId,@PathVariable String resourceTable) throws Exception {
List<LinkageInfoDTO> info = null;
if (CommonConstants.RESOURCE_TABLE.SNAPSHOT.equals(resourceTable)) {
info = extVisualizationLinkageMapper.getPanelAllLinkageInfoSnapshot(dvId);
}else{
info = extVisualizationLinkageMapper.getPanelAllLinkageInfo(dvId);
}
info = info.stream().map(item -> {
item.setTargetInfoList(item.getTargetInfoList().stream().map(targetInfo -> {
if (targetInfo.contains(".")) {
String[] split = targetInfo.split("\\.");
if (split.length == 2) {
targetInfo = split[1];
}
}
return targetInfo;
}).collect(Collectors.toList()));
return item;
}).collect(Collectors.toList());
return ResponseResult.successData( info);
}
}

View File

@ -0,0 +1,18 @@
package com.stdproject.entity.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class CurIpVO implements Serializable {
@Serial
private static final long serialVersionUID = -3025566841330382707L;
private String account;
private String name;
private String ip;
}

View File

@ -17,41 +17,5 @@ public interface ExtVisualizationLinkJumpMapper {
List<VisualizationLinkJumpDTO> queryWithDvIdSnapshot(@Param("dvId") Long dvId,@Param("uid") Long uid,@Param("isDesktop") Boolean isDesktop);
VisualizationLinkJumpDTO queryWithViewId(@Param("dvId") Long dvId,@Param("viewId") Long viewId,@Param("uid") Long uid,@Param("isDesktop") Boolean isDesktop);
void deleteJumpTargetViewInfoSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId);
void deleteJumpInfoSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId);
void deleteJumpSnapshot(@Param("dvId") Long dvId,@Param("viewId") Long viewId);
void deleteJumpTargetViewInfoWithVisualization(@Param("dvId") Long dvId);
void deleteJumpInfoWithVisualization(@Param("dvId") Long dvId);
void deleteJumpWithVisualization(@Param("dvId") Long dvId);
void deleteJumpTargetViewInfoWithVisualizationSnapshot(@Param("dvId") Long dvId);
void deleteJumpInfoWithVisualizationSnapshot(@Param("dvId") Long dvId);
void deleteJumpWithVisualizationSnapshot(@Param("dvId") Long dvId);
List<VisualizationLinkJumpDTO> getTargetVisualizationJumpInfo(@Param("request") VisualizationLinkJumpBaseRequest request);
List<VisualizationLinkJumpDTO> getTargetVisualizationJumpInfoSnapshot(@Param("request") VisualizationLinkJumpBaseRequest request);
void copyLinkJump(@Param("copyId")Long copyId);
void copyLinkJumpInfo(@Param("copyId")Long copyId);
void copyLinkJumpTarget(@Param("copyId")Long copyId);
List<VisualizationLinkJumpVO> findLinkJumpWithDvId(@Param("dvId")Long dvId);
List<VisualizationLinkJumpInfoVO> findLinkJumpInfoWithDvId(@Param("dvId")Long dvId);
List<VisualizationViewTableVO> getViewTableDetails(@Param("dvId")Long dvId);
List<VisualizationOutParamsJumpVO> queryOutParamsTargetWithDvId(@Param("dvId")Long dvId);
}

View File

@ -13,32 +13,6 @@ import java.util.List;
@Mapper
public interface ExtVisualizationLinkageMapper {
List<VisualizationLinkageDTO> getViewLinkageGather(@Param("dvId") Long dvId, @Param("sourceViewId") Long sourceViewId, @Param("targetViewIds") List<String> targetViewIds);
List<LinkageInfoDTO> getPanelAllLinkageInfo(@Param("dvId") Long dvId);
List<VisualizationLinkageDTO> getViewLinkageGatherSnapshot(@Param("dvId") Long dvId, @Param("sourceViewId") Long sourceViewId, @Param("targetViewIds") List<String> targetViewIds);
List<LinkageInfoDTO> getPanelAllLinkageInfoSnapshot(@Param("dvId") Long dvId);
List<DatasetTableFieldDTO> queryTableField(@Param("table_id") Long tableId);
List<DatasetTableFieldDTO> queryTableFieldWithViewId(@Param("viewId") Long viewId);
void deleteViewLinkage(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId);
void deleteViewLinkageField(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId);
void deleteViewLinkageSnapshot(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId);
void deleteViewLinkageFieldSnapshot(@Param("dvId") Long dvId,@Param("sourceViewId") Long sourceViewId);
void copyViewLinkage(@Param("copyId") Long copyId);
void copyViewLinkageField(@Param("copyId") Long copyId);
List<VisualizationLinkage> findLinkageWithDvId(@Param("dvId") Long dvId);
List<VisualizationLinkageField> findLinkageFieldWithDvId(@Param("dvId") Long dvId);
List<LinkageInfoDTO> getPanelAllLinkageInfo(@Param("dvId") Long dvId);
List<LinkageInfoDTO> getPanelAllLinkageInfoSnapshot(@Param("dvId") Long dvId);
}

View File

@ -251,9 +251,7 @@ public class ChartViewManege {
ChartFieldCompareDTO chartFieldCompareDTO = new ChartFieldCompareDTO();
chartFieldCompareDTO.setType("none");
dto.setCompareCalc(chartFieldCompareDTO);
dto.setFormatterCfg(new FormatterCfgDTO().setUnitLanguage(Lang.isChinese() ? "ch" : "en"));
dto.setFormatterCfg(new FormatterCfgDTO());
dto.setSort("none");
dto.setFilter(Collections.emptyList());
return dto;

View File

@ -8,10 +8,7 @@ import com.stdproject.entity.CoreDatasource;
import com.stdproject.entity.union.*;
import com.stdproject.mapper.CoreDatasetGroupMapper;
import com.stdproject.mapper.CoreDatasourceMapper;
import com.stdproject.utils.DatasetTableTypeConstants;
import com.stdproject.utils.SqlUtils;
import com.stdproject.utils.SqlparserUtils;
import com.stdproject.utils.TableUtils;
import com.stdproject.utils.*;
import io.gisbi.constant.SQLConstants;
import io.gisbi.exception.DEException;
import io.gisbi.extensions.datasource.api.PluginManageApi;
@ -480,8 +477,8 @@ public class DatasetSQLManage {
if (coreDatasource.getType().contains(DatasourceConfiguration.DatasourceType.Excel.name()) || coreDatasource.getType().contains(DatasourceConfiguration.DatasourceType.API.name())) {
coreDatasource = engineManage.getDeEngine();
}
Map map = JsonUtil.parseObject(coreDatasource.getConfiguration(), Map.class);
String config= String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration()));
Map map=JsonUtil.parseObject(config, Map.class);
if (!isCross && ObjectUtils.isNotEmpty(map.get("schema"))) {
schemaAlias = (String) map.get("schema");
} else {

View File

@ -478,8 +478,8 @@ public class CalciteProvider extends Provider {
public Map<String, Object> jdbcFetchResultField(DatasourceRequest datasourceRequest) throws BusinessException {
DatasourceSchemaDTO value = datasourceRequest.getDsList().entrySet().iterator().next().getValue();
datasourceRequest.setDatasource(value);
DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), DatasourceConfiguration.class);
String config=String.valueOf(EncryptUtils.aesDecrypt(datasourceRequest.getDatasource().getConfiguration()));
DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(config, DatasourceConfiguration.class);
Map<String, Object> map = new LinkedHashMap<>();
List<TableField> fieldList = new ArrayList<>();

View File

@ -1,8 +1,14 @@
package com.stdproject.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
@ -15,8 +21,11 @@ import java.security.spec.X509EncodedKeySpec;
* @date 2020-05-18
**/
public class RsaUtils {
public static final String IV_KEY = "0000000000000000";
private static final String SRC = "123456";
private static final String ALGORITHM = "AES";
public static String symmetricKey = null;
private static final int KEY_SIZE = 128;
public static void main(String[] args) throws Exception {
System.out.println("\n");
@ -30,6 +39,7 @@ public class RsaUtils {
System.out.println("\n");
}
/**
@ -69,6 +79,8 @@ public class RsaUtils {
System.out.println("***************** 私钥加密公钥解密结束 *****************");
}
/**
* 公钥解密
*
@ -156,8 +168,61 @@ public class RsaUtils {
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
}
/**
* 生成AES对称加密密钥
* @return Base64编码的密钥字符串
*/
public static String generateSymmetricKey() {
try {
if (StringUtils.isEmpty(symmetricKey)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(KEY_SIZE, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
symmetricKey = java.util.Base64.getEncoder().encodeToString(secretKey.getEncoded());
}
return symmetricKey;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 使用AES对称密钥进行加密
* @param data 待加密的数据
* @return 加密后的Base64编码字符串
*/
public static String symmetricEncrypt(String data) {
try {
byte[] iv = IV_KEY.getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(java.util.Base64.getDecoder().decode(generateSymmetricKey()), ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] ciphertext = cipher.doFinal(data.getBytes("UTF-8"));
return java.util.Base64.getEncoder().encodeToString(ciphertext);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 使用AES对称密钥进行解密
* @param data 待解密的Base64编码字符串
* @return 解密后的原始数据
*/
public static String symmetricDecrypt(String data) {
try {
byte[] iv = IV_KEY.getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(java.util.Base64.getDecoder().decode(generateSymmetricKey()), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decodedCiphertext = java.util.Base64.getDecoder().decode(data);
byte[] decryptedText = cipher.doFinal(decodedCiphertext);
return new String(decryptedText, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* RSA密钥对对象
*/

View File

@ -279,7 +279,8 @@ public class Utils {
public static String replaceSchemaAlias(String sql, Map<Long, DatasourceSchemaDTO> dsMap) {
DatasourceSchemaDTO value = dsMap.entrySet().iterator().next().getValue();
Map map = JsonUtil.parseObject(value.getConfiguration(), Map.class);
String config= String.valueOf(EncryptUtils.aesDecrypt(value.getConfiguration()));
Map map = JsonUtil.parseObject(config, Map.class);
if (ObjectUtils.isNotEmpty(map.get("schema"))) {
return sql;
}

View File

@ -19,9 +19,9 @@ spring:
main:
allow-bean-definition-overriding: true
datasource:
url: ${DB_URL:jdbc:mysql://121.37.111.42:3306/gisbi-demodb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true}
url: ${DB_URL:jdbc:mysql://192.168.1.58:3306/gisbi-demodb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true}
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:mysql_F8ysiK@2024}
password: ${DB_PASSWORD:123456}
driver-class-name: com.mysql.cj.jdbc.Driver
# HikariCP连接池配置
hikari:
@ -33,7 +33,13 @@ spring:
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
flyway:
enabled: true
table: flyway_schema_history
validate-on-migrate: true
locations: classpath:db/migration
baseline-on-migrate: true
out-of-order: true
cache:
jcache:
config: classpath:ehcache.xml # 指定Ehcache配置文件路径
@ -80,6 +86,7 @@ logging:
org.springframework.security: ${LOG_LEVEL_SECURITY:WARN}
org.hibernate.SQL: ${LOG_LEVEL_SQL:WARN}
org.hibernate.type.descriptor.sql.BasicBinder: ${LOG_LEVEL_SQL_PARAMS:WARN}
org.flywaydb: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
@ -160,6 +167,7 @@ spring:
security:
jwt:
enabled: true
logging:
level:
root: WARN

View File

@ -124,7 +124,7 @@
<result column="table_id" property="tableId"/>
<result column="type" property="type"/>
<result column="render" property="render"/>
<collection property="tableFields" ofType="com.stdproject.entity.vo.CoreDatasetTableFieldVO">
<collection property="tableFields" ofType="io.gisbi.extensions.datasource.dto.DatasetTableFieldDTO">
<result column="field_id" jdbcType="VARCHAR" property="id"/>
<result column="origin_name" jdbcType="VARCHAR" property="originName"/>
<result column="field_name" jdbcType="VARCHAR" property="name"/>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stdproject.mapper.ExtVisualizationLinkJumpMapper">
<resultMap id="LinkJumpBaseResultMap" type="com.stdproject.entity.vo.VisualizationLinkJumpVO">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="source_dv_id" jdbcType="BIGINT" property="sourceDvId"/>
<result column="source_view_id" jdbcType="BIGINT" property="sourceViewId"/>
<result column="link_jump_info" jdbcType="VARCHAR" property="linkJumpInfo"/>
<result column="checked" jdbcType="BIT" property="checked"/>
<result column="copy_from" jdbcType="BIGINT" property="copyFrom"/>
<result column="copy_id" jdbcType="BIGINT" property="copyId"/>
</resultMap>
<resultMap id="LinkJumpInfoBaseResultMap" type="com.stdproject.entity.vo.VisualizationLinkJumpInfoVO">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="link_jump_id" jdbcType="BIGINT" property="linkJumpId"/>
<result column="link_type" jdbcType="VARCHAR" property="linkType"/>
<result column="jump_type" jdbcType="VARCHAR" property="jumpType"/>
<result column="window_size" jdbcType="VARCHAR" property="windowSize"/>
<result column="target_dv_id" jdbcType="BIGINT" property="targetDvId"/>
<result column="source_field_id" jdbcType="BIGINT" property="sourceFieldId"/>
<result column="content" jdbcType="VARCHAR" property="content"/>
<result column="checked" jdbcType="BIT" property="checked"/>
<result column="attach_params" jdbcType="BIT" property="attachParams"/>
<result column="copy_from" jdbcType="BIGINT" property="copyFrom"/>
<result column="copy_id" jdbcType="BIGINT" property="copyId"/>
</resultMap>
<resultMap id="BaseResultMapDTO" type="com.stdproject.entity.dto.VisualizationLinkJumpDTO"
extends="LinkJumpBaseResultMap">
<collection property="linkJumpInfoArray" ofType="com.stdproject.entity.dto.VisualizationLinkJumpInfoDTO"
column="{id=id,source_view_id=source_view_id,uid=queryUid, isDesktop=isDesktop}"
select="getLinkJumpInfo">
</collection>
</resultMap>
<resultMap id="BaseResultMapDTOSnapshot" type="com.stdproject.entity.dto.VisualizationLinkJumpDTO"
extends="LinkJumpBaseResultMap">
<collection property="linkJumpInfoArray" ofType="com.stdproject.entity.dto.VisualizationLinkJumpInfoDTO"
column="{id=id,source_view_id=source_view_id,uid=queryUid, isDesktop=isDesktop}"
select="getLinkJumpInfoSnapshot">
</collection>
</resultMap>
<resultMap id="LinkJumpInfoMap" type="com.stdproject.entity.dto.VisualizationLinkJumpInfoDTO"
extends="LinkJumpInfoBaseResultMap">
<result column="target_dv_type" jdbcType="VARCHAR" property="targetDvType"/>
<result column="source_field_id" jdbcType="BIGINT" property="sourceFieldId"/>
<result column="source_de_type" jdbcType="INTEGER" property="sourceDeType"/>
<result column="source_field_name" jdbcType="VARCHAR" property="sourceFieldName"/>
<result column="publicJumpId" jdbcType="VARCHAR" property="publicJumpId"/>
<collection property="targetViewInfoList"
ofType="com.stdproject.entity.vo.VisualizationLinkJumpTargetViewInfoVO">
<result column="target_id" jdbcType="BIGINT" property="targetId"/>
<result column="target_view_id" jdbcType="BIGINT" property="targetViewId"/>
<result column="target_field_id" jdbcType="BIGINT" property="targetFieldId"/>
<result column="source_field_active_id" jdbcType="VARCHAR" property="sourceFieldActiveId"/>
<result column="target_type" jdbcType="VARCHAR" property="targetType"/>
<result column="outer_params_name" jdbcType="VARCHAR" property="outerParamsName"/>
</collection>
</resultMap>
<resultMap id="ViewTableFieldDetailsMap" type="com.stdproject.entity.vo.VisualizationViewTableVO">
<result column="id" jdbcType="BIGINT" property="id"/>
<result column="title" jdbcType="VARCHAR" property="title"/>
<result column="type" jdbcType="VARCHAR" property="type"/>
<result column="dv_id" jdbcType="BIGINT" property="dvId"/>
<collection property="tableFields"
ofType="io.gisbi.extensions.datasource.dto.DatasetTableFieldDTO">
<result column="field_id" jdbcType="BIGINT" property="id"/>
<result column="origin_name" jdbcType="VARCHAR" property="originName"/>
<result column="field_name" jdbcType="VARCHAR" property="name"/>
<result column="field_type" jdbcType="VARCHAR" property="type"/>
<result column="de_type" jdbcType="VARCHAR" property="deType"/>
</collection>
</resultMap>
<resultMap id="AllJumpMap" type="com.stdproject.entity.dto.VisualizationLinkJumpDTO">
<result column="sourceInfo" jdbcType="VARCHAR" property="sourceInfo"/>
<collection property="targetInfoList" ofType="String">
<result column="targetInfo" jdbcType="VARCHAR"/>
</collection>
</resultMap>
<select id="queryWithDvId" resultMap="BaseResultMapDTO">
SELECT ccv.id AS source_view_id,
#{uid} as queryUid,
#{isDesktop} as isDesktop,
vlj.id,
#{dvId} as source_dv_id, vlj.link_jump_info,
ifnull(ccv.jump_active, 0) AS checked
FROM core_chart_view ccv
LEFT JOIN visualization_link_jump vlj ON ccv.id = vlj.source_view_id
WHERE vlj.source_dv_id = #{dvId}
and ccv.jump_active = 1
</select>
<select id="queryWithDvIdSnapshot" resultMap="BaseResultMapDTOSnapshot">
SELECT ccv.id AS source_view_id,
#{uid} as queryUid,
#{isDesktop} as isDesktop,
vlj.id,
#{dvId} as source_dv_id, vlj.link_jump_info,
ifnull(ccv.jump_active, 0) AS checked
FROM snapshot_core_chart_view ccv
LEFT JOIN snapshot_visualization_link_jump vlj ON ccv.id = vlj.source_view_id
WHERE vlj.source_dv_id = #{dvId}
and ccv.jump_active = 1
</select>
</mapper>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stdproject.mapper.ExtVisualizationLinkageMapper">
<resultMap id="AllLinkageMap" type="com.stdproject.entity.dto.LinkageInfoDTO">
<result column="sourceInfo" jdbcType="VARCHAR" property="sourceInfo"/>
<collection property="targetInfoList" ofType="String">
<result column="targetInfo" jdbcType="VARCHAR"/>
</collection>
</resultMap>
<select id="getPanelAllLinkageInfo" resultMap="AllLinkageMap">
SELECT DISTINCT
CONCAT( vl.source_view_id, '#', vlf.source_field ) AS sourceInfo,
CONCAT( vl.target_view_id, '#', vlf.target_field ) AS targetInfo
FROM
visualization_linkage vl
LEFT JOIN core_chart_view ccv ON vl.source_view_id = ccv.id
LEFT JOIN visualization_linkage_field vlf ON vl.id = vlf.linkage_id
WHERE
vl.dv_id = #{dvId}
AND ccv.linkage_active = 1
AND vl.linkage_active = 1
AND vlf.id IS NOT NULL
</select>
<select id="getPanelAllLinkageInfoSnapshot" resultMap="AllLinkageMap">
SELECT DISTINCT
CONCAT( vl.source_view_id, '#', vlf.source_field ) AS sourceInfo,
CONCAT( vl.target_view_id, '#', vlf.target_field ) AS targetInfo
FROM
snapshot_visualization_linkage vl
LEFT JOIN snapshot_core_chart_view ccv ON vl.source_view_id = ccv.id
LEFT JOIN snapshot_visualization_linkage_field vlf ON vl.id = vlf.linkage_id
WHERE
vl.dv_id = #{dvId}
AND ccv.linkage_active = 1
AND vl.linkage_active = 1
AND vlf.id IS NOT NULL
</select>
</mapper>

View File

@ -9,10 +9,48 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ElCard: typeof import('element-plus-secondary/es')['ElCard']
ElBreadcrumb: typeof import('element-plus-secondary/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus-secondary/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus-secondary/es')['ElButton']
ElCheckbox: typeof import('element-plus-secondary/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus-secondary/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus-secondary/es')['ElCol']
ElCollapse: typeof import('element-plus-secondary/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus-secondary/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus-secondary/es')['ElColorPicker']
ElContainer: typeof import('element-plus-secondary/es')['ElContainer']
ElDatePicker: typeof import('element-plus-secondary/es')['ElDatePicker']
ElDialog: typeof import('element-plus-secondary/es')['ElDialog']
ElDivider: typeof import('element-plus-secondary/es')['ElDivider']
ElDropdown: typeof import('element-plus-secondary/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus-secondary/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus-secondary/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus-secondary/es')['ElEmpty']
ElForm: typeof import('element-plus-secondary/es')['ElForm']
ElFormItem: typeof import('element-plus-secondary/es')['ElFormItem']
ElHeader: typeof import('element-plus-secondary/es')['ElHeader']
ElIcon: typeof import('element-plus-secondary/es')['ElIcon']
ElTimeline: typeof import('element-plus-secondary/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus-secondary/es')['ElTimelineItem']
ElInput: typeof import('element-plus-secondary/es')['ElInput']
ElInputNumber: typeof import('element-plus-secondary/es')['ElInputNumber']
ElMain: typeof import('element-plus-secondary/es')['ElMain']
ElOption: typeof import('element-plus-secondary/es')['ElOption']
ElOptionGroup: typeof import('element-plus-secondary/es')['ElOptionGroup']
ElPopover: typeof import('element-plus-secondary/es')['ElPopover']
ElRadio: typeof import('element-plus-secondary/es')['ElRadio']
ElRadioGroup: typeof import('element-plus-secondary/es')['ElRadioGroup']
ElRow: typeof import('element-plus-secondary/es')['ElRow']
ElScrollbar: typeof import('element-plus-secondary/es')['ElScrollbar']
ElSelect: typeof import('element-plus-secondary/es')['ElSelect']
ElSelectV2: typeof import('element-plus-secondary/es')['ElSelectV2']
ElSpace: typeof import('element-plus-secondary/es')['ElSpace']
ElSwitch: typeof import('element-plus-secondary/es')['ElSwitch']
ElTabPane: typeof import('element-plus-secondary/es')['ElTabPane']
ElTabs: typeof import('element-plus-secondary/es')['ElTabs']
ElTimePicker: typeof import('element-plus-secondary/es')['ElTimePicker']
ElTooltip: typeof import('element-plus-secondary/es')['ElTooltip']
ElTree: typeof import('element-plus-secondary/es')['ElTree']
ElTreeSelect: typeof import('element-plus-secondary/es')['ElTreeSelect']
ElUpload: typeof import('element-plus-secondary/es')['ElUpload']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}

View File

@ -1,4 +1,6 @@
import request from '@/data-visualization/config/axios'
import { originNameHandleWithArr, originNameHandleBackWithArr } from '@/data-visualization/utils/CalculateFields'
import { cloneDeep } from 'lodash-es'
export interface Field {
id: number | string
datasourceId: number | string
@ -6,7 +8,7 @@ export interface Field {
datasetGroupId: number | string
originName: string
name: string
dataeaseName: string
gisbiName: string
groupType: string
type: string
deType: number
@ -27,6 +29,7 @@ export interface ComponentInfo {
export const getFieldByDQ = async (id, chartId, data): Promise<IResponse> => {
return request.post({ url: `/chart/listByDQ/${id}/${chartId}`, data: data }).then(res => {
originNameHandleBackWithArr(res?.data, ['dimensionList', 'quotaList'])
return res?.data
})
}
@ -52,10 +55,33 @@ export const deleteChartFieldByChartId = async (chartId): Promise<IResponse> =>
// 通过图表对象获取数据
export const getData = async (data): Promise<IResponse> => {
delete data.data
return request.post({ url: '/chartData/getData', data }).then(res => {
if (res.code === 0) {
const copyData = cloneDeep(data)
const fields = [
'xAxis',
'xAxisExt',
'yAxis',
'yAxisExt',
'extBubble',
'extLabel',
'extStack',
'extTooltip'
]
const dataFields = ['fields', 'sourceFields']
originNameHandleWithArr(copyData, fields)
return request.post({ url: '/chartData/getData', data: copyData }).then(res => {
if (res.code == 0) {
originNameHandleBackWithArr(res?.data, fields)
// 动态计算字段在数据中,也需要转码
originNameHandleWithArr(res?.data?.data, dataFields)
originNameHandleBackWithArr(res?.data?.data, dataFields)
originNameHandleBackWithArr(res?.data?.data?.left, ['fields'])
originNameHandleBackWithArr(res?.data?.data?.right, ['fields'])
return res?.data
} else {
originNameHandleBackWithArr(res, fields)
originNameHandleBackWithArr(res?.data, dataFields)
originNameHandleBackWithArr(res?.data?.left, ['fields'])
originNameHandleBackWithArr(res?.data?.right, ['fields'])
return res
}
})

View File

@ -1,9 +1,17 @@
import request from '@/utils/request'
import { type Field } from '@/api/data-visualization/chart'
import {
originNameHandle,
originNameHandleBack,
originNameHandleBackWithArr
} from '@/data-visualization/utils/CalculateFields'
import { cloneDeep } from 'lodash-es'
import { nameTrim } from '@/data-visualization/utils/utils'
export interface DatasetOrFolder {
name: string
action?: string
isCross?: boolean
id?: number | string
pid?: number | string
appId?: number | string
@ -57,6 +65,7 @@ export interface Dataset {
id: string
pid: string
name: string
isCross?: boolean
union?: Array<{}>
allFields?: Array<{}>
}
@ -72,7 +81,12 @@ export interface Table {
// edit
export const saveDatasetTree = async (data: DatasetOrFolder): Promise<IResponse> => {
nameTrim(data)
return request.post({ url: '/datasetTree/save', data }).then(res => {
const copyData = cloneDeep(data)
originNameHandle(copyData.allFields)
return request.post({ url: '/datasetTree/save', data: copyData }).then(res => {
if (res?.data?.allFields?.length) {
originNameHandleBack(res?.data?.allFields)
}
return res?.data
})
}
@ -80,7 +94,12 @@ export const saveDatasetTree = async (data: DatasetOrFolder): Promise<IResponse>
// create
export const createDatasetTree = async (data: DatasetOrFolder): Promise<IResponse> => {
nameTrim(data)
return request.post({ url: '/datasetTree/create', data }).then(res => {
const copyData = cloneDeep(data)
originNameHandle(copyData.allFields)
return request.post({ url: '/datasetTree/create', data: copyData }).then(res => {
if (res?.data?.allFields?.length) {
originNameHandleBack(res?.data?.allFields)
}
return res?.data
})
}
@ -135,7 +154,8 @@ export const exportDatasetData = (data = {}) => {
url: '/datasetTree/exportDataset',
method: 'post',
data: data,
loading: true
loading: true,
responseType: 'blob'
})
}
@ -177,7 +197,16 @@ export const getTableField = async (data): Promise<IResponse> => {
}
export const getPreviewData = async (data): Promise<IResponse> => {
return request.post({ url: '/datasetData/previewData', data }).then(res => {
const copyData = cloneDeep(data)
originNameHandle(copyData.allFields)
return request.post({ url: '/datasetData/previewData', data: copyData }).then(res => {
if (res?.data?.allFields?.length) {
originNameHandleBack(res?.data?.allFields)
}
if (res?.data?.data?.fields?.length) {
originNameHandleBack(res?.data?.data?.fields)
}
return res?.data
})
}
@ -196,6 +225,9 @@ export const getDatasetTotal = async (id): Promise<FieldData> => {
export const getDatasetDetails = async (id): Promise<Dataset> => {
return request.post({ url: `/datasetTree/details/${id}`, data: {} }).then(res => {
if (res?.data?.allFields?.length) {
originNameHandleBack(res?.data?.allFields)
}
return res?.data
})
}
@ -219,6 +251,9 @@ export const getDsDetails = async (data): Promise<DatasetDetail[]> => {
}
export const getDsDetailsWithPerm = async (data): Promise<DatasetDetail[]> => {
return request.post({ url: '/datasetTree/detailWithPerm', data }).then(res => {
;(res?.data || []).forEach(ele => {
originNameHandleBackWithArr(ele, ['dimensionList', 'quotaList'])
})
return res?.data
})
}
@ -236,15 +271,22 @@ export const columnPermissionList = (page: number, limit: number, datasetId: num
export const rowPermissionTargetObjList = (datasetId: number, type: string) =>
request.get({ url: '/dataset/rowPermissions/authObjs/' + datasetId + '/' + type })
export const listFieldByDatasetGroup = (datasetId: number) =>
request.post({ url: '/datasetField/listByDatasetGroup/' + datasetId })
export const listFieldByDatasetGroup = (datasetId: number) => {
return request.post({ url: '/datasetField/listByDatasetGroup/' + datasetId }).then(res => {
originNameHandleBack(res?.data)
return res
})
}
export const multFieldValuesForPermissions = (data = {}) => {
return request.post({ url: '/datasetField/multFieldValuesForPermissions', data })
}
export const listFieldsWithPermissions = (datasetId: number) => {
return request.get({ url: '/datasetField/listWithPermissions/' + datasetId })
return request.get({ url: '/datasetField/listWithPermissions/' + datasetId }).then(res => {
originNameHandleBack(res?.data)
return res
})
}
export const copilotFields = (datasetId: number) => {
@ -301,11 +343,11 @@ export const getFunction = async (): Promise<DatasetDetail[]> => {
})
}
export const exportTasks = async (type): Promise<IResponse> => {
return request.post({ url: '/exportCenter/exportTasks/' + type, data: {} }).then(res => {
return res
})
}
export const exportTasksRecords = () =>
request.post({ url: `/exportCenter/exportTasks/records`, data: {} })
export const exportTasks = (page: number, limit: number, status: string) =>
request.post({ url: `/exportCenter/exportTasks/${status}/${page}/${limit}`, data: {} })
export const exportRetry = async (id): Promise<IResponse> => {
return request.post({ url: '/exportCenter/retry/' + id, data: {} }).then(res => {
@ -325,6 +367,12 @@ export const exportDelete = async (id): Promise<IResponse> => {
})
}
export const generateDownloadUri = async (id): Promise<IResponse> => {
return request.get({ url: '/exportCenter/generateDownloadUri/' + id }).then(res => {
return res?.data
})
}
export const exportDeleteAll = async (type, data): Promise<IResponse> => {
return request.post({ url: '/exportCenter/deleteAll/' + type, data }).then(res => {
return res?.data

View File

@ -1,10 +1,14 @@
import request from '@/data-visualization/config/axios'
import { originNameHandleWithArr } from '@/data-visualization/utils/CalculateFields'
import { cloneDeep } from 'lodash-es'
export interface ResourceOrFolder {
name: string
id?: number | string
pid?: number | string
nodeType: 'folder' | 'leaf'
type: string
mobileLayout: boolean
status: boolean
}
export interface Panel {
@ -20,11 +24,16 @@ export const findCopyResource = async (dvId, busiFlag): Promise<IResponse> => {
}
export const findById = async (
dvId:any,
busiFlag:any,
dvId,
busiFlag,
attachInfo = { source: 'main', taskId: null }
): Promise<IResponse> => {
let busiFlagResult = busiFlag
if (!busiFlagResult) {
await findDvType(dvId).then(res => {
busiFlagResult = res.data
})
}
const data = { id: dvId, busiFlag: busiFlagResult, ...attachInfo }
return request.post({ url: '/dataVisualization/findById', data })
}
@ -60,8 +69,24 @@ export const appCanvasNameCheck = async data =>
export const updateBase = data => request.post({ url: '/dataVisualization/updateBase', data })
export const updateCanvas = data =>
request.post({ url: '/dataVisualization/updateCanvas', data, loading: true })
export const updateCanvas = data => {
const copyData = cloneDeep(data)
const fields = [
'xAxis',
'xAxisExt',
'yAxis',
'yAxisExt',
'extBubble',
'extLabel',
'extStack',
'extTooltip'
]
for (const key in copyData.canvasViewInfo) {
originNameHandleWithArr(copyData.canvasViewInfo[key], fields)
}
return request.post({ url: '/dataVisualization/updateCanvas', data: copyData, loading: true })
}
export const moveResource = data => request.post({ url: '/dataVisualization/move', data })

View File

@ -26,9 +26,9 @@ export function queryTargetVisualizationJumpInfo(requestInfo) {
})
}
export function queryVisualizationJumpInfo(dvId) {
export function queryVisualizationJumpInfo(dvId, resourceTable = 'snapshot') {
return request.get({
url: '/linkJump/queryVisualizationJumpInfo/' + dvId,
url: '/linkJump/queryVisualizationJumpInfo/' + dvId + '/' + resourceTable,
loading: false
})
}

View File

@ -8,8 +8,8 @@ export const getViewLinkageGatherArray = data =>
export const saveLinkage = data => request.post({ url: '/linkage/saveLinkage', data })
export const getPanelAllLinkageInfo = dvId =>
request.get({ url: '/linkage/getVisualizationAllLinkageInfo/' + dvId })
export const getPanelAllLinkageInfo = (dvId, resourceTable = 'snapshot') =>
request.get({ url: '/linkage/getVisualizationAllLinkageInfo/' + dvId + '/' + resourceTable })
export const updateLinkageActive = data =>
request.post({ url: '/linkage/updateLinkageActive', data })

View File

@ -1,17 +1,17 @@
<svg width="300" height="70" xmlns="http://www.w3.org/2000/svg" fill="none">
<g>
<title>Layer 1</title>
<path id="svg_7" fill="#426DF2" d="m878.59,121.25l0,14.8l-47.12,0l-0.1,2.69c0,5.64 2.25,8.91 5.71,10.13c2.987,0.797 6.069,1.18 9.16,1.14l2.06,0c9.16,0 22.54,-2.06 32.15,-4l0.15,0l-3.87,20.91c0,0.26 -4.5,2.17 -13.25,3.59c-6.285,0.651 -12.601,0.959 -18.92,0.92c-18.313,0 -30.14,-6.183 -35.48,-18.55c-2,-4.88 -3.82,-10 -3.82,-15.31l0,-20c0,-14.65 7,-24.4101 19.31,-29.2501c5.645,-1.8112 11.542,-2.7125 17.47,-2.67c17.254,0 28.447,5.6233 33.58,16.8701c1.98,4.393 2.97,10.637 2.97,18.73zm-24.69,-3.9c0,-5.79 -1.73,-9.67 -4.22,-10.74c-1.88,-1.27 -4.79,-1.46 -7.49,-1.46c-6.86,0 -10.293,3.23 -10.3,9.69l-0.42,3.89l22.43,0l0,-1.38z"/>
<g stroke="null" id="svg_16">
<path stroke="null" id="svg_1" fill="#426DF2" d="m32.0095,0.95467l29.92439,17.27307l0,34.54617l-29.92439,17.27623l-29.92123,-17.27623l0,-34.5525l29.92123,-17.27304l0,0.00629zm0,-0.95467l-30.74,17.74723l0,35.49455l30.74,17.74726l30.74,-17.74726l0,-35.48507l-30.74,-17.74726l0,-0.00946z"/>
<path stroke="null" id="svg_10" fill="#426DF2" d="m32.00965,11.47215l20.81351,12.01275l0,24.02553l-20.81351,12.01275l-20.81051,-12.01275l0,-24.02553l20.81051,-12.01275zm0,-6.45211l-26.3996,15.24042l0,30.4871l26.3996,15.24039l26.4026,-15.24039l0,-30.4871l-26.4026,-15.24042z"/>
<path stroke="null" id="svg_11" fill="#00BFFF" d="m19.23172,35.82013c-0.53763,0.00253 -1.06092,-0.17324 -1.48784,-0.49979c-0.4269,-0.32687 -0.73328,-0.7862 -0.87105,-1.30591c-0.1378,-0.51971 -0.0992,-1.0704 0.10973,-1.56577c0.20896,-0.49537 0.57639,-0.90722 1.04473,-1.17124l8.60176,-4.96316c0.55682,-0.31009 1.21326,-0.38877 1.82761,-0.21907c0.61436,0.16973 1.13732,0.57418 1.45604,1.12613c0.31872,0.55195 0.40761,1.20709 0.24749,1.82401c-0.16012,0.61692 -0.55641,1.14611 -1.10331,1.47336l-8.60492,4.96319c-0.36939,0.21939 -0.79063,0.33604 -1.22024,0.33825z"/>
<path stroke="null" id="svg_12" fill="#00BFFF" d="m34.45947,27.0413c-0.53425,-0.00013 -1.05364,-0.17652 -1.47757,-0.50185c-0.42386,-0.32532 -0.72857,-0.78143 -0.86691,-1.29757c-0.1383,-0.51614 -0.10246,-1.06348 0.10195,-1.55717c0.20441,-0.49369 0.56599,-0.90617 1.02861,-1.17346l3.16125,-1.81456c0.27661,-0.17068 0.58452,-0.2841 0.9057,-0.33358c0.32118,-0.04947 0.649,-0.03395 0.96387,0.04562c0.31518,0.07957 0.61107,0.22154 0.87029,0.41754c0.25891,0.196 0.47608,0.44204 0.63857,0.72352c0.16217,0.28148 0.26649,0.59267 0.30633,0.91515c0.03983,0.32248 0.01454,0.6497 -0.07429,0.96222c-0.08915,0.31252 -0.23994,0.60405 -0.44352,0.85724c-0.20358,0.25318 -0.45585,0.4629 -0.74226,0.61673l-3.16125,1.81456c-0.36765,0.21335 -0.78557,0.3257 -1.21076,0.32561z"/>
<path stroke="null" id="svg_13" fill="#00BFFF" d="m22.82935,43.2301c-0.53463,0.00063 -1.05447,-0.17545 -1.4789,-0.50043c-0.42443,-0.32498 -0.72968,-0.78114 -0.8684,-1.29738c-0.13872,-0.51655 -0.10312,-1.06408 0.10122,-1.55818c0.20434,-0.49379 0.56605,-0.90665 1.02899,-1.17409l0.08219,-0.04742c0.55584,-0.30222 1.20766,-0.37524 1.81668,-0.2039c0.60898,0.17134 1.12705,0.57377 1.4439,1.1213c0.31685,0.54753 0.40745,1.19717 0.25249,1.81045c-0.15493,0.61328 -0.5432,1.14216 -1.08206,1.47346l-0.08219,0.04742c-0.36857,0.21465 -0.78737,0.32814 -1.21392,0.32877z"/>
<path stroke="null" id="svg_14" fill="#00BFFF" d="m29.44582,39.49666c-0.53412,0.00063 -1.05361,-0.17482 -1.47798,-0.49916c-0.42433,-0.32434 -0.72984,-0.77956 -0.86922,-1.29516c-0.13935,-0.51592 -0.10476,-1.06313 0.09841,-1.55692c0.20317,-0.4941 0.56359,-0.90728 1.02541,-1.17567l11.75024,-6.85359c0.27598,-0.16764 0.5823,-0.27863 0.90127,-0.32653c0.31929,-0.04786 0.64458,-0.03164 0.95723,0.0477c0.31296,0.07938 0.60664,0.22031 0.86429,0.41453c0.25764,0.19423 0.47419,0.43786 0.63668,0.71669c0.16217,0.27882 0.26744,0.58723 0.30949,0.90718c0.04173,0.31998 0.01928,0.64505 -0.06575,0.95625c-0.08535,0.31123 -0.23172,0.60234 -0.43088,0.85638c-0.19916,0.25385 -0.44668,0.46565 -0.72867,0.62277l-11.75027,6.84411c-0.36923,0.22002 -0.79038,0.33794 -1.22024,0.34142z"/>
<path stroke="null" id="svg_15" fill="#426DF2" d="m26.41731,50.63691c-0.53435,0 -1.05371,-0.1764 -1.4776,-0.50201c-0.42389,-0.32529 -0.7286,-0.78114 -0.86691,-1.29738c-0.1383,-0.51623 -0.10249,-1.06344 0.10192,-1.55723c0.20441,-0.49379 0.56599,-0.90601 1.02867,-1.17346l18.36996,-10.59335c0.55796,-0.3215 1.22087,-0.40812 1.84269,-0.2412c0.62213,0.16723 1.15196,0.57472 1.47346,1.13268c0.3215,0.55796 0.40843,1.22087 0.2412,1.84269c-0.16723,0.62182 -0.57472,1.15196 -1.13268,1.47346l-18.3668,10.59019c-0.36876,0.21402 -0.78763,0.32624 -1.21392,0.32561z"/>
</g>
<text font-weight="bold" transform="matrix(1.04823 0 0 1.04823 -6.76884 -3.40162)" stroke="null" xml:space="preserve" text-anchor="start" font-family="'Andada Pro'" font-size="30" stroke-width="0" id="svg_19" y="46.28383" x="69.14952" fill="#3c6df2">GIS-BI开发平台</text>
</g>
<svg width="881" height="225" viewBox="0 0 881 225" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M97.24 3.0199L191.9 57.6599V166.94L97.24 221.59L2.59 166.94V57.6399L97.24 3V3.0199ZM97.24 0L0 56.1399V168.42L97.24 224.56L194.48 168.42V56.1699L97.24 0.0299072V0Z" fill="#426DF2"/>
<path d="M599.14 100.66H629.87L625.87 122.06H599.14V147.27H635.82L637.46 170.42H568.96V53.0599H637.5L635.86 75.7599H599.14V100.66Z" fill="#426DF2"/>
<path d="M409.65 118.75V170.25H387.14L387.04 165.04C379.167 169.133 371.477 171.177 363.97 171.17H361.74C354.027 171.17 347.897 168.207 343.35 162.28C340.339 157.654 338.766 152.239 338.83 146.72V146.11C338.83 135.99 342.38 129.27 349.48 125.95C352.387 124.01 359.77 123.04 371.63 123.04H383.74V120.21C383.74 114.383 382.897 111.05 381.21 110.21C379.37 108.83 376.127 108.14 371.48 108.14H347.34L347.75 87.5L375.54 86.75C392.04 86.75 402.26 90.8399 406.2 99.0199C408.5 103.747 409.65 110.323 409.65 118.75ZM366.31 140.14C365.54 141.05 364.73 144.02 364.73 146.14C364.73 150.74 367.063 153.04 371.73 153.04C374.537 153.04 378.537 151.89 383.73 149.59V138.34C383.73 138.34 369.1 136.81 366.31 140.14Z" fill="#426DF2"/>
<path d="M559.82 118.75V170.25H537.32L537.21 165.04C529.344 169.133 521.654 171.177 514.14 171.17H511.92C504.2 171.17 498.067 168.207 493.52 162.28C490.513 157.652 488.94 152.239 489 146.72V146.11C489 135.99 492.55 129.27 499.65 125.95C502.57 124.01 509.954 123.04 521.8 123.04H533.91V120.21C533.91 114.383 533.067 111.05 531.38 110.21C529.54 108.83 526.297 108.14 521.65 108.14H497.55L497.96 87.5L525.75 86.75C542.264 86.75 552.484 90.8399 556.41 99.0199C558.684 103.747 559.82 110.323 559.82 118.75ZM516.52 140.14C515.75 141.05 514.95 144.02 514.95 146.14C514.95 150.74 517.284 153.04 521.95 153.04C524.764 153.04 528.764 151.89 533.95 149.59V138.34C533.95 138.34 519.32 136.81 516.52 140.14Z" fill="#426DF2"/>
<path d="M716.951 118.75V170.25H694.451L694.341 165.04C686.467 169.133 678.777 171.177 671.271 171.17H669.051C661.331 171.17 655.197 168.207 650.651 162.28C647.643 157.652 646.071 152.239 646.131 146.72V146.11C646.131 135.99 649.684 129.27 656.791 125.95C659.704 124.01 667.087 123.04 678.941 123.04H691.041V120.21C691.041 114.383 690.201 111.05 688.521 110.21C686.681 108.83 683.434 108.14 678.781 108.14H654.641L655.051 87.5L682.841 86.75C699.347 86.75 709.567 90.8399 713.501 99.0199C715.801 103.747 716.951 110.323 716.951 118.75ZM673.611 140.14C672.841 141.05 672.041 144.02 672.041 146.14C672.041 150.74 674.374 153.04 679.041 153.04C681.854 153.04 685.854 151.89 691.041 149.59V138.34C691.041 138.34 676.411 136.81 673.611 140.14Z" fill="#426DF2"/>
<path d="M477.43 170.42H464.43C461.79 170.42 459.53 170.35 457.65 170.19C453.394 170.023 449.206 169.067 445.3 167.37C441.463 165.56 438.331 162.533 436.39 158.76C434.097 154.393 432.953 148.55 432.96 141.23L432.81 108.31H420.31V86.9399H432.82V60.34L458.42 65.2699V86.9399H477.93L475.26 108.35H458.38V139.83C458.38 140.54 458.38 141.15 458.46 141.66C458.518 142.772 458.723 143.872 459.07 144.93C459.478 145.99 460.133 146.937 460.979 147.693C461.826 148.45 462.841 148.994 463.94 149.28C466.57 150.273 469.309 150.944 472.1 151.28C472.71 151.38 473.34 151.46 474 151.51L475.6 151.66L477.43 151.74V170.42Z" fill="#426DF2"/>
<path d="M878.59 121.25V136.05H831.47L831.37 138.74C831.37 144.38 833.62 147.65 837.08 148.87C840.067 149.667 843.149 150.05 846.24 150.01H848.3C857.46 150.01 870.84 147.95 880.45 146.01H880.6L876.73 166.92C876.73 167.18 872.23 169.09 863.48 170.51C857.195 171.161 850.879 171.469 844.56 171.43C826.247 171.43 814.42 165.247 809.08 152.88C807.08 148 805.26 142.88 805.26 137.57V117.57C805.26 102.92 812.26 93.1599 824.57 88.3199C830.215 86.5087 836.112 85.6074 842.04 85.6499C859.294 85.6499 870.487 91.2732 875.62 102.52C877.6 106.913 878.59 113.157 878.59 121.25ZM853.9 117.35C853.9 111.56 852.17 107.68 849.68 106.61C847.8 105.34 844.89 105.15 842.19 105.15C835.33 105.15 831.897 108.38 831.89 114.84L831.47 118.73H853.9V117.35Z" fill="#426DF2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M276.98 51.8799H231.71V170.42H276.98C305.82 170.42 329.21 150.88 329.21 126.79V95.5199C329.21 71.4299 305.82 51.9399 276.98 51.8799ZM300.12 127.81C300.12 138.5 289.77 147.16 276.98 147.17H261.87V75.1699H276.98C289.77 75.1699 300.12 83.8499 300.12 94.5299V127.81Z" fill="#426DF2"/>
<path d="M758.75 172.74C753.95 172.74 748.683 172.463 742.95 171.91C740.75 171.69 738.49 171.44 736.17 171.14L730.74 170.37L727.74 147.75L733.57 150L740.76 151.82C743.61 152.22 746.29 152.53 748.82 152.75C751.827 153.01 754.55 153.14 756.99 153.14C765.86 153.14 770.99 152.93 772.37 149.3C773.14 147.79 773.1 143.68 772.25 142.3C771.4 140.92 769.58 139.36 766.82 138.23C765.64 137.75 764.15 137.23 762.36 136.74L757.11 135.31C754.8 134.8 752.72 134.31 750.89 133.77C748.53 133.103 746.373 132.407 744.42 131.68C740.3 130.257 736.475 128.095 733.13 125.3C730.595 122.881 728.87 119.737 728.19 116.3C727.652 113.45 727.591 110.53 728.01 107.66C729.11 98.3266 733.523 91.9266 741.25 88.46C744.558 86.9462 748.073 85.9351 751.68 85.46C753.27 85.24 755.08 85.0599 757.11 84.9099L762.36 84.58H765.9C769.9 84.58 774.333 84.8 779.2 85.24L785.12 85.84L790.12 86.5L792.2 106.83C788.91 106.065 785.568 105.55 782.2 105.29C777.517 104.798 772.807 104.618 768.1 104.75C758.82 104.75 753.37 104.24 751.74 108.02C751.436 108.737 751.29 109.511 751.313 110.289C751.335 111.068 751.525 111.832 751.87 112.53C752.763 114.183 754.713 115.633 757.72 116.88C758.98 117.4 760.567 117.95 762.48 118.53L774.56 121.78C776.88 122.447 778.957 123.113 780.79 123.78C784.479 125.024 787.887 126.981 790.82 129.54C793.484 131.926 795.302 135.112 796 138.62C796.532 142.304 796.596 146.04 796.19 149.74C795.09 158.98 790.673 165.38 782.94 168.94C779.643 170.483 776.123 171.496 772.51 171.94C770.92 172.16 769.11 172.35 767.08 172.49L762.38 172.77L758.75 172.74Z" fill="#426DF2"/>
<path d="M97.2405 36.2899L163.08 74.2899V150.29L97.2405 188.29L31.4105 150.29V74.2899L97.2405 36.2899ZM97.2405 15.8799L13.7305 64.09V160.53L97.2405 208.74L180.76 160.53V64.09L97.2405 15.8799Z" fill="#426DF2"/>
<path d="M56.82 113.31C55.1193 113.318 53.464 112.762 52.1135 111.729C50.7631 110.695 49.7939 109.242 49.3581 107.598C48.9222 105.954 49.0443 104.212 49.7052 102.645C50.3662 101.078 51.5285 99.7752 53.01 98.94L80.22 83.24C81.9814 82.2591 84.0579 82.0102 86.0013 82.547C87.9447 83.0839 89.599 84.3633 90.6072 86.1093C91.6154 87.8553 91.8966 89.9277 91.3901 91.8792C90.8836 93.8307 89.63 95.5047 87.9 96.5399L60.68 112.24C59.5115 112.934 58.179 113.303 56.82 113.31Z" fill="#00BFFF"/>
<path d="M104.99 85.5399C103.3 85.5395 101.657 84.9815 100.316 83.9524C98.9752 82.9233 98.0113 81.4805 97.5737 79.8478C97.1362 78.2151 97.2496 76.4837 97.8962 74.922C98.5428 73.3603 99.6866 72.0555 101.15 71.21L111.15 65.47C112.025 64.9301 112.999 64.5713 114.015 64.4148C115.031 64.2583 116.068 64.3074 117.064 64.5591C118.061 64.8108 118.997 65.2599 119.817 65.8799C120.636 66.4999 121.323 67.2782 121.837 68.1686C122.35 69.059 122.68 70.0434 122.806 71.0635C122.932 72.0836 122.852 73.1187 122.571 74.1073C122.289 75.0959 121.812 76.0181 121.168 76.819C120.524 77.6199 119.726 78.2833 118.82 78.7699L108.82 84.5099C107.657 85.1848 106.335 85.5402 104.99 85.5399Z" fill="#00BFFF"/>
<path d="M68.2004 136.75C66.5092 136.752 64.8648 136.195 63.5222 135.167C62.1796 134.139 61.214 132.696 60.7752 131.063C60.3364 129.429 60.449 127.697 61.0954 126.134C61.7418 124.572 62.886 123.266 64.3504 122.42L64.6104 122.27C66.3687 121.314 68.4306 121.083 70.3571 121.625C72.2835 122.167 73.9223 123.44 74.9246 125.172C75.9269 126.904 76.2135 128.959 75.7233 130.899C75.2332 132.839 74.005 134.512 72.3004 135.56L72.0404 135.71C70.8745 136.389 69.5497 136.748 68.2004 136.75Z" fill="#00BFFF"/>
<path d="M89.1303 124.94C87.4407 124.942 85.7974 124.387 84.455 123.361C83.1127 122.335 82.1463 120.895 81.7054 119.264C81.2646 117.632 81.374 115.901 82.0167 114.339C82.6594 112.776 83.7995 111.469 85.2604 110.62L122.43 88.94C123.303 88.4097 124.272 88.0586 125.281 87.9071C126.291 87.7557 127.32 87.807 128.309 88.058C129.299 88.3091 130.228 88.7549 131.043 89.3693C131.858 89.9837 132.543 90.7544 133.057 91.6364C133.57 92.5184 133.903 93.494 134.036 94.5061C134.168 95.5183 134.097 96.5466 133.828 97.531C133.558 98.5155 133.095 99.4364 132.465 100.24C131.835 101.043 131.052 101.713 130.16 102.21L92.9903 123.86C91.8223 124.556 90.4901 124.929 89.1303 124.94Z" fill="#00BFFF"/>
<path d="M79.5502 160.18C77.8599 160.18 76.217 159.622 74.8761 158.592C73.5352 157.563 72.5713 156.121 72.1338 154.488C71.6963 152.855 71.8096 151.124 72.4562 149.562C73.1028 148 74.2466 146.696 75.7102 145.85L133.82 112.34C135.585 111.323 137.682 111.049 139.649 111.577C141.617 112.106 143.293 113.395 144.31 115.16C145.327 116.925 145.602 119.022 145.073 120.989C144.544 122.956 143.255 124.633 141.49 125.65L83.3902 159.15C82.2237 159.827 80.8987 160.182 79.5502 160.18Z" fill="#426DF2"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 5.33335C16 3.86059 17.1939 2.66669 18.6667 2.66669H26.6667C28.1394 2.66669 29.3333 3.86059 29.3333 5.33335V13.3334C29.3333 14.8061 28.1394 16 26.6667 16H18.6667C17.1939 16 16 14.8061 16 13.3334V5.33335ZM18.6667 5.33335V13.3334H26.6667V5.33335H18.6667Z" fill="#34C724"/>
<path d="M2.66666 18.6667C2.66666 17.1939 3.86057 16 5.33333 16H13.3333C14.8061 16 16 17.1939 16 18.6667V26.6667C16 28.1394 14.8061 29.3334 13.3333 29.3334H5.33333C3.86057 29.3334 2.66666 28.1394 2.66666 26.6667V18.6667ZM5.33333 18.6667V26.6667H13.3333V18.6667H5.33333Z" fill="#34C724"/>
<path d="M4 9.33335C4 5.65146 6.98477 2.66669 10.6667 2.66669H13.3333V5.33335H10.6667C8.45753 5.33335 6.66666 7.12422 6.66666 9.33335H8.46364C8.80538 9.33335 8.98975 9.7342 8.76735 9.99367L5.63703 13.6457C5.4774 13.8319 5.18927 13.8319 5.02963 13.6457L1.89932 9.99367C1.67691 9.7342 1.86128 9.33335 2.20302 9.33335H4Z" fill="#34C724"/>
<path d="M28 22.6667C28 26.3486 25.0152 29.3334 21.3333 29.3334H18.6667V26.6667H21.3333C23.5425 26.6667 25.3333 24.8758 25.3333 22.6667V22H23.5364C23.1946 22 23.0102 21.5992 23.2327 21.3397L26.363 17.6877C26.5226 17.5014 26.8107 17.5014 26.9704 17.6877L30.1007 21.3397C30.3231 21.5992 30.1387 22 29.797 22H28V22.6667Z" fill="#34C724"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,15 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#434343"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#434343"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#434343"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#434343"/>
<path d="M9 12V8L44.5 8C44.7761 8 45 8.22386 45 8.5V11.5C45 11.7761 44.7761 12 44.5 12L9 12Z" fill="#3370FF"/>
<path d="M9 24V20H55.5C55.7761 20 56 20.2239 56 20.5V23.5C56 23.7761 55.7761 24 55.5 24H9Z" fill="#3370FF"/>
<path d="M9 36V32H37.5C37.7761 32 38 32.2239 38 32.5V35.5C38 35.7761 37.7761 36 37.5 36H9Z" fill="#3370FF"/>
<path d="M9 48V44H21.5C21.7761 44 22 44.2239 22 44.5V47.5C22 47.7761 21.7761 48 21.5 48H9Z" fill="#3370FF"/>
<path d="M47 14V6H48V14H47Z" fill="#3370FF"/>
<path d="M52 26V18H53V26H52Z" fill="#3370FF"/>
<path d="M42 38V30H43V38H42Z" fill="#3370FF"/>
<path d="M54 50V42H55V50H54Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,15 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#434343"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#434343"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#434343"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#434343"/>
<path d="M9 12V8L44.5 8C44.7761 8 45 8.22386 45 8.5V11.5C45 11.7761 44.7761 12 44.5 12L9 12Z" fill="#3370FF"/>
<path d="M9 24V20H55.5C55.7761 20 56 20.2239 56 20.5V23.5C56 23.7761 55.7761 24 55.5 24H9Z" fill="#3370FF"/>
<path d="M9 36V32H37.5C37.7761 32 38 32.2239 38 32.5V35.5C38 35.7761 37.7761 36 37.5 36H9Z" fill="#3370FF"/>
<path d="M9 48V44H21.5C21.7761 44 22 44.2239 22 44.5V47.5C22 47.7761 21.7761 48 21.5 48H9Z" fill="#3370FF"/>
<path d="M47 14V6H48V14H47Z" fill="#3370FF"/>
<path d="M52 26V18H53V26H52Z" fill="#3370FF"/>
<path d="M42 38V30H43V38H42Z" fill="#3370FF"/>
<path d="M54 50V42H55V50H54Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,15 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#DEE0E3"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#DEE0E3"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#DEE0E3"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#DEE0E3"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#DEE0E3"/>
<path d="M9 12V8L44.5 8C44.7761 8 45 8.22386 45 8.5V11.5C45 11.7761 44.7761 12 44.5 12L9 12Z" fill="#3370FF"/>
<path d="M9 24V20H55.5C55.7761 20 56 20.2239 56 20.5V23.5C56 23.7761 55.7761 24 55.5 24H9Z" fill="#3370FF"/>
<path d="M9 36V32H37.5C37.7761 32 38 32.2239 38 32.5V35.5C38 35.7761 37.7761 36 37.5 36H9Z" fill="#3370FF"/>
<path d="M9 48V44H21.5C21.7761 44 22 44.2239 22 44.5V47.5C22 47.7761 21.7761 48 21.5 48H9Z" fill="#3370FF"/>
<path d="M47 14V6H48V14H47Z" fill="#3370FF"/>
<path d="M52 26V18H53V26H52Z" fill="#3370FF"/>
<path d="M42 38V30H43V38H42Z" fill="#3370FF"/>
<path d="M54 50V42H55V50H54Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

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="1743046434070" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5630" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M657.8 696.3l39.1-39.1 262.5 262.5-44.7 39.1-256.9-262.5z" p-id="5631"></path><path d="M691.3 958.7l-39.1-39.1 262.5-262.5 39.1 39.1-262.5 262.5zM510.7 321.7L678.6 516H588v223.4H437.6V516h-92.1l165.2-194.3m-0.4-98.3L207.1 580h166.5v223.4H652V580h166.5L510.3 223.4z" p-id="5632"></path><path d="M512.8 896.2H129.3V129.3h766.9v383.5h64V110.3c0-24.9-20.1-45-45-45H110.3c-24.9 0-45 20.1-45 45v804.9c0 24.9 20.1 45 45 45h402.5v-64z" p-id="5633"></path></svg>

After

Width:  |  Height:  |  Size: 786 B

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33333 5.33398V13.334H10V5.33398H3.33333ZM11.3333 4.66732V14.0747C11.3333 14.402 11.0548 14.6673 10.7111 14.6673H2.62222C2.27858 14.6673 2 14.402 2 14.0747V4.59324C2 4.26596 2.27858 4.00065 2.62222 4.00065H10.6667C11.0349 4.00065 11.3333 4.29913 11.3333 4.66732ZM13.8047 1.52925C13.9254 1.64989 14 1.81656 14 2.00065V10.334C14 10.5181 13.8508 10.6673 13.6667 10.6673H13C12.8159 10.6673 12.6667 10.5181 12.6667 10.334V2.66732H6.33333C6.14924 2.66732 6 2.51808 6 2.33398V1.66732C6 1.48322 6.14924 1.33398 6.33333 1.33398H13.3333C13.5174 1.33398 13.6841 1.4086 13.8047 1.52925Z" fill="#646A73"/>
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33333 5.33398V13.334H10V5.33398H3.33333ZM11.3333 4.66732V14.0747C11.3333 14.402 11.0548 14.6673 10.7111 14.6673H2.62222C2.27858 14.6673 2 14.402 2 14.0747V4.59324C2 4.26596 2.27858 4.00065 2.62222 4.00065H10.6667C11.0349 4.00065 11.3333 4.29913 11.3333 4.66732ZM13.8047 1.52925C13.9254 1.64989 14 1.81656 14 2.00065V10.334C14 10.5181 13.8508 10.6673 13.6667 10.6673H13C12.8159 10.6673 12.6667 10.5181 12.6667 10.334V2.66732H6.33333C6.14924 2.66732 6 2.51808 6 2.33398V1.66732C6 1.48322 6.14924 1.33398 6.33333 1.33398H13.3333C13.5174 1.33398 13.6841 1.4086 13.8047 1.52925Z" />
</svg>

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 682 B

View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.59845 0 3.57025 0H14.4298C16.4015 0 18 1.34315 18 3V15C18 16.6569 16.4015 18 14.4298 18H3.57025C1.59845 18 0 16.6569 0 15V3Z" fill="#BBBFC4"/>
<path d="M4.25 13V8.77273C4.25 8.48403 4.48985 8.25 4.78571 8.25H7V4.52632C7 4.23564 7.25584 4 7.57143 4H10.4286C10.7442 4 11 4.23564 11 4.52632V7H13.2143C13.5102 7 13.75 7.22386 13.75 7.5V13H14C14.1381 13 14.25 13.1119 14.25 13.25V13.75C14.25 13.8881 14.1381 14 14 14H4C3.86193 14 3.75 13.8881 3.75 13.75V13.25C3.75 13.1119 3.86193 13 4 13H4.25ZM12.75 13V8H11V13H12.75ZM10 13V5H8V13H10ZM7 13V9.25H5.25V13H7Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@ -0,0 +1,7 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.46525 0 3.27273 0H14.7273C16.5347 0 18 1.34315 18 3V15C18 16.6569 16.5347 18 14.7273 18H3.27273C1.46525 18 0 16.6569 0 15V3Z" fill="#BBBFC4"/>
<path opacity="0.6" fill-rule="evenodd" clip-rule="evenodd" d="M3.75 13.5V8.77273C3.75 8.48403 3.98985 8.25 4.28571 8.25H6.5V4.02632C6.5 3.73564 6.75584 3.5 7.07143 3.5H9.92857C10.2442 3.5 10.5 3.73564 10.5 4.02632V5.15039H9.5V4.5H7.5V13.5V14.5H3.5C3.36193 14.5 3.25 14.3881 3.25 14.25V13.75C3.25 13.6119 3.36193 13.5 3.5 13.5H3.75ZM6.5 13.5V9.25H4.75V13.5H6.5Z" fill="white"/>
<rect x="8.5" y="6" width="5.5" height="8.5" fill="#00D6B9"/>
<path d="M11.3043 12.9348C11.7606 12.9348 12.1304 12.5649 12.1304 12.1087C12.1304 11.6525 11.7606 11.2826 11.3043 11.2826C10.8481 11.2826 10.4783 11.6525 10.4783 12.1087C10.4783 12.5649 10.8481 12.9348 11.3043 12.9348Z" fill="white"/>
<path d="M8.41304 5.5C8.18492 5.5 8 5.68492 8 5.91304V14.587C8 14.8151 8.18492 15 8.41304 15H14.1957C14.4238 15 14.6087 14.8151 14.6087 14.587V5.91304C14.6087 5.68492 14.4238 5.5 14.1957 5.5H8.41304ZM9.23913 6.73913H10.0652V7.15217C10.0652 7.38029 10.2501 7.56522 10.4783 7.56522H12.1304C12.3586 7.56522 12.5435 7.38029 12.5435 7.15217V6.73913H13.3696V13.7609H9.23913V6.73913Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M3.0166 1H11.3095C11.4421 1 11.5693 1.05268 11.663 1.14645L13.3702 2.85355C13.4639 2.94732 13.5166 3.0745 13.5166 3.20711V14.5C13.5166 14.7761 13.2927 15 13.0166 15L6.61962 14.9998C6.61373 14.9998 6.60786 14.9997 6.60202 14.9995L6.60786 14.9997V13.8H12.3167V4H10.6916C10.595 4 10.5166 3.92165 10.5166 3.825V2.2H3.71668V8.98657H2.5166V1.5C2.5166 1.22386 2.74046 1 3.0166 1Z" />
<path d="M5.94584 9.5C5.68794 9.26773 5.36928 9.10161 5.0166 9.02837V8.41667C5.0166 8.35224 5.06884 8.3 5.13327 8.3H8.23327C8.2977 8.3 8.34994 8.35223 8.34994 8.41667V9.38333C8.34994 9.44777 8.2977 9.5 8.23327 9.5H5.94584Z"/>
<path d="M5.0166 5.91667C5.0166 5.85223 5.06884 5.8 5.13327 5.8H10.8999C10.9644 5.8 11.0166 5.85223 11.0166 5.91667V6.88333C11.0166 6.94777 10.9644 7 10.8999 7H5.13327C5.06884 7 5.0166 6.94777 5.0166 6.88333V5.91667Z"/>
<path d="M5.17545 10.1799C5.17545 10.0805 5.09492 10 4.99557 10H1.00471C0.844448 10 0.76419 10.1938 0.87751 10.3071L2.15664 11.5862C1.31561 12.545 0.686543 14.7137 1.72678 15.754C1.79493 15.8221 1.8973 15.9133 2.00475 15.9865C2.03989 16.0091 2.07237 16.0023 2.09415 15.9741C2.10517 15.9586 2.10964 15.9395 2.10658 15.9207C2.10038 15.8659 2.09586 15.8212 2.09186 15.7817C2.0886 15.7497 2.0857 15.721 2.08252 15.693C2.00232 14.9869 2.885 13.7726 3.59451 13.0241L4.86837 14.2979C4.98169 14.4113 5.17545 14.331 5.17545 14.1707L5.17545 10.1799Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.66634 4.00065C8.66634 4.25739 8.70262 4.50565 8.77034 4.7406L5.88342 6.4465C5.401 5.96502 4.7351 5.66732 3.99967 5.66732C2.52692 5.66732 1.33301 6.86123 1.33301 8.33398C1.33301 9.80674 2.52692 11.0007 3.99967 11.0007C4.81776 11.0007 5.5498 10.6323 6.03896 10.0523L9.71633 11.8175C9.68353 11.9846 9.66634 12.1573 9.66634 12.334C9.66634 13.8067 10.8602 15.0007 12.333 15.0007C13.8058 15.0007 14.9997 13.8067 14.9997 12.334C14.9997 10.8612 13.8058 9.66732 12.333 9.66732C11.5149 9.66732 10.7829 10.0357 10.2937 10.6156L6.61635 8.8505C6.64915 8.6834 6.66634 8.5107 6.66634 8.33398C6.66634 8.07725 6.63006 7.82898 6.56234 7.59404L9.44926 5.88814C9.93168 6.36961 10.5976 6.66732 11.333 6.66732C12.8058 6.66732 13.9997 5.47341 13.9997 4.00065C13.9997 2.52789 12.8058 1.33398 11.333 1.33398C9.86025 1.33398 8.66634 2.52789 8.66634 4.00065ZM12.6663 4.00065C12.6663 4.73703 12.0694 5.33398 11.333 5.33398C10.5966 5.33398 9.99967 4.73703 9.99967 4.00065C9.99967 3.26427 10.5966 2.66732 11.333 2.66732C12.0694 2.66732 12.6663 3.26427 12.6663 4.00065ZM12.333 13.6673C11.5966 13.6673 10.9997 13.0704 10.9997 12.334C10.9997 11.5976 11.5966 11.0007 12.333 11.0007C13.0694 11.0007 13.6663 11.5976 13.6663 12.334C13.6663 13.0704 13.0694 13.6673 12.333 13.6673ZM5.33301 8.33398C5.33301 9.07036 4.73605 9.66732 3.99967 9.66732C3.26329 9.66732 2.66634 9.07036 2.66634 8.33398C2.66634 7.5976 3.26329 7.00065 3.99967 7.00065C4.73605 7.00065 5.33301 7.5976 5.33301 8.33398Z" fill="#646A73"/>
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M8.66634 4.00065C8.66634 4.25739 8.70262 4.50565 8.77034 4.7406L5.88342 6.4465C5.401 5.96502 4.7351 5.66732 3.99967 5.66732C2.52692 5.66732 1.33301 6.86123 1.33301 8.33398C1.33301 9.80674 2.52692 11.0007 3.99967 11.0007C4.81776 11.0007 5.5498 10.6323 6.03896 10.0523L9.71633 11.8175C9.68353 11.9846 9.66634 12.1573 9.66634 12.334C9.66634 13.8067 10.8602 15.0007 12.333 15.0007C13.8058 15.0007 14.9997 13.8067 14.9997 12.334C14.9997 10.8612 13.8058 9.66732 12.333 9.66732C11.5149 9.66732 10.7829 10.0357 10.2937 10.6156L6.61635 8.8505C6.64915 8.6834 6.66634 8.5107 6.66634 8.33398C6.66634 8.07725 6.63006 7.82898 6.56234 7.59404L9.44926 5.88814C9.93168 6.36961 10.5976 6.66732 11.333 6.66732C12.8058 6.66732 13.9997 5.47341 13.9997 4.00065C13.9997 2.52789 12.8058 1.33398 11.333 1.33398C9.86025 1.33398 8.66634 2.52789 8.66634 4.00065ZM12.6663 4.00065C12.6663 4.73703 12.0694 5.33398 11.333 5.33398C10.5966 5.33398 9.99967 4.73703 9.99967 4.00065C9.99967 3.26427 10.5966 2.66732 11.333 2.66732C12.0694 2.66732 12.6663 3.26427 12.6663 4.00065ZM12.333 13.6673C11.5966 13.6673 10.9997 13.0704 10.9997 12.334C10.9997 11.5976 11.5966 11.0007 12.333 11.0007C13.0694 11.0007 13.6663 11.5976 13.6663 12.334C13.6663 13.0704 13.0694 13.6673 12.333 13.6673ZM5.33301 8.33398C5.33301 9.07036 4.73605 9.66732 3.99967 9.66732C3.26329 9.66732 2.66634 9.07036 2.66634 8.33398C2.66634 7.5976 3.26329 7.00065 3.99967 7.00065C4.73605 7.00065 5.33301 7.5976 5.33301 8.33398Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>
<?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="1741080458837" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4350" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M851.42 64.58H172.58c-59.65 0-108 48.35-108 108v678.84c0 59.65 48.35 108 108 108h678.84c59.65 0 108-48.35 108-108V172.58c0-59.65-48.35-108-108-108z m47 786.84c0 25.92-21.08 47-47 47H172.58c-25.92 0-47-21.08-47-47V172.58c0-25.92 21.08-47 47-47h678.84c25.92 0 47 21.08 47 47v678.84z" p-id="4351"></path><path d="M382.64 225.23h-0.08c-16.84 0.04-30.46 13.73-30.42 30.58l0.25 97.26h-99.14c-16.84 0-30.5 13.66-30.5 30.5s13.66 30.5 30.5 30.5h129.72c8.1 0 15.87-3.22 21.59-8.96a30.515 30.515 0 0 0 8.91-21.62l-0.33-127.84c-0.05-16.82-13.69-30.42-30.5-30.42zM381 608.3l-127.84 0.33c-16.84 0.04-30.46 13.73-30.42 30.58 0.04 16.82 13.69 30.42 30.5 30.42h0.08l97.26-0.25v99.14c0 16.84 13.66 30.5 30.5 30.5s30.5-13.66 30.5-30.5V638.8c0-8.1-3.22-15.87-8.96-21.59s-13.54-8.93-21.62-8.91zM765.08 353.47l-97.26 0.25v-99.14c0-16.84-13.66-30.5-30.5-30.5s-30.5 13.66-30.5 30.5V384.3c0 8.1 3.22 15.87 8.96 21.59 5.72 5.7 13.46 8.91 21.54 8.91h0.08l127.84-0.33c16.84-0.04 30.47-13.73 30.42-30.58-0.05-16.85-13.77-30.48-30.58-30.42zM765.16 609.03H635.43c-8.1 0-15.87 3.22-21.59 8.96a30.515 30.515 0 0 0-8.91 21.62l0.33 127.84c0.04 16.82 13.69 30.42 30.5 30.42h0.08c16.84-0.04 30.46-13.73 30.42-30.58l-0.25-97.26h99.14c16.84 0 30.5-13.66 30.5-30.5s-13.65-30.5-30.49-30.5z" p-id="4352"></path></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,11 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2326_66186)">
<path d="M1 4C1 1.79086 2.79086 0 5 0H19C21.2091 0 23 1.79086 23 4V20C23 22.2091 21.2091 24 19 24H5C2.79086 24 1 22.2091 1 20V4Z" fill="#34C724"/>
<path d="M7.0166 5.58331H15.3095C15.4421 5.58331 15.5693 5.63599 15.663 5.72976L17.3702 7.43687C17.4639 7.53064 17.5166 7.65781 17.5166 7.79042V17.9167C17.5166 18.1928 17.2927 18.4166 17.0166 18.4166H7.0166C6.74046 18.4166 6.5166 18.1928 6.5166 17.9167V6.08331C6.5166 5.80717 6.74046 5.58331 7.0166 5.58331ZM7.71658 17.2167H16.3166V8.58331H14.6916C14.5949 8.58331 14.5166 8.50496 14.5166 8.40831V6.78339H7.71658V17.2167ZM15.063 9.85465C15.1222 9.77796 15.0676 9.66665 14.9706 9.66665H13.8072C13.771 9.66665 13.737 9.68339 13.7149 9.71198L11.9076 11.8119L12.7291 12.875L15.063 9.85465ZM11.9076 11.8119L11.9092 11.814L12.7291 12.875L11.9092 13.936L11.9076 13.938L10.2849 16.038C10.2628 16.0666 10.2287 16.0833 10.1926 16.0833H9.02916C8.93224 16.0833 8.87758 15.972 8.93684 15.8953L11.2156 12.9463C11.2481 12.9043 11.2481 12.8456 11.2156 12.8036L8.93686 9.85465C8.87761 9.77796 8.93227 9.66665 9.02918 9.66665H10.1926C10.2287 9.66665 10.2628 9.68339 10.2849 9.71198L11.9076 11.8119ZM13.7149 16.038L11.9076 13.938L12.7291 12.875L15.063 15.8953C15.1222 15.972 15.0676 16.0833 14.9707 16.0833H13.8072C13.771 16.0833 13.737 16.0666 13.7149 16.038Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_2326_66186">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
<path d="M4 2.5C4 1.94772 4.44772 1.5 5 1.5H14.7929C14.9255 1.5 15.0527 1.55268 15.1464 1.64645L19.8536 6.35355C19.9473 6.44732 20 6.5745 20 6.70711V21.5C20 22.0523 19.5523 22.5 19 22.5H5C4.44772 22.5 4 22.0523 4 21.5V2.5Z" fill="#34C724"/>
<path d="M15 1.54492C15.054 1.56949 15.1037 1.6037 15.1464 1.64646L19.8536 6.35357C19.8963 6.39632 19.9305 6.44602 19.9551 6.50001H16C15.4477 6.50001 15 6.0523 15 5.50001V1.54492Z" fill="#2CA91F"/>
<path d="M11.308 13.5956L8.33203 17.9996H9.60403L11.98 14.4596L14.284 17.9996H15.676L12.664 13.5956L15.496 9.43164H14.224L11.992 12.7796L9.85603 9.43164H8.48803L11.308 13.5956Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 741 B

View File

@ -1,20 +1,21 @@
<svg width="134" height="34" xmlns="http://www.w3.org/2000/svg">
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
</g>
<g>
<title>Layer 1</title>
<g id="logo">
<path d="m15.5761,0.45268l14.1893,8.19041l0,16.38081l-14.1893,8.1919l-14.18786,-8.1919l0,-16.38381l14.18786,-8.1904l0,0.00299zm0,-0.45268l-14.5761,8.41524l0,16.83056l14.5761,8.4152l14.576,-8.4152l0,-16.82605l-14.576,-8.41527l0,-0.00448z" id="Vector"/>
<path d="m15.5766,5.44028l9.8692,5.69612l0,11.3922l-9.8692,5.6961l-9.86781,-5.6961l0,-11.3922l9.86781,-5.69612zm0,-3.05942l-12.51801,7.22658l0,14.45616l12.51801,7.2266l12.5194,-7.2266l0,-14.45616l-12.5194,-7.22658z" id="Vector_10"/>
<path d="m9.51732,16.9849c-0.25492,0.0013 -0.50306,-0.0821 -0.70548,-0.2371c-0.20243,-0.1549 -0.34771,-0.3727 -0.41304,-0.6191c-0.06533,-0.2464 -0.04703,-0.5075 0.05204,-0.7424c0.09907,-0.2349 0.2733,-0.4303 0.49537,-0.5554l4.07869,-2.3534c0.2641,-0.1471 0.5753,-0.1844 0.8666,-0.1039c0.2913,0.0805 0.5393,0.2722 0.6905,0.534c0.1511,0.2617 0.1932,0.5723 0.1173,0.8649c-0.0759,0.2925 -0.2638,0.5434 -0.5232,0.6986l-4.0802,2.3534c-0.17512,0.104 -0.37487,0.1594 -0.57858,0.1604z" id="Vector_11"/>
<path d="m16.7379,12.8226c-0.2534,-0.0001 -0.4997,-0.0837 -0.7007,-0.238c-0.201,-0.1542 -0.3455,-0.3705 -0.4111,-0.6152c-0.0655,-0.2448 -0.0485,-0.5043 0.0484,-0.7384c0.0969,-0.2341 0.2684,-0.4297 0.4877,-0.5564l1.499,-0.86044c0.1311,-0.08093 0.2772,-0.13471 0.4294,-0.15817c0.1523,-0.02345 0.3078,-0.01609 0.4571,0.02163c0.1494,0.03772 0.2897,0.10505 0.4126,0.19799c0.1229,0.09294 0.2259,0.20959 0.3028,0.34309c0.077,0.1334 0.1264,0.281 0.1453,0.4339c0.0189,0.1529 0.0069,0.3081 -0.0353,0.4563c-0.0421,0.1482 -0.1136,0.2864 -0.2102,0.4065c-0.0966,0.12 -0.2163,0.2194 -0.352,0.2924l-1.4989,0.8604c-0.1745,0.1012 -0.3725,0.1544 -0.5741,0.1544z" id="Vector_12"/>
<path d="m11.2227,20.4977c-0.2535,0.0003 -0.4999,-0.0831 -0.7012,-0.2373c-0.2012,-0.1541 -0.346,-0.3704 -0.4118,-0.6152c-0.0657,-0.2448 -0.0489,-0.5045 0.048,-0.7387c0.0969,-0.2343 0.2684,-0.43 0.4879,-0.5568l0.039,-0.0225c0.2636,-0.1432 0.5727,-0.1779 0.8614,-0.0967c0.2888,0.0813 0.5345,0.272 0.6847,0.5317c0.1502,0.2596 0.1932,0.5676 0.1197,0.8585c-0.0734,0.2908 -0.2575,0.5415 -0.5131,0.6986l-0.0389,0.0225c-0.1748,0.1018 -0.3734,0.1556 -0.5757,0.1559z" id="Vector_13"/>
<path d="m14.3599,18.7279c-0.2533,0.0004 -0.4996,-0.0828 -0.7008,-0.2366c-0.2013,-0.1538 -0.3461,-0.3697 -0.4122,-0.6142c-0.0661,-0.2445 -0.0497,-0.504 0.0467,-0.7383c0.0963,-0.2342 0.2672,-0.4301 0.4862,-0.5574l5.5717,-3.2498c0.1307,-0.0795 0.276,-0.1321 0.4273,-0.1548c0.1513,-0.0227 0.3056,-0.015 0.4539,0.0226c0.1483,0.0377 0.2876,0.1045 0.4098,0.1966c0.1222,0.0921 0.2248,0.2076 0.3018,0.3398c0.077,0.1322 0.1269,0.2785 0.1468,0.4302c0.0198,0.1517 0.0092,0.3058 -0.0312,0.4534c-0.0404,0.1476 -0.1099,0.2856 -0.2042,0.406c-0.0944,0.1205 -0.2119,0.2209 -0.3455,0.2954l-5.5717,3.2453c-0.1751,0.1043 -0.3748,0.1602 -0.5786,0.1618z" id="Vector_14"/>
<path d="m12.9244,24.0106c-0.2534,0 -0.4997,-0.0837 -0.7007,-0.2379c-0.201,-0.1543 -0.3454,-0.3706 -0.411,-0.6153c-0.0656,-0.2448 -0.0486,-0.5043 0.0483,-0.7384c0.0969,-0.2341 0.2684,-0.4297 0.4878,-0.5564l8.7105,-5.0231c0.2646,-0.1524 0.5789,-0.1936 0.8738,-0.1143c0.2949,0.0793 0.5462,0.2724 0.6987,0.537c0.1524,0.2646 0.1935,0.5789 0.1143,0.8738c-0.0793,0.2949 -0.2725,0.5462 -0.5371,0.6986l-8.709,5.0216c-0.1749,0.1014 -0.3735,0.1547 -0.5756,0.1544z" id="Vector_15"/>
</g>
<text font-weight="bold" stroke="#000" transform="matrix(0.6079756476739105,0,0,0.6079756476739105,235.5518780268556,15.097456893304098) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_1" y="11.491753" x="-337.077494" stroke-width="0" fill="#ffffff">GIS-BI开发平台</text>
</g>
<svg width="134" height="34" viewBox="0 0 134 34" xmlns="http://www.w3.org/2000/svg">
<g id="logo">
<path id="Vector" d="M15.5761 0.452676L29.7654 8.64309V25.0239L15.5761 33.2158L1.38824 25.0239V8.64009L15.5761 0.449693V0.452676ZM15.5761 0L1 8.41524V25.2458L15.5761 33.661L30.1521 25.2458V8.41975L15.5761 0.00448303V0Z" />
<g id="Group 11">
<path id="Vector_2" d="M90.8091 15.0902H95.4154L94.8158 18.298H90.8091V22.0769H96.3073L96.5532 25.5471H86.2852V7.95508H96.5591L96.3133 11.3577H90.8091V15.0902Z" />
<path id="Vector_3" d="M62.4051 17.8006V25.5204H59.0309L59.0159 24.7394C57.8357 25.353 56.683 25.6593 55.5578 25.6583H55.2235C54.0673 25.6583 53.1484 25.2141 52.4669 24.3257C52.0155 23.6322 51.7798 22.8206 51.7893 21.9933V21.9018C51.7893 20.3849 52.3215 19.3776 53.3858 18.8799C53.8215 18.5891 54.9282 18.4437 56.706 18.4437H58.5213V18.0195C58.5213 17.1461 58.3948 16.6464 58.142 16.5205C57.8662 16.3136 57.38 16.2102 56.6835 16.2102H53.065L53.1264 13.1163L57.2921 13.0039C59.7654 13.0039 61.2974 13.617 61.888 14.8431C62.2327 15.5517 62.4051 16.5375 62.4051 17.8006ZM55.9085 21.0069C55.7931 21.1433 55.6717 21.5885 55.6717 21.9063C55.6717 22.5958 56.0215 22.9406 56.721 22.9406C57.1417 22.9406 57.7413 22.7682 58.5198 22.4235V20.7371C58.5198 20.7371 56.3267 20.5078 55.9085 21.0069Z" />
<path id="Vector_4" d="M84.9158 17.8006V25.5204H81.5431L81.5266 24.7394C80.3474 25.353 79.1947 25.6593 78.0685 25.6583H77.7357C76.5785 25.6583 75.6591 25.2141 74.9776 24.3257C74.5268 23.632 74.2911 22.8205 74.3001 21.9933V21.9018C74.3001 20.3849 74.8322 19.3776 75.8965 18.8799C76.3342 18.5891 77.4409 18.4437 79.2167 18.4437H81.032V18.0195C81.032 17.1461 80.9055 16.6464 80.6527 16.5205C80.3769 16.3136 79.8907 16.2102 79.1942 16.2102H75.5817L75.6431 13.1163L79.8088 13.0039C82.2841 13.0039 83.8161 13.617 84.4047 14.8431C84.7454 15.5517 84.9158 16.5375 84.9158 17.8006ZM78.4252 21.0069C78.3098 21.1433 78.1899 21.5885 78.1899 21.9063C78.1899 22.5958 78.5397 22.9406 79.2392 22.9406C79.6609 22.9406 80.2605 22.7682 81.038 22.4235V20.7371C81.038 20.7371 78.845 20.5078 78.4252 21.0069Z" />
<path id="Vector_5" d="M108.469 17.8006V25.5204H105.096L105.079 24.7394C103.899 25.353 102.746 25.6593 101.621 25.6583H101.288C100.131 25.6583 99.2119 25.2141 98.5303 24.3257C98.0795 23.632 97.8438 22.8205 97.8528 21.9933V21.9018C97.8528 20.3849 98.3854 19.3776 99.4507 18.8799C99.8874 18.5891 100.994 18.4437 102.771 18.4437H104.585V18.0195C104.585 17.1461 104.459 16.6464 104.207 16.5205C103.931 16.3136 103.444 16.2102 102.747 16.2102H99.1284L99.1899 13.1163L103.356 13.0039C105.83 13.0039 107.362 13.617 107.951 14.8431C108.296 15.5517 108.469 16.5375 108.469 17.8006ZM101.972 21.0069C101.857 21.1433 101.737 21.5885 101.737 21.9063C101.737 22.5958 102.086 22.9406 102.786 22.9406C103.208 22.9406 103.807 22.7682 104.585 22.4235V20.7371C104.585 20.7371 102.392 20.5078 101.972 21.0069Z" />
<path id="Vector_6" d="M72.5651 25.5457H70.6164C70.2207 25.5457 69.8819 25.5352 69.6001 25.5112C68.9622 25.4862 68.3344 25.3429 67.7489 25.0885C67.1737 24.8171 66.7042 24.3634 66.4133 23.7978C66.0695 23.1433 65.8981 22.2674 65.8991 21.1702L65.8767 16.2355H64.0029V13.0322H65.8782V9.04492L69.7155 9.78391V13.0322H72.64L72.2398 16.2415H69.7095V20.9603C69.7095 21.0667 69.7095 21.1582 69.7215 21.2346C69.7303 21.4013 69.761 21.5661 69.813 21.7248C69.8741 21.8836 69.9722 22.0256 70.0992 22.139C70.2261 22.2524 70.3783 22.3339 70.543 22.3768C70.9371 22.5256 71.3478 22.6263 71.7661 22.6766C71.8576 22.6916 71.952 22.7036 72.0509 22.7111L72.2908 22.7336L72.5651 22.7456V25.5457Z" />
<path id="Vector_7" d="M132.698 18.1764V20.3949H125.635L125.62 20.7981C125.62 21.6436 125.957 22.1337 126.476 22.3166C126.924 22.4361 127.386 22.4936 127.849 22.4875H128.158C129.531 22.4875 131.536 22.1787 132.977 21.8879H132.999L132.419 25.0223C132.419 25.0612 131.745 25.3475 130.433 25.5604C129.491 25.6581 128.544 25.7041 127.597 25.6983C124.852 25.6983 123.079 24.7714 122.279 22.9177C121.979 22.1862 121.706 21.4187 121.706 20.6228V17.6248C121.706 15.4288 122.755 13.9658 124.601 13.2403C125.447 12.9688 126.331 12.8337 127.219 12.8401C129.806 12.8401 131.483 13.683 132.253 15.3688C132.55 16.0274 132.698 16.9633 132.698 18.1764ZM128.997 17.5918C128.997 16.7239 128.738 16.1423 128.365 15.9819C128.083 15.7916 127.647 15.7631 127.242 15.7631C126.213 15.7631 125.699 16.2472 125.698 17.2156L125.635 17.7987H128.997V17.5918Z" />
<path id="Vector_8" fill-rule="evenodd" clip-rule="evenodd" d="M42.5183 7.77734H35.7324V25.5462H42.5183C46.8413 25.5462 50.3475 22.6172 50.3475 19.0062V14.3189C50.3475 10.7078 46.8413 7.78634 42.5183 7.77734ZM45.9869 19.1591C45.9869 20.7615 44.4355 22.0596 42.5183 22.0611H40.2533V11.2685H42.5183C44.4355 11.2685 45.9869 12.5696 45.9869 14.1705V19.1591Z" />
<path id="Vector_9" d="M114.735 25.8947C114.015 25.8947 113.226 25.8532 112.367 25.7703C112.037 25.7373 111.698 25.6998 111.35 25.6548L110.536 25.5394L110.087 22.1487L110.961 22.486L112.038 22.7588C112.466 22.8188 112.867 22.8652 113.246 22.8982C113.697 22.9372 114.105 22.9567 114.471 22.9567C115.801 22.9567 116.57 22.9252 116.777 22.3811C116.892 22.1547 116.886 21.5386 116.759 21.3318C116.631 21.1249 116.358 20.8911 115.945 20.7217C115.768 20.6498 115.544 20.5718 115.276 20.4984L114.489 20.284C114.143 20.2075 113.831 20.1341 113.557 20.0531C113.203 19.9532 112.88 19.8488 112.587 19.7399C111.969 19.5266 111.396 19.2025 110.895 18.7835C110.515 18.4209 110.256 17.9497 110.154 17.4344C110.073 17.0072 110.064 16.5695 110.127 16.1393C110.292 14.7403 110.954 13.7809 112.112 13.2613C112.608 13.0344 113.135 12.8828 113.675 12.8116C113.914 12.7786 114.185 12.7516 114.489 12.7291L115.276 12.6797H115.807C116.406 12.6797 117.071 12.7127 117.8 12.7786L118.688 12.8686L119.437 12.9675L119.749 16.0149C119.256 15.9002 118.755 15.823 118.25 15.7841C117.548 15.7104 116.842 15.6834 116.137 15.7031C114.745 15.7031 113.929 15.6267 113.684 16.1933C113.639 16.3007 113.617 16.4168 113.62 16.5334C113.623 16.6501 113.652 16.7647 113.704 16.8693C113.838 17.1172 114.13 17.3345 114.581 17.5214C114.769 17.5993 115.007 17.6818 115.294 17.7687L117.105 18.2559C117.453 18.3558 117.764 18.4557 118.039 18.5557C118.592 18.7421 119.102 19.0355 119.542 19.4191C119.942 19.7768 120.214 20.2544 120.319 20.7802C120.398 21.3324 120.408 21.8924 120.347 22.447C120.182 23.8321 119.52 24.7914 118.361 25.3251C117.867 25.5564 117.339 25.7082 116.798 25.7748C116.559 25.8077 116.288 25.8362 115.984 25.8572L115.279 25.8992L114.735 25.8947Z" />
</g>
<path id="Vector_10" d="M15.5766 5.44028L25.4458 11.1364V22.5286L15.5766 28.2247L5.70879 22.5286V11.1364L15.5766 5.44028ZM15.5766 2.38086L3.05859 9.60744V24.0636L15.5766 31.2902L28.096 24.0636V9.60744L15.5766 2.38086Z" />
<path id="Vector_11" d="M9.51732 16.9849C9.2624 16.9862 9.01426 16.9028 8.81184 16.7478C8.60941 16.5929 8.46413 16.3751 8.3988 16.1287C8.33347 15.8823 8.35177 15.6212 8.45084 15.3863C8.54991 15.1514 8.72414 14.956 8.94621 14.8309L13.0249 12.4775C13.289 12.3304 13.6002 12.2931 13.8915 12.3736C14.1828 12.4541 14.4308 12.6458 14.582 12.9076C14.7331 13.1693 14.7752 13.4799 14.6993 13.7725C14.6234 14.065 14.4355 14.3159 14.1761 14.4711L10.0959 16.8245C9.92078 16.9285 9.72103 16.9839 9.51732 16.9849Z" />
<path id="Vector_12" d="M16.7379 12.8226C16.4845 12.8225 16.2382 12.7389 16.0372 12.5846C15.8362 12.4304 15.6917 12.2141 15.6261 11.9694C15.5606 11.7246 15.5776 11.4651 15.6745 11.231C15.7714 10.9969 15.9429 10.8013 16.1622 10.6746L17.6612 9.81416C17.7923 9.73323 17.9384 9.67945 18.0906 9.65599C18.2429 9.63254 18.3984 9.6399 18.5477 9.67762C18.6971 9.71534 18.8374 9.78267 18.9603 9.87561C19.0832 9.96855 19.1862 10.0852 19.2631 10.2187C19.3401 10.3521 19.3895 10.4997 19.4084 10.6526C19.4273 10.8055 19.4153 10.9607 19.3731 11.1089C19.331 11.2571 19.2595 11.3953 19.1629 11.5154C19.0663 11.6354 18.9466 11.7348 18.8109 11.8078L17.312 12.6682C17.1375 12.7694 16.9395 12.8226 16.7379 12.8226Z" />
<path id="Vector_13" d="M11.2227 20.4977C10.9692 20.498 10.7228 20.4146 10.5215 20.2604C10.3203 20.1063 10.1755 19.89 10.1097 19.6452C10.044 19.4004 10.0608 19.1407 10.1577 18.9065C10.2546 18.6722 10.4261 18.4765 10.6456 18.3497L10.6846 18.3272C10.9482 18.184 11.2573 18.1493 11.546 18.2305C11.8348 18.3118 12.0805 18.5025 12.2307 18.7622C12.3809 19.0218 12.4239 19.3298 12.3504 19.6207C12.277 19.9115 12.0929 20.1622 11.8373 20.3193L11.7984 20.3418C11.6236 20.4436 11.425 20.4974 11.2227 20.4977Z" />
<path id="Vector_14" d="M14.3599 18.7279C14.1066 18.7283 13.8603 18.6451 13.6591 18.4913C13.4578 18.3375 13.313 18.1216 13.2469 17.8771C13.1808 17.6326 13.1972 17.3731 13.2936 17.1388C13.3899 16.9046 13.5608 16.7087 13.7798 16.5814L19.3515 13.3316C19.4822 13.2521 19.6275 13.1995 19.7788 13.1768C19.9301 13.1541 20.0844 13.1618 20.2327 13.1994C20.381 13.2371 20.5203 13.3039 20.6425 13.396C20.7647 13.4881 20.8673 13.6036 20.9443 13.7358C21.0213 13.868 21.0712 14.0143 21.0911 14.166C21.1109 14.3177 21.1003 14.4718 21.0599 14.6194C21.0195 14.767 20.95 14.905 20.8557 15.0254C20.7613 15.1459 20.6438 15.2463 20.5102 15.3208L14.9385 18.5661C14.7634 18.6704 14.5637 18.7263 14.3599 18.7279Z" />
<path id="Vector_15" d="M12.9244 24.0106C12.671 24.0106 12.4247 23.9269 12.2237 23.7727C12.0227 23.6184 11.8783 23.4021 11.8127 23.1574C11.7471 22.9126 11.7641 22.6531 11.861 22.419C11.9579 22.1849 12.1294 21.9893 12.3488 21.8626L21.0593 16.8395C21.3239 16.6871 21.6382 16.6459 21.9331 16.7252C22.228 16.8045 22.4793 16.9976 22.6318 17.2622C22.7842 17.5268 22.8253 17.8411 22.7461 18.136C22.6668 18.4309 22.4736 18.6822 22.209 18.8346L13.5 23.8562C13.3251 23.9576 13.1265 24.0109 12.9244 24.0106Z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1 +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="1733971867948" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4250" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M703.232 67.008h127.488v413.248c0 158.016-142.656 286.016-318.72 286.016-176 0-318.72-128-318.72-286.016V67.008h127.488v413.248c0 39.872 18.176 78.144 51.136 107.776 36.8 32.96 86.528 51.072 140.096 51.072s103.36-18.112 140.032-51.136c33.024-29.632 51.2-67.968 51.2-107.776V67.008zM193.28 871.616h637.44v85.376H193.28v-85.376z" fill="#040000" p-id="4251"></path></svg>
<?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="1733971867948" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4250" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M703.232 67.008h127.488v413.248c0 158.016-142.656 286.016-318.72 286.016-176 0-318.72-128-318.72-286.016V67.008h127.488v413.248c0 39.872 18.176 78.144 51.136 107.776 36.8 32.96 86.528 51.072 140.096 51.072s103.36-18.112 140.032-51.136c33.024-29.632 51.2-67.968 51.2-107.776V67.008zM193.28 871.616h637.44v85.376H193.28v-85.376z" p-id="4251"></path></svg>

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 687 B

View File

@ -1,45 +1,98 @@
<script lang="tsx" setup>
import dvUpArrow from '@/assets/svg/dv-up-arrow.svg'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t } = useI18n()
defineProps({
themes: {
type: String,
default: 'light'
}
})
</script>
<template>
<div class="view-panel-Mask">
<Icon class-name="item-icon" name="dv-up-arrow"><dvUpArrow class="svg-icon item-icon" /></Icon>
<div>
<el-button style="opacity: 1 !important" type="warning" size="mini" round>
<span style="font-weight: bold; opacity: 1">{{
t('visualization.template_view_tips')
}}</span>
</el-button>
</div>
<div class="view-panel-mask-left"></div>
<div class="view-panel-mask">
<el-popover
:visible="true"
placement="bottom"
popper-class="template-popper-tips"
:width="256"
show-arrow
>
<div class="template-popper-tips-content">
<p class="constant">{{ t('visualization.template_view_tips') }}</p>
</div>
<template #reference>
<div
class="view-panel-mask-inner"
:class="{ 'view-panel-mask-inner-dark': themes === 'dark' }"
></div>
</template>
</el-popover>
</div>
</template>
<style lang="less" scoped>
.view-panel-Mask {
display: flex;
height: calc(100vh - 148px);
width: 100%;
background-color: rgba(92, 94, 97, 0.7);
.view-panel-mask-left {
height: 100%;
width: 240px;
position: absolute;
top: 85px;
left: 0px;
top: 0;
left: 0;
cursor: not-allowed;
background-color: rgba(31, 35, 41);
opacity: 0.4;
z-index: 2;
}
.view-panel-mask {
height: 100%;
width: 178px;
background-color: rgba(31, 35, 41);
opacity: 0.4;
position: absolute;
top: 0;
left: 240px;
z-index: 2;
cursor: not-allowed;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.view-panel-mask-inner {
top: 51px;
left: 6px;
height: 34px;
width: 170px;
background: white;
position: relative;
pointer-events: none;
border-radius: 5px;
}
.item-icon {
position: absolute;
top: 10px;
left: 300px;
width: 40px;
height: 40px;
opacity: 1;
color: #ff8800;
.view-panel-mask-inner-dark {
background: rgba(31, 35, 41);
}
</style>
<style lang="less">
.template-popper-tips {
z-index: 1000 !important;
padding: 24px !important;
box-shadow: none !important;
border: 0 !important;
background: var(--ed-color-primary) !important;
inset: 0 auto auto -5px !important;
.ed-popper__arrow::before {
border: 1px solid var(--ed-color-primary) !important;
background: var(--ed-color-primary) !important;
}
}
.template-popper-tips-content {
color: rgba(255, 255, 255, 1);
.content {
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-weight: 500;
line-height: 22px;
text-align: left;
}
}
</style>

View File

@ -0,0 +1,76 @@
<script lang="ts" setup>
import dvAi from '@/assets/svg/dv-ai.svg'
import { ref } from 'vue'
const visible = ref(true)
</script>
<template>
<el-popover
:visible="visible"
placement="bottom"
popper-class="ai-popper-tips"
:width="288"
show-arrow
>
<div class="ai-popper-tips-content">
<p class="constant">
你好我是 DataEase 智能客服<br />点击一下开启高效解答模式~<br />&nbsp;
</p>
</div>
<template #reference>
<div class="ai-popper-tips-icon">
<el-icon style="margin: 2px" class="ai-icon">
<Icon name="dv-ai"><dvAi class="svg-icon" /></Icon>
</el-icon>
</div>
</template>
</el-popover>
</template>
<style lang="less">
.ai-popper-tips {
z-index: 10001 !important;
padding: 24px !important;
box-shadow: none !important;
border: 0px !important;
background: var(--ed-color-primary) !important;
.ed-popper__arrow::before {
border: 1px solid var(--ed-color-primary) !important;
background: var(--ed-color-primary) !important;
}
}
.ai-popper-tips-content {
color: rgba(255, 255, 255, 1);
.title {
font-family: var(--de-custom_font, 'PingFang');
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.content {
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-weight: 500;
line-height: 22px;
text-align: left;
}
.bottom {
line-height: 22px;
text-align: right;
button {
border: 0px !important;
border-color: #ffffff !important;
font-weight: 500;
color: rgba(51, 112, 255, 1) !important;
}
}
}
.ai-popper-tips-icon {
margin: 0 8px;
z-index: 10003;
border-radius: 50%;
background: #ffffff;
width: 28px;
height: 28px;
}
</style>

View File

@ -1,6 +1,6 @@
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { deepCopy } from '@/data-visualization/utils/utils'
import { formatterItem } from '@/data-visualization/chart/components/js/formatter'
import { formatterItem, isEnLocal } from '@/data-visualization/chart/components/js/formatter'
const { t } = useI18n()
export const DEFAULT_COLOR_CASE: DeepPartial<ChartAttr> = {
@ -318,6 +318,32 @@ export const DEFAULT_MISC: ChartMiscAttr = {
min: 0,
max: 0,
fieldId: undefined
},
bullet: {
bar: {
ranges: {
fill: ['rgba(0,128,255,0.3)'],
size: 20,
showType: 'dynamic',
fixedRangeNumber: 3,
symbol: 'circle',
symbolSize: 4
},
measures: {
fill: ['rgba(0,128,255,1)'],
size: 15,
symbol: 'circle',
symbolSize: 4
},
target: {
fill: 'rgb(0,0,0)',
size: 20,
showType: 'dynamic',
value: 0,
symbol: 'line',
symbolSize: 4
}
}
}
}
@ -453,7 +479,8 @@ export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = {
headerGroupConfig: {
columns: [],
meta: []
}
},
rowHeaderFreeze: true
}
export const DEFAULT_TABLE_CELL: ChartTableCellAttr = {
tableFontColor: '#000000',
@ -552,17 +579,6 @@ export const DEFAULT_TITLE_STYLE_DARK = {
remarkBackgroundColor: '#5A5C62'
}
export const DEFAULT_LEGEND_STYLE: ChartLegendStyle = {
show: true,
hPosition: 'center',
vPosition: 'bottom',
orient: 'horizontal',
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4
}
export const DEFAULT_LEGEND_STYLE_BASE: ChartLegendStyle = {
show: true,
hPosition: 'center',
@ -571,7 +587,24 @@ export const DEFAULT_LEGEND_STYLE_BASE: ChartLegendStyle = {
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4
size: 4,
showRange: true,
sort: 'none',
customSort: []
}
export const DEFAULT_LEGEND_STYLE: ChartLegendStyle = {
show: true,
hPosition: 'center',
vPosition: 'bottom',
orient: 'horizontal',
icon: 'circle',
color: '#333333',
fontSize: 12,
size: 4,
showRange: true,
sort: 'none',
customSort: []
}
export const DEFAULT_LEGEND_STYLE_LIGHT: ChartLegendStyle = {
@ -634,6 +667,7 @@ export const DEFAULT_XAXIS_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -680,6 +714,7 @@ export const DEFAULT_YAXIS_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -724,6 +759,7 @@ export const DEFAULT_YAXIS_EXT_STYLE: ChartAxisStyle = {
},
axisLabelFormatter: {
type: 'auto',
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1,
suffix: '',
decimalCount: 2,
@ -1165,7 +1201,7 @@ export const CHART_FONT_FAMILY = [
{ name: t('chart.font_family_kai_ti'), value: 'KaiTi' }
]
export const CHART_FONT_FAMILY_MAP:any = {
export const CHART_FONT_FAMILY_MAP = {
'Microsoft YaHei': 'Microsoft YaHei',
SimSun: 'SimSun, "Songti SC", STSong',
SimHei: 'SimHei, Helvetica',
@ -1395,6 +1431,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'stock-line',
title: t('chart.chart_stock_line'),
icon: 'stock-line'
},
{
render: 'antv',
category: 'compare',
value: 'bullet-graph',
title: t('chart.bullet_chart'),
icon: 'bullet-graph'
}
]
},
@ -1651,6 +1694,7 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
zoomButtonColor: '#aaa',
zoomBackground: '#fff',
tableLayoutMode: 'grid',
defaultExpandLevel: 1,
calcTopN: false,
topN: 5,
topNLabel: t('datasource.other'),
@ -1679,7 +1723,9 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
radarAreaColor: true,
circleBorderColor: '#fff',
circleBorderWidth: 0,
circlePadding: 0
circlePadding: 0,
quotaPosition: 'col',
quotaColLabel: t('dataset.value')
}
export const BASE_VIEW_CONFIG = {

File diff suppressed because one or more lines are too long

View File

@ -79,7 +79,8 @@ function createExtremumDiv(id, value, formatterCfg, chart) {
transform: translateX(-50%);
opacity: 1;
transition: opacity 0.2s ease-in-out;
white-space:nowrap;`
white-space:nowrap;
overflow:auto;`
)
div.textContent = valueFormatter(value, formatterCfg)
const span = document.createElement('span')
@ -109,7 +110,7 @@ const noChildrenFieldChart = chart => {
* 线
* @param chart
*/
const supportExtremumChartType = chart => {
export const supportExtremumChartType = chart => {
return ['line', 'area', 'bar', 'bar-group'].includes(chart.type)
}
@ -138,8 +139,8 @@ function removeDivsWithPrefix(parentDivId, prefix) {
export const extremumEvt = (newChart, chart, _options, container) => {
chart.container = container
clearExtremum(chart)
if (!supportExtremumChartType(chart)) {
clearExtremum(chart)
return
}
const { label: labelAttr } = parseJson(chart.customAttr)
@ -150,7 +151,9 @@ export const extremumEvt = (newChart, chart, _options, container) => {
i.forEach(item => {
delete item._origin.EXTREME
})
const { minItem, maxItem } = findMinMax(i.filter(item => item._origin.value))
const { minItem, maxItem } = findMinMax(
i.filter(item => item?._origin?.value !== null && item?._origin?.value !== undefined)
)
if (!minItem || !maxItem) {
return
}
@ -223,6 +226,7 @@ export const createExtremumPoint = (chart, ev) => {
divParent.style.zIndex = '1'
divParent.style.opacity = '0'
divParent.style.transition = 'opacity 0.2s ease-in-out'
divParent.style.overflow = 'visible'
// 将父标注加入到图表中
const containerElement = document.getElementById(chart.container)
containerElement.insertBefore(divParent, containerElement.firstChild)

View File

@ -1,7 +1,13 @@
import { Datum } from '@antv/g2plot'
import { find } from 'lodash-es'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { getLocale } from '@/data-visualization/utils/utils'
const { t } = useI18n()
export const isEnLocal = !['zh', 'zh-cn', 'zh-CN', 'tw'].includes(getLocale())
export const formatterItem = {
type: 'auto', // auto,value,percent
unitLanguage: isEnLocal ? 'en' : 'ch',
unit: 1, // 换算单位
suffix: '', // 单位后缀
decimalCount: 2, // 小数位数
@ -10,12 +16,51 @@ export const formatterItem = {
// 单位list
export const unitType = [
{ name: 'unit_none', value: 1 },
{ name: 'unit_thousand', value: 1000 },
{ name: 'unit_ten_thousand', value: 10000 },
{ name: 'unit_million', value: 1000000 },
{ name: 'unit_hundred_million', value: 100000000 }
{ name: t('chart.unit_none'), value: 1 },
{ name: t('chart.unit_thousand'), value: 1000 },
{ name: t('chart.unit_ten_thousand'), value: 10000 },
{ name: t('chart.unit_million'), value: 1000000 },
{ name: t('chart.unit_hundred_million'), value: 100000000 }
]
export const unitEnType = [
{ name: 'None', value: 1 },
{ name: 'Thousand (K)', value: 1000 },
{ name: 'Million (M)', value: 1000000 },
{ name: 'Billion (B)', value: 1000000000 }
]
export function getUnitTypeList(lang) {
if (isEnLocal) {
return unitEnType
}
if (lang === 'ch') {
return unitType
}
return unitEnType
}
export function getUnitTypeValue(lang, value) {
const list = getUnitTypeList(lang)
const item = find(list, l => l.value === value)
if (item) {
return value
}
return 1
}
export function initFormatCfgUnit(cfg) {
if (cfg && cfg.unitLanguage === undefined) {
cfg.unitLanguage = 'ch'
}
if (cfg && isEnLocal) {
cfg.unitLanguage = 'en'
}
onChangeFormatCfgUnitLanguage(cfg, cfg.unitLanguage)
}
export function onChangeFormatCfgUnitLanguage(cfg, lang) {
cfg.unit = getUnitTypeValue(lang, cfg.unit)
}
// 格式化方式
export const formatterType = [
@ -47,17 +92,32 @@ export function valueFormatter(value, formatter) {
}
function transUnit(value, formatter) {
initFormatCfgUnit(formatter)
return value / formatter.unit
}
function transDecimal(value, formatter) {
const resultV = value.toFixed(formatter.decimalCount)
const resultV = retain(value, formatter.decimalCount) as string
if (Object.is(parseFloat(resultV), -0)) {
return resultV.slice(1)
}
return resultV
}
function retain(value, n) {
if (!n) return Math.round(value)
const tran = Math.round(value * Math.pow(10, n)) / Math.pow(10, n)
let tranV = tran.toString()
const newVal = tranV.indexOf('.')
if (newVal < 0) {
tranV += '.'
}
for (let i = tranV.length - tranV.indexOf('.'); i <= n; i++) {
tranV += '0'
}
return tranV
}
function transSeparatorAndSuffix(value, formatter) {
let str = value + ''
if (str.match(/^(\d)(\.\d)?e-(\d)/)) {
@ -74,34 +134,27 @@ function transSeparatorAndSuffix(value, formatter) {
//百分比没有后缀,直接返回
return str
} else {
if (formatter.unit === 1000) {
str += '千'
} else if (formatter.unit === 10000) {
str += '万'
} else if (formatter.unit === 1000000) {
str += '百万'
} else if (formatter.unit === 100000000) {
str += '亿'
const unit = formatter.unit
if (formatter.unitLanguage === 'ch') {
if (unit === 1000) {
str += t('chart.unit_thousand')
} else if (unit === 10000) {
str += t('chart.unit_ten_thousand')
} else if (unit === 1000000) {
str += t('chart.unit_million')
} else if (unit === 100000000) {
str += t('chart.unit_hundred_million')
}
} else {
if (unit === 1000) {
str += 'K'
} else if (unit === 1000000) {
str += 'M'
} else if (unit === 1000000000) {
str += 'B'
}
}
}
return str + formatter.suffix.replace(/(^\s*)|(\s*$)/g, '')
}
export function singleDimensionTooltipFormatter(param: Datum, chart: Chart, prop = 'category') {
let res
const yAxis = chart.yAxis
const obj = { name: param[prop], value: param.value }
for (let i = 0; i < yAxis.length; i++) {
const f = yAxis[i]
if (f.name === param[prop]) {
if (f.formatterCfg) {
res = valueFormatter(param.value, f.formatterCfg)
} else {
res = valueFormatter(param.value, formatterItem)
}
break
}
}
obj.value = res ?? ''
return obj
}

View File

@ -0,0 +1,656 @@
import { DualAxes, Plot } from '@antv/g2plot'
/**
* 使 Map chart.container
*/
export const CAROUSEL_MANAGER_INSTANCES = new Map<string, ChartCarouselTooltip>()
/**
*
*/
const CHART_CATEGORY = {
COLUMN: ['bar', 'bar-stack', 'bar-group', 'bar-group-stack', 'percentage-bar-stack'],
LINE: ['line', 'area', 'area-stack'],
MIX: ['chart-mix', 'chart-mix-group', 'chart-mix-stack', 'chart-mix-dual-line'],
PIE: ['pie', 'pie-donut']
}
/**
*
* @param chartType
*/
export function isColumn(chartType: string) {
return CHART_CATEGORY.COLUMN.includes(chartType)
}
/**
* 线
* @param chartType
*/
export function isLine(chartType: string) {
return CHART_CATEGORY.LINE.includes(chartType)
}
/**
*
* @param chartType
*/
export function isPie(chartType: string) {
return CHART_CATEGORY.PIE.includes(chartType)
}
/**
*
* @param chartType
*/
export function isMix(chartType: string) {
return CHART_CATEGORY.MIX.includes(chartType)
}
export function isSupport(chartType: string) {
return Object.values(CHART_CATEGORY).some(category => category.includes(chartType))
}
// 轮播配置默认值
const DEFAULT_CAROUSEL_CONFIG: Required<CarouselConfig> = {
xField: '',
duration: 2000,
interval: 2000,
loop: true
}
type CarouselConfig = {
xField: string
duration?: number
interval?: number
loop?: boolean
}
/**
*
* */
class ChartCarouselTooltip {
private plot: Plot | DualAxes
private config: Required<CarouselConfig>
private currentIndex = 0
private values: string[] = []
// 合并定时器管理
private timers = { interval: null, carousel: null }
private states = { paused: false, destroyed: false }
// 图表可视性变化
private observers: Map<string, IntersectionObserver> = new Map()
// 图表元素大小变化
private resizeObservers: Map<string, ResizeObserver> = new Map()
// 图表是否在可视范围内
private chartIsVisible: boolean
private constructor(plot: Plot | DualAxes, private chart: Chart, config: CarouselConfig) {
this.plot = plot
this.config = { ...DEFAULT_CAROUSEL_CONFIG, ...config }
this.init()
}
/**
*
* */
static manage(plot: Plot | DualAxes, chart: Chart, config: CarouselConfig) {
if (!isSupport(chart.type)) return null
const container = chart.container
let instance = CAROUSEL_MANAGER_INSTANCES.get(container)
CAROUSEL_MANAGER_INSTANCES.forEach(instance => {
if (container.includes('viewDialog')) {
instance.paused()
}
})
if (instance) {
instance.update(plot, chart, config)
return instance
}
if (isSupport(chart.type)) {
instance = new this(plot, chart, config)
CAROUSEL_MANAGER_INSTANCES.set(container, instance)
}
return instance
}
/**
*
* @param container
*/
static destroyByContainer(container: string) {
const instance = CAROUSEL_MANAGER_INSTANCES.get(container)
if (instance) {
instance.destroy()
}
}
/**
* DOM获取对应实例
* */
static getInstanceByContainer(container: string) {
const instance = CAROUSEL_MANAGER_INSTANCES.get(container)
if (instance) {
return instance
}
return null
}
/**
* chart.id销毁对应实例
*
*
* */
static closeEnlargeDialogDestroy(id?: string) {
// 首先,暂停并删除包含 'viewDialog' 的实例
CAROUSEL_MANAGER_INSTANCES?.forEach((instance, key) => {
if (instance.chart.id === id && instance.chart.container.includes('viewDialog')) {
const dialogInstance = CAROUSEL_MANAGER_INSTANCES.get(key)
if (dialogInstance) {
dialogInstance.destroy()
}
}
})
setTimeout(() => {
// 然后,恢复
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
if (instance.chartIsVisible) {
instance.resume()
}
})
}, 400)
}
/**
*
* @param id
*/
static paused(id?: string) {
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
if (id && instance.chart.id === id) {
setTimeout(() => instance.paused(), 200)
}
if (!id) {
setTimeout(() => instance.paused(), 200)
}
})
}
/**
* @param id
*/
static resume(id?: string) {
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
if (instance.chart.id === id) {
instance.paused()
setTimeout(() => instance.resume(), 500)
}
if (!id) {
setTimeout(() => instance.resume(), 200)
}
})
}
/**
*
* */
private init() {
this.values = [].concat(this.getUniqueValues())
if (!this.values.length) return
this.chartIsVisible = true
this.states.paused = false
this.states.destroyed = false
this.bindEventListeners()
this.startCarousel()
}
/**
*
* */
private getUniqueValues() {
const data =
this.plot instanceof DualAxes
? [...this.plot.options.data[0], ...this.plot.options.data[1]]
: this.plot.options.data
return [...new Set(data.map(item => item[this.config.xField]))]
}
/**
*
* */
private startCarousel() {
if (!this.shouldStart()) {
this.stop()
return
}
// 定义启动嵌套定时器的函数
const startNestedTimers = () => {
// 重置当前索引
this.currentIndex = 0
// 定义递归处理数据数组的函数
const processArray = () => {
if (this.states.paused || this.states.destroyed || !this.isElementFullyVisible()) return
// 获取当前需要显示的值
const currentValue = this.values[this.currentIndex]
// 计算 Tooltip 显示的位置
const point = this.calculatePosition(currentValue)
// 高亮当前数据点
this.highlightElement(currentValue)
if (point) {
// 显示 Tooltip并设置其位置为顶部
this.plot.chart.showTooltip(point)
this.plot.chart.getController('tooltip').update()
}
// 更新索引,指向下一个数据点
this.currentIndex++
if (this.currentIndex > this.values.length) {
this.currentIndex = 0
this.hideTooltip()
this.plot.chart.showTooltip({ x: 0, y: 0 })
this.plot.chart.getController('tooltip').update()
this.unHighlightPoint(currentValue)
this.timers.interval = setTimeout(() => processArray(), this.config.interval)
} else {
// 如果未遍历完,继续处理下一个数据点
this.timers.carousel = setTimeout(() => processArray(), this.config.duration)
}
}
processArray()
}
this.stop()
startNestedTimers()
}
/**
* ' */
private shouldStart() {
return (
this.chart.customAttr?.tooltip?.show &&
this.chart.customAttr?.tooltip?.carousel?.enable &&
this.values.length > 0 &&
this.chartIsVisible
)
}
/**
*
* */
private isElementFullyVisible(): boolean {
// 全屏
const isFullscreen = document.fullscreenElement !== null
// 新页面或公共连接
const isNewPagePublicLink = document
.getElementById('enlarge-inner-content-' + this.chart.id)
?.getBoundingClientRect()
const isMobileEdit = document.getElementsByClassName('panel-mobile')?.length > 0
const isMobileList = document.getElementsByClassName('mobile-com-list')?.length > 0
if (isMobileList) {
return false
}
const rect = this.plot.chart.ele.getBoundingClientRect()
return (
rect.top >= (isFullscreen || isNewPagePublicLink || isMobileEdit ? 0 : 64) &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}
/**
*
* */
private calculatePosition(value: string) {
const view = this.plot.chart.views?.[0] || this.plot.chart
// 饼图特殊处理
if (CHART_CATEGORY.PIE.includes(this.chart.type)) {
return this.getPieTooltipPosition(view, value)
}
if (this.plot instanceof DualAxes) {
return this.getDualAxesTooltipPosition(view, value)
}
const types = view
.scale()
.getGeometries()
.map(item => item.type)
let point = { x: 0, y: 0 }
if (!types.length) return point
types.forEach(type => {
if (type === 'interval' || type === 'point') {
point = view
.scale()
.getGeometries()
.find(item => item.type === type)
.elements.find(item => item.data.field === value && (item.model.x || item.model.y))?.model
}
})
// 处理柱状图和折线图,柱状图固定y轴位置
const y = CHART_CATEGORY.COLUMN.includes(this.chart.type) ? 0 : [].concat(point?.y)?.[0]
return { x: [].concat(point?.x)?.[0], y: y }
}
/**
*
* */
private getPieTooltipPosition(view, value: string) {
const piePoint = view
.scale()
.getGeometries()[0]
?.elements.find(item => item.data.field === value)
?.getModel()
if (!piePoint) {
return { x: 0, y: 0 }
}
const coordinates = [
{ x: [].concat(piePoint.x)[0], y: piePoint.y[0] },
{ x: piePoint.x[0], y: piePoint.y[1] },
{ x: piePoint.x[1], y: piePoint.y[0] },
{ x: piePoint.x[1], y: piePoint.y[1] }
]
const index = coordinates.findIndex(coord => {
const items = this.plot.chart.getTooltipItems(coord)
return items.some(item => item.data.field === value)
})
if (index !== -1) {
return coordinates[index]
} else {
return {
x: piePoint.x[0],
y: piePoint.y[0]
}
}
}
/**
* Tooltip
* @param view
* @param value
* @private
*/
private getDualAxesTooltipPosition(view, value: string) {
const xScale = view.getXScale()
if (!xScale) return { x: 0, y: 0 }
const values = xScale.values
if (values.length < 2) {
const point = view
.getGeometries()?.[0]
.elements[view.getGeometries()?.[0].elements?.length - 1].getModel()
return point || { x: 0, y: 0 }
}
const [rangeStart, rangeEnd] = xScale.range
const totalMonths = values.length
const bandWidth = (rangeEnd - rangeStart) / totalMonths
const index = values.indexOf(value)
const xPos = rangeStart + bandWidth * (index + 0.5)
return view.getCoordinate().convert({ x: xPos, y: 0 })
}
/**
*
* */
private highlightElement(value: string) {
if (CHART_CATEGORY.LINE.includes(this.chart.type)) return
this.unHighlightPoint(value)
this.plot.setState(
this.getHighlightType(),
(data: any) => data[this.config.xField] === value,
true
)
}
/**
*
* **/
private unHighlightPoint(value?: string) {
if (CHART_CATEGORY.LINE.includes(this.chart.type)) return
this.plot.setState(
this.getHighlightType(),
(data: any) => data[this.config.xField] !== value,
false
)
}
private getHighlightType() {
return 'active'
}
/**
*
* */
private hideTooltip() {
const container = this.getTooltipContainer()
if (container) {
container.style.display = 'none'
}
}
/**
*
* */
private getTooltipContainer() {
const tooltipCtl = this.plot.chart.getController('tooltip')
if (!tooltipCtl) {
return
}
return tooltipCtl.tooltip?.cfg?.container
}
/**
*
* */
private bindEventListeners() {
// 定义图表元素ID前缀数组
// 图表在不同的显示页面可能有不同的ID前缀
const chartElementIds = ['enlarge-inner-content-', 'enlarge-inner-shape-']
let chartElement = null
// 查找图表元素
for (const idPrefix of chartElementIds) {
chartElement = document.getElementById(idPrefix + this.chart.id)
if (chartElement) break
}
// 绑定鼠标进入和离开事件
chartElement?.addEventListener('mouseenter', () => this.paused())
chartElement?.addEventListener('mouseleave', ev => {
setTimeout(() => {
// 获取鼠标位置
const mouseX = ev.clientX
const mouseY = ev.clientY
// 获取div的边界信息
const rect = chartElement.getBoundingClientRect()
// 判断鼠标位置是否在div内
const isInside =
mouseX >= rect.left + 10 &&
mouseX <= rect.right - 10 &&
mouseY >= rect.top + 10 &&
mouseY <= rect.bottom - 10
console.log(isInside)
if (!isInside) {
this.paused()
this.resume()
}
}, 300)
})
// 定义鼠标滚轮事件处理函数
const handleMouseWheel = this.debounce(() => {
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
instance.paused()
instance.resume()
})
}, 50)
// 定义 touchmove 事件处理函数(移动端)
const handleTouchMove = (event: TouchEvent) => {
handleMouseWheel(event)
}
// 获取目标元素,优先全屏预览
const targetDiv =
document.getElementById('de-preview-content') ||
document.getElementById('preview-canvas-main') ||
document.getElementById('dv-main-center') ||
document.getElementById('edit-canvas-main') ||
document.getElementById('canvas-mark-line') ||
document.getElementById('de-canvas-canvas-main')
// 绑定目标元素的事件
if (targetDiv) {
targetDiv.removeEventListener('wheel', handleMouseWheel)
targetDiv.addEventListener('wheel', handleMouseWheel)
//移除和添加 touchmove 事件监听器(移动端)
targetDiv.removeEventListener('touchmove', handleTouchMove)
targetDiv.addEventListener('touchmove', handleTouchMove)
}
// 页面可见性控制
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
instance.paused()
})
} else if (this.chartIsVisible) {
CAROUSEL_MANAGER_INSTANCES?.forEach(instance => {
instance.resume()
})
}
})
// 元素可视性观察(交叉观察器)
this.setupIntersectionObserver()
// 元素大小观察(大小观察器)
this.setupResizeObserver()
}
/**
*
* */
private setPaused(state: boolean) {
this.states.paused = state
state ? this.stop() : this.startCarousel()
}
/**
*
* */
private setupIntersectionObserver() {
setTimeout(() => {
// 监听元素可见性变化,全部可见时开始轮播
if (!this.observers.get(this.plot.chart.ele.id)) {
this.observers.set(
this.plot.chart.ele.id,
new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.intersectionRatio < 0.7) {
this.paused()
this.chartIsVisible = false
} else {
this.paused()
this.chartIsVisible = true
this.resume()
}
})
},
{ threshold: [0.7] }
)
)
this.observers.get(this.plot.chart.ele.id).observe(this.plot.chart.ele)
}
}, 100)
}
/**
*
*
*
* @private
*/
private setupResizeObserver() {
// 放大图表弹窗不需要监听
if (this.plot.chart.ele.id.includes('viewDialog')) return
// 创建防抖回调函数
const debouncedCallback = (entries: ResizeObserverEntry[]) => {
for (const entry of entries) {
if (entry.target) {
this.debounce(() => {
this.paused()
this.resume()
}, 200)
}
}
}
// 监听元素大小, 发生变化时重新轮播
if (!this.resizeObservers.get(this.plot.chart.ele.id)) {
this.resizeObservers.set(this.plot.chart.ele.id, new ResizeObserver(debouncedCallback))
this.resizeObservers.get(this.plot.chart.ele.id).observe(this.plot.chart.ele)
}
}
/**
*
* */
private update(plot: Plot | DualAxes, chart: Chart, config: CarouselConfig) {
this.stop()
this.plot = plot
this.chart = chart
this.config = { ...this.config, ...config }
this.currentIndex = 0
this.init()
}
/**
*
* @private
*/
private stop() {
clearTimeout(this.timers.interval)
clearTimeout(this.timers.carousel)
this.timers = { interval: null, carousel: null }
}
/**
*
* */
destroy() {
this.stop()
this.clearObserver()
this.states.destroyed = true
CAROUSEL_MANAGER_INSTANCES.delete(this.chart.container)
}
/**
*
* */
clearObserver() {
const observer = this.observers.get(this.plot.chart.ele.id)
if (observer) {
observer.disconnect()
this.observers.delete(this.plot.chart.ele.id)
}
const resizeObservers = this.resizeObservers.get(this.plot.chart.ele.id)
if (resizeObservers) {
resizeObservers.disconnect()
this.resizeObservers.delete(this.plot.chart.ele.id)
}
}
/** 暂停 */
paused() {
this.hideTooltip()
this.unHighlightPoint()
this.setPaused(true)
}
/** 恢复 */
resume() {
this.setPaused(false)
}
/**
*
*/
private debounce(func: (...args: any[]) => void, delay: number): (...args: any[]) => void {
let timeout: number | null = null
return (...args: any[]) => {
if (timeout) clearTimeout(timeout)
timeout = window.setTimeout(() => {
func(...args)
}, delay)
}
}
}
export default ChartCarouselTooltip

View File

@ -7,7 +7,6 @@ import {
import {
flow,
hexColorToRGBA,
hexToRgba,
parseJson,
setUpGroupSeriesColor,
setUpStackSeriesColor
@ -21,6 +20,7 @@ import {
} from '@/data-visualization/chart/components/js/panel/charts/bar/common'
import {
configPlotTooltipEvent,
configRoundAngle,
getLabel,
getPadding,
getTooltipContainer,
@ -43,7 +43,14 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
...BAR_EDITOR_PROPERTY_INNER,
'basic-style-selector': [...BAR_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'],
'label-selector': ['vPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'tooltip-selector': [
'fontSize',
'color',
'backgroundColor',
'seriesTooltipFormatter',
'show',
'carousel'
],
'y-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'], 'axisLabelFormatter']
}
protected baseOptions: ColumnOptions = {
@ -69,11 +76,14 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
async drawChart(drawOptions: G2PlotDrawOptions<Column>): Promise<Column> {
const { chart, container, action } = drawOptions
chart.container = container
if (!chart?.data?.data?.length) {
chart.container = container
clearExtremum(chart)
return
}
const isGroup = 'bar-group' === this.name && chart.xAxisExt?.length > 0
const isStack =
['bar-stack', 'bar-group-stack'].includes(this.name) && chart.extStack?.length > 0
const data = cloneDeep(drawOptions.chart.data?.data)
const initOptions: ColumnOptions = {
...this.baseOptions,
@ -108,7 +118,7 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
const label = {
fields: [],
...tmpOptions.label,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -174,19 +184,9 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
columnStyle
}
options = {
...options,
...configRoundAngle(chart, 'columnStyle')
}
let columnWidthRatio
const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio
@ -227,7 +227,10 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
tickCount: axisValue.splitCount
}
}
return { ...tmpOptions, ...axis }
// 根据axis的最小值过滤options中的data数据过滤掉小于最小值的数据
const { data } = options
const newData = data.filter(item => item.value >= axisValue.min)
return { ...tmpOptions, data: newData, ...axis }
}
return tmpOptions
}
@ -276,7 +279,14 @@ export class StackBar extends Bar {
'totalFormatter',
'showStackQuota'
],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
'tooltip-selector': [
'fontSize',
'color',
'backgroundColor',
'tooltipFormatter',
'show',
'carousel'
]
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
let label = getLabel(chart)
@ -438,6 +448,74 @@ export class GroupBar extends StackBar {
}
}
async drawChart(drawOptions: G2PlotDrawOptions<Column>): Promise<Column> {
const plot = await super.drawChart(drawOptions)
if (!plot) {
return plot
}
const { chart } = drawOptions
const { xAxis, xAxisExt, yAxis } = chart
let innerSort = !!(xAxis.length && xAxisExt.length && yAxis.length)
if (innerSort && yAxis[0].sort === 'none') {
innerSort = false
}
if (innerSort && xAxisExt[0].sort !== 'none') {
const sortPriority = chart.sortPriority ?? []
const yAxisIndex = sortPriority?.findIndex(e => e.id === yAxis[0].id)
const xAxisExtIndex = sortPriority?.findIndex(e => e.id === xAxisExt[0].id)
if (xAxisExtIndex <= yAxisIndex) {
innerSort = false
}
}
if (!innerSort) {
return plot
}
plot.chart.once('beforepaint', () => {
const geo = plot.chart.geometries[0]
const originMapping = geo.beforeMapping.bind(geo)
geo.beforeMapping = originData => {
const values = geo.getXScale().values
const valueMap = values.reduce((p, n) => {
if (!p?.[n]) {
p[n] = {
fieldArr: [],
indexArr: [],
dataArr: []
}
}
originData.forEach((arr, arrIndex) => {
arr.forEach((item, index) => {
if (item._origin.field === n) {
p[n].fieldArr.push(item.field)
p[n].indexArr.push([arrIndex, index])
p[n].dataArr.push(item)
}
})
})
return p
}, {})
values.forEach(v => {
const item = valueMap[v]
item.dataArr.sort((a, b) => {
if (yAxis[0].sort === 'asc') {
return a.value - b.value
}
if (yAxis[0].sort === 'desc') {
return b.value - a.value
}
return 0
})
item.indexArr.forEach((index, i) => {
item.dataArr[i].field = item.fieldArr[i]
originData[index[0]][index[1]] = item.dataArr[i]
})
})
return originMapping(originData)
}
})
return plot
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const tmpLabel = getLabel(chart)
if (!tmpLabel) {
@ -448,7 +526,7 @@ export class GroupBar extends StackBar {
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum, _point) {
formatter: function (param: Datum) {
if (param.EXTREME) {
return ''
}
@ -492,6 +570,7 @@ export class GroupBar extends StackBar {
super(name)
this.baseOptions = {
...this.baseOptions,
marginRatio: 0,
isGroup: true,
isStack: false,
meta: {
@ -606,7 +685,7 @@ export class PercentageStackBar extends GroupStackBar {
propertyInner = {
...this['propertyInner'],
'label-selector': ['color', 'fontSize', 'vPosition', 'reserveDecimalCount'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show']
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show', 'carousel']
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const baseOptions = super.configLabel(chart, options)

View File

@ -6,14 +6,14 @@ import { cloneDeep, defaultTo, isEmpty, map } from 'lodash-es'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
getTooltipItemConditionColor,
getYAxis,
getYAxisExt,
setGradientColor,
TOOLTIP_TPL,
addConditionsStyleColorToData
TOOLTIP_TPL
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import type {
BidirectionalBar as G2BidirectionalBar,
@ -213,19 +213,9 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
...options,
layout: basicStyle.layout
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
options = {
...options,
...configRoundAngle(chart, 'barStyle')
}
return options
}

View File

@ -0,0 +1,507 @@
import type {
Bullet as G2Bullet,
BulletOptions as G2BulletOptions
} from '@antv/g2plot/esm/plots/bullet'
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
import {
BAR_AXIS_TYPE,
BAR_EDITOR_PROPERTY,
BAR_EDITOR_PROPERTY_INNER
} from '@/data-visualization/chart/components/js/panel/charts/bar/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { flow, parseJson } from '@/data-visualization/chart/components/js/util'
import { BulletOptions } from '@antv/g2plot'
import { isEmpty } from 'lodash-es'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
getPadding,
getTooltipContainer,
TOOLTIP_TPL
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
const { t } = useI18n()
/**
*
*/
export class BulletGraph extends G2PlotChartView<G2BulletOptions, G2Bullet> {
constructor() {
super('bullet-graph', [])
}
axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt', 'extBubble']
axisConfig = {
...this['axisConfig'],
xAxis: { name: `${t('chart.form_type')} / ${t('chart.dimension')}`, type: 'd', limit: 1 },
yAxis: { name: `${t('chart.progress_current')} / ${t('chart.quota')}`, type: 'q', limit: 1 },
yAxisExt: { name: `${t('chart.progress_target')} / ${t('chart.quota')}`, type: 'q', limit: 1 },
extBubble: {
name: `${t('chart.range_bg')} / ${t('chart.quota')}`,
type: 'q',
allowEmpty: true,
limit: 1
}
}
properties: EditorProperty[] = [
...BAR_EDITOR_PROPERTY.filter(
item => !['function-cfg', 'assist-line', 'threshold'].includes(item)
),
'bullet-graph-selector'
]
propertyInner = {
'basic-style-selector': ['radiusColumnBar', 'layout'],
'label-selector': ['hPosition', 'fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'x-axis-selector': [
...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'].filter(item => item != 'position'),
'showLengthLimit'
],
'y-axis-selector': [
...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'].filter(
item => item !== 'axisValue' && item !== 'position'
),
'axisLabelFormatter'
],
'legend-selector': ['showRange', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition']
}
async drawChart(drawOption: G2PlotDrawOptions<G2Bullet>): Promise<G2Bullet> {
const { chart, container, action } = drawOption
if (!chart.data?.data?.length) return
const result = mergeBulletData(chart)
// 处理自定义区间
const { bullet } = parseJson(chart.customAttr).misc
if (bullet.bar.ranges.showType === 'fixed') {
const customRange = bullet.bar.ranges.fixedRange?.map(item => item.fixedRangeValue) || [0]
result.forEach(item => (item.ranges = customRange))
} else {
result.forEach(item => (item.ranges = item.originalRanges))
}
// 处理自定义目标值
if (bullet.bar.target.showType === 'fixed') {
const customTarget = bullet.bar.target.value || 0
result.forEach(item => (item.target = customTarget))
} else {
result.forEach(item => (item.target = item.originalTarget))
}
const initialOptions: BulletOptions = {
appendPadding: getPadding(chart),
data: result.reverse(),
measureField: 'measures',
rangeField: 'ranges',
targetField: 'target',
xField: 'title',
meta: {
title: {
type: 'cat'
}
},
interactions: [
{
type: 'active-region',
cfg: {
start: [{ trigger: 'element:mousemove', action: 'active-region:show' }],
end: [{ trigger: 'element:mouseleave', action: 'active-region:hide' }]
}
}
]
}
const options = this.setupOptions(chart, initialOptions)
let newChart = null
const { Bullet: BulletClass } = await import('@antv/g2plot/esm/plots/bullet')
newChart = new BulletClass(container, options)
newChart.on('element:click', ev => {
const pointData = ev?.data?.data
const dimensionList = options.data.find(item => item.title === pointData.title)?.dimensionList
const actionParams = {
x: ev.x,
y: ev.y,
data: {
data: {
...pointData,
dimensionList
}
}
}
action(actionParams)
})
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart, null)
return newChart
}
protected configBasicStyle(chart: Chart, options: BulletOptions): BulletOptions {
const basicStyle = parseJson(chart.customAttr).basicStyle
const { radiusColumnBar, columnBarRightAngleRadius, layout } = basicStyle
let radiusValue = 0
let rangeLength = 1
if (radiusColumnBar === 'roundAngle' || radiusColumnBar === 'topRoundAngle') {
radiusValue = columnBarRightAngleRadius
rangeLength = options.data[0]?.ranges?.length
}
const barRadiusStyle = { radius: Array(2).fill(radiusValue) }
const baseRadius = [...barRadiusStyle.radius, ...barRadiusStyle.radius]
options = {
...options,
bulletStyle: {
range: datum => {
if (!datum.rKey) return { fill: 'rgba(0, 0, 0, 0)' }
if (rangeLength === 1) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_0') {
return {
radius: radiusColumnBar === 'topRoundAngle' ? [] : [0, 0, ...barRadiusStyle.radius]
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_' + (rangeLength - 1)) {
return { radius: [...barRadiusStyle.radius, 0, 0] }
}
},
measure: datum => {
if (datum.measures) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
} else {
return undefined
}
},
target: datum => (datum.tKey === 'target' ? { lineWidth: 2 } : undefined)
}
}
if (layout === 'vertical') options = { ...options, layout: 'vertical' }
return options
}
protected configMisc(chart: Chart, options: BulletOptions): BulletOptions {
const { bullet } = parseJson(chart.customAttr).misc
const isDynamic = bullet.bar.ranges.showType === 'dynamic'
// 动态背景按大小升序
const rangeColor = isDynamic
? bullet.bar.ranges.fill
: bullet.bar.ranges.fixedRange
?.sort((a, b) => (a.fixedRangeValue ?? 0) - (b.fixedRangeValue ?? 0))
.map(item => item.fill) || []
return {
...options,
color: {
measure: [].concat(bullet.bar.measures.fill),
range: [].concat(rangeColor),
target: [].concat(bullet.bar.target.fill)
},
size: {
measure: bullet.bar.measures.size,
range: bullet.bar.ranges.size,
target: bullet.bar.target.size
}
}
}
protected configXAxis(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configXAxis(chart, options)
if (!tmpOptions.xAxis || !tmpOptions.xAxis.label) return tmpOptions
const { layout, xAxis } = tmpOptions
const position = xAxis.position
const style: any = { ...xAxis.label.style }
if (layout === 'vertical') {
style.textAlign = 'center'
style.textBaseline = position === 'bottom' ? 'top' : 'bottom'
} else {
style.textAlign = position === 'bottom' ? 'end' : 'start'
style.textBaseline = 'middle'
}
xAxis.label.style = style
return tmpOptions
}
protected configYAxis(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configYAxis(chart, options)
if (!tmpOptions.yAxis || !tmpOptions.yAxis.label) return tmpOptions
const yAxis = parseJson(chart.customStyle).yAxis
tmpOptions.yAxis.label.formatter = value => valueFormatter(value, yAxis.axisLabelFormatter)
const { layout, yAxis: yAxisConfig } = tmpOptions
const position = yAxisConfig.position
const style: any = { ...yAxisConfig.label.style }
if (layout === 'vertical') {
style.textAlign = position === 'left' ? 'end' : 'start'
style.textBaseline = 'middle'
} else {
style.textAlign = 'center'
style.textBaseline = position === 'left' ? 'top' : 'bottom'
}
yAxisConfig.label.style = style
return tmpOptions
}
protected configLabel(chart: Chart, options: BulletOptions): BulletOptions {
const tmpOptions = super.configLabel(chart, options)
if (!tmpOptions.label) return tmpOptions
const labelAttr = parseJson(chart.customAttr).label
const label: any = {
...tmpOptions.label,
formatter: param =>
param.mKey === 'measures'
? valueFormatter(param.measures, labelAttr.labelFormatter)
: undefined
}
return { ...tmpOptions, label: { measure: label } }
}
protected configLegend(chart: Chart, options: BulletOptions): BulletOptions {
const baseLegend = super.configLegend(chart, options).legend
if (!baseLegend) return options
const { bullet } = parseJson(chart.customAttr).misc
const customStyleLegend = parseJson(chart.customStyle).legend
const items = []
const createLegendItem = (value, name, symbol, fill, size = 4) => ({
value,
name,
marker: { symbol, style: { fill, stroke: value === 'measure' ? '' : fill, r: size } }
})
if (customStyleLegend.showRange) {
if (bullet.bar.ranges.showType === 'dynamic') {
if (chart.extBubble.length) {
const rangeName = chart.extBubble[0]?.chartShowName || bullet.bar.ranges.name
items.push(
createLegendItem(
'dynamic',
rangeName || chart.extBubble[0]?.name,
bullet.bar.ranges.symbol,
[].concat(bullet.bar.ranges.fill)[0],
bullet.bar.ranges.symbolSize
)
)
}
} else {
bullet.bar.ranges.fixedRange?.forEach(item => {
items.push(
createLegendItem(
item.name,
item.name,
bullet.bar.ranges.symbol,
item.fill,
bullet.bar.ranges.symbolSize
)
)
})
}
}
const targetName = chart.yAxisExt[0]?.chartShowName || bullet.bar.target.name
items.push(
createLegendItem(
'target',
targetName || chart.yAxisExt[0]?.name,
'line',
[].concat(bullet.bar.target.fill)[0],
bullet.bar.ranges.symbolSize
)
)
const measureName = chart.yAxis[0]?.chartShowName || bullet.bar.measures.name
items.push(
createLegendItem(
'measure',
measureName || chart.yAxis[0]?.name,
'square',
[].concat(bullet.bar.measures.fill)[0],
bullet.bar.ranges.symbolSize
)
)
return {
...options,
legend: { custom: true, position: baseLegend.position, layout: baseLegend.layout, items }
}
}
protected configTooltip(chart: Chart, options: BulletOptions): BulletOptions {
const customAttr: DeepPartial<ChartAttr> = parseJson(chart.customAttr)
const tooltipAttr = customAttr.tooltip
const { bullet } = parseJson(chart.customAttr).misc
if (!tooltipAttr.show) return { ...options, tooltip: false }
const formatterMap = tooltipAttr.seriesTooltipFormatter
?.filter(i => i.show)
.reduce((pre, next, index) => {
const keys = ['measures', 'target', 'ranges']
if (keys[index]) pre[keys[index]] = next
return pre
}, {}) as Record<string, SeriesFormatter>
const tooltip = {
shared: true,
showMarkers: true,
customItems(originalItems) {
if (!tooltipAttr.seriesTooltipFormatter?.length) return originalItems
const result = []
const data = options.data.find(item => item.title === originalItems[0].title)
Object.keys(formatterMap).forEach(key => {
if (key === '记录数*') return
const formatter = formatterMap[key]
if (formatter) {
if (key !== 'ranges') {
let value = 0
if (chart.yAxis[0].id === chart.yAxisExt[0].id) {
value = valueFormatter(parseFloat(data['target'] as string), formatter.formatterCfg)
} else {
value = valueFormatter(parseFloat(data[key] as string), formatter.formatterCfg)
}
const name = isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
result.push({ ...originalItems[0], color: bullet.bar[key].fill, name, value })
} else {
const ranges = data.ranges
const isDynamic = bullet.bar.ranges.showType === 'dynamic'
ranges.forEach((range, index) => {
const value = valueFormatter(
parseFloat(isDynamic ? data.minRanges[0] : (range as string)),
formatter.formatterCfg
)
let name = ''
let color: string | string[]
if (bullet.bar.ranges.showType === 'dynamic') {
name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName
color = bullet.bar[key].fill
} else {
const customRange = bullet.bar.ranges.fixedRange[index].name
name = customRange
? customRange
: isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
color = bullet.bar[key].fixedRange[index].fill
}
result.push({ ...originalItems[0], color, name, value })
})
}
}
})
const dynamicTooltipValue = chart.data.data.find(
d => d.field === originalItems[0]['title']
)?.dynamicTooltipValue
if (dynamicTooltipValue.length > 0) {
dynamicTooltipValue.forEach(dy => {
const q = tooltipAttr.seriesTooltipFormatter.filter(i => i.id === dy.fieldId)
if (q && q.length > 0) {
const value = valueFormatter(parseFloat(dy.value as string), q[0].formatterCfg)
const name = isEmpty(q[0].chartShowName) ? q[0].name : q[0].chartShowName
result.push({ color: 'grey', name, value })
}
})
}
return result
},
container: getTooltipContainer(`tooltip-${chart.id}`),
itemTpl: TOOLTIP_TPL,
enterable: true
}
return { ...options, tooltip }
}
setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customAttr.label.position = 'middle'
chart.customStyle.yAxis.splitLine.show = false
return super.setupDefaultOptions(chart)
}
protected setupOptions(chart: Chart, options: BulletOptions): BulletOptions {
return flow(
this.configTheme,
this.configBasicStyle,
this.configMisc,
this.configXAxis,
this.configYAxis,
this.configLabel,
this.configLegend,
this.configTooltip
)(chart, options, {}, this)
}
}
/**
*
* @param chart
*/
function mergeBulletData(chart): any[] {
// 先根据维度分组,再根据指标字段组装成子弹图的格式
const groupedData = chart.data.data.reduce((acc, item) => {
const field = item.field
if (!acc[field]) {
acc[field] = []
}
acc[field].push(item)
return acc
}, {})
const result = []
// 组装子弹图数据,每个维度对应一个子弹图
Object.keys(groupedData).forEach(field => {
const items = groupedData[field]
// 初始化子弹图条目结构
const entry = {
title: field,
ranges: [],
measures: [],
target: [],
dimensionList: items[0].dimensionList,
quotaList: []
}
// 防止指标相同时无数据有可能会导致数据不一致
items.forEach(item => {
const quotaId = item.quotaList[0]?.id
const v = item.value || 0
if (quotaId === chart.yAxis[0]?.id) {
entry.measures.push(v)
}
if (quotaId === chart.yAxisExt[0]?.id) {
entry.target.push(v)
}
if (quotaId === chart.extBubble[0]?.id) {
entry.ranges.push(v)
}
entry.quotaList.push(item.quotaList[0])
})
// 对数据进行累加
const ranges = chart.extBubble[0]?.id
? [].concat(entry.ranges?.reduce((acc, curr) => acc + curr, 0))
: []
const target = [].concat(entry.target?.reduce((acc, curr) => acc + curr, 0))
const measures = [].concat(entry.measures?.reduce((acc, curr) => acc + curr, 0))
const bulletData = {
...entry,
measures: measures,
target: target,
ranges: ranges,
quotaList: [...entry.quotaList],
minRanges: ranges,
originalRanges: ranges,
originalTarget: target
}
result.push(bulletData)
})
return result
}

View File

@ -6,6 +6,7 @@ import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
setGradientColor,
@ -101,6 +102,17 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
const newChart = new Bar(container, options)
newChart.on('interval:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart)
return newChart
@ -135,7 +147,10 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
tickCount: axisValue.splitCount
}
}
return { ...tmpOptions, ...axis }
// 根据axis的最小值过滤options中的data数据过滤掉小于最小值的数据
const { data } = options
const newData = data.filter(item => item.value >= axisValue.min)
return { ...tmpOptions, data: newData, ...axis }
}
return tmpOptions
}
@ -157,19 +172,9 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
options = {
...options,
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
@ -234,6 +239,7 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
@ -316,8 +322,24 @@ export class HorizontalStackBar extends HorizontalBar {
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum) {
return valueFormatter(param.value, labelAttr.labelFormatter)
formatter: function (data: Datum) {
const value = valueFormatter(data.value, labelAttr.labelFormatter)
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {
@ -435,11 +457,29 @@ export class HorizontalPercentageStackBar extends HorizontalStackBar {
const l = parseJson(customAttr).label
const label = {
...baseOptions.label,
formatter: function (param: Datum) {
if (!param.value) {
return '0%'
formatter: function (data: Datum) {
let value = data.value
if (value) {
value = (Math.round(value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%'
} else {
value = '0%'
}
return (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%'
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: l.fontSize,
fontFamily: chart.fontFamily,
fill: l.color
}
})
return group
}
}
return {

View File

@ -3,8 +3,8 @@ import { flow, hexColorToRGBA, parseJson } from '../../../util'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getTooltipContainer,
getTooltipItemConditionColor,
setGradientColor,
TOOLTIP_TPL
} from '../../common/common_antv'
@ -66,7 +66,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
'fontSize',
'axisForm',
'axisLabel',
'position',
// 'position',
'showLengthLimit'
],
'function-cfg': ['emptyDataStrategy'],
@ -166,6 +166,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
}
})
if (basicStyle.gradient) {
// eslint-disable-next-line
color1 = color1.map((ele, _index) => {
return setGradientColor(ele, true, 0)
})
@ -184,19 +185,9 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
options = {
...options,
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
@ -297,12 +288,31 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
if (!baseOption.yAxis) {
return baseOption
}
if (baseOption.yAxis.position === 'left') {
baseOption.yAxis.position = 'bottom'
const yAxis = parseJson(chart.customStyle).yAxis
if (yAxis.axisLabel.show) {
const rotate = yAxis.axisLabel.rotate
let textAlign = 'end'
let textBaseline = 'middle'
if (Math.abs(rotate) > 75) {
textAlign = 'center'
}
if (rotate > 75) {
textBaseline = 'top'
}
if (rotate < -75) {
textBaseline = 'bottom'
}
baseOption.yAxis.label.style.textBaseline = textBaseline
baseOption.yAxis.label.style.textAlign = textAlign
}
/*if (baseOption.yAxis.position === 'left') {
baseOption.yAxis.position = 'bottom'
}
if (baseOption.yAxis.position === 'right') {
baseOption.yAxis.position = 'top'
}
}*/
return baseOption
}
setupDefaultOptions(chart: ChartObj): ChartObj {

View File

@ -6,6 +6,7 @@ import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRoundAngle,
getPadding,
getTooltipContainer,
setGradientColor,
@ -22,6 +23,7 @@ import {
import { Datum } from '@antv/g2plot/esm/types/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { Group } from '@antv/g-canvas'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -170,6 +172,17 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
const newChart = new BarClass(container, options)
newChart.on('interval:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
configAxisLabelLengthLimit(chart, newChart)
return newChart
@ -309,19 +322,10 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
options = {
...options,
...configRoundAngle(chart, 'barStyle')
}
let barWidthRatio
const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio
@ -391,7 +395,22 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
valueFormatter(param.values[1], labelAttr.labelFormatter)
}
}
return res
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data: param,
text: res,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {

View File

@ -72,7 +72,8 @@ export class Waterfall extends G2PlotChartView<WaterfallOptions, G2Waterfall> {
'axisForm',
'axisLabel',
'axisLabelFormatter',
'showLengthLimit'
'showLengthLimit',
'axisLine'
],
threshold: ['lineThreshold']
}

View File

@ -46,7 +46,8 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': [
...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axis: AxisType[] = [...LINE_AXIS_TYPE]
@ -103,8 +104,8 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
async drawChart(drawOptions: G2PlotDrawOptions<G2Area>): Promise<G2Area> {
const { chart, container, action } = drawOptions
chart.container = container
if (!chart.data?.data?.length) {
chart.container = container
clearExtremum(chart)
return
}
@ -147,7 +148,7 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
fields: [],
...tmpOptions.label,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -305,7 +306,7 @@ export class StackArea extends Area {
propertyInner = {
...this['propertyInner'],
'label-selector': ['vPosition', 'fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show']
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show', 'carousel']
}
axisConfig = {
...this['axisConfig'],

View File

@ -10,10 +10,12 @@ import {
TOOLTIP_TPL
} from '../../common/common_antv'
import {
convertToAlphaColor,
flow,
getLineConditions,
getLineLabelColorByCondition,
hexColorToRGBA,
isAlphaColor,
parseJson,
setUpGroupSeriesColor
} from '@/data-visualization/chart/components/js/util'
@ -43,8 +45,10 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': [
...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
]
'seriesTooltipFormatter',
'carousel'
],
'legend-selector': [...LINE_EDITOR_PROPERTY_INNER['legend-selector'], 'legendSort']
}
axis: AxisType[] = [...LINE_AXIS_TYPE, 'xAxisExt']
axisConfig = {
@ -66,8 +70,8 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
}
async drawChart(drawOptions: G2PlotDrawOptions<G2Line>): Promise<G2Line> {
const { chart, action, container } = drawOptions
chart.container = container
if (!chart.data?.data?.length) {
chart.container = container
clearExtremum(chart)
return
}
@ -146,7 +150,7 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
fields: [],
...tmpOptions.label,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (data: Datum, _point) => {
formatter: (data: Datum) => {
if (data.EXTREME) {
return ''
}
@ -321,17 +325,30 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
if (sort?.length) {
// 用值域限定排序,有可能出现新数据但是未出现在图表上,所以这边要遍历一下子维度,加到后面,让新数据显示出来
const data = optionTmp.data
data?.forEach(d => {
const cat = d['category']
if (cat && !sort.includes(cat)) {
sort.push(cat)
const cats =
data?.reduce((p, n) => {
const cat = n['category']
if (cat && !p.includes(cat)) {
p.push(cat)
}
return p
}, []) || []
const values = sort.reduce((p, n) => {
if (cats.includes(n)) {
const index = cats.indexOf(n)
if (index !== -1) {
cats.splice(index, 1)
}
p.push(n)
}
})
return p
}, [])
cats.length > 0 && values.push(...cats)
optionTmp.meta = {
...optionTmp.meta,
category: {
type: 'cat',
values: sort
values
}
}
}
@ -351,6 +368,56 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
fill: style.stroke
}
}
const { sort, customSort, icon } = customStyle.legend
if (sort && sort !== 'none' && chart.xAxisExt.length) {
const customAttr = parseJson(chart.customAttr)
const { basicStyle } = customAttr
const seriesMap =
basicStyle.seriesColor?.reduce((p, n) => {
p[n.id] = n
return p
}, {}) || {}
const dupCheck = new Set()
const items = optionTmp.data?.reduce((arr, item) => {
if (!dupCheck.has(item.category)) {
const fill =
seriesMap[item.category]?.color ??
optionTmp.color[dupCheck.size % optionTmp.color.length]
dupCheck.add(item.category)
arr.push({
name: item.category,
value: item.category,
marker: {
symbol: icon,
style: {
r: size,
fill: isAlphaColor(fill) ? fill : convertToAlphaColor(fill, basicStyle.alpha)
}
}
})
}
return arr
}, [])
if (sort !== 'custom') {
items.sort((a, b) => {
return sort !== 'desc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name)
})
} else {
const tmp = []
;(customSort || []).forEach(item => {
const index = items.findIndex(i => i.name === item)
if (index !== -1) {
tmp.push(items[index])
items.splice(index, 1)
}
})
items.unshift(...tmp)
}
optionTmp.legend.items = items
if (xAxisExt?.customSort?.length > 0) {
delete optionTmp.meta?.category.values
}
}
return optionTmp
}
protected setupOptions(chart: Chart, options: LineOptions): LineOptions {

View File

@ -68,22 +68,22 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const xAxis = chart.xAxis
const yAxis = chart.yAxis
// 时间字段
const xAxisDataeaseName = xAxis[0].dataeaseName
const xAxisgisbiName = xAxis[0].gisbiName
// 收盘价字段
const yAxisDataeaseName = yAxis[1].dataeaseName
const yAxisgisbiName = yAxis[1].gisbiName
const result = []
for (let i = 0; i < data.length; i++) {
if (i < dayCount) {
result.push({
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
[xAxisgisbiName]: data[i][xAxisgisbiName],
value: null
})
} else {
const sum = data
.slice(i - dayCount + 1, i + 1)
.reduce((sum, item) => sum + item[yAxisDataeaseName], 0)
.reduce((sum, item) => sum + item[yAxisgisbiName], 0)
result.push({
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
[xAxisgisbiName]: data[i][xAxisgisbiName],
value: parseFloat((sum / dayCount).toFixed(3))
})
}
@ -228,7 +228,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const data = parseJson(chart.data?.tableRow)
// 时间字段
const xAxisDataeaseName = xAxis[0].dataeaseName
const xAxisgisbiName = xAxis[0].gisbiName
const averages = [5, 10, 20, 60, 120, 180]
const legendItems: any[] = [
{
@ -262,9 +262,9 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
// 将均线数据设置到主数据中
data.forEach((item: any) => {
const date = item[xAxisDataeaseName]
const date = item[xAxisgisbiName]
for (const [key, value] of averagesLineData) {
item[key] = value.find(m => m[xAxisDataeaseName] === date)?.value
item[key] = value.find(m => m[xAxisgisbiName] === date)?.value
}
})
@ -283,7 +283,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
top: true,
options: {
smooth: false,
xField: xAxisDataeaseName,
xField: xAxisgisbiName,
yField: key,
color: colors[index - 1],
xAxis: null,
@ -349,7 +349,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
options: {
meta: {
[xAxisDataeaseName]: {
[xAxisgisbiName]: {
mask: dateFormat
}
},
@ -363,12 +363,12 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
min: minValue,
max: maxValue
},
xField: xAxisDataeaseName,
xField: xAxisgisbiName,
yField: [
yAxis[0].dataeaseName,
yAxis[1].dataeaseName,
yAxis[2].dataeaseName,
yAxis[3].dataeaseName
yAxis[0].gisbiName,
yAxis[1].gisbiName,
yAxis[2].gisbiName,
yAxis[3].gisbiName
],
legend: {
position: 'top',
@ -384,7 +384,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const plot = new MixClass(container, option)
this.registerEvent(data, plot, averagesLineData)
plot.on('schema:click', evt => {
const selectSchema = evt.data.data[xAxisDataeaseName]
const selectSchema = evt.data.data[xAxisgisbiName]
const paramData = parseJson(chart.data?.data)
const selectData = paramData.filter(item => item.field === selectSchema)
const quotaList = []
@ -440,7 +440,6 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
protected configTooltip(chart: Chart, options: MixOptions): MixOptions {
const tooltipAttr = parseJson(chart.customAttr).tooltip
const xAxis = chart.xAxis
const newPlots = []
const linePlotList = options.plots.filter(item => item.type === 'line')
linePlotList.forEach(item => {
@ -464,7 +463,7 @@ export class StockLine extends G2PlotChartView<MixOptions, Mix> {
const showFiled = chart.data.fields
const customTooltipItems = originalItems => {
const formattedItems = originalItems.map(item => {
const fieldObj = showFiled.find(q => q.dataeaseName === item.name)
const fieldObj = showFiled.find(q => q.gisbiName === item.name)
const displayName = fieldObj?.chartShowName || fieldObj?.name || item.name
const formattedName = displayName.startsWith('ma') ? displayName.toUpperCase() : displayName
tooltipAttr.tooltipFormatter.decimalCount = 3

View File

@ -74,7 +74,7 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
})
// 处理空数据, 只要有一个指标是空数据,就不显示图表
const hasNoneData = chart.data?.series.some(s => !s.data?.[0])
this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container)
this.configEmptyDataStyle(hasNoneData ? [] : [1], container, newChart)
if (hasNoneData) {
return
}

View File

@ -402,7 +402,8 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(name)
}
if (label.showQuota) {
areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
;(areaMap[name] || areaMap[name] === 0) &&
content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
}
item.properties['_DE_LABEL_'] = content.join('\n\n')
}

View File

@ -1,3 +1,6 @@
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t } = useI18n()
export const MAP_EDITOR_PROPERTY: EditorProperty[] = [
'background-overall-component',
'border-style',
@ -51,6 +54,29 @@ export const MAP_AXIS_TYPE: AxisType[] = [
'extTooltip'
]
export const gaodeMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('chart.map_style_darkblue'), value: 'darkblue' },
{ name: t('chart.map_style_light'), value: 'light' },
{ name: t('chart.map_style_dark'), value: 'dark' },
{ name: t('chart.map_style_fresh'), value: 'fresh' },
{ name: t('chart.map_style_grey'), value: 'grey' },
{ name: t('chart.map_style_blue'), value: 'blue' },
{ name: t('chart.map_style_translate'), value: 'Satellite' },
{ name: t('commons.custom'), value: 'custom' }
]
export const tdtMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('chart.map_style_dark'), value: 'black' },
{ name: t('chart.map_style_darkblue'), value: 'indigo' }
]
export const qqMapStyleOptions = [
{ name: t('chart.map_style_normal'), value: 'normal' },
{ name: t('commons.custom'), value: 'custom' }
]
export declare type MapMouseEvent = MouseEvent & {
feature: GeoJSON.Feature
}

View File

@ -157,6 +157,11 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
})
})
data = filterChartDataByRange(sourceData, maxValue, minValue)
if (chart.drill) {
getMaxAndMinValueByData(sourceData, 'value', 0, 0, (max, min) => {
data = filterChartDataByRange(sourceData, max, min)
})
}
} else {
data = sourceData
}
@ -301,7 +306,8 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(name)
}
if (label.showQuota) {
areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
;(areaMap[name] || areaMap[name] === 0) &&
content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter))
}
item.properties['_DE_LABEL_'] = content.join('\n\n')
}
@ -346,11 +352,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
return listDom
}
private customConfigLegend(
chart: Chart,
options: ChoroplethOptions,
context: Record<string, any>
): ChoroplethOptions {
private customConfigLegend(chart: Chart, options: ChoroplethOptions): ChoroplethOptions {
const { basicStyle, misc } = parseJson(chart.customAttr)
const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha))
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
@ -420,14 +422,14 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
const isLessThanMin = range[0] < ranges[0][0] && range[1] < ranges[0][0]
let rangeColor = colors[colorIndex]
if (isLessThanMin) {
rangeColor = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
rangeColor = basicStyle.areaBaseColor
}
items.push({
value: tmpRange,
color: rangeColor
})
})
customLegend['customContent'] = (_: string, _items: CategoryLegendListItem[]) => {
customLegend['customContent'] = () => {
if (items?.length) {
return this.createLegendCustomContent(items)
}
@ -435,13 +437,16 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
}
options.color['value'] = ({ value }) => {
const item = items.find(item => value >= item.value[0] && value <= item.value[1])
return item ? item.color : hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
return item ? item.color : basicStyle.areaBaseColor
}
options.color.scale.domain = [ranges[0][0], ranges[ranges.length - 1][1]]
} else {
customLegend['customContent'] = (_: string, items: CategoryLegendListItem[]) => {
const showItems = items?.length > 30 ? items.slice(0, 30) : items
if (showItems?.length) {
if (showItems.length === 1) {
showItems[0].value = options.color.scale.domain.slice(0, 2)
}
return this.createLegendCustomContent(showItems)
}
return ''
@ -508,7 +513,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
content.push(area.name)
}
if (label.showQuota) {
areaMap[area.name] &&
;(areaMap[area.name] || areaMap[area.name] === 0) &&
content.push(valueFormatter(areaMap[area.name].value, label.quotaLabelFormatter))
}
labelLocation.push({
@ -567,6 +572,9 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
return result
}
const head = originalItem.properties
if (!head) {
return result
}
const { adcode } = head
const areaName = subAreaMap['156' + adcode]
const valItem = areaMap[areaName]

View File

@ -13,13 +13,17 @@ import {
svgStrToUrl
} from '@/data-visualization/chart/components/js/util'
import { deepCopy } from '@/data-visualization/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
import { PointLayer } from '@antv/l7-layers'
import { LayerPopup, Popup } from '@antv/l7'
import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import {
getMapCenter,
getMapScene,
getMapStyle,
mapRendered,
qqMapRendered
} from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { configCarouselTooltip } from '@/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel'
import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { filter } from 'lodash-es'
const { t } = useI18n()
@ -102,18 +106,10 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
miscStyle = parseJson(chart.customAttr).misc
}
let mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
const mapKey = await this.getMapKey()
let center: [number, number] = [
DEFAULT_BASIC_STYLE.mapCenter.longitude,
DEFAULT_BASIC_STYLE.mapCenter.latitude
]
if (basicStyle.autoFit === false) {
center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
}
const mapStyle = getMapStyle(mapKey, basicStyle)
let center = getMapCenter(basicStyle)
// 联动时,聚焦到数据点,多个取第一个
if (
chart.chartExtRequest?.linkageFilters?.length &&
@ -121,45 +117,25 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
chart.data?.tableRow.length
) {
// 经度
const lng = chart.data?.tableRow?.[0][chart.xAxis[0].dataeaseName]
const lng = chart.data?.tableRow?.[0][chart.xAxis[0].gisbiName]
// 纬度
const lat = chart.data?.tableRow?.[0][chart.xAxis[1].dataeaseName]
const lat = chart.data?.tableRow?.[0][chart.xAxis[1].gisbiName]
center = [lng, lat]
}
const chartObj = drawOption.chartObj as unknown as L7Wrapper<L7Config, Scene>
let scene = chartObj?.getScene()
if (!scene) {
scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
})
} else {
if (scene.getLayers()?.length) {
await scene.removeAllLayer()
scene.setPitch(miscStyle.mapPitch)
scene.setMapStyle(mapStyle)
scene.map.showLabel = !(basicStyle.showLabel === false)
}
if (basicStyle.autoFit === false) {
scene.setZoomAndCenter(basicStyle.zoomLevel, center)
}
}
mapRendering(container)
scene.once('loaded', () => {
mapRendered(container)
})
this.configZoomButton(chart, scene)
scene = await getMapScene(
chart,
scene,
container,
mapKey,
basicStyle,
miscStyle,
mapStyle,
center
)
this.configZoomButton(chart, scene, mapKey)
if (xAxis?.length < 2) {
return new L7Wrapper(scene, undefined)
}
@ -171,9 +147,13 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
scene.addPopup(tooltipLayer)
}
this.buildLabel(chart, configList)
symbolicLayer.once('inited', () => {
mapRendered(container)
})
symbolicLayer.on('inited', () => {
chart.container = container
configCarouselTooltip(chart, symbolicLayer, symbolicLayer.sourceOption.data, scene)
qqMapRendered(scene)
})
symbolicLayer.on('click', ev => {
const data = ev.feature
@ -181,17 +161,17 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
const quotaList = []
chart.data.fields.forEach((item, index) => {
Object.keys(data).forEach(key => {
if (key.startsWith('f_') && item.dataeaseName === key) {
if (key.startsWith('f_') && item.gisbiName === key) {
if (index === 0) {
dimensionList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: data[key]
})
} else {
quotaList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: data[key]
})
}
@ -239,7 +219,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let colorIndex = 0
// 存储已分配的颜色
const colorAssignments = new Map()
const sizeKey = extBubble.length > 0 ? extBubble[0].dataeaseName : ''
const sizeKey = extBubble.length > 0 ? extBubble[0].gisbiName : ''
//条件颜色
const { threshold } = parseJson(chart.senior)
@ -257,7 +237,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
? chart.data.tableRow.map((item, index) => {
item['_index'] = '_index' + index
// 颜色标识
const identifier = item[xAxisExt[0]?.dataeaseName]
const identifier = item[xAxisExt[0]?.gisbiName]
// 检查该标识是否已有颜色分配,如果没有则分配
let color = colorAssignments.get(identifier)
if (!color) {
@ -271,7 +251,7 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
if (conditions.length > 0) {
for (let i = 0; i < conditions.length; i++) {
const c = conditions[i]
const value = item[c.field.dataeaseName]
const value = item[c.field.gisbiName]
for (const t of c.conditions) {
const v = t.value
@ -332,12 +312,12 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
.source(data, {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.active(true)
if (xAxisExt[0]?.dataeaseName) {
if (xAxisExt[0]?.gisbiName) {
if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) {
// 图片无法改色
if (basicStyle.customIcon.startsWith('data')) {
@ -452,8 +432,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let showFields = tooltip.showFields || []
if (!tooltip.showFields || tooltip.showFields.length === 0) {
showFields = [
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
// 修改背景色
@ -584,8 +564,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
let showFields = label.showFields || []
if (!label.showFields || label.showFields.length === 0) {
showFields = [
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
data.forEach(item => {
@ -613,8 +593,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
.source(data, {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName
x: xAxis[0].gisbiName,
y: xAxis[1].gisbiName
}
})
.shape('textLayerContent', 'text')

View File

@ -469,8 +469,8 @@ export class CarouselManager {
})
}
if (this.chart.type === 'symbolic-map') {
const lngField = this.chart.xAxis[0].dataeaseName
const latField = this.chart.xAxis[1].dataeaseName
const lngField = this.chart.xAxis[0].gisbiName
const latField = this.chart.xAxis[1].gisbiName
const { _id } = this.scene
?.getLayers()
?.find(i => i.type === 'PointLayer')
@ -554,8 +554,8 @@ export class CarouselManager {
let showFields = tooltip.showFields || []
if (!tooltip.showFields || tooltip.showFields.length === 0) {
showFields = [
...this.chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
...this.chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
...this.chart.xAxisExt.map(i => `${i.gisbiName}@${i.name}`),
...this.chart.xAxis.map(i => `${i.gisbiName}@${i.name}`)
]
}
const style = document.createElement('style')
@ -571,8 +571,8 @@ export class CarouselManager {
}
`
document.head.appendChild(style)
const lngField = this.chart.xAxis[0].dataeaseName
const latField = this.chart.xAxis[1].dataeaseName
const lngField = this.chart.xAxis[0].gisbiName
const latField = this.chart.xAxis[1].gisbiName
const htmlPrefix = `<div style='font-size:${tooltip.fontSize}px;color:${tooltip.color};'>`
const htmlSuffix = '</div>'
const data = this.view.sourceOption.data[index]

View File

@ -1,9 +1,11 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from
'@/data-visualization/chart/components/js/panel/types/impl/g2plot'
import type { DualAxes, DualAxesOptions } from '@antv/g2plot/esm/plots/dual-axes'
import {
configRoundAngle,
configPlotTooltipEvent,
getAnalyse,
getLabel,
@ -25,7 +27,9 @@ import {
defaultsDeep,
defaults
} from 'lodash-es'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import { valueFormatter } from
'@/data-visualization/chart/components/js/formatter'
import {
CHART_MIX_AXIS_TYPE,
CHART_MIX_DEFAULT_BASIC_STYLE,
@ -42,6 +46,7 @@ import {
} from '@/data-visualization/chart/components/editor/util/chart'
import type { Options } from '@antv/g2plot/esm'
import { Group } from '@antv/g-canvas'
import { extremumEvt } from '@/data-visualization/chart/components/js/extremumUitl'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -56,7 +61,8 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axis: AxisType[] = [...CHART_MIX_AXIS_TYPE, 'xAxisExtRight', 'yAxisExt']
@ -94,6 +100,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
async drawChart(drawOptions: G2PlotDrawOptions<DualAxes>): Promise<DualAxes> {
const { chart, action, container } = drawOptions
chart.container = container
if (!chart.data?.left?.data?.length && !chart.data?.right?.data?.length) {
return
}
@ -117,7 +124,6 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
valueExt: d.value
}
})
// options
const initOptions: DualAxesOptions = {
data: [data1, data2],
@ -127,6 +133,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
geometryOptions: [
{
geometry: data1Type,
marginRatio: 0,
color: [],
isGroup: isGroup,
isStack: isStack,
@ -174,6 +181,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
newChart.on('point:click', action)
newChart.on('interval:click', action)
extremumEvt(newChart, chart, options, container)
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -292,18 +300,9 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
tempOption.geometryOptions[1].smooth = smooth
tempOption.geometryOptions[1].point = point
tempOption.geometryOptions[1].lineStyle = lineStyle
if (s.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius
]
}
tempOption.geometryOptions[0].columnStyle = columnStyle
tempOption.geometryOptions[1].columnStyle = columnStyle
tempOption.geometryOptions[0] = {
...tempOption.geometryOptions[0],
...configRoundAngle(chart, 'columnStyle')
}
}
@ -328,7 +327,7 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
}
setupDefaultOptions(chart: ChartObj): ChartObj {
const { customAttr, senior } = chart
const { senior } = chart
if (
senior.functionCfg.emptyDataStrategy == undefined ||
senior.functionCfg.emptyDataStrategy === 'ignoreData'
@ -670,7 +669,8 @@ export class GroupColumnLineMix extends ColumnLineMix {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {
@ -782,7 +782,8 @@ export class StackColumnLineMix extends ColumnLineMix {
'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {
@ -895,7 +896,8 @@ export class DualLineMix extends ColumnLineMix {
'label-selector': ['seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
'seriesTooltipFormatter',
'carousel'
]
}
axisConfig = {

View File

@ -69,7 +69,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
if (chart?.data?.data?.length) {
// data
const data = chart.data.data
const { xAxis, yAxis, drillFields } = chart
const { yAxis } = chart
const ySort = yAxis[0]?.sort ?? 'none'
const sort = {
sort: (a, b) =>
@ -123,7 +123,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
'@antv/g2plot/esm/plots/circle-packing'
)
const newChart = new G2CirclePacking(container, options)
newChart.on('point:click', param => {
newChart.on('element:click', param => {
const pointData = param?.data?.data
if (pointData?.name === t('commons.all')) {
return
@ -157,7 +157,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
...options,
hierarchyConfig: {
...options.hierarchyConfig,
padding: typeof padding === 'number' && !isNaN(padding) ? padding / 100 : 0
padding: padding / 100 ?? 0
},
pointStyle
}
@ -177,7 +177,7 @@ export class CirclePacking extends G2PlotChartView<CirclePackingOptions, G2Circl
textAlign: 'center',
offsetY: 5,
layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout,
formatter: (d: Datum, _point) => {
formatter: (d: Datum) => {
return d.children.length === 0 ? d.name : ''
}
}

View File

@ -74,7 +74,7 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
// options
const initOptions: GaugeOptions = {
percent: 0,
appendPadding: getPadding(chart),
appendPadding: [0, 10, 15, 10],
axis: {
tickInterval: 0.2,
label: {
@ -109,8 +109,10 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
}
})
})
const hasNoneData = chart.data?.series.some(s => !s.data?.[0])
this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container)
const hasNoneData = chart.data?.series.some(
s => s.data?.[0] === undefined || s.data?.[0] === null
)
this.configEmptyDataStyle(hasNoneData ? [] : [1], container, newChart)
if (hasNoneData) {
return
}

View File

@ -15,7 +15,8 @@ export class IndicatorChartView extends AbstractChartView {
'indicator-value-selector',
'indicator-name-selector',
'threshold',
'function-cfg'
'function-cfg',
'linkage'
]
propertyInner: EditorPropertyInner = {
'background-overall-component': ['all'],

View File

@ -13,7 +13,8 @@ import {
configPlotTooltipEvent,
configYaxisTitleLengthLimit,
getTooltipContainer,
TOOLTIP_TPL
TOOLTIP_TPL,
getPadding
} from '../../common/common_antv'
import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
@ -209,7 +210,7 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
data: data,
xField: 'yAxis',
yField: 'yAxisExt',
appendPadding: 30,
appendPadding: getPadding(chart),
pointStyle: {
fillOpacity: 0.8,
stroke: '#bbb'
@ -476,7 +477,6 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
this.configLegend,
this.configXAxis,
this.configYAxis,
this.configAnalyse,
this.configSlider,
this.configBasicStyle
)(chart, options, {}, this)

View File

@ -1,13 +1,14 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from
'@/data-visualization/chart/components/js/panel/types/impl/g2plot'
import type { Sankey, SankeyOptions } from '@antv/g2plot/esm/plots/sankey'
import { getPadding, setGradientColor } from '@/data-visualization/chart/components/js/panel/common/common_antv'
import { cloneDeep, get } from 'lodash-es'
import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util'
import { valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import { valueFormatter } from
'@/data-visualization/chart/components/js/formatter'
import { Datum } from '@antv/g2plot/esm/types/common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import {

View File

@ -14,6 +14,8 @@ import {
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { defaults, isEmpty } from 'lodash-es'
import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart'
import { type Datum } from '@antv/g2plot/esm'
import { Group } from '@antv/g-canvas'
const { t } = useI18n()
/**
@ -144,6 +146,17 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
const { Scatter: G2Scatter } = await import('@antv/g2plot/esm/plots/scatter')
const newChart = new G2Scatter(container, options)
newChart.on('point:click', action)
if (options.label) {
newChart.on('label:click', e => {
action({
x: e.x,
y: e.y,
data: {
data: e.target.attrs.data
}
})
})
}
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -277,6 +290,41 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
return optionTmp
}
protected configLabel(chart: Chart, options: ScatterOptions): ScatterOptions {
const tmpOption = super.configLabel(chart, options)
if (!tmpOption.label) {
return options
}
const { label: labelAttr } = parseJson(chart.customAttr)
tmpOption.label.style.fill = labelAttr.color
const label = {
...tmpOption.label,
formatter: function (data: Datum) {
const value = valueFormatter(data.value, labelAttr.labelFormatter)
const group = new Group({})
group.addShape({
type: 'text',
attrs: {
x: 0,
y: 0,
data,
text: value,
textAlign: 'start',
textBaseline: 'top',
fontSize: labelAttr.fontSize,
fontFamily: chart.fontFamily,
fill: labelAttr.color
}
})
return group
}
}
return {
...tmpOption,
label
}
}
protected setupOptions(chart: Chart, options: ScatterOptions) {
return flow(
this.configTheme,
@ -286,7 +334,6 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
this.configLegend,
this.configXAxis,
this.configYAxis,
this.configAnalyse,
this.configSlider,
this.configBasicStyle
)(chart, options)

View File

@ -1,7 +1,8 @@
import {
G2PlotChartView,
G2PlotDrawOptions
} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot'
} from
'@/data-visualization/chart/components/js/panel/types/impl/g2plot'
import type { WordCloud as G2WordCloud, WordCloudOptions } from '@antv/g2plot/esm/plots/word-cloud'
import {
filterChartDataByRange,

View File

@ -27,19 +27,23 @@ import type { Datum } from '@antv/g2plot/esm/types/common'
import { add } from 'mathjs'
import isEmpty from 'lodash-es/isEmpty'
import { cloneDeep } from 'lodash-es'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t } = useI18n()
const DEFAULT_DATA = []
export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
axis: AxisType[] = PIE_AXIS_TYPE
properties = PIE_EDITOR_PROPERTY
propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor']
'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
}
axisConfig = PIE_AXIS_CONFIG
async drawChart(drawOptions: G2PlotDrawOptions<G2Pie>): Promise<G2Pie> {
const { chart, container, action } = drawOptions
this.configEmptyDataStyle(chart.data?.data, container, null, t('chart.no_data_or_not_positive'))
chart.container = container
if (!chart.data?.data?.length) {
return
}
@ -115,12 +119,22 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
field: {
type: 'cat'
}
},
state: {
active: {
style: {
lineWidth: 2,
fillOpacity: 0.5
}
}
}
}
const options = this.setupOptions(chart, initOptions)
const { Pie: G2Pie } = await import('@antv/g2plot/esm/plots/pie')
const newChart = new G2Pie(container, options)
newChart.on('interval:click', action)
newChart.on('interval:click', d => {
d.data?.data?.field !== customAttr.basicStyle.topNLabel && action(d)
})
configPlotTooltipEvent(chart, newChart)
return newChart
}
@ -244,6 +258,7 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
},
container: getTooltipContainer(`tooltip-${chart.id}`),
itemTpl: TOOLTIP_TPL,
shared: true,
enterable: true
}
return {
@ -338,7 +353,8 @@ export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
export class PieDonut extends Pie {
propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor']
'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
}
protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions {
const tmp = super.configBasicStyle(chart, options)

View File

@ -40,6 +40,7 @@ export class Rose extends G2PlotChartView<RoseOptions, G2Rose> {
async drawChart(drawOptions: G2PlotDrawOptions<G2Rose>): Promise<G2Rose> {
const { chart, container, action } = drawOptions
this.configEmptyDataStyle(chart.data?.data, container, null, t('chart.no_data_or_not_positive'))
if (!chart?.data?.data?.length) {
return
}

View File

@ -6,6 +6,7 @@ export const TABLE_EDITOR_PROPERTY: EditorProperty[] = [
'table-cell-selector',
'title-selector',
'tooltip-selector',
'summary-selector',
'function-cfg',
'threshold',
'scroll-cfg',

View File

@ -119,11 +119,12 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
if (!xAxis?.length || !xAxisExt?.length || !extColor?.length) {
return
}
const xField = xAxis[0].dataeaseName
const xFieldExt = xAxisExt[0].dataeaseName
const extColorField = extColor[0].dataeaseName
const xField = xAxis[0].gisbiName
const xFieldExt = xAxisExt[0].gisbiName
const extColorField = extColor[0].gisbiName
// data
const data = cloneDeep(chart.data.tableRow)
const tmpData = cloneDeep(chart.data.tableRow)
const data = tmpData.filter(cell => cell[xField] && cell[xFieldExt] && cell[extColorField])
data.forEach(i => {
Object.keys(i).forEach(key => {
if (key === '*') {
@ -171,10 +172,10 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
const dimensionList = []
chart.data.fields.forEach(item => {
Object.keys(pointData).forEach(key => {
if (key.startsWith('f_') && item.dataeaseName === key) {
if (key.startsWith('f_') && item.gisbiName === key) {
dimensionList.push({
id: item.id,
dataeaseName: item.dataeaseName,
gisbiName: item.gisbiName,
value: pointData[key]
})
}
@ -197,7 +198,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
newChart.on('afterrender', ev => {
const l = JSON.parse(JSON.stringify(parseJson(chart.customStyle).legend))
if (l.show) {
const rail = ev.view.getController('legend').option[extColor[0].dataeaseName]?.['rail']
const rail = ev.view.getController('legend').option[extColor[0].gisbiName]?.['rail']
if (rail) {
rail.defaultLength = this.getDefaultLength(chart, l)
}
@ -207,6 +208,12 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
return newChart
}
protected configTheme(chart: Chart, options: HeatmapOptions): HeatmapOptions {
const tmp = super.configTheme(chart, options)
tmp.theme.innerLabels.offset = 0
return tmp
}
protected configBasicStyle(chart: Chart, options: HeatmapOptions): HeatmapOptions {
const basicStyle = parseJson(chart.customAttr).basicStyle
const color = basicStyle.colors?.map(ele => {
@ -235,7 +242,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
const items = []
const createItem = (fieldObj, items, originalItems) => {
const name = fieldObj?.chartShowName ? fieldObj?.chartShowName : fieldObj?.name
let value = originalItems[0].data[fieldObj.dataeaseName]
let value = originalItems[0].data[fieldObj.gisbiName]
if (!isNaN(Number(value))) {
value = valueFormatter(value, fieldObj?.formatterCfg)
}
@ -329,7 +336,7 @@ export class TableHeatmap extends G2PlotChartView<HeatmapOptions, Heatmap> {
position: 'middle',
layout,
formatter: data => {
const value = data[extColor[0]?.dataeaseName]
const value = data[extColor[0]?.gisbiName]
if (!isNaN(Number(value))) {
return valueFormatter(value, extColor[0]?.formatterCfg)
}

View File

@ -14,7 +14,7 @@ import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util'
import { S2ChartView, S2DrawOptions } from '../../types/impl/s2'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { isEqual, isNumber, merge } from 'lodash-es'
import { filter, isEqual, isNumber, merge } from 'lodash-es'
import {
copyContent,
CustomDataCell,
@ -22,37 +22,19 @@ import {
getRowIndex,
calculateHeaderHeight,
SortTooltip,
configSummaryRow,
summaryRowStyle,
configEmptyDataStyle,
getLeafNodes,
getColumns
getColumns,
drawImage,
getSummaryRow,
SummaryCell
} from '@/data-visualization/chart/components/js/panel/common/common_table'
const { t } = useI18n()
class ImageCell extends CustomDataCell {
protected drawTextShape(): void {
const img = new Image()
const { x, y, width, height, fieldValue } = this.meta
img.src = fieldValue as string
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => {
!this.cfg.children && (this.cfg.children = [])
const { width: imgWidth, height: imgHeight } = img
const ratio = Math.max(imgWidth / width, imgHeight / height)
// 不铺满,部分留白
const imgShowWidth = (imgWidth / ratio) * 0.8
const imgShowHeight = (imgHeight / ratio) * 0.8
this.textShape = this.addShape('image', {
attrs: {
x: x + (imgShowWidth < width ? (width - imgShowWidth) / 2 : 0),
y: y + (imgShowHeight < height ? (height - imgShowHeight) / 2 : 0),
width: imgShowWidth,
height: imgShowHeight,
img
}
})
}
drawImage.apply(this)
}
}
/**
@ -75,9 +57,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
'alpha',
'tablePageMode',
'showHoverStyle',
'autoWrap',
'showSummary',
'summaryLabel'
'autoWrap'
],
'table-cell-selector': [
...TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'],
@ -85,7 +65,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
'tableColumnFreezeHead',
'tableRowFreezeHead',
'mergeCells'
]
],
'summary-selector': ['showSummary', 'summaryLabel']
}
axis: AxisType[] = ['xAxis', 'filter', 'drill']
axisConfig: AxisConfig = {
@ -103,7 +84,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
const columns = []
const meta = []
const axisMap = chart.xAxis.reduce((pre, cur) => {
pre[cur.dataeaseName] = cur
pre[cur.gisbiName] = cur
return pre
}, {})
const drillFieldMap = {}
@ -121,17 +102,17 @@ export class TableInfo extends S2ChartView<TableSheet> {
fields = fields.filter(ele => {
return !filterFields.includes(ele.id)
})
drillFieldMap[curDrillField.dataeaseName] = chart.drillFields[0].dataeaseName
drillFieldMap[curDrillField.gisbiName] = chart.drillFields[0].gisbiName
fields.splice(drillFieldIndex, 0, curDrillField)
}
fields.forEach(ele => {
const f = axisMap[ele.dataeaseName]
const f = axisMap[ele.gisbiName]
if (f?.hide === true) {
return
}
columns.push(ele.dataeaseName)
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: function (value) {
if (!f) {
@ -140,7 +121,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
let formatCfg = f.formatterCfg
@ -204,7 +185,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
// 自适应列宽模式下URL 字段的宽度固定为 120
if (basicStyle.tableColumnMode === 'adapt') {
const urlFields = fields.filter(
field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide
field => field.deType === 7 && !axisMap[field.gisbiName]?.hide
)
s2Options.style.colCfg.widthByFieldValue = urlFields?.reduce((p, n) => {
p[n.chartShowName ?? n.name] = 120
@ -215,37 +196,6 @@ export class TableInfo extends S2ChartView<TableSheet> {
s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0
s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0
}
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
s2Options.dataCell = viewMeta => {
const field = fields.filter(f => f.dataeaseName === viewMeta.valueField)?.[0]
if (field?.deType === 7 && chart.showPosition !== 'dialog') {
return new ImageCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
if (tableCell.mergeCells) {
viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta)
} else {
viewMeta.fieldValue =
pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
}
// 配置文本自动换行参数
viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
// tooltip
this.configTooltip(chart, s2Options)
// 合并单元格
@ -274,12 +224,12 @@ export class TableInfo extends S2ChartView<TableSheet> {
return new CustomTableColCell(node, sheet, config)
}
}
// 总计
configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary)
// 序列号和总计
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig)
// 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
// 总计紧贴在单元格后面
summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
// 开启自动换行
if (basicStyle.autoWrap && !tableCell.mergeCells) {
// 调整表头宽度时,计算表头高度
@ -340,8 +290,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
}
// 第一次渲染初始化,把图片字段固定为 120 进行计算
const urlFields = fields
.filter(field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide)
.map(f => f.dataeaseName)
.filter(field => field.deType === 7 && !axisMap[field.gisbiName]?.hide)
.map(f => f.gisbiName)
const totalWidthWithImg = ev.colLeafNodes.reduce((p, n) => {
return p + (urlFields.includes(n.field) ? 120 : n.width)
}, 0)
@ -386,7 +336,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
const cell = newChart.getCell(ev.target)
const meta = cell.getMeta() as ViewMeta
const nameIdMap = fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
@ -417,13 +367,13 @@ export class TableInfo extends S2ChartView<TableSheet> {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.MERGED_CELLS_HOVER, event => this.showTooltip(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click
newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })
@ -444,6 +394,11 @@ export class TableInfo extends S2ChartView<TableSheet> {
const fontStyle = tableCell.isItalic ? 'italic' : 'normal'
const fontWeight = tableCell.isBolder === false ? 'normal' : 'bold'
const mergeCellTheme: S2Theme = {
dataCell: {
cell: {
crossBackgroundColor: tableItemBgColor
}
},
mergedCell: {
cell: {
backgroundColor: tableItemBgColor,
@ -488,6 +443,92 @@ export class TableInfo extends S2ChartView<TableSheet> {
return theme
}
protected configSummaryRowAndIndex(
chart: Chart,
pageInfo: PageInfo,
s2Options: S2Options,
s2DataConfig: S2DataConfig
) {
const { tableHeader, basicStyle, tableCell } = parseJson(chart.customAttr)
const fields = chart.data?.fields ?? []
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
const { showSummary, summaryLabel } = basicStyle
const data = s2DataConfig.data
const xAxis = chart.xAxis
if (showSummary && data?.length) {
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[data.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = { heightByField }
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const axis = filter(xAxis, axis => [2, 3, 4].includes(axis.deType))
const summaryObj = getSummaryRow(data, axis, basicStyle.seriesSummary) as any
data.push(summaryObj)
}
s2Options.dataCell = viewMeta => {
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
} else {
// 第一列不是数值类型的,显示总计
if (![2, 3, 4].includes(xAxis?.[0]?.deType)) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
}
}
}
return new SummaryCell(viewMeta, viewMeta?.spreadsheet)
}
const field = fields.find(f => f.gisbiName === viewMeta.valueField)
if (field?.deType === 7 && chart.showPosition !== 'dialog') {
return new ImageCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
if (tableCell.mergeCells) {
viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta)
} else {
viewMeta.fieldValue =
pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
}
// 配置文本自动换行参数
viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
}
protected summaryRowStyle(newChart: TableSheet, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length) return
const columns = newChart.dataCfg.fields.columns
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? getMaxTreeDepth(columns) + 1 : 1
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight
}
})
}
constructor() {
super('table-info', [])
}
@ -508,3 +549,17 @@ function getStartPosition(node) {
}
return getStartPosition(node.children[0])
}
function getMaxTreeDepth(nodes) {
if (!nodes?.length) {
return 0
}
return Math.max(
...nodes.map(node => {
if (!node.children?.length) {
return 1
}
return getMaxTreeDepth(node.children) + 1
})
)
}

View File

@ -2,10 +2,11 @@ import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { formatterItem, valueFormatter } from '@/data-visualization/chart/components/js/formatter'
import {
configEmptyDataStyle,
configSummaryRow,
copyContent,
CustomDataCell,
getSummaryRow,
SortTooltip,
summaryRowStyle
SummaryCell
} from '@/data-visualization/chart/components/js/panel/common/common_table'
import { S2ChartView, S2DrawOptions } from '@/data-visualization/chart/components/js/panel/types/impl/s2'
import { parseJson } from '@/data-visualization/chart/components/js/util'
@ -19,7 +20,7 @@ import {
TableSheet,
ViewMeta
} from '@antv/s2'
import { cloneDeep, isNumber } from 'lodash-es'
import { isNumber } from 'lodash-es'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
const { t } = useI18n()
@ -37,8 +38,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
],
'basic-style-selector': [
...TABLE_EDITOR_PROPERTY_INNER['basic-style-selector'],
'showSummary',
'summaryLabel',
'tablePageMode',
'showHoverStyle'
],
'table-cell-selector': [
@ -46,7 +46,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
'tableFreeze',
'tableColumnFreezeHead',
'tableRowFreezeHead'
]
],
'summary-selector': ['showSummary', 'summaryLabel']
}
axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter']
axisConfig: AxisConfig = {
@ -66,7 +67,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
}
drawChart(drawOption: S2DrawOptions<TableSheet>): TableSheet {
const { container, chart, action, resizeAction } = drawOption
const { container, chart, action, pageInfo, resizeAction } = drawOption
const containerDom = document.getElementById(container)
if (!containerDom) return
@ -92,18 +93,18 @@ export class TableNormal extends S2ChartView<TableSheet> {
fields.splice(drillFieldIndex, 0, ...curDrillField)
}
const axisMap = [...chart.xAxis, ...chart.yAxis].reduce((pre, cur) => {
pre[cur.dataeaseName] = cur
pre[cur.gisbiName] = cur
return pre
}, {})
// add drill list
fields.forEach(ele => {
const f = axisMap[ele.dataeaseName]
const f = axisMap[ele.gisbiName]
if (f?.hide === true) {
return
}
columns.push(ele.dataeaseName)
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: function (value) {
if (!f) {
@ -112,7 +113,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
let formatCfg = f.formatterCfg
@ -160,19 +161,6 @@ export class TableNormal extends S2ChartView<TableSheet> {
s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0
s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0
}
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
// tooltip
this.configTooltip(chart, s2Options)
// 隐藏表头,保留顶部的分割线, 禁用表头横向 resize
@ -193,13 +181,12 @@ export class TableNormal extends S2ChartView<TableSheet> {
chart.container = container
this.configHeaderInteraction(chart, s2Options)
}
// 总计
configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary)
// 配置总计和序号列
this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig)
// 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
// 总计紧贴在单元格后面
summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
this.summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => {
@ -253,7 +240,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
const cell = newChart.getCell(ev.target)
const meta = cell.getMeta() as ViewMeta
const nameIdMap = fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
@ -281,19 +268,86 @@ export class TableNormal extends S2ChartView<TableSheet> {
if (show) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click
newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta))
// touch
this.configTouchEvent(newChart, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })
return newChart
}
protected configSummaryRowAndIndex(
chart: Chart,
pageInfo: PageInfo,
s2Options: S2Options,
s2DataConfig: S2DataConfig
) {
const { tableHeader, basicStyle } = parseJson(chart.customAttr)
// 开启序号之后,第一列就是序号列,修改 label 即可
if (s2Options.showSeriesNumber) {
let indexLabel = tableHeader.indexLabel
if (!indexLabel) {
indexLabel = ''
}
s2Options.layoutCoordinate = (_, __, col) => {
if (col.colIndex === 0 && col.rowIndex === 0) {
col.label = indexLabel
col.value = indexLabel
}
}
}
const { showSummary, summaryLabel } = basicStyle
const data = s2DataConfig.data
const { xAxis, yAxis } = chart
if (showSummary && data?.length) {
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[data.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = { heightByField }
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const summaryObj = getSummaryRow(data, yAxis, basicStyle.seriesSummary) as any
data.push(summaryObj)
}
s2Options.dataCell = viewMeta => {
// 总计行处理
if (showSummary && viewMeta.rowIndex === data.length - 1) {
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex || xAxis?.length) {
viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
}
}
return new SummaryCell(viewMeta, viewMeta?.spreadsheet)
}
if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
viewMeta.fieldValue = pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
}
return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
}
}
protected summaryRowStyle(newChart, newData, tableCell, tableHeader, showSummary) {
if (!showSummary || !newData.length) return
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? 2 : 1
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.container.cfg.height) {
newChart.options.height =
totalHeight < newChart.container.cfg.height - 8 ? totalHeight + 8 : totalHeight
}
})
}
constructor() {
super('table-normal', [])
}

View File

@ -13,7 +13,8 @@ import {
TotalStatus,
Aggregation,
S2DataConfig,
MergedCell
MergedCell,
LayoutResult
} from '@antv/s2'
import { formatterItem, valueFormatter } from '../../../formatter'
import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util'
@ -91,7 +92,8 @@ export class TablePivot extends S2ChartView<PivotSheet> {
'showColTooltip',
'showRowTooltip',
'showHorizonBorder',
'showVerticalBorder'
'showVerticalBorder',
'rowHeaderFreeze'
],
'table-total-selector': ['row', 'col'],
'basic-style-selector': [
@ -100,7 +102,9 @@ export class TablePivot extends S2ChartView<PivotSheet> {
'tableScrollBarColor',
'alpha',
'tableLayoutMode',
'showHoverStyle'
'showHoverStyle',
'quotaPosition',
'quotaColLabel'
]
}
axis: AxisType[] = ['xAxis', 'xAxisExt', 'yAxis', 'filter']
@ -126,7 +130,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
const { xAxisExt: columnFields, xAxis: rowFields, yAxis: valueFields } = chart
const [c, r, v] = [columnFields, rowFields, valueFields].map(arr =>
arr.map(i => i.dataeaseName)
arr.map(i => i.gisbiName)
)
// fields
@ -146,14 +150,14 @@ export class TablePivot extends S2ChartView<PivotSheet> {
...chart.xAxisExt,
...chart.yAxis
].reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {})
fields.forEach(ele => {
const f = valueFieldMap[ele.dataeaseName]
columns.push(ele.dataeaseName)
const f = valueFieldMap[ele.gisbiName]
columns.push(ele.gisbiName)
meta.push({
field: ele.dataeaseName,
field: ele.gisbiName,
name: ele.chartShowName ?? ele.name,
formatter: value => {
if (!f) {
@ -162,7 +166,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
if (value === null || value === undefined) {
return value
}
if (![2, 3].includes(f.deType) || !isNumber(value)) {
if (![2, 3, 4].includes(f.deType) || !isNumber(value)) {
return value
}
if (f.formatterCfg) {
@ -175,7 +179,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
})
// total config
const { basicStyle, tooltip, tableTotal } = parseJson(chart.customAttr)
const { basicStyle, tooltip, tableTotal, tableHeader } = parseJson(chart.customAttr)
if (!tableTotal.row.subTotalsDimensionsNew || tableTotal.row.subTotalsDimensions == undefined) {
tableTotal.row.subTotalsDimensions = r
}
@ -183,6 +187,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
// 解析合计、小计排序
const sortParams = []
let rowTotalSort = false
if (
tableTotal.row.totalSort &&
tableTotal.row.totalSort !== 'none' &&
@ -190,16 +195,20 @@ export class TablePivot extends S2ChartView<PivotSheet> {
tableTotal.row.showGrandTotals &&
v.indexOf(tableTotal.row.totalSortField) > -1
) {
const sort = {
sortFieldId: c[0],
sortMethod: tableTotal.row.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: tableTotal.row.totalSortField
c.forEach(i => {
const sort = {
sortFieldId: i,
sortMethod: tableTotal.row.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: tableTotal.row.totalSortField
}
}
}
sortParams.push(sort)
sortParams.push(sort)
})
rowTotalSort = true
}
let colTotalSort = false
if (
tableTotal.col.totalSort &&
tableTotal.col.totalSort !== 'none' &&
@ -207,15 +216,18 @@ export class TablePivot extends S2ChartView<PivotSheet> {
tableTotal.col.showGrandTotals &&
v.indexOf(tableTotal.col.totalSortField) > -1
) {
const sort = {
sortFieldId: r[0],
sortMethod: tableTotal.col.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: tableTotal.col.totalSortField
r.forEach(i => {
const sort = {
sortFieldId: i,
sortMethod: tableTotal.col.totalSort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: tableTotal.col.totalSortField
}
}
}
sortParams.push(sort)
sortParams.push(sort)
})
colTotalSort = true
}
//列维度为空,行排序按照指标列来排序,取第一个有排序设置的指标
if (!columnFields?.length) {
@ -226,7 +238,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
sortMethod: sortField.sort.toUpperCase(),
sortByMeasure: TOTAL_VALUE,
query: {
[EXTRA_FIELD]: sortField.dataeaseName
[EXTRA_FIELD]: sortField.gisbiName
}
}
sortParams.push(sort)
@ -244,15 +256,29 @@ export class TablePivot extends S2ChartView<PivotSheet> {
col: chart.xAxisExt,
quota: chart.yAxis
}
//树形模式下,列维度为空,行小计会变成列总计,特殊处理下
if (basicStyle.tableLayoutMode === 'tree' && !chart.xAxisExt?.length) {
tableTotal.col.calcTotals = tableTotal.row.calcSubTotals
// 沒有列维度需要特殊处理
if (!chart.xAxisExt?.length) {
//树形模式下,列维度为空,行小计的配置会变成列总计
if (basicStyle.tableLayoutMode === 'tree') {
tableTotal.col.calcTotals = tableTotal.row.calcSubTotals
if (!tableTotal.col.calcTotals.cfg?.length) {
tableTotal.col.calcTotals.cfg = chart.yAxis.map(y => {
return {
gisbiName: y.gisbiName,
aggregation: 'SUM'
}
})
}
} else {
// 列总计设置为空
tableTotal.col.calcTotals.calcFunc = () => '-'
}
}
totals.forEach(total => {
if (total.cfg?.length) {
delete total.aggregation
const totalCfgMap = total.cfg.reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {})
total.calcFunc = (query, data, _, status) => {
@ -262,12 +288,93 @@ export class TablePivot extends S2ChartView<PivotSheet> {
})
// 空值处理
const newData = this.configEmptyDataStrategy(chart)
// 行列维度排序
if (!rowTotalSort) {
c?.forEach((f, i) => {
if (valueFieldMap[f]?.sort === 'none') {
return
}
const sort = {
sortFieldId: f
}
const sortMethod = valueFieldMap[f]?.sort?.toUpperCase()
if (sortMethod === 'CUSTOM_SORT') {
sort.sortBy = valueFieldMap[f].customSort
} else {
if (i === 0) {
sort.sortMethod = sortMethod
} else {
const fieldValues = newData.map(item => item[f])
const uniqueValues = [...new Set(fieldValues)]
// 根据配置动态决定排序顺序
uniqueValues.sort((a, b) => {
if ([2, 3, 4].includes(valueFieldMap[f]?.deType)) {
return sortMethod === 'ASC' ? a - b : b - a
}
if (!a && !b) {
return 0
}
if (!a) {
return sortMethod === 'ASC' ? -1 : 1
}
if (!b) {
return sortMethod === 'ASC' ? 1 : -1
}
return sortMethod === 'ASC' ? a.localeCompare(b) : b.localeCompare(a)
})
sort.sortBy = uniqueValues
}
}
sortParams.push(sort)
})
}
if (!colTotalSort) {
r?.forEach((f, i) => {
if (valueFieldMap[f]?.sort === 'none') {
return
}
const sort = {
sortFieldId: f
}
const sortMethod = valueFieldMap[f]?.sort?.toUpperCase()
if (sortMethod === 'CUSTOM_SORT') {
sort.sortBy = valueFieldMap[f].customSort
} else {
if (i === 0) {
sort.sortMethod = sortMethod
} else {
const fieldValues = newData.map(item => item[f])
const uniqueValues = [...new Set(fieldValues)]
// 根据配置动态决定排序顺序
uniqueValues.sort((a, b) => {
if ([2, 3, 4].includes(valueFieldMap[f]?.deType)) {
return sortMethod === 'ASC' ? a - b : b - a
}
if (!a && !b) {
return 0
}
if (!a) {
return sortMethod === 'ASC' ? -1 : 1
}
if (!b) {
return sortMethod === 'ASC' ? 1 : -1
}
return sortMethod === 'ASC' ? a.localeCompare(b) : b.localeCompare(a)
})
sort.sortBy = uniqueValues
}
}
sortParams.push(sort)
})
}
// data config
const s2DataConfig: S2DataConfig = {
fields: {
rows: r,
columns: c,
values: v
values: v,
valueInCols: !(basicStyle.quotaPosition === 'row')
},
meta: meta,
data: newData,
@ -277,6 +384,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
width: containerDom.offsetWidth,
height: containerDom.offsetHeight,
totals: tableTotal as Totals,
cornerExtraFieldText: basicStyle.quotaColLabel ?? t('dataset.value'),
conditions: this.configConditions(chart),
tooltip: {
getContainer: () => containerDom
@ -288,21 +396,204 @@ export class TablePivot extends S2ChartView<PivotSheet> {
},
dataCell: meta => {
return new CustomDataCell(meta, meta.spreadsheet)
}
},
frozenRowHeader: !(tableHeader.rowHeaderFreeze === false)
}
// options
s2Options.style = this.configStyle(chart, s2DataConfig)
s2Options.style.hierarchyCollapse = true
// 默认展开层级
if (basicStyle.tableLayoutMode === 'tree') {
const { defaultExpandLevel } = basicStyle
if (isNumber(defaultExpandLevel)) {
if (defaultExpandLevel >= chart.xAxis.length) {
s2Options.style.rowExpandDepth = defaultExpandLevel
} else {
s2Options.style.rowExpandDepth = defaultExpandLevel - 2
}
}
if (defaultExpandLevel === 'all') {
s2Options.style.rowExpandDepth = chart.xAxis.length
}
if (!defaultExpandLevel) {
s2Options.style.hierarchyCollapse = true
}
}
// 列汇总别名
if (!(basicStyle.quotaPosition === 'row' && basicStyle.tableLayoutMode === 'tree')) {
if (
basicStyle.quotaPosition !== 'row' &&
chart.xAxisExt?.length &&
chart.yAxis?.length > 1 &&
tableTotal.col.showGrandTotals &&
tableTotal.col.calcTotals?.cfg?.length
) {
const colTotalCfgMap = tableTotal.col.calcTotals.cfg.reduce((p, n) => {
p[n.gisbiName] = n
return p
}, {})
s2Options.layoutCoordinate = (_, __, col) => {
if (col?.isGrandTotals) {
if (colTotalCfgMap[col.value]?.label) {
col.label = colTotalCfgMap[col.value].label
}
}
}
}
if (
basicStyle.quotaPosition === 'row' &&
chart.xAxisExt?.length &&
chart.yAxis?.length > 1 &&
tableTotal.row.showGrandTotals &&
tableTotal.row.calcTotals?.cfg?.length
) {
const rowTotalCfgMap = tableTotal.row.calcTotals.cfg.reduce((p, n) => {
p[n.gisbiName] = n
return p
}, {})
// eslint-disable-next-line
s2Options.layoutCoordinate = (_, row, __) => {
if (row?.isGrandTotals) {
if (rowTotalCfgMap[row.value]?.label) {
row.label = rowTotalCfgMap[row.value].label
}
}
}
}
}
// tooltip
this.configTooltip(chart, s2Options)
// 开始渲染
const s2 = new PivotSheet(containerDom, s2DataConfig, s2Options as unknown as S2Options)
// 自适应铺满
if (basicStyle.tableColumnMode === 'adapt') {
s2.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
// 平铺模式行头resize
s2.on(S2Event.LAYOUT_RESIZE_ROW_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
// 树形模式行头resize
s2.on(S2Event.LAYOUT_RESIZE_TREE_WIDTH, () => {
s2.store.set('lastLayoutResult', s2.facet.layoutResult)
})
s2.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => {
const lastLayoutResult = s2.store.get('lastLayoutResult') as LayoutResult
if (lastLayoutResult) {
// 拖动 col 表头 resize
const colWidthByFieldValue = s2.options.style?.colCfg?.widthByFieldValue
// 平铺模式拖动 row 表头 resize
const rowWidthByField = s2.options.style?.rowCfg?.widthByField
// 树形模式拖动 row 表头 resize
const treeRowWidth =
s2.options.style?.treeRowsWidth || lastLayoutResult.rowsHierarchy.width
const colWidthMap =
lastLayoutResult.colLeafNodes.reduce((p, n) => {
p[n.id] = colWidthByFieldValue?.[n.value] ?? n.width
return p
}, {}) || {}
const totalColWidth = ev.colLeafNodes.reduce((p, n) => {
n.width = colWidthMap[n.id] || n.width
n.x = p
return p + n.width
}, 0)
ev.colNodes.forEach(n => {
if (n.isLeaf) {
return
}
n.width = this.getColWidth(n)
n.x = this.getLeftChild(n).x
})
if (basicStyle.tableLayoutMode === 'tree') {
ev.rowNodes.forEach(n => {
n.width = treeRowWidth
})
ev.rowsHierarchy.width = treeRowWidth
ev.colsHierarchy.width = totalColWidth
} else {
const rowWidthMap =
lastLayoutResult.rowNodes.reduce((p, n) => {
p[n.id] = rowWidthByField?.[n.field] ?? n.width
return p
}, {}) || {}
ev.rowNodes.forEach(n => {
n.x = 0
n.width = rowWidthMap[n.id] || n.width
let tmp = n
while (tmp.parent.id !== 'root') {
n.x += tmp.parent.width
tmp = tmp.parent
}
})
const totlaRowWidth = ev.rowsHierarchy.sampleNodesForAllLevels.reduce((p, n) => {
return p + n.width
}, 0)
const maxRowLevel = ev.rowsHierarchy.maxLevel
ev.rowNodes.forEach(n => {
// 总计和中间层级的小计需要重新计算宽度
if (n.isTotalRoot || (n.isSubTotals && n.level < maxRowLevel)) {
let width = 0
for (let i = n.level; i <= maxRowLevel; i++) {
width += ev.rowsHierarchy.sampleNodesForAllLevels[i].width
}
n.width = width
}
})
ev.rowsHierarchy.width = totlaRowWidth
ev.colsHierarchy.width = totalColWidth
}
s2.store.set('lastLayoutResult', undefined)
return
}
const containerWidth = containerDom.getBoundingClientRect().width
const scale = containerWidth / (ev.colsHierarchy.width + ev.rowsHierarchy.width)
if (scale <= 1) {
return
}
const totalRowWidth = Math.round(ev.rowsHierarchy.width * scale)
ev.rowNodes.forEach(n => {
n.width = Math.round(n.width * scale)
})
if (basicStyle.tableLayoutMode !== 'tree') {
ev.rowNodes.forEach(n => {
n.x = 0
let tmp = n
while (tmp.parent.id !== 'root') {
n.x += tmp.parent.width
tmp = tmp.parent
}
})
}
let totalColWidth = ev.colLeafNodes.reduce((p, n) => {
n.width = Math.round(n.width * scale)
n.x = p
return p + n.width
}, 0)
ev.colNodes.forEach(n => {
if (n.isLeaf) {
return
}
n.width = this.getColWidth(n)
n.x = this.getLeftChild(n).x
})
const totalWidth = totalColWidth + totalRowWidth
if (totalWidth > containerWidth) {
// 从最后一列减掉
ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth
totalColWidth = totalColWidth - (totalWidth - containerWidth)
}
ev.colsHierarchy.width = totalColWidth
ev.rowsHierarchy.width = totalRowWidth
})
}
// tooltip
const { show } = tooltip
if (show) {
s2.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(s2, event, meta))
s2.on(S2Event.ROW_CELL_HOVER, event => this.showTooltip(s2, event, meta))
s2.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(s2, event, meta))
// touch
this.configTouchEvent(s2, drawOption, meta)
}
// empty data tip
configEmptyDataStyle(s2, newData)
@ -312,19 +603,34 @@ export class TablePivot extends S2ChartView<PivotSheet> {
s2.on(S2Event.COL_CELL_CLICK, ev => this.headerCellClickAction(chart, ev, s2, action))
// right click
s2.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(s2, event, meta))
// touch
this.configTouchEvent(s2, drawOption, meta)
// theme
const customTheme = this.configTheme(chart)
s2.setThemeCfg({ theme: customTheme })
return s2
}
private getColWidth(node) {
let width = 0
if (node.children?.length) {
node.children.forEach(child => {
width += this.getColWidth(child)
})
} else {
width = node.width
}
return width
}
private getLeftChild(node) {
if (!node.children?.length) {
return node
}
return this.getLeftChild(node.children[0])
}
private dataCellClickAction(chart: Chart, ev, s2Instance: PivotSheet, callback) {
const cell = s2Instance.getCell(ev.target)
const meta = cell.getMeta()
const nameIdMap = chart.data.fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
const rowData = { ...meta.rowQuery, ...meta.colQuery }
@ -352,7 +658,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
const meta = cell.getMeta()
const rowData = meta.query
const nameIdMap = chart.data.fields.reduce((pre, next) => {
pre[next['dataeaseName']] = next['id']
pre[next['gisbiName']] = next['id']
return pre
}, {})
const dimensionList = []
@ -522,7 +828,7 @@ export class TablePivot extends S2ChartView<PivotSheet> {
}
function customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, customCalc) {
if (!data?.length || !query[EXTRA_FIELD]) {
return 0
return '-'
}
const aggregation = totalCfgMap[query[EXTRA_FIELD]]?.aggregation || 'SUM'
switch (aggregation) {
@ -549,10 +855,13 @@ function customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, custom
})
return result?.[query[EXTRA_FIELD]]
}
case 'NONE': {
return '-'
}
case 'CUSTOM': {
const val = getCustomCalcResult(query, axisMap, chart, status, customCalc || {})
if (val === '') {
return val
if (val === '' || val === undefined) {
return '-'
}
return parseFloat(val)
}
@ -593,11 +902,17 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
// 列小计
if (status.isColSubTotal && !status.isRowTotal && !status.isRowSubTotal) {
const { colSubTotal } = customCalc
const subLevel = getSubLevel(query, col)
const subColLevel = getSubLevel(query, col)
const subRowLevel = getSubLevel(query, row)
const rowPath = getTreePath(query, row)
const colPath = getTreePath(query, col)
const path = [...rowPath, ...colPath]
const data = colSubTotal?.[subLevel]?.data
let data = colSubTotal?.[subColLevel]?.data
// 列小计里面的行小计
if (rowPath.length < row.length) {
const { rowSubInColSub } = customCalc
data = rowSubInColSub?.[subRowLevel]?.[subColLevel]?.data
}
let val
if (path.length && data) {
path.push(quotaField)
@ -647,7 +962,7 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
if (status.isRowTotal && status.isColSubTotal) {
const { colSubInRowTotal } = customCalc
const colLevel = getSubLevel(query, col)
const { data } = colSubInRowTotal?.[colLevel]
const data = colSubInRowTotal?.[colLevel]?.data
const colPath = getTreePath(query, col)
let val
if (colPath.length && colSubInRowTotal) {
@ -669,23 +984,7 @@ function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
}
return val
}
// 列小计里面的行小计
if (status.isColSubTotal && status.isRowSubTotal) {
const { rowSubInColSub } = customCalc
const rowSubLevel = getSubLevel(query, row)
const colSubLevel = getSubLevel(query, col)
const data = rowSubInColSub?.[rowSubLevel]?.[colSubLevel]?.data
const rowPath = getTreePath(query, row)
const colPath = getTreePath(query, col)
const path = [...rowPath, ...colPath]
let val
if (path.length && rowSubInColSub) {
path.push(quotaField)
val = get(data, path)
}
return val
}
return NaN
return '-'
}
function getGridCustomCalcResult(query, axisMap, status: TotalStatus, customCalc) {
@ -759,7 +1058,7 @@ function getGridCustomCalcResult(query, axisMap, status: TotalStatus, customCalc
if (status.isRowTotal && status.isColSubTotal) {
const { colSubInRowTotal } = customCalc
const colLevel = getSubLevel(query, col)
const { data } = colSubInRowTotal?.[colLevel]
const data = colSubInRowTotal?.[colLevel]?.data
const colPath = getTreePath(query, col)
let val
if (colPath.length && colSubInRowTotal) {
@ -807,7 +1106,7 @@ function getCustomCalcResult(query, axisMap, chart: ChartObj, status: TotalStatu
}
function getSubLevel(query, axis) {
const fields: [] = axis.map(a => a.dataeaseName)
const fields: [] = axis.map(a => a.gisbiName)
let subLevel = -1
const queryFields = keys(query)
for (let i = fields.length - 1; i >= 0; i--) {
@ -824,9 +1123,9 @@ function getTreePath(query, axis) {
const path = []
const fields = keys(query)
axis.forEach(a => {
const index = fields.findIndex(f => f === a.dataeaseName)
const index = fields.findIndex(f => f === a.gisbiName)
if (index !== -1) {
path.push(query[a.dataeaseName])
path.push(query[a.gisbiName])
}
})
return path

View File

@ -33,10 +33,23 @@ import { PositionType } from '@antv/l7-core'
import { centroid } from '@turf/centroid'
import type { Plot } from '@antv/g2plot'
import type { PickOptions } from '@antv/g2plot/lib/core/plot'
import { defaults } from 'lodash-es'
import { defaults, find } from 'lodash-es'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const { t: tI18n } = useI18n()
import { isMobile } from '@/data-visualization/utils/utils'
import { GaodeMap, TMap, TencentMap } from '@antv/l7-maps'
import {
gaodeMapStyleOptions,
qqMapStyleOptions,
tdtMapStyleOptions
} from '@/data-visualization/chart/components/js/panel/charts/map/common'
import ChartCarouselTooltip, {
isPie,
isColumn,
isMix,
isSupport
} from '@/data-visualization/chart/components/js/g2plot_tooltip_carousel'
const { t: tI18n } = useI18n()
export function getPadding(chart: Chart): number[] {
if (chart.drill) {
@ -137,14 +150,22 @@ export function getTheme(chart: Chart) {
},
'g2-tooltip-list-item': {
display: 'flex',
'align-items': 'center'
'align-items': 'flex-start',
'justify-content': 'space-between',
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-name': {
display: 'inline-block',
'line-height': tooltipFontsize + 'px',
flex: 1
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-value': {
flex: 1,
display: 'inline-block',
'text-align': 'end',
'line-height': tooltipFontsize + 'px'
},
'g2-tooltip-marker': {
'margin-top': (tooltipFontsize - 8) / 2 + 'px',
'min-width': '8px',
'min-height': '8px'
}
@ -469,7 +490,8 @@ export function getXAxis(chart: Chart) {
style: {
fill: a.axisLabel.color,
fontSize: a.axisLabel.fontSize,
textAlign: textAlign
textAlign: textAlign,
fontFamily: chart.fontFamily
},
formatter: value => {
return chart.type === 'bidirectional-bar' && value.length > a.axisLabel.lengthLimit
@ -574,7 +596,8 @@ export function getYAxis(chart: Chart) {
fill: yAxis.axisLabel.color,
fontSize: yAxis.axisLabel.fontSize,
textBaseline,
textAlign
textAlign,
fontFamily: chart.fontFamily
},
formatter: value => {
return value.length > yAxis.axisLabel.lengthLimit
@ -603,7 +626,7 @@ export function getYAxisExt(chart: Chart) {
return false
}
const title =
yAxis.name && yAxis.name !== ''
yAxis.nameShow && yAxis.name && yAxis.name !== ''
? {
text: yAxis.name,
style: {
@ -629,14 +652,16 @@ export function getYAxisExt(chart: Chart) {
? {
style: {
stroke: axisCfg.lineStyle.color,
lineWidth: axisCfg.lineStyle.width
lineWidth: axisCfg.lineStyle.width,
lineDash: getLineDash(axisCfg.lineStyle.style)
}
}
: null
const tickLine = axisCfg.show
? {
style: {
stroke: axisCfg.lineStyle.color
stroke: axisCfg.lineStyle.color,
lineWidth: axisCfg.lineStyle.width
}
}
: null
@ -673,7 +698,8 @@ export function getYAxisExt(chart: Chart) {
fill: yAxis.axisLabel.color,
fontSize: yAxis.axisLabel.fontSize,
textBaseline,
textAlign
textAlign,
fontFamily: chart.fontFamily
}
}
: null
@ -821,10 +847,9 @@ export function getAnalyseHorizontal(chart: Chart) {
const assistLineArr = senior.assistLineCfg.assistLine
if (assistLineArr?.length > 0) {
const customStyle = parseJson(chart.customStyle)
let xAxisPosition, axisFormatterCfg
let axisFormatterCfg
if (customStyle.xAxis) {
const a = JSON.parse(JSON.stringify(customStyle.xAxis))
xAxisPosition = transAxisPosition(a.position)
axisFormatterCfg = a.axisLabelFormatter
? a.axisLabelFormatter
: DEFAULT_XAXIS_STYLE.axisLabelFormatter
@ -894,7 +919,9 @@ export function getLineDash(type) {
*/
export function setGradientColor(rawColor: string, show = false, angle = 0, start = 0) {
const item = rawColor.split(',')
item.splice(3, 1, '0.3)')
const alpha = parseFloat(item[3].replace(')', ''))
const startAlpha = alpha * 0.3
item.splice(3, 1, `${startAlpha})`)
let color: string
if (start == 0) {
color = `l(${angle}) 0:${item.join(',')} 1:${rawColor}`
@ -993,6 +1020,9 @@ export function configL7Tooltip(chart: Chart): TooltipOptions {
return result
}
const head = originalItem.properties
if (!head) {
return result
}
const formatter = formatterMap[head.quotaList?.[0]?.id]
if (!isEmpty(formatter)) {
const originValue = parseFloat(head.value as string)
@ -1152,13 +1182,27 @@ export class CustomZoom extends Zoom {
'l7-button-control',
container,
() => {
if (this.controlOption['bounds']) {
this.mapsService.fitBounds(this.controlOption['bounds'], { animate: true })
if (this.mapsService.map?.deMapProvider == 'qq') {
if (this.mapsService.map.deMapAutoFit) {
this.mapsService.setZoomAndCenter(this.mapsService.map.deMapAutoZoom, [
this.mapsService.map.deMapAutoLng,
this.mapsService.map.deMapAutoLat
])
} else {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
}
} else {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
if (this.controlOption['bounds']) {
this.mapsService.fitBounds(this.controlOption['bounds'], { animate: true })
} else {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
}
}
}
)
@ -1208,7 +1252,11 @@ export class CustomZoom extends Zoom {
} as IZoomControlOption
}
}
export function configL7Zoom(chart: Chart, scene: Scene) {
export function configL7Zoom(
chart: Chart,
scene: Scene,
mapKey?: { key: string; securityCode: string; mapType: string }
) {
const { basicStyle } = parseJson(chart.customAttr)
const zoomOption = scene?.getControlByName('zoom')
if (zoomOption) {
@ -1220,20 +1268,56 @@ export function configL7Zoom(chart: Chart, scene: Scene) {
if (!scene?.getControlByName('zoom')) {
if (!scene.map) {
scene.once('loaded', () => {
scene.map.on('complete', () => {
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().lng, scene.map.getCenter().lat]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
})
switch (mapKey?.mapType) {
case 'tianditu':
//天地图
{
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().getLng(), scene.map.getCenter().getLat()]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
}
break
case 'qq':
{
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().lng, scene.map.getCenter().lat]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
}
break
default:
scene.map.on('complete', () => {
const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom()
const center =
basicStyle.autoFit === false
? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude]
: [scene.map.getCenter().lng, scene.map.getCenter().lat]
const newZoomOptions = {
initZoom: initZoom,
center: center,
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
scene.addControl(new CustomZoom(newZoomOptions))
})
}
})
} else {
const newZoomOptions = {
@ -1250,19 +1334,19 @@ export function configL7Zoom(chart: Chart, scene: Scene) {
const endAxis = chart.xAxisExt
if (startAxis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[startAxis[0].dataeaseName], row[startAxis[1].dataeaseName]])
coordinates.push([row[startAxis[0].gisbiName], row[startAxis[1].gisbiName]])
})
}
if (endAxis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[endAxis[0].dataeaseName], row[endAxis[1].dataeaseName]])
coordinates.push([row[endAxis[0].gisbiName], row[endAxis[1].gisbiName]])
})
}
} else {
const axis = chart.xAxis
if (axis?.length === 2) {
chart.data?.tableRow?.forEach(row => {
coordinates.push([row[axis[0].dataeaseName], row[axis[1].dataeaseName]])
coordinates.push([row[axis[0].gisbiName], row[axis[1].gisbiName]])
})
}
}
@ -1335,6 +1419,18 @@ export function mapRendering(dom: HTMLElement | string) {
dom.classList.add('de-map-rendering')
}
export function qqMapRendered(scene?: Scene) {
if (scene?.map && scene.map.deMapProvider === 'qq') {
setTimeout(() => {
if (scene.map) {
scene.map.deMapAutoZoom = scene.map.getZoom()
scene.map.deMapAutoLng = scene.map.getCenter().getLng()
scene.map.deMapAutoLat = scene.map.getCenter().getLat()
}
}, 1000)
}
}
export function mapRendered(dom: HTMLElement | string) {
if (typeof dom === 'string') {
dom = document.getElementById(dom)
@ -1342,6 +1438,213 @@ export function mapRendered(dom: HTMLElement | string) {
dom.classList.add('de-map-rendered')
}
export function getMapCenter(basicStyle: ChartBasicStyle) {
let center: [number, number]
if (basicStyle.autoFit === false) {
const longitude = basicStyle?.mapCenter?.longitude ?? DEFAULT_BASIC_STYLE.mapCenter.longitude
const latitude = basicStyle?.mapCenter?.latitude ?? DEFAULT_BASIC_STYLE.mapCenter.latitude
center = [longitude, latitude]
} else {
center = undefined
}
return center
}
export function getMapStyle(
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle
) {
let mapStyle: string
switch (mapKey.mapType) {
case 'tianditu':
if (!find(tdtMapStyleOptions, s => s.value === basicStyle.mapStyle)) {
mapStyle = 'normal'
} else {
mapStyle = basicStyle.mapStyle
}
break
case 'qq':
if (
!find(qqMapStyleOptions, s => s.value === basicStyle.mapStyle) ||
basicStyle.mapStyle === 'normal'
) {
mapStyle = 'normal'
} else {
mapStyle = basicStyle.mapStyleUrl
}
break
default:
if (!find(gaodeMapStyleOptions, s => s.value === basicStyle.mapStyle)) {
basicStyle.mapStyle = 'normal'
}
mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
break
}
return mapStyle
}
export async function getMapScene(
chart: Chart,
scene: Scene,
container: string,
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle,
miscStyle: ChartMiscAttr,
mapStyle: string,
center?: [number, number]
) {
if (!scene) {
scene = new Scene({
id: container,
logoVisible: false,
map: getMapObject(mapKey, basicStyle, miscStyle, mapStyle, center)
})
} else {
if (mapKey.mapType === 'tianditu') {
scene.map?.checkResize()
}
if (scene.getLayers()?.length) {
await scene.removeAllLayer()
try {
scene.setPitch(miscStyle.mapPitch)
} catch (e) {}
if (mapKey.mapType === 'tianditu') {
if (mapStyle === 'normal') {
scene.map?.removeStyle()
} else {
scene.setMapStyle(mapStyle)
}
} else {
scene.setMapStyle(mapStyle)
}
scene.map.showLabel = !(basicStyle.showLabel === false)
if (mapKey.mapType === 'qq') {
scene.map.setBaseMap({
//底图设置参数为VectorBaseMap对象
type: 'vector', //类型:失量底图
features: basicStyle.showLabel === false ? ['base', 'building2d'] : undefined
//仅渲染:道路及底面(base) + 2d建筑物(building2d),以达到隐藏文字的效果
})
}
}
if (basicStyle.autoFit === false) {
scene.setZoomAndCenter(basicStyle.zoomLevel, center)
if (mapKey.mapType === 'qq') {
scene.map.deMapAutoFit = false
scene.map.deMapZoom = basicStyle.zoomLevel
scene.map.deMapCenter = center
}
}
}
mapRendering(container)
scene.once('loaded', () => {
mapRendered(container)
if (mapKey.mapType === 'qq') {
scene.map.setBaseMap({
//底图设置参数为VectorBaseMap对象
type: 'vector', //类型:失量底图
features: basicStyle.showLabel === false ? ['base', 'building2d'] : undefined
//仅渲染:道路及底面(base) + 2d建筑物(building2d),以达到隐藏文字的效果
})
scene.setMapStyle(mapStyle)
scene.map.deMapProvider = 'qq'
scene.map.deMapAutoFit = !!basicStyle.autoFit
// scene.map.deMapAutoZoom = scene.map.getZoom()
// scene.map.deMapAutoLng = scene.map.getCenter().getLng()
// scene.map.deMapAutoLat = scene.map.getCenter().getLat()
}
// 去除天地图自己的缩放按钮
if (mapKey.mapType === 'tianditu') {
if (mapStyle === 'normal') {
scene.map?.removeStyle()
} else {
scene.setMapStyle(mapStyle)
}
const tdtControl = document.querySelector(
`#component${chart.id} .tdt-control-zoom.tdt-bar.tdt-control`
)
if (tdtControl) {
tdtControl.style.display = 'none'
}
const tdtControlOuter = document.querySelectorAll(
`#wrapper-outer-id-${chart.id} .tdt-control-zoom.tdt-bar.tdt-control`
)
if (tdtControlOuter && tdtControlOuter.length > 0) {
for (let i = 0; i < tdtControlOuter.length; i++) {
tdtControlOuter[i].style.display = 'none'
}
}
const tdtCopyrightControl = document.querySelector(
`#component${chart.id} .tdt-control-copyright.tdt-control`
)
if (tdtCopyrightControl) {
tdtCopyrightControl.style.display = 'none'
}
const tdtCopyrightControlOuter = document.querySelectorAll(
`#wrapper-outer-id-${chart.id} .tdt-control-copyright.tdt-control`
)
if (tdtCopyrightControlOuter && tdtCopyrightControlOuter.length > 0) {
for (let i = 0; i < tdtCopyrightControlOuter.length; i++) {
tdtCopyrightControlOuter[i].style.display = 'none'
}
}
}
})
return scene
}
export function getMapObject(
mapKey: { key: string; securityCode: string; mapType: string },
basicStyle: ChartBasicStyle,
miscStyle: ChartMiscAttr,
mapStyle: string,
center?: [number, number]
) {
switch (mapKey.mapType) {
case 'tianditu':
return new TMap({
token: mapKey?.key ?? undefined,
style: mapStyle, //不生效
pitch: undefined, //不支持
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false), //不支持
WebGLParams: {
preserveDrawingBuffer: true
}
})
case 'qq':
return new TencentMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : 12,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
default:
return new GaodeMap({
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center,
zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined,
showLabel: !(basicStyle.showLabel === false),
WebGLParams: {
preserveDrawingBuffer: true
}
})
}
}
/**
*
* @param basicStyle
@ -1358,6 +1661,8 @@ export function getTooltipContainer(id) {
let wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER)
if (!wrapperDom) {
wrapperDom = document.createElement('div')
wrapperDom.style.position = 'absolute'
wrapperDom.style.zIndex = '9999'
wrapperDom.id = G2_TOOLTIP_WRAPPER
document.body.appendChild(wrapperDom)
}
@ -1391,14 +1696,78 @@ export function getTooltipContainer(id) {
}
return g2Tooltip
}
/**
*
* @param plot
* @param chart
*/
function configCarouselTooltip(plot, chart) {
const start = isSupport(chart.type) && !document.getElementById('multiplexingDrawer')
if (start) {
// 启用轮播
plot.once('afterrender', () => {
const carousel = chart.customAttr?.tooltip?.carousel
ChartCarouselTooltip.manage(plot, chart, {
xField: 'field',
duration: carousel.enable ? carousel?.stayTime * 1000 : 2000,
interval: carousel.enable ? carousel?.intervalTime * 1000 : 2000
})
})
}
}
/**
* Tooltip
* @param {Chart} chart -
* @param {boolean} isCarousel -
* @param {object} tooltipCtl - Tooltip
* @param {HTMLElement} chartElement -
* @param {Event} event -
* @param {boolean} enlargeElement -
* @returns {{x: number, y: number}} - x y
*/
function calculateTooltipPosition(chart, isCarousel, tooltipCtl, chartElement, event) {
// 辅助函数: 根据不同图表类型计算 Tooltip 的y位置
const getTooltipY = () => {
const top = Number(chartElement.getBoundingClientRect().top)
if (isColumn(chart.type)) {
return top + chartElement.getBoundingClientRect().height / 2
}
if (isMix(chart.type) || isPie(chart.type)) {
return top + tooltipCtl.point.y
}
return top + tooltipCtl.point.y + 60
}
if (isCarousel) {
return {
x: tooltipCtl.point.x + Number(chartElement.getBoundingClientRect().left),
y: getTooltipY()
}
} else {
return { x: event.clientX, y: event.clientY }
}
}
export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>(
chart: Chart,
plot: P
) {
const { tooltip } = parseJson(chart.customAttr)
if (!tooltip.show) {
ChartCarouselTooltip.destroyByContainer(chart.container)
return
}
// 图表容器,用于计算 tooltip 的位置
// 获取图表元素,优先顺序:放大 > 预览 > 公共连接页面 > 默认
const chartElement =
document.getElementById('container-viewDialog-' + chart.id + '-common') ||
document.getElementById('container-preview-' + chart.id + '-common') ||
document.getElementById('enlarge-inner-content-' + chart.id) ||
document.getElementById('shape-id-' + chart.id)
// 是否是放大弹窗
const enlargeElement = chartElement?.id.includes('viewDialog')
// 轮播时tooltip的zIndex
const carousel_zIndex = enlargeElement ? '9999' : '1002'
configCarouselTooltip(plot, chart)
// 鼠标可移入, 移入之后保持显示, 移出之后隐藏
plot.options.tooltip.container.addEventListener('mouseenter', e => {
e.target.style.visibility = 'visible'
@ -1415,10 +1784,25 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
if (!tooltipCtl) {
return
}
// 处理 tooltip 与下拉菜单的显示冲突问题
const viewTrackBarElement = document.getElementById('view-track-bar-' + chart.id)
const event = plot.chart.interactions.tooltip?.context?.event
// 是否时轮播模式
const isCarousel =
chart.customAttr?.tooltip?.carousel &&
(!event || // 事件触发时使用event的client坐标
['plot:leave', 'plot:mouseleave'].includes(event?.type) || //鼠标离开时使用tooltipCtl.point
['pie', 'pie-rose', 'pie-donut'].includes(chart.type)) // 饼图时使用tooltipCtl.point
plot.options.tooltip.showMarkers = isCarousel ? true : false
const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER)
wrapperDom.style.zIndex = isCarousel && wrapperDom ? carousel_zIndex : '9999'
if (tooltipCtl.tooltip) {
// 处理视图放大后再关闭 tooltip 的 dom 被清除
const container = tooltipCtl.tooltip.cfg.container
// 当下拉菜单不显示时移除tooltip的hidden-tooltip样式
if (viewTrackBarElement?.getAttribute('aria-expanded') === 'false') {
container.classList.toggle('hidden-tooltip', false)
}
container.style.display = 'block'
const dom = document.getElementById(container.id)
if (!dom) {
@ -1433,8 +1817,17 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
}
plot.chart.getOptions().tooltip.follow = false
tooltipCtl.title = Math.random().toString()
plot.chart.getTheme().components.tooltip.x = event.clientX
plot.chart.getTheme().components.tooltip.y = event.clientY
// 当显示提示为事件触发时使用event的client坐标否则使用tooltipCtl.point 数据点的位置,在图表中,需要加上图表在绘制区的位置
const { x, y } = calculateTooltipPosition(
chart,
isCarousel,
tooltipCtl,
chartElement,
event,
enlargeElement
)
plot.chart.getTheme().components.tooltip.x = x
plot.chart.getTheme().components.tooltip.y = y
})
// https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#hideTooltip
plot.on('plot:leave', () => {
@ -1457,14 +1850,22 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
if (!tooltipCtl) {
return
}
const container = tooltipCtl.tooltip.cfg.container
const container = tooltipCtl.tooltip?.cfg.container
for (const ele of wrapperDom.children) {
if (container.id !== ele.id) {
if (!container || container.id !== ele.id) {
ele.style.display = 'none'
}
}
}
})
plot.on('tooltip:hidden', () => {
const tooltipCtl = plot.chart.getController('tooltip')
if (!tooltipCtl) {
return
}
const container = tooltipCtl.tooltip?.cfg.container
container && (container.style.display = 'none')
})
}
export const TOOLTIP_TPL =
@ -1699,10 +2100,12 @@ export function configYaxisTitleLengthLimit(chart, plot) {
? wrappedTitle.slice(0, wrappedTitle.length - 2) + '...'
: wrappedTitle + '...'
}
// 更新Y轴标题的原始文本和截断后的文本
ev.view.options.axes.yAxisExt.title.originalText = yAxis.name
ev.view.options.axes.yAxisExt.title.text = wrappedTitle
const { title } = ev.view.options.axes.yAxisExt
if (title) {
title.originalText = yAxis.name
title.text = wrappedTitle
}
})
}
@ -1731,7 +2134,7 @@ export const addConditionsStyleColorToData = (chart: Chart, options) => {
})
} else if (item.quotaList?.length) {
const quotaList = item.quotaList.map(q => q.id) ?? []
quotaList.forEach((q, index) => {
quotaList.forEach(q => {
// 定义后,在 handleConditionsStyle 函数中使用
let currentValue = item[valueField]
if (chart.type === 'progress-bar') {
@ -1798,7 +2201,7 @@ const getColorByConditions = (quotaList: [], values: number | number[], chart) =
* @param chart
* @param options
*/
export function handleConditionsStyle(chart: Chart, options: O) {
export function handleConditionsStyle(chart: Chart, options) {
const { threshold } = parseJson(chart.senior)
if (!threshold.enable) return options
const { basicStyle } = parseJson(chart.customAttr)
@ -1810,8 +2213,6 @@ export function handleConditionsStyle(chart: Chart, options: O) {
// 辅助函数配置柱条样式颜色条形图为barStyle,柱形图为columnStyle
const columnStyle = data => {
return {
...options.columnStyle,
...options.barStyle,
...(data[colorField]?.[0] ? { fill: data[colorField][0] } : {})
}
}
@ -1825,8 +2226,8 @@ export function handleConditionsStyle(chart: Chart, options: O) {
const tmpOption = {
...options,
rawFields,
columnStyle: columnStyle,
barStyle: columnStyle,
...configRoundAngle(chart, 'columnStyle', columnStyle),
...configRoundAngle(chart, 'barStyle', columnStyle),
tooltip: {
...options.tooltip,
...(options.tooltip['customItems']
@ -1934,7 +2335,7 @@ export const getTooltipItemConditionColor = item => {
* @param newData
* @param container
*/
export const configEmptyDataStyle = (newChart, newData, container) => {
export const configEmptyDataStyle = (newData, container, newChart?, content?) => {
/**
* dom
*/
@ -1949,15 +2350,121 @@ export const configEmptyDataStyle = (newChart, newData, container) => {
if (!newData.length) {
const emptyDom = document.createElement('div')
emptyDom.id = container + '_empty'
emptyDom.textContent = tI18n('data_set.no_data')
emptyDom.textContent = content || tI18n('data_set.no_data')
emptyDom.setAttribute(
'style',
`position: absolute;
left: 45%;
top: 50%;`
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: darkgray;
textAlign: center;`
)
const parent = document.getElementById(container)
parent.insertBefore(emptyDom, parent.firstChild)
newChart.destroy()
newChart?.destroy()
}
}
export const numberToChineseUnderHundred = (num: number): string => {
// 合法性检查
if (num <= 0 || num > 99 || !Number.isInteger(num)) {
throw new Error('请输入1-99之间的整数')
}
const digits = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']
// 处理个位数
if (num < 10) return digits[num]
const tens = Math.floor(num / 10)
const ones = num % 10
// 处理整十
if (ones === 0) {
return tens === 1 ? '十' : digits[tens] + '十'
}
// 处理其他两位数
return tens === 1 ? '十' + digits[ones] : digits[tens] + '十' + digits[ones]
}
/**
*
* @param styleName
* @param callBack
*/
export const configRoundAngle = (chart: Chart, styleName: string, callBack?: (datum) => {}) => {
const { basicStyle } = parseJson(chart.customAttr)
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
const radius = Array(2).fill(basicStyle.columnBarRightAngleRadius)
const topRadius = [0, 0, ...radius]
const bottomRadius = [...radius, 0, 0]
const finalRadius = [...radius, ...radius]
if (chart.type.includes('-stack')) {
return {
[styleName]: datum => {
if (!datum.value) return { radius: [], ...(callBack ? callBack(datum) : {}) }
return { radius: finalRadius, ...(callBack ? callBack(datum) : {}) }
}
}
}
const isTopRound = basicStyle.radiusColumnBar === 'topRoundAngle'
// 对称条形图
if (chart.type === 'bidirectional-bar') {
const valueField = basicStyle.layout === 'vertical' ? 'valueExt' : 'value'
return {
[styleName]: datum => ({
radius: datum[valueField] && isTopRound ? topRadius : isTopRound ? radius : finalRadius,
...(callBack ? callBack(datum) : {})
})
}
}
// 进度条
if (chart.type === 'progress-bar') {
return {
[styleName]: datum => {
return {
radius: isTopRound ? bottomRadius : finalRadius,
...(callBack ? callBack(datum) : {})
}
}
}
}
// 区间条形图
if (chart.type === 'bar-range') {
return {
[styleName]: datum => {
return {
radius:
datum?.values[0] < datum?.values[1]
? isTopRound
? bottomRadius
: finalRadius
: isTopRound
? topRadius
: finalRadius,
...(callBack ? callBack(datum) : {})
}
}
}
}
// 配置柱条样式
const style = datum => {
if (isTopRound) {
return { radius, ...(callBack ? callBack(datum) : {}) }
}
if (!isTopRound) {
return { radius: finalRadius, ...(callBack ? callBack(datum) : {}) }
}
}
return {
[styleName]: style
}
}
return {
[styleName]: datum => {
return { ...(callBack ? callBack(datum) : {}) }
}
}
}

View File

@ -5,8 +5,10 @@ import {
isAlphaColor,
isTransparent,
parseJson,
resetRgbOpacity
} from '../../util'
resetRgbOpacity,
safeDecimalSum,
safeDecimalMean
} from '../..//util'
import {
DEFAULT_BASIC_STYLE,
DEFAULT_TABLE_CELL,
@ -43,13 +45,31 @@ import {
updateShapeAttr,
ViewMeta
} from '@antv/s2'
import { cloneDeep, filter, find, intersection, keys, merge, repeat } from 'lodash-es'
import {
cloneDeep,
filter,
find,
intersection,
keys,
map,
maxBy,
meanBy,
merge,
minBy,
repeat,
sumBy,
size,
sum
} from 'lodash-es'
import { createVNode, render } from 'vue'
import TableTooltip from '@/data-visualization/chart/components/editor/common/TableTooltip.vue'
import Exceljs from 'exceljs'
import { saveAs } from 'file-saver'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import Decimal from 'decimal.js'
const { t: i18nt } = useI18n()
export function getCustomTheme(chart: Chart): S2Theme {
@ -401,8 +421,7 @@ export function getCustomTheme(chart: Chart): S2Theme {
},
dataCell: {
cell: {
crossBackgroundColor:
enableTableCrossBG && !tableCell.mergeCells ? tableItemSubBgColor : tableItemBgColor,
crossBackgroundColor: enableTableCrossBG ? tableItemSubBgColor : tableItemBgColor,
backgroundColor: tableItemBgColor
},
bolderText: {
@ -504,8 +523,8 @@ export function getStyle(chart: Chart, dataConfig: S2DataConfig): Style {
item => item.id === chart.drillFilters[0].fieldId
)
const drillEnterField = xAxis[drillEnterFieldIndex]
fieldMap[curDrillField.dataeaseName] = {
width: fieldMap[drillEnterField.dataeaseName]?.width
fieldMap[curDrillField.gisbiName] = {
width: fieldMap[drillEnterField.gisbiName]?.width
}
}
// 铺满
@ -579,7 +598,7 @@ export function getCurrentField(valueFieldList: Axis[], field: ChartViewField) {
if (list) {
for (let i = 0; i < list.length; i++) {
const f = list[i]
if (field.dataeaseName === f.dataeaseName) {
if (field.gisbiName === f.gisbiName) {
res = f
break
}
@ -600,10 +619,10 @@ export function getConditions(chart: Chart) {
}
const conditions = threshold.tableThreshold ?? []
const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.dataeaseName)
const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.gisbiName)
if (conditions?.length > 0) {
const { tableCell, basicStyle, tableHeader } = parseJson(chart.customAttr)
// 合并单元格时,班马纹失效
// 合并单元格时马纹失效
const enableTableCrossBG =
chart.type === 'table-info'
? tableCell.enableTableCrossBG && !tableCell.mergeCells
@ -626,12 +645,12 @@ export function getConditions(chart: Chart) {
let defaultValueColor = valueColor
let defaultBgColor = valueBgColor
// 透视表表头颜色配置
if (chart.type === 'table-pivot' && dimFields.includes(field.field.dataeaseName)) {
if (chart.type === 'table-pivot' && dimFields.includes(field.field.gisbiName)) {
defaultValueColor = headerValueColor
defaultBgColor = headerValueBgColor
}
res.text.push({
field: field.field.dataeaseName,
field: field.field.gisbiName,
mapping(value, rowData) {
// 总计小计
if (rowData?.isTotals) {
@ -647,7 +666,7 @@ export function getConditions(chart: Chart) {
}
})
res.background.push({
field: field.field.dataeaseName,
field: field.field.gisbiName,
mapping(value, rowData) {
if (rowData?.isTotals) {
return null
@ -783,6 +802,9 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
}
} else {
// time
if (!tv || !value) {
break
}
const fc = field.conditions[i]
tv = new Date(tv.replace(/-/g, '/') + ' GMT+8').getTime()
const v = new Date(value.replace(/-/g, '/') + ' GMT+8').getTime()
@ -842,7 +864,7 @@ function getFieldValueMap(view) {
function getValue(field, filedValueMap, rowData) {
if (field.summary === 'value') {
return rowData ? rowData[field.field?.dataeaseName] : undefined
return rowData ? rowData[field.field?.gisbiName] : undefined
} else {
return filedValueMap[field.summary + '-' + field.fieldId]
}
@ -880,6 +902,7 @@ export function handleTableEmptyStrategy(chart: Chart) {
}
return newData
}
export class SortTooltip extends BaseTooltip {
show(showOptions) {
const { iconName } = showOptions
@ -934,6 +957,7 @@ export class SortTooltip extends BaseTooltip {
})
}
}
const SORT_DEFAULT =
'<svg t="1711681787276" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4355" width="200" height="200"><path d="M922.345786 372.183628l-39.393195 38.687114L676.138314 211.079416l0 683.909301-54.713113 0L621.425202 129.010259l53.320393 0L922.345786 372.183628zM349.254406 894.989741 101.654214 651.815349l39.393195-38.687114 206.814276 199.792349L347.861686 129.010259l54.713113 0 0 765.978459L349.254406 894.988718z" fill="{fill}" p-id="4356"></path></svg>'
const SORT_UP =
@ -1063,7 +1087,14 @@ export function copyContent(s2Instance: SpreadSheet, event, fieldMeta) {
if (cells.length === 1) {
const curCell = cells[0]
if (cell.getMeta().id === curCell.id) {
copyString(cellMeta.value + '', true)
const cellMeta = cell.getMeta()
const value = cellMeta.data?.[cellMeta.valueField]
const metaObj = find(fieldMeta, m => m.field === cellMeta.valueField)
let fieldVal = value?.toString()
if (metaObj) {
fieldVal = metaObj.formatter(value)
}
copyString(fieldVal, true)
}
s2Instance.interaction.clearState()
return
@ -1189,7 +1220,7 @@ export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) {
const { meta, fields } = instance.dataCfg
const rowLength = fields?.rows?.length || 0
const colLength = fields?.columns?.length || 0
const colNums = layoutResult.colLeafNodes.length + rowLength + 1
const colNums = layoutResult.colLeafNodes.length + rowLength
if (colNums > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
@ -1346,9 +1377,180 @@ export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) {
if (fieldValue === 0 || fieldValue) {
const meta = metaMap[dataCellMeta.valueField]
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, rowLength + colIndex + 1)
const value = meta?.formatter?.(fieldValue) || fieldValue.toString()
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = value
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
const buffer = await workbook.xlsx.writeBuffer()
const dataBlob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'
})
saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`)
}
export async function exportRowQuotaGridPivot(instance: PivotSheet, chart: ChartObj) {
const { layoutResult } = instance.facet
const { meta, fields } = instance.dataCfg
const rowLength = fields?.rows?.length || 0
const colLength = fields?.columns?.length || 0
const colNums = layoutResult.colLeafNodes.length + rowLength
if (colNums > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
const workbook = new Exceljs.Workbook()
const worksheet = workbook.addWorksheet(i18nt('chart.chart_data'))
const metaMap: Record<string, Meta> = meta?.reduce((p, n) => {
if (n.field) {
p[n.field] = n
}
return p
}, {})
// 角头
if (colLength > 1) {
fields.columns.forEach((column: string, index) => {
if (index >= colLength - 1) {
return
}
const cell = worksheet.getCell(index + 1, 1)
cell.value = metaMap[column]?.name ?? column
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
worksheet.mergeCells(index + 1, 1, index + 1, rowLength + 1)
})
}
fields?.rows?.forEach((row, index) => {
const cell = worksheet.getCell(colLength === 0 ? 1 : colLength, index + 1)
cell.value = metaMap[row]?.name ?? row
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = { bottom: { style: 'thick', color: { argb: '00000000' } } }
})
const quotaColLabel = chart.customAttr.basicStyle.quotaColLabel ?? t('dataset.value')
const quotaColHeadCell = worksheet.getCell(colLength === 0 ? 1 : colLength, rowLength + 1)
quotaColHeadCell.value = quotaColLabel
quotaColHeadCell.alignment = { vertical: 'middle', horizontal: 'center' }
quotaColHeadCell.border = {
bottom: { style: 'thick', color: { argb: '00000000' } },
right: { style: 'thick', color: { argb: '00000000' } }
}
// 行头
const { rowLeafNodes, rowNodes } = layoutResult
const notLeafNodeHeightMap: Record<string, number> = {}
rowLeafNodes.forEach(node => {
// 行头的高度由子节点相加决定,也就是行头子节点中包含的叶子节点数量
let curNode = node.parent
while (curNode) {
const height = notLeafNodeHeightMap[curNode.id] ?? 0
notLeafNodeHeightMap[curNode.id] = height + 1
curNode = curNode.parent
}
const { rowIndex } = node
const writeRowIndex = rowIndex + 2 + (colLength === 0 ? 1 : colLength - 1)
const writeColIndex = node.level + 1
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
let value = node.label
if (node.field === '$$extra$$' && metaMap[value]?.name) {
value = metaMap[value].name
}
cell.value = value
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
})
const getNodeStartRowIndex = (node: Node) => {
if (!node.children?.length) {
return node.rowIndex + 1
} else {
return getNodeStartRowIndex(node.children[0])
}
}
rowNodes?.forEach(node => {
if (node.isLeaf) {
return
}
const rowIndex = getNodeStartRowIndex(node)
const height = notLeafNodeHeightMap[node.id]
const writeRowIndex = rowIndex + 1 + (colLength === 0 ? 1 : colLength - 1)
const mergeColCount = node.children[0].level - node.level
const cell = worksheet.getCell(writeRowIndex, node.level + 1)
cell.value = node.label
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (mergeColCount > 1 || height > 1) {
worksheet.mergeCells(
writeRowIndex,
node.level + 1,
writeRowIndex + height - 1,
node.level + mergeColCount
)
}
})
// 列头
const { colLeafNodes, colNodes, colsHierarchy } = layoutResult
const maxColHeight = colsHierarchy.maxLevel + 1
const notLeafNodeWidthMap: Record<string, number> = {}
colLeafNodes.forEach(node => {
// 列头的宽度由子节点相加决定,也就是列头子节点中包含的叶子节点数量
let curNode = node.parent
while (curNode) {
const width = notLeafNodeWidthMap[curNode.id] ?? 0
notLeafNodeWidthMap[curNode.id] = width + 1
curNode = curNode.parent
}
const { colIndex } = node
const writeRowIndex = node.level + 1
const writeColIndex = colIndex + rowLength + 2
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
const value = node.label
cell.value = value
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (writeRowIndex < maxColHeight) {
worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex)
}
cell.border = {
bottom: { style: 'thick', color: { argb: '00000000' } }
}
})
const getNodeStartColIndex = (node: Node) => {
if (!node.children?.length) {
return node.colIndex + 1
} else {
return getNodeStartColIndex(node.children[0])
}
}
colNodes.forEach(node => {
if (node.isLeaf) {
return
}
const colIndex = getNodeStartColIndex(node)
const width = notLeafNodeWidthMap[node.id]
const writeRowIndex = node.level + 1
const value = node.label
const writeColIndex = colIndex + rowLength + 1
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = value
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (width > 1) {
worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, writeColIndex + width - 1)
}
})
// 单元格数据
for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) {
for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) {
const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex)
const { fieldValue } = dataCellMeta
if (fieldValue === 0 || fieldValue) {
const meta = metaMap[dataCellMeta.valueField]
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, rowLength + colIndex + 2)
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
@ -1361,7 +1563,7 @@ export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) {
export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
const layoutResult = instance.facet.layoutResult
if (layoutResult.colLeafNodes.length + 2 > 16384) {
if (layoutResult.colLeafNodes.length + 1 > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
@ -1468,9 +1670,9 @@ export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
if (fieldValue === 0 || fieldValue) {
const meta = metaMap[dataCellMeta.valueField]
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, colIndex + 1 + 1)
const value = meta?.formatter?.(fieldValue) || fieldValue.toString()
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = value
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
@ -1480,6 +1682,135 @@ export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) {
})
saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`)
}
export async function exportRowQuotaTreePivot(instance: PivotSheet, chart: ChartObj) {
const layoutResult = instance.facet.layoutResult
if (layoutResult.colLeafNodes.length + 1 > 16384) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed'))
return
}
const { meta, fields } = instance.dataCfg
const colLength = fields?.columns?.length || 0
const workbook = new Exceljs.Workbook()
const worksheet = workbook.addWorksheet(i18nt('chart.chart_data'))
const metaMap: Record<string, Meta> = meta?.reduce((p, n) => {
if (n.field) {
p[n.field] = n
}
return p
}, {})
// 角头
fields.columns?.forEach((column, index) => {
if (index >= fields.columns.length - 1) {
return
}
const cell = worksheet.getCell(index + 1, 1)
cell.value = metaMap[column]?.name ?? column
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
})
const quotaColLabel = chart.customAttr.basicStyle.quotaColLabel ?? t('dataset.value')
const maxColHeight = layoutResult.colsHierarchy.maxLevel + 1
const rowName = fields?.rows
?.map(row => metaMap[row]?.name ?? row)
.concat(quotaColLabel)
.join('/')
const cell = worksheet.getCell(colLength, 1)
cell.value = rowName
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } },
bottom: { style: 'thick', color: { argb: '00000000' } }
}
//行头
const { rowLeafNodes } = layoutResult
rowLeafNodes.forEach((node, index) => {
const cell = worksheet.getCell(maxColHeight + index + 1, 1)
let value = node.label
if (node.field === '$$extra$$' && metaMap[value]?.name) {
value = metaMap[value].name
}
cell.value = repeat(' ', node.level) + value
cell.alignment = { vertical: 'middle', horizontal: 'left' }
cell.border = {
right: { style: 'thick', color: { argb: '00000000' } }
}
})
// 列头
const notLeafNodeWidthMap: Record<string, number> = {}
const { colLeafNodes } = layoutResult
colLeafNodes.forEach(node => {
let curNode = node.parent
while (curNode) {
const width = notLeafNodeWidthMap[curNode.id] ?? 0
notLeafNodeWidthMap[curNode.id] = width + 1
curNode = curNode.parent
}
const { colIndex } = node
const writeRowIndex = node.level + 1
const writeColIndex = colIndex + 2
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = node.label
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (writeRowIndex < maxColHeight) {
worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex)
}
cell.border = {
bottom: { style: 'thick', color: { argb: '00000000' } }
}
})
const colNodes = layoutResult.colNodes
const getNodeStartIndex = (node: Node) => {
if (!node.children?.length) {
return node.colIndex + 1
} else {
return getNodeStartIndex(node.children[0])
}
}
colNodes.forEach(node => {
if (node.isLeaf) {
return
}
const colIndex = getNodeStartIndex(node)
const width = notLeafNodeWidthMap[node.id]
const writeRowIndex = node.level + 1
const writeColIndex = colIndex + 1
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
cell.value = node.label
cell.alignment = { vertical: 'middle', horizontal: 'center' }
if (width > 1) {
worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, writeColIndex + width - 1)
}
})
// 单元格数据
for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) {
for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) {
const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex)
const { fieldValue } = dataCellMeta
if (fieldValue === 0 || fieldValue) {
const meta = metaMap[dataCellMeta.valueField]
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, colIndex + 2)
const value = meta?.formatter?.(fieldValue) || fieldValue
cell.alignment = { vertical: 'middle', horizontal: 'center' }
cell.value = isNumeric(value) ? parseFloat(value) : value
}
}
}
const buffer = await workbook.xlsx.writeBuffer()
const dataBlob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'
})
saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`)
}
function isNumeric(value: string): boolean {
return /^[+-]?\d+(\.\d+)?$/.test(value)
}
export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) {
const { fields } = instance.dataCfg
const rowLength = fields?.rows?.length || 0
@ -1488,10 +1819,19 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) {
ElMessage.warning(i18nt('chart.pivot_export_invalid_field'))
return
}
const { quotaPosition } = chart.customAttr.basicStyle
if (chart.customAttr.basicStyle.tableLayoutMode !== 'tree') {
exportGridPivot(instance, chart)
if (quotaPosition === 'row') {
exportRowQuotaGridPivot(instance, chart)
} else {
exportGridPivot(instance, chart)
}
} else {
exportTreePivot(instance, chart)
if (quotaPosition === 'row') {
exportRowQuotaTreePivot(instance, chart)
} else {
exportTreePivot(instance, chart)
}
}
}
@ -1504,7 +1844,7 @@ export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S
const fields = chart.data.fields || []
const fieldsMap =
fields.reduce((p, n) => {
p[n.dataeaseName] = n
p[n.gisbiName] = n
return p
}, {}) || {}
const quotaIndex = dataConfig.meta.findIndex(m => fieldsMap[m.field]?.groupType === 'q')
@ -1571,6 +1911,7 @@ export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S
if (showIndex && meta.colIndex === 0) {
meta.fieldValue = getRowIndex(mergedCellsInfo, meta)
}
meta.deFieldType = fieldsMap[meta.valueField]?.deType
return new CustomMergedCell(sheet, cells, meta)
}
}
@ -1598,12 +1939,13 @@ export function getRowIndex(mergedCellsInfo: MergedCellInfo[][], meta: ViewMeta)
}, 0)
return curRangeStartIndex - lostCells + 1
}
class CustomMergedCell extends MergedCell {
protected drawBackgroundShape() {
const allPoints = getPolygonPoints(this.cells)
// 处理条件样式,这里没有用透明度
// 因为合并的单元格是单独的图层,透明度降低的话会显示底下未合并的单元格,需要单独处理被覆盖的单元格
const { backgroundColor: fill, backgroundColorOpacity: fillOpacity } = this.getBackgroundColor()
const { backgroundColor: fill } = this.getBackgroundColor()
const cellTheme = this.theme.dataCell.cell
this.backgroundShape = renderPolygon(this, {
points: allPoints,
@ -1612,6 +1954,14 @@ class CustomMergedCell extends MergedCell {
lineHeight: cellTheme.horizontalBorderWidth
})
}
drawTextShape(): void {
if (this.meta.deFieldType === 7) {
drawImage.apply(this)
} else {
super.drawTextShape()
}
}
}
export class CustomDataCell extends TableDataCell {
@ -1847,96 +2197,95 @@ const getWrapTextHeight = (wrapText, textStyle, spreadsheet, maxLines) => {
return Math.min(lines, maxLines) * maxHeight
}
/**
*
* @param chart
* @param s2Options
* @param newData
* @param tableHeader
* @param basicStyle
* @param showSummary
*/
export const configSummaryRow = (
chart,
s2Options,
newData,
tableHeader,
basicStyle,
showSummary
) => {
if (!showSummary || !newData.length) return
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[newData.length] = tableHeader.tableTitleHeight
s2Options.style.rowCfg = { heightByField }
// 计算汇总加入到数据里,冻结最后一行
s2Options.frozenTrailingRowCount = 1
const yAxis = chart.yAxis
const xAxis = chart.xAxis
const summaryObj = newData.reduce(
(p, n) => {
if (chart.type === 'table-info') {
xAxis
.filter(axis => [2, 3, 4].includes(axis.deType))
.forEach(axis => {
p[axis.dataeaseName] =
(parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0)
// 导出获取汇总行的函数
export function getSummaryRow(data, axis, sumCon = []) {
const summaryObj = { SUMMARY: true }
for (let i = 0; i < axis.length; i++) {
const a = axis[i].gisbiName
let savedAxis = find(sumCon, s => s.field === a)
if (savedAxis) {
if (savedAxis.summary == undefined) {
savedAxis.summary = 'sum' // 默认汇总方式为求和
}
if (savedAxis.show == undefined) {
savedAxis.show = true // 默认显示汇总结果
}
} else {
savedAxis = {
field: a,
summary: 'sum',
show: true
}
}
// 如果配置为不显示,则跳过该字段
if (!savedAxis.show) {
continue
}
// 根据汇总方式处理数据
switch (savedAxis.summary) {
case 'sum':
// 计算字段的总和
summaryObj[a] = safeDecimalSum(data, a)
break
case 'avg':
// 计算字段的平均值
summaryObj[a] = safeDecimalMean(data, a)
break
case 'max':
// 计算字段的最大值
summaryObj[a] = maxBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a]) // 提取数值
)[a]
break
case 'min':
// 计算字段的最小值
summaryObj[a] = minBy(
filter(data, d => parseFloat(d[a]) !== undefined),
d => parseFloat(d[a]) // 提取数值
)[a]
break
case 'var_pop':
// 计算总体方差需要至少2个数据点
if (data.length < 2) {
continue
} else {
const mean = safeDecimalMean(data, a) // 计算平均值
// 计算每个数据点与平均值的差的平方
const squaredDeviations = map(data, d => {
const value = new Decimal(d[a] ?? 0) // 获取字段值如果不存在则使用0
const dev = value.minus(mean) // 计算差值
return dev.times(dev) // 计算平方
})
} else {
yAxis.forEach(axis => {
p[axis.dataeaseName] =
(parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0)
})
}
return p
},
{ SUMMARY: true }
)
newData.push(summaryObj)
s2Options.dataCell = viewMeta => {
// 配置文本自动换行参数
viewMeta.autoWrap = basicStyle.autoWrap
viewMeta.maxLines = basicStyle.maxLines
if (viewMeta.rowIndex !== newData.length - 1) {
return new CustomDataCell(viewMeta, viewMeta.spreadsheet)
}
if (viewMeta.colIndex === 0) {
if (tableHeader.showIndex) {
viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show')
} else {
if (xAxis.length) {
viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show')
// 计算方差(平方差的平均值)
const variance = squaredDeviations.reduce((acc, val) => acc.plus(val), new Decimal(0))
summaryObj[a] = variance.dividedBy(data.length - 1).toNumber() // 计算总体方差
}
}
break
case 'stddev_pop':
// 计算总体标准差需要至少2个数据点
if (data.length < 2) {
continue
} else {
const mean = safeDecimalMean(data, a) // 计算平均值
// 计算每个数据点与平均值的差的平方
const squaredDeviations = map(data, d => {
const value = new Decimal(d[a] ?? 0) // 获取字段值如果不存在则使用0
const dev = value.minus(mean) // 计算差值
return dev.times(dev) // 计算平方
})
// 计算方差(平方差的平均值)
const variance = squaredDeviations.reduce((acc, val) => acc.plus(val), new Decimal(0))
summaryObj[a] = variance.dividedBy(data.length - 1).sqrt().toNumber() // 计算总体标准差
}
break
}
return new SummaryCell(viewMeta, viewMeta.spreadsheet)
}
// 返回汇总结果对象
return summaryObj
}
/**
* ,
* @param newChart
* @param newData
* @param tableCell
* @param tableHeader
* @param showSummary
*/
export const summaryRowStyle = (newChart, newData, tableCell, tableHeader, showSummary) => {
if (!showSummary || !newData.length) return
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时,减少一个表头的高度
const headerAndSummaryHeight = showHeader ? 2 : 1
const totalHeight =
tableHeader.tableTitleHeight * headerAndSummaryHeight +
tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.options.height) {
// 6 是阴影高度
newChart.options.height =
totalHeight < newChart.options.height - 6 ? totalHeight + 6 : totalHeight
}
})
}
export class SummaryCell extends CustomDataCell {
getTextStyle() {
@ -1944,6 +2293,7 @@ export class SummaryCell extends CustomDataCell {
textStyle.textAlign = this.theme.dataCell.text.textAlign
return textStyle
}
getBackgroundColor() {
const { backgroundColor, backgroundColorOpacity } = this.theme.colCell.cell
return { backgroundColor, backgroundColorOpacity }
@ -2019,3 +2369,27 @@ export const getColumns = (fields, cols: Array<ColumnNode>) => {
}
return result
}
export function drawImage() {
const img = new Image()
const { x, y, width, height, fieldValue } = this.meta
img.src = fieldValue as string
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => {
!this.cfg.children && (this.cfg.children = [])
const { width: imgWidth, height: imgHeight } = img
const ratio = Math.max(imgWidth / width, imgHeight / height)
// 不铺满,部分留白
const imgShowWidth = (imgWidth / ratio) * 0.8
const imgShowHeight = (imgHeight / ratio) * 0.8
this.textShape = this.addShape('image', {
attrs: {
x: x + (imgShowWidth < width ? (width - imgShowWidth) / 2 : 0),
y: y + (imgShowHeight < height ? (height - imgShowHeight) / 2 : 0),
width: imgShowWidth,
height: imgShowHeight,
img
}
})
}
}

View File

@ -170,7 +170,7 @@ export abstract class G2PlotChartView<
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return setupSeriesColor(chart, data)
}
// eslint-disable-next-line
public setupSubSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return undefined
}
@ -191,8 +191,8 @@ export abstract class G2PlotChartView<
return addConditionsStyleColorToData(chart, data)
}
protected configEmptyDataStyle(newChart, newData: any[], container: string) {
configEmptyDataStyle(newChart, newData, container)
protected configEmptyDataStyle(newData, container, newChart?, content?) {
configEmptyDataStyle(newData, container, newChart, content)
}
/**

View File

@ -107,8 +107,8 @@ export abstract class L7ChartView<
return options
}
protected configZoomButton(chart: Chart, plot: S) {
configL7Zoom(chart, plot)
protected configZoomButton(chart: Chart, plot: S, mapKey?: any) {
configL7Zoom(chart, plot, mapKey)
}
protected configLabel(chart: Chart, options: O): O {

View File

@ -136,18 +136,18 @@ export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractCha
if (duration > 300) {
return
}
const canvasPosition = canvas.getBoundingClientRect()
const touchPosition = [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
const relativePosition = [
touchPosition[0] - canvasPosition.x,
touchPosition[1] - canvasPosition.y
]
const shape = s2Instance.container.getShape(relativePosition[0], relativePosition[1])
// 图片单元格,表头排序图标点击放大图片
if (shape.cfg?.type === 'image') {
return
}
const callback = () => {
const canvasPosition = canvas.getBoundingClientRect()
const touchPosition = [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
const relativePosition = [
touchPosition[0] - canvasPosition.x,
touchPosition[1] - canvasPosition.y
]
const shape = s2Instance.container.getShape(relativePosition[0], relativePosition[1])
// 图片单元格点击放大图片
if (shape.cfg?.parent.constructor.name === 'ImageCell') {
return
}
e.preventDefault()
e.stopPropagation()
if (shape) {

View File

@ -1,4 +1,4 @@
import { isEmpty, isNumber } from 'lodash-es'
import { isNumber } from 'lodash-es'
import { DEFAULT_TITLE_STYLE } from '../editor/util/chart'
import { equalsAny, includesAny } from '../editor/util/StringUtils'
import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types'
@ -12,6 +12,8 @@ import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { useLinkStoreWithOut } from '@/data-visualization/store/modules/link'
import { useAppStoreWithOut } from '@/data-visualization/store/modules/app'
import { Decimal } from 'decimal.js'
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
@ -283,17 +285,23 @@ export function handleEmptyDataStrategy<O extends PickOptions>(chart: Chart, opt
}
return options
}
const { yAxis, xAxisExt, extStack } = chart
const { yAxis, xAxisExt, extStack, extBubble } = chart
const multiDimension = yAxis?.length >= 2 || xAxisExt?.length > 0 || extStack?.length > 0
switch (strategy) {
case 'breakLine': {
if (multiDimension) {
// 多维度保持空
if (isChartMix) {
for (let i = 0; i < data.length; i++) {
handleBreakLineMultiDimension(data[i] as Record<string, any>[])
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleBreakLineMultiDimension(data[0] as Record<string, any>[])
}
} else {
}
if (data[1]) {
if (extBubble?.length > 0) {
handleBreakLineMultiDimension(data[1] as Record<string, any>[])
}
}
} else {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
}
@ -303,22 +311,27 @@ export function handleEmptyDataStrategy<O extends PickOptions>(chart: Chart, opt
}
}
case 'setZero': {
if (multiDimension) {
// 多维度置0
if (isChartMix) {
for (let i = 0; i < data.length; i++) {
handleSetZeroMultiDimension(data[i] as Record<string, any>[])
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleSetZeroMultiDimension(data[0] as Record<string, any>[])
} else {
handleSetZeroSingleDimension(data[0] as Record<string, any>[])
}
}
if (data[1]) {
if (extBubble?.length > 0) {
handleSetZeroMultiDimension(data[1] as Record<string, any>[], true)
} else {
handleSetZeroSingleDimension(data[1] as Record<string, any>[], true)
}
} else {
handleSetZeroMultiDimension(data)
}
} else {
// 单维度置0
if (isChartMix) {
for (let i = 0; i < data.length; i++) {
handleSetZeroSingleDimension(data[i] as Record<string, any>[])
}
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
}
@ -364,7 +377,7 @@ function handleBreakLineMultiDimension(data) {
})
}
function handleSetZeroMultiDimension(data: Record<string, any>[]) {
function handleSetZeroMultiDimension(data: Record<string, any>[], isExt = false) {
const dimensionInfoMap = new Map()
const subDimensionSet = new Set()
const quotaMap = new Map<string, { id: string }[]>()
@ -372,6 +385,9 @@ function handleSetZeroMultiDimension(data: Record<string, any>[]) {
const item = data[i]
if (item.value === null) {
item.value = 0
if (isExt) {
item.valueExt = 0
}
}
const dimensionInfo = dimensionInfoMap.get(item.field)
if (dimensionInfo) {
@ -388,12 +404,17 @@ function handleSetZeroMultiDimension(data: Record<string, any>[]) {
let subInsertIndex = 0
subDimensionSet.forEach(dimension => {
if (!dimensionInfo.set.has(dimension)) {
data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, {
const _temp = {
field,
value: 0,
category: dimension,
quotaList: quotaMap.get(dimension as string)
})
} as any
if (isExt) {
_temp.valueExt = 0
}
data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, _temp)
}
subInsertIndex++
})
@ -402,10 +423,14 @@ function handleSetZeroMultiDimension(data: Record<string, any>[]) {
})
}
function handleSetZeroSingleDimension(data: Record<string, any>[]) {
function handleSetZeroSingleDimension(data: Record<string, any>[], isExt = false) {
data.forEach(item => {
if (item.value === null) {
item.value = 0
if (!isExt) {
item.value = 0
} else {
item.valueExt = 0
}
}
})
}
@ -489,7 +514,7 @@ const getExcelDownloadRequest = (data, type?) => {
const tableRow = JSON.parse(JSON.stringify(data.tableRow))
const excelHeader = fields.map(item => item.chartShowName ?? item.name)
const excelTypes = fields.map(item => item.deType)
const excelHeaderKeys = fields.map(item => item.dataeaseName)
const excelHeaderKeys = fields.map(item => item.gisbiName)
let excelData = tableRow.map(item => excelHeaderKeys.map(i => item[i]))
let detailFields = []
if (data.detailFields?.length) {
@ -497,7 +522,7 @@ const getExcelDownloadRequest = (data, type?) => {
return {
name: item.name,
deType: item.deType,
dataeaseName: item.dataeaseName
gisbiName: item.gisbiName
}
})
excelData = tableRow.map(item => {
@ -505,7 +530,7 @@ const getExcelDownloadRequest = (data, type?) => {
if (i === 'detail' && !item[i] && Array.isArray(item['details'])) {
const arr = item['details']
if (arr?.length) {
return arr.map(ele => detailFields.map(field => ele[field.dataeaseName]))
return arr.map(ele => detailFields.map(field => ele[field.gisbiName]))
}
return null
}
@ -522,8 +547,20 @@ const getExcelDownloadRequest = (data, type?) => {
}
}
export const exportExcelDownload = (chart, callBack?) => {
const excelName = chart.title
function getChartExcelTitle(preFix, viewTitle) {
const now = new Date()
const pad = n => n.toString().padStart(2, '0')
const year = now.getFullYear()
const month = pad(now.getMonth() + 1) // 月份从 0 开始
const day = pad(now.getDate())
const hour = pad(now.getHours())
const minute = pad(now.getMinutes())
const second = pad(now.getSeconds())
return `${preFix}_${viewTitle}_${year}${month}${day}_${hour}${minute}${second}`
}
export const exportExcelDownload = (chart, preFix, callBack?) => {
const excelName = getChartExcelTitle(preFix, chart.title)
let request: any = {
proxy: null,
dvId: chart.sceneId,
@ -586,18 +623,21 @@ export const exportExcelDownload = (chart, callBack?) => {
}
export const copyString = (content: string, notify = false) => {
const clipboard = navigator.clipboard || {
writeText: data => {
return new Promise(resolve => {
const textareaDom = document.createElement('textarea')
textareaDom.setAttribute('style', 'z-index: -1;position: fixed;opacity: 0;')
textareaDom.value = data
document.body.appendChild(textareaDom)
textareaDom.select()
document.execCommand('copy')
textareaDom.remove()
resolve()
})
let clipboard = navigator.clipboard as Pick<Clipboard, 'writeText'>
if (!clipboard || window.top !== window.self) {
clipboard = {
writeText: data => {
return new Promise<void>(resolve => {
const textareaDom = document.createElement('textarea')
textareaDom.setAttribute('style', 'z-index: -1;position: fixed;opacity: 0;')
textareaDom.value = data
document.body.appendChild(textareaDom)
textareaDom.select()
document.execCommand('copy')
textareaDom.remove()
resolve()
})
}
}
}
clipboard.writeText(content).then(() => {
@ -779,7 +819,7 @@ export function getColor(chart: Chart) {
}
}
export function setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
export function setupSeriesColor(chart: ChartObj): ChartBasicStyle['seriesColor'] {
const result: ChartBasicStyle['seriesColor'] = []
const seriesSet = new Set<string>()
const colors = chart.customAttr.basicStyle.colors
@ -1152,8 +1192,10 @@ export function getLineLabelColorByCondition(conditions, value, fieldId) {
if (fieldConditions.length) {
fieldConditions.some(item => {
if (
(item.term === 'lt' && value <= item.value) ||
(item.term === 'gt' && value >= item.value) ||
(item.term === 'lt' && value < item.value) ||
(item.term === 'le' && value <= item.value) ||
(item.term === 'gt' && value > item.value) ||
(item.term === 'ge' && value >= item.value) ||
(item.term === 'between' && value >= item.min && value <= item.max)
) {
color = item.color
@ -1207,3 +1249,27 @@ export const hexToRgba = (hex, alpha = 1) => {
// 返回 RGBA 格式
return `rgba(${r}, ${g}, ${b}, ${a})`
}
// 安全计算数值字段的总和,使用 Decimal 避免浮点数精度问题
export function safeDecimalSum(data, field) {
// 使用 reduce 累加所有行的指定字段值
return data
.reduce((acc, row) => {
// 将字段值转换为 Decimal 类型并累加到累加器
return acc.plus(new Decimal(row[field] ?? 0))
}, new Decimal(0))
.toNumber() // 最终结果转换为普通数字返回
}
// 安全计算数值字段的平均值,使用 Decimal 避免浮点数精度问题
export function safeDecimalMean(data, field) {
// 如果数据为空,直接返回 0
if (!data.length) return 0
// 计算所有行的指定字段值的总和
const sum = data.reduce((acc, row) => {
// 将字段值转换为 Decimal 类型并累加到累加器
return acc.plus(new Decimal(row[field] ?? 0))
}, new Decimal(0))
// 将总和除以数据行数,得到平均值,并转换为普通数字返回
return sum.dividedBy(data.length).toNumber()
}

View File

@ -28,7 +28,8 @@ import { isDashboard, trackBarStyleCheck } from '@/data-visualization/utils/canv
import { useEmitt } from '@/data-visualization/hooks/web/useEmitt'
import { L7ChartView } from '@/data-visualization/chart/components/js/panel/types/impl/l7'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { ExportImage,Scale } from '@antv/l7'
import { ExportImage, Scale, Fullscreen, Control, Scene, TileLayer } from '@antv/l7'
import { GaodeMap } from '@antv/l7-maps';
const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack, inMobile } =
@ -111,7 +112,8 @@ const state = reactive({
},
linkageActiveParam: null,
pointParam: null,
data: { fields: [] } //
data: { fields: [] }, //
satelliteVisible: false, //
})
let chartData = shallowRef<Partial<Chart['data']>>({
fields: []
@ -333,35 +335,100 @@ const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any
let mapL7Timer: number
let scaleControl: Scale | null = null //
let fullscreenControl
let satelliteControlInstance = null; //
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
mapL7Timer && clearTimeout(mapL7Timer)
mapL7Timer && clearTimeout(mapL7Timer);
mapL7Timer = setTimeout(async () => {
myChart = await chartView.drawChart({
chartObj: myChart,
container: containerId,
chart: chart,
action
})
});
//
if (scaleControl) {
myChart.getScene()?.removeControl(scaleControl)
scaleControl = null
if (!scaleControl) {
scaleControl = new Scale({
position: 'bottomleft',
imperial: false
});
myChart.getScene()?.addControl(scaleControl);
}
//
scaleControl = new Scale({
position: 'bottomleft',
imperial: false
})
myChart.getScene()?.addControl(scaleControl)
myChart?.render()
callback?.()
emit('resetLoading')
}, 500)
}
//
if (fullscreenControl) {
} else {
fullscreenControl = new Fullscreen({
position: 'bottomright',
});
myChart.getScene()?.addControl(fullscreenControl, 'bottomright');
}
// ====== 使API ======
let satelliteLayer: any = null;
let isSatelliteVisible = false;
class SatelliteControl extends Control {
protected onAdd() {
const btn = document.createElement('button');
btn.className = 'l7-control-button l7-satellite-control';
btn.innerHTML = '卫星';
// btn.title = '';
btn.style.backgroundColor = '#000';
btn.style.color = '#fff';
btn.style.padding = '2px';
btn.style.borderRadius = '4px';
btn.style.cursor = 'pointer'
btn.style.fontSize = '11px';
const scene = myChart.getScene()
//
scene.on('loaded', () => {
//
satelliteLayer = new window.AMap.TileLayer.Satellite();
btn.onclick = () => {
isSatelliteVisible = !isSatelliteVisible;
if (isSatelliteVisible) {
// 使 scene.addLayer
btn.style.backgroundColor = '#409eff';
scene.map.add(satelliteLayer)
} else {
// 使 scene.removeLayer
btn.style.backgroundColor = '#000';
scene.map.remove(satelliteLayer)
}
};
});
return btn;
}
}
//
//
if (satelliteControlInstance) {
} else {
//
satelliteControlInstance = new SatelliteControl({ position: 'bottomright' });
myChart.getScene()?.addControl(satelliteControlInstance);
}
// ====== ======
myChart?.render();
callback?.();
emit('resetLoading');
}, 500);
};
const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') {
trackClick('pointClick')
@ -706,15 +773,8 @@ onBeforeUnmount(() => {
<template>
<div class="canvas-area">
<view-track-bar
ref="viewTrack"
:track-menu="trackMenu"
:font-family="fontFamily"
:is-data-v-mobile="dataVMobile"
class="track-bar"
:style="state.trackBarStyle"
@trackClick="trackClick"
/>
<view-track-bar ref="viewTrack" :track-menu="trackMenu" :font-family="fontFamily" :is-data-v-mobile="dataVMobile"
class="track-bar" :style="state.trackBarStyle" @trackClick="trackClick" />
<div v-if="!isError" ref="chartContainer" class="canvas-content" :id="containerId"></div>
<chart-error v-else :err-msg="errMsg" />
</div>
@ -726,12 +786,32 @@ onBeforeUnmount(() => {
width: 100%;
height: 100%;
z-index: 0;
.canvas-content {
width: 100% !important;
height: 100% !important;
:deep(.g2-tooltip) {
position: fixed !important;
}
}
}
</style>
:deep(.l7-button-control) {
background-color: #000000 !important;
}
:deep(.l7-button-control:not(:disabled):hover) {
background-color: #000000d5 !important;
}
:deep(.l7-button-control .l7-iconfont) {
fill: #fff !important;
color: #fff !important;
}
// :deep(.l7-control-container .l7-top) {
// top: auto !important;
// bottom: 133px !important;
// }</style>

View File

@ -16,7 +16,6 @@ import {
} from 'vue'
import { getData } from '@/api/data-visualization/chart'
import chartViewManager from '@/data-visualization/chart/components/js/panel'
import { useAppStoreWithOut } from '@/data-visualization/store/modules/app'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
import ViewTrackBar from '@/data-visualization/components/visualization/ViewTrackBar.vue'
import { storeToRefs } from 'pinia'
@ -125,6 +124,7 @@ const state = reactive({
imgEnlarge: false,
imgSrc: ''
})
const PAGE_CHARTS = ['table-info', 'table-normal']
//
let chartData = shallowRef<Partial<Chart['data']>>({
fields: []
@ -133,19 +133,20 @@ let chartData = shallowRef<Partial<Chart['data']>>({
const containerId = 'container-' + showPosition.value + '-' + view.value.id + '-' + suffixId.value
const viewTrack = ref(null)
const calcData = (view: Chart, callback, resetPageInfo = true) => {
if (view.customAttr.basicStyle.tablePageStyle === 'general') {
const calcData = (viewInfo: Chart, callback, resetPageInfo = true) => {
if (viewInfo.customAttr.basicStyle.tablePageStyle === 'general') {
if (state.currentPageSize !== 0) {
view.chartExtRequest.pageSize = state.currentPageSize
viewInfo.chartExtRequest.pageSize = state.currentPageSize
state.pageInfo.pageSize = state.currentPageSize
} else {
viewInfo.chartExtRequest.pageSize = state.pageInfo.pageSize
}
} else {
if(view.chartExtRequest != undefined){
delete view.chartExtRequest.pageSize
}
delete viewInfo.chartExtRequest?.pageSize
}
if (view.tableId || view['dataFrom'] === 'template') {
if (viewInfo.tableId || viewInfo['dataFrom'] === 'template') {
isError.value = false
const v = JSON.parse(JSON.stringify(view))
const v = JSON.parse(JSON.stringify(viewInfo))
getData(v)
.then(res => {
if (res.code && res.code !== 0) {
@ -154,7 +155,7 @@ const calcData = (view: Chart, callback, resetPageInfo = true) => {
} else {
chartData.value = res?.data as Partial<Chart['data']>
state.totalItems = res?.totalItems
dvMainStore.setViewDataDetails(view.id, res)
dvMainStore.setViewDataDetails(viewInfo.id, res)
emit('onDrillFilters', res?.drillFilters)
renderChart(res as unknown as Chart, resetPageInfo)
}
@ -225,7 +226,7 @@ const renderChart = (viewInfo: Chart, resetPageInfo: boolean) => {
nextTick(() => debounceRender(resetPageInfo))
}
const debounceRender = debounce(resetPageInfo => {
const debounceRender = debounce(() => {
myChart?.facet?.timer?.stop()
myChart?.facet?.cancelScrollFrame()
myChart?.destroy()
@ -250,19 +251,13 @@ const debounceRender = debounce(resetPageInfo => {
const setupPage = (chart: ChartObj, resetPageInfo?: boolean) => {
const customAttr = chart.customAttr
if (chart.type !== 'table-info' || customAttr.basicStyle.tablePageMode !== 'page') {
if (!PAGE_CHARTS.includes(chart.type) || customAttr.basicStyle.tablePageMode !== 'page') {
state.showPage = false
return
}
const pageInfo = state.pageInfo
state.pageStyle = customAttr.basicStyle.tablePageStyle
if (state.pageStyle === 'general') {
if (state.currentPageSize === 0) {
state.currentPageSize = pageInfo.pageSize
} else {
pageInfo.pageSize = state.currentPageSize
}
} else {
if (state.pageStyle !== 'general') {
pageInfo.pageSize = customAttr.basicStyle.tablePageSize ?? 20
}
if (state.totalItems > state.pageInfo.pageSize || state.pageStyle === 'general') {
@ -274,6 +269,7 @@ const setupPage = (chart: ChartObj, resetPageInfo?: boolean) => {
if (resetPageInfo) {
state.pageInfo.currentPage = 1
}
dvMainStore.setViewPageInfo(chart.id, state.pageInfo)
}
const mouseMove = () => {
@ -295,7 +291,8 @@ const initScroll = () => {
myChart &&
senior?.scrollCfg?.open &&
chartData.value.tableRow?.length &&
(view.value.type === 'table-normal' || (view.value.type === 'table-info' && !state.showPage))
PAGE_CHARTS.includes(props.view.type) &&
!state.showPage
) {
//
myChart.facet.timer?.stop()
@ -339,7 +336,7 @@ const initScroll = () => {
}
const showPage = computed(() => {
if (view.value.type !== 'table-info') {
if (!PAGE_CHARTS.includes(view.value.type)) {
return false
}
return state.showPage
@ -357,6 +354,7 @@ const handleCurrentChange = pageNum => {
const handlePageSizeChange = pageSize => {
if (state.pageStyle === 'general') {
state.currentPageSize = pageSize
emitter.emit('set-page-size', pageSize)
}
let extReq = { pageSize: pageSize }
if (chartExtRequest.value) {
@ -403,10 +401,9 @@ const action = param => {
state.trackBarStyle.top = barStyleTemp.top + 'px'
}
viewTrack.value.trackButtonClick()
viewTrack.value.trackButtonClick(view.value.id)
}
}
const appStore = useAppStoreWithOut()
const trackClick = trackAction => {
const param = state.pointParam
@ -683,12 +680,6 @@ const autoStyle = computed(() => {
}
})
const autoHeightStyle = computed(() => {
return {
height: 20 * scale.value + 8 + 'px'
}
})
const tabStyle = computed(() => [
{ '--de-pager-color': canvasStyleData.value.component.seniorStyleSetting?.pagerColor }
])
@ -745,7 +736,7 @@ const tablePageClass = computed(() => {
v-else
class="table-page-content"
layout="prev, pager, next, sizes, jumper"
v-model:page-size="state.currentPageSize"
v-model:page-size="state.pageInfo.pageSize"
v-model:current-page="state.pageInfo.currentPage"
:pager-count="5"
:total="state.pageInfo.total"

View File

@ -4,7 +4,7 @@ import { ref } from 'vue'
const { t } = useI18n()
const props = defineProps({
defineProps({
errMsg: {
type: String,
required: true,

View File

@ -1,7 +1,6 @@
<script lang="tsx" setup>
import { computed, reactive, watch } from 'vue'
import { computed } from 'vue'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { reverseColor } from '../util/util'
import { ArrowRight } from '@element-plus/icons-vue'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
const dvMainStore = dvMainStoreWithOut()
@ -27,51 +26,16 @@ const props = defineProps({
const emit = defineEmits(['onDrillJump'])
const state = reactive({
textColor: '#bbbfc4'
})
const textColor = computed(
() => dvMainStore.canvasStyleData.component.seniorStyleSetting.drillLayerColor
)
// watch(
// [() => props.themeStyle?.backgroundColorSelect, () => props.themeStyle?.color],
// () => {
// loadThemeStyle()
// },
// { deep: true }
// )
const drillJump = index => {
if (index < props.drillFilters.length) {
emit('onDrillJump', index)
}
}
const loadThemeStyle = () => {
let themeStyle = null
if (props.themeStyle) {
themeStyle = JSON.parse(JSON.stringify(props.themeStyle))
if (themeStyle && themeStyle.commonBackground) {
const viewBGColor = themeStyle.commonBackground.color
if (viewBGColor !== '#FFFFFF') {
const reverseValue = reverseColor(viewBGColor)
state.textColor = reverseValue
} else {
state.textColor = null
}
}
if (themeStyle && themeStyle.backgroundColorSelect) {
const panelColor = themeStyle.color
if (panelColor !== '#FFFFFF') {
const reverseValue = reverseColor(panelColor)
state.textColor = reverseValue
} else {
state.textColor = null
}
}
}
}
const drillPathVar = computed(() => [{ '--drill-color': textColor.value }])
</script>

View File

@ -0,0 +1,16 @@
<script lang="tsx" setup></script>
<template>
<div class="scroll-shadow-content">tet</div>
</template>
<style lang="less" scoped>
.scroll-shadow-content {
z-index: 1;
position: absolute;
height: 100%;
width: 100%;
background-color: #ece7e7;
opacity: 0.5;
}
</style>

View File

@ -33,7 +33,7 @@ import {
} from '@/data-visualization/chart/components/editor/util/chart'
import DrillPath from '@/data-visualization/chart/components/views/components/DrillPath.vue'
import { ElIcon, ElInput, ElMessage } from 'element-plus-secondary'
// import { useFilter } from '@/data-visualization/hooks/web/useFilter'
import { useFilter } from '@/data-visualization/hooks/web/useFilter'
import { useCache } from '@/data-visualization/hooks/web/useCache'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
@ -54,6 +54,8 @@ import request from '@/data-visualization/config/axios'
import { store } from '@/data-visualization/store'
import { clearExtremum } from '@/data-visualization/chart/components/js/extremumUitl'
import DePreviewPopDialog from '@/data-visualization/components/visualization/DePreviewPopDialog.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const { wsCache } = useCache()
const chartComponent = ref<any>()
const { t } = useI18n()
@ -61,12 +63,13 @@ const dvMainStore = dvMainStoreWithOut()
const { emitter } = useEmitt()
const dePreviewPopDialogRef = ref(null)
let innerRefreshTimer = null
let innerSearchCount = 0
const appStore = useAppStoreWithOut()
const appearanceStore = useAppearanceStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const emit = defineEmits(['onPointClick'])
const emit = defineEmits(['onPointClick', 'onComponentEvent'])
const {
nowPanelJumpInfo,
@ -76,11 +79,15 @@ const {
canvasStyleData,
mobileInPc,
inMobile,
editMode,
hiddenListStatus
editMode
} = storeToRefs(dvMainStore)
const props = defineProps({
//
commonParams: {
type: Object,
required: false
},
active: {
type: Boolean,
default: false
@ -234,6 +241,7 @@ const buildInnerRefreshTimer = (
innerRefreshTimer = setInterval(() => {
clearViewLinkage()
queryData()
innerSearchCount++
}, timerRefreshTime)
}
}
@ -244,14 +252,6 @@ const clearViewLinkage = () => {
useEmitt().emitter.emit('clearPanelLinkage', { viewId: element.value.id })
}
watch(
[() => view.value],
() => {
initTitle()
},
{ deep: true }
)
watch([() => scale.value], () => {
initTitle()
})
@ -374,17 +374,29 @@ const chartClick = param => {
//
const filter = (firstLoad?: boolean) => {
// const { filter } = useFilter(view.value.id, firstLoad)
// return {
// user: wsCache.get('user.uid'),
// filter,
// linkageFilters: element.value.linkageFilters,
// outerParamsFilters: element.value.outerParamsFilters,
// webParamsFilters: element.value.webParamsFilters,
// drill: state.drillClickDimensionList,
// resultCount: resultCount.value,
// resultMode: resultMode.value
// }
const { filter } = useFilter(view.value.id, firstLoad)
const result = {
user: wsCache.get('user.uid'),
filter,
linkageFilters: element.value.linkageFilters,
outerParamsFilters: element.value.outerParamsFilters,
webParamsFilters: element.value.webParamsFilters,
drill: state.drillClickDimensionList,
resultCount: resultCount.value,
resultMode: resultMode.value
}
//
if (route.path === '/preview' && route.query.taskId) {
const sceneId = view.value['sceneId']
const filterJson = window[`de-report-filter-${sceneId}`]
let filterObj = {}
if (filterJson) {
filterObj = JSON.parse(filterJson)
}
filterObj[view.value.id] = result
window[`de-report-filter-${sceneId}`] = JSON.stringify(filterObj)
}
return result
}
const onDrillFilters = param => {
@ -457,9 +469,16 @@ const jumpClick = param => {
if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl
}
const jumpInfoParam = `&jumpInfoParam=${encodeURIComponent(
Base64.encode(JSON.stringify(param))
)}`
//
if (jumpInfo.linkType === 'inner') {
if (jumpInfo.targetDvId) {
const editPreviewParams = ['canvas', 'edit-preview'].includes(showPosition.value)
? '&editPreview=true'
: ''
const filterOuterParams = {}
const curFilter = dvMainStore.getLastViewRequestInfo(param.viewId)
const targetViewInfoList = jumpInfo.targetViewInfoList
@ -492,13 +511,11 @@ const jumpClick = param => {
if (publicLinkStatus.value) {
// ID
if (jumpInfo.publicJumpId) {
let url = `${embeddedBaseUrl}#/de-link/${
jumpInfo.publicJumpId
}?fromLink=true&jumpInfoParam=${encodeURIComponent(
Base64.encode(JSON.stringify(param))
)}`
let url = `${embeddedBaseUrl}#/de-link/${jumpInfo.publicJumpId}?fromLink=true&dvType=${jumpInfo.targetDvType}`
if (attachParamsInfo) {
url = url + attachParamsInfo
url = url + attachParamsInfo + jumpInfoParam + editPreviewParams
} else {
url = url + '&ignoreParams=true' + jumpInfoParam + editPreviewParams
}
const currentUrl = window.location.href
localStorage.setItem('beforeJumpUrl', currentUrl)
@ -507,11 +524,11 @@ const jumpClick = param => {
ElMessage.warning(t('visualization.public_link_tips'))
}
} else {
let url = `${embeddedBaseUrl}#/preview?dvId=${
jumpInfo.targetDvId
}&fromLink=true&jumpInfoParam=${encodeURIComponent(Base64.encode(JSON.stringify(param)))}`
let url = `${embeddedBaseUrl}#/preview?dvId=${jumpInfo.targetDvId}&fromLink=true&dvType=${jumpInfo.targetDvType}`
if (attachParamsInfo) {
url = url + attachParamsInfo
url = url + attachParamsInfo + jumpInfoParam + editPreviewParams
} else {
url = url + '&ignoreParams=true' + jumpInfoParam + editPreviewParams
}
const currentUrl = window.location.href
localStorage.setItem('beforeJumpUrl', currentUrl)
@ -567,13 +584,13 @@ const calcData = params => {
methodName: 'calcData',
args: [
params,
res => {
() => {
loading.value = false
}
]
})
} else {
chartComponent?.value?.calcData?.(params, res => {
chartComponent?.value?.calcData?.(params, () => {
loading.value = false
})
}
@ -691,10 +708,19 @@ const changeChartType = () => {
const changeDataset = () => {
checkFieldIsAllowEmpty()
}
const loadPlugin = ref(false)
onMounted(() => {
if (!view.value.isPlugin) {
state.drillClickDimensionList = view.value?.chartExtRequest?.drill ?? []
queryData(!showPosition.value.includes('viewDialog'))
} else {
const searched = dvMainStore.firstLoadMap.includes(element.value.id)
const queryFilter = filter(!searched)
view.value['chartExtRequest'] = queryFilter
chartExtRequest.value = queryFilter
loadPlugin.value = true
}
if (!listenerEnable.value) {
return
@ -829,7 +855,11 @@ onMounted(() => {
// 1. 2.searchCount =0 3.
const loadingFlag = computed(() => {
return (canvasStyleData.value.refreshViewLoading || searchCount.value === 0) && loading.value
return (
(canvasStyleData.value.refreshViewLoading ||
(searchCount.value === 0 && innerSearchCount === 0)) &&
loading.value
)
})
const chartAreaShow = computed(() => {
@ -903,7 +933,7 @@ function onTitleChange() {
}
const toolTip = computed(() => {
return props.themes === 'dark' ? 'ndark' : 'dark'
return props.themes === 'dark' ? 'light' : 'dark'
})
const marginBottom = computed<string | 0>(() => {
@ -1033,6 +1063,14 @@ const titleTooltipWidth = computed(() => {
}
return '500px'
})
const clearG2Tooltip = () => {
const g2TooltipWrapper = document.getElementById('g2-tooltip-wrapper')
if (g2TooltipWrapper) {
for (const ele of g2TooltipWrapper.children) {
ele.style.display = 'none'
}
}
}
</script>
<template>
@ -1123,7 +1161,7 @@ const titleTooltipWidth = computed(() => {
<!--这里去渲染不同图库的图表-->
<div v-if="allEmptyCheck || (chartAreaShow && !showEmpty)" style="flex: 1; overflow: hidden">
<plugin-component
v-if="view.plugin?.isPlugin"
v-if="view.plugin?.isPlugin && loadPlugin"
:jsname="view.plugin.staticMap['index']"
:scale="scale"
:dynamic-area-id="dynamicAreaId"
@ -1170,9 +1208,17 @@ const titleTooltipWidth = computed(() => {
:themes="canvasStyleData.dashboard.themeColor"
ref="chartComponent"
:view="view"
:element="element"
:show-position="showPosition"
:suffixId="suffixId"
:font-family="fontFamily"
:common-params="commonParams"
@touchstart="clearG2Tooltip"
@onChartClick="chartClick"
@onPointClick="onPointClick"
@onDrillFilters="onDrillFilters"
@onJumpClick="jumpClick"
@onComponentEvent="() => emit('onComponentEvent')"
/>
<chart-component-g2-plot
:scale="scale"
@ -1182,6 +1228,7 @@ const titleTooltipWidth = computed(() => {
:element="element"
:suffixId="suffixId"
:font-family="fontFamily"
:active="active"
v-else-if="
showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT, ChartLibraryType.L7)
"
@ -1212,6 +1259,7 @@ const titleTooltipWidth = computed(() => {
v-if="(!chartAreaShow || showEmpty) && !allEmptyCheck"
:themes="canvasStyleData.dashboard.themeColor"
:view-icon="view.type"
@touchstart="clearG2Tooltip"
></chart-empty-info>
<drill-path
:disabled="optType === 'enlarge'"
@ -1240,6 +1288,7 @@ const titleTooltipWidth = computed(() => {
overflow: hidden;
}
.title-container {
position: relative;
margin: 0;
width: 100%;

View File

@ -59,7 +59,6 @@ const contextmenuStore = contextmenuStoreWithOut()
const { curComponent, dvInfo, editMode, tabMoveOutComponentId, canvasState, mainScrollTop } =
storeToRefs(dvMainStore)
const { editorMap, areaData, isCtrlOrCmdDown } = storeToRefs(composeStore)
const emits = defineEmits(['scrollCanvasAdjust'])
const props = defineProps({
themes: {
type: String,
@ -265,7 +264,7 @@ watch(
watch(
() => areaData.value.components.length,
(val, oldVal) => {
() => {
groupAreaClickChange()
}
)
@ -280,7 +279,7 @@ const initWatermark = (waterDomId = 'editor-canvas-main') => {
try {
if (
dvInfo.value.watermarkInfo &&
dvInfo.value.watermarkInfo.settingContent &&
dvInfo.value.watermarkInfo?.settingContent &&
isMainCanvas(canvasId.value)
) {
activeWatermarkCheckUser(waterDomId, canvasId.value, curScale.value)
@ -938,7 +937,7 @@ function removeItem(index) {
dvMainStore.removeLinkageInfo(item['id'])
if (isMainCanvas(canvasId.value)) {
// componentData
dvMainStore.deleteComponentById(item.id)
dvMainStore.deleteComponentById(item.id, undefined, false)
} else {
componentData.value.splice(index, 1)
}

View File

@ -1,19 +1,27 @@
<script setup lang="ts">
import { getStyle } from '@/data-visualization/utils/style'
import eventBus from '@/data-visualization/utils/eventBus'
import { ref, onMounted, toRefs, getCurrentInstance, computed, nextTick } from 'vue'
import { ref, toRefs, computed, nextTick } from 'vue'
import findComponent from '@/data-visualization/utils/components'
import { downloadCanvas2, imgUrlTrans } from '@/data-visualization/utils/imgUtils'
import { useEmitt } from '@/data-visualization/hooks/web/useEmitt'
import Board from '@/data-visualization/components/de-board/Board.vue'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
import { activeWatermarkCheckUser, removeActiveWatermark } from '@/data-visualization/components/watermark/watermark'
import { isMobile } from '@/data-visualization/utils/utils'
import { isDashboard } from '@/data-visualization/utils/canvasUtils'
import { isDashboard, isMainCanvas } from '@/data-visualization/utils/canvasUtils'
import { XpackComponent } from '@/data-visualization/components/plugin'
import DePreviewPopDialog from '@/data-visualization/components/visualization/DePreviewPopDialog.vue'
import Icon from '../../icon-custom/src/Icon.vue'
import replaceOutlined from '@/assets/svg/icon_replace_outlined.svg'
const componentWrapperInnerRef = ref(null)
const componentEditBarRef = ref(null)
const dvMainStore = dvMainStoreWithOut()
const downLoading = ref(false)
const commonFilterAttrs = ['width', 'height', 'top', 'left', 'rotate']
const dePreviewPopDialogRef = ref(null)
const commonFilterAttrsFilterBorder = [
'width',
'height',
@ -113,6 +121,11 @@ const props = defineProps({
optType: {
type: String,
required: false
},
//
scrollMain: {
type: Number,
default: 0
}
})
const {
@ -124,23 +137,25 @@ const {
dvInfo,
searchCount,
scale,
suffixId
suffixId,
scrollMain
} = toRefs(props)
let currentInstance
const component = ref(null)
const emits = defineEmits(['userViewEnlargeOpen', 'datasetParamsInit', 'onPointClick'])
const wrapperId = 'wrapper-outer-id-' + config.value.id
const viewDemoInnerId = computed(() => 'enlarge-inner-content-' + config.value.id)
const htmlToImage = () => {
useEmitt().emitter.emit('l7-prepare-picture', config.value.id)
downLoading.value = true
setTimeout(() => {
const vueDom = componentWrapperInnerRef.value
const vueDom = document.getElementById(viewDemoInnerId.value)
activeWatermarkCheckUser(viewDemoInnerId.value, 'canvas-main', scale.value / 100)
downloadCanvas2('img', vueDom, '图表', () => {
// do callback
removeActiveWatermark(viewDemoInnerId.value)
downLoading.value = false
useEmitt().emitter.emit('l7-unprepare-picture', config.value.id)
})
}, 1000)
}
@ -149,20 +164,22 @@ const handleInnerMouseDown = e => {
// do setCurComponent
if (showPosition.value.includes('multiplexing')) {
componentEditBarRef.value.multiplexingCheckOut()
e.stopPropagation()
e.preventDefault()
e?.stopPropagation()
e?.preventDefault()
}
if (showPosition.value.includes('popEdit') || dvMainStore.mobileInPc) {
if (
(!['rich-text'].includes(config.value.innerType) &&
['popEdit', 'preview'].includes(showPosition.value)) ||
dvMainStore.mobileInPc
) {
onClick(e)
if (e.target?.className?.includes('ed-input__inner')) return
e?.stopPropagation()
e?.preventDefault()
}
}
onMounted(() => {
currentInstance = getCurrentInstance()
const methodName = 'componentImageDownload-' + config.value.id
})
const onClick = e => {
const onClick = () => {
//
eventBus.emit('componentClick')
dvMainStore.setInEditorStatus(true)
@ -287,11 +304,20 @@ const eventEnable = computed(
['indicator', 'rich-text'].includes(config.value.innerType)) &&
config.value.events &&
config.value.events.checked &&
(isDashboard() || (!isDashboard() && !isMobile()))
(isDashboard() || (!isDashboard() && !isMobile())) &&
showPosition.value !== 'canvas-multiplexing'
)
const onWrapperClickCur = e => {
//
if (['indicator'].includes(config.value.innerType)) {
return
}
onWrapperClick(e)
}
const onWrapperClick = e => {
if (eventEnable.value && showPosition.value !== 'canvas-multiplexing') {
if (eventEnable.value) {
if (config.value.events.type === 'showHidden') {
//
nextTick(() => {
@ -302,15 +328,9 @@ const onWrapperClick = e => {
const jumpType = config.value.events.jump.type
try {
if ('newPop' === jumpType) {
window.open(
url,
'_blank',
'width=800,height=600,left=200,top=100,toolbar=no,scrollbars=yes,resizable=yes,location=no'
)
dePreviewPopDialogRef.value.previewInit({ url, size: 'middle' })
} else if ('_blank' === jumpType) {
console.info('DataEase Component Jump _blank value:' + window['originOpen'])
if (window['originOpen']) {
console.info('DataEase Component originOpen _blank')
window['originOpen'](url, '_blank')
} else {
window.open(url, '_blank')
@ -321,9 +341,15 @@ const onWrapperClick = e => {
} catch (e) {
console.warn('url 格式错误:' + url)
}
} else if (config.value.events.type === 'refreshDataV') {
useEmitt().emitter.emit('componentRefresh')
} else if (config.value.events.type === 'fullScreen') {
useEmitt().emitter.emit('canvasFullscreen')
} else if (config.value.events.type === 'download') {
useEmitt().emitter.emit('canvasDownload')
}
e.preventDefault()
e.stopPropagation()
e?.preventDefault()
e?.stopPropagation()
}
}
@ -339,12 +365,47 @@ const initOpenHandler = newWindow => {
}
const deepScale = computed(() => scale.value / 100)
const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc && props.active))
const freezeFlag = computed(() => {
return (
isMainCanvas(props.canvasId) &&
config.value.freeze &&
scrollMain.value - config.value.style?.top > 0
)
})
const commonParams = computed(() => {
return {
eventEnable: eventEnable.value,
eventType: config.value.events.type
}
})
const showCheck = computed(() => {
return dvMainStore.mobileInPc && showPosition.value === 'edit'
})
const updateFromMobile = (e, type) => {
if (type === 'syncPcDesign') {
e.preventDefault()
e.stopPropagation()
}
useEmitt().emitter.emit('onMobileStatusChange', {
type: type,
value: config.value.id
})
}
</script>
<template>
<div
class="wrapper-outer"
:class="showPosition + '-' + config.component"
:class="[
showPosition + '-' + config.component,
{
'freeze-component': freezeFlag
}
]"
:id="wrapperId"
@mousedown="handleInnerMouseDown"
@mouseenter="onMouseEnter"
@ -352,6 +413,16 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
element-loading-text="导出中..."
element-loading-background="rgba(255, 255, 255, 1)"
>
<div
:title="$t('visualization.sync_pc_design')"
v-if="showCheck"
class="refresh-from-pc"
@click="updateFromMobile($event, 'syncPcDesign')"
>
<el-icon>
<Icon name="icon_replace_outlined"><replaceOutlined class="svg-icon" /></Icon>
</el-icon>
</div>
<component-edit-bar
v-if="!showPosition.includes('canvas') && !props.isSelector"
class="wrapper-edit-bar"
@ -361,6 +432,7 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
:element="config"
:show-position="showPosition"
:class="{ 'wrapper-edit-bar-active': active }"
@componentImageDownload="htmlToImage"
@userViewEnlargeOpen="opt => emits('userViewEnlargeOpen', opt)"
@datasetParamsInit="() => emits('datasetParamsInit')"
></component-edit-bar>
@ -382,7 +454,7 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
class="wrapper-inner-adaptor"
:style="slotStyle"
:class="{ 'pop-wrapper-inner': showActive, 'event-active': eventEnable }"
@mousedown="onWrapperClick"
@mousedown="onWrapperClickCur"
>
<component
:is="findComponent(config['component'])"
@ -406,7 +478,10 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
:is-edit="false"
:suffix-id="suffixId"
:font-family="fontFamily"
:active="active"
:common-params="commonParams"
@onPointClick="onPointClick"
@onComponentEvent="onWrapperClick"
/>
</div>
<!--边框背景-->
@ -426,12 +501,22 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
}
.wrapper-outer {
position: absolute;
.refresh-from-pc {
position: absolute;
right: 38px;
top: 12px;
z-index: 2;
font-size: 16px;
cursor: pointer;
color: var(--ed-color-primary);
}
}
.wrapper-inner {
width: 100%;
height: 100%;
position: relative;
background-size: 100% 100% !important;
padding: 0 !important;
.wrapper-inner-adaptor {
position: relative;
transform-style: preserve-3d;
@ -475,4 +560,11 @@ const showActive = computed(() => props.popActive || (dvMainStore.mobileInPc &&
.event-active {
cursor: pointer;
}
.freeze-component {
position: fixed;
z-index: 1;
top: var(--top-show-offset) px !important;
left: var(--left-show-offset) px !important;
}
</style>

View File

@ -298,64 +298,73 @@ const editQueryCriteria = () => {
<el-divider class="custom-divider" />
<li @click="deleteComponent">{{ t('visualization.delete') }}</li>
</template>
<li
v-show="!(!curComponent || curComponent['isLock'] || curComponent['component'] != 'Group')"
@click="decompose()"
>
{{ t('visualization.cancel_group') }}
</li>
<el-divider class="custom-divider" v-show="composeDivider" />
<template v-if="curComponent">
<template v-if="!curComponent['isLock'] && curComponent.category === 'hidden'">
<li @click="categoryChange('base')">{{ t('visualization.move_to_screen_show') }}</li>
<li @click="editQueryCriteria">{{ t('visualization.edit') }}</li>
<li v-if="activePosition === 'aside'" @click="rename">{{ t('visualization.rename') }}</li>
<li @click="copy">{{ t('visualization.copy') }}</li>
<li @click="paste">{{ t('visualization.paste') }}</li>
<el-divider class="custom-divider" />
<li @click="deleteComponent">{{ t('visualization.delete') }}</li>
<template v-else>
<li
v-show="
!(!curComponent || curComponent['isLock'] || curComponent['component'] != 'Group')
"
@click="decompose()"
>
{{ t('visualization.cancel_group') }}
</li>
<el-divider class="custom-divider" v-show="composeDivider" />
<template v-if="curComponent">
<template v-if="!curComponent['isLock'] && curComponent.category === 'hidden'">
<li @click="categoryChange('base')">{{ t('visualization.move_to_screen_show') }}</li>
<li @click="editQueryCriteria">{{ t('visualization.edit') }}</li>
<li v-if="activePosition === 'aside'" @click="rename">
{{ t('visualization.rename') }}
</li>
<li @click="copy">{{ t('visualization.copy') }}</li>
<li @click="paste">{{ t('visualization.paste') }}</li>
<el-divider class="custom-divider" />
<li @click="deleteComponent">{{ t('visualization.delete') }}</li>
</template>
<template v-if="!curComponent['isLock'] && curComponent.category !== 'hidden'">
<li v-if="curComponent.component === 'VQuery'" @click="editQueryCriteria">
{{ t('visualization.edit') }}
</li>
<li @click="upComponent">{{ t('visualization.up_component') }}</li>
<li @click="downComponent">{{ t('visualization.down_component') }}</li>
<li @click="topComponent">{{ t('visualization.top_component') }}</li>
<li @click="bottomComponent">{{ t('visualization.bottom_component') }}</li>
<li @click="customSort" v-if="curComponent.component === 'DeTabs'">
{{ t('visualization.sort') }}
</li>
<xpack-component
:chart="curComponent"
is-screen
resource-table="snapshot"
jsname="L2NvbXBvbmVudC90aHJlc2hvbGQtd2FybmluZy9FZGl0QmFySGFuZGxlcg=="
/>
<li @click="categoryChange('hidden')" v-show="showMoveMenu">
{{ t('visualization.move_to_pop_area') }}
</li>
<el-divider class="custom-divider" />
<li @click="hide" v-show="curComponent['isShow']">{{ t('visualization.hidden') }}</li>
<li @click="show" v-show="!curComponent['isShow'] || isGroupArea">
{{ t('visualization.cancel_hidden') }}
</li>
<li @click="lock">{{ t('visualization.lock') }}</li>
<li v-if="curComponent['isLock'] || isGroupArea" @click="unlock">
{{ t('visualization.unlock') }}
</li>
<el-divider class="custom-divider" />
<li v-if="activePosition === 'aside'" @click="rename">
{{ t('visualization.rename') }}
</li>
<li @click="copy">{{ t('visualization.copy') }}</li>
<li @click="paste">{{ t('visualization.paste') }}</li>
<li @click="cut">{{ t('visualization.cut') }}</li>
<el-divider class="custom-divider" />
<li @click="deleteComponent">{{ t('visualization.delete') }}</li>
</template>
<li v-if="curComponent['isLock']" @click="unlock">{{ t('visualization.unlock') }}</li>
</template>
<template v-if="!curComponent['isLock'] && curComponent.category !== 'hidden'">
<li v-if="curComponent.component === 'VQuery'" @click="editQueryCriteria">
{{ t('visualization.edit') }}
</li>
<li @click="upComponent">{{ t('visualization.up_component') }}</li>
<li @click="downComponent">{{ t('visualization.down_component') }}</li>
<li @click="topComponent">{{ t('visualization.top_component') }}</li>
<li @click="bottomComponent">{{ t('visualization.bottom_component') }}</li>
<li @click="customSort" v-if="curComponent.component === 'DeTabs'">
{{ t('visualization.sort') }}
</li>
<xpack-component
:chart="curComponent"
is-screen
jsname="L2NvbXBvbmVudC90aHJlc2hvbGQtd2FybmluZy9FZGl0QmFySGFuZGxlcg=="
/>
<li @click="categoryChange('hidden')" v-show="showMoveMenu">
{{ t('visualization.move_to_pop_area') }}
</li>
<el-divider class="custom-divider" />
<li @click="hide" v-show="curComponent['isShow']">{{ t('visualization.hidden') }}</li>
<li @click="show" v-show="!curComponent['isShow'] || isGroupArea">
{{ t('visualization.cancel_hidden') }}
</li>
<li @click="lock">{{ t('visualization.lock') }}</li>
<li v-if="curComponent['isLock'] || isGroupArea" @click="unlock">
{{ t('visualization.unlock') }}
</li>
<el-divider class="custom-divider" />
<li v-if="activePosition === 'aside'" @click="rename">{{ t('visualization.rename') }}</li>
<li @click="copy">{{ t('visualization.copy') }}</li>
<li @click="paste">{{ t('visualization.paste') }}</li>
<li @click="cut">{{ t('visualization.cut') }}</li>
<el-divider class="custom-divider" />
<li @click="deleteComponent">{{ t('visualization.delete') }}</li>
</template>
<li v-if="curComponent['isLock']" @click="unlock">{{ t('visualization.unlock') }}</li>
<li v-else-if="!curComponent && !areaData.components.length" @click="paste">
{{ t('visualization.paste') }}
</li>
</template>
<li v-else-if="!curComponent && !areaData.components.length" @click="paste">
{{ t('visualization.paste') }}
</li>
</ul>
</div>
</template>

View File

@ -1,27 +1,27 @@
<script setup lang="ts">
import { changeStyleWithScale } from '@/data-visualization/utils/translate'
import ComponentWrapper from './ComponentWrapper.vue'
import { getCanvasStyle, getShapeItemStyle } from '@/data-visualization/utils/style'
import ComponentWrapper from './ComponentWrapper.vue'
import { changeStyleWithScale } from '@/data-visualization/utils/translate'
import { computed, nextTick, ref, toRefs, watch, onBeforeUnmount, onMounted, reactive } from 'vue'
import { changeRefComponentsSizeWithScalePoint } from '@/data-visualization/utils/changeComponentsSizeWithScale'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import elementResizeDetectorMaker from 'element-resize-detector'
import UserViewEnlarge from '@/data-visualization/components/visualization/UserViewEnlarge.vue'
import CanvasOptBar from '@/data-visualization/components/visualization/CanvasOptBar.vue'
import { isDashboard, isMainCanvas, refreshOtherComponent } from '@/data-visualization/utils/canvasUtils'
import { activeWatermarkCheckUser } from '@/data-visualization/components/watermark/watermark'
import { XpackComponent } from '@/data-visualization/components/plugin'
import PopArea from '@/data-visualization/custom-component/pop-area/Component.vue'
// import CanvasFilterBtn from '@/data-visualization/custom-component/canvas-filter-btn/Component.vue'
import { useEmitt } from '@/data-visualization/hooks/web/useEmitt'
import DatasetParamsComponent from '@/data-visualization/components/visualization/DatasetParamsComponent.vue'
// import DeFullscreen from '@/components/visualization/common/DeFullscreen.vue'
import DeFullscreen from '@/data-visualization/components/visualization/common/DeFullscreen.vue'
import EmptyBackground from '../../empty-background/src/EmptyBackground.vue'
// import LinkOptBar from '@/data-visualization/components/data-visualization/canvas/LinkOptBar.vue'
import { isDesktop } from '@/data-visualization/utils/ModelUtil'
import { isMobile } from '@/data-visualization/utils/utils'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
const dvMainStore = dvMainStoreWithOut()
const { pcMatrixCount, curComponent, mobileInPc, canvasState, inMobile } = storeToRefs(dvMainStore)
const openHandler = ref(null)
@ -30,6 +30,7 @@ const emits = defineEmits(['onResetLayout'])
const fullScreeRef = ref(null)
const isOverSize = ref(false)
const isDesktopFlag = isDesktop()
const { t } = useI18n()
const props = defineProps({
canvasStyleData: {
type: Object,
@ -93,6 +94,11 @@ const props = defineProps({
type: String,
required: false,
default: 'inherit'
},
//
showLinkageButton: {
type: Boolean,
default: true
}
})
@ -125,7 +131,8 @@ const dashboardActive = computed(() => {
return dvInfo.value.type === 'dashboard'
})
const state = reactive({
initState: true
initState: true,
scrollMain: 0
})
const curSearchCount = computed(() => {
@ -171,6 +178,7 @@ const canvasStyle = computed(() => {
if (canvasStyleData.value?.screenAdaptor === 'keep') {
style['height'] = canvasStyleData.value?.height + 'px'
style['width'] = canvasStyleData.value?.width + 'px'
style['margin'] = 'auto'
} else {
style['height'] = dashboardActive.value
? downloadStatus.value
@ -217,6 +225,30 @@ watch(
}
)
useEmitt({
name: 'tabCanvasChange-' + canvasId.value,
callback: function () {
restore()
}
})
useEmitt({
name: 'componentRefresh',
callback: function () {
if (isMainCanvas(canvasId.value)) {
refreshDataV()
}
}
})
useEmitt({
name: 'canvasFullscreen',
callback: function () {
if (isMainCanvas(canvasId.value)) {
fullScreeRef.value.toggleFullscreen()
}
}
})
const resetLayout = () => {
if (downloadStatus.value) {
@ -446,6 +478,12 @@ const downloadAsPDF = () => {
// test
}
const scrollPreview = () => {
state.scrollMain = previewCanvas.value.scrollTop
}
const showUnpublishFlag = computed(() => dvInfo.value?.status === 0 && isMainCanvas(canvasId.value))
defineExpose({
restore
})
@ -456,13 +494,18 @@ defineExpose({
:id="domId"
class="canvas-container"
:style="canvasStyle"
:class="{ 'de-download-custom': downloadStatus, 'datav-preview': dataVPreview }"
:class="{
'de-download-custom': downloadStatus,
'datav-preview': dataVPreview,
'datav-preview-unpublish': showUnpublishFlag
}"
ref="previewCanvas"
@mousedown="handleMouseDown"
@scroll="scrollPreview"
v-if="state.initState"
>
<!--弹框触发区域-->
<!-- <canvas-filter-btn :is-fixed="isOverSize" v-if="filterBtnShow"></canvas-filter-btn> -->
<canvas-filter-btn :is-fixed="isOverSize" v-if="filterBtnShow"></canvas-filter-btn>
<!-- 弹框区域 -->
<PopArea
v-if="popAreaAvailable"
@ -475,8 +518,15 @@ defineExpose({
:canvas-state="canvasState"
:show-position="'preview'"
></PopArea>
<template v-if="renderReady">
<ComponentWrapper
<canvas-opt-bar
v-if="showLinkageButton"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:component-data="baseComponentData"
:is-fixed="isOverSize"
></canvas-opt-bar>
<template v-if="renderReady && !showUnpublishFlag">
<component-wrapper
v-for="(item, index) in baseComponentData"
v-show="item.isShow"
:active="item.id === (curComponent || {})['id']"
@ -493,16 +543,32 @@ defineExpose({
:scale="mobileInPc && isDashboard() ? 100 : scaleMin"
:is-selector="props.isSelector"
:font-family="canvasStyleData.fontFamily || fontFamily"
:scroll-main="state.scrollMain"
@userViewEnlargeOpen="userViewEnlargeOpen($event, item)"
@datasetParamsInit="datasetParamsInit(item)"
@onPointClick="onPointClick"
:index="index"
/>
</template>
<empty-background
v-if="showUnpublishFlag"
:description="t('visualization.resource_not_published')"
img-type="none"
>
</empty-background>
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>
</div>
<empty-background v-if="!state.initState" description="参数不能为空" img-type="noneWhite" />
<!-- <de-fullscreen ref="fullScreeRef"></de-fullscreen> -->
<de-fullscreen ref="fullScreeRef"></de-fullscreen>
<dataset-params-component ref="customDatasetParamsRef"></dataset-params-component>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
<link-opt-bar
v-if="linkOptBarShow"
ref="link-opt-bar"
:terminal="'pc'"
:canvas-style-data="canvasStyleData"
@link-export-pdf="downloadAsPDF"
/>
</template>
<style lang="less" scoped>
@ -530,4 +596,8 @@ defineExpose({
.datav-preview {
overflow-y: hidden !important;
}
.datav-preview-unpublish {
background-color: inherit !important;
}
</style>

View File

@ -1,7 +1,11 @@
<template>
<div
class="shape"
:class="{ 'shape-group-area': isGroupArea }"
:class="{
'shape-group-area': isGroupArea,
'freeze-component': freezeFlag,
'freeze-component-fullscreen': freezeFlag && fullscreenFlag
}"
ref="shapeInnerRef"
:id="domId"
v-loading="downLoading"
@ -55,6 +59,7 @@
:element="element"
:show-position="showPosition"
:canvas-id="canvasId"
@componentImageDownload="htmlToImage"
@userViewEnlargeOpen="userViewEnlargeOpen"
@datasetParamsInit="datasetParamsInit"
@linkJumpSetOpen="linkJumpSetOpen"
@ -177,7 +182,8 @@ const {
tabMoveOutComponentId,
mobileInPc,
mainScrollTop,
hiddenListStatus
hiddenListStatus,
fullscreenFlag
} = storeToRefs(dvMainStore)
const { editorMap, areaData, isCtrlOrCmdDown } = storeToRefs(composeStore)
const emit = defineEmits([
@ -326,6 +332,14 @@ const initialAngle = {
}
const cursors = ref({})
const freezeFlag = computed(() => {
return (
isMainCanvas(canvasId.value) &&
element.value.freeze &&
mainScrollTop.value - defaultStyle.value.top > 0
)
})
const showCheck = computed(() => {
return mobileInPc.value && element.value.canvasId === 'canvas-main'
})
@ -386,7 +400,7 @@ const getPointList = () => {
}
const isActive = () => {
return active.value && !element.value['isLock'] && isEditMode.value
return active.value && !element.value['isLock'] && isEditMode.value && !freezeFlag.value
}
const userViewEnlargeOpen = opt => {
@ -538,7 +552,8 @@ const handleMouseDownOnShape = e => {
// }
e.stopPropagation()
if (element.value['isLock'] || !isEditMode.value) return
//
if (element.value['isLock'] || !isEditMode.value || freezeFlag.value) return
cursors.value = getCursor() //
@ -897,30 +912,6 @@ const commonBackgroundSvgInner = computed(() => {
}
})
const padding3D = computed(() => {
const width = defaultStyle.value.width //
const height = defaultStyle.value.height //
const rotateX = element.value['multiDimensional'].x // X
const rotateY = element.value['multiDimensional'].y // Y
//
const radX = (rotateX * Math.PI) / 180
const radY = (rotateY * Math.PI) / 180
//
const newWidth = Math.abs(width * Math.cos(radY)) + Math.abs(height * Math.sin(radX))
const newHeight = Math.abs(height * Math.cos(radX)) + Math.abs(width * Math.sin(radY))
// padding
const paddingX = (newWidth - width) / 2
const paddingY = (newHeight - height) / 2
return {
paddingX: `${paddingX}px`,
paddingY: `${paddingY}px`
}
})
const componentBackgroundStyle = computed(() => {
if (element.value.commonBackground && element.value.component !== 'GroupArea') {
const {
@ -1107,12 +1098,15 @@ const dragCollision = computed(() => {
const htmlToImage = () => {
downLoading.value = true
useEmitt().emitter.emit('l7-prepare-picture', element.value.id)
setTimeout(() => {
activeWatermarkCheckUser(viewDemoInnerId.value, 'canvas-main', scale.value)
downloadCanvas2('img', componentInnerRef.value, '图表', () => {
const dom = document.getElementById(viewDemoInnerId.value)
downloadCanvas2('img', dom, '图表', () => {
// do callback
removeActiveWatermark(viewDemoInnerId.value)
downLoading.value = false
useEmitt().emitter.emit('l7-unprepare-picture', element.value.id)
})
}, 200)
}
@ -1138,13 +1132,11 @@ onMounted(() => {
})
settingAttribute()
const methodName = 'componentImageDownload-' + element.value.id
useEmitt().emitter.off(methodName)
useEmitt({
name: methodName,
callback: () => {
if (!useEmitt().emitter.all.get(methodName)?.length) {
useEmitt().emitter.on(methodName, () => {
htmlToImage()
}
})
})
}
})
</script>
@ -1189,6 +1181,7 @@ onMounted(() => {
height: 100%;
position: relative;
background-size: 100% 100% !important;
padding:0 !important;
}
.shape-selected {
@ -1318,4 +1311,14 @@ onMounted(() => {
position: relative;
transform-style: preserve-3d;
}
.freeze-component {
position: fixed;
z-index: 1;
top: 66px !important;
}
.freeze-component-fullscreen {
top: 5px !important;
}
</style>

View File

@ -60,7 +60,4 @@ const getAssetsFile = {
font-weight: 400;
line-height: 22px;
}
:deep(.ed-empty__image){
width: 106px !important;
}
</style>

View File

@ -43,6 +43,7 @@ import waterfallDark from '@/assets/svg/waterfall-dark.svg'
import wordCloudDark from '@/assets/svg/word-cloud-dark.svg'
import tHeatmapDark from '@/assets/svg/t-heatmap-dark.svg'
import circlePackingDark from '@/assets/svg/circle-packing-dark.svg'
import bulletGraphDark from '@/assets/svg/bullet-graph-dark.svg'
const iconChartDarkMap = {
'area-dark': areaDark,
@ -89,7 +90,8 @@ const iconChartDarkMap = {
'waterfall-dark': waterfallDark,
'word-cloud-dark': wordCloudDark,
't-heatmap-dark': tHeatmapDark,
'circle-packing-dark': circlePackingDark
'circle-packing-dark': circlePackingDark,
'bullet-graph-dark': bulletGraphDark
}
export { iconChartDarkMap }

View File

@ -46,6 +46,7 @@ import pictureGroup from '@/assets/svg/picture-group.svg'
import filter from '@/assets/svg/filter.svg'
import outerParams from '@/assets/svg/icon_params_setting.svg'
import circlePacking from '@/assets/svg/circle-packing.svg'
import bulletGraph from '@/assets/svg/bullet-graph.svg'
const iconChartMap = {
'area-stack': areaStack,
@ -95,7 +96,8 @@ const iconChartMap = {
'picture-group': pictureGroup,
filter: filter,
outerParams: outerParams,
'circle-packing': circlePacking
'circle-packing': circlePacking,
'bullet-graph': bulletGraph
}
export { iconChartMap }

View File

@ -12,6 +12,7 @@ import db2Ds from '@/assets/svg/db2-ds.svg'
import redshiftDs from '@/assets/svg/redshift-ds.svg'
import APIDs from '@/assets/svg/API-ds.svg'
import ExcelDs from '@/assets/svg/Excel-ds.svg'
import ExcelRemoteDs from '@/assets/svg/Excel-remote-ds.svg'
import dorisDs from '@/assets/svg/doris-ds.svg'
import esDs from '@/assets/svg/es-ds.svg'
const iconDatasourceMap = {
@ -29,6 +30,7 @@ const iconDatasourceMap = {
redshift: redshiftDs,
API: APIDs,
Excel: ExcelDs,
ExcelRemote: ExcelRemoteDs,
doris: dorisDs,
es: esDs
}

View File

@ -1,6 +1,6 @@
<template>
<div style="width: 100%" ref="bgForm">
<el-form label-position="top" style="width: 100%; margin-bottom: 16px">
<el-form size="small" label-position="top" style="width: 100%; margin-bottom: 16px">
<el-form-item
class="form-item no-margin-bottom"
:class="'form-item-' + themes"

View File

@ -33,12 +33,8 @@
import warnTree from '@/assets/svg/warn-tree.svg'
import { ref } from 'vue'
import { useI18n } from '@/data-visualization/hooks/web/useI18n'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
const dvMainStore = dvMainStoreWithOut()
const dialogShow = ref(false)
const { t } = useI18n()
import { useCache } from '@/data-visualization/hooks/web/useCache'
const { wsCache } = useCache()
const emits = defineEmits(['doUseCache'])
const dialogInfo = {

View File

@ -0,0 +1,84 @@
<template>
<div
v-if="showButton && (!dvMainStore.mobileInPc || isMobile())"
class="bar-main-right"
@mousedown="handOptBarMousedown"
>
<el-button size="mini" type="info" @click="exitFullscreen">
<el-icon style="margin-right: 8px">
<Icon name="exit_fullscreen"
><exit_fullscreen style="font-size: 16px" class="svg-icon"
/></Icon>
</el-icon>
{{ $t('visualization.ext_fullscreen') }}</el-button
>
</div>
</template>
<script lang="ts" setup>
import exit_fullscreen from '@/assets/svg/exit-fullscreen.svg'
import { dvMainStoreWithOut } from '@/data-visualization/store/modules/data-visualization/dvMain'
import { computed } from 'vue'
import { isMainCanvas } from '@/data-visualization/utils/canvasUtils'
import { isMobile } from '@/data-visualization/utils/utils'
import { storeToRefs } from 'pinia'
import Icon from '../icon-custom/src/Icon.vue'
import { ElIcon } from 'element-plus-secondary'
const dvMainStore = dvMainStoreWithOut()
const { fullscreenFlag } = storeToRefs(dvMainStore)
const props = defineProps({
canvasId: {
type: String,
required: false,
default: 'canvas-main'
},
showPosition: {
required: false,
type: String,
default: 'preview'
}
})
const handOptBarMousedown = e => {
e.preventDefault()
e.stopPropagation()
}
const showButton = computed(() => {
if (isMainCanvas(props.canvasId)) {
return fullscreenFlag.value && props.showPosition === 'preview'
} else {
return false
}
})
const exitFullscreen = () => {
document.exitFullscreen()
}
</script>
<style lang="less" scoped>
.bar-main-right {
top: 2px;
right: 2px;
opacity: 0.8;
z-index: 1;
position: absolute;
}
.bar-main-edit-right {
top: 8px;
right: 102px !important;
}
.bar-main-left {
left: 0px;
opacity: 0;
height: fit-content;
&:hover {
opacity: 0.8;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More