This is an automated email from the ASF dual-hosted git repository. juzhiyuan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push: new e2271b3 Feat: dashboard support route group (#433) e2271b3 is described below commit e2271b386764cff0b903cae7ee3b16afcd8d1883 Author: liuxiran <belovedx...@126.com> AuthorDate: Fri Sep 11 00:19:38 2020 +0800 Feat: dashboard support route group (#433) * feat: route group UI * feat(route-ui): add routegroup when create route * fix: add routegroup to route list * fix: support add routegroup together with route * fix: route path define error * feat: trigger redeploy * fix: update i18n key Co-authored-by: 琚致远 <juzhiy...@apache.org> --- config/routes.ts | 12 +++ src/helpers.tsx | 5 ++ src/locales/en-US/menu.ts | 1 + src/locales/zh-CN/menu.ts | 1 + src/pages/Route/Create.tsx | 11 ++- src/pages/Route/List.tsx | 4 + src/pages/Route/components/Step1/MetaView.tsx | 53 ++++++++++++- src/pages/Route/constants.ts | 2 + src/pages/Route/locales/en-US.ts | 8 +- src/pages/Route/locales/zh-CN.ts | 8 +- src/pages/Route/service.ts | 11 +++ src/pages/Route/transform.ts | 15 +++- src/pages/Route/typing.d.ts | 4 + src/pages/RouteGroup/Create.tsx | 88 ++++++++++++++++++++++ src/pages/{Route => RouteGroup}/List.tsx | 69 ++++++----------- src/pages/RouteGroup/components/Preview.tsx | 28 +++++++ .../components/Step1.tsx} | 43 ++++++----- src/pages/RouteGroup/constants.ts | 31 ++++++++ src/pages/RouteGroup/index.ts | 18 +++++ src/pages/RouteGroup/locales/en-US.ts | 45 +++++++++++ src/pages/RouteGroup/locales/zh-CN.ts | 45 +++++++++++ src/pages/RouteGroup/service.ts | 46 +++++++++++ src/pages/RouteGroup/typing.d.ts | 23 ++++++ 23 files changed, 498 insertions(+), 73 deletions(-) diff --git a/config/routes.ts b/config/routes.ts index 6648413..7512519 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -36,6 +36,18 @@ const routes = [ component: './Route/Create', }, { + path: '/routegroup/list', + component: './RouteGroup/List', + }, + { + path: '/routegroup/create', + component: './RouteGroup/Create', + }, + { + path: '/routegroup/:gid/edit', + component: './RouteGroup/Create', + }, + { path: '/ssl/:id/edit', component: './SSL/Create', }, diff --git a/src/helpers.tsx b/src/helpers.tsx index 39e97da..4b9151d 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -35,6 +35,11 @@ export const getMenuData = (): MenuDataItem[] => { icon: <IconFont type="iconroute" />, }, { + name: 'routegroup', + path: '/routegroup/list', + icon: <IconFont type="iconroute" />, + }, + { name: 'ssl', path: '/ssl/list', icon: <IconFont type="iconSSLshuzizhengshu" />, diff --git a/src/locales/en-US/menu.ts b/src/locales/en-US/menu.ts index 0528b97..8afd19b 100644 --- a/src/locales/en-US/menu.ts +++ b/src/locales/en-US/menu.ts @@ -66,6 +66,7 @@ export default { 'menu.editor.koni': 'Koni Editor', 'menu.metrics': 'Metrics', 'menu.routes': 'Route', + 'menu.routegroup': 'RouteGroup', 'menu.ssl': 'SSL', 'menu.upstream': 'Upstream', 'menu.consumer': 'Consumer', diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index df0fbca..7597a94 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -66,6 +66,7 @@ export default { 'menu.editor.koni': '拓扑编辑器', 'menu.metrics': '监控', 'menu.routes': '路由', + 'menu.routegroup': '路由分组', 'menu.ssl': '证书', 'menu.upstream': '上游', 'menu.consumer': '用户', diff --git a/src/pages/Route/Create.tsx b/src/pages/Route/Create.tsx index 274f5ba..dcc21b1 100644 --- a/src/pages/Route/Create.tsx +++ b/src/pages/Route/Create.tsx @@ -29,6 +29,7 @@ import { checkUniqueName, fetchUpstreamItem, checkHostWithSSL, + fetchRouteGroupItem, } from './service'; import Step1 from './components/Step1'; import Step2 from './components/Step2'; @@ -132,7 +133,15 @@ const Page: React.FC<Props> = (props) => { data={routeData} form={form1} onChange={(params: RouteModule.Step1Data) => { - setStep1Data({ ...step1Data, ...params }); + if (params.route_group_id) { + fetchRouteGroupItem(params.route_group_id).then((data) => { + form1.setFieldsValue({ + ...form1.getFieldsValue(), + ...data, + }); + }); + } + setStep1Data({ ...form1.getFieldsValue(), ...step1Data, ...params }); }} /> ); diff --git a/src/pages/Route/List.tsx b/src/pages/Route/List.tsx index df2a899..e37d043 100644 --- a/src/pages/Route/List.tsx +++ b/src/pages/Route/List.tsx @@ -63,6 +63,10 @@ const Page: React.FC = () => { dataIndex: 'description', }, { + title: formatMessage({ id: 'route.list.group.name' }), + dataIndex: 'route_group_name', + }, + { title: formatMessage({ id: 'route.list.edit.time' }), dataIndex: 'update_time', render: (text) => `${moment.unix(Number(text)).format('YYYY-MM-DD HH:mm:ss')}`, diff --git a/src/pages/Route/components/Step1/MetaView.tsx b/src/pages/Route/components/Step1/MetaView.tsx index b04a5ac..afa1b90 100644 --- a/src/pages/Route/components/Step1/MetaView.tsx +++ b/src/pages/Route/components/Step1/MetaView.tsx @@ -14,16 +14,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import Form from 'antd/es/form'; -import { Input } from 'antd'; +import { Input, Select } from 'antd'; import { useIntl } from 'umi'; import { PanelSection } from '@api7-dashboard/ui'; +import { fetchRouteGroupList } from '@/pages/Route/service'; + interface Props extends RouteModule.Data {} -const MetaView: React.FC<Props> = ({ disabled }) => { +const MetaView: React.FC<Props> = ({ data, disabled, onChange }) => { + const { step1Data } = data; const { formatMessage } = useIntl(); + const routeGroupDisabled = disabled || !!step1Data.route_group_id; + const [routeGroups, setRouteGroups] = useState<{ id: string; name: string }[]>(); + useEffect(() => { + // eslint-disable-next-line no-shadow + fetchRouteGroupList().then(({ data }) => { + setRouteGroups([ + { name: formatMessage({ id: 'route.meta.api.create.group.name' }), id: null }, + ...data, + ]); + if (step1Data.route_group_id) { + onChange({ route_group_id: step1Data.route_group_id }); + } + }); + }, []); return ( <PanelSection title={formatMessage({ id: 'route.meta.name.description' })}> <Form.Item @@ -43,6 +60,36 @@ const MetaView: React.FC<Props> = ({ disabled }) => { disabled={disabled} /> </Form.Item> + <Form.Item label={formatMessage({ id: 'route.meta.api.group.name' })} name="route_group_id"> + <Select + onChange={(value) => { + if (step1Data.route_group_id) { + onChange({ route_group_id: value }); + } + }} + disabled={disabled} + > + {(routeGroups || []).map((item) => { + return ( + <Select.Option value={item.id} key={item.id}> + {item.name} + </Select.Option> + ); + })} + </Select> + </Form.Item> + <Form.Item + label={formatMessage({ id: 'route.meta.group.name' })} + name="route_group_name" + rules={[ + { required: true, message: formatMessage({ id: 'route.meta.input.api.group.name' }) }, + ]} + > + <Input + placeholder={formatMessage({ id: 'route.meta.input.api.group.name' })} + disabled={routeGroupDisabled} + /> + </Form.Item> <Form.Item label={formatMessage({ id: 'route.meta.description' })} name="desc"> <Input.TextArea placeholder={formatMessage({ id: 'route.meta.description.rule' })} diff --git a/src/pages/Route/constants.ts b/src/pages/Route/constants.ts index 0620bbd..cc0e05b 100644 --- a/src/pages/Route/constants.ts +++ b/src/pages/Route/constants.ts @@ -41,6 +41,8 @@ export const FORM_ITEM_WITHOUT_LABEL = { }; export const DEFAULT_STEP_1_DATA: RouteModule.Step1Data = { + route_group_id: '', + route_group_name: '', name: '', desc: '', priority: 0, diff --git a/src/pages/Route/locales/en-US.ts b/src/pages/Route/locales/en-US.ts index 78baa5f..b102e2f 100644 --- a/src/pages/Route/locales/en-US.ts +++ b/src/pages/Route/locales/en-US.ts @@ -61,8 +61,13 @@ export default { 'Maximum length 100, only letters, Numbers, _, and - are supported, and can only begin with letters', 'rotue.meta.api.rule': 'Only letters, numbers, _ and - are supported, and can only begin with letters', - 'route.meta.description': 'Description', + 'route.meta.api.group.name': 'RouteGroup', + 'route.meta.group.name': 'GroupName', + 'route.meta.input.api.group.name': 'Please enter the group name', + 'route.meta.api.create.group.name': 'Create route group', + 'route.meta.description': 'APIDescription', 'route.meta.description.rule': 'Can not more than 200 characters', + 'route.meta.group.description': 'GroupDescription', 'route.request.config.domain.name': 'Domain Name', 'route.request.config.domain.or.ip': @@ -146,6 +151,7 @@ export default { 'route.list.domain.name': 'Domain Name', 'route.list.path': 'Path', 'route.list.description': 'Description', + 'route.list.group.name': 'RouteGroup', 'route.list.edit.time': 'Edit Time', 'route.list.operation': 'Operation', 'route.list.edit': 'Edit', diff --git a/src/pages/Route/locales/zh-CN.ts b/src/pages/Route/locales/zh-CN.ts index 5894354..a3385fd 100644 --- a/src/pages/Route/locales/zh-CN.ts +++ b/src/pages/Route/locales/zh-CN.ts @@ -58,8 +58,13 @@ export default { 'route.meta.input.api.name': '请输入 API 名称', 'route.meta.api.name.rule': '最大长度100,仅支持字母、数字、- 和 _,且只能以字母开头', 'rotue.meta.api.rule': '仅支持字母、数字、- 和 _,且只能以字母开头', - 'route.meta.description': '描述', + 'route.meta.api.group.name': '路由分组', + 'route.meta.group.name': '分组名称', + 'route.meta.input.api.group.name': '请输入路由分组名称', + 'route.meta.api.create.group.name': '创建路由分组', + 'route.meta.description': '路由描述', 'route.meta.description.rule': '不超过 200 个字符', + 'route.meta.group.description': '分组描述', 'route.request.config.domain.name': '域名', 'route.request.config.domain.or.ip': '域名或IP,支持泛域名,如:*.test.com', @@ -141,6 +146,7 @@ export default { 'route.list.domain.name': '域名', 'route.list.path': '路径', 'route.list.description': '描述', + 'route.list.group.name': '路由分组', 'route.list.edit.time': '编辑时间', 'route.list.operation': '操作', 'route.list.edit': '编辑', diff --git a/src/pages/Route/service.ts b/src/pages/Route/service.ts index e9434f5..171d4f5 100644 --- a/src/pages/Route/service.ts +++ b/src/pages/Route/service.ts @@ -62,6 +62,17 @@ export const checkUniqueName = (name = '', exclude = '') => ), }); +export const fetchRouteGroupList = () => request(`/names/routegroups`); + +export const fetchRouteGroupItem = (gid: string) => { + return request(`/routegroups/${gid}`).then((data) => { + return { + route_group_name: data.name, + route_group_id: data.id, + }; + }); +}; + export const fetchUpstreamList = () => request(`/names/upstreams`); export const fetchUpstreamItem = (sid: string) => { diff --git a/src/pages/Route/transform.ts b/src/pages/Route/transform.ts index dae7d91..e3c2b4e 100644 --- a/src/pages/Route/transform.ts +++ b/src/pages/Route/transform.ts @@ -155,10 +155,23 @@ export const transformUpstreamNodes = ( }; export const transformRouteData = (data: RouteModule.Body) => { - const { name, desc, methods, uris, protocols, hosts, vars, redirect } = data; + const { + name, + route_group_id, + route_group_name, + desc, + methods, + uris, + protocols, + hosts, + vars, + redirect, + } = data; const step1Data: Partial<RouteModule.Step1Data> = { name, + route_group_id, + route_group_name, desc, protocols: protocols.filter((item) => item !== 'websocket'), websocket: protocols.includes('websocket'), diff --git a/src/pages/Route/typing.d.ts b/src/pages/Route/typing.d.ts index 9fc25dc..409f4cc 100644 --- a/src/pages/Route/typing.d.ts +++ b/src/pages/Route/typing.d.ts @@ -51,6 +51,8 @@ declare namespace RouteModule { redirectURI?: string; redirectCode?: number; advancedMatchingRules: MatchingRule[]; + route_group_id?: string; + route_group_name: string; }; type Step3Data = { @@ -113,6 +115,8 @@ declare namespace RouteModule { // Request Body or Response Data for API type Body = { id?: number; + route_group_id?: string; + route_group_name: string; name: string; desc: string; priority?: number; diff --git a/src/pages/RouteGroup/Create.tsx b/src/pages/RouteGroup/Create.tsx new file mode 100644 index 0000000..80213a2 --- /dev/null +++ b/src/pages/RouteGroup/Create.tsx @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect, useState } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Card, Form, notification, Steps } from 'antd'; + +import ActionBar from '@/components/ActionBar'; +import { history, useIntl } from 'umi'; + +import Step1 from './components/Step1'; +import Preview from './components/Preview'; +import { create, fetchOne, update } from './service'; + +const Page: React.FC = (props) => { + const [step, setStep] = useState(1); + const [form1] = Form.useForm(); + const { formatMessage } = useIntl(); + + useEffect(() => { + const { gid } = (props as any).match.params; + + if (gid) { + fetchOne(gid).then((data) => { + form1.setFieldsValue(data); + }); + } + }, []); + + const onSubmit = () => { + const data = { ...form1.getFieldsValue() } as RouteGroupModule.RouteGroupEntity; + const { gid } = (props as any).match.params; + (gid ? update(gid, data) : create(data)).then(() => { + notification.success({ + message: `${ + gid + ? formatMessage({ id: 'routegroup.create.edit' }) + : formatMessage({ id: 'routegroup.create.create' }) + } ${formatMessage({ id: 'routegroup.create.routegroup.successfully' })}`, + }); + history.replace('/routegroup/list'); + }); + }; + + const onStepChange = (nextStep: number) => { + if (step === 1) { + form1.validateFields().then(() => { + setStep(nextStep); + }); + } else if (nextStep === 3) { + onSubmit(); + } else { + setStep(nextStep); + } + }; + + return ( + <> + <PageContainer title={formatMessage({ id: 'routegroup.create.create' })}> + <Card bordered={false}> + <Steps current={step - 1} style={{ marginBottom: 30 }}> + <Steps.Step title={formatMessage({ id: 'routegroup.create.basic.info' })} /> + <Steps.Step title={formatMessage({ id: 'routegroup.create.preview' })} /> + </Steps> + + {step === 1 && <Step1 form={form1} />} + {step === 2 && <Preview form1={form1} />} + </Card> + </PageContainer> + <ActionBar step={step} lastStep={2} onChange={onStepChange} /> + </> + ); +}; + +export default Page; diff --git a/src/pages/Route/List.tsx b/src/pages/RouteGroup/List.tsx similarity index 59% copy from src/pages/Route/List.tsx copy to src/pages/RouteGroup/List.tsx index df2a899..b2da173 100644 --- a/src/pages/Route/List.tsx +++ b/src/pages/RouteGroup/List.tsx @@ -15,86 +15,63 @@ * limitations under the License. */ import React, { useRef, useState } from 'react'; -import { PageHeaderWrapper } from '@ant-design/pro-layout'; +import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table'; -import { Button, Popconfirm, notification, Tag, Input } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; -import moment from 'moment'; +import { Popconfirm, Button, notification, Input } from 'antd'; import { history, useIntl } from 'umi'; +import moment from 'moment'; import { fetchList, remove } from './service'; const Page: React.FC = () => { const ref = useRef<ActionType>(); + const [search, setSearch] = useState(''); const { formatMessage } = useIntl(); - const columns: ProColumns<RouteModule.BaseData>[] = [ + const columns: ProColumns<RouteGroupModule.RouteGroupEntity>[] = [ { - title: formatMessage({ id: 'route.list.name' }), + title: formatMessage({ id: 'routegroup.list.name' }), dataIndex: 'name', }, { - title: formatMessage({ id: 'route.list.domain.name' }), - dataIndex: 'hosts', - render: (_, record) => - record.hosts.map((host) => ( - <Tag key={host} color="geekblue"> - {host} - </Tag> - )), - }, - { - title: formatMessage({ id: 'route.list.path' }), - dataIndex: 'uri', - render: (_, record) => - record.uris.map((uri) => ( - <Tag key={uri} color="geekblue"> - {uri} - </Tag> - )), - }, - // { - // title: '优先级', - // dataIndex: 'priority', - // }, - { - title: formatMessage({ id: 'route.list.description' }), + title: formatMessage({ id: 'routegroup.list.description' }), dataIndex: 'description', }, { - title: formatMessage({ id: 'route.list.edit.time' }), + title: formatMessage({ id: 'routegroup.list.edit.time' }), dataIndex: 'update_time', render: (text) => `${moment.unix(Number(text)).format('YYYY-MM-DD HH:mm:ss')}`, }, { - title: formatMessage({ id: 'route.list.operation' }), + title: formatMessage({ id: 'routegroup.list.operation' }), valueType: 'option', render: (_, record) => ( <> <Button type="primary" - onClick={() => history.push(`/routes/${record.id}/edit`)} style={{ marginRight: 10 }} + onClick={() => history.push(`/routegroup/${record.id}/edit`)} > - {formatMessage({ id: 'route.list.edit' })} + {formatMessage({ id: 'routegroup.list.edit' })} </Button> <Popconfirm - title={formatMessage({ id: 'route.list.delete.confrim' })} + title={formatMessage({ id: 'routegroup.list.confirm.delete' })} + okText={formatMessage({ id: 'routegroup.list.confirm' })} + cancelText={formatMessage({ id: 'routegroup.list.cancel' })} onConfirm={() => { remove(record.id!).then(() => { notification.success({ - message: formatMessage({ id: 'route.list.delete.success' }), + message: formatMessage({ id: 'routegroup.list.delete.successfully' }), }); /* eslint-disable no-unused-expressions */ ref.current?.reload(); }); }} - okText={formatMessage({ id: 'route.list.confirm' })} - cancelText={formatMessage({ id: 'route.list.cancel' })} > <Button type="primary" danger> - {formatMessage({ id: 'route.list.delete' })} + {formatMessage({ id: 'routegroup.list.delete' })} </Button> </Popconfirm> </> @@ -103,29 +80,29 @@ const Page: React.FC = () => { ]; return ( - <PageHeaderWrapper title={formatMessage({ id: 'route.list' })}> - <ProTable<RouteModule.BaseData> + <PageContainer title={formatMessage({ id: 'routegroup.list' })}> + <ProTable<RouteGroupModule.RouteGroupEntity> actionRef={ref} - rowKey="name" columns={columns} + rowKey="id" search={false} request={(params) => fetchList(params, search)} toolBarRender={(action) => [ <Input.Search - placeholder={formatMessage({ id: 'route.list.input' })} + placeholder={formatMessage({ id: 'routegroup.list.input' })} onSearch={(value) => { setSearch(value); action.setPageInfo({ page: 1 }); action.reload(); }} />, - <Button type="primary" onClick={() => history.push('/routes/create')}> + <Button type="primary" onClick={() => history.push('/routegroup/create')}> <PlusOutlined /> - {formatMessage({ id: 'route.list.create' })} + {formatMessage({ id: 'routegroup.list.create' })} </Button>, ]} /> - </PageHeaderWrapper> + </PageContainer> ); }; diff --git a/src/pages/RouteGroup/components/Preview.tsx b/src/pages/RouteGroup/components/Preview.tsx new file mode 100644 index 0000000..92d436f --- /dev/null +++ b/src/pages/RouteGroup/components/Preview.tsx @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { FormInstance } from 'antd/lib/form'; + +import Step1 from './Step1'; + +type Props = { + form1: FormInstance; +}; + +const Page: React.FC<Props> = ({ form1 }) => <Step1 form={form1} disabled />; + +export default Page; diff --git a/src/pages/Route/components/Step1/MetaView.tsx b/src/pages/RouteGroup/components/Step1.tsx similarity index 53% copy from src/pages/Route/components/Step1/MetaView.tsx copy to src/pages/RouteGroup/components/Step1.tsx index b04a5ac..59f5dc4 100644 --- a/src/pages/Route/components/Step1/MetaView.tsx +++ b/src/pages/RouteGroup/components/Step1.tsx @@ -15,42 +15,45 @@ * limitations under the License. */ import React from 'react'; -import Form from 'antd/es/form'; -import { Input } from 'antd'; +import { Form, Input } from 'antd'; +import { FormInstance } from 'antd/lib/form'; import { useIntl } from 'umi'; -import { PanelSection } from '@api7-dashboard/ui'; -interface Props extends RouteModule.Data {} +import { FORM_ITEM_LAYOUT } from '@/pages/Upstream/constants'; -const MetaView: React.FC<Props> = ({ disabled }) => { +type Props = { + form: FormInstance; + disabled?: boolean; +}; + +const initialValues = { + name: '', + description: '', +}; + +const Step1: React.FC<Props> = ({ form, disabled }) => { const { formatMessage } = useIntl(); return ( - <PanelSection title={formatMessage({ id: 'route.meta.name.description' })}> + <Form {...FORM_ITEM_LAYOUT} form={form} initialValues={initialValues}> <Form.Item - label={formatMessage({ id: 'route.meta.api.name' })} + label={formatMessage({ id: 'routegroup.step.name' })} name="name" - rules={[ - { required: true, message: formatMessage({ id: 'route.meta.input.api.name' }) }, - { - pattern: new RegExp(/^[a-zA-Z][a-zA-Z0-9_-]{0,100}$/, 'g'), - message: formatMessage({ id: 'route.meta.api.name.rule' }), - }, - ]} - extra={formatMessage({ id: 'rotue.meta.api.rule' })} + rules={[{ required: true }]} + extra={formatMessage({ id: 'routegroup.step.name.should.unique' })} > <Input - placeholder={formatMessage({ id: 'route.meta.input.api.name' })} + placeholder={formatMessage({ id: 'routegroup.step.input.routegroup.name' })} disabled={disabled} /> </Form.Item> - <Form.Item label={formatMessage({ id: 'route.meta.description' })} name="desc"> + <Form.Item label={formatMessage({ id: 'routegroup.step.description' })} name="description"> <Input.TextArea - placeholder={formatMessage({ id: 'route.meta.description.rule' })} + placeholder={formatMessage({ id: 'routegroup.step.input.description' })} disabled={disabled} /> </Form.Item> - </PanelSection> + </Form> ); }; -export default MetaView; +export default Step1; diff --git a/src/pages/RouteGroup/constants.ts b/src/pages/RouteGroup/constants.ts new file mode 100644 index 0000000..25bb12f --- /dev/null +++ b/src/pages/RouteGroup/constants.ts @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const FORM_ITEM_LAYOUT = { + labelCol: { + span: 6, + }, + wrapperCol: { + span: 18, + }, +}; + +export const FORM_ITEM_WITHOUT_LABEL = { + wrapperCol: { + xs: { span: 24, offset: 0 }, + sm: { span: 20, offset: 6 }, + }, +}; diff --git a/src/pages/RouteGroup/index.ts b/src/pages/RouteGroup/index.ts new file mode 100644 index 0000000..7120a79 --- /dev/null +++ b/src/pages/RouteGroup/index.ts @@ -0,0 +1,18 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as RouteGroupCN } from './locales/zh-CN'; +export { default as RouteGroupUS } from './locales/en-US'; diff --git a/src/pages/RouteGroup/locales/en-US.ts b/src/pages/RouteGroup/locales/en-US.ts new file mode 100644 index 0000000..318809d --- /dev/null +++ b/src/pages/RouteGroup/locales/en-US.ts @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export default { + 'routegroup.step.create': 'Create', + 'routegroup.step.name': 'Name', + 'routegroup.step.name.should.unique': 'Name should be unique', + 'routegroup.step.input.routegroup.name': 'Please input routegroup name', + 'routegroup.step.description': 'Description', + 'routegroup.step.input.description': 'Please input description', + + 'routegroup.create.edit': 'Edit', + 'routegroup.create.create': 'Create', + 'routegroup.create.routegroup.successfully': 'routegroup successfully', + 'routegroup.create.basic.info': 'Basic Information', + 'routegroup.create.preview': 'Preview', + + 'routegroup.list.name': 'Name', + 'routegroup.list.type': 'Type', + 'routegroup.list.description': 'Description', + 'routegroup.list.edit.time': 'Edit Time', + 'routegroup.list.operation': 'Operation', + 'routegroup.list.edit': 'Edit', + 'routegroup.list.confirm.delete': 'Are you sure to delete ?', + 'routegroup.list.confirm': 'Confirm', + 'routegroup.list.cancel': 'Cancel', + 'routegroup.list.delete.successfully': 'Delete successfully', + 'routegroup.list.delete': 'Delete', + 'routegroup.list': 'routegroup List', + 'routegroup.list.input': 'Please input', + 'routegroup.list.create': 'Create', +}; diff --git a/src/pages/RouteGroup/locales/zh-CN.ts b/src/pages/RouteGroup/locales/zh-CN.ts new file mode 100644 index 0000000..9797ae4 --- /dev/null +++ b/src/pages/RouteGroup/locales/zh-CN.ts @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export default { + 'routegroup.step.create': '创建', + 'routegroup.step.name': '名称', + 'routegroup.step.name.should.unique': '名称需全局唯一', + 'routegroup.step.input.routegroup.name': '请输入分组名称', + 'routegroup.step.description': '描述', + 'routegroup.step.input.description': '请输入描述', + + 'routegroup.create.edit': '编辑', + 'routegroup.create.create': '创建', + 'routegroup.create.routegroup.successfully': '分组成功', + 'routegroup.create.basic.info': '基础信息', + 'routegroup.create.preview': '预览', + + 'routegroup.list.name': '名称', + 'routegroup.list.type': '类型', + 'routegroup.list.description': '描述', + 'routegroup.list.edit.time': '编辑时间', + 'routegroup.list.operation': '操作', + 'routegroup.list.edit': '编辑', + 'routegroup.list.confirm.delete': '确定删除该条记录吗?', + 'routegroup.list.confirm': '确定', + 'routegroup.list.cancel': '取消', + 'routegroup.list.delete.successfully': '删除记录成功', + 'routegroup.list.delete': '删除', + 'routegroup.list': '分组列表', + 'routegroup.list.input': '请输入', + 'routegroup.list.create': '创建', +}; diff --git a/src/pages/RouteGroup/service.ts b/src/pages/RouteGroup/service.ts new file mode 100644 index 0000000..8504e4b --- /dev/null +++ b/src/pages/RouteGroup/service.ts @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { request } from 'umi'; + +export const fetchList = ({ current = 1, pageSize = 10 }, search: string) => + request('/routegroups', { + params: { + page: current, + size: pageSize, + search, + }, + }).then(({ data, count }) => ({ + data, + total: count, + })); + +export const fetchOne = (id: string) => + request<RouteGroupModule.RouteGroupEntity>(`/routegroups/${id}`); + +export const create = (data: RouteGroupModule.RouteGroupEntity) => + request('/routegroups', { + method: 'POST', + data, + }); + +export const update = (id: string, data: RouteGroupModule.RouteGroupEntity) => + request(`/routegroups/${id}`, { + method: 'PUT', + data, + }); + +export const remove = (id: string) => request(`/routegroups/${id}`, { method: 'DELETE' }); diff --git a/src/pages/RouteGroup/typing.d.ts b/src/pages/RouteGroup/typing.d.ts new file mode 100644 index 0000000..313272a --- /dev/null +++ b/src/pages/RouteGroup/typing.d.ts @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +declare namespace RouteGroupModule { + type RouteGroupEntity = { + id: string; + name: string; + description: string; + }; +}