refactor: 解决冲突

This commit is contained in:
taojinlong 2021-09-15 18:24:28 +08:00
commit 5b86ad0907
70 changed files with 1702 additions and 339 deletions

1
.gitignore vendored
View File

@ -40,6 +40,7 @@ yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.lh
.idea
.vscode
*.suo

View File

@ -252,11 +252,11 @@
<version>20171018</version>
</dependency>
<dependency>
<!--<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependency>-->
<!-- 反射工具包 -->
<dependency>
<groupId>net.oneandone.reflections8</groupId>
@ -337,10 +337,10 @@
<version>5.7.4</version>
</dependency>
<dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
</dependency>-->
<dependency>
<groupId>ru.yandex.clickhouse</groupId>

View File

@ -4,9 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.auth.api.dto.CurrentUserDto;
import io.dataease.auth.api.dto.LoginDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -46,4 +44,9 @@ public interface AuthApi {
@PostMapping("/isOpenLdap")
boolean isOpenLdap();
@ApiOperation("是否开启oidc")
@PostMapping("/isOpenOidc")
boolean isOpenOidc();
}

View File

@ -13,6 +13,8 @@ public class TokenInfo implements Serializable {
private Long userId;
/* private String idToken; */
public String format(){
return username + "," +userId;
}

View File

@ -4,7 +4,6 @@ import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

View File

@ -21,17 +21,19 @@ import io.dataease.plugins.util.PluginUtils;
import io.dataease.plugins.xpack.ldap.dto.request.LdapValidateRequest;
import io.dataease.plugins.xpack.ldap.dto.response.ValidateResult;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@RestController
public class AuthServer implements AuthApi {
@ -113,6 +115,16 @@ public class AuthServer implements AuthApi {
@Override
public String logout() {
String token = ServletUtils.getToken();
if (isOpenOidc()) {
HttpServletRequest request = ServletUtils.request();
String idToken = request.getHeader("IdToken");
if (StringUtils.isNotBlank(idToken)) {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
oidcXpackService.logout(idToken);
}
}
if (StringUtils.isEmpty(token) || StringUtils.equals("null", token) || StringUtils.equals("undefined", token)) {
return "success";
}
@ -144,6 +156,15 @@ public class AuthServer implements AuthApi {
return open;
}
@Override
public boolean isOpenOidc() {
Boolean licValid = PluginUtils.licValid();
if(!licValid) return false;
return authUserService.supportOidc();
}
/*@Override
public Boolean isLogin() {
return null;

View File

@ -13,6 +13,8 @@ public interface AuthUserService {
SysUserEntity getUserByName(String username);
SysUserEntity getUserBySub(String sub);
List<String> roles(Long userId);
List<String> permissions(Long userId);
@ -23,6 +25,8 @@ public interface AuthUserService {
boolean supportLdap();
Boolean supportOidc();
}

View File

@ -10,6 +10,8 @@ import io.dataease.commons.constants.AuthConstants;
import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
@ -19,6 +21,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@ -49,6 +52,11 @@ public class AuthUserServiceImpl implements AuthUserService {
return authMapper.findUserByName(username);
}
@Override
public SysUserEntity getUserBySub(String sub) {
return authMapper.findUserBySub(sub);
}
@Override
public List<String> roles(Long userId){
return authMapper.roleCodes(userId);
@ -107,8 +115,21 @@ public class AuthUserServiceImpl implements AuthUserService {
@Override
public boolean supportLdap() {
Map<String, LdapXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((LdapXpackService.class));
if(beansOfType.keySet().size() == 0) return false;
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
if(ObjectUtils.isEmpty(ldapXpackService)) return false;
return ldapXpackService.isOpen();
}
@Override
public Boolean supportOidc() {
Map<String, OidcXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((OidcXpackService.class));
if(beansOfType.keySet().size() == 0) return false;
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
if(ObjectUtils.isEmpty(oidcXpackService)) return false;
return oidcXpackService.isSuuportOIDC();
}
}

View File

@ -13,7 +13,6 @@ import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

View File

@ -56,9 +56,15 @@ public class ShiroServiceImpl implements ShiroService {
// filterChainDefinitionMap.put("/axios.map", ANON);
filterChainDefinitionMap.put("/api/auth/login", ANON);
filterChainDefinitionMap.put("/api/auth/logout", ANON);
// filterChainDefinitionMap.put("/api/auth/logout", ANON);
filterChainDefinitionMap.put("/api/auth/validateName", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenLdap", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenOidc", ANON);
filterChainDefinitionMap.put("/api/pluginCommon/component/*", ANON);
filterChainDefinitionMap.put("/plugin/oidc/authInfo", ANON);
filterChainDefinitionMap.put("/sso/callBack*", ANON);
filterChainDefinitionMap.put("/unauth", ANON);
filterChainDefinitionMap.put("/display/**", ANON);
filterChainDefinitionMap.put("/tokenExpired", ANON);

View File

@ -2,16 +2,18 @@ package io.dataease.auth.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.entity.TokenInfo.TokenInfoBuilder;
import io.dataease.commons.utils.CommonBeanFactory;
import io.dataease.exception.DataEaseException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import java.util.Date;
@ -34,10 +36,13 @@ public class JWTUtils {
*/
public static boolean verify(String token, TokenInfo tokenInfo, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
Verification verification = JWT.require(algorithm)
.withClaim("username", tokenInfo.getUsername())
.withClaim("userId", tokenInfo.getUserId())
.build();
.withClaim("userId", tokenInfo.getUserId());
/* if (StringUtils.isNotBlank(tokenInfo.getIdToken())) {
verification.withClaim("idToken", tokenInfo.getIdToken());
} */
JWTVerifier verifier = verification.build();
verifier.verify(token);
return true;
}
@ -50,10 +55,15 @@ public class JWTUtils {
DecodedJWT jwt = JWT.decode(token);
String username = jwt.getClaim("username").asString();
Long userId = jwt.getClaim("userId").asLong();
// String idToken = jwt.getClaim("idToken").asString();
if (StringUtils.isEmpty(username) || ObjectUtils.isEmpty(userId) ){
DataEaseException.throwException("token格式错误");
}
TokenInfo tokenInfo = TokenInfo.builder().username(username).userId(userId).build();
TokenInfoBuilder tokenInfoBuilder = TokenInfo.builder().username(username).userId(userId);
/* if (StringUtils.isNotBlank(idToken)) {
tokenInfoBuilder.idToken(idToken);
} */
TokenInfo tokenInfo = tokenInfoBuilder.build();
return tokenInfo;
}
@ -107,12 +117,14 @@ public class JWTUtils {
try {
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
Builder builder = JWT.create()
.withClaim("username", tokenInfo.getUsername())
.withClaim("userId", tokenInfo.getUserId())
.withExpiresAt(date)
.sign(algorithm);
.withClaim("userId", tokenInfo.getUserId());
/* if (StringUtils.isNotBlank(tokenInfo.getIdToken())) {
builder.withClaim("idToken", tokenInfo.getIdToken());
} */
return builder.withExpiresAt(date).sign(algorithm);
} catch (Exception e) {
return null;
}

View File

@ -39,5 +39,7 @@ public class SysUser implements Serializable {
private Integer from;
private String sub;
private static final long serialVersionUID = 1L;
}

View File

@ -1213,6 +1213,76 @@ public class SysUserExample {
addCriterion("`from` not between", value1, value2, "from");
return (Criteria) this;
}
public Criteria andSubIsNull() {
addCriterion("sub is null");
return (Criteria) this;
}
public Criteria andSubIsNotNull() {
addCriterion("sub is not null");
return (Criteria) this;
}
public Criteria andSubEqualTo(String value) {
addCriterion("sub =", value, "sub");
return (Criteria) this;
}
public Criteria andSubNotEqualTo(String value) {
addCriterion("sub <>", value, "sub");
return (Criteria) this;
}
public Criteria andSubGreaterThan(String value) {
addCriterion("sub >", value, "sub");
return (Criteria) this;
}
public Criteria andSubGreaterThanOrEqualTo(String value) {
addCriterion("sub >=", value, "sub");
return (Criteria) this;
}
public Criteria andSubLessThan(String value) {
addCriterion("sub <", value, "sub");
return (Criteria) this;
}
public Criteria andSubLessThanOrEqualTo(String value) {
addCriterion("sub <=", value, "sub");
return (Criteria) this;
}
public Criteria andSubLike(String value) {
addCriterion("sub like", value, "sub");
return (Criteria) this;
}
public Criteria andSubNotLike(String value) {
addCriterion("sub not like", value, "sub");
return (Criteria) this;
}
public Criteria andSubIn(List<String> values) {
addCriterion("sub in", values, "sub");
return (Criteria) this;
}
public Criteria andSubNotIn(List<String> values) {
addCriterion("sub not in", values, "sub");
return (Criteria) this;
}
public Criteria andSubBetween(String value1, String value2) {
addCriterion("sub between", value1, value2, "sub");
return (Criteria) this;
}
public Criteria andSubNotBetween(String value1, String value2) {
addCriterion("sub not between", value1, value2, "sub");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -19,6 +19,7 @@
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="language" jdbcType="VARCHAR" property="language" />
<result column="from" jdbcType="INTEGER" property="from" />
<result column="sub" jdbcType="VARCHAR" property="sub" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -81,7 +82,7 @@
<sql id="Base_Column_List">
user_id, dept_id, username, nick_name, gender, phone, email, `password`, is_admin,
enabled, create_by, update_by, pwd_reset_time, create_time, update_time, `language`,
`from`
`from`, sub
</sql>
<select id="selectByExample" parameterType="io.dataease.base.domain.SysUserExample" resultMap="BaseResultMap">
select
@ -119,13 +120,15 @@
email, `password`, is_admin,
enabled, create_by, update_by,
pwd_reset_time, create_time, update_time,
`language`, `from`)
`language`, `from`, sub
)
values (#{userId,jdbcType=BIGINT}, #{deptId,jdbcType=BIGINT}, #{username,jdbcType=VARCHAR},
#{nickName,jdbcType=VARCHAR}, #{gender,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{isAdmin,jdbcType=BIT},
#{enabled,jdbcType=BIGINT}, #{createBy,jdbcType=VARCHAR}, #{updateBy,jdbcType=VARCHAR},
#{pwdResetTime,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{language,jdbcType=VARCHAR}, #{from,jdbcType=INTEGER})
#{language,jdbcType=VARCHAR}, #{from,jdbcType=INTEGER}, #{sub,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.dataease.base.domain.SysUser">
insert into sys_user
@ -181,6 +184,9 @@
<if test="from != null">
`from`,
</if>
<if test="sub != null">
sub,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">
@ -234,6 +240,9 @@
<if test="from != null">
#{from,jdbcType=INTEGER},
</if>
<if test="sub != null">
#{sub,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.dataease.base.domain.SysUserExample" resultType="java.lang.Long">
@ -296,6 +305,9 @@
<if test="record.from != null">
`from` = #{record.from,jdbcType=INTEGER},
</if>
<if test="record.sub != null">
sub = #{record.sub,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -319,7 +331,8 @@
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT},
`language` = #{record.language,jdbcType=VARCHAR},
`from` = #{record.from,jdbcType=INTEGER}
`from` = #{record.from,jdbcType=INTEGER},
sub = #{record.sub,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -375,6 +388,9 @@
<if test="from != null">
`from` = #{from,jdbcType=INTEGER},
</if>
<if test="sub != null">
sub = #{sub,jdbcType=VARCHAR},
</if>
</set>
where user_id = #{userId,jdbcType=BIGINT}
</update>
@ -395,7 +411,8 @@
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT},
`language` = #{language,jdbcType=VARCHAR},
`from` = #{from,jdbcType=INTEGER}
`from` = #{from,jdbcType=INTEGER},
sub = #{sub,jdbcType=VARCHAR}
where user_id = #{userId,jdbcType=BIGINT}
</update>
</mapper>

View File

@ -25,6 +25,8 @@ public interface AuthMapper {
SysUserEntity findUserByName(@Param("username") String username);
SysUserEntity findUserBySub(@Param("sub") String sub);
List<CurrentRoleDto> roles(@Param("userId") Long userId);

View File

@ -28,6 +28,10 @@
select user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin from sys_user where username = #{username}
</select>
<select id="findUserBySub" resultMap="baseMap">
select user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin from sys_user where sub = #{sub}
</select>
<select id="roleCodes" resultType="String">
select r.id from sys_role r
left join sys_users_roles sur on sur.role_id = r.role_id

View File

@ -6,6 +6,7 @@ public class AuthConstants {
public final static String USER_CACHE_NAME = "users_info";
public final static String USER_ROLE_CACHE_NAME = "users_roles_info";
public final static String USER_PERMISSION_CACHE_NAME = "users_permissions_info";
public final static String ID_TOKEN_KEY = "IdToken";
}

View File

@ -45,9 +45,12 @@ public class DefaultLicenseService {
}
return f2CLicenseResponse;
}catch (Exception e){
e.printStackTrace();
return F2CLicenseResponse.invalid(e.getMessage());
LogUtil.error(e.getMessage());
// e.printStackTrace();
// return F2CLicenseResponse.invalid(e.getMessage());
return F2CLicenseResponse.noRecord();
}
}

View File

@ -95,7 +95,7 @@ public class DateUtils {
}
public static void main(String[] args) throws Exception {
/* public static void main(String[] args) throws Exception {
// System.out.println("start:");
Date paramTime = getTime(getTimeString(new Long("1607672440731")));
@ -110,7 +110,7 @@ public class DateUtils {
// System.out.println(getTimeString(--countTimeLong));
}
} */
/**

View File

@ -68,8 +68,8 @@ public class ExcelReaderUtil {
}
}
public static void main(String[] args) throws Exception {
/* public static void main(String[] args) throws Exception {
String file ="Metersphere_case_DataEase功能用例.xlsx";
ExcelReaderUtil.readExcel(file, new FileInputStream("/Users/taojinlong/Desktop/" + file));
}
} */
}

View File

@ -2,6 +2,7 @@ package io.dataease.plugins.config;
import io.dataease.base.domain.MyPlugin;
import io.dataease.plugins.loader.ClassloaderResponsity;
import io.dataease.plugins.loader.ControllerLoader;
import io.dataease.plugins.loader.ModuleClassLoader;
import io.dataease.plugins.loader.MybatisLoader;
import org.springframework.beans.factory.annotation.Autowired;
@ -19,6 +20,9 @@ public class LoadjarUtil {
@Autowired
private MybatisLoader mybatisLoader;
@Autowired
private ControllerLoader controllerLoader;
public List<?> loadJar(String jarPath, MyPlugin myPlugin) throws Exception{
File jar = new File(jarPath);
URI uri = jar.toURI();
@ -34,6 +38,10 @@ public class LoadjarUtil {
Thread.currentThread().setContextClassLoader(classLoader);
classLoader.initBean();
mybatisLoader.loadMybatis(myPlugin);
List<String> controllers = classLoader.getRegisteredController();
controllerLoader.registerController(controllers);
ClassloaderResponsity.getInstance().addClassLoader(moduleName,classLoader);

View File

@ -0,0 +1,95 @@
package io.dataease.plugins.loader;
import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.config.SpringContextUtil;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.List;
@Component
public class ControllerLoader {
/**
* 去掉Controller的Mapping
* @param controllerBeanName
*/
private void unregisterController(String controllerBeanName){
final RequestMappingHandlerMapping requestMappingHandlerMapping=(RequestMappingHandlerMapping)SpringContextUtil.getBean("requestMappingHandlerMapping");
if(requestMappingHandlerMapping!=null){
String handler=controllerBeanName;
Object controller= SpringContextUtil.getBean(handler);
if(controller==null){
return;
}
final Class<?> targetClass=controller.getClass();
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
try {
Method createMappingMethod = RequestMappingHandlerMapping.class.
getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
createMappingMethod.setAccessible(true);
RequestMappingInfo requestMappingInfo =(RequestMappingInfo)
createMappingMethod.invoke(requestMappingHandlerMapping,specificMethod,targetClass);
if(requestMappingInfo != null) {
requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
}
}catch (Exception e){
e.printStackTrace();
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
}
/**
* 注册Controller
* @param controllerBeanName
* @throws Exception
*/
private void registerController(String controllerBeanName) throws Exception{
final RequestMappingHandlerMapping requestMappingHandlerMapping=(RequestMappingHandlerMapping) SpringContextUtil.getBean("requestMappingHandlerMapping");
if(requestMappingHandlerMapping!=null){
String handler=controllerBeanName;
Object controller= SpringContextUtil.getBean(handler);
if(controller==null){
return;
}
unregisterController(controllerBeanName);
//注册Controller
Method method=requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().getDeclaredMethod("detectHandlerMethods",Object.class);
method.setAccessible(true);
method.invoke(requestMappingHandlerMapping,handler);
}
}
public void registerController(List<String> beanNames) {
beanNames.forEach(name -> {
try {
registerController(name);
} catch (Exception e) {
// e.printStackTrace();
LogUtil.error(e);
}
});
}
}

View File

@ -7,10 +7,14 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -35,6 +39,8 @@ public class ModuleClassLoader extends URLClassLoader {
//需要注册的spring bean的name集合
private List<String> registeredBean = new ArrayList<>();
private List<String> registeredController = new ArrayList<>();
//构造
public ModuleClassLoader(URL[] urls, ClassLoader parent) {
@ -150,8 +156,12 @@ public class ModuleClassLoader extends URLClassLoader {
beanName = StringUtils.uncapitalize(beanName);
SpringContextUtil.getBeanFactory().registerBeanDefinition(beanName,beanDefinition);
if (isHandler(cla)) {
registeredController.add(beanName);
}
registeredBean.add(beanName);
// System.out.println("注册bean:"+beanName);
}
}
@ -164,6 +174,10 @@ public class ModuleClassLoader extends URLClassLoader {
return registeredBean;
}
public List<String> getRegisteredController() {
return registeredController;
}
/**
* 方法描述 判断class对象是否带有spring的注解
@ -184,6 +198,9 @@ public class ModuleClassLoader extends URLClassLoader {
if( Modifier.isAbstract(cla.getModifiers())){
return false;
}
if (isHandler(cla)) {
return true;
}
if(cla.getAnnotation(Component.class)!=null){
return true;
@ -194,8 +211,15 @@ public class ModuleClassLoader extends URLClassLoader {
if(cla.getAnnotation(Service.class)!=null){
return true;
}
if(cla.getAnnotation(Service.class)!=null){
return true;
}
return false;
}
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
}

View File

@ -2,6 +2,7 @@ package io.dataease.plugins.server;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.plugins.common.dto.PluginSysMenu;
import io.dataease.plugins.common.service.PluginComponentService;
import io.dataease.plugins.common.service.PluginMenuService;
import io.dataease.plugins.config.SpringContextUtil;
import org.springframework.web.bind.annotation.GetMapping;
@ -9,7 +10,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
public class PluginCommonServer {
@GetMapping("/async/{menuId}")
public void componentInfo(@PathVariable Long menuId) {
public void menuInfo(@PathVariable Long menuId) {
Map<String, PluginMenuService> pluginMenuServiceMap = SpringContextUtil.getApplicationContext().getBeansOfType(PluginMenuService.class);
pluginMenuServiceMap.values().stream().forEach(service -> {
AtomicReference<PluginSysMenu> atomicReference = new AtomicReference<>();
@ -65,4 +65,41 @@ public class PluginCommonServer {
return;
});
}
@GetMapping("/component/{componentName}")
public void componentInfo(@PathVariable String componentName) {
Map<String, PluginComponentService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(PluginComponentService.class);
beansOfType.values().stream().forEach(service -> {
List<String> components = service.components();
if (components.contains(componentName)) {
HttpServletResponse response = ServletUtils.response();
BufferedInputStream bis = null;
InputStream inputStream = null;
OutputStream os = null; //输出流
try{
inputStream = service.vueResource(componentName);
byte[] buffer = new byte[1024];
os = response.getOutputStream();
bis = new BufferedInputStream(inputStream);
int i = bis.read(buffer);
while(i != -1){
os.write(buffer, 0, i);
i = bis.read(buffer);
}
os.flush();
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
bis.close();
inputStream.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return;
}
});
}
}

View File

@ -0,0 +1,85 @@
package io.dataease.plugins.server;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import io.dataease.auth.entity.SysUserEntity;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.service.AuthUserService;
import io.dataease.auth.util.JWTUtils;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.utils.CodingUtil;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.oidc.dto.SSOToken;
import io.dataease.plugins.xpack.oidc.dto.SSOUserInfo;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import io.dataease.service.sys.SysUserService;
@RequestMapping("/sso")
@Controller
public class SSOServer {
@Autowired
private AuthUserService authUserService;
@Autowired
private SysUserService sysUserService;
@GetMapping("/callBack")
public ModelAndView callBack(@RequestParam("code") String code, @RequestParam("state") String state) {
Map<String, OidcXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((OidcXpackService.class));
if(beansOfType.keySet().size() == 0) {
DEException.throwException("缺少oidc插件");
}
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
Boolean suuportOIDC = oidcXpackService.isSuuportOIDC();
if (!suuportOIDC) {
DEException.throwException("未开启oidc");
}
Map<String, String> config = config(oidcXpackService);
SSOToken ssoToken = oidcXpackService.requestSsoToken(config, code, state);
SSOUserInfo ssoUserInfo = oidcXpackService.requestUserInfo(config, ssoToken.getAccessToken());
SysUserEntity sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub());
if(null == sysUserEntity){
sysUserService.saveOIDCUser(ssoUserInfo);
sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub());
}
TokenInfo tokenInfo = TokenInfo.builder().userId(sysUserEntity.getUserId()).username(sysUserEntity.getUsername()).build();
String realPwd = CodingUtil.md5(sysUserService.defaultPWD());
String token = JWTUtils.sign(tokenInfo, realPwd);
ServletUtils.setToken(token);
HttpServletResponse response = ServletUtils.response();
Cookie cookie_token = new Cookie("Authorization", token);cookie_token.setPath("/");
Cookie cookie_id_token = new Cookie("IdToken", ssoToken.getIdToken());cookie_id_token.setPath("/");
Cookie cookie_ac_token = new Cookie("AccessToken", ssoToken.getAccessToken());cookie_ac_token.setPath("/");
response.addCookie(cookie_token);
response.addCookie(cookie_id_token);
response.addCookie(cookie_ac_token);
ModelAndView modelAndView = new ModelAndView("redirect:/");
return modelAndView;
}
private Map<String, String> config(OidcXpackService oidcXpackService) {
List<SysSettingDto> sysSettingDtos = oidcXpackService.oidcSettings();
Map<String, String> config = sysSettingDtos.stream().collect(Collectors.toMap(SysSettingDto::getParamKey, SysSettingDto::getParamValue));
return config;
}
}

View File

@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
@RequestMapping("/api/ldap")
@RequestMapping("/plugin/ldap")
@RestController
public class XLdapServer {

View File

@ -0,0 +1,59 @@
package io.dataease.plugins.server;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequestMapping("/plugin/oidc")
@RestController
public class XOidcServer {
@PostMapping("/info")
public List<SysSettingDto> getOidcInfo() {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
return oidcXpackService.oidcSettings();
}
@PostMapping("/save")
public void save(@RequestBody List<SysSettingDto> settings) {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
oidcXpackService.save(settings);
}
@PostMapping(value="/authInfo")
public Map<String, Object> authInfo() {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
Map<String, Object> result = new HashMap<String, Object>();
List<SysSettingDto> oidcSettings = oidcXpackService.oidcSettings();
Map<String, String> authParam = new HashMap<>();
authParam.put("response_type", "code");
authParam.put("state", "state");
// authParam.put("redirect_uri", "http://localhost:9528");
oidcSettings.forEach(param -> {
if(StringUtils.isNotBlank(param.getParamKey())) {
if (StringUtils.equals(param.getParamKey(), "oidc.authEndpoint")) {
result.put("authEndpoint", param.getParamValue());
}
if (StringUtils.equals(param.getParamKey(), "oidc.scope")) {
authParam.put("scope", param.getParamValue());
}
if (StringUtils.equals(param.getParamKey(), "oidc.clientId")) {
authParam.put("client_id", param.getParamValue());
}
}
});
result.put("authParam", authParam);
return result;
}
}

View File

@ -208,7 +208,9 @@ public class ChartViewService {
customFilter.addAll(collect);
}
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType())) {
if (StringUtils.equalsIgnoreCase("text", view.getType())
|| StringUtils.equalsIgnoreCase("gauge", view.getType())
|| StringUtils.equalsIgnoreCase("liquid", view.getType())) {
xAxis = new ArrayList<>();
if (CollectionUtils.isEmpty(yAxis)) {
ChartViewDTO dto = new ChartViewDTO();
@ -327,7 +329,7 @@ public class ChartViewService {
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
if (StringUtils.equalsIgnoreCase(table.getType(), "db")) {
datasourceRequest.setTable(dataTableInfoDTO.getTable());
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType())) {
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
datasourceRequest.setQuery(qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, customFilter, extFilterList));
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
datasourceRequest.setQuery(qp.getSQLStack(dataTableInfoDTO.getTable(), xAxis, yAxis, customFilter, extFilterList, extStack, ds));
@ -339,7 +341,7 @@ public class ChartViewService {
datasourceRequest.setQuery(qp.getSQL(dataTableInfoDTO.getTable(), xAxis, yAxis, customFilter, extFilterList, ds));
}
} else if (StringUtils.equalsIgnoreCase(table.getType(), "sql")) {
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType())) {
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
datasourceRequest.setQuery(qp.getSQLSummaryAsTmp(dataTableInfoDTO.getSql(), yAxis, customFilter, extFilterList));
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
datasourceRequest.setQuery(qp.getSQLAsTmpStack(dataTableInfoDTO.getSql(), xAxis, yAxis, customFilter, extFilterList, extStack));
@ -354,7 +356,7 @@ public class ChartViewService {
DataTableInfoDTO dt = new Gson().fromJson(table.getInfo(), DataTableInfoDTO.class);
List<DataSetTableUnionDTO> list = dataSetTableUnionService.listByTableId(dt.getList().get(0).getTableId());
String sql = dataSetTableService.getCustomSQLDatasource(dt, list, ds);
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType())) {
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
datasourceRequest.setQuery(qp.getSQLSummaryAsTmp(sql, yAxis, customFilter, extFilterList));
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
datasourceRequest.setQuery(qp.getSQLAsTmpStack(sql, xAxis, yAxis, customFilter, extFilterList, extStack));
@ -386,7 +388,7 @@ public class ChartViewService {
String tableName = "ds_" + table.getId().replaceAll("-", "_");
datasourceRequest.setTable(tableName);
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType())) {
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
datasourceRequest.setQuery(qp.getSQLSummary(tableName, yAxis, customFilter, extFilterList));
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
datasourceRequest.setQuery(qp.getSQLStack(tableName, xAxis, yAxis, customFilter, extFilterList, extStack, ds));
@ -458,6 +460,9 @@ public class ChartViewService {
map.putAll(mapChart);
map.putAll(mapTableNormal);
List<DatasetTableField> sourceFields = dataSetTableFieldsService.getFieldsByTableId(view.getTableId());
map.put("sourceFields",sourceFields);
ChartViewDTO dto = new ChartViewDTO();
BeanUtils.copyBean(dto, view);
dto.setData(map);

View File

@ -805,21 +805,35 @@ public class DataSetTableService {
String f = field.substring(0, field.length() - 1);
StringBuilder join = new StringBuilder();
List<DataSetTableUnionDTO> unions = new ArrayList<>();
for (DataTableInfoCustomUnion dataTableInfoCustomUnion : dataTableInfoDTO.getList()) {
for (DataSetTableUnionDTO dto : list) {
// 被关联表和自助数据集的表相等
if (StringUtils.equals(dto.getTargetTableId(), dataTableInfoCustomUnion.getTableId())) {
DatasetTableField sourceField = dataSetTableFieldsService.get(dto.getSourceTableFieldId());
DatasetTableField targetField = dataSetTableFieldsService.get(dto.getTargetTableFieldId());
if (ObjectUtils.isEmpty(sourceField) || ObjectUtils.isEmpty(targetField)) {
DEException.throwException(Translator.get("i18n_dataset_field_delete"));
}
unions.add(dto);
}
}
}
if (CollectionUtils.isNotEmpty(unions)) {
for (int i = 0; i < unions.size(); i++) {
DataSetTableUnionDTO dto = unions.get(i);
DatasetTableField sourceField = dataSetTableFieldsService.get(dto.getSourceTableFieldId());
DatasetTableField targetField = dataSetTableFieldsService.get(dto.getTargetTableFieldId());
if (ObjectUtils.isEmpty(sourceField) || ObjectUtils.isEmpty(targetField)) {
DEException.throwException(Translator.get("i18n_dataset_field_delete"));
}
if (i == 0) {
join.append(convertUnionTypeToSQL(dto.getSourceUnionRelation()))
.append(DorisTableUtils.dorisName(dto.getTargetTableId()))
.append(" ON ")
.append(DorisTableUtils.dorisName(dto.getSourceTableId())).append(".").append(sourceField.getDataeaseName())
.append(" = ")
.append(DorisTableUtils.dorisName(dto.getTargetTableId())).append(".").append(targetField.getDataeaseName());
} else {
join.append(" AND ")
.append(DorisTableUtils.dorisName(dto.getSourceTableId())).append(".").append(sourceField.getDataeaseName())
.append(" = ")
.append(DorisTableUtils.dorisName(dto.getTargetTableId())).append(".").append(targetField.getDataeaseName());
}
}
}
@ -865,25 +879,39 @@ public class DataSetTableService {
String f = field.substring(0, field.length() - 1);
StringBuilder join = new StringBuilder();
List<DataSetTableUnionDTO> unions = new ArrayList<>();
for (DataTableInfoCustomUnion dataTableInfoCustomUnion : dataTableInfoDTO.getList()) {
for (DataSetTableUnionDTO dto : list) {
// 被关联表和自助数据集的表相等
if (StringUtils.equals(dto.getTargetTableId(), dataTableInfoCustomUnion.getTableId())) {
DatasetTableField sourceField = dataSetTableFieldsService.get(dto.getSourceTableFieldId());
DatasetTableField targetField = dataSetTableFieldsService.get(dto.getTargetTableFieldId());
if (ObjectUtils.isEmpty(sourceField) || ObjectUtils.isEmpty(targetField)) {
DEException.throwException(Translator.get("i18n_dataset_field_delete"));
}
DatasetTable sourceTable = datasetTableMapper.selectByPrimaryKey(dto.getSourceTableId());
String sourceTableName = new Gson().fromJson(sourceTable.getInfo(), DataTableInfoDTO.class).getTable();
DatasetTable targetTable = datasetTableMapper.selectByPrimaryKey(dto.getTargetTableId());
String targetTableName = new Gson().fromJson(targetTable.getInfo(), DataTableInfoDTO.class).getTable();
unions.add(dto);
}
}
}
if (CollectionUtils.isNotEmpty(unions)) {
for (int i = 0; i < unions.size(); i++) {
DataSetTableUnionDTO dto = unions.get(i);
DatasetTableField sourceField = dataSetTableFieldsService.get(dto.getSourceTableFieldId());
DatasetTableField targetField = dataSetTableFieldsService.get(dto.getTargetTableFieldId());
if (ObjectUtils.isEmpty(sourceField) || ObjectUtils.isEmpty(targetField)) {
DEException.throwException(Translator.get("i18n_dataset_field_delete"));
}
DatasetTable sourceTable = datasetTableMapper.selectByPrimaryKey(dto.getSourceTableId());
String sourceTableName = new Gson().fromJson(sourceTable.getInfo(), DataTableInfoDTO.class).getTable();
DatasetTable targetTable = datasetTableMapper.selectByPrimaryKey(dto.getTargetTableId());
String targetTableName = new Gson().fromJson(targetTable.getInfo(), DataTableInfoDTO.class).getTable();
if (i == 0) {
join.append(convertUnionTypeToSQL(dto.getSourceUnionRelation()))
.append(String.format(keyword, targetTableName))
.append(" ON ")
.append(String.format(keyword, sourceTableName)).append(".").append(String.format(keyword, sourceField.getOriginName()))
.append(" = ")
.append(String.format(keyword, targetTableName)).append(".").append(String.format(keyword, targetField.getOriginName()));
} else {
join.append(" AND ")
.append(String.format(keyword, sourceTableName)).append(".").append(String.format(keyword, sourceField.getOriginName()))
.append(" = ")
.append(String.format(keyword, targetTableName)).append(".").append(String.format(keyword, targetField.getOriginName()));
}
}
}

View File

@ -22,6 +22,8 @@ import io.dataease.controller.sys.response.SysUserGridResponse;
import io.dataease.controller.sys.response.SysUserRole;
import io.dataease.i18n.Translator;
import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import io.dataease.plugins.xpack.oidc.dto.SSOUserInfo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -31,6 +33,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -82,6 +86,7 @@ public class SysUserService {
@Transactional
public int save(SysUserCreateRequest request) {
checkUsername(request);
checkEmail(request);
SysUser user = BeanUtils.copyBean(new SysUser(), request);
long now = System.currentTimeMillis();
user.setCreateTime(now);
@ -102,6 +107,34 @@ public class SysUserService {
return insert;
}
@Transactional
public void saveOIDCUser(SSOUserInfo ssoUserInfo) {
long now = System.currentTimeMillis();
SysUser sysUser = new SysUser();
sysUser.setUsername(ssoUserInfo.getUsername());
sysUser.setNickName(ssoUserInfo.getNickName());
sysUser.setEmail(ssoUserInfo.getEmail());
sysUser.setPassword(CodingUtil.md5(DEFAULT_PWD));
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled(1L);
sysUser.setLanguage("zh_CN");
sysUser.setFrom(2);
sysUser.setSub(ssoUserInfo.getSub());
sysUserMapper.insert(sysUser);
SysUser dbUser = findOne(sysUser);
if (null != dbUser && null != dbUser.getUserId()) {
// oidc默认角色是普通员工
List<Long> roleIds = new ArrayList<Long>();
roleIds.add(2L);
saveUserRoles( dbUser.getUserId(), roleIds);
}
}
public String defaultPWD() {
return DEFAULT_PWD;
}
@Transactional
public void saveLdapUsers(LdapAddRequest request) {
long now = System.currentTimeMillis();
@ -115,6 +148,7 @@ public class SysUserService {
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled(request.getEnabled());
sysUser.setLanguage("zh_CN");
sysUser.setFrom(1);
return sysUser;
}).collect(Collectors.toList());
@ -145,6 +179,7 @@ public class SysUserService {
@Transactional
public int update(SysUserCreateRequest request) {
checkUsername(request);
checkEmail(request);
if (StringUtils.isEmpty(request.getPassword())) {
request.setPassword(null);
}
@ -287,4 +322,19 @@ public class SysUserService {
throw new RuntimeException(Translator.get("i18n_username_exists"));
}
}
private void checkEmail(SysUserCreateRequest request) {
SysUserExample sysUserExample = new SysUserExample();
SysUserExample.Criteria criteria = sysUserExample.createCriteria();
if (request.getUserId() != null) {
criteria.andUserIdNotEqualTo(request.getUserId());
}
criteria.andEmailEqualTo(request.getEmail());
List<SysUser> sysUsers = sysUserMapper.selectByExample(sysUserExample);
if (CollectionUtils.isNotEmpty(sysUsers)) {
throw new RuntimeException(Translator.get("i18n_email_exists"));
}
}
}

View File

@ -229,9 +229,9 @@ public class SystemParameterService {
}
public static void main(String[] args) {
/* public static void main(String[] args) {
String info="[{\"paramKey\":\"base.url\",\"paramValue\":null,\"type\":\"text\",\"sort\":1,\"file\":null,\"fileName\":null},{\"paramKey\":\"base.title\",\"paramValue\":\"DataEase Title\",\"type\":\"text\",\"sort\":3,\"file\":null,\"fileName\":null},{\"paramKey\":\"base.logo\",\"paramValue\":\"DataEase\",\"type\":\"text\",\"sort\":4,\"file\":null,\"fileName\":\"favicon.icon.png\"}]";
List<SystemParameterDTO> temp = JSON.parseArray(info,SystemParameterDTO.class);
// System.out.println("===>");
}
} */
}

View File

@ -31,3 +31,24 @@ INSERT INTO `sys_menu` VALUES (60, 1, 0, 1, '导入LDAP用户', 'system-user-imp
ALTER TABLE `datasource` ADD COLUMN `compute_type` VARCHAR(45) NULL DEFAULT 'ALL' COMMENT '数据计算模式' AFTER `status`;
update datasource set compute_type='ALL';
BEGIN;
INSERT INTO `system_parameter` VALUES ('ldap.url', NULL, 'text', 1);
INSERT INTO `system_parameter` VALUES ('ldap.dn', NULL, 'text', 2);
INSERT INTO `system_parameter` VALUES ('ldap.password', NULL, 'password', 3);
INSERT INTO `system_parameter` VALUES ('ldap.ou', NULL, 'text', 4);
INSERT INTO `system_parameter` VALUES ('ldap.mapping', NULL, 'text', 6);
INSERT INTO `system_parameter` VALUES ('ldap.open', NULL, 'text', 7);
INSERT INTO `system_parameter` VALUES ('oidc.authEndpoint', NULL, 'text', 1);
INSERT INTO `system_parameter` VALUES ('oidc.tokenEndpoint', NULL, 'text', 2);
INSERT INTO `system_parameter` VALUES ('oidc.userinfoEndpoint', NULL, 'text', 3);
INSERT INTO `system_parameter` VALUES ('oidc.logoutEndpoint', NULL, 'text', 4);
INSERT INTO `system_parameter` VALUES ('oidc.clientId', NULL, 'text', 5);
INSERT INTO `system_parameter` VALUES ('oidc.secret', NULL, 'password', 6);
INSERT INTO `system_parameter` VALUES ('oidc.scope', NULL, 'text', 7);
INSERT INTO `system_parameter` VALUES ('oidc.redirectUrl', NULL, 'text', 8);
INSERT INTO `system_parameter` VALUES ('oidc.open', NULL, 'text', 9);
COMMIT;
ALTER TABLE `sys_user` ADD COLUMN `sub` varchar(255) COMMENT 'oidc用户ID' AFTER `from`;

View File

@ -198,6 +198,7 @@ authsource_configuration_is_null=Authentication source configuration cannot be e
删除角色=Delete Role
删除连接=Delete Connection
显示设置=Display
系统参数=System Param
参数管理=Parameter
数据源=Data Source
数据源表单=Data source form
@ -243,6 +244,7 @@ i18n_union_field_exists=The same field can't in two dataset
i18n_cron_time_error=Start time can't greater then end time
i18n_auth_source_be_canceled=This Auth Resource Already Be Canceled,Please Connect Admin
i18n_username_exists=ID is already exists
i18n_email_exists=Email is already exists
i18n_ds_name_exists=Datasource name used
i18n_sync_job_exists=There is already a synchronization task running, please try again later
i18n_datasource_check_fail=Invalid,please check config

View File

@ -198,6 +198,7 @@ authsource_configuration_is_null=认证源配置不能为空
删除角色=删除角色
删除连接=删除连接
显示设置=显示设置
系统参数=系统参数
数据源=数据源
数据源表单=数据源表单
数据集=数据集
@ -233,7 +234,7 @@ i18n_chart_count=记录数*
i18n_excel_have_merge_region=Excel 存在合并单元格
i18n_cron_expression_error=Cron 表达式校验错误
i18n_same_folder_can_not_repeat=同一目录下该名称已被使用
i18n_select_diff_folder= 请选择不的目录
i18n_select_diff_folder= 请选择不的目录
i18n_default_panel=默认仪表板
i18n_panel_list=仪表板
i18n_processing_data=正在处理数据,稍后刷新
@ -242,6 +243,7 @@ i18n_union_field_exists=两个数据集之间关联不能出现多次相同字
i18n_cron_time_error=开始时间不能大于结束时间
i18n_auth_source_be_canceled=本用户当前资源所有授权权限已经被取消,如需再次开通,请联系管理员
i18n_username_exists=用户 ID 已存在
i18n_email_exists=邮箱已存在
i18n_ds_name_exists=数据源名称已被使用
i18n_sync_job_exists=已经有同步任务在运行,稍后重试
i18n_datasource_check_fail=校验失败,请检查配置信息

View File

@ -200,6 +200,7 @@ authsource_configuration_is_null=認證源配置不能為空
删除角色=刪除角色
刪除连接=刪除鏈接
显示设置=顯示設置
系统参数=系統參數
参数管理=參數管理
数据源=數據源
数据源表单=數據源表單
@ -236,7 +237,7 @@ i18n_chart_count=記錄數*
i18n_excel_have_merge_region=Excel存在合並單元格
i18n_cron_expression_error=Cron表達式校驗錯誤
i18n_same_folder_can_not_repeat=同一目錄下該名稱已被使用
i18n_select_diff_folder= 请选择不的目录
i18n_select_diff_folder= 请选择不的目录
i18n_default_panel=默認儀表板
i18n_panel_list=儀表板
i18n_processing_data=正在處理數據,稍後刷新
@ -245,6 +246,7 @@ i18n_union_field_exists=兩個數據集之間關聯不能出現多次相同字
i18n_cron_time_error=開始時間不能大於結束時間
i18n_auth_source_be_canceled=本用户当前资源所有授权权限已经被取消,如需再次开通,请联系管理员
i18n_username_exists=用戶ID已存在
i18n_email_exists=郵箱已存在
i18n_ds_name_exists=數據源名稱已被使用
i18n_sync_job_exists=已經有同步任務在運行,稍後重試
i18n_datasource_check_fail=校驗失敗,請檢查配置信息

View File

@ -15,6 +15,7 @@
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"dependencies": {
"@antv/g2plot": "^2.3.32",
"@riophae/vue-treeselect": "0.4.0",
"@tinymce/tinymce-vue": "^3.2.8",
"axios": "^0.21.1",

View File

@ -100,7 +100,7 @@ export function roleGrid(pageIndex, pageSize, data) {
export function ldapUsers(data) {
return request({
url: '/api/ldap/users',
url: '/plugin/ldap/users',
method: 'post',
loading: true
})

View File

@ -57,3 +57,10 @@ export function ldapStatus() {
method: 'post'
})
}
export function oidcStatus() {
return request({
url: '/api/auth/isOpenOidc',
method: 'post'
})
}

View File

@ -610,7 +610,7 @@ export default {
const rect = this.$el.parentNode.getBoundingClientRect()
this.parentX = rect.x
this.parentY = rect.y
return [Math.round(parseFloat(style.getPropertyValue('width'), 10)), Math.round(parseFloat(style.getPropertyValue('height'), 10))]
return [Math.round(parseFloat(style.getPropertyValue('width'), 10)), 100000]
}
if (typeof this.parent === 'string') {
const parentNode = document.querySelector(this.parent)

View File

@ -199,6 +199,7 @@ export default {
query(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
this.count = this.paginationConfig.total
}).catch(() => {
const token = getToken()
if (!token || token === 'null' || token === 'undefined') {
@ -232,7 +233,7 @@ export default {
right: 178px;
top: 8px;
background: red;
// color: #fff;
color: #fff;
border-radius: 17px;
padding: 4px 7px;
font-size: 16px;

View File

@ -110,12 +110,16 @@ export default {
}
}
}
if (this.canvasStyleData.selfAdaption) {
style = {
overflow: 'hidden',
...style
}
}
// if (this.canvasStyleData.selfAdaption) {
// style = {
// overflow: 'hidden',
// ...style
// }
// }
// style = {
// overflow-x :'hidden',
// ...style
// }
return style
},
// componentData mapState
@ -168,10 +172,13 @@ export default {
const canvasWidth = document.getElementById('canvasInfoTemp').offsetWidth
this.scaleWidth = canvasWidth * 100 / parseInt(this.canvasStyleData.width)//
this.scaleHeight = canvasHeight * 100 / parseInt(this.canvasStyleData.height)//
if (this.showType === 'width') {
this.scaleHeight = this.scaleWidth
this.mainHeight = this.canvasStyleData.height * this.scaleHeight / 100 + 'px'
}
this.scaleHeight = this.scaleWidth
// this.mainHeight = this.canvasStyleData.height * this.scaleHeight / 100 + 'px'
// if (this.showType === 'width') {
// this.scaleHeight = this.scaleWidth
// this.mainHeight = this.canvasStyleData.height * this.scaleHeight / 100 + 'px'
// }
this.handleScaleChange()
},
resetID(data) {

View File

@ -1,6 +1,5 @@
<template>
<div
v-if="showDrag"
id="editor"
class="editor"
:class="[
@ -13,7 +12,7 @@
@mousedown="handleMouseDown"
>
<!-- 网格线 -->
<Grid v-if="canvasStyleData.auxiliaryMatrix&&!linkageSettingStatus" :matrix-style="matrixStyle" />
<!-- <Grid v-if="canvasStyleData.auxiliaryMatrix&&!linkageSettingStatus" :matrix-style="matrixStyle" />-->
<!-- 仪表板联动清除按钮-->
<canvas-opt-bar />
@ -217,10 +216,10 @@ export default {
width: 80,
height: 20
},
// 12 * 24
// 128 * 72
matrixCount: {
x: 24,
y: 72
x: 80,
y: 45
},
customStyleHistory: null,
showDrag: true,
@ -556,6 +555,9 @@ export default {
if (this.canvasStyleData.matrixCount) {
this.matrixCount = this.canvasStyleData.matrixCount
}
// 1.3 1600*900 scaleWidth
// scaleHeight = scaleWidth 128*72 12.5*12.5
if (this.outStyle.width && this.outStyle.height) {
//
if (!this.canvasStyleData.selfAdaption) {
@ -643,6 +645,7 @@ export default {
position: relative;
/*background: #fff;*/
margin: auto;
overflow-x: hidden;
background-size:100% 100% !important;
/*transform-style:preserve-3d;*/
.lock {

View File

@ -49,6 +49,14 @@
<el-input v-model="innerOpacity" type="number" size="mini" min="0" max="100" step="10" @change="styleChange" />
</div>
<el-tooltip :content="$t('panel.borderRadius')">
<i style="float: left;margin-top: 3px;margin-left: 2px;" class="icon iconfont icon-fangxing-" />
</el-tooltip>
<div style="width: 70px;float: left;margin-top: 2px;margin-left: 2px;">
<el-input v-model="styleInfo.borderRadius" type="number" size="mini" min="0" max="100" step="1" @change="styleChange" />
</div>
<div style="width: 20px;float: left;margin-top: 2px;margin-left: 10px;">
<div style="width: 16px;height: 18px">
<el-tooltip :content="$t('panel.color')">
@ -173,7 +181,7 @@ export default {
ps = x + 60
}
// toolbar
const xGap = ps + 495 - this.canvasWidth
const xGap = ps + 565 - this.canvasWidth
// console.log('canvasWidth:' + this.canvasWidth + ';xGap:' + xGap)
if (xGap > 0) {
return ps - xGap
@ -205,7 +213,7 @@ export default {
.el-card-main {
height: 34px;
z-index: 10;
width: 550px;
width: 620px;
position: absolute;
}

View File

@ -17,18 +17,6 @@
<el-switch v-model="canvasStyleData.auxiliaryMatrix" :width="35" name="auxiliaryMatrix" />
<span>{{ $t('panel.matrix_design') }}</span>
</div>
<div class="canvas-config" style="margin-right: 10px">
<el-switch v-model="canvasStyleData.selfAdaption" :width="35" name="selfAdaption" />
<span>{{ $t('panel.canvas_self_adaption') }} </span>
</div>
<div class="canvas-config" style="margin-right: 55px">
<span> {{ $t('panel.canvas_size') }} </span>
<input v-model="canvasStyleData.width" :disabled="canvasStyleData.selfAdaption">
<span>*</span>
<input v-model="canvasStyleData.height" :disabled="canvasStyleData.selfAdaption">
</div>
<!-- <div class="canvas-config" style="margin-right: 10px">-->
<!-- <span> {{ $t('panel.canvas_scale') }} </span>-->
<!-- <input v-model="scale" @input="handleScaleChange"> %-->
@ -383,7 +371,7 @@ export default {
float: right;
height: 35px;
line-height: 35px;
min-width: 900px;
min-width: 500px;
/*background: #fff;*/
/*border-bottom: 1px solid #ddd;*/

View File

@ -16,7 +16,8 @@
{{ $t('chart.chart_error_tips') }}
</div>
</div>
<chart-component v-if="httpRequest.status &&chart.type && !chart.type.includes('table') && !chart.type.includes('text')" :ref="element.propValue.id" class="chart-class" :chart="chart" :track-menu="trackMenu" @onChartClick="chartClick" />
<chart-component v-if="httpRequest.status &&chart.type && !chart.type.includes('table') && !chart.type.includes('text') && renderComponent() === 'echarts'" :ref="element.propValue.id" class="chart-class" :chart="chart" :track-menu="trackMenu" @onChartClick="chartClick" />
<chart-component-g2 v-if="httpRequest.status &&chart.type && !chart.type.includes('table') && !chart.type.includes('text') && renderComponent() === 'g2'" :ref="element.propValue.id" class="chart-class" :chart="chart" :track-menu="trackMenu" @onChartClick="chartClick" />
<!-- <chart-component :ref="element.propValue.id" class="chart-class" :chart="chart" :track-menu="trackMenu" @onChartClick="chartClick" />-->
<table-normal v-if="httpRequest.status &&chart.type && chart.type.includes('table')" :ref="element.propValue.id" :show-summary="chart.type === 'table-normal'" :chart="chart" class="table-class" />
<label-normal v-if="httpRequest.status && chart.type && chart.type.includes('text')" :ref="element.propValue.id" :chart="chart" class="table-class" />
@ -43,9 +44,10 @@ import { deepCopy } from '@/components/canvas/utils/utils'
import { getToken, getLinkToken } from '@/utils/auth'
import DrillPath from '@/views/chart/view/DrillPath'
import { areaMapping } from '@/api/map/map'
import ChartComponentG2 from '@/views/chart/components/ChartComponentG2'
export default {
name: 'UserView',
components: { ChartComponent, TableNormal, LabelNormal, DrillPath },
components: { ChartComponent, TableNormal, LabelNormal, DrillPath, ChartComponentG2 },
props: {
element: {
type: Object,
@ -119,7 +121,7 @@ export default {
trackMenu() {
const trackMenuInfo = []
let linkageCount = 0
this.chart.data && this.chart.data.fields && this.chart.data.fields.forEach(item => {
this.chart.data && this.chart.data.sourceFields && this.chart.data.sourceFields.forEach(item => {
const sourceInfo = this.chart.id + '#' + item.id
if (this.nowPanelTrackInfo[sourceInfo]) {
linkageCount++
@ -262,6 +264,7 @@ export default {
this.chart.drillFields = this.chart.drillFields ? JSON.parse(this.chart.drillFields) : []
if (!response.data.drill) {
this.drillClickDimensionList.splice(this.drillClickDimensionList.length - 1, 1)
this.resetDrill()
}
this.drillFilters = JSON.parse(JSON.stringify(response.data.drillFilters))
this.drillFields = JSON.parse(JSON.stringify(response.data.drillFields))
@ -434,6 +437,14 @@ export default {
this.destroyTimeMachine()
}, 200)
}
},
renderComponent() {
if (this.chart.type === 'liquid') {
return 'g2'
} else {
return 'echarts'
}
}
}
}

View File

@ -57,8 +57,11 @@ const list = [
fontWeight: 400,
lineHeight: '',
letterSpacing: 0,
textAlign: 'left',
color: '#000000'
textAlign: 'center',
color: '#000000',
verticalAlign: 'middle',
backgroundColor: '#ffffff',
borderRadius: 0
}
},
{

View File

@ -0,0 +1 @@
<svg t="1631072907294" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3122" width="200" height="200"><path d="M512 51.2C257.504 51.2 51.2 257.504 51.2 512S257.504 972.8 512 972.8 972.8 766.496 972.8 512 766.496 51.2 512 51.2m0 42.624c230.592 0 418.176 187.584 418.176 418.176 0 230.592-187.584 418.176-418.176 418.176-230.592 0-418.176-187.584-418.176-418.176C93.824 281.408 281.408 93.824 512 93.824" p-id="3123"></path><path d="M757.92 598.88c-56.704 0-113.856-13.6-164.352-39.296-37.408-19.04-70.016-46.016-100.224-74.848-29.536-28.16-58.944-52.8-98.944-64.992-17.472-5.312-69.76-20.448-129.472 1.024a190.624 190.624 0 0 0-75.872 50.304A187.264 187.264 0 0 0 153.6 528.96c10.88 190.304 168.32 341.44 361.536 341.44 175.328 0 321.568-124.32 355.264-289.504a362.048 362.048 0 0 1-112.48 17.984" p-id="3124"></path></svg>

After

Width:  |  Height:  |  Size: 867 B

View File

@ -130,6 +130,8 @@ export default {
re_login: 'Login again'
},
commons: {
search: 'Search',
folder: 'Folder',
no_target_permission: 'No permission',
success: 'Success',
switch_lang: 'Switch Language Success',
@ -431,6 +433,11 @@ export default {
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
},
sysParams: {
display: 'Display Setting',
ldap: 'LDAP Setting',
oidc: 'OIDC Setting'
},
license: {
i18n_no_license_record: 'No License Record',
i18n_license_is_empty: 'License is empty.',
@ -498,6 +505,47 @@ export default {
change_password: 'Change Password',
search_by_name: 'Search by name'
},
ldap: {
url: 'LDAP url',
dn: 'LDAP DN',
password: 'Password',
ou: 'OU',
filter: 'filter',
mapping: 'LDAP mapping',
open: 'Enable LDAP Authentication',
input_url: 'Please key LDAP url',
input_dn: 'Please key DN',
input_password: 'Please key password',
input_ou: 'Please key OU',
input_filter: 'Please key filter',
input_mapping: 'Please key LDAP mapping',
input_username: 'Please key username',
input_url_placeholder: 'Please key url (like ldap://localhost:389)',
input_ou_placeholder: 'Please key OU ',
input_filter_placeholder: 'Please key filter',
input_mapping_placeholder: 'like{"username":"uid","name":"sn","email":"mail"}',
test_connect: 'Test connect',
edit: 'Edit',
login_success: 'Login success',
url_cannot_be_empty: 'LDAP url can not be empty',
dn_cannot_be_empty: 'LDAP DN can not be empty',
ou_cannot_be_empty: 'LDAP OU can not be empty',
filter_cannot_be_empty: 'LDAP filter can not be empty',
mapping_cannot_be_empty: 'LDAP mapping can not be empty',
password_cannot_be_empty: 'LDAP password can not be empty',
import_ldap: 'Import LDAP User'
},
oidc: {
auth_endpoint: 'Please key AuthEndpoint',
token_endpoint: 'Please key TokenEndpoint',
userinfo_endpoint: 'Please key UserinfoEndpoint',
logout_endpoint: 'Please key logoutEndpoint',
clientId: 'Please key ClientId',
secret: 'Please key Secret',
scope: 'Please key scope',
redirectUrl: 'Please key redirectUrl',
open: 'Enable OIDC Authentication'
},
role: {
menu_authorization: 'Menu Authorization',
data_authorization: 'Data Authorization',
@ -862,7 +910,20 @@ export default {
yAxis_main: 'Main Vertical Axis',
yAxis_ext: 'Ext Vertical Axis',
total: 'Total',
items: 'Items'
items: 'Items',
chart_liquid: 'Liquid',
drag_block_progress: 'Progress',
liquid_max: 'End Value',
liquid_outline_border: 'Border Width',
liquid_outline_distance: 'Border Distance',
liquid_wave_length: 'Wave Length',
liquid_wave_count: 'Wave Count',
liquid_shape: 'Shape',
liquid_shape_circle: 'Circle',
liquid_shape_diamond: 'Diamond',
liquid_shape_triangle: 'Triangle',
liquid_shape_pin: 'Pin',
liquid_shape_rect: 'Rect'
},
dataset: {
sheet_warn: 'There are multiple sheet pages, and the first one is extracted by default',

View File

@ -45,7 +45,7 @@ export default {
dashboard: '首頁',
documentation: '文檔',
guide: '引導頁',
permission: '全縣測試頁',
permission: '權限測試頁',
rolePermission: '角色權限',
pagePermission: '頁面權限',
directivePermission: '指令權限',
@ -91,7 +91,7 @@ export default {
errorPages: '錯誤頁面',
page401: '401',
page404: '404',
errorLog: '錯誤日',
errorLog: '錯誤日',
excel: 'Excel',
exportExcel: '導出 Excel',
selectExcel: '導出 已選擇項',
@ -109,34 +109,36 @@ export default {
navbar: {
dashboard: '首頁',
github: '項目地址',
logOut: '退出登',
logOut: '退出登',
profile: '個人中心',
theme: '換膚',
size: '局大小'
size: '局大小'
},
login: {
title: '系統登',
title: '系統登',
welcome: '歡迎使用',
logIn: '登',
username: '號',
logIn: '登',
username: '號',
password: '密碼',
any: '任意字符',
thirdparty: '第三方登',
any: '隨便填',
thirdparty: '第三方登',
thirdpartyTips: '本地不能模擬,請結合自己業務進行模擬!!!',
expires: '登陸信息過期,請重新登陸',
tokenError: '信息錯誤,請重新登陸',
expires: '登錄信息過期,請重新登錄',
tokenError: '登陸信息錯誤,請重新登錄',
username_error: '請輸入正確的 ID',
password_error: '密碼不小 8 位',
re_login: '重新登'
password_error: '密碼不小 8 位',
re_login: '重新登'
},
commons: {
search: '搜索',
folder: '目錄',
no_target_permission: '沒有權限',
success: '成功',
switch_lang: '切換語言成功',
close: '關閉',
icon: '圖標',
all: '全部',
enable: '用',
enable: '用',
disable: '禁用',
yes: '是',
no: '否',
@ -153,30 +155,30 @@ export default {
not_cover: '不覆蓋',
import_mode: '導入模式',
import_module: '導入模塊',
please_fill_in_the_template: '請填寫模內容',
cut_back_old_version: '切舊版',
cut_back_new_version: '切新版',
please_fill_in_the_template: '請填寫模內容',
cut_back_old_version: '切舊版',
cut_back_new_version: '切新版',
comment: '評論',
examples: '示例',
help_documentation: '幫助文檔',
api_help_documentation: 'API文檔',
delete_cancelled: '已取消刪除',
workspace: '工作空開你',
workspace: '工作空',
organization: '組織',
menu: '菜單',
setting: '設置',
project: '項目',
about_us: '關',
about_us: '關',
current_project: '當前項目',
name: '名稱',
description: '描述',
annotation: '釋',
annotation: '釋',
clear: '清空',
save: '保存',
update: '更新',
save_success: '保存成功',
delete_success: '刪除成功',
copy_success: '複成功',
copy_success: '複成功',
modify_success: '修改成功',
delete_cancel: '已取消刪除',
confirm: '確定',
@ -184,8 +186,8 @@ export default {
prompt: '提示',
operating: '操作',
input_limit: '長度在 {0} 到 {1} 個字符',
login: '登',
welcome: '一站式開源數據分析平',
login: '登',
welcome: '一站式開源數據分析平',
username: '姓名',
password: '密碼',
input_username: '請輸入用戶姓名',
@ -213,11 +215,11 @@ export default {
input_content: '請輸入內容',
create: '新建',
edit: '編輯',
copy: '複',
copy: '複',
refresh: '刷新',
remark: '備',
remark: '備',
delete: '刪除',
reduction: '恢',
reduction: '恢',
not_filled: '未填寫',
please_select: '請選擇',
search_by_name: '根據名稱搜索',
@ -225,7 +227,7 @@ export default {
exit_system: '退出系統',
verification: '驗證',
title: '標題',
custom: '自義',
custom: '自義',
select_date: '選擇日期',
months_1: '一月',
months_2: '二月',
@ -250,13 +252,13 @@ export default {
connection_successful: '連接成功',
connection_failed: '連接失敗',
save_failed: '保存失敗',
host_cannot_be_empty: '主機不能空',
port_cannot_be_empty: '端口號不能空',
account_cannot_be_empty: '帳戶不能空',
host_cannot_be_empty: '主機不能空',
port_cannot_be_empty: '端口號不能空',
account_cannot_be_empty: '帳戶不能空',
remove: '移除',
remove_cancel: '移除取消',
remove_success: '移除成功',
tips: '認證信息已過期,請重新登',
tips: '認證信息已過期,請重新登',
not_performed_yet: '尚未執行',
incorrect_input: '輸入內容不正確',
delete_confirm: '請輸入以下內容,確認刪除:',
@ -264,13 +266,13 @@ export default {
input_login_username: '請輸入用戶 ID 或 郵箱',
input_name: '請輸入名稱',
please_upload: '請上傳文件',
please_fill_path: '請填寫url 路徑',
please_fill_path: '請填寫ur路徑',
formatErr: '格式錯誤',
please_save: '請先保存',
reference_documentation: '參考文檔',
id: 'ID',
millisecond: '毫秒',
cannot_be_null: '不能空',
cannot_be_null: '不能空',
required: '{0}是必填的',
already_exists: '名稱不能重複',
modifier: '修改人',
@ -288,7 +290,7 @@ export default {
start_date_time: '開始日期時間',
end_date_time: '結束日期時間',
range_separator: '至',
data_time_error: '開始日期不能大結束日期'
data_time_error: '開始日期不能大結束日期'
},
adv_search: {
title: '高級搜索',
@ -304,14 +306,14 @@ export default {
is_not_empty: '非空',
like: '包含',
not_like: '不包含',
in: '屬',
not_in: '不屬',
gt: '大',
ge: '大與等與',
lt: '小',
le: '小與等與',
equals: '等',
not_equals: '不等',
in: '屬',
not_in: '不屬',
gt: '大',
ge: '大于等于',
lt: '小',
le: '小于等于',
equals: '等',
not_equals: '不等',
between: '之間',
current_user: '是當前用戶'
},
@ -355,23 +357,23 @@ export default {
cancel: '取消'
},
guide: {
description: '引導頁對一些第一次進入項目的人很有用,你可以簡單介紹下項目的功能。本 Demo 是基',
description: '引導頁對一些第一次進入項目的人很有用,你可以簡單介紹下項目的功能。本 Demo 是基',
button: '打開引導'
},
components: {
documentation: '文檔',
tinymceTips: '富文本是管理後一個核心的功能但同時又是一個有很多坑的地方。在選擇富文本的過程中我也走了不少的彎路市面上常見的富文本都基本用過了最終權衡了一下選擇了Tinymce。更詳細的富文本比較和介紹見',
dropzoneTips: '由我司業務有特殊需求,而且要傳七牛 所以沒用第三方,選擇了自己封裝。代碼非常的簡單,具體代碼你可以在這裏看到 @/components/Dropzone',
tinymceTips: '富文本是管理後一個核心的功能但同時又是一個有很多坑的地方。在選擇富文本的過程中我也走了不少的彎路市面上常見的富文本都基本用過了最終權衡了一下選擇了Tinymce。更詳細的富文本比較和介紹見',
dropzoneTips: '由我司業務有特殊需求,而且要傳七牛 所以沒用第三方,選擇了自己封裝。代碼非常的簡單,具體代碼你可以在這裏看到 @/components/Dropzone',
stickyTips: '當頁面滾動到預設的位置會吸附在頂部',
backToTopTips1: '頁面滾動到指定位置會在右下角出現返回頂部按鈕',
backToTopTips2: '可自定義按鈕的樣式、show/hide、出現的高度、返回的位置 如需文字提示可在外部使用Element的el-tooltip元素',
imageUploadTips: '由我在使用時它只有vue@1版本而且和mockjs不兼容所以自己改造了一下如果大家要使用的話優先還是使用官方版本。'
imageUploadTips: '由我在使用時它只有vue@1版本而且和mockjs不兼容所以自己改造了一下如果大家要使用的話優先還是使用官方版本。'
},
table: {
dynamicTips1: '固定標頭,按照表頭順序排序',
dynamicTips2: '不固定頭, 按照點擊順序排序',
dragTips1: '默認序',
dragTips2: '拖拽後序',
dynamicTips1: '固定表頭, 按照表頭順序排序',
dynamicTips2: '不固定頭, 按照點擊順序排序',
dragTips1: '默認序',
dragTips2: '拖拽後序',
title: '標題',
importance: '重要性',
type: '類型',
@ -390,15 +392,15 @@ export default {
publish: '發布',
draft: '草稿',
delete: '刪除',
cancel: '取消',
confirm: '確定'
cancel: '取 消',
confirm: '確 定'
},
example: {
warning: '創建和編輯頁面是不能被 keep-alive 緩存的,因為keep-alive 的 include 目前不支持根據路由來緩存,所以目前都是基與 component name 來進行緩存的。如果你想類似的實現緩存效果,可以使用 localStorage 等覽器緩存方案。或者不要使用 keep-alive 的 include直接緩存所有頁面。詳情見'
warning: '創建和編輯頁面是不能被 keep-alive 緩存的,因爲keep-alive 的 include 目前不支持根據路由來緩存,所以目前都是基于 component name 來進行緩存的。如果你想類似的實現緩存效果,可以使用 localStorage 等覽器緩存方案。或者不要使用 keep-alive 的 include直接緩存所有頁面。詳情見'
},
errorLog: {
tips: '請點擊右上角bug小圖標',
description: '現在的管理後基本都是spa的形式了它增強了用戶體驗但同時也會增加頁面出問題的可能性可能一個小小的疏忽就導致整個頁面的死鎖。好在 Vue 官網提供了一個方法來捕獲處理異常,你可以在其中進行錯誤處理或者異常上報。',
description: '現在的管理後基本都是spa的形式了它增強了用戶體驗但同時也會增加頁面出問題的可能性可能一個小小的疏忽就導致整個頁面的死鎖。好在 Vue 官網提供了一個方法來捕獲處理異常,你可以在其中進行錯誤處理或者異常上報。',
documentation: '文檔介紹'
},
excel: {
@ -411,34 +413,39 @@ export default {
placeholder: '請輸入文件名(默認file)'
},
pdf: {
tips: '這裏使用 window.print() 來實現下載pdf的功能'
tips: '這裏使用 window.print() 來實現下載pdf的功能'
},
theme: {
change: '換膚',
documentation: '換膚文檔',
tips: 'Tips: 它區別 navbar 上的 theme-pick, 是兩種不同的換膚方法,各自有不同的應用場景,具體請參考文檔。'
tips: 'Tips: 它區別 navbar 上的 theme-pick, 是兩種不同的換膚方法,各自有不同的應用場景,具體請參考文檔。'
},
tagsView: {
refresh: '刷新',
close: '關閉',
closeOthers: '關閉其',
closeOthers: '關閉其',
closeAll: '關閉所有'
},
settings: {
title: '系統局配置',
title: '系統局配置',
theme: '主題色',
tagsView: '開 Tags-View',
tagsView: '開 Tags-View',
fixedHeader: '固定 Header',
sidebarLogo: '側邊欄 Logo'
},
sysParams: {
display: '顯示設置',
ldap: 'LDAP設置',
oidc: 'OIDC設置'
},
license: {
i18n_no_license_record: '沒有 License 紀錄',
i18n_license_is_empty: 'License 為空',
i18n_no_license_record: '沒有 License 錄',
i18n_license_is_empty: 'License 空',
title: '授權管理',
corporation: '客戶名稱',
time: '授權時間',
product: '品名稱',
edition: '品版本',
product: '品名稱',
edition: '品版本',
licenseVersion: '授權版本',
count: '授權數量',
valid_license: '授權驗證',
@ -469,7 +476,7 @@ export default {
repeat_password: '確認密碼',
inconsistent_passwords: '兩次輸入的密碼不一致',
remove_member: '確定要移除該成員嗎',
org_remove_member: '確定要移除該成員嗎?',
org_remove_member: '將該用戶從組織中移除,將同時移除該組織下所有工作空間的權限,確定要移除該成員嗎?',
input_id_or_email: '請輸入用戶 ID, 或者 用戶郵箱',
no_such_user: '無此用戶信息, 請輸入正確的用戶 ID 或者 用戶郵箱!'
},
@ -482,6 +489,7 @@ export default {
input_password: '請輸入密碼',
input_phone: '請輸入電話號碼',
input_roles: '請選擇角色',
select_users: '請選擇用戶',
special_characters_are_not_supported: '不支持特殊字符',
mobile_number_format_is_incorrect: '手機號碼格式不正確',
email_format_is_incorrect: '郵箱格式不正確',
@ -496,7 +504,49 @@ export default {
new_passwd: '新密碼',
confirm_passwd: '確認密碼',
change_password: '修改密碼',
search_by_name: '按姓名搜索'
search_by_name: '按姓名搜索',
import_ldap: '導入LDAP用戶'
},
ldap: {
url: 'LDAP地址',
dn: '綁定DN',
password: '密碼',
ou: '用戶OU',
filter: '用戶過濾器',
mapping: 'LDAP屬性映射',
open: '啓用LDAP認證',
input_url: '請輸入LDAP地址',
input_dn: '請輸入DN',
input_password: '請輸入密碼',
input_ou: '請輸入用戶OU',
input_filter: '請輸入用戶過濾器',
input_mapping: '請輸入LDAP屬性映射',
input_username: '請輸入用戶名',
input_url_placeholder: '請輸入LDAP地址 (如 ldap://localhost:389)',
input_ou_placeholder: '輸入用戶OU (使用|分隔各OU)',
input_filter_placeholder: '輸入過濾器 [可能的選項是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的選項可能是cn或uid或sAMAccountName',
test_connect: '測試連接',
test_login: '測試登錄',
edit: '編輯',
login_success: '登錄成功',
url_cannot_be_empty: 'LDAP 地址不能爲空',
dn_cannot_be_empty: 'LDAP DN不能爲空',
ou_cannot_be_empty: 'LDAP OU不能爲空',
filter_cannot_be_empty: 'LDAP 用戶過濾器不能爲空',
mapping_cannot_be_empty: 'LDAP 用戶屬性映射不能爲空',
password_cannot_be_empty: 'LDAP 密碼不能爲空'
},
oidc: {
auth_endpoint: '請輸入AuthEndpoint',
token_endpoint: '請輸入TokenEndpoint',
userinfo_endpoint: '請輸入UserinfoEndpoint',
logout_endpoint: '請輸入logoutEndpoint',
clientId: '請輸入ClientId',
secret: '請輸入Secret',
scope: '請輸入scope',
redirectUrl: '請輸入redirectUrl',
open: '啓用OIDC認證'
},
role: {
menu_authorization: '菜單授權',
@ -528,7 +578,7 @@ export default {
create: '創建菜單',
modify: '修改菜單',
delete: '刪除菜單',
delete_confirm: '確定刪除菜單嗎',
delete_confirm: '確定刪除菜單嗎',
menu_type: '菜單類型'
},
organization: {
@ -551,19 +601,19 @@ export default {
},
system_parameter_setting: {
mailbox_service_settings: '郵件設置',
test_connection: '測試接',
test_connection: '測試接',
SMTP_host: 'SMTP主機',
SMTP_port: 'SMTP端口',
SMTP_account: 'SMTP帳號',
SMTP_account: 'SMTP賬戶',
SMTP_password: 'SMTP密碼',
SSL: '開啟SSL(如果SMTP端口是465通常需要啟用SSL)',
TLS: '開啟TLS(如果SMTP端口是587通常需要啟用TLS)',
SSL: '開啓SSL(如果SMTP端口是465通常需要啓用SSL)',
TLS: '開啓TLS(如果SMTP端口是587通常需要啓用TLS)',
SMTP: '是否免密 SMTP',
host: '主機不能為空',
port: '端口號不能空',
account: '帳戶不能為空',
host: '主機號不能爲空',
port: '端口號不能空',
account: '賬戶不能爲空',
test_recipients: '測試收件人',
tip: ' 提示: 僅用來作為測試郵件收件人'
tip: '提示:僅用來作爲測試郵件收件人'
},
chart: {
save_snapshot: '保存縮略圖',
@ -589,14 +639,14 @@ export default {
sql_data: 'SQL數據集',
excel_data: 'Excel數據集',
custom_data: '自定義數據集',
pls_slc_tbl_left: '請從左側選視圖',
add_db_table: '添加數據庫',
pls_slc_tbl_left: '請從左側選視圖',
add_db_table: '添加數據庫數據集',
pls_slc_data_source: '請選擇數據源',
table: '表',
edit: '編輯',
create_view: '創建圖',
create_view: '創建圖',
data_preview: '數據預覽',
dimension: '度',
dimension: '度',
quota: '指標',
title: '標題',
show: '顯示',
@ -608,22 +658,22 @@ export default {
y_axis: '縱軸',
chart: '視圖',
close: '關閉',
summary: '總方式',
summary: '總方式',
fast_calc: '快速計算',
sum: '求和',
count: '數',
count: '數',
avg: '平均',
max: '最大值',
min: '最小值',
stddev_pop: '標差',
stddev_pop: '標差',
var_pop: '方差',
quick_calc: '快速計算',
show_name_set: '顯示名設置',
color: '色',
color: '色',
color_case: '配色方案',
pls_slc_color_case: '請選配色方案',
pls_slc_color_case: '請選配色方案',
color_default: '默認',
color_retro: '古',
color_retro: '古',
color_future: '未來',
color_gradual: '漸變',
color_business: '商務',
@ -640,7 +690,7 @@ export default {
line_type: '線型',
line_symbol: '折點',
line_symbol_size: '折點大小',
line_type_solid: '實',
line_type_solid: '實',
line_type_dashed: '虛線',
line_symbol_circle: '圓形',
line_symbol_emptyCircle: '空心圓',
@ -652,13 +702,13 @@ export default {
line_symbol_arrow: '箭頭',
line_symbol_none: '無',
line_area: '面積',
pie_inner_radius: '內',
pie_outer_radius: '外',
pie_inner_radius: '內',
pie_outer_radius: '外',
funnel_width: '寬度',
line_smooth: '平滑折線',
title_style: '標題式',
title_style: '標題式',
text_fontsize: '字體大小',
text_color: '字體色',
text_color: '字體色',
text_h_position: '水平位置',
text_v_position: '垂直位置',
text_pos_left: '左',
@ -674,23 +724,23 @@ export default {
legend: '圖例',
shape: '形狀',
polygon: '多邊形',
circle: '形',
label: '標',
label_position: '標位置',
circle: '形',
label: '標',
label_position: '標位置',
content_formatter: '內容格式',
inside: '內',
tooltip: '提示',
tooltip_item: '數據項',
tooltip_axis: '標軸',
formatter_plc: '內痛格式為空時,顯示默認格式',
xAxis: '橫',
tooltip_axis: '標軸',
formatter_plc: '內容格式爲空時,顯示默認格式',
xAxis: '橫',
yAxis: '縱軸',
position: '位置',
rotate: '角度',
name: '名稱',
icon: '國際',
icon: '圖標',
trigger_position: '觸發位置',
asc: '序',
asc: '序',
desc: '降序',
sort: '排序',
filter: '過濾',
@ -701,39 +751,39 @@ export default {
border_radius: '邊框半徑',
alpha: '透明度',
add_filter: '添加過濾',
no_limit: '無顯示',
filter_eq: '等',
filter_not_eq: '不等',
filter_lt: '小',
filter_le: '小於等於',
filter_gt: '大',
filter_ge: '大於等於',
filter_null: '空',
filter_not_null: '不空',
no_limit: '無限制',
filter_eq: '等',
filter_not_eq: '不等',
filter_lt: '小',
filter_le: '小于等于',
filter_gt: '大',
filter_ge: '大于等于',
filter_null: '空',
filter_not_null: '不空',
filter_include: '包含',
filter_not_include: '不包含',
rose_type: '玫瑰圖模式',
radius_mode: '半徑',
area_mode: '面積',
rose_radius: '角',
rose_radius: '角',
view_name: '視圖標題',
belong_group: '所屬分組',
select_group: '選擇分組',
name_can_not_empty: '名稱不能空',
template_can_not_empty: '請選擇儀表',
name_can_not_empty: '名稱不能空',
template_can_not_empty: '請選擇儀表',
custom_count: '記錄數',
table_title_fontsize: '表頭字體大小',
table_item_fontsize: '表格字體大小',
table_header_bg: '表頭背景',
table_item_bg: '表格背景',
table_item_font_color: '字體色',
table_item_font_color: '字體色',
stripe: '斑馬紋',
start_angle: '起始角度',
end_angle: '結束角度',
style_priority: '樣式優先級',
dashboard: '儀表板',
dimension_color: '名稱色',
quota_color: '值色',
dimension_color: '名稱色',
quota_color: '值色',
dimension_font_size: '名稱字體大小',
quota_font_size: '值字體大小',
space_split: '名稱/值間隔',
@ -741,11 +791,11 @@ export default {
only_one_result: '僅顯示第1個計算結果',
dimension_show: '名稱顯示',
quota_show: '值顯示',
title_limit: '標題不能大50個字符',
title_limit: '標題不能大50個字符',
filter_condition: '過濾條件',
filter_field_can_null: '過濾字段必填',
preview_100_data: '預覽前100條記錄',
chart_table_normal: '總表',
chart_table_normal: '總表',
chart_table_info: '明細表',
chart_card: '指標卡',
chart_bar: '基礎柱狀圖',
@ -773,24 +823,24 @@ export default {
chartName: '新建視圖',
chart_show_error: '無法正常顯示',
chart_error_tips: '如有疑問請聯系管理員',
title_cannot_empty: '標題不能空',
title_cannot_empty: '標題不能空',
table_title_height: '表頭行高',
table_item_height: '表格行高',
axis_show: '軸線顯示',
axis_color: '軸線色',
axis_color: '軸線色',
axis_width: '軸線寬度',
axis_type: '軸線類型',
axis_type_solid: '實線',
axis_type_dashed: '虛線',
axis_type_dotted: '點',
axis_label_show: '標簽顯示',
axis_label_color: '標簽色',
axis_label_color: '標簽色',
axis_label_fontsize: '標簽大小',
text_style: '字體樣式',
bolder: '加粗',
change_ds: '更換數據集',
change_ds_tip: '提示:更換數據集將導致字段發生變化,需重新作視圖',
axis_name_color: '名稱色',
change_ds_tip: '提示:更換數據集將導致字段發生變化,需重新作視圖',
axis_name_color: '名稱色',
axis_name_fontsize: '名稱字體',
pie_label_line_show: '引導線',
outside: '外',
@ -800,10 +850,10 @@ export default {
axis_label: '軸標簽',
label_fontsize: '標簽大小',
split_line: '分割線',
split_color: '分割色',
split_color: '分割色',
shadow: '陰影',
condition: '過濾值',
filter_value_can_null: '過濾值不能空',
filter_value_can_null: '過濾值不能空',
filter_like: '包含',
filter_not_like: '不包含',
color_light: '明亮',
@ -838,7 +888,7 @@ export default {
chart_scatter: '散點圖',
bubble_size: '氣泡大小',
chart_treemap: '矩形樹圖',
drill: '取',
drill: '取',
drag_block_treemap_label: '色塊標簽',
drag_block_treemap_size: '色塊大小',
bubble_symbol: '圖形',
@ -861,10 +911,23 @@ export default {
yAxis_main: '主縱軸',
yAxis_ext: '副縱軸',
total: '共',
items: '條數據'
items: '條數據',
chart_liquid: '水波圖',
drag_block_progress: '進度指示',
liquid_max: '目標值',
liquid_outline_border: '邊框粗細',
liquid_outline_distance: '邊框間隔',
liquid_wave_length: '水波長度',
liquid_wave_count: '水波數量',
liquid_shape: '形狀',
liquid_shape_circle: '圓形',
liquid_shape_diamond: '菱形',
liquid_shape_triangle: '三角形',
liquid_shape_pin: '氣球',
liquid_shape_rect: '矩形'
},
dataset: {
sheet_warn: '有多個sheet頁面默認抽取第一個',
sheet_warn: '有多個 Sheet 頁,默認抽取第一個',
datalist: '數據集',
name: '數據集名稱',
add_group: '添加分組',
@ -905,7 +968,7 @@ export default {
time: '時間',
value: '數值',
mode: '模式',
direct_connect: '直',
direct_connect: '直',
sync_data: '定時同步',
update_setting: '更新設置',
sync_now: '立即更新',
@ -924,10 +987,10 @@ export default {
add_scope: '增量更新',
select_data_time: '選擇日期時間',
execute_rate: '執行頻率',
simple_cron: '簡單重複',
execute_once: '立即執行',
cron_config: '表達時設定',
no_limit: '無限製',
simple_cron: '簡單重複',
cron_config: '表達式設定',
no_limit: '無限制',
set_end_time: '設定結束時間',
operate: '操作',
save_success: '保存成功',
@ -937,7 +1000,7 @@ export default {
add_sql_table: '添加 SQL 數據集',
preview: '預覽',
pls_input_name: '請輸入名稱',
connect_mode: '接模式',
connect_mode: '接模式',
incremental_update_type: '增量更新方式',
incremental_add: '增量添加',
incremental_delete: '增量刪除',
@ -946,7 +1009,7 @@ export default {
param: '參數',
edit_sql: '編輯 SQL 數據集',
showRow: '顯示行',
add_excel_table: ' 添加 Excel 數據集',
add_excel_table: '添加Excel數據集',
add_custom_table: '添加自定義數據集',
upload_file: '上傳文件',
detail: '詳情',
@ -968,14 +1031,14 @@ export default {
source_field: '關聯字段',
target_table: '被關聯表',
target_field: '被關聯字段',
union_relation: '關聯關',
pls_setting_union_success: '請正確設置關聯關',
invalid_dataset: ' Kettle 未運行,無效數據集',
check_all: '選',
union_relation: '關聯關',
pls_setting_union_success: '請正確設置關聯關',
invalid_dataset: 'Kettle未運行無效數據集',
check_all: '選',
can_not_union_self: '被關聯表不能與關聯表相同',
float: '小數',
edit_custom_table: '編輯自數據集',
edit_field: '編輯自斷',
edit_custom_table: '編輯自定義數據集',
edit_field: '編輯字段',
preview_100_data: '顯示前100行數據',
invalid_table_check: '非直連數據集請先完成數據同步',
parse_error: 'Excel解析失敗請檢查格式、字段等信息。具體參考https://dataease.io/docs/faq/dataset_faq/',
@ -993,7 +1056,7 @@ export default {
sync_latter: '稍後同步',
task: {
list: '任務列表',
record: '執行錄',
record: '執行錄',
create: '新建任務',
name: '任務名稱',
last_exec_time: '上次執行時間',
@ -1029,10 +1092,10 @@ export default {
calc_field: '計算字段',
show_sql: '顯示SQL',
ple_select_excel: '請選擇要導入的 Excel',
merge: '合',
no_merge: '不合',
merge_msg: '數據表中存在自斷一直的情況,是否合併到一個數據集中?',
merge_title: '合數據',
merge: '合',
no_merge: '不合',
merge_msg: '數據表中存在字段一致的情況,是否合並到一個數據集中?',
merge_title: '合數據',
field_name_less_50: '字段名不能超過50個字符'
},
datasource: {
@ -1050,16 +1113,17 @@ export default {
datasource_url: '地址',
please_input_datasource_url: '請輸入 Elasticsearch 地址,如: http://es_host:es_port',
please_input_data_base: '請輸入數據庫名稱',
please_select_oracle_type: '選擇連接類型',
please_input_user_name: '請輸入用戶名',
please_input_password: '請輸入密碼',
please_input_host: '請輸入主機',
please_input_url: '請輸入URL地址',
please_input_port: '請輸入',
please_input_port: '請輸入端口',
modify: '編輯數據源',
validate_success: '校驗成功',
validate: '校驗',
search_by_name: '根據名稱搜索',
delete_warning: '確認刪除?',
delete_warning: '確定要刪除嗎?',
input_name: '請輸入名稱',
input_limit_2_25: '2-25字符',
input_limit_0_50: '0-50字符',
@ -1069,20 +1133,21 @@ export default {
get_schema: '獲取 Schema',
schema: '數據庫 Schema',
please_choose_schema: '請選擇數據庫 Schema',
edit_datasource_msg: '修改數據源信息,可能會導致改數據源下的數據集不可用,確認修改?',
in_valid: '無效數據源',
initial_pool_size: '初始連數',
min_pool_size: '最小連數',
max_pool_size: '最大連數',
max_idle_time: '最大空(秒)',
initial_pool_size: '初始連數',
min_pool_size: '最小連數',
max_pool_size: '最大連數',
max_idle_time: '最大空(秒)',
acquire_increment: '增長數',
connect_timeout: '連接超時(秒)',
please_input_initial_pool_size: '請輸入初始連數',
please_input_min_pool_size: '請輸入最小連數',
please_input_max_pool_size: '請輸入最大連數',
please_input_max_idle_time: '請輸入最大空(秒)',
please_input_initial_pool_size: '請輸入初始連數',
please_input_min_pool_size: '請輸入最小連數',
please_input_max_pool_size: '請輸入最大連數',
please_input_max_idle_time: '請輸入最大空(秒)',
please_input_acquire_increment: '請輸入增長數',
please_input_connect_timeout: '請輸入連接超時(秒)',
no_less_then_0: '高級設置中的參數不能小零',
no_less_then_0: '高級設置中的參數不能小零',
priority: '高級設置'
},
pblink: {
@ -1097,14 +1162,14 @@ export default {
auth_role: '已分享角色',
picture_limit: '只能插入圖片',
drag_here: '請將左側字段拖至此處',
copy_link_passwd: '複鏈接及密碼',
copy_link: '複鏈接',
copy_short_link: '複短鏈接',
copy_short_link_passwd: '複短鏈接及密碼',
copy_link_passwd: '複鏈接及密碼',
copy_link: '複鏈接',
copy_short_link: '複短鏈接',
copy_short_link_passwd: '複短鏈接及密碼',
passwd_protect: '密碼保護',
link: '鏈接',
link_share: '鏈接分享',
link_share_desc: '開鏈接後,任何人可通過此鏈接訪問儀表板。',
link_share_desc: '開鏈接後,任何人可通過此鏈接訪問儀表板。',
share: '分享',
datalist: '視圖列表',
group: '目錄',
@ -1126,7 +1191,7 @@ export default {
view: '視圖',
module: '組件',
filter_module: '過濾組件',
select_by_module: '組件選擇',
select_by_module: '組件選擇',
edit: '編輯',
sys_template: '系統模板',
user_template: '用戶模板',
@ -1140,12 +1205,12 @@ export default {
photo: '圖片',
default_panel: '默認儀表板',
create_public_links: '創建公共鏈接',
to_default: '另存默認',
to_default_panel: '另存默認儀表板',
to_default: '另存默認',
to_default_panel: '另存默認儀表板',
store: '收藏',
save_to_panel: '保存模板',
export_to_panel: '導出模板',
export_to_pdf: '導出PDF',
save_to_panel: '保存模板',
export_to_panel: '導出模板',
export_to_pdf: '導出PDF',
preview: '預覽',
fullscreen_preview: '全屏預覽',
new_tab_preview: '新Tab頁預覽',
@ -1156,21 +1221,21 @@ export default {
all_org: '所有組織',
custom: '自定義',
import_template: '導入模板',
copy_template: '用模板',
copy_template: '用模板',
upload_template: '上傳模板',
belong_to_category: '所屬類別',
pls_select_belong_to_category: '請選擇所屬類別',
template_name_cannot_be_empty: '模板名稱不能空',
select_by_table: '表選擇',
template_name_cannot_be_empty: '模板名稱不能空',
select_by_table: '表選擇',
data_list: '數據列表',
component_list: '組件列表',
custom_scope: '自定義控範圍',
custom_scope: '自定義控範圍',
multiple_choice: '多選',
single_choice: '單選',
field: '字段',
unshared_people: '未分享人員',
shared_people: '已分享人員',
error_data: '獲取數據出錯,請聯管理員',
error_data: '獲取數據出錯,請聯管理員',
canvas_size: '畫布大小',
canvas_scale: '畫布比例',
style: '樣式',
@ -1179,7 +1244,7 @@ export default {
redo: '重做',
undo: '撤銷',
panelNull: '這是個空的儀表板,可以通過編輯來豐富內容',
copy: '復製',
copy: '複制',
paste: '粘貼',
cut: '剪切',
lock: '鎖定',
@ -1197,11 +1262,11 @@ export default {
top: 'y 坐標',
height: '高',
width: '寬',
color: '色',
color: '色',
backgroundColor: '背景色',
borderStyle: '邊框風格',
borderWidth: '邊框寬度',
borderColor: '邊框色',
borderColor: '邊框色',
borderRadius: '邊框半徑',
fontSize: '字體大小',
fontWeight: '字體粗細',
@ -1254,12 +1319,21 @@ export default {
uninstall_cancel: '取消卸載插件'
},
display: {
logo: '頭部系統 Logo',
loginLogo: '登陸頁面頭部 Logo',
loginImage: '登頁面右側圖片',
loginTitle: '登頁面標題',
logo: '頭部系統logo',
loginLogo: '登錄頁面頭部logo',
loginImage: '登頁面右側圖片',
loginTitle: '登頁面標題',
title: '系統名稱',
advice_size: '建議圖片大小'
advice_size: '建議圖片大小',
theme: '主題顔色',
topMenuColor: '頭部背景',
topMenuActiveColor: '頭部選中背景',
topMenuTextColor: '頭部字體顔色',
topMenuTextActiveColor: '頭部字體選中顔色'
},
auth: {
authConfig: '權限配置',
@ -1279,6 +1353,7 @@ export default {
datasetAuthHead: '所有數據集',
chartAuthHead: '所有視圖',
panelAuthHead: '所有儀表板',
menuAuthHead: '所有菜單和操作',
view: '查看',
use: '使用',
export: '導出',
@ -1293,13 +1368,13 @@ export default {
auth_num: '授權數量',
version: '版本',
version_num: '版本號',
standard: '標版',
standard: '標版',
enterprise: '企業版',
suport: '獲取技術支持',
update_success: '更新成功'
},
template: {
exit_same_template_check: '當前存在相同名稱模板,是否覆蓋?',
exit_same_template_check: '當前分類存在相同名稱模板,是否覆蓋?',
override: '覆蓋',
cancel: '取消',
confirm_upload: '上傳確認'
@ -1315,8 +1390,8 @@ export default {
month: '月',
week: '周',
year: '年',
d_w_cant_not_set: '日期與星期不可以同時“不指定”',
d_w_must_one_set: '日期與星期必須有一個“不指定”',
d_w_cant_not_set: '日期與星期不可以同時“不指定”',
d_w_must_one_set: '日期與星期必須有一個“不指定”',
every_day: '每日',
cycle: '周期',
not_set: '不指定',
@ -1344,15 +1419,15 @@ export default {
week_end: '至星期',
every_year: '每年',
week_tips: '說明1-7 分別對應 周日-周六',
minute_limit: '分鐘不能小於1大於59',
hour_limit: '小時不能小於1大於523',
day_limit: '天不能小於1大於531'
minute_limit: '分鍾不能小于1大于59',
hour_limit: '小時不能小于1大于23',
day_limit: '天不能小于1大于31'
},
dept: {
can_not_move_change_sort: '不能移動以改變排序',
can_not_move_parent_to_children: '父組織不能移動到自己的子節點下',
move_success: '移動成功',
name_exist_pre: '下已存在名稱【',
name_exist_pre: '下已存在名稱【',
name_exist_suf: '】的組織',
root_org: '頂層組織'
},

View File

@ -130,6 +130,8 @@ export default {
re_login: '重新登录'
},
commons: {
search: '搜索',
folder: '目录',
no_target_permission: '没有权限',
success: '成功',
switch_lang: '切换语言成功',
@ -431,6 +433,11 @@ export default {
fixedHeader: '固定 Header',
sidebarLogo: '侧边栏 Logo'
},
sysParams: {
display: '显示设置',
ldap: 'LDAP设置',
oidc: 'OIDC设置'
},
license: {
i18n_no_license_record: '没有 License 记录',
i18n_license_is_empty: 'License 为空',
@ -530,6 +537,17 @@ export default {
mapping_cannot_be_empty: 'LDAP 用户属性映射不能为空',
password_cannot_be_empty: 'LDAP 密码不能为空'
},
oidc: {
auth_endpoint: '请输入AuthEndpoint',
token_endpoint: '请输入TokenEndpoint',
userinfo_endpoint: '请输入UserinfoEndpoint',
logout_endpoint: '请输入logoutEndpoint',
clientId: '请输入ClientId',
secret: '请输入Secret',
scope: '请输入scope',
redirectUrl: '请输入redirectUrl',
open: '启用OIDC认证'
},
role: {
menu_authorization: '菜单授权',
data_authorization: '数据授权',
@ -893,7 +911,20 @@ export default {
yAxis_main: '主纵轴',
yAxis_ext: '副纵轴',
total: '共',
items: '条数据'
items: '条数据',
chart_liquid: '水波图',
drag_block_progress: '进度指示',
liquid_max: '目标值',
liquid_outline_border: '边框粗细',
liquid_outline_distance: '边框间隔',
liquid_wave_length: '水波长度',
liquid_wave_count: '水波数量',
liquid_shape: '形状',
liquid_shape_circle: '圆形',
liquid_shape_diamond: '菱形',
liquid_shape_triangle: '三角形',
liquid_shape_pin: '气球',
liquid_shape_rect: '矩形'
},
dataset: {
sheet_warn: '有多个 Sheet 页,默认抽取第一个',

View File

@ -3,6 +3,9 @@ module.exports = {
RefreshTokenKey: 'refreshauthorization',
LinkTokenKey: 'LINK-PWD-TOKEN',
title: 'DataEase',
/* for sso */
IdTokenKey: 'IdToken',
AccessTokenKey: 'AccessToken',
/**
* @type {boolean} true | false

View File

@ -79,6 +79,9 @@ const data = {
},
setCanvasStyle(state, style) {
if (style) {
style['selfAdaption'] = true
}
state.canvasStyleData = style
},

View File

@ -3,8 +3,16 @@ import Config from '@/settings'
const TokenKey = Config.TokenKey
const IdTokenKey = Config.IdTokenKey
const AccessTokenKey = Config.AccessTokenKey
const linkTokenKey = Config.LinkTokenKey
export function getIdToken() {
return Cookies.get(IdTokenKey)
}
export function getToken() {
return Cookies.get(TokenKey)
}
@ -14,6 +22,8 @@ export function setToken(token) {
}
export function removeToken() {
Cookies.remove(IdTokenKey)
Cookies.remove(AccessTokenKey)
return Cookies.remove(TokenKey)
}

View File

@ -2,7 +2,7 @@ import axios from 'axios'
// import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { $alert, $error } from './message'
import { getToken } from '@/utils/auth'
import { getToken, getIdToken } from '@/utils/auth'
import Config from '@/settings'
import i18n from '@/lang'
import { tryShowLoading, tryHideLoading } from './loading'
@ -23,6 +23,10 @@ const service = axios.create({
service.interceptors.request.use(
config => {
// do something before request is sent
const idToken = getIdToken()
if (idToken) {
config.headers[Config.IdTokenKey] = idToken
}
if (store.getters.token) {
// let each request carry token

View File

@ -42,7 +42,14 @@ export const DEFAULT_SIZE = {
scatterSymbol: 'circle',
scatterSymbolSize: 20,
treemapWidth: 80,
treemapHeight: 80
treemapHeight: 80,
liquidMax: 100,
liquidSize: 80,
liquidOutlineBorder: 4,
liquidOutlineDistance: 8,
liquidWaveLength: 128,
liquidWaveCount: 3,
liquidShape: 'circle'
}
export const DEFAULT_LABEL = {
show: false,

View File

@ -0,0 +1,104 @@
import { Liquid } from '@antv/g2plot'
import { digToHex } from '@/views/chart/chart/util'
import { DEFAULT_SIZE } from '@/views/chart/chart/chart'
export function baseLiquid(plot, container, chart) {
let value = 0
const colors = []
let max, radius, outlineBorder, outlineDistance, waveLength, waveCount, bgColor, shape, labelContent, title
if (chart.data) {
if (chart.data.series.length > 0) {
value = chart.data.series[0].data[0].value
}
}
let customAttr = {}
if (chart.customAttr) {
customAttr = JSON.parse(chart.customAttr)
// color
if (customAttr.color) {
const c = JSON.parse(JSON.stringify(customAttr.color))
const alpha = digToHex(parseInt(c.alpha))
c.colors.forEach(ele => {
colors.push(ele.concat(alpha))
})
}
// size
if (customAttr.size) {
const size = JSON.parse(JSON.stringify(customAttr.size))
max = size.liquidMax ? size.liquidMax : DEFAULT_SIZE.liquidMax
radius = parseFloat((size.liquidSize ? size.liquidSize : DEFAULT_SIZE.liquidSize) / 100)
outlineBorder = parseInt(size.liquidOutlineBorder ? size.liquidOutlineBorder : DEFAULT_SIZE.liquidOutlineBorder)
outlineDistance = parseInt((size.liquidOutlineDistance || size.liquidOutlineDistance === 0) ? size.liquidOutlineDistance : DEFAULT_SIZE.liquidOutlineDistance)
waveLength = parseInt(size.liquidWaveLength ? size.liquidWaveLength : DEFAULT_SIZE.liquidWaveLength)
waveCount = parseInt(size.liquidWaveCount ? size.liquidWaveCount : DEFAULT_SIZE.liquidWaveCount)
shape = size.liquidShape ? size.liquidShape : DEFAULT_SIZE.liquidShape
}
// label
if (customAttr.label) {
const label = JSON.parse(JSON.stringify(customAttr.label))
if (label.show) {
labelContent = {
style: ({ percent }) => ({
fontSize: parseInt(label.fontSize),
color: label.color
})
}
} else {
labelContent = false
}
}
}
let customStyle
if (chart.customStyle) {
customStyle = JSON.parse(chart.customStyle)
if (customStyle.background) {
bgColor = customStyle.background.color.concat(digToHex(parseInt(customStyle.background.alpha)))
}
if (customStyle.text) {
const t = JSON.parse(JSON.stringify(customStyle.text))
if (t.show) {
title = {
formatter: () => { return chart.title },
style: ({ percent }) => ({
fontSize: parseInt(t.fontSize),
color: t.color,
fontWeight: t.isBolder ? 'bold' : 'normal',
fontStyle: t.isItalic ? 'italic' : 'normal'
})
}
} else {
title = false
}
}
}
// 开始渲染
if (plot) {
plot.destroy()
}
plot = new Liquid(container, {
theme: {
styleSheet: {
brandColor: colors[0],
paletteQualitative10: colors,
backgroundColor: bgColor
}
},
percent: (parseFloat(value) / parseFloat(max)),
radius: radius,
shape: shape,
outline: {
border: outlineBorder,
distance: outlineDistance
},
wave: {
length: waveLength,
count: waveCount
},
statistic: {
title: title,
content: labelContent
}
})
plot.render()
return plot
}

View File

@ -17,3 +17,12 @@ export function hexColorToRGBA(hex, alpha) {
return 'rgb(0,0,0)'
}
}
export function digToHex(dig) {
let prefix = ''
const num = parseInt(dig * 2.55)
if (num < 16) {
prefix = '0'
}
return prefix.concat(num.toString(16).toUpperCase())
}

View File

@ -0,0 +1,162 @@
<template>
<div style="display: flex;">
<view-track-bar ref="viewTrack" :track-menu="trackMenu" class="track-bar" :style="trackBarStyleTime" @trackClick="trackClick" />
<div :id="chartId" style="width: 100%;height: 100%;overflow: hidden;" :style="{ borderRadius: borderRadius}" />
</div>
</template>
<script>
import { baseLiquid } from '@/views/chart/chart/liquid/liquid'
// import eventBus from '@/components/canvas/utils/eventBus'
import { uuid } from 'vue-uuid'
import ViewTrackBar from '@/components/canvas/components/Editor/ViewTrackBar'
export default {
name: 'ChartComponentG2',
components: { ViewTrackBar },
props: {
chart: {
type: Object,
required: true
},
filter: {
type: Object,
required: false,
default: function() {
return {}
}
},
trackMenu: {
type: Array,
required: false,
default: function() {
return ['drill']
}
}
},
data() {
return {
myChart: null,
chartId: uuid.v1(),
showTrackBar: true,
trackBarStyle: {
position: 'absolute',
left: '0px',
top: '0px'
},
pointParam: null,
dynamicAreaCode: null,
borderRadius: '0px'
}
},
computed: {
trackBarStyleTime() {
return this.trackBarStyle
}
},
watch: {
chart: {
handler(newVal, oldVla) {
this.preDraw()
},
deep: true
},
resize() {
this.drawEcharts()
}
},
mounted() {
this.preDraw()
},
methods: {
preDraw() {
// domecharts
// echartdom,idechart id
new Promise((resolve) => { resolve() }).then(() => {
// domechartsdom
// this.myChart = this.$echarts.getInstanceByDom(document.getElementById(this.chartId))
// if (!this.myChart) {
// this.myChart = this.$echarts.init(document.getElementById(this.chartId))
// }
this.drawEcharts()
// this.myChart.off('click')
// this.myChart.on('click', function(param) {
// that.pointParam = param
// if (that.trackMenu.length < 2) { //
// that.trackClick(that.trackMenu[0])
// } else { //
// that.trackBarStyle.left = param.event.offsetX + 'px'
// that.trackBarStyle.top = (param.event.offsetY - 15) + 'px'
// that.$refs.viewTrack.trackButtonClick()
// }
// })
})
},
drawEcharts() {
const chart = this.chart
// type
if (chart.type === 'liquid') {
this.myChart = baseLiquid(this.myChart, this.chartId, chart)
}
this.setBackGroundBorder()
// console.log(JSON.stringify(chart_option))
},
// myEcharts(option) {
// //
// const chart = this.myChart
// this.setBackGroundBorder()
// setTimeout(chart.setOption(option, true), 500)
// window.onresize = function() {
// chart.resize()
// }
// },
setBackGroundBorder() {
if (this.chart.customStyle) {
const customStyle = JSON.parse(this.chart.customStyle)
if (customStyle.background) {
this.borderRadius = (customStyle.background.borderRadius || 0) + 'px'
}
}
},
chartResize() {
//
// const chart = this.myChart
// chart.resize()
},
trackClick(trackAction) {
const param = this.pointParam
if (!param || !param.data || !param.data.dimensionList) {
//
if (this.chart.type === 'map') {
this.$warning(this.$t('panel.no_drill_field'))
}
return
}
const linkageParam = {
viewId: this.chart.id,
dimensionList: this.pointParam.data.dimensionList,
quotaList: this.pointParam.data.quotaList
}
switch (trackAction) {
case 'drill':
this.$emit('onChartClick', this.pointParam)
break
case 'linkage':
this.$store.commit('addViewTrackFilter', linkageParam)
break
default:
break
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -24,14 +24,14 @@
<el-form-item :label="$t('chart.text_color')" class="form-item">
<el-color-picker v-model="titleForm.color" class="color-picker-style" @change="changeTitleStyle" />
</el-form-item>
<el-form-item :label="$t('chart.text_h_position')" class="form-item">
<el-form-item v-show="chart.type && chart.type !== 'liquid'" :label="$t('chart.text_h_position')" class="form-item">
<el-radio-group v-model="titleForm.hPosition" size="mini" @change="changeTitleStyle">
<el-radio-button label="left">{{ $t('chart.text_pos_left') }}</el-radio-button>
<el-radio-button label="center">{{ $t('chart.text_pos_center') }}</el-radio-button>
<el-radio-button label="right">{{ $t('chart.text_pos_right') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="chart.type && !chart.type.includes('table')" :label="$t('chart.text_v_position')" class="form-item">
<el-form-item v-show="chart.type && !chart.type.includes('table') && chart.type !== 'liquid'" :label="$t('chart.text_v_position')" class="form-item">
<el-radio-group v-model="titleForm.vPosition" size="mini" @change="changeTitleStyle">
<el-radio-button label="top">{{ $t('chart.text_pos_top') }}</el-radio-button>
<el-radio-button label="center">{{ $t('chart.text_pos_center') }}</el-radio-button>

View File

@ -49,7 +49,45 @@
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item v-show="conf && conf.includes('sort')">
<el-dropdown-item v-show="item.deType === 1">
<el-dropdown placement="right-start" size="mini" style="width: 100%" @command="dateStyle">
<span class="el-dropdown-link inner-dropdown-menu">
<span>
<i class="el-icon-c-scale-to-original" />
<span>{{ $t('chart.dateStyle') }}</span>
<span class="summary-span-item">({{ $t('chart.'+item.dateStyle) }})</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="beforeDateStyle('y')">{{ $t('chart.y') }}</el-dropdown-item>
<el-dropdown-item :command="beforeDateStyle('y_M')">{{ $t('chart.y_M') }}</el-dropdown-item>
<el-dropdown-item :command="beforeDateStyle('y_M_d')">{{ $t('chart.y_M_d') }}</el-dropdown-item>
<el-dropdown-item :command="beforeDateStyle('H_m_s')">{{ $t('chart.H_m_s') }}</el-dropdown-item>
<el-dropdown-item :command="beforeDateStyle('y_M_d_H_m')">{{ $t('chart.y_M_d_H_m') }}</el-dropdown-item>
<el-dropdown-item :command="beforeDateStyle('y_M_d_H_m_s')">{{ $t('chart.y_M_d_H_m_s') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item v-show="item.deType === 1">
<el-dropdown placement="right-start" size="mini" style="width: 100%" @command="datePattern">
<span class="el-dropdown-link inner-dropdown-menu">
<span>
<i class="el-icon-timer" />
<span>{{ $t('chart.datePattern') }}</span>
<span class="summary-span-item">({{ $t('chart.'+item.datePattern) }})</span>
</span>
<i class="el-icon-arrow-right el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="beforeDatePattern('date_sub')">{{ $t('chart.date_sub') }}(1990-01-01)</el-dropdown-item>
<el-dropdown-item :command="beforeDatePattern('date_split')">{{ $t('chart.date_split') }}(1990/01/01)</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
<el-dropdown-item v-show="conf && conf.includes('sort')" :divided="item.deType === 1">
<el-dropdown placement="right-start" size="mini" style="width: 100%" @command="sort">
<span class="el-dropdown-link inner-dropdown-menu">
<span>
@ -143,6 +181,27 @@ export default {
removeItem() {
this.item.index = this.index
this.$emit('onItemRemove', this.item)
},
dateStyle(param) {
// console.log(param)
this.item.dateStyle = param.type
this.$emit('onItemChange', this.item)
},
beforeDateStyle(type) {
return {
type: type
}
},
datePattern(param) {
// console.log(param)
this.item.datePattern = param.type
this.$emit('onItemChange', this.item)
},
beforeDatePattern(type) {
return {
type: type
}
}
}
}

View File

@ -17,12 +17,12 @@
<el-form-item :label="$t('chart.text_color')" class="form-item">
<el-color-picker v-model="labelForm.color" class="color-picker-style" @change="changeLabelAttr" />
</el-form-item>
<el-form-item :label="$t('chart.label_position')" class="form-item">
<el-form-item v-show="chart.type && chart.type !== 'liquid'" :label="$t('chart.label_position')" class="form-item">
<el-select v-model="labelForm.position" :placeholder="$t('chart.label_position')" @change="changeLabelAttr">
<el-option v-for="option in labelPosition" :key="option.value" :label="option.name" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item class="form-item">
<el-form-item v-show="chart.type && chart.type !== 'liquid'" class="form-item">
<span slot="label">
<span class="span-box">
<span>{{ $t('chart.content_formatter') }}</span>
@ -126,7 +126,7 @@ export default {
},
init() {
const arr = []
for (let i = 10; i <= 20; i = i + 2) {
for (let i = 10; i <= 40; i = i + 2) {
arr.push({
name: i + '',
value: i + ''

View File

@ -165,6 +165,7 @@
</el-form>
<el-form v-show="chart.type && chart.type === 'chart-mix'" ref="sizeFormBar" :disabled="param && !hasDataPermission('manage',param.privileges)" :model="sizeForm" label-width="80px" size="mini">
<el-divider content-position="center" class="divider-style">{{ $t('chart.chart_bar') }}</el-divider>
<el-form-item :label="$t('chart.adapt')" class="form-item">
<el-checkbox v-model="sizeForm.barDefault" @change="changeBarSizeCase">{{ $t('chart.adapt') }}</el-checkbox>
</el-form-item>
@ -174,7 +175,7 @@
<el-form-item :label="$t('chart.bar_gap')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.barGap" :disabled="sizeForm.barDefault" show-input :show-input-controls="false" input-size="mini" :min="0" :max="5" :step="0.1" @change="changeBarSizeCase" />
</el-form-item>
<el-divider />
<el-divider content-position="center" class="divider-style">{{ $t('chart.chart_line') }}</el-divider>
<el-form-item :label="$t('chart.line_width')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.lineWidth" show-input :show-input-controls="false" input-size="mini" :min="0" :max="10" @change="changeBarSizeCase" />
</el-form-item>
@ -203,7 +204,7 @@
<el-form-item :label="$t('chart.line_area')" class="form-item">
<el-checkbox v-model="sizeForm.lineArea" @change="changeBarSizeCase">{{ $t('chart.show') }}</el-checkbox>
</el-form-item>
<el-divider />
<el-divider content-position="center" class="divider-style">{{ $t('chart.chart_scatter') }}</el-divider>
<el-form-item :label="$t('chart.bubble_symbol')" class="form-item">
<el-select v-model="sizeForm.scatterSymbol" :placeholder="$t('chart.line_symbol')" @change="changeBarSizeCase">
<el-option
@ -218,6 +219,37 @@
<el-slider v-model="sizeForm.scatterSymbolSize" show-input :show-input-controls="false" input-size="mini" :min="1" :max="40" @change="changeBarSizeCase" />
</el-form-item>
</el-form>
<el-form v-show="chart.type && chart.type === 'liquid'" ref="sizeFormLine" :disabled="param && !hasDataPermission('manage',param.privileges)" :model="sizeForm" label-width="80px" size="mini">
<el-form-item :label="$t('chart.liquid_shape')" class="form-item">
<el-select v-model="sizeForm.liquidShape" :placeholder="$t('chart.liquid_shape')" @change="changeBarSizeCase">
<el-option
v-for="item in liquidShapeOptions"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('chart.liquid_max')" class="form-item form-item-slider">
<el-input-number v-model="sizeForm.liquidMax" :min="1" size="mini" @change="changeBarSizeCase" />
</el-form-item>
<el-form-item :label="$t('chart.radar_size')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.liquidSize" show-input :show-input-controls="false" input-size="mini" :min="1" :max="100" @change="changeBarSizeCase" />
</el-form-item>
<el-form-item :label="$t('chart.liquid_outline_border')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.liquidOutlineBorder" show-input :show-input-controls="false" input-size="mini" :min="1" :max="20" @change="changeBarSizeCase" />
</el-form-item>
<el-form-item :label="$t('chart.liquid_outline_distance')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.liquidOutlineDistance" show-input :show-input-controls="false" input-size="mini" :min="0" :max="20" @change="changeBarSizeCase" />
</el-form-item>
<el-form-item :label="$t('chart.liquid_wave_length')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.liquidWaveLength" show-input :show-input-controls="false" input-size="mini" :min="10" :max="500" @change="changeBarSizeCase" />
</el-form-item>
<el-form-item :label="$t('chart.liquid_wave_count')" class="form-item form-item-slider">
<el-slider v-model="sizeForm.liquidWaveCount" show-input :show-input-controls="false" input-size="mini" :min="2" :max="10" @change="changeBarSizeCase" />
</el-form-item>
</el-form>
</el-col>
</div>
</template>
@ -250,6 +282,13 @@ export default {
{ name: this.$t('chart.line_symbol_pin'), value: 'pin' },
{ name: this.$t('chart.line_symbol_arrow'), value: 'arrow' }
],
liquidShapeOptions: [
{ name: this.$t('chart.liquid_shape_circle'), value: 'circle' },
{ name: this.$t('chart.liquid_shape_diamond'), value: 'diamond' },
{ name: this.$t('chart.liquid_shape_triangle'), value: 'triangle' },
{ name: this.$t('chart.liquid_shape_pin'), value: 'pin' },
{ name: this.$t('chart.liquid_shape_rect'), value: 'rect' }
],
fontSize: []
}
},
@ -279,6 +318,14 @@ export default {
this.sizeForm.treemapWidth = this.sizeForm.treemapWidth ? this.sizeForm.treemapWidth : 80
this.sizeForm.treemapHeight = this.sizeForm.treemapHeight ? this.sizeForm.treemapHeight : 80
this.sizeForm.radarSize = this.sizeForm.radarSize ? this.sizeForm.radarSize : 80
this.sizeForm.liquidShape = this.sizeForm.liquidShape ? this.sizeForm.liquidShape : DEFAULT_SIZE.liquidShape
this.sizeForm.liquidMax = this.sizeForm.liquidMax ? this.sizeForm.liquidMax : DEFAULT_SIZE.liquidMax
this.sizeForm.liquidSize = this.sizeForm.liquidSize ? this.sizeForm.liquidSize : DEFAULT_SIZE.liquidSize
this.sizeForm.liquidOutlineBorder = this.sizeForm.liquidOutlineBorder ? this.sizeForm.liquidOutlineBorder : DEFAULT_SIZE.liquidOutlineBorder
this.sizeForm.liquidOutlineDistance = (this.sizeForm.liquidOutlineDistance || this.sizeForm.liquidOutlineDistance === 0) ? this.sizeForm.liquidOutlineDistance : DEFAULT_SIZE.liquidOutlineDistance
this.sizeForm.liquidWaveLength = this.sizeForm.liquidWaveLength ? this.sizeForm.liquidWaveLength : DEFAULT_SIZE.liquidWaveLength
this.sizeForm.liquidWaveCount = this.sizeForm.liquidWaveCount ? this.sizeForm.liquidWaveCount : DEFAULT_SIZE.liquidWaveCount
}
}
},
@ -326,4 +373,10 @@ export default {
.el-divider--horizontal {
margin: 10px 0
}
.divider-style>>>.el-divider__text{
color: #606266;
font-size: 12px;
font-weight: 400;
padding: 0 10px;
}
</style>

View File

@ -33,7 +33,7 @@
<el-row v-show="chart.type === 'table-info'" class="table-page">
<span class="total-style">
{{ $t('chart.total') }}
<span>{{ chart.data.tableRow.length }}</span>
<span>{{ (chart.data && chart.data.tableRow)?chart.data.tableRow.length:0 }}</span>
{{ $t('chart.items') }}
</span>
<el-pagination

View File

@ -189,18 +189,23 @@
<svg-icon icon-class="radar" class="chart-icon" />
</span>
</el-radio>
<el-radio value="liquid" label="liquid">
<span :title="$t('chart.chart_liquid')">
<svg-icon icon-class="liquid" class="chart-icon" />
</span>
</el-radio>
<el-radio value="gauge" label="gauge">
<span :title="$t('chart.chart_gauge')">
<svg-icon icon-class="gauge" class="chart-icon" />
</span>
</el-radio>
</div>
<div style="width: 100%;display: flex;display: -webkit-flex;justify-content: space-between;flex-direction: row;flex-wrap: wrap;">
<el-radio value="pie" label="pie">
<span :title="$t('chart.chart_pie')">
<svg-icon icon-class="pie" class="chart-icon" />
</span>
</el-radio>
</div>
<div style="width: 100%;display: flex;display: -webkit-flex;justify-content: space-between;flex-direction: row;flex-wrap: wrap;">
<el-radio value="pie-rose" label="pie-rose">
<span :title="$t('chart.chart_pie_rose')">
<svg-icon icon-class="pie-rose" class="chart-icon" />
@ -217,8 +222,14 @@
</span>
</el-radio>
<el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>
<el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>
</div>
<!-- <div style="width: 100%;display: flex;display: -webkit-flex;justify-content: space-between;flex-direction: row;flex-wrap: wrap;">-->
<!-- <el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>-->
<!-- <el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>-->
<!-- <el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>-->
<!-- <el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>-->
<!-- <el-radio value="" label="" disabled class="disabled-none-cursor"><svg-icon icon-class="" class="chart-icon" /></el-radio>-->
<!-- </div>-->
</el-radio-group>
</div>
</el-row>
@ -254,7 +265,7 @@
/>
</span>
</el-row>
<el-row v-if="view.type !=='text' && view.type !== 'gauge'" class="padding-lr">
<el-row v-if="view.type !=='text' && view.type !== 'gauge' && view.type !== 'liquid'" class="padding-lr">
<span style="width: 80px;text-align: right;">
<span v-if="view.type && view.type.includes('table')">{{ $t('chart.drag_block_table_data_column') }}</span>
<span v-else-if="view.type && (view.type.includes('bar') || view.type.includes('line') || view.type.includes('scatter') || view.type === 'chart-mix')">{{ $t('chart.drag_block_type_axis') }}</span>
@ -296,6 +307,7 @@
<span v-else-if="view.type && view.type === 'map'">{{ $t('chart.chart_data') }}</span>
<span v-else-if="view.type && view.type.includes('tree')">{{ $t('chart.drag_block_treemap_size') }}</span>
<span v-else-if="view.type && view.type === 'chart-mix'">{{ $t('chart.drag_block_value_axis_main') }}</span>
<span v-else-if="view.type && view.type === 'liquid'">{{ $t('chart.drag_block_progress') }}</span>
/
<span>{{ $t('chart.quota') }}</span>
</span>
@ -418,7 +430,7 @@
<span class="drag-placeholder-style-span">{{ $t('chart.placeholder_field') }}</span>
</div>
</el-row>
<el-row v-if="view.type && view.type !== 'treemap' && !view.type.includes('table') && !view.type.includes('text') && !view.type.includes('radar') && !view.type.includes('gauge')" class="padding-lr" style="margin-top: 6px;">
<el-row v-if="view.type && view.type !== 'treemap' && !view.type.includes('table') && !view.type.includes('text') && !view.type.includes('radar') && !view.type.includes('gauge') && view.type !== 'liquid'" class="padding-lr" style="margin-top: 6px;">
<span style="width: 80px;text-align: right;">
<span>{{ $t('chart.drill') }}</span>
/
@ -471,7 +483,7 @@
<el-collapse-item v-show="!view.type.includes('table') && !view.type.includes('text')" name="label" :title="$t('chart.label')">
<label-selector :param="param" class="attr-selector" :chart="chart" @onLabelChange="onLabelChange" />
</el-collapse-item>
<el-collapse-item v-show="!view.type.includes('table') && !view.type.includes('text')" name="tooltip" :title="$t('chart.tooltip')">
<el-collapse-item v-show="!view.type.includes('table') && !view.type.includes('text') && view.type !== 'liquid'" name="tooltip" :title="$t('chart.tooltip')">
<tooltip-selector :param="param" class="attr-selector" :chart="chart" @onTooltipChange="onTooltipChange" />
</el-collapse-item>
</el-collapse>
@ -491,10 +503,10 @@
<el-collapse-item v-show="view.type && view.type.includes('radar')" name="split" :title="$t('chart.split')">
<split-selector :param="param" class="attr-selector" :chart="chart" @onChangeSplitForm="onChangeSplitForm" />
</el-collapse-item>
<el-collapse-item name="title" :title="$t('chart.title')">
<el-collapse-item v-show="view.type" name="title" :title="$t('chart.title')">
<title-selector :param="param" class="attr-selector" :chart="chart" @onTextChange="onTextChange" />
</el-collapse-item>
<el-collapse-item v-show="view.type && view.type !== 'map' && !view.type.includes('table') && !view.type.includes('text') && chart.type !== 'treemap'" name="legend" :title="$t('chart.legend')">
<el-collapse-item v-show="view.type && view.type !== 'map' && !view.type.includes('table') && !view.type.includes('text') && chart.type !== 'treemap' && view.type !== 'liquid'" name="legend" :title="$t('chart.legend')">
<legend-selector :param="param" class="attr-selector" :chart="chart" @onLegendChange="onLegendChange" />
</el-collapse-item>
<el-collapse-item name="background" :title="$t('chart.background')">
@ -510,7 +522,8 @@
<el-col style="height: 100%;min-width: 500px;border-top: 1px solid #E6E6E6;">
<el-row style="width: 100%;height: 100%;" class="padding-lr">
<div ref="imageWrapper" style="height: 100%">
<chart-component v-if="httpRequest.status && chart.type && !chart.type.includes('table') && !chart.type.includes('text')" ref="dynamicChart" :chart-id="chart.id" :chart="chart" class="chart-class" @onChartClick="chartClick" />
<chart-component v-if="httpRequest.status && chart.type && !chart.type.includes('table') && !chart.type.includes('text') && renderComponent() === 'echarts'" ref="dynamicChart" :chart-id="chart.id" :chart="chart" class="chart-class" @onChartClick="chartClick" />
<chart-component-g2 v-if="httpRequest.status && chart.type && !chart.type.includes('table') && !chart.type.includes('text') && renderComponent() === 'g2'" ref="dynamicChart" :chart-id="chart.id" :chart="chart" class="chart-class" />
<table-normal v-if="httpRequest.status && chart.type && chart.type.includes('table')" :show-summary="chart.type === 'table-normal'" :chart="chart" class="table-class" />
<label-normal v-if="httpRequest.status && chart.type && chart.type.includes('text')" :chart="chart" class="table-class" />
<div v-if="!httpRequest.status" class="chart-error-class">
@ -670,9 +683,11 @@ import FieldEdit from '../../dataset/data/FieldEdit'
import { areaMapping } from '@/api/map/map'
import QuotaExtItem from '@/views/chart/components/drag-item/QuotaExtItem'
import YAxisExtSelector from '@/views/chart/components/component-style/YAxisExtSelector'
import ChartComponentG2 from '@/views/chart/components/ChartComponentG2'
export default {
name: 'ChartEdit',
components: {
ChartComponentG2,
YAxisExtSelector,
QuotaExtItem,
FilterItem,
@ -934,6 +949,12 @@ export default {
})
}
view.extStack.forEach(function(ele) {
if (!ele.dateStyle || ele.dateStyle === '') {
ele.dateStyle = 'y_M_d'
}
if (!ele.datePattern || ele.datePattern === '') {
ele.datePattern = 'date_sub'
}
if (!ele.sort || ele.sort === '') {
ele.sort = 'none'
}
@ -1043,6 +1064,8 @@ export default {
}
if (!response.data.drill) {
this.drillClickDimensionList.splice(this.drillClickDimensionList.length - 1, 1)
this.resetDrill()
}
this.drillFilters = JSON.parse(JSON.stringify(response.data.drillFilters ? response.data.drillFilters : []))
}).catch(err => {
@ -1593,6 +1616,14 @@ export default {
if (temp) return temp
}
}
},
renderComponent() {
if (this.chart.type === 'liquid') {
return 'g2'
} else {
return 'echarts'
}
}
}

View File

@ -15,11 +15,11 @@
{{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }}
</div>
<div class="login-form">
<el-form-item>
<el-radio-group v-model="loginForm.loginType">
<el-radio v-if="openLdap" :label="0" size="mini">普通登录</el-radio>
<el-radio v-if="openLdap" :label="1" size="mini">LDAP</el-radio>
<el-form-item v-if="loginTypes.length > 1">
<el-radio-group v-if="loginTypes.length > 1" v-model="loginForm.loginType" @change="changeLoginType">
<el-radio :label="0" size="mini">普通登录</el-radio>
<el-radio v-if="loginTypes.includes(1)" :label="1" size="mini">LDAP</el-radio>
<el-radio v-if="loginTypes.includes(2)" :label="2" size="mini">OIDC</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="username">
@ -56,41 +56,20 @@
</el-col>
</el-row>
</div>
<plugin-com v-if="loginTypes.includes(2) && loginForm.loginType === 2" ref="SSOComponent" component-name="SSOComponent" />
</div>
</template>
<script>
import { encrypt } from '@/utils/rsaEncrypt'
import { ldapStatus } from '@/api/user'
import { ldapStatus, oidcStatus } from '@/api/user'
import { getSysUI } from '@/utils/auth'
import PluginCom from '@/views/system/plugin/PluginCom'
export default {
name: 'Login',
components: { PluginCom },
data() {
// const validateUsername = (rule, value, callback) => {
// const userName = value.trim()
// validateUserName({ userName: userName }).then(res => {
// if (res.data) {
// callback()
// } else {
// callback(this.$t('login.username_error'))
// }
// }).catch(() => {
// callback(this.$t('login.username_error'))
// })
// // if (!validUsername(value)) {
// // callback(new Error('Please enter the correct user name'))
// // } else {
// // callback()
// // }
// }
// const validatePassword = (rule, value, callback) => {
// if (value.length < 8) {
// callback(this.$t('login.password_error'))
// } else {
// callback()
// }
// }
return {
loginForm: {
loginType: 0,
@ -108,7 +87,7 @@ export default {
loginImageUrl: null,
loginLogoUrl: null,
axiosFinished: false,
openLdap: true
loginTypes: [0]
}
},
computed: {
@ -126,7 +105,15 @@ export default {
},
beforeCreate() {
ldapStatus().then(res => {
this.openLdap = res.success && res.data
if (res.success && res.data) {
this.loginTypes.push(1)
}
})
oidcStatus().then(res => {
if (res.success && res.data) {
this.loginTypes.push(2)
}
})
},
created() {
@ -169,6 +156,12 @@ export default {
return false
}
})
},
changeLoginType(val) {
if (val !== 2) return
this.$nextTick(() => {
})
}
}
}

View File

@ -96,7 +96,6 @@
<div
id="canvasInfo"
:class="{'style-hidden':canvasStyleData.selfAdaption}"
class="content this_canvas"
@drop="handleDrop"
@dragover="handleDragOver"
@ -839,7 +838,7 @@ export default {
}
}
.style-hidden{
overflow: hidden;
overflow-x: hidden;
}
</style>

View File

@ -1,6 +1,41 @@
<template xmlns:el-col="http://www.w3.org/1999/html">
<el-col style="padding: 0 5px 0 5px;">
<el-col>
<el-row style="margin-bottom: 10px">
<el-col :span="16">
<el-input
v-model="filterText"
size="mini"
:placeholder="$t('commons.search')"
prefix-icon="el-icon-search"
clearable
/>
</el-col>
<el-col :span="8">
<el-dropdown>
<el-button size="mini" type="primary">
{{ searchMap[searchType] }}<i class="el-icon-arrow-down el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="searchTypeClick('all')">全部</el-dropdown-item>
<el-dropdown-item @click.native="searchTypeClick('folder')">目录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <el-select-->
<!-- v-model="searchType"-->
<!-- default-first-option-->
<!-- size="mini"-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="item in searchTypeList"-->
<!-- :key="item.value"-->
<!-- :label="item.label"-->
<!-- :value="item.value"-->
<!-- />-->
<!-- </el-select>-->
</el-col>
</el-row>
<el-row>
<span class="header-title">{{ $t('panel.default_panel') }}</span>
<div class="block">
@ -11,6 +46,7 @@
node-key="id"
:highlight-current="activeTree==='system'"
:expand-on-click-node="true"
:filter-node-method="filterNode"
@node-click="nodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node father">
@ -59,6 +95,7 @@
node-key="id"
:highlight-current="activeTree==='self'"
:expand-on-click-node="true"
:filter-node-method="filterNode"
@node-click="nodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node-list father">
@ -294,7 +331,14 @@ export default {
moveDialogTitle: '',
moveInfo: {},
tGroup: {},
tGroupData: [] //
tGroupData: [], //
searchPids: [], // pid
filterText: '',
searchType: 'all',
searchMap: {
all: this.$t('commons.all'),
folder: this.$t('commons.folder')
}
}
},
computed: {
@ -311,6 +355,16 @@ export default {
if (newVal === 'PanelMain' && this.lastActiveNode && this.lastActiveNodeData) {
this.activeNodeAndClickOnly(this.lastActiveNodeData)
}
},
filterText(val) {
this.searchPids = []
this.$refs.default_panel_tree.filter(val)
this.$refs.panel_list_tree.filter(val)
},
searchType(val) {
this.searchPids = []
this.$refs.default_panel_tree.filter(this.filterText)
this.$refs.panel_list_tree.filter(this.filterText)
}
},
mounted() {
@ -710,6 +764,27 @@ export default {
targetGroup(val) {
this.tGroup = val
this.groupMoveConfirmDisabled = false
},
filterNode(value, data) {
if (!value) return true
if (this.searchType === 'folder') {
if (data.nodeType === 'folder' && data.label.indexOf(value) !== -1) {
this.searchPids.push(data.id)
return true
}
if (this.searchPids.indexOf(data.pid) !== -1) {
if (data.nodeType === 'folder') {
this.searchPids.push(data.id)
}
return true
}
} else {
return data.label.indexOf(value) !== -1
}
return false
},
searchTypeClick(searchTypeInfo) {
this.searchType = searchTypeInfo
}
}
}

View File

@ -56,7 +56,7 @@
<el-row class="panel-design-preview">
<div id="imageWrapper" ref="imageWrapper" style="width: 100%;height: 100%">
<fullscreen style="height: 100%;background: #f7f8fa;overflow-y: auto" :fullscreen.sync="fullscreen">
<Preview v-if="showMain" :in-screen="!fullscreen" :show-type="canvasStyleData.selfAdaption?'full':'width'" />
<Preview v-if="showMain" :in-screen="!fullscreen" :show-type="'width'" />
</fullscreen>
</div>
</el-row>

View File

@ -0,0 +1,69 @@
<template>
<div>
<async-component v-if="showAsync" :url="url" @execute-axios="executeAxios" @on-add-languanges="addLanguages" @plugin-call-back="pluginCallBack" />
<div v-else>
<h1>未知组件无法展示</h1>
</div>
</div>
</template>
<script>
import AsyncComponent from '@/components/AsyncComponent'
import i18n from '@/lang'
import bus from '@/utils/bus'
import { execute } from '@/api/system/dynamic'
export default {
name: 'PluginCom',
components: {
AsyncComponent
},
props: {
componentName: {
type: String,
default: null
}
},
data() {
return {
showAsync: false,
baseUrl: '/api/pluginCommon/component/',
url: null
}
},
created() {
if (this.componentName) {
this.showAsync = true
this.url = this.baseUrl + this.componentName
} else {
this.showAsync = false
}
},
methods: {
// hasLicense
executeAxios(options) {
execute(options).then(res => {
if (options.callBack) {
options.callBack(res)
}
}).catch(e => {
if (options.callBack) {
options.callBack(e)
}
})
},
addLanguages(options) {
for (const key in i18n.messages) {
if (Object.hasOwnProperty.call(i18n.messages, key)) {
const element = options[key]
i18n.mergeLocaleMessage(key, element)
}
}
},
pluginCallBack(param) {
const { eventName, eventParam } = param
bus.$emit(eventName, eventParam)
}
}
}
</script>

View File

@ -22,7 +22,7 @@
<!-- <el-table-column prop="gender" :label="$t('commons.gender')" width="60" /> -->
<el-table-column prop="from" :label="$t('user.source')" width="80">
<template slot-scope="scope">
<div>{{ scope.row.from === 0 ? 'LOCAL' : 'LDAP' }}</div>
<div>{{ scope.row.from === 0 ? 'LOCAL' : scope.row.from === 1 ? 'LDAP' : 'OIDC' }}</div>
</template>
</el-table-column>