Merge branch 'dev-v2' into radius-column-bar

This commit is contained in:
ulleo 2024-06-06 18:06:48 +08:00
commit ee8bce2fe4
94 changed files with 1095 additions and 768 deletions

View File

@ -175,7 +175,7 @@ public class ChartDataManage {
FilterTreeObj fieldCustomFilter = view.getCustomFilter();
List<ChartViewFieldDTO> drill = new ArrayList<>(view.getDrillFields());
DatasetGroupInfoDTO table = datasetGroupManage.get(view.getTableId(), null);
DatasetGroupInfoDTO table = datasetGroupManage.getDatasetGroupInfoDTO(view.getTableId(), null);
if (table == null) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_no_ds"));
}

View File

@ -4,8 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dataease.api.chart.dto.*;
import io.dataease.api.chart.vo.ViewSelectorVO;
import io.dataease.api.chart.filter.FilterTreeObj;
import io.dataease.api.chart.vo.ViewSelectorVO;
import io.dataease.api.dataset.union.model.SQLObj;
import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
@ -20,6 +20,7 @@ import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.i18n.Translator;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
@ -120,6 +121,7 @@ public class ChartViewManege {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.isNull("chart_id");
List<CoreDatasetTableField> fields = coreDatasetTableFieldMapper.selectList(wrapper);
List<DatasetTableFieldDTO> collect = fields.stream().map(ele -> {
@ -173,6 +175,36 @@ public class ChartViewManege {
return map;
}
public void copyField(Long id, Long chartId) {
CoreDatasetTableField coreDatasetTableField = coreDatasetTableFieldMapper.selectById(id);
QueryWrapper<CoreDatasetTableField> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("dataset_group_id", coreDatasetTableField.getDatasetGroupId());
List<CoreDatasetTableField> coreDatasetTableFields = coreDatasetTableFieldMapper.selectList(queryWrapper);
HashMap<String, String> map = new HashMap<>();
for (CoreDatasetTableField ele : coreDatasetTableFields) {
map.put(ele.getName(), ele.getName());
}
newName(map, coreDatasetTableField, coreDatasetTableField.getName());
coreDatasetTableField.setChartId(chartId);
coreDatasetTableField.setExtField(2);
coreDatasetTableField.setOriginName("[" + id + "]");
coreDatasetTableField.setId(IDUtils.snowID());
coreDatasetTableFieldMapper.insert(coreDatasetTableField);
}
private void newName(HashMap<String, String> map, CoreDatasetTableField coreDatasetTableField, String name) {
name = name + "_copy";
if (map.containsKey(name)) {
newName(map, coreDatasetTableField, name);
} else {
coreDatasetTableField.setName(name);
}
}
public void deleteField(Long id) {
coreDatasetTableFieldMapper.deleteById(id);
}
public DatasetTableFieldDTO createCountField(Long id) {
DatasetTableFieldDTO dto = new DatasetTableFieldDTO();
dto.setId(-1L);

View File

@ -4,6 +4,7 @@ import io.dataease.api.chart.ChartDataApi;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.chart.constant.ChartConstants;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.AuthConstant;
import io.dataease.constant.CommonConstants;
@ -69,7 +70,12 @@ public class ChartDataServer implements ChartDataApi {
try {
ChartViewDTO viewDTO = request.getViewInfo();
viewDTO.setIsExcelExport(true);
viewDTO.setResultCount(limit);
if(ChartConstants.VIEW_RESULT_MODE.CUSTOM.equals(viewDTO.getResultMode())){
Integer limitCount = viewDTO.getResultCount();
viewDTO.setResultCount(limitCount>limit?limit:limitCount);
}else{
viewDTO.setResultCount(limit);
}
ChartViewDTO chartViewInfo = getData(viewDTO);
List<Object[]> tableRow = (List) chartViewInfo.getData().get("sourceData");
request.setDetails(tableRow);

View File

@ -57,4 +57,14 @@ public class ChartViewServer implements ChartViewApi {
public List<ViewSelectorVO> viewOption(Long resourceId) {
return chartViewManege.viewOption(resourceId);
}
@Override
public void copyField(Long id, Long chartId) {
chartViewManege.copyField(id, chartId);
}
@Override
public void deleteField(Long id) {
chartViewManege.deleteField(id);
}
}

View File

@ -1,7 +1,6 @@
package io.dataease.dataset.manage;
import io.dataease.api.chart.dto.ChartExtFilterDTO;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ColumnPermissionItem;
import io.dataease.api.chart.dto.DeSortField;
import io.dataease.api.chart.request.ChartExtRequest;
@ -204,7 +203,7 @@ public class DatasetDataManage {
Table2SQLObj.table2sqlobj(sqlMeta, null, "(" + sql + ")", crossDs);
Field2SQLObj.field2sqlObj(sqlMeta, fields, fields, crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, datasetGroupInfoDTO.getSortFields(), fields, crossDs, dsMap);
String querySQL;
if (start == null || count == null) {
querySQL = SQLProvider.createQuerySQL(sqlMeta, false, needOrder, false);
@ -425,13 +424,9 @@ public class DatasetDataManage {
}
List<DatasetTableFieldDTO> allFields = new ArrayList<>();
// 根据图表计算字段获取数据集
Long datasetGroupId;
if (field.getDatasetGroupId() == null && field.getChartId() != null) {
ChartViewDTO chart = chartViewManege.getChart(field.getChartId());
datasetGroupId = chart.getTableId();
Long datasetGroupId = field.getDatasetGroupId();
if (field.getChartId() != null) {
allFields.addAll(datasetTableFieldManage.getChartCalcFields(field.getChartId()));
} else {
datasetGroupId = field.getDatasetGroupId();
}
DatasetGroupInfoDTO datasetGroupInfoDTO = datasetGroupManage.get(datasetGroupId, null);
@ -471,9 +466,9 @@ public class DatasetDataManage {
rowPermissionsTree = permissionManage.getRowPermissionsTree(datasetGroupInfoDTO.getId(), user.getUserId());
}
Field2SQLObj.field2sqlObj(sqlMeta, fields, datasetGroupInfoDTO.getAllFields(), crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
Field2SQLObj.field2sqlObj(sqlMeta, fields, allFields, crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, allFields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, datasetGroupInfoDTO.getSortFields(), allFields, crossDs, dsMap);
String querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, true, 0, 1000);
querySQL = SqlUtils.rebuildSQL(querySQL, sqlMeta, crossDs, dsMap);
logger.info("calcite data enum sql: " + querySQL);
@ -539,20 +534,18 @@ public class DatasetDataManage {
}
}
List<DatasetTableFieldDTO> allFields = new ArrayList<>();
for (Long id : ids) {
DatasetTableFieldDTO field = datasetTableFieldManage.selectById(id);
if (field == null) {
DEException.throwException(Translator.get("i18n_no_field"));
}
List<DatasetTableFieldDTO> allFields = new ArrayList<>();
// 根据图表计算字段获取数据集
Long datasetGroupId;
if (field.getDatasetGroupId() == null && field.getChartId() != null) {
ChartViewDTO chart = chartViewManege.getChart(field.getChartId());
datasetGroupId = chart.getTableId();
Long datasetGroupId = field.getDatasetGroupId();
if (field.getChartId() != null) {
allFields.addAll(datasetTableFieldManage.getChartCalcFields(field.getChartId()));
} else {
datasetGroupId = field.getDatasetGroupId();
}
datasetGroupInfoDTO = datasetGroupManage.get(datasetGroupId, null);
@ -687,10 +680,10 @@ public class DatasetDataManage {
sortDistinct = false;
}
Field2SQLObj.field2sqlObj(sqlMeta, fields, datasetGroupInfoDTO.getAllFields(), crossDs, dsMap);
ExtWhere2Str.extWhere2sqlOjb(sqlMeta, extFilterList, datasetGroupInfoDTO.getAllFields(), crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, fields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, fields, datasetGroupInfoDTO.getSortFields(), crossDs, dsMap);
Field2SQLObj.field2sqlObj(sqlMeta, fields, allFields, crossDs, dsMap);
ExtWhere2Str.extWhere2sqlOjb(sqlMeta, extFilterList, allFields, crossDs, dsMap);
WhereTree2Str.transFilterTrees(sqlMeta, rowPermissionsTree, allFields, crossDs, dsMap);
Order2SQLObj.getOrders(sqlMeta, datasetGroupInfoDTO.getSortFields(), allFields, crossDs, dsMap);
String querySQL = SQLProvider.createQuerySQLWithLimit(sqlMeta, false, needOrder, sortDistinct && ids.size() == 1, 0, 1000);
querySQL = SqlUtils.rebuildSQL(querySQL, sqlMeta, crossDs, dsMap);
logger.info("calcite data enum sql: " + querySQL);

View File

@ -371,6 +371,10 @@ public class DatasetGroupManage {
return dto;
}
public DatasetGroupInfoDTO getDatasetGroupInfoDTO(Long id, String type) throws Exception {
return get(id, type);
}
public DatasetGroupInfoDTO get(Long id, String type) throws Exception {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(id);
if (coreDatasetGroup == null) {

View File

@ -156,6 +156,7 @@ public class DatasetTableFieldManage {
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
wrapper.eq("dataset_group_id", id);
wrapper.eq("checked", true);
wrapper.isNull("chart_id");
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
}

View File

@ -117,9 +117,9 @@ public class CalciteProvider {
tableDesc.setDatasourceId(datasourceRequest.getDatasource().getId());
tableDesc.setType("db");
tableDesc.setTableName(resultSet.getString(1));
if(resultSet.getMetaData().getColumnCount() > 1){
if (resultSet.getMetaData().getColumnCount() > 1) {
tableDesc.setName(resultSet.getString(2));
}else {
} else {
tableDesc.setName(resultSet.getString(1));
}
return tableDesc;
@ -316,10 +316,104 @@ public class CalciteProvider {
return list;
}
public void hidePW(DatasourceDTO datasourceDTO) {
DatasourceConfiguration configuration = null;
DatasourceType datasourceType = DatasourceType.valueOf(datasourceDTO.getType());
switch (datasourceType) {
case mysql:
case mongo:
case mariadb:
case TiDB:
case StarRocks:
case doris:
configuration = JsonUtil.parseObject(datasourceDTO.getConfiguration(), Mysql.class);
if (StringUtils.isNotEmpty(configuration.getUrlType()) && configuration.getUrlType().equalsIgnoreCase("jdbcUrl")) {
if (configuration.getJdbcUrl().contains("password=")) {
String[] params = configuration.getJdbcUrl().split("\\?")[1].split("&");
String pd = "";
for (int i = 0; i < params.length; i++) {
if (params[i].contains("password=")) {
pd = params[i];
}
}
configuration.setJdbcUrl(configuration.getJdbcUrl().replace(pd, "password=******"));
datasourceDTO.setConfiguration(JsonUtil.toJSONString(configuration).toString());
}
}
break;
case pg:
configuration = JsonUtil.parseObject(datasourceDTO.getConfiguration(), Pg.class);
if (StringUtils.isNotEmpty(configuration.getUrlType()) && configuration.getUrlType().equalsIgnoreCase("jdbcUrl")) {
if (configuration.getJdbcUrl().contains("password=")) {
String[] params = configuration.getJdbcUrl().split("\\?")[1].split("&");
String pd = "";
for (int i = 0; i < params.length; i++) {
if (params[i].contains("password=")) {
pd = params[i];
}
}
configuration.setJdbcUrl(configuration.getJdbcUrl().replace(pd, "password=******"));
datasourceDTO.setConfiguration(JsonUtil.toJSONString(configuration).toString());
}
}
break;
case redshift:
configuration = JsonUtil.parseObject(datasourceDTO.getConfiguration(), Redshift.class);
if (StringUtils.isNotEmpty(configuration.getUrlType()) && configuration.getUrlType().equalsIgnoreCase("jdbcUrl")) {
if (configuration.getJdbcUrl().contains("password=")) {
String[] params = configuration.getJdbcUrl().split("\\?")[1].split("&");
String pd = "";
for (int i = 0; i < params.length; i++) {
if (params[i].contains("password=")) {
pd = params[i];
}
}
configuration.setJdbcUrl(configuration.getJdbcUrl().replace(pd, "password=******"));
datasourceDTO.setConfiguration(JsonUtil.toJSONString(configuration).toString());
}
}
break;
case ck:
configuration = JsonUtil.parseObject(datasourceDTO.getConfiguration(), CK.class);
if (StringUtils.isNotEmpty(configuration.getUrlType()) && configuration.getUrlType().equalsIgnoreCase("jdbcUrl")) {
if (configuration.getJdbcUrl().contains("password=")) {
String[] params = configuration.getJdbcUrl().split("\\?")[1].split("&");
String pd = "";
for (int i = 0; i < params.length; i++) {
if (params[i].contains("password=")) {
pd = params[i];
}
}
configuration.setJdbcUrl(configuration.getJdbcUrl().replace(pd, "password=******"));
datasourceDTO.setConfiguration(JsonUtil.toJSONString(configuration).toString());
}
}
break;
case impala:
configuration = JsonUtil.parseObject(datasourceDTO.getConfiguration(), Impala.class);
if (StringUtils.isNotEmpty(configuration.getUrlType()) && configuration.getUrlType().equalsIgnoreCase("jdbcUrl")) {
if (configuration.getJdbcUrl().contains("password=")) {
String[] params = configuration.getJdbcUrl().split(";")[1].split("&");
String pd = "";
for (int i = 0; i < params.length; i++) {
if (params[i].contains("password=")) {
pd = params[i];
}
}
configuration.setJdbcUrl(configuration.getJdbcUrl().replace(pd, "password=******"));
datasourceDTO.setConfiguration(JsonUtil.toJSONString(configuration).toString());
}
}
break;
default:
break;
}
}
private String getTableFiledSql(DatasourceRequest datasourceRequest) {
String sql = "";
DatasourceConfiguration configuration = null;
String database="";
String database = "";
DatasourceType datasourceType = DatasourceType.valueOf(datasourceRequest.getDatasource().getType());
switch (datasourceType) {
case mysql:
@ -440,7 +534,8 @@ public class CalciteProvider {
return sql;
}
private TableField getTableFieldDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException {
private TableField getTableFieldDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws
SQLException {
TableField tableField = new TableField();
tableField.setOriginName(resultSet.getString(1));
tableField.setType(resultSet.getString(2).toUpperCase());
@ -809,7 +904,7 @@ public class CalciteProvider {
" AND ep.class = 1 \n" +
" AND ep.name = 'MS_Description'\n" +
"where sc.name ='DS_SCHEMA'"
.replace("DS_SCHEMA", configuration.getSchema()));
.replace("DS_SCHEMA", configuration.getSchema()));
tableSqls.add("SELECT \n" +
" t.name AS TableName, \n" +
" ep.value AS TableDescription \n" +

View File

@ -488,12 +488,20 @@ public class DatasourceServer implements DatasourceApi {
return calciteProvider.getSchema(datasourceRequest);
}
@Override
public DatasourceDTO hidePw(Long datasourceId) throws DEException {
return getDatasourceDTOById(datasourceId, true);
}
@Override
public DatasourceDTO get(Long datasourceId) throws DEException {
return getDatasourceDTOById(datasourceId, false);
}
private DatasourceDTO getDatasourceDTOById(Long datasourceId, boolean hidePw) throws DEException {
DatasourceDTO datasourceDTO = new DatasourceDTO();
CoreDatasource datasource = datasourceMapper.selectById(datasourceId);
if(datasource == null){
if (datasource == null) {
DEException.throwException("不存在的数据源!");
}
BeanUtils.copyBean(datasourceDTO, datasource);
@ -547,15 +555,18 @@ public class DatasourceServer implements DatasourceApi {
if (task != null) {
datasourceDTO.setLastSyncTime(task.getStartTime());
}
} else {
if (hidePw) {
calciteProvider.hidePW(datasourceDTO);
}
}
if (datasourceDTO.getType().equalsIgnoreCase(DatasourceConfiguration.DatasourceType.Excel.toString())) {
datasourceDTO.setFileName(ExcelUtils.getFileName(datasource));
datasourceDTO.setSize(ExcelUtils.getSize(datasource));
}
datasourceDTO.setConfiguration(new String(Base64.getEncoder().encode(datasourceDTO.getConfiguration().getBytes())));
datasourceDTO.setCreator(coreUserManage.getUserName(Long.valueOf(datasourceDTO.getCreateBy())));
return datasourceDTO;
}
@ -750,7 +761,7 @@ public class DatasourceServer implements DatasourceApi {
datasourceRequest.setDsList(Map.of(datasourceSchemaDTO.getId(), datasourceSchemaDTO));
datasourceRequest.setQuery(TableUtils.tableName2Sql(datasourceSchemaDTO, tableName) + " LIMIT 0 OFFSET 0");
datasourceRequest.setTable(tableName);
List<TableField> tableFields = (List<TableField>) calciteProvider.fetchTableField(datasourceRequest) ;
List<TableField> tableFields = (List<TableField>) calciteProvider.fetchTableField(datasourceRequest);
return tableFields.stream().filter(tableField -> {
return !tableField.getOriginName().equalsIgnoreCase("dataease_uuid");
}).collect(Collectors.toList());

View File

@ -22,17 +22,17 @@ import java.util.Objects;
*/
public class Order2SQLObj {
public static void getOrders(SQLMeta meta, List<DatasetTableFieldDTO> fields, List<DeSortField> sortFields, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
public static void getOrders(SQLMeta meta, List<DeSortField> sortFields, List<DatasetTableFieldDTO> originFields, boolean isCross, Map<Long, DatasourceSchemaDTO> dsMap) {
SQLObj tableObj = meta.getTable();
List<SQLObj> xOrders = meta.getXOrders() == null ? new ArrayList<>() : meta.getXOrders();
if (ObjectUtils.isEmpty(tableObj)) {
return;
}
if (ObjectUtils.isNotEmpty(sortFields)) {
int step = fields.size();
int step = originFields.size();
for (int i = step; i < (step + sortFields.size()); i++) {
DeSortField deSortField = sortFields.get(i - step);
SQLObj order = buildSortField(deSortField, tableObj, i, fields, isCross, dsMap);
SQLObj order = buildSortField(deSortField, tableObj, i, originFields, isCross, dsMap);
xOrders.add(order);
}
meta.setXOrders(xOrders);

View File

@ -12,6 +12,7 @@ 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.system.manage.SysParameterManage;
import io.dataease.utils.*;
import io.dataease.visualization.server.DataVisualizationServer;
import io.dataease.websocket.WsMessage;
@ -50,6 +51,8 @@ public class ExportCenterManage {
private CoreChartViewMapper coreChartViewMapper;
@Autowired
private WsService wsService;
@Resource
private SysParameterManage sysParameterManage;
@Value("${export.dataset.limit:100000}")
private int limit;
@ -183,12 +186,21 @@ public class ExportCenterManage {
if (status.equalsIgnoreCase(exportTaskDTO.getExportStatus())) {
setExportFromAbsName(exportTaskDTO);
}
if (status.equalsIgnoreCase(exportTaskDTO.getExportStatus())) {
setOrgName(exportTaskDTO);
}
result.add(exportTaskDTO);
});
return result;
}
private void setOrgName(ExportTaskDTO exportTaskDTO) {
if (exportTaskDTO.getExportFromType().equalsIgnoreCase("chart")) {
exportTaskDTO.setOrgName(dataVisualizationServer.getAbsPath(exportTaskDTO.getExportFrom()));
}
}
private void setExportFromAbsName(ExportTaskDTO exportTaskDTO) {
if (exportTaskDTO.getExportFromType().equalsIgnoreCase("chart")) {
exportTaskDTO.setExportFromName(dataVisualizationServer.getAbsPath(exportTaskDTO.getExportFrom()));
@ -408,6 +420,21 @@ public class ExportCenterManage {
private static final String LOG_RETENTION = "30";
public void cleanLog() {
String key = "basic.exportFileLiveTime";
String val = sysParameterManage.singleVal(key);
if (StringUtils.isBlank(val)) {
DEException.throwException("未获取到文件保留时间");
}
QueryWrapper<CoreExportTask> queryWrapper = new QueryWrapper<>();
long expTime = Long.parseLong(val) * 24L * 3600L * 1000L;
long threshold = System.currentTimeMillis() - expTime;
queryWrapper.lt("export_time", threshold);
exportTaskMapper.selectList(queryWrapper).forEach(coreExportTask -> {
delete(coreExportTask.getId());
});
}
}

View File

@ -10,9 +10,9 @@ import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
@Component
public class CheckDsStatusJob implements Job {
@Resource
private DatasourceServer datasourceServer;

View File

@ -14,12 +14,13 @@ import io.dataease.constant.AuthConstant;
import io.dataease.constant.BusiResourceEnum;
import io.dataease.exception.DEException;
import io.dataease.license.config.XpackInteract;
import io.dataease.share.dao.auto.mapper.XpackShareMapper;
import io.dataease.utils.*;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.share.dao.auto.entity.XpackShare;
import io.dataease.share.dao.auto.mapper.XpackShareMapper;
import io.dataease.share.dao.ext.mapper.XpackShareExtMapper;
import io.dataease.share.dao.ext.po.XpackSharePO;
import io.dataease.share.util.LinkTokenUtil;
import io.dataease.utils.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
@ -125,7 +126,6 @@ public class XpackShareManage {
}
public IPage<XpackSharePO> querySharePage(int goPage, int pageSize, VisualizationWorkbranchQueryRequest request) {
Long uid = AuthUtils.getUser().getUserId();
QueryWrapper<Object> queryWrapper = new QueryWrapper<>();
@ -174,7 +174,7 @@ public class XpackShareManage {
return pos.stream().map(po ->
new XpackShareGridVO(
po.getShareId(), po.getResourceId(), po.getName(), po.getCreator().toString(),
po.getTime(), po.getExp(), 9,po.getExtFlag(),po.getType())).toList();
po.getTime(), po.getExp(), 9, po.getExtFlag(), po.getType())).toList();
}
private XpackShareManage proxy() {
@ -182,6 +182,10 @@ public class XpackShareManage {
}
public XpackShareProxyVO proxyInfo(XpackShareProxyRequest request) {
boolean inIframeError = request.isInIframe() && !LicenseUtil.licenseValid();
if (inIframeError) {
return new XpackShareProxyVO();
}
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("uuid", request.getUuid());
XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper);
@ -192,7 +196,7 @@ public class XpackShareManage {
response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken);
Integer type = xpackShare.getType();
String typeText = (ObjectUtils.isNotEmpty(type) && type == 1) ? "dashboard" : "dataV";
return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText);
return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError);
}
private boolean linkExp(XpackShare xpackShare) {

View File

@ -20,8 +20,10 @@ public class DeWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
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);
if(name != null){
Long userId = Long.parseLong(name);
WsUtil.onLine(userId);
}
});
super.afterConnectionEstablished(session);
}
@ -30,8 +32,10 @@ public class DeWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
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);
if(name != null){
Long userId = Long.parseLong(name);
WsUtil.offLine(userId);
}
});
super.afterConnectionClosed(session, closeStatus);

View File

@ -13,3 +13,24 @@ CREATE TABLE `core_sys_startup_job`
BEGIN;
INSERT INTO `core_sys_startup_job` VALUES ('chartFilterMerge', 'chartFilterMerge', 'ready');
COMMIT;
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='导出任务表';
UPDATE `QRTZ_JOB_DETAILS` SET `JOB_CLASS_NAME` = 'io.dataease.job.schedule.CheckDsStatusJob' WHERE (`SCHED_NAME` = 'deSyncJob') and (`JOB_NAME` = 'Datasource') and (`JOB_GROUP` = 'check_status');

View File

@ -52,3 +52,6 @@ CREATE TABLE `xpack_platform_token`
PRIMARY KEY (`id`)
);
UPDATE `QRTZ_JOB_DETAILS` SET `JOB_CLASS_NAME` = 'io.dataease.job.schedule.CheckDsStatusJob' WHERE (`SCHED_NAME` = 'deSyncJob') and (`JOB_NAME` = 'Datasource') and (`JOB_GROUP` = 'check_status');

View File

@ -20,6 +20,7 @@
"@codemirror/lang-sql": "^6.4.0",
"@npkg/tinymce-plugins": "^0.0.7",
"@tinymce/tinymce-vue": "^5.1.0",
"@videojs-player/vue": "^1.0.0",
"@vueuse/core": "^9.13.0",
"ace-builds": "^1.15.3",
"axios": "^1.3.3",
@ -46,6 +47,7 @@
"snowflake-id": "^1.1.0",
"tinymce": "^5.8.2",
"vant": "^4.8.3",
"video.js": "^7.21.6",
"vue": "^3.3.4",
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",
@ -54,7 +56,6 @@
"vue-types": "^5.0.2",
"vue-uuid": "^3.0.0",
"vue3-ace-editor": "^2.2.2",
"vue3-video-play": "^1.3.2",
"vuedraggable": "^4.1.0",
"web-storage-cache": "^1.1.1",
"xss": "^1.0.14"

View File

@ -147,6 +147,8 @@ export const deleteById = (id: number) => request.get({ url: '/datasource/delete
export const getById = (id: number) => request.get({ url: '/datasource/get/' + id })
export const getHidePwById = (id: number) => request.get({ url: '/datasource/hidePw/' + id })
export const uploadFile = async (data): Promise<IResponse> => {
return request
.post({

View File

@ -0,0 +1,7 @@
<svg width="80" height="56" viewBox="0 0 80 56" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" />
<path d="M17.5 50C17.2239 50 17 49.7761 17 49.5V42.5C17 42.2239 17.2239 42 17.5 42H52.5C52.7761 42 53 42.2239 53 42.5V49.5C53 49.7761 52.7761 50 52.5 50H17.5Z" />
<path d="M48.5 38C48.2239 38 48 37.7761 48 37.5V30.5C48 30.2239 48.2239 30 48.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H48.5Z" />
<path d="M28.5 26C28.2239 26 28 25.7761 28 25.5V18.5C28 18.2239 28.2239 18 28.5 18H47.5C47.7761 18 48 18.2239 48 18.5V25.5C48 25.7761 47.7761 26 47.5 26H28.5Z" />
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L48.5 6C48.7761 6 49 6.22386 49 6.5V13.5C49 13.7761 48.7761 14 48.5 14L9.5 14Z" />
</svg>

After

Width:  |  Height:  |  Size: 831 B

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M41 2.00391V53.0016V54.0016H38V2.00391H41Z" />
<path d="M4.5 50C4.22386 50 4 49.7761 4 49.5L4 42.5C4 42.2239 4.22386 42 4.5 42H35.5C35.7761 42 36 42.2239 36 42.5V49.5C36 49.7761 35.7761 50 35.5 50H4.5Z" />
<path d="M12.5 38C12.2239 38 12 37.7761 12 37.5V30.5C12 30.2239 12.2239 30 12.5 30H35.5C35.7761 30 36 30.2239 36 30.5V37.5C36 37.7761 35.7761 38 35.5 38H12.5Z" />
<path d="M43.5 14C43.2239 14 43 13.7761 43 13.5V6.5C43 6.22386 43.2239 6 43.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L43.5 14Z" />
<path d="M43.5 26C43.2239 26 43 25.7761 43 25.5V18.5C43 18.2239 43.2239 18 43.5 18H62.5C62.7761 18 63 18.2239 63 18.5V25.5C63 25.7761 62.7761 26 62.5 26H43.5Z" />
<path d="M43.5 38C43.2239 38 43 37.7761 43 37.5V30.5C43 30.2239 43.2239 30 43.5 30H57.5C57.7761 30 58 30.2239 58 30.5V37.5C58 37.7761 57.7761 38 57.5 38H43.5Z" />
<path d="M43.5 50C43.2239 50 43 49.7761 43 49.5V42.5C43 42.2239 43.2239 42 43.5 42H75.5C75.7761 42 76 42.2239 76 42.5V49.5C76 49.7761 75.7761 50 75.5 50H43.5Z" />
<path d="M16.5 26C16.2239 26 16 25.7761 16 25.5V18.5C16 18.2239 16.2239 18 16.5 18H35.5C35.7761 18 36 18.2239 36 18.5V25.5C36 25.7761 35.7761 26 35.5 26H16.5Z" />
<path d="M8.5 14C8.22386 14 8 13.7761 8 13.5V6.5C8 6.22386 8.22386 6 8.5 6L35.5 6C35.7761 6 36 6.22386 36 6.5V13.5C36 13.7761 35.7761 14 35.5 14L8.5 14Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1717496887274" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4257" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M376.5 70c41.4 0 81.5 8.1 119.3 24.1 36.5 15.4 69.3 37.5 97.4 65.7 28.2 28.2 50.3 61 65.7 97.4 16 37.7 24.1 77.9 24.1 119.3s-8.1 81.5-24.1 119.3c-15.4 36.5-37.5 69.3-65.7 97.4-28.2 28.2-61 50.3-97.4 65.7-37.7 16-77.9 24.1-119.3 24.1s-81.5-8.1-119.3-24.1c-36.5-15.4-69.3-37.5-97.4-65.7s-50.3-61-65.7-97.4C78.1 458 70 417.9 70 376.5s8.1-81.5 24.1-119.3c15.4-36.5 37.5-69.3 65.7-97.4 28.2-28.2 61-50.3 97.4-65.7C295 78.1 335.1 70 376.5 70m0-70C168.6 0 0 168.6 0 376.5S168.6 753 376.5 753 753 584.4 753 376.5 584.4 0 376.5 0z" p-id="4258"></path><path d="M274.5 136.1c8.8-8.8 18-17 27.5-24.6-37.3 13.6-72.2 35.3-102.1 65.2-109.7 109.7-109.7 287.7 0 397.4 33.2 33.2 72.6 56.3 114.4 69.4-13.9-10.1-27.2-21.5-39.8-34.1-130.7-130.7-130.7-342.6 0-473.3zM998.3 998.3c-34.2 34.2-90.2 34.2-124.5 0L622.2 746.6c-34.2-34.2-34.2-90.2 0-124.5 34.2-34.2 90.2-34.2 124.5 0l251.7 251.7c34.2 34.3 34.2 90.3-0.1 124.5z" p-id="4259"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" />
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" />
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" />
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" />
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" />
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L59.5 6C59.7761 6 60 6.22386 60 6.5V13.5C60 13.7761 59.7761 14 59.5 14L9.5 14Z" />
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H46.5C46.7761 18 47 18.2239 47 18.5V25.5C47 25.7761 46.7761 26 46.5 26H9.5Z" />
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H37.5C37.7761 30 38 30.2239 38 30.5V37.5C38 37.7761 37.7761 38 37.5 38H9.5Z" />
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H21.5C21.7761 42 22 42.2239 22 42.5V49.5C22 49.7761 21.7761 50 21.5 50H9.5Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,12 @@
<svg width="88" height="64" viewBox="0 0 88 64" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 30.002H80L80 33.002H8V30.002H9Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.0417 6H31L31 58H28V6H28.0417Z"/>
<path d="M57.666 22C58.0616 22 58.4483 21.8827 58.7772 21.6629C59.1061 21.4432 59.3624 21.1308 59.5138 20.7654C59.6651 20.3999 59.7048 19.9978 59.6276 19.6098C59.5504 19.2219 59.3599 18.8655 59.0802 18.5858C58.8005 18.3061 58.4442 18.1156 58.0562 18.0384C57.6682 17.9613 57.2661 18.0009 56.9006 18.1522C56.5352 18.3036 56.2228 18.56 56.0031 18.8889C55.7833 19.2178 55.666 19.6044 55.666 20C55.666 20.5304 55.8767 21.0391 56.2518 21.4142C56.6269 21.7893 57.1356 22 57.666 22Z" />
<path d="M46.4432 46.9969C46.1143 47.2167 45.7276 47.334 45.332 47.334C44.8016 47.334 44.2929 47.1233 43.9178 46.7482C43.5427 46.3731 43.332 45.8644 43.332 45.334C43.332 44.9384 43.4493 44.5517 43.6691 44.2228C43.8889 43.8939 44.2012 43.6376 44.5667 43.4862C44.9321 43.3349 45.3342 43.2952 45.7222 43.3724C46.1102 43.4496 46.4665 43.6401 46.7462 43.9198C47.0259 44.1995 47.2164 44.5558 47.2936 44.9438C47.3708 45.3318 47.3312 45.7339 47.1798 46.0994C47.0284 46.4648 46.7721 46.7772 46.4432 46.9969Z" />
<path d="M18.7772 48.9969C18.4483 49.2167 18.0616 49.334 17.666 49.334C17.1356 49.334 16.6269 49.1233 16.2518 48.7482C15.8767 48.3731 15.666 47.8644 15.666 47.334C15.666 46.9384 15.7833 46.5517 16.0031 46.2228C16.2228 45.8939 16.5352 45.6376 16.9006 45.4862C17.2661 45.3349 17.6682 45.2952 18.0562 45.3724C18.4442 45.4496 18.8005 45.6401 19.0802 45.9198C19.3599 46.1995 19.5504 46.5558 19.6276 46.9438C19.7048 47.3318 19.6651 47.7339 19.5138 48.0994C19.3624 48.4648 19.1061 48.7772 18.7772 48.9969Z" />
<path d="M44.5543 19.9938C43.8965 20.4334 43.1232 20.668 42.332 20.668C41.2712 20.668 40.2537 20.2465 39.5036 19.4964C38.7535 18.7463 38.332 17.7288 38.332 16.668C38.332 15.8768 38.5666 15.1035 39.0062 14.4457C39.4457 13.7879 40.0704 13.2752 40.8013 12.9725C41.5322 12.6697 42.3365 12.5905 43.1124 12.7448C43.8883 12.8992 44.601 13.2801 45.1605 13.8395C45.7199 14.399 46.1008 15.1117 46.2552 15.8876C46.4095 16.6635 46.3303 17.4678 46.0275 18.1987C45.7248 18.9296 45.2121 19.5543 44.5543 19.9938Z" />
<path d="M62.221 49.3272C61.5632 49.7667 60.7898 50.0013 59.9987 50.0013C58.9378 50.0013 57.9204 49.5799 57.1703 48.8297C56.4201 48.0796 55.9987 47.0622 55.9987 46.0013C55.9987 45.2102 56.2333 44.4368 56.6728 43.779C57.1123 43.1212 57.7371 42.6085 58.468 42.3058C59.1989 42.003 60.0031 41.9238 60.7791 42.0782C61.555 42.2325 62.2677 42.6135 62.8271 43.1729C63.3865 43.7323 63.7675 44.445 63.9218 45.2209C64.0762 45.9969 63.997 46.8011 63.6942 47.532C63.3915 48.2629 62.8788 48.8877 62.221 49.3272Z" />
<path d="M20.5543 21.6605C19.8965 22.1 19.1232 22.3346 18.332 22.3346C17.2712 22.3346 16.2538 21.9132 15.5036 21.1631C14.7535 20.4129 14.332 19.3955 14.332 18.3346C14.332 17.5435 14.5666 16.7702 15.0062 16.1124C15.4457 15.4546 16.0704 14.9419 16.8013 14.6391C17.5322 14.3364 18.3365 14.2572 19.1124 14.4115C19.8883 14.5658 20.601 14.9468 21.1605 15.5062C21.7199 16.0656 22.1008 16.7784 22.2552 17.5543C22.4095 18.3302 22.3303 19.1345 22.0275 19.8654C21.7248 20.5963 21.2121 21.221 20.5543 21.6605Z" />
<path d="M68.666 16C69.4571 16 70.2305 15.7654 70.8883 15.3259C71.5461 14.8864 72.0588 14.2616 72.3615 13.5307C72.6643 12.7998 72.7435 11.9956 72.5892 11.2196C72.4348 10.4437 72.0539 9.73099 71.4944 9.17158C70.935 8.61217 70.2223 8.2312 69.4464 8.07686C68.6705 7.92252 67.8662 8.00173 67.1353 8.30448C66.4044 8.60723 65.7797 9.11993 65.3401 9.77772C64.9006 10.4355 64.666 11.2089 64.666 12C64.666 13.0609 65.0874 14.0783 65.8376 14.8284C66.5877 15.5786 67.6052 16 68.666 16Z" />
<path d="M16 43C16.7911 43 17.5645 42.7654 18.2223 42.3259C18.8801 41.8864 19.3928 41.2616 19.6955 40.5307C19.9983 39.7998 20.0775 38.9956 19.9231 38.2196C19.7688 37.4437 19.3878 36.731 18.8284 36.1716C18.269 35.6122 17.5563 35.2312 16.7804 35.0769C16.0044 34.9225 15.2002 35.0017 14.4693 35.3045C13.7384 35.6072 13.1136 36.1199 12.6741 36.7777C12.2346 37.4355 12 38.2089 12 39C12 40.0609 12.4214 41.0783 13.1716 41.8284C13.9217 42.5786 14.9391 43 16 43Z" />
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -93,10 +93,13 @@ const saveCanvasWithCheck = () => {
const saveResource = () => {
wsCache.delete('DE-DV-CATCH-' + dvInfo.value.id)
if (styleChangeTimes.value > 0) {
snapshotStore.resetStyleChangeTimes()
canvasSave(() => {
ElMessage.success('保存成功')
window.history.pushState({}, '', `#/dvCanvas?dvId=${dvInfo.value.id}`)
eventBus.emit('hideArea-canvas-main')
nextTick(() => {
snapshotStore.resetStyleChangeTimes()
canvasSave(() => {
ElMessage.success('保存成功')
window.history.pushState({}, '', `#/dvCanvas?dvId=${dvInfo.value.id}`)
})
})
}
}

View File

@ -1,473 +0,0 @@
<script lang="ts" setup>
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { layerStoreWithOut } from '@/store/modules/data-visualization/layer'
import { storeToRefs } from 'pinia'
import { ElIcon, ElRow } from 'element-plus-secondary'
import Icon from '../icon-custom/src/Icon.vue'
import { nextTick, ref } from 'vue'
import draggable from 'vuedraggable'
import { lockStoreWithOut } from '@/store/modules/data-visualization/lock'
import ContextMenuAsideDetails from '@/components/data-visualization/canvas/ContextMenuAsideDetails.vue'
import ComposeShow from '@/components/data-visualization/canvas/ComposeShow.vue'
import { composeStoreWithOut } from '@/store/modules/data-visualization/compose'
const dropdownMore = ref(null)
const lockStore = lockStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const layerStore = layerStoreWithOut()
const composeStore = composeStoreWithOut()
const { areaData, isCtrlOrCmdDown, isShiftDown, laterIndex } = storeToRefs(composeStore)
const { componentData, curComponent, canvasViewInfo } = storeToRefs(dvMainStore)
const getComponent = index => {
return componentData.value[componentData.value.length - 1 - index]
}
const transformIndex = index => {
return componentData.value.length - 1 - index
}
const areaDataPush = component => {
if (component && !component.isLock && component.isShow) {
areaData.value.components.push(component)
}
}
// shift
// 1.laterIndexlaterIndex=0;
// 2.index curClickIndex;
// 3.laterIndex curClickIndex;
// 4.[laterIndex,curClickIndex] [curClickIndex,laterIndex]areaData.value.components();
const shiftDataPush = curClickIndex => {
const areaDataIdArray = areaData.value.components.map(com => com.id)
let indexBegin, indexEnd
const laterIndexTrans = laterIndex.value === null ? componentData.value.length : laterIndex.value
if (laterIndexTrans < curClickIndex) {
indexBegin = laterIndexTrans
indexEnd = curClickIndex
} else {
indexBegin = curClickIndex
indexEnd = laterIndexTrans
}
const shiftAreaComponents = componentData.value
.slice(indexBegin, indexEnd + 1)
.filter(
component => !areaDataIdArray.includes(component.id) && !component.isLock && component.isShow
)
areaData.value.components.push(...shiftAreaComponents)
dvMainStore.setCurComponent({ component: null, index: null })
}
const onClick = (e, index) => {
// laterIndex=0
if (!curComponent.value) {
composeStore.setLaterIndex(null)
}
// ctrl or command (ComposeShow)
if (isCtrlOrCmdDown.value && !areaData.value.components.includes(componentData.value[index])) {
areaDataPush(componentData.value[index])
if (curComponent.value && curComponent.value.id !== componentData.value[index].id) {
areaDataPush(curComponent.value)
}
dvMainStore.setCurComponent({ component: null, index: null })
e.stopPropagation()
composeStore.setLaterIndex(index)
return
}
//shift
if (isShiftDown.value) {
shiftDataPush(index)
return
}
//
areaData.value.components.splice(0, areaData.value.components.length)
setCurComponent(index)
composeStore.setLaterIndex(index)
}
const setCurComponent = index => {
dvMainStore.setCurComponent({ component: componentData.value[index], index })
}
let nameEdit = ref(false)
let editComponentId = ref('')
let inputName = ref('')
let nameInput = ref(null)
let curEditComponent = null
const editComponentName = item => {
curEditComponent = curComponent.value
editComponentId.value = `#component-label-${item.id}`
nameEdit.value = true
inputName.value = item.name
nextTick(() => {
nameInput.value.focus()
})
}
const closeEditComponentName = () => {
nameEdit.value = false
if (!inputName.value || !inputName.value.trim()) {
return
}
if (inputName.value.trim() === curEditComponent.name) {
return
}
curEditComponent.name = inputName.value
inputName.value = ''
curEditComponent = null
}
const lock = () => {
setTimeout(() => {
lockStore.lock()
snapshotStore.recordSnapshotCache('realTime-lock')
})
}
const unlock = () => {
setTimeout(() => {
lockStore.unlock()
snapshotStore.recordSnapshotCache('realTime-unlock')
})
}
const hideComponent = () => {
setTimeout(() => {
layerStore.hideComponent()
snapshotStore.recordSnapshotCache('realTime-hideComponent')
})
}
const showComponent = () => {
setTimeout(() => {
layerStore.showComponent()
snapshotStore.recordSnapshotCache()
})
}
const dragOnEnd = ({ oldIndex, newIndex }) => {
const source = componentData.value[newIndex]
const comLength = componentData.value.length
//
componentData.value.splice(newIndex, 1)
componentData.value.splice(oldIndex, 0, source)
const target = componentData.value[comLength - 1 - oldIndex]
//
componentData.value.splice(comLength - 1 - oldIndex, 1)
componentData.value.splice(comLength - 1 - newIndex, 0, target)
dvMainStore.setCurComponent({ component: target, index: transformIndex(comLength - oldIndex) })
}
const getIconName = item => {
if (item.component === 'UserView') {
const viewInfo = canvasViewInfo.value[item.id]
return `${viewInfo.type}-origin`
} else {
return item.icon
}
}
const menuAsideClose = (param, index) => {
const iconDom = document.getElementById('close-button')
if (iconDom) {
iconDom.click()
}
if (param.opt === 'rename') {
setTimeout(() => {
editComponentName(getComponent(index))
}, 200)
}
}
const handleContextMenu = e => {
e.preventDefault()
//
const x = e.clientX
const y = e.clientY
const customContextMenu = document.createElement('div')
customContextMenu.style.position = 'fixed'
customContextMenu.style.left = x + 'px'
customContextMenu.style.top = y + 'px'
//
document.body.appendChild(customContextMenu)
//
customContextMenu.addEventListener('click', () => {
//
//
document.body.removeChild(customContextMenu)
})
}
</script>
<template>
<!--为了保持图层视觉上的一致性 这里进行数组的倒序排列 相应的展示和移动按照倒序处理-->
<div class="real-time-component-list">
<button hidden="true" id="close-button"></button>
<el-row class="list-wrap">
<div class="list-container" @contextmenu="handleContextMenu">
<draggable
@end="dragOnEnd"
:list="componentData"
animation="100"
class="drag-list"
item-key="id"
>
<template #item="{ index }">
<div
:title="getComponent(index)?.name"
class="component-item"
:class="{
'container-item-not-show': !getComponent(index)?.isShow,
activated:
(curComponent && curComponent?.id === getComponent(index)?.id) ||
areaData.components.includes(getComponent(index))
}"
@click="onClick($event, transformIndex(index))"
>
<el-icon class="component-icon">
<Icon :name="getIconName(getComponent(index))"></Icon>
</el-icon>
<span
:id="`component-label-${getComponent(index)?.id}`"
class="component-label"
@dblclick="editComponentName(getComponent(index))"
>
{{ getComponent(index)?.name }}
</span>
<div
v-show="!nameEdit || (nameEdit && curComponent?.id !== getComponent(index)?.id)"
class="icon-container"
:class="{
'icon-container-lock': getComponent(index)?.isLock && getComponent(index)?.isShow,
'icon-container-show': !getComponent(index)?.isShow
}"
>
<el-icon
class="component-base component-icon-display"
v-show="!getComponent(index).isShow"
@click="showComponent"
>
<Icon name="dv-eye-close" class="opt-icon"></Icon>
</el-icon>
<el-icon
class="component-base"
v-show="getComponent(index)?.isShow"
@click="hideComponent"
>
<Icon name="dv-show" class="opt-icon"></Icon>
</el-icon>
<el-icon v-show="!getComponent(index)?.isLock" class="component-base" @click="lock">
<Icon class="opt-icon" name="dv-unlock"></Icon>
</el-icon>
<el-icon
class="component-base component-icon-display"
v-show="getComponent(index)?.isLock"
@click="unlock"
>
<Icon name="dv-lock" class="opt-icon"></Icon>
</el-icon>
<el-dropdown
ref="dropdownMore"
trigger="click"
placement="bottom-start"
effect="dark"
:hide-timeout="0"
>
<span :class="'dropdownMore-' + index" @click="onClick(transformIndex(index))">
<el-icon class="component-base">
<Icon name="dv-more" class="opt-icon"></Icon>
</el-icon>
</span>
<template #dropdown>
<context-menu-aside-details
:element="getComponent(index)"
@close="menuAsideClose($event, index)"
></context-menu-aside-details>
</template>
</el-dropdown>
</div>
<el-dropdown
class="compose-dropdown"
trigger="contextmenu"
placement="bottom-start"
effect="dark"
:hide-timeout="0"
>
<compose-show
:show-border="false"
:element-index="transformIndex(index)"
:element="getComponent(index)"
></compose-show>
<template #dropdown>
<context-menu-aside-details
:element="getComponent(index)"
@close="menuAsideClose($event, index)"
></context-menu-aside-details>
</template>
</el-dropdown>
</div>
</template>
</draggable>
<el-row style="width: 100%; height: 150px"></el-row>
</div>
</el-row>
<Teleport v-if="editComponentId && nameEdit" :to="editComponentId">
<input
@keydown.stop
@keyup.stop
ref="nameInput"
v-model="inputName"
@blur="closeEditComponentName"
/>
</Teleport>
</div>
</template>
<style lang="less" scoped>
.real-time-component-list {
white-space: nowrap;
.list-wrap {
max-height: calc(100% - @component-toolbar-height);
overflow-y: auto;
width: 100%;
.list-container {
width: 100%;
.component-item {
position: relative;
height: 30px;
width: 100%;
cursor: grab;
color: @dv-canvas-main-font-color;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 12px;
padding: 0 2px 0 22px;
user-select: none;
.component-icon {
color: #a6a6a6;
font-size: 14px;
}
.component-label {
color: #ebebeb;
}
> span.component-label {
font-size: 12px;
margin-left: 10px;
position: relative;
min-width: 65px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
input {
position: absolute;
left: 0;
width: 100%;
background-color: white;
outline: none;
border: none;
border-radius: 2px;
padding: 0 4px;
height: 100%;
}
}
&:active {
cursor: grabbing;
}
&:hover {
background-color: rgba(235, 235, 235, 0.1);
.icon-container {
.component-base {
opacity: 1;
}
width: 70px !important;
}
}
.icon-container {
.component-base {
opacity: 0;
}
width: 0px;
display: flex;
justify-content: flex-end;
align-items: center;
flex-grow: 1;
cursor: none;
i {
font-size: 16px;
cursor: pointer;
}
}
}
.activated {
background-color: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1)) !important;
:deep(.component-icon) {
color: var(--ed-color-primary);
}
:deep(.component-label) {
color: var(--ed-color-primary);
}
}
}
}
}
.real-time-component-list :deep(.ed-popper) {
background: #303133 !important;
}
.component-base {
cursor: pointer;
height: 22px !important;
width: 22px !important;
border-radius: 4px;
padding: 0 4px;
.opt-icon {
font-size: 14px;
}
&:hover {
background: rgba(235, 235, 235, 0.1);
}
&:active {
background: rgba(235, 235, 235, 0.1);
}
}
.component-icon-display {
opacity: 1 !important;
}
.icon-container-show {
width: 70px !important;
}
.icon-container-lock {
width: 45px !important;
}
.container-item-not-show {
color: #5f5f5f !important;
:deep(.component-icon) {
color: #5f5f5f !important;
}
:deep(.component-label) {
color: #5f5f5f !important;
}
}
</style>
<style lang="less">
.compose-dropdown {
position: initial !important;
}
</style>

View File

@ -1380,6 +1380,11 @@ const groupAreaClickChange = async () => {
groupAreaCom.style.top = areaData.value.style.top
groupAreaCom.style.width = areaData.value.style.width
groupAreaCom.style.height = areaData.value.style.height
dvMainStore.setClickComponentStatus(true)
dvMainStore.setCurComponent({
component: groupAreaCom,
index: dvMainStore.componentData.length - 1
})
} else if (groupAreaCom) {
groupAreaHis.forEach(ele => {
dvMainStore.deleteComponentById(ele.id)

View File

@ -37,7 +37,7 @@ const handleMouseDown = e => {
return
}
const index = areaData.value.components.findIndex(component => component === props.element)
if (index != -1) {
if (index != -1 && props.element.component !== 'GroupArea') {
areaData.value.components.splice(index, 1)
e.stopPropagation()
}

View File

@ -424,7 +424,6 @@ const handleInnerMouseDownOnShape = e => {
if (curComponent.value && curComponent.value.id !== element.value.id) {
areaDataPush(curComponent.value)
}
dvMainStore.setCurComponent({ component: null, index: null })
e.stopPropagation()
return
}

View File

@ -8,7 +8,6 @@ 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'
@ -82,7 +81,11 @@ const storeCacheProxy = byteArray => {
}
const pluginProxy = ref(null)
const invokeMethod = param => {
pluginProxy.value['invokeMethod'](param)
if (pluginProxy.value['invokeMethod']) {
pluginProxy.value['invokeMethod'](param)
} else {
pluginProxy.value[param.methodName](param.args)
}
}
onMounted(async () => {
@ -96,17 +99,16 @@ onMounted(async () => {
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 {
window['Vue'] = Vue
window['Axios'] = axios
window['Pinia'] = Pinia
window['vueRouter'] = vueRouter
window['MittAll'] = useEmitt().emitter.all
window['I18n'] = i18n
loadDistributed().then(async res => {
new Function(res.data)()
const xpack = await window['DEXPack'].mapping[attrs.jsname]
@ -125,7 +127,13 @@ defineExpose({
</script>
<template>
<component ref="pluginProxy" :is="plugin" v-loading="loading" v-bind="attrs"></component>
<component
:key="attrs.jsname"
ref="pluginProxy"
:is="plugin"
v-loading="loading"
v-bind="attrs"
></component>
</template>
<style lang="less" scoped></style>

View File

@ -371,14 +371,9 @@ const linkageFieldAdaptor = async data => {
if (state.curLinkageViewInfo.tableId === targetChartDetails.tableId) {
const curCheckAllAxisStr =
JSON.stringify(state.curLinkageViewInfo.xAxis) +
JSON.stringify(state.curLinkageViewInfo.xAxisExt) +
JSON.stringify(state.curLinkageViewInfo.yAxis) +
JSON.stringify(state.curLinkageViewInfo.yAxisExt)
JSON.stringify(state.curLinkageViewInfo.xAxisExt)
const targetCheckAllAxisStr =
JSON.stringify(targetChartDetails.xAxis) +
JSON.stringify(targetChartDetails.xAxisExt) +
JSON.stringify(targetChartDetails.yAxis) +
JSON.stringify(targetChartDetails.yAxisExt)
JSON.stringify(targetChartDetails.xAxis) + JSON.stringify(targetChartDetails.xAxisExt)
state.sourceLinkageInfo.targetViewFields.forEach(item => {
if (curCheckAllAxisStr.includes(item.id) && targetCheckAllAxisStr.includes(item.id)) {
addLinkageField(item.id, item.id)
@ -395,9 +390,7 @@ const sourceLinkageInfoFilter = computed(() => {
if (state.sourceLinkageInfo.targetViewFields) {
const curCheckAllAxisStr =
JSON.stringify(state.curLinkageViewInfo.xAxis) +
JSON.stringify(state.curLinkageViewInfo.xAxisExt) +
JSON.stringify(state.curLinkageViewInfo.yAxis) +
JSON.stringify(state.curLinkageViewInfo.yAxisExt)
JSON.stringify(state.curLinkageViewInfo.xAxisExt)
return state.sourceLinkageInfo.targetViewFields.filter(item =>
curCheckAllAxisStr.includes(item.id)
)

View File

@ -8,7 +8,12 @@
trigger="click"
>
<div class="export-button">
<el-select v-if="optType === 'enlarge'" v-model="pixel" class="pixel-select" size="small">
<el-select
v-if="optType === 'enlarge' && authShow"
v-model="pixel"
class="pixel-select"
size="small"
>
<el-option-group v-for="group in pixelOptions" :key="group.label" :label="group.label">
<el-option
v-for="item in group.options"
@ -21,7 +26,7 @@
<el-button
class="m-button"
v-if="optType === 'enlarge'"
v-if="optType === 'enlarge' && authShow"
link
icon="Download"
size="middle"
@ -31,7 +36,7 @@
</el-button>
<el-button
class="m-button"
v-if="optType === 'details'"
v-if="optType === 'details' && authShow"
link
icon="Download"
size="middle"
@ -40,7 +45,7 @@
>
导出Excel
</el-button>
<el-divider class="close-divider" direction="vertical" />
<el-divider class="close-divider" direction="vertical" v-if="authShow" />
</div>
<div
v-loading="downLoading"
@ -93,7 +98,7 @@ const viewContainer = ref(null)
const { t } = useI18n()
const optType = ref(null)
const chartComponentDetails = ref(null)
const { dvInfo } = storeToRefs(dvMainStore)
const { dvInfo, editMode } = storeToRefs(dvMainStore)
const exportLoading = ref(false)
const sourceViewType = ref()
const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = {
@ -111,10 +116,15 @@ const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = {
tableItemBgColor: '#FFFFFF',
tableFontColor: '#7C7E81',
enableTableCrossBG: false
},
tooltip: {
show: false
}
}
}
const authShow = computed(() => editMode.value === 'edit' || dvInfo.value.weight > 3)
const customExport = computed(() => {
if (downLoading.value) {
const bashStyle = pixel.value.split(' * ')
@ -204,8 +214,8 @@ const downloadViewDetails = () => {
exportLoading.value = true
exportExcelDownload(chart, () => {
openMessageLoading(exportData)
exportLoading.value = false
})
exportLoading.value = false
}
const exportData = () => {

View File

@ -16,8 +16,8 @@ export const STREAMMEDIALINKS = {
isLive: false,
cors: true, // 允许跨域
loop: true,
autoplay: false
// url: null // 网络动画视频
autoplay: false,
url: null // 网络动画视频
}
}
@ -26,21 +26,20 @@ export const VIDEO_LINKS_DE2 = {
videoType: 'web',
poster: null,
web: {
width: '100%', //播放器宽度
height: '100%', //播放器高度
color: '#409eff', //主题色
title: '', //视频名称
src: null, //视频源
muted: false, //静音
webFullScreen: false,
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
autoPlay: true, //自动播放
loop: false, //循环播放
mirror: false, //镜像画面
lightOff: false, //关灯模式
volume: 0.3, //默认音量大小
control: true, //是否显示控制
controlBtns: ['audioTrack', 'quality', 'volume', 'fullScreen'] //显示所有按钮,
autoplay: true, // 如果true,浏览器准备好时开始回放
muted: true, // 默认情况下将会消除任何音频
loop: true, // 导致视频一结束就重新开始
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据auto浏览器选择最佳行为,立即开始加载视频如果浏览器支持
language: 'zh-CN',
fluid: true, // 当true时Video.js player将拥有流体大小换句话说它将按比例缩放以适应其容器
notSupportedMessage: '此视频暂无法播放请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息
controls: true,
controlBar: {
timeDivider: true,
remainingTimeDisplay: false,
fullscreenToggle: true // 全屏按钮
}
},
rtmp: {
sources: [
@ -209,7 +208,7 @@ const list = [
name: '查询',
label: '查询',
propValue: '',
icon: 'other_text',
icon: 'icon_search',
innerType: 'VQuery',
isHang: false,
x: 1,

View File

@ -21,7 +21,7 @@
<script lang="ts" setup>
import flvjs from 'flv.js'
import '@/style/custom-theme.css'
import { onMounted, reactive, toRefs, getCurrentInstance } from 'vue'
import { onMounted, reactive, toRefs, getCurrentInstance, nextTick, onBeforeUnmount } from 'vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
@ -76,25 +76,29 @@ onMounted(() => {
})
const initOption = () => {
if (flvjs.isSupported() && state.pOption.url) {
destroyPlayer()
const video = currentInstance.proxy.$refs['player-' + element.value.id]
if (video) {
try {
state.flvPlayer = flvjs.createPlayer(state.pOption, {
enableWorker: false, // 线
enableStashBuffer: false, // IO
isLive: state.pOption.isLive,
lazyLoad: false
})
state.flvPlayer.attachMediaElement(video)
state.flvPlayer.load()
state.flvPlayer.play()
} catch (error) {
console.error('flvjs err ignore', error)
state.pOption = element.value.streamMediaLinks[element.value.streamMediaLinks.videoType]
delete state.pOption.segments
nextTick(() => {
if (flvjs.isSupported() && state.pOption.url) {
destroyPlayer()
const video = currentInstance.proxy.$refs['player-' + element.value.id]
if (video) {
try {
state.flvPlayer = flvjs.createPlayer(state.pOption, {
enableWorker: false, // 线
enableStashBuffer: false, // IO
isLive: state.pOption.isLive,
lazyLoad: false
})
state.flvPlayer.attachMediaElement(video)
state.flvPlayer.load()
state.flvPlayer.play()
} catch (error) {
console.error('flvjs err ignore', error)
}
}
}
}
})
}
const destroyPlayer = () => {
@ -105,6 +109,10 @@ const destroyPlayer = () => {
state.flvPlayer = null
}
}
onBeforeUnmount(() => {
destroyPlayer()
})
</script>
<style lang="less" scoped>

View File

@ -1,7 +1,7 @@
<template>
<el-row ref="mainPlayer">
<div v-if="element.videoLinks[element.videoLinks.videoType].src" class="player">
<video-play v-bind="playerOptions" />
<VideoPlayer :src="playerOptions.src" :options="playerOptions" />
</div>
<div v-else class="info-class">
<span>{{ t('visualization.video_add_tips') }}</span>
@ -10,8 +10,8 @@
</template>
<script setup lang="ts">
import { videoPlay } from 'vue3-video-play/lib/index' //
import 'vue3-video-play/dist/style.css' // css
import { VideoPlayer } from '@videojs-player/vue'
import 'video.js/dist/video-js.css'
import { computed, nextTick, reactive, toRefs, watch, onMounted, ref } from 'vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useI18n } from '@/hooks/web/useI18n'

View File

@ -5,7 +5,7 @@
<el-switch
:effect="themes"
@change="onChange"
v-model="state.linkInfoTemp[state.linkInfoTemp.videoType].autoPlay"
v-model="state.linkInfoTemp[state.linkInfoTemp.videoType].autoplay"
size="mini"
/>
</el-form-item>

View File

@ -81,14 +81,14 @@ const clearStyle = e => {
}
const handleBlur = e => {
element.value.propValue = e.target.innerHTML || '&nbsp;'
element.value.propValue = e.target.innerHTML || ''
const html = e.target.innerHTML
if (html !== '') {
element.value.propValue = e.target.innerHTML
} else {
element.value.propValue = ''
nextTick(function () {
element.value.propValue = '&nbsp;'
element.value.propValue = ''
})
}
canEdit.value = false

View File

@ -389,7 +389,7 @@ const autoStyle = computed(() => {
</script>
<template>
<div class="v-query-container" :style="autoStyle">
<div class="v-query-container" :style="autoStyle" @keydown.stop @keyup.stop>
<p v-if="customStyle.titleShow" class="title" :style="titleStyle">
{{ customStyle.title }}
</p>
@ -423,8 +423,8 @@ const autoStyle = computed(() => {
<el-tooltip effect="dark" :content="ele.name" placement="top">
{{ ele.name }}
</el-tooltip>
<span v-if="ele.required" class="required">*</span>
</div>
<span v-if="ele.required" class="required">*</span>
</div>
<div
class="label-wrapper-tooltip"
@ -573,13 +573,13 @@ const autoStyle = computed(() => {
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.required {
font-size: 14px;
color: #f54a45;
margin-left: 3px;
line-height: 22px;
}
.required {
font-size: 14px;
color: #f54a45;
margin-left: 3px;
line-height: 22px;
}
.label-wrapper-tooltip {
align-items: center;

View File

@ -501,7 +501,6 @@ const validate = () => {
const isMultiple = +ele.displayType === 7 || ele.multiple
ele.selectValue = isMultiple ? [] : undefined
ele.defaultValue = isMultiple ? [] : undefined
return false
}
if (ele.displayType === '1') {
@ -587,7 +586,7 @@ const validate = () => {
return false
}
if (ele.optionValueSource === 2 && !ele.valueSource?.length) {
if (ele.optionValueSource === 2 && !ele.valueSource?.filter(ele => !!ele).length) {
ElMessage.error('手工输入-选项值不能为空')
return true
}
@ -687,6 +686,19 @@ const setConditionOut = () => {
init(conditions.value[conditions.value.length - 1].id)
}
const setActiveSelectTab = (arr, id) => {
let activelist = 'dimensionList'
arr.some((ele, index) => {
if ((ele || []).some(itx => itx.id === id)) {
activelist = ['dimensionList', 'quotaList', 'parameterList'][index]
return true
}
return false
})
return activelist
}
const init = (queryId: string) => {
if (!datasetTree.value.length) {
initDataset()
@ -725,7 +737,12 @@ const init = (queryId: string) => {
fields.value = datasetFieldList.value
.map(ele => {
if (!datasetMap[ele.tableId]) return null
return { ...datasetMap[ele.tableId], componentId: ele.id }
const activeCom = datasetMap[ele.tableId].fields || {}
const activelist = setActiveSelectTab(
[activeCom.dimensionList, activeCom.quotaList, activeCom.parameterList],
curComponent.value.checkedFieldsMap[ele.id]
)
return { ...datasetMap[ele.tableId], componentId: ele.id, activelist }
})
.filter(ele => !!ele)
})
@ -813,6 +830,13 @@ const handleCondition = item => {
idMap.includes(ele)
)
if (!!fields.value?.length) {
fields.value.forEach(ele => {
const activeCom = ele.fields
ele.activelist = setActiveSelectTab(
[activeCom.dimensionList, activeCom.quotaList, activeCom.parameterList],
curComponent.value.checkedFieldsMap[ele.componentId]
)
})
handleCheckedFieldsChange(curComponent.value.checkedFields)
}
multipleChange(curComponent.value.multiple)
@ -1337,7 +1361,7 @@ defineExpose({
value="1"
/>
<el-option
:disabled="!['1', '7'].includes(curComponent.displayType)"
:disabled="!['1', '7'].includes(curComponent.displayType) || isTimeParameter"
label="时间范围"
value="7"
/>
@ -1544,11 +1568,7 @@ defineExpose({
</el-icon>
</template>
<el-option
v-for="ele in curComponent.dataset.fields.filter(
ele =>
ele.deType === +curComponent.displayType ||
([3, 4].includes(ele.deType) && +curComponent.displayType === 2)
)"
v-for="ele in curComponent.dataset.fields"
:key="ele.id"
:label="ele.name"
:value="ele.id"
@ -1603,6 +1623,13 @@ defineExpose({
<div :key="index" v-for="(_, index) in valueSource" class="select-item">
<el-input
maxlength="20"
v-if="curComponent.displayType === '2'"
@blur="weightlessness"
v-model.number="valueSource[index]"
></el-input>
<el-input
maxlength="20"
v-else
@blur="weightlessness"
v-model="valueSource[index]"
></el-input>

View File

@ -334,15 +334,19 @@ watch(
watch(
() => config.value.optionValueSource,
val => {
debounceOptions(val)
(valNew, newOld) => {
if ([valNew, newOld].includes(2)) {
selectValue.value = Array.isArray(selectValue.value) ? [] : undefined
config.value.selectValue = cloneDeep(selectValue.value)
config.value.defaultValue = cloneDeep(selectValue.value)
}
debounceOptions(valNew)
}
)
watch(
[() => config.value.checkedFields, () => config.value.checkedFieldsMap],
() => {
if (!props.isConfig) return
debounceOptions(config.value.optionValueSource)
},
{

View File

@ -23,7 +23,8 @@ import AiTips from '@/layout/components/AiTips.vue'
const appearanceStore = useAppearanceStoreWithOut()
const { push } = useRouter()
const route = useRoute()
import { useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache('localStorage')
const aiBaseUrl = ref(null)
const handleIconClick = () => {
if (route.path === '/workbranch/index') return
@ -69,7 +70,7 @@ const navigateBg = computed(() => appearanceStore.getNavigateBg)
const navigate = computed(() => appearanceStore.getNavigate)
const initAiBase = async () => {
const aiTipsCheck = localStorage.getItem('DE-AI-TIPS-CHECK')
const aiTipsCheck = wsCache.get('DE-AI-TIPS-CHECK')
if (aiTipsCheck === 'CHECKED') {
showOverlay.value = false
} else {
@ -84,7 +85,7 @@ const initAiBase = async () => {
}
const aiTipsConfirm = () => {
localStorage.setItem('DE-AI-TIPS-CHECK', 'CHECKED')
wsCache.set('DE-AI-TIPS-CHECK', 'CHECKED')
showOverlay.value = false
}
onMounted(() => {

View File

@ -1,11 +1,11 @@
<script lang="ts" setup>
import { ElMain } from 'element-plus-secondary'
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
<template>
<el-main>
<RouterView />
<RouterView :key="route.path" />
</el-main>
</template>
<style lang="less" scoped></style>

View File

@ -2232,6 +2232,7 @@ export default {
dsExecuteTime: '数据源检测频率',
frontTimeOut: '请求超时时间',
logLiveTime: '操作日志保留时间',
exportFileLiveTime: '后台导出文件保留时间',
platformOid: '第三方平台用户组织',
platformRid: '第三方平台用户角色',
pwdStrategy: '开启密码策略',

View File

@ -491,3 +491,8 @@ strong {
color: var(--deSuccess, #34c724);
}
}
.vjs-big-play-button {
top: calc(50% - 22px) !important;
left: calc(50% - 40px) !important;
}

View File

@ -101,7 +101,7 @@ let isShiftDown = false
// 全局监听按键操作并执行相应命令
export function listenGlobalKeyDown() {
window.onkeydown = e => {
if (!isInEditor.value || editMode.value === 'preview' || checkDialog()) return
if (editMode.value === 'preview' || checkDialog()) return
const { keyCode } = e
if (positionMoveKey[keyCode] && curComponent.value) {
positionMoveKey[keyCode](keyCode)

View File

@ -0,0 +1,17 @@
function parseUrl(url) {
const [pathname, params] = url.split('?')
const [_, path] = pathname.split('#/')
return {
path,
query: params
.split('&')
.map(ele => ele.split('='))
.reduce((pre, next) => {
const [key, value] = next
pre[key] = value
return pre
}, {})
}
}
export { parseUrl }

View File

@ -125,8 +125,14 @@ export const customAttrTrans = {
'wordSizeRange',
'wordSpacing'
],
label: ['fontSize'],
tooltip: ['fontSize'],
label: {
fontSize: '',
seriesLabelFormatter: ['fontSize']
},
tooltip: {
fontSize: '',
seriesTooltipFormatter: ['fontSize']
},
indicator: ['fontSize', 'suffixFontSize'],
indicatorName: ['fontSize', 'nameValueSpacing']
}
@ -296,15 +302,25 @@ export function recursionTransObj(template, infoObj, scale, terminal) {
// 如果是数组 进行赋值计算
if (template[templateKey] instanceof Array) {
template[templateKey].forEach(templateProp => {
if (infoObj[templateKey] && infoObj[templateKey][templateProp]) {
if (
infoObj[templateKey] &&
(infoObj[templateKey][templateProp] || infoObj[templateKey].length)
) {
// 移动端特殊属性值设置
if (terminal === 'mobile' && mobileSpecialProps[templateProp] !== undefined) {
infoObj[templateKey][templateProp] = mobileSpecialProps[templateProp]
} else {
infoObj[templateKey][templateProp] = getScaleValue(
infoObj[templateKey][templateProp],
scale
)
// 数组依次设置
if (infoObj[templateKey] instanceof Array) {
infoObj[templateKey].forEach(v => {
v[templateProp] = getScaleValue(v[templateProp], scale)
})
} else {
infoObj[templateKey][templateProp] = getScaleValue(
infoObj[templateKey][templateProp],
scale
)
}
}
}
})

View File

@ -152,7 +152,7 @@ export function initCanvasDataPrepare(dvId, busiFlag, callBack) {
})
}
export function initCanvasData(dvId, busiFlag, callBack) {
export async function initCanvasData(dvId, busiFlag, callBack) {
initCanvasDataPrepare(
dvId,
busiFlag,
@ -174,6 +174,48 @@ export function initCanvasData(dvId, busiFlag, callBack) {
)
}
export async function backCanvasData(dvId, busiFlag, callBack) {
initCanvasDataPrepare(dvId, busiFlag, function ({ canvasDataResult, canvasStyleResult }) {
const componentDataCopy = canvasDataResult.filter(ele => !!ele.inMobile)
const componentDataId = componentDataCopy.map(ele => ele.id)
componentData.value.forEach(ele => {
ele.inMobile = componentDataId.includes(ele.id)
if (ele.inMobile) {
const { mx, my, mSizeX, mSizeY } = componentDataCopy.find(itx => itx.id === ele.id)
ele.mx = mx
ele.my = my
ele.mSizeX = mSizeX
ele.mSizeY = mSizeY
if (ele.component === 'DeTabs') {
ele.propValue.forEach(tabItem => {
tabItem.componentData.forEach(tabComponent => {
tabComponent.mx = tabComponent.mx
tabComponent.my = tabComponent.my
tabComponent.mSizeX = tabComponent.mSizeX
tabComponent.mSizeY = tabComponent.mSizeY
})
})
}
}
})
dvMainStore.setComponentData(componentData.value)
const canvasStyleDataCopy = cloneDeep(canvasStyleData.value)
if (!canvasStyleDataCopy.mobileSetting) {
canvasStyleDataCopy.mobileSetting = {
backgroundColorSelect: false,
background: '',
color: '#ffffff',
backgroundImageEnable: false,
customSetting: false
}
} else {
canvasStyleDataCopy.mobileSetting = canvasStyleResult.mobileSetting
}
dvMainStore.setCanvasStyle(canvasStyleDataCopy)
callBack()
})
}
export function initCanvasDataMobile(dvId, busiFlag, callBack) {
initCanvasDataPrepare(
dvId,

View File

@ -64,10 +64,15 @@ const changeThreshold = () => {
const changeSplitThreshold = (threshold: string) => {
// check input
if (threshold) {
const regex = /^(\d+)(,\d+)*$/
if (!regex.test(threshold)) {
ElMessage.error(t('chart.gauge_threshold_format_error'))
return
}
const arr = threshold.split(',')
for (let i = 0; i < arr.length; i++) {
const ele = arr[i]
if (parseFloat(ele).toString() === 'NaN' || parseFloat(ele) <= 0 || parseFloat(ele) >= 100) {
if (parseFloat(ele) <= 0 || parseFloat(ele) >= 100) {
ElMessage.error(t('chart.gauge_threshold_format_error'))
return
}

View File

@ -634,7 +634,7 @@ onMounted(() => {
:effect="themes"
v-model="state.basicStyleForm.showZoom"
:predefine="predefineColors"
@change="changeBasicStyle('zoomShow')"
@change="changeBasicStyle('showZoom')"
>
{{ t('chart.show_zoom') }}
</el-checkbox>

View File

@ -101,6 +101,7 @@ onMounted(() => {
:themes="themes"
type="left"
:chart-type="chart.type"
:layout="chart.customAttr.basicStyle.layout"
@on-change-y-axis-form="changeAxisStyle"
/>
</el-tab-pane>
@ -120,6 +121,7 @@ onMounted(() => {
:themes="themes"
type="right"
:chart-type="chart.type"
:layout="chart.customAttr.basicStyle.layout"
@on-change-y-axis-form="changeSubAxisStyle"
/>
</el-tab-pane>

View File

@ -14,6 +14,7 @@ const props = withDefaults(
propertyInner?: Array<string>
type?: 'left' | 'right'
chartType?: string
layout?: string
}>(),
{
themes: 'dark',
@ -75,6 +76,14 @@ const init = () => {
const showProperty = prop => props.propertyInner?.includes(prop)
const isBidirectionalBar = computed(() => {
return props.chartType === 'bidirectional-bar'
})
const isVerticalLayout = computed(() => {
return props.layout === 'vertical'
})
onMounted(() => {
init()
})
@ -99,9 +108,19 @@ onMounted(() => {
size="small"
@change="changeAxisStyle('position')"
>
<div v-if="chartType === 'bidirectional-bar'">
<el-radio :effect="props.themes" label="right">{{ t('chart.text_pos_top') }}</el-radio>
<el-radio :effect="props.themes" label="left">{{ t('chart.text_pos_bottom') }}</el-radio>
<div v-if="isBidirectionalBar">
<div v-if="isVerticalLayout">
<el-radio :effect="props.themes" label="left">{{ t('chart.text_pos_left') }}</el-radio>
<el-radio :effect="props.themes" label="right">{{
t('chart.text_pos_right')
}}</el-radio>
</div>
<div v-else>
<el-radio :effect="props.themes" label="right">{{ t('chart.text_pos_top') }}</el-radio>
<el-radio :effect="props.themes" label="left">{{
t('chart.text_pos_bottom')
}}</el-radio>
</div>
</div>
<div v-else>
<el-radio :effect="props.themes" label="left">{{ t('chart.text_pos_left') }}</el-radio>
@ -309,7 +328,7 @@ onMounted(() => {
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px">
<el-input-number
:disabled="!state.axisForm.splitLine.show"
style="width: 74px"
style="width: 70px"
:effect="props.themes"
v-model="state.axisForm.splitLine.lineStyle.width"
:min="1"

View File

@ -220,6 +220,48 @@ const isBarRangeTime = computed<boolean>(() => {
}
return false
})
const showPositionH = computed(() => {
if (showProperty('hPosition')) {
if (props.chart.type !== 'bidirectional-bar') {
return true
}
return props.chart.customAttr.basicStyle.layout === 'horizontal'
}
return false
})
const showPositionV = computed(() => {
if (showProperty('vPosition')) {
if (props.chart.type !== 'bidirectional-bar') {
return true
}
return props.chart.customAttr.basicStyle.layout === 'vertical'
}
return false
})
watch(
() => props.chart.customAttr.basicStyle.layout,
() => {
const layout = props.chart.customAttr.basicStyle.layout
if (layout === 'horizontal') {
if (state?.labelForm?.position === 'top') {
state.labelForm.position = 'right'
}
if (state?.labelForm?.position === 'bottom') {
state.labelForm.position = 'left'
}
}
if (layout === 'vertical') {
if (state?.labelForm?.position === 'left') {
state.labelForm.position = 'bottom'
}
if (state?.labelForm?.position === 'right') {
state.labelForm.position = 'top'
}
}
changeLabelAttr('position')
},
{ deep: true }
)
onMounted(() => {
init()
@ -298,7 +340,7 @@ onMounted(() => {
</el-select>
</el-form-item>
<el-form-item
v-if="showProperty('hPosition')"
v-if="showPositionH"
:label="t('chart.label_position')"
class="form-item"
:class="'form-item-' + themes"
@ -319,7 +361,7 @@ onMounted(() => {
</el-select>
</el-form-item>
<el-form-item
v-if="showProperty('vPosition')"
v-if="showPositionV"
:label="t('chart.label_position')"
class="form-item"
:class="'form-item-' + themes"

View File

@ -100,6 +100,14 @@ const init = () => {
const showProperty = prop => props.propertyInner?.includes(prop)
const isBidirectionalBar = computed(() => {
return props.chart.type === 'bidirectional-bar'
})
const isHorizontalLayout = computed(() => {
return props.chart.customAttr.basicStyle.layout === 'horizontal'
})
onMounted(() => {
init()
})
@ -124,8 +132,10 @@ onMounted(() => {
size="small"
@change="changeAxisStyle('position')"
>
<div v-if="chart.type === 'bidirectional-bar'">
<el-radio :effect="props.themes" label="top">{{ t('chart.text_pos_left') }}</el-radio>
<div v-if="isBidirectionalBar">
<el-radio :effect="props.themes" label="top">{{
isHorizontalLayout ? t('chart.text_pos_left') : t('chart.text_pos_top')
}}</el-radio>
<el-radio :effect="props.themes" label="bottom">{{
t('chart.text_pos_center')
}}</el-radio>
@ -335,7 +345,7 @@ onMounted(() => {
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px">
<el-input-number
:disabled="!state.axisForm.splitLine.show"
style="width: 74px"
style="width: 70px"
:effect="props.themes"
v-model="state.axisForm.splitLine.lineStyle.width"
:min="1"

View File

@ -317,7 +317,7 @@ onMounted(() => {
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px">
<el-input-number
:disabled="!state.axisForm.splitLine.show"
style="width: 74px"
style="width: 70px"
:effect="props.themes"
v-model="state.axisForm.splitLine.lineStyle.width"
:min="1"

View File

@ -1461,7 +1461,7 @@ export const BASE_VIEW_CONFIG = {
type: 'bar',
render: 'antv',
resultCount: 1000,
resultMode: 'all',
resultMode: 'custom',
refreshViewEnable: false,
refreshTime: 5,
refreshUnit: 'minute',

View File

@ -87,7 +87,7 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
],
'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'],
'function-cfg': ['emptyDataStrategy'],
'label-selector': ['hPosition', 'seriesLabelFormatter'],
'label-selector': ['hPosition', 'vPosition', 'seriesLabelFormatter'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show']
}
@ -339,8 +339,9 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
valueExt: undefined
}
}
const layoutHorizontal = options.layout === 'horizontal'
// 处理横轴标题方向不对
if (yAxis && yAxis['title']) {
if (yAxis && yAxis['title'] && layoutHorizontal) {
yAxis['title'].autoRotate = false
}
const yAxisTmp = parseJson(chart.customStyle).yAxis
@ -400,6 +401,7 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
...chart.customAttr.label,
position: 'right'
}
chart.customAttr.basicStyle.layout = 'horizontal'
return chart
}
@ -413,6 +415,7 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
return pre
}, {})
let customAttr: DeepPartial<ChartAttr>
const layoutHorizontal = options.layout === 'horizontal'
if (chart.customAttr) {
customAttr = parseJson(chart.customAttr)
// label
@ -448,7 +451,33 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
} else {
res = valueFormatter(value, l.labelFormatter)
}
return res
const group = new G2PlotChartView.engine.Group({})
const isValue = param['series-field-key'] === 'value'
const textAlign = isValue && layoutHorizontal ? 'end' : 'start'
const isMiddle = label.position === 'middle'
group.addShape({
type: 'text',
attrs: {
x:
isValue && layoutHorizontal && !isMiddle
? -6
: !isValue && layoutHorizontal && !isMiddle
? 6
: 0,
y:
isValue && !layoutHorizontal && !isMiddle
? -8
: !isValue && !layoutHorizontal && !isMiddle
? 8
: 0,
text: res,
textAlign: label.position === 'middle' ? 'start' : textAlign,
textBaseline: 'top',
fontSize: labelCfg.fontSize,
fill: labelCfg.color
}
})
return group
}
}
} else {
@ -456,6 +485,14 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
}
}
}
if (!layoutHorizontal) {
if (label.position === 'left') {
label.position = 'bottom'
}
if (label.position === 'right') {
label.position = 'top'
}
}
return { ...options, label }
}

View File

@ -86,24 +86,25 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
drawChart(drawOptions: G2PlotDrawOptions<G2Area>): G2Area {
const { chart, container, action } = drawOptions
if (chart?.data) {
// data
const data = cloneDeep(chart.data.data)
const initOptions: AreaOptions = {
...this.baseOptions,
data,
appendPadding: getPadding(chart)
}
// options
const options = this.setupOptions(chart, initOptions)
// 开始渲染
const newChart = new G2Area(container, options)
newChart.on('point:click', action)
return newChart
if (!chart.data.data?.length) {
return
}
// data
const data = cloneDeep(chart.data.data)
const initOptions: AreaOptions = {
...this.baseOptions,
data,
appendPadding: getPadding(chart)
}
// options
const options = this.setupOptions(chart, initOptions)
// 开始渲染
const newChart = new G2Area(container, options)
newChart.on('point:click', action)
return newChart
}
protected configLabel(chart: Chart, options: AreaOptions): AreaOptions {

View File

@ -260,57 +260,63 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
...options
}
tempOption.yAxis = {}
if (!yAxis) {
//左右轴都要隐藏
tempOption.yAxis = {
value: false,
valueExt: false
}
tempOption.yAxis.value = false
} else {
tempOption.yAxis = {
value: undefined,
valueExt: undefined
tempOption.yAxis.value = undefined
yAxis.position = 'left'
const yAxisTmp = parseJson(chart.customStyle).yAxis
if (yAxis.label) {
yAxis.label.style.textAlign = 'end'
yAxis.label.formatter = value => {
return valueFormatter(value, yAxisTmp.axisLabelFormatter)
}
}
const axisValue = yAxisTmp.axisValue
if (!axisValue?.auto) {
tempOption.yAxis.value = {
...yAxis,
min: axisValue.min,
max: axisValue.max,
minLimit: axisValue.min,
maxLimit: axisValue.max,
tickCount: axisValue.splitCount
}
} else {
tempOption.yAxis.value = yAxis
}
}
const yAxisTmp = parseJson(chart.customStyle).yAxis
if (yAxis.label) {
yAxis.label.formatter = value => {
return valueFormatter(value, yAxisTmp.axisLabelFormatter)
}
}
const axisValue = yAxisTmp.axisValue
if (!axisValue?.auto) {
tempOption.yAxis.value = {
...yAxis,
min: axisValue.min,
max: axisValue.max,
minLimit: axisValue.min,
maxLimit: axisValue.max,
tickCount: axisValue.splitCount
}
if (!yAxisExt) {
//左右轴都要隐藏
tempOption.yAxis.valueExt = false
} else {
tempOption.yAxis.value = yAxis
}
tempOption.yAxis.valueExt = undefined
yAxisExt.position = 'right'
const yAxisExtTmp = parseJson(chart.customStyle).yAxisExt
if (yAxisExt.label) {
yAxisExt.label.formatter = value => {
return valueFormatter(value, yAxisExtTmp.axisLabelFormatter)
const yAxisExtTmp = parseJson(chart.customStyle).yAxisExt
if (yAxisExt.label) {
yAxisExt.label.style.textAlign = 'start'
yAxisExt.label.formatter = value => {
return valueFormatter(value, yAxisExtTmp.axisLabelFormatter)
}
}
}
const axisExtValue = yAxisExtTmp.axisValue
if (!axisExtValue?.auto) {
tempOption.yAxis.valueExt = {
...yAxisExt,
min: axisExtValue.min,
max: axisExtValue.max,
minLimit: axisExtValue.min,
maxLimit: axisExtValue.max,
tickCount: axisExtValue.splitCount
const axisExtValue = yAxisExtTmp.axisValue
if (!axisExtValue?.auto) {
tempOption.yAxis.valueExt = {
...yAxisExt,
min: axisExtValue.min,
max: axisExtValue.max,
minLimit: axisExtValue.min,
maxLimit: axisExtValue.max,
tickCount: axisExtValue.splitCount
}
} else {
tempOption.yAxis.valueExt = yAxisExt
}
} else {
tempOption.yAxis.valueExt = yAxisExt
}
return tempOption

View File

@ -24,9 +24,10 @@ import {
import substitute from '@antv/util/esm/substitute'
import type { Plot as L7Plot, PlotOptions } from '@antv/l7plot/dist/esm'
import { Zoom } from '@antv/l7'
import { createL7Icon } from '@antv/l7-component/es/utils/icon'
import { DOM } from '@antv/l7-utils'
import { Scene } from '@antv/l7-scene'
import { type IZoomControlOption } from '@antv/l7-component'
import { PositionType } from '@antv/l7-core'
export function getPadding(chart: Chart): number[] {
if (chart.drill) {
@ -828,7 +829,8 @@ export function configL7Label(chart: Chart): false | LabelOptions {
visible: label.show,
style: {
fill: label.color,
fontSize: label.fontSize
fontSize: label.fontSize,
fontWeight: 'bold'
}
}
}
@ -970,7 +972,13 @@ export function configL7Legend(chart: Chart): LegendOptions | false {
}
}
}
class CustomZoom extends Zoom {
const ZOOM_IN_BTN =
'<svg t="1717484429999" fill="${fill}" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9332" width="14px" height="14px"><path d="M47.653 439.326c-24.501 0-44.368 19.881-44.368 44.4v56.548c0 24.492 19.862 44.4 44.368 44.4h928.694c24.501 0 44.368-19.881 44.368-44.4v-56.548c0-24.497-19.862-44.4-44.368-44.4H47.653z" p-id="9333"></path><path d="M586.326 47.653c0-24.501-19.881-44.368-44.4-44.368h-56.548c-24.492 0-44.4 19.862-44.4 44.368v928.694c0 24.501 19.881 44.368 44.4 44.368h56.548c24.497 0 44.4-19.862 44.4-44.368V47.653z" p-id="9334"></path></svg>'
const RESET_BTN =
'<svg t="1717487786436" fill="${fill}" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18361" width="14px" height="14px"><path d="M127.594667 503.274667a383.573333 383.573333 0 0 1 112.426666-263.04 380.864 380.864 0 0 1 122.24-82.474667 382.421333 382.421333 0 0 1 149.632-30.165333c51.946667 0 102.250667 10.176 149.504 30.165333a381.610667 381.610667 0 0 1 122.133334 82.474667 385.152 385.152 0 0 1 31.082666 35.093333l-67.285333 52.501333a8.96 8.96 0 0 0 3.349333 15.765334l196.352 48.042666a8.96 8.96 0 0 0 11.050667-8.597333l0.896-202.154667c0-7.466667-8.597333-11.733333-14.421333-7.04l-63.018667 49.28C795.605333 113.173333 661.973333 42.666667 511.786667 42.666667 255.786667 42.666667 47.488 247.829333 42.666667 502.826667a8.96 8.96 0 0 0 8.96 9.173333h67.029333c4.906667 0 8.832-3.925333 8.96-8.725333z m844.8 8.725333h-67.050667a8.917333 8.917333 0 0 0-8.96 8.704 381.76 381.76 0 0 1-30.037333 140.8 382.336 382.336 0 0 1-82.346667 122.24 382.656 382.656 0 0 1-271.893333 112.64 382.421333 382.421333 0 0 1-271.765334-112.64 385.152 385.152 0 0 1-31.061333-35.072l67.264-52.522667a8.96 8.96 0 0 0-3.349333-15.765333l-196.330667-48.042667a8.96 8.96 0 0 0-11.050667 8.597334l-0.789333 202.261333c0 7.488 8.597333 11.733333 14.421333 7.04l63.018667-49.28C228.394667 910.826667 362.026667 981.333333 512.213333 981.333333 768.341333 981.333333 976.512 776.042667 981.333333 521.173333a8.96 8.96 0 0 0-8.96-9.173333z" p-id="18362"></path></svg>'
const ZOOM_OUT_BTN =
'<svg t="1717486240292" fill="${fill}" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13641" width="14px" height="14px"><path d="M935 423.3H89C40.2 423.3 0.3 463.2 0.3 512c0 48.8 39.9 88.7 88.7 88.7h846c48.8 0 88.7-39.9 88.7-88.7 0-48.8-39.9-88.7-88.7-88.7z" p-id="13642"></path></svg>'
export class CustomZoom extends Zoom {
resetButtonGroup(container) {
DOM.clearChildren(container)
this['zoomInButton'] = this['createButton'](
@ -980,9 +988,8 @@ class CustomZoom extends Zoom {
container,
this.zoomIn
)
const resetBtnIconText = createL7Icon('l7-icon-round')
this['zoomResetButton'] = this['createButton'](
resetBtnIconText,
this.controlOption['resetText'],
'Reset',
'l7-button-control',
container,
@ -1008,15 +1015,7 @@ class CustomZoom extends Zoom {
container,
this.zoomOut
)
const { buttonColor, buttonBackground } = this.controlOption as any
if (buttonColor) {
const elements = [
this.controlOption.zoomInText,
this.controlOption.zoomOutText,
resetBtnIconText
] as HTMLElement[]
setStyle(elements, 'fill', buttonColor)
}
const { buttonBackground } = this.controlOption as any
const elements = [this['zoomResetButton'], this['zoomInButton'], this['zoomOutButton']]
if (buttonBackground) {
setStyle(elements, 'background', buttonBackground)
@ -1024,6 +1023,28 @@ class CustomZoom extends Zoom {
setStyle(elements, 'border-bottom', 'none')
this['updateDisabled']()
}
public getDefault(option: Partial<IZoomControlOption>) {
const { buttonColor } = option as any
let zoomInText = ZOOM_IN_BTN
let zoomOutText = ZOOM_OUT_BTN
let resetText = RESET_BTN
if (buttonColor) {
zoomInText = zoomInText.replace('${fill}', buttonColor)
zoomOutText = zoomOutText.replace('${fill}', buttonColor)
resetText = resetText.replace('${fill}', buttonColor)
}
return {
...option,
position: PositionType.BOTTOMRIGHT,
name: 'zoom',
zoomInText,
zoomInTitle: 'Zoom in',
zoomOutText,
zoomOutTitle: 'Zoom out',
resetText,
showZoom: false
} as IZoomControlOption
}
}
export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions> | Scene) {
const { basicStyle } = parseJson(chart.customAttr)
@ -1034,7 +1055,7 @@ export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions> | Scene) {
return
}
const plotScene = plot instanceof Scene ? plot : plot.scene
plotScene.once('loaded', () => {
plot.once('loaded', () => {
const zoomOptions = {
initZoom: plotScene.getZoom(),
center: plotScene.getCenter(),

View File

@ -325,6 +325,18 @@ export function getStyle(chart: Chart): Style {
p[n.fieldId] = n
return p
}, {}) || {}
// 下钻字段使用入口字段的宽度
if (chart.drill) {
const { xAxis } = parseJson(chart)
const curDrillField = chart.drillFields[chart.drillFilters.length]
const drillEnterFieldIndex = xAxis.findIndex(
item => item.id === chart.drillFilters[0].fieldId
)
const drillEnterField = xAxis[drillEnterFieldIndex]
fieldMap[curDrillField.dataeaseName] = {
width: fieldMap[drillEnterField.dataeaseName]?.width
}
}
style.colCfg.width = node => {
const width = node.spreadsheet.container.cfg.el.offsetWidth
if (!basicStyle.tableFieldWidth?.length) {

View File

@ -475,7 +475,6 @@ export const exportExcelDownload = (chart, callBack?) => {
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
callBack('success')
} else {
callBack && callBack(res)
}

View File

@ -261,6 +261,29 @@ const trackClick = trackAction => {
if (state.pointParam.data.dimensionList.length > 1) {
checkName = state.pointParam.data.dimensionList[0].id
}
//
let jumpName = state.pointParam.data.name
if (state.pointParam.data.dimensionList.length > 1) {
const fieldIds = []
//
if (curView.drill) {
const curFiled = curView.drillFields[curView.drillFilters.length]
fieldIds.push(curFiled.id)
}
chartData.value?.fields.forEach(field => {
if (!fieldIds.includes(field.id)) {
fieldIds.push(field.id)
}
})
for (let i = 0; i < fieldIds.length; i++) {
const id = fieldIds[i]
const sourceInfo = view.value.id + '#' + id
if (nowPanelJumpInfo.value[sourceInfo]) {
jumpName = id
break
}
}
}
let quotaList = state.pointParam.data.quotaList
if (curView.type === 'bar-range') {
quotaList = state.pointParam.data.dimensionList
@ -276,7 +299,7 @@ const trackClick = trackAction => {
}
const jumpParam = {
option: 'jump',
name: checkName,
name: jumpName,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: quotaList

View File

@ -3,6 +3,7 @@ import { useI18n } from '@/hooks/web/useI18n'
import ChartComponentG2Plot from './components/ChartComponentG2Plot.vue'
import DeIndicator from '@/custom-component/indicator/DeIndicator.vue'
import { useAppStoreWithOut } from '@/store/modules/app'
import router from '@/router'
import { useEmbedded } from '@/store/modules/embedded'
import { XpackComponent } from '@/components/plugin'
import {
@ -29,6 +30,7 @@ import DrillPath from '@/views/chart/components/views/components/DrillPath.vue'
import { ElIcon, ElInput, ElMessage } from 'element-plus-secondary'
import { useFilter } from '@/hooks/web/useFilter'
import { useCache } from '@/hooks/web/useCache'
import { parseUrl } from '@/utils/ParseUrl'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { cloneDeep } from 'lodash-es'
@ -50,6 +52,7 @@ const dvMainStore = dvMainStoreWithOut()
let innerRefreshTimer = null
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const emit = defineEmits(['onPointClick'])
@ -353,9 +356,6 @@ const windowsJump = (url, jumpType) => {
try {
const newWindow = window.open(url, jumpType)
initOpenHandler(newWindow)
if (jumpType === '_self') {
location.reload()
}
} catch (e) {
ElMessage.error(t('visualization.url_check_error') + ':' + url)
}
@ -388,6 +388,7 @@ const jumpClick = param => {
param.sourceFieldId = dimension.id
let embeddedBaseUrl = ''
const divSelf = isDataEaseBi.value && jumpInfo.jumpType === '_self'
const iframeSelf = isIframe.value && jumpInfo.jumpType === '_self'
if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl
}
@ -410,12 +411,21 @@ const jumpClick = param => {
const url = `${embeddedBaseUrl}#/preview?dvId=${
jumpInfo.targetDvId
}&jumpInfoParam=${encodeURIComponent(Base64.encode(JSON.stringify(param)))}`
if (isIframe.value || isDataEaseBi.value) {
embeddedStore.clearState()
}
if (divSelf) {
embeddedStore.setDvId(jumpInfo.targetDvId)
embeddedStore.setJumpInfoParam(encodeURIComponent(Base64.encode(JSON.stringify(param))))
divEmbedded('Preview')
return
}
if (iframeSelf) {
router.push(parseUrl(url))
return
}
windowsJump(url, jumpInfo.jumpType)
}
} else {
@ -425,11 +435,16 @@ const jumpClick = param => {
const colList = [...param.dimensionList, ...param.quotaList]
let url = setIdValueTrans('id', 'value', jumpInfo.content, colList)
url = checkAddHttp(url)
if (isIframe.value || isDataEaseBi.value) {
embeddedStore.clearState()
}
if (divSelf) {
embeddedStore.setOuterUrl(url)
divEmbedded('Iframe')
return
}
windowsJump(url, jumpInfo.jumpType)
}
} else {

View File

@ -16,6 +16,7 @@ import { interactiveStoreWithOut } from '@/store/modules/interactive'
const interactiveStore = interactiveStoreWithOut()
import router from '@/router'
import { useI18n } from '@/hooks/web/useI18n'
import { parseUrl } from '@/utils/ParseUrl'
import _ from 'lodash'
import DeResourceCreateOptV2 from '@/views/common/DeResourceCreateOptV2.vue'
import { useCache } from '@/hooks/web/useCache'
@ -106,6 +107,7 @@ const dvSvgType = computed(() =>
)
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const resourceTypeList = computed(() => {
const list = [
@ -300,6 +302,11 @@ const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => {
return
}
if (isIframe.value) {
router.push(parseUrl(baseUrl))
return
}
const newWindow = window.open(baseUrl, '_blank')
initOpenHandler(newWindow)
})
@ -331,6 +338,11 @@ const addOperation = (
)
return
}
if (isIframe.value) {
router.push(parseUrl(data?.id ? baseUrl + `&pid=${data.id}` : baseUrl))
return
}
if (data?.id) {
newWindow = window.open(baseUrl + `&pid=${data.id}`, '_blank')
} else {
@ -368,6 +380,11 @@ const resourceEdit = resourceId => {
)
return
}
if (isIframe.value) {
router.push(parseUrl(baseUrl + resourceId))
return
}
const newWindow = window.open(baseUrl + resourceId, '_blank')
initOpenHandler(newWindow)
}
@ -397,6 +414,13 @@ const resourceCreateFinish = templateData => {
)
return
}
if (isIframe.value) {
router.push(
parseUrl(state.templateCreatePid ? baseUrl + `&pid=${state.templateCreatePid}` : baseUrl)
)
return
}
if (state.templateCreatePid) {
newWindow = window.open(baseUrl + `&pid=${state.templateCreatePid}`, '_blank')
} else {

View File

@ -8,6 +8,7 @@ import { useEmbedded } from '@/store/modules/embedded'
import { canvasSave } from '@/utils/canvasUtils'
import { useEmitt } from '@/hooks/web/useEmitt'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { backCanvasData } from '@/utils/canvasUtils'
import { storeToRefs } from 'pinia'
import { debounce } from 'lodash-es'
import mobileHeader from '@/assets/img/mobile-header.png'
@ -175,6 +176,7 @@ onBeforeUnmount(() => {
})
const addToMobile = com => {
if (mobileLoading.value) return
com.inMobile = true
changeTimes.value++
mobileStatusChange('addToMobile', JSON.parse(JSON.stringify(unref(com))))
@ -194,7 +196,10 @@ const handleBack = () => {
showClose: false
}).then(() => {
setTimeout(() => {
mobileStatusChange('mobilePatch', undefined)
backCanvasData(dvInfo.value.id, 'dashboard', () => {
changeTimes.value = 0
emits('pcMode')
})
}, 100)
})
}
@ -274,7 +279,11 @@ const save = () => {
:scale="80"
/>
</div>
<div class="mobile-com-mask" @click="addToMobile(item)"></div>
<div class="mobile-com-mask" @click="addToMobile(item)">
<span v-show="item.component === 'DeStreamMedia'" style="color: #909399"
>IOS可能无法显示</span
>
</div>
<div class="pc-select-to-mobile" @click="addToMobile(item)" v-if="!mobileLoading"></div>
</div>
</template>
@ -506,6 +515,9 @@ const save = () => {
left: 0;
z-index: 10;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.pc-select-to-mobile {

View File

@ -73,7 +73,6 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
ElMessage.error(t('visualization.outer_param_decode_error'))
}
}
initCanvasData(
dvId,
dvType,

View File

@ -79,7 +79,7 @@ const dvLayout = ref(null)
const canvasCenterRef = ref(null)
const state = reactive({
datasetTree: [],
scaleHistory: 100,
scaleHistory: null,
canvasId: 'canvas-main',
canvasInitStatus: false,
sourcePid: null,
@ -204,10 +204,10 @@ const doUseCache = flag => {
}
}
const initLocalCanvasData = () => {
const initLocalCanvasData = async () => {
const { opt, sourcePid, resourceId } = state
const busiFlg = opt === 'copy' ? 'dataV-copy' : 'dataV'
initCanvasData(resourceId, busiFlg, function () {
await initCanvasData(resourceId, busiFlg, function () {
state.canvasInitStatus = true
// afterInit
nextTick(() => {
@ -238,7 +238,9 @@ watch(
() => editMode.value,
val => {
if (val === 'edit') {
canvasStyleData.value.scale = state.scaleHistory
if (state.scaleHistory) {
canvasStyleData.value.scale = state.scaleHistory
}
initScroll()
} else {
previewScaleChange()
@ -288,7 +290,7 @@ onMounted(async () => {
if (canvasCache) {
canvasCacheOutRef.value?.dialogInit({ canvasType: 'dataV', resourceId: dvId })
} else {
initLocalCanvasData()
await initLocalCanvasData()
}
} else if (opt && opt === 'create') {
state.canvasInitStatus = false
@ -303,7 +305,7 @@ onMounted(async () => {
}
let deTemplateData
if (createType === 'template') {
const templateParamsApply = JSON.parse(decodeURIComponent(Base64.decode(templateParams + '')))
const templateParamsApply = JSON.parse(Base64.decode(decodeURIComponent(templateParams + '')))
await decompressionPre(templateParamsApply, result => {
deTemplateData = result
})

View File

@ -117,8 +117,12 @@ const handleLogin = () => {
if (!xpackLoadFail.value && xpackInvalidPwd.value?.invokeMethod) {
const param = {
methodName: 'init',
args: () => {
duringLogin.value = false
args: r => {
duringLogin.value = !!r
if (r) {
const queryRedirectPath = getCurLocation()
router.push({ path: queryRedirectPath })
}
}
}
xpackInvalidPwd?.value.invokeMethod(param)

View File

@ -0,0 +1,9 @@
<script lang="ts" setup>
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
</script>
<template>
<EmptyBackground
img-type="noneWhite"
description="仅嵌入式版和企业版支持iframe方式内嵌公共链接"
/>
</template>

View File

@ -1,5 +1,6 @@
import request from '@/config/axios'
import { useCache } from '@/hooks/web/useCache'
import { isInIframe } from '@/utils/utils'
const { wsCache } = useCache()
export interface ProxyInfo {
resourceId: string
@ -7,6 +8,7 @@ export interface ProxyInfo {
exp?: boolean
pwdValid?: boolean
type: string
inIframeError: boolean
}
class ShareProxy {
uuid: string
@ -28,7 +30,8 @@ class ShareProxy {
}
const uuid = this.uuid
const url = '/share/proxyInfo'
const param = { uuid, ciphertext: null }
const inIframe = isInIframe()
const param = { uuid, ciphertext: null, inIframe }
const ciphertext = wsCache.get(`link-${uuid}`)
if (ciphertext) {
param['ciphertext'] = ciphertext

View File

@ -1,6 +1,7 @@
<template>
<div class="link-container" v-loading="loading">
<LinkError v-if="!loading && !linkExist" />
<IframeError v-if="!loading && iframeError" />
<LinkError v-else-if="!loading && !linkExist" />
<Exp v-else-if="!loading && linkExp" />
<PwdTips v-else-if="!loading && !pwdValid" />
<PreviewCanvas
@ -18,13 +19,21 @@ import { ProxyInfo, shareProxy } from './ShareProxy'
import Exp from './exp.vue'
import LinkError from './error.vue'
import PwdTips from './pwd.vue'
import IframeError from './IframeError.vue'
const pcanvas = ref(null)
const iframeError = ref(true)
const linkExist = ref(false)
const loading = ref(true)
const linkExp = ref(false)
const pwdValid = ref(false)
onMounted(async () => {
const proxyInfo = (await shareProxy.loadProxy()) as ProxyInfo
if (proxyInfo?.inIframeError) {
loading.value = false
iframeError.value = true
return
}
iframeError.value = false
if (!proxyInfo?.resourceId) {
loading.value = false
return

View File

@ -1,23 +1,33 @@
<template>
<div class="mobile-link-container" v-loading="loading">
<div
:class="curType === 'dashboard' ? 'mobile-link-container' : 'link-container'"
v-loading="loading"
>
<LinkError v-if="!loading && !linkExist" />
<Exp v-else-if="!loading && linkExp" />
<PwdTips v-else-if="!loading && !pwdValid" />
<de-preview
ref="dashboardPreview"
v-else-if="state.canvasStylePreview && dataInitState"
v-else-if="state.canvasStylePreview && dataInitState && curType === 'dashboard'"
:component-data="state.canvasDataPreview"
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
:cur-gap="state.curPreviewGap"
></de-preview>
<PreviewCanvas
v-else-if="curType !== 'dashboard'"
:class="{ 'hidden-link': loading }"
ref="pcanvas"
public-link-status="true"
/>
</div>
</template>
<script lang="ts" setup>
import { onMounted, nextTick, ref, reactive } from 'vue'
import { initCanvasDataMobile } from '@/utils/canvasUtils'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import PreviewCanvas from '@/views/data-visualization/PreviewCanvas.vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import { ProxyInfo, shareProxy } from './ShareProxy'
import Exp from './exp.vue'
@ -71,9 +81,14 @@ const loadCanvasData = (dvId, weight?) => {
}
)
}
const curType = ref('')
const pcanvas = ref(null)
onMounted(async () => {
dvMainStore.setMobileInPc(true)
const proxyInfo = (await shareProxy.loadProxy()) as ProxyInfo
curType.value = proxyInfo.type || 'dashboard'
dvMainStore.setMobileInPc(curType.value === 'dashboard')
if (!proxyInfo?.resourceId) {
loading.value = false
return
@ -82,9 +97,17 @@ onMounted(async () => {
linkExp.value = !!proxyInfo.exp
pwdValid.value = !!proxyInfo.pwdValid
nextTick(() => {
loadCanvasData(proxyInfo.resourceId, proxyInfo.type)
dvMainStore.setPublicLinkStatus(true)
loading.value = false
if (curType.value === 'dashboard') {
loadCanvasData(proxyInfo.resourceId, proxyInfo.type)
dvMainStore.setPublicLinkStatus(true)
loading.value = false
} else {
const method = pcanvas?.value?.loadCanvasDataAsync
if (method) {
method(proxyInfo.resourceId, proxyInfo.type, null)
}
loading.value = false
}
})
})
</script>
@ -96,3 +119,15 @@ onMounted(async () => {
position: relative;
}
</style>
<style lang="less" scoped>
.link-container {
position: absolute !important;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.hidden-link {
display: none !important;
}
</style>

View File

@ -125,6 +125,15 @@ const edit = (list, orgOptions, roleOptions) => {
}
]
}
if (pkey === 'basic.exportFileLiveTime') {
rule[pkey.split('.')[1]] = [
{
required: true,
message: t('common.require'),
trigger: ['blur', 'change']
}
]
}
item['label'] = `setting_${pkey}`
item['pkey'] = pkey.split('.')[1]
let pval = item.pval
@ -301,6 +310,19 @@ defineExpose({
/>
</el-select>
</div>
<div v-else-if="item.pkey === 'exportFileLiveTime'">
<el-input-number
v-model="state.form[item.pkey]"
autocomplete="off"
step-strictly
class="text-left edit-all-line"
:min="1"
:max="4000"
:placeholder="t('common.inputText')"
controls-position="right"
type="number"
/>
</div>
<v-else />
</el-form-item>
</el-form>

View File

@ -169,7 +169,9 @@ import { nextTick, reactive, watch, onMounted, ref, computed } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { ElMessage } from 'element-plus-secondary'
import { useCache } from '@/hooks/web/useCache'
import router from '@/router'
import MarketPreviewV2 from '@/views/template-market/component/MarketPreviewV2.vue'
import { parseUrl } from '@/utils/ParseUrl'
import { imgUrlTrans } from '@/utils/imgUtils'
import CategoryTemplateV2 from '@/views/template-market/component/CategoryTemplateV2.vue'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
@ -192,6 +194,7 @@ const close = () => {
const title = computed(() => (state.curPosition === 'branch' ? '模板中心' : '使用模板新建'))
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const state = reactive({
initReady: true,
curPosition: 'branch',
@ -283,8 +286,8 @@ const state = reactive({
const createAuth = computed(() => {
const authMap = interactiveStore.getData
return {
PANEL: authMap['0'].menuAuth && authMap['0'].anyManage,
SCREEN: authMap['1'].menuAuth && authMap['1'].anyManage
PANEL: authMap['0']?.menuAuth && authMap['0']?.anyManage,
SCREEN: authMap['1']?.menuAuth && authMap['1']?.anyManage
}
})
@ -454,6 +457,11 @@ const apply = template => {
)
return
}
if (isIframe.value) {
router.push(parseUrl(state.pid ? baseUrl + `&pid=${state.pid}` : baseUrl))
return
}
if (state.pid) {
newWindow = window.open(baseUrl + `&pid=${state.pid}`, '_blank')
} else {

View File

@ -65,8 +65,6 @@ const handleClick = tab => {
} else {
description.value = t('data_export.no_task')
}
tableData.value = []
drawerLoading.value = true
exportTasks(activeName.value)
.then(res => {
@ -348,7 +346,7 @@ defineExpose({
<el-tab-pane v-for="tab in tabList" :key="tab.name" :label="tab.label" :name="tab.name" />
</el-tabs>
<el-button
v-show="activeName === 'SUCCESS' && multipleSelection.length === 0"
v-if="activeName === 'SUCCESS' && multipleSelection.length === 0"
secondary
@click="downLoadAll"
>
@ -358,15 +356,15 @@ defineExpose({
{{ $t('data_export.download_all') }}
</el-button>
<el-button
v-show="activeName === 'SUCCESS' && multipleSelection.length !== 0"
v-if="activeName === 'SUCCESS' && multipleSelection.length !== 0"
secondary
@click="downLoadAll"
><template #icon> <Icon name="de-delete"></Icon> </template>{{ $t('data_export.download') }}
</el-button>
<el-button v-show="multipleSelection.length === 0" secondary @click="delAll"
<el-button v-if="multipleSelection.length === 0" secondary @click="delAll"
><template #icon> <Icon name="de-delete"></Icon> </template>{{ $t('data_export.del_all') }}
</el-button>
<el-button v-show="multipleSelection.length !== 0" secondary @click="delAll"
<el-button v-if="multipleSelection.length !== 0" secondary @click="delAll"
><template #icon> <Icon name="de-delete"></Icon> </template>{{ $t('commons.delete') }}
</el-button>
<div class="table-container" :class="!tableData.length && 'hidden-bottom'">

View File

@ -1327,10 +1327,10 @@ const getDsIconName = data => {
</el-icon>
<span class="label">自定义SQL</span>
</div>
<template v-for="ele in state.tableData" :key="ele.name">
<template v-for="ele in state.tableData" :key="ele.tableName">
<div
class="list-item_primary"
:title="ele.name"
:title="ele.tableName"
@dragstart="$event => dragstart($event, ele)"
@dragend="maskShow = false"
:draggable="true"

View File

@ -35,7 +35,9 @@ import type { TabPaneName } from 'element-plus-secondary'
import { timestampFormatDate } from './form/util'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import { XpackComponent } from '@/components/plugin'
import { useCache } from '@/hooks/web/useCache'
const interactiveStore = interactiveStoreWithOut()
const { wsCache } = useCache()
interface Field {
fieldShortName: string
name: string
@ -86,6 +88,7 @@ let originResourceTree = []
const sortTypeChange = sortType => {
state.datasetTree = treeSort(originResourceTree, sortType)
state.curSortType = sortType
wsCache.set('TreeSort-dataset', state.curSortType)
}
const resourceCreate = (pid, name) => {
@ -211,10 +214,12 @@ const getData = () => {
rootManage.value = nodeData[0]['weight'] >= 7
state.datasetTree = nodeData[0]['children'] || []
originResourceTree = cloneDeep(unref(state.datasetTree))
sortTypeChange(state.curSortType)
return
}
state.datasetTree = nodeData
originResourceTree = cloneDeep(unref(state.datasetTree))
sortTypeChange(state.curSortType)
})
.finally(() => {
dtLoading.value = false
@ -252,6 +257,7 @@ const dfsDatasetTree = (ds, id) => {
onBeforeMount(() => {
nodeInfo.id = (route.params.id as string) || ''
loadInit()
getData()
})
@ -471,6 +477,13 @@ const sortList = [
}
]
const loadInit = () => {
const historyTreeSort = wsCache.get('TreeSort-dataset')
if (historyTreeSort) {
state.curSortType = historyTreeSort
}
}
const sortTypeTip = computed(() => {
return sortList.find(ele => ele.value === state.curSortType).name
})

View File

@ -458,7 +458,7 @@ defineExpose({
</div>
<el-form-item :label="$t('datasource.query_timeout')" prop="apiQueryTimeout">
<el-input v-model="apiItem.apiQueryTimeout" autocomplete="off" type="number" :min="0">
<template v-slot:append>{{ $t('panel.second') }}</template>
<template v-slot:append>{{ $t('chart.second') }}</template>
</el-input>
</el-form-item>
<div class="title-form_primary request-info">

View File

@ -73,8 +73,8 @@ const selectDsType = (type: string) => {
currentDsType.value = type
activeStep.value = 1
activeApiStep.value = 1
detail.value.initForm(type)
nextTick(() => {
detail.value.initForm(type)
if (!dsTree.value) return
currentTypeList.value
.map(ele => ele.dbList)
@ -493,7 +493,9 @@ const init = (nodeInfo: Form | Param, id?: string, res?: object) => {
excel.value.appendReplaceExcel(res)
})
}
detail.value.clearForm()
nextTick(() => {
detail.value.clearForm()
})
})
}
}
@ -653,7 +655,7 @@ defineExpose({
:form="form"
:editDs="editDs"
:active-step="activeApiStep"
v-show="activeStep !== 0 && currentDsType && currentDsType !== 'Excel'"
v-if="activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible"
></editor-detail>
<template v-if="activeStep !== 0 && currentDsType == 'Excel'">
<excel-detail :editDs="editDs" ref="excel" :param="form2"></excel-detail>

View File

@ -8,7 +8,7 @@ import ArrowSide from '@/views/common/DeResourceArrow.vue'
import { HandleMore } from '@/components/handle-more'
import { Icon } from '@/components/icon-custom'
import { fieldType } from '@/utils/attr'
import { listSyncRecord, uploadFile } from '@/api/datasource'
import { getHidePwById, listSyncRecord, uploadFile } from '@/api/datasource'
import CreatDsGroup from './form/CreatDsGroup.vue'
import type { Tree } from '../dataset/form/CreatDsGroup.vue'
import { previewData, getById } from '@/api/datasource'
@ -42,6 +42,7 @@ import { useMoveLine } from '@/hooks/web/useMoveLine'
import { cloneDeep } from 'lodash-es'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import treeSort from '@/utils/treeSortUtils'
import { useCache } from '@/hooks/web/useCache'
const route = useRoute()
const interactiveStore = interactiveStoreWithOut()
interface Field {
@ -51,7 +52,7 @@ interface Field {
originName: string
deType: number
}
const { wsCache } = useCache()
const { t } = useI18n()
const router = useRouter()
const appStore = useAppStoreWithOut()
@ -165,6 +166,7 @@ let originResourceTree = []
const sortTypeChange = sortType => {
state.datasourceTree = treeSort(originResourceTree, sortType)
state.curSortType = sortType
wsCache.set('TreeSort-datasource', state.curSortType)
}
const handleSizeChange = pageSize => {
state.paginationConfig.currentPage = 1
@ -391,10 +393,12 @@ const listDs = () => {
rootManage.value = nodeData[0]['weight'] >= 7
state.datasourceTree = nodeData[0]['children'] || []
originResourceTree = cloneDeep(unref(state.datasourceTree))
sortTypeChange(state.curSortType)
return
}
originResourceTree = cloneDeep(unref(state.datasourceTree))
state.datasourceTree = nodeData
sortTypeChange(state.curSortType)
})
.finally(() => {
mounted.value = true
@ -455,7 +459,7 @@ const handleNodeClick = data => {
dsListTree.value.setCurrentKey(null)
return
}
return getById(data.id).then(res => {
return getHidePwById(data.id).then(res => {
let {
name,
createBy,
@ -571,7 +575,48 @@ const editDatasource = (editType?: number) => {
if (nodeInfo.type === 'Excel') {
nodeInfo.editType = editType
}
datasourceEditor.value.init(nodeInfo)
return getById(nodeInfo.id).then(res => {
let {
name,
createBy,
id,
createTime,
creator,
type,
pid,
configuration,
syncSetting,
apiConfigurationStr,
fileName,
size,
description,
lastSyncTime
} = res.data
if (configuration) {
configuration = JSON.parse(Base64.decode(configuration))
}
if (apiConfigurationStr) {
apiConfigurationStr = JSON.parse(Base64.decode(apiConfigurationStr))
}
let datasource = reactive<Node>(cloneDeep(defaultInfo))
Object.assign(datasource, {
name,
pid,
description,
fileName,
size,
createTime,
creator,
createBy,
id,
type,
configuration,
syncSetting,
apiConfiguration: apiConfigurationStr,
lastSyncTime
})
datasourceEditor.value.init(datasource)
})
}
const handleEdit = async data => {
@ -693,8 +738,17 @@ const defaultProps = {
children: 'children',
label: 'name'
}
const loadInit = () => {
const historyTreeSort = wsCache.get('TreeSort-datasource')
if (historyTreeSort) {
state.curSortType = historyTreeSort
}
}
onMounted(() => {
nodeInfo.id = (route.params.id as string) || ''
loadInit()
listDs()
const { opt } = router.currentRoute.value.query
if (opt && opt === 'create') {

View File

@ -22,7 +22,7 @@ export default {
}
]
function isLoginStatus() {
return wsCache.get('user.token')
return wsCache.get('user.token') && wsCache.get('user.uid')
}
function connection() {

@ -1 +1 @@
Subproject commit 448c319f8e519be5f4b47663258136bcb6fc3faa
Subproject commit a12eea394d8d5edf2941988819b08676e512a11e

View File

@ -138,6 +138,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes -X POST -H "X-API-KEY: $DE_APISIX
]
],
"upstream_id": "1",
"enable_websocket": true,
"status": 1
}'
@ -173,4 +174,4 @@ curl http://127.0.0.1:9180/apisix/admin/routes -X POST -H "X-API-KEY: $DE_APISIX
],
"service_id": "10",
"status": 1
}'
}'

View File

@ -67,7 +67,7 @@ function _check_apisix_init() {
function _prepare_apisix() {
if [[ -z $DE_APISIX_KEY ]];then
need_init_apisix=true
DE_APISIX_KEY=$(uuidgen | sed 's/-//g')
DE_APISIX_KEY=$(head -c 32 /dev/urandom | base64)
export DE_APISIX_KEY=$DE_APISIX_KEY
cd $DE_RUNNING_BASE
env | grep DE_ >.env

View File

@ -53,6 +53,7 @@ function check_and_prepare_env_params() {
fi
set -a
source ${CURRENT_DIR}/install.conf
if [[ -d $DE_BASE ]] && [[ -f $DE_BASE/dataease2.0/.env ]]; then
source $DE_BASE/dataease2.0/.env
INSTALL_TYPE='upgrade'
@ -64,7 +65,6 @@ function check_and_prepare_env_params() {
fi
log_content "升级安装"
else
source ${CURRENT_DIR}/install.conf
INSTALL_TYPE='install'
mkdir -p ${DE_BASE}
log_content "全新安装"

View File

@ -43,4 +43,12 @@ public interface ChartViewApi {
@Operation(summary = "查询仪表板下视图项")
@GetMapping("/viewOption/{resourceId}")
List<ViewSelectorVO> viewOption(@PathVariable("resourceId") Long resourceId);
@Operation(summary = "视图复制字段")
@PostMapping("copyField/{id}/{chartId}")
void copyField(@PathVariable Long id, @PathVariable Long chartId);
@Operation(summary = "视图删除字段")
@PostMapping("deleteField/{id}")
void deleteField(@PathVariable Long id);
}

View File

@ -71,6 +71,11 @@ public interface DatasourceApi {
@GetMapping("/get/{datasourceId}")
DatasourceDTO get(@PathVariable("datasourceId") Long datasourceId) throws DEException;
@DePermit({"#p0+':read'"})
@GetMapping("/hidePw/{datasourceId}")
DatasourceDTO hidePw(@PathVariable("datasourceId") Long datasourceId) throws DEException;
@PostMapping("/getTableField")
List<TableField> getTableField(@RequestBody Map<String, String> req) throws DEException;

View File

@ -30,4 +30,6 @@ public class ExportTaskDTO {
private String exportMachineName;
private String exportFromName;
private String orgName;
}

View File

@ -19,4 +19,5 @@ public class XpackShareProxyRequest implements Serializable {
private String uuid;
@Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED)
private String ciphertext;
private boolean inIframe;
}

View File

@ -31,4 +31,5 @@ public class XpackShareProxyVO implements Serializable {
private boolean pwdValid;
@Schema(description = "类型")
private String type;
private boolean inIframeError = true;
}

View File

@ -10,7 +10,9 @@ public class SysVariableDto {
private Long id;
private String type;
private String name;
@JsonSerialize(using = ToStringSerializer.class)
private Long min;
@JsonSerialize(using = ToStringSerializer.class)
private Long max;
private String startTime;
private String endTime;

View File

@ -8,7 +8,6 @@ import java.util.List;
@Data
public class SysVariableValueItem {
@JsonSerialize(using = ToStringSerializer.class)
private String variableValue;
@JsonSerialize(using = ToStringSerializer.class)
private String variableValue2;

View File

@ -4,7 +4,7 @@ import java.util.Arrays;
public enum MessageEnum {
INNER(0), EMAIL(1), WECOM(2), DINGTALK(3), LARK(4), LARKSUITE(5);
INNER(0), EMAIL(1), WECOM(2), DINGTALK(3), LARK(4), LARKSUITE(5), LARKGROUP(6);
private Integer flag;
public Integer getFlag() {

View File

@ -65,6 +65,7 @@ public class WhitelistUtils {
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content/")
|| StringUtils.startsWithAny(requestURI, "/geo/")
|| StringUtils.startsWithAny(requestURI, "/websocket")
|| StringUtils.startsWithAny(requestURI, "/map/");
|| StringUtils.startsWithAny(requestURI, "/map/")
|| StringUtils.startsWithAny(requestURI, "/communicate/down/");
}
}