feat:2.5.0
1. 完成重试任务、死信任务、定时任务、任务批次、用户权限详情页抽屉展示
This commit is contained in:
parent
722be7623d
commit
3a76423849
@ -12,7 +12,7 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface ServerNodeMapper extends BaseMapper<ServerNode> {
|
||||
|
||||
int insertOrUpdate(List<ServerNode> records);
|
||||
int insertOrUpdate(@Param("records") List<ServerNode> records);
|
||||
|
||||
int deleteByExpireAt(@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
|
@ -19,9 +19,9 @@
|
||||
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
|
||||
FROM job_task_batch a join job b on a.job_id = b.id
|
||||
<where>
|
||||
namespace_id = #{queryDO.namespaceId}
|
||||
a.namespace_id = #{queryDO.namespaceId}
|
||||
<if test="queryDO.jobId != null">
|
||||
and job_id = #{queryDO.jobId}
|
||||
and a.job_id = #{queryDO.jobId}
|
||||
</if>
|
||||
<if test="queryDO.groupName != null">
|
||||
and a.group_name = #{queryDO.groupName}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -15,16 +16,16 @@
|
||||
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
|
||||
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
|
||||
</sql>
|
||||
<insert id="insertBatch">
|
||||
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
|
||||
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
|
||||
idempotent_id, biz_no, executor_name, args_str,
|
||||
ext_attrs, create_dt
|
||||
)
|
||||
values
|
||||
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
|
||||
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
|
||||
</foreach>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -19,15 +20,15 @@
|
||||
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
create_dt, update_dt, task_type
|
||||
</sql>
|
||||
<!-- 定义批量新增的 SQL 映射 -->
|
||||
<insert id="batchInsert" parameterType="java.util.List">
|
||||
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
|
@ -19,9 +19,9 @@
|
||||
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
|
||||
FROM job_task_batch a join job b on a.job_id = b.id
|
||||
<where>
|
||||
namespace_id = #{queryDO.namespaceId}
|
||||
a.namespace_id = #{queryDO.namespaceId}
|
||||
<if test="queryDO.jobId != null">
|
||||
and job_id = #{queryDO.jobId}
|
||||
and a.job_id = #{queryDO.jobId}
|
||||
</if>
|
||||
<if test="queryDO.groupName != null">
|
||||
and a.group_name = #{queryDO.groupName}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -18,13 +19,13 @@
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
|
||||
</sql>
|
||||
<insert id="insertBatch">
|
||||
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
|
||||
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
|
||||
idempotent_id, biz_no, executor_name, args_str,
|
||||
ext_attrs, create_dt
|
||||
)
|
||||
values
|
||||
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
|
||||
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
|
||||
</foreach>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -19,15 +20,15 @@
|
||||
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
create_dt, update_dt, task_type
|
||||
</sql>
|
||||
<!-- 定义批量新增的 SQL 映射 -->
|
||||
<insert id="batchInsert" parameterType="java.util.List">
|
||||
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
</foreach>
|
||||
</insert>
|
||||
<update id="updateBatchNextTriggerAtById" parameterType="java.util.List">
|
||||
|
@ -7,6 +7,7 @@
|
||||
<id column="id" property="id" />
|
||||
<result column="group_name" property="groupName" />
|
||||
<result column="system_user_id" property="systemUserId" />
|
||||
<result column="namespace_id" property="namespaceId" />
|
||||
<result column="create_dt" property="createDt" />
|
||||
</resultMap>
|
||||
|
||||
|
@ -19,9 +19,9 @@
|
||||
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
|
||||
FROM job_task_batch a join job b on a.job_id = b.id
|
||||
<where>
|
||||
namespace_id = #{queryDO.namespaceId}
|
||||
a.namespace_id = #{queryDO.namespaceId}
|
||||
<if test="queryDO.jobId != null">
|
||||
and job_id = #{queryDO.jobId}
|
||||
and a.job_id = #{queryDO.jobId}
|
||||
</if>
|
||||
<if test="queryDO.groupName != null">
|
||||
and a.group_name = #{queryDO.groupName}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -15,16 +16,16 @@
|
||||
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
|
||||
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
|
||||
</sql>
|
||||
<insert id="insertBatch">
|
||||
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
|
||||
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
|
||||
idempotent_id, biz_no, executor_name, args_str,
|
||||
ext_attrs, create_dt
|
||||
)
|
||||
values
|
||||
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
|
||||
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
|
||||
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
|
||||
</foreach>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
|
||||
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
|
||||
<id column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
|
||||
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
|
||||
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
|
||||
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
|
||||
@ -19,15 +20,15 @@
|
||||
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
|
||||
create_dt, update_dt, task_type
|
||||
</sql>
|
||||
<!-- 定义批量新增的 SQL 映射 -->
|
||||
<insert id="batchInsert" parameterType="java.util.List">
|
||||
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
|
||||
</foreach>
|
||||
</insert>
|
||||
<update id="updateBatchNextTriggerAtById" parameterType="java.util.List">
|
||||
|
@ -159,7 +159,7 @@ public class ScanJobTaskActor extends AbstractActor {
|
||||
new LambdaQueryWrapper<Job>()
|
||||
.select(Job::getGroupName, Job::getNextTriggerAt, Job::getBlockStrategy, Job::getTriggerType,
|
||||
Job::getTriggerInterval, Job::getExecutorTimeout, Job::getTaskType, Job::getResident,
|
||||
Job::getId)
|
||||
Job::getId, Job::getNamespaceId)
|
||||
.eq(Job::getJobStatus, StatusEnum.YES.getStatus())
|
||||
.eq(Job::getDeleted, StatusEnum.NO.getStatus())
|
||||
.in(Job::getBucketIndex, scanTask.getBuckets())
|
||||
|
@ -129,7 +129,7 @@ public abstract class AbstractGenerator implements TaskGenerator {
|
||||
}
|
||||
|
||||
RetryTask retryTask = RetryTaskConverter.INSTANCE.toRetryTask(taskInfo);
|
||||
retryTask.setNamespaceId(sceneConfig.getNamespaceId());
|
||||
retryTask.setNamespaceId(taskContext.getNamespaceId());
|
||||
retryTask.setUniqueId(getIdGenerator(taskContext.getGroupName(), taskContext.getNamespaceId()));
|
||||
retryTask.setTaskType(TaskTypeEnum.RETRY.getType());
|
||||
retryTask.setGroupName(taskContext.getGroupName());
|
||||
|
@ -186,7 +186,8 @@ public abstract class AbstractScanGroup extends AbstractActor {
|
||||
.listPage(groupName, new PageDTO<>(0, systemProperties.getRetryPullPageSize()),
|
||||
new LambdaQueryWrapper<RetryTask>()
|
||||
.select(RetryTask::getId, RetryTask::getNextTriggerAt, RetryTask::getUniqueId,
|
||||
RetryTask::getGroupName, RetryTask::getRetryCount, RetryTask::getSceneName)
|
||||
RetryTask::getGroupName, RetryTask::getRetryCount, RetryTask::getSceneName,
|
||||
RetryTask::getNamespaceId)
|
||||
.eq(RetryTask::getRetryStatus, RetryStatusEnum.RUNNING.getStatus())
|
||||
.eq(RetryTask::getGroupName, groupName)
|
||||
.eq(RetryTask::getNamespaceId, namespaceId)
|
||||
|
@ -49,7 +49,8 @@ public class RetryTaskSchedule extends AbstractSchedule implements Lifecycle {
|
||||
try {
|
||||
Set<String> groupNameList = accessTemplate.getGroupConfigAccess()
|
||||
.list(new LambdaQueryWrapper<GroupConfig>()
|
||||
.select(GroupConfig::getGroupName).eq(GroupConfig::getGroupStatus, StatusEnum.YES))
|
||||
.select(GroupConfig::getGroupName)
|
||||
.eq(GroupConfig::getGroupStatus, StatusEnum.YES.getStatus()))
|
||||
.stream().map(GroupConfig::getGroupName).collect(Collectors.toSet());
|
||||
|
||||
for (String groupName : groupNameList) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.aizuda.easy.retry.server.web.controller;
|
||||
|
||||
import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
|
||||
import com.aizuda.easy.retry.server.web.service.SystemUserService;
|
||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
|
||||
@ -62,6 +63,12 @@ public class SystemUserController {
|
||||
return systemUserService.getSystemUserByUserName(username);
|
||||
}
|
||||
|
||||
@LoginRequired(role = RoleEnum.ADMIN)
|
||||
@GetMapping("/user-permissions/{id}")
|
||||
public List<PermissionsResponseVO> getSystemUserPermissionByUserName(@PathVariable("id") Long id) {
|
||||
return systemUserService.getSystemUserPermissionByUserName(id);
|
||||
}
|
||||
|
||||
@LoginRequired
|
||||
@DeleteMapping("/user/{id}")
|
||||
public boolean delUser(@PathVariable("id") Long id) {
|
||||
|
@ -29,12 +29,6 @@ public class JobRequestVO {
|
||||
@NotBlank(message = "jobName 不能为空")
|
||||
private String jobName;
|
||||
|
||||
/**
|
||||
* 命名空间id
|
||||
*/
|
||||
@NotNull(message = "命名空间id 不能为空")
|
||||
private String namespaceId;
|
||||
|
||||
/**
|
||||
* 重试状态 0、关闭、1、开启
|
||||
*/
|
||||
|
@ -20,12 +20,6 @@ public class NotifyConfigRequestVO {
|
||||
@Pattern(regexp = "^[A-Za-z0-9_]{1,64}$", message = "仅支持长度为1~64字符且类型为数字、字母和下划线")
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 命名空间id
|
||||
*/
|
||||
@NotNull(message = "命名空间id 不能为空")
|
||||
private String namespaceId;
|
||||
|
||||
private String sceneName;
|
||||
|
||||
@NotNull(message = "通知状态不能为空")
|
||||
|
@ -2,6 +2,9 @@ package com.aizuda.easy.retry.server.web.model.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author: xiaowoniu
|
||||
* @date : 2023-11-23 14:01
|
||||
@ -12,4 +15,6 @@ public class PermissionsResponseVO {
|
||||
|
||||
private String groupName;
|
||||
private String namespaceId;
|
||||
private String namespaceName;
|
||||
private Set<String> groupNames;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
|
||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
|
||||
import com.aizuda.easy.retry.server.web.model.request.SystemUserRequestVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.SystemUserResponseVO;
|
||||
|
||||
import java.util.List;
|
||||
@ -31,4 +32,6 @@ public interface SystemUserService {
|
||||
SystemUserResponseVO getSystemUserByUserName(String username);
|
||||
|
||||
boolean delUser(Long id);
|
||||
|
||||
List<PermissionsResponseVO> getSystemUserPermissionByUserName(Long id);
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ public class JobServiceImpl implements JobService {
|
||||
job.setBucketIndex(HashUtil.bkdrHash(jobRequestVO.getGroupName() + jobRequestVO.getJobName())
|
||||
% systemProperties.getBucketTotal());
|
||||
job.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli()));
|
||||
job.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
|
||||
return 1 == jobMapper.insert(job);
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class NotifyConfigServiceImpl implements NotifyConfigService {
|
||||
public Boolean saveNotify(NotifyConfigRequestVO requestVO) {
|
||||
NotifyConfig notifyConfig = NotifyConfigConverter.INSTANCE.toNotifyConfig(requestVO);
|
||||
notifyConfig.setCreateDt(LocalDateTime.now());
|
||||
|
||||
notifyConfig.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
|
||||
ConfigAccess<NotifyConfig> notifyConfigAccess = accessTemplate.getNotifyConfigAccess();
|
||||
|
||||
Assert.isTrue(1 == notifyConfigAccess.insert(notifyConfig),
|
||||
|
@ -122,8 +122,8 @@ public class SceneConfigServiceImpl implements SceneConfigService {
|
||||
Assert.isTrue(1 == accessTemplate.getSceneConfigAccess().update(sceneConfig,
|
||||
new LambdaUpdateWrapper<SceneConfig>()
|
||||
.eq(SceneConfig::getNamespaceId, namespaceId)
|
||||
.eq(SceneConfig::getGroupName, sceneConfig.getGroupName())
|
||||
.eq(SceneConfig::getSceneName, sceneConfig.getSceneName())),
|
||||
.eq(SceneConfig::getGroupName, requestVO.getGroupName())
|
||||
.eq(SceneConfig::getSceneName, requestVO.getSceneName())),
|
||||
() -> new EasyRetryServerException("failed to update scene. sceneConfig:[{}]",
|
||||
JsonUtil.toJsonString(sceneConfig)));
|
||||
return Boolean.TRUE;
|
||||
|
@ -1,12 +1,15 @@
|
||||
package com.aizuda.easy.retry.server.web.service.impl;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.aizuda.easy.retry.server.common.config.SystemProperties;
|
||||
import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
|
||||
import com.aizuda.easy.retry.server.common.triple.Triple;
|
||||
import com.aizuda.easy.retry.server.web.model.request.UserPermissionRequestVO;
|
||||
import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
|
||||
import com.aizuda.easy.retry.server.web.service.convert.NamespaceResponseVOConverter;
|
||||
import com.aizuda.easy.retry.server.web.service.convert.PermissionsResponseVOConverter;
|
||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.NamespaceMapper;
|
||||
@ -27,14 +30,13 @@ import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
|
||||
import com.aizuda.easy.retry.server.web.model.request.SystemUserRequestVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.SystemUserResponseVO;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -230,30 +232,57 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
}
|
||||
|
||||
SystemUserResponseVO responseVO = SystemUserResponseVOConverter.INSTANCE.convert(systemUser);
|
||||
if (RoleEnum.ADMIN.getRoleId().equals(systemUser.getRole())) {
|
||||
return responseVO;
|
||||
}
|
||||
getPermission(systemUser.getRole(), systemUser.getId(), responseVO);
|
||||
|
||||
List<SystemUserPermission> systemUserPermissions = systemUserPermissionMapper.selectList(
|
||||
new LambdaQueryWrapper<SystemUserPermission>()
|
||||
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
|
||||
.eq(SystemUserPermission::getSystemUserId, responseVO.getId()));
|
||||
|
||||
LambdaQueryWrapper<Namespace> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.select(Namespace::getId, Namespace::getUniqueId, Namespace::getName);
|
||||
queryWrapper.in(Namespace::getUniqueId, systemUserPermissions.stream()
|
||||
.map(SystemUserPermission::getNamespaceId).distinct().collect(Collectors.toList()));
|
||||
List<Namespace> namespaces = namespaceMapper.selectList(queryWrapper);
|
||||
responseVO.setNamespaceIds(
|
||||
NamespaceResponseVOConverter.INSTANCE.toNamespaceResponseVOs(namespaces));
|
||||
|
||||
new LambdaQueryWrapper<SystemUserPermission>()
|
||||
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
|
||||
.eq(SystemUserPermission::getSystemUserId, responseVO.getId()));
|
||||
responseVO.setPermissions(PermissionsResponseVOConverter.INSTANCE.toPermissionsResponseVOs(systemUserPermissions));
|
||||
|
||||
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionsResponseVO> getSystemUserPermissionByUserName(Long id) {
|
||||
|
||||
List<SystemUserPermission> systemUserPermissions = systemUserPermissionMapper.selectList(
|
||||
new LambdaQueryWrapper<SystemUserPermission>()
|
||||
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
|
||||
.eq(SystemUserPermission::getSystemUserId, id));
|
||||
|
||||
if (CollectionUtils.isEmpty(systemUserPermissions)) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
Map<String, List<SystemUserPermission>> permissionsMap = systemUserPermissions.stream().collect(Collectors.groupingBy(SystemUserPermission::getNamespaceId));
|
||||
|
||||
LambdaQueryWrapper<Namespace> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.select(Namespace::getId, Namespace::getUniqueId, Namespace::getName);
|
||||
queryWrapper.in(Namespace::getUniqueId, permissionsMap.keySet());
|
||||
List<Namespace> namespaces = namespaceMapper.selectList(queryWrapper);
|
||||
Map<String, String> map = namespaces.stream().collect(Collectors.toMap(Namespace::getUniqueId, Namespace::getName));
|
||||
|
||||
List<PermissionsResponseVO> response = new ArrayList<>();
|
||||
permissionsMap.forEach((namespaceId, values) -> {
|
||||
PermissionsResponseVO responseVO = new PermissionsResponseVO();
|
||||
responseVO.setNamespaceName(map.get(namespaceId));
|
||||
responseVO.setNamespaceId(namespaceId);
|
||||
responseVO.setGroupNames(values.stream().map(SystemUserPermission::getGroupName).collect(Collectors.toSet()));
|
||||
response.add(responseVO);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean delUser(final Long id) {
|
||||
systemUserPermissionMapper.delete(
|
||||
new LambdaQueryWrapper<SystemUserPermission>()
|
||||
.eq(SystemUserPermission::getSystemUserId, id)
|
||||
);
|
||||
return 1 == systemUserMapper.deleteById(id);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ const api = {
|
||||
delUser: '/user/',
|
||||
saveUser: '/user',
|
||||
systemUserByUserName: '/user/username/user-info',
|
||||
systemUserPermissionByUserId: '/user-permissions/',
|
||||
countTask: '/dashboard/task/count',
|
||||
countDispatch: '/dashboard/dispatch/count',
|
||||
countActivePod: '/dashboard/active-pod/count',
|
||||
@ -359,6 +360,13 @@ export function getSystemUserByUserName (parameter) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getSystemUserPermissionByUserId (id) {
|
||||
return request({
|
||||
url: api.systemUserPermissionByUserId + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getNotifyConfigList (parameter) {
|
||||
return request({
|
||||
url: api.notifyConfigList,
|
||||
|
81
frontend/src/components/Drawer/Drawer.vue
Normal file
81
frontend/src/components/Drawer/Drawer.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
placement="right"
|
||||
:width="width"
|
||||
@afterVisibleChange="afterVisibleChange"
|
||||
:visible="visible"
|
||||
@close="onClose"
|
||||
>
|
||||
<span slot="title">
|
||||
{{ title }}
|
||||
<button class="ant-drawer-close" style="margin-right: 40px" v-if="visibleAmplify">
|
||||
<a-icon type="arrows-alt" style="font-size : 16px;" @click="this.onAmplify"/>
|
||||
</button>
|
||||
</span>
|
||||
<slot></slot>
|
||||
</a-drawer>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
visibleAmplify: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 300
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClose () {
|
||||
this.$emit('closeDrawer')
|
||||
},
|
||||
onAmplify () {
|
||||
this.$emit('handlerAmplify')
|
||||
},
|
||||
afterVisibleChange () {
|
||||
this.$emit('afterVisibleChange')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.amplify {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
display: block;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
line-height: 56px;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
-webkit-transition: color 0.3s;
|
||||
transition: color 0.3s;
|
||||
text-rendering: auto;
|
||||
margin-right: 45px;
|
||||
}
|
||||
</style>
|
3
frontend/src/components/Drawer/index.js
Normal file
3
frontend/src/components/Drawer/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import Drawer from './Drawer.vue'
|
||||
|
||||
export default Drawer
|
@ -25,6 +25,7 @@ import IconSelector from '@/components/IconSelector'
|
||||
import TagSelect from '@/components/TagSelect'
|
||||
import StandardFormRow from '@/components/StandardFormRow'
|
||||
import ArticleListContent from '@/components/ArticleListContent'
|
||||
import Drawer from '@/components/Drawer'
|
||||
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
@ -53,6 +54,6 @@ export {
|
||||
StandardFormRow,
|
||||
ArticleListContent,
|
||||
G2Line,
|
||||
|
||||
Drawer,
|
||||
Dialog
|
||||
}
|
||||
|
@ -76,20 +76,20 @@ export const asyncRouterMap = [
|
||||
name: 'RetryTaskInfo',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/RetryTaskInfo'),
|
||||
meta: { title: '任务管理详情', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
meta: { title: '重试任务详情', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/dead-letter/list',
|
||||
name: 'RetryDeadLetterList',
|
||||
component: () => import('@/views/task/RetryDeadLetterList'),
|
||||
meta: { title: '死信队列', icon: 'profile', permission: ['retryDeadLetter'] }
|
||||
meta: { title: '死信任务', icon: 'profile', permission: ['retryDeadLetter'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/dead-letter/info',
|
||||
name: 'RetryDeadLetterInfo',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/RetryDeadLetterInfo'),
|
||||
meta: { title: '死信队列管理详情', icon: 'profile', permission: ['retryDeadLetter'] }
|
||||
meta: { title: '死信任务详情', icon: 'profile', permission: ['retryDeadLetter'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/log/list',
|
||||
@ -143,14 +143,14 @@ export const asyncRouterMap = [
|
||||
path: '/job/list',
|
||||
name: 'JobList',
|
||||
component: () => import('@/views/job/JobList'),
|
||||
meta: { title: '任务信息', icon: 'profile', permission: ['job'] }
|
||||
meta: { title: '任务信息', keepAlive: true, icon: 'profile', permission: ['job'] }
|
||||
},
|
||||
{
|
||||
path: '/job/info',
|
||||
name: 'JobInfo',
|
||||
hidden: true,
|
||||
component: () => import('@/views/job/JobInfo'),
|
||||
meta: { title: '定时任务详情', icon: 'profile', permission: ['job'] }
|
||||
meta: { title: '定时任务详情', keepAlive: true, icon: 'profile', permission: ['job'] }
|
||||
},
|
||||
{
|
||||
path: '/job/config',
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<page-header-wrapper @back="() => $router.replace('/job/batch/list')" style="margin: -24px -1px 0"v-if="showHeader">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
<a-card :bordered="false" v-if="jobBatchInfo !==null ">
|
||||
<a-descriptions title="" :column="3" bordered>
|
||||
<a-descriptions title="" :column="column" bordered>
|
||||
<a-descriptions-item label="组名称">
|
||||
{{ jobBatchInfo.groupName }}
|
||||
</a-descriptions-item>
|
||||
@ -38,14 +38,15 @@
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
<div style="margin: 20px 0; border-left: #f5222d 5px solid; font-size: medium; font-weight: bold">
|
||||
任务项列表
|
||||
<span style="padding-left: 18px">任务项列表</span>
|
||||
<span style="padding-left: 18px"><a-icon type="sync" @click="()=> this.$refs.JobTaskListRef.refreshTable(this.queryParam)"/></span>
|
||||
</div>
|
||||
<JobTaskList ref="JobTaskListRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { jobBatchDetail, jobTaskList } from '@/api/jobApi'
|
||||
import { jobBatchDetail } from '@/api/jobApi'
|
||||
import moment from 'moment'
|
||||
import enums from '@/utils/jobEnum'
|
||||
import JobTaskList from './JobTaskList'
|
||||
@ -56,6 +57,16 @@ export default {
|
||||
JobTaskList
|
||||
|
||||
},
|
||||
props: {
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
jobBatchInfo: null,
|
||||
@ -64,13 +75,26 @@ export default {
|
||||
taskType: enums.taskType,
|
||||
triggerType: enums.triggerType,
|
||||
blockStrategy: enums.blockStrategy,
|
||||
executorType: enums.executorType
|
||||
executorType: enums.executorType,
|
||||
queryParam: {}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const id = this.$route.query.id
|
||||
const groupName = this.$route.query.groupName
|
||||
if (id && groupName) {
|
||||
this.jobBatchDetail(id)
|
||||
} else {
|
||||
if (this.showHeader) {
|
||||
this.$router.push({ path: '/404' })
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
jobBatchDetail (id) {
|
||||
jobBatchDetail(id).then(res => {
|
||||
this.jobBatchInfo = res.data
|
||||
this.queryParam = {
|
||||
@ -79,14 +103,6 @@ export default {
|
||||
}
|
||||
this.$refs.JobTaskListRef.refreshTable(this.queryParam)
|
||||
})
|
||||
} else {
|
||||
this.$router.push({ path: '/404' })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
jobTaskList,
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,13 +77,6 @@
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="table-operator">
|
||||
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
|
||||
<a-menu slot="overlay" @click="onClick">
|
||||
<a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>
|
||||
<a-menu-item key="2"><a-icon type="edit" />更新</a-menu-item>
|
||||
</a-menu>
|
||||
<a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
|
||||
<s-table
|
||||
@ -97,7 +90,7 @@
|
||||
:scroll="{ x: 1800 }"
|
||||
>
|
||||
<span slot="serial" slot-scope="record">
|
||||
{{ record.id }}
|
||||
<a href="#" @click="handlerOpenDrawer(record)">{{ record.id }}</a>
|
||||
</span>
|
||||
<span slot="taskBatchStatus" slot-scope="text">
|
||||
<a-tag :color="taskBatchStatus[text].color">
|
||||
@ -119,60 +112,39 @@
|
||||
>
|
||||
<a href="javascript:;" v-if="record.taskBatchStatus === 1 || record.taskBatchStatus === 2">停止</a>
|
||||
</a-popconfirm>
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="是否暂停?"-->
|
||||
<!-- ok-text="恢复"-->
|
||||
<!-- cancel-text="取消"-->
|
||||
<!-- @confirm="handleSuspend(record)"-->
|
||||
<!-- >-->
|
||||
<!-- <a href="javascript:;" v-if="record.retryStatus === 0">暂停</a>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
<!-- <a-divider type="vertical" v-if="record.retryStatus === 0" />-->
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="是否恢复?"-->
|
||||
<!-- ok-text="恢复"-->
|
||||
<!-- cancel-text="取消"-->
|
||||
<!-- @confirm="handleRecovery(record)"-->
|
||||
<!-- >-->
|
||||
<!-- <a href="javascript:;" v-if="record.retryStatus === 3">恢复</a>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
<!-- <a-divider type="vertical" v-if="record.retryStatus === 3" />-->
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="是否完成?"-->
|
||||
<!-- ok-text="完成"-->
|
||||
<!-- cancel-text="取消"-->
|
||||
<!-- @confirm="handleFinish(record)"-->
|
||||
<!-- >-->
|
||||
<!-- <a href="javascript:;" v-if="record.retryStatus !== 1 && record.retryStatus !== 2">完成</a>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
<!-- <a-divider type="vertical" v-if="record.retryStatus !== 1 && record.retryStatus !== 2" />-->
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="是否执行任务?"-->
|
||||
<!-- ok-text="执行"-->
|
||||
<!-- cancel-text="取消"-->
|
||||
<!-- @confirm="handleTrigger(record)"-->
|
||||
<!-- >-->
|
||||
<!-- <a href="javascript:;" v-if="record.retryStatus !== 1 && record.retryStatus !== 2">执行</a>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
|
||||
</template>
|
||||
</span>
|
||||
</s-table>
|
||||
|
||||
<Drawer
|
||||
title="任务详情"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="true"
|
||||
:visible="openDrawer"
|
||||
@closeDrawer="onClose"
|
||||
@handlerAmplify="handleInfo"
|
||||
>
|
||||
<job-batch-info ref="jobBatchInfoRef" :showHeader="false" :column="2"/>
|
||||
</Drawer>
|
||||
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||
import AInput from 'ant-design-vue/es/input/Input'
|
||||
import { STable } from '@/components'
|
||||
import { Drawer, STable } from '@/components'
|
||||
import { jobBatchList, jobNameList, stop } from '@/api/jobApi'
|
||||
import { getAllGroupNameList } from '@/api/manage'
|
||||
import JobBatchInfo from '@/views/job/JobBatchInfo'
|
||||
const enums = require('@/utils/jobEnum')
|
||||
|
||||
export default {
|
||||
name: 'JobBatchList',
|
||||
components: {
|
||||
JobBatchInfo,
|
||||
Drawer,
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable
|
||||
@ -266,7 +238,9 @@ export default {
|
||||
},
|
||||
optionAlertShow: false,
|
||||
groupNameList: [],
|
||||
jobNameList: []
|
||||
jobNameList: [],
|
||||
openDrawer: false,
|
||||
currentShowRecord: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -285,27 +259,19 @@ export default {
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleNew () {
|
||||
this.$refs.saveRetryTask.isShow(true, null)
|
||||
},
|
||||
handleBatchNew () {
|
||||
this.$refs.batchSaveRetryTask.isShow(true, null)
|
||||
},
|
||||
handleSearch (value) {
|
||||
console.log(`selected ${value}`)
|
||||
jobNameList({ keywords: value }).then(res => {
|
||||
this.jobNameList = res.data
|
||||
})
|
||||
},
|
||||
handleChange (value) {
|
||||
console.log(`handleChange ${value}`)
|
||||
// this.queryParam['jobId'] = value
|
||||
// this.$refs.table.refresh(true)
|
||||
},
|
||||
toggleAdvanced () {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
handleInfo (record) {
|
||||
record = record || this.currentShowRecord
|
||||
this.$router.push({ path: '/job/batch/info', query: { id: record.id, groupName: record.groupName } })
|
||||
},
|
||||
handleOk (record) {},
|
||||
@ -320,51 +286,6 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
handleRecovery (record) {
|
||||
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 0 }).then((res) => {
|
||||
// const { status } = res
|
||||
// if (status === 0) {
|
||||
// this.$message.error('恢复失败')
|
||||
// } else {
|
||||
// this.$refs.table.refresh(true)
|
||||
// this.$message.success('恢复成功')
|
||||
// }
|
||||
// })
|
||||
},
|
||||
handleFinish (record) {
|
||||
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 1 }).then((res) => {
|
||||
// const { status } = res
|
||||
// if (status === 0) {
|
||||
// this.$message.error('执行失败')
|
||||
// } else {
|
||||
// this.$refs.table.refresh(true)
|
||||
// this.$message.success('执行成功')
|
||||
// }
|
||||
// })
|
||||
},
|
||||
handleTrigger (record) {
|
||||
// if (record.taskType === 1) {
|
||||
// manualTriggerRetryTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
|
||||
// const { status } = res
|
||||
// if (status === 0) {
|
||||
// this.$message.error('执行失败')
|
||||
// } else {
|
||||
// this.$refs.table.refresh(true)
|
||||
// this.$message.success('执行成功')
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// manualTriggerCallbackTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
|
||||
// const { status } = res
|
||||
// if (status === 0) {
|
||||
// this.$message.error('执行失败')
|
||||
// } else {
|
||||
// this.$refs.table.refresh(true)
|
||||
// this.$message.success('执行完成')
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
},
|
||||
refreshTable (v) {
|
||||
this.$refs.table.refresh(true)
|
||||
},
|
||||
@ -372,32 +293,16 @@ export default {
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.selectedRows = selectedRows
|
||||
},
|
||||
handlerDel () {
|
||||
// var that = this
|
||||
// this.$confirm({
|
||||
// title: '您要删除这些数据吗?',
|
||||
// content: h => <div style="color:red;">删除后数据不可恢复,请确认!</div>,
|
||||
// onOk () {
|
||||
// batchDelete({ groupName: that.selectedRows[0].groupName, ids: that.selectedRowKeys }).then(res => {
|
||||
// that.$refs.table.refresh(true)
|
||||
// that.$message.success(`成功删除${res.data}条数据`)
|
||||
// that.selectedRowKeys = []
|
||||
// })
|
||||
// },
|
||||
// onCancel () {
|
||||
// },
|
||||
// class: 'test'
|
||||
// })
|
||||
handlerOpenDrawer (record) {
|
||||
this.currentShowRecord = record
|
||||
this.openDrawer = true
|
||||
setTimeout(() => {
|
||||
this.$refs.jobBatchInfoRef.jobBatchDetail(record.id)
|
||||
}, 200)
|
||||
},
|
||||
onClick ({ key }) {
|
||||
if (key === '2') {
|
||||
this.$refs.batchUpdateRetryTaskInfo.isShow(true, this.selectedRows, this.selectedRowKeys)
|
||||
return
|
||||
}
|
||||
|
||||
if (key === '1') {
|
||||
this.handlerDel()
|
||||
}
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.currentShowRecord = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<page-header-wrapper @back="() => $router.replace('/job/list')" style="margin: -24px -1px 0" v-if="showHeader">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
<a-card :bordered="false" v-if="jobInfo !==null ">
|
||||
<a-descriptions title="" :column="4" bordered>
|
||||
<a-descriptions title="" :column="column" bordered>
|
||||
<a-descriptions-item label="组名称">
|
||||
{{ jobInfo.groupName }}
|
||||
</a-descriptions-item>
|
||||
@ -84,7 +84,16 @@ import enums from '@/utils/jobEnum'
|
||||
export default {
|
||||
name: 'JobInfo',
|
||||
components: {
|
||||
|
||||
},
|
||||
props: {
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 4
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@ -101,16 +110,21 @@ export default {
|
||||
const id = this.$route.query.id
|
||||
const groupName = this.$route.query.groupName
|
||||
if (id && groupName) {
|
||||
getJobDetail(id).then(res => {
|
||||
this.jobInfo = res.data
|
||||
})
|
||||
this.jobDetail(id)
|
||||
} else {
|
||||
this.$router.push({ path: '/404' })
|
||||
if (this.showHeader) {
|
||||
this.$router.push({ path: '/404' })
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
jobDetail (id) {
|
||||
getJobDetail(id).then(res => {
|
||||
this.jobInfo = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,10 +65,8 @@
|
||||
<a-button type="primary" icon="plus" @click="handleNew()">新增</a-button>
|
||||
<!-- <a-button type="primary" icon="plus" @click="handleBatchNew()">批量</a-button>-->
|
||||
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
|
||||
<a-menu slot="overlay" @click="onClick">
|
||||
<!-- <a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>-->
|
||||
</a-menu>
|
||||
<!-- <a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>-->
|
||||
<!-- <a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>-->
|
||||
<!-- <a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>-->
|
||||
</a-dropdown>
|
||||
</div>
|
||||
|
||||
@ -83,6 +81,9 @@
|
||||
<span slot="serial" slot-scope="text, record">
|
||||
{{ record.id }}
|
||||
</span>
|
||||
<span slot="jobName" slot-scope="text, record">
|
||||
<a href="#" @click="handlerOpenDrawer(record)">{{ text }}</a>
|
||||
</span>
|
||||
<span slot="taskType" slot-scope="text">
|
||||
<a-tag :color="taskType[text].color">
|
||||
{{ taskType[text].name }}
|
||||
@ -157,23 +158,38 @@
|
||||
</span>
|
||||
</s-table>
|
||||
|
||||
<Drawer
|
||||
title="任务详情"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="true"
|
||||
:visible="openDrawer"
|
||||
@closeDrawer="onClose"
|
||||
@handlerAmplify="handleInfo"
|
||||
>
|
||||
<job-info ref="jobInfoRef" :showHeader="false" :column="2"/>
|
||||
</Drawer>
|
||||
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||
import AInput from 'ant-design-vue/es/input/Input'
|
||||
import { STable } from '@/components'
|
||||
import { STable, Drawer } from '@/components'
|
||||
import { delJob, getJobList, triggerJob, updateJobStatus } from '@/api/jobApi'
|
||||
import { getAllGroupNameList } from '@/api/manage'
|
||||
import enums from '@/utils/jobEnum'
|
||||
import JobInfo from '@/views/job/JobInfo'
|
||||
|
||||
export default {
|
||||
name: 'JobList',
|
||||
components: {
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable
|
||||
STable,
|
||||
JobInfo,
|
||||
Drawer
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@ -205,6 +221,7 @@ export default {
|
||||
{
|
||||
title: '任务名称',
|
||||
dataIndex: 'jobName',
|
||||
scopedSlots: { customRender: 'jobName' },
|
||||
ellipsis: true,
|
||||
width: '10%'
|
||||
},
|
||||
@ -282,7 +299,9 @@ export default {
|
||||
},
|
||||
optionAlertShow: false,
|
||||
groupNameList: [],
|
||||
sceneList: []
|
||||
sceneList: [],
|
||||
openDrawer: false,
|
||||
currentShowRecord: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -306,6 +325,7 @@ export default {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
handleInfo (record) {
|
||||
record = record || this.currentShowRecord
|
||||
this.$router.push({ path: '/job/info', query: { id: record.id, groupName: record.groupName } })
|
||||
},
|
||||
handleOk (record) {},
|
||||
@ -360,15 +380,16 @@ export default {
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.selectedRows = selectedRows
|
||||
},
|
||||
onClick ({ key }) {
|
||||
if (key === '2') {
|
||||
this.$refs.batchUpdateRetryTaskInfo.isShow(true, this.selectedRows, this.selectedRowKeys)
|
||||
return
|
||||
}
|
||||
|
||||
if (key === '1') {
|
||||
this.handlerDel()
|
||||
}
|
||||
handlerOpenDrawer (record) {
|
||||
this.currentShowRecord = record
|
||||
this.openDrawer = true
|
||||
setTimeout(() => {
|
||||
this.$refs.jobInfoRef.jobDetail(record.id)
|
||||
}, 200)
|
||||
},
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.currentShowRecord = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +283,7 @@
|
||||
{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: '分片参数必填',
|
||||
message: '分片参数必填'
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<page-header-wrapper @back="() => $router.replace('/retry/dead-letter/list')" style="margin: -24px -1px 0" v-if="showHeader">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
|
||||
<a-card :bordered="false">
|
||||
<a-descriptions title="" bordered v-if='retryDealLetterInfo !== null'>
|
||||
<a-descriptions title="" :column="column" bordered v-if="retryDealLetterInfo !== null">
|
||||
<a-descriptions-item label="组名称">
|
||||
{{ retryDealLetterInfo.groupName }}
|
||||
</a-descriptions-item>
|
||||
@ -43,10 +43,19 @@
|
||||
|
||||
<script>
|
||||
import { getRetryDeadLetterById } from '@/api/manage'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'RetryDeadLetterInfo',
|
||||
props: {
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
retryDealLetterInfo: null,
|
||||
@ -66,15 +75,16 @@ export default {
|
||||
const id = this.$route.query.id
|
||||
const groupName = this.$route.query.groupName
|
||||
if (id && groupName) {
|
||||
this.retryDeadLetterById(id, groupName)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
retryDeadLetterById (id, groupName) {
|
||||
getRetryDeadLetterById(id, { 'groupName': groupName }).then(res => {
|
||||
this.retryDealLetterInfo = res.data
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -66,10 +66,10 @@
|
||||
:data="loadData"
|
||||
:alert="options.alert"
|
||||
:rowSelection="options.rowSelection"
|
||||
:scroll="{ x: 2000 }"
|
||||
:scroll="{ x: 1800 }"
|
||||
>
|
||||
<span slot="serial" slot-scope="text, record">
|
||||
{{ record.id }}
|
||||
<span slot="uniqueId" slot-scope="text, record">
|
||||
<a href="#" @click="handlerOpenDrawer(record)">{{ text }}</a>
|
||||
</span>
|
||||
<span slot="taskType" slot-scope="text">
|
||||
<a-tag :color="taskType[text].color">
|
||||
@ -110,6 +110,18 @@
|
||||
</a-dropdown>
|
||||
</span>
|
||||
</s-table>
|
||||
|
||||
<Drawer
|
||||
title="任务详情"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="true"
|
||||
:visible="openDrawer"
|
||||
@closeDrawer="onClose"
|
||||
@handlerAmplify="handleInfo"
|
||||
>
|
||||
<retry-dead-letter-info ref="retryDeadLetterInfoRef" :showHeader="false" :column="1"/>
|
||||
</Drawer>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@ -125,12 +137,15 @@ import {
|
||||
deleteRetryDeadLetter
|
||||
} from '@/api/manage'
|
||||
|
||||
import { STable } from '@/components'
|
||||
import { Drawer, STable } from '@/components'
|
||||
import moment from 'moment'
|
||||
import RetryDeadLetterInfo from '@/views/task/RetryDeadLetterInfo'
|
||||
|
||||
export default {
|
||||
name: 'RetryDeadLetterList',
|
||||
components: {
|
||||
RetryDeadLetterInfo,
|
||||
Drawer,
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable
|
||||
@ -157,9 +172,10 @@ export default {
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: '#',
|
||||
scopedSlots: { customRender: 'serial' },
|
||||
width: '5%'
|
||||
title: 'UniqueId',
|
||||
dataIndex: 'uniqueId',
|
||||
fixed: 'left',
|
||||
scopedSlots: { customRender: 'uniqueId' }
|
||||
},
|
||||
{
|
||||
title: '组名称',
|
||||
@ -171,11 +187,6 @@ export default {
|
||||
dataIndex: 'sceneName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'UniqueId',
|
||||
dataIndex: 'uniqueId',
|
||||
width: '10%'
|
||||
},
|
||||
{
|
||||
title: '幂等id',
|
||||
dataIndex: 'idempotentId',
|
||||
@ -189,15 +200,13 @@ export default {
|
||||
{
|
||||
title: '任务类型',
|
||||
dataIndex: 'taskType',
|
||||
scopedSlots: { customRender: 'taskType' },
|
||||
width: '5%'
|
||||
scopedSlots: { customRender: 'taskType' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createDt',
|
||||
sorter: true,
|
||||
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
|
||||
ellipsis: true
|
||||
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@ -227,7 +236,9 @@ export default {
|
||||
},
|
||||
optionAlertShow: false,
|
||||
groupNameList: [],
|
||||
sceneList: []
|
||||
sceneList: [],
|
||||
currentShowRecord: null,
|
||||
openDrawer: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -267,6 +278,7 @@ export default {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
handleInfo (record) {
|
||||
record = record || this.currentShowRecord
|
||||
this.$router.push({ path: '/retry/dead-letter/info', query: { id: record.id, groupName: record.groupName } })
|
||||
},
|
||||
onClick ({ key }) {
|
||||
@ -314,6 +326,17 @@ export default {
|
||||
onSelectChange (selectedRowKeys, selectedRows) {
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.selectedRows = selectedRows
|
||||
},
|
||||
handlerOpenDrawer (record) {
|
||||
this.currentShowRecord = record
|
||||
this.openDrawer = true
|
||||
setTimeout(() => {
|
||||
this.$refs.retryDeadLetterInfoRef.retryDeadLetterById(record.id, record.groupName)
|
||||
}, 200)
|
||||
},
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.currentShowRecord = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<page-header-wrapper @back="() => $router.replace('/retry/log/list')" style="margin: -24px -1px 0" v-if="showHeader">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
<a-card :bordered="false">
|
||||
<a-descriptions title="" bordered v-if="retryInfo !== null">
|
||||
<a-descriptions title="" :column="column" bordered v-if="retryInfo !== null">
|
||||
<a-descriptions-item label="组名称">
|
||||
{{ retryInfo.groupName }}
|
||||
</a-descriptions-item>
|
||||
@ -30,7 +30,7 @@
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ retryInfo.createDt }}
|
||||
{{ retryInfo.createDt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="执行器名称" :span="3">
|
||||
{{ retryInfo.executorName }}
|
||||
@ -49,7 +49,6 @@
|
||||
|
||||
<script>
|
||||
import { getRetryTaskLogById } from '@/api/manage'
|
||||
import moment from 'moment'
|
||||
import { STable } from '@/components'
|
||||
import RetryTaskLogMessageList from '@/views/task/RetryTaskLogMessageList'
|
||||
|
||||
@ -59,6 +58,16 @@ export default {
|
||||
RetryTaskLogMessageList,
|
||||
STable
|
||||
},
|
||||
props: {
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
retryInfo: null,
|
||||
@ -82,6 +91,11 @@ export default {
|
||||
created () {
|
||||
const id = this.$route.query.id
|
||||
if (id) {
|
||||
this.getRetryTaskLogById(id)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getRetryTaskLogById (id) {
|
||||
getRetryTaskLogById(id).then(res => {
|
||||
this.retryInfo = res.data
|
||||
this.queryParam = {
|
||||
@ -91,11 +105,7 @@ export default {
|
||||
this.$refs.retryTaskLogMessageListRef.refreshTable(this.queryParam)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -58,8 +58,8 @@
|
||||
:rowSelection="options.rowSelection"
|
||||
:scroll="{ x: 2000 }"
|
||||
>
|
||||
<span slot="serial" slot-scope="text, record, index">
|
||||
{{ index + 1 }}
|
||||
<span slot="uniqueId" slot-scope="text, record">
|
||||
<a href="#" @click="handlerOpenDrawer(record)"> {{ text }}</a>
|
||||
</span>
|
||||
<span slot="taskType" slot-scope="text">
|
||||
<a-tag :color="taskType[text].color">
|
||||
@ -77,6 +77,19 @@
|
||||
</template>
|
||||
</span>
|
||||
</s-table>
|
||||
|
||||
<Drawer
|
||||
title="重试日志详情"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="true"
|
||||
:visible="openDrawer"
|
||||
@closeDrawer="onClose"
|
||||
@handlerAmplify="handleInfo"
|
||||
>
|
||||
<retry-log-info ref="retryLogInfoRef" :showHeader="false" :column="1"/>
|
||||
</Drawer>
|
||||
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@ -86,12 +99,15 @@ import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||
import AInput from 'ant-design-vue/es/input/Input'
|
||||
import { getAllGroupNameList, getRetryTaskLogPage, getSceneList } from '@/api/manage'
|
||||
|
||||
import { STable } from '@/components'
|
||||
import { Drawer, STable } from '@/components'
|
||||
import moment from 'moment'
|
||||
import RetryLogInfo from '@/views/task/RetryLogInfo'
|
||||
|
||||
export default {
|
||||
name: 'RetryTaskLog',
|
||||
components: {
|
||||
RetryLogInfo,
|
||||
Drawer,
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable
|
||||
@ -140,15 +156,11 @@ export default {
|
||||
},
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: '#',
|
||||
scopedSlots: { customRender: 'serial' },
|
||||
width: '5%'
|
||||
},
|
||||
{
|
||||
title: 'UniqueId',
|
||||
dataIndex: 'uniqueId',
|
||||
width: '10%'
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'uniqueId' }
|
||||
},
|
||||
{
|
||||
title: '组名称',
|
||||
@ -227,7 +239,9 @@ export default {
|
||||
sceneList: [],
|
||||
groupName: '',
|
||||
uniqueId: '',
|
||||
sceneName: ''
|
||||
sceneName: '',
|
||||
currentShowRecord: null,
|
||||
openDrawer: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -257,8 +271,19 @@ export default {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
handleInfo (record) {
|
||||
console.log(record)
|
||||
record = record || this.currentShowRecord
|
||||
this.$router.push({ path: '/retry/log/info', query: { id: record.id } })
|
||||
},
|
||||
handlerOpenDrawer (record) {
|
||||
this.currentShowRecord = record
|
||||
this.openDrawer = true
|
||||
setTimeout(() => {
|
||||
this.$refs.retryLogInfoRef.getRetryTaskLogById(record.id)
|
||||
}, 1000)
|
||||
},
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.currentShowRecord = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<page-header-wrapper @back="() => $router.replace('/retry/list')" style="margin: -24px -1px 0" v-if="showHeader">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
<a-card :bordered="false" v-if="retryTaskInfo !==null ">
|
||||
<a-descriptions title="" bordered>
|
||||
<a-card :bordered="false" v-if="retryTaskInfo !== null">
|
||||
<a-descriptions title="" :column="column" bordered>
|
||||
<a-descriptions-item label="组名称">
|
||||
{{ retryTaskInfo.groupName }}
|
||||
</a-descriptions-item>
|
||||
@ -55,7 +55,6 @@
|
||||
|
||||
<script>
|
||||
import { getRetryTaskById } from '@/api/manage'
|
||||
import moment from 'moment'
|
||||
import RetryTaskLogMessageList from './RetryTaskLogMessageList'
|
||||
|
||||
export default {
|
||||
@ -63,6 +62,16 @@ export default {
|
||||
components: {
|
||||
RetryTaskLogMessageList
|
||||
},
|
||||
props: {
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
column: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
retryTaskInfo: null,
|
||||
@ -88,6 +97,15 @@ export default {
|
||||
const id = this.$route.query.id
|
||||
const groupName = this.$route.query.groupName
|
||||
if (id && groupName) {
|
||||
this.getRetryTaskById(id, groupName)
|
||||
} else {
|
||||
if (this.showHeader) {
|
||||
this.$router.push({ path: '/404' })
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getRetryTaskById (id, groupName) {
|
||||
getRetryTaskById(id, { 'groupName': groupName }).then(res => {
|
||||
this.retryTaskInfo = res.data
|
||||
this.queryParam = {
|
||||
@ -96,13 +114,6 @@ export default {
|
||||
}
|
||||
this.$refs.retryTaskLogMessageListRef.refreshTable(this.queryParam)
|
||||
})
|
||||
} else {
|
||||
// this.$router.push({ path: '/404' })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDate (date) {
|
||||
return moment(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +87,8 @@
|
||||
:rowSelection="options.rowSelection"
|
||||
:scroll="{ x: 2000 }"
|
||||
>
|
||||
<span slot="serial" slot-scope="text, record">
|
||||
{{ record.id }}
|
||||
<span slot="uniqueId" slot-scope="text, record">
|
||||
<a href="#" @click="handlerOpenDrawer(record)"> {{ text }}</a>
|
||||
</span>
|
||||
<span slot="taskType" slot-scope="text">
|
||||
<a-tag :color="taskType[text].color">
|
||||
@ -144,6 +144,18 @@
|
||||
</span>
|
||||
</s-table>
|
||||
|
||||
<Drawer
|
||||
title="任务详情"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="true"
|
||||
:visible="openDrawer"
|
||||
@closeDrawer="onClose"
|
||||
@handlerAmplify="handleInfo"
|
||||
>
|
||||
<retry-task-info ref="retryTaskInfoRef" :showHeader="false" :column="1"/>
|
||||
</Drawer>
|
||||
|
||||
<SaveRetryTask ref="saveRetryTask" @refreshTable="refreshTable"/>
|
||||
<BatchUpdateRetryTaskInfo ref="batchUpdateRetryTaskInfo" @refreshTable="refreshTable"/>
|
||||
<BatchSaveRetryTask ref="batchSaveRetryTask" @refreshTable="refreshTable"/>
|
||||
@ -162,14 +174,17 @@ import {
|
||||
batchDelete,
|
||||
manualTriggerCallbackTask, manualTriggerRetryTask
|
||||
} from '@/api/manage'
|
||||
import { STable } from '@/components'
|
||||
import { Drawer, STable } from '@/components'
|
||||
import SaveRetryTask from './form/SaveRetryTask'
|
||||
import BatchUpdateRetryTaskInfo from './form/BatchUpdateRetryTaskInfo'
|
||||
import BatchSaveRetryTask from '@/views/task/form/BatchSaveRetryTask.vue'
|
||||
import RetryTaskInfo from '@/views/task/RetryTaskInfo'
|
||||
|
||||
export default {
|
||||
name: 'RetryTask',
|
||||
components: {
|
||||
Drawer,
|
||||
RetryTaskInfo,
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable,
|
||||
@ -217,15 +232,11 @@ export default {
|
||||
},
|
||||
// 表头
|
||||
columns: [
|
||||
{
|
||||
title: 'ID',
|
||||
scopedSlots: { customRender: 'serial' },
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title: 'UniqueId',
|
||||
dataIndex: 'uniqueId',
|
||||
width: '9%'
|
||||
fixed: 'left',
|
||||
scopedSlots: { customRender: 'uniqueId' }
|
||||
},
|
||||
{
|
||||
title: '组名称',
|
||||
@ -310,7 +321,9 @@ export default {
|
||||
},
|
||||
optionAlertShow: false,
|
||||
groupNameList: [],
|
||||
sceneList: []
|
||||
sceneList: [],
|
||||
openDrawer: false,
|
||||
currentShowRecord: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -343,6 +356,7 @@ export default {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
handleInfo (record) {
|
||||
record = record || this.currentShowRecord
|
||||
this.$router.push({ path: '/retry/info', query: { id: record.id, groupName: record.groupName } })
|
||||
},
|
||||
handleOk (record) {},
|
||||
@ -435,6 +449,17 @@ export default {
|
||||
if (key === '1') {
|
||||
this.handlerDel()
|
||||
}
|
||||
},
|
||||
handlerOpenDrawer (record) {
|
||||
this.currentShowRecord = record
|
||||
this.openDrawer = true
|
||||
setTimeout(() => {
|
||||
this.$refs.retryTaskInfoRef.getRetryTaskById(record.id, record.groupName)
|
||||
}, 200)
|
||||
},
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.currentShowRecord = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin: 20px 0; border-left: #f5222d 5px solid; font-size: medium; font-weight: bold">
|
||||
调用日志详情 (总调度次数: {{ total }})
|
||||
<span style="padding-left: 18px">调用日志详情 (总调度次数: {{ total }})</span>
|
||||
<span style="padding-left: 18px"><a-icon type="sync" @click="()=> this.$refs.table.refresh(true)"/></span>
|
||||
</div>
|
||||
<a-card>
|
||||
<s-table
|
||||
@ -64,7 +65,6 @@ export default {
|
||||
queryParam: {},
|
||||
// 加载数据方法 必须为 Promise 对象
|
||||
loadData: parameter => {
|
||||
console.log(this.queryParam)
|
||||
if (!this.queryParam['groupName']) {
|
||||
return
|
||||
}
|
||||
|
@ -1,85 +1,103 @@
|
||||
<template>
|
||||
<a-card class="card" title="组配置" :bordered="false">
|
||||
<a-form @submit="handleSubmit" :form="form" :body-style="{padding: '24px 32px'}" v-bind="formItemLayout" >
|
||||
<a-form-item>
|
||||
<a-input
|
||||
hidden
|
||||
v-decorator="['id']" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="用户名">
|
||||
<a-input
|
||||
placeholder="请输入用户名"
|
||||
v-decorator="[
|
||||
'username',
|
||||
{rules: [{ required: true, message: '请输入用户名', whitespace: true}]}
|
||||
]" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码">
|
||||
<a-input
|
||||
placeholder="请输入密码"
|
||||
v-decorator="[
|
||||
'password',
|
||||
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}]}
|
||||
]"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="角色">
|
||||
<a-select
|
||||
placeholder="请选择角色"
|
||||
v-decorator="[
|
||||
'role',
|
||||
{rules: [{ required: true, message: '请选择角色'}]}
|
||||
]"
|
||||
@change="value => handleChange(value)">
|
||||
<a-select-option value="1">普通用户</a-select-option>
|
||||
<a-select-option value="2">管理员</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="命名空间"
|
||||
v-if="role !== '2'">
|
||||
<a-select
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:token-separators="[',']"
|
||||
@change="value => handleNamespacesIdChange(value)"
|
||||
v-decorator="[
|
||||
'namespaceIds',
|
||||
{rules: [{ required: true, message: '请分配命名空间'}]}
|
||||
]">
|
||||
<a-select-option v-for="(item, index) in namespaceList" :key="index" :value="item.uniqueId">
|
||||
{{ item.name }} ({{ item.uniqueId }})
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="组"
|
||||
v-if="role !== '2'">
|
||||
<a-select
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:token-separators="[',']"
|
||||
v-decorator="[
|
||||
'permissions',
|
||||
{rules: [{ required: true, message: '请分配组'}]}
|
||||
]">
|
||||
<a-select-option v-for="(item, index) in groupNameList" :key="index" :value="item.groupName + '@@'+ item.namespaceId">
|
||||
{{ item.groupName }} ({{ item.namespaceId }})
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:wrapper-col="{
|
||||
xs: { span: 24, offset: 0 },
|
||||
sm: { span: 16, offset: 8 },
|
||||
lg: { span: 7 }
|
||||
}">
|
||||
<a-button htmlType="submit" type="primary">提交</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
<div>
|
||||
<page-header-wrapper :content="formType === 'create' ? '新增用户':'更新用户信息'" @back="() => $router.go(-1)" style="margin: -24px -1px 0">
|
||||
<div></div>
|
||||
</page-header-wrapper>
|
||||
<a-card class="card" title="" :bordered="false">
|
||||
<a-form @submit="handleSubmit" :form="form" :body-style="{padding: '24px 32px'}" v-bind="formItemLayout" :rules="rules">
|
||||
<a-form-item>
|
||||
<a-input
|
||||
hidden
|
||||
v-decorator="['id']" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="用户名">
|
||||
<a-input
|
||||
placeholder="请输入用户名"
|
||||
v-decorator="[
|
||||
'username',
|
||||
{rules: [{ required: true, message: '请输入用户名', whitespace: true}]}
|
||||
]" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码">
|
||||
<a-input
|
||||
placeholder="请输入密码"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
v-decorator="[
|
||||
'password',
|
||||
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}, {validator: validatePass, validateTrigger: ['change', 'blur']}]}
|
||||
]"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="确认密码">
|
||||
<a-input
|
||||
placeholder="请输入确认密码"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
v-decorator="[
|
||||
'checkPassword',
|
||||
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}, {validator: validateCheckPass, validateTrigger: ['change', 'blur']}]}
|
||||
]"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="角色">
|
||||
<a-select
|
||||
placeholder="请选择角色"
|
||||
v-decorator="[
|
||||
'role',
|
||||
{rules: [{ required: true, message: '请选择角色'}]}
|
||||
]"
|
||||
@change="value => handleChange(value)">
|
||||
<a-select-option value="1">普通用户</a-select-option>
|
||||
<a-select-option value="2">管理员</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="命名空间"
|
||||
v-if="role !== '2'">
|
||||
<a-select
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:token-separators="[',']"
|
||||
@change="value => handleNamespacesIdChange(value)"
|
||||
v-decorator="[
|
||||
'namespaceIds',
|
||||
{rules: [{ required: true, message: '请分配命名空间'}]}
|
||||
]">
|
||||
<a-select-option v-for="(item, index) in namespaceList" :key="index" :value="item.uniqueId">
|
||||
{{ item.name }} ({{ item.uniqueId }})
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="组"
|
||||
v-if="role !== '2'">
|
||||
<a-select
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:token-separators="[',']"
|
||||
v-decorator="[
|
||||
'permissions',
|
||||
{rules: [{ required: true, message: '请分配组'}]}
|
||||
]">
|
||||
<a-select-option v-for="(item, index) in groupNameList" :key="index" :value="item.groupName + '@@'+ item.namespaceId">
|
||||
{{ item.groupName }} ({{ item.namespaceId }})
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:wrapper-col="{
|
||||
xs: { span: 24, offset: 0 },
|
||||
sm: { span: 16, offset: 8 },
|
||||
lg: { span: 7 }
|
||||
}">
|
||||
<a-button htmlType="submit" type="primary">提交</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
@ -161,19 +179,31 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
validate (rule, value, callback) {
|
||||
const regex = /^user-(.*)$/
|
||||
if (!regex.test(value)) {
|
||||
callback(new Error('需要以 user- 开头'))
|
||||
validatePass (rule, value, callback) {
|
||||
if (value === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else {
|
||||
if (this.form.getFieldValue('checkPassword') !== '') {
|
||||
this.$refs.form.validateField('checkPassword')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
},
|
||||
validateCheckPass (rule, value, callback) {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== this.form.getFieldValue('password')) {
|
||||
callback(new Error('两次密码不匹配!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
callback()
|
||||
},
|
||||
loadEditInfo (data) {
|
||||
this.formType = 'edit'
|
||||
const { form } = this
|
||||
// ajax
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, 1500)
|
||||
setTimeout(resolve, 100)
|
||||
}).then(() => {
|
||||
const formData = pick(data, ['id', 'username', 'role', 'permissions', 'namespaceIds'])
|
||||
formData.role = formData.role.toString()
|
||||
|
@ -43,7 +43,11 @@
|
||||
:rowSelection="options.rowSelection"
|
||||
>
|
||||
<span slot="serial" slot-scope="text, record">
|
||||
<a href="#" @click="handlerDetail">{{ record.id }}</a>
|
||||
{{ record.id }}
|
||||
</span>
|
||||
<span slot="username" slot-scope="text, record">
|
||||
<a href="#" @click="handlerDetail(record)" v-if="record.role === 1">{{ text }}</a>
|
||||
<span v-else>{{ text }}</span>
|
||||
</span>
|
||||
<span slot="groupNameList" slot-scope="text, record">
|
||||
{{ record.role === 2 ? '所有组' : text.toString() }}
|
||||
@ -66,33 +70,23 @@
|
||||
</template>
|
||||
</span>
|
||||
</s-table>
|
||||
<a-drawer
|
||||
title="Basic Drawer"
|
||||
|
||||
<Drawer
|
||||
title="用户权限"
|
||||
placement="right"
|
||||
:width="800"
|
||||
:visibleAmplify="false"
|
||||
:visible="openDrawer"
|
||||
@close="onClose"
|
||||
@closeDrawer="onClose"
|
||||
>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
marginRight: '5px',
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e9e9e9',
|
||||
padding: '10px 16px',
|
||||
background: '#fff',
|
||||
// textAlign: 'right',
|
||||
zIndex: 1,
|
||||
}"
|
||||
>
|
||||
<a-button style='background: none'><a-icon type="arrows-alt" style='size: 16px;'/></a-button>
|
||||
|
||||
</div></a-drawer>
|
||||
<a-descriptions :column="8" bordered>
|
||||
<a-descriptions-item :label="item.namespaceName + ' (' + item.namespaceId + ')'" :key="item.namespaceId" :span="8" v-for="item in systemPermissions">
|
||||
<a-tag v-for="item in item.groupNames" :key="item">
|
||||
{{ item }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</Drawer>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@ -102,12 +96,15 @@ import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||
import AInput from 'ant-design-vue/es/input/Input'
|
||||
import moment from 'moment'
|
||||
// 动态切换组件
|
||||
import { getUserPage, delUser } from '@/api/manage'
|
||||
import { STable } from '@/components'
|
||||
import { getUserPage, delUser, getSystemUserPermissionByUserId } from '@/api/manage'
|
||||
import { Drawer, STable } from '@/components'
|
||||
import RetryDeadLetterInfo from '@/views/task/RetryDeadLetterInfo.vue'
|
||||
|
||||
export default {
|
||||
name: 'TableListWrapper',
|
||||
components: {
|
||||
Drawer,
|
||||
RetryDeadLetterInfo,
|
||||
AInput,
|
||||
ATextarea,
|
||||
STable
|
||||
@ -125,29 +122,20 @@ export default {
|
||||
columns: [
|
||||
{
|
||||
title: '#',
|
||||
width: '5%',
|
||||
scopedSlots: { customRender: 'serial' }
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
width: '12%',
|
||||
dataIndex: 'username'
|
||||
dataIndex: 'username',
|
||||
scopedSlots: { customRender: 'username' }
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
dataIndex: 'role',
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'role' }
|
||||
},
|
||||
{
|
||||
title: '权限',
|
||||
dataIndex: 'groupNameList',
|
||||
width: '45%',
|
||||
scopedSlots: { customRender: 'groupNameList' }
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
width: '18%',
|
||||
dataIndex: 'updateDt',
|
||||
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
@ -178,7 +166,8 @@ export default {
|
||||
}
|
||||
},
|
||||
optionAlertShow: false,
|
||||
openDrawer: false
|
||||
openDrawer: false,
|
||||
systemPermissions: []
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
@ -204,11 +193,14 @@ export default {
|
||||
this.record = ''
|
||||
this.currentComponet = 'List'
|
||||
},
|
||||
handlerDetail () {
|
||||
this.openDrawer = true
|
||||
handlerDetail (record) {
|
||||
getSystemUserPermissionByUserId(record.id).then(res => {
|
||||
this.systemPermissions = res.data
|
||||
this.openDrawer = true
|
||||
})
|
||||
},
|
||||
onClose () {
|
||||
this.openDrawer = false
|
||||
this.openDrawer = !this.openDrawer
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -219,3 +211,31 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.open {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
display: block;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
padding: 0;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
line-height: 56px;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
-webkit-transition: color 0.3s;
|
||||
transition: color 0.3s;
|
||||
text-rendering: auto;
|
||||
padding-right: 70px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user