Merge branch 'dev-v2' of github.com:dataease/dataease into dev-v2

This commit is contained in:
wangjiahao 2023-11-02 15:15:30 +08:00
commit b02232be7f
52 changed files with 748 additions and 472 deletions

2
.gitignore vendored
View File

@ -48,3 +48,5 @@ core/core-frontend/src/assets/fsSvg.html
/sdk/dataease-plugin-filter/
/sdk/dataease-plugin-interface/
/sdk/dataease-plugin-view/
/extensions/
.vite/

View File

@ -2,9 +2,9 @@ FROM registry.cn-qingdao.aliyuncs.com/dataease/alpine-openjdk17-jre
ARG IMAGE_TAG
RUN mkdir -p /opt/apps/config /opt/dataease/drivers/ /opt/dataease2.0/cache/ /opt/dataease2.0/data/map /opt/dataease2.0/data/static-resource/
RUN mkdir -p /opt/apps/config /opt/dataease2.0/drivers/ /opt/dataease2.0/cache/ /opt/dataease2.0/data/map /opt/dataease2.0/data/static-resource/
ADD drivers/* /opt/dataease/drivers/
ADD drivers/* /opt/dataease2.0/drivers/
ADD mapFiles/ /opt/dataease2.0/data/map/
ADD staticResource/ /opt/dataease2.0/data/static-resource/

View File

@ -15,7 +15,9 @@
DataEase 是开源的数据可视化分析工具帮助用户快速分析数据并洞察业务趋势从而实现业务的改进与优化。DataEase 支持丰富的数据源连接,能够通过拖拉拽方式快速制作图表,并可以方便的与他人分享。
![DataEase 概览图](https://github.com/dataease/dataease/assets/41712985/ef020c86-68e0-43a3-8054-f51463eae361)
![DataEase 概览图](https://github.com/dataease/dataease/assets/41712985/52ca92d0-955c-42f1-bb79-d9bb19913649)
**DataEase 的优势:**
@ -52,11 +54,8 @@ DataEase 是开源的数据可视化分析工具,帮助用户快速分析数
## DataEase 快速入门
- [安装部署教程](https://dataease.io/docs/installation/installation_mode/)
- [快速入门视频](https://www.bilibili.com/video/BV1qG4y1F7uc/)
- [完整在线文档](https://dataease.io/docs/)
- [中文社区论坛](https://bbs.fit2cloud.com/c/de/6)
- [模板应用市场](https://dataease.io/templates/)
- [在线文档](https://dataease.io/docs/)
- [社区论坛](https://bbs.fit2cloud.com/c/de/6)
## License

View File

@ -1,14 +1,12 @@
package io.dataease.chart.manage;
import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import io.dataease.api.chart.dto.*;
import io.dataease.api.chart.request.ChartDrillRequest;
import io.dataease.api.chart.request.ChartExtRequest;
import io.dataease.api.dataset.dto.SqlVariableDetails;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.dataset.union.model.SQLMeta;
import io.dataease.api.permissions.auth.api.InteractiveAuthApi;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.api.permissions.dataset.dto.DataSetRowPermissionsTreeDTO;
import io.dataease.chart.constant.ChartConstants;
@ -30,6 +28,7 @@ import io.dataease.engine.utils.Utils;
import io.dataease.exception.DEException;
import io.dataease.i18n.Translator;
import io.dataease.result.ResultCode;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
@ -37,7 +36,6 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@ -65,8 +63,9 @@ public class ChartDataManage {
private ChartViewManege chartViewManege;
@Resource
private PermissionManage permissionManage;
@Autowired(required = false)
private InteractiveAuthApi interactiveAuthApi;
@Resource
private CorePermissionManage corePermissionManage;
private static Logger logger = LoggerFactory.getLogger(ChartDataManage.class);
@ -140,11 +139,12 @@ public class ChartDataManage {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_no_ds"));
}
// check permission
if (interactiveAuthApi != null) {
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(table.getId());
dto.setAuthEnum(AuthEnum.READ);
interactiveAuthApi.checkAuth(dto);
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(table.getId());
dto.setAuthEnum(AuthEnum.READ);
boolean checked = corePermissionManage.checkAuth(dto);
if (!checked) {
DEException.throwException(Translator.get("i18n_no_datasource_permission"));
}
// column permission

View File

@ -9,8 +9,6 @@ import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.dataset.union.UnionDTO;
import io.dataease.api.dataset.vo.DataSetBarVO;
import io.dataease.api.ds.vo.DatasourceDTO;
import io.dataease.api.permissions.user.api.UserApi;
import io.dataease.api.permissions.user.vo.UserFormVO;
import io.dataease.commons.constants.OptConstants;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTable;
@ -31,11 +29,11 @@ import io.dataease.license.config.XpackInteract;
import io.dataease.model.BusiNodeRequest;
import io.dataease.model.BusiNodeVO;
import io.dataease.operation.manage.CoreOptRecentManage;
import io.dataease.system.manage.CoreUserManage;
import io.dataease.utils.*;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@ -68,8 +66,10 @@ public class DatasetGroupManage {
private CoreDatasetTableMapper coreDatasetTableMapper;
@Resource
private CoreDatasourceMapper coreDatasourceMapper;
@Autowired(required = false)
private UserApi userApi;
@Resource
private CoreUserManage coreUserManage;
@Resource
private CoreOptRecentManage coreOptRecentManage;
@ -88,13 +88,9 @@ public class DatasetGroupManage {
CoreDatasetGroup coreDatasetGroup = coreDatasetGroupMapper.selectById(datasetGroupInfoDTO.getId());
datasetGroupInfoDTO.setPid(coreDatasetGroup.getPid());
}
if (userApi == null) {
checkName(datasetGroupInfoDTO);
}
if (userApi != null) {
datasetGroupInfoDTO.setUpdateBy(userApi.info().getId() + "");
datasetGroupInfoDTO.setLastUpdateTime(System.currentTimeMillis());
}
checkName(datasetGroupInfoDTO);
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setLastUpdateTime(System.currentTimeMillis());
if (StringUtils.equalsIgnoreCase(datasetGroupInfoDTO.getNodeType(), leafType)) {
if (!rename && ObjectUtils.isEmpty(datasetGroupInfoDTO.getAllFields())) {
DEException.throwException(Translator.get("i18n_no_fields"));
@ -112,10 +108,8 @@ public class DatasetGroupManage {
if (ObjectUtils.isEmpty(datasetGroupInfoDTO.getId())) {
isCreate = true;
datasetGroupInfoDTO.setId(IDUtils.snowID());
if (userApi != null) {
datasetGroupInfoDTO.setCreateBy(userApi.info().getId() + "");
datasetGroupInfoDTO.setUpdateBy(userApi.info().getId() + "");
}
datasetGroupInfoDTO.setCreateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetGroupInfoDTO.setCreateTime(time);
datasetGroupInfoDTO.setLastUpdateTime(time);
datasetGroupInfoDTO.setPid(datasetGroupInfoDTO.getPid() == null ? 0L : datasetGroupInfoDTO.getPid());
@ -152,21 +146,19 @@ public class DatasetGroupManage {
CoreDatasetGroup coreDatasetGroup = BeanUtils.copyBean(new CoreDatasetGroup(), datasetGroupInfoDTO);
coreDatasetGroup.setLastUpdateTime(System.currentTimeMillis());
coreDatasetGroupMapper.updateById(coreDatasetGroup);
coreOptRecentManage.saveOpt(datasetGroupInfoDTO.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET,OptConstants.OPT_TYPE.UPDATE);
coreOptRecentManage.saveOpt(datasetGroupInfoDTO.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.UPDATE);
}
@XpackInteract(value = "authResourceTree", before = false)
public void innerSave(DatasetGroupInfoDTO datasetGroupInfoDTO) {
CoreDatasetGroup coreDatasetGroup = BeanUtils.copyBean(new CoreDatasetGroup(), datasetGroupInfoDTO);
coreDatasetGroupMapper.insert(coreDatasetGroup);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET,OptConstants.OPT_TYPE.NEW);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.NEW);
}
@XpackInteract(value = "authResourceTree", before = false)
public DatasetGroupInfoDTO move(DatasetGroupInfoDTO datasetGroupInfoDTO) {
if (userApi == null) {
checkName(datasetGroupInfoDTO);
}
checkName(datasetGroupInfoDTO);
if (datasetGroupInfoDTO.getPid() != 0) {
checkMove(datasetGroupInfoDTO);
}
@ -174,12 +166,10 @@ public class DatasetGroupManage {
long time = System.currentTimeMillis();
CoreDatasetGroup coreDatasetGroup = new CoreDatasetGroup();
BeanUtils.copyBean(coreDatasetGroup, datasetGroupInfoDTO);
if (userApi != null) {
datasetGroupInfoDTO.setUpdateBy(userApi.info().getId() + "");
}
datasetGroupInfoDTO.setUpdateBy(AuthUtils.getUser().getUserId() + "");
coreDatasetGroup.setLastUpdateTime(time);
coreDatasetGroupMapper.updateById(coreDatasetGroup);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET,OptConstants.OPT_TYPE.UPDATE);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.UPDATE);
return datasetGroupInfoDTO;
}
@ -190,7 +180,7 @@ public class DatasetGroupManage {
DEException.throwException("resource not exist");
}
Objects.requireNonNull(CommonBeanFactory.getBean(this.getClass())).recursionDel(id);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET,OptConstants.OPT_TYPE.DELETE);
coreOptRecentManage.saveOpt(coreDatasetGroup.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASET, OptConstants.OPT_TYPE.DELETE);
}
public void recursionDel(Long id) {
@ -231,15 +221,13 @@ public class DatasetGroupManage {
public DataSetBarVO queryBarInfo(Long id) {
DataSetBarVO dataSetBarVO = coreDataSetExtMapper.queryBarInfo(id);
// get creator
if (userApi != null) {
UserFormVO userFormVO = userApi.queryById(Long.valueOf(dataSetBarVO.getCreateBy()));
if (userFormVO != null) {
dataSetBarVO.setCreator(userFormVO.getName());
}
UserFormVO userFormVOUpdateBy = userApi.queryById(Long.valueOf(dataSetBarVO.getUpdateBy()));
if (userFormVOUpdateBy != null) {
dataSetBarVO.setUpdater(userFormVOUpdateBy.getName());
}
String userName = coreUserManage.getUserName(Long.valueOf(dataSetBarVO.getCreateBy()));
if (StringUtils.isNotBlank(userName)) {
dataSetBarVO.setCreator(userName);
}
String updateUserName = coreUserManage.getUserName(Long.valueOf(dataSetBarVO.getUpdateBy()));
if (StringUtils.isNotBlank(updateUserName)) {
dataSetBarVO.setUpdater(updateUserName);
}
dataSetBarVO.setDatasourceDTOList(getDatasource(id));
return dataSetBarVO;
@ -254,13 +242,13 @@ public class DatasetGroupManage {
QueryWrapper<CoreDatasource> datasourceQueryWrapper = new QueryWrapper<>();
datasourceQueryWrapper.in("id", ids);
List<DatasourceDTO> datasourceDTOList = coreDatasourceMapper.selectList(datasourceQueryWrapper).stream().map(ele -> {
List<DatasourceDTO> datasourceDTOList = coreDatasourceMapper.selectList(datasourceQueryWrapper).stream().map(ele -> {
DatasourceDTO dto = new DatasourceDTO();
BeanUtils.copyBean(dto, ele);
dto.setConfiguration(null);
return dto;
}).collect(Collectors.toList());
if(ids.size() != datasourceDTOList.size()){
if (ids.size() != datasourceDTOList.size()) {
DEException.throwException("由于数据集所用的数据源已被删除,无法显示数据集");
}
return datasourceDTOList;
@ -366,15 +354,13 @@ public class DatasetGroupManage {
DatasetGroupInfoDTO dto = new DatasetGroupInfoDTO();
BeanUtils.copyBean(dto, coreDatasetGroup);
// get creator
if (userApi != null) {
UserFormVO userFormVO = userApi.queryById(Long.valueOf(dto.getCreateBy()));
if (userFormVO != null) {
dto.setCreator(userFormVO.getName());
}
UserFormVO userFormVOUpdateBy = userApi.queryById(Long.valueOf(dto.getUpdateBy()));
if (userFormVOUpdateBy != null) {
dto.setUpdater(userFormVOUpdateBy.getName());
}
String userName = coreUserManage.getUserName(Long.valueOf(dto.getCreateBy()));
if (StringUtils.isNotBlank(userName)) {
dto.setCreator(userName);
}
String updateUserName = coreUserManage.getUserName(Long.valueOf(dto.getUpdateBy()));
if (StringUtils.isNotBlank(updateUserName)) {
dto.setUpdater(updateUserName);
}
dto.setUnionSql(null);
if (StringUtils.equalsIgnoreCase(dto.getNodeType(), "dataset")) {
@ -440,8 +426,8 @@ public class DatasetGroupManage {
for (CoreDatasetTable datasetTable : datasetTables) {
if (StringUtils.isNotEmpty(datasetTable.getSqlVariableDetails())) {
List<SqlVariableDetails> defaultsSqlVariableDetails = JsonUtil.parseList(datasetTable.getSqlVariableDetails(), listTypeReference);
if(CollectionUtil.isNotEmpty(defaultsSqlVariableDetails)){
List<String> fullName = new ArrayList<>();
if (CollectionUtil.isNotEmpty(defaultsSqlVariableDetails)) {
List<String> fullName = new ArrayList<>();
geFullName(id, fullName);
List<String> finalFullName = CollectionUtil.reverse(fullName);
defaultsSqlVariableDetails.forEach(sqlVariableDetails -> {

View File

@ -6,12 +6,10 @@ import io.dataease.api.dataset.dto.DatasetTableDTO;
import io.dataease.api.dataset.dto.SqlVariableDetails;
import io.dataease.api.dataset.union.*;
import io.dataease.api.dataset.union.model.SQLObj;
import io.dataease.api.permissions.auth.api.InteractiveAuthApi;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.commons.utils.SqlparserUtils;
import io.dataease.constant.AuthEnum;
import io.dataease.dataset.constant.DatasetTableType;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper;
import io.dataease.dataset.dto.DatasourceSchemaDTO;
import io.dataease.dataset.utils.DatasetTableTypeConstants;
import io.dataease.dataset.utils.SqlUtils;
@ -24,6 +22,7 @@ import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.i18n.Translator;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
@ -31,7 +30,6 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@ -44,16 +42,14 @@ import java.util.stream.Collectors;
*/
@Component
public class DatasetSQLManage {
@Resource
private CoreDatasetTableMapper coreDatasetTableMapper;
@Resource
private DatasetTableFieldManage datasetTableFieldManage;
@Resource
private CoreDatasourceMapper coreDatasourceMapper;
@Resource
private EngineServer engineServer;
@Autowired(required = false)
private InteractiveAuthApi interactiveAuthApi;
@Resource
private CorePermissionManage corePermissionManage;
private static Logger logger = LoggerFactory.getLogger(DatasetSQLManage.class);
@ -328,17 +324,15 @@ public class DatasetSQLManage {
private String putObj2Map(Map<Long, DatasourceSchemaDTO> dsMap, DatasetTableDTO ds) throws Exception {
// 通过datasource id校验数据源权限
if (interactiveAuthApi != null) {
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(ds.getDatasourceId());
dto.setAuthEnum(AuthEnum.READ);
try {
interactiveAuthApi.checkAuth(dto);
} catch (Exception e) {
DEException.throwException(Translator.get("i18n_no_datasource_permission"));
}
BusiPerCheckDTO dto = new BusiPerCheckDTO();
dto.setId(ds.getDatasourceId());
dto.setAuthEnum(AuthEnum.READ);
boolean checked = corePermissionManage.checkAuth(dto);
if (!checked) {
DEException.throwException(Translator.get("i18n_no_datasource_permission"));
}
String schemaAlias;
if (StringUtils.equalsIgnoreCase(ds.getType(), DatasetTableType.DB) || StringUtils.equalsIgnoreCase(ds.getType(), DatasetTableType.SQL)) {
CoreDatasource coreDatasource = coreDatasourceMapper.selectById(ds.getDatasourceId());

View File

@ -87,6 +87,13 @@ public class DataSourceManage {
coreOptRecentManage.saveOpt(coreDatasource.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASOURCE,OptConstants.OPT_TYPE.UPDATE);
}
@XpackInteract(value = "datasourceResourceTree", before = false)
public void innerEditStatus(CoreDatasource coreDatasource) {
UpdateWrapper<CoreDatasource> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", coreDatasource.getId());
coreDatasourceMapper.update(coreDatasource, updateWrapper);
}
@XpackInteract(value = "datasourceResourceTree", before = false)
public void move(DatasourceDTO dataSourceDTO) {
Long id = dataSourceDTO.getId();

View File

@ -319,7 +319,7 @@ public class ApiUtils {
}
}
for (JsonNode node : jsonArray) {
handleStr(apiDefinition, node.toString(), childrenField, rootPath + "." + fieldName + "[*]");
handleStr(apiDefinition, node.toString(), childrenField, rootPath + "." + String.format(path, fieldName) + "[*]");
}
o.put("children", childrenField);
o.put("childrenDataType", "LIST");

View File

@ -47,15 +47,14 @@ import java.util.stream.Collectors;
@Component("calciteProvider")
public class CalciteProvider {
//TODO mongo impala es hive
@Resource
protected CoreDatasourceMapper coreDatasourceMapper;
@Resource
private EngineServer engineServer;
protected ExtendedJdbcClassLoader extendedJdbcClassLoader;
private Map<Long, ExtendedJdbcClassLoader> customJdbcClassLoaders = new HashMap<>();
private final String FILE_PATH = "/opt/dataease/drivers";
private final String CUSTOM_PATH = "/opt/dataease/custom-drivers/";
private final String FILE_PATH = "/opt/dataease2.0/drivers";
private final String CUSTOM_PATH = "/opt/dataease2.0/custom-drivers/";
private static String split = "DE";
@Resource
@ -93,7 +92,7 @@ public class CalciteProvider {
schemas.add(resultSet.getString(1));
}
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return schemas;
}
@ -107,7 +106,7 @@ public class CalciteProvider {
tables.add(getTableDesc(datasourceRequest, resultSet));
}
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
}
return tables;
@ -145,15 +144,11 @@ public class CalciteProvider {
String querySql = getTablesSql(datasourceRequest).get(0);
try (Connection con = getConnection(datasourceRequest.getDatasource()); Statement statement = getStatement(con, 30); ResultSet resultSet = statement.executeQuery(querySql)) {
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return "Success";
}
public List<TableField> getTableFields(DatasourceRequest datasourceRequest) throws Exception {
return null;
}
public Map<String, Object> fetchResultField(DatasourceRequest datasourceRequest) throws DEException {
List<TableField> datasetTableFields = new ArrayList<>();
List<String[]> list = new LinkedList<>();
@ -242,7 +237,7 @@ public class CalciteProvider {
Class.forName("org.apache.calcite.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:calcite:", info);
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return connection;
}
@ -433,7 +428,7 @@ public class CalciteProvider {
list.add(row);
}
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return list;
}
@ -573,7 +568,7 @@ public class CalciteProvider {
Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance();
conn = driverClass.connect(configuration.getJdbc(), props);
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return conn;
}
@ -587,7 +582,7 @@ public class CalciteProvider {
stat = connection.createStatement();
stat.setQueryTimeout(queryTimeout);
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return stat;
}
@ -640,7 +635,7 @@ public class CalciteProvider {
customJdbcClassLoaders.put(coreDriver.getId(), customJdbcClassLoader);
return customJdbcClassLoader;
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
return null;
}
@ -691,7 +686,7 @@ public class CalciteProvider {
SchemaPlus rootSchema = buildSchema(datasourceRequest, calciteConnection);
addCustomFunctions(rootSchema);
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
}
@ -709,7 +704,7 @@ public class CalciteProvider {
rootSchema.removeSubSchema(datasourceSchemaDTO.getSchemaAlias());
}
} catch (Exception e) {
DEException.throwException(e);
DEException.throwException(e.getMessage());
}
}

View File

@ -35,7 +35,7 @@ import java.util.stream.Collectors;
public class ExcelUtils {
public static final String UFEFF = "\uFEFF";
private static String path = "/opt/dataease/data/excel/";
private static String path = "/opt/dataease2.0/data/excel/";
private static ObjectMapper objectMapper = new ObjectMapper();
private static TypeReference<List<TableField>> TableFieldListTypeReference = new TypeReference<List<TableField>>() {

View File

@ -31,7 +31,7 @@ import java.util.*;
@RequestMapping("/datasourceDriver")
public class DatasourceDriverServer implements DatasourceDriverApi {
private final static String DRIVER_PATH = "/opt/dataease/custom-drivers/";
private final static String DRIVER_PATH = "/opt/dataease2.0/custom-drivers/";
@Resource
private CoreDriverMapper coreDriverMapper;

View File

@ -12,10 +12,6 @@ import io.dataease.api.dataset.dto.DatasetTableDTO;
import io.dataease.api.dataset.dto.PreviewSqlDTO;
import io.dataease.api.ds.DatasourceApi;
import io.dataease.api.ds.vo.*;
import io.dataease.api.permissions.auth.api.InteractiveAuthApi;
import io.dataease.api.permissions.auth.dto.BusiResourceEditor;
import io.dataease.api.permissions.user.api.UserApi;
import io.dataease.api.permissions.user.vo.UserFormVO;
import io.dataease.commons.constants.TaskStatus;
import io.dataease.commons.utils.CommonThreadPool;
import io.dataease.constant.DataSourceType;
@ -34,18 +30,17 @@ import io.dataease.datasource.provider.ApiUtils;
import io.dataease.datasource.provider.CalciteProvider;
import io.dataease.datasource.provider.ExcelUtils;
import io.dataease.datasource.request.DatasourceRequest;
import io.dataease.datasource.type.Pg;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.i18n.Translator;
import io.dataease.license.config.XpackInteract;
import io.dataease.model.BusiNodeRequest;
import io.dataease.model.BusiNodeVO;
import io.dataease.system.manage.CoreUserManage;
import io.dataease.utils.*;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
@ -89,10 +84,10 @@ public class DatasourceServer implements DatasourceApi {
private CoreDsFinishPageMapper coreDsFinishPageMapper;
@Resource
private DatasetDataManage datasetDataManage;
@Autowired(required = false)
private UserApi userApi;
@Autowired(required = false)
private InteractiveAuthApi interactiveAuthApi;
@Resource
private CoreUserManage coreUserManage;
@Override
public List<DatasourceDTO> query(String keyWord) {
@ -125,10 +120,12 @@ public class DatasourceServer implements DatasourceApi {
if (Objects.equals(dataSourceDTO.getId(), dataSourceDTO.getPid())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
}
List<Long> ids = new ArrayList<>();
getParents(dataSourceDTO.getPid(), ids);
if (ids.contains(dataSourceDTO.getId())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
if (dataSourceDTO.getPid() != 0) {
List<Long> ids = new ArrayList<>();
getParents(dataSourceDTO.getPid(), ids);
if (ids.contains(dataSourceDTO.getId())) {
DEException.throwException(Translator.get("i18n_pid_not_eq_id"));
}
}
dataSourceManage.move(dataSourceDTO);
}
@ -309,16 +306,16 @@ public class DatasourceServer implements DatasourceApi {
private static void checkParams(String configurationStr) {
DatasourceConfiguration configuration = JsonUtil.parseObject(configurationStr, DatasourceConfiguration.class);
if(configuration.getInitialPoolSize() < configuration.getMinPoolSize()){
if (configuration.getInitialPoolSize() < configuration.getMinPoolSize()) {
DEException.throwException("初始连接数不能小于最小连接数!");
}
if(configuration.getInitialPoolSize() > configuration.getMaxPoolSize()){
if (configuration.getInitialPoolSize() > configuration.getMaxPoolSize()) {
DEException.throwException("初始连接数不能大于最大连接数!");
}
if(configuration.getMaxPoolSize() < configuration.getMinPoolSize()){
if (configuration.getMaxPoolSize() < configuration.getMinPoolSize()) {
DEException.throwException("最大连接数不能小于最小连接数!");
}
if(configuration.getQueryTimeout() < 0){
if (configuration.getQueryTimeout() < 0) {
DEException.throwException("查询超时不能小于0");
}
}
@ -538,12 +535,8 @@ public class DatasourceServer implements DatasourceApi {
}
datasourceDTO.setConfiguration(new String(Base64.getEncoder().encode(datasourceDTO.getConfiguration().getBytes())));
if (userApi != null) {
UserFormVO userFormVO = userApi.queryById(Long.valueOf(datasourceDTO.getCreateBy()));
if (userFormVO != null) {
datasourceDTO.setCreator(userFormVO.getName());
}
}
datasourceDTO.setCreator(coreUserManage.getUserName(Long.valueOf(datasourceDTO.getCreateBy())));
return datasourceDTO;
}
@ -620,14 +613,12 @@ public class DatasourceServer implements DatasourceApi {
record.setStatus(coreDatasource.getStatus());
QueryWrapper<CoreDatasource> wrapper = new QueryWrapper<>();
wrapper.eq("id", coreDatasource.getId());
datasourceMapper.update(record, wrapper);
if (interactiveAuthApi != null) {
BusiResourceEditor editor = new BusiResourceEditor();
editor.setId((long) coreDatasource.getId());
editor.setName(coreDatasource.getName());
editor.setExtraFlag(getExtraFlag(coreDatasource.getType(), coreDatasource.getStatus()));
interactiveAuthApi.editResource(editor);
CoreDatasource originData = datasourceMapper.selectById(coreDatasource.getId());
String originStatus = originData.getStatus();
if (StringUtils.equals(coreDatasource.getStatus(), originStatus)) {
return datasourceDTO;
}
dataSourceManage.innerEditStatus(coreDatasource);
return datasourceDTO;
}
@ -880,7 +871,7 @@ public class DatasourceServer implements DatasourceApi {
List<DatasetTableDTO> datasetTableDTOS = ApiUtils.getTables(datasourceRequest);
for (int i = 0; i < pager.getRecords().size(); i++) {
for (int i1 = 0; i1 < datasetTableDTOS.size(); i1++) {
if(pager.getRecords().get(i).getTableName().equalsIgnoreCase(datasetTableDTOS.get(i1).getTableName())){
if (pager.getRecords().get(i).getTableName().equalsIgnoreCase(datasetTableDTOS.get(i1).getTableName())) {
pager.getRecords().get(i).setName(datasetTableDTOS.get(i1).getName());
}
}

View File

@ -1,6 +1,7 @@
package io.dataease.datasource.type;
import io.dataease.api.ds.vo.DatasourceConfiguration;
import io.dataease.exception.DEException;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@ -25,7 +26,7 @@ public class Mysql extends DatasourceConfiguration {
} else {
for (String illegalParameter : illegalParameters) {
if (getExtraParams().toLowerCase().contains(illegalParameter.toLowerCase())) {
throw new RuntimeException("Illegal parameter: " + illegalParameter);
DEException.throwException("Illegal parameter: " + illegalParameter);
}
}

View File

@ -0,0 +1,14 @@
package io.dataease.system.manage;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.license.config.XpackInteract;
import org.springframework.stereotype.Component;
@Component
public class CorePermissionManage {
@XpackInteract(value = "corePermissionManage", replace = true)
public boolean checkAuth(BusiPerCheckDTO dto) {
return true;
}
}

View File

@ -0,0 +1,14 @@
package io.dataease.system.manage;
import io.dataease.license.config.XpackInteract;
import org.springframework.stereotype.Component;
@Component
public class CoreUserManage {
@XpackInteract(value = "coreUserManage", replace = true)
public String getUserName(Long uid) {
return "管理员";
}
}

View File

@ -53,3 +53,4 @@ i18n_schema_is_empty=schema \u4E3A\u7A7A\uFF01
i18n_table_name_repeat=\u540D\u79F0\u91CD\u590D:
i18n_sql_not_empty=sql \u4E0D\u80FD\u4E3A\u7A7A
i18n_menu.parameter=\u7CFB\u7EDF\u53C2\u6570
i18n_user_old_pwd_error=\u539F\u59CB\u5BC6\u7801\u9519\u8BEF

View File

@ -0,0 +1,6 @@
<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<g id="icon_file-add_outlined">
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M4.16667 2.50001H13.3333V4.59959C13.3333 4.7101 13.3772 4.81608 13.4554 4.89422C13.5335 4.97236 13.6395 5.01626 13.75 5.01626H15.8333V10.8333H17.5V4.09626C17.5001 3.87532 17.4124 3.66341 17.2563 3.50709L14.8275 1.07751C14.7501 1.00007 14.6582 0.938651 14.557 0.896756C14.4558 0.85486 14.3474 0.833312 14.2379 0.833344H3.33333C3.11232 0.833344 2.90036 0.921141 2.74408 1.07742C2.5878 1.2337 2.5 1.44566 2.5 1.66668V18.3333C2.5 18.5544 2.5878 18.7663 2.74408 18.9226C2.90036 19.0789 3.11232 19.1667 3.33333 19.1667H10V17.5H4.16667V2.50001Z"/>
<path id="Vector" d="M14.5833 12.5C14.3532 12.5 14.1667 12.6865 14.1667 12.9167V15H12.0833C11.8532 15 11.6667 15.1865 11.6667 15.4167V16.25C11.6667 16.4801 11.8532 16.6667 12.0833 16.6667H14.1667V18.75C14.1667 18.9801 14.3532 19.1667 14.5833 19.1667H15.4167C15.6468 19.1667 15.8333 18.9801 15.8333 18.75V16.6667H17.9167C18.1468 16.6667 18.3333 16.4801 18.3333 16.25V15.4167C18.3333 15.1865 18.1468 15 17.9167 15H15.8333V12.9167C15.8333 12.6865 15.6468 12.5 15.4167 12.5H14.5833Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -152,8 +152,8 @@ eventBus.on('clearCanvas', clearCanvas)
</div>
<div class="middle-area"></div>
</template>
<template v-else-if="!isDataEaseBi">
<el-icon class="custom-el-icon back-icon" @click="backToMain()">
<template v-else>
<el-icon v-if="!isDataEaseBi" class="custom-el-icon back-icon" @click="backToMain()">
<Icon class="toolbar-icon" name="icon_left_outlined" />
</el-icon>
<div class="left-area">

View File

@ -184,6 +184,7 @@ onMounted(() => {
emitter.on(`addQueryCriteria${element.value.id}`, addCriteriaConfigOut)
emitter.on(`editQueryCriteria${element.value.id}`, editQueryCriteria)
emitter.on(`updateQueryCriteria${element.value.id}`, updateQueryCriteria)
updateQueryCriteria()
})
const dragover = () => {

View File

@ -364,6 +364,16 @@ const init = (queryId: string) => {
const datasetMapKeyList = Object.keys(datasetMap)
getSqlParams([
...new Set(
datasetFieldList.value
.map(ele => ele.tableId)
.filter(ele => !datasetMapKeyList.includes(ele) && ele)
)
]).then(res => {
parameters.value = res || []
})
if (datasetFieldIdList.every(ele => datasetMapKeyList.includes(ele))) {
fields.value = datasetFieldList.value
.map(ele => {
@ -400,15 +410,6 @@ const init = (queryId: string) => {
.finally(() => {
handleCheckedFieldsChange(curComponent.value.checkedFields)
})
getSqlParams([
...new Set(
datasetFieldList.value
.map(ele => ele.tableId)
.filter(ele => !datasetMapKeyList.includes(ele) && ele)
)
]).then(res => {
parameters.value = res || []
})
}
const weightlessness = () => {
@ -729,10 +730,17 @@ defineExpose({
value="0"
/>
<el-option
v-if="curComponent.displayType === '2'"
:disabled="curComponent.displayType !== '2'"
label="数字下拉"
value="2"
/>
<el-option
v-else
:disabled="curComponent.displayType !== '5'"
label="数字下拉"
value="5"
/>
<el-option
:disabled="!['1', '7'].includes(curComponent.displayType)"
label="时间"
@ -802,7 +810,7 @@ defineExpose({
<el-icon size="18px" v-if="data.leaf">
<Icon name="icon_dataset"></Icon>
</el-icon>
<span class="label" style="margin-left: 8px" :title="node.label">{{
<span class="label ellipsis" style="margin-left: 8px" :title="node.label">{{
node.label
}}</span>
</div>
@ -1287,9 +1295,10 @@ defineExpose({
align-items: center;
.label {
margin-left: 5px;
width: calc(100% - 45px);
}
}
max-width: 321px;
.ed-select-dropdown__item.selected {
font-weight: 400;
}

View File

@ -230,6 +230,7 @@ export default {
manage: '管理',
auth: '授权',
resource_name: '资源名称',
menu_name: '菜单名称',
from_role: '继承自以下角色:',
auth_alone: '单独授权',
org_role_empty: '组织管理员已拥有所有资源的权限,无需再授权',
@ -263,7 +264,7 @@ export default {
data_source_table: '数据源表',
auth_method: '认证方式',
passwd: '用户名密码',
kerbers_info: '请确保 krb5.Conf、Keytab Key已经添加到路径/opt/dataease/conf',
kerbers_info: '请确保 krb5.Conf、Keytab Key已经添加到路径/opt/dataease2.0/conf',
client_principal: 'Client Principal',
keytab_Key_path: 'Keytab Key Path',
please_select_left: '请从左侧选择',
@ -1429,8 +1430,9 @@ export default {
auth_num: '授权数量',
version: '版本',
version_num: '版本号',
standard: '标准版',
standard: '社区版',
enterprise: '企业版',
Embedded: '嵌入式版',
support: '获取技术支持',
update_success: '更新成功',
serial_no: '序列号',

View File

@ -86,4 +86,25 @@ declare interface ChartEditorForm<T> {
*
*/
render: boolean
/**
*
*/
prop?: string
}
/**
*
*/
declare interface AxisEditForm {
/**
*
*/
axisType: AxisType
/**
*
*/
axis: Axis[]
/**
*
*/
editType: 'add' | 'remove' | 'update'
}

View File

@ -57,7 +57,7 @@ import AppElement from './App.vue'
import { setupI18n } from '@/plugins/vue-i18n'
import { setupStore } from '@/store'
import { useUserStoreWithOut } from '@/store/modules/user'
import { setupElementPlus } from '@/plugins/element-plus'
import { setupElementPlus, setupElementPlusIcons } from '@/plugins/element-plus'
import { setupRouter } from '@/router'
const setupAll = async (dom: string, componentName: string): Promise<App<Element>> => {
@ -66,6 +66,7 @@ const setupAll = async (dom: string, componentName: string): Promise<App<Element
setupStore(app)
setupRouter(app)
setupElementPlus(app)
setupElementPlusIcons(app)
const userStore = useUserStoreWithOut()
await userStore.setUser()
app.mount(dom)

View File

@ -95,6 +95,9 @@ export const pathValid = path => {
* @returns
*/
const hasCurrentRouter = (locations, routers, index) => {
if (!routers?.length) {
return false
}
const location = locations[index]
let kids = []
const isvalid = routers.some(router => {

View File

@ -150,7 +150,13 @@ const update = (licKey: string) => {
<div class="item">
<div class="label">{{ $t('about.version') }}</div>
<div class="value">
{{ license.edition === 'Standard' ? $t('about.standard') : $t('about.enterprise') }}
{{
!license?.edition
? $t('about.standard')
: license.edition === 'Embedded'
? $t('about.Embedded')
: $t('about.enterprise')
}}
</div>
</div>
<div class="item">

View File

@ -173,33 +173,10 @@ const dsClick = (data: Tree) => {
}
//
_modelValue.value = data.id
getFields(data.id, props.viewId)
//
datasetSelectorPopover.value?.hide()
}
}
const getFields = (id, chartId) => {
if (id && chartId) {
getFieldByDQ(id, chartId)
.then(res => {
state.value.dimension = (res.dimensionList as unknown as Field[]) || []
state.value.quota = (res.quotaList as unknown as Field[]) || []
state.value.dimensionData = JSON.parse(JSON.stringify(state.value.dimension))
state.value.quotaData = JSON.parse(JSON.stringify(state.value.quota))
})
.catch(() => {
state.value.dimension = []
state.value.quota = []
state.value.dimensionData = []
state.value.quotaData = []
})
} else {
state.value.dimension = []
state.value.quota = []
state.value.dimensionData = []
state.value.quotaData = []
}
}
const _popoverShow = ref(false)
function onPopoverShow() {
_popoverShow.value = true

View File

@ -7,10 +7,11 @@ import cloneDeep from 'lodash-es/cloneDeep'
import defaultsDeep from 'lodash-es/defaultsDeep'
import { formatterType, unitType } from '../../../js/formatter'
import { fieldType } from '@/utils/attr'
import { differenceBy, partition } from 'lodash-es'
import { partition } from 'lodash-es'
import chartViewManager from '../../../js/panel'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { useEmitt } from '@/hooks/web/useEmitt'
const { t } = useI18n()
@ -34,11 +35,10 @@ const toolTip = computed(() => {
return props.themes === 'dark' ? 'ndark' : 'dark'
})
const emit = defineEmits(['onTooltipChange', 'onExtTooltipChange'])
const curSeriesFormatter = ref<DeepPartial<SeriesFormatter>>({})
const quotaData = ref<Axis[]>(inject('quotaData'))
const showSeriesTooltipFormatter = computed(() => {
return showProperty('seriesTooltipFormatter') && !batchOptStatus.value
return showProperty('seriesTooltipFormatter') && !batchOptStatus.value && props.chart.id
})
//
const initSeriesTooltip = () => {
@ -69,7 +69,7 @@ const initSeriesTooltip = () => {
...next,
seriesId: next.seriesId ?? next.id,
show: index <= quotaAxis.value.length - 1,
summary: 'sum'
summary: COUNT_DE_TYPE.includes(next.deType) ? 'count' : 'sum'
} as SeriesFormatter
if (seriesAxisMap[tmp.seriesId]) {
tmp = {
@ -90,84 +90,15 @@ const initSeriesTooltip = () => {
}
curSeriesFormatter.value = axisMap[curSeriesFormatter.value.seriesId]
}
//
const updateSeriesTooltip = (newAxis?: SeriesFormatter[], oldAxis?: SeriesFormatter[]) => {
if (
!showSeriesTooltipFormatter.value ||
!state.tooltipForm.seriesTooltipFormatter.length ||
!quotaData.value?.length
) {
return
}
const axisMap: Record<string, Axis> = newAxis?.reduce((pre, next) => {
pre[next.seriesId] = next
return pre
}, {})
//
const addedAxisMap = differenceBy(newAxis, oldAxis, 'seriesId').reduce((pre, next) => {
pre[next.id] = next
return pre
}, {}) as Record<string, SeriesFormatter>
//
const removedAxisMap = differenceBy(oldAxis, newAxis, 'seriesId').reduce((pre, next) => {
pre[next.seriesId] = next
return pre
}, {}) as Record<string, SeriesFormatter>
const quotaIds = quotaData.value?.map(i => i.id)
state.tooltipForm.seriesTooltipFormatter = state.tooltipForm.seriesTooltipFormatter?.filter(i =>
quotaIds?.includes(i.id)
)
const dupAxis: SeriesFormatter[] = []
state.tooltipForm.seriesTooltipFormatter?.forEach(ele => {
if (addedAxisMap[ele.id]) {
//
ele.show = true
if (ele.seriesId === ele.id) {
ele.seriesId = addedAxisMap[ele.id].seriesId
ele.axisType = addedAxisMap[ele.id].axisType
} else {
//
const tmp = cloneDeep(addedAxisMap[ele.id])
tmp.show = true
dupAxis.push(tmp)
}
}
if (removedAxisMap[ele.seriesId]) {
ele.show = false
ele.seriesId = ele.id
}
ele.chartShowName = axisMap[ele.seriesId]?.chartShowName
ele.summary = axisMap[ele.seriesId]?.summary ?? ele.summary
})
//
state.tooltipForm.seriesTooltipFormatter =
state.tooltipForm.seriesTooltipFormatter.concat(dupAxis)
state.tooltipForm.seriesTooltipFormatter = partition(
state.tooltipForm.seriesTooltipFormatter,
ele => axisMap[ele.seriesId]
).flat()
if (removedAxisMap[curSeriesFormatter.value?.seriesId]) {
curSeriesFormatter.value = state.tooltipForm.seriesTooltipFormatter?.[0]
}
if (!newAxis.length) {
curSeriesFormatter.value = {}
}
emit('onTooltipChange', { data: state.tooltipForm, render: false })
emit('onExtTooltipChange', extTooltip.value)
}
const AXIS_PROP: AxisType[] = ['yAxis', 'yAxisExt', 'extBubble']
const quotaAxis = computed(() => {
let result = []
const axisProp: AxisType[] = ['yAxis', 'yAxisExt', 'extBubble']
axisProp.forEach(prop => {
AXIS_PROP.forEach(prop => {
if (!chartViewInstance.value?.axis?.includes(prop)) {
return
}
const axis = props.chart[prop]
axis?.forEach(item => {
item.axisType = prop
item.seriesId = `${item.id}-${prop}`
result.push(item)
})
axis?.forEach(item => result.push(item))
})
return result
})
@ -205,13 +136,18 @@ const AGGREGATION_TYPE = [
{ name: t('chart.count'), value: 'count' },
{ name: t('chart.count_distinct'), value: 'count_distinct' }
]
watch(
() => cloneDeep(quotaAxis.value),
(newVal, oldVal) => {
updateSeriesTooltip(newVal, oldVal)
},
{ deep: true }
)
const COUNT_AGGREGATION_TYPE = [
{ name: t('chart.count'), value: 'count' },
{ name: t('chart.count_distinct'), value: 'count_distinct' }
]
const COUNT_DE_TYPE = [0, 1, 5]
const aggregationList = computed(() => {
if (COUNT_DE_TYPE.includes(curSeriesFormatter.value?.deType)) {
return COUNT_AGGREGATION_TYPE
}
return AGGREGATION_TYPE
})
watch(
[() => props.chart.customAttr.tooltip, () => props.chart.customAttr.tooltip.show],
() => {
@ -262,7 +198,13 @@ const init = () => {
if (customAttr.tooltip) {
state.tooltipForm = defaultsDeep(customAttr.tooltip, cloneDeep(DEFAULT_TOOLTIP))
formatterSelector.value?.blur()
//
const formatter = state.tooltipForm.seriesTooltipFormatter
if (!formatter.length) {
quotaData.value?.forEach(i => formatter.push({ ...i, seriesId: i.id, show: false }))
curSeriesFormatter.value = {}
return
}
const seriesAxisMap = formatter.reduce((pre, next) => {
next.seriesId = next.seriesId ?? next.id
pre[next.seriesId] = next
@ -277,10 +219,138 @@ const init = () => {
}
}
const showProperty = prop => props.propertyInner?.includes(prop)
const showProperty = prop => {
const instance = chartViewManager.getChartView(props.chart.render, props.chart.type)
if (instance) {
return instance.propertyInner['tooltip-selector'].includes(prop)
}
return props.propertyInner?.includes(prop)
}
const updateSeriesTooltipFormatter = (form: AxisEditForm) => {
const { axisType, editType } = form
if (
!showSeriesTooltipFormatter.value ||
!state.tooltipForm.seriesTooltipFormatter.length ||
!quotaData.value?.length ||
!AXIS_PROP.includes(axisType)
) {
return
}
switch (editType) {
case 'add':
addAxis(form)
break
case 'remove':
removeAxis(form)
break
case 'update':
updateAxis(form)
break
default:
break
}
emit('onTooltipChange', { data: state.tooltipForm, render: false }, 'seriesTooltipFormatter')
emit('onExtTooltipChange', extTooltip.value)
}
const addAxis = (form: AxisEditForm) => {
const { axis, axisType } = form
const axisMap = axis.reduce((pre, next) => {
next.axisType = axisType
next.seriesId = `${next.id}-${axisType}`
pre[next.id] = next
return pre
}, {})
const dupAxis = []
state.tooltipForm.seriesTooltipFormatter.forEach(ele => {
if (axisMap[ele.id]) {
//
ele.show = true
if (ele.seriesId === ele.id) {
ele.seriesId = axisMap[ele.id].seriesId
ele.axisType = axisMap[ele.id].axisType
ele.summary = axisMap[ele.id].summary
ele.chartShowName = axisMap[ele.id].chartShowName
} else {
//
const tmp = cloneDeep(axisMap[ele.id])
tmp.show = true
dupAxis.push(tmp)
}
}
})
state.tooltipForm.seriesTooltipFormatter =
state.tooltipForm.seriesTooltipFormatter.concat(dupAxis)
state.tooltipForm.seriesTooltipFormatter = partition(
state.tooltipForm.seriesTooltipFormatter,
ele => quotaAxis.value.findIndex(item => item.id === ele.id) !== -1
).flat()
}
const removeAxis = (form: AxisEditForm) => {
const { axis, axisType } = form
const axisMap = axis.reduce((pre, next) => {
if (!next) {
return pre
}
next.axisType = axisType
next.seriesId = `${next.id}-${axisType}`
pre[next.seriesId] = next
return pre
}, {})
const quotaIds = quotaData.value?.map(i => i.id)
const formatterDupMap = state.tooltipForm.seriesTooltipFormatter.reduce((pre, next) => {
if (pre[next.id] !== undefined) {
pre[`${next.id}-${axisType}`] = true
} else {
pre[next.id] = false
}
return pre
}, {})
state.tooltipForm.seriesTooltipFormatter = state.tooltipForm.seriesTooltipFormatter?.filter(
i => quotaIds?.includes(i.id) && !formatterDupMap[i.seriesId]
)
state.tooltipForm.seriesTooltipFormatter.forEach(ele => {
if (axisMap[ele.seriesId]) {
//
ele.show = false
ele.seriesId = ele.id
ele.summary = 'sum'
}
})
state.tooltipForm.seriesTooltipFormatter = partition(
state.tooltipForm.seriesTooltipFormatter,
ele => quotaAxis.value.findIndex(item => item.id === ele.id) !== -1
).flat()
if (!quotaAxis.value?.length) {
curSeriesFormatter.value = {}
return
}
if (axisMap[curSeriesFormatter.value?.seriesId]) {
curSeriesFormatter.value = state.tooltipForm.seriesTooltipFormatter?.[0]
}
}
const updateAxis = (form: AxisEditForm) => {
const { axis, axisType } = form
const axisMap = axis.reduce((pre, next) => {
if (!next) {
return pre
}
next.axisType = axisType
next.seriesId = `${next.id}-${axisType}`
pre[next.seriesId] = next
return pre
}, {})
state.tooltipForm.seriesTooltipFormatter.forEach(ele => {
if (axisMap[ele.seriesId]) {
ele.chartShowName = axisMap[ele.seriesId]?.chartShowName
ele.summary = axisMap[ele.seriesId]?.summary ?? ele.summary
}
})
}
onMounted(() => {
init()
useEmitt({ name: 'addAxis', callback: updateSeriesTooltipFormatter })
useEmitt({ name: 'removeAxis', callback: updateSeriesTooltipFormatter })
useEmitt({ name: 'updateAxis', callback: updateSeriesTooltipFormatter })
})
</script>
@ -482,10 +552,11 @@ onMounted(() => {
<template v-if="curSeriesFormatter?.seriesId">
<el-form-item class="form-item form-item-checkbox" :class="'form-item-' + themes">
<el-checkbox
size="small"
@change="changeTooltipAttr('seriesTooltipFormatter', true)"
:disabled="!formatterEditable"
v-model="curSeriesFormatter.show"
size="small"
label="quota"
@change="changeTooltipAttr('seriesTooltipFormatter', true)"
>
{{ t('chart.show') }}
</el-checkbox>
@ -520,7 +591,7 @@ onMounted(() => {
@change="changeTooltipAttr('seriesTooltipFormatter', true)"
>
<el-option
v-for="item in AGGREGATION_TYPE"
v-for="item in aggregationList"
:label="item.name"
:value="item.value"
:key="item.value"

View File

@ -15,7 +15,6 @@ import {
import Icon from '@/components/icon-custom/src/Icon.vue'
import type { FormInstance, FormRules } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import { Field, getFieldByDQ } from '@/api/chart'
import { Tree } from '../../../visualized/data/dataset/form/CreatDsGroup.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { ElMessage, ElTreeSelect } from 'element-plus-secondary'
@ -43,13 +42,14 @@ import CustomSortEdit from '@/views/chart/components/editor/drag-item/components
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import CalcFieldEdit from '@/views/visualized/data/dataset/form/CalcFieldEdit.vue'
import { getFieldName, guid } from '@/views/visualized/data/dataset/form/util'
import { cloneDeep } from 'lodash-es'
import { cloneDeep, get } from 'lodash-es'
import { deleteField, saveField } from '@/api/dataset'
import { getWorldTree } from '@/api/map'
import chartViewManager from '@/views/chart/components/js/panel'
import DatasetSelect from '@/views/chart/components/editor/dataset-select/DatasetSelect.vue'
import { useDraggable } from '@vueuse/core'
import _ from 'lodash'
import { set, concat, keys } from 'lodash-es'
import { Field, getFieldByDQ } from '@/api/chart'
const snapshotStore = snapshotStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
@ -63,7 +63,7 @@ const tabActiveVQuery = ref('style')
const datasetSelector = ref(null)
const curDatasetWeight = ref(0)
const renameForm = ref<FormInstance>()
const { emitter } = useEmitt()
const props = defineProps({
view: {
type: Object as PropType<ChartObj>,
@ -144,17 +144,10 @@ const state = reactive({
useless: null
})
watch(
[() => props.view.tableId],
() => {
getFields(props.view.tableId, props.view.id)
},
{ deep: true }
)
watch(
[() => view.value['tableId']],
() => {
getFields(props.view.tableId, props.view.id)
const nodeId = view.value['tableId']
if (!!nodeId) {
cacheId = nodeId as unknown as string
@ -166,7 +159,28 @@ watch(
},
{ deep: true }
)
const getFields = (id, chartId) => {
if (id && chartId) {
getFieldByDQ(id, chartId)
.then(res => {
state.dimension = (res.dimensionList as unknown as Field[]) || []
state.quota = (res.quotaList as unknown as Field[]) || []
state.dimensionData = JSON.parse(JSON.stringify(state.dimension))
state.quotaData = JSON.parse(JSON.stringify(state.quota))
})
.catch(() => {
state.dimension = []
state.quota = []
state.dimensionData = []
state.quotaData = []
})
} else {
state.dimension = []
state.quota = []
state.dimensionData = []
state.quotaData = []
}
}
watch(
[() => state.searchField],
newVal => {
@ -222,31 +236,8 @@ const filterNode = (value, data) => {
return data.name?.includes(value)
}
const getFields = (id, chartId) => {
if (id && chartId) {
getFieldByDQ(id, chartId)
.then(res => {
state.dimension = (res.dimensionList as unknown as Field[]) || []
state.quota = (res.quotaList as unknown as Field[]) || []
state.dimensionData = JSON.parse(JSON.stringify(state.dimension))
state.quotaData = JSON.parse(JSON.stringify(state.quota))
})
.catch(() => {
state.dimension = []
state.quota = []
state.dimensionData = []
state.quotaData = []
})
} else {
state.dimension = []
state.quota = []
state.dimensionData = []
state.quotaData = []
}
}
const allFields = computed(() => {
return _.concat(state.quotaData, state.dimensionData)
return concat(state.quotaData, state.dimensionData)
})
const queryList = computed(() => {
@ -266,9 +257,9 @@ const queryList = computed(() => {
const quotaData = computed(() => {
if (view.value?.type === 'table-info') {
return state.quotaData?.filter(item => item.id !== '-1')
return state.quota?.filter(item => item.id !== '-1')
}
return state.quotaData
return state.quota
})
provide('quotaData', quotaData)
@ -313,23 +304,29 @@ const dimensionItemRemove = item => {
}
}
const quotaItemChange = () => {
const quotaItemChange = (axis: Axis, axisType: AxisType) => {
recordSnapshotInfo('calcData')
// do quotaItemChange
emitter.emit('updateAxis', { axisType, axis: [axis], editType: 'update' })
}
const quotaItemRemove = item => {
recordSnapshotInfo('calcData')
let axisType: AxisType = item.removeType
let axis
if (item.removeType === 'quota') {
view.value.yAxis.splice(item.index, 1)
axisType = 'yAxis'
axis = view.value.yAxis.splice(item.index, 1)
} else if (item.removeType === 'quotaExt') {
view.value.yAxisExt.splice(item.index, 1)
axisType = 'yAxisExt'
axis = view.value.yAxisExt.splice(item.index, 1)
} else if (item.removeType === 'extLabel') {
view.value.extLabel.splice(item.index, 1)
axis = view.value.extLabel.splice(item.index, 1)
} else if (item.removeType === 'extTooltip') {
view.value.extTooltip.splice(item.index, 1)
axis = view.value.extTooltip.splice(item.index, 1)
} else if (item.removeType === 'extBubble') {
view.value.extBubble.splice(item.index, 1)
axis = view.value.extBubble.splice(item.index, 1)
}
useEmitt().emitter.emit('removeAxis', { axisType, axis, editType: 'remove' })
}
const arrowIcon = () => {
return h(Icon, { name: 'icon_down_outlined-1' })
@ -394,7 +391,7 @@ const onMove = e => {
// drag
const dragCheckType = (list, type) => {
if (list && list.length > 0) {
var valid = true
let valid = true
for (let i = 0; i < list.length; i++) {
if (list[i].groupType !== type) {
list.splice(i, 1)
@ -407,6 +404,7 @@ const dragCheckType = (list, type) => {
type: 'warning'
})
}
return valid
}
}
const dragMoveDuplicate = (list, e, mode) => {
@ -418,6 +416,7 @@ const dragMoveDuplicate = (list, e, mode) => {
})
if (dup && dup.length > 1) {
list.splice(e.newDraggableIndex, 1)
return dup
}
}
}
@ -439,14 +438,37 @@ const addAxis = (e, axis: AxisType) => {
return
}
const { type, limit, duplicate } = axisSpec
let typeValid, dup
if (type) {
dragCheckType(view.value[axis], type)
typeValid = dragCheckType(view.value[axis], type)
}
if (!duplicate) {
dragMoveDuplicate(view.value[axis], e, 'chart')
dup = dragMoveDuplicate(view.value[axis], e, 'chart')
}
if (limit) {
view.value[axis] = view.value[axis].splice(0, limit)
if (view.value[axis].length > limit) {
const removedAxis = view.value[axis].splice(limit)
if (e.newDraggableIndex + 1 <= limit) {
emitter.emit('removeAxis', { axisType: axis, axis: removedAxis, editType: 'remove' })
emitter.emit('addAxis', {
axisType: axis,
axis: [view.value[axis][e.newDraggableIndex]],
editType: 'add'
})
}
} else {
if (!dup && typeValid) {
emitter.emit('addAxis', {
axisType: axis,
axis: [view.value[axis][e.newDraggableIndex]],
editType: 'add'
})
}
}
if (view.value.type === 'line') {
if (view.value?.xAxisExt?.length && view.value?.yAxis?.length > 1) {
const axis = view.value.yAxis.splice(1)
emitter.emit('removeAxis', { axisType: 'yAxis', axis, editType: 'remove' })
}
}
}
@ -505,6 +527,13 @@ const moveToQuota = e => {
dragMoveDuplicate(state.quotaData, e, 'ds')
}
const onAxisChange = (e, axis: AxisType) => {
if (e.removed) {
const { element } = e.removed
emitter.emit('removeAxis', { axisType: axis, axis: [element], editType: 'remove' })
}
}
const calcData = (view, resetDrill = false, updateQuery = '') => {
if (
view.refreshTime === '' ||
@ -551,26 +580,36 @@ const onTypeChange = (render, type) => {
view.value = chartViewInstance.setupDefaultOptions(view.value) as unknown as ChartObj
//
const axisConfig = chartViewInstance.axisConfig
_.keys(axisConfig).forEach((axis: AxisType) => {
keys(axisConfig).forEach((axis: AxisType) => {
const axisArr = view.value[axis] as Axis[]
if (!axisArr?.length) {
return
}
const axisSpec = axisConfig[axis]
const { type, limit } = axisSpec
const removedAxis = []
// check type
if (type) {
for (let i = axisArr.length - 1; i >= 0; i--) {
if (axisArr[i].groupType !== type) {
axisArr.splice(i, 1)
const [axis] = axisArr.splice(i, 1)
removedAxis.push(axis)
}
}
}
// check limit
if (limit && axisArr.length) {
axisArr.splice(0, axisArr.length - limit)
if (limit && limit < axisArr.length) {
axisArr.splice(limit).forEach(i => removedAxis.push(i))
}
removedAxis.length &&
emitter.emit('removeAxis', { axisType: axis, axis: removedAxis, editType: 'remove' })
})
if (view.value.type === 'line') {
if (view.value?.xAxisExt?.length && view.value?.yAxis?.length > 1) {
const axis = view.value.yAxis.splice(1)
emitter.emit('removeAxis', { axisType: 'yAxis', axis, editType: 'remove' })
}
}
}
curComponent.value.innerType = type
calcData(view.value, true)
@ -617,23 +656,22 @@ const onLabelChange = val => {
view.value.customAttr.label = val
renderChart(view.value)
}
watch([() => view.value.xAxisExt?.length, () => view.value.yAxis?.length], () => {
if (view.value.type === 'line') {
if (view.value?.xAxisExt?.length && view.value?.yAxis?.length > 1) {
view.value.yAxis.splice(1)
}
}
})
const onTooltipChange = (chartForm: ChartEditorForm<ChartTooltipAttr>) => {
const onTooltipChange = (chartForm: ChartEditorForm<ChartTooltipAttr>, prop: string) => {
const { data, requestData, render } = chartForm
let tooltipObj = data
if (!data) {
view.value.customAttr.tooltip = chartForm as unknown as ChartTooltipAttr
tooltipObj = chartForm as unknown as ChartTooltipAttr
}
if (prop) {
const val = get(tooltipObj, prop)
set(view.value.customAttr.tooltip, prop, val)
} else {
view.value.customAttr.tooltip = data
view.value.customAttr.tooltip = tooltipObj
}
if (requestData) {
calcData(view.value)
return
}
// for compatibility
if (render !== false) {
@ -716,29 +754,31 @@ const removeItems = (
_type: 'xAxis' | 'xAxisExt' | 'extStack' | 'yAxis' | 'extBubble' | 'customFilter' | 'drillFields'
) => {
recordSnapshotInfo('calcData')
let axis = []
switch (_type) {
case 'xAxis':
view.value.xAxis = []
axis = view.value.xAxis?.splice(0)
break
case 'xAxisExt':
view.value.xAxisExt = []
axis = view.value.xAxisExt?.splice(0)
break
case 'extStack':
view.value.extStack = []
axis = view.value.extStack?.splice(0)
break
case 'yAxis':
view.value.yAxis = []
axis = view.value.yAxis?.splice(0)
break
case 'extBubble':
view.value.extBubble = []
axis = view.value.extBubble?.splice(0)
break
case 'customFilter':
view.value.customFilter = []
axis = view.value.customFilter?.splice(0)
break
case 'drillFields':
view.value.drillFields = []
axis = view.value.drillFields?.splice(0)
break
}
axis?.length && emitter.emit('removeAxis', { axisType: _type, axis, editType: 'remove' })
}
const saveRename = ref => {
@ -746,14 +786,19 @@ const saveRename = ref => {
ref.validate(valid => {
if (valid) {
const { renameType, index, chartShowName } = state.itemForm
let axisType, axis
switch (renameType) {
case 'quota':
axisType = 'yAxis'
axis = view.value.yAxis[index]
view.value.yAxis[index].chartShowName = chartShowName
break
case 'dimension':
view.value.xAxis[index].chartShowName = chartShowName
break
case 'quotaExt':
axisType = 'yAxisExt'
axis = view.value.yAxisExt[index]
view.value.yAxisExt[index].chartShowName = chartShowName
break
case 'dimensionExt':
@ -763,6 +808,8 @@ const saveRename = ref => {
view.value.extStack[index].chartShowName = chartShowName
break
case 'extBubble':
axisType = 'extBubble'
axis = view.value.extBubble[index]
view.value.extBubble[index].chartShowName = chartShowName
break
case 'extLabel':
@ -774,6 +821,7 @@ const saveRename = ref => {
default:
break
}
axisType && emitter.emit('updateAxis', { axisType, axis: [axis], editType: 'update' })
closeRename()
} else {
return false
@ -1356,6 +1404,7 @@ const onRefreshChange = val => {
class="drag-block-style"
:class="{ dark: themes === 'dark' }"
@add="addYaxis"
@change="e => onAxisChange(e, 'yAxis')"
>
<template #item="{ element, index }">
<quota-item
@ -1366,7 +1415,7 @@ const onRefreshChange = val => {
:index="index"
type="quota"
:themes="props.themes"
@onQuotaItemChange="quotaItemChange"
@onQuotaItemChange="item => quotaItemChange(item, 'yAxis')"
@onQuotaItemRemove="quotaItemRemove"
@onNameEdit="showRename"
@editItemFilter="showQuotaEditFilter"
@ -1403,6 +1452,7 @@ const onRefreshChange = val => {
class="drag-block-style"
:class="{ dark: themes === 'dark' }"
@add="addExtBubble"
@change="e => onAxisChange(e, 'extBubble')"
>
<template #item="{ element, index }">
<quota-item
@ -1413,7 +1463,7 @@ const onRefreshChange = val => {
:index="index"
type="extBubble"
:themes="props.themes"
@onQuotaItemChange="quotaItemChange"
@onQuotaItemChange="item => quotaItemChange(item, 'extBubble')"
@onQuotaItemRemove="quotaItemRemove"
@onNameEdit="showRename"
@editItemFilter="showQuotaEditFilter"

View File

@ -116,6 +116,7 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
pre[next.id] = next
return pre
}, {})
// 默认是灰色
tmpOptions.label.style.fill = DEFAULT_LABEL.color
const label = {
fields: [],
@ -242,6 +243,7 @@ export class StackBar extends Bar {
return baseOptions
}
const { label: labelAttr } = parseJson(chart.customAttr)
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum) {

View File

@ -164,11 +164,12 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
}
setupDefaultOptions(chart: ChartObj): ChartObj {
const { customAttr } = chart
const { customAttr, senior } = chart
const { label } = customAttr
if (!['left', 'middle', 'right'].includes(label.position)) {
label.position = 'middle'
}
senior.functionCfg.emptyDataStrategy = 'ignoreData'
return chart
}
@ -185,6 +186,7 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
pre[next.id] = next
return pre
}, {})
// 默认灰色
tmpOptions.label.style.fill = DEFAULT_LABEL.color
const label = {
fields: [],
@ -272,6 +274,7 @@ export class HorizontalStackBar extends HorizontalBar {
return baseOptions
}
const { label: labelAttr } = parseJson(chart.customAttr)
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
formatter: function (param: Datum) {

View File

@ -78,7 +78,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
const options = this.setupOptions(chart, initOptions)
const newChart = new G2WordCloud(container, options)
newChart.on('point:click', param => {
action({ data: { data: param.data.data.datum } })
action({ x: param.x, y: param.y, data: { data: param.data.data.datum } })
})
return newChart
}

View File

@ -253,9 +253,8 @@ export function handleEmptyDataStrategy<O extends PickOptions>(chart: Chart, opt
handleIgnoreData(data)
return options
}
const yAxis = JSON.parse(JSON.stringify(chart.yAxis))
const extAxis = JSON.parse(JSON.stringify(chart.xAxisExt))
const multiDimension = yAxis?.length >= 2 || extAxis?.length > 0
const { yAxis, xAxisExt, extStack } = chart
const multiDimension = yAxis?.length >= 2 || xAxisExt?.length > 0 || extStack?.length > 0
switch (strategy) {
case 'breakLine': {
if (multiDimension) {

View File

@ -434,7 +434,7 @@ onMounted(() => {
}
})
useEmitt({
name: 'calc-data-' + view.value.id,
name: 'calcData-' + view.value.id,
callback: function (val) {
if (!state.initReady) {
return

View File

@ -311,8 +311,7 @@ defineExpose({
</el-tooltip>
<el-tooltip :content="newResourceLabel" placement="top" effect="dark">
<el-icon class="custom-icon btn" @click="addOperation('newLeaf', null, 'leaf', true)">
<Icon v-if="curCanvasType === 'dashboard'" name="dv-new"></Icon>
<Icon v-else name="dv-screen-new" />
<Icon name="icon_file-add_outlined" />
</el-icon>
</el-tooltip>
</div>

View File

@ -40,7 +40,7 @@ const handleClick = (tab, event: Event) => {
.sys-setting-p {
width: 100%;
background: var(--ContentBG, #ffffff);
height: calc(100% - 95px);
height: calc(100vh - 176px);
box-sizing: border-box;
margin-top: 12px;
}

View File

@ -1,6 +1,6 @@
<template>
<el-container class="geometry-container">
<el-aside width="200px" class="geonetry-aside">
<el-aside class="geonetry-aside">
<div class="geo-title">
<span>{{ t('online_map.geometry') }}</span>
<span class="add-icon-span">
@ -10,7 +10,13 @@
</span>
</div>
<div class="geo-search">
<el-input class="m16 w100" v-model="keyword" clearable :placeholder="t('commons.search')">
<el-input
class="m16 w100"
v-model="keyword"
clearable
:placeholder="t('commons.search')"
@change="filterResource"
>
<template #prefix>
<el-icon>
<Icon name="icon_search-outline_outlined"></Icon>
@ -19,90 +25,118 @@
</el-input>
</div>
<div class="map-tree-container">
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
<el-scrollbar class="menu-tree">
<el-tree
menu
ref="areaTreeRef"
node-key="id"
:data="treeData"
@node-click="handleNodeClick"
:highlight-current="true"
:expand-on-click-node="false"
:default-expand-all="false"
:filter-node-method="filterResourceNode"
>
<template #default="{ node, data }">
<span class="custom-tree-node" :class="{ 'is-disabled': node.disabled || data.root }">
<span
:title="data.name"
v-html="data.colorName && keyword ? data.colorName : data.name"
/>
</span>
</template>
</el-tree>
</el-scrollbar>
</div>
</el-aside>
<el-main>地理信息内容区域</el-main>
<el-main class="geometry-main">
<div class="geo-content-container" v-if="!selectedData">
<EmptyBackground img-type="noneWhite" description="请在左侧选择区域" />
</div>
<div v-else class="geo-content-container">
<div class="geo-content-top">
<span>{{ selectedData.name }}</span>
</div>
<div class="geo-content-middle">
<div class="geo-area">
<div class="area-label"><span>区域代码</span></div>
<div class="area-content">
<span>{{ selectedData.id }}</span>
</div>
</div>
<div class="geo-area">
<div class="area-label"><span>上级区域</span></div>
<div class="area-content">
<span>{{ selectedData.parentName }}</span>
<span v-if="selectedData.pid" class="area-secondary">{{
'(' + selectedData.pid + ')'
}}</span>
</div>
</div>
</div>
<div class="geo-content-bottom">
<div class="area-label"><span>坐标文件</span></div>
<el-scrollbar class="area-content-geo">
<span>{{ selectedData.geoJson }}</span>
</el-scrollbar>
</div>
</div>
</el-main>
</el-container>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { getWorldTree } from '@/api/map'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
import { getGeoJsonFile } from '@/views/chart/components/js/util'
import { cloneDeep } from 'lodash-es'
import { setColorName } from '@/utils/utils'
const { t } = useI18n()
const keyword = ref('')
const treeData = ref([])
interface Tree {
label: string
children?: Tree[]
}
const areaTreeRef = ref(null)
const handleNodeClick = (data: Tree) => {
console.log(data)
}
const selectedData = ref(null)
const data: Tree[] = [
{
label: 'Level one 1',
children: [
{
label: 'Level two 1-1',
children: [
{
label: 'Level three 1-1-1'
}
]
}
]
},
{
label: 'Level one 2',
children: [
{
label: 'Level two 2-1',
children: [
{
label: 'Level three 2-1-1'
}
]
},
{
label: 'Level two 2-2',
children: [
{
label: 'Level three 2-2-1'
}
]
}
]
},
{
label: 'Level one 3',
children: [
{
label: 'Level two 3-1',
children: [
{
label: 'Level three 3-1-1'
}
]
},
{
label: 'Level two 3-2',
children: [
{
label: 'Level three 3-2-1'
}
]
}
]
const handleNodeClick = async (data: Tree) => {
selectedData.value = data
const geoJson = cloneDeep(await getGeoJsonFile(data['id']))
selectedData.value['geoJson'] = geoJson
const pid = data['pid']
if (pid) {
const parent = areaTreeRef.value.getNode(pid)
if (parent) {
selectedData.value.parentName = parent.data.name
}
}
]
const defaultProps = {
children: 'children',
label: 'label'
}
const filterResource = val => {
areaTreeRef.value?.filter(val)
}
const filterResourceNode = (value: string, data) => {
setColorName(data, value)
if (!value) return true
return data.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
}
const loadTreeData = () => {
getWorldTree()
.then(res => {
const root = res.data
treeData.value = [root]
})
.catch(e => {
console.error(e)
})
}
loadTreeData()
</script>
<style lang="less" scoped>
@ -112,6 +146,7 @@ const defaultProps = {
width: 280px !important;
border-right: 1px solid #1f232926;
padding: 16px;
height: 100%;
.geo-title {
display: flex;
justify-content: space-between;
@ -123,6 +158,7 @@ const defaultProps = {
line-height: 24px;
}
.add-icon-span {
display: none;
color: #3370ff;
height: 20px;
width: 20px;
@ -140,6 +176,79 @@ const defaultProps = {
.geo-search {
margin-bottom: 16px;
}
.map-tree-container {
height: calc(100% - 96px);
overflow-y: auto;
}
}
.geometry-main {
padding: 16px !important;
}
}
.geo-content-container {
width: 100%;
height: 100%;
.geo-content-top {
height: 24px;
line-height: 24px;
margin-bottom: 16px;
span {
font-weight: 500;
font-size: 16px;
color: #1f2329;
}
}
.geo-content-middle {
display: flex;
.geo-area {
height: 48px;
width: 50%;
}
margin-bottom: 16px;
}
:deep(.area-label) {
height: 22px;
line-height: 22px;
span {
font-size: 14px;
color: #646a73;
font-weight: 400;
}
}
:deep(.area-content) {
line-height: 22px;
height: 22px;
span {
font-size: 14px;
color: #1f2329;
font-weight: 400;
}
.area-secondary {
color: #646a73;
}
}
.geo-content-bottom {
width: 100%;
height: calc(100% - 110px);
.area-content-geo {
line-height: 22px;
overflow-x: hidden;
overflow-y: auto;
height: calc(100% - 30px);
span {
font-size: 14px;
color: #1f2329;
font-weight: 400;
}
}
}
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
box-sizing: content-box;
padding-right: 4px;
overflow: hidden;
}
</style>

View File

@ -443,7 +443,7 @@ const filterNode = (value: string, data: BusiTreeNode) => {
</el-tooltip>
<el-tooltip class="box-item" effect="dark" content="新建数据集" placement="top">
<el-icon class="custom-icon btn" @click="createDataset">
<Icon name="icon_dataset_outlined" />
<Icon name="icon_file-add_outlined" />
</el-icon>
</el-tooltip>
</div>

View File

@ -614,6 +614,18 @@ defineExpose({
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.port')" prop="configuration.port">
<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">
<el-input
v-model="form.configuration.dataBase"
@ -687,18 +699,6 @@ defineExpose({
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.port')" prop="configuration.port">
<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
v-if="form.type == 'oracle'"
:label="t('datasource.connection_mode')"

View File

@ -654,7 +654,7 @@ onMounted(() => {
</el-tooltip>
<el-tooltip effect="dark" :content="t('datasource.create')" placement="top">
<el-icon class="custom-icon btn" @click="createDatasource">
<Icon name="icon_dataset_outlined" />
<Icon name="icon_file-add_outlined" />
</el-icon>
</el-tooltip>
</div>

View File

@ -336,8 +336,9 @@ const getEmptyDesc = (): string => {
padding: 8px 24px 0 24px;
background: #fff;
border-radius: 4px;
height: calc(100% - 280px);
margin-top: 16px;
// height: calc(100% - 280px);
// margin-top: 16px;
height: 100%;
.select-type-list {
width: 104px;

View File

@ -17,6 +17,8 @@ const { t } = useI18n()
const busiDataMap = computed(() => interactiveStore.getData)
const busiCountCardList = ref([])
const showTemplate = ref(false)
const router = useRouter()
const quickCreationList = shallowRef([
@ -159,7 +161,7 @@ fillCardInfo()
</div>
</div>
<div class="template-market-dashboard">
<div class="template-market">
<div v-if="showTemplate" class="template-market">
<div class="label">
模版市场
<div class="expand-all">

@ -1 +1 @@
Subproject commit 1ca8187376d058b65d163dbad52ff6159ca024f3
Subproject commit 912a648808e87bdf1e5a11f7cdc6467b89fd9347

View File

@ -1,4 +1,4 @@
pisix:
apisix:
node_listen: 9080 # APISIX listening port
enable_ipv6: false

View File

@ -2,7 +2,7 @@ version: "3"
services:
apisix-dashboard:
image: apache/apisix-dashboard:3.0.1-alpine
image: registry.cn-qingdao.aliyuncs.com/dataease/apisix-dashboard:3.0.1-alpine
container_name: apisix-dashboard
restart: always
volumes:
@ -13,7 +13,7 @@ services:
dataease-network:
apisix:
image: apache/apisix:3.4.1-debian
image: registry.cn-qingdao.aliyuncs.com/dataease/apisix:3.6.0-debian
container_name: apisix
environment:
- TZ=Asia/Shanghai
@ -33,7 +33,7 @@ services:
dataease-network:
etcd:
image: bitnami/etcd:3.4.15
image: registry.cn-qingdao.aliyuncs.com/dataease/etcd:3.5.10
container_name: apisix-etcd
restart: always
volumes:

View File

@ -2,7 +2,7 @@ version: '2.1'
services:
DE_MYSQL_HOST:
image: mysql:8.1.0
image: registry.cn-qingdao.aliyuncs.com/dataease/mysql:8.1.0
container_name: ${DE_MYSQL_HOST}
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u${DE_MYSQL_USER}", "-p${DE_MYSQL_PASSWORD}", "--protocol","tcp"]

View File

@ -2,7 +2,7 @@ version: '2.1'
services:
dataease:
image: registry.cn-qingdao.aliyuncs.com/dataease/dataease2.0:DE_TAG
image: registry.cn-qingdao.aliyuncs.com/dataease/dataease:DE_TAG
container_name: dataease
ports:
- ${DE_PORT}:8100

View File

@ -14,4 +14,4 @@ dataease:
origin-list: localhost:8080,localhost:8100,localhost:9080
apisix-api:
domain: http://apisix:9180
key: edd1c9f034335f136f87ad84b625c8f1
key: DE_APISIX_KEY

View File

@ -44,7 +44,7 @@ function usage() {
echo " version 查看 DATAEASE 版本"
}
function _check_apisix_init() {
if [[ $DE_INSTALL_MODE = "enterprise" ]];then
if [[ $DE_INSTALL_MODE != "community" ]];then
_prepare_apisix
fi
}
@ -56,7 +56,7 @@ function _prepare_apisix() {
cd $DE_RUNNING_BASE
env | grep DE_ >.env
sed -i -e "s/DE_APISIX_KEY/${DE_APISIX_KEY}/g" $DE_RUNNING_BASE/apisix/apisix_conf/config.yaml
sed -i -e "s/key:.*/key: ${DE_APISIX_KEY}/g" $DE_RUNNING_BASE/conf/application.yml
sed -i -e "s/DE_APISIX_KEY/${DE_APISIX_KEY}/g" $DE_RUNNING_BASE/conf/application.yml
fi
compose_files="${compose_files} -f docker-compose-apisix.yml"
}
@ -83,7 +83,7 @@ function _healthcheck() {
echo
}
function _get_current_version() {
de_current_version=$(grep "^ image:.*dataease2.0:" ${DE_RUNNING_BASE}/docker-compose.yml | awk -F'dataease2.0:' '{print $2}')
de_current_version=$(grep "^ image:.*dataease:" ${DE_RUNNING_BASE}/docker-compose.yml | awk -F'dataease:' '{print $2}')
echo $de_current_version
}
function status() {
@ -143,7 +143,7 @@ function version() {
}
function upgrade() {
echo
git_urls=('gitee.com' 'github.com')
git_urls=('github.com')
if [[ -x "$(command -v python)" ]]; then
py_cmd='python'
elif [[ -x "$(command -v python3)" ]]; then
@ -170,7 +170,7 @@ function upgrade() {
done
if [[ "x${server_url}" == "x" ]]; then
echo "没有找到稳定的下载服务器,请稍候重试"
echo "没有找到稳定的下载服务器,请访问 https://community.fit2cloud.com/#/products/dataease/downloads 下载离线安装包"
exit 1
fi

View File

@ -6,7 +6,7 @@ DE_PORT=8100
## 登录超时时间单位min。如果不设置则默认8小时也就是480
DE_LOGIN_TIMEOUT=480
## 安装模式
DE_INSTALL_MODE=enterprise
DE_INSTALL_MODE=community
# 数据库配置
## 是否使用外部数据库

View File

@ -183,7 +183,7 @@ else
cd ${DE_RUN_BASE} && docker-compose $compose_files pull 2>&1
DEVERSION=$(cat ${CURRENT_DIR}/dataease/templates/version)
#curl -sfL https://resource.fit2cloud.com/installation-log.sh | sh -s de ${INSTALL_TYPE} ${DEVERSION}
curl -sfL https://resource.fit2cloud.com/installation-log.sh | sh -s de ${INSTALL_TYPE} ${DEVERSION}
cd -
fi
@ -258,5 +258,4 @@ if [[ $http_code != 200 ]];then
fi
echo -e "======================= 安装完成 =======================\n" 2>&1 | tee -a ${CURRENT_DIR}/install.log
echo -e "请通过以下方式访问:\n URL: http://\$LOCAL_IP:$DE_PORT\n 用户名: admin\n 初始密码: dataease" 2>&1 | tee -a ${CURRENT_DIR}/install.log

View File

@ -78,13 +78,14 @@ public class HttpClientUtil {
* @return 响应结果字符串
*/
public static String get(String url, HttpClientConfig config) {
CloseableHttpClient httpClient = buildHttpClient(url);
HttpGet httpGet = new HttpGet(url);
if (config == null) {
config = new HttpClientConfig();
}
CloseableHttpClient httpClient = null;
try {
httpClient = buildHttpClient(url);
HttpGet httpGet = new HttpGet(url);
if (config == null) {
config = new HttpClientConfig();
}
httpGet.setConfig(config.buildRequestConfig());
Map<String, String> header = config.getHeader();
@ -98,7 +99,9 @@ public class HttpClientUtil {
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());
} finally {
try {
httpClient.close();
if(httpClient != null){
httpClient.close();
}
} catch (Exception e) {
logger.error("HttpClient关闭连接失败", e);
}
@ -143,14 +146,14 @@ public class HttpClientUtil {
* @return 响应结果字符串
*/
public static String post(String url, String json, HttpClientConfig config) {
CloseableHttpClient httpClient = buildHttpClient(url);
HttpPost httpPost = new HttpPost(url);
if (config == null) {
config = new HttpClientConfig();
}
CloseableHttpClient httpClient = null;
try {
buildHttpClient(url);
HttpPost httpPost = new HttpPost(url);
if (config == null) {
config = new HttpClientConfig();
}
httpPost.setConfig(config.buildRequestConfig());
Map<String, String> header = config.getHeader();
for (String key : header.keySet()) {
httpPost.addHeader(key, header.get(key));
@ -168,7 +171,9 @@ public class HttpClientUtil {
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());
} finally {
try {
httpClient.close();
if(httpClient != null){
httpClient.close();
}
} catch (Exception e) {
logger.error("HttpClient关闭连接失败", e);
}
@ -195,14 +200,15 @@ public class HttpClientUtil {
* @return 响应结果字符串
*/
public static String post(String url, Map<String, String> body, HttpClientConfig config) {
CloseableHttpClient httpClient = buildHttpClient(url);
HttpPost httpPost = new HttpPost(url);
if (config == null) {
config = new HttpClientConfig();
}
try {
httpPost.setConfig(config.buildRequestConfig());
CloseableHttpClient httpClient = null;
try {
buildHttpClient(url);
HttpPost httpPost = new HttpPost(url);
if (config == null) {
config = new HttpClientConfig();
}
httpPost.setConfig(config.buildRequestConfig());
Map<String, String> header = config.getHeader();
for (String key : header.keySet()) {
httpPost.addHeader(key, header.get(key));
@ -227,7 +233,9 @@ public class HttpClientUtil {
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());
} finally {
try {
httpClient.close();
if(httpClient != null){
httpClient.close();
}
} catch (Exception e) {
logger.error("HttpClient关闭连接失败", e);
}

View File

@ -1,11 +1,14 @@
package io.dataease.utils;
import org.springframework.core.env.Environment;
public class VersionUtil {
private static final String randomId = IDUtils.randomID(16);
public static String getRandomVersion() {
return randomId;
Environment environment = CommonBeanFactory.getBean(Environment.class);
assert environment != null;
return environment.getProperty("dataease.version", "2.0.0");
}
}