完善定时任务视图

This commit is contained in:
whyour 2022-09-02 17:52:06 +08:00
parent bc5a3a2028
commit 2f05c95422
7 changed files with 139 additions and 90 deletions

View File

@ -28,8 +28,8 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
name: Joi.string().required(), name: Joi.string().required(),
sorts: Joi.string().optional(), sorts: Joi.array().optional(),
filters: Joi.string().optional(), filters: Joi.array().optional(),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
@ -49,8 +49,8 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
name: Joi.string().required(), name: Joi.string().required(),
id: Joi.number().required(), id: Joi.number().required(),
sorts: Joi.string().optional(), sorts: Joi.array().optional(),
filters: Joi.string().optional(), filters: Joi.array().optional(),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {

View File

@ -14,7 +14,7 @@ import dayjs from 'dayjs';
@Service() @Service()
export default class CronService { export default class CronService {
constructor(@Inject('logger') private logger: winston.Logger) {} constructor(@Inject('logger') private logger: winston.Logger) { }
private isSixCron(cron: Crontab) { private isSixCron(cron: Crontab) {
const { schedule } = cron; const { schedule } = cron;
@ -113,20 +113,34 @@ export default class CronService {
} }
} }
public async crontabs(params?: { private formatViewQuery(query: any, viewQuery: any) {
searchValue: string; if (viewQuery.filters) {
page: string; for (const col of viewQuery.filters) {
size: string; const { property, value, operation } = col;
sortField: string; let operate = null;
sortType: string; switch (operation) {
}): Promise<{ data: Crontab[]; total: number }> { case 'Reg':
const searchText = params?.searchValue; operate = Op.like;
const page = Number(params?.page || '0'); break;
const size = Number(params?.size || '0'); case 'Reg':
const sortField = params?.sortField || ''; operate = Op.notLike;
const sortType = params?.sortType || ''; break;
default:
break;
}
if (operate) {
query[property] = {
[Op.or]: [
{ [operate]: `%${value}%` },
{ [operate]: `%${encodeURIComponent(value)}%` },
],
}
}
}
}
}
let query = {}; private formatSearchText(query: any, searchText: string | undefined) {
if (searchText) { if (searchText) {
const textArray = searchText.split(':'); const textArray = searchText.split(':');
switch (textArray[0]) { switch (textArray[0]) {
@ -170,12 +184,43 @@ export default class CronService {
break; break;
} }
} }
}
private formatViewSort(order: string[][], viewQuery: any) {
if (viewQuery.sorts) {
for (const [col, sortType] of viewQuery.sorts) {
order.unshift([col, sortType]);
}
}
}
public async crontabs(params?: {
searchValue: string;
page: string;
size: string;
sortField: string;
sortType: string;
queryString: string;
}): Promise<{ data: Crontab[]; total: number }> {
const searchText = params?.searchValue;
const page = Number(params?.page || '0');
const size = Number(params?.size || '0');
const sortField = params?.sortField || '';
const sortType = params?.sortType || '';
const viewQuery = JSON.parse(params?.queryString || '{}');
let query: any = {};
let order = [ let order = [
['isPinned', 'DESC'], ['isPinned', 'DESC'],
['isDisabled', 'ASC'], ['isDisabled', 'ASC'],
['status', 'ASC'], ['status', 'ASC'],
['createdAt', 'DESC'], ['createdAt', 'DESC'],
]; ];
this.formatViewQuery(query, viewQuery);
this.formatSearchText(query, searchText);
this.formatViewSort(order, viewQuery);
if (sortType && sortField) { if (sortType && sortField) {
order.unshift([sortField, sortType]); order.unshift([sortField, sortType]);
} }
@ -229,9 +274,9 @@ export default class CronService {
const endTime = dayjs(); const endTime = dayjs();
const diffTimeStr = doc.last_execution_time const diffTimeStr = doc.last_execution_time
? `,耗时 ${endTime.diff( ? `,耗时 ${endTime.diff(
dayjs(doc.last_execution_time * 1000), dayjs(doc.last_execution_time * 1000),
'second', 'second',
)}` )}`
: ''; : '';
if (logFileExist) { if (logFileExist) {
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';

View File

@ -186,9 +186,8 @@ export default function (props: any) {
if ( if (
['/login', '/initialization', '/error'].includes(props.location.pathname) ['/login', '/initialization', '/error'].includes(props.location.pathname)
) { ) {
document.title = `${ document.title = `${(config.documentTitleMap as any)[props.location.pathname]
(config.documentTitleMap as any)[props.location.pathname] } - `;
} - `;
if ( if (
systemInfo?.isInitialized && systemInfo?.isInitialized &&
props.location.pathname === '/initialization' props.location.pathname === '/initialization'
@ -282,7 +281,7 @@ export default function (props: any) {
shape="square" shape="square"
size="small" size="small"
icon={<UserOutlined />} icon={<UserOutlined />}
src={`/api/static/${user.avatar}`} src={user.avatar ? `/api/static/${user.avatar}` : ''}
/> />
<span style={{ marginLeft: 5 }}>{user.username}</span> <span style={{ marginLeft: 5 }}>{user.username}</span>
</span> </span>
@ -304,7 +303,7 @@ export default function (props: any) {
shape="square" shape="square"
size="small" size="small"
icon={<UserOutlined />} icon={<UserOutlined />}
src={`/api/static/${user.avatar}`} src={user.avatar ? `/api/static/${user.avatar}` : ''}
/> />
<span style={{ marginLeft: 5 }}>{user.username}</span> <span style={{ marginLeft: 5 }}>{user.username}</span>
</span> </span>

View File

@ -108,7 +108,7 @@
} }
.view-more { .view-more {
margin-left: 20px; margin-left: 32px;
padding: 8px 0; padding: 8px 0;
cursor: pointer; cursor: pointer;

View File

@ -358,6 +358,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
size: number; size: number;
sorter: any; sorter: any;
}>({} as any); }>({} as any);
const [viewConf, setViewConf] = useState<any>();
const [tableScrollHeight, setTableScrollHeight] = useState<number>(); const [tableScrollHeight, setTableScrollHeight] = useState<number>();
const [isDetailModalVisible, setIsDetailModalVisible] = useState(false); const [isDetailModalVisible, setIsDetailModalVisible] = useState(false);
const [detailCron, setDetailCron] = useState<any>(); const [detailCron, setDetailCron] = useState<any>();
@ -396,6 +397,9 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
sorter.order === 'ascend' ? 'ASC' : 'DESC' sorter.order === 'ascend' ? 'ASC' : 'DESC'
}`; }`;
} }
if (viewConf) {
url += `&queryString=${JSON.stringify({ filters: viewConf.filters })}`;
}
request request
.get(url) .get(url)
.then((_data: any) => { .then((_data: any) => {
@ -828,7 +832,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
if (pageConf.page && pageConf.size) { if (pageConf.page && pageConf.size) {
getCrons(); getCrons();
} }
}, [pageConf]); }, [pageConf, viewConf]);
useEffect(() => { useEffect(() => {
setPageConf({ setPageConf({
@ -944,6 +948,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
break; break;
default: default:
tabClick(key);
break; break;
} }
}; };
@ -955,7 +960,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
viewAction(key); viewAction(key);
}} }}
items={[ items={[
...[...cronViews].slice(5).map((x) => ({ ...[...cronViews].slice(2).map((x) => ({
label: x.name, label: x.name,
key: x.id, key: x.id,
icon: <UnorderedListOutlined />, icon: <UnorderedListOutlined />,
@ -989,6 +994,11 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
}); });
}; };
const tabClick = (key: string) => {
const view = cronViews.find(x => x.id == key);
setViewConf(view ? view : null);
}
return ( return (
<PageContainer <PageContainer
className="ql-container-wrapper crontab-wrapper ql-container-wrapper-has-tab" className="ql-container-wrapper crontab-wrapper ql-container-wrapper-has-tab"
@ -1018,7 +1028,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
tabPosition="top" tabPosition="top"
className="crontab-view" className="crontab-view"
tabBarExtraContent={ tabBarExtraContent={
<Dropdown overlay={menu} trigger={['click']}> <Dropdown overlay={menu} trigger={['click']} overlayStyle={{minWidth: 200}}>
<div className="view-more"> <div className="view-more">
<Space> <Space>
@ -1028,11 +1038,12 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
</div> </div>
</Dropdown> </Dropdown>
} }
onTabClick={tabClick}
> >
<Tabs.TabPane tab="全部任务" key="all"> <Tabs.TabPane tab="全部任务" key="all">
{panelContent} {panelContent}
</Tabs.TabPane> </Tabs.TabPane>
{[...cronViews].slice(0, 5).map((x) => ( {[...cronViews].slice(0, 2).map((x) => (
<Tabs.TabPane tab={x.name} key={x.id}> <Tabs.TabPane tab={x.name} key={x.id}>
{panelContent} {panelContent}
</Tabs.TabPane> </Tabs.TabPane>
@ -1073,14 +1084,19 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
<ViewCreateModal <ViewCreateModal
visible={isCreateViewModalVisible} visible={isCreateViewModalVisible}
handleCancel={() => { handleCancel={() => {
getCronViews();
setIsCreateViewModalVisible(false); setIsCreateViewModalVisible(false);
}} }}
/> />
<ViewManageModal <ViewManageModal
cronViews={cronViews}
visible={isViewManageModalVisible} visible={isViewManageModalVisible}
handleCancel={() => { handleCancel={() => {
setIsViewManageModalVisible(false); setIsViewManageModalVisible(false);
}} }}
cronViewChange={() => {
getCronViews();
}}
/> />
</PageContainer> </PageContainer>
); );

View File

@ -20,10 +20,14 @@ const PROPERTIES = [
]; ];
const OPERATIONS = [ const OPERATIONS = [
{ name: '包含', value: 'contains' }, { name: '包含', value: 'Reg' },
{ name: '不包含', value: 'noncontains' }, { name: '不包含', value: 'NotReg' },
// { name: '属于', value: 'belong' }, // { name: '属于', value: 'In' },
// { name: '不属于', value: 'nonbelong' }, // { name: '不属于', value: 'Nin' },
// { name: '等于', value: 'Eq' },
// { name: '不等于', value: 'Ne' },
// { name: '为空', value: 'IsNull' },
// { name: '不为空', value: 'NotNull' },
]; ];
const ViewCreateModal = ({ const ViewCreateModal = ({
@ -61,7 +65,7 @@ const ViewCreateModal = ({
useEffect(() => { useEffect(() => {
form.resetFields(); form.resetFields();
form.setFieldsValue({ form.setFieldsValue({
filters: [{ property: 'command', operation: 'contains' }], filters: [{ property: 'command', operation: 'Reg' }],
}); });
}, [view, visible]); }, [view, visible]);
@ -154,9 +158,9 @@ const ViewCreateModal = ({
))} ))}
<Form.Item> <Form.Item>
<a <a
href="" href="#"
onClick={() => onClick={() =>
add({ property: 'command', operation: 'contains' }) add({ property: 'command', operation: 'Reg' })
} }
> >
<PlusOutlined /> <PlusOutlined />

View File

@ -3,8 +3,6 @@ import { Modal, message, Space, Table, Tag, Typography, Button } from 'antd';
import { request } from '@/utils/http'; import { request } from '@/utils/http';
import config from '@/utils/config'; import config from '@/utils/config';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
import { PageLoading } from '@ant-design/pro-layout';
import Paragraph from 'antd/lib/skeleton/Paragraph';
import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import ViewCreateModal from './viewCreateModal'; import ViewCreateModal from './viewCreateModal';
@ -58,11 +56,15 @@ const DragableBodyRow = ({
}; };
const ViewManageModal = ({ const ViewManageModal = ({
cronViews,
handleCancel, handleCancel,
visible, visible,
cronViewChange
}: { }: {
cronViews: any[];
visible: boolean; visible: boolean;
handleCancel: () => void; handleCancel: () => void;
cronViewChange: () => void;
}) => { }) => {
const columns: any = [ const columns: any = [
{ {
@ -72,16 +74,16 @@ const ViewManageModal = ({
align: 'center' as const, align: 'center' as const,
width: 70, width: 70,
}, },
{ // {
title: '显示', // title: '显示',
key: 'status', // key: 'isDisabled',
dataIndex: 'status', // dataIndex: 'isDisabled',
align: 'center' as const, // align: 'center' as const,
width: 70, // width: 70,
render: (text: string, record: any, index: number) => { // render: (text: string, record: any, index: number) => {
return <Space size="middle" style={{ cursor: 'text' }}></Space>; // return <Space size="middle" style={{ cursor: 'text' }}></Space>;
}, // },
}, // },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
@ -102,7 +104,6 @@ const ViewManageModal = ({
}, },
]; ];
const [list, setList] = useState<any[]>([]); const [list, setList] = useState<any[]>([]);
const [loading, setLoading] = useState<any>(true);
const [isCreateViewModalVisible, setIsCreateViewModalVisible] = const [isCreateViewModalVisible, setIsCreateViewModalVisible] =
useState<boolean>(false); useState<boolean>(false);
@ -129,9 +130,7 @@ const ViewManageModal = ({
.then((data: any) => { .then((data: any) => {
if (data.code === 200) { if (data.code === 200) {
message.success('删除成功'); message.success('删除成功');
const result = [...list]; cronViewChange();
result.splice(index, 1);
setList(result);
} else { } else {
message.error(data); message.error(data);
} }
@ -143,18 +142,6 @@ const ViewManageModal = ({
}); });
}; };
const getCronViews = () => {
setLoading(true);
request
.get(`${config.apiPrefix}crons/views`)
.then((data: any) => {
console.log(data);
})
.finally(() => {
setLoading(false);
});
};
const components = { const components = {
body: { body: {
row: DragableBodyRow, row: DragableBodyRow,
@ -186,8 +173,8 @@ const ViewManageModal = ({
); );
useEffect(() => { useEffect(() => {
// getCronViews(); setList(cronViews);
}, []); }, [cronViews]);
return ( return (
<Modal <Modal
@ -201,7 +188,7 @@ const ViewManageModal = ({
footer={false} footer={false}
maskClosable={false} maskClosable={false}
> >
<Space style={{ display: 'flex', justifyContent: 'flex-end' }}> <Space style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }}>
<Button <Button
key="2" key="2"
type="primary" type="primary"
@ -210,30 +197,28 @@ const ViewManageModal = ({
</Button> </Button>
</Space> </Space>
{loading ? ( <DndProvider backend={HTML5Backend}>
<PageLoading /> <Table
) : ( bordered
<DndProvider backend={HTML5Backend}> columns={columns}
<Table pagination={false}
columns={columns} dataSource={list}
pagination={false} rowKey="id"
dataSource={list} size="middle"
rowKey="id" style={{ marginBottom: 20 }}
size="middle" components={components}
components={components} onRow={(record: any, index: number) => {
loading={loading} return {
onRow={(record: any, index: number) => { index,
return { moveRow,
index, } as any;
moveRow, }}
} as any; />
}} </DndProvider>
/>
</DndProvider>
)}
<ViewCreateModal <ViewCreateModal
visible={isCreateViewModalVisible} visible={isCreateViewModalVisible}
handleCancel={() => { handleCancel={() => {
cronViewChange();
setIsCreateViewModalVisible(false); setIsCreateViewModalVisible(false);
}} }}
/> />