forked from github/dataease
Merge branch 'dev-v2' into pr@dev-v2@refactor_market
This commit is contained in:
commit
b563b81a56
@ -21,12 +21,16 @@ public class DeMvcConfig implements WebMvcConfigurer {
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
String workDir = FILE_PROTOCOL + ensureSuffix(WORK_DIR, FILE_SEPARATOR);
|
||||
String uploadUrlPattern = ensureBoth(URL_SEPARATOR + UPLOAD_URL_PREFIX, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||
registry.addResourceHandler(uploadUrlPattern)
|
||||
.addResourceLocations(workDir);
|
||||
registry.addResourceHandler(uploadUrlPattern).addResourceLocations(workDir);
|
||||
|
||||
// map
|
||||
String mapDir = FILE_PROTOCOL + ensureSuffix(MAP_DIR, FILE_SEPARATOR);
|
||||
String mapUrlPattern = ensureBoth(MAP_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||
registry.addResourceHandler(mapUrlPattern)
|
||||
.addResourceLocations(mapDir);
|
||||
registry.addResourceHandler(mapUrlPattern).addResourceLocations(mapDir);
|
||||
|
||||
String geoDir = FILE_PROTOCOL + ensureSuffix(CUSTOM_MAP_DIR, FILE_SEPARATOR);
|
||||
String geoUrlPattern = ensureBoth(GEO_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||
registry.addResourceHandler(geoUrlPattern).addResourceLocations(geoDir);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import io.dataease.datasource.request.DatasourceRequest;
|
||||
import io.dataease.datasource.server.EngineServer;
|
||||
import io.dataease.datasource.type.*;
|
||||
import io.dataease.engine.constant.SQLConstants;
|
||||
import io.dataease.engine.func.scalar.ScalarFunctions;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.i18n.Translator;
|
||||
import io.dataease.utils.BeanUtils;
|
||||
@ -26,6 +25,7 @@ import io.dataease.utils.LogUtil;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.calcite.adapter.jdbc.JdbcSchema;
|
||||
import org.apache.calcite.func.scalar.ScalarFunctions;
|
||||
import org.apache.calcite.jdbc.CalciteConnection;
|
||||
import org.apache.calcite.schema.Schema;
|
||||
import org.apache.calcite.schema.SchemaPlus;
|
||||
|
@ -1,194 +0,0 @@
|
||||
package io.dataease.engine.func.scalar;
|
||||
|
||||
import io.dataease.engine.utils.Utils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class ScalarFunctions {
|
||||
public static String format = "yyyy-MM-dd HH:mm:ss";
|
||||
public static String minuteFormat = "yyyy-MM-dd HH:mm";
|
||||
public static String hourFormat = "yyyy-MM-dd HH";
|
||||
public static String dateOnly = "yyyy-MM-dd";
|
||||
public static String monthOnly = "yyyy-MM";
|
||||
public static String yearOnly = "yyyy";
|
||||
public static String timeOnly = "HH:mm:ss";
|
||||
|
||||
public static String date_format(String date, String format) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
format = get_date_format(date);
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
return simpleDateFormat.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String de_date_format(String date, String format) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
return simpleDateFormat.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String str_to_date(String date, String format) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
format = get_date_format(date);
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
return simpleDateFormat.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String de_str_to_date(String date, String format) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
return simpleDateFormat.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String cast_date_format(String date, String sourceFormat, String targetFormat) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
sourceFormat = get_date_format(date);
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(sourceFormat);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
|
||||
SimpleDateFormat s = new SimpleDateFormat(targetFormat);
|
||||
return s.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String de_cast_date_format(String date, String sourceFormat, String targetFormat) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(sourceFormat);
|
||||
Date parse = simpleDateFormat.parse(date);
|
||||
|
||||
SimpleDateFormat s = new SimpleDateFormat(targetFormat);
|
||||
return s.format(parse);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Long unix_timestamp(String date) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
return Utils.allDateFormat2Long(date);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String get_date_format(String date) {
|
||||
// check date split '-' or '/'
|
||||
String format1 = format;
|
||||
String minuteFormat1 = minuteFormat;
|
||||
String hourFormat1 = hourFormat;
|
||||
String timeOnly1 = timeOnly;
|
||||
String dateOnly1 = dateOnly;
|
||||
String monthOnly1 = monthOnly;
|
||||
String yearOnly1 = yearOnly;
|
||||
if (date != null && date.contains("/")) {
|
||||
format1 = format1.replaceAll("-", "/");
|
||||
minuteFormat1 = minuteFormat1.replaceAll("-", "/");
|
||||
hourFormat1 = hourFormat1.replaceAll("-", "/");
|
||||
timeOnly1 = timeOnly1.replaceAll("-", "/");
|
||||
dateOnly1 = dateOnly1.replaceAll("-", "/");
|
||||
monthOnly1 = monthOnly1.replaceAll("-", "/");
|
||||
yearOnly1 = yearOnly1.replaceAll("-", "/");
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format1);
|
||||
simpleDateFormat.parse(date);
|
||||
return format1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(minuteFormat1);
|
||||
simpleDateFormat.parse(date);
|
||||
return minuteFormat1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(hourFormat1);
|
||||
simpleDateFormat.parse(date);
|
||||
return hourFormat1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(timeOnly1);
|
||||
simpleDateFormat.parse(date);
|
||||
return timeOnly1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateOnly1);
|
||||
simpleDateFormat.parse(date);
|
||||
return dateOnly1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(monthOnly1);
|
||||
simpleDateFormat.parse(date);
|
||||
return monthOnly1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(yearOnly1);
|
||||
simpleDateFormat.parse(date);
|
||||
return yearOnly1;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return format1;
|
||||
}
|
||||
|
||||
public static String from_unixtime(Long timestamp, String format) {
|
||||
try {
|
||||
if (ObjectUtils.isEmpty(timestamp)) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
|
||||
Date date = new Date(timestamp);
|
||||
return simpleDateFormat.format(date);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String concat(String str1, String str2) {
|
||||
return str1 + str2;
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ import io.dataease.api.dataset.union.model.SQLMeta;
|
||||
import io.dataease.api.dataset.union.model.SQLObj;
|
||||
import io.dataease.dto.dataset.DatasetTableFieldDTO;
|
||||
import io.dataease.engine.constant.SQLConstants;
|
||||
import io.dataease.engine.func.scalar.ScalarFunctions;
|
||||
import io.dataease.engine.utils.Utils;
|
||||
import org.apache.calcite.func.scalar.ScalarFunctions;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -5,8 +5,8 @@ import io.dataease.api.dataset.union.model.SQLMeta;
|
||||
import io.dataease.api.dataset.union.model.SQLObj;
|
||||
import io.dataease.dto.dataset.DatasetTableFieldDTO;
|
||||
import io.dataease.engine.constant.SQLConstants;
|
||||
import io.dataease.engine.func.scalar.ScalarFunctions;
|
||||
import io.dataease.engine.utils.Utils;
|
||||
import org.apache.calcite.func.scalar.ScalarFunctions;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -55,7 +55,7 @@ public class ExtWhere2Str {
|
||||
if (field.getDeType() == 1) {
|
||||
String date_format;
|
||||
if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) {
|
||||
date_format = ScalarFunctions.format;
|
||||
date_format = "yyyy-MM-dd HH:mm:ss";
|
||||
} else {
|
||||
date_format = ScalarFunctions.get_date_format(value.get(0));
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ import io.dataease.api.permissions.dataset.dto.DatasetRowPermissionsTreeObj;
|
||||
import io.dataease.dto.dataset.DatasetTableFieldDTO;
|
||||
import io.dataease.engine.constant.ExtFieldConstant;
|
||||
import io.dataease.engine.constant.SQLConstants;
|
||||
import io.dataease.engine.func.scalar.ScalarFunctions;
|
||||
import io.dataease.engine.utils.Utils;
|
||||
import org.apache.calcite.func.scalar.ScalarFunctions;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -0,0 +1,13 @@
|
||||
package io.dataease.map.bo;
|
||||
|
||||
import io.dataease.map.dao.auto.entity.Area;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AreaBO extends Area implements Serializable {
|
||||
private boolean custom = false;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.dataease.map.dao.ext.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class CoreAreaCustom implements Serializable {
|
||||
|
||||
private String id;
|
||||
|
||||
private String pid;
|
||||
|
||||
private String name;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.dataease.map.dao.ext.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import io.dataease.map.dao.ext.entity.CoreAreaCustom;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CoreAreaCustomMapper extends BaseMapper<CoreAreaCustom> {
|
||||
}
|
@ -1,17 +1,34 @@
|
||||
package io.dataease.map.manage;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||
import io.dataease.api.map.vo.AreaNode;
|
||||
import io.dataease.constant.StaticResourceConstants;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.map.bo.AreaBO;
|
||||
import io.dataease.map.dao.auto.entity.Area;
|
||||
import io.dataease.map.dao.auto.mapper.AreaMapper;
|
||||
import io.dataease.map.dao.ext.entity.CoreAreaCustom;
|
||||
import io.dataease.map.dao.ext.mapper.CoreAreaCustomMapper;
|
||||
import io.dataease.utils.BeanUtils;
|
||||
import io.dataease.utils.CommonBeanFactory;
|
||||
import io.dataease.utils.LogUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.dataease.constant.CacheConstant.CommonCacheConstant.WORLD_MAP_CACHE;
|
||||
|
||||
@ -19,6 +36,8 @@ import static io.dataease.constant.CacheConstant.CommonCacheConstant.WORLD_MAP_C
|
||||
public class MapManage {
|
||||
private final static AreaNode WORLD;
|
||||
|
||||
private static final String GEO_PREFIX = "geo_";
|
||||
|
||||
static {
|
||||
WORLD = AreaNode.builder()
|
||||
.id("000")
|
||||
@ -30,13 +49,34 @@ public class MapManage {
|
||||
@Resource
|
||||
private AreaMapper areaMapper;
|
||||
|
||||
@Resource
|
||||
private CoreAreaCustomMapper coreAreaCustomMapper;
|
||||
|
||||
public List<Area> defaultArea() {
|
||||
return areaMapper.selectList(null);
|
||||
}
|
||||
|
||||
private MapManage proxy() {
|
||||
return CommonBeanFactory.getBean(MapManage.class);
|
||||
}
|
||||
|
||||
@Cacheable(value = WORLD_MAP_CACHE, key = "'world_map'")
|
||||
public AreaNode getWorldTree() {
|
||||
List<Area> areas = areaMapper.selectList(null);
|
||||
List<Area> areas = proxy().defaultArea();
|
||||
List<AreaBO> areaBOS = areas.stream().map(item -> BeanUtils.copyBean(new AreaBO(), item)).collect(Collectors.toList());
|
||||
List<CoreAreaCustom> coreAreaCustoms = coreAreaCustomMapper.selectList(null);
|
||||
if (CollectionUtils.isNotEmpty(coreAreaCustoms)) {
|
||||
List<AreaBO> customBoList = coreAreaCustoms.stream().map(item -> {
|
||||
AreaBO areaBO = BeanUtils.copyBean(new AreaBO(), item);
|
||||
areaBO.setCustom(true);
|
||||
return areaBO;
|
||||
}).toList();
|
||||
areaBOS.addAll(customBoList);
|
||||
}
|
||||
WORLD.setChildren(new ArrayList<>());
|
||||
var areaNodeMap = new HashMap<String, AreaNode>();
|
||||
areaNodeMap.put(WORLD.getId(), WORLD);
|
||||
areas.forEach(area -> {
|
||||
areaBOS.forEach(area -> {
|
||||
var node = areaNodeMap.get(area.getId());
|
||||
if (node == null) {
|
||||
node = AreaNode.builder().build();
|
||||
@ -64,5 +104,80 @@ public class MapManage {
|
||||
return WORLD;
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = WORLD_MAP_CACHE, key = "'world_map'")
|
||||
@Transactional
|
||||
public void saveMapGeo(GeometryNodeCreator request, MultipartFile file) {
|
||||
List<Area> areas = proxy().defaultArea();
|
||||
String code = getBusiGeoCode(request.getCode());
|
||||
|
||||
AtomicReference<String> atomicReference = new AtomicReference<>();
|
||||
if (areas.stream().anyMatch(area -> {
|
||||
boolean exist = area.getId().equals(code);
|
||||
if (exist) {
|
||||
atomicReference.set(area.getName());
|
||||
}
|
||||
return exist;
|
||||
})) {
|
||||
DEException.throwException(String.format("Area code [%s] is already exists for [%s]", code, atomicReference.get()));
|
||||
}
|
||||
|
||||
CoreAreaCustom originData = null;
|
||||
if (ObjectUtils.isNotEmpty(originData = coreAreaCustomMapper.selectById(getDaoGeoCode(code)))) {
|
||||
DEException.throwException(String.format("Area code [%s] is already exists for [%s]", code, originData.getName()));
|
||||
}
|
||||
|
||||
CoreAreaCustom coreAreaCustom = new CoreAreaCustom();
|
||||
coreAreaCustom.setId(getDaoGeoCode(code));
|
||||
coreAreaCustom.setPid(request.getPid());
|
||||
coreAreaCustom.setName(request.getName());
|
||||
coreAreaCustomMapper.insert(coreAreaCustom);
|
||||
|
||||
File geoFile = buildGeoFile(code);
|
||||
try {
|
||||
file.transferTo(geoFile);
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
DEException.throwException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = WORLD_MAP_CACHE, key = "'world_map'")
|
||||
@Transactional
|
||||
public void deleteGeo(String code) {
|
||||
if (!StringUtils.startsWith(code, GEO_PREFIX)) {
|
||||
DEException.throwException("内置Geometry,禁止删除");
|
||||
}
|
||||
coreAreaCustomMapper.deleteById(code);
|
||||
File file = buildGeoFile(code);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private String getDaoGeoCode(String code) {
|
||||
return StringUtils.startsWith(code, GEO_PREFIX) ? code : (GEO_PREFIX + code);
|
||||
}
|
||||
|
||||
private String getBusiGeoCode(String code) {
|
||||
return StringUtils.startsWith(code, GEO_PREFIX) ? code.substring(GEO_PREFIX.length()) : code;
|
||||
}
|
||||
|
||||
private File buildGeoFile(String code) {
|
||||
String id = getBusiGeoCode(code);
|
||||
String customMapDir = StaticResourceConstants.CUSTOM_MAP_DIR;
|
||||
String countryCode = countryCode(id);
|
||||
String fileDirPath = customMapDir + "/" + countryCode + "/";
|
||||
File dir = new File(fileDirPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
String filePath = fileDirPath + id + ".json";
|
||||
return new File(filePath);
|
||||
}
|
||||
|
||||
private String countryCode(String code) {
|
||||
return code.substring(0, 3);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package io.dataease.map.server;
|
||||
|
||||
import io.dataease.api.map.GeoApi;
|
||||
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||
import io.dataease.map.manage.MapManage;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/geometry")
|
||||
public class GeoServer implements GeoApi {
|
||||
|
||||
@Resource
|
||||
private MapManage mapManage;
|
||||
@Override
|
||||
public void saveMapGeo(GeometryNodeCreator request, MultipartFile file) {
|
||||
mapManage.saveMapGeo(request, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteGeo(String id) {
|
||||
mapManage.deleteGeo(id);
|
||||
}
|
||||
}
|
@ -95,6 +95,7 @@ public class MenuManage {
|
||||
|| coreMenu.getId().equals(18L)
|
||||
|| coreMenu.getId().equals(21L)
|
||||
|| coreMenu.getPid().equals(21L)
|
||||
|| coreMenu.getId().equals(25L);
|
||||
|| coreMenu.getId().equals(25L)
|
||||
|| coreMenu.getId().equals(26L);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package io.dataease.system.dao.ext.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import io.dataease.system.dao.auto.entity.CoreSysSetting;
|
||||
import io.dataease.system.dao.auto.mapper.CoreSysSettingMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("extCoreSysSettingMapper")
|
||||
public class ExtCoreSysSettingMapper extends ServiceImpl<CoreSysSettingMapper, CoreSysSetting> {
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
package io.dataease.system.manage;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.dataease.api.system.vo.SettingItemVO;
|
||||
import io.dataease.license.config.XpackInteract;
|
||||
import io.dataease.system.dao.auto.entity.CoreSysSetting;
|
||||
import io.dataease.system.dao.auto.mapper.CoreSysSettingMapper;
|
||||
import io.dataease.system.dao.ext.mapper.ExtCoreSysSettingMapper;
|
||||
import io.dataease.utils.BeanUtils;
|
||||
import io.dataease.utils.CommonBeanFactory;
|
||||
import io.dataease.utils.IDUtils;
|
||||
import io.dataease.utils.SystemSettingUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -22,6 +29,9 @@ public class SysParameterManage {
|
||||
@Resource
|
||||
private CoreSysSettingMapper coreSysSettingMapper;
|
||||
|
||||
@Resource
|
||||
private ExtCoreSysSettingMapper extCoreSysSettingMapper;
|
||||
|
||||
public String singleVal(String key) {
|
||||
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("pkey", key);
|
||||
@ -53,22 +63,54 @@ public class SysParameterManage {
|
||||
sysSetting.setPval(val);
|
||||
coreSysSettingMapper.updateById(sysSetting);
|
||||
}
|
||||
void save(List<CoreSysSetting> boList) {
|
||||
List<CoreSysSetting> all = all();
|
||||
}
|
||||
|
||||
private List<CoreSysSetting> all() {
|
||||
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
|
||||
return coreSysSettingMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
public Map<String,String> groupVal(String groupKey) {
|
||||
public Map<String, String> groupVal(String groupKey) {
|
||||
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.like("pkey", groupKey);
|
||||
queryWrapper.likeRight("pkey", groupKey);
|
||||
queryWrapper.orderByAsc("sort");
|
||||
List<CoreSysSetting> sysSettings = coreSysSettingMapper.selectList(queryWrapper);
|
||||
if (!CollectionUtils.isEmpty(sysSettings)) {
|
||||
return sysSettings.stream().collect(Collectors.toMap(CoreSysSetting::getPkey, CoreSysSetting::getPval));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<CoreSysSetting> groupList(String groupKey) {
|
||||
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.likeRight("pkey", groupKey);
|
||||
queryWrapper.orderByAsc("sort");
|
||||
return coreSysSettingMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
@XpackInteract(value = "perSetting")
|
||||
public List<SettingItemVO> convert(List<CoreSysSetting> sysSettings) {
|
||||
return sysSettings.stream().sorted(Comparator.comparing(CoreSysSetting::getSort)).map(item -> BeanUtils.copyBean(new SettingItemVO(), item)).toList();
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public void saveGroup(List<SettingItemVO> vos, String groupKey) {
|
||||
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.likeRight("pkey", groupKey);
|
||||
coreSysSettingMapper.delete(queryWrapper);
|
||||
List<CoreSysSetting> sysSettings = vos.stream().filter(vo -> !SystemSettingUtils.xpackSetting(vo.getPkey())).map(item -> {
|
||||
CoreSysSetting sysSetting = BeanUtils.copyBean(new CoreSysSetting(), item);
|
||||
sysSetting.setId(IDUtils.snowID());
|
||||
return sysSetting;
|
||||
}).collect(Collectors.toList());
|
||||
extCoreSysSettingMapper.saveBatch(sysSettings);
|
||||
}
|
||||
|
||||
|
||||
@XpackInteract(value = "perSetting", before = false)
|
||||
@Transactional
|
||||
public void saveBasic(List<SettingItemVO> vos) {
|
||||
String key = "basic.";
|
||||
proxy().saveGroup(vos, key);
|
||||
}
|
||||
|
||||
private SysParameterManage proxy() {
|
||||
return CommonBeanFactory.getBean(SysParameterManage.class);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,16 @@ package io.dataease.system.server;
|
||||
|
||||
import io.dataease.api.system.SysParameterApi;
|
||||
import io.dataease.api.system.request.OnlineMapEditor;
|
||||
import io.dataease.api.system.vo.SettingItemVO;
|
||||
import io.dataease.system.dao.auto.entity.CoreSysSetting;
|
||||
import io.dataease.system.manage.SysParameterManage;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/sysParameter")
|
||||
public class SysParameterServer implements SysParameterApi {
|
||||
@ -29,4 +33,16 @@ public class SysParameterServer implements SysParameterApi {
|
||||
String key = sysParameterManage.queryOnlineMap();
|
||||
return StringUtils.isNotBlank(key) ? key : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SettingItemVO> queryBasicSetting() {
|
||||
String key = "basic.";
|
||||
List<CoreSysSetting> coreSysSettings = sysParameterManage.groupList(key);
|
||||
return sysParameterManage.convert(coreSysSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBasicSetting(List<SettingItemVO> settingItemVOS) {
|
||||
sysParameterManage.saveBasic(settingItemVOS);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,17 +26,32 @@ VALUES (20, 15, 2, 'template-setting', 'system/template-setting', 4, 'icon_templ
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE IF EXISTS `visualization_template_extend_data`;
|
||||
CREATE TABLE `visualization_template_extend_data` (
|
||||
`id` bigint NOT NULL,
|
||||
`dv_id` bigint DEFAULT NULL,
|
||||
`view_id` bigint DEFAULT NULL,
|
||||
`view_details` longtext,
|
||||
`copy_from` varchar(255) DEFAULT NULL,
|
||||
`copy_id` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
CREATE TABLE `visualization_template_extend_data`
|
||||
(
|
||||
`id` bigint NOT NULL,
|
||||
`dv_id` bigint DEFAULT NULL,
|
||||
`view_id` bigint DEFAULT NULL,
|
||||
`view_details` longtext,
|
||||
`copy_from` varchar(255) DEFAULT NULL,
|
||||
`copy_id` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE `core_opt_recent`
|
||||
MODIFY COLUMN `resource_id` bigint NULL COMMENT '资源ID' AFTER `id`,
|
||||
ADD COLUMN `resource_name` varchar(255) NULL COMMENT '资源名称' AFTER `resource_id`;
|
||||
ADD COLUMN `resource_name` varchar(255) NULL COMMENT '资源名称' AFTER `resource_id`;
|
||||
|
||||
DROP TABLE IF EXISTS `core_area_custom`;
|
||||
CREATE TABLE `core_area_custom`
|
||||
(
|
||||
`id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`pid` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
BEGIN;
|
||||
INSERT INTO `core_sys_setting` VALUES (1, 'basic.dsIntervalTime', '6', 'text', 2);
|
||||
INSERT INTO `core_sys_setting` VALUES (2, 'basic.dsExecuteTime', 'minute', 'text', 3);
|
||||
COMMIT;
|
||||
|
@ -23,6 +23,7 @@ i18n_menu.summary=\u6982\u89C8
|
||||
i18n_menu.ds=\u6570\u636E\u6E90\u7BA1\u7406
|
||||
i18n_menu.task=\u4EFB\u52A1\u7BA1\u7406
|
||||
i18n_menu.embedded=\u5D4C\u5165\u5F0F\u7BA1\u7406
|
||||
i18n_menu.platform=\u5E73\u53F0\u5BF9\u63A5
|
||||
i18n_field_name_repeat=\u6709\u91CD\u590D\u5B57\u6BB5\u540D\uFF1A
|
||||
i18n_pid_not_eq_id=\u79FB\u52A8\u76EE\u6807\u4E0D\u80FD\u662F\u81EA\u5DF1\u6216\u5B50\u76EE\u5F55
|
||||
i18n_ds_name_exists=\u8BE5\u5206\u7EC4\u4E0B\u540D\u79F0\u91CD\u590D
|
||||
|
@ -5,9 +5,21 @@ export const getWorldTree = (): Promise<IResponse<AreaNode>> => {
|
||||
return request.get({ url: '/map/worldTree' })
|
||||
}
|
||||
|
||||
export const getGeoJson = (
|
||||
country: string,
|
||||
areaId: string
|
||||
): Promise<IResponse<FeatureCollection>> => {
|
||||
return request.get({ url: `/map/${country}/${areaId}.json` })
|
||||
export const getGeoJson = (areaId: string): Promise<IResponse<FeatureCollection>> => {
|
||||
let prefix = '/map'
|
||||
let areaCode = areaId
|
||||
if (isCustomGeo(areaId)) {
|
||||
prefix = '/geo'
|
||||
areaCode = getBusiGeoCode(areaId)
|
||||
}
|
||||
const realCountry = areaCode.substring(0, 3)
|
||||
const url = `${prefix}/${realCountry}/${areaCode}.json`
|
||||
return request.get({ url })
|
||||
}
|
||||
|
||||
const isCustomGeo = (id: string) => {
|
||||
return id.startsWith('geo_')
|
||||
}
|
||||
const getBusiGeoCode = (id: string) => {
|
||||
return id.substring(4)
|
||||
}
|
||||
|
1
core/core-frontend/src/assets/svg/de-json.svg
Normal file
1
core/core-frontend/src/assets/svg/de-json.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.1 KiB |
1
core/core-frontend/src/assets/svg/platform.svg
Normal file
1
core/core-frontend/src/assets/svg/platform.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M314.49 3.082H157.692C70.793 3.082 0 74.073 0 161.271V864.32c0 87.198 70.792 158.189 157.592 158.189H314.49c86.9 0 157.592-70.991 157.592-158.189V702.652h-72.98V864.32c0 46.93-37.98 84.91-84.612 84.91H157.693c-46.731 0-84.613-38.18-84.613-84.91V161.27c0-46.929 37.981-84.91 84.613-84.91h156.896c46.73 0 84.612 38.18 84.612 84.91v159.68h72.98V161.27c-0.1-87.197-70.793-158.188-157.692-158.188z m546.252 0H703.746c-86.899 0-157.592 70.991-157.592 158.189v159.68h72.98V161.27c0-46.93 37.981-84.911 84.612-84.911h156.896c46.731 0 84.613 38.18 84.613 84.91v703.05c0 46.93-37.981 84.91-84.613 84.91H703.746c-46.73 0-84.612-38.18-84.612-84.91V702.652h-72.98V864.32c0 87.198 70.792 158.189 157.592 158.189h156.896c86.9 0 157.592-70.991 157.592-158.189V161.27c0.1-87.197-70.692-158.188-157.492-158.188z m0 0" /><path d="M191.995 485.602H461.84c-0.596-27.74-23.465-50.31-51.304-50.31H186.129c-28.238 0-51.305 23.166-51.305 51.503v105.492c0 28.337 23.067 51.504 51.305 51.504h224.506c28.237 0 51.305-23.167 51.305-51.504v-1.093H191.995V485.602z m0 0" /><path d="M395.423 512.447h230.074v46.035H395.423z" /><path d="M556.395 485.602H826.34v105.492H556.395v1.094c0 28.337 23.067 51.503 51.305 51.503h224.506c28.238 0 51.305-23.166 51.305-51.503V486.696c0-28.337-23.067-51.503-51.305-51.503H607.7c-27.84 0.099-50.708 22.669-51.305 50.41z m0 0" /></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -58,8 +58,7 @@ const loadComponent = () => {
|
||||
storeCacheProxy(byteArray)
|
||||
importProxy(byteArray)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
.catch(() => {
|
||||
showNolic()
|
||||
})
|
||||
.finally(() => {
|
||||
|
@ -130,7 +130,7 @@ service.interceptors.response.use(
|
||||
return response
|
||||
} else if (response.data.code === result_code || response.data.code === 50002) {
|
||||
return response.data
|
||||
} else if (response.config.url.match(/^\/map\/\d{3}\/\d+\.json$/)) {
|
||||
} else if (response.config.url.match(/^\/map|geo\/\d{3}\/\d+\.json$/)) {
|
||||
// TODO 处理静态文件
|
||||
return response
|
||||
} else {
|
||||
|
@ -3,6 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { formatDataEaseBi } from '@/utils/url'
|
||||
//JS部分
|
||||
//在js中引入所需的主题和组件
|
||||
import tinymce from 'tinymce/tinymce'
|
||||
@ -58,9 +59,9 @@ const tinymceId = ref('vue-tinymce-' + +new Date() + ((Math.random() * 1000).toF
|
||||
const init = reactive({
|
||||
// inline: true, // 开启内联模式
|
||||
selector: '#' + tinymceId.value, //富文本编辑器的id,
|
||||
language_url: '/tinymce-dataease-private/langs/zh_CN.js', // 语言包的路径,具体路径看自己的项目,文档后面附上中文js文件
|
||||
language_url: formatDataEaseBi('/tinymce-dataease-private/langs/zh_CN.js'), // 语言包的路径,具体路径看自己的项目,文档后面附上中文js文件
|
||||
language: 'zh_CN', //语言
|
||||
skin_url: '/tinymce-dataease-private/skins/ui/oxide', // skin路径,具体路径看自己的项目
|
||||
skin_url: formatDataEaseBi('/tinymce-dataease-private/skins/ui/oxide'), // skin路径,具体路径看自己的项目
|
||||
height: 400, //编辑器高度
|
||||
branding: false, //是否禁用“Powered by TinyMCE”
|
||||
menubar: true, //顶部菜单栏显示
|
||||
@ -75,7 +76,7 @@ const init = reactive({
|
||||
nonbreaking_force_tab: false,
|
||||
paste_auto_cleanup_on_paste: false,
|
||||
file_picker_types: 'file',
|
||||
content_css: '/tinymce-dataease-private/skins/content/default/content.css', //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
|
||||
content_css: formatDataEaseBi('/tinymce-dataease-private/skins/content/default/content.css'), //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
|
||||
//图片上传
|
||||
images_upload_handler: blobInfo =>
|
||||
new Promise((resolve, reject) => {
|
||||
|
@ -28,6 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { formatDataEaseBi } from '@/utils/url'
|
||||
import tinymce from 'tinymce/tinymce' // tinymce默认hidden,不引入不显示
|
||||
import Editor from '@tinymce/tinymce-vue' // 编辑器引入
|
||||
import 'tinymce/themes/silver/theme' // 编辑器主题
|
||||
@ -114,10 +115,10 @@ const myValue = ref('')
|
||||
const init = ref({
|
||||
selector: '#' + tinymceId,
|
||||
toolbar_items_size: 'small',
|
||||
language_url: '/tinymce-dataease-private/langs/zh_CN.js', // 汉化路径是自定义的,一般放在public或static里面
|
||||
language_url: formatDataEaseBi('/tinymce-dataease-private/langs/zh_CN.js'), // 汉化路径是自定义的,一般放在public或static里面
|
||||
language: 'zh_CN',
|
||||
skin_url: '/tinymce-dataease-private/skins/ui/oxide', // 皮肤
|
||||
content_css: '/tinymce-dataease-private/skins/content/default/content.css',
|
||||
skin_url: formatDataEaseBi('/tinymce-dataease-private/skins/ui/oxide'), // 皮肤
|
||||
content_css: formatDataEaseBi('/tinymce-dataease-private/skins/content/default/content.css'),
|
||||
plugins:
|
||||
'advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', // 插件
|
||||
// 工具栏
|
||||
|
@ -17,6 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { formatDataEaseBi } from '@/utils/url'
|
||||
import tinymce from 'tinymce/tinymce' // tinymce默认hidden,不引入不显示
|
||||
import Editor from '@tinymce/tinymce-vue' // 编辑器引入
|
||||
import 'tinymce/themes/silver/theme' // 编辑器主题
|
||||
@ -80,10 +81,10 @@ const myValue = ref(element.value.propValue.textValue)
|
||||
const init = ref({
|
||||
selector: '#' + tinymceId,
|
||||
toolbar_items_size: 'small',
|
||||
language_url: '/tinymce-dataease-private/langs/zh_CN.js', // 汉化路径是自定义的,一般放在public或static里面
|
||||
language_url: formatDataEaseBi('/tinymce-dataease-private/langs/zh_CN.js'), // 汉化路径是自定义的,一般放在public或static里面
|
||||
language: 'zh_CN',
|
||||
skin_url: '/tinymce-dataease-private/skins/ui/oxide', // 皮肤
|
||||
content_css: '/tinymce-dataease-private/skins/content/default/content.css',
|
||||
skin_url: formatDataEaseBi('/tinymce-dataease-private/skins/ui/oxide'), // 皮肤
|
||||
content_css: formatDataEaseBi('/tinymce-dataease-private/skins/content/default/content.css'),
|
||||
plugins:
|
||||
'advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', // 插件
|
||||
// 工具栏
|
||||
|
@ -2122,5 +2122,10 @@ export default {
|
||||
geometry: '地理信息',
|
||||
onlinemap: '在线地图',
|
||||
empty_desc: '请在左侧输入信息然后保存'
|
||||
},
|
||||
setting_basic: {
|
||||
autoCreateUser: '禁止第三方自动创建用户',
|
||||
dsIntervalTime: '数据源检测时间间隔',
|
||||
dsExecuteTime: '数据源检测频率'
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const formatterUrl = <T extends Node>(node: T, prefix: string) => {
|
||||
url = node.src
|
||||
}
|
||||
|
||||
if (url.includes(suffix)) {
|
||||
if (url.includes(suffix) || url.includes('dataease-private')) {
|
||||
const currentUrlprefix = new URL(url).origin
|
||||
const newUrl = url.replace(currentUrlprefix, prefix)
|
||||
if (node instanceof HTMLLinkElement) {
|
||||
|
22
core/core-frontend/src/utils/RemoteJs.ts
Normal file
22
core/core-frontend/src/utils/RemoteJs.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export const loadScript = (url: string, jsId?: string) => {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const scriptId = jsId || 'de-fit2cloud-script-id'
|
||||
let dom = document.getElementById(scriptId)
|
||||
if (dom) {
|
||||
dom.parentElement?.removeChild(dom)
|
||||
dom = null
|
||||
}
|
||||
const script = document.createElement('script')
|
||||
|
||||
script.id = scriptId
|
||||
script.onload = function () {
|
||||
return resolve(null)
|
||||
}
|
||||
script.onerror = function () {
|
||||
return reject(new Error('Load script from '.concat(url, ' failed')))
|
||||
}
|
||||
script.src = url
|
||||
const head = document.head || document.getElementsByTagName('head')[0]
|
||||
;(document.body || head).appendChild(script)
|
||||
})
|
||||
}
|
@ -10,19 +10,24 @@ const { canvasStyleData, componentData, canvasViewInfo, canvasViewDataInfo, dvIn
|
||||
storeToRefs(dvMainStore)
|
||||
const basePath = import.meta.env.VITE_API_BASEPATH
|
||||
|
||||
export function formatterUrl(url: string) {
|
||||
return url.replace('//de2api', '/de2api')
|
||||
}
|
||||
export function imgUrlTrans(url) {
|
||||
if (url) {
|
||||
if (typeof url === 'string' && url.indexOf('static-resource') > -1) {
|
||||
const rawUrl = url
|
||||
? (basePath.endsWith('/') ? basePath.substring(0, basePath.length - 1) : basePath) + url
|
||||
: null
|
||||
return window.DataEaseBi
|
||||
? `${window.DataEaseBi.baseUrl}${
|
||||
rawUrl.startsWith('/api') ? rawUrl.slice(5) : rawUrl
|
||||
}`.replace('com//', 'com/')
|
||||
: rawUrl
|
||||
return formatterUrl(
|
||||
window.DataEaseBi
|
||||
? `${window.DataEaseBi.baseUrl}${
|
||||
rawUrl.startsWith('/api') ? rawUrl.slice(5) : rawUrl
|
||||
}`.replace('com//', 'com/')
|
||||
: rawUrl
|
||||
)
|
||||
} else {
|
||||
return url.replace('com//', 'com/')
|
||||
return formatterUrl(url.replace('com//', 'com/'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
core/core-frontend/src/utils/url.ts
Normal file
3
core/core-frontend/src/utils/url.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const formatDataEaseBi = (url: string) => {
|
||||
return window.DataEaseBi?.baseUrl ? `${window.DataEaseBi.baseUrl}${url}` : url
|
||||
}
|
@ -67,3 +67,16 @@ export const setColorName = (obj, keyword: string, key?: string, colorKey?: stri
|
||||
}
|
||||
obj[colorKey] = null
|
||||
}
|
||||
|
||||
export const getQueryString = (name: string) => {
|
||||
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
|
||||
const r = window.location.search.substr(1).match(reg)
|
||||
if (r != null) {
|
||||
return unescape(r[2])
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const isLarkPlatform = () => {
|
||||
return !!getQueryString('state') && !!getQueryString('code')
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ const license: F2CLicense = reactive({
|
||||
remark: '',
|
||||
isv: ''
|
||||
})
|
||||
const tipsSuffix = ref('')
|
||||
const build = ref('')
|
||||
const isAdmin = ref(false)
|
||||
const fileList = reactive([])
|
||||
@ -88,11 +89,14 @@ const validateHandler = (param, success) => {
|
||||
validateApi(param).then(success)
|
||||
}
|
||||
const getLicense = result => {
|
||||
if (result.status === 'valid') {
|
||||
tipsSuffix.value = result.edition === 'Embedded' ? '套' : '个账号'
|
||||
}
|
||||
return {
|
||||
status: result.status,
|
||||
corporation: result.license ? result.license.corporation : '',
|
||||
expired: result.license ? result.license.expired : '',
|
||||
count: result.license ? result.license.count : '',
|
||||
count: result.license ? result.license.count : 0,
|
||||
version: result.license ? result.license.version : '',
|
||||
edition: result.license ? result.license.edition : '',
|
||||
serialNo: result.license ? result.license.serialNo : '',
|
||||
@ -145,7 +149,9 @@ const update = (licKey: string) => {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">{{ $t('about.auth_num') }}</div>
|
||||
<div class="value">{{ license.count }}</div>
|
||||
<div class="value">
|
||||
{{ license.status === 'valid' ? `${license.count} ${tipsSuffix}` : '' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">{{ $t('about.version') }}</div>
|
||||
|
@ -18,9 +18,8 @@ import { storeToRefs } from 'pinia'
|
||||
import { BASE_VIEW_CONFIG } from '../util/chart'
|
||||
import { cloneDeep, defaultsDeep } from 'lodash-es'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { dvInfo } = storeToRefs(dvMainStore)
|
||||
|
||||
const { nowPanelTrackInfo, nowPanelJumpInfo } = storeToRefs(dvMainStore)
|
||||
const { nowPanelTrackInfo, nowPanelJumpInfo, dvInfo } = storeToRefs(dvMainStore)
|
||||
|
||||
const { t } = useI18n()
|
||||
const linkJumpRef = ref(null)
|
||||
@ -154,7 +153,7 @@ const noSenior = computed(() => {
|
||||
const linkJumpActiveChange = () => {
|
||||
// 直接触发刷新
|
||||
const params = {
|
||||
sourceDvId: chart.value.sceneId,
|
||||
sourceDvId: dvInfo.value.id,
|
||||
sourceViewId: chart.value.id,
|
||||
activeStatus: chart.value.jumpActive
|
||||
}
|
||||
@ -164,7 +163,7 @@ const linkJumpActiveChange = () => {
|
||||
}
|
||||
const linkageActiveChange = () => {
|
||||
const params = {
|
||||
dvId: chart.value.sceneId,
|
||||
dvId: dvInfo.value.id,
|
||||
sourceViewId: chart.value.id,
|
||||
activeStatus: chart.value.linkageActive
|
||||
}
|
||||
|
@ -421,10 +421,8 @@ export const getGeoJsonFile = async (areaId: string): Promise<FeatureCollection>
|
||||
const mapStore = useMapStoreWithOut()
|
||||
let geoJson = mapStore.mapCache[areaId]
|
||||
if (!geoJson) {
|
||||
const country = areaId.slice(0, 3)
|
||||
geoJson = await getGeoJson(country, areaId).then(result => {
|
||||
return result.data
|
||||
})
|
||||
const res = await getGeoJson(areaId)
|
||||
geoJson = res.data
|
||||
mapStore.setMap({ id: areaId, geoJson })
|
||||
}
|
||||
return toRaw(geoJson)
|
||||
|
@ -16,6 +16,7 @@ import { logoutHandler } from '@/utils/logout'
|
||||
import DeImage from '@/assets/login-desc-de.png'
|
||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||
import PreheatImage from '@/assets/preheat.png'
|
||||
import { isLarkPlatform } from '@/utils/utils'
|
||||
const { wsCache } = useCache()
|
||||
const appStore = useAppStoreWithOut()
|
||||
const userStore = useUserStoreWithOut()
|
||||
@ -129,7 +130,7 @@ const showLoginImage = computed<boolean>(() => {
|
||||
const checkPlatform = () => {
|
||||
const flagArray = ['/casbi', 'oidcbi']
|
||||
const pathname = window.location.pathname
|
||||
if (!flagArray.some(flag => pathname.includes(flag))) {
|
||||
if (!flagArray.some(flag => pathname.includes(flag)) && !isLarkPlatform()) {
|
||||
cleanPlatformFlag()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="info-template-container">
|
||||
<div class="info-template-header">
|
||||
<div class="info-template-title">
|
||||
<span>基础设置</span>
|
||||
<span>{{ curTitle }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" @click="edit">{{ t('commons.edit') }}</el-button>
|
||||
@ -11,7 +11,7 @@
|
||||
<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>
|
||||
<span>{{ t(item.pkey) }}</span>
|
||||
<el-tooltip
|
||||
v-if="tooltipItem[item.pkey]"
|
||||
effect="dark"
|
||||
@ -33,6 +33,9 @@
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<span v-else-if="item.pkey.includes('basic.dsIntervalTime')">
|
||||
<span>{{ item.pval + ' ' + executeTime + '执行一次' }}</span>
|
||||
</span>
|
||||
<span v-else>{{ item.pval }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -40,7 +43,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineProps, PropType } from 'vue'
|
||||
import { ref, defineProps, PropType, computed } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { SettingRecord, ToolTipRecord } from './SettingTemplate'
|
||||
const { t } = useI18n()
|
||||
@ -52,89 +55,47 @@ const props = defineProps({
|
||||
labelTooltips: {
|
||||
type: Array as PropType<ToolTipRecord[]>,
|
||||
default: () => []
|
||||
},
|
||||
settingData: {
|
||||
type: Array as PropType<SettingRecord[]>,
|
||||
default: () => []
|
||||
},
|
||||
settingTitle: {
|
||||
type: String,
|
||||
default: '基础设置'
|
||||
}
|
||||
})
|
||||
const executeTime = ref('0分0秒')
|
||||
const curTitle = computed(() => {
|
||||
return props.settingTitle
|
||||
})
|
||||
|
||||
const loadList = () => {
|
||||
settingList.value = []
|
||||
if (props.settingData?.length) {
|
||||
props.settingData.forEach(item => {
|
||||
if (item.pkey.includes('basic.dsExecuteTime')) {
|
||||
executeTime.value = getExecuteTime(item.pval)
|
||||
} else {
|
||||
settingList.value.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getExecuteTime = val => {
|
||||
const options = [
|
||||
{ value: 'minute', label: '分钟(执行时间:0秒)' },
|
||||
{ value: 'hour', label: '小时(执行时间:0分0秒)' }
|
||||
]
|
||||
return options.filter(item => item.value === val)[0].label
|
||||
}
|
||||
|
||||
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()
|
||||
if (props.settingData?.length) {
|
||||
loadList()
|
||||
}
|
||||
}
|
||||
const pwdItem = ref({})
|
||||
@ -163,6 +124,9 @@ const emits = defineEmits(['edit'])
|
||||
const edit = () => {
|
||||
emits('edit')
|
||||
}
|
||||
defineExpose({
|
||||
init
|
||||
})
|
||||
init()
|
||||
formatPwd()
|
||||
formatLabel()
|
||||
|
@ -3,51 +3,57 @@ 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'
|
||||
|
||||
import request from '@/config/axios'
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref(null)
|
||||
const createUserForm = ref<FormInstance>()
|
||||
const basicForm = ref<FormInstance>()
|
||||
const options = [
|
||||
{ value: 'minute', label: '分钟(执行时间:0秒)' },
|
||||
{ value: 'hour', label: '小时(执行时间:0分0秒)' }
|
||||
]
|
||||
const state = reactive({
|
||||
roleList: [],
|
||||
form: reactive<UserForm>({
|
||||
id: null,
|
||||
account: null,
|
||||
name: null,
|
||||
email: null,
|
||||
enable: true,
|
||||
phone: null,
|
||||
phonePrefix: '+86',
|
||||
roleIds: []
|
||||
})
|
||||
form: reactive({
|
||||
dsIntervalTime: '30',
|
||||
dsExecuteTime: 'minute'
|
||||
}),
|
||||
settingList: []
|
||||
})
|
||||
|
||||
const rule = reactive<FormRules>({})
|
||||
const rule = reactive<FormRules>({
|
||||
dsIntervalTime: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const edit = () => {
|
||||
dialogVisible.value = true
|
||||
// queryForm()
|
||||
}
|
||||
/* const queryForm = () => {
|
||||
showLoading()
|
||||
personInfoApi().then(res => {
|
||||
state.form = reactive<UserForm>(res.data)
|
||||
closeLoading()
|
||||
const buildSettingList = () => {
|
||||
return state.settingList.map(item => {
|
||||
const pkey = item.pkey.startsWith('basic.') ? item.pkey : `basic.${item.pkey}`
|
||||
const sort = item.sort
|
||||
const type = item.type
|
||||
const pval = state.form[item.pkey]
|
||||
return { pkey, pval, type, sort }
|
||||
})
|
||||
} */
|
||||
}
|
||||
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
|
||||
const param = buildSettingList()
|
||||
if (param.length < 2) {
|
||||
return
|
||||
}
|
||||
showLoading()
|
||||
method(param)
|
||||
request
|
||||
.post({ url: '/sysParameter/basic/save', data: param })
|
||||
.then(res => {
|
||||
if (!res.msg) {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
|
||||
emits('saved')
|
||||
reset()
|
||||
}
|
||||
@ -63,13 +69,14 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
state.settingList = []
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(createUserForm.value)
|
||||
resetForm(basicForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
@ -78,6 +85,19 @@ const showLoading = () => {
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const edit = list => {
|
||||
state.settingList = list.map(item => {
|
||||
const pkey = item.pkey
|
||||
item['label'] = `setting_${pkey}`
|
||||
item['pkey'] = pkey.split('.')[1]
|
||||
let pval = item.pval
|
||||
state.form[item['pkey']] = pval || state.form[item['pkey']]
|
||||
return item
|
||||
})
|
||||
console.log(state.settingList)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
defineExpose({
|
||||
edit
|
||||
})
|
||||
@ -92,32 +112,83 @@ defineExpose({
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="createUserForm"
|
||||
ref="basicForm"
|
||||
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
|
||||
v-for="item in state.settingList"
|
||||
:key="item.pkey"
|
||||
:prop="item.pkey"
|
||||
:class="{ 'setting-hidden-item': item.pkey === 'dsExecuteTime' }"
|
||||
:label="t(item.label)"
|
||||
>
|
||||
<el-switch
|
||||
v-if="item.pkey === 'autoCreateUser'"
|
||||
active-value="true"
|
||||
inactive-value="false"
|
||||
v-model="state.form[item.pkey]"
|
||||
/>
|
||||
<div v-else-if="item.pkey === 'dsIntervalTime'" class="ds-task-form-inline">
|
||||
<span>每</span>
|
||||
<el-input-number
|
||||
v-model="state.form.dsIntervalTime"
|
||||
autocomplete="off"
|
||||
step-strictly
|
||||
class="text-left"
|
||||
:min="1"
|
||||
:placeholder="t('common.inputText')"
|
||||
controls-position="right"
|
||||
type="number"
|
||||
/>
|
||||
<el-select v-model="state.form.dsExecuteTime">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span class="ds-span">执行一次</span>
|
||||
</div>
|
||||
<div v-else />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="禁止扫码创建用户" prop="autoCreateUser">
|
||||
<el-switch v-model="state.form.autoCreateUser" />
|
||||
</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-item label="数据源检测时间间隔" prop="dsIntervalTime">
|
||||
<div class="ds-task-form-inline">
|
||||
<span>每</span>
|
||||
<el-input-number
|
||||
v-model="state.form.dsIntervalTime"
|
||||
autocomplete="off"
|
||||
step-strictly
|
||||
class="text-left"
|
||||
:min="1"
|
||||
:placeholder="t('common.inputText')"
|
||||
controls-position="right"
|
||||
type="number"
|
||||
/>
|
||||
<el-select v-model="state.form.dsExecuteTime">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span class="ds-span">执行一次</span>
|
||||
</div>
|
||||
</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)">
|
||||
<el-button @click="resetForm(basicForm)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitForm(basicForm)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@ -125,47 +196,25 @@ defineExpose({
|
||||
</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;
|
||||
<style scoped lang="less">
|
||||
.setting-hidden-item {
|
||||
display: none !important;
|
||||
}
|
||||
.ds-task-form-inline {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
.ed-input-number {
|
||||
width: 140px;
|
||||
margin: 0 6px;
|
||||
}
|
||||
.ed-select {
|
||||
width: 240px;
|
||||
:deep(.ed-input) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
span.ds-span {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,22 +1,63 @@
|
||||
<template>
|
||||
<InfoTemplate :label-tooltips="tooltips" setting-key="basic" @edit="edit" />
|
||||
<basic-edit ref="editor" />
|
||||
<InfoTemplate
|
||||
ref="infoTemplate"
|
||||
:label-tooltips="tooltips"
|
||||
setting-key="basic"
|
||||
setting-title="基础设置"
|
||||
:setting-data="state.templateList"
|
||||
@edit="edit"
|
||||
/>
|
||||
<basic-edit ref="editor" @saved="refresh" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import InfoTemplate from '../../common/InfoTemplate.vue'
|
||||
import BasicEdit from './BasicEdit.vue'
|
||||
|
||||
import request from '@/config/axios'
|
||||
import { SettingRecord } from '@/views/system/common/SettingTemplate'
|
||||
import { reactive } from 'vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const editor = ref()
|
||||
const infoTemplate = ref()
|
||||
const tooltips = [
|
||||
{
|
||||
key: '请求超时时间',
|
||||
val: '请求超时时间(单位:秒,注意:保存后刷新浏览器生效)'
|
||||
}
|
||||
]
|
||||
const state = reactive({
|
||||
templateList: [] as SettingRecord[]
|
||||
})
|
||||
let originData = []
|
||||
const search = cb => {
|
||||
const url = '/sysParameter/basic/query'
|
||||
originData = []
|
||||
state.templateList = []
|
||||
request.get({ url }).then(res => {
|
||||
originData = cloneDeep(res.data)
|
||||
const data = res.data
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const item = data[index]
|
||||
if (item.pkey === 'basic.autoCreateUser') {
|
||||
item.pval = item.pval === 'true' ? '开启' : '未开启'
|
||||
} else {
|
||||
item.pval = item.pval
|
||||
}
|
||||
item.pkey = 'setting_' + item.pkey
|
||||
state.templateList.push(item)
|
||||
}
|
||||
cb && cb()
|
||||
})
|
||||
}
|
||||
const refresh = () => {
|
||||
search(() => {
|
||||
infoTemplate?.value.init()
|
||||
})
|
||||
}
|
||||
refresh()
|
||||
|
||||
const edit = () => {
|
||||
editor?.value.edit()
|
||||
editor?.value.edit(cloneDeep(originData))
|
||||
}
|
||||
</script>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<InfoTemplate :label-tooltips="tooltips" setting-key="email" @edit="edit" />
|
||||
<InfoTemplate
|
||||
:label-tooltips="tooltips"
|
||||
setting-title="邮件设置"
|
||||
setting-key="email"
|
||||
@edit="edit"
|
||||
/>
|
||||
<email-edit ref="editor" />
|
||||
</template>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="container-sys-param">
|
||||
<map-setting v-if="activeName === 'map'" />
|
||||
<basic-info v-if="activeName === 'basic'" />
|
||||
<email-info v-if="activeName === 'email'" />
|
||||
<!-- <email-info v-if="activeName === 'email'" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -17,12 +17,12 @@ 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'
|
||||
/* import EmailInfo from './email/EmailInfo.vue' */
|
||||
const { t } = useI18n()
|
||||
|
||||
const tabArray = [
|
||||
{ label: '基础设置', name: 'basic' },
|
||||
{ label: '邮件设置', name: 'email' },
|
||||
/* { label: '邮件设置', name: 'email' }, */
|
||||
{ label: '地图设置', name: 'map' }
|
||||
/* {label: '引擎设置', name: 'engine'}, */
|
||||
]
|
||||
|
@ -3,7 +3,7 @@
|
||||
<el-aside class="geonetry-aside">
|
||||
<div class="geo-title">
|
||||
<span>{{ t('online_map.geometry') }}</span>
|
||||
<span class="add-icon-span">
|
||||
<span class="add-icon-span" @click="add()">
|
||||
<el-icon>
|
||||
<Icon name="icon_add_outlined"></Icon>
|
||||
</el-icon>
|
||||
@ -43,6 +43,19 @@
|
||||
:title="data.name"
|
||||
v-html="data.colorName && keyword ? data.colorName : data.name"
|
||||
/>
|
||||
<span class="geo-operate-container">
|
||||
<el-tooltip
|
||||
v-if="data.custom"
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('common.delete')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon @click.stop="delHandler(data)" class="hover-icon">
|
||||
<Icon name="icon_delete-trash_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
@ -83,6 +96,7 @@
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<geometry-edit ref="editor" @saved="loadTreeData(false)" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -93,15 +107,21 @@ import EmptyBackground from '@/components/empty-background/src/EmptyBackground.v
|
||||
import { getGeoJsonFile } from '@/views/chart/components/js/util'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { setColorName } from '@/utils/utils'
|
||||
import GeometryEdit from './GeometryEdit.vue'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
|
||||
import request from '@/config/axios'
|
||||
const { wsCache } = useCache()
|
||||
const { t } = useI18n()
|
||||
const keyword = ref('')
|
||||
const treeData = ref([])
|
||||
const editor = ref()
|
||||
interface Tree {
|
||||
label: string
|
||||
children?: Tree[]
|
||||
}
|
||||
const areaTreeRef = ref(null)
|
||||
|
||||
const loading = ref(false)
|
||||
const selectedData = ref(null)
|
||||
|
||||
const handleNodeClick = async (data: Tree) => {
|
||||
@ -116,6 +136,29 @@ const handleNodeClick = async (data: Tree) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
const delHandler = data => {
|
||||
ElMessageBox.confirm('确定删除此节点吗', {
|
||||
confirmButtonType: 'danger',
|
||||
type: 'warning',
|
||||
confirmButtonText: t('common.delete'),
|
||||
cancelButtonText: t('dataset.cancel'),
|
||||
autofocus: false,
|
||||
showClose: false
|
||||
})
|
||||
.then(() => {
|
||||
const url = '/geometry/delete/' + data.id
|
||||
request.post({ url }).then(() => {
|
||||
if (selectedData.value?.id === data.id) {
|
||||
selectedData.value = null
|
||||
}
|
||||
ElMessage.success(t('common.delete_success'))
|
||||
loadTreeData(false)
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const filterResource = val => {
|
||||
areaTreeRef.value?.filter(val)
|
||||
}
|
||||
@ -125,18 +168,29 @@ const filterResourceNode = (value: string, data) => {
|
||||
return data.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
||||
}
|
||||
|
||||
const loadTreeData = () => {
|
||||
const loadTreeData = (cache?: boolean) => {
|
||||
const key = 'de-area-tree'
|
||||
const result = wsCache.get(key)
|
||||
if (result && cache) {
|
||||
treeData.value = result
|
||||
return
|
||||
}
|
||||
getWorldTree()
|
||||
.then(res => {
|
||||
const root = res.data
|
||||
treeData.value = [root]
|
||||
wsCache.set(key, treeData.value)
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
loadTreeData()
|
||||
const add = (pid?: string) => {
|
||||
editor?.value.edit(pid)
|
||||
}
|
||||
|
||||
loadTreeData(true)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@ -158,7 +212,6 @@ loadTreeData()
|
||||
line-height: 24px;
|
||||
}
|
||||
.add-icon-span {
|
||||
display: none;
|
||||
color: #3370ff;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
@ -250,5 +303,16 @@ loadTreeData()
|
||||
box-sizing: content-box;
|
||||
padding-right: 4px;
|
||||
overflow: hidden;
|
||||
justify-content: space-between;
|
||||
|
||||
.geo-operate-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.geo-operate-container {
|
||||
display: contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,248 @@
|
||||
<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,
|
||||
UploadRequestOptions,
|
||||
UploadProps
|
||||
} from 'element-plus-secondary'
|
||||
import request from '@/config/axios'
|
||||
import { GeometryFrom } from './interface'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache()
|
||||
const { t } = useI18n()
|
||||
const dialogVisible = ref(false)
|
||||
const loadingInstance = ref(null)
|
||||
const geoForm = ref<FormInstance>()
|
||||
const geoFile = ref()
|
||||
const fileName = ref()
|
||||
const state = reactive({
|
||||
form: reactive<GeometryFrom>({
|
||||
pid: null,
|
||||
code: null,
|
||||
name: null
|
||||
}),
|
||||
treeData: []
|
||||
})
|
||||
const treeProps = {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
disabled: 'readOnly'
|
||||
}
|
||||
|
||||
const rule = reactive<FormRules>({
|
||||
pid: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.require'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const edit = (pid?: string) => {
|
||||
const key = 'de-area-tree'
|
||||
state.treeData = wsCache.get(key)
|
||||
state.form.pid = pid
|
||||
state.form.code = null
|
||||
state.form.name = null
|
||||
geoFile.value = null
|
||||
fileName.value = null
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
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 formData = buildFormData(geoFile.value, param)
|
||||
showLoading()
|
||||
request
|
||||
.post({ url: '/geometry/save', data: formData, headersType: 'multipart/form-data;' })
|
||||
.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
|
||||
geoFile.value = null
|
||||
fileName.value = null
|
||||
formEl.resetFields()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetForm(geoForm.value)
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({ target: '.basic-info-drawer' })
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
const handleExceed: UploadProps['onExceed'] = () => {
|
||||
ElMessage.warning(t('userimport.exceedMsg'))
|
||||
}
|
||||
const handleError = () => {
|
||||
ElMessage.warning('执行失败请联系管理员')
|
||||
}
|
||||
const setFile = (options: UploadRequestOptions) => {
|
||||
geoFile.value = options.file
|
||||
fileName.value = options.file.name
|
||||
}
|
||||
const uploadValidate = file => {
|
||||
const suffix = file.name.substring(file.name.lastIndexOf('.') + 1)
|
||||
if (suffix !== 'json') {
|
||||
ElMessage.warning('只能上传json文件')
|
||||
return false
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 200) {
|
||||
ElMessage.warning('最大上传200M')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
const buildFormData = (file, param) => {
|
||||
const formData = new FormData()
|
||||
if (file) {
|
||||
formData.append('file', file)
|
||||
}
|
||||
formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }))
|
||||
return formData
|
||||
}
|
||||
defineExpose({
|
||||
edit
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer
|
||||
title="地理信息"
|
||||
v-model="dialogVisible"
|
||||
custom-class="basic-info-drawer"
|
||||
size="600px"
|
||||
direction="rtl"
|
||||
>
|
||||
<el-form
|
||||
ref="geoForm"
|
||||
require-asterisk-position="right"
|
||||
:model="state.form"
|
||||
:rules="rule"
|
||||
label-width="80px"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item label="上级区域" prop="pid">
|
||||
<el-tree-select
|
||||
class="map-tree-selector"
|
||||
node-key="id"
|
||||
v-model="state.form.pid"
|
||||
:props="treeProps"
|
||||
:data="state.treeData"
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
:placeholder="t('common.please_select')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="区域代码" prop="code">
|
||||
<el-input v-model="state.form.code" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="区域名称" prop="name">
|
||||
<el-input v-model="state.form.name" />
|
||||
</el-form-item>
|
||||
|
||||
<div class="geo-label-mask" />
|
||||
<el-form-item label="坐标文件">
|
||||
<el-upload
|
||||
class="upload-geo"
|
||||
action=""
|
||||
accept=".json"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="setFile"
|
||||
>
|
||||
<el-input :placeholder="t('userimport.placeholder')" readonly v-model="fileName">
|
||||
<template #suffix>
|
||||
<el-icon>
|
||||
<Icon name="icon_upload_outlined" />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #prefix>
|
||||
<el-icon v-if="!!fileName">
|
||||
<Icon name="de-json" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="resetForm(geoForm)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitForm(geoForm)">
|
||||
{{ t('commons.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.map-tree-selector {
|
||||
width: 100%;
|
||||
}
|
||||
.geo-btn-container {
|
||||
position: absolute;
|
||||
bottom: 33px;
|
||||
right: 0px;
|
||||
}
|
||||
.upload-geo {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
:deep(.ed-upload) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.geo-label-mask {
|
||||
position: absolute;
|
||||
width: calc(100% - 48px);
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,5 @@
|
||||
export interface GeometryFrom {
|
||||
pid?: string
|
||||
code?: string
|
||||
name?: string
|
||||
}
|
@ -517,7 +517,7 @@ initMarketTemplate()
|
||||
}
|
||||
|
||||
.template-market-dashboard {
|
||||
width: calc(100% - 384px);
|
||||
width: calc(100% - 376px);
|
||||
height: 100%;
|
||||
|
||||
.template-market {
|
||||
|
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit a0ed462213c8f274e09305cdc2ab0b238e9a2547
|
||||
Subproject commit e31c9212056eae3656aa534e3cc778d00001fe98
|
@ -66,6 +66,7 @@ curl http://127.0.0.1:9180/apisix/admin/services/10 -X PUT -H "X-API-KEY: $DE_AP
|
||||
"X-DE-TOKEN",
|
||||
"X-DE-LINK-TOKEN",
|
||||
"X-EMBEDDED-TOKEN",
|
||||
"X-DE-ASK-TOKEN",
|
||||
"Content-Type"
|
||||
],
|
||||
"request_method": "POST",
|
||||
|
@ -11,6 +11,7 @@ services:
|
||||
- ${DE_BASE}/dataease2.0/logs:/opt/dataease2.0/logs
|
||||
- ${DE_BASE}/dataease2.0/data/static-resource:/opt/dataease2.0/data/static-resource
|
||||
- ${DE_BASE}/dataease2.0/cache:/opt/dataease2.0/cache
|
||||
- ${DE_BASE}/dataease2.0/data/geo:/opt/dataease2.0/data/geo
|
||||
depends_on:
|
||||
DE_MYSQL_HOST:
|
||||
condition: service_healthy
|
||||
|
@ -11,7 +11,6 @@ spring:
|
||||
username: ${DE_MYSQL_USER}
|
||||
password: ${DE_MYSQL_PASSWORD}
|
||||
dataease:
|
||||
origin-list: localhost:8080,localhost:8100,localhost:9080
|
||||
apisix-api:
|
||||
domain: http://apisix:9180
|
||||
key: DE_APISIX_KEY
|
2
pom.xml
2
pom.xml
@ -25,7 +25,7 @@
|
||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||
<h2.version>1.4.199</h2.version>
|
||||
<knife4j.version>4.1.0</knife4j.version>
|
||||
<calcite-core.version>1.35.0</calcite-core.version>
|
||||
<calcite-core.version>1.36.0</calcite-core.version>
|
||||
<commons-dbcp2.version>2.6.0</commons-dbcp2.version>
|
||||
<antlr.version>3.5.2</antlr.version>
|
||||
<java-jwt.version>3.12.1</java-jwt.version>
|
||||
|
@ -0,0 +1,21 @@
|
||||
package io.dataease.api.lark.api;
|
||||
|
||||
import io.dataease.api.lark.dto.LarkTokenRequest;
|
||||
import io.dataease.api.lark.vo.LarkInfoVO;
|
||||
import io.dataease.api.lark.dto.LarkSettingCreator;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
public interface LarkApi {
|
||||
|
||||
@GetMapping("/info")
|
||||
LarkInfoVO info();
|
||||
|
||||
@PostMapping("/create")
|
||||
void save(@RequestBody LarkSettingCreator creator);
|
||||
|
||||
@PostMapping("/token")
|
||||
String larkToken(@RequestBody LarkTokenRequest request);
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package io.dataease.api.lark.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LarkSettingCreator implements Serializable {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
private String callBack;
|
||||
|
||||
private Boolean enable;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package io.dataease.api.lark.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LarkTokenRequest implements Serializable {
|
||||
|
||||
private String code;
|
||||
|
||||
private String state;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.dataease.api.lark.vo;
|
||||
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LarkInfoVO implements Serializable {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
private String callBack;
|
||||
|
||||
private Boolean enable = false;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.dataease.api.map;
|
||||
|
||||
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Tag(name = "地理信息")
|
||||
public interface GeoApi {
|
||||
|
||||
@PostMapping(value = "/save", consumes = {"multipart/form-data"})
|
||||
void saveMapGeo(@RequestPart("request") GeometryNodeCreator request, @RequestPart(value = "file") MultipartFile file);
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
void deleteGeo(@PathVariable("id") String id);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.dataease.api.map.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class GeometryNodeCreator implements Serializable {
|
||||
|
||||
private String code;
|
||||
|
||||
private String name;
|
||||
|
||||
private String pid;
|
||||
}
|
@ -18,6 +18,7 @@ public class AreaNode implements Serializable {
|
||||
private String level;
|
||||
private String name;
|
||||
private String pid;
|
||||
private boolean custom = false;
|
||||
/**
|
||||
* 国家代码
|
||||
*/
|
||||
|
@ -1,11 +1,14 @@
|
||||
package io.dataease.api.system;
|
||||
|
||||
import io.dataease.api.system.request.OnlineMapEditor;
|
||||
import io.dataease.api.system.vo.SettingItemVO;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SysParameterApi {
|
||||
|
||||
@GetMapping("/singleVal/{key}")
|
||||
@ -17,4 +20,10 @@ public interface SysParameterApi {
|
||||
@GetMapping("/queryOnlineMap")
|
||||
String queryOnlineMap();
|
||||
|
||||
@GetMapping("basic/query")
|
||||
List<SettingItemVO> queryBasicSetting();
|
||||
|
||||
@PostMapping("/basic/save")
|
||||
void saveBasicSetting(@RequestBody List<SettingItemVO> settingItemVOS);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package io.dataease.api.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class SettingItemVO implements Serializable {
|
||||
|
||||
private String pkey;
|
||||
|
||||
private String pval;
|
||||
|
||||
private String type;
|
||||
|
||||
private Integer sort;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.dataease.api.permissions.apikey.api;
|
||||
|
||||
import io.dataease.api.permissions.apikey.dto.ApikeyEnableEditor;
|
||||
import io.dataease.api.permissions.apikey.vo.ApiKeyVO;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ApiKeyApi {
|
||||
|
||||
@PostMapping("/generate")
|
||||
void generate();
|
||||
|
||||
@GetMapping("/query")
|
||||
List<ApiKeyVO> query();
|
||||
|
||||
@PostMapping("/switch")
|
||||
void switchEnable(@RequestBody ApikeyEnableEditor editor);
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
void delete(@PathVariable("id") Long id);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package io.dataease.api.permissions.apikey.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class ApikeyEnableEditor implements Serializable {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Boolean enable = false;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.dataease.api.permissions.apikey.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class ApiKeyVO implements Serializable {
|
||||
|
||||
@JsonSerialize(using= ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
private String accessKey;
|
||||
|
||||
private String accessSecret;
|
||||
|
||||
private Boolean enable;
|
||||
|
||||
private Long createTime;
|
||||
}
|
@ -27,4 +27,7 @@ public interface EmbeddedApi {
|
||||
|
||||
@PostMapping("/reset")
|
||||
void reset(@RequestBody EmbeddedResetRequest request);
|
||||
|
||||
@GetMapping("/domainList")
|
||||
List<String> domainList();
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.dataease.api.permissions.setting.api;
|
||||
|
||||
import io.dataease.api.permissions.setting.vo.PerSettingItemVO;
|
||||
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;
|
||||
|
||||
public interface PerSettingApi {
|
||||
|
||||
@GetMapping("/basic/query")
|
||||
List<PerSettingItemVO> basicSetting();
|
||||
|
||||
@PostMapping("/baisc/save")
|
||||
void saveBasic(@RequestBody List<Object> settings);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.dataease.api.permissions.setting.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class PerSettingItemVO implements Serializable {
|
||||
|
||||
private String pkey;
|
||||
|
||||
private String pval;
|
||||
|
||||
private String type;
|
||||
|
||||
private Integer sort;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package io.dataease.auth.interceptor;
|
||||
|
||||
import io.dataease.constant.AuthConstant;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@ -13,12 +14,16 @@ import java.util.List;
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("#{'${dataease.origin-list}'.split(',')}")
|
||||
@Resource(name = "deCorsInterceptor")
|
||||
private CorsInterceptor corsInterceptor;
|
||||
|
||||
@Value("#{'${dataease.origin-list:http://127.0.0.1:8100}'.split(',')}")
|
||||
private List<String> originList;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new CorsInterceptor(originList)).addPathPatterns("/**");
|
||||
corsInterceptor.addOriginList(originList);
|
||||
registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,27 +1,73 @@
|
||||
package io.dataease.auth.interceptor;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import io.dataease.utils.CommonBeanFactory;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component("deCorsInterceptor")
|
||||
public class CorsInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
private List<String> originList;
|
||||
private final List<String> originList;
|
||||
|
||||
private final List<String> busiOriginList = new ArrayList<>();
|
||||
|
||||
private Class<?> aClass;
|
||||
|
||||
private Object bean;
|
||||
|
||||
|
||||
public CorsInterceptor(List<String> originList) {
|
||||
this.originList = originList;
|
||||
}
|
||||
|
||||
public void addOriginList(List<String> list) {
|
||||
List<String> strings = list.stream().filter(item -> !originList.contains(item)).toList();
|
||||
originList.addAll(strings);
|
||||
}
|
||||
|
||||
|
||||
public void addOriginList() {
|
||||
String className = "io.dataease.api.permissions.embedded.api.EmbeddedApi";
|
||||
String methodName = "domainList";
|
||||
if (ObjectUtils.isEmpty(aClass)) {
|
||||
try {
|
||||
aClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isEmpty(bean)) {
|
||||
bean = CommonBeanFactory.getBean(aClass);
|
||||
}
|
||||
if (ObjectUtils.isNotEmpty(bean)) {
|
||||
Object result = ReflectUtil.invoke(bean, methodName);
|
||||
if (ObjectUtils.isNotEmpty(result)) {
|
||||
List<String> list = (List<String>) result;
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
List<String> strings = list.stream().filter(item -> !busiOriginList.contains(item)).toList();
|
||||
busiOriginList.addAll(strings);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
addOriginList();
|
||||
String origin = request.getHeader("Origin");
|
||||
boolean embedded = StringUtils.startsWithAny(request.getRequestURI(), "/assets/", "/js/");
|
||||
if ((StringUtils.isNotBlank(origin) && originList.contains(origin)) || embedded) {
|
||||
if ((StringUtils.isNotBlank(origin) && originList.contains(origin)) || busiOriginList.contains(origin) || embedded) {
|
||||
response.setHeader("Access-Control-Allow-Origin", embedded ? "*" : origin);
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS");
|
||||
|
@ -18,6 +18,7 @@ public class AuthConstant {
|
||||
public final static String USER_IMPORT_ERROR_KEY = "USER-IMPORT-ERROR-KEY";
|
||||
|
||||
public final static String LINK_TOKEN_KEY = "X-DE-LINK-TOKEN";
|
||||
public final static String ASK_TOKEN_KEY = "X-DE-ASK-TOKEN";
|
||||
|
||||
public final static String DE_EXECUTE_VERSION = "X-DE-EXECUTE-VERSION";
|
||||
|
||||
|
@ -16,8 +16,10 @@ public class StaticResourceConstants {
|
||||
public static String WORK_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "static-resource" + FILE_SEPARATOR;
|
||||
|
||||
public static String MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "map";
|
||||
public static String CUSTOM_MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "geo";
|
||||
|
||||
public static String MAP_URL = "/map";
|
||||
public static String GEO_URL = "/geo";
|
||||
|
||||
/**
|
||||
* Upload prefix.
|
||||
|
@ -0,0 +1,6 @@
|
||||
package io.dataease.constant;
|
||||
|
||||
public class XpackSettingConstants {
|
||||
|
||||
public static final String AUTO_CREATE_USER = "basic.autoCreateUser";
|
||||
}
|
36
sdk/common/src/main/java/io/dataease/utils/AesUtils.java
Normal file
36
sdk/common/src/main/java/io/dataease/utils/AesUtils.java
Normal file
@ -0,0 +1,36 @@
|
||||
package io.dataease.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class AesUtils {
|
||||
|
||||
public static String aesDecrypt(String src, String secretKey, String iv) {
|
||||
if (StringUtils.isBlank(secretKey)) {
|
||||
throw new RuntimeException("secretKey is empty");
|
||||
}
|
||||
try {
|
||||
byte[] raw = secretKey.getBytes(UTF_8);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
IvParameterSpec iv1 = new IvParameterSpec(iv.getBytes());
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv1);
|
||||
byte[] encrypted1 = Base64.decodeBase64(src);
|
||||
byte[] original = cipher.doFinal(encrypted1);
|
||||
return new String(original, UTF_8);
|
||||
} catch (BadPaddingException | IllegalBlockSizeException e) {
|
||||
// 解密的原字符串为非加密字符串,则直接返回原字符串
|
||||
return src;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("decrypt error,please check parameters", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -148,7 +148,7 @@ public class HttpClientUtil {
|
||||
public static String post(String url, String json, HttpClientConfig config) {
|
||||
CloseableHttpClient httpClient = null;
|
||||
try {
|
||||
buildHttpClient(url);
|
||||
httpClient = buildHttpClient(url);
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
if (config == null) {
|
||||
config = new HttpClientConfig();
|
||||
|
@ -0,0 +1,14 @@
|
||||
package io.dataease.utils;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import io.dataease.constant.XpackSettingConstants;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SystemSettingUtils {
|
||||
|
||||
public static boolean xpackSetting(String pkey) {
|
||||
List<String> xpackSettingList = ListUtil.toList(XpackSettingConstants.AUTO_CREATE_USER);
|
||||
return xpackSettingList.contains(pkey);
|
||||
}
|
||||
}
|
@ -19,6 +19,8 @@ public class WhitelistUtils {
|
||||
"/swagger-resources",
|
||||
"/doc.html",
|
||||
"/panel.html",
|
||||
"/lark/info",
|
||||
"/lark/token",
|
||||
"/setting/authentication/status",
|
||||
"/");
|
||||
|
||||
@ -34,6 +36,7 @@ public class WhitelistUtils {
|
||||
|| StringUtils.startsWithAny(requestURI, "/static-resource/")
|
||||
|| StringUtils.startsWithAny(requestURI, "/share/proxyInfo")
|
||||
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content/")
|
||||
|| StringUtils.startsWithAny(requestURI, "/geo/")
|
||||
|| StringUtils.startsWithAny(requestURI, "/map/");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user