Commit 21576b25 authored by xiaohuoni's avatar xiaohuoni

Merge branch 'master' of https://github.com/ant-design/ant-design-pro into Conventional-Routing

# Conflicts:
#	package.json
#	src/models/menu.js
parents f8ca7d2f 7679507b
/functions/mock/**
/lambda/mock/**
/scripts
/config
......@@ -34,6 +34,6 @@ module.exports = {
'linebreak-style': 0,
},
settings: {
polyfills: ['fetch', 'promises', 'url'],
polyfills: ['fetch', 'promises', 'url', 'object-assign'],
},
};
{
"projects": {
"default": "antd-pro"
}
}
......@@ -25,8 +25,8 @@ package-lock.json
# visual studio code
.history
*.log
functions/mock
functions/*
lambda/mock/index.js
.temp/**
# umi
......
English | [简体中文](./README.zh-CN.md) | [Русский](./README.ru-RU.md)
English | [简体中文](./README.zh-CN.md) | [Русский](./README.ru-RU.md) | [Türkçe](./README.tr-TR.md)
<h1 align="center">Ant Design Pro</h1>
......@@ -12,7 +12,7 @@ An out-of-box UI solution for enterprise applications as a React boilerplate.
[![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?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)
[![Netlify Status](https://api.netlify.com/api/v1/badges/b68e9850-a529-4364-9d3b-d70aade560f9/deploy-status)](https://app.netlify.com/sites/ant-design-pro/deploys)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
</div>
......
[English](./README.md) | [简体中文](./README.zh-CN.md) | Русский
[English](./README.md) | [简体中文](./README.zh-CN.md) | Русский | [Türkçe](./README.tr-TR.md)
<h1 align="center">Ant Design Pro</h1>
......@@ -12,7 +12,7 @@ UI-решение "из коробки" для корпоративных при
[![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?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)
[![Netlify Status](https://api.netlify.com/api/v1/badges/b68e9850-a529-4364-9d3b-d70aade560f9/deploy-status)](https://app.netlify.com/sites/ant-design-pro/deploys)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
</div>
......
[English](./README.md) | [简体中文](./README.zh-CN.md) | [Русский](./README.ru-RU.md) | Türkçe
<h1 align="center">Ant Design Pro</h1>
<div align="center">
React ile kurumsal uygulamalar için taslak olarak geliştirilmiş kullanıma hazır bir UI çözümü.
[![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)
[![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)(🇨🇳)
[![Netlify Status](https://api.netlify.com/api/v1/badges/b68e9850-a529-4364-9d3b-d70aade560f9/deploy-status)](https://app.netlify.com/sites/ant-design-pro/deploys)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
</div>
- Önizleme: http://preview.pro.ant.design
- Anasayfa: http://pro.ant.design
- Dokümantasyon: http://pro.ant.design/docs/getting-started
- ChangeLog: http://pro.ant.design/docs/changelog
- SSS: http://pro.ant.design/docs/faq
- Çinde barındırılan site: http://ant-design-pro.gitee.io
## 2.0 Versiyonu Şimdi Yayında! 🎉🎉🎉
[Announcing Ant Design Pro 2.0.0](https://medium.com/ant-design/beautiful-and-powerful-ant-design-pro-2-0-release-51358da5af95)
## Çeviri Desteği :loudspeaker:
Çeviriler için yardımınıza ihtiyacımız var: https://github.com/ant-design/ant-design-pro/issues/120
## Özellikler
- :gem: **Zarif Tasarım**: Buradan [Ant Design özellikleri](http://ant.design/)
- :triangular_ruler: **Ortak Şablonlar**: Kurumsal uygulamalar için şablonlar
- :rocket: **Sanatsal gelişim durumu**: Newest development stack of React/umi/dva/antd
- :iphone: **Responsive**: Değişken ekran boyutları için tasarlanmıştır
- :art: **Tema Kullanımı**: Basit ayarlar ile özelleştirilebilir tema
- :globe_with_meridians: **Uluslararası**: Built-in i18n solution
- :gear: **Best Practices**: İyi kod için sağlam iş akışı
- :1234: **Mock Geliştirme**: Model(Mock) geliştirmeler için kolay çözüm
- :white_check_mark: **UI Testi**: Unit ve e2e testleri ile güvenli sürdürülebilirlik
## Şablonlar
```
- Dashboard
- Analitik
- Monitör
- Çalışma alanı
- Form
- Basit Form
- Step Form
- Gelişmiş Form
- List
- Standard Tablo
- Standard Liste
- Kart Liste
- Arama Listesi (Project/Applications/Article)
- Profil
- Basit Profil
- Gelişmiş Profil
- Hesap
- Hesap Yönetimi
- Hesap Ayarları
- Sonuç
- Başarılı
- Hatalı
- Hatalar
- 403
- 404
- 500
- Kullanıcı
- Giriş
- Kayıt
- Kayıt Sonucu
```
## Kullanım
### bash ile kullanım
```bash
$ git clone https://github.com/ant-design/ant-design-pro.git --depth=1
$ cd ant-design-pro
$ npm install
$ npm start # visit http://localhost:8000
```
### Docker ile kullanım
```bash
# preview
$ docker pull chenshuai2144/ant-design-pro
$ docker run -p 80:80 chenshuai2144/ant-design-pro
# open http://localhost
# dev
$ npm run docker:dev
# build
$ npm run docker:build
# production dev
$ npm run docker-prod:dev
# production build
$ npm run docker-prod:build
```
Daha fazla talimat için [dokümantasyon](http://pro.ant.design/docs/getting-started) sayfasına göz atın.
## Tarayıcı desteği
Modern internet tarayıcıları ve IE11.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
| --------- | --------- | --------- | --------- | --------- |
| IE11, Edge| son 2 versiyon | son 2 versiyon | son 2 versiyon | son 2 versiyon
## Destek
Her türlü desteğinize açığız, bu projeye nasıl katkıda bulunabileceğinize dair bazı örnekler:
- Günlük işinizde Ant Design Pro kullanın.
- Hataları bildirmek veya soru sormak için [issues](http://github.com/ant-design/ant-design-pro/issues) gönderin.
- kodumuzu geliştirmek için [pull requests](http://github.com/ant-design/ant-design-pro/pulls) gönderin.
[English](./README.md) | 简体中文 | [Русский](./README.ru-RU.md)
[English](./README.md) | 简体中文 | [Русский](./README.ru-RU.md) | [Türkçe](./README.tr-TR.md)
<h1 align="center">Ant Design Pro</h1>
......@@ -11,7 +11,7 @@
[![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)
[![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)
[![Netlify Status](https://api.netlify.com/api/v1/badges/b68e9850-a529-4364-9d3b-d70aade560f9/deploy-status)](https://app.netlify.com/sites/ant-design-pro/deploys)
![](https://user-images.githubusercontent.com/8186664/44953195-581e3d80-aec4-11e8-8dcb-54b9db38ec11.png)
</div>
......
......@@ -52,6 +52,10 @@ jobs:
displayName: install
- script: npm run lint
displayName: lint
- script: npm run test:all
env:
PROGRESS: none
displayName: test
- script: npm run build
env:
PROGRESS: none
......
......@@ -5,7 +5,7 @@ import defaultSettings from '../src/defaultSettings';
import slash from 'slash2';
const { pwa, primaryColor } = defaultSettings;
const { NODE_ENV, APP_TYPE, TEST } = process.env;
const { APP_TYPE, TEST } = process.env;
const plugins = [
[
......@@ -37,12 +37,12 @@ const plugins = [
importWorkboxFrom: 'local',
},
}
: {},
: false,
...(!TEST && os.platform() === 'darwin'
? {
dll: {
include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch'],
exclude: ['@babel/runtime'],
exclude: ['@babel/runtime', 'netlify-lambda'],
},
hardSource: false,
}
......@@ -91,10 +91,6 @@ export default {
theme: {
'primary-color': primaryColor,
},
externals: {
'@antv/data-set': 'DataSet',
bizcharts: 'BizCharts',
},
// proxy: {
// '/server/api/': {
// target: 'https://preview.pro.ant.design/',
......
......@@ -4,6 +4,25 @@ import MergeLessPlugin from 'antd-pro-merge-less';
import AntDesignThemePlugin from 'antd-theme-webpack-plugin';
import path from 'path';
function getModulePackageName(module) {
if (!module.context) return null;
const nodeModulesPath = path.join(__dirname, '../node_modules/');
if (module.context.substring(0, nodeModulesPath.length) !== nodeModulesPath) {
return null;
}
const moduleRelativePath = module.context.substring(nodeModulesPath.length);
const [moduleDirName] = moduleRelativePath.split(path.sep);
let packageName = moduleDirName;
// handle tree shaking
if (packageName.match('^_')) {
// eslint-disable-next-line prefer-destructuring
packageName = packageName.match(/^_(@?[^@]+)/)[1];
}
return packageName;
}
export default config => {
// pro 和 开发环境再添加这个插件
if (process.env.APP_TYPE === 'site' || process.env.NODE_ENV !== 'production') {
......@@ -30,4 +49,32 @@ export default config => {
},
]);
}
// optimize chunks
config.optimization
.runtimeChunk(false) // share the same chunks across different modules
.splitChunks({
chunks: 'async',
name: 'vendors',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendors: {
test: module => {
const packageName = getModulePackageName(module);
if (packageName) {
return ['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0;
}
return false;
},
name(module) {
const packageName = getModulePackageName(module);
if (['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0) {
return 'viz'; // visualization package
}
return 'misc';
},
},
},
});
};
......@@ -12,6 +12,9 @@ export default [
name: 'register.result',
component: './User/RegisterResult',
},
{
component: '404',
},
],
},
// app
......@@ -19,10 +22,9 @@ export default [
path: '/',
component: '../layouts/BasicLayout',
Routes: ['src/pages/Authorized'],
authority: ['admin', 'user'],
routes: [
// dashboard
{ path: '/', redirect: '/dashboard/analysis' },
{ path: '/', redirect: '/dashboard/analysis', authority: ['admin', 'user'] },
{
path: '/dashboard',
name: 'dashboard',
......@@ -265,6 +267,29 @@ export default [
},
],
},
// editor
{
name: 'editor',
icon: 'highlight',
path: '/editor',
routes: [
{
path: '/editor/flow',
name: 'flow',
component: './Editor/GGEditor/Flow',
},
{
path: '/editor/mind',
name: 'mind',
component: './Editor/GGEditor/Mind',
},
{
path: '/editor/koni',
name: 'koni',
component: './Editor/GGEditor/Koni',
},
],
},
{
component: '404',
},
......
{
"hosting": {
"public": "dist",
"rewrites": [
{ "source": "/api/**", "function": "api" },
{
"source": "**",
"destination": "/index.html"
}
],
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
}
}
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "npm run mock && firebase deploy --only functions",
"logs": "firebase functions:log",
"mock": "node ../scripts/generateMock.js"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"body-parser": "^1.18.3",
"express": "^4.16.4",
"firebase-admin": "^6.4.0",
"firebase-functions": "^2.1.0",
"mockjs": "^1.0.1-beta3",
"moment": "^2.22.2",
"path-to-regexp": "^3.0.0"
},
"private": true
}
......@@ -6,6 +6,7 @@ module.exports = {
'--disable-dev-shm-usage',
'--no-first-run',
'--no-zygote',
'--no-sandbox',
],
},
};
// [START functionsimport]
const functions = require('firebase-functions');
// [START functions import]
const express = require('express');
const serverLess = require('serverless-http');
const matchMock = require('./matchMock');
const matchMock = require('./mock/matchMock');
const app = express();
app.use(matchMock);
exports.api = functions.https.onRequest(app);
exports.handler = serverLess(app);
const pathToRegexp = require('path-to-regexp');
const bodyParser = require('body-parser');
const mockFile = require('./mock/index');
const mockFile = require('./index');
const BODY_PARSED_METHODS = ['post', 'put', 'patch'];
......@@ -10,13 +10,14 @@ function parseKey(key) {
let method = 'get';
let path = key;
if (key.indexOf(' ') > -1) {
const splited = key.split(' ');
method = splited[0].toLowerCase();
path = splited[1]; // eslint-disable-line
const spliced = key.split(' ');
method = spliced[0].toLowerCase();
path = spliced[1]; // eslint-disable-line
}
const routerBasePath = process.env.NODE_ENV === 'dev' ? `${path}` : `/.netlify/functions${path}`;
return {
method,
path,
path: routerBasePath,
};
}
......
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,
......
......@@ -192,6 +192,25 @@ export default {
},
],
},
{
name: 'editor',
icon: 'highlight',
path: '/editor',
routes: [
{
path: '/editor/flow',
name: 'flow',
},
{
path: '/editor/mind',
name: 'mind',
},
{
path: '/editor/koni',
name: 'koni',
},
],
},
],
},
};
[build]
functions = "./functions"
[[redirects]]
from = "/api/*"
to = "https://us-central1-antd-pro.cloudfunctions.net/api/api/:splat"
to = "/.netlify/functions/api/:splat"
status = 200
force = true
[redirects.headers]
......
{
"name": "ant-design-pro",
"version": "2.2.1",
"description": "An out-of-box UI solution for enterprise applications",
"version": "2.3.1",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
"presite": "cd functions && npm install",
"start": "cross-env APP_TYPE=site umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"build": "umi build",
"site": "npm run presite && cross-env APP_TYPE=site npm run build && firebase deploy && npm run docker:push",
"analyze": "cross-env ANALYZE=1 umi build",
"lint:style": "stylelint 'src/**/*.less' --syntax less",
"lint:prettier": "check-prettier lint",
"build": "umi build && npm run functions:build",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
"docker:build": "docker-compose -f ./docker/docker-compose.dev.yml build",
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro",
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"functions:build": "npm run generateMock && netlify-lambda build ./lambda",
"functions:run": "npm run generateMock && cross-env NODE_ENV=dev netlify-lambda serve ./lambda",
"generateMock": "node ./scripts/generateMock",
"lint": "eslint --ext .js src mock tests && npm run lint:style && npm run lint:prettier",
"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",
"tslint": "npm run tslint:fix",
"tslint:fix": "tslint --fix 'src/**/*.ts*'",
"lint:fix": "eslint --fix --ext .js src mock tests && stylelint --fix 'src/**/*.less' --syntax less",
"lint:prettier": "check-prettier lint",
"lint:style": "stylelint 'src/**/*.less' --syntax less",
"prettier": "node ./scripts/prettier.js",
"start": "cross-env APP_TYPE=site umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"test": "umi test",
"test:component": "umi test ./src/components",
"test:all": "node ./tests/run-tests.js",
"prettier": "node ./scripts/prettier.js",
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
"docker:build": "docker-compose -f ./docker/docker-compose.dev.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro"
"test:component": "umi test ./src/components",
"tslint": "npm run tslint:fix",
"tslint:fix": "tslint --fix 'src/**/*.ts*'"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint-staged"
}
},
"lint-staged": {
"**/*.less": "stylelint --syntax less",
"**/*.{js,jsx}": "npm run lint-staged:js",
"**/*.{js,ts,tsx,json,jsx,less}": [
"node ./scripts/lint-prettier.js",
"git add"
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
],
"dependencies": {
"@antv/data-set": "^0.10.1",
"@babel/runtime": "^7.3.1",
"antd": "^3.13.0",
"bizcharts": "^3.4.3",
"bizcharts-plugin-slider": "^2.1.1-beta.1",
"classnames": "^2.2.6",
"dva": "^2.4.1",
"enquire-js": "^0.2.1",
"hash.js": "^1.1.7",
"express": "^4.16.4",
"gg-editor": "^2.0.2",
"lodash": "^4.17.11",
"lodash-decorators": "^6.0.1",
"memoize-one": "^5.0.0",
"moment": "^2.24.0",
"netlify-lambda": "^1.4.3",
"numeral": "^2.0.6",
"nzh": "^1.0.4",
"omit.js": "^1.0.0",
......@@ -58,10 +78,13 @@
"react-dom": "^16.7.0",
"react-fittext": "^1.0.0",
"react-media": "^1.9.2",
"react-router-dom": "^4.3.1",
"umi": "^2.4.4",
"umi-plugin-react": "^1.7.2",
"umi-request": "^1.0.5",
"umi-plugin-authorize": "^1.0.0"
},
"devDependencies": {
"@types/history": "^4.7.2",
"@types/react": "^16.8.1",
"@types/react-dom": "^16.0.11",
"antd-pro-merge-less": "^1.0.0",
......@@ -71,28 +94,28 @@
"check-prettier": "^1.0.1",
"cross-env": "^5.2.0",
"cross-port-killer": "^1.0.1",
"enzyme": "3.8.0",
"enzyme": "^3.9.0",
"eslint": "^5.13.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^4.0.0",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-compat": "^2.6.3",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-markdown": "^1.0.0",
"eslint-plugin-react": "^7.12.4",
"gh-pages": "^2.0.1",
"husky": "^1.3.1",
"jest-puppeteer": "^3.9.0",
"jest-puppeteer": "^4.1.0",
"less": "^3.9.0",
"lint-staged": "^8.1.1",
"merge-umi-mock-data": "^1.0.4",
"mockjs": "^1.0.1-beta3",
"prettier": "1.16.3",
"prettier": "^1.16.4",
"slash2": "^2.0.0",
"stylelint": "^9.10.1",
"stylelint-config-css-modules": "^1.3.0",
"stylelint-config-prettier": "^4.0.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",
......@@ -100,39 +123,20 @@
"tslint": "^5.12.1",
"tslint-config-prettier": "^1.17.0",
"tslint-react": "^3.6.0",
"umi": "^2.4.4",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-react": "^1.4.2"
"serverless-http": "^1.9.1"
},
"optionalDependencies": {
"puppeteer": "^1.12.1"
},
"lint-staged": {
"**/*.{js,ts,tsx,json,jsx,less}": [
"node ./scripts/lint-prettier.js",
"git add"
],
"**/*.{js,jsx}": "npm run lint-staged:js",
"**/*.less": "stylelint --syntax less"
},
"engines": {
"node": ">=8.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
],
"checkFiles": [
"src/**/*.js*",
"src/**/*.ts*",
"src/**/*.less",
"config/**/*.js*",
"scripts/**/*.js"
],
"husky": {
"hooks": {
"pre-commit": "npm run lint-staged"
}
}
]
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="86px" height="78px" viewBox="0 0 86 78" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 3</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M42.6703722,1.66995728 L77.6353026,33.0218945 C79.2800648,34.4967003 79.4178417,37.0256099 77.9430359,38.6703722 C77.8461185,38.7784583 77.7433887,38.8811881 77.6353026,38.9781055 L42.6703722,70.3300427 C41.1508289,71.6925687 38.8491711,71.6925687 37.3296278,70.3300427 L2.36469744,38.9781055 C0.719935183,37.5032997 0.582158272,34.9743901 2.0569641,33.3296278 C2.1538815,33.2215417 2.25661129,33.1188119 2.36469744,33.0218945 L37.3296278,1.66995728 C38.8491711,0.307431303 41.1508289,0.307431303 42.6703722,1.66995728 Z" id="path-1"></path>
<filter x="-8.8%" y="-6.9%" width="117.5%" height="119.4%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-7.000000, -184.000000)">
<g id="Group-3" transform="translate(10.000000, 185.000000)">
<g id="Polygon">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#E6FFFB" fill-rule="evenodd" xlink:href="#path-1"></use>
<path stroke="#5CDBD3" stroke-width="1" d="M42.3365757,2.04222047 C41.0069753,0.850010239 38.9930247,0.850010239 37.6634243,2.04222047 L2.69849396,33.3941577 C2.60391858,33.4789604 2.51403002,33.568849 2.42922729,33.6634243 C1.13877219,35.1025913 1.25932699,37.3153872 2.69849396,38.6058423 L37.6634243,69.9577795 C38.9930247,71.1499898 41.0069753,71.1499898 42.3365757,69.9577795 L77.301506,38.6058423 C77.3960814,38.5210396 77.48597,38.431151 77.5707727,38.3365757 C78.8612278,36.8974087 78.740673,34.6846128 77.301506,33.3941577 L42.3365757,2.04222047 Z"></path>
</g>
<text id="decision" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="18" y="42">Decision</tspan>
</text>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="88px" height="56px" viewBox="0 0 88 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 4</title>
<desc>Created with Sketch.</desc>
<defs>
<rect id="path-1" x="0" y="0" width="80" height="48" rx="24"></rect>
<filter x="-8.8%" y="-10.4%" width="117.5%" height="129.2%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-102.000000, -195.000000)">
<g id="Group-4" transform="translate(106.000000, 197.000000)">
<g id="Rectangle-15-Copy-35">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#F9F0FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#B37FEB" stroke-width="1" x="0.5" y="0.5" width="79" height="47" rx="23.5"></rect>
</g>
<text id="model" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="24" y="29">Model</tspan>
</text>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="88px" height="56px" viewBox="0 0 88 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group</title>
<desc>Created with Sketch.</desc>
<defs>
<rect id="path-1" x="0" y="0" width="80" height="48" rx="4"></rect>
<filter x="-8.8%" y="-10.4%" width="117.5%" height="129.2%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-6.000000, -105.000000)">
<g id="Group" transform="translate(10.000000, 107.000000)">
<g id="Rectangle-15-Copy">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#E6F7FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#1890FF" stroke-width="1" x="0.5" y="0.5" width="79" height="47" rx="4"></rect>
</g>
<text id="normal" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="21" y="29">Normal</tspan>
</text>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 2</title>
<desc>Created with Sketch.</desc>
<defs>
<circle id="path-1" cx="36" cy="36" r="36"></circle>
<filter x="-9.7%" y="-6.9%" width="119.4%" height="119.4%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-106.000000, -93.000000)">
<g id="Group-2" transform="translate(110.000000, 95.000000)">
<g id="Oval">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#FFF2E8" fill-rule="evenodd" xlink:href="#path-1"></use>
<circle stroke="#FFC069" stroke-width="1" cx="36" cy="36" r="35.5"></circle>
</g>
<text id="start" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="23" y="41">Start</tspan>
</text>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Group 16</title>
<desc>Created with Sketch.</desc>
<defs>
<circle id="path-1" cx="20" cy="20" r="20"></circle>
<filter x="-17.5%" y="-12.5%" width="135.0%" height="135.0%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="拓扑图编译器" transform="translate(-30.000000, -139.000000)">
<g id="Group-16" transform="translate(34.000000, 141.000000)">
<g id="Oval-Copy-8">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<circle stroke="#69C0FF" stroke-width="1" stroke-linejoin="square" fill-opacity="0.92" fill="#69C0FF" fill-rule="evenodd" cx="20" cy="20" r="19.5"></circle>
</g>
<g id="earth-copy-6" transform="translate(12.000000, 12.000000)" fill="#FFFFFF" fill-rule="nonzero">
<rect id="Rectangle-path" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M13.35,12.5140625 C13.353125,12.509375 13.3578125,12.5046875 13.3609375,12.5 C14.384375,11.2828125 15,9.7140625 15,8 C15,6.2859375 14.384375,4.7171875 13.3625,3.5 C13.359375,3.4953125 13.3546875,3.4921875 13.3515625,3.4875 C13.334375,3.4671875 13.31875,3.4484375 13.3015625,3.4296875 C13.2953125,3.421875 13.2890625,3.415625 13.2828125,3.4078125 C13.2609375,3.3828125 13.240625,3.359375 13.21875,3.334375 L13.2171875,3.3328125 C13.19375,3.30625 13.16875,3.2796875 13.1453125,3.253125 L13.14375,3.2515625 C13.09375,3.1984375 13.04375,3.1453125 12.9921875,3.09375 L12.990625,3.0921875 L12.915625,3.0171875 L12.9109375,3.0125 C12.8875,2.9890625 12.8640625,2.9671875 12.840625,2.9453125 C12.8328125,2.9375 12.825,2.9296875 12.815625,2.921875 C12.8,2.90625 12.784375,2.8921875 12.76875,2.878125 C12.7640625,2.8734375 12.7578125,2.86875 12.753125,2.8625 C11.50625,1.70625 9.8359375,1 8,1 C6.1640625,1 4.49375,1.70625 3.2453125,2.8625 C3.240625,2.8671875 3.234375,2.871875 3.2296875,2.878125 C3.2140625,2.8921875 3.1984375,2.9078125 3.1828125,2.9234375 C3.175,2.93125 3.1671875,2.9390625 3.1578125,2.946875 C3.134375,2.96875 3.1109375,2.9921875 3.0875,3.0140625 L3.0828125,3.01875 L3.0078125,3.09375 L3.00625,3.0953125 C2.9546875,3.146875 2.9046875,3.2 2.8546875,3.253125 L2.853125,3.2546875 C2.828125,3.28125 2.8046875,3.3078125 2.78125,3.334375 L2.7796875,3.3359375 C2.7578125,3.359375 2.7359375,3.384375 2.715625,3.409375 C2.709375,3.4171875 2.703125,3.4234375 2.696875,3.43125 C2.6796875,3.45 2.6640625,3.4703125 2.646875,3.4890625 C2.64375,3.49375 2.6390625,3.496875 2.6359375,3.5015625 C1.615625,4.7171875 1,6.2859375 1,8 C1,9.7140625 1.615625,11.2828125 2.6375,12.5 C2.640625,12.5046875 2.6453125,12.509375 2.6484375,12.5140625 C2.6640625,12.5328125 2.68125,12.553125 2.696875,12.571875 C2.703125,12.5796875 2.709375,12.5859375 2.715625,12.59375 C2.7375,12.61875 2.7578125,12.6421875 2.7796875,12.6671875 C2.7796875,12.66875 2.78125,12.66875 2.78125,12.6703125 C2.8046875,12.696875 2.828125,12.7234375 2.853125,12.7484375 L2.8546875,12.75 C2.9046875,12.803125 2.9546875,12.85625 3.0046875,12.9078125 L3.00625,12.909375 C3.03125,12.934375 3.0546875,12.959375 3.0796875,12.9828125 L3.084375,12.9875 C3.1359375,13.0390625 3.1890625,13.0890625 3.2421875,13.1375 C4.49375,14.29375 6.1640625,15 8,15 C9.8359375,15 11.50625,14.29375 12.7546875,13.1375 C12.8078125,13.0890625 12.859375,13.0390625 12.9109375,12.9875 L12.915625,12.9828125 C12.940625,12.9578125 12.965625,12.934375 12.9890625,12.909375 L12.990625,12.9078125 C13.0421875,12.85625 13.0921875,12.803125 13.140625,12.75 L13.1421875,12.7484375 C13.165625,12.721875 13.190625,12.696875 13.2140625,12.6703125 C13.2140625,12.66875 13.215625,12.66875 13.215625,12.6671875 C13.2375,12.64375 13.259375,12.61875 13.2796875,12.59375 C13.2859375,12.5859375 13.2921875,12.5796875 13.2984375,12.571875 C13.3171875,12.5515625 13.334375,12.5328125 13.35,12.5140625 Z M13.4140625,10.2859375 C13.1984375,10.7953125 12.9140625,11.2671875 12.5671875,11.6953125 C12.178125,11.359375 11.7515625,11.065625 11.29375,10.821875 C11.475,10.0890625 11.5875,9.284375 11.6171875,8.4375 L13.859375,8.4375 C13.8125,9.0765625 13.6625,9.696875 13.4140625,10.2859375 Z M13.859375,7.5625 L11.6171875,7.5625 C11.5875,6.715625 11.475,5.9109375 11.29375,5.178125 C11.7515625,4.934375 12.178125,4.640625 12.5671875,4.3046875 C12.9140625,4.7328125 13.1984375,5.2046875 13.4140625,5.7140625 C13.6625,6.303125 13.8125,6.9234375 13.859375,7.5625 Z M10.2859375,2.5859375 C10.90625,2.8484375 11.4703125,3.2109375 11.9671875,3.6671875 C11.678125,3.9140625 11.3671875,4.13125 11.0390625,4.3203125 C10.79375,3.6171875 10.4796875,3.00625 10.1140625,2.5171875 C10.171875,2.5390625 10.2296875,2.5625 10.2859375,2.5859375 Z M8.8703125,13.5328125 C8.7265625,13.6453125 8.5828125,13.73125 8.4375,13.7890625 L8.4375,10.890625 C9.0609375,10.934375 9.665625,11.071875 10.2453125,11.3 C10.115625,11.684375 9.965625,12.0390625 9.7921875,12.359375 C9.5203125,12.865625 9.2015625,13.2703125 8.8703125,13.5328125 Z M9.7921875,3.640625 C9.9640625,3.9625 10.115625,4.3171875 10.2453125,4.7 C9.665625,4.928125 9.0609375,5.065625 8.4375,5.109375 L8.4375,2.2125 C8.58125,2.2703125 8.7265625,2.3546875 8.8703125,2.46875 C9.2015625,2.7296875 9.5203125,3.134375 9.7921875,3.640625 Z M8.4375,10.0140625 L8.4375,8.4375 L10.7421875,8.4375 C10.7171875,9.128125 10.63125,9.7984375 10.4875,10.434375 L10.4828125,10.453125 C9.840625,10.209375 9.153125,10.0578125 8.4375,10.0140625 Z M8.4375,7.5625 L8.4375,5.9859375 C9.153125,5.9421875 9.840625,5.790625 10.4828125,5.546875 L10.4875,5.565625 C10.63125,6.2015625 10.7171875,6.8703125 10.7421875,7.5625 L8.4375,7.5625 Z M7.5625,8.4375 L7.5625,10.0140625 C6.846875,10.0578125 6.159375,10.209375 5.5171875,10.453125 L5.5125,10.434375 C5.36875,9.7984375 5.2828125,9.1296875 5.2578125,8.4375 L7.5625,8.4375 Z M5.2578125,7.5625 C5.2828125,6.871875 5.36875,6.2015625 5.5125,5.565625 L5.5171875,5.546875 C6.159375,5.790625 6.8453125,5.9421875 7.5625,5.9859375 L7.5625,7.5625 L5.2578125,7.5625 Z M7.5625,10.890625 L7.5625,13.7875 C7.41875,13.7296875 7.2734375,13.6453125 7.1296875,13.53125 C6.7984375,13.2703125 6.478125,12.8640625 6.20625,12.3578125 C6.034375,12.0359375 5.8828125,11.68125 5.753125,11.2984375 C6.334375,11.0703125 6.9390625,10.934375 7.5625,10.890625 Z M7.5625,5.109375 C6.9390625,5.065625 6.334375,4.928125 5.7546875,4.7 C5.884375,4.315625 6.034375,3.9609375 6.2078125,3.640625 C6.4796875,3.134375 6.7984375,2.728125 7.13125,2.4671875 C7.275,2.3546875 7.41875,2.26875 7.5640625,2.2109375 L7.5640625,5.109375 L7.5625,5.109375 Z M5.7140625,2.5859375 C5.771875,2.5625 5.828125,2.5390625 5.8859375,2.5171875 C5.5203125,3.00625 5.20625,3.6171875 4.9609375,4.3203125 C4.6328125,4.1328125 4.321875,3.9140625 4.0328125,3.6671875 C4.5296875,3.2109375 5.09375,2.8484375 5.7140625,2.5859375 Z M2.5859375,5.7140625 C2.8015625,5.2046875 3.0859375,4.7328125 3.4328125,4.3046875 C3.821875,4.640625 4.2484375,4.934375 4.70625,5.178125 C4.525,5.9109375 4.4125,6.715625 4.3828125,7.5625 L2.140625,7.5625 C2.1875,6.9234375 2.3375,6.303125 2.5859375,5.7140625 Z M2.140625,8.4375 L4.3828125,8.4375 C4.4125,9.284375 4.525,10.0890625 4.70625,10.821875 C4.2484375,11.065625 3.821875,11.359375 3.4328125,11.6953125 C3.0859375,11.2671875 2.8015625,10.7953125 2.5859375,10.2859375 C2.3375,9.696875 2.1875,9.0765625 2.140625,8.4375 Z M5.7140625,13.4140625 C5.09375,13.1515625 4.5296875,12.7890625 4.0328125,12.3328125 C4.321875,12.0859375 4.6328125,11.86875 4.9609375,11.6796875 C5.20625,12.3828125 5.5203125,12.99375 5.8859375,13.4828125 C5.828125,13.4609375 5.7703125,13.4375 5.7140625,13.4140625 Z M10.2859375,13.4140625 C10.228125,13.4375 10.171875,13.4609375 10.1140625,13.4828125 C10.4796875,12.99375 10.79375,12.3828125 11.0390625,11.6796875 C11.3671875,11.8671875 11.678125,12.0859375 11.9671875,12.3328125 C11.4703125,12.7890625 10.90625,13.1515625 10.2859375,13.4140625 Z" id="Shape"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Group 18</title>
<desc>Created with Sketch.</desc>
<defs>
<circle id="path-1" cx="20" cy="20" r="20"></circle>
<filter x="-17.5%" y="-12.5%" width="135.0%" height="135.0%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="拓扑图编译器" transform="translate(-218.000000, -139.000000)">
<g id="Group-18" transform="translate(222.000000, 141.000000)">
<g id="Oval-Copy-10">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<circle stroke="#B37FEB" stroke-width="1" stroke-linejoin="square" fill-opacity="0.92" fill="#B37FEB" fill-rule="evenodd" cx="20" cy="20" r="19.5"></circle>
</g>
<g id="earth-copy-8" transform="translate(12.000000, 12.000000)" fill="#FFFFFF" fill-rule="nonzero">
<rect id="Rectangle-path" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M13.35,12.5140625 C13.353125,12.509375 13.3578125,12.5046875 13.3609375,12.5 C14.384375,11.2828125 15,9.7140625 15,8 C15,6.2859375 14.384375,4.7171875 13.3625,3.5 C13.359375,3.4953125 13.3546875,3.4921875 13.3515625,3.4875 C13.334375,3.4671875 13.31875,3.4484375 13.3015625,3.4296875 C13.2953125,3.421875 13.2890625,3.415625 13.2828125,3.4078125 C13.2609375,3.3828125 13.240625,3.359375 13.21875,3.334375 L13.2171875,3.3328125 C13.19375,3.30625 13.16875,3.2796875 13.1453125,3.253125 L13.14375,3.2515625 C13.09375,3.1984375 13.04375,3.1453125 12.9921875,3.09375 L12.990625,3.0921875 L12.915625,3.0171875 L12.9109375,3.0125 C12.8875,2.9890625 12.8640625,2.9671875 12.840625,2.9453125 C12.8328125,2.9375 12.825,2.9296875 12.815625,2.921875 C12.8,2.90625 12.784375,2.8921875 12.76875,2.878125 C12.7640625,2.8734375 12.7578125,2.86875 12.753125,2.8625 C11.50625,1.70625 9.8359375,1 8,1 C6.1640625,1 4.49375,1.70625 3.2453125,2.8625 C3.240625,2.8671875 3.234375,2.871875 3.2296875,2.878125 C3.2140625,2.8921875 3.1984375,2.9078125 3.1828125,2.9234375 C3.175,2.93125 3.1671875,2.9390625 3.1578125,2.946875 C3.134375,2.96875 3.1109375,2.9921875 3.0875,3.0140625 L3.0828125,3.01875 L3.0078125,3.09375 L3.00625,3.0953125 C2.9546875,3.146875 2.9046875,3.2 2.8546875,3.253125 L2.853125,3.2546875 C2.828125,3.28125 2.8046875,3.3078125 2.78125,3.334375 L2.7796875,3.3359375 C2.7578125,3.359375 2.7359375,3.384375 2.715625,3.409375 C2.709375,3.4171875 2.703125,3.4234375 2.696875,3.43125 C2.6796875,3.45 2.6640625,3.4703125 2.646875,3.4890625 C2.64375,3.49375 2.6390625,3.496875 2.6359375,3.5015625 C1.615625,4.7171875 1,6.2859375 1,8 C1,9.7140625 1.615625,11.2828125 2.6375,12.5 C2.640625,12.5046875 2.6453125,12.509375 2.6484375,12.5140625 C2.6640625,12.5328125 2.68125,12.553125 2.696875,12.571875 C2.703125,12.5796875 2.709375,12.5859375 2.715625,12.59375 C2.7375,12.61875 2.7578125,12.6421875 2.7796875,12.6671875 C2.7796875,12.66875 2.78125,12.66875 2.78125,12.6703125 C2.8046875,12.696875 2.828125,12.7234375 2.853125,12.7484375 L2.8546875,12.75 C2.9046875,12.803125 2.9546875,12.85625 3.0046875,12.9078125 L3.00625,12.909375 C3.03125,12.934375 3.0546875,12.959375 3.0796875,12.9828125 L3.084375,12.9875 C3.1359375,13.0390625 3.1890625,13.0890625 3.2421875,13.1375 C4.49375,14.29375 6.1640625,15 8,15 C9.8359375,15 11.50625,14.29375 12.7546875,13.1375 C12.8078125,13.0890625 12.859375,13.0390625 12.9109375,12.9875 L12.915625,12.9828125 C12.940625,12.9578125 12.965625,12.934375 12.9890625,12.909375 L12.990625,12.9078125 C13.0421875,12.85625 13.0921875,12.803125 13.140625,12.75 L13.1421875,12.7484375 C13.165625,12.721875 13.190625,12.696875 13.2140625,12.6703125 C13.2140625,12.66875 13.215625,12.66875 13.215625,12.6671875 C13.2375,12.64375 13.259375,12.61875 13.2796875,12.59375 C13.2859375,12.5859375 13.2921875,12.5796875 13.2984375,12.571875 C13.3171875,12.5515625 13.334375,12.5328125 13.35,12.5140625 Z M13.4140625,10.2859375 C13.1984375,10.7953125 12.9140625,11.2671875 12.5671875,11.6953125 C12.178125,11.359375 11.7515625,11.065625 11.29375,10.821875 C11.475,10.0890625 11.5875,9.284375 11.6171875,8.4375 L13.859375,8.4375 C13.8125,9.0765625 13.6625,9.696875 13.4140625,10.2859375 Z M13.859375,7.5625 L11.6171875,7.5625 C11.5875,6.715625 11.475,5.9109375 11.29375,5.178125 C11.7515625,4.934375 12.178125,4.640625 12.5671875,4.3046875 C12.9140625,4.7328125 13.1984375,5.2046875 13.4140625,5.7140625 C13.6625,6.303125 13.8125,6.9234375 13.859375,7.5625 Z M10.2859375,2.5859375 C10.90625,2.8484375 11.4703125,3.2109375 11.9671875,3.6671875 C11.678125,3.9140625 11.3671875,4.13125 11.0390625,4.3203125 C10.79375,3.6171875 10.4796875,3.00625 10.1140625,2.5171875 C10.171875,2.5390625 10.2296875,2.5625 10.2859375,2.5859375 Z M8.8703125,13.5328125 C8.7265625,13.6453125 8.5828125,13.73125 8.4375,13.7890625 L8.4375,10.890625 C9.0609375,10.934375 9.665625,11.071875 10.2453125,11.3 C10.115625,11.684375 9.965625,12.0390625 9.7921875,12.359375 C9.5203125,12.865625 9.2015625,13.2703125 8.8703125,13.5328125 Z M9.7921875,3.640625 C9.9640625,3.9625 10.115625,4.3171875 10.2453125,4.7 C9.665625,4.928125 9.0609375,5.065625 8.4375,5.109375 L8.4375,2.2125 C8.58125,2.2703125 8.7265625,2.3546875 8.8703125,2.46875 C9.2015625,2.7296875 9.5203125,3.134375 9.7921875,3.640625 Z M8.4375,10.0140625 L8.4375,8.4375 L10.7421875,8.4375 C10.7171875,9.128125 10.63125,9.7984375 10.4875,10.434375 L10.4828125,10.453125 C9.840625,10.209375 9.153125,10.0578125 8.4375,10.0140625 Z M8.4375,7.5625 L8.4375,5.9859375 C9.153125,5.9421875 9.840625,5.790625 10.4828125,5.546875 L10.4875,5.565625 C10.63125,6.2015625 10.7171875,6.8703125 10.7421875,7.5625 L8.4375,7.5625 Z M7.5625,8.4375 L7.5625,10.0140625 C6.846875,10.0578125 6.159375,10.209375 5.5171875,10.453125 L5.5125,10.434375 C5.36875,9.7984375 5.2828125,9.1296875 5.2578125,8.4375 L7.5625,8.4375 Z M5.2578125,7.5625 C5.2828125,6.871875 5.36875,6.2015625 5.5125,5.565625 L5.5171875,5.546875 C6.159375,5.790625 6.8453125,5.9421875 7.5625,5.9859375 L7.5625,7.5625 L5.2578125,7.5625 Z M7.5625,10.890625 L7.5625,13.7875 C7.41875,13.7296875 7.2734375,13.6453125 7.1296875,13.53125 C6.7984375,13.2703125 6.478125,12.8640625 6.20625,12.3578125 C6.034375,12.0359375 5.8828125,11.68125 5.753125,11.2984375 C6.334375,11.0703125 6.9390625,10.934375 7.5625,10.890625 Z M7.5625,5.109375 C6.9390625,5.065625 6.334375,4.928125 5.7546875,4.7 C5.884375,4.315625 6.034375,3.9609375 6.2078125,3.640625 C6.4796875,3.134375 6.7984375,2.728125 7.13125,2.4671875 C7.275,2.3546875 7.41875,2.26875 7.5640625,2.2109375 L7.5640625,5.109375 L7.5625,5.109375 Z M5.7140625,2.5859375 C5.771875,2.5625 5.828125,2.5390625 5.8859375,2.5171875 C5.5203125,3.00625 5.20625,3.6171875 4.9609375,4.3203125 C4.6328125,4.1328125 4.321875,3.9140625 4.0328125,3.6671875 C4.5296875,3.2109375 5.09375,2.8484375 5.7140625,2.5859375 Z M2.5859375,5.7140625 C2.8015625,5.2046875 3.0859375,4.7328125 3.4328125,4.3046875 C3.821875,4.640625 4.2484375,4.934375 4.70625,5.178125 C4.525,5.9109375 4.4125,6.715625 4.3828125,7.5625 L2.140625,7.5625 C2.1875,6.9234375 2.3375,6.303125 2.5859375,5.7140625 Z M2.140625,8.4375 L4.3828125,8.4375 C4.4125,9.284375 4.525,10.0890625 4.70625,10.821875 C4.2484375,11.065625 3.821875,11.359375 3.4328125,11.6953125 C3.0859375,11.2671875 2.8015625,10.7953125 2.5859375,10.2859375 C2.3375,9.696875 2.1875,9.0765625 2.140625,8.4375 Z M5.7140625,13.4140625 C5.09375,13.1515625 4.5296875,12.7890625 4.0328125,12.3328125 C4.321875,12.0859375 4.6328125,11.86875 4.9609375,11.6796875 C5.20625,12.3828125 5.5203125,12.99375 5.8859375,13.4828125 C5.828125,13.4609375 5.7703125,13.4375 5.7140625,13.4140625 Z M10.2859375,13.4140625 C10.228125,13.4375 10.171875,13.4609375 10.1140625,13.4828125 C10.4796875,12.99375 10.79375,12.3828125 11.0390625,11.6796875 C11.3671875,11.8671875 11.678125,12.0859375 11.9671875,12.3328125 C11.4703125,12.7890625 10.90625,13.1515625 10.2859375,13.4140625 Z" id="Shape"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>earth copy 53</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="拓扑图编译器" transform="translate(-222.000000, -1591.000000)" fill="#FFFFFF" fill-rule="nonzero">
<g id="earth-copy-53" transform="translate(221.000000, 1590.000000)">
<rect id="Rectangle-path" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M13.35,12.5140625 C13.353125,12.509375 13.3578125,12.5046875 13.3609375,12.5 C14.384375,11.2828125 15,9.7140625 15,8 C15,6.2859375 14.384375,4.7171875 13.3625,3.5 C13.359375,3.4953125 13.3546875,3.4921875 13.3515625,3.4875 C13.334375,3.4671875 13.31875,3.4484375 13.3015625,3.4296875 C13.2953125,3.421875 13.2890625,3.415625 13.2828125,3.4078125 C13.2609375,3.3828125 13.240625,3.359375 13.21875,3.334375 L13.2171875,3.3328125 C13.19375,3.30625 13.16875,3.2796875 13.1453125,3.253125 L13.14375,3.2515625 C13.09375,3.1984375 13.04375,3.1453125 12.9921875,3.09375 L12.990625,3.0921875 L12.915625,3.0171875 L12.9109375,3.0125 C12.8875,2.9890625 12.8640625,2.9671875 12.840625,2.9453125 C12.8328125,2.9375 12.825,2.9296875 12.815625,2.921875 C12.8,2.90625 12.784375,2.8921875 12.76875,2.878125 C12.7640625,2.8734375 12.7578125,2.86875 12.753125,2.8625 C11.50625,1.70625 9.8359375,1 8,1 C6.1640625,1 4.49375,1.70625 3.2453125,2.8625 C3.240625,2.8671875 3.234375,2.871875 3.2296875,2.878125 C3.2140625,2.8921875 3.1984375,2.9078125 3.1828125,2.9234375 C3.175,2.93125 3.1671875,2.9390625 3.1578125,2.946875 C3.134375,2.96875 3.1109375,2.9921875 3.0875,3.0140625 L3.0828125,3.01875 L3.0078125,3.09375 L3.00625,3.0953125 C2.9546875,3.146875 2.9046875,3.2 2.8546875,3.253125 L2.853125,3.2546875 C2.828125,3.28125 2.8046875,3.3078125 2.78125,3.334375 L2.7796875,3.3359375 C2.7578125,3.359375 2.7359375,3.384375 2.715625,3.409375 C2.709375,3.4171875 2.703125,3.4234375 2.696875,3.43125 C2.6796875,3.45 2.6640625,3.4703125 2.646875,3.4890625 C2.64375,3.49375 2.6390625,3.496875 2.6359375,3.5015625 C1.615625,4.7171875 1,6.2859375 1,8 C1,9.7140625 1.615625,11.2828125 2.6375,12.5 C2.640625,12.5046875 2.6453125,12.509375 2.6484375,12.5140625 C2.6640625,12.5328125 2.68125,12.553125 2.696875,12.571875 C2.703125,12.5796875 2.709375,12.5859375 2.715625,12.59375 C2.7375,12.61875 2.7578125,12.6421875 2.7796875,12.6671875 C2.7796875,12.66875 2.78125,12.66875 2.78125,12.6703125 C2.8046875,12.696875 2.828125,12.7234375 2.853125,12.7484375 L2.8546875,12.75 C2.9046875,12.803125 2.9546875,12.85625 3.0046875,12.9078125 L3.00625,12.909375 C3.03125,12.934375 3.0546875,12.959375 3.0796875,12.9828125 L3.084375,12.9875 C3.1359375,13.0390625 3.1890625,13.0890625 3.2421875,13.1375 C4.49375,14.29375 6.1640625,15 8,15 C9.8359375,15 11.50625,14.29375 12.7546875,13.1375 C12.8078125,13.0890625 12.859375,13.0390625 12.9109375,12.9875 L12.915625,12.9828125 C12.940625,12.9578125 12.965625,12.934375 12.9890625,12.909375 L12.990625,12.9078125 C13.0421875,12.85625 13.0921875,12.803125 13.140625,12.75 L13.1421875,12.7484375 C13.165625,12.721875 13.190625,12.696875 13.2140625,12.6703125 C13.2140625,12.66875 13.215625,12.66875 13.215625,12.6671875 C13.2375,12.64375 13.259375,12.61875 13.2796875,12.59375 C13.2859375,12.5859375 13.2921875,12.5796875 13.2984375,12.571875 C13.3171875,12.5515625 13.334375,12.5328125 13.35,12.5140625 Z M13.4140625,10.2859375 C13.1984375,10.7953125 12.9140625,11.2671875 12.5671875,11.6953125 C12.178125,11.359375 11.7515625,11.065625 11.29375,10.821875 C11.475,10.0890625 11.5875,9.284375 11.6171875,8.4375 L13.859375,8.4375 C13.8125,9.0765625 13.6625,9.696875 13.4140625,10.2859375 Z M13.859375,7.5625 L11.6171875,7.5625 C11.5875,6.715625 11.475,5.9109375 11.29375,5.178125 C11.7515625,4.934375 12.178125,4.640625 12.5671875,4.3046875 C12.9140625,4.7328125 13.1984375,5.2046875 13.4140625,5.7140625 C13.6625,6.303125 13.8125,6.9234375 13.859375,7.5625 Z M10.2859375,2.5859375 C10.90625,2.8484375 11.4703125,3.2109375 11.9671875,3.6671875 C11.678125,3.9140625 11.3671875,4.13125 11.0390625,4.3203125 C10.79375,3.6171875 10.4796875,3.00625 10.1140625,2.5171875 C10.171875,2.5390625 10.2296875,2.5625 10.2859375,2.5859375 Z M8.8703125,13.5328125 C8.7265625,13.6453125 8.5828125,13.73125 8.4375,13.7890625 L8.4375,10.890625 C9.0609375,10.934375 9.665625,11.071875 10.2453125,11.3 C10.115625,11.684375 9.965625,12.0390625 9.7921875,12.359375 C9.5203125,12.865625 9.2015625,13.2703125 8.8703125,13.5328125 Z M9.7921875,3.640625 C9.9640625,3.9625 10.115625,4.3171875 10.2453125,4.7 C9.665625,4.928125 9.0609375,5.065625 8.4375,5.109375 L8.4375,2.2125 C8.58125,2.2703125 8.7265625,2.3546875 8.8703125,2.46875 C9.2015625,2.7296875 9.5203125,3.134375 9.7921875,3.640625 Z M8.4375,10.0140625 L8.4375,8.4375 L10.7421875,8.4375 C10.7171875,9.128125 10.63125,9.7984375 10.4875,10.434375 L10.4828125,10.453125 C9.840625,10.209375 9.153125,10.0578125 8.4375,10.0140625 Z M8.4375,7.5625 L8.4375,5.9859375 C9.153125,5.9421875 9.840625,5.790625 10.4828125,5.546875 L10.4875,5.565625 C10.63125,6.2015625 10.7171875,6.8703125 10.7421875,7.5625 L8.4375,7.5625 Z M7.5625,8.4375 L7.5625,10.0140625 C6.846875,10.0578125 6.159375,10.209375 5.5171875,10.453125 L5.5125,10.434375 C5.36875,9.7984375 5.2828125,9.1296875 5.2578125,8.4375 L7.5625,8.4375 Z M5.2578125,7.5625 C5.2828125,6.871875 5.36875,6.2015625 5.5125,5.565625 L5.5171875,5.546875 C6.159375,5.790625 6.8453125,5.9421875 7.5625,5.9859375 L7.5625,7.5625 L5.2578125,7.5625 Z M7.5625,10.890625 L7.5625,13.7875 C7.41875,13.7296875 7.2734375,13.6453125 7.1296875,13.53125 C6.7984375,13.2703125 6.478125,12.8640625 6.20625,12.3578125 C6.034375,12.0359375 5.8828125,11.68125 5.753125,11.2984375 C6.334375,11.0703125 6.9390625,10.934375 7.5625,10.890625 Z M7.5625,5.109375 C6.9390625,5.065625 6.334375,4.928125 5.7546875,4.7 C5.884375,4.315625 6.034375,3.9609375 6.2078125,3.640625 C6.4796875,3.134375 6.7984375,2.728125 7.13125,2.4671875 C7.275,2.3546875 7.41875,2.26875 7.5640625,2.2109375 L7.5640625,5.109375 L7.5625,5.109375 Z M5.7140625,2.5859375 C5.771875,2.5625 5.828125,2.5390625 5.8859375,2.5171875 C5.5203125,3.00625 5.20625,3.6171875 4.9609375,4.3203125 C4.6328125,4.1328125 4.321875,3.9140625 4.0328125,3.6671875 C4.5296875,3.2109375 5.09375,2.8484375 5.7140625,2.5859375 Z M2.5859375,5.7140625 C2.8015625,5.2046875 3.0859375,4.7328125 3.4328125,4.3046875 C3.821875,4.640625 4.2484375,4.934375 4.70625,5.178125 C4.525,5.9109375 4.4125,6.715625 4.3828125,7.5625 L2.140625,7.5625 C2.1875,6.9234375 2.3375,6.303125 2.5859375,5.7140625 Z M2.140625,8.4375 L4.3828125,8.4375 C4.4125,9.284375 4.525,10.0890625 4.70625,10.821875 C4.2484375,11.065625 3.821875,11.359375 3.4328125,11.6953125 C3.0859375,11.2671875 2.8015625,10.7953125 2.5859375,10.2859375 C2.3375,9.696875 2.1875,9.0765625 2.140625,8.4375 Z M5.7140625,13.4140625 C5.09375,13.1515625 4.5296875,12.7890625 4.0328125,12.3328125 C4.321875,12.0859375 4.6328125,11.86875 4.9609375,11.6796875 C5.20625,12.3828125 5.5203125,12.99375 5.8859375,13.4828125 C5.828125,13.4609375 5.7703125,13.4375 5.7140625,13.4140625 Z M10.2859375,13.4140625 C10.228125,13.4375 10.171875,13.4609375 10.1140625,13.4828125 C10.4796875,12.99375 10.79375,12.3828125 11.0390625,11.6796875 C11.3671875,11.8671875 11.678125,12.0859375 11.9671875,12.3328125 C11.4703125,12.7890625 10.90625,13.1515625 10.2859375,13.4140625 Z" id="Shape"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Group 17</title>
<desc>Created with Sketch.</desc>
<defs>
<circle id="path-1" cx="20" cy="20" r="20"></circle>
<filter x="-17.5%" y="-12.5%" width="135.0%" height="135.0%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="拓扑图编译器" transform="translate(-119.000000, -139.000000)">
<g id="Group-17" transform="translate(123.000000, 141.000000)">
<g id="Oval-Copy-9">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<circle stroke="#5CDBD3" stroke-width="1" stroke-linejoin="square" fill-opacity="0.92" fill="#5CDBD3" fill-rule="evenodd" cx="20" cy="20" r="19.5"></circle>
</g>
<g id="earth-copy-7" transform="translate(12.000000, 12.000000)" fill="#FFFFFF" fill-rule="nonzero">
<rect id="Rectangle-path" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M13.35,12.5140625 C13.353125,12.509375 13.3578125,12.5046875 13.3609375,12.5 C14.384375,11.2828125 15,9.7140625 15,8 C15,6.2859375 14.384375,4.7171875 13.3625,3.5 C13.359375,3.4953125 13.3546875,3.4921875 13.3515625,3.4875 C13.334375,3.4671875 13.31875,3.4484375 13.3015625,3.4296875 C13.2953125,3.421875 13.2890625,3.415625 13.2828125,3.4078125 C13.2609375,3.3828125 13.240625,3.359375 13.21875,3.334375 L13.2171875,3.3328125 C13.19375,3.30625 13.16875,3.2796875 13.1453125,3.253125 L13.14375,3.2515625 C13.09375,3.1984375 13.04375,3.1453125 12.9921875,3.09375 L12.990625,3.0921875 L12.915625,3.0171875 L12.9109375,3.0125 C12.8875,2.9890625 12.8640625,2.9671875 12.840625,2.9453125 C12.8328125,2.9375 12.825,2.9296875 12.815625,2.921875 C12.8,2.90625 12.784375,2.8921875 12.76875,2.878125 C12.7640625,2.8734375 12.7578125,2.86875 12.753125,2.8625 C11.50625,1.70625 9.8359375,1 8,1 C6.1640625,1 4.49375,1.70625 3.2453125,2.8625 C3.240625,2.8671875 3.234375,2.871875 3.2296875,2.878125 C3.2140625,2.8921875 3.1984375,2.9078125 3.1828125,2.9234375 C3.175,2.93125 3.1671875,2.9390625 3.1578125,2.946875 C3.134375,2.96875 3.1109375,2.9921875 3.0875,3.0140625 L3.0828125,3.01875 L3.0078125,3.09375 L3.00625,3.0953125 C2.9546875,3.146875 2.9046875,3.2 2.8546875,3.253125 L2.853125,3.2546875 C2.828125,3.28125 2.8046875,3.3078125 2.78125,3.334375 L2.7796875,3.3359375 C2.7578125,3.359375 2.7359375,3.384375 2.715625,3.409375 C2.709375,3.4171875 2.703125,3.4234375 2.696875,3.43125 C2.6796875,3.45 2.6640625,3.4703125 2.646875,3.4890625 C2.64375,3.49375 2.6390625,3.496875 2.6359375,3.5015625 C1.615625,4.7171875 1,6.2859375 1,8 C1,9.7140625 1.615625,11.2828125 2.6375,12.5 C2.640625,12.5046875 2.6453125,12.509375 2.6484375,12.5140625 C2.6640625,12.5328125 2.68125,12.553125 2.696875,12.571875 C2.703125,12.5796875 2.709375,12.5859375 2.715625,12.59375 C2.7375,12.61875 2.7578125,12.6421875 2.7796875,12.6671875 C2.7796875,12.66875 2.78125,12.66875 2.78125,12.6703125 C2.8046875,12.696875 2.828125,12.7234375 2.853125,12.7484375 L2.8546875,12.75 C2.9046875,12.803125 2.9546875,12.85625 3.0046875,12.9078125 L3.00625,12.909375 C3.03125,12.934375 3.0546875,12.959375 3.0796875,12.9828125 L3.084375,12.9875 C3.1359375,13.0390625 3.1890625,13.0890625 3.2421875,13.1375 C4.49375,14.29375 6.1640625,15 8,15 C9.8359375,15 11.50625,14.29375 12.7546875,13.1375 C12.8078125,13.0890625 12.859375,13.0390625 12.9109375,12.9875 L12.915625,12.9828125 C12.940625,12.9578125 12.965625,12.934375 12.9890625,12.909375 L12.990625,12.9078125 C13.0421875,12.85625 13.0921875,12.803125 13.140625,12.75 L13.1421875,12.7484375 C13.165625,12.721875 13.190625,12.696875 13.2140625,12.6703125 C13.2140625,12.66875 13.215625,12.66875 13.215625,12.6671875 C13.2375,12.64375 13.259375,12.61875 13.2796875,12.59375 C13.2859375,12.5859375 13.2921875,12.5796875 13.2984375,12.571875 C13.3171875,12.5515625 13.334375,12.5328125 13.35,12.5140625 Z M13.4140625,10.2859375 C13.1984375,10.7953125 12.9140625,11.2671875 12.5671875,11.6953125 C12.178125,11.359375 11.7515625,11.065625 11.29375,10.821875 C11.475,10.0890625 11.5875,9.284375 11.6171875,8.4375 L13.859375,8.4375 C13.8125,9.0765625 13.6625,9.696875 13.4140625,10.2859375 Z M13.859375,7.5625 L11.6171875,7.5625 C11.5875,6.715625 11.475,5.9109375 11.29375,5.178125 C11.7515625,4.934375 12.178125,4.640625 12.5671875,4.3046875 C12.9140625,4.7328125 13.1984375,5.2046875 13.4140625,5.7140625 C13.6625,6.303125 13.8125,6.9234375 13.859375,7.5625 Z M10.2859375,2.5859375 C10.90625,2.8484375 11.4703125,3.2109375 11.9671875,3.6671875 C11.678125,3.9140625 11.3671875,4.13125 11.0390625,4.3203125 C10.79375,3.6171875 10.4796875,3.00625 10.1140625,2.5171875 C10.171875,2.5390625 10.2296875,2.5625 10.2859375,2.5859375 Z M8.8703125,13.5328125 C8.7265625,13.6453125 8.5828125,13.73125 8.4375,13.7890625 L8.4375,10.890625 C9.0609375,10.934375 9.665625,11.071875 10.2453125,11.3 C10.115625,11.684375 9.965625,12.0390625 9.7921875,12.359375 C9.5203125,12.865625 9.2015625,13.2703125 8.8703125,13.5328125 Z M9.7921875,3.640625 C9.9640625,3.9625 10.115625,4.3171875 10.2453125,4.7 C9.665625,4.928125 9.0609375,5.065625 8.4375,5.109375 L8.4375,2.2125 C8.58125,2.2703125 8.7265625,2.3546875 8.8703125,2.46875 C9.2015625,2.7296875 9.5203125,3.134375 9.7921875,3.640625 Z M8.4375,10.0140625 L8.4375,8.4375 L10.7421875,8.4375 C10.7171875,9.128125 10.63125,9.7984375 10.4875,10.434375 L10.4828125,10.453125 C9.840625,10.209375 9.153125,10.0578125 8.4375,10.0140625 Z M8.4375,7.5625 L8.4375,5.9859375 C9.153125,5.9421875 9.840625,5.790625 10.4828125,5.546875 L10.4875,5.565625 C10.63125,6.2015625 10.7171875,6.8703125 10.7421875,7.5625 L8.4375,7.5625 Z M7.5625,8.4375 L7.5625,10.0140625 C6.846875,10.0578125 6.159375,10.209375 5.5171875,10.453125 L5.5125,10.434375 C5.36875,9.7984375 5.2828125,9.1296875 5.2578125,8.4375 L7.5625,8.4375 Z M5.2578125,7.5625 C5.2828125,6.871875 5.36875,6.2015625 5.5125,5.565625 L5.5171875,5.546875 C6.159375,5.790625 6.8453125,5.9421875 7.5625,5.9859375 L7.5625,7.5625 L5.2578125,7.5625 Z M7.5625,10.890625 L7.5625,13.7875 C7.41875,13.7296875 7.2734375,13.6453125 7.1296875,13.53125 C6.7984375,13.2703125 6.478125,12.8640625 6.20625,12.3578125 C6.034375,12.0359375 5.8828125,11.68125 5.753125,11.2984375 C6.334375,11.0703125 6.9390625,10.934375 7.5625,10.890625 Z M7.5625,5.109375 C6.9390625,5.065625 6.334375,4.928125 5.7546875,4.7 C5.884375,4.315625 6.034375,3.9609375 6.2078125,3.640625 C6.4796875,3.134375 6.7984375,2.728125 7.13125,2.4671875 C7.275,2.3546875 7.41875,2.26875 7.5640625,2.2109375 L7.5640625,5.109375 L7.5625,5.109375 Z M5.7140625,2.5859375 C5.771875,2.5625 5.828125,2.5390625 5.8859375,2.5171875 C5.5203125,3.00625 5.20625,3.6171875 4.9609375,4.3203125 C4.6328125,4.1328125 4.321875,3.9140625 4.0328125,3.6671875 C4.5296875,3.2109375 5.09375,2.8484375 5.7140625,2.5859375 Z M2.5859375,5.7140625 C2.8015625,5.2046875 3.0859375,4.7328125 3.4328125,4.3046875 C3.821875,4.640625 4.2484375,4.934375 4.70625,5.178125 C4.525,5.9109375 4.4125,6.715625 4.3828125,7.5625 L2.140625,7.5625 C2.1875,6.9234375 2.3375,6.303125 2.5859375,5.7140625 Z M2.140625,8.4375 L4.3828125,8.4375 C4.4125,9.284375 4.525,10.0890625 4.70625,10.821875 C4.2484375,11.065625 3.821875,11.359375 3.4328125,11.6953125 C3.0859375,11.2671875 2.8015625,10.7953125 2.5859375,10.2859375 C2.3375,9.696875 2.1875,9.0765625 2.140625,8.4375 Z M5.7140625,13.4140625 C5.09375,13.1515625 4.5296875,12.7890625 4.0328125,12.3328125 C4.321875,12.0859375 4.6328125,11.86875 4.9609375,11.6796875 C5.20625,12.3828125 5.5203125,12.99375 5.8859375,13.4828125 C5.828125,13.4609375 5.7703125,13.4375 5.7140625,13.4140625 Z M10.2859375,13.4140625 C10.228125,13.4375 10.171875,13.4609375 10.1140625,13.4828125 C10.4796875,12.99375 10.79375,12.3828125 11.0390625,11.6796875 C11.3671875,11.8671875 11.678125,12.0859375 11.9671875,12.3328125 C11.4703125,12.7890625 10.90625,13.1515625 10.2859375,13.4140625 Z" id="Shape"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
const generateMock = require('merge-umi-mock-data');
const path = require('path');
generateMock(path.join(__dirname, '../mock'), path.join(__dirname, '../functions/mock/index.js'));
generateMock(path.join(__dirname, '../mock'), path.join(__dirname, '../lambda/mock/index.js'));
import { Icon } from 'antd';
const IconFont = Icon.createFromIconfontCN({
scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js',
});
export default IconFont;
{
"roots": [
{
"label": "法国",
"children": [
{
"label": "克罗地亚",
"side": "left",
"children": [
{
"label": "克罗地亚",
"children": [
{
"label": "克罗地亚",
"children": [
{
"label": "克罗地亚"
},
{
"label": "丹麦"
}
]
},
{
"label": "俄罗斯",
"children": [
{
"label": "俄罗斯"
},
{
"label": "西班牙"
}
]
}
]
},
{
"label": "英格兰",
"children": [
{
"label": "英格兰",
"children": [
{
"label": "英格兰"
},
{
"label": "哥伦比亚"
}
]
},
{
"label": "瑞典",
"children": [
{
"label": "瑞士"
},
{
"label": "瑞典"
}
]
}
]
}
]
},
{
"label": "法国",
"side": "right",
"children": [
{
"label": "法国",
"children": [
{
"label": "法国",
"children": [
{
"label": "法国"
},
{
"label": "阿根廷"
}
]
},
{
"label": "乌拉圭",
"children": [
{
"label": "乌拉圭"
},
{
"label": "葡萄牙"
}
]
}
]
},
{
"label": "比利时",
"children": [
{
"label": "比利时",
"children": [
{
"label": "比利时"
},
{
"label": "日本"
}
]
},
{
"label": "巴西",
"children": [
{
"label": "巴西"
},
{
"label": "墨西哥"
}
]
}
]
}
]
}
]
}
]
}
import React from 'react';
export interface ApplicationsProps {
data: {
content?: string;
updatedAt?: any;
avatar?: string;
owner?: string;
href?: string;
};
}
export default class ArticleListContent extends React.Component<ApplicationsProps, any> {}
import * as React from 'react';
import React from 'react';
import { RouteProps } from 'react-router';
type authorityFN = (currentAuthority?: string) => boolean;
......
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { Route, Redirect } from 'umi';
import Authorized from './Authorized';
// TODO: umi只会返回render和rest
......
......@@ -2,21 +2,13 @@ import React from 'react';
import PromiseRender from './PromiseRender';
import { CURRENT } from './renderAuthorize';
function isPromise(obj) {
return (
!!obj &&
(typeof obj === 'object' || typeof obj === 'function') &&
typeof obj.then === 'function'
);
}
/**
* 通用权限检查方法
* Common check permissions method
* @param { 权限判定 Permission judgment type string |array | Promise | Function } authority
* @param { 你的权限 Your permission description type:string} currentAuthority
* @param { 通过的组件 Passing components } target
* @param { 未通过的组件 no pass components } Exception
* @param { 权限判定 | Permission judgment } authority
* @param { 你的权限 | Your permission description } currentAuthority
* @param { 通过的组件 | Passing components } target
* @param { 未通过的组件 | no pass components } Exception
*/
const checkPermissions = (authority, currentAuthority, target, Exception) => {
// 没有判定权限.默认查看所有
......@@ -26,47 +18,36 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
}
// 数组处理
if (Array.isArray(authority)) {
if (authority.indexOf(currentAuthority) >= 0) {
return target;
}
if (Array.isArray(currentAuthority)) {
for (let i = 0; i < currentAuthority.length; i += 1) {
const element = currentAuthority[i];
if (authority.indexOf(element) >= 0) {
if (currentAuthority.some(item => authority.includes(item))) {
return target;
}
}
} else if (authority.includes(currentAuthority)) {
return target;
}
return Exception;
}
// string 处理
if (typeof authority === 'string') {
if (authority === currentAuthority) {
return target;
}
if (Array.isArray(currentAuthority)) {
for (let i = 0; i < currentAuthority.length; i += 1) {
const element = currentAuthority[i];
if (authority === element) {
if (currentAuthority.some(item => authority === item)) {
return target;
}
}
} else if (authority === currentAuthority) {
return target;
}
return Exception;
}
// Promise 处理
if (isPromise(authority)) {
if (authority instanceof Promise) {
return <PromiseRender ok={target} error={Exception} promise={authority} />;
}
// Function 处理
if (typeof authority === 'function') {
try {
const bool = authority(currentAuthority);
// 函数执行后返回值是 Promise
if (isPromise(bool)) {
if (bool instanceof Promise) {
return <PromiseRender ok={target} error={Exception} promise={bool} />;
}
if (bool) {
......
import React from 'react';
import { Spin } from 'antd';
import isEqual from 'lodash/isEqual';
import React from 'react';
import { isComponentClass } from './Secured';
export default class PromiseRender extends React.PureComponent {
export default class PromiseRender extends React.Component {
state = {
component: null,
};
......@@ -10,10 +12,14 @@ export default class PromiseRender extends React.PureComponent {
this.setRenderComponent(this.props);
}
componentDidUpdate(nextProps) {
// new Props enter
shouldComponentUpdate = (nextProps, nextState) => {
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) {
......@@ -37,8 +43,12 @@ export default class PromiseRender extends React.PureComponent {
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
checkIsInstantiation = target => {
if (!React.isValidElement(target)) {
return target;
if (isComponentClass(target)) {
const Target = target;
return props => <Target {...props} />;
}
if (React.isValidElement(target)) {
return props => React.cloneElement(target, props);
}
return () => target;
};
......
import React from 'react';
import Exception from '../Exception';
import CheckPermissions from './CheckPermissions';
/**
* 默认不能访问任何页面
* default is "NULL"
*/
const Exception403 = () => <Exception type="403" />;
export const isComponentClass = component => {
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 => {
if (!React.isValidElement(target)) {
return target;
if (isComponentClass(target)) {
const Target = target;
return props => <Target {...props} />;
}
if (React.isValidElement(target)) {
return props => React.cloneElement(target, props);
}
return () => target;
};
/**
* 用于判断是否拥有权限访问此view权限
* authority 支持传入 string, function:()=>boolean|Promise
* e.g. 'user' 只有user用户能访问
* e.g. 'user,admin' user和 admin 都能访问
* 用于判断是否拥有权限访问此 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, function: () => boolean | Promise
* 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
......
......@@ -16,7 +16,9 @@ const { Secured } = RenderAuthorized('user');
@Secured('admin')
class TestSecuredString extends React.Component {
render() {
<Alert message="user Passed!" type="success" showIcon />;
return (
<Alert message="user Passed!" type="success" showIcon />
)
}
}
ReactDOM.render(
......
import * as React from 'react';
import React from 'react';
import AuthorizedRoute, { authority } from './AuthorizedRoute';
export type IReactComponent<P = any> =
| React.StatelessComponent<P>
......
import * as React from 'react';
export interface IAvatarItemProps {
import React from 'react';
export declare type SizeType = number | 'small' | 'default' | 'large';
export interface AvatarItemProps {
tips: React.ReactNode;
src: string;
size?: SizeType;
style?: React.CSSProperties;
onClick?: () => void;
}
export default class AvatarItem extends React.Component<IAvatarItemProps, any> {
constructor(props: IAvatarItemProps);
export default class AvatarItem extends React.Component<AvatarItemProps, any> {
constructor(props: AvatarItemProps);
}
import * as React from 'react';
import AvatarItem from './AvatarItem';
import React from 'react';
import AvatarItem, { AvatarItemProps, SizeType } from './AvatarItem';
export interface IAvatarListProps {
size?: 'large' | 'small' | 'mini' | 'default';
export interface AvatarListProps {
Item?: React.ReactElement<AvatarItemProps>;
size?: SizeType;
maxLength?: number;
excessItemsStyle?: React.CSSProperties;
style?: React.CSSProperties;
children: React.ReactElement<AvatarItem> | Array<React.ReactElement<AvatarItem>>;
children: React.ReactElement<AvatarItemProps> | Array<React.ReactElement<AvatarItemProps>>;
}
export default class AvatarList extends React.Component<IAvatarListProps, any> {
export default class AvatarList extends React.Component<AvatarListProps, any> {
public static Item: typeof AvatarItem;
}
import React from 'react';
import PageLoading from '../PageLoading';
import { importCDN } from '@/utils/utils';
let isLoaderBizChart = false;
const loadBizCharts = async () => {
if (isLoaderBizChart) {
return Promise.resolve(true);
}
await Promise.all([
importCDN('//gw.alipayobjects.com/os/lib/bizcharts/3.4.3/umd/BizCharts.min.js'),
importCDN('//gw.alipayobjects.com/os/lib/antv/data-set/0.10.1/dist/data-set.min.js'),
]);
// eslint-disable-next-line no-console
console.log('bizCharts load success');
isLoaderBizChart = true;
return Promise.resolve(true);
};
class AsyncLoadBizCharts extends React.Component {
state = {
loading: !isLoaderBizChart,
};
async componentDidMount() {
await loadBizCharts();
requestAnimationFrame(() => {
this.setState({
loading: false,
});
});
}
render() {
const { children } = this.props;
const { loading } = this.state;
if (!loading) {
return children;
}
return <PageLoading />;
}
}
export { loadBizCharts, AsyncLoadBizCharts };
import * as React from 'react';
import React from 'react';
export interface IBarProps {
title: React.ReactNode;
color?: string;
......
import { CardProps } from 'antd/lib/card';
import * as React from 'react';
import React from 'react';
export interface IChartCardProps extends CardProps {
title: React.ReactNode;
......
import * as React from 'react';
import React from 'react';
export interface IFieldProps {
label: React.ReactNode;
value: React.ReactNode;
......
import * as React from 'react';
import React from 'react';
export interface IGaugeProps {
title: React.ReactNode;
color?: string;
......
......@@ -146,7 +146,7 @@ class Gauge extends React.Component {
<div style="width: 300px;text-align: center;font-size: 12px!important;">
<p style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">${title}</p>
<p style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
${data[0].value * 10}%
${(data[0].value * 10).toFixed(2)}%
</p>
</div>`}
/>
......
import * as React from 'react';
import React from 'react';
// g2已经更新到3.0
// 不带的写了
......
import * as React from 'react';
import React from 'react';
export interface IMiniBarProps {
color?: string;
height: number;
......
import * as React from 'react';
import React from 'react';
export interface IMiniProgressProps {
target: number;
targetLabel: string;
color?: string;
strokeWidth?: number;
percent?: number;
......
import React from 'react';
import { Tooltip } from 'antd';
import styles from './index.less';
const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => (
const MiniProgress = ({
targetLabel,
target,
color = 'rgb(19, 194, 194)',
strokeWidth,
percent,
}) => {
return (
<div className={styles.miniProgress}>
<Tooltip title={`目标值: ${target}%`}>
<Tooltip title={targetLabel}>
<div className={styles.target} style={{ left: target ? `${target}%` : null }}>
<span style={{ backgroundColor: color || null }} />
<span style={{ backgroundColor: color || null }} />
......@@ -22,6 +28,7 @@ const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percen
/>
</div>
</div>
);
);
};
export default MiniProgress;
......@@ -19,6 +19,8 @@
position: absolute;
top: 0;
bottom: 0;
z-index: 9;
width: 20px;
span {
position: absolute;
top: 0;
......
import * as React from 'react';
import React from 'react';
export interface IPieProps {
animate?: boolean;
color?: string;
......
import * as React from 'react';
import React from 'react';
export interface IRadarProps {
title?: React.ReactNode;
height: number;
......
import * as React from 'react';
import React from 'react';
export interface ITagCloudProps {
data: Array<{
name: string;
......
......@@ -51,9 +51,7 @@ class TagCloud extends Component {
initTagCloud = () => {
function getTextAttrs(cfg) {
return Object.assign(
{},
{
return Object.assign({}, cfg.style, {
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
......@@ -62,9 +60,7 @@ class TagCloud extends Component {
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
},
cfg.style
);
});
}
// 给point注册一个词云的shape
......
import * as React from 'react';
import React from 'react';
export interface ITimelineChartProps {
data: Array<{
x: number;
......
import * as React from 'react';
import React from 'react';
export interface IWaterWaveProps {
title: React.ReactNode;
color?: string;
......
import React, { Suspense } from 'react';
import numeral from 'numeral';
import ChartCard from './ChartCard';
import Field from './Field';
const getComponent = Component => {
return props => {
return (
<Suspense fallback="...">
<Component {...props} />
</Suspense>
);
};
};
const Bar = getComponent(React.lazy(() => import('./Bar')));
const Pie = getComponent(React.lazy(() => import('./Pie')));
const Radar = getComponent(React.lazy(() => import('./Radar')));
const Gauge = getComponent(React.lazy(() => import('./Gauge')));
const MiniArea = getComponent(React.lazy(() => import('./MiniArea')));
const MiniBar = getComponent(React.lazy(() => import('./MiniBar')));
const MiniProgress = getComponent(React.lazy(() => import('./MiniProgress')));
const WaterWave = getComponent(React.lazy(() => import('./WaterWave')));
const TagCloud = getComponent(React.lazy(() => import('./TagCloud')));
const TimelineChart = getComponent(React.lazy(() => import('./TimelineChart')));
import Bar from './Bar';
import Pie from './Pie';
import Radar from './Radar';
import Gauge from './Gauge';
import MiniArea from './MiniArea';
import MiniBar from './MiniBar';
import MiniProgress from './MiniProgress';
import WaterWave from './WaterWave';
import TagCloud from './TagCloud';
import TimelineChart from './TimelineChart';
const yuan = val => ${numeral(val).format('0,0')}`;
......
import * as React from 'react';
import React from 'react';
export interface ICountDownProps {
format?: (time: number) => void;
target: Date | number;
......
import * as React from 'react';
import React from 'react';
import { ColProps } from 'antd/es/col';
export default class Description extends React.Component<
{
term: React.ReactNode;
export interface DescriptionProps extends ColProps {
column?: number;
key?: string | number;
style?: React.CSSProperties;
},
any
> {}
term?: React.ReactNode;
}
export default class Description extends React.Component<DescriptionProps, any> {}
import * as React from 'react';
import Description from './Description';
import React from 'react';
import Description, { DescriptionProps } from './Description';
export interface IDescriptionListProps {
layout?: 'horizontal' | 'vertical';
export interface DescriptionListProps {
className?: string;
col?: number;
title: React.ReactNode;
description?: DescriptionProps[];
gutter?: number;
layout?: 'horizontal' | 'vertical';
size?: 'large' | 'small';
style?: React.CSSProperties;
title?: React.ReactNode;
}
export default class DescriptionList extends React.Component<IDescriptionListProps, any> {
export default class DescriptionList extends React.Component<DescriptionListProps, any> {
public static Description: typeof Description;
}
......@@ -8,6 +8,14 @@
overflow: hidden;
}
}
// fix margin top error of following descriptionList
& + & {
:global {
.ant-row {
margin-top: 16px;
}
}
}
.title {
margin-bottom: 16px;
......@@ -47,6 +55,14 @@
margin-bottom: -8px;
}
}
// fix margin top error of following descriptionList
& + .descriptionList {
:global {
.ant-row {
margin-top: 8px;
}
}
}
.title {
margin-bottom: 12px;
color: @text-color;
......
import React from 'react';
export interface EditableItemProps {
onChange?: (value?: string | string[] | number) => void;
}
export default class EditableItem extends React.Component<EditableItemProps, any> {}
import React from 'react';
export interface EditableLinkGroupProps {
links: any[];
onAdd: () => void;
linkElement: string;
}
export default class EditableLinkGroup extends React.Component<EditableLinkGroupProps, any> {}
import React from 'react';
import { NodeMenu, EdgeMenu, GroupMenu, MultiMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const FlowContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
</NodeMenu>
<EdgeMenu>
<MenuItem command="delete" />
</EdgeMenu>
<GroupMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
<MenuItem command="unGroup" icon="ungroup" text="Ungroup" />
</GroupMenu>
<MultiMenu>
<MenuItem command="copy" />
<MenuItem command="paste" />
<MenuItem command="addGroup" icon="group" text="Add Group" />
<MenuItem command="delete" />
</MultiMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
<MenuItem command="pasteHere" icon="paste" text="Paste Here" />
</CanvasMenu>
</ContextMenu>
);
};
export default FlowContextMenu;
import FlowContextMenu from './FlowContextMenu';
export default FlowContextMenu;
import React from 'react';
import { Command } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
import IconFont from '../../common/IconFont';
import styles from './index.less';
const MenuItem = props => {
const { command, icon, text } = props;
return (
<Command name={command}>
<div className={styles.item}>
<IconFont type={`icon-${icon || command}`} />
<span>{text || upperFirst(command)}</span>
</div>
</Command>
);
};
export default MenuItem;
import React from 'react';
import { NodeMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const MindContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="append" text="Topic" />
<MenuItem command="appendChild" icon="append-child" text="Subtopic" />
<MenuItem command="collapse" text="Fold" />
<MenuItem command="expand" text="Unfold" />
<MenuItem command="delete" />
</NodeMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
</CanvasMenu>
</ContextMenu>
);
};
export default MindContextMenu;
import FlowContextMenu from './FlowContextMenu';
import MindContextMenu from './MindContextMenu';
import KoniContextMenu from './KoniContextMenu';
export { FlowContextMenu, MindContextMenu, KoniContextMenu };
.contextMenu {
display: none;
overflow: hidden;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
.item {
display: flex;
align-items: center;
padding: 5px 12px;
cursor: pointer;
transition: all 0.3s;
user-select: none;
&:hover {
background: #e6f7ff;
}
i {
margin-right: 8px;
}
}
:global {
.disable {
:local {
.item {
color: rgba(0, 0, 0, 0.25);
cursor: auto;
&:hover {
background: #fff;
}
}
}
}
}
}
import React, { Fragment } from 'react';
import { Card, Form, Input, Select } from 'antd';
import { withPropsAPI } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
const { Item } = Form;
const { Option } = Select;
const inlineFormItemLayout = {
labelCol: {
sm: { span: 8 },
},
wrapperCol: {
sm: { span: 16 },
},
};
class DetailForm extends React.Component {
get item() {
const { propsAPI } = this.props;
return propsAPI.getSelected()[0];
}
handleSubmit = e => {
if (e && e.preventDefault) {
e.preventDefault();
}
const { form, propsAPI } = this.props;
const { getSelected, executeCommand, update } = propsAPI;
setTimeout(() => {
form.validateFieldsAndScroll((err, values) => {
if (err) {
return;
}
const item = getSelected()[0];
if (!item) {
return;
}
executeCommand(() => {
update(item, {
...values,
});
});
});
}, 0);
};
renderEdgeShapeSelect = () => {
return (
<Select onChange={this.handleSubmit}>
<Option value="flow-smooth">Smooth</Option>
<Option value="flow-polyline">Polyline</Option>
<Option value="flow-polyline-round">Polyline Round</Option>
</Select>
);
};
renderNodeDetail = () => {
const { form } = this.props;
const { label } = this.item.getModel();
return (
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
);
};
renderEdgeDetail = () => {
const { form } = this.props;
const { label = '', shape = 'flow-smooth' } = this.item.getModel();
return (
<Fragment>
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
<Item label="Shape" {...inlineFormItemLayout}>
{form.getFieldDecorator('shape', {
initialValue: shape,
})(this.renderEdgeShapeSelect())}
</Item>
</Fragment>
);
};
renderGroupDetail = () => {
const { form } = this.props;
const { label = '新建分组' } = this.item.getModel();
return (
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
);
};
render() {
const { type } = this.props;
if (!this.item) {
return null;
}
return (
<Card type="inner" size="small" title={upperFirst(type)} bordered={false}>
<Form onSubmit={this.handleSubmit}>
{type === 'node' && this.renderNodeDetail()}
{type === 'edge' && this.renderEdgeDetail()}
{type === 'group' && this.renderGroupDetail()}
</Form>
</Card>
);
}
}
export default Form.create()(withPropsAPI(DetailForm));
import React from 'react';
import { Card } from 'antd';
import { NodePanel, EdgePanel, GroupPanel, MultiPanel, CanvasPanel, DetailPanel } from 'gg-editor';
import DetailForm from './DetailForm';
import styles from './index.less';
const FlowDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<EdgePanel>
<DetailForm type="edge" />
</EdgePanel>
<GroupPanel>
<DetailForm type="group" />
</GroupPanel>
<MultiPanel>
<Card type="inner" size="small" title="Multi Select" bordered={false} />
</MultiPanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
export default FlowDetailPanel;
import FlowDetailPanel from './FlowDetailPanel';
export default FlowDetailPanel;
import React from 'react';
import { Card } from 'antd';
import { NodePanel, CanvasPanel, DetailPanel } from 'gg-editor';
import DetailForm from './DetailForm';
import styles from './index.less';
const MindDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
export default MindDetailPanel;
import FlowDetailPanel from './FlowDetailPanel';
import MindDetailPanel from './MindDetailPanel';
import KoniDetailPanel from './KoniDetailPanel';
export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };
.detailPanel {
flex: 1;
background: #fafafa;
:global {
.ant-card {
background: #fafafa;
}
}
}
import React from 'react';
import { Card } from 'antd';
import { ItemPanel, Item } from 'gg-editor';
import styles from './index.less';
const FlowItemPanel = () => {
return (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="72*72"
shape="flow-circle"
model={{
color: '#FA8C16',
label: 'Start',
}}
src="/ggeditor/flow/start.svg"
/>
<Item
type="node"
size="80*48"
shape="flow-rect"
model={{
color: '#1890FF',
label: 'Normal',
}}
src="/ggeditor/flow/normal.svg"
/>
<Item
type="node"
size="80*72"
shape="flow-rhombus"
model={{
color: '#13C2C2',
label: 'Decision',
}}
src="/ggeditor/flow/decision.svg"
/>
<Item
type="node"
size="80*48"
shape="flow-capsule"
model={{
color: '#722ED1',
label: 'Model',
}}
src="/ggeditor/flow/model.svg"
/>
</Card>
</ItemPanel>
);
};
export default FlowItemPanel;
import React from 'react';
import { Card } from 'antd';
import { ItemPanel, Item } from 'gg-editor';
import styles from './index.less';
const KoniItemPanel = () => {
return (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#69C0FF',
label: 'Bank',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/bank.svg"
/>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#5CDBD3',
label: 'Person',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/person.svg"
/>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#B37FEB',
label: 'Country',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/country.svg"
/>
</Card>
</ItemPanel>
);
};
export default KoniItemPanel;
import FlowItemPanel from './FlowItemPanel';
import KoniItemPanel from './KoniItemPanel';
export { FlowItemPanel, KoniItemPanel };
.itemPanel {
flex: 1;
background: #fafafa;
:global {
.ant-card {
background: #fafafa;
}
.ant-card-body {
display: flex;
flex-direction: column;
align-items: center;
> div {
margin-bottom: 16px;
}
}
}
}
import React from 'react';
import { RegisterNode } from 'gg-editor';
class KoniCustomNode extends React.Component {
render() {
const config = {
draw(item) {
const keyShape = this.drawKeyShape(item);
// draw label
this.drawLabel(item);
// draw image
const group = item.getGraphicGroup();
const model = item.getModel();
group.addShape('image', {
attrs: {
x: -7,
y: -7,
img: model.icon,
},
});
return keyShape;
},
};
return <RegisterNode name="koni-custom-node" config={config} />;
}
}
export default KoniCustomNode;
import React from 'react';
import { Card } from 'antd';
import { Minimap } from 'gg-editor';
const EditorMinimap = () => {
return (
<Card type="inner" size="small" title="Minimap" bordered={false}>
<Minimap height={200} />
</Card>
);
};
export default EditorMinimap;
import React from 'react';
import { Divider } from 'antd';
import { Toolbar } from 'gg-editor';
import ToolbarButton from './ToolbarButton';
import styles from './index.less';
const FlowToolbar = () => {
return (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="copy" />
<ToolbarButton command="paste" />
<ToolbarButton command="delete" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="toBack" icon="to-back" text="To Back" />
<ToolbarButton command="toFront" icon="to-front" text="To Front" />
<Divider type="vertical" />
<ToolbarButton command="multiSelect" icon="multi-select" text="Multi Select" />
<ToolbarButton command="addGroup" icon="group" text="Add Group" />
<ToolbarButton command="unGroup" icon="ungroup" text="Ungroup" />
</Toolbar>
);
};
export default FlowToolbar;
import FlowToolbar from './FlowToolbar';
export default FlowToolbar;
import React from 'react';
import { Divider } from 'antd';
import { Toolbar } from 'gg-editor';
import ToolbarButton from './ToolbarButton';
import styles from './index.less';
const FlowToolbar = () => {
return (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="append" text="Topic" />
<ToolbarButton command="appendChild" icon="append-child" text="Subtopic" />
<Divider type="vertical" />
<ToolbarButton command="collapse" text="Fold" />
<ToolbarButton command="expand" text="Unfold" />
</Toolbar>
);
};
export default FlowToolbar;
import React from 'react';
import { Tooltip } from 'antd';
import { Command } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
import IconFont from '../../common/IconFont';
import styles from './index.less';
const ToolbarButton = props => {
const { command, icon, text } = props;
return (
<Command name={command}>
<Tooltip
title={text || upperFirst(command)}
placement="bottom"
overlayClassName={styles.tooltip}
>
<IconFont type={`icon-${icon || command}`} />
</Tooltip>
</Command>
);
};
export default ToolbarButton;
import FlowToolbar from './FlowToolbar';
import MindToolbar from './MindToolbar';
import KoniToolbar from './KoniToolbar';
export { FlowToolbar, MindToolbar, KoniToolbar };
.toolbar {
display: flex;
align-items: center;
:global {
.command i {
display: inline-block;
width: 27px;
height: 27px;
margin: 0 6px;
padding-top: 6px;
text-align: center;
border: 1px solid #fff;
cursor: pointer;
&:hover {
border: 1px solid #e6e9ed;
}
}
.disable i {
color: rgba(0, 0, 0, 0.25);
cursor: auto;
&:hover {
border: 1px solid #fff;
}
}
}
}
.tooltip {
:global {
.ant-tooltip-inner {
font-size: 12px;
border-radius: 0;
}
}
}
import * as React from 'react';
import React from 'react';
import { TooltipProps } from 'antd/lib/tooltip';
export interface IEllipsisTooltipProps extends TooltipProps {
export interface EllipsisTooltipProps extends TooltipProps {
title?: undefined;
overlayStyle?: undefined;
}
export interface IEllipsisProps {
tooltip?: boolean | IEllipsisTooltipProps;
export interface EllipsisProps {
tooltip?: boolean | EllipsisTooltipProps;
length?: number;
lines?: number;
style?: React.CSSProperties;
......@@ -18,4 +18,4 @@ export interface IEllipsisProps {
export function getStrFullLength(str: string): number;
export function cutStrByFullLength(str: string, maxLength: number): string;
export default class Ellipsis extends React.Component<IEllipsisProps, any> {}
export default class Ellipsis extends React.Component<EllipsisProps, any> {}
import * as React from 'react';
export interface IExceptionProps {
import React from 'react';
import * as H from 'history';
export interface ExceptionProps<
L = {
to: H.LocationDescriptor;
href?: H.LocationDescriptor;
replace?: boolean;
innerRef?: (node: HTMLAnchorElement | null) => void;
}
> {
type?: '403' | '404' | '500';
title?: React.ReactNode;
desc?: React.ReactNode;
img?: string;
actions?: React.ReactNode;
linkElement?: string | React.ComponentType;
linkElement?: string | React.ComponentType<L>;
style?: React.CSSProperties;
className?: string;
backText?: React.ReactNode;
redirect?: string;
}
export default class Exception extends React.Component<IExceptionProps, any> {}
export default class Exception extends React.Component<ExceptionProps, any> {}
import * as React from 'react';
export interface IFooterToolbarProps {
import React from 'react';
export interface FooterToolbarProps {
extra: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
export default class FooterToolbar extends React.Component<IFooterToolbarProps, any> {}
export default class FooterToolbar extends React.Component<FooterToolbarProps, any> {}
import * as React from 'react';
export interface IGlobalFooterProps {
import React from 'react';
export interface GlobalFooterProps {
links?: Array<{
key?: string;
title: React.ReactNode;
......@@ -8,6 +8,7 @@ export interface IGlobalFooterProps {
}>;
copyright?: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
export default class GlobalFooter extends React.Component<IGlobalFooterProps, any> {}
export default class GlobalFooter extends React.Component<GlobalFooterProps, any> {}
import React from 'react';
import { DropDownProps } from 'antd/lib/dropdown';
import { ClickParam } from 'antd/es/menu';
import { SiderTheme } from 'antd/es/Layout/Sider';
export interface GlobalHeaderRightProps {
notices?: any[];
dispatch?: (args: any) => void;
currentUser?: {
avatar?: string;
name?: string;
title?: string;
group?: string;
signature?: string;
geographic?: any;
tags?: any[];
unreadCount: number;
};
fetchingNotices?: boolean;
onNoticeVisibleChange?: (visible: boolean) => void;
onMenuClick?: (param: ClickParam) => void;
onNoticeClear?: (tabName: string) => void;
theme?: SiderTheme;
}
export default class GlobalHeaderRight extends React.Component<GlobalHeaderRightProps, any> {}
import React, { PureComponent } from 'react';
import { FormattedMessage, formatMessage } from 'umi/locale';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip } from 'antd';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
import moment from 'moment';
import groupBy from 'lodash/groupBy';
import NoticeIcon from '../NoticeIcon';
......@@ -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 ? (
......
import React from 'react';
export interface GlobalHeaderProps {
collapsed?: boolean;
onCollapse?: (collapsed: boolean) => void;
isMobile?: boolean;
logo?: string;
onNoticeClear?: (type: string) => void;
onMenuClick?: ({ key: string }) => void;
onNoticeVisibleChange?: (b: boolean) => void;
}
export default class GlobalHeader extends React.Component<GlobalHeaderProps, any> {}
import * as React from 'react';
export default class HeaderDropdown extends React.Component<any, any> {}
import React from 'react';
import { DropDownProps } from 'antd/lib/dropdown';
declare type OverlayFunc = () => React.ReactNode;
export interface HeaderDropdownProps extends DropDownProps {
overlayClassName?: string;
overlay: React.ReactNode | OverlayFunc;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
}
export default class HeaderDropdown extends React.Component<HeaderDropdownProps, any> {}
import * as React from 'react';
export interface IHeaderSearchProps {
import React from 'react';
export interface HeaderSearchProps {
placeholder?: string;
dataSource?: string[];
defaultOpen?: boolean;
......@@ -12,4 +12,4 @@ export interface IHeaderSearchProps {
className?: string;
}
export default class HeaderSearch extends React.Component<IHeaderSearchProps, any> {}
export default class HeaderSearch extends React.Component<HeaderSearchProps, any> {}
import * as React from 'react';
export interface ILoginItemProps {
import React from 'react';
import { WrappedFormUtils } from 'antd/es/form/Form';
import ItemMap from './map';
import { Omit } from 'antd/es/_util/type';
export type WrappedLoginItemProps = Omit<LoginItemProps, 'form' | 'type' | 'updateActive'>;
export type LoginItemType = { [K in keyof typeof ItemMap]: React.FC<WrappedLoginItemProps> };
export interface LoginItemProps {
name?: string;
rules?: any[];
style?: React.CSSProperties;
onGetCaptcha?: () => void;
onGetCaptcha?: (event?: MouseEvent) => void;
placeholder?: string;
buttonText?: React.ReactNode;
onPressEnter?: (e: any) => void;
countDown?: number;
getCaptchaButtonText?: string;
getCaptchaSecondText?: string;
updateActive: (activeItem: any) => void;
form: WrappedFormUtils;
type: string;
defaultValue?: string;
customprops?: any;
onChange?: (e: any) => void;
}
export default class LoginItem extends React.Component<ILoginItemProps, any> {}
export default class LoginItem extends React.Component<LoginItemProps, any> {}
import * as React from 'react';
import React from 'react';
export interface ILoginTabProps {
export interface LoginTabProps {
key?: string;
tab?: React.ReactNode;
tabUtil: {
addTab: (id: any) => void;
removeTab: (id: any) => void;
};
}
export default class LoginTab extends React.Component<ILoginTabProps, any> {}
export default class LoginTab extends React.Component<LoginTabProps, any> {}
......@@ -18,55 +18,81 @@ class LoginDemo extends React.Component {
notice: '',
type: 'tab2',
autoLogin: true,
}
};
onSubmit = (err, values) => {
console.log('value collected ->', { ...values, autoLogin: this.state.autoLogin });
console.log('value collected ->', {
...values,
autoLogin: this.state.autoLogin,
});
if (this.state.type === 'tab1') {
this.setState({
this.setState(
{
notice: '',
}, () => {
if (!err && (values.username !== 'admin' || values.password !== '888888')) {
},
() => {
if (
!err &&
(values.username !== 'admin' || values.password !== '888888')
) {
setTimeout(() => {
this.setState({
notice: 'The combination of username and password is incorrect!',
notice:
'The combination of username and password is incorrect!',
});
}, 500);
}
});
}
},
);
}
};
onTabChange = (key) => {
this.setState({
type: key,
});
}
};
changeAutoLogin = (e) => {
this.setState({
autoLogin: e.target.checked,
});
}
};
render() {
return (
<div className="login-warp">
<Login
defaultActiveKey={this.state.type}
onTabChange={this.onTabChange}
onSubmit={this.onSubmit}
>
<Tab key="tab1" tab="Account">
{
this.state.notice &&
<Alert style={{ marginBottom: 24 }} message={this.state.notice} type="error" showIcon closable />
}
{this.state.notice && (
<Alert
style={{ marginBottom: 24 }}
message={this.state.notice}
type="error"
showIcon
closable
/>
)}
<UserName name="username" />
<Password name="password" />
</Tab>
<Tab key="tab2" tab="Mobile">
<Mobile name="mobile" />
<Captcha onGetCaptcha={() => console.log('Get captcha!')} name="captcha" />
<Captcha
onGetCaptcha={() => console.log('Get captcha!')}
name="captcha"
/>
</Tab>
<div>
<Checkbox checked={this.state.autoLogin} onChange={this.changeAutoLogin}>Keep me logged in</Checkbox>
<a style={{ float: 'right' }} href="">Forgot password</a>
<Checkbox
checked={this.state.autoLogin}
onChange={this.changeAutoLogin}
>
Keep me logged in
</Checkbox>
<a style={{ float: 'right' }} href="">
Forgot password
</a>
</div>
<Submit>Login</Submit>
<div>
......@@ -74,9 +100,12 @@ class LoginDemo extends React.Component {
<span className="icon icon-alipay" />
<span className="icon icon-taobao" />
<span className="icon icon-weibo" />
<a style={{ float: 'right' }} href="">Register</a>
<a style={{ float: 'right' }} href="">
Register
</a>
</div>
</Login>
</div>
);
}
}
......@@ -85,6 +114,10 @@ ReactDOM.render(<LoginDemo />, mountNode);
````
<style>
#scaffold-src-components-Login-demo-basic .login-warp{
max-width: 360px;
margin: auto;
}
#scaffold-src-components-Login-demo-basic .icon {
display: inline-block;
width: 24px;
......
import Button from 'antd/lib/button';
import * as React from 'react';
import LoginItem from './LoginItem';
import React from 'react';
import LoginItem, { LoginItemProps, LoginItemType } from './LoginItem';
import LoginTab from './LoginTab';
import { WrappedFormUtils } from 'antd/es/form/Form';
import LoginSubmit from './LoginSubmit';
export interface ILoginProps {
export interface LoginProps {
defaultActiveKey?: string;
onTabChange?: (key: string) => void;
style?: React.CSSProperties;
onSubmit?: (error: any, values: any) => void;
className?: string;
}
export default class Login extends React.Component<ILoginProps, any> {
interface Login extends WrappedFormUtils {}
declare class Login extends React.Component<LoginProps, any> {
public static Tab: typeof LoginTab;
public static UserName: typeof LoginItem;
public static Password: typeof LoginItem;
public static Mobile: typeof LoginItem;
public static Captcha: typeof LoginItem;
public static Submit: typeof Button;
public static UserName: React.FC<LoginItemProps>;
public static Password: React.FC<LoginItemProps>;
public static Mobile: React.FC<LoginItemProps>;
public static Captcha: React.FC<LoginItemProps>;
public static Submit: typeof LoginSubmit;
}
export default Login;
import { SkeletonProps } from 'antd/lib/skeleton';
import * as React from 'react';
import React from 'react';
export interface INoticeIconData {
export interface NoticeIconData {
avatar?: string | React.ReactNode;
title?: React.ReactNode;
description?: React.ReactNode;
......@@ -10,19 +10,21 @@ export interface INoticeIconData {
style?: React.CSSProperties;
}
export interface INoticeIconTabProps {
export interface NoticeIconTabProps {
count?: number;
emptyText?: React.ReactNode;
emptyImage?: string;
list?: INoticeIconData[];
loadedAll?: boolean;
loading?: boolean;
list?: NoticeIconData[];
name?: string;
showClear?: boolean;
skeletonCount?: number;
skeletonProps?: SkeletonProps;
showViewMore?: boolean;
style?: React.CSSProperties;
title?: string;
data?: any[];
onClick: (item: any) => void;
onClear: (item: any) => void;
locale: any;
onViewMore: (e: any) => void;
}
export default class NoticeIconTab extends React.Component<INoticeIconTabProps, any> {}
export default class NoticeIconTab extends React.Component<NoticeIconTabProps, any> {}
import React from 'react';
import { Avatar, List, Skeleton } from 'antd';
import { Avatar, List } from 'antd';
import classNames from 'classnames';
import styles from './NoticeList.less';
let ListElement = null;
export default function NoticeList({
data = [],
onClick,
......@@ -13,14 +11,9 @@ export default function NoticeList({
locale,
emptyText,
emptyImage,
loading,
onLoadMore,
visible,
loadedAll = true,
scrollToLoad = true,
onViewMore = null,
showClear = true,
skeletonCount = 5,
skeletonProps = {},
showViewMore = false,
}) {
if (data.length === 0) {
return (
......@@ -30,36 +23,10 @@ export default function NoticeList({
</div>
);
}
const loadingList = Array.from({ length: loading ? skeletonCount : 0 }).map(() => ({ loading }));
const LoadMore = loadedAll ? (
<div className={classNames(styles.loadMore, styles.loadedAll)}>
<span>{locale.loadedAll}</span>
</div>
) : (
<div className={styles.loadMore} onClick={onLoadMore}>
<span>{locale.loadMore}</span>
</div>
);
const onScroll = event => {
if (!scrollToLoad || loading || loadedAll) return;
if (typeof onLoadMore !== 'function') return;
const { currentTarget: t } = event;
if (t.scrollHeight - t.scrollTop - t.clientHeight <= 40) {
onLoadMore(event);
ListElement = t;
}
};
if (!visible && ListElement) {
try {
ListElement.scrollTo(null, 0);
} catch (err) {
ListElement = null;
}
}
return (
<div>
<List className={styles.list} loadMore={LoadMore} onScroll={onScroll}>
{[...data, ...loadingList].map((item, i) => {
<List className={styles.list}>
{data.map((item, i) => {
const itemCls = classNames(styles.item, {
[styles.read]: item.read,
});
......@@ -74,7 +41,6 @@ export default function NoticeList({
return (
<List.Item className={itemCls} key={item.key || i} onClick={() => onClick(item)}>
<Skeleton avatar title={false} active {...skeletonProps} loading={item.loading}>
<List.Item.Meta
className={styles.meta}
avatar={leftIcon}
......@@ -93,16 +59,18 @@ export default function NoticeList({
</div>
}
/>
</Skeleton>
</List.Item>
);
})}
</List>
<div className={styles.bottomBar}>
{showClear ? (
<div className={styles.clear} onClick={onClear}>
{locale.clear} {title}
<div onClick={onClear}>
{locale.clear} {locale[title] || title}
</div>
) : null}
{showViewMore ? <div onClick={onViewMore}>{locale.viewMore}</div> : null}
</div>
</div>
);
}
......@@ -78,17 +78,28 @@
}
}
.clear {
.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;
&:hover {
color: @heading-color;
}
&:only-child {
width: 100%;
}
&:not(:only-child):last-child {
border-left: 1px solid @border-color-split;
}
}
}
......@@ -5,10 +5,8 @@ title: 带浮层卡片
点击展开通知卡片,展现多种类型的通知,通常放在导航工具栏。
````jsx
```jsx
import NoticeIcon from 'ant-design-pro/lib/NoticeIcon';
import moment from 'moment';
import groupBy from 'lodash/groupBy';
import { Tag } from 'antd';
const data = [{
......@@ -16,39 +14,39 @@ const data = [{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: '通知',
type: 'notification',
}, {
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: '通知',
type: 'notification',
}, {
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: '通知',
type: 'notification',
}, {
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: '通知',
type: 'notification',
}, {
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: '通知',
type: 'notification',
}, {
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: '消息',
type: 'message',
clickClose: true,
}, {
id: '000000007',
......@@ -56,7 +54,7 @@ const data = [{
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: '消息',
type: 'message',
clickClose: true,
}, {
id: '000000008',
......@@ -64,7 +62,7 @@ const data = [{
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: '消息',
type: 'message',
clickClose: true,
}, {
id: '000000009',
......@@ -72,28 +70,28 @@ const data = [{
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: '待办',
type: 'event',
}, {
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: '待办',
type: 'event',
}, {
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: '待办',
type: 'event',
}, {
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: '待办',
type: 'event',
}];
function onItemClick(item, tabProps) {
......@@ -108,32 +106,38 @@ function getNoticeData(notices) {
if (notices.length === 0) {
return {};
}
const newNotices = notices.map((notice) => {
const newNotices = notices.map(notice => {
const newNotice = { ...notice };
if (newNotice.datetime) {
newNotice.datetime = moment(notice.datetime).fromNow();
}
// transform id to item key
if (newNotice.id) {
newNotice.key = newNotice.id;
}
if (newNotice.extra && newNotice.status) {
const color = ({
const color = {
todo: '',
processing: 'blue',
urgent: 'red',
doing: 'gold',
})[newNotice.status];
newNotice.extra = <Tag color={color} style={{ marginRight: 0 }}>{newNotice.extra}</Tag>;
}[newNotice.status];
newNotice.extra = (
<Tag color={color} style={{ marginRight: 0 }}>
{newNotice.extra}
</Tag>
);
}
return newNotice;
});
return groupBy(newNotices, 'type');
return newNotices.reduce((pre, data) => {
if (!pre[data.type]) {
pre[data.type] = [];
}
pre[data.type].push(data);
return pre;
}, {});
}
const noticeData = getNoticeData(data);
ReactDOM.render(
const Demo = () => (
<div
style={{
textAlign: 'right',
......@@ -144,38 +148,28 @@ ReactDOM.render(
width: '400px',
}}
>
<NoticeIcon
className="notice-icon"
count={5}
onItemClick={onItemClick}
onClear={onClear}
>
<NoticeIcon className="notice-icon" count={5} onItemClick={onItemClick} onClear={onClear}>
<NoticeIcon.Tab
list={noticeData['通知']}
name="通知"
title="通知"
list={noticeData.notification}
title="notification"
emptyText="你已查看所有通知"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
/>
<NoticeIcon.Tab
list={noticeData['消息']}
name="消息"
title="消息"
list={noticeData.message}
title="message"
emptyText="您已读完所有消息"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
/>
<NoticeIcon.Tab
list={noticeData['待办']}
name="待办"
title="待办"
list={noticeData.event}
title="event"
emptyText="你已完成所有待办"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
/>
</NoticeIcon>
</div>
, mountNode);
````
```css
);
ReactDOM.render(<Demo />, mountNode);
```
import * as React from 'react';
import NoticeIconTab, { INoticeIconData } from './NoticeIconTab';
import React from 'react';
import NoticeIconTab, { NoticeIconData } from './NoticeIconTab';
export interface INoticeIconProps {
export interface NoticeIconProps {
count?: number;
bell?: React.ReactNode;
className?: string;
loading?: boolean;
onClear?: (tabName: string) => void;
onItemClick?: (item: INoticeIconData, tabProps: INoticeIconProps) => void;
onLoadMore?: (tabProps: INoticeIconProps) => void;
onItemClick?: (item: NoticeIconData, tabProps: NoticeIconProps) => void;
onViewMore?: (tabProps: NoticeIconProps, e: MouseEvent) => void;
onTabChange?: (tabTile: string) => void;
style?: React.CSSProperties;
onPopupVisibleChange?: (visible: boolean) => void;
......@@ -16,12 +16,12 @@ export interface INoticeIconProps {
locale?: {
emptyText: string;
clear: string;
loadedAll: string;
loadMore: string;
viewMore: string;
[key: string]: string;
};
clearClose?: boolean;
}
export default class NoticeIcon extends React.Component<INoticeIconProps, any> {
export default class NoticeIcon extends React.Component<NoticeIconProps, any> {
public static Tab: typeof NoticeIconTab;
}
......@@ -5,7 +5,7 @@ cols: 1
order: 9
---
用在导航工具栏上,作为整个产品统一的通知中心。
Used in navigation toolbar as a unified notification center for the entire product.
## API
......@@ -16,11 +16,11 @@ bell | Change the bell Icon | ReactNode | `<Icon type='bell' />`
loading | Popup card loading status | boolean | `false`
onClear | Click to clear button the callback | function(tabName) | -
onItemClick | Click on the list item's callback | function(item, tabProps) | -
onLoadMore | Callback of click for loading more | function(tabProps, event) | -
onPopupVisibleChange | Popup Card Showing or Hiding Callbacks | function(visible) | -
onTabChange | Switching callbacks for tabs | function(tabTitle) | -
onViewMore | Callback of click for view more | function(tabProps, event) | -
popupVisible | Popup card display state | boolean | -
locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
clearClose | Close menu after clear | boolean | `false`
### NoticeIcon.Tab
......@@ -31,14 +31,9 @@ count | Unread messages count of this tab | number | list.length
emptyText | Message text when list is empty | ReactNode | -
emptyImage | Image when list is empty | string | -
list | List data, format refer to the following table | Array | `[]`
loadedAll | All messages have been loaded | boolean | `true`
loading | Loading status of this tab | boolean | `false`
name | identifier for message Tab | string | -
scrollToLoad | Scroll to load | boolean | `true`
skeletonCount | Number of skeleton when tab is loading | number | `5`
skeletonProps | Props of skeleton | SkeletonProps | `{}`
showClear | Clear button display status | boolean | `true`
title | header for message Tab | string | -
showViewMore | View more button display status | boolean | `false`
title | header for message Tab, the actual text is `locale[title] || title` | string | -
### Tab data
......
......@@ -16,13 +16,13 @@ export default class NoticeIcon extends PureComponent {
onPopupVisibleChange: () => {},
onTabChange: () => {},
onClear: () => {},
onViewMore: () => {},
loading: false,
clearClose: false,
locale: {
emptyText: 'No notifications',
clear: 'Clear',
loadedAll: 'Loaded',
loadMore: 'Loading more',
viewMore: 'More',
},
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
};
......@@ -53,53 +53,35 @@ export default class NoticeIcon extends PureComponent {
onTabChange(tabType);
};
onLoadMore = (tabProps, event) => {
const { onLoadMore } = this.props;
onLoadMore(tabProps, event);
onViewMore = (tabProps, event) => {
const { onViewMore } = this.props;
onViewMore(tabProps, event);
};
getNotificationBox() {
const { visible } = this.state;
const { children, loading, locale } = this.props;
if (!children) {
return null;
}
const panes = React.Children.map(children, child => {
const {
list,
title,
name,
count,
emptyText,
emptyImage,
showClear,
loadedAll,
scrollToLoad,
skeletonCount,
skeletonProps,
loading: tabLoading,
} = child.props;
const { list, title, count, emptyText, emptyImage, showClear, showViewMore } = child.props;
const len = list && list.length ? list.length : 0;
const msgCount = count || count === 0 ? count : len;
const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
const localeTitle = locale[title] || title;
const tabTitle = msgCount > 0 ? `${localeTitle} (${msgCount})` : localeTitle;
return (
<TabPane tab={tabTitle} key={name}>
<TabPane tab={tabTitle} key={title}>
<List
data={list}
emptyImage={emptyImage}
emptyText={emptyText}
loadedAll={loadedAll}
loading={tabLoading}
locale={locale}
onClear={() => this.onClear(name)}
onClear={() => this.onClear(title)}
onClick={item => this.onItemClick(item, child.props)}
onLoadMore={event => this.onLoadMore(child.props, event)}
scrollToLoad={scrollToLoad}
onViewMore={event => this.onViewMore(child.props, event)}
showClear={showClear}
skeletonCount={skeletonCount}
skeletonProps={skeletonProps}
showViewMore={showViewMore}
title={title}
visible={visible}
/>
</TabPane>
);
......
......@@ -16,11 +16,11 @@ bell | translate this please -> Change the bell Icon | ReactNode | `<Icon type='
loading | 弹出卡片加载状态 | boolean | `false`
onClear | 点击清空按钮的回调 | function(tabName) | -
onItemClick | 点击列表项的回调 | function(item, tabProps) | -
onLoadMore | 加载更多的回调 | function(tabProps, event) | -
onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
onTabChange | 切换页签的回调 | function(tabTitle) | -
onViewMore | 点击查看更多的回调 | function(tabProps, event) | -
popupVisible | 控制弹层显隐 | boolean | -
locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
clearClose | 点击清空按钮后关闭通知菜单 | boolean | `false`
### NoticeIcon.Tab
......@@ -31,14 +31,9 @@ count | 当前 Tab 未读消息数量 | number | list.length
emptyText | 针对每个 Tab 定制空数据文案 | ReactNode | -
emptyImage | 针对每个 Tab 定制空数据图片 | string | -
list | 列表数据,格式参照下表 | Array | `[]`
loadedAll | 已加载完所有消息 | boolean | `true`
loading | 当前 Tab 的加载状态 | boolean | `false`
name | 消息分类的标识符 | string | -
scrollToLoad | 允许滚动自加载 | boolean | `true`
skeletonCount | 加载时占位骨架的数量 | number | `5`
skeletonProps | 加载时占位骨架的属性 | SkeletonProps | `{}`
showClear | 是否显示清空按钮 | boolean | `true`
title | 消息分类的页签标题 | string | -
showViewMore | 是否显示查看更多按钮 | boolean | `false`
title | 消息分类的页签标题,实际的文案是 `locale[title] || title` | string | -
### Tab data
......
import * as React from 'react';
export interface INumberInfoProps {
import React from 'react';
export interface NumberInfoProps {
title?: React.ReactNode | string;
subTitle?: React.ReactNode | string;
total?: React.ReactNode | string;
......@@ -7,7 +7,8 @@ export interface INumberInfoProps {
theme?: string;
gap?: number;
subTotal?: number;
suffix?: string;
style?: React.CSSProperties;
}
export default class NumberInfo extends React.Component<INumberInfoProps, any> {}
export default class NumberInfo extends React.Component<NumberInfoProps, any> {}
import * as React from 'react';
import { IPageHeaderProps } from './index';
export default class BreadcrumbView extends React.Component<IPageHeaderProps, any> {}
export function getBreadcrumb(breadcrumbNameMap: object, url: string): object;
import React, { PureComponent, createElement } from 'react';
import pathToRegexp from 'path-to-regexp';
import { Breadcrumb } from 'antd';
import styles from './index.less';
import { urlToList } from '../_utils/pathTools';
export const getBreadcrumb = (breadcrumbNameMap, url) => {
let breadcrumb = breadcrumbNameMap[url];
if (!breadcrumb) {
Object.keys(breadcrumbNameMap).forEach(item => {
if (pathToRegexp(item).test(url)) {
breadcrumb = breadcrumbNameMap[item];
}
});
}
return breadcrumb || {};
};
export default class BreadcrumbView extends PureComponent {
state = {
breadcrumb: null,
};
componentDidMount() {
this.getBreadcrumbDom();
}
componentDidUpdate(preProps) {
const { location } = this.props;
if (!location || !preProps.location) {
return;
}
const prePathname = preProps.location.pathname;
if (prePathname !== location.pathname) {
this.getBreadcrumbDom();
}
}
getBreadcrumbDom = () => {
const breadcrumb = this.conversionBreadcrumbList();
this.setState({
breadcrumb,
});
};
getBreadcrumbProps = () => {
const { routes, params, location, breadcrumbNameMap } = this.props;
return {
routes,
params,
routerLocation: location,
breadcrumbNameMap,
};
};
// Generated according to props
conversionFromProps = () => {
const { breadcrumbList, breadcrumbSeparator, itemRender, linkElement = 'a' } = this.props;
return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
{breadcrumbList.map(item => {
const title = itemRender ? itemRender(item) : item.title;
return (
<Breadcrumb.Item key={item.title}>
{item.href
? createElement(
linkElement,
{
[linkElement === 'a' ? 'href' : 'to']: item.href,
},
title
)
: title}
</Breadcrumb.Item>
);
})}
</Breadcrumb>
);
};
conversionFromLocation = (routerLocation, breadcrumbNameMap) => {
const { breadcrumbSeparator, home, itemRender, linkElement = 'a' } = this.props;
// Convert the url to an array
const pathSnippets = urlToList(routerLocation.pathname);
// Loop data mosaic routing
const extraBreadcrumbItems = pathSnippets.map((url, index) => {
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
if (currentBreadcrumb.inherited) {
return null;
}
const isLinkable = index !== pathSnippets.length - 1 && currentBreadcrumb.component;
const name = itemRender ? itemRender(currentBreadcrumb) : currentBreadcrumb.name;
return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}>
{createElement(
isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url },
name
)}
</Breadcrumb.Item>
) : null;
});
// Add home breadcrumbs to your head if defined
if (home) {
extraBreadcrumbItems.unshift(
<Breadcrumb.Item key="home">
{createElement(
linkElement,
{
[linkElement === 'a' ? 'href' : 'to']: '/',
},
home
)}
</Breadcrumb.Item>
);
}
return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
{extraBreadcrumbItems}
</Breadcrumb>
);
};
/**
* 将参数转化为面包屑
* Convert parameters into breadcrumbs
*/
conversionBreadcrumbList = () => {
const { breadcrumbList, breadcrumbSeparator } = this.props;
const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
if (breadcrumbList && breadcrumbList.length) {
return this.conversionFromProps();
}
// 如果传入 routes 和 params 属性
// If pass routes and params attributes
if (routes && params) {
return (
<Breadcrumb
className={styles.breadcrumb}
routes={routes.filter(route => route.breadcrumbName)}
params={params}
itemRender={this.itemRender}
separator={breadcrumbSeparator}
/>
);
}
// 根据 location 生成 面包屑
// Generate breadcrumbs based on location
if (routerLocation && routerLocation.pathname) {
return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
}
return null;
};
// 渲染Breadcrumb 子节点
// Render the Breadcrumb child node
itemRender = (route, params, routes, paths) => {
const { linkElement = 'a' } = this.props;
const last = routes.indexOf(route) === routes.length - 1;
return last || !route.component ? (
<span>{route.breadcrumbName}</span>
) : (
createElement(
linkElement,
{
href: paths.join('/') || '/',
to: paths.join('/') || '/',
},
route.breadcrumbName
)
);
};
render() {
const { breadcrumb } = this.state;
return breadcrumb;
}
}
---
order: 2
title: With Image
---
带图片的页头。
````jsx
import PageHeader from 'ant-design-pro/lib/PageHeader';
const content = (
<div>
<p>段落示意:蚂蚁金服务设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。</p>
<div className="link">
<a>
<img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/MjEImQtenlyueSmVEfUD.svg" /> 快速开始
</a>
<a>
<img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/NbuDUAuBlIApFuDvWiND.svg" /> 产品简介
</a>
<a>
<img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/ohOEPSYdDTNnyMbGuyLb.svg" /> 产品文档
</a>
</div>
</div>
);
const extra = (
<div className="imgContainer">
<img style={{ width: '100%' }} alt="" src="https://gw.alipayobjects.com/zos/rmsportal/RzwpdLnhmvDJToTdfDPe.png" />
</div>
);
const breadcrumbList = [{
title: '一级菜单',
href: '/',
}, {
title: '二级菜单',
href: '/',
}, {
title: '三级菜单',
}];
ReactDOM.render(
<div>
<PageHeader
title="这是一个标题"
content={content}
extraContent={extra}
breadcrumbList={breadcrumbList}
/>
</div>
, mountNode);
````
<style>
#scaffold-src-components-PageHeader-demo-image .code-box-demo {
background: #f2f4f5;
}
#scaffold-src-components-PageHeader-demo-image .imgContainer {
margin-top: -60px;
text-align: center;
width: 195px;
}
#scaffold-src-components-PageHeader-demo-image .link {
margin-top: 16px;
}
#scaffold-src-components-PageHeader-demo-image .link a {
margin-right: 32px;
}
#scaffold-src-components-PageHeader-demo-image .link img {
vertical-align: middle;
margin-right: 8px;
}
</style>
---
order: 3
title: Simple
---
简单的页头。
````jsx
import PageHeader from 'ant-design-pro/lib/PageHeader';
const breadcrumbList = [{
title: '一级菜单',
href: '/',
}, {
title: '二级菜单',
href: '/',
}, {
title: '三级菜单',
}];
ReactDOM.render(
<div>
<PageHeader title="页面标题" breadcrumbList={breadcrumbList} />
</div>
, mountNode);
````
<style>
#scaffold-src-components-PageHeader-demo-simple .code-box-demo {
background: #f2f4f5;
}
</style>
---
order: 1
title: Standard
---
标准页头。
````jsx
import PageHeader from 'ant-design-pro/lib/PageHeader';
import DescriptionList from 'ant-design-pro/lib/DescriptionList';
import { Button, Menu, Dropdown, Icon, Row, Col } from 'antd';
const { Description } = DescriptionList;
const ButtonGroup = Button.Group;
const description = (
<DescriptionList size="small" col="2">
<Description term="创建人">曲丽丽</Description>
<Description term="订购产品">XX 服务</Description>
<Description term="创建时间">2017-07-07</Description>
<Description term="关联单据"><a href="">12421</a></Description>
</DescriptionList>
);
const menu = (
<Menu>
<Menu.Item key="1">选项一</Menu.Item>
<Menu.Item key="2">选项二</Menu.Item>
<Menu.Item key="3">选项三</Menu.Item>
</Menu>
);
const action = (
<div>
<ButtonGroup>
<Button>操作</Button>
<Button>操作</Button>
<Dropdown overlay={menu} placement="bottomRight">
<Button><Icon type="ellipsis" /></Button>
</Dropdown>
</ButtonGroup>
<Button type="primary">主操作</Button>
</div>
);
const extra = (
<Row>
<Col sm={24} md={12}>
<div style={{ color: 'rgba(0, 0, 0, 0.43)' }}>状态</div>
<div style={{ color: 'rgba(0, 0, 0, 0.85)', fontSize: 20 }}>待审批</div>
</Col>
<Col sm={24} md={12}>
<div style={{ color: 'rgba(0, 0, 0, 0.43)' }}>订单金额</div>
<div style={{ color: 'rgba(0, 0, 0, 0.85)', fontSize: 20 }}>¥ 568.08</div>
</Col>
</Row>
);
const breadcrumbList = [{
title: '一级菜单',
href: '/',
}, {
title: '二级菜单',
href: '/',
}, {
title: '三级菜单',
}];
const tabList = [{
key: 'detail',
tab: '详情',
}, {
key: 'rule',
tab: '规则',
}];
function onTabChange(key) {
console.log(key);
}
ReactDOM.render(
<div>
<PageHeader
title="单号:234231029431"
logo={<img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png" />}
action={action}
content={description}
extraContent={extra}
breadcrumbList={breadcrumbList}
tabList={tabList}
tabActiveKey="detail"
onTabChange={onTabChange}
/>
</div>
, mountNode);
````
<style>
#scaffold-src-components-PageHeader-demo-standard .code-box-demo {
background: #f2f4f5;
}
</style>
---
order: 0
title: Structure
---
基本结构,具备响应式布局功能,主要断点为 768px 和 576px,拖动窗口改变大小试试看。
````jsx
import PageHeader from 'ant-design-pro/lib/PageHeader';
const breadcrumbList = [{
title: '面包屑',
}];
const tabList = [{
key: '1',
tab: '页签一',
}, {
key: '2',
tab: '页签二',
}, {
key: '3',
tab: '页签三',
}];
ReactDOM.render(
<div>
<PageHeader
className="tabs"
title={<div className="title">Title</div>}
logo={<div className="logo">logo</div>}
action={<div className="action">action</div>}
content={<div className="content">content</div>}
extraContent={<div className="extraContent">extraContent</div>}
breadcrumbList={breadcrumbList}
tabList={tabList}
tabActiveKey="1"
/>
</div>
, mountNode);
````
<style>
#scaffold-src-components-PageHeader-demo-structure .code-box-demo {
background: #f2f4f5;
}
#scaffold-src-components-PageHeader-demo-structure .logo {
background: #3ba0e9;
color: #fff;
height: 100%;
}
#scaffold-src-components-PageHeader-demo-structure .title {
background: rgba(16, 142, 233, 1);
color: #fff;
}
#scaffold-src-components-PageHeader-demo-structure .action {
background: #7dbcea;
color: #fff;
}
#scaffold-src-components-PageHeader-demo-structure .content {
background: #7dbcea;
color: #fff;
}
#scaffold-src-components-PageHeader-demo-structure .extraContent {
background: #7dbcea;
color: #fff;
}
</style>
import * as React from 'react';
export interface IPageHeaderProps {
title?: React.ReactNode | string;
logo?: React.ReactNode | string;
action?: React.ReactNode | string;
content?: React.ReactNode;
extraContent?: React.ReactNode;
routes?: any[];
params?: any;
breadcrumbList?: Array<{ title: React.ReactNode; href?: string }>;
tabList?: Array<{ key: string; tab: React.ReactNode }>;
tabActiveKey?: string;
tabDefaultActiveKey?: string;
onTabChange?: (key: string) => void;
tabBarExtraContent?: React.ReactNode;
linkElement?: React.ReactNode;
style?: React.CSSProperties;
home?: React.ReactNode;
wide?: boolean;
hiddenBreadcrumb?: boolean;
}
export default class PageHeader extends React.Component<IPageHeaderProps, any> {}
import React, { PureComponent } from 'react';
import { Tabs, Skeleton } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
import BreadcrumbView from './breadcrumb';
const { TabPane } = Tabs;
export default class PageHeader extends PureComponent {
onChange = key => {
const { onTabChange } = this.props;
if (onTabChange) {
onTabChange(key);
}
};
render() {
const {
title = '',
logo,
action,
content,
extraContent,
tabList,
className,
tabActiveKey,
tabDefaultActiveKey,
tabBarExtraContent,
loading = false,
wide = false,
hiddenBreadcrumb = false,
} = this.props;
const clsString = classNames(styles.pageHeader, className);
const activeKeyProps = {};
if (tabDefaultActiveKey !== undefined) {
activeKeyProps.defaultActiveKey = tabDefaultActiveKey;
}
if (tabActiveKey !== undefined) {
activeKeyProps.activeKey = tabActiveKey;
}
return (
<div className={clsString}>
<div className={wide ? styles.wide : ''}>
<Skeleton
loading={loading}
title={false}
active
paragraph={{ rows: 3 }}
avatar={{ size: 'large', shape: 'circle' }}
>
{hiddenBreadcrumb ? null : <BreadcrumbView {...this.props} />}
<div className={styles.detail}>
{logo && <div className={styles.logo}>{logo}</div>}
<div className={styles.main}>
<div className={styles.row}>
<h1 className={styles.title}>{title}</h1>
{action && <div className={styles.action}>{action}</div>}
</div>
<div className={styles.row}>
{content && <div className={styles.content}>{content}</div>}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>}
</div>
</div>
</div>
{tabList && tabList.length ? (
<Tabs
className={styles.tabs}
{...activeKeyProps}
onChange={this.onChange}
tabBarExtraContent={tabBarExtraContent}
>
{tabList.map(item => (
<TabPane tab={item.tab} key={item.key} />
))}
</Tabs>
) : null}
</Skeleton>
</div>
</div>
);
}
}
@import '~antd/lib/style/themes/default.less';
.pageHeader {
padding: 16px 32px 0 32px;
background: @component-background;
border-bottom: @border-width-base @border-style-base @border-color-split;
.wide {
max-width: 1200px;
margin: auto;
}
.detail {
display: flex;
}
.row {
display: flex;
width: 100%;
}
.breadcrumb {
margin-bottom: 16px;
}
.tabs {
margin: 0 0 0 -8px;
:global {
// 1px 可以让选中效果显示完成
.ant-tabs-bar {
margin-bottom: 1px;
border-bottom: none;
}
}
}
.logo {
flex: 0 1 auto;
margin-right: 16px;
padding-top: 1px;
> img {
display: block;
width: 28px;
height: 28px;
border-radius: @border-radius-base;
}
}
.title {
color: @heading-color;
font-weight: 500;
font-size: 20px;
}
.action {
min-width: 266px;
margin-left: 56px;
:global {
.ant-btn-group:not(:last-child),
.ant-btn:not(:last-child) {
margin-right: 8px;
}
.ant-btn-group > .ant-btn {
margin-right: 0;
}
}
}
.title,
.content {
flex: auto;
}
.action,
.extraContent,
.main {
flex: 0 1 auto;
}
.main {
width: 100%;
}
.title,
.action {
margin-bottom: 16px;
}
.logo,
.content,
.extraContent {
margin-bottom: 16px;
}
.action,
.extraContent {
text-align: right;
}
.extraContent {
min-width: 242px;
margin-left: 88px;
}
}
@media screen and (max-width: @screen-xl) {
.pageHeader {
.extraContent {
margin-left: 44px;
}
}
}
@media screen and (max-width: @screen-lg) {
.pageHeader {
.extraContent {
margin-left: 20px;
}
}
}
@media screen and (max-width: @screen-md) {
.pageHeader {
.row {
display: block;
}
.action,
.extraContent {
margin-left: 0;
text-align: left;
}
}
}
@media screen and (max-width: @screen-sm) {
.pageHeader {
.detail {
display: block;
}
}
}
@media screen and (max-width: @screen-xs) {
.pageHeader {
.action {
:global {
.ant-btn-group,
.ant-btn {
display: block;
margin-bottom: 8px;
}
.ant-btn-group > .ant-btn {
display: inline-block;
margin-bottom: 0;
}
}
}
}
}
---
title: PageHeader
subtitle: 页头
cols: 1
order: 11
---
页头用来声明页面的主题,包含了用户所关注的最重要的信息,使用户可以快速理解当前页面是什么以及它的功能。
## API
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| title | title 区域 | ReactNode | - |
| logo | logo区域 | ReactNode | - |
| action | 操作区,位于 title 行的行尾 | ReactNode | - |
| home | 默认的主页说明文字 | ReactNode | - |
| content | 内容区 | ReactNode | - |
| extraContent | 额外内容区,位于content的右侧 | ReactNode | - |
| breadcrumbList | 面包屑数据,配置了此属性时 `routes` `params` `location` `breadcrumbNameMap` 无效 | array<{title: ReactNode, href?: string}> | - |
| hiddenBreadcrumb |隐藏面包屑 | boolean | false |
| routes | 面包屑相关属性,router 的路由栈信息 | object[] | - |
| params | 面包屑相关属性,路由的参数 | object | - |
| location | 面包屑相关属性,当前的路由信息 | object | - |
| breadcrumbNameMap | 面包屑相关属性,路由的地址-名称映射表 | object | - |
| tabList | tab 标题列表 | array<{key: string, tab: ReactNode}> | - |
| tabActiveKey | 当前高亮的 tab 项 | string | - |
| tabDefaultActiveKey | 默认高亮的 tab 项 | string | 第一项 |
| wide | 是否定宽 | boolean | false |
| onTabChange | 切换面板的回调 | (key) => void | - |
| itemRender | 自定义节点方法 | (menuItem) => ReactNode | - |
| linkElement | 定义链接的元素,默认为 `a`,可传入 react-router 的 Link | string\|ReactElement | - |
> 面包屑的配置方式有三种,一是直接配置 `breadcrumbList`,二是结合 `react-router@2` `react-router@3`,配置 `routes` 及 `params` 实现,类似 [面包屑 Demo](https://ant.design/components/breadcrumb-cn/#components-breadcrumb-demo-router),三是结合 `react-router@4`,配置 `location` `breadcrumbNameMap`,优先级依次递减,脚手架中使用最后一种。 对于后两种用法,你也可以将 `routes` `params` 及 `location` `breadcrumbNameMap` 放到 context 中,组件会自动获取。
import { getBreadcrumb } from './breadcrumb';
import { urlToList } from '../_utils/pathTools';
const routerData = {
'/dashboard/analysis': {
name: '分析页',
},
'/userinfo': {
name: '用户列表',
},
'/userinfo/:id': {
name: '用户信息',
},
'/userinfo/:id/addr': {
name: '收货订单',
},
};
describe('test getBreadcrumb', () => {
it('Simple url', () => {
expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual('分析页');
});
it('Parameters url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual('用户信息');
});
it('The middle parameter url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual('收货订单');
});
it('Loop through the parameters', () => {
const urlNameList = urlToList('/userinfo/2144/addr').map(
url => getBreadcrumb(routerData, url).name
);
expect(urlNameList).toEqual(['用户列表', '用户信息', '收货订单']);
});
it('a path', () => {
const urlNameList = urlToList('/userinfo').map(url => getBreadcrumb(routerData, url).name);
expect(urlNameList).toEqual(['用户列表']);
});
it('Secondary path', () => {
const urlNameList = urlToList('/userinfo/2144').map(url => getBreadcrumb(routerData, url).name);
expect(urlNameList).toEqual(['用户列表', '用户信息']);
});
});
import React from 'react';
export interface GridContentProps {
contentWidth: string;
children: React.ReactNode;
}
export default class GridContent extends React.Component<GridContentProps, any> {}
import React from 'react';
import pathToRegexp from 'path-to-regexp';
import Link from 'umi/link';
import { FormattedMessage } from 'umi-plugin-react/locale';
import { urlToList } from '../_utils/pathTools';
// 渲染Breadcrumb 子节点
// Render the Breadcrumb child node
const itemRender = (route, params, routes, paths) => {
const last = routes.indexOf(route) === routes.length - 1;
return last || !route.component ? (
<span>{route.breadcrumbName}</span>
) : (
<Link to={paths.join('/')}>{route.breadcrumbName}</Link>
);
};
const renderItemLocal = item => {
if (item.locale) {
return <FormattedMessage id={item.locale} defaultMessage={item.name} />;
}
return item.name;
};
export const getBreadcrumb = (breadcrumbNameMap, url) => {
let breadcrumb = breadcrumbNameMap[url];
if (!breadcrumb) {
Object.keys(breadcrumbNameMap).forEach(item => {
if (pathToRegexp(item).test(url)) {
breadcrumb = breadcrumbNameMap[item];
}
});
}
return breadcrumb || {};
};
export const getBreadcrumbProps = props => {
const { routes, params, location, breadcrumbNameMap } = props;
return {
routes,
params,
routerLocation: location,
breadcrumbNameMap,
};
};
// Generated according to props
const conversionFromProps = props => {
const { breadcrumbList } = props;
return breadcrumbList.map(item => {
const { title, href } = item;
return {
path: href,
breadcrumbName: title,
};
});
};
const conversionFromLocation = (routerLocation, breadcrumbNameMap, props) => {
const { home } = props;
// Convert the url to an array
const pathSnippets = urlToList(routerLocation.pathname);
// Loop data mosaic routing
const extraBreadcrumbItems = pathSnippets.map(url => {
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
if (currentBreadcrumb.inherited) {
return null;
}
const name = renderItemLocal(currentBreadcrumb);
const { hideInBreadcrumb } = currentBreadcrumb;
return name && !hideInBreadcrumb
? {
path: url,
breadcrumbName: name,
}
: null;
});
// Add home breadcrumbs to your head if defined
if (home) {
extraBreadcrumbItems.unshift({
path: '/',
breadcrumbName: home,
});
}
return extraBreadcrumbItems;
};
/**
* 将参数转化为面包屑
* Convert parameters into breadcrumbs
*/
export const conversionBreadcrumbList = props => {
const { breadcrumbList } = props;
const { routes, params, routerLocation, breadcrumbNameMap } = getBreadcrumbProps(props);
if (breadcrumbList && breadcrumbList.length) {
return conversionFromProps();
}
// 如果传入 routes 和 params 属性
// If pass routes and params attributes
if (routes && params) {
return {
routes: routes.filter(route => route.breadcrumbName),
params,
itemRender,
};
}
// 根据 location 生成 面包屑
// Generate breadcrumbs based on location
if (routerLocation && routerLocation.pathname) {
return {
routes: conversionFromLocation(routerLocation, breadcrumbNameMap, props),
itemRender,
};
}
return {};
};
import React from 'react';
export interface ResultProps {
actions?: React.ReactNode;
className?: string;
description?: React.ReactNode;
extra?: React.ReactNode;
style?: React.CSSProperties;
title?: React.ReactNode;
type: 'success' | 'error';
}
export default class Result extends React.Component<ResultProps, any> {}
import React from 'react';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import PageHeader from '@/components/PageHeader';
import { PageHeader, Tabs, Typography } from 'antd';
import { connect } from 'dva';
import classNames from 'classnames';
import GridContent from './GridContent';
import styles from './index.less';
import MenuContext from '@/layouts/MenuContext';
import { conversionBreadcrumbList } from './breadcrumb';
const PageHeaderWrapper = ({ children, contentWidth, wrapperClassName, top, ...restProps }) => (
<div style={{ margin: '-24px -24px 0' }} className={wrapperClassName}>
const { Title } = Typography;
/**
* render Footer tabList
* In order to be compatible with the old version of the PageHeader
* basically all the functions are implemented.
*/
const renderFooter = ({ tabList, activeKeyProps, onTabChange, tabBarExtraContent }) => {
return tabList && tabList.length ? (
<Tabs
className={styles.tabs}
{...activeKeyProps}
onChange={key => {
if (onTabChange) {
onTabChange(key);
}
}}
tabBarExtraContent={tabBarExtraContent}
>
{tabList.map(item => (
<Tabs.TabPane tab={item.tab} key={item.key} />
))}
</Tabs>
) : null;
};
const PageHeaderWrapper = ({
children,
contentWidth,
wrapperClassName,
top,
title,
content,
logo,
extraContent,
...restProps
}) => {
return (
<div style={{ margin: '-24px -24px 0' }} className={classNames(classNames, styles.main)}>
{top}
{title && content && (
<MenuContext.Consumer>
{value => (
{value => {
return (
<PageHeader
wide={contentWidth === 'Fixed'}
home={<FormattedMessage id="menu.home" defaultMessage="Home" />}
{...value}
title={
<Title
level={4}
style={{
marginBottom: 0,
}}
>
{title}
</Title>
}
key="pageheader"
{...restProps}
breadcrumb={conversionBreadcrumbList({
...value,
...restProps,
home: <FormattedMessage id="menu.home" defaultMessage="Home" />,
})}
className={styles.pageHeader}
linkElement={Link}
itemRender={item => {
if (item.locale) {
return <FormattedMessage id={item.locale} defaultMessage={item.title} />;
}
return item.title;
footer={renderFooter(restProps)}
>
<div className={styles.detail}>
{logo && <div className={styles.logo}>{logo}</div>}
<div className={styles.main}>
<div className={styles.row}>
{content && <div className={styles.content}>{content}</div>}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>}
</div>
</div>
</div>
</PageHeader>
);
}}
/>
)}
</MenuContext.Consumer>
)}
{children ? (
<div className={styles.content}>
<div className={styles['children-content']}>
<GridContent>{children}</GridContent>
</div>
) : null}
</div>
);
);
};
export default connect(({ setting }) => ({
contentWidth: setting.contentWidth,
......
@import '~antd/lib/style/themes/default.less';
.content {
.children-content {
margin: 24px 24px 0;
}
@media screen and (max-width: @screen-sm) {
.main {
:global {
.ant-page-header {
padding: 16px 32px 0;
background: #fff;
border-bottom: 1px solid #e8e8e8;
}
}
.wide {
max-width: 1200px;
margin: auto;
}
.detail {
display: flex;
}
.row {
display: flex;
width: 100%;
}
.logo {
flex: 0 1 auto;
margin-right: 16px;
padding-top: 1px;
> img {
display: block;
width: 28px;
height: 28px;
border-radius: @border-radius-base;
}
}
.title-content {
margin-bottom: 16px;
}
@media screen and (max-width: @screen-sm) {
.content {
margin: 24px 0 0;
}
}
.title,
.content {
flex: auto;
}
.extraContent,
.main {
flex: 0 1 auto;
}
.main {
width: 100%;
}
.title {
margin-bottom: 16px;
}
.logo,
.content,
.extraContent {
margin-bottom: 16px;
}
.extraContent {
min-width: 242px;
margin-left: 88px;
text-align: right;
}
}
@media screen and (max-width: @screen-xl) {
.extraContent {
margin-left: 44px;
}
}
@media screen and (max-width: @screen-lg) {
.extraContent {
margin-left: 20px;
}
}
@media screen and (max-width: @screen-md) {
.row {
display: block;
}
.action,
.extraContent {
margin-left: 0;
text-align: left;
}
}
@media screen and (max-width: @screen-sm) {
.detail {
display: block;
}
}
import * as React from 'react';
export interface IResultProps {
type: 'success' | 'error';
title: React.ReactNode;
import React from 'react';
export interface ResultProps {
actions?: React.ReactNode;
className?: string;
description?: React.ReactNode;
extra?: React.ReactNode;
actions?: React.ReactNode;
style?: React.CSSProperties;
title?: React.ReactNode;
type: 'success' | 'error';
}
export default class Result extends React.Component<IResultProps, any> {}
export default class Result extends React.Component<ResultProps, any> {}
import React from 'react';
export interface SelectLangProps {
className?: string;
}
export default class SelectLang extends React.Component<SelectLangProps, any> {}
import React, { PureComponent } from 'react';
import { formatMessage, setLocale, getLocale } from 'umi/locale';
import { formatMessage, setLocale, getLocale } from 'umi-plugin-react/locale';
import { Menu, Icon } from 'antd';
import classNames from 'classnames';
import HeaderDropdown from '../HeaderDropdown';
......
import React from 'react';
export interface BlockChecboxProps {
value: string;
onChange: (key: string) => void;
list: any[];
}
export default class BlockChecbox extends React.Component<BlockChecboxProps, any> {}
import React from 'react';
export interface TagProps {
color: string;
check: boolean;
className?: string;
onClick?: () => void;
}
export default class ThemeColor extends React.Component<TagProps, any> {}
import React from 'react';
import { Tooltip, Icon } from 'antd';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import styles from './ThemeColor.less';
const Tag = ({ color, check, ...rest }) => (
......
import React from 'react';
import { SiderTheme } from 'antd/es/Layout/Sider';
export interface SettingModelState {
navTheme: string | SiderTheme;
primaryColor: string;
layout: string;
contentWidth: string;
fixedHeader: boolean;
autoHideHeader: boolean;
fixSiderbar: boolean;
menu: { disableLocal: boolean };
title: string;
pwa: boolean;
iconfontUrl: string;
colorWeak: boolean;
}
export interface SettingDrawerProps {
setting?: SettingModelState;
dispatch?: (args: any) => void;
}
export default class SettingDrawer extends React.Component<SettingDrawerProps, any> {}
import React, { PureComponent } from 'react';
import React, { Component } from 'react';
import { Select, message, Drawer, List, Switch, Divider, Icon, Button, Alert, Tooltip } from 'antd';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { connect } from 'dva';
import omit from 'omit.js';
......@@ -23,7 +23,7 @@ const Body = ({ children, title, style }) => (
);
@connect(({ setting }) => ({ setting }))
class SettingDrawer extends PureComponent {
class SettingDrawer extends Component {
state = {
collapse: false,
};
......@@ -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,
}}
......@@ -208,17 +207,22 @@ class SettingDrawer extends PureComponent {
<Divider />
<Body title={formatMessage({ id: 'app.setting.othersettings' })}>
<List.Item
actions={[
<List
split={false}
renderItem={this.renderLayoutSettingItem}
dataSource={[
{
title: formatMessage({ id: 'app.setting.weakmode' }),
action: (
<Switch
size="small"
checked={!!colorWeak}
onChange={checked => this.changeSetting('colorWeak', checked)}
/>,
/>
),
},
]}
>
{formatMessage({ id: 'app.setting.weakmode' })}
</List.Item>
/>
</Body>
<Divider />
<CopyToClipboard
......
......@@ -4,6 +4,13 @@
position: relative;
min-height: 100%;
background: #fff;
:global {
.ant-list-item {
span {
flex: 1;
}
}
}
}
.blockChecbox {
......
import React from 'react';
import * as H from 'history';
import { SiderTheme, CollapseType } from 'antd/es/Layout/Sider';
import { MenuMode } from 'antd/es/menu';
export interface BaseMenuProps {
flatMenuKeys?: any[];
location?: H.Location;
onCollapse?: (collapsed: boolean, type?: CollapseType) => void;
isMobile?: boolean;
openKeys?: any;
theme?: SiderTheme;
mode?: MenuMode;
className?: string;
collapsed?: boolean;
handleOpenChange?: (openKeys: any[]) => void;
menuData?: any[];
style?: React.CSSProperties;
onOpenChange?: (openKeys: string[]) => void;
}
export default class BaseMenu extends React.Component<BaseMenuProps, any> {}
......@@ -33,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);
};
......
import React from 'react';
import * as H from 'history';
import { BaseMenuProps } from './BaseMenu';
import { SiderTheme } from 'antd/es/Layout/Sider';
export interface SiderMenuProps extends BaseMenuProps {
logo?: string;
fixSiderbar?: boolean;
}
export default class SiderMenu extends React.Component<SiderMenuProps, any> {}
import React from 'react';
import { SiderTheme } from 'antd/es/Layout/Sider';
export interface SiderMenuProps {
isMobile: boolean;
menuData: any[];
collapsed: boolean;
logo?: string;
theme?: SiderTheme;
onCollapse: (payload: boolean) => void;
}
export default class SiderMenuWrapper extends React.Component<SiderMenuProps, any> {}
import React from 'react';
export interface StandardFormRowProps {
title: string;
last?: boolean;
block?: boolean;
grid?: boolean;
style?: React.CSSProperties;
}
export default class StandardFormRow extends React.Component<StandardFormRowProps, any> {}
import React from 'react';
import { PaginationConfig, SorterResult, TableCurrentDataSource } from 'antd/lib/table';
export interface StandardTableProps {
columns: any;
onSelectRow: (row: any) => void;
data: any;
rowKey?: string;
selectedRows: any[];
onChange?: (
pagination: PaginationConfig,
filters: Record<keyof any, string[]>,
sorter: SorterResult<any>,
extra?: TableCurrentDataSource<any>
) => void;
loading?: boolean;
}
export default class StandardTable extends React.Component<StandardTableProps, any> {}
import * as React from 'react';
import React from 'react';
export interface ITagSelectOptionProps {
value: string | number;
export interface TagSelectOptionProps {
value?: string | number;
style?: React.CSSProperties;
checked?: boolean;
onChange?: (value: string | number, state: boolean) => void;
}
export default class TagSelectOption extends React.Component<ITagSelectOptionProps, any> {}
export default class TagSelectOption extends React.Component<TagSelectOptionProps, any> {
public static isTagSelectOption?: boolean;
}
import * as React from 'react';
import TagSelectOption from './TagSelectOption';
import React from 'react';
import TagSelectOption, { TagSelectOptionProps } from './TagSelectOption';
export interface ITagSelectProps {
export interface TagSelectProps {
onChange?: (value: string[]) => void;
expandable?: boolean;
value?: string[] | number[];
style?: React.CSSProperties;
hideCheckAll?: boolean;
actionsText?: { expandText?: string; collapseText?: string; selectAllText?: string };
actionsText?: {
expandText?: React.ReactNode;
collapseText?: React.ReactNode;
selectAllText?: React.ReactNode;
};
className: string;
Option: TagSelectOptionProps;
children: React.ReactElement<TagSelectOption> | Array<React.ReactElement<TagSelectOption>>;
}
export default class TagSelect extends React.Component<ITagSelectProps, any> {
export default class TagSelect extends React.Component<TagSelectProps, any> {
public static Option: typeof TagSelectOption;
private children:
| React.ReactElement<TagSelectOption>
......
import React from 'react';
import { SiderTheme, CollapseType } from 'antd/es/Layout/Sider';
import { MenuMode } from 'antd/es/menu';
export interface TopNavHeaderProps {
theme: SiderTheme;
contentWidth?: string;
menuData?: any[];
logo?: string;
mode?: MenuMode;
flatMenuKeys?: any[];
onCollapse?: (collapsed: boolean, type?: CollapseType) => void;
isMobile?: boolean;
openKeys?: any;
className?: string;
collapsed?: boolean;
handleOpenChange?: (openKeys: any[]) => void;
style?: React.CSSProperties;
onOpenChange?: (openKeys: string[]) => void;
onNoticeClear?: (type: string) => void;
onMenuClick?: ({ key: string }) => void;
onNoticeVisibleChange?: (b: boolean) => void;
}
export default class TopNavHeader extends React.Component<TopNavHeaderProps, any> {}
......@@ -13,7 +13,11 @@ export default class TopNavHeader extends PureComponent {
static getDerivedStateFromProps(props) {
return {
maxWidth: (props.contentWidth === 'Fixed' ? 1200 : window.innerWidth) - 280 - 165 - 40,
maxWidth:
(props.contentWidth === 'Fixed' && window.innerWidth > 1200 ? 1200 : window.innerWidth) -
280 -
120 -
40,
};
}
......
import * as React from 'react';
import React from 'react';
export interface ITrendProps {
colorful?: boolean;
flag: 'up' | 'down';
style?: React.CSSProperties;
reverseColor?: boolean;
className?: string;
}
export default class Trend extends React.Component<ITrendProps, any> {}
......@@ -11,8 +11,9 @@ module.exports = {
},
title: 'Ant Design Pro',
pwa: true,
// your iconfont Symbol Scrip Url
// Your custom iconfont Symbol script Url
// eg://at.alicdn.com/t/font_1039637_btcrd5co4w.js
// 注意:如果需要图标多色,Iconfont图标项目里要进行批量去色处理
// 注意:如果需要图标多色,Iconfont 图标项目里要进行批量去色处理
// Usage: https://github.com/ant-design/ant-design-pro/pull/3517
iconfontUrl: '',
};
......@@ -5,13 +5,17 @@ const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
function formatter(data) {
return data
.reduce((pre, item) => {
if (item.routes) {
pre.push(item.routes[0].path);
} else {
pre.push(item.path);
}
return pre;
}, [])
.filter(item => item);
}
describe('Homepage', async () => {
describe('Homepage', () => {
const testPage = path => async () => {
await page.goto(`${BASE_URL}${path}`);
await page.waitForSelector('footer', {
......
......@@ -4,6 +4,7 @@ describe('Homepage', () => {
beforeAll(async () => {
jest.setTimeout(1000000);
});
it('topmenu should have footer', async () => {
const params = '/form/basic-form?navTheme=light&layout=topmenu';
await page.goto(`${BASE_URL}${params}`);
......
import React from 'react';
import { notification, Button, message } from 'antd';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import defaultSettings from './defaultSettings';
window.React = React;
......
......@@ -4,15 +4,11 @@ import DocumentTitle from 'react-document-title';
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 Authorized from '@/utils/Authorized';
import logo from '../assets/logo.svg';
import Footer from './Footer';
import Header from './Header';
import Context from './MenuContext';
import Exception403 from '../pages/Exception/403';
import PageLoading from '@/components/PageLoading';
import SiderMenu from '@/components/SiderMenu';
import getPageTitle from '@/utils/getPageTitle';
import styles from './BasicLayout.less';
......@@ -49,10 +45,7 @@ const query = {
class BasicLayout extends React.Component {
componentDidMount() {
const {
dispatch,
route: { routes, authority },
} = this.props;
const { dispatch } = this.props;
dispatch({
type: 'user/fetchCurrent',
});
......@@ -61,7 +54,6 @@ class BasicLayout extends React.Component {
});
dispatch({
type: 'menu/getMenuData',
payload: { routes, authority },
});
}
......@@ -73,29 +65,6 @@ class BasicLayout extends React.Component {
};
}
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);
};
getLayoutStyle = () => {
const { fixSiderbar, isMobile, collapsed, layout } = this.props;
if (fixSiderbar && layout !== 'topmenu' && !isMobile) {
......@@ -132,12 +101,10 @@ class BasicLayout extends React.Component {
isMobile,
menuData,
breadcrumbNameMap,
route: { routes },
fixedHeader,
} = this.props;
const isTop = PropsLayout === 'topmenu';
const routerConfig = this.getRouteAuthority(pathname, routes);
const contentStyle = !fixedHeader ? { paddingTop: 0 } : {};
const layout = (
<Layout>
......@@ -165,9 +132,7 @@ class BasicLayout extends React.Component {
{...this.props}
/>
<Content className={styles.content} style={contentStyle}>
<Authorized authority={routerConfig} noMatch={<Exception403 />}>
{children}
</Authorized>
</Content>
<Footer />
</Layout>
......@@ -184,7 +149,7 @@ class BasicLayout extends React.Component {
)}
</ContainerQuery>
</DocumentTitle>
<Suspense fallback={<PageLoading />}>{this.renderSettingDrawer()}</Suspense>
<Suspense fallback={null}>{this.renderSettingDrawer()}</Suspense>
</React.Fragment>
);
}
......
import React, { Component } from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import { Layout, message } from 'antd';
import Animate from 'rc-animate';
import { connect } from 'dva';
......@@ -120,7 +120,10 @@ class HeaderView extends Component {
const isTop = layout === 'topmenu';
const width = this.getHeadWidth();
const HeaderDom = visible ? (
<Header style={{ padding: 0, width }} className={fixedHeader ? styles.fixedHeader : ''}>
<Header
style={{ padding: 0, width, zIndex: 2 }}
className={fixedHeader ? styles.fixedHeader : ''}
>
{isTop && !isMobile ? (
<TopNavHeader
theme={navTheme}
......@@ -155,7 +158,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);
import React, { Component, Fragment } from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import { connect } from 'dva';
import Link from 'umi/link';
import { Icon } from 'antd';
......
......@@ -10,6 +10,7 @@ import settingDrawer from './en-US/settingDrawer';
import settings from './en-US/settings';
import pwa from './en-US/pwa';
import component from './en-US/component';
import editor from './en-US/editor';
export default {
'navBar.lang': 'Languages',
......@@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};
......@@ -2,4 +2,5 @@ export default {
'component.tagSelect.expand': 'Expand',
'component.tagSelect.collapse': 'Collapse',
'component.tagSelect.all': 'All',
'component.miniProgress.tooltipDefault': 'Target value',
};
export default {
'app.editor.flow.title': 'Flowchart Editor',
'app.editor.flow.description':
'The flow chart is an excellent way to represent the idea of the algorithm.',
'app.editor.koni.title': 'Koni Editor',
'app.editor.koni.description':
'The topology diagram refers to the network structure diagram composed of network node devices and communication media.',
'app.editor.mind.title': 'Mind Map Editor',
'app.editor.mind.description':
'The brain map is an effective graphical thinking tool for expressing divergent thinking. It is simple but effective and is a practical thinking tool.',
};
......@@ -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',
};
......@@ -38,4 +38,8 @@ export default {
'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',
};
......@@ -10,6 +10,7 @@ import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
import pwa from './pt-BR/pwa';
import component from './pt-BR/component';
import editor from './pt-BR/editor';
export default {
'navBar.lang': 'Idiomas',
......@@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};
......@@ -2,4 +2,5 @@ export default {
'component.tagSelect.expand': 'Expandir',
'component.tagSelect.collapse': 'Diminuir',
'component.tagSelect.all': 'Todas',
'component.miniProgress.tooltipDefault': 'Valor alvo',
};
export default {
'app.editor.flow.title': 'Editor de diagrama de flujo',
'app.editor.flow.description':
'El diagrama de flujo es una excelente manera de representar la idea del algoritmo.',
'app.editor.koni.title': 'Editor de topologia',
'app.editor.koni.description':
'El diagrama de topología se refiere al diagrama de estructura de red compuesto por dispositivos de nodo de red y medios de comunicación.',
'app.editor.mind.title': 'Editor de mapas cerebrales',
'app.editor.mind.description':
'El mapa cerebral es una herramienta de pensamiento gráfico eficaz para expresar el pensamiento divergente. Es simple pero efectivo y es una herramienta de pensamiento práctico.',
};
......@@ -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',
};
......@@ -38,4 +38,8 @@ export default {
'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',
};
......@@ -10,6 +10,7 @@ import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
import pwa from './zh-CN/pwa';
import component from './zh-CN/component';
import editor from './zh-CN/editor';
export default {
'navBar.lang': '语言',
......@@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};
......@@ -2,4 +2,5 @@ export default {
'component.tagSelect.expand': '展开',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
'component.miniProgress.tooltipDefault': '目标值',
};
export default {
'app.editor.flow.title': '流程图编辑器',
'app.editor.flow.description': '千言万语不如一张图,流程图是表示算法思路的好方法',
'app.editor.koni.title': '拓扑编辑器',
'app.editor.koni.description': '拓扑结构图是指由网络节点设备和通信介质构成的网络结构图',
'app.editor.mind.title': '脑图编辑器',
'app.editor.mind.description':
'脑图是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。',
};
export default {
'app.exception.back': '返回首页',
'app.exception.description.403': '抱歉,你无权访问页面',
'app.exception.description.403': '抱歉,你无权访问页面',
'app.exception.description.404': '抱歉,你访问的页面不存在',
'app.exception.description.500': '抱歉,服务器出错了',
};
......@@ -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': '查看更多',
};
......@@ -38,4 +38,8 @@ export default {
'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录',
'menu.editor': '图形编辑器',
'menu.editor.flow': '流程编辑器',
'menu.editor.mind': '脑图编辑器',
'menu.editor.koni': '拓扑编辑器',
};
......@@ -10,6 +10,7 @@ import settingDrawer from './zh-TW/settingDrawer';
import settings from './zh-TW/settings';
import pwa from './zh-TW/pwa';
import component from './zh-TW/component';
import editor from './zh-TW/editor';
export default {
'navBar.lang': '語言',
......@@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};
......@@ -2,4 +2,5 @@ export default {
'component.tagSelect.expand': '展開',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
'component.miniProgress.tooltipDefault': '目标值',
};
export default {
'app.editor.flow.title': '流程圖編輯器',
'app.editor.flow.description': '千言萬語不如一張圖,流程圖是表示算法思路的好方法',
'app.editor.koni.title': '拓撲編輯器',
'app.editor.koni.description': '拓撲結構圖是指由網絡節點設備和通信介質構成的網絡結構圖',
'app.editor.mind.title': '腦圖編輯器',
'app.editor.mind.description':
'腦圖是表達發散性思維的有效圖形思維工具 ,它簡單卻又很有效,是一種實用性的思維工具',
};
export default {
'app.exception.back': '返回首頁',
'app.exception.description.403': '抱歉,妳無權訪問頁面',
'app.exception.description.403': '抱歉,妳無權訪問頁面',
'app.exception.description.404': '抱歉,妳訪問的頁面不存在',
'app.exception.description.500': '抱歉,服務器出錯了',
};
......@@ -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': '查看更多',
};
......@@ -38,4 +38,8 @@ export default {
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '触发错误',
'menu.editor': '圖形編輯器',
'menu.editor.flow': '流程編輯器',
'menu.editor.mind': '腦圖編輯器',
'menu.editor.koni': '拓撲編輯器',
};
......@@ -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: {
......
......@@ -33,8 +33,7 @@ export default {
redirect = redirect.substr(redirect.indexOf('#') + 1);
}
} else {
window.location.href = redirect;
return;
redirect = null;
}
}
yield put(routerRedux.replace(redirect || '/'));
......@@ -54,14 +53,17 @@ export default {
},
});
reloadAuthorized();
// redirect
if (window.location.pathname !== '/user/login') {
yield put(
routerRedux.push({
routerRedux.replace({
pathname: '/user/login',
search: stringify({
redirect: window.location.href,
}),
})
);
}
},
},
......
import memoizeOne from 'memoize-one';
import isEqual from 'lodash/isEqual';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import Authorized from '@/utils/Authorized';
import { menu } from '../defaultSettings';
import { queryMenu } from '@/services/user';
......@@ -9,6 +9,9 @@ const { check } = Authorized;
// Conversion router to menu.
function formatter(data, parentAuthority, parentName) {
if (!data) {
return undefined;
}
return data
.map(item => {
if (!item.name || !item.path) {
......@@ -16,7 +19,7 @@ function formatter(data, parentAuthority, parentName) {
}
let locale = 'menu';
if (parentName) {
if (parentName && parentName !== '/') {
locale = `${parentName}.${item.name}`;
} else {
locale = `menu.${item.name}`;
......@@ -76,6 +79,9 @@ const filterMenuData = menuData => {
* @param {Object} menuData 菜单配置
*/
const getBreadcrumbNameMap = menuData => {
if (!menuData) {
return {};
}
const routerMap = {};
const flattenMenuData = data => {
......
import React from 'react';
import Link from 'umi/link';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import Exception from '@/components/Exception';
export default () => (
......
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Form, Input, Upload, Select, Button } from 'antd';
import { connect } from 'dva';
import styles from './BaseView.less';
......
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Icon, List } from 'antd';
class BindingView extends Component {
......
import React, { Component, Fragment } from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import { Switch, List } from 'antd';
class NotificationView extends Component {
......
import React, { Component, Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { List } from 'antd';
// import { getTimeDistance } from '@/utils/utils';
......
import React, { Component } from 'react';
import { connect } from 'dva';
import router from 'umi/router';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import { Menu } from 'antd';
import GridContent from '@/components/PageHeaderWrapper/GridContent';
import styles from './Info.less';
......
import React from 'react';
import RenderAuthorized from '@/components/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';
import { getAuthority } from '@/utils/authority';
import Exception403 from '@/pages/Exception/403';
const Authority = getAuthority();
const Authorized = RenderAuthorized(Authority);
function AuthComponent({ children, location, routerData }) {
const auth = getAuthority();
const isLogin = auth && auth[0] !== 'guest';
const getRouteAuthority = (path, routeData) => {
let authorities;
routeData.forEach(route => {
// match prefix
if (pathToRegexp(`${route.path}(.*)`).test(path)) {
authorities = route.authority || authorities;
export default ({ children }) => (
<Authorized authority={children.props.route.authority} noMatch={<Redirect to="/user/login" />}>
// get children authority recursively
if (route.routes) {
authorities = getRouteAuthority(path, route.routes) || authorities;
}
}
});
return authorities;
};
return (
<Authorized
authority={getRouteAuthority(location.pathname, routerData)}
noMatch={isLogin ? <Exception403 /> : <Redirect to="/user/login" />}
>
{children}
</Authorized>
);
);
}
export default connect(({ menu: menuModel }) => ({
routerData: menuModel.routerData,
}))(AuthComponent);
......@@ -5,7 +5,6 @@ import GridContent from '@/components/PageHeaderWrapper/GridContent';
import { getTimeDistance } from '@/utils/utils';
import styles from './Analysis.less';
import PageLoading from '@/components/PageLoading';
import { AsyncLoadBizCharts } from '@/components/Charts/AsyncLoadBizCharts';
const IntroduceRow = React.lazy(() => import('./IntroduceRow'));
const SalesCard = React.lazy(() => import('./SalesCard'));
......@@ -39,7 +38,6 @@ class Analysis extends Component {
type: 'chart/clear',
});
cancelAnimationFrame(this.reqRef);
clearTimeout(this.timeoutId);
}
handleChangeSalesType = e => {
......@@ -183,8 +181,4 @@ class Analysis extends Component {
}
}
export default props => (
<AsyncLoadBizCharts>
<Analysis {...props} />
</AsyncLoadBizCharts>
);
export default Analysis;
import React, { memo } from 'react';
import { Row, Col, Icon, Tooltip } from 'antd';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import styles from './Analysis.less';
import { ChartCard, MiniArea, MiniBar, MiniProgress, Field } from '@/components/Charts';
import Trend from '@/components/Trend';
......@@ -135,7 +135,15 @@ const IntroduceRow = memo(({ loading, visitData }) => (
}
contentHeight={46}
>
<MiniProgress percent={78} strokeWidth={8} target={80} color="#13C2C2" />
<MiniProgress
percent={78}
strokeWidth={8}
target={80}
targetLabel={`${formatMessage({ id: 'component.miniProgress.tooltipDefault' }).concat(
': '
)}80%`}
color="#13C2C2"
/>
</ChartCard>
</Col>
</Row>
......
import React, { Component } from 'react';
import { AsyncLoadBizCharts } from '@/components/Charts/AsyncLoadBizCharts';
import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Row, Col, Card, Tooltip } from 'antd';
import { Pie, WaterWave, Gauge, TagCloud } from '@/components/Charts';
import NumberInfo from '@/components/NumberInfo';
......@@ -242,8 +241,4 @@ class Monitor extends Component {
}
}
export default props => (
<AsyncLoadBizCharts>
<Monitor {...props} />
</AsyncLoadBizCharts>
);
export default Monitor;
import React, { memo } from 'react';
import { Card, Tabs, Row, Col } from 'antd';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import styles from './Analysis.less';
import { TimelineChart, Pie } from '@/components/Charts';
import NumberInfo from '@/components/NumberInfo';
......
import React, { memo } from 'react';
import { Card, Radio } from 'antd';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import styles from './Analysis.less';
import { Pie } from '@/components/Charts';
import Yuan from '@/utils/Yuan';
......
import React, { memo } from 'react';
import { Row, Col, Card, Tabs, DatePicker } from 'antd';
import { FormattedMessage, formatMessage } from 'umi/locale';
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
import numeral from 'numeral';
import styles from './Analysis.less';
import { Bar } from '@/components/Charts';
......
import React, { memo } from 'react';
import { Row, Col, Table, Tooltip, Card, Icon } from 'antd';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import Trend from '@/components/Trend';
import numeral from 'numeral';
import styles from './Analysis.less';
......
......@@ -3,7 +3,6 @@ import moment from 'moment';
import { connect } from 'dva';
import Link from 'umi/link';
import { Row, Col, Card, List, Avatar } from 'antd';
import { AsyncLoadBizCharts } from '@/components/Charts/AsyncLoadBizCharts';
import { Radar } from '@/components/Charts';
import EditableLinkGroup from '@/components/EditableLinkGroup';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
......@@ -253,8 +252,4 @@ class Workplace extends PureComponent {
}
}
export default props => (
<AsyncLoadBizCharts>
<Workplace {...props} />
</AsyncLoadBizCharts>
);
export default Workplace;
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Flow } from 'gg-editor';
import EditorMinimap from '@/components/EditorMinimap';
import { FlowContextMenu } from '@/components/EditorContextMenu';
import { FlowToolbar } from '@/components/EditorToolbar';
import { FlowItemPanel } from '@/components/EditorItemPanel';
import { FlowDetailPanel } from '@/components/EditorDetailPanel';
import styles from './index.less';
import { FormattedMessage } from 'umi-plugin-react/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
GGEditor.setTrackable(false);
const FlowPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.flow.title" />}
content={<FormattedMessage id="app.editor.flow.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<FlowToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={4} className={styles.editorSidebar}>
<FlowItemPanel />
</Col>
<Col span={16} className={styles.editorContent}>
<Flow className={styles.flow} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<FlowDetailPanel />
<EditorMinimap />
</Col>
</Row>
<FlowContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default FlowPage;
.editor {
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: calc(100vh - 250px);
background: #fff;
}
.editorHd {
padding: 8px;
border: 1px solid #e6e9ed;
}
.editorBd {
flex: 1;
}
.editorSidebar,
.editorContent {
display: flex;
flex-direction: column;
}
.editorSidebar {
background: #fafafa;
&:first-child {
border-right: 1px solid #e6e9ed;
}
&:last-child {
border-left: 1px solid #e6e9ed;
}
}
.flow,
.mind,
.koni {
flex: 1;
}
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Koni } from 'gg-editor';
import EditorMinimap from '@/components/EditorMinimap';
import { KoniContextMenu } from '@/components/EditorContextMenu';
import { KoniToolbar } from '@/components/EditorToolbar';
import { KoniItemPanel } from '@/components/EditorItemPanel';
import { KoniDetailPanel } from '@/components/EditorDetailPanel';
import KoniCustomNode from '@/components/EditorKoniCustomNode';
import styles from '../Flow/index.less';
import { FormattedMessage } from 'umi-plugin-react/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
GGEditor.setTrackable(false);
const KoniPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.koni.title" />}
content={<FormattedMessage id="app.editor.koni.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<KoniToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={4} className={styles.editorSidebar}>
<KoniItemPanel />
</Col>
<Col span={16} className={styles.editorContent}>
<Koni className={styles.koni} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<KoniDetailPanel />
<EditorMinimap />
</Col>
</Row>
<KoniCustomNode />
<KoniContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default KoniPage;
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Mind } from 'gg-editor';
import EditorMinimap from '@/components/EditorMinimap';
import { MindContextMenu } from '@/components/EditorContextMenu';
import { MindToolbar } from '@/components/EditorToolbar';
import { MindDetailPanel } from '@/components/EditorDetailPanel';
import data from '@/common/worldCup2018.json';
import styles from '../Flow/index.less';
import { FormattedMessage } from 'umi-plugin-react/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
GGEditor.setTrackable(false);
const MindPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.mind.title" />}
content={<FormattedMessage id="app.editor.mind.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<MindToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={20} className={styles.editorContent}>
<Mind data={data} className={styles.mind} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<MindDetailPanel />
<EditorMinimap />
</Col>
</Row>
<MindContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default MindPage;
import React from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import Exception from '@/components/Exception';
......
import React from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import Exception from '@/components/Exception';
......
import React from 'react';
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import Exception from '@/components/Exception';
......
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import {
Form,
Input,
......
import React, { PureComponent } from 'react';
import numeral from 'numeral';
import { connect } from 'dva';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import { Row, Col, Form, Card, Select, Icon, Avatar, List, Tooltip, Dropdown, Menu } from 'antd';
import TagSelect from '@/components/TagSelect';
import StandardFormRow from '@/components/StandardFormRow';
......
import React, { Component, Fragment } from 'react';
import { connect } from 'dva';
import { Form, Card, Select, List, Tag, Icon, Row, Col, Button } from 'antd';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import TagSelect from '@/components/TagSelect';
import StandardFormRow from '@/components/StandardFormRow';
......
......@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import moment from 'moment';
import { connect } from 'dva';
import { Row, Col, Form, Card, Select, List } from 'antd';
import { FormattedMessage } from 'umi/locale';
import { FormattedMessage } from 'umi-plugin-react/locale';
import TagSelect from '@/components/TagSelect';
import AvatarList from '@/components/AvatarList';
......
import React, { Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Button, Icon, Card } from 'antd';
import Result from '@/components/Result';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
......
import React, { Fragment } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Button, Row, Col, Icon, Steps, Card } from 'antd';
import Result from '@/components/Result';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
......
import React from 'react';
import { shallow } from 'enzyme';
import Success from './Success';
it('renders with Result', () => {
const wrapper = shallow(<Success />);
expect(wrapper.find('Result').length).toBe(1);
expect(wrapper.find('Result').prop('type')).toBe('success');
});
import React, { Component } from 'react';
import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import { Checkbox, Alert, Icon } from 'antd';
import Login from '@/components/Login';
......
import React, { Component } from 'react';
import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import Link from 'umi/link';
import router from 'umi/router';
import { Form, Input, Button, Select, Row, Col, Popover, Progress } from 'antd';
......
import React from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale';
import { formatMessage, FormattedMessage } from 'umi-plugin-react/locale';
import { Button } from 'antd';
import Link from 'umi/link';
import Result from '@/components/Result';
......
......@@ -53,5 +53,10 @@
"path": "/account/settings",
"exact": true,
"redirect": "/account/settings/baseview"
},
{
"path": "/editor",
"exact": true,
"redirect": "/deitor/flow"
}
]
......@@ -16,7 +16,7 @@ export async function queryRule(params) {
export async function removeRule(params) {
return request('/api/rule', {
method: 'POST',
body: {
data: {
...params,
method: 'delete',
},
......@@ -26,7 +26,7 @@ export async function removeRule(params) {
export async function addRule(params) {
return request('/api/rule', {
method: 'POST',
body: {
data: {
...params,
method: 'post',
},
......@@ -36,7 +36,7 @@ export async function addRule(params) {
export async function updateRule(params = {}) {
return request(`/api/rule?${stringify(params.query)}`, {
method: 'POST',
body: {
data: {
...params.body,
method: 'update',
},
......@@ -46,7 +46,7 @@ export async function updateRule(params = {}) {
export async function fakeSubmitForm(params) {
return request('/api/forms', {
method: 'POST',
body: params,
data: params,
});
}
......@@ -74,7 +74,7 @@ export async function removeFakeList(params) {
const { count = 5, ...restParams } = params;
return request(`/api/fake_list?count=${count}`, {
method: 'POST',
body: {
data: {
...restParams,
method: 'delete',
},
......@@ -85,7 +85,7 @@ export async function addFakeList(params) {
const { count = 5, ...restParams } = params;
return request(`/api/fake_list?count=${count}`, {
method: 'POST',
body: {
data: {
...restParams,
method: 'post',
},
......@@ -96,7 +96,7 @@ export async function updateFakeList(params) {
const { count = 5, ...restParams } = params;
return request(`/api/fake_list?count=${count}`, {
method: 'POST',
body: {
data: {
...restParams,
method: 'update',
},
......@@ -106,14 +106,14 @@ export async function updateFakeList(params) {
export async function fakeAccountLogin(params) {
return request('/api/login/account', {
method: 'POST',
body: params,
data: params,
});
}
export async function fakeRegister(params) {
return request('/api/register', {
method: 'POST',
body: params,
data: params,
});
}
......
// use localStorage to store the authority info, which might be sent from server in actual project.
const { NODE_ENV } = process.env;
export function getAuthority(str) {
// return localStorage.getItem('antd-pro-authority') || ['admin', 'user'];
const authorityString =
......@@ -13,7 +15,10 @@ export function getAuthority(str) {
if (typeof authority === 'string') {
return [authority];
}
return authority || ['admin'];
if (!authority && NODE_ENV !== 'production') {
return ['admin'];
}
return authority;
}
export function setAuthority(authority) {
......
import { formatMessage } from 'umi/locale';
import { formatMessage } from 'umi-plugin-react/locale';
import pathToRegexp from 'path-to-regexp';
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
......
import fetch from 'dva/fetch';
/**
* request 网络请求工具
* 更详细的api文档: https://bigfish.alipay.com/doc/api#request
*/
import { extend } from 'umi-request';
import { notification } from 'antd';
import router from 'umi/router';
import hash from 'hash.js';
import { isAntdPro } from './utils';
const codeMessage = {
200: '服务器成功返回请求的数据。',
......@@ -22,116 +24,18 @@ const codeMessage = {
504: '网关超时。',
};
const checkStatus = response => {
if (response.status >= 200 && response.status < 300) {
return response;
}
const errortext = codeMessage[response.status] || response.statusText;
notification.error({
message: `请求错误 ${response.status}: ${response.url}`,
description: errortext,
});
const error = new Error(errortext);
error.name = response.status;
error.response = response;
throw error;
};
const cachedSave = (response, hashcode) => {
/**
* Clone a response data and store it in sessionStorage
* Does not support data other than json, Cache only json
*/
const contentType = response.headers.get('Content-Type');
if (contentType && contentType.match(/application\/json/i)) {
// All data is saved as text
response
.clone()
.text()
.then(content => {
sessionStorage.setItem(hashcode, content);
sessionStorage.setItem(`${hashcode}:timestamp`, Date.now());
});
}
return response;
};
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [option] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, option) {
const options = {
expirys: isAntdPro(),
...option,
};
/**
* Produce fingerprints based on url and parameters
* Maybe url has the same parameters
* 异常处理程序
*/
const fingerprint = url + (options.body ? JSON.stringify(options.body) : '');
const hashcode = hash
.sha256()
.update(fingerprint)
.digest('hex');
const defaultOptions = {
credentials: 'include',
};
const newOptions = { ...defaultOptions, ...options };
if (
newOptions.method === 'POST' ||
newOptions.method === 'PUT' ||
newOptions.method === 'DELETE'
) {
if (!(newOptions.body instanceof FormData)) {
newOptions.headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
...newOptions.headers,
};
newOptions.body = JSON.stringify(newOptions.body);
} else {
// newOptions.body is FormData
newOptions.headers = {
Accept: 'application/json',
...newOptions.headers,
};
}
}
const errorHandler = error => {
const { response = {} } = error;
const errortext = codeMessage[response.status] || response.statusText;
const { status, url } = response;
const expirys = options.expirys && 60;
// options.expirys !== false, return the cache,
if (options.expirys !== false) {
const cached = sessionStorage.getItem(hashcode);
const whenCached = sessionStorage.getItem(`${hashcode}:timestamp`);
if (cached !== null && whenCached !== null) {
const age = (Date.now() - whenCached) / 1000;
if (age < expirys) {
const response = new Response(new Blob([cached]));
return response.json();
}
sessionStorage.removeItem(hashcode);
sessionStorage.removeItem(`${hashcode}:timestamp`);
}
}
return fetch(url, newOptions)
.then(checkStatus)
.then(response => cachedSave(response, hashcode))
.then(response => {
// DELETE and 204 do not return data by default
// using .json will report an error.
if (newOptions.method === 'DELETE' || response.status === 204) {
return response.text();
}
return response.json();
})
.catch(e => {
const status = e.name;
if (status === 401) {
notification.error({
message: '未登录或登录已过期,请重新登录。',
});
// @HACK
/* eslint-disable no-underscore-dangle */
window.g_app._store.dispatch({
......@@ -139,6 +43,10 @@ export default function request(url, option) {
});
return;
}
notification.error({
message: `请求错误 ${status}: ${url}`,
description: errortext,
});
// environment should not be used
if (status === 403) {
router.push('/exception/403');
......@@ -151,5 +59,14 @@ export default function request(url, option) {
if (status >= 404 && status < 422) {
router.push('/exception/404');
}
});
}
};
/**
* 配置request请求时的默认参数
*/
const request = extend({
errorHandler, // 默认错误处理
credentials: 'include', // 默认请求是否带上cookie
});
export default request;
......@@ -9,7 +9,7 @@
"jsx": "react",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"rootDirs": ["/src", "/test", "/mock","./typings"],
"rootDirs": ["/src", "/test", "/mock", "./typings"],
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"suppressImplicitAnyIndexErrors": true,
......
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