├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .firebaserc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .stylelintrc.json ├── Dockerfile ├── Dockerfile.dev ├── Dockerfile.hub ├── LICENSE ├── README.md ├── README.ru-RU.md ├── README.zh-CN.md ├── appveyor.yml ├── config ├── config.js ├── plugin.config.js └── router.config.js ├── docker ├── docker-compose.dev.yml ├── docker-compose.yml └── nginx.conf ├── firebase.json ├── functions ├── index.js ├── matchMock.js └── package.json ├── jest-puppeteer.config.js ├── jest.config.js ├── jsconfig.json ├── mock ├── api.js ├── chart.js ├── geographic.js ├── geographic │ ├── city.json │ └── province.json ├── notices.js ├── profile.js ├── rule.js └── user.js ├── netlify.toml ├── package.json ├── public ├── ant.jpeg ├── ant1.jpeg ├── favicon.png ├── icons │ ├── icon-128x128.png │ ├── icon-192x192.png │ └── icon-512x512.png └── image │ ├── braft-editor.png │ ├── center.png │ ├── drag.png │ ├── react-color.png │ ├── scroll.png │ ├── select-tree.png │ ├── star.png │ └── table.png ├── scripts ├── generateMock.js ├── getPrettierFiles.js ├── lint-prettier.js └── prettier.js ├── src ├── assets │ └── logo.svg ├── components │ ├── ActiveChart │ │ ├── index.js │ │ └── index.less │ ├── AntTableFinder │ │ ├── AntTableFinder.js │ │ ├── AntTableFinder.less │ │ └── AntTableFinder.md │ ├── ArticleListContent │ │ ├── index.js │ │ └── index.less │ ├── Authorized │ │ ├── Authorized.js │ │ ├── AuthorizedRoute.d.ts │ │ ├── AuthorizedRoute.js │ │ ├── CheckPermissions.js │ │ ├── CheckPermissions.test.js │ │ ├── PromiseRender.js │ │ ├── Secured.js │ │ ├── demo │ │ │ ├── AuthorizedArray.md │ │ │ ├── AuthorizedFunction.md │ │ │ ├── basic.md │ │ │ └── secured.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.md │ │ └── renderAuthorize.js │ ├── AvatarList │ │ ├── AvatarItem.d.ts │ │ ├── demo │ │ │ ├── maxLength.md │ │ │ └── simple.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ ├── index.test.js │ │ └── index.zh-CN.md │ ├── Charts │ │ ├── Bar │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── ChartCard │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── Field │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── Gauge │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── MiniArea │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── MiniBar │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── MiniProgress │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── Pie │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── Radar │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── TagCloud │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── TimelineChart │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── WaterWave │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── autoHeight.js │ │ ├── bizcharts.d.ts │ │ ├── bizcharts.js │ │ ├── demo │ │ │ ├── bar.md │ │ │ ├── chart-card.md │ │ │ ├── gauge.md │ │ │ ├── mini-area.md │ │ │ ├── mini-bar.md │ │ │ ├── mini-pie.md │ │ │ ├── mini-progress.md │ │ │ ├── mix.md │ │ │ ├── pie.md │ │ │ ├── radar.md │ │ │ ├── tag-cloud.md │ │ │ ├── timeline-chart.md │ │ │ └── waterwave.md │ │ ├── g2.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ └── index.md │ ├── CountDown │ │ ├── demo │ │ │ └── simple.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ └── index.zh-CN.md │ ├── DescriptionList │ │ ├── Description.d.ts │ │ ├── Description.js │ │ ├── DescriptionList.js │ │ ├── demo │ │ │ ├── basic.md │ │ │ └── vertical.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ ├── index.zh-CN.md │ │ └── responsive.js │ ├── EditableItem │ │ ├── index.js │ │ └── index.less │ ├── EditableLinkGroup │ │ ├── index.js │ │ └── index.less │ ├── Ellipsis │ │ ├── demo │ │ │ ├── line.md │ │ │ └── number.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ ├── index.test.js │ │ └── index.zh-CN.md │ ├── Exception │ │ ├── demo │ │ │ ├── 403.md │ │ │ ├── 404.md │ │ │ └── 500.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ ├── index.zh-CN.md │ │ └── typeConfig.js │ ├── FooterToolbar │ │ ├── demo │ │ │ └── basic.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ └── index.zh-CN.md │ ├── GlobalFooter │ │ ├── demo │ │ │ └── basic.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ └── index.md │ ├── GlobalHeader │ │ ├── RightContent.js │ │ ├── index.js │ │ └── index.less │ ├── HeaderDropdown │ │ ├── index.js │ │ └── index.less │ ├── HeaderSearch │ │ ├── demo │ │ │ └── basic.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ └── index.zh-CN.md │ ├── Login │ │ ├── LoginItem.d.ts │ │ ├── LoginItem.js │ │ ├── LoginSubmit.js │ │ ├── LoginTab.d.ts │ │ ├── LoginTab.js │ │ ├── demo │ │ │ └── basic.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ ├── index.zh-CN.md │ │ ├── loginContext.js │ │ └── map.js │ ├── NoticeIcon │ │ ├── NoticeIconTab.d.ts │ │ ├── NoticeList.js │ │ ├── NoticeList.less │ │ ├── demo │ │ │ ├── basic.md │ │ │ └── popover.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ └── index.zh-CN.md │ ├── NumberInfo │ │ ├── demo │ │ │ └── basic.md │ │ ├── index.d.ts │ │ ├── index.en-US.md │ │ ├── index.js │ │ ├── index.less │ │ └── index.zh-CN.md │ ├── PageHeader │ │ ├── breadcrumb.d.ts │ │ ├── breadcrumb.js │ │ ├── demo │ │ │ ├── image.md │ │ │ ├── simple.md │ │ │ ├── standard.md │ │ │ └── structure.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ ├── index.md │ │ └── index.test.js │ ├── PageHeaderWrapper │ │ ├── GridContent.js │ │ ├── GridContent.less │ │ ├── index.js │ │ └── index.less │ ├── PageLoading │ │ └── index.js │ ├── Result │ │ ├── demo │ │ │ ├── classic.md │ │ │ ├── error.md │ │ │ └── structure.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ └── index.md │ ├── SelectLang │ │ ├── index.js │ │ └── index.less │ ├── SettingDrawer │ │ ├── BlockCheckbox.js │ │ ├── ThemeColor.js │ │ ├── ThemeColor.less │ │ ├── index.js │ │ └── index.less │ ├── SiderMenu │ │ ├── BaseMenu.js │ │ ├── SiderMenu.js │ │ ├── SiderMenu.test.js │ │ ├── SiderMenuUtils.js │ │ ├── index.js │ │ └── index.less │ ├── StandardFormRow │ │ ├── index.js │ │ └── index.less │ ├── StandardTable │ │ ├── index.js │ │ └── index.less │ ├── TagSelect │ │ ├── TagSelectOption.d.ts │ │ ├── demo │ │ │ ├── controlled.md │ │ │ ├── expandable.md │ │ │ └── simple.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ └── index.md │ ├── TopNavHeader │ │ ├── index.js │ │ └── index.less │ ├── TreeCheck │ │ └── index.js │ ├── Trend │ │ ├── demo │ │ │ ├── basic.md │ │ │ └── reverse.md │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── index.less │ │ └── index.md │ └── _utils │ │ ├── pathTools.js │ │ └── pathTools.test.js ├── defaultSettings.js ├── e2e │ ├── baseLayout.e2e.js │ ├── home.e2e.js │ ├── login.e2e.js │ ├── topMenu.e2e.js │ └── userLayout.e2e.js ├── global.js ├── global.less ├── layouts │ ├── BasicLayout.js │ ├── BasicLayout.less │ ├── BlankLayout.js │ ├── Footer.js │ ├── Header.js │ ├── Header.less │ ├── MenuContext.js │ ├── UserLayout.js │ └── UserLayout.less ├── locales │ ├── en-US.js │ ├── en-US │ │ ├── analysis.js │ │ ├── exception.js │ │ ├── form.js │ │ ├── globalHeader.js │ │ ├── login.js │ │ ├── menu.js │ │ ├── monitor.js │ │ ├── pwa.js │ │ ├── result.js │ │ ├── settingDrawer.js │ │ └── settings.js │ ├── pt-BR.js │ ├── pt-BR │ │ ├── analysis.js │ │ ├── exception.js │ │ ├── form.js │ │ ├── globalHeader.js │ │ ├── login.js │ │ ├── menu.js │ │ ├── monitor.js │ │ ├── pwa.js │ │ ├── result.js │ │ ├── settingDrawer.js │ │ └── settings.js │ ├── zh-CN.js │ ├── zh-CN │ │ ├── analysis.js │ │ ├── exception.js │ │ ├── form.js │ │ ├── globalHeader.js │ │ ├── login.js │ │ ├── menu.js │ │ ├── monitor.js │ │ ├── pwa.js │ │ ├── result.js │ │ ├── settingDrawer.js │ │ └── settings.js │ ├── zh-TW.js │ └── zh-TW │ │ ├── analysis.js │ │ ├── exception.js │ │ ├── form.js │ │ ├── globalHeader.js │ │ ├── login.js │ │ ├── menu.js │ │ ├── monitor.js │ │ ├── pwa.js │ │ ├── result.js │ │ ├── settingDrawer.js │ │ └── settings.js ├── manifest.json ├── models │ ├── global.js │ ├── list.js │ ├── login.js │ ├── menu.js │ ├── project.js │ ├── setting.js │ └── user.js ├── pages │ ├── 404.js │ ├── Account │ │ ├── Center │ │ │ ├── Applications.js │ │ │ ├── Articles.js │ │ │ ├── Articles.less │ │ │ ├── Center.js │ │ │ ├── Center.less │ │ │ └── Projects.js │ │ └── Settings │ │ │ ├── BaseView.js │ │ │ ├── BaseView.less │ │ │ ├── BindingView.js │ │ │ ├── GeographicView.js │ │ │ ├── GeographicView.less │ │ │ ├── Info.js │ │ │ ├── Info.less │ │ │ ├── NotificationView.js │ │ │ ├── PhoneView.js │ │ │ ├── PhoneView.less │ │ │ ├── SecurityView.js │ │ │ └── models │ │ │ └── geographic.js │ ├── Authorized.js │ ├── Component │ │ ├── Edit.js │ │ ├── SelectTree.js │ │ ├── Table.js │ │ ├── Table.less │ │ └── TableData.json │ ├── Dashboard │ │ ├── Analysis.js │ │ ├── Analysis.less │ │ ├── IntroduceRow.js │ │ ├── Monitor.js │ │ ├── Monitor.less │ │ ├── OfflineData.js │ │ ├── ProportionSales.js │ │ ├── SalesCard.js │ │ ├── TopSearch.js │ │ ├── Workplace.js │ │ ├── Workplace.less │ │ └── models │ │ │ ├── activities.js │ │ │ ├── chart.js │ │ │ └── monitor.js │ ├── Exception │ │ ├── 403.js │ │ ├── 404.js │ │ ├── 500.js │ │ ├── TriggerException.js │ │ ├── models │ │ │ └── error.js │ │ └── style.less │ ├── Forms │ │ ├── AdvancedForm.js │ │ ├── BasicForm.js │ │ ├── StepForm │ │ │ ├── Step1.js │ │ │ ├── Step2.js │ │ │ ├── Step3.js │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── TableForm.js │ │ ├── models │ │ │ └── form.js │ │ └── style.less │ ├── Home │ │ ├── Home.js │ │ └── home.json │ ├── Libraries │ │ ├── BraftEditor.js │ │ ├── Drag │ │ │ ├── Basic.js │ │ │ ├── Collections.js │ │ │ ├── DragHandle.js │ │ │ └── index.js │ │ ├── ReactColor.js │ │ ├── ReactDataGrid │ │ │ ├── Editable │ │ │ │ └── index.js │ │ │ ├── ReactDataGrid.md │ │ │ └── index.js │ │ └── ScrollBars │ │ │ ├── ColoredScrollbars.js │ │ │ └── index.js │ ├── List │ │ ├── Applications.js │ │ ├── Applications.less │ │ ├── Articles.js │ │ ├── Articles.less │ │ ├── BasicList.js │ │ ├── BasicList.less │ │ ├── CardList.js │ │ ├── CardList.less │ │ ├── List.js │ │ ├── Projects.js │ │ ├── Projects.less │ │ ├── TableList.js │ │ ├── TableList.less │ │ └── models │ │ │ └── rule.js │ ├── Profile │ │ ├── AdvancedProfile.js │ │ ├── AdvancedProfile.less │ │ ├── BasicProfile.js │ │ ├── BasicProfile.less │ │ └── models │ │ │ └── profile.js │ ├── Result │ │ ├── Error.js │ │ ├── Success.js │ │ └── Success.test.js │ ├── User │ │ ├── Login.js │ │ ├── Login.less │ │ ├── Register.js │ │ ├── Register.less │ │ ├── RegisterResult.js │ │ ├── RegisterResult.less │ │ └── models │ │ │ └── register.js │ └── document.ejs ├── service-worker.js ├── services │ ├── api.js │ ├── error.js │ ├── geographic.js │ └── user.js ├── style │ ├── img │ │ └── ant.jpeg │ └── less │ │ └── common.less └── utils │ ├── Authorized.js │ ├── Yuan.js │ ├── authority.js │ ├── authority.test.js │ ├── request.js │ ├── utils.js │ ├── utils.less │ └── utils.test.js ├── tests └── run-tests.js ├── tsconfig.json └── tslint.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | **/node_modules 5 | /src/utils/request-temp.js 6 | 7 | # production 8 | /.vscode 9 | 10 | # misc 11 | .DS_Store 12 | npm-debug.log* 13 | yarn-error.log 14 | 15 | /coverage 16 | .idea 17 | yarn.lock 18 | package-lock.json 19 | *bak 20 | .vscode 21 | 22 | # visual studio code 23 | .history 24 | *.log 25 | 26 | functions/mock 27 | .temp/** 28 | 29 | # umi 30 | .umi 31 | .umi-production 32 | 33 | # screenshot 34 | screenshot 35 | .firebase -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /functions/mock/** 2 | /scripts 3 | /config 4 | /src -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: ['airbnb', 'prettier', 'plugin:compat/recommended'], 4 | env: { 5 | browser: true, 6 | node: true, 7 | es6: true, 8 | mocha: true, 9 | jest: true, 10 | jasmine: true, 11 | }, 12 | globals: { 13 | APP_TYPE: true, 14 | page: true, 15 | }, 16 | rules: { 17 | 'react/jsx-filename-extension': [1, { extensions: ['.js'] }], 18 | 'react/jsx-wrap-multilines': 0, 19 | 'react/prop-types': 0, 20 | 'react/forbid-prop-types': 0, 21 | 'react/jsx-one-expression-per-line': 0, 22 | 'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }], 23 | 'import/no-extraneous-dependencies': [ 24 | 2, 25 | { 26 | optionalDependencies: true, 27 | devDependencies: ['**/tests/**.js', '/mock/**.js', '**/**.test.js'], 28 | }, 29 | ], 30 | 'jsx-a11y/no-noninteractive-element-interactions': 0, 31 | 'jsx-a11y/click-events-have-key-events': 0, 32 | 'jsx-a11y/no-static-element-interactions': 0, 33 | 'jsx-a11y/anchor-is-valid': 0, 34 | 'linebreak-style': 0, 35 | }, 36 | settings: { 37 | polyfills: ['fetch', 'promises', 'url'], 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "antd-pro" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | **/node_modules 5 | # roadhog-api-doc ignore 6 | /src/utils/request-temp.js 7 | _roadhog-api-doc 8 | 9 | # production 10 | 11 | /dist 12 | /.vscode 13 | /.circleci 14 | 15 | # misc 16 | .DS_Store 17 | npm-debug.log* 18 | yarn-error.log 19 | 20 | /coverage 21 | .idea 22 | yarn.lock 23 | package-lock.json 24 | *bak 25 | .vscode 26 | 27 | # visual studio code 28 | .history 29 | *.log 30 | 31 | functions/mock 32 | .temp/** 33 | 34 | # umi 35 | .umi 36 | .umi-production 37 | 38 | # screenshot 39 | screenshot 40 | .firebase -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "printWidth": 100, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard", "stylelint-config-prettier"], 3 | "rules": { 4 | "declaration-empty-line-before": null, 5 | "no-descending-specificity": null, 6 | "selector-pseudo-class-no-unknown": null, 7 | "selector-pseudo-element-colon-notation": null 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM circleci/node:latest-browsers 2 | 3 | WORKDIR /usr/src/app/ 4 | USER root 5 | COPY package.json ./ 6 | RUN yarn 7 | 8 | COPY ./ ./ 9 | 10 | RUN npm run test:all 11 | 12 | CMD ["npm", "run", "build"] 13 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | WORKDIR /usr/src/app/ 4 | 5 | COPY package.json ./ 6 | RUN npm install --silent --no-cache 7 | 8 | COPY ./ ./ 9 | 10 | 11 | CMD ["npm", "run", "start"] 12 | -------------------------------------------------------------------------------- /Dockerfile.hub: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | WORKDIR /usr/src/app/ 4 | 5 | COPY ./docker/nginx.conf /etc/nginx/conf.d/default.conf 6 | 7 | COPY ./dist /usr/share/nginx/html/ 8 | 9 | EXPOSE 80 10 | 11 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 776248185@qq.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against the latest version of this Node.js version 2 | environment: 3 | nodejs_version: '10' 4 | 5 | # this is how to allow failing jobs in the matrix 6 | matrix: 7 | fast_finish: true # set this flag to immediately finish build once one of the jobs fails. 8 | 9 | # Install scripts. (runs after repo cloning) 10 | install: 11 | # Get the latest stable version of Node.js or io.js 12 | - ps: Install-Product node $env:nodejs_version 13 | # install modules 14 | - npm install 15 | # Output useful info for debugging. 16 | - node --version 17 | - npm --version 18 | 19 | # Post-install test scripts. 20 | test_script: 21 | - npm run lint 22 | - npm run test:all 23 | - npm run build 24 | 25 | # Don't actually build. 26 | build: off 27 | -------------------------------------------------------------------------------- /config/plugin.config.js: -------------------------------------------------------------------------------- 1 | // Change theme plugin 2 | 3 | import MergeLessPlugin from 'antd-pro-merge-less'; 4 | import AntDesignThemePlugin from 'antd-theme-webpack-plugin'; 5 | import path from 'path'; 6 | 7 | export default config => { 8 | // pro 和 开发环境再添加这个插件 9 | if (process.env.APP_TYPE === 'site' || process.env.NODE_ENV !== 'production') { 10 | // 将所有 less 合并为一个供 themePlugin使用 11 | const outFile = path.join(__dirname, '../.temp/ant-design-pro.less'); 12 | const stylesDir = path.join(__dirname, '../src/'); 13 | 14 | config.plugin('merge-less').use(MergeLessPlugin, [ 15 | { 16 | stylesDir, 17 | outFile, 18 | }, 19 | ]); 20 | 21 | config.plugin('ant-design-theme').use(AntDesignThemePlugin, [ 22 | { 23 | antDir: path.join(__dirname, '../node_modules/antd'), 24 | stylesDir, 25 | varFile: path.join(__dirname, '../node_modules/antd/lib/style/themes/default.less'), 26 | mainLessFile: outFile, // themeVariables: ['@primary-color'], 27 | indexFileName: 'index.html', 28 | generateOne: true, 29 | lessUrl: 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js', 30 | }, 31 | ]); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /docker/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | ant-design-pro_dev: 5 | ports: 6 | - 8000:8000 7 | build: 8 | context: ../ 9 | dockerfile: Dockerfile.dev 10 | container_name: "ant-design-pro_dev" 11 | volumes: 12 | - ../src:/usr/src/app/src 13 | - ../config:/usr/src/app/config 14 | - ../mock:/usr/src/app/mock 15 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | ant-design-pro_build: 5 | build: ../ 6 | container_name: "ant-design-pro_build" 7 | volumes: 8 | - dist:/usr/src/app/dist 9 | 10 | ant-design-pro_web: 11 | image: nginx 12 | ports: 13 | - 80:80 14 | container_name: "ant-design-pro_web" 15 | restart: unless-stopped 16 | volumes: 17 | - dist:/usr/share/nginx/html:ro 18 | - ./nginx.conf:/etc/nginx/conf.d/default.conf 19 | 20 | volumes: 21 | dist: 22 | -------------------------------------------------------------------------------- /docker/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | # gzip config 4 | gzip on; 5 | gzip_min_length 1k; 6 | gzip_comp_level 9; 7 | gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml; 8 | gzip_vary on; 9 | gzip_disable "MSIE [1-6]\."; 10 | 11 | root /usr/share/nginx/html; 12 | 13 | location / { 14 | try_files $uri $uri/ /index.html; 15 | } 16 | location /api { 17 | proxy_pass https://preview.pro.ant.design; 18 | proxy_set_header X-Forwarded-Proto $scheme; 19 | proxy_set_header Host $http_host; 20 | proxy_set_header X-Real-IP $remote_addr; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "rewrites": [ 5 | { "source": "/api/**", "function": "api" }, 6 | { 7 | "source": "**", 8 | "destination": "/index.html" 9 | } 10 | ], 11 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | // [START functionsimport] 2 | const functions = require('firebase-functions'); 3 | const express = require('express'); 4 | const matchMock = require('./matchMock'); 5 | const app = express(); 6 | 7 | app.use(matchMock); 8 | 9 | exports.api = functions.https.onRequest(app); 10 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "npm run mock && firebase serve --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log", 10 | "mock": "node ../scripts/generateMock.js" 11 | }, 12 | "dependencies": { 13 | "@babel/runtime": "^7.0.0", 14 | "body-parser": "^1.18.3", 15 | "express": "^4.16.3", 16 | "firebase-admin": "^5.12.1", 17 | "firebase-functions": "^2.0.5", 18 | "mockjs": "^1.0.1-beta3", 19 | "moment": "^2.22.2", 20 | "path-to-regexp": "^2.2.1" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /jest-puppeteer.config.js: -------------------------------------------------------------------------------- 1 | // ps https://github.com/GoogleChrome/puppeteer/issues/3120 2 | module.exports = { 3 | launch: { 4 | headless: true, 5 | args: [ 6 | '--disable-gpu', 7 | '--disable-dev-shm-usage', 8 | '--disable-setuid-sandbox', 9 | '--no-first-run', 10 | '--no-sandbox', 11 | '--no-zygote', 12 | '--single-process', 13 | ], 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testURL: 'http://localhost:8000', 3 | preset: 'jest-puppeteer', 4 | }; 5 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./src/*"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mock/geographic.js: -------------------------------------------------------------------------------- 1 | import city from './geographic/city.json'; 2 | import province from './geographic/province.json'; 3 | 4 | function getProvince(req, res) { 5 | return res.json(province); 6 | } 7 | 8 | function getCity(req, res) { 9 | return res.json(city[req.params.province]); 10 | } 11 | 12 | export default { 13 | 'GET /api/geographic/province': getProvince, 14 | 'GET /api/geographic/city/:province': getCity, 15 | }; 16 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/api/*" 3 | to = "https://us-central1-antd-pro.cloudfunctions.net/api/api/:splat" 4 | status = 200 5 | force = true 6 | [redirects.headers] 7 | X-From = "Netlify" 8 | X-Api-Key = "some-api-key-string" 9 | 10 | [[redirects]] 11 | from = "/*" 12 | to = "/index.html" 13 | status = 200 -------------------------------------------------------------------------------- /public/ant.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/ant.jpeg -------------------------------------------------------------------------------- /public/ant1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/ant1.jpeg -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/favicon.png -------------------------------------------------------------------------------- /public/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/image/braft-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/braft-editor.png -------------------------------------------------------------------------------- /public/image/center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/center.png -------------------------------------------------------------------------------- /public/image/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/drag.png -------------------------------------------------------------------------------- /public/image/react-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/react-color.png -------------------------------------------------------------------------------- /public/image/scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/scroll.png -------------------------------------------------------------------------------- /public/image/select-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/select-tree.png -------------------------------------------------------------------------------- /public/image/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/star.png -------------------------------------------------------------------------------- /public/image/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/public/image/table.png -------------------------------------------------------------------------------- /scripts/generateMock.js: -------------------------------------------------------------------------------- 1 | const generateMock = require('merge-umi-mock-data'); 2 | const path = require('path'); 3 | generateMock(path.join(__dirname, '../mock'), path.join(__dirname, '../functions/mock/index.js')); 4 | -------------------------------------------------------------------------------- /scripts/getPrettierFiles.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob'); 2 | 3 | const getPrettierFiles = () => { 4 | let files = []; 5 | const jsFiles = glob.sync('src/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] }); 6 | const tsFiles = glob.sync('src/**/*.ts*', { ignore: ['**/node_modules/**', 'build/**'] }); 7 | const configFiles = glob.sync('config/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] }); 8 | const scriptFiles = glob.sync('scripts/**/*.js'); 9 | const lessFiles = glob.sync('src/**/*.less*', { ignore: ['**/node_modules/**', 'build/**'] }); 10 | files = files.concat(jsFiles); 11 | files = files.concat(tsFiles); 12 | files = files.concat(configFiles); 13 | files = files.concat(scriptFiles); 14 | files = files.concat(lessFiles); 15 | if (!files.length) { 16 | return; 17 | } 18 | return files; 19 | }; 20 | 21 | module.exports = getPrettierFiles; 22 | -------------------------------------------------------------------------------- /scripts/prettier.js: -------------------------------------------------------------------------------- 1 | /** 2 | * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js 3 | * prettier api doc https://prettier.io/docs/en/api.html 4 | *----------*****-------------- 5 | * prettier all js and all ts. 6 | *----------*****-------------- 7 | */ 8 | 9 | const glob = require('glob'); 10 | const prettier = require('prettier'); 11 | const fs = require('fs'); 12 | const getPrettierFiles = require('./getPrettierFiles'); 13 | const prettierConfigPath = require.resolve('../.prettierrc'); 14 | 15 | let didError = false; 16 | 17 | const files = getPrettierFiles(); 18 | 19 | files.forEach(file => { 20 | const options = prettier.resolveConfig.sync(file, { 21 | config: prettierConfigPath, 22 | }); 23 | const fileInfo = prettier.getFileInfo.sync(file); 24 | if (fileInfo.ignored) { 25 | return; 26 | } 27 | try { 28 | const input = fs.readFileSync(file, 'utf8'); 29 | const withParserOptions = { 30 | ...options, 31 | parser: fileInfo.inferredParser, 32 | }; 33 | const output = prettier.format(input, withParserOptions); 34 | if (output !== input) { 35 | fs.writeFileSync(file, output, 'utf8'); 36 | console.log(`\x1b[34m ${file} is prettier`); 37 | } 38 | } catch (e) { 39 | didError = true; 40 | } 41 | }); 42 | 43 | if (didError) { 44 | process.exit(1); 45 | } 46 | console.log('\x1b[32m prettier success!'); 47 | -------------------------------------------------------------------------------- /src/components/ActiveChart/index.less: -------------------------------------------------------------------------------- 1 | .activeChart { 2 | position: relative; 3 | } 4 | .activeChartGrid { 5 | p { 6 | position: absolute; 7 | top: 80px; 8 | } 9 | p:last-child { 10 | top: 115px; 11 | } 12 | } 13 | .activeChartLegend { 14 | position: relative; 15 | font-size: 0; 16 | margin-top: 8px; 17 | height: 20px; 18 | line-height: 20px; 19 | span { 20 | display: inline-block; 21 | font-size: 12px; 22 | text-align: center; 23 | width: 33.33%; 24 | } 25 | span:first-child { 26 | text-align: left; 27 | } 28 | span:last-child { 29 | text-align: right; 30 | } 31 | } 32 | .dashedLine { 33 | position: relative; 34 | height: 1px; 35 | top: -70px; 36 | left: -3px; 37 | 38 | .line { 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | width: 100%; 43 | height: 100%; 44 | background-image: linear-gradient(to right, transparent 50%, #e9e9e9 50%); 45 | background-size: 6px; 46 | } 47 | } 48 | 49 | .dashedLine:last-child { 50 | top: -36px; 51 | } 52 | -------------------------------------------------------------------------------- /src/components/AntTableFinder/AntTableFinder.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .tableList { 4 | .tableListOperator { 5 | margin-bottom: 16px; 6 | button { 7 | margin-right: 8px; 8 | } 9 | } 10 | } 11 | .rowBackground{ 12 | background: #f0f0f0; 13 | } 14 | .tableListForm { 15 | :global { 16 | .ant-form-item { 17 | margin-bottom: 14px; 18 | margin-right: 0; 19 | display: flex; 20 | > .ant-form-item-label { 21 | width: auto; 22 | line-height: 32px; 23 | padding-right: 8px; 24 | } 25 | .ant-form-item-control { 26 | line-height: 32px; 27 | } 28 | } 29 | .ant-form-item-control-wrapper { 30 | flex: 1; 31 | } 32 | } 33 | .submitButtons { 34 | display: block; 35 | white-space: nowrap; 36 | margin-bottom: 14px; 37 | } 38 | } 39 | 40 | .td_ellipsis{ 41 | text-overflow: ellipsis; 42 | overflow: hidden; 43 | white-space: nowrap; 44 | } 45 | 46 | @media screen and (max-width: @screen-lg) { 47 | .tableListForm :global(.ant-form-item) { 48 | margin-right: 24px; 49 | } 50 | } 51 | 52 | @media screen and (max-width: @screen-md) { 53 | .tableListForm :global(.ant-form-item) { 54 | margin-right: 8px; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/AntTableFinder/AntTableFinder.md: -------------------------------------------------------------------------------- 1 | #Table 2 | 3 | 基于 `ant-design` 封装的多功能 `table` 4 | 5 | ##功能列表 6 | 7 | - -------------------------------------------------------------------------------- /src/components/ArticleListContent/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import moment from 'moment'; 3 | import { Avatar } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | const ArticleListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => ( 7 |
8 |
{content}
9 |
10 | 11 | {owner} 发布在 {href} 12 | {moment(updatedAt).format('YYYY-MM-DD HH:mm')} 13 |
14 |
15 | ); 16 | 17 | export default ArticleListContent; 18 | -------------------------------------------------------------------------------- /src/components/ArticleListContent/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .listContent { 4 | .description { 5 | line-height: 22px; 6 | max-width: 720px; 7 | } 8 | .extra { 9 | color: @text-color-secondary; 10 | margin-top: 16px; 11 | line-height: 22px; 12 | & > :global(.ant-avatar) { 13 | vertical-align: top; 14 | margin-right: 8px; 15 | width: 20px; 16 | height: 20px; 17 | position: relative; 18 | top: 1px; 19 | } 20 | & > em { 21 | color: @disabled-color; 22 | font-style: normal; 23 | margin-left: 16px; 24 | } 25 | } 26 | } 27 | 28 | @media screen and (max-width: @screen-xs) { 29 | .listContent { 30 | .extra { 31 | & > em { 32 | display: block; 33 | margin-left: 0; 34 | margin-top: 8px; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Authorized/Authorized.js: -------------------------------------------------------------------------------- 1 | import CheckPermissions from './CheckPermissions'; 2 | 3 | const Authorized = ({ children, authority, noMatch = null }) => { 4 | const childrenRender = typeof children === 'undefined' ? null : children; 5 | return CheckPermissions(authority, childrenRender, noMatch); 6 | }; 7 | 8 | export default Authorized; 9 | -------------------------------------------------------------------------------- /src/components/Authorized/AuthorizedRoute.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { RouteProps } from 'react-router'; 3 | 4 | type authorityFN = (currentAuthority?: string) => boolean; 5 | 6 | type authority = string | string[] | authorityFN | Promise; 7 | 8 | export interface IAuthorizedRouteProps extends RouteProps { 9 | authority: authority; 10 | } 11 | export { authority }; 12 | 13 | export class AuthorizedRoute extends React.Component {} 14 | -------------------------------------------------------------------------------- /src/components/Authorized/AuthorizedRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import Authorized from './Authorized'; 4 | 5 | // TODO: umi只会返回render和rest 6 | const AuthorizedRoute = ({ component: Component, render, authority, redirectPath, ...rest }) => ( 7 | } />} 10 | > 11 | (Component ? : render(props))} /> 12 | 13 | ); 14 | 15 | export default AuthorizedRoute; 16 | -------------------------------------------------------------------------------- /src/components/Authorized/demo/AuthorizedArray.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | title: 4 | zh-CN: 使用数组作为参数 5 | en-US: Use Array as a parameter 6 | --- 7 | 8 | Use Array as a parameter 9 | 10 | ```jsx 11 | import RenderAuthorized from 'ant-design-pro/lib/Authorized'; 12 | import { Alert } from 'antd'; 13 | 14 | const Authorized = RenderAuthorized('user'); 15 | const noMatch = ; 16 | 17 | ReactDOM.render( 18 | 19 | 20 | , 21 | mountNode, 22 | ); 23 | ``` 24 | -------------------------------------------------------------------------------- /src/components/Authorized/demo/AuthorizedFunction.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | title: 4 | zh-CN: 使用方法作为参数 5 | en-US: Use function as a parameter 6 | --- 7 | 8 | Use Function as a parameter 9 | 10 | ```jsx 11 | import RenderAuthorized from 'ant-design-pro/lib/Authorized'; 12 | import { Alert } from 'antd'; 13 | 14 | const Authorized = RenderAuthorized('user'); 15 | const noMatch = ; 16 | 17 | const havePermission = () => { 18 | return false; 19 | }; 20 | 21 | ReactDOM.render( 22 | 23 | 28 | , 29 | mountNode, 30 | ); 31 | ``` 32 | -------------------------------------------------------------------------------- /src/components/Authorized/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 基本使用 5 | en-US: ColoredScrollbars use 6 | --- 7 | 8 | ColoredScrollbars use 9 | 10 | ```jsx 11 | import RenderAuthorized from 'ant-design-pro/lib/Authorized'; 12 | import { Alert } from 'antd'; 13 | 14 | const Authorized = RenderAuthorized('user'); 15 | const noMatch = ; 16 | 17 | ReactDOM.render( 18 |
19 | 20 | 21 | 22 |
, 23 | mountNode, 24 | ); 25 | ``` 26 | -------------------------------------------------------------------------------- /src/components/Authorized/demo/secured.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 3 3 | title: 4 | zh-CN: 注解基本使用 5 | en-US: ColoredScrollbars use secured 6 | --- 7 | 8 | secured demo used 9 | 10 | ```jsx 11 | import RenderAuthorized from 'ant-design-pro/lib/Authorized'; 12 | import { Alert } from 'antd'; 13 | 14 | const { Secured } = RenderAuthorized('user'); 15 | 16 | @Secured('admin') 17 | class TestSecuredString extends React.Component { 18 | render() { 19 | ; 20 | } 21 | } 22 | ReactDOM.render( 23 |
24 | 25 |
, 26 | mountNode, 27 | ); 28 | ``` 29 | -------------------------------------------------------------------------------- /src/components/Authorized/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import AuthorizedRoute, { authority } from './AuthorizedRoute'; 3 | export type IReactComponent

= 4 | | React.StatelessComponent

5 | | React.ComponentClass

6 | | React.ClassicComponentClass

; 7 | 8 | type Secured = ( 9 | authority: authority, 10 | error?: React.ReactNode 11 | ) => (target: T) => T; 12 | 13 | type check = ( 14 | authority: authority, 15 | target: T, 16 | Exception: S 17 | ) => T | S; 18 | 19 | export interface IAuthorizedProps { 20 | authority: authority; 21 | noMatch?: React.ReactNode; 22 | } 23 | 24 | export class Authorized extends React.Component { 25 | public static Secured: Secured; 26 | public static AuthorizedRoute: typeof AuthorizedRoute; 27 | public static check: check; 28 | } 29 | 30 | declare function renderAuthorize(currentAuthority: string): typeof Authorized; 31 | 32 | export default renderAuthorize; 33 | -------------------------------------------------------------------------------- /src/components/Authorized/index.js: -------------------------------------------------------------------------------- 1 | import Authorized from './Authorized'; 2 | import AuthorizedRoute from './AuthorizedRoute'; 3 | import Secured from './Secured'; 4 | import check from './CheckPermissions'; 5 | import renderAuthorize from './renderAuthorize'; 6 | 7 | Authorized.Secured = Secured; 8 | Authorized.AuthorizedRoute = AuthorizedRoute; 9 | Authorized.check = check; 10 | 11 | export default renderAuthorize(Authorized); 12 | -------------------------------------------------------------------------------- /src/components/Authorized/renderAuthorize.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-mutable-exports */ 2 | let CURRENT = 'NULL'; 3 | /** 4 | * use authority or getAuthority 5 | * @param {string|()=>String} currentAuthority 6 | */ 7 | const renderAuthorize = Authorized => currentAuthority => { 8 | if (currentAuthority) { 9 | if (typeof currentAuthority === 'function') { 10 | CURRENT = currentAuthority(); 11 | } 12 | if ( 13 | Object.prototype.toString.call(currentAuthority) === '[object String]' || 14 | Array.isArray(currentAuthority) 15 | ) { 16 | CURRENT = currentAuthority; 17 | } 18 | } else { 19 | CURRENT = 'NULL'; 20 | } 21 | return Authorized; 22 | }; 23 | 24 | export { CURRENT }; 25 | export default Authorized => renderAuthorize(Authorized); 26 | -------------------------------------------------------------------------------- /src/components/AvatarList/AvatarItem.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IAvatarItemProps { 3 | tips: React.ReactNode; 4 | src: string; 5 | style?: React.CSSProperties; 6 | } 7 | 8 | export default class AvatarItem extends React.Component { 9 | constructor(props: IAvatarItemProps); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/AvatarList/demo/maxLength.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 要显示的最大项目 5 | en-US: Max Items to Show 6 | --- 7 | 8 | `maxLength` attribute specifies the maximum number of items to show while `excessItemsStyle` style the excess 9 | item component. 10 | 11 | ````jsx 12 | import AvatarList from 'ant-design-pro/lib/AvatarList'; 13 | 14 | ReactDOM.render( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | , mountNode); 24 | ```` 25 | -------------------------------------------------------------------------------- /src/components/AvatarList/demo/simple.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 基础样例 5 | en-US: ColoredScrollbars Usage 6 | --- 7 | 8 | Simplest of usage. 9 | 10 | ````jsx 11 | import AvatarList from 'ant-design-pro/lib/AvatarList'; 12 | 13 | ReactDOM.render( 14 | 15 | 16 | 17 | 18 | 19 | , mountNode); 20 | ```` 21 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import AvatarItem from './AvatarItem'; 3 | 4 | export interface IAvatarListProps { 5 | size?: 'large' | 'small' | 'mini' | 'default'; 6 | maxLength?: number; 7 | excessItemsStyle?: React.CSSProperties; 8 | style?: React.CSSProperties; 9 | children: React.ReactElement | Array>; 10 | } 11 | 12 | export default class AvatarList extends React.Component { 13 | public static Item: typeof AvatarItem; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AvatarList 3 | order: 1 4 | cols: 1 5 | --- 6 | 7 | A list of user's avatar for project or group member list frequently. If a large or small AvatarList is desired, set the `size` property to either `large` or `small` and `mini` respectively. Omit the `size` property for a AvatarList with the default size. 8 | 9 | ## API 10 | 11 | ### AvatarList 12 | 13 | | Property | Description | Type | Default | 14 | | ---------------- | --------------------- | ---------------------------------- | --------- | 15 | | size | size of list | `large`、`small` 、`mini`, `default` | `default` | 16 | | maxLength | max items to show | number | - | 17 | | excessItemsStyle | the excess item style | CSSProperties | - | 18 | 19 | ### AvatarList.Item 20 | 21 | | Property | Description | Type | Default | 22 | | -------- | -------------------------------------------- | --------- | ------- | 23 | | tips | title tips for avatar item | ReactNode | - | 24 | | src | the address of the image for an image avatar | string | - | 25 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .avatarList { 4 | display: inline-block; 5 | ul { 6 | display: inline-block; 7 | margin-left: 8px; 8 | font-size: 0; 9 | } 10 | } 11 | 12 | .avatarItem { 13 | display: inline-block; 14 | font-size: @font-size-base; 15 | margin-left: -8px; 16 | width: @avatar-size-base; 17 | height: @avatar-size-base; 18 | :global { 19 | .ant-avatar { 20 | border: 1px solid #fff; 21 | } 22 | } 23 | } 24 | 25 | .avatarItemLarge { 26 | width: @avatar-size-lg; 27 | height: @avatar-size-lg; 28 | } 29 | 30 | .avatarItemSmall { 31 | width: @avatar-size-sm; 32 | height: @avatar-size-sm; 33 | } 34 | 35 | .avatarItemMini { 36 | width: 20px; 37 | height: 20px; 38 | :global { 39 | .ant-avatar { 40 | width: 20px; 41 | height: 20px; 42 | line-height: 20px; 43 | 44 | .ant-avatar-string { 45 | font-size: 12px; 46 | line-height: 18px; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import range from 'lodash/range'; 3 | import { mount } from 'enzyme'; 4 | import AvatarList from './index'; 5 | 6 | const renderItems = numItems => 7 | range(numItems).map(i => ( 8 | 13 | )); 14 | 15 | describe('AvatarList', () => { 16 | it('renders all items', () => { 17 | const wrapper = mount({renderItems(4)}); 18 | expect(wrapper.find('AvatarList').length).toBe(1); 19 | expect(wrapper.find('Item').length).toBe(4); 20 | expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(0); 21 | }); 22 | 23 | it('renders max of 3 items', () => { 24 | const wrapper = mount({renderItems(4)}); 25 | expect(wrapper.find('AvatarList').length).toBe(1); 26 | expect(wrapper.find('Item').length).toBe(3); 27 | expect(wrapper.findWhere(node => node.key() === 'exceed').length).toBe(1); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AvatarList 3 | subtitle: 用户头像列表 4 | order: 1 5 | cols: 1 6 | --- 7 | 8 | 一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。 9 | 10 | ## API 11 | 12 | ### AvatarList 13 | 14 | | 参数 | 说明 | 类型 | 默认值 | 15 | | ---------------- | -------- | ---------------------------------- | --------- | 16 | | size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` | 17 | | maxLength | 要显示的最大项目 | number | - | 18 | | excessItemsStyle | 多余的项目风格 | CSSProperties | - | 19 | 20 | ### AvatarList.Item 21 | 22 | | 参数 | 说明 | 类型 | 默认值 | 23 | | ---- | ------ | --------- | --- | 24 | | tips | 头像展示文案 | ReactNode | - | 25 | | src | 头像图片连接 | string | - | 26 | -------------------------------------------------------------------------------- /src/components/Charts/Bar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IBarProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | padding?: [number, number, number, number]; 6 | height: number; 7 | data: Array<{ 8 | x: string; 9 | y: number; 10 | }>; 11 | autoLabel?: boolean; 12 | style?: React.CSSProperties; 13 | } 14 | 15 | export default class Bar extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/components/Charts/ChartCard/index.d.ts: -------------------------------------------------------------------------------- 1 | import { CardProps } from 'antd/lib/card'; 2 | import * as React from 'react'; 3 | 4 | export interface IChartCardProps extends CardProps { 5 | title: React.ReactNode; 6 | action?: React.ReactNode; 7 | total?: React.ReactNode | number | (() => React.ReactNode | number); 8 | footer?: React.ReactNode; 9 | contentHeight?: number; 10 | avatar?: React.ReactNode; 11 | style?: React.CSSProperties; 12 | } 13 | 14 | export default class ChartCard extends React.Component {} 15 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IFieldProps { 3 | label: React.ReactNode; 4 | value: React.ReactNode; 5 | style?: React.CSSProperties; 6 | } 7 | 8 | export default class Field extends React.Component {} 9 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './index.less'; 4 | 5 | const Field = ({ label, value, ...rest }) => ( 6 |

7 | {label} 8 | {value} 9 |
10 | ); 11 | 12 | export default Field; 13 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .field { 4 | white-space: nowrap; 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | margin: 0; 8 | .label, 9 | .number { 10 | font-size: @font-size-base; 11 | line-height: 22px; 12 | } 13 | .number { 14 | color: @heading-color; 15 | margin-left: 8px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Charts/Gauge/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IGaugeProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | height: number; 6 | bgColor?: number; 7 | percent: number; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | export default class Gauge extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/Charts/MiniArea/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | // g2已经更新到3.0 4 | // 不带的写了 5 | 6 | export interface IAxis { 7 | title: any; 8 | line: any; 9 | gridAlign: any; 10 | labels: any; 11 | tickLine: any; 12 | grid: any; 13 | } 14 | 15 | export interface IMiniAreaProps { 16 | color?: string; 17 | height: number; 18 | borderColor?: string; 19 | line?: boolean; 20 | animate?: boolean; 21 | xAxis?: IAxis; 22 | yAxis?: IAxis; 23 | data: Array<{ 24 | x: number | string; 25 | y: number; 26 | }>; 27 | } 28 | 29 | export default class MiniArea extends React.Component {} 30 | -------------------------------------------------------------------------------- /src/components/Charts/MiniBar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IMiniBarProps { 3 | color?: string; 4 | height: number; 5 | data: Array<{ 6 | x: number | string; 7 | y: number; 8 | }>; 9 | style?: React.CSSProperties; 10 | } 11 | 12 | export default class MiniBar extends React.Component {} 13 | -------------------------------------------------------------------------------- /src/components/Charts/MiniBar/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Chart, Tooltip, Geom } from 'bizcharts'; 3 | import autoHeight from '../autoHeight'; 4 | import styles from '../index.less'; 5 | 6 | @autoHeight() 7 | class MiniBar extends React.Component { 8 | render() { 9 | const { height, forceFit = true, color = '#1890FF', data = [] } = this.props; 10 | 11 | const scale = { 12 | x: { 13 | type: 'cat', 14 | }, 15 | y: { 16 | min: 0, 17 | }, 18 | }; 19 | 20 | const padding = [36, 5, 30, 5]; 21 | 22 | const tooltip = [ 23 | 'x*y', 24 | (x, y) => ({ 25 | name: x, 26 | value: y, 27 | }), 28 | ]; 29 | 30 | // for tooltip not to be hide 31 | const chartHeight = height + 54; 32 | 33 | return ( 34 |
35 |
36 | 43 | 44 | 45 | 46 |
47 |
48 | ); 49 | } 50 | } 51 | export default MiniBar; 52 | -------------------------------------------------------------------------------- /src/components/Charts/MiniProgress/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IMiniProgressProps { 3 | target: number; 4 | color?: string; 5 | strokeWidth?: number; 6 | percent?: number; 7 | style?: React.CSSProperties; 8 | } 9 | 10 | export default class MiniProgress extends React.Component {} 11 | -------------------------------------------------------------------------------- /src/components/Charts/MiniProgress/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Tooltip } from 'antd'; 3 | 4 | import styles from './index.less'; 5 | 6 | const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => ( 7 |
8 | 9 |
10 | 11 | 12 |
13 |
14 |
15 |
23 |
24 |
25 | ); 26 | 27 | export default MiniProgress; 28 | -------------------------------------------------------------------------------- /src/components/Charts/MiniProgress/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .miniProgress { 4 | padding: 5px 0; 5 | position: relative; 6 | width: 100%; 7 | .progressWrap { 8 | background-color: @background-color-base; 9 | position: relative; 10 | } 11 | .progress { 12 | transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s; 13 | border-radius: 1px 0 0 1px; 14 | background-color: @primary-color; 15 | width: 0; 16 | height: 100%; 17 | } 18 | .target { 19 | position: absolute; 20 | top: 0; 21 | bottom: 0; 22 | span { 23 | border-radius: 100px; 24 | position: absolute; 25 | top: 0; 26 | left: 0; 27 | height: 4px; 28 | width: 2px; 29 | } 30 | span:last-child { 31 | top: auto; 32 | bottom: 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Charts/Pie/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IPieProps { 3 | animate?: boolean; 4 | color?: string; 5 | colors?: string[]; 6 | height: number; 7 | hasLegend?: boolean; 8 | padding?: [number, number, number, number]; 9 | percent?: number; 10 | data?: Array<{ 11 | x: string | string; 12 | y: number; 13 | }>; 14 | total?: React.ReactNode | number | (() => React.ReactNode | number); 15 | title?: React.ReactNode; 16 | tooltip?: boolean; 17 | valueFormat?: (value: string) => string | React.ReactNode; 18 | subTitle?: React.ReactNode; 19 | } 20 | 21 | export default class Pie extends React.Component {} 22 | -------------------------------------------------------------------------------- /src/components/Charts/Radar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IRadarProps { 3 | title?: React.ReactNode; 4 | height: number; 5 | padding?: [number, number, number, number]; 6 | hasLegend?: boolean; 7 | data: Array<{ 8 | name: string; 9 | label: string; 10 | value: string; 11 | }>; 12 | style?: React.CSSProperties; 13 | } 14 | 15 | export default class Radar extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/components/Charts/Radar/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .radar { 4 | .legend { 5 | margin-top: 16px; 6 | .legendItem { 7 | position: relative; 8 | text-align: center; 9 | cursor: pointer; 10 | color: @text-color-secondary; 11 | line-height: 22px; 12 | p { 13 | margin: 0; 14 | } 15 | h6 { 16 | color: @heading-color; 17 | padding-left: 16px; 18 | font-size: 24px; 19 | line-height: 32px; 20 | margin-top: 4px; 21 | margin-bottom: 0; 22 | } 23 | &:after { 24 | background-color: @border-color-split; 25 | position: absolute; 26 | top: 8px; 27 | right: 0; 28 | height: 40px; 29 | width: 1px; 30 | content: ''; 31 | } 32 | } 33 | > :last-child .legendItem:after { 34 | display: none; 35 | } 36 | .dot { 37 | border-radius: 6px; 38 | display: inline-block; 39 | margin-right: 6px; 40 | position: relative; 41 | top: -1px; 42 | height: 6px; 43 | width: 6px; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Charts/TagCloud/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface ITagCloudProps { 3 | data: Array<{ 4 | name: string; 5 | value: number; 6 | }>; 7 | height: number; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | export default class TagCloud extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/Charts/TagCloud/index.less: -------------------------------------------------------------------------------- 1 | .tagCloud { 2 | overflow: hidden; 3 | canvas { 4 | transform-origin: 0 0; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Charts/TimelineChart/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface ITimelineChartProps { 3 | data: Array<{ 4 | x: number; 5 | y1: number; 6 | y2?: number; 7 | }>; 8 | titleMap: { y1: string; y2?: string }; 9 | padding?: [number, number, number, number]; 10 | height?: number; 11 | style?: React.CSSProperties; 12 | } 13 | 14 | export default class TimelineChart extends React.Component {} 15 | -------------------------------------------------------------------------------- /src/components/Charts/TimelineChart/index.less: -------------------------------------------------------------------------------- 1 | .timelineChart { 2 | background: #fff; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Charts/WaterWave/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IWaterWaveProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | height: number; 6 | percent: number; 7 | style?: React.CSSProperties; 8 | } 9 | 10 | export default class WaterWave extends React.Component {} 11 | -------------------------------------------------------------------------------- /src/components/Charts/WaterWave/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .waterWave { 4 | display: inline-block; 5 | position: relative; 6 | transform-origin: left; 7 | .text { 8 | position: absolute; 9 | left: 0; 10 | top: 32px; 11 | text-align: center; 12 | width: 100%; 13 | span { 14 | color: @text-color-secondary; 15 | font-size: 14px; 16 | line-height: 22px; 17 | } 18 | h4 { 19 | color: @heading-color; 20 | line-height: 32px; 21 | font-size: 24px; 22 | } 23 | } 24 | .waterWaveCanvasWrapper { 25 | transform: scale(0.5); 26 | transform-origin: 0 0; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Charts/bizcharts.d.ts: -------------------------------------------------------------------------------- 1 | import * as BizChart from 'bizcharts'; 2 | 3 | export = BizChart; 4 | -------------------------------------------------------------------------------- /src/components/Charts/bizcharts.js: -------------------------------------------------------------------------------- 1 | import * as BizChart from 'bizcharts'; 2 | 3 | export default BizChart; 4 | -------------------------------------------------------------------------------- /src/components/Charts/demo/bar.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 4 3 | title: 柱状图 4 | --- 5 | 6 | 通过设置 `x`,`y` 属性,可以快速的构建出一个漂亮的柱状图,各种纬度的关系则是通过自定义的数据展现。 7 | 8 | ````jsx 9 | import { Bar } from 'ant-design-pro/lib/Charts'; 10 | 11 | const salesData = []; 12 | for (let i = 0; i < 12; i += 1) { 13 | salesData.push({ 14 | x: `${i + 1}月`, 15 | y: Math.floor(Math.random() * 1000) + 200, 16 | }); 17 | } 18 | 19 | ReactDOM.render( 20 | 25 | , mountNode); 26 | ```` 27 | -------------------------------------------------------------------------------- /src/components/Charts/demo/gauge.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 7 3 | title: 仪表盘 4 | --- 5 | 6 | 仪表盘是一种进度展示方式,可以更直观的展示当前的进展情况,通常也可表示占比。 7 | 8 | ````jsx 9 | import { Gauge } from 'ant-design-pro/lib/Charts'; 10 | 11 | ReactDOM.render( 12 | 17 | , mountNode); 18 | ```` 19 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-area.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | col: 2 4 | title: 迷你区域图 5 | --- 6 | 7 | ````jsx 8 | import { MiniArea } from 'ant-design-pro/lib/Charts'; 9 | import moment from 'moment'; 10 | 11 | const visitData = []; 12 | const beginDay = new Date().getTime(); 13 | for (let i = 0; i < 20; i += 1) { 14 | visitData.push({ 15 | x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'), 16 | y: Math.floor(Math.random() * 100) + 10, 17 | }); 18 | } 19 | 20 | ReactDOM.render( 21 | 27 | , mountNode); 28 | ```` 29 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-bar.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | col: 2 4 | title: 迷你柱状图 5 | --- 6 | 7 | 迷你柱状图更适合展示简单的区间数据,简洁的表现方式可以很好的减少大数据量的视觉展现压力。 8 | 9 | ````jsx 10 | import { MiniBar } from 'ant-design-pro/lib/Charts'; 11 | import moment from 'moment'; 12 | 13 | const visitData = []; 14 | const beginDay = new Date().getTime(); 15 | for (let i = 0; i < 20; i += 1) { 16 | visitData.push({ 17 | x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'), 18 | y: Math.floor(Math.random() * 100) + 10, 19 | }); 20 | } 21 | 22 | ReactDOM.render( 23 | 27 | , mountNode); 28 | ```` 29 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-pie.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 6 3 | title: 迷你饼状图 4 | --- 5 | 6 | 通过简化 `Pie` 属性的设置,可以快速的实现极简的饼状图,可配合 `ChartCard` 组合展 7 | 现更多业务场景。 8 | 9 | ```jsx 10 | import { Pie } from 'ant-design-pro/lib/Charts'; 11 | 12 | ReactDOM.render( 13 | , 14 | mountNode 15 | ); 16 | ``` 17 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-progress.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 3 3 | title: 迷你进度条 4 | --- 5 | 6 | ````jsx 7 | import { MiniProgress } from 'ant-design-pro/lib/Charts'; 8 | 9 | ReactDOM.render( 10 | 11 | , mountNode); 12 | ```` 13 | -------------------------------------------------------------------------------- /src/components/Charts/demo/pie.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 5 3 | title: 饼状图 4 | --- 5 | 6 | ```jsx 7 | import { Pie, yuan } from 'ant-design-pro/lib/Charts'; 8 | 9 | const salesPieData = [ 10 | { 11 | x: '家用电器', 12 | y: 4544, 13 | }, 14 | { 15 | x: '食用酒水', 16 | y: 3321, 17 | }, 18 | { 19 | x: '个护健康', 20 | y: 3113, 21 | }, 22 | { 23 | x: '服饰箱包', 24 | y: 2341, 25 | }, 26 | { 27 | x: '母婴产品', 28 | y: 1231, 29 | }, 30 | { 31 | x: '其他', 32 | y: 1231, 33 | }, 34 | ]; 35 | 36 | ReactDOM.render( 37 | ( 42 | now.y + pre, 0)) 45 | }} 46 | /> 47 | )} 48 | data={salesPieData} 49 | valueFormat={val => } 50 | height={294} 51 | />, 52 | mountNode, 53 | ); 54 | ``` 55 | -------------------------------------------------------------------------------- /src/components/Charts/demo/radar.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 7 3 | title: 雷达图 4 | --- 5 | 6 | ````jsx 7 | import { Radar, ChartCard } from 'ant-design-pro/lib/Charts'; 8 | 9 | const radarOriginData = [ 10 | { 11 | name: '个人', 12 | ref: 10, 13 | koubei: 8, 14 | output: 4, 15 | contribute: 5, 16 | hot: 7, 17 | }, 18 | { 19 | name: '团队', 20 | ref: 3, 21 | koubei: 9, 22 | output: 6, 23 | contribute: 3, 24 | hot: 1, 25 | }, 26 | { 27 | name: '部门', 28 | ref: 4, 29 | koubei: 1, 30 | output: 6, 31 | contribute: 5, 32 | hot: 7, 33 | }, 34 | ]; 35 | const radarData = []; 36 | const radarTitleMap = { 37 | ref: '引用', 38 | koubei: '口碑', 39 | output: '产量', 40 | contribute: '贡献', 41 | hot: '热度', 42 | }; 43 | radarOriginData.forEach((item) => { 44 | Object.keys(item).forEach((key) => { 45 | if (key !== 'name') { 46 | radarData.push({ 47 | name: item.name, 48 | label: radarTitleMap[key], 49 | value: item[key], 50 | }); 51 | } 52 | }); 53 | }); 54 | 55 | ReactDOM.render( 56 | 57 | 62 | 63 | , mountNode); 64 | ```` 65 | -------------------------------------------------------------------------------- /src/components/Charts/demo/tag-cloud.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 9 3 | title: 标签云 4 | --- 5 | 6 | 标签云是一套相关的标签以及与此相应的权重展示方式,一般典型的标签云有 30 至 150 个标签,而权重影响使用的字体大小或其他视觉效果。 7 | 8 | ````jsx 9 | import { TagCloud } from 'ant-design-pro/lib/Charts'; 10 | 11 | const tags = []; 12 | for (let i = 0; i < 50; i += 1) { 13 | tags.push({ 14 | name: `TagClout-Title-${i}`, 15 | value: Math.floor((Math.random() * 50)) + 20, 16 | }); 17 | } 18 | 19 | ReactDOM.render( 20 | 24 | , mountNode); 25 | ```` 26 | -------------------------------------------------------------------------------- /src/components/Charts/demo/timeline-chart.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 9 3 | title: 带有时间轴的图表 4 | --- 5 | 6 | 使用 `TimelineChart` 组件可以实现带有时间轴的柱状图展现,而其中的 `x` 属性,则是时间值的指向,默认最多支持同时展现两个指标,分别是 `y1` 和 `y2`。 7 | 8 | ````jsx 9 | import { TimelineChart } from 'ant-design-pro/lib/Charts'; 10 | 11 | const chartData = []; 12 | for (let i = 0; i < 20; i += 1) { 13 | chartData.push({ 14 | x: (new Date().getTime()) + (1000 * 60 * 30 * i), 15 | y1: Math.floor(Math.random() * 100) + 1000, 16 | y2: Math.floor(Math.random() * 100) + 10, 17 | }); 18 | } 19 | 20 | ReactDOM.render( 21 | 26 | , mountNode); 27 | ```` 28 | -------------------------------------------------------------------------------- /src/components/Charts/demo/waterwave.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 8 3 | title: 水波图 4 | --- 5 | 6 | 水波图是一种比例的展示方式,可以更直观的展示关键值的占比。 7 | 8 | ````jsx 9 | import { WaterWave } from 'ant-design-pro/lib/Charts'; 10 | 11 | ReactDOM.render( 12 |
13 | 18 |
19 | , mountNode); 20 | ```` 21 | -------------------------------------------------------------------------------- /src/components/Charts/g2.js: -------------------------------------------------------------------------------- 1 | // 全局 G2 设置 2 | import { track, setTheme } from 'bizcharts'; 3 | 4 | track(false); 5 | 6 | const config = { 7 | defaultColor: '#1089ff', 8 | shape: { 9 | interval: { 10 | fillOpacity: 1, 11 | }, 12 | }, 13 | }; 14 | 15 | setTheme(config); 16 | -------------------------------------------------------------------------------- /src/components/Charts/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as numeral from 'numeral'; 2 | export { default as ChartCard } from './ChartCard'; 3 | export { default as Bar } from './Bar'; 4 | export { default as Pie } from './Pie'; 5 | export { default as Radar } from './Radar'; 6 | export { default as Gauge } from './Gauge'; 7 | export { default as MiniArea } from './MiniArea'; 8 | export { default as MiniBar } from './MiniBar'; 9 | export { default as MiniProgress } from './MiniProgress'; 10 | export { default as Field } from './Field'; 11 | export { default as WaterWave } from './WaterWave'; 12 | export { default as TagCloud } from './TagCloud'; 13 | export { default as TimelineChart } from './TimelineChart'; 14 | 15 | declare const yuan: (value: number | string) => string; 16 | 17 | export { yuan }; 18 | -------------------------------------------------------------------------------- /src/components/Charts/index.js: -------------------------------------------------------------------------------- 1 | import numeral from 'numeral'; 2 | import './g2'; 3 | import ChartCard from './ChartCard'; 4 | import Bar from './Bar'; 5 | import Pie from './Pie'; 6 | import Radar from './Radar'; 7 | import Gauge from './Gauge'; 8 | import MiniArea from './MiniArea'; 9 | import MiniBar from './MiniBar'; 10 | import MiniProgress from './MiniProgress'; 11 | import Field from './Field'; 12 | import WaterWave from './WaterWave'; 13 | import TagCloud from './TagCloud'; 14 | import TimelineChart from './TimelineChart'; 15 | 16 | const yuan = val => `¥ ${numeral(val).format('0,0')}`; 17 | 18 | const Charts = { 19 | yuan, 20 | Bar, 21 | Pie, 22 | Gauge, 23 | Radar, 24 | MiniBar, 25 | MiniArea, 26 | MiniProgress, 27 | ChartCard, 28 | Field, 29 | WaterWave, 30 | TagCloud, 31 | TimelineChart, 32 | }; 33 | 34 | export { 35 | Charts as default, 36 | yuan, 37 | Bar, 38 | Pie, 39 | Gauge, 40 | Radar, 41 | MiniBar, 42 | MiniArea, 43 | MiniProgress, 44 | ChartCard, 45 | Field, 46 | WaterWave, 47 | TagCloud, 48 | TimelineChart, 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/Charts/index.less: -------------------------------------------------------------------------------- 1 | .miniChart { 2 | position: relative; 3 | width: 100%; 4 | .chartContent { 5 | position: absolute; 6 | bottom: -28px; 7 | width: 100%; 8 | > div { 9 | margin: 0 -5px; 10 | overflow: hidden; 11 | } 12 | } 13 | .chartLoading { 14 | position: absolute; 15 | top: 16px; 16 | left: 50%; 17 | margin-left: -7px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/CountDown/demo/simple.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 基本 5 | en-US: ColoredScrollbars 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 简单的倒计时组件使用。 11 | 12 | ## en-US 13 | 14 | The simplest usage. 15 | 16 | ````jsx 17 | import CountDown from 'ant-design-pro/lib/CountDown'; 18 | 19 | const targetTime = new Date().getTime() + 3900000; 20 | 21 | ReactDOM.render( 22 | 23 | , mountNode); 24 | ```` 25 | -------------------------------------------------------------------------------- /src/components/CountDown/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface ICountDownProps { 3 | format?: (time: number) => void; 4 | target: Date | number; 5 | onEnd?: () => void; 6 | style?: React.CSSProperties; 7 | } 8 | 9 | export default class CountDown extends React.Component {} 10 | -------------------------------------------------------------------------------- /src/components/CountDown/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CountDown 3 | cols: 1 4 | order: 3 5 | --- 6 | 7 | Simple CountDown Component. 8 | 9 | ## API 10 | 11 | | Property | Description | Type | Default | 12 | |----------|------------------------------------------|-------------|-------| 13 | | format | Formatter of time | Function(time) | | 14 | | target | Target time | Date | - | 15 | | onEnd | Countdown to the end callback | funtion | -| 16 | -------------------------------------------------------------------------------- /src/components/CountDown/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CountDown 3 | subtitle: 倒计时 4 | cols: 1 5 | order: 3 6 | --- 7 | 8 | 倒计时组件。 9 | 10 | ## API 11 | 12 | | 参数 | 说明 | 类型 | 默认值 | 13 | |----------|------------------------------------------|-------------|-------| 14 | | format | 时间格式化显示 | Function(time) | | 15 | | target | 目标时间 | Date | - | 16 | | onEnd | 倒计时结束回调 | funtion | -| 17 | -------------------------------------------------------------------------------- /src/components/DescriptionList/Description.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default class Description extends React.Component< 4 | { 5 | term: React.ReactNode; 6 | style?: React.CSSProperties; 7 | }, 8 | any 9 | > {} 10 | -------------------------------------------------------------------------------- /src/components/DescriptionList/Description.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Col } from 'antd'; 4 | import styles from './index.less'; 5 | import responsive from './responsive'; 6 | 7 | const Description = ({ term, column, children, ...restProps }) => ( 8 | 9 | {term &&
{term}
} 10 | {children !== null && children !== undefined &&
{children}
} 11 | 12 | ); 13 | 14 | Description.defaultProps = { 15 | term: '', 16 | }; 17 | 18 | Description.propTypes = { 19 | term: PropTypes.node, 20 | }; 21 | 22 | export default Description; 23 | -------------------------------------------------------------------------------- /src/components/DescriptionList/DescriptionList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import { Row } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | const DescriptionList = ({ 7 | className, 8 | title, 9 | col = 3, 10 | layout = 'horizontal', 11 | gutter = 32, 12 | children, 13 | size, 14 | ...restProps 15 | }) => { 16 | const clsString = classNames(styles.descriptionList, styles[layout], className, { 17 | [styles.small]: size === 'small', 18 | [styles.large]: size === 'large', 19 | }); 20 | const column = col > 4 ? 4 : col; 21 | return ( 22 |
23 | {title ?
{title}
: null} 24 | 25 | {React.Children.map(children, child => 26 | child ? React.cloneElement(child, { column }) : child 27 | )} 28 | 29 |
30 | ); 31 | }; 32 | 33 | export default DescriptionList; 34 | -------------------------------------------------------------------------------- /src/components/DescriptionList/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 基本 5 | en-US: ColoredScrollbars 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 基本描述列表。 11 | 12 | ## en-US 13 | 14 | ColoredScrollbars DescriptionList. 15 | 16 | ````jsx 17 | import DescriptionList from 'ant-design-pro/lib/DescriptionList'; 18 | 19 | const { Description } = DescriptionList; 20 | 21 | ReactDOM.render( 22 | 23 | 24 | A free, open source, cross-platform, 25 | graphical web browser developed by the 26 | Mozilla Corporation and hundreds of 27 | volunteers. 28 | 29 | 30 | A free, open source, cross-platform, 31 | graphical web browser developed by the 32 | Mozilla Corporation and hundreds of 33 | volunteers. 34 | 35 | 36 | A free, open source, cross-platform, 37 | graphical web browser developed by the 38 | Mozilla Corporation and hundreds of 39 | volunteers. 40 | 41 | 42 | , mountNode); 43 | ```` 44 | -------------------------------------------------------------------------------- /src/components/DescriptionList/demo/vertical.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | title: 4 | zh-CN: 垂直型 5 | en-US: Vertical 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 垂直布局。 11 | 12 | ## en-US 13 | 14 | Vertical layout. 15 | 16 | ````jsx 17 | import DescriptionList from 'ant-design-pro/lib/DescriptionList'; 18 | 19 | const { Description } = DescriptionList; 20 | 21 | ReactDOM.render( 22 | 23 | 24 | A free, open source, cross-platform, 25 | graphical web browser developed by the 26 | Mozilla Corporation and hundreds of 27 | volunteers. 28 | 29 | 30 | A free, open source, cross-platform, 31 | graphical web browser developed by the 32 | Mozilla Corporation and hundreds of 33 | volunteers. 34 | 35 | 36 | A free, open source, cross-platform, 37 | graphical web browser developed by the 38 | Mozilla Corporation and hundreds of 39 | volunteers. 40 | 41 | 42 | , mountNode); 43 | ```` 44 | -------------------------------------------------------------------------------- /src/components/DescriptionList/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Description from './Description'; 3 | 4 | export interface IDescriptionListProps { 5 | layout?: 'horizontal' | 'vertical'; 6 | col?: number; 7 | title: React.ReactNode; 8 | gutter?: number; 9 | size?: 'large' | 'small'; 10 | style?: React.CSSProperties; 11 | } 12 | 13 | export default class DescriptionList extends React.Component { 14 | public static Description: typeof Description; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/DescriptionList/index.js: -------------------------------------------------------------------------------- 1 | import DescriptionList from './DescriptionList'; 2 | import Description from './Description'; 3 | 4 | DescriptionList.Description = Description; 5 | export default DescriptionList; 6 | -------------------------------------------------------------------------------- /src/components/DescriptionList/responsive.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 1: { xs: 24 }, 3 | 2: { xs: 24, sm: 12 }, 4 | 3: { xs: 24, sm: 12, md: 8 }, 5 | 4: { xs: 24, sm: 12, md: 6 }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/components/EditableItem/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Input, Icon } from 'antd'; 3 | import styles from './index.less'; 4 | 5 | export default class EditableItem extends PureComponent { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | value: props.value, 10 | editable: false, 11 | }; 12 | } 13 | 14 | handleChange = e => { 15 | const { value } = e.target; 16 | this.setState({ value }); 17 | }; 18 | 19 | check = () => { 20 | this.setState({ editable: false }); 21 | const { value } = this.state; 22 | const { onChange } = this.props; 23 | if (onChange) { 24 | onChange(value); 25 | } 26 | }; 27 | 28 | edit = () => { 29 | this.setState({ editable: true }); 30 | }; 31 | 32 | render() { 33 | const { value, editable } = this.state; 34 | return ( 35 |
36 | {editable ? ( 37 |
38 | 39 | 40 |
41 | ) : ( 42 |
43 | {value || ' '} 44 | 45 |
46 | )} 47 |
48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/EditableItem/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .editableItem { 4 | line-height: @input-height-base; 5 | display: table; 6 | width: 100%; 7 | margin-top: (@font-size-base * @line-height-base - @input-height-base) / 2; 8 | 9 | .wrapper { 10 | display: table-row; 11 | 12 | & > * { 13 | display: table-cell; 14 | } 15 | 16 | & > *:first-child { 17 | width: 85%; 18 | } 19 | 20 | .icon { 21 | cursor: pointer; 22 | text-align: right; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/EditableLinkGroup/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent, createElement } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Button } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | // TODO: 添加逻辑 7 | 8 | class EditableLinkGroup extends PureComponent { 9 | static propTypes = { 10 | links: PropTypes.array, 11 | onAdd: PropTypes.func, 12 | linkElement: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), 13 | }; 14 | 15 | static defaultProps = { 16 | links: [], 17 | onAdd: () => {}, 18 | linkElement: 'a', 19 | }; 20 | 21 | render() { 22 | const { links, linkElement, onAdd } = this.props; 23 | return ( 24 |
25 | {links.map(link => 26 | createElement( 27 | linkElement, 28 | { 29 | key: `linkGroup-item-${link.id || link.title}`, 30 | to: link.href, 31 | href: link.href, 32 | }, 33 | link.title 34 | ) 35 | )} 36 | { 37 | 40 | } 41 |
42 | ); 43 | } 44 | } 45 | 46 | export default EditableLinkGroup; 47 | -------------------------------------------------------------------------------- /src/components/EditableLinkGroup/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .linkGroup { 4 | padding: 20px 0 8px 24px; 5 | font-size: 0; 6 | & > a { 7 | color: @text-color; 8 | display: inline-block; 9 | font-size: @font-size-base; 10 | margin-bottom: 13px; 11 | width: 25%; 12 | &:hover { 13 | color: @primary-color; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Ellipsis/demo/line.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | title: 4 | zh-CN: 按照行数省略 5 | en-US: Truncate according to the number of rows 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 通过设置 `lines` 属性指定最大行数,如果超过这个行数的文本会自动截取。但是在这种模式下所有 `children` 将会被转换成纯文本。 11 | 12 | 并且注意在这种模式下,外容器需要有指定的宽度(或设置自身宽度)。 13 | 14 | ## en-US 15 | 16 | `lines` attribute specifies the maximum number of rows where the text will automatically be truncated when exceeded. In this mode, all children will be converted to plain text. 17 | 18 | Also note that, in this mode, the outer container needs to have a specified width (or set its own width). 19 | 20 | 21 | ````jsx 22 | import Ellipsis from 'ant-design-pro/lib/Ellipsis'; 23 | 24 | const article =

There were injuries alleged in three cases in 2015, and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.

; 25 | 26 | ReactDOM.render( 27 |
28 | {article} 29 |
30 | , mountNode); 31 | ```` 32 | -------------------------------------------------------------------------------- /src/components/Ellipsis/demo/number.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 按照字符数省略 5 | en-US: Truncate according to the number of character 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 通过设置 `length` 属性指定文本最长长度,如果超过这个长度会自动截取。 11 | 12 | ## en-US 13 | 14 | `length` attribute specifies the maximum length where the text will automatically be truncated when exceeded. 15 | 16 | ````jsx 17 | import Ellipsis from 'ant-design-pro/lib/Ellipsis'; 18 | 19 | const article = 'There were injuries alleged in three cases in 2015, and a fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.'; 20 | 21 | ReactDOM.render( 22 |
23 | {article} 24 |

Show Tooltip

25 | {article} 26 |
27 | , mountNode); 28 | ```` 29 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { TooltipProps } from 'antd/lib/tooltip'; 3 | 4 | export interface IEllipsisTooltipProps extends TooltipProps { 5 | title?: undefined; 6 | overlayStyle?: undefined; 7 | } 8 | 9 | export interface IEllipsisProps { 10 | tooltip?: boolean | IEllipsisTooltipProps; 11 | length?: number; 12 | lines?: number; 13 | style?: React.CSSProperties; 14 | className?: string; 15 | fullWidthRecognition?: boolean; 16 | } 17 | 18 | export function getStrFullLength(str: string): number; 19 | export function cutStrByFullLength(str: string, maxLength: number): number; 20 | 21 | export default class Ellipsis extends React.Component {} 22 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ellipsis 3 | cols: 1 4 | order: 10 5 | --- 6 | 7 | When the text is too long, the Ellipsis automatically shortens it according to its length or the maximum number of lines. 8 | 9 | ## API 10 | 11 | Property | Description | Type | Default 12 | ----|------|-----|------ 13 | tooltip | tooltip for showing the full text content when hovering over | boolean | - 14 | length | maximum number of characters in the text before being truncated | number | - 15 | lines | maximum number of rows in the text before being truncated | number | `1` 16 | fullWidthRecognition | whether consider full-width character length as 2 when calculate string length | boolean | - 17 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.less: -------------------------------------------------------------------------------- 1 | .ellipsis { 2 | overflow: hidden; 3 | display: inline-block; 4 | word-break: break-all; 5 | width: 100%; 6 | } 7 | 8 | .lines { 9 | position: relative; 10 | .shadow { 11 | display: block; 12 | position: absolute; 13 | color: transparent; 14 | opacity: 0; 15 | z-index: -999; 16 | } 17 | } 18 | 19 | .lineClamp { 20 | position: relative; 21 | overflow: hidden; 22 | text-overflow: ellipsis; 23 | display: -webkit-box; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.test.js: -------------------------------------------------------------------------------- 1 | import { getStrFullLength, cutStrByFullLength } from './index'; 2 | 3 | describe('test calculateShowLength', () => { 4 | it('get full length', () => { 5 | expect(getStrFullLength('一二,a,')).toEqual(8); 6 | }); 7 | it('cut str by full length', () => { 8 | expect(cutStrByFullLength('一二,a,', 7)).toEqual('一二,a'); 9 | }); 10 | it('cut str when length small', () => { 11 | expect(cutStrByFullLength('一22三', 5)).toEqual('一22'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ellipsis 3 | subtitle: 文本自动省略号 4 | cols: 1 5 | order: 10 6 | --- 7 | 8 | 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 9 | 10 | ## API 11 | 12 | 参数 | 说明 | 类型 | 默认值 13 | ----|------|-----|------ 14 | tooltip | 移动到文本展示完整内容的提示 | boolean | - 15 | length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - 16 | lines | 在按照行数截取下最大的行数,超过则截取省略 | number | `1` 17 | fullWidthRecognition | 是否将全角字符的长度视为2来计算字符串长度 | boolean | - 18 | -------------------------------------------------------------------------------- /src/components/Exception/demo/403.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | title: 4 | zh-CN: 403 5 | en-US: 403 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 403 页面,配合自定义操作。 11 | 12 | ## en-US 13 | 14 | 403 page with custom operations. 15 | 16 | ````jsx 17 | import Exception from 'ant-design-pro/lib/Exception'; 18 | import { Button } from 'antd'; 19 | 20 | const actions = ( 21 |
22 | 23 | 24 |
25 | ); 26 | ReactDOM.render( 27 | 28 | , mountNode); 29 | ```` 30 | -------------------------------------------------------------------------------- /src/components/Exception/demo/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 404 5 | en-US: 404 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 404 页面。 11 | 12 | ## en-US 13 | 14 | 404 page. 15 | 16 | ````jsx 17 | import Exception from 'ant-design-pro/lib/Exception'; 18 | 19 | ReactDOM.render( 20 | 21 | , mountNode); 22 | ```` 23 | -------------------------------------------------------------------------------- /src/components/Exception/demo/500.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | title: 4 | zh-CN: 500 5 | en-US: 500 6 | --- 7 | 8 | ## zh-CN 9 | 10 | 500 页面。 11 | 12 | ## en-US 13 | 14 | 500 page. 15 | 16 | ````jsx 17 | import Exception from 'ant-design-pro/lib/Exception'; 18 | 19 | ReactDOM.render( 20 | 21 | , mountNode); 22 | ```` 23 | -------------------------------------------------------------------------------- /src/components/Exception/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IExceptionProps { 3 | type?: '403' | '404' | '500'; 4 | title?: React.ReactNode; 5 | desc?: React.ReactNode; 6 | img?: string; 7 | actions?: React.ReactNode; 8 | linkElement?: string | React.ComponentType; 9 | style?: React.CSSProperties; 10 | className?: string; 11 | backText?: React.ReactNode; 12 | redirect?: string; 13 | } 14 | 15 | export default class Exception extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/components/Exception/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exception 3 | cols: 1 4 | order: 5 5 | --- 6 | 7 | Exceptions page is used to provide feedback on specific abnormal state. Usually, it contains an explanation of the error status, and provides users with suggestions or operations, to prevent users from feeling lost and confused. 8 | 9 | ## API 10 | 11 | Property | Description | Type | Default 12 | ---------|-------------|------|-------- 13 | | backText | default return button text | ReactNode | back to home | 14 | type | type of exception, the corresponding default `title`, `desc`, `img` will be given if set, which can be overridden by explicit setting of `title`, `desc`, `img` | Enum {'403', '404', '500'} | - 15 | title | title | ReactNode | - 16 | desc | supplementary description | ReactNode | - 17 | img | the url of background image | string | - 18 | actions | suggested operations, a default 'Home' link will show if not set | ReactNode | - 19 | linkElement | to specify the element of link | string\|ReactElement | 'a' 20 | redirect | redirect path | string | '/' -------------------------------------------------------------------------------- /src/components/Exception/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exception 3 | subtitle: 异常 4 | cols: 1 5 | order: 5 6 | --- 7 | 8 | 异常页用于对页面特定的异常状态进行反馈。通常,它包含对错误状态的阐述,并向用户提供建议或操作,避免用户感到迷失和困惑。 9 | 10 | ## API 11 | 12 | | 参数 | 说明| 类型 | 默认值 | 13 | |-------------|------------------------------------------|-------------|-------| 14 | | backText| 默认的返回按钮文本 | ReactNode| back to home | 15 | | type| 页面类型,若配置,则自带对应类型默认的 `title`,`desc`,`img`,此默认设置可以被 `title`,`desc`,`img` 覆盖 | Enum {'403', '404', '500'} | - | 16 | | title | 标题 | ReactNode| -| 17 | | desc| 补充描述| ReactNode| -| 18 | | img | 背景图片地址 | string| -| 19 | | actions | 建议操作,配置此属性时默认的『返回首页』按钮不生效| ReactNode| -| 20 | | linkElement | 定义链接的元素 | string\|ReactElement | 'a' | 21 | | redirect | 返回按钮的跳转地址 | string | '/' 22 | -------------------------------------------------------------------------------- /src/components/Exception/typeConfig.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | 403: { 3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg', 4 | title: '403', 5 | desc: '抱歉,你无权访问该页面', 6 | }, 7 | 404: { 8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg', 9 | title: '404', 10 | desc: '抱歉,你访问的页面不存在', 11 | }, 12 | 500: { 13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg', 14 | title: '500', 15 | desc: '抱歉,服务器出错了', 16 | }, 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 4 | zh-CN: 演示 5 | en-US: demo 6 | iframe: 400 7 | --- 8 | 9 | ## zh-CN 10 | 11 | 浮动固定页脚。 12 | 13 | ## en-US 14 | 15 | Fixed to the footer. 16 | 17 | ````jsx 18 | import FooterToolbar from 'ant-design-pro/lib/FooterToolbar'; 19 | import { Button } from 'antd'; 20 | 21 | ReactDOM.render( 22 |
23 |

Content Content Content Content

24 |

Content Content Content Content

25 |

Content Content Content Content

26 |

Content Content Content Content

27 |

Content Content Content Content

28 |

Content Content Content Content

29 |

Content Content Content Content

30 |

Content Content Content Content

31 |

Content Content Content Content

32 |

Content Content Content Content

33 |

Content Content Content Content

34 |

Content Content Content Content

35 |

Content Content Content Content

36 |

Content Content Content Content

37 |

Content Content Content Content

38 | 39 | 40 | 41 | 42 |
43 | , mountNode); 44 | ```` -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IFooterToolbarProps { 3 | extra: React.ReactNode; 4 | style?: React.CSSProperties; 5 | } 6 | 7 | export default class FooterToolbar extends React.Component {} 8 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FooterToolbar 3 | cols: 1 4 | order: 6 5 | --- 6 | 7 | A toolbar fixed at the bottom. 8 | 9 | ## Usage 10 | 11 | It is fixed at the bottom of the content area and does not move along with the scroll bar, which is usually used for data collection and submission for long pages. 12 | 13 | ## API 14 | 15 | Property | Description | Type | Default 16 | ---------|-------------|------|-------- 17 | children | toolbar content, align to the right | ReactNode | - 18 | extra | extra information, align to the left | ReactNode | - -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .toolbar { 4 | position: fixed; 5 | width: 100%; 6 | bottom: 0; 7 | right: 0; 8 | height: 56px; 9 | line-height: 56px; 10 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03); 11 | background: #fff; 12 | border-top: 1px solid @border-color-split; 13 | padding: 0 24px; 14 | z-index: 9; 15 | 16 | &:after { 17 | content: ''; 18 | display: block; 19 | clear: both; 20 | } 21 | 22 | .left { 23 | float: left; 24 | } 25 | 26 | .right { 27 | float: right; 28 | } 29 | 30 | button + button { 31 | margin-left: 8px; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FooterToolbar 3 | subtitle: 底部工具栏 4 | cols: 1 5 | order: 6 6 | --- 7 | 8 | 固定在底部的工具栏。 9 | 10 | ## 何时使用 11 | 12 | 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。 13 | 14 | ## API 15 | 16 | 参数 | 说明 | 类型 | 默认值 17 | ----|------|-----|------ 18 | children | 工具栏内容,向右对齐 | ReactNode | - 19 | extra | 额外信息,向左对齐 | ReactNode | - 20 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 演示 4 | iframe: 400 5 | --- 6 | 7 | 基本页脚。 8 | 9 | ````jsx 10 | import GlobalFooter from 'ant-design-pro/lib/GlobalFooter'; 11 | import { Icon } from 'antd'; 12 | 13 | const links = [{ 14 | key: '帮助', 15 | title: '帮助', 16 | href: '', 17 | }, { 18 | key: 'github', 19 | title: , 20 | href: 'https://github.com/ant-design/ant-design-pro', 21 | blankTarget: true, 22 | }, { 23 | key: '条款', 24 | title: '条款', 25 | href: '', 26 | blankTarget: true, 27 | }]; 28 | 29 | const copyright =
Copyright 2017 蚂蚁金服体验技术部出品
; 30 | 31 | ReactDOM.render( 32 |
33 |
34 | 35 |
36 | , mountNode); 37 | ```` 38 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IGlobalFooterProps { 3 | links?: Array<{ 4 | key?: string; 5 | title: React.ReactNode; 6 | href: string; 7 | blankTarget?: boolean; 8 | }>; 9 | copyright?: React.ReactNode; 10 | style?: React.CSSProperties; 11 | } 12 | 13 | export default class GlobalFooter extends React.Component {} 14 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import styles from './index.less'; 4 | 5 | const GlobalFooter = ({ className, links, copyright }) => { 6 | const clsString = classNames(styles.globalFooter, className); 7 | return ( 8 |
9 | {links && ( 10 |
11 | {links.map(link => ( 12 | 18 | {link.title} 19 | 20 | ))} 21 |
22 | )} 23 | {copyright &&
{copyright}
} 24 |
25 | ); 26 | }; 27 | 28 | export default GlobalFooter; 29 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .globalFooter { 4 | padding: 0 16px; 5 | margin: 48px 0 24px 0; 6 | text-align: center; 7 | 8 | .links { 9 | margin-bottom: 8px; 10 | 11 | a { 12 | color: @text-color-secondary; 13 | transition: all 0.3s; 14 | 15 | &:not(:last-child) { 16 | margin-right: 40px; 17 | } 18 | 19 | &:hover { 20 | color: @text-color; 21 | } 22 | } 23 | } 24 | 25 | .copyright { 26 | color: @text-color-secondary; 27 | font-size: @font-size-base; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: GlobalFooter 4 | zh-CN: GlobalFooter 5 | subtitle: 全局页脚 6 | cols: 1 7 | order: 7 8 | --- 9 | 10 | 页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。 11 | 12 | ## API 13 | 14 | 参数 | 说明 | 类型 | 默认值 15 | ----|------|-----|------ 16 | links | 链接数据 | array<{ title: ReactNode, href: string, blankTarget?: boolean }> | - 17 | copyright | 版权信息 | ReactNode | - 18 | -------------------------------------------------------------------------------- /src/components/GlobalHeader/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Icon } from 'antd'; 3 | import Link from 'umi/link'; 4 | import Debounce from 'lodash-decorators/debounce'; 5 | import styles from './index.less'; 6 | import RightContent from './RightContent'; 7 | 8 | export default class GlobalHeader extends PureComponent { 9 | componentWillUnmount() { 10 | this.triggerResizeEvent.cancel(); 11 | } 12 | /* eslint-disable*/ 13 | @Debounce(600) 14 | triggerResizeEvent() { 15 | // eslint-disable-line 16 | const event = document.createEvent('HTMLEvents'); 17 | event.initEvent('resize', true, false); 18 | window.dispatchEvent(event); 19 | } 20 | toggle = () => { 21 | const { collapsed, onCollapse } = this.props; 22 | onCollapse(!collapsed); 23 | this.triggerResizeEvent(); 24 | }; 25 | render() { 26 | const { collapsed, isMobile, logo } = this.props; 27 | return ( 28 |
29 | {isMobile && ( 30 | 31 | logo 32 | 33 | )} 34 | 35 | 36 | 37 | 38 |
39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/HeaderDropdown/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Dropdown } from 'antd'; 3 | import classNames from 'classnames'; 4 | import styles from './index.less'; 5 | 6 | export default class HeaderDropdown extends PureComponent { 7 | render() { 8 | const { overlayClassName, ...props } = this.props; 9 | return ( 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/HeaderDropdown/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .container > * { 4 | background-color: #fff; 5 | box-shadow: @shadow-1-down; 6 | border-radius: 4px; 7 | } 8 | 9 | @media screen and (max-width: @screen-xs) { 10 | .container { 11 | width: 100% !important; 12 | } 13 | .container > * { 14 | border-radius: 0 !important; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/HeaderSearch/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 全局搜索 4 | --- 5 | 6 | 通常放置在导航工具条右侧。(点击搜索图标预览效果) 7 | 8 | ````jsx 9 | import HeaderSearch from 'ant-design-pro/lib/HeaderSearch'; 10 | 11 | ReactDOM.render( 12 |
22 | { 26 | console.log('input', value); // eslint-disable-line 27 | }} 28 | onPressEnter={(value) => { 29 | console.log('enter', value); // eslint-disable-line 30 | }} 31 | /> 32 |
33 | , mountNode); 34 | ```` 35 | -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IHeaderSearchProps { 3 | placeholder?: string; 4 | dataSource?: string[]; 5 | defaultOpen?: boolean; 6 | open?: boolean; 7 | onSearch?: (value: string) => void; 8 | onChange?: (value: string) => void; 9 | onVisibleChange?: (visible: boolean) => void; 10 | onPressEnter?: (value: string) => void; 11 | style?: React.CSSProperties; 12 | className?: string; 13 | } 14 | 15 | export default class HeaderSearch extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: HeaderSearch 4 | zh-CN: HeaderSearch 5 | subtitle: Top search box 6 | cols: 1 7 | order: 8 8 | --- 9 | 10 | Usually placed as an entry to the global search, placed on the right side of the navigation toolbar. 11 | 12 | ## API 13 | 14 | 参数 | 说明 | 类型 | 默认值 15 | ----|------|-----|------ 16 | placeholder | placeholder text | string | - 17 | dataSource | current list of prompts | string[] | - 18 | onSearch | Callback when selecting an item or pressing Enter | function(value) | - 19 | onChange | Enter a callback for the search text | function(value) | - 20 | onPressEnter | Callback when pressing Enter | function(value) | - 21 | onVisibleChange | Show or hide the callback of the text box | function(value) |- 22 | defaultOpen | The input box is displayed for the first time. | boolean | false 23 | open | The input box is displayed | booelan |false -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .headerSearch { 4 | :global(.anticon-search) { 5 | cursor: pointer; 6 | font-size: 16px; 7 | } 8 | .input { 9 | transition: width 0.3s, margin-left 0.3s; 10 | width: 0; 11 | background: transparent; 12 | border-radius: 0; 13 | :global(.ant-select-selection) { 14 | background: transparent; 15 | } 16 | input { 17 | border: 0; 18 | padding-left: 0; 19 | padding-right: 0; 20 | box-shadow: none !important; 21 | } 22 | &, 23 | &:hover, 24 | &:focus { 25 | border-bottom: 1px solid @border-color-base; 26 | } 27 | &.show { 28 | width: 210px; 29 | margin-left: 8px; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: HeaderSearch 4 | zh-CN: HeaderSearch 5 | subtitle: 顶部搜索框 6 | cols: 1 7 | order: 8 8 | --- 9 | 10 | 通常作为全局搜索的入口,放置在导航工具条右侧。 11 | 12 | ## API 13 | 14 | 参数 | 说明 | 类型 | 默认值 15 | ----|------|-----|------ 16 | placeholder | 占位文字 | string | - 17 | dataSource | 当前提示内容列表 | string[] | - 18 | onSearch | 选择某项或按下回车时的回调 | function(value) | - 19 | onChange | 输入搜索字符的回调 | function(value) | - 20 | onPressEnter | 按下回车时的回调 | function(value) | - 21 | onVisibleChange | 显示或隐藏文本框的回调 | function(value) |- 22 | defaultOpen | 输入框首次显示是否显示 | boolean | false 23 | open | 控制输入框是否显示 | booelan |false -------------------------------------------------------------------------------- /src/components/Login/LoginItem.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface ILoginItemProps { 3 | name?: string; 4 | rules?: any[]; 5 | style?: React.CSSProperties; 6 | onGetCaptcha?: () => void; 7 | placeholder?: string; 8 | buttonText?: React.ReactNode; 9 | } 10 | 11 | export class LoginItem extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/Login/LoginSubmit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import { Button, Form } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | const FormItem = Form.Item; 7 | 8 | const LoginSubmit = ({ className, ...rest }) => { 9 | const clsString = classNames(styles.submit, className); 10 | return ( 11 | 12 | ; 29 | 30 | ReactDOM.render( 31 | 38 | , mountNode); 39 | ```` 40 | -------------------------------------------------------------------------------- /src/components/Result/demo/structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: Structure 4 | --- 5 | 6 | 结构包含 `处理结果`,`补充信息` 以及 `操作建议` 三个部分,其中 `处理结果` 由 `提示图标`,`标题` 和 `结果描述` 组成。 7 | 8 | ````jsx 9 | import Result from 'ant-design-pro/lib/Result'; 10 | 11 | ReactDOM.render( 12 | 标题
} 15 | description={
结果描述
} 16 | extra="其他补充信息,自带灰底效果" 17 | actions={
操作建议,一般放置按钮组
} 18 | /> 19 | , mountNode); 20 | ```` 21 | -------------------------------------------------------------------------------- /src/components/Result/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface IResultProps { 3 | type: 'success' | 'error'; 4 | title: React.ReactNode; 5 | description?: React.ReactNode; 6 | extra?: React.ReactNode; 7 | actions?: React.ReactNode; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | export default class Result extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/Result/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import { Icon } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | export default function Result({ 7 | className, 8 | type, 9 | title, 10 | description, 11 | extra, 12 | actions, 13 | ...restProps 14 | }) { 15 | const iconMap = { 16 | error: , 17 | success: , 18 | }; 19 | const clsString = classNames(styles.result, className); 20 | return ( 21 |
22 |
{iconMap[type]}
23 |
{title}
24 | {description &&
{description}
} 25 | {extra &&
{extra}
} 26 | {actions &&
{actions}
} 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Result/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .result { 4 | text-align: center; 5 | width: 72%; 6 | margin: 0 auto; 7 | @media screen and (max-width: @screen-xs) { 8 | width: 100%; 9 | } 10 | 11 | .icon { 12 | font-size: 72px; 13 | line-height: 72px; 14 | margin-bottom: 24px; 15 | 16 | & > .success { 17 | color: @success-color; 18 | } 19 | 20 | & > .error { 21 | color: @error-color; 22 | } 23 | } 24 | 25 | .title { 26 | font-size: 24px; 27 | color: @heading-color; 28 | font-weight: 500; 29 | line-height: 32px; 30 | margin-bottom: 16px; 31 | } 32 | 33 | .description { 34 | font-size: 14px; 35 | line-height: 22px; 36 | color: @text-color-secondary; 37 | margin-bottom: 24px; 38 | } 39 | 40 | .extra { 41 | background: #fafafa; 42 | padding: 24px 40px; 43 | border-radius: @border-radius-sm; 44 | text-align: left; 45 | 46 | @media screen and (max-width: @screen-xs) { 47 | padding: 18px 20px; 48 | } 49 | } 50 | 51 | .actions { 52 | margin-top: 32px; 53 | 54 | button:not(:last-child) { 55 | margin-right: 8px; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/Result/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: Result 4 | zh-CN: Result 5 | subtitle: 处理结果 6 | cols: 1 7 | order: 12 8 | --- 9 | 10 | 结果页用于对用户进行的一系列任务处理结果进行反馈。 11 | 12 | ## API 13 | 14 | | 参数 | 说明 | 类型 | 默认值 | 15 | |----------|------------------------------------------|-------------|-------| 16 | | type | 类型,不同类型自带对应的图标 | Enum {'success', 'error'} | - | 17 | | title | 标题 | ReactNode | - | 18 | | description | 结果描述 | ReactNode | - | 19 | | extra | 补充信息,有默认的灰色背景 | ReactNode | - | 20 | | actions | 操作建议,推荐放置跳转链接,按钮组等 | ReactNode | - | 21 | -------------------------------------------------------------------------------- /src/components/SelectLang/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .menu { 4 | :global(.anticon) { 5 | margin-right: 8px; 6 | } 7 | :global(.ant-dropdown-menu-item) { 8 | min-width: 160px; 9 | } 10 | } 11 | 12 | .dropDown { 13 | cursor: pointer; 14 | vertical-align: top; 15 | line-height: @layout-header-height; 16 | > i { 17 | font-size: 14px !important; 18 | transform: none !important; 19 | svg { 20 | position: relative; 21 | top: -1px; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/BlockCheckbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Tooltip, Icon } from 'antd'; 3 | import style from './index.less'; 4 | 5 | const BlockChecbox = ({ value, onChange, list }) => ( 6 |
7 | {list.map(item => ( 8 | 9 |
onChange(item.key)}> 10 | {item.key} 11 |
17 | 18 |
19 |
20 |
21 | ))} 22 |
23 | ); 24 | 25 | export default BlockChecbox; 26 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/ThemeColor.less: -------------------------------------------------------------------------------- 1 | .themeColor { 2 | overflow: hidden; 3 | margin-top: 24px; 4 | .title { 5 | font-size: 14px; 6 | color: rgba(0, 0, 0, 0.65); 7 | line-height: 22px; 8 | margin-bottom: 12px; 9 | } 10 | .colorBlock { 11 | width: 20px; 12 | height: 20px; 13 | border-radius: 2px; 14 | float: left; 15 | cursor: pointer; 16 | margin-right: 8px; 17 | text-align: center; 18 | color: #fff; 19 | font-weight: bold; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/SiderMenu/SiderMenu.test.js: -------------------------------------------------------------------------------- 1 | import { getFlatMenuKeys } from './SiderMenuUtils'; 2 | 3 | const menu = [ 4 | { 5 | path: '/dashboard', 6 | children: [ 7 | { 8 | path: '/dashboard/name', 9 | }, 10 | ], 11 | }, 12 | { 13 | path: '/userinfo', 14 | children: [ 15 | { 16 | path: '/userinfo/:id', 17 | children: [ 18 | { 19 | path: '/userinfo/:id/info', 20 | }, 21 | ], 22 | }, 23 | ], 24 | }, 25 | ]; 26 | 27 | const flatMenuKeys = getFlatMenuKeys(menu); 28 | 29 | describe('test convert nested menu to flat menu', () => { 30 | it('simple menu', () => { 31 | expect(flatMenuKeys).toEqual([ 32 | '/dashboard', 33 | '/dashboard/name', 34 | '/userinfo', 35 | '/userinfo/:id', 36 | '/userinfo/:id/info', 37 | ]); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/components/SiderMenu/SiderMenuUtils.js: -------------------------------------------------------------------------------- 1 | import pathToRegexp from 'path-to-regexp'; 2 | import { urlToList } from '../_utils/pathTools'; 3 | 4 | /** 5 | * Recursively flatten the data 6 | * [{path:string},{path:string}] => {path,path2} 7 | * @param menus 8 | */ 9 | export const getFlatMenuKeys = menuData => { 10 | let keys = []; 11 | menuData.forEach(item => { 12 | keys.push(item.path); 13 | if (item.children) { 14 | keys = keys.concat(getFlatMenuKeys(item.children)); 15 | } 16 | }); 17 | return keys; 18 | }; 19 | 20 | export const getMenuMatches = (flatMenuKeys, path) => 21 | flatMenuKeys.filter(item => { 22 | if (item) { 23 | return pathToRegexp(item).test(path); 24 | } 25 | return false; 26 | }); 27 | /** 28 | * 获得菜单子节点 29 | * @memberof SiderMenu 30 | */ 31 | export const getDefaultCollapsedSubMenus = props => { 32 | const { 33 | location: { pathname }, 34 | flatMenuKeys, 35 | } = props; 36 | return urlToList(pathname) 37 | .map(item => getMenuMatches(flatMenuKeys, item)[0]) 38 | .filter(item => item); 39 | }; 40 | -------------------------------------------------------------------------------- /src/components/SiderMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Drawer } from 'antd'; 3 | import SiderMenu from './SiderMenu'; 4 | import { getFlatMenuKeys } from './SiderMenuUtils'; 5 | 6 | const SiderMenuWrapper = React.memo(props => { 7 | const { isMobile, menuData, collapsed, onCollapse } = props; 8 | const flatMenuKeys = getFlatMenuKeys(menuData); 9 | return isMobile ? ( 10 | onCollapse(true)} 14 | style={{ 15 | padding: 0, 16 | height: '100vh', 17 | }} 18 | > 19 | 20 | 21 | ) : ( 22 | 23 | ); 24 | }); 25 | 26 | export default SiderMenuWrapper; 27 | -------------------------------------------------------------------------------- /src/components/StandardFormRow/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import styles from './index.less'; 4 | 5 | const StandardFormRow = ({ title, children, last, block, grid, ...rest }) => { 6 | const cls = classNames(styles.standardFormRow, { 7 | [styles.standardFormRowBlock]: block, 8 | [styles.standardFormRowLast]: last, 9 | [styles.standardFormRowGrid]: grid, 10 | }); 11 | 12 | return ( 13 |
14 | {title && ( 15 |
16 | {title} 17 |
18 | )} 19 |
{children}
20 |
21 | ); 22 | }; 23 | 24 | export default StandardFormRow; 25 | -------------------------------------------------------------------------------- /src/components/StandardTable/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .standardTable { 4 | :global { 5 | .ant-table-pagination { 6 | margin-top: 24px; 7 | } 8 | } 9 | 10 | .tableAlert { 11 | margin-bottom: 16px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/TagSelect/TagSelectOption.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface ITagSelectOptionProps { 4 | value: string | number; 5 | style?: React.CSSProperties; 6 | } 7 | 8 | export default class TagSelectOption extends React.Component {} 9 | -------------------------------------------------------------------------------- /src/components/TagSelect/demo/controlled.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 3 3 | title: 受控模式 4 | --- 5 | 6 | 结合 `Tag` 的 `TagSelect` 组件,方便的应用于筛选类目的业务场景中。 7 | 8 | ```jsx 9 | import { Button } from 'antd'; 10 | import TagSelect from 'ant-design-pro/lib/TagSelect'; 11 | 12 | class Demo extends React.Component { 13 | state = { 14 | value: ['cat1'], 15 | }; 16 | handleFormSubmit = value => { 17 | this.setState({ 18 | value, 19 | }); 20 | }; 21 | checkAll = () => { 22 | this.setState({ 23 | value: ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6'], 24 | }); 25 | }; 26 | render() { 27 | return ( 28 |
29 | 30 |
35 | 36 | 类目一 37 | 类目二 38 | 类目三 39 | 类目四 40 | 类目五 41 | 类目六 42 | 43 |
44 |
45 | ); 46 | } 47 | } 48 | 49 | ReactDOM.render(, mountNode); 50 | ``` 51 | -------------------------------------------------------------------------------- /src/components/TagSelect/demo/expandable.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | title: 可展开和收起 4 | --- 5 | 6 | 使用 `expandable` 属性,让标签组可以收起,避免过高。 7 | 8 | ````jsx 9 | import TagSelect from 'ant-design-pro/lib/TagSelect'; 10 | 11 | function handleFormSubmit(checkedValue) { 12 | console.log(checkedValue); 13 | } 14 | 15 | ReactDOM.render( 16 | 17 | 类目一 18 | 类目二 19 | 类目三 20 | 类目四 21 | 类目五 22 | 类目六 23 | 类目七 24 | 类目八 25 | 类目九 26 | 类目十 27 | 类目十一 28 | 类目十二 29 | 30 | , mountNode); 31 | ```` 32 | -------------------------------------------------------------------------------- /src/components/TagSelect/demo/simple.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 基础样例 4 | --- 5 | 6 | 结合 `Tag` 的 `TagSelect` 组件,方便的应用于筛选类目的业务场景中。 7 | 8 | ````jsx 9 | import TagSelect from 'ant-design-pro/lib/TagSelect'; 10 | 11 | function handleFormSubmit(checkedValue) { 12 | console.log(checkedValue); 13 | } 14 | 15 | ReactDOM.render( 16 | 17 | 类目一 18 | 类目二 19 | 类目三 20 | 类目四 21 | 类目五 22 | 类目六 23 | 24 | , mountNode); 25 | ```` 26 | -------------------------------------------------------------------------------- /src/components/TagSelect/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import TagSelectOption from './TagSelectOption'; 3 | 4 | export interface ITagSelectProps { 5 | onChange?: (value: string[]) => void; 6 | expandable?: boolean; 7 | value?: string[] | number[]; 8 | style?: React.CSSProperties; 9 | hideCheckAll?: boolean; 10 | } 11 | 12 | export default class TagSelect extends React.Component { 13 | public static Option: typeof TagSelectOption; 14 | private children: 15 | | React.ReactElement 16 | | Array>; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/TagSelect/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .tagSelect { 4 | user-select: none; 5 | margin-left: -8px; 6 | position: relative; 7 | overflow: hidden; 8 | max-height: 32px; 9 | line-height: 32px; 10 | transition: all 0.3s; 11 | :global { 12 | .ant-tag { 13 | padding: 0 8px; 14 | margin-right: 24px; 15 | font-size: @font-size-base; 16 | } 17 | } 18 | &.expanded { 19 | transition: all 0.3s; 20 | max-height: 200px; 21 | } 22 | .trigger { 23 | position: absolute; 24 | top: 0; 25 | right: 0; 26 | i { 27 | font-size: 12px; 28 | } 29 | } 30 | &.hasExpandTag { 31 | padding-right: 50px; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/TagSelect/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: TagSelect 4 | zh-CN: TagSelect 5 | subtitle: 标签选择器 6 | cols: 1 7 | order: 13 8 | --- 9 | 10 | 可进行多选,带折叠收起和展开更多功能,常用于对列表进行筛选。 11 | 12 | ## API 13 | 14 | ### TagSelect 15 | 16 | | 参数 | 说明 | 类型 | 默认值 | 17 | |----------|------------------------------------------|-------------|-------| 18 | | value |选中的项 |string[] \| number[] | | 19 | | defaultValue |默认选中的项 |string[] \| number[] | | 20 | | onChange | 标签选择的回调函数 | Function(checkedTags) | | 21 | | expandable | 是否展示 `展开/收起` 按钮 | Boolean | false | 22 | | hideCheckAll | 隐藏 `全部` 按钮 | Boolean | false | 23 | 24 | ### TagSelectOption 25 | 26 | | 参数 | 说明 | 类型 | 默认值 | 27 | |----------|------------------------------------------|-------------|-------| 28 | | value | TagSelect的值 | string\| number | - | 29 | | children | tag的内容 | string \| ReactNode | - | 30 | -------------------------------------------------------------------------------- /src/components/Trend/demo/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 演示 4 | --- 5 | 6 | 在数值背后添加一个小图标来标识涨跌情况。 7 | 8 | ```jsx 9 | import Trend from 'ant-design-pro/lib/Trend'; 10 | 11 | ReactDOM.render( 12 |
13 | 12% 14 | 11% 15 |
16 | , mountNode); 17 | ``` 18 | -------------------------------------------------------------------------------- /src/components/Trend/demo/reverse.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 0 3 | title: 颜色反转 4 | --- 5 | 6 | 在数值背后添加一个小图标来标识涨跌情况。 7 | 8 | ```jsx 9 | import Trend from 'ant-design-pro/lib/Trend'; 10 | 11 | ReactDOM.render( 12 |
13 | 12% 14 | 11% 15 |
16 | , mountNode); 17 | ``` 18 | -------------------------------------------------------------------------------- /src/components/Trend/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface ITrendProps { 4 | colorful?: boolean; 5 | flag: 'up' | 'down'; 6 | style?: React.CSSProperties; 7 | reverseColor?: boolean; 8 | } 9 | 10 | export default class Trend extends React.Component {} 11 | -------------------------------------------------------------------------------- /src/components/Trend/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Icon } from 'antd'; 3 | import classNames from 'classnames'; 4 | import styles from './index.less'; 5 | 6 | const Trend = ({ colorful = true, reverseColor = false, flag, children, className, ...rest }) => { 7 | const classString = classNames( 8 | styles.trendItem, 9 | { 10 | [styles.trendItemGrey]: !colorful, 11 | [styles.reverseColor]: reverseColor && colorful, 12 | }, 13 | className 14 | ); 15 | return ( 16 |
17 | {children} 18 | {flag && ( 19 | 20 | 21 | 22 | )} 23 |
24 | ); 25 | }; 26 | 27 | export default Trend; 28 | -------------------------------------------------------------------------------- /src/components/Trend/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .trendItem { 4 | display: inline-block; 5 | font-size: @font-size-base; 6 | line-height: 22px; 7 | 8 | .up, 9 | .down { 10 | margin-left: 4px; 11 | position: relative; 12 | top: 1px; 13 | i { 14 | font-size: 12px; 15 | transform: scale(0.83); 16 | } 17 | } 18 | .up { 19 | color: @red-6; 20 | } 21 | .down { 22 | color: @green-6; 23 | top: -1px; 24 | } 25 | 26 | &.trendItemGrey .up, 27 | &.trendItemGrey .down { 28 | color: @text-color; 29 | } 30 | 31 | &.reverseColor .up { 32 | color: @green-6; 33 | } 34 | &.reverseColor .down { 35 | color: @red-6; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Trend/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | en-US: Trend 4 | zh-CN: Trend 5 | subtitle: 趋势标记 6 | cols: 1 7 | order: 14 8 | --- 9 | 10 | 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。 11 | 12 | ## API 13 | 14 | ```html 15 | 50% 16 | ``` 17 | 18 | | 参数 | 说明 | 类型 | 默认值 | 19 | |----------|------------------------------------------|-------------|-------| 20 | | colorful | 是否彩色标记 | Boolean | true | 21 | | flag | 上升下降标识:`up|down` | string | - | 22 | | reverseColor | 颜色反转 | Boolean | false | 23 | -------------------------------------------------------------------------------- /src/components/_utils/pathTools.js: -------------------------------------------------------------------------------- 1 | // /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id'] 2 | // eslint-disable-next-line import/prefer-default-export 3 | export function urlToList(url) { 4 | const urllist = url.split('/').filter(i => i); 5 | return urllist.map((urlItem, index) => `/${urllist.slice(0, index + 1).join('/')}`); 6 | } 7 | -------------------------------------------------------------------------------- /src/components/_utils/pathTools.test.js: -------------------------------------------------------------------------------- 1 | import { urlToList } from './pathTools'; 2 | 3 | describe('test urlToList', () => { 4 | it('A path', () => { 5 | expect(urlToList('/userinfo')).toEqual(['/userinfo']); 6 | }); 7 | it('Secondary path', () => { 8 | expect(urlToList('/userinfo/2144')).toEqual(['/userinfo', '/userinfo/2144']); 9 | }); 10 | it('Three paths', () => { 11 | expect(urlToList('/userinfo/2144/addr')).toEqual([ 12 | '/userinfo', 13 | '/userinfo/2144', 14 | '/userinfo/2144/addr', 15 | ]); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/defaultSettings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | navTheme: 'dark', // theme for nav menu 3 | primaryColor: '#1890FF', // primary color of ant design 4 | layout: 'sidemenu', // nav menu position: sidemenu or topmenu 5 | contentWidth: 'Fluid', // layout of content: Fluid or Fixed, only works when layout is topmenu 6 | fixedHeader: true, // sticky header 7 | autoHideHeader: false, // auto hide header 8 | fixSiderbar: true, // sticky siderbar 9 | hidenAntTabs:false, // hidden AntTabs 10 | pwa:false, 11 | }; 12 | -------------------------------------------------------------------------------- /src/e2e/baseLayout.e2e.js: -------------------------------------------------------------------------------- 1 | import RouterConfig from '../../config/router.config'; 2 | 3 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 4 | 5 | function formatter(data) { 6 | return data 7 | .reduce((pre, item) => { 8 | pre.push(item.path); 9 | return pre; 10 | }, []) 11 | .filter(item => item); 12 | } 13 | 14 | describe('Homepage', async () => { 15 | const testPage = path => async () => { 16 | await page.goto(`${BASE_URL}${path}`); 17 | await page.waitForSelector('footer', { 18 | timeout: 2000, 19 | }); 20 | const haveFooter = await page.evaluate( 21 | () => document.getElementsByTagName('footer').length > 0 22 | ); 23 | expect(haveFooter).toBeTruthy(); 24 | }; 25 | 26 | beforeAll(async () => { 27 | jest.setTimeout(1000000); 28 | await page.setCacheEnabled(false); 29 | }); 30 | const routers = formatter(RouterConfig[1].routes); 31 | routers.forEach(route => { 32 | fit(`test pages ${route}`, testPage(route)); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/e2e/home.e2e.js: -------------------------------------------------------------------------------- 1 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 2 | 3 | describe('Homepage', () => { 4 | beforeAll(async () => { 5 | jest.setTimeout(1000000); 6 | }); 7 | it('it should have logo text', async () => { 8 | await page.goto(BASE_URL); 9 | await page.waitForSelector('h1', { 10 | timeout: 5000, 11 | }); 12 | const text = await page.evaluate(() => document.getElementsByTagName('h1')[0].innerText); 13 | expect(text).toContain('Ant Design Pro'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/e2e/login.e2e.js: -------------------------------------------------------------------------------- 1 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 2 | 3 | describe('Login', () => { 4 | beforeAll(async () => { 5 | jest.setTimeout(1000000); 6 | }); 7 | 8 | beforeEach(async () => { 9 | await page.goto(`${BASE_URL}/user/login`, { waitUntil: 'networkidle2' }); 10 | await page.evaluate(() => window.localStorage.setItem('antd-pro-authority', 'guest')); 11 | }); 12 | 13 | it('should login with failure', async () => { 14 | await page.waitForSelector('#userName', { 15 | timeout: 2000, 16 | }); 17 | await page.type('#userName', 'mockuser'); 18 | await page.type('#password', 'wrong_password'); 19 | await page.click('button[type="submit"]'); 20 | await page.waitForSelector('.ant-alert-error'); // should display error 21 | }); 22 | 23 | it('should login successfully', async () => { 24 | await page.waitForSelector('#userName', { 25 | timeout: 2000, 26 | }); 27 | await page.type('#userName', 'admin'); 28 | await page.type('#password', 'ant.design'); 29 | await page.click('button[type="submit"]'); 30 | await page.waitForSelector('.ant-layout-sider h1'); // should display error 31 | const text = await page.evaluate(() => document.body.innerHTML); 32 | expect(text).toContain('

Ant Design Pro

'); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/e2e/topMenu.e2e.js: -------------------------------------------------------------------------------- 1 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 2 | 3 | describe('Homepage', () => { 4 | beforeAll(async () => { 5 | jest.setTimeout(1000000); 6 | }); 7 | it('topmenu should have footer', async () => { 8 | const params = '/form/basic-form?navTheme=light&layout=topmenu'; 9 | await page.goto(`${BASE_URL}${params}`); 10 | await page.waitForSelector('footer', { 11 | timeout: 2000, 12 | }); 13 | const haveFooter = await page.evaluate( 14 | () => document.getElementsByTagName('footer').length > 0 15 | ); 16 | expect(haveFooter).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/e2e/userLayout.e2e.js: -------------------------------------------------------------------------------- 1 | import RouterConfig from '../../config/router.config'; 2 | 3 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 4 | 5 | function formatter(data) { 6 | return data 7 | .reduce((pre, item) => { 8 | pre.push(item.path); 9 | return pre; 10 | }, []) 11 | .filter(item => item); 12 | } 13 | 14 | describe('Homepage', () => { 15 | const testPage = path => async () => { 16 | await page.goto(`${BASE_URL}${path}`); 17 | await page.waitForSelector('footer', { 18 | timeout: 2000, 19 | }); 20 | const haveFooter = await page.evaluate( 21 | () => document.getElementsByTagName('footer').length > 0 22 | ); 23 | expect(haveFooter).toBeTruthy(); 24 | }; 25 | 26 | beforeAll(async () => { 27 | jest.setTimeout(1000000); 28 | }); 29 | formatter(RouterConfig[0].routes).forEach(route => { 30 | fit(`test pages ${route}`, testPage(route)); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/layouts/BasicLayout.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .content { 4 | margin: 24px 2px; 5 | // padding-top: @layout-header-height; 6 | padding-top: 42px; 7 | } 8 | -------------------------------------------------------------------------------- /src/layouts/BlankLayout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default props =>
; 4 | -------------------------------------------------------------------------------- /src/layouts/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { Layout, Icon } from 'antd'; 3 | import GlobalFooter from '@/components/GlobalFooter'; 4 | 5 | const { Footer } = Layout; 6 | const FooterView = () => ( 7 |
8 | , 19 | href: 'https://github.com/kuhami/react-ant', 20 | blankTarget: true, 21 | }, 22 | { 23 | key: 'Ant Tabs', 24 | title: 'Ant Tabs', 25 | href: 'https://github.com/kuhami/react-ant', 26 | blankTarget: true, 27 | }, 28 | ]} 29 | copyright={ 30 | 31 | Copyright 2018 蚂蚁金服体验技术部出品 32 | 33 | } 34 | /> 35 |
36 | ); 37 | export default FooterView; 38 | -------------------------------------------------------------------------------- /src/layouts/Header.less: -------------------------------------------------------------------------------- 1 | .fixedHeader { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | width: 100%; 6 | z-index: 9; 7 | transition: width 0.2s; 8 | } 9 | -------------------------------------------------------------------------------- /src/layouts/MenuContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export default createContext(); 4 | -------------------------------------------------------------------------------- /src/locales/en-US.js: -------------------------------------------------------------------------------- 1 | import analysis from './en-US/analysis'; 2 | import exception from './en-US/exception'; 3 | import form from './en-US/form'; 4 | import globalHeader from './en-US/globalHeader'; 5 | import login from './en-US/login'; 6 | import menu from './en-US/menu'; 7 | import monitor from './en-US/monitor'; 8 | import result from './en-US/result'; 9 | import settingDrawer from './en-US/settingDrawer'; 10 | import settings from './en-US/settings'; 11 | import pwa from './en-US/pwa'; 12 | 13 | export default { 14 | 'navBar.lang': 'Languages', 15 | 'layout.user.link.help': 'Help', 16 | 'layout.user.link.privacy': 'Privacy', 17 | 'layout.user.link.terms': 'Terms', 18 | 'app.home.introduce': 'introduce', 19 | 'app.forms.basic.title': 'Basic form', 20 | 'app.forms.basic.description': 21 | 'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.', 22 | ...analysis, 23 | ...exception, 24 | ...form, 25 | ...globalHeader, 26 | ...login, 27 | ...menu, 28 | ...monitor, 29 | ...result, 30 | ...settingDrawer, 31 | ...settings, 32 | ...pwa, 33 | }; 34 | -------------------------------------------------------------------------------- /src/locales/en-US/exception.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.exception.back': 'Back to home', 3 | 'app.exception.description.403': "Sorry, you don't have access to this page", 4 | 'app.exception.description.404': 'Sorry, the page you visited does not exist', 5 | 'app.exception.description.500': 'Sorry, the server is reporting an error', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/en-US/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': 'Search', 3 | 'component.globalHeader.search.example1': 'Search example 1', 4 | 'component.globalHeader.search.example2': 'Search example 2', 5 | 'component.globalHeader.search.example3': 'Search example 3', 6 | 'component.globalHeader.help': 'Help', 7 | 'component.globalHeader.notification': 'Notification', 8 | 'component.globalHeader.notification.empty': 'You have viewed all notifications.', 9 | 'component.globalHeader.message': 'Message', 10 | 'component.globalHeader.message.empty': 'You have viewed all messsages.', 11 | 'component.globalHeader.event': 'Event', 12 | 'component.globalHeader.event.empty': 'You have viewed all events.', 13 | 'component.noticeIcon.clear': 'Clear', 14 | 'component.noticeIcon.cleared': 'Cleared', 15 | 'component.noticeIcon.empty': 'No notifications', 16 | 'component.noticeIcon.loaded': 'Loaded', 17 | 'component.noticeIcon.loading-more': 'Loading more', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/en-US/monitor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.monitor.trading-activity': 'Real-Time Trading Activity', 3 | 'app.monitor.total-transactions': 'Total transactions today', 4 | 'app.monitor.sales-target': 'Sales target completion rate', 5 | 'app.monitor.remaining-time': 'Remaining time of activity', 6 | 'app.monitor.total-transactions-per-second': 'Total transactions per second', 7 | 'app.monitor.activity-forecast': 'Activity forecast', 8 | 'app.monitor.efficiency': 'Efficiency', 9 | 'app.monitor.ratio': 'Ratio', 10 | 'app.monitor.proportion-per-category': 'Proportion Per Category', 11 | 'app.monitor.fast-food': 'Fast food', 12 | 'app.monitor.western-food': 'Western food', 13 | 'app.monitor.hot-pot': 'Hot pot', 14 | 'app.monitor.waiting-for-implementation': 'Waiting for implementation', 15 | 'app.monitor.popular-searches': 'Popular Searches', 16 | 'app.monitor.resource-surplus': 'Resource Surplus', 17 | 'app.monitor.fund-surplus': 'Fund Surplus', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/en-US/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': 'You are offline now', 3 | 'app.pwa.serviceworker.updated': 'New content is available', 4 | 'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page', 5 | 'app.pwa.serviceworker.updated.ok': 'Refresh', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/pt-BR.js: -------------------------------------------------------------------------------- 1 | import analysis from './pt-BR/analysis'; 2 | import exception from './pt-BR/exception'; 3 | import form from './pt-BR/form'; 4 | import globalHeader from './pt-BR/globalHeader'; 5 | import login from './pt-BR/login'; 6 | import menu from './pt-BR/menu'; 7 | import monitor from './pt-BR/monitor'; 8 | import result from './pt-BR/result'; 9 | import settingDrawer from './pt-BR/settingDrawer'; 10 | import settings from './pt-BR/settings'; 11 | import pwa from './pt-BR/pwa'; 12 | 13 | export default { 14 | 'navBar.lang': 'Idiomas', 15 | 'layout.user.link.help': 'ajuda', 16 | 'layout.user.link.privacy': 'política de privacidade', 17 | 'layout.user.link.terms': 'termos de serviços', 18 | 'app.home.introduce': 'introduzir', 19 | 'app.forms.basic.title': 'Basic form', 20 | 'app.forms.basic.description': 21 | 'Páginas de formulário são usadas para coletar e verificar as informações dos usuários e formulários básicos são comuns nos cenários onde existem alguns formatos de informações.', 22 | ...analysis, 23 | ...exception, 24 | ...form, 25 | ...globalHeader, 26 | ...login, 27 | ...menu, 28 | ...monitor, 29 | ...result, 30 | ...settingDrawer, 31 | ...settings, 32 | ...pwa, 33 | }; 34 | -------------------------------------------------------------------------------- /src/locales/pt-BR/exception.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.exception.back': 'Voltar para Início', 3 | 'app.exception.description.403': 'Desculpe, você não tem acesso a esta página', 4 | 'app.exception.description.404': 'Desculpe, a página que você visitou não existe', 5 | 'app.exception.description.500': 'Desculpe, o servidor está reportando um erro', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/pt-BR/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': 'Busca', 3 | 'component.globalHeader.search.example1': 'Exemplo de busca 1', 4 | 'component.globalHeader.search.example2': 'Exemplo de busca 2', 5 | 'component.globalHeader.search.example3': 'Exemplo de busca 3', 6 | 'component.globalHeader.help': 'Ajuda', 7 | 'component.globalHeader.notification': 'Notificação', 8 | 'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.', 9 | 'component.globalHeader.message': 'Mensagem', 10 | 'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.', 11 | 'component.globalHeader.event': 'Evento', 12 | 'component.globalHeader.event.empty': 'Você visualizou todos os eventos.', 13 | 'component.noticeIcon.clear': 'Limpar', 14 | 'component.noticeIcon.cleared': 'Limpo', 15 | 'component.noticeIcon.empty': 'Sem notificações', 16 | 'component.noticeIcon.loaded': 'Carregado', 17 | 'component.noticeIcon.loading-more': 'Carregar mais', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/pt-BR/monitor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.monitor.trading-activity': 'Atividade de Trading Real-time', 3 | 'app.monitor.total-transactions': 'Total de transações hoje', 4 | 'app.monitor.sales-target': 'Taxa de conclusão da meta de vendas', 5 | 'app.monitor.remaining-time': 'Tempo restante da atividade', 6 | 'app.monitor.total-transactions-per-second': 'Total de transações por segundo', 7 | 'app.monitor.activity-forecast': 'Previsão atual', 8 | 'app.monitor.efficiency': 'Eficiência', 9 | 'app.monitor.ratio': 'Relação', 10 | 'app.monitor.proportion-per-category': 'Proporção por categoria', 11 | 'app.monitor.fast-food': 'Fast food', 12 | 'app.monitor.western-food': 'Comida Ocidental', 13 | 'app.monitor.hot-pot': 'Hot pot', 14 | 'app.monitor.waiting-for-implementation': 'Aguardando implementação', 15 | 'app.monitor.popular-searches': 'Buscas populares', 16 | 'app.monitor.resource-surplus': 'Excedente de recursos', 17 | 'app.monitor.fund-surplus': 'Excedente do fundo', 18 | 'app.exception.back': 'Voltar a home', 19 | }; 20 | -------------------------------------------------------------------------------- /src/locales/pt-BR/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': 'Você está offline agora', 3 | 'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível', 4 | 'app.pwa.serviceworker.updated.hint': 5 | 'Por favor, pressione o botão "Atualizar" para recarregar a página atual', 6 | 'app.pwa.serviceworker.updated.ok': 'Atualizar', 7 | }; 8 | -------------------------------------------------------------------------------- /src/locales/zh-CN.js: -------------------------------------------------------------------------------- 1 | import analysis from './zh-CN/analysis'; 2 | import exception from './zh-CN/exception'; 3 | import form from './zh-CN/form'; 4 | import globalHeader from './zh-CN/globalHeader'; 5 | import login from './zh-CN/login'; 6 | import menu from './zh-CN/menu'; 7 | import monitor from './zh-CN/monitor'; 8 | import result from './zh-CN/result'; 9 | import settingDrawer from './zh-CN/settingDrawer'; 10 | import settings from './zh-CN/settings'; 11 | import pwa from './zh-CN/pwa'; 12 | 13 | export default { 14 | 'navBar.lang': '语言', 15 | 'layout.user.link.help': '帮助', 16 | 'layout.user.link.privacy': '隐私', 17 | 'layout.user.link.terms': '条款', 18 | 'app.home.introduce': '介绍', 19 | 'app.forms.basic.title': '基础表单', 20 | 'app.forms.basic.description': 21 | '表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。', 22 | ...analysis, 23 | ...exception, 24 | ...form, 25 | ...globalHeader, 26 | ...login, 27 | ...menu, 28 | ...monitor, 29 | ...result, 30 | ...settingDrawer, 31 | ...settings, 32 | ...pwa, 33 | }; 34 | -------------------------------------------------------------------------------- /src/locales/zh-CN/analysis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.analysis.test': '工专路 {no} 号店', 3 | 'app.analysis.introduce': '指标说明', 4 | 'app.analysis.total-sales': '总销售额', 5 | 'app.analysis.day-sales': '日销售额', 6 | 'app.analysis.visits': '访问量', 7 | 'app.analysis.visits-trend': '访问量趋势', 8 | 'app.analysis.visits-ranking': '门店访问量排名', 9 | 'app.analysis.day-visits': '日访问量', 10 | 'app.analysis.week': '周同比', 11 | 'app.analysis.day': '日同比', 12 | 'app.analysis.payments': '支付笔数', 13 | 'app.analysis.conversion-rate': '转化率', 14 | 'app.analysis.operational-effect': '运营活动效果', 15 | 'app.analysis.sales-trend': '销售趋势', 16 | 'app.analysis.sales-ranking': '门店销售额排名', 17 | 'app.analysis.all-year': '全年', 18 | 'app.analysis.all-month': '本月', 19 | 'app.analysis.all-week': '本周', 20 | 'app.analysis.all-day': '今日', 21 | 'app.analysis.search-users': '搜索用户数', 22 | 'app.analysis.per-capita-search': '人均搜索次数', 23 | 'app.analysis.online-top-search': '线上热门搜索', 24 | 'app.analysis.the-proportion-of-sales': '销售额类别占比', 25 | 'app.analysis.channel.all': '全部渠道', 26 | 'app.analysis.channel.online': '线上', 27 | 'app.analysis.channel.stores': '门店', 28 | 'app.analysis.sales': '销售额', 29 | 'app.analysis.traffic': '客流量', 30 | 'app.analysis.table.rank': '排名', 31 | 'app.analysis.table.search-keyword': '搜索关键词', 32 | 'app.analysis.table.users': '用户数', 33 | 'app.analysis.table.weekly-range': '周涨幅', 34 | }; 35 | -------------------------------------------------------------------------------- /src/locales/zh-CN/exception.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.exception.back': '返回首页', 3 | 'app.exception.description.403': '抱歉,你无权访问该页面', 4 | 'app.exception.description.404': '抱歉,你访问的页面不存在', 5 | 'app.exception.description.500': '抱歉,服务器出错了', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/zh-CN/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': '站内搜索', 3 | 'component.globalHeader.search.example1': '搜索提示一', 4 | 'component.globalHeader.search.example2': '搜索提示二', 5 | 'component.globalHeader.search.example3': '搜索提示三', 6 | 'component.globalHeader.help': '使用文档', 7 | 'component.globalHeader.notification': '通知', 8 | 'component.globalHeader.notification.empty': '你已查看所有通知', 9 | 'component.globalHeader.message': '消息', 10 | 'component.globalHeader.message.empty': '您已读完所有消息', 11 | 'component.globalHeader.event': '待办', 12 | 'component.globalHeader.event.empty': '你已完成所有待办', 13 | 'component.noticeIcon.clear': '清空', 14 | 'component.noticeIcon.cleared': '清空了', 15 | 'component.noticeIcon.empty': '暂无数据', 16 | 'component.noticeIcon.loaded': '加载完毕', 17 | 'component.noticeIcon.loading-more': '加载更多', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/zh-CN/monitor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.monitor.trading-activity': '活动实时交易情况', 3 | 'app.monitor.total-transactions': '今日交易总额', 4 | 'app.monitor.sales-target': '销售目标完成率', 5 | 'app.monitor.remaining-time': '活动剩余时间', 6 | 'app.monitor.total-transactions-per-second': '每秒交易总额', 7 | 'app.monitor.activity-forecast': '活动情况预测', 8 | 'app.monitor.efficiency': '券核效率', 9 | 'app.monitor.ratio': '跳出率', 10 | 'app.monitor.proportion-per-category': '各品类占比', 11 | 'app.monitor.fast-food': '中式快餐', 12 | 'app.monitor.western-food': '西餐', 13 | 'app.monitor.hot-pot': '火锅', 14 | 'app.monitor.waiting-for-implementation': 'Waiting for implementation', 15 | 'app.monitor.popular-searches': '热门搜索', 16 | 'app.monitor.resource-surplus': '资源剩余', 17 | 'app.monitor.fund-surplus': '补贴资金剩余', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/zh-CN/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': '当前处于离线状态', 3 | 'app.pwa.serviceworker.updated': '有新内容', 4 | 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', 5 | 'app.pwa.serviceworker.updated.ok': '刷新', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/zh-CN/result.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.result.error.title': '提交失败', 3 | 'app.result.error.description': '请核对并修改以下信息后,再重新提交。', 4 | 'app.result.error.hint-title': '您提交的内容有如下错误:', 5 | 'app.result.error.hint-text1': '您的账户已被冻结', 6 | 'app.result.error.hint-btn1': '立即解冻', 7 | 'app.result.error.hint-text2': '您的账户还不具备申请资格', 8 | 'app.result.error.hint-btn2': '立即升级', 9 | 'app.result.error.btn-text': '返回修改', 10 | 'app.result.success.title': '提交成功', 11 | 'app.result.success.description': 12 | '提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示 “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。', 13 | 'app.result.success.operate-title': '项目名称', 14 | 'app.result.success.operate-id': '项目 ID:', 15 | 'app.result.success.principal': '负责人:', 16 | 'app.result.success.operate-time': '生效时间:', 17 | 'app.result.success.step1-title': '创建项目', 18 | 'app.result.success.step1-operator': '曲丽丽', 19 | 'app.result.success.step2-title': '部门初审', 20 | 'app.result.success.step2-operator': '周毛毛', 21 | 'app.result.success.step2-extra': '催一下', 22 | 'app.result.success.step3-title': '财务复核', 23 | 'app.result.success.step4-title': '完成', 24 | 'app.result.success.btn-return': '返回列表', 25 | 'app.result.success.btn-project': '查看项目', 26 | 'app.result.success.btn-print': '打印', 27 | }; 28 | -------------------------------------------------------------------------------- /src/locales/zh-CN/settingDrawer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.setting.pagestyle': '整体风格设置', 3 | 'app.setting.pagestyle.dark': '暗色菜单风格', 4 | 'app.setting.pagestyle.light': '亮色菜单风格', 5 | 'app.setting.content-width': '内容区域宽度', 6 | 'app.setting.content-width.fixed': '定宽', 7 | 'app.setting.content-width.fluid': '流式', 8 | 'app.setting.themecolor': '主题色', 9 | 'app.setting.themecolor.dust': '薄暮', 10 | 'app.setting.themecolor.volcano': '火山', 11 | 'app.setting.themecolor.sunset': '日暮', 12 | 'app.setting.themecolor.cyan': '明青', 13 | 'app.setting.themecolor.green': '极光绿', 14 | 'app.setting.themecolor.daybreak': '拂晓蓝(默认)', 15 | 'app.setting.themecolor.geekblue': '极客蓝', 16 | 'app.setting.themecolor.purple': '酱紫', 17 | 'app.setting.navigationmode': '导航模式', 18 | 'app.setting.sidemenu': '侧边菜单布局', 19 | 'app.setting.topmenu': '顶部菜单布局', 20 | 'app.setting.fixedheader': '固定 Header', 21 | 'app.setting.fixedsidebar': '固定侧边菜单', 22 | 'app.setting.hidenAntTabs': '隐藏 Ant-Tabs', 23 | 'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置', 24 | 'app.setting.hideheader': '下滑时隐藏 Header', 25 | 'app.setting.hideheader.hint': '固定 Header 时可配置', 26 | 'app.setting.othersettings': '其他设置', 27 | 'app.setting.weakmode': '色弱模式', 28 | 'app.setting.copy': '拷贝设置', 29 | 'app.setting.copyinfo': '拷贝成功,请到 src/defaultSettings.js 中替换默认配置', 30 | 'app.setting.production.hint': 31 | '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件', 32 | }; 33 | -------------------------------------------------------------------------------- /src/locales/zh-TW.js: -------------------------------------------------------------------------------- 1 | import analysis from './zh-TW/analysis'; 2 | import exception from './zh-TW/exception'; 3 | import form from './zh-TW/form'; 4 | import globalHeader from './zh-TW/globalHeader'; 5 | import login from './zh-TW/login'; 6 | import menu from './zh-TW/menu'; 7 | import monitor from './zh-TW/monitor'; 8 | import result from './zh-TW/result'; 9 | import settingDrawer from './zh-TW/settingDrawer'; 10 | import settings from './zh-TW/settings'; 11 | import pwa from './zh-TW/pwa'; 12 | 13 | export default { 14 | 'navBar.lang': '語言', 15 | 'layout.user.link.help': '幫助', 16 | 'layout.user.link.privacy': '隱私', 17 | 'layout.user.link.terms': '條款', 18 | 'app.home.introduce': '介紹', 19 | 'app.forms.basic.title': '基礎表單', 20 | 'app.forms.basic.description': 21 | '表單頁用於向用戶收集或驗證信息,基礎表單常見於數據項較少的表單場景。', 22 | ...analysis, 23 | ...exception, 24 | ...form, 25 | ...globalHeader, 26 | ...login, 27 | ...menu, 28 | ...monitor, 29 | ...result, 30 | ...settingDrawer, 31 | ...settings, 32 | ...pwa, 33 | }; 34 | -------------------------------------------------------------------------------- /src/locales/zh-TW/analysis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.analysis.test': '工專路 {no} 號店', 3 | 'app.analysis.introduce': '指標說明', 4 | 'app.analysis.total-sales': '總銷售額', 5 | 'app.analysis.day-sales': '日銷售額', 6 | 'app.analysis.visits': '訪問量', 7 | 'app.analysis.visits-trend': '訪問量趨勢', 8 | 'app.analysis.visits-ranking': '門店訪問量排名', 9 | 'app.analysis.day-visits': '日訪問量', 10 | 'app.analysis.week': '周同比', 11 | 'app.analysis.day': '日同比', 12 | 'app.analysis.payments': '支付筆數', 13 | 'app.analysis.conversion-rate': '轉化率', 14 | 'app.analysis.operational-effect': '運營活動效果', 15 | 'app.analysis.sales-trend': '銷售趨勢', 16 | 'app.analysis.sales-ranking': '門店銷售額排名', 17 | 'app.analysis.all-year': '全年', 18 | 'app.analysis.all-month': '本月', 19 | 'app.analysis.all-week': '本周', 20 | 'app.analysis.all-day': '今日', 21 | 'app.analysis.search-users': '搜索用戶數', 22 | 'app.analysis.per-capita-search': '人均搜索次數', 23 | 'app.analysis.online-top-search': '線上熱門搜索', 24 | 'app.analysis.the-proportion-of-sales': '銷售額類別占比', 25 | 'app.analysis.channel.all': '全部渠道', 26 | 'app.analysis.channel.online': '線上', 27 | 'app.analysis.channel.stores': '門店', 28 | 'app.analysis.sales': '銷售額', 29 | 'app.analysis.traffic': '客流量', 30 | 'app.analysis.table.rank': '排名', 31 | 'app.analysis.table.search-keyword': '搜索關鍵詞', 32 | 'app.analysis.table.users': '用戶數', 33 | 'app.analysis.table.weekly-range': '周漲幅', 34 | }; 35 | -------------------------------------------------------------------------------- /src/locales/zh-TW/exception.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.exception.back': '返回首頁', 3 | 'app.exception.description.403': '抱歉,妳無權訪問該頁面', 4 | 'app.exception.description.404': '抱歉,妳訪問的頁面不存在', 5 | 'app.exception.description.500': '抱歉,服務器出錯了', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/zh-TW/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': '站內搜索', 3 | 'component.globalHeader.search.example1': '搜索提示壹', 4 | 'component.globalHeader.search.example2': '搜索提示二', 5 | 'component.globalHeader.search.example3': '搜索提示三', 6 | 'component.globalHeader.help': '使用文檔', 7 | 'component.globalHeader.notification': '通知', 8 | 'component.globalHeader.notification.empty': '妳已查看所有通知', 9 | 'component.globalHeader.message': '消息', 10 | 'component.globalHeader.message.empty': '您已讀完所有消息', 11 | 'component.globalHeader.event': '待辦', 12 | 'component.globalHeader.event.empty': '妳已完成所有待辦', 13 | 'component.noticeIcon.clear': '清空', 14 | 'component.noticeIcon.cleared': '清空了', 15 | 'component.noticeIcon.empty': '暫無數據', 16 | 'component.noticeIcon.loaded': '加載完畢', 17 | 'component.noticeIcon.loading-more': '加載更多', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/zh-TW/monitor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.monitor.trading-activity': '活動實時交易情況', 3 | 'app.monitor.total-transactions': '今日交易總額', 4 | 'app.monitor.sales-target': '銷售目標完成率', 5 | 'app.monitor.remaining-time': '活動剩余時間', 6 | 'app.monitor.total-transactions-per-second': '每秒交易總額', 7 | 'app.monitor.activity-forecast': '活動情況預測', 8 | 'app.monitor.efficiency': '券核效率', 9 | 'app.monitor.ratio': '跳出率', 10 | 'app.monitor.proportion-per-category': '各品類占比', 11 | 'app.monitor.fast-food': '中式快餐', 12 | 'app.monitor.western-food': '西餐', 13 | 'app.monitor.hot-pot': '火鍋', 14 | 'app.monitor.waiting-for-implementation': 'Waiting for implementation', 15 | 'app.monitor.popular-searches': '熱門搜索', 16 | 'app.monitor.resource-surplus': '資源剩余', 17 | 'app.monitor.fund-surplus': '補貼資金剩余', 18 | }; 19 | -------------------------------------------------------------------------------- /src/locales/zh-TW/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': '當前處於離線狀態', 3 | 'app.pwa.serviceworker.updated': '有新內容', 4 | 'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面', 5 | 'app.pwa.serviceworker.updated.ok': '刷新', 6 | }; 7 | -------------------------------------------------------------------------------- /src/locales/zh-TW/result.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.result.error.title': '提交失敗', 3 | 'app.result.error.description': '請核對並修改以下信息後,再重新提交。', 4 | 'app.result.error.hint-title': '您提交的內容有如下錯誤:', 5 | 'app.result.error.hint-text1': '您的賬戶已被凍結', 6 | 'app.result.error.hint-btn1': '立即解凍', 7 | 'app.result.error.hint-text2': '您的賬戶還不具備申請資格', 8 | 'app.result.error.hint-btn2': '立即升級', 9 | 'app.result.error.btn-text': '返回修改', 10 | 'app.result.success.title': '提交成功', 11 | 'app.result.success.description': 12 | '提交結果頁用於反饋壹系列操作任務的處理結果, 如果僅是簡單操作,使用 Message 全局提示反饋即可。 本文字區域可以展示簡單的補充說明,如果有類似展示 “單據”的需求,下面這個灰色區域可以呈現比較復雜的內容。', 13 | 'app.result.success.operate-title': '項目名稱', 14 | 'app.result.success.operate-id': '項目 ID:', 15 | 'app.result.success.principal': '負責人:', 16 | 'app.result.success.operate-time': '生效時間:', 17 | 'app.result.success.step1-title': '創建項目', 18 | 'app.result.success.step1-operator': '曲麗麗', 19 | 'app.result.success.step2-title': '部門初審', 20 | 'app.result.success.step2-operator': '周毛毛', 21 | 'app.result.success.step2-extra': '催壹下', 22 | 'app.result.success.step3-title': '財務復核', 23 | 'app.result.success.step4-title': '完成', 24 | 'app.result.success.btn-return': '返回列表', 25 | 'app.result.success.btn-project': '查看項目', 26 | 'app.result.success.btn-print': '打印', 27 | }; 28 | -------------------------------------------------------------------------------- /src/locales/zh-TW/settingDrawer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.setting.pagestyle': '整體風格設置', 3 | 'app.setting.pagestyle.dark': '暗色菜單風格', 4 | 'app.setting.pagestyle.light': '亮色菜單風格', 5 | 'app.setting.content-width': '內容區域寬度', 6 | 'app.setting.content-width.fixed': '定寬', 7 | 'app.setting.content-width.fluid': '流式', 8 | 'app.setting.themecolor': '主題色', 9 | 'app.setting.themecolor.dust': '薄暮', 10 | 'app.setting.themecolor.volcano': '火山', 11 | 'app.setting.themecolor.sunset': '日暮', 12 | 'app.setting.themecolor.cyan': '明青', 13 | 'app.setting.themecolor.green': '極光綠', 14 | 'app.setting.themecolor.daybreak': '拂曉藍(默認)', 15 | 'app.setting.themecolor.geekblue': '極客藍', 16 | 'app.setting.themecolor.purple': '醬紫', 17 | 'app.setting.navigationmode': '導航模式', 18 | 'app.setting.sidemenu': '側邊菜單布局', 19 | 'app.setting.topmenu': '頂部菜單布局', 20 | 'app.setting.fixedheader': '固定 Header', 21 | 'app.setting.fixedsidebar': '固定側邊菜單', 22 | 'app.setting.fixedsidebar.hint': '側邊菜單布局時可配置', 23 | 'app.setting.hideheader': '下滑時隱藏 Header', 24 | 'app.setting.hideheader.hint': '固定 Header 時可配置', 25 | 'app.setting.othersettings': '其他設置', 26 | 'app.setting.weakmode': '色弱模式', 27 | 'app.setting.copy': '拷貝設置', 28 | 'app.setting.copyinfo': '拷貝成功,請到 src/defaultSettings.js 中替換默認配置', 29 | 'app.setting.production.hint': 30 | '配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件', 31 | 'app.setting.hidenAntTabs': '隐藏 Ant-Tabs', 32 | }; 33 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ant Tabs", 3 | "short_name": "Ant Tabs", 4 | "display": "standalone", 5 | "start_url": "./?utm_source=homescreen", 6 | "theme_color": "#002140", 7 | "background_color": "#001529", 8 | "icons": [ 9 | { 10 | "src": "icons/icon-192x192.png", 11 | "sizes": "192x192" 12 | }, 13 | { 14 | "src": "icons/icon-128x128.png", 15 | "sizes": "128x128" 16 | }, 17 | { 18 | "src": "icons/icon-512x512.png", 19 | "sizes": "512x512" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/models/project.js: -------------------------------------------------------------------------------- 1 | import { queryProjectNotice } from '@/services/api'; 2 | 3 | export default { 4 | namespace: 'project', 5 | 6 | state: { 7 | notice: [], 8 | }, 9 | 10 | effects: { 11 | *fetchNotice(_, { call, put }) { 12 | const response = yield call(queryProjectNotice); 13 | yield put({ 14 | type: 'saveNotice', 15 | payload: Array.isArray(response) ? response : [], 16 | }); 17 | }, 18 | }, 19 | 20 | reducers: { 21 | saveNotice(state, action) { 22 | return { 23 | ...state, 24 | notice: action.payload, 25 | }; 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/models/user.js: -------------------------------------------------------------------------------- 1 | import { query as queryUsers, queryCurrent } from '@/services/user'; 2 | 3 | export default { 4 | namespace: 'user', 5 | 6 | state: { 7 | list: [], 8 | currentUser: {}, 9 | }, 10 | 11 | effects: { 12 | *fetch(_, { call, put }) { 13 | const response = yield call(queryUsers); 14 | yield put({ 15 | type: 'save', 16 | payload: response, 17 | }); 18 | }, 19 | *fetchCurrent(_, { call, put }) { 20 | const response = yield call(queryCurrent); 21 | yield put({ 22 | type: 'saveCurrentUser', 23 | payload: response, 24 | }); 25 | }, 26 | }, 27 | 28 | reducers: { 29 | save(state, action) { 30 | return { 31 | ...state, 32 | list: action.payload, 33 | }; 34 | }, 35 | saveCurrentUser(state, action) { 36 | return { 37 | ...state, 38 | currentUser: action.payload || {}, 39 | }; 40 | }, 41 | changeNotifyCount(state, action) { 42 | return { 43 | ...state, 44 | currentUser: { 45 | ...state.currentUser, 46 | notifyCount: action.payload.totalCount, 47 | unreadCount: action.payload.unreadCount, 48 | }, 49 | }; 50 | }, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'umi/link'; 3 | import { formatMessage } from 'umi/locale'; 4 | import Exception from '@/components/Exception'; 5 | 6 | export default () => ( 7 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/pages/Account/Center/Articles.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .articleList { 4 | :global { 5 | .ant-list-item:first-child { 6 | padding-top: 0; 7 | } 8 | } 9 | } 10 | a.listItemMetaTitle { 11 | color: @heading-color; 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/Account/Settings/BaseView.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .baseView { 4 | display: flex; 5 | padding-top: 12px; 6 | 7 | .left { 8 | max-width: 448px; 9 | min-width: 224px; 10 | } 11 | .right { 12 | flex: 1; 13 | padding-left: 104px; 14 | .avatar_title { 15 | height: 22px; 16 | font-size: @font-size-base; 17 | color: @heading-color; 18 | line-height: 22px; 19 | margin-bottom: 8px; 20 | } 21 | .avatar { 22 | width: 144px; 23 | height: 144px; 24 | margin-bottom: 12px; 25 | overflow: hidden; 26 | img { 27 | width: 100%; 28 | } 29 | } 30 | .button_view { 31 | width: 144px; 32 | text-align: center; 33 | } 34 | } 35 | } 36 | 37 | @media screen and (max-width: @screen-xl) { 38 | .baseView { 39 | flex-direction: column-reverse; 40 | 41 | .right { 42 | padding: 20px; 43 | display: flex; 44 | flex-direction: column; 45 | align-items: center; 46 | max-width: 448px; 47 | .avatar_title { 48 | display: none; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/pages/Account/Settings/GeographicView.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .row { 4 | .item { 5 | max-width: 220px; 6 | width: 50%; 7 | } 8 | .item:first-child { 9 | margin-right: 8px; 10 | width: ~'calc(50% - 8px)'; 11 | } 12 | } 13 | 14 | @media screen and (max-width: @screen-sm) { 15 | .item:first-child { 16 | margin: 0; 17 | margin-bottom: 8px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/Account/Settings/PhoneView.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, PureComponent } from 'react'; 2 | import { Input } from 'antd'; 3 | import styles from './PhoneView.less'; 4 | 5 | class PhoneView extends PureComponent { 6 | render() { 7 | const { value, onChange } = this.props; 8 | let values = ['', '']; 9 | if (value) { 10 | values = value.split('-'); 11 | } 12 | return ( 13 | 14 | { 18 | onChange(`${e.target.value}-${values[1]}`); 19 | }} 20 | /> 21 | { 24 | onChange(`${values[0]}-${e.target.value}`); 25 | }} 26 | value={values[1]} 27 | /> 28 | 29 | ); 30 | } 31 | } 32 | 33 | export default PhoneView; 34 | -------------------------------------------------------------------------------- /src/pages/Account/Settings/PhoneView.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .area_code { 4 | max-width: 128px; 5 | margin-right: 8px; 6 | width: 30%; 7 | } 8 | .phone_number { 9 | max-width: 312px; 10 | width: ~'calc(70% - 8px)'; 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/Authorized.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RenderAuthorized from '@/components/Authorized'; 3 | import { getAuthority } from '@/utils/authority'; 4 | import Redirect from 'umi/redirect'; 5 | 6 | const Authority = getAuthority(); 7 | const Authorized = RenderAuthorized(Authority); 8 | 9 | export default ({ children }) => ( 10 | }> 11 | {children} 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/pages/Component/Edit.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Card, Divider } from 'antd'; 3 | import DescriptionList from '@/components/DescriptionList'; 4 | 5 | const { Description } = DescriptionList; 6 | 7 | class Edit extends Component { 8 | constructor(props){ 9 | super(props) 10 | 11 | } 12 | componentDidMount() { 13 | console.log(this.props.location) 14 | } 15 | 16 | render() { 17 | 18 | return ( 19 | 20 | 21 | 1000000000 22 | 已取货 23 | 1234123421 24 | 3214321432 25 | 26 | 27 | 28 | 付小小 29 | 18100000000 30 | 菜鸟仓储 31 | 浙江省杭州市西湖区万塘路18号 32 | 33 | 34 | 35 | ); 36 | } 37 | } 38 | 39 | export default Edit; 40 | -------------------------------------------------------------------------------- /src/pages/Component/Table.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import '~@/utils/utils.less'; 3 | 4 | .tableList { 5 | .tableListOperator { 6 | margin-bottom: 16px; 7 | button { 8 | margin-right: 8px; 9 | } 10 | } 11 | } 12 | .aa{ 13 | color: red ; 14 | background: #e1e1e1; 15 | } 16 | 17 | .tableListForm { 18 | :global { 19 | .ant-form-item { 20 | margin-bottom: 14px; 21 | margin-right: 0; 22 | display: flex; 23 | > .ant-form-item-label { 24 | width: auto; 25 | line-height: 32px; 26 | padding-right: 8px; 27 | } 28 | .ant-form-item-control { 29 | line-height: 32px; 30 | } 31 | } 32 | .ant-form-item-control-wrapper { 33 | flex: 1; 34 | } 35 | } 36 | .submitButtons { 37 | display: block; 38 | white-space: nowrap; 39 | margin-bottom: 14px; 40 | } 41 | } 42 | 43 | @media screen and (max-width: @screen-lg) { 44 | .tableListForm :global(.ant-form-item) { 45 | margin-right: 24px; 46 | } 47 | } 48 | 49 | @media screen and (max-width: @screen-md) { 50 | .tableListForm :global(.ant-form-item) { 51 | margin-right: 8px; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/pages/Dashboard/Monitor.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import '~@/utils/utils.less'; 3 | 4 | .mapChart { 5 | padding-top: 24px; 6 | height: 452px; 7 | text-align: center; 8 | img { 9 | display: inline-block; 10 | max-width: 100%; 11 | max-height: 437px; 12 | } 13 | } 14 | 15 | .pieCard :global(.pie-stat) { 16 | font-size: 24px !important; 17 | } 18 | 19 | @media screen and (max-width: @screen-lg) { 20 | .mapChart { 21 | height: auto; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/Dashboard/models/activities.js: -------------------------------------------------------------------------------- 1 | import { queryActivities } from '@/services/api'; 2 | 3 | export default { 4 | namespace: 'activities', 5 | 6 | state: { 7 | list: [], 8 | }, 9 | 10 | effects: { 11 | *fetchList(_, { call, put }) { 12 | const response = yield call(queryActivities); 13 | yield put({ 14 | type: 'saveList', 15 | payload: Array.isArray(response) ? response : [], 16 | }); 17 | }, 18 | }, 19 | 20 | reducers: { 21 | saveList(state, action) { 22 | return { 23 | ...state, 24 | list: action.payload, 25 | }; 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/pages/Dashboard/models/monitor.js: -------------------------------------------------------------------------------- 1 | import { queryTags } from '@/services/api'; 2 | 3 | export default { 4 | namespace: 'monitor', 5 | 6 | state: { 7 | tags: [], 8 | }, 9 | 10 | effects: { 11 | *fetchTags(_, { call, put }) { 12 | const response = yield call(queryTags); 13 | yield put({ 14 | type: 'saveTags', 15 | payload: response.list, 16 | }); 17 | }, 18 | }, 19 | 20 | reducers: { 21 | saveTags(state, action) { 22 | return { 23 | ...state, 24 | tags: action.payload, 25 | }; 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/pages/Exception/403.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatMessage } from 'umi/locale'; 3 | import Link from 'umi/link'; 4 | import Exception from '@/components/Exception'; 5 | 6 | const Exception403 = () => ( 7 | 13 | ); 14 | 15 | export default Exception403; 16 | -------------------------------------------------------------------------------- /src/pages/Exception/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatMessage } from 'umi/locale'; 3 | import Link from 'umi/link'; 4 | import Exception from '@/components/Exception'; 5 | 6 | const Exception404 = () => ( 7 | 13 | ); 14 | 15 | export default Exception404; 16 | -------------------------------------------------------------------------------- /src/pages/Exception/500.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatMessage } from 'umi/locale'; 3 | import Link from 'umi/link'; 4 | import Exception from '@/components/Exception'; 5 | 6 | const Exception500 = () => ( 7 | 13 | ); 14 | 15 | export default Exception500; 16 | -------------------------------------------------------------------------------- /src/pages/Exception/models/error.js: -------------------------------------------------------------------------------- 1 | import queryError from '@/services/error'; 2 | 3 | export default { 4 | namespace: 'error', 5 | 6 | state: { 7 | error: '', 8 | isloading: false, 9 | }, 10 | 11 | effects: { 12 | *query({ payload }, { call, put }) { 13 | yield call(queryError, payload.code); 14 | yield put({ 15 | type: 'trigger', 16 | payload: payload.code, 17 | }); 18 | }, 19 | }, 20 | 21 | reducers: { 22 | trigger(state, action) { 23 | return { 24 | error: action.payload, 25 | }; 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/pages/Exception/style.less: -------------------------------------------------------------------------------- 1 | .trigger { 2 | background: 'red'; 3 | :global(.ant-btn) { 4 | margin-right: 8px; 5 | margin-bottom: 12px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/Forms/models/form.js: -------------------------------------------------------------------------------- 1 | import { routerRedux } from 'dva/router'; 2 | import { message } from 'antd'; 3 | import { fakeSubmitForm } from '@/services/api'; 4 | 5 | export default { 6 | namespace: 'form', 7 | 8 | state: { 9 | step: { 10 | payAccount: 'ant-design@alipay.com', 11 | receiverAccount: 'test@example.com', 12 | receiverName: 'Alex', 13 | amount: '500', 14 | }, 15 | }, 16 | 17 | effects: { 18 | *submitRegularForm({ payload }, { call }) { 19 | yield call(fakeSubmitForm, payload); 20 | message.success('提交成功'); 21 | }, 22 | *submitStepForm({ payload }, { call, put }) { 23 | yield call(fakeSubmitForm, payload); 24 | yield put({ 25 | type: 'saveStepFormData', 26 | payload, 27 | }); 28 | yield put(routerRedux.push('/form/step-form/result')); 29 | }, 30 | *submitAdvancedForm({ payload }, { call }) { 31 | yield call(fakeSubmitForm, payload); 32 | message.success('提交成功'); 33 | }, 34 | }, 35 | 36 | reducers: { 37 | saveStepFormData(state, { payload }) { 38 | return { 39 | ...state, 40 | step: { 41 | ...state.step, 42 | ...payload, 43 | }, 44 | }; 45 | }, 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /src/pages/Home/home.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "fd_fsid": "1", 4 | "key":"/libraries/drag", 5 | "Name": "拖拽", 6 | "IconUrl": "image/drag.png", 7 | "init_enable": true 8 | }, 9 | { 10 | "fd_fsid": "2", 11 | "key":"/libraries/braft-editor", 12 | "Name": "富文本编译器", 13 | "IconUrl": "image/braft-editor.png", 14 | "init_enable": true 15 | }, 16 | { 17 | "fd_fsid": "3", 18 | "key":"/libraries/react-color", 19 | "Name": "拾色器", 20 | "IconUrl": "image/react-color.png", 21 | "init_enable": true 22 | }, 23 | { 24 | "fd_fsid": "7", 25 | "key":"/libraries/scrollbars", 26 | "Name": "滚动条", 27 | "IconUrl": "image/scroll.png", 28 | "init_enable": true 29 | }, 30 | { 31 | "fd_fsid": "8", 32 | "key":"/libraries/reactdatagrid", 33 | "Name": "ReactDatagrid", 34 | "IconUrl": "image/table.png", 35 | "init_enable": true 36 | }, 37 | { 38 | "fd_fsid": "4", 39 | "key":"/component/table", 40 | "Name": "多功能Table", 41 | "IconUrl": "image/table.png", 42 | "init_enable": true 43 | }, 44 | { 45 | "fd_fsid": "5", 46 | "key":"/component/select-tree", 47 | "Name": "多选SelectTree", 48 | "IconUrl": "image/select-tree.png", 49 | "init_enable": true 50 | }, 51 | { 52 | "fd_fsid": "6", 53 | "key":"/account/center", 54 | "Name": "个人中心", 55 | "IconUrl": "image/center.png", 56 | "init_enable": true 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /src/pages/Libraries/Drag/Basic.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import {render} from 'react-dom'; 3 | import { Card } from 'antd'; 4 | import {SortableContainer, SortableElement} from 'react-sortable-hoc'; 5 | import arrayMove from 'array-move'; 6 | 7 | const SortableItem = SortableElement(({value}) =>
  • {value}
  • ); 8 | const SortableList = SortableContainer(({items}) => { 9 | return ( 10 |
      11 | {items.map((value, index) => ( 12 | 13 | ))} 14 |
    15 | ); 16 | }); 17 | export default class Basic extends Component { 18 | constructor(props){ 19 | super(props); 20 | this.state = { 21 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], 22 | } 23 | } 24 | componentDidMount() { 25 | 26 | } 27 | onSortEnd = ({oldIndex, newIndex}) => { 28 | this.setState(({items}) => ({ 29 | items: arrayMove(items, oldIndex, newIndex), 30 | })); 31 | }; 32 | render() { 33 | const title = 拖拽(react-sortable-hoc) 34 | return ( 35 | 36 | ); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/pages/Libraries/Drag/DragHandle.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import {render} from 'react-dom'; 3 | import { Card } from 'antd'; 4 | import { 5 | sortableContainer, 6 | sortableElement, 7 | sortableHandle,} from 'react-sortable-hoc'; 8 | import arrayMove from 'array-move'; 9 | 10 | const DragHandles = sortableHandle(() => ::++::); 11 | 12 | const SortableItem = sortableElement(({value}) => ( 13 |
  • 14 | 15 | {value} 16 |
  • 17 | )); 18 | 19 | const SortableContainer = sortableContainer(({children}) => { 20 | return
      {children}
    ; 21 | }); 22 | export default class DragHandle extends Component { 23 | constructor(props){ 24 | super(props); 25 | this.state = { 26 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], 27 | } 28 | } 29 | 30 | onSortEnd = ({oldIndex, newIndex}) => { 31 | this.setState(({items}) => ({ 32 | items: arrayMove(items, oldIndex, newIndex), 33 | })); 34 | }; 35 | render() { 36 | const {items} = this.state; 37 | return ( 38 | 39 | {items.map((value, index) => ( 40 | 41 | ))} 42 | 43 | ); 44 | } 45 | } -------------------------------------------------------------------------------- /src/pages/Libraries/Drag/index.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import {render} from 'react-dom'; 3 | import { Row,Card,Col } from 'antd'; 4 | import Basic from './Basic.js'//DragHandle.js 5 | import Collections from './Collections.js' 6 | import DragHandle from './DragHandle.js' 7 | 8 | export default class Drag extends Component { 9 | constructor(props){ 10 | super(props); 11 | this.state = { 12 | items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], 13 | } 14 | } 15 | componentDidMount() { 16 | 17 | } 18 | 19 | render() { 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/pages/Libraries/ReactDataGrid/ReactDataGrid.md: -------------------------------------------------------------------------------- 1 | # ReactDataGrid 中文API 2 | -------------------------------------------------------------------------------- /src/pages/List/Applications.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import '~@/utils/utils.less'; 3 | 4 | .filterCardList { 5 | margin-bottom: -24px; 6 | :global { 7 | .ant-card-meta-content { 8 | margin-top: 0; 9 | } 10 | // disabled white space 11 | .ant-card-meta-avatar { 12 | font-size: 0; 13 | } 14 | .ant-card-actions { 15 | background: #f7f9fa; 16 | } 17 | .ant-list .ant-list-item-content-single { 18 | max-width: 100%; 19 | } 20 | } 21 | .cardInfo { 22 | .clearfix(); 23 | margin-top: 16px; 24 | margin-left: 40px; 25 | & > div { 26 | position: relative; 27 | text-align: left; 28 | float: left; 29 | width: 50%; 30 | p { 31 | line-height: 32px; 32 | font-size: 24px; 33 | margin: 0; 34 | } 35 | p:first-child { 36 | color: @text-color-secondary; 37 | font-size: 12px; 38 | line-height: 20px; 39 | margin-bottom: 4px; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/List/Articles.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | a.listItemMetaTitle { 4 | color: @heading-color; 5 | } 6 | .listItemExtra { 7 | width: 272px; 8 | height: 1px; 9 | } 10 | .selfTrigger { 11 | margin-left: 12px; 12 | } 13 | 14 | @media screen and (max-width: @screen-xs) { 15 | .selfTrigger { 16 | display: block; 17 | margin-left: 0; 18 | } 19 | } 20 | @media screen and (max-width: @screen-md) { 21 | .selfTrigger { 22 | display: block; 23 | margin-left: 0; 24 | } 25 | } 26 | @media screen and (max-width: @screen-lg) { 27 | .listItemExtra { 28 | width: 0; 29 | height: 1px; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/List/Projects.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import '~@/utils/utils.less'; 3 | 4 | .coverCardList { 5 | margin-bottom: -24px; 6 | 7 | .card { 8 | :global { 9 | .ant-card-meta-title { 10 | margin-bottom: 4px; 11 | & > a { 12 | color: @heading-color; 13 | display: inline-block; 14 | max-width: 100%; 15 | } 16 | } 17 | .ant-card-meta-description { 18 | height: 44px; 19 | line-height: 22px; 20 | overflow: hidden; 21 | } 22 | } 23 | 24 | &:hover { 25 | :global { 26 | .ant-card-meta-title > a { 27 | color: @primary-color; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .cardItemContent { 34 | display: flex; 35 | margin-top: 16px; 36 | margin-bottom: -4px; 37 | line-height: 20px; 38 | height: 20px; 39 | & > span { 40 | color: @text-color-secondary; 41 | flex: 1; 42 | font-size: 12px; 43 | } 44 | .avatarList { 45 | flex: 0 1 auto; 46 | } 47 | } 48 | .cardList { 49 | margin-top: 24px; 50 | } 51 | 52 | :global { 53 | .ant-list .ant-list-item-content-single { 54 | max-width: 100%; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/pages/List/TableList.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import '~@/utils/utils.less'; 3 | 4 | .tableList { 5 | .tableListOperator { 6 | margin-bottom: 16px; 7 | button { 8 | margin-right: 8px; 9 | } 10 | } 11 | } 12 | 13 | .tableListForm { 14 | :global { 15 | .ant-form-item { 16 | margin-bottom: 24px; 17 | margin-right: 0; 18 | display: flex; 19 | > .ant-form-item-label { 20 | width: auto; 21 | line-height: 32px; 22 | padding-right: 8px; 23 | } 24 | .ant-form-item-control { 25 | line-height: 32px; 26 | } 27 | } 28 | .ant-form-item-control-wrapper { 29 | flex: 1; 30 | } 31 | } 32 | .submitButtons { 33 | display: block; 34 | white-space: nowrap; 35 | margin-bottom: 24px; 36 | } 37 | } 38 | 39 | @media screen and (max-width: @screen-lg) { 40 | .tableListForm :global(.ant-form-item) { 41 | margin-right: 24px; 42 | } 43 | } 44 | 45 | @media screen and (max-width: @screen-md) { 46 | .tableListForm :global(.ant-form-item) { 47 | margin-right: 8px; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/pages/List/models/rule.js: -------------------------------------------------------------------------------- 1 | import { queryRule, removeRule, addRule, updateRule } from '@/services/api'; 2 | 3 | export default { 4 | namespace: 'rule', 5 | 6 | state: { 7 | data: { 8 | list: [], 9 | pagination: {}, 10 | }, 11 | }, 12 | 13 | effects: { 14 | *fetch({ payload }, { call, put }) { 15 | const response = yield call(queryRule, payload); 16 | yield put({ 17 | type: 'save', 18 | payload: response, 19 | }); 20 | }, 21 | *add({ payload, callback }, { call, put }) { 22 | const response = yield call(addRule, payload); 23 | yield put({ 24 | type: 'save', 25 | payload: response, 26 | }); 27 | if (callback) callback(); 28 | }, 29 | *remove({ payload, callback }, { call, put }) { 30 | const response = yield call(removeRule, payload); 31 | yield put({ 32 | type: 'save', 33 | payload: response, 34 | }); 35 | if (callback) callback(); 36 | }, 37 | *update({ payload, callback }, { call, put }) { 38 | const response = yield call(updateRule, payload); 39 | yield put({ 40 | type: 'save', 41 | payload: response, 42 | }); 43 | if (callback) callback(); 44 | }, 45 | }, 46 | 47 | reducers: { 48 | save(state, action) { 49 | return { 50 | ...state, 51 | data: action.payload, 52 | }; 53 | }, 54 | }, 55 | }; 56 | -------------------------------------------------------------------------------- /src/pages/Profile/AdvancedProfile.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .headerList { 4 | margin-bottom: 4px; 5 | } 6 | 7 | .tabsCard { 8 | :global { 9 | .ant-card-head { 10 | padding: 0 16px; 11 | } 12 | } 13 | } 14 | 15 | .noData { 16 | color: @disabled-color; 17 | text-align: center; 18 | line-height: 64px; 19 | font-size: 16px; 20 | i { 21 | font-size: 24px; 22 | margin-right: 16px; 23 | position: relative; 24 | top: 3px; 25 | } 26 | } 27 | 28 | .heading { 29 | color: @heading-color; 30 | font-size: 20px; 31 | } 32 | 33 | .stepDescription { 34 | font-size: 14px; 35 | position: relative; 36 | left: 38px; 37 | padding-top: 8px; 38 | text-align: left; 39 | 40 | > div { 41 | margin-top: 8px; 42 | margin-bottom: 4px; 43 | } 44 | } 45 | 46 | .textSecondary { 47 | color: @text-color-secondary; 48 | } 49 | 50 | @media screen and (max-width: @screen-sm) { 51 | .stepDescription { 52 | left: 8px; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/pages/Profile/BasicProfile.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .title { 4 | color: @heading-color; 5 | font-size: 16px; 6 | font-weight: 500; 7 | margin-bottom: 16px; 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/Profile/models/profile.js: -------------------------------------------------------------------------------- 1 | import { queryBasicProfile, queryAdvancedProfile } from '@/services/api'; 2 | 3 | export default { 4 | namespace: 'profile', 5 | 6 | state: { 7 | basicGoods: [], 8 | advancedOperation1: [], 9 | advancedOperation2: [], 10 | advancedOperation3: [], 11 | }, 12 | 13 | effects: { 14 | *fetchBasic(_, { call, put }) { 15 | const response = yield call(queryBasicProfile); 16 | yield put({ 17 | type: 'show', 18 | payload: response, 19 | }); 20 | }, 21 | *fetchAdvanced(_, { call, put }) { 22 | const response = yield call(queryAdvancedProfile); 23 | yield put({ 24 | type: 'show', 25 | payload: response, 26 | }); 27 | }, 28 | }, 29 | 30 | reducers: { 31 | show(state, { payload }) { 32 | return { 33 | ...state, 34 | ...payload, 35 | }; 36 | }, 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/pages/Result/Success.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Success from './Success'; 4 | 5 | it('renders with Result', () => { 6 | const wrapper = shallow(); 7 | expect(wrapper.find('Result').length).toBe(1); 8 | expect(wrapper.find('Result').prop('type')).toBe('success'); 9 | }); 10 | -------------------------------------------------------------------------------- /src/pages/User/Login.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .main { 4 | width: 368px; 5 | margin: 0 auto; 6 | @media screen and (max-width: @screen-sm) { 7 | width: 95%; 8 | } 9 | 10 | .icon { 11 | font-size: 24px; 12 | color: rgba(0, 0, 0, 0.2); 13 | margin-left: 16px; 14 | vertical-align: middle; 15 | cursor: pointer; 16 | transition: color 0.3s; 17 | 18 | &:hover { 19 | color: @primary-color; 20 | } 21 | } 22 | 23 | .other { 24 | text-align: left; 25 | margin-top: 24px; 26 | line-height: 22px; 27 | 28 | .register { 29 | float: right; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/User/Register.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | 3 | .main { 4 | width: 368px; 5 | margin: 0 auto; 6 | 7 | :global { 8 | .ant-form-item { 9 | margin-bottom: 24px; 10 | } 11 | } 12 | 13 | h3 { 14 | font-size: 16px; 15 | margin-bottom: 20px; 16 | } 17 | 18 | .getCaptcha { 19 | display: block; 20 | width: 100%; 21 | } 22 | 23 | .submit { 24 | width: 50%; 25 | } 26 | 27 | .login { 28 | float: right; 29 | line-height: @btn-height-lg; 30 | } 31 | } 32 | 33 | .success, 34 | .warning, 35 | .error { 36 | transition: color 0.3s; 37 | } 38 | 39 | .success { 40 | color: @success-color; 41 | } 42 | 43 | .warning { 44 | color: @warning-color; 45 | } 46 | 47 | .error { 48 | color: @error-color; 49 | } 50 | 51 | .progress-pass > .progress { 52 | :global { 53 | .ant-progress-bg { 54 | background-color: @warning-color; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/pages/User/RegisterResult.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatMessage, FormattedMessage } from 'umi/locale'; 3 | import { Button } from 'antd'; 4 | import Link from 'umi/link'; 5 | import Result from '@/components/Result'; 6 | import styles from './RegisterResult.less'; 7 | 8 | const actions = ( 9 |
    10 | 11 | 14 | 15 | 16 | 19 | 20 |
    21 | ); 22 | 23 | const RegisterResult = ({ location }) => ( 24 | 29 | 33 |
    34 | } 35 | description={formatMessage({ id: 'app.register-result.activation-email' })} 36 | actions={actions} 37 | style={{ marginTop: 56 }} 38 | /> 39 | ); 40 | 41 | export default RegisterResult; 42 | -------------------------------------------------------------------------------- /src/pages/User/RegisterResult.less: -------------------------------------------------------------------------------- 1 | .registerResult { 2 | :global { 3 | .anticon { 4 | font-size: 64px; 5 | } 6 | } 7 | .title { 8 | margin-top: 32px; 9 | font-size: 20px; 10 | line-height: 28px; 11 | } 12 | .actions { 13 | margin-top: 40px; 14 | a + a { 15 | margin-left: 8px; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/User/models/register.js: -------------------------------------------------------------------------------- 1 | import { fakeRegister } from '@/services/api'; 2 | import { setAuthority } from '@/utils/authority'; 3 | import { reloadAuthorized } from '@/utils/Authorized'; 4 | 5 | export default { 6 | namespace: 'register', 7 | 8 | state: { 9 | status: undefined, 10 | }, 11 | 12 | effects: { 13 | *submit({ payload }, { call, put }) { 14 | const response = yield call(fakeRegister, payload); 15 | yield put({ 16 | type: 'registerHandle', 17 | payload: response, 18 | }); 19 | }, 20 | }, 21 | 22 | reducers: { 23 | registerHandle(state, { payload }) { 24 | setAuthority('user'); 25 | reloadAuthorized(); 26 | return { 27 | ...state, 28 | status: payload.status, 29 | }; 30 | }, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /src/pages/document.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ant Tabs 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 15 | 16 | -------------------------------------------------------------------------------- /src/services/error.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | export default async function queryError(code) { 4 | return request(`/api/${code}`); 5 | } 6 | -------------------------------------------------------------------------------- /src/services/geographic.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | export async function queryProvince() { 4 | return request('/api/geographic/province'); 5 | } 6 | 7 | export async function queryCity(province) { 8 | return request(`/api/geographic/city/${province}`); 9 | } 10 | -------------------------------------------------------------------------------- /src/services/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | export async function query() { 4 | return request('/api/users'); 5 | } 6 | 7 | export async function queryCurrent() { 8 | return request('/api/currentUser'); 9 | } 10 | -------------------------------------------------------------------------------- /src/style/img/ant.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuhami/react-ant/717f325a74c1da98343665a8004b5474326ec442/src/style/img/ant.jpeg -------------------------------------------------------------------------------- /src/style/less/common.less: -------------------------------------------------------------------------------- 1 | .hide { 2 | display: none; 3 | } -------------------------------------------------------------------------------- /src/utils/Authorized.js: -------------------------------------------------------------------------------- 1 | import RenderAuthorized from '@/components/Authorized'; 2 | import { getAuthority } from './authority'; 3 | 4 | let Authorized = RenderAuthorized(getAuthority()); // eslint-disable-line 5 | 6 | // Reload the rights component 7 | const reloadAuthorized = () => { 8 | Authorized = RenderAuthorized(getAuthority()); 9 | }; 10 | 11 | export { reloadAuthorized }; 12 | export default Authorized; 13 | -------------------------------------------------------------------------------- /src/utils/Yuan.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { yuan } from '@/components/Charts'; 3 | /** 4 | * 减少使用 dangerouslySetInnerHTML 5 | */ 6 | export default class Yuan extends React.PureComponent { 7 | componentDidMount() { 8 | this.rendertoHtml(); 9 | } 10 | 11 | componentDidUpdate() { 12 | this.rendertoHtml(); 13 | } 14 | 15 | rendertoHtml = () => { 16 | const { children } = this.props; 17 | if (this.main) { 18 | this.main.innerHTML = yuan(children); 19 | } 20 | }; 21 | 22 | render() { 23 | return ( 24 | { 26 | this.main = ref; 27 | }} 28 | /> 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/authority.js: -------------------------------------------------------------------------------- 1 | // use localStorage to store the authority info, which might be sent from server in actual project. 2 | export function getAuthority(str) { 3 | // return localStorage.getItem('antd-pro-authority') || ['admin', 'user']; 4 | const authorityString = 5 | typeof str === 'undefined' ? localStorage.getItem('antd-pro-authority') : str; 6 | // authorityString could be admin, "admin", ["admin"] 7 | let authority; 8 | try { 9 | authority = JSON.parse(authorityString); 10 | } catch (e) { 11 | authority = authorityString; 12 | } 13 | if (typeof authority === 'string') { 14 | return [authority]; 15 | } 16 | return authority || ['admin']; 17 | } 18 | 19 | export function setAuthority(authority) { 20 | const proAuthority = typeof authority === 'string' ? [authority] : authority; 21 | return localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority)); 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/authority.test.js: -------------------------------------------------------------------------------- 1 | import { getAuthority } from './authority'; 2 | 3 | describe('getAuthority should be strong', () => { 4 | it('empty', () => { 5 | expect(getAuthority(null)).toEqual(['admin']); // default value 6 | }); 7 | it('string', () => { 8 | expect(getAuthority('admin')).toEqual(['admin']); 9 | }); 10 | it('array with double quotes', () => { 11 | expect(getAuthority('"admin"')).toEqual(['admin']); 12 | }); 13 | it('array with single item', () => { 14 | expect(getAuthority('["admin"]')).toEqual(['admin']); 15 | }); 16 | it('array with multiple items', () => { 17 | expect(getAuthority('["admin", "guest"]')).toEqual(['admin', 'guest']); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/utils/utils.less: -------------------------------------------------------------------------------- 1 | .textOverflow() { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | word-break: break-all; 5 | white-space: nowrap; 6 | } 7 | 8 | .textOverflowMulti(@line: 3, @bg: #fff) { 9 | overflow: hidden; 10 | position: relative; 11 | line-height: 1.5em; 12 | max-height: @line * 1.5em; 13 | text-align: justify; 14 | margin-right: -1em; 15 | padding-right: 1em; 16 | &:before { 17 | background: @bg; 18 | content: '...'; 19 | padding: 0 1px; 20 | position: absolute; 21 | right: 14px; 22 | bottom: 0; 23 | } 24 | &:after { 25 | background: white; 26 | content: ''; 27 | margin-top: 0.2em; 28 | position: absolute; 29 | right: 14px; 30 | width: 1em; 31 | height: 1em; 32 | } 33 | } 34 | 35 | // mixins for clearfix 36 | // ------------------------ 37 | .clearfix() { 38 | zoom: 1; 39 | &:before, 40 | &:after { 41 | content: ' '; 42 | display: table; 43 | } 44 | &:after { 45 | clear: both; 46 | visibility: hidden; 47 | font-size: 0; 48 | height: 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/run-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const { spawn } = require('child_process'); 3 | const { kill } = require('cross-port-killer'); 4 | 5 | const env = Object.create(process.env); 6 | env.BROWSER = 'none'; 7 | env.TEST = true; 8 | // flag to prevent multiple test 9 | let once = false; 10 | 11 | const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], { 12 | env, 13 | }); 14 | 15 | startServer.stderr.on('data', data => { 16 | // eslint-disable-next-line 17 | console.log(data.toString()); 18 | }); 19 | 20 | startServer.on('exit', () => { 21 | kill(process.env.PORT || 8000); 22 | }); 23 | 24 | console.log('Starting development server for e2e tests...'); 25 | startServer.stdout.on('data', data => { 26 | console.log(data.toString(),'data.toString'); 27 | if (!once && data.toString().indexOf('Compiled successfully') >= 0) { 28 | // eslint-disable-next-line 29 | once = true; 30 | console.log('Development server is started, ready to run tests.'); 31 | const testCmd = spawn( 32 | /^win/.test(process.platform) ? 'npm.cmd' : 'npm', 33 | ['test', '--', '--maxWorkers=1', '--runInBand'], 34 | { 35 | stdio: 'inherit', 36 | } 37 | ); 38 | testCmd.on('exit', code => { 39 | startServer.kill(); 40 | process.exit(code); 41 | }); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build/dist", 4 | "module": "esnext", 5 | "target": "es2016", 6 | "lib": ["es6", "dom"], 7 | "sourceMap": true, 8 | "baseUrl": ".", 9 | "jsx": "react", 10 | "allowSyntheticDefaultImports": true, 11 | "moduleResolution": "node", 12 | "rootDirs": ["/src", "/test", "/mock","./typings"], 13 | "forceConsistentCasingInFileNames": true, 14 | "noImplicitReturns": true, 15 | "suppressImplicitAnyIndexErrors": true, 16 | "noUnusedLocals": true, 17 | "allowJs": true, 18 | "experimentalDecorators": true, 19 | "paths": { 20 | "@/*": ["./src/*"] 21 | } 22 | }, 23 | "include": ["./src"], 24 | "exclude": [ 25 | "node_modules", 26 | "build", 27 | "scripts", 28 | "acceptance-tests", 29 | "webpack", 30 | "jest", 31 | "src/setupTests.ts", 32 | "tslint:latest", 33 | "tslint-config-prettier" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-react", "tslint-config-prettier"], 3 | "rules": { 4 | "no-var-requires": false, 5 | "no-submodule-imports": false, 6 | "object-literal-sort-keys": false, 7 | "jsx-no-lambda": false, 8 | "no-implicit-dependencies": false, 9 | "no-console": false 10 | } 11 | } 12 | --------------------------------------------------------------------------------