├── public
├── CNAME
├── favicon.ico
├── home_bg.png
├── icons
│ ├── icon-128x128.png
│ ├── icon-192x192.png
│ └── icon-512x512.png
├── pro_icon.svg
└── logo.svg
├── .eslintignore
├── .prettierrc.js
├── .stylelintrc.js
├── mock
├── route.ts
├── notices.ts
├── user.ts
└── listTableList.ts
├── src
├── locales
│ ├── zh-CN
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── zh-TW
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ └── settings.ts
│ ├── fa-IR
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── ja-JP
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── en-US
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── id-ID
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── pt-BR
│ │ ├── component.ts
│ │ ├── pwa.ts
│ │ ├── globalHeader.ts
│ │ ├── settingDrawer.ts
│ │ ├── menu.ts
│ │ ├── settings.ts
│ │ └── pages.ts
│ ├── zh-TW.ts
│ ├── pt-BR.ts
│ ├── ja-JP.ts
│ ├── zh-CN.ts
│ ├── en-US.ts
│ ├── fa-IR.ts
│ └── id-ID.ts
├── pages
│ ├── Welcome.less
│ ├── project
│ │ ├── components
│ │ │ ├── seconed.tsx
│ │ │ ├── three.tsx
│ │ │ └── four.tsx
│ │ └── index.tsx
│ ├── 404.tsx
│ ├── product
│ │ ├── list
│ │ │ └── components
│ │ │ │ └── productModal.tsx
│ │ └── addProduct
│ │ │ └── index.tsx
│ ├── Admin.tsx
│ ├── Welcome.tsx
│ ├── user
│ │ └── Login
│ │ │ └── index.less
│ └── editProTable
│ │ └── index.tsx
├── services
│ ├── ant-design-pro
│ │ ├── index.ts
│ │ ├── login.ts
│ │ ├── api.ts
│ │ └── typings.d.ts
│ └── swagger
│ │ ├── index.ts
│ │ ├── typings.d.ts
│ │ ├── store.ts
│ │ ├── user.ts
│ │ └── pet.ts
├── utils
│ ├── utils.less
│ ├── utils.ts
│ └── utils.test.ts
├── access.ts
├── components
│ ├── HeaderDropdown
│ │ ├── index.less
│ │ └── index.tsx
│ ├── HeaderSearch
│ │ ├── index.less
│ │ └── index.tsx
│ ├── NoticeIcon
│ │ ├── index.less
│ │ ├── NoticeList.less
│ │ ├── NoticeList.tsx
│ │ ├── NoticeIcon.tsx
│ │ └── index.tsx
│ ├── Footer
│ │ └── index.tsx
│ ├── RightContent
│ │ ├── index.less
│ │ ├── index.tsx
│ │ └── AvatarDropdown.tsx
│ └── ProductInfo
│ │ └── index.tsx
├── models
│ └── project.ts
├── manifest.json
├── global.less
├── typings.d.ts
├── e2e
│ └── baseLayout.e2e.js
├── service-worker.js
├── global.tsx
└── app.tsx
├── jsconfig.json
├── .eslintrc.js
├── tests
├── setupTests.js
├── PuppeteerEnvironment.js
├── getBrowser.js
├── beforeTest.js
├── run-tests.js
└── issue.js
├── .editorconfig
├── jest.config.js
├── .prettierignore
├── config
├── config.dev.ts
├── defaultSettings.ts
├── proxy.ts
├── routes.ts
└── config.ts
├── .gitignore
├── README.md
├── tsconfig.json
└── package.json
/public/CNAME:
--------------------------------------------------------------------------------
1 | preview.pro.ant.design
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengbid/antprov5/master/public/favicon.ico
--------------------------------------------------------------------------------
/public/home_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengbid/antprov5/master/public/home_bg.png
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lambda/
2 | /scripts
3 | /config
4 | .history
5 | public
6 | dist
7 | .umi
8 | mock
--------------------------------------------------------------------------------
/public/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengbid/antprov5/master/public/icons/icon-128x128.png
--------------------------------------------------------------------------------
/public/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengbid/antprov5/master/public/icons/icon-192x192.png
--------------------------------------------------------------------------------
/public/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shengbid/antprov5/master/public/icons/icon-512x512.png
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.prettier,
5 | };
6 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | const fabric = require('@umijs/fabric');
2 |
3 | module.exports = {
4 | ...fabric.stylelint,
5 | };
6 |
--------------------------------------------------------------------------------
/mock/route.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | '/api/auth_routes': {
3 | '/form/advanced-form': { authority: ['admin', 'user'] },
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展开',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展開',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/fa-IR/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'باز',
3 | 'component.tagSelect.collapse': 'بسته ',
4 | 'component.tagSelect.all': 'همه',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展開',
3 | 'component.tagSelect.collapse': '折りたたむ',
4 | 'component.tagSelect.all': 'すべて',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/en-US/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Expand',
3 | 'component.tagSelect.collapse': 'Collapse',
4 | 'component.tagSelect.all': 'All',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/id-ID/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Perluas',
3 | 'component.tagSelect.collapse': 'Lipat',
4 | 'component.tagSelect.all': 'Semua',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Expandir',
3 | 'component.tagSelect.collapse': 'Diminuir',
4 | 'component.tagSelect.all': 'Todas',
5 | };
6 |
--------------------------------------------------------------------------------
/src/pages/Welcome.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .pre {
4 | margin: 12px 0;
5 | padding: 12px 20px;
6 | background: @input-bg;
7 | box-shadow: @card-shadow;
8 | }
9 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "emitDecoratorMetadata": true,
4 | "experimentalDecorators": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@umijs/fabric/dist/eslint')],
3 | globals: {
4 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
5 | page: true,
6 | REACT_APP_ENV: true,
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | // API 更新时间:
4 | // API 唯一标识:
5 | import * as api from './api';
6 | import * as login from './login';
7 | export default {
8 | api,
9 | login,
10 | };
11 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/pwa.ts:
--------------------------------------------------------------------------------
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/pwa.ts:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/tests/setupTests.js:
--------------------------------------------------------------------------------
1 | // do some test init
2 |
3 | const localStorageMock = {
4 | getItem: jest.fn(),
5 | setItem: jest.fn(),
6 | removeItem: jest.fn(),
7 | clear: jest.fn(),
8 | };
9 |
10 | global.localStorage = localStorageMock;
11 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/pwa.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': 'あなたは今オフラインです',
3 | 'app.pwa.serviceworker.updated': '新しいコンテンツが利用可能です',
4 | 'app.pwa.serviceworker.updated.hint':
5 | '現在のページをリロードするには、「更新」ボタンを押してください',
6 | 'app.pwa.serviceworker.updated.ok': 'リフレッシュ',
7 | };
8 |
--------------------------------------------------------------------------------
/src/services/swagger/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | // API 更新时间:
4 | // API 唯一标识:
5 | import * as pet from './pet';
6 | import * as store from './store';
7 | import * as user from './user';
8 | export default {
9 | pet,
10 | store,
11 | user,
12 | };
13 |
--------------------------------------------------------------------------------
/src/locales/en-US/pwa.ts:
--------------------------------------------------------------------------------
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/utils/utils.less:
--------------------------------------------------------------------------------
1 | // mixins for clearfix
2 | // ------------------------
3 | .clearfix() {
4 | zoom: 1;
5 | &::before,
6 | &::after {
7 | display: table;
8 | content: ' ';
9 | }
10 | &::after {
11 | clear: both;
12 | height: 0;
13 | font-size: 0;
14 | visibility: hidden;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testURL: 'http://localhost:8000',
3 | testEnvironment: './tests/PuppeteerEnvironment',
4 | verbose: false,
5 | extraSetupFiles: ['./tests/setupTests.js'],
6 | globals: {
7 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
8 | localStorage: null,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/src/access.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @see https://umijs.org/zh-CN/plugins/plugin-access
3 | * */
4 | export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
5 | const { currentUser } = initialState || {};
6 | return {
7 | canAdmin: currentUser && currentUser.access === 'admin',
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/src/locales/fa-IR/pwa.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': 'شما اکنون آفلاین هستید',
3 | 'app.pwa.serviceworker.updated': 'مطالب جدید در دسترس است',
4 | 'app.pwa.serviceworker.updated.hint':
5 | 'لطفاً برای بارگیری مجدد صفحه فعلی ، دکمه "تازه سازی" را فشار دهید',
6 | 'app.pwa.serviceworker.updated.ok': 'تازه سازی',
7 | };
8 |
--------------------------------------------------------------------------------
/src/locales/id-ID/pwa.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': 'Koneksi anda terputus',
3 | 'app.pwa.serviceworker.updated': 'Konten baru sudah tersedia',
4 | 'app.pwa.serviceworker.updated.hint':
5 | 'Silahkan klik tombol "Refresh" untuk memuat ulang halaman ini',
6 | 'app.pwa.serviceworker.updated.ok': 'Memuat ulang',
7 | };
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.svg
2 | package.json
3 | .umi
4 | .umi-production
5 | /dist
6 | .dockerignore
7 | .DS_Store
8 | .eslintignore
9 | *.png
10 | *.toml
11 | docker
12 | .editorconfig
13 | Dockerfile*
14 | .gitignore
15 | .prettierignore
16 | LICENSE
17 | .eslintcache
18 | *.lock
19 | yarn-error.log
20 | .history
21 | CNAME
22 | /build
23 | /public
--------------------------------------------------------------------------------
/src/locales/pt-BR/pwa.ts:
--------------------------------------------------------------------------------
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/pages/project/components/seconed.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Project3 from './three'
3 |
4 | const Project2: React.Fc = (props) => {
5 |
6 | return
7 | 项目第二层组件
8 |
{
9 | // console.log(2,name)
10 | props.onSuccess(name)
11 | }}>
12 |
13 | }
14 |
15 | export default Project2
--------------------------------------------------------------------------------
/src/pages/project/components/three.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Project4 from './four'
3 |
4 | const Project3: React.Fc = (props: {onSuccess: (name: string) => void}) => {
5 |
6 | return
7 | 项目第三层组件
8 |
{
9 | props.onSuccess(name)
10 | }}>
11 |
12 | }
13 |
14 | export default Project3
--------------------------------------------------------------------------------
/src/components/HeaderDropdown/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .container > * {
4 | background-color: @popover-bg;
5 | border-radius: 4px;
6 | box-shadow: @shadow-1-down;
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/models/project.ts:
--------------------------------------------------------------------------------
1 | export interface stateProp {
2 | update: string
3 | }
4 |
5 | const projectModel = {
6 | namespace: 'project',
7 | state: {
8 | update: ''
9 | },
10 | reducers: {
11 | changeState: (state: stateProp, { payload: name }: {payload: {name: string}}) => {
12 | return {
13 | update: name
14 | }
15 | },
16 | },
17 | }
18 |
19 | export default projectModel
--------------------------------------------------------------------------------
/src/pages/404.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from 'antd';
2 | import React from 'react';
3 | import { history } from 'umi';
4 |
5 | const NoFoundPage: React.FC = () => (
6 | history.push('/')}>
12 | Back Home
13 |
14 | }
15 | />
16 | );
17 |
18 | export default NoFoundPage;
19 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/login.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from 'umi';
4 |
5 | /** 发送验证码 POST /api/login/captcha */
6 | export async function getFakeCaptcha(
7 | params: {
8 | // query
9 | /** 手机号 */
10 | phone?: string;
11 | },
12 | options?: { [key: string]: any },
13 | ) {
14 | return request('/api/login/captcha', {
15 | method: 'POST',
16 | params: {
17 | ...params,
18 | },
19 | ...(options || {}),
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/config/config.dev.ts:
--------------------------------------------------------------------------------
1 | // https://umijs.org/config/
2 | import { defineConfig } from 'umi';
3 |
4 | export default defineConfig({
5 | plugins: [
6 | // https://github.com/zthxxx/react-dev-inspector
7 | 'react-dev-inspector/plugins/umi/react-inspector',
8 | ],
9 | // https://github.com/zthxxx/react-dev-inspector#inspector-loader-props
10 | inspectorConfig: {
11 | exclude: [],
12 | babelPlugins: [],
13 | babelOptions: {},
14 | },
15 | webpack5: {
16 | // lazyCompilation: {},
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ant Design Pro",
3 | "short_name": "Ant Design Pro",
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/components/HeaderSearch/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .headerSearch {
4 | display: inline-flex;
5 | align-items: center;
6 | .input {
7 | width: 0;
8 | min-width: 0;
9 | overflow: hidden;
10 | background: transparent;
11 | border-radius: 0;
12 | transition: width 0.3s, margin-left 0.3s;
13 | :global(.ant-select-selection) {
14 | background: transparent;
15 | }
16 | input {
17 | box-shadow: none !important;
18 | }
19 |
20 | &.show {
21 | width: 210px;
22 | margin-left: 8px;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | import component from './zh-TW/component';
2 | import globalHeader from './zh-TW/globalHeader';
3 | import menu from './zh-TW/menu';
4 | import pwa from './zh-TW/pwa';
5 | import settingDrawer from './zh-TW/settingDrawer';
6 | import settings from './zh-TW/settings';
7 |
8 | export default {
9 | 'navBar.lang': '語言',
10 | 'layout.user.link.help': '幫助',
11 | 'layout.user.link.privacy': '隱私',
12 | 'layout.user.link.terms': '條款',
13 | 'app.preview.down.block': '下載此頁面到本地項目',
14 | ...globalHeader,
15 | ...menu,
16 | ...settingDrawer,
17 | ...settings,
18 | ...pwa,
19 | ...component,
20 | };
21 |
--------------------------------------------------------------------------------
/config/defaultSettings.ts:
--------------------------------------------------------------------------------
1 | import { Settings as LayoutSettings } from '@ant-design/pro-layout';
2 |
3 | const Settings: LayoutSettings & {
4 | pwa?: boolean;
5 | logo?: string;
6 | } = {
7 | navTheme: 'light',
8 | menu: {
9 | locale: false, //关闭国际化
10 | },
11 | // 拂晓蓝
12 | primaryColor: '#1890ff',
13 | layout: 'mix',
14 | contentWidth: 'Fluid',
15 | fixedHeader: false,
16 | fixSiderbar: true,
17 | colorWeak: false,
18 | title: 'Ant Design Pro',
19 | pwa: false,
20 | logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
21 | iconfontUrl: '',
22 | };
23 |
24 | export default Settings;
25 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .popover {
4 | position: relative;
5 | width: 336px;
6 | }
7 |
8 | .noticeButton {
9 | display: inline-block;
10 | cursor: pointer;
11 | transition: all 0.3s;
12 | }
13 | .icon {
14 | padding: 4px;
15 | vertical-align: middle;
16 | }
17 |
18 | .badge {
19 | font-size: 16px;
20 | }
21 |
22 | .tabs {
23 | :global {
24 | .ant-tabs-nav-list {
25 | margin: auto;
26 | }
27 |
28 | .ant-tabs-nav-scroll {
29 | text-align: center;
30 | }
31 | .ant-tabs-bar {
32 | margin-bottom: 0;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.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 | /dist
11 | /.vscode
12 |
13 | # misc
14 | .DS_Store
15 | npm-debug.log*
16 | yarn-error.log
17 |
18 | /coverage
19 | .idea
20 | yarn.lock
21 | package-lock.json
22 | *bak
23 | .vscode
24 |
25 | # visual studio code
26 | .history
27 | *.log
28 | functions/*
29 | .temp/**
30 |
31 | # umi
32 | .umi
33 | .umi-production
34 |
35 | # screenshot
36 | screenshot
37 | .firebase
38 | .eslintcache
39 |
40 | build
41 |
--------------------------------------------------------------------------------
/src/pages/project/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import Project2 from './components/seconed'
3 | import { connect } from 'dva'
4 |
5 | const Project: React.Fc = (props) => {
6 | const [name, setName] = useState('小李')
7 | console.log(props.update)
8 |
9 | return
10 | 项目顶层组件
11 |
12 | 姓名: {name}
13 |
14 |
15 | dva姓名: {props.update}
16 |
17 |
{
18 | setName(name)
19 | console.log(999)
20 | }}>
21 |
22 | }
23 |
24 | export default connect(({ project }) => ({
25 | update: project.update,
26 | }))(Project)
--------------------------------------------------------------------------------
/public/pro_icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/locales/pt-BR.ts:
--------------------------------------------------------------------------------
1 | import component from './pt-BR/component';
2 | import globalHeader from './pt-BR/globalHeader';
3 | import menu from './pt-BR/menu';
4 | import pwa from './pt-BR/pwa';
5 | import settingDrawer from './pt-BR/settingDrawer';
6 | import settings from './pt-BR/settings';
7 | import pages from './pt-BR/pages';
8 |
9 | export default {
10 | 'navBar.lang': 'Idiomas',
11 | 'layout.user.link.help': 'ajuda',
12 | 'layout.user.link.privacy': 'política de privacidade',
13 | 'layout.user.link.terms': 'termos de serviços',
14 | 'app.preview.down.block': 'Download this page to your local project',
15 | ...globalHeader,
16 | ...menu,
17 | ...settingDrawer,
18 | ...settings,
19 | ...pwa,
20 | ...component,
21 | ...pages,
22 | };
23 |
--------------------------------------------------------------------------------
/src/pages/project/components/four.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from 'antd'
2 | import React, { useState } from 'react'
3 | import { connect } from 'dva'
4 |
5 | const Project4: React.Fc = (props: {onSuccess: (name: string) => void, dispatch: any}) => {
6 | const [name, setName] = useState('')
7 |
8 | const clickName = () => {
9 | setName(`小明${Math.floor(Math.random()*10)}`)
10 | props.onSuccess(name)
11 | props.dispatch({
12 | type: 'project/changeState',
13 | payload: name,
14 | })
15 | }
16 |
17 | return
18 | 项目第四层组件
19 |
姓名: {name}
20 |
21 |
22 | }
23 |
24 | export default connect(({ project }) => ({
25 | project,
26 | }))(Project4)
--------------------------------------------------------------------------------
/src/components/HeaderDropdown/index.tsx:
--------------------------------------------------------------------------------
1 | import type { DropDownProps } from 'antd/es/dropdown';
2 | import { Dropdown } from 'antd';
3 | import React from 'react';
4 | import classNames from 'classnames';
5 | import styles from './index.less';
6 |
7 | export type HeaderDropdownProps = {
8 | overlayClassName?: string;
9 | overlay: React.ReactNode | (() => React.ReactNode) | any;
10 | placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
11 | } & Omit;
12 |
13 | const HeaderDropdown: React.FC = ({ overlayClassName: cls, ...restProps }) => (
14 |
15 | );
16 |
17 | export default HeaderDropdown;
18 |
--------------------------------------------------------------------------------
/src/locales/ja-JP.ts:
--------------------------------------------------------------------------------
1 | import globalHeader from './ja-JP/globalHeader';
2 | import menu from './ja-JP/menu';
3 | import settingDrawer from './ja-JP/settingDrawer';
4 | import settings from './ja-JP/settings';
5 | import pwa from './ja-JP/pwa';
6 | import component from './ja-JP/component';
7 | import pages from './ja-JP/pages';
8 |
9 | export default {
10 | 'navBar.lang': '言語',
11 | 'layout.user.link.help': 'ヘルプ',
12 | 'layout.user.link.privacy': 'プライバシー',
13 | 'layout.user.link.terms': '利用規約',
14 | 'app.preview.down.block': 'このページをローカルプロジェクトにダウンロードしてください',
15 | 'app.welcome.link.fetch-blocks': '',
16 | 'app.welcome.link.block-list': '',
17 | ...globalHeader,
18 | ...menu,
19 | ...settingDrawer,
20 | ...settings,
21 | ...pwa,
22 | ...component,
23 | ...pages,
24 | };
25 |
--------------------------------------------------------------------------------
/src/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | import component from './zh-CN/component';
2 | import globalHeader from './zh-CN/globalHeader';
3 | import menu from './zh-CN/menu';
4 | import pwa from './zh-CN/pwa';
5 | import settingDrawer from './zh-CN/settingDrawer';
6 | import settings from './zh-CN/settings';
7 | import pages from './zh-CN/pages';
8 |
9 | export default {
10 | 'navBar.lang': '语言',
11 | 'layout.user.link.help': '帮助',
12 | 'layout.user.link.privacy': '隐私',
13 | 'layout.user.link.terms': '条款',
14 | 'app.preview.down.block': '下载此页面到本地项目',
15 | 'app.welcome.link.fetch-blocks': '获取全部区块',
16 | 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
17 | ...pages,
18 | ...globalHeader,
19 | ...menu,
20 | ...settingDrawer,
21 | ...settings,
22 | ...pwa,
23 | ...component,
24 | };
25 |
--------------------------------------------------------------------------------
/config/proxy.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
3 | * The agent cannot take effect in the production environment
4 | * so there is no configuration of the production environment
5 | * For details, please see
6 | * https://pro.ant.design/docs/deploy
7 | */
8 | export default {
9 | dev: {
10 | '/api/': {
11 | target: 'https://preview.pro.ant.design',
12 | changeOrigin: true,
13 | pathRewrite: { '^': '' },
14 | },
15 | },
16 | test: {
17 | '/api/': {
18 | target: 'https://preview.pro.ant.design',
19 | changeOrigin: true,
20 | pathRewrite: { '^': '' },
21 | },
22 | },
23 | pre: {
24 | '/api/': {
25 | target: 'your pre url',
26 | changeOrigin: true,
27 | pathRewrite: { '^': '' },
28 | },
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import { GithubOutlined } from '@ant-design/icons';
2 | import { DefaultFooter } from '@ant-design/pro-layout';
3 |
4 | export default () => (
5 | ,
17 | href: 'https://github.com/ant-design/ant-design-pro',
18 | blankTarget: true,
19 | },
20 | {
21 | key: 'Ant Design',
22 | title: 'Ant Design',
23 | href: 'https://ant.design',
24 | blankTarget: true,
25 | },
26 | ]}
27 | />
28 | );
29 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/globalHeader.ts:
--------------------------------------------------------------------------------
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.view-more': '查看更多',
17 | };
18 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/globalHeader.ts:
--------------------------------------------------------------------------------
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.view-more': '查看更多',
17 | };
18 |
--------------------------------------------------------------------------------
/src/utils/utils.ts:
--------------------------------------------------------------------------------
1 | /* eslint no-useless-escape:0 import/prefer-default-export:0 */
2 | const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
3 |
4 | export const isUrl = (path: string): boolean => reg.test(path);
5 |
6 | export const isAntDesignPro = (): boolean => {
7 | if (ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
8 | return true;
9 | }
10 | return window.location.hostname === 'preview.pro.ant.design';
11 | };
12 |
13 | // 给官方演示站点用,用于关闭真实开发环境不需要使用的特性
14 | export const isAntDesignProOrDev = (): boolean => {
15 | const { NODE_ENV } = process.env;
16 | if (NODE_ENV === 'development') {
17 | return true;
18 | }
19 | return isAntDesignPro();
20 | };
21 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/globalHeader.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': '検索',
3 | 'component.globalHeader.search.example1': '検索例1',
4 | 'component.globalHeader.search.example2': '検索例2',
5 | 'component.globalHeader.search.example3': '検索例3',
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.view-more': 'もっと見る',
17 | };
18 |
--------------------------------------------------------------------------------
/src/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | import component from './en-US/component';
2 | import globalHeader from './en-US/globalHeader';
3 | import menu from './en-US/menu';
4 | import pwa from './en-US/pwa';
5 | import settingDrawer from './en-US/settingDrawer';
6 | import settings from './en-US/settings';
7 | import pages from './en-US/pages';
8 |
9 | export default {
10 | 'navBar.lang': 'Languages',
11 | 'layout.user.link.help': 'Help',
12 | 'layout.user.link.privacy': 'Privacy',
13 | 'layout.user.link.terms': 'Terms',
14 | 'app.preview.down.block': 'Download this page to your local project',
15 | 'app.welcome.link.fetch-blocks': 'Get all block',
16 | 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
17 | ...globalHeader,
18 | ...menu,
19 | ...settingDrawer,
20 | ...settings,
21 | ...pwa,
22 | ...component,
23 | ...pages,
24 | };
25 |
--------------------------------------------------------------------------------
/src/locales/fa-IR.ts:
--------------------------------------------------------------------------------
1 | import component from './fa-IR/component';
2 | import globalHeader from './fa-IR/globalHeader';
3 | import menu from './fa-IR/menu';
4 | import pwa from './fa-IR/pwa';
5 | import settingDrawer from './fa-IR/settingDrawer';
6 | import settings from './fa-IR/settings';
7 | import pages from './fa-IR/pages';
8 |
9 | export default {
10 | 'navBar.lang': 'زبان ها ',
11 | 'layout.user.link.help': 'کمک',
12 | 'layout.user.link.privacy': 'حریم خصوصی',
13 | 'layout.user.link.terms': 'مقررات',
14 | 'app.preview.down.block': 'این صفحه را در پروژه محلی خود بارگیری کنید',
15 | 'app.welcome.link.fetch-blocks': 'دریافت تمام بلوک',
16 | 'app.welcome.link.block-list': 'به سرعت صفحات استاندارد مبتنی بر توسعه "بلوک" را بسازید',
17 | ...globalHeader,
18 | ...menu,
19 | ...settingDrawer,
20 | ...settings,
21 | ...pwa,
22 | ...component,
23 | ...pages,
24 | };
25 |
--------------------------------------------------------------------------------
/src/locales/id-ID.ts:
--------------------------------------------------------------------------------
1 | import component from './id-ID/component';
2 | import globalHeader from './id-ID/globalHeader';
3 | import menu from './id-ID/menu';
4 | import pwa from './id-ID/pwa';
5 | import settingDrawer from './id-ID/settingDrawer';
6 | import settings from './id-ID/settings';
7 | import pages from './id-ID/pages';
8 |
9 | export default {
10 | 'navbar.lang': 'Bahasa',
11 | 'layout.user.link.help': 'Bantuan',
12 | 'layout.user.link.privacy': 'Privasi',
13 | 'layout.user.link.terms': 'Ketentuan',
14 | 'app.preview.down.block': 'Unduh halaman ini dalam projek lokal anda',
15 | 'app.welcome.link.fetch-blocks': 'Dapatkan semua blok',
16 | 'app.welcome.link.block-list':
17 | 'Buat standar dengan cepat, halaman-halaman berdasarkan pengembangan `block`',
18 | ...globalHeader,
19 | ...menu,
20 | ...settingDrawer,
21 | ...settings,
22 | ...pwa,
23 | ...component,
24 | ...pages,
25 | };
26 |
--------------------------------------------------------------------------------
/src/pages/product/list/components/productModal.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react'
2 | import { Modal } from 'antd'
3 | import ProductInfo from '@/components/ProductInfo'
4 |
5 | export type productProps = {
6 | modalVisible: boolean
7 | infoData?: any
8 | onCancel: () => void
9 | }
10 |
11 | const ProductModal = ({modalVisible, infoData, onCancel}: productProps) => {
12 |
13 | const productRef = useRef({})
14 |
15 | const onSubmit = async() => {
16 | const form = productRef.current.getForm()
17 | await form.validateFields()
18 | const data = form.getFieldsValue()
19 | console.log(data)
20 | }
21 |
22 | return (
23 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default ProductModal
--------------------------------------------------------------------------------
/src/locales/en-US/globalHeader.ts:
--------------------------------------------------------------------------------
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.view-more': 'View more',
17 | };
18 |
--------------------------------------------------------------------------------
/src/locales/fa-IR/globalHeader.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': 'جستجو ',
3 | 'component.globalHeader.search.example1': 'مثال 1 را جستجو کنید',
4 | 'component.globalHeader.search.example2': 'مثال 2 را جستجو کنید',
5 | 'component.globalHeader.search.example3': 'مثال 3 را جستجو کنید',
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.view-more': 'نمایش بیشتر',
17 | };
18 |
--------------------------------------------------------------------------------
/src/locales/id-ID/globalHeader.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.globalHeader.search': 'Pencarian',
3 | 'component.globalHeader.search.example1': 'Contoh 1 Pencarian',
4 | 'component.globalHeader.search.example2': 'Contoh 2 Pencarian',
5 | 'component.globalHeader.search.example3': 'Contoh 3 Pencarian',
6 | 'component.globalHeader.help': 'Bantuan',
7 | 'component.globalHeader.notification': 'Notifikasi',
8 | 'component.globalHeader.notification.empty': 'Anda telah membaca semua notifikasi',
9 | 'component.globalHeader.message': 'Pesan',
10 | 'component.globalHeader.message.empty': 'Anda telah membaca semua pesan.',
11 | 'component.globalHeader.event': 'Acara',
12 | 'component.globalHeader.event.empty': 'Anda telah melihat semua acara.',
13 | 'component.noticeIcon.clear': 'Kosongkan',
14 | 'component.noticeIcon.cleared': 'Berhasil dikosongkan',
15 | 'component.noticeIcon.empty': 'Tidak ada pemberitahuan',
16 | 'component.noticeIcon.view-more': 'Melihat lebih',
17 | };
18 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/globalHeader.ts:
--------------------------------------------------------------------------------
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.view-more': 'Veja mais',
18 | };
19 |
--------------------------------------------------------------------------------
/src/global.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | html,
4 | body,
5 | #root {
6 | height: 100%;
7 | }
8 |
9 | .colorWeak {
10 | filter: invert(80%);
11 | }
12 |
13 | .ant-layout {
14 | min-height: 100vh;
15 | }
16 | .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
17 | left: unset;
18 | }
19 |
20 | canvas {
21 | display: block;
22 | }
23 |
24 | body {
25 | text-rendering: optimizeLegibility;
26 | -webkit-font-smoothing: antialiased;
27 | -moz-osx-font-smoothing: grayscale;
28 | }
29 |
30 | ul,
31 | ol {
32 | list-style: none;
33 | }
34 |
35 | @media (max-width: @screen-xs) {
36 | .ant-table {
37 | width: 100%;
38 | overflow-x: auto;
39 | &-thead > tr,
40 | &-tbody > tr {
41 | > th,
42 | > td {
43 | white-space: pre;
44 | > span {
45 | display: block;
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | // 兼容IE11
53 | @media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
54 | body .ant-design-pro > .ant-layout {
55 | min-height: 100vh;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ant Design Pro
2 |
3 | This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
4 |
5 | ## Environment Prepare
6 |
7 | Install `node_modules`:
8 |
9 | ```bash
10 | npm install
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn
17 | ```
18 |
19 | ## Provided Scripts
20 |
21 | Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
22 |
23 | Scripts provided in `package.json`. It's safe to modify or add additional script:
24 |
25 | ### Start project
26 |
27 | ```bash
28 | npm start
29 | ```
30 |
31 | ### Build project
32 |
33 | ```bash
34 | npm run build
35 | ```
36 |
37 | ### Check code style
38 |
39 | ```bash
40 | npm run lint
41 | ```
42 |
43 | You can also use script to auto fix some lint error:
44 |
45 | ```bash
46 | npm run lint:fix
47 | ```
48 |
49 | ### Test code
50 |
51 | ```bash
52 | npm test
53 | ```
54 |
55 | ## More
56 |
57 | You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
58 |
--------------------------------------------------------------------------------
/src/services/swagger/typings.d.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 |
4 | declare namespace API {
5 | type Order = {
6 | id?: number;
7 | petId?: number;
8 | quantity?: number;
9 | shipDate?: string;
10 | /** Order Status */
11 | status?: 'placed' | 'approved' | 'delivered';
12 | complete?: boolean;
13 | };
14 |
15 | type Category = {
16 | id?: number;
17 | name?: string;
18 | };
19 |
20 | type User = {
21 | id?: number;
22 | username?: string;
23 | firstName?: string;
24 | lastName?: string;
25 | email?: string;
26 | password?: string;
27 | phone?: string;
28 | /** User Status */
29 | userStatus?: number;
30 | };
31 |
32 | type Tag = {
33 | id?: number;
34 | name?: string;
35 | };
36 |
37 | type Pet = {
38 | id?: number;
39 | category?: Category;
40 | name: string;
41 | photoUrls: string[];
42 | tags?: Tag[];
43 | /** pet status in the store */
44 | status?: 'available' | 'pending' | 'sold';
45 | };
46 |
47 | type ApiResponse = {
48 | code?: number;
49 | type?: string;
50 | message?: string;
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "build/dist",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "lib": ["esnext", "dom"],
7 | "sourceMap": true,
8 | "baseUrl": ".",
9 | "jsx": "react",
10 | "resolveJsonModule": true,
11 | "allowSyntheticDefaultImports": true,
12 | "moduleResolution": "node",
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "allowJs": true,
18 | "skipLibCheck": true,
19 | "experimentalDecorators": true,
20 | "strict": true,
21 | "paths": {
22 | "@/*": ["./src/*"],
23 | "@@/*": ["./src/.umi/*"]
24 | }
25 | },
26 | "include": [
27 | "mock/**/*",
28 | "src/**/*",
29 | "tests/**/*",
30 | "test/**/*",
31 | "__test__/**/*",
32 | "typings/**/*",
33 | "config/**/*",
34 | ".eslintrc.js",
35 | ".stylelintrc.js",
36 | ".prettierrc.js",
37 | "jest.config.js",
38 | "mock/*"
39 | ],
40 | "exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
41 | }
42 |
--------------------------------------------------------------------------------
/tests/PuppeteerEnvironment.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | const NodeEnvironment = require('jest-environment-node');
3 | const getBrowser = require('./getBrowser');
4 |
5 | class PuppeteerEnvironment extends NodeEnvironment {
6 | // Jest is not available here, so we have to reverse engineer
7 | // the setTimeout function, see https://github.com/facebook/jest/blob/v23.1.0/packages/jest-runtime/src/index.js#L823
8 | setTimeout(timeout) {
9 | if (this.global.jasmine) {
10 | // eslint-disable-next-line no-underscore-dangle
11 | this.global.jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout;
12 | } else {
13 | this.global[Symbol.for('TEST_TIMEOUT_SYMBOL')] = timeout;
14 | }
15 | }
16 |
17 | async setup() {
18 | const browser = await getBrowser();
19 | const page = await browser.newPage();
20 | this.global.browser = browser;
21 | this.global.page = page;
22 | }
23 |
24 | async teardown() {
25 | const { page, browser } = this.global;
26 |
27 | if (page) {
28 | await page.close();
29 | }
30 |
31 | if (browser) {
32 | await browser.disconnect();
33 | }
34 |
35 | if (browser) {
36 | await browser.close();
37 | }
38 | }
39 | }
40 |
41 | module.exports = PuppeteerEnvironment;
42 |
--------------------------------------------------------------------------------
/src/components/RightContent/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | @pro-header-hover-bg: rgba(0, 0, 0, 0.025);
4 |
5 | .menu {
6 | :global(.anticon) {
7 | margin-right: 8px;
8 | }
9 | :global(.ant-dropdown-menu-item) {
10 | min-width: 160px;
11 | }
12 | }
13 |
14 | .right {
15 | display: flex;
16 | float: right;
17 | height: 48px;
18 | margin-left: auto;
19 | overflow: hidden;
20 | .action {
21 | display: flex;
22 | align-items: center;
23 | height: 48px;
24 | padding: 0 12px;
25 | cursor: pointer;
26 | transition: all 0.3s;
27 | > span {
28 | vertical-align: middle;
29 | }
30 | &:hover {
31 | background: @pro-header-hover-bg;
32 | }
33 | &:global(.opened) {
34 | background: @pro-header-hover-bg;
35 | }
36 | }
37 | .search {
38 | padding: 0 12px;
39 | &:hover {
40 | background: transparent;
41 | }
42 | }
43 | .account {
44 | .avatar {
45 | margin-right: 8px;
46 | color: @primary-color;
47 | vertical-align: top;
48 | background: rgba(255, 255, 255, 0.85);
49 | }
50 | }
51 | }
52 |
53 | .dark {
54 | .action {
55 | &:hover {
56 | background: #252a3d;
57 | }
58 | &:global(.opened) {
59 | background: #252a3d;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'slash2';
2 | declare module '*.css';
3 | declare module '*.less';
4 | declare module '*.scss';
5 | declare module '*.sass';
6 | declare module '*.svg';
7 | declare module '*.png';
8 | declare module '*.jpg';
9 | declare module '*.jpeg';
10 | declare module '*.gif';
11 | declare module '*.bmp';
12 | declare module '*.tiff';
13 | declare module 'omit.js';
14 | declare module 'numeral';
15 | declare module '@antv/data-set';
16 | declare module 'mockjs';
17 | declare module 'react-fittext';
18 | declare module 'bizcharts-plugin-slider';
19 |
20 | // google analytics interface
21 | type GAFieldsObject = {
22 | eventCategory: string;
23 | eventAction: string;
24 | eventLabel?: string;
25 | eventValue?: number;
26 | nonInteraction?: boolean;
27 | };
28 |
29 | type Window = {
30 | ga: (
31 | command: 'send',
32 | hitType: 'event' | 'pageview',
33 | fieldsObject: GAFieldsObject | string,
34 | ) => void;
35 | reloadAuthorized: () => void;
36 | routerBase: string;
37 | };
38 |
39 | declare let ga: () => void;
40 |
41 | // preview.pro.ant.design only do not use in your production ;
42 | // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
43 | declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined;
44 |
45 | declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
46 |
--------------------------------------------------------------------------------
/tests/getBrowser.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | const findChrome = require('carlo/lib/find_chrome');
4 |
5 | const getBrowser = async () => {
6 | try {
7 | // eslint-disable-next-line import/no-unresolved
8 | const puppeteer = require('puppeteer');
9 | const browser = await puppeteer.launch({
10 | args: [
11 | '--disable-gpu',
12 | '--disable-dev-shm-usage',
13 | '--no-first-run',
14 | '--no-zygote',
15 | '--no-sandbox',
16 | ],
17 | });
18 | return browser;
19 | } catch (error) {
20 | // console.log(error)
21 | }
22 |
23 | try {
24 | // eslint-disable-next-line import/no-unresolved
25 | const puppeteer = require('puppeteer-core');
26 | const findChromePath = await findChrome({});
27 | const { executablePath } = findChromePath;
28 | const browser = await puppeteer.launch({
29 | executablePath,
30 | args: [
31 | '--disable-gpu',
32 | '--disable-dev-shm-usage',
33 | '--no-first-run',
34 | '--no-zygote',
35 | '--no-sandbox',
36 | ],
37 | });
38 | return browser;
39 | } catch (error) {
40 | console.log('🧲 no find chrome');
41 | }
42 | throw new Error('no find puppeteer');
43 | };
44 |
45 | module.exports = getBrowser;
46 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/settingDrawer.ts:
--------------------------------------------------------------------------------
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 | };
32 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/settingDrawer.ts:
--------------------------------------------------------------------------------
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 | };
32 |
--------------------------------------------------------------------------------
/tests/beforeTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | const { execSync } = require('child_process');
4 | const { join } = require('path');
5 | const findChrome = require('carlo/lib/find_chrome');
6 | const detectInstaller = require('detect-installer');
7 |
8 | const installPuppeteer = () => {
9 | // find can use package manger
10 | const packages = detectInstaller(join(__dirname, '../../'));
11 | // get installed package manger
12 | const packageName = packages.find(detectInstaller.hasPackageCommand) || 'npm';
13 | console.log(`🤖 will use ${packageName} install puppeteer`);
14 | const command = `${packageName} ${packageName.includes('yarn') ? 'add' : 'i'} puppeteer`;
15 | execSync(command, {
16 | stdio: 'inherit',
17 | });
18 | };
19 |
20 | const initPuppeteer = async () => {
21 | try {
22 | // eslint-disable-next-line import/no-unresolved
23 | const findChromePath = await findChrome({});
24 | const { executablePath } = findChromePath;
25 | console.log(`🧲 find you browser in ${executablePath}`);
26 | return;
27 | } catch (error) {
28 | console.log('🧲 no find chrome');
29 | }
30 |
31 | try {
32 | require.resolve('puppeteer');
33 | } catch (error) {
34 | // need install puppeteer
35 | await installPuppeteer();
36 | }
37 | };
38 |
39 | initPuppeteer();
40 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/settingDrawer.ts:
--------------------------------------------------------------------------------
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': '固定ヘッダー',
21 | 'app.setting.fixedsidebar': '固定サイドバー',
22 | 'app.setting.fixedsidebar.hint': 'サイドメニューのレイアウトで動作します',
23 | 'app.setting.hideheader': 'スクロール時の非表示ヘッダー',
24 | 'app.setting.hideheader.hint': '非表示ヘッダーが有効になっている場合に機能します',
25 | 'app.setting.othersettings': 'その他の設定',
26 | 'app.setting.weakmode': 'ウィークモード',
27 | 'app.setting.copy': 'コピー設定',
28 | 'app.setting.copyinfo':
29 | 'コピーが成功しました。src/models/setting.jsのdefaultSettingsを置き換えてください',
30 | 'app.setting.production.hint': '設定パネルは開発環境でのみ表示されます。手動で変更してください',
31 | };
32 |
--------------------------------------------------------------------------------
/src/pages/Admin.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
3 | import { Card, Typography, Alert } from 'antd';
4 | import { PageHeaderWrapper } from '@ant-design/pro-layout';
5 | import { useIntl } from 'umi';
6 |
7 | export default (): React.ReactNode => {
8 | const intl = useIntl();
9 | return (
10 |
16 |
17 |
30 |
31 | Ant Design Pro You
32 |
33 |
34 |
35 | Want to add more pages? Please refer to{' '}
36 |
37 | use block
38 |
39 | 。
40 |
41 |
42 | );
43 | };
44 |
--------------------------------------------------------------------------------
/src/pages/product/addProduct/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react'
2 | import ProductInfo from '@/components/ProductInfo'
3 | import { Button, Collapse } from 'antd'
4 | import { history, useLocation } from 'umi'
5 |
6 | const { Panel } = Collapse
7 |
8 | const formInput = (props: any) => {
9 | const params = useLocation()
10 | console.log(props)
11 | console.log(params)
12 | // hash: ""
13 | // key: "inw4iu"
14 | // pathname: "/product/add"
15 | // query: {name: "跳转的参数"}
16 | // search: "name=%E8%B7%B3%E8%BD%AC%E7%9A%84%E5%8F%82%E6%95%B0"
17 |
18 | const childRef = useRef()
19 |
20 | // 保存
21 | const onSave = async() => {
22 | // 获取子组件数据
23 | const childForm = childRef.current.getForm()
24 | await childForm.validateFields()
25 | const data = childForm.getFieldsValue()
26 | console.log(data)
27 | }
28 |
29 | return (
30 |
31 |
32 |
35 |
36 |
37 |
38 | {/*子组件 */}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 | export default formInput
--------------------------------------------------------------------------------
/src/locales/fa-IR/settingDrawer.ts:
--------------------------------------------------------------------------------
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': 'سرصفحه ثابت',
21 | 'app.setting.fixedsidebar': 'نوار کناری ثابت',
22 | 'app.setting.fixedsidebar.hint': 'کار بر روی منوی کناری',
23 | 'app.setting.hideheader': 'هدر پنهان هنگام پیمایش',
24 | 'app.setting.hideheader.hint': 'وقتی Hidden Header فعال باشد کار می کند',
25 | 'app.setting.othersettings': 'تنظیمات دیگر',
26 | 'app.setting.weakmode': 'حالت ضعیف',
27 | 'app.setting.copy': 'تنظیمات کپی',
28 | 'app.setting.copyinfo':
29 | 'موفقیت در کپی کردن , لطفا defaultSettings را در src / models / setting.js جایگزین کنید',
30 | 'app.setting.production.hint':
31 | 'صفحه تنظیم فقط در محیط توسعه نمایش داده می شود ، لطفاً دستی تغییر دهید',
32 | };
33 |
--------------------------------------------------------------------------------
/src/locales/en-US/settingDrawer.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': 'Page style setting',
3 | 'app.setting.pagestyle.dark': 'Dark style',
4 | 'app.setting.pagestyle.light': 'Light style',
5 | 'app.setting.content-width': 'Content Width',
6 | 'app.setting.content-width.fixed': 'Fixed',
7 | 'app.setting.content-width.fluid': 'Fluid',
8 | 'app.setting.themecolor': 'Theme Color',
9 | 'app.setting.themecolor.dust': 'Dust Red',
10 | 'app.setting.themecolor.volcano': 'Volcano',
11 | 'app.setting.themecolor.sunset': 'Sunset Orange',
12 | 'app.setting.themecolor.cyan': 'Cyan',
13 | 'app.setting.themecolor.green': 'Polar Green',
14 | 'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
15 | 'app.setting.themecolor.geekblue': 'Geek Glue',
16 | 'app.setting.themecolor.purple': 'Golden Purple',
17 | 'app.setting.navigationmode': 'Navigation Mode',
18 | 'app.setting.sidemenu': 'Side Menu Layout',
19 | 'app.setting.topmenu': 'Top Menu Layout',
20 | 'app.setting.fixedheader': 'Fixed Header',
21 | 'app.setting.fixedsidebar': 'Fixed Sidebar',
22 | 'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
23 | 'app.setting.hideheader': 'Hidden Header when scrolling',
24 | 'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
25 | 'app.setting.othersettings': 'Other Settings',
26 | 'app.setting.weakmode': 'Weak Mode',
27 | 'app.setting.copy': 'Copy Setting',
28 | 'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js',
29 | 'app.setting.production.hint':
30 | 'Setting panel shows in development environment only, please manually modify',
31 | };
32 |
--------------------------------------------------------------------------------
/tests/run-tests.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable eslint-comments/disable-enable-pair */
2 | /* eslint-disable @typescript-eslint/no-var-requires */
3 | /* eslint-disable eslint-comments/no-unlimited-disable */
4 | const { spawn } = require('child_process');
5 | // eslint-disable-next-line import/no-extraneous-dependencies
6 | const { kill } = require('cross-port-killer');
7 |
8 | const env = Object.create(process.env);
9 | env.BROWSER = 'none';
10 | env.TEST = true;
11 | env.UMI_UI = 'none';
12 | env.PROGRESS = 'none';
13 | // flag to prevent multiple test
14 | let once = false;
15 |
16 | const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], {
17 | env,
18 | });
19 |
20 | startServer.stderr.on('data', (data) => {
21 | // eslint-disable-next-line
22 | console.log(data.toString());
23 | });
24 |
25 | startServer.on('exit', () => {
26 | kill(process.env.PORT || 8000);
27 | });
28 |
29 | console.log('Starting development server for e2e tests...');
30 | startServer.stdout.on('data', (data) => {
31 | console.log(data.toString());
32 | // hack code , wait umi
33 | if (
34 | (!once && data.toString().indexOf('Compiled successfully') >= 0) ||
35 | data.toString().indexOf('Theme generated successfully') >= 0
36 | ) {
37 | // eslint-disable-next-line
38 | once = true;
39 | console.log('Development server is started, ready to run tests.');
40 | const testCmd = spawn(
41 | /^win/.test(process.platform) ? 'npm.cmd' : 'npm',
42 | ['test', '--', '--maxWorkers=1', '--runInBand'],
43 | {
44 | stdio: 'inherit',
45 | },
46 | );
47 | testCmd.on('exit', (code) => {
48 | startServer.kill();
49 | process.exit(code);
50 | });
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/src/locales/id-ID/settingDrawer.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': 'Pengaturan style Halaman',
3 | 'app.setting.pagestyle.dark': 'Style Gelap',
4 | 'app.setting.pagestyle.light': 'Style Cerah',
5 | 'app.setting.content-width': 'Lebar Konten',
6 | 'app.setting.content-width.fixed': 'Tetap',
7 | 'app.setting.content-width.fluid': 'Fluid',
8 | 'app.setting.themecolor': 'Theme Color',
9 | 'app.setting.themecolor.dust': 'Dust Red',
10 | 'app.setting.themecolor.volcano': 'Volcano',
11 | 'app.setting.themecolor.sunset': 'Sunset Orange',
12 | 'app.setting.themecolor.cyan': 'Cyan',
13 | 'app.setting.themecolor.green': 'Polar Green',
14 | 'app.setting.themecolor.daybreak': 'Daybreak Blue (bawaan)',
15 | 'app.setting.themecolor.geekblue': 'Geek Glue',
16 | 'app.setting.themecolor.purple': 'Golden Purple',
17 | 'app.setting.navigationmode': 'Mode Navigasi',
18 | 'app.setting.sidemenu': 'Susunan Menu Samping',
19 | 'app.setting.topmenu': 'Susunan Menu Atas',
20 | 'app.setting.fixedheader': 'Header Tetap',
21 | 'app.setting.fixedsidebar': 'Sidebar Tetap',
22 | 'app.setting.fixedsidebar.hint': 'Berjalan pada Susunan Menu Samping',
23 | 'app.setting.hideheader': 'Sembunyikan Header ketika gulir ke bawah',
24 | 'app.setting.hideheader.hint': 'Bekerja ketika Header tersembunyi dimunculkan',
25 | 'app.setting.othersettings': 'Pengaturan Lainnya',
26 | 'app.setting.weakmode': 'Mode Lemah',
27 | 'app.setting.copy': 'Salin Pengaturan',
28 | 'app.setting.copyinfo':
29 | 'Berhasil disalin,tolong ubah defaultSettings pada src/models/setting.js',
30 | 'app.setting.production.hint':
31 | 'Panel pengaturan hanya muncul pada lingkungan pengembangan, silahkan modifikasi secara menual',
32 | };
33 |
--------------------------------------------------------------------------------
/src/utils/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { isUrl } from './utils';
2 |
3 | describe('isUrl tests', () => {
4 | it('should return false for invalid and corner case inputs', () => {
5 | expect(isUrl([] as any)).toBeFalsy();
6 | expect(isUrl({} as any)).toBeFalsy();
7 | expect(isUrl(false as any)).toBeFalsy();
8 | expect(isUrl(true as any)).toBeFalsy();
9 | expect(isUrl(NaN as any)).toBeFalsy();
10 | expect(isUrl(null as any)).toBeFalsy();
11 | expect(isUrl(undefined as any)).toBeFalsy();
12 | expect(isUrl('')).toBeFalsy();
13 | });
14 |
15 | it('should return false for invalid URLs', () => {
16 | expect(isUrl('foo')).toBeFalsy();
17 | expect(isUrl('bar')).toBeFalsy();
18 | expect(isUrl('bar/test')).toBeFalsy();
19 | expect(isUrl('http:/example.com/')).toBeFalsy();
20 | expect(isUrl('ttp://example.com/')).toBeFalsy();
21 | });
22 |
23 | it('should return true for valid URLs', () => {
24 | expect(isUrl('http://example.com/')).toBeTruthy();
25 | expect(isUrl('https://example.com/')).toBeTruthy();
26 | expect(isUrl('http://example.com/test/123')).toBeTruthy();
27 | expect(isUrl('https://example.com/test/123')).toBeTruthy();
28 | expect(isUrl('http://example.com/test/123?foo=bar')).toBeTruthy();
29 | expect(isUrl('https://example.com/test/123?foo=bar')).toBeTruthy();
30 | expect(isUrl('http://www.example.com/')).toBeTruthy();
31 | expect(isUrl('https://www.example.com/')).toBeTruthy();
32 | expect(isUrl('http://www.example.com/test/123')).toBeTruthy();
33 | expect(isUrl('https://www.example.com/test/123')).toBeTruthy();
34 | expect(isUrl('http://www.example.com/test/123?foo=bar')).toBeTruthy();
35 | expect(isUrl('https://www.example.com/test/123?foo=bar')).toBeTruthy();
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/config/routes.ts:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | path: '/user',
4 | layout: false,
5 | routes: [
6 | {
7 | path: '/user',
8 | routes: [
9 | {
10 | name: 'login',
11 | path: '/user/login',
12 | component: './user/Login',
13 | },
14 | ],
15 | },
16 | ],
17 | },
18 | {
19 | path: '/welcome',
20 | name: 'welcome',
21 | icon: 'smile',
22 | component: './Welcome',
23 | },
24 | {
25 | path: '/admin',
26 | name: 'admin',
27 | icon: 'crown',
28 | access: 'canAdmin',
29 | component: './Admin',
30 | routes: [
31 | {
32 | path: '/admin/sub-page',
33 | name: 'sub-page',
34 | icon: 'smile',
35 | component: './Welcome',
36 | },
37 | ],
38 | },
39 | {
40 | name: 'list.table-list',
41 | icon: 'table',
42 | path: '/list',
43 | component: './TableList',
44 | },
45 | {
46 | name: '嵌套组件',
47 | icon: 'table',
48 | path: '/project',
49 | component: './project',
50 | },
51 | {
52 | name: '可编辑表格',
53 | icon: 'table',
54 | path: '/editProTable',
55 | component: './editProTable',
56 | },
57 | {
58 | name: '表格合并',
59 | icon: 'table',
60 | path: '/spanTable',
61 | component: './spanTable',
62 | },
63 | {
64 | name: '产品列表',
65 | icon: 'BarsOutlined',
66 | path: '/product/list',
67 | component: './product/list',
68 | },
69 | {
70 | name: 'Form表单',
71 | icon: 'FileAddOutlined',
72 | path: '/product/add',
73 | component: './product/addProduct',
74 | hideInMenu: true
75 | },
76 | {
77 | path: '/',
78 | redirect: '/welcome',
79 | },
80 | {
81 | component: './404',
82 | },
83 | ];
84 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/settingDrawer.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.setting.pagestyle': 'Configuração de estilo da página',
3 | 'app.setting.pagestyle.dark': 'Dark style',
4 | 'app.setting.pagestyle.light': 'Light style',
5 | 'app.setting.content-width': 'Largura do conteúdo',
6 | 'app.setting.content-width.fixed': 'Fixo',
7 | 'app.setting.content-width.fluid': 'Fluido',
8 | 'app.setting.themecolor': 'Cor do Tema',
9 | 'app.setting.themecolor.dust': 'Dust Red',
10 | 'app.setting.themecolor.volcano': 'Volcano',
11 | 'app.setting.themecolor.sunset': 'Sunset Orange',
12 | 'app.setting.themecolor.cyan': 'Cyan',
13 | 'app.setting.themecolor.green': 'Polar Green',
14 | 'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
15 | 'app.setting.themecolor.geekblue': 'Geek Glue',
16 | 'app.setting.themecolor.purple': 'Golden Purple',
17 | 'app.setting.navigationmode': 'Modo de Navegação',
18 | 'app.setting.sidemenu': 'Layout do Menu Lateral',
19 | 'app.setting.topmenu': 'Layout do Menu Superior',
20 | 'app.setting.fixedheader': 'Cabeçalho fixo',
21 | 'app.setting.fixedsidebar': 'Barra lateral fixa',
22 | 'app.setting.fixedsidebar.hint': 'Funciona no layout do menu lateral',
23 | 'app.setting.hideheader': 'Esconder o cabeçalho quando rolar',
24 | 'app.setting.hideheader.hint': 'Funciona quando o esconder cabeçalho está abilitado',
25 | 'app.setting.othersettings': 'Outras configurações',
26 | 'app.setting.weakmode': 'Weak Mode',
27 | 'app.setting.copy': 'Copiar Configuração',
28 | 'app.setting.copyinfo':
29 | 'copiado com sucesso,por favor trocar o defaultSettings em src/models/setting.js',
30 | 'app.setting.production.hint':
31 | 'O painel de configuração apenas é exibido no ambiente de desenvolvimento, por favor modifique manualmente o',
32 | };
33 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': '欢迎',
3 | 'menu.more-blocks': '更多区块',
4 | 'menu.home': '首页',
5 | 'menu.admin': '管理页',
6 | 'menu.admin.sub-page': '二级管理页',
7 | 'menu.login': '登录',
8 | 'menu.register': '注册',
9 | 'menu.register.result': '注册结果',
10 | 'menu.dashboard': 'Dashboard',
11 | 'menu.dashboard.analysis': '分析页',
12 | 'menu.dashboard.monitor': '监控页',
13 | 'menu.dashboard.workplace': '工作台',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': '表单页',
18 | 'menu.form.basic-form': '基础表单',
19 | 'menu.form.step-form': '分步表单',
20 | 'menu.form.step-form.info': '分步表单(填写转账信息)',
21 | 'menu.form.step-form.confirm': '分步表单(确认转账信息)',
22 | 'menu.form.step-form.result': '分步表单(完成)',
23 | 'menu.form.advanced-form': '高级表单',
24 | 'menu.list': '列表页',
25 | 'menu.list.table-list': '查询表格',
26 | 'menu.list.basic-list': '标准列表',
27 | 'menu.list.card-list': '卡片列表',
28 | 'menu.list.search-list': '搜索列表',
29 | 'menu.list.search-list.articles': '搜索列表(文章)',
30 | 'menu.list.search-list.projects': '搜索列表(项目)',
31 | 'menu.list.search-list.applications': '搜索列表(应用)',
32 | 'menu.profile': '详情页',
33 | 'menu.profile.basic': '基础详情页',
34 | 'menu.profile.advanced': '高级详情页',
35 | 'menu.result': '结果页',
36 | 'menu.result.success': '成功页',
37 | 'menu.result.fail': '失败页',
38 | 'menu.exception': '异常页',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': '触发错误',
43 | 'menu.account': '个人页',
44 | 'menu.account.center': '个人中心',
45 | 'menu.account.settings': '个人设置',
46 | 'menu.account.trigger': '触发报错',
47 | 'menu.account.logout': '退出登录',
48 | 'menu.editor': '图形编辑器',
49 | 'menu.editor.flow': '流程编辑器',
50 | 'menu.editor.mind': '脑图编辑器',
51 | 'menu.editor.koni': '拓扑编辑器',
52 | };
53 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': '歡迎',
3 | 'menu.more-blocks': '更多區塊',
4 | 'menu.home': '首頁',
5 | 'menu.login': '登錄',
6 | 'menu.admin': '权限',
7 | 'menu.admin.sub-page': '二级管理页',
8 | 'menu.exception.403': '403',
9 | 'menu.exception.404': '404',
10 | 'menu.exception.500': '500',
11 | 'menu.register': '註冊',
12 | 'menu.register.result': '註冊結果',
13 | 'menu.dashboard': 'Dashboard',
14 | 'menu.dashboard.analysis': '分析頁',
15 | 'menu.dashboard.monitor': '監控頁',
16 | 'menu.dashboard.workplace': '工作臺',
17 | 'menu.form': '表單頁',
18 | 'menu.form.basic-form': '基礎表單',
19 | 'menu.form.step-form': '分步表單',
20 | 'menu.form.step-form.info': '分步表單(填寫轉賬信息)',
21 | 'menu.form.step-form.confirm': '分步表單(確認轉賬信息)',
22 | 'menu.form.step-form.result': '分步表單(完成)',
23 | 'menu.form.advanced-form': '高級表單',
24 | 'menu.list': '列表頁',
25 | 'menu.list.table-list': '查詢表格',
26 | 'menu.list.basic-list': '標淮列表',
27 | 'menu.list.card-list': '卡片列表',
28 | 'menu.list.search-list': '搜索列表',
29 | 'menu.list.search-list.articles': '搜索列表(文章)',
30 | 'menu.list.search-list.projects': '搜索列表(項目)',
31 | 'menu.list.search-list.applications': '搜索列表(應用)',
32 | 'menu.profile': '詳情頁',
33 | 'menu.profile.basic': '基礎詳情頁',
34 | 'menu.profile.advanced': '高級詳情頁',
35 | 'menu.result': '結果頁',
36 | 'menu.result.success': '成功頁',
37 | 'menu.result.fail': '失敗頁',
38 | 'menu.account': '個人頁',
39 | 'menu.account.center': '個人中心',
40 | 'menu.account.settings': '個人設置',
41 | 'menu.account.trigger': '觸發報錯',
42 | 'menu.account.logout': '退出登錄',
43 | 'menu.exception': '异常页',
44 | 'menu.exception.not-permission': '403',
45 | 'menu.exception.not-find': '404',
46 | 'menu.exception.server-error': '500',
47 | 'menu.exception.trigger': '触发错误',
48 | 'menu.editor': '圖形編輯器',
49 | 'menu.editor.flow': '流程編輯器',
50 | 'menu.editor.mind': '腦圖編輯器',
51 | 'menu.editor.koni': '拓撲編輯器',
52 | };
53 |
--------------------------------------------------------------------------------
/src/services/swagger/store.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from 'umi';
4 |
5 | /** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
6 | export async function getInventory(options?: { [key: string]: any }) {
7 | return request>('/store/inventory', {
8 | method: 'GET',
9 | ...(options || {}),
10 | });
11 | }
12 |
13 | /** Place an order for a pet POST /store/order */
14 | export async function placeOrder(body: API.Order, options?: { [key: string]: any }) {
15 | return request('/store/order', {
16 | method: 'POST',
17 | data: body,
18 | ...(options || {}),
19 | });
20 | }
21 |
22 | /** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
23 | export async function getOrderById(
24 | params: {
25 | // path
26 | /** ID of pet that needs to be fetched */
27 | orderId: number;
28 | },
29 | options?: { [key: string]: any },
30 | ) {
31 | const { orderId: param0 } = params;
32 | return request(`/store/order/${param0}`, {
33 | method: 'GET',
34 | params: { ...params },
35 | ...(options || {}),
36 | });
37 | }
38 |
39 | /** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
40 | export async function deleteOrder(
41 | params: {
42 | // path
43 | /** ID of the order that needs to be deleted */
44 | orderId: number;
45 | },
46 | options?: { [key: string]: any },
47 | ) {
48 | const { orderId: param0 } = params;
49 | return request(`/store/order/${param0}`, {
50 | method: 'DELETE',
51 | params: { ...params },
52 | ...(options || {}),
53 | });
54 | }
55 |
--------------------------------------------------------------------------------
/src/e2e/baseLayout.e2e.js:
--------------------------------------------------------------------------------
1 | const { uniq } = require('lodash');
2 | const RouterConfig = require('../../config/config').default.routes;
3 |
4 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
5 |
6 | function formatter(routes, parentPath = '') {
7 | const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
8 | let result = [];
9 | routes.forEach((item) => {
10 | if (item.path) {
11 | result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/'));
12 | }
13 | if (item.routes) {
14 | result = result.concat(
15 | formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath),
16 | );
17 | }
18 | });
19 | return uniq(result.filter((item) => !!item));
20 | }
21 |
22 | beforeEach(async () => {
23 | await page.goto(`${BASE_URL}`);
24 | await page.evaluate(() => {
25 | localStorage.setItem('antd-pro-authority', '["admin"]');
26 | });
27 | });
28 |
29 | describe('Ant Design Pro E2E test', () => {
30 | const testPage = (path) => async () => {
31 | await page.goto(`${BASE_URL}${path}`);
32 | await page.waitForSelector('footer', {
33 | timeout: 2000,
34 | });
35 | const haveFooter = await page.evaluate(
36 | () => document.getElementsByTagName('footer').length > 0,
37 | );
38 | expect(haveFooter).toBeTruthy();
39 | };
40 |
41 | const routers = formatter(RouterConfig);
42 | routers.forEach((route) => {
43 | it(`test pages ${route}`, testPage(route));
44 | });
45 |
46 | it('topmenu should have footer', async () => {
47 | const params = '?navTheme=light&layout=topmenu';
48 | await page.goto(`${BASE_URL}${params}`);
49 | await page.waitForSelector('footer', {
50 | timeout: 2000,
51 | });
52 | const haveFooter = await page.evaluate(
53 | () => document.getElementsByTagName('footer').length > 0,
54 | );
55 | expect(haveFooter).toBeTruthy();
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': 'ようこそ',
3 | 'menu.more-blocks': 'その他のブロック',
4 | 'menu.home': 'ホーム',
5 | 'menu.admin': '管理者',
6 | 'menu.admin.sub-page': 'サブページ',
7 | 'menu.login': 'ログイン',
8 | 'menu.register': '登録',
9 | 'menu.register.result': '登録結果',
10 | 'menu.dashboard': 'ダッシュボード',
11 | 'menu.dashboard.analysis': '分析',
12 | 'menu.dashboard.monitor': 'モニター',
13 | 'menu.dashboard.workplace': '職場',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': 'フォーム',
18 | 'menu.form.basic-form': '基本フォーム',
19 | 'menu.form.step-form': 'ステップフォーム',
20 | 'menu.form.step-form.info': 'ステップフォーム(転送情報の書き込み)',
21 | 'menu.form.step-form.confirm': 'ステップフォーム(転送情報の確認)',
22 | 'menu.form.step-form.result': 'ステップフォーム(完成)',
23 | 'menu.form.advanced-form': '高度なフォーム',
24 | 'menu.list': 'リスト',
25 | 'menu.list.table-list': '検索テーブル',
26 | 'menu.list.basic-list': '基本リスト',
27 | 'menu.list.card-list': 'カードリスト',
28 | 'menu.list.search-list': '検索リスト',
29 | 'menu.list.search-list.articles': '検索リスト(記事)',
30 | 'menu.list.search-list.projects': '検索リスト(プロジェクト)',
31 | 'menu.list.search-list.applications': '検索リスト(アプリ)',
32 | 'menu.profile': 'プロフィール',
33 | 'menu.profile.basic': '基本プロフィール',
34 | 'menu.profile.advanced': '高度なプロフィール',
35 | 'menu.result': '結果',
36 | 'menu.result.success': '成功',
37 | 'menu.result.fail': '失敗',
38 | 'menu.exception': '例外',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': 'トリガー',
43 | 'menu.account': 'アカウント',
44 | 'menu.account.center': 'アカウントセンター',
45 | 'menu.account.settings': 'アカウント設定',
46 | 'menu.account.trigger': 'トリガーエラー',
47 | 'menu.account.logout': 'ログアウト',
48 | 'menu.editor': 'グラフィックエディタ',
49 | 'menu.editor.flow': 'フローエディタ',
50 | 'menu.editor.mind': 'マインドエディター',
51 | 'menu.editor.koni': 'コニエディター',
52 | };
53 |
--------------------------------------------------------------------------------
/config/config.ts:
--------------------------------------------------------------------------------
1 | // https://umijs.org/config/
2 | import { defineConfig } from 'umi';
3 | import { join } from 'path';
4 |
5 | import defaultSettings from './defaultSettings';
6 | import proxy from './proxy';
7 | import routes from './routes';
8 |
9 | const { REACT_APP_ENV } = process.env;
10 |
11 | export default defineConfig({
12 | hash: true,
13 | antd: {},
14 | dva: {
15 | hmr: true,
16 | },
17 | layout: {
18 | // https://umijs.org/zh-CN/plugins/plugin-layout
19 | locale: true,
20 | siderWidth: 208,
21 | ...defaultSettings,
22 | },
23 | // https://umijs.org/zh-CN/plugins/plugin-locale
24 | locale: {
25 | // default zh-CN
26 | default: 'zh-CN',
27 | antd: true,
28 | // default true, when it is true, will use `navigator.language` overwrite default
29 | baseNavigator: true,
30 | },
31 | dynamicImport: {
32 | loading: '@ant-design/pro-layout/es/PageLoading',
33 | },
34 | targets: {
35 | ie: 11,
36 | },
37 | // umi routes: https://umijs.org/docs/routing
38 | routes,
39 | // Theme for antd: https://ant.design/docs/react/customize-theme-cn
40 | theme: {
41 | 'primary-color': defaultSettings.primaryColor,
42 | },
43 | // esbuild is father build tools
44 | // https://umijs.org/plugins/plugin-esbuild
45 | esbuild: {},
46 | title: false,
47 | ignoreMomentLocale: true,
48 | proxy: proxy[REACT_APP_ENV || 'dev'],
49 | manifest: {
50 | basePath: '/',
51 | },
52 | // Fast Refresh 热更新
53 | fastRefresh: {},
54 | openAPI: [
55 | {
56 | requestLibPath: "import { request } from 'umi'",
57 | // 或者使用在线的版本
58 | // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
59 | schemaPath: join(__dirname, 'oneapi.json'),
60 | mock: false,
61 | },
62 | {
63 | requestLibPath: "import { request } from 'umi'",
64 | schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
65 | projectName: 'swagger',
66 | },
67 | ],
68 | });
69 |
--------------------------------------------------------------------------------
/src/locales/fa-IR/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': 'خوش آمدید',
3 | 'menu.more-blocks': 'بلوک های بیشتر',
4 | 'menu.home': 'خانه',
5 | 'menu.admin': 'مدیر',
6 | 'menu.admin.sub-page': 'زیر صفحه',
7 | 'menu.login': 'ورود',
8 | 'menu.register': 'ثبت نام',
9 | 'menu.register.result': 'ثبت نام نتیجه',
10 | 'menu.dashboard': 'داشبورد',
11 | 'menu.dashboard.analysis': 'تحلیل و بررسی',
12 | 'menu.dashboard.monitor': 'نظارت',
13 | 'menu.dashboard.workplace': 'محل کار',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': 'فرم',
18 | 'menu.form.basic-form': 'فرم اساسی',
19 | 'menu.form.step-form': 'فرم مرحله',
20 | 'menu.form.step-form.info': 'فرم مرحله (نوشتن اطلاعات انتقال)',
21 | 'menu.form.step-form.confirm': 'فرم مرحله (تأیید اطلاعات انتقال)',
22 | 'menu.form.step-form.result': 'فرم مرحله (تمام شده)',
23 | 'menu.form.advanced-form': 'فرم پیشرفته',
24 | 'menu.list': 'لیست',
25 | 'menu.list.table-list': 'جدول جستجو',
26 | 'menu.list.basic-list': 'لیست اصلی',
27 | 'menu.list.card-list': 'لیست کارت',
28 | 'menu.list.search-list': 'لیست جستجو',
29 | 'menu.list.search-list.articles': 'لیست جستجو (مقالات)',
30 | 'menu.list.search-list.projects': 'لیست جستجو (پروژه ها)',
31 | 'menu.list.search-list.applications': 'لیست جستجو (برنامه ها)',
32 | 'menu.profile': 'مشخصات',
33 | 'menu.profile.basic': 'مشخصات عمومی',
34 | 'menu.profile.advanced': 'مشخصات پیشرفته',
35 | 'menu.result': 'نتیجه',
36 | 'menu.result.success': 'موفق',
37 | 'menu.result.fail': 'ناموفق',
38 | 'menu.exception': 'استثنا',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': 'راه اندازی',
43 | 'menu.account': 'حساب',
44 | 'menu.account.center': 'مرکز حساب',
45 | 'menu.account.settings': 'تنظیمات حساب',
46 | 'menu.account.trigger': 'خطای راه اندازی',
47 | 'menu.account.logout': 'خروج',
48 | 'menu.editor': 'ویرایشگر گرافیک',
49 | 'menu.editor.flow': 'ویرایشگر جریان',
50 | 'menu.editor.mind': 'ویرایشگر ذهن',
51 | 'menu.editor.koni': 'ویرایشگر Koni',
52 | };
53 |
--------------------------------------------------------------------------------
/src/pages/Welcome.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { PageContainer } from '@ant-design/pro-layout';
3 | import { Card, Alert, Typography } from 'antd';
4 | import { useIntl, FormattedMessage } from 'umi';
5 | import styles from './Welcome.less';
6 |
7 | const CodePreview: React.FC = ({ children }) => (
8 |
9 |
10 | {children}
11 |
12 |
13 | );
14 |
15 | export default (): React.ReactNode => {
16 | const intl = useIntl();
17 | return (
18 |
19 |
20 |
33 |
34 | {' '}
35 |
40 |
41 |
42 |
43 | yarn add @ant-design/pro-table
44 |
50 | {' '}
51 |
56 |
57 |
58 |
59 | yarn add @ant-design/pro-layout
60 |
61 |
62 | );
63 | };
64 |
--------------------------------------------------------------------------------
/src/locales/en-US/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': 'Welcome',
3 | 'menu.more-blocks': 'More Blocks',
4 | 'menu.home': 'Home',
5 | 'menu.admin': 'Admin',
6 | 'menu.admin.sub-page': 'Sub-Page',
7 | 'menu.login': 'Login',
8 | 'menu.register': 'Register',
9 | 'menu.register.result': 'Register Result',
10 | 'menu.dashboard': 'Dashboard',
11 | 'menu.dashboard.analysis': 'Analysis',
12 | 'menu.dashboard.monitor': 'Monitor',
13 | 'menu.dashboard.workplace': 'Workplace',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': 'Form',
18 | 'menu.form.basic-form': 'Basic Form',
19 | 'menu.form.step-form': 'Step Form',
20 | 'menu.form.step-form.info': 'Step Form(write transfer information)',
21 | 'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
22 | 'menu.form.step-form.result': 'Step Form(finished)',
23 | 'menu.form.advanced-form': 'Advanced Form',
24 | 'menu.list': 'List',
25 | 'menu.list.table-list': 'Search Table',
26 | 'menu.list.basic-list': 'Basic List',
27 | 'menu.list.card-list': 'Card List',
28 | 'menu.list.search-list': 'Search List',
29 | 'menu.list.search-list.articles': 'Search List(articles)',
30 | 'menu.list.search-list.projects': 'Search List(projects)',
31 | 'menu.list.search-list.applications': 'Search List(applications)',
32 | 'menu.profile': 'Profile',
33 | 'menu.profile.basic': 'Basic Profile',
34 | 'menu.profile.advanced': 'Advanced Profile',
35 | 'menu.result': 'Result',
36 | 'menu.result.success': 'Success',
37 | 'menu.result.fail': 'Fail',
38 | 'menu.exception': 'Exception',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': 'Trigger',
43 | 'menu.account': 'Account',
44 | 'menu.account.center': 'Account Center',
45 | 'menu.account.settings': 'Account Settings',
46 | 'menu.account.trigger': 'Trigger Error',
47 | 'menu.account.logout': 'Logout',
48 | 'menu.editor': 'Graphic Editor',
49 | 'menu.editor.flow': 'Flow Editor',
50 | 'menu.editor.mind': 'Mind Editor',
51 | 'menu.editor.koni': 'Koni Editor',
52 | };
53 |
--------------------------------------------------------------------------------
/src/locales/id-ID/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': 'Selamat Datang',
3 | 'menu.more-blocks': 'Blocks Lainnya',
4 | 'menu.home': 'Halaman Awal',
5 | 'menu.admin': 'Admin',
6 | 'menu.admin.sub-page': 'Sub-Halaman',
7 | 'menu.login': 'Masuk',
8 | 'menu.register': 'Pendaftaran',
9 | 'menu.register.result': 'Hasil Pendaftaran',
10 | 'menu.dashboard': 'Dasbor',
11 | 'menu.dashboard.analysis': 'Analisis',
12 | 'menu.dashboard.monitor': 'Monitor',
13 | 'menu.dashboard.workplace': 'Workplace',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': 'Form',
18 | 'menu.form.basic-form': 'Form Dasar',
19 | 'menu.form.step-form': 'Form Bertahap',
20 | 'menu.form.step-form.info': 'Form Bertahap(menulis informasi yang dibagikan)',
21 | 'menu.form.step-form.confirm': 'Form Bertahap(konfirmasi informasi yang dibagikan)',
22 | 'menu.form.step-form.result': 'Form Bertahap(selesai)',
23 | 'menu.form.advanced-form': 'Form Lanjutan',
24 | 'menu.list': 'Daftar',
25 | 'menu.list.table-list': 'Tabel Pencarian',
26 | 'menu.list.basic-list': 'Daftar Dasar',
27 | 'menu.list.card-list': 'Daftar Kartu',
28 | 'menu.list.search-list': 'Daftar Pencarian',
29 | 'menu.list.search-list.articles': 'Daftar Pencarian(artikel)',
30 | 'menu.list.search-list.projects': 'Daftar Pencarian(projek)',
31 | 'menu.list.search-list.applications': 'Daftar Pencarian(aplikasi)',
32 | 'menu.profile': 'Profil',
33 | 'menu.profile.basic': 'Profil Dasar',
34 | 'menu.profile.advanced': 'Profile Lanjutan',
35 | 'menu.result': 'Hasil',
36 | 'menu.result.success': 'Sukses',
37 | 'menu.result.fail': 'Gagal',
38 | 'menu.exception': 'Pengecualian',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': 'Jalankan',
43 | 'menu.account': 'Akun',
44 | 'menu.account.center': 'Detail Akun',
45 | 'menu.account.settings': 'Pengaturan Akun',
46 | 'menu.account.trigger': 'Mengaktivasi Error',
47 | 'menu.account.logout': 'Keluar',
48 | 'menu.editor': 'Penyusun Grafis',
49 | 'menu.editor.flow': 'Penyusun Alur',
50 | 'menu.editor.mind': 'Penyusun Mind',
51 | 'menu.editor.koni': 'Penyusun Koni',
52 | };
53 |
--------------------------------------------------------------------------------
/src/service-worker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable eslint-comments/disable-enable-pair */
2 | /* eslint-disable no-restricted-globals */
3 | /* eslint-disable no-underscore-dangle */
4 | /* globals workbox */
5 | workbox.core.setCacheNameDetails({
6 | prefix: 'antd-pro',
7 | suffix: 'v5',
8 | });
9 | // Control all opened tabs ASAP
10 | workbox.clientsClaim();
11 |
12 | /**
13 | * Use precaching list generated by workbox in build process.
14 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
15 | */
16 | workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
17 |
18 | /**
19 | * Register a navigation route.
20 | * https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
21 | */
22 | workbox.routing.registerNavigationRoute('/index.html');
23 |
24 | /**
25 | * Use runtime cache:
26 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
27 | *
28 | * Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
29 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
30 | */
31 |
32 | /** Handle API requests */
33 | workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
34 |
35 | /** Handle third party requests */
36 | workbox.routing.registerRoute(
37 | /^https:\/\/gw\.alipayobjects\.com\//,
38 | workbox.strategies.networkFirst(),
39 | );
40 | workbox.routing.registerRoute(
41 | /^https:\/\/cdnjs\.cloudflare\.com\//,
42 | workbox.strategies.networkFirst(),
43 | );
44 | workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
45 |
46 | /** Response to client after skipping waiting with MessageChannel */
47 | addEventListener('message', (event) => {
48 | const replyPort = event.ports[0];
49 | const message = event.data;
50 | if (replyPort && message && message.type === 'skip-waiting') {
51 | event.waitUntil(
52 | self.skipWaiting().then(
53 | () => {
54 | replyPort.postMessage({
55 | error: null,
56 | });
57 | },
58 | (error) => {
59 | replyPort.postMessage({
60 | error,
61 | });
62 | },
63 | ),
64 | );
65 | }
66 | });
67 |
--------------------------------------------------------------------------------
/tests/issue.js:
--------------------------------------------------------------------------------
1 | const Octokit = require('@octokit/core');
2 |
3 | const octokit = new Octokit.Octokit({
4 | auth: process.env.GITHUB_TOKEN || process.env.GITHUB_AUTH,
5 | });
6 |
7 | const queryIssue = ({ title, id }) => {
8 | return octokit
9 | .request('GET /search/issues', {
10 | q: title,
11 | per_page: 5,
12 | })
13 | .then(({ data }) => {
14 | const list = data.items
15 | .map((item) => {
16 | return {
17 | title: item.title,
18 | url: item.html_url,
19 | id: item.id,
20 | };
21 | })
22 | .filter((item) => {
23 | return item.id !== id;
24 | });
25 |
26 | if (list.length > 0) {
27 | return `
28 | > Issue Robot generation
29 |
30 | ### 以下的issue可能会帮助到你 :
31 |
32 | ${list
33 | .map((item) => {
34 | return `* [${item.title}](${item.url})`;
35 | })
36 | .join('\n')}`;
37 | }
38 | return null;
39 | })
40 | .then(async (markdown) => {
41 | return markdown;
42 | });
43 | };
44 |
45 | const findIssue = async (issueId) => {
46 | const { data } = await octokit.request('GET /repos/{owner}/{repo}/issues/{issue_number}', {
47 | owner: 'ant-design',
48 | repo: 'ant-design-pro',
49 | issue_number: issueId,
50 | });
51 | return data;
52 | };
53 | const closeIssue = async (issueId) => {
54 | await octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
55 | owner: 'ant-design',
56 | repo: 'ant-design-pro',
57 | issue_number: issueId,
58 | state: 'closed',
59 | });
60 | };
61 | const replyCommit = async (issueId, markdown) => {
62 | await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
63 | owner: 'ant-design',
64 | repo: 'ant-design-pro',
65 | issue_number: issueId,
66 | body: markdown,
67 | });
68 | };
69 |
70 | const reply = async () => {
71 | const issueId = process.env.ISSUE_NUMBER;
72 | const issue = await findIssue(issueId);
73 | if (!issue.title || issue.title.length < 12) {
74 | replyCommit(issueId, '**请写标题!**');
75 | closeIssue(issueId);
76 | return;
77 | }
78 | const markdown = await queryIssue({
79 | title: issue.title,
80 | id: issue.id,
81 | });
82 | replyCommit(issueId, markdown);
83 | };
84 |
85 | reply();
86 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'menu.welcome': 'Welcome',
3 | 'menu.more-blocks': 'More Blocks',
4 | 'menu.home': 'Início',
5 | 'menu.login': 'Login',
6 | 'menu.admin': 'Admin',
7 | 'menu.admin.sub-page': 'Sub-Page',
8 | 'menu.register': 'Registro',
9 | 'menu.register.result': 'Resultado de registro',
10 | 'menu.dashboard': 'Dashboard',
11 | 'menu.dashboard.analysis': 'Análise',
12 | 'menu.dashboard.monitor': 'Monitor',
13 | 'menu.dashboard.workplace': 'Ambiente de Trabalho',
14 | 'menu.exception.403': '403',
15 | 'menu.exception.404': '404',
16 | 'menu.exception.500': '500',
17 | 'menu.form': 'Formulário',
18 | 'menu.form.basic-form': 'Formulário Básico',
19 | 'menu.form.step-form': 'Formulário Assistido',
20 | 'menu.form.step-form.info': 'Formulário Assistido(gravar informações de transferência)',
21 | 'menu.form.step-form.confirm': 'Formulário Assistido(confirmar informações de transferência)',
22 | 'menu.form.step-form.result': 'Formulário Assistido(finalizado)',
23 | 'menu.form.advanced-form': 'Formulário Avançado',
24 | 'menu.list': 'Lista',
25 | 'menu.list.table-list': 'Tabela de Busca',
26 | 'menu.list.basic-list': 'Lista Básica',
27 | 'menu.list.card-list': 'Lista de Card',
28 | 'menu.list.search-list': 'Lista de Busca',
29 | 'menu.list.search-list.articles': 'Lista de Busca(artigos)',
30 | 'menu.list.search-list.projects': 'Lista de Busca(projetos)',
31 | 'menu.list.search-list.applications': 'Lista de Busca(aplicações)',
32 | 'menu.profile': 'Perfil',
33 | 'menu.profile.basic': 'Perfil Básico',
34 | 'menu.profile.advanced': 'Perfil Avançado',
35 | 'menu.result': 'Resultado',
36 | 'menu.result.success': 'Sucesso',
37 | 'menu.result.fail': 'Falha',
38 | 'menu.exception': 'Exceção',
39 | 'menu.exception.not-permission': '403',
40 | 'menu.exception.not-find': '404',
41 | 'menu.exception.server-error': '500',
42 | 'menu.exception.trigger': 'Disparar',
43 | 'menu.account': 'Conta',
44 | 'menu.account.center': 'Central da Conta',
45 | 'menu.account.settings': 'Configurar Conta',
46 | 'menu.account.trigger': 'Disparar Erro',
47 | 'menu.account.logout': 'Sair',
48 | 'menu.editor': 'Graphic Editor',
49 | 'menu.editor.flow': 'Flow Editor',
50 | 'menu.editor.mind': 'Mind Editor',
51 | 'menu.editor.koni': 'Koni Editor',
52 | };
53 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/api.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from 'umi';
4 |
5 | /** 获取当前的用户 GET /api/currentUser */
6 | export async function currentUser(options?: { [key: string]: any }) {
7 | return request('/api/currentUser', {
8 | method: 'GET',
9 | ...(options || {}),
10 | });
11 | }
12 |
13 | /** 登录接口 POST /api/login/outLogin */
14 | export async function outLogin(options?: { [key: string]: any }) {
15 | return request>('/api/login/outLogin', {
16 | method: 'POST',
17 | ...(options || {}),
18 | });
19 | }
20 |
21 | /** 登录接口 POST /api/login/account */
22 | export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
23 | return request('/api/login/account', {
24 | method: 'POST',
25 | headers: {
26 | 'Content-Type': 'application/json',
27 | },
28 | data: body,
29 | ...(options || {}),
30 | });
31 | }
32 |
33 | /** 此处后端没有提供注释 GET /api/notices */
34 | export async function getNotices(options?: { [key: string]: any }) {
35 | return request('/api/notices', {
36 | method: 'GET',
37 | ...(options || {}),
38 | });
39 | }
40 |
41 | /** 获取规则列表 GET /api/rule */
42 | export async function rule(
43 | params: {
44 | // query
45 | /** 当前的页码 */
46 | current?: number;
47 | /** 页面的容量 */
48 | pageSize?: number;
49 | },
50 | options?: { [key: string]: any },
51 | ) {
52 | return request('/api/rule', {
53 | method: 'GET',
54 | params: {
55 | ...params,
56 | },
57 | ...(options || {}),
58 | });
59 | }
60 |
61 | /** 新建规则 PUT /api/rule */
62 | export async function updateRule(options?: { [key: string]: any }) {
63 | return request('/api/rule', {
64 | method: 'PUT',
65 | ...(options || {}),
66 | });
67 | }
68 |
69 | /** 新建规则 POST /api/rule */
70 | export async function addRule(options?: { [key: string]: any }) {
71 | return request('/api/rule', {
72 | method: 'POST',
73 | ...(options || {}),
74 | });
75 | }
76 |
77 | /** 删除规则 DELETE /api/rule */
78 | export async function removeRule(options?: { [key: string]: any }) {
79 | return request>('/api/rule', {
80 | method: 'DELETE',
81 | ...(options || {}),
82 | });
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/RightContent/index.tsx:
--------------------------------------------------------------------------------
1 | import { Tag, Space } from 'antd';
2 | import { QuestionCircleOutlined } from '@ant-design/icons';
3 | import React from 'react';
4 | import { useModel, SelectLang } from 'umi';
5 | import Avatar from './AvatarDropdown';
6 | import HeaderSearch from '../HeaderSearch';
7 | import styles from './index.less';
8 |
9 | export type SiderTheme = 'light' | 'dark';
10 |
11 | const ENVTagColor = {
12 | dev: 'orange',
13 | test: 'green',
14 | pre: '#87d068',
15 | };
16 |
17 | const GlobalHeaderRight: React.FC = () => {
18 | const { initialState } = useModel('@@initialState');
19 |
20 | if (!initialState || !initialState.settings) {
21 | return null;
22 | }
23 |
24 | const { navTheme, layout } = initialState.settings;
25 | let className = styles.right;
26 |
27 | if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
28 | className = `${styles.right} ${styles.dark}`;
29 | }
30 | return (
31 |
32 | umi ui, value: 'umi ui' },
38 | {
39 | label: Ant Design,
40 | value: 'Ant Design',
41 | },
42 | {
43 | label: Pro Table,
44 | value: 'Pro Table',
45 | },
46 | {
47 | label: Pro Layout,
48 | value: 'Pro Layout',
49 | },
50 | ]}
51 | // onSearch={value => {
52 | // console.log('input', value);
53 | // }}
54 | />
55 | {
58 | window.open('https://pro.ant.design/docs/getting-started');
59 | }}
60 | >
61 |
62 |
63 |
64 | {REACT_APP_ENV && (
65 |
66 | {REACT_APP_ENV}
67 |
68 | )}
69 |
70 |
71 | );
72 | };
73 | export default GlobalHeaderRight;
74 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeList.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .list {
4 | max-height: 400px;
5 | overflow: auto;
6 | &::-webkit-scrollbar {
7 | display: none;
8 | }
9 | .item {
10 | padding-right: 24px;
11 | padding-left: 24px;
12 | overflow: hidden;
13 | cursor: pointer;
14 | transition: all 0.3s;
15 |
16 | .meta {
17 | width: 100%;
18 | }
19 |
20 | .avatar {
21 | margin-top: 4px;
22 | background: @component-background;
23 | }
24 | .iconElement {
25 | font-size: 32px;
26 | }
27 |
28 | &.read {
29 | opacity: 0.4;
30 | }
31 | &:last-child {
32 | border-bottom: 0;
33 | }
34 | &:hover {
35 | background: @primary-1;
36 | }
37 | .title {
38 | margin-bottom: 8px;
39 | font-weight: normal;
40 | }
41 | .description {
42 | font-size: 12px;
43 | line-height: @line-height-base;
44 | }
45 | .datetime {
46 | margin-top: 4px;
47 | font-size: 12px;
48 | line-height: @line-height-base;
49 | }
50 | .extra {
51 | float: right;
52 | margin-top: -1.5px;
53 | margin-right: 0;
54 | color: @text-color-secondary;
55 | font-weight: normal;
56 | }
57 | }
58 | .loadMore {
59 | padding: 8px 0;
60 | color: @primary-6;
61 | text-align: center;
62 | cursor: pointer;
63 | &.loadedAll {
64 | color: rgba(0, 0, 0, 0.25);
65 | cursor: unset;
66 | }
67 | }
68 | }
69 |
70 | .notFound {
71 | padding: 73px 0 88px;
72 | color: @text-color-secondary;
73 | text-align: center;
74 | img {
75 | display: inline-block;
76 | height: 76px;
77 | margin-bottom: 16px;
78 | }
79 | }
80 |
81 | .bottomBar {
82 | height: 46px;
83 | color: @text-color;
84 | line-height: 46px;
85 | text-align: center;
86 | border-top: 1px solid @border-color-split;
87 | border-radius: 0 0 @border-radius-base @border-radius-base;
88 | transition: all 0.3s;
89 | div {
90 | display: inline-block;
91 | width: 50%;
92 | cursor: pointer;
93 | transition: all 0.3s;
94 | user-select: none;
95 |
96 | &:only-child {
97 | width: 100%;
98 | }
99 | &:not(:only-child):last-child {
100 | border-left: 1px solid @border-color-split;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/pages/user/Login/index.less:
--------------------------------------------------------------------------------
1 | @import '~antd/es/style/themes/default.less';
2 |
3 | .container {
4 | display: flex;
5 | flex-direction: column;
6 | height: 100vh;
7 | overflow: auto;
8 | background: @layout-body-background;
9 | }
10 |
11 | .lang {
12 | width: 100%;
13 | height: 40px;
14 | line-height: 44px;
15 | text-align: right;
16 | :global(.ant-dropdown-trigger) {
17 | margin-right: 24px;
18 | }
19 | }
20 |
21 | .content {
22 | flex: 1;
23 | padding: 32px 0;
24 | }
25 |
26 | @media (min-width: @screen-md-min) {
27 | .container {
28 | background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
29 | background-repeat: no-repeat;
30 | background-position: center 110px;
31 | background-size: 100%;
32 | }
33 |
34 | .content {
35 | padding: 32px 0 24px;
36 | }
37 | }
38 |
39 | .top {
40 | text-align: center;
41 | }
42 |
43 | .header {
44 | height: 44px;
45 | line-height: 44px;
46 | a {
47 | text-decoration: none;
48 | }
49 | }
50 |
51 | .logo {
52 | height: 44px;
53 | margin-right: 16px;
54 | vertical-align: top;
55 | }
56 |
57 | .title {
58 | position: relative;
59 | top: 2px;
60 | color: @heading-color;
61 | font-weight: 600;
62 | font-size: 33px;
63 | font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
64 | }
65 |
66 | .desc {
67 | margin-top: 12px;
68 | margin-bottom: 40px;
69 | color: @text-color-secondary;
70 | font-size: @font-size-base;
71 | }
72 |
73 | .main {
74 | width: 328px;
75 | margin: 0 auto;
76 | @media screen and (max-width: @screen-sm) {
77 | width: 95%;
78 | max-width: 328px;
79 | }
80 |
81 | :global {
82 | .@{ant-prefix}-tabs-nav-list {
83 | margin: auto;
84 | font-size: 16px;
85 | }
86 | }
87 |
88 | .icon {
89 | margin-left: 16px;
90 | color: rgba(0, 0, 0, 0.2);
91 | font-size: 24px;
92 | vertical-align: middle;
93 | cursor: pointer;
94 | transition: color 0.3s;
95 |
96 | &:hover {
97 | color: @primary-color;
98 | }
99 | }
100 |
101 | .other {
102 | margin-top: 24px;
103 | line-height: 22px;
104 | text-align: left;
105 | .register {
106 | float: right;
107 | }
108 | }
109 |
110 | .prefixIcon {
111 | color: @primary-color;
112 | font-size: @font-size-base;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/typings.d.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 |
4 | declare namespace API {
5 | type CurrentUser = {
6 | name?: string;
7 | avatar?: string;
8 | userid?: string;
9 | email?: string;
10 | signature?: string;
11 | title?: string;
12 | group?: string;
13 | tags?: { key?: string; label?: string }[];
14 | notifyCount?: number;
15 | unreadCount?: number;
16 | country?: string;
17 | access?: string;
18 | geographic?: {
19 | province?: { label?: string; key?: string };
20 | city?: { label?: string; key?: string };
21 | };
22 | address?: string;
23 | phone?: string;
24 | };
25 |
26 | type LoginResult = {
27 | status?: string;
28 | type?: string;
29 | currentAuthority?: string;
30 | };
31 |
32 | type PageParams = {
33 | current?: number;
34 | pageSize?: number;
35 | };
36 |
37 | type RuleListItem = {
38 | key?: number;
39 | disabled?: boolean;
40 | href?: string;
41 | avatar?: string;
42 | name?: string;
43 | owner?: string;
44 | desc?: string;
45 | callNo?: number;
46 | status?: number;
47 | updatedAt?: string;
48 | createdAt?: string;
49 | progress?: number;
50 | };
51 |
52 | type RuleList = {
53 | data?: RuleListItem[];
54 | /** 列表的内容总数 */
55 | total?: number;
56 | success?: boolean;
57 | };
58 |
59 | type FakeCaptcha = {
60 | code?: number;
61 | status?: string;
62 | };
63 |
64 | type LoginParams = {
65 | username?: string;
66 | password?: string;
67 | autoLogin?: boolean;
68 | type?: string;
69 | };
70 |
71 | type ErrorResponse = {
72 | /** 业务约定的错误码 */
73 | errorCode: string;
74 | /** 业务上的错误信息 */
75 | errorMessage?: string;
76 | /** 业务上的请求是否成功 */
77 | success?: boolean;
78 | };
79 |
80 | type NoticeIconList = {
81 | data?: NoticeIconItem[];
82 | /** 列表的内容总数 */
83 | total?: number;
84 | success?: boolean;
85 | };
86 |
87 | type NoticeIconItemType = 'notification' | 'message' | 'event';
88 |
89 | type NoticeIconItem = {
90 | id?: string;
91 | extra?: string;
92 | key?: string;
93 | read?: boolean;
94 | avatar?: string;
95 | title?: string;
96 | status?: string;
97 | datetime?: string;
98 | description?: string;
99 | type?: NoticeIconItemType;
100 | };
101 | }
102 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': '基本设置',
3 | 'app.settings.menuMap.security': '安全设置',
4 | 'app.settings.menuMap.binding': '账号绑定',
5 | 'app.settings.menuMap.notification': '新消息通知',
6 | 'app.settings.basic.avatar': '头像',
7 | 'app.settings.basic.change-avatar': '更换头像',
8 | 'app.settings.basic.email': '邮箱',
9 | 'app.settings.basic.email-message': '请输入您的邮箱!',
10 | 'app.settings.basic.nickname': '昵称',
11 | 'app.settings.basic.nickname-message': '请输入您的昵称!',
12 | 'app.settings.basic.profile': '个人简介',
13 | 'app.settings.basic.profile-message': '请输入个人简介!',
14 | 'app.settings.basic.profile-placeholder': '个人简介',
15 | 'app.settings.basic.country': '国家/地区',
16 | 'app.settings.basic.country-message': '请输入您的国家或地区!',
17 | 'app.settings.basic.geographic': '所在省市',
18 | 'app.settings.basic.geographic-message': '请输入您的所在省市!',
19 | 'app.settings.basic.address': '街道地址',
20 | 'app.settings.basic.address-message': '请输入您的街道地址!',
21 | 'app.settings.basic.phone': '联系电话',
22 | 'app.settings.basic.phone-message': '请输入您的联系电话!',
23 | 'app.settings.basic.update': '更新基本信息',
24 | 'app.settings.security.strong': '强',
25 | 'app.settings.security.medium': '中',
26 | 'app.settings.security.weak': '弱',
27 | 'app.settings.security.password': '账户密码',
28 | 'app.settings.security.password-description': '当前密码强度',
29 | 'app.settings.security.phone': '密保手机',
30 | 'app.settings.security.phone-description': '已绑定手机',
31 | 'app.settings.security.question': '密保问题',
32 | 'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',
33 | 'app.settings.security.email': '备用邮箱',
34 | 'app.settings.security.email-description': '已绑定邮箱',
35 | 'app.settings.security.mfa': 'MFA 设备',
36 | 'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
37 | 'app.settings.security.modify': '修改',
38 | 'app.settings.security.set': '设置',
39 | 'app.settings.security.bind': '绑定',
40 | 'app.settings.binding.taobao': '绑定淘宝',
41 | 'app.settings.binding.taobao-description': '当前未绑定淘宝账号',
42 | 'app.settings.binding.alipay': '绑定支付宝',
43 | 'app.settings.binding.alipay-description': '当前未绑定支付宝账号',
44 | 'app.settings.binding.dingding': '绑定钉钉',
45 | 'app.settings.binding.dingding-description': '当前未绑定钉钉账号',
46 | 'app.settings.binding.bind': '绑定',
47 | 'app.settings.notification.password': '账户密码',
48 | 'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',
49 | 'app.settings.notification.messages': '系统消息',
50 | 'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',
51 | 'app.settings.notification.todo': '待办任务',
52 | 'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',
53 | 'app.settings.open': '开',
54 | 'app.settings.close': '关',
55 | };
56 |
--------------------------------------------------------------------------------
/src/locales/zh-TW/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': '基本設置',
3 | 'app.settings.menuMap.security': '安全設置',
4 | 'app.settings.menuMap.binding': '賬號綁定',
5 | 'app.settings.menuMap.notification': '新消息通知',
6 | 'app.settings.basic.avatar': '頭像',
7 | 'app.settings.basic.change-avatar': '更換頭像',
8 | 'app.settings.basic.email': '郵箱',
9 | 'app.settings.basic.email-message': '請輸入您的郵箱!',
10 | 'app.settings.basic.nickname': '昵稱',
11 | 'app.settings.basic.nickname-message': '請輸入您的昵稱!',
12 | 'app.settings.basic.profile': '個人簡介',
13 | 'app.settings.basic.profile-message': '請輸入個人簡介!',
14 | 'app.settings.basic.profile-placeholder': '個人簡介',
15 | 'app.settings.basic.country': '國家/地區',
16 | 'app.settings.basic.country-message': '請輸入您的國家或地區!',
17 | 'app.settings.basic.geographic': '所在省市',
18 | 'app.settings.basic.geographic-message': '請輸入您的所在省市!',
19 | 'app.settings.basic.address': '街道地址',
20 | 'app.settings.basic.address-message': '請輸入您的街道地址!',
21 | 'app.settings.basic.phone': '聯系電話',
22 | 'app.settings.basic.phone-message': '請輸入您的聯系電話!',
23 | 'app.settings.basic.update': '更新基本信息',
24 | 'app.settings.security.strong': '強',
25 | 'app.settings.security.medium': '中',
26 | 'app.settings.security.weak': '弱',
27 | 'app.settings.security.password': '賬戶密碼',
28 | 'app.settings.security.password-description': '當前密碼強度',
29 | 'app.settings.security.phone': '密保手機',
30 | 'app.settings.security.phone-description': '已綁定手機',
31 | 'app.settings.security.question': '密保問題',
32 | 'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全',
33 | 'app.settings.security.email': '備用郵箱',
34 | 'app.settings.security.email-description': '已綁定郵箱',
35 | 'app.settings.security.mfa': 'MFA 設備',
36 | 'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認',
37 | 'app.settings.security.modify': '修改',
38 | 'app.settings.security.set': '設置',
39 | 'app.settings.security.bind': '綁定',
40 | 'app.settings.binding.taobao': '綁定淘寶',
41 | 'app.settings.binding.taobao-description': '當前未綁定淘寶賬號',
42 | 'app.settings.binding.alipay': '綁定支付寶',
43 | 'app.settings.binding.alipay-description': '當前未綁定支付寶賬號',
44 | 'app.settings.binding.dingding': '綁定釘釘',
45 | 'app.settings.binding.dingding-description': '當前未綁定釘釘賬號',
46 | 'app.settings.binding.bind': '綁定',
47 | 'app.settings.notification.password': '賬戶密碼',
48 | 'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知',
49 | 'app.settings.notification.messages': '系統消息',
50 | 'app.settings.notification.messages-description': '系統消息將以站內信的形式通知',
51 | 'app.settings.notification.todo': '待辦任務',
52 | 'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知',
53 | 'app.settings.open': '開',
54 | 'app.settings.close': '關',
55 | };
56 |
--------------------------------------------------------------------------------
/src/global.tsx:
--------------------------------------------------------------------------------
1 | import { Button, message, notification } from 'antd';
2 | import { useIntl } from 'umi';
3 | import defaultSettings from '../config/defaultSettings';
4 |
5 | const { pwa } = defaultSettings;
6 | const isHttps = document.location.protocol === 'https:';
7 |
8 | // if pwa is true
9 | if (pwa) {
10 | // Notify user if offline now
11 | window.addEventListener('sw.offline', () => {
12 | message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' }));
13 | });
14 |
15 | // Pop up a prompt on the page asking the user if they want to use the latest version
16 | window.addEventListener('sw.updated', (event: Event) => {
17 | const e = event as CustomEvent;
18 | const reloadSW = async () => {
19 | // Check if there is sw whose state is waiting in ServiceWorkerRegistration
20 | // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
21 | const worker = e.detail && e.detail.waiting;
22 | if (!worker) {
23 | return true;
24 | }
25 | // Send skip-waiting event to waiting SW with MessageChannel
26 | await new Promise((resolve, reject) => {
27 | const channel = new MessageChannel();
28 | channel.port1.onmessage = (msgEvent) => {
29 | if (msgEvent.data.error) {
30 | reject(msgEvent.data.error);
31 | } else {
32 | resolve(msgEvent.data);
33 | }
34 | };
35 | worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
36 | });
37 | // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
38 | window.location.reload(true);
39 | return true;
40 | };
41 | const key = `open${Date.now()}`;
42 | const btn = (
43 |
52 | );
53 | notification.open({
54 | message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }),
55 | description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }),
56 | btn,
57 | key,
58 | onClose: async () => null,
59 | });
60 | });
61 | } else if ('serviceWorker' in navigator && isHttps) {
62 | // unregister service worker
63 | const { serviceWorker } = navigator;
64 | if (serviceWorker.getRegistrations) {
65 | serviceWorker.getRegistrations().then((sws) => {
66 | sws.forEach((sw) => {
67 | sw.unregister();
68 | });
69 | });
70 | }
71 | serviceWorker.getRegistration().then((sw) => {
72 | if (sw) sw.unregister();
73 | });
74 |
75 | // remove all caches
76 | if (window.caches && window.caches.keys()) {
77 | caches.keys().then((keys) => {
78 | keys.forEach((key) => {
79 | caches.delete(key);
80 | });
81 | });
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': '基本設定',
3 | 'app.settings.menuMap.security': 'セキュリティ設定',
4 | 'app.settings.menuMap.binding': 'アカウントのバインド',
5 | 'app.settings.menuMap.notification': '新しいメッセージの通知',
6 | 'app.settings.basic.avatar': 'アバター',
7 | 'app.settings.basic.change-avatar': 'アバターを変更する',
8 | 'app.settings.basic.email': 'メール',
9 | 'app.settings.basic.email-message': 'メールアドレスを入力してください!',
10 | 'app.settings.basic.nickname': 'ニックネーム',
11 | 'app.settings.basic.nickname-message': 'ニックネームを入力してください!',
12 | 'app.settings.basic.profile': '個人プロフィール',
13 | 'app.settings.basic.profile-message': '個人プロフィールを入力してください!',
14 | 'app.settings.basic.profile-placeholder': '自己紹介',
15 | 'app.settings.basic.country': '国/地域',
16 | 'app.settings.basic.country-message': 'あなたの国を入力してください!',
17 | 'app.settings.basic.geographic': '州または市',
18 | 'app.settings.basic.geographic-message': '地理情報を入力してください!',
19 | 'app.settings.basic.address': '住所',
20 | 'app.settings.basic.address-message': '住所を入力してください!',
21 | 'app.settings.basic.phone': '電話番号',
22 | 'app.settings.basic.phone-message': '電話番号を入力してください!',
23 | 'app.settings.basic.update': '更新情報',
24 | 'app.settings.security.strong': '強い',
25 | 'app.settings.security.medium': 'ミディアム',
26 | 'app.settings.security.weak': '弱い',
27 | 'app.settings.security.password': 'アカウントパスワード',
28 | 'app.settings.security.password-description': '現在のパスワードの強度',
29 | 'app.settings.security.phone': 'セキュリティ電話番号',
30 | 'app.settings.security.phone-description': 'バインドされた電話番号',
31 | 'app.settings.security.question': '秘密の質問',
32 | 'app.settings.security.question-description':
33 | 'セキュリティの質問が設定されてません。セキュリティポリシーはアカウントのセキュリティを効果的に保護できます',
34 | 'app.settings.security.email': 'バックアップメール',
35 | 'app.settings.security.email-description': 'バインドされたメール',
36 | 'app.settings.security.mfa': '多要素認証デバイス',
37 | 'app.settings.security.mfa-description':
38 | 'バインドされていない多要素認証デバイスは、バインド後、2回確認できます',
39 | 'app.settings.security.modify': '変更する',
40 | 'app.settings.security.set': 'セットする',
41 | 'app.settings.security.bind': 'バインド',
42 | 'app.settings.binding.taobao': 'タオバオをバインドする',
43 | 'app.settings.binding.taobao-description': '現在バインドされていないタオバオアカウント',
44 | 'app.settings.binding.alipay': 'アリペイをバインドする',
45 | 'app.settings.binding.alipay-description': '現在バインドされていないアリペイアカウント',
46 | 'app.settings.binding.dingding': 'ディントークをバインドする',
47 | 'app.settings.binding.dingding-description': '現在バインドされていないディントークアカウント',
48 | 'app.settings.binding.bind': 'バインド',
49 | 'app.settings.notification.password': 'アカウントパスワード',
50 | 'app.settings.notification.password-description':
51 | '他のユーザーからのメッセージは、ステーションレターの形式で通知されます',
52 | 'app.settings.notification.messages': 'システムメッセージ',
53 | 'app.settings.notification.messages-description':
54 | 'システムメッセージは、ステーションレターの形式で通知されます',
55 | 'app.settings.notification.todo': 'To Do(用事) 通知',
56 | 'app.settings.notification.todo-description': 'To Doタスクは、内部レターの形式で通知されます',
57 | 'app.settings.open': '開く',
58 | 'app.settings.close': '閉じる',
59 | };
60 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.tsx:
--------------------------------------------------------------------------------
1 | import { SearchOutlined } from '@ant-design/icons';
2 | import { AutoComplete, Input } from 'antd';
3 | import useMergedState from 'rc-util/es/hooks/useMergedState';
4 | import type { AutoCompleteProps } from 'antd/es/auto-complete';
5 | import React, { useRef } from 'react';
6 |
7 | import classNames from 'classnames';
8 | import styles from './index.less';
9 |
10 | export type HeaderSearchProps = {
11 | onSearch?: (value?: string) => void;
12 | onChange?: (value?: string) => void;
13 | onVisibleChange?: (b: boolean) => void;
14 | className?: string;
15 | placeholder?: string;
16 | options: AutoCompleteProps['options'];
17 | defaultVisible?: boolean;
18 | visible?: boolean;
19 | defaultValue?: string;
20 | value?: string;
21 | };
22 |
23 | const HeaderSearch: React.FC = (props) => {
24 | const {
25 | className,
26 | defaultValue,
27 | onVisibleChange,
28 | placeholder,
29 | visible,
30 | defaultVisible,
31 | ...restProps
32 | } = props;
33 |
34 | const inputRef = useRef(null);
35 |
36 | const [value, setValue] = useMergedState(defaultValue, {
37 | value: props.value,
38 | onChange: props.onChange,
39 | });
40 |
41 | const [searchMode, setSearchMode] = useMergedState(defaultVisible ?? false, {
42 | value: props.visible,
43 | onChange: onVisibleChange,
44 | });
45 |
46 | const inputClass = classNames(styles.input, {
47 | [styles.show]: searchMode,
48 | });
49 | return (
50 | {
53 | setSearchMode(true);
54 | if (searchMode && inputRef.current) {
55 | inputRef.current.focus();
56 | }
57 | }}
58 | onTransitionEnd={({ propertyName }) => {
59 | if (propertyName === 'width' && !searchMode) {
60 | if (onVisibleChange) {
61 | onVisibleChange(searchMode);
62 | }
63 | }
64 | }}
65 | >
66 |
72 |
79 | {
86 | if (e.key === 'Enter') {
87 | if (restProps.onSearch) {
88 | restProps.onSearch(value);
89 | }
90 | }
91 | }}
92 | onBlur={() => {
93 | setSearchMode(false);
94 | }}
95 | />
96 |
97 |
98 | );
99 | };
100 |
101 | export default HeaderSearch;
102 |
--------------------------------------------------------------------------------
/src/components/RightContent/AvatarDropdown.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
3 | import { Avatar, Menu, Spin } from 'antd';
4 | import { history, useModel } from 'umi';
5 | import { stringify } from 'querystring';
6 | import HeaderDropdown from '../HeaderDropdown';
7 | import styles from './index.less';
8 | import { outLogin } from '@/services/ant-design-pro/api';
9 |
10 | export type GlobalHeaderRightProps = {
11 | menu?: boolean;
12 | };
13 |
14 | /**
15 | * 退出登录,并且将当前的 url 保存
16 | */
17 | const loginOut = async () => {
18 | await outLogin();
19 | const { query = {}, pathname } = history.location;
20 | const { redirect } = query;
21 | // Note: There may be security issues, please note
22 | if (window.location.pathname !== '/user/login' && !redirect) {
23 | history.replace({
24 | pathname: '/user/login',
25 | search: stringify({
26 | redirect: pathname,
27 | }),
28 | });
29 | }
30 | };
31 |
32 | const AvatarDropdown: React.FC = ({ menu }) => {
33 | const { initialState, setInitialState } = useModel('@@initialState');
34 |
35 | const onMenuClick = useCallback(
36 | (event: {
37 | key: React.Key;
38 | keyPath: React.Key[];
39 | item: React.ReactInstance;
40 | domEvent: React.MouseEvent;
41 | }) => {
42 | const { key } = event;
43 | if (key === 'logout' && initialState) {
44 | setInitialState({ ...initialState, currentUser: undefined });
45 | loginOut();
46 | return;
47 | }
48 | history.push(`/account/${key}`);
49 | },
50 | [initialState, setInitialState],
51 | );
52 |
53 | const loading = (
54 |
55 |
62 |
63 | );
64 |
65 | if (!initialState) {
66 | return loading;
67 | }
68 |
69 | const { currentUser } = initialState;
70 |
71 | if (!currentUser || !currentUser.name) {
72 | return loading;
73 | }
74 |
75 | const menuHeaderDropdown = (
76 |
96 | );
97 | return (
98 |
99 |
100 |
101 | {currentUser.name}
102 |
103 |
104 | );
105 | };
106 |
107 | export default AvatarDropdown;
108 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
3 | 'pages.login.accountLogin.tab': '账户密码登录',
4 | 'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)',
5 | 'pages.login.username.placeholder': '用户名: admin or user',
6 | 'pages.login.username.required': '用户名是必填项!',
7 | 'pages.login.password.placeholder': '密码: ant.design',
8 | 'pages.login.password.required': '密码是必填项!',
9 | 'pages.login.phoneLogin.tab': '手机号登录',
10 | 'pages.login.phoneLogin.errorMessage': '验证码错误',
11 | 'pages.login.phoneNumber.placeholder': '请输入手机号!',
12 | 'pages.login.phoneNumber.required': '手机号是必填项!',
13 | 'pages.login.phoneNumber.invalid': '不合法的手机号!',
14 | 'pages.login.captcha.placeholder': '请输入验证码!',
15 | 'pages.login.captcha.required': '验证码是必填项!',
16 | 'pages.login.phoneLogin.getVerificationCode': '获取验证码',
17 | 'pages.getCaptchaSecondText': '秒后重新获取',
18 | 'pages.login.rememberMe': '自动登录',
19 | 'pages.login.forgotPassword': '忘记密码 ?',
20 | 'pages.login.submit': '登录',
21 | 'pages.login.loginWith': '其他登录方式 :',
22 | 'pages.login.registerAccount': '注册账户',
23 | 'pages.welcome.advancedComponent': '高级表格',
24 | 'pages.welcome.link': '欢迎使用',
25 | 'pages.welcome.advancedLayout': '高级布局',
26 | 'pages.welcome.alertMessage': '更快更强的重型组件,已经发布。',
27 | 'pages.admin.subPage.title': ' 这个页面只有 admin 权限才能查看',
28 | 'pages.admin.subPage.alertMessage': 'umi ui 现已发布,欢迎使用 npm run ui 启动体验。',
29 | 'pages.searchTable.createForm.newRule': '新建规则',
30 | 'pages.searchTable.updateForm.ruleConfig': '规则配置',
31 | 'pages.searchTable.updateForm.basicConfig': '基本信息',
32 | 'pages.searchTable.updateForm.ruleName.nameLabel': '规则名称',
33 | 'pages.searchTable.updateForm.ruleName.nameRules': '请输入规则名称!',
34 | 'pages.searchTable.updateForm.ruleDesc.descLabel': '规则描述',
35 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder': '请输入至少五个字符',
36 | 'pages.searchTable.updateForm.ruleDesc.descRules': '请输入至少五个字符的规则描述!',
37 | 'pages.searchTable.updateForm.ruleProps.title': '配置规则属性',
38 | 'pages.searchTable.updateForm.object': '监控对象',
39 | 'pages.searchTable.updateForm.ruleProps.templateLabel': '规则模板',
40 | 'pages.searchTable.updateForm.ruleProps.typeLabel': '规则类型',
41 | 'pages.searchTable.updateForm.schedulingPeriod.title': '设定调度周期',
42 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': '开始时间',
43 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules': '请选择开始时间!',
44 | 'pages.searchTable.titleDesc': '描述',
45 | 'pages.searchTable.ruleName': '规则名称为必填项',
46 | 'pages.searchTable.titleCallNo': '服务调用次数',
47 | 'pages.searchTable.titleStatus': '状态',
48 | 'pages.searchTable.nameStatus.default': '关闭',
49 | 'pages.searchTable.nameStatus.running': '运行中',
50 | 'pages.searchTable.nameStatus.online': '已上线',
51 | 'pages.searchTable.nameStatus.abnormal': '异常',
52 | 'pages.searchTable.titleUpdatedAt': '上次调度时间',
53 | 'pages.searchTable.exception': '请输入异常原因!',
54 | 'pages.searchTable.titleOption': '操作',
55 | 'pages.searchTable.config': '配置',
56 | 'pages.searchTable.subscribeAlert': '订阅警报',
57 | 'pages.searchTable.title': '查询表格',
58 | 'pages.searchTable.new': '新建',
59 | 'pages.searchTable.chosen': '已选择',
60 | 'pages.searchTable.item': '项',
61 | 'pages.searchTable.totalServiceCalls': '服务调用次数总计',
62 | 'pages.searchTable.tenThousand': '万',
63 | 'pages.searchTable.batchDeletion': '批量删除',
64 | 'pages.searchTable.batchApproval': '批量审批',
65 | };
66 |
--------------------------------------------------------------------------------
/src/services/swagger/user.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from 'umi';
4 |
5 | /** Create user This can only be done by the logged in user. POST /user */
6 | export async function createUser(body: API.User, options?: { [key: string]: any }) {
7 | return request('/user', {
8 | method: 'POST',
9 | data: body,
10 | ...(options || {}),
11 | });
12 | }
13 |
14 | /** Creates list of users with given input array POST /user/createWithArray */
15 | export async function createUsersWithArrayInput(
16 | body: API.User[],
17 | options?: { [key: string]: any },
18 | ) {
19 | return request('/user/createWithArray', {
20 | method: 'POST',
21 | data: body,
22 | ...(options || {}),
23 | });
24 | }
25 |
26 | /** Creates list of users with given input array POST /user/createWithList */
27 | export async function createUsersWithListInput(body: API.User[], options?: { [key: string]: any }) {
28 | return request('/user/createWithList', {
29 | method: 'POST',
30 | data: body,
31 | ...(options || {}),
32 | });
33 | }
34 |
35 | /** Logs user into the system GET /user/login */
36 | export async function loginUser(
37 | params: {
38 | // query
39 | /** The user name for login */
40 | username: string;
41 | /** The password for login in clear text */
42 | password: string;
43 | },
44 | options?: { [key: string]: any },
45 | ) {
46 | return request('/user/login', {
47 | method: 'GET',
48 | params: {
49 | ...params,
50 | },
51 | ...(options || {}),
52 | });
53 | }
54 |
55 | /** Logs out current logged in user session GET /user/logout */
56 | export async function logoutUser(options?: { [key: string]: any }) {
57 | return request('/user/logout', {
58 | method: 'GET',
59 | ...(options || {}),
60 | });
61 | }
62 |
63 | /** Get user by user name GET /user/${param0} */
64 | export async function getUserByName(
65 | params: {
66 | // path
67 | /** The name that needs to be fetched. Use user1 for testing. */
68 | username: string;
69 | },
70 | options?: { [key: string]: any },
71 | ) {
72 | const { username: param0 } = params;
73 | return request(`/user/${param0}`, {
74 | method: 'GET',
75 | params: { ...params },
76 | ...(options || {}),
77 | });
78 | }
79 |
80 | /** Updated user This can only be done by the logged in user. PUT /user/${param0} */
81 | export async function updateUser(
82 | params: {
83 | // path
84 | /** name that need to be updated */
85 | username: string;
86 | },
87 | body: API.User,
88 | options?: { [key: string]: any },
89 | ) {
90 | const { username: param0 } = params;
91 | return request(`/user/${param0}`, {
92 | method: 'PUT',
93 | params: { ...params },
94 | data: body,
95 | ...(options || {}),
96 | });
97 | }
98 |
99 | /** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
100 | export async function deleteUser(
101 | params: {
102 | // path
103 | /** The name that needs to be deleted */
104 | username: string;
105 | },
106 | options?: { [key: string]: any },
107 | ) {
108 | const { username: param0 } = params;
109 | return request(`/user/${param0}`, {
110 | method: 'DELETE',
111 | params: { ...params },
112 | ...(options || {}),
113 | });
114 | }
115 |
--------------------------------------------------------------------------------
/src/locales/fa-IR/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': 'تنظیمات پایه ',
3 | 'app.settings.menuMap.security': 'تنظیمات امنیتی',
4 | 'app.settings.menuMap.binding': 'صحافی حساب',
5 | 'app.settings.menuMap.notification': 'اعلان پیام جدید',
6 | 'app.settings.basic.avatar': 'آواتار',
7 | 'app.settings.basic.change-avatar': 'آواتار را تغییر دهید',
8 | 'app.settings.basic.email': 'ایمیل',
9 | 'app.settings.basic.email-message': 'لطفا ایمیل خود را وارد کنید!',
10 | 'app.settings.basic.nickname': 'نام مستعار',
11 | 'app.settings.basic.nickname-message': 'لطفاً نام مستعار خود را وارد کنید!',
12 | 'app.settings.basic.profile': 'پروفایل شخصی',
13 | 'app.settings.basic.profile-message': 'لطفاً مشخصات شخصی خود را وارد کنید!',
14 | 'app.settings.basic.profile-placeholder': 'معرفی مختصر خودتان',
15 | 'app.settings.basic.country': 'کشور / منطقه',
16 | 'app.settings.basic.country-message': 'لطفاً کشور خود را وارد کنید!',
17 | 'app.settings.basic.geographic': 'استان یا شهر',
18 | 'app.settings.basic.geographic-message': 'لطفاً اطلاعات جغرافیایی خود را وارد کنید!',
19 | 'app.settings.basic.address': 'آدرس خیابان',
20 | 'app.settings.basic.address-message': 'لطفا آدرس خود را وارد کنید!',
21 | 'app.settings.basic.phone': 'شماره تلفن',
22 | 'app.settings.basic.phone-message': 'لطفاً تلفن خود را وارد کنید!',
23 | 'app.settings.basic.update': 'به روز رسانی اطلاعات',
24 | 'app.settings.security.strong': 'قوی',
25 | 'app.settings.security.medium': 'متوسط',
26 | 'app.settings.security.weak': 'ضعیف',
27 | 'app.settings.security.password': 'رمز عبور حساب کاربری',
28 | 'app.settings.security.password-description': 'قدرت رمز عبور فعلی',
29 | 'app.settings.security.phone': 'تلفن امنیتی',
30 | 'app.settings.security.phone-description': 'تلفن مقید',
31 | 'app.settings.security.question': 'سوال امنیتی',
32 | 'app.settings.security.question-description':
33 | 'سوال امنیتی تنظیم نشده است و سیاست امنیتی می تواند به طور موثر از امنیت حساب محافظت کند',
34 | 'app.settings.security.email': 'ایمیل پشتیبان',
35 | 'app.settings.security.email-description': 'ایمیل مقید',
36 | 'app.settings.security.mfa': 'دستگاه MFA',
37 | 'app.settings.security.mfa-description':
38 | 'دستگاه MFA بسته نشده ، پس از اتصال ، می تواند دو بار تأیید شود',
39 | 'app.settings.security.modify': 'تغییر',
40 | 'app.settings.security.set': 'تنظیم',
41 | 'app.settings.security.bind': 'بستن',
42 | 'app.settings.binding.taobao': 'اتصال Taobao',
43 | 'app.settings.binding.taobao-description': 'حساب Taobao در حال حاضر بسته نشده است',
44 | 'app.settings.binding.alipay': 'اتصال Alipay',
45 | 'app.settings.binding.alipay-description': 'حساب Alipay در حال حاضر بسته نشده است',
46 | 'app.settings.binding.dingding': 'اتصال DingTalk',
47 | 'app.settings.binding.dingding-description': 'حساب DingTalk در حال حاضر محدود نشده است',
48 | 'app.settings.binding.bind': 'بستن',
49 | 'app.settings.notification.password': 'رمز عبور حساب کاربری',
50 | 'app.settings.notification.password-description':
51 | 'پیام های سایر کاربران در قالب یک نامه ایستگاهی اعلام خواهد شد',
52 | 'app.settings.notification.messages': 'پیام های سیستم',
53 | 'app.settings.notification.messages-description':
54 | 'پیام های سیستم به صورت نامه ایستگاه مطلع می شوند',
55 | 'app.settings.notification.todo': 'اعلان کارها',
56 | 'app.settings.notification.todo-description':
57 | 'لیست کارها به صورت نامه ای از ایستگاه اطلاع داده می شود',
58 | 'app.settings.open': 'باز کن',
59 | 'app.settings.close': 'بستن',
60 | };
61 |
--------------------------------------------------------------------------------
/mock/notices.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | const getNotices = (req: Request, res: Response) => {
4 | res.json({
5 | data: [
6 | {
7 | id: '000000001',
8 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
9 | title: '你收到了 14 份新周报',
10 | datetime: '2017-08-09',
11 | type: 'notification',
12 | },
13 | {
14 | id: '000000002',
15 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
16 | title: '你推荐的 曲妮妮 已通过第三轮面试',
17 | datetime: '2017-08-08',
18 | type: 'notification',
19 | },
20 | {
21 | id: '000000003',
22 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
23 | title: '这种模板可以区分多种通知类型',
24 | datetime: '2017-08-07',
25 | read: true,
26 | type: 'notification',
27 | },
28 | {
29 | id: '000000004',
30 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
31 | title: '左侧图标用于区分不同的类型',
32 | datetime: '2017-08-07',
33 | type: 'notification',
34 | },
35 | {
36 | id: '000000005',
37 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
38 | title: '内容不要超过两行字,超出时自动截断',
39 | datetime: '2017-08-07',
40 | type: 'notification',
41 | },
42 | {
43 | id: '000000006',
44 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
45 | title: '曲丽丽 评论了你',
46 | description: '描述信息描述信息描述信息',
47 | datetime: '2017-08-07',
48 | type: 'message',
49 | clickClose: true,
50 | },
51 | {
52 | id: '000000007',
53 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
54 | title: '朱偏右 回复了你',
55 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
56 | datetime: '2017-08-07',
57 | type: 'message',
58 | clickClose: true,
59 | },
60 | {
61 | id: '000000008',
62 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
63 | title: '标题',
64 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
65 | datetime: '2017-08-07',
66 | type: 'message',
67 | clickClose: true,
68 | },
69 | {
70 | id: '000000009',
71 | title: '任务名称',
72 | description: '任务需要在 2017-01-12 20:00 前启动',
73 | extra: '未开始',
74 | status: 'todo',
75 | type: 'event',
76 | },
77 | {
78 | id: '000000010',
79 | title: '第三方紧急代码变更',
80 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
81 | extra: '马上到期',
82 | status: 'urgent',
83 | type: 'event',
84 | },
85 | {
86 | id: '000000011',
87 | title: '信息安全考试',
88 | description: '指派竹尔于 2017-01-09 前完成更新并发布',
89 | extra: '已耗时 8 天',
90 | status: 'doing',
91 | type: 'event',
92 | },
93 | {
94 | id: '000000012',
95 | title: 'ABCD 版本发布',
96 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
97 | extra: '进行中',
98 | status: 'processing',
99 | type: 'event',
100 | },
101 | ],
102 | });
103 | };
104 |
105 | export default {
106 | 'GET /api/notices': getNotices,
107 | };
108 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeList.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar, List } from 'antd';
2 |
3 | import React from 'react';
4 | import classNames from 'classnames';
5 | import styles from './NoticeList.less';
6 |
7 | export type NoticeIconTabProps = {
8 | count?: number;
9 | showClear?: boolean;
10 | showViewMore?: boolean;
11 | style?: React.CSSProperties;
12 | title: string;
13 | tabKey: API.NoticeIconItemType;
14 | onClick?: (item: API.NoticeIconItem) => void;
15 | onClear?: () => void;
16 | emptyText?: string;
17 | clearText?: string;
18 | viewMoreText?: string;
19 | list: API.NoticeIconItem[];
20 | onViewMore?: (e: any) => void;
21 | };
22 | const NoticeList: React.FC = ({
23 | list = [],
24 | onClick,
25 | onClear,
26 | title,
27 | onViewMore,
28 | emptyText,
29 | showClear = true,
30 | clearText,
31 | viewMoreText,
32 | showViewMore = false,
33 | }) => {
34 | if (!list || list.length === 0) {
35 | return (
36 |
37 |

41 |
{emptyText}
42 |
43 | );
44 | }
45 | return (
46 |
47 |
48 | className={styles.list}
49 | dataSource={list}
50 | renderItem={(item, i) => {
51 | const itemCls = classNames(styles.item, {
52 | [styles.read]: item.read,
53 | });
54 | // eslint-disable-next-line no-nested-ternary
55 | const leftIcon = item.avatar ? (
56 | typeof item.avatar === 'string' ? (
57 |
58 | ) : (
59 | {item.avatar}
60 | )
61 | ) : null;
62 |
63 | return (
64 | {
68 | onClick?.(item);
69 | }}
70 | >
71 |
76 | {item.title}
77 | {item.extra}
78 |
79 | }
80 | description={
81 |
82 |
{item.description}
83 |
{item.datetime}
84 |
85 | }
86 | />
87 |
88 | );
89 | }}
90 | />
91 |
92 | {showClear ? (
93 |
94 | {clearText} {title}
95 |
96 | ) : null}
97 | {showViewMore ? (
98 |
{
100 | if (onViewMore) {
101 | onViewMore(e);
102 | }
103 | }}
104 | >
105 | {viewMoreText}
106 |
107 | ) : null}
108 |
109 |
110 | );
111 | };
112 |
113 | export default NoticeList;
114 |
--------------------------------------------------------------------------------
/src/locales/en-US/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': 'Basic Settings',
3 | 'app.settings.menuMap.security': 'Security Settings',
4 | 'app.settings.menuMap.binding': 'Account Binding',
5 | 'app.settings.menuMap.notification': 'New Message Notification',
6 | 'app.settings.basic.avatar': 'Avatar',
7 | 'app.settings.basic.change-avatar': 'Change avatar',
8 | 'app.settings.basic.email': 'Email',
9 | 'app.settings.basic.email-message': 'Please input your email!',
10 | 'app.settings.basic.nickname': 'Nickname',
11 | 'app.settings.basic.nickname-message': 'Please input your Nickname!',
12 | 'app.settings.basic.profile': 'Personal profile',
13 | 'app.settings.basic.profile-message': 'Please input your personal profile!',
14 | 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
15 | 'app.settings.basic.country': 'Country/Region',
16 | 'app.settings.basic.country-message': 'Please input your country!',
17 | 'app.settings.basic.geographic': 'Province or city',
18 | 'app.settings.basic.geographic-message': 'Please input your geographic info!',
19 | 'app.settings.basic.address': 'Street Address',
20 | 'app.settings.basic.address-message': 'Please input your address!',
21 | 'app.settings.basic.phone': 'Phone Number',
22 | 'app.settings.basic.phone-message': 'Please input your phone!',
23 | 'app.settings.basic.update': 'Update Information',
24 | 'app.settings.security.strong': 'Strong',
25 | 'app.settings.security.medium': 'Medium',
26 | 'app.settings.security.weak': 'Weak',
27 | 'app.settings.security.password': 'Account Password',
28 | 'app.settings.security.password-description': 'Current password strength',
29 | 'app.settings.security.phone': 'Security Phone',
30 | 'app.settings.security.phone-description': 'Bound phone',
31 | 'app.settings.security.question': 'Security Question',
32 | 'app.settings.security.question-description':
33 | 'The security question is not set, and the security policy can effectively protect the account security',
34 | 'app.settings.security.email': 'Backup Email',
35 | 'app.settings.security.email-description': 'Bound Email',
36 | 'app.settings.security.mfa': 'MFA Device',
37 | 'app.settings.security.mfa-description':
38 | 'Unbound MFA device, after binding, can be confirmed twice',
39 | 'app.settings.security.modify': 'Modify',
40 | 'app.settings.security.set': 'Set',
41 | 'app.settings.security.bind': 'Bind',
42 | 'app.settings.binding.taobao': 'Binding Taobao',
43 | 'app.settings.binding.taobao-description': 'Currently unbound Taobao account',
44 | 'app.settings.binding.alipay': 'Binding Alipay',
45 | 'app.settings.binding.alipay-description': 'Currently unbound Alipay account',
46 | 'app.settings.binding.dingding': 'Binding DingTalk',
47 | 'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',
48 | 'app.settings.binding.bind': 'Bind',
49 | 'app.settings.notification.password': 'Account Password',
50 | 'app.settings.notification.password-description':
51 | 'Messages from other users will be notified in the form of a station letter',
52 | 'app.settings.notification.messages': 'System Messages',
53 | 'app.settings.notification.messages-description':
54 | 'System messages will be notified in the form of a station letter',
55 | 'app.settings.notification.todo': 'To-do Notification',
56 | 'app.settings.notification.todo-description':
57 | 'The to-do list will be notified in the form of a letter from the station',
58 | 'app.settings.open': 'Open',
59 | 'app.settings.close': 'Close',
60 | };
61 |
--------------------------------------------------------------------------------
/src/locales/id-ID/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': 'Pengaturan Dasar',
3 | 'app.settings.menuMap.security': 'Pengaturan Keamanan',
4 | 'app.settings.menuMap.binding': 'Pengikatan Akun',
5 | 'app.settings.menuMap.notification': 'Notifikasi Pesan Baru',
6 | 'app.settings.basic.avatar': 'Avatar',
7 | 'app.settings.basic.change-avatar': 'Ubah avatar',
8 | 'app.settings.basic.email': 'Email',
9 | 'app.settings.basic.email-message': 'Tolong masukkan email!',
10 | 'app.settings.basic.nickname': 'Nickname',
11 | 'app.settings.basic.nickname-message': 'Tolong masukkan Nickname!',
12 | 'app.settings.basic.profile': 'Profil Personal',
13 | 'app.settings.basic.profile-message': 'Tolong masukkan profil personal!',
14 | 'app.settings.basic.profile-placeholder': 'Perkenalan Singkat tentang Diri Anda',
15 | 'app.settings.basic.country': 'Negara/Wilayah',
16 | 'app.settings.basic.country-message': 'Tolong masukkan negara anda!',
17 | 'app.settings.basic.geographic': 'Provinsi atau kota',
18 | 'app.settings.basic.geographic-message': 'Tolong masukkan info geografis anda!',
19 | 'app.settings.basic.address': 'Alamat Jalan',
20 | 'app.settings.basic.address-message': 'Tolong masukkan Alamat Jalan anda!',
21 | 'app.settings.basic.phone': 'Nomor Ponsel',
22 | 'app.settings.basic.phone-message': 'Tolong masukkan Nomor Ponsel anda!',
23 | 'app.settings.basic.update': 'Perbarui Informasi',
24 | 'app.settings.security.strong': 'Kuat',
25 | 'app.settings.security.medium': 'Sedang',
26 | 'app.settings.security.weak': 'Lemah',
27 | 'app.settings.security.password': 'Kata Sandi Akun',
28 | 'app.settings.security.password-description': 'Kekuatan Kata Sandi saat ini',
29 | 'app.settings.security.phone': 'Keamanan Ponsel',
30 | 'app.settings.security.phone-description': 'Mengikat Ponsel',
31 | 'app.settings.security.question': 'Pertanyaan Keamanan',
32 | 'app.settings.security.question-description':
33 | 'Pertanyaan Keamanan belum diatur, dan kebijakan keamanan dapat melindungi akun secara efektif',
34 | 'app.settings.security.email': 'Email Cadangan',
35 | 'app.settings.security.email-description': 'Mengikat Email',
36 | 'app.settings.security.mfa': 'Perangka MFA',
37 | 'app.settings.security.mfa-description':
38 | 'Tidak mengikat Perangkat MFA, setelah diikat, dapat dikonfirmasi dua kali',
39 | 'app.settings.security.modify': 'Modifikasi',
40 | 'app.settings.security.set': 'Setel',
41 | 'app.settings.security.bind': 'Ikat',
42 | 'app.settings.binding.taobao': 'Mengikat Taobao',
43 | 'app.settings.binding.taobao-description': 'Tidak mengikat akun Taobao saat ini',
44 | 'app.settings.binding.alipay': 'Mengikat Alipay',
45 | 'app.settings.binding.alipay-description': 'Tidak mengikat akun Alipay saat ini',
46 | 'app.settings.binding.dingding': 'Mengikat DingTalk',
47 | 'app.settings.binding.dingding-description': 'Tidak mengikat akun DingTalk',
48 | 'app.settings.binding.bind': 'Ikat',
49 | 'app.settings.notification.password': 'Kata Sandi Akun',
50 | 'app.settings.notification.password-description':
51 | 'Pesan dari pengguna lain akan diberitahu dalam bentuk surat',
52 | 'app.settings.notification.messages': 'Pesan Sistem',
53 | 'app.settings.notification.messages-description':
54 | 'Pesan sistem akan diberitahu dalam bentuk surat',
55 | 'app.settings.notification.todo': 'Notifikasi daftar To-do',
56 | 'app.settings.notification.todo-description':
57 | 'Daftar to-do akan diberitahukan dalam bentuk surat dari stasiun',
58 | 'app.settings.open': 'Buka',
59 | 'app.settings.close': 'Tutup',
60 | };
61 |
--------------------------------------------------------------------------------
/src/locales/ja-JP/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title': 'Ant Designは、西湖区で最も影響力のあるWebデザイン仕様です。',
3 | 'pages.login.accountLogin.tab': 'アカウントログイン',
4 | 'pages.login.accountLogin.errorMessage':
5 | 'ユーザー名/パスワードが正しくありません(admin/ant.design)',
6 | 'pages.login.username.placeholder': 'ユーザー名:adminまたはuser',
7 | 'pages.login.username.required': 'ユーザー名を入力してください!',
8 | 'pages.login.password.placeholder': 'パスワード:ant.design',
9 | 'pages.login.password.required': 'パスワードを入力してください!',
10 | 'pages.login.phoneLogin.tab': '電話ログイン',
11 | 'pages.login.phoneLogin.errorMessage': '検証コードエラー',
12 | 'pages.login.phoneNumber.placeholder': '電話番号',
13 | 'pages.login.phoneNumber.required': '電話番号を入力してください!',
14 | 'pages.login.phoneNumber.invalid': '電話番号が無効です!',
15 | 'pages.login.captcha.placeholder': '確認コード',
16 | 'pages.login.captcha.required': '確認コードを入力してください!',
17 | 'pages.login.phoneLogin.getVerificationCode': '確認コードを取得',
18 | 'pages.getCaptchaSecondText': '秒',
19 | 'pages.login.rememberMe': 'Remember me',
20 | 'pages.login.forgotPassword': 'パスワードをお忘れですか?',
21 | 'pages.login.submit': 'ログイン',
22 | 'pages.login.loginWith': 'その他のログイン方法:',
23 | 'pages.login.registerAccount': 'アカウント登録',
24 | 'pages.welcome.advancedComponent': '高度なコンポーネント',
25 | 'pages.welcome.link': 'ようこそ',
26 | 'pages.welcome.advancedLayout': '高度なレイアウト',
27 | 'pages.welcome.alertMessage': 'より高速で強力な頑丈なコンポーネントがリリースされました。',
28 | 'pages.admin.subPage.title': 'このページは管理者のみが表示できます',
29 | 'pages.admin.subPage.alertMessage':
30 | 'Umi uiがリリースされました。npm run uiを使用して体験してください。',
31 | 'pages.searchTable.createForm.newRule': '新しいルール',
32 | 'pages.searchTable.updateForm.ruleConfig': 'ルール構成',
33 | 'pages.searchTable.updateForm.basicConfig': '基本情報',
34 | 'pages.searchTable.updateForm.ruleName.nameLabel': 'ルール名',
35 | 'pages.searchTable.updateForm.ruleName.nameRules': 'ルール名を入力してください!',
36 | 'pages.searchTable.updateForm.ruleDesc.descLabel': 'ルールの説明',
37 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder': '5文字以上入力してください',
38 | 'pages.searchTable.updateForm.ruleDesc.descRules': '5文字以上のルールの説明を入力してください!',
39 | 'pages.searchTable.updateForm.ruleProps.title': 'プロパティの構成',
40 | 'pages.searchTable.updateForm.object': '監視対象',
41 | 'pages.searchTable.updateForm.ruleProps.templateLabel': 'ルールテンプレート',
42 | 'pages.searchTable.updateForm.ruleProps.typeLabel': 'ルールタイプ',
43 | 'pages.searchTable.updateForm.schedulingPeriod.title': 'スケジュール期間の設定',
44 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': '開始時間',
45 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules': '開始時間を選択してください!',
46 | 'pages.searchTable.titleDesc': '説明',
47 | 'pages.searchTable.ruleName': 'ルール名が必要です',
48 | 'pages.searchTable.titleCallNo': 'サービスコール数',
49 | 'pages.searchTable.titleStatus': 'ステータス',
50 | 'pages.searchTable.nameStatus.default': 'デフォルト',
51 | 'pages.searchTable.nameStatus.running': '起動中',
52 | 'pages.searchTable.nameStatus.online': 'オンライン',
53 | 'pages.searchTable.nameStatus.abnormal': '異常',
54 | 'pages.searchTable.titleUpdatedAt': '最終スケジュール',
55 | 'pages.searchTable.exception': '例外の理由を入力してください!',
56 | 'pages.searchTable.titleOption': 'オプション',
57 | 'pages.searchTable.config': '構成',
58 | 'pages.searchTable.subscribeAlert': 'アラートを購読する',
59 | 'pages.searchTable.title': 'お問い合わせフォーム',
60 | 'pages.searchTable.new': '新しい',
61 | 'pages.searchTable.chosen': '選んだ項目',
62 | 'pages.searchTable.item': '項目',
63 | 'pages.searchTable.totalServiceCalls': 'サービスコールの総数',
64 | 'pages.searchTable.tenThousand': '万',
65 | 'pages.searchTable.batchDeletion': 'バッチ削除',
66 | 'pages.searchTable.batchApproval': 'バッチ承認',
67 | };
68 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/settings.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.settings.menuMap.basic': 'Configurações Básicas',
3 | 'app.settings.menuMap.security': 'Configurações de Segurança',
4 | 'app.settings.menuMap.binding': 'Vinculação de Conta',
5 | 'app.settings.menuMap.notification': 'Mensagens de Notificação',
6 | 'app.settings.basic.avatar': 'Avatar',
7 | 'app.settings.basic.change-avatar': 'Alterar avatar',
8 | 'app.settings.basic.email': 'Email',
9 | 'app.settings.basic.email-message': 'Por favor insira seu email!',
10 | 'app.settings.basic.nickname': 'Nome de usuário',
11 | 'app.settings.basic.nickname-message': 'Por favor insira seu nome de usuário!',
12 | 'app.settings.basic.profile': 'Perfil pessoal',
13 | 'app.settings.basic.profile-message': 'Por favor insira seu perfil pessoal!',
14 | 'app.settings.basic.profile-placeholder': 'Breve introdução sua',
15 | 'app.settings.basic.country': 'País/Região',
16 | 'app.settings.basic.country-message': 'Por favor insira país!',
17 | 'app.settings.basic.geographic': 'Província, estado ou cidade',
18 | 'app.settings.basic.geographic-message': 'Por favor insira suas informações geográficas!',
19 | 'app.settings.basic.address': 'Endereço',
20 | 'app.settings.basic.address-message': 'Por favor insira seu endereço!',
21 | 'app.settings.basic.phone': 'Número de telefone',
22 | 'app.settings.basic.phone-message': 'Por favor insira seu número de telefone!',
23 | 'app.settings.basic.update': 'Atualizar Informações',
24 | 'app.settings.security.strong': 'Forte',
25 | 'app.settings.security.medium': 'Média',
26 | 'app.settings.security.weak': 'Fraca',
27 | 'app.settings.security.password': 'Senha da Conta',
28 | 'app.settings.security.password-description': 'Força da senha',
29 | 'app.settings.security.phone': 'Telefone de Seguraça',
30 | 'app.settings.security.phone-description': 'Telefone vinculado',
31 | 'app.settings.security.question': 'Pergunta de Segurança',
32 | 'app.settings.security.question-description':
33 | 'A pergunta de segurança não está definida e a política de segurança pode proteger efetivamente a segurança da conta',
34 | 'app.settings.security.email': 'Email de Backup',
35 | 'app.settings.security.email-description': 'Email vinculado',
36 | 'app.settings.security.mfa': 'Dispositivo MFA',
37 | 'app.settings.security.mfa-description':
38 | 'O dispositivo MFA não vinculado, após a vinculação, pode ser confirmado duas vezes',
39 | 'app.settings.security.modify': 'Modificar',
40 | 'app.settings.security.set': 'Atribuir',
41 | 'app.settings.security.bind': 'Vincular',
42 | 'app.settings.binding.taobao': 'Vincular Taobao',
43 | 'app.settings.binding.taobao-description': 'Atualmente não vinculado à conta Taobao',
44 | 'app.settings.binding.alipay': 'Vincular Alipay',
45 | 'app.settings.binding.alipay-description': 'Atualmente não vinculado à conta Alipay',
46 | 'app.settings.binding.dingding': 'Vincular DingTalk',
47 | 'app.settings.binding.dingding-description': 'Atualmente não vinculado à conta DingTalk',
48 | 'app.settings.binding.bind': 'Vincular',
49 | 'app.settings.notification.password': 'Senha da Conta',
50 | 'app.settings.notification.password-description':
51 | 'Mensagens de outros usuários serão notificadas na forma de uma estação de letra',
52 | 'app.settings.notification.messages': 'Mensagens de Sistema',
53 | 'app.settings.notification.messages-description':
54 | 'Mensagens de sistema serão notificadas na forma de uma estação de letra',
55 | 'app.settings.notification.todo': 'Notificação de To-do',
56 | 'app.settings.notification.todo-description':
57 | 'A lista de to-do será notificada na forma de uma estação de letra',
58 | 'app.settings.open': 'Aberto',
59 | 'app.settings.close': 'Fechado',
60 | };
61 |
--------------------------------------------------------------------------------
/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/ProductInfo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, forwardRef, useImperativeHandle } from 'react'
2 | import { Form, Input, Select,
3 | Row, Col, DatePicker } from 'antd'
4 | import moment from 'moment'
5 |
6 | const { Option } = Select
7 | const { TextArea } = Input
8 |
9 | const dateFormat = 'YYYY-MM-DD'
10 |
11 | export type productProps = {
12 | infoData?: any
13 | }
14 |
15 | const formInput = ({infoData}: productProps, ref: any) => {
16 | const [form] = Form.useForm()
17 |
18 | // 暴露组件的方法
19 | useImperativeHandle(ref, () => ({
20 | getForm: () => {
21 | return form
22 | }
23 | }))
24 |
25 | useEffect(() => {
26 | if (infoData) {
27 | form.setFieldsValue({...infoData,
28 | establishedTime: moment(infoData.establishedTime)})
29 | }
30 | }, [])
31 |
32 | const companyOptions = [
33 | {
34 | companyName: '大米科技有限公司',
35 | id: 1,
36 | companyCode: '1273883',
37 | desc: '这个公司非常棒',
38 | establishedTime: '2020-09-28'
39 | },
40 | {
41 | companyName: '大米科技有限公司2',
42 | id: 2,
43 | companyCode: '2273883',
44 | desc: '这个公司非常棒2',
45 | establishedTime: '2011-09-09'
46 | },
47 | {
48 | companyName: '大米科技有限公司3',
49 | id: 3,
50 | companyCode: '2273883',
51 | desc: '这个公司非常棒3',
52 | establishedTime: '2018-08-18'
53 | }
54 | ]
55 |
56 | // 根据选择公司进行回填
57 | const changeCompany = (val, option) => {
58 | console.log(option)
59 | form.setFieldsValue({
60 | ...option,
61 | establishedTime: moment(option.establishedtime)
62 | })
63 | }
64 | return (
65 |
66 |
128 |
129 | )
130 | }
131 |
132 | // 通过forwardRef传递
133 | export default forwardRef(formInput)
--------------------------------------------------------------------------------
/src/locales/fa-IR/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title': 'طراحی مورچه تأثیرگذارترین مشخصات طراحی وب در منطقه Xihu است',
3 | 'pages.login.accountLogin.tab': 'ورود به حساب کاربری',
4 | 'pages.login.accountLogin.errorMessage': 'نام کاربری / رمزعبور نادرست (مدیر / ant.design)',
5 | 'pages.login.username.placeholder': 'نام کاربری: مدیر یا کاربر',
6 | 'pages.login.username.required': 'لطفا نام کاربری خود را وارد کنید!',
7 | 'pages.login.password.placeholder': 'رمز عبور: ant.design',
8 | 'pages.login.password.required': 'لطفاً رمز ورود خود را وارد کنید!',
9 | 'pages.login.phoneLogin.tab': 'ورود به سیستم تلفن',
10 | 'pages.login.phoneLogin.errorMessage': 'خطای کد تأیید',
11 | 'pages.login.phoneNumber.placeholder': 'شماره تلفن',
12 | 'pages.login.phoneNumber.required': 'لطفاً شماره تلفن خود را وارد کنید!',
13 | 'pages.login.phoneNumber.invalid': 'شماره تلفن نامعتبر است!',
14 | 'pages.login.captcha.placeholder': 'کد تایید',
15 | 'pages.login.captcha.required': 'لطفا کد تأیید را وارد کنید!',
16 | 'pages.login.phoneLogin.getVerificationCode': 'دریافت کد',
17 | 'pages.getCaptchaSecondText': 'ثانیه',
18 | 'pages.login.rememberMe': 'مرا به خاطر بسپار',
19 | 'pages.login.forgotPassword': 'رمز عبور را فراموش کرده اید ?',
20 | 'pages.login.submit': 'ارسال',
21 | 'pages.login.loginWith': 'وارد شوید با :',
22 | 'pages.login.registerAccount': 'ثبت نام',
23 | 'pages.welcome.advancedComponent': 'مولفه پیشرفته',
24 | 'pages.welcome.link': 'خوش آمدید',
25 | 'pages.welcome.advancedLayout': 'چیدمان پیشرفته',
26 | 'pages.welcome.alertMessage': 'اجزای سنگین تر سریعتر و قوی تر آزاد شده اند.',
27 | 'pages.admin.subPage.title': 'این صفحه فقط توسط مدیر قابل مشاهده است',
28 | 'pages.admin.subPage.alertMessage':
29 | 'رابط کاربری Umi اکنون منتشر شده است ، برای شروع تجربه استفاده از npm run ui خوش آمدید.',
30 | 'pages.searchTable.createForm.newRule': 'قانون جدید',
31 | 'pages.searchTable.updateForm.ruleConfig': 'پیکربندی قانون',
32 | 'pages.searchTable.updateForm.basicConfig': 'اطلاعات اولیه',
33 | 'pages.searchTable.updateForm.ruleName.nameLabel': ' نام قانون',
34 | 'pages.searchTable.updateForm.ruleName.nameRules': 'لطفاً نام قانون را وارد کنید!',
35 | 'pages.searchTable.updateForm.ruleDesc.descLabel': 'شرح قانون',
36 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'لطفاً حداقل پنج حرف وارد کنید',
37 | 'pages.searchTable.updateForm.ruleDesc.descRules':
38 | 'لطفاً حداقل یک قانون حاوی پنج کاراکتر شرح دهید!',
39 | 'pages.searchTable.updateForm.ruleProps.title': 'پیکربندی خصوصیات',
40 | 'pages.searchTable.updateForm.object': 'نظارت بر شی',
41 | 'pages.searchTable.updateForm.ruleProps.templateLabel': 'الگوی قانون',
42 | 'pages.searchTable.updateForm.ruleProps.typeLabel': 'نوع قانون',
43 | 'pages.searchTable.updateForm.schedulingPeriod.title': 'تنظیم دوره زمان بندی',
44 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'زمان شروع',
45 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'لطفاً زمان شروع را انتخاب کنید!',
46 | 'pages.searchTable.titleDesc': 'شرح',
47 | 'pages.searchTable.ruleName': 'نام قانون لازم است',
48 | 'pages.searchTable.titleCallNo': 'تعداد تماس های خدماتی',
49 | 'pages.searchTable.titleStatus': 'وضعیت',
50 | 'pages.searchTable.nameStatus.default': 'پیش فرض',
51 | 'pages.searchTable.nameStatus.running': 'در حال دویدن',
52 | 'pages.searchTable.nameStatus.online': 'برخط',
53 | 'pages.searchTable.nameStatus.abnormal': 'غیرطبیعی',
54 | 'pages.searchTable.titleUpdatedAt': 'آخرین برنامه ریزی در',
55 | 'pages.searchTable.exception': 'لطفا دلیل استثنا را وارد کنید!',
56 | 'pages.searchTable.titleOption': 'گزینه',
57 | 'pages.searchTable.config': 'پیکربندی',
58 | 'pages.searchTable.subscribeAlert': 'مشترک شدن در هشدارها',
59 | 'pages.searchTable.title': 'فرم درخواست',
60 | 'pages.searchTable.new': 'جدید',
61 | 'pages.searchTable.chosen': 'انتخاب شده',
62 | 'pages.searchTable.item': 'مورد',
63 | 'pages.searchTable.totalServiceCalls': 'تعداد کل تماس های خدماتی',
64 | 'pages.searchTable.tenThousand': '0000',
65 | 'pages.searchTable.batchDeletion': 'حذف دسته ای',
66 | 'pages.searchTable.batchApproval': 'تصویب دسته ای',
67 | };
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ant-design-pro",
3 | "version": "5.0.0-beta.2",
4 | "private": true,
5 | "description": "An out-of-box UI solution for enterprise applications",
6 | "scripts": {
7 | "analyze": "cross-env ANALYZE=1 umi build",
8 | "build": "umi build",
9 | "deploy": "npm run site && npm run gh-pages",
10 | "dev": "npm run start:dev",
11 | "gh-pages": "gh-pages -d dist",
12 | "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
13 | "postinstall": "umi g tmp",
14 | "lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier",
15 | "lint-staged": "lint-staged",
16 | "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
17 | "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
18 | "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
19 | "lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
20 | "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
21 | "openapi": "umi openapi",
22 | "precommit": "lint-staged",
23 | "prettier": "prettier -c --write \"src/**/*\"",
24 | "start": "cross-env UMI_ENV=dev umi dev",
25 | "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev",
26 | "start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev",
27 | "start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev",
28 | "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev",
29 | "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev",
30 | "pretest": "node ./tests/beforeTest",
31 | "test": "umi test",
32 | "test:all": "node ./tests/run-tests.js",
33 | "test:component": "umi test ./src/components",
34 | "tsc": "tsc --noEmit"
35 | },
36 | "lint-staged": {
37 | "**/*.less": "stylelint --syntax less",
38 | "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
39 | "**/*.{js,jsx,tsx,ts,less,md,json}": [
40 | "prettier --write"
41 | ]
42 | },
43 | "browserslist": [
44 | "> 1%",
45 | "last 2 versions",
46 | "not ie <= 10"
47 | ],
48 | "dependencies": {
49 | "@ant-design/icons": "^4.5.0",
50 | "@ant-design/pro-card": "^1.11.11",
51 | "@ant-design/pro-descriptions": "^1.6.8",
52 | "@ant-design/pro-form": "^1.18.3",
53 | "@ant-design/pro-layout": "^6.15.3",
54 | "@ant-design/pro-table": "^2.30.8",
55 | "@umijs/openapi": "^1.1.14",
56 | "@umijs/plugin-openapi": "^1.2.0",
57 | "@umijs/route-utils": "^1.0.36",
58 | "antd": "^4.14.0",
59 | "classnames": "^2.2.6",
60 | "lodash": "^4.17.11",
61 | "moment": "^2.25.3",
62 | "omit.js": "^2.0.2",
63 | "react": "^17.0.0",
64 | "react-dev-inspector": "^1.1.1",
65 | "react-dom": "^17.0.0",
66 | "react-helmet-async": "^1.0.4",
67 | "umi": "^3.4.0",
68 | "umi-request": "^1.0.8"
69 | },
70 | "devDependencies": {
71 | "@ant-design/pro-cli": "^2.0.2",
72 | "@types/classnames": "^2.2.7",
73 | "@types/express": "^4.17.0",
74 | "@types/history": "^4.7.2",
75 | "@types/jest": "^26.0.0",
76 | "@types/lodash": "^4.14.144",
77 | "@types/react": "^17.0.0",
78 | "@types/react-dom": "^17.0.0",
79 | "@types/react-helmet": "^6.1.0",
80 | "@umijs/fabric": "^2.5.2",
81 | "@umijs/plugin-blocks": "^2.0.5",
82 | "@umijs/plugin-esbuild": "^1.0.1",
83 | "@umijs/preset-ant-design-pro": "^1.2.0",
84 | "@umijs/preset-dumi": "^1.1.7",
85 | "@umijs/preset-react": "^1.7.4",
86 | "@umijs/yorkie": "^2.0.3",
87 | "carlo": "^0.9.46",
88 | "cross-env": "^7.0.0",
89 | "cross-port-killer": "^1.1.1",
90 | "detect-installer": "^1.0.1",
91 | "enzyme": "^3.11.0",
92 | "eslint": "^7.1.0",
93 | "express": "^4.17.1",
94 | "gh-pages": "^3.0.0",
95 | "jsdom-global": "^3.0.2",
96 | "lint-staged": "^10.0.0",
97 | "mockjs": "^1.0.1-beta3",
98 | "prettier": "^2.0.1",
99 | "puppeteer-core": "^8.0.0",
100 | "stylelint": "^13.0.0",
101 | "typescript": "^4.2.2"
102 | },
103 | "engines": {
104 | "node": ">=10.0.0"
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/app.tsx:
--------------------------------------------------------------------------------
1 | import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
2 | import { PageLoading } from '@ant-design/pro-layout';
3 | import { notification } from 'antd';
4 | import type { RequestConfig, RunTimeLayoutConfig } from 'umi';
5 | import { history, Link } from 'umi';
6 | import RightContent from '@/components/RightContent';
7 | import Footer from '@/components/Footer';
8 | import type { ResponseError } from 'umi-request';
9 | import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
10 | import { BookOutlined, LinkOutlined } from '@ant-design/icons';
11 |
12 | const isDev = process.env.NODE_ENV === 'development';
13 | const loginPath = '/user/login';
14 |
15 | /** 获取用户信息比较慢的时候会展示一个 loading */
16 | export const initialStateConfig = {
17 | loading: ,
18 | };
19 |
20 | /**
21 | * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
22 | * */
23 | export async function getInitialState(): Promise<{
24 | settings?: Partial;
25 | currentUser?: API.CurrentUser;
26 | fetchUserInfo?: () => Promise;
27 | }> {
28 | const fetchUserInfo = async () => {
29 | try {
30 | const currentUser = await queryCurrentUser();
31 | return currentUser;
32 | } catch (error) {
33 | history.push(loginPath);
34 | }
35 | return undefined;
36 | };
37 | // 如果是登录页面,不执行
38 | if (history.location.pathname !== loginPath) {
39 | const currentUser = await fetchUserInfo();
40 | return {
41 | fetchUserInfo,
42 | currentUser,
43 | settings: {},
44 | };
45 | }
46 | return {
47 | fetchUserInfo,
48 | settings: {},
49 | };
50 | }
51 |
52 | // https://umijs.org/zh-CN/plugins/plugin-layout
53 | export const layout: RunTimeLayoutConfig = ({ initialState }) => {
54 | return {
55 | rightContentRender: () => ,
56 | disableContentMargin: false,
57 | waterMarkProps: {
58 | content: initialState?.currentUser?.name,
59 | },
60 | footerRender: () => ,
61 | onPageChange: () => {
62 | const { location } = history;
63 | // 如果没有登录,重定向到 login
64 | if (!initialState?.currentUser && location.pathname !== loginPath) {
65 | history.push(loginPath);
66 | }
67 | },
68 | links: isDev
69 | ? [
70 |
71 |
72 | openAPI 文档
73 | ,
74 |
75 |
76 | 业务组件文档
77 | ,
78 | ]
79 | : [],
80 | menuHeaderRender: undefined,
81 | // 自定义 403 页面
82 | // unAccessible: unAccessible
,
83 | ...initialState?.settings,
84 | };
85 | };
86 |
87 | const codeMessage = {
88 | 200: '服务器成功返回请求的数据。',
89 | 201: '新建或修改数据成功。',
90 | 202: '一个请求已经进入后台排队(异步任务)。',
91 | 204: '删除数据成功。',
92 | 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
93 | 401: '用户没有权限(令牌、用户名、密码错误)。',
94 | 403: '用户得到授权,但是访问是被禁止的。',
95 | 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
96 | 405: '请求方法不被允许。',
97 | 406: '请求的格式不可得。',
98 | 410: '请求的资源被永久删除,且不会再得到的。',
99 | 422: '当创建一个对象时,发生一个验证错误。',
100 | 500: '服务器发生错误,请检查服务器。',
101 | 502: '网关错误。',
102 | 503: '服务不可用,服务器暂时过载或维护。',
103 | 504: '网关超时。',
104 | };
105 |
106 | /** 异常处理程序
107 | * @see https://beta-pro.ant.design/docs/request-cn
108 | */
109 | const errorHandler = (error: ResponseError) => {
110 | const { response } = error;
111 | if (response && response.status) {
112 | const errorText = codeMessage[response.status] || response.statusText;
113 | const { status, url } = response;
114 |
115 | notification.error({
116 | message: `请求错误 ${status}: ${url}`,
117 | description: errorText,
118 | });
119 | }
120 |
121 | if (!response) {
122 | notification.error({
123 | description: '您的网络发生异常,无法连接服务器',
124 | message: '网络异常',
125 | });
126 | }
127 | throw error;
128 | };
129 |
130 | // https://umijs.org/zh-CN/plugins/plugin-request
131 | export const request: RequestConfig = {
132 | errorHandler,
133 | };
134 |
--------------------------------------------------------------------------------
/src/locales/en-US/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title':
3 | 'Ant Design is the most influential web design specification in Xihu district',
4 | 'pages.login.accountLogin.tab': 'Account Login',
5 | 'pages.login.accountLogin.errorMessage': 'Incorrect username/password(admin/ant.design)',
6 | 'pages.login.username.placeholder': 'Username: admin or user',
7 | 'pages.login.username.required': 'Please input your username!',
8 | 'pages.login.password.placeholder': 'Password: ant.design',
9 | 'pages.login.password.required': 'Please input your password!',
10 | 'pages.login.phoneLogin.tab': 'Phone Login',
11 | 'pages.login.phoneLogin.errorMessage': 'Verification Code Error',
12 | 'pages.login.phoneNumber.placeholder': 'Phone Number',
13 | 'pages.login.phoneNumber.required': 'Please input your phone number!',
14 | 'pages.login.phoneNumber.invalid': 'Phone number is invalid!',
15 | 'pages.login.captcha.placeholder': 'Verification Code',
16 | 'pages.login.captcha.required': 'Please input verification code!',
17 | 'pages.login.phoneLogin.getVerificationCode': 'Get Code',
18 | 'pages.getCaptchaSecondText': 'sec(s)',
19 | 'pages.login.rememberMe': 'Remember me',
20 | 'pages.login.forgotPassword': 'Forgot Password ?',
21 | 'pages.login.submit': 'Login',
22 | 'pages.login.loginWith': 'Login with :',
23 | 'pages.login.registerAccount': 'Register Account',
24 | 'pages.welcome.advancedComponent': 'Advanced Component',
25 | 'pages.welcome.link': 'Welcome',
26 | 'pages.welcome.advancedLayout': 'Advanced Layout',
27 | 'pages.welcome.alertMessage': 'Faster and stronger heavy-duty components have been released.',
28 | 'pages.admin.subPage.title': 'This page can only be viewed by Admin',
29 | 'pages.admin.subPage.alertMessage':
30 | 'Umi ui is now released, welcome to use npm run ui to start the experience.',
31 | 'pages.searchTable.createForm.newRule': 'New Rule',
32 | 'pages.searchTable.updateForm.ruleConfig': 'Rule configuration',
33 | 'pages.searchTable.updateForm.basicConfig': 'Basic Information',
34 | 'pages.searchTable.updateForm.ruleName.nameLabel': 'Rule Name',
35 | 'pages.searchTable.updateForm.ruleName.nameRules': 'Please enter the rule name!',
36 | 'pages.searchTable.updateForm.ruleDesc.descLabel': 'Rule Description',
37 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'Please enter at least five characters',
38 | 'pages.searchTable.updateForm.ruleDesc.descRules':
39 | 'Please enter a rule description of at least five characters!',
40 | 'pages.searchTable.updateForm.ruleProps.title': 'Configure Properties',
41 | 'pages.searchTable.updateForm.object': 'Monitoring Object',
42 | 'pages.searchTable.updateForm.ruleProps.templateLabel': 'Rule Template',
43 | 'pages.searchTable.updateForm.ruleProps.typeLabel': 'Rule Type',
44 | 'pages.searchTable.updateForm.schedulingPeriod.title': 'Set Scheduling Period',
45 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Starting Time',
46 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Please choose a start time!',
47 | 'pages.searchTable.titleDesc': 'Description',
48 | 'pages.searchTable.ruleName': 'Rule name is required',
49 | 'pages.searchTable.titleCallNo': 'Number of Service Calls',
50 | 'pages.searchTable.titleStatus': 'Status',
51 | 'pages.searchTable.nameStatus.default': 'default',
52 | 'pages.searchTable.nameStatus.running': 'running',
53 | 'pages.searchTable.nameStatus.online': 'online',
54 | 'pages.searchTable.nameStatus.abnormal': 'abnormal',
55 | 'pages.searchTable.titleUpdatedAt': 'Last Scheduled at',
56 | 'pages.searchTable.exception': 'Please enter the reason for the exception!',
57 | 'pages.searchTable.titleOption': 'Option',
58 | 'pages.searchTable.config': 'Configuration',
59 | 'pages.searchTable.subscribeAlert': 'Subscribe to alerts',
60 | 'pages.searchTable.title': 'Enquiry Form',
61 | 'pages.searchTable.new': 'New',
62 | 'pages.searchTable.chosen': 'chosen',
63 | 'pages.searchTable.item': 'item',
64 | 'pages.searchTable.totalServiceCalls': 'Total Number of Service Calls',
65 | 'pages.searchTable.tenThousand': '0000',
66 | 'pages.searchTable.batchDeletion': 'bacth deletion',
67 | 'pages.searchTable.batchApproval': 'batch approval',
68 | };
69 |
--------------------------------------------------------------------------------
/src/locales/id-ID/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title':
3 | 'Ant Design adalah spesifikasi desain Web yang paling berpengaruh di Kabupaten Xihu',
4 | 'pages.login.accountLogin.tab': 'Login dengan akun',
5 | 'pages.login.accountLogin.errorMessage': 'Nama pengguna dan kata sandi salah(admin/ant.design)',
6 | 'pages.login.username.placeholder': 'nama pengguna: admin atau user',
7 | 'pages.login.username.required': 'Nama pengguna harus diisi!',
8 | 'pages.login.password.placeholder': 'kata sandi: ant.design',
9 | 'pages.login.password.required': 'Kata sandi harus diisi!',
10 | 'pages.login.phoneLogin.tab': 'Login dengan ponsel',
11 | 'pages.login.phoneLogin.errorMessage': 'Kesalahan kode verifikasi',
12 | 'pages.login.phoneNumber.placeholder': 'masukkan nomor telepon',
13 | 'pages.login.phoneNumber.required': 'Nomor ponsel harus diisi!',
14 | 'pages.login.phoneNumber.invalid': 'Nomor ponsel tidak valid!',
15 | 'pages.login.captcha.placeholder': 'kode verifikasi',
16 | 'pages.login.captcha.required': 'Kode verifikasi diperlukan!',
17 | 'pages.login.phoneLogin.getVerificationCode': 'Dapatkan kode',
18 | 'pages.getCaptchaSecondText': 'detik tersisa',
19 | 'pages.login.rememberMe': 'Ingat saya',
20 | 'pages.login.forgotPassword': 'Lupa Kata Sandi?',
21 | 'pages.login.submit': 'Masuk',
22 | 'pages.login.loginWith': 'Masuk dengan :',
23 | 'pages.login.registerAccount': 'Daftar Akun',
24 | 'pages.welcome.advancedComponent': 'Formulir Lanjutan',
25 | 'pages.welcome.link': 'Selamat datang',
26 | 'pages.welcome.advancedLayout': 'Tata letak Lanjutan',
27 | 'pages.welcome.alertMessage':
28 | 'Komponen heavy-duty yang lebih cepat dan lebih kuat telah dirilis.',
29 | 'pages.admin.subPage.title': 'Halaman ini hanya dapat dilihat oleh admin',
30 | 'pages.admin.subPage.alertMessage':
31 | 'umi ui telah dirilis, silahkan gunakan npm run ui untuk memulai pengalaman.',
32 | 'pages.searchTable.createForm.newRule': 'Aturan baru',
33 | 'pages.searchTable.updateForm.ruleConfig': 'Konfigurasi aturan',
34 | 'pages.searchTable.updateForm.basicConfig': 'Informasi dasar',
35 | 'pages.searchTable.updateForm.ruleName.nameLabel': 'Nama aturan',
36 | 'pages.searchTable.updateForm.ruleName.nameRules': 'Harap masukkan nama aturan!',
37 | 'pages.searchTable.updateForm.ruleDesc.descLabel': 'Deskripsi aturan',
38 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder':
39 | 'Harap masukkan setidaknya lima karakter',
40 | 'pages.searchTable.updateForm.ruleDesc.descRules':
41 | 'Harap masukkan deskripsi aturan setidaknya lima karakter!',
42 | 'pages.searchTable.updateForm.ruleProps.title': 'Properti aturan',
43 | 'pages.searchTable.updateForm.object': 'Objek pemantauan',
44 | 'pages.searchTable.updateForm.ruleProps.templateLabel': 'Template aturan',
45 | 'pages.searchTable.updateForm.ruleProps.typeLabel': 'Jenis aturan',
46 | 'pages.searchTable.updateForm.schedulingPeriod.title': 'Periode penjadwalan',
47 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Waktu mulai',
48 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Pilih waktu mulai!',
49 | 'pages.searchTable.titleDesc': 'deskripsi',
50 | 'pages.searchTable.ruleName': 'Nama aturan wajib diisi',
51 | 'pages.searchTable.titleCallNo': 'Jumlah panggilan',
52 | 'pages.searchTable.titleStatus': 'Status',
53 | 'pages.searchTable.nameStatus.default': 'default',
54 | 'pages.searchTable.nameStatus.running': 'menyala',
55 | 'pages.searchTable.nameStatus.online': 'online',
56 | 'pages.searchTable.nameStatus.abnormal': 'abnormal',
57 | 'pages.searchTable.titleUpdatedAt': 'Waktu terjadwal',
58 | 'pages.searchTable.exception': 'Harap masukkan alasan pengecualian!',
59 | 'pages.searchTable.titleOption': 'Pengoperasian',
60 | 'pages.searchTable.config': 'Konfigurasi',
61 | 'pages.searchTable.subscribeAlert': 'Berlangganan notifikasi',
62 | 'pages.searchTable.title': 'Formulir pertanyaan',
63 | 'pages.searchTable.new': 'Baru',
64 | 'pages.searchTable.chosen': 'Terpilih',
65 | 'pages.searchTable.item': 'item',
66 | 'pages.searchTable.totalServiceCalls': 'Jumlah total panggilan layanan',
67 | 'pages.searchTable.tenThousand': '0000',
68 | 'pages.searchTable.batchDeletion': 'Penghapusan batch',
69 | 'pages.searchTable.batchApproval': 'Persetujuan batch',
70 | };
71 |
--------------------------------------------------------------------------------
/src/locales/pt-BR/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title':
3 | 'Ant Design é a especificação de web design mais influente no distrito de Xihu',
4 | 'pages.login.accountLogin.tab': 'Login da conta',
5 | 'pages.login.accountLogin.errorMessage': 'usuário/senha incorreto(admin/ant.design)',
6 | 'pages.login.username.placeholder': 'Usuário: admin or user',
7 | 'pages.login.username.required': 'Por favor insira seu usuário!',
8 | 'pages.login.password.placeholder': 'Senha: ant.design',
9 | 'pages.login.password.required': 'Por favor insira sua senha!',
10 | 'pages.login.phoneLogin.tab': 'Login com Telefone',
11 | 'pages.login.phoneLogin.errorMessage': 'Erro de Código de Verificação',
12 | 'pages.login.phoneNumber.placeholder': 'Telefone',
13 | 'pages.login.phoneNumber.required': 'Por favor entre com seu telefone!',
14 | 'pages.login.phoneNumber.invalid': 'Telefone é inválido!',
15 | 'pages.login.captcha.placeholder': 'Código de Verificação',
16 | 'pages.login.captcha.required': 'Por favor entre com o código de verificação!',
17 | 'pages.login.phoneLogin.getVerificationCode': 'Obter Código',
18 | 'pages.getCaptchaSecondText': 'seg(s)',
19 | 'pages.login.rememberMe': 'Lembre-me',
20 | 'pages.login.forgotPassword': 'Perdeu a Senha ?',
21 | 'pages.login.submit': 'Enviar',
22 | 'pages.login.loginWith': 'Login com :',
23 | 'pages.login.registerAccount': 'Registra Conta',
24 | 'pages.welcome.advancedComponent': 'Componente Avançado',
25 | 'pages.welcome.link': 'Bem-vindo',
26 | 'pages.welcome.advancedLayout': 'Layout Avançado',
27 | 'pages.welcome.alertMessage': 'Componentes pesados mais rápidos e mais fortes foram lançados.',
28 | 'pages.admin.subPage.title': 'Esta página só pode ser vista pelo Admin',
29 | 'pages.admin.subPage.alertMessage':
30 | 'O Umi ui foi lançado, bem-vindo ao usar o npm run ui para iniciar a experiência.',
31 | 'pages.searchTable.createForm.newRule': 'Neva Regra',
32 | 'pages.searchTable.updateForm.ruleConfig': 'Configuração de Regra',
33 | 'pages.searchTable.updateForm.basicConfig': 'Informação básica',
34 | 'pages.searchTable.updateForm.ruleName.nameLabel': 'Nome da Regra',
35 | 'pages.searchTable.updateForm.ruleName.nameRules': 'Por favor entre com o nome da regra!',
36 | 'pages.searchTable.updateForm.ruleDesc.descLabel': 'Descrição da Regra',
37 | 'pages.searchTable.updateForm.ruleDesc.descPlaceholder':
38 | 'Por favor insira ao menos cinco caracteres',
39 | 'pages.searchTable.updateForm.ruleDesc.descRules':
40 | 'Insira uma descrição de regra de pelo menos cinco caracteres!',
41 | 'pages.searchTable.updateForm.ruleProps.title': 'Configurar Propriedades',
42 | 'pages.searchTable.updateForm.object': 'Objeto de Monitoramento',
43 | 'pages.searchTable.updateForm.ruleProps.templateLabel': 'Modelo de Regra',
44 | 'pages.searchTable.updateForm.ruleProps.typeLabel': 'Tipo de Regra',
45 | 'pages.searchTable.updateForm.schedulingPeriod.title': 'Definir Período de Agendamento',
46 | 'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Hora de Início',
47 | 'pages.searchTable.updateForm.schedulingPeriod.timeRules':
48 | 'Por favor selecione um horáriod e início!',
49 | 'pages.searchTable.titleDesc': 'Descrição',
50 | 'pages.searchTable.ruleName': 'O nome da regra é obrigatório',
51 | 'pages.searchTable.titleCallNo': 'Número de chamadas de serviço',
52 | 'pages.searchTable.titleStatus': 'Status',
53 | 'pages.searchTable.nameStatus.default': 'padrão',
54 | 'pages.searchTable.nameStatus.running': 'executando',
55 | 'pages.searchTable.nameStatus.online': 'online',
56 | 'pages.searchTable.nameStatus.abnormal': 'anormal',
57 | 'pages.searchTable.titleUpdatedAt': 'Última programação em',
58 | 'pages.searchTable.exception': 'Por favor, indique o motivo da exceção!',
59 | 'pages.searchTable.titleOption': 'Opção',
60 | 'pages.searchTable.config': 'Configuração',
61 | 'pages.searchTable.subscribeAlert': 'Inscreva-se para receber alertas',
62 | 'pages.searchTable.title': 'Formulário de Consulta',
63 | 'pages.searchTable.new': 'Novo',
64 | 'pages.searchTable.chosen': 'selecionado',
65 | 'pages.searchTable.item': 'item',
66 | 'pages.searchTable.totalServiceCalls': 'Número total de chamadas de serviço',
67 | 'pages.searchTable.tenThousand': '0000',
68 | 'pages.searchTable.batchDeletion': 'deleção em lote',
69 | 'pages.searchTable.batchApproval': 'aprovação em lote',
70 | };
71 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeIcon.tsx:
--------------------------------------------------------------------------------
1 | import { BellOutlined } from '@ant-design/icons';
2 | import { Badge, Spin, Tabs } from 'antd';
3 | import useMergedState from 'rc-util/es/hooks/useMergedState';
4 | import React from 'react';
5 | import classNames from 'classnames';
6 | import type { NoticeIconTabProps } from './NoticeList';
7 | import NoticeList from './NoticeList';
8 | import HeaderDropdown from '../HeaderDropdown';
9 | import styles from './index.less';
10 |
11 | const { TabPane } = Tabs;
12 |
13 | export type NoticeIconProps = {
14 | count?: number;
15 | bell?: React.ReactNode;
16 | className?: string;
17 | loading?: boolean;
18 | onClear?: (tabName: string, tabKey: string) => void;
19 | onItemClick?: (item: API.NoticeIconItem, tabProps: NoticeIconTabProps) => void;
20 | onViewMore?: (tabProps: NoticeIconTabProps, e: MouseEvent) => void;
21 | onTabChange?: (tabTile: string) => void;
22 | style?: React.CSSProperties;
23 | onPopupVisibleChange?: (visible: boolean) => void;
24 | popupVisible?: boolean;
25 | clearText?: string;
26 | viewMoreText?: string;
27 | clearClose?: boolean;
28 | emptyImage?: string;
29 | children?: React.ReactElement[];
30 | };
31 |
32 | const NoticeIcon: React.FC & {
33 | Tab: typeof NoticeList;
34 | } = (props) => {
35 | const getNotificationBox = (): React.ReactNode => {
36 | const {
37 | children,
38 | loading,
39 | onClear,
40 | onTabChange,
41 | onItemClick,
42 | onViewMore,
43 | clearText,
44 | viewMoreText,
45 | } = props;
46 | if (!children) {
47 | return null;
48 | }
49 | const panes: React.ReactNode[] = [];
50 | React.Children.forEach(children, (child: React.ReactElement): void => {
51 | if (!child) {
52 | return;
53 | }
54 | const { list, title, count, tabKey, showClear, showViewMore } = child.props;
55 | const len = list && list.length ? list.length : 0;
56 | const msgCount = count || count === 0 ? count : len;
57 | const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title;
58 | panes.push(
59 |
60 | onClear && onClear(title, tabKey)}
66 | onClick={(item): void => onItemClick && onItemClick(item, child.props)}
67 | onViewMore={(event): void => onViewMore && onViewMore(child.props, event)}
68 | showClear={showClear}
69 | showViewMore={showViewMore}
70 | title={title}
71 | />
72 | ,
73 | );
74 | });
75 | return (
76 | <>
77 |
78 |
79 | {panes}
80 |
81 |
82 | >
83 | );
84 | };
85 |
86 | const { className, count, bell } = props;
87 |
88 | const [visible, setVisible] = useMergedState(false, {
89 | value: props.popupVisible,
90 | onChange: props.onPopupVisibleChange,
91 | });
92 | const noticeButtonClass = classNames(className, styles.noticeButton);
93 | const notificationBox = getNotificationBox();
94 | const NoticeBellIcon = bell || ;
95 | const trigger = (
96 |
97 |
98 | {NoticeBellIcon}
99 |
100 |
101 | );
102 | if (!notificationBox) {
103 | return trigger;
104 | }
105 |
106 | return (
107 |
115 | {trigger}
116 |
117 | );
118 | };
119 |
120 | NoticeIcon.defaultProps = {
121 | emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
122 | };
123 |
124 | NoticeIcon.Tab = NoticeList;
125 |
126 | export default NoticeIcon;
127 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { Tag, message } from 'antd';
3 | import { groupBy } from 'lodash';
4 | import moment from 'moment';
5 | import { useModel } from 'umi';
6 | import { getNotices } from '@/services/ant-design-pro/api';
7 |
8 | import NoticeIcon from './NoticeIcon';
9 | import styles from './index.less';
10 |
11 | export type GlobalHeaderRightProps = {
12 | fetchingNotices?: boolean;
13 | onNoticeVisibleChange?: (visible: boolean) => void;
14 | onNoticeClear?: (tabName?: string) => void;
15 | };
16 |
17 | const getNoticeData = (notices: API.NoticeIconItem[]): Record => {
18 | if (!notices || notices.length === 0 || !Array.isArray(notices)) {
19 | return {};
20 | }
21 |
22 | const newNotices = notices.map((notice) => {
23 | const newNotice = { ...notice };
24 |
25 | if (newNotice.datetime) {
26 | newNotice.datetime = moment(notice.datetime as string).fromNow();
27 | }
28 |
29 | if (newNotice.id) {
30 | newNotice.key = newNotice.id;
31 | }
32 |
33 | if (newNotice.extra && newNotice.status) {
34 | const color = {
35 | todo: '',
36 | processing: 'blue',
37 | urgent: 'red',
38 | doing: 'gold',
39 | }[newNotice.status];
40 | newNotice.extra = (
41 |
47 | {newNotice.extra}
48 |
49 | ) as any;
50 | }
51 |
52 | return newNotice;
53 | });
54 | return groupBy(newNotices, 'type');
55 | };
56 |
57 | const getUnreadData = (noticeData: Record) => {
58 | const unreadMsg: Record = {};
59 | Object.keys(noticeData).forEach((key) => {
60 | const value = noticeData[key];
61 |
62 | if (!unreadMsg[key]) {
63 | unreadMsg[key] = 0;
64 | }
65 |
66 | if (Array.isArray(value)) {
67 | unreadMsg[key] = value.filter((item) => !item.read).length;
68 | }
69 | });
70 | return unreadMsg;
71 | };
72 |
73 | const NoticeIconView = () => {
74 | const { initialState } = useModel('@@initialState');
75 | const { currentUser } = initialState || {};
76 | const [notices, setNotices] = useState([]);
77 |
78 | useEffect(() => {
79 | getNotices().then(({ data }) => setNotices(data || []));
80 | }, []);
81 |
82 | const noticeData = getNoticeData(notices);
83 | const unreadMsg = getUnreadData(noticeData || {});
84 |
85 | const changeReadState = (id: string) => {
86 | setNotices(
87 | notices.map((item) => {
88 | const notice = { ...item };
89 | if (notice.id === id) {
90 | notice.read = true;
91 | }
92 | return notice;
93 | }),
94 | );
95 | };
96 |
97 | const clearReadState = (title: string, key: string) => {
98 | setNotices(
99 | notices.map((item) => {
100 | const notice = { ...item };
101 | if (notice.type === key) {
102 | notice.read = true;
103 | }
104 | return notice;
105 | }),
106 | );
107 | message.success(`${'清空了'} ${title}`);
108 | };
109 |
110 | return (
111 | {
115 | changeReadState(item.id!);
116 | }}
117 | onClear={(title: string, key: string) => clearReadState(title, key)}
118 | loading={false}
119 | clearText="清空"
120 | viewMoreText="查看更多"
121 | onViewMore={() => message.info('Click on view more')}
122 | clearClose
123 | >
124 |
132 |
140 |
148 |
149 | );
150 | };
151 |
152 | export default NoticeIconView;
153 |
--------------------------------------------------------------------------------
/src/services/swagger/pet.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from 'umi';
4 |
5 | /** Update an existing pet PUT /pet */
6 | export async function updatePet(body: API.Pet, options?: { [key: string]: any }) {
7 | return request('/pet', {
8 | method: 'PUT',
9 | headers: {
10 | 'Content-Type': 'application/json',
11 | },
12 | data: body,
13 | ...(options || {}),
14 | });
15 | }
16 |
17 | /** Add a new pet to the store POST /pet */
18 | export async function addPet(body: API.Pet, options?: { [key: string]: any }) {
19 | return request('/pet', {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | data: body,
25 | ...(options || {}),
26 | });
27 | }
28 |
29 | /** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
30 | export async function findPetsByStatus(
31 | params: {
32 | // query
33 | /** Status values that need to be considered for filter */
34 | status: 'available' | 'pending' | 'sold'[];
35 | },
36 | options?: { [key: string]: any },
37 | ) {
38 | return request('/pet/findByStatus', {
39 | method: 'GET',
40 | params: {
41 | ...params,
42 | },
43 |
44 | ...(options || {}),
45 | });
46 | }
47 |
48 | /** Finds Pets by tags Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
49 | export async function findPetsByTags(
50 | params: {
51 | // query
52 | /** Tags to filter by */
53 | tags: string[];
54 | },
55 | options?: { [key: string]: any },
56 | ) {
57 | return request('/pet/findByTags', {
58 | method: 'GET',
59 | params: {
60 | ...params,
61 | },
62 |
63 | ...(options || {}),
64 | });
65 | }
66 |
67 | /** Find pet by ID Returns a single pet GET /pet/${param0} */
68 | export async function getPetById(
69 | params: {
70 | // path
71 | /** ID of pet to return */
72 | petId: number;
73 | },
74 | options?: { [key: string]: any },
75 | ) {
76 | const { petId: param0 } = params;
77 | return request(`/pet/${param0}`, {
78 | method: 'GET',
79 | params: { ...params },
80 |
81 | ...(options || {}),
82 | });
83 | }
84 |
85 | /** Updates a pet in the store with form data POST /pet/${param0} */
86 | export async function updatePetWithForm(
87 | params: {
88 | // path
89 | /** ID of pet that needs to be updated */
90 | petId: number;
91 | },
92 | body: { name?: string; status?: string },
93 | options?: { [key: string]: any },
94 | ) {
95 | const { petId: param0 } = params;
96 | const formData = new FormData();
97 |
98 | Object.keys(body).forEach((ele) => {
99 | const item = (body as any)[ele];
100 |
101 | if (item !== undefined && item !== null) {
102 | formData.append(ele, typeof item === 'object' ? JSON.stringify(item) : item);
103 | }
104 | });
105 |
106 | return request(`/pet/${param0}`, {
107 | method: 'POST',
108 | headers: {
109 | 'Content-Type': 'application/x-www-form-urlencoded',
110 | },
111 | params: { ...params },
112 | data: formData,
113 | ...(options || {}),
114 | });
115 | }
116 |
117 | /** Deletes a pet DELETE /pet/${param0} */
118 | export async function deletePet(
119 | params: {
120 | // header
121 | api_key?: string;
122 | // path
123 | /** Pet id to delete */
124 | petId: number;
125 | },
126 | options?: { [key: string]: any },
127 | ) {
128 | const { petId: param0 } = params;
129 | return request(`/pet/${param0}`, {
130 | method: 'DELETE',
131 | params: { ...params },
132 | ...(options || {}),
133 | });
134 | }
135 |
136 | /** uploads an image POST /pet/${param0}/uploadImage */
137 | export async function uploadFile(
138 | params: {
139 | // path
140 | /** ID of pet to update */
141 | petId: number;
142 | },
143 | body: { additionalMetadata?: string; file?: string },
144 | options?: { [key: string]: any },
145 | ) {
146 | const { petId: param0 } = params;
147 | const formData = new FormData();
148 |
149 | Object.keys(body).forEach((ele) => {
150 | const item = (body as any)[ele];
151 |
152 | if (item !== undefined && item !== null) {
153 | formData.append(ele, typeof item === 'object' ? JSON.stringify(item) : item);
154 | }
155 | });
156 |
157 | return request(`/pet/${param0}/uploadImage`, {
158 | method: 'POST',
159 | headers: {
160 | 'Content-Type': 'multipart/form-data',
161 | },
162 | params: { ...params },
163 | data: formData,
164 | ...(options || {}),
165 | });
166 | }
167 |
--------------------------------------------------------------------------------
/mock/user.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | const waitTime = (time: number = 100) => {
4 | return new Promise((resolve) => {
5 | setTimeout(() => {
6 | resolve(true);
7 | }, time);
8 | });
9 | };
10 |
11 | async function getFakeCaptcha(req: Request, res: Response) {
12 | await waitTime(2000);
13 | return res.json('captcha-xxx');
14 | }
15 |
16 | const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env;
17 |
18 | /**
19 | * 当前用户的权限,如果为空代表没登录
20 | * current user access, if is '', user need login
21 | * 如果是 pro 的预览,默认是有权限的
22 | */
23 | let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : '';
24 |
25 | const getAccess = () => {
26 | return access;
27 | };
28 |
29 | // 代码中会兼容本地 service mock 以及部署站点的静态数据
30 | export default {
31 | // 支持值为 Object 和 Array
32 | 'GET /api/currentUser': (req: Request, res: Response) => {
33 | if (!getAccess()) {
34 | res.status(401).send({
35 | data: {
36 | isLogin: false,
37 | },
38 | errorCode: '401',
39 | errorMessage: '请先登录!',
40 | success: true,
41 | });
42 | return;
43 | }
44 | res.send({
45 | name: 'Serati Ma',
46 | avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
47 | userid: '00000001',
48 | email: 'antdesign@alipay.com',
49 | signature: '海纳百川,有容乃大',
50 | title: '交互专家',
51 | group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
52 | tags: [
53 | {
54 | key: '0',
55 | label: '很有想法的',
56 | },
57 | {
58 | key: '1',
59 | label: '专注设计',
60 | },
61 | {
62 | key: '2',
63 | label: '辣~',
64 | },
65 | {
66 | key: '3',
67 | label: '大长腿',
68 | },
69 | {
70 | key: '4',
71 | label: '川妹子',
72 | },
73 | {
74 | key: '5',
75 | label: '海纳百川',
76 | },
77 | ],
78 | notifyCount: 12,
79 | unreadCount: 11,
80 | country: 'China',
81 | access: getAccess(),
82 | geographic: {
83 | province: {
84 | label: '浙江省',
85 | key: '330000',
86 | },
87 | city: {
88 | label: '杭州市',
89 | key: '330100',
90 | },
91 | },
92 | address: '西湖区工专路 77 号',
93 | phone: '0752-268888888',
94 | });
95 | },
96 | // GET POST 可省略
97 | 'GET /api/users': [
98 | {
99 | key: '1',
100 | name: 'John Brown',
101 | age: 32,
102 | address: 'New York No. 1 Lake Park',
103 | },
104 | {
105 | key: '2',
106 | name: 'Jim Green',
107 | age: 42,
108 | address: 'London No. 1 Lake Park',
109 | },
110 | {
111 | key: '3',
112 | name: 'Joe Black',
113 | age: 32,
114 | address: 'Sidney No. 1 Lake Park',
115 | },
116 | ],
117 | 'POST /api/login/account': async (req: Request, res: Response) => {
118 | const { password, username, type } = req.body;
119 | await waitTime(2000);
120 | if (password === 'ant.design' && username === 'admin') {
121 | res.send({
122 | status: 'ok',
123 | type,
124 | currentAuthority: 'admin',
125 | });
126 | access = 'admin';
127 | return;
128 | }
129 | if (password === 'ant.design' && username === 'user') {
130 | res.send({
131 | status: 'ok',
132 | type,
133 | currentAuthority: 'user',
134 | });
135 | access = 'user';
136 | return;
137 | }
138 | if (type === 'mobile') {
139 | res.send({
140 | status: 'ok',
141 | type,
142 | currentAuthority: 'admin',
143 | });
144 | access = 'admin';
145 | return;
146 | }
147 |
148 | res.send({
149 | status: 'error',
150 | type,
151 | currentAuthority: 'guest',
152 | });
153 | access = 'guest';
154 | },
155 | 'POST /api/login/outLogin': (req: Request, res: Response) => {
156 | access = '';
157 | res.send({ data: {}, success: true });
158 | },
159 | 'POST /api/register': (req: Request, res: Response) => {
160 | res.send({ status: 'ok', currentAuthority: 'user', success: true });
161 | },
162 | 'GET /api/500': (req: Request, res: Response) => {
163 | res.status(500).send({
164 | timestamp: 1513932555104,
165 | status: 500,
166 | error: 'error',
167 | message: 'error',
168 | path: '/base/category/list',
169 | });
170 | },
171 | 'GET /api/404': (req: Request, res: Response) => {
172 | res.status(404).send({
173 | timestamp: 1513932643431,
174 | status: 404,
175 | error: 'Not Found',
176 | message: 'No message available',
177 | path: '/base/category/list/2121212',
178 | });
179 | },
180 | 'GET /api/403': (req: Request, res: Response) => {
181 | res.status(403).send({
182 | timestamp: 1513932555104,
183 | status: 403,
184 | error: 'Unauthorized',
185 | message: 'Unauthorized',
186 | path: '/base/category/list',
187 | });
188 | },
189 | 'GET /api/401': (req: Request, res: Response) => {
190 | res.status(401).send({
191 | timestamp: 1513932555104,
192 | status: 401,
193 | error: 'Unauthorized',
194 | message: 'Unauthorized',
195 | path: '/base/category/list',
196 | });
197 | },
198 |
199 | 'GET /api/login/captcha': getFakeCaptcha,
200 | };
201 |
--------------------------------------------------------------------------------
/mock/listTableList.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | import { Request, Response } from 'express';
3 | import moment from 'moment';
4 | import { parse } from 'url';
5 |
6 | // mock tableListDataSource
7 | const genList = (current: number, pageSize: number) => {
8 | const tableListDataSource: API.RuleListItem[] = [];
9 |
10 | for (let i = 0; i < pageSize; i += 1) {
11 | const index = (current - 1) * 10 + i;
12 | tableListDataSource.push({
13 | key: index,
14 | disabled: i % 6 === 0,
15 | href: 'https://ant.design',
16 | avatar: [
17 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
18 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
19 | ][i % 2],
20 | name: `TradeCode ${index}`,
21 | owner: '曲丽丽',
22 | desc: '这是一段描述',
23 | callNo: Math.floor(Math.random() * 1000),
24 | status: Math.floor(Math.random() * 10) % 4,
25 | updatedAt: moment().format('YYYY-MM-DD'),
26 | createdAt: moment().format('YYYY-MM-DD'),
27 | progress: Math.ceil(Math.random() * 100),
28 | });
29 | }
30 | tableListDataSource.reverse();
31 | return tableListDataSource;
32 | };
33 |
34 | let tableListDataSource = genList(1, 100);
35 |
36 | function getRule(req: Request, res: Response, u: string) {
37 | let realUrl = u;
38 | if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
39 | realUrl = req.url;
40 | }
41 | const { current = 1, pageSize = 10 } = req.query;
42 | const params = (parse(realUrl, true).query as unknown) as API.PageParams &
43 | API.RuleListItem & {
44 | sorter: any;
45 | filter: any;
46 | };
47 |
48 | let dataSource = [...tableListDataSource].slice(
49 | ((current as number) - 1) * (pageSize as number),
50 | (current as number) * (pageSize as number),
51 | );
52 | const sorter = JSON.parse(params.sorter || ('{}' as any));
53 | if (sorter) {
54 | dataSource = dataSource.sort((prev, next) => {
55 | let sortNumber = 0;
56 | Object.keys(sorter).forEach((key) => {
57 | if (sorter[key] === 'descend') {
58 | if (prev[key] - next[key] > 0) {
59 | sortNumber += -1;
60 | } else {
61 | sortNumber += 1;
62 | }
63 | return;
64 | }
65 | if (prev[key] - next[key] > 0) {
66 | sortNumber += 1;
67 | } else {
68 | sortNumber += -1;
69 | }
70 | });
71 | return sortNumber;
72 | });
73 | }
74 | if (params.filter) {
75 | const filter = JSON.parse(params.filter as any) as {
76 | [key: string]: string[];
77 | };
78 | if (Object.keys(filter).length > 0) {
79 | dataSource = dataSource.filter((item) => {
80 | return Object.keys(filter).some((key) => {
81 | if (!filter[key]) {
82 | return true;
83 | }
84 | if (filter[key].includes(`${item[key]}`)) {
85 | return true;
86 | }
87 | return false;
88 | });
89 | });
90 | }
91 | }
92 |
93 | if (params.name) {
94 | dataSource = dataSource.filter((data) => data?.name?.includes(params.name || ''));
95 | }
96 | const result = {
97 | data: dataSource,
98 | total: tableListDataSource.length,
99 | success: true,
100 | pageSize,
101 | current: parseInt(`${params.current}`, 10) || 1,
102 | };
103 |
104 | return res.json(result);
105 | }
106 |
107 | function postRule(req: Request, res: Response, u: string, b: Request) {
108 | let realUrl = u;
109 | if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
110 | realUrl = req.url;
111 | }
112 |
113 | const body = (b && b.body) || req.body;
114 | const { method, name, desc, key } = body;
115 |
116 | switch (method) {
117 | /* eslint no-case-declarations:0 */
118 | case 'delete':
119 | tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1);
120 | break;
121 | case 'post':
122 | (() => {
123 | const i = Math.ceil(Math.random() * 10000);
124 | const newRule: API.RuleListItem = {
125 | key: tableListDataSource.length,
126 | href: 'https://ant.design',
127 | avatar: [
128 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
129 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
130 | ][i % 2],
131 | name,
132 | owner: '曲丽丽',
133 | desc,
134 | callNo: Math.floor(Math.random() * 1000),
135 | status: Math.floor(Math.random() * 10) % 2,
136 | updatedAt: moment().format('YYYY-MM-DD'),
137 | createdAt: moment().format('YYYY-MM-DD'),
138 | progress: Math.ceil(Math.random() * 100),
139 | };
140 | tableListDataSource.unshift(newRule);
141 | return res.json(newRule);
142 | })();
143 | return;
144 |
145 | case 'update':
146 | (() => {
147 | let newRule = {};
148 | tableListDataSource = tableListDataSource.map((item) => {
149 | if (item.key === key) {
150 | newRule = { ...item, desc, name };
151 | return { ...item, desc, name };
152 | }
153 | return item;
154 | });
155 | return res.json(newRule);
156 | })();
157 | return;
158 | default:
159 | break;
160 | }
161 |
162 | const result = {
163 | list: tableListDataSource,
164 | pagination: {
165 | total: tableListDataSource.length,
166 | },
167 | };
168 |
169 | res.json(result);
170 | }
171 |
172 | export default {
173 | 'GET /api/rule': getRule,
174 | 'POST /api/rule': postRule,
175 | };
176 |
--------------------------------------------------------------------------------
/src/pages/editProTable/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import type { ProColumns } from '@ant-design/pro-table';
3 | import { EditableProTable } from '@ant-design/pro-table';
4 | import ProField from '@ant-design/pro-field';
5 | import ProCard from '@ant-design/pro-card';
6 | import { Button, Form } from 'antd';
7 |
8 | type DataSourceType = {
9 | id: React.Key;
10 | title?: string;
11 | decs?: string;
12 | state?: string;
13 | created_at?: string;
14 | fileList?: any[]
15 | children?: DataSourceType[];
16 | };
17 |
18 | const defaultData: DataSourceType[] = [
19 | {
20 | id: 624748504,
21 | title: '活动名称一',
22 | decs: '这个活动真好玩',
23 | state: 'open',
24 | created_at: '2020-05-26T09:42:56Z',
25 | fileList: [{
26 | fileName: '这是一个测试图片',
27 | url: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2109030888,3411151480&fm=26&gp=0.jpg'
28 | }]
29 | },
30 | ];
31 |
32 | const EditTable: React.Fc = () => {
33 | const [editableKeys, setEditableRowKeys] = useState(() =>
34 | defaultData.map((item) => item.id),
35 | );
36 | const [dataSource, setDataSource] = useState(() => defaultData);
37 |
38 | const [ editForm ] = Form.useForm()
39 |
40 | const getTableInfo = () => {
41 | const newData = [
42 | {
43 | id: 624748504,
44 | title: '活动名称一',
45 | decs: '这个活动真好玩',
46 | state: 'open',
47 | created_at: '2020-05-26T09:42:56Z',
48 | fileList: [{
49 | uid: '1',
50 | fileName: '这是一个测试图片',
51 | url: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2109030888,3411151480&fm=26&gp=0.jpg'
52 | }]
53 | },
54 | {
55 | id: 624691229,
56 | title: '',
57 | decs: '这个活动真好玩',
58 | state: 'closed',
59 | created_at: '2020-05-26T08:19:22Z',
60 | fileList: []
61 | },
62 | ]
63 | setEditableRowKeys(newData.map(item => item.id))
64 | setDataSource(newData)
65 | }
66 |
67 | useEffect(() => {
68 | getTableInfo()
69 | }, [])
70 | const columns: ProColumns[] = [
71 | {
72 | title: '活动名称',
73 | dataIndex: 'title',
74 | width: '30%',
75 | formItemProps: {
76 | rules: [
77 | {
78 | required: true,
79 | whitespace: true,
80 | message: '此项是必填项',
81 | },
82 | {
83 | max: 16,
84 | whitespace: true,
85 | message: '最长为 16 位',
86 | },
87 | {
88 | min: 2,
89 | whitespace: true,
90 | message: '最小为 2 位',
91 | },
92 | ],
93 | },
94 | },
95 | {
96 | title: '状态',
97 | key: 'state',
98 | dataIndex: 'state',
99 | valueType: 'select',
100 | formItemProps: {
101 | rules: [
102 | {
103 | required: true,
104 | whitespace: true,
105 | message: '此项是必选项',
106 | },
107 | ]
108 | },
109 | valueEnum: {
110 | all: { text: '全部', status: 'Default' },
111 | open: {
112 | text: '未解决',
113 | status: 'Error',
114 | },
115 | closed: {
116 | text: '已解决',
117 | status: 'Success',
118 | },
119 | },
120 | },
121 | {
122 | title: '描述',
123 | dataIndex: 'decs',
124 | },
125 | {
126 | title: '文件',
127 | dataIndex: 'fileList',
128 | editable: false,
129 | render: (val: any) => {
130 | return
131 | {val !== '-' && val.length ? val.map(item =>
132 |
133 |

134 |
) : ''}
135 |
136 | }
137 | },
138 | {
139 | title: '操作',
140 | valueType: 'option',
141 | width: 250,
142 | render: () => {
143 | return null;
144 | },
145 | },
146 | ];
147 |
148 |
149 | return
150 |
151 | headerTitle="可编辑表格"
152 | columns={columns}
153 | rowKey="id"
154 | value={dataSource}
155 | onChange={setDataSource}
156 | recordCreatorProps={{
157 | newRecordType: 'dataSource',
158 | record: () => ({
159 | id: Date.now(),
160 | }),
161 | }}
162 | toolBarRender={() => {
163 | return [
164 | ,
175 | ];
176 | }}
177 | editable={{
178 | type: 'multiple',
179 | editableKeys,
180 | form: editForm,
181 | actionRender: (row, config, defaultDoms) => {
182 | return [defaultDoms.delete];
183 | },
184 | onValuesChange: (record, recordList) => {
185 | setDataSource(recordList);
186 | },
187 | onChange: setEditableRowKeys,
188 | }}
189 | />
190 |
191 |
201 |
202 |
203 | }
204 |
205 | export default EditTable
--------------------------------------------------------------------------------