feat(X-Pack): 新增LDAP认证

This commit is contained in:
fit2cloud-chenyw 2024-07-18 14:56:09 +08:00
commit 0ce521ef0f
62 changed files with 1590 additions and 276 deletions

View File

@ -131,6 +131,21 @@
<artifactId>flexmark-all</artifactId>
<version>${flexmark.version}</version>
</dependency>
<dependency>
<groupId>io.dataease</groupId>
<artifactId>xpack-permissions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dataease</groupId>
<artifactId>xpack-sync</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dataease</groupId>
<artifactId>xpack-base</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -90,7 +90,7 @@ public class DatasetDataManage {
}
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
datasourceSchemaDTO.setSchemaAlias(String.format(SQLConstants.SCHEMA, datasourceSchemaDTO.getId()));
Provider provider = ProviderFactory.getDefaultProvider();
Provider provider = ProviderFactory.getProvider(coreDatasource.getType());
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(Map.of(datasourceSchemaDTO.getId(), datasourceSchemaDTO));
@ -149,9 +149,9 @@ public class DatasetDataManage {
dto.setChecked(defaultStatus);
dto.setType(ele.getType());
int deType = FieldUtils.transType2DeType(ele.getType());
dto.setDeExtractType(deType);
dto.setDeType(deType);
dto.setGroupType(FieldUtils.transDeType2DQ(deType));
dto.setDeExtractType(ObjectUtils.isEmpty(ele.getDeExtractType()) ? deType : ele.getDeExtractType());
dto.setDeType(ObjectUtils.isEmpty(ele.getDeType()) ? deType : ele.getDeType());
dto.setGroupType(FieldUtils.transDeType2DQ(dto.getDeType()));
dto.setExtField(0);
dto.setDescription(StringUtils.isNotEmpty(ele.getName()) ? ele.getName() : null);
return dto;

View File

@ -80,6 +80,7 @@ public class DatasetGroupManage {
private Lock lock = new ReentrantLock();
@Transactional
public DatasetGroupInfoDTO save(DatasetGroupInfoDTO datasetGroupInfoDTO, boolean rename) throws Exception {
lock.lock();
try {

View File

@ -1,13 +1,5 @@
package io.dataease.dataset.manage;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.view.dto.ChartExtFilterDTO;
import io.dataease.extensions.view.dto.ChartExtRequest;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.api.dataset.union.*;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.commons.utils.SqlparserUtils;
@ -22,6 +14,16 @@ import io.dataease.datasource.manage.EngineManage;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.DsTypeDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.PluginDatasourceType;
import io.dataease.extensions.view.dto.ChartExtFilterDTO;
import io.dataease.extensions.view.dto.ChartExtRequest;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.i18n.Translator;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.BeanUtils;
@ -135,7 +137,7 @@ public class DatasetSQLManage {
prefix = "`";
suffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
@ -188,7 +190,7 @@ public class DatasetSQLManage {
tablePrefix = "`";
tableSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
tablePrefix = datasourceType.getPrefix();
tableSuffix = datasourceType.getSuffix();
}
@ -214,7 +216,7 @@ public class DatasetSQLManage {
pPrefix = "`";
pSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, parentDs.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, parentDs.getDatasourceId());
pPrefix = datasourceType.getPrefix();
pSuffix = datasourceType.getSuffix();
}
@ -226,7 +228,7 @@ public class DatasetSQLManage {
cPrefix = "`";
cSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
cPrefix = datasourceType.getPrefix();
cSuffix = datasourceType.getSuffix();
}
@ -305,7 +307,7 @@ public class DatasetSQLManage {
prefix = "`";
suffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
@ -348,7 +350,7 @@ public class DatasetSQLManage {
}
}
private DatasourceConfiguration.DatasourceType getDatasourceType(Map<Long, DatasourceSchemaDTO> dsMap, Long datasourceId) {
private DsTypeDTO getDatasourceType(Map<Long, DatasourceSchemaDTO> dsMap, Long datasourceId) {
DatasourceSchemaDTO datasourceSchemaDTO = dsMap.get(datasourceId);
String type;
if (datasourceSchemaDTO == null) {
@ -360,7 +362,17 @@ public class DatasetSQLManage {
} else {
type = datasourceSchemaDTO.getType();
}
return DatasourceConfiguration.DatasourceType.valueOf(type);
if (Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList().contains(type)) {
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(type);
DsTypeDTO dto = new DsTypeDTO();
BeanUtils.copyBean(dto, datasourceType);
return dto;
} else {
PluginDatasourceType.DatasourceType datasourceType = PluginDatasourceType.DatasourceType.valueOf(type);
DsTypeDTO dto = new DsTypeDTO();
BeanUtils.copyBean(dto, datasourceType);
return dto;
}
}
public String subPrefixSuffixChar(String str) {

View File

@ -1,8 +1,8 @@
package io.dataease.dataset.utils;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.DsTypeDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.utils.Md5Utils;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.commons.lang3.StringUtils;
@ -39,7 +39,7 @@ public class TableUtils {
return "C_" + Md5Utils.md5(fieldName);
}
public static String getTableAndAlias(SQLObj sqlObj, DatasourceConfiguration.DatasourceType datasourceType, boolean isCross) {
public static String getTableAndAlias(SQLObj sqlObj, DsTypeDTO datasourceType, boolean isCross) {
String schema = "";
String prefix = "";
String suffix = "";

View File

@ -11,6 +11,8 @@ import io.dataease.datasource.type.*;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.*;
import io.dataease.extensions.datasource.provider.DriverShim;
import io.dataease.extensions.datasource.provider.ExtendedJdbcClassLoader;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.i18n.Translator;
@ -24,7 +26,6 @@ import org.apache.calcite.adapter.jdbc.JdbcSchema;
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlDialect;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@ -442,6 +443,7 @@ public class CalciteProvider extends Provider {
return list;
}
@Override
public void hidePW(DatasourceDTO datasourceDTO) {
DatasourceConfiguration configuration = null;
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(datasourceDTO.getType());
@ -1047,20 +1049,6 @@ public class CalciteProvider extends Provider {
}
}
public Statement getStatement(Connection connection, int queryTimeout) {
if (connection == null) {
DEException.throwException("Failed to get connection!");
}
Statement stat = null;
try {
stat = connection.createStatement();
stat.setQueryTimeout(queryTimeout);
} catch (Exception e) {
DEException.throwException(e.getMessage());
}
return stat;
}
protected boolean isDefaultClassLoader(String customDriver) {
return StringUtils.isEmpty(customDriver) || customDriver.equalsIgnoreCase("default");
}

View File

@ -36,6 +36,7 @@ import io.dataease.extensions.datasource.dto.*;
import io.dataease.extensions.datasource.factory.ProviderFactory;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.PluginDatasourceType;
import io.dataease.i18n.Translator;
import io.dataease.job.schedule.CheckDsStatusJob;
import io.dataease.job.schedule.ScheduleManager;
@ -575,7 +576,8 @@ public class DatasourceServer implements DatasourceApi {
}
} else {
if (hidePw) {
calciteProvider.hidePW(datasourceDTO);
Provider provider = ProviderFactory.getProvider(datasourceDTO.getType());
provider.hidePW(datasourceDTO);
}
}
@ -851,7 +853,8 @@ public class DatasourceServer implements DatasourceApi {
}
private void preCheckDs(DatasourceDTO datasource) throws DEException {
if (!datasourceTypes().stream().map(DatasourceConfiguration.DatasourceType::getType).toList().contains(datasource.getType())) {
if (!datasourceTypes().stream().map(DatasourceConfiguration.DatasourceType::getType).toList().contains(datasource.getType())
&& !Arrays.stream(PluginDatasourceType.DatasourceType.values()).map(PluginDatasourceType.DatasourceType::getType).toList().contains(datasource.getType())) {
DEException.throwException("Datasource type not supported.");
}
}
@ -867,7 +870,8 @@ public class DatasourceServer implements DatasourceApi {
if (coreDatasource.getType().equals("API")) {
status = ApiUtils.checkStatus(datasourceRequest);
} else {
status = calciteProvider.checkStatus(datasourceRequest);
Provider provider = ProviderFactory.getProvider(coreDatasource.getType());
status = provider.checkStatus(datasourceRequest);
}
coreDatasource.setStatus(status);
} catch (Exception e) {

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
* </p>
*
* @author fit2cloud
* @since 2024-07-16
* @since 2024-07-17
*/
@TableName("visualization_template")
public class VisualizationTemplate implements Serializable {

View File

@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
* </p>
*
* @author fit2cloud
* @since 2024-07-16
* @since 2024-07-17
*/
@Mapper
public interface VisualizationTemplateMapper extends BaseMapper<VisualizationTemplate> {

View File

@ -2,8 +2,16 @@ package io.dataease.visualization.server;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.gson.Gson;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.visualization.request.VisualizationAppExportRequest;
import io.dataease.api.visualization.vo.*;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTable;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper;
import io.dataease.dataset.manage.DatasetDataManage;
import io.dataease.dataset.manage.DatasetGroupManage;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
@ -37,6 +45,7 @@ import io.dataease.template.dao.auto.mapper.VisualizationTemplateMapper;
import io.dataease.template.dao.ext.ExtVisualizationTemplateMapper;
import io.dataease.template.manage.TemplateCenterManage;
import io.dataease.utils.*;
import io.dataease.visualization.dao.auto.entity.CoreStore;
import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo;
import io.dataease.visualization.dao.auto.entity.VisualizationWatermark;
import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper;
@ -45,6 +54,7 @@ import io.dataease.visualization.dao.ext.mapper.ExtDataVisualizationMapper;
import io.dataease.visualization.manage.CoreVisualizationManage;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
@ -104,6 +114,15 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Resource
private ExtVisualizationTemplateMapper appTemplateMapper;
@Resource
private CoreDatasetGroupMapper coreDatasetGroupMapper;
@Resource
private CoreDatasetTableMapper coreDatasetTableMapper;
@Resource
private CoreDatasetTableFieldMapper coreDatasetTableFieldMapper;
@Override
public DataVisualizationVO findCopyResource(Long dvId, String busiFlag) {
DataVisualizationVO result = findById(new DataVisualizationBaseRequest(dvId, busiFlag));
@ -152,7 +171,86 @@ public class DataVisualizationServer implements DataVisualizationApi {
@DeLog(id = "#p0.id", pid = "#p0.pid", ot = LogOT.CREATE, stExp = "#p0.type")
@Override
@Transactional
public String saveCanvas(DataVisualizationBaseRequest request) {
public String saveCanvas(DataVisualizationBaseRequest request) throws Exception{
Boolean isAppSave = false;
Long time = System.currentTimeMillis();
// 如果是应用 则新进行应用校验 数据集名称和 数据源名称校验
VisualizationExport2AppVO appData = request.getAppData();
Map<Long,Long> dsGroupIdMap = new HashMap<>();
Map<Long,Long> dsTableIdMap = new HashMap<>();
Map<Long,Long> dsTableFieldsIdMap = new HashMap<>();
if(appData != null){
isAppSave = true;
try {
Map<Long,Long> datasourceIdMap = appData.getDatasourceInfo().stream()
.collect(Collectors.toMap(AppCoreDatasourceVO::getId, AppCoreDatasourceVO::getSystemDatasourceId));
Long datasetFolderPid = request.getDatasetFolderPid();
String datasetFolderName = request.getDatasetFolderName();
QueryWrapper<CoreDatasetGroup> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", datasetFolderName);
queryWrapper.eq("pid", datasetFolderPid);
if (coreDatasetGroupMapper.exists(queryWrapper)) {
DEException.throwException("当前数据集分组名称已存在");
}
//新建数据集分组
DatasetGroupInfoDTO datasetFolderNewRequest = new DatasetGroupInfoDTO();
datasetFolderNewRequest.setName(datasetFolderName);
datasetFolderNewRequest.setNodeType("folder");
datasetFolderNewRequest.setPid(datasetFolderPid);
DatasetGroupInfoDTO datasetFolderNew = datasetGroupManage.save(datasetFolderNewRequest, false);
Long datasetFolderNewId = datasetFolderNew.getId();
//新建数据集
appData.getDatasetGroupsInfo().forEach(appDatasetGroup -> {
if ("dataset".equals(appDatasetGroup.getNodeType())) {
Long oldId = appDatasetGroup.getId();
Long newId = IDUtils.snowID();
DatasetGroupInfoDTO datasetNewRequest = new DatasetGroupInfoDTO();
BeanUtils.copyBean(datasetNewRequest, appDatasetGroup);
datasetNewRequest.setId(newId);
datasetNewRequest.setCreateBy(AuthUtils.getUser().getUserId() + "");
datasetNewRequest.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetNewRequest.setCreateTime(time);
datasetNewRequest.setLastUpdateTime(time);
datasetNewRequest.setPid(datasetFolderNewId);
try {
datasetGroupManage.innerSave(datasetNewRequest);
dsGroupIdMap.put(oldId,newId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
// 新建数据集表
appData.getDatasetTablesInfo().forEach(appCoreDatasetTableVO -> {
Long oldId = appCoreDatasetTableVO.getId();
Long newId = IDUtils.snowID();
CoreDatasetTable datasetTable = new CoreDatasetTable();
BeanUtils.copyBean(datasetTable,appCoreDatasetTableVO);
datasetTable.setDatasetGroupId(dsGroupIdMap.get(datasetTable.getDatasetGroupId()));
datasetTable.setId(newId);
datasetTable.setDatasourceId(datasourceIdMap.get(datasetTable.getDatasourceId()));
coreDatasetTableMapper.insert(datasetTable);
dsTableIdMap.put(oldId,newId);
});
// 新建数据字段
appData.getDatasetTableFieldsInfo().forEach( appDsTableFields ->{
Long oldId = appDsTableFields.getId();
Long newId = IDUtils.snowID();
CoreDatasetTableField dsDsField = new CoreDatasetTableField();
BeanUtils.copyBean(dsDsField,appDsTableFields);
dsDsField.setDatasetGroupId(dsGroupIdMap.get(dsDsField.getDatasetGroupId()));
dsDsField.setDatasetTableId(dsTableIdMap.get(dsDsField.getDatasetTableId()));
dsDsField.setDatasourceId(datasourceIdMap.get(dsDsField.getDatasourceId()));
dsDsField.setId(newId);
coreDatasetTableFieldMapper.insert(dsDsField);
dsTableFieldsIdMap.put(oldId,newId);
});
}catch (Exception e){
DEException.throwException("应用创建失败");
}
}
DataVisualizationInfo visualizationInfo = new DataVisualizationInfo();
BeanUtils.copyBean(visualizationInfo, request);
visualizationInfo.setNodeType(request.getNodeType() == null ? DataVisualizationConstants.NODE_TYPE.LEAF : request.getNodeType());
@ -168,6 +266,14 @@ public class DataVisualizationServer implements DataVisualizationApi {
}
Long newDvId = coreVisualizationManage.innerSave(visualizationInfo);
request.setId(newDvId);
// TODO 还原ID信息
if(isAppSave){
request.getCanvasViewInfo().forEach((key,viewInfo) ->{
viewInfo.setTableId(dsGroupIdMap.get(viewInfo.getTableId()));
viewInfo.setDataFrom("dataset");
});
}
//保存图表信息
chartDataManage.saveChartViewFromVisualization(request.getComponentData(), newDvId, request.getCanvasViewInfo());
return newDvId.toString();
@ -298,96 +404,112 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Override
public DataVisualizationVO decompression(DataVisualizationBaseRequest request) throws Exception {
Long newDvId = IDUtils.snowID();
String newFrom = request.getNewFrom();
String templateStyle = null;
String templateData = null;
String dynamicData = null;
String staticResource = null;
String appData = null;
String name = null;
String dvType = null;
Integer version = null;
//内部模板新建
if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_INNER_TEMPLATE.equals(newFrom)) {
VisualizationTemplate visualizationTemplate = templateMapper.selectById(request.getTemplateId());
templateStyle = visualizationTemplate.getTemplateStyle();
templateData = visualizationTemplate.getTemplateData();
dynamicData = visualizationTemplate.getDynamicData();
name = visualizationTemplate.getName();
dvType = visualizationTemplate.getDvType();
version = visualizationTemplate.getVersion();
appData = visualizationTemplate.getAppData();
// 模板市场记录
coreOptRecentManage.saveOpt(request.getTemplateId(), OptConstants.OPT_RESOURCE_TYPE.TEMPLATE, OptConstants.OPT_TYPE.NEW);
VisualizationTemplate visualizationTemplateUpdate = new VisualizationTemplate();
visualizationTemplateUpdate.setId(visualizationTemplate.getId());
visualizationTemplateUpdate.setUseCount(visualizationTemplate.getUseCount() == null ? 0 : visualizationTemplate.getUseCount() + 1);
templateMapper.updateById(visualizationTemplateUpdate);
} else if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_OUTER_TEMPLATE.equals(newFrom)) {
templateStyle = request.getCanvasStyleData();
templateData = request.getComponentData();
dynamicData = request.getDynamicData();
staticResource = request.getStaticResource();
appData = request.getAppData();
name = request.getName();
dvType = request.getType();
} else if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_MARKET_TEMPLATE.equals(newFrom)) {
TemplateManageFileDTO templateFileInfo = templateCenterManage.getTemplateFromMarket(request.getTemplateUrl());
if (templateFileInfo == null) {
DEException.throwException("Can't find the template's info from market,please check");
}
templateStyle = templateFileInfo.getCanvasStyleData();
templateData = templateFileInfo.getComponentData();
dynamicData = templateFileInfo.getDynamicData();
staticResource = templateFileInfo.getStaticResource();
name = templateFileInfo.getName();
dvType = templateFileInfo.getDvType();
version = templateFileInfo.getVersion();
appData = templateFileInfo.getAppData();
// 模板市场记录
coreOptRecentManage.saveOpt(request.getResourceName(), OptConstants.OPT_RESOURCE_TYPE.TEMPLATE, OptConstants.OPT_TYPE.NEW);
}
// 解析动态数据
Map<String, String> dynamicDataMap = JsonUtil.parseObject(dynamicData, Map.class);
List<ChartViewDTO> chartViews = new ArrayList<>();
Map<Long, ChartViewDTO> canvasViewInfo = new HashMap<>();
Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>();
for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) {
String originViewId = entry.getKey();
Object viewInfo = entry.getValue();
try {
// 旧模板图表过滤器适配
if (viewInfo instanceof Map && ((Map) viewInfo).get("customFilter") instanceof ArrayList) {
((Map) viewInfo).put("customFilter", new HashMap<>());
try{
Long newDvId = IDUtils.snowID();
String newFrom = request.getNewFrom();
String templateStyle = null;
String templateData = null;
String dynamicData = null;
String staticResource = null;
String appDataStr = null;
String name = null;
String dvType = null;
Integer version = null;
//内部模板新建
if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_INNER_TEMPLATE.equals(newFrom)) {
VisualizationTemplate visualizationTemplate = templateMapper.selectById(request.getTemplateId());
templateStyle = visualizationTemplate.getTemplateStyle();
templateData = visualizationTemplate.getTemplateData();
dynamicData = visualizationTemplate.getDynamicData();
name = visualizationTemplate.getName();
dvType = visualizationTemplate.getDvType();
version = visualizationTemplate.getVersion();
appDataStr = visualizationTemplate.getAppData();
// 模板市场记录
coreOptRecentManage.saveOpt(request.getTemplateId(), OptConstants.OPT_RESOURCE_TYPE.TEMPLATE, OptConstants.OPT_TYPE.NEW);
VisualizationTemplate visualizationTemplateUpdate = new VisualizationTemplate();
visualizationTemplateUpdate.setId(visualizationTemplate.getId());
visualizationTemplateUpdate.setUseCount(visualizationTemplate.getUseCount() == null ? 0 : visualizationTemplate.getUseCount() + 1);
templateMapper.updateById(visualizationTemplateUpdate);
} else if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_OUTER_TEMPLATE.equals(newFrom)) {
templateStyle = request.getCanvasStyleData();
templateData = request.getComponentData();
dynamicData = request.getDynamicData();
staticResource = request.getStaticResource();
name = request.getName();
dvType = request.getType();
} else if (DataVisualizationConstants.NEW_PANEL_FROM.NEW_MARKET_TEMPLATE.equals(newFrom)) {
TemplateManageFileDTO templateFileInfo = templateCenterManage.getTemplateFromMarket(request.getTemplateUrl());
if (templateFileInfo == null) {
DEException.throwException("Can't find the template's info from market,please check");
}
} catch (Exception e) {
LogUtil.error("History Adaptor Error", e);
templateStyle = templateFileInfo.getCanvasStyleData();
templateData = templateFileInfo.getComponentData();
dynamicData = templateFileInfo.getDynamicData();
staticResource = templateFileInfo.getStaticResource();
name = templateFileInfo.getName();
dvType = templateFileInfo.getDvType();
version = templateFileInfo.getVersion();
appDataStr = templateFileInfo.getAppData();
// 模板市场记录
coreOptRecentManage.saveOpt(request.getResourceName(), OptConstants.OPT_RESOURCE_TYPE.TEMPLATE, OptConstants.OPT_TYPE.NEW);
}
String originViewData = JsonUtil.toJSONString(entry.getValue()).toString();
ChartViewDTO chartView = JsonUtil.parseObject(originViewData, ChartViewDTO.class);
if (chartView == null) {
continue;
if(StringUtils.isNotEmpty(appDataStr)){
VisualizationExport2AppVO appDataFormat = JsonUtil.parseObject(appDataStr,VisualizationExport2AppVO.class);
String dvInfo = appDataFormat.getVisualizationInfo();
VisualizationBaseInfoVO baseInfoVO = JsonUtil.parseObject(dvInfo,VisualizationBaseInfoVO.class);
Long sourceDvId = baseInfoVO.getId();
appDataStr = appDataStr.replaceAll(sourceDvId.toString(), newDvId.toString());
}
Long newViewId = IDUtils.snowID();
chartView.setId(newViewId);
chartView.setSceneId(newDvId);
chartView.setTableId(null);
chartView.setDataFrom(CommonConstants.VIEW_DATA_FROM.TEMPLATE);
// 数据处理 1.替换viewId 2.加入模板view data数据
VisualizationTemplateExtendDataDTO extendDataDTO = new VisualizationTemplateExtendDataDTO(newDvId, newViewId, originViewData);
extendDataInfo.put(newViewId, extendDataDTO);
templateData = templateData.replaceAll(originViewId, newViewId.toString());
canvasViewInfo.put(chartView.getId(), chartView);
//插入模板数据 此处预先插入减少数据交互量
VisualizationTemplateExtendData extendData = new VisualizationTemplateExtendData();
templateExtendDataMapper.insert(BeanUtils.copyBean(extendData, extendDataDTO));
// 解析动态数据
Map<String, String> dynamicDataMap = JsonUtil.parseObject(dynamicData, Map.class);
List<ChartViewDTO> chartViews = new ArrayList<>();
Map<Long, ChartViewDTO> canvasViewInfo = new HashMap<>();
Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>();
for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) {
String originViewId = entry.getKey();
Object viewInfo = entry.getValue();
try {
// 旧模板图表过滤器适配
if (viewInfo instanceof Map && ((Map) viewInfo).get("customFilter") instanceof ArrayList) {
((Map) viewInfo).put("customFilter", new HashMap<>());
}
} catch (Exception e) {
LogUtil.error("History Adaptor Error", e);
}
String originViewData = JsonUtil.toJSONString(entry.getValue()).toString();
ChartViewDTO chartView = JsonUtil.parseObject(originViewData, ChartViewDTO.class);
if (chartView == null) {
continue;
}
Long newViewId = IDUtils.snowID();
chartView.setId(newViewId);
chartView.setSceneId(newDvId);
chartView.setTableId(null);
chartView.setDataFrom(CommonConstants.VIEW_DATA_FROM.TEMPLATE);
// 数据处理 1.替换viewId 2.加入模板view data数据
VisualizationTemplateExtendDataDTO extendDataDTO = new VisualizationTemplateExtendDataDTO(newDvId, newViewId, originViewData);
extendDataInfo.put(newViewId, extendDataDTO);
templateData = templateData.replaceAll(originViewId, newViewId.toString());
if(StringUtils.isNotEmpty(appDataStr)){
appDataStr = appDataStr.replaceAll(originViewId, newViewId.toString());
}
canvasViewInfo.put(chartView.getId(), chartView);
//插入模板数据 此处预先插入减少数据交互量
VisualizationTemplateExtendData extendData = new VisualizationTemplateExtendData();
templateExtendDataMapper.insert(BeanUtils.copyBean(extendData, extendDataDTO));
}
request.setComponentData(templateData);
request.setCanvasStyleData(templateStyle);
//Store static resource into the server
staticResourceServer.saveFilesToServe(staticResource);
return new DataVisualizationVO(newDvId, name, dvType, version, templateStyle, templateData,appDataStr, canvasViewInfo, null);
}catch (Exception e){
e.printStackTrace();
DEException.throwException("解析错误");
return null;
}
request.setComponentData(templateData);
request.setCanvasStyleData(templateStyle);
//Store static resource into the server
staticResourceServer.saveFilesToServe(staticResource);
return new DataVisualizationVO(newDvId, name, dvType, version, templateStyle, templateData,appData, canvasViewInfo, null);
}
@Override

View File

@ -47,9 +47,9 @@ quartz:
dataease:
version: '@project.version@'
xpack-front-distributed: true
origin-list: http://192.168.2.70:9080
origin-list: http://192.168.1.9:9080
apisix-api:
domain: http://192.168.2.70:9180
domain: http://192.168.1.9:9180
key: edd1c9f034335f136f87ad84b625c8f1
# springdoc-openapi项目配置

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,9 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g clip-path="url(#clip0_3726_157430)">
<rect width="24" height="24" fill="url(#pattern0)"/>
<rect width="24" height="24" fill="url(#pattern26)"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern26" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3726_157430" transform="scale(0.0113636)"/>
</pattern>
<clipPath id="clip0_3726_157430">

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 742 KiB

After

Width:  |  Height:  |  Size: 742 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 742 KiB

After

Width:  |  Height:  |  Size: 742 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 741 KiB

After

Width:  |  Height:  |  Size: 741 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 742 KiB

After

Width:  |  Height:  |  Size: 742 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

View File

@ -4,11 +4,11 @@
<path d="M24 0H0V24H24V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_3719_157276)">
<rect x="5" y="-1" width="13.8768" height="26" fill="url(#pattern0)"/>
<rect x="5" y="-1" width="13.8768" height="26" fill="url(#pattern11)"/>
</g>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern11" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3719_157276" transform="scale(0.00549451 0.00293255)"/>
</pattern>
<clipPath id="clip0_3719_157276">

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

View File

@ -1,9 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g clip-path="url(#clip0_3726_157403)">
<rect x="-0.599609" y="3.70203" width="25.452" height="16.758" fill="url(#pattern0)"/>
<rect x="-0.599609" y="3.70203" width="25.452" height="16.758" fill="url(#pattern25)"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern25" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3726_157403" transform="scale(0.0049505 0.0075188)"/>
</pattern>
<clipPath id="clip0_3726_157403">

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,9 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g clip-path="url(#clip0_3726_157417)">
<rect width="24" height="24" fill="url(#pattern0)"/>
<rect width="24" height="24" fill="url(#pattern12)"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern12" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3726_157417" transform="translate(-0.00172414) scale(0.00344828)"/>
</pattern>
<clipPath id="clip0_3726_157417">

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 742 KiB

After

Width:  |  Height:  |  Size: 742 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 739 KiB

View File

@ -94,7 +94,7 @@
<g opacity="0.8" filter="url(#filter0_d_3828_138019)">
<path d="M408.823 52.121H221.143C219.655 52.0559 218.201 52.5834 217.101 53.5881C216.001 54.5928 215.343 55.9927 215.273 57.481V169.781C215.323 170.793 215.772 171.744 216.522 172.424C217.272 173.105 218.262 173.46 219.273 173.411H410.723C411.735 173.46 412.725 173.105 413.475 172.424C414.225 171.744 414.674 170.793 414.723 169.781V57.481C414.689 56.7414 414.509 56.0159 414.193 55.346C413.878 54.6761 413.433 54.075 412.885 53.5771C412.337 53.0792 411.696 52.6943 410.999 52.4445C410.302 52.1946 409.563 52.0847 408.823 52.121Z" fill="white"/>
</g>
<rect opacity="0.5" x="206" y="111" width="105" height="71" fill="url(#pattern0)"/>
<rect opacity="0.5" x="206" y="111" width="105" height="71" fill="url(#pattern29)"/>
<path d="M294.353 126.421H225.573C224.585 126.421 223.783 127.222 223.783 128.211V163.491C223.783 164.479 224.585 165.281 225.573 165.281H294.353C295.342 165.281 296.143 164.479 296.143 163.491V128.211C296.143 127.222 295.342 126.421 294.353 126.421Z" fill="white"/>
<g opacity="0.3">
<rect x="291.713" y="117.111" width="105" height="71" fill="url(#pattern1)"/>
@ -215,7 +215,7 @@
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3828_138019"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3828_138019" result="shape"/>
</filter>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern29" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3828_138019" transform="scale(0.00952381 0.0140845)"/>
</pattern>
<pattern id="pattern1" patternContentUnits="objectBoundingBox" width="1" height="1">

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -1,7 +1,7 @@
<svg width="160" height="190" viewBox="0 0 160 190" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g clip-path="url(#clip0_3828_138297)">
<path opacity="0.8" d="M11.5336 42H144.578C145.028 42 145.474 42.1082 145.89 42.3184C146.306 42.5285 146.683 42.8366 147.001 43.2248C147.319 43.613 147.571 44.0738 147.742 44.5808C147.914 45.0877 148.001 45.6309 148 46.179V140.904C148 142.01 147.64 143.071 146.998 143.854C146.357 144.638 145.487 145.079 144.578 145.083H11.5336C10.6234 145.083 9.75046 144.642 9.10687 143.859C8.46327 143.075 8.1017 142.012 8.1017 140.904V46.179C8.10031 45.6298 8.18814 45.0856 8.36011 44.5778C8.53209 44.07 8.78482 43.6087 9.10378 43.2203C9.42273 42.8319 9.80161 42.5241 10.2186 42.3147C10.6356 42.1053 11.0825 41.9983 11.5336 42" fill="#F5F6F7"/>
<rect opacity="0.25" x="-12" y="35" width="177" height="120" fill="url(#pattern0)"/>
<rect opacity="0.25" x="-12" y="35" width="177" height="120" fill="url(#pattern31)"/>
<path d="M11.6234 42.0219H144.566C145.017 42.0205 145.464 42.1084 145.882 42.2805C146.299 42.4526 146.678 42.7055 146.997 43.0247C147.316 43.3439 147.569 43.723 147.741 44.1403C147.913 44.5576 148.001 45.0048 148 45.4562V50.3505H8V45.5823C8.00134 45.1098 8.09644 44.6422 8.27979 44.2067C8.46314 43.7713 8.73109 43.3765 9.06812 43.0453C9.40515 42.7142 9.80456 42.4532 10.2432 42.2775C10.6818 42.1018 11.151 42.0149 11.6234 42.0219Z" fill="#154397"/>
<path d="M16.7797 46.3058C16.786 46.5917 16.7069 46.8729 16.5527 47.1136C16.3984 47.3543 16.176 47.5437 15.9137 47.6574C15.6514 47.7712 15.3611 47.8042 15.08 47.7523C14.7988 47.7004 14.5395 47.5659 14.3351 47.366C14.1307 47.166 13.9905 46.9098 13.9324 46.6298C13.8743 46.3499 13.9009 46.059 14.0088 45.7942C14.1167 45.5295 14.301 45.3029 14.5383 45.1433C14.7755 44.9838 15.055 44.8985 15.3409 44.8985C15.7179 44.8957 16.0807 45.042 16.3503 45.3057C16.6198 45.5693 16.7742 45.9288 16.7797 46.3058Z" fill="#F54A45"/>
<path d="M21.7895 46.3058C21.7957 46.5921 21.7164 46.8738 21.5617 47.1147C21.407 47.3557 21.1839 47.545 20.921 47.6584C20.6581 47.7719 20.3673 47.8043 20.0858 47.7515C19.8044 47.6988 19.5451 47.5632 19.3411 47.3622C19.1371 47.1613 18.9978 46.904 18.9408 46.6234C18.8839 46.3427 18.912 46.0515 19.0215 45.7869C19.1311 45.5223 19.3171 45.2965 19.5557 45.1382C19.7943 44.9799 20.0748 44.8965 20.3611 44.8985C20.7363 44.8984 21.0965 45.046 21.3638 45.3094C21.631 45.5727 21.784 45.9307 21.7895 46.3058" fill="#FF8800"/>
@ -36,7 +36,7 @@
<path d="M58.3459 126.266L35.4408 162.211C35.3014 162.429 35.1138 162.611 34.8925 162.745C34.6712 162.879 34.4221 162.96 34.1644 162.982C33.9068 163.004 33.6475 162.967 33.4065 162.873C33.1656 162.779 32.9495 162.631 32.7749 162.44L22.3408 150.978L21.6019 149.81C21.4755 149.62 21.3892 149.406 21.3484 149.181C21.3075 148.956 21.3129 148.726 21.3644 148.503C21.4158 148.281 21.5121 148.071 21.6474 147.887C21.7826 147.703 21.954 147.549 22.1511 147.433L58.3459 126.266Z" fill="#F4C04C"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<pattern id="pattern31" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3828_138297" transform="scale(0.00952381 0.0140845)"/>
</pattern>
<clipPath id="clip0_3828_138297">

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import eventBus from '@/utils/eventBus'
import { ref, nextTick, computed } from 'vue'
import { ref, nextTick, computed, toRefs } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { useAppStoreWithOut } from '@/store/modules/app'
@ -32,6 +32,7 @@ const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const { styleChangeTimes, snapshotIndex } = storeToRefs(snapshotStore)
const resourceGroupOpt = ref(null)
const resourceAppOpt = ref(null)
const dvToolbarMain = ref(null)
const { componentData, canvasStyleData, canvasViewInfo, dvInfo, editMode } =
storeToRefs(dvMainStore)
@ -41,6 +42,15 @@ const dvModel = 'dataV'
const outerParamsSetRef = ref(null)
const fullScreeRef = ref(null)
const props = defineProps({
createType: {
type: String,
default: 'create'
}
})
const { createType } = toRefs(props)
const closeEditCanvasName = () => {
nameEdit.value = false
if (!inputName.value || !inputName.value.trim()) {
@ -91,9 +101,24 @@ const resourceOptFinish = param => {
}
const saveCanvasWithCheck = () => {
const appData = dvMainStore.getAppDataInfo()
if (dvInfo.value.dataState === 'prepare') {
const params = { name: dvInfo.value.name, leaf: true, id: dvInfo.value.pid }
resourceGroupOpt.value.optInit('leaf', params, 'newLeaf', true)
if (appData) {
//
const params = {
base: {
pid: '',
name: dvInfo.value.name,
datasetFolderPid: null,
datasetFolderName: dvInfo.value.name
},
appData: appData
}
resourceAppOpt.value.init(params)
} else {
const params = { name: dvInfo.value.name, leaf: true, id: dvInfo.value.pid }
resourceGroupOpt.value.optInit('leaf', params, 'newLeaf', true)
}
return
}
saveResource()
@ -345,10 +370,12 @@ const fullScreenPreview = () => {
ref="resourceGroupOpt"
/>
<de-app-apply
ref="resourceGroupOpt"
ref="resourceAppOpt"
:component-data="componentData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
cur-canvas-type="dataV"
@saveApp="saveCanvasWithCheck"
></de-app-apply>
</div>
<de-fullscreen ref="fullScreeRef" show-position="dvEdit"></de-fullscreen>

View File

@ -169,7 +169,11 @@ const downloadApp = () => {
const dsIds = []
gatherAppInfo(viewIds, dsIds)
export2AppCheck({ dvId: dvInfo.value.id, viewIds, dsIds }).then(rsp => {
const params = { ...rsp.data, ...state.form }
const params = {
...rsp.data,
...state.form,
visualizationInfo: JSON.stringify(dvInfo.value)
}
emits('downLoadApp', params)
state.applyDownloadDrawer = false
})

View File

@ -228,7 +228,17 @@ const queryDataForId = id => {
requiredName = next.name
}
if (
if (next.displayType === '8') {
const { conditionValueF, conditionValueS, conditionType } = next
if (conditionType === 0) {
requiredName = conditionValueF === '' ? next.name : ''
} else {
requiredName = [conditionValueF || '', conditionValueS || ''].filter(ele => ele !== '')
.length
? next.name
: ''
}
} else if (
(Array.isArray(next.selectValue) && !next.selectValue.length) ||
(next.selectValue !== 0 && !next.selectValue)
) {
@ -258,6 +268,13 @@ const getQueryConditionWidth = () => {
const getCascadeList = () => {
return props.element.cascade
}
const isConfirmSearch = id => {
if (componentWithSure.value) return
queryDataForId(id)
}
provide('is-confirm-search', isConfirmSearch)
provide('unmount-select', unMountSelect)
provide('release-unmount-select', releaseSelect)
provide('query-data-for-id', queryDataForId)
@ -415,6 +432,22 @@ const listVisible = computed(() => {
return list.value.filter(itx => itx.visible)
})
const componentWithSure = computed(() => {
return customStyle.btnList.includes('sure')
})
watch(
() => componentWithSure.value,
(val, oldVal) => {
if (!val && oldVal) {
queryData()
}
},
{
immediate: false
}
)
const queryData = () => {
let requiredName = ''
const emitterList = (element.value.propValue || []).reduce((pre, next) => {
@ -574,7 +607,7 @@ const autoStyle = computed(() => {
@click.stop="queryData"
style="margin-right: 7px"
:style="btnStyle"
v-if="customStyle.btnList.includes('sure')"
v-if="componentWithSure"
type="primary"
>
{{ t('commons.adv_search.search') }}

View File

@ -75,6 +75,7 @@ const options = shallowRef([])
const unMountSelect: Ref = inject('unmount-select')
const releaseSelect = inject('release-unmount-select', Function, true)
const queryDataForId = inject('query-data-for-id', Function, true)
const isConfirmSearch = inject('is-confirm-search', Function, true)
const queryConditionWidth = inject('com-width', Function, true)
const cascadeList = inject('cascade-list', Function, true)
const setCascadeDefault = inject('set-cascade-default', Function, true)
@ -198,6 +199,9 @@ const handleValueChange = () => {
)
setCascadeValueBack(config.value.mapValue)
emitCascade()
nextTick(() => {
isConfirmSearch(config.value.id)
})
return
}

View File

@ -1,8 +1,9 @@
<script lang="ts" setup>
import { toRefs, onBeforeMount, type PropType, inject, computed } from 'vue'
import { toRefs, onBeforeMount, type PropType, inject, computed, nextTick } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
interface SelectConfig {
id: string
conditionValueOperatorF: string
conditionValueF: string
conditionValueOperatorS: string
@ -31,6 +32,7 @@ const props = defineProps({
type: Object as PropType<SelectConfig>,
default: () => {
return {
id: '',
conditionType: 0,
conditionValueOperatorF: 'eq',
conditionValueF: '',
@ -42,6 +44,10 @@ const props = defineProps({
defaultConditionValueS: ''
}
}
},
isConfig: {
type: Boolean,
default: false
}
})
const { config } = toRefs(props)
@ -62,10 +68,18 @@ onBeforeMount(() => {
})
const queryConditionWidth = inject('com-width', Function, true)
const customStyle = inject<{ background: string }>('$custom-style-filter')
const isConfirmSearch = inject('is-confirm-search', Function, true)
const selectStyle = computed(() => {
return { width: queryConditionWidth() + 'px' }
})
const handleValueChange = () => {
if (!props.isConfig) {
nextTick(() => {
isConfirmSearch(config.value.id)
})
return
}
}
const lineWidth = computed(() => {
return { width: queryConditionWidth() - 15 + 'px' }
})
@ -76,6 +90,7 @@ const lineWidth = computed(() => {
<div class="condition-type">
<el-select
class="condition-value-select"
@change="handleValueChange"
:effect="dvInfo.type === 'dataV' ? 'dark' : ''"
popper-class="condition-value-select-popper"
v-model="config.conditionValueOperatorF"
@ -85,6 +100,7 @@ const lineWidth = computed(() => {
</el-select>
<el-input
:style="selectStyle"
@blur="handleValueChange"
class="condition-value-input"
v-model="config.conditionValueF"
/>
@ -94,6 +110,7 @@ const lineWidth = computed(() => {
<sapn class="condition-type-tip">{{ config.conditionType === 1 ? '与' : '或' }}</sapn>
<el-select
class="condition-value-select"
@change="handleValueChange"
:effect="dvInfo.type === 'dataV' ? 'dark' : ''"
popper-class="condition-value-select-popper"
v-model="config.conditionValueOperatorS"
@ -103,6 +120,7 @@ const lineWidth = computed(() => {
</el-select>
<el-input
:style="selectStyle"
@blur="handleValueChange"
class="condition-value-input"
v-model="config.conditionValueS"
/>

View File

@ -130,9 +130,12 @@ const handleValueChange = () => {
config.value.selectValue = Array.isArray(selectValue.value)
? [...selectValue.value]
: selectValue.value
nextTick(() => {
isConfirmSearch(config.value.id)
})
return
}
config.value.defaultValue = value
config.value.defaultValue = new Date(value).toLocaleString()
}
const init = () => {
@ -149,6 +152,7 @@ const init = () => {
}
const queryConditionWidth = inject('com-width', Function, true)
const isConfirmSearch = inject('is-confirm-search', Function, true)
const selectStyle = computed(() => {
return props.isConfig
? {}

View File

@ -63,6 +63,9 @@ const handleValueChange = () => {
config.value.selectValue = Array.isArray(treeValue.value)
? [...treeValue.value]
: treeValue.value
nextTick(() => {
isConfirmSearch(config.value.id)
})
return
}
config.value.defaultValue = value
@ -114,6 +117,7 @@ watch(
)
const showOrHide = ref(true)
const queryConditionWidth = inject('com-width', Function, true)
const isConfirmSearch = inject('is-confirm-search', Function, true)
watch(
() => config.value.id,
() => {

View File

@ -186,6 +186,7 @@ if (uid.value === '1') {
}
.is-light-top-info {
.uname-span {
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
color: var(--ed-color-black) !important;
}
&:hover {
@ -209,6 +210,7 @@ if (uid.value === '1') {
border-radius: 50%;
}
.uname-span {
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
}

View File

@ -45,6 +45,7 @@ export const dvMainStore = defineStore('dataVisualization', {
inMobile: false,
firstLoadMap: [],
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },
appData: {}, //应用信息
// 当前展示画布缓存数据
componentDataCache: null,
// PC布局画布组件数据
@ -245,7 +246,12 @@ export const dvMainStore = defineStore('dataVisualization', {
setCanvasViewInfo(canvasViewInfo) {
this.canvasViewInfo = canvasViewInfo
},
getAppDataInfo() {
return this.appData
},
setAppDataInfo(appDataInfo) {
this.appData = appDataInfo
},
setCurComponent({ component, index }) {
if (!component && this.curComponent) {
this.curComponent['editing'] = false
@ -1205,13 +1211,14 @@ export const dvMainStore = defineStore('dataVisualization', {
this.canvasState[key] = value
}
},
createInit(dvType, resourceId?, pid?, watermarkInfo?) {
createInit(dvType, resourceId?, pid?, watermarkInfo?, preName) {
const optName = dvType === 'dashboard' ? '新建仪表板' : '新建数据大屏'
const name = preName ? preName : optName
this.dvInfo = {
dataState: 'prepare',
optType: null,
id: resourceId,
name: optName,
name: name,
pid: pid,
type: dvType,
status: 1,

View File

@ -25,7 +25,7 @@ import {
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { deepCopy } from '@/utils/utils'
const dvMainStore = dvMainStoreWithOut()
const { curBatchOptComponents, dvInfo, canvasStyleData, componentData, canvasViewInfo } =
const { curBatchOptComponents, dvInfo, canvasStyleData, componentData, canvasViewInfo, appData } =
storeToRefs(dvMainStore)
const snapshotStore = snapshotStoreWithOut()
@ -349,6 +349,7 @@ export async function canvasSave(callBack) {
canvasStyleData: JSON.stringify(canvasStyleData.value),
componentData: JSON.stringify(componentDataToSave),
canvasViewInfo: canvasViewInfo.value,
appData: appData.value,
...dvInfo.value,
watermarkInfo: null
}
@ -521,6 +522,7 @@ export async function decompressionPre(params, callBack) {
.then(response => {
const deTemplateDataTemp = response.data
const sourceComponentData = JSON.parse(deTemplateDataTemp['componentData'])
const appData = deTemplateDataTemp['appData']
sourceComponentData.forEach(componentItem => {
// 2 为基础版本 此处需要增加仪表板矩阵密度
if (
@ -538,7 +540,10 @@ export async function decompressionPre(params, callBack) {
canvasStyleData: sourceCanvasStyle,
componentData: sourceComponentData,
canvasViewInfo: deTemplateDataTemp['canvasViewInfo'],
appData: deTemplateDataTemp['appData']
appData: appData,
baseInfo: {
preName: deTemplateDataTemp.name
}
}
})
.catch(e => {

View File

@ -5,8 +5,9 @@ import { Plus, Search } from '@element-plus/icons-vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useAppStoreWithOut } from '@/store/modules/app'
import _ from 'lodash'
import { getDatasetTree } from '@/api/dataset'
import { getDatasetTree, getDatasourceList } from '@/api/dataset'
import { ElFormItem, FormInstance } from 'element-plus-secondary'
import type { DataSource } from '@/views/visualized/data/dataset/form/util'
const props = withDefaults(
defineProps<{
@ -14,10 +15,12 @@ const props = withDefaults(
modelValue?: string | number
stateObj: any
viewId: string
sourceType: string
}>(),
{
datasetTree: () => [],
themes: 'dark'
themes: 'dark',
sourceType: 'dataset'
}
)
@ -29,9 +32,13 @@ const datasetTree = ref<Tree[]>([])
const toolTip = computed(() => {
return props.themes === 'dark' ? 'ndark' : 'dark'
})
const sourceName = computed(() => (props.sourceType === 'datasource' ? '数据源' : '数据集'))
const initDataset = () => {
loadingDatasetTree.value = true
getDatasetTree({})
const method = props.sourceType === 'datasource' ? getDatasourceList : getDatasetTree
method({})
.then(res => {
datasetTree.value = (res as unknown as Tree[]) || []
})
@ -110,7 +117,7 @@ const exist = computed(() => {
const selectedNodeName = computed(() => {
if (!exist.value) {
return '数据集不存在'
return sourceName.value + '不存在'
}
return selectedNode.value?.name
})
@ -212,7 +219,7 @@ onMounted(() => {
v-model="selectedNodeName"
readonly
class="data-set-dark"
placeholder="请选择数据集"
:placeholder="'请选择' + sourceName"
>
<template #suffix>
<el-icon class="input-arrow-icon" :class="{ reverse: _popoverShow }">
@ -227,7 +234,7 @@ onMounted(() => {
<el-container :class="themes">
<el-header>
<div class="m-title" :class="{ dark: themes === 'dark' }">
<div>{{ t('dataset.datalist') }}</div>
<div>{{ sourceName }}</div>
<el-button type="primary" link class="refresh-btn" @click="refresh">
{{ t('commons.refresh') }}
</el-button>
@ -244,7 +251,7 @@ onMounted(() => {
<el-main :class="{ dark: themes === 'dark' }">
<el-scrollbar max-height="252px" always>
<div class="m-loading" v-if="loadingDatasetTree" v-loading="loadingDatasetTree"></div>
<div class="empty-info" v-if="showEmptyInfo">暂无数据集</div>
<div class="empty-info" v-if="showEmptyInfo">暂无{{ sourceName }}</div>
<!-- <div class="empty-info" v-if="showEmptySearchInfo">暂无相关数据</div>-->
<el-tree
:class="{ dark: themes === 'dark' }"
@ -294,7 +301,7 @@ onMounted(() => {
<el-footer v-if="!isDataEaseBi">
<div class="footer-container">
<el-button type="primary" :icon="Plus" link class="add-btn" @click="addDataset">
新建数据集
新建{{ sourceName }}
</el-button>
</div>
</el-footer>

View File

@ -281,7 +281,7 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
/>
</el-select>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<el-tooltip :effect="toolTip" placement="bottom">
<template #content>
{{ t('chart.bolder') }}
</template>
@ -300,7 +300,7 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<el-tooltip :effect="toolTip" placement="bottom">
<template #content>
{{ t('chart.italic') }}
</template>
@ -343,9 +343,19 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
:class="'form-item-' + themes"
>
<el-checkbox-group :effect="themes" v-model="chart.customStyle.component.btnList">
<el-checkbox :effect="themes" size="small" disabled label="sure">
<el-checkbox class="checkbox-with_icon" :effect="themes" size="small" label="sure">
{{ t('commons.adv_search.search') }}
<el-tooltip
:effect="toolTip"
content="如果展示查询按钮,需要点击该按钮后才能触发图表查询;如果不展示查询按钮,选择完查询条件后立即触发图表查询"
placement="top"
>
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">
<Icon name="icon_info_outlined" />
</el-icon>
</el-tooltip>
</el-checkbox>
<el-checkbox :effect="themes" size="small" label="clear">
{{ t('commons.clear') }}
</el-checkbox>
@ -389,7 +399,7 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
/>
</el-select>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<el-tooltip :effect="toolTip" placement="bottom">
<template #content>
{{ t('chart.bolder') }}
</template>
@ -408,7 +418,7 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
</div>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<el-tooltip :effect="toolTip" placement="bottom">
<template #content>
{{ t('chart.italic') }}
</template>
@ -455,6 +465,27 @@ if (!chart.value.customStyle.component.hasOwnProperty('labelShow')) {
&.no-margin-bottom {
margin-bottom: 0 !important;
}
.checkbox-with_icon {
:deep(.ed-checkbox__label) {
display: inline-flex;
align-items: center;
.ed-icon {
margin-left: 5px;
}
}
}
.hint-icon {
cursor: pointer;
font-size: 14px;
color: #646a73;
&.hint-icon--dark {
color: #a6a6a6;
}
}
}
.m-divider {
border-color: rgba(31, 35, 41, 0.15);

View File

@ -751,7 +751,7 @@ export function getAnalyseHorizontal(chart: Chart) {
dynamicLineFields?.includes(item.fieldId) &&
!!_.find(quotaFields, d => d.id === item.fieldId)
)
const lines = fixedLines.concat(dynamicLines)
const lines = fixedLines.concat(dynamicLines || [])
lines.forEach(ele => {
const value = parseFloat(ele.value)

View File

@ -3,7 +3,7 @@
:title="'保存应用'"
v-model="state.appApplyDrawer"
custom-class="de-user-drawer"
size="600px"
size="500px"
direction="rtl"
>
<div class="app-export">
@ -12,16 +12,17 @@
:model="state.form"
:rules="state.rule"
class="de-form-item"
size="middle"
label-width="180px"
label-position="top"
>
<div class="de-row-rules" style="margin: 0 0 16px">
<span>基本信息</span>
</div>
<el-form-item :label="dvPreName + '名称'" prop="appName">
<el-form-item :label="dvPreName + '名称'" prop="name">
<el-input v-model="state.form.name" autocomplete="off" :placeholder="'请输入名称'" />
</el-form-item>
<el-form-item :label="dvPreName + '所在位置'" prop="version">
<el-form-item :label="dvPreName + '所在位置'" prop="pid">
<el-tree-select
style="width: 100%"
@keydown.stop
@ -44,23 +45,23 @@
</template>
</el-tree-select>
</el-form-item>
<el-form-item :label="'数据集分组名称'" prop="appName">
<el-form-item :label="'数据集分组名称'" prop="datasetFolderName">
<el-input
v-model="state.form.datasetFolderName"
autocomplete="off"
:placeholder="'请输入名称'"
/>
</el-form-item>
<el-form-item label="数据集分组位置" prop="version">
<el-form-item label="数据集分组位置" prop="datasetFolderPid">
<el-tree-select
style="width: 100%"
@keydown.stop
@keyup.stop
v-model="state.form.datasetFolderPid"
:data="state.dvTree"
:data="state.dsTree"
:props="state.propsTree"
@node-click="dvTreeSelect"
:filter-method="dvTreeFilterMethod"
@node-click="dsTreeSelect"
:filter-method="dsTreeFilterMethod"
:render-after-expand="false"
filterable
>
@ -77,13 +78,47 @@
<div class="de-row-rules" style="margin: 0 0 16px">
<span>数据源信息</span>
</div>
<div>数据源信息配置</div>
<el-row class="datasource-link">
<el-row class="head">
<el-col :span="11">应用数据源</el-col><el-col :span="2"></el-col
><el-col :span="11">系统数据源</el-col>
</el-row>
<el-row
:key="index"
class="content"
v-for="(appDatasource, index) in state.appData.datasourceInfo"
>
<el-col :span="11">
<el-select style="width: 100%" v-model="appDatasource.name" disabled>
<el-option
:key="appDatasource.name"
:label="appDatasource.name"
:value="appDatasource.name"
>
</el-option>
</el-select> </el-col
><el-col :span="2" class="icon-center">
<Icon style="width: 20px; height: 20px" name="dv-link-target" /></el-col
><el-col :span="11">
<dataset-select
ref="datasetSelector"
v-model="appDatasource.systemDatasourceId"
style="flex: 1"
:state-obj="state"
themes="light"
source-type="datasource"
@add-ds-window="addDsWindow"
view-id="0"
/>
</el-col>
</el-row>
</el-row>
</el-form>
</div>
<template #footer>
<div class="apply" style="width: 100%">
<el-button secondary @click="close">{{ $t('commons.cancel') }} </el-button>
<el-button type="primary" @click="downloadApp">保存</el-button>
<el-button type="primary" @click="saveApp">保存</el-button>
</div>
</template>
</el-drawer>
@ -98,11 +133,20 @@ import {
ElInput,
ElTreeSelect
} from 'element-plus-secondary'
import { computed, reactive, ref, toRefs } from 'vue'
import { computed, PropType, reactive, ref, toRefs } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { queryTreeApi } from '@/api/visualization/dataVisualization'
import { BusiTreeNode, BusiTreeRequest } from '@/models/tree/TreeNode'
import { getDatasetTree } from '@/api/dataset'
import DatasetSelect from '@/views/chart/components/editor/dataset-select/DatasetSelect.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { deepCopy } from '@/utils/utils'
const { t } = useI18n()
const emits = defineEmits(['closeDraw', 'downLoadApp'])
const emits = defineEmits(['closeDraw', 'saveApp'])
const appSaveForm = ref(null)
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, appData } = storeToRefs(dvMainStore)
const props = defineProps({
componentData: {
@ -113,34 +157,42 @@ const props = defineProps({
type: Object,
required: true
},
dvInfo: {
type: Object,
curCanvasType: {
type: String,
required: true
},
dvType: {
type: String,
default: 'dashboard'
themes: {
type: String as PropType<EditorTheme>,
default: 'dark'
}
})
const { componentData, canvasViewInfo, dvInfo, dvType } = toRefs(props)
const { componentData, canvasViewInfo, curCanvasType, themes } = toRefs(props)
const dvPreName = computed(() => (dvType.value === 'dashboard' ? '仪表板' : '数据大屏'))
const dvPreName = computed(() => (curCanvasType.value === 'dashboard' ? '仪表板' : '数据大屏'))
const addDsWindow = () => {
// do addDsWindow
const url = '#/data/datasource?opt=create'
window.open(url, '_blank')
}
const state = reactive({
appApplyDrawer: false,
dvTree: [],
dsTree: [],
propsTree: {
label: 'name',
children: 'children',
isLeaf: node => !node.children?.length
},
appData: {
datasourceInfo: []
},
form: {
pid: '',
name: '新建',
datasetFolderPid: null,
datasetFolderName: null,
datasourceMap: {} // ID
datasetFolderName: null
},
rule: {
name: [
@ -155,8 +207,6 @@ const state = reactive({
pid: [
{
required: true,
min: 2,
max: 25,
message: '请选择所属文件夹',
trigger: 'blur'
}
@ -173,8 +223,6 @@ const state = reactive({
datasetFolderPid: [
{
required: true,
min: 2,
max: 25,
message: '请选择数据集分组所属文件夹',
trigger: 'blur'
}
@ -182,20 +230,59 @@ const state = reactive({
}
})
const initData = () => {
const request = { busiFlag: curCanvasType.value, leaf: false, weight: 7 }
queryTreeApi(request).then(res => {
const resultTree = res || []
dfs(resultTree as unknown as BusiTreeNode[])
state.dvTree = (resultTree as unknown as BusiTreeNode[]) || []
if (state.dvTree.length && state.dvTree[0].name === 'root' && state.dvTree[0].id === '0') {
state.dvTree[0].name = curCanvasType.value === 'dataV' ? '数据大屏' : '仪表板'
}
})
const requestDs = { leaf: false, weight: 7 } as BusiTreeRequest
getDatasetTree(requestDs).then(res => {
dfs(res as unknown as BusiTreeNode[])
state.dsTree = (res as unknown as BusiTreeNode[]) || []
if (state.dsTree.length && state.dsTree[0].name === 'root' && state.dsTree[0].id === '0') {
state.dsTree[0].name = '数据集'
}
})
}
const dfs = (arr: BusiTreeNode[]) => {
arr.forEach(ele => {
ele['value'] = ele.id
if (ele.children?.length) {
dfs(ele.children)
}
})
}
const init = params => {
console.log('init==')
state.appApplyDrawer = true
state.form = params
state.form = params.base
state.appData.datasourceInfo = deepCopy(appData.value?.datasourceInfo)
initData()
}
const dvTreeFilterMethod = value => {
state.dvTree = [...state.dvTree].filter(item => item.name.includes(value))
}
const dsTreeFilterMethod = value => {
state.dsTree = [...state.dsTree].filter(item => item.name.includes(value))
}
const dvTreeSelect = element => {
state.form.pid = element.id
}
const dsTreeSelect = element => {
state.form.datasetFolderPid = element.id
}
const close = () => {
emits('closeDraw')
state.appApplyDrawer = false
@ -204,8 +291,14 @@ const close = () => {
const saveApp = () => {
appSaveForm.value?.validate(valid => {
if (valid) {
const viewIds = []
const dsIds = []
// datasource
appData.value['datasourceInfo'] = state.appData.datasourceInfo
dvInfo.value['pid'] = state.form.pid
dvInfo.value['name'] = state.form.name
dvInfo.value['datasetFolderPid'] = state.form.datasetFolderPid
dvInfo.value['datasetFolderName'] = state.form.datasetFolderName
dvInfo.value['dataState'] = 'ready'
emits('saveApp')
} else {
return false
}
@ -241,6 +334,7 @@ defineExpose({
line-height: 22px;
padding-left: 10px;
margin: 24px 0 16px 0;
color: var(--ed-text-color-regular);
&::before {
content: '';
@ -253,4 +347,37 @@ defineExpose({
background: #3370ff;
}
}
.custom-tree-node {
display: flex;
align-items: center;
span {
margin-left: 8.75px;
width: 120px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.datasource-link {
color: var(--ed-text-color-regular);
font-size: 12px;
font-weight: 500;
width: 100%;
.head {
width: 100%;
}
.content {
width: 100%;
margin-top: 8px;
}
}
.icon-center {
padding: 0 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>

View File

@ -326,13 +326,15 @@ onMounted(async () => {
console.error('can not find watermark info')
}
let deTemplateData
let preName
if (createType === 'template') {
const templateParamsApply = JSON.parse(Base64.decode(decodeURIComponent(templateParams + '')))
await decompressionPre(templateParamsApply, result => {
deTemplateData = result
preName = deTemplateData.baseInfo?.preName
})
}
dvMainStore.createInit('dataV', null, pid, watermarkBaseInfo)
dvMainStore.createInit('dataV', null, pid, watermarkBaseInfo, preName)
nextTick(() => {
state.canvasInitStatus = true
dvMainStore.setDataPrepareState(true)
@ -342,6 +344,7 @@ onMounted(async () => {
dvMainStore.setComponentData(deTemplateData['componentData'])
dvMainStore.setCanvasStyle(deTemplateData['canvasStyleData'])
dvMainStore.setCanvasViewInfo(deTemplateData['canvasViewInfo'])
dvMainStore.setAppDataInfo(deTemplateData['appData'])
setTimeout(() => {
snapshotStore.recordSnapshotCache()
}, 1500)

View File

@ -1200,6 +1200,21 @@ const treeProps = {
return (!data.children?.length && !data.leaf) || data.extraFlag < 0
}
}
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
}
const getDsIcon = data => {
if (pluginDs?.value.length === 0) return null
if (!data.leaf) return null
const arr = pluginDs.value.filter(ele => {
return ele.type === data.type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconName = data => {
if (!data.leaf) return 'dv-folder'
return `${data.type}-ds`
@ -1280,7 +1295,10 @@ const getDsIconName = data => {
<template #default="{ data: { name, leaf, type, extraFlag } }">
<div class="flex-align-center icon">
<el-icon>
<icon :name="getDsIconName({ leaf, type })"></icon>
<icon
:static-content="getDsIcon({ leaf, type })"
:name="getDsIconName({ leaf, type })"
></icon>
</el-icon>
<span v-if="!leaf || extraFlag > -1">{{ name }}</span>
<el-tooltip effect="dark" v-else :content="`无效数据源:${name}`" placement="top">
@ -1975,6 +1993,10 @@ const getDsIconName = data => {
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</template>
<style lang="less" scoped>

View File

@ -1,6 +1,8 @@
<script lang="ts" setup>
import { shallowRef, PropType, computed } from 'vue'
import { dsTypes, typeList, nameMap } from './option'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { XpackComponent } from '@/components/plugin'
export type DsType = 'OLTP' | 'OLAP' | 'DL' | 'OTHER' | 'LOCAL' | 'latestUse' | 'all'
const props = defineProps({
@ -80,6 +82,25 @@ const getDatasourceTypes = () => {
})
}
getDatasourceTypes()
const loadDsPlugin = data => {
data.forEach(item => {
const { name, category, type, icon, extraParams, staticMap } = item
const node = {
name,
catalog: category,
type,
icon,
extraParams,
isPlugin: true,
staticMap
}
const index = typeList.findIndex(ele => ele === node.catalog)
if (index !== -1) {
databaseList.value[index].push(node)
}
})
}
const emits = defineEmits(['selectDsType'])
const selectDs = ({ type }) => {
emits('selectDsType', type)
@ -95,12 +116,18 @@ const selectDs = ({ type }) => {
<div class="item-container">
<div v-for="db in ele.dbList" :key="db.type" class="db-card" @click="selectDs(db)">
<el-icon class="icon-border">
<Icon :name="`${db.type}-ds`"></Icon>
<Icon v-if="db['isPlugin']" :static-content="db.icon"></Icon>
<Icon v-else :name="`${db.type}-ds`"></Icon>
</el-icon>
<p class="db-name">{{ db.name }}</p>
</div>
</div>
</template>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</template>

View File

@ -0,0 +1,629 @@
<script lang="ts" setup>
import { ref, reactive, toRefs, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
import { cloneDeep } from 'lodash-es'
import type { Configuration, ApiConfiguration, SyncSetting } from './option'
import { Base64 } from 'js-base64'
import { ElMessage } from 'element-plus-secondary'
const { t } = useI18n()
const prop = defineProps({
form: {
required: false,
default() {
return reactive<{
id: number
name: string
desc: string
type: string
syncSetting?: SyncSetting
configuration?: Configuration
apiConfiguration?: ApiConfiguration[]
paramsConfiguration?: ApiConfiguration[]
}>({
id: 0,
name: '',
desc: '',
type: 'API',
apiConfiguration: []
})
},
type: Object
},
activeStep: {
required: false,
default: 1,
type: Number
}
})
const { form, activeStep } = toRefs(prop)
const state = reactive({
itemRef: []
})
const schemas = ref([])
const dsForm = ref<FormInstance>()
const defaultRule = {
name: [
{
required: true,
message: t('datasource.input_name'),
trigger: 'blur'
},
{
min: 2,
max: 64,
message: t('datasource.input_limit_2_25', [2, 64]),
trigger: 'blur'
}
]
}
const rule = ref<FormRules>(cloneDeep(defaultRule))
const api_table_title = ref('')
const editApiItem = ref()
const defaultApiItem = {
name: '',
deTableName: '',
url: '',
type: '',
serialNumber: 0,
method: 'GET',
request: {
headers: [{}],
arguments: [],
body: {
type: '',
raw: '',
kvs: []
},
authManager: {
verification: '',
username: '',
password: ''
}
},
fields: []
}
const initForm = type => {
form.value.configuration = {
dataBase: '',
jdbcUrl: '',
urlType: 'hostName',
extraParams: '',
username: '',
password: '',
host: '',
authMethod: '',
port: '',
initialPoolSize: 5,
minPoolSize: 5,
maxPoolSize: 5,
queryTimeout: 30
}
schemas.value = []
rule.value = cloneDeep(defaultRule)
setRules()
form.value.type = type
setTimeout(() => {
dsForm.value.clearValidate()
}, 0)
}
const setRules = () => {
const configRules = {
'configuration.jdbcUrl': [
{
required: true,
message: t('datasource.please_input_jdbc_url'),
trigger: 'blur'
}
],
'configuration.dataBase': [
{
required: true,
message: t('datasource.please_input_data_base'),
trigger: 'blur'
}
],
'configuration.authMethod': [
{
required: true,
message: t('datasource.please_select_oracle_type'),
trigger: 'blur'
}
],
'configuration.username': [
{
required: true,
message: t('datasource.please_input_user_name'),
trigger: 'blur'
}
],
'configuration.password': [
{
required: true,
message: t('datasource.please_input_password'),
trigger: 'blur'
}
],
'configuration.host': [
{
required: true,
message: t('datasource._ip_address'),
trigger: 'blur'
}
],
'configuration.extraParams': [
{
required: false,
message: t('datasource.please_input_url'),
trigger: 'blur'
}
],
'configuration.port': [
{
required: true,
message: t('datasource.please_input_port'),
trigger: 'blur'
}
],
'configuration.initialPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.initial_pool_size'),
trigger: 'blur'
}
],
'configuration.minPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.min_pool_size'),
trigger: 'blur'
}
],
'configuration.maxPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.max_pool_size'),
trigger: 'blur'
}
],
'configuration.queryTimeout': [
{
required: true,
message: t('common.inputText') + t('datasource.query_timeout'),
trigger: 'blur'
}
]
}
if (['oracle', 'sqlServer', 'pg', 'redshift', 'db2'].includes(form.value.type)) {
configRules['configuration.schema'] = [
{
required: true,
message: t('datasource.please_choose_schema'),
trigger: 'blur'
}
]
}
if (form.value.type === 'oracle') {
configRules['configuration.connectionType'] = [
{
required: true,
message: t('datasource.connection_mode'),
trigger: 'change'
}
]
}
rule.value = { ...cloneDeep(configRules), ...cloneDeep(defaultRule) }
}
watch(
() => form.value.type,
val => {
if (val !== 'API') {
rule.value = cloneDeep(defaultRule)
setRules()
}
},
{
immediate: true
}
)
const activeName = ref('table')
const showPriority = ref(false)
const submitForm = () => {
dsForm.value.clearValidate()
return dsForm.value.validate
}
const clearForm = () => {
return dsForm.value.clearValidate()
}
const resetForm = () => {
dsForm.value.resetFields()
}
const showSchema = ref(false)
const validatorSchema = () => {
dsForm.value.validateField('configuration.schema')
}
const activeParamsName = ref('')
const activeParamsID = ref(0)
const setActiveName = val => {
gridData.value = val.fields
activeParamsName.value = val.name
activeParamsName.value = val.serialNumber
}
const gridData = ref([])
defineExpose({
submitForm,
resetForm,
initForm,
clearForm
})
</script>
<template>
<div class="editor-detail">
<div class="detail-inner create-dialog">
<el-form
ref="dsForm"
:model="form"
:rules="rule"
label-width="180px"
label-position="top"
require-asterisk-position="right"
>
<el-form-item
:label="t('auth.datasource') + t('chart.name')"
prop="name"
v-show="activeStep !== 2"
>
<el-input
v-model="form.name"
autocomplete="off"
:placeholder="t('datasource.input_name')"
/>
</el-form-item>
<el-form-item :label="t('common.description')" v-show="activeStep !== 2">
<el-input
class="description-text"
type="textarea"
:placeholder="t('common.inputText')"
v-model="form.description"
:row="10"
:maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item
:label="t('datasource.host')"
prop="configuration.host"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
v-model="form.configuration.host"
:placeholder="t('datasource._ip_address')"
autocomplete="off"
/>
</el-form-item>
<el-form-item
:label="t('datasource.port')"
prop="configuration.port"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input-number
v-model="form.configuration.port"
autocomplete="off"
step-strictly
class="text-left"
:min="0"
:placeholder="t('common.inputText') + t('datasource.port')"
controls-position="right"
type="number"
/>
</el-form-item>
<el-form-item
:label="t('datasource.data_base')"
prop="configuration.dataBase"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
v-model="form.configuration.dataBase"
:placeholder="t('datasource.please_input_data_base')"
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.user_name')" v-if="form.type !== 'presto'">
<el-input
:placeholder="t('common.inputText') + t('datasource.user_name')"
v-model="form.configuration.username"
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.password')" v-if="form.type !== 'presto'">
<CustomPassword
:placeholder="t('common.inputText') + t('datasource.password')"
show-password
type="password"
v-model="form.configuration.password"
/>
</el-form-item>
<el-form-item
:label="t('datasource.extra_params')"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
:placeholder="t('common.inputText') + t('datasource.extra_params')"
v-model="form.configuration.extraParams"
autocomplete="off"
/>
</el-form-item>
<span
v-if="!['es', 'api'].includes(form.type)"
class="de-expand"
@click="showPriority = !showPriority"
>{{ t('datasource.priority') }}
<el-icon>
<Icon :name="showPriority ? 'icon_down_outlined' : 'icon_down_outlined-1'"></Icon>
</el-icon>
</span>
<template v-if="showPriority">
<el-row :gutter="24" class="mb16">
<el-col :span="12">
<el-form-item
:label="t('datasource.initial_pool_size')"
prop="configuration.initialPoolSize"
>
<el-input-number
v-model="form.configuration.initialPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.initial_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('datasource.min_pool_size')" prop="configuration.minPoolSize">
<el-input-number
v-model="form.configuration.minPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.min_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item :label="t('datasource.max_pool_size')" prop="configuration.maxPoolSize">
<el-input-number
v-model="form.configuration.maxPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.max_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="`${t('datasource.query_timeout')}(${t('common.second')})`"
prop="configuration.queryTimeout"
>
<el-input-number
v-model="form.configuration.queryTimeout"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.query_timeout')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
</el-row>
</template>
</el-form>
</div>
</div>
</template>
<style lang="less" scoped>
.editor-detail {
width: 100%;
display: flex;
justify-content: center;
.ed-radio {
height: 22px;
}
.mb16 {
:deep(.ed-form-item) {
margin-bottom: 16px;
}
}
.execute-rate-cont {
border-radius: 4px;
margin-top: -8px;
}
.de-select {
width: 100%;
}
.ed-input-number {
width: 100%;
}
:deep(.is-controls-right > span) {
background: #fff;
}
.de-expand {
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: var(--ed-color-primary);
cursor: pointer;
display: inline-flex;
align-items: center;
.ed-icon {
margin-left: 4px;
}
}
:deep(.ed-date-editor.ed-input) {
.ed-input__wrapper {
width: 100%;
}
width: 100%;
}
.simple-cron {
height: 32px;
.ed-select,
.ed-input-number {
width: 140px;
margin: 0 8px;
}
}
.detail-inner {
width: 800px;
padding-top: 8px;
.description-text {
:deep(.ed-textarea__inner) {
height: 92px;
}
}
.base-info {
margin: 24px 0 16px 0;
}
.left-api_params {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border: 1px solid #bbbfc4;
width: 300px;
padding: 16px;
.name-copy {
display: none;
line-height: 24px;
margin-left: 4px;
}
.list-item_primary:hover {
.name-copy {
display: inline;
}
.label {
width: 74% !important;
}
}
}
.right-api_params {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border: 1px solid #bbbfc4;
border-left: none;
width: calc(100% - 200px);
}
.table-info-mr {
margin: 28px 0 12px 0;
.api-tabs {
:deep(.ed-tabs__nav-wrap::after) {
display: none;
}
}
}
.info-update {
height: 22px;
width: 100%;
display: flex;
align-items: center;
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
justify-content: center;
.update-info-line {
width: 208px;
height: 1px;
background: #bcbdbf;
margin: 0 8px;
}
.info-text,
.update-text {
padding-left: 16px;
position: relative;
color: #1f2329;
font-weight: 400;
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-style: normal;
line-height: 22px;
&::before {
width: 8px;
height: 8px;
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border: 1px solid var(--ed-color-primary);
border-radius: 50%;
}
&.active {
font-weight: 500;
}
&.active::before {
border: none;
background: var(--ed-color-primary);
}
}
}
.detail-operate {
text-align: right;
padding: 8px 0;
}
.flex-space {
display: flex;
align-items: center;
}
}
}
</style>

View File

@ -2,11 +2,11 @@
import { reactive, ref, computed, watch, nextTick } from 'vue'
import { ElIcon, ElMessage, ElMessageBox, ElMessageBoxOptions } from 'element-plus-secondary'
import CreatDsGroup from './CreatDsGroup.vue'
import { Icon } from '@/components/icon-custom'
import type { DsType } from './DsTypeList.vue'
import DsTypeList from './DsTypeList.vue'
import { useI18n } from '@/hooks/web/useI18n'
import EditorDetail from './EditorDetail.vue'
import EditorDetailPlugin from './EditorDetailPlugin.vue'
import ExcelDetail from './ExcelDetail.vue'
import { save, update, validate, latestUse, isShowFinishPage, checkRepeat } from '@/api/datasource'
import { Base64 } from 'js-base64'
@ -19,6 +19,9 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import FinishPage from '../FinishPage.vue'
import { cloneDeep } from 'lodash-es'
import { useCache } from '@/hooks/web/useCache'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { XpackComponent, PluginComponent } from '@/components/plugin'
interface Node {
name: string
id: string
@ -70,6 +73,7 @@ const filterText = ref('')
const currentDsType = ref('')
const emits = defineEmits(['refresh'])
const { emitter } = useEmitt()
const isPlugin = ref(false)
const selectDsType = (type: string) => {
currentDsType.value = type
activeStep.value = 1
@ -83,6 +87,7 @@ const selectDsType = (type: string) => {
.some(ele => {
if (ele.type === currentDsType.value) {
dsTree.value.setCurrentNode(ele)
isPlugin.value = ele['isPlugin']
return true
}
return false
@ -93,6 +98,7 @@ const selectDsType = (type: string) => {
const handleDsNodeClick = data => {
if (!data.type) return
selectDsType(data.type)
isPlugin.value = data['isPlugin']
}
const handleNodeClick = (data: Node) => {
currentType.value = data.type
@ -135,25 +141,33 @@ const getDatasourceTypes = () => {
})
}
getDatasourceTypes()
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
data.forEach(item => {
const { name, category, type, icon, extraParams, staticMap } = item
const node = {
name,
category,
catalog: category,
type,
icon,
extraParams,
isPlugin: true,
staticMap
}
const index = typeList.findIndex(ele => ele === node.catalog)
if (index !== -1) {
databaseList[index].push(node)
databaseList.value[index].push(node)
}
})
}
const getPluginStatic = type => {
const arr = pluginDs.value.filter(ele => {
return ele.type === type
})
return arr && arr.length > 0 ? arr[0].staticMap?.index : null
}
const getLatestUseTypes = () => {
latestUse({}).then(res => {
@ -663,7 +677,8 @@ defineExpose({
<template #default="{ node, data }">
<span class="custom-tree-node flex-align-center">
<el-icon v-if="!!data.catalog" class="icon-border" style="width: 18px; height: 18px">
<Icon :name="`${data.type}-ds`"></Icon>
<Icon v-if="data['isPlugin']" :static-content="data.icon"></Icon>
<Icon v-else :name="`${data.type}-ds`"></Icon>
</el-icon>
<span :title="node.label" class="label-tooltip">{{ node.label }}</span>
</span>
@ -687,8 +702,21 @@ defineExpose({
:form="form"
:editDs="editDs"
:active-step="activeApiStep"
v-if="activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible"
v-if="
activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible && !isPlugin
"
></editor-detail>
<plugin-component
:jsname="getPluginStatic(currentDsType)"
ref="detail"
:form="form"
:editDs="editDs"
:active-step="activeApiStep"
v-if="
activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible && isPlugin
"
>
</plugin-component>
<template v-if="activeStep !== 0 && currentDsType == 'Excel'">
<excel-detail :editDs="editDs" ref="excel" :param="form2"></excel-detail>
</template>
@ -738,6 +766,11 @@ defineExpose({
:name="dsInfo.name"
v-if="showFinishPage"
></FinishPage>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</el-drawer>
<creat-ds-group
@ -745,10 +778,6 @@ defineExpose({
@finish="complete"
ref="creatDsFolder"
></creat-ds-group>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</template>
<style lang="less">

View File

@ -45,6 +45,7 @@ import { interactiveStoreWithOut } from '@/store/modules/interactive'
import treeSort from '@/utils/treeSortUtils'
import { useCache } from '@/hooks/web/useCache'
import { useEmbedded } from '@/store/modules/embedded'
import { XpackComponent } from '@/components/plugin'
const route = useRoute()
const interactiveStore = interactiveStoreWithOut()
interface Field {
@ -302,6 +303,26 @@ const showErrorInfo = info => {
dialogErrorInfo.value = true
}
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
}
const getDsIcon = data => {
if (pluginDs?.value.length === 0) return null
if (!data.leaf) return null
const arr = pluginDs.value.filter(ele => {
return ele.type === data.type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconType = type => {
const arr = pluginDs.value.filter(ele => {
return ele.type === type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconName = data => {
if (!data.leaf) return 'dv-folder'
return `${data.type}-ds`
@ -945,7 +966,7 @@ const getMenuList = (val: boolean) => {
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-icon :class="data.leaf && 'icon-border'" style="font-size: 18px">
<Icon :name="getDsIconName(data)"></Icon>
<Icon :static-content="getDsIcon(data)" :name="getDsIconName(data)"></Icon>
</el-icon>
<span
:title="node.label"
@ -1004,7 +1025,10 @@ const getMenuList = (val: boolean) => {
<div class="datasource-info">
<div class="info-method">
<el-icon class="icon-border">
<Icon :name="`${nodeInfo.type}-ds`"></Icon>
<Icon
:static-content="getDsIconType(nodeInfo.type)"
:name="`${nodeInfo.type}-ds`"
></Icon>
</el-icon>
<span :title="nodeInfo.name" class="name ellipsis">
{{ nodeInfo.name }}
@ -1584,6 +1608,11 @@ const getMenuList = (val: boolean) => {
</span>
</template>
</el-dialog>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</template>

View File

@ -46,7 +46,7 @@ public interface DataVisualizationApi {
@PostMapping("/saveCanvas")
@DePermit(value = {"#p0.pid + ':manage'"}, busiFlag = "#p0.type")
@Operation(summary = "画布保存")
String saveCanvas(@RequestBody DataVisualizationBaseRequest request);
String saveCanvas(@RequestBody DataVisualizationBaseRequest request) throws Exception;
@PostMapping("/updateCanvas")
@DePermit(value = {"#p0.id + ':manage'"}, busiFlag = "#p0.type")

View File

@ -1,6 +1,9 @@
package io.dataease.api.visualization.request;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationExport2AppVO;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -32,11 +35,20 @@ public class DataVisualizationBaseRequest extends DataVisualizationVO {
private String source;
// 定时报告id
@JsonSerialize(using = ToStringSerializer.class)
private Long reportId;
// 定时报告任务id
@JsonSerialize(using = ToStringSerializer.class)
private Long taskId;
@JsonSerialize(using = ToStringSerializer.class)
// 数据集分组PID
private Long datasetFolderPid;
// 数据集分组名称
private String datasetFolderName;
public DataVisualizationBaseRequest(Long id,String busiFlag) {
this.busiFlag = busiFlag;

View File

@ -79,4 +79,9 @@ public class AppCoreDatasourceVO implements Serializable {
*/
private String taskStatus;
/**
* 映射系统数据源ID
*/
private Long systemDatasourceId;
}

View File

@ -2,10 +2,13 @@ package io.dataease.api.visualization.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.google.gson.Gson;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.utils.JsonUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.HashMap;
@ -156,7 +159,7 @@ public class DataVisualizationVO implements Serializable {
/**
* 定时报告自定义过滤数据
*/
private Map<Long,VisualizationReportFilterVO> reportFilterInfo = new HashMap<>();
private Map<Long, VisualizationReportFilterVO> reportFilterInfo = new HashMap<>();
/**
* 水印信息
@ -171,10 +174,10 @@ public class DataVisualizationVO implements Serializable {
/**
* 应用信息
*/
private String appData;
private VisualizationExport2AppVO appData;
public DataVisualizationVO(Long id, String name, String type, Integer version, String canvasStyleData, String componentData,String appData, Map<Long, ChartViewDTO> canvasViewInfo, Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo) {
public DataVisualizationVO(Long id, String name, String type, Integer version, String canvasStyleData, String componentData,String appDataStr, Map<Long, ChartViewDTO> canvasViewInfo, Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo) {
this.id = id;
this.name = name;
this.type = type;
@ -182,7 +185,9 @@ public class DataVisualizationVO implements Serializable {
this.componentData = componentData;
this.canvasViewInfo = canvasViewInfo;
this.extendDataInfo = extendDataInfo;
this.appData = appData;
if(StringUtils.isNotEmpty(appDataStr)){
this.appData= JsonUtil.parseObject(appDataStr,VisualizationExport2AppVO.class);
}
this.version = version;
}
}

View File

@ -0,0 +1,48 @@
package io.dataease.api.visualization.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@Data
public class VisualizationBaseInfoVO {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String name;
private String label;
private String nodeType;
@JsonSerialize(using = ToStringSerializer.class)
private Long pid;
/**
* 移动端布局
*/
private String mobileLayout;
/**
* 创建时间
*/
private Long createTime;
/**
* 创建人
*/
private String createBy;
/**
* 更新时间
*/
private Long updateTime;
/**
* 更新人
*/
private String updateBy;
}

View File

@ -1,6 +1,7 @@
package io.dataease.api.visualization.vo;
import com.google.gson.Gson;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import lombok.Data;
import java.util.ArrayList;
@ -17,27 +18,27 @@ public class VisualizationExport2AppVO {
private String visualizationViewsInfo;
private String chartViewsInfo;
List<AppCoreChartViewVO> chartViewsInfo;
private String datasetGroupsInfo;
List<AppCoreDatasetGroupVO> datasetGroupsInfo;
private String datasetTablesInfo;
List<AppCoreDatasetTableVO> datasetTablesInfo;
private String datasetTableFieldsInfo;
List<AppCoreDatasetTableFieldVO> datasetTableFieldsInfo;
private String datasourceInfo;
List<AppCoreDatasourceVO> datasourceInfo;
private String datasourceTaskInfo;
List<AppCoreDatasourceTaskVO> datasourceTaskInfo;
private String linkJumps;
List<VisualizationLinkJumpVO> linkJumps;
private String linkJumpInfos;
List<VisualizationLinkJumpInfoVO> linkJumpInfos;
private String linkJumpTargetInfos;
List<VisualizationLinkJumpTargetViewInfoVO> linkJumpTargetInfos;
private String linkages;
List<VisualizationLinkageVO> linkages;
private String linkageFields;
List<VisualizationLinkageFieldVO> linkageFields;
public VisualizationExport2AppVO() {
@ -58,20 +59,18 @@ public class VisualizationExport2AppVO {
List<VisualizationLinkJumpTargetViewInfoVO> linkJumpTargetViewVOInfo,
List<VisualizationLinkageVO> linkagesVOInfo,
List<VisualizationLinkageFieldVO> linkageFieldVOInfo) {
List<Object> empty = new ArrayList<>();
Gson gson = new Gson();
this.checkStatus = true;
this.checkMes = "success";
this.chartViewsInfo = gson.toJson(chartViewVOInfo != null ? chartViewVOInfo : empty);
this.datasetGroupsInfo = gson.toJson(datasetGroupVOInfo != null ? datasetGroupVOInfo : empty);
this.datasetTablesInfo = gson.toJson(datasetTableVOInfo != null ? datasetTableVOInfo : empty);
this.datasetTableFieldsInfo = gson.toJson(datasetTableFieldVOInfo != null ? datasetTableFieldVOInfo : empty);
this.datasourceTaskInfo = gson.toJson(datasourceTaskVOInfo != null ? datasourceTaskVOInfo : empty);
this.datasourceInfo = gson.toJson(datasourceVOInfo != null ? datasourceVOInfo : empty);
this.linkJumps = gson.toJson(linkJumpVOInfo != null ? linkJumpVOInfo : empty);
this.linkJumpInfos = gson.toJson(linkJumpInfoVOInfo != null ? linkJumpInfoVOInfo : empty);
this.linkJumpTargetInfos = gson.toJson(linkJumpTargetViewVOInfo != null ? linkJumpTargetViewVOInfo : empty);
this.linkages = gson.toJson(linkagesVOInfo != null ? linkagesVOInfo : empty);
this.linkageFields = gson.toJson(linkageFieldVOInfo != null ? linkageFieldVOInfo : empty);
this.chartViewsInfo = chartViewVOInfo != null ? chartViewVOInfo : new ArrayList<>();
this.datasetGroupsInfo = datasetGroupVOInfo != null ? datasetGroupVOInfo : new ArrayList<>();
this.datasetTablesInfo = datasetTableVOInfo != null ? datasetTableVOInfo : new ArrayList<>();
this.datasetTableFieldsInfo = datasetTableFieldVOInfo != null ? datasetTableFieldVOInfo : new ArrayList<>();
this.datasourceTaskInfo = datasourceTaskVOInfo != null ? datasourceTaskVOInfo : new ArrayList<>();
this.datasourceInfo = datasourceVOInfo != null ? datasourceVOInfo : new ArrayList<>();
this.linkJumps = linkJumpVOInfo != null ? linkJumpVOInfo : new ArrayList<>();
this.linkJumpInfos = linkJumpInfoVOInfo != null ? linkJumpInfoVOInfo : new ArrayList<>();
this.linkJumpTargetInfos = linkJumpTargetViewVOInfo != null ? linkJumpTargetViewVOInfo : new ArrayList<>();
this.linkages = linkagesVOInfo != null ? linkagesVOInfo : new ArrayList<>();
this.linkageFields = linkageFieldVOInfo != null ? linkageFieldVOInfo : new ArrayList<>();
}
}

View File

@ -0,0 +1,16 @@
package io.dataease.extensions.datasource.dto;
import lombok.Data;
/**
* @Author Junjun
*/
@Data
public class DsTypeDTO {
private String type;
private String name;
private String catalog;
private String prefix;
private String suffix;
}

View File

@ -20,12 +20,16 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class ProviderFactory {
public static Provider getProvider(String type) {
public static Provider getProvider(String type) throws DEException {
List<String> list = Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList();
if (list.contains(type)) {
return SpringContextUtil.getApplicationContext().getBean("calciteProvider", Provider.class);
}
return getInstance(type);
Provider instance = getInstance(type);
if (instance == null) {
DEException.throwException("插件异常,请检查插件");
}
return instance;
}
public static Provider getDefaultProvider() {

View File

@ -1,4 +1,4 @@
package io.dataease.datasource.provider;
package io.dataease.extensions.datasource.provider;
import java.sql.*;
import java.util.Properties;
@ -43,4 +43,4 @@ public class DriverShim implements Driver {
public Connection connect(String u, Properties p) throws SQLException {
return this.driver.connect(u, p);
}
}
}

View File

@ -1,4 +1,4 @@
package io.dataease.datasource.provider;
package io.dataease.extensions.datasource.provider;
import java.io.File;
@ -94,4 +94,4 @@ public class ExtendedJdbcClassLoader extends URLClassLoader {
throw new IOException("Error, could not add URL to system classloader");
}
}
}
}

View File

@ -14,6 +14,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -38,6 +39,22 @@ public abstract class Provider {
public abstract List<TableField> fetchTableField(DatasourceRequest datasourceRequest) throws DEException;
public abstract void hidePW(DatasourceDTO datasourceDTO);
public Statement getStatement(Connection connection, int queryTimeout) {
if (connection == null) {
DEException.throwException("Failed to get connection!");
}
Statement stat = null;
try {
stat = connection.createStatement();
stat.setQueryTimeout(queryTimeout);
} catch (Exception e) {
DEException.throwException(e.getMessage());
}
return stat;
}
public String rebuildSQL(String sql, SQLMeta sqlMeta, boolean crossDs, Map<Long, DatasourceSchemaDTO> dsMap) {
logger.info("calcite sql: " + sql);
if (crossDs) {

View File

@ -0,0 +1,54 @@
package io.dataease.extensions.datasource.vo;
import lombok.Data;
import java.util.List;
/**
* @Author Junjun
*/
@Data
public class PluginDatasourceType extends Configuration {
private List<String> illegalParameters;
private List<String> showTableSqls;
static public enum DatasourceType {
hive("hive", "Apache Hive", "DL", "`", "`");
private String type;
private String name;
private String catalog;
private String prefix;
private String suffix;
DatasourceType(String type, String name, String catalog, String prefix, String suffix) {
this.type = type;
this.name = name;
this.catalog = catalog;
this.prefix = prefix;
this.suffix = suffix;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
public String getCatalog() {
return catalog;
}
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
}
}