Commit fe1f28aa authored by maple's avatar maple

[new]第一次提交

parents
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
/lambda/
/scripts
/config
.history
\ No newline at end of file
module.exports = {
extends: [require.resolve('@umijs/fabric/dist/eslint')],
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
page: true,
REACT_APP_ENV: true,
},
};
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
# roadhog-api-doc ignore
/src/utils/request-temp.js
_roadhog-api-doc
# production
/dist
/.vscode
# misc
.DS_Store
npm-debug.log*
yarn-error.log
/coverage
.idea
yarn.lock
package-lock.json
*bak
.vscode
# visual studio code
.history
*.log
functions/*
.temp/**
# umi
.umi
.umi-production
# screenshot
screenshot
.firebase
.eslintcache
build
**/*.svg
package.json
.umi
.umi-production
/dist
.dockerignore
.DS_Store
.eslintignore
*.png
*.toml
docker
.editorconfig
Dockerfile*
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
.history
CNAME
/build
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.prettier,
};
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.stylelint,
};
# Ant Design Pro
This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
## Environment Prepare
Install `node_modules`:
```bash
npm install
```
or
```bash
yarn
```
## Provided Scripts
Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
Scripts provided in `package.json`. It's safe to modify or add additional script:
### Start project
```bash
npm start
```
### Build project
```bash
npm run build
```
### Check code style
```bash
npm run lint
```
You can also use script to auto fix some lint error:
```bash
npm run lint:fix
```
### Test code
```bash
npm test
```
## More
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
// https://umijs.org/config/
import { defineConfig } from 'umi';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
const { REACT_APP_ENV } = process.env;
export default defineConfig({
hash: true,
antd: {},
dva: {
hmr: true,
},
locale: {
// default zh-CN
default: 'zh-CN',
// default true, when it is true, will use `navigator.language` overwrite default
antd: true,
baseNavigator: true,
},
dynamicImport: {
loading: '@/components/PageLoading/index',
},
targets: {
ie: 11,
},
// umi routes: https://umijs.org/docs/routing
routes: [
{
path: '/login2',
component: '../pages/user/login2',
},
{
path: '/others',
component: '../pages/user/others',
},
{
path: '/user',
component: '../layouts/UserLayout',
routes: [
{
name: 'login',
path: '/user/login',
component: './user/login',
},
],
},
{
path: '/',
component: '../layouts/SecurityLayout',
routes: [
{
path: '/',
component: '../layouts/BasicLayout',
authority: ['admin', 'user'],
routes: [
{
path: '/',
redirect: '/welcome',
},
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './Welcome',
},
// {
// path: '/runTest',
// component: './runTest/Template',
// },
{
path: '/admin',
name: 'admin',
icon: 'crown',
component: './Admin',
authority: ['admin'],
routes: [
{
path: '/admin/sub-page',
name: 'sub-page',
icon: 'smile',
component: './Welcome',
authority: ['admin'],
},
],
},
{
name: 'welcome',
icon: 'table',
path: '/runTest',
component: './runTest/Template',
},
{
name: 'welcome',
icon: 'table',
path: '/runTest2',
component: './runTest/Template',
},
{
component: './404',
},
],
},
{
component: './404',
},
],
},
{
component: './404',
},
],
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
// ...darkTheme,
'primary-color': defaultSettings.primaryColor,
},
// @ts-ignore
title: false,
ignoreMomentLocale: true,
proxy: proxy[REACT_APP_ENV || 'dev'],
manifest: {
basePath: '/',
},
});
import { Settings as ProSettings } from '@ant-design/pro-layout';
type DefaultSettings = ProSettings & {
pwa: boolean;
};
const proSettings: DefaultSettings = {
navTheme: 'dark',
// 拂晓蓝
primaryColor: '#1890ff',
layout: 'sidemenu',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: false,
colorWeak: false,
menu: {
locale: true,
},
title: '这只是个测试',
pwa: false,
iconfontUrl: '',
};
export type { DefaultSettings };
export default proSettings;
/**
* 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
* The agent cannot take effect in the production environment
* so there is no configuration of the production environment
* For details, please see
* https://pro.ant.design/docs/deploy
*/
export default {
dev: {
'/api/': {
target: 'https://preview.pro.ant.design',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
test: {
'/api/': {
target: 'https://preview.pro.ant.design',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
pre: {
'/api/': {
target: 'your pre url',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
};
module.exports = {
testURL: 'http://localhost:8000',
testEnvironment: './tests/PuppeteerEnvironment',
verbose: false,
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
localStorage: null,
},
};
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
// eslint-disable-next-line import/no-extraneous-dependencies
import { Request, Response } from 'express';
import { parse } from 'url';
import { TableListItem, TableListParams } from '@/pages/ListTableList/data';
// mock tableListDataSource
const genList = (current: number, pageSize: number) => {
const tableListDataSource: TableListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
key: index,
disabled: i % 6 === 0,
href: 'https://ant.design',
avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
name: `TradeCode ${index}`,
owner: '曲丽丽',
desc: '这是一段描述',
callNo: Math.floor(Math.random() * 1000),
status: Math.floor(Math.random() * 10) % 4,
updatedAt: new Date(),
createdAt: new Date(),
progress: Math.ceil(Math.random() * 100),
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
let tableListDataSource = genList(1, 100);
function getRule(req: Request, res: Response, u: string) {
let realUrl = u;
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
realUrl = req.url;
}
const { current = 1, pageSize = 10 } = req.query;
const params = (parse(realUrl, true).query as unknown) as TableListParams;
let dataSource = [...tableListDataSource].slice(
((current as number) - 1) * (pageSize as number),
(current as number) * (pageSize as number),
);
const sorter = JSON.parse(params.sorter as any);
if (sorter) {
dataSource = dataSource.sort((prev, next) => {
let sortNumber = 0;
Object.keys(sorter).forEach((key) => {
if (sorter[key] === 'descend') {
if (prev[key] - next[key] > 0) {
sortNumber += -1;
} else {
sortNumber += 1;
}
return;
}
if (prev[key] - next[key] > 0) {
sortNumber += 1;
} else {
sortNumber += -1;
}
});
return sortNumber;
});
}
if (params.filter) {
const filter = JSON.parse(params.filter as any) as {
[key: string]: string[];
};
if (Object.keys(filter).length > 0) {
dataSource = dataSource.filter((item) => {
return Object.keys(filter).some((key) => {
if (!filter[key]) {
return true;
}
if (filter[key].includes(`${item[key]}`)) {
return true;
}
return false;
});
});
}
}
if (params.name) {
dataSource = dataSource.filter((data) => data.name.includes(params.name || ''));
}
const result = {
data: dataSource,
total: tableListDataSource.length,
success: true,
pageSize,
current: parseInt(`${params.currentPage}`, 10) || 1,
};
return res.json(result);
}
function postRule(req: Request, res: Response, u: string, b: Request) {
let realUrl = u;
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
realUrl = req.url;
}
const body = (b && b.body) || req.body;
const { method, name, desc, key } = body;
switch (method) {
/* eslint no-case-declarations:0 */
case 'delete':
tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1);
break;
case 'post':
(() => {
const i = Math.ceil(Math.random() * 10000);
const newRule = {
key: tableListDataSource.length,
href: 'https://ant.design',
avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
name,
owner: '曲丽丽',
desc,
callNo: Math.floor(Math.random() * 1000),
status: Math.floor(Math.random() * 10) % 2,
updatedAt: new Date(),
createdAt: new Date(),
progress: Math.ceil(Math.random() * 100),
};
tableListDataSource.unshift(newRule);
return res.json(newRule);
})();
return;
case 'update':
(() => {
let newRule = {};
tableListDataSource = tableListDataSource.map((item) => {
if (item.key === key) {
newRule = { ...item, desc, name };
return { ...item, desc, name };
}
return item;
});
return res.json(newRule);
})();
return;
default:
break;
}
const result = {
list: tableListDataSource,
pagination: {
total: tableListDataSource.length,
},
};
res.json(result);
}
export default {
'GET /api/rule': getRule,
'POST /api/rule': postRule,
};
import { Request, Response } from 'express';
const getNotices = (req: Request, res: Response) => {
res.json([
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
]);
};
export default {
'GET /api/notices': getNotices,
};
export default {
'/api/auth_routes': {
'/form/advanced-form': { authority: ['admin', 'user'] },
},
};
import { Request, Response } from 'express';
function getFakeCaptcha(req: Request, res: Response) {
return res.json('captcha-xxx');
}
// 代码中会兼容本地 service mock 以及部署站点的静态数据
export default {
// 支持值为 Object 和 Array
'GET /api/currentUser': {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'antdesign@alipay.com',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
tags: [
{
key: '0',
label: '很有想法的',
},
{
key: '1',
label: '专注设计',
},
{
key: '2',
label: '辣~',
},
{
key: '3',
label: '大长腿',
},
{
key: '4',
label: '川妹子',
},
{
key: '5',
label: '海纳百川',
},
],
notifyCount: 12,
unreadCount: 11,
country: 'China',
geographic: {
province: {
label: '浙江省',
key: '330000',
},
city: {
label: '杭州市',
key: '330100',
},
},
address: '西湖区工专路 77 号',
phone: '0752-268888888',
},
// GET POST 可省略
'GET /api/users': [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
],
'POST /api/login/account': (req: Request, res: Response) => {
const { password, userName, type } = req.body;
if (password === 'ant.design' && userName === 'admin') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
return;
}
if (password === 'ant.design' && userName === 'user') {
res.send({
status: 'ok',
type,
currentAuthority: 'user',
});
return;
}
if (type === 'mobile') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
return;
}
res.send({
status: 'error',
type,
currentAuthority: 'guest',
});
},
'POST /api/register': (req: Request, res: Response) => {
res.send({ status: 'ok', currentAuthority: 'user' });
},
'GET /api/500': (req: Request, res: Response) => {
res.status(500).send({
timestamp: 1513932555104,
status: 500,
error: 'error',
message: 'error',
path: '/base/category/list',
});
},
'GET /api/404': (req: Request, res: Response) => {
res.status(404).send({
timestamp: 1513932643431,
status: 404,
error: 'Not Found',
message: 'No message available',
path: '/base/category/list/2121212',
});
},
'GET /api/403': (req: Request, res: Response) => {
res.status(403).send({
timestamp: 1513932555104,
status: 403,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
'GET /api/401': (req: Request, res: Response) => {
res.status(401).send({
timestamp: 1513932555104,
status: 401,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
'GET /api/login/captcha': getFakeCaptcha,
};
{
"name": "ant-design-pro",
"version": "4.0.0",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
"analyze": "cross-env ANALYZE=1 umi build",
"build": "umi build",
"deploy": "npm run site && npm run gh-pages",
"dev": "npm run start:dev",
"fetch:blocks": "pro fetch-blocks && npm run prettier",
"gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "umi g tmp",
"lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
"lint:prettier": "prettier --check \"**/*\" --end-of-line auto",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"prettier": "prettier -c --write \"**/*\"",
"start": "umi dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"start:no-ui": "cross-env UMI_UI=none umi dev",
"start:pre": "cross-env REACT_APP_ENV=pre umi dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none umi dev",
"pretest": "node ./tests/beforeTest",
"test": "umi test",
"test:all": "node ./tests/run-tests.js",
"test:component": "umi test ./src/components",
"tsc": "tsc"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint-staged"
}
},
"lint-staged": {
"**/*.less": "stylelint --syntax less",
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": [
"prettier --write"
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
],
"dependencies": {
"@ant-design/icons": "^4.0.0",
"@ant-design/pro-layout": "^5.0.16",
"@ant-design/pro-table": "2.3.4",
"@bundled-es-modules/pdfjs-dist": "^2.2.228-alpha1",
"antd": "4.3.3",
"classnames": "^2.2.6",
"lodash": "^4.17.11",
"moment": "^2.25.3",
"omit.js": "^1.0.2",
"path-to-regexp": "2.4.0",
"pdfjs": "^2.3.9",
"qs": "^6.9.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-helmet-async": "^1.0.4",
"react-pdf-js": "^5.1.0",
"umi": "^3.1.2",
"umi-request": "^1.0.8",
"use-merge-value": "^1.0.1"
},
"devDependencies": {
"@ant-design/pro-cli": "^1.0.18",
"@types/classnames": "^2.2.7",
"@types/express": "^4.17.0",
"@types/history": "^4.7.2",
"@types/jest": "^26.0.0",
"@types/lodash": "^4.14.144",
"@types/qs": "^6.5.3",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.13",
"@umijs/fabric": "^2.0.5",
"@umijs/plugin-blocks": "^2.0.5",
"@umijs/preset-ant-design-pro": "^1.2.0",
"@umijs/preset-react": "^1.4.8",
"@umijs/preset-ui": "^2.0.9",
"carlo": "^0.9.46",
"chalk": "^4.0.0",
"cross-env": "^7.0.0",
"cross-port-killer": "^1.1.1",
"detect-installer": "^1.0.1",
"enzyme": "^3.11.0",
"eslint": "^7.1.0",
"express": "^4.17.1",
"gh-pages": "^3.0.0",
"husky": "^4.0.7",
"jsdom-global": "^3.0.2",
"lint-staged": "^10.0.0",
"mockjs": "^1.0.1-beta3",
"prettier": "^2.0.1",
"pro-download": "1.0.1",
"puppeteer-core": "^3.0.0",
"stylelint": "^13.0.0"
},
"engines": {
"node": ">=10.0.0"
},
"checkFiles": [
"src/**/*.js*",
"src/**/*.ts*",
"src/**/*.less",
"config/**/*.js*",
"scripts/**/*.js"
]
}
preview.pro.ant.design
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="图层_1" width="512" height="512" x="0" y="0" enable-background="new 0 0 512 512" version="1.1" viewBox="0 0 512 512" xml:space="preserve"><path fill-rule="evenodd" d="M259.119,233.588c0-3.644,0.041-7.289-0.008-10.932 c-0.111-8.558-4.697-13.308-13.231-13.486c-6.658-0.139-13.326,0.12-19.98-0.096c-3.292-0.107-4.247,0.995-4.24,4.266 c0.094,44.794,0.101,89.589-0.008,134.383c-0.009,3.492,1.346,4.154,4.407,4.113c9.99-0.135,19.985-0.18,29.972,0.019 c3.52,0.069,4.561-1.136,4.534-4.575c-0.138-16.817-0.124-33.637-0.037-50.455c0.053-10.153-0.512-20.322,0.426-30.457 c1.339-14.5,10.473-23.758,24.802-25.619c4.382-0.569,8.652-0.143,12.92,0.505c4.367,0.663,6.274-1.139,6.266-5.396 c-0.015-7.494-0.048-14.987,0.015-22.48c0.029-3.358-1.322-5.272-4.784-5.789c-13.2-1.97-24.368,1.438-32.905,12.103 C263.914,223.876,262.08,228.86,259.119,233.588z" clip-rule="evenodd"/><g><defs><path id="SVGID_1_" d="M56.964,256.003c0-30.332,0.092-60.665-0.105-90.996c-0.028-4.252,1.185-5.176,5.265-5.141 c25.497,0.221,51.007-0.295,76.491,0.323c20.853,0.505,39.051,8.125,50.951,25.966c14.972,22.448,16.14,46.757,4.473,70.786 c-11.114,22.892-31.459,32.606-56.012,33.989c-12.13,0.683-24.326,0.327-36.489,0.176c-3.462-0.043-4.55,0.903-4.514,4.451 c0.174,17.164-0.026,34.333,0.161,51.496c0.04,3.676-0.859,4.918-4.695,4.82c-10.326-0.262-20.668-0.277-30.994,0.006 c-3.947,0.109-4.622-1.318-4.606-4.882C57.021,316.668,56.964,286.335,56.964,256.003z M115.378,191.878 c-4.832,0-9.666,0.105-14.493-0.042c-2.639-0.08-3.862,0.506-3.844,3.544c0.121,20.324,0.09,40.65,0.026,60.975 c-0.007,2.423,0.494,3.632,3.281,3.58c9.16-0.174,18.349,0.301,27.481-0.248c20.562-1.234,31.673-12.484,32.665-32.392 c0.974-19.536-9.987-32.905-29.141-35.031C126.09,191.681,115.378,191.978,115.378,191.878z"/></defs><use fill-rule="evenodd" clip-rule="evenodd" overflow="visible" xlink:href="#SVGID_1_"/><clipPath id="SVGID_2_"><use overflow="visible" xlink:href="#SVGID_1_"/></clipPath><path fill-rule="evenodd" d="M56.964,256.003c0,30.332,0.056,60.665-0.075,90.996 c-0.016,3.563,0.659,4.991,4.606,4.882c10.325-0.283,20.667-0.268,30.994-0.006c3.836,0.098,4.735-1.145,4.695-4.82 c-0.188-17.163,0.013-34.332-0.161-51.496c-0.036-3.548,1.052-4.494,4.514-4.451c12.163,0.151,24.359,0.507,36.489-0.176 c24.552-1.383,44.897-11.097,56.012-33.989c11.667-24.029,10.5-48.338-4.473-70.786c-11.9-17.841-30.098-25.461-50.951-25.966 c-25.484-0.618-50.995-0.103-76.491-0.323c-4.08-0.035-5.292,0.889-5.265,5.141C57.056,195.339,56.964,225.671,56.964,256.003z" clip-path="url(#SVGID_2_)" clip-rule="evenodd"/></g><g><defs><path id="SVGID_3_" d="M315.446,281.626c0.099-19.011,3.621-36.469,15.474-51.155c15.867-19.659,37.477-25.831,61.573-23.923 c38.108,3.018,61.858,28.28,63.712,66.526c0.692,14.277-0.41,28.252-5.881,41.65c-7.521,18.418-20.696,30.726-39.764,36.411 c-18.27,5.447-36.605,5.267-54.348-1.839c-25.895-10.371-37.139-31.363-40.178-57.816 C315.637,288.012,315.61,284.501,315.446,281.626z M417.02,280.467c-0.092-9.835-1.27-19.53-5.888-28.481 c-5.233-10.144-13.333-15.962-25.039-16.006c-11.688-0.044-20.367,5.439-25.118,15.79c-8.715,18.984-8.698,38.444-0.095,57.46 c4.719,10.431,13.509,15.761,25.142,15.739c11.655-0.021,19.883-5.677,25.127-15.869 C415.757,300.142,416.912,290.442,417.02,280.467z"/></defs><use fill-rule="evenodd" clip-rule="evenodd" overflow="visible" xlink:href="#SVGID_3_"/><clipPath id="SVGID_4_"><use overflow="visible" xlink:href="#SVGID_3_"/></clipPath><path fill-rule="evenodd" d="M315.446,281.626c0.164,2.875,0.19,6.386,0.589,9.854 c3.039,26.453,14.283,47.445,40.178,57.816c17.742,7.105,36.078,7.286,54.348,1.839c19.067-5.686,32.242-17.993,39.764-36.411 c5.471-13.398,6.573-27.373,5.881-41.65c-1.854-38.247-25.604-63.509-63.712-66.526c-24.097-1.908-45.706,4.264-61.573,23.923 C319.067,245.157,315.545,262.615,315.446,281.626z" clip-path="url(#SVGID_4_)" clip-rule="evenodd"/></g><path fill-rule="evenodd" d="M259.119,233.588c2.961-4.728,4.795-9.712,8.146-13.899 c8.537-10.664,19.705-14.072,32.905-12.103c3.462,0.517,4.813,2.431,4.784,5.789c-0.063,7.493-0.029,14.987-0.015,22.48 c0.009,4.258-1.898,6.06-6.266,5.396c-4.268-0.648-8.538-1.074-12.92-0.505c-14.329,1.861-23.463,11.119-24.802,25.619 c-0.938,10.135-0.373,20.304-0.426,30.457c-0.087,16.818-0.101,33.638,0.037,50.455c0.027,3.439-1.014,4.645-4.534,4.575 c-9.987-0.198-19.982-0.153-29.972-0.019c-3.061,0.041-4.416-0.621-4.407-4.113c0.109-44.794,0.103-89.589,0.008-134.383 c-0.007-3.271,0.948-4.373,4.24-4.266c6.654,0.216,13.322-0.042,19.98,0.096c8.535,0.178,13.12,4.928,13.231,13.486 C259.16,226.3,259.119,229.944,259.119,233.588z" clip-rule="evenodd"/></svg>
\ No newline at end of file
File added
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>
\ No newline at end of file
import React from 'react';
import { Result } from 'antd';
import check, { IAuthorityType } from './CheckPermissions';
import AuthorizedRoute from './AuthorizedRoute';
import Secured from './Secured';
interface AuthorizedProps {
authority: IAuthorityType;
noMatch?: React.ReactNode;
}
type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & {
Secured: typeof Secured;
check: typeof check;
AuthorizedRoute: typeof AuthorizedRoute;
};
const Authorized: React.FunctionComponent<AuthorizedProps> = ({
children,
authority,
noMatch = (
<Result
status="403"
title="403"
subTitle="Sorry, you are not authorized to access this page."
/>
),
}) => {
const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children;
const dom = check(authority, childrenRender, noMatch);
return <>{dom}</>;
};
export default Authorized as IAuthorizedType;
import { Redirect, Route } from 'umi';
import React from 'react';
import Authorized from './Authorized';
import { IAuthorityType } from './CheckPermissions';
interface AuthorizedRouteProps {
currentAuthority: string;
component: React.ComponentClass<any, any>;
render: (props: any) => React.ReactNode;
redirectPath: string;
authority: IAuthorityType;
}
const AuthorizedRoute: React.SFC<AuthorizedRouteProps> = ({
component: Component,
render,
authority,
redirectPath,
...rest
}) => (
<Authorized
authority={authority}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route
{...rest}
render={(props: any) => (Component ? <Component {...props} /> : render(props))}
/>
</Authorized>
);
export default AuthorizedRoute;
import React from 'react';
import { CURRENT } from './renderAuthorize';
// eslint-disable-next-line import/no-cycle
import PromiseRender from './PromiseRender';
export type IAuthorityType =
| undefined
| string
| string[]
| Promise<boolean>
| ((currentAuthority: string | string[]) => IAuthorityType);
/**
* 通用权限检查方法
* Common check permissions method
* @param { 权限判定 | Permission judgment } authority
* @param { 你的权限 | Your permission description } currentAuthority
* @param { 通过的组件 | Passing components } target
* @param { 未通过的组件 | no pass components } Exception
*/
const checkPermissions = <T, K>(
authority: IAuthorityType,
currentAuthority: string | string[],
target: T,
Exception: K,
): T | K | React.ReactNode => {
// 没有判定权限.默认查看所有
// Retirement authority, return target;
if (!authority) {
return target;
}
// 数组处理
if (Array.isArray(authority)) {
if (Array.isArray(currentAuthority)) {
if (currentAuthority.some((item) => authority.includes(item))) {
return target;
}
} else if (authority.includes(currentAuthority)) {
return target;
}
return Exception;
}
// string 处理
if (typeof authority === 'string') {
if (Array.isArray(currentAuthority)) {
if (currentAuthority.some((item) => authority === item)) {
return target;
}
} else if (authority === currentAuthority) {
return target;
}
return Exception;
}
// Promise 处理
if (authority instanceof Promise) {
return <PromiseRender<T, K> ok={target} error={Exception} promise={authority} />;
}
// Function 处理
if (typeof authority === 'function') {
try {
const bool = authority(currentAuthority);
// 函数执行后返回值是 Promise
if (bool instanceof Promise) {
return <PromiseRender<T, K> ok={target} error={Exception} promise={bool} />;
}
if (bool) {
return target;
}
return Exception;
} catch (error) {
throw error;
}
}
throw new Error('unsupported parameters');
};
export { checkPermissions };
function check<T, K>(authority: IAuthorityType, target: T, Exception: K): T | K | React.ReactNode {
return checkPermissions<T, K>(authority, CURRENT, target, Exception);
}
export default check;
import React from 'react';
import { Spin } from 'antd';
import isEqual from 'lodash/isEqual';
import { isComponentClass } from './Secured';
// eslint-disable-next-line import/no-cycle
interface PromiseRenderProps<T, K> {
ok: T;
error: K;
promise: Promise<boolean>;
}
interface PromiseRenderState {
component: React.ComponentClass | React.FunctionComponent;
}
export default class PromiseRender<T, K> extends React.Component<
PromiseRenderProps<T, K>,
PromiseRenderState
> {
state: PromiseRenderState = {
component: () => null,
};
componentDidMount() {
this.setRenderComponent(this.props);
}
shouldComponentUpdate = (nextProps: PromiseRenderProps<T, K>, nextState: PromiseRenderState) => {
const { component } = this.state;
if (!isEqual(nextProps, this.props)) {
this.setRenderComponent(nextProps);
}
if (nextState.component !== component) return true;
return false;
};
// set render Component : ok or error
setRenderComponent(props: PromiseRenderProps<T, K>) {
const ok = this.checkIsInstantiation(props.ok);
const error = this.checkIsInstantiation(props.error);
props.promise
.then(() => {
this.setState({
component: ok,
});
return true;
})
.catch(() => {
this.setState({
component: error,
});
});
}
// Determine whether the incoming component has been instantiated
// AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
checkIsInstantiation = (
target: React.ReactNode | React.ComponentClass,
): React.FunctionComponent => {
if (isComponentClass(target)) {
const Target = target as React.ComponentClass;
return (props: any) => <Target {...props} />;
}
if (React.isValidElement(target)) {
return (props: any) => React.cloneElement(target, props);
}
return () => target as React.ReactNode & null;
};
render() {
const { component: Component } = this.state;
const { ok, error, promise, ...rest } = this.props;
return Component ? (
<Component {...rest} />
) : (
<div
style={{
width: '100%',
height: '100%',
margin: 'auto',
paddingTop: 50,
textAlign: 'center',
}}
>
<Spin size="large" />
</div>
);
}
}
import React from 'react';
import CheckPermissions from './CheckPermissions';
/**
* 默认不能访问任何页面
* default is "NULL"
*/
const Exception403 = () => 403;
export const isComponentClass = (component: React.ComponentClass | React.ReactNode): boolean => {
if (!component) return false;
const proto = Object.getPrototypeOf(component);
if (proto === React.Component || proto === Function.prototype) return true;
return isComponentClass(proto);
};
// Determine whether the incoming component has been instantiated
// AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
const checkIsInstantiation = (target: React.ComponentClass | React.ReactNode) => {
if (isComponentClass(target)) {
const Target = target as React.ComponentClass;
return (props: any) => <Target {...props} />;
}
if (React.isValidElement(target)) {
return (props: any) => React.cloneElement(target, props);
}
return () => target;
};
/**
* 用于判断是否拥有权限访问此 view 权限
* authority 支持传入 string, () => boolean | Promise
* e.g. 'user' 只有 user 用户能访问
* e.g. 'user,admin' user 和 admin 都能访问
* e.g. ()=>boolean 返回true能访问,返回false不能访问
* e.g. Promise then 能访问 catch不能访问
* e.g. authority support incoming string, () => boolean | Promise
* e.g. 'user' only user user can access
* e.g. 'user, admin' user and admin can access
* e.g. () => boolean true to be able to visit, return false can not be accessed
* e.g. Promise then can not access the visit to catch
* @param {string | function | Promise} authority
* @param {ReactNode} error 非必需参数
*/
const authorize = (authority: string, error?: React.ReactNode) => {
/**
* conversion into a class
* 防止传入字符串时找不到staticContext造成报错
* String parameters can cause staticContext not found error
*/
let classError: boolean | React.FunctionComponent = false;
if (error) {
classError = (() => error) as React.FunctionComponent;
}
if (!authority) {
throw new Error('authority is required');
}
return function decideAuthority(target: React.ComponentClass | React.ReactNode) {
const component = CheckPermissions(authority, target, classError || Exception403);
return checkIsInstantiation(component);
};
};
export default authorize;
import Authorized from './Authorized';
import Secured from './Secured';
import check from './CheckPermissions';
import renderAuthorize from './renderAuthorize';
Authorized.Secured = Secured;
Authorized.check = check;
const RenderAuthorize = renderAuthorize(Authorized);
export default RenderAuthorize;
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable import/no-mutable-exports */
let CURRENT: string | string[] = 'NULL';
type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
/**
* use authority or getAuthority
* @param {string|()=>String} currentAuthority
*/
const renderAuthorize = <T>(Authorized: T): ((currentAuthority: CurrentAuthorityType) => T) => (
currentAuthority: CurrentAuthorityType,
): T => {
if (currentAuthority) {
if (typeof currentAuthority === 'function') {
CURRENT = currentAuthority();
}
if (
Object.prototype.toString.call(currentAuthority) === '[object String]' ||
Array.isArray(currentAuthority)
) {
CURRENT = currentAuthority as string[];
}
} else {
CURRENT = 'NULL';
}
return Authorized;
};
export { CURRENT };
export default <T>(Authorized: T) => renderAuthorize<T>(Authorized);
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
import { Avatar, Menu, Spin } from 'antd';
import { ClickParam } from 'antd/es/menu';
import React from 'react';
import { history, ConnectProps, connect } from 'umi';
import { ConnectState } from '@/models/connect';
import { CurrentUser } from '@/models/user';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
currentUser?: CurrentUser;
menu?: boolean;
}
class AvatarDropdown extends React.Component<GlobalHeaderRightProps> {
onMenuClick = (event: ClickParam) => {
const { key } = event;
if (key === 'logout') {
const { dispatch } = this.props;
if (dispatch) {
dispatch({
type: 'login/logout',
});
}
return;
}
history.push(`/account/${key}`);
};
render(): React.ReactNode {
const {
currentUser = {
avatar: '',
name: '',
},
menu,
} = this.props;
const menuHeaderDropdown = (
<Menu className={styles.menu} selectedKeys={[]} onClick={this.onMenuClick}>
{menu && (
<Menu.Item key="center">
<UserOutlined />
个人中心
</Menu.Item>
)}
{menu && (
<Menu.Item key="settings">
<SettingOutlined />
个人设置
</Menu.Item>
)}
{menu && <Menu.Divider />}
<Menu.Item key="logout">
<LogoutOutlined />
退出登录
</Menu.Item>
</Menu>
);
return currentUser && currentUser.name ? (
<HeaderDropdown overlay={menuHeaderDropdown}>
<span className={`${styles.action} ${styles.account}`}>
<Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
<span className={styles.name}>{currentUser.name}</span>
</span>
</HeaderDropdown>
) : (
<span className={`${styles.action} ${styles.account}`}>
<Spin
size="small"
style={{
marginLeft: 8,
marginRight: 8,
}}
/>
</span>
);
}
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.currentUser,
}))(AvatarDropdown);
import React, { Component } from 'react';
import { connect, ConnectProps } from 'umi';
import { Tag, message } from 'antd';
import groupBy from 'lodash/groupBy';
import moment from 'moment';
import { NoticeItem } from '@/models/global';
import { CurrentUser } from '@/models/user';
import { ConnectState } from '@/models/connect';
import NoticeIcon from '../NoticeIcon';
import styles from './index.less';
export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
notices?: NoticeItem[];
currentUser?: CurrentUser;
fetchingNotices?: boolean;
onNoticeVisibleChange?: (visible: boolean) => void;
onNoticeClear?: (tabName?: string) => void;
}
class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
componentDidMount() {
const { dispatch } = this.props;
if (dispatch) {
dispatch({
type: 'global/fetchNotices',
});
}
}
changeReadState = (clickedItem: NoticeItem): void => {
const { id } = clickedItem;
const { dispatch } = this.props;
if (dispatch) {
dispatch({
type: 'global/changeNoticeReadState',
payload: id,
});
}
};
handleNoticeClear = (title: string, key: string) => {
const { dispatch } = this.props;
message.success(`${'清空了'} ${title}`);
if (dispatch) {
dispatch({
type: 'global/clearNotices',
payload: key,
});
}
};
getNoticeData = (): {
[key: string]: NoticeItem[];
} => {
const { notices = [] } = this.props;
if (!notices || notices.length === 0 || !Array.isArray(notices)) {
return {};
}
const newNotices = notices.map((notice) => {
const newNotice = { ...notice };
if (newNotice.datetime) {
newNotice.datetime = moment(notice.datetime as string).fromNow();
}
if (newNotice.id) {
newNotice.key = newNotice.id;
}
if (newNotice.extra && newNotice.status) {
const color = {
todo: '',
processing: 'blue',
urgent: 'red',
doing: 'gold',
}[newNotice.status];
newNotice.extra = (
<Tag
color={color}
style={{
marginRight: 0,
}}
>
{newNotice.extra}
</Tag>
);
}
return newNotice;
});
return groupBy(newNotices, 'type');
};
getUnreadData = (noticeData: { [key: string]: NoticeItem[] }) => {
const unreadMsg: {
[key: string]: number;
} = {};
Object.keys(noticeData).forEach((key) => {
const value = noticeData[key];
if (!unreadMsg[key]) {
unreadMsg[key] = 0;
}
if (Array.isArray(value)) {
unreadMsg[key] = value.filter((item) => !item.read).length;
}
});
return unreadMsg;
};
render() {
const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
const noticeData = this.getNoticeData();
const unreadMsg = this.getUnreadData(noticeData);
return (
<NoticeIcon
className={styles.action}
count={currentUser && currentUser.unreadCount}
onItemClick={(item) => {
this.changeReadState(item as NoticeItem);
}}
loading={fetchingNotices}
clearText="清空"
viewMoreText="查看更多"
onClear={this.handleNoticeClear}
onPopupVisibleChange={onNoticeVisibleChange}
onViewMore={() => message.info('Click on view more')}
clearClose
>
<NoticeIcon.Tab
tabKey="notification"
count={unreadMsg.notification}
list={noticeData.notification}
title="通知"
emptyText="你已查看所有通知"
showViewMore
/>
<NoticeIcon.Tab
tabKey="message"
count={unreadMsg.message}
list={noticeData.message}
title="消息"
emptyText="您已读完所有消息"
showViewMore
/>
<NoticeIcon.Tab
tabKey="event"
title="待办"
emptyText="你已完成所有待办"
count={unreadMsg.event}
list={noticeData.event}
showViewMore
/>
</NoticeIcon>
);
}
}
export default connect(({ user, global, loading }: ConnectState) => ({
currentUser: user.currentUser,
collapsed: global.collapsed,
fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
fetchingNotices: loading.effects['global/fetchNotices'],
notices: global.notices,
}))(GlobalHeaderRight);
import { Tooltip, Tag } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import React from 'react';
import { connect, ConnectProps } from 'umi';
import { ConnectState } from '@/models/connect';
import Avatar from './AvatarDropdown';
import HeaderSearch from '../HeaderSearch';
import SelectLang from '../SelectLang';
import styles from './index.less';
export type SiderTheme = 'light' | 'dark';
export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
theme?: SiderTheme | 'realDark';
layout: 'sidemenu' | 'topmenu';
}
const ENVTagColor = {
dev: 'orange',
test: 'green',
pre: '#87d068',
};
const GlobalHeaderRight: React.SFC<GlobalHeaderRightProps> = (props) => {
const { theme, layout } = props;
let className = styles.right;
if (theme === 'dark' && layout === 'topmenu') {
className = `${styles.right} ${styles.dark}`;
}
return (
<div className={className}>
<HeaderSearch
className={`${styles.action} ${styles.search}`}
placeholder="站内搜索"
defaultValue="umi ui"
options={[
{ 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>,
value: 'Ant Design',
},
{
label: <a href="https://protable.ant.design/">Pro Table</a>,
value: 'Pro Table',
},
{
label: <a href="https://prolayout.ant.design/">Pro Layout</a>,
value: 'Pro Layout',
},
]}
// onSearch={value => {
// //console.log('input', value);
// }}
/>
<Tooltip title="使用文档">
<a
target="_blank"
href="https://pro.ant.design/docs/getting-started"
rel="noopener noreferrer"
className={styles.action}
>
<QuestionCircleOutlined />
</a>
</Tooltip>
<Avatar />
{REACT_APP_ENV && (
<span>
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
</span>
)}
<SelectLang className={styles.action} />
</div>
);
};
export default connect(({ settings }: ConnectState) => ({
theme: settings.navTheme,
layout: settings.layout,
}))(GlobalHeaderRight);
@import '~antd/es/style/themes/default.less';
@pro-header-hover-bg: rgba(0, 0, 0, 0.025);
.menu {
:global(.anticon) {
margin-right: 8px;
}
:global(.ant-dropdown-menu-item) {
min-width: 160px;
}
}
.right {
display: flex;
float: right;
height: @layout-header-height;
margin-left: auto;
overflow: hidden;
.action {
display: flex;
align-items: center;
height: 100%;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
> span {
color: @text-color;
vertical-align: middle;
}
&:hover {
background: @pro-header-hover-bg;
}
&:global(.opened) {
background: @pro-header-hover-bg;
}
}
.search {
padding: 0 12px;
&:hover {
background: transparent;
}
}
.account {
.avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgba(255, 255, 255, 0.85);
}
}
}
.dark {
.action {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
}
&:hover,
&:global(.opened) {
background: @primary-color;
}
}
}
:global(.ant-pro-global-header) {
.dark {
.action {
color: @text-color;
> span {
color: @text-color;
}
&:hover {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
}
}
}
}
}
@media only screen and (max-width: @screen-md) {
:global(.ant-divider-vertical) {
vertical-align: unset;
}
.name {
display: none;
}
.right {
position: absolute;
top: 0;
right: 12px;
.account {
.avatar {
margin-right: 0;
}
}
.search {
display: none;
}
}
}
@import '~antd/es/style/themes/default.less';
.container > * {
background-color: @popover-bg;
border-radius: 4px;
box-shadow: @shadow-1-down;
}
@media screen and (max-width: @screen-xs) {
.container {
width: 100% !important;
}
.container > * {
border-radius: 0 !important;
}
}
import { DropDownProps } from 'antd/es/dropdown';
import { Dropdown } from 'antd';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
declare type OverlayFunc = () => React.ReactNode;
export interface HeaderDropdownProps extends Omit<DropDownProps, 'overlay'> {
overlayClassName?: string;
overlay: React.ReactNode | OverlayFunc | any;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
}
const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, ...restProps }) => (
<Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} />
);
export default HeaderDropdown;
@import '~antd/es/style/themes/default.less';
.headerSearch {
.input {
width: 0;
min-width: 0;
overflow: hidden;
background: transparent;
border-radius: 0;
transition: width 0.3s, margin-left 0.3s;
:global(.ant-select-selection) {
background: transparent;
}
input {
padding-right: 0;
padding-left: 0;
border: 0;
box-shadow: none !important;
}
&,
&:hover,
&:focus {
border-bottom: 1px solid @border-color-base;
}
&.show {
width: 210px;
margin-left: 8px;
}
}
}
import { SearchOutlined } from '@ant-design/icons';
import { AutoComplete, Input } from 'antd';
import useMergeValue from 'use-merge-value';
import { AutoCompleteProps } from 'antd/es/auto-complete';
import React, { useRef } from 'react';
import classNames from 'classnames';
import styles from './index.less';
export interface HeaderSearchProps {
onSearch?: (value?: string) => void;
onChange?: (value?: string) => void;
onVisibleChange?: (b: boolean) => void;
className?: string;
placeholder?: string;
options: AutoCompleteProps['options'];
defaultOpen?: boolean;
open?: boolean;
defaultValue?: string;
value?: string;
}
const HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
const {
className,
defaultValue,
onVisibleChange,
placeholder,
open,
defaultOpen,
...restProps
} = props;
const inputRef = useRef<Input | null>(null);
const [value, setValue] = useMergeValue<string | undefined>(defaultValue, {
value: props.value,
onChange: props.onChange,
});
const [searchMode, setSearchMode] = useMergeValue(defaultOpen || false, {
value: props.open,
onChange: onVisibleChange,
});
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<div
className={classNames(className, styles.headerSearch)}
onClick={() => {
setSearchMode(true);
if (searchMode && inputRef.current) {
inputRef.current.focus();
}
}}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
if (onVisibleChange) {
onVisibleChange(searchMode);
}
}
}}
>
<SearchOutlined
key="Icon"
style={{
cursor: 'pointer',
}}
/>
<AutoComplete
key="AutoComplete"
className={inputClass}
value={value}
style={{
height: 28,
marginTop: -6,
}}
options={restProps.options}
onChange={setValue}
>
<Input
ref={inputRef}
defaultValue={defaultValue}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={(e) => {
if (e.key === 'Enter') {
if (restProps.onSearch) {
restProps.onSearch(value);
}
}
}}
onBlur={() => {
setSearchMode(false);
}}
/>
</AutoComplete>
</div>
);
};
export default HeaderSearch;
@import '~antd/es/style/themes/default.less';
.list {
max-height: 400px;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
.item {
padding-right: 24px;
padding-left: 24px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s;
.meta {
width: 100%;
}
.avatar {
margin-top: 4px;
background: @component-background;
}
.iconElement {
font-size: 32px;
}
&.read {
opacity: 0.4;
}
&:last-child {
border-bottom: 0;
}
&:hover {
background: @primary-1;
}
.title {
margin-bottom: 8px;
font-weight: normal;
}
.description {
font-size: 12px;
line-height: @line-height-base;
}
.datetime {
margin-top: 4px;
font-size: 12px;
line-height: @line-height-base;
}
.extra {
float: right;
margin-top: -1.5px;
margin-right: 0;
color: @text-color-secondary;
font-weight: normal;
}
}
.loadMore {
padding: 8px 0;
color: @primary-6;
text-align: center;
cursor: pointer;
&.loadedAll {
color: rgba(0, 0, 0, 0.25);
cursor: unset;
}
}
}
.notFound {
padding: 73px 0 88px;
color: @text-color-secondary;
text-align: center;
img {
display: inline-block;
height: 76px;
margin-bottom: 16px;
}
}
.bottomBar {
height: 46px;
color: @text-color;
line-height: 46px;
text-align: center;
border-top: 1px solid @border-color-split;
border-radius: 0 0 @border-radius-base @border-radius-base;
transition: all 0.3s;
div {
display: inline-block;
width: 50%;
cursor: pointer;
transition: all 0.3s;
user-select: none;
&:only-child {
width: 100%;
}
&:not(:only-child):last-child {
border-left: 1px solid @border-color-split;
}
}
}
import { Avatar, List } from 'antd';
import React from 'react';
import classNames from 'classnames';
import { NoticeIconData } from './index';
import styles from './NoticeList.less';
export interface NoticeIconTabProps {
count?: number;
name?: string;
showClear?: boolean;
showViewMore?: boolean;
style?: React.CSSProperties;
title: string;
tabKey: string;
data?: NoticeIconData[];
onClick?: (item: NoticeIconData) => void;
onClear?: () => void;
emptyText?: string;
clearText?: string;
viewMoreText?: string;
list: NoticeIconData[];
onViewMore?: (e: any) => void;
}
const NoticeList: React.SFC<NoticeIconTabProps> = ({
data = [],
onClick,
onClear,
title,
onViewMore,
emptyText,
showClear = true,
clearText,
viewMoreText,
showViewMore = false,
}) => {
if (!data || data.length === 0) {
return (
<div className={styles.notFound}>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
alt="not found"
/>
<div>{emptyText}</div>
</div>
);
}
return (
<div>
<List<NoticeIconData>
className={styles.list}
dataSource={data}
renderItem={(item, i) => {
const itemCls = classNames(styles.item, {
[styles.read]: item.read,
});
// eslint-disable-next-line no-nested-ternary
const leftIcon = item.avatar ? (
typeof item.avatar === 'string' ? (
<Avatar className={styles.avatar} src={item.avatar} />
) : (
<span className={styles.iconElement}>{item.avatar}</span>
)
) : null;
return (
<List.Item
className={itemCls}
key={item.key || i}
onClick={() => onClick && onClick(item)}
>
<List.Item.Meta
className={styles.meta}
avatar={leftIcon}
title={
<div className={styles.title}>
{item.title}
<div className={styles.extra}>{item.extra}</div>
</div>
}
description={
<div>
<div className={styles.description}>{item.description}</div>
<div className={styles.datetime}>{item.datetime}</div>
</div>
}
/>
</List.Item>
);
}}
/>
<div className={styles.bottomBar}>
{showClear ? (
<div onClick={onClear}>
{clearText} {title}
</div>
) : null}
{showViewMore ? (
<div
onClick={(e) => {
if (onViewMore) {
onViewMore(e);
}
}}
>
{viewMoreText}
</div>
) : null}
</div>
</div>
);
};
export default NoticeList;
@import '~antd/es/style/themes/default.less';
.popover {
position: relative;
width: 336px;
}
.noticeButton {
display: inline-block;
cursor: pointer;
transition: all 0.3s;
}
.icon {
padding: 4px;
vertical-align: middle;
}
.badge {
font-size: 16px;
}
.tabs {
:global {
.ant-tabs-nav-list {
margin: auto;
}
.ant-tabs-nav-scroll {
text-align: center;
}
.ant-tabs-bar {
margin-bottom: 0;
}
}
}
import { BellOutlined } from '@ant-design/icons';
import { Badge, Spin, Tabs } from 'antd';
import useMergeValue from 'use-merge-value';
import React from 'react';
import classNames from 'classnames';
import NoticeList, { NoticeIconTabProps } from './NoticeList';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
const { TabPane } = Tabs;
export interface NoticeIconData {
avatar?: string | React.ReactNode;
title?: React.ReactNode;
description?: React.ReactNode;
datetime?: React.ReactNode;
extra?: React.ReactNode;
style?: React.CSSProperties;
key?: string | number;
read?: boolean;
}
export interface NoticeIconProps {
count?: number;
bell?: React.ReactNode;
className?: string;
loading?: boolean;
onClear?: (tabName: string, tabKey: string) => void;
onItemClick?: (item: NoticeIconData, tabProps: NoticeIconTabProps) => void;
onViewMore?: (tabProps: NoticeIconTabProps, e: MouseEvent) => void;
onTabChange?: (tabTile: string) => void;
style?: React.CSSProperties;
onPopupVisibleChange?: (visible: boolean) => void;
popupVisible?: boolean;
clearText?: string;
viewMoreText?: string;
clearClose?: boolean;
emptyImage?: string;
children: React.ReactElement<NoticeIconTabProps>[];
}
const NoticeIcon: React.FC<NoticeIconProps> & {
Tab: typeof NoticeList;
} = (props) => {
const getNotificationBox = (): React.ReactNode => {
const {
children,
loading,
onClear,
onTabChange,
onItemClick,
onViewMore,
clearText,
viewMoreText,
} = props;
if (!children) {
return null;
}
const panes: React.ReactNode[] = [];
React.Children.forEach(children, (child: React.ReactElement<NoticeIconTabProps>): void => {
if (!child) {
return;
}
const { list, title, count, tabKey, showClear, showViewMore } = child.props;
const len = list && list.length ? list.length : 0;
const msgCount = count || count === 0 ? count : len;
const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title;
panes.push(
<TabPane tab={tabTitle} key={tabKey}>
<NoticeList
{...child.props}
clearText={clearText}
viewMoreText={viewMoreText}
data={list}
onClear={(): void => onClear && onClear(title, tabKey)}
onClick={(item): void => onItemClick && onItemClick(item, child.props)}
onViewMore={(event): void => onViewMore && onViewMore(child.props, event)}
showClear={showClear}
showViewMore={showViewMore}
title={title}
/>
</TabPane>,
);
});
return (
<Spin spinning={loading} delay={300}>
<Tabs className={styles.tabs} onChange={onTabChange}>
{panes}
</Tabs>
</Spin>
);
};
const { className, count, bell } = props;
const [visible, setVisible] = useMergeValue<boolean>(false, {
value: props.popupVisible,
onChange: props.onPopupVisibleChange,
});
const noticeButtonClass = classNames(className, styles.noticeButton);
const notificationBox = getNotificationBox();
const NoticeBellIcon = bell || <BellOutlined className={styles.icon} />;
const trigger = (
<span className={classNames(noticeButtonClass, { opened: visible })}>
<Badge count={count} style={{ boxShadow: 'none' }} className={styles.badge}>
{NoticeBellIcon}
</Badge>
</span>
);
if (!notificationBox) {
return trigger;
}
return (
<HeaderDropdown
placement="bottomRight"
overlay={notificationBox}
overlayClassName={styles.popover}
trigger={['click']}
visible={visible}
onVisibleChange={setVisible}
>
{trigger}
</HeaderDropdown>
);
};
NoticeIcon.defaultProps = {
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
};
NoticeIcon.Tab = NoticeList;
export default NoticeIcon;
import { PageLoading } from '@ant-design/pro-layout';
// loading components from code split
// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
export default PageLoading;
@import '~antd/es/style/themes/default.less';
.menu {
:global(.anticon) {
margin-right: 8px;
}
:global(.ant-dropdown-menu-item) {
min-width: 160px;
}
}
.dropDown {
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
> span {
font-size: 16px !important;
transform: none !important;
svg {
position: relative;
top: -1px;
}
}
}
import { GlobalOutlined } from '@ant-design/icons';
import { Menu } from 'antd';
import { getLocale, setLocale } from 'umi';
import { ClickParam } from 'antd/es/menu';
import React from 'react';
import classNames from 'classnames';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
interface SelectLangProps {
className?: string;
}
const SelectLang: React.FC<SelectLangProps> = (props) => {
const { className } = props;
const selectedLang = getLocale();
const changeLang = ({ key }: ClickParam): void => setLocale(key);
const locales = ['zh-CN', 'zh-TW', 'en-US', 'pt-BR'];
const languageLabels = {
'zh-CN': '简体中文',
'zh-TW': '繁体中文',
'en-US': 'English',
'pt-BR': 'Português',
};
const languageIcons = {
'zh-CN': '🇨🇳',
'zh-TW': '🇭🇰',
'en-US': '🇺🇸',
'pt-BR': '🇧🇷',
};
const langMenu = (
<Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={changeLang}>
{locales.map((locale) => (
<Menu.Item key={locale}>
<span role="img" aria-label={languageLabels[locale]}>
{languageIcons[locale]}
</span>{' '}
{languageLabels[locale]}
</Menu.Item>
))}
</Menu>
);
return (
<HeaderDropdown overlay={langMenu} placement="bottomRight">
<span className={classNames(styles.dropDown, className)}>
<GlobalOutlined title="语言" />
</span>
</HeaderDropdown>
);
};
export default SelectLang;
//使用说明如下
// var ArrayToPingYin = require('./ArrayToPingYin');
// var beArray=["美国","加拿大","XO"];
// console.log(ArrayToPingYin.get(beArray));
var strChineseFirstPY = "YDYQSXMWZSSXJBYMGCCZQPSSQBYCDSCDQLDYLYBSSJGYZZJJFKCCLZDHWDWZJLJPFYYNWJJTMYHZWZHFLZPPQHGSCYYYNJQYXXGJHHSDSJNKKTMOMLCRXYPSNQSECCQZGGLLYJLMYZZSECYKYYHQWJSSGGYXYZYJWWKDJHYCHMYXJTLXJYQBYXZLDWRDJRWYSRLDZJPCBZJJBRCFTLECZSTZFXXZHTRQHYBDLYCZSSYMMRFMYQZPWWJJYFCRWFDFZQPYDDWYXKYJAWJFFXYPSFTZYHHYZYSWCJYXSCLCXXWZZXNBGNNXBXLZSZSBSGPYSYZDHMDZBQBZCWDZZYYTZHBTSYYBZGNTNXQYWQSKBPHHLXGYBFMJEBJHHGQTJCYSXSTKZHLYCKGLYSMZXYALMELDCCXGZYRJXSDLTYZCQKCNNJWHJTZZCQLJSTSTBNXBTYXCEQXGKWJYFLZQLYHYXSPSFXLMPBYSXXXYDJCZYLLLSJXFHJXPJBTFFYABYXBHZZBJYZLWLCZGGBTSSMDTJZXPTHYQTGLJSCQFZKJZJQNLZWLSLHDZBWJNCJZYZSQQYCQYRZCJJWYBRTWPYFTWEXCSKDZCTBZHYZZYYJXZCFFZZMJYXXSDZZOTTBZLQWFCKSZSXFYRLNYJMBDTHJXSQQCCSBXYYTSYFBXDZTGBCNSLCYZZPSAZYZZSCJCSHZQYDXLBPJLLMQXTYDZXSQJTZPXLCGLQTZWJBHCTSYJSFXYEJJTLBGXSXJMYJQQPFZASYJNTYDJXKJCDJSZCBARTDCLYJQMWNQNCLLLKBYBZZSYHQQLTWLCCXTXLLZNTYLNEWYZYXCZXXGRKRMTCNDNJTSYYSSDQDGHSDBJGHRWRQLYBGLXHLGTGXBQJDZPYJSJYJCTMRNYMGRZJCZGJMZMGXMPRYXKJNYMSGMZJYMKMFXMLDTGFBHCJHKYLPFMDXLQJJSMTQGZSJLQDLDGJYCALCMZCSDJLLNXDJFFFFJCZFMZFFPFKHKGDPSXKTACJDHHZDDCRRCFQYJKQCCWJDXHWJLYLLZGCFCQDSMLZPBJJPLSBCJGGDCKKDEZSQCCKJGCGKDJTJDLZYCXKLQSCGJCLTFPCQCZGWPJDQYZJJBYJHSJDZWGFSJGZKQCCZLLPSPKJGQJHZZLJPLGJGJJTHJJYJZCZMLZLYQBGJWMLJKXZDZNJQSYZMLJLLJKYWXMKJLHSKJGBMCLYYMKXJQLBMLLKMDXXKWYXYSLMLPSJQQJQXYXFJTJDXMXXLLCXQBSYJBGWYMBGGBCYXPJYGPEPFGDJGBHBNSQJYZJKJKHXQFGQZKFHYGKHDKLLSDJQXPQYKYBNQSXQNSZSWHBSXWHXWBZZXDMNSJBSBKBBZKLYLXGWXDRWYQZMYWSJQLCJXXJXKJEQXSCYETLZHLYYYSDZPAQYZCMTLSHTZCFYZYXYLJSDCJQAGYSLCQLYYYSHMRQQKLDXZSCSSSYDYCJYSFSJBFRSSZQSBXXPXJYSDRCKGJLGDKZJZBDKTCSYQPYHSTCLDJDHMXMCGXYZHJDDTMHLTXZXYLYMOHYJCLTYFBQQXPFBDFHHTKSQHZYYWCNXXCRWHOWGYJLEGWDQCWGFJYCSNTMYTOLBYGWQWESJPWNMLRYDZSZTXYQPZGCWXHNGPYXSHMYQJXZTDPPBFYHZHTJYFDZWKGKZBLDNTSXHQEEGZZYLZMMZYJZGXZXKHKSTXNXXWYLYAPSTHXDWHZYMPXAGKYDXBHNHXKDPJNMYHYLPMGOCSLNZHKXXLPZZLBMLSFBHHGYGYYGGBHSCYAQTYWLXTZQCEZYDQDQMMHTKLLSZHLSJZWFYHQSWSCWLQAZYNYTLSXTHAZNKZZSZZLAXXZWWCTGQQTDDYZTCCHYQZFLXPSLZYGPZSZNGLNDQTBDLXGTCTAJDKYWNSYZLJHHZZCWNYYZYWMHYCHHYXHJKZWSXHZYXLYSKQYSPSLYZWMYPPKBYGLKZHTYXAXQSYSHXASMCHKDSCRSWJPWXSGZJLWWSCHSJHSQNHCSEGNDAQTBAALZZMSSTDQJCJKTSCJAXPLGGXHHGXXZCXPDMMHLDGTYBYSJMXHMRCPXXJZCKZXSHMLQXXTTHXWZFKHCCZDYTCJYXQHLXDHYPJQXYLSYYDZOZJNYXQEZYSQYAYXWYPDGXDDXSPPYZNDLTWRHXYDXZZJHTCXMCZLHPYYYYMHZLLHNXMYLLLMDCPPXHMXDKYCYRDLTXJCHHZZXZLCCLYLNZSHZJZZLNNRLWHYQSNJHXYNTTTKYJPYCHHYEGKCTTWLGQRLGGTGTYGYHPYHYLQYQGCWYQKPYYYTTTTLHYHLLTYTTSPLKYZXGZWGPYDSSZZDQXSKCQNMJJZZBXYQMJRTFFBTKHZKBXLJJKDXJTLBWFZPPTKQTZTGPDGNTPJYFALQMKGXBDCLZFHZCLLLLADPMXDJHLCCLGYHDZFGYDDGCYYFGYDXKSSEBDHYKDKDKHNAXXYBPBYYHXZQGAFFQYJXDMLJCSQZLLPCHBSXGJYNDYBYQSPZWJLZKSDDTACTBXZDYZYPJZQSJNKKTKNJDJGYYPGTLFYQKASDNTCYHBLWDZHBBYDWJRYGKZYHEYYFJMSDTYFZJJHGCXPLXHLDWXXJKYTCYKSSSMTWCTTQZLPBSZDZWZXGZAGYKTYWXLHLSPBCLLOQMMZSSLCMBJCSZZKYDCZJGQQDSMCYTZQQLWZQZXSSFPTTFQMDDZDSHDTDWFHTDYZJYQJQKYPBDJYYXTLJHDRQXXXHAYDHRJLKLYTWHLLRLLRCXYLBWSRSZZSYMKZZHHKYHXKSMDSYDYCJPBZBSQLFCXXXNXKXWYWSDZYQOGGQMMYHCDZTTFJYYBGSTTTYBYKJDHKYXBELHTYPJQNFXFDYKZHQKZBYJTZBXHFDXKDASWTAWAJLDYJSFHBLDNNTNQJTJNCHXFJSRFWHZFMDRYJYJWZPDJKZYJYMPCYZNYNXFBYTFYFWYGDBNZZZDNYTXZEMMQBSQEHXFZMBMFLZZSRXYMJGSXWZJSPRYDJSJGXHJJGLJJYNZZJXHGXKYMLPYYYCXYTWQZSWHWLYRJLPXSLSXMFSWWKLCTNXNYNPSJSZHDZEPTXMYYWXYYSYWLXJQZQXZDCLEEELMCPJPCLWBXSQHFWWTFFJTNQJHJQDXHWLBYZNFJLALKYYJLDXHHYCSTYYWNRJYXYWTRMDRQHWQCMFJDYZMHMYYXJWMYZQZXTLMRSPWWCHAQBXYGZYPXYYRRCLMPYMGKSJSZYSRMYJSNXTPLNBAPPYPYLXYYZKYNLDZYJZCZNNLMZHHARQMPGWQTZMXXMLLHGDZXYHXKYXYCJMFFYYHJFSBSSQLXXNDYCANNMTCJCYPRRNYTYQNYYMBMSXNDLYLYSLJRLXYSXQMLLYZLZJJJKYZZCSFBZXXMSTBJGNXYZHLXNMCWSCYZYFZLXBRNNNYLBNRTGZQYSATSWRYHYJZMZDHZGZDWYBSSCSKXSYHYTXXGCQGXZZSHYXJSCRHMKKBXCZJYJYMKQHZJFNBHMQHYSNJNZYBKNQMCLGQHWLZNZSWXKHLJHYYBQLBFCDSXDLDSPFZPSKJYZWZXZDDXJSMMEGJSCSSMGCLXXKYYYLNYPWWWGYDKZJGGGZGGSYCKNJWNJPCXBJJTQTJWDSSPJXZXNZXUMELPXFSXTLLXCLJXJJLJZXCTPSWXLYDHLYQRWHSYCSQYYBYAYWJJJQFWQCQQCJQGXALDBZZYJGKGXPLTZYFXJLTPADKYQHPMATLCPDCKBMTXYBHKLENXDLEEGQDYMSAWHZMLJTWYGXLYQZLJEEYYBQQFFNLYXRDSCTGJGXYYNKLLYQKCCTLHJLQMKKZGCYYGLLLJDZGYDHZWXPYSJBZKDZGYZZHYWYFQYTYZSZYEZZLYMHJJHTSMQWYZLKYYWZCSRKQYTLTDXWCTYJKLWSQZWBDCQYNCJSRSZJLKCDCDTLZZZACQQZZDDXYPLXZBQJYLZLLLQDDZQJYJYJZYXNYYYNYJXKXDAZWYRDLJYYYRJLXLLDYXJCYWYWNQCCLDDNYYYNYCKCZHXXCCLGZQJGKWPPCQQJYSBZZXYJSQPXJPZBSBDSFNSFPZXHDWZTDWPPTFLZZBZDMYYPQJRSDZSQZSQXBDGCPZSWDWCSQZGMDHZXMWWFYBPDGPHTMJTHZSMMBGZMBZJCFZWFZBBZMQCFMBDMCJXLGPNJBBXGYHYYJGPTZGZMQBQTCGYXJXLWZKYDPDYMGCFTPFXYZTZXDZXTGKMTYBBCLBJASKYTSSQYYMSZXFJEWLXLLSZBQJJJAKLYLXLYCCTSXMCWFKKKBSXLLLLJYXTYLTJYYTDPJHNHNNKBYQNFQYYZBYYESSESSGDYHFHWTCJBSDZZTFDMXHCNJZYMQWSRYJDZJQPDQBBSTJGGFBKJBXTGQHNGWJXJGDLLTHZHHYYYYYYSXWTYYYCCBDBPYPZYCCZYJPZYWCBDLFWZCWJDXXHYHLHWZZXJTCZLCDPXUJCZZZLYXJJTXPHFXWPYWXZPTDZZBDZCYHJHMLXBQXSBYLRDTGJRRCTTTHYTCZWMXFYTWWZCWJWXJYWCSKYBZSCCTZQNHXNWXXKHKFHTSWOCCJYBCMPZZYKBNNZPBZHHZDLSYDDYTYFJPXYNGFXBYQXCBHXCPSXTYZDMKYSNXSXLHKMZXLYHDHKWHXXSSKQYHHCJYXGLHZXCSNHEKDTGZXQYPKDHEXTYKCNYMYYYPKQYYYKXZLTHJQTBYQHXBMYHSQCKWWYLLHCYYLNNEQXQWMCFBDCCMLJGGXDQKTLXKGNQCDGZJWYJJLYHHQTTTNWCHMXCXWHWSZJYDJCCDBQCDGDNYXZTHCQRXCBHZTQCBXWGQWYYBXHMBYMYQTYEXMQKYAQYRGYZSLFYKKQHYSSQYSHJGJCNXKZYCXSBXYXHYYLSTYCXQTHYSMGSCPMMGCCCCCMTZTASMGQZJHKLOSQYLSWTMXSYQKDZLJQQYPLSYCZTCQQPBBQJZCLPKHQZYYXXDTDDTSJCXFFLLCHQXMJLWCJCXTSPYCXNDTJSHJWXDQQJSKXYAMYLSJHMLALYKXCYYDMNMDQMXMCZNNCYBZKKYFLMCHCMLHXRCJJHSYLNMTJZGZGYWJXSRXCWJGJQHQZDQJDCJJZKJKGDZQGJJYJYLXZXXCDQHHHEYTMHLFSBDJSYYSHFYSTCZQLPBDRFRZTZYKYWHSZYQKWDQZRKMSYNBCRXQBJYFAZPZZEDZCJYWBCJWHYJBQSZYWRYSZPTDKZPFPBNZTKLQYHBBZPNPPTYZZYBQNYDCPJMMCYCQMCYFZZDCMNLFPBPLNGQJTBTTNJZPZBBZNJKLJQYLNBZQHKSJZNGGQSZZKYXSHPZSNBCGZKDDZQANZHJKDRTLZLSWJLJZLYWTJNDJZJHXYAYNCBGTZCSSQMNJPJYTYSWXZFKWJQTKHTZPLBHSNJZSYZBWZZZZLSYLSBJHDWWQPSLMMFBJDWAQYZTCJTBNNWZXQXCDSLQGDSDPDZHJTQQPSWLYYJZLGYXYZLCTCBJTKTYCZJTQKBSJLGMGZDMCSGPYNJZYQYYKNXRPWSZXMTNCSZZYXYBYHYZAXYWQCJTLLCKJJTJHGDXDXYQYZZBYWDLWQCGLZGJGQRQZCZSSBCRPCSKYDZNXJSQGXSSJMYDNSTZTPBDLTKZWXQWQTZEXNQCZGWEZKSSBYBRTSSSLCCGBPSZQSZLCCGLLLZXHZQTHCZMQGYZQZNMCOCSZJMMZSQPJYGQLJYJPPLDXRGZYXCCSXHSHGTZNLZWZKJCXTCFCJXLBMQBCZZWPQDNHXLJCTHYZLGYLNLSZZPCXDSCQQHJQKSXZPBAJYEMSMJTZDXLCJYRYYNWJBNGZZTMJXLTBSLYRZPYLSSCNXPHLLHYLLQQZQLXYMRSYCXZLMMCZLTZSDWTJJLLNZGGQXPFSKYGYGHBFZPDKMWGHCXMSGDXJMCJZDYCABXJDLNBCDQYGSKYDQTXDJJYXMSZQAZDZFSLQXYJSJZYLBTXXWXQQZBJZUFBBLYLWDSLJHXJYZJWTDJCZFQZQZZDZSXZZQLZCDZFJHYSPYMPQZMLPPLFFXJJNZZYLSJEYQZFPFZKSYWJJJHRDJZZXTXXGLGHYDXCSKYSWMMZCWYBAZBJKSHFHJCXMHFQHYXXYZFTSJYZFXYXPZLCHMZMBXHZZSXYFYMNCWDABAZLXKTCSHHXKXJJZJSTHYGXSXYYHHHJWXKZXSSBZZWHHHCWTZZZPJXSNXQQJGZYZYWLLCWXZFXXYXYHXMKYYSWSQMNLNAYCYSPMJKHWCQHYLAJJMZXHMMCNZHBHXCLXTJPLTXYJHDYYLTTXFSZHYXXSJBJYAYRSMXYPLCKDUYHLXRLNLLSTYZYYQYGYHHSCCSMZCTZQXKYQFPYYRPFFLKQUNTSZLLZMWWTCQQYZWTLLMLMPWMBZSSTZRBPDDTLQJJBXZCSRZQQYGWCSXFWZLXCCRSZDZMCYGGDZQSGTJSWLJMYMMZYHFBJDGYXCCPSHXNZCSBSJYJGJMPPWAFFYFNXHYZXZYLREMZGZCYZSSZDLLJCSQFNXZKPTXZGXJJGFMYYYSNBTYLBNLHPFZDCYFBMGQRRSSSZXYSGTZRNYDZZCDGPJAFJFZKNZBLCZSZPSGCYCJSZLMLRSZBZZLDLSLLYSXSQZQLYXZLSKKBRXBRBZCYCXZZZEEYFGKLZLYYHGZSGZLFJHGTGWKRAAJYZKZQTSSHJJXDCYZUYJLZYRZDQQHGJZXSSZBYKJPBFRTJXLLFQWJHYLQTYMBLPZDXTZYGBDHZZRBGXHWNJTJXLKSCFSMWLSDQYSJTXKZSCFWJLBXFTZLLJZLLQBLSQMQQCGCZFPBPHZCZJLPYYGGDTGWDCFCZQYYYQYSSCLXZSKLZZZGFFCQNWGLHQYZJJCZLQZZYJPJZZBPDCCMHJGXDQDGDLZQMFGPSYTSDYFWWDJZJYSXYYCZCYHZWPBYKXRYLYBHKJKSFXTZJMMCKHLLTNYYMSYXYZPYJQYCSYCWMTJJKQYRHLLQXPSGTLYYCLJSCPXJYZFNMLRGJJTYZBXYZMSJYJHHFZQMSYXRSZCWTLRTQZSSTKXGQKGSPTGCZNJSJCQCXHMXGGZTQYDJKZDLBZSXJLHYQGGGTHQSZPYHJHHGYYGKGGCWJZZYLCZLXQSFTGZSLLLMLJSKCTBLLZZSZMMNYTPZSXQHJCJYQXYZXZQZCPSHKZZYSXCDFGMWQRLLQXRFZTLYSTCTMJCXJJXHJNXTNRZTZFQYHQGLLGCXSZSJDJLJCYDSJTLNYXHSZXCGJZYQPYLFHDJSBPCCZHJJJQZJQDYBSSLLCMYTTMQTBHJQNNYGKYRQYQMZGCJKPDCGMYZHQLLSLLCLMHOLZGDYYFZSLJCQZLYLZQJESHNYLLJXGJXLYSYYYXNBZLJSSZCQQCJYLLZLTJYLLZLLBNYLGQCHXYYXOXCXQKYJXXXYKLXSXXYQXCYKQXQCSGYXXYQXYGYTQOHXHXPYXXXULCYEYCHZZCBWQBBWJQZSCSZSSLZYLKDESJZWMYMCYTSDSXXSCJPQQSQYLYYZYCMDJDZYWCBTJSYDJKCYDDJLBDJJSODZYSYXQQYXDHHGQQYQHDYXWGMMMAJDYBBBPPBCMUUPLJZSMTXERXJMHQNUTPJDCBSSMSSSTKJTSSMMTRCPLZSZMLQDSDMJMQPNQDXCFYNBFSDQXYXHYAYKQYDDLQYYYSSZBYDSLNTFQTZQPZMCHDHCZCWFDXTMYQSPHQYYXSRGJCWTJTZZQMGWJJTJHTQJBBHWZPXXHYQFXXQYWYYHYSCDYDHHQMNMTMWCPBSZPPZZGLMZFOLLCFWHMMSJZTTDHZZYFFYTZZGZYSKYJXQYJZQBHMBZZLYGHGFMSHPZFZSNCLPBQSNJXZSLXXFPMTYJYGBXLLDLXPZJYZJYHHZCYWHJYLSJEXFSZZYWXKZJLUYDTMLYMQJPWXYHXSKTQJEZRPXXZHHMHWQPWQLYJJQJJZSZCPHJLCHHNXJLQWZJHBMZYXBDHHYPZLHLHLGFWLCHYYTLHJXCJMSCPXSTKPNHQXSRTYXXTESYJCTLSSLSTDLLLWWYHDHRJZSFGXTSYCZYNYHTDHWJSLHTZDQDJZXXQHGYLTZPHCSQFCLNJTCLZPFSTPDYNYLGMJLLYCQHYSSHCHYLHQYQTMZYPBYWRFQYKQSYSLZDQJMPXYYSSRHZJNYWTQDFZBWWTWWRXCWHGYHXMKMYYYQMSMZHNGCEPMLQQMTCWCTMMPXJPJJHFXYYZSXZHTYBMSTSYJTTQQQYYLHYNPYQZLCYZHZWSMYLKFJXLWGXYPJYTYSYXYMZCKTTWLKSMZSYLMPWLZWXWQZSSAQSYXYRHSSNTSRAPXCPWCMGDXHXZDZYFJHGZTTSBJHGYZSZYSMYCLLLXBTYXHBBZJKSSDMALXHYCFYGMQYPJYCQXJLLLJGSLZGQLYCJCCZOTYXMTMTTLLWTGPXYMZMKLPSZZZXHKQYSXCTYJZYHXSHYXZKXLZWPSQPYHJWPJPWXQQYLXSDHMRSLZZYZWTTCYXYSZZSHBSCCSTPLWSSCJCHNLCGCHSSPHYLHFHHXJSXYLLNYLSZDHZXYLSXLWZYKCLDYAXZCMDDYSPJTQJZLNWQPSSSWCTSTSZLBLNXSMNYYMJQBQHRZWTYYDCHQLXKPZWBGQYBKFCMZWPZLLYYLSZYDWHXPSBCMLJBSCGBHXLQHYRLJXYSWXWXZSLDFHLSLYNJLZYFLYJYCDRJLFSYZFSLLCQYQFGJYHYXZLYLMSTDJCYHBZLLNWLXXYGYYHSMGDHXXHHLZZJZXCZZZCYQZFNGWPYLCPKPYYPMCLQKDGXZGGWQBDXZZKZFBXXLZXJTPJPTTBYTSZZDWSLCHZHSLTYXHQLHYXXXYYZYSWTXZKHLXZXZPYHGCHKCFSYHUTJRLXFJXPTZTWHPLYXFCRHXSHXKYXXYHZQDXQWULHYHMJTBFLKHTXCWHJFWJCFPQRYQXCYYYQYGRPYWSGSUNGWCHKZDXYFLXXHJJBYZWTSXXNCYJJYMSWZJQRMHXZWFQSYLZJZGBHYNSLBGTTCSYBYXXWXYHXYYXNSQYXMQYWRGYQLXBBZLJSYLPSYTJZYHYZAWLRORJMKSCZJXXXYXCHDYXRYXXJDTSQFXLYLTSFFYXLMTYJMJUYYYXLTZCSXQZQHZXLYYXZHDNBRXXXJCTYHLBRLMBRLLAXKYLLLJLYXXLYCRYLCJTGJCMTLZLLCYZZPZPCYAWHJJFYBDYYZSMPCKZDQYQPBPCJPDCYZMDPBCYYDYCNNPLMTMLRMFMMGWYZBSJGYGSMZQQQZTXMKQWGXLLPJGZBQCDJJJFPKJKCXBLJMSWMDTQJXLDLPPBXCWRCQFBFQJCZAHZGMYKPHYYHZYKNDKZMBPJYXPXYHLFPNYYGXJDBKXNXHJMZJXSTRSTLDXSKZYSYBZXJLXYSLBZYSLHXJPFXPQNBYLLJQKYGZMCYZZYMCCSLCLHZFWFWYXZMWSXTYNXJHPYYMCYSPMHYSMYDYSHQYZCHMJJMZCAAGCFJBBHPLYZYLXXSDJGXDHKXXTXXNBHRMLYJSLTXMRHNLXQJXYZLLYSWQGDLBJHDCGJYQYCMHWFMJYBMBYJYJWYMDPWHXQLDYGPDFXXBCGJSPCKRSSYZJMSLBZZJFLJJJLGXZGYXYXLSZQYXBEXYXHGCXBPLDYHWETTWWCJMBTXCHXYQXLLXFLYXLLJLSSFWDPZSMYJCLMWYTCZPCHQEKCQBWLCQYDPLQPPQZQFJQDJHYMMCXTXDRMJWRHXCJZYLQXDYYNHYYHRSLSRSYWWZJYMTLTLLGTQCJZYABTCKZCJYCCQLJZQXALMZYHYWLWDXZXQDLLQSHGPJFJLJHJABCQZDJGTKHSSTCYJLPSWZLXZXRWGLDLZRLZXTGSLLLLZLYXXWGDZYGBDPHZPBRLWSXQBPFDWOFMWHLYPCBJCCLDMBZPBZZLCYQXLDOMZBLZWPDWYYGDSTTHCSQSCCRSSSYSLFYBFNTYJSZDFNDPDHDZZMBBLSLCMYFFGTJJQWFTMTPJWFNLBZCMMJTGBDZLQLPYFHYYMJYLSDCHDZJWJCCTLJCLDTLJJCPDDSQDSSZYBNDBJLGGJZXSXNLYCYBJXQYCBYLZCFZPPGKCXZDZFZTJJFJSJXZBNZYJQTTYJYHTYCZHYMDJXTTMPXSPLZCDWSLSHXYPZGTFMLCJTYCBPMGDKWYCYZCDSZZYHFLYCTYGWHKJYYLSJCXGYWJCBLLCSNDDBTZBSCLYZCZZSSQDLLMQYYHFSLQLLXFTYHABXGWNYWYYPLLSDLDLLBJCYXJZMLHLJDXYYQYTDLLLBUGBFDFBBQJZZMDPJHGCLGMJJPGAEHHBWCQXAXHHHZCHXYPHJAXHLPHJPGPZJQCQZGJJZZUZDMQYYBZZPHYHYBWHAZYJHYKFGDPFQSDLZMLJXKXGALXZDAGLMDGXMWZQYXXDXXPFDMMSSYMPFMDMMKXKSYZYSHDZKXSYSMMZZZMSYDNZZCZXFPLSTMZDNMXCKJMZTYYMZMZZMSXHHDCZJEMXXKLJSTLWLSQLYJZLLZJSSDPPMHNLZJCZYHMXXHGZCJMDHXTKGRMXFWMCGMWKDTKSXQMMMFZZYDKMSCLCMPCGMHSPXQPZDSSLCXKYXTWLWJYAHZJGZQMCSNXYYMMPMLKJXMHLMLQMXCTKZMJQYSZJSYSZHSYJZJCDAJZYBSDQJZGWZQQXFKDMSDJLFWEHKZQKJPEYPZYSZCDWYJFFMZZYLTTDZZEFMZLBNPPLPLPEPSZALLTYLKCKQZKGENQLWAGYXYDPXLHSXQQWQCQXQCLHYXXMLYCCWLYMQYSKGCHLCJNSZKPYZKCQZQLJPDMDZHLASXLBYDWQLWDNBQCRYDDZTJYBKBWSZDXDTNPJDTCTQDFXQQMGNXECLTTBKPWSLCTYQLPWYZZKLPYGZCQQPLLKCCYLPQMZCZQCLJSLQZDJXLDDHPZQDLJJXZQDXYZQKZLJCYQDYJPPYPQYKJYRMPCBYMCXKLLZLLFQPYLLLMBSGLCYSSLRSYSQTMXYXZQZFDZUYSYZTFFMZZSMZQHZSSCCMLYXWTPZGXZJGZGSJSGKDDHTQGGZLLBJDZLCBCHYXYZHZFYWXYZYMSDBZZYJGTSMTFXQYXQSTDGSLNXDLRYZZLRYYLXQHTXSRTZNGZXBNQQZFMYKMZJBZYMKBPNLYZPBLMCNQYZZZSJZHJCTZKHYZZJRDYZHNPXGLFZTLKGJTCTSSYLLGZRZBBQZZKLPKLCZYSSUYXBJFPNJZZXCDWXZYJXZZDJJKGGRSRJKMSMZJLSJYWQSKYHQJSXPJZZZLSNSHRNYPZTWCHKLPSRZLZXYJQXQKYSJYCZTLQZYBBYBWZPQDWWYZCYTJCJXCKCWDKKZXSGKDZXWWYYJQYYTCYTDLLXWKCZKKLCCLZCQQDZLQLCSFQCHQHSFSMQZZLNBJJZBSJHTSZDYSJQJPDLZCDCWJKJZZLPYCGMZWDJJBSJQZSYZYHHXJPBJYDSSXDZNCGLQMBTSFSBPDZDLZNFGFJGFSMPXJQLMBLGQCYYXBQKDJJQYRFKZTJDHCZKLBSDZCFJTPLLJGXHYXZCSSZZXSTJYGKGCKGYOQXJPLZPBPGTGYJZGHZQZZLBJLSQFZGKQQJZGYCZBZQTLDXRJXBSXXPZXHYZYCLWDXJJHXMFDZPFZHQHQMQGKSLYHTYCGFRZGNQXCLPDLBZCSCZQLLJBLHBZCYPZZPPDYMZZSGYHCKCPZJGSLJLNSCDSLDLXBMSTLDDFJMKDJDHZLZXLSZQPQPGJLLYBDSZGQLBZLSLKYYHZTTNTJYQTZZPSZQZTLLJTYYLLQLLQYZQLBDZLSLYYZYMDFSZSNHLXZNCZQZPBWSKRFBSYZMTHBLGJPMCZZLSTLXSHTCSYZLZBLFEQHLXFLCJLYLJQCBZLZJHHSSTBRMHXZHJZCLXFNBGXGTQJCZTMSFZKJMSSNXLJKBHSJXNTNLZDNTLMSJXGZJYJCZXYJYJWRWWQNZTNFJSZPZSHZJFYRDJSFSZJZBJFZQZZHZLXFYSBZQLZSGYFTZDCSZXZJBQMSZKJRHYJZCKMJKHCHGTXKXQGLXPXFXTRTYLXJXHDTSJXHJZJXZWZLCQSBTXWXGXTXXHXFTSDKFJHZYJFJXRZSDLLLTQSQQZQWZXSYQTWGWBZCGZLLYZBCLMQQTZHZXZXLJFRMYZFLXYSQXXJKXRMQDZDMMYYBSQBHGZMWFWXGMXLZPYYTGZYCCDXYZXYWGSYJYZNBHPZJSQSYXSXRTFYZGRHZTXSZZTHCBFCLSYXZLZQMZLMPLMXZJXSFLBYZMYQHXJSXRXSQZZZSSLYFRCZJRCRXHHZXQYDYHXSJJHZCXZBTYNSYSXJBQLPXZQPYMLXZKYXLXCJLCYSXXZZLXDLLLJJYHZXGYJWKJRWYHCPSGNRZLFZWFZZNSXGXFLZSXZZZBFCSYJDBRJKRDHHGXJLJJTGXJXXSTJTJXLYXQFCSGSWMSBCTLQZZWLZZKXJMLTMJYHSDDBXGZHDLBMYJFRZFSGCLYJBPMLYSMSXLSZJQQHJZFXGFQFQBPXZGYYQXGZTCQWYLTLGWSGWHRLFSFGZJMGMGBGTJFSYZZGZYZAFLSSPMLPFLCWBJZCLJJMZLPJJLYMQDMYYYFBGYGYZMLYZDXQYXRQQQHSYYYQXYLJTYXFSFSLLGNQCYHYCWFHCCCFXPYLYPLLZYXXXXXKQHHXSHJZCFZSCZJXCPZWHHHHHAPYLQALPQAFYHXDYLUKMZQGGGDDESRNNZLTZGCHYPPYSQJJHCLLJTOLNJPZLJLHYMHEYDYDSQYCDDHGZUNDZCLZYZLLZNTNYZGSLHSLPJJBDGWXPCDUTJCKLKCLWKLLCASSTKZZDNQNTTLYYZSSYSSZZRYLJQKCQDHHCRXRZYDGRGCWCGZQFFFPPJFZYNAKRGYWYQPQXXFKJTSZZXSWZDDFBBXTBGTZKZNPZZPZXZPJSZBMQHKCYXYLDKLJNYPKYGHGDZJXXEAHPNZKZTZCMXCXMMJXNKSZQNMNLWBWWXJKYHCPSTMCSQTZJYXTPCTPDTNNPGLLLZSJLSPBLPLQHDTNJNLYYRSZFFJFQWDPHZDWMRZCCLODAXNSSNYZRESTYJWJYJDBCFXNMWTTBYLWSTSZGYBLJPXGLBOCLHPCBJLTMXZLJYLZXCLTPNCLCKXTPZJSWCYXSFYSZDKNTLBYJCYJLLSTGQCBXRYZXBXKLYLHZLQZLNZCXWJZLJZJNCJHXMNZZGJZZXTZJXYCYYCXXJYYXJJXSSSJSTSSTTPPGQTCSXWZDCSYFPTFBFHFBBLZJCLZZDBXGCXLQPXKFZFLSYLTUWBMQJHSZBMDDBCYSCCLDXYCDDQLYJJWMQLLCSGLJJSYFPYYCCYLTJANTJJPWYCMMGQYYSXDXQMZHSZXPFTWWZQSWQRFKJLZJQQYFBRXJHHFWJJZYQAZMYFRHCYYBYQWLPEXCCZSTYRLTTDMQLYKMBBGMYYJPRKZNPBSXYXBHYZDJDNGHPMFSGMWFZMFQMMBCMZZCJJLCNUXYQLMLRYGQZCYXZLWJGCJCGGMCJNFYZZJHYCPRRCMTZQZXHFQGTJXCCJEAQCRJYHPLQLSZDJRBCQHQDYRHYLYXJSYMHZYDWLDFRYHBPYDTSSCNWBXGLPZMLZZTQSSCPJMXXYCSJYTYCGHYCJWYRXXLFEMWJNMKLLSWTXHYYYNCMMCWJDQDJZGLLJWJRKHPZGGFLCCSCZMCBLTBHBQJXQDSPDJZZGKGLFQYWBZYZJLTSTDHQHCTCBCHFLQMPWDSHYYTQWCNZZJTLBYMBPDYYYXSQKXWYYFLXXNCWCXYPMAELYKKJMZZZBRXYYQJFLJPFHHHYTZZXSGQQMHSPGDZQWBWPJHZJDYSCQWZKTXXSQLZYYMYSDZGRXCKKUJLWPYSYSCSYZLRMLQSYLJXBCXTLWDQZPCYCYKPPPNSXFYZJJRCEMHSZMSXLXGLRWGCSTLRSXBZGBZGZTCPLUJLSLYLYMTXMTZPALZXPXJTJWTCYYZLBLXBZLQMYLXPGHDSLSSDMXMBDZZSXWHAMLCZCPJMCNHJYSNSYGCHSKQMZZQDLLKABLWJXSFMOCDXJRRLYQZKJMYBYQLYHETFJZFRFKSRYXFJTWDSXXSYSQJYSLYXWJHSNLXYYXHBHAWHHJZXWMYLJCSSLKYDZTXBZSYFDXGXZJKHSXXYBSSXDPYNZWRPTQZCZENYGCXQFJYKJBZMLJCMQQXUOXSLYXXLYLLJDZBTYMHPFSTTQQWLHOKYBLZZALZXQLHZWRRQHLSTMYPYXJJXMQSJFNBXYXYJXXYQYLTHYLQYFMLKLJTMLLHSZWKZHLJMLHLJKLJSTLQXYLMBHHLNLZXQJHXCFXXLHYHJJGBYZZKBXSCQDJQDSUJZYYHZHHMGSXCSYMXFEBCQWWRBPYYJQTYZCYQYQQZYHMWFFHGZFRJFCDPXNTQYZPDYKHJLFRZXPPXZDBBGZQSTLGDGYLCQMLCHHMFYWLZYXKJLYPQHSYWMQQGQZMLZJNSQXJQSYJYCBEHSXFSZPXZWFLLBCYYJDYTDTHWZSFJMQQYJLMQXXLLDTTKHHYBFPWTYYSQQWNQWLGWDEBZWCMYGCULKJXTMXMYJSXHYBRWFYMWFRXYQMXYSZTZZTFYKMLDHQDXWYYNLCRYJBLPSXCXYWLSPRRJWXHQYPHTYDNXHHMMYWYTZCSQMTSSCCDALWZTCPQPYJLLQZYJSWXMZZMMYLMXCLMXCZMXMZSQTZPPQQBLPGXQZHFLJJHYTJSRXWZXSCCDLXTYJDCQJXSLQYCLZXLZZXMXQRJMHRHZJBHMFLJLMLCLQNLDXZLLLPYPSYJYSXCQQDCMQJZZXHNPNXZMEKMXHYKYQLXSXTXJYYHWDCWDZHQYYBGYBCYSCFGPSJNZDYZZJZXRZRQJJYMCANYRJTLDPPYZBSTJKXXZYPFDWFGZZRPYMTNGXZQBYXNBUFNQKRJQZMJEGRZGYCLKXZDSKKNSXKCLJSPJYYZLQQJYBZSSQLLLKJXTBKTYLCCDDBLSPPFYLGYDTZJYQGGKQTTFZXBDKTYYHYBBFYTYYBCLPDYTGDHRYRNJSPTCSNYJQHKLLLZSLYDXXWBCJQSPXBPJZJCJDZFFXXBRMLAZHCSNDLBJDSZBLPRZTSWSBXBCLLXXLZDJZSJPYLYXXYFTFFFBHJJXGBYXJPMMMPSSJZJMTLYZJXSWXTYLEDQPJMYGQZJGDJLQJWJQLLSJGJGYGMSCLJJXDTYGJQJQJCJZCJGDZZSXQGSJGGCXHQXSNQLZZBXHSGZXCXYLJXYXYYDFQQJHJFXDHCTXJYRXYSQTJXYEFYYSSYYJXNCYZXFXMSYSZXYYSCHSHXZZZGZZZGFJDLTYLNPZGYJYZYYQZPBXQBDZTZCZYXXYHHSQXSHDHGQHJHGYWSZTMZMLHYXGEBTYLZKQWYTJZRCLEKYSTDBCYKQQSAYXCJXWWGSBHJYZYDHCSJKQCXSWXFLTYNYZPZCCZJQTZWJQDZZZQZLJJXLSBHPYXXPSXSHHEZTXFPTLQYZZXHYTXNCFZYYHXGNXMYWXTZSJPTHHGYMXMXQZXTSBCZYJYXXTYYZYPCQLMMSZMJZZLLZXGXZAAJZYXJMZXWDXZSXZDZXLEYJJZQBHZWZZZQTZPSXZTDSXJJJZNYAZPHXYYSRNQDTHZHYYKYJHDZXZLSWCLYBZYECWCYCRYLCXNHZYDZYDYJDFRJJHTRSQTXYXJRJHOJYNXELXSFSFJZGHPZSXZSZDZCQZBYYKLSGSJHCZSHDGQGXYZGXCHXZJWYQWGYHKSSEQZZNDZFKWYSSTCLZSTSYMCDHJXXYWEYXCZAYDMPXMDSXYBSQMJMZJMTZQLPJYQZCGQHXJHHLXXHLHDLDJQCLDWBSXFZZYYSCHTYTYYBHECXHYKGJPXHHYZJFXHWHBDZFYZBCAPNPGNYDMSXHMMMMAMYNBYJTMPXYYMCTHJBZYFCGTYHWPHFTWZZEZSBZEGPFMTSKFTYCMHFLLHGPZJXZJGZJYXZSBBQSCZZLZCCSTPGXMJSFTCCZJZDJXCYBZLFCJSYZFGSZLYBCWZZBYZDZYPSWYJZXZBDSYUXLZZBZFYGCZXBZHZFTPBGZGEJBSTGKDMFHYZZJHZLLZZGJQZLSFDJSSCBZGPDLFZFZSZYZYZSYGCXSNXXCHCZXTZZLJFZGQSQYXZJQDCCZTQCDXZJYQJQCHXZTDLGSCXZSYQJQTZWLQDQZTQCHQQJZYEZZZPBWKDJFCJPZTYPQYQTTYNLMBDKTJZPQZQZZFPZSBNJLGYJDXJDZZKZGQKXDLPZJTCJDQBXDJQJSTCKNXBXZMSLYJCQMTJQWWCJQNJNLLLHJCWQTBZQYDZCZPZZDZYDDCYZZZCCJTTJFZDPRRTZTJDCQTQZDTJNPLZBCLLCTZSXKJZQZPZLBZRBTJDCXFCZDBCCJJLTQQPLDCGZDBBZJCQDCJWYNLLZYZCCDWLLXWZLXRXNTQQCZXKQLSGDFQTDDGLRLAJJTKUYMKQLLTZYTDYYCZGJWYXDXFRSKSTQTENQMRKQZHHQKDLDAZFKYPBGGPZREBZZYKZZSPEGJXGYKQZZZSLYSYYYZWFQZYLZZLZHWCHKYPQGNPGBLPLRRJYXCCSYYHSFZFYBZYYTGZXYLXCZWXXZJZBLFFLGSKHYJZEYJHLPLLLLCZGXDRZELRHGKLZZYHZLYQSZZJZQLJZFLNBHGWLCZCFJYSPYXZLZLXGCCPZBLLCYBBBBUBBCBPCRNNZCZYRBFSRLDCGQYYQXYGMQZWTZYTYJXYFWTEHZZJYWLCCNTZYJJZDEDPZDZTSYQJHDYMBJNYJZLXTSSTPHNDJXXBYXQTZQDDTJTDYYTGWSCSZQFLSHLGLBCZPHDLYZJYCKWTYTYLBNYTSDSYCCTYSZYYEBHEXHQDTWNYGYCLXTSZYSTQMYGZAZCCSZZDSLZCLZRQXYYELJSBYMXSXZTEMBBLLYYLLYTDQYSHYMRQWKFKBFXNXSBYCHXBWJYHTQBPBSBWDZYLKGZSKYHXQZJXHXJXGNLJKZLYYCDXLFYFGHLJGJYBXQLYBXQPQGZTZPLNCYPXDJYQYDYMRBESJYYHKXXSTMXRCZZYWXYQYBMCLLYZHQYZWQXDBXBZWZMSLPDMYSKFMZKLZCYQYCZLQXFZZYDQZPZYGYJYZMZXDZFYFYTTQTZHGSPCZMLCCYTZXJCYTJMKSLPZHYSNZLLYTPZCTZZCKTXDHXXTQCYFKSMQCCYYAZHTJPCYLZLYJBJXTPNYLJYYNRXSYLMMNXJSMYBCSYSYLZYLXJJQYLDZLPQBFZZBLFNDXQKCZFYWHGQMRDSXYCYTXNQQJZYYPFZXDYZFPRXEJDGYQBXRCNFYYQPGHYJDYZXGRHTKYLNWDZNTSMPKLBTHBPYSZBZTJZSZZJTYYXZPHSSZZBZCZPTQFZMYFLYPYBBJQXZMXXDJMTSYSKKBJZXHJCKLPSMKYJZCXTMLJYXRZZQSLXXQPYZXMKYXXXJCLJPRMYYGADYSKQLSNDHYZKQXZYZTCGHZTLMLWZYBWSYCTBHJHJFCWZTXWYTKZLXQSHLYJZJXTMPLPYCGLTBZZTLZJCYJGDTCLKLPLLQPJMZPAPXYZLKKTKDZCZZBNZDYDYQZJYJGMCTXLTGXSZLMLHBGLKFWNWZHDXUHLFMKYSLGXDTWWFRJEJZTZHYDXYKSHWFZCQSHKTMQQHTZHYMJDJSKHXZJZBZZXYMPAGQMSTPXLSKLZYNWRTSQLSZBPSPSGZWYHTLKSSSWHZZLYYTNXJGMJSZSUFWNLSOZTXGXLSAMMLBWLDSZYLAKQCQCTMYCFJBSLXCLZZCLXXKSBZQCLHJPSQPLSXXCKSLNHPSFQQYTXYJZLQLDXZQJZDYYDJNZPTUZDSKJFSLJHYLZSQZLBTXYDGTQFDBYAZXDZHZJNHHQBYKNXJJQCZMLLJZKSPLDYCLBBLXKLELXJLBQYCXJXGCNLCQPLZLZYJTZLJGYZDZPLTQCSXFDMNYCXGBTJDCZNBGBQYQJWGKFHTNPYQZQGBKPBBYZMTJDYTBLSQMPSXTBNPDXKLEMYYCJYNZCTLDYKZZXDDXHQSHDGMZSJYCCTAYRZLPYLTLKXSLZCGGEXCLFXLKJRTLQJAQZNCMBYDKKCXGLCZJZXJHPTDJJMZQYKQSECQZDSHHADMLZFMMZBGNTJNNLGBYJBRBTMLBYJDZXLCJLPLDLPCQDHLXZLYCBLCXZZJADJLNZMMSSSMYBHBSQKBHRSXXJMXSDZNZPXLGBRHWGGFCXGMSKLLTSJYYCQLTSKYWYYHYWXBXQYWPYWYKQLSQPTNTKHQCWDQKTWPXXHCPTHTWUMSSYHBWCRWXHJMKMZNGWTMLKFGHKJYLSYYCXWHYECLQHKQHTTQKHFZLDXQWYZYYDESBPKYRZPJFYYZJCEQDZZDLATZBBFJLLCXDLMJSSXEGYGSJQXCWBXSSZPDYZCXDNYXPPZYDLYJCZPLTXLSXYZYRXCYYYDYLWWNZSAHJSYQYHGYWWAXTJZDAXYSRLTDPSSYYFNEJDXYZHLXLLLZQZSJNYQYQQXYJGHZGZCYJCHZLYCDSHWSHJZYJXCLLNXZJJYYXNFXMWFPYLCYLLABWDDHWDXJMCXZTZPMLQZHSFHZYNZTLLDYWLSLXHYMMYLMBWWKYXYADTXYLLDJPYBPWUXJMWMLLSAFDLLYFLBHHHBQQLTZJCQJLDJTFFKMMMBYTHYGDCQRDDWRQJXNBYSNWZDBYYTBJHPYBYTTJXAAHGQDQTMYSTQXKBTZPKJLZRBEQQSSMJJBDJOTGTBXPGBKTLHQXJJJCTHXQDWJLWRFWQGWSHCKRYSWGFTGYGBXSDWDWRFHWYTJJXXXJYZYSLPYYYPAYXHYDQKXSHXYXGSKQHYWFDDDPPLCJLQQEEWXKSYYKDYPLTJTHKJLTCYYHHJTTPLTZZCDLTHQKZXQYSTEEYWYYZYXXYYSTTJKLLPZMCYHQGXYHSRMBXPLLNQYDQHXSXXWGDQBSHYLLPJJJTHYJKYPPTHYYKTYEZYENMDSHLCRPQFDGFXZPSFTLJXXJBSWYYSKSFLXLPPLBBBLBSFXFYZBSJSSYLPBBFFFFSSCJDSTZSXZRYYSYFFSYZYZBJTBCTSBSDHRTJJBYTCXYJEYLXCBNEBJDSYXYKGSJZBXBYTFZWGENYHHTHZHHXFWGCSTBGXKLSXYWMTMBYXJSTZSCDYQRCYTWXZFHMYMCXLZNSDJTTTXRYCFYJSBSDYERXJLJXBBDEYNJGHXGCKGSCYMBLXJMSZNSKGXFBNBPTHFJAAFXYXFPXMYPQDTZCXZZPXRSYWZDLYBBKTYQPQJPZYPZJZNJPZJLZZFYSBTTSLMPTZRTDXQSJEHBZYLZDHLJSQMLHTXTJECXSLZZSPKTLZKQQYFSYGYWPCPQFHQHYTQXZKRSGTTSQCZLPTXCDYYZXSQZSLXLZMYCPCQBZYXHBSXLZDLTCDXTYLZJYYZPZYZLTXJSJXHLPMYTXCQRBLZSSFJZZTNJYTXMYJHLHPPLCYXQJQQKZZSCPZKSWALQSBLCCZJSXGWWWYGYKTJBBZTDKHXHKGTGPBKQYSLPXPJCKBMLLXDZSTBKLGGQKQLSBKKTFXRMDKBFTPZFRTBBRFERQGXYJPZSSTLBZTPSZQZSJDHLJQLZBPMSMMSXLQQNHKNBLRDDNXXDHDDJCYYGYLXGZLXSYGMQQGKHBPMXYXLYTQWLWGCPBMQXCYZYDRJBHTDJYHQSHTMJSBYPLWHLZFFNYPMHXXHPLTBQPFBJWQDBYGPNZTPFZJGSDDTQSHZEAWZZYLLTYYBWJKXXGHLFKXDJTMSZSQYNZGGSWQSPHTLSSKMCLZXYSZQZXNCJDQGZDLFNYKLJCJLLZLMZZNHYDSSHTHZZLZZBBHQZWWYCRZHLYQQJBEYFXXXWHSRXWQHWPSLMSSKZTTYGYQQWRSLALHMJTQJSMXQBJJZJXZYZKXBYQXBJXSHZTSFJLXMXZXFGHKZSZGGYLCLSARJYHSLLLMZXELGLXYDJYTLFBHBPNLYZFBBHPTGJKWETZHKJJXZXXGLLJLSTGSHJJYQLQZFKCGNNDJSSZFDBCTWWSEQFHQJBSAQTGYPQLBXBMMYWXGSLZHGLZGQYFLZBYFZJFRYSFMBYZHQGFWZSYFYJJPHZBYYZFFWODGRLMFTWLBZGYCQXCDJYGZYYYYTYTYDWEGAZYHXJLZYYHLRMGRXXZCLHNELJJTJTPWJYBJJBXJJTJTEEKHWSLJPLPSFYZPQQBDLQJJTYYQLYZKDKSQJYYQZLDQTGJQYZJSUCMRYQTHTEJMFCTYHYPKMHYZWJDQFHYYXWSHCTXRLJHQXHCCYYYJLTKTTYTMXGTCJTZAYYOCZLYLBSZYWJYTSJYHBYSHFJLYGJXXTMZYYLTXXYPZLXYJZYZYYPNHMYMDYYLBLHLSYYQQLLNJJYMSOYQBZGDLYXYLCQYXTSZEGXHZGLHWBLJHEYXTWQMAKBPQCGYSHHEGQCMWYYWLJYJHYYZLLJJYLHZYHMGSLJLJXCJJYCLYCJPCPZJZJMMYLCQLNQLJQJSXYJMLSZLJQLYCMMHCFMMFPQQMFYLQMCFFQMMMMHMZNFHHJGTTHHKHSLNCHHYQDXTMMQDCYZYXYQMYQYLTDCYYYZAZZCYMZYDLZFFFMMYCQZWZZMABTBYZTDMNZZGGDFTYPCGQYTTSSFFWFDTZQSSYSTWXJHXYTSXXYLBYQHWWKXHZXWZNNZZJZJJQJCCCHYYXBZXZCYZTLLCQXYNJYCYYCYNZZQYYYEWYCZDCJYCCHYJLBTZYYCQWMPWPYMLGKDLDLGKQQBGYCHJXY" ;
// 此处收录了375个多音字
var oMultiDiff = { " 19969 ": " DZ ", " 19975 ": " WM ", " 19988 ": " QJ ", " 20048 ": " YL ", " 20056 ": " SC ", " 20060 ": " NM ", " 20094 ": " QG ", " 20127 ": " QJ ", " 20167 ": " QC ", " 20193 ": " YG ", " 20250 ": " KH ", " 20256 ": " ZC ", " 20282 ": " SC ", " 20285 ": " QJG ", " 20291 ": " TD ", " 20314 ": " YD ", " 20340 ": " NE ", " 20375 ": " TD ", " 20389 ": " YJ ", " 20391 ": " CZ ", " 20415 ": " PB ", " 20446 ": " YS ", " 20447 ": " SQ ", " 20504 ": " TC ", " 20608 ": " KG ", " 20854 ": " QJ ", " 20857 ": " ZC ", " 20911 ": " PF ", " 20504 ": " TC ", " 20608 ": " KG ", " 20854 ": " QJ ", " 20857 ": " ZC ", " 20911 ": " PF ", " 20985 ": " AW ", " 21032 ": " PB ", " 21048 ": " XQ ", " 21049 ": " SC ", " 21089 ": " YS ", " 21119 ": " JC ", " 21242 ": " SB ", " 21273 ": " SC ", " 21305 ": " YP ", " 21306 ": " QO ", " 21330 ": " ZC ", " 21333 ": " SDC ", " 21345 ": " QK ", " 21378 ": " CA ", " 21397 ": " SC ", " 21414 ": " XS ", " 21442 ": " SC ", " 21477 ": " JG ", " 21480 ": " TD ", " 21484 ": " ZS ", " 21494 ": " YX ", " 21505 ": " YX ", " 21512 ": " HG ", " 21523 ": " XH ", " 21537 ": " PB ", " 21542 ": " PF ", " 21549 ": " KH ", " 21571 ": " E ", " 21574 ": " DA ", " 21588 ": " TD ", " 21589 ": " O ", " 21618 ": " ZC ", " 21621 ": " KHA ", " 21632 ": " ZJ ", " 21654 ": " KG ", " 21679 ": " LKG ", " 21683 ": " KH ", " 21710 ": " A ", " 21719 ": " YH ", " 21734 ": " WOE ", " 21769 ": " A ", " 21780 ": " WN ", " 21804 ": " XH ", " 21834 ": " A ", " 21899 ": " ZD ", " 21903 ": " RN ", " 21908 ": " WO ", " 21939 ": " ZC ", " 21956 ": " SA ", " 21964 ": " YA ", " 21970 ": " TD ", " 22003 ": " A ", " 22031 ": " JG ", " 22040 ": " XS ", " 22060 ": " ZC ", " 22066 ": " ZC ", " 22079 ": " MH ", " 22129 ": " XJ ", " 22179 ": " XA ", " 22237 ": " NJ ", " 22244 ": " TD ", " 22280 ": " JQ ", " 22300 ": " YH ", " 22313 ": " XW ", " 22331 ": " YQ ", " 22343 ": " YJ ", " 22351 ": " PH ", " 22395 ": " DC ", " 22412 ": " TD ", " 22484 ": " PB ", " 22500 ": " PB ", " 22534 ": " ZD ", " 22549 ": " DH ", " 22561 ": " PB ", " 22612 ": " TD ", " 22771 ": " KQ ", " 22831 ": " HB ", " 22841 ": " JG ", " 22855 ": " QJ ", " 22865 ": " XQ ", " 23013 ": " ML ", " 23081 ": " WM ", " 23487 ": " SX ", " 23558 ": " QJ ", " 23561 ": " YW ", " 23586 ": " YW ", " 23614 ": " YW ", " 23615 ": " SN ", " 23631 ": " PB ", " 23646 ": " ZS ", " 23663 ": " ZT ", " 23673 ": " YG ", " 23762 ": " TD ", " 23769 ": " ZS ", " 23780 ": " QJ ", " 23884 ": " QK ", " 24055 ": " XH ", " 24113 ": " DC ", " 24162 ": " ZC ", " 24191 ": " GA ", " 24273 ": " QJ ", " 24324 ": " NL ", " 24377 ": " TD ", " 24378 ": " QJ ", " 24439 ": " PF ", " 24554 ": " ZS ", " 24683 ": " TD ", " 24694 ": " WE ", " 24733 ": " LK ", " 24925 ": " TN ", " 25094 ": " ZG ", " 25100 ": " XQ ", " 25103 ": " XH ", " 25153 ": " PB ", " 25170 ": " PB ", " 25179 ": " KG ", " 25203 ": " PB ", " 25240 ": " ZS ", " 25282 ": " FB ", " 25303 ": " NA ", " 25324 ": " KG ", " 25341 ": " ZY ", " 25373 ": " WZ ", " 25375 ": " XJ ", " 25384 ": " A ", " 25457 ": " A ", " 25528 ": " SD ", " 25530 ": " SC ", " 25552 ": " TD ", " 25774 ": " ZC ", " 25874 ": " ZC ", " 26044 ": " YW ", " 26080 ": " WM ", " 26292 ": " PB ", " 26333 ": " PB ", " 26355 ": " ZY ", " 26366 ": " CZ ", " 26397 ": " ZC ", " 26399 ": " QJ ", " 26415 ": " ZS ", " 26451 ": " SB ", " 26526 ": " ZC ", " 26552 ": " JG ", " 26561 ": " TD ", " 26588 ": " JG ", " 26597 ": " CZ ", " 26629 ": " ZS ", " 26638 ": " YL ", " 26646 ": " XQ ", " 26653 ": " KG ", " 26657 ": " XJ ", " 26727 ": " HG ", " 26894 ": " ZC ", " 26937 ": " ZS ", " 26946 ": " ZC ", " 26999 ": " KJ ", " 27099 ": " KJ ", " 27449 ": " YQ ", " 27481 ": " XS ", " 27542 ": " ZS ", " 27663 ": " ZS ", " 27748 ": " TS ", " 27784 ": " SC ", " 27788 ": " ZD ", " 27795 ": " TD ", " 27812 ": " O ", " 27850 ": " PB ", " 27852 ": " MB ", " 27895 ": " SL ", " 27898 ": " PL ", " 27973 ": " QJ ", " 27981 ": " KH ", " 27986 ": " HX ", " 27994 ": " XJ ", " 28044 ": " YC ", " 28065 ": " WG ", " 28177 ": " SM ", " 28267 ": " QJ ", " 28291 ": " KH ", " 28337 ": " ZQ ", " 28463 ": " TL ", " 28548 ": " DC ", " 28601 ": " TD ", " 28689 ": " PB ", " 28805 ": " JG ", " 28820 ": " QG ", " 28846 ": " PB ", " 28952 ": " TD ", " 28975 ": " ZC ", " 29100 ": " A ", " 29325 ": " QJ ", " 29575 ": " SL ", " 29602 ": " FB ", " 30010 ": " TD ", " 30044 ": " CX ", " 30058 ": " PF ", " 30091 ": " YSP ", " 30111 ": " YN ", " 30229 ": " XJ ", " 30427 ": " SC ", " 30465 ": " SX ", " 30631 ": " YQ ", " 30655 ": " QJ ", " 30684 ": " QJG ", " 30707 ": " SD ", " 30729 ": " XH ", " 30796 ": " LG ", " 30917 ": " PB ", " 31074 ": " NM ", " 31085 ": " JZ ", " 31109 ": " SC ", " 31181 ": " ZC ", " 31192 ": " MLB ", " 31293 ": " JQ ", " 31400 ": " YX ", " 31584 ": " YJ ", " 31896 ": " ZN ", " 31909 ": " ZY ", " 31995 ": " XJ ", " 32321 ": " PF ", " 32327 ": " ZY ", " 32418 ": " HG ", " 32420 ": " XQ ", " 32421 ": " HG ", " 32438 ": " LG ", " 32473 ": " GJ ", " 32488 ": " TD ", " 32521 ": " QJ ", " 32527 ": " PB ", " 32562 ": " ZSQ ", " 32564 ": " JZ ", " 32735 ": " ZD ", " 32793 ": " PB ", " 33071 ": " PF ", " 33098 ": " XL ", " 33100 ": " YA ", " 33152 ": " PB ", " 33261 ": " CX ", " 33324 ": " BP ", " 33333 ": " TD ", " 33406 ": " YA ", " 33426 ": " WM ", " 33432 ": " PB ", " 33445 ": " JG ", " 33486 ": " ZN ", " 33493 ": " TS ", " 33507 ": " QJ ", " 33540 ": " QJ ", " 33544 ": " ZC ", " 33564 ": " XQ ", " 33617 ": " YT ", " 33632 ": " QJ ", " 33636 ": " XH ", " 33637 ": " YX ", " 33694 ": " WG ", " 33705 ": " PF ", " 33728 ": " YW ", " 33882 ": " SR ", " 34067 ": " WM ", " 34074 ": " YW ", " 34121 ": " QJ ", " 34255 ": " ZC ", " 34259 ": " XL ", " 34425 ": " JH ", " 34430 ": " XH ", " 34485 ": " KH ", " 34503 ": " YS ", " 34532 ": " HG ", " 34552 ": " XS ", " 34558 ": " YE ", " 34593 ": " ZL ", " 34660 ": " YQ ", " 34892 ": " XH ", " 34928 ": " SC ", " 34999 ": " QJ ", " 35048 ": " PB ", " 35059 ": " SC ", " 35098 ": " ZC ", " 35203 ": " TQ ", " 35265 ": " JX ", " 35299 ": " JX ", " 35782 ": " SZ ", " 35828 ": " YS ", " 35830 ": " E ", " 35843 ": " TD ", " 35895 ": " YG ", " 35977 ": " MH ", " 36158 ": " JG ", " 36228 ": " QJ ", " 36426 ": " XQ ", " 36466 ": " DC ", " 36710 ": " JC ", " 36711 ": " ZYG ", " 36767 ": " PB ", " 36866 ": " SK ", " 36951 ": " YW ", " 37034 ": " YX ", " 37063 ": " XH ", " 37218 ": " ZC ", " 37325 ": " ZC ", " 38063 ": " PB ", " 38079 ": " TD ", " 38085 ": " QY ", " 38107 ": " DC ", " 38116 ": " TD ", " 38123 ": " YD ", " 38224 ": " HG ", " 38241 ": " XTC ", " 38271 ": " ZC ", " 38415 ": " YE ", " 38426 ": " KH ", " 38461 ": " YD ", " 38463 ": " AE ", " 38466 ": " PB ", " 38477 ": " XJ ", " 38518 ": " YT ", " 38551 ": " WK ", " 38585 ": " ZC ", " 38704 ": " XS ", " 38739 ": " LJ ", " 38761 ": " GJ ", " 38808 ": " SQ ", " 39048 ": " JG ", " 39049 ": " XJ ", " 39052 ": " HG ", " 39076 ": " CZ ", " 39271 ": " XT ", " 39534 ": " TD ", " 39552 ": " TD ", " 39584 ": " PB ", " 39647 ": " SB ", " 39730 ": " LG ", " 39748 ": " TPB ", " 40109 ": " ZQ ", " 40479 ": " ND ", " 40516 ": " HG ", " 40536 ": " HG ", " 40583 ": " QJ ", " 40765 ": " YQ ", " 40784 ": " QJ ", " 40840 ": " YK ", " 40863 ": " QJG " };
function makePy(str){  
var arrResult = new Array(); 
for(var i=0,len=str.length;i<len;i++){  
var ch = str.charAt(i);  
arrResult.push(checkCh(ch));  
}  
return mkRslt(arrResult);  
}  
function checkCh(ch){  
var uni = ch.charCodeAt(0);  
if(uni > 40869 || uni < 19968)  
return ch;  
else
return (oMultiDiff[uni]?oMultiDiff[uni]:(strChineseFirstPY.charAt(uni-19968)));  
}  
function mkRslt(arr) {  
var arrRslt = [""];  
for(var i=0,len=arr.length;i<len;i++){  
var str = arr[i];  
var strlen = str.length;  
if(strlen == 1){  
for(var k=0;k<arrRslt.length;k++){  
arrRslt[k] += str;  
}  
}else{  
var tmpArr = arrRslt.slice(0);  
arrRslt = [];  
for(k=0;k<strlen;k++){  
var tmp = tmpArr.slice(0);  
for(var j=0;j<tmp.length;j++){  
tmp[j] += str.charAt(k);  
}  
arrRslt = arrRslt.concat(tmp);  
}  
}  
}  
return arrRslt;  
}  
String.prototype.trim = function() {    return this.replace(/(^\s*)|(\s*$)/g,""); }  
var ArrayToPingYin={
get:function(beArray){
if(beArray){
var retArray=new Array();
for(var i=0;i<beArray.length;i++){
var item=makePy(beArray[i].substring(0,1))
retArray.push(item[0]);
}
return retArray;
}
}
}
module.exports=ArrayToPingYin;
import React from 'react';
import { Table, Tag, Space,Button,Radio,Row,Col, Input, Checkbox } from 'antd';
import { connect } from 'dva';
import { gray } from 'chalk';
import { StarOutlined, StarFilled, StarTwoTone } from '@ant-design/icons';
import { indexOf } from 'lodash';
var ArrayToPingYin = require('./ArrayToPingYin');
const CheckboxGroup = Checkbox.Group;
const menuListNormal=["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
function getDotText(arr) {
if(arr){
var str = "";
for (var i = 0; i < arr.length; i++) {
str += arr[i]+ ",";
}
if (str.length > 0) {
str = str.substr(0, str.length - 1);
}
return str;
}
}
class SelectOptions extends React.Component {
state = {
};
constructor(props) {
super(props);
const {list, dispatch } = this.props;
if(list==null){
console.error("组件错误:没有数据导入")
}
this.state = {
menuList:ArrayToPingYin.get(list),
componetVisible:"展开",
checkedListOptions:list,
checkedList: list,
indeterminate: false,
checkAll: true,
checkNone: false,
resultList:list,
};
}
hideAndexpand = ()=> {
if(this.state.componetVisible=="展开"){
this.setState({componetVisible:"收起"})
}else{
this.setState({componetVisible:"展开"})
}
};
onChangeValue = (checkedList) => {
let difference;
var a=this.state.resultList;
var b=this.state.checkedListOptions
let first = a.concat(b).filter(v => a.includes(v) && !b.includes(v))
a=first
b=checkedList
difference=a.concat(b.filter(v => !a.includes(v)))
this.setState({
checkedList,
indeterminate: !!difference.length && difference.length < this.props.list.length,
checkAll: difference.length === this.props.list.length,
checkNone:difference.length===0,
resultList:difference
});
this.props.onSubmit(difference)
};
onCheckAllChange = e => {
this.setState({
checkedList:this.props.list,
indeterminate: false,
checkAll: true,
checkNone:false,
resultList:this.props.list,
});
this.props.onSubmit(this.props.list)
};
onCheckAllChangeOver = e => {
this.setState({
checkedList: [],
indeterminate:false,
checkAll: false,
checkNone:true,
resultList:[],
});
this.props.onSubmit([])
};
itemSelectAll = (e)=>{
this.setState({
checkedListOptions:this.props.list,
checkedList:this.state.resultList,
});
}
itemSelect = (item,e)=>{
var arraylist=this.props.list
var array=ArrayToPingYin.get(this.props.list)
var tempAarry=new Array();
if(array){
for(var i=0;i<array.length;i++){
if(array[i]==item){
tempAarry.push(arraylist[i]);
}
}
}
this.setState({
checkedListOptions:tempAarry,
checkedList:this.state.resultList,
});
};
getSearchValue=(item,e)=>{
var list=this.props.list
var tempAarry=new Array()
for(var i=0;i<list.length;i++){
if((list[i].toLowerCase()).indexOf(item.toLowerCase())!=-1){
tempAarry.push(list[i]);
}
}
this.setState({
checkedListOptions:tempAarry,
})
}
render() {
return (
<div >
{
this.state.componetVisible=="展开"?
<div style={{width:"100%",height:30, position:"relative"}}>
<div style={{ position:"absolute",left:5,top:5}}>已选{this.state.resultList.length==this.props.list.length?'全部小区':getDotText(this.state.resultList)}</div>
<div style={{ position:"absolute",right:5,top:5,cursor:"pointer"}} onClick={()=>this.hideAndexpand()}>{this.state.componetVisible}</div>
</div>
:
<div style={{width:"100%",backgroundColor:"#eeeeee"}}>
<div style={{width:"100%",height:30, position:"relative"}}>
<div style={{ position:"absolute",left:5,top:5}}>{this.state.resultList.length==0?'全不选':'已选'}{this.state.resultList.length==this.props.list.length?'全部小区':getDotText(this.state.resultList)}</div>
<div style={{ position:"absolute",right:5,top:5,cursor:"pointer"}} onClick={()=>this.hideAndexpand()}>{this.state.componetVisible}</div>
</div>
<div style={{width:"100%",backgroundColor:"#eeeeee",userSelect:"none"}}>
<div style={{width:"100%",height:40, position:"relative"}}>
<div style={{fontSize:14,position:"absolute"}}>
<a style={{marginLeft:10}} onClick={this.itemSelectAll.bind(this)}>所有</a>
{
menuListNormal.map((item, index) => {
if(this.state.menuList.indexOf(item)>-1){
return (<a key={item} style={{marginLeft:10,userSelect:"none"}} onClick={this.itemSelect.bind(this,item)}>{item}</a>)
}else{
return (<a key={item} style={{marginLeft:10,userSelect:"none"}} disabled>{item}</a>)
}
})
}
</div>
<Checkbox style={{ position:"absolute",right:100}} indeterminate={this.state.indeterminate} onChange={this.onCheckAllChange} checked={this.state.checkAll}>全选</Checkbox>
<Checkbox style={{ position:"absolute",right:0}} onChange={this.onCheckAllChangeOver} checked={this.state.checkNone} >全不选</Checkbox>
</div>
<div style={{width:"100%",paddingLeft:10,paddingRight:10}}>
<div style={{width:"100%",backgroundColor:"#ffffff",border:"1px solid #cfcfcf",padding:5}}>
<div><h1> </h1></div>
<Input.Search placeholder="搜索小区" onSearch={this.getSearchValue.bind(this)} style={{width:200}} />
<div><h1> </h1></div>
<CheckboxGroup
options={ this.state.checkedListOptions}
value={this.state.checkedList}
onChange={this.onChangeValue}/>
<div><h1> </h1></div>
</div>
</div>
<br/>
</div>
</div>
}
</div>
);
}
};
export default SelectOptions;
\ No newline at end of file
/* 使用说明:(传入的参数 使用sort排序 )
import SelectOptions from '../../components/SelectOptions/index';
const printContent = (comment: any)=>{
console.log(comment)
}
<SelectOptions list={["美国","美丽的","美好","加拿大","加油","XO"].sort()} onSubmit={printContent}/>
*/
\ No newline at end of file
const { uniq } = require('lodash');
const RouterConfig = require('../../config/config').default.routes;
const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
function formatter(routes, parentPath = '') {
const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
let result = [];
routes.forEach((item) => {
if (item.path) {
result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/'));
}
if (item.routes) {
result = result.concat(
formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath),
);
}
});
return uniq(result.filter((item) => !!item));
}
beforeEach(async () => {
await page.goto(`${BASE_URL}`);
await page.evaluate(() => {
localStorage.setItem('antd-pro-authority', '["admin"]');
});
});
describe('Ant Design Pro E2E test', () => {
const testPage = (path) => async () => {
await page.goto(`${BASE_URL}${path}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
};
const routers = formatter(RouterConfig);
routers.forEach((route) => {
it(`test pages ${route}`, testPage(route));
});
it('topmenu should have footer', async () => {
const params = '?navTheme=light&layout=topmenu';
await page.goto(`${BASE_URL}${params}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
});
});
@import '~antd/es/style/themes/default.less';
html,
body,
#root {
height: 100%;
}
.colorWeak {
filter: invert(80%);
}
.ant-layout {
min-height: 100vh;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul,
ol {
list-style: none;
}
@media (max-width: @screen-xs) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}
// 兼容IE11
@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
body .ant-design-pro > .ant-layout {
min-height: 100vh;
}
}
import { Button, message, notification } from 'antd';
import React from 'react';
import { useIntl } from 'umi';
import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings;
// if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' }));
});
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event: Event) => {
const e = event as CustomEvent;
const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
const worker = e.detail && e.detail.waiting;
if (!worker) {
return true;
}
// Send skip-waiting event to waiting SW with MessageChannel
await new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (msgEvent) => {
if (msgEvent.data.error) {
reject(msgEvent.data.error);
} else {
resolve(msgEvent.data);
}
};
worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
});
// Refresh current page to use the updated HTML and other assets after SW has skiped waiting
window.location.reload(true);
return true;
};
const key = `open${Date.now()}`;
const btn = (
<Button
type="primary"
onClick={() => {
notification.close(key);
reloadSW();
}}
>
{useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.ok' })}
</Button>
);
notification.open({
message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }),
description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }),
btn,
key,
onClose: async () => {},
});
});
} else if ('serviceWorker' in navigator) {
// unregister service worker
const { serviceWorker } = navigator;
if (serviceWorker.getRegistrations) {
serviceWorker.getRegistrations().then((sws) => {
sws.forEach((sw) => {
sw.unregister();
});
});
}
serviceWorker.getRegistration().then((sw) => {
if (sw) sw.unregister();
});
// remove all caches
if (window.caches && window.caches.keys) {
caches.keys().then((keys) => {
keys.forEach((key) => {
caches.delete(key);
});
});
}
}
/**
* Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
* You can view component api by:
* https://github.com/ant-design/ant-design-pro-layout
*/
import ProLayout, {
MenuDataItem,
BasicLayoutProps as ProLayoutProps,
Settings,
DefaultFooter,
} from '@ant-design/pro-layout';
import React, { useEffect } from 'react';
import { Link, useIntl, connect, Dispatch } from 'umi';
import { GithubOutlined } from '@ant-design/icons';
import { Result, Button } from 'antd';
import Authorized from '@/utils/Authorized';
import RightContent from '@/components/GlobalHeader/RightContent';
import { ConnectState } from '@/models/connect';
import { getAuthorityFromRouter } from '@/utils/utils';
import logo from '../assets/logo.svg';
const noMatch = (
<Result
status={403}
title="403"
subTitle="Sorry, you are not authorized to access this page."
extra={
<Button type="primary">
<Link to="/user/login">Go Login</Link>
</Button>
}
/>
);
export interface BasicLayoutProps extends ProLayoutProps {
breadcrumbNameMap: {
[path: string]: MenuDataItem;
};
route: ProLayoutProps['route'] & {
authority: string[];
};
settings: Settings;
dispatch: Dispatch;
}
export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {
breadcrumbNameMap: {
[path: string]: MenuDataItem;
};
};
/**
* use Authorized check all menu item
*/
const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>
menuList.map((item) => {
const localItem = {
...item,
children: item.children ? menuDataRender(item.children) : undefined,
};
return Authorized.check(item.authority, localItem, null) as MenuDataItem;
});
const defaultFooterDom = (
<DefaultFooter
copyright={`${new Date().getFullYear()} 蚂蚁金服体验技术部出品`}
links={[
{
key: 'Ant Design Pro',
title: 'Ant Design Pro',
href: 'https://pro.ant.design',
blankTarget: true,
},
{
key: 'github',
title: <GithubOutlined />,
href: 'https://github.com/ant-design/ant-design-pro',
blankTarget: true,
},
{
key: 'Ant Design',
title: 'Ant Design',
href: 'https://ant.design',
blankTarget: true,
},
]}
/>
);
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
const {
dispatch,
children,
settings,
location = {
pathname: '/',
},
} = props;
/**
* constructor
*/
useEffect(() => {
if (dispatch) {
dispatch({
type: 'user/fetchCurrent',
});
}
}, []);
/**
* init variables
*/
const handleMenuCollapse = (payload: boolean): void => {
if (dispatch) {
dispatch({
type: 'global/changeLayoutCollapsed',
payload,
});
}
}; // get children authority
const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || {
authority: undefined,
};
const { formatMessage } = useIntl();
return (
<ProLayout
logo={logo}
formatMessage={formatMessage}
menuHeaderRender={(logoDom, titleDom) => (
<Link to="/">
{logoDom}
{titleDom}
</Link>
)}
onCollapse={handleMenuCollapse}
menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom;
}
return <Link to={menuItemProps.path}>{defaultDom}</Link>;
}}
breadcrumbRender={(routers = []) => [
{
path: '/',
breadcrumbName: formatMessage({ id: 'menu.home' }),
},
...routers,
]}
itemRender={(route, params, routes, paths) => {
const first = routes.indexOf(route) === 0;
return first ? (
<Link to={paths.join('/')}>{route.breadcrumbName}</Link>
) : (
<span>{route.breadcrumbName}</span>
);
}}
//footerRender={() => defaultFooterDom}
menuDataRender={menuDataRender}
rightContentRender={() => <RightContent />}
{...props}
{...settings}
>
<Authorized authority={authorized!.authority} noMatch={noMatch}>
{children}
</Authorized>
</ProLayout>
);
};
export default connect(({ global, settings }: ConnectState) => ({
collapsed: global.collapsed,
settings,
}))(BasicLayout);
import React from 'react';
const Layout: React.FC = ({ children }) => <>{children}</>;
export default Layout;
import React from 'react';
import { PageLoading } from '@ant-design/pro-layout';
import { Redirect, connect, ConnectProps } from 'umi';
import { stringify } from 'querystring';
import { ConnectState } from '@/models/connect';
import { CurrentUser } from '@/models/user';
interface SecurityLayoutProps extends ConnectProps {
loading?: boolean;
currentUser?: CurrentUser;
}
interface SecurityLayoutState {
isReady: boolean;
}
class SecurityLayout extends React.Component<SecurityLayoutProps, SecurityLayoutState> {
state: SecurityLayoutState = {
isReady: false,
};
componentDidMount() {
this.setState({
isReady: true,
});
const { dispatch } = this.props;
if (dispatch) {
dispatch({
type: 'user/fetchCurrent',
});
}
}
render() {
const { isReady } = this.state;
const { children, loading, currentUser } = this.props;
// You can replace it to your authentication rule (such as check token exists)
// 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
const isLogin = currentUser && currentUser.userid;
const queryString = stringify({
redirect: window.location.href,
});
if ((!isLogin && loading) || !isReady) {
return <PageLoading />;
}
if (!isLogin && window.location.pathname !== '/user/login') {
return <Redirect to={`/user/login?${queryString}`} />;
}
return children;
}
}
export default connect(({ user, loading }: ConnectState) => ({
currentUser: user.currentUser,
loading: loading.models.user,
}))(SecurityLayout);
@import '~antd/es/style/themes/default.less';
.container {
display: flex;
flex-direction: column;
height: 100vh;
overflow: auto;
background: @layout-body-background;
}
.lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
:global(.ant-dropdown-trigger) {
margin-right: 24px;
}
}
.content {
flex: 1;
padding: 32px 0;
}
@media (min-width: @screen-md-min) {
.container {
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;
}
.content {
padding: 32px 0 24px;
}
}
.top {
text-align: center;
}
.header {
height: 44px;
line-height: 44px;
a {
text-decoration: none;
}
}
.logo {
height: 44px;
margin-right: 16px;
vertical-align: top;
}
.title {
position: relative;
top: 2px;
color: @heading-color;
font-weight: 600;
font-size: 33px;
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
}
.desc {
margin-top: 12px;
margin-bottom: 40px;
color: @text-color-secondary;
font-size: @font-size-base;
}
import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import { Link, useIntl, ConnectProps, connect } from 'umi';
import React from 'react';
import SelectLang from '@/components/SelectLang';
import { ConnectState } from '@/models/connect';
import logo from '../assets/logo.svg';
import styles from './UserLayout.less';
export interface UserLayoutProps extends Partial<ConnectProps> {
breadcrumbNameMap: {
[path: string]: MenuDataItem;
};
}
const UserLayout: React.FC<UserLayoutProps> = (props) => {
const {
route = {
routes: [],
},
} = props;
const { routes = [] } = route;
const {
children,
location = {
pathname: '',
},
} = props;
const { formatMessage } = useIntl();
const { breadcrumb } = getMenuData(routes);
const title = getPageTitle({
pathname: location.pathname,
formatMessage,
breadcrumb,
...props,
});
return (
<HelmetProvider>
<Helmet>
<title>{title}</title>
<meta name="description" content={title} />
</Helmet>
<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}>Ant Design</span>
</Link>
</div>
<div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
</div>
{children}
</div>
<DefaultFooter />
</div>
</HelmetProvider>
);
};
export default connect(({ settings }: ConnectState) => ({ ...settings }))(UserLayout);
import component from './en-US/component';
import globalHeader from './en-US/globalHeader';
import menu from './en-US/menu';
import pwa from './en-US/pwa';
import settingDrawer from './en-US/settingDrawer';
import settings from './en-US/settings';
import R from './en-US/R';
export default {
'navBar.lang': 'Languages',
'layout.user.link.help': 'Help',
'layout.user.link.privacy': 'Privacy',
'layout.user.link.terms': 'Terms',
'app.preview.down.block': 'Download this page to your local project',
'app.welcome.link.fetch-blocks': 'Get all block',
'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
...R,
};
export default {
'R.charge.input.project': 'Project',
'R.charge.input.nameofowner':'nameofowner',
'R.charge.username': 'username',
'R.charge.project': 'project',
'R.charge.unit': 'unit',
'R.charge.status': 'status',
'R.charge.submissionTime': 'submissionTime',
'R.charge.actions': 'actions',
'R.charge.search': 'search',
'R.charge.addaccount': 'addaccount',
'R.charge.paymentsetting':'paymentsetting',
};
export default {
'component.tagSelect.expand': 'Expand',
'component.tagSelect.collapse': 'Collapse',
'component.tagSelect.all': 'All',
};
export default {
'component.globalHeader.search': 'Search',
'component.globalHeader.search.example1': 'Search example 1',
'component.globalHeader.search.example2': 'Search example 2',
'component.globalHeader.search.example3': 'Search example 3',
'component.globalHeader.help': 'Help',
'component.globalHeader.notification': 'Notification',
'component.globalHeader.notification.empty': 'You have viewed all notifications.',
'component.globalHeader.message': 'Message',
'component.globalHeader.message.empty': 'You have viewed all messsages.',
'component.globalHeader.event': 'Event',
'component.globalHeader.event.empty': 'You have viewed all events.',
'component.noticeIcon.clear': 'Clear',
'component.noticeIcon.cleared': 'Cleared',
'component.noticeIcon.empty': 'No notifications',
'component.noticeIcon.view-more': 'View more',
};
export default {
'menu.welcome': 'Welcome',
'menu.more-blocks': 'More Blocks',
'menu.home': 'Home',
'menu.admin': 'Admin',
'menu.admin.sub-page': 'Sub-Page',
'menu.login': 'Login',
'menu.register': 'Register',
'menu.register.result': 'Register Result',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Analysis',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Workplace',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': 'Form',
'menu.form.basic-form': 'Basic Form',
'menu.form.step-form': 'Step Form',
'menu.form.step-form.info': 'Step Form(write transfer information)',
'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
'menu.form.step-form.result': 'Step Form(finished)',
'menu.form.advanced-form': 'Advanced Form',
'menu.list': 'List',
'menu.list.table-list': 'Search Table',
'menu.list.basic-list': 'Basic List',
'menu.list.card-list': 'Card List',
'menu.list.search-list': 'Search List',
'menu.list.search-list.articles': 'Search List(articles)',
'menu.list.search-list.projects': 'Search List(projects)',
'menu.list.search-list.applications': 'Search List(applications)',
'menu.profile': 'Profile',
'menu.profile.basic': 'Basic Profile',
'menu.profile.advanced': 'Advanced Profile',
'menu.result': 'Result',
'menu.result.success': 'Success',
'menu.result.fail': 'Fail',
'menu.exception': 'Exception',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Trigger',
'menu.account': 'Account',
'menu.account.center': 'Account Center',
'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout',
'menu.editor': 'Graphic Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};
export default {
'app.pwa.offline': 'You are offline now',
'app.pwa.serviceworker.updated': 'New content is available',
'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page',
'app.pwa.serviceworker.updated.ok': 'Refresh',
};
export default {
'app.setting.pagestyle': 'Page style setting',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.light': 'Light style',
'app.setting.content-width': 'Content Width',
'app.setting.content-width.fixed': 'Fixed',
'app.setting.content-width.fluid': 'Fluid',
'app.setting.themecolor': 'Theme Color',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
'app.setting.themecolor.geekblue': 'Geek Glue',
'app.setting.themecolor.purple': 'Golden Purple',
'app.setting.navigationmode': 'Navigation Mode',
'app.setting.sidemenu': 'Side Menu Layout',
'app.setting.topmenu': 'Top Menu Layout',
'app.setting.fixedheader': 'Fixed Header',
'app.setting.fixedsidebar': 'Fixed Sidebar',
'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
'app.setting.hideheader': 'Hidden Header when scrolling',
'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
'app.setting.othersettings': 'Other Settings',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copy Setting',
'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js',
'app.setting.production.hint':
'Setting panel shows in development environment only, please manually modify',
};
export default {
'app.settings.menuMap.basic': 'Basic Settings',
'app.settings.menuMap.security': 'Security Settings',
'app.settings.menuMap.binding': 'Account Binding',
'app.settings.menuMap.notification': 'New Message Notification',
'app.settings.basic.avatar': 'Avatar',
'app.settings.basic.change-avatar': 'Change avatar',
'app.settings.basic.email': 'Email',
'app.settings.basic.email-message': 'Please input your email!',
'app.settings.basic.nickname': 'Nickname',
'app.settings.basic.nickname-message': 'Please input your Nickname!',
'app.settings.basic.profile': 'Personal profile',
'app.settings.basic.profile-message': 'Please input your personal profile!',
'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
'app.settings.basic.country': 'Country/Region',
'app.settings.basic.country-message': 'Please input your country!',
'app.settings.basic.geographic': 'Province or city',
'app.settings.basic.geographic-message': 'Please input your geographic info!',
'app.settings.basic.address': 'Street Address',
'app.settings.basic.address-message': 'Please input your address!',
'app.settings.basic.phone': 'Phone Number',
'app.settings.basic.phone-message': 'Please input your phone!',
'app.settings.basic.update': 'Update Information',
'app.settings.security.strong': 'Strong',
'app.settings.security.medium': 'Medium',
'app.settings.security.weak': 'Weak',
'app.settings.security.password': 'Account Password',
'app.settings.security.password-description': 'Current password strength',
'app.settings.security.phone': 'Security Phone',
'app.settings.security.phone-description': 'Bound phone',
'app.settings.security.question': 'Security Question',
'app.settings.security.question-description':
'The security question is not set, and the security policy can effectively protect the account security',
'app.settings.security.email': 'Backup Email',
'app.settings.security.email-description': 'Bound Email',
'app.settings.security.mfa': 'MFA Device',
'app.settings.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
'app.settings.security.modify': 'Modify',
'app.settings.security.set': 'Set',
'app.settings.security.bind': 'Bind',
'app.settings.binding.taobao': 'Binding Taobao',
'app.settings.binding.taobao-description': 'Currently unbound Taobao account',
'app.settings.binding.alipay': 'Binding Alipay',
'app.settings.binding.alipay-description': 'Currently unbound Alipay account',
'app.settings.binding.dingding': 'Binding DingTalk',
'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',
'app.settings.binding.bind': 'Bind',
'app.settings.notification.password': 'Account Password',
'app.settings.notification.password-description':
'Messages from other users will be notified in the form of a station letter',
'app.settings.notification.messages': 'System Messages',
'app.settings.notification.messages-description':
'System messages will be notified in the form of a station letter',
'app.settings.notification.todo': 'To-do Notification',
'app.settings.notification.todo-description':
'The to-do list will be notified in the form of a letter from the station',
'app.settings.open': 'Open',
'app.settings.close': 'Close',
};
import component from './pt-BR/component';
import globalHeader from './pt-BR/globalHeader';
import menu from './pt-BR/menu';
import pwa from './pt-BR/pwa';
import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
export default {
'navBar.lang': 'Idiomas',
'layout.user.link.help': 'ajuda',
'layout.user.link.privacy': 'política de privacidade',
'layout.user.link.terms': 'termos de serviços',
'app.preview.down.block': 'Download this page to your local project',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': 'Expandir',
'component.tagSelect.collapse': 'Diminuir',
'component.tagSelect.all': 'Todas',
};
export default {
'component.globalHeader.search': 'Busca',
'component.globalHeader.search.example1': 'Exemplo de busca 1',
'component.globalHeader.search.example2': 'Exemplo de busca 2',
'component.globalHeader.search.example3': 'Exemplo de busca 3',
'component.globalHeader.help': 'Ajuda',
'component.globalHeader.notification': 'Notificação',
'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.',
'component.globalHeader.message': 'Mensagem',
'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.',
'component.globalHeader.event': 'Evento',
'component.globalHeader.event.empty': 'Você visualizou todos os eventos.',
'component.noticeIcon.clear': 'Limpar',
'component.noticeIcon.cleared': 'Limpo',
'component.noticeIcon.empty': 'Sem notificações',
'component.noticeIcon.loaded': 'Carregado',
'component.noticeIcon.view-more': 'Veja mais',
};
export default {
'menu.welcome': 'Welcome',
'menu.more-blocks': 'More Blocks',
'menu.home': 'Início',
'menu.login': 'Login',
'menu.admin': 'Admin',
'menu.admin.sub-page': 'Sub-Page',
'menu.register': 'Registro',
'menu.register.result': 'Resultado de registro',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Análise',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Ambiente de Trabalho',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': 'Formulário',
'menu.form.basic-form': 'Formulário Básico',
'menu.form.step-form': 'Formulário Assistido',
'menu.form.step-form.info': 'Formulário Assistido(gravar informações de transferência)',
'menu.form.step-form.confirm': 'Formulário Assistido(confirmar informações de transferência)',
'menu.form.step-form.result': 'Formulário Assistido(finalizado)',
'menu.form.advanced-form': 'Formulário Avançado',
'menu.list': 'Lista',
'menu.list.table-list': 'Tabela de Busca',
'menu.list.basic-list': 'Lista Básica',
'menu.list.card-list': 'Lista de Card',
'menu.list.search-list': 'Lista de Busca',
'menu.list.search-list.articles': 'Lista de Busca(artigos)',
'menu.list.search-list.projects': 'Lista de Busca(projetos)',
'menu.list.search-list.applications': 'Lista de Busca(aplicações)',
'menu.profile': 'Perfil',
'menu.profile.basic': 'Perfil Básico',
'menu.profile.advanced': 'Perfil Avançado',
'menu.result': 'Resultado',
'menu.result.success': 'Sucesso',
'menu.result.fail': 'Falha',
'menu.exception': 'Exceção',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Disparar',
'menu.account': 'Conta',
'menu.account.center': 'Central da Conta',
'menu.account.settings': 'Configurar Conta',
'menu.account.trigger': 'Disparar Erro',
'menu.account.logout': 'Sair',
'menu.editor': 'Graphic Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};
export default {
'app.pwa.offline': 'Você está offline agora',
'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível',
'app.pwa.serviceworker.updated.hint':
'Por favor, pressione o botão "Atualizar" para recarregar a página atual',
'app.pwa.serviceworker.updated.ok': 'Atualizar',
};
export default {
'app.setting.pagestyle': 'Configuração de estilo da página',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.light': 'Light style',
'app.setting.content-width': 'Largura do conteúdo',
'app.setting.content-width.fixed': 'Fixo',
'app.setting.content-width.fluid': 'Fluido',
'app.setting.themecolor': 'Cor do Tema',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
'app.setting.themecolor.geekblue': 'Geek Glue',
'app.setting.themecolor.purple': 'Golden Purple',
'app.setting.navigationmode': 'Modo de Navegação',
'app.setting.sidemenu': 'Layout do Menu Lateral',
'app.setting.topmenu': 'Layout do Menu Superior',
'app.setting.fixedheader': 'Cabeçalho fixo',
'app.setting.fixedsidebar': 'Barra lateral fixa',
'app.setting.fixedsidebar.hint': 'Funciona no layout do menu lateral',
'app.setting.hideheader': 'Esconder o cabeçalho quando rolar',
'app.setting.hideheader.hint': 'Funciona quando o esconder cabeçalho está abilitado',
'app.setting.othersettings': 'Outras configurações',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copiar Configuração',
'app.setting.copyinfo':
'copiado com sucesso,por favor trocar o defaultSettings em src/models/setting.js',
'app.setting.production.hint':
'O painel de configuração apenas é exibido no ambiente de desenvolvimento, por favor modifique manualmente o',
};
export default {
'app.settings.menuMap.basic': 'Configurações Básicas',
'app.settings.menuMap.security': 'Configurações de Segurança',
'app.settings.menuMap.binding': 'Vinculação de Conta',
'app.settings.menuMap.notification': 'Mensagens de Notificação',
'app.settings.basic.avatar': 'Avatar',
'app.settings.basic.change-avatar': 'Alterar avatar',
'app.settings.basic.email': 'Email',
'app.settings.basic.email-message': 'Por favor insira seu email!',
'app.settings.basic.nickname': 'Nome de usuário',
'app.settings.basic.nickname-message': 'Por favor insira seu nome de usuário!',
'app.settings.basic.profile': 'Perfil pessoal',
'app.settings.basic.profile-message': 'Por favor insira seu perfil pessoal!',
'app.settings.basic.profile-placeholder': 'Breve introdução sua',
'app.settings.basic.country': 'País/Região',
'app.settings.basic.country-message': 'Por favor insira país!',
'app.settings.basic.geographic': 'Província, estado ou cidade',
'app.settings.basic.geographic-message': 'Por favor insira suas informações geográficas!',
'app.settings.basic.address': 'Endereço',
'app.settings.basic.address-message': 'Por favor insira seu endereço!',
'app.settings.basic.phone': 'Número de telefone',
'app.settings.basic.phone-message': 'Por favor insira seu número de telefone!',
'app.settings.basic.update': 'Atualizar Informações',
'app.settings.security.strong': 'Forte',
'app.settings.security.medium': 'Média',
'app.settings.security.weak': 'Fraca',
'app.settings.security.password': 'Senha da Conta',
'app.settings.security.password-description': 'Força da senha',
'app.settings.security.phone': 'Telefone de Seguraça',
'app.settings.security.phone-description': 'Telefone vinculado',
'app.settings.security.question': 'Pergunta de Segurança',
'app.settings.security.question-description':
'A pergunta de segurança não está definida e a política de segurança pode proteger efetivamente a segurança da conta',
'app.settings.security.email': 'Email de Backup',
'app.settings.security.email-description': 'Email vinculado',
'app.settings.security.mfa': 'Dispositivo MFA',
'app.settings.security.mfa-description':
'O dispositivo MFA não vinculado, após a vinculação, pode ser confirmado duas vezes',
'app.settings.security.modify': 'Modificar',
'app.settings.security.set': 'Atribuir',
'app.settings.security.bind': 'Vincular',
'app.settings.binding.taobao': 'Vincular Taobao',
'app.settings.binding.taobao-description': 'Atualmente não vinculado à conta Taobao',
'app.settings.binding.alipay': 'Vincular Alipay',
'app.settings.binding.alipay-description': 'Atualmente não vinculado à conta Alipay',
'app.settings.binding.dingding': 'Vincular DingTalk',
'app.settings.binding.dingding-description': 'Atualmente não vinculado à conta DingTalk',
'app.settings.binding.bind': 'Vincular',
'app.settings.notification.password': 'Senha da Conta',
'app.settings.notification.password-description':
'Mensagens de outros usuários serão notificadas na forma de uma estação de letra',
'app.settings.notification.messages': 'Mensagens de Sistema',
'app.settings.notification.messages-description':
'Mensagens de sistema serão notificadas na forma de uma estação de letra',
'app.settings.notification.todo': 'Notificação de To-do',
'app.settings.notification.todo-description':
'A lista de to-do será notificada na forma de uma estação de letra',
'app.settings.open': 'Aberto',
'app.settings.close': 'Fechado',
};
import component from './zh-CN/component';
import globalHeader from './zh-CN/globalHeader';
import menu from './zh-CN/menu';
import pwa from './zh-CN/pwa';
import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
import R from './zh-CN/R';
export default {
'navBar.lang': '语言',
'layout.user.link.help': '帮助',
'layout.user.link.privacy': '隐私',
'layout.user.link.terms': '条款',
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
...R,
};
export default {
'R.charge.input.project': '所在小区',
'R.charge.input.nameofowner':'业主名称',
'R.charge.username': '业主名称',
'R.charge.project': '所在小区',
'R.charge.unit': '所在单元',
'R.charge.status': '状态',
'R.charge.submissionTime': '提交时间',
'R.charge.actions': '操作',
'R.charge.search': '搜索',
'R.charge.addaccount': '添加会计',
'R.charge.paymentsetting':'缴费设置',
};
export default {
'component.tagSelect.expand': '展开',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
export default {
'component.globalHeader.search': '站内搜索',
'component.globalHeader.search.example1': '搜索提示一',
'component.globalHeader.search.example2': '搜索提示二',
'component.globalHeader.search.example3': '搜索提示三',
'component.globalHeader.help': '使用文档',
'component.globalHeader.notification': '通知',
'component.globalHeader.notification.empty': '你已查看所有通知',
'component.globalHeader.message': '消息',
'component.globalHeader.message.empty': '您已读完所有消息',
'component.globalHeader.event': '待办',
'component.globalHeader.event.empty': '你已完成所有待办',
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暂无数据',
'component.noticeIcon.view-more': '查看更多',
};
export default {
'menu.welcome': '欢迎',
'menu.more-blocks': '更多区块',
'menu.home': '首页',
'menu.admin': '管理页',
'menu.admin.sub-page': '二级管理页',
'menu.login': '登录',
'menu.register': '注册',
'menu.register.result': '注册结果',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': '分析页',
'menu.dashboard.monitor': '监控页',
'menu.dashboard.workplace': '工作台',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': '表单页',
'menu.form.basic-form': '基础表单',
'menu.form.step-form': '分步表单',
'menu.form.step-form.info': '分步表单(填写转账信息)',
'menu.form.step-form.confirm': '分步表单(确认转账信息)',
'menu.form.step-form.result': '分步表单(完成)',
'menu.form.advanced-form': '高级表单',
'menu.list': '列表页',
'menu.list.table-list': '查询表格',
'menu.list.basic-list': '标准列表',
'menu.list.card-list': '卡片列表',
'menu.list.search-list': '搜索列表',
'menu.list.search-list.articles': '搜索列表(文章)',
'menu.list.search-list.projects': '搜索列表(项目)',
'menu.list.search-list.applications': '搜索列表(应用)',
'menu.profile': '详情页',
'menu.profile.basic': '基础详情页',
'menu.profile.advanced': '高级详情页',
'menu.result': '结果页',
'menu.result.success': '成功页',
'menu.result.fail': '失败页',
'menu.exception': '异常页',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '触发错误',
'menu.account': '个人页',
'menu.account.center': '个人中心',
'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录',
'menu.editor': '图形编辑器',
'menu.editor.flow': '流程编辑器',
'menu.editor.mind': '脑图编辑器',
'menu.editor.koni': '拓扑编辑器',
};
export default {
'app.pwa.offline': '当前处于离线状态',
'app.pwa.serviceworker.updated': '有新内容',
'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面',
'app.pwa.serviceworker.updated.ok': '刷新',
};
export default {
'app.setting.pagestyle': '整体风格设置',
'app.setting.pagestyle.dark': '暗色菜单风格',
'app.setting.pagestyle.light': '亮色菜单风格',
'app.setting.content-width': '内容区域宽度',
'app.setting.content-width.fixed': '定宽',
'app.setting.content-width.fluid': '流式',
'app.setting.themecolor': '主题色',
'app.setting.themecolor.dust': '薄暮',
'app.setting.themecolor.volcano': '火山',
'app.setting.themecolor.sunset': '日暮',
'app.setting.themecolor.cyan': '明青',
'app.setting.themecolor.green': '极光绿',
'app.setting.themecolor.daybreak': '拂晓蓝(默认)',
'app.setting.themecolor.geekblue': '极客蓝',
'app.setting.themecolor.purple': '酱紫',
'app.setting.navigationmode': '导航模式',
'app.setting.sidemenu': '侧边菜单布局',
'app.setting.topmenu': '顶部菜单布局',
'app.setting.fixedheader': '固定 Header',
'app.setting.fixedsidebar': '固定侧边菜单',
'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置',
'app.setting.hideheader': '下滑时隐藏 Header',
'app.setting.hideheader.hint': '固定 Header 时可配置',
'app.setting.othersettings': '其他设置',
'app.setting.weakmode': '色弱模式',
'app.setting.copy': '拷贝设置',
'app.setting.copyinfo': '拷贝成功,请到 src/defaultSettings.js 中替换默认配置',
'app.setting.production.hint':
'配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
};
export default {
'app.settings.menuMap.basic': '基本设置',
'app.settings.menuMap.security': '安全设置',
'app.settings.menuMap.binding': '账号绑定',
'app.settings.menuMap.notification': '新消息通知',
'app.settings.basic.avatar': '头像',
'app.settings.basic.change-avatar': '更换头像',
'app.settings.basic.email': '邮箱',
'app.settings.basic.email-message': '请输入您的邮箱!',
'app.settings.basic.nickname': '昵称',
'app.settings.basic.nickname-message': '请输入您的昵称!',
'app.settings.basic.profile': '个人简介',
'app.settings.basic.profile-message': '请输入个人简介!',
'app.settings.basic.profile-placeholder': '个人简介',
'app.settings.basic.country': '国家/地区',
'app.settings.basic.country-message': '请输入您的国家或地区!',
'app.settings.basic.geographic': '所在省市',
'app.settings.basic.geographic-message': '请输入您的所在省市!',
'app.settings.basic.address': '街道地址',
'app.settings.basic.address-message': '请输入您的街道地址!',
'app.settings.basic.phone': '联系电话',
'app.settings.basic.phone-message': '请输入您的联系电话!',
'app.settings.basic.update': '更新基本信息',
'app.settings.security.strong': '强',
'app.settings.security.medium': '中',
'app.settings.security.weak': '弱',
'app.settings.security.password': '账户密码',
'app.settings.security.password-description': '当前密码强度',
'app.settings.security.phone': '密保手机',
'app.settings.security.phone-description': '已绑定手机',
'app.settings.security.question': '密保问题',
'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',
'app.settings.security.email': '备用邮箱',
'app.settings.security.email-description': '已绑定邮箱',
'app.settings.security.mfa': 'MFA 设备',
'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
'app.settings.security.modify': '修改',
'app.settings.security.set': '设置',
'app.settings.security.bind': '绑定',
'app.settings.binding.taobao': '绑定淘宝',
'app.settings.binding.taobao-description': '当前未绑定淘宝账号',
'app.settings.binding.alipay': '绑定支付宝',
'app.settings.binding.alipay-description': '当前未绑定支付宝账号',
'app.settings.binding.dingding': '绑定钉钉',
'app.settings.binding.dingding-description': '当前未绑定钉钉账号',
'app.settings.binding.bind': '绑定',
'app.settings.notification.password': '账户密码',
'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',
'app.settings.notification.messages': '系统消息',
'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',
'app.settings.notification.todo': '待办任务',
'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',
'app.settings.open': '开',
'app.settings.close': '关',
'app.names':'名字'
};
import component from './zh-TW/component';
import globalHeader from './zh-TW/globalHeader';
import menu from './zh-TW/menu';
import pwa from './zh-TW/pwa';
import settingDrawer from './zh-TW/settingDrawer';
import settings from './zh-TW/settings';
export default {
'navBar.lang': '語言',
'layout.user.link.help': '幫助',
'layout.user.link.privacy': '隱私',
'layout.user.link.terms': '條款',
'app.preview.down.block': '下載此頁面到本地項目',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': '展開',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
export default {
'component.globalHeader.search': '站內搜索',
'component.globalHeader.search.example1': '搜索提示壹',
'component.globalHeader.search.example2': '搜索提示二',
'component.globalHeader.search.example3': '搜索提示三',
'component.globalHeader.help': '使用手冊',
'component.globalHeader.notification': '通知',
'component.globalHeader.notification.empty': '妳已查看所有通知',
'component.globalHeader.message': '消息',
'component.globalHeader.message.empty': '您已讀完所有消息',
'component.globalHeader.event': '待辦',
'component.globalHeader.event.empty': '妳已完成所有待辦',
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暫無資料',
'component.noticeIcon.view-more': '查看更多',
};
export default {
'menu.welcome': '歡迎',
'menu.more-blocks': '更多區塊',
'menu.home': '首頁',
'menu.login': '登錄',
'menu.admin': '权限',
'menu.admin.sub-page': '二级管理页',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.register': '註冊',
'menu.register.result': '註冊結果',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': '分析頁',
'menu.dashboard.monitor': '監控頁',
'menu.dashboard.workplace': '工作臺',
'menu.form': '表單頁',
'menu.form.basic-form': '基礎表單',
'menu.form.step-form': '分步表單',
'menu.form.step-form.info': '分步表單(填寫轉賬信息)',
'menu.form.step-form.confirm': '分步表單(確認轉賬信息)',
'menu.form.step-form.result': '分步表單(完成)',
'menu.form.advanced-form': '高級表單',
'menu.list': '列表頁',
'menu.list.table-list': '查詢表格',
'menu.list.basic-list': '標淮列表',
'menu.list.card-list': '卡片列表',
'menu.list.search-list': '搜索列表',
'menu.list.search-list.articles': '搜索列表(文章)',
'menu.list.search-list.projects': '搜索列表(項目)',
'menu.list.search-list.applications': '搜索列表(應用)',
'menu.profile': '詳情頁',
'menu.profile.basic': '基礎詳情頁',
'menu.profile.advanced': '高級詳情頁',
'menu.result': '結果頁',
'menu.result.success': '成功頁',
'menu.result.fail': '失敗頁',
'menu.account': '個人頁',
'menu.account.center': '個人中心',
'menu.account.settings': '個人設置',
'menu.account.trigger': '觸發報錯',
'menu.account.logout': '退出登錄',
'menu.exception': '异常页',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '触发错误',
'menu.editor': '圖形編輯器',
'menu.editor.flow': '流程編輯器',
'menu.editor.mind': '腦圖編輯器',
'menu.editor.koni': '拓撲編輯器',
};
export default {
'app.pwa.offline': '當前處於離線狀態',
'app.pwa.serviceworker.updated': '有新內容',
'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面',
'app.pwa.serviceworker.updated.ok': '刷新',
};
export default {
'app.setting.pagestyle': '整體風格設置',
'app.setting.pagestyle.dark': '暗色菜單風格',
'app.setting.pagestyle.light': '亮色菜單風格',
'app.setting.content-width': '內容區域寬度',
'app.setting.content-width.fixed': '定寬',
'app.setting.content-width.fluid': '流式',
'app.setting.themecolor': '主題色',
'app.setting.themecolor.dust': '薄暮',
'app.setting.themecolor.volcano': '火山',
'app.setting.themecolor.sunset': '日暮',
'app.setting.themecolor.cyan': '明青',
'app.setting.themecolor.green': '極光綠',
'app.setting.themecolor.daybreak': '拂曉藍(默認)',
'app.setting.themecolor.geekblue': '極客藍',
'app.setting.themecolor.purple': '醬紫',
'app.setting.navigationmode': '導航模式',
'app.setting.sidemenu': '側邊菜單布局',
'app.setting.topmenu': '頂部菜單布局',
'app.setting.fixedheader': '固定 Header',
'app.setting.fixedsidebar': '固定側邊菜單',
'app.setting.fixedsidebar.hint': '側邊菜單布局時可配置',
'app.setting.hideheader': '下滑時隱藏 Header',
'app.setting.hideheader.hint': '固定 Header 時可配置',
'app.setting.othersettings': '其他設置',
'app.setting.weakmode': '色弱模式',
'app.setting.copy': '拷貝設置',
'app.setting.copyinfo': '拷貝成功,請到 src/defaultSettings.js 中替換默認配置',
'app.setting.production.hint':
'配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件',
};
export default {
'app.settings.menuMap.basic': '基本設置',
'app.settings.menuMap.security': '安全設置',
'app.settings.menuMap.binding': '賬號綁定',
'app.settings.menuMap.notification': '新消息通知',
'app.settings.basic.avatar': '頭像',
'app.settings.basic.change-avatar': '更換頭像',
'app.settings.basic.email': '郵箱',
'app.settings.basic.email-message': '請輸入您的郵箱!',
'app.settings.basic.nickname': '昵稱',
'app.settings.basic.nickname-message': '請輸入您的昵稱!',
'app.settings.basic.profile': '個人簡介',
'app.settings.basic.profile-message': '請輸入個人簡介!',
'app.settings.basic.profile-placeholder': '個人簡介',
'app.settings.basic.country': '國家/地區',
'app.settings.basic.country-message': '請輸入您的國家或地區!',
'app.settings.basic.geographic': '所在省市',
'app.settings.basic.geographic-message': '請輸入您的所在省市!',
'app.settings.basic.address': '街道地址',
'app.settings.basic.address-message': '請輸入您的街道地址!',
'app.settings.basic.phone': '聯系電話',
'app.settings.basic.phone-message': '請輸入您的聯系電話!',
'app.settings.basic.update': '更新基本信息',
'app.settings.security.strong': '強',
'app.settings.security.medium': '中',
'app.settings.security.weak': '弱',
'app.settings.security.password': '賬戶密碼',
'app.settings.security.password-description': '當前密碼強度',
'app.settings.security.phone': '密保手機',
'app.settings.security.phone-description': '已綁定手機',
'app.settings.security.question': '密保問題',
'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',
'app.settings.security.email': '備用郵箱',
'app.settings.security.email-description': '已綁定郵箱',
'app.settings.security.mfa': 'MFA 設備',
'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',
'app.settings.security.modify': '修改',
'app.settings.security.set': '設置',
'app.settings.security.bind': '綁定',
'app.settings.binding.taobao': '綁定淘寶',
'app.settings.binding.taobao-description': '當前未綁定淘寶賬號',
'app.settings.binding.alipay': '綁定支付寶',
'app.settings.binding.alipay-description': '當前未綁定支付寶賬號',
'app.settings.binding.dingding': '綁定釘釘',
'app.settings.binding.dingding-description': '當前未綁定釘釘賬號',
'app.settings.binding.bind': '綁定',
'app.settings.notification.password': '賬戶密碼',
'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知',
'app.settings.notification.messages': '系統消息',
'app.settings.notification.messages-description': '系統消息將以站內信的形式通知',
'app.settings.notification.todo': '待辦任務',
'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知',
'app.settings.open': '開',
'app.settings.close': '關',
};
{
"name": "Ant Design Pro",
"short_name": "Ant Design Pro",
"display": "standalone",
"start_url": "./?utm_source=homescreen",
"theme_color": "#002140",
"background_color": "#001529",
"icons": [
{
"src": "icons/icon-192x192.png",
"sizes": "192x192"
},
{
"src": "icons/icon-128x128.png",
"sizes": "128x128"
},
{
"src": "icons/icon-512x512.png",
"sizes": "512x512"
}
]
}
import { MenuDataItem, Settings as ProSettings } from '@ant-design/pro-layout';
import { GlobalModelState } from './global';
import { UserModelState } from './user';
import { StateType } from './login';
export { GlobalModelState, UserModelState };
export interface Loading {
global: boolean;
effects: { [key: string]: boolean | undefined };
models: {
global?: boolean;
menu?: boolean;
setting?: boolean;
user?: boolean;
login?: boolean;
};
}
export interface ConnectState {
global: GlobalModelState;
loading: Loading;
settings: ProSettings;
user: UserModelState;
login: StateType;
}
export interface Route extends MenuDataItem {
routes?: Route[];
}
import { Subscription, Reducer, Effect } from 'umi';
import { NoticeIconData } from '@/components/NoticeIcon';
import { queryNotices } from '@/services/user';
import { ConnectState } from './connect.d';
export interface NoticeItem extends NoticeIconData {
id: string;
type: string;
status: string;
}
export interface GlobalModelState {
collapsed: boolean;
notices: NoticeItem[];
}
export interface GlobalModelType {
namespace: 'global';
state: GlobalModelState;
effects: {
fetchNotices: Effect;
clearNotices: Effect;
changeNoticeReadState: Effect;
};
reducers: {
changeLayoutCollapsed: Reducer<GlobalModelState>;
saveNotices: Reducer<GlobalModelState>;
saveClearedNotices: Reducer<GlobalModelState>;
};
subscriptions: { setup: Subscription };
}
const GlobalModel: GlobalModelType = {
namespace: 'global',
state: {
collapsed: false,
notices: [],
},
effects: {
*fetchNotices(_, { call, put, select }) {
const data = yield call(queryNotices);
yield put({
type: 'saveNotices',
payload: data,
});
const unreadCount: number = yield select(
(state: ConnectState) => state.global.notices.filter((item) => !item.read).length,
);
yield put({
type: 'user/changeNotifyCount',
payload: {
totalCount: data.length,
unreadCount,
},
});
},
*clearNotices({ payload }, { put, select }) {
yield put({
type: 'saveClearedNotices',
payload,
});
const count: number = yield select((state: ConnectState) => state.global.notices.length);
const unreadCount: number = yield select(
(state: ConnectState) => state.global.notices.filter((item) => !item.read).length,
);
yield put({
type: 'user/changeNotifyCount',
payload: {
totalCount: count,
unreadCount,
},
});
},
*changeNoticeReadState({ payload }, { put, select }) {
const notices: NoticeItem[] = yield select((state: ConnectState) =>
state.global.notices.map((item) => {
const notice = { ...item };
if (notice.id === payload) {
notice.read = true;
}
return notice;
}),
);
yield put({
type: 'saveNotices',
payload: notices,
});
yield put({
type: 'user/changeNotifyCount',
payload: {
totalCount: notices.length,
unreadCount: notices.filter((item) => !item.read).length,
},
});
},
},
reducers: {
changeLayoutCollapsed(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
return {
...state,
collapsed: payload,
};
},
saveNotices(state, { payload }): GlobalModelState {
return {
collapsed: false,
...state,
notices: payload,
};
},
saveClearedNotices(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
return {
...state,
collapsed: false,
notices: state.notices.filter((item): boolean => item.type !== payload),
};
},
},
subscriptions: {
setup({ history }): void {
// Subscribe history(url) change, trigger `load` action if pathname is `/`
history.listen(({ pathname, search }): void => {
if (typeof window.ga !== 'undefined') {
window.ga('send', 'pageview', pathname + search);
}
});
},
},
};
export default GlobalModel;
import { stringify } from 'querystring';
import { history, Reducer, Effect } from 'umi';
import { fakeAccountLogin } from '@/services/login';
import { setAuthority } from '@/utils/authority';
import { getPageQuery } from '@/utils/utils';
export interface StateType {
status?: 'ok' | 'error';
type?: string;
currentAuthority?: 'user' | 'guest' | 'admin';
}
export interface LoginModelType {
namespace: string;
state: StateType;
effects: {
login: Effect;
logout: Effect;
};
reducers: {
changeLoginStatus: Reducer<StateType>;
};
}
const Model: LoginModelType = {
namespace: 'login',
state: {
status: undefined,
},
effects: {
*login({ payload }, { call, put }) {
const response = yield call(fakeAccountLogin, payload);
yield put({
type: 'changeLoginStatus',
payload: response,
});
// Login successfully
if (response.status === 'ok') {
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 || '/');
}
},
logout() {
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,
}),
});
}
},
},
reducers: {
changeLoginStatus(state, { payload }) {
setAuthority(payload.currentAuthority);
return {
...state,
status: payload.status,
type: payload.type,
};
},
},
};
export default Model;
import { Reducer } from 'umi';
import defaultSettings, { DefaultSettings } from '../../config/defaultSettings';
export interface SettingModelType {
namespace: 'settings';
state: DefaultSettings;
reducers: {
changeSetting: Reducer<DefaultSettings>;
};
}
const updateColorWeak: (colorWeak: boolean) => void = (colorWeak) => {
const root = document.getElementById('root');
if (root) {
root.className = colorWeak ? 'colorWeak' : '';
}
};
const SettingModel: SettingModelType = {
namespace: 'settings',
state: defaultSettings,
reducers: {
changeSetting(state = defaultSettings, { payload }) {
const { colorWeak, contentWidth } = payload;
if (state.contentWidth !== contentWidth && window.dispatchEvent) {
window.dispatchEvent(new Event('resize'));
}
updateColorWeak(!!colorWeak);
return {
...state,
...payload,
};
},
},
};
export default SettingModel;
import { Effect, Reducer } from 'umi';
import { queryCurrent, query as queryUsers } from '@/services/user';
export interface CurrentUser {
avatar?: string;
name?: string;
title?: string;
group?: string;
signature?: string;
tags?: {
key: string;
label: string;
}[];
userid?: string;
unreadCount?: number;
}
export interface UserModelState {
currentUser?: CurrentUser;
}
export interface UserModelType {
namespace: 'user';
state: UserModelState;
effects: {
fetch: Effect;
fetchCurrent: Effect;
};
reducers: {
saveCurrentUser: Reducer<UserModelState>;
changeNotifyCount: Reducer<UserModelState>;
};
}
const UserModel: UserModelType = {
namespace: 'user',
state: {
currentUser: {},
},
effects: {
*fetch(_, { call, put }) {
const response = yield call(queryUsers);
yield put({
type: 'save',
payload: response,
});
},
*fetchCurrent(_, { call, put }) {
const response = yield call(queryCurrent);
yield put({
type: 'saveCurrentUser',
payload: response,
});
},
},
reducers: {
saveCurrentUser(state, action) {
return {
...state,
currentUser: action.payload || {},
};
},
changeNotifyCount(
state = {
currentUser: {},
},
action,
) {
return {
...state,
currentUser: {
...state.currentUser,
notifyCount: action.payload.totalCount,
unreadCount: action.payload.unreadCount,
},
};
},
},
};
export default UserModel;
import { Button, Result } from 'antd';
import React from 'react';
import { history } from 'umi';
const NoFoundPage: React.FC<{}> = () => (
<Result
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
extra={
<Button type="primary" onClick={() => history.push('/')}>
Back Home
</Button>
}
/>
);
export default NoFoundPage;
import React from 'react';
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
import { Card, Typography, Alert } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
export default (): React.ReactNode => (
<PageHeaderWrapper content=" 这个页面只有 admin 权限才能查看">
<Card>
<Alert
message="umi ui 现已发布,欢迎使用 npm run ui 启动体验。"
type="success"
showIcon
banner
style={{
margin: -12,
marginBottom: 48,
}}
/>
<Typography.Title level={2} style={{ textAlign: 'center' }}>
<SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You
</Typography.Title>
</Card>
<p style={{ textAlign: 'center', marginTop: 24 }}>
Want to add more pages? Please refer to{' '}
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
use block
</a>
</p>
</PageHeaderWrapper>
);
import React from 'react';
import { Redirect, connect, ConnectProps } from 'umi';
import Authorized from '@/utils/Authorized';
import { getRouteAuthority } from '@/utils/utils';
import { ConnectState, UserModelState } from '@/models/connect';
interface AuthComponentProps extends ConnectProps {
user: UserModelState;
}
const AuthComponent: React.FC<AuthComponentProps> = ({
children,
route = {
routes: [],
},
location = {
pathname: '',
},
user,
}) => {
const { currentUser } = user;
const { routes = [] } = route;
const isLogin = currentUser && currentUser.name;
return (
<Authorized
authority={getRouteAuthority(location.pathname, routes) || ''}
noMatch={isLogin ? <Redirect to="/exception/403" /> : <Redirect to="/user/login" />}
>
{children}
</Authorized>
);
};
export default connect(({ user }: ConnectState) => ({
user,
}))(AuthComponent);
import React from 'react';
import { Modal } from 'antd';
interface CreateFormProps {
modalVisible: boolean;
onCancel: () => void;
}
const CreateForm: React.FC<CreateFormProps> = (props) => {
const { modalVisible, onCancel } = props;
return (
<Modal
destroyOnClose
title="新建规则"
visible={modalVisible}
onCancel={() => onCancel()}
footer={null}
>
{props.children}
</Modal>
);
};
export default CreateForm;
import React, { useState } from 'react';
import { Form, Button, DatePicker, Input, Modal, Radio, Select, Steps } from 'antd';
import { TableListItem } from '../data.d';
export interface FormValueType extends Partial<TableListItem> {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
}
export interface UpdateFormProps {
onCancel: (flag?: boolean, formVals?: FormValueType) => void;
onSubmit: (values: FormValueType) => void;
updateModalVisible: boolean;
values: Partial<TableListItem>;
}
const FormItem = Form.Item;
const { Step } = Steps;
const { TextArea } = Input;
const { Option } = Select;
const RadioGroup = Radio.Group;
export interface UpdateFormState {
formVals: FormValueType;
currentStep: number;
}
const formLayout = {
labelCol: { span: 7 },
wrapperCol: { span: 13 },
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const [formVals, setFormVals] = useState<FormValueType>({
name: props.values.name,
desc: props.values.desc,
key: props.values.key,
target: '0',
template: '0',
type: '1',
time: '',
frequency: 'month',
});
const [currentStep, setCurrentStep] = useState<number>(0);
const [form] = Form.useForm();
const {
onSubmit: handleUpdate,
onCancel: handleUpdateModalVisible,
updateModalVisible,
values,
} = props;
const forward = () => setCurrentStep(currentStep + 1);
const backward = () => setCurrentStep(currentStep - 1);
const handleNext = async () => {
const fieldsValue = await form.validateFields();
setFormVals({ ...formVals, ...fieldsValue });
if (currentStep < 2) {
forward();
} else {
handleUpdate({ ...formVals, ...fieldsValue });
}
};
const renderContent = () => {
if (currentStep === 1) {
return (
<>
<FormItem name="target" label="监控对象">
<Select style={{ width: '100%' }}>
<Option value="0">表一</Option>
<Option value="1">表二</Option>
</Select>
</FormItem>
<FormItem name="template" label="规则模板">
<Select style={{ width: '100%' }}>
<Option value="0">规则模板一</Option>
<Option value="1">规则模板二</Option>
</Select>
</FormItem>
<FormItem name="type" label="规则类型">
<RadioGroup>
<Radio value="0"></Radio>
<Radio value="1"></Radio>
</RadioGroup>
</FormItem>
</>
);
}
if (currentStep === 2) {
return (
<>
<FormItem
name="time"
label="开始时间"
rules={[{ required: true, message: '请选择开始时间!' }]}
>
<DatePicker
style={{ width: '100%' }}
showTime
format="YYYY-MM-DD HH:mm:ss"
placeholder="选择开始时间"
/>
</FormItem>
<FormItem name="frequency" label="调度周期">
<Select style={{ width: '100%' }}>
<Option value="month"></Option>
<Option value="week"></Option>
</Select>
</FormItem>
</>
);
}
return (
<>
<FormItem
name="name"
label="规则名称"
rules={[{ required: true, message: '请输入规则名称!' }]}
>
<Input placeholder="请输入" />
</FormItem>
<FormItem
name="desc"
label="规则描述"
rules={[{ required: true, message: '请输入至少五个字符的规则描述!', min: 5 }]}
>
<TextArea rows={4} placeholder="请输入至少五个字符" />
</FormItem>
</>
);
};
const renderFooter = () => {
if (currentStep === 1) {
return (
<>
<Button style={{ float: 'left' }} onClick={backward}>
上一步
</Button>
<Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
<Button type="primary" onClick={() => handleNext()}>
下一步
</Button>
</>
);
}
if (currentStep === 2) {
return (
<>
<Button style={{ float: 'left' }} onClick={backward}>
上一步
</Button>
<Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
<Button type="primary" onClick={() => handleNext()}>
完成
</Button>
</>
);
}
return (
<>
<Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
<Button type="primary" onClick={() => handleNext()}>
下一步
</Button>
</>
);
};
return (
<Modal
width={640}
bodyStyle={{ padding: '32px 40px 48px' }}
destroyOnClose
title="规则配置"
visible={updateModalVisible}
footer={renderFooter()}
onCancel={() => handleUpdateModalVisible()}
>
<Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
<Step title="基本信息" />
<Step title="配置规则属性" />
<Step title="设定调度周期" />
</Steps>
<Form
{...formLayout}
form={form}
initialValues={{
target: formVals.target,
template: formVals.template,
type: formVals.type,
frequency: formVals.frequency,
name: formVals.name,
desc: formVals.desc,
}}
>
{renderContent()}
</Form>
</Modal>
);
};
export default UpdateForm;
export interface TableListItem {
key: number;
disabled?: boolean;
href: string;
avatar: string;
name: string;
owner: string;
desc: string;
callNo: number;
status: number;
updatedAt: Date;
createdAt: Date;
progress: number;
}
export interface TableListPagination {
total: number;
pageSize: number;
current: number;
}
export interface TableListData {
list: TableListItem[];
pagination: Partial<TableListPagination>;
}
export interface TableListParams {
status?: string;
name?: string;
desc?: string;
key?: number;
pageSize?: number;
currentPage?: number;
filter?: { [key: string]: any[] };
sorter?: { [key: string]: any };
}
import { DownOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Divider, Dropdown, Menu, message, Input } from 'antd';
import React, { useState, useRef } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
import CreateForm from './components/CreateForm';
import UpdateForm, { FormValueType } from './components/UpdateForm';
import { TableListItem } from './data.d';
import { queryRule, updateRule, addRule, removeRule } from './service';
/**
* 添加节点
* @param fields
*/
const handleAdd = async (fields: TableListItem) => {
const hide = message.loading('正在添加');
try {
await addRule({ ...fields });
hide();
message.success('添加成功');
return true;
} catch (error) {
hide();
message.error('添加失败请重试!');
return false;
}
};
/**
* 更新节点
* @param fields
*/
const handleUpdate = async (fields: FormValueType) => {
const hide = message.loading('正在配置');
try {
await updateRule({
name: fields.name,
desc: fields.desc,
key: fields.key,
});
hide();
message.success('配置成功');
return true;
} catch (error) {
hide();
message.error('配置失败请重试!');
return false;
}
};
/**
* 删除节点
* @param selectedRows
*/
const handleRemove = async (selectedRows: TableListItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
try {
await removeRule({
key: selectedRows.map((row) => row.key),
});
hide();
message.success('删除成功,即将刷新');
return true;
} catch (error) {
hide();
message.error('删除失败,请重试');
return false;
}
};
const TableList: React.FC<{}> = () => {
const [createModalVisible, handleModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [stepFormValues, setStepFormValues] = useState({});
const actionRef = useRef<ActionType>();
const columns: ProColumns<TableListItem>[] = [
{
title: '规则名称',
dataIndex: 'name',
rules: [
{
required: true,
message: '规则名称为必填项',
},
],
},
{
title: '描述',
dataIndex: 'desc',
valueType: 'textarea',
},
{
title: '服务调用次数',
dataIndex: 'callNo',
sorter: true,
hideInForm: true,
renderText: (val: string) => `${val} 万`,
},
{
title: '状态',
dataIndex: 'status',
hideInForm: true,
valueEnum: {
0: { text: '关闭', status: 'Default' },
1: { text: '运行中', status: 'Processing' },
2: { text: '已上线', status: 'Success' },
3: { text: '异常', status: 'Error' },
},
},
{
title: '上次调度时间',
dataIndex: 'updatedAt',
sorter: true,
valueType: 'dateTime',
hideInForm: true,
renderFormItem: (item, { defaultRender, ...rest }, form) => {
const status = form.getFieldValue('status');
if (`${status}` === '0') {
return false;
}
if (`${status}` === '3') {
return <Input {...rest} placeholder="请输入异常原因!" />;
}
return defaultRender(item);
},
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => (
<>
<a
onClick={() => {
handleUpdateModalVisible(true);
setStepFormValues(record);
}}
>
配置
</a>
<Divider type="vertical" />
<a href="">订阅警报</a>
</>
),
},
];
return (
<PageHeaderWrapper>
<ProTable<TableListItem>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
toolBarRender={(action, { selectedRows }) => [
<Button type="primary" onClick={() => handleModalVisible(true)}>
<PlusOutlined /> 新建
</Button>,
selectedRows && selectedRows.length > 0 && (
<Dropdown
overlay={
<Menu
onClick={async (e) => {
if (e.key === 'remove') {
await handleRemove(selectedRows);
action.reload();
}
}}
selectedKeys={[]}
>
<Menu.Item key="remove">批量删除</Menu.Item>
<Menu.Item key="approval">批量审批</Menu.Item>
</Menu>
}
>
<Button>
批量操作 <DownOutlined />
</Button>
</Dropdown>
),
]}
tableAlertRender={({ selectedRowKeys, selectedRows }) => (
<div>
已选择 <a style={{ fontWeight: 600 }}>{selectedRowKeys.length}</a>&nbsp;&nbsp;
<span>
服务调用次数总计 {selectedRows.reduce((pre, item) => pre + item.callNo, 0)}
</span>
</div>
)}
request={(params, sorter, filter) => queryRule({ ...params, sorter, filter })}
columns={columns}
rowSelection={{}}
/>
<CreateForm onCancel={() => handleModalVisible(false)} modalVisible={createModalVisible}>
<ProTable<TableListItem, TableListItem>
onSubmit={async (value) => {
const success = await handleAdd(value);
if (success) {
handleModalVisible(false);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
rowKey="key"
type="form"
columns={columns}
rowSelection={{}}
/>
</CreateForm>
{stepFormValues && Object.keys(stepFormValues).length ? (
<UpdateForm
onSubmit={async (value) => {
const success = await handleUpdate(value);
if (success) {
handleUpdateModalVisible(false);
setStepFormValues({});
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
handleUpdateModalVisible(false);
setStepFormValues({});
}}
updateModalVisible={updateModalVisible}
values={stepFormValues}
/>
) : null}
</PageHeaderWrapper>
);
};
export default TableList;
import request from '@/utils/request';
import { TableListParams, TableListItem } from './data.d';
export async function queryRule(params?: TableListParams) {
return request('/api/rule', {
params,
});
}
export async function removeRule(params: { key: number[] }) {
return request('/api/rule', {
method: 'POST',
data: {
...params,
method: 'delete',
},
});
}
export async function addRule(params: TableListItem) {
return request('/api/rule', {
method: 'POST',
data: {
...params,
method: 'post',
},
});
}
export async function updateRule(params: TableListParams) {
return request('/api/rule', {
method: 'POST',
data: {
...params,
method: 'update',
},
});
}
@import '~antd/lib/style/themes/default.less';
.pre {
margin: 12px 0;
padding: 12px 20px;
background: @input-bg;
box-shadow: @card-shadow;
}
import React from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Card, Typography, Alert } from 'antd';
import styles from './Welcome.less';
const CodePreview: React.FC<{}> = ({ children }) => (
<pre className={styles.pre}>
<code>
<Typography.Text copyable>{children}</Typography.Text>
</code>
</pre>
);
export default (): React.ReactNode => (
<PageHeaderWrapper>
<Card>
<Alert
message="umi ui 现已发布,点击右下角 umi 图标即可使用"
type="success"
showIcon
banner
style={{
margin: -12,
marginBottom: 24,
}}
/>
<Typography.Text strong>
<a target="_blank" rel="noopener noreferrer" href="https://pro.ant.design/docs/block">
基于 block 开发,快速构建标准页面
</a>
</Typography.Text>
<CodePreview> npm run ui</CodePreview>
<Typography.Text
strong
style={{
marginBottom: 12,
}}
>
<a
target="_blank"
rel="noopener noreferrer"
href="https://pro.ant.design/docs/available-script#npm-run-fetchblocks"
>
获取全部区块
</a>
</Typography.Text>
<CodePreview> npm run fetch:blocks</CodePreview>
</Card>
<p
style={{
textAlign: 'center',
marginTop: 24,
}}
>
Want to add more pages? Please refer to{' '}
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
use block
</a>
</p>
</PageHeaderWrapper>
);
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const Template = () => {
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const Template = () => {
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,DatePicker,Button,Space,Pagination,Tooltip, Radio } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const Template = () => {
const [radioGroupValue,setRadioGroupValue]=useState(0)
const changeRadio = () => {
}
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
<div className={styles.box1}>
<div className={styles.box1item1}>Project</div>
<div className={styles.box1item2}>
<Input.Search style={{width:200,height:32}} />
</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>Facility Bookings</div>
<div className={styles.box2item2}>
<Radio.Group onChange={changeRadio} defaultValue={1}>
<Radio value={1}>A</Radio>
<Radio value={2}>B</Radio>
<Radio value={3}>C</Radio>
<Radio value={4}>D</Radio>
</Radio.Group>
</div>
</div>
<div className={styles.line}></div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}><Input style={{width:200}} /></div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}><Input style={{width:200}} /></div>
</div>
<div className={styles.box4}>
<div className={styles.box4item1}>Unit</div>
<div className={styles.box4item2}><Input style={{width:80}} /></div>
<div className={styles.box4item3}>#</div>
<div className={styles.box4item4}><Input style={{width:80}} /></div>
</div>
<div className={styles.line}></div>
<div className={styles.box5}>
<div className={styles.box5item1}>Booking Schedule</div>
<div className={styles.box5item2}><DatePicker /></div>
</div>
<div className={styles.box6}>
<div className={styles.box6item1}>Management fee required</div>
<div className={styles.box6item2}>$100.00</div>
</div>
<div className={styles.box6}>
<div className={styles.box6item1}>Deposit is required</div>
<div className={styles.box6item2}>$100.00</div>
</div>
<div className={styles.line}></div><Button type="primary" style={{ width: 80, height: 32 }}>提交</Button>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item2{
width:300px;
position: absolute;
left: (300px);
line-height: 30px;
}
.item4{
position: absolute;
left: (500px);
line-height: 30px;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.box1{
width: 100%;
height: 34px;
position: relative;
margin-bottom: 26px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 161px;
}
.box2{
width: 100%;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
width: 336px;
padding-left: 161px;
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box3{
width: 100%;
height: 32px;
position: relative;
margin-bottom: 28px;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 161px;
}
.box3item3{
position: absolute;
left: 467px;
}
.box3item4{
position: absolute;
left: 592px;
}
.box4{
width: 100%;
height: 32px;
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 161px;
}
.box4item3{
position: absolute;
left: 257px;
}
.box4item4{
position: absolute;
left: 281px;
}
.box5{
width: 100%;
height: 32px;
position: relative;
margin-bottom: 28px;
}
.box5item1{
position: absolute;
}
.box5item2{
position: absolute;
left: 161px;
}
.box6{
width: 100%;
height: 32px;
position: relative;
}
.box6item1{
position: absolute;
}
.box6item2{
position: absolute;
left: 216px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Upload,message,Button } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const pagination={defaultCurrent:1,total: 16}
const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
];
const PropertyServices = (props: { location: any; }) => {
const { formatMessage } = useIntl();
const nameofowner = formatMessage({ id: 'R.charge.input.nameofowner' })
const inputProject = formatMessage({ id: 'R.charge.input.project' })
const username = formatMessage({ id: 'R.charge.username' })
const project = formatMessage({ id: 'R.charge.project' })
const unit = formatMessage({ id: 'R.charge.unit' })
const status = formatMessage({ id: 'R.charge.status' })
const submissionTime = formatMessage({ id: 'R.charge.submissionTime' })
const actions = formatMessage({ id: 'R.charge.actions' })
const search = formatMessage({ id: 'R.charge.search' })
const addaccount = formatMessage({ id: 'R.charge.addaccount' })
const paymentsetting = formatMessage({ id: 'R.charge.paymentsetting' })
const { location} = props;
const tags = [username, project, unit, submissionTime, status,actions];
const [columns, setColums] = useState([{title:"1", dataIndex:"1", key:"1"}]);
useEffect(() => {
var tagval;
if (location.pathname=="/runTest2") {
tagval=['name', 'age', 'address', 'address', 'address'];
} else {
tagval=['name', 'age', 'age', 'age', 'address'];
}
var columnsTmp = [
{ title: tags[0], dataIndex: tagval[0], key: tagval[0] },
{ title: tags[1], dataIndex: tagval[1], key: tagval[1] },
{ title: tags[2], dataIndex: tagval[2], key: tagval[2] },
{ title: tags[3], dataIndex: tagval[3], key: tagval[3] },
{ title: tags[4], dataIndex: tagval[4], key: tagval[4] },
{ title: tags[5], dataIndex: tagval[3], key: tagval[3],
render: (text: any, record: any) => (
<Space size="middle">
<a>{text==33?"回复":"详情"}</a>
</Space>
),},];
setColums(columnsTmp)
},[location.pathname]);
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<input className={styles.item1} placeholder={nameofowner} />
<input className={styles.item2} placeholder={inputProject} />
<input className={styles.item4} placeholder={inputProject} />
<button className={styles.item3}>{search}</button>
</div>
{/* 列表组件 */}
<Table style={{ marginTop: 16 }} dataSource={dataSource} columns={columns} pagination={pagination} />;
</div>
);
};
export default PropertyServices;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.input{
width:200px;
height:32px;
background:none;
outline: none;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
font-size:14px;
font-weight:400;
line-height:30px;
text-indent: 19px;
}
.item1{
.input();
}
.item2{
.input();
margin-left: 20px;
}
.item4{
.input();
margin-left: 20px;
}
.item3{
width:80px;
height:32px;
border:1px solid rgba(24,144,255,1);
border-radius:2px;
background-color:#e7f4ff;
outline: none;
font-size:14px;
font-weight:400;
color:rgba(24,144,255,1);
line-height:30px;
cursor: pointer;
margin-left: 20px;
}
.item3:active{
background-color:#ffffff;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Radio,Button,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import TextArea from 'antd/lib/input/TextArea';
const Template = () => {
const [Approval, setApproval] = useState(0);
const ApprovalChoose = (value:number) => {
if (Approval != 0) {
if (Approval==value) {
setApproval(0)
}else {
setApproval(value)
}
} else {
setApproval(value)
}
}
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<div className={styles.item2}>Status: To Be Reviewed</div>
<div className={styles.item4}>Complaint Time:26-03-2020</div>
<button className={styles.item3}>返回</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>Sunshine Stanford </div>
<div className={styles.box1item2}>03#06-2050</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>Albert Street,175,Singapore,Albert,Bugis,Victoria Street,Rochor,Central Singapore 189969</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}>Josephine </div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}>13657724536</div>
<div className={styles.box3item5}>371712650@qq.com</div>
</div>
<div className={styles.line}></div>
<div className={styles.box3}>
<div className={styles.box3item1}>Applicant</div>
<div className={styles.box3item2}>Josephine </div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}>13657724536</div>
<div className={styles.box3item5}>371712650@qq.com</div>
</div>
<div className={styles.box4}>
<div className={styles.box4item1}>Relationship With the Owner</div>
<div className={styles.box4item2}>Family</div>
</div>
<div className={styles.box5}>
<div className={styles.box5item1}>Reason For Handling</div>
<div className={styles.box5item2}>
<TextArea autoSize={false} style={{width:400,height:80}}/>
</div>
</div>
<div className={styles.line}></div>
<div className={styles.box8}>
<div className={styles.box8item1}>Applcation Approval</div>
<div className={styles.box8item2}><div onClick={ApprovalChoose.bind(null, 1)} className={Approval == 1 ? styles.round : styles.roundnone}>
{
Approval == 1?(<div className={styles.triangleTopright}></div>):(<div/>)
}
<div>Agree</div></div></div>
<div className={styles.box8item3}><div onClick={ApprovalChoose.bind(null, 2)} className={Approval == 2 ? styles.round : styles.roundnone}>
{
Approval == 2?(<div className={styles.triangleTopright}></div>):(<div/>)
}
<div>Reject</div></div></div>
</div>
<div className={styles.box7}>
<div className={styles.box7item1}>Comment</div>
<div className={styles.box7item2}><TextArea style={{ width: 394, height: 80 }} autoSize={false} /></div>
</div>
<div className={styles.box9}>
<div className={styles.box9item1}>Access Card Has Been Collected</div>
<div className={styles.box9item2}>
<Radio>Collected</Radio>
</div>
</div>
<div><div className={styles.line}></div><Button type="primary">summit</Button></div>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item2{
width:300px;
position: absolute;
left: (300px);
line-height: 30px;
}
.item4{
position: absolute;
left: (500px);
line-height: 30px;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
//内容组件
.box1{
width: 100%;
height: 36px;
position: relative;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 215px;
}
.box2{
width: 100%;
height: 36px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 430px;
}
.box3{
width: 100%;
height: 36px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 132px;
}
.box3item3{
position: absolute;
left: 315px;
}
.box3item4{
position: absolute;
left: 445px;
}
.box3item5{
position: absolute;
left: 588px;
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box4{
width: 100%;
height: 36px;
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 253px;
}
.box5{
width: 100%;
height: 100px;
position: relative;
}
.box5item1{
position: absolute;
}
.box5item2{
margin-top: 10px;
position: absolute;
left: 198px;
}
//选择的list
.box8{
width: 100%;
height: 58px;
position: relative;
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select:none;
-khtml-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
user-select:none;
}
.box8item1{
position: absolute;
}
.box8item2{
position: absolute;
left: 199px;
line-height: 32px;
text-align: center;
cursor: pointer;
}
.box8item3{
position: absolute;
left: 308px;
line-height: 32px;
text-align: center;
cursor: pointer;
}
.round{
width: 80px;
height: 32px;
border: 1px solid rgba(24,144,255,1);
position: relative;
}
.roundnone{
width: 80px;
height: 32px;
border: 1px solid rgba(217,217,217,1);
position: relative;
}
.triangleTopright {
width: 0;
height: 0;
border-top: 12px solid rgba(24,144,255,1);
border-left: 12px solid transparent;
position: absolute;
right: 0;
}
//
.box7{
width: 100%;
height: 80px;
position: relative;
margin-bottom: 28px;
}
.box7item1{
position: absolute;
}
.box7item2{
position: absolute;
left: 141px;
}
.box9{
width: 100%;
height: 34px;
position: relative;
}
.box9item1{
position: absolute;
}
.box9item2{
position: absolute;
left: 268px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Button,Table,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import TextArea from 'antd/lib/input/TextArea';
const Template = () => {
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
<div className={styles.box1}>
<div className={styles.box1item1}>Contract Details</div>
<div className={styles.box1item2}><Input.Search style={{width:200}} /></div>
<div className={styles.box1item3}><Input style={{width:94}}/></div>
<div className={styles.box1item4}>#</div>
<div className={styles.box1item5}><Input style={{width:80}}/></div>
<div className={styles.box1item6}></div>
<div className={styles.box1item7}><Input style={{width:112}}/></div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}><Input style={{width:680}}/></div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>Owner</div>
<div className={styles.box3item2}><Input style={{ width: 200 }} /></div>
<div className={styles.box3item3}>Contract Details</div>
<div className={styles.box3item4}><Input style={{ width: 200 }} /></div>
<div className={styles.box3item5}><Input style={{width:200}}/></div>
</div>
<div className={styles.line}></div>
<div className={styles.box3}>
<div className={styles.box3item1}>Applicant</div>
<div className={styles.box3item2}><Input style={{ width: 200 }} /></div>
<div className={styles.box3item3}>Contract Details</div>
<div className={styles.box3item4}><Input style={{ width: 200 }} /></div>
<div className={styles.box3item5}><Input style={{width:200}}/></div>
</div>
<div className={styles.box4}>
<div className={styles.box4item1}>Relationship With The Owner</div>
<div className={styles.box4item2}><Input style={{ width: 120 }} /></div>
</div>
<div className={styles.box5}>
<div className={styles.box5item1}>Relationship With The Owner</div>
<div className={styles.box5item2}><TextArea autoSize={false} style={{ width: 520,height: 120}} /></div>
</div>
<div><div className={styles.line}></div><Button type="primary">summit</Button></div>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item2{
width:300px;
position: absolute;
left: (300px);
line-height: 30px;
}
.item4{
position: absolute;
left: (500px);
line-height: 30px;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.box1{
width: 100%;
height: 32px;
line-height: 32px;
position: relative;
margin-bottom: 16px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 146px;
}
.box1item3{
position: absolute;
left: 362px;
}
.box1item4{
position: absolute;
left: 461px;
}
.box1item5{
position: absolute;
left: 475px
}
.box1item6{
position: absolute;
left: 561px;
}
.box1item7{
position: absolute;
left: 583px;
}
.box2{
width: 100%;
height: 32px;
position: relative;
padding-left: 146px;
margin-bottom: 27px;
}
.box2item1{
position: absolute;
}
.box3{
width: 100%;
height: 32px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left:146px;
}
.box3item3{
position: absolute;
left:462px;
}
.box3item4{
position: absolute;
left:590px;
}
.box3item5{
position: absolute;
left:818px;
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box4{
width: 100%;
height: 32px;
margin-top: 28px;
margin-bottom: 28px;
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 252px;
}
.box5{
width: 100%;
height: 120px;
position: relative;
}
.box5item1{
position: absolute;
}
.box5item2{
position: absolute;
left: 252px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Upload,Modal, Button } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import TextArea from 'antd/lib/input/TextArea';
import { PlusOutlined } from '@ant-design/icons';
function getBase64(file: Blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
const stylesList5 = [
styles.box5item2,
styles.box5item3,
styles.box5item4
]
const stylesList8 = [
styles.box8item01,
styles.box8item01,
styles.box8item01
]
const Template = (props) => {
const [picList] = useState(['https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png']);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
const [fileList, setFileList] = useState([]);
const [editorFlag, setEditorFlag] = useState(true);
const { location } = props;
useEffect(() => {
if (location.pathname=="/runTest2") {
setEditorFlag(true)
} else {
setEditorFlag(false)
}
},[location.pathname]);
const uploadButton = (
<div>
<PlusOutlined />
<div className="ant-upload-text">Upload</div>
</div>
);
const handleCancel = () =>{ setPreviewVisible(false) };
const handlePreview = async (file: { url: string; preview: string; originFileObj: Blob; name: any; }) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setPreviewImage((file.url || file.preview));
setPreviewVisible(true);
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
};
const handleChange = (Obj:any) => {setFileList(Obj.fileList)}
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>Sunshine Stanford </div>
<div className={styles.box1item2}>03#06-2050</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>Albert Street,175,Singapore,Albert,Bugis,Victoria Street,Rochor,Central Singapore 189969</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}>Josephine </div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}>13657724536</div>
<div className={styles.box3item5}>371712650@qq.com</div>
</div>
<div className={styles.box4}>
<div className={styles.box4item1}>Warranty Details</div>
<div className={styles.box4item2}>
垃圾分类(英文名为:Garbage classification),一般是指按一定规定或标准将垃圾分类储存、分类投放和分类搬运,从而转变成公共资源的一系列活动的总称。分类的目的是提高垃圾的资源价值和经济价值,力争物尽其用。
垃圾在分类储存阶段属于公众的私有品,垃圾经公众分类投放后成为公众所在小区或社区的区域性准公共资源,垃圾分类搬运到垃圾集中点或转运站后成为没有排除性的公共资源。从国内外各城市对生活垃圾分类的方法来看,大致都是根据垃圾的成分、产生量,结合本地垃圾的资源利用和处理方式来进行分类的。
进行垃圾分类收集可以减少垃圾处理量和处理设备,降低处理成本,减少土地资源的消耗,具有社会、经济、生态等几方面的效益。
</div>
</div>
<div className={styles.box5}>
<div className={styles.box5item1}>Picture</div>
{
stylesList5.map((item, index) => {
return (
picList[index] != null ? (
<div className={item} key={"stylesList5_" + index}>
<img alt="pic1" style={{ width: 80, height: 80 }} src={picList[0]} />
</div>) : (<div key={"stylesList5_" + index} />)
)
})
}
</div>
<div className={styles.line}></div>
<div className={styles.box6}>
<div className={styles.box6item1}>response</div>
<div className={styles.box6item2}><Input style={{ width: 200 }} placeholder="Please enter a Name" disabled={!editorFlag} /></div>
<div className={styles.box6item3}>Contract Detail</div>
<div className={styles.box6item4}><Input style={{ width: 300 }} placeholder="Please enter contrat number" disabled={!editorFlag} /></div>
</div>
<div className={styles.box7}>
<div className={styles.box7item1}>Resolved Complaint</div>
<div className={styles.box7item2}><TextArea style={{ width: 394, height: 80 }} autoSize={false} disabled={!editorFlag}/></div>
</div>
<div className={fileList.length !=0?styles.box8:styles.box8min}>
<div className={styles.box8item1}>Resolved Complaint</div>
<div className={styles.box8item2}>
{
editorFlag ?
(<div>
<Upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" listType="picture-card" fileList={fileList} onPreview={handlePreview} onChange={handleChange}>
{fileList.length >= 3 ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="picture" style={{ width: '100%' }} src={previewImage} />
</Modal></div>) :
(<div>
{
stylesList8.map((item, index) => {
return (
picList[index] != null ? (
<div className={item} key={"stylesList8_" + index}>
<img alt="pic1" style={{ width: 80,height:80 }} src={picList[0]} />
</div>):(<div key={"stylesList8_" + index} />)
)
})
}
</div>)
}</div>
</div>
{
editorFlag ?
(<div><div className={styles.line}></div><Button type="primary">summit</Button></div>):(<div/>)
}
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
//内容组件
.box1{
width: 100%;
height: 36px;
position: relative;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 215px;
}
.box2{
width: 100%;
height: 36px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 430px;
}
.box3{
width: 100%;
height: 36px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 132px;
}
.box3item3{
position: absolute;
left: 315px;
}
.box3item4{
position: absolute;
left: 445px;
}
.box3item5{
position: absolute;
left: 588px;
}
.box4{
width: 100%;
height: 90px;
position: relative;
margin-bottom: 28px;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 193px;
top:10px;
width: 394px;
height: 80px;
background-color: rgba(244,244,244,1);
overflow-y: auto;
}
.box5{
width: 100%;
height: 80px;
position: relative;
}
.box5item1{
position: absolute;
}
.box5item2{
position: absolute;
left: 193px;
}
.box5item3{
position: absolute;
left: 311px;
}
.box5item4{
position: absolute;
left: 429px;
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box6{
width: 100%;
height: 32px;
position: relative;
margin-bottom: 28px;
line-height: 32px;
}
.box6item1{
position: absolute;
}
.box6item2{
position: absolute;
left: 193px;
}
.box6item3{
position: absolute;
left: 498px;
}
.box6item4{
position: absolute;
left: 626px;
}
.box7{
width: 100%;
height: 80px;
position: relative;
margin-bottom: 28px;
}
.box7item1{
position: absolute;
}
.box7item2{
position: absolute;
left: 193px;
}
.box8{
width: 100%;
height: 200px;
position: relative;
}
.box8min{
width: 100%;
height: 100px;
position: relative;
}
.box8item1{
position: absolute;
}
.box8item2{
position: absolute;
left: 193px;
}
.box8item3{
position: absolute;
left: 800px;
}
.box8item01{
position: absolute;
}
.box8item02{
position: absolute;
left: 20px;
}
.box8item03{
position: absolute;
left: 20px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Button,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const { TextArea } = Input;
const Template = () => {
const [editorFlag, setEditorFlag] = useState(true);
const [Approval, setApproval] = useState(0);
const ApprovalChoose = (value:number) => {
if (Approval != 0) {
if (Approval==value) {
setApproval(0)
}else {
setApproval(value)
}
} else {
setApproval(value)
}
}
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<div className={styles.item2}>Status: To Be Reviewed</div>
<div className={styles.item4}>Complaint Time:26-03-2020</div>
<button className={styles.item3}>返回</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>Sunshine Stanford </div>
<div className={styles.box1item2}>03#06-2050</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>Albert Street,175,Singapore,Albert,Bugis,Victoria Street,Rochor,Central Singapore 189969</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}>Josephine </div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}>13657724536</div>
<div className={styles.box3item5}>371712650@qq.com</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}>Josephine </div>
<div className={styles.box3item3}>Contact Details</div>
<div className={styles.box3item4}>13657724536</div>
<div className={styles.box3item5}>371712650@qq.com</div>
</div>
{/* 差异内容 */}
<div className={styles.box4}>
<div className={styles.box4item1}>Application For Decoration Time</div>
<div className={styles.box4item2}>26-03-2020</div>
<div className={styles.box4item3}>26-04-2020</div>
</div>
<div>Complaint Description</div>
<div className={styles.box5}>
<div className={styles.box5item1}>
whole house decoraion,the newly purchased rough house needs whole house decoration
</div>
</div>
<div className={styles.line}></div>
<div className={styles.box6}>
<div className={styles.box6item1}>response</div>
<div className={styles.box6item2}><Input style={{ width: 200 }} placeholder="Please enter a Name" disabled={!editorFlag} /></div>
<div className={styles.box6item3}>Contract Detail</div>
<div className={styles.box6item4}><Input style={{ width: 300 }} placeholder="Please enter contrat number" disabled={!editorFlag} /></div>
</div>
<div className={styles.box8}>
<div className={styles.box8item1}>Applcation Approval</div>
<div className={styles.box8item2}><div onClick={ApprovalChoose.bind(null, 1)} className={Approval == 1 ? styles.round : styles.roundnone}>
{
Approval == 1?(<div className={styles.triangleTopright}></div>):(<div/>)
}
<div>Agree</div></div></div>
<div className={styles.box8item3}><div onClick={ApprovalChoose.bind(null, 2)} className={Approval == 2 ? styles.round : styles.roundnone}>
{
Approval == 2?(<div className={styles.triangleTopright}></div>):(<div/>)
}
<div>Reject</div></div></div>
</div>
<div className={styles.box7}>
<div className={styles.box7item1}>Comment</div>
<div className={styles.box7item2}><TextArea style={{ width: 394, height: 80 }} autoSize={false} disabled={!editorFlag}/></div>
</div>
<div><div className={styles.line}></div><Button type="primary">summit</Button></div>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item2{
width:300px;
position: absolute;
left: (300px);
line-height: 30px;
}
.item4{
position: absolute;
left: (500px);
line-height: 30px;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
//内容组件
.box1{
width: 100%;
height: 36px;
position: relative;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 215px;
}
.box2{
width: 100%;
height: 36px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 430px;
}
.box3{
width: 100%;
height: 36px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 132px;
}
.box3item3{
position: absolute;
left: 315px;
}
.box3item4{
position: absolute;
left: 445px;
}
.box3item5{
position: absolute;
left: 588px;
}
// 差异内容
.box4{
width: 100%;
height: 36px;
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 272px;
}
.box4item3{
position: absolute;
left: 380px;
}
.box5{
width: 100%;
position: relative;
padding-left: 141px;
}
.box5item1{
width: 394px;
height: 80px;
background-color: rgba(244,244,244,1);
padding: 8px;
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box6{
width: 100%;
height: 32px;
position: relative;
margin-bottom: 28px;
line-height: 32px;
}
.box6item1{
position: absolute;
}
.box6item2{
position: absolute;
left: 141px;
}
.box6item3{
position: absolute;
left: 458px;
}
.box6item4{
position: absolute;
left: 585px;
}
.box7{
width: 100%;
height: 80px;
position: relative;
margin-bottom: 28px;
}
.box7item1{
position: absolute;
}
.box7item2{
position: absolute;
left: 141px;
}
.box8{
width: 100%;
height: 58px;
position: relative;
}
.box8item1{
position: absolute;
}
.box8item2{
position: absolute;
left: 199px;
line-height: 32px;
text-align: center;
cursor: pointer;
}
.box8item3{
position: absolute;
left: 308px;
line-height: 32px;
text-align: center;
cursor: pointer;
}
.round{
width: 80px;
height: 32px;
border: 1px solid rgba(24,144,255,1);
position: relative;
}
.roundnone{
width: 80px;
height: 32px;
border: 1px solid rgba(217,217,217,1);
position: relative;
}
.triangleTopright {
width: 0;
height: 0;
border-top: 12px solid rgba(24,144,255,1);
border-left: 12px solid transparent;
position: absolute;
right: 0;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './Usermanager.less';
import { Input ,Menu,Table,Space} from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
];
const ChargeManager = () => {
const { formatMessage } = useIntl();
const nameofowner = formatMessage({ id: 'R.charge.input.nameofowner' })
const inputProject = formatMessage({ id: 'R.charge.input.project' })
const username = formatMessage({ id: 'R.charge.username' })
const project = formatMessage({ id: 'R.charge.project' })
const unit = formatMessage({ id: 'R.charge.unit' })
const status = formatMessage({ id: 'R.charge.status' })
const submissionTime = formatMessage({ id: 'R.charge.submissionTime' })
const actions = formatMessage({ id: 'R.charge.actions' })
const search = formatMessage({ id: 'R.charge.search' })
const addaccount = formatMessage({ id: 'R.charge.addaccount' })
const paymentsetting = formatMessage({ id: 'R.charge.paymentsetting' })
const menuListNormal = ["A"]
const [display, setDisplay] = useState("A");//小组件:线框
const pagination={defaultCurrent:1,total: 16}
const columns = [
{
title: username,
dataIndex: 'name',
key: 'name',
},
{
title: project,
dataIndex: 'age',
key: 'age',
},
{
title: unit,
dataIndex: 'address',
key: 'address',
},
{
title: status,
dataIndex: 'address',
key: 'address',
},
{
title: submissionTime,
dataIndex: 'address',
key: 'address',
},
{
title: actions,
key: 'action',
render: (text: any, record: any) => (
<Space size="middle">
<a>详情</a>
</Space>
),
},
];
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<input className={styles.item1} placeholder={nameofowner} />
<input className={styles.item2} placeholder={inputProject} />
<button className={styles.item3}>{search}</button>
</div>
{/* 内容组件 */}
<div className={styles.box2}>
{
menuListNormal.map((item, index) => {
return (<button key={"bt"+ index } className={display==item?styles.buttonChoose:styles.buttonWait} onClick={()=>{setDisplay(item)}}>{item}</button>)
})
}
<button className={styles.buttonAdd2} >+ {addaccount} </button>
<button className={styles.buttonAdd} >{paymentsetting}</button>
</div>
{/* 列表组件 */}
<Table style={{ marginTop: 16 }} dataSource={dataSource} columns={columns} pagination={pagination} />;
</div>
);
};
export default ChargeManager;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
}
//边栏1
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.input{
width:200px;
height:32px;
background:none;
outline: none;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
font-size:14px;
font-weight:400;
line-height:30px;
text-indent: 19px;
}
.item1{
.input();
}
.item2{
.input();
margin-left: 20px;
}
.item3{
width:80px;
height:32px;
border:1px solid rgba(24,144,255,1);
border-radius:2px;
background-color:#e7f4ff;
outline: none;
font-size:14px;
font-weight:400;
color:rgba(24,144,255,1);
line-height:30px;
cursor: pointer;
margin-left: 20px;
}
.item3:active{
background-color:#ffffff;
}
//边栏2
.box2{
width: 100%;
height: 40px;
margin-top: 0px;
position: relative;
}
//小组件1
.buttonLine{
width:200px;
height:32px;
border:none;
background:none;
outline: none;
font-size:14px;
font-weight:400;
line-height:30px;
cursor: pointer;
}
.buttonWait{
.buttonLine();
border-bottom:2px solid#dedede;
color:#454545;
}
.buttonWait:hover{
border-bottom:3px solid rgba(24,144,255,1);
color:rgba(24,144,255,1);
}
.buttonChoose{
.buttonLine();
border-bottom:3px solid rgba(24,144,255,1);
color:rgba(24,144,255,1);
}
.buttonAdd{
background:none;
outline: none;
cursor: pointer;
width:148px;
height:32px;
border:1px solid rgba(24,144,255,1);
color:rgba(24,144,255,1);
border-radius:2px;
position: absolute;
right: 0;
}
.buttonAdd2{
.buttonAdd();
right: 168px;
}
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip, Button, Checkbox } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const {TextArea} = Input;
const ChangeSetting = () => {
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>none</div>
<div className={styles.box1item2}>
<Input style={{width:80,height:32}} placeholder="7"/><span></span>
</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>none</div>
<div className={styles.box2item2}>
<TextArea style={{ width: 423, height: 142 }} autoSize={false} />
<div className={styles.tip}>12321</div>
</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>none</div>
<div className={styles.box3item2}>
<div>
<Checkbox>12</Checkbox>
</div>
<div>
<Checkbox>12</Checkbox>
</div>
</div>
</div>
{/* 操作组件 */}
<div className={styles.line}></div>
<Button type="primary" style={{ width: 80, height: 32 }}>提交</Button>
</div>
);
};
export default ChangeSetting;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//边栏1
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.box1{
width: 100%;
height: 36px;
position: relative;
margin-bottom: 28px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 215px;
}
.box2{
width: 100%;
height: 183px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 215px;
}
.tip{
font-size:12px;
font-family:'Source Han Sans CN';
font-weight:400;
color:rgba(153,153,153,1);
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box3{
width: 100%;
height: 44px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 215px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './Usermanager.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import PDF from 'react-pdf-js';//
import { render } from 'react-dom';
const ChangeDetail = () => {
const { formatMessage } = useIntl();
const [scale, setState] = useState(1.3)
const [pages,setPages] = useState(1)
const [curpage,setCurpage]=useState(1)
const pdfpages = (pages: any) => {
setPages(pages)
//alert(pages)
}
const lookup = () => {
if (scale == 1.3) {
setState(2)
} else {
setState(1.3)
}
}
const pageChange = (curpage: any) => {
setCurpage(curpage)
}
return (
<div className={styles.base}>
{/* 组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<div className={styles.item2}>Upload Time 2020/7/14/18:09:00</div>
<button className={styles.item3}>返回</button>
</div>
<div className={styles.box2}>
<div className={styles.item21}>Project</div>
<div className={styles.item22}>Project Name</div>
<div className={styles.item23}>Building No</div>
<div className={styles.item234}>#</div>
<div className={styles.item24}>Floor</div>
<div className={styles.item245}>——</div>
<div className={styles.item25}>Room Number</div>
<div className={styles.item26}>Owner's Name</div>
</div>
<div className={styles.box4}>
<Pagination simple defaultCurrent={curpage} total={pages*10} onChange={pageChange} />
</div>
{
scale == 1.3 ? (
<Tooltip title="点击放大查看">
<div className={styles.box3out}>
<div className={styles.box3} onClick={lookup}>
<PDF key="pdfjs" file="test2.pdf" cMapUrl="http://127.0.0.1:80/" workerSrc = "//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.worker.js" scale={scale} page={curpage} onDocumentComplete={pdfpages} />
</div>
</div>
</Tooltip>
) : (
<Tooltip title="还原视图">
< div className={ styles.box3of } onClick={ lookup } >
<PDF key="pdfjs" file="test2.pdf" cMapUrl="http://127.0.0.1:80/" workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.worker.js' scale={scale} page={curpage} onDocumentComplete={pdfpages} />
</div>
</Tooltip>
)
}
</div>
);
};
export default ChangeDetail;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//边栏1
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item2{
width:300px;
position: absolute;
left: (180px+36px);
line-height: 30px;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.box2{
width: 100%;
height: 80px;
text-align: center;
position: relative;
}
.item21{
line-height: 32px;
width: 200px;
position: absolute;
}
.item22{
line-height: 32px;
position: absolute;
left: 200px;
width: 200px;
height: 32px;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.item23{
line-height: 32px;
position: absolute;
left: 416px;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
width: 94px;
height: 32px;
}
.item234{
line-height: 32px;
height: 32px;
position: absolute;
left: 514px;
}
.item24{
line-height: 32px;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
position: absolute;
left: 529px;
width: 80px;
height: 32px;
}
.item245{
line-height: 32px;
position: absolute;
left: 609px;
}
.item25{
line-height: 32px;
width: 112px;
height: 32px;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
position: absolute;
left: 640px;
}
.item26{
width: 200px;
height: 32px;
line-height: 32px;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
position: absolute;
left: 780px;
}
.box3out{
padding-left: 200px;
}
.box3{
width: 780px;
border:1px solid rgba(217,217,217,1);
background-color: rgba(244,244,244,1);
cursor: pointer;
overflow: auto;
}
.box3of{
cursor: pointer;
}
.box4{
padding-left: 200px;
}
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Button,Table,Space,Pagination,Tooltip } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
//导入自组件
import SelectOptions from '../../components/SelectOptions/index';
const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
];
const Template = () => {
const menuListNormal = ["A","B"]
const [display, setDisplay] = useState("A");//小组件:线框
const printContent = (comment: any)=>{
console.log(comment)
}
const pagination={defaultCurrent:1,total: 16}
const columns = [
{
title: "username",
dataIndex: 'name',
key: 'name',
},
{
title: "project",
dataIndex: 'age',
key: 'age',
},
{
title: "unit",
dataIndex: 'address',
key: 'address',
},
{
title: "status",
dataIndex: 'address',
key: 'address',
},
{
title: "submissionTime",
dataIndex: 'address',
key: 'address',
},
{
title: "actions",
key: 'action',
render: (text: any, record: any) => (
<Space size="middle">
<a>详情</a>
</Space>
),
},
];
return (
<div className={styles.base}>
{/* 头部组件 */}
{
display=="A"?
(<div>
<div className={styles.box1}>
<div className={styles.box1item1}>
<Input />
</div>
<div className={styles.box1item2}>
<Input />
</div>
</div>
{/* 差异内容 */}
<SelectOptions list={["美国", "美丽的", "美好", "加拿大", "加油", "BO"].sort()} onSubmit={printContent} />
<button className={styles.search}>search</button>
</div>) : (
<div className={styles.boxs}>
<input className={styles.items1} placeholder={"nameofowner"} />
<input className={styles.items2} placeholder={"inputProject"} />
<button className={styles.items3}>search</button>
</div>
)
}
{/* 内容组件 */}
<div className={styles.box2}>
{
menuListNormal.map((item, index) => {
return (<button key={"bt"+ index } className={display==item?styles.buttonChoose:styles.buttonWait} onClick={()=>{setDisplay(item)}}>{item}</button>)
})
}
<Button type="primary" className={styles.buttonAdd} >+ Add Facility</Button>
</div>
{/* 列表组件 */}
<Table style={{ marginTop:16}} dataSource={dataSource} columns={columns} pagination={pagination} />
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
.box1{
width: 100%;
height: 34px;
position: relative;
margin-bottom: 16px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 262px;
}
//按钮
.search{
width:80px;
height:32px;
border:1px solid rgba(24,144,255,1);
border-radius:2px;
background-color:#e7f4ff;
outline: none;
font-size:14px;
font-weight:400;
color:rgba(24,144,255,1);
line-height:30px;
cursor: pointer;
margin-top: 16px;
}
//小组件1
.box2{
width: 100%;
height: 40px;
margin-top: 27px;
position: relative;
}
.buttonLine{
width:200px;
height:32px;
border:none;
background:none;
outline: none;
font-size:14px;
font-weight:400;
line-height:30px;
cursor: pointer;
}
.buttonWait{
.buttonLine();
border-bottom:2px solid#dedede;
color:#454545;
}
.buttonWait:hover{
border-bottom:3px solid rgba(24,144,255,1);
color:rgba(24,144,255,1);
}
.buttonChoose{
.buttonLine();
border-bottom:3px solid rgba(24,144,255,1);
color:rgba(24,144,255,1);
}
.buttonAdd{
width:120px;
height:32px;
position: absolute;
right: 0;
}
//头部组件
.boxs{
width: 100%;
height: 64px;
position: relative;
}
.inputs{
width:200px;
height:32px;
background:none;
outline: none;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
font-size:14px;
font-weight:400;
line-height:30px;
text-indent: 19px;
}
.items1{
.inputs();
}
.items2{
.inputs();
margin-left: 20px;
}
.items3{
width:80px;
height:32px;
border:1px solid rgba(24,144,255,1);
border-radius:2px;
background-color:#e7f4ff;
outline: none;
font-size:14px;
font-weight:400;
color:rgba(24,144,255,1);
line-height:30px;
cursor: pointer;
margin-left: 20px;
}
.items3:active{
background-color:#ffffff;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Upload,Modal,Button,Space,Pagination,Tooltip, Radio, Checkbox } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import { PlusOutlined } from '@ant-design/icons';
function getBase64(file: Blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
const Template = () => {
const [namelist, setNamelist] = useState(['A']);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
const [fileList, setFileList] = useState([]);
const uploadButton = ( <div><PlusOutlined /> <div className="ant-upload-text">Upload</div></div>);
const handleCancel = () => { setPreviewVisible(false) };
const handleChange = (Obj: any) => { setFileList(Obj.fileList) }
const handlePreview = async (file: { url: string; preview: string; originFileObj: Blob; name: any; }) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setPreviewImage((file.url || file.preview));
setPreviewVisible(true);
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
};
const onCreate = () => {
let newlist = [...namelist];
newlist.push('D')
console.log(newlist)
setNamelist(newlist)
}
const onDelete = () => {
let newlist = [...namelist];
newlist.pop();
setNamelist(newlist)
}
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>返回</button>
</div>
<div className={styles.box1}>
<div className={styles.box1item1}>Project</div>
<div className={styles.box1item2}><Input.Search style={{width:200}} /></div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>Facility Name</div>
<div className={styles.box2item2}><Input style={{ width: 200 }} /></div>
<div className={styles.box2item3}>Management Expense</div>
<div className={styles.box2item4}><Input style={{ width: 80 }} /></div>
<div className={styles.box2item5}>Deposit</div>
<div className={styles.box2item6}><Input style={{width:80}} /></div>
</div>
<div style={{marginBottom:20}}>Facility Category</div>
<ul className={styles.ulist}>
{
namelist.map((item, index) => {
return (
<div key={"namelist"+index}>
<li className={styles.llist}><div className={styles.alist2}>
<div className={styles.alist1}>
<Upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" listType="picture-card" fileList={fileList} onPreview={handlePreview} onChange={handleChange}>
{fileList.length >= 1? null : uploadButton}
</Upload>
<Modal visible={previewVisible} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="picture" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
<div className={styles.alist2box}>
<div className={styles.alist2boxitem1}>Name</div>
<div className={styles.alist2boxitem2}><Input style={{width:120}}/></div>
<div className={styles.alist2boxitem3}><div style={{cursor:"pointer",color:"#FF5151",fontWeight:600}} onClick={onDelete}>Delete</div></div>
</div>
</div>
</li>
</div>
)})
}
<li className={styles.llist}><div className={styles.alist3} onClick={onCreate}></div></li>
</ul>
<div><p className={styles.alist2boxNone}>none</p></div>
<div className={styles.box3}>
<div className={styles.box3item1}>Facility Category</div>
<div className={styles.box3item2}>
<div className={styles.box3group1}>
<Upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" listType="picture-card" fileList={fileList} onPreview={handlePreview} onChange={handleChange}>
{fileList.length >= 1? null : uploadButton}
</Upload>
<Modal visible={previewVisible} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="picture" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
</div>
</div>
<div className={styles.line}></div>
<div className={styles.box5}>
<div className={styles.box5item1}>Opening Hours</div>
<div className={styles.box5item2}>09:00~20:00</div>
</div>
<div className={styles.box6}>
<div className={styles.box6item1}>Settings For Booking</div>
<div className={styles.box6item2}><Input style={{width:80}} /></div>
<div className={styles.box6item3}>Cancel Appointment</div>
<div className={styles.box6item4}><Input style={{width:80}}/></div>
<div className={styles.box6item5}>No deposit,cancellation</div>
<div className={styles.box6item6}><Input style={{width:80}}/></div>
<div className={styles.box6item7}>Bookable</div>
</div>
<div className={styles.box7}>
<div className={styles.box7item1}><Input style={{width:120}}/></div>
<div className={styles.box7item2}><Input style={{width:80}}/></div>
</div>
<div className={styles.box8}>
<div className={styles.box8item1}><Checkbox>Fixed Time Period For Each Appointment</Checkbox></div>
<div className={styles.box8item2}><Input style={{width:100}}/></div>
<div className={styles.box8item3}></div>
<div className={styles.box8item4}><Input style={{width:100}}/></div>
</div>
<div className={styles.line}></div>
<Button type="primary" style={{ width: 80, height: 32 }}>提交</Button>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
.box1{
width: 100%;
height: 34px;
position: relative;
margin-bottom: 28px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 139px;
}
.box2{
width: 100%;
height: 34px;
position: relative;
margin-bottom: 28px;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 139px;
}
.box2item3{
position: absolute;
left: 407px;
}
.box2item4{
position: absolute;
left: 565px;
}
.box2item5{
position: absolute;
left: 713px;
}
.box2item6{
position: absolute;
left: 778px;
}
.box3{
position: relative;
margin-bottom: 28px;
}
.box3item1{
position: absolute;
}
.box3item2{
padding-left: 160px;
}
.box3group1{
width: 266px;
height: 100px;
}
.box4{
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
padding-left: 160px;
}
.ulist{
float:left;
width:100%;
padding:0;
margin:0;
list-style-type:none;
}
.llist{
display:inline;
}
.alist{
float:left;
width: 160px;
}
.alist1{
position: relative;
width: 100px;
height: 100px;
}
.alist2{
float: left;
width: 326px;
margin-bottom: 20px;
z-index: 1;
}
.alist2box{
width: 100%;
height: 100%;
position: relative;
}
.alist2boxitem1{
position: absolute;
top: -100px;
left: 127px;
}
.alist2boxitem2{
position: absolute;
top: -100px;
left: 180px;
}
.alist2boxitem3{
position: absolute;
top: -40px;
left: 216px;
}
.alist2boxNone{
color: #ffffff;
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select:none;
-khtml-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
user-select:none;
}
.alist3{
z-index: 10;
cursor: pointer;
float: left;
width: 100px;
height: 100px;
background-color: rgba(244,244,244,1);
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box5{
position: relative;
width: 100%;
height: 34px;
margin-bottom: 28px;
}
.box5item1{
position: absolute;
}
.box5item2{
position: absolute;
left: 186px;
}
.box6{
position: relative;
line-height: 34px;
width: 100%;
height: 34px;
margin-bottom: 32px;
}
.box6item1{
position: absolute;
}
.box6item2{
position: absolute;
left: 186px;
}
.box6item3{
position: absolute;
left: 276px;
}
.box6item4{
position: absolute;
left: 478px;
}
.box6item5{
position: absolute;
left: 569px;
}
.box6item6{
position: absolute;
left: 794px;
}
.box6item7{
position: absolute;
left: 885px;
}
.box7{
position: relative;
width: 100%;
height: 34px;
margin-bottom: 28px;
}
.box7item1{
position: absolute;
left: 186px;
}
.box7item2{
position: absolute;
left: 323px;
}
.box8{
position: relative;
width: 100%;
height: 34px;
}
.box8item1{
position: absolute;
}
.box8item2{
position: absolute;
left: 346px;
}
.box8item3{
position: absolute;
left: 462px;
color: rgba(217,217,217,1);
}
.box8item4{
position: absolute;
left: 502px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip, Button, Radio } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import TextArea from 'antd/lib/input/TextArea';
const Template = () => {
return (
<div className={styles.base}>
{/* 头部组件 */}
<div className={styles.box}>
<div className={styles.item1}>View Facilty Bookings</div>
<button className={styles.item3}>返回</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>Appointment Status</div>
<div className={styles.box1item2}>Reservation Successful</div>
<div className={styles.box1item3}>OrderTime</div>
<div className={styles.box1item4}>02-12 10:20</div>
<div className={styles.box1item5}><Button style={{width:152}}>取消</Button></div>
</div>
<div className={styles.line}></div>
<div className={styles.box2}>
<div className={styles.box2item1}>Service Community</div>
<div className={styles.box2item2}>Constant company</div>
<div className={styles.box2item3}>Faciity Bookings</div>
<div className={styles.box2item4}>Swimming pool A</div>
<div className={styles.box2item5}>Booking Schedule</div>
<div className={styles.box2item6}>02-22 10:30</div>
<div className={styles.box2item7}>——</div>
<div className={styles.box2item8}>02-22 10:30</div>
</div>
<div className={styles.box3}>
<div className={styles.box3item1}>User Name</div>
<div className={styles.box3item2}>Josephine</div>
<div className={styles.box3item3}>Contract Details</div>
<div className={styles.box3item4}>13466573211</div>
<div className={styles.box3item5}>Unit</div>
<div className={styles.box3item6}>03#15-2020</div>
</div>
<div className={styles.line}></div>
<div className={styles.box4}>
<div className={styles.box4item1}>Management fee required</div>
<div className={styles.box4item2}>$ 100.00</div>
</div>
<div className={styles.box4}>
<div className={styles.box4item1}>Deposit is Required</div>
<div className={styles.box4item2}>$ 100.00</div>
</div>
{/* <Button style={{ width: 80, marginTop: 28 }}>支付</Button> */}
<div style={{marginBottom:19}}><Radio>Full Refund of Deposit</Radio></div>
<div className={styles.box5}>
<div className={styles.box5item1}><Radio>Partial Refund Of Deposit </Radio></div>
<div className={styles.box5item2}>$ <Input style={{width:80}}/></div>
</div>
<div className={styles.box6}>
<div className={styles.box6item1}><TextArea autoSize={false} style={{ width: 300,height:80}}/></div>
</div>
<button className={styles.search}>Return The Deposit Money</button>
</div>
);
};
export default Template;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 34px;
min-width: 1020px;
}
//头部组件
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 320px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
//内容组件
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box1{
width: 100%;
height: 34px;
position: relative;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 182px;
}
.box1item3{
position: absolute;
left: 440px;
}
.box1item4{
position: absolute;
left: 529px;
}
.box1item5{
position: absolute;
right:0;
}
.box2{
width: 100%;
height: 34px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 182px;
}
.box2item3{
position: absolute;
left: 374px;
}
.box2item4{
position: absolute;
left: 500px;
}
.box2item5{
position: absolute;
left: 682px;
}
.box2item6{
position: absolute;
left: 814px;
}
.box2item7{
position: absolute;
left: 910px;
}
.box2item8{
position: absolute;
left: 950px;
}
.box3{
position: relative;
width: 100%;
height: 34px;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 121px;
}
.box3item3{
position: absolute;
left: 256px;
}
.box3item4{
position: absolute;
left: 372px;
}
.box3item5{
position: absolute;
left: 524px;
}
.box3item6{
position: absolute;
left: 567px;
}
.box4{
width: 100%;
height: 34px;
position: relative;
}
.box4item1{
position: absolute;
}
.box4item2{
position: absolute;
left: 216px;
}
.box5{
width: 100%;
height: 34px;
position: relative;
margin-bottom: 16px;
}
.box5item1{
position: absolute;
}
.box5item2{
position: absolute;
left: 242px;
}
.box6{
padding-left: 254px;
position: relative;
}
.box6item{
position: absolute;
width: 300px;
height: 80px;
}
//按钮
.search{
width:194px;
height:34px;
border:1px solid rgba(24,144,255,1);
border-radius:2px;
background-color:#e7f4ff;
outline: none;
font-size:14px;
font-weight:400;
color:rgba(24,144,255,1);
line-height:34px;
cursor: pointer;
margin-top: 16px;
}
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;
import { Button, Col, Input, Row, Form, message } from 'antd';
import React, { useState, useCallback, useEffect } from 'react';
import omit from 'omit.js';
import { FormItemProps } from 'antd/es/form/FormItem';
import { getFakeCaptcha } from '@/services/login';
import ItemMap from './map';
import LoginContext, { LoginContextProps } from './LoginContext';
import styles from './index.less';
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) => {
const [count, setCount] = useState<number>(props.countDown || 0);
const [timing, setTiming] = useState(false);
// 这么写是为了防止restProps中 带入 onChange, defaultValue, rules props tabUtil
const {
onChange,
customProps,
defaultValue,
rules,
name,
getCaptchaButtonText,
getCaptchaSecondText,
updateActive,
type,
tabUtil,
...restProps
} = props;
const onGetCaptcha = useCallback(async (mobile: string) => {
const result = await getFakeCaptcha(mobile);
if (result === false) {
return;
}
message.success('获取验证码成功!验证码为:1234');
setTiming(true);
}, []);
useEffect(() => {
let interval: number = 0;
const { countDown } = props;
if (timing) {
interval = window.setInterval(() => {
setCount((preSecond) => {
if (preSecond <= 1) {
setTiming(false);
clearInterval(interval);
// 重置秒数
return countDown || 60;
}
return preSecond - 1;
});
}, 1000);
}
return () => clearInterval(interval);
}, [timing]);
if (!name) {
return null;
}
// get getFieldDecorator props
const options = getFormItemOptions(props);
const otherProps = restProps || {};
if (type === 'Captcha') {
const inputProps = omit(otherProps, ['onGetCaptcha', 'countDown']);
return (
<FormItem shouldUpdate noStyle>
{({ getFieldValue }) => (
<Row gutter={8}>
<Col span={16}>
<FormItem name={name} {...options}>
<Input {...customProps} {...inputProps} />
</FormItem>
</Col>
<Col span={8}>
<Button
disabled={timing}
className={styles.getCaptcha}
size="large"
onClick={() => {
const value = getFieldValue('mobile');
onGetCaptcha(value);
}}
>
{timing ? `${count} 秒` : '获取验证码'}
</Button>
</Col>
</Row>
)}
</FormItem>
);
}
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;
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;
import React, { useEffect } from 'react';
import { Tabs } from 'antd';
import LoginContext, { LoginContextProps } from './LoginContext';
const { TabPane } = Tabs;
const generateId = (() => {
let i = 0;
return (prefix = '') => {
i += 1;
return `${prefix}${i}`;
};
})();
type TabPaneProps = Parameters<typeof Tabs.TabPane>[0];
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;
@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;
}
}
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>;
Mobile: React.FunctionComponent<LoginItemProps>;
Captcha: 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
destroyInactiveTabPane
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;
Login.Mobile = LoginItem.Mobile;
Login.Captcha = LoginItem.Captcha;
export default Login;
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!',
},
],
},
};
import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons';
import { Alert, Checkbox } from 'antd';
import React, { useState } from 'react';
import { Link, connect, Dispatch } from 'umi';
import { StateType } from '@/models/login';
import { LoginParamsType } from '@/services/login';
import { ConnectState } from '@/models/connect';
import LoginForm from './components/Login';
import styles from './style.less';
const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginForm;
interface LoginProps {
dispatch: Dispatch;
userLogin: StateType;
submitting?: boolean;
}
const LoginMessage: React.FC<{
content: string;
}> = ({ content }) => (
<Alert
style={{
marginBottom: 24,
}}
message={content}
type="error"
showIcon
/>
);
const Login: React.FC<LoginProps> = (props) => {
const { userLogin = {}, submitting } = props;
const { status, type: loginType } = userLogin;
const [autoLogin, setAutoLogin] = useState(true);
const [type, setType] = useState<string>('account');
const handleSubmit = (values: LoginParamsType) => {
const { dispatch } = props;
dispatch({
type: 'login/login',
payload: { ...values, type },
});
};
return (
<div className={styles.main}>
<LoginForm activeKey={type} onTabChange={setType} onSubmit={handleSubmit}>
<Tab key="account" tab="账户密码登录">
{status === 'error' && loginType === 'account' && !submitting && (
<LoginMessage content="账户或密码错误(admin/ant.design)" />
)}
<UserName
name="userName"
placeholder="用户名: admin or user"
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
/>
<Password
name="password"
placeholder="密码: ant.design"
rules={[
{
required: true,
message: '请输入密码!',
},
]}
/>
</Tab>
<Tab key="mobile" tab="手机号登录">
{status === 'error' && loginType === 'mobile' && !submitting && (
<LoginMessage content="验证码错误" />
)}
<Mobile
name="mobile"
placeholder="手机号"
rules={[
{
required: true,
message: '请输入手机号!',
},
{
pattern: /^1\d{10}$/,
message: '手机号格式错误!',
},
]}
/>
<Captcha
name="captcha"
placeholder="验证码"
countDown={120}
getCaptchaButtonText=""
getCaptchaSecondText="秒"
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>
<div className={styles.other}>
其他登录方式
<AlipayCircleOutlined className={styles.icon} />
<TaobaoCircleOutlined className={styles.icon} />
<WeiboCircleOutlined className={styles.icon} />
<Link className={styles.register} to="/user/register">
注册账户
</Link>
</div>
</LoginForm>
</div>
);
};
export default connect(({ login, loading }: ConnectState) => ({
userLogin: login,
submitting: loading.effects['login/login'],
}))(Login);
@import '~antd/es/style/themes/default.less';
.main {
width: 368px;
margin: 0 auto;
@media screen and (max-width: @screen-sm) {
width: 95%;
}
.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;
}
}
:global {
.antd-pro-login-submit {
width: 100%;
margin-top: 24px;
}
}
}
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
min-width: 1020px;
position: relative;
}
//边栏1
.box{
width: 800px;
height: 450px;
padding-left: 211px;
padding-top: 160px;
border:1px solid rgba(217, 217, 217, 1);
box-shadow:0px 2px 15px 0px rgba(217,217,217,0.47);
position: absolute;
left: 50%;
top:222px;
margin-left: -400px;
}
.input{
margin-top: 26px;
width: 380px;
background: none;
outline: none;
border: none;
border-bottom: 1px solid rgba(217,217,217,1);
text-indent: 20px;
}
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.item1{
margin-top: 28px;
width: 380px;
position: relative;
}
.item2{
position: absolute;
right: 0;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Pagination,Tooltip, Button } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
const Login2 = () => {
return (
<div className={styles.base}>
<div className={styles.box}>
<div>please input a password</div>
<input type="password" className={styles.input} placeholder="" />
<div className={styles.item1}>
<Button type="primary" className={styles.item2}>提交</Button>
</div>
</div>
</div>
);
};
export default Login2;
@import '~antd/lib/style/themes/default.less';
//基石
.base {
width: 100%;
background-color: #ffffff;
padding: 116px;
min-width: 1020px;
}
//边栏1
.box{
width: 100%;
height: 64px;
position: relative;
}
.item1{
position: absolute;
width: 180px;
text-align: center;
border-left: 5px solid rgba(24,144,255,1);
font-family:'Source Han Sans CN';
font-size: 18px;
font-weight: 600;
color: #000000;
}
.item3{
width:80px;
height: 32px;
position: absolute;
right: 0;
outline: none;
background:none;
cursor: pointer;
border:1px solid rgba(217,217,217,1);
border-radius:2px;
}
//盒子内容
.box1{
width: 100%;
height: 36px;
position: relative;
margin-bottom: 28px;
}
.box1item1{
position: absolute;
}
.box1item2{
position: absolute;
left: 115px;
}
.box2{
width: 100%;
height: 40px;
position: relative;
}
.box2item1{
position: absolute;
}
.box2item2{
position: absolute;
left: 115px;
}
.tip{
font-size:12px;
font-family:'Source Han Sans CN';
font-weight:400;
color:rgba(153,153,153,1);
}
// 线栏
.line{
width: 100%;
height: 1px;
border-top: 1px solid rgba(217,217,217,1);
margin-top: 28px;
margin-bottom: 28px;
}
.box3{
width: 100%;
height: 44px;
position: relative;
}
.box3item1{
position: absolute;
}
.box3item2{
position: absolute;
left: 215px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import styles from './index.less';
import { Input ,Menu,Table,Space,Upload,Select,Button } from 'antd';
import { Link, useIntl, connect, Dispatch } from 'umi';
import { UploadOutlined } from '@ant-design/icons';
const uploadProp = {
name: 'file',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
headers: {
authorization: 'authorization-text',
}
};
const { Option } = Select;
const Others = () => {
const [list] = useState(['小区', '小区22222222222222222222222222222222222222222222222222','小区3','4','5','6','7','8','9','0','小0区','小06区','小67区']);
return (
<div className={styles.base}>
{/* 组件 */}
<div className={styles.box}>
<div className={styles.item1}>Payment Settings</div>
<button className={styles.item3}>退出</button>
</div>
{/* 内容组件 */}
<div className={styles.box1}>
<div className={styles.box1item1}>select</div>
<div className={styles.box1item2}>
<Select defaultValue={list[0]} style={{ width: 200 }}>
{
list.map((item, index) => {
return (
<Option key={'op_'+index} value={item}>{item}</Option>
)
})
}
</Select>
</div>
</div>
<div className={styles.box2}>
<div className={styles.box2item1}>upload</div>
<div className={styles.box2item2}>
<Upload {...uploadProp}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
</Upload>
</div>
</div>
{/* 操作组件 */}
<div className={styles.line}></div>
<Button type="primary" style={{ width: 80, height: 32 }}>提交</Button>
</div>
);
};
export default Others;
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-restricted-globals */
/* eslint-disable no-underscore-dangle */
/* globals workbox */
workbox.core.setCacheNameDetails({
prefix: 'antd-pro',
suffix: 'v1',
});
// Control all opened tabs ASAP
workbox.clientsClaim();
/**
* Use precaching list generated by workbox in build process.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
*/
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
/**
* Register a navigation route.
* https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
*/
workbox.routing.registerNavigationRoute('/index.html');
/**
* Use runtime cache:
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
*
* Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
*/
/**
* Handle API requests
*/
workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
/**
* Handle third party requests
*/
workbox.routing.registerRoute(
/^https:\/\/gw\.alipayobjects\.com\//,
workbox.strategies.networkFirst(),
);
workbox.routing.registerRoute(
/^https:\/\/cdnjs\.cloudflare\.com\//,
workbox.strategies.networkFirst(),
);
workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
/**
* Response to client after skipping waiting with MessageChannel
*/
addEventListener('message', (event) => {
const replyPort = event.ports[0];
const message = event.data;
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self.skipWaiting().then(
() =>
replyPort.postMessage({
error: null,
}),
(error) =>
replyPort.postMessage({
error,
}),
),
);
}
});
import request from '@/utils/request';
export interface LoginParamsType {
userName: string;
password: string;
mobile: string;
captcha: string;
}
export async function fakeAccountLogin(params: LoginParamsType) {
return request('/api/login/account', {
method: 'POST',
data: params,
});
}
export async function getFakeCaptcha(mobile: string) {
return request(`/api/login/captcha?mobile=${mobile}`);
}
import request from '@/utils/request';
export async function query(): Promise<any> {
return request('/api/users');
}
export async function queryCurrent(): Promise<any> {
return request('/api/currentUser');
}
export async function queryNotices(): Promise<any> {
return request('/api/notices');
}
declare module 'slash2';
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';
// google analytics interface
interface GAFieldsObject {
eventCategory: string;
eventAction: string;
eventLabel?: string;
eventValue?: number;
nonInteraction?: boolean;
}
interface Window {
ga: (
command: 'send',
hitType: 'event' | 'pageview',
fieldsObject: GAFieldsObject | string,
) => void;
reloadAuthorized: () => void;
}
declare let ga: Function;
// preview.pro.ant.design only do not use in your production ;
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined;
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
import RenderAuthorize from '@/components/Authorized';
import { getAuthority } from './authority';
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable import/no-mutable-exports */
let Authorized = RenderAuthorize(getAuthority());
// Reload the rights component
const reloadAuthorized = (): void => {
Authorized = RenderAuthorize(getAuthority());
};
/**
* hard code
* block need it。
*/
window.reloadAuthorized = reloadAuthorized;
export { reloadAuthorized };
export default Authorized;
import { reloadAuthorized } from './Authorized';
// use localStorage to store the authority info, which might be sent from server in actual project.
export function getAuthority(str?: string): string | string[] {
const authorityString =
typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-authority') : str;
// authorityString could be admin, "admin", ["admin"]
let authority;
try {
if (authorityString) {
authority = JSON.parse(authorityString);
}
} catch (e) {
authority = authorityString;
}
if (typeof authority === 'string') {
return [authority];
}
// preview.pro.ant.design only do not use in your production.
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
if (!authority && ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
return ['admin'];
}
return authority;
}
export function setAuthority(authority: string | string[]): void {
const proAuthority = typeof authority === 'string' ? [authority] : authority;
localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority));
// auto reload
reloadAuthorized();
}
/**
* request 网络请求工具
* 更详细的 api 文档: https://github.com/umijs/umi-request
*/
import { extend } from 'umi-request';
import { notification } from 'antd';
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
/**
* 异常处理程序
*/
const errorHandler = (error: { response: Response }): Response => {
const { response } = error;
if (response && response.status) {
const errorText = codeMessage[response.status] || response.statusText;
const { status, url } = response;
notification.error({
message: `请求错误 ${status}: ${url}`,
description: errorText,
});
} else if (!response) {
notification.error({
description: '您的网络发生异常,无法连接服务器',
message: '网络异常',
});
}
return response;
};
/**
* 配置request请求时的默认参数
*/
const request = extend({
errorHandler, // 默认错误处理
credentials: 'include', // 默认请求是否带上cookie
});
export default request;
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
}
import { isUrl, getRouteAuthority } from './utils';
describe('isUrl tests', (): void => {
it('should return false for invalid and corner case inputs', (): void => {
expect(isUrl([] as any)).toBeFalsy();
expect(isUrl({} as any)).toBeFalsy();
expect(isUrl(false as any)).toBeFalsy();
expect(isUrl(true as any)).toBeFalsy();
expect(isUrl(NaN as any)).toBeFalsy();
expect(isUrl(null as any)).toBeFalsy();
expect(isUrl(undefined as any)).toBeFalsy();
expect(isUrl('')).toBeFalsy();
});
it('should return false for invalid URLs', (): void => {
expect(isUrl('foo')).toBeFalsy();
expect(isUrl('bar')).toBeFalsy();
expect(isUrl('bar/test')).toBeFalsy();
expect(isUrl('http:/example.com/')).toBeFalsy();
expect(isUrl('ttp://example.com/')).toBeFalsy();
});
it('should return true for valid URLs', (): void => {
expect(isUrl('http://example.com/')).toBeTruthy();
expect(isUrl('https://example.com/')).toBeTruthy();
expect(isUrl('http://example.com/test/123')).toBeTruthy();
expect(isUrl('https://example.com/test/123')).toBeTruthy();
expect(isUrl('http://example.com/test/123?foo=bar')).toBeTruthy();
expect(isUrl('https://example.com/test/123?foo=bar')).toBeTruthy();
expect(isUrl('http://www.example.com/')).toBeTruthy();
expect(isUrl('https://www.example.com/')).toBeTruthy();
expect(isUrl('http://www.example.com/test/123')).toBeTruthy();
expect(isUrl('https://www.example.com/test/123')).toBeTruthy();
expect(isUrl('http://www.example.com/test/123?foo=bar')).toBeTruthy();
expect(isUrl('https://www.example.com/test/123?foo=bar')).toBeTruthy();
});
});
describe('getRouteAuthority tests', () => {
it('should return authority for each route', (): void => {
const routes = [
{ path: '/user', name: 'user', authority: ['user'], exact: true },
{ path: '/admin', name: 'admin', authority: ['admin'], exact: true },
];
expect(getRouteAuthority('/user', routes)).toEqual(['user']);
expect(getRouteAuthority('/admin', routes)).toEqual(['admin']);
});
it('should return inherited authority for unconfigured route', (): void => {
const routes = [
{ path: '/nested', authority: ['admin', 'user'], exact: true },
{ path: '/nested/user', name: 'user', exact: true },
];
expect(getRouteAuthority('/nested/user', routes)).toEqual(['admin', 'user']);
});
it('should return authority for configured route', (): void => {
const routes = [
{ path: '/nested', authority: ['admin', 'user'], exact: true },
{ path: '/nested/user', name: 'user', authority: ['user'], exact: true },
{ path: '/nested/admin', name: 'admin', authority: ['admin'], exact: true },
];
expect(getRouteAuthority('/nested/user', routes)).toEqual(['user']);
expect(getRouteAuthority('/nested/admin', routes)).toEqual(['admin']);
});
it('should return authority for substring route', (): void => {
const routes = [
{ path: '/nested', authority: ['user', 'users'], exact: true },
{ path: '/nested/users', name: 'users', authority: ['users'], exact: true },
{ path: '/nested/user', name: 'user', authority: ['user'], exact: true },
];
expect(getRouteAuthority('/nested/user', routes)).toEqual(['user']);
expect(getRouteAuthority('/nested/users', routes)).toEqual(['users']);
});
});
import { parse } from 'querystring';
import pathRegexp from 'path-to-regexp';
import { Route } from '@/models/connect';
/* eslint no-useless-escape:0 import/prefer-default-export:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
export const isUrl = (path: string): boolean => reg.test(path);
export const isAntDesignPro = (): boolean => {
if (ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
return true;
}
return window.location.hostname === 'preview.pro.ant.design';
};
// 给官方演示站点用,用于关闭真实开发环境不需要使用的特性
export const isAntDesignProOrDev = (): boolean => {
const { NODE_ENV } = process.env;
if (NODE_ENV === 'development') {
return true;
}
return isAntDesignPro();
};
export const getPageQuery = () => parse(window.location.href.split('?')[1]);
/**
* props.route.routes
* @param router [{}]
* @param pathname string
*/
export const getAuthorityFromRouter = <T extends Route>(
router: T[] = [],
pathname: string,
): T | undefined => {
const authority = router.find(
({ routes, path = '/', target = '_self' }) =>
(path && target !== '_blank' && pathRegexp(path).exec(pathname)) ||
(routes && getAuthorityFromRouter(routes, pathname)),
);
if (authority) return authority;
return undefined;
};
export const getRouteAuthority = (path: string, routeData: Route[]) => {
let authorities: string[] | string | undefined;
routeData.forEach((route) => {
// match prefix
if (pathRegexp(`${route.path}/(.*)`).test(`${path}/`)) {
if (route.authority) {
authorities = route.authority;
}
// exact match
if (route.path === path) {
authorities = route.authority || authorities;
}
// get children authority recursively
if (route.routes) {
authorities = getRouteAuthority(path, route.routes) || authorities;
}
}
});
return authorities;
};
// eslint-disable-next-line
const NodeEnvironment = require('jest-environment-node');
const getBrowser = require('./getBrowser');
class PuppeteerEnvironment extends NodeEnvironment {
// Jest is not available here, so we have to reverse engineer
// the setTimeout function, see https://github.com/facebook/jest/blob/v23.1.0/packages/jest-runtime/src/index.js#L823
setTimeout(timeout) {
if (this.global.jasmine) {
// eslint-disable-next-line no-underscore-dangle
this.global.jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout;
} else {
this.global[Symbol.for('TEST_TIMEOUT_SYMBOL')] = timeout;
}
}
async setup() {
const browser = await getBrowser();
const page = await browser.newPage();
this.global.browser = browser;
this.global.page = page;
}
async teardown() {
const { page, browser } = this.global;
if (page) {
await page.close();
}
if (browser) {
await browser.disconnect();
}
if (browser) {
await browser.close();
}
}
}
module.exports = PuppeteerEnvironment;
/* eslint-disable global-require */
/* eslint-disable import/no-extraneous-dependencies */
const { execSync } = require('child_process');
const { join } = require('path');
const findChrome = require('carlo/lib/find_chrome');
const detectInstaller = require('detect-installer');
const installPuppeteer = () => {
// find can use package manger
const packages = detectInstaller(join(__dirname, '../../'));
// get installed package manger
const packageName = packages.find(detectInstaller.hasPackageCommand) || 'npm';
console.log(`🤖 will use ${packageName} install puppeteer`);
const command = `${packageName} ${packageName.includes('yarn') ? 'add' : 'i'} puppeteer`;
execSync(command, {
stdio: 'inherit',
});
};
const initPuppeteer = async () => {
try {
// eslint-disable-next-line import/no-unresolved
const findChromePath = await findChrome({});
const { executablePath } = findChromePath;
console.log(`🧲 find you browser in ${executablePath}`);
return;
} catch (error) {
console.log('🧲 no find chrome');
}
try {
require.resolve('puppeteer');
} catch (error) {
// need install puppeteer
await installPuppeteer();
}
};
initPuppeteer();
/* eslint-disable global-require */
/* eslint-disable import/no-extraneous-dependencies */
const findChrome = require('carlo/lib/find_chrome');
const getBrowser = async () => {
try {
// eslint-disable-next-line import/no-unresolved
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--no-first-run',
'--no-zygote',
'--no-sandbox',
],
});
return browser;
} catch (error) {
// console.log(error)
}
try {
// eslint-disable-next-line import/no-unresolved
const puppeteer = require('puppeteer-core');
const findChromePath = await findChrome({});
const { executablePath } = findChromePath;
const browser = await puppeteer.launch({
executablePath,
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--no-first-run',
'--no-zygote',
'--no-sandbox',
],
});
return browser;
} catch (error) {
console.log('🧲 no find chrome');
}
throw new Error('no find puppeteer');
};
module.exports = getBrowser;
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable eslint-comments/no-unlimited-disable */
const { spawn } = require('child_process');
// eslint-disable-next-line import/no-extraneous-dependencies
const { kill } = require('cross-port-killer');
const env = Object.create(process.env);
env.BROWSER = 'none';
env.TEST = true;
env.UMI_UI = 'none';
env.PROGRESS = 'none';
// flag to prevent multiple test
let once = false;
const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], {
env,
});
startServer.stderr.on('data', (data) => {
// eslint-disable-next-line
console.log(data.toString());
});
startServer.on('exit', () => {
kill(process.env.PORT || 8000);
});
console.log('Starting development server for e2e tests...');
startServer.stdout.on('data', (data) => {
console.log(data.toString());
// hack code , wait umi
if (
(!once && data.toString().indexOf('Compiled successfully') >= 0) ||
data.toString().indexOf('Theme generated successfully') >= 0
) {
// eslint-disable-next-line
once = true;
console.log('Development server is started, ready to run tests.');
const testCmd = spawn(
/^win/.test(process.platform) ? 'npm.cmd' : 'npm',
['test', '--', '--maxWorkers=1', '--runInBand'],
{
stdio: 'inherit',
},
);
testCmd.on('exit', (code) => {
startServer.kill();
process.exit(code);
});
}
});
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "esnext",
"lib": ["esnext", "dom"],
"sourceMap": true,
"baseUrl": ".",
"jsx": "react",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"allowJs": true,
"skipLibCheck": true,
"experimentalDecorators": true,
"strict": true,
"paths": {
"@/*": ["./src/*"],
"@@/*": ["./src/.umi/*"]
}
},
"exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment