Browse Source

诗歌列表初步

master
nili 3 years ago
parent
commit
9354c22eb9
  1. 24
      config/config.ts
  2. 2
      config/proxy.ts
  3. 54
      config/routes.ts
  4. 1
      package.json
  5. 28
      src/app.tsx
  6. 28
      src/components/AuthorSelect/index.tsx
  7. 66
      src/components/DebunceSelect/index.tsx
  8. 9
      src/components/Footer/index.tsx
  9. 14
      src/components/RightContent/index.tsx
  10. 36
      src/global.tsx
  11. 28
      src/pages/Admin.tsx
  12. 56
      src/pages/PoemDetail/index.tsx
  13. 6
      src/pages/PoemPage/index.less
  14. 138
      src/pages/PoemPage/index.tsx
  15. 89
      src/pages/TableList/components/UpdateForm.tsx
  16. 148
      src/pages/TableList/index.tsx
  17. 16
      src/pages/Welcome.tsx
  18. 140
      src/pages/user/Login/index.tsx
  19. 68
      src/services/ant-design-pro/api.ts
  20. 2
      src/services/ant-design-pro/index.ts
  21. 29
      src/services/ant-design-pro/login.ts
  22. 42
      src/services/ant-design-pro/rule.ts
  23. 15
      src/services/ant-design-pro/typings.d.ts
  24. 18
      src/services/luigi/author.ts
  25. 12
      src/services/luigi/index.ts
  26. 30
      src/services/luigi/poem.ts
  27. 88
      src/services/luigi/typings.d.ts
  28. 11
      src/services/luigi/user.ts

24
config/config.ts

@ -1,13 +1,10 @@
// https://umijs.org/config/ // https://umijs.org/config/
import { defineConfig } from 'umi'; import { defineConfig } from 'umi';
import { join } from 'path'; import { join } from 'path';
import defaultSettings from './defaultSettings'; import defaultSettings from './defaultSettings';
import proxy from './proxy'; import proxy from './proxy';
import routes from './routes'; import routes from './routes';
const { REACT_APP_ENV } = process.env; const { REACT_APP_ENV } = process.env;
export default defineConfig({ export default defineConfig({
hash: true, hash: true,
antd: {}, antd: {},
@ -16,18 +13,10 @@ export default defineConfig({
}, },
layout: { layout: {
// https://umijs.org/zh-CN/plugins/plugin-layout // https://umijs.org/zh-CN/plugins/plugin-layout
locale: true, locale: false,
siderWidth: 208, siderWidth: 208,
...defaultSettings, ...defaultSettings,
}, },
// https://umijs.org/zh-CN/plugins/plugin-locale
locale: {
// default zh-CN
default: 'zh-CN',
antd: true,
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
},
dynamicImport: { dynamicImport: {
loading: '@ant-design/pro-layout/es/PageLoading', loading: '@ant-design/pro-layout/es/PageLoading',
}, },
@ -64,11 +53,16 @@ export default defineConfig({
}, },
{ {
requestLibPath: "import { request } from 'umi'", requestLibPath: "import { request } from 'umi'",
schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', // 或者使用在线的版本
projectName: 'swagger', schemaPath: 'http://localhost:8001/api/luigi/swagger/v3/api-docs',
// schemaPath: join(__dirname, 'swaggerApi.json'),
mock: false,
projectName: 'luigi',
}, },
], ],
nodeModulesTransform: { type: 'none' }, nodeModulesTransform: {
type: 'none',
},
mfsu: {}, mfsu: {},
webpack5: {}, webpack5: {},
exportStatic: {}, exportStatic: {},

2
config/proxy.ts

@ -11,7 +11,7 @@ export default {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/** // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': { '/api/': {
// 要代理的地址 // 要代理的地址
target: 'https://preview.pro.ant.design', target: 'http://localhost:8001',
// 配置了这个可以从 http 代理到 https // 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie // 依赖 origin 的功能可能需要这个,比如 cookie
changeOrigin: true, changeOrigin: true,

54
config/routes.ts

@ -1,58 +1,26 @@
export default [ export default [
{ {
path: '/user', path: '/user',
layout: false, layout: false,
routes: [ routes: [
{ { path: '/user', routes: [{ name: '登录', path: '/user/login', component: './user/Login' }] },
path: '/user', { component: './404' },
routes: [
{
name: 'login',
path: '/user/login',
component: './user/Login',
},
], ],
}, },
{ { path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' },
component: './404',
},
],
},
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './Welcome',
},
{ {
path: '/admin', path: '/admin',
name: 'admin', name: '管理页',
icon: 'crown', icon: 'crown',
access: 'canAdmin', access: 'canAdmin',
component: './Admin', component: './Admin',
routes: [ routes: [
{ { path: '/admin/sub-page', name: '二级管理页', icon: 'smile', component: './Welcome' },
path: '/admin/sub-page', { component: './404' },
name: 'sub-page',
icon: 'smile',
component: './Welcome',
},
{
component: './404',
},
], ],
}, },
{ { name: '查询表格', icon: 'table', path: '/list', component: './TableList' },
name: 'list.table-list', { path: '/', redirect: '/welcome' },
icon: 'table', { icon: 'smile', path: '/poem', component: './PoemPage' },
path: '/list', { component: './404' },
component: './TableList',
},
{
path: '/',
redirect: '/welcome',
},
{
component: './404',
},
]; ];

1
package.json

@ -85,6 +85,7 @@
"@umijs/preset-ant-design-pro": "^1.3.0", "@umijs/preset-ant-design-pro": "^1.3.0",
"@umijs/preset-dumi": "^1.1.0", "@umijs/preset-dumi": "^1.1.0",
"@umijs/preset-react": "^1.8.17", "@umijs/preset-react": "^1.8.17",
"@umijs/preset-ui": "^2.2.9",
"@umijs/yorkie": "^2.0.5", "@umijs/yorkie": "^2.0.5",
"cross-env": "^7.0.0", "cross-env": "^7.0.0",
"cross-port-killer": "^1.3.0", "cross-port-killer": "^1.3.0",

28
src/app.tsx

@ -1,14 +1,14 @@
import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
import { SettingDrawer } from '@ant-design/pro-layout';
import { PageLoading } from '@ant-design/pro-layout';
import type { RunTimeLayoutConfig } from 'umi';
import { history, Link } from 'umi';
import RightContent from '@/components/RightContent';
import Footer from '@/components/Footer'; import Footer from '@/components/Footer';
import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; import RightContent from '@/components/RightContent';
import { BookOutlined, LinkOutlined } from '@ant-design/icons'; import { BookOutlined, LinkOutlined } from '@ant-design/icons';
import { PageLoading, SettingDrawer } from '@ant-design/pro-layout';
import { history, Link, RequestConfig } from 'umi';
import defaultSettings from '../config/defaultSettings'; import defaultSettings from '../config/defaultSettings';
import { getCurrentUsingGET } from './services/luigi/user';
import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
import type { RunTimeLayoutConfig } from 'umi';
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login'; const loginPath = '/user/login';
@ -28,7 +28,7 @@ export async function getInitialState(): Promise<{
}> { }> {
const fetchUserInfo = async () => { const fetchUserInfo = async () => {
try { try {
const msg = await queryCurrentUser(); const msg = await getCurrentUsingGET();
return msg.data; return msg.data;
} catch (error) { } catch (error) {
history.push(loginPath); history.push(loginPath);
@ -106,3 +106,15 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
...initialState?.settings, ...initialState?.settings,
}; };
}; };
export const request: RequestConfig = {
errorConfig: {
adaptor: (resData) => {
return {
...resData,
success: resData.code == 1,
errorMessage: resData.message,
};
},
},
};

28
src/components/AuthorSelect/index.tsx

@ -0,0 +1,28 @@
import { seekUsingGET } from '@/services/luigi/author';
import React from 'react';
import DebounceSelect from '../DebunceSelect';
const AuthorSelect: React.FC<{
value?: string;
onChange?: (value: string) => void;
}> = (props) => {
return (
<DebounceSelect
initOption={props.value}
fetchOptions={seekUsingGET}
onChange={(newValue) => {
if (typeof newValue == 'string') {
props.onChange(JSON.parse(newValue));
} else {
props.onChange(newValue);
}
}}
style={{
width: '100%',
}}
/>
);
};
export default AuthorSelect;

66
src/components/DebunceSelect/index.tsx

@ -0,0 +1,66 @@
import { Select, Spin } from 'antd';
import { SelectProps } from 'antd/es/select';
import debounce from 'lodash/debounce';
import React from 'react';
import { useEffect } from '../../.umi/.cache/.mfsu/mf-dep_vendors-node_modules_react_index_js.598bdd42.async';
export interface DebounceSelectProps<ValueType = any>
extends Omit<SelectProps<ValueType>, 'options' | 'children'> {
fetchOptions: (search: string) => Promise<ValueType[]>;
debounceTimeout?: number;
initOption?: string;
}
function DebounceSelect<
ValueType extends { key?: string; label: React.ReactNode; value: string | number } = any,
>({ fetchOptions, debounceTimeout = 800, initOption, ...props }: DebounceSelectProps) {
const [fetching, setFetching] = React.useState(false);
const initOptions = [];
let defaultValue = null;
if (initOption && typeof initOption == 'string') {
const option = JSON.parse(initOption);
initOptions.push(option);
defaultValue = option.value;
}
const [options, setOptions] = React.useState<ValueType[]>(initOptions);
const fetchRef = React.useRef(0);
const debounceFetcher = React.useMemo(() => {
const loadOptions = (value: string | number) => {
fetchRef.current += 1;
const fetchId = fetchRef.current;
setOptions([]);
setFetching(true);
fetchOptions({ query: value }).then((newOptions) => {
if (fetchId !== fetchRef.current) {
// for fetch callback order
return;
}
const list = newOptions.data.list.map((user: { id: number; name: string }) => ({
label: user.name,
value: user.id,
}));
setOptions(list);
setFetching(false);
});
};
return debounce(loadOptions, debounceTimeout);
}, [fetchOptions, debounceTimeout]);
return (
<Select<ValueType>
showSearch
labelInValue
filterOption={false}
defaultValue={defaultValue}
onSearch={debounceFetcher}
notFoundContent={fetching ? <Spin size="small" /> : null}
{...props}
options={options}
/>
);
}
export default DebounceSelect;

9
src/components/Footer/index.tsx

@ -1,16 +1,9 @@
import { useIntl } from 'umi';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { DefaultFooter } from '@ant-design/pro-layout'; import { DefaultFooter } from '@ant-design/pro-layout';
const Footer: React.FC = () => { const Footer: React.FC = () => {
const intl = useIntl(); const defaultMessage = '蚂蚁集团体验技术部出品';
const defaultMessage = intl.formatMessage({
id: 'app.copyright.produced',
defaultMessage: '蚂蚁集团体验技术部出品',
});
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
return ( return (
<DefaultFooter <DefaultFooter
copyright={`${currentYear} ${defaultMessage}`} copyright={`${currentYear} ${defaultMessage}`}

14
src/components/RightContent/index.tsx

@ -1,11 +1,10 @@
import { Space } from 'antd'; import { Space } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons'; import { QuestionCircleOutlined } from '@ant-design/icons';
import React from 'react'; import React from 'react';
import { useModel, SelectLang } from 'umi'; import { useModel } from 'umi';
import Avatar from './AvatarDropdown'; import Avatar from './AvatarDropdown';
import HeaderSearch from '../HeaderSearch'; import HeaderSearch from '../HeaderSearch';
import styles from './index.less'; import styles from './index.less';
export type SiderTheme = 'light' | 'dark'; export type SiderTheme = 'light' | 'dark';
const GlobalHeaderRight: React.FC = () => { const GlobalHeaderRight: React.FC = () => {
@ -21,6 +20,7 @@ const GlobalHeaderRight: React.FC = () => {
if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') { if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
className = `${styles.right} ${styles.dark}`; className = `${styles.right} ${styles.dark}`;
} }
return ( return (
<Space className={className}> <Space className={className}>
<HeaderSearch <HeaderSearch
@ -28,7 +28,10 @@ const GlobalHeaderRight: React.FC = () => {
placeholder="站内搜索" placeholder="站内搜索"
defaultValue="umi ui" defaultValue="umi ui"
options={[ options={[
{ label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>, value: 'umi ui' }, {
label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>,
value: 'umi ui',
},
{ {
label: <a href="next.ant.design">Ant Design</a>, label: <a href="next.ant.design">Ant Design</a>,
value: 'Ant Design', value: 'Ant Design',
@ -41,8 +44,7 @@ const GlobalHeaderRight: React.FC = () => {
label: <a href="https://prolayout.ant.design/">Pro Layout</a>, label: <a href="https://prolayout.ant.design/">Pro Layout</a>,
value: 'Pro Layout', value: 'Pro Layout',
}, },
]} ]} // onSearch={value => {
// onSearch={value => {
// console.log('input', value); // console.log('input', value);
// }} // }}
/> />
@ -55,8 +57,8 @@ const GlobalHeaderRight: React.FC = () => {
<QuestionCircleOutlined /> <QuestionCircleOutlined />
</span> </span>
<Avatar /> <Avatar />
<SelectLang className={styles.action} />
</Space> </Space>
); );
}; };
export default GlobalHeaderRight; export default GlobalHeaderRight;

36
src/global.tsx

@ -1,7 +1,5 @@
import { Button, message, notification } from 'antd'; import { Button, message, notification } from 'antd';
import { useIntl } from 'umi';
import defaultSettings from '../config/defaultSettings'; import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings; const { pwa } = defaultSettings;
const isHttps = document.location.protocol === 'https:'; const isHttps = document.location.protocol === 'https:';
@ -17,28 +15,29 @@ const clearCache = () => {
}) })
.catch((e) => console.log(e)); .catch((e) => console.log(e));
} }
}; }; // if pwa is true
// if pwa is true
if (pwa) { if (pwa) {
// Notify user if offline now // Notify user if offline now
window.addEventListener('sw.offline', () => { window.addEventListener('sw.offline', () => {
message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' })); message.warning('当前处于离线状态');
}); }); // Pop up a prompt on the page asking the user if they want to use the latest version
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event: Event) => { window.addEventListener('sw.updated', (event: Event) => {
const e = event as CustomEvent; const e = event as CustomEvent;
const reloadSW = async () => { const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration // Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
const worker = e.detail && e.detail.waiting; const worker = e.detail && e.detail.waiting;
if (!worker) { if (!worker) {
return true; return true;
} } // Send skip-waiting event to waiting SW with MessageChannel
// Send skip-waiting event to waiting SW with MessageChannel
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const channel = new MessageChannel(); const channel = new MessageChannel();
channel.port1.onmessage = (msgEvent) => { channel.port1.onmessage = (msgEvent) => {
if (msgEvent.data.error) { if (msgEvent.data.error) {
reject(msgEvent.data.error); reject(msgEvent.data.error);
@ -46,13 +45,19 @@ if (pwa) {
resolve(msgEvent.data); resolve(msgEvent.data);
} }
}; };
worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
});
worker.postMessage(
{
type: 'skip-waiting',
},
[channel.port2],
);
});
clearCache(); clearCache();
window.location.reload(); window.location.reload();
return true; return true;
}; };
const key = `open${Date.now()}`; const key = `open${Date.now()}`;
const btn = ( const btn = (
<Button <Button
@ -62,12 +67,12 @@ if (pwa) {
reloadSW(); reloadSW();
}} }}
> >
{useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.ok' })} {'刷新'}
</Button> </Button>
); );
notification.open({ notification.open({
message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }), message: '有新内容',
description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }), description: '请点击“刷新”按钮或者手动刷新页面',
btn, btn,
key, key,
onClose: async () => null, onClose: async () => null,
@ -76,6 +81,7 @@ if (pwa) {
} else if ('serviceWorker' in navigator && isHttps) { } else if ('serviceWorker' in navigator && isHttps) {
// unregister service worker // unregister service worker
const { serviceWorker } = navigator; const { serviceWorker } = navigator;
if (serviceWorker.getRegistrations) { if (serviceWorker.getRegistrations) {
serviceWorker.getRegistrations().then((sws) => { serviceWorker.getRegistrations().then((sws) => {
sws.forEach((sw) => { sws.forEach((sw) => {
@ -83,9 +89,9 @@ if (pwa) {
}); });
}); });
} }
serviceWorker.getRegistration().then((sw) => { serviceWorker.getRegistration().then((sw) => {
if (sw) sw.unregister(); if (sw) sw.unregister();
}); });
clearCache(); clearCache();
} }

28
src/pages/Admin.tsx

@ -2,23 +2,13 @@ import React from 'react';
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons'; import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
import { Card, Typography, Alert } from 'antd'; import { Card, Typography, Alert } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout'; import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { useIntl } from 'umi';
const Admin: React.FC = () => { const Admin: React.FC = () => {
const intl = useIntl();
return ( return (
<PageHeaderWrapper <PageHeaderWrapper content={' 这个页面只有 admin 权限才能查看'}>
content={intl.formatMessage({
id: 'pages.admin.subPage.title',
defaultMessage: 'This page can only be viewed by admin',
})}
>
<Card> <Card>
<Alert <Alert
message={intl.formatMessage({ message={'更快更强的重型组件,已经发布。'}
id: 'pages.welcome.alertMessage',
defaultMessage: 'Faster and stronger heavy-duty components have been released.',
})}
type="success" type="success"
showIcon showIcon
banner banner
@ -27,11 +17,21 @@ const Admin: React.FC = () => {
marginBottom: 48, marginBottom: 48,
}} }}
/> />
<Typography.Title level={2} style={{ textAlign: 'center' }}> <Typography.Title
level={2}
style={{
textAlign: 'center',
}}
>
<SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You <SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You
</Typography.Title> </Typography.Title>
</Card> </Card>
<p style={{ textAlign: 'center', marginTop: 24 }}> <p
style={{
textAlign: 'center',
marginTop: 24,
}}
>
Want to add more pages? Please refer to{' '} Want to add more pages? Please refer to{' '}
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer"> <a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
use block use block

56
src/pages/PoemDetail/index.tsx

@ -0,0 +1,56 @@
import React from 'react';
import { Form, message } from 'antd';
import ProForm, {
ProFormDateRangePicker,
ProFormSelect,
ProFormDatePicker,
} from '@ant-design/pro-form';
import AuthorSelect from '@/components/AuthorSelect';
export default () => {
return (
<ProForm
onFinish={async () => {
message.success('提交成功');
}}
syncToUrl={(values, type) => {
if (type === 'get') {
// 为了配合 transform
// startTime 和 endTime 拼成 createTimeRanger
return {
...values,
createTimeRanger:
values.startTime || values.endTime ? [values.startTime, values.endTime] : undefined,
};
}
// expirationTime 不同步到 url
return {
...values,
expirationTime: undefined,
};
}}
initialValues={{
name: '蚂蚁设计有限公司',
useMode: 'chapter',
}}
autoFocusFirstInput
>
<ProFormSelect
options={[
{
value: 'chapter',
label: '盖章后生效',
},
]}
width="sm"
name="useMode"
label="合同约定生效方式"
/>
<Form.Item name="authorId">
{(form) => {
return <AuthorSelect />;
}}
</Form.Item>
</ProForm>
);
};

6
src/pages/PoemPage/index.less

@ -0,0 +1,6 @@
@import '~antd/es/style/themes/default.less';
.main {
width: 100%;
background: @component-background;
}

138
src/pages/PoemPage/index.tsx

@ -0,0 +1,138 @@
import AuthorSelect from '@/components/AuthorSelect';
import { poemListUsingPOST } from '@/services/luigi/poem';
import { PlusOutlined } from '@ant-design/icons';
import ProTable from '@ant-design/pro-table';
import { Button } from 'antd';
import React, { useRef, useState } from 'react';
import type { ProColumns, ActionType } from '@ant-design/pro-table';
const columns: ProColumns<API.PoemBo>[] = [
{
dataIndex: 'index',
valueType: 'indexBorder',
width: 48,
},
{
title: '标题',
dataIndex: 'title',
ellipsis: true,
hideInSearch: true,
tip: '标题过长会自动收缩',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
},
{
title: '状态',
dataIndex: 'status',
valueType: 'select',
valueEnum: {
0: {
text: '未校准',
status: 'Error',
},
1: {
text: '已校准',
status: 'Success',
},
},
},
{
title: '作者',
dataIndex: 'authorName',
hideInSearch: true,
},
{
title: '作者',
dataIndex: 'author',
hideInTable: true,
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return <AuthorSelect {...rest} />;
},
},
{
title: '首行',
dataIndex: 'line',
hideInSearch: true,
},
];
export default () => {
const actionRef = useRef<ActionType>();
return (
<ProTable<API.PoemBo>
columns={columns}
actionRef={actionRef}
cardBordered
request={async (params = {}, sort, filter) => {
const query: API.QueryParam[] = [];
if (params.author) {
let author = params.author;
if (typeof params.author == 'string') {
author = JSON.parse(params.author);
}
query.push({ key: 'authorId', val: author.value });
}
if (params.status) {
query.push({ key: 'status', val: params.status });
}
const response = await poemListUsingPOST({
current: params.current,
pageSize: params.pageSize,
query: query,
});
return { data: response.data?.list, total: response.data?.total };
}}
editable={{
type: 'multiple',
}}
columnsState={{
persistenceKey: 'pro-table-singe-demos',
persistenceType: 'localStorage',
onChange(value) {
console.log('value: ', value);
},
}}
rowKey="id"
search={{
labelWidth: 'auto',
}}
form={{
// 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
syncToUrl: (values, type) => {
if (type === 'get') {
return {
...values,
created_at: [values.startTime, values.endTime],
};
}
return values;
},
}}
pagination={{
pageSize: 5,
}}
dateFormatter="string"
headerTitle="高级表格"
toolBarRender={() => [
<Button key="button" icon={<PlusOutlined />} type="primary">
</Button>,
]}
/>
);
};

89
src/pages/TableList/components/UpdateForm.tsx

@ -8,8 +8,6 @@ import {
ProFormRadio, ProFormRadio,
ProFormDateTimePicker, ProFormDateTimePicker,
} from '@ant-design/pro-form'; } from '@ant-design/pro-form';
import { useIntl, FormattedMessage } from 'umi';
export type FormValueType = { export type FormValueType = {
target?: string; target?: string;
template?: string; template?: string;
@ -17,7 +15,6 @@ export type FormValueType = {
time?: string; time?: string;
frequency?: string; frequency?: string;
} & Partial<API.RuleListItem>; } & Partial<API.RuleListItem>;
export type UpdateFormProps = { export type UpdateFormProps = {
onCancel: (flag?: boolean, formVals?: FormValueType) => void; onCancel: (flag?: boolean, formVals?: FormValueType) => void;
onSubmit: (values: FormValueType) => Promise<void>; onSubmit: (values: FormValueType) => Promise<void>;
@ -26,7 +23,6 @@ export type UpdateFormProps = {
}; };
const UpdateForm: React.FC<UpdateFormProps> = (props) => { const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const intl = useIntl();
return ( return (
<StepsForm <StepsForm
stepsProps={{ stepsProps={{
@ -36,12 +32,11 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
return ( return (
<Modal <Modal
width={640} width={640}
bodyStyle={{ padding: '32px 40px 48px' }} bodyStyle={{
padding: '32px 40px 48px',
}}
destroyOnClose destroyOnClose
title={intl.formatMessage({ title={'规则配置'}
id: 'pages.searchTable.updateForm.ruleConfig',
defaultMessage: '规则配置',
})}
visible={props.updateModalVisible} visible={props.updateModalVisible}
footer={submitter} footer={submitter}
onCancel={() => { onCancel={() => {
@ -59,50 +54,28 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
name: props.values.name, name: props.values.name,
desc: props.values.desc, desc: props.values.desc,
}} }}
title={intl.formatMessage({ title={'基本信息'}
id: 'pages.searchTable.updateForm.basicConfig',
defaultMessage: '基本信息',
})}
> >
<ProFormText <ProFormText
name="name" name="name"
label={intl.formatMessage({ label={'规则名称'}
id: 'pages.searchTable.updateForm.ruleName.nameLabel',
defaultMessage: '规则名称',
})}
width="md" width="md"
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '请输入规则名称!',
<FormattedMessage
id="pages.searchTable.updateForm.ruleName.nameRules"
defaultMessage="请输入规则名称!"
/>
),
}, },
]} ]}
/> />
<ProFormTextArea <ProFormTextArea
name="desc" name="desc"
width="md" width="md"
label={intl.formatMessage({ label={'规则描述'}
id: 'pages.searchTable.updateForm.ruleDesc.descLabel', placeholder={'请输入至少五个字符'}
defaultMessage: '规则描述',
})}
placeholder={intl.formatMessage({
id: 'pages.searchTable.updateForm.ruleDesc.descPlaceholder',
defaultMessage: '请输入至少五个字符',
})}
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '请输入至少五个字符的规则描述!',
<FormattedMessage
id="pages.searchTable.updateForm.ruleDesc.descRules"
defaultMessage="请输入至少五个字符的规则描述!"
/>
),
min: 5, min: 5,
}, },
]} ]}
@ -113,18 +86,12 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
target: '0', target: '0',
template: '0', template: '0',
}} }}
title={intl.formatMessage({ title={'配置规则属性'}
id: 'pages.searchTable.updateForm.ruleProps.title',
defaultMessage: '配置规则属性',
})}
> >
<ProFormSelect <ProFormSelect
name="target" name="target"
width="md" width="md"
label={intl.formatMessage({ label={'监控对象'}
id: 'pages.searchTable.updateForm.object',
defaultMessage: '监控对象',
})}
valueEnum={{ valueEnum={{
0: '表一', 0: '表一',
1: '表二', 1: '表二',
@ -133,10 +100,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
<ProFormSelect <ProFormSelect
name="template" name="template"
width="md" width="md"
label={intl.formatMessage({ label={'规则模板'}
id: 'pages.searchTable.updateForm.ruleProps.templateLabel',
defaultMessage: '规则模板',
})}
valueEnum={{ valueEnum={{
0: '规则模板一', 0: '规则模板一',
1: '规则模板二', 1: '规则模板二',
@ -144,10 +108,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
/> />
<ProFormRadio.Group <ProFormRadio.Group
name="type" name="type"
label={intl.formatMessage({ label={'规则类型'}
id: 'pages.searchTable.updateForm.ruleProps.typeLabel',
defaultMessage: '规则类型',
})}
options={[ options={[
{ {
value: '0', value: '0',
@ -165,36 +126,22 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
type: '1', type: '1',
frequency: 'month', frequency: 'month',
}} }}
title={intl.formatMessage({ title={'设定调度周期'}
id: 'pages.searchTable.updateForm.schedulingPeriod.title',
defaultMessage: '设定调度周期',
})}
> >
<ProFormDateTimePicker <ProFormDateTimePicker
name="time" name="time"
width="md" width="md"
label={intl.formatMessage({ label={'开始时间'}
id: 'pages.searchTable.updateForm.schedulingPeriod.timeLabel',
defaultMessage: '开始时间',
})}
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '请选择开始时间!',
<FormattedMessage
id="pages.searchTable.updateForm.schedulingPeriod.timeRules"
defaultMessage="请选择开始时间!"
/>
),
}, },
]} ]}
/> />
<ProFormSelect <ProFormSelect
name="frequency" name="frequency"
label={intl.formatMessage({ label={'监控对象'}
id: 'pages.searchTable.updateForm.object',
defaultMessage: '监控对象',
})}
width="md" width="md"
valueEnum={{ valueEnum={{
month: '月', month: '月',

148
src/pages/TableList/index.tsx

@ -1,7 +1,6 @@
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { Button, message, Input, Drawer } from 'antd'; import { Button, message, Input, Drawer } from 'antd';
import React, { useState, useRef } from 'react'; import React, { useState, useRef } from 'react';
import { useIntl, FormattedMessage } from 'umi';
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout'; import { PageContainer, FooterToolbar } from '@ant-design/pro-layout';
import type { ProColumns, ActionType } from '@ant-design/pro-table'; import type { ProColumns, ActionType } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table'; import ProTable from '@ant-design/pro-table';
@ -11,14 +10,15 @@ import ProDescriptions from '@ant-design/pro-descriptions';
import type { FormValueType } from './components/UpdateForm'; import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm'; import UpdateForm from './components/UpdateForm';
import { rule, addRule, updateRule, removeRule } from '@/services/ant-design-pro/api'; import { rule, addRule, updateRule, removeRule } from '@/services/ant-design-pro/api';
/** /**
* @en-US Add node * @en-US Add node
* @zh-CN * @zh-CN
* @param fields * @param fields
*/ */
const handleAdd = async (fields: API.RuleListItem) => { const handleAdd = async (fields: API.RuleListItem) => {
const hide = message.loading('正在添加'); const hide = message.loading('正在添加');
try { try {
await addRule({ ...fields }); await addRule({ ...fields });
hide(); hide();
@ -30,15 +30,16 @@ const handleAdd = async (fields: API.RuleListItem) => {
return false; return false;
} }
}; };
/** /**
* @en-US Update node * @en-US Update node
* @zh-CN * @zh-CN
* *
* @param fields * @param fields
*/ */
const handleUpdate = async (fields: FormValueType) => { const handleUpdate = async (fields: FormValueType) => {
const hide = message.loading('Configuring'); const hide = message.loading('Configuring');
try { try {
await updateRule({ await updateRule({
name: fields.name, name: fields.name,
@ -46,7 +47,6 @@ const handleUpdate = async (fields: FormValueType) => {
key: fields.key, key: fields.key,
}); });
hide(); hide();
message.success('Configuration is successful'); message.success('Configuration is successful');
return true; return true;
} catch (error) { } catch (error) {
@ -55,16 +55,17 @@ const handleUpdate = async (fields: FormValueType) => {
return false; return false;
} }
}; };
/** /**
* Delete node * Delete node
* @zh-CN * @zh-CN
* *
* @param selectedRows * @param selectedRows
*/ */
const handleRemove = async (selectedRows: API.RuleListItem[]) => { const handleRemove = async (selectedRows: API.RuleListItem[]) => {
const hide = message.loading('正在删除'); const hide = message.loading('正在删除');
if (!selectedRows) return true; if (!selectedRows) return true;
try { try {
await removeRule({ await removeRule({
key: selectedRows.map((row) => row.key), key: selectedRows.map((row) => row.key),
@ -89,28 +90,20 @@ const TableList: React.FC = () => {
* @en-US The pop-up window of the distribution update window * @en-US The pop-up window of the distribution update window
* @zh-CN * @zh-CN
* */ * */
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [showDetail, setShowDetail] = useState<boolean>(false); const [showDetail, setShowDetail] = useState<boolean>(false);
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const [currentRow, setCurrentRow] = useState<API.RuleListItem>(); const [currentRow, setCurrentRow] = useState<API.RuleListItem>();
const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]); const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);
/** /**
* @en-US International configuration * @en-US International configuration
* @zh-CN * @zh-CN
* */ * */
const intl = useIntl();
const columns: ProColumns<API.RuleListItem>[] = [ const columns: ProColumns<API.RuleListItem>[] = [
{ {
title: ( title: '规则名称',
<FormattedMessage
id="pages.searchTable.updateForm.ruleName.nameLabel"
defaultMessage="Rule name"
/>
),
dataIndex: 'name', dataIndex: 'name',
tip: 'The rule name is the unique key', tip: 'The rule name is the unique key',
render: (dom, entity) => { render: (dom, entity) => {
@ -127,94 +120,61 @@ const TableList: React.FC = () => {
}, },
}, },
{ {
title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />, title: '描述',
dataIndex: 'desc', dataIndex: 'desc',
valueType: 'textarea', valueType: 'textarea',
}, },
{ {
title: ( title: '服务调用次数',
<FormattedMessage
id="pages.searchTable.titleCallNo"
defaultMessage="Number of service calls"
/>
),
dataIndex: 'callNo', dataIndex: 'callNo',
sorter: true, sorter: true,
hideInForm: true, hideInForm: true,
renderText: (val: string) => renderText: (val: string) => `${val}${'万'}`,
`${val}${intl.formatMessage({
id: 'pages.searchTable.tenThousand',
defaultMessage: ' 万 ',
})}`,
}, },
{ {
title: <FormattedMessage id="pages.searchTable.titleStatus" defaultMessage="Status" />, title: '状态',
dataIndex: 'status', dataIndex: 'status',
hideInForm: true, hideInForm: true,
valueEnum: { valueEnum: {
0: { 0: {
text: ( text: '关闭',
<FormattedMessage
id="pages.searchTable.nameStatus.default"
defaultMessage="Shut down"
/>
),
status: 'Default', status: 'Default',
}, },
1: { 1: {
text: ( text: '运行中',
<FormattedMessage id="pages.searchTable.nameStatus.running" defaultMessage="Running" />
),
status: 'Processing', status: 'Processing',
}, },
2: { 2: {
text: ( text: '已上线',
<FormattedMessage id="pages.searchTable.nameStatus.online" defaultMessage="Online" />
),
status: 'Success', status: 'Success',
}, },
3: { 3: {
text: ( text: '异常',
<FormattedMessage
id="pages.searchTable.nameStatus.abnormal"
defaultMessage="Abnormal"
/>
),
status: 'Error', status: 'Error',
}, },
}, },
}, },
{ {
title: ( title: '上次调度时间',
<FormattedMessage
id="pages.searchTable.titleUpdatedAt"
defaultMessage="Last scheduled time"
/>
),
sorter: true, sorter: true,
dataIndex: 'updatedAt', dataIndex: 'updatedAt',
valueType: 'dateTime', valueType: 'dateTime',
renderFormItem: (item, { defaultRender, ...rest }, form) => { renderFormItem: (item, { defaultRender, ...rest }, form) => {
const status = form.getFieldValue('status'); const status = form.getFieldValue('status');
if (`${status}` === '0') { if (`${status}` === '0') {
return false; return false;
} }
if (`${status}` === '3') { if (`${status}` === '3') {
return ( return <Input {...rest} placeholder={'请输入异常原因!'} />;
<Input
{...rest}
placeholder={intl.formatMessage({
id: 'pages.searchTable.exception',
defaultMessage: 'Please enter the reason for the exception!',
})}
/>
);
} }
return defaultRender(item); return defaultRender(item);
}, },
}, },
{ {
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />, title: '操作',
dataIndex: 'option', dataIndex: 'option',
valueType: 'option', valueType: 'option',
render: (_, record) => [ render: (_, record) => [
@ -225,25 +185,18 @@ const TableList: React.FC = () => {
setCurrentRow(record); setCurrentRow(record);
}} }}
> >
<FormattedMessage id="pages.searchTable.config" defaultMessage="Configuration" />
</a>, </a>,
<a key="subscribeAlert" href="https://procomponents.ant.design/"> <a key="subscribeAlert" href="https://procomponents.ant.design/">
<FormattedMessage
id="pages.searchTable.subscribeAlert"
defaultMessage="Subscribe to alerts"
/>
</a>, </a>,
], ],
}, },
]; ];
return ( return (
<PageContainer> <PageContainer>
<ProTable<API.RuleListItem, API.PageParams> <ProTable<API.RuleListItem, API.PageParams>
headerTitle={intl.formatMessage({ headerTitle={'查询表格'}
id: 'pages.searchTable.title',
defaultMessage: 'Enquiry form',
})}
actionRef={actionRef} actionRef={actionRef}
rowKey="key" rowKey="key"
search={{ search={{
@ -257,7 +210,7 @@ const TableList: React.FC = () => {
handleModalVisible(true); handleModalVisible(true);
}} }}
> >
<PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" /> <PlusOutlined />
</Button>, </Button>,
]} ]}
request={rule} request={rule}
@ -272,17 +225,17 @@ const TableList: React.FC = () => {
<FooterToolbar <FooterToolbar
extra={ extra={
<div> <div>
<FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '} {' '}
<a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '} <a
<FormattedMessage id="pages.searchTable.item" defaultMessage="项" /> style={{
&nbsp;&nbsp; fontWeight: 600,
}}
>
{selectedRowsState.length}
</a>{' '}
&nbsp;&nbsp;
<span> <span>
<FormattedMessage {selectedRowsState.reduce((pre, item) => pre + item.callNo!, 0)}
id="pages.searchTable.totalServiceCalls"
defaultMessage="Total number of service calls"
/>{' '}
{selectedRowsState.reduce((pre, item) => pre + item.callNo!, 0)}{' '}
<FormattedMessage id="pages.searchTable.tenThousand" defaultMessage="万" />
</span> </span>
</div> </div>
} }
@ -294,31 +247,22 @@ const TableList: React.FC = () => {
actionRef.current?.reloadAndRest?.(); actionRef.current?.reloadAndRest?.();
}} }}
> >
<FormattedMessage
id="pages.searchTable.batchDeletion"
defaultMessage="Batch deletion"
/>
</Button>
<Button type="primary">
<FormattedMessage
id="pages.searchTable.batchApproval"
defaultMessage="Batch approval"
/>
</Button> </Button>
<Button type="primary"></Button>
</FooterToolbar> </FooterToolbar>
)} )}
<ModalForm <ModalForm
title={intl.formatMessage({ title={'新建规则'}
id: 'pages.searchTable.createForm.newRule',
defaultMessage: 'New rule',
})}
width="400px" width="400px"
visible={createModalVisible} visible={createModalVisible}
onVisibleChange={handleModalVisible} onVisibleChange={handleModalVisible}
onFinish={async (value) => { onFinish={async (value) => {
const success = await handleAdd(value as API.RuleListItem); const success = await handleAdd(value as API.RuleListItem);
if (success) { if (success) {
handleModalVisible(false); handleModalVisible(false);
if (actionRef.current) { if (actionRef.current) {
actionRef.current.reload(); actionRef.current.reload();
} }
@ -329,12 +273,7 @@ const TableList: React.FC = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '规则名称为必填项',
<FormattedMessage
id="pages.searchTable.ruleName"
defaultMessage="Rule name is required"
/>
),
}, },
]} ]}
width="md" width="md"
@ -345,9 +284,11 @@ const TableList: React.FC = () => {
<UpdateForm <UpdateForm
onSubmit={async (value) => { onSubmit={async (value) => {
const success = await handleUpdate(value); const success = await handleUpdate(value);
if (success) { if (success) {
handleUpdateModalVisible(false); handleUpdateModalVisible(false);
setCurrentRow(undefined); setCurrentRow(undefined);
if (actionRef.current) { if (actionRef.current) {
actionRef.current.reload(); actionRef.current.reload();
} }
@ -355,6 +296,7 @@ const TableList: React.FC = () => {
}} }}
onCancel={() => { onCancel={() => {
handleUpdateModalVisible(false); handleUpdateModalVisible(false);
if (!showDetail) { if (!showDetail) {
setCurrentRow(undefined); setCurrentRow(undefined);
} }

16
src/pages/Welcome.tsx

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { PageContainer } from '@ant-design/pro-layout'; import { PageContainer } from '@ant-design/pro-layout';
import { Card, Alert, Typography } from 'antd'; import { Card, Alert, Typography } from 'antd';
import { useIntl, FormattedMessage } from 'umi';
import styles from './Welcome.less'; import styles from './Welcome.less';
const CodePreview: React.FC = ({ children }) => ( const CodePreview: React.FC = ({ children }) => (
@ -13,16 +12,11 @@ const CodePreview: React.FC = ({ children }) => (
); );
const Welcome: React.FC = () => { const Welcome: React.FC = () => {
const intl = useIntl();
return ( return (
<PageContainer> <PageContainer>
<Card> <Card>
<Alert <Alert
message={intl.formatMessage({ message={'更快更强的重型组件,已经发布。'}
id: 'pages.welcome.alertMessage',
defaultMessage: 'Faster and stronger heavy-duty components have been released.',
})}
type="success" type="success"
showIcon showIcon
banner banner
@ -32,13 +26,13 @@ const Welcome: React.FC = () => {
}} }}
/> />
<Typography.Text strong> <Typography.Text strong>
<FormattedMessage id="pages.welcome.advancedComponent" defaultMessage="Advanced Form" />{' '} {' '}
<a <a
href="https://procomponents.ant.design/components/table" href="https://procomponents.ant.design/components/table"
rel="noopener noreferrer" rel="noopener noreferrer"
target="__blank" target="__blank"
> >
<FormattedMessage id="pages.welcome.link" defaultMessage="Welcome" /> 使
</a> </a>
</Typography.Text> </Typography.Text>
<CodePreview>yarn add @ant-design/pro-table</CodePreview> <CodePreview>yarn add @ant-design/pro-table</CodePreview>
@ -48,13 +42,13 @@ const Welcome: React.FC = () => {
marginBottom: 12, marginBottom: 12,
}} }}
> >
<FormattedMessage id="pages.welcome.advancedLayout" defaultMessage="Advanced layout" />{' '} {' '}
<a <a
href="https://procomponents.ant.design/components/layout" href="https://procomponents.ant.design/components/layout"
rel="noopener noreferrer" rel="noopener noreferrer"
target="__blank" target="__blank"
> >
<FormattedMessage id="pages.welcome.link" defaultMessage="Welcome" /> 使
</a> </a>
</Typography.Text> </Typography.Text>
<CodePreview>yarn add @ant-design/pro-layout</CodePreview> <CodePreview>yarn add @ant-design/pro-layout</CodePreview>

140
src/pages/user/Login/index.tsx

@ -9,11 +9,10 @@ import {
import { Alert, message, Tabs } from 'antd'; import { Alert, message, Tabs } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ProFormCaptcha, ProFormCheckbox, ProFormText, LoginForm } from '@ant-design/pro-form'; import { ProFormCaptcha, ProFormCheckbox, ProFormText, LoginForm } from '@ant-design/pro-form';
import { useIntl, history, FormattedMessage, SelectLang, useModel } from 'umi'; import { history, useModel } from 'umi';
import Footer from '@/components/Footer'; import Footer from '@/components/Footer';
import { login } from '@/services/ant-design-pro/api'; import { login } from '@/services/ant-design-pro/api';
import { getFakeCaptcha } from '@/services/ant-design-pro/login'; import { getFakeCaptcha } from '@/services/ant-design-pro/login';
import styles from './index.less'; import styles from './index.less';
const LoginMessage: React.FC<{ const LoginMessage: React.FC<{
@ -34,15 +33,11 @@ const Login: React.FC = () => {
const [type, setType] = useState<string>('account'); const [type, setType] = useState<string>('account');
const { initialState, setInitialState } = useModel('@@initialState'); const { initialState, setInitialState } = useModel('@@initialState');
const intl = useIntl();
const fetchUserInfo = async () => { const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.(); const userInfo = await initialState?.fetchUserInfo?.();
if (userInfo) { if (userInfo) {
await setInitialState((s) => ({ await setInitialState((s) => ({ ...s, currentUser: userInfo }));
...s,
currentUser: userInfo,
}));
} }
}; };
@ -50,52 +45,44 @@ const Login: React.FC = () => {
try { try {
// 登录 // 登录
const msg = await login({ ...values, type }); const msg = await login({ ...values, type });
if (msg.status === 'ok') { if (msg.status === 'ok') {
const defaultLoginSuccessMessage = intl.formatMessage({ const defaultLoginSuccessMessage = '登录成功!';
id: 'pages.login.success',
defaultMessage: '登录成功!',
});
message.success(defaultLoginSuccessMessage); message.success(defaultLoginSuccessMessage);
await fetchUserInfo(); await fetchUserInfo();
/** 此方法会跳转到 redirect 参数所在的位置 */ /** 此方法会跳转到 redirect 参数所在的位置 */
if (!history) return; if (!history) return;
const { query } = history.location; const { query } = history.location;
const { redirect } = query as { redirect: string }; const { redirect } = query as {
redirect: string;
};
history.push(redirect || '/'); history.push(redirect || '/');
return; return;
} }
console.log(msg);
// 如果失败去设置用户错误信息 console.log(msg); // 如果失败去设置用户错误信息
setUserLoginState(msg); setUserLoginState(msg);
} catch (error) { } catch (error) {
const defaultLoginFailureMessage = intl.formatMessage({ const defaultLoginFailureMessage = '登录失败,请重试!';
id: 'pages.login.failure',
defaultMessage: '登录失败,请重试!',
});
message.error(defaultLoginFailureMessage); message.error(defaultLoginFailureMessage);
} }
}; };
const { status, type: loginType } = userLoginState;
const { status, type: loginType } = userLoginState;
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.lang} data-lang>
{SelectLang && <SelectLang />}
</div>
<div className={styles.content}> <div className={styles.content}>
<LoginForm <LoginForm
logo={<img alt="logo" src="/logo.svg" />} logo={<img alt="logo" src="/logo.svg" />}
title="Ant Design" title="Ant Design"
subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })} subTitle={'Ant Design 是西湖区最具影响力的 Web 设计规范'}
initialValues={{ initialValues={{
autoLogin: true, autoLogin: true,
}} }}
actions={[ actions={[
<FormattedMessage '其他登录方式 :',
key="loginWith"
id="pages.login.loginWith"
defaultMessage="其他登录方式"
/>,
<AlipayCircleOutlined key="AlipayCircleOutlined" className={styles.icon} />, <AlipayCircleOutlined key="AlipayCircleOutlined" className={styles.icon} />,
<TaobaoCircleOutlined key="TaobaoCircleOutlined" className={styles.icon} />, <TaobaoCircleOutlined key="TaobaoCircleOutlined" className={styles.icon} />,
<WeiboCircleOutlined key="WeiboCircleOutlined" className={styles.icon} />, <WeiboCircleOutlined key="WeiboCircleOutlined" className={styles.icon} />,
@ -105,29 +92,12 @@ const Login: React.FC = () => {
}} }}
> >
<Tabs activeKey={type} onChange={setType}> <Tabs activeKey={type} onChange={setType}>
<Tabs.TabPane <Tabs.TabPane key="account" tab={'账户密码登录'} />
key="account" <Tabs.TabPane key="mobile" tab={'手机号登录'} />
tab={intl.formatMessage({
id: 'pages.login.accountLogin.tab',
defaultMessage: '账户密码登录',
})}
/>
<Tabs.TabPane
key="mobile"
tab={intl.formatMessage({
id: 'pages.login.phoneLogin.tab',
defaultMessage: '手机号登录',
})}
/>
</Tabs> </Tabs>
{status === 'error' && loginType === 'account' && ( {status === 'error' && loginType === 'account' && (
<LoginMessage <LoginMessage content={'错误的用户名和密码(admin/ant.design)'} />
content={intl.formatMessage({
id: 'pages.login.accountLogin.errorMessage',
defaultMessage: '账户或密码错误(admin/ant.design)',
})}
/>
)} )}
{type === 'account' && ( {type === 'account' && (
<> <>
@ -137,19 +107,11 @@ const Login: React.FC = () => {
size: 'large', size: 'large',
prefix: <UserOutlined className={styles.prefixIcon} />, prefix: <UserOutlined className={styles.prefixIcon} />,
}} }}
placeholder={intl.formatMessage({ placeholder={'用户名: admin or user'}
id: 'pages.login.username.placeholder',
defaultMessage: '用户名: admin or user',
})}
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '用户名是必填项!',
<FormattedMessage
id="pages.login.username.required"
defaultMessage="请输入用户名!"
/>
),
}, },
]} ]}
/> />
@ -159,19 +121,11 @@ const Login: React.FC = () => {
size: 'large', size: 'large',
prefix: <LockOutlined className={styles.prefixIcon} />, prefix: <LockOutlined className={styles.prefixIcon} />,
}} }}
placeholder={intl.formatMessage({ placeholder={'密码: ant.design'}
id: 'pages.login.password.placeholder',
defaultMessage: '密码: ant.design',
})}
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '密码是必填项!',
<FormattedMessage
id="pages.login.password.required"
defaultMessage="请输入密码!"
/>
),
}, },
]} ]}
/> />
@ -187,28 +141,15 @@ const Login: React.FC = () => {
prefix: <MobileOutlined className={styles.prefixIcon} />, prefix: <MobileOutlined className={styles.prefixIcon} />,
}} }}
name="mobile" name="mobile"
placeholder={intl.formatMessage({ placeholder={'请输入手机号!'}
id: 'pages.login.phoneNumber.placeholder',
defaultMessage: '手机号',
})}
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '手机号是必填项!',
<FormattedMessage
id="pages.login.phoneNumber.required"
defaultMessage="请输入手机号!"
/>
),
}, },
{ {
pattern: /^1\d{10}$/, pattern: /^1\d{10}$/,
message: ( message: '不合法的手机号!',
<FormattedMessage
id="pages.login.phoneNumber.invalid"
defaultMessage="手机号格式错误!"
/>
),
}, },
]} ]}
/> />
@ -220,41 +161,30 @@ const Login: React.FC = () => {
captchaProps={{ captchaProps={{
size: 'large', size: 'large',
}} }}
placeholder={intl.formatMessage({ placeholder={'请输入验证码!'}
id: 'pages.login.captcha.placeholder',
defaultMessage: '请输入验证码',
})}
captchaTextRender={(timing, count) => { captchaTextRender={(timing, count) => {
if (timing) { if (timing) {
return `${count} ${intl.formatMessage({ return `${count} ${'秒后重新获取'}`;
id: 'pages.getCaptchaSecondText',
defaultMessage: '获取验证码',
})}`;
} }
return intl.formatMessage({
id: 'pages.login.phoneLogin.getVerificationCode', return '获取验证码';
defaultMessage: '获取验证码',
});
}} }}
name="captcha" name="captcha"
rules={[ rules={[
{ {
required: true, required: true,
message: ( message: '验证码是必填项!',
<FormattedMessage
id="pages.login.captcha.required"
defaultMessage="请输入验证码!"
/>
),
}, },
]} ]}
onGetCaptcha={async (phone) => { onGetCaptcha={async (phone) => {
const result = await getFakeCaptcha({ const result = await getFakeCaptcha({
phone, phone,
}); });
if (result === false) { if (result === false) {
return; return;
} }
message.success('获取验证码成功!验证码为:1234'); message.success('获取验证码成功!验证码为:1234');
}} }}
/> />
@ -266,14 +196,14 @@ const Login: React.FC = () => {
}} }}
> >
<ProFormCheckbox noStyle name="autoLogin"> <ProFormCheckbox noStyle name="autoLogin">
<FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
</ProFormCheckbox> </ProFormCheckbox>
<a <a
style={{ style={{
float: 'right', float: 'right',
}} }}
> >
<FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" /> ?
</a> </a>
</div> </div>
</LoginForm> </LoginForm>

68
src/services/ant-design-pro/api.ts

@ -4,34 +4,12 @@ import { request } from 'umi';
/** 获取当前的用户 GET /api/currentUser */ /** 获取当前的用户 GET /api/currentUser */
export async function currentUser(options?: { [key: string]: any }) { export async function currentUser(options?: { [key: string]: any }) {
return request<{ return request<API.CurrentUser>('/api/currentUser', {
data: API.CurrentUser;
}>('/api/currentUser', {
method: 'GET', method: 'GET',
...(options || {}), ...(options || {}),
}); });
} }
/** 退出登录接口 POST /api/login/outLogin */
export async function outLogin(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/login/outLogin', {
method: 'POST',
...(options || {}),
});
}
/** 登录接口 POST /api/login/account */
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
return request<API.LoginResult>('/api/login/account', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 此处后端没有提供注释 GET /api/notices */ /** 此处后端没有提供注释 GET /api/notices */
export async function getNotices(options?: { [key: string]: any }) { export async function getNotices(options?: { [key: string]: any }) {
return request<API.NoticeIconList>('/api/notices', { return request<API.NoticeIconList>('/api/notices', {
@ -39,47 +17,3 @@ export async function getNotices(options?: { [key: string]: any }) {
...(options || {}), ...(options || {}),
}); });
} }
/** 获取规则列表 GET /api/rule */
export async function rule(
params: {
// query
/** 当前的页码 */
current?: number;
/** 页面的容量 */
pageSize?: number;
},
options?: { [key: string]: any },
) {
return request<API.RuleList>('/api/rule', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** 新建规则 PUT /api/rule */
export async function updateRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'PUT',
...(options || {}),
});
}
/** 新建规则 POST /api/rule */
export async function addRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'POST',
...(options || {}),
});
}
/** 删除规则 DELETE /api/rule */
export async function removeRule(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/rule', {
method: 'DELETE',
...(options || {}),
});
}

2
src/services/ant-design-pro/index.ts

@ -4,7 +4,9 @@
// API 唯一标识: // API 唯一标识:
import * as api from './api'; import * as api from './api';
import * as login from './login'; import * as login from './login';
import * as rule from './rule';
export default { export default {
api, api,
login, login,
rule,
}; };

29
src/services/ant-design-pro/login.ts

@ -4,18 +4,35 @@ import { request } from 'umi';
/** 发送验证码 POST /api/login/captcha */ /** 发送验证码 POST /api/login/captcha */
export async function getFakeCaptcha( export async function getFakeCaptcha(
params: { // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
// query params: API.getFakeCaptchaParams,
/** 手机号 */
phone?: string;
},
options?: { [key: string]: any }, options?: { [key: string]: any },
) { ) {
return request<API.FakeCaptcha>('/api/login/captcha', { return request<API.FakeCaptcha>('/api/login/captcha', {
method: 'GET', method: 'POST',
params: { params: {
...params, ...params,
}, },
...(options || {}), ...(options || {}),
}); });
} }
/** 登录接口 POST /api/login/outLogin */
export async function outLogin(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/login/outLogin', {
method: 'POST',
...(options || {}),
});
}
/** 登录接口 POST /api/login/account */
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
return request<API.LoginResult>('/api/login/account', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

42
src/services/ant-design-pro/rule.ts

@ -0,0 +1,42 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** 获取规则列表 GET /api/rule */
export async function rule(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.ruleParams,
options?: { [key: string]: any },
) {
return request<API.RuleList>('/api/rule', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** 新建规则 PUT /api/rule */
export async function updateRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'PUT',
...(options || {}),
});
}
/** 新建规则 POST /api/rule */
export async function addRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'POST',
...(options || {}),
});
}
/** 删除规则 DELETE /api/rule */
export async function removeRule(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/rule', {
method: 'DELETE',
...(options || {}),
});
}

15
src/services/ant-design-pro/typings.d.ts

@ -1,6 +1,3 @@
// @ts-ignore
/* eslint-disable */
declare namespace API { declare namespace API {
type CurrentUser = { type CurrentUser = {
name?: string; name?: string;
@ -98,4 +95,16 @@ declare namespace API {
description?: string; description?: string;
type?: NoticeIconItemType; type?: NoticeIconItemType;
}; };
type getFakeCaptchaParams = {
/** 手机号 */
phone?: string;
};
type ruleParams = {
/** 当前的页码 */
current?: number;
/** 页面的容量 */
pageSize?: number;
};
} }

18
src/services/luigi/author.ts

@ -0,0 +1,18 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** seek GET /api/luigi/author/seek */
export async function seekUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.seekUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.ResponseCommonListAuthor>('/api/luigi/author/seek', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}

12
src/services/luigi/index.ts

@ -0,0 +1,12 @@
// @ts-ignore
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
import * as author from './author';
import * as poem from './poem';
import * as user from './user';
export default {
author,
poem,
user,
};

30
src/services/luigi/poem.ts

@ -0,0 +1,30 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** poemDetail GET /api/luigi/poem/detail */
export async function poemDetailUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.poemDetailUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.ResponsePoemBo>('/api/luigi/poem/detail', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** poemList POST /api/luigi/poem/list */
export async function poemListUsingPOST(body: API.PoemQuery, options?: { [key: string]: any }) {
return request<API.ResponseCommonListPoemBo>('/api/luigi/poem/list', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

88
src/services/luigi/typings.d.ts

@ -0,0 +1,88 @@
declare namespace API {
type Author = {
dynasty?: string;
id?: number;
introduction?: string;
name?: string;
poemCnt?: number;
status?: number;
};
type CommonListAuthor = {
current?: number;
list?: Author[];
pageSize?: number;
total?: number;
};
type CommonListPoemBo = {
current?: number;
list?: PoemBo[];
pageSize?: number;
total?: number;
};
type PoemBo = {
authorId?: number;
authorName?: string;
content?: string;
id?: number;
line?: string;
note?: string;
status?: number;
title?: string;
translation?: string;
};
type PoemQuery = {
current?: number;
pageSize?: number;
query?: QueryParam[];
};
type QueryParam = {
key?: string;
val?: string;
};
type ResponseCommonListAuthor = {
code?: number;
data?: CommonListAuthor;
message?: string;
};
type ResponseCommonListPoemBo = {
code?: number;
data?: CommonListPoemBo;
message?: string;
};
type ResponsePoemBo = {
code?: number;
data?: PoemBo;
message?: string;
};
type ResponseUserBo = {
code?: number;
data?: UserBo;
message?: string;
};
type UserBo = {
avatar?: string;
id?: number;
name?: string;
role?: string;
};
type seekUsingGETParams = {
/** name */
name: string;
};
type poemDetailUsingGETParams = {
/** id */
id: number;
};
}

11
src/services/luigi/user.ts

@ -0,0 +1,11 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** getCurrent GET /api/luigi/user/current */
export async function getCurrentUsingGET(options?: { [key: string]: any }) {
return request<API.ResponseUserBo>('/api/luigi/user/current', {
method: 'GET',
...(options || {}),
});
}
Loading…
Cancel
Save