Commit a82923c7 authored by wenmo's avatar wenmo

任务监控的savepoint等操作

parent 6fd1a0fe
......@@ -153,7 +153,7 @@ public class StudioController {
*/
@GetMapping("/savepoint")
public Result savepoint(@RequestParam Integer clusterId,@RequestParam String jobId,
@RequestParam String savePointType,@RequestParam String name) {
return Result.succeed(studioService.savepoint(clusterId,jobId,savePointType,name),"savepoint 成功");
@RequestParam String savePointType,@RequestParam String name,@RequestParam Integer taskId) {
return Result.succeed(studioService.savepoint(taskId,clusterId,jobId,savePointType,name),"savepoint 成功");
}
}
......@@ -12,7 +12,6 @@ public class JobInfoDetail {
private JobInstance instance;
private Cluster cluster;
private ClusterConfiguration clusterConfiguration;
private Task task;
private History history;
private JobHistory jobHistory;
......@@ -52,14 +51,6 @@ public class JobInfoDetail {
this.clusterConfiguration = clusterConfiguration;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public History getHistory() {
return history;
}
......
......@@ -13,19 +13,17 @@ public class JobInstanceStatus {
private Integer finished = 0;
private Integer failed = 0;
private Integer canceled = 0;
private Integer restarting = 0;
private Integer created = 0;
private Integer failing = 0;
private Integer cancelling = 0;
private Integer suspended = 0;
private Integer reconciling = 0;
private Integer unknown = 0;
public JobInstanceStatus() {
}
public JobInstanceStatus(Integer all, Integer initializing, Integer running, Integer finished, Integer failed, Integer canceled) {
this.all = all;
this.initializing = initializing;
this.running = running;
this.finished = finished;
this.failed = failed;
this.canceled = canceled;
}
public Integer getAll() {
return all;
}
......@@ -73,4 +71,60 @@ public class JobInstanceStatus {
public void setCanceled(Integer canceled) {
this.canceled = canceled;
}
public Integer getRestarting() {
return restarting;
}
public void setRestarting(Integer restarting) {
this.restarting = restarting;
}
public Integer getCreated() {
return created;
}
public void setCreated(Integer created) {
this.created = created;
}
public Integer getFailing() {
return failing;
}
public void setFailing(Integer failing) {
this.failing = failing;
}
public Integer getCancelling() {
return cancelling;
}
public void setCancelling(Integer cancelling) {
this.cancelling = cancelling;
}
public Integer getSuspended() {
return suspended;
}
public void setSuspended(Integer suspended) {
this.suspended = suspended;
}
public Integer getReconciling() {
return reconciling;
}
public void setReconciling(Integer reconciling) {
this.reconciling = reconciling;
}
public Integer getUnknown() {
return unknown;
}
public void setUnknown(Integer unknown) {
this.unknown = unknown;
}
}
......@@ -54,5 +54,5 @@ public interface StudioService {
boolean cancel(Integer clusterId,String jobId);
boolean savepoint(Integer clusterId,String jobId,String savePointType,String name);
boolean savepoint(Integer taskId,Integer clusterId,String jobId,String savePointType,String name);
}
......@@ -67,6 +67,27 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
break;
case CANCELED:
jobInstanceStatus.setCanceled(counts);
break;
case RESTARTING:
jobInstanceStatus.setRestarting(counts);
break;
case CREATED:
jobInstanceStatus.setCreated(counts);
break;
case FAILING:
jobInstanceStatus.setFailed(counts);
break;
case CANCELLING:
jobInstanceStatus.setCancelling(counts);
break;
case SUSPENDED:
jobInstanceStatus.setSuspended(counts);
break;
case RECONCILING:
jobInstanceStatus.setReconciling(counts);
break;
case UNKNOWN:
jobInstanceStatus.setUnknown(counts);
}
}
jobInstanceStatus.setAll(total);
......@@ -83,7 +104,6 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
Asserts.checkNull(jobInstance, "该任务实例不存在");
JobInfoDetail jobInfoDetail = new JobInfoDetail(jobInstance.getId());
jobInfoDetail.setInstance(jobInstance);
jobInfoDetail.setTask(taskService.getTaskInfoById(jobInstance.getTaskId()));
jobInfoDetail.setCluster(clusterService.getById(jobInstance.getClusterId()));
jobInfoDetail.setJobHistory(jobHistoryService.getJobHistory(jobInstance.getId()));
History history = historyService.getById(jobInstance.getHistoryId());
......@@ -105,6 +125,11 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
Cluster cluster = clusterService.getById(jobInstance.getClusterId());
JobHistory jobHistoryJson = jobHistoryService.refreshJobHistory(id, cluster.getJobManagerHost(), jobInstance.getJid());
JobHistory jobHistory = jobHistoryService.getJobHistoryInfo(jobHistoryJson);
if(jobHistory.getJob().has(FlinkRestResultConstant.ERRORS)){
jobInstance.setStatus(JobStatus.UNKNOWN.getValue());
updateById(jobInstance);
return jobInstance;
}
jobInstance.setDuration(jobHistory.getJob().get(FlinkRestResultConstant.JOB_DURATION).asLong()/1000);
jobInstance.setStatus(jobHistory.getJob().get(FlinkRestResultConstant.JOB_STATE).asText());
updateById(jobInstance);
......
......@@ -295,9 +295,10 @@ public class StudioServiceImpl implements StudioService {
}
@Override
public boolean savepoint(Integer clusterId, String jobId, String savePointType, String name) {
public boolean savepoint(Integer taskId, Integer clusterId, String jobId, String savePointType, String name) {
Cluster cluster = clusterService.getById(clusterId);
Asserts.checkNotNull(cluster, "该集群不存在");
boolean useGateway = false;
JobConfig jobConfig = new JobConfig();
jobConfig.setAddress(cluster.getJobManagerHost());
jobConfig.setType(cluster.getType());
......@@ -306,18 +307,21 @@ public class StudioServiceImpl implements StudioService {
jobConfig.buildGatewayConfig(gatewayConfig);
jobConfig.getGatewayConfig().getClusterConfig().setAppId(cluster.getName());
jobConfig.setTaskId(cluster.getTaskId());
useGateway = true;
}else {
jobConfig.setTaskId(taskId);
}
JobManager jobManager = JobManager.build(jobConfig);
jobManager.setUseGateway(true);
jobManager.setUseGateway(useGateway);
SavePointResult savePointResult = jobManager.savepoint(jobId, savePointType, null);
if (Asserts.isNotNull(savePointResult)) {
for (JobInfo item : savePointResult.getJobInfos()) {
if (Asserts.isEqualsIgnoreCase(jobId, item.getJobId())) {
if (Asserts.isEqualsIgnoreCase(jobId, item.getJobId())&&Asserts.isNotNull(jobConfig.getTaskId())) {
Savepoints savepoints = new Savepoints();
savepoints.setName(name);
savepoints.setType(savePointType);
savepoints.setPath(item.getSavePoint());
savepoints.setTaskId(cluster.getTaskId());
savepoints.setTaskId(jobConfig.getTaskId());
savepointsService.save(savepoints);
}
}
......
......@@ -3,6 +3,7 @@ package com.dlink.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDateTime;
......@@ -19,22 +20,32 @@ public class LogUtil {
public static String getError(Exception e){
// e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String error = sw.toString();
logger.error(sw.toString());
return error;
String error = null;
try(StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw)){
e.printStackTrace(pw);
error = sw.toString();
logger.error(error);
} catch (IOException ioe) {
ioe.printStackTrace();
}finally {
return error;
}
}
public static String getError(String msg,Exception e){
// e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
LocalDateTime now = LocalDateTime.now();
String error = now.toString() + ": " + msg + " \nError message:\n " + sw.toString();
logger.error(error);
return error;
String error = null;
try(StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw)){
e.printStackTrace(pw);
LocalDateTime now = LocalDateTime.now();
error = now.toString() + ": " + msg + " \nError message:\n " + sw.toString();
logger.error(error);
} catch (IOException ioe) {
ioe.printStackTrace();
}finally {
return error;
}
}
}
......@@ -8,6 +8,7 @@ package com.dlink.constant;
**/
public final class FlinkRestResultConstant {
public static final String ERRORS = "errors";
public static final String JOB_DURATION = "duration";
public static final String JOB_STATE = "state";
......
......@@ -12,22 +12,51 @@ export type JobStatusFormProps = {
status: string|undefined;
};
export const JOB_STATUS = {
FINISHED:'FINISHED',
RUNNING:'RUNNING',
FAILED:'FAILED',
CANCELED:'CANCELED',
INITIALIZING:'INITIALIZING',
RESTARTING:'RESTARTING',
CREATED:'CREATED',
FAILING:'FAILING',
SUSPENDED:'SUSPENDED',
CANCELLING:'CANCELLING',
UNKNOWN:'UNKNOWN',
};
export function isStatusDone(type: string){
if(!type){
return true;
}
switch (type) {
case JOB_STATUS.FAILED:
case JOB_STATUS.CANCELED:
case JOB_STATUS.FINISHED:
case JOB_STATUS.UNKNOWN:
return true;
default:
return false;
}
};
const JobStatus = (props: JobStatusFormProps) => {
const {status} = props;
return (<>
{ (status === 'FINISHED') ?
(<Tag icon={<CheckCircleOutlined/>} color="success">
(<Tag icon={<CheckCircleOutlined/>} color="blue">
FINISHED
</Tag>) : (status === 'RUNNING') ?
(<Tag icon={<SyncOutlined spin/>} color="processing">
(<Tag icon={<SyncOutlined spin/>} color="green">
RUNNING
</Tag>) : (status === 'FAILED') ?
(<Tag icon={<CloseCircleOutlined/>} color="error">
FAILED
</Tag>) : (status === 'CANCELED') ?
(<Tag icon={<MinusCircleOutlined/>} color="default">
(<Tag icon={<MinusCircleOutlined/>} color="orange">
CANCELED
</Tag>) : (status === 'INITIALIZING') ?
(<Tag icon={<ClockCircleOutlined/>} color="default">
......
......@@ -187,8 +187,8 @@ export function cancelJob(clusterId:number,jobId:string) {
return getData('api/studio/cancel',{clusterId:clusterId,jobId:jobId});
}
/*--- 停止 SavePoint Jobs ---*/
export function savepointJob(clusterId:number,jobId:string,savePointType:string,name:string) {
return getData('api/studio/savepoint',{clusterId,jobId,savePointType,name});
export function savepointJob(clusterId:number,jobId:string,savePointType:string,name:string,taskId:number) {
return getData('api/studio/savepoint',{clusterId,jobId,savePointType,name,taskId});
}
/*--- 根据版本号获取所有自动补全的文档 ---*/
export function getFillAllByVersion(version:string,dispatch: any) {
......
......@@ -3,7 +3,7 @@ import {
RocketOutlined
} from '@ant-design/icons';
const {Link} = Typography;
const {Text, Link} = Typography;
const Config = (props: any) => {
......@@ -28,10 +28,22 @@ const Config = (props: any) => {
</Descriptions.Item>
<Descriptions.Item label="片段机制">{job?.history?.config.useSqlFragment?'启用':'禁用'}</Descriptions.Item>
<Descriptions.Item label="语句集">{job?.history?.config.useStatementSet?'启用':'禁用'}</Descriptions.Item>
<Descriptions.Item label="任务类型">{job?.history?.config.isJarTask?'Jar':'FlinkSQL'}</Descriptions.Item>
<Descriptions.Item label="批模式">{job?.history?.config.useBatchModel?'启用':'禁用'}</Descriptions.Item>
<Descriptions.Item label="CheckPoint">{job?.history?.config.checkpoint}</Descriptions.Item>
<Descriptions.Item label="SavePoint机制">
{job?.history?.config.savePointStrategy?'启用':'禁用'}
{job?.history?.config.savePointStrategy=='NONE'?'禁用':
job?.history?.config.savePointStrategy=='LATEST'?'最近一次':
job?.history?.config.savePointStrategy=='EARLIEST'?'最早一次':
job?.history?.config.savePointStrategy=='CUSTOM'?'指定一次':'禁用'}
</Descriptions.Item>
<Descriptions.Item label="SavePoint" span={2}>{job?.history?.config.savePointPath}</Descriptions.Item>
<Descriptions.Item label="Flink Configuration" span={3}><Text code>{JSON.stringify(job?.history?.config.config)}</Text></Descriptions.Item>
{job?.jar?<>
<Descriptions.Item label="Jar 路径">{job?.jar?.path}</Descriptions.Item>
<Descriptions.Item label="Jar 主类">{job?.jar?.mainClass}</Descriptions.Item>
<Descriptions.Item label="Jar 入参">{job?.jar?.paras}</Descriptions.Item>
</>:undefined}
</Descriptions>
</>)
};
......
......@@ -4,7 +4,7 @@ import {
EllipsisOutlined, RedoOutlined,
FireOutlined, ClusterOutlined, RocketOutlined
} from '@ant-design/icons';
import {Button, Dropdown, Menu, Tag, Space, Typography} from 'antd';
import {Button, Dropdown, Menu, Tag, Space, Typography, message, Modal} from 'antd';
import {PageContainer} from '@ant-design/pro-layout';
import ProCard from '@ant-design/pro-card';
import {JobInfoDetail} from "@/pages/DevOps/data";
......@@ -12,7 +12,8 @@ import {getJobInfoDetail, refreshJobInfoDetail} from "@/pages/DevOps/service";
import moment from "moment";
import BaseInfo from "@/pages/DevOps/JobInfo/BaseInfo";
import Config from "@/pages/DevOps/JobInfo/Config";
import JobStatus from "@/components/Common/JobStatus";
import JobStatus, {isStatusDone} from "@/components/Common/JobStatus";
import {cancelJob, savepointJob} from "@/components/Studio/StudioEvent/DDL";
const {Link} = Typography;
......@@ -53,36 +54,88 @@ const JobInfo = (props: any) => {
history.goBack();
};
const handleSavepoint = (key: string) => {
if(key=='canceljob'){
Modal.confirm({
title: '停止任务',
content: `确定只停止该作业,不进行 SavePoint 操作吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
if (!job?.cluster?.id) return;
const res = cancelJob(job?.cluster?.id, job?.instance?.jid);
res.then((result) => {
if (result.datas == true) {
message.success(key+"成功");
handleGetJobInfoDetail();
} else {
message.error(key+"失败");
}
});
}
});
return;
}
Modal.confirm({
title: key+'任务',
content: `确定${key}该作业吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
if (!job?.cluster?.id) return;
const res = savepointJob(job?.cluster?.id, job?.instance?.jid,key,key,job?.instance?.taskId);
res.then((result) => {
if (result.datas == true) {
message.success(key+"成功");
handleGetJobInfoDetail();
} else {
message.error(key+"失败");
}
});
}
});
};
const getButtons = () => {
let buttons = [
<Button key="back" type="dashed" onClick={handleBack}>返回</Button>,
];
if(!isStatusDone(job?.instance?.status as string)){
buttons.push(<Button key="refresh" icon={<RedoOutlined/>} onClick={handleRefreshJobInfoDetail}/>);
buttons.push(<Button key="flinkwebui">
<Link href={`http://${job?.history?.jobManagerAddress}/#/job/${job?.instance?.jid}/overview`} target="_blank">
FlinkWebUI
</Link></Button>);
}
buttons.push(<Button key="autorestart" type="primary">智能重启</Button>);
if(!isStatusDone(job?.instance?.status as string)){
buttons.push(<Button key="autostop" type="primary" danger onClick={()=>{handleSavepoint('cancel')}}>智能停止</Button>);
buttons.push(<Dropdown
key="dropdown"
trigger={['click']}
overlay={
<Menu onClick={({key}) => handleSavepoint(key)}>
<Menu.Item key="trigger">SavePoint触发</Menu.Item>
<Menu.Item key="stop">SavePoint暂停</Menu.Item>
<Menu.Item key="cancel">SavePoint停止</Menu.Item>
<Menu.Item key="canceljob">普通停止</Menu.Item>
</Menu>
}
>
<Button key="4" style={{padding: '0 8px'}}>
<EllipsisOutlined/>
</Button>
</Dropdown>);
}
return buttons;
}
return (
<PageContainer
header={{
title: `${job?.instance?.name}`,
ghost: true,
extra: [
<Button key="back" type="dashed" onClick={handleBack}>返回</Button>,
<Button key="refresh" icon={<RedoOutlined/>} onClick={handleRefreshJobInfoDetail}/>,
<Button key="flinkwebui">
<Link href={`http://${job?.history?.jobManagerAddress}/#/job/${job?.instance?.jid}/overview`} target="_blank">
FlinkWebUI
</Link></Button>,
<Button key="autorestart" type="primary">智能重启</Button>,
<Button key="autostop" type="primary" danger>智能停止</Button>,
<Dropdown
key="dropdown"
trigger={['click']}
overlay={
<Menu>
<Menu.Item key="1">普通停止</Menu.Item>
<Menu.Item key="2">SavePoint停止</Menu.Item>
<Menu.Item key="3">SavePoint暂停</Menu.Item>
</Menu>
}
>
<Button key="4" style={{padding: '0 8px'}}>
<EllipsisOutlined/>
</Button>
</Dropdown>,
],
extra: getButtons(),
}}
content={<>
<Space size={0}>
......
......@@ -11,6 +11,7 @@ import ProTable from "@ant-design/pro-table";
import {JobInstanceTableListItem} from "@/pages/DevOps/data";
import moment from 'moment';
import {RUN_MODE} from "@/components/Studio/conf";
import JobStatus from "@/components/Common/JobStatus";
const url = '/api/jobInstance';
const JobInstanceTable = (props: any) => {
......@@ -74,34 +75,7 @@ const JobInstanceTable = (props: any) => {
hideInSearch: true,
render: (_, row) => {
return (
<>
{(row.status == 'FINISHED') ?
(<Tag icon={<CheckCircleOutlined />} color="success">
FINISHED
</Tag>) :
(row.status == 'RUNNING') ?
(<Tag icon={<SyncOutlined spin />} color="processing">
RUNNING
</Tag>) :
(row.status == 'FAILED') ?
(<Tag icon={<CloseCircleOutlined />} color="error">
FAILED
</Tag>) :
(row.status == 'CANCELED') ?
(<Tag icon={<MinusCircleOutlined />} color="default">
CANCELED
</Tag>) :
(row.status == 'INITIALIZING') ?
(<Tag icon={<ClockCircleOutlined />} color="default">
INITIALIZING
</Tag>) :(row.status == 'RESTARTING') ?
(<Tag icon={<ClockCircleOutlined />} color="default">
RESTARTING
</Tag>) :
(<Tag color="default">
UNKNOWEN
</Tag>)
}</>)
<JobStatus status={row.status}/>)
;
}
}, {
......
import {ClusterTableListItem} from "@/pages/Cluster/data";
import {ClusterConfigurationTableListItem} from "@/pages/ClusterConfiguration/data";
import {HistoryItem} from "@/components/Studio/StudioConsole/StudioHistory/data";
import {JarTableListItem} from "@/pages/Jar/data";
export type JobInstanceTableListItem = {
id: number,
......@@ -28,6 +29,13 @@ export type StatusCount = {
finished: number,
failed: number,
canceled: number,
restarting: number,
created: number,
failing: number,
cancelling: number,
suspended: number,
reconciling: number,
unknown: number,
}
export type JobInfoDetail = {
......@@ -35,8 +43,8 @@ export type JobInfoDetail = {
instance: JobInstanceTableListItem,
cluster: ClusterTableListItem,
clusterConfiguration: ClusterConfigurationTableListItem,
task: TaskTableListItem,
history: HistoryItem
history: HistoryItem,
jar: JarTableListItem
}
export type VerticesTableListItem = {
......@@ -47,5 +55,5 @@ export type VerticesTableListItem = {
startTime: string,
duration: number,
endTime: string,
tasks:any,
tasks: any,
}
......@@ -5,6 +5,7 @@ import JobInstanceTable from "./JobInstanceTable";
import {getStatusCount} from "@/pages/DevOps/service";
import {useEffect, useState} from "react";
import {StatusCount} from "@/pages/DevOps/data";
import {JOB_STATUS} from "@/components/Common/JobStatus";
const { Statistic } = StatisticCard;
......@@ -13,11 +14,17 @@ const DevOps = (props:any) => {
// const {current} = props;
const statusCountDefault = [
{ key: '', title: '全部', value: 0, total: true },
{ key: 'INITIALIZING', status: 'default', title: '初始化', value: 0 },
{ key: 'RUNNING', status: 'success', title: '运行中', value: 0 },
{ key: 'FINISHED', status: 'processing', title: '已完成', value: 0 },
{ key: 'FAILED', status: 'error', title: '发生异常', value: 0 },
{ key: 'CANCELED', status: 'warning', title: '停止', value: 0 },
{ key: JOB_STATUS.INITIALIZING, status: 'default', title: '初始化', value: 0 },
{ key: JOB_STATUS.CREATED, status: 'default', title: '创建中', value: 0 },
{ key: JOB_STATUS.RUNNING, status: 'success', title: '运行中', value: 0 },
{ key: JOB_STATUS.FINISHED, status: 'processing', title: '已完成', value: 0 },
{ key: JOB_STATUS.FAILING, status: 'error', title: '异常中', value: 0 },
{ key: JOB_STATUS.FAILED, status: 'error', title: '已异常', value: 0 },
{ key: JOB_STATUS.SUSPENDED, status: 'warning', title: '已暂停', value: 0 },
{ key: JOB_STATUS.CANCELLING, status: 'warning', title: '停止中', value: 0 },
{ key: JOB_STATUS.CANCELED, status: 'warning', title: '已停止', value: 0 },
{ key: JOB_STATUS.RESTARTING, status: 'default', title: '重启中', value: 0 },
{ key: JOB_STATUS.UNKNOWN, status: 'default', title: '未知', value: 0 },
];
const [statusCount, setStatusCount] = useState<any[]>(statusCountDefault);
const [activeKey, setActiveKey] = useState<string>('');
......@@ -28,11 +35,17 @@ const DevOps = (props:any) => {
const statusCountData: StatusCount = result.datas;
const items: any = [
{ key: '', title: '全部', value: statusCountData.all, total: true },
{ key: 'INITIALIZING', status: 'default', title: '初始化', value: statusCountData.initializing },
{ key: 'RUNNING', status: 'success', title: '运行中', value: statusCountData.running },
{ key: 'FINISHED', status: 'processing', title: '已完成', value: statusCountData.finished },
{ key: 'FAILED', status: 'error', title: '发生异常', value: statusCountData.failed },
{ key: 'CANCELED', status: 'warning', title: '停止', value: statusCountData.canceled },
{ key: JOB_STATUS.INITIALIZING, status: 'default', title: '初始化', value: statusCountData.initializing },
{ key: JOB_STATUS.CREATED, status: 'default', title: '创建中', value: statusCountData.created },
{ key: JOB_STATUS.RUNNING, status: 'success', title: '运行中', value: statusCountData.running },
{ key: JOB_STATUS.FINISHED, status: 'processing', title: '已完成', value: statusCountData.finished },
{ key: JOB_STATUS.FAILING, status: 'error', title: '异常中', value: statusCountData.failing },
{ key: JOB_STATUS.FAILED, status: 'error', title: '已异常', value: statusCountData.failed },
{ key: JOB_STATUS.SUSPENDED, status: 'warning', title: '已暂停', value: statusCountData.suspended },
{ key: JOB_STATUS.CANCELLING, status: 'warning', title: '停止中', value: statusCountData.cancelling },
{ key: JOB_STATUS.CANCELED, status: 'warning', title: '停止', value: statusCountData.canceled },
{ key: JOB_STATUS.RESTARTING, status: 'default', title: '重启中', value: statusCountData.restarting },
{ key: JOB_STATUS.UNKNOWN, status: 'default', title: '未知', value: statusCountData.unknown },
];
setStatusCount(items);
});
......@@ -64,7 +77,7 @@ const DevOps = (props:any) => {
title={item.title}
value={item.value}
status={item.status as StatisticProps['status']}
style={{ width: 120, borderRight: item.total ? '1px solid #f0f0f0' : undefined }}
style={{ width: 80, borderRight: item.total ? '1px solid #f0f0f0' : undefined }}
/>
}
>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment