import React, { useEffect, useRef, useState } from 'react'; import { Modal, message, Input, Form, Button, Card, Tag, List, Divider, Typography, Tooltip, } from 'antd'; import { ClockCircleOutlined, CloseCircleOutlined, FieldTimeOutlined, Loading3QuartersOutlined, FileOutlined, PlayCircleOutlined, PauseCircleOutlined, } from '@ant-design/icons'; import { CrontabStatus } from './index'; import { diffTime } from '@/utils/date'; import { request } from '@/utils/http'; import config from '@/utils/config'; import CronLogModal from './logModal'; import Editor from '@monaco-editor/react'; import IconFont from '@/components/iconfont'; const { Text } = Typography; const tabList = [ { key: 'log', tab: '日志', }, { key: 'script', tab: '脚本', }, ]; interface LogItem { directory: string; filename: string; } const language = navigator.language || navigator.languages[0]; const CronDetailModal = ({ cron = {}, handleCancel, visible, theme, isPhone, }: { cron?: any; visible: boolean; handleCancel: (needUpdate?: boolean) => void; theme: string; isPhone: boolean; }) => { const [activeTabKey, setActiveTabKey] = useState('log'); const [loading, setLoading] = useState(true); const [logs, setLogs] = useState([]); const [log, setLog] = useState(''); const [value, setValue] = useState(''); const [isLogModalVisible, setIsLogModalVisible] = useState(false); const editorRef = useRef(null); const [scriptInfo, setScriptInfo] = useState({}); const [logUrl, setLogUrl] = useState(''); const [validTabs, setValidTabs] = useState(tabList); const [currentCron, setCurrentCron] = useState({}); const contentList: any = { log: ( ( onClickItem(item)}> {item.directory}/{item.filename} )} /> ), script: ( { editorRef.current = editor; }} /> ), }; const onClickItem = (item: LogItem) => { localStorage.setItem('logCron', currentCron.id); setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`); request .get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`) .then((data) => { setLog(data.data); setIsLogModalVisible(true); }); }; const onTabChange = (key: string) => { setActiveTabKey(key); }; const getLogs = () => { setLoading(true); request .get(`${config.apiPrefix}crons/${cron.id}/logs`) .then((data: any) => { if (data.code === 200) { setLogs(data.data); } }) .finally(() => setLoading(false)); }; const getScript = () => { const cmd = cron.command.split(' ') as string[]; if (cmd[0] === 'task') { setValidTabs(validTabs); if (cmd[1].startsWith('/ql/data/scripts')) { cmd[1] = cmd[1].replace('/ql/data/scripts/', ''); } let [p, s] = cmd[1].split('/'); if (!s) { s = p; p = ''; } setScriptInfo({ parent: p, filename: s }); request .get(`${config.apiPrefix}scripts/${s}?path=${p || ''}`) .then((data) => { setValue(data.data); }); } else { setValidTabs([validTabs[0]]); } }; const saveFile = () => { Modal.confirm({ title: `确认保存`, content: ( <> 确认保存文件 {scriptInfo.filename} {' '} ,保存后不可恢复 ), onOk() { const content = editorRef.current ? editorRef.current.getValue().replace(/\r\n/g, '\n') : value; return new Promise((resolve, reject) => { request .put(`${config.apiPrefix}scripts`, { data: { filename: scriptInfo.filename, path: scriptInfo.parent || '', content, }, }) .then((_data: any) => { if (_data.code === 200) { setValue(content); message.success(`保存成功`); } else { message.error(_data); } resolve(null); }) .catch((e) => reject(e)); }); }, onCancel() { console.log('Cancel'); }, }); }; const runCron = () => { Modal.confirm({ title: '确认运行', content: ( <> 确认运行定时任务{' '} {currentCron.name} {' '} 吗 ), onOk() { request .put(`${config.apiPrefix}crons/run`, { data: [currentCron.id] }) .then((data: any) => { if (data.code === 200) { setCurrentCron({ ...currentCron, status: CrontabStatus.running }); setTimeout(() => { getLogs(); }, 1000); } else { message.error(data); } }); }, onCancel() { console.log('Cancel'); }, }); }; const stopCron = () => { Modal.confirm({ title: '确认停止', content: ( <> 确认停止定时任务{' '} {currentCron.name} {' '} 吗 ), onOk() { request .put(`${config.apiPrefix}crons/stop`, { data: [currentCron.id] }) .then((data: any) => { if (data.code === 200) { setCurrentCron({ ...currentCron, status: CrontabStatus.idle }); } else { message.error(data); } }); }, onCancel() { console.log('Cancel'); }, }); }; const enabledOrDisabledCron = () => { Modal.confirm({ title: `确认${currentCron.isDisabled === 1 ? '启用' : '禁用'}`, content: ( <> 确认{currentCron.isDisabled === 1 ? '启用' : '禁用'} 定时任务{' '} {currentCron.name} {' '} 吗 ), onOk() { request .put( `${config.apiPrefix}crons/${ currentCron.isDisabled === 1 ? 'enable' : 'disable' }`, { data: [currentCron.id], }, ) .then((data: any) => { if (data.code === 200) { setCurrentCron({ ...currentCron, isDisabled: currentCron.isDisabled === 1 ? 0 : 1, }); } else { message.error(data); } }); }, onCancel() { console.log('Cancel'); }, }); }; const pinOrUnPinCron = () => { Modal.confirm({ title: `确认${currentCron.isPinned === 1 ? '取消置顶' : '置顶'}`, content: ( <> 确认{currentCron.isPinned === 1 ? '取消置顶' : '置顶'} 定时任务{' '} {currentCron.name} {' '} 吗 ), onOk() { request .put( `${config.apiPrefix}crons/${ currentCron.isPinned === 1 ? 'unpin' : 'pin' }`, { data: [currentCron.id], }, ) .then((data: any) => { if (data.code === 200) { setCurrentCron({ ...currentCron, isPinned: currentCron.isPinned === 1 ? 0 : 1, }); } else { message.error(data); } }); }, onCancel() { console.log('Cancel'); }, }); }; useEffect(() => { if (cron && cron.id) { setCurrentCron(cron); getLogs(); getScript(); } }, [cron]); return (
{currentCron.name} {currentCron.labels?.length > 0 && currentCron.labels[0] !== '' && ( )} {currentCron.labels?.length > 0 && currentCron.labels[0] !== '' && currentCron.labels?.map((label: string, i: number) => ( {label} ))}
} centered visible={visible} forceRender footer={false} onCancel={() => handleCancel()} wrapClassName="crontab-detail" width={!isPhone ? '80vw' : ''} >
任务
{currentCron.command}
状态
{(!currentCron.isDisabled || currentCron.status !== CrontabStatus.idle) && ( <> {currentCron.status === CrontabStatus.idle && ( } color="default"> 空闲中 )} {currentCron.status === CrontabStatus.running && ( } color="processing" > 运行中 )} {currentCron.status === CrontabStatus.queued && ( } color="default"> 队列中 )} )} {currentCron.isDisabled === 1 && currentCron.status === CrontabStatus.idle && ( } color="error"> 已禁用 )}
定时
{currentCron.schedule}
最后运行时间
{currentCron.last_execution_time ? new Date(currentCron.last_execution_time * 1000) .toLocaleString(language, { hour12: false, }) .replace(' 24:', ' 00:') : '-'}
最后运行时长
{currentCron.last_running_time ? diffTime(currentCron.last_running_time) : '-'}
下次运行时间
{currentCron.nextRunTime && currentCron.nextRunTime .toLocaleString(language, { hour12: false, }) .replace(' 24:', ' 00:')}
{ onTabChange(key); }} tabBarExtraContent={ activeTabKey === 'script' && ( ) } > {contentList[activeTabKey]}
{ setIsLogModalVisible(false); }} cron={cron} data={log} logUrl={logUrl} />
); }; export default CronDetailModal;