From 3b3d45da29b708fec1cd1a58e3589a3e5f3db2b7 Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 28 Aug 2022 15:45:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/cron.ts | 4 +- back/services/cron.ts | 32 +++++--- src/pages/crontab/index.less | 8 ++ src/pages/crontab/index.tsx | 64 +++++++++++---- src/pages/crontab/viewCreateModal.tsx | 110 +++++++++++++++++++++++--- src/pages/crontab/viewManageModal.tsx | 23 +++++- 6 files changed, 203 insertions(+), 38 deletions(-) diff --git a/back/api/cron.ts b/back/api/cron.ts index 0eded1f5..249627ea 100644 --- a/back/api/cron.ts +++ b/back/api/cron.ts @@ -28,7 +28,7 @@ export default (app: Router) => { celebrate({ body: Joi.object({ name: Joi.string().required(), - sorts: Joi.string().required(), + sorts: Joi.string().optional(), filters: Joi.string().optional(), }), }), @@ -107,6 +107,8 @@ export default (app: Router) => { searchText: Joi.string().required().allow(''), page: Joi.string().required(), size: Joi.string().required(), + sortField: Joi.string().optional(), + sortType: Joi.string().optional(), t: Joi.string().required(), }), }), diff --git a/back/services/cron.ts b/back/services/cron.ts index c114434a..9d8e8d5b 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -117,10 +117,14 @@ export default class CronService { searchText: string; page: string; size: string; + sortField: string; + sortType: string; }): Promise<{ data: Crontab[]; total: number }> { const searchText = params?.searchText; const page = Number(params?.page || '0'); const size = Number(params?.size || '0'); + const sortField = params?.sortField || ''; + const sortType = params?.sortType || ''; let query = {}; if (searchText) { @@ -166,14 +170,18 @@ export default class CronService { break; } } + let order = [ + ['isPinned', 'DESC'], + ['isDisabled', 'ASC'], + ['status', 'ASC'], + ['createdAt', 'DESC'], + ]; + if (sortType && sortField) { + order.unshift([sortField, sortType]); + } let condition: any = { where: query, - order: [ - ['isPinned', 'DESC'], - ['isDisabled', 'ASC'], - ['status', 'ASC'], - ['createdAt', 'DESC'], - ], + order: order, }; if (page && size) { condition.offset = (page - 1) * size; @@ -298,11 +306,13 @@ export default class CronService { if (!cmdStr.includes('task ') && !cmdStr.includes('ql ')) { cmdStr = `task ${cmdStr}`; } - if (cmdStr.endsWith('.js') - || cmdStr.endsWith('.py') - || cmdStr.endsWith('.pyc') - || cmdStr.endsWith('.sh') - || cmdStr.endsWith('.ts')) { + if ( + cmdStr.endsWith('.js') || + cmdStr.endsWith('.py') || + cmdStr.endsWith('.pyc') || + cmdStr.endsWith('.sh') || + cmdStr.endsWith('.ts') + ) { cmdStr = `${cmdStr} now`; } diff --git a/src/pages/crontab/index.less b/src/pages/crontab/index.less index 289113d6..7365dc1b 100644 --- a/src/pages/crontab/index.less +++ b/src/pages/crontab/index.less @@ -126,3 +126,11 @@ } } } + +.view-create-modal-filters { + display: flex; + + .ant-space-item:nth-child(3) { + flex: 1; + } +} diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx index 57077fa0..e091f623 100644 --- a/src/pages/crontab/index.tsx +++ b/src/pages/crontab/index.tsx @@ -13,6 +13,7 @@ import { Input, Popover, Tabs, + TablePaginationConfig, } from 'antd'; import { ClockCircleOutlined, @@ -46,6 +47,8 @@ import { history } from 'umi'; import './index.less'; import ViewCreateModal from './viewCreateModal'; import ViewManageModal from './viewManageModal'; +import pagination from 'antd/lib/pagination'; +import { FilterValue, SorterResult } from 'antd/lib/table/interface'; const { Text, Paragraph } = Typography; const { Search } = Input; @@ -130,7 +133,6 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { ), sorter: { compare: (a: any, b: any) => a?.name?.localeCompare(b?.name), - multiple: 2, }, }, { @@ -155,7 +157,6 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { }, sorter: { compare: (a: any, b: any) => a.command.localeCompare(b.command), - multiple: 3, }, }, { @@ -166,7 +167,6 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { align: 'center' as const, sorter: { compare: (a: any, b: any) => a.schedule.localeCompare(b.schedule), - multiple: 1, }, }, { @@ -353,9 +353,11 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { const [isLogModalVisible, setIsLogModalVisible] = useState(false); const [logCron, setLogCron] = useState(); const [selectedRowIds, setSelectedRowIds] = useState([]); - const [pageConf, setPageConf] = useState<{ page: number; size: number }>( - {} as any, - ); + const [pageConf, setPageConf] = useState<{ + page: number; + size: number; + sorter: any; + }>({} as any); const [tableScrollHeight, setTableScrollHeight] = useState(); const [isDetailModalVisible, setIsDetailModalVisible] = useState(false); const [detailCron, setDetailCron] = useState(); @@ -365,6 +367,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { useState(false); const [isViewManageModalVisible, setIsViewManageModalVisible] = useState(false); + const [cronViews, setCronViews] = useState([]); const goToScriptManager = (record: any) => { const cmd = record.command.split(' ') as string[]; @@ -386,10 +389,15 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { const getCrons = () => { setLoading(true); + const { page, size, sorter } = pageConf; + let url = `${config.apiPrefix}crons?searchText=${searchText}&page=${page}&size=${size}`; + if (sorter && sorter.field) { + url += `&sortField=${sorter.field}&sortType=${ + sorter.order === 'ascend' ? 'ASC' : 'DESC' + }`; + } request - .get( - `${config.apiPrefix}crons?searchText=${searchText}&page=${pageConf.page}&size=${pageConf.size}`, - ) + .get(url) .then((_data: any) => { const { data, total } = _data.data; setValue( @@ -791,8 +799,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { }); }; - const onPageChange = (page: number, pageSize: number | undefined) => { - setPageConf({ page, size: pageSize as number }); + const onPageChange = ( + pagination: TablePaginationConfig, + filters: Record, + sorter: SorterResult | SorterResult[], + ) => { + const { current, pageSize } = pagination; + setPageConf({ page: current as number, size: pageSize as number, sorter }); localStorage.setItem('pageSize', String(pageSize)); }; @@ -821,10 +834,12 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { setPageConf({ page: 1, size: parseInt(localStorage.getItem('pageSize') || '20'), + sorter: {}, }); setTimeout(() => { setTableScrollHeight(getTableScroll()); }); + getCronViews(); }, []); const panelContent = ( @@ -889,7 +904,6 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { columns={columns} pagination={{ current: pageConf.page, - onChange: onPageChange, pageSize: pageConf.size, showSizeChanger: true, simple: isPhone, @@ -915,6 +929,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { loading={loading} rowSelection={rowSelection} rowClassName={getRowClassName} + onChange={onPageChange} /> ); @@ -940,11 +955,11 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { viewAction(key); }} items={[ - { - label: 'Clicking me will not close the menu.', - key: '1', + ...[...cronViews].slice(5).map((x) => ({ + label: x.name, + key: x.id, icon: , - }, + })), { type: 'divider', }, @@ -962,6 +977,18 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { /> ); + const getCronViews = () => { + setLoading(true); + request + .get(`${config.apiPrefix}crons/views`) + .then((data: any) => { + setCronViews(data.data); + }) + .finally(() => { + setLoading(false); + }); + }; + return ( { {panelContent} + {[...cronViews].slice(0, 5).map((x) => ( + + {panelContent} + + ))} { setLoading(true); - const { value, name } = values; const method = view ? 'put' : 'post'; - let payload; - if (!view) { - payload = [{ value, name }]; - } else { - payload = { ...values, id: view.id }; - } try { const { code, data } = await request[method]( `${config.apiPrefix}crons/views`, { - data: payload, + data: values, }, ); if (code !== 200) { @@ -44,13 +60,37 @@ const ViewCreateModal = ({ useEffect(() => { form.resetFields(); + form.setFieldsValue({ + filters: [{ property: 'command', operation: 'contains' }], + }); }, [view, visible]); + const operationElement = ( + + ); + + const propertyElement = ( + + ); + return ( { @@ -74,6 +114,58 @@ const ViewCreateModal = ({ > + + {(fields, { add, remove }) => ( + <> + {fields.map(({ key, name, ...restField }, index) => ( + + + + {propertyElement} + + + {operationElement} + + + + + {index !== 0 && ( + remove(name)} /> + )} + + + ))} + + + add({ property: 'command', operation: 'contains' }) + } + > + + 新增筛选条件 + + + + )} + ); diff --git a/src/pages/crontab/viewManageModal.tsx b/src/pages/crontab/viewManageModal.tsx index 457b31d7..aba6ecb7 100644 --- a/src/pages/crontab/viewManageModal.tsx +++ b/src/pages/crontab/viewManageModal.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { Modal, message, Space, Table, Tag, Typography } from 'antd'; +import { Modal, message, Space, Table, Tag, Typography, Button } from 'antd'; import { request } from '@/utils/http'; import config from '@/utils/config'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; @@ -7,6 +7,7 @@ import { PageLoading } from '@ant-design/pro-layout'; import Paragraph from 'antd/lib/skeleton/Paragraph'; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; +import ViewCreateModal from './viewCreateModal'; const { Text } = Typography; @@ -69,6 +70,7 @@ const ViewManageModal = ({ dataIndex: 'name', key: 'name', align: 'center' as const, + width: 70, }, { title: '显示', @@ -101,6 +103,8 @@ const ViewManageModal = ({ ]; const [list, setList] = useState([]); const [loading, setLoading] = useState(true); + const [isCreateViewModalVisible, setIsCreateViewModalVisible] = + useState(false); const editView = (record: any, index: number) => { // setEditedEnv(record); @@ -190,11 +194,22 @@ const ViewManageModal = ({ title="视图管理" visible={visible} centered + width={620} onCancel={() => handleCancel()} className="view-manage-modal" forceRender footer={false} + maskClosable={false} > + + + {loading ? ( ) : ( @@ -216,6 +231,12 @@ const ViewManageModal = ({ /> )} + { + setIsCreateViewModalVisible(false); + }} + /> ); };