diff --git a/web/src/pages/Route/Create.tsx b/web/src/pages/Route/Create.tsx index 73e3e9e034..575550530f 100644 --- a/web/src/pages/Route/Create.tsx +++ b/web/src/pages/Route/Create.tsx @@ -156,6 +156,7 @@ const Page: React.FC = (props) => { form2={form2} upstreamRef={upstreamRef} step3Data={step3Data} + isEdit={props.route.path.indexOf('edit') > 0} /> ); } diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx index 0602eacb4d..30979fca6b 100644 --- a/web/src/pages/Route/List.tsx +++ b/web/src/pages/Route/List.tsx @@ -22,12 +22,37 @@ import { history, useIntl } from 'umi'; import { PlusOutlined, BugOutlined } from '@ant-design/icons'; import { timestampToLocaleString } from '@/helpers'; -import { fetchList, remove } from './service'; +import { fetchList, remove, updateRouteStatus } from './service'; import { DebugDrawView } from './components/DebugViews'; const Page: React.FC = () => { const ref = useRef(); const { formatMessage } = useIntl(); + + enum RouteStatus { + Offline = 0, + Publish, + } + + const handleTableActionSuccessResponse = (msgTip: string) => { + notification.success({ + message: msgTip, + }); + + ref.current?.reload(); + }; + + const handlePublishOffline = (rid: string, status: RouteModule.RouteStatus) => { + updateRouteStatus(rid, status).then(() => { + const actionName = status ? formatMessage({ id: 'page.route.publish' }) : formatMessage({ id: 'page.route.offline' }) + handleTableActionSuccessResponse( + `${actionName} ${formatMessage({ + id: 'menu.routes', + })} ${formatMessage({ id: 'component.status.success' })}`, + ); + }); + } + const [debugDrawVisible, setDebugDrawVisible] = useState(false); const columns: ProColumns[] = [ @@ -65,6 +90,19 @@ const Page: React.FC = () => { dataIndex: 'desc', hideInSearch: true, }, + { + title: formatMessage({ id: 'page.route.status' }), + dataIndex: 'status', + render: (_, record) => ( + <> + {record.status ? ( + {formatMessage({ id: 'page.route.published' })} + ) : ( + {formatMessage({ id: 'page.route.unpublished' })} + )} + + ), + }, { title: formatMessage({ id: 'component.global.updateTime' }), dataIndex: 'update_time', @@ -85,17 +123,38 @@ const Page: React.FC = () => { > {formatMessage({ id: 'component.global.edit' })} + + { + handlePublishOffline(record.id, RouteStatus.Offline) + }} + okText={formatMessage({ id: 'component.global.confirm' })} + cancelText={formatMessage({ id: 'component.global.cancel' })} + disabled={Boolean(!record.status)} + > + + { remove(record.id!).then(() => { - notification.success({ - message: `${formatMessage({ id: 'component.global.delete' })} ${formatMessage({ + handleTableActionSuccessResponse( + `${formatMessage({ id: 'component.global.delete' })} ${formatMessage({ id: 'menu.routes', })} ${formatMessage({ id: 'component.status.success' })}`, - }); - /* eslint-disable no-unused-expressions */ - ref.current?.reload(); + ); }); }} okText={formatMessage({ id: 'component.global.confirm' })} diff --git a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx index f0c668a1d6..16ba096922 100644 --- a/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx +++ b/web/src/pages/Route/components/CreateStep4/CreateStep4.tsx @@ -30,6 +30,7 @@ type Props = { step3Data: RouteModule.Step3Data; advancedMatchingRules: RouteModule.MatchingRule[]; upstreamRef: any; + isEdit?: boolean; }; const style = { @@ -43,7 +44,7 @@ const CreateStep4: React.FC = ({ form1, form2, redirect, upstreamRef, ... return ( <>

{formatMessage({ id: 'page.route.steps.stepTitle.defineApiRequest' })}

- + {!redirect && ( <>

diff --git a/web/src/pages/Route/components/Step1/MetaView.tsx b/web/src/pages/Route/components/Step1/MetaView.tsx index 7c3053ee3c..a7d86ebc75 100644 --- a/web/src/pages/Route/components/Step1/MetaView.tsx +++ b/web/src/pages/Route/components/Step1/MetaView.tsx @@ -16,11 +16,11 @@ */ import React from 'react'; import Form from 'antd/es/form'; -import { Input } from 'antd'; +import { Input, Switch } from 'antd'; import { useIntl } from 'umi'; import { PanelSection } from '@api7-dashboard/ui'; -const MetaView: React.FC = ({ disabled }) => { +const MetaView: React.FC = ({ disabled, isEdit }) => { const { formatMessage } = useIntl(); return ( @@ -55,6 +55,13 @@ const MetaView: React.FC = ({ disabled }) => { disabled={disabled} /> + + + ); }; diff --git a/web/src/pages/Route/constants.ts b/web/src/pages/Route/constants.ts index 5490f8f737..ee91b1fa6d 100644 --- a/web/src/pages/Route/constants.ts +++ b/web/src/pages/Route/constants.ts @@ -43,7 +43,7 @@ export const FORM_ITEM_WITHOUT_LABEL = { export const DEFAULT_STEP_1_DATA: RouteModule.Form1Data = { name: '', desc: '', - status: false, + status: 1, priority: 0, websocket: false, hosts: [''], diff --git a/web/src/pages/Route/locales/en-US.ts b/web/src/pages/Route/locales/en-US.ts index 56b9ffc35d..ebef2c253e 100644 --- a/web/src/pages/Route/locales/en-US.ts +++ b/web/src/pages/Route/locales/en-US.ts @@ -84,6 +84,9 @@ export default { 'page.route.status': 'Status', 'page.route.groupName': 'GroupName', 'page.route.offline': 'Offline', + 'page.route.publish': 'Publish', + 'page.route.published': 'Published', + 'page.route.unpublished': 'UnPublished', 'page.route.select.option.inputManually': 'Input Manually', 'page.route.form.itemLabel.domainNameOrIp': 'Domain Name/IP', diff --git a/web/src/pages/Route/locales/zh-CN.ts b/web/src/pages/Route/locales/zh-CN.ts index f805e67526..b2d7bc385d 100644 --- a/web/src/pages/Route/locales/zh-CN.ts +++ b/web/src/pages/Route/locales/zh-CN.ts @@ -36,6 +36,9 @@ export default { 'page.route.status': '状态', 'page.route.groupName': '分组名称', 'page.route.offline': '下线', + 'page.route.publish': '发布', + 'page.route.published': '已发布', + 'page.route.unpublished': '未发布', 'page.route.onlineDebug': '在线调试', // button diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts index 8a1fad6375..68eb6d3790 100644 --- a/web/src/pages/Route/service.ts +++ b/web/src/pages/Route/service.ts @@ -86,6 +86,13 @@ export const checkHostWithSSL = (hosts: string[]) => data: hosts, }); + +export const updateRouteStatus = (rid: string, status: RouteModule.RouteStatus) => + request(`/routes/${rid}`, { + method: 'PATCH', + data: {status} + }); + export const debugRoute = (data: RouteModule.debugRequest) => { return request('/debug-request-forwarding', { method: 'post', diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts index 28985dde1a..1a325fffa1 100644 --- a/web/src/pages/Route/transform.ts +++ b/web/src/pages/Route/transform.ts @@ -70,6 +70,9 @@ export const transformStepData = ({ } data.plugins!.redirect = redirect; } + if (data.status !== undefined) { + data.status = Number(data.status); + } // Remove some of the frontend custom variables return omit(data, [ diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts index 824fa829e7..7710337474 100644 --- a/web/src/pages/Route/typing.d.ts +++ b/web/src/pages/Route/typing.d.ts @@ -35,7 +35,7 @@ declare namespace RouteModule { desc: string; uris: string[]; hosts: string[]; - status: boolean; + status: number; }; type Step3Data = { @@ -70,7 +70,7 @@ declare namespace RouteModule { // Request Body or Response Data for API type Body = { id?: number; - status: boolean; + status: number; name: string; desc: string; priority?: number; @@ -140,7 +140,7 @@ declare namespace RouteModule { redirectOption: 'forceHttps' | 'customRedirect' | 'disabled'; redirectURI?: string; ret_code?: number; - status: boolean; + status: number; enable_websocket?: boolean; }; @@ -230,8 +230,11 @@ declare namespace RouteModule { hosts?: string[]; create_time: number; update_time: number; + status: number; }; + type RouteStatus = 0 | 1; + // TODO: grpc and websocket type debugRequest = { url: string;