├── 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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /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 | 77 | {menu && ( 78 | 79 | 80 | 个人中心 81 | 82 | )} 83 | {menu && ( 84 | 85 | 86 | 个人设置 87 | 88 | )} 89 | {menu && } 90 | 91 | 92 | 93 | 退出登录 94 | 95 | 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 | not found 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 | Group 28 Copy 5Created with Sketch. -------------------------------------------------------------------------------- /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 |
70 | 71 | 72 | 76 | 95 | 96 | 97 | 98 | 102 | 103 | 104 | 105 | 106 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 120 |