mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-22 22:36:06 +08:00
更新新建文件订阅
This commit is contained in:
parent
5523d537dc
commit
f6a122e5ea
|
@ -29,7 +29,7 @@ export default (app: Router) => {
|
|||
body: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
schedule: Joi.string().optional(),
|
||||
intervalSchedule: Joi.object().optional(),
|
||||
interval_schedule: Joi.object().optional(),
|
||||
name: Joi.string().optional(),
|
||||
url: Joi.string().required(),
|
||||
whitelist: Joi.string().optional(),
|
||||
|
@ -158,7 +158,7 @@ export default (app: Router) => {
|
|||
body: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
schedule: Joi.string().optional(),
|
||||
intervalSchedule: Joi.object().optional(),
|
||||
interval_schedule: Joi.object().optional(),
|
||||
name: Joi.string().optional(),
|
||||
url: Joi.string().required(),
|
||||
whitelist: Joi.string().optional(),
|
||||
|
|
|
@ -2,13 +2,14 @@ import { sequelize } from '.';
|
|||
import { DataTypes, Model, ModelDefined } from 'sequelize';
|
||||
import { SimpleIntervalSchedule } from 'toad-scheduler';
|
||||
|
||||
type SimpleIntervalScheduleUnit = keyof SimpleIntervalSchedule;
|
||||
export class Subscription {
|
||||
id?: number;
|
||||
name?: string;
|
||||
type?: 'public-repo' | 'private-repo' | 'file';
|
||||
schedule_type?: 'crontab' | 'interval';
|
||||
schedule?: string;
|
||||
intervalSchedule?: SimpleIntervalSchedule;
|
||||
interval_schedule?: { type: SimpleIntervalScheduleUnit; value: number };
|
||||
url?: string;
|
||||
whitelist?: string;
|
||||
blacklist?: string;
|
||||
|
@ -20,7 +21,7 @@ export class Subscription {
|
|||
| { private_key: string }
|
||||
| { username: string; password: string };
|
||||
pid?: number;
|
||||
isDisabled?: 1 | 0;
|
||||
is_disabled?: 1 | 0;
|
||||
log_path?: string;
|
||||
alias: string;
|
||||
command?: string;
|
||||
|
@ -30,6 +31,10 @@ export class Subscription {
|
|||
this.name = options.name;
|
||||
this.type = options.type;
|
||||
this.schedule = options.schedule;
|
||||
this.status =
|
||||
options.status && SubscriptionStatus[options.status]
|
||||
? options.status
|
||||
: SubscriptionStatus.idle;
|
||||
this.url = options.url;
|
||||
this.whitelist = options.whitelist;
|
||||
this.blacklist = options.blacklist;
|
||||
|
@ -39,11 +44,11 @@ export class Subscription {
|
|||
this.pull_type = options.pull_type;
|
||||
this.pull_option = options.pull_option;
|
||||
this.pid = options.pid;
|
||||
this.isDisabled = options.isDisabled;
|
||||
this.is_disabled = options.is_disabled;
|
||||
this.log_path = options.log_path;
|
||||
this.schedule_type = options.schedule_type;
|
||||
this.alias = options.alias;
|
||||
this.intervalSchedule = options.intervalSchedule;
|
||||
this.interval_schedule = options.interval_schedule;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +77,7 @@ export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
|
|||
unique: 'compositeIndex',
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
intervalSchedule: {
|
||||
interval_schedule: {
|
||||
unique: 'compositeIndex',
|
||||
type: DataTypes.JSON,
|
||||
},
|
||||
|
@ -85,7 +90,7 @@ export const SubscriptionModel = sequelize.define<SubscriptionInstance>(
|
|||
pull_type: DataTypes.STRING,
|
||||
pull_option: DataTypes.JSON,
|
||||
pid: DataTypes.NUMBER,
|
||||
isDisabled: DataTypes.NUMBER,
|
||||
is_disabled: DataTypes.NUMBER,
|
||||
log_path: DataTypes.STRING,
|
||||
schedule_type: DataTypes.STRING,
|
||||
alias: { type: DataTypes.STRING, unique: 'alias' },
|
||||
|
|
|
@ -100,10 +100,11 @@ export default class SubscriptionService {
|
|||
needCreate && this.scheduleService.createCronTask(doc as any);
|
||||
} else {
|
||||
this.scheduleService.cancelIntervalTask(doc as any);
|
||||
const { type, value } = doc.interval_schedule as any;
|
||||
needCreate &&
|
||||
this.scheduleService.createIntervalTask(
|
||||
doc as any,
|
||||
doc.intervalSchedule as SimpleIntervalSchedule,
|
||||
{ [type]: value } as SimpleIntervalSchedule,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ enum OperationPath {
|
|||
const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const columns: any = [
|
||||
{
|
||||
title: '任务名',
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
|
@ -127,7 +127,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
},
|
||||
},
|
||||
{
|
||||
title: '任务',
|
||||
title: '命令',
|
||||
dataIndex: 'command',
|
||||
key: 'command',
|
||||
width: 250,
|
||||
|
@ -152,7 +152,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
},
|
||||
},
|
||||
{
|
||||
title: '任务定时',
|
||||
title: '定时规则',
|
||||
dataIndex: 'schedule',
|
||||
key: 'schedule',
|
||||
width: 110,
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
Menu,
|
||||
Typography,
|
||||
Input,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import {
|
||||
ClockCircleOutlined,
|
||||
|
@ -20,6 +21,9 @@ import {
|
|||
EditOutlined,
|
||||
StopOutlined,
|
||||
DeleteOutlined,
|
||||
FileTextOutlined,
|
||||
PauseCircleOutlined,
|
||||
PlayCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
|
@ -28,6 +32,7 @@ import SubscriptionModal from './modal';
|
|||
import { getTableScroll } from '@/utils/index';
|
||||
import { history } from 'umi';
|
||||
import './index.less';
|
||||
import SubscriptionLogModal from './logModal';
|
||||
|
||||
const { Text, Paragraph } = Typography;
|
||||
const { Search } = Input;
|
||||
|
@ -39,10 +44,17 @@ export enum SubscriptionStatus {
|
|||
'queued',
|
||||
}
|
||||
|
||||
export enum IntervalSchedule {
|
||||
'days' = '天',
|
||||
'hours' = '时',
|
||||
'minutes' = '分',
|
||||
'seconds' = '秒',
|
||||
}
|
||||
|
||||
const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||
const columns: any = [
|
||||
{
|
||||
title: '订阅名',
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
|
@ -53,39 +65,35 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
|||
},
|
||||
},
|
||||
{
|
||||
title: '订阅',
|
||||
dataIndex: 'command',
|
||||
key: 'command',
|
||||
width: 250,
|
||||
title: '链接',
|
||||
dataIndex: 'url',
|
||||
key: 'url',
|
||||
align: 'center' as const,
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<Paragraph
|
||||
style={{
|
||||
wordBreak: 'break-all',
|
||||
marginBottom: 0,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
ellipsis={{ tooltip: text, rows: 2 }}
|
||||
>
|
||||
{text}
|
||||
</Paragraph>
|
||||
);
|
||||
},
|
||||
sorter: {
|
||||
compare: (a: any, b: any) => a.command.localeCompare(b.command),
|
||||
multiple: 3,
|
||||
compare: (a: any, b: any) => a.name.localeCompare(b.name),
|
||||
multiple: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '订阅定时',
|
||||
dataIndex: 'schedule',
|
||||
key: 'schedule',
|
||||
width: 110,
|
||||
title: '分支',
|
||||
dataIndex: 'branch',
|
||||
key: 'branch',
|
||||
width: 130,
|
||||
align: 'center' as const,
|
||||
sorter: {
|
||||
compare: (a: any, b: any) => a.schedule.localeCompare(b.schedule),
|
||||
multiple: 1,
|
||||
},
|
||||
{
|
||||
title: '定时规则',
|
||||
width: 180,
|
||||
align: 'center' as const,
|
||||
render: (text: string, record: any) => {
|
||||
const { type, value } = record.interval_schedule;
|
||||
return (
|
||||
<span>
|
||||
{record.schedule_type === 'interval'
|
||||
? `每${value}${(IntervalSchedule as any)[type]}`
|
||||
: record.schedule}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -93,7 +101,7 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
|||
key: 'status',
|
||||
dataIndex: 'status',
|
||||
align: 'center' as const,
|
||||
width: 85,
|
||||
width: 110,
|
||||
filters: [
|
||||
{
|
||||
text: '运行中',
|
||||
|
@ -148,10 +156,45 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
|||
title: '操作',
|
||||
key: 'action',
|
||||
align: 'center' as const,
|
||||
width: 100,
|
||||
width: 130,
|
||||
render: (text: string, record: any, index: number) => {
|
||||
const isPc = !isPhone;
|
||||
return (
|
||||
<Space size="middle">
|
||||
{record.status === SubscriptionStatus.idle && (
|
||||
<Tooltip title={isPc ? '运行' : ''}>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
runSubscription(record, index);
|
||||
}}
|
||||
>
|
||||
<PlayCircleOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
{record.status !== SubscriptionStatus.idle && (
|
||||
<Tooltip title={isPc ? '停止' : ''}>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
stopSubsciption(record, index);
|
||||
}}
|
||||
>
|
||||
<PauseCircleOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title={isPc ? '日志' : ''}>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setLogSubscription({ ...record, timestamp: Date.now() });
|
||||
}}
|
||||
>
|
||||
<FileTextOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<MoreBtn key="more" record={record} index={index} />
|
||||
</Space>
|
||||
);
|
||||
|
@ -168,23 +211,78 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
|||
const [pageSize, setPageSize] = useState(20);
|
||||
const [tableScrollHeight, setTableScrollHeight] = useState<number>();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
|
||||
const [logSubscription, setLogSubscription] = useState<any>();
|
||||
|
||||
const goToScriptManager = (record: any) => {
|
||||
const cmd = record.command.split(' ') as string[];
|
||||
if (cmd[0] === 'task') {
|
||||
if (cmd[1].startsWith('/ql/data/scripts')) {
|
||||
cmd[1] = cmd[1].replace('/ql/data/scripts/', '');
|
||||
}
|
||||
const runSubscription = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确认运行',
|
||||
content: (
|
||||
<>
|
||||
确认运行定时任务{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.name}
|
||||
</Text>{' '}
|
||||
吗
|
||||
</>
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}subscriptions/run`, { data: [record.id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
const result = [...value];
|
||||
const i = result.findIndex((x) => x.id === record.id);
|
||||
result.splice(i, 1, {
|
||||
...record,
|
||||
status: SubscriptionStatus.running,
|
||||
});
|
||||
setValue(result);
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let [p, s] = cmd[1].split('/');
|
||||
if (!s) {
|
||||
s = p;
|
||||
p = '';
|
||||
}
|
||||
history.push(`/script?p=${p}&s=${s}`);
|
||||
} else if (cmd[1] === 'repo') {
|
||||
location.href = cmd[2];
|
||||
}
|
||||
const stopSubsciption = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确认停止',
|
||||
content: (
|
||||
<>
|
||||
确认停止定时任务{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.name}
|
||||
</Text>{' '}
|
||||
吗
|
||||
</>
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}subscriptions/stop`, { data: [record.id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
const result = [...value];
|
||||
const i = result.findIndex((x) => x.id === record.id);
|
||||
result.splice(i, 1, {
|
||||
...record,
|
||||
pid: null,
|
||||
status: SubscriptionStatus.idle,
|
||||
});
|
||||
setValue(result);
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getSubscriptions = () => {
|
||||
|
@ -438,6 +536,13 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
|||
handleCancel={handleCancel}
|
||||
subscription={editedSubscription}
|
||||
/>
|
||||
<SubscriptionLogModal
|
||||
visible={isLogModalVisible}
|
||||
handleCancel={() => {
|
||||
setIsLogModalVisible(false);
|
||||
}}
|
||||
cron={logSubscription}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
|
158
src/pages/subscription/logModal.tsx
Normal file
158
src/pages/subscription/logModal.tsx
Normal file
|
@ -0,0 +1,158 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, message, Input, Form, Statistic, Button } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
import {
|
||||
Loading3QuartersOutlined,
|
||||
CheckCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { PageLoading } from '@ant-design/pro-layout';
|
||||
|
||||
enum CrontabStatus {
|
||||
'running',
|
||||
'idle',
|
||||
'disabled',
|
||||
'queued',
|
||||
}
|
||||
const { Countdown } = Statistic;
|
||||
|
||||
const SubscriptionLogModal = ({
|
||||
cron,
|
||||
handleCancel,
|
||||
visible,
|
||||
data,
|
||||
logUrl,
|
||||
}: {
|
||||
cron?: any;
|
||||
visible: boolean;
|
||||
handleCancel: () => void;
|
||||
data?: string;
|
||||
logUrl?: string;
|
||||
}) => {
|
||||
const [value, setValue] = useState<string>('启动中...');
|
||||
const [loading, setLoading] = useState<any>(true);
|
||||
const [executing, setExecuting] = useState<any>(true);
|
||||
const [isPhone, setIsPhone] = useState(false);
|
||||
const [theme, setTheme] = useState<string>('');
|
||||
|
||||
const getCronLog = (isFirst?: boolean) => {
|
||||
if (isFirst) {
|
||||
setLoading(true);
|
||||
}
|
||||
request
|
||||
.get(logUrl ? logUrl : `${config.apiPrefix}crons/${cron.id}/log`)
|
||||
.then((data: any) => {
|
||||
if (localStorage.getItem('logCron') === String(cron.id)) {
|
||||
const log = data.data as string;
|
||||
setValue(log || '暂无日志');
|
||||
setExecuting(
|
||||
log && !log.includes('执行结束') && !log.includes('重启面板'),
|
||||
);
|
||||
if (log && !log.includes('执行结束') && !log.includes('重启面板')) {
|
||||
setTimeout(() => {
|
||||
getCronLog();
|
||||
}, 2000);
|
||||
}
|
||||
if (
|
||||
log &&
|
||||
log.includes('重启面板') &&
|
||||
cron.status === CrontabStatus.running
|
||||
) {
|
||||
message.warning({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 30}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 10,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 30000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (isFirst) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
localStorage.removeItem('logCron');
|
||||
handleCancel();
|
||||
};
|
||||
|
||||
const titleElement = () => {
|
||||
return (
|
||||
<>
|
||||
{(executing || loading) && <Loading3QuartersOutlined spin />}
|
||||
{!executing && !loading && <CheckCircleOutlined />}
|
||||
<span style={{ marginLeft: 5 }}>日志-{cron && cron.name}</span>{' '}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (cron && cron.id && visible) {
|
||||
getCronLog(true);
|
||||
}
|
||||
}, [cron, visible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValue(data);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsPhone(document.body.clientWidth < 768);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={titleElement()}
|
||||
visible={visible}
|
||||
centered
|
||||
className="log-modal"
|
||||
bodyStyle={{
|
||||
minHeight: '300px',
|
||||
}}
|
||||
forceRender
|
||||
onOk={() => cancel()}
|
||||
onCancel={() => cancel()}
|
||||
footer={[
|
||||
<Button type="primary" onClick={() => cancel()}>
|
||||
知道了
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
{loading ? (
|
||||
<PageLoading />
|
||||
) : (
|
||||
<pre
|
||||
style={
|
||||
isPhone
|
||||
? {
|
||||
fontFamily: 'Source Code Pro',
|
||||
width: 375,
|
||||
zoom: 0.83,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{value}
|
||||
</pre>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubscriptionLogModal;
|
|
@ -3,7 +3,6 @@ import { Modal, message, InputNumber, Form, Radio, Select, Input } from 'antd';
|
|||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
import cron_parser from 'cron-parser';
|
||||
import EditableTagGroup from '@/components/tag';
|
||||
|
||||
const { Option } = Select;
|
||||
const repoUrlRegx = /[^\/\:]+\/[^\/]+(?=\.git)/;
|
||||
|
@ -39,9 +38,7 @@ const SubscriptionModal = ({
|
|||
},
|
||||
);
|
||||
if (code === 200) {
|
||||
message.success(
|
||||
subscription ? '更新Subscription成功' : '新建Subscription成功',
|
||||
);
|
||||
message.success(subscription ? '更新订阅成功' : '新建订阅成功');
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
|
@ -108,21 +105,18 @@ const SubscriptionModal = ({
|
|||
const [intervalNumber, setIntervalNumber] = useState<number>();
|
||||
const intervalTypeChange = (e) => {
|
||||
setIntervalType(e.target.value);
|
||||
onChange?.({ [e.target.value]: intervalNumber });
|
||||
onChange?.({ type: e.target.value, value: intervalNumber });
|
||||
};
|
||||
|
||||
const numberChange = (value: number) => {
|
||||
setIntervalNumber(value);
|
||||
onChange?.({ [intervalType]: value });
|
||||
onChange?.({ type: intervalType, value });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
const key = Object.keys(value)[0];
|
||||
if (key) {
|
||||
setIntervalType(key);
|
||||
setIntervalNumber(value[key]);
|
||||
}
|
||||
setIntervalType(value.type);
|
||||
setIntervalNumber(value.value);
|
||||
}
|
||||
}, [value]);
|
||||
return (
|
||||
|
@ -131,11 +125,11 @@ const SubscriptionModal = ({
|
|||
addonBefore="每"
|
||||
precision={0}
|
||||
min={1}
|
||||
defaultValue={intervalNumber}
|
||||
value={intervalNumber}
|
||||
style={{ width: 'calc(100% - 58px)' }}
|
||||
onChange={numberChange}
|
||||
/>
|
||||
<Select defaultValue={intervalType} onChange={intervalTypeChange}>
|
||||
<Select value={intervalType} onChange={intervalTypeChange}>
|
||||
<Option value="days">天</Option>
|
||||
<Option value="hours">时</Option>
|
||||
<Option value="minutes">分</Option>
|
||||
|
@ -176,10 +170,10 @@ const SubscriptionModal = ({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
setType('public-repo');
|
||||
setScheduleType('crontab');
|
||||
setPullType('ssh-key');
|
||||
form.setFieldsValue(subscription || {});
|
||||
setType((subscription && subscription.type) || 'public-repo');
|
||||
setScheduleType((subscription && subscription.schedule_type) || 'crontab');
|
||||
setPullType((subscription && subscription.pull_type) || 'ssh-key');
|
||||
}, [subscription, visible]);
|
||||
|
||||
return (
|
||||
|
@ -201,14 +195,9 @@ const SubscriptionModal = ({
|
|||
onCancel={() => handleCancel()}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
name="form_in_modal"
|
||||
initialValues={subscription}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item name="name" label="别名">
|
||||
<Input placeholder="请输入订阅别名" />
|
||||
<Form form={form} name="form_in_modal" layout="vertical">
|
||||
<Form.Item name="name" label="名称">
|
||||
<Input placeholder="请输入订阅名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="type"
|
||||
|
@ -230,7 +219,9 @@ const SubscriptionModal = ({
|
|||
{ pattern: type === 'file' ? fileUrlRegx : repoUrlRegx },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
autoSize={true}
|
||||
placeholder="请输入订阅链接"
|
||||
onPaste={onUrlChange}
|
||||
onChange={onUrlChange}
|
||||
|
@ -281,7 +272,7 @@ const SubscriptionModal = ({
|
|||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={scheduleType === 'crontab' ? 'schedule' : 'intervalSchedule'}
|
||||
name={scheduleType === 'crontab' ? 'schedule' : 'interval_schedule'}
|
||||
label="定时规则"
|
||||
rules={[
|
||||
{ required: true },
|
||||
|
|
Loading…
Reference in New Issue
Block a user