feat[数据源]: 数据源支持ssh隧道

This commit is contained in:
taojinlong 2024-07-18 18:21:33 +08:00
commit 514436de7e
88 changed files with 2225 additions and 350 deletions

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

@ -13,6 +13,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;
@ -491,6 +493,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());
@ -1106,20 +1109,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

@ -5,11 +5,11 @@ import java.io.Serializable;
/**
* <p>
*
* 模板表
* </p>
*
* @author fit2cloud
* @since 2024-04-11
* @since 2024-07-17
*/
@TableName("visualization_template")
public class VisualizationTemplate implements Serializable {
@ -37,12 +37,12 @@ public class VisualizationTemplate implements Serializable {
private Integer level;
/**
* 种类 dataV or dashboard 目录或者文件夹
* 种类 dataV or dashboard 目录或者文件夹
*/
private String dvType;
/**
* 节点类型 folder or panel 目录或者文件夹
* 节点类型 app or template 应用 或者 模板
*/
private String nodeType;
@ -62,7 +62,7 @@ public class VisualizationTemplate implements Serializable {
private String snapshot;
/**
* 类型 system 系统内置 self 用户自建
* 类型 system 系统内置 self 用户自建
*/
private String templateType;
@ -81,14 +81,16 @@ public class VisualizationTemplate implements Serializable {
*/
private String dynamicData;
/**
* app数据
*/
private String appData;
/**
* 使用次数
*/
private Integer useCount;
/**
* 使用仪表板的版本
*/
private Integer version;
public String getId() {
@ -195,6 +197,14 @@ public class VisualizationTemplate implements Serializable {
this.dynamicData = dynamicData;
}
public String getAppData() {
return appData;
}
public void setAppData(String appData) {
this.appData = appData;
}
public Integer getUseCount() {
return useCount;
}
@ -227,6 +237,7 @@ public class VisualizationTemplate implements Serializable {
", templateStyle = " + templateStyle +
", templateData = " + templateData +
", dynamicData = " + dynamicData +
", appData = " + appData +
", useCount = " + useCount +
", version = " + version +
"}";

View File

@ -6,11 +6,11 @@ import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* 模板表 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2024-04-11
* @since 2024-07-17
*/
@Mapper
public interface VisualizationTemplateMapper extends BaseMapper<VisualizationTemplate> {

View File

@ -52,6 +52,6 @@ public interface ExtVisualizationTemplateMapper{
List<VisualizationLinkJumpInfoVO> findAppLinkJumpInfoInfo(@Param("dvId") Long dvId);
List<VisualizationLinkJumpTargetViewInfoVO> findAppJumpTargetViewInfo(@Param("dvId") Long dvId);
List<VisualizationLinkJumpTargetViewInfoVO> findAppLinkJumpTargetViewInfoInfo(@Param("dvId") Long dvId);
}

View File

@ -80,10 +80,10 @@ public class TemplateManageService implements TemplateManageApi {
request.setId(UUID.randomUUID().toString());
request.setCreateTime(System.currentTimeMillis());
request.setCreateBy(AuthUtils.getUser().getUserId().toString());
if ("template".equals(request.getNodeType())) {
if ("template".equals(request.getNodeType()) || "app".equals(request.getNodeType())) {
//Store static resource into the server
staticResourceServer.saveFilesToServe(request.getStaticResource());
String snapshotName = "template-" + request.getId() + ".jpeg";
String snapshotName = request.getNodeType() + "-" + request.getId() + ".jpeg";
staticResourceServer.saveSingleFileToServe(snapshotName, request.getSnapshot().replace("data:image/jpeg;base64,", ""));
request.setSnapshot("/" + UPLOAD_URL_PREFIX + '/' + snapshotName);
}
@ -107,7 +107,7 @@ public class TemplateManageService implements TemplateManageApi {
VisualizationTemplate template = new VisualizationTemplate();
BeanUtils.copyBean(template, request);
if(template.getVersion() == null){
if (template.getVersion() == null) {
template.setVersion(2);
}
templateMapper.insert(template);
@ -137,7 +137,7 @@ public class TemplateManageService implements TemplateManageApi {
}
VisualizationTemplate template = new VisualizationTemplate();
BeanUtils.copyBean(template, request);
if(template.getVersion() == null){
if (template.getVersion() == null) {
template.setVersion(2);
}
templateMapper.updateById(template);
@ -191,7 +191,7 @@ public class TemplateManageService implements TemplateManageApi {
@Override
public String checkCategoryTemplateBatchNames(TemplateManageRequest request) {
Long result = extTemplateMapper.checkCategoryTemplateBatchNames(request.getTemplateNames(),request.getCategories(),request.getTemplateArray());
Long result = extTemplateMapper.checkCategoryTemplateBatchNames(request.getTemplateNames(), request.getCategories(), request.getTemplateArray());
if (result == 0) {
return CommonConstants.CHECK_RESULT.NONE;
} else {
@ -272,7 +272,7 @@ public class TemplateManageService implements TemplateManageApi {
public List<String> findCategoriesByTemplateIds(TemplateManageRequest request) throws Exception {
if (!CollectionUtils.isEmpty(request.getTemplateArray())) {
List<String> result = extTemplateMapper.findTemplateArrayCategories(request.getTemplateArray());
if(!CollectionUtils.isEmpty(result) &&result.size() == 1 ){
if (!CollectionUtils.isEmpty(result) && result.size() == 1) {
return Arrays.stream(result.get(0).split(",")).toList();
}
}

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,92 +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 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();
// 模板市场记录
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");
}
templateStyle = templateFileInfo.getCanvasStyleData();
templateData = templateFileInfo.getComponentData();
dynamicData = templateFileInfo.getDynamicData();
staticResource = templateFileInfo.getStaticResource();
name = templateFileInfo.getName();
dvType = templateFileInfo.getDvType();
version = templateFileInfo.getVersion();
// 模板市场记录
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, canvasViewInfo, null);
}
@Override
@ -415,10 +541,10 @@ public class DataVisualizationServer implements DataVisualizationApi {
List<AppCoreDatasourceVO> datasourceVOInfo = null;
List<AppCoreDatasourceTaskVO> datasourceTaskVOInfo = null;
//TODO 获取所有视图信息
if (CollectionUtils.isEmpty(viewIds)) {
if (!CollectionUtils.isEmpty(viewIds)) {
chartViewVOInfo = appTemplateMapper.findAppViewInfo(viewIds);
}
if (CollectionUtils.isEmpty(dsIds)) {
if (!CollectionUtils.isEmpty(dsIds)) {
datasetGroupVOInfo = appTemplateMapper.findAppDatasetGroupInfo(dsIds);
datasetTableVOInfo = appTemplateMapper.findAppDatasetTableInfo(dsIds);
datasetTableFieldVOInfo = appTemplateMapper.findAppDatasetTableFieldInfo(dsIds);
@ -430,7 +556,7 @@ public class DataVisualizationServer implements DataVisualizationApi {
List<VisualizationLinkageFieldVO> linkageFieldVOInfo = appTemplateMapper.findAppLinkageFieldInfo(dvId);
List<VisualizationLinkJumpVO> linkJumpVOInfo = appTemplateMapper.findAppLinkJumpInfo(dvId);
List<VisualizationLinkJumpInfoVO> linkJumpInfoVOInfo = appTemplateMapper.findAppLinkJumpInfoInfo(dvId);
List<VisualizationLinkJumpTargetViewInfoVO> listJumpTargetViewInfoVO = appTemplateMapper.findAppJumpTargetViewInfo(dvId);
List<VisualizationLinkJumpTargetViewInfoVO> listJumpTargetViewInfoVO = appTemplateMapper.findAppLinkJumpTargetViewInfoInfo(dvId);
return new VisualizationExport2AppVO(chartViewVOInfo, datasetGroupVOInfo, datasetTableVOInfo,
datasetTableFieldVOInfo, datasourceVOInfo, datasourceTaskVOInfo,

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项目配置

View File

@ -48,3 +48,7 @@ INSERT INTO `core_copilot_config` VALUES (1, 'https://copilot-demo.test.fit2clou
UPDATE `core_sys_setting` SET `pkey` = 'ai.baseUrl', `pval` = 'https://maxkb.fit2cloud.com/ui/chat/2ddd8b594ce09dbb?mode=embed', `type` = 'text', `sort` = 0 WHERE `id` = 3;
ALTER TABLE `visualization_template`
MODIFY COLUMN `node_type` varchar(255) NULL DEFAULT NULL COMMENT '节点类型 app or template 应用 或者 模板' AFTER `dv_type`,
ADD COLUMN `app_data` longtext NULL COMMENT 'app数据' AFTER `dynamic_data`;

View File

@ -69,3 +69,7 @@ CREATE TABLE `core_api_traffic`
PRIMARY KEY (`id`)
);
ALTER TABLE `visualization_template`
MODIFY COLUMN `node_type` varchar(255) NULL DEFAULT NULL COMMENT '节点类型 app or template 应用 或者 模板' AFTER `dv_type`,
ADD COLUMN `app_data` longtext NULL COMMENT 'app数据' AFTER `dynamic_data`;

View File

@ -95,10 +95,10 @@ export const getComponentInfo = dvId => {
})
}
export const export2AppCheck = dvId => {
return request.get({
url: '/dataVisualization/export2AppCheck/' + dvId,
method: 'get',
export const export2AppCheck = params => {
return request.post({
url: '/dataVisualization/export2AppCheck',
data: params,
loading: true
})
}

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

View File

@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.68833 5.68831C5.91562 5.46102 6.2239 5.33333 6.54534 5.33333H17.4547C17.7761 5.33333 18.0844 5.46102 18.3117 5.68831C18.539 5.91561 18.6667 6.22389 18.6667 6.54533V17.4547C18.6667 17.7761 18.539 18.0844 18.3117 18.3117C18.0844 18.539 17.7761 18.6667 17.4547 18.6667H6.54534C5.87601 18.6667 5.33334 18.124 5.33334 17.4547V6.54533C5.33334 6.22389 5.46104 5.91561 5.68833 5.68831ZM6.66668 10.6667V13.3333H9.33334V10.6667H6.66668ZM6.66668 14.6667V17.3333H9.33334V14.6667H6.66668ZM10.6667 17.3333H13.3333V14.6667H10.6667V17.3333ZM14.6667 17.3333H17.3333V14.6667H14.6667V17.3333ZM17.3333 13.3333V10.6667H14.6667V13.3333H17.3333ZM13.3333 10.6667H10.6667V13.3333H13.3333V10.6667ZM17.3333 6.66666H6.66668V9.33333H17.3333V6.66666Z" fill="#646A73"/>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.68833 5.68831C5.91562 5.46102 6.2239 5.33333 6.54534 5.33333H17.4547C17.7761 5.33333 18.0844 5.46102 18.3117 5.68831C18.539 5.91561 18.6667 6.22389 18.6667 6.54533V17.4547C18.6667 17.7761 18.539 18.0844 18.3117 18.3117C18.0844 18.539 17.7761 18.6667 17.4547 18.6667H6.54534C5.87601 18.6667 5.33334 18.124 5.33334 17.4547V6.54533C5.33334 6.22389 5.46104 5.91561 5.68833 5.68831ZM6.66668 10.6667V13.3333H9.33334V10.6667H6.66668ZM6.66668 14.6667V17.3333H9.33334V14.6667H6.66668ZM10.6667 17.3333H13.3333V14.6667H10.6667V17.3333ZM14.6667 17.3333H17.3333V14.6667H14.6667V17.3333ZM17.3333 13.3333V10.6667H14.6667V13.3333H17.3333ZM13.3333 10.6667H10.6667V13.3333H13.3333V10.6667ZM17.3333 6.66666H6.66668V9.33333H17.3333V6.66666Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 894 B

After

Width:  |  Height:  |  Size: 867 B

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'
@ -24,6 +24,7 @@ import OuterParamsSet from '@/components/visualization/OuterParamsSet.vue'
import MultiplexingCanvas from '@/views/common/MultiplexingCanvas.vue'
import ComponentButtonLabel from '@/components/visualization/ComponentButtonLabel.vue'
import DeFullscreen from '@/components/visualization/common/DeFullscreen.vue'
import DeAppApply from '@/views/common/DeAppApply.vue'
let nameEdit = ref(false)
let inputName = ref('')
let nameInput = ref(null)
@ -31,14 +32,25 @@ 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, dvInfo, editMode } = storeToRefs(dvMainStore)
const { componentData, canvasStyleData, canvasViewInfo, dvInfo, editMode } =
storeToRefs(dvMainStore)
let scaleEdit = 100
const { wsCache } = useCache('localStorage')
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()) {
@ -89,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()
@ -342,6 +369,14 @@ const fullScreenPreview = () => {
cur-canvas-type="dataV"
ref="resourceGroupOpt"
/>
<de-app-apply
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>
<multiplexing-canvas ref="multiplexingRef"></multiplexing-canvas>

View File

@ -1,11 +1,5 @@
<script setup lang="ts">
<script setup lang="ts"></script>
</script>
<template></template>
<template>
</template>
<style scoped lang="less">
</style>
<style scoped lang="less"></style>

View File

@ -1,7 +1,7 @@
<template>
<el-drawer
:title="'应用导出'"
v-model:visible="state.applyDownloadDrawer"
v-model="state.applyDownloadDrawer"
custom-class="de-user-drawer"
size="600px"
direction="rtl"
@ -11,13 +11,12 @@
ref="applyDownloadForm"
:model="state.form"
:rules="state.rule"
size="small"
class="de-form-item"
label-width="180px"
label-position="right"
label-position="top"
>
<el-form-item :label="'应用名称'" prop="appName">
<el-input v-model="form.appName" autocomplete="off" :placeholder="'请输入名称'" />
<el-input v-model="state.form.appName" autocomplete="off" :placeholder="'请输入名称'" />
</el-form-item>
<el-form-item :label="'应用版本号'" prop="version">
<el-input v-model="state.form.version" autocomplete="off" />
@ -38,22 +37,43 @@
</el-form-item>
</el-form>
</div>
<div class="app-export-bottom">
<template #footer>
<div class="apply" style="width: 100%">
<el-button secondary @click="close">{{ $t('commons.cancel') }} </el-button>
<el-button type="primary" @click="downloadApp">{{ $t('app_template.export') }} </el-button>
<el-button type="primary" @click="downloadApp">导出</el-button>
</div>
</div>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { ElButton, ElDrawer, ElForm, ElFormItem, ElInput } from 'element-plus-secondary'
import { reactive, ref } from 'vue'
import { reactive, ref, toRefs } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { export2AppCheck } from '@/api/visualization/dataVisualization'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
const { t } = useI18n()
const emits = defineEmits(['closeDraw', 'downLoadApp'])
const applyDownloadForm = ref(null)
const dvMainStore = dvMainStoreWithOut()
const props = defineProps({
componentData: {
type: Object,
required: true
},
canvasViewInfo: {
type: Object,
required: true
},
dvInfo: {
type: Object,
required: true
}
})
const { componentData, canvasViewInfo, dvInfo } = toRefs(props)
const state = reactive({
applyDownloadDrawer: false,
form: {
@ -61,7 +81,7 @@ const state = reactive({
icon: null,
version: null,
creator: null,
required: '1.16.0',
required: '2.8.0',
description: null
},
rule: {
@ -105,6 +125,7 @@ const state = reactive({
})
const init = params => {
console.log('init==')
state.applyDownloadDrawer = true
state.form = params
}
@ -114,11 +135,48 @@ const close = () => {
state.applyDownloadDrawer = false
}
const gatherAppInfo = (viewIds, dsIds) => {
componentData.value.forEach(item => {
if (item.component === 'UserView' && canvasViewInfo.value[item.id]) {
const viewDetails = canvasViewInfo.value[item.id]
const { id, tableId } = viewDetails
viewIds.push(id)
dsIds.push(tableId)
} else if (item.component === 'Group') {
item.propValue.forEach(groupItem => {
const viewDetails = canvasViewInfo.value[groupItem.id]
const { id, tableId } = viewDetails
viewIds.push(id)
dsIds.push(tableId)
})
} else if (item.component === 'DeTabs') {
item.propValue.forEach(tabItem => {
tabItem.componentData.forEach(tabComponent => {
const viewDetails = canvasViewInfo.value[tabComponent.id]
const { id, tableId } = viewDetails
viewIds.push(id)
dsIds.push(tableId)
})
})
}
})
}
const downloadApp = () => {
applyDownloadForm.value?.validate(valid => {
if (valid) {
emits('downLoadApp', state.form)
state.applyDownloadDrawer = false
const viewIds = []
const dsIds = []
gatherAppInfo(viewIds, dsIds)
export2AppCheck({ dvId: dvInfo.value.id, viewIds, dsIds }).then(rsp => {
const params = {
...rsp.data,
...state.form,
visualizationInfo: JSON.stringify(dvInfo.value)
}
emits('downLoadApp', params)
state.applyDownloadDrawer = false
})
} else {
return false
}

View File

@ -1336,7 +1336,14 @@ const svgClass = computed(() => {
</script>
<template>
<component :class="svgClass" :is="iconMap[name]"></component>
<div
class="svg-container"
v-if="staticContent"
v-html="staticContent"
:class="svgClass"
aria-hidden="true"
></div>
<component v-else :class="svgClass" :is="iconMap[name]"></component>
</template>
<style lang="less" scope>
.svg-icon {

View File

@ -49,6 +49,18 @@ const importProxy = (bytesArray: any[]) => {
})
}
const loadXpack = async () => {
if (window['DEXPack']) {
const xpack = await window['DEXPack'].mapping[attrs.jsname]
plugin.value = xpack.default
}
}
useEmitt({
name: 'load-xpack',
callback: loadXpack
})
const loadComponent = () => {
loading.value = true
const byteArray = wsCache.get(`de-plugin-proxy`)
@ -103,7 +115,8 @@ onMounted(async () => {
if (window['DEXPack']) {
const xpack = await window['DEXPack'].mapping[attrs.jsname]
plugin.value = xpack.default
} else {
} else if (!window._de_xpack_not_loaded) {
window._de_xpack_not_loaded = true
window['Vue'] = Vue
window['Axios'] = axios
window['Pinia'] = Pinia
@ -115,8 +128,7 @@ onMounted(async () => {
}
loadDistributed().then(async res => {
new Function(res.data)()
const xpack = await window['DEXPack'].mapping[attrs.jsname]
plugin.value = xpack.default
useEmitt().emitter.emit('load-xpack')
})
}
} else {

View File

@ -111,8 +111,6 @@ service.interceptors.request.use(
;(config.headers as AxiosRequestHeaders)['X-DE-LINK-TOKEN'] = linkStore.getLinkToken
} else if (embeddedStore.token) {
;(config.headers as AxiosRequestHeaders)['X-EMBEDDED-TOKEN'] = embeddedStore.token
} else if (wsCache.get('de-ldap-token')) {
;(config.headers as AxiosRequestHeaders)['Authorization'] = wsCache.get('de-ldap-token')
}
if (wsCache.get('user.language')) {
const key = wsCache.get('user.language')

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

@ -245,11 +245,10 @@ const setTreeDefault = () => {
})
if (checkId && tableId) {
const componentObj = fields.value.find(ele => ele.componentId === comId)
const fieldArr = (
const fieldArr =
curComponent.value.optionValueSource === 0
? componentObj?.fields?.dimensionList
: curComponent.value.dataset?.fields
).filter(ele => ele.deType === +curComponent.value.field.deType)
: (fields.value.find(itx => itx.id === tableId) || {}).fields?.dimensionList
fields.value.forEach(ele => {
if (curComponent.value.checkedFields.includes(ele.componentId)) {
if (datasetFieldList.value.find(itx => itx.id === ele.componentId)?.tableId === tableId) {
@ -260,10 +259,10 @@ const setTreeDefault = () => {
const fieldObj = fieldArr.find(element => element.id === checkId)
if (!!curComponent.value.treeFieldList.length) {
const [fir] = curComponent.value.treeFieldList
if (fir.field !== checkId) {
if (fir && fir.field !== checkId) {
curComponent.value.treeFieldList = [fieldObj]
}
} else {
} else if (fieldObj) {
curComponent.value.treeFieldList = [fieldObj]
}
}
@ -1484,7 +1483,10 @@ defineExpose({
value="8"
/>
<el-option
:disabled="!['0', '8', '9'].includes(curComponent.displayType)"
:disabled="
!['0', '8', '9'].includes(curComponent.displayType) ||
!!curComponent.parameters.length
"
label="下拉树"
value="9"
/>
@ -1513,6 +1515,15 @@ defineExpose({
</el-select>
</div>
</div>
<div class="list-item" v-if="curComponent.displayType === '9'">
<div class="label">选项值数量</div>
<div class="value">
<el-radio-group v-model="curComponent.resultMode">
<el-radio :label="0">默认</el-radio>
<el-radio :label="1">全部</el-radio>
</el-radio-group>
</div>
</div>
<div class="list-item" v-if="curComponent.displayType === '9'">
<div class="label" style="width: 135px; height: 26px; line-height: 26px">
下拉树结构设计

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

@ -24,6 +24,7 @@ const DashboardPanel = defineAsyncComponent(
)
const Preview = defineAsyncComponent(() => import('@/views/data-visualization/PreviewCanvas.vue'))
const DashboardEmpty = defineAsyncComponent(() => import('@/views/mobile/panel/DashboardEmpty.vue'))
const props = defineProps({
componentName: propTypes.string.def('Iframe')
@ -41,7 +42,8 @@ const componentMap = {
Datasource,
ScreenPanel,
DashboardPanel,
DatasetEditor
DatasetEditor,
DashboardEmpty
}
const changeCurrentComponent = val => {

View File

@ -1,14 +1,20 @@
<script lang="ts" setup>
import { ref, reactive, onBeforeMount, nextTick } from 'vue'
import { initCanvasData } from '@/utils/canvasUtils'
import { initCanvasData, initCanvasDataMobile } from '@/utils/canvasUtils'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import { useEmbedded } from '@/store/modules/embedded'
import { isMobile } from '@/utils/utils'
import { check } from '@/utils/CrossPermission'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useCache } from '@/hooks/web/useCache'
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
import { ElMessage } from 'element-plus-secondary'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { useI18n } from '@/hooks/web/useI18n'
import VanSticky from 'vant/es/sticky'
import VanNavBar from 'vant/es/nav-bar'
import 'vant/es/nav-bar/style'
import 'vant/es/sticky/style'
const { wsCache } = useCache()
const interactiveStore = interactiveStoreWithOut()
const embeddedStore = useEmbedded()
@ -32,12 +38,12 @@ const checkPer = async resourceId => {
const key = embeddedStore.busiFlag === 'dataV' ? 'screen-weight' : 'panel-weight'
return check(wsCache.get(key), resourceId, 1)
}
const isPc = ref(true)
onBeforeMount(async () => {
const checkResult = await checkPer(embeddedStore.dvId)
if (!checkResult) {
return
}
//
let attachParams
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
@ -56,7 +62,10 @@ onBeforeMount(async () => {
}
}
initCanvasData(
isPc.value = !isMobile()
const req = isPc.value ? initCanvasData : initCanvasDataMobile
req(
embeddedStore.dvId,
embeddedStore.busiFlag,
function ({
@ -66,6 +75,15 @@ onBeforeMount(async () => {
canvasViewInfoPreview,
curPreviewGap
}) {
if (!isPc.value) {
if (!dvInfo.mobileLayout) {
useEmitt().emitter.emit('changeCurrentComponent', 'DashboardEmpty')
return
} else {
dvMainStore.setMobileInPc(true)
dvMainStore.setInMobile(true)
}
}
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
@ -83,7 +101,13 @@ onBeforeMount(async () => {
</script>
<template>
<div class="dashboard-preview" v-if="state.canvasStylePreview">
<div
:class="isPc ? 'dashboard-preview' : 'dv-common-layout-mobile_embedded'"
v-if="state.canvasStylePreview"
>
<van-sticky v-if="!isPc">
<van-nav-bar :title="state.dvInfo.name" />
</van-sticky>
<de-preview
ref="dashboardPreview"
:dv-info="state.dvInfo"
@ -92,6 +116,7 @@ onBeforeMount(async () => {
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
show-position="preview"
:download-status="isPc"
></de-preview>
</div>
</template>
@ -102,3 +127,16 @@ onBeforeMount(async () => {
height: 100%;
}
</style>
<style lang="less">
.dv-common-layout-mobile_embedded {
width: 100%;
height: 100%;
overflow-y: auto;
--van-nav-bar-height: 44px;
--van-nav-bar-arrow-size: 20px;
--van-nav-bar-icon-color: #1f2329;
--van-nav-bar-title-text-color: #1f2329;
--van-font-bold: 500;
--van-nav-bar-title-font-size: 17px;
}
</style>

View File

@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => {
await appStore.setAppModel()
isDesktop = appStore.getDesktop
}
if (isMobile() && to.path !== '/notSupport') {
if (isMobile() && !['/notSupport', '/chart-view'].includes(to.path)) {
done()
loadDone()
if (to.name === 'link') {

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 (
@ -537,7 +539,11 @@ export async function decompressionPre(params, callBack) {
deTemplateData = {
canvasStyleData: sourceCanvasStyle,
componentData: sourceComponentData,
canvasViewInfo: deTemplateDataTemp['canvasViewInfo']
canvasViewInfo: deTemplateDataTemp['canvasViewInfo'],
appData: appData,
baseInfo: {
preName: deTemplateDataTemp.name
}
}
})
.catch(e => {

View File

@ -35,7 +35,7 @@ export function imgUrlTrans(url) {
}
}
export function download2AppTemplate(downloadType, canvasDom, name, callBack?) {
export function download2AppTemplate(downloadType, canvasDom, name, attachParams, callBack?) {
try {
findStaticSource(function (staticResource) {
html2canvas(canvasDom).then(canvas => {
@ -50,11 +50,13 @@ export function download2AppTemplate(downloadType, canvasDom, name, callBack?) {
templateType: 'self',
snapshot: snapshot,
dvType: dvInfo.value.type,
nodeType: downloadType,
version: 3,
canvasStyleData: JSON.stringify(canvasStyleData.value),
componentData: JSON.stringify(componentData.value),
dynamicData: JSON.stringify(canvasViewDataTemplate),
staticResource: JSON.stringify(staticResource || {})
staticResource: JSON.stringify(staticResource || {}),
appData: JSON.stringify(attachParams || {})
}
const blob = new Blob([JSON.stringify(templateInfo)], { type: '' })
if (downloadType === 'template') {

View File

@ -2,6 +2,7 @@
import { shallowRef, defineAsyncComponent, ref, onBeforeUnmount, onBeforeMount } from 'vue'
import { debounce } from 'lodash-es'
import { XpackComponent } from '@/components/plugin'
import { useEmitt } from '@/hooks/web/useEmitt'
const currentComponent = shallowRef()
@ -49,6 +50,11 @@ onBeforeUnmount(() => {
const initIframe = (name: string) => {
currentComponent.value = componentMap[name || 'ViewWrapper']
}
useEmitt({
name: 'changeCurrentComponent',
callback: initIframe
})
</script>
<template>

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

@ -0,0 +1,383 @@
<template>
<el-drawer
:title="'保存应用'"
v-model="state.appApplyDrawer"
custom-class="de-user-drawer"
size="500px"
direction="rtl"
>
<div class="app-export">
<el-form
ref="appSaveForm"
: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="name">
<el-input v-model="state.form.name" autocomplete="off" :placeholder="'请输入名称'" />
</el-form-item>
<el-form-item :label="dvPreName + '所在位置'" prop="pid">
<el-tree-select
style="width: 100%"
@keydown.stop
@keyup.stop
v-model="state.form.pid"
:data="state.dvTree"
:props="state.propsTree"
@node-click="dvTreeSelect"
:filter-method="dvTreeFilterMethod"
:render-after-expand="false"
filterable
>
<template #default="{ data: { name } }">
<span class="custom-tree-node">
<el-icon>
<Icon name="dv-folder"></Icon>
</el-icon>
<span :title="name">{{ name }}</span>
</span>
</template>
</el-tree-select>
</el-form-item>
<el-form-item :label="'数据集分组名称'" prop="datasetFolderName">
<el-input
v-model="state.form.datasetFolderName"
autocomplete="off"
:placeholder="'请输入名称'"
/>
</el-form-item>
<el-form-item label="数据集分组位置" prop="datasetFolderPid">
<el-tree-select
style="width: 100%"
@keydown.stop
@keyup.stop
v-model="state.form.datasetFolderPid"
:data="state.dsTree"
:props="state.propsTree"
@node-click="dsTreeSelect"
:filter-method="dsTreeFilterMethod"
:render-after-expand="false"
filterable
>
<template #default="{ data: { name } }">
<span class="custom-tree-node">
<el-icon>
<Icon name="dv-folder"></Icon>
</el-icon>
<span :title="name">{{ name }}</span>
</span>
</template>
</el-tree-select>
</el-form-item>
<div class="de-row-rules" style="margin: 0 0 16px">
<span>数据源信息</span>
</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="saveApp">保存</el-button>
</div>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import {
ElButton,
ElDrawer,
ElForm,
ElFormItem,
ElInput,
ElTreeSelect
} from 'element-plus-secondary'
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', 'saveApp'])
const appSaveForm = ref(null)
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, appData } = storeToRefs(dvMainStore)
const props = defineProps({
componentData: {
type: Object,
required: true
},
canvasViewInfo: {
type: Object,
required: true
},
curCanvasType: {
type: String,
required: true
},
themes: {
type: String as PropType<EditorTheme>,
default: 'dark'
}
})
const { componentData, canvasViewInfo, curCanvasType, themes } = toRefs(props)
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
},
rule: {
name: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
pid: [
{
required: true,
message: '请选择所属文件夹',
trigger: 'blur'
}
],
datasetFolderName: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
datasetFolderPid: [
{
required: true,
message: '请选择数据集分组所属文件夹',
trigger: 'blur'
}
]
}
})
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 => {
state.appApplyDrawer = true
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
}
const saveApp = () => {
appSaveForm.value?.validate(valid => {
if (valid) {
// 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
}
})
}
defineExpose({
init
})
</script>
<style lang="less" scoped>
.app-export {
width: 100%;
height: calc(100% - 56px);
}
.app-export-bottom {
width: 100%;
height: 56px;
text-align: right;
}
:deep(.ed-drawer__body) {
padding-bottom: 0 !important;
}
.de-row-rules {
display: flex;
align-items: center;
position: relative;
font-size: 14px;
font-weight: 500;
line-height: 22px;
padding-left: 10px;
margin: 24px 0 16px 0;
color: var(--ed-text-color-regular);
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 14px;
width: 2px;
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

@ -299,27 +299,45 @@ const tips = computed(() => {
v-if="copilotInfo.msgType === 'api' && copilotInfo.msgStatus === 1"
>
<template v-if="!renderTable">
<el-icon v-if="activeCommand" class="select-prefix">
<el-icon
:class="!(renderTable || renderTableLocal) && 'active'"
v-if="activeCommand"
class="select-prefix"
>
<Icon :name="curTypeList.find(ele => ele.value === activeCommand).icon" />
</el-icon>
<el-select
popper-class="copilot-select_popper"
class="select-copilot-list"
v-model="activeCommand"
size="small"
@change="changeChartType"
>
<el-option
v-for="item in curTypeList"
:key="item.label"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-tooltip effect="dark" content="切换图表类型" placement="top">
<div
v-show="renderTable || renderTableLocal"
@click="switchChartType(activeCommand)"
class="fake-mask_select"
></div>
</el-tooltip>
<el-tooltip effect="dark" content="切换图表类型" placement="top">
<el-select
popper-class="copilot-select_popper"
class="select-copilot-list"
:class="!(renderTable || renderTableLocal) && 'active'"
v-model="activeCommand"
size="small"
@change="changeChartType"
>
<el-option
v-for="item in curTypeList"
:key="item.label"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-tooltip>
<el-divider direction="vertical" />
</template>
<el-tooltip effect="dark" content="切换至明细表" placement="top">
<el-icon class="ed-icon_chart" @click="switchChartType('table')">
<el-icon
:class="(renderTable || renderTableLocal) && 'active'"
class="ed-icon_chart"
@click="switchChartType('table')"
>
<Icon name="chart-table" />
</el-icon>
</el-tooltip>
@ -345,13 +363,19 @@ const tips = computed(() => {
z-index: 100;
:deep(.ed-input__wrapper) {
background: #3370ff1a;
background: transparent;
box-shadow: none !important;
padding-right: 4px;
.ed-input__inner {
visibility: hidden;
}
}
&.active {
:deep(.ed-input__wrapper) {
background: #3370ff1a;
}
}
}
.chart-type_list {
@ -361,9 +385,24 @@ const tips = computed(() => {
display: flex;
align-items: center;
font-size: 24px;
.fake-mask_select {
width: 40px;
height: 24px;
cursor: pointer;
border-radius: 4px;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
z-index: 101;
&:hover {
background: #1f23291a;
}
}
.ed-icon_chart {
position: relative;
cursor: pointer;
color: #646a73;
&::after {
content: '';
position: absolute;
@ -382,6 +421,14 @@ const tips = computed(() => {
display: block;
}
}
&.active {
color: var(--ed-color-primary, #3370ff);
&::after {
display: block;
background: #3370ff1a;
}
}
}
.select-prefix {
@ -390,7 +437,10 @@ const tips = computed(() => {
top: 50%;
transform: translateY(-50%);
font-size: 16px;
color: var(--ed-color-primary, #3370ff);
color: #646a73;
&.active {
color: var(--ed-color-primary, #3370ff);
}
}
.ed-divider--vertical {
border-color: #1f232926;

View File

@ -172,6 +172,9 @@ const initOpenHandler = newWindow => {
<el-dropdown-item style="width: 118px" @click="downloadAsAppTemplate('template')"
>模板</el-dropdown-item
>
<el-dropdown-item style="width: 118px" @click="downloadAsAppTemplate('app')"
>应用</el-dropdown-item
>
<el-dropdown-item @click="download('img')">{{
t('chart.image')
}}</el-dropdown-item>

View File

@ -104,16 +104,25 @@ const download = type => {
}, 200)
}
const downloadAsAppTemplate = downloadType => {
const fileDownload = (downloadType, attachParams) => {
downloadStatus.value = true
nextTick(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
download2AppTemplate(downloadType, vueDom, state.dvInfo.name, () => {
download2AppTemplate(downloadType, vueDom, state.dvInfo.name, attachParams, () => {
downloadStatus.value = false
})
})
}
const downloadAsAppTemplate = downloadType => {
console.log('===test===' + downloadType)
if (downloadType === 'template') {
fileDownload(downloadType, null)
} else if (downloadType === 'app') {
downLoadToAppPre()
}
}
const downLoadToAppPre = () => {
const result = checkTemplate()
if (result && result.length > 0) {
@ -123,7 +132,7 @@ const downLoadToAppPre = () => {
appName: dvInfo.value.name,
icon: null,
version: '2.0',
creator: state.userLoginInfo?.nickName,
creator: state.userLoginInfo?.name,
required: '2.9.0',
description: null
})
@ -139,9 +148,6 @@ const checkTemplate = () => {
})
return templateViewNames.slice(1)
}
const downLoadToApp = appAttachInfo => {
// do attach
}
const slideOpenChange = () => {
slideShow.value = !slideShow.value
@ -182,7 +188,7 @@ const getPreviewStateInfo = () => {
}
const downLoadApp = appAttachInfo => {
downLoadToApp(appAttachInfo)
fileDownload('app', appAttachInfo)
}
const findUserData = callback => {
@ -291,7 +297,13 @@ onBeforeMount(() => {
</template>
</el-container>
</div>
<app-export-form ref="appExportFormRef" @downLoadApp="downLoadApp"></app-export-form>
<app-export-form
ref="appExportFormRef"
:dv-info="state.dvInfo"
:component-data="state.canvasDataPreview"
:canvas-view-info="state.canvasViewInfoPreview"
@downLoadApp="downLoadApp"
></app-export-form>
</template>
<style lang="less">

View File

@ -88,6 +88,7 @@ const leftSidebarRef = ref(null)
const dvLayout = ref(null)
const canvasCenterRef = ref(null)
const mainHeight = ref(300)
let createType = null
const state = reactive({
datasetTree: [],
scaleHistory: null,
@ -295,7 +296,7 @@ onMounted(async () => {
const pid = embeddedStore.pid || router.currentRoute.value.query.pid
const templateParams =
embeddedStore.templateParams || router.currentRoute.value.query.templateParams
const createType = embeddedStore.createType || router.currentRoute.value.query.createType
createType = embeddedStore.createType || router.currentRoute.value.query.createType
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
const checkResult = await checkPer(dvId)
if (!checkResult) {
@ -325,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)
@ -341,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

@ -141,10 +141,14 @@ const ldapValidate = callback => {
if (!formRef.value) return
formRef.value.validate((valid: boolean) => {
if (valid && callback) {
duringLogin.value = true
callback()
}
})
}
const ldapFeedback = () => {
duringLogin.value = false
}
const activeType = ref('account')
const tablePaneList = ref([{ title: '普通登录', name: 'simple' }])
const xpackLoaded = info => {
@ -230,23 +234,28 @@ onMounted(async () => {
if (!checkPlatform()) {
const res = await loginCategoryApi()
const adminLogin = router.currentRoute?.value?.name === 'admin-login'
if (adminLogin && !res.data) {
if (adminLogin && (!res.data || res.data === 1)) {
router.push('/401')
return
}
if (res.data && !adminLogin) {
loadingText.value = '加载中...'
document.getElementsByClassName('ed-loading-text')?.length &&
(document.getElementsByClassName('ed-loading-text')[0]['innerText'] = loadingText.value)
nextTick(() => {
const param = { methodName: 'ssoLogin', args: res.data }
const timer = setInterval(() => {
if (xpackLoginHandler?.value.invokeMethod) {
xpackLoginHandler?.value.invokeMethod(param)
clearInterval(timer)
}
}, 1000)
})
if (res.data === 1) {
activeName.value = 'LDAP'
preheat.value = false
} else {
loadingText.value = '加载中...'
document.getElementsByClassName('ed-loading-text')?.length &&
(document.getElementsByClassName('ed-loading-text')[0]['innerText'] = loadingText.value)
nextTick(() => {
const param = { methodName: 'ssoLogin', args: res.data }
const timer = setInterval(() => {
if (xpackLoginHandler?.value.invokeMethod) {
xpackLoginHandler?.value.invokeMethod(param)
clearInterval(timer)
}
}, 1000)
})
}
} else {
preheat.value = false
}
@ -325,7 +334,9 @@ onMounted(async () => {
<XpackComponent
class="default-login-tabs"
:active-name="activeName"
:login-form="state.loginForm"
@validate="ldapValidate"
@feedback="ldapFeedback"
jsname="L2NvbXBvbmVudC9sb2dpbi9MZGFw"
/>

View File

@ -17,7 +17,7 @@
id="input"
ref="filesRef"
type="file"
accept=".DET2"
accept=".DET2,.DET2APP"
hidden
@change="handleFileChange"
/>
@ -127,10 +127,12 @@ const state = reactive({
pid: props.pid,
categories: [],
dvType: 'dashboard',
nodeType: 'template',
name: '',
templateStyle: null,
templateData: null,
dynamicData: null,
appData: null,
staticResource: null,
snapshot: '',
version: null
@ -265,8 +267,9 @@ const handleFileChange = e => {
state.templateInfo.templateData = state.importTemplateInfo['componentData']
state.templateInfo.snapshot = state.importTemplateInfo.snapshot
state.templateInfo.dynamicData = state.importTemplateInfo['dynamicData']
state.templateInfo.appData = state.importTemplateInfo['appData']
state.templateInfo.staticResource = state.importTemplateInfo['staticResource']
state.templateInfo['nodeType'] = 'template'
state.templateInfo['nodeType'] = state.importTemplateInfo['nodeType'] || 'template'
state.templateInfo['version'] = state.importTemplateInfo['version']
}
reader.readAsText(file)

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 => {
@ -666,7 +680,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>
@ -690,8 +705,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>
@ -741,6 +769,11 @@ defineExpose({
:name="dsInfo.name"
v-if="showFinishPage"
></FinishPage>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</el-drawer>
<creat-ds-group
@ -748,10 +781,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

@ -76,6 +76,11 @@ public class VisualizationTemplateVO {
*/
private String dynamicData;
/**
* app数据
*/
private String appData;
/**
* 使用次数
*/

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")
@ -113,7 +113,7 @@ public interface DataVisualizationApi {
@Operation(summary = "仪表板视图明细数据")
List<VisualizationViewTableDTO> detailList(@PathVariable("dvId") Long dvId);
@GetMapping("/export2AppCheck")
@PostMapping("/export2AppCheck")
@Operation(summary = "仪表板视图明细数据")
VisualizationExport2AppVO export2AppCheck(@RequestBody VisualizationAppExportRequest appExportRequest);
}

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<>();
/**
* 水印信息
@ -168,8 +171,13 @@ public class DataVisualizationVO implements Serializable {
*/
private Integer weight;
/**
* 应用信息
*/
private VisualizationExport2AppVO appData;
public DataVisualizationVO(Long id, String name, String type, Integer version, String canvasStyleData, String componentData, 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;
@ -177,6 +185,9 @@ public class DataVisualizationVO implements Serializable {
this.componentData = componentData;
this.canvasViewInfo = canvasViewInfo;
this.extendDataInfo = extendDataInfo;
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

@ -2,10 +2,7 @@ package io.dataease.api.xpack.settings;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.xpack.settings.request.XpackAuthenticationEditor;
import io.dataease.api.xpack.settings.vo.XpackCasVO;
import io.dataease.api.xpack.settings.vo.XpackOidcVO;
import io.dataease.api.xpack.settings.vo.XpackAuthenticationStatusVO;
import io.dataease.api.xpack.settings.vo.XpackAuthenticationVO;
import io.dataease.api.xpack.settings.vo.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
@ -34,6 +31,9 @@ public interface XpackAuthenticationApi {
@PostMapping("/save/cas")
String saveCas(@RequestBody XpackCasVO editor);
@PostMapping("/save/ldap")
String saveLdap(@RequestBody XpackLdapVO editor);
@GetMapping("/info/oidc")
XpackOidcVO oidcInfo();
@ -41,6 +41,9 @@ public interface XpackAuthenticationApi {
@GetMapping("/info/cas")
XpackCasVO casInfo();
@GetMapping("/info/ldap")
XpackLdapVO ldapInfo();
@PostMapping("/validate/oidc")
String validateOidc(@RequestBody XpackOidcVO editor);
@ -48,6 +51,9 @@ public interface XpackAuthenticationApi {
@PostMapping("/validate/cas")
String validateCas(@RequestBody XpackCasVO editor);
@PostMapping("/validate/ldap")
String validateLdap(@RequestBody XpackLdapVO editor);
@PostMapping("/validateId/{id}")
String validate(@PathVariable("id") Long id);

View File

@ -0,0 +1,25 @@
package io.dataease.api.xpack.settings.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class XpackLdapVO implements Serializable {
@Serial
private static final long serialVersionUID = -2996803523472015035L;
private String addr;
private String dn;
private String pwd;
private String ou;
private String filter;
private String mapping;
}

View File

@ -11,4 +11,11 @@
<artifactId>api-permissions</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,5 +1,6 @@
package io.dataease.api.permissions.login.dto;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@ -15,4 +16,7 @@ public class PwdLoginDTO {
@Schema(description = "密码(需加密)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "login.validator.pwd")
private String pwd;
@Hidden
private Integer origin = 0;
}

View File

@ -33,4 +33,33 @@ public class AesUtils {
throw new RuntimeException("decrypt errorplease check parameters", e);
}
}
public static String aesEncrypt(String src, String secretKey, String iv) {
if (StringUtils.isBlank(secretKey)) {
throw new RuntimeException("secretKey is empty");
}
try {
byte[] raw = secretKey.getBytes(UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
// "算法/模式/补码方式" ECB
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv1 = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv1);
byte[] encrypted = cipher.doFinal(src.getBytes(UTF_8));
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES encrypt error:", e);
}
}
public static Object aesEncrypt(Object o) {
return o == null ? null : aesEncrypt(o.toString(), "www.fit2cloud.co", "1234567890123456");
}
public static Object aesDecrypt(Object o) {
return o == null ? null : aesDecrypt(o.toString(), "www.fit2cloud.co", "1234567890123456");
}
}

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

@ -15,6 +15,8 @@ import org.apache.calcite.sql.parser.SqlParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.Statement;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
@ -46,6 +48,22 @@ public abstract class Provider {
@Getter
private static final Map<Long, Session> sessions = new HashMap<>();
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;
}
}
}