9b990809 by charles

feat: 项目初始化

0 parents
Showing 150 changed files with 4676 additions and 0 deletions
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
1 /lambda/
2 /scripts
3 /config
4 .history
...\ No newline at end of file ...\ No newline at end of file
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 };
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 /dist
10
11 # production
12 /.vscode
13
14 # misc
15 .DS_Store
16 npm-debug.log*
17 yarn-error.log
18
19 /coverage
20 .idea
21 yarn.lock
22 package-lock.json
23 *bak
24 .vscode
25
26 # visual studio code
27 .history
28 *.log
29 functions/*
30 .temp/**
31
32 # umi
33 .umi
34 .umi-production
35
36 # screenshot
37 screenshot
38 .firebase
39 .eslintcache
40
41 build
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
...\ No newline at end of file ...\ No newline at end of file
1 const fabric = require("@umijs/fabric");
2
3 module.exports = {
4 ...fabric.prettier
5 };
1 const fabric = require('@umijs/fabric');
2
3 module.exports = {
4 ...fabric.stylelint,
5 };
1 # 项目名称
2
3 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).
1 import defaultSettings from './defaultSettings'; // https://umijs.org/config/
2 import slash from 'slash2';
3 import routes from './routerConfig';
4 import proxy from './proxy';
5 const { pwa, primaryColor } = defaultSettings; // preview.pro.ant.design only do not use in your production ;
6 // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
7 const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV } = process.env;
8 import { defineConfig, utils } from 'umi';
9
10 const { winPath } = utils;
11 export default defineConfig({
12 antd: {},
13 dva: {
14 hmr: true,
15 },
16 locale: {
17 default: 'zh-CN',
18 baseNavigator: true,
19 },
20 dynamicImport: {
21 // 无需 level, webpackChunkName 配置
22 // loadingComponent: './components/PageLoading/index'
23 loading: '@/components/PageLoading/index',
24 },
25 hash: true,
26 targets: {
27 ie: 11,
28 },
29 title: false,
30 // umi routes: https://umijs.org/zh/guide/router.html
31 routes: routes,
32 // Theme for antd: https://ant.design/docs/react/customize-theme-cn
33 theme: {
34 // ...darkTheme,
35 'primary-color': primaryColor,
36 },
37 define: {
38 REACT_APP_ENV: REACT_APP_ENV || false,
39 ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION:
40 ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION || '', // preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
41 },
42 ignoreMomentLocale: true,
43 lessLoader: {
44 javascriptEnabled: true,
45 },
46 cssLoader: {
47 modules: {
48 getLocalIdent: (context, _, localName) => {
49 if (
50 context.resourcePath.includes('node_modules') ||
51 context.resourcePath.includes('ant.design.pro.less') ||
52 context.resourcePath.includes('global.less')
53 ) {
54 return localName;
55 }
56
57 const match = context.resourcePath.match(/src(.*)/);
58
59 if (match && match[1]) {
60 const antdProPath = match[1].replace('.less', '');
61 const arr = slash(antdProPath)
62 .split('/')
63 .map(a => a.replace(/([A-Z])/g, '-$1'))
64 .map(a => a.toLowerCase());
65 return `antd-pro${arr.join('-')}-${localName}`.replace(/--/g, '-');
66 }
67
68 return localName;
69 },
70 },
71 },
72 manifest: {
73 basePath: '/',
74 },
75 proxy: proxy[REACT_APP_ENV || 'dev'],
76 externals: {
77 react: 'React',
78 'react-dom': 'ReactDOM',
79 bizcharts: 'BizCharts',
80 mockjs: 'Mock',
81 '@antv/data-set': 'DataSet',
82 },
83 });
1 import { routeMaps } from './routerConfig';
2 export default {
3 navTheme: 'dark',
4 primaryColor: '#185DA2',
5 layout: 'sidemenu',
6 contentWidth: 'Fluid',
7 fixedHeader: false,
8 autoHideHeader: false,
9 fixSiderbar: false,
10 colorWeak: false,
11 menu: {
12 locale: true,
13 },
14 title: '一网打“净”数智在线',
15 pwa: false,
16 iconfontUrl: '',
17 //缓存token的key
18 tokenKey: 'yh_token',
19 //md5的key
20 md5Key: 'QHZT',
21 // 路由对象
22 routeMaps,
23 };
1 import path from 'path';
2
3 function getModulePackageName(module) {
4 if (!module.context) return null;
5 const nodeModulesPath = path.join(__dirname, '../node_modules/');
6
7 if (module.context.substring(0, nodeModulesPath.length) !== nodeModulesPath) {
8 return null;
9 }
10
11 const moduleRelativePath = module.context.substring(nodeModulesPath.length);
12 const [moduleDirName] = moduleRelativePath.split(path.sep);
13 let packageName = moduleDirName; // handle tree shaking
14
15 if (packageName && packageName.match('^_')) {
16 // eslint-disable-next-line prefer-destructuring
17 packageName = packageName.match(/^_(@?[^@]+)/)[1];
18 }
19
20 return packageName;
21 }
22
23 export const webpackPlugin = config => {
24 // optimize chunks
25 config.optimization // share the same chunks across different modules
26 .runtimeChunk(false)
27 .splitChunks({
28 chunks: 'async',
29 name: 'vendors',
30 maxInitialRequests: Infinity,
31 minSize: 0,
32 cacheGroups: {
33 vendors: {
34 test: module => {
35 const packageName = getModulePackageName(module) || '';
36
37 if (packageName) {
38 return [
39 'bizcharts',
40 'gg-editor',
41 'g6',
42 '@antv',
43 'gg-editor-core',
44 'bizcharts-plugin-slider',
45 ].includes(packageName);
46 }
47
48 return false;
49 },
50
51 name(module) {
52 const packageName = getModulePackageName(module);
53
54 if (packageName) {
55 if (['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0) {
56 return 'viz'; // visualization package
57 }
58 }
59
60 return 'misc';
61 },
62 },
63 },
64 });
65 };
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 '/ql/api/': {
11 target: 'https://yh2.jimilicai.com',
12 changeOrigin: true,
13 pathRewrite: {
14 '^': '',
15 },
16 },
17 },
18 };
1 export default {
2 path: '/',
3 name: 'appliction',
4 component: '../layouts/BasicLayout',
5 routes: [
6 { path: '/', redirect: '/screen' },
7 {
8 path: '/screen',
9 name: 'dataScreen',
10 component: './Screen',
11 icons: 'screen',
12 resourceCode: 'Home',
13 },
14 {
15 path: '/risk',
16 name: 'risk',
17 component: './Risk',
18 icons: 'risk',
19 resourceCode: 'RiskManage',
20 },
21 {
22 path: '/event',
23 name: 'event',
24 component: './Incident',
25 icons: 'event',
26 resourceCode: 'EventManage',
27 },
28 {
29 path: '/data',
30 name: 'data',
31 component: './Data',
32 icons: 'data',
33 resourceCode: 'DataManage',
34 },
35 {
36 path: '/system',
37 name: 'system',
38 icons: 'system',
39
40 resourceCode: 'SystemManage',
41 routes: [
42 {
43 path: '/system/account',
44 name: 'account',
45 component: './System/Account',
46 icons: '',
47 resourceCode: 'AccountManage',
48 },
49 {
50 path: '/system/role',
51 name: 'role',
52 component: './System/Role',
53 icons: '',
54 resourceCode: 'RoleManage',
55 },
56 {
57 path: '/system/menu',
58 name: 'menu',
59 component: './System/Menu',
60 icons: '',
61 resourceCode: 'MenuManage',
62 },
63 ],
64 },
65 {
66 component: './404',
67 resourceCode: '404',
68 },
69 ],
70 };
1 import user from './user';
2 import app from './app';
3 import { cloneDeep } from 'lodash';
4
5 const getRoutes = (routes, maps = {}) => {
6 routes = routes.map(item => {
7 if (item.auths) {
8 maps[item.path] = item.auths;
9 }
10 if (item.routes) {
11 item.routes = getRoutes(item.routes, maps).routes;
12 }
13 return item;
14 });
15 routes.push({
16 component: '../pages/404',
17 });
18 return {
19 routes,
20 maps,
21 };
22 };
23
24 const { routes, maps } = getRoutes([user, app]);
25
26 export const routeMaps = maps;
27
28 export const codeMappingForResource = (() => {
29 let menu = cloneDeep([app]);
30 let codeMapping = new Map();
31 const getItem = arr => {
32 arr.forEach(item => {
33 codeMapping.set(item?.resourceCode, item);
34 if (item.routes) {
35 getItem(item.routes);
36 }
37 });
38 };
39 getItem(menu);
40 return codeMapping;
41 })();
42
43 export default routes;
1 export default {
2 path: '/user',
3 component: '../layouts/UserLayout',
4 routes: [
5 { path: '/user', name: 'login', redirect: '/user/login' },
6 { path: '/user/login', name: 'login', component: './User/Login' },
7 { path: '/user/risk', name: 'login', component: './User/Risk' },
8 ],
9 };
1 export default {
2 theme: [
3 {
4 key: 'dark',
5 fileName: 'dark.css',
6 theme: 'dark',
7 },
8 {
9 key: 'dust',
10 fileName: 'dust.css',
11 modifyVars: {
12 '@primary-color': '#F5222D',
13 },
14 },
15 {
16 key: 'volcano',
17 fileName: 'volcano.css',
18 modifyVars: {
19 '@primary-color': '#FA541C',
20 },
21 },
22 {
23 key: 'sunset',
24 fileName: 'sunset.css',
25 modifyVars: {
26 '@primary-color': '#FAAD14',
27 },
28 },
29 {
30 key: 'cyan',
31 fileName: 'cyan.css',
32 modifyVars: {
33 '@primary-color': '#13C2C2',
34 },
35 },
36 {
37 key: 'green',
38 fileName: 'green.css',
39 modifyVars: {
40 '@primary-color': '#52C41A',
41 },
42 },
43 {
44 key: 'geekblue',
45 fileName: 'geekblue.css',
46 modifyVars: {
47 '@primary-color': '#2F54EB',
48 },
49 },
50 {
51 key: 'purple',
52 fileName: 'purple.css',
53 modifyVars: {
54 '@primary-color': '#722ED1',
55 },
56 },
57 {
58 key: 'dust',
59 theme: 'dark',
60 fileName: 'dark-dust.css',
61 modifyVars: {
62 '@primary-color': '#F5222D',
63 },
64 },
65 {
66 key: 'volcano',
67 theme: 'dark',
68 fileName: 'dark-volcano.css',
69 modifyVars: {
70 '@primary-color': '#FA541C',
71 },
72 },
73 {
74 key: 'sunset',
75 theme: 'dark',
76 fileName: 'dark-sunset.css',
77 modifyVars: {
78 '@primary-color': '#FAAD14',
79 },
80 },
81 {
82 key: 'cyan',
83 theme: 'dark',
84 fileName: 'dark-cyan.css',
85 modifyVars: {
86 '@primary-color': '#13C2C2',
87 },
88 },
89 {
90 key: 'green',
91 theme: 'dark',
92 fileName: 'dark-green.css',
93 modifyVars: {
94 '@primary-color': '#52C41A',
95 },
96 },
97 {
98 key: 'geekblue',
99 theme: 'dark',
100 fileName: 'dark-geekblue.css',
101 modifyVars: {
102 '@primary-color': '#2F54EB',
103 },
104 },
105 {
106 key: 'purple',
107 theme: 'dark',
108 fileName: 'dark-purple.css',
109 modifyVars: {
110 '@primary-color': '#722ED1',
111 },
112 },
113 ],
114 };
1 // ps https://github.com/GoogleChrome/puppeteer/issues/3120
2 module.exports = {
3 launch: {
4 args: [
5 '--disable-gpu',
6 '--disable-dev-shm-usage',
7 '--no-first-run',
8 '--no-zygote',
9 '--no-sandbox',
10 ],
11 },
12 };
1 module.exports = {
2 testURL: 'http://localhost:8000',
3 preset: 'jest-puppeteer',
4 extraSetupFiles: ['./tests/setupTests.js'],
5 globals: {
6 ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
7 localStorage: null,
8 },
9 };
1 {
2 "compilerOptions": {
3 "emitDecoratorMetadata": true,
4 "experimentalDecorators": true,
5 "baseUrl": ".",
6 "paths": {
7 "@/*": ["./src/*"]
8 }
9 }
10 }
1 {
2 "name": "ant-design-pro",
3 "version": "1.0.0",
4 "private": true,
5 "description": "An out-of-box UI solution for enterprise applications",
6 "scripts": {
7 "analyze": "cross-env ANALYZE=1 umi build",
8 "build": "umi build",
9 "deploy": "npm run site && npm run gh-pages",
10 "dev": "npm run start:dev",
11 "fetch:blocks": "pro fetch-blocks --branch antd@4 && npm run prettier",
12 "format-imports": "cross-env import-sort --write '**/*.{js,jsx,ts,tsx}'",
13 "gh-pages": "cp CNAME ./dist/ && gh-pages -d dist",
14 "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
15 "lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
16 "lint-staged": "lint-staged",
17 "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
18 "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
19 "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
20 "lint:prettier": "check-prettier lint",
21 "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
22 "prettier": "prettier -c --write \"**/*\"",
23 "start": "umi dev",
24 "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
25 "start:no-mock": "cross-env MOCK=none umi dev",
26 "start:no-ui": "cross-env UMI_UI=none umi dev",
27 "start:pre": "cross-env REACT_APP_ENV=pre umi dev",
28 "start:test": "cross-env REACT_APP_ENV=test MOCK=none umi dev",
29 "test": "umi test",
30 "test:all": "node ./tests/run-tests.js",
31 "test:component": "umi test ./src/components",
32 "tsc": "tsc",
33 "ui": "umi ui"
34 },
35 "lint-staged": {
36 "**/*.less": "stylelint --syntax less",
37 "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
38 "**/*.{js,jsx,tsx,ts,less,md,json}": [
39 "prettier --write"
40 ]
41 },
42 "browserslist": [
43 "> 1%",
44 "last 2 versions",
45 "not ie <= 10"
46 ],
47 "dependencies": {
48 "@ant-design/icons": "^4.0.0-rc.0",
49 "@ant-design/pro-layout": "^5.0.3",
50 "@antv/data-set": "^0.10.2",
51 "@umijs/preset-react": "^1.2.2",
52 "antd": "4.23.1",
53 "bizcharts": "^3.5.7",
54 "blueimp-md5": "^2.16.0",
55 "classnames": "^2.2.6",
56 "crypto-js": "^4.1.1",
57 "lodash": "^4.17.21",
58 "moment": "^2.24.0",
59 "omit.js": "^1.0.2",
60 "path-to-regexp": "2.4.0",
61 "qs": "^6.11.0",
62 "react": "^16.8.6",
63 "react-copy-to-clipboard": "^5.0.2",
64 "react-dom": "^16.8.6",
65 "react-helmet": "^5.2.1",
66 "react-redux": "^7.2.0",
67 "redux": "^4.0.1",
68 "styled-components": "^5.3.5",
69 "umi": "^3.0.0",
70 "umi-request": "^1.2.19",
71 "use-merge-value": "^1.0.1"
72 },
73 "devDependencies": {
74 "@ant-design/pro-cli": "^1.0.18",
75 "@types/classnames": "^2.2.7",
76 "@types/express": "^4.17.0",
77 "@types/history": "^4.7.2",
78 "@types/jest": "^25.1.0",
79 "@types/lodash": "^4.14.144",
80 "@types/qs": "^6.5.3",
81 "@types/react": "^16.9.17",
82 "@types/react-dom": "^16.8.4",
83 "@types/react-helmet": "^5.0.13",
84 "@umijs/fabric": "^2.0.2",
85 "chalk": "^3.0.0",
86 "check-prettier": "^1.0.3",
87 "cross-env": "^7.0.0",
88 "cross-port-killer": "^1.1.1",
89 "enzyme": "^3.11.0",
90 "express": "^4.17.1",
91 "gh-pages": "^2.0.1",
92 "husky": "^4.0.7",
93 "import-sort-cli": "^6.0.0",
94 "import-sort-parser-babylon": "^6.0.0",
95 "import-sort-parser-typescript": "^6.0.0",
96 "import-sort-style-module": "^6.0.0",
97 "jest-puppeteer": "^4.4.0",
98 "jsdom-global": "^3.0.2",
99 "lint-staged": "^10.0.0",
100 "mockjs": "^1.0.1-beta3",
101 "node-fetch": "^2.6.0",
102 "prettier": "^1.19.1",
103 "pro-download": "1.0.1",
104 "stylelint": "^13.0.0"
105 },
106 "optionalDependencies": {
107 "puppeteer": "^2.0.0"
108 },
109 "engines": {
110 "node": ">=10.0.0"
111 },
112 "checkFiles": [
113 "src/**/*.js*",
114 "src/**/*.ts*",
115 "src/**/*.less",
116 "config/**/*.js*",
117 "scripts/**/*.js"
118 ]
119 }
No preview for this file type
1 import { createFromIconfontCN } from '@ant-design/icons';
2
3 /* 接口域名 */
4 const domain = window.location.hostname.toLowerCase();
5 export const urlConfig = {
6 'middletest.lipinclub.com': {
7 URL_API: 'http://middletest.lipinclub.com/api/',
8 },
9 'middle.lipinclub.com': {
10 URL_API: 'http://middle.lipinclub.com/api/',
11 },
12 }[domain] || {
13 URL_API: '/ql/api',
14 };
15
16 // 七牛云图片域名
17 export const IMG_URL = 'http://qn.qinwell.com/';
18
19 /* 自定义icon的地址 */
20 export const IconFontConfig = createFromIconfontCN({
21 scriptUrl: '//at.alicdn.com/t/font_1948876_xye4vujgq2j.js',
22 });
1 /**
2 * Auther: APIS
3 */
4 import React, { useEffect, useState } from 'react';
5 import { connect } from 'umi';
6 const AuthBlock = props => {
7 const { auth, userAuths, children } = props;
8
9 if (userAuths.includes(auth) || !auth) {
10 return <>{children}</>;
11 } else {
12 return null;
13 }
14 };
15 export default connect(({ user }) => {
16 return {
17 userAuths: user.userAuths,
18 };
19 })(AuthBlock);
1 /**
2 * Auther: APIS
3 */
4 import React, { useEffect, useState } from 'react';
5 import { connect } from 'umi';
6 import NoFoundPage from '@/pages/404';
7
8 /**
9 * 路由权限控制
10 * 路由权限控制需要在路由配置里面对应的路由设置'auths'字段即可,支持字符串和数组,不需要加权限的路由不设置即可。
11 * 原则上一个页面允许存在多个权限code,但是不允许一个code对应多个页面
12 * @param {*} props
13 */
14 const AuthRouter = props=> {
15 const { userAuths, routeMaps, path, children } = props;
16
17 const currentAuths = routeMaps[path];
18
19 let hasAuth = true;
20 if (currentAuths) {
21 if (Array.isArray(currentAuths)) {
22 hasAuth = currentAuths.some(item=> userAuths.includes(item));
23 } else {
24 hasAuth = userAuths.includes(currentAuths);
25 }
26 }
27
28 return <>{hasAuth ? children : <NoFoundPage />}</>;
29 };
30 export default connect(({user, settings})=> {
31 return {
32 userAuths: user.userAuths,
33 routeMaps: settings.routeMaps
34 }
35 })(AuthRouter);
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * Auther: APIS
3 */
4 import React, { useEffect, useState } from 'react';
5 import { message} from 'antd';
6 import { CopyToClipboard } from 'react-copy-to-clipboard';
7
8 const style = {
9 cursor: 'pointer'
10 };
11
12 const Template = props=> {
13 const { text, children } = props;
14
15 useEffect(()=> {
16 }, [text]);
17
18 const onCopy = (test, result)=> {
19 if (result) {
20 message.success('已复制到剪切板~');
21 } else {
22 message.error('复制失败,请手动进行复制~');
23 }
24 }
25
26 return (
27 <CopyToClipboard text={text} onCopy={onCopy} style={style} title='点击可复制'>
28 {children}
29 </CopyToClipboard>
30 );
31 };
32 export default Template;
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * Auther: APIS
3 */
4 import React, { useEffect, useState } from 'react';
5
6 const FileExport = props=> {
7 const { url, dispatch } = props;
8
9 useEffect(()=> {
10 const linkExport = document.getElementById('J_HandleExport');
11 if (url && linkExport) {
12 linkExport.click();
13 setTimeout(()=> {
14 dispatch({type: 'global/changeState', payload: { urlFileExport: '' }});
15 }, 100);
16 }
17 }, [url]);
18
19 return <a href={url} download id="J_HandleExport"></a>
20 };
21 export default FileExport;
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * 省市区formItem组件
3 * @param {*省市区的标签的文本} label
4 * @param {*省市区的标签的字段名} name
5 * @param {*详细地址的标签的文本} alabel
6 * @param {*详细地址的字段名} aname
7 */
8 import React, { useState, useEffect, forwardRef } from 'react';
9 import { connect } from 'dva';
10 import { Card, Button, Row, Col, Input, Popover, Form, Cascader, Select } from 'antd';
11 import InputFormItem from '@/components/FormItems/InputFormItem';
12 const { TextArea } = Input;
13 const { Option } = Select;
14 const CitySelects = forwardRef((props, ref) => {
15 const { value = {}, onChange, options = [], disabled } = props;
16 const { province, city, district } = value;
17 const [provinceData, setprovinceData] = useState([]);
18 const [cityData, setcityData] = useState([]);
19 const [districtData, setdistrictData] = useState([]);
20 useEffect(() => {
21 if (options.length > 0) {
22 if (province) {
23 const plists = provinceData.filter(_ => _.label === province);
24 setcityData(plists[0].children);
25 }
26 }
27 }, [province]);
28 useEffect(() => {
29 if (city && cityData.length > 0) {
30 const cityLists = cityData.filter(_ => _.label === city);
31 setdistrictData(cityLists[0].children);
32 }
33 }, [city]);
34
35 useEffect(() => {
36 if (cityData.length > 0) {
37 const cityLists = cityData.filter(_ => _.label === city);
38
39 setdistrictData(cityLists.length > 0 ? cityLists[0].children : []);
40 }
41 }, [cityData]);
42
43 useEffect(() => {
44 setprovinceData(options);
45 }, [options]);
46
47 const handleProvinceChange = province => {
48 const provinceLists = provinceData.filter(_ => _.label === province);
49 setcityData(provinceLists[0].children);
50 onChange({
51 ...value,
52 province,
53 city: '',
54 });
55 };
56 const onSecondCityChange = city => {
57 const cityLists = cityData.filter(_ => _.label === city);
58 setdistrictData(cityLists[0].children);
59 onChange({
60 ...value,
61 city,
62 district: '',
63 });
64 };
65 const onDistrictChange = district => {
66 onChange({
67 ...value,
68 district,
69 });
70 };
71 return (
72 <>
73 <Select
74 className="mr-10"
75 value={province}
76 style={{ width: 150 }}
77 onChange={handleProvinceChange}
78 placeholder="请选择省份"
79 disabled={disabled}
80 >
81 {provinceData.map(province => (
82 <Option key={province.label}>{province.label}</Option>
83 ))}
84 </Select>
85 <Select
86 className="mr-10"
87 value={city}
88 style={{ width: 150 }}
89 onChange={onSecondCityChange}
90 placeholder="请选择城市"
91 disabled={disabled}
92 >
93 {cityData.map(city => (
94 <Option key={city.label}>{city.label}</Option>
95 ))}
96 </Select>
97 <Select
98 value={district}
99 style={{ width: 150 }}
100 onChange={onDistrictChange}
101 placeholder="请选择区县"
102 disabled={disabled}
103 >
104 {districtData.map(district => (
105 <Option key={district.label}>{district.label}</Option>
106 ))}
107 </Select>
108 </>
109 );
110 });
111
112 const FormItem = props => {
113 const {
114 name,
115 label,
116 alabel,
117 aname,
118 wrapperCol = { span: 10 },
119 BaseProvinceList,
120 dispatch,
121 form,
122 disabled,
123 } = props;
124 const validator = (rule, value = {}) => {
125 const { province, city, district } = value;
126 if (!province && !city && !district) {
127 return Promise.reject('请选择省市区!');
128 } else if (!province) {
129 return Promise.reject('请选择省份!');
130 } else if (!city) {
131 return Promise.reject('请选择城市!');
132 } else if (!district) {
133 return Promise.reject('请选择区县!');
134 } else {
135 return Promise.resolve();
136 }
137 };
138 return (
139 <>
140 <Form.Item
141 label={label}
142 name={name}
143 required
144 rules={[
145 {
146 validator,
147 },
148 ]}
149 >
150 <CitySelects disabled={disabled} options={BaseProvinceList} />
151 </Form.Item>
152 <InputFormItem disabled={disabled} label={alabel} name={aname} max={25} />
153 </>
154 );
155 };
156 const mapStateToProps = ({ global }) => {
157 return {
158 BaseProvinceList: global.BaseProvinceList,
159 };
160 };
161 export default connect(mapStateToProps)(FormItem);
1 /**
2 * Author: wjw
3 * Date:
4 * Description:
5 */
6 import React, { useState, useEffect, forwardRef } from 'react';
7 import { connect } from 'dva';
8 import { Card, Button, Row, Col, Input } from 'antd';
9 import styles from './index.less';
10 const { TextArea } = Input;
11 const InputCom = forwardRef((props, ref) => {
12 const { value, onChange, placeholder, max, type = 'input', style, disabled, defaultLen = 0 } = props;
13 const onIptChange = ({ target: { value } }) => {
14 if (onChange) {
15 onChange(value);
16 }
17 };
18 const suffix = (
19 <span style={{ lineHeight: '20px' }}>
20 {(value) ? (value.length + defaultLen) : ((!value && defaultLen) ? defaultLen : 0) }/{!defaultLen ? max : 500}
21 </span>
22 );
23 const params = {
24 autoComplete: 'off',
25 maxLength: max,
26 placeholder,
27 value,
28 style,
29 onChange: onIptChange,
30 disabled,
31 };
32 return (
33 <div>
34 {type === 'input' && <Input {...params} suffix={suffix} />}
35 {type === 'textArea' && (
36 <div style={{ position: 'relative' }}>
37 <TextArea {...params} rows={4} />
38 <div className={styles.suffixttextarea}>{suffix}</div>
39 </div>
40 )}
41 </div>
42 );
43 });
44
45 export default InputCom;
1 .suffixttextarea {
2 position: absolute;
3 bottom : 5px;
4 right : 10px;
5 }
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * 带有长度统计formItem组件
3 * @param {*标签的文本} label
4 * @param {*字段名} name
5 * @param {*占位符} placeholder
6 * @param {*最小长度} min
7 * @param {*最大长度} max
8 * @param {*input框类型input、textArea} type
9 * @param {*规则} rules
10 * @param {*是否必填} required
11 * @param {*必填提示} message
12 */
13 import React, { useState, useEffect, forwardRef } from 'react';
14 import { connect } from 'dva';
15 import { Card, Button, Row, Col, Input, Popover, Form, Cascader, Upload } from 'antd';
16 import InputCom from './InputCom';
17 const FormItem = props => {
18 const { label, name, placeholder, max, type, rules, required = true, min = 0, message, extra } = props;
19 const placeholderText = placeholder ? placeholder : `请输入${label}`;
20 const rlueMessage = message ? message : placeholderText;
21 return (
22 <Form.Item
23 label={label}
24 name={name}
25 extra={extra}
26 rules={rules ? rules : [{ required: required, message: rlueMessage, min }]}
27 >
28 <InputCom {...props} placeholder={placeholderText} />
29 </Form.Item>
30 );
31 };
32 export default FormItem;
1 /**
2 * Author: wjw
3 * Date:
4 * Description:
5 */
6 import React, { useState, useEffect, forwardRef } from 'react';
7 import { connect } from 'umi';
8 import { Card, Button, Row, Col, Input, Popover, Form } from 'antd';
9 import { CheckCircleOutlined } from '@ant-design/icons';
10 const hasLength = value => {
11 return value.length >= 6 && value.length <= 16;
12 };
13 const hasletter = value => {
14 for (var i in value) {
15 var asc = value.charCodeAt(i);
16 if ((asc >= 65 && asc <= 90) || (asc >= 97 && asc <= 122)) {
17 return true;
18 }
19 }
20 return false;
21 };
22 const hasnum = value => {
23 var str = value;
24 var reg = new RegExp(/\d+/gi);
25 if (reg.test(str)) {
26 return true;
27 } else {
28 return false;
29 }
30 };
31 const PasswordFormItem = forwardRef((props, ref) => {
32 const {
33 value = '',
34 onChange,
35 placeholder = '6 - 16位密码,包含字母和数字',
36 form,
37 visible,
38 prefix,
39 } = props;
40
41 const onIptChange = ({ target: { value } }) => {
42 onChange(value);
43 };
44 const [isObj, setIsObj] = useState({
45 count: false,
46 letter: false,
47 num: false,
48 });
49
50 useEffect(() => {
51 const nobj = {
52 count: hasLength(value),
53 letter: hasletter(value),
54 num: hasnum(value),
55 };
56 setIsObj(nobj);
57 }, [value]);
58 const isTure = bool => {
59 return (
60 <div className="mr-5">
61 {bool ? (
62 <CheckCircleOutlined style={{ color: '#52c41a' }} />
63 ) : (
64 <div
65 style={{
66 width: '14px',
67 height: '14px',
68 border: '1px solid #ccc',
69 borderRadius: '100%',
70 }}
71 ></div>
72 )}
73 </div>
74 );
75 };
76 const styles = {
77 display: 'flex',
78 flexDirection: 'row',
79 justifyContent: 'flex-start',
80 alignItems: 'center',
81 };
82 const Content = props => {
83 return (
84 <div>
85 <div style={styles}>
86 {isTure(isObj.count)}输入6 - 16 位字符,特殊字符包括下划线,@,#等等
87 </div>
88 <div style={styles}>{isTure(isObj.letter)}包含字母</div>
89 <div style={styles}>{isTure(isObj.num)}包含数字</div>
90 </div>
91 );
92 };
93 return (
94 <Popover placement="right" content={<Content />} visible={visible}>
95 <Input.Password
96 prefix={prefix}
97 autoComplete="new-password"
98 placeholder={placeholder}
99 value={value}
100 onChange={onIptChange}
101 visibilityToggle={false}
102 />
103 </Popover>
104 );
105 });
106 const FormItem = props => {
107 const { prefix, label } = props;
108 const [visible, setvisible] = useState(false);
109 const checkpassword = (rule, value) => {
110 if (!value || !(hasLength(value) && hasletter(value) && hasnum(value))) {
111 setvisible(true);
112 return Promise.reject('');
113 } else {
114 setvisible(false);
115 return Promise.resolve();
116 }
117 };
118 return (
119 <Form.Item required label={label} name="password" rules={[{ validator: checkpassword }]}>
120 <PasswordFormItem prefix={prefix} visible={visible} />
121 </Form.Item>
122 );
123 };
124 export default FormItem;
1 /**
2 * Author: wjw
3 * Date:
4 * Description:
5 */
6 import React, { useState, useEffect, forwardRef } from 'react';
7 import { connect } from 'umi';
8 import { Card, Button, Icon, Row, Col, Input, Popover, Form, Select } from 'antd';
9 import { isPhone } from '@/utils/utils';
10 const { Option } = Select;
11 const FormItem = props => {
12 const { label = null, name, isPrefixSelector = false, cd, hasFeedback = true } = props;
13
14 const prefixSelector = (
15 <Form.Item name="prefix" noStyle>
16 <Select style={{ width: 70 }}>
17 <Option value="86">+86</Option>
18 </Select>
19 </Form.Item>
20 );
21 const validateTocheckMobile = (rule, value) => {
22 if (value) {
23 if (!isPhone(value)) {
24 return Promise.reject('请输入正确手机格式');
25 return;
26 } else {
27 if (cd) {
28 cd();
29 } else {
30 return Promise.resolve();
31 }
32 }
33 } else {
34 return Promise.resolve();
35 }
36 };
37
38 return (
39 <Form.Item
40 label={label}
41 hasFeedback={hasFeedback}
42 name={name}
43 rules={[
44 {
45 required: true,
46 message: '输入手机号',
47 },
48 {
49 validator: validateTocheckMobile,
50 },
51 ]}
52 validateTrigger="onBlur"
53 >
54 <Input
55 addonBefore={isPrefixSelector ? prefixSelector : null}
56 style={{ width: '100%' }}
57 placeholder="11位手机号"
58 />
59 </Form.Item>
60 );
61 };
62 export default FormItem;
1 /**
2 * Author: wjw
3 * Date:
4 * Description:
5 */
6 import React, { useState, useEffect, forwardRef } from 'react';
7 import { connect } from 'dva';
8 import { Card, Button, Form, Radio } from 'antd';
9
10 const RadioFormItem = props => {
11 const { name, label, list = [], disabled } = props;
12
13 return (
14 <div>
15 <Form.Item label={label} name={name} rules={[{ required: true, message: '请选择' }]}>
16 <Radio.Group disabled={disabled}>
17 {list.map((item, index) => {
18 return (
19 <Radio key={index} value={item.value}>
20 {item.name}
21 </Radio>
22 );
23 })}
24 </Radio.Group>
25 </Form.Item>
26 </div>
27 );
28 };
29 export default RadioFormItem;
1 import React, { useState, useEffect, forwardRef } from 'react';
2 import { connect } from 'dva';
3 import { Card, Button, Upload, message } from 'antd';
4 import { urlConfig } from '@/common';
5 import styles from '../index.less';
6 import { getFileName, getFileType } from '@/utils/utils';
7 import { PlusOutlined, DownloadOutlined } from '@ant-design/icons';
8 // 上传图片的配置
9 const token = window.localStorage.getItem('qintaoyouxuan_token') || '';
10
11 const settings = {
12 name: 'file',
13 withCredentials: true,
14 headers: {
15 token,
16 },
17 };
18 const IMGUploadButton = ({ text }) => {
19 return (
20 <div>
21 <PlusOutlined />
22 <div className="ant-upload-text">{text}</div>
23 </div>
24 );
25 };
26
27 const UploadButton = ({ text }) => {
28 return (
29 <Button className={styles.textbtn}>
30 <DownloadOutlined />
31 {text}
32 </Button>
33 );
34 };
35
36 const UploadItem = forwardRef((props, ref) => {
37 const {
38 value,
39 onChange,
40 listType = 'text',
41 length = 1,
42 defaultFileList = [],
43 accept = 'image/jpg,image/jpeg,image/png',
44 maxSize = 2,
45 filemaxSize = 25,
46 dispatch,
47 style,
48 className,
49 text = '上传文件',
50 disabled,
51 children,
52 isError = false,
53 action = 'imgs/uploadImg',
54 } = props;
55
56 const serverURL = `${urlConfig.URL_API}${action}`;
57 const [fileList, setfileList] = useState(defaultFileList);
58 const [len, setlen] = useState(-1);
59 const [isupload, setIsupload] = useState(false);
60
61 useEffect(() => {
62 if (Array.isArray(value) && !isupload) {
63 let files = value.map((item, index) => {
64 let name = getFileName(item);
65 return {
66 uid: index,
67 name,
68 url: item,
69 thumbUrl: item,
70 status: 'done',
71 response: { status: 1, data: item },
72 percent: 100,
73 size: 0,
74 };
75 });
76 setfileList(files);
77 }
78 }, [value]);
79
80 useEffect(() => {
81 if (fileList.length >= len && len !== -1) {
82 let values = fileList.filter(item => item.url).map(i => i.url);
83 if (isError) {
84 values = fileList.map(i => {
85 const { status, response, url } = i;
86 return {
87 status,
88 url,
89 response,
90 };
91 });
92 }
93
94 if (onChange) {
95 onChange(values);
96 }
97 }
98 }, [fileList]);
99 const getisLtM = file => {
100 const { name, size } = file;
101 const type = getFileType(name);
102 const is = !!(type === '.pdf' || type === '.rar');
103 const MAX_SIZE = is ? filemaxSize : maxSize;
104 return {
105 MAX_SIZE,
106 isLtM: size / 1024 / 1024 < MAX_SIZE,
107 };
108 };
109 const getStatus = item => {
110 const { name, size, status, response, type } = item;
111 const MAX_SIZE = getisLtM(item).MAX_SIZE;
112 const isLtM = getisLtM(item).isLtM;
113
114 if (!isLtM) {
115 return {
116 ...item,
117 status: status === 'removed' ? 'removed' : 'error',
118 response: `上传的文件大小不能超过${MAX_SIZE}M`,
119 url: item.url,
120 };
121 }
122 if (status === 'done') {
123 if (response && response.status !== 1) {
124 return {
125 ...item,
126 status: 'error',
127 response: response.msg ? response.msg : response.data,
128 url: item.url,
129 };
130 }
131 }
132 return {
133 ...item,
134 status: status,
135 response: response,
136 url:
137 item.status === 'done'
138 ? item.response.data
139 : item.status === 'uploading'
140 ? 'uploading'
141 : item.url,
142 };
143 };
144
145 const onFilechange = info => {
146 setIsupload(true);
147 let fileone = info.file;
148 let fileList = info.fileList;
149 fileone = getStatus(fileone);
150 if (fileone.status === 'done' && fileone.response) {
151 message.success('上传成功');
152 // onChange();
153 } else if (fileone.status === 'error' && fileone.response) {
154 message.error(`${fileone.response}`);
155 //不符合的文件删除
156 fileList = fileList.filter(_ => _.uid !== fileone.uid);
157 }
158 //长度限制
159 fileList = fileList
160 .filter((i, idx) => idx < length)
161 .map(item => {
162 return getStatus(item);
163 });
164
165 setfileList(fileList);
166 setlen(fileList.length);
167 };
168
169 const onRemove = file => {
170 setlen(fileList.length - 1);
171 };
172 const onPreview = file => {
173 const img = file.url;
174 if (img) {
175 handleAdd('PREVIEWIMG', { imgUrl: img });
176 }
177 };
178 const handleAdd = (modalType, r) => {
179 const payload = {
180 preImgDataModal: {
181 modalType,
182 modalShow: true,
183 modalData: r ? r : {},
184 },
185 };
186 dispatch({ type: 'global/changeState', payload });
187 };
188
189 //上传前判断文件大小是否符合
190 const imgupload = (file, fileList) => {
191 const isLtM = getisLtM(file).isLtM;
192 if (!isLtM) {
193 return false;
194 }
195 return isLtM;
196 };
197 return (
198 <Upload
199 action={serverURL}
200 // accept={accept}
201 multiple={!!(length > 1)}
202 onChange={onFilechange}
203 fileList={fileList}
204 {...settings}
205 beforeUpload={imgupload}
206 listType={listType}
207 onRemove={onRemove}
208 onPreview={listType === 'picture-card' ? onPreview : null}
209 style={style}
210 className={className}
211 disabled={disabled}
212 >
213 {!disabled && fileList.length < length && (
214 <div>
215 {listType === 'picture-card' ? (
216 <IMGUploadButton text={text} />
217 ) : children ? (
218 children
219 ) : (
220 <UploadButton text={text} />
221 )}
222 </div>
223 )}
224 </Upload>
225 );
226 });
227 const mapStateToProps = ({ global }) => {
228 return {
229 preImgDataModal: global.preImgDataModal,
230 };
231 };
232 export default connect(mapStateToProps)(UploadItem);
1 /**
2 * 上传图片组件
3 * @param {*标签的文本} label
4 * @param {*标签的字段名} name
5 * @param {*额外的提示信息} extra
6 * @param {*插槽} children
7 * @param {*value是否含有上传文件的当前状态{status:'done',url:'',response:''}} isError
8 */
9 import React, { useState, useEffect, forwardRef } from 'react';
10 import { connect } from 'dva';
11 import { Card, Button, Icon, Row, Col, Input, Popover, Form, Cascader, Upload } from 'antd';
12 import UploadItem from './UploadItem';
13 const FormItem = props => {
14 const { name, label, extra, children, isError } = props;
15 const validator = (rule, value) => {
16 if (value && value.some(i => i.status === 'error')) {
17 return Promise.reject('您有文件不符合要求,请删除!');
18 } else if (value && value.some(i => i.status === 'uploading' || i === 'uploading')) {
19 return Promise.reject('您有文件正在上传!');
20 } else {
21 return Promise.resolve();
22 }
23 };
24
25 let rules = [
26 { required: true, message: '请上传文件' },
27 {
28 validator: validator,
29 },
30 ];
31
32 return (
33 <Form.Item label={label} extra={extra} name={name} rules={rules}>
34 <UploadItem {...props}>{children}</UploadItem>
35 </Form.Item>
36 );
37 };
38 export default FormItem;
1 .upfilebtn {
2 width: 70px;
3 height: 70px;
4 background: rgba(251, 251, 251, 1);
5 border: 1px dashed rgba(218, 218, 218, 1);
6 display: flex;
7 flex-direction: column;
8 justify-content: center;
9 align-items: center;
10 p {
11 font-size: 14px;
12 font-family: Source Han Sans CN;
13 font-weight: 400;
14 color: rgba(153, 153, 153, 1);
15 margin-top: 11px;
16 }
17 }
18 .textbtn:hover,
19 .textbtn:focus {
20 border: 1px solid #1890ff !important;
21 color: #1890ff !important;
22 }
1 import React from 'react';
2 import { connect } from 'umi';
3 import { LogoutOutlined, InfoCircleOutlined } from '@ant-design/icons';
4 import { Avatar, Menu, Spin } from 'antd';
5 import { IconFontConfig } from '@/common';
6 import HeaderDropdown from '../HeaderDropdown';
7 import styles from './index.less';
8 import ModalUpdatePassword from '../ModalUpdatePassword';
9
10 class AvatarDropdown extends React.Component {
11 onMenuClick = event => {
12 const { key } = event;
13
14 if (key === 'logout') {
15 const { dispatch } = this.props;
16 if (dispatch) {
17 dispatch({
18 type: 'login/logout',
19 });
20 }
21 return;
22 } else if (key === 'password') {
23 const { dispatch } = this.props;
24 dispatch({
25 type: 'user/changeState',
26 payload: {
27 dataModal: {
28 modalType: 'PASSWORD_UPDATE_MODAL',
29 modalShow: true,
30 modalData: {},
31 },
32 },
33 });
34 }
35 };
36
37 render() {
38 const {
39 currentUser = {
40 avatar: '',
41 name: '',
42 },
43 menu,
44 } = this.props;
45 const menuHeaderDropdown = (
46 <Menu className={styles.menu} selectedKeys={[]} onClick={this.onMenuClick}>
47 <Menu.Item key="password">
48 <InfoCircleOutlined />
49 修改密码
50 </Menu.Item>
51 <Menu.Item key="logout">
52 <LogoutOutlined />
53 退出登录
54 </Menu.Item>
55 </Menu>
56 );
57 return currentUser && currentUser.name ? (
58 <>
59 <HeaderDropdown overlay={menuHeaderDropdown}>
60 <span className={`${styles.action} ${styles.account}`}>
61 <IconFontConfig type="icon-header" style={{ fontSize: '28px' }} className="mr-10" />
62 <span className={styles.name}>{currentUser.name}</span>
63 </span>
64 </HeaderDropdown>
65 <ModalUpdatePassword />
66 </>
67 ) : (
68 <Spin
69 size="small"
70 style={{
71 marginLeft: 8,
72 marginRight: 8,
73 lineHeight: '64px',
74 }}
75 />
76 );
77 }
78 }
79
80 export default connect(({ user }) => ({
81 currentUser: user.currentUser,
82 }))(AvatarDropdown);
1 import { Tooltip, Tag } from 'antd';
2 import { QuestionCircleOutlined } from '@ant-design/icons';
3 import React from 'react';
4 import { connect } from 'umi';
5 import Avatar from './AvatarDropdown';
6 import styles from './index.less';
7 const ENVTagColor = {
8 dev: 'orange',
9 test: 'green',
10 pre: '#87d068',
11 };
12
13 const GlobalHeaderRight = props => {
14 const { theme, layout } = props;
15 let className = styles.right;
16
17 if (theme === 'dark' && layout === 'topmenu') {
18 className = `${styles.right} ${styles.dark}`;
19 }
20
21 return (
22 <div className={className}>
23
24 {/* <Tooltip title="使用文档">
25 <a
26 target="_blank"
27 href="https://pro.ant.design/docs/getting-started"
28 rel="noopener noreferrer"
29 className={styles.action}
30 >
31 <QuestionCircleOutlined />
32 </a>
33 </Tooltip> */}
34 <Avatar />
35 {REACT_APP_ENV && (
36 <span>
37 <Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
38 </span>
39 )}
40 </div>
41 );
42 };
43
44 export default connect(({ settings }) => ({
45 theme: settings.navTheme,
46 layout: settings.layout,
47 }))(GlobalHeaderRight);
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: @layout-header-height;
18 margin-left: auto;
19 overflow: hidden;
20 .action {
21 display: flex;
22 align-items: center;
23 height: 100%;
24 padding: 0 12px;
25 cursor: pointer;
26 transition: all 0.3s;
27 > span {
28 color: @text-color;
29 vertical-align: middle;
30 }
31 // &:hover {
32 // background: @pro-header-hover-bg;
33 // }
34 // &:global(.opened) {
35 // background: @pro-header-hover-bg;
36 // }
37 }
38 .search {
39 padding: 0 12px;
40 &:hover {
41 background: transparent;
42 }
43 }
44 .account {
45 .avatar {
46 margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
47 margin-right: 8px;
48 color: @primary-color;
49 vertical-align: top;
50 background: rgba(255, 255, 255, 0.85);
51 }
52 }
53 }
54
55 .dark {
56 .action {
57 color: rgba(255, 255, 255, 0.85);
58 > span {
59 color: rgba(255, 255, 255, 0.85);
60 }
61 // &:hover,
62 // &:global(.opened) {
63 // background: @primary-color;
64 // }
65 }
66 }
67
68 :global(.ant-pro-global-header) {
69 img {
70 height: 50px;
71 }
72 .dark {
73 .action {
74 color: @text-color;
75 > span {
76 color: @text-color;
77 }
78 &:hover {
79 color: rgba(255, 255, 255, 0.85);
80 > span {
81 color: rgba(255, 255, 255, 0.85);
82 }
83 }
84 }
85 }
86 }
87
88 @media only screen and (max-width: @screen-md) {
89 :global(.ant-divider-vertical) {
90 vertical-align: unset;
91 }
92 .name {
93 display: none;
94 }
95 .right {
96 position: absolute;
97 top: 0;
98 right: 12px;
99 .account {
100 .avatar {
101 margin-right: 0;
102 }
103 }
104 }
105 }
1 import { Dropdown } from 'antd';
2 import React from 'react';
3 import classNames from 'classnames';
4 import styles from './index.less';
5
6 const HeaderDropdown = ({ overlayClassName: cls, ...restProps }) => (
7 <Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} />
8 );
9
10 export default HeaderDropdown;
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 }
1 /**
2 * Auther: APIS
3 */
4 import React, { useEffect, useState } from 'react';
5 import { Spin } from 'antd';
6 import styles from './index.less';
7 export const customLoadingParams = {
8 indicator: (
9 <img
10 className="loading-img"
11 src="https://youxuan-prod.oss-cn-zhangjiakou.aliyuncs.com/qintaoyouxuan/loading.gif"
12 />
13 ),
14 delay: 50,
15 };
16 const Loading = props => {
17 const { loading } = props;
18
19 return <Spin spinning={loading} {...customLoadingParams} className="wrap-loading"></Spin>;
20 };
21 export default Loading;
1 :global {
2 .ant-pro-basicLayout .ant-pro-basicLayout-is-children.ant-pro-basicLayout-fix-siderbar{
3 .wrap-loading{
4 left: -100px !important;
5 }
6 }
7 .wrap-loading{
8 position: fixed !important;
9 top: 0 !important;
10 left: 0px !important;
11 right: 0 !important;
12 bottom: 0 !important;
13 z-index: 999 !important;
14 &:before{
15 content: '';
16 display: block;
17 width: 100%;
18 height: 100%;
19 background: #FFF;
20 opacity: .6;
21 }
22 .ant-spin-dot.ant-spin-dot-spin{
23 position: absolute;
24 top: 40%;
25 left: 50%;
26 }
27 .loading-img{
28 position: absolute;
29 top: 40%;
30 left: 50%;
31 width: 30px;
32 height: auto;
33 opacity: .6;
34 }
35 }
36 }
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * Author: Charles
3 * Date: 2022.9.13
4 * Description: [修改密码]
5 */
6 import React, { useEffect } from 'react';
7 import { connect } from 'umi';
8 import { Modal, Form, Input, Button } from 'antd';
9
10 const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
11
12 const ModalUpdatePassword = props => {
13 const [form] = Form.useForm();
14 const {
15 dispatch,
16 dataModal: { modalType, modalShow },
17 } = props;
18
19 useEffect(() => {
20 if (modalType === 'PASSWORD_UPDATE_MODAL' && modalShow) {
21 form.resetFields();
22 }
23 }, [modalType, modalShow]);
24
25 /* 点击保存 */
26 const handleSave = () => {
27 form.validateFields().then(values => {
28 dispatch({ type: 'user/updatePassword', payload: values });
29 });
30 };
31
32 return (
33 <Modal
34 title="修改密码"
35 placement="right"
36 width={700}
37 maskClosable={false}
38 onCancel={() => {
39 dispatch({ type: 'user/cancelModal' });
40 }}
41 visible={modalType === 'PASSWORD_UPDATE_MODAL' && modalShow}
42 footer={
43 <div
44 style={{
45 textAlign: 'right',
46 }}
47 >
48 <Button
49 onClick={() => {
50 dispatch({ type: 'user/cancelModal' });
51 }}
52 className="mr-10"
53 >
54 取消
55 </Button>
56 <Button onClick={handleSave} type="primary">
57 保存
58 </Button>
59 </div>
60 }
61 >
62 <Form form={form} {...formItemLayout} name="password_set_modal">
63 <Form.Item
64 name="oldPassword"
65 label="原密码"
66 rules={[{ required: true, message: '请输入原密码' }]}
67 >
68 <Input.Password placeholder="请输入原密码" />
69 </Form.Item>
70 <Form.Item
71 name="newPassword"
72 label="新密码"
73 rules={[
74 { required: true, message: '请输入新密码' },
75 {
76 pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![!#$%^&*]+$)[\da-zA-Z!#$%^&@*]{6,16}$/,
77 message: '密码至少包含字母、数字、特殊符号的两种组合,限制6~16个字符~',
78 },
79 ]}
80 >
81 <Input.Password placeholder="请输入新密码" />
82 </Form.Item>
83 <Form.Item
84 name="checkPassword"
85 label="确认新密码"
86 rules={[
87 { required: true, message: '请输入确认新密码' },
88 ({ getFieldValue }) => ({
89 validator(_, value) {
90 if (!value || getFieldValue('newPassword') === value) {
91 return Promise.resolve();
92 }
93 return Promise.reject(new Error('两次密码不一致,请重新输入'));
94 },
95 }),
96 ]}
97 >
98 <Input.Password placeholder="请输入确认新密码" />
99 </Form.Item>
100 </Form>
101 </Modal>
102 );
103 };
104
105 export default connect(({ user }) => ({
106 ...user,
107 }))(ModalUpdatePassword);
1 /**
2 * Auther: APIS
3 */
4 import ModalPreImg from '@/pages/Modals/ModalPreImg';
5 import FileExport from '@/components/FileExport';
6 import Loading from '@/components/Loading';
7
8
9 const PageCommons = props=> {
10 const { urlFileExport, loading, dispatch } = props;
11 return (
12 <>
13 {/* 图片预览 */}
14 <ModalPreImg />
15 {/* 文件下载 */}
16 <FileExport url={urlFileExport} dispatch={dispatch} />
17 {/* 全局loading */}
18 <Loading loading={loading} />
19 </>
20 )
21 };
22 export default PageCommons;
...\ No newline at end of file ...\ No newline at end of file
1 import { PageLoading } from '@ant-design/pro-layout'; // loading components from code split
2 // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
3
4 export default PageLoading;
1 import React, { useEffect } from 'react';
2 import { Row, Col, Card, Typography, Tooltip, Spin } from 'antd';
3 import styles from './index.less';
4 import { ExclamationCircleOutlined } from '@ant-design/icons';
5 import Loading from '@/components/Loading';
6 const { Title } = Typography;
7
8 const QCard = props => {
9 const { title, des, children, extra, loading = false, ...otherProps } = props;
10 return (
11 <Card {...otherProps}>
12 <Loading loading={loading} />
13 <Row className={styles.main} type="flex" justify="space-between" align="middle">
14 <Col>
15 <Row type="flex" align="middle">
16 <Col>
17 <div className={styles.fk} />
18 </Col>
19 {title && (
20 <Col>
21 <div className={styles.title}>{title}</div>
22 </Col>
23 )}
24 {des && (
25 <Col>
26 <Tooltip title={des}>
27 <ExclamationCircleOutlined />
28 </Tooltip>
29 </Col>
30 )}
31 </Row>
32 </Col>
33 {extra && <Col>{extra}</Col>}
34 </Row>
35
36 {children}
37 </Card>
38 );
39 };
40
41 export default QCard;
1 .main {
2 margin-bottom: 20px;
3 .title {
4 font-size: 14px;
5 font-family: PingFang SC;
6 font-weight: 500;
7 color: rgba(51, 51, 51, 1);
8 margin: 0 8px;
9 }
10
11 .fk {
12 width: 3px;
13 height: 16px;
14 background: rgba(230, 15, 26, 1);
15 }
16 }
1 import styled, { css } from 'styled-components';
2
3 export const StyledPageContainer = styled.div`
4 width: 100%;
5 height: 100%;
6 `;
7
8 export const StyledPageHeader = styled.div<{ border: Boolean }>`
9 display: flex;
10 align-items: center;
11 justify-content: flex-end;
12 background-color: #fff;
13 padding: 8px 16px;
14 border-radius: 2px;
15 border-top: ${props => (props.border ? '1px solid rgba(0, 0, 0, 0.06)' : 'none')};
16 .ant-btn {
17 margin-left: 10px;
18 }
19 .ant-upload-list {
20 display: none;
21 }
22 `;
23
24 export const StyledPageContent = styled.div`
25 margin: 16px 16px 0;
26 .ant-form {
27 .ant-form-item {
28 margin-bottom: 16px;
29 }
30 }
31 `;
32
33 export const StyledEllipsisWrap = styled.div<{ maxLine: number }>`
34 overflow: hidden;
35 text-overflow: ellipsis;
36 ${p =>
37 p.maxLine === 1
38 ? css`
39 white-space: nowrap;
40 `
41 : css`
42 display: -webkit-box;
43 white-space: normal;
44 -webkit-line-clamp: ${p.maxLine};
45 -webkit-box-orient: vertical;
46 word-break: break-all;
47 `}
48 `;
49
50 export const StyledWapperTab = styled.div`
51 .ant-tabs {
52 background-color: #fff;
53 padding: 0px 16px;
54 }
55 .ant-tabs-nav {
56 margin: 0;
57 }
58 .ant-tabs-top > .ant-tabs-nav::before,
59 .ant-tabs-bottom > .ant-tabs-nav::before,
60 .ant-tabs-top > div > .ant-tabs-nav::before,
61 .ant-tabs-bottom > div > .ant-tabs-nav::before {
62 border-bottom: none;
63 }
64 `;
65
66 export const StyledPageFlex = styled.div`
67 display: flex;
68 `;
69
70 export const StyledPageLeft = styled.div`
71 width: 300px;
72 margin-right: 16px;
73 border-radius: 2px;
74 padding: 22px 16px;
75 background-color: #fff;
76 min-height: calc(100vh - 144px);
77 max-height: calc(100vh - 144px);
78 overflow-y: scroll;
79 .ant-tree .ant-tree-node-content-wrapper {
80 padding: 0 6px;
81 .ant-tree-title {
82 font-size: 15px;
83 }
84 }
85 `;
86
87 export const StyledPageRight = styled.div`
88 flex: 1;
89 border-radius: 2px;
90 `;
91
92 export const StyledWapperIframe = styled.div`
93 height: calc(100vh - 62px);
94 width: 100%;
95 background-color: #fff;
96 `;
97
98 export const StyledTitle = styled.div`
99 h1 {
100 font-size: 16px;
101 }
102 `;
103
104 export const StyledText = styled.div`
105 display: flex;
106 flex-wrap: wrap;
107 .item-text {
108 width: calc(50% - 10px);
109 margin-bottom: 10px;
110 margin-right: 10px;
111 .title {
112 color: #797e8f;
113 font-size: 14px;
114 margin-bottom: 4px;
115 }
116 .desc {
117 color: #000;
118 font-size: 14px;
119 }
120 }
121 `;
1 import moment from 'moment';
2 // paginationDefault
3 export const paginations = {
4 position: ['bottomCenter'],
5 defaultCurrent: 1,
6 showSizeChanger: true,
7 showQuickJumper: true,
8 pageSizeOptions: ['5', '10', '20', '50'],
9 };
10 // staticModal
11 export const staticModal = {
12 modalType: '',
13 modalShow: false,
14 modalData: {},
15 };
16
17 // 处理状态
18 export const mapStatus = {
19 1: {
20 label: '待办理',
21 value: '1',
22 },
23 2: {
24 label: '待整改',
25 value: '2',
26 },
27 3: {
28 label: '已办结',
29 value: '3',
30 },
31 4: {
32 label: '已整改',
33 value: '4',
34 },
35 };
36
37 // 风险类型
38 export const mapRiskType = {
39 1: {
40 label: '涉黄',
41 value: '1',
42 },
43 2: {
44 label: '涉非',
45 value: '2',
46 },
47 3: {
48 label: '涉政',
49 value: '3',
50 },
51 };
52
53 // 风险源类型
54 export const mapRiskSourceType = {
55 1: {
56 label: '文本',
57 value: '1',
58 },
59 2: {
60 label: '图片',
61 value: '2',
62 },
63 3: {
64 label: '音频',
65 value: '3',
66 },
67 4: {
68 label: '视频',
69 value: '4',
70 },
71 };
72
73 // 事件类型
74 export const mapEventType = {
75 1: {
76 label: '网吧',
77 value: '1',
78 },
79 2: {
80 label: '出版物',
81 value: '2',
82 },
83 3: {
84 label: '网络文化',
85 value: '3',
86 },
87 4: {
88 label: '印刷',
89 value: '4',
90 },
91 5: {
92 label: '电影',
93 value: '5',
94 },
95 6: {
96 label: '广电',
97 value: '6',
98 },
99 7: {
100 label: '互联网视听',
101 value: '7',
102 },
103 };
104
105 // 事件等级
106 export const mapEventLevel = {
107 1: {
108 label: '轻微',
109 value: '1',
110 },
111 2: {
112 label: '一般',
113 value: '2',
114 },
115 3: {
116 label: '严重',
117 value: '3',
118 },
119 };
120
121 // 风险标签
122 export const enumRiskLabel = [
123 {
124 label: '涉黄',
125 value: '1',
126 },
127 {
128 label: '涉非',
129 value: '2',
130 },
131 {
132 label: '涉政',
133 value: '3',
134 },
135 ];
136
137 // 事件状态
138 export const mapEventStatus = {
139 1: {
140 label: '发起中',
141 value: '1',
142 },
143 2: {
144 label: '处理中',
145 value: '2',
146 },
147 3: {
148 label: '协同中',
149 value: '3',
150 },
151 4: {
152 label: '已办结',
153 value: '4',
154 },
155 5: {
156 label: '已关闭',
157 value: '5',
158 },
159 };
160
161 // 区域
162 export const enumArea = [
163 '百丈镇',
164 '黄湖镇',
165 '鸬鸟镇',
166 '径山镇',
167 '瓶窑镇',
168 '良渚街道',
169 '仁和街道',
170 '仓前街道',
171 '余杭街道',
172 '中泰街道',
173 '闲林街道',
174 '五常街道',
175 ];
176
177 // 案件原由
178 export const mapCause = {
179 1: {
180 label: '行政案件办理',
181 value: '1',
182 },
183 2: {
184 label: '网络行政案件',
185 value: '2',
186 },
187 3: {
188 label: '刑事案件',
189 value: '3',
190 },
191 4: {
192 label: '保护未成年人重要案件',
193 value: '4',
194 },
195 0: {
196 label: '其他',
197 value: '0',
198 },
199 };
200
201 export const enumYear = (startYear = 1950, endYear = +moment().format('YYYY')) => {
202 let year = [];
203 for (let i = startYear; i <= endYear; i++) {
204 year.push({
205 label: `${i} 年`,
206 value: i + '',
207 });
208 }
209 return year;
210 };
1 // 手机号正则
2 export const phoneReg = /^[1][3,4,5,7,8][0-9]{9}$/;
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 beforeAll(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 });
1 const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
2
3 describe('Homepage', () => {
4 it('topmenu should have footer', async () => {
5 const params = '?navTheme=light&layout=topmenu';
6 await page.goto(`${BASE_URL}${params}`);
7 await page.waitForSelector('footer', {
8 timeout: 2000,
9 });
10 const haveFooter = await page.evaluate(
11 () => document.getElementsByTagName('footer').length > 0,
12 );
13 expect(haveFooter).toBeTruthy();
14 });
15 });
1 import { Button, message, notification } from 'antd';
2 import React from 'react';
3 import { formatMessage } from 'umi';
4 import defaultSettings from '../config/defaultSettings';
5 const { pwa } = defaultSettings; // if pwa is true
6
7 if (pwa) {
8 // Notify user if offline now
9 window.addEventListener('sw.offline', () => {
10 message.warning(
11 formatMessage({
12 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
17 window.addEventListener('sw.updated', event => {
18 const e = event;
19
20 const reloadSW = async () => {
21 // Check if there is sw whose state is waiting in ServiceWorkerRegistration
22 // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
23 const worker = e.detail && e.detail.waiting;
24
25 if (!worker) {
26 return true;
27 } // Send skip-waiting event to waiting SW with MessageChannel
28
29 await new Promise((resolve, reject) => {
30 const channel = new MessageChannel();
31
32 channel.port1.onmessage = msgEvent => {
33 if (msgEvent.data.error) {
34 reject(msgEvent.data.error);
35 } else {
36 resolve(msgEvent.data);
37 }
38 };
39
40 worker.postMessage(
41 {
42 type: 'skip-waiting',
43 },
44 [channel.port2],
45 );
46 }); // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
47
48 window.location.reload(true);
49 return true;
50 };
51
52 const key = `open${Date.now()}`;
53 const btn = (
54 <Button
55 type="primary"
56 onClick={() => {
57 notification.close(key);
58 reloadSW();
59 }}
60 >
61 {formatMessage({
62 id: 'app.pwa.serviceworker.updated.ok',
63 })}
64 </Button>
65 );
66 notification.open({
67 message: formatMessage({
68 id: 'app.pwa.serviceworker.updated',
69 }),
70 description: formatMessage({
71 id: 'app.pwa.serviceworker.updated.hint',
72 }),
73 btn,
74 key,
75 onClose: async () => {},
76 });
77 });
78 } else if ('serviceWorker' in navigator) {
79 // unregister service worker
80 const { serviceWorker } = navigator;
81
82 if (serviceWorker.getRegistrations) {
83 serviceWorker.getRegistrations().then(sws => {
84 sws.forEach(sw => {
85 sw.unregister();
86 });
87 });
88 }
89
90 serviceWorker.getRegistration().then(sw => {
91 if (sw) sw.unregister();
92 }); // remove all caches
93
94 if (window.caches && window.caches.keys) {
95 caches.keys().then(keys => {
96 keys.forEach(key => {
97 caches.delete(key);
98 });
99 });
100 }
101 }
1 @import '~antd/es/style/themes/default.less';
2
3 html,
4 body,
5 #root {
6 height: 100%;
7 overflow: hidden;
8 }
9
10 .colorWeak {
11 filter: invert(80%);
12 }
13
14 .ant-layout {
15 min-height: 100vh;
16 }
17
18 canvas {
19 display: block;
20 }
21
22 body {
23 text-rendering: optimizeLegibility;
24 -webkit-font-smoothing: antialiased;
25 -moz-osx-font-smoothing: grayscale;
26 }
27
28 /* 浏览器滚动条相关 */
29 ::-webkit-scrollbar-thumb {
30 height: 50px;
31 background: rgba(160, 160, 160, 0.8);
32 border-radius: 3px;
33 transition: background 1s;
34 &:hover {
35 background: rgba(160, 160, 160, 1);
36 }
37 }
38 ::-webkit-scrollbar {
39 width: 4px;
40 height: 4px;
41 }
42
43 ul,
44 ol {
45 list-style: none;
46 }
47
48 @media (max-width: @screen-xs) {
49 .ant-table {
50 width: 100%;
51 overflow-x: auto;
52 &-thead > tr,
53 &-tbody > tr {
54 > th,
55 > td {
56 white-space: pre;
57 > span {
58 display: block;
59 }
60 }
61 }
62 }
63 }
64
65 // 兼容IE11
66 @media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
67 body .ant-design-pro > .ant-layout {
68 min-height: 100vh;
69 }
70 }
71 .mt-5 {
72 margin-top: 5px !important;
73 }
74 .mt-10 {
75 margin-top: 10px !important;
76 }
77 .mt-15 {
78 margin-top: 15px !important;
79 }
80 .mt-20 {
81 margin-top: 20px !important;
82 }
83 .mt-24 {
84 margin-top: 24px !important;
85 }
86
87 .ml-5 {
88 margin-left: 5px !important;
89 }
90 .ml-10 {
91 margin-left: 10px !important;
92 }
93 .ml-15 {
94 margin-left: 15px !important;
95 }
96 .ml-20 {
97 margin-left: 20px !important;
98 }
99
100 .mr-5 {
101 margin-right: 5px !important;
102 }
103 .mr-10 {
104 margin-right: 10px !important;
105 }
106 .mr-15 {
107 margin-right: 15px !important;
108 }
109 .mr-20 {
110 margin-right: 20px !important;
111 }
112 .mr-30 {
113 margin-right: 30px !important;
114 }
115
116 .mb-5 {
117 margin-bottom: 5px !important;
118 }
119 .mb-10 {
120 margin-bottom: 10px !important;
121 }
122 .mb-15 {
123 margin-bottom: 15px !important;
124 }
125 .mb-20 {
126 margin-bottom: 20px !important;
127 }
128
129 .tn-l {
130 text-align: left;
131 }
132 .tn-c {
133 text-align: center;
134 }
135 .tn-r {
136 text-align: right;
137 }
138 .ft-24 {
139 font-size: 24px;
140 }
141 .color-gray {
142 color: rgba(0, 0, 0, 0.35);
143 }
144 .color-primary {
145 color: @primary-color !important;
146 }
147
148 // 清除上下浮动
149 .clearfix {
150 &::before,
151 &::after {
152 content: ' ';
153 display: table;
154 }
155 &::after {
156 clear: both;
157 }
158 }
159 .left {
160 float: left;
161 }
162 .right {
163 float: right;
164 }
165 .fn-hide {
166 display: none !important;
167 }
168 // 单行字体溢出时显示省略号
169 .ellipsis {
170 white-space: nowrap;
171 overflow: hidden;
172 text-overflow: ellipsis;
173 }
174
175 .pointer {
176 cursor: pointer;
177 &:hover {
178 color: #40a9ff;
179 }
180 }
181 .wflex_r_s {
182 display: flex;
183 flex-direction: row;
184 justify-content: space-between;
185 align-items: center;
186 }
187
188 .c9 {
189 color: #999 !important;
190 }
191
192 :global {
193 .ant-table-wrapper {
194 .ant-table-pagination.ant-pagination {
195 margin-top: 24px;
196 margin-bottom: 0px;
197 justify-content: flex-end;
198 }
199 }
200 .ant-pro-basicLayout-content {
201 margin: 0px;
202 }
203 .ant-tooltip-inner {
204 .anticon {
205 margin-right: 6px;
206 }
207 }
208 }
209
210 /* 权限相关样式 */
211 .view-tree-auth {
212 :global {
213 .ant-tree {
214 position: relative;
215 top: 3px;
216 }
217 .info-tree {
218 .ant-tree-list {
219 .ant-tree-treenode {
220 .ant-tree-node-content-wrapper {
221 .ant-tree-title {
222 span {
223 display: none;
224 &:hover {
225 opacity: 0.8;
226 }
227 &:first-child {
228 color: #185da2;
229 }
230 &:last-child {
231 color: #666;
232 }
233 }
234 &:hover {
235 span {
236 display: inline-block;
237 }
238 }
239 }
240 }
241 }
242 }
243 }
244 }
245 }
246
247 .box-info {
248 min-height: calc(~'100vh - 112px');
249 :global {
250 .ant-card {
251 min-height: calc(~'100vh - 195px');
252 margin-bottom: 70px;
253 .footer-btn {
254 position: fixed;
255 bottom: 0;
256 left: 0;
257 z-index: 10;
258 width: 100%;
259 height: 56px;
260 line-height: 56px;
261 margin-bottom: 0;
262 text-align: center;
263 background-color: #fff;
264 box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
265 }
266 }
267 }
268 }
1 /**
2 * Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
3 * You can view component api by:
4 * https://github.com/ant-design/ant-design-pro-layout
5 */
6 import ProLayout from '@ant-design/pro-layout';
7 import { Link, connect, useIntl, history } from 'umi';
8 import React from 'react';
9 import RightContent from '@/components/GlobalHeader/RightContent';
10 import AuthRouter from '@/components/Auth/AuthRouter';
11 import PageCommons from '@/components/PageCommons';
12 import YH from '@/assets/yh-logo.png';
13 import {
14 DesktopOutlined,
15 WarningOutlined,
16 SelectOutlined,
17 BarsOutlined,
18 SettingOutlined,
19 } from '@ant-design/icons';
20 import { StyledTitle } from '@/components/style';
21 import { codeMappingForResource } from '../../config/routerConfig';
22
23 const IconMap = {
24 screen: <DesktopOutlined />,
25 risk: <WarningOutlined />,
26 event: <SelectOutlined />,
27 data: <BarsOutlined />,
28 system: <SettingOutlined />,
29 };
30
31 const BasicLayout = props => {
32 const intl = useIntl();
33 const { formatMessage } = intl;
34 const {
35 dispatch,
36 children,
37 settings,
38 location: { pathname },
39 loading,
40 urlFileExport,
41 currentUser: { menus },
42 } = props;
43 const handleMenuCollapse = payload => {
44 if (dispatch) {
45 dispatch({
46 type: 'global/changeLayoutCollapsed',
47 payload,
48 });
49 }
50 };
51 // 重定向处理
52 // const isRedirect = pathname === '/';
53 // if (isRedirect && dataMenus.length) {
54 // history.replace({ pathname: dataMenus[0].path });
55 // };
56 // 菜单 loop
57 const loopMenuItem = menus => {
58 // console.log(menus, 'menus', codeMappingForResource);
59 return menus?.map(({ icon, children, ...item }) => ({
60 ...(codeMappingForResource.get(item.resourceCode) || {}),
61 icon: icon && IconMap[icon],
62 routes: children && loopMenuItem(children),
63 }));
64 };
65
66 return (
67 <ProLayout
68 formatMessage={formatMessage}
69 menuHeaderRender={(logoDom, titleDom) => (
70 <Link to="/">
71 <img src={YH} />
72 <StyledTitle>{titleDom}</StyledTitle>
73 </Link>
74 )}
75 onCollapse={handleMenuCollapse}
76 menuItemRender={(menuItemProps, defaultDom) => {
77 if (menuItemProps.isUrl || menuItemProps.children || !menuItemProps.path) {
78 return defaultDom;
79 }
80 return (
81 <Link to={menuItemProps.path}>
82 {IconMap[menuItemProps.icons]}
83 {defaultDom}
84 </Link>
85 );
86 }}
87 subMenuItemRender={subProps => {
88 return (
89 <span className="ant-pro-menu-item">
90 {IconMap[subProps.icons]}
91 <span>{subProps.name}</span>
92 </span>
93 );
94 }}
95 menuDataRender={() => loopMenuItem(menus)}
96 rightContentRender={() => <RightContent />}
97 {...props}
98 {...settings}
99 >
100 {/* 路由权限 */}
101 <AuthRouter path={pathname}>{children}</AuthRouter>
102 <PageCommons loading={loading} urlFileExport={urlFileExport} dispatch={dispatch} />
103 </ProLayout>
104 );
105 };
106
107 export default connect(({ global, settings, user }) => ({
108 settings,
109 loading: global.loading,
110 collapsed: global.collapsed,
111 urlFileExport: global.urlFileExport,
112 currentUser: user.currentUser,
113 }))(BasicLayout);
1 import React from 'react';
2
3 const Layout = ({ children }) => <>{children}</>;
4
5 export default Layout;
1 import { connect } from 'umi';
2 import React from 'react';
3 import styles from './UserLayout.less';
4
5 const UserLayout = props => {
6 const { children } = props;
7 return (
8 <>
9 <div className={styles.container}>
10 <div className={styles.bgTop}>
11 <div>一网打“净”数智在线</div>
12 </div>
13 <div className={styles.content}>
14 <div className={styles.login}>{children}</div>
15 </div>
16 </div>
17 </>
18 );
19 };
20
21 export default connect(({ settings, user }) => ({
22 ...settings,
23 currentUser: user.currentUser,
24 }))(UserLayout);
1 @import '~antd/es/style/themes/default.less';
2
3 .container {
4 width: 100%;
5 display: flex;
6 flex-direction: column;
7 height: 100vh;
8 overflow: auto;
9 position: relative;
10 background-color: #F5F5F5;
11 .bgTop {
12 background-color: @primary-color;
13 height: 400px;
14 div {
15 color: #fff;
16 text-align: center;
17 font-size: 22px;
18 position: absolute;
19 top: 170px;
20 left: 50%;
21 letter-spacing: 2px;
22 transform: translateX(-50%);
23 }
24 }
25 .content {
26 position: absolute;
27 background-color: #fff;
28 border-radius: 2px;
29 top: 220px;
30 left: 50%;
31 z-index: 2;
32 transform: translateX(-50%);
33 .login {
34 width: 400px;
35 box-shadow: 0 4px 50px 0 rgba(121,173,201,.17);
36 }
37 }
38 }
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 export default {
8 'navBar.lang': 'Languages',
9 'layout.user.link.help': 'Help',
10 'layout.user.link.privacy': 'Privacy',
11 'layout.user.link.terms': 'Terms',
12 'app.preview.down.block': 'Download this page to your local project',
13 'app.welcome.link.fetch-blocks': 'Get all block',
14 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
15 ...globalHeader,
16 ...menu,
17 ...settingDrawer,
18 ...settings,
19 ...pwa,
20 ...component,
21 };
1 export default {
2 'component.tagSelect.expand': 'Expand',
3 'component.tagSelect.collapse': 'Collapse',
4 'component.tagSelect.all': 'All',
5 };
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 };
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 };
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 };
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 };
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 };
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 export default {
8 'navBar.lang': 'Idiomas',
9 'layout.user.link.help': 'ajuda',
10 'layout.user.link.privacy': 'política de privacidade',
11 'layout.user.link.terms': 'termos de serviços',
12 'app.preview.down.block': 'Download this page to your local project',
13 ...globalHeader,
14 ...menu,
15 ...settingDrawer,
16 ...settings,
17 ...pwa,
18 ...component,
19 };
1 export default {
2 'component.tagSelect.expand': 'Expandir',
3 'component.tagSelect.collapse': 'Diminuir',
4 'component.tagSelect.all': 'Todas',
5 };
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 };
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 };
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 };
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 };
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 };
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 export default {
8 'navBar.lang': '语言',
9 'layout.user.link.help': '帮助',
10 'layout.user.link.privacy': '隐私',
11 'layout.user.link.terms': '条款',
12 'app.preview.down.block': '下载此页面到本地项目',
13 'app.welcome.link.fetch-blocks': '获取全部区块',
14 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
15 ...globalHeader,
16 ...menu,
17 ...settingDrawer,
18 ...settings,
19 ...pwa,
20 ...component,
21 };
1 export default {
2 'component.tagSelect.expand': '展开',
3 'component.tagSelect.collapse': '收起',
4 'component.tagSelect.all': '全部',
5 };
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 };
1 export default {
2 'menu.appliction': '应用列表',
3 'menu.info': '应用详情',
4 'menu.login': '登录',
5 'menu.risk': '风险管理',
6 'menu.event': '事件管理',
7 'menu.data': '数据管理',
8 'menu.system': '系统管理',
9 'menu.system.account': '账号管理',
10 'menu.system.role': '角色管理',
11 'menu.system.menu': '菜单管理',
12 'menu.dataScreen': '数智大屏',
13 };
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 };
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 };
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 };
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 export default {
8 'navBar.lang': '語言',
9 'layout.user.link.help': '幫助',
10 'layout.user.link.privacy': '隱私',
11 'layout.user.link.terms': '條款',
12 'app.preview.down.block': '下載此頁面到本地項目',
13 ...globalHeader,
14 ...menu,
15 ...settingDrawer,
16 ...settings,
17 ...pwa,
18 ...component,
19 };
1 export default {
2 'component.tagSelect.expand': '展開',
3 'component.tagSelect.collapse': '收起',
4 'component.tagSelect.all': '全部',
5 };
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 };
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 };
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 };
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 };
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 };
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 }
1 import { queryNotices } from '@/services/user';
2 const GlobalModel = {
3 namespace: 'global',
4 state: {
5 collapsed: false,
6 loading: false,
7 urlFileExport: '',
8 dataModal: {
9 modalType: '',
10 modalShow: false,
11 modalData: {},
12 },
13 preImgDataModal: {
14 modalType: '',
15 modalShow: false,
16 modalData: {},
17 },
18 },
19 effects: {},
20 reducers: {
21 changeLayoutCollapsed(
22 state = {
23 notices: [],
24 collapsed: true,
25 },
26 { payload },
27 ) {
28 return { ...state, collapsed: payload };
29 },
30 },
31 subscriptions: {
32 setup({ history, dispatch }) {
33 // Subscribe history(url) change, trigger `load` action if pathname is `/`
34 history.listen(({ pathname, search }) => {
35 // 路由发生变化的时候切换菜单
36 const whiteList = ['/user/login'];
37
38 if (!whiteList.includes(pathname)) {
39 // 获取用户信息
40 dispatch({ type: 'user/fetchCurrent' });
41 }
42 });
43 },
44 },
45 };
46 export default GlobalModel;
1 import { queryLogin, loginOut } from '@/services/login';
2 import { getUserInfo, getBosUserList } from '@/services/user';
3 import { message } from 'antd';
4 import { history } from 'umi';
5 import md5 from 'blueimp-md5';
6 import defaultSettings from '../../config/defaultSettings';
7 import { getDataMenus } from '@/utils/menu';
8 const { tokenKey, md5Key } = defaultSettings;
9 const Model = {
10 namespace: 'login',
11 state: {
12 status: undefined,
13 },
14 effects: {
15 //退出登录
16 *logout({ payload }, { call, put, select }) {
17 try {
18 const res = yield call(loginOut);
19 if (res.code === 0 && res.data) {
20 if (!payload) {
21 message.success('退出成功');
22 }
23 localStorage.removeItem(tokenKey);
24 setTimeout(() => {
25 window.location.href = '/user/login';
26 }, 500);
27 }
28 } catch (err) {
29 console.error(err);
30 }
31 },
32 //登录
33 *login({ payload }, { call, put }) {
34 try {
35 const res = yield call(queryLogin, payload);
36 if (res.code === 0) {
37 localStorage.setItem(tokenKey, res.data);
38 // const userinfo = yield call(getUserInfo);
39 // const { permissionVos = [] } = userinfo.data;
40 // const dataMenus = getDataMenus(permissionVos);
41 // console.log(dataMenus);
42 // if (dataMenus.length) {
43 // history.replace(dataMenus[0].path);
44 // } else {
45 // history.replace('/');
46 // }
47 history.replace('/');
48 message.success('登录成功');
49 }
50 } catch (err) {
51 console.error(err);
52 }
53 },
54 },
55 reducers: {
56 changeLoginStatus(state, { payload }) {
57 return { ...state, status: payload.status, type: payload.type };
58 },
59 },
60 };
61 export default Model;
1 import defaultSettings from '../../config/defaultSettings';
2
3 const updateColorWeak = colorWeak => {
4 const root = document.getElementById('root');
5
6 if (root) {
7 root.className = colorWeak ? 'colorWeak' : '';
8 }
9 };
10
11 const SettingModel = {
12 namespace: 'settings',
13 state: defaultSettings,
14 reducers: {
15 changeSetting(state = defaultSettings, { payload }) {
16 const { colorWeak, contentWidth } = payload;
17
18 if (state.contentWidth !== contentWidth && window.dispatchEvent) {
19 window.dispatchEvent(new Event('resize'));
20 }
21
22 updateColorWeak(!!colorWeak);
23 return { ...state, ...payload };
24 },
25 },
26 };
27 export default SettingModel;
1 import { getUserInfo, updatePassword } from '@/services/user';
2 import { getDataMenus } from '@/utils/menu';
3 import { message } from 'antd';
4 const UserModel = {
5 namespace: 'user',
6 state: {
7 currentUser: {
8 admin: {},
9 dataMenus: [],
10 username: '',
11 },
12 userAuths: [], // 权限
13 dataModal: {
14 modalType: '',
15 modalShow: false,
16 modalData: {},
17 },
18 },
19 effects: {
20 *fetchCurrent(_, { call, put, select }) {
21 const {
22 currentUser,
23 currentUser: { username },
24 } = yield select(state => state.user);
25 if (!currentUser || !username) {
26 try {
27 const res = yield call(getUserInfo);
28 // data.dataMenus = getDataMenus(data.permissionVos);
29 if (res.code === 0) {
30 res.data.menus = [
31 {
32 children: [],
33 id: 999999,
34 parentId: 0,
35 resourceCode: 'Home',
36 resourceNodeType: '1',
37 title: '数智大屏',
38 },
39 ...(res.data.menus || []),
40 {
41 resourceCode: '404',
42 },
43 ];
44 yield put({ type: 'saveCurrentUser', payload: res.data });
45 }
46 } catch (err) {
47 console.error(err, 'err');
48 }
49 }
50 },
51 /* 修改密码 */
52 *updatePassword({ payload }, { call, put, select }) {
53 try {
54 const res = yield call(updatePassword, payload);
55 if (res.code === 0) {
56 message.success('修改成功');
57 yield put({ type: 'cancelModal' });
58 yield put({ type: 'login/logout', payload: { message: 1 } });
59 }
60 } catch (err) {
61 console.error(err);
62 }
63 },
64 },
65 reducers: {
66 changeState(state, { payload }) {
67 return {
68 ...state,
69 ...payload,
70 };
71 },
72 cancelModal(state, { payload }) {
73 return {
74 ...state,
75 dataModal: {
76 modalType: '',
77 modalShow: false,
78 modalData: {},
79 },
80 };
81 },
82 saveCurrentUser(state, { payload }) {
83 return {
84 ...state,
85 currentUser: payload || {},
86 // userAuths: payload.authorities.map(_ => _.code),
87 };
88 },
89 },
90 };
91 export default UserModel;
1 import { Button, Result } from 'antd';
2 import React from 'react';
3 import { history } from 'umi';
4
5 const NoFoundPage = () => (
6 <Result
7 status="404"
8 title="404"
9 subTitle="Sorry, the page you visited does not exist."
10 extra={
11 <Button type="primary" onClick={() => history.push('/')}>
12 Back Home
13 </Button>
14 }
15 />
16 );
17
18 export default NoFoundPage;
1 /**
2 * Author: llw
3 * Date: 2020.7.20
4 * Description: [设置权限弹框]
5 */
6 import React, { useEffect, useState } from 'react';
7 import { Drawer, Radio, Form, Select, Input, Button } from 'antd';
8
9 const { Option } = Select;
10 const formItemLayout = {labelCol: { span: 4 }, wrapperCol: { span: 20 }};
11
12 const ModalSetAuth = props => {
13 const [menuType, setType] = useState(1);
14 const [form] = Form.useForm();
15 let {
16 handleCancelModal,
17 handleAuthSetOk,
18 dataMenuList = [],
19 dataModal: {
20 modalType,
21 modalShow,
22 modalData,
23 modalData: { name, code, type, url, icon, parentCode, isEdit }
24 }
25 } = props;
26
27 dataMenuList = code ? dataMenuList.filter(item => code !== item.code) : dataMenuList;
28
29 /* 重置type */
30 useEffect(() => {
31 if (modalType === "AUTH_SET_MODAL" && modalShow) {
32 setType(type || 1);
33 setTimeout(() => {
34 form.resetFields();
35 form.setFieldsValue({
36 name,
37 code,
38 url,
39 icon,
40 parentCode,
41 type: type || 1,
42 });
43 }, 100)
44 }
45 }, [modalType, modalShow])
46
47 /* 点击保存 */
48 const handleSave = () => {
49 form.validateFields().then(values => {
50 handleAuthSetOk({
51 ...modalData,
52 ...values,
53 isEdit
54 });
55 })
56 };
57
58 /* 选择菜单类型 */
59 const handleChangeRadio = (value) => {
60 form.resetFields();
61 setType(value);
62 form.setFieldsValue({type: value});
63 };
64
65 return (
66 <Drawer
67 title="权限设置"
68 placement="right"
69 width={600}
70 maskClosable={false}
71 onClose={handleCancelModal}
72 visible={modalType === 'AUTH_SET_MODAL' && modalShow}
73 footer={
74 <div
75 style={{
76 textAlign: 'right',
77 }}
78 >
79 <Button onClick={handleCancelModal} className="mr-10">取消</Button>
80 <Button onClick={handleSave} type="primary">保存</Button>
81 </div>
82 }
83 >
84 <Form
85 form={form}
86 {...formItemLayout}
87 name="auth_set_modal"
88 initialValues={{ type: 1 }}
89 >
90 <Form.Item
91 name="type"
92 label="菜单类型"
93 >
94 <Radio.Group buttonStyle="solid" onChange={(e) => handleChangeRadio(e.target.value)}>
95 <Radio.Button disabled={(isEdit && type !== 1) ? true : false} value={1}>一级菜单</Radio.Button>
96 <Radio.Button disabled={(isEdit && type !== 2) ? true : false} value={2}>子菜单</Radio.Button>
97 <Radio.Button disabled={(isEdit && type !== 3) ? true : false} value={3}>按钮</Radio.Button>
98 </Radio.Group>
99 </Form.Item>
100 {
101 menuType !== 1 && <Form.Item
102 name="parentCode"
103 label={menuType === 2 ? "上级菜单" : "包含菜单"}
104 rules={
105 [{required: true, message: menuType === 2 ? "请选择上级菜单" : "请选择包含该按钮的菜单"}]
106 }
107 >
108 <Select
109 showSearch
110 placeholder={menuType === 2 ? '请选择上级菜单' : '请选择包含该按钮的菜单'}
111 optionFilterProp="children"
112 filterOption={(input, option) =>
113 option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
114 }
115 >
116 {
117 dataMenuList.map(item => {
118 return (
119 <Option key={item.code} value={item.code}>{item.name}</Option>
120 )
121 })
122 }
123 </Select>
124 </Form.Item>
125 }
126 <Form.Item
127 name="name"
128 label={menuType === 3 ? "按钮名称" : "菜单名称"}
129 rules={
130 [
131 {required: true, message: menuType === 3 ? '请输入按钮名称' : '请输入菜单名称'},
132 {min: 2, max: 15, message: '名称长度2~15个字符'}
133 ]
134 }
135 >
136 <Input maxLength={15} placeholder={menuType === 3 ? "请输入按钮名称" : "请输入菜单名称"} />
137 </Form.Item>
138 <Form.Item
139 name="code"
140 label={menuType === 3 ? "按钮code" : "菜单code"}
141 rules={
142 [{required: true, message: menuType === 3 ? '请输入按钮code' : '请输入菜单code'}]
143 }
144 >
145 <Input maxLength={20} placeholder={menuType === 3 ? "请输入按钮code" : "请输入菜单code"} />
146 </Form.Item>
147 {
148 menuType !== 3 && <>
149 <Form.Item
150 name="url"
151 label="菜单URL"
152 rules={
153 [{required: true, message: '请输入菜单URL'}]
154 }
155 >
156 <Input placeholder="请输入菜单URL" />
157 </Form.Item>
158 <Form.Item
159 name="icon"
160 label="菜单Icon"
161 >
162 <Input placeholder="请输入菜单Icon" />
163 </Form.Item>
164 </>
165 }
166 </Form>
167 </Drawer>
168 )
169 };
170
171 export default ModalSetAuth;
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * Author: llw
3 * Date: 2020.7.16
4 * Description: [应用列表]
5 */
6 import React, { useEffect } from 'react';
7 import { connect, Link } from 'umi';
8 import { Card, Form, DatePicker, Input, Button, Table, Divider, Modal } from 'antd';
9 import { ExclamationCircleOutlined } from '@ant-design/icons';
10 import { paginations } from '@/constants';
11 import moment from 'moment';
12
13 const { RangePicker } = DatePicker;
14 const FormItem = Form.Item;
15
16 /* SearchForm */
17 const SearchForm = props => {
18 const [form] = Form.useForm();
19 const {
20 dataSearch,
21 handleReset,
22 handleFinish,
23 dataSearch: { createTimeBegin, createTimeEnd }
24 } = props;
25
26 useEffect(() => {
27 form.setFieldsValue({
28 ...dataSearch,
29 createTime: [createTimeBegin ? moment(createTimeBegin) : undefined, createTimeEnd ? moment(createTimeEnd) : undefined]
30 });
31 }, [dataSearch]);
32
33 /* 点击搜索 */
34 const onFinish = (values) => {
35 handleFinish(values);
36 };
37
38 /* 点击重置 */
39 const onReset = () => {
40 handleReset();
41 };
42
43 return (
44 <Form
45 name="Form_Application"
46 layout="inline"
47 form={form}
48 onFinish={onFinish}
49 >
50 <FormItem label="应用名称" name="name">
51 <Input placeholder="请输入应用名称" style={{width: "220px"}} />
52 </FormItem>
53 <FormItem label="创建时间" name="createTime">
54 <RangePicker allowClear={false} />
55 </FormItem>
56 <FormItem>
57 <Button type="primary" htmlType="submit" className="mr-15">搜索</Button>
58 <Button className="mr-15" htmlType="button" onClick={onReset}>重置</Button>
59 <Link to="/appliction/info"><Button>创建应用</Button></Link>
60 </FormItem>
61 </Form>
62 )
63 };
64
65 /* DataTable */
66 const DataTable = props => {
67 const {
68 loading,
69 handleDel,
70 handleGetList,
71 dataApplication: { data = [], page, size, totalItem }
72 } = props;
73
74 /* 点击分页 */
75 const handlePageChange = (page, size) => {
76 handleGetList(page, size);
77 };
78
79 const columns = [
80 {
81 title: '应用名称',
82 dataIndex: 'name',
83 align: 'center'
84 },
85 {
86 title: '应用标识码',
87 dataIndex: 'code',
88 align: 'center',
89 render(t, r) {
90 return t || '--'
91 }
92 },
93 {
94 title: '应用链接地址',
95 dataIndex: 'url',
96 align: 'center',
97 render(t, r) {
98 return t || '--'
99 }
100 },
101 {
102 title: '创建时间',
103 dataIndex: 'createTime',
104 align: 'center',
105 render(t, r) {
106 return t ? moment(t).format("YYYY-MM-DD HH:mm:ss") : '--'
107 }
108 },
109 {
110 title: '操作',
111 align: 'center',
112 render(t, r) {
113 return (
114 <>
115 <Link to={`/appliction/info?id=${r.id}`}>编辑</Link>
116 <Divider type="vertical" />
117 <a onClick={() => handleDel(r)}>删除</a>
118 </>
119 )
120 }
121 }
122 ];
123 const pagination = {
124 ...paginations,
125 total: totalItem,
126 pageSize: size,
127 current: page,
128 showSizeChanger: totalItem > 20,
129 onChange: (page, pageSize) => {
130 handlePageChange(page, pageSize);
131 },
132 onShowSizeChange: (page, pageSize) => {
133 handlePageChange(1, pageSize);
134 },
135 showTotal(total) {
136 return `总共 ${total} 条数据`;
137 },
138 };
139 return (
140 <Table
141 rowKey="id"
142 loading={loading}
143 dataSource={data}
144 columns={columns}
145 pagination={pagination}
146 />
147 )
148 };
149
150 /* Main */
151 const Application = props => {
152 const { dispatch, loading, dataSearch, dataApplication, dataApplication: { size } } = props;
153
154 useEffect(() => {
155 handleGetList(1, 10);
156 }, [])
157
158 /* 应用列表 */
159 const handleGetList = (page, size) => {
160 dispatch({type: 'Application/getApplicationList', payload: {
161 page: page || 1,
162 size: size || 10
163 }});
164 };
165
166 /* 点击搜索 */
167 const handleFinish = (values) => {
168 const { createTime, name } = values;
169 dispatch({type: 'Application/changeState', payload: {
170 dataSearch: {
171 name,
172 createTimeBegin: createTime[0] ? createTime[0].startOf('day').valueOf() : undefined,
173 createTimeEnd: createTime[1] ? createTime[1].endOf('day').valueOf() : undefined,
174 }
175 }});
176 handleGetList(0, size);
177 };
178
179 /* 点击重置 */
180 const handleReset = () => {
181 dispatch({type: 'Application/resetSearch'});
182 handleGetList(0, 10);
183 };
184
185 /* 点击删除 */
186 const handleDel = ({ id }) => {
187 Modal.confirm({
188 title: '确定删除该应用吗?',
189 icon: <ExclamationCircleOutlined />,
190 okText: '确定',
191 cancelText: '取消',
192 onOk() {
193 dispatch({type: 'Application/delApplication', payload: {id}});
194 },
195 });
196 };
197
198 return (
199 <Card bordered={false}>
200 <SearchForm
201 dataSearch={dataSearch}
202 handleReset={handleReset}
203 handleFinish={handleFinish}
204 />
205 <div className="mt-24">
206 <DataTable
207 loading={loading}
208 handleDel={handleDel}
209 handleGetList={handleGetList}
210 dataApplication={dataApplication}
211 />
212 </div>
213 </Card>
214 )
215 };
216
217 export default connect(({ Application, loading }) => ({
218 ...Application,
219 loading: loading.effects["Application/getApplicationList"]
220 }))(Application)
...\ No newline at end of file ...\ No newline at end of file
1 import { routerRedux } from 'dva';
2 import { message } from 'antd';
3 import { getPermissionList } from '@/utils/utils';
4 import * as services from '@/services/application';
5
6 /* SerachParams */
7 const staticSearch = {
8 name: undefined,
9 createTimeBegin: undefined,
10 createTimeEnd: undefined
11 };
12
13 export default {
14 namespace: 'Application',
15 state: {
16 dataSearch: {
17 ...staticSearch
18 },
19 dataApplication: {
20 data: [],
21 page: 1,
22 size: 10,
23 totalItem: 0,
24 totalPage: 0,
25 },
26 dataModal: {
27 modalType: '',
28 modalShow: false,
29 modalData: {}
30 },
31 dataMenuList: [], // 获取所有的菜单
32 dataAuth: [], // 权限模块提交给后端的数据
33 dataExpandedKeys: [], // tree展开的数据
34 dataApplicationInfo: {}, // 应用相关信息
35 oldInfo: {}, // 上一次权限信息
36 },
37 effects: {
38 /* 获取应用列表 */
39 *getApplicationList({ payload }, { call, put, select }) {
40 const { dataSearch } = yield select(state => state.Application);
41 try {
42 const res = yield call(services.getApplicationList, {
43 page: 1,
44 size: 10,
45 ...dataSearch,
46 ...payload
47 });
48 if (res.status === 1) {
49 res.data.data = res.data.data || [];
50 yield put({type: 'changeState', payload: {
51 dataApplication: res.data
52 }})
53 }
54 } catch (err) {
55 console.error(err)
56 }
57 },
58 /* 删除应用 */
59 *delApplication({ payload }, { call, put, select }) {
60 const { dataApplication: { data = [], page, size } } = yield select(state => state.Application);
61 try {
62 const res = yield call(services.delApplication, payload);
63 if (res.status === 1) {
64 message.success("删除成功~");
65 yield put({type: 'getApplicationList', payload: {
66 size,
67 page: (data.length === 1 ? (page === 1 ? 1 : page - 1) : page)
68 }})
69 }
70 } catch (err) {
71 console.error(err)
72 }
73 },
74 /* 新增、修改应用 */
75 *updateApplication({ payload }, { call, put, select }) {
76 const { id } = payload;
77 try {
78 const res = yield call(services[!id ? "addApplication" : "updateApplication"], payload);
79 if (res.status === 1) {
80 message.success(!id ? "新增成功~" : "修改成功~");
81 yield put(routerRedux.push('/appliction'));
82 }
83 } catch (err) {
84 console.error(err)
85 }
86 },
87 /* 获取应用详情 */
88 *getApplicationInfo({ payload }, { call, put, select }) {
89 try {
90 const res = yield call(services.getApplicationInfo, payload);
91 if (res.status === 1) {
92 const { data, dataMenu, dataExpandedKeys } = getPermissionList(res.data.permissionList, true);
93 yield put({type: 'changeState', payload: {
94 dataApplicationInfo: res.data || {},
95 dataAuth: data,
96 dataMenuList: dataMenu,
97 dataExpandedKeys
98 }})
99 };
100 return res;
101 } catch (err) {
102 console.error(err)
103 }
104 }
105 },
106 reducers: {
107 changeState(state, { payload }) {
108 return {
109 ...state,
110 ...payload
111 }
112 },
113 cancelModal(state, { payload }) {
114 return {
115 ...state,
116 dataModal: {
117 modalType: '',
118 modalShow: false,
119 modalData: {}
120 }
121 }
122 },
123 resetSearch(state, { payload }) {
124 return {
125 ...state,
126 dataSearch: {
127 ...staticSearch
128 }
129 }
130 }
131 }
132 };
1 /**
2 * Author: llw
3 * Date: 2022.9.14
4 * Description: [行政执法案件详情]
5 */
6 import React, { useEffect } from 'react';
7 import { connect } from 'umi';
8 import { Drawer } from 'antd';
9 import { StyledText } from '@/components/style';
10 import { mapCause } from '@/constants';
11
12 const ModalEnforceMent = props => {
13 let {
14 dispatch,
15 dataModal: {
16 modalType,
17 modalShow,
18 modalData: { id },
19 },
20 enforcementInfo,
21 } = props;
22
23 useEffect(() => {
24 if (modalType === 'Enforce_Ment_Modal' && modalShow) {
25 dispatch({ type: 'Enforcement/getEventIllegalDetail', payload: { id } });
26 }
27 }, [modalType, modalShow]);
28
29 return (
30 <Drawer
31 title="详情"
32 placement="right"
33 width={900}
34 maskClosable={false}
35 onClose={() => {
36 dispatch({ type: 'Enforcement/cancelModal' });
37 }}
38 visible={modalType === 'Enforce_Ment_Modal' && modalShow}
39 footer={null}
40 >
41 <StyledText>
42 <div className="item-text">
43 <div className="title">立案单位</div>
44 <div className="desc">{enforcementInfo.company || '-'}</div>
45 </div>
46 <div className="item-text">
47 <div className="title">类型</div>
48 <div className="desc">
49 {(mapCause[enforcementInfo.type] && mapCause[enforcementInfo.type].label) || '-'}
50 </div>
51 </div>
52 <div className="item-text">
53 <div className="title">案件编号</div>
54 <div className="desc">{enforcementInfo.num || '-'}</div>
55 </div>
56 <div className="item-text">
57 <div className="title">呈批时间</div>
58 <div className="desc">{enforcementInfo.submitDate || '-'}</div>
59 </div>
60 <div className="item-text">
61 <div className="title">法定代表人及负责人</div>
62 <div className="desc">{enforcementInfo.placeUserName || '-'}</div>
63 </div>
64 <div className="item-text">
65 <div className="title">电话</div>
66 <div className="desc">{enforcementInfo.placeUserTel || '-'}</div>
67 </div>
68 <div className="item-text">
69 <div className="title">案件来源</div>
70 <div className="desc">{enforcementInfo.source || '-'}</div>
71 </div>
72 <div className="item-text">
73 <div className="title">执法人员及执法编号</div>
74 <div className="desc">{enforcementInfo.nameCode || '-'}</div>
75 </div>
76 <div className="item-text">
77 <div className="title">案由</div>
78 <div className="desc">{enforcementInfo.cause || '-'}</div>
79 </div>
80 <div className="item-text">
81 <div className="title">案发区域</div>
82 <div className="desc">{enforcementInfo.area || '-'}</div>
83 </div>
84 <div className="item-text" style={{ width: '100%' }}>
85 <div className="title">违法依据</div>
86 <div className="desc">{enforcementInfo.illegalBasis || '-'}</div>
87 </div>
88 <div className="item-text" style={{ width: '100%' }}>
89 <div className="title">处罚依据</div>
90 <div className="desc">{enforcementInfo.punishBasis || '-'}</div>
91 </div>
92 <div className="item-text" style={{ width: '100%' }}>
93 <div className="title">地址</div>
94 <div className="desc">{enforcementInfo.placeName || '-'}</div>
95 </div>
96 <div className="item-text" style={{ width: '100%' }}>
97 <div className="title">处罚内容</div>
98 <div className="desc">{enforcementInfo.contents || '-'}</div>
99 </div>
100 <div className="item-text" style={{ width: '100%' }}>
101 <div className="title">案卷相关文书</div>
102 <div className="desc">{enforcementInfo.instruments || '-'}</div>
103 </div>
104 </StyledText>
105 </Drawer>
106 );
107 };
108 export default connect(({ Enforcement }) => ({
109 ...Enforcement,
110 }))(ModalEnforceMent);
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!