You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/05/30 09:42:24 UTC
[incubator-apisix-dashboard] branch next updated: feature: new
settings page (#217)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch next
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
The following commit(s) were added to refs/heads/next by this push:
new d5a4372 feature: new settings page (#217)
d5a4372 is described below
commit d5a4372269e7b69c5c025c98291821389e8a3ae1
Author: bzp2010 <bz...@gmail.com>
AuthorDate: Sat May 30 17:42:18 2020 +0800
feature: new settings page (#217)
* feature: move the settings page
* feature: change logout button to settings
* fix: clean code
---
config/routes.ts | 18 +--
src/app.tsx | 8 +-
src/components/RightContent/AvatarDropdown.tsx | 31 ++---
src/locales/en-US/settings.ts | 1 +
src/locales/zh-CN/settings.ts | 1 +
src/pages/Settings/Settings.tsx | 121 ------------------
src/pages/Settings/typing.d.ts | 9 --
src/pages/settings/Settings.tsx | 129 +++++++++++++++++++
src/pages/{Settings => settings}/index.ts | 0
src/pages/{Settings => settings}/service.ts | 0
src/pages/{user/login => settings}/style.less | 10 ++
.../user/login/components/Login/LoginContext.tsx | 13 --
.../user/login/components/Login/LoginItem.tsx | 107 ----------------
.../user/login/components/Login/LoginSubmit.tsx | 23 ----
src/pages/user/login/components/Login/LoginTab.tsx | 44 -------
src/pages/user/login/components/Login/index.less | 49 -------
src/pages/user/login/components/Login/index.tsx | 114 -----------------
src/pages/user/login/components/Login/map.tsx | 72 -----------
src/pages/user/login/index.tsx | 142 ---------------------
src/services/API.d.ts | 1 -
src/services/user.ts | 20 +--
21 files changed, 173 insertions(+), 740 deletions(-)
diff --git a/config/routes.ts b/config/routes.ts
index 79aa5ee..67c1a72 100644
--- a/config/routes.ts
+++ b/config/routes.ts
@@ -1,24 +1,14 @@
const routes = [
{
- path: '/user',
- layout: false,
- routes: [
- {
- name: 'login',
- path: '/user/login',
- component: './user/login',
- },
- ],
- },
- {
path: '/',
- redirect: '/settings',
+ redirect: '/ssl',
},
{
name: 'settings',
path: '/settings',
- icon: 'setting',
- component: './Settings',
+ component: './settings',
+ layout: false,
+ hideInMenu: true,
},
{
name: 'ssl',
diff --git a/src/app.tsx b/src/app.tsx
index 9971333..6336e81 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -3,7 +3,7 @@ import { notification } from 'antd';
import { RequestConfig, history } from 'umi';
import { BasicLayoutProps, Settings as LayoutSettings } from '@ant-design/pro-layout';
-import { getAdminAPIConfig } from '@/pages/Settings';
+import { getAdminAPIConfig } from '@/pages/settings';
import RightContent from '@/components/RightContent';
import Footer from '@/components/Footer';
import { queryCurrent } from '@/services/user';
@@ -13,8 +13,8 @@ export async function getInitialState(): Promise<{
currentUser?: API.CurrentUser;
settings?: LayoutSettings;
}> {
- // 如果是登录页面,不执行
- if (history.location.pathname !== '/user/login') {
+ // 如果是设置页面,不执行
+ if (history.location.pathname !== '/settings') {
try {
const currentUser = await queryCurrent();
return {
@@ -22,7 +22,7 @@ export async function getInitialState(): Promise<{
settings: defaultSettings,
};
} catch (error) {
- history.push('/user/login');
+ history.push('/settings');
}
}
return {
diff --git a/src/components/RightContent/AvatarDropdown.tsx b/src/components/RightContent/AvatarDropdown.tsx
index 5555355..9f3a674 100644
--- a/src/components/RightContent/AvatarDropdown.tsx
+++ b/src/components/RightContent/AvatarDropdown.tsx
@@ -1,9 +1,8 @@
import React, { useCallback } from 'react';
-import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
+import { SettingOutlined, UserOutlined, SettingFilled } from '@ant-design/icons';
import { Avatar, Menu, Spin } from 'antd';
import { ClickParam } from 'antd/es/menu';
import { history, useModel } from 'umi';
-import { getPageQuery } from '@/utils/utils';
import { stringify } from 'querystring';
import HeaderDropdown from '../HeaderDropdown';
@@ -16,17 +15,13 @@ export interface GlobalHeaderRightProps {
/**
* 退出登录,并且将当前的 url 保存
*/
-const loginOut = async () => {
- const { redirect } = getPageQuery();
- // Note: There may be security issues, please note
- if (window.location.pathname !== '/user/login' && !redirect) {
- history.replace({
- pathname: '/user/login',
- search: stringify({
- redirect: window.location.href,
- }),
- });
- }
+const settings = async () => {
+ history.replace({
+ pathname: '/settings',
+ search: stringify({
+ redirect: window.location.href,
+ }),
+ });
};
const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
@@ -34,9 +29,9 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
const onMenuClick = useCallback((event: ClickParam) => {
const { key } = event;
- if (key === 'logout') {
+ if (key === 'settings') {
setInitialState({ ...initialState, currentUser: undefined });
- loginOut();
+ settings();
return;
}
history.push(`/account/${key}`);
@@ -80,9 +75,9 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
)}
{menu && <Menu.Divider />}
- <Menu.Item key="logout">
- <LogoutOutlined />
- 退出登录
+ <Menu.Item key="settings">
+ <SettingFilled />
+ 修改设置
</Menu.Item>
</Menu>
);
diff --git a/src/locales/en-US/settings.ts b/src/locales/en-US/settings.ts
index fe2cff7..0c7081c 100644
--- a/src/locales/en-US/settings.ts
+++ b/src/locales/en-US/settings.ts
@@ -1,4 +1,5 @@
export default {
+ 'app.settings.admin-api': 'Admin API Config',
'app.settings.item.admin-api-schema': 'Admin API Schema',
'app.settings.item.admin-api-host': 'Admin API Host',
'app.settings.item.admin-api-path': 'Admin API Path',
diff --git a/src/locales/zh-CN/settings.ts b/src/locales/zh-CN/settings.ts
index 03bc5f8..34d2fa2 100644
--- a/src/locales/zh-CN/settings.ts
+++ b/src/locales/zh-CN/settings.ts
@@ -1,4 +1,5 @@
export default {
+ 'app.settings.admin-api': '管理 API 配置',
'app.settings.item.admin-api-schema': '管理 API 协议',
'app.settings.item.admin-api-host': '管理 API 地址',
'app.settings.item.admin-api-path': '管理 API 路径',
diff --git a/src/pages/Settings/Settings.tsx b/src/pages/Settings/Settings.tsx
deleted file mode 100644
index 4589c79..0000000
--- a/src/pages/Settings/Settings.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import React, { useEffect } from 'react';
-import { useForm } from 'antd/es/form/util';
-import { Button, Card, Form, Input, notification, Select } from 'antd';
-import { useIntl, FormattedMessage, history } from 'umi';
-import { PageHeaderWrapper } from '@ant-design/pro-layout';
-
-import { getAdminAPIConfig } from './service';
-
-const { Option } = Select;
-
-const layout = {
- labelCol: {
- span: 4,
- },
- wrapperCol: {
- span: 8,
- },
-};
-
-const tailLayout = {
- wrapperCol: {
- offset: 4,
- },
-};
-
-const Settings: React.FC = () => {
- const [form] = useForm();
- const { formatMessage } = useIntl();
-
- useEffect(() => {
- const adminAPIConfig = getAdminAPIConfig();
- form.setFieldsValue({
- adminAPISchema: adminAPIConfig.schema,
- adminAPIHost: adminAPIConfig.host,
- adminAPIPath: adminAPIConfig.path,
- adminAPIKey: adminAPIConfig.key,
- });
- }, []);
-
- const onFinish = (values: any) => {
- localStorage.setItem('GLOBAL_ADMIN_API_SCHEMA', values.adminAPISchema);
- localStorage.setItem('GLOBAL_ADMIN_API_HOST', values.adminAPIHost);
- localStorage.setItem('GLOBAL_ADMIN_API_PATH', values.adminAPIPath);
- localStorage.setItem('GLOBAL_ADMIN_API_KEY', values.adminAPIKey);
-
- notification.success({
- message: `${formatMessage({ id: 'component.global.update' })} Admin API ${formatMessage({
- id: 'component.status.success',
- }).toLowerCase()}`,
- });
- };
-
- return (
- <PageHeaderWrapper>
- <Card>
- <Form {...layout} form={form} onFinish={onFinish}>
- <Form.Item
- label={formatMessage({ id: 'app.settings.item.admin-api-schema' })}
- name="adminAPISchema"
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'app.settings.description.invalid-admin-api-schema' }),
- },
- ]}
- >
- <Select>
- <Option value="http">HTTP</Option>
- <Option value="https">HTTPS</Option>
- </Select>
- </Form.Item>
-
- <Form.Item
- label={formatMessage({ id: 'app.settings.item.admin-api-host' })}
- name="adminAPIHost"
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'app.settings.description.invalid-admin-api-host' }),
- },
- ]}
- >
- <Input />
- </Form.Item>
-
- <Form.Item
- label={formatMessage({ id: 'app.settings.item.admin-api-path' })}
- name="adminAPIPath"
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'app.settings.description.invalid-admin-api-path' }),
- },
- ]}
- >
- <Input />
- </Form.Item>
-
- <Form.Item
- label={formatMessage({ id: 'app.settings.item.admin-api-key' })}
- name="adminAPIKey"
- >
- <Input />
- </Form.Item>
-
- <Form.Item {...tailLayout}>
- <Button style={{ marginRight: 10 }} onClick={() => history.goBack()}>
- <FormattedMessage id="component.global.cancel" />
- </Button>
-
- <Button htmlType="submit" type="primary">
- <FormattedMessage id="component.global.save" />
- </Button>
- </Form.Item>
- </Form>
- </Card>
- </PageHeaderWrapper>
- );
-};
-
-export default Settings;
diff --git a/src/pages/Settings/typing.d.ts b/src/pages/Settings/typing.d.ts
deleted file mode 100644
index e846281..0000000
--- a/src/pages/Settings/typing.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-declare namespace SettingModule {
- interface AdminAPIConfig {
- schema: string;
- host: string;
- path: string;
- prefix: string;
- key: string;
- }
-}
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
new file mode 100644
index 0000000..5405c98
--- /dev/null
+++ b/src/pages/settings/Settings.tsx
@@ -0,0 +1,129 @@
+import { Button, Form, Input, notification, Select, Tabs } from 'antd';
+import React, { useEffect } from 'react';
+import { Link, SelectLang, FormattedMessage, useIntl } from 'umi';
+import logo from '@/assets/logo.svg';
+import styles from './style.less';
+import { useForm } from 'antd/es/form/util';
+import { getAdminAPIConfig } from './service';
+
+const { Option } = Select;
+const { TabPane } = Tabs;
+
+const Settings: React.FC<{}> = () => {
+ const [form] = useForm();
+ const { formatMessage } = useIntl();
+
+ useEffect(() => {
+ const adminAPIConfig = getAdminAPIConfig();
+ form.setFieldsValue({
+ adminAPISchema: adminAPIConfig.schema,
+ adminAPIHost: adminAPIConfig.host,
+ adminAPIPath: adminAPIConfig.path,
+ adminAPIKey: adminAPIConfig.key,
+ });
+ }, []);
+
+ const onFinish = (values: any) => {
+ localStorage.setItem('GLOBAL_ADMIN_API_SCHEMA', values.adminAPISchema);
+ localStorage.setItem('GLOBAL_ADMIN_API_HOST', values.adminAPIHost);
+ localStorage.setItem('GLOBAL_ADMIN_API_PATH', values.adminAPIPath);
+ localStorage.setItem('GLOBAL_ADMIN_API_KEY', values.adminAPIKey);
+
+ notification.success({
+ duration: 1,
+ message: `${formatMessage({ id: 'component.global.update' })} Admin API ${formatMessage({
+ id: 'component.status.success',
+ }).toLowerCase()}`,
+ onClose: () => {
+ document.location.href = '/';
+ },
+ });
+ };
+
+ return (
+ <div className={styles.container}>
+ <div className={styles.lang}>
+ <SelectLang />
+ </div>
+ <div className={styles.content}>
+ <div className={styles.top}>
+ <div className={styles.header}>
+ <Link to="/">
+ <img alt="logo" className={styles.logo} src={logo} />
+ <span className={styles.title}>APISIX Dashboard</span>
+ </Link>
+ </div>
+ <div className={styles.desc}>Cloud-Native Microservices API Gateway</div>
+ </div>
+
+ <div className={styles.main}>
+ <Tabs>
+ <TabPane tab={formatMessage({ id: 'app.settings.admin-api' })}>
+ <Form form={form} onFinish={onFinish}>
+ <Form.Item
+ name="adminAPISchema"
+ rules={[
+ {
+ required: true,
+ message: formatMessage({
+ id: 'app.settings.description.invalid-admin-api-schema',
+ }),
+ },
+ ]}
+ >
+ <Select
+ placeholder={formatMessage({ id: 'app.settings.item.admin-api-schema' })}
+ allowClear
+ >
+ <Option value="http">HTTP</Option>
+ <Option value="https">HTTPS</Option>
+ </Select>
+ </Form.Item>
+
+ <Form.Item
+ name="adminAPIHost"
+ rules={[
+ {
+ required: true,
+ message: formatMessage({
+ id: 'app.settings.description.invalid-admin-api-host',
+ }),
+ },
+ ]}
+ >
+ <Input placeholder={formatMessage({ id: 'app.settings.item.admin-api-host' })} />
+ </Form.Item>
+
+ <Form.Item
+ name="adminAPIPath"
+ rules={[
+ {
+ required: true,
+ message: formatMessage({
+ id: 'app.settings.description.invalid-admin-api-path',
+ }),
+ },
+ ]}
+ >
+ <Input placeholder={formatMessage({ id: 'app.settings.item.admin-api-path' })} />
+ </Form.Item>
+
+ <Form.Item name="adminAPIKey">
+ <Input placeholder={formatMessage({ id: 'app.settings.item.admin-api-key' })} />
+ </Form.Item>
+
+ <Form.Item>
+ <Button size="large" type="primary" htmlType="submit" block>
+ <FormattedMessage id="component.global.save" />
+ </Button>
+ </Form.Item>
+ </Form>
+ </TabPane>
+ </Tabs>
+ </div>
+ </div>
+ </div>
+ );
+};
+
+export default Settings;
diff --git a/src/pages/Settings/index.ts b/src/pages/settings/index.ts
similarity index 100%
rename from src/pages/Settings/index.ts
rename to src/pages/settings/index.ts
diff --git a/src/pages/Settings/service.ts b/src/pages/settings/service.ts
similarity index 100%
rename from src/pages/Settings/service.ts
rename to src/pages/settings/service.ts
diff --git a/src/pages/user/login/style.less b/src/pages/settings/style.less
similarity index 91%
rename from src/pages/user/login/style.less
rename to src/pages/settings/style.less
index c195315..539f1ee 100644
--- a/src/pages/user/login/style.less
+++ b/src/pages/settings/style.less
@@ -1,5 +1,15 @@
@import '~antd/es/style/themes/default.less';
+.login {
+ :global {
+ .ant-tabs .ant-tabs-bar {
+ margin-bottom: 24px;
+ text-align: center;
+ border-bottom: 0;
+ }
+ }
+}
+
.container {
display: flex;
flex-direction: column;
diff --git a/src/pages/user/login/components/Login/LoginContext.tsx b/src/pages/user/login/components/Login/LoginContext.tsx
deleted file mode 100644
index ae571e0..0000000
--- a/src/pages/user/login/components/Login/LoginContext.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { createContext } from 'react';
-
-export interface LoginContextProps {
- tabUtil?: {
- addTab: (id: string) => void;
- removeTab: (id: string) => void;
- };
- updateActive?: (activeItem: { [key: string]: string } | string) => void;
-}
-
-const LoginContext: React.Context<LoginContextProps> = createContext({});
-
-export default LoginContext;
diff --git a/src/pages/user/login/components/Login/LoginItem.tsx b/src/pages/user/login/components/Login/LoginItem.tsx
deleted file mode 100644
index 9473240..0000000
--- a/src/pages/user/login/components/Login/LoginItem.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Input, Form } from 'antd';
-import React from 'react';
-import { FormItemProps } from 'antd/es/form/FormItem';
-
-import ItemMap from './map';
-import LoginContext, { LoginContextProps } from './LoginContext';
-
-export type WrappedLoginItemProps = LoginItemProps;
-export type LoginItemKeyType = keyof typeof ItemMap;
-export interface LoginItemType {
- UserName: React.FC<WrappedLoginItemProps>;
- Password: React.FC<WrappedLoginItemProps>;
- Mobile: React.FC<WrappedLoginItemProps>;
- Captcha: React.FC<WrappedLoginItemProps>;
-}
-
-export interface LoginItemProps extends Partial<FormItemProps> {
- name?: string;
- style?: React.CSSProperties;
- placeholder?: string;
- buttonText?: React.ReactNode;
- countDown?: number;
- getCaptchaButtonText?: string;
- getCaptchaSecondText?: string;
- updateActive?: LoginContextProps['updateActive'];
- type?: string;
- defaultValue?: string;
- customProps?: { [key: string]: unknown };
- onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
- tabUtil?: LoginContextProps['tabUtil'];
-}
-
-const FormItem = Form.Item;
-
-const getFormItemOptions = ({
- onChange,
- defaultValue,
- customProps = {},
- rules,
-}: LoginItemProps) => {
- const options: {
- rules?: LoginItemProps['rules'];
- onChange?: LoginItemProps['onChange'];
- initialValue?: LoginItemProps['defaultValue'];
- } = {
- rules: rules || (customProps.rules as LoginItemProps['rules']),
- };
- if (onChange) {
- options.onChange = onChange;
- }
- if (defaultValue) {
- options.initialValue = defaultValue;
- }
- return options;
-};
-
-const LoginItem: React.FC<LoginItemProps> = (props) => {
- // 这么写是为了防止restProps中 带入 onChange, defaultValue, rules props tabUtil
- const {
- onChange,
- customProps,
- defaultValue,
- rules,
- name,
- getCaptchaButtonText,
- getCaptchaSecondText,
- updateActive,
- type,
- tabUtil,
- ...restProps
- } = props;
-
- if (!name) {
- return null;
- }
- // get getFieldDecorator props
- const options = getFormItemOptions(props);
- const otherProps = restProps || {};
-
- return (
- <FormItem name={name} {...options}>
- <Input {...customProps} {...otherProps} />
- </FormItem>
- );
-};
-
-const LoginItems: Partial<LoginItemType> = {};
-
-Object.keys(ItemMap).forEach((key) => {
- const item = ItemMap[key];
- LoginItems[key] = (props: LoginItemProps) => (
- <LoginContext.Consumer>
- {(context) => (
- <LoginItem
- customProps={item.props}
- rules={item.rules}
- {...props}
- type={key}
- {...context}
- updateActive={context.updateActive}
- />
- )}
- </LoginContext.Consumer>
- );
-});
-
-export default LoginItems as LoginItemType;
diff --git a/src/pages/user/login/components/Login/LoginSubmit.tsx b/src/pages/user/login/components/Login/LoginSubmit.tsx
deleted file mode 100644
index 280fb0f..0000000
--- a/src/pages/user/login/components/Login/LoginSubmit.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Button, Form } from 'antd';
-
-import { ButtonProps } from 'antd/es/button';
-import React from 'react';
-import classNames from 'classnames';
-import styles from './index.less';
-
-const FormItem = Form.Item;
-
-interface LoginSubmitProps extends ButtonProps {
- className?: string;
-}
-
-const LoginSubmit: React.FC<LoginSubmitProps> = ({ className, ...rest }) => {
- const clsString = classNames(styles.submit, className);
- return (
- <FormItem>
- <Button size="large" className={clsString} type="primary" htmlType="submit" {...rest} />
- </FormItem>
- );
-};
-
-export default LoginSubmit;
diff --git a/src/pages/user/login/components/Login/LoginTab.tsx b/src/pages/user/login/components/Login/LoginTab.tsx
deleted file mode 100644
index adbed2a..0000000
--- a/src/pages/user/login/components/Login/LoginTab.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { useEffect } from 'react';
-import { TabPaneProps } from 'antd/es/tabs';
-import { Tabs } from 'antd';
-import LoginContext, { LoginContextProps } from './LoginContext';
-
-const { TabPane } = Tabs;
-
-const generateId = (() => {
- let i = 0;
- return (prefix = '') => {
- i += 1;
- return `${prefix}${i}`;
- };
-})();
-
-interface LoginTabProps extends TabPaneProps {
- tabUtil: LoginContextProps['tabUtil'];
- active?: boolean;
-}
-
-const LoginTab: React.FC<LoginTabProps> = (props) => {
- useEffect(() => {
- const uniqueId = generateId('login-tab-');
- const { tabUtil } = props;
- if (tabUtil) {
- tabUtil.addTab(uniqueId);
- }
- }, []);
- const { children } = props;
- return <TabPane {...props}>{props.active && children}</TabPane>;
-};
-
-const WrapContext: React.FC<TabPaneProps> & {
- typeName: string;
-} = (props) => (
- <LoginContext.Consumer>
- {(value) => <LoginTab tabUtil={value.tabUtil} {...props} />}
- </LoginContext.Consumer>
-);
-
-// 标志位 用来判断是不是自定义组件
-WrapContext.typeName = 'LoginTab';
-
-export default WrapContext;
diff --git a/src/pages/user/login/components/Login/index.less b/src/pages/user/login/components/Login/index.less
deleted file mode 100644
index dc16aab..0000000
--- a/src/pages/user/login/components/Login/index.less
+++ /dev/null
@@ -1,49 +0,0 @@
-@import '~antd/es/style/themes/default.less';
-
-.login {
- :global {
- .ant-tabs .ant-tabs-bar {
- margin-bottom: 24px;
- text-align: center;
- border-bottom: 0;
- }
- }
-
- .getCaptcha {
- display: block;
- width: 100%;
- }
-
- .icon {
- margin-left: 16px;
- color: rgba(0, 0, 0, 0.2);
- font-size: 24px;
- vertical-align: middle;
- cursor: pointer;
- transition: color 0.3s;
-
- &:hover {
- color: @primary-color;
- }
- }
-
- .other {
- margin-top: 24px;
- line-height: 22px;
- text-align: left;
-
- .register {
- float: right;
- }
- }
-
- .prefixIcon {
- color: @disabled-color;
- font-size: @font-size-base;
- }
-
- .submit {
- width: 100%;
- margin-top: 24px;
- }
-}
diff --git a/src/pages/user/login/components/Login/index.tsx b/src/pages/user/login/components/Login/index.tsx
deleted file mode 100644
index 8c550e3..0000000
--- a/src/pages/user/login/components/Login/index.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { Tabs, Form } from 'antd';
-import React, { useState } from 'react';
-import useMergeValue from 'use-merge-value';
-import classNames from 'classnames';
-import { FormInstance } from 'antd/es/form';
-import { LoginParamsType } from '@/services/login';
-
-import LoginContext from './LoginContext';
-import LoginItem, { LoginItemProps } from './LoginItem';
-import LoginSubmit from './LoginSubmit';
-import LoginTab from './LoginTab';
-import styles from './index.less';
-
-export interface LoginProps {
- activeKey?: string;
- onTabChange?: (key: string) => void;
- style?: React.CSSProperties;
- onSubmit?: (values: LoginParamsType) => void;
- className?: string;
- from?: FormInstance;
- children: React.ReactElement<typeof LoginTab>[];
-}
-
-interface LoginType extends React.FC<LoginProps> {
- Tab: typeof LoginTab;
- Submit: typeof LoginSubmit;
- UserName: React.FunctionComponent<LoginItemProps>;
- Password: React.FunctionComponent<LoginItemProps>;
-}
-
-const Login: LoginType = (props) => {
- const { className } = props;
- const [tabs, setTabs] = useState<string[]>([]);
- const [active, setActive] = useState({});
- const [type, setType] = useMergeValue('', {
- value: props.activeKey,
- onChange: props.onTabChange,
- });
- const TabChildren: React.ReactComponentElement<typeof LoginTab>[] = [];
- const otherChildren: React.ReactElement<unknown>[] = [];
- React.Children.forEach(
- props.children,
- (child: React.ReactComponentElement<typeof LoginTab> | React.ReactElement<unknown>) => {
- if (!child) {
- return;
- }
- if ((child.type as { typeName: string }).typeName === 'LoginTab') {
- TabChildren.push(child as React.ReactComponentElement<typeof LoginTab>);
- } else {
- otherChildren.push(child);
- }
- },
- );
- return (
- <LoginContext.Provider
- value={{
- tabUtil: {
- addTab: (id) => {
- setTabs([...tabs, id]);
- },
- removeTab: (id) => {
- setTabs(tabs.filter((currentId) => currentId !== id));
- },
- },
- updateActive: (activeItem) => {
- if (!active) return;
- if (active[type]) {
- active[type].push(activeItem);
- } else {
- active[type] = [activeItem];
- }
- setActive(active);
- },
- }}
- >
- <div className={classNames(className, styles.login)}>
- <Form
- form={props.from}
- onFinish={(values) => {
- if (props.onSubmit) {
- props.onSubmit(values as LoginParamsType);
- }
- }}
- >
- {tabs.length ? (
- <React.Fragment>
- <Tabs
- animated={false}
- className={styles.tabs}
- activeKey={type}
- onChange={(activeKey) => {
- setType(activeKey);
- }}
- >
- {TabChildren}
- </Tabs>
- {otherChildren}
- </React.Fragment>
- ) : (
- props.children
- )}
- </Form>
- </div>
- </LoginContext.Provider>
- );
-};
-
-Login.Tab = LoginTab;
-Login.Submit = LoginSubmit;
-
-Login.UserName = LoginItem.UserName;
-Login.Password = LoginItem.Password;
-
-export default Login;
diff --git a/src/pages/user/login/components/Login/map.tsx b/src/pages/user/login/components/Login/map.tsx
deleted file mode 100644
index bf2f55f..0000000
--- a/src/pages/user/login/components/Login/map.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { LockTwoTone, MailTwoTone, MobileTwoTone, UserOutlined } from '@ant-design/icons';
-import React from 'react';
-import styles from './index.less';
-
-export default {
- UserName: {
- props: {
- size: 'large',
- id: 'userName',
- prefix: (
- <UserOutlined
- style={{
- color: '#1890ff',
- }}
- className={styles.prefixIcon}
- />
- ),
- placeholder: 'admin',
- },
- rules: [
- {
- required: true,
- message: 'Please enter username!',
- },
- ],
- },
- Password: {
- props: {
- size: 'large',
- prefix: <LockTwoTone className={styles.prefixIcon} />,
- type: 'password',
- id: 'password',
- placeholder: '888888',
- },
- rules: [
- {
- required: true,
- message: 'Please enter password!',
- },
- ],
- },
- Mobile: {
- props: {
- size: 'large',
- prefix: <MobileTwoTone className={styles.prefixIcon} />,
- placeholder: 'mobile number',
- },
- rules: [
- {
- required: true,
- message: 'Please enter mobile number!',
- },
- {
- pattern: /^1\d{10}$/,
- message: 'Wrong mobile number format!',
- },
- ],
- },
- Captcha: {
- props: {
- size: 'large',
- prefix: <MailTwoTone className={styles.prefixIcon} />,
- placeholder: 'captcha',
- },
- rules: [
- {
- required: true,
- message: 'Please enter Captcha!',
- },
- ],
- },
-};
diff --git a/src/pages/user/login/index.tsx b/src/pages/user/login/index.tsx
deleted file mode 100644
index 238dee8..0000000
--- a/src/pages/user/login/index.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-import { Alert, Checkbox, message } from 'antd';
-import React, { useState } from 'react';
-import { Link, SelectLang, history, useModel } from 'umi';
-import { getPageQuery } from '@/utils/utils';
-import logo from '@/assets/logo.svg';
-import { fakeAccountLogin } from '@/services/login';
-import LoginFrom from './components/Login';
-import styles from './style.less';
-
-const { Tab, UserName, Password, Submit } = LoginFrom;
-
-const LoginMessage: React.FC<{
- content: string;
-}> = ({ content }) => (
- <Alert
- style={{
- marginBottom: 24,
- }}
- message={content}
- type="error"
- showIcon
- />
-);
-
-/**
- * 此方法会跳转到 redirect 参数所在的位置
- */
-const replaceGoto = () => {
- const urlParams = new URL(window.location.href);
- const params = getPageQuery();
- let { redirect } = params as { redirect: string };
- if (redirect) {
- const redirectUrlParams = new URL(redirect);
- if (redirectUrlParams.origin === urlParams.origin) {
- redirect = redirect.substr(urlParams.origin.length);
- if (redirect.match(/^\/.*#/)) {
- redirect = redirect.substr(redirect.indexOf('#') + 1);
- }
- } else {
- window.location.href = '/';
- return;
- }
- }
- history.replace(redirect || '/');
-};
-
-const Login: React.FC<{}> = () => {
- const [userLoginState, setUserLoginState] = useState<API.LoginStateType>({});
- const [submitting, setSubmitting] = useState(false);
-
- const { refresh } = useModel('@@initialState');
- const [autoLogin, setAutoLogin] = useState(true);
- const [type, setType] = useState<string>('account');
-
- const handleSubmit = async () => {
- setSubmitting(true);
- try {
- // 登录
- const msg = await fakeAccountLogin();
- if (msg.status === 'ok') {
- message.success('登陆成功!');
- replaceGoto();
- setTimeout(() => {
- refresh();
- }, 0);
- return;
- }
- // 如果失败去设置用户错误信息
- setUserLoginState(msg);
- } catch (error) {
- message.error('登陆失败,请重试!');
- }
- setSubmitting(false);
- };
-
- const { status, type: loginType } = userLoginState;
-
- return (
- <div className={styles.container}>
- <div className={styles.lang}>
- <SelectLang />
- </div>
- <div className={styles.content}>
- <div className={styles.top}>
- <div className={styles.header}>
- <Link to="/">
- <img alt="logo" className={styles.logo} src={logo} />
- <span className={styles.title}>APISIX Dashboard</span>
- </Link>
- </div>
- <div className={styles.desc}>Cloud-Native Microservices API Gateway</div>
- </div>
-
- <div className={styles.main}>
- <LoginFrom activeKey={type} onTabChange={setType} onSubmit={handleSubmit}>
- <Tab key="account" tab="账户密码登录">
- {status === 'error' && loginType === 'account' && !submitting && (
- <LoginMessage content="账户或密码错误" />
- )}
-
- <UserName
- name="userName"
- placeholder="请输入用户名"
- rules={[
- {
- required: true,
- message: '请输入用户名!',
- },
- ]}
- />
- <Password
- name="password"
- placeholder="请输入密码"
- rules={[
- {
- required: true,
- message: '请输入密码!',
- },
- ]}
- />
- </Tab>
- <div>
- <Checkbox checked={autoLogin} onChange={(e) => setAutoLogin(e.target.checked)}>
- 自动登录
- </Checkbox>
- <a
- style={{
- float: 'right',
- }}
- >
- 忘记密码
- </a>
- </div>
- <Submit loading={submitting}>登录</Submit>
- </LoginFrom>
- </div>
- </div>
- </div>
- );
-};
-
-export default Login;
diff --git a/src/services/API.d.ts b/src/services/API.d.ts
index d31fb9e..d66825d 100644
--- a/src/services/API.d.ts
+++ b/src/services/API.d.ts
@@ -4,7 +4,6 @@ declare namespace API {
name?: string;
userid?: string;
access?: 'user' | 'guest' | 'admin';
- unreadCount?: number;
}
export interface LoginStateType {
diff --git a/src/services/user.ts b/src/services/user.ts
index 2734525..d22fb44 100644
--- a/src/services/user.ts
+++ b/src/services/user.ts
@@ -1,19 +1,21 @@
import { request } from 'umi';
+import logo from '@/assets/logo.svg';
export async function query() {
return request<API.CurrentUser[]>('/api/users');
}
export async function queryCurrent(): Promise<API.CurrentUser> {
- return Promise.resolve({
- name: 'APISIX User',
- avatar:
- 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmlld0JveD0iMCAwIDUwMCA1MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQogIDxkZWZzPg0KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iaWQwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjI1MTE5LjgiIHkxPSIxMTA1Mi41IiB4Mj0iMjE3MjUuNyIgeTI9IjIxNTUxLjciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4wMjgwOTUsIDAsIDAsIDAuMDI4MDk1LCAtNDkyLjg2NzA5NiwgLTE0NC43Njk4MjEpIj4NCiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29 [...]
- userid: '00000001',
- notifyCount: 12,
- unreadCount: 11,
- access: 'admin',
- });
+ if (!localStorage.getItem('GLOBAL_ADMIN_API_HOST')) {
+ throw new Error('Need Settings');
+ } else {
+ return Promise.resolve({
+ name: 'APISIX User',
+ avatar: logo,
+ userid: '00000001',
+ access: 'admin',
+ });
+ }
}
export async function queryNotices(): Promise<any> {