diff --git a/core/core-backend/src/main/java/io/dataease/chart/charts/impl/bar/StackBarHandler.java b/core/core-backend/src/main/java/io/dataease/chart/charts/impl/bar/StackBarHandler.java index fb8b8cb805..82f0827134 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/charts/impl/bar/StackBarHandler.java +++ b/core/core-backend/src/main/java/io/dataease/chart/charts/impl/bar/StackBarHandler.java @@ -52,10 +52,10 @@ public class StackBarHandler extends BarHandler { if (ObjectUtils.isNotEmpty(extStack) && Objects.equals(drillFields.get(0).getId(), extStack.get(0).getId())) { fieldsToFilter.addAll(view.getXAxis()); + groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList); + formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis); + result.setFilterList(noDrillFilterList); } - groupStackDrill(noDrillFieldAxis, noDrillFilterList, fieldsToFilter, drillFields, drillRequestList); - formatResult.getAxisMap().put(ChartAxis.xAxis, noDrillFieldAxis); - result.setFilterList(noDrillFilterList); } return (T) result; } diff --git a/core/core-backend/src/main/java/io/dataease/chart/manage/ChartDataManage.java b/core/core-backend/src/main/java/io/dataease/chart/manage/ChartDataManage.java index 3707da539a..8ce8bc3aa1 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/manage/ChartDataManage.java +++ b/core/core-backend/src/main/java/io/dataease/chart/manage/ChartDataManage.java @@ -316,6 +316,11 @@ public class ChartDataManage { dillAxis.add(nextDrillField); fields.add(nextDrillField.getId()); } else { + Optional axis = xAxis.stream().filter(x -> Objects.equals(x.getId(), nextDrillField.getId())).findFirst(); + axis.ifPresent(field -> { + field.setSort(nextDrillField.getSort()); + field.setCustomSort(nextDrillField.getCustomSort()); + }); dillAxis.add(nextDrillField); } } diff --git a/core/core-backend/src/main/java/io/dataease/chart/manage/ChartViewThresholdManage.java b/core/core-backend/src/main/java/io/dataease/chart/manage/ChartViewThresholdManage.java index 21b31efe2e..7513d8dbcb 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/manage/ChartViewThresholdManage.java +++ b/core/core-backend/src/main/java/io/dataease/chart/manage/ChartViewThresholdManage.java @@ -362,9 +362,9 @@ public class ChartViewThresholdManage { } } else { if (timeFlag == 4) { - return now.withDayOfMonth(1).format(formatter); + return now.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).format(formatter); } else if (timeFlag == 5) { - return now.plusMonths(1).withDayOfMonth(1).minusDays(1).format(formatter); + return now.plusMonths(1).withDayOfMonth(1).minusDays(1).withHour(0).withMinute(0).withSecond(0).format(formatter); } else { return getCustomTimeValue(format, 3, suffix, count, true); } @@ -381,7 +381,9 @@ public class ChartViewThresholdManage { LocalDateTime now = LocalDateTime.now(); String fullFormat = "yyyy-MM-dd HH:mm:ss"; int len = format.length(); - if (!hasTime) { + if (hasTime) { + now = now.withHour(0).withMinute(0).withSecond(0); + } else { len = Math.min(len, 10); } DateTimeFormatter formatter = DateTimeFormatter.ofPattern(fullFormat.substring(0, len)); diff --git a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java index cf716553da..3f1d231ab5 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java +++ b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java @@ -5,6 +5,7 @@ import io.dataease.api.chart.ChartDataApi; import io.dataease.api.chart.dto.ViewDetailField; import io.dataease.api.chart.request.ChartExcelRequest; import io.dataease.api.chart.request.ChartExcelRequestInner; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.constant.ChartConstants; import io.dataease.chart.manage.ChartDataManage; import io.dataease.constant.AuthConstant; @@ -76,6 +77,7 @@ public class ChartDataServer implements ChartDataApi { return Math.toIntExact(Math.min(f2CLicLimitedManage.checkDatasetLimit(), limit)); } + @DeLinkPermit("#p0.sceneId") @Override public ChartViewDTO getData(ChartViewDTO chartViewDTO) throws Exception { try { diff --git a/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java b/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java index a21c988dc5..3a06641dd3 100644 --- a/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java +++ b/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java @@ -1,7 +1,10 @@ package io.dataease.config; import io.dataease.constant.AuthConstant; +import io.dataease.share.interceptor.LinkInterceptor; +import jakarta.annotation.Resource; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -11,6 +14,8 @@ import static io.dataease.utils.StaticResourceUtils.ensureSuffix; @Configuration public class DeMvcConfig implements WebMvcConfigurer { + @Resource + private LinkInterceptor linkInterceptor; /** * Configuring static resource path @@ -33,4 +38,9 @@ public class DeMvcConfig implements WebMvcConfigurer { registry.addResourceHandler(geoUrlPattern).addResourceLocations(geoDir); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(linkInterceptor).addPathPatterns("/**"); + } } diff --git a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetDataManage.java b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetDataManage.java index 1dc104bcdf..69703e730f 100644 --- a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetDataManage.java +++ b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetDataManage.java @@ -260,6 +260,7 @@ public class DatasetDataManage { public Long getDatasetTotal(Long datasetGroupId) throws Exception { DatasetGroupInfoDTO dto = datasetGroupManage.getForCount(datasetGroupId); + if (ObjectUtils.isEmpty(dto)) return 0L; if (StringUtils.equalsIgnoreCase(dto.getNodeType(), "dataset")) { return getDatasetTotal(dto, null, new ChartExtRequest()); } diff --git a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetGroupManage.java b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetGroupManage.java index a4bde77608..52db3e5c46 100644 --- a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetGroupManage.java +++ b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetGroupManage.java @@ -262,6 +262,9 @@ public class DatasetGroupManage { List coreDatasetTables = coreDatasetTableMapper.selectList(wrapper); Set ids = new LinkedHashSet(); coreDatasetTables.forEach(ele -> ids.add(ele.getDatasourceId())); + if (CollectionUtils.isEmpty(ids)) { + DEException.throwException("数据集因异常导致无法使用,请重新创建"); + } QueryWrapper datasourceQueryWrapper = new QueryWrapper<>(); datasourceQueryWrapper.in("id", ids); diff --git a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetSQLManage.java b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetSQLManage.java index 9a8f5358b9..520be1882a 100644 --- a/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetSQLManage.java +++ b/core/core-backend/src/main/java/io/dataease/dataset/manage/DatasetSQLManage.java @@ -209,8 +209,6 @@ public class DatasetSQLManage { String tablePrefix = ""; String tableSuffix = ""; if (ObjectUtils.isNotEmpty(currentSQLObj.getTableSchema())) { - ts = currentSQLObj.getTableSchema() + "."; - if (isCross) { tablePrefix = "`"; tableSuffix = "`"; @@ -219,6 +217,8 @@ public class DatasetSQLManage { tablePrefix = datasourceType.getPrefix(); tableSuffix = datasourceType.getSuffix(); } + + ts = tablePrefix + currentSQLObj.getTableSchema() + tableSuffix + "."; } // build join join.append(" ").append(joinType).append(" ") diff --git a/core/core-backend/src/main/java/io/dataease/dataset/utils/TableUtils.java b/core/core-backend/src/main/java/io/dataease/dataset/utils/TableUtils.java index e1d6bd4001..7fec9dcb5e 100644 --- a/core/core-backend/src/main/java/io/dataease/dataset/utils/TableUtils.java +++ b/core/core-backend/src/main/java/io/dataease/dataset/utils/TableUtils.java @@ -44,7 +44,6 @@ public class TableUtils { String prefix = ""; String suffix = ""; if (StringUtils.isNotEmpty(sqlObj.getTableSchema())) { - schema = sqlObj.getTableSchema() + "."; if (isCross) { prefix = "`"; suffix = "`"; @@ -52,6 +51,7 @@ public class TableUtils { prefix = datasourceType.getPrefix(); suffix = datasourceType.getSuffix(); } + schema = prefix + sqlObj.getTableSchema() + suffix + "."; } return schema + prefix + sqlObj.getTableName() + suffix + " " + sqlObj.getTableAlias(); } diff --git a/core/core-backend/src/main/java/io/dataease/engine/trans/ExtWhere2Str.java b/core/core-backend/src/main/java/io/dataease/engine/trans/ExtWhere2Str.java index a6e1594d62..fa3d123eef 100644 --- a/core/core-backend/src/main/java/io/dataease/engine/trans/ExtWhere2Str.java +++ b/core/core-backend/src/main/java/io/dataease/engine/trans/ExtWhere2Str.java @@ -147,19 +147,55 @@ public class ExtWhere2Str { if (value.contains(SQLConstants.EMPTY_SIGN)) { whereValue = "('" + StringUtils.join(value, "','") + "', '')" + " or " + whereName + " is null "; } else { - if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") - || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { - whereValue = "(" + value.stream().map(str -> "'" + SQLConstants.MSSQL_N_PREFIX + str + "'").collect(Collectors.joining(",")) + ")"; + // tree的情况需额外处理 + if (request.getIsTree()) { + List datasetTableFieldList = request.getDatasetTableFieldList(); + boolean hasN = false; + for (DatasetTableFieldDTO dto : datasetTableFieldList) { + if (StringUtils.containsIgnoreCase(dto.getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(dto.getType(), "NCHAR")) { + hasN = true; + break; + } + } + if (hasN) { + whereValue = "(" + value.stream().map(str -> "'" + SQLConstants.MSSQL_N_PREFIX + str + "'").collect(Collectors.joining(",")) + ")"; + } else { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } } else { - whereValue = "('" + StringUtils.join(value, "','") + "')"; + if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { + whereValue = "(" + value.stream().map(str -> "'" + SQLConstants.MSSQL_N_PREFIX + str + "'").collect(Collectors.joining(",")) + ")"; + } else { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } } } } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { - if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") - || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { - whereValue = "'" + SQLConstants.MSSQL_N_PREFIX + "%" + value.get(0) + "%'"; + // tree的情况需额外处理 + if (request.getIsTree()) { + List datasetTableFieldList = request.getDatasetTableFieldList(); + boolean hasN = false; + for (DatasetTableFieldDTO dto : datasetTableFieldList) { + if (StringUtils.containsIgnoreCase(dto.getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(dto.getType(), "NCHAR")) { + hasN = true; + break; + } + } + if (hasN) { + whereValue = "'" + SQLConstants.MSSQL_N_PREFIX + "%" + value.get(0) + "%'"; + } else { + whereValue = "'%" + value.get(0) + "%'"; + } } else { - whereValue = "'%" + value.get(0) + "%'"; + if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { + whereValue = "'" + SQLConstants.MSSQL_N_PREFIX + "%" + value.get(0) + "%'"; + } else { + whereValue = "'%" + value.get(0) + "%'"; + } } } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { if (request.getDatasetTableField().getDeType() == 1) { @@ -191,11 +227,29 @@ public class ExtWhere2Str { if (StringUtils.equals(value.get(0), SQLConstants.EMPTY_SIGN)) { whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE, "") + " or " + whereName + " is null "; } else { - if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") - || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { - whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE_CH, value.get(0)); + // tree的情况需额外处理 + if (request.getIsTree()) { + List datasetTableFieldList = request.getDatasetTableFieldList(); + boolean hasN = false; + for (DatasetTableFieldDTO dto : datasetTableFieldList) { + if (StringUtils.containsIgnoreCase(dto.getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(dto.getType(), "NCHAR")) { + hasN = true; + break; + } + } + if (hasN) { + whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE_CH, value.get(0)); + } else { + whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE, value.get(0)); + } } else { - whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE, value.get(0)); + if (StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NVARCHAR") + || StringUtils.containsIgnoreCase(request.getDatasetTableField().getType(), "NCHAR")) { + whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE_CH, value.get(0)); + } else { + whereValue = String.format(SQLConstants.WHERE_VALUE_VALUE, value.get(0)); + } } } } diff --git a/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java b/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java new file mode 100644 index 0000000000..2a99251b02 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java @@ -0,0 +1,110 @@ +package io.dataease.share.interceptor; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.Verification; +import io.dataease.auth.DeLinkPermit; +import io.dataease.constant.AuthConstant; +import io.dataease.exception.DEException; +import io.dataease.share.manage.XpackShareManage; +import io.dataease.share.util.LinkTokenUtil; +import io.dataease.utils.LogUtil; +import io.dataease.utils.ServletUtils; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Objects; + +@Aspect +@Component +public class DeLinkAop { + + private static final String PARAM_VARIABLE_PREFIX = "p"; + private static final String SPRING_EL_FLAG = "#"; + + private final ExpressionParser parser = new SpelExpressionParser(); + + @Resource + private XpackShareManage xpackShareManage; + + @Around(value = "@annotation(io.dataease.auth.DeLinkPermit)") + public Object logAround(ProceedingJoinPoint point) throws Throwable { + Object[] params = point.getArgs(); + String linkToken = ServletUtils.getHead(AuthConstant.LINK_TOKEN_KEY); + if (StringUtils.isNotBlank(linkToken)) { + MethodSignature ms = (MethodSignature) point.getSignature(); + Method method = ms.getMethod(); + DeLinkPermit deLinkPermit = method.getAnnotation(DeLinkPermit.class); + String value = deLinkPermit.value(); + if (StringUtils.isBlank(value)) { + value = SPRING_EL_FLAG + PARAM_VARIABLE_PREFIX + "0"; + } + Long id = getExpression(params, value); + DecodedJWT jwt = JWT.decode(linkToken); + Long resourceId = jwt.getClaim("resourceId").asLong(); + if (!id.equals(resourceId)) { + DEException.throwException("link token invalid"); + return false; + } + + Long uid = jwt.getClaim("uid").asLong(); + String secret = xpackShareManage.queryPwd(resourceId, uid); + if (StringUtils.isBlank(secret)) { + secret = LinkTokenUtil.defaultPwd; + } + Algorithm algorithm = Algorithm.HMAC256(secret); + Verification verification = JWT.require(algorithm); + JWTVerifier verifier = verification.build(); + DecodedJWT decode = JWT.decode(linkToken); + algorithm.verify(decode); + verifier.verify(linkToken); + } + try { + return point.proceed(params); + } catch (Exception e) { + LogUtil.error(e.getMessage()); + throw e; + } + } + + public Long getExpression(Object[] params, String expression) { + StandardEvaluationContext context = buildContext(params); + Object o = resolveValue(expression, context); + if (ObjectUtils.isNotEmpty(o)) return Long.parseLong(o.toString()); + return null; + } + + private StandardEvaluationContext buildContext(Object[] params) { + StandardEvaluationContext context = new StandardEvaluationContext(); + if (params != null && params.length == 1) { + context.setRootObject(params[0]); + } + for (int i = 0; i < Objects.requireNonNull(params).length; i++) { + Object paramValue = params[i]; + context.setVariable(PARAM_VARIABLE_PREFIX + i, paramValue); + } + return context; + } + + private Object resolveValue(String exp, EvaluationContext context) { + if (StringUtils.contains(exp, SPRING_EL_FLAG)) { + Expression expression = parser.parseExpression(exp); + return expression.getValue(context); + } + return exp; + } +} diff --git a/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java b/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java new file mode 100644 index 0000000000..8cdebaf322 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java @@ -0,0 +1,54 @@ +package io.dataease.share.interceptor; + +import io.dataease.auth.DeLinkPermit; +import io.dataease.constant.AuthConstant; +import io.dataease.exception.DEException; +import io.dataease.utils.ServletUtils; +import io.dataease.utils.WhitelistUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Arrays; +import java.util.List; + + +@Component +public class LinkInterceptor implements HandlerInterceptor { + + private final static String whiteListText = "/user/ipInfo, /apisix/check, /datasetData/enumValue, /datasetData/enumValueObj, /datasetData/getFieldTree, /dekey, /share/validate, /sysParameter/queryOnlineMap, /chartData/innerExportDetails"; + + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String linkToken = ServletUtils.getHead(AuthConstant.LINK_TOKEN_KEY); + if (linkToken == null) { + return true; + } + if (handler instanceof HandlerMethod handlerMethod) { + DeLinkPermit deLinkPermit = handlerMethod.getMethodAnnotation(DeLinkPermit.class); + if (deLinkPermit == null) { + + List whiteList = Arrays.stream(StringUtils.split(whiteListText, ",")).map(String::trim).toList(); + String requestURI = ServletUtils.request().getRequestURI(); + if (StringUtils.startsWith(requestURI, WhitelistUtils.getContextPath())) { + requestURI = requestURI.replaceFirst(WhitelistUtils.getContextPath(), ""); + } + if (StringUtils.startsWith(requestURI, AuthConstant.DE_API_PREFIX)) { + requestURI = requestURI.replaceFirst(AuthConstant.DE_API_PREFIX, ""); + } + boolean valid = whiteList.contains(requestURI) || WhitelistUtils.match(requestURI); + if (!valid) { + DEException.throwException("分享链接Token不支持访问当前url[" + requestURI + "]"); + } + return true; + } + } + return true; + } + + +} diff --git a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java index 2caae368b2..60aeaf8f32 100644 --- a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java +++ b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java @@ -64,6 +64,15 @@ public class XpackShareManage { return xpackShareMapper.selectOne(queryWrapper); } + public String queryPwd(Long resourceId, Long userId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("creator", userId); + queryWrapper.eq("resource_id", resourceId); + XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper); + if (ObjectUtils.isEmpty(xpackShare)) return null; + return xpackShare.getPwd(); + } + @Transactional public void switcher(Long resourceId) { XpackShare originData = queryByResource(resourceId); @@ -199,7 +208,7 @@ public class XpackShareManage { if (ObjectUtils.isEmpty(sharedBase) || !sharedBase.isPeRequire()) return true; Long exp = share.getExp(); String pwd = share.getPwd(); - return StringUtils.isNotBlank(pwd) && ObjectUtils.isNotEmpty(exp); + return StringUtils.isNotBlank(pwd) && ObjectUtils.isNotEmpty(exp) && exp > 0L; } public XpackShareProxyVO proxyInfo(XpackShareProxyRequest request) { diff --git a/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java b/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java index e460e9361e..37adf92631 100644 --- a/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java +++ b/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java @@ -9,7 +9,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Date; public class LinkTokenUtil { - private static final String defaultPwd = "link-pwd-fit2cloud"; + public static final String defaultPwd = "link-pwd-fit2cloud"; public static String generate(Long uid, Long resourceId, Long exp, String pwd, Long oid) { pwd = StringUtils.isBlank(pwd) ? defaultPwd : pwd; Algorithm algorithm = Algorithm.HMAC256(pwd); diff --git a/core/core-backend/src/main/java/io/dataease/substitute/permissions/user/SubstituteUserServer.java b/core/core-backend/src/main/java/io/dataease/substitute/permissions/user/SubstituteUserServer.java index 0015979364..1b64d68531 100644 --- a/core/core-backend/src/main/java/io/dataease/substitute/permissions/user/SubstituteUserServer.java +++ b/core/core-backend/src/main/java/io/dataease/substitute/permissions/user/SubstituteUserServer.java @@ -1,6 +1,7 @@ package io.dataease.substitute.permissions.user; +import io.dataease.api.permissions.user.vo.CurIpVO; import io.dataease.api.permissions.user.vo.UserFormVO; import io.dataease.utils.IPUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -27,6 +28,7 @@ public class SubstituteUserServer { result.put("language", "zh-CN"); return result; } + @GetMapping("/personInfo") public UserFormVO personInfo() { UserFormVO userFormVO = new UserFormVO(); @@ -38,4 +40,13 @@ public class SubstituteUserServer { userFormVO.setModel("lose"); return userFormVO; } + + @GetMapping("/ipInfo") + public CurIpVO ipInfo() { + CurIpVO curIpVO = new CurIpVO(); + curIpVO.setAccount("admin"); + curIpVO.setName("管理员"); + curIpVO.setIp(IPUtils.get()); + return curIpVO; + } } diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java b/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java index 7bf017ba72..1395bef676 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java @@ -11,6 +11,7 @@ import io.dataease.api.visualization.request.DataVisualizationBaseRequest; import io.dataease.api.visualization.request.VisualizationAppExportRequest; import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest; import io.dataease.api.visualization.vo.*; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.chart.manage.ChartDataManage; @@ -142,6 +143,7 @@ public class DataVisualizationServer implements DataVisualizationApi { } } + @DeLinkPermit("#p0.id") @DeLog(id = "#p0.id", ot = LogOT.READ, stExp = "#p0.busiFlag") @Override @XpackInteract(value = "dataVisualizationServer", original = true) diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java index b01bb82a5b..ecd35afbfb 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java @@ -7,6 +7,7 @@ import io.dataease.api.visualization.dto.VisualizationLinkJumpInfoDTO; import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest; import io.dataease.api.visualization.response.VisualizationLinkJumpBaseResponse; import io.dataease.api.visualization.vo.VisualizationViewTableVO; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO; @@ -67,6 +68,7 @@ public class VisualizationLinkJumpService implements VisualizationLinkJumpApi { return extVisualizationLinkageMapper.queryTableFieldWithViewId(viewId); } + @DeLinkPermit //获取仪表板的跳转信息 @Override public VisualizationLinkJumpBaseResponse queryVisualizationJumpInfo(Long dvId) { @@ -135,6 +137,7 @@ public class VisualizationLinkJumpService implements VisualizationLinkJumpApi { }); } + @DeLinkPermit("#p0.targetDvId") @Override public VisualizationLinkJumpBaseResponse queryTargetVisualizationJumpInfo(VisualizationLinkJumpBaseRequest request) { List result = extVisualizationLinkJumpMapper.getTargetVisualizationJumpInfo(request); diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java index e7af4e5562..8f256d9eaf 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java @@ -6,6 +6,7 @@ import io.dataease.api.visualization.dto.LinkageInfoDTO; import io.dataease.api.visualization.dto.VisualizationLinkageDTO; import io.dataease.api.visualization.request.VisualizationLinkageRequest; import io.dataease.api.visualization.vo.VisualizationLinkageFieldVO; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.utils.BeanUtils; @@ -108,6 +109,7 @@ public class VisualizationLinkageService implements VisualizationLinkageApi { return new BaseRspModel(); } + @DeLinkPermit @Override public Map> getVisualizationAllLinkageInfo(Long dvId) { List info = extVisualizationLinkageMapper.getPanelAllLinkageInfo(dvId); diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java index ddf4c31d4d..b3f4bdf018 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java @@ -2,13 +2,13 @@ package io.dataease.visualization.server; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.fasterxml.jackson.core.type.TypeReference; -import com.ibm.icu.impl.coll.CollationLoader; import io.dataease.api.dataset.vo.CoreDatasetGroupVO; import io.dataease.api.dataset.vo.CoreDatasetTableFieldVO; import io.dataease.api.visualization.VisualizationOuterParamsApi; import io.dataease.api.visualization.dto.VisualizationOuterParamsDTO; import io.dataease.api.visualization.dto.VisualizationOuterParamsInfoDTO; import io.dataease.api.visualization.response.VisualizationOuterParamsBaseResponse; +import io.dataease.auth.DeLinkPermit; import io.dataease.dataset.dao.auto.entity.CoreDatasetTable; import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper; import io.dataease.engine.constant.DeTypeConstants; @@ -29,7 +29,10 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -98,6 +101,7 @@ public class VisualizationOuterParamsService implements VisualizationOuterParams } + @DeLinkPermit @Override public VisualizationOuterParamsBaseResponse getOuterParamsInfo(String visualizationId) { List result = extOuterParamsMapper.getVisualizationOuterParamsInfo(visualizationId); diff --git a/core/core-frontend/src/api/user.ts b/core/core-frontend/src/api/user.ts index d02e0a227a..19a23e94bc 100644 --- a/core/core-frontend/src/api/user.ts +++ b/core/core-frontend/src/api/user.ts @@ -32,6 +32,8 @@ export const queryFormApi = uid => request.get({ url: `/user/queryById/${uid}` } export const personInfoApi = () => request.get({ url: `/user/personInfo` }) +export const ipInfoApi = () => request.get({ url: `/user/ipInfo` }) + export const roleCreateApi = data => request.post({ url: '/role/create', data }) export const roleEditApi = data => request.post({ url: '/role/edit', data }) diff --git a/core/core-frontend/src/components/custom-password/src/CustomPassword.vue b/core/core-frontend/src/components/custom-password/src/CustomPassword.vue index 0feec96f3c..0f151ad3c5 100644 --- a/core/core-frontend/src/components/custom-password/src/CustomPassword.vue +++ b/core/core-frontend/src/components/custom-password/src/CustomPassword.vue @@ -2,7 +2,6 @@ import { useAttrs, computed } from 'vue' import icon_visible_outlined from '@/assets/svg/icon_visible_outlined.svg' import icon_invisible_outlined from '@/assets/svg/icon_invisible_outlined.svg' -import { hIcon } from '@/components/icon-custom' const attrs = useAttrs() const props = defineProps(['modelValue']) const emits = defineEmits(['update:modelValue']) @@ -14,15 +13,12 @@ const value = computed({ emits('update:modelValue', value) } }) - -const iconView = hIcon(icon_visible_outlined) -const iconHide = hIcon(icon_invisible_outlined) diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/VQueryChartStyle.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/VQueryChartStyle.vue index cb43a43d29..074b835942 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/VQueryChartStyle.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/VQueryChartStyle.vue @@ -256,7 +256,7 @@ initParams() - + - + - + { } } const resetCustomColor = () => { - state.value.basicStyleForm[seriesColorName.value] = [] - changeBasicStyle(seriesColorName.value) - setupSeriesColor() + if (props.chart.type.includes('map')) { + changeColorOption() + } else { + state.value.basicStyleForm[seriesColorName.value] = [] + changeBasicStyle(seriesColorName.value) + setupSeriesColor() + } } const switchColorCase = () => { diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/LegendSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/LegendSelector.vue index 143a27cee2..fc8f8845ab 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/LegendSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/LegendSelector.vue @@ -155,11 +155,12 @@ const initMapCustomRange = () => { } /** * 计算自定义区间 + * 最大最小值取等分区间的最大最小值 */ const calcMapCustomRange = () => { const customRange = getDynamicColorScale( - mapLegendDefaultRange.min, - mapLegendDefaultRange.max, + state.legendForm.miscForm.mapLegendMin, + state.legendForm.miscForm.mapLegendMax, state.legendForm.miscForm.mapLegendNumber ) state.legendForm.miscForm.mapLegendCustomRange = [] @@ -178,9 +179,7 @@ const calcMapCustomRange = () => { const changeLegendCustomType = (prop?) => { const type = state.legendForm.miscForm.mapLegendRangeType if (type === 'custom') { - state.legendForm.miscForm.mapLegendCustomRange = cloneDeep( - mapLegendCustomRangeCacheList.slice(0, state.legendForm.miscForm.mapLegendNumber + 1) - ) + calcMapCustomRange() } else { state.legendForm.miscForm.mapLegendCustomRange = [] } diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/TooltipSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/TooltipSelector.vue index 174f6fec9c..d4d21a4727 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/TooltipSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/TooltipSelector.vue @@ -36,7 +36,7 @@ const props = defineProps({ } }) const dvMainStore = dvMainStoreWithOut() -const { batchOptStatus } = storeToRefs(dvMainStore) +const { batchOptStatus, mobileInPc } = storeToRefs(dvMainStore) const predefineColors = COLOR_PANEL const toolTip = computed(() => { return props.themes === 'dark' ? 'ndark' : 'dark' @@ -45,7 +45,12 @@ const emit = defineEmits(['onTooltipChange', 'onExtTooltipChange']) const curSeriesFormatter = ref>({}) const quotaData = ref(inject('quotaData')) const showSeriesTooltipFormatter = computed(() => { - return showProperty('seriesTooltipFormatter') && !batchOptStatus.value && props.chart.id + return ( + showProperty('seriesTooltipFormatter') && + !batchOptStatus.value && + !mobileInPc.value && + props.chart.id + ) }) // 切换图表类型直接重置为默认 @@ -488,7 +493,7 @@ onMounted(() => { -
+
{ @@ -339,7 +341,10 @@ onMounted(() => { :effect="themes" controls-position="right" v-model="state.tableCellForm.tableColumnFreezeHead" - :disabled="!state.tableCellForm.tableFreeze" + :disabled=" + (showProperty('mergeCells') && state.tableCellForm.mergeCells) || + !state.tableCellForm.tableFreeze + " :min="0" :max="100" @change="changeTableCell('tableColumnFreezeHead')" @@ -357,7 +362,10 @@ onMounted(() => { :effect="themes" controls-position="right" v-model="state.tableCellForm.tableRowFreezeHead" - :disabled="!state.tableCellForm.tableFreeze" + :disabled=" + (showProperty('mergeCells') && state.tableCellForm.mergeCells) || + !state.tableCellForm.tableFreeze + " :min="0" :max="100" @change="changeTableCell('tableRowFreezeHead')" @@ -376,7 +384,17 @@ onMounted(() => { v-model="state.tableCellForm.mergeCells" @change="changeTableCell('mergeCells')" > - {{ t('chart.merge_cells') }} + + {{ t('chart.merge_cells') }} + + + + + + + { .mobile-style { margin-top: 25px; } +.data-area-label { + text-align: left; + position: relative; + width: 100%; + display: flex; + flex-direction: row; + align-items: center; +} diff --git a/core/core-frontend/src/views/chart/components/editor/index.vue b/core/core-frontend/src/views/chart/components/editor/index.vue index 5d8c154f27..7a023d1246 100644 --- a/core/core-frontend/src/views/chart/components/editor/index.vue +++ b/core/core-frontend/src/views/chart/components/editor/index.vue @@ -74,7 +74,7 @@ import { const embeddedStore = useEmbedded() const snapshotStore = snapshotStoreWithOut() const dvMainStore = dvMainStoreWithOut() -const { canvasCollapse, curComponent, componentData, editMode, mobileInPc } = +const { canvasCollapse, curComponent, componentData, editMode, mobileInPc, fullscreenFlag } = storeToRefs(dvMainStore) const router = useRouter() let componentNameEdit = ref(false) @@ -3316,7 +3316,7 @@ const deleteChartFieldItem = id => {
= { alpha: 100, gradient: false, mapStyle: 'darkblue', - areaBaseColor: '5470C6', + areaBaseColor: '#5470C6', areaBorderColor: '#EBEEF5', gaugeStyle: 'default', tableBorderColor: '#CCCCCC', diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bar.ts index 8cf46f0d9d..f83807ca3d 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bar.ts @@ -19,9 +19,12 @@ import { BAR_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/bar/common' import { + configPlotTooltipEvent, getLabel, getPadding, - setGradientColor + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL } from '@/views/chart/components/js/panel/common/common_antv' import { useI18n } from '@/hooks/web/useI18n' import { DEFAULT_LABEL } from '@/views/chart/components/editor/util/chart' @@ -82,6 +85,7 @@ export class Bar extends G2PlotChartView { newChart = new ColumnClass(container, options) newChart.on('interval:click', action) extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -308,7 +312,10 @@ export class StackBar extends Bar { const res = valueFormatter(param.value, tooltipAttr.tooltipFormatter) obj.value = res ?? '' return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, @@ -522,7 +529,10 @@ export class GroupStackBar extends StackBar { const obj = { name: `${param.category} - ${param.group}`, value: param.value } obj.value = valueFormatter(param.value, tooltipAttr.tooltipFormatter) return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, @@ -605,7 +615,10 @@ export class PercentageStackBar extends GroupStackBar { const obj = { name: param.category, value: param.value } obj.value = (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bidirectional-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bidirectional-bar.ts index 415965baee..23d0f09c6b 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bidirectional-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/bidirectional-bar.ts @@ -4,10 +4,13 @@ import { } from '@/views/chart/components/js/panel/types/impl/g2plot' import { cloneDeep, defaultTo, isEmpty, map } from 'lodash-es' import { + configPlotTooltipEvent, getPadding, + getTooltipContainer, getYAxis, getYAxisExt, - setGradientColor + setGradientColor, + TOOLTIP_TPL } from '@/views/chart/components/js/panel/common/common_antv' import type { BidirectionalBar as G2BidirectionalBar, @@ -171,7 +174,7 @@ export class BidirectionalHorizontalBar extends G2PlotChartView< ...sourceData[0] } }) - + configPlotTooltipEvent(chart, newChart) return newChart } @@ -299,7 +302,10 @@ export class BidirectionalHorizontalBar extends G2PlotChartView< }) } return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/horizontal-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/horizontal-bar.ts index 3c6c2516d2..815d8f42c4 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/horizontal-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/horizontal-bar.ts @@ -3,7 +3,13 @@ import { G2PlotDrawOptions } from '@/views/chart/components/js/panel/types/impl/g2plot' import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar' -import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/views/chart/components/js/panel/common/common_antv' import { cloneDeep } from 'lodash-es' import { flow, @@ -93,7 +99,7 @@ export class HorizontalBar extends G2PlotChartView { const newChart = new Bar(container, options) newChart.on('interval:click', action) - + configPlotTooltipEvent(chart, newChart) return newChart } @@ -313,7 +319,10 @@ export class HorizontalStackBar extends HorizontalBar { const res = valueFormatter(param.value, tooltipAttr.tooltipFormatter) obj.value = res ?? '' return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, @@ -423,7 +432,10 @@ export class HorizontalPercentageStackBar extends HorizontalStackBar { const obj = { name: param.category, value: param.value } obj.value = (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/progress-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/progress-bar.ts index 41b6079b35..a91503eb42 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/progress-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/progress-bar.ts @@ -1,6 +1,11 @@ import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' import { flow, hexColorToRGBA, parseJson } from '../../../util' -import { setGradientColor } from '../../common/common_antv' +import { + configPlotTooltipEvent, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '../../common/common_antv' import { useI18n } from '@/hooks/web/useI18n' import type { Bar as G2Progress, BarOptions } from '@antv/g2plot/esm/plots/bar' import { @@ -134,7 +139,7 @@ export class ProgressBar extends G2PlotChartView { const newChart = new G2Progress(container, options) newChart.on('interval:click', action) - + configPlotTooltipEvent(chart, newChart) return newChart } protected configBasicStyle(chart: Chart, options: BarOptions): BarOptions { @@ -224,7 +229,10 @@ export class ProgressBar extends G2PlotChartView { } }) return result.length == 0 ? originalItems : result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } } } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/range-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/range-bar.ts index 709b8b674d..a3b982c228 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/range-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/range-bar.ts @@ -3,7 +3,13 @@ import { G2PlotDrawOptions } from '@/views/chart/components/js/panel/types/impl/g2plot' import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar' -import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/views/chart/components/js/panel/common/common_antv' import { cloneDeep, find } from 'lodash-es' import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util' import { valueFormatter } from '@/views/chart/components/js/formatter' @@ -161,7 +167,7 @@ export class RangeBar extends G2PlotChartView { const newChart = new BarClass(container, options) newChart.on('interval:click', action) - + configPlotTooltipEvent(chart, newChart) return newChart } @@ -232,7 +238,10 @@ export class RangeBar extends G2PlotChartView { } } return { value: res, values: param.values, name: param.field } - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } } else { tooltip = false diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/waterfall.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/waterfall.ts index 608aece2af..36ef5e0a89 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/bar/waterfall.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/bar/waterfall.ts @@ -2,7 +2,14 @@ import type { WaterfallOptions, Waterfall as G2Waterfall } from '@antv/g2plot/es import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' import { flow, hexColorToRGBA, parseJson } from '../../../util' import { valueFormatter } from '../../../formatter' -import { getPadding, getTooltipSeriesTotalMap, setGradientColor } from '../../common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + getTooltipSeriesTotalMap, + setGradientColor, + TOOLTIP_TPL +} from '../../common/common_antv' import { isEmpty } from 'lodash-es' import { useI18n } from '@/hooks/web/useI18n' const { t } = useI18n() @@ -92,6 +99,7 @@ export class Waterfall extends G2PlotChartView { const { Waterfall: G2Waterfall } = await import('@antv/g2plot/esm/plots/waterfall') const newChart = new G2Waterfall(container, options) newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -224,7 +232,10 @@ export class Waterfall extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/line/area.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/line/area.ts index b7091824ef..4596d9bb72 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/line/area.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/line/area.ts @@ -3,7 +3,13 @@ import { G2PlotDrawOptions } from '@/views/chart/components/js/panel/types/impl/g2plot' import type { Area as G2Area, AreaOptions } from '@antv/g2plot/esm/plots/area' -import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/views/chart/components/js/panel/common/common_antv' import { cloneDeep } from 'lodash-es' import { flow, @@ -116,6 +122,7 @@ export class Area extends G2PlotChartView { newChart.on('point:click', action) extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -352,7 +359,10 @@ export class StackArea extends Area { value: valueFormatter(param.value, tooltipAttr.tooltipFormatter) } return obj - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, tooltip } } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/line/line.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/line/line.ts index 1781068a20..0735f8a78a 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/line/line.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/line/line.ts @@ -3,7 +3,12 @@ import { G2PlotDrawOptions } from '@/views/chart/components/js/panel/types/impl/g2plot' import type { Line as G2Line, LineOptions } from '@antv/g2plot/esm/plots/line' -import { getPadding } from '../../common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + TOOLTIP_TPL +} from '../../common/common_antv' import { flow, hexColorToRGBA, @@ -116,6 +121,7 @@ export class Line extends G2PlotChartView { newChart.on('point:click', action) extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -279,7 +285,10 @@ export class Line extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/line/stock-line.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/line/stock-line.ts index 1508c06d8e..b3126aa381 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/line/stock-line.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/line/stock-line.ts @@ -610,6 +610,7 @@ export class StockLine extends G2PlotChartView { label } : { + ...yAxisOptions['yAxis'], label, grid: null, line: null diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/map/flow-map.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/map/flow-map.ts index cdad0642fc..8622b11273 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/map/flow-map.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/map/flow-map.ts @@ -79,6 +79,11 @@ export class FlowMap extends L7ChartView { async drawChart(drawOption: L7DrawConfig) { const { chart, container } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } const xAxis = deepCopy(chart.xAxis) const xAxisExt = deepCopy(chart.xAxisExt) const { basicStyle, misc } = deepCopy(parseJson(chart.customAttr)) diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/map/heat-map.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/map/heat-map.ts index e935f3e6bb..0ae7c06df7 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/map/heat-map.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/map/heat-map.ts @@ -56,6 +56,11 @@ export class HeatMap extends L7ChartView { async drawChart(drawOption: L7DrawConfig) { const { chart, container } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } const xAxis = deepCopy(chart.xAxis) const yAxis = deepCopy(chart.yAxis) let basicStyle: DeepPartial diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/map/map.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/map/map.ts index 90f75a93fc..b67ec32794 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/map/map.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/map/map.ts @@ -10,7 +10,8 @@ import { getGeoJsonFile, hexColorToRGBA, parseJson, - getMaxAndMinValueByData + getMaxAndMinValueByData, + filterEmptyMinValue } from '@/views/chart/components/js/util' import { handleGeoJson, @@ -102,7 +103,7 @@ export class Map extends L7PlotChartView { from: 'map', data: { max: maxValue, - min: minValue, + min: minValue ?? filterEmptyMinValue(sourceData, 'value'), legendNumber: legendNumber } }) @@ -252,7 +253,10 @@ export class Map extends L7PlotChartView { if (colorScale.length) { options.color['value'] = colorScale.map(item => (item.color ? item.color : item)) if (colorScale[0].value && !misc.mapAutoLegend) { - options.color['scale']['domain'] = [minValue, maxValue] + options.color['scale']['domain'] = [ + minValue ?? filterEmptyMinValue(sourceData, 'value'), + maxValue + ] } } return options @@ -272,6 +276,33 @@ export class Map extends L7PlotChartView { if (!legend.show) { return options } + // 内部函数 创建自定义图例的内容 + const createLegendCustomContent = showItems => { + const containerDom = createDom(CONTAINER_TPL) as HTMLElement + const listDom = containerDom.getElementsByClassName(LIST_CLASS)[0] as HTMLElement + showItems.forEach(item => { + let value = '-' + if (item.value !== '') { + if (Array.isArray(item.value)) { + item.value.forEach((v, i) => { + item.value[i] = Number.isNaN(v) || v === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0) + }) + value = item.value.join('-') + } else { + const tmp = item.value as string + value = Number.isNaN(tmp) || tmp === 'NaN' ? 'NaN' : parseFloat(tmp).toFixed(0) + } + } + const substituteObj = { ...item, value } + + const domStr = substitute(ITEM_TPL, substituteObj) + const itemDom = createDom(domStr) + // 给 legend 形状用的 + itemDom.style.setProperty('--bgColor', item.color) + listDom.appendChild(itemDom) + }) + return listDom + } const LEGEND_SHAPE_STYLE_MAP = { circle: { borderRadius: '50%' @@ -299,7 +330,7 @@ export class Map extends L7PlotChartView { ...LEGEND_SHAPE_STYLE_MAP[legend.icon], width: '9px', height: '9px', - border: '0.01px solid #f4f4f4' + ...(legend.icon === 'triangle' ? {} : { border: '0.01px solid #f4f4f4' }) } } } @@ -324,61 +355,17 @@ export class Map extends L7PlotChartView { color: rangeColor }) }) - customLegend['items'] = items - const findColorByValue = (value, intervals) => { - if (value) { - for (const interval of intervals) { - if (value >= interval.value[0] && value <= interval.value[1]) { - return interval.color - } - } - } - // 或者可以返回 undefined - return null - } - options.color.value = t => { - const c = findColorByValue(t.value, items) - return c ? c : null - } - customLegend['domStyles'] = { - ...customLegend['domStyles'], - 'l7plot-legend l7plot-legend__category': { - 'box-shadow': '0px 0px 0px 0px', - 'background-color': 'var(--bgColor)', - padding: 0 - }, - 'l7plot-legend__list-item': { - 'margin-bottom': '3px' + customLegend['customContent'] = (_: string, _items: CategoryLegendListItem[]) => { + if (items?.length) { + return createLegendCustomContent(items) } + return '' } } else { customLegend['customContent'] = (_: string, items: CategoryLegendListItem[]) => { const showItems = items?.length > 30 ? items.slice(0, 30) : items if (showItems?.length) { - const containerDom = createDom(CONTAINER_TPL) as HTMLElement - const listDom = containerDom.getElementsByClassName(LIST_CLASS)[0] as HTMLElement - showItems.forEach(item => { - let value = '-' - if (item.value !== '') { - if (Array.isArray(item.value)) { - item.value.forEach((v, i) => { - item.value[i] = Number.isNaN(v) || v === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0) - }) - value = item.value.join('-') - } else { - const tmp = item.value as string - value = Number.isNaN(tmp) || tmp === 'NaN' ? 'NaN' : parseFloat(tmp).toFixed(0) - } - } - const substituteObj = { ...item, value } - - const domStr = substitute(ITEM_TPL, substituteObj) - const itemDom = createDom(domStr) - // 给 legend 形状用的 - itemDom.style.setProperty('--bgColor', item.color) - listDom.appendChild(itemDom) - }) - return listDom + return createLegendCustomContent(showItems) } return '' } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/map/symbolic-map.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/map/symbolic-map.ts index c7809aae30..a3be048dc5 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/map/symbolic-map.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/map/symbolic-map.ts @@ -81,6 +81,11 @@ export class SymbolicMap extends L7ChartView { async drawChart(drawOption: L7DrawConfig) { const { chart, container, action } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } const xAxis = deepCopy(chart.xAxis) let basicStyle let miscStyle @@ -330,6 +335,15 @@ export class SymbolicMap extends L7ChartView { return resultMap } + /** + * 清除 popup + * @param container + */ + clearPopup = container => { + const containerElement = document.getElementById(container) + containerElement?.querySelectorAll('.l7-popup').forEach((element: Element) => element.remove()) + } + /** * 构建 tooltip * @param chart @@ -337,6 +351,7 @@ export class SymbolicMap extends L7ChartView { */ buildTooltip = (chart, container, pointLayer) => { const customAttr = chart.customAttr ? parseJson(chart.customAttr) : null + this.clearPopup(container) if (customAttr?.tooltip?.show) { const { tooltip } = deepCopy(customAttr) let showFields = tooltip.showFields || [] diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/map/tooltip-carousel.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/map/tooltip-carousel.ts index ae451b5940..bf91cbf1b0 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/map/tooltip-carousel.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/map/tooltip-carousel.ts @@ -12,11 +12,7 @@ export const configCarouselTooltip = (chart, view, data, scene) => { ?.filter(i => i.dimensionList?.length > 0) .reduce((acc, current) => { const existingItem = acc.find(obj => { - if (!obj.abbrev || obj.abbrev === 'China') { - return obj.adcode === current.adcode - } else { - return obj.abbrev === current.abbrev - } + return obj.name === current.name || (obj.adcode && obj.adcode === current.adcode) }) if (!existingItem) { acc.push(current) diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/chart-mix.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/chart-mix.ts index d617102e87..e573c0dc5b 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/chart-mix.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/chart-mix.ts @@ -4,12 +4,15 @@ import { } from '@/views/chart/components/js/panel/types/impl/g2plot' import type { DualAxes, DualAxesOptions } from '@antv/g2plot/esm/plots/dual-axes' import { + configPlotTooltipEvent, getAnalyse, getLabel, getPadding, + getTooltipContainer, getYAxis, getYAxisExt, - setGradientColor + setGradientColor, + TOOLTIP_TPL } from '../../common/common_antv' import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util' import { cloneDeep, isEmpty, defaultTo, map, filter, union, defaultsDeep } from 'lodash-es' @@ -158,7 +161,7 @@ export class ColumnLineMix extends G2PlotChartView { newChart.on('point:click', action) newChart.on('interval:click', action) - + configPlotTooltipEvent(chart, newChart) return newChart } @@ -544,7 +547,10 @@ export class ColumnLineMix extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/funnel.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/funnel.ts index 2cea0114d6..14adddfae1 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/funnel.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/funnel.ts @@ -1,7 +1,7 @@ import type { FunnelOptions, Funnel as G2Funnel } from '@antv/g2plot/esm/plots/funnel' import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' import { flow, parseJson, setUpSingleDimensionSeriesColor } from '@/views/chart/components/js/util' -import { getPadding } from '../../common/common_antv' +import { configPlotTooltipEvent, getPadding } from '../../common/common_antv' import { useI18n } from '@/hooks/web/useI18n' import { Datum } from '@antv/g2plot/esm/types/common' import { valueFormatter } from '@/views/chart/components/js/formatter' @@ -110,6 +110,7 @@ export class Funnel extends G2PlotChartView { const { Funnel: G2Funnel } = await import('@antv/g2plot/esm/plots/funnel') const newChart = new G2Funnel(container, options) newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) return newChart } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/quadrant.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/quadrant.ts index 4d0de4afce..d755dcfdbd 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/quadrant.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/quadrant.ts @@ -8,6 +8,7 @@ import { valueFormatter } from '@/views/chart/components/js/formatter' import { useI18n } from '@/hooks/web/useI18n' import { isEmpty, map } from 'lodash-es' import { cloneDeep, defaultTo } from 'lodash-es' +import { configPlotTooltipEvent, getTooltipContainer, TOOLTIP_TPL } from '../../common/common_antv' const { t } = useI18n() /** @@ -209,6 +210,7 @@ export class Quadrant extends G2PlotChartView { newChart.on('point:click', action) newChart.on('click', () => quadrantDefaultBaseline(defaultBaselineQuadrant)) newChart.on('afterrender', () => quadrantDefaultBaseline(defaultBaselineQuadrant)) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -379,7 +381,10 @@ export class Quadrant extends G2PlotChartView { }) } return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/radar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/radar.ts index 3e2571bc42..a89771c516 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/radar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/radar.ts @@ -1,7 +1,7 @@ import type { RadarOptions, Radar as G2Radar } from '@antv/g2plot/esm/plots/radar' import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' import { flow, parseJson } from '../../../util' -import { getPadding } from '../../common/common_antv' +import { configPlotTooltipEvent, getPadding } from '../../common/common_antv' import { valueFormatter } from '../../../formatter' import type { Datum } from '@antv/g2plot/esm/types/common' import { useI18n } from '@/hooks/web/useI18n' @@ -117,6 +117,7 @@ export class Radar extends G2PlotChartView { const { Radar: G2Radar } = await import('@antv/g2plot/esm/plots/radar') const newChart = new G2Radar(container, options) newChart.on('point:click', action) + configPlotTooltipEvent(chart, newChart) return newChart } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/scatter.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/scatter.ts index fa96eb8230..c9be7c9522 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/scatter.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/scatter.ts @@ -5,7 +5,12 @@ import { import type { ScatterOptions, Scatter as G2Scatter } from '@antv/g2plot/esm/plots/scatter' import { flow, parseJson } from '../../../util' import { valueFormatter } from '../../../formatter' -import { getPadding } from '../../common/common_antv' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + TOOLTIP_TPL +} from '../../common/common_antv' import { useI18n } from '@/hooks/web/useI18n' import { isEmpty } from 'lodash-es' @@ -133,6 +138,7 @@ export class Scatter extends G2PlotChartView { const { Scatter: G2Scatter } = await import('@antv/g2plot/esm/plots/scatter') const newChart = new G2Scatter(container, options) newChart.on('point:click', action) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -233,7 +239,10 @@ export class Scatter extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/pie/pie.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/pie/pie.ts index 192b80d6ce..9947863e98 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/pie/pie.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/pie/pie.ts @@ -10,8 +10,11 @@ import { setUpSingleDimensionSeriesColor } from '@/views/chart/components/js/util' import { + configPlotTooltipEvent, getPadding, - getTooltipSeriesTotalMap + getTooltipContainer, + getTooltipSeriesTotalMap, + TOOLTIP_TPL } from '@/views/chart/components/js/panel/common/common_antv' import { valueFormatter } from '@/views/chart/components/js/formatter' import { @@ -118,6 +121,7 @@ export class Pie extends G2PlotChartView { const { Pie: G2Pie } = await import('@antv/g2plot/esm/plots/pie') const newChart = new G2Pie(container, options) newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) return newChart } @@ -237,7 +241,10 @@ export class Pie extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/pie/rose.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/pie/rose.ts index 66047b64ed..424c793a83 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/pie/rose.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/pie/rose.ts @@ -10,8 +10,11 @@ import { PIE_EDITOR_PROPERTY_INNER } from './common' import { + configPlotTooltipEvent, getPadding, - getTooltipSeriesTotalMap + getTooltipContainer, + getTooltipSeriesTotalMap, + TOOLTIP_TPL } from '@/views/chart/components/js/panel/common/common_antv' import { parseJson, flow, setUpSingleDimensionSeriesColor } from '@/views/chart/components/js/util' import { Label } from '@antv/g2plot/lib/types/label' @@ -94,7 +97,7 @@ export class Rose extends G2PlotChartView { const plot = new G2Rose(container, options) plot.on('interval:click', action) - + configPlotTooltipEvent(chart, plot) return plot } @@ -213,7 +216,10 @@ export class Rose extends G2PlotChartView { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return { ...options, diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts index ad9b102a1b..025052c7b6 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-info.ts @@ -210,7 +210,7 @@ export class TableInfo extends S2ChartView { // tooltip this.configTooltip(chart, s2Options) // 合并单元格 - this.configMergeCells(chart, s2Options) + this.configMergeCells(chart, s2Options, s2DataConfig) // 隐藏表头,保留顶部的分割线, 禁用表头横向 resize if (tableHeader.showTableHeader === false) { s2Options.style.colCfg.height = 1 diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-pivot.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-pivot.ts index 2378e45623..d7071b592a 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-pivot.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/table/table-pivot.ts @@ -353,7 +353,10 @@ export class TablePivot extends S2ChartView { if (!isAlphaColor(tableHeaderBgColor)) { tableHeaderBgColor = hexColorToRGBA(tableHeaderBgColor, basicStyle.alpha) } - const tableBorderColor = hexColorToRGBA(basicStyle.tableBorderColor, basicStyle.alpha) + let tableBorderColor = basicStyle.tableBorderColor + if (!isAlphaColor(tableBorderColor)) { + tableBorderColor = hexColorToRGBA(tableBorderColor, basicStyle.alpha) + } const tableHeaderFontColor = hexColorToRGBA(tableHeader.tableHeaderFontColor, basicStyle.alpha) const fontStyle = tableHeader.isItalic ? 'italic' : 'normal' const fontWeight = tableHeader.isBolder === false ? 'normal' : 'bold' diff --git a/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts b/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts index ac879b5c3c..674cecf6da 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts @@ -30,6 +30,8 @@ import { Scene } from '@antv/l7-scene' import { type IZoomControlOption } from '@antv/l7-component' 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' export function getPadding(chart: Chart): number[] { if (chart.drill) { @@ -124,7 +126,9 @@ export function getTheme(chart: Chart) { color: tooltipColor, fontSize: tooltipFontsize + 'px', background: tooltipBackgroundColor, - boxShadow: '0 4px 8px 0 rgba(0, 0, 0, 0.1)' + boxShadow: '0 4px 8px 0 rgba(0, 0, 0, 0.1)', + 'z-index': 3000, + position: 'fixed' } } }, @@ -164,7 +168,8 @@ export function getLabel(chart: Chart) { 'pie-donut', 'radar', 'waterfall', - 't-heatmap' + 't-heatmap', + 'bar' ].includes(chart.type) ) { layout.push({ type: 'hide-overlap' }) @@ -206,7 +211,10 @@ export function getTooltip(chart: Chart) { formatter: function (param: Datum) { const value = valueFormatter(param.value, t.tooltipFormatter) return { name: param.field, value } - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } } else { tooltip = false @@ -257,7 +265,10 @@ export function getMultiSeriesTooltip(chart: Chart) { } }) return result - } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true } return tooltip } @@ -1195,7 +1206,10 @@ function shouldHideZoom(basicStyle: any): boolean { * @param basicStyle */ function getCenter(basicStyle: any): [number, number] { - let center = [DEFAULT_BASIC_STYLE.mapCenter.longitude, DEFAULT_BASIC_STYLE.mapCenter.latitude] + 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] } @@ -1210,3 +1224,107 @@ function getCenter(basicStyle: any): [number, number] { function addCustomZoom(plotScene: Scene, newZoomOptions: any): void { plotScene.addControl(new CustomZoom(newZoomOptions)) } + +const G2_TOOLTIP_WRAPPER = 'g2-tooltip-wrapper' +export function getTooltipContainer(id) { + let wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) + if (!wrapperDom) { + wrapperDom = document.createElement('div') + wrapperDom.id = G2_TOOLTIP_WRAPPER + document.body.appendChild(wrapperDom) + } + const curDom = document.getElementById(id) + if (curDom) { + curDom.remove() + } + const g2Tooltip = document.createElement('div') + g2Tooltip.setAttribute('id', id) + g2Tooltip.classList.add('g2-tooltip') + // 最多半屏,鼠标移入可滚动 + g2Tooltip.style.maxHeight = '50%' + g2Tooltip.style.overflowY = 'auto' + g2Tooltip.style.display = 'none' + g2Tooltip.style.position = 'fixed' + g2Tooltip.style.left = '0px' + g2Tooltip.style.top = '0px' + const g2TooltipTitle = document.createElement('div') + g2TooltipTitle.classList.add('g2-tooltip-title') + g2Tooltip.appendChild(g2TooltipTitle) + + const g2TooltipList = document.createElement('ul') + g2TooltipList.classList.add('g2-tooltip-list') + g2Tooltip.appendChild(g2TooltipList) + const full = document.getElementsByClassName('fullscreen') + if (full.length) { + full.item(0).appendChild(g2Tooltip) + } else { + wrapperDom.appendChild(g2Tooltip) + } + return g2Tooltip +} +export function configPlotTooltipEvent>( + chart: Chart, + plot: P +) { + const { tooltip } = parseJson(chart.customAttr) + if (!tooltip.show) { + return + } + // 鼠标可移入, 移入之后保持显示, 移出之后隐藏 + plot.options.tooltip.container.addEventListener('mouseenter', e => { + e.target.style.visibility = 'visible' + e.target.style.display = 'block' + }) + plot.options.tooltip.container.addEventListener('mouseleave', e => { + e.target.style.visibility = 'hidden' + e.target.style.display = 'none' + }) + // 手动处理 tooltip 的显示和隐藏事件,需配合源码理解 + // https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#showTooltip + plot.on('tooltip:show', () => { + const tooltipCtl = plot.chart.getController('tooltip') + if (!tooltipCtl) { + return + } + const event = plot.chart.interactions.tooltip?.context?.event + if (tooltipCtl.tooltip) { + // 处理视图放大后再关闭 tooltip 的 dom 被清除 + const container = tooltipCtl.tooltip.cfg.container + container.style.display = 'block' + const dom = document.getElementById(container.id) + if (!dom) { + const full = document.getElementsByClassName('fullscreen') + if (full.length) { + full.item(0).appendChild(container) + } else { + const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) + wrapperDom.appendChild(container) + } + } + } + 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 + }) + // https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#hideTooltip + plot.on('plot:mouseleave', () => { + const tooltipCtl = plot.chart.getController('tooltip') + if (!tooltipCtl) { + return + } + plot.chart.getOptions().tooltip.follow = true + const container = tooltipCtl.tooltip?.cfg?.container + if (container) { + container.style.display = 'none' + } + tooltipCtl.hideTooltip() + }) +} + +export const TOOLTIP_TPL = + '
  • ' + + '' + + '{name}:' + + '{value}' + + '
  • ' diff --git a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts index b5397678da..aa461ec2d1 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts @@ -564,7 +564,7 @@ export function getConditions(chart: Chart) { if (conditions?.length > 0) { const { tableCell, basicStyle, tableHeader } = parseJson(chart.customAttr) const enableTableCrossBG = tableCell.enableTableCrossBG - const valueColor = tableCell.tableFontColor + const valueColor = isAlphaColor(tableCell.tableFontColor) ? tableCell.tableFontColor : hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha) const valueBgColor = enableTableCrossBG ? null : isAlphaColor(tableCell.tableItemBgColor) @@ -629,7 +629,7 @@ export function getConditions(chart: Chart) { } export function mappingColor(value, defaultColor, field, type, filedValueMap?, rowData?) { - let color + let color = null for (let i = 0; i < field.conditions.length; i++) { let flag = false const t = field.conditions[i] @@ -1426,29 +1426,35 @@ export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { } } -export function configMergeCells(chart: Chart, options: S2Options) { +export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S2DataConfig) { const { mergeCells } = parseJson(chart.customAttr).tableCell const { showIndex } = parseJson(chart.customAttr).tableHeader if (mergeCells) { - const xAxis = chart.xAxis - const quotaIndex = xAxis.findIndex(axis => axis.groupType === 'q') + options.frozenColCount = 0 + options.frozenRowCount = 0 + const fields = chart.data.fields || [] + const fieldsMap = fields.reduce((p, n) => { + p[n.dataeaseName] = n + return p + }, {}) || {} + const quotaIndex = dataConfig.meta.findIndex(m => fieldsMap[m.field].groupType === 'q') const data = chart.data?.tableRow if (quotaIndex === 0 || !data?.length) { return } const mergedColInfo: number[][][] = [[[0, data.length - 1]]] const mergedCellsInfo = [] - const axisToMerge = xAxis.filter((a, i) => a.hide !== true && (i < quotaIndex || quotaIndex === -1)) + const axisToMerge = dataConfig.meta.filter((_, i) => i < quotaIndex || quotaIndex === -1) axisToMerge.forEach((a, i) => { const preMergedColInfo = mergedColInfo[i] const curMergedColInfo = [] mergedColInfo.push(curMergedColInfo) preMergedColInfo.forEach(range => { const [start, end] = range - let lastVal = data[start][a.dataeaseName] + let lastVal = data[start][a.field] let lastIndex = start for (let index = start; index <= end; index++) { - const curVal = data[index][a.dataeaseName] + const curVal = data[index][a.field] if (curVal !== lastVal || index === end) { const curRange = index - lastIndex if (curRange > 1 || @@ -1499,7 +1505,10 @@ export function configMergeCells(chart: Chart, options: S2Options) { } export function getRowIndex(mergedCellsInfo: MergedCellInfo[][], meta: ViewMeta): number { - let curRangeStartIndex = 0 + if (!mergedCellsInfo?.length) { + return meta.rowIndex + 1 + } + let curRangeStartIndex = meta.rowIndex const lostCells = mergedCellsInfo.reduce((p, n) => { if (n[0].colIndex !== 0) { return p diff --git a/core/core-frontend/src/views/chart/components/js/panel/types/impl/s2.ts b/core/core-frontend/src/views/chart/components/js/panel/types/impl/s2.ts index cbd8c005aa..275557c5a4 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/types/impl/s2.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/types/impl/s2.ts @@ -64,8 +64,8 @@ export abstract class S2ChartView

    extends AntVAbstractCha return getConditions(chart) } - protected configMergeCells(chart: Chart, option: S2Options) { - configMergeCells(chart, option) + protected configMergeCells(chart: Chart, option: S2Options, dataConfig: S2DataConfig) { + configMergeCells(chart, option, dataConfig) } protected showTooltip(s2Instance: P, event, metaConfig: Meta[]) { diff --git a/core/core-frontend/src/views/chart/components/js/util.ts b/core/core-frontend/src/views/chart/components/js/util.ts index 897ec36972..6638e5552b 100644 --- a/core/core-frontend/src/views/chart/components/js/util.ts +++ b/core/core-frontend/src/views/chart/components/js/util.ts @@ -1048,3 +1048,23 @@ export function svgStrToUrl(svgStr: string): string { } catch (e) {} return file } + +/** + * 获取非空数据的最小值 + * @param sourceData + * @param field + * @private + */ +export function filterEmptyMinValue(sourceData, field) { + let notEmptyMinValue = 0 + getMaxAndMinValueByData( + sourceData.filter(item => item[field]), + 'value', + 0, + 0, + (max, min) => { + notEmptyMinValue = min + } + ) + return notEmptyMinValue +} diff --git a/core/core-frontend/src/views/common/DeResourceTree.vue b/core/core-frontend/src/views/common/DeResourceTree.vue index 69e122cfdb..3a0a0d98b0 100644 --- a/core/core-frontend/src/views/common/DeResourceTree.vue +++ b/core/core-frontend/src/views/common/DeResourceTree.vue @@ -4,6 +4,7 @@ import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg' import dvCopyDark from '@/assets/svg/dv-copy-dark.svg' import dvDelete from '@/assets/svg/dv-delete.svg' import dvMove from '@/assets/svg/dv-move.svg' +import { treeDraggbleChart } from '@/utils/treeDraggbleChart' import dvRename from '@/assets/svg/dv-rename.svg' import dvDashboardSpine from '@/assets/svg/dv-dashboard-spine.svg' import dvScreenSpine from '@/assets/svg/dv-screen-spine.svg' @@ -153,7 +154,11 @@ const resourceTypeList = computed(() => { ] return list }) - +const { handleDrop, allowDrop, handleDragStart } = treeDraggbleChart( + state, + 'resourceTree', + curCanvasType.value +) const menuList = computed(() => { const list = [ { @@ -640,6 +645,10 @@ defineExpose({ @node-expand="nodeExpand" @node-collapse="nodeCollapse" @node-click="nodeClick" + @node-drag-start="handleDragStart" + :allow-drop="allowDrop" + @node-drop="handleDrop" + draggable > diff --git a/core/core-frontend/src/views/data-visualization/PreviewShow.vue b/core/core-frontend/src/views/data-visualization/PreviewShow.vue index 9e1b156b9d..bb6de66ba2 100644 --- a/core/core-frontend/src/views/data-visualization/PreviewShow.vue +++ b/core/core-frontend/src/views/data-visualization/PreviewShow.vue @@ -17,10 +17,14 @@ import { download2AppTemplate, downloadCanvas2 } from '@/utils/imgUtils' import MultiplexPreviewShow from '@/views/data-visualization/MultiplexPreviewShow.vue' import DvPreview from '@/views/data-visualization/DvPreview.vue' import AppExportForm from '@/components/de-app/AppExportForm.vue' -import { personInfoApi } from '@/api/user' import { ElMessage } from 'element-plus-secondary' import { useEmitt } from '@/hooks/web/useEmitt' +import { useUserStoreWithOut } from '@/store/modules/user' +const userStore = useUserStoreWithOut() + +const userName = computed(() => userStore.getName) + const dvMainStore = dvMainStoreWithOut() const { dvInfo, canvasViewDataInfo } = storeToRefs(dvMainStore) const previewCanvasContainer = ref(null) @@ -136,7 +140,7 @@ const downLoadToAppPre = () => { appName: state.dvInfo.name, icon: null, version: '2.0', - creator: state.userLoginInfo?.name, + creator: userName.value, required: '2.9.0', description: null }) @@ -170,8 +174,7 @@ const state = reactive({ canvasStylePreview: null, canvasViewInfoPreview: null, dvInfo: null, - curPreviewGap: 0, - userLoginInfo: {} + curPreviewGap: 0 }) const sideTreeStatus = ref(true) @@ -195,12 +198,6 @@ const downLoadApp = appAttachInfo => { fileDownload('app', appAttachInfo) } -const findUserData = callback => { - personInfoApi().then(rsp => { - callback(rsp) - }) -} - onMounted(() => { useEmitt({ name: 'canvasDownload', @@ -218,9 +215,6 @@ onBeforeMount(() => { if (props.showPosition === 'preview') { dvMainStore.canvasDataInit() } - findUserData(res => { - state.userLoginInfo = res.data - }) }) diff --git a/core/core-frontend/src/views/data-visualization/index.vue b/core/core-frontend/src/views/data-visualization/index.vue index 7f60d3b76c..a3e705ab70 100644 --- a/core/core-frontend/src/views/data-visualization/index.vue +++ b/core/core-frontend/src/views/data-visualization/index.vue @@ -85,7 +85,7 @@ const { canvasState, batchOptStatus } = storeToRefs(dvMainStore) -const { editorMap } = storeToRefs(composeStore) +const { editorMap, isSpaceDown } = storeToRefs(composeStore) const canvasOut = ref(null) const canvasInner = ref(null) const leftSidebarRef = ref(null) @@ -93,6 +93,8 @@ const dvLayout = ref(null) const canvasCenterRef = ref(null) const mainHeight = ref(300) let createType = null +let isDragging = false // 标记是否在拖动 +let startX, startY, scrollLeft, scrollTop const state = reactive({ datasetTree: [], scaleHistory: null, @@ -103,6 +105,39 @@ const state = reactive({ opt: null }) +// 启用拖动 +const enableDragging = e => { + if (isSpaceDown.value) { + // 仅在空格键按下时启用拖动 + isDragging = true + startX = e.pageX - canvasOut.value.wrapRef.offsetLeft + startY = e.pageY - canvasOut.value.wrapRef.offsetTop + scrollLeft = canvasOut.value.wrapRef.scrollLeft + scrollTop = canvasOut.value.wrapRef.scrollTop + e.preventDefault() + e.stopPropagation() + } +} + +// 执行拖动 +const onMouseMove = e => { + if (!isDragging) return + e.preventDefault() + e.stopPropagation() + const x = e.pageX - canvasOut.value.wrapRef.offsetLeft + const y = e.pageY - canvasOut.value.wrapRef.offsetTop + const walkX = x - startX + const walkY = y - startY + canvasOut.value.wrapRef.scrollLeft = scrollLeft - walkX + canvasOut.value.wrapRef.scrollTop = scrollTop - walkY + console.log('====onMouseMove==walkX=' + walkX + ';walkY=' + walkY) +} + +// 禁用拖动 +const disableDragging = () => { + isDragging = false +} + const contentStyle = computed(() => { const { width, height } = canvasStyleData.value if (editMode.value === 'preview') { @@ -421,6 +456,8 @@ eventBus.on('handleNew', handleNew) :class="isDataEaseBi && !newWindowFromDiv && 'dataease-w-h'" > + ---{{ isSpaceDown }} +

    +
    + > + +
    @@ -580,6 +626,7 @@ eventBus.on('handleNew', handleNew) background-color: rgba(51, 51, 51, 1); overflow: auto; .content { + position: relative; flex: 1; width: 100%; overflow: auto; @@ -644,4 +691,19 @@ eventBus.on('handleNew', handleNew) color: #ebebeb; } } + +.canvas-drag { + position: absolute; + z-index: 1; + opacity: 0.3; + cursor: pointer; +} + +.canvas-drag-tip { + position: absolute; + right: 5px; + bottom: -20px; + font-size: 12px; + color: rgb(169, 175, 184); +} diff --git a/core/core-frontend/src/views/visualized/data/dataset/index.vue b/core/core-frontend/src/views/visualized/data/dataset/index.vue index de0812a317..bf25c29738 100644 --- a/core/core-frontend/src/views/visualized/data/dataset/index.vue +++ b/core/core-frontend/src/views/visualized/data/dataset/index.vue @@ -6,10 +6,12 @@ import icon_intoItem_outlined from '@/assets/svg/icon_into-item_outlined.svg' import icon_rename_outlined from '@/assets/svg/icon_rename_outlined.svg' import dvNewFolder from '@/assets/svg/dv-new-folder.svg' import icon_fileAdd_outlined from '@/assets/svg/icon_file-add_outlined.svg' +import { moveDatasetTree } from '@/api/dataset' import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg' import dvSortAsc from '@/assets/svg/dv-sort-asc.svg' import dvSortDesc from '@/assets/svg/dv-sort-desc.svg' import dvFolder from '@/assets/svg/dv-folder.svg' +import { treeDraggble } from '@/utils/treeDraggble' import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg' import icon_info_outlined from '@/assets/svg/icon_info_outlined.svg' import icon_dashboard_outlined from '@/assets/svg/icon_dashboard_outlined.svg' @@ -247,6 +249,13 @@ const infoList = computed(() => { } }) +const { handleDrop, allowDrop, handleDragStart } = treeDraggble( + state, + 'datasetTree', + moveDatasetTree, + 'dataset' +) + const generateColumns = (arr: Field[]) => arr.map(ele => ({ key: ele.dataeaseName, @@ -852,6 +861,10 @@ const getMenuList = (val: boolean) => { :filter-node-method="filterNode" expand-on-click-node highlight-current + @node-drag-start="handleDragStart" + :allow-drop="allowDrop" + @node-drop="handleDrop" + draggable @node-expand="nodeExpand" @node-collapse="nodeCollapse" :default-expanded-keys="expandedKey" diff --git a/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue b/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue index 1abc26fe18..1405b22a3a 100644 --- a/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue +++ b/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue @@ -325,6 +325,16 @@ const setRules = () => { } ] } + + if (form.value.type === 'es') { + configRules['configuration.url'] = [ + { + required: true, + message: t('datasource.please_input_datasource_url'), + trigger: 'change' + } + ] + } rule.value = { ...cloneDeep(configRules), ...cloneDeep(defaultRule) } } diff --git a/core/core-frontend/src/views/visualized/data/datasource/index.vue b/core/core-frontend/src/views/visualized/data/datasource/index.vue index 8ea91497e1..6ddeb4eb8a 100644 --- a/core/core-frontend/src/views/visualized/data/datasource/index.vue +++ b/core/core-frontend/src/views/visualized/data/datasource/index.vue @@ -34,6 +34,7 @@ import { ElScrollbar, ElAside } from 'element-plus-secondary' +import { treeDraggble } from '@/utils/treeDraggble' import GridTable from '@/components/grid-table/src/GridTable.vue' import ArrowSide from '@/views/common/DeResourceArrow.vue' import relationChart from '@/components/relation-chart/index.vue' @@ -744,6 +745,13 @@ const handleEdit = async data => { editDatasource() } +const { handleDrop, allowDrop, handleDragStart } = treeDraggble( + state, + 'datasourceTree', + move, + 'datasource' +) + const handleCopy = async data => { getById(data.id).then(res => { let { @@ -1112,6 +1120,10 @@ const getMenuList = (val: boolean) => { :default-expanded-keys="expandedKey" :data="state.datasourceTree" :props="defaultProps" + @node-drag-start="handleDragStart" + :allow-drop="allowDrop" + @node-drop="handleDrop" + draggable @node-click="handleNodeClick" >