Merge branch 'dev-v2' into heatmap

This commit is contained in:
ulleo 2024-05-31 17:03:45 +08:00
commit 4b492d29ca
149 changed files with 3184 additions and 478 deletions

View File

@ -1,6 +1,6 @@
FROM registry.cn-qingdao.aliyuncs.com/dataease/alpine-openjdk17-jre FROM registry.cn-qingdao.aliyuncs.com/dataease/alpine-openjdk21-jre
RUN mkdir -p /opt/apps/config /opt/dataease2.0/drivers/ /opt/dataease2.0/cache/ /opt/dataease2.0/data/map /opt/dataease2.0/data/static-resource/ /opt/dataease2.0/data/appearance/ RUN mkdir -p /opt/apps/config /opt/dataease2.0/drivers/ /opt/dataease2.0/cache/ /opt/dataease2.0/data/map /opt/dataease2.0/data/static-resource/ /opt/dataease2.0/data/appearance/ /opt/dataease2.0/data/exportData/
ADD drivers/* /opt/dataease2.0/drivers/ ADD drivers/* /opt/dataease2.0/drivers/
ADD mapFiles/ /opt/dataease2.0/data/map/ ADD mapFiles/ /opt/dataease2.0/data/map/
@ -11,8 +11,9 @@ WORKDIR /opt/apps
ADD core/core-backend/target/CoreApplication.jar /opt/apps/app.jar ADD core/core-backend/target/CoreApplication.jar /opt/apps/app.jar
ENV JAVA_APP_JAR=/opt/apps/app.jar ENV JAVA_APP_JAR=/opt/apps/app.jar
ENV RUNNING_PORT=8100
ENV JAVA_OPTIONS="-Dfile.encoding=utf-8 -Dloader.path=/opt/apps -Dspring.config.additional-location=/opt/apps/config/" ENV JAVA_OPTIONS="-Dfile.encoding=utf-8 -Dloader.path=/opt/apps -Dspring.config.additional-location=/opt/apps/config/"
HEALTHCHECK --interval=15s --timeout=5s --retries=20 --start-period=30s CMD nc -zv 127.0.0.1 8100 HEALTHCHECK --interval=15s --timeout=5s --retries=20 --start-period=30s CMD nc -zv 127.0.0.1 $RUNNING_PORT
CMD ["/deployments/run-java.sh"] CMD ["/deployments/run-java.sh"]

View File

@ -48,6 +48,10 @@
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
</exclusion> </exclusion>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions> </exclusions>
<classifier>de</classifier> <classifier>de</classifier>
</dependency> </dependency>
@ -83,6 +87,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fit2cloud</groupId> <groupId>com.fit2cloud</groupId>
<artifactId>quartz-spring-boot-starter</artifactId> <artifactId>quartz-spring-boot-starter</artifactId>

View File

@ -5,13 +5,16 @@ import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ViewDetailField; import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest; import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.chart.manage.ChartDataManage; import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.AuthConstant;
import io.dataease.constant.CommonConstants; import io.dataease.constant.CommonConstants;
import io.dataease.engine.constant.DeTypeConstants; import io.dataease.engine.constant.DeTypeConstants;
import io.dataease.exception.DEException; import io.dataease.exception.DEException;
import io.dataease.exportCenter.manage.ExportCenterManage;
import io.dataease.result.ResultCode; import io.dataease.result.ResultCode;
import io.dataease.utils.LogUtil; import io.dataease.utils.LogUtil;
import io.dataease.visualization.manage.VisualizationTemplateExtendDataManage; import io.dataease.visualization.manage.VisualizationTemplateExtendDataManage;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@ -22,12 +25,13 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -38,10 +42,11 @@ import java.util.stream.Collectors;
public class ChartDataServer implements ChartDataApi { public class ChartDataServer implements ChartDataApi {
@Resource @Resource
private ChartDataManage chartDataManage; private ChartDataManage chartDataManage;
@Resource
private ExportCenterManage exportCenterManage;
@Resource @Resource
private VisualizationTemplateExtendDataManage extendDataManage; private VisualizationTemplateExtendDataManage extendDataManage;
@Value("${export.views.limit:500000}") @Value("${export.views.limit:500000}")
private Integer limit; private Integer limit;
@ -76,6 +81,12 @@ public class ChartDataServer implements ChartDataApi {
@Override @Override
public void innerExportDetails(ChartExcelRequest request, HttpServletResponse response) throws Exception { public void innerExportDetails(ChartExcelRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String linkToken = httpServletRequest.getHeader(AuthConstant.LINK_TOKEN_KEY);
if (StringUtils.isEmpty(linkToken)) {
exportCenterManage.addTask(request.getViewId(), "chart", request);
return;
}
OutputStream outputStream = response.getOutputStream(); OutputStream outputStream = response.getOutputStream();
try { try {
findExcelData(request); findExcelData(request);

View File

@ -207,7 +207,7 @@ public class DatasetDataManage {
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap); Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
String querySQL; String querySQL;
if (start == null || count == null) { if (start == null || count == null) {
querySQL = SQLProvider.createQuerySQL(sqlMeta, false, false, needOrder); querySQL = SQLProvider.createQuerySQL(sqlMeta, false, needOrder, false);
} else { } else {
querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, false, start, count); querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, false, start, count);
} }
@ -231,7 +231,7 @@ public class DatasetDataManage {
map.put("allFields", fieldList); map.put("allFields", fieldList);
} }
map.put("sql", Base64.getEncoder().encodeToString(querySQL.getBytes())); map.put("sql", Base64.getEncoder().encodeToString(querySQL.getBytes()));
String replaceSql = SqlUtils.rebuildSQL(SQLProvider.createQuerySQL(sqlMeta, false, false, needOrder), sqlMeta, crossDs, dsMap); String replaceSql = SqlUtils.rebuildSQL(SQLProvider.createQuerySQL(sqlMeta, false, false, false), sqlMeta, crossDs, dsMap);
map.put("total", getDatasetTotal(datasetGroupInfoDTO, replaceSql, null)); map.put("total", getDatasetTotal(datasetGroupInfoDTO, replaceSql, null));
return map; return map;
} }

View File

@ -220,11 +220,12 @@ public class PermissionManage {
private String handleSysVariable(UserFormVO userEntity, String sysVariable) { private String handleSysVariable(UserFormVO userEntity, String sysVariable) {
String value = null; String value = null;
System.out.println(sysVariable);
System.out.println(JsonUtil.toJSONString(userEntity));
if (StringUtils.isNotBlank(sysVariable) && sysVariable.startsWith("${") && sysVariable.endsWith("}")) { if (StringUtils.isNotBlank(sysVariable) && sysVariable.startsWith("${") && sysVariable.endsWith("}")) {
String variableId = sysVariable.substring(2, sysVariable.length() - 1); String variableId = sysVariable.substring(2, sysVariable.length() - 1);
for (SysVariableValueItem variable : userEntity.getVariables()) { for (SysVariableValueItem variable : userEntity.getVariables()) {
if (!variable.isValid()){
continue;
}
if (variableId.equalsIgnoreCase(variable.getVariableId().toString())) { if (variableId.equalsIgnoreCase(variable.getVariableId().toString())) {
if (variable.getSysVariableDto().getType().equalsIgnoreCase("text")) { if (variable.getSysVariableDto().getType().equalsIgnoreCase("text")) {
for (SysVariableValueDto sysVariableValueDto : variable.getValueList()) { for (SysVariableValueDto sysVariableValueDto : variable.getValueList()) {

View File

@ -932,9 +932,9 @@ public class CalciteProvider {
Properties props = new Properties(); Properties props = new Properties();
if (StringUtils.isNotBlank(configuration.getUsername())) { if (StringUtils.isNotBlank(configuration.getUsername())) {
props.setProperty("user", configuration.getUsername()); props.setProperty("user", configuration.getUsername());
if (StringUtils.isNotBlank(configuration.getPassword())) { }
props.setProperty("password", configuration.getPassword()); if (StringUtils.isNotBlank(configuration.getPassword())) {
} props.setProperty("password", configuration.getPassword());
} }
String driverClassName = configuration.getDriver(); String driverClassName = configuration.getDriver();
ExtendedJdbcClassLoader jdbcClassLoader = extendedJdbcClassLoader; ExtendedJdbcClassLoader jdbcClassLoader = extendedJdbcClassLoader;

View File

@ -7,7 +7,7 @@ import java.util.Optional;
*/ */
public class SQLUtils { public class SQLUtils {
public static String transKeyword(String value) { public static String transKeyword(String value) {
return Optional.ofNullable(value).orElse("").replaceAll("'", "\\\\'"); return Optional.ofNullable(value).orElse("").replaceAll("'", "''");
} }
public static String buildOriginPreviewSql(String sql, int limit, int offset) { public static String buildOriginPreviewSql(String sql, int limit, int offset) {

View File

@ -68,7 +68,8 @@ public class Utils {
String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), ele.getDataeaseName())); String.format(SQLConstants.FIELD_NAME, tableObj.getTableAlias(), ele.getDataeaseName()));
} else { } else {
originField = originField.replaceAll("\\[" + ele.getId() + "]", originField = originField.replaceAll("\\[" + ele.getId() + "]",
tableObj.getTableAlias() + "." + datasourceType.getPrefix() + ele.getDataeaseName() + datasourceType.getSuffix()); datasourceType.getPrefix() + tableObj.getTableAlias() + datasourceType.getSuffix() +
"." + datasourceType.getPrefix() + ele.getDataeaseName() + datasourceType.getSuffix());
} }
} else { } else {
originField = originField.replaceAll("\\[" + ele.getId() + "]", "(" + ele.getOriginName() + ")"); originField = originField.replaceAll("\\[" + ele.getId() + "]", "(" + ele.getOriginName() + ")");

Binary file not shown.

View File

@ -0,0 +1,159 @@
package io.dataease.exportCenter.dao.auto.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
/**
* <p>
* 导出任务表
* </p>
*
* @author fit2cloud
* @since 2024-05-23
*/
@TableName("core_export_task")
public class CoreExportTask implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private Long userId;
private String fileName;
private Double fileSize;
private String fileSizeUnit;
private String exportFrom;
private String exportStatus;
private String exportFromType;
private Long exportTime;
private String exportProgress;
private String exportMachineName;
/**
* 过滤参数
*/
private String params;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Double getFileSize() {
return fileSize;
}
public void setFileSize(Double fileSize) {
this.fileSize = fileSize;
}
public String getFileSizeUnit() {
return fileSizeUnit;
}
public void setFileSizeUnit(String fileSizeUnit) {
this.fileSizeUnit = fileSizeUnit;
}
public String getExportFrom() {
return exportFrom;
}
public void setExportFrom(String exportFrom) {
this.exportFrom = exportFrom;
}
public String getExportStatus() {
return exportStatus;
}
public void setExportStatus(String exportStatus) {
this.exportStatus = exportStatus;
}
public String getExportFromType() {
return exportFromType;
}
public void setExportFromType(String exportFromType) {
this.exportFromType = exportFromType;
}
public Long getExportTime() {
return exportTime;
}
public void setExportTime(Long exportTime) {
this.exportTime = exportTime;
}
public String getExportProgress() {
return exportProgress;
}
public void setExportProgress(String exportProgress) {
this.exportProgress = exportProgress;
}
public String getExportMachineName() {
return exportMachineName;
}
public void setExportMachineName(String exportMachineName) {
this.exportMachineName = exportMachineName;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
@Override
public String toString() {
return "CoreExportTask{" +
"id = " + id +
", userId = " + userId +
", fileName = " + fileName +
", fileSize = " + fileSize +
", fileSizeUnit = " + fileSizeUnit +
", exportFrom = " + exportFrom +
", exportStatus = " + exportStatus +
", exportFromType = " + exportFromType +
", exportTime = " + exportTime +
", exportProgress = " + exportProgress +
", exportMachineName = " + exportMachineName +
", params = " + params +
"}";
}
}

View File

@ -0,0 +1,18 @@
package io.dataease.exportCenter.dao.auto.mapper;
import io.dataease.exportCenter.dao.auto.entity.CoreExportTask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 导出任务表 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-05-23
*/
@Mapper
public interface CoreExportTaskMapper extends BaseMapper<CoreExportTask> {
}

View File

@ -0,0 +1,413 @@
package io.dataease.exportCenter.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.api.exportCenter.vo.ExportTaskDTO;
import io.dataease.auth.bo.TokenUserBO;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.server.ChartDataServer;
import io.dataease.engine.constant.DeTypeConstants;
import io.dataease.exception.DEException;
import io.dataease.exportCenter.dao.auto.entity.CoreExportTask;
import io.dataease.exportCenter.dao.auto.mapper.CoreExportTaskMapper;
import io.dataease.utils.*;
import io.dataease.visualization.server.DataVisualizationServer;
import io.dataease.websocket.WsMessage;
import io.dataease.websocket.WsService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.*;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
@Transactional(rollbackFor = Exception.class)
public class ExportCenterManage {
@Resource
private CoreExportTaskMapper exportTaskMapper;
@Resource
DataVisualizationServer dataVisualizationServer;
@Resource
private CoreChartViewMapper coreChartViewMapper;
@Autowired
private WsService wsService;
@Value("${export.dataset.limit:100000}")
private int limit;
private final static String DATA_URL_TITLE = "data:image/jpeg;base64,";
private static final String exportData_path = "/opt/dataease2.0/data/exportData/";
@Value("${extract.page.size:50000}")
private Integer extractPageSize;
static private List<String> STATUS = Arrays.asList("SUCCESS", "FAILED", "PENDING", "IN_PROGRESS", "ALL");
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private int corePoolSize = 10;
private int keepAliveSeconds = 600;
private Map<String, Future> Running_Task = new HashMap<>();
@Resource
private ChartDataServer chartDataServer;
@PostConstruct
public void init() {
scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(corePoolSize);
scheduledThreadPoolExecutor.setKeepAliveTime(keepAliveSeconds, TimeUnit.SECONDS);
}
@Scheduled(fixedRate = 5000)
public void checkRunningTask() {
Iterator<Map.Entry<String, Future>> iterator = Running_Task.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Future> entry = iterator.next();
if (entry.getValue().isDone()) {
iterator.remove();
try {
CoreExportTask exportTask = exportTaskMapper.selectById(entry.getKey());
ExportTaskDTO exportTaskDTO = new ExportTaskDTO();
BeanUtils.copyBean(exportTaskDTO, exportTask);
setExportFromName(exportTaskDTO);
WsMessage message = new WsMessage(exportTask.getUserId(), "/task-export-topic", exportTaskDTO);
wsService.releaseMessage(message);
} catch (Exception e) {
}
}
}
}
public void download(String id, HttpServletResponse response) throws Exception {
CoreExportTask exportTask = exportTaskMapper.selectById(id);
OutputStream outputStream = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + exportTask.getFileName());
InputStream fileInputStream = new FileInputStream(exportData_path + id + "/" + exportTask.getFileName());
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
outputStream.close();
fileInputStream.close();
response.flushBuffer();
}
public void delete(String id) {
Iterator<Map.Entry<String, Future>> iterator = Running_Task.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Future> entry = iterator.next();
if (entry.getKey().equalsIgnoreCase(id)) {
entry.getValue().cancel(true);
iterator.remove();
}
}
FileUtils.deleteDirectoryRecursively(exportData_path + id);
exportTaskMapper.deleteById(id);
}
public void deleteAll(String type) {
if (!STATUS.contains(type)) {
DEException.throwException("无效的状态");
}
QueryWrapper<CoreExportTask> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", AuthUtils.getUser().getUserId());
if (!type.equalsIgnoreCase("ALL")) {
queryWrapper.eq("export_status", type);
}
List<CoreExportTask> exportTasks = exportTaskMapper.selectList(queryWrapper);
exportTasks.parallelStream().forEach(exportTask -> {
Iterator<Map.Entry<String, Future>> iterator = Running_Task.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Future> entry = iterator.next();
if (entry.getKey().equalsIgnoreCase(exportTask.getId())) {
entry.getValue().cancel(true);
iterator.remove();
}
}
FileUtils.deleteDirectoryRecursively(exportData_path + exportTask.getId());
exportTaskMapper.deleteById(exportTask.getId());
});
}
public void delete(List<String> ids) {
ids.forEach(this::delete);
}
public void retry(String id) {
CoreExportTask exportTask = exportTaskMapper.selectById(id);
exportTask.setExportStatus("PENDING");
exportTask.setExportProgress("0");
exportTask.setExportMachineName(hostName());
exportTask.setExportTime(System.currentTimeMillis());
exportTaskMapper.updateById(exportTask);
FileUtils.deleteDirectoryRecursively(exportData_path + id);
if (exportTask.getExportFromType().equalsIgnoreCase("chart")) {
ChartExcelRequest request = JsonUtil.parse(exportTask.getParams(), ChartExcelRequest.class);
startViewTask(exportTask, request);
}
}
public List<ExportTaskDTO> exportTasks(String status) {
if (!STATUS.contains(status)) {
DEException.throwException("Invalid status: " + status);
}
QueryWrapper<CoreExportTask> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", AuthUtils.getUser().getUserId());
queryWrapper.orderByDesc("export_time");
List<CoreExportTask> exportTasks = exportTaskMapper.selectList(queryWrapper);
List<ExportTaskDTO> result = new ArrayList<>();
exportTasks.forEach(exportTask -> {
ExportTaskDTO exportTaskDTO = new ExportTaskDTO();
BeanUtils.copyBean(exportTaskDTO, exportTask);
if (status.equalsIgnoreCase("ALL")) {
setExportFromAbsName(exportTaskDTO);
}
if (status.equalsIgnoreCase(exportTaskDTO.getExportStatus())) {
setExportFromAbsName(exportTaskDTO);
}
result.add(exportTaskDTO);
});
return result;
}
private void setExportFromAbsName(ExportTaskDTO exportTaskDTO) {
if (exportTaskDTO.getExportFromType().equalsIgnoreCase("chart")) {
exportTaskDTO.setExportFromName(dataVisualizationServer.getAbsPath(exportTaskDTO.getExportFrom()));
}
}
private void setExportFromName(ExportTaskDTO exportTaskDTO) {
if (exportTaskDTO.getExportFromType().equalsIgnoreCase("chart")) {
exportTaskDTO.setExportFromName(coreChartViewMapper.selectById(exportTaskDTO.getExportFrom()).getTitle());
}
}
private String hostName() {
String hostname = null;
try {
InetAddress localMachine = InetAddress.getLocalHost();
hostname = localMachine.getHostName();
} catch (Exception e) {
DEException.throwException("请设置主机名!");
}
return hostname;
}
public void addTask(String exportFrom, String exportFromType, ChartExcelRequest request) {
CoreExportTask exportTask = new CoreExportTask();
exportTask.setId(UUID.randomUUID().toString());
exportTask.setUserId(AuthUtils.getUser().getUserId());
exportTask.setExportFrom(exportFrom);
exportTask.setExportFromType(exportFromType);
exportTask.setExportStatus("PENDING");
exportTask.setFileName(request.getViewName() + ".xlsx");
exportTask.setExportProgress("0");
exportTask.setExportTime(System.currentTimeMillis());
exportTask.setParams(JsonUtil.toJSONString(request).toString());
exportTask.setExportMachineName(hostName());
exportTaskMapper.insert(exportTask);
startViewTask(exportTask, request);
}
private void startViewTask(CoreExportTask exportTask, ChartExcelRequest request) {
String dataPath = exportData_path + exportTask.getId();
File directory = new File(dataPath);
boolean isCreated = directory.mkdir();
TokenUserBO tokenUserBO = AuthUtils.getUser();
Future future = scheduledThreadPoolExecutor.submit(() -> {
AuthUtils.setUser(tokenUserBO);
try {
exportTask.setExportStatus("IN_PROGRESS");
exportTaskMapper.updateById(exportTask);
chartDataServer.findExcelData(request);
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
Workbook wb = new SXSSFWorkbook();
//明细sheet
Sheet detailsSheet = wb.createSheet("数据");
//给单元格设置样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
//设置字体大小
font.setFontHeightInPoints((short) 12);
//设置字体加粗
font.setBold(true);
//给字体设置样式
cellStyle.setFont(font);
//设置单元格背景颜色
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//设置单元格填充样式(使用纯色背景颜色填充)
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Boolean mergeHead = false;
ViewDetailField[] detailFields = request.getDetailFields();
if (ArrayUtils.isNotEmpty(detailFields)) {
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
String[] detailField = Arrays.stream(detailFields).map(field -> field.getName()).collect(Collectors.toList()).toArray(new String[detailFields.length]);
Object[] header = request.getHeader();
Row row = detailsSheet.createRow(0);
int headLen = header.length;
int detailFieldLen = detailField.length;
for (int i = 0; i < headLen; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(header[i].toString());
if (i < headLen - 1) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 1, i, i);
detailsSheet.addMergedRegion(cellRangeAddress);
} else {
for (int j = i + 1; j < detailFieldLen + i; j++) {
row.createCell(j).setCellStyle(cellStyle);
}
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, i, i + detailFieldLen - 1);
detailsSheet.addMergedRegion(cellRangeAddress);
}
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(i, 255 * 20);
}
Row detailRow = detailsSheet.createRow(1);
for (int i = 0; i < headLen - 1; i++) {
Cell cell = detailRow.createCell(i);
cell.setCellStyle(cellStyle);
}
for (int i = 0; i < detailFieldLen; i++) {
int colIndex = headLen - 1 + i;
Cell cell = detailRow.createCell(colIndex);
cell.setCellValue(detailField[i]);
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(colIndex, 255 * 20);
}
details.add(1, detailField);
mergeHead = true;
}
if (CollectionUtils.isNotEmpty(details) && (!mergeHead || details.size() > 2)) {
int realDetailRowIndex = 2;
for (int i = (mergeHead ? 2 : 0); i < details.size(); i++) {
Row row = detailsSheet.createRow(realDetailRowIndex > 2 ? realDetailRowIndex : i);
Object[] rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.length; j++) {
Object cellValObj = rowData[j];
if (mergeHead && j == rowData.length - 1 && (cellValObj.getClass().isArray() || cellValObj instanceof ArrayList)) {
Object[] detailRowArray = ((List<Object>) cellValObj).toArray(new Object[((List<?>) cellValObj).size()]);
int detailRowArrayLen = detailRowArray.length;
int temlJ = j;
while (detailRowArrayLen > 1 && temlJ-- > 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(realDetailRowIndex, realDetailRowIndex + detailRowArrayLen - 1, temlJ, temlJ);
detailsSheet.addMergedRegion(cellRangeAddress);
}
for (int k = 0; k < detailRowArrayLen; k++) {
List<Object> detailRows = (List<Object>) detailRowArray[k];
Row curRow = row;
if (k > 0) {
curRow = detailsSheet.createRow(realDetailRowIndex + k);
}
for (int l = 0; l < detailRows.size(); l++) {
Object col = detailRows.get(l);
Cell cell = curRow.createCell(j + l);
cell.setCellValue(col.toString());
}
}
realDetailRowIndex += detailRowArrayLen;
break;
}
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(cellValObj.toString());
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
// with DataType
if ((excelTypes[j].equals(DeTypeConstants.DE_INT) || excelTypes[j].equals(DeTypeConstants.DE_FLOAT)) && StringUtils.isNotEmpty(cellValObj.toString())) {
cell.setCellValue(Double.valueOf(cellValObj.toString()));
} else {
cell.setCellValue(cellValObj.toString());
}
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
}
}
}
}
}
try (FileOutputStream outputStream = new FileOutputStream(dataPath + "/" + request.getViewName() + ".xlsx")) {
wb.write(outputStream);
outputStream.flush();
}
wb.close();
exportTask.setExportProgress("100");
exportTask.setExportStatus("SUCCESS");
setFileSize(dataPath + "/" + request.getViewName() + ".xlsx", exportTask);
} catch (Exception e) {
e.printStackTrace();
LogUtil.error("Failed to export data", e);
exportTask.setExportStatus("FAILED");
} finally {
exportTaskMapper.updateById(exportTask);
}
});
Running_Task.put(exportTask.getId(), future);
}
private void setFileSize(String filePath, CoreExportTask exportTask) {
File file = new File(filePath);
long length = file.length();
String unit = "Mb";
Double size = 0.0;
if ((double) length / 1024 / 1024 > 1) {
if ((double) length / 1024 / 1024 / 1024 > 1) {
unit = "Gb";
size = Double.valueOf(String.format("%.2f", (double) length / 1024 / 1024 / 1024));
} else {
size = Double.valueOf(String.format("%.2f", (double) length / 1024 / 1024));
}
} else {
unit = "Kb";
size = Double.valueOf(String.format("%.2f", (double) length / 1024));
}
exportTask.setFileSize(size);
exportTask.setFileSizeUnit(unit);
}
private static final String LOG_RETENTION = "30";
}

View File

@ -0,0 +1,50 @@
package io.dataease.exportCenter.server;
import io.dataease.api.exportCenter.ExportCenterApi;
import io.dataease.api.exportCenter.vo.ExportTaskDTO;
import io.dataease.exportCenter.manage.ExportCenterManage;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/exportCenter")
@Transactional(rollbackFor = Exception.class)
public class ExportCenterServer implements ExportCenterApi {
@Resource
private ExportCenterManage exportCenterManage;
@Override
public List<ExportTaskDTO> exportTasks(String status) {
return exportCenterManage.exportTasks(status);
}
@Override
public void delete(String id) {
exportCenterManage.delete(id);
}
@Override
public void delete(List<String> ids) {
exportCenterManage.delete(ids);
}
@Override
public void deleteAll(String type) {
exportCenterManage.deleteAll(type);
}
@Override
public void download(String id, HttpServletResponse response) throws Exception {
exportCenterManage.download(id, response);
}
@Override
public void retry(String id) {
exportCenterManage.retry(id);
}
}

View File

@ -2,6 +2,7 @@ package io.dataease.home;
import io.dataease.utils.ModelUtils; import io.dataease.utils.ModelUtils;
import io.dataease.utils.RsaUtils; import io.dataease.utils.RsaUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -11,6 +12,9 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping @RequestMapping
public class RestIndexController { public class RestIndexController {
@Value("${dataease.xpack-front-distributed:false}")
private boolean xpackFrontDistributed;
@GetMapping("/dekey") @GetMapping("/dekey")
@ResponseBody @ResponseBody
public String dekey() { public String dekey() {
@ -23,4 +27,11 @@ public class RestIndexController {
return ModelUtils.isDesktop(); return ModelUtils.isDesktop();
} }
@GetMapping("/xpackModel")
@ResponseBody
public boolean xpackModel() {
return xpackFrontDistributed;
}
} }

View File

@ -174,7 +174,7 @@ public class XpackShareManage {
return pos.stream().map(po -> return pos.stream().map(po ->
new XpackShareGridVO( new XpackShareGridVO(
po.getShareId(), po.getResourceId(), po.getName(), po.getCreator().toString(), po.getShareId(), po.getResourceId(), po.getName(), po.getCreator().toString(),
po.getTime(), po.getExp(), 9,po.getExtFlag())).toList(); po.getTime(), po.getExp(), 9,po.getExtFlag(),po.getType())).toList();
} }
private XpackShareManage proxy() { private XpackShareManage proxy() {

View File

@ -13,6 +13,7 @@ import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationResourceVO; import io.dataease.api.visualization.vo.VisualizationResourceVO;
import io.dataease.api.visualization.vo.VisualizationWatermarkVO; import io.dataease.api.visualization.vo.VisualizationWatermarkVO;
import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.manage.ChartDataManage; import io.dataease.chart.manage.ChartDataManage;
import io.dataease.chart.manage.ChartViewManege; import io.dataease.chart.manage.ChartViewManege;
import io.dataease.commons.constants.DataVisualizationConstants; import io.dataease.commons.constants.DataVisualizationConstants;
@ -30,10 +31,7 @@ import io.dataease.template.dao.auto.entity.VisualizationTemplateExtendData;
import io.dataease.template.dao.auto.mapper.VisualizationTemplateExtendDataMapper; import io.dataease.template.dao.auto.mapper.VisualizationTemplateExtendDataMapper;
import io.dataease.template.dao.auto.mapper.VisualizationTemplateMapper; import io.dataease.template.dao.auto.mapper.VisualizationTemplateMapper;
import io.dataease.template.manage.TemplateCenterManage; import io.dataease.template.manage.TemplateCenterManage;
import io.dataease.utils.AuthUtils; import io.dataease.utils.*;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo; import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo;
import io.dataease.visualization.dao.auto.entity.VisualizationWatermark; import io.dataease.visualization.dao.auto.entity.VisualizationWatermark;
import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper; import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper;
@ -41,6 +39,7 @@ import io.dataease.visualization.dao.auto.mapper.VisualizationWatermarkMapper;
import io.dataease.visualization.dao.ext.mapper.ExtDataVisualizationMapper; import io.dataease.visualization.dao.ext.mapper.ExtDataVisualizationMapper;
import io.dataease.visualization.manage.CoreVisualizationManage; import io.dataease.visualization.manage.CoreVisualizationManage;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -48,10 +47,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -64,6 +60,8 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Resource @Resource
private ChartViewManege chartViewManege; private ChartViewManege chartViewManege;
@Resource
private CoreChartViewMapper coreChartViewMapper;
@Resource @Resource
private ExtDataVisualizationMapper extDataVisualizationMapper; private ExtDataVisualizationMapper extDataVisualizationMapper;
@ -327,6 +325,15 @@ public class DataVisualizationServer implements DataVisualizationApi {
Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>(); Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>();
for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) { for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) {
String originViewId = entry.getKey(); String originViewId = entry.getKey();
Object viewInfo = entry.getValue();
try{
// 旧模板图表过滤器适配
if(viewInfo instanceof Map && ((Map)viewInfo).get("customFilter") instanceof ArrayList){
((Map)viewInfo).put("customFilter",new HashMap<>());
}
}catch(Exception e){
LogUtil.error("History Adaptor Error",e);
}
String originViewData = JsonUtil.toJSONString(entry.getValue()).toString(); String originViewData = JsonUtil.toJSONString(entry.getValue()).toString();
ChartViewDTO chartView = JsonUtil.parseObject(originViewData, ChartViewDTO.class); ChartViewDTO chartView = JsonUtil.parseObject(originViewData, ChartViewDTO.class);
if (chartView == null) { if (chartView == null) {
@ -391,4 +398,42 @@ public class DataVisualizationServer implements DataVisualizationApi {
} }
} }
public String getAbsPath(String id) {
CoreChartView coreChartView = coreChartViewMapper.selectById(id);
if (coreChartView == null) {
return null;
}
if (coreChartView.getSceneId() == null) {
return coreChartView.getTitle();
}
List<DataVisualizationInfo> parents = getParents(coreChartView.getSceneId());
StringBuilder stringBuilder = new StringBuilder();
parents.forEach(ele -> {
if (ObjectUtils.isNotEmpty(ele)) {
stringBuilder.append(ele.getName()).append("/");
}
});
stringBuilder.append(coreChartView.getTitle());
return stringBuilder.toString();
}
public List<DataVisualizationInfo> getParents(Long id) {
List<DataVisualizationInfo> list = new ArrayList<>();
DataVisualizationInfo dataVisualizationInfo = visualizationInfoMapper.selectById(id);
list.add(dataVisualizationInfo);
getParent(list, dataVisualizationInfo);
Collections.reverse(list);
return list;
}
public void getParent(List<DataVisualizationInfo> list, DataVisualizationInfo dataVisualizationInfo) {
if (ObjectUtils.isNotEmpty(dataVisualizationInfo)) {
if (dataVisualizationInfo.getPid() != null) {
DataVisualizationInfo d = visualizationInfoMapper.selectById(dataVisualizationInfo.getPid());
list.add(d);
getParent(list, d);
}
}
}
} }

Binary file not shown.

View File

@ -0,0 +1,33 @@
package io.dataease.websocket.aop;
import io.dataease.websocket.WsMessage;
import io.dataease.websocket.WsService;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Aspect
@Component
public class WSTrigger {
@Autowired
private WsService wsService;
@AfterReturning(value = "execution(* io.dataease.service.message.service.strategy.SendStation.sendMsg(..))")
public void after(JoinPoint point) {
Object[] args = point.getArgs();
Optional.ofNullable(args).ifPresent(objs -> {
if (ArrayUtils.isEmpty(objs)) return;
Object arg = args[0];
Long userId = (Long) arg;
WsMessage message = new WsMessage(userId, "/web-msg-topic", "refresh");
wsService.releaseMessage(message);
});
}
}

View File

@ -0,0 +1,38 @@
package io.dataease.websocket.config;
import io.dataease.websocket.factory.DeWsHandlerFactory;
import io.dataease.websocket.handler.PrincipalHandshakeHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
@Configuration
@EnableWebSocketMessageBroker
public class WsConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOriginPatterns("*")
.setHandshakeHandler(new PrincipalHandshakeHandler())
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/user");
registry.setUserDestinationPrefix("/user");
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
registry.addDecoratorFactory(new DeWsHandlerFactory());
registry.setMessageSizeLimit(8192) //设置消息字节数大小
.setSendBufferSizeLimit(8192)//设置消息缓存大小
.setSendTimeLimit(10000); //设置消息发送时间限制毫秒
}
}

View File

@ -0,0 +1,17 @@
package io.dataease.websocket.entity;
import java.security.Principal;
public class DePrincipal implements Principal {
public DePrincipal(String name) {
this.name = name;
}
private String name;
@Override
public String getName() {
return name;
}
}

View File

@ -0,0 +1,40 @@
package io.dataease.websocket.factory;
import io.dataease.websocket.util.WsUtil;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import java.util.Optional;
public class DeWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
public DeWebSocketHandlerDecorator(WebSocketHandler delegate) {
super(delegate);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
Optional.ofNullable(session.getPrincipal()).ifPresent(principal -> {
String name = principal.getName();
Long userId = Long.parseLong(name);
WsUtil.onLine(userId);
});
super.afterConnectionEstablished(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
Optional.ofNullable(session.getPrincipal()).ifPresent(principal -> {
String name = principal.getName();
Long userId = Long.parseLong(name);
WsUtil.offLine(userId);
});
super.afterConnectionClosed(session, closeStatus);
}
}

View File

@ -0,0 +1,13 @@
package io.dataease.websocket.factory;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
public class DeWsHandlerFactory implements WebSocketHandlerDecoratorFactory {
@Override
public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {
return new DeWebSocketHandlerDecorator(webSocketHandler);
}
}

View File

@ -0,0 +1,29 @@
package io.dataease.websocket.handler;
import io.dataease.websocket.entity.DePrincipal;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import java.security.Principal;
import java.util.Map;
public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();
final String userId = httpRequest.getParameter("userId");
if (StringUtils.isEmpty(userId)) {
return null;
}
return new DePrincipal(userId);
}
return null;
}
}

View File

@ -0,0 +1,21 @@
package io.dataease.websocket.service.impl;
import io.dataease.websocket.WsMessage;
import io.dataease.websocket.WsService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
@Service
public class StandaloneWsService implements WsService {
@Resource
private SimpMessagingTemplate messagingTemplate;
public void releaseMessage(WsMessage wsMessage){
if(ObjectUtils.isEmpty(wsMessage) || ObjectUtils.isEmpty(wsMessage.getUserId()) || ObjectUtils.isEmpty(wsMessage.getTopic())) return;
messagingTemplate.convertAndSendToUser(String.valueOf(wsMessage.getUserId()), wsMessage.getTopic(),wsMessage.getData());
}
}

View File

@ -0,0 +1,41 @@
package io.dataease.websocket.util;
import io.dataease.auth.bo.TokenUserBO;
import io.dataease.utils.AuthUtils;
import org.apache.commons.lang3.ObjectUtils;
import java.util.concurrent.CopyOnWriteArraySet;
public class WsUtil {
private static final CopyOnWriteArraySet<Long> ONLINE_USERS = new CopyOnWriteArraySet();
public static boolean onLine() {
TokenUserBO user = AuthUtils.getUser();
if (ObjectUtils.isNotEmpty(user) && ObjectUtils.isNotEmpty(user.getUserId()))
return onLine(user.getUserId());
return false;
}
public static boolean onLine(Long userId) {
return ONLINE_USERS.add(userId);
}
public static boolean offLine() {
TokenUserBO user = AuthUtils.getUser();
if (ObjectUtils.isNotEmpty(user) && ObjectUtils.isNotEmpty(user.getUserId()))
return offLine(user.getUserId());
return false;
}
public static boolean offLine(Long userId) {
return ONLINE_USERS.remove(userId);
}
public static boolean isOnLine(Long userId) {
return ONLINE_USERS.contains(userId);
}
}

View File

@ -3,6 +3,10 @@ spring:
url: jdbc:mysql://localhost:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true url: jdbc:mysql://localhost:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root username: root
password: 123456 password: 123456
# datasource:
# url: jdbc:mysql://39.98.78.97:13306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
# username: root
# password: Password123@mysql
messages: messages:
basename: i18n/lic,i18n/core,i18n/permissions,i18n/xpack basename: i18n/lic,i18n/core,i18n/permissions,i18n/xpack
flyway: flyway:

View File

@ -24,6 +24,24 @@ ALTER TABLE `xpack_setting_authentication`
ADD COLUMN `valid` tinyint(1) NOT NULL DEFAULT 0 COMMENT '有效' AFTER `synced`; ADD COLUMN `valid` tinyint(1) NOT NULL DEFAULT 0 COMMENT '有效' AFTER `synced`;
DROP TABLE IF EXISTS `core_export_task`;
CREATE TABLE `core_export_task`
(
`id` varchar(255) NOT NULL,
`user_id` bigint(20) NOT NULL,
`file_name` varchar(2048) DEFAULT NULL,
`file_size` DOUBLE DEFAULT NULL,
`file_size_unit` varchar(255) DEFAULT NULL,
`export_from` varchar(255) DEFAULT NULL,
`export_status` varchar(255) DEFAULT NULL,
`export_from_type` varchar(255) DEFAULT NULL,
`export_time` bigint(20) DEFAULT NULL,
`export_progress` varchar(255) DEFAULT NULL,
`export_machine_name` varchar(512) DEFAULT NULL,
`params` longtext NOT NULL COMMENT '过滤参数',
PRIMARY KEY (`id`)
) COMMENT='导出任务表';
DROP TABLE IF EXISTS `xpack_platform_token`; DROP TABLE IF EXISTS `xpack_platform_token`;
CREATE TABLE `xpack_platform_token` CREATE TABLE `xpack_platform_token`
( (
@ -33,3 +51,4 @@ CREATE TABLE `xpack_platform_token`
`exp_time` bigint NOT NULL, `exp_time` bigint NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );

View File

@ -38,6 +38,7 @@
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mathjs": "^11.6.0", "mathjs": "^11.6.0",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"net": "^1.0.2",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.32", "pinia": "^2.0.32",
@ -63,6 +64,8 @@
"@types/element-resize-detector": "^1.1.3", "@types/element-resize-detector": "^1.1.3",
"@types/jquery": "^3.5.16", "@types/jquery": "^3.5.16",
"@types/lodash-es": "^4.17.6", "@types/lodash-es": "^4.17.6",
"@types/sockjs-client": "^1.5.4",
"@types/stompjs": "^2.3.9",
"@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0", "@typescript-eslint/parser": "^5.53.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
@ -80,6 +83,8 @@
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"rimraf": "^4.1.2", "rimraf": "^4.1.2",
"sockjs-client": "^1.6.1",
"stompjs": "^2.3.3",
"stylelint": "^15.2.0", "stylelint": "^15.2.0",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-recommended": "^10.0.1", "stylelint-config-recommended": "^10.0.1",
@ -96,6 +101,7 @@
"vite-plugin-style-import-secondary": "^2.0.0", "vite-plugin-style-import-secondary": "^2.0.0",
"vite-plugin-stylelint": "^4.2.0", "vite-plugin-stylelint": "^4.2.0",
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^1.0.24" "vue-tsc": "^1.0.24",
"xss": "^1.0.14"
} }
} }

View File

@ -36,7 +36,10 @@ export interface DatasetDetail {
fields: { fields: {
dimensionList: Array<Field> dimensionList: Array<Field>
quotaList: Array<Field> quotaList: Array<Field>
parameterList?: Array<Field>
} }
activelist?: string
hasParameter?: boolean
checkList: string[] checkList: string[]
list: Array<Field> list: Array<Field>
} }
@ -247,3 +250,39 @@ export const getFunction = async (): Promise<DatasetDetail[]> => {
return res?.data return res?.data
}) })
} }
export const exportTasks = async (type): Promise<IResponse> => {
return request.post({ url: '/exportCenter/exportTasks/' + type, data: {} }).then(res => {
return res
})
}
export const exportRetry = async (id): Promise<IResponse> => {
return request.post({ url: '/exportCenter/retry/' + id, data: {} }).then(res => {
return res?.data
})
}
export const downloadFile = async (id): Promise<Blob> => {
return request.get({ url: 'exportCenter/download/' + id, responseType: 'blob' }).then(res => {
return res?.data
})
}
export const exportDelete = async (id): Promise<IResponse> => {
return request.get({ url: '/exportCenter/delete/' + id }).then(res => {
return res?.data
})
}
export const exportDeleteAll = async (type, data): Promise<IResponse> => {
return request.post({ url: '/exportCenter/deleteAll/' + type, data }).then(res => {
return res?.data
})
}
export const exportDeletePost = async (data): Promise<IResponse> => {
return request.post({ url: '/exportCenter/delete', data }).then(res => {
return res?.data
})
}

View File

@ -1,3 +1,7 @@
import request from '@/config/axios' import request from '@/config/axios'
export const load = (key: string) => request.get({ url: `/xpackComponent/content/${key}` }) export const load = (key: string) => request.get({ url: `/xpackComponent/content/${key}` })
export const loadDistributed = () => request.get({ url: '/DEXPack.umd.js' })
export const xpackModelApi = () => request.get({ url: '/xpackModel' })

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="1716798774711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3450" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M474.3168 450.56H369.3568l66.4576-73.3184 13.7216-6.144h40.96L549.4784 79.1552c-57.6512 3.584-104.448 20.992-141.312 52.224-40.6528 34.5088-60.8256 79.1552-60.8256 134.2464l1.024 24.1664-143.9744 67.9936 2.8672-32.768c9.6256-111.7184 54.6816-194.9696 134.8608-247.808C420.7616 25.6 537.3952 0 692.224 0H1024l-60.0064 184.832h-73.4208l8.192-25.088c4.096-11.8784 6.0416-23.552 6.0416-34.816C904.8064 96.256 840.3968 76.1856 706.56 76.1856h-7.5776l-59.6992 294.8096H892.928l-84.5824 85.2992-15.6672 5.4272a1191.424 1191.424 0 0 0-158.72-11.264h-10.6496l-27.648 136.9088c-26.624 131.3792-78.1312 236.9536-154.8288 316.2112C363.52 983.6544 272.4864 1024 169.1648 1024 111.3088 1024 59.392 1002.5984 14.336 960.3072L0 946.7904l110.1824-110.08 11.4688 23.3472c24.7808 50.3808 63.1808 74.6496 117.9648 74.6496 50.176 0 88.7808-21.7088 117.76-66.4576 30.72-47.5136 59.0848-134.144 84.48-258.8672L474.3168 450.56z" p-id="3451"></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="1716884731544" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3731" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M872 561.7V93.4c0-17.3-15.1-31.4-33.8-31.4-18.6 0-33.8 14.1-33.8 31.4v468.3c-51.9 14.7-90 62.4-90 119.1s38.1 104.4 90 119.1v130.8c0 17.3 15.1 31.4 33.8 31.4 18.6 0 33.8-14.1 33.8-31.4V799.8c51.9-14.7 90-62.4 90-119.1s-38.1-104.3-90-119z m6 158.8c-11 11-24.2 16.5-39.8 16.5s-28.8-5.5-39.8-16.5c-11-11-16.5-24.2-16.5-39.8 0-15.5 5.5-28.8 16.5-39.8 11-11 24.2-16.5 39.8-16.5S867 630 878 641c11 11 16.5 24.2 16.5 39.8 0 15.5-5.5 28.7-16.5 39.7zM557 201.7V93.4c0-17.3-15.1-31.4-33.8-31.4-18.6 0-33.8 14.1-33.8 31.4v108.3c-51.9 14.7-90 62.4-90 119.1 0 56.6 38.1 104.4 90 119.1v490.8c0 17.3 15.1 31.4 33.8 31.4 18.6 0 33.8-14.1 33.8-31.4V439.8c51.9-14.7 90-62.4 90-119.1 0-56.6-38.1-104.4-90-119z m6 158.8c-11 11-24.2 16.5-39.8 16.5s-28.8-5.5-39.8-16.5c-11-11-16.5-24.2-16.5-39.8s5.5-28.8 16.5-39.8c11-11 24.2-16.5 39.8-16.5S552 270 563 281c11 11 16.5 24.2 16.5 39.8s-5.5 28.7-16.5 39.7zM219.5 561.7V93.4c0-17.3-15.1-31.4-33.8-31.4-18.6 0-33.7 14.1-33.7 31.4v468.3c-51.9 14.7-90 62.4-90 119.1s38.1 104.4 90 119.1v130.8c0 17.3 15.1 31.4 33.8 31.4 18.6 0 33.8-14.1 33.8-31.4V799.8c51.9-14.7 90-62.4 90-119.1s-38.2-104.3-90.1-119z m6 158.8c-11 11-24.2 16.5-39.8 16.5s-28.8-5.5-39.8-16.5c-11-11-16.5-24.2-16.5-39.8 0-15.5 5.5-28.8 16.5-39.8 11-11 24.2-16.5 39.8-16.5s28.8 5.5 39.8 16.5c11 11 16.5 24.2 16.5 39.8 0 15.6-5.5 28.8-16.5 39.8z" p-id="3732"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -13,6 +13,11 @@ const props = defineProps({
themes: { themes: {
type: String as PropType<EditorTheme>, type: String as PropType<EditorTheme>,
default: 'dark' default: 'dark'
},
showSwitch: {
type: Boolean,
required: false,
default: true
} }
}) })
const emit = defineEmits(['update:modelValue', 'modelChange']) const emit = defineEmits(['update:modelValue', 'modelChange'])
@ -45,12 +50,15 @@ const switchValue = computed({
<span> <span>
{{ title }} {{ title }}
</span> </span>
<el-switch <div>
:effect="themes" <el-switch
size="small" v-show="showSwitch"
v-model="switchValue" v-model="switchValue"
@click.stop="onSwitchChange" :effect="themes"
/> size="small"
@click.stop="onSwitchChange"
/>
</div>
</div> </div>
</template> </template>
<slot /> <slot />

View File

@ -285,6 +285,14 @@ const batchOptStatusChange = value => {
} }
const openOuterParamsSet = () => { const openOuterParamsSet = () => {
if (componentData.value.length === 0) {
ElMessage.warning('当前仪表板为空,请先添加组件')
return
}
if (!dvInfo.value.id) {
ElMessage.warning('请先保存当前页面')
return
}
outerParamsSetRef.value.optInit() outerParamsSetRef.value.optInit()
} }

View File

@ -1391,6 +1391,7 @@ onMounted(() => {
if (isMainCanvas(canvasId.value)) { if (isMainCanvas(canvasId.value)) {
initSnapshotTimer() initSnapshotTimer()
initWatermark() initWatermark()
dvMainStore.setEditMode('edit')
} }
// //
composeStore.getEditor(canvasId.value) composeStore.getEditor(canvasId.value)

View File

@ -83,7 +83,7 @@ const { config, showPosition, index, canvasStyleData, canvasViewInfo, dvInfo, se
toRefs(props) toRefs(props)
let currentInstance let currentInstance
const component = ref(null) const component = ref(null)
const emits = defineEmits(['userViewEnlargeOpen']) const emits = defineEmits(['userViewEnlargeOpen', 'onPointClick'])
const htmlToImage = () => { const htmlToImage = () => {
setTimeout(() => { setTimeout(() => {
@ -180,6 +180,10 @@ const commonBackgroundSvgInner = computed(() => {
} }
}) })
const onPointClick = param => {
emits('onPointClick', param)
}
const deepScale = computed(() => scale.value / 100) const deepScale = computed(() => scale.value / 100)
</script> </script>
@ -234,6 +238,7 @@ const deepScale = computed(() => scale.value / 100)
:scale="deepScale" :scale="deepScale"
:disabled="true" :disabled="true"
:is-edit="false" :is-edit="false"
@onPointClick="onPointClick"
/> />
</div> </div>
</div> </div>

View File

@ -90,7 +90,7 @@ const paste = () => {
} }
const deleteComponent = () => { const deleteComponent = () => {
if (curComponent.value) { if (curComponent.value && !isGroupArea.value) {
const curInfo = getCurInfo() const curInfo = getCurInfo()
dvMainStore.deleteComponentById(curComponent.value?.id, curInfo.componentData) dvMainStore.deleteComponentById(curComponent.value?.id, curInfo.componentData)
} else if (areaData.value.components.length) { } else if (areaData.value.components.length) {
@ -153,6 +153,10 @@ const handleComposeMouseDown = e => {
const composeDivider = computed(() => { const composeDivider = computed(() => {
return !(!curComponent || curComponent['isLock'] || curComponent['component'] != 'Group') return !(!curComponent || curComponent['isLock'] || curComponent['component'] != 'Group')
}) })
const isGroupArea = computed(() => {
return curComponent.value?.component === 'GroupArea'
})
</script> </script>
<template> <template>
@ -201,7 +205,7 @@ const composeDivider = computed(() => {
取消组合 取消组合
</li> </li>
<el-divider class="custom-divider" v-show="composeDivider" /> <el-divider class="custom-divider" v-show="composeDivider" />
<template v-if="curComponent"> <template v-if="curComponent && !isGroupArea">
<template v-if="!curComponent['isLock']"> <template v-if="!curComponent['isLock']">
<li @click="upComponent">上移一层</li> <li @click="upComponent">上移一层</li>
<li @click="downComponent">下移一层</li> <li @click="downComponent">下移一层</li>

View File

@ -13,9 +13,10 @@ import { isMainCanvas } from '@/utils/canvasUtils'
import { activeWatermark } from '@/components/watermark/watermark' import { activeWatermark } from '@/components/watermark/watermark'
import { personInfoApi } from '@/api/user' import { personInfoApi } from '@/api/user'
import router from '@/router' import router from '@/router'
import { XpackComponent } from '@/components/plugin'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { pcMatrixCount, curComponent, mobileInPc } = storeToRefs(dvMainStore) const { pcMatrixCount, curComponent, mobileInPc } = storeToRefs(dvMainStore)
const openHandler = ref(null)
const props = defineProps({ const props = defineProps({
canvasStyleData: { canvasStyleData: {
type: Object, type: Object,
@ -238,7 +239,6 @@ const initWatermark = (waterDomId = 'preview-canvas-main') => {
// targetSourceId ID // targetSourceId ID
const winMsgHandle = event => { const winMsgHandle = event => {
console.info('PostMessage Params Received')
const msgInfo = event.data const msgInfo = event.data
// targetSourceId // targetSourceId
if ( if (
@ -246,9 +246,9 @@ const winMsgHandle = event => {
msgInfo.type === 'attachParams' && msgInfo.type === 'attachParams' &&
msgInfo.targetSourceId === dvInfo.value.id + '' msgInfo.targetSourceId === dvInfo.value.id + ''
) { ) {
const attachParam = msgInfo.params const attachParams = msgInfo.params
if (attachParam) { if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParam, componentData.value, 'outer') dvMainStore.addOuterParamsFilter(attachParams, componentData.value, 'outer')
} }
} }
} }
@ -281,6 +281,32 @@ const userViewEnlargeOpen = (opt, item) => {
const handleMouseDown = () => { const handleMouseDown = () => {
dvMainStore.setCurComponent({ component: null, index: null }) dvMainStore.setCurComponent({ component: null, index: null })
} }
const onPointClick = param => {
try {
console.info('de_inner_params send')
if (window['dataease-embedded-host'] && openHandler?.value) {
const pm = {
methodName: 'embeddedInteractive',
args: {
eventName: 'de_inner_params',
args: param
}
}
openHandler.value.invokeMethod(pm)
} else {
console.info('de_inner_params send to host')
const targetPm = {
type: 'dataease-embedded-interactive',
eventName: 'de_inner_params',
args: param
}
window.parent.postMessage(targetPm, '*')
}
} catch (e) {
console.warn('de_inner_params send error')
}
}
defineExpose({ defineExpose({
restore restore
}) })
@ -316,17 +342,14 @@ defineExpose({
:scale="mobileInPc ? 100 : scaleWidth" :scale="mobileInPc ? 100 : scaleWidth"
:is-selector="props.isSelector" :is-selector="props.isSelector"
@userViewEnlargeOpen="userViewEnlargeOpen($event, item)" @userViewEnlargeOpen="userViewEnlargeOpen($event, item)"
@onPointClick="onPointClick"
/> />
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge> <user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>
</div> </div>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
::-webkit-scrollbar {
width: 0px !important;
height: 0px !important;
}
.canvas-container { .canvas-container {
background-size: 100% 100% !important; background-size: 100% 100% !important;
width: 100%; width: 100%;
@ -334,6 +357,10 @@ defineExpose({
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
position: relative; position: relative;
::-webkit-scrollbar {
width: 0px !important;
height: 0px !important;
}
} }
.fix-button { .fix-button {

View File

@ -1,9 +1,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import noLic from './nolic.vue' import noLic from './nolic.vue'
import { ref, useAttrs } from 'vue' import { ref, useAttrs, onMounted } from 'vue'
import { execute, randomKey, formatArray } from './convert' import { execute, randomKey, formatArray } from './convert'
import { load } from '@/api/plugin' import { load, loadDistributed, xpackModelApi } from '@/api/plugin'
import { useCache } from '@/hooks/web/useCache' import { useCache } from '@/hooks/web/useCache'
import { i18n } from '@/plugins/vue-i18n'
import * as Vue from 'vue'
import axios from 'axios'
import * as Pinia from 'pinia'
import * as vueI18n from 'vue-i18n'
import * as vueRouter from 'vue-router'
import { useEmitt } from '@/hooks/web/useEmitt'
const { wsCache } = useCache() const { wsCache } = useCache()
@ -73,11 +80,44 @@ const storeCacheProxy = byteArray => {
}) })
wsCache.set(`de-plugin-proxy`, JSON.stringify(result)) wsCache.set(`de-plugin-proxy`, JSON.stringify(result))
} }
loadComponent()
const pluginProxy = ref(null) const pluginProxy = ref(null)
const invokeMethod = param => { const invokeMethod = param => {
pluginProxy.value['invokeMethod'](param) pluginProxy.value['invokeMethod'](param)
} }
onMounted(async () => {
const key = 'xpack-model-distributed'
let distributed = false
if (wsCache.get(key) === null) {
const res = await xpackModelApi()
wsCache.set('xpack-model-distributed', res.data)
distributed = res.data
} else {
distributed = wsCache.get(key)
}
if (distributed) {
window['Vue'] = Vue
window['Axios'] = axios
window['Pinia'] = Pinia
window['vueI18n'] = vueI18n
window['vueRouter'] = vueRouter
window['MittAll'] = useEmitt().emitter.all
window['i18n'] = i18n
if (window['DEXPack']) {
const xpack = await window['DEXPack'].mapping[attrs.jsname]
plugin.value = xpack.default
} else {
loadDistributed().then(async res => {
new Function(res.data)()
const xpack = await window['DEXPack'].mapping[attrs.jsname]
plugin.value = xpack.default
})
}
} else {
loadComponent()
}
})
const emits = defineEmits(['loadFail']) const emits = defineEmits(['loadFail'])
defineExpose({ defineExpose({
invokeMethod invokeMethod

View File

@ -4,6 +4,7 @@
:append-to-body="true" :append-to-body="true"
v-model="dialogShow" v-model="dialogShow"
width="340px" width="340px"
:show-close="false"
trigger="click" trigger="click"
> >
<el-row style="height: 20px"> <el-row style="height: 20px">

View File

@ -146,7 +146,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted, reactive, toRefs, watch } from 'vue' import { computed, h, onBeforeUnmount, onMounted, reactive, toRefs, watch } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -156,7 +156,8 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import { copyStoreWithOut } from '@/store/modules/data-visualization/copy' import { copyStoreWithOut } from '@/store/modules/data-visualization/copy'
import { exportExcelDownload } from '@/views/chart/components/js/util' import { exportExcelDownload } from '@/views/chart/components/js/util'
import FieldsList from '@/custom-component/rich-text/FieldsList.vue' import FieldsList from '@/custom-component/rich-text/FieldsList.vue'
import { ElTooltip } from 'element-plus-secondary' import { RefreshLeft } from '@element-plus/icons-vue'
import { ElMessage, ElTooltip, ElButton } from 'element-plus-secondary'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut() const snapshotStore = snapshotStoreWithOut()
const copyStore = copyStoreWithOut() const copyStore = copyStoreWithOut()
@ -312,12 +313,45 @@ const showBarTooltipPosition = computed(() => {
} }
}) })
const openMessageLoading = cb => {
const iconClass = `el-icon-loading`
const customClass = `de-message-loading de-message-export`
ElMessage({
message: h('p', null, [
'后台导出中,可前往',
h(
ElButton,
{
text: true,
size: 'small',
class: 'btn-text',
onClick: () => {
cb()
}
},
t('data_export.export_center')
),
'查看进度,进行下载'
]),
iconClass,
icon: h(RefreshLeft),
showClose: true,
customClass
})
}
const callbackExport = () => {
useEmitt().emitter.emit('data-export-center')
}
const exportAsExcel = () => { const exportAsExcel = () => {
const viewDataInfo = dvMainStore.getViewDataDetails(element.value.id) const viewDataInfo = dvMainStore.getViewDataDetails(element.value.id)
const chartExtRequest = dvMainStore.getLastViewRequestInfo(element.value.id) const chartExtRequest = dvMainStore.getLastViewRequestInfo(element.value.id)
const viewInfo = dvMainStore.getViewDetails(element.value.id) const viewInfo = dvMainStore.getViewDetails(element.value.id)
const chart = { ...viewInfo, chartExtRequest, data: viewDataInfo } const chart = { ...viewInfo, chartExtRequest, data: viewDataInfo }
exportExcelDownload(chart) exportExcelDownload(chart, () => {
openMessageLoading(callbackExport)
})
} }
const exportAsImage = () => { const exportAsImage = () => {
// do export // do export

View File

@ -71,7 +71,7 @@
<script setup lang="ts"> <script setup lang="ts">
import ComponentWrapper from '@/components/data-visualization/canvas/ComponentWrapper.vue' import ComponentWrapper from '@/components/data-visualization/canvas/ComponentWrapper.vue'
import { computed, nextTick, ref } from 'vue' import { computed, h, nextTick, ref } from 'vue'
import { toPng } from 'html-to-image' import { toPng } from 'html-to-image'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { deepCopy } from '@/utils/utils' import { deepCopy } from '@/utils/utils'
@ -79,8 +79,10 @@ import ChartComponentS2 from '@/views/chart/components/views/components/ChartCom
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { exportExcelDownload } from '@/views/chart/components/js/util' import { exportExcelDownload } from '@/views/chart/components/js/util'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { RefreshLeft } from '@element-plus/icons-vue'
import { assign } from 'lodash-es' import { assign } from 'lodash-es'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import { ElMessage, ElButton } from 'element-plus-secondary'
const downLoading = ref(false) const downLoading = ref(false)
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const dialogShow = ref(false) const dialogShow = ref(false)
@ -201,10 +203,41 @@ const downloadViewDetails = () => {
} }
exportLoading.value = true exportLoading.value = true
exportExcelDownload(chart, () => { exportExcelDownload(chart, () => {
openMessageLoading(exportData)
exportLoading.value = false exportLoading.value = false
}) })
} }
const exportData = () => {
useEmitt().emitter.emit('data-export-center')
}
const openMessageLoading = cb => {
const iconClass = `el-icon-loading`
const customClass = `de-message-loading de-message-export`
ElMessage({
message: h('p', null, [
'后台导出中,可前往',
h(
ElButton,
{
text: true,
size: 'small',
class: 'btn-text',
onClick: () => {
cb()
}
},
t('data_export.export_center')
),
'查看进度,进行下载'
]),
iconClass,
icon: h(RefreshLeft),
showClose: true,
customClass
})
}
const htmlToImage = () => { const htmlToImage = () => {
downLoading.value = true downLoading.value = true
useEmitt().emitter.emit('renderChart-' + viewInfo.value.id) useEmitt().emitter.emit('renderChart-' + viewInfo.value.id)

View File

@ -171,6 +171,8 @@ service.interceptors.response.use(
} else if (response.config.url.match(/^\/map|geo\/\d{3}\/\d+\.json$/)) { } else if (response.config.url.match(/^\/map|geo\/\d{3}\/\d+\.json$/)) {
// TODO 处理静态文件 // TODO 处理静态文件
return response return response
} else if (response.config.url.includes('DEXPack.umd.js')) {
return response
} else { } else {
if ( if (
!response?.config?.url.startsWith('/xpackComponent/content') && !response?.config?.url.startsWith('/xpackComponent/content') &&

View File

@ -2,6 +2,39 @@
<el-row class="custom-row"> <el-row class="custom-row">
<el-row class="custom-row-inner"> <el-row class="custom-row-inner">
<el-space wrap> <el-space wrap>
<template v-for="styleOptionKey in styleOptionKeyArrayPre">
<el-tooltip
:key="styleOptionKey.value"
v-if="styleForm[styleOptionKey.value] !== undefined"
:effect="themes"
placement="bottom"
>
<template #content> {{ styleOptionKey.label }} </template>
<el-form-item class="form-item no-margin-bottom" :class="'form-item-' + themes">
<el-select
:style="{ width: styleOptionKey.width }"
:effect="themes"
v-model="styleForm[styleOptionKey.value]"
size="small"
@change="changeStyle"
>
<template #prefix>
<el-icon :class="{ 'dark-icon': themes === 'dark' }">
<Icon :name="styleOptionKey.icon" />
</el-icon>
</template>
<el-option
class="custom-style-option"
v-for="option in styleOptionKey.customOption"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-tooltip>
</template>
<template v-for="styleColorKey in styleColorKeyArray"> <template v-for="styleColorKey in styleColorKeyArray">
<el-tooltip <el-tooltip
:key="styleColorKey.value" :key="styleColorKey.value"
@ -274,18 +307,25 @@ const styleMounted = ref({
color: '#000000' color: '#000000'
}) })
const fontFamilyList = [
{ name: '微软雅黑', value: 'Microsoft YaHei' },
{ name: '宋体', value: 'SimSun, "Songti SC", STSong' },
{ name: '黑体', value: 'SimHei, Helvetica' },
{ name: '楷体', value: 'KaiTi, "Kaiti SC", STKaiti' }
]
const scrollSpeedList = [ const scrollSpeedList = [
{ name: '停止', value: 0 }, { name: '停止', value: 0 },
{ name: '1', value: 20 }, { name: '1', value: 80 },
{ name: '2', value: 18 }, { name: '2', value: 60 },
{ name: '3', value: 16 }, { name: '3', value: 40 },
{ name: '4', value: 14 }, { name: '4', value: 30 },
{ name: '5', value: 12 }, { name: '5', value: 20 },
{ name: '6', value: 10 }, { name: '6', value: 15 },
{ name: '7', value: 8 }, { name: '7', value: 10 },
{ name: '8', value: 6 }, { name: '8', value: 8 },
{ name: '9', value: 4 }, { name: '9', value: 6 },
{ name: '10', value: 2 } { name: '10', value: 3 }
] ]
const opacitySizeList = [ const opacitySizeList = [
@ -376,6 +416,16 @@ const borderStyleList = [
{ name: '点线', value: 'dotted' } { name: '点线', value: 'dotted' }
] ]
const styleOptionKeyArrayPre = [
{
value: 'fontFamily',
label: '字体',
customOption: fontFamilyList,
width: '188px',
icon: 'dv-style-fontFamily'
}
]
// //
const styleOptionMountedKeyArray = [ const styleOptionMountedKeyArray = [
{ {

View File

@ -498,7 +498,8 @@ const list = [
color: '', color: '',
padding: 4, padding: 4,
verticalAlign: 'middle', verticalAlign: 'middle',
scrollSpeed: 0 scrollSpeed: 0,
fontFamily: 'Microsoft YaHei'
} }
} }
] ]

View File

@ -117,7 +117,6 @@ const destroyPlayer = () => {
justify-content: center; justify-content: center;
background-color: rgba(245, 245, 220, 0.1); background-color: rgba(245, 245, 220, 0.1);
font-size: 12px; font-size: 12px;
color: #000000;
} }
.move-bg { .move-bg {

View File

@ -88,7 +88,6 @@ watch(
justify-content: center; justify-content: center;
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
font-size: 12px; font-size: 12px;
color: #9ea6b2;
} }
.player { .player {

View File

@ -467,11 +467,10 @@ defineExpose({
height: 100%; height: 100%;
overflow-y: auto !important; overflow-y: auto !important;
position: relative; position: relative;
} ::-webkit-scrollbar {
width: 0px !important;
::-webkit-scrollbar { height: 0px !important;
width: 0px !important; }
height: 0px !important;
} }
:deep(.ol) { :deep(.ol) {

View File

@ -78,8 +78,6 @@ const clearStyle = e => {
if (text !== '') { if (text !== '') {
document.execCommand('insertText', false, text) document.execCommand('insertText', false, text)
} }
emit('input', element.value, e.target.innerHTML)
} }
const handleBlur = e => { const handleBlur = e => {
@ -141,11 +139,12 @@ const textStyle = computed(() => {
@mousedown="handleMousedown" @mousedown="handleMousedown"
@blur="handleBlur" @blur="handleBlur"
@input="handleInput" @input="handleInput"
v-html="element['propValue']" >
></div> {{ element['propValue'] }}
</div>
</div> </div>
<div v-else class="v-text preview"> <div v-else class="v-text preview" :style="varStyle">
<div class="marquee-txt" :style="textStyle" v-html="element['propValue']"></div> <div class="marquee-txt" :style="textStyle">{{ element['propValue'] }}</div>
</div> </div>
</template> </template>

View File

@ -71,6 +71,11 @@ const autoStyle = computed(() => {
return {} return {}
} }
}) })
const emits = defineEmits(['onPointClick'])
const onPointClick = param => {
emits('onPointClick', param)
}
</script> </script>
<template> <template>
@ -83,6 +88,7 @@ const autoStyle = computed(() => {
:show-position="showPosition" :show-position="showPosition"
:search-count="searchCount" :search-count="searchCount"
:disabled="disabled" :disabled="disabled"
@onPointClick="onPointClick"
></chart> ></chart>
</div> </div>
</template> </template>

View File

@ -107,7 +107,6 @@ const activeCondition = ref('')
const isIndeterminate = ref(false) const isIndeterminate = ref(false)
const datasetTree = shallowRef([]) const datasetTree = shallowRef([])
const fields = ref<DatasetDetail[]>() const fields = ref<DatasetDetail[]>()
const parameters = ref([])
const { queryElement } = toRefs(props) const { queryElement } = toRefs(props)
@ -191,6 +190,9 @@ const setParameters = () => {
if (!!curComponent.value.parameters.length) { if (!!curComponent.value.parameters.length) {
curComponent.value.conditionType = 0 curComponent.value.conditionType = 0
if (curComponent.value.optionValueSource === 0) {
curComponent.value.optionValueSource = 1
}
} }
}) })
setType() setType()
@ -1386,7 +1388,9 @@ defineExpose({
@change="handleValueSourceChange" @change="handleValueSourceChange"
v-model="curComponent.optionValueSource" v-model="curComponent.optionValueSource"
> >
<el-radio :label="0">{{ t('chart.margin_model_auto') }}</el-radio> <el-radio :disabled="!!curComponent.parameters.length" :label="0">{{
t('chart.margin_model_auto')
}}</el-radio>
<el-radio :label="1">{{ t('chart.select_dataset') }}</el-radio> <el-radio :label="1">{{ t('chart.select_dataset') }}</el-radio>
<el-radio :label="2">手动输入</el-radio> <el-radio :label="2">手动输入</el-radio>
</el-radio-group> </el-radio-group>

View File

@ -4,6 +4,7 @@ import { usePermissionStore } from '@/store/modules/permission'
import { isExternal } from '@/utils/validate' import { isExternal } from '@/utils/validate'
import { formatRoute } from '@/router/establish' import { formatRoute } from '@/router/establish'
import HeaderMenuItem from './HeaderMenuItem.vue' import HeaderMenuItem from './HeaderMenuItem.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { ElHeader, ElMenu } from 'element-plus-secondary' import { ElHeader, ElMenu } from 'element-plus-secondary'
import SystemCfg from './SystemCfg.vue' import SystemCfg from './SystemCfg.vue'
@ -15,8 +16,8 @@ import { isDesktop } from '@/utils/ModelUtil'
import { XpackComponent } from '@/components/plugin' import { XpackComponent } from '@/components/plugin'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance' import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
import AiComponent from '@/layout/components/AiComponent.vue' import AiComponent from '@/layout/components/AiComponent.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { findBaseParams } from '@/api/aiComponent' import { findBaseParams } from '@/api/aiComponent'
import ExportExcel from '@/views/visualized/data/dataset/ExportExcel.vue'
import AiTips from '@/layout/components/AiTips.vue' import AiTips from '@/layout/components/AiTips.vue'
const appearanceStore = useAppearanceStoreWithOut() const appearanceStore = useAppearanceStoreWithOut()
@ -42,7 +43,10 @@ const activeIndex = computed(() => {
}) })
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const ExportExcelRef = ref()
const downloadClick = () => {
ExportExcelRef.value.init()
}
const routers: any[] = formatRoute(permissionStore.getRoutersNotHidden as AppCustomRouteRecordRaw[]) const routers: any[] = formatRoute(permissionStore.getRoutersNotHidden as AppCustomRouteRecordRaw[])
const showSystem = ref(false) const showSystem = ref(false)
const showToolbox = ref(false) const showToolbox = ref(false)
@ -87,6 +91,10 @@ onMounted(() => {
initShowSystem() initShowSystem()
initShowToolbox() initShowToolbox()
initAiBase() initAiBase()
useEmitt({
name: 'data-export-center',
callback: downloadClick
})
}) })
</script> </script>
@ -118,6 +126,9 @@ onMounted(() => {
> >
<Icon name="dv-ai" @click="handleAiClick" /> <Icon name="dv-ai" @click="handleAiClick" />
</el-icon> </el-icon>
<el-icon style="margin: 0 10px">
<Icon name="dv-preview-download" @click="downloadClick" />
</el-icon>
<ai-tips <ai-tips
@confirm="aiTipsConfirm" @confirm="aiTipsConfirm"
v-if="showOverlay && appearanceStore.getShowAi" v-if="showOverlay && appearanceStore.getShowAi"
@ -134,6 +145,7 @@ onMounted(() => {
<div v-if="showOverlay && appearanceStore.getShowAi" class="overlay"></div> <div v-if="showOverlay && appearanceStore.getShowAi" class="overlay"></div>
</div> </div>
</el-header> </el-header>
<ExportExcel ref="ExportExcelRef"></ExportExcel>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -50,6 +50,37 @@ export default {
filter_condition: '筛选条件', filter_condition: '筛选条件',
no_auth_tips: '缺少菜单权限请联系管理员' no_auth_tips: '缺少菜单权限请联系管理员'
}, },
data_export: {
export_center: '数据导出中心',
export_info: '查看进度进行下载',
exporting: '后台导出中,可前往',
del_all: '全部删除',
export_failed: '导出失败',
export_from: '导出来源',
export_obj: '导出对象',
export_time: '导出时间',
sure_del_all: '确定删除全部导出记录吗',
sure_del: '确定删除该导出记录吗',
no_failed_file: '暂无失败文件',
no_file: '暂无文件',
no_task: '暂无任务',
download_all: '下载全部',
download: '下载'
},
driver: {
driver: '驱动',
please_choose_driver: '请选择驱动',
mgm: '驱动管理',
exit_mgm: '退出驱动管理',
add: '添加驱动',
modify: '修改',
show_info: '驱动信息',
file_name: '文件名',
version: '版本',
please_set_driverClass: '请指定驱动类',
please_set_surpportVersions: '请输入支持的数据库大版本',
surpportVersions: '支持版本'
},
login: { login: {
welcome: '欢迎使用', welcome: '欢迎使用',
btn: '登录', btn: '登录',
@ -291,6 +322,7 @@ export default {
please_input_host: '请输入主机', please_input_host: '请输入主机',
please_input_url: '请输入URL地址', please_input_url: '请输入URL地址',
please_input_port: '请输入端口', please_input_port: '请输入端口',
please_input_be_port: '请输入BE端口',
modify: '编辑数据源', modify: '编辑数据源',
copy: '复制数据源', copy: '复制数据源',
validate_success: '校验成功', validate_success: '校验成功',
@ -656,6 +688,7 @@ export default {
table_show_row_tooltip: '开启行头提示', table_show_row_tooltip: '开启行头提示',
table_show_col_tooltip: '开启列头提示', table_show_col_tooltip: '开启列头提示',
table_show_cell_tooltip: '开启单元格提示', table_show_cell_tooltip: '开启单元格提示',
table_show_header_tooltip: '开启表头提示',
stripe: '斑马纹', stripe: '斑马纹',
start_angle: '起始角度', start_angle: '起始角度',
end_angle: '结束角度', end_angle: '结束角度',
@ -680,11 +713,11 @@ export default {
chart_bar: '基础柱状图', chart_bar: '基础柱状图',
chart_bar_stack: '堆叠柱状图', chart_bar_stack: '堆叠柱状图',
chart_percentage_bar_stack: '百分比柱状图', chart_percentage_bar_stack: '百分比柱状图',
chart_bar_horizontal: '横向柱状', chart_bar_horizontal: '基础条形',
chart_bar_stack_horizontal: '横向堆叠柱状', chart_bar_stack_horizontal: '堆叠条形',
chart_percentage_bar_stack_horizontal: '横向百分比柱状', chart_percentage_bar_stack_horizontal: '百分比条形',
chart_bar_range: '区间条形图', chart_bar_range: '区间条形图',
chart_bidirectional_bar: '对称柱状', chart_bidirectional_bar: '对称条形',
chart_progress_bar: '进度条', chart_progress_bar: '进度条',
chart_line: '基础折线图', chart_line: '基础折线图',
chart_area_stack: '堆叠折线图', chart_area_stack: '堆叠折线图',
@ -851,7 +884,7 @@ export default {
chart_type_table: '表格', chart_type_table: '表格',
chart_type_quota: '指标', chart_type_quota: '指标',
chart_type_trend: '线/面图', chart_type_trend: '线/面图',
chart_type_compare: '', chart_type_compare: '/',
chart_type_distribute: '分布图', chart_type_distribute: '分布图',
chart_type_relation: '关系图', chart_type_relation: '关系图',
chart_type_dual_axes: '双轴图', chart_type_dual_axes: '双轴图',
@ -1141,7 +1174,10 @@ export default {
top_n_input_2: ', 其余合并至其他', top_n_input_2: ', 其余合并至其他',
top_n_label: '其他项名称', top_n_label: '其他项名称',
progress_target: '目标值', progress_target: '目标值',
progress_current: '实际值' progress_current: '实际值',
gauge_axis_label: '显示刻度',
gauge_percentage_tick: '百分比刻度',
add_style: '添加样式'
}, },
dataset: { dataset: {
scope_edit: '仅编辑时生效', scope_edit: '仅编辑时生效',
@ -1412,6 +1448,7 @@ export default {
pls_input_filename: '请输入文件名称', pls_input_filename: '请输入文件名称',
calc_tips: { calc_tips: {
tip1: '表达式语法请遵循calcite语法', tip1: '表达式语法请遵循calcite语法',
tip1_1: '表达式语法请遵循该数据源对应的数据库语法',
tip2: '聚合运算仅能在图表中生效', tip2: '聚合运算仅能在图表中生效',
tip3: '引用字段以 "[" 开始 "]" 结束', tip3: '引用字段以 "[" 开始 "]" 结束',
tip4: '请勿修改引用内容否则将引用失败', tip4: '请勿修改引用内容否则将引用失败',
@ -1904,7 +1941,7 @@ export default {
yes: '是', yes: '是',
no: '否', no: '否',
live_tips: '优先HTTPS链接', live_tips: '优先HTTPS链接',
stream_media_add_tips: '在右侧添加流媒体信息...', stream_media_add_tips: '添加流媒体信息...',
stream_mobile_tips: 'IOS终端可能无法显示', stream_mobile_tips: 'IOS终端可能无法显示',
json_params_error: '第三方参数解析失败请检查参数格式是否正确', json_params_error: '第三方参数解析失败请检查参数格式是否正确',
inner_padding: '内边距', inner_padding: '内边距',
@ -2142,8 +2179,8 @@ export default {
play_circle: '循环播放', play_circle: '循环播放',
video_links: '视频链接', video_links: '视频链接',
web_url: '网页地址', web_url: '网页地址',
video_add_tips: '在右侧添加视频信息...', video_add_tips: '配置视频信息...',
link_add_tips_pre: '在右侧配置网页信息..', link_add_tips_pre: '配置网页信息..',
web_add_tips_suf: '添加网页信息...', web_add_tips_suf: '添加网页信息...',
panel_view_result_show: '图表结果', panel_view_result_show: '图表结果',
panel_view_result_tips: '选择{0}会覆盖图表的结果展示数量取值范围1~10000', panel_view_result_tips: '选择{0}会覆盖图表的结果展示数量取值范围1~10000',

View File

@ -104,6 +104,14 @@ declare interface ChartBasicStyle {
* 仪表盘样式 * 仪表盘样式
*/ */
gaugeStyle: string gaugeStyle: string
/**
* 仪表盘刻度显示
*/
gaugeAxisLine: boolean
/**
* 仪表盘百分比刻度
*/
gaugePercentLabel: boolean
/** /**
* 配色方案 * 配色方案
*/ */
@ -274,13 +282,19 @@ declare interface ChartTableHeaderAttr {
*/ */
tableHeaderSort: boolean tableHeaderSort: boolean
/** /**
* @deprecated since version 2.7.0 由提示统一控制
* 行头鼠标悬浮提示开关 * 行头鼠标悬浮提示开关
*/ */
showRowTooltip: boolean showRowTooltip: boolean
/** /**
* @deprecated since version 2.7.0 由提示统一控制
* 列头鼠标悬浮提示开关 * 列头鼠标悬浮提示开关
*/ */
showColTooltip: boolean showColTooltip: boolean
/**
* 表头显示开关
*/
showTableHeader: boolean
} }
/** /**
* 单元格属性 * 单元格属性
@ -315,6 +329,7 @@ declare interface ChartTableCellAttr {
*/ */
tableItemSubBgColor: string tableItemSubBgColor: string
/** /**
* @deprecated since version 2.7.0 由提示统一控制
* 鼠标悬浮提示 * 鼠标悬浮提示
*/ */
showTooltip: boolean showTooltip: boolean

View File

@ -14,6 +14,7 @@ import { setupCustomComponent } from '@/custom-component'
import { installDirective } from '@/directive' import { installDirective } from '@/directive'
import '@/utils/DateUtil' import '@/utils/DateUtil'
import '@/permission' import '@/permission'
import WebSocketPlugin from '../../websocket'
const setupAll = async () => { const setupAll = async () => {
const app = createApp(App) const app = createApp(App)
installDirective(app) installDirective(app)
@ -23,6 +24,7 @@ const setupAll = async () => {
setupElementPlus(app) setupElementPlus(app)
setupCustomComponent(app) setupCustomComponent(app)
setupElementPlusIcons(app) setupElementPlusIcons(app)
app.use(WebSocketPlugin)
app.mount('#app') app.mount('#app')
} }

View File

@ -10,6 +10,7 @@ const DashboardEditor = defineAsyncComponent(() => import('@/views/dashboard/ind
const Dashboard = defineAsyncComponent(() => import('./DashboardPreview.vue')) const Dashboard = defineAsyncComponent(() => import('./DashboardPreview.vue'))
const ViewWrapper = defineAsyncComponent(() => import('./ViewWrapper.vue')) const ViewWrapper = defineAsyncComponent(() => import('./ViewWrapper.vue'))
const Iframe = defineAsyncComponent(() => import('./Iframe.vue'))
const Dataset = defineAsyncComponent(() => import('@/views/visualized/data/dataset/index.vue')) const Dataset = defineAsyncComponent(() => import('@/views/visualized/data/dataset/index.vue'))
const Datasource = defineAsyncComponent( const Datasource = defineAsyncComponent(
() => import('@/views/visualized/data/datasource/index.vue') () => import('@/views/visualized/data/datasource/index.vue')
@ -18,6 +19,9 @@ const ScreenPanel = defineAsyncComponent(() => import('@/views/data-visualizatio
const DashboardPanel = defineAsyncComponent( const DashboardPanel = defineAsyncComponent(
() => import('@/views/dashboard/DashboardPreviewShow.vue') () => import('@/views/dashboard/DashboardPreviewShow.vue')
) )
const Preview = defineAsyncComponent(() => import('@/views/data-visualization/PreviewCanvas.vue'))
const props = defineProps({ const props = defineProps({
componentName: propTypes.string.def('DashboardEditor') componentName: propTypes.string.def('DashboardEditor')
}) })
@ -27,8 +31,10 @@ const componentMap = {
DashboardEditor, DashboardEditor,
VisualizationEditor, VisualizationEditor,
ViewWrapper, ViewWrapper,
Preview,
Dashboard, Dashboard,
Dataset, Dataset,
Iframe,
Datasource, Datasource,
ScreenPanel, ScreenPanel,
DashboardPanel DashboardPanel

View File

@ -39,7 +39,7 @@ onBeforeMount(async () => {
} }
// //
let attachParam let attachParams
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => { await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfo(rsp.data) dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
}) })
@ -48,7 +48,7 @@ onBeforeMount(async () => {
if (embeddedStore.outerParams) { if (embeddedStore.outerParams) {
try { try {
const outerPramsParse = JSON.parse(embeddedStore.outerParams) const outerPramsParse = JSON.parse(embeddedStore.outerParams)
attachParam = outerPramsParse.attachParam attachParams = outerPramsParse.attachParams
dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no') dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -74,8 +74,8 @@ onBeforeMount(async () => {
nextTick(() => { nextTick(() => {
dashboardPreview.value.restore() dashboardPreview.value.restore()
}) })
if (attachParam) { if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParam, canvasDataResult, 'outer') dvMainStore.addOuterParamsFilter(attachParams, canvasDataResult, 'outer')
} }
} }
) )

View File

@ -0,0 +1,19 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { useEmbedded } from '@/store/modules/embedded'
const embeddedStore = useEmbedded()
const outerUrl = computed(() => {
return embeddedStore.outerUrl
})
</script>
<template>
<iframe class="de-jump_outer_url" :src="outerUrl" frameborder="0"></iframe>
</template>
<style lang="less" scoped>
.de-jump_outer_url {
width: 100%;
height: 100%;
}
</style>

View File

@ -9,6 +9,7 @@ import { getOuterParamsInfo } from '@/api/visualization/outerParams'
import { ElMessage } from 'element-plus-secondary' import { ElMessage } from 'element-plus-secondary'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { XpackComponent } from '@/components/plugin'
const { wsCache } = useCache() const { wsCache } = useCache()
const interactiveStore = interactiveStoreWithOut() const interactiveStore = interactiveStoreWithOut()
const embeddedStore = useEmbedded() const embeddedStore = useEmbedded()
@ -17,7 +18,7 @@ const viewInfo = ref()
const userViewEnlargeRef = ref() const userViewEnlargeRef = ref()
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { t } = useI18n() const { t } = useI18n()
const openHandler = ref(null)
const state = reactive({ const state = reactive({
canvasDataPreview: null, canvasDataPreview: null,
canvasStylePreview: null, canvasStylePreview: null,
@ -29,13 +30,12 @@ const state = reactive({
// targetSourceId ID // targetSourceId ID
const winMsgHandle = event => { const winMsgHandle = event => {
console.info('PostMessage Params Received')
const msgInfo = event.data const msgInfo = event.data
// targetSourceId // targetSourceId
if (msgInfo && msgInfo.type === 'attachParams' && msgInfo.targetSourceId === state.chartId + '') { if (msgInfo && msgInfo.type === 'attachParams' && msgInfo.targetSourceId === state.chartId + '') {
const attachParam = msgInfo.params const attachParams = msgInfo.params
if (attachParam) { if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParam, state.canvasDataPreview, 'outer') dvMainStore.addOuterParamsFilter(attachParams, state.canvasDataPreview, 'outer')
} }
} }
} }
@ -58,7 +58,7 @@ onBeforeMount(async () => {
window.addEventListener('message', winMsgHandle) window.addEventListener('message', winMsgHandle)
// //
let attachParam let attachParams
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => { await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfo(rsp.data) dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
}) })
@ -67,7 +67,7 @@ onBeforeMount(async () => {
if (embeddedStore.outerParams) { if (embeddedStore.outerParams) {
try { try {
const outerPramsParse = JSON.parse(embeddedStore.outerParams) const outerPramsParse = JSON.parse(embeddedStore.outerParams)
attachParam = outerPramsParse.attachParam attachParams = outerPramsParse.attachParams
dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no') dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -90,8 +90,8 @@ onBeforeMount(async () => {
state.canvasViewInfoPreview = canvasViewInfoPreview state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap state.curPreviewGap = curPreviewGap
if (attachParam) { if (attachParams) {
dvMainStore.addOuterParamsFilter(attachParam, canvasDataResult) dvMainStore.addOuterParamsFilter(attachParams, canvasDataResult)
} }
viewInfo.value = canvasViewInfoPreview[embeddedStore.chartId] viewInfo.value = canvasViewInfoPreview[embeddedStore.chartId]
@ -124,6 +124,32 @@ onBeforeMount(async () => {
const userViewEnlargeOpen = () => { const userViewEnlargeOpen = () => {
userViewEnlargeRef.value.dialogInit(state.canvasStylePreview, viewInfo.value, config.value) userViewEnlargeRef.value.dialogInit(state.canvasStylePreview, viewInfo.value, config.value)
} }
const onPointClick = param => {
try {
console.info('de_inner_params send')
if (window['dataease-embedded-host'] && openHandler?.value) {
const pm = {
methodName: 'embeddedInteractive',
args: {
eventName: 'de_inner_params',
args: param
}
}
openHandler.value.invokeMethod(pm)
} else {
console.info('de_inner_params send to host')
const targetPm = {
type: 'dataease-embedded-interactive',
eventName: 'de_inner_params',
args: param
}
window.parent.postMessage(targetPm, '*')
}
} catch (e) {
console.warn('de_inner_params send error')
}
}
</script> </script>
<template> <template>
@ -136,9 +162,11 @@ const userViewEnlargeOpen = () => {
:dv-info="state.dvInfo" :dv-info="state.dvInfo"
:canvas-view-info="state.canvasViewInfoPreview" :canvas-view-info="state.canvasViewInfoPreview"
@userViewEnlargeOpen="userViewEnlargeOpen" @userViewEnlargeOpen="userViewEnlargeOpen"
@onPointClick="onPointClick"
/> />
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge> <user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>
</div> </div>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -29,20 +29,24 @@ router.beforeEach(async (to, from, next) => {
start() start()
loadStart() loadStart()
checkPlatform() checkPlatform()
let isDesktop = wsCache.get('app.desktop')
if (isDesktop === null) {
await appStore.setAppModel()
isDesktop = appStore.getDesktop
}
if (isMobile()) { if (isMobile()) {
done() done()
loadDone() loadDone()
if (to.name === 'link') { if (to.name === 'link') {
window.location.href = window.origin + '/mobile.html#' + to.path window.location.href = window.origin + '/mobile.html#' + to.path
} else if (!isPlatformClient() && !isLarkPlatform()) { } else if (
wsCache.get('user.token') ||
isDesktop ||
(!isPlatformClient() && !isLarkPlatform())
) {
window.location.href = window.origin + '/mobile.html#/index' window.location.href = window.origin + '/mobile.html#/index'
} }
} }
let isDesktop = wsCache.get('app.desktop')
if (isDesktop === null) {
await appStore.setAppModel()
isDesktop = appStore.getDesktop
}
await appearanceStore.setAppearance() await appearanceStore.setAppearance()
if (wsCache.get('user.token') || isDesktop) { if (wsCache.get('user.token') || isDesktop) {
if (!userStore.getUid) { if (!userStore.getUid) {

View File

@ -33,7 +33,7 @@ export const dvMainStore = defineStore('dataVisualization', {
datasetAreaCollapse: false datasetAreaCollapse: false
}, },
embeddedCallBack: 'no', // 嵌入模式是否允许反馈参数 embeddedCallBack: 'no', // 嵌入模式是否允许反馈参数
editMode: 'edit', // 编辑器模式 edit preview editMode: 'preview', // 编辑器模式 edit preview
mobileInPc: false, mobileInPc: false,
firstLoadMap: [], firstLoadMap: [],
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null }, canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },

View File

@ -1,6 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import { clear } from '@/api/sync/syncTaskLog'
interface AppState { interface AppState {
type: string type: string
token: string token: string
@ -14,6 +13,8 @@ interface AppState {
opt: string opt: string
createType: string createType: string
templateParams: string templateParams: string
jumpInfoParam: string
outerUrl: string
} }
export const userStore = defineStore('embedded', { export const userStore = defineStore('embedded', {
@ -30,13 +31,21 @@ export const userStore = defineStore('embedded', {
resourceId: '', resourceId: '',
opt: '', opt: '',
createType: '', createType: '',
templateParams: '' templateParams: '',
outerUrl: '',
jumpInfoParam: ''
} }
}, },
getters: { getters: {
getType(): string { getType(): string {
return this.type return this.type
}, },
getJumpInfoParam(): string {
return this.jumpInfoParam
},
getOuterUrl(): string {
return this.outerUrl
},
getCreateType(): string { getCreateType(): string {
return this.createType return this.createType
}, },
@ -87,6 +96,12 @@ export const userStore = defineStore('embedded', {
setType(type: string) { setType(type: string) {
this.type = type this.type = type
}, },
setOuterUrl(outerUrl: string) {
this.outerUrl = outerUrl
},
setJumpInfoParam(jumpInfoParam: string) {
this.jumpInfoParam = jumpInfoParam
},
setCreateType(createType: string) { setCreateType(createType: string) {
this.createType = createType this.createType = createType
}, },
@ -137,6 +152,8 @@ export const userStore = defineStore('embedded', {
this.setTemplateParams('') this.setTemplateParams('')
this.setResourceId('') this.setResourceId('')
this.setDvId('') this.setDvId('')
this.setJumpInfoParam('')
this.setOuterUrl('')
} }
} }
}) })

View File

@ -376,7 +376,7 @@ em {
} }
.color-dataV { .color-dataV {
background: rgb(0, 214, 185); background: rgb(0, 214, 185)!important;
} }
.color-dataset { .color-dataset {
@ -392,7 +392,7 @@ em {
} }
strong { strong {
font-synthesis: style weight!important; font-synthesis: style weight !important;
} }
.ed-date-editor .ed-range__icon { .ed-date-editor .ed-range__icon {
@ -400,12 +400,94 @@ strong {
} }
.ed-picker__popper { .ed-picker__popper {
--ed-datepicker-border-color: #DEE0E3 !important; --ed-datepicker-border-color: #dee0e3 !important;
} }
.ed-dialog__headerbtn { .ed-dialog__headerbtn {
top: 21px !important; top: 21px !important;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center justify-content: center;
}
.de-message-export {
min-width: 20px !important;
padding: 16px 20px !important;
display: flex;
align-items: center;
box-shadow: 0px 4px 8px 0px #1f23291a;
& > p {
font-family: AlibabaPuHuiTi;
font-size: 14px;
font-weight: 500;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
color: #1f2329;
display: flex;
align-items: center;
padding-right: 20px;
}
.btn-text {
padding: 2px 4px;
&:hover {
background: var(--primary10, #3370ff1a);
}
}
.ed-message__closeBtn {
margin-left: 28px;
height: 16px;
width: 16px;
position: relative;
margin-right: 0;
top: 0;
right: 0;
transform: translateY(0);
color: #646a73;
}
.ed-message__icon {
height: 16px;
width: 16px;
margin-right: 8px;
}
}
.de-message-loading {
border: 1px solid var(--primary, #3370ff) !important;
background: #f0f4ff !important;
@keyframes circle {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
.ed-message__icon {
color: var(--primary, #3370ff);
animation: circle infinite 0.75s linear;
}
}
.de-message-error {
border: 1px solid var(--deDanger, #f54a45) !important;
background: var(--deWhitemsgDeDanger, #fef1f1) !important;
.ed-message__icon {
color: var(--deDanger, #f54a45);
}
}
.de-message-success {
border: 1px solid var(--deSuccess, #34c724) !important;
background: var(--deWhitemsgDeSuccess, #f0fbef) !important;
.ed-message__icon {
color: var(--deSuccess, #34c724);
}
} }

View File

@ -119,8 +119,8 @@ export function initCanvasDataPrepare(dvId, busiFlag, callBack) {
const canvasStyleResult = JSON.parse(canvasInfo.canvasStyleData) const canvasStyleResult = JSON.parse(canvasInfo.canvasStyleData)
const canvasViewInfoPreview = canvasInfo.canvasViewInfo const canvasViewInfoPreview = canvasInfo.canvasViewInfo
//历史字段适配 //历史字段适配
canvasStyleResult.component.seniorStyleSetting = canvasStyleResult.component['seniorStyleSetting'] =
canvasStyleResult.component.seniorStyleSetting || deepCopy(SENIOR_STYLE_SETTING_LIGHT) canvasStyleResult.component['seniorStyleSetting'] || deepCopy(SENIOR_STYLE_SETTING_LIGHT)
canvasDataResult.forEach(componentItem => { canvasDataResult.forEach(componentItem => {
componentItem['canvasActive'] = false componentItem['canvasActive'] = false
@ -420,6 +420,9 @@ export async function decompressionPre(params, callBack) {
} }
}) })
const sourceCanvasStyle = JSON.parse(deTemplateDataTemp['canvasStyleData']) const sourceCanvasStyle = JSON.parse(deTemplateDataTemp['canvasStyleData'])
//历史字段适配
sourceCanvasStyle.component['seniorStyleSetting'] =
sourceCanvasStyle.component['seniorStyleSetting'] || deepCopy(SENIOR_STYLE_SETTING_LIGHT)
deTemplateData = { deTemplateData = {
canvasStyleData: sourceCanvasStyle, canvasStyleData: sourceCanvasStyle,
componentData: sourceComponentData, componentData: sourceComponentData,
@ -432,19 +435,28 @@ export async function decompressionPre(params, callBack) {
callBack(deTemplateData) callBack(deTemplateData)
} }
export function trackBarStyleCheck(element, trackbarStyle, scale) { export function trackBarStyleCheck(element, trackbarStyle, _scale, trackMenuNumber) {
const { width, height } = element.style const { width, height } = element.style
const widthReal = width const widthReal = width
const heightReal = height const heightReal = height
// 浮窗高度
function calculateTrackHeight(trackMenuNumber) {
if (trackMenuNumber === 2) {
return 75
} else {
const increment = Math.floor(trackMenuNumber - 2) * 35
return 75 + increment
}
}
if (trackbarStyle.left < 0) { if (trackbarStyle.left < 0) {
trackbarStyle.left = 0 trackbarStyle.left = 0
} else if (widthReal - trackbarStyle.left < 60) { } else if (widthReal - trackbarStyle.left < 60) {
trackbarStyle.left = trackbarStyle.left - 60 trackbarStyle.left = trackbarStyle.left - 60
} }
const trackMenuHeight = calculateTrackHeight(trackMenuNumber)
if (trackbarStyle.top < 0) { if (trackbarStyle.top < 0) {
trackbarStyle.top = 0 trackbarStyle.top = 0
} else if (heightReal - trackbarStyle.top < 100) { } else if (trackbarStyle.top + trackMenuHeight + 60 > heightReal) {
trackbarStyle.top = trackbarStyle.top - 100 trackbarStyle.top = trackbarStyle.top - trackMenuHeight
} }
} }

View File

@ -138,7 +138,7 @@ const canvasInit = (isFistLoad = true) => {
} }
// afterInit // afterInit
dvMainStore.setDataPrepareState(true) dvMainStore.setDataPrepareState(true)
if (isMainCanvas(canvasId.value.id) && isFistLoad) { if (isMainCanvas(canvasId.value) && isFistLoad) {
snapshotStore.recordSnapshotCache('renderChart') snapshotStore.recordSnapshotCache('renderChart')
} }
}, 500) }, 500)
@ -289,10 +289,9 @@ defineExpose({
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} ::-webkit-scrollbar {
display: none;
&::-webkit-scrollbar { }
display: none;
} }
.render-active { .render-active {

View File

@ -231,7 +231,12 @@ init()
<div @keydown.stop @keyup.stop style="width: 100%; margin-bottom: 16px"> <div @keydown.stop @keyup.stop style="width: 100%; margin-bottom: 16px">
<!--仪表盘--> <!--仪表盘-->
<el-col v-show="showProperty('gaugeThreshold')"> <el-col v-show="showProperty('gaugeThreshold')">
<el-form ref="thresholdForm" :model="state.thresholdForm" label-position="top"> <el-form
:model="state.thresholdForm"
ref="thresholdForm"
label-position="top"
@submit.prevent
>
<el-form-item <el-form-item
:label="t('chart.threshold_range') + '(%)'" :label="t('chart.threshold_range') + '(%)'"
class="form-item" class="form-item"
@ -252,7 +257,7 @@ init()
<el-tooltip effect="dark" placement="bottom"> <el-tooltip effect="dark" placement="bottom">
<el-icon style="margin-left: 10px"><InfoFilled /></el-icon> <el-icon style="margin-left: 10px"><InfoFilled /></el-icon>
<template #content> <template #content>
阈值设置决定仪表盘区间颜色为空则不开启阈值范围(0-100)逐级递增 条件样式设置决定仪表盘区间颜色为空则不开启阈值范围(0-100)逐级递增
<br /> <br />
例如输入 30,70表示分为3段分别为[0,30],(30,70],(70,100] 例如输入 30,70表示分为3段分别为[0,30],(30,70],(70,100]
</template> </template>
@ -261,7 +266,12 @@ init()
</el-form> </el-form>
</el-col> </el-col>
<el-col v-show="showProperty('liquidThreshold')"> <el-col v-show="showProperty('liquidThreshold')">
<el-form ref="thresholdForm" :model="state.thresholdForm" label-position="top"> <el-form
:model="state.thresholdForm"
ref="thresholdForm"
label-position="top"
@submit.prevent
>
<el-form-item <el-form-item
:label="t('chart.threshold_range') + '(%)'" :label="t('chart.threshold_range') + '(%)'"
class="form-item" class="form-item"
@ -282,7 +292,7 @@ init()
<el-tooltip effect="dark" placement="bottom"> <el-tooltip effect="dark" placement="bottom">
<el-icon style="margin-left: 10px"><InfoFilled /></el-icon> <el-icon style="margin-left: 10px"><InfoFilled /></el-icon>
<template #content> <template #content>
阈值设置决定水波图颜色为空则不开启阈值范围(0-100)逐级递增 条件样式设置决定水波图颜色为空则不开启阈值范围(0-100)逐级递增
<br /> <br />
例如输入 30,70表示分为3段分别为[0,30],(30,70],(70,100] 例如输入 30,70表示分为3段分别为[0,30],(30,70],(70,100]
</template> </template>
@ -360,7 +370,7 @@ init()
<el-col v-if="props.chart.type && props.chart.type === 'indicator'"> <el-col v-if="props.chart.type && props.chart.type === 'indicator'">
<el-col> <el-col>
<div class="inner-container"> <div class="inner-container">
<span class="label" :class="'label-' + props.themes">阈值设置</span> <span class="label" :class="'label-' + props.themes">条件样式设置</span>
<span class="right-btns"> <span class="right-btns">
<span <span
class="set-text-info" class="set-text-info"
@ -458,7 +468,7 @@ init()
<el-col v-show="showProperty('tableThreshold')"> <el-col v-show="showProperty('tableThreshold')">
<el-col> <el-col>
<div class="inner-container"> <div class="inner-container">
<span class="label" :class="'label-' + props.themes">阈值设置</span> <span class="label" :class="'label-' + props.themes">条件样式设置</span>
<span class="right-btns"> <span class="right-btns">
<span <span
class="set-text-info" class="set-text-info"

View File

@ -436,7 +436,7 @@ init()
<template #icon> <template #icon>
<Icon name="icon_add_outlined" /> <Icon name="icon_add_outlined" />
</template> </template>
{{ t('chart.add_condition') }} {{ t('chart.add_style') }}
</el-button> </el-button>
</div> </div>
</div> </div>
@ -451,7 +451,7 @@ init()
<template #icon> <template #icon>
<Icon name="icon_add_outlined" /> <Icon name="icon_add_outlined" />
</template> </template>
{{ t('chart.add_threshold') }} {{ t('chart.add_condition') }}
</el-button> </el-button>
</el-col> </el-col>
</template> </template>

View File

@ -354,13 +354,14 @@ watch(
/> />
</collapse-switch-item> </collapse-switch-item>
<collapse-switch-item <collapse-switch-item
:themes="themes"
v-if="showProperties('tooltip-selector')" v-if="showProperties('tooltip-selector')"
v-model="chart.customAttr.tooltip.show" v-model="chart.customAttr.tooltip.show"
:themes="themes"
:change-model="chart.customAttr.tooltip" :change-model="chart.customAttr.tooltip"
@modelChange="val => onTooltipChange({ data: val }, 'show')"
name="tooltip"
:title="$t('chart.tooltip')" :title="$t('chart.tooltip')"
:show-switch="propertyInnerAll['tooltip-selector'].includes('show')"
name="tooltip"
@modelChange="val => onTooltipChange({ data: val }, 'show')"
> >
<tooltip-selector <tooltip-selector
class="attr-selector" class="attr-selector"
@ -371,11 +372,15 @@ watch(
@onExtTooltipChange="onExtTooltipChange" @onExtTooltipChange="onExtTooltipChange"
/> />
</collapse-switch-item> </collapse-switch-item>
<el-collapse-item <collapse-switch-item
:effect="themes"
name="tableHeader"
:title="t('chart.table_header')"
v-if="showProperties('table-header-selector')" v-if="showProperties('table-header-selector')"
v-model="chart.customAttr.tableHeader.showTableHeader"
:change-model="chart.customAttr.tableHeader"
:effect="themes"
:title="t('chart.table_header')"
:show-switch="propertyInnerAll['table-header-selector'].includes('showTableHeader')"
name="tableHeader"
@modelChange="val => onTableHeaderChange(val, 'showTableHeader')"
> >
<table-header-selector <table-header-selector
:property-inner="propertyInnerAll['table-header-selector']" :property-inner="propertyInnerAll['table-header-selector']"
@ -383,7 +388,7 @@ watch(
:chart="chart" :chart="chart"
@onTableHeaderChange="onTableHeaderChange" @onTableHeaderChange="onTableHeaderChange"
/> />
</el-collapse-item> </collapse-switch-item>
<el-collapse-item <el-collapse-item
:effect="themes" :effect="themes"
name="tableCell" name="tableCell"
@ -434,7 +439,7 @@ watch(
:change-model="chart.customStyle.xAxis" :change-model="chart.customStyle.xAxis"
@modelChange="val => onChangeXAxisForm(val, 'show')" @modelChange="val => onChangeXAxisForm(val, 'show')"
name="xAxis" name="xAxis"
:title="chart.type === 'bidirectional-bar' ? $t('chart.yAxis') : t('chart.xAxis')" :title="selectorSpec['x-axis-selector']?.title"
> >
<x-axis-selector <x-axis-selector
class="attr-selector" class="attr-selector"
@ -469,7 +474,7 @@ watch(
:change-model="chart.customStyle.yAxis" :change-model="chart.customStyle.yAxis"
@modelChange="val => onChangeYAxisForm(val, 'show')" @modelChange="val => onChangeYAxisForm(val, 'show')"
name="yAxis" name="yAxis"
:title="chart.type === 'bidirectional-bar' ? $t('chart.xAxis') : $t('chart.yAxis')" :title="selectorSpec['dual-y-axis-selector']?.title"
> >
<dual-y-axis-selector <dual-y-axis-selector
class="attr-selector" class="attr-selector"

View File

@ -834,6 +834,34 @@ onMounted(() => {
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item
v-if="showProperty('gaugeAxisLine')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
v-model="state.basicStyleForm.gaugeAxisLine"
:effect="themes"
size="small"
@change="changeBasicStyle('gaugeAxisLine')"
>
{{ t('chart.gauge_axis_label') }}</el-checkbox
>
</el-form-item>
<el-form-item
v-if="showProperty('gaugePercentLabel') && state.basicStyleForm.gaugeAxisLine"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
v-model="state.basicStyleForm.gaugePercentLabel"
:effect="themes"
size="small"
@change="changeBasicStyle('gaugePercentLabel')"
>
{{ t('chart.gauge_percentage_tick') }}</el-checkbox
>
</el-form-item>
<!--gauge end--> <!--gauge end-->
<!--bar start--> <!--bar start-->
<el-form-item <el-form-item

View File

@ -7,12 +7,11 @@ import cloneDeep from 'lodash-es/cloneDeep'
import defaultsDeep from 'lodash-es/defaultsDeep' import defaultsDeep from 'lodash-es/defaultsDeep'
import { formatterType, unitType } from '../../../js/formatter' import { formatterType, unitType } from '../../../js/formatter'
import { fieldType } from '@/utils/attr' import { fieldType } from '@/utils/attr'
import { partition, uniqWith, isEqual } from 'lodash-es' import { partition } from 'lodash-es'
import chartViewManager from '../../../js/panel' import chartViewManager from '../../../js/panel'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import { deepCopy } from '@/utils/utils'
const { t } = useI18n() const { t } = useI18n()
@ -41,55 +40,54 @@ const quotaData = ref<Axis[]>(inject('quotaData'))
const showSeriesTooltipFormatter = computed(() => { const showSeriesTooltipFormatter = computed(() => {
return showProperty('seriesTooltipFormatter') && !batchOptStatus.value && props.chart.id return showProperty('seriesTooltipFormatter') && !batchOptStatus.value && props.chart.id
}) })
// //
const initSeriesTooltip = () => { const changeChartType = () => {
if (!showSeriesTooltipFormatter.value) { if (!showSeriesTooltipFormatter.value) {
return return
} }
curSeriesFormatter.value = {}
const formatter = state.tooltipForm.seriesTooltipFormatter const formatter = state.tooltipForm.seriesTooltipFormatter
const seriesAxisMap = formatter.reduce((pre, next) => {
next.seriesId = next.seriesId ?? next.id
pre[next.seriesId] = next
return pre
}, {})
//
if (!quotaAxis.value?.length) {
if (!formatter.length) {
quotaData.value?.forEach(i => formatter.push({ ...i, seriesId: i.id, show: false }))
}
curSeriesFormatter.value = {}
return
}
formatter.splice(0, formatter.length) formatter.splice(0, formatter.length)
const axisIds = quotaAxis.value?.map(i => i.id) const axisIds = []
const allQuotaAxis = quotaAxis.value?.concat( quotaAxis.value.forEach(axis => {
quotaData.value?.filter(ele => !axisIds.includes(ele.id)) formatter.push({
) ...axis,
const axisMap = allQuotaAxis.reduce((pre, next, index) => { show: true
let tmp = { })
...next, axisIds.push(axis.id)
seriesId: next.seriesId ?? next.id, })
show: index <= quotaAxis.value.length - 1, quotaData.value.forEach(quotaAxis => {
summary: COUNT_DE_TYPE.includes(next.deType) ? 'count' : 'sum' if (!axisIds.includes(quotaAxis.id)) {
} as SeriesFormatter formatter.push({
if (seriesAxisMap[tmp.seriesId]) { ...quotaAxis,
tmp = { seriesId: quotaAxis.id,
...tmp, show: false
formatterCfg: seriesAxisMap[tmp.seriesId].formatterCfg, })
show: seriesAxisMap[tmp.seriesId].show, }
summary: seriesAxisMap[tmp.seriesId].summary, })
chartShowName: seriesAxisMap[tmp.seriesId].chartShowName emit('onTooltipChange', { data: state.tooltipForm, render: false }, 'seriesTooltipFormatter')
} emit('onExtTooltipChange', extTooltip.value)
}
//
const changeDataset = () => {
curSeriesFormatter.value = {}
const formatter = state.tooltipForm.seriesTooltipFormatter
const quotaIds = quotaData.value.map(i => i.id)
for (let i = formatter.length - 1; i >= 0; i--) {
if (!quotaIds.includes(formatter[i].id)) {
formatter.splice(i, 1)
} }
formatter.push(tmp)
pre[tmp.seriesId] = tmp
return pre
}, {})
if (!curSeriesFormatter.value || !axisMap[curSeriesFormatter.value.seriesId]) {
curSeriesFormatter.value = axisMap[formatter[0].seriesId]
return
} }
curSeriesFormatter.value = axisMap[curSeriesFormatter.value.seriesId] const formatterIds = formatter.map(i => i.id)
quotaData.value.forEach(axis => {
if (!formatterIds.includes(axis.id)) {
formatter.push({
...axis,
seriesId: axis.id,
show: false
})
}
})
} }
const AXIS_PROP: AxisType[] = ['yAxis', 'yAxisExt', 'extBubble'] const AXIS_PROP: AxisType[] = ['yAxis', 'yAxisExt', 'extBubble']
const quotaAxis = computed(() => { const quotaAxis = computed(() => {
@ -173,16 +171,6 @@ watch(
}, },
{ deep: false } { deep: false }
) )
watch(
[quotaData, () => props.chart.type],
newVal => {
if (!newVal?.[0]?.length) {
return
}
initSeriesTooltip()
},
{ deep: false }
)
const state = reactive({ const state = reactive({
tooltipForm: { tooltipForm: {
@ -373,6 +361,8 @@ onMounted(() => {
useEmitt({ name: 'addAxis', callback: updateSeriesTooltipFormatter }) useEmitt({ name: 'addAxis', callback: updateSeriesTooltipFormatter })
useEmitt({ name: 'removeAxis', callback: updateSeriesTooltipFormatter }) useEmitt({ name: 'removeAxis', callback: updateSeriesTooltipFormatter })
useEmitt({ name: 'updateAxis', callback: updateSeriesTooltipFormatter }) useEmitt({ name: 'updateAxis', callback: updateSeriesTooltipFormatter })
useEmitt({ name: 'chart-type-change', callback: changeChartType })
useEmitt({ name: 'dataset-change', callback: changeDataset })
}) })
</script> </script>

View File

@ -255,7 +255,11 @@ onMounted(() => {
</el-form-item> </el-form-item>
</template> </template>
</template> </template>
<el-divider class="m-divider" :class="'m-divider--' + themes" /> <el-divider
v-if="showProperty('splitLine') || showProperty('axisLine')"
class="m-divider"
:class="'m-divider--' + themes"
/>
<el-form-item class="form-item" :class="'form-item-' + themes" v-if="showProperty('axisLine')"> <el-form-item class="form-item" :class="'form-item-' + themes" v-if="showProperty('axisLine')">
<el-checkbox <el-checkbox
size="small" size="small"
@ -325,7 +329,11 @@ onMounted(() => {
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
<el-divider class="m-divider" :class="'m-divider--' + themes" /> <el-divider
v-if="showProperty('axisLabel')"
class="m-divider"
:class="'m-divider--' + themes"
/>
<el-form-item <el-form-item
class="form-item form-item-checkbox" class="form-item form-item-checkbox"
:class="{ :class="{

View File

@ -234,20 +234,6 @@ onMounted(() => {
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </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> </el-form>
</template> </template>

View File

@ -66,7 +66,12 @@ onMounted(() => {
</script> </script>
<template> <template>
<el-form ref="tableHeaderForm" :model="state.tableHeaderForm" label-position="top"> <el-form
:model="state.tableHeaderForm"
:disabled="!state.tableHeaderForm.showTableHeader"
ref="tableHeaderForm"
label-position="top"
>
<el-form-item <el-form-item
:label="t('chart.backgroundColor')" :label="t('chart.backgroundColor')"
class="form-item" class="form-item"
@ -245,34 +250,6 @@ onMounted(() => {
{{ t('chart.table_header_sort') }} {{ t('chart.table_header_sort') }}
</el-checkbox> </el-checkbox>
</el-form-item> </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> </el-form>
</template> </template>

View File

@ -184,7 +184,7 @@ const state = reactive({
}) })
const filedList = computed(() => { const filedList = computed(() => {
return [...state.dimension, ...state.quota].filter(ele => ele.id !== 'count') return [...state.dimension, ...state.quota].filter(ele => ele.id !== 'count' && !!ele.summary)
}) })
provide('filedList', () => filedList.value) provide('filedList', () => filedList.value)
@ -211,6 +211,7 @@ const getFields = (id, chartId) => {
state.quota = (res.quotaList as unknown as Field[]) || [] state.quota = (res.quotaList as unknown as Field[]) || []
state.dimensionData = JSON.parse(JSON.stringify(state.dimension)) state.dimensionData = JSON.parse(JSON.stringify(state.dimension))
state.quotaData = JSON.parse(JSON.stringify(state.quota)) state.quotaData = JSON.parse(JSON.stringify(state.quota))
emitter.emit('dataset-change')
}) })
.catch(() => { .catch(() => {
state.dimension = [] state.dimension = []
@ -739,6 +740,7 @@ const onAreaChange = val => {
const onTypeChange = (render, type) => { const onTypeChange = (render, type) => {
view.value.render = render view.value.render = render
view.value.type = type view.value.type = type
emitter.emit('chart-type-change')
// //
const chartViewInstance = chartViewManager.getChartView(view.value.render, view.value.type) const chartViewInstance = chartViewManager.getChartView(view.value.render, view.value.type)
if (chartViewInstance) { if (chartViewInstance) {
@ -3289,7 +3291,7 @@ span {
} }
:deep(.ed-tabs__content) { :deep(.ed-tabs__content) {
height: calc(100% - 33px); height: calc(100% - 35px);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }

View File

@ -349,7 +349,8 @@ export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = {
tableTitleHeight: 36, tableTitleHeight: 36,
tableHeaderSort: false, tableHeaderSort: false,
showColTooltip: false, showColTooltip: false,
showRowTooltip: false showRowTooltip: false,
showTableHeader: true
} }
export const DEFAULT_TABLE_CELL: ChartTableCellAttr = { export const DEFAULT_TABLE_CELL: ChartTableCellAttr = {
tableFontColor: '#000000', tableFontColor: '#000000',
@ -1445,7 +1446,9 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
tableLayoutMode: 'grid', tableLayoutMode: 'grid',
calcTopN: false, calcTopN: false,
topN: 5, topN: 5,
topNLabel: '其他' topNLabel: '其他',
gaugeAxisLine: true,
gaugePercentLabel: true
} }
export const BASE_VIEW_CONFIG = { export const BASE_VIEW_CONFIG = {

View File

@ -26,7 +26,7 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
propertyInner = { propertyInner = {
...BAR_EDITOR_PROPERTY_INNER, ...BAR_EDITOR_PROPERTY_INNER,
'label-selector': ['vPosition', 'seriesLabelFormatter'], 'label-selector': ['vPosition', 'seriesLabelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'y-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'], 'axisLabelFormatter'] 'y-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'], 'axisLabelFormatter']
} }
protected baseOptions: ColumnOptions = { protected baseOptions: ColumnOptions = {
@ -236,7 +236,7 @@ export class StackBar extends Bar {
propertyInner = { propertyInner = {
...this['propertyInner'], ...this['propertyInner'],
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition'], 'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter'] 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
} }
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const baseOptions = super.configLabel(chart, options) const baseOptions = super.configLabel(chart, options)
@ -372,7 +372,7 @@ export class PercentageStackBar extends GroupStackBar {
propertyInner = { propertyInner = {
...this['propertyInner'], ...this['propertyInner'],
'label-selector': ['color', 'fontSize', 'vPosition', 'reserveDecimalCount'], 'label-selector': ['color', 'fontSize', 'vPosition', 'reserveDecimalCount'],
'tooltip-selector': ['color', 'fontSize'] 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show']
} }
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
const baseOptions = super.configLabel(chart, options) const baseOptions = super.configLabel(chart, options)

View File

@ -16,6 +16,7 @@ import {
import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util' import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { valueFormatter } from '@/views/chart/components/js/formatter' import { valueFormatter } from '@/views/chart/components/js/formatter'
import { Options } from '@antv/g2plot/esm'
const { t } = useI18n() const { t } = useI18n()
/** /**
* 对称柱状图 * 对称柱状图
@ -87,7 +88,17 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'],
'function-cfg': ['emptyDataStrategy'], 'function-cfg': ['emptyDataStrategy'],
'label-selector': ['hPosition', 'seriesLabelFormatter'], 'label-selector': ['hPosition', 'seriesLabelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'] 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show']
}
selectorSpec: EditorSelectorSpec = {
...this['selectorSpec'],
'dual-y-axis-selector': {
title: `${t('chart.xAxis')}`
},
'x-axis-selector': {
title: `${t('chart.yAxis')}`
}
} }
drawChart(drawOptions: G2PlotDrawOptions<G2BidirectionalBar>): G2BidirectionalBar { drawChart(drawOptions: G2PlotDrawOptions<G2BidirectionalBar>): G2BidirectionalBar {
@ -118,11 +129,27 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
}, },
interactions: [{ type: 'active-region' }], interactions: [{ type: 'active-region' }],
yField: ['value', 'valueExt'], yField: ['value', 'valueExt'],
appendPadding: getPadding(chart) appendPadding: getPadding(chart),
meta: {
field: {
type: 'cat'
}
}
}
const customOptions = this.setupOptions(chart, initOptions)
const options = {
...customOptions
}
const xAxis = chart.xAxis
if (xAxis?.length === 1 && xAxis[0].deType === 1) {
const values = data2.map(item => item.field)
options.meta = {
field: {
type: 'cat',
values: values.reverse()
}
}
} }
const options = this.setupOptions(chart, initOptions)
// 开始渲染 // 开始渲染
const newChart = new G2BidirectionalBar(container, options) const newChart = new G2BidirectionalBar(container, options)
@ -418,6 +445,45 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
return { ...options, label } return { ...options, label }
} }
protected configEmptyDataStrategy(
chart: Chart,
options: BidirectionalBarOptions
): BidirectionalBarOptions {
const { data } = options as unknown as Options
if (!data?.length) {
return options
}
const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy
if (strategy === 'ignoreData') {
const emptyFields = data
.filter(obj => obj['value'] === null || obj['valueExt'] === null)
.map(obj => obj['field'])
return {
...options,
data: data.filter(obj => {
if (emptyFields.includes(obj['field'])) {
return false
}
return true
})
}
}
const updateValues = (strategy: 'breakLine' | 'setZero', data: any[]) => {
data.forEach(obj => {
if (obj['value'] === null) {
obj['value'] = strategy === 'breakLine' ? null : 0
}
if (obj['valueExt'] === null) {
obj['valueExt'] = strategy === 'breakLine' ? null : 0
}
})
}
if (strategy === 'breakLine' || strategy === 'setZero') {
updateValues(strategy, data)
}
return options
}
protected setupOptions(chart: Chart, options: BidirectionalBarOptions) { protected setupOptions(chart: Chart, options: BidirectionalBarOptions) {
return flow( return flow(
this.configTheme, this.configTheme,

View File

@ -30,7 +30,7 @@ export const BAR_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'gradient'], 'basic-style-selector': ['colors', 'alpha', 'gradient'],
'label-selector': ['fontSize', 'color', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show'],
'x-axis-selector': [ 'x-axis-selector': [
'name', 'name',
'color', 'color',

View File

@ -34,7 +34,7 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
propertyInner = { propertyInner = {
...BAR_EDITOR_PROPERTY_INNER, ...BAR_EDITOR_PROPERTY_INNER,
'label-selector': ['hPosition', 'seriesLabelFormatter'], 'label-selector': ['hPosition', 'seriesLabelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'axisLabelFormatter'] 'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'axisLabelFormatter']
} }
axis: AxisType[] = [...BAR_AXIS_TYPE] axis: AxisType[] = [...BAR_AXIS_TYPE]
@ -267,7 +267,7 @@ export class HorizontalStackBar extends HorizontalBar {
propertyInner = { propertyInner = {
...this['propertyInner'], ...this['propertyInner'],
'label-selector': ['color', 'fontSize', 'hPosition', 'labelFormatter'], 'label-selector': ['color', 'fontSize', 'hPosition', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter'] 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
} }
protected configLabel(chart: Chart, options: BarOptions): BarOptions { protected configLabel(chart: Chart, options: BarOptions): BarOptions {
const baseOptions = super.configLabel(chart, options) const baseOptions = super.configLabel(chart, options)
@ -328,7 +328,7 @@ export class HorizontalPercentageStackBar extends HorizontalStackBar {
propertyInner = { propertyInner = {
...this['propertyInner'], ...this['propertyInner'],
'label-selector': ['color', 'fontSize', 'hPosition', 'reserveDecimalCount'], 'label-selector': ['color', 'fontSize', 'hPosition', 'reserveDecimalCount'],
'tooltip-selector': ['color', 'fontSize'] 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show']
} }
protected configLabel(chart: Chart, options: BarOptions): BarOptions { protected configLabel(chart: Chart, options: BarOptions): BarOptions {
const baseOptions = super.configLabel(chart, options) const baseOptions = super.configLabel(chart, options)

View File

@ -49,7 +49,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'gradient'], 'basic-style-selector': ['colors', 'alpha', 'gradient'],
'label-selector': ['hPosition', 'color', 'fontSize'], 'label-selector': ['hPosition', 'color', 'fontSize'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'],
'y-axis-selector': ['name', 'color', 'fontSize', 'axisForm', 'axisLabel', 'position'], 'y-axis-selector': ['name', 'color', 'fontSize', 'axisForm', 'axisLabel', 'position'],
'function-cfg': ['emptyDataStrategy'] 'function-cfg': ['emptyDataStrategy']
} }
@ -62,7 +62,8 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
isGroup: false, isGroup: false,
isPercent: true, isPercent: true,
isStack: true, isStack: true,
xAxis: false xAxis: false,
appendPadding: [0, 0, 10, 0]
} }
drawChart(drawOptions: G2PlotDrawOptions<G2Progress>): G2Progress { drawChart(drawOptions: G2PlotDrawOptions<G2Progress>): G2Progress {
@ -98,13 +99,15 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
const data1 = defaultTo(sourceData[0]?.data, []) const data1 = defaultTo(sourceData[0]?.data, [])
const data2 = defaultTo(sourceData[1]?.data, []) const data2 = defaultTo(sourceData[1]?.data, [])
const currentData = data2.map(item => { const currentData = data2.map(item => {
const progress = getCompletionRate(data1.find(i => i.field === item.field)?.value, item.value)
return { return {
...item, ...item,
type: 'current', type: 'current',
title: item.field, title: item.field,
id: item.quotaList[0].id, id: item.quotaList[0].id,
originalValue: item.value, originalValue: item.value,
progress: getCompletionRate(data1.find(i => i.field === item.field)?.value, item.value) originalProgress: progress,
progress: progress >= 100 ? 100 : progress
} }
}) })
const targetData = data1.map(item => { const targetData = data1.map(item => {
@ -223,7 +226,7 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
if (item.type === 'target') { if (item.type === 'target') {
return '' return ''
} }
return (item.progress * 100).toFixed(2) + '%' return item.originalProgress.toFixed(2) + '%'
} }
} }
if (label.position === 'top') { if (label.position === 'top') {

View File

@ -39,7 +39,14 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
propertyInner = { propertyInner = {
...BAR_EDITOR_PROPERTY_INNER, ...BAR_EDITOR_PROPERTY_INNER,
'label-selector': ['hPosition', 'color', 'fontSize', 'labelFormatter', 'showGap'], 'label-selector': ['hPosition', 'color', 'fontSize', 'labelFormatter', 'showGap'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'showGap'], 'tooltip-selector': [
'fontSize',
'color',
'backgroundColor',
'tooltipFormatter',
'showGap',
'show'
],
'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'axisLabelFormatter'] 'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'axisLabelFormatter']
} }
axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt'] axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt']
@ -47,7 +54,7 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
data: [], data: [],
xField: 'values', xField: 'values',
yField: 'field', yField: 'field',
colorFiled: 'category', colorField: 'category',
isGroup: true, isGroup: true,
interactions: [ interactions: [
{ {

View File

@ -23,7 +23,7 @@ export class Waterfall extends G2PlotChartView<WaterfallOptions, G2Waterfall> {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'gradient'], 'basic-style-selector': ['colors', 'alpha', 'gradient'],
'label-selector': ['fontSize', 'color', 'vPosition', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'vPosition', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'title-selector': [ 'title-selector': [
'title', 'title',
'fontSize', 'fontSize',
@ -187,7 +187,7 @@ export class Waterfall extends G2PlotChartView<WaterfallOptions, G2Waterfall> {
let tmpValue = totalMap[id] let tmpValue = totalMap[id]
let color = 'grey' let color = 'grey'
if (id === yAxis[0].id) { if (id === yAxis[0].id) {
tmpValue = parseFloat(head.value as unknown as string) tmpValue = head.data.value
color = head.color color = head.color
} }
const value = valueFormatter(tmpValue, formatter.formatterCfg) const value = valueFormatter(tmpValue, formatter.formatterCfg)

View File

@ -261,7 +261,7 @@ export class StackArea extends Area {
propertyInner = { propertyInner = {
...this['propertyInner'], ...this['propertyInner'],
'label-selector': ['fontSize', 'color', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter'] 'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show']
} }
protected configLabel(chart: Chart, options: AreaOptions): AreaOptions { protected configLabel(chart: Chart, options: AreaOptions): AreaOptions {
const customAttr = parseJson(chart.customAttr) const customAttr = parseJson(chart.customAttr)

View File

@ -15,7 +15,7 @@ export const LINE_EDITOR_PROPERTY: EditorProperty[] = [
export const LINE_EDITOR_PROPERTY_INNER: EditorPropertyInner = { export const LINE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'label-selector': ['fontSize', 'color'], 'label-selector': ['fontSize', 'color'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'],
'basic-style-selector': [ 'basic-style-selector': [
'colors', 'colors',
'alpha', 'alpha',

View File

@ -95,7 +95,8 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
// 禁用线上地图数据 // 禁用线上地图数据
customFetchGeoData: () => null customFetchGeoData: () => null
} }
options = this.setupOptions(chart, options, drawOption, geoJson) const context = { drawOption, geoJson }
options = this.setupOptions(chart, options, context)
const view = new Choropleth(container, options) const view = new Choropleth(container, options)
const dotLayer = this.getDotLayer(chart, geoJson, drawOption) const dotLayer = this.getDotLayer(chart, geoJson, drawOption)
this.configZoomButton(chart, view) this.configZoomButton(chart, view)
@ -170,10 +171,10 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
private configBasicStyle( private configBasicStyle(
chart: Chart, chart: Chart,
options: ChoroplethOptions, options: ChoroplethOptions,
extra: any[] context: Record<string, any>
): ChoroplethOptions { ): ChoroplethOptions {
const { areaId }: L7PlotDrawOptions<any> = extra[0] const { areaId }: L7PlotDrawOptions<any> = context.drawOption
const geoJson: FeatureCollection = extra[1] const geoJson: FeatureCollection = context.geoJson
const { basicStyle, label } = parseJson(chart.customAttr) const { basicStyle, label } = parseJson(chart.customAttr)
const senior = parseJson(chart.senior) const senior = parseJson(chart.senior)
const curAreaNameMapping = senior.areaMapping?.[areaId] const curAreaNameMapping = senior.areaMapping?.[areaId]
@ -208,7 +209,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
protected setupOptions( protected setupOptions(
chart: Chart, chart: Chart,
options: ChoroplethOptions, options: ChoroplethOptions,
...extra: any[] context: Record<string, any>
): ChoroplethOptions { ): ChoroplethOptions {
return flow( return flow(
this.configEmptyDataStrategy, this.configEmptyDataStrategy,
@ -216,6 +217,6 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configStyle, this.configStyle,
this.configTooltip, this.configTooltip,
this.configBasicStyle this.configBasicStyle
)(chart, options, extra) )(chart, options, context)
} }
} }

View File

@ -34,7 +34,7 @@ export const MAP_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'showDimension', 'showDimension',
'showQuota' 'showQuota'
], ],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'tooltipFormatter'], 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'tooltipFormatter', 'show'],
'function-cfg': ['emptyDataStrategy'], 'function-cfg': ['emptyDataStrategy'],
'map-mapping': [''] 'map-mapping': ['']
} }

View File

@ -96,7 +96,8 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
// 禁用线上地图数据 // 禁用线上地图数据
customFetchGeoData: () => null customFetchGeoData: () => null
} }
options = this.setupOptions(chart, options, drawOption, geoJson) const context = { drawOption, geoJson }
options = this.setupOptions(chart, options, context)
const view = new Choropleth(container, options) const view = new Choropleth(container, options)
this.configZoomButton(chart, view) this.configZoomButton(chart, view)
view.once('loaded', () => { view.once('loaded', () => {
@ -120,10 +121,10 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
private configBasicStyle( private configBasicStyle(
chart: Chart, chart: Chart,
options: ChoroplethOptions, options: ChoroplethOptions,
extra: any[] context: Record<string, any>
): ChoroplethOptions { ): ChoroplethOptions {
const { areaId }: L7PlotDrawOptions<any> = extra[0] const { areaId }: L7PlotDrawOptions<any> = context.drawOption
const geoJson: FeatureCollection = extra[1] const geoJson: FeatureCollection = context.geoJson
const { basicStyle, label } = parseJson(chart.customAttr) const { basicStyle, label } = parseJson(chart.customAttr)
const senior = parseJson(chart.senior) const senior = parseJson(chart.senior)
const curAreaNameMapping = senior.areaMapping?.[areaId] const curAreaNameMapping = senior.areaMapping?.[areaId]
@ -176,7 +177,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
protected setupOptions( protected setupOptions(
chart: Chart, chart: Chart,
options: ChoroplethOptions, options: ChoroplethOptions,
...extra: any[] context: Record<string, any>
): ChoroplethOptions { ): ChoroplethOptions {
return flow( return flow(
this.configEmptyDataStrategy, this.configEmptyDataStrategy,
@ -185,6 +186,6 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configTooltip, this.configTooltip,
this.configBasicStyle, this.configBasicStyle,
this.configLegend this.configLegend
)(chart, options, extra) )(chart, options, context)
} }
} }

View File

@ -15,7 +15,7 @@ export const CHART_MIX_EDITOR_PROPERTY: EditorProperty[] = [
export const CHART_MIX_EDITOR_PROPERTY_INNER: EditorPropertyInner = { export const CHART_MIX_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'label-selector': ['fontSize', 'color'], 'label-selector': ['fontSize', 'color'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'],
'basic-style-selector': [ 'basic-style-selector': [
'colors', 'colors',
'alpha', 'alpha',

View File

@ -24,7 +24,7 @@ export class Funnel extends G2PlotChartView<FunnelOptions, G2Funnel> {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha'], 'basic-style-selector': ['colors', 'alpha'],
'label-selector': ['fontSize', 'color', 'hPosition', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'hPosition', 'labelFormatter'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'title-selector': [ 'title-selector': [
'show', 'show',
'title', 'title',

View File

@ -13,6 +13,7 @@ import {
import { valueFormatter } from '@/views/chart/components/js/formatter' import { valueFormatter } from '@/views/chart/components/js/formatter'
import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv' import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { merge } from 'lodash-es'
const { t } = useI18n() const { t } = useI18n()
@ -28,7 +29,7 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
] ]
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'gaugeStyle', 'gradient'], 'basic-style-selector': ['colors', 'alpha', 'gradient', 'gaugeAxisLine', 'gaugePercentLabel'],
'label-selector': ['fontSize', 'color', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'labelFormatter'],
'title-selector': [ 'title-selector': [
'title', 'title',
@ -77,10 +78,6 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
label: { label: {
style: { style: {
fontSize: getScaleValue(12, scale) // 刻度值字体大小 fontSize: getScaleValue(12, scale) // 刻度值字体大小
},
formatter: function (v) {
const r = parseFloat(v)
return v === '0' || !r ? v : r * 100 + '%'
} }
}, },
tickLine: { tickLine: {
@ -98,11 +95,15 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
} }
} }
} }
const options = this.setupOptions(chart, initOptions, scale) const options = this.setupOptions(chart, initOptions, { scale })
return new G2Gauge(container, options) return new G2Gauge(container, options)
} }
protected configMisc(chart: Chart, options: GaugeOptions): GaugeOptions { protected configMisc(
chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
const customAttr = parseJson(chart.customAttr) const customAttr = parseJson(chart.customAttr)
const data = chart.data.series[0].data[0] const data = chart.data.series[0].data[0]
let min, max, startAngle, endAngle let min, max, startAngle, endAngle
@ -123,6 +124,8 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
} }
startAngle = (misc.gaugeStartAngle * Math.PI) / 180 startAngle = (misc.gaugeStartAngle * Math.PI) / 180
endAngle = (misc.gaugeEndAngle * Math.PI) / 180 endAngle = (misc.gaugeEndAngle * Math.PI) / 180
context.min = min
context.max = max
} }
const percent = (parseFloat(data) - parseFloat(min)) / (parseFloat(max) - parseFloat(min)) const percent = (parseFloat(data) - parseFloat(min)) / (parseFloat(max) - parseFloat(min))
const tmp = { const tmp = {
@ -133,8 +136,12 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return { ...options, ...tmp } return { ...options, ...tmp }
} }
private configRange(chart: Chart, options: GaugeOptions, extra: any[]): GaugeOptions { private configRange(
const [scale] = extra chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
const { scale } = context
const range = [0] const range = [0]
let index = 0 let index = 0
let flag = false let flag = false
@ -216,36 +223,55 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return { ...options, ...rangOptions } return { ...options, ...rangOptions }
} }
protected configLabel(chart: Chart, options: GaugeOptions): GaugeOptions { protected configLabel(
chart: Chart,
options: GaugeOptions,
context?: Record<string, any>
): GaugeOptions {
const customAttr = parseJson(chart.customAttr) const customAttr = parseJson(chart.customAttr)
const data = chart.data.series[0].data[0] const data = chart.data.series[0].data[0]
let labelContent let labelContent: GaugeOptions['statistic']['content'] = false
if (customAttr.label) { const label = customAttr.label
const label = customAttr.label const labelFormatter = label.labelFormatter ?? DEFAULT_LABEL.labelFormatter
const labelFormatter = label.labelFormatter ?? DEFAULT_LABEL.labelFormatter if (label.show) {
if (label.show) { labelContent = {
labelContent = { style: {
style: () => ({ fontSize: `${label.fontSize}`,
fontSize: label.fontSize, color: label.color
color: label.color },
}), formatter: function () {
formatter: function () { let value
let value if (labelFormatter.type === 'percent') {
if (labelFormatter.type === 'percent') { value = options.percent
value = options.percent } else {
} else { value = data
value = data
}
return valueFormatter(value, labelFormatter)
} }
return valueFormatter(value, labelFormatter)
} }
} else { } as GaugeOptions['statistic']['content']
labelContent = false
}
} }
const statistic = { const statistic = {
content: labelContent content: labelContent
} }
const { gaugeAxisLine, gaugePercentLabel } = customAttr.basicStyle
const { min, max } = context
const tmp = {
axis: {
label: {
formatter: v => {
if (gaugeAxisLine === false) {
return ''
}
if (gaugePercentLabel === false) {
const val = v === '0' ? min : v === '1' ? max : min + (max - min) * v
return valueFormatter(val, labelFormatter)
}
return v === '0' ? v : v * 100 + '%'
}
}
}
}
options = merge(options, tmp)
return { ...options, statistic } return { ...options, statistic }
} }
@ -263,13 +289,17 @@ export class Gauge extends G2PlotChartView<GaugeOptions, G2Gauge> {
return chart return chart
} }
protected setupOptions(chart: Chart, options: GaugeOptions, ...extra: any[]): GaugeOptions { protected setupOptions(
chart: Chart,
options: GaugeOptions,
context: Record<string, any>
): GaugeOptions {
return flow( return flow(
this.configTheme, this.configTheme,
this.configMisc, this.configMisc,
this.configLabel, this.configLabel,
this.configRange this.configRange
)(chart, options, extra) )(chart, options, context)
} }
constructor() { constructor() {
super('gauge', DEFAULT_DATA) super('gauge', DEFAULT_DATA)

View File

@ -30,7 +30,7 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
'basic-style-selector': ['colors', 'alpha', 'scatterSymbol', 'scatterSymbolSize'], 'basic-style-selector': ['colors', 'alpha', 'scatterSymbol', 'scatterSymbolSize'],
'label-selector': ['fontSize', 'color'], 'label-selector': ['fontSize', 'color'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'x-axis-selector': [ 'x-axis-selector': [
'position', 'position',
'name', 'name',

View File

@ -24,7 +24,7 @@ export class Radar extends G2PlotChartView<RadarOptions, G2Radar> {
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
'basic-style-selector': ['colors', 'alpha', 'radarShape'], 'basic-style-selector': ['colors', 'alpha', 'radarShape'],
'label-selector': ['seriesLabelFormatter'], 'label-selector': ['seriesLabelFormatter'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'misc-style-selector': ['showName', 'color', 'fontSize', 'axisColor'], 'misc-style-selector': ['showName', 'color', 'fontSize', 'axisColor'],
'title-selector': [ 'title-selector': [
'show', 'show',
@ -42,6 +42,7 @@ export class Radar extends G2PlotChartView<RadarOptions, G2Radar> {
'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'] 'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition']
} }
selectorSpec: EditorSelectorSpec = { selectorSpec: EditorSelectorSpec = {
...this['selectorSpec'],
'misc-style-selector': { 'misc-style-selector': {
title: `${t('chart.tooltip_axis')}` title: `${t('chart.tooltip_axis')}`
} }

View File

@ -45,7 +45,7 @@ export class RangeBar extends G2PlotChartView<SankeyOptions, Sankey> {
propertyInner = { propertyInner = {
...SANKEY_EDITOR_PROPERTY_INNER, ...SANKEY_EDITOR_PROPERTY_INNER,
'label-selector': ['color', 'fontSize'], 'label-selector': ['color', 'fontSize'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter'] 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
} }
axis: AxisType[] = [...SANKEY_AXIS_TYPE] axis: AxisType[] = [...SANKEY_AXIS_TYPE]
protected baseOptions: SankeyOptions = { protected baseOptions: SankeyOptions = {

View File

@ -29,7 +29,7 @@ export class Scatter extends G2PlotChartView<ScatterOptions, G2Scatter> {
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
'basic-style-selector': ['colors', 'alpha', 'scatterSymbol', 'scatterSymbolSize'], 'basic-style-selector': ['colors', 'alpha', 'scatterSymbol', 'scatterSymbolSize'],
'label-selector': ['fontSize', 'color', 'labelFormatter'], 'label-selector': ['fontSize', 'color', 'labelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'x-axis-selector': [ 'x-axis-selector': [
'position', 'position',
'name', 'name',

View File

@ -29,7 +29,7 @@ export class Treemap extends G2PlotChartView<TreemapOptions, G2Treemap> {
'basic-style-selector': ['colors', 'alpha'], 'basic-style-selector': ['colors', 'alpha'],
'label-selector': ['fontSize', 'color', 'showDimension', 'showQuota', 'showProportion'], 'label-selector': ['fontSize', 'color', 'showDimension', 'showQuota', 'showProportion'],
'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'title-selector': [ 'title-selector': [
'title', 'title',
'fontSize', 'fontSize',

View File

@ -41,7 +41,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'fontShadow' 'fontShadow'
], ],
'misc-selector': ['wordSizeRange', 'wordSpacing'], 'misc-selector': ['wordSizeRange', 'wordSpacing'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter'] 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show']
} }
axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] axis: AxisType[] = ['xAxis', 'yAxis', 'filter']
axisConfig: AxisConfig = { axisConfig: AxisConfig = {

View File

@ -22,7 +22,7 @@ export const PIE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'showQuota', 'showQuota',
'showProportion' 'showProportion'
], ],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter'], 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
'basic-style-selector': ['colors', 'alpha', 'radius'], 'basic-style-selector': ['colors', 'alpha', 'radius'],
'title-selector': [ 'title-selector': [
'title', 'title',

View File

@ -4,6 +4,7 @@ export const TABLE_EDITOR_PROPERTY: EditorProperty[] = [
'table-header-selector', 'table-header-selector',
'table-cell-selector', 'table-cell-selector',
'title-selector', 'title-selector',
'tooltip-selector',
'function-cfg', 'function-cfg',
'threshold', 'threshold',
'scroll-cfg', 'scroll-cfg',
@ -45,6 +46,7 @@ export const TABLE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'letterSpace', 'letterSpace',
'fontShadow' 'fontShadow'
], ],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'],
'function-cfg': ['emptyDataStrategy'], 'function-cfg': ['emptyDataStrategy'],
threshold: ['tableThreshold'] threshold: ['tableThreshold']
} }

View File

@ -5,7 +5,7 @@ import { S2ChartView, S2DrawOptions } from '../../types/impl/s2'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common' import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { isNumber } from 'lodash-es' import { isNumber } from 'lodash-es'
import { copyContent } from '@/views/chart/components/js/panel/common/common_table' import { copyContent, SortTooltip } from '@/views/chart/components/js/panel/common/common_table'
const { t } = useI18n() const { t } = useI18n()
@ -18,7 +18,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
...TABLE_EDITOR_PROPERTY_INNER, ...TABLE_EDITOR_PROPERTY_INNER,
'table-header-selector': [ 'table-header-selector': [
...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'],
'tableHeaderSort' 'tableHeaderSort',
'showTableHeader'
], ],
'basic-style-selector': [ 'basic-style-selector': [
'tableColumnMode', 'tableColumnMode',
@ -107,7 +108,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
style: this.configStyle(chart), style: this.configStyle(chart),
conditions: this.configConditions(chart), conditions: this.configConditions(chart),
tooltip: { tooltip: {
getContainer: () => containerDom getContainer: () => containerDom,
renderTooltip: sheet => new SortTooltip(sheet)
} }
} }
// 开启序号之后第一列就是序号列修改 label 即可 // 开启序号之后第一列就是序号列修改 label 即可
@ -134,9 +136,23 @@ export class TableInfo extends S2ChartView<TableSheet> {
} }
} }
// tooltip // tooltip
this.configTooltip(s2Options) this.configTooltip(chart, s2Options)
// header interaction // 隐藏表头保留顶部的分割线, 禁用表头横向 resize
this.configHeaderInteraction(chart, s2Options) if (customAttr.tableHeader.showTableHeader === false) {
s2Options.style.colCfg.height = 1
s2Options.interaction = {
resize: {
colCellVertical: false
}
}
s2Options.colCell = (node, sheet, config) => {
node.label = ' '
return new TableColCell(node, sheet, config)
}
} else {
// header interaction
this.configHeaderInteraction(chart, s2Options)
}
// 开始渲染 // 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options) const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
@ -168,14 +184,10 @@ export class TableInfo extends S2ChartView<TableSheet> {
} }
action(param) action(param)
}) })
// tooltip
// hover const { show } = customAttr.tooltip
const { showColTooltip } = customAttr.tableHeader if (show) {
if (showColTooltip) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) 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)) newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
} }
// header resize // header resize

View File

@ -2,7 +2,11 @@ import { S2ChartView, S2DrawOptions } from '@/views/chart/components/js/panel/ty
import { S2Event, S2Options, TableSheet, TableColCell, ViewMeta, TableDataCell } from '@antv/s2' import { S2Event, S2Options, TableSheet, TableColCell, ViewMeta, TableDataCell } from '@antv/s2'
import { parseJson } from '@/views/chart/components/js/util' import { parseJson } from '@/views/chart/components/js/util'
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter' import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
import { copyContent, getCurrentField } from '@/views/chart/components/js/panel/common/common_table' import {
copyContent,
getCurrentField,
SortTooltip
} from '@/views/chart/components/js/panel/common/common_table'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common' import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { isNumber } from 'lodash-es' import { isNumber } from 'lodash-es'
@ -17,7 +21,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
...TABLE_EDITOR_PROPERTY_INNER, ...TABLE_EDITOR_PROPERTY_INNER,
'table-header-selector': [ 'table-header-selector': [
...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'],
'tableHeaderSort' 'tableHeaderSort',
'showTableHeader'
] ]
} }
axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter'] axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter']
@ -110,7 +115,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
style: this.configStyle(chart), style: this.configStyle(chart),
conditions: this.configConditions(chart), conditions: this.configConditions(chart),
tooltip: { tooltip: {
getContainer: () => containerDom getContainer: () => containerDom,
renderTooltip: sheet => new SortTooltip(sheet)
} }
} }
// 开启序号之后第一列就是序号列修改 label 即可 // 开启序号之后第一列就是序号列修改 label 即可
@ -133,9 +139,23 @@ export class TableNormal extends S2ChartView<TableSheet> {
} }
} }
// tooltip // tooltip
this.configTooltip(s2Options) this.configTooltip(chart, s2Options)
// header interaction // 隐藏表头保留顶部的分割线, 禁用表头横向 resize
this.configHeaderInteraction(chart, s2Options) if (customAttr.tableHeader.showTableHeader === false) {
s2Options.style.colCfg.height = 1
s2Options.interaction = {
resize: {
colCellVertical: false
}
}
s2Options.colCell = (node, sheet, config) => {
node.label = ' '
return new TableColCell(node, sheet, config)
}
} else {
// header interaction
this.configHeaderInteraction(chart, s2Options)
}
// 开始渲染 // 开始渲染
const newChart = new TableSheet(containerDom, s2DataConfig, s2Options) const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
@ -167,13 +187,10 @@ export class TableNormal extends S2ChartView<TableSheet> {
} }
action(param) action(param)
}) })
// hover // tooltip
const { showColTooltip } = customAttr.tableHeader const { show } = customAttr.tooltip
if (showColTooltip) { if (show) {
newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) 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)) newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
} }
// header resize // header resize

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