Merge branch 'dev-v2' into pr@dev-v2_dzz

This commit is contained in:
dataeaseShu 2023-11-07 15:46:02 +08:00
commit a888a09cd0
60 changed files with 2086 additions and 203 deletions

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

@ -14,18 +14,18 @@ public class MybatisPlusGenerator {
* 第一 我嫌麻烦
* 第二 后面配置会放到nacos读起来更麻烦了
*/
private static final String url = "jdbc:mysql://127.0.0.1:3306/de_standalone?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false";
private static final String url = "jdbc:mysql://39.98.78.97:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false";
private static final String username = "root";
private static final String password = "Password123@mysql";
/**
* 业务模块例如datasource,dataset,panel等
*/
private static final String busi = "system";
private static final String busi = "template";
/**
* 这是要生成代码的表名称
*/
private static final String TABLE_NAME = "core_sys_setting";
private static final String TABLE_NAME = "visualization_template";
/**
* 下面两个配置基本上不用动

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

@ -53,8 +53,8 @@ public class CalciteProvider {
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

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) {
@ -311,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");
}
}
@ -540,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;
}
@ -622,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;
}
@ -882,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

@ -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

@ -1,6 +1,6 @@
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/de_standalone?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://39.98.78.97:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: Password123@mysql
messages:

View File

@ -0,0 +1,42 @@
import request from '@/config/axios'
export function save(data) {
return request.post({
url: '/template/save',
data: data,
loading: true
})
}
export function templateDelete(id) {
return request.post({
url: '/template/delete/' + id
})
}
export function showTemplateList(data) {
return request.post({
url: '/template/templateList',
data: data
})
}
export function findOne(id) {
return request.get({
url: '/template/findOne/' + id
})
}
export function find(data) {
return request.post({
url: '/template/find',
data: data,
loading: true
})
}
export function nameCheck(data) {
return request.post({
url: '/template/nameCheck',
data: data
})
}

View File

@ -0,0 +1,14 @@
import request from '@/config/axios'
export function searchMarket(data) {
return request.post({
url: '/template/market/search',
data
})
}
export function getCategories() {
return request.post({
url: '/template/market/categories'
})
}

View File

@ -287,7 +287,7 @@ const showEditPosition = computed(() => {
if (showPosition.value === 'canvas') {
const baseLeft = element.value.x - 1
const baseRight = pcMatrixCount.value.x - (element.value.x + element.value.sizeX - 1)
if (baseLeft === 0 && baseRight === 0) {
if ((baseLeft === 0 && baseRight === 0) || baseRight < 0) {
return 'bar-main-right-inner'
} else if (baseRight === 0) {
return 'bar-main-left-outer'

View File

@ -264,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: '请从左侧选择',
@ -1430,8 +1430,9 @@ export default {
auth_num: '授权数量',
version: '版本',
version_num: '版本号',
standard: '标准版',
standard: '社区版',
enterprise: '企业版',
Embedded: '嵌入式版',
support: '获取技术支持',
update_success: '更新成功',
serial_no: '序列号',
@ -1629,7 +1630,7 @@ export default {
weeks_4: '周四',
weeks_5: '周五',
weeks_6: '周六',
system_parameter_setting: '系统参数设置',
system_parameter_setting: '系统参数',
connection_successful: '连接成功',
connection_failed: '连接失败',
save_failed: '保存失败',

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

@ -175,14 +175,16 @@ const addItemBox = component => {
}
const moveOutFromTab = component => {
component.canvasId = canvasId.value
dvMainStore.addComponent({
component,
index: undefined,
isFromGroup: true,
componentData: componentData.value
})
addItemBox(component)
setTimeout(() => {
component.canvasId = canvasId.value
dvMainStore.addComponent({
component,
index: undefined,
isFromGroup: true,
componentData: componentData.value
})
addItemBox(component)
}, 500)
}
//

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

@ -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 = {
@ -136,6 +136,18 @@ const AGGREGATION_TYPE = [
{ name: t('chart.count'), value: 'count' },
{ name: t('chart.count_distinct'), value: 'count_distinct' }
]
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],
() => {
@ -188,10 +200,8 @@ const init = () => {
formatterSelector.value?.blur()
//
const formatter = state.tooltipForm.seriesTooltipFormatter
if (!quotaAxis.value?.length) {
if (!formatter.length) {
quotaData.value?.forEach(i => formatter.push({ ...i, seriesId: i.id, show: false }))
}
if (!formatter.length) {
quotaData.value?.forEach(i => formatter.push({ ...i, seriesId: i.id, show: false }))
curSeriesFormatter.value = {}
return
}
@ -209,7 +219,13 @@ 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 (
@ -575,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

@ -49,6 +49,7 @@ 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 { set, concat, keys } from 'lodash-es'
import { Field, getFieldByDQ } from '@/api/chart'
const snapshotStore = snapshotStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
@ -146,6 +147,7 @@ const state = reactive({
watch(
[() => view.value['tableId']],
() => {
getFields(props.view.tableId, props.view.id)
const nodeId = view.value['tableId']
if (!!nodeId) {
cacheId = nodeId as unknown as string
@ -157,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 => {

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

@ -1,11 +1,11 @@
<template>
<div class="info-card">
<div class="info-title">{{ t('visualization.create_by') }}</div>
<div class="info-content">{{ dvInfo.creatorName || 'N/A' }}</div>
<div v-if="dvInfo.creatorName" class="info-title">{{ t('visualization.create_by') }}</div>
<div v-if="dvInfo.creatorName" class="info-content">{{ dvInfo.creatorName }}</div>
<div class="info-title">{{ t('visualization.create_time') }}</div>
<div class="info-content">{{ timestampFormatDate(dvInfo.createTime) }}</div>
<div class="info-title">{{ t('visualization.update_by') }}</div>
<div class="info-content">{{ dvInfo.updateName || 'N/A' }}</div>
<div v-if="dvInfo.updateName" class="info-title">{{ t('visualization.update_by') }}</div>
<div v-if="dvInfo.updateName" class="info-content">{{ dvInfo.updateName }}</div>
<div class="info-title">{{ t('visualization.update_time') }}</div>
<div v-if="dvInfo.updateTime" class="info-content">
{{ timestampFormatDate(dvInfo.updateTime) }}

View File

@ -93,6 +93,7 @@ watch(
jsname="L2NvbXBvbmVudC9zaGFyZS9TaGFyZVZpc3VhbEhlYWQ="
:resource-id="dvInfo.id"
:weight="dvInfo.weight"
:resource-type="dvInfo.type"
/>
<el-button v-if="dvInfo.weight > 6" type="primary" @click="dvEdit()">
<template #icon>

View File

@ -0,0 +1,230 @@
<template>
<div class="info-template-container">
<div class="info-template-header">
<div class="info-template-title">
<span>基础设置</span>
</div>
<div>
<el-button type="primary" @click="edit">{{ t('commons.edit') }}</el-button>
</div>
</div>
<div class="info-template-content">
<div class="info-content-item" v-for="item in settingList" :key="item.pkey">
<div class="info-item-label">
<span>{{ item.pkey }}</span>
<el-tooltip
v-if="tooltipItem[item.pkey]"
effect="dark"
:content="tooltipItem[item.pkey]"
placement="top"
>
<el-icon class="info-tips"><Icon name="dv-info"></Icon></el-icon>
</el-tooltip>
</div>
<div class="info-item-content">
<div class="info-item-pwd" v-if="item.type === 'pwd'">
<span>{{ pwdItem[item.pkey]['hidden'] ? '********' : item.pval }}</span>
<el-tooltip effect="dark" content="新页面预览" placement="top">
<el-icon
class="hover-icon hover-icon-in-table switch-pwd-icon"
@click="switchPwd(item.pkey)"
>
<Icon :name="pwdItem[item.pkey]['hidden'] ? 'eye' : 'eye-open'"></Icon>
</el-icon>
</el-tooltip>
</div>
<span v-else>{{ item.pval }}</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, defineProps, PropType } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { SettingRecord, ToolTipRecord } from './SettingTemplate'
const { t } = useI18n()
const props = defineProps({
settingKey: {
type: String,
default: 'basic'
},
labelTooltips: {
type: Array as PropType<ToolTipRecord[]>,
default: () => []
}
})
const settingList = ref([] as SettingRecord[])
const loadBasic = () => {
settingList.value.push({
pkey: '请求超时时间',
pval: '100',
type: 'text',
sort: 1
})
settingList.value.push({
pkey: '数据源检测时间间隔',
pval: '100',
type: 'text',
sort: 2
})
settingList.value.push({
pkey: '默认登录方式',
pval: '普通登录',
type: 'text',
sort: 3
})
settingList.value.push({
pkey: '默认密码',
pval: 'DataEase@123456',
type: 'pwd',
sort: 4
})
}
const loadEmail = () => {
settingList.value.push({
pkey: 'SMTP主机',
pval: 'smtp.exmail.qq.com',
type: 'text',
sort: 1
})
settingList.value.push({
pkey: 'SMTP端口',
pval: '465',
type: 'text',
sort: 2
})
settingList.value.push({
pkey: 'SMTP账户',
pval: 'test@fit2cloud.com',
type: 'text',
sort: 3
})
settingList.value.push({
pkey: 'SMTP密码',
pval: 'DataEase@123456',
type: 'pwd',
sort: 4
})
settingList.value.push({
pkey: '测试收件人',
pval: 'yawen.chen@fit2cloud.com',
type: 'pwd',
sort: 5
})
settingList.value.push({
pkey: 'SSL',
pval: '开启',
type: 'text',
sort: 6
})
settingList.value.push({
pkey: 'TSL',
pval: '未开启',
type: 'text',
sort: 7
})
}
const init = () => {
if (props.settingKey === 'basic') {
loadBasic()
}
if (props.settingKey === 'email') {
loadEmail()
}
}
const pwdItem = ref({})
const formatPwd = () => {
settingList.value.forEach(setting => {
if (setting.type === 'pwd') {
pwdItem.value[setting.pkey] = { hidden: true }
}
})
}
const tooltipItem = ref({})
const formatLabel = () => {
props.labelTooltips?.length &&
props.labelTooltips.forEach(tooltip => {
tooltipItem.value[tooltip.key] = tooltip.val
})
}
const switchPwd = (pkey: string) => {
pwdItem.value[pkey]['hidden'] = !pwdItem.value[pkey]['hidden']
}
const emits = defineEmits(['edit'])
const edit = () => {
emits('edit')
}
init()
formatPwd()
formatLabel()
</script>
<style lang="less" scope>
.info-template-container {
padding: 24px;
.info-template-header {
display: flex;
justify-content: space-between;
.info-template-title {
height: 24px;
line-height: 23px;
font-size: 16px;
font-weight: 500;
color: #1f2329;
}
}
.info-template-content {
width: 100%;
margin: 8px 0;
.info-content-item {
width: 50%;
height: 48px;
float: left;
margin-bottom: 16px;
.info-item-label {
height: 22px;
line-height: 22px;
display: flex;
align-items: center;
span {
font-size: 14px;
color: #646a73;
font-weight: 400;
}
i {
margin-left: 2px;
}
}
.info-item-content {
line-height: 22px;
height: 22px;
span {
font-size: 14px;
color: #1f2329;
font-weight: 400;
}
.info-item-pwd {
height: 22px;
line-height: 22px;
display: flex;
align-items: center;
i {
margin-left: 2px;
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,11 @@
export interface SettingRecord {
pkey: string
pval: string
type: string
sort: number
}
export interface ToolTipRecord {
key: string
val: string
}

View File

@ -0,0 +1,171 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref(null)
const createUserForm = ref<FormInstance>()
const state = reactive({
roleList: [],
form: reactive<UserForm>({
id: null,
account: null,
name: null,
email: null,
enable: true,
phone: null,
phonePrefix: '+86',
roleIds: []
})
})
const rule = reactive<FormRules>({})
const edit = () => {
dialogVisible.value = true
// queryForm()
}
/* const queryForm = () => {
showLoading()
personInfoApi().then(res => {
state.form = reactive<UserForm>(res.data)
closeLoading()
})
} */
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
const param = { ...state.form }
const method = null
showLoading()
method(param)
.then(res => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
closeLoading()
})
.catch(() => {
closeLoading()
})
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
}
const reset = () => {
resetForm(createUserForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({ target: '.basic-info-drawer' })
}
const closeLoading = () => {
loadingInstance.value?.close()
}
defineExpose({
edit
})
</script>
<template>
<el-drawer
title="基础设置"
v-model="dialogVisible"
custom-class="basic-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="createUserForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="请求超时时间" prop="name">
<el-input
v-model="state.form.name"
:placeholder="t('common.please_input') + t('user.name')"
/>
</el-form-item>
<el-form-item label="数据源检测时间间隔" prop="account">
<el-input
v-model="state.form.account"
:placeholder="t('common.please_input') + t('common.account')"
disabled
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="resetForm(createUserForm)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submitForm(createUserForm)">
{{ t('commons.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.basic-info-drawer {
.editer-form-title {
width: 100%;
border-radius: 4px;
background: #e1eaff;
margin: -8px 0 16px 0;
height: 40px;
padding-left: 16px;
i {
color: #3370ff;
font-size: 14.666666030883789px;
}
.pwd {
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 22px;
text-align: left;
}
.pwd {
margin: 0 8px;
color: #1f2329;
}
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: PingFang SC;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<InfoTemplate :label-tooltips="tooltips" setting-key="basic" @edit="edit" />
<basic-edit ref="editor" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import InfoTemplate from '../../common/InfoTemplate.vue'
import BasicEdit from './BasicEdit.vue'
const editor = ref()
const tooltips = [
{
key: '请求超时时间',
val: '请求超时时间(单位:秒,注意:保存后刷新浏览器生效)'
}
]
const edit = () => {
editor?.value.edit()
}
</script>

View File

@ -0,0 +1,171 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref(null)
const createUserForm = ref<FormInstance>()
const state = reactive({
roleList: [],
form: reactive<UserForm>({
id: null,
account: null,
name: null,
email: null,
enable: true,
phone: null,
phonePrefix: '+86',
roleIds: []
})
})
const rule = reactive<FormRules>({})
const edit = () => {
dialogVisible.value = true
// queryForm()
}
/* const queryForm = () => {
showLoading()
personInfoApi().then(res => {
state.form = reactive<UserForm>(res.data)
closeLoading()
})
} */
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
const param = { ...state.form }
const method = null
showLoading()
method(param)
.then(res => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
closeLoading()
})
.catch(() => {
closeLoading()
})
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
}
const reset = () => {
resetForm(createUserForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({ target: '.basic-info-drawer' })
}
const closeLoading = () => {
loadingInstance.value?.close()
}
defineExpose({
edit
})
</script>
<template>
<el-drawer
title="邮件设置"
v-model="dialogVisible"
custom-class="basic-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="createUserForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="请求超时时间" prop="name">
<el-input
v-model="state.form.name"
:placeholder="t('common.please_input') + t('user.name')"
/>
</el-form-item>
<el-form-item label="数据源检测时间间隔" prop="account">
<el-input
v-model="state.form.account"
:placeholder="t('common.please_input') + t('common.account')"
disabled
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="resetForm(createUserForm)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submitForm(createUserForm)">
{{ t('commons.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.basic-info-drawer {
.editer-form-title {
width: 100%;
border-radius: 4px;
background: #e1eaff;
margin: -8px 0 16px 0;
height: 40px;
padding-left: 16px;
i {
color: #3370ff;
font-size: 14.666666030883789px;
}
.pwd {
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 22px;
text-align: left;
}
.pwd {
margin: 0 8px;
color: #1f2329;
}
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: PingFang SC;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<InfoTemplate :label-tooltips="tooltips" setting-key="email" @edit="edit" />
<email-edit ref="editor" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import InfoTemplate from '../../common/InfoTemplate.vue'
import EmailEdit from './EmailEdit.vue'
const tooltips = [
{
key: '测试收件人',
val: '测试收件人xxxx'
},
{
key: 'SSL',
val: 'SSLxxxx'
},
{
key: 'TSL',
val: 'TSLxxxx'
}
]
const editor = ref()
const edit = () => {
editor?.value.edit()
}
</script>

View File

@ -1,11 +1,13 @@
<template>
<p class="router-title">系统参数</p>
<p class="router-title">{{ t('commons.system_parameter_setting') }}</p>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane v-for="item in tabArray" :key="item.name" :label="item.label" :name="item.name" />
</el-tabs>
<div class="sys-setting-p">
<div class="sys-setting-p" :class="{ 'setting-auto-h': activeName !== 'map' }">
<div class="container-sys-param">
<map-setting v-if="activeName === 'map'" />
<basic-info v-if="activeName === 'basic'" />
<email-info v-if="activeName === 'email'" />
</div>
</div>
</template>
@ -14,15 +16,17 @@
import { ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import MapSetting from './map/MapSetting.vue'
import BasicInfo from './basic/BasicInfo.vue'
import EmailInfo from './email/EmailInfo.vue'
const { t } = useI18n()
const tabArray = [
/* {label: '', name: 'basic'},
{label: '邮件设置', name: 'email'}, */
{ label: '基础设置', name: 'basic' },
{ label: '邮件设置', name: 'email' },
{ label: '地图设置', name: 'map' }
/* {label: '引擎设置', name: 'engine'}, */
]
const activeName = ref('map')
const activeName = ref('basic')
const handleClick = (tab, event: Event) => {
console.log(tab, event)
}
@ -44,6 +48,9 @@ const handleClick = (tab, event: Event) => {
box-sizing: border-box;
margin-top: 12px;
}
.setting-auto-h {
height: auto !important;
}
.container-sys-param {
height: 100%;

View File

@ -0,0 +1,209 @@
<template>
<div class="template-import">
<el-form
ref="templateImportForm"
class="de-form-item"
:model="state.templateInfo"
:rules="state.templateInfoRules"
>
<el-form-item :label="t('system_parameter_setting.template_name')" prop="name">
<div class="flex-template">
<el-input v-model="state.templateInfo.name" clearable size="small" />
<el-button style="margin-left: 10px" class="el-icon-upload2" secondary @click="goFile">{{
t('panel.upload_template')
}}</el-button>
<input
id="input"
ref="filesRef"
type="file"
accept=".DET"
hidden
@change="handleFileChange"
/>
</div>
</el-form-item>
</el-form>
<el-row class="preview" :style="classBackground" />
<el-row class="de-root-class">
<deBtn secondary @click="cancel()">{{ t('commons.cancel') }}</deBtn>
<deBtn type="primary" @click="saveTemplate()">{{ t('commons.confirm') }}</deBtn>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { save, nameCheck, find } from '@/api/template'
import { computed, onMounted, reactive, ref } from 'vue'
import { imgUrlTrans } from '@/utils/imgUtils'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
const emits = defineEmits(['closeEditTemplateDialog', 'refresh'])
const { t } = useI18n()
const filesRef = ref(null)
const props = defineProps({
pid: {
type: String,
required: true
}
})
const state = reactive({
nameList: [],
importTemplateInfo: {
snapshot: ''
},
templateInfoRules: {
name: [
{
required: true,
message: t('commons.input_content'),
trigger: 'change'
}
]
},
recover: false,
templateInfo: {
level: '1',
pid: props.pid,
name: '',
templateStyle: null,
templateData: null,
dynamicData: null,
staticResource: null,
snapshot: ''
}
})
const classBackground = computed(() => {
if (state.importTemplateInfo.snapshot) {
return {
background: `url(${imgUrlTrans(state.importTemplateInfo.snapshot)}) no-repeat`
}
} else {
return {}
}
})
const showCurrentTemplate = pid => {
find({ pid }).then(response => {
state.nameList = response.data
})
}
const cancel = () => {
emits('closeEditTemplateDialog')
}
const saveTemplate = () => {
if (!state.templateInfo.name) {
ElMessage.warning(t('chart.name_can_not_empty'))
return false
}
if (!state.templateInfo.templateData) {
ElMessage.warning(t('chart.template_can_not_empty'))
return false
}
const nameCheckRequest = {
pid: state.templateInfo.pid,
name: state.templateInfo.name,
optType: 'insert'
}
nameCheck(nameCheckRequest).then(response => {
if (response.data.indexOf('exist') > -1) {
const options = {
title: 'commons.prompt',
content: 'system_parameter_setting.to_overwrite_them',
type: 'primary',
cb: () =>
save(state.templateInfo).then(response => {
ElMessage.success(t('system_parameter_setting.import_succeeded'))
emits('refresh')
emits('closeEditTemplateDialog')
}),
confirmButtonText: t('template.override')
}
handlerConfirm(options)
} else {
save(state.templateInfo).then(response => {
ElMessage.success(t('system_parameter_setting.import_succeeded'))
emits('refresh')
emits('closeEditTemplateDialog')
})
}
})
}
const handlerConfirm = option => {
// do handlerConfirm
}
const handleFileChange = e => {
const file = e.target.files[0]
const reader = new FileReader()
reader.onload = res => {
const result = res.target.result as string
state.importTemplateInfo = JSON.parse(result)
state.templateInfo.name = state.importTemplateInfo['name']
state.templateInfo.templateStyle = state.importTemplateInfo['panelStyle']
state.templateInfo.templateData = state.importTemplateInfo['panelData']
state.templateInfo.snapshot = state.importTemplateInfo.snapshot
state.templateInfo.dynamicData = state.importTemplateInfo['dynamicData']
state.templateInfo.staticResource = state.importTemplateInfo['staticResource']
state.templateInfo['nodeType'] = 'template'
}
reader.readAsText(file)
}
const goFile = () => {
filesRef.value.click()
}
onMounted(() => {
showCurrentTemplate(props.pid)
})
</script>
<style scoped>
.my_table :deep(.el-table__row > td) {
/* 去除表格线 */
border: none;
padding: 0 0;
}
.my_table :deep(.el-table th.is-leaf) {
/* 去除上边框 */
border: none;
}
.my_table :deep(.el-table::before) {
/* 去除下边框 */
height: 0;
}
.de-root-class {
margin-top: 24px;
text-align: right;
}
.preview {
margin-top: -12px;
border: 1px solid #e6e6e6;
height: 300px !important;
overflow: auto;
background-size: 100% 100% !important;
border-radius: 4px;
}
.preview-show {
border-left: 1px solid #e6e6e6;
height: 300px;
background-size: 100% 100% !important;
}
</style>
<style lang="scss">
.flex-template {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.el-input {
margin-right: 2px;
flex: 1;
}
}
</style>

View File

@ -0,0 +1,140 @@
<template>
<div :style="classBackground" class="de-card-model">
<div class="card-img-model" :style="classImg">
<img :src="model.snapshot" alt="" />
</div>
<div class="card-info">
<el-tooltip class="item" effect="dark" :content="model.name" placement="top">
<span class="de-model-text">{{ model.name }}</span>
</el-tooltip>
<el-dropdown size="medium" trigger="click" @command="handleCommand">
<i class="el-icon-more" />
<template #dropdown>
<el-dropdown-menu class="de-card-dropdown">
<slot>
<el-dropdown-item command="rename">
<i class="el-icon-edit" />
{{ $t('chart.rename') }}
</el-dropdown-item>
<el-dropdown-item command="delete">
<i class="el-icon-delete" />
{{ $t('chart.delete') }}
</el-dropdown-item>
</slot>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
const emits = defineEmits(['command'])
const props = defineProps({
model: {
type: Object
},
width: {
type: Number
}
})
const classBackground = computed(() => {
return {
width: props.width + 'px',
height: props.width * 0.714 + 'px'
}
})
const classImg = computed(() => {
return {
width: props.width + 'px',
height: props.width * 0.576 + 'px'
}
})
const handleCommand = key => {
emits('command', key)
}
</script>
<style lang="scss">
.de-card-model {
box-sizing: border-box;
background: #ffffff;
border: 1px solid var(--deCardStrokeColor, #dee0e3);
border-radius: 4px;
margin: 0 24px 25px 0;
.card-img-model {
border-bottom: 1px solid var(--deCardStrokeColor, #dee0e3);
height: 144px;
width: 100%;
overflow: hidden;
img {
width: 100%;
height: 100%;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
.card-info {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px 9px 12px;
box-sizing: border-box;
.el-icon-more {
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
font-size: 12px;
color: #646a73;
cursor: pointer;
}
.el-icon-more:hover {
background: rgba(31, 35, 41, 0.1);
border-radius: 4px;
}
.el-icon-more:active {
background: rgba(31, 35, 41, 0.2);
border-radius: 4px;
}
}
.de-model-text {
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #1f2329;
display: inline-block;
width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-right: 10px;
}
}
.de-card-model:hover {
box-shadow: 0px 6px 24px rgba(31, 35, 41, 0.08);
}
.de-card-dropdown {
margin-top: 0 !important;
.popper__arrow {
display: none !important;
}
}
</style>

View File

@ -0,0 +1,247 @@
<template xmlns:el-col="http://www.w3.org/1999/html">
<div class="de-template-list">
<el-input
v-model="state.templateFilterText"
:placeholder="t('system_parameter_setting.search_keywords')"
size="small"
class="de-input-search"
clearable
>
<template #prefix>
<svg-icon icon-class="de-search" />
</template>
</el-input>
<el-empty
v-if="!templateListComputed.length && state.templateFilterText === ''"
:image="state.noneImg"
:description="t('components.no_classification')"
/>
<el-empty
v-if="!templateListComputed.length && state.templateFilterText !== ''"
:image="state.nothingImg"
:description="t('components.relevant_content_found')"
/>
<ul>
<li
v-for="ele in templateListComputed"
:key="ele.name"
:class="[{ select: state.activeTemplate === ele.id }]"
@click="nodeClick(ele)"
>
<svg-icon icon-class="scene" class="de-icon-sense" />
<span class="text-template-overflow" :title="ele.name">{{ ele.name }}</span>
<span class="more" @click.stop>
<el-dropdown trigger="click" size="small" @command="type => clickMore(type, ele)">
<span class="el-dropdown-link">
<i class="el-icon-more" />
</span>
<template #dropdown>
<el-dropdown-menu class="de-template-dropdown">
<el-dropdown-item icon="el-icon-upload2" command="import">
{{ t('panel.import') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="edit">
{{ t('panel.rename') }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-delete" command="delete">
{{ t('panel.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
</li>
</ul>
<el-button
v-if="state.templateFilterText === ''"
style="width: 100%"
icon="el-icon-plus"
secondary
@click="add()"
>
{{ t('panel.add_category') }}
</el-button>
</div>
</template>
<script setup lang="ts">
import { useI18n } from '@/hooks/web/useI18n'
import { computed, reactive } from 'vue'
const { t } = useI18n()
const emits = defineEmits([
'showCurrentTemplate',
'showTemplateEditDialog',
'templateDelete',
'templateEdit',
'templateImport'
])
const props = defineProps({
templateType: {
type: String,
default: ''
},
templateList: {
type: Array,
default: function () {
return []
}
}
})
const state = reactive({
templateFilterText: '',
activeTemplate: '',
noneImg: '@/assets/None.png',
nothingImg: '@/assets/nothing.png'
})
const templateListComputed = computed(() => {
if (!state.templateFilterText) return [...props.templateList]
return props.templateList.filter(ele => ele['name']?.includes(state.templateFilterText))
})
const clickMore = (type, data) => {
switch (type) {
case 'edit':
templateEdit(data)
break
case 'delete':
templateDelete(data)
break
case 'import':
templateImport(data)
break
}
}
const nodeClick = ({ id, label }) => {
state.activeTemplate = id
emits('showCurrentTemplate', id, label)
}
const add = () => {
emits('showTemplateEditDialog', 'new')
}
const templateDelete = template => {
const options = {
title: 'system_parameter_setting.delete_this_category',
content: 'system_parameter_setting.also_be_deleted',
type: 'primary',
cb: () => emits('templateDelete', template.id)
}
handlerConfirm(options)
}
const templateEdit = template => {
emits('templateEdit', template)
}
const templateImport = template => {
emits('templateImport', template.id)
}
const handlerConfirm = options => {
// do handlerConfirm
}
</script>
<style scoped lang="scss">
.de-template-list {
height: 100%;
position: relative;
ul {
margin: 16px 0 20px 0;
padding: 0;
overflow-y: auto;
max-height: calc(100% - 90px);
}
li {
list-style: none;
width: 100%;
box-sizing: border-box;
height: 40px;
padding: 0 30px 0 12px;
display: flex;
align-items: center;
border-radius: 4px;
color: var(--deTextPrimary, #1f2329);
font-family: 'PingFang SC';
font-style: normal;
font-weight: 500;
font-size: 14px;
cursor: pointer;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.text-template-overflow {
display: inline-block;
max-width: 87%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.folder {
color: #8f959e;
margin-right: 9px;
}
.more {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
display: none;
.el-icon-more {
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
font-size: 12px;
color: #646a73;
cursor: pointer;
}
.el-icon-more:hover {
background: rgba(31, 35, 41, 0.1);
border-radius: 4px;
}
.el-icon-more:active {
background: rgba(31, 35, 41, 0.2);
border-radius: 4px;
}
}
&:hover {
background: rgba(31, 35, 41, 0.1);
.more {
display: block;
}
}
}
li.select {
background: var(--deWhiteHover, #e0eaff) !important;
color: var(--TextActive, #3370ff) !important;
}
.de-btn-fix {
position: absolute;
bottom: 0;
left: 0;
}
}
.de-template-dropdown {
margin-top: 0 !important;
.popper__arrow {
display: none !important;
}
}
</style>

View File

@ -1,12 +1,412 @@
<script lang="ts" setup>
import { ref } from 'vue'
const wizard = ref('wizard')
</script>
<template>
<div>
{{ wizard }}
<div class="de-template">
<el-tabs v-model="state.currentTemplateType" class="de-tabs" @tab-click="handleClick">
<el-tab-pane name="self">
<template #label>
<span>{{ t('panel.user_template') }}</span>
</template>
</el-tab-pane>
<el-tab-pane name="system">
<template #label>
<span>{{ t('panel.sys_template') }}</span>
</template>
</el-tab-pane>
</el-tabs>
<div class="tabs-container flex-tabs">
<div class="de-tabs-left">
<template-list
ref="templateListRef"
:template-type="state.currentTemplateType"
:template-list="state.templateList"
@templateDelete="templateFolderDelete"
@templateEdit="templateEdit"
@showCurrentTemplate="showCurrentTemplate"
@templateImport="templateImport"
@showTemplateEditDialog="showTemplateEditDialog"
/>
</div>
<div class="de-tabs-right">
<div v-if="state.currentTemplateLabel" class="active-template">
{{ state.currentTemplateLabel }}&nbsp;&nbsp;({{ state.currentTemplateShowList.length }})
<el-button
type="primary"
icon="el-icon-upload2"
@click="templateImport(state.currentTemplateId)"
>
{{ t('panel.import') }}
</el-button>
</div>
<el-empty
v-if="!state.currentTemplateShowList.length"
:image="state.noneImg"
:description="t('components.no_template')"
/>
<div v-show="state.currentTemplateId !== ''" id="template-box" class="template-box">
<template-item
v-for="item in state.currentTemplateShowList"
:key="item.id"
:width="state.templateCurWidth"
:model="item"
@command="key => handleCommand(key, item)"
/>
</div>
</div>
</div>
</div>
<el-dialog
:title="state.dialogTitle"
v-model:visible="state.editTemplate"
append-to-body
class="de-dialog-form"
width="600px"
>
<el-form
ref="templateEditFormRef"
class="de-form-item"
:model="state.templateEditForm"
:rules="state.templateEditFormRules"
>
<el-form-item :label="state.dialogTitleLabel" prop="name">
<el-input v-model="state.templateEditForm.name" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button secondary @click="close()">{{ t('commons.cancel') }}</el-button>
<el-button type="primary" @click="saveTemplateEdit(state.templateEditForm)"
>{{ t('commons.confirm') }}
</el-button>
</div>
</template>
</el-dialog>
<!--导入templatedialog-->
<el-dialog
:title="state.templateDialog.title"
v-model:visible="state.templateDialog.visible"
:show-close="true"
class="de-dialog-form"
width="600px"
>
<template-import
v-if="state.templateDialog.visible"
:pid="state.templateDialog.pid"
@refresh="showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)"
@closeEditTemplateDialog="closeEditTemplateDialog"
/>
</el-dialog>
</div>
</template>
<style lang="less" scoped></style>
<script lang="ts" setup>
import { save, templateDelete, find } from '@/api/template'
import elementResizeDetectorMaker from 'element-resize-detector'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { ElMessage } from 'element-plus-secondary'
import TemplateList from '@/views/template/component/TemplateList.vue'
const { t } = useI18n()
const templateEditFormRef = ref(null)
const templateListRef = ref(null)
const roleValidator = (rule, value, callback) => {
if (nameRepeat(value)) {
const { nodeType } = state.templateEditForm || {}
callback(
new Error(
t(
`system_parameter_setting.${
nodeType === 'folder' ? 'name_already_exists_type' : 'the_same_category'
}`
)
)
)
} else {
callback()
}
}
const state = reactive({
showShare: false,
currentTemplateShowList: [],
noneImg: '@/assets/None.png',
currentPid: '',
currentTemplateType: 'self',
templateEditFormRules: {
name: [
{ required: true, trigger: 'blur', validator: roleValidator },
{
required: true,
message: t('commons.input_content'),
trigger: 'blur'
},
{
max: 50,
message: t('commons.char_can_not_more_50'),
trigger: 'change'
}
]
},
templateEditForm: {},
editTemplate: false,
dialogTitle: '',
dialogTitleLabel: '',
currentTemplateLabel: '',
currentTemplateId: '',
templateList: [],
templateMiniWidth: 286,
templateCurWidth: 286,
formType: '',
originName: '',
templateDialog: {
title: t('panel.import_template'),
visible: false,
pid: ''
}
})
const nameList = computed(() => {
const { nodeType } = state.templateEditForm || {}
if (nodeType === 'template') {
return state.currentTemplateShowList.map(ele => ele.label)
}
if (nodeType === 'folder') {
return state.templateList.map(ele => ele.label)
}
return []
})
const nameRepeat = value => {
if (!nameList.value) {
return false
}
//
if (state.formType === 'edit' && state.originName === value) {
return false
}
return nameList.value.some(name => name === value)
}
const handleCommand = (key, data) => {
switch (key) {
case 'rename':
templateEdit(data)
break
case 'delete':
templateDeleteConfirm(data)
break
default:
break
}
}
const handlerConfirm = option => {
//do handlerConfirm
}
const templateDeleteConfirm = template => {
const options = {
title: 'system_parameter_setting.delete_this_template',
type: 'primary',
cb: () => templateDelete(template.id)
}
handlerConfirm(options)
}
const handleClick = (tab, event) => {
getTree()
}
const showCurrentTemplate = (pid, label) => {
state.currentTemplateId = pid
state.currentTemplateLabel = label
if (state.currentTemplateId) {
find({ pid: state.currentTemplateId }).then(response => {
state.currentTemplateShowList = response.data
})
}
}
const templateFolderDelete = id => {
if (id) {
templateDelete(id).then(response => {
ElMessage.info(t('commons.delete_success'))
getTree()
})
}
}
const templateDelete = id => {
if (id) {
templateDelete(id).then(response => {
ElMessage.info(t('commons.delete_success'))
showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)
})
}
}
const showTemplateEditDialog = (type, templateInfo) => {
state.templateEditForm = null
state.formType = type
if (type === 'edit') {
state.templateEditForm = JSON.parse(JSON.stringify(templateInfo))
state.dialogTitle = t(
`system_parameter_setting.${
state.templateEditForm['nodeType'] === 'folder' ? 'edit_classification' : 'edit_template'
}`
)
state.originName = state.templateEditForm['label']
} else {
state.dialogTitle = t('panel.add_category')
state.templateEditForm = {
name: '',
nodeType: 'folder',
templateType: state.currentTemplateType,
level: 0
}
}
state.dialogTitleLabel = t(
`system_parameter_setting.${
state.templateEditForm['nodeType'] === 'folder' ? 'classification_name' : 'template_name'
}`
)
state.editTemplate = true
}
const templateEdit = templateInfo => {
showTemplateEditDialog('edit', templateInfo)
}
const saveTemplateEdit = templateEditForm => {
templateEditFormRef.value.validate(valid => {
if (valid) {
save(templateEditForm).then(response => {
close()
// openMessageSuccess(
// `system_parameter_setting.${
// this.templateEditForm.id ? 'rename_succeeded' : 'added_successfully'
// }`
// )
getTree()
})
} else {
return false
}
})
}
const close = () => {
templateEditFormRef.value.resetFields()
state.editTemplate = false
}
const getTree = () => {
const request = {
templateType: state.currentTemplateType,
level: '0'
}
find(request).then(res => {
state.templateList = res.data
showFirst()
})
}
const showFirst = () => {
//
if (state.templateList && state.templateList.length > 0) {
let showFirst = true
state.templateList.forEach(template => {
if (template.id === state.currentTemplateId) {
showFirst = false
}
})
if (showFirst) {
nextTick().then(() => {
const [obj = {}] = state.templateList
templateListRef.value.nodeClick(obj)
})
} else {
showCurrentTemplate(state.currentTemplateId, state.currentTemplateLabel)
}
} else {
state.currentTemplateShowList = []
}
}
const closeEditTemplateDialog = () => {
state.templateDialog.visible = false
}
const templateImport = pid => {
state.templateDialog.visible = true
state.templateDialog.pid = pid
}
onMounted(() => {
getTree()
const erd = elementResizeDetectorMaker()
const templateMainDom = document.getElementById('template-box')
// div
erd.listenTo(templateMainDom, element => {
nextTick(() => {
const curSeparator = Math.trunc(templateMainDom.offsetWidth / state.templateMiniWidth)
state.templateCurWidth =
Math.trunc(templateMainDom.offsetWidth / curSeparator) - 24 - curSeparator
})
})
})
</script>
<style lang="scss" scoped>
.de-template {
height: 100%;
background-color: var(--MainBG, #f5f6f7);
.tabs-container {
height: calc(100% - 48px);
background: var(--ContentBG, #ffffff);
overflow-x: auto;
}
.flex-tabs {
display: flex;
background: #f5f6f7;
}
.de-tabs-left {
background: #fff;
width: 269px;
border-right: 1px solid rgba(31, 35, 41, 0.15);
padding: 24px;
}
.de-tabs-right {
flex: 1;
background: #fff;
padding: 24px 0 24px 24px;
overflow: hidden;
.template-box {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
box-sizing: border-box;
align-content: flex-start;
height: calc(100% - 10px);
width: 100%;
padding-bottom: 24px;
}
.active-template {
margin: 4px 0 20px 0;
padding-right: 24px;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 500;
font-size: 16px;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--deTextPrimary, #1f2329);
}
}
}
</style>

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 c2c4436bac4d300e5297ff1854fd6d45e01435ad
Subproject commit 3850e6d19884be36d24dea6589552d66b5675eea

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

@ -0,0 +1,13 @@
package io.dataease.api.template;
import io.dataease.api.template.dto.TemplateManageDTO;
import org.springframework.web.bind.annotation.*;
import java.util.List;
public interface TemplateManageApi {
@PostMapping("/templateList")
List<TemplateManageDTO> templateList();
}

View File

@ -0,0 +1,23 @@
package io.dataease.api.template;
import io.dataease.api.template.request.TemplateMarketSearchRequest;
import io.dataease.api.template.response.MarketBaseResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* @author : WangJiaHao
* @date : 2023/11/6 17:23
*/
public interface TemplateMarketApi {
@PostMapping("/search")
MarketBaseResponse searchTemplate(@RequestBody TemplateMarketSearchRequest request);
@GetMapping("/categories")
List<String> categories();
}

View File

@ -0,0 +1,15 @@
package io.dataease.api.template.dto;
import lombok.Data;
@Data
public class TemplateManageDTO {
private String label;
private Integer childrenCount;
}

View File

@ -0,0 +1,21 @@
package io.dataease.api.template.dto;
import io.dataease.api.template.vo.MarketCategoryVO;
import io.dataease.api.template.vo.MarketMetasVO;
import lombok.Data;
import java.util.List;
@Data
public class TemplateMarketDTO {
private String id;
private String title;
private String status;
private String slug;
private String editorType;
private String summary;
private String thumbnail;
private Boolean showFlag = true;
private List<MarketCategoryVO> categories;
private MarketMetasVO metas;
}

View File

@ -0,0 +1,7 @@
package io.dataease.api.template.request;
import io.dataease.api.template.dto.TemplateMarketDTO;
public class TemplateMarketSearchRequest extends TemplateMarketDTO {
}

View File

@ -0,0 +1,24 @@
package io.dataease.api.template.response;
import io.dataease.api.template.dto.TemplateMarketDTO;
import java.util.List;
/**
* @author : WangJiaHao
* @date : 2023/11/6 17:43
*/
public class MarketBaseResponse {
private String baseUrl;
private List<TemplateMarketDTO> contents;
public MarketBaseResponse() {
}
public MarketBaseResponse(String baseUrl, List<TemplateMarketDTO> contents) {
this.baseUrl = baseUrl;
this.contents = contents;
}
}

View File

@ -0,0 +1,15 @@
package io.dataease.api.template.vo;
import lombok.Data;
/**
* Author: wangjiahao
* Date: 2022/7/15
* Description:
*/
@Data
public class MarketCategoryVO {
private String id;
private String name;
private String slug;
}

View File

@ -0,0 +1,13 @@
package io.dataease.api.template.vo;
import lombok.Data;
/**
* Author: wangjiahao
* Date: 2022/7/15
* Description:
*/
@Data
public class MarketMetasVO {
private String theme_repo;
}

View File

@ -0,0 +1,19 @@
package io.dataease.api.template.vo;
import lombok.Data;
/**
* Author: wangjiahao
* Date: 2022/7/18
* Description:
*/
@Data
public class TemplateCategoryVO {
private Integer id;
private String name;
private String slug;
private Integer priority;
}

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");
}
}