feat:2.5.0
1. 支持手动触发任务 2. 支持手动停止任务
This commit is contained in:
parent
008629b60d
commit
3907e27b8a
@ -23,6 +23,7 @@ public enum JobOperationReasonEnum {
|
|||||||
JOB_OVERLAY(5, "任务被覆盖"),
|
JOB_OVERLAY(5, "任务被覆盖"),
|
||||||
NOT_EXECUTE_TASK(6, "无可执行任务项"),
|
NOT_EXECUTE_TASK(6, "无可执行任务项"),
|
||||||
TASK_EXECUTE_ERROR(7, "任务执行期间发生非预期异常"),
|
TASK_EXECUTE_ERROR(7, "任务执行期间发生非预期异常"),
|
||||||
|
MANNER_STOP(8, "手动停止"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final int reason;
|
private final int reason;
|
||||||
|
@ -3,15 +3,10 @@ package com.aizuda.easy.retry.server.web.controller;
|
|||||||
import com.aizuda.easy.retry.server.web.annotation.LoginRequired;
|
import com.aizuda.easy.retry.server.web.annotation.LoginRequired;
|
||||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||||
import com.aizuda.easy.retry.server.web.model.request.JobBatchQueryVO;
|
import com.aizuda.easy.retry.server.web.model.request.JobBatchQueryVO;
|
||||||
import com.aizuda.easy.retry.server.web.model.request.JobQueryVO;
|
|
||||||
import com.aizuda.easy.retry.server.web.model.response.JobBatchResponseVO;
|
import com.aizuda.easy.retry.server.web.model.response.JobBatchResponseVO;
|
||||||
import com.aizuda.easy.retry.server.web.model.response.JobResponseVO;
|
|
||||||
import com.aizuda.easy.retry.server.web.service.JobBatchService;
|
import com.aizuda.easy.retry.server.web.service.JobBatchService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -39,5 +34,10 @@ public class JobBatchController {
|
|||||||
return jobBatchService.getJobBatchDetail(id);
|
return jobBatchService.getJobBatchDetail(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/stop/{taskBatchId}")
|
||||||
|
@LoginRequired
|
||||||
|
public Boolean stop(@PathVariable("taskBatchId") Long taskBatchId) {
|
||||||
|
return jobBatchService.stop(taskBatchId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -77,4 +77,10 @@ public class JobController {
|
|||||||
) {
|
) {
|
||||||
return jobService.getJobNameList(keywords, jobId);
|
return jobService.getJobNameList(keywords, jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/trigger/{jobId}")
|
||||||
|
@LoginRequired
|
||||||
|
public Boolean trigger(@PathVariable("jobId") Long jobId) {
|
||||||
|
return jobService.trigger(jobId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,7 @@ public interface JobBatchService {
|
|||||||
PageResult<List<JobBatchResponseVO>> getJobBatchPage(JobBatchQueryVO jobQueryVO);
|
PageResult<List<JobBatchResponseVO>> getJobBatchPage(JobBatchQueryVO jobQueryVO);
|
||||||
|
|
||||||
JobBatchResponseVO getJobBatchDetail(Long id);
|
JobBatchResponseVO getJobBatchDetail(Long id);
|
||||||
|
|
||||||
|
boolean stop(Long taskBatchId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,7 @@ public interface JobService {
|
|||||||
List<String> getTimeByCron(String cron);
|
List<String> getTimeByCron(String cron);
|
||||||
|
|
||||||
List<JobResponseVO> getJobNameList(String keywords, Long jobId);
|
List<JobResponseVO> getJobNameList(String keywords, Long jobId);
|
||||||
|
|
||||||
|
boolean trigger(Long jobId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
package com.aizuda.easy.retry.server.web.service.impl;
|
package com.aizuda.easy.retry.server.web.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.aizuda.easy.retry.common.core.enums.JobOperationReasonEnum;
|
||||||
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
|
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
|
||||||
|
import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
|
||||||
|
import com.aizuda.easy.retry.server.common.util.DateUtils;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.dto.JobTaskPrepareDTO;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.JobPrePareHandler;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.JobTaskConverter;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.JobTaskStopHandler;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.stop.JobTaskStopFactory;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.stop.TaskStopJobContext;
|
||||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||||
import com.aizuda.easy.retry.server.web.model.request.JobBatchQueryVO;
|
import com.aizuda.easy.retry.server.web.model.request.JobBatchQueryVO;
|
||||||
import com.aizuda.easy.retry.server.web.model.response.JobBatchResponseVO;
|
import com.aizuda.easy.retry.server.web.model.response.JobBatchResponseVO;
|
||||||
@ -16,6 +26,7 @@ import com.aizuda.easy.retry.template.datasource.persistence.po.JobTaskBatch;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -47,10 +58,10 @@ public class JobBatchServiceImpl implements JobBatchService {
|
|||||||
jobBatchQueryDO.setJobId(queryVO.getJobId());
|
jobBatchQueryDO.setJobId(queryVO.getJobId());
|
||||||
jobBatchQueryDO.setTaskBatchStatus(queryVO.getTaskBatchStatus());
|
jobBatchQueryDO.setTaskBatchStatus(queryVO.getTaskBatchStatus());
|
||||||
jobBatchQueryDO.setGroupName(queryVO.getGroupName());
|
jobBatchQueryDO.setGroupName(queryVO.getGroupName());
|
||||||
List<JobBatchResponseDO> batchResponseDOList = jobTaskBatchMapper.selectJobBatchList(pageDTO, jobBatchQueryDO);
|
List<JobBatchResponseDO> batchResponseDOList = jobTaskBatchMapper.selectJobBatchList(pageDTO, jobBatchQueryDO);
|
||||||
|
|
||||||
List<JobBatchResponseVO> batchResponseVOList = JobBatchResponseVOConverter.INSTANCE.toJobBatchResponseVOs(
|
List<JobBatchResponseVO> batchResponseVOList = JobBatchResponseVOConverter.INSTANCE.toJobBatchResponseVOs(
|
||||||
batchResponseDOList);
|
batchResponseDOList);
|
||||||
|
|
||||||
return new PageResult<>(pageDTO, batchResponseVOList);
|
return new PageResult<>(pageDTO, batchResponseVOList);
|
||||||
}
|
}
|
||||||
@ -65,4 +76,29 @@ public class JobBatchServiceImpl implements JobBatchService {
|
|||||||
Job job = jobMapper.selectById(jobTaskBatch.getJobId());
|
Job job = jobMapper.selectById(jobTaskBatch.getJobId());
|
||||||
return JobBatchResponseVOConverter.INSTANCE.toJobBatchResponseVO(jobTaskBatch, job);
|
return JobBatchResponseVOConverter.INSTANCE.toJobBatchResponseVO(jobTaskBatch, job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stop(Long taskBatchId) {
|
||||||
|
JobTaskBatch jobTaskBatch = jobTaskBatchMapper.selectById(taskBatchId);
|
||||||
|
Assert.notNull(jobTaskBatch, () -> new EasyRetryServerException("job batch can not be null."));
|
||||||
|
|
||||||
|
Job job = jobMapper.selectById(jobTaskBatch.getJobId());
|
||||||
|
Assert.notNull(job, () -> new EasyRetryServerException("job can not be null."));
|
||||||
|
|
||||||
|
JobTaskStopHandler jobTaskStop = JobTaskStopFactory.getJobTaskStop(job.getTaskType());
|
||||||
|
|
||||||
|
TaskStopJobContext taskStopJobContext = new TaskStopJobContext();
|
||||||
|
taskStopJobContext.setJobId(job.getId());
|
||||||
|
taskStopJobContext.setTaskType(job.getTaskType());
|
||||||
|
taskStopJobContext.setGroupName(job.getGroupName());
|
||||||
|
taskStopJobContext.setJobOperationReason(JobOperationReasonEnum.MANNER_STOP.getReason());
|
||||||
|
taskStopJobContext.setTaskBatchId(jobTaskBatch.getId());
|
||||||
|
taskStopJobContext.setForceStop(Boolean.TRUE);
|
||||||
|
taskStopJobContext.setNeedUpdateTaskStatus(Boolean.TRUE);
|
||||||
|
|
||||||
|
jobTaskStop.stop(taskStopJobContext);
|
||||||
|
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
|
|||||||
import com.aizuda.easy.retry.server.common.strategy.WaitStrategies;
|
import com.aizuda.easy.retry.server.common.strategy.WaitStrategies;
|
||||||
import com.aizuda.easy.retry.server.common.util.CronUtils;
|
import com.aizuda.easy.retry.server.common.util.CronUtils;
|
||||||
import com.aizuda.easy.retry.server.common.util.DateUtils;
|
import com.aizuda.easy.retry.server.common.util.DateUtils;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.dto.JobTaskPrepareDTO;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.JobPrePareHandler;
|
||||||
|
import com.aizuda.easy.retry.server.job.task.support.JobTaskConverter;
|
||||||
import com.aizuda.easy.retry.server.job.task.support.cache.ResidentTaskCache;
|
import com.aizuda.easy.retry.server.job.task.support.cache.ResidentTaskCache;
|
||||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||||
import com.aizuda.easy.retry.server.web.model.request.JobQueryVO;
|
import com.aizuda.easy.retry.server.web.model.request.JobQueryVO;
|
||||||
@ -25,6 +28,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -42,11 +46,13 @@ import java.util.Optional;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class JobServiceImpl implements JobService {
|
public class JobServiceImpl implements JobService {
|
||||||
|
|
||||||
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SystemProperties systemProperties;
|
private SystemProperties systemProperties;
|
||||||
@Autowired
|
@Autowired
|
||||||
private JobMapper jobMapper;
|
private JobMapper jobMapper;
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("terminalJobPrepareHandler")
|
||||||
|
private JobPrePareHandler jobPrePareHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<List<JobResponseVO>> getJobPage(JobQueryVO queryVO) {
|
public PageResult<List<JobResponseVO>> getJobPage(JobQueryVO queryVO) {
|
||||||
@ -190,4 +196,19 @@ public class JobServiceImpl implements JobService {
|
|||||||
job.setDeleted(StatusEnum.YES.getStatus());
|
job.setDeleted(StatusEnum.YES.getStatus());
|
||||||
return 1 == jobMapper.updateById(job);
|
return 1 == jobMapper.updateById(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean trigger(Long jobId) {
|
||||||
|
|
||||||
|
Job job = jobMapper.selectById(jobId);
|
||||||
|
Assert.notNull(job, () -> new EasyRetryServerException("job can not be null."));
|
||||||
|
|
||||||
|
JobTaskPrepareDTO jobTaskPrepare = JobTaskConverter.INSTANCE.toJobTaskPrepare(job);
|
||||||
|
// 设置now表示立即执行
|
||||||
|
jobTaskPrepare.setNextTriggerAt(DateUtils.toNowMilli());
|
||||||
|
// 创建批次
|
||||||
|
jobPrePareHandler.handler(jobTaskPrepare);
|
||||||
|
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,12 @@ const jobApi = {
|
|||||||
delJob: '/job/',
|
delJob: '/job/',
|
||||||
timeByCron: '/job/cron',
|
timeByCron: '/job/cron',
|
||||||
jobNameList: '/job/job-name/list',
|
jobNameList: '/job/job-name/list',
|
||||||
|
triggerJob: '/job/trigger/',
|
||||||
|
|
||||||
// 任务批次
|
// 任务批次
|
||||||
jobBatchList: '/job/batch/list',
|
jobBatchList: '/job/batch/list',
|
||||||
jobBatchDetail: '/job/batch/',
|
jobBatchDetail: '/job/batch/',
|
||||||
|
stop: '/job/batch/stop/',
|
||||||
|
|
||||||
// 任务
|
// 任务
|
||||||
jobTaskList: '/job/task/list',
|
jobTaskList: '/job/task/list',
|
||||||
@ -23,6 +25,20 @@ const jobApi = {
|
|||||||
|
|
||||||
export default jobApi
|
export default jobApi
|
||||||
|
|
||||||
|
export function triggerJob (id) {
|
||||||
|
return request({
|
||||||
|
url: jobApi.triggerJob + id,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stop (id) {
|
||||||
|
return request({
|
||||||
|
url: jobApi.stop + id,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function jobNameList (parameter) {
|
export function jobNameList (parameter) {
|
||||||
return request({
|
return request({
|
||||||
url: jobApi.jobNameList,
|
url: jobApi.jobNameList,
|
||||||
|
@ -128,6 +128,10 @@ const enums = {
|
|||||||
'7': {
|
'7': {
|
||||||
'name': '任务执行期间发生非预期异常',
|
'name': '任务执行期间发生非预期异常',
|
||||||
'color': '#bdc223'
|
'color': '#bdc223'
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
'name': '手动停止',
|
||||||
|
'color': '#23c28a'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
taskStatus: {
|
taskStatus: {
|
||||||
|
@ -111,6 +111,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="handleInfo(record)">详情</a>
|
<a @click="handleInfo(record)">详情</a>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
|
<a-popconfirm
|
||||||
|
title="是否停止?"
|
||||||
|
ok-text="停止"
|
||||||
|
cancel-text="取消"
|
||||||
|
@confirm="handleStop(record)"
|
||||||
|
>
|
||||||
|
<a href="javascript:;" v-if="record.taskBatchStatus === 1 || record.taskBatchStatus === 2">停止</a>
|
||||||
|
</a-popconfirm>
|
||||||
<!-- <a-popconfirm-->
|
<!-- <a-popconfirm-->
|
||||||
<!-- title="是否暂停?"-->
|
<!-- title="是否暂停?"-->
|
||||||
<!-- ok-text="恢复"-->
|
<!-- ok-text="恢复"-->
|
||||||
@ -158,7 +166,7 @@
|
|||||||
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||||
import AInput from 'ant-design-vue/es/input/Input'
|
import AInput from 'ant-design-vue/es/input/Input'
|
||||||
import { STable } from '@/components'
|
import { STable } from '@/components'
|
||||||
import { jobBatchList, jobNameList } from '@/api/jobApi'
|
import { jobBatchList, jobNameList, stop } from '@/api/jobApi'
|
||||||
import { getAllGroupNameList } from '@/api/manage'
|
import { getAllGroupNameList } from '@/api/manage'
|
||||||
const enums = require('@/utils/jobEnum')
|
const enums = require('@/utils/jobEnum')
|
||||||
|
|
||||||
@ -301,16 +309,16 @@ export default {
|
|||||||
this.$router.push({ path: '/job/batch/info', query: { id: record.id, groupName: record.groupName } })
|
this.$router.push({ path: '/job/batch/info', query: { id: record.id, groupName: record.groupName } })
|
||||||
},
|
},
|
||||||
handleOk (record) {},
|
handleOk (record) {},
|
||||||
handleSuspend (record) {
|
handleStop (record) {
|
||||||
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 3 }).then((res) => {
|
stop(record.id).then((res) => {
|
||||||
// const { status } = res
|
const { status } = res
|
||||||
// if (status === 0) {
|
if (status === 0) {
|
||||||
// this.$message.error('暂停失败')
|
this.$message.error('停止失败')
|
||||||
// } else {
|
} else {
|
||||||
// this.$refs.table.refresh(true)
|
this.$refs.table.refresh(true)
|
||||||
// this.$message.success('暂停成功')
|
this.$message.success('停止成功')
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
},
|
},
|
||||||
handleRecovery (record) {
|
handleRecovery (record) {
|
||||||
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 0 }).then((res) => {
|
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 0 }).then((res) => {
|
||||||
|
@ -111,6 +111,15 @@
|
|||||||
</span>
|
</span>
|
||||||
<span slot="action" slot-scope="text, record">
|
<span slot="action" slot-scope="text, record">
|
||||||
<template>
|
<template>
|
||||||
|
<a-popconfirm
|
||||||
|
title="是否运行?"
|
||||||
|
ok-text="运行"
|
||||||
|
cancel-text="取消"
|
||||||
|
@confirm="handleTrigger(record)"
|
||||||
|
>
|
||||||
|
<a href="javascript:;">运行</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
<a-divider type="vertical" />
|
||||||
<a @click="handleInfo(record)">详情</a>
|
<a @click="handleInfo(record)">详情</a>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<a @click="goJobBatchList(record)">批次</a>
|
<a @click="goJobBatchList(record)">批次</a>
|
||||||
@ -155,7 +164,7 @@
|
|||||||
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
import ATextarea from 'ant-design-vue/es/input/TextArea'
|
||||||
import AInput from 'ant-design-vue/es/input/Input'
|
import AInput from 'ant-design-vue/es/input/Input'
|
||||||
import { STable } from '@/components'
|
import { STable } from '@/components'
|
||||||
import { delJob, getJobList, updateJobStatus } from '@/api/jobApi'
|
import { delJob, getJobList, triggerJob, updateJobStatus } from '@/api/jobApi'
|
||||||
import { getAllGroupNameList } from '@/api/manage'
|
import { getAllGroupNameList } from '@/api/manage'
|
||||||
import enums from '@/utils/jobEnum'
|
import enums from '@/utils/jobEnum'
|
||||||
|
|
||||||
@ -311,6 +320,17 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
handleTrigger (record) {
|
||||||
|
triggerJob(record.id).then((res) => {
|
||||||
|
const { status } = res
|
||||||
|
if (status === 0) {
|
||||||
|
this.$message.error('执行失败')
|
||||||
|
} else {
|
||||||
|
// this.$refs.table.refresh(true)
|
||||||
|
this.$message.success('执行成功')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
handleOpen (record) {
|
handleOpen (record) {
|
||||||
updateJobStatus({ id: record.id, jobStatus: 1 }).then((res) => {
|
updateJobStatus({ id: record.id, jobStatus: 1 }).then((res) => {
|
||||||
const { status } = res
|
const { status } = res
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
title="是否暂停?"
|
title="是否暂停?"
|
||||||
ok-text="恢复"
|
ok-text="暂停"
|
||||||
cancel-text="取消"
|
cancel-text="取消"
|
||||||
@confirm="handleSuspend(record)"
|
@confirm="handleSuspend(record)"
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user