Merge branch 'dev-v2' into pr@dev-v2@feat_report_task

This commit is contained in:
fit2cloud-chenyw 2024-04-23 15:32:46 +08:00
commit db87205e6b
87 changed files with 2003 additions and 1110 deletions

View File

@ -865,7 +865,7 @@ public class ChartDataManage {
return list;
}
var assistLineCfg = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("assistLineCfg")), ChartSeniorAssistCfgDTO.class);
ChartSeniorAssistCfgDTO assistLineCfg = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("assistLineCfg")), ChartSeniorAssistCfgDTO.class);
if (null == assistLineCfg || !assistLineCfg.isEnable()) {
return list;
}

View File

@ -200,7 +200,7 @@ public class DatasetDataManage {
// build query sql
SQLMeta sqlMeta = new SQLMeta();
Table2SQLObj.table2sqlobj(sqlMeta, null, "(" + sql + ")", crossDs);
Field2SQLObj.field2sqlObj(sqlMeta, fields, crossDs, dsMap);
Field2SQLObj.field2sqlObj(sqlMeta, fields, fields, crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
String querySQL;
@ -459,7 +459,7 @@ public class DatasetDataManage {
rowPermissionsTree = permissionManage.getRowPermissionsTree(datasetGroupInfoDTO.getId(), user.getUserId());
}
Field2SQLObj.field2sqlObj(sqlMeta, fields, crossDs, dsMap);
Field2SQLObj.field2sqlObj(sqlMeta, fields, datasetGroupInfoDTO.getAllFields(), crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
String querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, true, 0, 1000);
@ -585,7 +585,7 @@ public class DatasetDataManage {
datasetGroupInfoDTO.setSortFields(Collections.singletonList(deSortField));
}
Field2SQLObj.field2sqlObj(sqlMeta, fields, crossDs, dsMap);
Field2SQLObj.field2sqlObj(sqlMeta, fields, datasetGroupInfoDTO.getAllFields(), crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
String querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, false, 0, 1000);

View File

@ -47,7 +47,7 @@ public class SQLConstants {
public static final String FIELD_NAME = "%s.`%s`";
public static final String FIELD_DOT = "`%s`";
public static final String FIELD_DOT = "%s";
public static final String UNIX_TIMESTAMP = "DE_UNIX_TIMESTAMP(%s)";

View File

@ -45,6 +45,9 @@ public class CustomWhere2Str {
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originName = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, field.getId());
fieldsDialect.put(originName, calcFieldExp);
if (isCross) {
originName = calcFieldExp;
}
} else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) {
originName = String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), field.getDataeaseName());
} else {
@ -86,18 +89,17 @@ public class CustomWhere2Str {
res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))");
}
} else {
if (field.getDeType() == 1) {
// 规定几种日期格式一一匹配匹配到就是该格式
whereName = String.format(SQLConstants.UNIX_TIMESTAMP, whereName);
}
List<ChartCustomFilterItemDTO> filter = request.getFilter();
for (ChartCustomFilterItemDTO filterItemDTO : filter) {
String value = filterItemDTO.getValue();
String whereTerm = Utils.transFilterTerm(filterItemDTO.getTerm());
String whereValue = "";
// String whereNameReal;
if (field.getDeType() == 1) {
// 规定几种日期格式一一匹配匹配到就是该格式
whereName = String.format(SQLConstants.UNIX_TIMESTAMP, whereName);
}
if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) {
whereValue = "";
} else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) {
@ -113,12 +115,12 @@ public class CustomWhere2Str {
} else {
// 如果是时间字段过滤当条件是等于和不等于的时候转换成between和not between
if (field.getDeType() == 1) {
if (StringUtils.containsIgnoreCase(whereTerm, "=")) {
if (StringUtils.equalsIgnoreCase(whereTerm, " = ")) {
whereTerm = " BETWEEN ";
// 把value类似过滤组件处理获得start time和end time
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(value);
whereValue = String.format(SQLConstants.WHERE_VALUE_BETWEEN, stringLongMap.get("startTime"), stringLongMap.get("endTime"));
} else if (StringUtils.containsIgnoreCase(whereTerm, "<>")) {
} else if (StringUtils.equalsIgnoreCase(whereTerm, " <> ")) {
whereTerm = " NOT BETWEEN ";
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(value);
whereValue = String.format(SQLConstants.WHERE_VALUE_BETWEEN, stringLongMap.get("startTime"), stringLongMap.get("endTime"));

View File

@ -39,6 +39,9 @@ public class Dimension2SQLObj {
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originField = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, x.getId());
fieldsDialect.put(originField, calcFieldExp);
if (isCross) {
originField = calcFieldExp;
}
} else if (ObjectUtils.isNotEmpty(x.getExtField()) && Objects.equals(x.getExtField(), ExtFieldConstant.EXT_COPY)) {
originField = String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), x.getDataeaseName());
} else {

View File

@ -53,6 +53,9 @@ public class ExtWhere2Str {
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originName = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, field.getId());
fieldsDialect.put(originName, calcFieldExp);
if (isCross) {
originName = calcFieldExp;
}
} else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) {
originName = String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), field.getDataeaseName());
} else {

View File

@ -20,7 +20,7 @@ import java.util.*;
*/
public class Field2SQLObj {
public static void field2sqlObj(SQLMeta meta, List<DatasetTableFieldDTO> fields, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
public static void field2sqlObj(SQLMeta meta, List<DatasetTableFieldDTO> fields, List<DatasetTableFieldDTO> originFields, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
SQLObj tableObj = meta.getTable();
if (ObjectUtils.isEmpty(tableObj)) {
return;
@ -33,10 +33,13 @@ public class Field2SQLObj {
String originField;
if (ObjectUtils.isNotEmpty(x.getExtField()) && Objects.equals(x.getExtField(), ExtFieldConstant.EXT_CALC)) {
// 解析origin name中有关联的字段生成sql表达式
String calcFieldExp = Utils.calcFieldRegex(x.getOriginName(), tableObj, fields, isCross, dsMap);
String calcFieldExp = Utils.calcFieldRegex(x.getOriginName(), tableObj, originFields, isCross, dsMap);
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originField = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, x.getId());
fieldsDialect.put(originField, calcFieldExp);
if (isCross) {
originField = calcFieldExp;
}
// 此处是数据集预览获取数据库原始字段枚举值等操作使用如果遇到聚合函数则将originField设置为null
for (String func : FunctionConstant.AGG_FUNC) {
if (Utils.matchFunction(func, calcFieldExp)) {

View File

@ -11,8 +11,8 @@ import io.dataease.engine.constant.SQLConstants;
import io.dataease.engine.utils.Utils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -24,8 +24,8 @@ public class Order2SQLObj {
public static void getOrders(SQLMeta meta, List<DatasetTableFieldDTO> fields, List<DeSortField> sortFields, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
SQLObj tableObj = meta.getTable();
List<SQLObj> xOrders = meta.getXOrders();
if (ObjectUtils.isEmpty(tableObj) || CollectionUtils.isEmpty(xOrders)) {
List<SQLObj> xOrders = meta.getXOrders() == null ? new ArrayList<>() : meta.getXOrders();
if (ObjectUtils.isEmpty(tableObj)) {
return;
}
if (ObjectUtils.isNotEmpty(sortFields)) {
@ -35,6 +35,7 @@ public class Order2SQLObj {
SQLObj order = buildSortField(deSortField, tableObj, i, fields, isCross, dsMap);
xOrders.add(order);
}
meta.setXOrders(xOrders);
}
}

View File

@ -40,6 +40,9 @@ public class Quota2SQLObj {
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originField = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, y.getId());
fieldsDialect.put(originField, calcFieldExp);
if (isCross) {
originField = calcFieldExp;
}
} else if (ObjectUtils.isNotEmpty(y.getExtField()) && Objects.equals(y.getExtField(), ExtFieldConstant.EXT_COPY)) {
originField = String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), y.getDataeaseName());
} else {

View File

@ -88,6 +88,9 @@ public class WhereTree2Str {
// 给计算字段处加一个占位符后续SQL方言转换后再替换
originName = String.format(SqlPlaceholderConstants.CALC_FIELD_PLACEHOLDER, field.getId());
fieldsDialect.put(originName, calcFieldExp);
if (isCross) {
originName = calcFieldExp;
}
} else if (ObjectUtils.isNotEmpty(field.getExtField()) && Objects.equals(field.getExtField(), ExtFieldConstant.EXT_COPY)) {
originName = String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), field.getDataeaseName());
} else {
@ -149,12 +152,12 @@ public class WhereTree2Str {
} else {
// 如果是时间字段过滤当条件是等于和不等于的时候转换成between和not between
if (field.getDeType() == 1) {
if (StringUtils.containsIgnoreCase(whereTerm, "=")) {
if (StringUtils.equalsIgnoreCase(whereTerm, " = ")) {
whereTerm = " BETWEEN ";
// 把value类似过滤组件处理获得start time和end time
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(value);
whereValue = String.format(SQLConstants.WHERE_VALUE_BETWEEN, stringLongMap.get("startTime"), stringLongMap.get("endTime"));
} else if (StringUtils.containsIgnoreCase(whereTerm, "<>")) {
} else if (StringUtils.equalsIgnoreCase(whereTerm, " <> ")) {
whereTerm = " NOT BETWEEN ";
Map<String, Long> stringLongMap = Utils.parseDateTimeValue(value);
whereValue = String.format(SQLConstants.WHERE_VALUE_BETWEEN, stringLongMap.get("startTime"), stringLongMap.get("endTime"));

View File

@ -3,6 +3,7 @@ package io.dataease.visualization.server;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.permissions.dataset.dto.DataSetRowPermissionsTreeDTO;
import io.dataease.api.visualization.VisualizationLinkJumpApi;
import io.dataease.api.visualization.dto.VisualizationComponentDTO;
import io.dataease.api.visualization.dto.VisualizationLinkJumpDTO;
import io.dataease.api.visualization.dto.VisualizationLinkJumpInfoDTO;
import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest;
@ -144,14 +145,18 @@ public class VisualizationLinkJumpService implements VisualizationLinkJumpApi {
}
@Override
public List<VisualizationViewTableVO> viewTableDetailList(Long dvId) {
public VisualizationComponentDTO viewTableDetailList(Long dvId) {
DataVisualizationInfo dvInfo = dataVisualizationInfoMapper.selectById(dvId);
List<VisualizationViewTableVO> result;
String componentData;
if (dvInfo != null) {
List<VisualizationViewTableVO> result = extVisualizationLinkJumpMapper.getViewTableDetails(dvId);
return result.stream().filter(viewTableInfo -> dvInfo.getComponentData().indexOf(viewTableInfo.getId().toString()) > -1).collect(Collectors.toList());
}else {
return new ArrayList<>();
result = extVisualizationLinkJumpMapper.getViewTableDetails(dvId).stream().filter(viewTableInfo -> dvInfo.getComponentData().indexOf(viewTableInfo.getId().toString()) > -1).collect(Collectors.toList());
componentData = dvInfo.getComponentData();
} else {
result = new ArrayList<>();
componentData = "[]";
}
return new VisualizationComponentDTO(componentData,result);
}

View File

@ -24,7 +24,7 @@
"axios": "^1.3.3",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.9",
"element-plus-secondary": "^0.5.5",
"element-plus-secondary": "^0.5.6",
"element-resize-detector": "^1.2.4",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.11",

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="1713773038663" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6292" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M960 64H64C28.8 64 0 92.8 0 128v768c0 35.2 28.8 64 64 64h896c35.2 0 64-28.8 64-64V128c0-35.2-28.8-64-64-64z m-26.56 808h-832v-704h832v704z" p-id="6293"></path><path d="M695.36 735.04c-32.64 0-60.48-9.28-81.92-28.16-22.4-19.2-33.92-44.48-33.92-74.88 0-55.04 32.96-120.96 190.72-120.96h5.12c-0.64-11.52-1.6-18.24-2.24-21.76-0.64-2.88-2.24-7.04-9.6-11.52-3.84-2.24-13.44-6.4-36.48-6.4-49.6 0-49.92 8-50.56 20.16l-0.64 18.24h-88.96l1.28-20.48c5.44-85.76 78.4-96.64 141.76-96.64 40.32 0 72.64 8.64 96.32 25.92 25.6 18.56 38.4 53.12 38.4 102.4v206.72h-88.64v-11.84c-23.68 13.12-50.56 19.2-80.64 19.2z m64.64-147.52c-90.56 0-90.56 31.36-90.56 41.92 0 11.2 4.48 15.68 8.32 18.56 14.4 10.56 45.76 11.52 71.68-1.28 10.88-5.44 19.2-11.84 25.92-20.16v-38.72h-15.36z m-177.6 140.8h-100.16l-45.44-121.92h-137.6L256 728.32H160l165.12-439.36h82.24l175.04 439.36z m-255.36-202.56h80.32l-40.96-110.72-39.36 110.72z" p-id="6294"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 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="1713772528957" class="icon" viewBox="0 0 1029 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5278" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.9765625" height="200"><path d="M860.16025 508.587c0-20.48-3.413-40.96-20.48-58.027L505.17325 95.573c-27.306-27.306-68.266-27.306-95.573 0l-98.987 102.4-143.36-153.6c-13.653-13.653-34.133-13.653-47.786 0-13.654 17.067-13.654 37.547 0 51.2l143.36 150.187L20.48025 498.347c-27.307 27.306-27.307 71.68 0 102.4l341.333 354.986c27.307 27.307 68.267 27.307 95.574 0L843.09325 552.96c10.24-13.653 17.067-30.72 17.067-44.373z m-740.693-6.827l191.146-201.387L430.08025 426.667c13.653 13.653 34.133 13.653 47.787 0 13.653-13.654 13.653-37.547 0-51.2L358.40025 249.173l95.573-102.4 337.92 354.987H119.46725z m907.946 334.507c-6.826-75.094-136.533-286.72-136.533-286.72s-126.293 201.386-136.533 286.72C737.28025 976.213 822.61325 993.28 890.88025 993.28c68.267 0 146.773-13.653 136.533-157.013z m0 0" p-id="5279"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 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="1713772449367" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4293" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M770 900c34.242 0 62 27.758 62 62 0 34.242-27.758 62-62 62H254c-34.242 0-62-27.758-62-62 0-34.242 27.758-62 62-62h516z m-78-736c59.05 0 107.032 47.391 107.986 106.214L800 272v33.867c0 19.882-16.118 36-36 36-19.683 0-35.677-15.797-35.995-35.405l-0.005-0.595V272c0-19.683-15.797-35.677-35.405-35.995L692 236H270c-19.683 0-35.677 15.797-35.995 35.405L234 272v424c0 19.683 15.797 35.677 35.405 35.995L270 732h162.756c19.882 0 36 16.118 36 36 0 19.683-15.797 35.677-35.405 36H270c-59.05 0-107.032-47.391-107.986-106.214L162 696V272c0-59.05 47.391-107.032 106.214-107.986L270 164h422z m171.445 200.445l49.497 49.497c9.372 9.372 9.372 24.568 0 33.94l-0.088 0.088-281.16 278.274-95.972 28.583c-8.47 2.522-17.38-2.298-19.902-10.767a16 16 0 0 1 0.228-9.84l31.74-90.946L829.59 364.358c9.387-9.29 24.515-9.252 33.854 0.087z" p-id="4294"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 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="1689150709983" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4725" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M231.424 227.04128v160.89088h338.37056a63.8976 63.8976 0 0 1 63.8976 63.8976v314.40896H794.624V331.776a104.61184 104.61184 0 0 0-104.61184-104.61184z" p-id="4726" data-spm-anchor-id="a313x.7781069.0.i0" class="selected"></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="1713521630002" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4283" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M396 140h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m-44 684h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m524-204h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM192 344h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m0 160h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m0 160h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m0 160h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m320 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m160 0h-56c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z m140-284c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V370c0-127-103-230-230-230H484c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h170c87.3 0 158 70.7 158 158v170zM236 96H92c-4.4 0-8 3.6-8 8v144c0 4.4 3.6 8 8 8h144c4.4 0 8-3.6 8-8V104c0-4.4-3.6-8-8-8z m-48 101.6c0 1.3-1.1 2.4-2.4 2.4h-43.2c-1.3 0-2.4-1.1-2.4-2.4v-43.2c0-1.3 1.1-2.4 2.4-2.4h43.2c1.3 0 2.4 1.1 2.4 2.4v43.2zM920 780H776c-4.4 0-8 3.6-8 8v144c0 4.4 3.6 8 8 8h144c4.4 0 8-3.6 8-8V788c0-4.4-3.6-8-8-8z m-48 101.6c0 1.3-1.1 2.4-2.4 2.4h-43.2c-1.3 0-2.4-1.1-2.4-2.4v-43.2c0-1.3 1.1-2.4 2.4-2.4h43.2c1.3 0 2.4 1.1 2.4 2.4v43.2z" p-id="4284"></path></svg>

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 1.7 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="1713774473081" class="icon" viewBox="0 0 1129 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9335" xmlns:xlink="http://www.w3.org/1999/xlink" width="220.5078125" height="200"><path d="M0 741.517241h1129.931034v282.482759H0zM0 353.103448h1129.931034v211.862069H0zM0 0h1129.931034v141.241379H0z" p-id="9336"></path></svg>

After

Width:  |  Height:  |  Size: 478 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1713752109906" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4245" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M176 261.75H92v-168h84z m288-168H272v168h192z m288 0H560v168h192z m180 0h-84v168h84z m-798 378H92v126h42z m420 126h84v-126h-84z m-168 0h84v-126h-84z m-168 0h84v-126h-84z m504 0h84v-126h-84z m168 0h42v-126h-42z m42 252H92v84h840z" p-id="4246"></path></svg>

After

Width:  |  Height:  |  Size: 590 B

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="1693218467455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15153" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M116.181404 133.722146 116.181404 441.133425 201.417293 441.133425 201.417293 133.722146 317.598697 133.722146 317.598697 64 0 64 0 133.722146 116.181404 133.722146ZM412.356282 125.799174 412.356282 64 335.263948 64 335.263948 125.799174 412.356282 125.799174ZM335.263948 168.055021 335.263948 441.133425 412.356282 441.133425 412.356282 168.055021 335.263948 168.055021ZM550.003263 168.055021 550.003263 86.184319 472.91093 86.184319 472.91093 168.055021 426.221206 168.055021 426.221206 218.233837 472.91093 218.233837 472.91093 379.33425C472.91093 393.067469 475.263489 404.159517 479.968678 412.610729 484.673868 421.06194 491.098164 427.576318 499.241762 432.154058 507.385359 436.731797 516.795596 439.812922 527.472757 441.397524 538.149917 442.982126 549.4603 443.774415 561.404242 443.774415 569.004932 443.774415 576.786475 443.598351 584.749103 443.246218 592.711731 442.894084 599.950376 442.189827 606.465253 441.133425L606.465253 383.031637C602.845877 383.735905 599.045588 384.264097 595.064275 384.616231 591.082961 384.968365 586.92074 385.144429 582.577488 385.144429 569.547733 385.144429 560.86136 383.031658 556.518108 378.806052 552.174856 374.580447 550.003263 366.129362 550.003263 353.452545L550.003263 218.233837 606.465253 218.233837 606.465253 168.055021 550.003263 168.055021ZM624.673409 64 624.673409 441.133425 701.765741 441.133425 701.765741 64 624.673409 64ZM930.620557 275.279229 805.209786 275.279229C805.571725 269.997222 806.748006 264.011037 808.738662 257.320495 810.729318 250.629953 814.167674 244.291639 819.05383 238.305364 823.939994 232.31909 830.454771 227.301258 838.598368 223.25172 846.741965 219.20218 856.966547 217.177441 869.272429 217.177441 888.09319 217.177441 902.118061 222.107241 911.347469 231.966988 920.576883 241.826734 927.001178 256.264004 930.620557 275.279229L930.620557 275.279229ZM805.209786 322.817057 1007.71289 322.817057C1009.16064 301.689028 1007.350976 281.441637 1002.283853 262.074278 997.216723 242.706918 988.982765 225.45262 977.581728 210.310867 966.180691 195.169113 951.612922 183.10871 933.877978 174.129299 916.143034 165.149886 895.33193 160.660248 871.444045 160.660248 850.089722 160.660248 830.635866 164.357597 813.081888 171.752407 795.527917 179.147217 780.417248 189.270913 767.749427 202.123796 755.081613 214.97668 745.30944 230.206239 738.432627 247.81293 731.555808 265.41962 728.117453 284.434561 728.117453 304.858322 728.117453 325.986351 731.465325 345.35342 738.161171 362.96011 744.857018 380.566801 754.357741 395.708327 766.663622 408.385144 778.969498 421.061962 793.98969 430.833528 811.724634 437.700138 829.459578 444.566747 849.36585 448 871.444045 448 903.29456 448 930.439475 440.957429 952.87961 426.872077 975.319744 412.786724 991.968627 389.370177 1002.826752 356.621733L934.963782 356.621733C932.430221 365.072945 925.55351 373.083869 914.33344 380.654746 903.113376 388.225622 889.721882 392.011004 874.158566 392.011004 852.442304 392.011004 835.793421 386.553012 824.211418 375.636864 812.629414 364.720716 806.295597 347.114289 805.209786 322.817057ZM0 576 1024 576 1024 640 0 640 0 576ZM0 768 1024 768 1024 832 0 832 0 768ZM0 960 768 960 768 1024 0 1024 0 960Z" fill="#666666" p-id="15154"></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="1693218467455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15153" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M116.181404 133.722146 116.181404 441.133425 201.417293 441.133425 201.417293 133.722146 317.598697 133.722146 317.598697 64 0 64 0 133.722146 116.181404 133.722146ZM412.356282 125.799174 412.356282 64 335.263948 64 335.263948 125.799174 412.356282 125.799174ZM335.263948 168.055021 335.263948 441.133425 412.356282 441.133425 412.356282 168.055021 335.263948 168.055021ZM550.003263 168.055021 550.003263 86.184319 472.91093 86.184319 472.91093 168.055021 426.221206 168.055021 426.221206 218.233837 472.91093 218.233837 472.91093 379.33425C472.91093 393.067469 475.263489 404.159517 479.968678 412.610729 484.673868 421.06194 491.098164 427.576318 499.241762 432.154058 507.385359 436.731797 516.795596 439.812922 527.472757 441.397524 538.149917 442.982126 549.4603 443.774415 561.404242 443.774415 569.004932 443.774415 576.786475 443.598351 584.749103 443.246218 592.711731 442.894084 599.950376 442.189827 606.465253 441.133425L606.465253 383.031637C602.845877 383.735905 599.045588 384.264097 595.064275 384.616231 591.082961 384.968365 586.92074 385.144429 582.577488 385.144429 569.547733 385.144429 560.86136 383.031658 556.518108 378.806052 552.174856 374.580447 550.003263 366.129362 550.003263 353.452545L550.003263 218.233837 606.465253 218.233837 606.465253 168.055021 550.003263 168.055021ZM624.673409 64 624.673409 441.133425 701.765741 441.133425 701.765741 64 624.673409 64ZM930.620557 275.279229 805.209786 275.279229C805.571725 269.997222 806.748006 264.011037 808.738662 257.320495 810.729318 250.629953 814.167674 244.291639 819.05383 238.305364 823.939994 232.31909 830.454771 227.301258 838.598368 223.25172 846.741965 219.20218 856.966547 217.177441 869.272429 217.177441 888.09319 217.177441 902.118061 222.107241 911.347469 231.966988 920.576883 241.826734 927.001178 256.264004 930.620557 275.279229L930.620557 275.279229ZM805.209786 322.817057 1007.71289 322.817057C1009.16064 301.689028 1007.350976 281.441637 1002.283853 262.074278 997.216723 242.706918 988.982765 225.45262 977.581728 210.310867 966.180691 195.169113 951.612922 183.10871 933.877978 174.129299 916.143034 165.149886 895.33193 160.660248 871.444045 160.660248 850.089722 160.660248 830.635866 164.357597 813.081888 171.752407 795.527917 179.147217 780.417248 189.270913 767.749427 202.123796 755.081613 214.97668 745.30944 230.206239 738.432627 247.81293 731.555808 265.41962 728.117453 284.434561 728.117453 304.858322 728.117453 325.986351 731.465325 345.35342 738.161171 362.96011 744.857018 380.566801 754.357741 395.708327 766.663622 408.385144 778.969498 421.061962 793.98969 430.833528 811.724634 437.700138 829.459578 444.566747 849.36585 448 871.444045 448 903.29456 448 930.439475 440.957429 952.87961 426.872077 975.319744 412.786724 991.968627 389.370177 1002.826752 356.621733L934.963782 356.621733C932.430221 365.072945 925.55351 373.083869 914.33344 380.654746 903.113376 388.225622 889.721882 392.011004 874.158566 392.011004 852.442304 392.011004 835.793421 386.553012 824.211418 375.636864 812.629414 364.720716 806.295597 347.114289 805.209786 322.817057ZM0 576 1024 576 1024 640 0 640 0 576ZM0 768 1024 768 1024 832 0 832 0 768ZM0 960 768 960 768 1024 0 1024 0 960Z" p-id="15154"></path></svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1052_111876)">
<path d="M8.00033 15.3332C12.0504 15.3332 15.3337 12.0499 15.3337 7.99984C15.3337 3.94975 12.0504 0.666504 8.00033 0.666504C3.95024 0.666504 0.666992 3.94975 0.666992 7.99984C0.666992 12.0499 3.95024 15.3332 8.00033 15.3332Z" fill="#FF8800"/>
<path d="M7.66634 4.6665C7.48225 4.6665 7.33301 4.81574 7.33301 4.99984V8.99984C7.33301 9.18393 7.48225 9.33317 7.66634 9.33317H8.33301C8.5171 9.33317 8.66634 9.18393 8.66634 8.99984V4.99984C8.66634 4.81574 8.5171 4.6665 8.33301 4.6665H7.66634Z" fill="white"/>
<path d="M7.66634 9.99984C7.48225 9.99984 7.33301 10.1491 7.33301 10.3332V10.9998C7.33301 11.1839 7.48225 11.3332 7.66634 11.3332H8.33301C8.5171 11.3332 8.66634 11.1839 8.66634 10.9998V10.3332C8.66634 10.1491 8.5171 9.99984 8.33301 9.99984H7.66634Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1052_111876">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1022 B

View File

@ -63,33 +63,31 @@ const checkDialog = () => {
}
const handleMouseWheel = e => {
if (editMode.value === 'preview' || checkDialog()) {
if (
editMode.value === 'preview' ||
checkDialog() ||
(Math.abs(e.deltaX) !== 0 && Math.abs(e.deltaY) !== 0)
) {
return
}
let dvMain = document.getElementById('dv-main-center')
let dvMainLeftSlide = document.getElementById('dv-main-left-sidebar')
let areaLeftWidth = dvMainLeftSlide.clientWidth
let areaRight = dvMain.clientWidth + areaLeftWidth
if (areaLeftWidth < e.clientX && e.clientX < areaRight) {
const delta = e.wheelDelta ? e.wheelDelta : -e.detail
if ((lastWheelNum === 240 && delta === 240) || delta > 240) {
//
scaleIncrease(3)
} else if ((lastWheelNum === -240 && delta === -240) || delta < -240) {
//
if (e.ctrlKey) {
if (e.deltaY > 0) {
//
scaleDecrease(3)
}
if (delta >= 240 || delta <= -240) {
e.stopPropagation()
e.preventDefault()
}
lastWheelNum = delta
if (e.deltaY < 0) {
//
scaleIncrease(3)
e.stopPropagation()
e.preventDefault()
}
}
}
onMounted(() => {
window.addEventListener('mousewheel', handleMouseWheel, { passive: false })
window.addEventListener('wheel', handleMouseWheel, { passive: false })
setTimeout(() => {
scale.value = canvasStyleData.value.scale
nextTick(() => {
@ -99,7 +97,7 @@ onMounted(() => {
})
onUnmounted(() => {
window.removeEventListener('mousewheel', handleMouseWheel)
window.removeEventListener('wheel', handleMouseWheel)
})
</script>
<template>

View File

@ -216,11 +216,10 @@ watch(
)
watch(
() => areaData.value.components,
() => {
() => areaData.value.components.length,
(val, oldVal) => {
groupAreaClickChange()
},
{ deep: true }
}
)
const initWatermark = (waterDomId = 'editor-canvas-main') => {
@ -1062,8 +1061,8 @@ const clearInfoBox = e => {
const cellInit = () => {
// 1,why: x,y 使 style.left/cellWidth style.top/cellWidth
// () xy ,
cellWidth.value = Math.floor((baseWidth.value + baseMarginLeft.value) * 10) / 10
cellHeight.value = Math.floor((baseHeight.value + baseMarginTop.value) * 10) / 10
cellWidth.value = Math.floor((baseWidth.value + baseMarginLeft.value) * 1000) / 1000
cellHeight.value = Math.floor((baseHeight.value + baseMarginTop.value) * 1000) / 1000
}
const canvasSizeInit = () => {
@ -1359,9 +1358,9 @@ const contextMenuShow = computed(() => {
const markLineShow = computed(() => isMainCanvas(canvasId.value))
//
const groupAreaClickChange = () => {
const groupAreaClickChange = async () => {
let groupAreaCom
const groupAreaHis = componentData.value.filter(ele => ele.id === 100000001)
const groupAreaHis = dvMainStore.componentData.filter(ele => ele.component === 'GroupArea')
if (groupAreaHis && groupAreaHis.length > 0) {
groupAreaCom = groupAreaHis[0]
}
@ -1369,7 +1368,8 @@ const groupAreaClickChange = () => {
if (areaData.value.components.length > 1) {
//
composeStore.calcComposeArea()
if (!groupAreaCom) {
const hist2 = dvMainStore.componentData.filter(ele => ele.component === 'GroupArea')
if (groupAreaHis.length === 0) {
//
groupAreaCom = findNewComponent('GroupArea', 'GroupArea')
dvMainStore.addComponent({ component: groupAreaCom, index: undefined })
@ -1379,7 +1379,9 @@ const groupAreaClickChange = () => {
groupAreaCom.style.width = areaData.value.style.width
groupAreaCom.style.height = areaData.value.style.height
} else if (groupAreaCom) {
dvMainStore.deleteComponentById(100000001)
groupAreaHis.forEach(ele => {
dvMainStore.deleteComponentById(ele.id)
})
}
}

View File

@ -231,7 +231,8 @@
>
<span class="custom-option">
<Icon
:icon-class="item.type"
:name="item.type"
class-name="view-type-icon"
style="width: 14px; height: 14px"
/>
<span style="float: left; margin-left: 4px; font-size: 14px">{{
@ -245,7 +246,7 @@
<el-select
v-model="targetViewInfo.targetFieldId"
:placeholder="'请选择字段'"
:disabled="!targetViewInfo.sourceFieldActiveId"
:disabled="fieldIdDisabledCheck(targetViewInfo)"
style="width: 100%"
>
<el-option
@ -405,7 +406,7 @@ import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapsho
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
import { filterEmptyFolderTree } from '@/utils/canvasUtils'
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, canvasViewInfo } = storeToRefs(dvMainStore)
const { dvInfo, canvasViewInfo, componentData } = storeToRefs(dvMainStore)
const linkJumpInfoTree = ref(null)
const { t } = useI18n()
const dialogShow = ref(false)
@ -643,12 +644,28 @@ const codeMirrorContentSet = content => {
const getPanelViewList = dvId => {
viewTableDetailList(dvId).then(rsp => {
state.viewIdFieldArrayMap = {}
state.currentLinkPanelViewArray = rsp.data
state.currentLinkPanelViewArray = rsp.data.visualizationViewTables
if (state.currentLinkPanelViewArray) {
state.currentLinkPanelViewArray.forEach(view => {
state.viewIdFieldArrayMap[view.id] = view.tableFields
})
}
//
JSON.parse(rsp.data.bashComponentData).forEach(componentItem => {
if (componentItem.component === 'VQuery') {
componentItem.propValue.forEach(filterItem => {
state.currentLinkPanelViewArray.push({
id: filterItem.id,
type: 'filter',
name: filterItem.name,
title: filterItem.name
})
state.viewIdFieldArrayMap[filterItem.id] = [
{ id: '1000001', name: t('visualization.filter_no_select') }
]
})
}
})
})
}
const dvNodeClick = data => {
@ -667,8 +684,26 @@ const addLinkJumpField = () => {
const deleteLinkJumpField = index => {
state.linkJumpInfo.targetViewInfoList.splice(index, 1)
}
const fieldIdDisabledCheck = targetViewInfo => {
return (
(state.viewIdFieldArrayMap[targetViewInfo.targetViewId] &&
state.viewIdFieldArrayMap[targetViewInfo.targetViewId].length === 1 &&
state.viewIdFieldArrayMap[targetViewInfo.targetViewId][0].id === '1000001') ||
!targetViewInfo.sourceFieldActiveId
)
}
const viewInfoOnChange = targetViewInfo => {
targetViewInfo.targetFieldId = null
if (
state.viewIdFieldArrayMap[targetViewInfo.targetViewId] &&
state.viewIdFieldArrayMap[targetViewInfo.targetViewId].length === 1 &&
state.viewIdFieldArrayMap[targetViewInfo.targetViewId][0].id === '1000001'
) {
targetViewInfo.targetFieldId = '1000001'
} else {
targetViewInfo.targetFieldId = null
}
}
const sourceFieldCheckedChange = data => {
nextTick(() => {

View File

@ -19,10 +19,13 @@
:title="t('chart.text_color')"
v-model="styleForm[styleColorKey.value]"
class="color-picker-style"
:prefix-icon="expandIcon(styleColorKey.icon)"
:triggerWidth="styleColorKey.width"
is-custom
:predefine="state.predefineColors"
@change="changeStyle"
/>
>
</el-color-picker>
</el-form-item>
</el-tooltip>
</template>
@ -41,12 +44,17 @@
:class="'form-item-' + themes"
>
<el-select
style="width: 50px"
:style="{ width: styleOptionMountedKey.width }"
:effect="themes"
v-model="styleMounted[styleOptionMountedKey.value]"
size="small"
@change="sizeChange(styleOptionMountedKey.value)"
>
<template #prefix>
<el-icon :class="{ 'dark-icon': themes === 'dark' }">
<Icon :name="styleOptionMountedKey.icon" />
</el-icon>
</template>
<el-option
class="custom-style-option"
v-for="option in styleOptionMountedKey.customOption"
@ -72,10 +80,14 @@
:style="{ width: styleOptionKey.width }"
:effect="themes"
v-model="styleForm[styleOptionKey.value]"
placeholder="透明度"
size="small"
@change="changeStyle"
>
<template #prefix>
<el-icon>
<Icon :name="styleOptionKey.icon" />
</el-icon>
</template>
<el-option
class="custom-style-option"
v-for="option in styleOptionKey.customOption"
@ -118,48 +130,50 @@
</el-tooltip>
<template v-if="styleForm.textAlign">
<div class="m-divider" :class="'custom-divider-' + themes"></div>
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_left') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'left' }"
@click="setPosition('textAlign', 'left')"
>
<el-icon>
<Icon name="icon_left-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_center') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'center' }"
@click="setPosition('textAlign', 'center')"
>
<el-icon>
<Icon name="icon_center-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_right') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'right' }"
@click="setPosition('textAlign', 'right')"
>
<el-icon>
<Icon name="icon_right-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
<div style="display: flex">
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_left') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'left' }"
@click="setPosition('textAlign', 'left')"
>
<el-icon>
<Icon name="icon_left-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_center') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'center' }"
@click="setPosition('textAlign', 'center')"
>
<el-icon>
<Icon name="icon_center-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<template #content>
{{ t('chart.text_pos_right') }}
</template>
<div
class="icon-btn"
:class="{ dark: themes === 'dark', active: styleForm.textAlign === 'right' }"
@click="setPosition('textAlign', 'right')"
>
<el-icon>
<Icon name="icon_right-alignment_outlined" />
</el-icon>
</div>
</el-tooltip>
</div>
</template>
<template v-if="styleForm.headHorizontalPosition">
<div class="m-divider"></div>
@ -220,8 +234,8 @@
</el-row>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, toRefs, watch } from 'vue'
<script lang="tsx" setup>
import { computed, h, reactive, ref, toRefs, watch } from 'vue'
import { COLOR_PANEL } from '@/views/chart/components/editor/util/chart'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { useI18n } from '@/hooks/web/useI18n'
@ -243,7 +257,9 @@ const props = withDefaults(
themes: 'dark'
}
)
const expandIcon = (name: string) => {
return h(Icon, { className: '', name })
}
const { themes, element } = toRefs(props)
const emits = defineEmits(['onTextChange'])
const styleMounted = ref({
@ -277,11 +293,21 @@ const state = reactive({
})
const styleColorKeyArray = [
{ value: 'color', label: '颜色' },
{ value: 'borderColor', label: '边框颜色' },
{ value: 'headFontColor', label: '头部字体颜色' },
{ value: 'headFontActiveColor', label: '激活字体颜色' },
{ value: 'backgroundColor', label: '背景色' }
{ value: 'color', label: '颜色', width: 90, icon: 'dv-style-color' },
{ value: 'borderColor', label: '边框颜色', width: 90, icon: 'dv-style-borderColor' },
{
value: 'headFontColor',
label: '头部字体颜色',
width: 90,
icon: 'dv-style-headFontColor'
},
{
value: 'headFontActiveColor',
label: '激活字体颜色',
width: 90,
icon: 'dv-style-headFontActiveColor'
},
{ value: 'backgroundColor', label: '背景色', width: 90, icon: 'dv-style-backgroundColor' }
]
const fontSizeList = computed(() => {
@ -325,16 +351,52 @@ const borderStyleList = [
//
const styleOptionMountedKeyArray = [
{ value: 'fontSize', label: '字体大小', customOption: fontSizeList.value },
{ value: 'activeFontSize', label: '激活字体大小', customOption: fontSizeList.value }
{
value: 'fontSize',
label: '字体大小',
customOption: fontSizeList.value,
width: '90px',
icon: 'dv-style-fontSize'
},
{
value: 'activeFontSize',
label: '激活字体大小',
customOption: fontSizeList.value,
width: '90px',
icon: 'dv-style-activeFont'
}
]
//
const styleOptionKeyArray = [
{ value: 'opacity', label: '透明度', customOption: opacitySizeList, width: '50px' },
{ value: 'borderWidth', label: '边框宽度', customOption: borderWidthList.value, width: '50px' },
{ value: 'borderRadius', label: '圆角', customOption: borderRadiusList.value, width: '50px' },
{ value: 'borderStyle', label: '边框样式', customOption: borderStyleList, width: '60px' }
{
value: 'opacity',
label: '透明度',
customOption: opacitySizeList,
width: '90px',
icon: 'dv-style-opacity'
},
{
value: 'borderWidth',
label: '边框宽度',
customOption: borderWidthList.value,
width: '90px',
icon: 'dv-style-borderSize'
},
{
value: 'borderRadius',
label: '圆角',
customOption: borderRadiusList.value,
width: '90px',
icon: 'dv-style-borderRadius'
},
{
value: 'borderStyle',
label: '边框样式',
customOption: borderStyleList,
width: '90px',
icon: 'dv-style-borderStyle'
}
]
const styleInit = () => {
@ -514,4 +576,8 @@ watch(
.custom-row-inner {
margin: 8px 0px 24px;
}
.dark-icon {
color: #ffffff;
}
</style>

View File

@ -264,16 +264,18 @@ const componentMoveIn = component => {
dvMainStore.setCurComponent({ component: null, index: null })
component.canvasId = element.value.id + '--' + tabItem.name
const refInstance = currentInstance.refs['tabCanvas_' + index][0]
const matrixBase = refInstance.getBaseMatrixSize() //
canvasChangeAdaptor(component, matrixBase)
tabItem.componentData.push(component)
nextTick(() => {
component.x = 1
component.y = 1
component.style.left = 0
component.style.top = 0
refInstance.addItemBox(component) //
})
if (refInstance) {
const matrixBase = refInstance.getBaseMatrixSize() //
canvasChangeAdaptor(component, matrixBase)
tabItem.componentData.push(component)
nextTick(() => {
component.x = 1
component.y = 1
component.style.left = 0
component.style.top = 0
refInstance.addItemBox(component) //
})
}
}
})
}

View File

@ -7,7 +7,7 @@ import { deepCopy } from '@/utils/utils'
import { cloneDeep, defaultsDeep, defaultTo } from 'lodash-es'
import {
BASE_VIEW_CONFIG,
CHART_CONT_FAMILY_MAP,
CHART_FONT_FAMILY_MAP,
DEFAULT_INDICATOR_NAME_STYLE,
DEFAULT_INDICATOR_STYLE
} from '@/views/chart/components/editor/util/chart'
@ -170,7 +170,7 @@ const formattedResult = computed(() => {
const emit = defineEmits(['onChartClick', 'onDrillFilters', 'onJumpClick'])
const contentStyle = ref({
const contentStyle = ref<CSSProperties>({
display: 'flex',
'flex-direction': 'column',
'align-items': 'center',
@ -182,7 +182,7 @@ const indicatorClass = ref<CSSProperties>({
color: thresholdColor.value,
'font-size': DEFAULT_INDICATOR_STYLE.fontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.fontFamily],
CHART_FONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.fontFamily],
DEFAULT_INDICATOR_STYLE.fontFamily
),
'font-weight': DEFAULT_INDICATOR_STYLE.isBolder ? 'bold' : 'normal',
@ -196,7 +196,7 @@ const indicatorSuffixClass = ref<CSSProperties>({
color: DEFAULT_INDICATOR_STYLE.suffixColor,
'font-size': DEFAULT_INDICATOR_STYLE.suffixFontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.suffixFontFamily],
CHART_FONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.suffixFontFamily],
DEFAULT_INDICATOR_STYLE.suffixFontFamily
),
'font-weight': DEFAULT_INDICATOR_STYLE.suffixIsBolder ? 'bold' : 'normal',
@ -212,11 +212,15 @@ const suffixContent = ref('')
const indicatorNameShow = ref(false)
const indicatorNameWrapperStyle = reactive<CSSProperties>({
'margin-top': DEFAULT_INDICATOR_NAME_STYLE.nameValueSpacing + 'px'
})
const indicatorNameClass = ref<CSSProperties>({
color: DEFAULT_INDICATOR_NAME_STYLE.color,
'font-size': DEFAULT_INDICATOR_NAME_STYLE.fontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[DEFAULT_INDICATOR_NAME_STYLE.fontFamily],
CHART_FONT_FAMILY_MAP[DEFAULT_INDICATOR_NAME_STYLE.fontFamily],
DEFAULT_INDICATOR_NAME_STYLE.fontFamily
),
'font-weight': DEFAULT_INDICATOR_NAME_STYLE.isBolder ? 'bold' : 'normal',
@ -237,16 +241,16 @@ const renderChart = async view => {
const chart = deepCopy({
...defaultsDeep(view, TEMP_DEFAULT_CHART),
data: chartData.value
})
}) as ChartObj
recursionTransObj(customAttrTrans, chart.customAttr, scale.value, terminal.value)
recursionTransObj(customStyleTrans, chart.customStyle, scale.value, terminal.value)
if (chart.customAttr) {
const customAttr = chart.customAttr
const { indicator, indicatorName, basicStyle } = chart.customAttr
if (customAttr.indicator) {
switch (customAttr.indicator.hPosition) {
if (indicator) {
switch (indicator.hPosition) {
case 'left':
contentStyle.value['align-items'] = 'flex-start'
break
@ -256,7 +260,7 @@ const renderChart = async view => {
default:
contentStyle.value['align-items'] = 'center'
}
switch (customAttr.indicator.vPosition) {
switch (indicator.vPosition) {
case 'top':
contentStyle.value['justify-content'] = 'flex-start'
break
@ -267,73 +271,68 @@ const renderChart = async view => {
contentStyle.value['justify-content'] = 'center'
}
indicatorColor.value = customAttr.indicator.color
let suffixColor = customAttr.indicator.suffixColor
indicatorColor.value = indicator.color
let suffixColor = indicator.suffixColor
if (
customAttr.basicStyle &&
customAttr.basicStyle.alpha !== undefined &&
!batchOptStatus.value
) {
indicatorColor.value = hexColorToRGBA(
customAttr.basicStyle.colors[0],
customAttr.basicStyle.alpha
)
suffixColor = hexColorToRGBA(customAttr.basicStyle.colors[1], customAttr.basicStyle.alpha)
if (basicStyle?.alpha !== undefined && !batchOptStatus.value) {
indicatorColor.value = hexColorToRGBA(basicStyle.colors[0], basicStyle.alpha)
suffixColor = hexColorToRGBA(basicStyle.colors[1], basicStyle.alpha)
}
indicatorClass.value = {
color: thresholdColor.value,
'font-size': customAttr.indicator.fontSize + 'px',
'font-size': indicator.fontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[customAttr.indicator.fontFamily],
CHART_FONT_FAMILY_MAP[indicator.fontFamily],
DEFAULT_INDICATOR_STYLE.fontFamily
),
'font-weight': customAttr.indicator.isBolder ? 'bold' : 'normal',
'font-style': customAttr.indicator.isItalic ? 'italic' : 'normal',
'letter-spacing': customAttr.indicator.letterSpace + 'px',
'text-shadow': customAttr.indicator.fontShadow ? '2px 2px 4px' : 'none',
'font-weight': indicator.isBolder ? 'bold' : 'normal',
'font-style': indicator.isItalic ? 'italic' : 'normal',
'letter-spacing': indicator.letterSpace + 'px',
'text-shadow': indicator.fontShadow ? '2px 2px 4px' : 'none',
'font-synthesis': 'weight style'
}
indicatorSuffixClass.value = {
color: suffixColor,
'font-size': customAttr.indicator.suffixFontSize + 'px',
'font-size': indicator.suffixFontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[customAttr.indicator.suffixFontFamily],
CHART_FONT_FAMILY_MAP[indicator.suffixFontFamily],
DEFAULT_INDICATOR_STYLE.suffixFontFamily
),
'font-weight': customAttr.indicator.suffixIsBolder ? 'bold' : 'normal',
'font-style': customAttr.indicator.suffixIsItalic ? 'italic' : 'normal',
'letter-spacing': customAttr.indicator.suffixLetterSpace + 'px',
'text-shadow': customAttr.indicator.suffixFontShadow ? '2px 2px 4px' : 'none',
'font-weight': indicator.suffixIsBolder ? 'bold' : 'normal',
'font-style': indicator.suffixIsItalic ? 'italic' : 'normal',
'letter-spacing': indicator.suffixLetterSpace + 'px',
'text-shadow': indicator.suffixFontShadow ? '2px 2px 4px' : 'none',
'font-synthesis': 'weight style'
}
showSuffix.value = customAttr.indicator.suffixEnable
suffixContent.value = defaultTo(customAttr.indicator.suffix, '')
showSuffix.value = indicator.suffixEnable
suffixContent.value = defaultTo(indicator.suffix, '')
}
if (customAttr.indicatorName && customAttr.indicatorName.show) {
let nameColor = customAttr.indicatorName.color
if (indicatorName?.show) {
let nameColor = indicatorName.color
if (customAttr.basicStyle && customAttr.basicStyle.alpha !== undefined) {
nameColor = hexColorToRGBA(customAttr.basicStyle.colors[2], customAttr.basicStyle.alpha)
if (basicStyle?.alpha !== undefined) {
nameColor = hexColorToRGBA(basicStyle.colors[2], basicStyle.alpha)
}
indicatorNameShow.value = true
indicatorNameClass.value = {
color: nameColor,
'font-size': customAttr.indicatorName.fontSize + 'px',
'font-size': indicatorName.fontSize + 'px',
'font-family': defaultTo(
CHART_CONT_FAMILY_MAP[customAttr.indicatorName.fontFamily],
CHART_FONT_FAMILY_MAP[indicatorName.fontFamily],
DEFAULT_INDICATOR_NAME_STYLE.fontFamily
),
'font-weight': customAttr.indicatorName.isBolder ? 'bold' : 'normal',
'font-style': customAttr.indicatorName.isItalic ? 'italic' : 'normal',
'letter-spacing': customAttr.indicatorName.letterSpace + 'px',
'text-shadow': customAttr.indicatorName.fontShadow ? '2px 2px 4px' : 'none',
'font-weight': indicatorName.isBolder ? 'bold' : 'normal',
'font-style': indicatorName.isItalic ? 'italic' : 'normal',
'letter-spacing': indicatorName.letterSpace + 'px',
'text-shadow': indicatorName.fontShadow ? '2px 2px 4px' : 'none',
'font-synthesis': 'weight style'
}
indicatorNameWrapperStyle['margin-top'] =
(indicatorName.nameValueSpacing ?? DEFAULT_INDICATOR_NAME_STYLE.nameValueSpacing) + 'px'
} else {
indicatorNameShow.value = false
}
@ -363,9 +362,6 @@ const calcData = (view, callback) => {
callback?.()
})
} else {
if (view.type === 'map') {
renderChart(view)
}
callback?.()
}
}
@ -382,7 +378,7 @@ defineExpose({
<span :style="indicatorClass">{{ formattedResult }}</span>
<span :style="indicatorSuffixClass" v-if="showSuffix">{{ suffixContent }}</span>
</div>
<div v-if="indicatorNameShow">
<div :style="indicatorNameWrapperStyle" v-if="indicatorNameShow">
<span :style="indicatorNameClass">{{ resultName }}</span>
</div>
</div>

View File

@ -10,9 +10,10 @@
<chart-error v-if="isError" :err-msg="errMsg" />
<Editor
v-if="editShow && !isError"
:id="tinymceId"
v-model="myValue"
class="custom-text-content"
:style="wrapperStyle"
:id="tinymceId"
:init="init"
:disabled="!canEdit || disabled"
/>
@ -46,6 +47,7 @@ import 'tinymce/plugins/contextmenu' // contextmenu
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/pagebreak'
import './plugins' //
import { computed, nextTick, reactive, ref, toRefs, watch, onMounted, PropType } from 'vue'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import eventBus from '@/utils/eventBus'
@ -119,11 +121,11 @@ const init = ref({
skin_url: formatDataEaseBi('./tinymce-dataease-private/skins/ui/oxide'), //
content_css: formatDataEaseBi('./tinymce-dataease-private/skins/content/default/content.css'),
plugins:
'advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', //
'vertical-content advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', //
//
toolbar:
'undo redo |fontselect fontsizeselect |forecolor backcolor bold italic |underline strikethrough link| formatselect |' +
'alignleft aligncenter alignright | bullist numlist |' +
'top-align center-align bottom-align | alignleft aligncenter alignright | bullist numlist |' +
' blockquote subscript superscript removeformat | table image | fullscreen ' +
'| bdmap indent2em lineheight formatpainter axupimgs',
toolbar_location: '/',
@ -134,7 +136,9 @@ const init = ref({
placeholder: '',
outer_placeholder: '双击输入文字',
inline: true, //
branding: false
branding: false,
icons: 'vertical-content',
vertical_align: element.value.propValue.verticalAlign
})
const editStatus = computed(() => {
@ -169,6 +173,36 @@ watch(
}
}
)
const ALIGN_MAP = {
'top-align': {
display: 'flex',
'flex-direction': 'column',
'justify-content': 'flex-start'
},
'center-align': {
display: 'flex',
'flex-direction': 'column',
'justify-content': 'center'
},
'bottom-align': {
display: 'flex',
'flex-direction': 'column',
'justify-content': 'flex-end'
}
}
const wrapperStyle = computed(() => {
const align = element.value.propValue.verticalAlign
if (!align) {
return {}
}
return ALIGN_MAP[align]
})
useEmitt({
name: 'vertical-change-' + tinymceId,
callback: align => {
element.value.propValue.verticalAlign = align
}
})
const viewInit = () => {
useEmitt({
@ -496,6 +530,14 @@ defineExpose({
</style>
<style lang="less">
.tox {
border-radius: 4px !important;
border-bottom: 1px solid #ccc !important;
z-index: 1000;
}
.tox-tbtn {
height: auto !important;
}
.tox-collection__item-label {
p {
color: #1a1a1a !important;

View File

@ -0,0 +1,11 @@
import tinymce from 'tinymce/tinymce'
const plugins = import.meta.glob(['./*.ts', '!./index.ts'], { eager: true })
for (const pluginName in plugins) {
const plugin = plugins[pluginName]['default']
const exist = tinymce.PluginManager.get(plugin.name)
if (exist) {
continue
}
tinymce.PluginManager.add(plugin.name, plugin.plugin)
}

View File

@ -0,0 +1,87 @@
import { type Editor } from 'tinymce'
import tinymce from 'tinymce/tinymce'
import { useEmitt } from '@/hooks/web/useEmitt'
const { emitter } = useEmitt()
const TOP_ALIGN_BTN =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M2.5 3C2.22386 3 2 3.22386 2 3.5V4.5C2 4.77614 2.22386 5 2.5 5H21.5C21.7761 5 22 4.77614 22 4.5V3.5C22 3.22386 21.7761 3 21.5 3H2.5Z" fill="currentColor"/>' +
'<path d="M6.19133 14.4465L11 9.63788V21.467C11 21.7615 11.2388 22.0003 11.5333 22.0003H12.4667C12.7612 22.0003 13 21.7615 13 21.467V9.61452L17.832 14.4465C18.0403 14.6548 18.378 14.6548 18.5863 14.4465L19.2462 13.7866C19.4545 13.5783 19.4545 13.2406 19.2462 13.0323L12.458 6.2441C12.3362 6.12232 12.1702 6.07174 12.0117 6.09237C11.8531 6.07174 11.6871 6.12232 11.5653 6.2441L4.77712 13.0323C4.56884 13.2406 4.56884 13.5783 4.77712 13.7866L5.43709 14.4465C5.64537 14.6548 5.98305 14.6548 6.19133 14.4465Z" fill="currentColor"/>' +
'</svg>'
const CENTER_ALIGN_BTN =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M11 6.36207L9.19133 4.5534C8.98305 4.34513 8.64537 4.34513 8.43709 4.55341L7.77712 5.21337C7.56884 5.42165 7.56884 5.75934 7.77712 5.96762L11.5653 9.75584C11.6871 9.87762 11.8531 9.9282 12.0117 9.90757C12.1702 9.9282 12.3362 9.87762 12.458 9.75584L16.2462 5.96762C16.4545 5.75934 16.4545 5.42165 16.2462 5.21337L15.5863 4.55341C15.378 4.34513 15.0403 4.34513 14.832 4.5534L13 6.38542V1.53297C13 1.23841 12.7612 0.999634 12.4667 0.999634H11.5333C11.2388 0.999634 11 1.23841 11 1.53297V6.36207Z" fill="currentColor"/>' +
'<path d="M11 17.5499L9.19133 19.3586C8.98305 19.5669 8.64537 19.5669 8.43709 19.3586L7.77712 18.6986C7.56884 18.4903 7.56884 18.1527 7.77712 17.9444L11.5653 14.1562C11.6871 14.0344 11.8531 13.9838 12.0117 14.0044C12.1702 13.9838 12.3362 14.0344 12.458 14.1562L16.2462 17.9444C16.4545 18.1527 16.4545 18.4903 16.2462 18.6986L15.5863 19.3586C15.378 19.5669 15.0403 19.5669 14.832 19.3586L13 17.5266V22.379C13 22.6736 12.7612 22.9124 12.4667 22.9124H11.5333C11.2388 22.9124 11 22.6736 11 22.379V17.5499Z" fill="currentColor"/>' +
'<path d="M2.5 10.9999C2.22386 10.9999 2 11.2238 2 11.4999V12.4999C2 12.7761 2.22386 12.9999 2.5 12.9999H21.5C21.7761 12.9999 22 12.7761 22 12.4999V11.4999C22 11.2238 21.7761 10.9999 21.5 10.9999H2.5Z" fill="currentColor"/>' +
'</svg>'
const BOTTOM_ALIGN_BTN =
'<svg t="1713518245725" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17410" width="24" height="24">' +
'<path d="M508.842667 42.666667a42.666667 42.666667 0 0 1 42.666666 42.666666V725.333333a42.666667 42.666667 0 0 1-85.333333 0l0-640a42.666667 42.666667 0 0 1 42.666667-42.666666z" fill="#373C43" p-id="17411"></path><path d="M502.314667 707.84l275.541333-266.069333a42.666667 42.666667 0 1 1 59.306667 61.44L531.029333 798.634667a42.538667 42.538667 0 0 1-60.16-0.853334l-291.242666-298.666666a42.666667 42.666667 0 0 1 61.098666-59.605334l261.589334 268.245334z" fill="#373C43" p-id="17412"></path><path d="M935.509333 981.333333m-42.666666 0l-768 0q-42.666667 0-42.666667-42.666666l0 0q0-42.666667 42.666667-42.666667l768 0q42.666667 0 42.666666 42.666667l0 0q0 42.666667-42.666666 42.666666Z" fill="#8D9399" p-id="17413">' +
'</path>' +
'</svg>'
const pack = tinymce.IconManager.has('vertical-content')
if (!pack) {
tinymce.IconManager.add('vertical-content', {
icons: {
'top-align': TOP_ALIGN_BTN,
'center-align': CENTER_ALIGN_BTN,
'bottom-align': BOTTOM_ALIGN_BTN
}
})
}
export default {
name: 'vertical-content',
plugin: function (editor: Editor) {
const wrapperDom = editor.targetElm
const verticalAlign = editor.settings.vertical_align
const btnMap = {
'top-align': {
component: null,
tooltip: '置顶'
},
'center-align': {
component: null,
tooltip: '居中'
},
'bottom-align': {
component: null,
tooltip: '置底'
}
}
for (const key in btnMap) {
editor.ui.registry.addToggleButton(key, {
icon: key,
tooltip: btnMap[key].tooltip,
onAction: api => {
for (const btnKey in btnMap) {
if (btnKey === key) {
// 反选清空样式
const align = api.isActive() ? '' : key
emitter.emit('vertical-change-' + wrapperDom.id, align)
btnMap[key].component.setActive(!api.isActive())
} else {
const active = btnMap[btnKey].component.isActive()
if (active) {
btnMap[btnKey].component.setActive(false)
}
}
}
},
onSetup(a) {
if (verticalAlign === key) {
a.setActive(true)
}
return api => (btnMap[key].component = api)
}
})
}
return {
getMetadata: function () {
return {
name: 'Vertical align',
url: 'https://dataease.io'
}
}
}
}
}

View File

@ -13,7 +13,10 @@ import {
watch,
computed,
onMounted,
CSSProperties
onBeforeMount,
CSSProperties,
shallowRef,
provide
} from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from '@/hooks/web/useI18n'
@ -156,7 +159,52 @@ const onComponentClick = () => {
}
const { emitter } = useEmitt()
const unMountSelect = shallowRef([])
onBeforeMount(() => {
unMountSelect.value = list.value.map(ele => ele.id)
})
const releaseSelect = id => {
unMountSelect.value = unMountSelect.value.filter(ele => ele !== id)
}
const queryDataForId = id => {
let requiredName = ''
const emitterList = (element.value.propValue || [])
.filter(ele => ele.id === id)
.reduce((pre, next) => {
if (next.required) {
if (!next.defaultValueCheck) {
requiredName = next.name
}
if (
(Array.isArray(next.selectValue) && !next.selectValue.length) ||
(next.selectValue !== 0 && !next.selectValue)
) {
requiredName = next.name
}
}
const keyList = Object.entries(next.checkedFieldsMap)
.filter(ele => next.checkedFields.includes(ele[0]))
.filter(ele => !!ele[1])
.map(ele => ele[0])
pre = [...new Set([...keyList, ...pre])]
return pre
}, [])
if (!!requiredName) {
ElMessage.error(`${requiredName}】查询条件是必填项,请设置选项值后,再进行查询!`)
return
}
if (!emitterList.length) return
emitterList.forEach(ele => {
emitter.emit(`query-data-${ele}`)
})
}
provide('unmount-select', unMountSelect)
provide('release-unmount-select', releaseSelect)
provide('query-data-for-id', queryDataForId)
onBeforeUnmount(() => {
emitter.off(`addQueryCriteria${element.value.id}`)
emitter.off(`editQueryCriteria${element.value.id}`)
@ -245,10 +293,20 @@ const delQueryConfig = index => {
const resetData = () => {
;(list.value || []).reduce((pre, next) => {
next.conditionValueF = next.defaultConditionValueF
next.conditionValueOperatorF = next.defaultConditionValueOperatorF
next.conditionValueS = next.defaultConditionValueS
next.conditionValueOperatorS = next.defaultConditionValueOperatorS
if (!next.defaultValueCheck) {
next.defaultValue = next.multiple || +next.displayType === 7 ? [] : undefined
}
next.selectValue = Array.isArray(next.defaultValue) ? [...next.defaultValue] : next.defaultValue
if (next.optionValueSource === 1 && next.defaultMapValue?.length) {
next.mapValue = Array.isArray(next.defaultMapValue)
? [...next.defaultMapValue]
: next.defaultMapValue
}
const keyList = Object.entries(next.checkedFieldsMap)
.filter(ele => next.checkedFields.includes(ele[0]))
.filter(ele => !!ele[1])
@ -261,6 +319,11 @@ const resetData = () => {
const clearData = () => {
;(list.value || []).reduce((pre, next) => {
next.selectValue = next.multiple || +next.displayType === 7 ? [] : undefined
if (next.optionValueSource === 1 && next.defaultMapValue?.length) {
next.mapValue = next.multiple ? [] : undefined
}
next.conditionValueF = ''
next.conditionValueS = ''
const keyList = Object.entries(next.checkedFieldsMap)
.filter(ele => next.checkedFields.includes(ele[0]))
.filter(ele => !!ele[1])
@ -557,7 +620,7 @@ const autoStyle = computed(() => {
}
.query-button {
align-self: flex-end;
line-height: 28px;
line-height: 40px;
margin: auto 0 5px auto;
z-index: 0;
}

View File

@ -248,6 +248,9 @@ const handleDatasetChange = () => {
const handleFieldChange = () => {
if (!curComponent.value.defaultValueCheck) return
curComponent.value.defaultValue = curComponent.value.multiple ? [] : undefined
if (!curComponent.value.displayId) {
curComponent.value.displayId = curComponent.value.field.id
}
}
const handleValueSourceChange = () => {
@ -545,6 +548,7 @@ const validate = () => {
const handleBeforeClose = () => {
inputCom.value?.mult?.handleClickOutside?.()
inputCom.value?.single?.handleClickOutside?.()
handleDialogClick()
dialogVisible.value = false
visiblePopover.value = false
@ -553,6 +557,8 @@ const handleBeforeClose = () => {
const confirmClick = () => {
if (validate()) return
inputCom.value?.mult?.handleClickOutside?.()
inputCom.value?.single?.handleClickOutside?.()
handleDialogClick()
visiblePopover.value = false
dialogVisible.value = false
conditions.value.forEach(ele => {
@ -675,6 +681,7 @@ const parameterCompletion = () => {
timeType: 'fixed',
required: false,
defaultMapValue: [],
mapValue: [],
parametersStart: null,
conditionType: 0,
conditionValueOperatorF: 'eq',
@ -781,7 +788,7 @@ const handleDialogClick = () => {
const operators = [
{
label: '精匹配',
label: '精匹配',
value: 'eq'
},
{
@ -1569,7 +1576,6 @@ defineExpose({
</el-select>
<el-input
class="condition-value-input"
size="small"
v-model="curComponent.defaultConditionValueF"
/>
<div class="bottom-line"></div>
@ -1593,7 +1599,6 @@ defineExpose({
</el-select>
<el-input
class="condition-value-input"
size="small"
v-model="curComponent.defaultConditionValueS"
/>
<div class="bottom-line next-line"></div>
@ -1654,7 +1659,10 @@ defineExpose({
>
</div>
</div>
<div class="list-item" v-if="+curComponent.displayType === 0">
<div
class="list-item"
v-if="+curComponent.displayType === 0 && curComponent.optionValueSource !== 1"
>
<div class="label">
<el-tooltip
effect="dark"
@ -2219,7 +2227,7 @@ defineExpose({
.value {
width: 321px;
.condition-type {
margin-top: 8px;
margin-top: 3px !important;
display: flex;
position: relative;
.ed-input__wrapper {
@ -2271,7 +2279,7 @@ defineExpose({
opacity: 0.3;
position: absolute;
right: 5px;
bottom: 5px;
bottom: 3px;
width: 220px;
z-index: 10;

View File

@ -1,11 +1,23 @@
<script lang="ts" setup>
import { ref, toRefs, PropType, onBeforeMount, shallowRef, watch, nextTick, computed } from 'vue'
import {
ref,
toRefs,
PropType,
onBeforeMount,
shallowRef,
watch,
nextTick,
computed,
inject,
Ref
} from 'vue'
import { enumValueObj, type EnumValue, getEnumValue } from '@/api/dataset'
import { cloneDeep, debounce } from 'lodash-es'
interface SelectConfig {
selectValue: any
defaultMapValue: any
mapValue: any
defaultValue: any
checkedFieldsMap: object
displayType: string
@ -53,7 +65,9 @@ const selectValue = ref()
const loading = ref(false)
const multiple = ref(false)
const options = shallowRef([])
const unMountSelect: Ref = inject('unmount-select')
const releaseSelect = inject('release-unmount-select', Function, true)
const queryDataForId = inject('query-data-for-id', Function, true)
const setDefaultMapValue = arr => {
const { displayId, field } = config.value
if (!displayId || displayId === field?.id) {
@ -81,13 +95,16 @@ const handleValueChange = () => {
config.value.selectValue = Array.isArray(selectValue.value)
? [...selectValue.value]
: selectValue.value
config.value.defaultMapValue = setDefaultMapValue(
config.value.mapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
return
}
config.value.defaultValue = value
config.value.mapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
config.value.defaultMapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
@ -152,12 +169,29 @@ const handleFieldIdChange = (val: EnumValue) => {
selectValue.value = Array.isArray(config.value.defaultValue)
? [...config.value.defaultValue]
: config.value.defaultValue
let shouldReSearch = false
if (unMountSelect.value.includes(config.value.id)) {
const mapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
if (mapValue.length !== config.value.defaultMapValue.length) {
shouldReSearch = true
} else if (!mapValue.every(value => config.value.defaultMapValue.includes(value))) {
shouldReSearch = true
}
releaseSelect(config.value.id)
}
config.value.mapValue = setDefaultMapValue(
Array.isArray(selectValue.value) ? [...selectValue.value] : [selectValue.value]
)
if (shouldReSearch) {
queryDataForId(config.value.id)
}
} else {
selectValue.value = Array.isArray(selectValue.value)
? [...selectValue.value]
: selectValue.value
}
setEmptyData()
})
}
@ -176,8 +210,8 @@ watch(
)
const setEmptyData = () => {
const { showEmpty, displayType } = config.value
if (+displayType !== 0) return
const { showEmpty, displayType, optionValueSource } = config.value
if (+displayType !== 0 || optionValueSource === 1) return
const [s] = options.value
if (showEmpty) {
if (s?.value !== '_empty_$') {
@ -268,6 +302,7 @@ watch(
watch(
[() => config.value.checkedFields, () => config.value.checkedFieldsMap],
() => {
if (!props.isConfig) return
debounceOptions(config.value.optionValueSource)
},
{
@ -350,6 +385,7 @@ const selectStyle = computed(() => {
})
const mult = ref()
const single = ref()
onBeforeMount(() => {
init()
@ -357,7 +393,8 @@ onBeforeMount(() => {
defineExpose({
displayTypeChange,
mult
mult,
single
})
</script>
@ -390,6 +427,7 @@ defineExpose({
v-loading="loading"
@change="handleValueChange"
clearable
ref="single"
:style="selectStyle"
filterable
radio

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { toRefs, onBeforeMount, type PropType } from 'vue'
import { toRefs, onBeforeMount, type PropType, inject, type CSSProperties } from 'vue'
interface SelectConfig {
conditionValueOperatorF: string
conditionValueF: string
@ -14,7 +14,7 @@ interface SelectConfig {
const operators = [
{
label: '精匹配',
label: '精匹配',
value: 'eq'
},
{
@ -57,10 +57,11 @@ const setParams = () => {
onBeforeMount(() => {
setParams()
})
const customStyle = inject<{ background: string }>('$custom-style-filter')
</script>
<template>
<div class="text-search-select">
<div class="text-search-select" :style="{ background: customStyle.background }">
<div class="condition-type">
<el-select
class="condition-value-select"
@ -143,7 +144,7 @@ onBeforeMount(() => {
opacity: 0.3;
position: absolute;
right: 5px;
bottom: 5px;
bottom: 3px;
width: 195px;
z-index: 10;

View File

@ -15,10 +15,11 @@ const infoFormat = (obj: ComponentInfo) => {
name,
deType
},
displayId: '',
displayId: id,
sortId: '',
sort: 'asc',
defaultMapValue: [],
mapValue: [],
conditionType: 0,
conditionValueOperatorF: 'eq',
conditionValueF: '',

View File

@ -83,13 +83,15 @@ const getValueByDefaultValueCheckOrFirstLoad = (
firstLoad: boolean,
multiple: boolean,
defaultMapValue: any,
optionValueSource: number
optionValueSource: number,
mapValue: any,
displayType: string
) => {
if (optionValueSource === 1) {
if (firstLoad && !selectValue?.length) {
if (optionValueSource === 1 && defaultMapValue?.length && ![1, 7].includes(+displayType)) {
if (firstLoad) {
return defaultValueCheck ? defaultMapValue : multiple ? [] : ''
}
return (selectValue?.length ? defaultMapValue : selectValue) || ''
return (selectValue?.length ? mapValue : selectValue) || ''
}
if (firstLoad && !selectValue?.length) {
@ -152,7 +154,7 @@ const getOperator = (
const operatorS = firstLoad ? defaultConditionValueOperatorS : conditionValueOperatorS
if (displayType === '8') {
if (conditionType === 0) {
return defaultConditionValueOperatorF
return operatorF
}
const operatorArr = [valueF === '' ? '' : operatorF, valueS === '' ? '' : operatorS].filter(
ele => ele !== ''
@ -180,7 +182,7 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
item.checkedFields.includes(curComponentId) &&
item.checkedFieldsMap[curComponentId]
) {
let selectValue = ''
let selectValue
const {
selectValue: value,
timeGranularityMultiple,
@ -200,6 +202,7 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
defaultValue,
optionValueSource,
defaultMapValue,
mapValue,
parameters = [],
parametersCheck = false,
isTree = false,
@ -247,6 +250,7 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
)
item.defaultValue = [startTime, endTime]
item.selectValue = [startTime, endTime]
selectValue = [startTime, endTime]
}
} else if (displayType === '8') {
selectValue = getResult(
@ -265,7 +269,9 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
firstLoad,
multiple,
defaultMapValue,
optionValueSource
optionValueSource,
mapValue,
displayType
)
}
if (
@ -294,23 +300,25 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
firstLoad,
optionValueSource
)
filter.push({
componentId: ele.id,
fieldId: item.checkedFieldsMap[curComponentId],
operator,
value: result,
parameters: parametersCheck
? +displayType === 7
? [
parametersStart,
parametersEnd?.id
? { ...parametersEnd, id: `${parametersEnd.id}_START_END_SPLIT` }
: parametersEnd
]
: parameters
: [],
isTree
})
if (result?.length) {
filter.push({
componentId: ele.id,
fieldId: item.checkedFieldsMap[curComponentId],
operator,
value: result,
parameters: parametersCheck
? +displayType === 7
? [
parametersStart,
parametersEnd?.id
? { ...parametersEnd, id: `${parametersEnd.id}_START_END_SPLIT` }
: parametersEnd
]
: parameters
: [],
isTree
})
}
}
}
})

View File

@ -80,7 +80,7 @@ onMounted(() => {
}
}
.ai-main-active {
border: 1px solid #d9d9d9;
border: 1px solid rgba(239, 240, 241, 1);
box-shadow: 0px 6px 24px 0px #1f232914;
}
.ai-main-active-min {

View File

@ -0,0 +1,84 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
const visible = ref(true)
const emits = defineEmits(['confirm'])
const confirm = () => {
emits('confirm')
}
onMounted(() => {
// do
})
</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="title">DataEase 智能客服</p>
<p class="constant">
你好我是DataEase智能客服<br />点击一下开启高效解答模式~<br />&nbsp;
</p>
<div class="bottom">
<el-button size="middle" @click="confirm"> 我知道了 </el-button>
</div>
</div>
<template #reference>
<div class="ai-popper-tips-icon">
<el-icon style="margin: 2px" class="ai-icon">
<Icon name="dv-ai" />
</el-icon>
</div>
</template>
</el-popover>
</template>
<style lang="less">
.ai-popper-tips {
z-index: 10001 !important;
padding: 24px !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: PingFang SC;
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.content {
font-family: PingFang SC;
font-size: 14px;
font-weight: 500;
line-height: 22px;
text-align: left;
}
.bottom {
line-height: 22px;
text-align: right;
button {
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

@ -17,6 +17,7 @@ import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
import AiComponent from '@/layout/components/AiComponent.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { findBaseParams } from '@/api/aiComponent'
import AiTips from '@/layout/components/AiTips.vue'
const appearanceStore = useAppearanceStoreWithOut()
const { push } = useRouter()
const route = useRoute()
@ -43,6 +44,7 @@ const permissionStore = usePermissionStore()
const routers: any[] = formatRoute(permissionStore.getRoutersNotHidden as AppCustomRouteRecordRaw[])
const showSystem = ref(false)
const showToolbox = ref(false)
const showOverlay = ref(true)
const handleSelect = (index: string) => {
//
if (isExternal(index)) {
@ -61,6 +63,12 @@ const navigateBg = computed(() => appearanceStore.getNavigateBg)
const navigate = computed(() => appearanceStore.getNavigate)
const initAiBase = async () => {
const aiTipsCheck = localStorage.getItem('DE-AI-TIPS-CHECK')
if (aiTipsCheck === 'CHECKED') {
showOverlay.value = false
} else {
showOverlay.value = true
}
await findBaseParams().then(rsp => {
const params = rsp.data
if (params && params['ai.baseUrl']) {
@ -68,6 +76,11 @@ const initAiBase = async () => {
}
})
}
const aiTipsConfirm = () => {
localStorage.setItem('DE-AI-TIPS-CHECK', 'CHECKED')
showOverlay.value = false
}
onMounted(() => {
initShowSystem()
initShowToolbox()
@ -96,19 +109,30 @@ onMounted(() => {
</el-menu>
<div class="operate-setting" v-if="!desktop">
<XpackComponent jsname="c3dpdGNoZXI=" />
<el-icon style="margin: 0 10px" class="ai-icon" v-if="aiBaseUrl">
<el-icon style="margin: 0 10px" class="ai-icon" v-if="aiBaseUrl && !showOverlay">
<Icon name="dv-ai" @click="handleAiClick" />
</el-icon>
<ai-tips @confirm="aiTipsConfirm" v-if="showOverlay" class="ai-icon-tips"></ai-tips>
<ToolboxCfg v-if="showToolbox" />
<TopDoc />
<SystemCfg v-if="showSystem" />
<AccountOperator />
<ai-component v-if="aiBaseUrl" :base-url="aiBaseUrl"></ai-component>
<div v-if="showOverlay" class="overlay"></div>
</div>
</el-header>
</template>
<style lang="less" scoped>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色 */
z-index: 10000;
}
.header-light {
background-color: #ffffff !important;
box-shadow: 0px 0.5px 0px 0px #1f232926 !important;
@ -208,4 +232,9 @@ onMounted(() => {
.ai-icon {
font-size: 24px !important;
}
.ai-icon-tips {
font-size: 24px !important;
z-index: 10001;
}
</style>

View File

@ -449,7 +449,9 @@ export default {
latitude: '纬度',
gradient: '渐变',
layer_controller: '指标切换',
suspension: '悬浮',
show_zoom: '显示缩放按钮',
button_color: '按钮颜色',
button_background_color: '按钮背景色',
chart_background: '组件背景',
date_format: '请选择日期解析格式',
solid_color: '纯色',
@ -646,6 +648,9 @@ export default {
table_item_font_color: '表格字体',
table_show_index: '显示序号',
table_header_sort: '开启表头排序',
table_show_row_tooltip: '开启行头提示',
table_show_col_tooltip: '开启列头提示',
table_show_cell_tooltip: '开启单元格提示',
stripe: '斑马纹',
start_angle: '起始角度',
end_angle: '结束角度',
@ -977,6 +982,7 @@ export default {
dimension_font_family: '名称字体',
dimension_text_style: '名称样式',
dimension_letter_space: '名称字间距',
name_value_spacing: '名称/值间距',
font_family: '字体',
letter_space: '字间距',
font_shadow: '字体阴影',
@ -1102,7 +1108,9 @@ export default {
add_condition: '添加条件',
chart_quadrant: '象限图',
quadrant: '象限',
font_size: '字号'
font_size: '字号',
word_size_range: '字号区间',
word_spacing: '文字间隔'
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@ -38,6 +38,14 @@ declare interface ChartAttr {
* 象限设置
*/
quadrant: QuadrantAttr
/**
* 指标值
*/
indicator: ChartIndicatorStyle
/**
* 指标名称
*/
indicatorName: ChartIndicatorNameStyle
}
/**
* 基础样式设置
@ -161,9 +169,10 @@ declare interface ChartBasicStyle {
*/
areaBorderColor: string
/**
* @deprecated
* 悬浮工具栏
*/
suspension: boolean
suspension?: boolean
/**
* 地图底色
*/
@ -192,6 +201,18 @@ declare interface ChartBasicStyle {
* 环形图/玫瑰图外径占比
*/
radius: number
/**
* 是否显示地图缩放按钮
*/
showZoom: boolean
/**
* 地图缩放按钮颜色
*/
zoomButtonColor: string
/**
* 地图缩放按钮背景颜色
*/
zoomBackground: string
}
/**
* 表头属性
@ -229,6 +250,14 @@ declare interface ChartTableHeaderAttr {
* 表头排序开关
*/
tableHeaderSort: boolean
/**
* 行头鼠标悬浮提示开关
*/
showRowTooltip: boolean
/**
* 列头鼠标悬浮提示开关
*/
showColTooltip: boolean
}
/**
* 单元格属性
@ -262,6 +291,10 @@ declare interface ChartTableCellAttr {
* 斑马纹单数行颜色
*/
tableItemSubBgColor: string
/**
* 鼠标悬浮提示
*/
showTooltip: boolean
}
/**
@ -517,6 +550,14 @@ declare interface ChartMiscAttr {
* 指标/文本卡垂直位置
*/
vPosition: 'top' | 'center' | 'bottom'
/**
* 词云图字体大小区间
*/
wordSizeRange: [number, number]
/**
* 词云图文字间距
*/
wordSpacing: number
}
/**
* 动态极值配置
@ -702,3 +743,127 @@ declare interface QuadrantLineStyle {
*/
opacity: number
}
/**
* 指标卡值样式
*/
declare interface ChartIndicatorStyle {
/**
* 是否显示
*/
show: boolean
/**
* 字体大小
*/
fontSize: number
/**
* 字体颜色
*/
color: string
/**
* 水平位置
*/
hPosition: 'left' | 'center' | 'right'
/**
* 垂直位置
*/
vPosition: 'top' | 'center' | 'bottom'
/**
* 是否斜体
*/
isItalic: boolean
/**
* 是否加粗
*/
isBolder: boolean
/**
* 字体类型
*/
fontFamily: string
/**
* 字间距
*/
letterSpace: number
/**
* 是否显示字体阴影
*/
fontShadow: boolean
/**
* 是否显示后缀
*/
suffixEnable: boolean
/**
* 后缀内容
*/
suffix: string
/**
* 后缀字体大小
*/
suffixFontSize: number
/**
* 后缀字体颜色
*/
suffixColor: string
/**
* 后缀是否斜体
*/
suffixIsItalic: boolean
/**
* 后缀是否加粗
*/
suffixIsBolder: boolean
/**
* 后缀字体类型
*/
suffixFontFamily: string
/**
* 后缀字间距
*/
suffixLetterSpace: number
/**
* 后置是否显示阴影
*/
suffixFontShadow: boolean
}
/**
* 指标卡名称样式
*/
declare interface ChartIndicatorNameStyle {
/**
* 是否显示
*/
show: boolean
/**
* 字体大小
*/
fontSize: number
/**
* 字体颜色
*/
color: string
/**
* 是否斜体
*/
isItalic: boolean
/**
* 是否加粗
*/
isBolder: boolean
/**
* 字体类型
*/
fontFamily: string
/**
* 字间距
*/
letterSpace: number
/**
* 是否显示字体阴影
*/
fontShadow: boolean
/**
* 指标/名称间距
*/
nameValueSpacing: number
}

View File

@ -108,7 +108,7 @@ declare interface AssistLine {
*/
summary: string
axisType: 'left' | 'right'
yAxisType: 'left' | 'right'
}
/**

View File

@ -32,39 +32,6 @@ declare interface ChartStyle {
}
}
declare interface ChartIndicatorStyle {
show: boolean
fontSize: string
color: string
hPosition: 'left' | 'center' | 'right'
vPosition: 'top' | 'center' | 'bottom'
isItalic: boolean
isBolder: boolean
fontFamily: string
letterSpace: string
fontShadow: boolean
suffixEnable: boolean
suffix: string
suffixFontSize: string
suffixColor: string
suffixIsItalic: boolean
suffixIsBolder: boolean
suffixFontFamily: string
suffixLetterSpace: string
suffixFontShadow: boolean
}
declare interface ChartIndicatorNameStyle {
show: boolean
fontSize: string
color: string
isItalic: boolean
isBolder: boolean
fontFamily: string
letterSpace: string
fontShadow: boolean
}
/**
* 标题样式设置
*/

View File

@ -2,7 +2,22 @@ import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import type { App } from 'vue'
export const routes: AppRouteRecordRaw[] = []
export const routes: AppRouteRecordRaw[] = [
{
path: '/dvCanvas',
name: 'dvCanvas',
hidden: true,
meta: {},
component: () => import('@/views/data-visualization/index.vue')
},
{
path: '/dashboard',
name: 'dashboard',
hidden: true,
meta: {},
component: () => import('@/views/dashboard/index.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),

View File

@ -812,7 +812,7 @@ export const dvMainStore = defineStore('dataVisualization', {
const checkQDList = [...data.dimensionList, ...data.quotaList]
for (let indexOuter = 0; indexOuter < this.componentData.length; indexOuter++) {
const element = this.componentData[indexOuter]
if (element.component === 'UserView' && element.innerType != 'VQuery') {
if (['UserView', 'VQuery'].includes(element.component)) {
this.trackFilterCursor(element, checkQDList, trackInfo, preActiveComponentIds, viewId)
this.componentData[indexOuter] = element
} else if (element.component === 'Group') {
@ -900,18 +900,36 @@ export const dvMainStore = defineStore('dataVisualization', {
if (element.component === 'VQuery') {
element.propValue.forEach(filterItem => {
if (filterItem.id === targetViewId) {
let queryParams = paramValue
if (!['1', '7'].includes(filterItem.displayType)) {
// 查询组件除了时间组件 其他入参只支持文本 这里全部转为文本
queryParams = paramValue.map(number => String(number))
}
filterItem.defaultValueCheck = true
if (filterItem.displayType === '0' && filterItem.multiple) {
filterItem.selectValue = paramValue
filterItem.defaultValue = paramValue
} else if (filterItem.displayType === '0' && !filterItem.multiple) {
filterItem.selectValue = paramValue[0]
filterItem.defaultValue = paramValue[0]
filterItem.timeType = 'fixed'
if (['0', '2'].includes(filterItem.displayType)) {
// 0 文本类型 1 数字类型
if (filterItem.multiple) {
// multiple === true 多选
filterItem.selectValue = queryParams
filterItem.defaultValue = queryParams
} else {
// 单选
filterItem.selectValue = queryParams[0]
filterItem.defaultValue = queryParams[0]
}
} else if (filterItem.displayType === '1') {
// 1 时间类型
filterItem.selectValue = queryParams[0]
filterItem.defaultValue = queryParams[0]
} else if (filterItem.displayType === '7') {
// 7 时间范围类型
filterItem.selectValue = queryParams
filterItem.defaultValue = queryParams
} else if (filterItem.displayType === '8') {
filterItem.conditionValueF = parmaValueSource
} else {
filterItem.selectValue = paramValue[0]
filterItem.defaultValue = paramValue[0]
// 8 文本搜索
filterItem.conditionValueF = parmaValueSource + ''
filterItem.defaultConditionValueF = parmaValueSource + ''
}
}
})
@ -933,10 +951,11 @@ export const dvMainStore = defineStore('dataVisualization', {
const sourceInfo = viewId + '#' + QDItem.id
// 获取所有目标联动信息
const targetInfoList = trackInfo[sourceInfo] || []
const paramValue = [QDItem.value]
targetInfoList.forEach(targetInfo => {
const targetInfoArray = targetInfo.split('#')
const targetViewId = targetInfoArray[0] // 目标图表
if (element.id === targetViewId) {
if (element.component === 'UserView' && element.id === targetViewId) {
// 如果目标图表 当前循环组件id相等 则进行条件增减
const targetFieldId = targetInfoArray[1] // 目标图表列ID
const condition = {
@ -959,6 +978,43 @@ export const dvMainStore = defineStore('dataVisualization', {
currentFilters.push(condition)
preActiveComponentIds.includes(element.id) || preActiveComponentIds.push(element.id)
}
if (element.component === 'VQuery') {
element.propValue.forEach(filterItem => {
if (filterItem.id === targetViewId) {
let queryParams = paramValue
if (!['1', '7'].includes(filterItem.displayType)) {
// 查询组件除了时间组件 其他入参只支持文本 这里全部转为文本
queryParams = paramValue.map(number => String(number))
}
filterItem.defaultValueCheck = true
filterItem.timeType = 'fixed'
if (['0', '2'].includes(filterItem.displayType)) {
// 0 文本类型 1 数字类型
if (filterItem.multiple) {
// multiple === true 多选
filterItem.selectValue = queryParams
filterItem.defaultValue = queryParams
} else {
// 单选
filterItem.selectValue = queryParams[0]
filterItem.defaultValue = queryParams[0]
}
} else if (filterItem.displayType === '1') {
// 1 时间类型
filterItem.selectValue = queryParams[0]
filterItem.defaultValue = queryParams[0]
} else if (filterItem.displayType === '7') {
// 7 时间范围类型
filterItem.selectValue = queryParams
filterItem.defaultValue = queryParams
} else if (filterItem.displayType === '8') {
// 8 文本搜索
filterItem.conditionValueF = queryParams[0]
filterItem.defaultConditionValueF = queryParams[0]
}
}
})
}
})
})
element.linkageFilters = currentFilters

View File

@ -121,12 +121,14 @@ export const customAttrTrans = {
'valueFontSize',
'spaceSplit', // 间隔
'scatterSymbolSize', // 气泡大小散点图
'radarSize' // 雷达占比
'radarSize', // 雷达占比
'wordSizeRange',
'wordSpacing'
],
label: ['fontSize'],
tooltip: ['fontSize'],
indicator: ['fontSize', 'suffixFontSize'],
indicatorName: ['fontSize']
indicatorName: ['fontSize', 'nameValueSpacing']
}
export const customStyleTrans = {
text: ['fontSize'],
@ -278,6 +280,13 @@ export const mobileSpecialProps = {
}
export function getScaleValue(propValue, scale) {
if (propValue instanceof Array) {
propValue.forEach((v, i) => {
const val = Math.round(v * scale)
propValue[i] = val > 1 ? val : 1
})
return propValue
}
const propValueTemp = Math.round(propValue * scale)
return propValueTemp > 1 ? propValueTemp : 1
}

View File

@ -420,3 +420,20 @@ export async function decompressionPre(params, callBack) {
})
callBack(deTemplateData)
}
export function trackBarStyleCheck(element, trackbarStyle, scale) {
const { width, height } = element.style
const widthReal = width
const heightReal = height
if (trackbarStyle.left < 0) {
trackbarStyle.left = 0
} else if (widthReal - trackbarStyle.left < 60) {
trackbarStyle.left = trackbarStyle.left - 60
}
if (trackbarStyle.top < 0) {
trackbarStyle.top = 0
} else if (heightReal - trackbarStyle.top < 100) {
trackbarStyle.top = trackbarStyle.top - 100
}
}

View File

@ -355,7 +355,7 @@ onMounted(() => {
<span class="el-dropdown-link inner-dropdown-menu menu-item-padding">
<span class="menu-item-content">
<el-icon>
<Icon name="icon_functions_outlined" />
<Icon name="icon_dashboard_outlined" />
</el-icon>
<span>{{ t('chart.chart_type') }}</span>
</span>

View File

@ -54,11 +54,6 @@ const state = reactive({
quotaFields: []
})
const axisType = [
{ type: 'left', name: t('chart.drag_block_value_axis_left') },
{ type: 'right', name: t('chart.drag_block_value_axis_right') }
]
watch(
() => props.chart.senior.assistLineCfg,
() => {

View File

@ -1,6 +1,5 @@
<script lang="ts" setup>
import { computed, nextTick, onMounted, PropType, reactive, ref, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { getGeoJsonFile, parseJson } from '../../../js/util'
import { forEach, debounce } from 'lodash-es'
import { EmptyBackground } from '@/components/empty-background'
@ -118,14 +117,6 @@ const updateAreaData = debounce(() => {
const onMapMappingChange = () => {
emit('onMapMappingChange', state.mappingForm)
}
const mergeCellMethod = ({ columnIndex }) => {
if (columnIndex === 1) {
return [1, 2]
}
if (columnIndex === 2) {
return [0, 0]
}
}
onMounted(() => {
init()
})
@ -141,7 +132,6 @@ onMounted(() => {
:cell-class-name="'area-map-table-cell-' + themes"
:row-class-name="'area-map-table-row-' + themes"
:data="areaData"
:span-method="mergeCellMethod"
>
<el-table-column label="图形" prop="originName" width="80" show-overflow-tooltip />
<el-table-column width="144">

View File

@ -26,7 +26,7 @@ const props = defineProps({
}
})
const axisTypes = [
const yAxisTypes = [
{ type: 'left', name: t('chart.drag_block_value_axis_left') },
{ type: 'right', name: t('chart.drag_block_value_axis_right') }
]
@ -39,7 +39,7 @@ const state = reactive({
fieldId: '',
summary: 'avg',
axis: 'y', //
axisType: 'left',
yAxisType: 'left',
value: '0',
lineType: 'solid',
color: '#ff0000',
@ -76,8 +76,23 @@ const init = () => {
state.lineArr = JSON.parse(JSON.stringify(props.line))
state.lineArr.forEach(line => {
if (find(props.quotaFields, d => d.id === line.fieldId) == undefined) {
line.fieldId = undefined
if (props.useQuotaExt) {
if (
line.yAxisType === 'left' &&
find(props.quotaFields, d => d.id === line.fieldId) == undefined
) {
line.fieldId = undefined
}
if (
line.yAxisType === 'right' &&
find(props.quotaExtFields, d => d.id === line.fieldId) == undefined
) {
line.fieldId = undefined
}
} else {
if (find(props.quotaFields, d => d.id === line.fieldId) == undefined) {
line.fieldId = undefined
}
}
})
@ -98,8 +113,8 @@ const removeLine = index => {
changeAssistLine()
}
const changeAxisType = item => {
if (props.useQuotaExt && item.axisType === 'right') {
const changeYAxisType = item => {
if (props.useQuotaExt && item.yAxisType === 'right') {
item.fieldId = props.quotaExtFields ? props.quotaExtFields[0]?.id : null
item.curField = getQuotaExtField(item.fieldId)
} else {
@ -113,7 +128,7 @@ const changeAssistLine = () => {
emit('onAssistLineChange', state.lineArr)
}
const changeAssistLineField = item => {
if (props.useQuotaExt && item.axisType === 'right') {
if (props.useQuotaExt && item.yAxisType === 'right') {
item.curField = getQuotaExtField(item.fieldId)
} else {
item.curField = getQuotaField(item.fieldId)
@ -169,9 +184,9 @@ onMounted(() => {
/>
</el-col>
<el-col v-if="useQuotaExt" :span="3">
<el-select v-model="item.axisType" class="select-item" @change="changeAxisType(item)">
<el-select v-model="item.yAxisType" class="select-item" @change="changeYAxisType(item)">
<el-option
v-for="opt in axisTypes"
v-for="opt in yAxisTypes"
:key="opt.type"
:label="opt.name"
:value="opt.type"
@ -206,7 +221,7 @@ onMounted(() => {
@change="changeAssistLineField(item)"
>
<el-option
v-for="quota in useQuotaExt && item.axisType === 'right'
v-for="quota in useQuotaExt && item.yAxisType === 'right'
? quotaExtFields
: quotaFields"
:key="quota.id"

View File

@ -116,11 +116,14 @@ const onChangeXAxisForm = (val, prop) => {
}
const onChangeYAxisForm = (val, prop) => {
if (prop === 'show' && chart.value.type === 'chart-mix') {
chart.value.customStyle.yAxisExt.show = val.show
onChangeYAxisExtForm(chart.value.customStyle.yAxisExt, 'show')
}
state.initReady && emit('onChangeYAxisForm', val, prop)
}
const onChangeYAxisExtForm = (val, prop) => {
console.log(val, prop)
state.initReady && emit('onChangeYAxisExtForm', val, prop)
}

View File

@ -49,6 +49,7 @@ const changeBasicStyle = (prop?: string, requestData = false) => {
}
const init = () => {
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
configCompat(basicStyle)
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
if (!state.customColor) {
state.customColor = state.basicStyleForm.colors[0]
@ -56,6 +57,12 @@ const init = () => {
}
initTableColumnWidth()
}
const configCompat = (basicStyle: ChartBasicStyle) => {
//
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
basicStyle.showZoom = false
}
}
const COLUMN_WIDTH_TYPE = ['table-info', 'table-normal']
const initTableColumnWidth = () => {
if (!COLUMN_WIDTH_TYPE.includes(props.chart.type)) {
@ -191,6 +198,39 @@ onMounted(() => {
</el-checkbox>
</el-form-item>
<div class="alpha-setting" v-if="showProperty('alpha')">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.not_alpha') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
v-model="state.basicStyleForm.alpha"
@change="changeBasicStyle('alpha')"
/>
</el-form-item>
</el-col>
<el-col :span="11" style="padding-top: 2px">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-input
type="number"
:effect="themes"
v-model="state.basicStyleForm.alpha"
:min="0"
:max="100"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('alpha')"
>
<template #suffix> % </template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</div>
<!--map start-->
<el-row :gutter="8">
<el-col :span="12" v-if="showProperty('areaBorderColor')">
@ -230,21 +270,51 @@ onMounted(() => {
</el-form-item>
</el-col>
</el-row>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('suspension')"
>
<el-form-item class="form-item" :class="'form-item-' + themes" v-if="showProperty('zoom')">
<el-checkbox
size="small"
:effect="themes"
v-model="state.basicStyleForm.suspension"
v-model="state.basicStyleForm.showZoom"
:predefine="predefineColors"
@change="changeBasicStyle('suspension')"
@change="changeBasicStyle('zoomShow')"
>
{{ t('chart.suspension') }}
{{ t('chart.show_zoom') }}
</el-checkbox>
</el-form-item>
<div v-if="showProperty('zoom') && state.basicStyleForm.showZoom">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.button_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.basicStyleForm.zoomButtonColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeBasicStyle('zoomButtonColor')"
/>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.button_background_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.basicStyleForm.zoomBackground"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeBasicStyle('zoomBackground')"
/>
</el-form-item>
</div>
<!--map end-->
@ -335,39 +405,6 @@ onMounted(() => {
</el-row>
<!--table end-->
<div class="alpha-setting" v-if="showProperty('alpha')">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.not_alpha') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
v-model="state.basicStyleForm.alpha"
@change="changeBasicStyle('alpha')"
/>
</el-form-item>
</el-col>
<el-col :span="11" style="padding-top: 2px">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-input
type="number"
:effect="themes"
v-model="state.basicStyleForm.alpha"
:min="0"
:max="100"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('alpha')"
>
<template #suffix> % </template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</div>
<!--table2 start-->
<el-form-item
:label="t('chart.table_column_width_config')"

View File

@ -49,7 +49,6 @@ const changeAxisStyle = (val, prop) => {
}
const changeSubAxisStyle = (val, prop) => {
console.log(val, prop)
emit('onChangeYAxisExtForm', val, prop)
}

View File

@ -9,7 +9,6 @@ import {
DEFAULT_BASIC_STYLE
} from '@/views/chart/components/editor/util/chart'
import { cloneDeep, defaultsDeep } from 'lodash-es'
import { ElIcon } from 'element-plus-secondary'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { hexColorToRGBA } from '@/views/chart/components/js/util'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
@ -251,6 +250,24 @@ defineExpose({ getFormData })
{{ t('chart.font_shadow') }}
</el-checkbox>
</el-form-item>
<el-form-item
class="form-item name-value-spacing-input"
:class="'form-item-' + themes"
:label="t('chart.name_value_spacing')"
>
<el-input-number
step-strictly
v-model="state.indicatorNameForm.nameValueSpacing"
size="small"
:min="0"
:max="100"
:value-on-clear="0"
:precision="0"
:step="1"
:effect="themes"
@change="changeTitleStyle('nameValueSpacing')"
/>
</el-form-item>
</el-form>
</div>
</template>
@ -366,4 +383,14 @@ defineExpose({ getFormData })
color: var(--N600-Dark, #a6a6a6);
}
}
.name-value-spacing-input {
display: flex !important;
:deep(label) {
line-height: 28px !important;
margin-bottom: 0 !important;
}
:deep(.ed-input__inner) {
text-align: center !important;
}
}
</style>

View File

@ -7,11 +7,12 @@ import cloneDeep from 'lodash-es/cloneDeep'
import defaultsDeep from 'lodash-es/defaultsDeep'
import { formatterType, unitType } from '../../../js/formatter'
import { fieldType } from '@/utils/attr'
import { partition } from 'lodash-es'
import { partition, uniqWith, isEqual } from 'lodash-es'
import chartViewManager from '../../../js/panel'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { useEmitt } from '@/hooks/web/useEmitt'
import { deepCopy } from '@/utils/utils'
const { t } = useI18n()
@ -98,7 +99,9 @@ const quotaAxis = computed(() => {
return
}
const axis = props.chart[prop]
axis?.forEach(item => result.push(item))
axis?.forEach(item => {
result.push({ ...item, seriesId: `${item.id}-${prop}` })
})
})
return result
})
@ -255,6 +258,9 @@ const updateSeriesTooltipFormatter = (form: AxisEditForm) => {
const addAxis = (form: AxisEditForm) => {
const { axis, axisType } = form
const axisMap = axis.reduce((pre, next) => {
if (!next) {
return pre
}
next.axisType = axisType
next.seriesId = `${next.id}-${axisType}`
pre[next.id] = next
@ -272,16 +278,17 @@ const addAxis = (form: AxisEditForm) => {
ele.chartShowName = axisMap[ele.id].chartShowName
} else {
//
if (dupAxis.findIndex(i => i.id === ele.id) !== -1) {
return
}
const tmp = cloneDeep(axisMap[ele.id])
tmp.show = true
dupAxis.push(tmp)
}
}
})
state.tooltipForm.seriesTooltipFormatter =
state.tooltipForm.seriesTooltipFormatter.concat(dupAxis)
state.tooltipForm.seriesTooltipFormatter = partition(
state.tooltipForm.seriesTooltipFormatter,
state.tooltipForm.seriesTooltipFormatter.concat(dupAxis),
ele => quotaAxis.value.findIndex(item => item.id === ele.id) !== -1
).flat()
}
@ -312,8 +319,8 @@ const removeAxis = (form: AxisEditForm) => {
if (axisMap[ele.seriesId]) {
//
ele.show = false
ele.summary = axisMap[ele.seriesId].summary
ele.seriesId = ele.id
ele.summary = 'sum'
}
})
state.tooltipForm.seriesTooltipFormatter = partition(
@ -531,7 +538,7 @@ onMounted(() => {
</template>
<el-option
class="series-select-option"
:key="item.id"
:key="item.seriesId"
:value="item"
:label="`${item.name}${
item.summary !== '' ? '(' + t('chart.' + item.summary) + ')' : ''

View File

@ -234,6 +234,20 @@ onMounted(() => {
</el-form-item>
</el-col>
</el-row>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('showTooltip')"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.tableCellForm.showTooltip"
@change="changeTableCell('showTooltip')"
>
{{ t('chart.table_show_cell_tooltip') }}
</el-checkbox>
</el-form-item>
</el-form>
</template>

View File

@ -250,6 +250,34 @@ onMounted(() => {
{{ t('chart.table_header_sort') }}
</el-checkbox>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('showColTooltip')"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.tableHeaderForm.showColTooltip"
@change="changeTableHeader('showColTooltip')"
>
{{ t('chart.table_show_col_tooltip') }}
</el-checkbox>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('showRowTooltip')"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.tableHeaderForm.showRowTooltip"
@change="changeTableHeader('showRowTooltip')"
>
{{ t('chart.table_show_row_tooltip') }}
</el-checkbox>
</el-form-item>
</el-form>
</template>

View File

@ -1394,7 +1394,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
const obj = cloneDeep(arr[i])
state.moveId = obj.id as unknown as number
view.value[type].push(obj)
const e = { newDraggableIndex: view.value.xAxis.length - 1 }
const e = { newDraggableIndex: view.value[type].length - 1 }
if ('drillFields' === type) {
addDrill(e)
} else if (type === 'customFilter') {

View File

@ -24,7 +24,9 @@ export const DEFAULT_COLOR_CASE: DeepPartial<ChartAttr> = {
areaBorderColor: '#303133',
gaugeStyle: 'default',
tableBorderColor: '#E6E7E4',
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)'
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)',
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
},
misc: {
mapLineGradient: false,
@ -65,7 +67,9 @@ export const DEFAULT_COLOR_CASE_LIGHT: DeepPartial<ChartAttr> = {
areaBorderColor: '#303133',
gaugeStyle: 'default',
tableBorderColor: '#E6E7E4',
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)'
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)',
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
},
misc: {
mapLineGradient: false,
@ -106,7 +110,9 @@ export const DEFAULT_COLOR_CASE_DARK: DeepPartial<ChartAttr> = {
areaBorderColor: '#EBEEF5',
gaugeStyle: 'default',
tableBorderColor: '#CCCCCC',
tableScrollBarColor: 'rgba(255, 255, 255, 0.5)'
tableScrollBarColor: 'rgba(255, 255, 255, 0.5)',
zoomButtonColor: '#fff',
zoomBackground: '#000'
},
misc: {
mapLineGradient: false,
@ -242,10 +248,9 @@ export const DEFAULT_MISC: ChartMiscAttr = {
mapLineAnimateDuration: 3,
mapLineGradient: false,
mapLineSourceColor: '#146C94',
mapLineTargetColor: '#576CBC'
}
export const DEFAULT_SUSPENSION = {
show: true
mapLineTargetColor: '#576CBC',
wordSizeRange: [8, 32],
wordSpacing: 6
}
export const DEFAULT_MARK = {
@ -330,7 +335,9 @@ export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = {
tableHeaderFontColor: '#000000',
tableTitleFontSize: 12,
tableTitleHeight: 36,
tableHeaderSort: false
tableHeaderSort: false,
showColTooltip: false,
showRowTooltip: false
}
export const DEFAULT_TABLE_CELL: ChartTableCellAttr = {
tableFontColor: '#000000',
@ -339,7 +346,8 @@ export const DEFAULT_TABLE_CELL: ChartTableCellAttr = {
tableItemFontSize: 12,
tableItemHeight: 36,
enableTableCrossBG: false,
tableItemSubBgColor: '#EEEEEE'
tableItemSubBgColor: '#EEEEEE',
showTooltip: false
}
export const DEFAULT_TITLE_STYLE: ChartTextStyle = {
show: true,
@ -359,35 +367,36 @@ export const DEFAULT_TITLE_STYLE: ChartTextStyle = {
export const DEFAULT_INDICATOR_STYLE: ChartIndicatorStyle = {
show: true,
fontSize: '20',
fontSize: 20,
color: '#5470C6ff',
hPosition: 'center',
vPosition: 'center',
isItalic: false,
isBolder: true,
fontFamily: 'Microsoft YaHei',
letterSpace: '0',
letterSpace: 0,
fontShadow: false,
suffixEnable: true,
suffix: '',
suffixFontSize: '14',
suffixFontSize: 14,
suffixColor: '#5470C6ff',
suffixIsItalic: false,
suffixIsBolder: true,
suffixFontFamily: 'Microsoft YaHei',
suffixLetterSpace: '0',
suffixLetterSpace: 0,
suffixFontShadow: false
}
export const DEFAULT_INDICATOR_NAME_STYLE: ChartIndicatorNameStyle = {
show: true,
fontSize: '18',
fontSize: 18,
color: '#ffffffff',
isItalic: false,
isBolder: true,
fontFamily: 'Microsoft YaHei',
letterSpace: '0',
fontShadow: false
letterSpace: 0,
fontShadow: false,
nameValueSpacing: 0
}
export const DEFAULT_TITLE_STYLE_BASE: ChartTextStyle = {
@ -994,7 +1003,7 @@ export const CHART_FONT_FAMILY = [
{ name: '楷体', value: 'KaiTi' }
]
export const CHART_CONT_FAMILY_MAP = {
export const CHART_FONT_FAMILY_MAP = {
'Microsoft YaHei': 'Microsoft YaHei',
SimSun: 'SimSun, "Songti SC", STSong',
SimHei: 'SimHei, Helvetica',
@ -1364,14 +1373,16 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
radarShape: 'polygon',
mapStyle: 'normal',
areaBorderColor: '#EBEEF5',
suspension: true,
areaBaseColor: '#ffffff',
mapSymbolOpacity: 0.7,
mapSymbolStrokeWidth: 2,
mapSymbol: 'circle',
mapSymbolSize: 20,
radius: 80,
innerRadius: 60
innerRadius: 60,
showZoom: true,
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
}
export const BASE_VIEW_CONFIG = {

View File

@ -25,7 +25,7 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha'],
'label-selector': ['fontSize', 'color', 'labelFormatter'],
'misc-selector': ['liquidShape', 'liquidMaxType', 'liquidMaxField'],
'misc-selector': ['liquidShape', 'liquidSize', 'liquidMaxType', 'liquidMaxField'],
'title-selector': [
'title',
'fontSize',

View File

@ -98,9 +98,10 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
options = this.setupOptions(chart, options, drawOption, geoJson)
const view = new Choropleth(container, options)
const dotLayer = this.getDotLayer(chart, geoJson, drawOption)
this.configZoomButton(view)
this.configZoomButton(chart, view)
view.once('loaded', () => {
view.addLayer(dotLayer)
view.scene.map['keyboard'].disable()
view.on('fillAreaLayer:click', (ev: MapMouseEvent) => {
const data = ev.feature.properties
action({
@ -178,13 +179,6 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
const curAreaNameMapping = senior.areaMapping?.[areaId]
handleGeoJson(geoJson, curAreaNameMapping)
options.color = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
const suspension = basicStyle.suspension
if (!suspension) {
options = {
...options,
zoom: false
}
}
if (!chart.data?.data?.length || !geoJson?.features?.length) {
options.label && (options.label.field = 'name')
return options
@ -221,8 +215,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configLabel,
this.configStyle,
this.configTooltip,
this.configBasicStyle,
this.configLegend
this.configBasicStyle
)(chart, options, extra)
}
}

View File

@ -12,7 +12,7 @@ export const MAP_EDITOR_PROPERTY: EditorProperty[] = [
export const MAP_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'areaBorderColor', 'suspension'],
'basic-style-selector': ['colors', 'alpha', 'areaBorderColor', 'zoom'],
'title-selector': [
'title',
'fontSize',

View File

@ -22,8 +22,11 @@ const { t } = useI18n()
* 地图
*/
export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
properties = MAP_EDITOR_PROPERTY
propertyInner = MAP_EDITOR_PROPERTY_INNER
properties: EditorProperty[] = [...MAP_EDITOR_PROPERTY, 'legend-selector']
propertyInner: EditorPropertyInner = {
...MAP_EDITOR_PROPERTY_INNER,
'legend-selector': ['icon', 'fontSize', 'color']
}
axis = MAP_AXIS_TYPE
axisConfig: AxisConfig = {
xAxis: {
@ -90,16 +93,14 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
active: { stroke: 'green', lineWidth: 1 }
},
tooltip: {},
legend: {
position: 'bottomleft'
},
// 禁用线上地图数据
customFetchGeoData: () => null
}
options = this.setupOptions(chart, options, drawOption, geoJson)
const view = new Choropleth(container, options)
this.configZoomButton(view)
this.configZoomButton(chart, view)
view.once('loaded', () => {
view.scene.map['keyboard'].disable()
view.on('fillAreaLayer:click', (ev: MapMouseEvent) => {
const data = ev.feature.properties
action({
@ -135,14 +136,6 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
unknown: basicStyle.areaBaseColor
}
}
const suspension = basicStyle.suspension
if (!suspension) {
options = {
...options,
legend: false,
zoom: false
}
}
if (!chart.data?.data?.length || !geoJson?.features?.length) {
options.label && (options.label.field = 'name')
return options

View File

@ -365,8 +365,8 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
protected configAnalyse(chart: Chart, options: DualAxesOptions): DualAxesOptions {
const list = getAnalyse(chart)
const annotations = {
value: filter(list, l => l.axisType === 'left'),
valueExt: filter(list, l => l.axisType === 'right')
value: filter(list, l => l.yAxisType === 'left'),
valueExt: filter(list, l => l.yAxisType === 'right')
}
return { ...options, annotations }
}

View File

@ -123,7 +123,7 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
return
}
const { colorFieldObj, sizeFieldObj, xFieldObj, yFieldObj } = this.getFieldObject(chart)
if (!xFieldObj.id || !yFieldObj.id) {
if (!xFieldObj.id || !yFieldObj.id || yFieldObj.id === xFieldObj.id) {
return
}
const data: any[] = []
@ -316,14 +316,10 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
tooltip: false
}
}
xAxisTitle['show'] = true
yAxisTitle['show'] = true
yAxisExtTitle['show'] = true
tooltipAttr.seriesTooltipFormatter?.push(xAxisTitle)
const formatterMap = tooltipAttr.seriesTooltipFormatter
?.filter(i => i.show)
.reduce((pre, next) => {
pre[next['originName']] = next
pre[next['seriesId']] = next
return pre
}, {}) as Record<string, SeriesFormatter>
const tooltip: ScatterOptions['tooltip'] = {
@ -339,17 +335,21 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
originalItems
?.filter(i => i.name !== xAxisTitle['originName'])
.forEach(item => {
const formatter = formatterMap[item.name]
if (formatter) {
const value =
formatter.groupType === 'q'
? valueFormatter(parseFloat(item.value as string), formatter.formatterCfg)
: item.value
const name = isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
result.push({ color: item.color, name, value })
}
Object.keys(formatterMap).forEach(key => {
if (formatterMap[key]['originName'] === item.name) {
const formatter = formatterMap[key]
if (formatter) {
const value =
formatter.groupType === 'q'
? valueFormatter(parseFloat(item.value as string), formatter.formatterCfg)
: item.value
const name = isEmpty(formatter.chartShowName)
? formatter.name
: formatter.chartShowName
result.push({ color: item.color, name, value })
}
}
})
})
return result
}

View File

@ -8,6 +8,7 @@ import { getPadding } from '@/views/chart/components/js/panel/common/common_antv
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { useI18n } from '@/hooks/web/useI18n'
import { isEmpty } from 'lodash-es'
import { DEFAULT_MISC } from '@/views/chart/components/editor/util/chart'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -20,6 +21,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'background-overall-component',
'title-selector',
'tooltip-selector',
'misc-selector',
'jump-set',
'linkage'
]
@ -38,6 +40,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'letterSpace',
'fontShadow'
],
'misc-selector': ['wordSizeRange', 'wordSpacing'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter']
}
axis: AxisType[] = ['xAxis', 'yAxis', 'filter']
@ -58,6 +61,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
if (chart?.data) {
// data
const data = chart.data.data
const { misc } = parseJson(chart.customAttr)
// options
const initOptions: WordCloudOptions = {
data: data,
@ -66,9 +70,9 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
colorField: 'field',
wordStyle: {
fontFamily: 'Verdana',
fontSize: [8, 32],
fontSize: (misc.wordSizeRange ?? DEFAULT_MISC.wordSizeRange) as [number, number],
rotation: [0, 0],
padding: 6
padding: misc.wordSpacing ?? DEFAULT_MISC.wordSpacing
},
random: () => 0.5,
appendPadding: getPadding(chart),

View File

@ -20,7 +20,8 @@ export const TABLE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'tableTitleHeight',
'tableHeaderAlign',
'showIndex',
'indexLabel'
'indexLabel',
'showColTooltip'
],
'table-cell-selector': [
'tableItemBgColor',
@ -29,7 +30,8 @@ export const TABLE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'tableItemAlign',
'tableItemHeight',
'enableTableCrossBG',
'tableItemSubBgColor'
'tableItemSubBgColor',
'showTooltip'
],
'title-selector': [
'title',

View File

@ -169,6 +169,15 @@ export class TableInfo extends S2ChartView<TableSheet> {
action(param)
})
// hover
const { showColTooltip } = customAttr.tableHeader
if (showColTooltip) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
}
const { showTooltip } = customAttr.tableCell
if (showTooltip) {
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click

View File

@ -164,6 +164,15 @@ export class TableNormal extends S2ChartView<TableSheet> {
}
action(param)
})
// hover
const { showColTooltip } = customAttr.tableHeader
if (showColTooltip) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event))
}
const { showTooltip } = customAttr.tableCell
if (showTooltip) {
newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
}
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// right click

View File

@ -32,7 +32,9 @@ export class TablePivot extends S2ChartView<PivotSheet> {
'tableTitleFontSize',
'tableHeaderFontColor',
'tableTitleHeight',
'tableHeaderAlign'
'tableHeaderAlign',
'showColTooltip',
'showRowTooltip'
],
'table-total-selector': ['row', 'col'],
'basic-style-selector': ['tableColumnMode', 'tableBorderColor', 'tableScrollBarColor', 'alpha']
@ -185,7 +187,18 @@ export class TablePivot extends S2ChartView<PivotSheet> {
// 开始渲染
const s2 = new PivotSheet(containerDom, s2DataConfig, s2Options as unknown as S2Options)
// hover
const { showColTooltip, showRowTooltip } = customAttr.tableHeader
if (showColTooltip) {
s2.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(s2, event, meta))
}
if (showRowTooltip) {
s2.on(S2Event.ROW_CELL_HOVER, event => this.showTooltip(s2, event, meta))
}
const { showTooltip } = customAttr.tableCell
if (showTooltip) {
s2.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(s2, event, meta))
}
// click
s2.on(S2Event.DATA_CELL_CLICK, ev => this.dataCellClickAction(chart, ev, s2, action))
s2.on(S2Event.ROW_CELL_CLICK, ev => this.headerCellClickAction(chart, ev, s2, action))

View File

@ -29,9 +29,9 @@ import { DOM } from '@antv/l7-utils'
export function getPadding(chart: Chart): number[] {
if (chart.drill) {
return [0, 10, 26, 10]
return [0, 10, 22, 10]
} else {
return [0, 10, 14, 10]
return [0, 10, 10, 10]
}
}
// color,label,tooltip,axis,legend,background
@ -273,9 +273,7 @@ export function getLegend(chart: Chart) {
offsetY = 0
} else if (l.vPosition === 'bottom') {
if (chart.drill) {
offsetY = -16
} else {
offsetY = -4
offsetY = -12
}
} else {
offsetY = 0
@ -292,9 +290,9 @@ export function getLegend(chart: Chart) {
offsetY = 0
} else if (l.vPosition === 'bottom') {
if (chart.drill) {
offsetY = -22
offsetY = -18
} else {
offsetY = -10
offsetY = -6
}
} else {
offsetY = 0
@ -309,6 +307,7 @@ export function getLegend(chart: Chart) {
marker: {
symbol: legendSymbol
},
itemHeight: l.fontSize + 4,
radio: false,
pageNavigator: {
marker: {
@ -681,10 +680,10 @@ export function getAnalyse(chart: Chart) {
const content =
ele.name +
' : ' +
valueFormatter(value, ele.axisType === 'left' ? axisFormatterCfg : axisExtFormatterCfg)
valueFormatter(value, ele.yAxisType === 'left' ? axisFormatterCfg : axisExtFormatterCfg)
assistLine.push({
type: 'line',
axisType: ele.axisType,
yAxisType: ele.yAxisType,
start: ['start', value],
end: ['end', value],
style: {
@ -694,15 +693,17 @@ export function getAnalyse(chart: Chart) {
})
assistLine.push({
type: 'text',
axisType: ele.axisType,
yAxisType: ele.yAxisType,
position: [
(ele.axisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left' ? 'start' : 'end',
(ele.yAxisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left'
? 'start'
: 'end',
value
],
content: content,
offsetY: -2,
offsetX:
(ele.axisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left'
(ele.yAxisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left'
? 2
: -10 * (content.length - 2),
style: {
@ -898,9 +899,32 @@ export function getTooltipSeriesTotalMap(data: any[]): Record<string, number> {
})
return result
}
export function configL7Legend(): LegendOptions {
const LEGEND_SHAPE_STYLE_MAP = {
circle: {
borderRadius: '50%'
},
square: {},
triangle: {
borderLeft: '5px solid transparent',
borderRight: '5px solid transparent',
borderBottom: '10px solid var(--bgColor)',
background: 'unset'
},
diamond: {
transform: 'rotate(45deg)'
}
}
export function configL7Legend(chart: Chart): LegendOptions | false {
const { basicStyle } = parseJson(chart.customAttr)
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
return false
}
const { legend } = parseJson(chart.customStyle)
if (!legend.show) {
return false
}
return {
position: 'bottomleft',
customContent: (_: string, items: CategoryLegendListItem[]) => {
const showItems = items?.length > 30 ? items.slice(0, 30) : items
if (showItems?.length) {
@ -923,11 +947,22 @@ export function configL7Legend(): LegendOptions {
const domStr = substitute(ITEM_TPL, substituteObj)
const itemDom = createDom(domStr)
// legend 形状用的
itemDom.style.setProperty('--bgColor', item.color)
listDom.appendChild(itemDom)
})
return listDom
}
return ''
},
domStyles: {
'l7plot-legend__category-value': {
fontSize: legend.fontSize + 'px',
color: legend.color
},
'l7plot-legend__category-marker': {
...LEGEND_SHAPE_STYLE_MAP[legend.icon]
}
}
}
}
@ -942,12 +977,18 @@ class CustomZoom extends Zoom {
this.zoomIn
)
const resetBtnIconText = createL7Icon('l7-icon-round')
this['createButton'](resetBtnIconText, 'Reset', 'l7-button-control', container, () => {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
})
this['zoomResetButton'] = this['createButton'](
resetBtnIconText,
'Reset',
'l7-button-control',
container,
() => {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
}
)
if (this.controlOption.showZoom) {
this['zoomNumDiv'] = this['createButton'](
'0',
@ -963,19 +1004,44 @@ class CustomZoom extends Zoom {
container,
this.zoomOut
)
const { buttonColor, buttonBackground } = this.controlOption as any
if (buttonColor) {
const elements = [
this.controlOption.zoomInText,
this.controlOption.zoomOutText,
resetBtnIconText
] as HTMLElement[]
setStyle(elements, 'fill', buttonColor)
}
const elements = [this['zoomResetButton'], this['zoomInButton'], this['zoomOutButton']]
if (buttonBackground) {
setStyle(elements, 'background', buttonBackground)
}
setStyle(elements, 'border-bottom', 'none')
this['updateDisabled']()
}
}
export function configL7Zoom(plot: L7Plot<PlotOptions>) {
const options = plot.options
if (options.zoom === false) {
export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) {
const { basicStyle } = parseJson(chart.customAttr)
if (
(basicStyle.suspension === false && basicStyle.showZoom === undefined) ||
basicStyle.showZoom === false
) {
return
}
plot.once('loaded', () => {
const zoomOptions = {
initZoom: plot.scene.getZoom(),
center: plot.scene.getCenter()
center: plot.scene.getCenter(),
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
plot.scene.addControl(new CustomZoom(zoomOptions))
})
}
function setStyle(elements: HTMLElement[], styleProp: string, value) {
elements.forEach(e => {
e.style[styleProp] = value
})
}

View File

@ -15,7 +15,6 @@ import {
ChartLibraryType
} from '@/views/chart/components/js/panel/types'
import { cloneDeep, defaultsDeep } from 'lodash-es'
import { ChoroplethOptions } from '@antv/l7plot/dist/esm/plots/choropleth'
import { parseJson } from '@/views/chart/components/js/util'
export interface L7PlotDrawOptions<P> extends AntVDrawOptions<P> {
@ -47,12 +46,12 @@ export abstract class L7PlotChartView<
defaultsDeep(options.tooltip, tooltip)
return options
}
protected configLegend(_: Chart, options: ChoroplethOptions) {
const legend = configL7Legend()
defaultsDeep(options.legend, legend)
protected configLegend(chart: Chart, options: O): O {
const legend = configL7Legend(chart)
defaultsDeep(options, { legend })
return options
}
protected configEmptyDataStrategy(chart: Chart, options: ChoroplethOptions): ChoroplethOptions {
protected configEmptyDataStrategy(chart: Chart, options: O): O {
const { functionCfg } = parseJson(chart.senior)
const emptyDataStrategy = functionCfg.emptyDataStrategy
if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') {
@ -75,8 +74,8 @@ export abstract class L7PlotChartView<
return options
}
protected configZoomButton(plot: P) {
configL7Zoom(plot)
protected configZoomButton(chart: Chart, plot: P) {
configL7Zoom(chart, plot)
}
protected constructor(name: string, defaultData?: any[]) {
super(ChartLibraryType.L7_PLOT, name)

View File

@ -3,7 +3,7 @@ import {
AntVDrawOptions,
ChartLibraryType
} from '@/views/chart/components/js/panel/types'
import { S2Theme, SpreadSheet, Style, S2Options } from '@antv/s2'
import { S2Theme, SpreadSheet, Style, S2Options, Meta } from '@antv/s2'
import {
configHeaderInteraction,
configTooltip,
@ -13,6 +13,7 @@ import {
handleTableEmptyStrategy
} from '@/views/chart/components/js/panel/common/common_table'
import '@antv/s2/dist/style.min.css'
import { find } from 'lodash-es'
declare interface PageInfo {
currentPage: number
@ -52,4 +53,40 @@ export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractCha
protected configConditions(chart: Chart) {
return getConditions(chart)
}
protected showTooltip(s2Instance: P, event, metaConfig: Meta[]) {
const cell = s2Instance.getCell(event.target)
const meta = cell.getMeta()
let content = ''
let field
switch (cell.cellType) {
case 'dataCell':
field = find(metaConfig, item => item.field === meta.valueField)
if (meta.fieldValue) {
content = field?.formatter?.(meta.fieldValue)
}
break
case 'rowCell':
case 'colCell':
content = meta.label
field = find(metaConfig, item => item.field === content)
if (field) {
content = field.name
}
break
}
if (!content) {
return
}
event.s2Instance = s2Instance
s2Instance.showTooltip({
position: {
x: event.clientX,
y: event.clientY
},
content,
meta,
event
})
}
}

View File

@ -15,6 +15,7 @@ import ChartError from '@/views/chart/components/views/components/ChartError.vue
import { BASE_VIEW_CONFIG } from '../../editor/util/chart'
import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/canvasStyle'
import { deepCopy } from '@/utils/utils'
import { trackBarStyleCheck } from '@/utils/canvasUtils'
const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc } = storeToRefs(dvMainStore)
@ -202,8 +203,13 @@ const action = param => {
trackClick(trackMenu.value[0])
} else {
//
state.trackBarStyle.left = param.x - 50 + 'px'
state.trackBarStyle.top = param.y + 10 + 'px'
const barStyleTemp = {
left: param.x - 50,
top: param.y + 10
}
trackBarStyleCheck(props.element, barStyleTemp, props.scale)
state.trackBarStyle.left = barStyleTemp.left + 'px'
state.trackBarStyle.top = barStyleTemp.top + 'px'
viewTrack.value.trackButtonClick()
}
}

View File

@ -26,6 +26,7 @@ import { BASE_VIEW_CONFIG } from '../../editor/util/chart'
import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/canvasStyle'
import { deepCopy } from '@/utils/utils'
import { useEmitt } from '@/hooks/web/useEmitt'
import { trackBarStyleCheck } from '@/utils/canvasUtils'
const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc } = storeToRefs(dvMainStore)
@ -247,8 +248,13 @@ const action = param => {
trackClick(trackMenu.value[0])
} else {
//
state.trackBarStyle.left = param.x - 50 + 'px'
state.trackBarStyle.top = param.y + 10 + 'px'
const barStyleTemp = {
left: param.x - 50,
top: param.y + 10
}
trackBarStyleCheck(props.element, barStyleTemp, props.scale)
state.trackBarStyle.left = barStyleTemp.left + 'px'
state.trackBarStyle.top = barStyleTemp.top + 'px'
viewTrack.value.trackButtonClick()
}
}
@ -463,6 +469,7 @@ const autoHeightStyle = computed(() => {
}
.table-page-info {
position: relative;
padding-left: 4px;
margin: 4px;
height: 20px;
display: flex;

View File

@ -19,7 +19,7 @@ import {
import { useEmitt } from '@/hooks/web/useEmitt'
import { hexColorToRGBA } from '@/views/chart/components/js/util.js'
import {
CHART_CONT_FAMILY_MAP,
CHART_FONT_FAMILY_MAP,
DEFAULT_TITLE_STYLE
} from '@/views/chart/components/editor/util/chart'
import DrillPath from '@/views/chart/components/views/components/DrillPath.vue'
@ -153,13 +153,13 @@ const trackMenu = computed<Array<string>>(() => {
})
const hasLinkIcon = computed(() => {
return trackMenu.value.indexOf('linkage') > -1 || trackMenu.value.indexOf('linkageAndDrill')
return trackMenu.value.indexOf('linkage') > -1 || trackMenu.value.indexOf('linkageAndDrill') > -1
})
const hasJumpIcon = computed(() => {
return trackMenu.value.indexOf('jump') > -1 && !mobileInPc.value
})
const hasDrillIcon = computed(() => {
return trackMenu.value.indexOf('drill') > -1 || trackMenu.value.indexOf('linkageAndDrill')
return trackMenu.value.indexOf('drill') > -1 || trackMenu.value.indexOf('linkageAndDrill') > -1
})
const loading = ref(false)
@ -250,7 +250,7 @@ const initTitle = () => {
state.title_class.fontWeight = customStyle.text.isBolder ? 'bold' : 'normal'
state.title_class.fontFamily = customStyle.text.fontFamily
? CHART_CONT_FAMILY_MAP[customStyle.text.fontFamily]
? CHART_FONT_FAMILY_MAP[customStyle.text.fontFamily]
: DEFAULT_TITLE_STYLE.fontFamily
state.title_class.letterSpacing =
(customStyle.text.letterSpace
@ -587,9 +587,9 @@ const toolTip = computed(() => {
const marginBottom = computed<string | 0>(() => {
if (titleShow.value || trackMenu.value.length > 0 || state.title_remark.show) {
return 8 * scale.value + 'px'
return 12 * scale.value + 'px'
}
return 0
return 12
})
const iconSize = computed<string>(() => {
@ -686,6 +686,7 @@ const iconSize = computed<string>(() => {
:view="view"
:scale="scale"
:show-position="showPosition"
:element="element"
v-else-if="showChartView(ChartLibraryType.S2)"
ref="chartComponent"
@onChartClick="chartClick"

View File

@ -317,10 +317,14 @@ const addOperation = (
const baseUrl =
curCanvasType.value === 'dataV' ? '#/dvCanvas?opt=create' : '#/dashboard?opt=create'
let newWindow = null
let embeddedBaseUrl = ''
if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl
}
if (data?.id) {
newWindow = window.open(baseUrl + `&pid=${data.id}`, '_blank')
newWindow = window.open(embeddedBaseUrl + baseUrl + `&pid=${data.id}`, '_blank')
} else {
newWindow = window.open(baseUrl, '_blank')
newWindow = window.open(embeddedBaseUrl + baseUrl, '_blank')
}
initOpenHandler(newWindow)
} else if (cmd === 'newFromTemplate') {
@ -463,19 +467,14 @@ defineExpose({
<el-tooltip content="新建文件夹" placement="top" effect="dark">
<el-icon
class="custom-icon btn"
:style="{ marginRight: isDataEaseBi ? 0 : '20px' }"
style="margin-right: 20px"
@click="addOperation('newFolder', null, 'folder')"
>
<Icon name="dv-new-folder" />
</el-icon>
</el-tooltip>
<el-tooltip
v-if="!isDataEaseBi"
:content="newResourceLabel"
placement="top"
effect="dark"
>
<el-tooltip :content="newResourceLabel" placement="top" effect="dark">
<el-dropdown popper-class="menu-outer-dv_popper" trigger="hover">
<el-icon class="custom-icon btn" @click="addOperation('newLeaf', null, 'leaf', true)">
<Icon name="icon_file-add_outlined" />

View File

@ -9,11 +9,11 @@ const dvMainStore = dvMainStoreWithOut()
const checkItemPosition = component => {
component.x = 1
component.sizeX = 36
component.sizeX = 72
component.y = dvMainStore.componentData.reduce((pre, next) => {
return Math.max(pre, next.y + next.sizeY)
}, 1)
component.sizeY = 10
component.sizeY = 20
}
const hanedleMessage = event => {

View File

@ -9,15 +9,15 @@
</el-tooltip>
<el-row v-show="state.asideActive" style="padding: 24px 12px 0">
<el-row style="align-items: center">
<span class="custom-breadcrumb-item" @click="closePreview()">{{
t('visualization.template_preview')
}}</span>
<span class="custom-breadcrumb-item" @click="closePreview()">模版中心</span>
<el-icon><ArrowRight /></el-icon> <span class="custom-breadcrumb-item-to"></span>
<el-tooltip class="box-item" effect="dark" content="收起" placement="right">
<el-icon class="insert-retract" @click="asideActiveChange(false)">
<Icon name="market-retract"></Icon>
</el-icon>
<div @click="asideActiveChange(false)" class="insert-retract">
<el-icon>
<Icon name="icon_left_outlined" />
</el-icon>
</div>
</el-tooltip>
</el-row>
<el-row class="margin-top16 search-area">
@ -499,34 +499,44 @@ onMounted(() => {
}
}
.arrow-side-tree {
position: absolute;
border: 1px solid #dee0e3;
background: #fff;
cursor: pointer;
z-index: 10;
&:hover {
.ed-icon {
color: var(--ed-color-primary);
}
}
.ed-icon {
font-size: 12px;
}
}
.insert-retract {
position: absolute;
left: 176px;
left: 182px;
top: 2px;
display: inline-block;
font-size: 34px;
font-weight: 400 !important;
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
line-height: 1;
white-space: nowrap;
border: 1px solid #dee0e3;
background: #fff;
cursor: pointer;
color: #646a73;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
transition: 0.1s;
border-radius: 3px;
&:active {
color: #000;
border-color: #3a8ee6;
outline: 0;
}
height: 24px;
width: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0px 5px 10px 0px #1f23291a;
&:hover {
color: #3a8ee6;
.ed-icon {
color: var(--ed-color-primary);
}
}
.ed-icon {
font-size: 12px;
}
}

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { reactive, computed, ref, nextTick, inject, type Ref, watch } from 'vue'
import { reactive, computed, ref, nextTick, inject, type Ref, watch, unref } from 'vue'
import AddSql from './AddSql.vue'
import { useI18n } from '@/hooks/web/useI18n'
import zeroNodeImg from '@/assets/img/drag.png'
@ -108,6 +108,23 @@ const dfsNodeNameList = (list, arr) => {
})
}
const dfsForDsId = (arr, datasourceId) => {
return arr.every(ele => {
if (ele.children?.length) {
return dfsForDsId(ele.children, datasourceId)
}
return ele.datasourceId === datasourceId || !ele.datasourceId
})
}
const crossDatasources = computed(() => {
const { datasourceId, children = [] } = state.nodeList[0] || {}
if (datasourceId && !!children.length) {
return !dfsForDsId(children, datasourceId)
}
return false
})
let isUpdate = false
watch(
@ -234,6 +251,8 @@ const changeNodeFields = val => {
}
const closeEditUnion = () => {
nodeField.value = []
currentNode.value = null
const [fir] = state.nodeList
if (fir.isShadow) {
delete fir.isShadow
@ -831,14 +850,19 @@ const notConfirm = () => {
confirm()
}
const getNodeList = () => {
return cloneDeep(unref(state.nodeList))
}
defineExpose({
nodeNameList,
nodeList: state.nodeList,
getNodeList,
setStateBack,
notConfirm,
dfsNodeFieldBack,
initState,
setChangeStatus
setChangeStatus,
crossDatasources
})
const handleActiveNode = ele => {

View File

@ -120,7 +120,9 @@ const handleChange = () => {
<template>
<el-popover
popper-class="menu-more_popper_one"
:popper-class="
options.length === 6 ? 'menu-more_popper_one menu-more_popper_six' : 'menu-more_popper_one'
"
:persistent="false"
ref="popover"
placement="right"
@ -173,6 +175,9 @@ const handleChange = () => {
</style>
<style lang="less">
.menu-more_popper_six > :first-child > :first-child > :first-child {
height: 210px;
}
.menu-more_popper_one {
padding: 0 !important;
background: transparent !important;

View File

@ -318,7 +318,7 @@ watch(searchTable, val => {
const editeSave = () => {
const union = []
loading.value = true
dfsNodeList(union, datasetDrag.value.nodeList)
dfsNodeList(union, datasetDrag.value.getNodeList())
saveDatasetTree({
...nodeInfo,
name: datasetName.value,
@ -448,7 +448,7 @@ const deleteField = item => {
callback: (action: Action) => {
if (action === 'confirm') {
delFieldById([item.id])
datasetDrag.value.dfsNodeFieldBack(datasetDrag.value.nodeList, item)
datasetDrag.value.dfsNodeFieldBack(datasetDrag.value.getNodeList(), item)
ElMessage({
message: t('chart.delete_success'),
type: 'success'
@ -671,7 +671,6 @@ const addComplete = () => {
const state = reactive({
nodeNameList: [],
editArr: [],
nodeList: [],
dataSourceList: [],
tableData: [],
fieldCollapse: ['dimension', 'quota']
@ -750,7 +749,7 @@ const fieldUnion = ref()
const setFieldAll = () => {
const arr = []
dfsFields(arr, datasetDrag.value.nodeList)
dfsFields(arr, datasetDrag.value.getNodeList())
const delIdArr = getDelIdArr(arr, allfields.value)
allfields.value = diffArr(arr, allfields.value)
delFieldById(delIdArr)
@ -815,6 +814,10 @@ const mouseupDrag = () => {
dom.removeEventListener('mousemove', calculateWidth)
dom.removeEventListener('mousemove', calculateHeight)
}
const crossDatasources = computed(() => {
return datasetDrag.value?.crossDatasources
})
const calculateWidth = (e: MouseEvent) => {
if (e.pageX < 240) {
LeftWidth.value = 240
@ -920,7 +923,7 @@ const resetAllfieldsId = arr => {
const resetAllfieldsUnionId = (arr, idMap) => {
let strUnion = JSON.stringify(arr) as string
let strNodeList = JSON.stringify(toRaw(datasetDrag.value.nodeList)) as string
let strNodeList = JSON.stringify(toRaw(datasetDrag.value.getNodeList())) as string
let strAllfields = JSON.stringify(unref(allfields.value)) as string
Object.entries(idMap).forEach(([key, value]) => {
strUnion = strUnion.replaceAll(key, value as string)
@ -938,7 +941,7 @@ const datasetSave = () => {
return
}
let union = []
dfsNodeList(union, datasetDrag.value.nodeList)
dfsNodeList(union, datasetDrag.value.getNodeList())
const pid = route.query.pid || nodeInfo.pid
if (!union.length) {
ElMessage.error('数据集不能为空')
@ -965,7 +968,7 @@ const datasetPreviewLoading = ref(false)
const datasetPreview = () => {
if (datasetPreviewLoading.value) return
const arr = []
dfsNodeList(arr, datasetDrag.value.nodeList)
dfsNodeList(arr, datasetDrag.value.getNodeList())
datasetPreviewLoading.value = true
getPreviewData({ union: arr, allFields: allfields.value })
.then(res => {
@ -1328,6 +1331,12 @@ const getDsIconName = data => {
</div>
</div>
<div class="drag-right" :style="{ width: `calc(100vw - ${showLeft ? LeftWidth : 0}px)` }">
<div v-if="crossDatasources" class="different-datasource">
<el-icon>
<Icon name="icon_warning_colorful"></Icon>
</el-icon>
您正在进行跨数据源的表关联,请确保使用calcite的标准语法和函数,否则会导致数据集报错
</div>
<dataset-union
@join-editor="joinEditor"
@changeUpdate="changeUpdate"
@ -1343,7 +1352,9 @@ const getDsIconName = data => {
<div
class="sql-result"
:style="{
height: sqlResultHeight ? `${sqlResultHeight}px` : `calc(100% - ${dragHeight}px)`
height: sqlResultHeight
? `${crossDatasources ? sqlResultHeight - 40 : sqlResultHeight}px`
: `calc(100% - ${crossDatasources ? dragHeight + 40 : dragHeight}px)`
}"
>
<div class="sql-title">
@ -2101,6 +2112,23 @@ const getDsIconName = data => {
display: flex;
.drag-right {
height: calc(100vh - 56px);
.different-datasource {
height: 40px;
width: 100%;
background: #ffe7cc;
color: #1f2329;
font-size: 14px;
font-weight: 400;
line-height: 22px;
display: flex;
align-items: center;
padding: 0 16px;
.ed-icon {
font-size: 16px;
margin-right: 8px;
}
}
.sql-result {
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;

View File

@ -320,6 +320,34 @@ function clear_images() {
fi
}
function backup() {
need_stop=0
if [[ -z $1 ]];then
echo "如需备份 DataEase 数据,建议您先停止 DataEase 服务,以保证备份数据的完整性。"
read -r -p "即将备份 DataEase 数据,是否需要停止 DataEase 服务? [Y/n] " input
case $input in
[yY][eE][sS]|[yY])
echo "Yes"
need_stop=1
;;
[nN][oO]|[nN])
echo "No"
;;
*)
echo "无效输入..."
exit 1
;;
esac
elif [[ "$1" == "stop" ]];then
need_stop=1
fi
if [[ $need_stop == 1 ]];then
service dataease stop
else
echo "不停服进行备份"
fi
backup_file_name=dataease-backup-$(date +%Y%m%d)_$(date +%H%M%S).tar.gz
tar --exclude=logs/dataease -zcf $backup_file_name -C $DE_RUNNING_BASE .
if [ $? -ne 0 ]; then
@ -328,6 +356,10 @@ function backup() {
else
echo "备份成功,备份文件 : $backup_file_name"
fi
if [[ $need_stop == 1 ]];then
service dataease start
fi
}
function restore() {
if [[ -z $target ]];then
@ -367,7 +399,7 @@ function main() {
upgrade
;;
backup)
backup
backup $target
;;
restore)
restore $target

View File

@ -1,5 +1,6 @@
package io.dataease.api.chart.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@ -15,7 +16,8 @@ public class ChartSeniorAssistDTO {
private Long fieldId;
private String summary;
private String axis;
private String axisType;
@JsonProperty("yAxisType")
private String yAxisType;
private String value;
private String lineType;
private String color;

View File

@ -1,6 +1,7 @@
package io.dataease.api.visualization;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.visualization.dto.VisualizationComponentDTO;
import io.dataease.api.visualization.dto.VisualizationLinkJumpDTO;
import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest;
import io.dataease.api.visualization.response.VisualizationLinkJumpBaseResponse;
@ -46,7 +47,7 @@ public interface VisualizationLinkJumpApi {
@GetMapping("/viewTableDetailList/{dvId}")
@Operation(summary = "查询跳转明细")
List<VisualizationViewTableVO> viewTableDetailList(@PathVariable Long dvId);
VisualizationComponentDTO viewTableDetailList(@PathVariable Long dvId);
@PostMapping("/updateJumpSetActive")
@Operation(summary = "更新跳转信息可用状态")

View File

@ -0,0 +1,23 @@
package io.dataease.api.visualization.dto;
import io.dataease.api.visualization.vo.VisualizationViewTableVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author : WangJiaHao
* @date : 2024/4/18 17:14
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VisualizationComponentDTO {
private String bashComponentData;
List<VisualizationViewTableVO> visualizationViewTables;
}