diff --git a/backend/pom.xml b/backend/pom.xml index 98a85d5f11..2ec6c7ff5e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -256,6 +256,11 @@ selenium-java + + org.jsoup + jsoup + 1.14.3 + @@ -415,10 +420,10 @@ - + - - + + diff --git a/backend/src/main/java/io/dataease/auth/api/AuthApi.java b/backend/src/main/java/io/dataease/auth/api/AuthApi.java index 7f2fe37636..12de6b0738 100644 --- a/backend/src/main/java/io/dataease/auth/api/AuthApi.java +++ b/backend/src/main/java/io/dataease/auth/api/AuthApi.java @@ -13,31 +13,27 @@ import org.springframework.web.bind.annotation.RequestMapping; import java.util.Map; - @Api(tags = "权限:权限管理") @ApiSupport(order = 10) @RequestMapping("/api/auth") public interface AuthApi { - @ApiOperation("登录") @PostMapping("/login") Object login(LoginDto loginDto) throws Exception; - @ApiOperation("获取用户信息") @PostMapping("/userInfo") CurrentUserDto userInfo(); - - - + @ApiOperation("是否使用初始密码") + @PostMapping("/useInitPwd") + Boolean useInitPwd(); @ApiOperation("登出") @PostMapping("/logout") String logout(); - @ApiOperation("验证账号") @PostMapping("/validateName") Boolean validateName(Map nameDto); @@ -46,7 +42,6 @@ public interface AuthApi { @PostMapping("/isOpenLdap") boolean isOpenLdap(); - @ApiOperation("是否开启oidc") @PostMapping("/isOpenOidc") boolean isOpenOidc(); diff --git a/backend/src/main/java/io/dataease/auth/server/AuthServer.java b/backend/src/main/java/io/dataease/auth/server/AuthServer.java index 6d03a46028..9f1332b8fa 100644 --- a/backend/src/main/java/io/dataease/auth/server/AuthServer.java +++ b/backend/src/main/java/io/dataease/auth/server/AuthServer.java @@ -27,6 +27,7 @@ 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.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -40,6 +41,9 @@ import javax.servlet.http.HttpServletRequest; @RestController public class AuthServer implements AuthApi { + @Value("${dataease.init_password:DataEase123..}") + private String DEFAULT_PWD; + @Autowired private AuthUserService authUserService; @@ -65,14 +69,19 @@ public class AuthServer implements AuthApi { SysUserEntity user = authUserService.getLdapUserByName(username); if (ObjectUtils.isEmpty(user) || ObjectUtils.isEmpty(user.getUserId())) { LdapAddRequest ldapAddRequest = new LdapAddRequest(); - ldapAddRequest.setUsers(new ArrayList() {{ - add(ldapUserEntity); - }}); + ldapAddRequest.setUsers(new ArrayList() { + { + add(ldapUserEntity); + } + }); ldapAddRequest.setEnabled(1L); - ldapAddRequest.setRoleIds(new ArrayList() {{ - add(2L); - }}); - sysUserService.validateExistUser(ldapUserEntity.getUsername(), ldapUserEntity.getNickname(), ldapUserEntity.getEmail()); + ldapAddRequest.setRoleIds(new ArrayList() { + { + add(2L); + } + }); + sysUserService.validateExistUser(ldapUserEntity.getUsername(), ldapUserEntity.getNickname(), + ldapUserEntity.getEmail()); sysUserService.saveLdapUsers(ldapAddRequest); } @@ -92,9 +101,9 @@ public class AuthServer implements AuthApi { // 普通登录需要验证密码 if (loginType == 0 || !isSupportLdap) { - //私钥解密 + // 私钥解密 - //md5加密 + // md5加密 pwd = CodingUtil.md5(pwd); if (!StringUtils.equals(pwd, realPwd)) { @@ -128,6 +137,16 @@ public class AuthServer implements AuthApi { return userDto; } + @Override + public Boolean useInitPwd() { + CurrentUserDto user = AuthUtils.getUser(); + if (null == user) { + return false; + } + String md5 = CodingUtil.md5(DEFAULT_PWD); + return StringUtils.equals(AuthUtils.getUser().getPassword(), md5); + } + @Override public String logout() { String token = ServletUtils.getToken(); @@ -158,7 +177,8 @@ public class AuthServer implements AuthApi { @Override public Boolean validateName(@RequestBody Map nameDto) { String userName = nameDto.get("userName"); - if (StringUtils.isEmpty(userName)) return false; + if (StringUtils.isEmpty(userName)) + return false; SysUserEntity userEntity = authUserService.getUserByName(userName); return !ObjectUtils.isEmpty(userEntity); } @@ -166,29 +186,30 @@ public class AuthServer implements AuthApi { @Override public boolean isOpenLdap() { Boolean licValid = PluginUtils.licValid(); - if (!licValid) return false; + if (!licValid) + return false; return authUserService.supportLdap(); } @Override public boolean isOpenOidc() { Boolean licValid = PluginUtils.licValid(); - if (!licValid) return false; + if (!licValid) + return false; return authUserService.supportOidc(); } @Override public boolean isPluginLoaded() { Boolean licValid = PluginUtils.licValid(); - if (!licValid) return false; + if (!licValid) + return false; return authUserService.pluginLoaded(); } - @Override public String getPublicKey() { return RsaProperties.publicKey; } - } diff --git a/backend/src/main/java/io/dataease/auth/service/impl/DynamicMenuServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/DynamicMenuServiceImpl.java index 029c8e7aaf..bb33a2cc6c 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/DynamicMenuServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/DynamicMenuServiceImpl.java @@ -7,6 +7,7 @@ import io.dataease.base.domain.SysMenu; import io.dataease.base.domain.SysMenuExample; import io.dataease.base.mapper.SysMenuMapper; import io.dataease.base.mapper.ext.ExtPluginSysMenuMapper; +import io.dataease.base.mapper.ext.ExtSysMenuMapper; import io.dataease.plugins.common.dto.PluginSysMenu; import io.dataease.plugins.util.PluginUtils; import org.apache.commons.collections4.CollectionUtils; @@ -30,12 +31,16 @@ public class DynamicMenuServiceImpl implements DynamicMenuService { @Resource private ExtPluginSysMenuMapper extPluginSysMenuMapper; + @Resource + private ExtSysMenuMapper extSysMenuMapper; + @Override public List load(String userId) { - SysMenuExample sysMenuExample = new SysMenuExample(); - sysMenuExample.createCriteria().andTypeLessThanOrEqualTo(1); - sysMenuExample.setOrderByClause(" menu_sort "); - List sysMenus = sysMenuMapper.selectByExample(sysMenuExample); +// SysMenuExample sysMenuExample = new SysMenuExample(); +// sysMenuExample.createCriteria().andTypeLessThanOrEqualTo(1); +// sysMenuExample.setOrderByClause(" menu_sort "); +// List sysMenus = sysMenuMapper.selectByExample(sysMenuExample); + List sysMenus = extSysMenuMapper.querySysMenu(); List dynamicMenuDtos = sysMenus.stream().map(this::convert).collect(Collectors.toList()); //增加插件中的菜单 List pluginSysMenus = PluginUtils.pluginMenus(); diff --git a/backend/src/main/java/io/dataease/auth/util/JWTUtils.java b/backend/src/main/java/io/dataease/auth/util/JWTUtils.java index 9e27662bf1..28072efd05 100644 --- a/backend/src/main/java/io/dataease/auth/util/JWTUtils.java +++ b/backend/src/main/java/io/dataease/auth/util/JWTUtils.java @@ -17,16 +17,13 @@ import org.springframework.core.env.Environment; import java.util.Date; - public class JWTUtils { - // token过期时间1min (过期会自动刷新续命 目的是避免一直都是同一个token ) private static final long EXPIRE_TIME = 1 * 60 * 1000; // 登录间隔时间10min 超过这个时间强制重新登录 private static long Login_Interval; - /** * 校验token是否正确 * @@ -82,7 +79,8 @@ public class JWTUtils { public static boolean loginExpire(String token) { if (Login_Interval == 0) { // 默认超时时间是8h - int minute = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Integer.class, 8 * 60); + Long minute = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class, + 8 * 60L); // 分钟换算成毫秒 Login_Interval = minute * 1000 * 60; } @@ -128,19 +126,19 @@ public class JWTUtils { public static String signLink(String resourceId, Long userId, String secret) { Algorithm algorithm = Algorithm.HMAC256(secret); - if(userId == null){ + if (userId == null) { return JWT.create().withClaim("resourceId", resourceId).sign(algorithm); - }else { + } else { return JWT.create().withClaim("resourceId", resourceId).withClaim("userId", userId).sign(algorithm); } } - public static boolean verifyLink(String token, String resourceId, Long userId, String secret) { + public static boolean verifyLink(String token, String resourceId, Long userId, String secret) { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier; - if(userId == null){ + if (userId == null) { verifier = JWT.require(algorithm).withClaim("resourceId", resourceId).build(); - }else { + } else { verifier = JWT.require(algorithm).withClaim("resourceId", resourceId).withClaim("userId", userId).build(); } diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.java b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.java index 147e44e607..3620bab9e8 100644 --- a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.java +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.java @@ -1,5 +1,6 @@ package io.dataease.base.mapper.ext; +import io.dataease.base.domain.SysMenu; import io.dataease.base.mapper.ext.query.GridExample; import io.dataease.controller.sys.request.SimpleTreeNode; @@ -10,4 +11,6 @@ public interface ExtSysMenuMapper { List allNodes(); List nodesByExample(GridExample example); + + List querySysMenu(); } diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.xml b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.xml index 56e79e0b8a..1d65075622 100644 --- a/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.xml +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtSysMenuMapper.xml @@ -7,6 +7,8 @@ + + @@ -19,4 +21,27 @@ + + diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.java b/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.java new file mode 100644 index 0000000000..efec50afcf --- /dev/null +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.java @@ -0,0 +1,9 @@ +package io.dataease.base.mapper.ext; + +public interface ExtTaskMapper { + + int runningCount(Long taskId); + + void resetRunnings(Long taskId); + +} diff --git a/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.xml b/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.xml new file mode 100644 index 0000000000..213d5ae488 --- /dev/null +++ b/backend/src/main/java/io/dataease/base/mapper/ext/ExtTaskMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + update sys_task_instance set status = -1, info = 'System Interrupt Error' where task_id = #{taskId} and status = 0 + + + diff --git a/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolExecutor.java b/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolExecutor.java new file mode 100644 index 0000000000..d55d9e8a65 --- /dev/null +++ b/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolExecutor.java @@ -0,0 +1,113 @@ +package io.dataease.commons.pool; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import io.dataease.commons.utils.LogUtil; + +public class PriorityThreadPoolExecutor extends ThreadPoolExecutor { + + public static AtomicInteger globaInteger = new AtomicInteger(1); + + private ThreadLocal local = new ThreadLocal() { + @Override + protected Integer initialValue() { + return 1; + } + }; + + public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue()); + } + + public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + ThreadFactory threadFactory) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), threadFactory); + } + + public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), handler); + } + + public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + ThreadFactory threadFactory, RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), threadFactory, handler); + } + + protected static PriorityBlockingQueue getWorkQueue() { + return new PriorityBlockingQueue(); + } + + @Override + public void execute(Runnable command) { + int andIncrement = globaInteger.getAndIncrement(); + Integer theadInteger = local.get(); + try { + if (theadInteger == 0) { + this.execute(command, 0); + } else { + this.execute(command, andIncrement); + } + + } finally { + local.set(1); + } + } + + public void execute(Runnable command, int priority) { + super.execute(new PriorityRunnable(command, priority)); + } + + public Future submit(Callable task, int priority) { + local.set(priority); + return super.submit(task); + } + + protected static class PriorityRunnable> + implements Runnable, Comparable> { + private final static AtomicLong seq = new AtomicLong(); + private final long seqNum; + Runnable run; + private int priority; + + public PriorityRunnable(Runnable run, int priority) { + seqNum = seq.getAndIncrement(); + this.run = run; + this.priority = priority; + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + public Runnable getRun() { + return run; + } + + @Override + public void run() { + LogUtil.info("number " + priority + " is starting..."); + this.run.run(); + } + + @Override + public int compareTo(PriorityRunnable other) { + int res = 0; + if (this.priority == other.priority) { + if (other.run != this.run) {// ASC + res = (seqNum < other.seqNum ? -1 : 1); + } + } else {// DESC + res = this.priority > other.priority ? 1 : -1; + } + return res; + } + } + +} diff --git a/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolProperties.java b/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolProperties.java new file mode 100644 index 0000000000..efb807ba86 --- /dev/null +++ b/backend/src/main/java/io/dataease/commons/pool/PriorityThreadPoolProperties.java @@ -0,0 +1,17 @@ +package io.dataease.commons.pool; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import lombok.Data; + +@ConfigurationProperties(prefix = "detask", ignoreInvalidFields = true) +@Data +@Component +public class PriorityThreadPoolProperties { + + private int corePoolSize = 2; + private int maximumPoolSize = 100; + private int keepAliveTime = 60; + +} diff --git a/backend/src/main/java/io/dataease/config/AsyncConfig.java b/backend/src/main/java/io/dataease/config/AsyncConfig.java index 22a88ef995..8a8786c294 100644 --- a/backend/src/main/java/io/dataease/config/AsyncConfig.java +++ b/backend/src/main/java/io/dataease/config/AsyncConfig.java @@ -1,15 +1,25 @@ package io.dataease.config; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import io.dataease.commons.pool.PriorityThreadPoolExecutor; +import io.dataease.commons.pool.PriorityThreadPoolProperties; + @EnableAsync(proxyTargetClass = true) @Configuration public class AsyncConfig { + @Resource + private PriorityThreadPoolProperties priorityThreadPoolProperties; + @Bean public AsyncTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); @@ -18,4 +28,18 @@ public class AsyncConfig { executor.setMaxPoolSize(10); return executor; } + + @Bean + public PriorityThreadPoolExecutor priorityExecutor() { + int corePoolSize = priorityThreadPoolProperties.getCorePoolSize(); + + int maximumPoolSize = priorityThreadPoolProperties.getMaximumPoolSize(); + + int keepAliveTime = priorityThreadPoolProperties.getKeepAliveTime(); + + PriorityThreadPoolExecutor executor = new PriorityThreadPoolExecutor(corePoolSize, maximumPoolSize, + keepAliveTime, TimeUnit.SECONDS); + return executor; + } + } diff --git a/backend/src/main/java/io/dataease/controller/wizard/ReptileController.java b/backend/src/main/java/io/dataease/controller/wizard/ReptileController.java new file mode 100644 index 0000000000..56f01e09b0 --- /dev/null +++ b/backend/src/main/java/io/dataease/controller/wizard/ReptileController.java @@ -0,0 +1,30 @@ +package io.dataease.controller.wizard; + +import com.github.xiaoymin.knife4j.annotations.ApiSupport; +import io.dataease.service.wizard.ReptileService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.Map; + +@Api(tags = "首页") +@ApiSupport(order = 80) +@RestController +@RequestMapping("Reptile") +public class ReptileController { + + @Resource + private ReptileService reptileService; + + @GetMapping("lastActive") + @ApiOperation("获取官方Blog最新动态") + public Map lastActive() { + return reptileService.lastActive(); + } +} diff --git a/backend/src/main/java/io/dataease/job/sechedule/ScheduleManager.java b/backend/src/main/java/io/dataease/job/sechedule/ScheduleManager.java index c93e59e11c..3a41798e93 100644 --- a/backend/src/main/java/io/dataease/job/sechedule/ScheduleManager.java +++ b/backend/src/main/java/io/dataease/job/sechedule/ScheduleManager.java @@ -28,7 +28,7 @@ public class ScheduleManager { * @throws SchedulerException */ public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime, - JobDataMap jobDataMap) throws SchedulerException { + JobDataMap jobDataMap) throws SchedulerException { JobBuilder jobBuilder = JobBuilder.newJob(cls).withIdentity(jobKey); @@ -46,7 +46,8 @@ public class ScheduleManager { scheduler.scheduleJob(jd, trigger); } - public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime) throws SchedulerException { + public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime) + throws SchedulerException { addSimpleJob(jobKey, triggerKey, cls, repeatIntervalTime); } @@ -59,7 +60,8 @@ public class ScheduleManager { * @param cron * @param jobDataMap */ - public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, Date endTime, JobDataMap jobDataMap) { + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, + Date endTime, JobDataMap jobDataMap) { try { LogUtil.info("addCronJob: " + triggerKey.getName() + "," + triggerKey.getGroup()); @@ -99,7 +101,8 @@ public class ScheduleManager { } } - public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, Date endTime) { + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, + Date endTime) { addCronJob(jobKey, triggerKey, jobClass, cron, startTime, endTime, null); } @@ -140,7 +143,8 @@ public class ScheduleManager { * @param cron * @throws SchedulerException */ - public void modifyCronJobTime(TriggerKey triggerKey, String cron, Date startTime, Date endTime) throws SchedulerException { + public void modifyCronJobTime(TriggerKey triggerKey, String cron, Date startTime, Date endTime) + throws SchedulerException { LogUtil.info("modifyCronJobTime: " + triggerKey.getName() + "," + triggerKey.getGroup()); @@ -151,7 +155,6 @@ public class ScheduleManager { return; } - /** 方式一 :调用 rescheduleJob 开始 */ TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();// 触发器 @@ -279,7 +282,6 @@ public class ScheduleManager { } } - public static void startJobs(Scheduler sched) { try { sched.start(); @@ -289,7 +291,6 @@ public class ScheduleManager { } } - public void shutdownJobs(Scheduler sched) { try { if (!sched.isShutdown()) { @@ -312,7 +313,7 @@ public class ScheduleManager { * @throws SchedulerException */ public void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, - int intervalTime, JobDataMap jobDataMap) throws SchedulerException { + int intervalTime, JobDataMap jobDataMap) throws SchedulerException { if (scheduler.checkExists(triggerKey)) { modifySimpleJobTime(triggerKey, intervalTime); @@ -323,7 +324,7 @@ public class ScheduleManager { } public void addOrUpdateSingleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, - Date date, JobDataMap jobDataMap) throws SchedulerException { + Date date, JobDataMap jobDataMap) throws SchedulerException { if (scheduler.checkExists(triggerKey)) { modifySingleJobTime(triggerKey, date); } else { @@ -333,15 +334,15 @@ public class ScheduleManager { } public void addOrUpdateSingleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, - Date date) throws SchedulerException { + Date date) throws SchedulerException { addOrUpdateSingleJob(jobKey, triggerKey, clz, date, null); } - public void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, int intervalTime) throws SchedulerException { + public void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, int intervalTime) + throws SchedulerException { addOrUpdateSimpleJob(jobKey, triggerKey, clz, intervalTime, null); } - /** * 添加或修改 cronJob * @@ -352,7 +353,8 @@ public class ScheduleManager { * @param jobDataMap * @throws SchedulerException */ - public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, Date endTime, JobDataMap jobDataMap) throws SchedulerException { + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, + Date endTime, JobDataMap jobDataMap) throws SchedulerException { LogUtil.info("AddOrUpdateCronJob: " + jobKey.getName() + "," + triggerKey.getGroup()); @@ -363,7 +365,8 @@ public class ScheduleManager { } } - public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, Date endTime) throws SchedulerException { + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, Date startTime, + Date endTime) throws SchedulerException { addOrUpdateCronJob(jobKey, triggerKey, jobClass, cron, startTime, endTime, null); } @@ -398,7 +401,8 @@ public class ScheduleManager { if (!CronExpression.isValidExpression(cron)) { DataEaseException.throwException("cron :" + cron + " error"); } - return TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build(); + return TriggerBuilder.newTrigger().withIdentity("Calculate Date") + .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build(); } diff --git a/backend/src/main/java/io/dataease/job/sechedule/strategy/TaskHandler.java b/backend/src/main/java/io/dataease/job/sechedule/strategy/TaskHandler.java index c685930a17..87dff10bdf 100644 --- a/backend/src/main/java/io/dataease/job/sechedule/strategy/TaskHandler.java +++ b/backend/src/main/java/io/dataease/job/sechedule/strategy/TaskHandler.java @@ -13,9 +13,6 @@ import java.util.Date; public abstract class TaskHandler implements InitializingBean { - private static final String[] week = {"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"}; - - public void addTask(ScheduleManager scheduleManager, GlobalTaskEntity taskEntity) throws Exception { // 1。首先看看是否过期 Long endTime = taskEntity.getEndTime(); @@ -30,12 +27,11 @@ public abstract class TaskHandler implements InitializingBean { if (ObjectUtils.isNotEmpty(taskEntity.getEndTime())) { new Date(taskEntity.getEndTime()); } - Class executor = this.getClass(); + Class executor = this.getClass(); String cron = cron(taskEntity); scheduleManager.addOrUpdateCronJob(jobKey, triggerKey, executor, cron, start, end, jobDataMap(taskEntity)); } - protected abstract JobDataMap jobDataMap(GlobalTaskEntity taskEntity); private String cron(GlobalTaskEntity taskEntity) { @@ -54,36 +50,34 @@ public abstract class TaskHandler implements InitializingBean { instance.setTime(date); if (taskEntity.getRateType() == 0) { - return - instance.get(Calendar.SECOND) + " " + - instance.get(Calendar.MINUTE) + " " + - instance.get(Calendar.HOUR_OF_DAY) + " * * ?"; + return instance.get(Calendar.SECOND) + " " + + instance.get(Calendar.MINUTE) + " " + + instance.get(Calendar.HOUR_OF_DAY) + " * * ?"; } if (taskEntity.getRateType() == 1) { - return - instance.get(Calendar.SECOND) + " " + - instance.get(Calendar.MINUTE) + " " + - instance.get(Calendar.HOUR_OF_DAY) + " ? * " + - getDayOfWeek(instance); + return instance.get(Calendar.SECOND) + " " + + instance.get(Calendar.MINUTE) + " " + + instance.get(Calendar.HOUR_OF_DAY) + " ? * " + + getDayOfWeek(instance); } if (taskEntity.getRateType() == 2) { - return - instance.get(Calendar.SECOND) + " " + - instance.get(Calendar.MINUTE) + " " + - instance.get(Calendar.HOUR_OF_DAY) + " " + - instance.get(Calendar.DATE) + " * ?"; + return instance.get(Calendar.SECOND) + " " + + instance.get(Calendar.MINUTE) + " " + + instance.get(Calendar.HOUR_OF_DAY) + " " + + instance.get(Calendar.DATE) + " * ?"; } return null; } + public abstract void resetRunningInstance(Long taskId); + private String getDayOfWeek(Calendar instance) { int index = instance.get(Calendar.DAY_OF_WEEK); - index = (index + 1) % 7; + index = (index + 1) % 7; return String.valueOf(index); } - public void removeTask(ScheduleManager scheduleManager, GlobalTaskEntity taskEntity) { JobKey jobKey = new JobKey(taskEntity.getTaskId().toString()); TriggerKey triggerKey = new TriggerKey(taskEntity.getTaskId().toString()); @@ -95,14 +89,16 @@ public abstract class TaskHandler implements InitializingBean { scheduleManager.fireNow(jobKey); } - - //判断任务是否过期 + // 判断任务是否过期 public Boolean taskExpire(Long endTime) { - if (ObjectUtils.isEmpty(endTime)) return false; + if (ObjectUtils.isEmpty(endTime)) + return false; Long now = System.currentTimeMillis(); return now > endTime; } + protected abstract Boolean taskIsRunning(Long taskId); + @Override public void afterPropertiesSet() throws Exception { String beanName = null; diff --git a/backend/src/main/java/io/dataease/job/sechedule/strategy/impl/EmailTaskHandler.java b/backend/src/main/java/io/dataease/job/sechedule/strategy/impl/EmailTaskHandler.java index d84ba736de..12ba9352a2 100644 --- a/backend/src/main/java/io/dataease/job/sechedule/strategy/impl/EmailTaskHandler.java +++ b/backend/src/main/java/io/dataease/job/sechedule/strategy/impl/EmailTaskHandler.java @@ -5,6 +5,7 @@ import io.dataease.auth.entity.TokenInfo; import io.dataease.auth.service.AuthUserService; import io.dataease.auth.service.impl.AuthUserServiceImpl; import io.dataease.auth.util.JWTUtils; +import io.dataease.base.mapper.ext.ExtTaskMapper; import io.dataease.commons.utils.CommonBeanFactory; import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.ServletUtils; @@ -20,6 +21,7 @@ import io.dataease.service.system.EmailService; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.quartz.*; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -46,6 +48,16 @@ public class EmailTaskHandler extends TaskHandler implements Job { return jobDataMap; } + public EmailTaskHandler proxy() { + return CommonBeanFactory.getBean(EmailTaskHandler.class); + } + + @Override + protected Boolean taskIsRunning(Long taskId) { + ExtTaskMapper extTaskMapper = CommonBeanFactory.getBean(ExtTaskMapper.class); + return extTaskMapper.runningCount(taskId) > 0; + } + @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 插件没有加载 空转 @@ -54,11 +66,16 @@ public class EmailTaskHandler extends TaskHandler implements Job { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); GlobalTaskEntity taskEntity = (GlobalTaskEntity) jobDataMap.get("taskEntity"); + ScheduleManager scheduleManager = SpringContextUtil.getBean(ScheduleManager.class); if (taskExpire(taskEntity.getEndTime())) { - ScheduleManager scheduleManager = SpringContextUtil.getBean(ScheduleManager.class); removeTask(scheduleManager, taskEntity); return; } + if (taskIsRunning(taskEntity.getTaskId())) { + LogUtil.info("Skip synchronization task: {} ,due to task status is {}", + taskEntity.getTaskId(), "running"); + return; + } GlobalTaskInstance taskInstance = buildInstance(taskEntity); Long instanceId = saveInstance(taskInstance); @@ -67,10 +84,15 @@ public class EmailTaskHandler extends TaskHandler implements Job { XpackEmailTemplateDTO emailTemplate = (XpackEmailTemplateDTO) jobDataMap.get("emailTemplate"); SysUserEntity creator = (SysUserEntity) jobDataMap.get("creator"); LogUtil.info("start execute send panel report task..."); - sendReport(taskInstance, emailTemplate, creator); + proxy().sendReport(taskInstance, emailTemplate, creator); } + @Override + public void resetRunningInstance(Long taskId) { + ExtTaskMapper extTaskMapper = CommonBeanFactory.getBean(ExtTaskMapper.class); + extTaskMapper.resetRunnings(taskId); + } public Long saveInstance(GlobalTaskInstance taskInstance) { EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); @@ -99,11 +121,12 @@ public class EmailTaskHandler extends TaskHandler implements Job { emailXpackService.saveInstance(taskInstance); } - + @Async("priorityExecutor") public void sendReport(GlobalTaskInstance taskInstance, XpackEmailTemplateDTO emailTemplateDTO, - SysUserEntity user) { + SysUserEntity user) { EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); try { + String panelId = emailTemplateDTO.getPanelId(); String url = panelUrl(panelId); String token = tokenByUser(user); @@ -116,11 +139,15 @@ public class EmailTaskHandler extends TaskHandler implements Job { String recipients = emailTemplateDTO.getRecipients(); byte[] content = emailTemplateDTO.getContent(); EmailService emailService = SpringContextUtil.getBean(EmailService.class); + String contentStr = ""; if (ObjectUtils.isNotEmpty(content)) { contentStr = new String(content, "UTF-8"); } - emailService.sendWithImage(recipients, emailTemplateDTO.getTitle(), contentStr, bytes); + emailService.sendWithImage(recipients, emailTemplateDTO.getTitle(), + contentStr, bytes); + + Thread.sleep(10000); success(taskInstance); } catch (Exception e) { error(taskInstance, e); diff --git a/backend/src/main/java/io/dataease/listener/GlobalTaskStartListener.java b/backend/src/main/java/io/dataease/listener/GlobalTaskStartListener.java index c7361ee93b..c8de41274a 100644 --- a/backend/src/main/java/io/dataease/listener/GlobalTaskStartListener.java +++ b/backend/src/main/java/io/dataease/listener/GlobalTaskStartListener.java @@ -32,6 +32,7 @@ public class GlobalTaskStartListener implements ApplicationListener { TaskHandler taskHandler = TaskStrategyFactory.getInvokeStrategy(task.getTaskType()); try { + taskHandler.resetRunningInstance(task.getTaskId()); taskHandler.addTask(scheduleManager, task); } catch (Exception e) { e.printStackTrace(); diff --git a/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java b/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java index 08e2d10374..99dc4c5fec 100644 --- a/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java +++ b/backend/src/main/java/io/dataease/plugins/server/ThemeServer.java @@ -4,12 +4,13 @@ import java.util.List; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import io.dataease.commons.exception.DEException; +import io.dataease.commons.utils.LogUtil; import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.xpack.theme.dto.ThemeDto; import io.dataease.plugins.xpack.theme.dto.ThemeItem; @@ -20,10 +21,8 @@ import io.dataease.plugins.xpack.theme.service.ThemeXpackService; @RestController public class ThemeServer { - - @PostMapping("/themes") - public List themes(){ + public List themes() { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); return themeXpackService.themes(); @@ -36,15 +35,22 @@ public class ThemeServer { } @PostMapping("/save") - public void save(@RequestPart("request") ThemeRequest request, @RequestPart(value = "file", required = false) MultipartFile bodyFile) { + public void save(@RequestPart("request") ThemeRequest request, + @RequestPart(value = "file", required = false) MultipartFile bodyFile) { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); - themeXpackService.save(request, bodyFile); + try { + themeXpackService.save(request, bodyFile); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + DEException.throwException(e); + } + } @PostMapping("/delete/{themeId}") - public void save(@PathVariable("themeId") int themeId) { + public void delete(@PathVariable("themeId") int themeId) { ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class); themeXpackService.deleteTheme(themeId); } - + } diff --git a/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java b/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java index 9ecd645704..fd8eb56a74 100644 --- a/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java +++ b/backend/src/main/java/io/dataease/plugins/server/XEmailTaskServer.java @@ -3,6 +3,7 @@ package io.dataease.plugins.server; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.dataease.commons.exception.DEException; +import io.dataease.commons.pool.PriorityThreadPoolExecutor; import io.dataease.commons.utils.*; import io.dataease.plugins.common.entity.GlobalTaskEntity; import io.dataease.plugins.common.entity.GlobalTaskInstance; @@ -23,6 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.concurrent.Future; + +import javax.annotation.Resource; @Api(tags = "xpack:定时报告") @RequestMapping("/plugin/task") @@ -32,6 +36,9 @@ public class XEmailTaskServer { @Autowired private ScheduleService scheduleService; + @Resource + private PriorityThreadPoolExecutor priorityExecutor; + @PostMapping("/queryTasks/{goPage}/{pageSize}") public Pager> queryTask(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody XpackGridRequest request) { @@ -85,7 +92,19 @@ public class XEmailTaskServer { String token = ServletUtils.getToken(); String fileId = null; try { - fileId = emailXpackService.print(url, token, buildPixel(request.getPixel())); + Future future = priorityExecutor.submit(() -> { + try { + return emailXpackService.print(url, token, buildPixel(request.getPixel())); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + DEException.throwException("预览失败,请联系管理员"); + } + return null; + }, 0); + Object object = future.get(); + if (ObjectUtils.isNotEmpty(object)) { + fileId = object.toString(); + } } catch (Exception e) { LogUtil.error(e.getMessage(), e); DEException.throwException("预览失败,请联系管理员"); diff --git a/backend/src/main/java/io/dataease/service/wizard/ReptileService.java b/backend/src/main/java/io/dataease/service/wizard/ReptileService.java new file mode 100644 index 0000000000..2530f7d287 --- /dev/null +++ b/backend/src/main/java/io/dataease/service/wizard/ReptileService.java @@ -0,0 +1,45 @@ +package io.dataease.service.wizard; + +import io.dataease.commons.utils.HttpClientUtil; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * Author: wangjiahao + * Date: 2022/1/11 + * Description: + */ +@Service +public class ReptileService { + String blogUrl = "https://blog.fit2cloud.com/?cat=321"; + + public Map lastActive() { + Map result = new HashMap(); + try { + //爬取最新数据 + Document doc = Jsoup.parse(HttpClientUtil.get(blogUrl, null)); + Elements elementsContent = doc.getElementsByAttributeValue("rel", "bookmark"); + Elements elementsTime = doc.getElementsByTag("time"); + Element lastInfo = elementsContent.get(0); + result.put("title",lastInfo.attr("title")); + result.put("href",lastInfo.attr("href")); + result.put("time",elementsTime.get(0).childNode(0).outerHtml()); + } catch (Exception e) { + //ignore + result.put("title","支持移动端展示,数据源新增对DB2的支持,DataEase开源数据可视化分析平台v1.6.0发布"); + result.put("href","https://blog.fit2cloud.com/?p=3200"); + result.put("time","2022年1月10日"); + } + + return result; + + } + + +} diff --git a/backend/src/main/resources/db/migration/V31__1.7.sql b/backend/src/main/resources/db/migration/V31__1.7.sql index 6729c85c10..e244d139aa 100644 --- a/backend/src/main/resources/db/migration/V31__1.7.sql +++ b/backend/src/main/resources/db/migration/V31__1.7.sql @@ -6,4 +6,8 @@ CREATE TABLE `dataset_column_permissions` ( `permissions` longtext DEFAULT NULL COMMENT '权限', `update_time` bigint(13) NULL DEFAULT NULL, PRIMARY KEY (`id`) -)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; \ No newline at end of file +)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; + +INSERT INTO `sys_menu` (`menu_id`, `pid`, `sub_count`, `type`, `title`, `name`, `component`, `menu_sort`, `icon`, `path`, `i_frame`, `cache`, `hidden`, `permission`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (61, 0, 0, 1, '首页', 'wizard', 'wizard/index', 0, '', '/wizard', b'1', b'0', b'0', NULL, NULL, NULL, NULL, 1614915491036); +INSERT INTO `system_parameter` (`param_key`, `param_value`, `type`, `sort`) VALUES ('ui.openHomePage', 'true', 'boolean', 13); + diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 41c74fb98e..04edcb89a8 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -223,6 +223,7 @@ authsource_configuration_is_null=Authentication source configuration cannot be e 角色表单=Role Form 重置密码=Reset Password 关于=About Us +首页=Home Page i18n_auth_view=View i18n_auth_use=Use i18n_auth_export=Export diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 52afec5982..fe61bae269 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -222,6 +222,7 @@ authsource_configuration_is_null=认证源配置不能为空 角色表单=角色表单 重置密码=重置密码 关于=关于 +首页=首页 i18n_auth_view=查看 i18n_auth_use=使用 i18n_auth_export=导出 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index e4692e67cd..529af64b07 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -225,6 +225,7 @@ authsource_configuration_is_null=認證源配置不能為空 角色表单=角色表單 重置密码=重置密碼 关于=關於 +首页=首頁 i18n_auth_view=查看 i18n_auth_use=使用 i18n_auth_export=導出 diff --git a/frontend/src/api/user.js b/frontend/src/api/user.js index 01850eecc5..c343c82030 100644 --- a/frontend/src/api/user.js +++ b/frontend/src/api/user.js @@ -22,6 +22,13 @@ export function logout() { }) } +export function needModifyPwd() { + return request({ + url: '/api/auth/useInitPwd', + method: 'post' + }) +} + export function validateUserName(data) { return request({ url: '/api/auth/validateName', diff --git a/frontend/src/api/wizard/wizard.js b/frontend/src/api/wizard/wizard.js new file mode 100644 index 0000000000..99d0e921f7 --- /dev/null +++ b/frontend/src/api/wizard/wizard.js @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export function blogLastActive() { + return request({ + url: 'Reptile/lastActive', + method: 'get' + }) +} diff --git a/frontend/src/assets/deV.png b/frontend/src/assets/deV.png new file mode 100644 index 0000000000..16bc00398e Binary files /dev/null and b/frontend/src/assets/deV.png differ diff --git a/frontend/src/components/DeDrag/index.vue b/frontend/src/components/DeDrag/index.vue index 0c339f1b8f..8832a2c813 100644 --- a/frontend/src/components/DeDrag/index.vue +++ b/frontend/src/components/DeDrag/index.vue @@ -535,7 +535,7 @@ export default { return this.$store.state.curComponent }, curGap() { - return (this.canvasStyleData.panel.gap === 'yes' && this.element.auxiliaryMatrix && this.element.type !== 'custom') ? this.componentGap : 0 + return (this.canvasStyleData.panel.gap === 'yes' && this.element.auxiliaryMatrix) ? this.componentGap : 0 }, ...mapState([ 'editor', diff --git a/frontend/src/components/canvas/components/Editor/EditBar.vue b/frontend/src/components/canvas/components/Editor/EditBar.vue index c10fc98aac..d1d34c4f3f 100644 --- a/frontend/src/components/canvas/components/Editor/EditBar.vue +++ b/frontend/src/components/canvas/components/Editor/EditBar.vue @@ -1,5 +1,6 @@ @@ -34,6 +38,7 @@ import { mapState } from 'vuex' import bus from '@/utils/bus' import SettingMenu from '@/components/canvas/components/Editor/SettingMenu' import LinkageField from '@/components/canvas/components/Editor/LinkageField' +import toast from '@/components/canvas/utils/toast' export default { components: { SettingMenu, LinkageField }, @@ -207,6 +212,24 @@ export default { }, linkJumpSet() { this.$emit('linkJumpSet') + }, + goFile() { + this.$refs.files.click() + }, + handleFileChange(e) { + const file = e.target.files[0] + if (!file.type.includes('image')) { + toast('只能插入图片') + return + } + const reader = new FileReader() + reader.onload = (res) => { + const fileResult = res.target.result + this.curComponent.propValue = fileResult + this.$store.commit('recordSnapshot', 'handleFileChange') + } + + reader.readAsDataURL(file) } } } diff --git a/frontend/src/components/canvas/utils/utils.js b/frontend/src/components/canvas/utils/utils.js index d5d4fa84fa..932e596c4b 100644 --- a/frontend/src/components/canvas/utils/utils.js +++ b/frontend/src/components/canvas/utils/utils.js @@ -3,6 +3,10 @@ import { HYPERLINKS } from '@/components/canvas/custom-component/component-list' +import { + ApplicationContext +} from '@/utils/ApplicationContext' + export function deepCopy(target) { if (typeof target === 'object') { const result = Array.isArray(target) ? [] : {} @@ -62,28 +66,17 @@ export function mobile2MainCanvas(mainSource, mobileSource) { export function panelInit(componentDatas) { componentDatas.forEach(item => { if (item.component && item.component === 'de-date') { - if (item.serviceName === 'timeDateWidget' && item.options.attrs && !item.options.attrs.default) { - item.options.attrs.default = { - isDynamic: false, - dkey: 0, - dynamicPrefix: 1, - dynamicInfill: 'day', - dynamicSuffix: 'before' - } - } - if (item.serviceName === 'timeDateRangeWidget' && item.options.attrs && !item.options.attrs.default) { - item.options.attrs.default = { - isDynamic: false, - dkey: 0, - sDynamicPrefix: 1, - sDynamicInfill: 'day', - sDynamicSuffix: 'before', - eDynamicPrefix: 1, - eDynamicInfill: 'day', - eDynamicSuffix: 'after' + if (item.options.attrs && + (!item.options.attrs.default || (item.serviceName === 'timeYearWidget' && item.options.attrs.default.dynamicInfill !== 'year') || (item.serviceName === 'timeMonthWidget' && item.options.attrs.default.dynamicInfill !== 'month'))) { + const widget = ApplicationContext.getService(item.serviceName) + if (widget && widget.defaultSetting) { + item.options.attrs.default = widget.defaultSetting() } } } + if (item.type === 'custom') { + item.options.manualModify = false + } if (item.filters && item.filters.length > 0) { item.filters = [] } diff --git a/frontend/src/components/dataease/DeOutWidget.vue b/frontend/src/components/dataease/DeOutWidget.vue index 42ef2809cd..aefbd1878b 100644 --- a/frontend/src/components/dataease/DeOutWidget.vue +++ b/frontend/src/components/dataease/DeOutWidget.vue @@ -161,7 +161,7 @@ export default { diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index ed439d8f33..6b74d3ca00 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -1,7 +1,7 @@ @@ -23,6 +31,8 @@ import DeContainer from '@/components/dataease/DeContainer' import DeAsideContainer from '@/components/dataease/DeAsideContainer' import bus from '@/utils/bus' +import { needModifyPwd } from '@/api/user' + export default { name: 'Layout', components: { @@ -37,7 +47,9 @@ export default { mixins: [ResizeMixin], data() { return { - componentName: 'PanelMain' + componentName: 'PanelMain', + showTips: false, + finishLoad: false } }, computed: { @@ -76,6 +88,14 @@ export default { } } }, + beforeCreate() { + needModifyPwd().then(res => { + this.showTips = res.success && res.data + this.finishLoad = true + }).catch(e => { + this.finishLoad = true + }) + }, mounted() { bus.$on('PanelSwitchComponent', (c) => { this.componentName = c.name @@ -94,6 +114,8 @@ export default { @import "~@/styles/variables.scss"; .app-wrapper { + min-width: 1000px!important; + min-height: 600px!important; @include clearfix; position: relative; height: 100%; @@ -153,4 +175,29 @@ export default { } } + .pwd-tips { + position: absolute; + box-shadow: 0 0 0 1000em rgb(0, 0, 0, 0.3); + height: 100px; + width: 225px; + top: 105px; + right: 115px; + z-index: 9999; + border-radius: 4px; + padding: 15px; + background: #fff; + } + .arrow{ + border-bottom: 7px solid #fff; + border-right: 7px solid #b5b5b7; + border-left: 7px solid #b5b5b7; + border-top: 7px solid #b5b5b7; + width: 0px; + height: 0px; + position: relative; + top:-60px; + left:210px; + transform: rotate(90deg); +} + diff --git a/frontend/src/permission.js b/frontend/src/permission.js index 04a8df7b17..c1d473f23b 100644 --- a/frontend/src/permission.js +++ b/frontend/src/permission.js @@ -16,6 +16,7 @@ import { import { isMobile } from '@/utils/index' +import Layout from '@/layout/index' // import bus from './utils/bus' NProgress.configure({ @@ -97,6 +98,23 @@ export const loadMenus = (next, to) => { const datas = res.data const filterDatas = filterRouter(datas) const asyncRouter = filterAsyncRouter(filterDatas) + // 如果包含首页 则默认页面是 首页 否则默认页面是仪表板页面 + if (JSON.stringify(datas).indexOf('wizard') > -1) { + asyncRouter.push({ + path: '/', + component: Layout, + redirect: '/wizard/index', + hidden: true + }) + } else { + asyncRouter.push({ + path: '/', + component: Layout, + redirect: '/panel/index', + hidden: true + }) + } + asyncRouter.push({ path: '*', redirect: '/404', diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 37b18ae39d..066503b320 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -91,12 +91,6 @@ export const constantRoutes = [ path: '/previewFullScreen', component: () => import('@/components/canvas/components/Editor/PreviewFullScreen'), hidden: true - }, - { - path: '/', - component: Layout, - redirect: '/panel/index', - hidden: true } // { diff --git a/frontend/src/store/getters.js b/frontend/src/store/getters.js index 6e40c8f092..f336da0de7 100644 --- a/frontend/src/store/getters.js +++ b/frontend/src/store/getters.js @@ -16,6 +16,7 @@ const getters = { errorLogs: state => state.errorLog.logs, sceneData: state => state.dataset.sceneData, table: state => state.dataset.table, + chartTable: state => state.chart.table, hideCustomDs: state => state.dataset.hideCustomDs, loadingMap: state => state.request.loadingMap, currentPath: state => state.permission.currentPath, diff --git a/frontend/src/styles/deicon/demo_index.html b/frontend/src/styles/deicon/demo_index.html index bd13b8ef1a..7b9d1d8a9e 100644 --- a/frontend/src/styles/deicon/demo_index.html +++ b/frontend/src/styles/deicon/demo_index.html @@ -38,7 +38,7 @@

- +

    - + +
  • + +
    更换
    +
    &#xe606;
    +
  • + +
  • + +
    发送邮件
    +
    &#xe605;
    +
  • + +
  • + +
    github
    +
    &#xe6f8;
    +
  • + +
  • + +
    电话
    +
    &#xe681;
    +
  • +
  • 关闭
    &#xe60d;
  • - +
  • 矩形
    &#xe67e;
  • - +
  • 移动端
    &#xe653;
  • - +
  • video
    &#xe625;
  • - +
  • 悬浮按钮发动态
    &#xe6e8;
  • - +
  • 吸附选择
    &#xe697;
  • - +
  • margin
    &#xe902;
  • - +
  • padding
    &#xe91b;
  • - +
  • 时间
    &#xe665;
  • - +
  • 时间格式转换
    &#xe6fb;
  • - +
  • 超链接
    &#xe9b2;
  • - +
  • 科学技术
    &#xe60f;
  • - +
  • 符号-数据矩阵
    &#xe69c;
  • - +
  • 视图矩阵_o
    &#xeb85;
  • - +
  • 悬浮
    &#xe62c;
  • - +
  • 右悬浮-选中
    &#xe6db;
  • - +
  • 悬浮
    &#xe6f6;
  • - +
  • 悬浮按钮
    &#xe8e6;
  • - +
  • 44.tabs
    &#xe62a;
  • - +
  • 洗浴
    &#xe619;
  • - +
  • 线性图标-取消下钻
    &#xe973;
  • - +
  • 线性图标-取消下钻
    &#xe6ed;
  • - +
  • 联动
    &#xe6f7;
  • - +
  • 下钻
    &#xe613;
  • - +
  • 上钻
    &#xe61f;
  • - +
  • 取消联动
    &#xe637;
  • - +
  • edit-2
    &#xe604;
  • - +
  • edit-2
    &#xe612;
  • - +
  • 详情
    &#xe706;
  • - +
  • 弧形框
    &#xe603;
  • - +
  • 弧形框
    &#xe602;
  • - +
  • 透明
    &#xe642;
  • - +
  • 弧度
    &#xe61a;
  • - +
  • 放大
    &#xe62d;
  • - +
  • 设 置
    &#xe696;
  • - +
  • 屏幕_全屏
    &#xe655;
  • - +
  • font-weight-bold
    &#xe659;
  • - +
  • letter_spacing
    &#xe601;
  • - +
  • letter-spacing
    &#xe679;
  • - +
  • 字体颜色
    &#xe60e;
  • - +
  • format_letter_spacing_2
    &#xe6c3;
  • - +
  • font_size
    &#xe710;
  • - +
  • 居中
    &#xe972;
  • - +
  • 居右
    &#xe608;
  • - +
  • 居左
    &#xe688;
  • - +
  • 实线
    &#xe64a;
  • - +
  • 画笔
    &#xe640;
  • - +
  • 点线
    &#xe614;
  • - +
  • 虚线
    &#xe617;
  • - +
  • 背景色‘
    &#xe600;
  • - +
  • 矩形
    &#xe648;
  • - +
  • text
    &#xe959;
  • - +
  • picture
    &#xe643;
  • - +
  • 输入
    &#xe6ab;
  • - +
  • &#xe628;
  • - +
  • 查询搜索
    &#xe615;
  • - +
  • 季度
    &#xe624;
  • - +
  • 数字顺序
    &#xe7de;
  • - +
  • 树列表
    &#xe6a6;
  • - +
  • 日期
    &#xe639;
  • - +
  • 左侧-区间
    &#xe6dd;
  • - +
  • 列表
    &#xe66f;
  • - +
  • 下拉框
    &#xe8ca;
  • - +
  • 下拉树
    &#xe8d0;
  • - +
  • 重置
    &#xe611;
  • - +
  • &#xe691;
  • - +
  • &#xe692;
  • - +
  • &#xe695;
  • - +

Unicode 引用

@@ -480,9 +504,9 @@
@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1639622225820') format('woff2'),
-       url('iconfont.woff?t=1639622225820') format('woff'),
-       url('iconfont.ttf?t=1639622225820') format('truetype');
+  src: url('iconfont.woff2?t=1642061879222') format('woff2'),
+       url('iconfont.woff?t=1642061879222') format('woff'),
+       url('iconfont.ttf?t=1642061879222') format('truetype');
 }
 

第二步:定义使用 iconfont 的样式

@@ -507,7 +531,43 @@
    - + +
  • + +
    + 更换 +
    +
    .icon-genghuan +
    +
  • + +
  • + +
    + 发送邮件 +
    +
    .icon-fasongyoujian +
    +
  • + +
  • + +
    + github +
    +
    .icon-github +
    +
  • + +
  • + +
    + 电话 +
    +
    .icon-dianhua +
    +
  • +
  • @@ -516,7 +576,7 @@
    .icon-guanbi
  • - +
  • @@ -525,7 +585,7 @@
    .icon-juxing1
  • - +
  • @@ -534,7 +594,7 @@
    .icon-yidongduan
  • - +
  • @@ -543,7 +603,7 @@
    .icon-video
  • - +
  • @@ -552,7 +612,7 @@
    .icon-xuanfuanniufadongtai
  • - +
  • @@ -561,7 +621,7 @@
    .icon-xifuxuanze
  • - +
  • @@ -570,7 +630,7 @@
    .icon-margin
  • - +
  • @@ -579,7 +639,7 @@
    .icon-padding
  • - +
  • @@ -588,7 +648,7 @@
    .icon-shijian
  • - +
  • @@ -597,7 +657,7 @@
    .icon-shijiangeshizhuanhuan
  • - +
  • @@ -606,7 +666,7 @@
    .icon-chaolianjie
  • - +
  • @@ -615,7 +675,7 @@
    .icon-kexuejishu
  • - +
  • @@ -624,7 +684,7 @@
    .icon-shujujuzhen
  • - +
  • @@ -633,7 +693,7 @@
    .icon-shitujuzhen_o
  • - +
  • @@ -642,7 +702,7 @@
    .icon-xuanfu1
  • - +
  • @@ -651,7 +711,7 @@
    .icon-youxuanfu-copy
  • - +
  • @@ -660,7 +720,7 @@
    .icon-xuanfu
  • - +
  • @@ -669,7 +729,7 @@
    .icon-xuanfuanniu
  • - +
  • @@ -678,7 +738,7 @@
    .icon-tabs
  • - +
  • @@ -687,7 +747,7 @@
    .icon-xiyu
  • - +
  • @@ -696,7 +756,7 @@
    .icon-quxiaoshangzuan
  • - +
  • @@ -705,7 +765,7 @@
    .icon-quxiaoxiazuan
  • - +
  • @@ -714,7 +774,7 @@
    .icon-linkage
  • - +
  • @@ -723,7 +783,7 @@
    .icon-xiazuan
  • - +
  • @@ -732,7 +792,7 @@
    .icon-shangzuan
  • - +
  • @@ -741,7 +801,7 @@
    .icon-quxiaoliandong
  • - +
  • @@ -750,7 +810,7 @@
    .icon-edit-outline
  • - +
  • @@ -759,7 +819,7 @@
    .icon-edit
  • - +
  • @@ -768,7 +828,7 @@
    .icon-xiangqing1
  • - +
  • @@ -777,7 +837,7 @@
    .icon-weibiaoti-1
  • - +
  • @@ -786,7 +846,7 @@
    .icon-weibiaoti-
  • - +
  • @@ -795,7 +855,7 @@
    .icon-touming
  • - +
  • @@ -804,7 +864,7 @@
    .icon-fangxing-
  • - +
  • @@ -813,7 +873,7 @@
    .icon-fangda
  • - +
  • @@ -822,7 +882,7 @@
    .icon-shezhi
  • - +
  • @@ -831,7 +891,7 @@
    .icon-quanping1
  • - +
  • @@ -840,7 +900,7 @@
    .icon-font-weight-bold
  • - +
  • @@ -849,7 +909,7 @@
    .icon-letter_spacing
  • - +
  • @@ -858,7 +918,7 @@
    .icon-letter-spacing
  • - +
  • @@ -867,7 +927,7 @@
    .icon-zimua
  • - +
  • @@ -876,7 +936,7 @@
    .icon-format_letter_spacing_
  • - +
  • @@ -885,7 +945,7 @@
    .icon-font_size
  • - +
  • @@ -894,7 +954,7 @@
    .icon-align-center
  • - +
  • @@ -903,7 +963,7 @@
    .icon-juyou
  • - +
  • @@ -912,7 +972,7 @@
    .icon-juzuo
  • - +
  • @@ -921,7 +981,7 @@
    .icon-solid_line
  • - +
  • @@ -930,7 +990,7 @@
    .icon-huabi
  • - +
  • @@ -939,7 +999,7 @@
    .icon-dianxian
  • - +
  • @@ -948,7 +1008,7 @@
    .icon-xuxian
  • - +
  • @@ -957,7 +1017,7 @@
    .icon-beijingse1
  • - +
  • @@ -966,7 +1026,7 @@
    .icon-juxing
  • - +
  • @@ -975,7 +1035,7 @@
    .icon-text
  • - +
  • @@ -984,7 +1044,7 @@
    .icon-picture
  • - +
  • @@ -993,7 +1053,7 @@
    .icon-shuru
  • - +
  • @@ -1002,7 +1062,7 @@
    .icon-tree
  • - +
  • @@ -1011,7 +1071,7 @@
    .icon-chaxunsousuo
  • - +
  • @@ -1020,7 +1080,7 @@
    .icon-jidu
  • - +
  • @@ -1029,7 +1089,7 @@
    .icon-shuzishunxu
  • - +
  • @@ -1038,7 +1098,7 @@
    .icon-Group-
  • - +
  • @@ -1047,7 +1107,7 @@
    .icon-riqi
  • - +
  • @@ -1056,7 +1116,7 @@
    .icon-zuoce-qujian
  • - +
  • @@ -1065,7 +1125,7 @@
    .icon-liebiao
  • - +
  • @@ -1074,7 +1134,7 @@
    .icon-xialakuang
  • - +
  • @@ -1083,7 +1143,7 @@
    .icon-xialashu
  • - +
  • @@ -1092,7 +1152,7 @@
    .icon-zhongzhi
  • - +
  • @@ -1101,7 +1161,7 @@
    .icon-ri
  • - +
  • @@ -1110,7 +1170,7 @@
    .icon-nian
  • - +
  • @@ -1119,7 +1179,7 @@
    .icon-yue
  • - +

font-class 引用

@@ -1146,7 +1206,39 @@
    - + +
  • + +
    更换
    +
    #icon-genghuan
    +
  • + +
  • + +
    发送邮件
    +
    #icon-fasongyoujian
    +
  • + +
  • + +
    github
    +
    #icon-github
    +
  • + +
  • + +
    电话
    +
    #icon-dianhua
    +
  • +
  • 关闭
    #icon-guanbi
  • - +
  • 矩形
    #icon-juxing1
  • - +
  • 移动端
    #icon-yidongduan
  • - +
  • video
    #icon-video
  • - +
  • 悬浮按钮发动态
    #icon-xuanfuanniufadongtai
  • - +
  • 吸附选择
    #icon-xifuxuanze
  • - +
  • margin
    #icon-margin
  • - +
  • padding
    #icon-padding
  • - +
  • 时间
    #icon-shijian
  • - +
  • 时间格式转换
    #icon-shijiangeshizhuanhuan
  • - +
  • 超链接
    #icon-chaolianjie
  • - +
  • 科学技术
    #icon-kexuejishu
  • - +
  • 符号-数据矩阵
    #icon-shujujuzhen
  • - +
  • 视图矩阵_o
    #icon-shitujuzhen_o
  • - +
  • 悬浮
    #icon-xuanfu1
  • - +
  • 右悬浮-选中
    #icon-youxuanfu-copy
  • - +
  • 悬浮
    #icon-xuanfu
  • - +
  • 悬浮按钮
    #icon-xuanfuanniu
  • - +
  • 44.tabs
    #icon-tabs
  • - +
  • 洗浴
    #icon-xiyu
  • - +
  • 线性图标-取消下钻
    #icon-quxiaoshangzuan
  • - +
  • 线性图标-取消下钻
    #icon-quxiaoxiazuan
  • - +
  • 联动
    #icon-linkage
  • - +
  • 下钻
    #icon-xiazuan
  • - +
  • 上钻
    #icon-shangzuan
  • - +
  • 取消联动
    #icon-quxiaoliandong
  • - +
  • edit-2
    #icon-edit-outline
  • - +
  • edit-2
    #icon-edit
  • - +
  • 详情
    #icon-xiangqing1
  • - +
  • 弧形框
    #icon-weibiaoti-1
  • - +
  • 弧形框
    #icon-weibiaoti-
  • - +
  • 透明
    #icon-touming
  • - +
  • 弧度
    #icon-fangxing-
  • - +
  • 放大
    #icon-fangda
  • - +
  • 设 置
    #icon-shezhi
  • - +
  • 屏幕_全屏
    #icon-quanping1
  • - +
  • font-weight-bold
    #icon-font-weight-bold
  • - +
  • letter_spacing
    #icon-letter_spacing
  • - +
  • letter-spacing
    #icon-letter-spacing
  • - +
  • 字体颜色
    #icon-zimua
  • - +
  • format_letter_spacing_2
    #icon-format_letter_spacing_
  • - +
  • font_size
    #icon-font_size
  • - +
  • 居中
    #icon-align-center
  • - +
  • 居右
    #icon-juyou
  • - +
  • 居左
    #icon-juzuo
  • - +
  • 实线
    #icon-solid_line
  • - +
  • 画笔
    #icon-huabi
  • - +
  • 点线
    #icon-dianxian
  • - +
  • 虚线
    #icon-xuxian
  • - +
  • 背景色‘
    #icon-beijingse1
  • - +
  • 矩形
    #icon-juxing
  • - +
  • text
    #icon-text
  • - +
  • picture
    #icon-picture
  • - +
  • 输入
    #icon-shuru
  • - +
  • #icon-tree
  • - +
  • 查询搜索
    #icon-chaxunsousuo
  • - +
  • 季度
    #icon-jidu
  • - +
  • 数字顺序
    #icon-shuzishunxu
  • - +
  • 树列表
    #icon-Group-
  • - +
  • 日期
    #icon-riqi
  • - +
  • 左侧-区间
    #icon-zuoce-qujian
  • - +
  • 列表
    #icon-liebiao
  • - +
  • 下拉框
    #icon-xialakuang
  • - +
  • 下拉树
    #icon-xialashu
  • - +
  • 重置
    #icon-zhongzhi
  • - +
  • #icon-ri
  • - +
  • #icon-nian
  • - +
  • #icon-yue
  • - +

Symbol 引用

diff --git a/frontend/src/styles/deicon/iconfont.css b/frontend/src/styles/deicon/iconfont.css index 45af36e21b..b69087d95e 100644 --- a/frontend/src/styles/deicon/iconfont.css +++ b/frontend/src/styles/deicon/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 2459092 */ - src: url('iconfont.woff2?t=1639622225820') format('woff2'), - url('iconfont.woff?t=1639622225820') format('woff'), - url('iconfont.ttf?t=1639622225820') format('truetype'); + src: url('iconfont.woff2?t=1642061879222') format('woff2'), + url('iconfont.woff?t=1642061879222') format('woff'), + url('iconfont.ttf?t=1642061879222') format('truetype'); } .iconfont { @@ -13,6 +13,22 @@ -moz-osx-font-smoothing: grayscale; } +.icon-genghuan:before { + content: "\e606"; +} + +.icon-fasongyoujian:before { + content: "\e605"; +} + +.icon-github:before { + content: "\e6f8"; +} + +.icon-dianhua:before { + content: "\e681"; +} + .icon-guanbi:before { content: "\e60d"; } diff --git a/frontend/src/styles/deicon/iconfont.js b/frontend/src/styles/deicon/iconfont.js index 1e6df8ac58..59a1f1e499 100644 --- a/frontend/src/styles/deicon/iconfont.js +++ b/frontend/src/styles/deicon/iconfont.js @@ -1 +1 @@ -!function(c){var h,l,a,v,t,i='',z=(z=document.getElementsByTagName("script"))[z.length-1].getAttribute("data-injectcss"),o=function(c,h){h.parentNode.insertBefore(c,h)};if(z&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function m(){t||(t=!0,a())}function s(){try{v.documentElement.doScroll("left")}catch(c){return void setTimeout(s,50)}m()}h=function(){var c,h;(h=document.createElement("div")).innerHTML=i,i=null,(c=h.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",h=c,(c=document.body).firstChild?o(h,c.firstChild):c.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),h()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(a=h,v=c.document,t=!1,s(),v.onreadystatechange=function(){"complete"==v.readyState&&(v.onreadystatechange=null,m())})}(window); +!function(c){var l,h,a,v,t,i='',z=(z=document.getElementsByTagName("script"))[z.length-1].getAttribute("data-injectcss"),o=function(c,l){l.parentNode.insertBefore(c,l)};if(z&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function m(){t||(t=!0,a())}function s(){try{v.documentElement.doScroll("left")}catch(c){return void setTimeout(s,50)}m()}l=function(){var c,l;(l=document.createElement("div")).innerHTML=i,i=null,(c=l.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",l=c,(c=document.body).firstChild?o(l,c.firstChild):c.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),l()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(a=l,v=c.document,t=!1,s(),v.onreadystatechange=function(){"complete"==v.readyState&&(v.onreadystatechange=null,m())})}(window); \ No newline at end of file diff --git a/frontend/src/styles/deicon/iconfont.json b/frontend/src/styles/deicon/iconfont.json index 9ca1419ce4..cbc3a0fa7e 100644 --- a/frontend/src/styles/deicon/iconfont.json +++ b/frontend/src/styles/deicon/iconfont.json @@ -5,6 +5,34 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "12851123", + "name": "更换", + "font_class": "genghuan", + "unicode": "e606", + "unicode_decimal": 58886 + }, + { + "icon_id": "1286", + "name": "发送邮件", + "font_class": "fasongyoujian", + "unicode": "e605", + "unicode_decimal": 58885 + }, + { + "icon_id": "7239484", + "name": "github", + "font_class": "github", + "unicode": "e6f8", + "unicode_decimal": 59128 + }, + { + "icon_id": "11810462", + "name": "电话", + "font_class": "dianhua", + "unicode": "e681", + "unicode_decimal": 59009 + }, { "icon_id": "1367318", "name": "关闭", diff --git a/frontend/src/styles/deicon/iconfont.ttf b/frontend/src/styles/deicon/iconfont.ttf index c19298b658..870d7f0a3e 100644 Binary files a/frontend/src/styles/deicon/iconfont.ttf and b/frontend/src/styles/deicon/iconfont.ttf differ diff --git a/frontend/src/styles/deicon/iconfont.woff b/frontend/src/styles/deicon/iconfont.woff index f079466170..b86bf5e347 100644 Binary files a/frontend/src/styles/deicon/iconfont.woff and b/frontend/src/styles/deicon/iconfont.woff differ diff --git a/frontend/src/styles/deicon/iconfont.woff2 b/frontend/src/styles/deicon/iconfont.woff2 index 69569792ff..fa730c9626 100644 Binary files a/frontend/src/styles/deicon/iconfont.woff2 and b/frontend/src/styles/deicon/iconfont.woff2 differ diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss index e0da947fbb..93ff912679 100644 --- a/frontend/src/styles/index.scss +++ b/frontend/src/styles/index.scss @@ -534,14 +534,7 @@ div:focus { color: var(--Main) !important; } -/* .blackTheme .el-dialog { - background: var(--ContentBG) !important; - color: var(--TextPrimary) !important; -} - */ -.blackTheme .el-dialog__title { - color: #ffffff !important; -} + .blackTheme .title-text { color: var(--TextPrimary) !important; @@ -663,9 +656,7 @@ div:focus { background: #38393a !important; } -.blackTheme .el-message-box__content { - color: #F2F6FC; -} + .blackTheme .el-message-box__btns { .el-button--default:not(.el-button--primary) { diff --git a/frontend/src/views/chart/chart/chart.js b/frontend/src/views/chart/chart/chart.js index 2db3f6c550..e0f36d1f81 100644 --- a/frontend/src/views/chart/chart/chart.js +++ b/frontend/src/views/chart/chart/chart.js @@ -13,11 +13,11 @@ export const DEFAULT_SIZE = { barDefault: true, barWidth: 40, barGap: 0.4, - lineWidth: 1, + lineWidth: 2, lineType: 'solid', lineSymbol: 'circle', lineSymbolSize: 4, - lineSmooth: false, + lineSmooth: true, lineArea: false, pieInnerRadius: 0, pieOuterRadius: 80, diff --git a/frontend/src/views/chart/chart/liquid/liquid.js b/frontend/src/views/chart/chart/liquid/liquid.js index d317e06232..2d61bc2066 100644 --- a/frontend/src/views/chart/chart/liquid/liquid.js +++ b/frontend/src/views/chart/chart/liquid/liquid.js @@ -5,7 +5,7 @@ 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 + let max, radius, bgColor, shape, labelContent, title if (chart.data) { if (chart.data.series.length > 0) { value = chart.data.series[0].data[0] @@ -26,10 +26,6 @@ export function baseLiquid(plot, container, chart) { 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 @@ -86,14 +82,6 @@ export function baseLiquid(plot, container, chart) { percent: (parseFloat(value) / parseFloat(max)), radius: radius, shape: shape, - outline: { - border: outlineBorder, - distance: outlineDistance - }, - wave: { - length: waveLength, - count: waveCount - }, statistic: { // title: title, content: labelContent diff --git a/frontend/src/views/chart/chart/map/map.js b/frontend/src/views/chart/chart/map/map.js index eba14b830c..aee4192f28 100644 --- a/frontend/src/views/chart/chart/map/map.js +++ b/frontend/src/views/chart/chart/map/map.js @@ -26,7 +26,7 @@ export function baseMapOption(chart_option, chart) { // 处理data if (chart.data) { chart_option.title.text = chart.title - if (chart.data.series.length > 0) { + if (chart.data.series && chart.data.series.length > 0) { chart_option.series[0].name = chart.data.series[0].name // label if (customAttr.label) { diff --git a/frontend/src/views/chart/components/ChartComponent.vue b/frontend/src/views/chart/components/ChartComponent.vue index c966e46054..3082ccf498 100644 --- a/frontend/src/views/chart/components/ChartComponent.vue +++ b/frontend/src/views/chart/components/ChartComponent.vue @@ -74,7 +74,7 @@ import { import { baseMixOption } from '@/views/chart/chart/mix/mix' - // import eventBus from '@/components/canvas/utils/eventBus' +// import eventBus from '@/components/canvas/utils/eventBus' import { uuid } from 'vue-uuid' @@ -130,7 +130,8 @@ export default { pointParam: null, dynamicAreaCode: null, - borderRadius: '0px' + borderRadius: '0px', + mapCenter: null } }, @@ -276,6 +277,11 @@ export default { const base_json = JSON.parse(JSON.stringify(BASE_MAP)) const chart_option = baseMapOption(base_json, chart) this.myEcharts(chart_option) + const opt = this.myChart.getOption() + if (opt && opt.series) { + const center = opt.series[0].center + this.mapCenter = center + } }, myEcharts(option) { // 指定图表的配置项和数据 @@ -356,6 +362,7 @@ export default { resetZoom() { const options = JSON.parse(JSON.stringify(this.myChart.getOption())) options.series[0].zoom = 1 + options.series[0].center = this.mapCenter this.myChart.setOption(options) } } diff --git a/frontend/src/views/chart/components/normal/LabelNormal.vue b/frontend/src/views/chart/components/normal/LabelNormal.vue index 27f728943c..616692f810 100644 --- a/frontend/src/views/chart/components/normal/LabelNormal.vue +++ b/frontend/src/views/chart/components/normal/LabelNormal.vue @@ -6,7 +6,7 @@ id="label-content" :style="content_class" > - +

{{ item.data[0] }}

diff --git a/frontend/src/views/chart/components/shape-attr/LabelSelector.vue b/frontend/src/views/chart/components/shape-attr/LabelSelector.vue index e94542193d..3a5acfe09f 100644 --- a/frontend/src/views/chart/components/shape-attr/LabelSelector.vue +++ b/frontend/src/views/chart/components/shape-attr/LabelSelector.vue @@ -17,7 +17,7 @@ - + @@ -84,27 +84,35 @@ export default { labelForm: JSON.parse(JSON.stringify(DEFAULT_LABEL)), fontSize: [], isSetting: false, - labelPosition: [ + labelPosition: [], + labelPositionPie: [ { name: this.$t('chart.inside'), value: 'inside' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, + { name: this.$t('chart.outside'), value: 'outside' } + ], + labelPositionH: [ { name: this.$t('chart.text_pos_left'), value: 'left' }, + { name: this.$t('chart.center'), value: 'inside' }, { name: this.$t('chart.text_pos_right'), value: 'right' } ], + labelPositionV: [ + { name: this.$t('chart.text_pos_top'), value: 'top' }, + { name: this.$t('chart.center'), value: 'inside' }, + { name: this.$t('chart.text_pos_bottom'), value: 'bottom' } + ], predefineColors: COLOR_PANEL } }, watch: { 'chart': { handler: function() { + this.initOptions() this.initData() } } }, mounted() { this.init() + this.initOptions() this.initData() }, methods: { @@ -140,6 +148,18 @@ export default { this.isSetting = false } this.$emit('onLabelChange', this.labelForm) + }, + initOptions() { + const type = this.chart.type + if (type) { + if (type.includes('horizontal') || type === 'funnel') { + this.labelPosition = this.labelPositionH + } else if (type.includes('pie')) { + this.labelPosition = this.labelPositionPie + } else { + this.labelPosition = this.labelPositionV + } + } } } } diff --git a/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue b/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue index 52c90ecdf3..50142634b7 100644 --- a/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue +++ b/frontend/src/views/chart/components/shape-attr/LabelSelectorAntV.vue @@ -17,7 +17,7 @@ - + @@ -62,29 +62,35 @@ export default { labelForm: JSON.parse(JSON.stringify(DEFAULT_LABEL)), fontSize: [], isSetting: false, - labelPosition: [ - { name: this.$t('chart.inside'), value: 'middle' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, + labelPosition: [], + labelPositionPie: [ + { name: this.$t('chart.inside'), value: 'inner' }, + { name: this.$t('chart.outside'), value: 'outer' } + ], + labelPositionH: [ { name: this.$t('chart.text_pos_left'), value: 'left' }, + { name: this.$t('chart.center'), value: 'middle' }, { name: this.$t('chart.text_pos_right'), value: 'right' } ], + labelPositionV: [ + { name: this.$t('chart.text_pos_top'), value: 'top' }, + { name: this.$t('chart.center'), value: 'middle' }, + { name: this.$t('chart.text_pos_bottom'), value: 'bottom' } + ], predefineColors: COLOR_PANEL } }, watch: { 'chart': { handler: function() { - this.initLabelPosition() + this.initOptions() this.initData() } } }, mounted() { this.init() - this.initLabelPosition() + this.initOptions() this.initData() }, methods: { @@ -121,22 +127,16 @@ export default { } this.$emit('onLabelChange', this.labelForm) }, - initLabelPosition() { - if (this.chart && this.chart.type && this.chart.type.includes('pie')) { - this.labelPosition = [ - { name: this.$t('chart.inside'), value: 'inner' }, - { name: this.$t('chart.outside'), value: 'outer' } - ] - } else { - this.labelPosition = [ - { name: this.$t('chart.inside'), value: 'middle' }, - { name: this.$t('chart.outside'), value: 'outside' }, - { name: this.$t('chart.center'), value: 'center' }, - { name: this.$t('chart.text_pos_top'), value: 'top' }, - { name: this.$t('chart.text_pos_bottom'), value: 'bottom' }, - { name: this.$t('chart.text_pos_left'), value: 'left' }, - { name: this.$t('chart.text_pos_right'), value: 'right' } - ] + initOptions() { + const type = this.chart.type + if (type) { + if (type.includes('horizontal') || type === 'funnel') { + this.labelPosition = this.labelPositionH + } else if (type.includes('pie')) { + this.labelPosition = this.labelPositionPie + } else { + this.labelPosition = this.labelPositionV + } } } } diff --git a/frontend/src/views/chart/components/shape-attr/SizeSelector.vue b/frontend/src/views/chart/components/shape-attr/SizeSelector.vue index 362b8d15ab..892ea3bd90 100644 --- a/frontend/src/views/chart/components/shape-attr/SizeSelector.vue +++ b/frontend/src/views/chart/components/shape-attr/SizeSelector.vue @@ -128,9 +128,6 @@ - - {{ $t('chart.show') }} - diff --git a/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue b/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue index bedb4a527d..ad36ad6972 100644 --- a/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue +++ b/frontend/src/views/chart/components/shape-attr/SizeSelectorAntV.vue @@ -125,9 +125,6 @@ - - {{ $t('chart.show') }} - @@ -244,18 +241,6 @@ - - - - - - - - - - - -
diff --git a/frontend/src/views/chart/group/Group.vue b/frontend/src/views/chart/group/Group.vue index e7df267002..ce6df9f42e 100644 --- a/frontend/src/views/chart/group/Group.vue +++ b/frontend/src/views/chart/group/Group.vue @@ -196,9 +196,9 @@ :options="chartGroupTreeAvailable" :normalizer="normalizer" :placeholder="$t('chart.select_group')" - :noChildrenText="$t('commons.treeselect.no_children_text')" - :noOptionsText="$t('commons.treeselect.no_options_text')" - :noResultsText="$t('commons.treeselect.no_results_text')" + :no-children-text="$t('commons.treeselect.no_children_text')" + :no-options-text="$t('commons.treeselect.no_options_text')" + :no-results-text="$t('commons.treeselect.no_results_text')" /> @@ -558,7 +558,7 @@ export default { showClose: true }) this.treeNode() - this.$store.dispatch('chart/setTable', null) + this.$store.dispatch('chart/setTable', new Date().getTime()) }) } else { // this.$message({ @@ -773,6 +773,7 @@ export default { view.customFilter = JSON.stringify([]) view.drillFields = JSON.stringify([]) view.extBubble = JSON.stringify([]) + this.setChartDefaultOptions(view) const _this = this post('/chart/view/save', view).then(response => { this.closeCreateChart() @@ -788,6 +789,33 @@ export default { }) }, + setChartDefaultOptions(view) { + const type = view.type + const attr = JSON.parse(view.customAttr) + if (type.includes('pie')) { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'inner' + } + } else if (type.includes('line')) { + attr.label.position = 'top' + } else if (type.includes('treemap')) { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'middle' + } + } else { + if (view.render === 'echarts') { + attr.label.position = 'inside' + } else { + attr.label.position = 'middle' + } + } + view.customAttr = JSON.stringify(attr) + }, + getTable(table) { this.table = JSON.parse(JSON.stringify(table)) }, diff --git a/frontend/src/views/chart/view/ChartEdit.vue b/frontend/src/views/chart/view/ChartEdit.vue index e1e89fb7a5..9e06993987 100644 --- a/frontend/src/views/chart/view/ChartEdit.vue +++ b/frontend/src/views/chart/view/ChartEdit.vue @@ -1,6 +1,7 @@