Commit e0b20cb7 authored by Yu's avatar Yu Committed by 陈帅

feat: merge master to v4 (#3666)

* fix: error-processing-request (#3402)

* Close: #3398

* error authority is undefined

* feat: add new config "menu.enableLocale" (#3310)

* feat: add new config "layoutLocal"

* Use enableLayoutLocale instead of layoutLocale

* Use enableMenuLocale instead of enableMenuLocale

* use menu.disableLocal

* Fix an issue in list item Add (#3423)

* try fix test error (#3424)

* try fix test error

* try fix ci error in windows

* feat: Routing is in the root directory can also matchs. (#3364)

* fix viewport content

Error parsing a meta element's content: ';' is not a valid key-value pair separator. Please use ',' instead.

* Set up CI with Azure Pipelines (#3268)

* Set up CI with Azure Pipelines

* change some js filemod to 644 (#3447)

* fix #2851 (#3440)

* add responsive-table (#3472)

* remove PureComponent (#3470)

* remove PureComponent

* fix typo

* chore: upgrade jest-puppeteer and remove some puppeteer config

* html prettier (#3473)

* prettier html

* update dependencies

* centering icon

* set icon size

* edit text

* Removed redundant colon - zh-CN (#3480)

* Removed redundant colon - zh-CN

* Removed redundant colon - zh-TW

* Removed redundant colon - en-US

* fix: horizontal scroll bar appears on SiderMenu with light navTheme (#3381)

* fix: horizontal scroll bar appears on SiderMenu with light navTheme

* a better solution

* fix Badge error (#3488)

* fix tag error

* Move flags to badges in README

* Make "home" optional in breadcrumb (#3416)

* fixed submenu collapsed when refresh page (#3494)

* perf: add checkout config in azure (#3498)

* 🆙 upgrade deps (#3503)

* fix: unresponsive collapse btn on mobile (#3504)

* style: prettier SiderMenu.js (#3501)

* style: prettier SiderMenu.js

* new config file

* fix ci error

* feat: support pwa config (#3508)

* feat: support pwa config

* style: change code style

* reset ci (#3507)

* add actionsText prop to TagSelect, add locales to List page #3367 (#3442)

* add actionsText prop to TagSelect, add locales to List page

* add actionsText prop to TagSelect, add locales to List page

* 修正繁体中文文案 (#3511)

* Route authority attribute behavior (#3514)

* fix customize menu icon bug (#3509)

* fix customize menu icon bug

当采用自定义菜单图标(即通过url引用方式)且菜单折叠时,文字不隐藏

* fix customize menu icon bug

修改样式

* feat: add a demo that jump to details (#3502)

* feat: add a demo that jump to details

* feat: hide progress in coi

* remove trigger config

* remove fetchDepth: 1

* refactor: userinfo and application  from api

* style: fix code style

* Enhance stylelint rules, fix propTypes error of TagSelect (#3518)

* add stylelint shareable config for css modules

* fix CSS pseudo element with double colon

* support stylelint declaration-block-no-ignored-properties rule

* support sorted CSS properties order for readability and consistency

* autofix order of all styles by lint:fix script

* fix propTypes error of TagSelect component

* Revert "autofix order of all styles by lint:fix script"

This reverts commit 51cb9d055f607489a259fd878d80c6b6e70c61de.

* Revert "support sorted CSS properties order for readability and consistency"

This reverts commit ff6c24d263cf5d3f73880bd7a5866efb4911de9f.

* make lint:fix work for stylelint

* Revert "Revert "autofix order of all styles by lint:fix script""

This reverts commit 946ed0a351542904e70680c166e47a1f5080d1ca.

* Revert "Revert "support sorted CSS properties order for readability and consistency""

This reverts commit 31b557e3826e6e5c5cf9b591fc97e24b9b2abb6e.

* Update README.zh-CN.md

* doc: add umi-badge (#3538)

* doc: add umi-badge

* Update README.ru-RU.md (#3539)

add umi badge

* feat: Officially traded will use cdn to optimize bizchart (#3535)

* route authority attribute behavior no use while (#3522)

* route authority attribute behavior no use while

* i18n pt-BR: analysis & component (#3540)

* Translation of form and monitor (pt-BR)

* Change flag of Portugal (pt-PT) to Brazil (pt-BR)

* i18n pt-BR: analysis & component

* Fix missing export default  (#3525)

* fix types missing export default

* Update package.json

* Update index.d.ts

* Update index.d.ts

* Update index.d.ts

* 增加IconFont组件、菜单图标可以使用自己的IconFont项目图标 (#3517)

* feature:
1.add iconfont component;
2.menu can add iconfont icon.

* fix: 调整菜单引入iconfont的方式为String.
1. 新增IconFont组件,需在组件内配置自己的IconFont图标项目地址;
2. 然后,菜单图标可以引入自己的IconFont图标,图标字符串以icon-开头.

* ajust: put the IconFont Script Url into defaultSetting.js

* 调整iconfontUrl名称

* fix:注释更新

* 留空iconfontUrl

* Site title use defaultSettings (#3546)

* Site title od top use defaultSettings (#3551)

* pref: optimize performance (#3542)

* pref: optimize performance

* pref: use less img

* pref: use less img

* fix: Eslint warning of Mock dependence (#3554)

* perf: use requestAnimationFrame

* Fix: onPressEnter trigger twice login request in IE11

* Fix: onPressEnter trigger twice login request in IE11

* Login title (#3564)

* Added document title to UserLayout by identifying the current route object and using its name kaey to set the title

* Adjusment to document title

* feat: use same getPageTile function

* when select 3 item ,text branch

* style: use standard frontmatter

* doc: remove subtitle in en-Us

* better demo md

* fix PageHeader no title bug (#3583)

* fix PageHeader no title bug

* default value

* fix: React does not recognize the `staticContext` prop on a DOM element. (#3582)

* doc: better demo

* remove drawer onHandleClick, ant-design/ant-design#15051 (#3602)

* Use Umi Permission Routing (#3587)

Use Umi Permission Routing

* fix the problem that breadcrumbNameMap does not contain hidden menus. (#3606)

* Update enzyme to version 3.9.0

* Update package.json

* Update jest-puppeteer to version 4.0.0

* Update prettier to version 1.16.4

* Update package.json

* 🐛 fix TagCloud style override bug (#3632)

* Update stylelint-config-prettier to version 5.0.0

* Update index.md (#3644)

* dead code (#3639)

* dead code
Close: #3637

* delete dead props

* [NoticeIcon] Replace `LoadMore` with `ViewMore` button (#3439)

* enhance LoadMore: Debounce

* enhance LoadMore: debounce

* use Tag instead of div

* rewrite margin-right of Tag

* hide LoadMore in NoticeList without onLoadMore

* another style

* fix a mistake

* remove local config

* fix a bug

* user-select: none

* remover local config

* replace global/fetchMoreNotices with global/fetchNotices

* replace LoadMore with ViewMore

* remove prop `name` in NoticeIcon

* fix: tab title does not show correct text

* Fix margin top style error of Description List following Description List (#3653)

* fix login model statu
parent 0a9094ec
version: 2
jobs:
build:
docker:
- image: circleci/node:latest
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run build
test:
docker:
- image: circleci/node:latest-browsers
steps:
- checkout
- run: npm install
- run:
command: npm run test:all
no_output_timeout: 30m
workflows:
version: 2
build_and_test:
jobs:
- build
- test
......@@ -24,7 +24,7 @@ module.exports = {
2,
{
optionalDependencies: true,
devDependencies: ['**/tests/**.js', '/mock/**.js', '**/**.test.js'],
devDependencies: ['**/tests/**.js', '/mock/**/**.js', '**/**.test.js'],
},
],
'jsx-a11y/no-noninteractive-element-interactions': 0,
......
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"extends": [
"stylelint-config-standard",
"stylelint-config-css-modules",
"stylelint-config-rational-order",
"stylelint-config-prettier"
],
"plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
"rules": {
"declaration-empty-line-before": null,
"no-descending-specificity": null,
"selector-pseudo-class-no-unknown": null,
"selector-pseudo-element-colon-notation": null
"plugin/declaration-block-no-ignored-properties": true
}
}
......@@ -6,13 +6,12 @@ English | [简体中文](./README.zh-CN.md) | [Русский](./README.ru-RU.md
An out-of-box UI solution for enterprise applications as a React boilerplate.
[![CircleCI Status](https://circleci.com/gh/ant-design/ant-design-pro.svg?style=svg)](https://circleci.com/gh/ant-design/ant-design-pro/)
[![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master)
[![Build With Umi](https://img.shields.io/badge/build%20with-umi-028fe4.svg?style=flat-square)](http://umijs.org/)
[![Build Status](https://dev.azure.com/ant-design/ant-design-pro/_apis/build/status/ant-design.ant-design-pro?branchName=master)](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro?type=dev)
[![Gitter](https://img.shields.io/gitter/room/ant-design/pro-english.svg)](https://gitter.im/ant-design/pro-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)(🇺🇸)
[![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-pro.svg?style=flat-square)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)(🇨🇳)
[![Gitter](https://img.shields.io/gitter/room/ant-design/pro-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/pro-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design-pro](https://img.shields.io/gitter/room/ant-design/ant-design-pro.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
......
......@@ -6,11 +6,12 @@
UI-решение "из коробки" для корпоративных приложений как React boilerplate
[![CircleCI Status](https://circleci.com/gh/ant-design/ant-design-pro.svg?style=svg)](https://circleci.com/gh/ant-design/ant-design-pro/)
[![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master)
[![Build With Umi](https://img.shields.io/badge/build%20with-umi-028fe4.svg?style=flat-square)](http://umijs.org/)
[![Build Status](https://dev.azure.com/ant-design/ant-design-pro/_apis/build/status/ant-design.ant-design-pro?branchName=master)](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro?type=dev)
[![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Gitter](https://img.shields.io/gitter/room/ant-design/pro-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/pro-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design-pro](https://img.shields.io/gitter/room/ant-design/ant-design-pro.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
......
......@@ -6,11 +6,11 @@
开箱即用的中台前端/设计解决方案。
[![CircleCI Status](https://circleci.com/gh/ant-design/ant-design-pro.svg?style=svg)](https://circleci.com/gh/ant-design/ant-design-pro/)
[![Build status](https://ci.appveyor.com/api/projects/status/67fxu2by3ibvqtat/branch/master?svg=true)](https://ci.appveyor.com/project/afc163/ant-design-pro/branch/master)
[![Build With Umi](https://img.shields.io/badge/build%20with-umi-028fe4.svg?style=flat-square)](http://umijs.org/)
[![Build Status](https://dev.azure.com/ant-design/ant-design-pro/_apis/build/status/ant-design.ant-design-pro?branchName=master)](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro?type=dev)
[![Gitter](https://badges.gitter.im/ant-design/ant-design-pro.svg)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design-pro](https://img.shields.io/gitter/room/ant-design/ant-design-pro.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
......@@ -84,8 +84,8 @@ $ npm start # 访问 http://localhost:8000
```bash
# preview
$ docker pull chenshuai2144/ant-design-pro
$ docker run -p 80:80 chenshuai2144/ant-design-pro
$ docker pull antdesign/ant-design-pro
$ docker run -p 80:80 antdesign/ant-design-pro
# open http://localhost
# dev
......
# Test against the latest version of this Node.js version
environment:
nodejs_version: '10'
# this is how to allow failing jobs in the matrix
matrix:
fast_finish: true # set this flag to immediately finish build once one of the jobs fails.
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install
# Output useful info for debugging.
- node --version
- npm --version
# Post-install test scripts.
test_script:
- npm run lint
- npm run test:all
- npm run build
# Don't actually build.
build: off
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
name: ant design pro
trigger:
- master
jobs:
- job: lintAndBuild
pool:
vmImage: 'Ubuntu-16.04'
steps:
- checkout: self
clean: false
- script: yarn install
displayName: install
- script: npm run lint
displayName: lint
- script: npm run build
env:
PROGRESS: none
displayName: build
- job: test
pool:
vmImage: 'Ubuntu-16.04'
container:
image: circleci/node:latest-browsers
options: '-u root'
steps:
- script: yarn install
displayName: install
- script: npm run test:all
env:
PROGRESS: none
displayName: test
- job: Windows
pool:
vmImage: 'vs2017-win2016'
steps:
- task: NodeTool@0
inputs:
versionSpec: '11.x'
- script: yarn install
displayName: install
- script: npm run lint
displayName: lint
- script: npm run build
env:
PROGRESS: none
displayName: build
- job: MacOS
pool:
vmImage: 'macOS-10.13'
steps:
- task: NodeTool@0
inputs:
versionSpec: '11.x'
- script: yarn install
displayName: install
- script: npm run lint
displayName: lint
- script: npm run
env:
PROGRESS: none
displayName: build
......@@ -4,6 +4,9 @@ import webpackPlugin from './plugin.config';
import defaultSettings from '../src/defaultSettings';
import slash from 'slash2';
const { pwa, primaryColor } = defaultSettings;
const { NODE_ENV, APP_TYPE, TEST } = process.env;
const plugins = [
[
'umi-plugin-react',
......@@ -20,6 +23,7 @@ const plugins = [
dynamicImport: {
loadingComponent: './components/PageLoading/index',
webpackChunkName: true,
level: 3,
},
pwa: {
workboxPluginMode: 'InjectManifest',
......@@ -51,7 +55,7 @@ const plugins = [
// 针对 preview.pro.ant.design 的 GA 统计代码
// 业务上不需要这个
if (process.env.APP_TYPE === 'site') {
if (APP_TYPE === 'site') {
plugins.push([
'umi-plugin-ga',
{
......@@ -64,7 +68,7 @@ export default {
// add for transfer to umi
plugins,
define: {
APP_TYPE: process.env.APP_TYPE || '',
APP_TYPE: APP_TYPE || '',
},
treeShaking: true,
targets: {
......@@ -96,10 +100,11 @@ export default {
// Theme for antd
// https://ant.design/docs/react/customize-theme-cn
theme: {
'primary-color': defaultSettings.primaryColor,
'primary-color': primaryColor,
},
externals: {
'@antv/data-set': 'DataSet',
bizcharts: 'BizCharts',
},
// proxy: {
// '/server/api/': {
......
......@@ -17,7 +17,7 @@
"firebase-functions": "^2.1.0",
"mockjs": "^1.0.1-beta3",
"moment": "^2.22.2",
"path-to-regexp": "^2.2.1"
"path-to-regexp": "^3.0.0"
},
"private": true
}
// ps https://github.com/GoogleChrome/puppeteer/issues/3120
module.exports = {
launch: {
headless: true,
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--single-process',
],
args: ['--disable-gpu', '--disable-dev-shm-usage', '--no-first-run', '--no-zygote'],
},
};
const fakeNotices = [
const getNotices = (req, res) =>
res.json([
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
......@@ -94,21 +95,7 @@ const fakeNotices = [
status: 'processing',
type: 'event',
},
];
const getNotices = (req, res) => {
if (req.query && req.query.type) {
const startFrom = parseInt(req.query.lastItemId, 10) + 1;
const result = fakeNotices
.filter(({ type }) => type === req.query.type)
.map((notice, index) => ({
...notice,
id: `0000000${startFrom + index}`,
}));
return res.json(startFrom > 24 ? result.concat(null) : result);
}
return res.json(fakeNotices);
};
]);
export default {
'GET /api/notices': getNotices,
......
......@@ -3,7 +3,7 @@ export default {
// 支持值为 Object 和 Array
'GET /api/currentUser': {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'antdesign@alipay.com',
signature: '海纳百川,有容乃大',
......
{
"name": "ant-design-pro",
"version": "2.2.0",
"version": "2.2.1",
"description": "An out-of-box UI solution for enterprise applications",
"private": true,
"scripts": {
......@@ -11,10 +11,10 @@
"dev:no-mock": "cross-env MOCK=none umi dev",
"build": "umi build",
"analyze": "cross-env ANALYZE=1 umi build",
"lint:style": "stylelint \"src/**/*.less\" --syntax less",
"lint:style": "stylelint 'src/**/*.less' --syntax less",
"lint:prettier": "check-prettier lint",
"lint": "eslint --ext .js src mock tests && npm run lint:style && npm run lint:prettier",
"lint:fix": "eslint --fix --ext .js src mock tests && npm run lint:style",
"lint:fix": "eslint --fix --ext .js src mock tests && stylelint --fix 'src/**/*.less' --syntax less",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js",
"test": "umi test",
......@@ -50,46 +50,51 @@
"umi-request": "^1.0.0"
},
"devDependencies": {
"@types/react": "^16.7.7",
"@types/react-dom": "^16.0.10",
"@types/react": "^16.8.1",
"@types/react-dom": "^16.0.11",
"antd-pro-merge-less": "^1.0.0",
"antd-theme-webpack-plugin": "^1.1.8",
"antd-theme-webpack-plugin": "^1.2.0",
"babel-eslint": "^10.0.1",
"chalk": "^2.4.2",
"check-prettier": "^1.0.1",
"cross-env": "^5.1.1",
"cross-env": "^5.2.0",
"cross-port-killer": "^1.0.1",
"enzyme": "3.7.0",
"eslint": "^5.4.0",
"eslint-config-airbnb": "^17.0.0",
"eslint-config-prettier": "^3.0.1",
"enzyme": "^3.9.0",
"eslint": "^5.13.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^4.0.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-compat": "^2.6.2",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-react": "^7.11.1",
"husky": "^1.2.0",
"jest-puppeteer": "^3.5.1",
"eslint-plugin-compat": "^2.6.3",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.0",
"eslint-plugin-markdown": "^1.0.0",
"eslint-plugin-react": "^7.12.4",
"gh-pages": "^2.0.1",
"husky": "^1.3.1",
"jest-puppeteer": "^4.0.0",
"less": "^3.9.0",
"lint-staged": "^8.1.0",
"lint-staged": "^8.1.1",
"merge-umi-mock-data": "^1.0.4",
"mockjs": "^1.0.1-beta3",
"prettier": "1.15.2",
"pro-download": "^1.0.1",
"prettier": "^1.16.4",
"slash2": "^2.0.0",
"stylelint": "^9.8.0",
"stylelint-config-prettier": "^4.0.0",
"stylelint-config-standard": "^18.0.0",
"tslint": "^5.10.0",
"tslint-config-prettier": "^1.10.0",
"stylelint": "^9.10.1",
"stylelint-config-css-modules": "^1.3.0",
"stylelint-config-prettier": "^5.0.0",
"stylelint-config-rational-order": "^0.0.4",
"stylelint-config-standard": "^18.2.0",
"stylelint-declaration-block-no-ignored-properties": "^1.1.0",
"stylelint-order": "^2.0.0",
"tslint": "^5.12.1",
"tslint-config-prettier": "^1.17.0",
"tslint-react": "^3.6.0",
"umi": "^2.4.2",
"umi": "^2.4.4",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-react": "^1.3.4",
"umi-plugin-pro-block": "^1.2.0"
},
"optionalDependencies": {
"puppeteer": "^1.10.0"
"puppeteer": "^1.12.1"
},
"lint-staged": {
"**/*.{js,ts,tsx,json,jsx,less}": [
......
......@@ -8,11 +8,12 @@ export const dva = {
},
};
let authRoutes = null;
let authRoutes = {};
function ergodicRoutes(routes, authKey, authority) {
routes.forEach(element => {
if (element.path === authKey) {
if (!element.authority) element.authority = []; // eslint-disable-line
Object.assign(element.authority, authority || []);
} else if (element.routes) {
ergodicRoutes(element.routes, authKey, authority);
......@@ -31,8 +32,13 @@ export function patchRoutes(routes) {
export function render(oldRender) {
fetch('/api/auth_routes')
.then(res => res.json())
.then(ret => {
.then(
ret => {
authRoutes = ret;
oldRender();
});
},
() => {
oldRender();
}
);
}
import React, { PureComponent } from 'react';
import { FormattedMessage, formatMessage } from 'umi/locale';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip } from 'antd';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
import moment from 'moment';
import groupBy from 'lodash/groupBy';
import { NoticeIcon } from 'ant-design-pro';
......@@ -63,30 +63,13 @@ export default class GlobalHeaderRight extends PureComponent {
});
};
fetchMoreNotices = tabProps => {
const { list, name } = tabProps;
const { dispatch, notices = [] } = this.props;
const lastItemId = notices[notices.length - 1].id;
dispatch({
type: 'global/fetchMoreNotices',
payload: {
lastItemId,
type: name,
offset: list.length,
},
});
};
render() {
const {
currentUser,
fetchingMoreNotices,
fetchingNotices,
loadedAllNotices,
onNoticeVisibleChange,
onMenuClick,
onNoticeClear,
skeletonCount,
theme,
} = this.props;
const menu = (
......@@ -110,11 +93,6 @@ export default class GlobalHeaderRight extends PureComponent {
</Menu.Item>
</Menu>
);
const loadMoreProps = {
skeletonCount,
loadedAll: loadedAllNotices,
loading: fetchingMoreNotices,
};
const noticeData = this.getNoticeData();
const unreadMsg = this.getUnreadData(noticeData);
let className = styles.right;
......@@ -155,44 +133,43 @@ export default class GlobalHeaderRight extends PureComponent {
console.log(item, tabProps); // eslint-disable-line
this.changeReadState(item, tabProps);
}}
loading={fetchingNotices}
locale={{
emptyText: formatMessage({ id: 'component.noticeIcon.empty' }),
clear: formatMessage({ id: 'component.noticeIcon.clear' }),
loadedAll: formatMessage({ id: 'component.noticeIcon.loaded' }),
loadMore: formatMessage({ id: 'component.noticeIcon.loading-more' }),
viewMore: formatMessage({ id: 'component.noticeIcon.view-more' }),
notification: formatMessage({ id: 'component.globalHeader.notification' }),
message: formatMessage({ id: 'component.globalHeader.message' }),
event: formatMessage({ id: 'component.globalHeader.event' }),
}}
onClear={onNoticeClear}
onLoadMore={this.fetchMoreNotices}
onPopupVisibleChange={onNoticeVisibleChange}
loading={fetchingNotices}
onViewMore={() => message.info('Click on view more')}
clearClose
>
<NoticeIcon.Tab
count={unreadMsg.notification}
list={noticeData.notification}
title={formatMessage({ id: 'component.globalHeader.notification' })}
name="notification"
title="notification"
emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
{...loadMoreProps}
showViewMore
/>
<NoticeIcon.Tab
count={unreadMsg.message}
list={noticeData.message}
title={formatMessage({ id: 'component.globalHeader.message' })}
name="message"
title="message"
emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
{...loadMoreProps}
showViewMore
/>
<NoticeIcon.Tab
count={unreadMsg.event}
list={noticeData.event}
title={formatMessage({ id: 'component.globalHeader.event' })}
name="event"
title="event"
emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
{...loadMoreProps}
showViewMore
/>
</NoticeIcon>
{currentUser.name ? (
......
......@@ -3,21 +3,21 @@
@pro-header-hover-bg: rgba(0, 0, 0, 0.025);
.header {
position: relative;
height: @layout-header-height;
padding: 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
position: relative;
}
.logo {
display: inline-block;
height: @layout-header-height;
padding: 0 0 0 24px;
font-size: 20px;
line-height: @layout-header-height;
vertical-align: top;
display: inline-block;
padding: 0 0 0 24px;
cursor: pointer;
font-size: 20px;
img {
display: inline-block;
vertical-align: middle;
......@@ -34,11 +34,11 @@
}
.trigger {
font-size: 20px;
height: @layout-header-height;
padding: ~'calc((@{layout-header-height} - 20px) / 2)' 24px;
font-size: 20px;
cursor: pointer;
transition: all 0.3s, padding 0s;
padding: ~'calc((@{layout-header-height} - 20px) / 2)' 24px;
&:hover {
background: @pro-header-hover-bg;
}
......@@ -49,14 +49,14 @@
height: 100%;
overflow: hidden;
.action {
cursor: pointer;
padding: 0 12px;
display: inline-block;
transition: all 0.3s;
height: 100%;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
> i {
vertical-align: middle;
color: @text-color;
vertical-align: middle;
}
&:hover {
background: @pro-header-hover-bg;
......@@ -76,8 +76,8 @@
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
background: rgba(255, 255, 255, 0.85);
vertical-align: top;
background: rgba(255, 255, 255, 0.85);
}
}
}
......@@ -111,14 +111,14 @@
padding: 22px 12px;
}
.logo {
padding-left: 12px;
padding-right: 12px;
position: relative;
padding-right: 12px;
padding-left: 12px;
}
.right {
position: absolute;
right: 12px;
top: 0;
right: 12px;
background: #fff;
.account {
.avatar {
......
import * as React from 'react';
export default class HeaderDropdown extends React.Component<any, any> {}
......@@ -2,8 +2,8 @@
.container > * {
background-color: #fff;
box-shadow: @shadow-1-down;
border-radius: 4px;
box-shadow: @shadow-1-down;
}
@media screen and (max-width: @screen-xs) {
......
---
title:
en-US: HeaderSearch
zh-CN: HeaderSearch
subtitle: Top search box
title: HeaderSearch
subtitle:
cols: 1
order: 8
---
......@@ -21,4 +19,4 @@ onSelect | Called when a option is selected. param is option's value and option
onPressEnter | Callback when pressing Enter | function(value) | -
onVisibleChange | Show or hide the callback of the text box | function(value) |-
defaultOpen | The input box is displayed for the first time. | boolean | false
open | The input box is displayed | booelan |false
\ No newline at end of file
open | The input box is displayed | boolean |false
\ No newline at end of file
......@@ -2,21 +2,21 @@
.headerSearch {
:global(.anticon-search) {
cursor: pointer;
font-size: 16px;
cursor: pointer;
}
.input {
transition: width 0.3s, margin-left 0.3s;
width: 0;
background: transparent;
border-radius: 0;
transition: width 0.3s, margin-left 0.3s;
:global(.ant-select-selection) {
background: transparent;
}
input {
border: 0;
padding-left: 0;
padding-right: 0;
padding-left: 0;
border: 0;
box-shadow: none !important;
}
&,
......
---
title:
en-US: HeaderSearch
zh-CN: HeaderSearch
title: HeaderSearch
subtitle: 顶部搜索框
cols: 1
order: 8
......@@ -21,4 +19,4 @@ onSelect | 被选中时调用,参数为选中项的 value 值 | function(value
onPressEnter | 按下回车时的回调 | function(value) | -
onVisibleChange | 显示或隐藏文本框的回调 | function(value) |-
defaultOpen | 输入框首次显示是否显示 | boolean | false
open | 控制输入框是否显示 | booelan |false
\ No newline at end of file
open | 控制输入框是否显示 | boolean |false
\ No newline at end of file
import { Icon } from 'antd';
import { iconfontUrl as scriptUrl } from '../../defaultSettings';
// 使用:
// import IconFont from '@/components/IconFont';
// <IconFont type='icon-demo' className='xxx-xxx' />
export default Icon.createFromIconfontCN({ scriptUrl });
......@@ -10,11 +10,11 @@
}
.dropDown {
cursor: pointer;
vertical-align: top;
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
> i {
font-size: 14px !important;
font-size: 16px !important;
transform: none !important;
svg {
position: relative;
......
.themeColor {
overflow: hidden;
margin-top: 24px;
overflow: hidden;
.title {
font-size: 14px;
margin-bottom: 12px;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
line-height: 22px;
margin-bottom: 12px;
}
.colorBlock {
float: left;
width: 20px;
height: 20px;
border-radius: 2px;
float: left;
cursor: pointer;
margin-right: 8px;
text-align: center;
color: #fff;
font-weight: bold;
text-align: center;
border-radius: 2px;
cursor: pointer;
}
}
......@@ -137,7 +137,7 @@ class SettingDrawer extends PureComponent {
onClose={this.togglerContent}
placement="right"
handler={
<div className={styles.handle}>
<div className={styles.handle} onClick={this.togglerContent}>
<Icon
type={collapse ? 'close' : 'setting'}
style={{
......@@ -147,7 +147,6 @@ class SettingDrawer extends PureComponent {
/>
</div>
}
onHandleClick={this.togglerContent}
style={{
zIndex: 999,
}}
......
@import '~antd/lib/style/themes/default.less';
.content {
position: relative;
min-height: 100%;
background: #fff;
position: relative;
}
.blockChecbox {
display: flex;
.item {
margin-right: 16px;
position: relative;
margin-right: 16px;
// box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
border-radius: @border-radius-base;
cursor: pointer;
......@@ -23,52 +23,52 @@
top: 0;
right: 0;
width: 100%;
height: 100%;
padding-top: 15px;
padding-left: 24px;
height: 100%;
color: @primary-color;
font-size: 14px;
font-weight: bold;
font-size: 14px;
}
}
.color_block {
display: inline-block;
width: 38px;
height: 22px;
margin: 4px;
border-radius: 4px;
cursor: pointer;
margin-right: 12px;
display: inline-block;
vertical-align: middle;
border-radius: 4px;
cursor: pointer;
}
.title {
font-size: 14px;
margin-bottom: 12px;
color: @heading-color;
font-size: 14px;
line-height: 22px;
margin-bottom: 12px;
}
.handle {
position: absolute;
top: 240px;
background: @primary-color;
width: 48px;
height: 48px;
right: 300px;
z-index: 0;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
pointer-events: auto;
z-index: 0;
text-align: center;
width: 48px;
height: 48px;
font-size: 16px;
text-align: center;
background: @primary-color;
border-radius: 4px 0 0 4px;
cursor: pointer;
pointer-events: auto;
}
.productionHint {
font-size: 12px;
margin-top: 16px;
font-size: 12px;
}
......@@ -6,18 +6,23 @@ import { urlToList } from '../_utils/pathTools';
import { getMenuMatches } from './SiderMenuUtils';
import { isUrl } from '@/utils/utils';
import styles from './index.less';
import IconFont from '@/components/IconFont';
const { SubMenu } = Menu;
// Allow menu.js config icon as string or ReactNode
// icon: 'setting',
// icon: 'icon-geren' #For Iconfont ,
// icon: 'http://demo.com/icon.png',
// icon: <Icon type="setting" />,
const getIcon = icon => {
if (typeof icon === 'string' && isUrl(icon)) {
return <img src={icon} alt="icon" className={styles.icon} />;
}
if (typeof icon === 'string') {
if (isUrl(icon)) {
return <Icon component={() => <img src={icon} alt="icon" className={styles.icon} />} />;
}
if (icon.startsWith('icon-')) {
return <IconFont type={icon} />;
}
return <Icon type={icon} />;
}
return icon;
......@@ -28,13 +33,13 @@ export default class BaseMenu extends PureComponent {
* 获得菜单子节点
* @memberof SiderMenu
*/
getNavMenuItems = (menusData, parent) => {
getNavMenuItems = menusData => {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map(item => this.getSubMenuOrItem(item, parent))
.map(item => this.getSubMenuOrItem(item))
.filter(item => item);
};
......
......@@ -5,10 +5,13 @@ import Link from 'umi/link';
import styles from './index.less';
import PageLoading from '../PageLoading';
import { getDefaultCollapsedSubMenus } from './SiderMenuUtils';
import { title } from '../../defaultSettings';
const BaseMenu = React.lazy(() => import('./BaseMenu'));
const { Sider } = Layout;
let firstMount = true;
export default class SiderMenu extends PureComponent {
constructor(props) {
super(props);
......@@ -17,11 +20,16 @@ export default class SiderMenu extends PureComponent {
};
}
componentDidMount() {
firstMount = false;
}
static getDerivedStateFromProps(props, state) {
const { pathname } = state;
if (props.location.pathname !== pathname) {
const { pathname, flatMenuKeysLen } = state;
if (props.location.pathname !== pathname || props.flatMenuKeys.length !== flatMenuKeysLen) {
return {
pathname: props.location.pathname,
flatMenuKeysLen: props.flatMenuKeys.length,
openKeys: getDefaultCollapsedSubMenus(props),
};
}
......@@ -46,12 +54,12 @@ export default class SiderMenu extends PureComponent {
};
render() {
const { logo, collapsed, onCollapse, fixSiderbar, theme } = this.props;
const { logo, collapsed, onCollapse, fixSiderbar, theme, isMobile } = this.props;
const { openKeys } = this.state;
const defaultProps = collapsed ? {} : { openKeys };
const siderClassName = classNames(styles.sider, {
[styles.fixSiderbar]: fixSiderbar,
[styles.fixSiderBar]: fixSiderbar,
[styles.light]: theme === 'light',
});
return (
......@@ -60,7 +68,11 @@ export default class SiderMenu extends PureComponent {
collapsible
collapsed={collapsed}
breakpoint="lg"
onCollapse={onCollapse}
onCollapse={collapse => {
if (firstMount || !isMobile) {
onCollapse(collapse);
}
}}
width={256}
theme={theme}
className={siderClassName}
......@@ -68,7 +80,7 @@ export default class SiderMenu extends PureComponent {
<div className={styles.logo} id="logo">
<Link to="/">
<img src={logo} alt="logo" />
<h1>Ant Design Pro</h1>
<h1>{title}</h1>
</Link>
</div>
<Suspense fallback={<PageLoading />}>
......
......@@ -35,5 +35,6 @@ export const getDefaultCollapsedSubMenus = props => {
} = props;
return urlToList(pathname)
.map(item => getMenuMatches(flatMenuKeys, item)[0])
.filter(item => item);
.filter(item => item)
.reduce((acc, curr) => [...acc, curr], ['/']);
};
......@@ -3,46 +3,55 @@
@nav-header-height: @layout-header-height;
.logo {
height: @nav-header-height;
position: relative;
line-height: @nav-header-height;
height: @nav-header-height;
padding-left: (@menu-collapsed-width - 32px) / 2;
transition: all 0.3s;
background: #002140;
overflow: hidden;
line-height: @nav-header-height;
background: #002140;
transition: all 0.3s;
img {
display: inline-block;
vertical-align: middle;
height: 32px;
vertical-align: middle;
}
h1 {
color: white;
display: inline-block;
vertical-align: middle;
font-size: 20px;
margin: 0 0 0 12px;
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
color: white;
font-weight: 600;
font-size: 20px;
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
.sider {
min-height: 100vh;
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
position: relative;
z-index: 10;
&.fixSiderbar {
min-height: 100vh;
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
&.fixSiderBar {
position: fixed;
top: 0;
left: 0;
:global(.ant-menu-root) {
overflow-y: auto;
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
:global {
.ant-menu-root {
height: ~'calc(100vh - @{nav-header-height})';
overflow-y: auto;
}
.ant-menu-inline {
border-right: 0;
.ant-menu-item,
.ant-menu-submenu-title {
width: 100%;
}
}
}
}
&.light {
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
background-color: white;
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.logo {
background: white;
box-shadow: 1px 1px 0 0 @border-color-split;
......@@ -58,7 +67,7 @@
.icon {
width: 14px;
margin-right: 10px;
vertical-align: baseline;
}
:global {
......@@ -78,15 +87,15 @@
.sider-menu-item-img
+ span,
& > .ant-menu-submenu > .ant-menu-submenu-title .sider-menu-item-img + span {
max-width: 0;
display: inline-block;
max-width: 0;
opacity: 0;
}
}
.ant-menu-item .sider-menu-item-img + span,
.ant-menu-submenu-title .sider-menu-item-img + span {
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
opacity: 1;
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
}
.ant-drawer-left {
.ant-drawer-body {
......
......@@ -4,6 +4,7 @@ import RightContent from '../GlobalHeader/RightContent';
import BaseMenu from '../SiderMenu/BaseMenu';
import { getFlatMenuKeys } from '../SiderMenu/SiderMenuUtils';
import styles from './index.less';
import { title } from '../../defaultSettings';
export default class TopNavHeader extends PureComponent {
state = {
......@@ -32,7 +33,7 @@ export default class TopNavHeader extends PureComponent {
<div className={styles.logo} key="logo" id="logo">
<Link to="/">
<img src={logo} alt="logo" />
<h1>Ant Design Pro</h1>
<h1>{title}</h1>
</Link>
</div>
<div
......
@import '~antd/lib/style/themes/default.less';
.head {
position: relative;
width: 100%;
transition: background 0.3s, width 0.2s;
height: @layout-header-height;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
position: relative;
transition: background 0.3s, width 0.2s;
:global {
.ant-menu-submenu.ant-menu-submenu-horizontal {
line-height: @layout-header-height;
height: 100%;
line-height: @layout-header-height;
.ant-menu-submenu-title {
height: 100%;
}
......@@ -28,8 +28,8 @@
padding-left: 0;
}
.left {
flex: 1;
display: flex;
flex: 1;
}
.right {
width: 324px;
......@@ -38,24 +38,24 @@
}
.logo {
position: relative;
width: 165px;
height: @layout-header-height;
position: relative;
overflow: hidden;
line-height: @layout-header-height;
transition: all 0.3s;
overflow: hidden;
img {
display: inline-block;
vertical-align: middle;
height: 32px;
vertical-align: middle;
}
h1 {
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 16px;
margin: 0 0 0 12px;
color: #fff;
font-weight: 400;
font-size: 16px;
vertical-align: top;
}
}
......@@ -66,7 +66,7 @@
}
.menu {
border: none;
height: @layout-header-height;
line-height: @layout-header-height;
border: none;
}
......@@ -6,5 +6,13 @@ module.exports = {
fixedHeader: false, // sticky header
autoHideHeader: false, // auto hide header
fixSiderbar: false, // sticky siderbar
menu: {
disableLocal: false,
},
title: 'Ant Design Pro',
pwa: true,
// your iconfont Symbol Scrip Url
// eg://at.alicdn.com/t/font_1039637_btcrd5co4w.js
// 注意:如果需要图标多色,Iconfont图标项目里要进行批量去色处理
iconfontUrl: '',
};
import React from 'react';
import { notification, Button, message } from 'antd';
import { formatMessage } from 'umi/locale';
import defaultSettings from './defaultSettings';
// Notify user if offline now
window.addEventListener('sw.offline', () => {
window.React = React;
const { pwa } = defaultSettings;
// if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning(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', e => {
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', e => {
const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
......@@ -51,4 +57,5 @@ window.addEventListener('sw.updated', e => {
key,
onClose: async () => {},
});
});
});
}
@import '~antd/lib/style/themes/default.less';
html,
body,
#root {
......@@ -31,3 +33,20 @@ 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;
}
}
}
}
}
import React, { Suspense } from 'react';
import { Layout } from 'antd';
import DocumentTitle from 'react-document-title';
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
import { connect } from 'dva';
import { ContainerQuery } from 'react-container-query';
import classNames from 'classnames';
import pathToRegexp from 'path-to-regexp';
import Media from 'react-media';
import { formatMessage } from 'umi/locale';
import Authorized from '@/utils/Authorized';
import logo from '../assets/logo.svg';
import Footer from './Footer';
import Header from './Header';
import Context from './MenuContext';
import PageLoading from '@/components/PageLoading';
import SiderMenu from '@/components/SiderMenu';
import { title } from '../defaultSettings';
import getPageTitle from '@/utils/getPageTitle';
import styles from './BasicLayout.less';
// lazy load SettingDrawer
......@@ -49,13 +44,7 @@ const query = {
},
};
class BasicLayout extends React.PureComponent {
constructor(props) {
super(props);
this.getPageTitle = memoizeOne(this.getPageTitle);
this.matchParamsPath = memoizeOne(this.matchParamsPath, isEqual);
}
class BasicLayout extends React.Component {
componentDidMount() {
const {
dispatch,
......@@ -73,15 +62,6 @@ class BasicLayout extends React.PureComponent {
});
}
componentDidUpdate(preProps) {
// After changing to phone mode,
// if collapsed is true, you need to click twice to display
const { collapsed, isMobile } = this.props;
if (isMobile && !preProps.isMobile && !collapsed) {
this.handleMenuCollapse(false);
}
}
getContext() {
const { location, breadcrumbNameMap } = this.props;
return {
......@@ -90,41 +70,6 @@ class BasicLayout extends React.PureComponent {
};
}
matchParamsPath = (pathname, breadcrumbNameMap) => {
const pathKey = Object.keys(breadcrumbNameMap).find(key => pathToRegexp(key).test(pathname));
return breadcrumbNameMap[pathKey];
};
getRouterAuthority = (pathname, routeData) => {
let routeAuthority = ['noAuthority'];
const getAuthority = (key, routes) => {
routes.forEach(route => {
if (route.path && pathToRegexp(route.path).test(key)) {
routeAuthority = route.authority;
} else if (route.routes) {
routeAuthority = getAuthority(key, route.routes);
}
return route;
});
return routeAuthority;
};
return getAuthority(pathname, routeData);
};
getPageTitle = (pathname, breadcrumbNameMap) => {
const currRouterData = this.matchParamsPath(pathname, breadcrumbNameMap);
if (!currRouterData) {
return title;
}
const pageName = formatMessage({
id: currRouterData.locale || currRouterData.name,
defaultMessage: currRouterData.name,
});
return `${pageName} - ${title}`;
};
getLayoutStyle = () => {
const { fixSiderbar, isMobile, collapsed, layout } = this.props;
if (fixSiderbar && layout !== 'topmenu' && !isMobile) {
......@@ -161,12 +106,10 @@ class BasicLayout extends React.PureComponent {
isMobile,
menuData,
breadcrumbNameMap,
route: { routes },
fixedHeader,
} = this.props;
const isTop = PropsLayout === 'topmenu';
const routerConfig = this.getRouterAuthority(pathname, routes);
const contentStyle = !fixedHeader ? { paddingTop: 0 } : {};
const layout = (
<Layout>
......@@ -194,9 +137,7 @@ class BasicLayout extends React.PureComponent {
{...this.props}
/>
<Content className={styles.content} style={contentStyle}>
<Authorized authority={routerConfig} noMatch={<p>Exception403</p>}>
{children}
</Authorized>
</Content>
<Footer />
</Layout>
......@@ -204,7 +145,7 @@ class BasicLayout extends React.PureComponent {
);
return (
<React.Fragment>
<DocumentTitle title={this.getPageTitle(pathname, breadcrumbNameMap)}>
<DocumentTitle title={getPageTitle(pathname, breadcrumbNameMap)}>
<ContainerQuery query={query}>
{params => (
<Context.Provider value={this.getContext()}>
......@@ -219,11 +160,11 @@ class BasicLayout extends React.PureComponent {
}
}
export default connect(({ global, setting, menu }) => ({
export default connect(({ global, setting, menu: menuModel }) => ({
collapsed: global.collapsed,
layout: setting.layout,
menuData: menu.menuData,
breadcrumbNameMap: menu.breadcrumbNameMap,
menuData: menuModel.menuData,
breadcrumbNameMap: menuModel.breadcrumbNameMap,
...setting,
}))(props => (
<Media query="(max-width: 599px)">
......
import React from 'react';
export default props => <div {...props} />;
export default ({ children }) => <div>{children}</div>;
import React, { PureComponent } from 'react';
import React, { Component } from 'react';
import { formatMessage } from 'umi/locale';
import { Layout, message } from 'antd';
import Animate from 'rc-animate';
......@@ -10,7 +10,7 @@ import styles from './Header.less';
const { Header } = Layout;
class HeaderView extends PureComponent {
class HeaderView extends Component {
state = {
visible: true,
};
......@@ -155,7 +155,6 @@ export default connect(({ user, global, setting, loading }) => ({
collapsed: global.collapsed,
fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
fetchingNotices: loading.effects['global/fetchNotices'],
loadedAllNotices: global.loadedAllNotices,
notices: global.notices,
setting,
}))(HeaderView);
......@@ -2,7 +2,7 @@
position: fixed;
top: 0;
right: 0;
width: 100%;
z-index: 9;
width: 100%;
transition: width 0.2s;
}
import React, { Fragment } from 'react';
import React, { Component, Fragment } from 'react';
import { formatMessage } from 'umi/locale';
import { connect } from 'dva';
import Link from 'umi/link';
import { Icon } from 'antd';
import { GlobalFooter } from 'ant-design-pro';
import DocumentTitle from 'react-document-title';
import SelectLang from '@/components/SelectLang';
import styles from './UserLayout.less';
import logo from '../assets/logo.svg';
import getPageTitle from '@/utils/getPageTitle';
const links = [
{
......@@ -31,22 +34,26 @@ const copyright = (
</Fragment>
);
class UserLayout extends React.PureComponent {
// @TODO title
// getPageTitle() {
// const { routerData, location } = this.props;
// const { pathname } = location;
// let title = 'Ant Design Pro';
// if (routerData[pathname] && routerData[pathname].name) {
// title = `${routerData[pathname].name} - Ant Design Pro`;
// }
// return title;
// }
class UserLayout extends Component {
componentDidMount() {
const {
dispatch,
route: { routes, authority },
} = this.props;
dispatch({
type: 'menu/getMenuData',
payload: { routes, authority },
});
}
render() {
const { children } = this.props;
const {
children,
location: { pathname },
breadcrumbNameMap,
} = this.props;
return (
// @TODO <DocumentTitle title={this.getPageTitle()}>
<DocumentTitle title={getPageTitle(pathname, breadcrumbNameMap)}>
<div className={styles.container}>
<div className={styles.lang}>
<SelectLang />
......@@ -65,8 +72,12 @@ class UserLayout extends React.PureComponent {
</div>
<GlobalFooter links={links} copyright={copyright} />
</div>
</DocumentTitle>
);
}
}
export default UserLayout;
export default connect(({ menu: menuModel }) => ({
menuData: menuModel.menuData,
breadcrumbNameMap: menuModel.breadcrumbNameMap,
}))(UserLayout);
......@@ -9,18 +9,18 @@
}
.lang {
text-align: right;
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
:global(.ant-dropdown-trigger) {
margin-right: 24px;
}
}
.content {
padding: 32px 0;
flex: 1;
padding: 32px 0;
}
@media (min-width: @screen-md-min) {
......@@ -50,22 +50,22 @@
.logo {
height: 44px;
vertical-align: top;
margin-right: 16px;
vertical-align: top;
}
.title {
font-size: 33px;
color: @heading-color;
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: 600;
position: relative;
top: 2px;
color: @heading-color;
font-weight: 600;
font-size: 33px;
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
}
.desc {
font-size: @font-size-base;
color: @text-color-secondary;
margin-top: 12px;
margin-bottom: 40px;
color: @text-color-secondary;
font-size: @font-size-base;
}
......@@ -3,6 +3,7 @@ import menu from './en-US/menu';
import settingDrawer from './en-US/settingDrawer';
import settings from './en-US/settings';
import pwa from './en-US/pwa';
import component from './en-US/component';
export default {
'navBar.lang': 'Languages',
......@@ -15,4 +16,5 @@ export default {
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': 'Expand',
'component.tagSelect.collapse': 'Collapse',
'component.tagSelect.all': 'All',
};
......@@ -13,6 +13,5 @@ export default {
'component.noticeIcon.clear': 'Clear',
'component.noticeIcon.cleared': 'Cleared',
'component.noticeIcon.empty': 'No notifications',
'component.noticeIcon.loaded': 'Loaded',
'component.noticeIcon.loading-more': 'Loading more',
'component.noticeIcon.view-more': 'View more',
};
......@@ -25,14 +25,14 @@ export default {
'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.password-description': 'Current password strength',
'app.settings.security.phone': 'Security Phone',
'app.settings.security.phone-description': 'Bound 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.email-description': 'Bound Email',
'app.settings.security.mfa': 'MFA Device',
'app.settings.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
......
......@@ -3,6 +3,7 @@ import menu from './pt-BR/menu';
import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
import pwa from './pt-BR/pwa';
import component from './pt-BR/component';
export default {
'navBar.lang': 'Idiomas',
......@@ -15,4 +16,5 @@ export default {
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': 'Expandir',
'component.tagSelect.collapse': 'Diminuir',
'component.tagSelect.all': 'Todas',
};
......@@ -14,5 +14,5 @@ export default {
'component.noticeIcon.cleared': 'Limpo',
'component.noticeIcon.empty': 'Sem notificações',
'component.noticeIcon.loaded': 'Carregado',
'component.noticeIcon.loading-more': 'Carregar mais',
'component.noticeIcon.view-more': 'Veja mais',
};
......@@ -3,6 +3,7 @@ import menu from './zh-CN/menu';
import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
import pwa from './zh-CN/pwa';
import component from './zh-CN/component';
export default {
'navBar.lang': '语言',
......@@ -15,4 +16,5 @@ export default {
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': '展开',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
......@@ -13,6 +13,5 @@ export default {
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暂无数据',
'component.noticeIcon.loaded': '加载完毕',
'component.noticeIcon.loading-more': '加载更多',
'component.noticeIcon.view-more': '查看更多',
};
......@@ -25,13 +25,13 @@ export default {
'app.settings.security.medium': '',
'app.settings.security.weak': '',
'app.settings.security.password': '账户密码',
'app.settings.security.password-description': '当前密码强度',
'app.settings.security.password-description': '当前密码强度',
'app.settings.security.phone': '密保手机',
'app.settings.security.phone-description': '已绑定手机',
'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.email-description': '已绑定邮箱',
'app.settings.security.mfa': 'MFA 设备',
'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
'app.settings.security.modify': '修改',
......
......@@ -3,6 +3,7 @@ import menu from './zh-TW/menu';
import settingDrawer from './zh-TW/settingDrawer';
import settings from './zh-TW/settings';
import pwa from './zh-TW/pwa';
import component from './zh-TW/component';
export default {
'navBar.lang': '語言',
......@@ -18,4 +19,5 @@ export default {
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': '展開',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
......@@ -3,7 +3,7 @@ export default {
'component.globalHeader.search.example1': '搜索提示壹',
'component.globalHeader.search.example2': '搜索提示二',
'component.globalHeader.search.example3': '搜索提示三',
'component.globalHeader.help': '使用文檔',
'component.globalHeader.help': '使用手冊',
'component.globalHeader.notification': '通知',
'component.globalHeader.notification.empty': '妳已查看所有通知',
'component.globalHeader.message': '消息',
......@@ -12,7 +12,6 @@ export default {
'component.globalHeader.event.empty': '妳已完成所有待辦',
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暫無數據',
'component.noticeIcon.loaded': '加載完畢',
'component.noticeIcon.loading-more': '加載更多',
'component.noticeIcon.empty': '暫無資料',
'component.noticeIcon.view-more': '查看更多',
};
......@@ -25,13 +25,13 @@ export default {
'app.settings.security.medium': '',
'app.settings.security.weak': '',
'app.settings.security.password': '賬戶密碼',
'app.settings.security.password-description': '當前密碼強度',
'app.settings.security.password-description': '當前密碼強度',
'app.settings.security.phone': '密保手機',
'app.settings.security.phone-description': '已綁定手機',
'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.email-description': '已綁定郵箱',
'app.settings.security.mfa': 'MFA 設備',
'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',
'app.settings.security.modify': '修改',
......
......@@ -6,42 +6,14 @@ export default {
state: {
collapsed: false,
notices: [],
loadedAllNotices: false,
},
effects: {
*fetchNotices(_, { call, put, select }) {
const data = yield call(queryNotices);
const loadedAllNotices = data && data.length && data[data.length - 1] === null;
yield put({
type: 'setLoadedStatus',
payload: loadedAllNotices,
});
yield put({
type: 'saveNotices',
payload: data.filter(item => item),
});
const unreadCount = yield select(
state => state.global.notices.filter(item => !item.read).length
);
yield put({
type: 'user/changeNotifyCount',
payload: {
totalCount: data.length,
unreadCount,
},
});
},
*fetchMoreNotices({ payload }, { call, put, select }) {
const data = yield call(queryNotices, payload);
const loadedAllNotices = data && data.length && data[data.length - 1] === null;
yield put({
type: 'setLoadedStatus',
payload: loadedAllNotices,
});
yield put({
type: 'pushNotices',
payload: data.filter(item => item),
payload: data,
});
const unreadCount = yield select(
state => state.global.notices.filter(item => !item.read).length
......@@ -114,18 +86,6 @@ export default {
notices: state.notices.filter(item => item.type !== payload),
};
},
pushNotices(state, { payload }) {
return {
...state,
notices: [...state.notices, ...payload],
};
},
setLoadedStatus(state, { payload }) {
return {
...state,
loadedAllNotices: payload,
};
},
},
subscriptions: {
......
......@@ -2,6 +2,7 @@ import memoizeOne from 'memoize-one';
import isEqual from 'lodash/isEqual';
import { formatMessage } from 'umi/locale';
import Authorized from '@/utils/Authorized';
import { menu } from '../defaultSettings';
const { check } = Authorized;
......@@ -19,10 +20,14 @@ function formatter(data, parentAuthority, parentName) {
} else {
locale = `menu.${item.name}`;
}
// if enableMenuLocale use item.name,
// close menu international
const name = menu.disableLocal
? item.name
: formatMessage({ id: locale, defaultMessage: item.name });
const result = {
...item,
name: formatMessage({ id: locale, defaultMessage: item.name }),
name,
locale,
authority: item.authority || parentAuthority,
};
......@@ -92,17 +97,19 @@ export default {
state: {
menuData: [],
routerData: [],
breadcrumbNameMap: {},
},
effects: {
*getMenuData({ payload }, { put }) {
const { routes, authority } = payload;
const menuData = filterMenuData(memoizeOneFormatter(routes, authority));
const breadcrumbNameMap = memoizeOneGetBreadcrumbNameMap(menuData);
const originalMenuData = memoizeOneFormatter(routes, authority);
const menuData = filterMenuData(originalMenuData);
const breadcrumbNameMap = memoizeOneGetBreadcrumbNameMap(originalMenuData);
yield put({
type: 'save',
payload: { menuData, breadcrumbNameMap },
payload: { menuData, breadcrumbNameMap, routerData: routes },
});
},
},
......
import React from 'react';
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
import { getAuthority } from '@/utils/authority';
import Redirect from 'umi/redirect';
import pathToRegexp from 'path-to-regexp';
import { connect } from 'dva';
import Authorized from '@/utils/Authorized';
const Authority = getAuthority();
const Authorized = RenderAuthorized(Authority);
function AuthComponent({ children, location, routerData, currentCuser }) {
const isLogin = currentCuser && currentCuser.name;
export default ({ children }) => (
<Authorized authority={children.props.route.authority} noMatch={<Redirect to="/user/login" />}>
const getRouteAuthority = (pathname, routeData) => {
const routes = routeData.slice(); // clone
const getAuthority = (routeDatas, path) => {
let authorities;
routeDatas.forEach(route => {
// check partial route
if (pathToRegexp(`${route.path}(.*)`).test(path)) {
if (route.authority) {
authorities = route.authority;
}
// is exact route?
if (!pathToRegexp(route.path).test(path) && route.routes) {
authorities = getAuthority(route.routes, path);
}
}
});
return authorities;
};
return getAuthority(routes, pathname);
};
return (
<Authorized
authority={getRouteAuthority(location.pathname, routerData)}
noMatch={isLogin ? <Redirect to="/exception/403" /> : <Redirect to="/user/login" />}
>
{children}
</Authorized>
);
);
}
export default connect(({ menu: menuModel, user: userModel }) => ({
routerData: menuModel.routerData,
currentCuser: userModel.currentCuser,
}))(AuthComponent);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>Ant Design Pro</title>
<link rel="icon" href="/favicon.png" type="image/x-icon">
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.data-set-0.9.6/dist/data-set.min.js"></script>
</head>
<body>
<link rel="icon" href="/favicon.png" type="image/x-icon" />
</head>
<body>
<noscript>Sorry, we need js to run correctly!</noscript>
<div id="root"></div>
</body>
</body>
</html>
import { formatMessage } from 'umi/locale';
import pathToRegexp from 'path-to-regexp';
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
import { menu, title } from '../defaultSettings';
export const matchParamsPath = (pathname, breadcrumbNameMap) => {
const pathKey = Object.keys(breadcrumbNameMap).find(key => pathToRegexp(key).test(pathname));
return breadcrumbNameMap[pathKey];
};
const getPageTitle = (pathname, breadcrumbNameMap) => {
const currRouterData = matchParamsPath(pathname, breadcrumbNameMap);
if (!currRouterData) {
return title;
}
const pageName = menu.disableLocal
? currRouterData.name
: formatMessage({
id: currRouterData.locale || currRouterData.name,
defaultMessage: currRouterData.name,
});
return `${pageName} - ${title}`;
};
export default memoizeOne(getPageTitle, isEqual);
.textOverflow() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
.textOverflowMulti(@line: 3, @bg: #fff) {
overflow: hidden;
position: relative;
line-height: 1.5em;
max-height: @line * 1.5em;
text-align: justify;
margin-right: -1em;
padding-right: 1em;
&:before {
background: @bg;
content: '...';
padding: 0 1px;
overflow: hidden;
line-height: 1.5em;
text-align: justify;
&::before {
position: absolute;
right: 14px;
bottom: 0;
padding: 0 1px;
background: @bg;
content: '...';
}
&:after {
background: white;
content: '';
margin-top: 0.2em;
&::after {
position: absolute;
right: 14px;
width: 1em;
height: 1em;
margin-top: 0.2em;
background: white;
content: '';
}
}
......@@ -36,15 +36,15 @@
// ------------------------
.clearfix() {
zoom: 1;
&:before,
&:after {
&::before,
&::after {
content: ' ';
display: table;
}
&:after {
&::after {
clear: both;
visibility: hidden;
font-size: 0;
height: 0;
font-size: 0;
visibility: hidden;
}
}
......@@ -24,7 +24,11 @@ startServer.on('exit', () => {
console.log('Starting development server for e2e tests...');
startServer.stdout.on('data', data => {
console.log(data.toString());
if (!once && data.toString().indexOf('Compiled successfully') >= 0) {
// 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.');
......
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