9b990809 by charles

feat: 项目初始化

0 parents
Showing 150 changed files with 11803 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 } from 'react';
7 import { connect, Link } from 'umi';
8 import { Modal, Card, Breadcrumb, Form, Input, Button, Tree, message, Spin } from 'antd';
9 import { IconFontConfig } from '@/common';
10 import { EditOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
11 import ModalSetAuth from './ModalSetAuth';
12 import { getAuthData, getAllMenuParams, getPermissionList, getDelAuthData, getDragData } from '@/utils/utils';
13
14 const { TreeNode } = Tree;
15 const layout = {labelCol: { span: 3 }, wrapperCol: { span: 12 }};
16
17 const ApplicationInfo = props => {
18 const [form] = Form.useForm();
19
20 let {
21 loading,
22 isRepeat,
23 dispatch,
24 dataModal,
25 oldInfo = {},
26 dataAuth = [],
27 dataMenuList = [],
28 dataExpandedKeys = [],
29 location: { query: { id } },
30 } = props;
31
32 /* 获取应用详情 */
33 useEffect(() => {
34 if (id) {
35 dispatch({type: 'Application/getApplicationInfo', payload: {id}}).then(res => {
36 const { name, url, code, apiDomain } = res.data || {};
37 form.setFieldsValue({
38 name, url, code, apiDomain
39 });
40 });
41 }
42 return (() => {
43 dispatch({type: 'Application/changeState', payload: {
44 dataMenuList: [],
45 dataAuth: [],
46 dataExpandedKeys: [],
47 dataApplicationInfo: {}
48 }})
49 })
50 }, []);
51
52 /* 添加权限 */
53 const handleAddTemp = () => {
54 dispatch({type: 'Application/changeState', payload: {
55 dataModal: {
56 modalType: 'AUTH_SET_MODAL',
57 modalShow: true,
58 modalData: {isEdit: false}
59 }
60 }})
61 };
62
63 /* 关闭弹框 */
64 const handleCancelModal = () => {
65 dispatch({type: 'Application/changeState', payload: {oldInfo: {}}});
66 dispatch({type: 'Application/cancelModal'});
67 };
68
69 /* 保存权限配置 */
70 const handleAuthSetOk = (values) => {
71 const { isEdit } = values;
72 const { allCode = [], allMenuName = [] } = getAllMenuParams(dataAuth);
73
74 if (oldInfo.name !== values.name && allMenuName.includes(values.name)) {
75 message.warning("菜单名称不能重复~")
76 return false;
77 }
78 if (oldInfo.code !== values.code && allCode.includes(values.code)) {
79 message.warning("code不能重复~")
80 return false;
81 }
82
83 /* 上述条件不成立时才会走下面生成tree */
84 let params = {
85 subPermissionList: [],
86 ...values,
87 isEdit: undefined,
88 key: values.code,
89 oldCode: (oldInfo.parentCode && oldInfo.parentCode !== values.parentCode) ? oldInfo.parentCode : undefined
90 };
91 let dataSetAuth =
92 !isEdit
93 ?
94 getAuthData(dataAuth, params)
95 :
96 getDelAuthData(dataAuth, params, 'EDIT');
97 const { data, dataMenu, dataExpandedKeys } = dataSetAuth;
98 handleCancelModal();
99 dispatch({type: 'Application/changeState', payload: {
100 dataAuth: JSON.parse(JSON.stringify(data)),
101 dataMenuList: JSON.parse(JSON.stringify(dataMenu)),
102 dataExpandedKeys: JSON.parse(JSON.stringify(dataExpandedKeys))
103 }});
104 };
105
106 // TreeNode的DOM
107 const renderTreeNode = (data) => {
108 return data.map((item) => {
109 if (item.subPermissionList && item.subPermissionList.length) {
110 return (
111 <TreeNode
112 title={
113 <>
114 {item.name}
115 <EditOutlined title="编辑" className="ml-10" onClick={() => handleTreeNode(item, "INFO")} />
116 <DeleteOutlined title="删除" className="ml-10" onClick={() => handleTreeNode(item, "DEL")} />
117 </>
118 }
119 key={item.code}
120 icon={<IconFontConfig type={(item.type === 3 || item.type === "BTN") ? "icon-btn" : "icon-menu"} style={{fontSize: '14px'}} />}
121 >
122 {renderTreeNode(item.subPermissionList)}
123 </TreeNode>
124 )
125 }
126 return (
127 <TreeNode
128 title={
129 <>
130 {item.name}
131 <EditOutlined title="编辑" className="ml-10" onClick={() => handleTreeNode(item, "INFO")} />
132 <DeleteOutlined title="删除" className="ml-10" onClick={() => handleTreeNode(item, "DEL")} />
133 </>
134 }
135 key={item.code}
136 icon={<IconFontConfig type={(item.type === 3 || item.type === "BTN") ? "icon-btn" : "icon-menu"} style={{fontSize: '14px'}} />}
137 />
138 )
139 })
140 };
141
142 // 点击TreeNode
143 const handleTreeNode = (item, type) => {
144 if (type === "INFO") {
145 dispatch({type: 'Application/changeState', payload: {
146 oldInfo: {...item},
147 dataModal: {
148 modalType: 'AUTH_SET_MODAL',
149 modalShow: true,
150 modalData: {...item, isEdit: true}
151 }
152 }})
153 } else if (type === "DEL") {
154 const { data, dataMenu, dataExpandedKeys } = getDelAuthData(dataAuth, item, 'DEL');
155 Modal.confirm({
156 title: '确定删除吗?',
157 icon: <ExclamationCircleOutlined />,
158 okText: '确定',
159 cancelText: '取消',
160 onOk() {
161 dispatch({type: 'Application/changeState', payload: {
162 dataAuth: JSON.parse(JSON.stringify(data)),
163 dataMenuList: JSON.parse(JSON.stringify(dataMenu)),
164 dataExpandedKeys: JSON.parse(JSON.stringify(dataExpandedKeys))
165 }});
166 }
167 });
168 }
169 };
170
171 // 点击Tree
172 const handleOnExpand = (expandedKeys) => {
173 dispatch({type: 'Application/changeState', payload: {
174 dataExpandedKeys: expandedKeys
175 }});
176 };
177
178 // 保存应用
179 const handleSaveApplication = () => {
180 let permissionList = getPermissionList(dataAuth);
181 form.validateFields().then(values => {
182 dispatch({type: 'Application/updateApplication', payload: {
183 ...values,
184 id,
185 permissionList
186 }})
187 })
188 };
189
190 /* 拖拽排序 */
191 const onDrop = info => {
192 const dropKey = info.node.props.eventKey;
193 const dragKey = info.dragNode.props.eventKey;
194 const dropPos = info.node.props.pos.split('-');
195 const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
196
197 const loop = (data, key, callback) => {
198 for (let i = 0; i < data.length; i++) {
199 if (data[i].key === key) {
200 return callback(data[i], i, data);
201 }
202 if (data[i].subPermissionList) {
203 loop(data[i].subPermissionList, key, callback);
204 }
205 }
206 };
207 let dataStateAuth = JSON.parse(JSON.stringify(dataAuth));
208 const data = [...dataStateAuth];
209
210 // Find dragObject
211 let dragObj;
212 loop(data, dragKey, (item, index, arr) => {
213 arr.splice(index, 1);
214 dragObj = item;
215 });
216
217 if (!info.dropToGap) {
218 // Drop on the content
219 loop(data, dropKey, item => {
220 item.subPermissionList = item.subPermissionList || [];
221 // where to insert 示例添加到尾部,可以是随意位置
222 item.subPermissionList.push(dragObj);
223 });
224 } else if (
225 (info.node.props.subPermissionList || []).length > 0 &&
226 info.node.props.expanded &&
227 dropPosition === 1
228 ) {
229 loop(data, dropKey, item => {
230 item.subPermissionList = item.subPermissionList || [];
231 // where to insert 示例添加到头部,可以是随意位置
232 item.subPermissionList.unshift(dragObj);
233 });
234 } else {
235 let ar;
236 let i;
237 loop(data, dropKey, (item, index, arr) => {
238 ar = arr;
239 i = index;
240 });
241 if (dropPosition === -1) {
242 ar.splice(i, 0, dragObj);
243 } else {
244 ar.splice(i + 1, 0, dragObj);
245 }
246 }
247 let { dataDragAuth, dataMenu, dataExpandedKeys } = getDragData(data);
248 dispatch({type: 'Application/changeState', payload: {
249 dataAuth: JSON.parse(JSON.stringify(dataDragAuth.length > 0 ? dataDragAuth : dataAuth)),
250 dataMenuList: JSON.parse(JSON.stringify(dataMenu)),
251 dataExpandedKeys: JSON.parse(JSON.stringify(dataExpandedKeys))
252 }});
253 };
254
255 /* 清空所有权限 */
256 const handleRemoveAuth = () => {
257 Modal.confirm({
258 title: '确定清空该应用权限的相关数据吗?',
259 icon: <ExclamationCircleOutlined />,
260 okText: '确定',
261 cancelText: '取消',
262 onOk() {
263 dispatch({type: 'Application/changeState', payload: {
264 dataAuth: [],
265 dataMenuList: [],
266 dataExpandedKeys: []
267 }});
268 }
269 });
270 };
271
272 return (
273 <div className="box-info">
274 <Breadcrumb>
275 <Breadcrumb.Item><Link to="/appliction">应用列表</Link></Breadcrumb.Item>
276 <Breadcrumb.Item>{!id ? '新建应用' : '编辑应用'}</Breadcrumb.Item>
277 </Breadcrumb>
278 <Spin spinning={id ? loading : false}>
279 <Card bordered={false} className="mt-15">
280 <Form
281 {...layout}
282 form={form}
283 name="applicationInfo"
284 >
285 <Form.Item
286 label="应用名称"
287 name="name"
288 rules={[{ required: true, message: '请输入应用名称' }]}
289 >
290 <Input placeholder="请输入应用名称" />
291 </Form.Item>
292 <Form.Item
293 label="应用标识码"
294 name="code"
295 >
296 <Input placeholder="请输入应用标识码" />
297 </Form.Item>
298 <Form.Item
299 label="应用Api域名地址"
300 name="apiDomain"
301 >
302 <Input placeholder="请输入应用api域名地址" />
303 </Form.Item>
304 <Form.Item
305 label="应用链接地址"
306 name="url"
307 >
308 <Input placeholder="请输入应用链接地址" />
309 </Form.Item>
310 <Form.Item label="权限服务模板">
311 <Button onClick={handleAddTemp}>添加</Button>
312 </Form.Item>
313 <Form.Item label="权限预览" className="view-tree-auth">
314 {
315 dataAuth.length > 0 ?
316 (
317 <Tree
318 showLine
319 showIcon
320 draggable
321 selectedKeys={[]}
322 className="info-tree"
323 onExpand={handleOnExpand}
324 expandedKeys={dataExpandedKeys}
325 onDrop={onDrop}
326 >
327 {renderTreeNode(dataAuth)}
328 </Tree>
329 )
330 :
331 <span>暂无数据</span>
332 }
333 </Form.Item>
334 <Form.Item className="footer-btn" wrapperCol={{span: 24}}>
335 <Button disabled={!dataAuth.length} onClick={handleRemoveAuth} className="mr-10">清空权限</Button>
336 <Button loading={isRepeat} onClick={handleSaveApplication} type="primary" className="ml-10">保存应用</Button>
337 </Form.Item>
338 </Form>
339 </Card>
340 {/* 设置权限抽屉 */}
341 <ModalSetAuth
342 dataModal={dataModal}
343 dataMenuList={dataMenuList}
344 handleAuthSetOk={handleAuthSetOk}
345 handleCancelModal={handleCancelModal}
346 />
347 </Spin>
348 </div>
349 )
350 };
351
352 export default connect(({ Application, loading }) => ({
353 ...Application,
354 isRepeat: loading.effects["Application/updateApplication"],
355 loading: loading.effects["Application/getApplicationInfo"]
356 }))(ApplicationInfo);
...\ No newline at end of file ...\ No newline at end of file
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);
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
11 const ModalNetworkEvent = props => {
12 let {
13 dispatch,
14 dataModal: {
15 modalType,
16 modalShow,
17 modalData: { id },
18 },
19 baseGridInfo,
20 } = props;
21
22 useEffect(() => {
23 if (modalType === 'Network_Event_Modal' && modalShow) {
24 dispatch({ type: 'NetworkEvent/getBaseGridDetail', payload: { id } });
25 }
26 }, [modalType, modalShow]);
27
28 return (
29 <Drawer
30 title="详情"
31 placement="right"
32 width={600}
33 maskClosable={false}
34 onClose={() => {
35 dispatch({ type: 'NetworkEvent/cancelModal' });
36 }}
37 visible={modalType === 'Network_Event_Modal' && modalShow}
38 footer={null}
39 >
40 <StyledText>
41 <div className="item-text">
42 <div className="title">事件编号</div>
43 <div className="desc">{baseGridInfo.eventCode || '-'}</div>
44 </div>
45 <div className="item-text">
46 <div className="title">事件来源</div>
47 <div className="desc">{baseGridInfo.eventSource || '-'}</div>
48 </div>
49 <div className="item-text">
50 <div className="title">发起人</div>
51 <div className="desc">{baseGridInfo.sponsor || '-'}</div>
52 </div>
53 <div className="item-text">
54 <div className="title">发起组织</div>
55 <div className="desc">{baseGridInfo.sponsorOrg || '-'}</div>
56 </div>
57 <div className="item-text">
58 <div className="title">联系方式</div>
59 <div className="desc">{baseGridInfo.sponsorTel || '-'}</div>
60 </div>
61 <div className="item-text">
62 <div className="title">发起时间</div>
63 <div className="desc">{baseGridInfo.origTime || '-'}</div>
64 </div>
65 <div className="item-text">
66 <div className="title">处理时间</div>
67 <div className="desc">{baseGridInfo.processingTime || '-'}</div>
68 </div>
69 <div className="item-text">
70 <div className="title">截止时间</div>
71 <div className="desc">{baseGridInfo.deadTime || '-'}</div>
72 </div>
73 <div className="item-text">
74 <div className="title">事发时间</div>
75 <div className="desc">{baseGridInfo.incidentTime || '-'}</div>
76 </div>
77 <div className="item-text">
78 <div className="title">事发地址</div>
79 <div className="desc">{baseGridInfo.incidentAddress || '-'}</div>
80 </div>
81 <div className="item-text">
82 <div className="title">事发经度</div>
83 <div className="desc">{baseGridInfo.longitude || '-'}</div>
84 </div>
85 <div className="item-text">
86 <div className="title">事发维度</div>
87 <div className="desc">{baseGridInfo.latitude || '-'}</div>
88 </div>
89 <div className="item-text">
90 <div className="title">统一地址</div>
91 <div className="desc">{baseGridInfo.address || '-'}</div>
92 </div>
93 <div className="item-text">
94 <div className="title">事发区县</div>
95 <div className="desc">{baseGridInfo.county || '-'}</div>
96 </div>
97 <div className="item-text">
98 <div className="title">事发镇街</div>
99 <div className="desc">{baseGridInfo.town || '-'}</div>
100 </div>
101 <div className="item-text">
102 <div className="title">事发村社</div>
103 <div className="desc">{baseGridInfo.village || '-'}</div>
104 </div>
105 <div className="item-text">
106 <div className="title">事发网格</div>
107 <div className="desc">{baseGridInfo.grid || '-'}</div>
108 </div>
109 <div className="item-text">
110 <div className="title">事发小区</div>
111 <div className="desc">{baseGridInfo.quarters || '-'}</div>
112 </div>
113 <div className="item-text">
114 <div className="title">事项大类</div>
115 <div className="desc">{baseGridInfo.matterBig || '-'}</div>
116 </div>
117 <div className="item-text">
118 <div className="title">事项小类</div>
119 <div className="desc">{baseGridInfo.matterSmall || '-'}</div>
120 </div>
121 <div className="item-text">
122 <div className="title">事项细类</div>
123 <div className="desc">{baseGridInfo.matterFine || '-'}</div>
124 </div>
125 <div className="item-text">
126 <div className="title">事项编码</div>
127 <div className="desc">{baseGridInfo.matterCode || '-'}</div>
128 </div>
129 <div className="item-text">
130 <div className="title">事件级别</div>
131 <div className="desc">{baseGridInfo.matterLevel || '-'}</div>
132 </div>
133 <div className="item-text">
134 <div className="title">事项性质</div>
135 <div className="desc">{baseGridInfo.matterNature || '-'}</div>
136 </div>
137
138 <div className="item-text" style={{ width: '100%' }}>
139 <div className="title">事件状态</div>
140 <div className="desc">{baseGridInfo.stateName || '-'}</div>
141 </div>
142 <div className="item-text" style={{ width: '100%' }}>
143 <div className="title">事件详情</div>
144 <div className="desc">{baseGridInfo.matterDetailed || '-'}</div>
145 </div>
146 <div className="item-text" style={{ width: '100%' }}>
147 <div className="title">事件描述</div>
148 <div className="desc">{baseGridInfo.matterDescribe || '-'}</div>
149 </div>
150 </StyledText>
151 </Drawer>
152 );
153 };
154 export default connect(({ NetworkEvent }) => ({
155 ...NetworkEvent,
156 }))(ModalNetworkEvent);
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 { Card, Form, Input, Button, Table, Tooltip, DatePicker } from 'antd';
9 import { paginations } from '@/constants';
10 import { StyledPageContainer, StyledPageContent, StyledEllipsisWrap } from '@/components/style';
11 import { ZoomInOutlined } from '@ant-design/icons';
12 import moment from 'moment';
13
14 const FormItem = Form.Item;
15 const { RangePicker } = DatePicker;
16
17 /* SearchForm */
18 const SearchForm = props => {
19 const [form] = Form.useForm();
20 const {
21 dataSearch,
22 dataSearch: { startDate, endDate },
23 handleReset,
24 handleFinish,
25 } = props;
26
27 useEffect(() => {
28 form.setFieldsValue({
29 ...dataSearch,
30 createTime: [
31 startDate ? moment(startDate) : undefined,
32 endDate ? moment(endDate) : undefined,
33 ],
34 });
35 }, [dataSearch]);
36
37 /* 点击搜索 */
38
39 const onFinish = values => {
40 handleFinish(values);
41 };
42
43 /* 点击重置 */
44 const onReset = () => {
45 handleReset();
46 };
47
48 return (
49 <Form name="Form_Complaint" layout="inline" form={form} onFinish={onFinish}>
50 <FormItem label="公司名称" name="keyWord">
51 <Input placeholder="请输入公司名称" style={{ width: '250px' }} />
52 </FormItem>
53 <FormItem label="投诉举报时间" name="createTime">
54 <RangePicker allowClear={false} style={{ width: '300px' }} />
55 </FormItem>
56 <FormItem>
57 <Button type="primary" htmlType="submit" className="mr-15">
58 搜索
59 </Button>
60 <Button className="mr-15" htmlType="button" onClick={onReset}>
61 重置
62 </Button>
63 </FormItem>
64 </Form>
65 );
66 };
67
68 /* DataTable */
69 const DataTable = props => {
70 const {
71 loading,
72 handleGetList,
73 dataComplaint: { records = [], current, size, total },
74 } = props;
75
76 /* 点击分页 */
77 const handlePageChange = (current, size) => {
78 handleGetList(current, size);
79 };
80
81 const columns = [
82 {
83 title: '序号',
84 dataIndex: 'id',
85 align: 'center',
86 fixed: 'left',
87 width: 80,
88 render(t, r, idx) {
89 return (current - 1) * size + idx + 1;
90 },
91 },
92 {
93 title: '公司名称',
94 dataIndex: 'name',
95 align: 'center',
96 width: 200,
97 render: (t, r) => {
98 return t ? (
99 <Tooltip placement="top" title={t}>
100 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
101 </Tooltip>
102 ) : (
103 '-'
104 );
105 },
106 },
107 {
108 title: '公司地址',
109 dataIndex: 'address',
110 align: 'center',
111 width: 220,
112 render: (t, r) => {
113 return t ? (
114 <Tooltip placement="top" title={t}>
115 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
116 </Tooltip>
117 ) : (
118 '-'
119 );
120 },
121 },
122 {
123 title: '通讯地址',
124 dataIndex: 'postalAddress',
125 align: 'center',
126 width: 220,
127 render: (t, r) => {
128 return t ? (
129 <Tooltip placement="top" title={t}>
130 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
131 </Tooltip>
132 ) : (
133 '-'
134 );
135 },
136 },
137 {
138 title: '所属地区',
139 dataIndex: 'region',
140 align: 'center',
141 width: 160,
142 render: (t, r) => {
143 return t ? (
144 <Tooltip placement="top" title={t}>
145 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
146 </Tooltip>
147 ) : (
148 '-'
149 );
150 },
151 },
152 {
153 title: '负责人',
154 dataIndex: 'person',
155 align: 'center',
156 width: 150,
157 },
158 {
159 title: '身份证号码',
160 dataIndex: 'personCode',
161 align: 'center',
162 width: 220,
163 },
164 {
165 title: '负责人电话',
166 dataIndex: 'personTel',
167 align: 'center',
168 width: 150,
169 },
170 {
171 title: '联系人',
172 dataIndex: 'contacts',
173 align: 'center',
174 width: 150,
175 },
176 {
177 title: '联系人电话',
178 dataIndex: 'contactsTel',
179 align: 'center',
180 width: 180,
181 },
182 {
183 title: '主营范围',
184 dataIndex: 'mainScope',
185 align: 'center',
186 width: 180,
187 render: (t, r) => {
188 return t ? (
189 <Tooltip placement="top" title={t}>
190 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
191 </Tooltip>
192 ) : (
193 '-'
194 );
195 },
196 },
197 {
198 title: '备注',
199 dataIndex: 'remarks',
200 align: 'center',
201 width: 200,
202 render: (t, r) => {
203 return t ? (
204 <Tooltip placement="top" title={t}>
205 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
206 </Tooltip>
207 ) : (
208 '-'
209 );
210 },
211 },
212 // {
213 // title: '操作',
214 // align: 'center',
215 // fixed: 'right',
216 // width: 80,
217 // render(t, r) {
218 // return (
219 // <Tooltip placement="top" title="查看详情">
220 // <ZoomInOutlined style={{ cursor: 'pointer', fontSize: 18 }} onClick={() => {}} />
221 // </Tooltip>
222 // );
223 // },
224 // },
225 ];
226 const pagination = {
227 ...paginations,
228 total: total,
229 pageSize: size,
230 current,
231 showSizeChanger: total > 10,
232 onChange: (current, pageSize) => {
233 handlePageChange(current, pageSize);
234 },
235 onShowSizeChange: (current, pageSize) => {
236 handlePageChange(1, pageSize);
237 },
238 showTotal(total) {
239 return `总共 ${total} 条数据`;
240 },
241 };
242 return (
243 <Table
244 rowKey="id"
245 loading={loading}
246 dataSource={records}
247 columns={columns}
248 pagination={pagination}
249 scroll={{ x: 2000, y: `calc(100vh - 350px)` }}
250 />
251 );
252 };
253
254 /* Main */
255 const Complaint = props => {
256 const {
257 dispatch,
258 loading,
259 dataSearch,
260 dataComplaint,
261 dataComplaint: { size },
262 } = props;
263
264 useEffect(() => {
265 handleGetList(1, 10);
266 }, []);
267
268 /* 列表 */
269 const handleGetList = (current, size) => {
270 dispatch({
271 type: 'Complaint/getComplaintList',
272 payload: {
273 current: current || 1,
274 size: size || 10,
275 },
276 });
277 };
278
279 /* 点击搜索 */
280 const handleFinish = values => {
281 const { keyWord, createTime } = values;
282 dispatch({
283 type: 'Complaint/changeState',
284 payload: {
285 dataSearch: {
286 keyWord,
287 startDate: createTime[0]
288 ? moment(createTime[0].startOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
289 : undefined,
290 endDate: createTime[1]
291 ? moment(createTime[1].endOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
292 : undefined,
293 },
294 },
295 });
296 handleGetList(0, size);
297 };
298
299 /* 点击重置 */
300 const handleReset = () => {
301 dispatch({ type: 'Complaint/resetSearch' });
302 handleGetList(0, 10);
303 };
304
305 return (
306 <StyledPageContainer>
307 <StyledPageContent>
308 <Card bordered={false}>
309 <SearchForm
310 dataSearch={dataSearch}
311 handleReset={handleReset}
312 handleFinish={handleFinish}
313 />
314 <div className="mt-16">
315 <DataTable
316 loading={loading}
317 handleGetList={handleGetList}
318 dataComplaint={dataComplaint}
319 />
320 </div>
321 </Card>
322 </StyledPageContent>
323 </StyledPageContainer>
324 );
325 };
326
327 export default connect(({ Complaint, loading }) => ({
328 ...Complaint,
329 loading: loading.effects['Complaint/getComplaintList'],
330 }))(Complaint);
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 { Card, Form, Input, Button, Table, Tooltip, Upload, message, Select } from 'antd';
9 import { paginations } from '@/constants';
10 import {
11 StyledPageContainer,
12 StyledPageContent,
13 StyledEllipsisWrap,
14 StyledPageHeader,
15 } from '@/components/style';
16 import { ZoomInOutlined, UploadOutlined } from '@ant-design/icons';
17 import * as services from '@/services/data';
18 import { ExportFile } from '@/utils/utils';
19 import { mapCause, enumYear } from '@/constants';
20 import ModalEnforcement from './Modal/ModalEnforcement';
21
22 const FormItem = Form.Item;
23 const Option = Select.Option;
24
25 /* SearchForm */
26 const SearchForm = props => {
27 const [form] = Form.useForm();
28 const { dataSearch, handleReset, handleFinish } = props;
29
30 useEffect(() => {
31 form.setFieldsValue({
32 ...dataSearch,
33 });
34 }, [dataSearch]);
35
36 /* 点击搜索 */
37
38 const onFinish = values => {
39 handleFinish(values);
40 };
41
42 /* 点击重置 */
43 const onReset = () => {
44 handleReset();
45 };
46
47 return (
48 <Form name="Form_Enforcement" layout="inline" form={form} onFinish={onFinish}>
49 <FormItem label="当事人或法人姓名" name="placeName">
50 <Input placeholder="请输入当事人或法人姓名" style={{ width: '220px' }} />
51 </FormItem>
52 <FormItem label="办案单位" name="company">
53 <Input placeholder="请输入办案单位" style={{ width: '220px' }} />
54 </FormItem>
55 <FormItem label="案件类型" name="type">
56 <Select placeholder="请选择案件类型" style={{ width: '150px' }}>
57 <Option value="ALL">全部</Option>
58 {Object.values(mapCause).map(item => {
59 return (
60 <Option key={item.value} value={item.value}>
61 {item.label}
62 </Option>
63 );
64 })}
65 </Select>
66 </FormItem>
67 <FormItem label="处罚年份" name="yearDate">
68 <Select placeholder="请选择处罚年份" style={{ width: '150px' }} showSearch>
69 <Option value="ALL">全部</Option>
70 {enumYear().map(item => {
71 return (
72 <Option key={item.value} value={item.value}>
73 {item.label}
74 </Option>
75 );
76 })}
77 </Select>
78 </FormItem>
79 <FormItem>
80 <Button type="primary" htmlType="submit" className="mr-15">
81 搜索
82 </Button>
83 <Button className="mr-15" htmlType="button" onClick={onReset}>
84 重置
85 </Button>
86 </FormItem>
87 </Form>
88 );
89 };
90
91 /* DataTable */
92 const DataTable = props => {
93 const {
94 dispatch,
95 loading,
96 handleGetList,
97 dataEnforcement: { records = [], current, size, total },
98 } = props;
99
100 /* 点击分页 */
101 const handlePageChange = (current, size) => {
102 handleGetList(current, size);
103 };
104
105 const columns = [
106 {
107 title: '序号',
108 dataIndex: 'id',
109 align: 'center',
110 fixed: 'left',
111 width: 80,
112 render(t, r, idx) {
113 return (current - 1) * size + idx + 1;
114 },
115 },
116 {
117 title: '统一编号',
118 dataIndex: 'num',
119 align: 'center',
120 width: 200,
121 },
122 {
123 title: '案件类型',
124 dataIndex: 'type',
125 align: 'center',
126 width: 120,
127 render: (t, r) => {
128 return (mapCause[t] && mapCause[t].label) || '-';
129 },
130 },
131 {
132 title: '当事人',
133 dataIndex: 'placeUserName',
134 align: 'center',
135 width: 160,
136 render: (t, r) => {
137 return t ? (
138 <Tooltip placement="top" title={t}>
139 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
140 </Tooltip>
141 ) : (
142 '-'
143 );
144 },
145 },
146 {
147 title: '案由',
148 dataIndex: 'cause',
149 align: 'center',
150 width: 220,
151 render: (t, r) => {
152 return t ? (
153 <Tooltip placement="top" title={t}>
154 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
155 </Tooltip>
156 ) : (
157 '-'
158 );
159 },
160 },
161 {
162 title: '办案单位',
163 dataIndex: 'company',
164 align: 'center',
165 width: 160,
166 render: (t, r) => {
167 return t ? (
168 <Tooltip placement="top" title={t}>
169 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
170 </Tooltip>
171 ) : (
172 '-'
173 );
174 },
175 },
176 {
177 title: '立案时间',
178 dataIndex: 'filingDate',
179 align: 'center',
180 width: 160,
181 render: (t, r) => {
182 return t || '-';
183 },
184 },
185 {
186 title: '处罚时间',
187 dataIndex: 'punishDate',
188 align: 'center',
189 width: 160,
190 render: (t, r) => {
191 return t || '-';
192 },
193 },
194 {
195 title: '结案时间',
196 dataIndex: 'closeDate',
197 align: 'center',
198 width: 160,
199 render: (t, r) => {
200 return t || '-';
201 },
202 },
203 {
204 title: '操作',
205 align: 'center',
206 fixed: 'right',
207 width: 80,
208 render(t, r) {
209 return (
210 <Tooltip placement="top" title="查看详情">
211 <ZoomInOutlined
212 style={{ cursor: 'pointer', fontSize: 18 }}
213 onClick={() => {
214 dispatch({
215 type: 'Enforcement/changeState',
216 payload: {
217 dataModal: {
218 modalType: 'Enforce_Ment_Modal',
219 modalShow: true,
220 modalData: r,
221 },
222 },
223 });
224 }}
225 />
226 </Tooltip>
227 );
228 },
229 },
230 ];
231 const pagination = {
232 ...paginations,
233 total: total,
234 pageSize: size,
235 current,
236 showSizeChanger: total > 10,
237 onChange: (current, pageSize) => {
238 handlePageChange(current, pageSize);
239 },
240 onShowSizeChange: (current, pageSize) => {
241 handlePageChange(1, pageSize);
242 },
243 showTotal(total) {
244 return `总共 ${total} 条数据`;
245 },
246 };
247 return (
248 <Table
249 rowKey="id"
250 loading={loading}
251 dataSource={records}
252 columns={columns}
253 pagination={pagination}
254 scroll={{ x: 2000, y: `calc(100vh - 448px)` }}
255 />
256 );
257 };
258
259 /* Main */
260 const Enforcement = props => {
261 const {
262 dispatch,
263 loading,
264 loadingUpload,
265 dataSearch,
266 dataEnforcement,
267 dataEnforcement: { size },
268 } = props;
269
270 useEffect(() => {
271 handleGetList(1, 10);
272 }, []);
273
274 /* 列表 */
275 const handleGetList = (current, size) => {
276 dispatch({
277 type: 'Enforcement/getEnforcementList',
278 payload: {
279 current: current || 1,
280 size: size || 10,
281 },
282 });
283 };
284
285 /* 点击搜索 */
286 const handleFinish = values => {
287 dispatch({
288 type: 'Enforcement/changeState',
289 payload: {
290 dataSearch: {
291 ...values,
292 },
293 },
294 });
295 handleGetList(0, size);
296 };
297
298 /* 点击重置 */
299 const handleReset = () => {
300 dispatch({ type: 'Enforcement/resetSearch' });
301 handleGetList(0, 10);
302 };
303
304 /* 上传组件配置 */
305 const uploadProps = {
306 customRequest: file => {
307 const formData = new FormData();
308 formData.append('file', file.file);
309 dispatch({ type: 'Enforcement/importEventIllegal', payload: formData });
310 },
311 beforeUpload: file => {
312 const isLt50M = file.size / 1024 / 1024 < 50;
313 if (!isLt50M) {
314 message.error('文件大小需小于50M');
315 return false;
316 }
317 return isLt50M;
318 },
319 };
320
321 return (
322 <StyledPageContainer>
323 <StyledPageHeader border={true}>
324 <Upload {...uploadProps}>
325 <Button loading={loadingUpload} type="primary" icon={<UploadOutlined />}>
326 数据导入
327 </Button>
328 </Upload>
329 <Button
330 type="primary"
331 onClick={() => {
332 ExportFile('行政执法案件模版', services.exportEventIllegalTemplate, {});
333 }}
334 >
335 模版下载
336 </Button>
337 </StyledPageHeader>
338 <StyledPageContent>
339 <Card bordered={false}>
340 <SearchForm
341 dataSearch={dataSearch}
342 handleReset={handleReset}
343 handleFinish={handleFinish}
344 />
345 <div className="mt-16">
346 <DataTable
347 loading={loading}
348 dispatch={dispatch}
349 handleGetList={handleGetList}
350 dataEnforcement={dataEnforcement}
351 />
352 </div>
353 </Card>
354 </StyledPageContent>
355 <ModalEnforcement />
356 </StyledPageContainer>
357 );
358 };
359
360 export default connect(({ Enforcement, loading }) => ({
361 ...Enforcement,
362 loading: loading.effects['Enforcement/getEnforcementList'],
363 loadingUpload: loading.effects['Enforcement/importEventIllegal'],
364 }))(Enforcement);
1 import { Tabs } from 'antd';
2 import React, { useState } from 'react';
3 import Place from './place';
4 import Complaint from './complaint';
5 import Enforcement from './enforcement';
6 import NetworkEvent from './networkEvent';
7 import { StyledWapperTab } from '@/components/style';
8
9 const Data = () => {
10 const [activeKey, setActiveKey] = useState('1');
11 const [dataTab, setDataTab] = useState(() => {
12 return [
13 { label: '行政执法案件', value: '1' },
14 { label: '基层网络事件', value: '2' },
15 { label: '场所/网站备案信息', value: '3' },
16 { label: '投诉举报事件', value: '4' },
17 ];
18 });
19 return (
20 <StyledWapperTab>
21 <Tabs
22 defaultActiveKey={activeKey}
23 onChange={key => {
24 setActiveKey(key);
25 }}
26 >
27 {dataTab.map(item => {
28 return <Tabs.TabPane tab={item.label} key={item.value}></Tabs.TabPane>;
29 })}
30 </Tabs>
31 {activeKey === '1' ? <Enforcement /> : null}
32 {activeKey === '2' ? <NetworkEvent /> : null}
33 {activeKey === '3' ? <Place /> : null}
34 {activeKey === '4' ? <Complaint /> : null}
35 </StyledWapperTab>
36 );
37 };
38
39 export default Data;
1 import * as services from '@/services/data';
2 import moment from 'moment';
3
4 /* SerachParams */
5 const staticSearch = {
6 current: 1,
7 size: 10,
8 keyWord: undefined,
9 startDate: moment(new Date())
10 .add(-7, 'days')
11 .startOf('day'),
12 endDate: moment(new Date())
13 .add(0, 'days')
14 .endOf('day'),
15 };
16
17 export default {
18 namespace: 'Complaint',
19 state: {
20 dataSearch: {
21 ...staticSearch,
22 },
23 dataComplaint: {
24 records: [],
25 current: 1,
26 size: 10,
27 total: 0,
28 totalPage: 0,
29 },
30 },
31 effects: {
32 /* 获取投诉举报列表 */
33 *getComplaintList({ payload }, { call, put, select }) {
34 const {
35 dataSearch,
36 dataSearch: { startDate, endDate },
37 } = yield select(state => state.Complaint);
38 try {
39 const res = yield call(services.getComplaintList, {
40 current: 1,
41 size: 10,
42 ...dataSearch,
43 ...payload,
44 startDate: moment(startDate).format('YYYY-MM-DD HH:mm:ss'),
45 endDate: moment(endDate).format('YYYY-MM-DD HH:mm:ss'),
46 });
47 if (res.code === 0) {
48 res.data.records =
49 res.data.records.map(item => {
50 return {
51 ...(item.placeVo || {}),
52 ...item,
53 };
54 }) || [];
55 yield put({
56 type: 'changeState',
57 payload: {
58 dataComplaint: res.data,
59 },
60 });
61 }
62 } catch (err) {
63 console.error(err);
64 }
65 },
66 },
67 reducers: {
68 changeState(state, { payload }) {
69 return {
70 ...state,
71 ...payload,
72 };
73 },
74 resetSearch(state, { payload }) {
75 return {
76 ...state,
77 dataSearch: {
78 ...staticSearch,
79 },
80 };
81 },
82 },
83 };
1 import * as services from '@/services/data';
2 import { message } from 'antd';
3 import moment from 'moment';
4
5 /* SerachParams */
6 const staticSearch = {
7 current: 1,
8 size: 10,
9 placeName: undefined,
10 company: undefined,
11 type: 'ALL',
12 yearDate: 'ALL',
13 };
14
15 export default {
16 namespace: 'Enforcement',
17 state: {
18 dataSearch: {
19 ...staticSearch,
20 },
21 dataEnforcement: {
22 records: [],
23 current: 1,
24 size: 10,
25 total: 0,
26 totalPage: 0,
27 },
28 dataModal: {
29 modalType: '',
30 modalShow: false,
31 modalData: {},
32 },
33 enforcementInfo: {},
34 },
35 effects: {
36 /* 获取行政执法列表 */
37 *getEnforcementList({ payload }, { call, put, select }) {
38 const {
39 dataSearch,
40 dataSearch: { type, yearDate },
41 } = yield select(state => state.Enforcement);
42 try {
43 const res = yield call(services.getEventIllegalList, {
44 current: 1,
45 size: 10,
46 ...dataSearch,
47 ...payload,
48 type: type === 'ALL' ? '' : type,
49 yearDate: yearDate === 'ALL' ? '' : yearDate,
50 });
51 if (res.code === 0) {
52 res.data.records =
53 res.data.records.map(item => {
54 return {
55 ...(item.placeVo || {}),
56 ...item,
57 };
58 }) || [];
59 yield put({
60 type: 'changeState',
61 payload: {
62 dataEnforcement: res.data,
63 },
64 });
65 }
66 } catch (err) {
67 console.error(err);
68 }
69 },
70 /* 行政执法案件导入 */
71 *importEventIllegal({ payload }, { call, put, select }) {
72 try {
73 const res = yield call(services.importEventIllegal, payload);
74 if (res.code === 0) {
75 message.success('导入成功');
76 yield put({ type: 'getEnforcementList' });
77 }
78 } catch (err) {
79 console.error(err);
80 }
81 },
82 /* 获取详情 */
83 *getEventIllegalDetail({ payload }, { call, put, select }) {
84 try {
85 const res = yield call(services.getEventIllegalDetail, payload);
86 if (res.code === 0) {
87 yield put({ type: 'changeState', payload: { enforcementInfo: res.data } });
88 }
89 } catch (err) {
90 console.error(err);
91 }
92 },
93 },
94 reducers: {
95 changeState(state, { payload }) {
96 return {
97 ...state,
98 ...payload,
99 };
100 },
101 cancelModal(state, { payload }) {
102 return {
103 ...state,
104 dataModal: {
105 modalType: '',
106 modalShow: false,
107 modalData: {},
108 },
109 };
110 },
111 resetSearch(state, { payload }) {
112 return {
113 ...state,
114 dataSearch: {
115 ...staticSearch,
116 },
117 };
118 },
119 },
120 };
1 import * as services from '@/services/data';
2 import moment from 'moment';
3
4 /* SerachParams */
5 const staticSearch = {
6 current: 1,
7 size: 10,
8 eventCode: undefined,
9 keyWord: undefined,
10 region: 'ALL',
11 sponsor: 'ALL',
12 handledBy: 'ALL',
13 sponsorOrg: 'ALL',
14 matterBig: 'ALL',
15 eventSource: 'ALL',
16 state: 'ALL',
17 startDate: moment(new Date())
18 .add(-7, 'days')
19 .startOf('day'),
20 endDate: moment(new Date())
21 .add(0, 'days')
22 .endOf('day'),
23 };
24
25 export default {
26 namespace: 'NetworkEvent',
27 state: {
28 dataSearch: {
29 ...staticSearch,
30 },
31 dataNetworkEvent: {
32 records: [],
33 current: 1,
34 size: 10,
35 total: 0,
36 totalPage: 0,
37 },
38 dataModal: {
39 modalType: '',
40 modalShow: false,
41 modalData: {},
42 },
43 baseGridInfo: {}, // 基层网格详情
44 eventSourceList: [], // 事件源
45 matterBigList: [], // 事件类型
46 handledByList: [], // 处理人
47 sponsorList: [], // 发起人
48 sponsorOrgList: [], // 发起组织
49 },
50 effects: {
51 /* 获取基层网络列表 */
52 *getNetworkEventList({ payload }, { call, put, select }) {
53 const {
54 dataSearch,
55 dataSearch: {
56 region,
57 sponsor,
58 handledBy,
59 sponsorOrg,
60 matterBig,
61 eventSource,
62 state,
63 startDate,
64 endDate,
65 },
66 } = yield select(state => state.NetworkEvent);
67 try {
68 const res = yield call(services.getNetworkEventList, {
69 current: 1,
70 size: 10,
71 ...dataSearch,
72 ...payload,
73 startDate: moment(startDate).format('YYYY-MM-DD HH:mm:ss'),
74 endDate: moment(endDate).format('YYYY-MM-DD HH:mm:ss'),
75 region: region === 'ALL' ? '' : region,
76 sponsor: sponsor === 'ALL' ? '' : sponsor,
77 handledBy: handledBy === 'ALL' ? '' : handledBy,
78 sponsorOrg: sponsorOrg === 'ALL' ? '' : sponsorOrg,
79 matterBig: matterBig === 'ALL' ? '' : matterBig,
80 eventSource: eventSource === 'ALL' ? '' : eventSource,
81 state: state === 'ALL' ? '' : state,
82 });
83 if (res.code === 0) {
84 res.data.records =
85 res.data.records.map(item => {
86 return {
87 ...(item.placeVo || {}),
88 ...item,
89 };
90 }) || [];
91 yield put({
92 type: 'changeState',
93 payload: {
94 dataNetworkEvent: res.data,
95 },
96 });
97 }
98 } catch (err) {
99 console.error(err);
100 }
101 },
102 /* 获取搜索条件 */
103 *getBaseGridSponsor({ payload }, { call, put, select }) {
104 try {
105 const res = yield call(services.getBaseGridSponsor);
106 if (res.code === 0) {
107 res.data = res.data || {};
108 yield put({
109 type: 'changeState',
110 payload: {
111 eventSourceList: res.data.eventSourceList || [],
112 handledByList: res.data.handledByList || [],
113 sponsorList: res.data.sponsorList || [],
114 sponsorOrgList: res.data.sponsorOrgList || [],
115 matterBigList: res.data.matterBigList || [],
116 },
117 });
118 }
119 } catch (err) {
120 console.error(err);
121 }
122 },
123 /* 获取详情 */
124 *getBaseGridDetail({ payload }, { call, put, select }) {
125 try {
126 const res = yield call(services.getBaseGridDetail, payload);
127 if (res.code === 0) {
128 yield put({
129 type: 'changeState',
130 payload: { baseGridInfo: res.data || {} },
131 });
132 }
133 } catch (err) {
134 console.error(err);
135 }
136 },
137 },
138 reducers: {
139 changeState(state, { payload }) {
140 return {
141 ...state,
142 ...payload,
143 };
144 },
145 cancelModal(state, { payload }) {
146 return {
147 ...state,
148 dataModal: {
149 modalType: '',
150 modalShow: false,
151 modalData: {},
152 },
153 };
154 },
155 resetSearch(state, { payload }) {
156 return {
157 ...state,
158 dataSearch: {
159 ...staticSearch,
160 },
161 };
162 },
163 },
164 };
1 import * as services from '@/services/data';
2 import { message } from 'antd';
3
4 /* SerachParams */
5 const staticSearch = {
6 current: 1,
7 size: 10,
8 name: undefined,
9 };
10
11 export default {
12 namespace: 'Place',
13 state: {
14 dataSearch: {
15 ...staticSearch,
16 },
17 dataPlace: {
18 records: [],
19 current: 1,
20 size: 10,
21 total: 0,
22 totalPage: 0,
23 },
24 },
25 effects: {
26 /* 获取场所列表 */
27 *getPlaceList({ payload }, { call, put, select }) {
28 const { dataSearch } = yield select(state => state.Place);
29 try {
30 const res = yield call(services.getPlaceList, {
31 current: 1,
32 size: 10,
33 ...dataSearch,
34 ...payload,
35 });
36 if (res.code === 0) {
37 res.data.records = res.data.records || [];
38 yield put({
39 type: 'changeState',
40 payload: {
41 dataPlace: res.data,
42 },
43 });
44 }
45 } catch (err) {
46 console.error(err);
47 }
48 },
49 /* 场所导入 */
50 *importPlace({ payload }, { call, put, select }) {
51 try {
52 const res = yield call(services.importPlace, payload);
53 if (res.code === 0) {
54 message.success('导入成功');
55 yield put({ type: 'getPlaceList' });
56 }
57 } catch (err) {
58 console.error(err);
59 }
60 },
61 },
62 reducers: {
63 changeState(state, { payload }) {
64 return {
65 ...state,
66 ...payload,
67 };
68 },
69 resetSearch(state, { payload }) {
70 return {
71 ...state,
72 dataSearch: {
73 ...staticSearch,
74 },
75 };
76 },
77 },
78 };
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 { Card, Form, Input, Button, Table, Tooltip, Select, DatePicker } from 'antd';
9 import { paginations, mapEventStatus, enumArea } from '@/constants';
10 import { StyledPageContainer, StyledPageContent, StyledEllipsisWrap } from '@/components/style';
11 import { ZoomInOutlined } from '@ant-design/icons';
12 import moment from 'moment';
13 import ModalNetworkEvent from './Modal/ModalNetworkEvent';
14
15 const { RangePicker } = DatePicker;
16 const FormItem = Form.Item;
17 const Option = Select.Option;
18
19 /* SearchForm */
20 const SearchForm = props => {
21 const [form] = Form.useForm();
22 const {
23 dataSearch,
24 dataSearch: { startDate, endDate },
25 handleReset,
26 handleFinish,
27 eventSourceList,
28 matterBigList,
29 handledByList,
30 sponsorList,
31 sponsorOrgList,
32 } = props;
33
34 useEffect(() => {
35 form.setFieldsValue({
36 ...dataSearch,
37 createTime: [
38 startDate ? moment(startDate) : undefined,
39 endDate ? moment(endDate) : undefined,
40 ],
41 });
42 }, [dataSearch]);
43
44 /* 点击搜索 */
45
46 const onFinish = values => {
47 handleFinish(values);
48 };
49
50 /* 点击重置 */
51 const onReset = () => {
52 handleReset();
53 };
54
55 return (
56 <Form name="Form_NetworkEvent" layout="inline" form={form} onFinish={onFinish}>
57 <FormItem label="事件编号" name="eventCode">
58 <Input placeholder="请输入事件编号" style={{ width: '200px' }} />
59 </FormItem>
60 <FormItem label="事件关键字" name="keyWord">
61 <Input placeholder="请输入事件关键字" style={{ width: '200px' }} />
62 </FormItem>
63 <FormItem label="区域" name="region">
64 <Select placeholder="请选择区域" style={{ width: '150px' }}>
65 <Option value="ALL">全部</Option>
66 {enumArea.map(item => {
67 return (
68 <Option key={item} value={item}>
69 {item}
70 </Option>
71 );
72 })}
73 </Select>
74 </FormItem>
75 <FormItem label="发起人" name="sponsor">
76 <Select placeholder="请选择发起人" style={{ width: '150px' }}>
77 <Option value="ALL">全部</Option>
78 {sponsorList.map(item => {
79 return (
80 <Option key={item} value={item}>
81 {item}
82 </Option>
83 );
84 })}
85 </Select>
86 </FormItem>
87 <FormItem label="处理人" name="handledBy">
88 <Select placeholder="请选择处理人" style={{ width: '150px' }}>
89 <Option value="ALL">全部</Option>
90 {handledByList.map(item => {
91 return (
92 <Option key={item} value={item}>
93 {item}
94 </Option>
95 );
96 })}
97 </Select>
98 </FormItem>
99 <FormItem label="发起组织" name="sponsorOrg">
100 <Select placeholder="请选择发起组织" style={{ width: '150px' }}>
101 <Option value="ALL">全部</Option>
102 {sponsorOrgList.map(item => {
103 return (
104 <Option key={item} value={item}>
105 {item}
106 </Option>
107 );
108 })}
109 </Select>
110 </FormItem>
111 <FormItem label="事件类型" name="matterBig">
112 <Select placeholder="请选择事件类型" style={{ width: '150px' }}>
113 <Option value="ALL">全部</Option>
114 {matterBigList.map(item => {
115 return (
116 <Option key={item} value={item}>
117 {item}
118 </Option>
119 );
120 })}
121 </Select>
122 </FormItem>
123 <FormItem label="事件来源" name="eventSource">
124 <Select placeholder="请选择事件来源" style={{ width: '150px' }}>
125 <Option value="ALL">全部</Option>
126 {eventSourceList.map(item => {
127 return (
128 <Option key={item} value={item}>
129 {item}
130 </Option>
131 );
132 })}
133 </Select>
134 </FormItem>
135 <FormItem label="事件状态" name="state">
136 <Select placeholder="请选择事件状态" style={{ width: '150px' }}>
137 <Option value="ALL">全部</Option>
138 {Object.values(mapEventStatus).map(item => {
139 const { label, value } = item;
140 return (
141 <Option key={value} value={value}>
142 {label}
143 </Option>
144 );
145 })}
146 </Select>
147 </FormItem>
148 <FormItem label="发起时间" name="createTime">
149 <RangePicker allowClear={false} style={{ width: '320px' }} />
150 </FormItem>
151 <FormItem>
152 <Button type="primary" htmlType="submit" className="mr-15">
153 搜索
154 </Button>
155 <Button className="mr-15" htmlType="button" onClick={onReset}>
156 重置
157 </Button>
158 </FormItem>
159 </Form>
160 );
161 };
162
163 /* DataTable */
164 const DataTable = props => {
165 const {
166 dispatch,
167 loading,
168 handleGetList,
169 dataNetworkEvent: { records = [], current, size, total },
170 } = props;
171
172 /* 点击分页 */
173 const handlePageChange = (current, size) => {
174 handleGetList(current, size);
175 };
176
177 const columns = [
178 {
179 title: '序号',
180 dataIndex: 'id',
181 align: 'center',
182 fixed: 'left',
183 width: 80,
184 render(t, r, idx) {
185 return (current - 1) * size + idx + 1;
186 },
187 },
188 {
189 title: '事件编号',
190 dataIndex: 'eventCode',
191 align: 'center',
192 width: 180,
193 render: (t, r) => {
194 return t || '-';
195 },
196 },
197 {
198 title: '镇街',
199 dataIndex: 'town',
200 align: 'center',
201 width: 180,
202 render: (t, r) => {
203 return t || '-';
204 },
205 },
206 {
207 title: '村庄',
208 dataIndex: 'village',
209 align: 'center',
210 width: 180,
211 render: (t, r) => {
212 return t || '-';
213 },
214 },
215 {
216 title: '网络',
217 dataIndex: 'grid',
218 align: 'center',
219 width: 160,
220 render: (t, r) => {
221 return t || '-';
222 },
223 },
224 {
225 title: '发起人',
226 dataIndex: 'sponsor',
227 align: 'center',
228 width: 140,
229 },
230 {
231 title: '事件来源',
232 dataIndex: 'eventSource',
233 align: 'center',
234 width: 140,
235 },
236 {
237 title: '事件级别',
238 dataIndex: 'matterLevel',
239 align: 'center',
240 width: 140,
241 },
242 {
243 title: '事件大类',
244 dataIndex: 'matterBig',
245 align: 'center',
246 width: 140,
247 },
248 {
249 title: '事件小类',
250 dataIndex: 'matterSmall',
251 align: 'center',
252 width: 140,
253 },
254 {
255 title: '事件描述',
256 dataIndex: 'matterDescribe',
257 align: 'center',
258 width: 220,
259 render: (t, r) => {
260 return t ? (
261 <Tooltip placement="top" title={t}>
262 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
263 </Tooltip>
264 ) : (
265 '-'
266 );
267 },
268 },
269 {
270 title: '发起事件时间',
271 dataIndex: 'origTime',
272 align: 'center',
273 width: 200,
274 },
275 {
276 title: '事件状态',
277 dataIndex: 'stateName',
278 align: 'center',
279 width: 160,
280 },
281 {
282 title: '操作',
283 align: 'center',
284 fixed: 'right',
285 width: 80,
286 render(t, r) {
287 return (
288 <Tooltip placement="top" title="查看详情">
289 <ZoomInOutlined
290 style={{ cursor: 'pointer', fontSize: 18 }}
291 onClick={() => {
292 dispatch({
293 type: 'NetworkEvent/changeState',
294 payload: {
295 dataModal: {
296 modalType: 'Network_Event_Modal',
297 modalShow: true,
298 modalData: r,
299 },
300 },
301 });
302 }}
303 />
304 </Tooltip>
305 );
306 },
307 },
308 ];
309 const pagination = {
310 ...paginations,
311 total: total,
312 pageSize: size,
313 current,
314 showSizeChanger: total > 10,
315 onChange: (current, pageSize) => {
316 handlePageChange(current, pageSize);
317 },
318 onShowSizeChange: (current, pageSize) => {
319 handlePageChange(1, pageSize);
320 },
321 showTotal(total) {
322 return `总共 ${total} 条数据`;
323 },
324 };
325 return (
326 <Table
327 rowKey="id"
328 loading={loading}
329 dataSource={records}
330 columns={columns}
331 pagination={pagination}
332 scroll={{ x: 2000, y: `calc(100vh - 446px)` }}
333 />
334 );
335 };
336
337 /* Main */
338 const NetworkEvent = props => {
339 const {
340 dispatch,
341 loading,
342 dataNetworkEvent,
343 dataNetworkEvent: { size },
344 } = props;
345
346 useEffect(() => {
347 handleGetList(1, 10);
348 dispatch({ type: 'NetworkEvent/getBaseGridSponsor' });
349 }, []);
350
351 /* 列表 */
352 const handleGetList = (current, size) => {
353 dispatch({
354 type: 'NetworkEvent/getNetworkEventList',
355 payload: {
356 current: current || 1,
357 size: size || 10,
358 },
359 });
360 };
361
362 /* 点击搜索 */
363 const handleFinish = values => {
364 const { createTime } = values;
365 dispatch({
366 type: 'NetworkEvent/changeState',
367 payload: {
368 dataSearch: {
369 ...values,
370 startDate: createTime[0]
371 ? moment(createTime[0].startOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
372 : undefined,
373 endDate: createTime[1]
374 ? moment(createTime[1].endOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
375 : undefined,
376 createTime: undefined,
377 },
378 },
379 });
380 handleGetList(0, size);
381 };
382
383 /* 点击重置 */
384 const handleReset = () => {
385 dispatch({ type: 'NetworkEvent/resetSearch' });
386 handleGetList(0, 10);
387 };
388
389 return (
390 <StyledPageContainer>
391 <StyledPageContent>
392 <Card bordered={false}>
393 <SearchForm handleReset={handleReset} handleFinish={handleFinish} {...props} />
394 <div className="mt-16">
395 <DataTable
396 dispatch={dispatch}
397 loading={loading}
398 handleGetList={handleGetList}
399 dataNetworkEvent={dataNetworkEvent}
400 />
401 </div>
402 </Card>
403 </StyledPageContent>
404 <ModalNetworkEvent />
405 </StyledPageContainer>
406 );
407 };
408
409 export default connect(({ NetworkEvent, loading }) => ({
410 ...NetworkEvent,
411 loading: loading.effects['NetworkEvent/getNetworkEventList'],
412 }))(NetworkEvent);
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 { Card, Form, Input, Button, Table, Tooltip, Upload, message } from 'antd';
9 import { paginations } from '@/constants';
10 import {
11 StyledPageContainer,
12 StyledPageContent,
13 StyledEllipsisWrap,
14 StyledPageHeader,
15 } from '@/components/style';
16 import { ZoomInOutlined, UploadOutlined } from '@ant-design/icons';
17 import * as services from '@/services/data';
18 import { ExportFile } from '@/utils/utils';
19
20 const FormItem = Form.Item;
21
22 /* SearchForm */
23 const SearchForm = props => {
24 const [form] = Form.useForm();
25 const { dataSearch, handleReset, handleFinish } = props;
26
27 useEffect(() => {
28 form.setFieldsValue({
29 ...dataSearch,
30 });
31 }, [dataSearch]);
32
33 /* 点击搜索 */
34
35 const onFinish = values => {
36 handleFinish(values);
37 };
38
39 /* 点击重置 */
40 const onReset = () => {
41 handleReset();
42 };
43
44 return (
45 <Form name="Form_Place" layout="inline" form={form} onFinish={onFinish}>
46 <FormItem label="公司名称" name="name">
47 <Input placeholder="请输入公司名称" style={{ width: '250px' }} />
48 </FormItem>
49 <FormItem>
50 <Button type="primary" htmlType="submit" className="mr-15">
51 搜索
52 </Button>
53 <Button className="mr-15" htmlType="button" onClick={onReset}>
54 重置
55 </Button>
56 </FormItem>
57 </Form>
58 );
59 };
60
61 /* DataTable */
62 const DataTable = props => {
63 const {
64 loading,
65 handleGetList,
66 dataPlace: { records = [], current, size, total },
67 } = props;
68
69 /* 点击分页 */
70 const handlePageChange = (current, size) => {
71 handleGetList(current, size);
72 };
73
74 const columns = [
75 {
76 title: '序号',
77 dataIndex: 'id',
78 align: 'center',
79 fixed: 'left',
80 width: 80,
81 render(t, r, idx) {
82 return (current - 1) * size + idx + 1;
83 },
84 },
85 {
86 title: '公司名称',
87 dataIndex: 'name',
88 align: 'center',
89 width: 200,
90 render: (t, r) => {
91 return t ? (
92 <Tooltip placement="top" title={t}>
93 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
94 </Tooltip>
95 ) : (
96 '-'
97 );
98 },
99 },
100 {
101 title: '公司地址',
102 dataIndex: 'address',
103 align: 'center',
104 width: 220,
105 render: (t, r) => {
106 return t ? (
107 <Tooltip placement="top" title={t}>
108 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
109 </Tooltip>
110 ) : (
111 '-'
112 );
113 },
114 },
115 {
116 title: '通讯地址',
117 dataIndex: 'postalAddress',
118 align: 'center',
119 width: 220,
120 render: (t, r) => {
121 return t ? (
122 <Tooltip placement="top" title={t}>
123 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
124 </Tooltip>
125 ) : (
126 '-'
127 );
128 },
129 },
130 {
131 title: '所属地区',
132 dataIndex: 'region',
133 align: 'center',
134 width: 160,
135 render: (t, r) => {
136 return t ? (
137 <Tooltip placement="top" title={t}>
138 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
139 </Tooltip>
140 ) : (
141 '-'
142 );
143 },
144 },
145 {
146 title: '负责人',
147 dataIndex: 'person',
148 align: 'center',
149 width: 150,
150 },
151 {
152 title: '身份证号码',
153 dataIndex: 'personCode',
154 align: 'center',
155 width: 220,
156 },
157 {
158 title: '负责人电话',
159 dataIndex: 'personTel',
160 align: 'center',
161 width: 150,
162 },
163 {
164 title: '联系人',
165 dataIndex: 'contacts',
166 align: 'center',
167 width: 150,
168 },
169 {
170 title: '联系人电话',
171 dataIndex: 'contactsTel',
172 align: 'center',
173 width: 180,
174 },
175 {
176 title: '主营范围',
177 dataIndex: 'mainScope',
178 align: 'center',
179 width: 180,
180 render: (t, r) => {
181 return t ? (
182 <Tooltip placement="top" title={t}>
183 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
184 </Tooltip>
185 ) : (
186 '-'
187 );
188 },
189 },
190 {
191 title: '备注',
192 dataIndex: 'remarks',
193 align: 'center',
194 width: 200,
195 render: (t, r) => {
196 return t ? (
197 <Tooltip placement="top" title={t}>
198 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
199 </Tooltip>
200 ) : (
201 '-'
202 );
203 },
204 },
205 // {
206 // title: '操作',
207 // align: 'center',
208 // fixed: 'right',
209 // width: 80,
210 // render(t, r) {
211 // return (
212 // <Tooltip placement="top" title="查看详情">
213 // <ZoomInOutlined style={{ cursor: 'pointer', fontSize: 18 }} onClick={() => {}} />
214 // </Tooltip>
215 // );
216 // },
217 // },
218 ];
219 const pagination = {
220 ...paginations,
221 total: total,
222 pageSize: size,
223 current,
224 showSizeChanger: total > 10,
225 onChange: (current, pageSize) => {
226 handlePageChange(current, pageSize);
227 },
228 onShowSizeChange: (current, pageSize) => {
229 handlePageChange(1, pageSize);
230 },
231 showTotal(total) {
232 return `总共 ${total} 条数据`;
233 },
234 };
235 return (
236 <Table
237 rowKey="id"
238 loading={loading}
239 dataSource={records}
240 columns={columns}
241 pagination={pagination}
242 scroll={{ x: 2000, y: `calc(100vh - 400px)` }}
243 />
244 );
245 };
246
247 /* Main */
248 const Place = props => {
249 const {
250 dispatch,
251 loading,
252 loadingUpload,
253 dataSearch,
254 dataPlace,
255 dataPlace: { size },
256 } = props;
257
258 useEffect(() => {
259 handleGetList(1, 10);
260 }, []);
261
262 /* 列表 */
263 const handleGetList = (current, size) => {
264 dispatch({
265 type: 'Place/getPlaceList',
266 payload: {
267 current: current || 1,
268 size: size || 10,
269 },
270 });
271 };
272
273 /* 点击搜索 */
274 const handleFinish = values => {
275 const { name } = values;
276 dispatch({
277 type: 'Place/changeState',
278 payload: {
279 dataSearch: { name },
280 },
281 });
282 handleGetList(0, size);
283 };
284
285 /* 点击重置 */
286 const handleReset = () => {
287 dispatch({ type: 'Place/resetSearch' });
288 handleGetList(0, 10);
289 };
290
291 /* 上传组件配置 */
292 const uploadProps = {
293 customRequest: file => {
294 const formData = new FormData();
295 formData.append('file', file.file);
296 dispatch({ type: 'Place/importPlace', payload: formData });
297 },
298 beforeUpload: file => {
299 const isLt50M = file.size / 1024 / 1024 < 50;
300 if (!isLt50M) {
301 message.error('文件大小需小于50M');
302 return false;
303 }
304 return isLt50M;
305 },
306 };
307
308 return (
309 <StyledPageContainer>
310 <StyledPageHeader border={true}>
311 <Upload {...uploadProps}>
312 <Button loading={loadingUpload} type="primary" icon={<UploadOutlined />}>
313 数据导入
314 </Button>
315 </Upload>
316 <Button
317 type="primary"
318 onClick={() => {
319 ExportFile('场所/网站备案信息模版', services.exportPlaceTemplate, {});
320 }}
321 >
322 模版下载
323 </Button>
324 </StyledPageHeader>
325 <StyledPageContent>
326 <Card bordered={false}>
327 <SearchForm
328 dataSearch={dataSearch}
329 handleReset={handleReset}
330 handleFinish={handleFinish}
331 />
332 <div className="mt-16">
333 <DataTable loading={loading} handleGetList={handleGetList} dataPlace={dataPlace} />
334 </div>
335 </Card>
336 </StyledPageContent>
337 </StyledPageContainer>
338 );
339 };
340
341 export default connect(({ Place, loading }) => ({
342 ...Place,
343 loading: loading.effects['Place/getPlaceList'],
344 loadingUpload: loading.effects['Place/importPlace'],
345 }))(Place);
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, Tooltip, Table } from 'antd';
9 import { paginations, mapStatus, mapRiskType, mapRiskSourceType } from '@/constants';
10 import { StyledEllipsisWrap } from '@/components/style';
11
12 const ModalEventInfo = props => {
13 const {
14 dispatch,
15 loading,
16 dataModal: {
17 modalType,
18 modalShow,
19 modalData: { id },
20 },
21 dataEventRiskList,
22 } = props;
23
24 useEffect(() => {
25 if (modalType === 'EVENT_INFO_MODAL' && modalShow) {
26 dispatch({ type: 'Event/getEventRiskList', payload: { eventId: id } });
27 }
28 }, [modalType, modalShow]);
29
30 const columns = [
31 {
32 title: '序号',
33 dataIndex: 'id',
34 align: 'center',
35 fixed: 'left',
36 width: 80,
37 render(t, r, idx) {
38 return idx + 1;
39 },
40 },
41 {
42 title: '风险源名称',
43 dataIndex: 'riskName',
44 align: 'center',
45 width: 160,
46 render: (t, r) => {
47 return (
48 <Tooltip placement="top" title={t}>
49 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
50 </Tooltip>
51 );
52 },
53 },
54 {
55 title: '风险源地址',
56 dataIndex: 'riskUrl',
57 align: 'center',
58 width: 160,
59 render: (t, r) => {
60 return (
61 <Tooltip placement="top" title={t}>
62 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
63 </Tooltip>
64 );
65 },
66 },
67 {
68 title: '风险站点',
69 dataIndex: 'riskHost',
70 align: 'center',
71 width: 160,
72 render: (t, r) => {
73 return (
74 <Tooltip placement="top" title={t}>
75 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
76 </Tooltip>
77 );
78 },
79 },
80 {
81 title: '风险类型',
82 dataIndex: 'riskType',
83 align: 'center',
84 width: 160,
85 render: (t, r) => {
86 return mapRiskType[t] ? mapRiskType[t].label : '-';
87 },
88 },
89 {
90 title: '风险源类型',
91 dataIndex: 'fileFormat',
92 align: 'center',
93 width: 160,
94 render: (t, r) => {
95 return mapRiskSourceType[t] ? mapRiskSourceType[t].label : '-';
96 },
97 },
98 {
99 title: '检测时间',
100 dataIndex: 'checkTime',
101 align: 'center',
102 width: 160,
103 render: (t, r) => {
104 return t || '-';
105 },
106 },
107 {
108 title: '处理状态',
109 dataIndex: 'status',
110 align: 'center',
111 fixed: 'right',
112 width: 100,
113 render: (t, r) => {
114 return mapStatus[t] ? mapStatus[t].label : '-';
115 },
116 },
117 ];
118
119 const pagination = {
120 ...paginations,
121 total: dataEventRiskList.length,
122 pageSize: 500,
123 current: 1,
124 showSizeChanger: dataEventRiskList.length > 500,
125 showTotal(total) {
126 return `总共 ${total} 条数据`;
127 },
128 };
129
130 return (
131 <Modal
132 title="事件风险详情"
133 placement="right"
134 width={1000}
135 maskClosable={false}
136 onCancel={() => {
137 dispatch({ type: 'Event/cancelModal' });
138 }}
139 visible={modalType === 'EVENT_INFO_MODAL' && modalShow}
140 footer={null}
141 >
142 <Table
143 rowKey="id"
144 loading={loading}
145 dataSource={dataEventRiskList}
146 columns={columns}
147 pagination={pagination}
148 scroll={{ x: 1500, y: 600 }}
149 />
150 </Modal>
151 );
152 };
153
154 export default connect(({ Event, loading }) => ({
155 ...Event,
156 loading: loading.effects['Event/getEventRiskList'],
157 }))(ModalEventInfo);
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, Tooltip, Table } from 'antd';
9 import { paginations, mapStatus, mapRiskType, mapRiskSourceType } from '@/constants';
10 import { StyledEllipsisWrap } from '@/components/style';
11
12 const ModalEventLog = props => {
13 const {
14 dispatch,
15 loading,
16 dataModal: {
17 modalType,
18 modalShow,
19 modalData: { id },
20 },
21 dataEventLogList: { records = [], current, size, total },
22 } = props;
23
24 useEffect(() => {
25 if (modalType === 'View_Event_Log' && modalShow) {
26 dispatch({ type: 'Event/getEventRiskLog', payload: { eventId: id } });
27 }
28 }, [modalType, modalShow]);
29
30 /* 点击分页 */
31 const handlePageChange = (current, size) => {
32 dispatch({ type: 'Event/getEventRiskLog', payload: { eventId: id, current, size } });
33 };
34
35 const columns = [
36 {
37 title: '序号',
38 dataIndex: 'id',
39 align: 'center',
40 fixed: 'left',
41 width: 60,
42 render(t, r, idx) {
43 return idx + 1;
44 },
45 },
46 {
47 title: '发送内容',
48 dataIndex: 'content',
49 align: 'center',
50 width: 160,
51 render: (t, r) => {
52 return (
53 <Tooltip placement="top" title={t}>
54 <StyledEllipsisWrap maxLine={1}>{t || '-'}</StyledEllipsisWrap>
55 </Tooltip>
56 );
57 },
58 },
59 {
60 title: '接收者信息',
61 dataIndex: 'recipient',
62 align: 'center',
63 width: 160,
64 render: (t, r) => {
65 return r.type == 2
66 ? `${r.recipientName || '-'}${r.recipient || '-'})`
67 : `${r.recipientName || '-'}`;
68 },
69 },
70 {
71 title: '状态',
72 dataIndex: 'status',
73 align: 'center',
74 width: 120,
75 },
76 {
77 title: '失败原因',
78 dataIndex: 'statusMessage:',
79 align: 'center',
80 width: 160,
81 render: (t, r) => {
82 return (
83 <Tooltip placement="top" title={t}>
84 <StyledEllipsisWrap maxLine={1}>{t || '-'}</StyledEllipsisWrap>
85 </Tooltip>
86 );
87 },
88 },
89 ];
90
91 const pagination = {
92 ...paginations,
93 total: total,
94 pageSize: size,
95 current,
96 showSizeChanger: total > 10,
97 onChange: (current, pageSize) => {
98 handlePageChange(current, pageSize);
99 },
100 onShowSizeChange: (current, pageSize) => {
101 handlePageChange(1, pageSize);
102 },
103 showTotal(total) {
104 return `总共 ${total} 条数据`;
105 },
106 };
107
108 return (
109 <Modal
110 title="事件日志"
111 placement="right"
112 width={1000}
113 maskClosable={false}
114 onCancel={() => {
115 dispatch({ type: 'Event/cancelModal' });
116 }}
117 visible={modalType === 'View_Event_Log' && modalShow}
118 footer={null}
119 >
120 <Table
121 rowKey="id"
122 loading={loading}
123 dataSource={records}
124 columns={columns}
125 pagination={pagination}
126 scroll={{ x: 1500, y: 600 }}
127 />
128 </Modal>
129 );
130 };
131
132 export default connect(({ Event, loading }) => ({
133 ...Event,
134 loading: loading.effects['Event/getEventRiskLog'],
135 }))(ModalEventLog);
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 { Card, Form, Input, Button, Table, Select, DatePicker, Tooltip, Divider } from 'antd';
9 import { paginations, mapStatus, mapEventLevel, mapEventType, enumRiskLabel } from '@/constants';
10 import { ZoomInOutlined, SearchOutlined } from '@ant-design/icons';
11 import { StyledPageContainer, StyledPageContent, StyledEllipsisWrap } from '@/components/style';
12 import ModalEventInfo from './ModalEventInfo';
13 import ModalEventLog from './ModalEventLog';
14 import moment from 'moment';
15
16 const { RangePicker } = DatePicker;
17 const FormItem = Form.Item;
18 const { Option } = Select;
19
20 /* SearchForm */
21 const SearchForm = props => {
22 const [form] = Form.useForm();
23 const {
24 dataSearch,
25 dataSearch: { startTime, endTime },
26 handleReset,
27 handleFinish,
28 } = props;
29
30 useEffect(() => {
31 form.setFieldsValue({
32 ...dataSearch,
33 createTime: [
34 startTime ? moment(startTime) : undefined,
35 endTime ? moment(endTime) : undefined,
36 ],
37 });
38 }, [dataSearch]);
39
40 /* 点击搜索 */
41
42 const onFinish = values => {
43 handleFinish(values);
44 };
45
46 /* 点击重置 */
47 const onReset = () => {
48 handleReset();
49 };
50
51 return (
52 <Form name="Form_Event" layout="inline" form={form} onFinish={onFinish}>
53 <FormItem label="公司名称" name="companyName">
54 <Input placeholder="请输入公司名称" style={{ width: '250px' }} />
55 </FormItem>
56 <FormItem label="事件类型" name="eventType">
57 <Select placeholder="请选择事件类型" style={{ width: '150px' }}>
58 <Option value="ALL">全部</Option>
59 {Object.values(mapEventType).map(item => {
60 const { label, value } = item;
61 return (
62 <Option key={value} value={value}>
63 {label}
64 </Option>
65 );
66 })}
67 </Select>
68 </FormItem>
69 <FormItem label="事件等级" name="eventLevel">
70 <Select placeholder="请选择事件等级" style={{ width: '150px' }}>
71 <Option value="ALL">全部</Option>
72 {Object.values(mapEventLevel).map(item => {
73 const { label, value } = item;
74 return (
75 <Option key={value} value={value}>
76 {label}
77 </Option>
78 );
79 })}
80 </Select>
81 </FormItem>
82 <FormItem label="处理状态" name="status">
83 <Select placeholder="请选择处理状态" style={{ width: '150px' }}>
84 <Option value="ALL">全部</Option>
85 {Object.values(mapStatus).map(item => {
86 const { label, value } = item;
87 return (
88 <Option key={value} value={value}>
89 {label}
90 </Option>
91 );
92 })}
93 </Select>
94 </FormItem>
95 <FormItem label="创建时间" name="createTime">
96 <RangePicker allowClear={false} />
97 </FormItem>
98 <FormItem>
99 <Button type="primary" htmlType="submit" className="mr-15">
100 搜索
101 </Button>
102 <Button className="mr-15" htmlType="button" onClick={onReset}>
103 重置
104 </Button>
105 </FormItem>
106 </Form>
107 );
108 };
109
110 /* DataTable */
111 const DataTable = props => {
112 const {
113 dispatch,
114 loading,
115 handleGetList,
116 dataEvent: { records = [], current, size, total },
117 } = props;
118
119 /* 点击分页 */
120 const handlePageChange = (current, size) => {
121 handleGetList(current, size);
122 };
123
124 const columns = [
125 {
126 title: '序号',
127 dataIndex: 'id',
128 align: 'center',
129 fixed: 'left',
130 width: 80,
131 render(t, r, idx) {
132 return (current - 1) * size + idx + 1;
133 },
134 },
135 {
136 title: '事件编号',
137 dataIndex: 'serialNumber',
138 align: 'center',
139 width: 220,
140 },
141 {
142 title: '事件类型',
143 dataIndex: 'eventType',
144 align: 'center',
145 width: 160,
146 render: (t, r) => {
147 return mapEventType[t] ? mapEventType[t].label : '-';
148 },
149 },
150 {
151 title: '事件等级',
152 dataIndex: 'eventLevel',
153 align: 'center',
154 width: 160,
155 render: (t, r) => {
156 return mapEventLevel[t] ? mapEventLevel[t].label : '-';
157 },
158 },
159 {
160 title: '公司名称',
161 dataIndex: 'companyName',
162 align: 'center',
163 width: 160,
164 render: (t, r) => {
165 return t ? (
166 <Tooltip placement="top" title={t}>
167 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
168 </Tooltip>
169 ) : (
170 '-'
171 );
172 },
173 },
174 {
175 title: '公司地址',
176 dataIndex: 'companyAddress',
177 align: 'center',
178 width: 260,
179 render: (t, r) => {
180 return t ? (
181 <Tooltip placement="top" title={t}>
182 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
183 </Tooltip>
184 ) : (
185 '-'
186 );
187 },
188 },
189 {
190 title: '风险站点',
191 dataIndex: 'riskHost',
192 align: 'center',
193 width: 160,
194 render: (t, r) => {
195 return t ? (
196 <Tooltip placement="top" title={t}>
197 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
198 </Tooltip>
199 ) : (
200 '-'
201 );
202 },
203 },
204 {
205 title: '风险数量',
206 dataIndex: 'riskNumber',
207 align: 'center',
208 width: 120,
209 },
210 {
211 title: '风险标签',
212 dataIndex: 'riskLabel',
213 align: 'center',
214 width: 120,
215 render: (t, r) => {
216 let riskLabel = [];
217 if (t) {
218 for (let i = 0; i < enumRiskLabel.length; i++) {
219 for (let j = 0; j < t.split(',').length; j++) {
220 if (enumRiskLabel[i].value === t.split(',')[j]) {
221 riskLabel.push(enumRiskLabel[i].label);
222 }
223 }
224 }
225 }
226 return t ? riskLabel.join(',') : '-';
227 },
228 },
229 {
230 title: '限期整改',
231 dataIndex: 'deadlineDesc',
232 align: 'center',
233 width: 120,
234 },
235 {
236 title: '创建时间',
237 dataIndex: 'createTime',
238 align: 'center',
239 width: 160,
240 },
241 {
242 title: '处理状态',
243 dataIndex: 'status',
244 align: 'center',
245 width: 100,
246 render: (t, r) => {
247 return mapStatus[t] ? mapStatus[t].label : '-';
248 },
249 },
250 {
251 title: '操作',
252 align: 'center',
253 fixed: 'right',
254 width: 120,
255 render(t, r) {
256 return (
257 <>
258 <Tooltip placement="top" title="查看详情">
259 <ZoomInOutlined
260 style={{ cursor: 'pointer', fontSize: 18 }}
261 onClick={() => {
262 dispatch({
263 type: 'Event/getEventRiskLog',
264 payload: { eventId: r.id },
265 });
266 dispatch({
267 type: 'Event/changeState',
268 payload: {
269 dataModal: {
270 modalType: 'EVENT_INFO_MODAL',
271 modalShow: true,
272 modalData: r,
273 },
274 },
275 });
276 }}
277 />
278 </Tooltip>
279 <Divider type="vertical" style={{ margin: '0 16px' }} />
280 <Tooltip
281 placement="top"
282 title="查看日志"
283 onClick={() => {
284 dispatch({
285 type: 'Event/changeState',
286 payload: {
287 dataModal: {
288 modalType: 'View_Event_Log',
289 modalShow: true,
290 modalData: r,
291 },
292 },
293 });
294 }}
295 >
296 <SearchOutlined style={{ cursor: 'pointer', fontSize: 20 }} />
297 </Tooltip>
298 </>
299 );
300 },
301 },
302 ];
303 const pagination = {
304 ...paginations,
305 total: total,
306 pageSize: size,
307 current,
308 showSizeChanger: total > 10,
309 onChange: (current, pageSize) => {
310 handlePageChange(current, pageSize);
311 },
312 onShowSizeChange: (current, pageSize) => {
313 handlePageChange(1, pageSize);
314 },
315 showTotal(total) {
316 return `总共 ${total} 条数据`;
317 },
318 };
319 return (
320 <Table
321 rowKey="id"
322 loading={loading}
323 dataSource={records}
324 columns={columns}
325 pagination={pagination}
326 scroll={{ x: 1500, y: `calc(100vh - 340px)` }}
327 />
328 );
329 };
330
331 /* Main */
332 const Event = props => {
333 const {
334 dispatch,
335 loading,
336 dataSearch,
337 dataEvent,
338 dataEvent: { size },
339 } = props;
340
341 useEffect(() => {
342 handleGetList(1, 10);
343 }, []);
344
345 /* 账号列表 */
346 const handleGetList = (current, size) => {
347 dispatch({
348 type: 'Event/getEventList',
349 payload: {
350 current: current || 1,
351 size: size || 10,
352 },
353 });
354 };
355
356 /* 点击搜索 */
357 const handleFinish = values => {
358 const { status, eventType, eventLevel, companyName, createTime } = values;
359 dispatch({
360 type: 'Event/changeState',
361 payload: {
362 dataSearch: {
363 status,
364 eventType,
365 eventLevel,
366 companyName,
367 startTime: createTime[0]
368 ? moment(createTime[0].startOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
369 : undefined,
370 endTime: createTime[1]
371 ? moment(createTime[1].endOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
372 : undefined,
373 },
374 },
375 });
376 handleGetList(0, size);
377 };
378
379 /* 点击重置 */
380 const handleReset = () => {
381 dispatch({ type: 'Event/resetSearch' });
382 handleGetList(0, 10);
383 };
384
385 return (
386 <StyledPageContainer>
387 <StyledPageContent>
388 <Card bordered={false}>
389 <SearchForm
390 dataSearch={dataSearch}
391 handleReset={handleReset}
392 handleFinish={handleFinish}
393 />
394 <div className="mt-16">
395 <DataTable
396 dispatch={dispatch}
397 loading={loading}
398 handleGetList={handleGetList}
399 dataEvent={dataEvent}
400 />
401 </div>
402 </Card>
403 </StyledPageContent>
404 {/* 事件详情 */}
405 <ModalEventInfo />
406
407 {/* 事件日志 */}
408 <ModalEventLog />
409 </StyledPageContainer>
410 );
411 };
412
413 export default connect(({ Event, loading }) => ({
414 ...Event,
415 loading: loading.effects['Event/getEventList'],
416 }))(Event);
1 import * as services from '@/services/incident';
2 import moment from 'moment';
3
4 /* SerachParams */
5 const staticSearch = {
6 current: 1,
7 size: 10,
8 status: 'ALL',
9 eventType: 'ALL',
10 eventLevel: 'ALL',
11 companyName: undefined,
12 startTime: moment(new Date())
13 .add(-7, 'days')
14 .startOf('day'),
15 endTime: moment(new Date())
16 .add(0, 'days')
17 .endOf('day'),
18 };
19
20 export default {
21 namespace: 'Event',
22 state: {
23 dataSearch: {
24 ...staticSearch,
25 },
26 dataEvent: {
27 records: [],
28 current: 1,
29 size: 10,
30 total: 0,
31 totalPage: 0,
32 },
33 dataEventRiskList: [],
34 dataModal: {
35 modalType: '',
36 modalShow: false,
37 modalData: {},
38 },
39 dataEventLogList: {
40 records: [],
41 current: 1,
42 size: 10,
43 total: 0,
44 totalPage: 0,
45 },
46 },
47 effects: {
48 /* 获取事件列表 */
49 *getEventList({ payload }, { call, put, select }) {
50 const {
51 dataSearch,
52 dataSearch: { status, eventType, eventLevel, startTime, endTime },
53 } = yield select(state => state.Event);
54 try {
55 const res = yield call(services.getEventList, {
56 current: 1,
57 size: 10,
58 ...dataSearch,
59 ...payload,
60 startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
61 endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
62 status: status === 'ALL' ? '' : status,
63 eventType: eventType === 'ALL' ? '' : eventType,
64 eventLevel: eventLevel === 'ALL' ? '' : eventLevel,
65 });
66 if (res.code === 0) {
67 res.data.records = res.data.records || [];
68 yield put({
69 type: 'changeState',
70 payload: {
71 dataEvent: res.data,
72 },
73 });
74 }
75 } catch (err) {
76 console.error(err);
77 }
78 },
79 /* 事件详情 */
80 *getEventRiskList({ payload }, { call, put, select }) {
81 try {
82 const res = yield call(services.getEventRiskList, {
83 current: 1,
84 size: 10,
85 ...payload,
86 });
87 if (res.code === 0) {
88 res.data.records = res.data.records || [];
89 yield put({
90 type: 'changeState',
91 payload: {
92 dataEventRiskList: res.data,
93 },
94 });
95 }
96 } catch (err) {
97 console.error(err);
98 }
99 },
100 /* 事件日志 */
101 *getEventRiskLog({ payload }, { call, put, select }) {
102 try {
103 const res = yield call(services.getEventRiskLog, {
104 current: 1,
105 size: 10,
106 ...payload,
107 });
108 if (res.code === 0) {
109 res.data.records = res.data.records || [];
110 yield put({
111 type: 'changeState',
112 payload: {
113 dataEventLogList: res.data,
114 },
115 });
116 }
117 } catch (err) {
118 console.error(err);
119 }
120 },
121 },
122 reducers: {
123 changeState(state, { payload }) {
124 return {
125 ...state,
126 ...payload,
127 };
128 },
129 cancelModal(state, { payload }) {
130 return {
131 ...state,
132 dataModal: {
133 modalType: '',
134 modalShow: false,
135 modalData: {},
136 },
137 };
138 },
139 resetSearch(state, { payload }) {
140 return {
141 ...state,
142 dataSearch: {
143 ...staticSearch,
144 },
145 };
146 },
147 },
148 };
1 /**
2 * Author: wjw
3 * Date: 2019.05.13
4 * Description: '图片预览'
5 */
6 import React, { useState, useEffect } from 'react';
7 import { connect } from 'umi';
8 import { Modal } from 'antd';
9 const ModalPreImg = props => {
10 const {
11 preImgDataModal: {
12 modalType,
13 modalShow,
14 modalData: { imgUrl, title, type = 'imgurl' },
15 },
16 width,
17 dispatch,
18 } = props;
19 const params = {
20 width,
21 };
22 // const QRCode = require('qrcode.react');
23 const handleCancel = () => {
24 const payload = {
25 preImgDataModal: {
26 modalType: '',
27 modalShow: false,
28 modalData: {},
29 },
30 };
31 dispatch({ type: 'global/changeState', payload });
32 };
33 return (
34 <Modal
35 zIndex={10001}
36 {...params}
37 visible={modalType === 'PREVIEWIMG' && modalShow}
38 footer={null}
39 getContainer={()=> document.body}
40 onCancel={() => {
41 handleCancel();
42 }}
43 >
44 <div style={{ textAlign: 'center' }}>
45 <p>{title}</p>
46 {type === 'imgurl' && imgUrl && (
47 <img alt="example" style={{ width: '100%' }} src={imgUrl} />
48 )}
49 {/* {type === 'qrcode' && (
50 <div style={{ textAlign: 'center' }}>
51 {imgUrl ? <QRCode value={imgUrl} style={{ width: '250px', height: '250px' }} /> : ''}
52 </div>
53 )} */}
54 </div>
55 </Modal>
56 );
57 };
58 const mapStateToProps = ({ global }) => {
59 return {
60 preImgDataModal: global.preImgDataModal,
61 };
62 };
63 export default connect(mapStateToProps)(ModalPreImg);
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 {
9 Card,
10 Form,
11 Input,
12 Button,
13 Table,
14 Select,
15 DatePicker,
16 Tooltip,
17 Upload,
18 message,
19 } from 'antd';
20 import { paginations, mapStatus, mapRiskType, mapRiskSourceType } from '@/constants';
21 import {
22 StyledPageContainer,
23 StyledPageContent,
24 StyledEllipsisWrap,
25 StyledPageHeader,
26 } from '@/components/style';
27 import { UploadOutlined } from '@ant-design/icons';
28 import moment from 'moment';
29 import * as services from '@/services/risk';
30 import { ExportFile } from '@/utils/utils';
31
32 const { RangePicker } = DatePicker;
33 const FormItem = Form.Item;
34 const { Option } = Select;
35
36 /* SearchForm */
37 const SearchForm = props => {
38 const [form] = Form.useForm();
39 const {
40 dataSearch,
41 dataSearch: { startTime, endTime },
42 handleReset,
43 handleFinish,
44 } = props;
45
46 useEffect(() => {
47 form.setFieldsValue({
48 ...dataSearch,
49 createTime: [
50 startTime ? moment(startTime) : undefined,
51 endTime ? moment(endTime) : undefined,
52 ],
53 });
54 }, [dataSearch]);
55
56 /* 点击搜索 */
57
58 const onFinish = values => {
59 handleFinish(values);
60 };
61
62 /* 点击重置 */
63 const onReset = () => {
64 handleReset();
65 };
66
67 return (
68 <Form name="Form_Risk" layout="inline" form={form} onFinish={onFinish}>
69 <FormItem label="网站地址" name="riskHost">
70 <Input placeholder="请输入网站地址" style={{ width: '250px' }} />
71 </FormItem>
72 <FormItem label="风险类型" name="riskType">
73 <Select placeholder="请选择风险类型" style={{ width: '150px' }}>
74 <Option value="ALL">全部</Option>
75 {Object.values(mapRiskType).map(item => {
76 const { label, value } = item;
77 return (
78 <Option key={value} value={value}>
79 {label}
80 </Option>
81 );
82 })}
83 </Select>
84 </FormItem>
85 <FormItem label="风险源类型" name="fileFormat">
86 <Select placeholder="请选择风险源类型" style={{ width: '150px' }}>
87 <Option value="ALL">全部</Option>
88 {Object.values(mapRiskSourceType).map(item => {
89 const { label, value } = item;
90 return (
91 <Option key={value} value={value}>
92 {label}
93 </Option>
94 );
95 })}
96 </Select>
97 </FormItem>
98 <FormItem label="处理状态" name="status">
99 <Select placeholder="请选择处理状态" style={{ width: '150px' }}>
100 <Option value="ALL">全部</Option>
101 {Object.values(mapStatus).map(item => {
102 const { label, value } = item;
103 return (
104 <Option key={value} value={value}>
105 {label}
106 </Option>
107 );
108 })}
109 </Select>
110 </FormItem>
111 <FormItem label="检测时间" name="createTime">
112 <RangePicker allowClear={false} />
113 </FormItem>
114 <FormItem>
115 <Button type="primary" htmlType="submit" className="mr-15">
116 搜索
117 </Button>
118 <Button className="mr-15" htmlType="button" onClick={onReset}>
119 重置
120 </Button>
121 </FormItem>
122 </Form>
123 );
124 };
125
126 /* DataTable */
127 const DataTable = props => {
128 const {
129 loading,
130 handleGetList,
131 dataRisk: { records = [], current, size, total },
132 } = props;
133
134 /* 点击分页 */
135 const handlePageChange = (current, size) => {
136 handleGetList(current, size);
137 };
138
139 const columns = [
140 {
141 title: '序号',
142 dataIndex: 'id',
143 align: 'center',
144 fixed: 'left',
145 width: 80,
146 render(t, r, idx) {
147 return (current - 1) * size + idx + 1;
148 },
149 },
150 {
151 title: '风险源名称',
152 dataIndex: 'riskName',
153 align: 'center',
154 width: 160,
155 render: (t, r) => {
156 return (
157 <Tooltip placement="top" title={t}>
158 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
159 </Tooltip>
160 );
161 },
162 },
163 {
164 title: '风险源地址',
165 dataIndex: 'riskUrl',
166 align: 'center',
167 width: 160,
168 render: (t, r) => {
169 return (
170 <Tooltip placement="top" title={t}>
171 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
172 </Tooltip>
173 );
174 },
175 },
176 {
177 title: '风险站点',
178 dataIndex: 'riskHost',
179 align: 'center',
180 width: 160,
181 render: (t, r) => {
182 return (
183 <Tooltip placement="top" title={t}>
184 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
185 </Tooltip>
186 );
187 },
188 },
189 {
190 title: '关键词',
191 dataIndex: 'riskKeyword',
192 align: 'center',
193 width: 260,
194 render: (t, r) => {
195 return (
196 <Tooltip placement="top" title={t}>
197 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
198 </Tooltip>
199 );
200 },
201 },
202 {
203 title: '风险类型',
204 dataIndex: 'riskType',
205 align: 'center',
206 width: 160,
207 render: (t, r) => {
208 return mapRiskType[t] ? mapRiskType[t].label : '-';
209 },
210 },
211 {
212 title: '风险源类型',
213 dataIndex: 'fileFormat',
214 align: 'center',
215 width: 160,
216 render: (t, r) => {
217 return mapRiskSourceType[t] ? mapRiskSourceType[t].label : '-';
218 },
219 },
220 {
221 title: '检测时间',
222 dataIndex: 'checkTime',
223 align: 'center',
224 width: 160,
225 render: (t, r) => {
226 return t ? moment(t).format('YYYY-MM-DD HH:mm:ss') : '-';
227 },
228 },
229 {
230 title: '处理状态',
231 dataIndex: 'status',
232 align: 'center',
233 fixed: 'right',
234 width: 100,
235 render: (t, r) => {
236 return mapStatus[t] ? mapStatus[t].label : '-';
237 },
238 },
239 ];
240 const pagination = {
241 ...paginations,
242 total: total,
243 pageSize: size,
244 current,
245 showSizeChanger: total > 10,
246 onChange: (current, pageSize) => {
247 handlePageChange(current, pageSize);
248 },
249 onShowSizeChange: (current, pageSize) => {
250 handlePageChange(1, pageSize);
251 },
252 showTotal(total) {
253 return `总共 ${total} 条数据`;
254 },
255 };
256 return (
257 <Table
258 rowKey="id"
259 loading={loading}
260 dataSource={records}
261 columns={columns}
262 pagination={pagination}
263 scroll={{ x: 1500, y: `calc(100vh - 400px)` }}
264 />
265 );
266 };
267
268 /* Main */
269 const Risk = props => {
270 const {
271 dispatch,
272 loading,
273 loadingUpload,
274 dataSearch,
275 dataRisk,
276 dataRisk: { size },
277 } = props;
278
279 useEffect(() => {
280 handleGetList(1, 10);
281 }, []);
282
283 /* 账号列表 */
284 const handleGetList = (current, size) => {
285 dispatch({
286 type: 'Risk/getRiskList',
287 payload: {
288 current: current || 1,
289 size: size || 10,
290 },
291 });
292 };
293
294 /* 点击搜索 */
295 const handleFinish = values => {
296 const { status, riskType, fileFormat, riskHost, createTime } = values;
297 dispatch({
298 type: 'Risk/changeState',
299 payload: {
300 dataSearch: {
301 status,
302 riskType,
303 fileFormat,
304 riskHost,
305 startTime: createTime[0]
306 ? moment(createTime[0].startOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
307 : undefined,
308 endTime: createTime[1]
309 ? moment(createTime[1].endOf('day').valueOf()).format('YYYY-MM-DD HH:mm:ss')
310 : undefined,
311 },
312 },
313 });
314 handleGetList(0, size);
315 };
316
317 /* 点击重置 */
318 const handleReset = () => {
319 dispatch({ type: 'Risk/resetSearch' });
320 handleGetList(0, 10);
321 };
322
323 /* 上传组件配置 */
324 const uploadProps = {
325 customRequest: file => {
326 const formData = new FormData();
327 formData.append('file', file.file);
328 dispatch({ type: 'Risk/importRisk', payload: formData });
329 },
330 beforeUpload: file => {
331 const isLt50M = file.size / 1024 / 1024 < 50;
332 if (!isLt50M) {
333 message.error('文件大小需小于50M');
334 return false;
335 }
336 return isLt50M;
337 },
338 };
339
340 return (
341 <StyledPageContainer>
342 <StyledPageHeader>
343 <Upload {...uploadProps}>
344 <Button loading={loadingUpload} type="primary" icon={<UploadOutlined />}>
345 数据导入
346 </Button>
347 </Upload>
348 <Button
349 type="primary"
350 onClick={() => {
351 ExportFile('风险模版', services.exportRiskTemplate, {});
352 }}
353 >
354 模版下载
355 </Button>
356 </StyledPageHeader>
357 <StyledPageContent>
358 <Card bordered={false}>
359 <SearchForm
360 dataSearch={dataSearch}
361 handleReset={handleReset}
362 handleFinish={handleFinish}
363 />
364 <div className="mt-16">
365 <DataTable loading={loading} handleGetList={handleGetList} dataRisk={dataRisk} />
366 </div>
367 </Card>
368 </StyledPageContent>
369 </StyledPageContainer>
370 );
371 };
372
373 export default connect(({ Risk, loading }) => ({
374 ...Risk,
375 loading: loading.effects['Risk/getRiskList'],
376 loadingUpload: loading.effects['Risk/importRisk'],
377 }))(Risk);
1 import * as services from '@/services/risk';
2 import { message } from 'antd';
3 import moment from 'moment';
4
5 /* SerachParams */
6 const staticSearch = {
7 current: 1,
8 size: 10,
9 status: 'ALL',
10 riskType: 'ALL',
11 fileFormat: 'ALL',
12 riskHost: undefined,
13 startTime: moment(new Date())
14 .add(-7, 'days')
15 .startOf('day'),
16 endTime: moment(new Date())
17 .add(0, 'days')
18 .endOf('day'),
19 };
20
21 export default {
22 namespace: 'Risk',
23 state: {
24 dataSearch: {
25 ...staticSearch,
26 },
27 dataRisk: {
28 records: [],
29 current: 1,
30 size: 10,
31 total: 0,
32 totalPage: 0,
33 },
34 },
35 effects: {
36 /* 获取风险列表 */
37 *getRiskList({ payload }, { call, put, select }) {
38 const {
39 dataSearch,
40 dataSearch: { status, riskType, fileFormat, startTime, endTime },
41 } = yield select(state => state.Risk);
42 try {
43 const res = yield call(services.getRiskList, {
44 current: 1,
45 size: 10,
46 ...dataSearch,
47 ...payload,
48 startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
49 endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
50 status: status === 'ALL' ? '' : status,
51 riskType: riskType === 'ALL' ? '' : riskType,
52 fileFormat: fileFormat === 'ALL' ? '' : fileFormat,
53 });
54 if (res.code === 0) {
55 res.data.records = res.data.records || [];
56 yield put({
57 type: 'changeState',
58 payload: {
59 dataRisk: res.data,
60 },
61 });
62 }
63 } catch (err) {
64 console.error(err);
65 }
66 },
67 /* 风险导入 */
68 *importRisk({ payload }, { call, put, select }) {
69 try {
70 const res = yield call(services.importRisk, payload);
71 if (res.code === 0) {
72 message.success('导入成功');
73 yield put({ type: 'getRiskList' });
74 }
75 } catch (err) {
76 console.error(err);
77 }
78 },
79 },
80 reducers: {
81 changeState(state, { payload }) {
82 return {
83 ...state,
84 ...payload,
85 };
86 },
87 resetSearch(state, { payload }) {
88 return {
89 ...state,
90 dataSearch: {
91 ...staticSearch,
92 },
93 };
94 },
95 },
96 };
1 import { StyledWapperIframe } from '@/components/style';
2
3 const Index = () => {
4 return (
5 <StyledWapperIframe>
6 <iframe
7 frameBorder="0"
8 src="https://yh2-screen.jimilicai.com/"
9 style={{ width: '100%', height: '100%' }}
10 allowfullscreen="true"
11 webkitallowfullscreen="true"
12 mozallowfullscreen="true"
13 ></iframe>
14 </StyledWapperIframe>
15 );
16 };
17
18 export default Index;
1 /**
2 * Author: Charles
3 * Date: 2022.9.13
4 * Description: [重置密码]
5 */
6 import React, { useEffect } from 'react';
7 import { Modal, Form, Input, Button } from 'antd';
8 import {aesEncrypt} from '@/utils/encrypt';
9
10 const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
11
12 const ModalResetPassword = props => {
13 const [form] = Form.useForm();
14 const {
15 handleCancelModal,
16 handleOk,
17 dataModal: {
18 modalType,
19 modalShow,
20 modalData: { id },
21 },
22 } = props;
23
24 useEffect(() => {
25 if (modalType === 'PASSWORD_SET_MODAL' && modalShow) {
26 form.resetFields();
27 }
28 }, [modalType, modalShow]);
29
30 /* 点击保存 */
31 const handleSave = () => {
32
33 form.validateFields().then(values => {
34 const saveData = {
35 newPassword: aesEncrypt(values.newPassword),
36 checkPassword: aesEncrypt(values.checkPassword)
37 }
38 handleOk({ saveType: 'PASSWORD', id, ...saveData });
39 });
40 };
41
42 return (
43 <Modal
44 title="重置密码"
45 placement="right"
46 width={700}
47 maskClosable={false}
48 onCancel={handleCancelModal}
49 visible={modalType === 'PASSWORD_SET_MODAL' && modalShow}
50 footer={
51 <div
52 style={{
53 textAlign: 'right',
54 }}
55 >
56 <Button onClick={handleCancelModal} className="mr-10">
57 取消
58 </Button>
59 <Button onClick={handleSave} type="primary">
60 保存
61 </Button>
62 </div>
63 }
64 >
65 <Form form={form} {...formItemLayout} name="password_set_modal">
66 <Form.Item
67 name="newPassword"
68 label="密码"
69 rules={[
70 { required: true, message: '请输入密码' },
71 {
72 pattern: /^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,16}$/,
73 // pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![!#$%^&*]+$)[\da-zA-Z!#$%^&@*]{8,16}$/,
74 message: '密码至少包含大小写字母、数字、特殊符号的三种组合,限制8~16个字符~',
75 },
76 ]}
77 >
78 <Input.Password placeholder="请输入密码" />
79 </Form.Item>
80 <Form.Item
81 name="checkPassword"
82 label="密码确认"
83 rules={[
84 { required: true, message: '请输入确认密码' },
85 ({ getFieldValue }) => ({
86 validator(_, value) {
87 if (!value || getFieldValue('newPassword') === value) {
88 return Promise.resolve();
89 }
90 return Promise.reject(new Error('两次密码不一致,请重新输入'));
91 },
92 }),
93 ]}
94 >
95 <Input.Password placeholder="请确认密码" />
96 </Form.Item>
97 </Form>
98 </Modal>
99 );
100 };
101
102 export default ModalResetPassword;
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, Select, Input, Button } from 'antd';
9 import {aesEncrypt} from '@/utils/encrypt';
10
11 const { Option } = Select;
12 const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
13
14 const ModalSetAccount = props => {
15 const [form] = Form.useForm();
16 const {
17 dispatch,
18 handleCancelModal,
19 handleOk,
20 dataRoleList,
21 dataModal: {
22 modalType,
23 modalShow,
24 modalData: { id, realName, username, roleId },
25 },
26 } = props;
27
28 useEffect(() => {
29 if (modalType === 'ACCOUNT_SET_MODAL' && modalShow) {
30 dispatch({ type: 'Account/getRoleSelect' });
31 form.resetFields();
32 form.setFieldsValue({
33 realName,
34 username,
35 roleId,
36 });
37 }
38 }, [modalType, modalShow]);
39
40 /* 点击保存 */
41 const handleSave = () => {
42 form.validateFields().then(values => {
43 const saveData = {...values}
44 saveData.password = aesEncrypt(values.password),
45 saveData.checkPassword = aesEncrypt(values.checkPassword),
46 handleOk({ saveType: 'ACCOUNT', id, ...saveData });
47 });
48 };
49
50 return (
51 <Modal
52 title={id ? '编辑账号' : '新增账号'}
53 placement="right"
54 width={700}
55 maskClosable={false}
56 onCancel={handleCancelModal}
57 visible={modalType === 'ACCOUNT_SET_MODAL' && modalShow}
58 footer={
59 <div
60 style={{
61 textAlign: 'right',
62 }}
63 >
64 <Button onClick={handleCancelModal} className="mr-10">
65 取消
66 </Button>
67 <Button onClick={handleSave} type="primary">
68 保存
69 </Button>
70 </div>
71 }
72 >
73 <Form form={form} {...formItemLayout} name="account_set_modal">
74 <Form.Item
75 name="username"
76 label="账号"
77 rules={[
78 { required: true, message: '请输入账号名称, 由4~10位字母、数字、符号组成不包括空格' },
79 {
80 pattern: /^[0-9a-zA-Z]{4,10}$/,
81 message: '账号名称由4~10位字母、数字、符号组成不包括空格~',
82 },
83 ]}
84 >
85 <Input placeholder="请输入账号" disabled={id ? true : false} />
86 </Form.Item>
87 <Form.Item
88 name="realName"
89 label="姓名"
90 rules={[
91 { required: true, message: '请输入姓名, 由2~10位非空格字符组成' },
92 { pattern: /^[\S]{2,10}$/, message: '昵称由2~10位非空格字符组成' },
93 ]}
94 >
95 <Input placeholder="请输入姓名" />
96 </Form.Item>
97 {!id && (
98 <>
99 <Form.Item
100 name="password"
101 label="密码"
102 rules={[
103 { required: true, message: '请输入密码' },
104 {
105 pattern: /^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,16}$/,
106 // pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![!#$%^&*]+$)[\da-zA-Z!#$%^&@*]{6,16}$/,
107 message: '密码至少包含大小写字母、数字、特殊符号的三种组合,限制8~16个字符~',
108 },
109 ]}
110 >
111 <Input.Password placeholder="请输入密码" />
112 </Form.Item>
113 <Form.Item
114 name="checkPassword"
115 label="密码确认"
116 rules={[
117 { required: true, message: '请输入确认密码' },
118 ({ getFieldValue }) => ({
119 validator(_, value) {
120 if (!value || getFieldValue('password') === value) {
121 return Promise.resolve();
122 }
123 return Promise.reject(new Error('两次密码不一致,请重新输入'));
124 },
125 }),
126 ]}
127 >
128 <Input.Password placeholder="请输入确认密码" />
129 </Form.Item>
130 </>
131 )}
132 <Form.Item
133 name="roleId"
134 label="角色"
135 rules={[{ required: true, message: '请选择关联的角色' }]}
136 >
137 <Select placeholder="请选择关联的角色">
138 {dataRoleList.map(item => {
139 return (
140 <Option key={item.id} value={item.id}>
141 {item.name}
142 </Option>
143 );
144 })}
145 </Select>
146 </Form.Item>
147 </Form>
148 </Modal>
149 );
150 };
151
152 export default connect(({ Account }) => ({
153 ...Account,
154 }))(ModalSetAccount);
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 { Card, Form, Input, Button, Table, Divider, Tooltip, Select, Switch } from 'antd';
9 import { EditOutlined, RedoOutlined } from '@ant-design/icons';
10 import { paginations } from '@/constants';
11 import ModalUpdateAccount from './ModalUpdateAccount';
12 import ModalResetPassword from './ModalResetPassword';
13 import { StyledPageContainer, StyledPageHeader, StyledPageContent } from '@/components/style';
14
15 const FormItem = Form.Item;
16 const { Option } = Select;
17
18 /* SearchForm */
19 const SearchForm = props => {
20 const [form] = Form.useForm();
21 const { dataSearch, handleReset, handleFinish } = props;
22
23 useEffect(() => {
24 form.setFieldsValue({
25 ...dataSearch,
26 });
27 }, [dataSearch]);
28
29 /* 点击搜索 */
30
31 const onFinish = values => {
32 handleFinish(values);
33 };
34
35 /* 点击重置 */
36 const onReset = () => {
37 handleReset();
38 };
39
40 return (
41 <Form name="Form_Account" layout="inline" form={form} onFinish={onFinish}>
42 <FormItem label="账号名称" name="keyword">
43 <Input placeholder="请输入账号名称" style={{ width: '220px' }} />
44 </FormItem>
45 <FormItem label="账号状态" name="status">
46 <Select placeholder="请选择账号状态" style={{ width: '180px' }}>
47 <Option value="ALL">全部</Option>
48 <Option value="1">启用</Option>
49 <Option value="0">禁用</Option>
50 </Select>
51 </FormItem>
52 <FormItem>
53 <Button type="primary" htmlType="submit" className="mr-15">
54 搜索
55 </Button>
56 <Button className="mr-15" htmlType="button" onClick={onReset}>
57 重置
58 </Button>
59 </FormItem>
60 </Form>
61 );
62 };
63
64 /* DataTable */
65 const DataTable = props => {
66 const {
67 dispatch,
68 loading,
69 handleGetList,
70 currentUser: { name },
71 dataAccount: { records = [], current, size, total },
72 } = props;
73
74 /* 点击分页 */
75 const handlePageChange = (current, size) => {
76 handleGetList(current, size);
77 };
78
79 const columns = [
80 {
81 title: '序号',
82 dataIndex: 'id',
83 align: 'center',
84 width: 100,
85 render(t, r, idx) {
86 return (current - 1) * size + idx + 1;
87 },
88 },
89 {
90 title: '账号',
91 dataIndex: 'username',
92 align: 'center',
93 width: 140,
94 },
95 {
96 title: '姓名',
97 dataIndex: 'realName',
98 align: 'center',
99 width: 140,
100 },
101 {
102 title: '角色',
103 dataIndex: 'roleName',
104 align: 'center',
105 width: 140,
106 },
107 {
108 title: '账号状态',
109 dataIndex: 'status',
110 align: 'center',
111 width: 160,
112 render(t, r, idx) {
113 return (
114 <Switch
115 checkedChildren="启用"
116 unCheckedChildren="已禁用"
117 checked={t === 1}
118 disabled={name === r.username}
119 onChange={checked => {
120 dispatch({
121 type: 'Account/changeStatus',
122 payload: {
123 id: r.id,
124 status: checked ? 1 : 0,
125 },
126 });
127 }}
128 />
129 );
130 },
131 },
132 {
133 title: '创建时间',
134 dataIndex: 'createTime',
135 align: 'center',
136 width: 200,
137 render(t, r, idx) {
138 return t || '-';
139 },
140 },
141 {
142 title: '操作',
143 align: 'center',
144 width: 160,
145 render(t, r) {
146 return (
147 <>
148 <Tooltip placement="top" title="编辑">
149 <EditOutlined
150 style={{ cursor: 'pointer', fontSize: 16 }}
151 onClick={() => {
152 dispatch({
153 type: 'Account/changeState',
154 payload: {
155 dataModal: {
156 modalType: 'ACCOUNT_SET_MODAL',
157 modalShow: true,
158 modalData: r,
159 },
160 },
161 });
162 }}
163 />
164 </Tooltip>
165 <Divider type="vertical" style={{ margin: '0 16px' }} />
166 <Tooltip
167 placement="top"
168 title="重置密码"
169 onClick={() => {
170 dispatch({
171 type: 'Account/changeState',
172 payload: {
173 dataModal: {
174 modalType: 'PASSWORD_SET_MODAL',
175 modalShow: true,
176 modalData: r,
177 },
178 },
179 });
180 }}
181 >
182 <RedoOutlined style={{ cursor: 'pointer', fontSize: 16 }} />
183 </Tooltip>
184 </>
185 );
186 },
187 },
188 ];
189 const pagination = {
190 ...paginations,
191 total: total,
192 pageSize: size,
193 current,
194 showSizeChanger: total > 10,
195 onChange: (current, pageSize) => {
196 handlePageChange(current, pageSize);
197 },
198 onShowSizeChange: (current, pageSize) => {
199 handlePageChange(1, pageSize);
200 },
201 showTotal(total) {
202 return `总共 ${total} 条数据`;
203 },
204 };
205 return (
206 <Table
207 rowKey="id"
208 loading={loading}
209 dataSource={records}
210 columns={columns}
211 pagination={pagination}
212 scroll={{ y: `calc(100vh - 353px)` }}
213 />
214 );
215 };
216
217 /* Main */
218 const Account = props => {
219 const {
220 dispatch,
221 loading,
222 dataSearch,
223 dataModal,
224 currentUser,
225 dataAccount,
226 dataAccount: { size },
227 } = props;
228
229 useEffect(() => {
230 handleGetList(1, 10);
231 }, []);
232
233 /* 账号列表 */
234 const handleGetList = (current, size) => {
235 dispatch({
236 type: 'Account/getAccountList',
237 payload: {
238 current: current || 1,
239 size: size || 10,
240 },
241 });
242 };
243
244 /* 点击搜索 */
245 const handleFinish = values => {
246 const { status, keyword } = values;
247 dispatch({
248 type: 'Account/changeState',
249 payload: {
250 dataSearch: {
251 keyword,
252 status,
253 },
254 },
255 });
256 handleGetList(0, size);
257 };
258
259 /* 点击重置 */
260 const handleReset = () => {
261 dispatch({ type: 'Account/resetSearch' });
262 handleGetList(0, 10);
263 };
264
265 // 关闭弹框
266 const handleCancelModal = () => {
267 dispatch({ type: 'Account/cancelModal' });
268 };
269
270 // 点击保存
271 const handleOk = values => {
272 const { saveType } = values;
273 if (saveType === 'ACCOUNT') {
274 dispatch({ type: 'Account/updateApplication', payload: { ...values, saveType: undefined } });
275 } else if (saveType === 'PASSWORD') {
276 dispatch({ type: 'Account/resetPassword', payload: { ...values, saveType: undefined } });
277 }
278 };
279
280 return (
281 <StyledPageContainer>
282 <StyledPageHeader>
283 <Button
284 type="primary"
285 onClick={() => {
286 dispatch({
287 type: 'Account/changeState',
288 payload: {
289 dataModal: {
290 modalType: 'ACCOUNT_SET_MODAL',
291 modalShow: true,
292 modalData: {},
293 },
294 },
295 });
296 }}
297 >
298 新增用户
299 </Button>
300 </StyledPageHeader>
301 <StyledPageContent>
302 <Card bordered={false}>
303 <SearchForm
304 dataSearch={dataSearch}
305 handleReset={handleReset}
306 handleFinish={handleFinish}
307 />
308 <div className="mt-16">
309 <DataTable
310 dispatch={dispatch}
311 loading={loading}
312 handleGetList={handleGetList}
313 dataAccount={dataAccount}
314 currentUser={currentUser}
315 />
316 </div>
317 </Card>
318 </StyledPageContent>
319 <ModalUpdateAccount
320 dataModal={dataModal}
321 handleCancelModal={handleCancelModal}
322 handleOk={handleOk}
323 />
324 <ModalResetPassword
325 dataModal={dataModal}
326 handleCancelModal={handleCancelModal}
327 handleOk={handleOk}
328 />
329 </StyledPageContainer>
330 );
331 };
332
333 export default connect(({ Account, user, loading }) => ({
334 ...Account,
335 currentUser: user.currentUser,
336 loading: loading.effects['Account/getAccountList'],
337 }))(Account);
1 import { message } from 'antd';
2 import * as services from '@/services/account';
3
4 /* SerachParams */
5 const staticSearch = {
6 keyword: undefined,
7 current: 1,
8 size: 10,
9 status: 'ALL',
10 };
11
12 export default {
13 namespace: 'Account',
14 state: {
15 dataSearch: {
16 ...staticSearch,
17 },
18 dataAccount: {
19 records: [],
20 current: 1,
21 size: 10,
22 total: 0,
23 totalPage: 0,
24 },
25 dataModal: {
26 modalType: '',
27 modalShow: false,
28 modalData: {},
29 },
30 dataRoleList: [],
31 },
32 effects: {
33 /* 获取账号列表 */
34 *getAccountList({ payload }, { call, put, select }) {
35 const {
36 dataSearch,
37 dataSearch: { status },
38 } = yield select(state => state.Account);
39 try {
40 const res = yield call(services.getAccountList, {
41 current: 1,
42 size: 10,
43 ...dataSearch,
44 ...payload,
45 status: status === 'ALL' ? '' : status,
46 });
47 if (res.code === 0) {
48 res.data.records = res.data.records || [];
49 yield put({
50 type: 'changeState',
51 payload: {
52 dataAccount: res.data,
53 },
54 });
55 }
56 } catch (err) {
57 console.error(err);
58 }
59 },
60 /* 新增、修改账号 */
61 *updateApplication({ payload }, { call, put, select }) {
62 const { id } = payload;
63 const {
64 dataAccount: { current, size },
65 } = yield select(state => state.Account);
66 try {
67 const res = yield call(services[!id ? 'addAccount' : 'updateAccount'], payload);
68 if (res.code === 0) {
69 message.success(!id ? '新增成功' : '修改成功');
70 yield put({
71 type: 'getAccountList',
72 payload: { size, current },
73 });
74 yield put({ type: 'cancelModal' });
75 }
76 } catch (err) {
77 console.error(err);
78 }
79 },
80 /* 重置密码 */
81 *resetPassword({ payload }, { call, put, select }) {
82 try {
83 const res = yield call(services.resetPassword, payload);
84 if (res.code === 0) {
85 message.success('重置成功');
86 yield put({ type: 'cancelModal' });
87 }
88 } catch (err) {
89 console.error(err);
90 }
91 },
92 /* 状态修改 */
93 *changeStatus({ payload }, { call, put, select }) {
94 const { status } = payload;
95 const {
96 dataAccount: { current, size },
97 } = yield select(state => state.Account);
98 try {
99 const res = yield call(services.changeStatus, payload);
100 if (res.code === 0) {
101 message.success(status == '1' ? '启用成功' : '禁用成功');
102 yield put({
103 type: 'getAccountList',
104 payload: { size, current },
105 });
106 }
107 } catch (err) {
108 console.error(err);
109 }
110 },
111 /* 获取角色列表 */
112 *getRoleSelect({ payload }, { call, put, select }) {
113 try {
114 const res = yield call(services.getRoleSelect);
115 if (res.code === 0) {
116 yield put({
117 type: 'changeState',
118 payload: {
119 dataRoleList: res.data,
120 },
121 });
122 }
123 } catch (err) {
124 console.error(err);
125 }
126 },
127 },
128 reducers: {
129 changeState(state, { payload }) {
130 return {
131 ...state,
132 ...payload,
133 };
134 },
135 cancelModal(state, { payload }) {
136 return {
137 ...state,
138 dataModal: {
139 modalType: '',
140 modalShow: false,
141 modalData: {},
142 },
143 };
144 },
145 resetSearch(state, { payload }) {
146 return {
147 ...state,
148 dataSearch: {
149 ...staticSearch,
150 },
151 };
152 },
153 },
154 };
1 /**
2 * Author: llw
3 * Date: 2022.9.14
4 * Description: [菜单层级树]
5 */
6 import React from 'react';
7 import { connect } from 'umi';
8 import { Tree } from 'antd';
9
10 const { TreeNode } = Tree;
11
12 const MenuTree = props => {
13 const { dispatch, dataSystemMenu, dataLevelMenu, dataExpandedKeys } = props;
14 // TreeNode的DOM
15 const renderTreeNode = data => {
16 return data.map(item => {
17 if (item.children && item.children.length) {
18 return (
19 <TreeNode title={item.title} key={item.resourceCode}>
20 {renderTreeNode(item.children)}
21 </TreeNode>
22 );
23 }
24 return <TreeNode title={item.title} key={item.resourceCode} />;
25 });
26 };
27 return (
28 <Tree
29 showLine
30 showIcon
31 className="info-tree"
32 expandedKeys={dataExpandedKeys}
33 onSelect={([key], e) => {
34 const meunInfo = dataLevelMenu.find(item => item.resourceCode === key);
35 dispatch({
36 type: 'SystemMenu/changeState',
37 payload: {
38 dataMenuInfo: !key || !meunInfo ? [] : [{ ...meunInfo, children: undefined }],
39 },
40 });
41 }}
42 >
43 {renderTreeNode(dataSystemMenu)}
44 </Tree>
45 );
46 };
47 export default connect(({ SystemMenu }) => ({
48 ...SystemMenu,
49 }))(MenuTree);
1 /**
2 * Author: llw
3 * Date: 2022.9.14
4 * Description: [设置菜单弹框]
5 */
6 import React, { useEffect, useState } from 'react';
7 import { connect } from 'umi';
8 import { Drawer, Radio, Form, Select, Input, Button } from 'antd';
9
10 const { Option } = Select;
11 const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
12
13 const ModalSetMenu = props => {
14 const [menuType, setType] = useState(1);
15 const [form] = Form.useForm();
16 let {
17 dispatch,
18 handleCancelModal,
19 dataLevelMenu = [],
20 dataModal: {
21 modalType,
22 modalShow,
23 modalData: { id, title, resourceCode, resourceNodeType, isEdit, parentId },
24 },
25 } = props;
26
27 /* 重置resourceNodeType */
28 useEffect(() => {
29 if (modalType === 'MENU_SET_MODAL' && modalShow) {
30 setType(resourceNodeType ? +resourceNodeType : 1);
31 setTimeout(() => {
32 form.resetFields();
33 form.setFieldsValue({
34 parentId,
35 title,
36 resourceCode,
37 resourceNodeType: resourceNodeType ? +resourceNodeType : 1,
38 });
39 }, 100);
40 }
41 }, [modalType, modalShow]);
42
43 /* 点击保存 */
44 const handleSave = () => {
45 form.validateFields().then(values => {
46 dispatch({ type: 'SystemMenu/updateSystemMenu', payload: { ...values, id } });
47 });
48 };
49
50 /* 选择菜单类型 */
51 const handleChangeRadio = value => {
52 form.resetFields();
53 setType(value);
54 form.setFieldsValue({ resourceNodeType: value });
55 };
56
57 return (
58 <Drawer
59 title="菜单设置"
60 placement="right"
61 width={600}
62 maskClosable={false}
63 onClose={handleCancelModal}
64 visible={modalType === 'MENU_SET_MODAL' && modalShow}
65 footer={
66 <div
67 style={{
68 textAlign: 'right',
69 }}
70 >
71 <Button onClick={handleCancelModal} className="mr-10">
72 取消
73 </Button>
74 <Button onClick={handleSave} type="primary">
75 保存
76 </Button>
77 </div>
78 }
79 >
80 <Form
81 form={form}
82 {...formItemLayout}
83 name="auth_set_modal"
84 initialValues={{ resourceNodeType: 1 }}
85 >
86 <Form.Item name="resourceNodeType" label="菜单类型">
87 <Radio.Group buttonStyle="solid" onChange={e => handleChangeRadio(e.target.value)}>
88 <Radio.Button disabled={isEdit && +resourceNodeType !== 1 ? true : false} value={1}>
89 一级菜单
90 </Radio.Button>
91 <Radio.Button disabled={isEdit && +resourceNodeType !== 2 ? true : false} value={2}>
92 子菜单
93 </Radio.Button>
94 {/* <Radio.Button disabled={isEdit && resourceNodeType !== 3 ? true : false} value={3}>
95 按钮
96 </Radio.Button> */}
97 </Radio.Group>
98 </Form.Item>
99 {menuType !== 1 && (
100 <Form.Item
101 name="parentId"
102 label={menuType === 2 ? '上级菜单' : '包含菜单'}
103 rules={[
104 {
105 required: true,
106 message: menuType === 2 ? '请选择上级菜单' : '请选择包含该按钮的菜单',
107 },
108 ]}
109 >
110 <Select
111 showSearch
112 placeholder={menuType === 2 ? '请选择上级菜单' : '请选择包含该按钮的菜单'}
113 optionFilterProp="children"
114 filterOption={(input, option) =>
115 option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
116 }
117 >
118 {dataLevelMenu
119 .filter(_ =>
120 isEdit ? _.resourceCode && _.resourceCode !== resourceCode : _.resourceCode,
121 )
122 .map(item => {
123 return (
124 <Option key={item.id} value={item.id}>
125 {item.title}
126 </Option>
127 );
128 })}
129 </Select>
130 </Form.Item>
131 )}
132 <Form.Item
133 name="title"
134 label={menuType === 3 ? '按钮名称' : '菜单名称'}
135 rules={[
136 { required: true, message: menuType === 3 ? '请输入按钮名称' : '请输入菜单名称' },
137 { min: 2, max: 15, message: '名称长度2~15个字符' },
138 ]}
139 >
140 <Input
141 maxLength={15}
142 placeholder={menuType === 3 ? '请输入按钮名称' : '请输入菜单名称'}
143 />
144 </Form.Item>
145 <Form.Item
146 name="resourceCode"
147 label={menuType === 3 ? '按钮code' : '菜单code'}
148 rules={[
149 { required: true, message: menuType === 3 ? '请输入按钮code' : '请输入菜单code' },
150 ]}
151 >
152 <Input
153 maxLength={20}
154 placeholder={menuType === 3 ? '请输入按钮code' : '请输入菜单code'}
155 />
156 </Form.Item>
157 </Form>
158 </Drawer>
159 );
160 };
161 export default connect(({ SystemMenu }) => ({
162 ...SystemMenu,
163 }))(ModalSetMenu);
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 { Card, Button, Table, Tooltip, Divider, Modal } from 'antd';
9 import ModalSetMenu from './ModalSetMenu';
10 import MenuTree from './MenuTree';
11 import { EditOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
12 import {
13 StyledPageContainer,
14 StyledPageHeader,
15 StyledPageContent,
16 StyledPageFlex,
17 StyledPageLeft,
18 StyledPageRight,
19 } from '@/components/style';
20
21 /* DataTable */
22 const DataTable = props => {
23 const { dispatch, dataMenuInfo } = props;
24
25 const columns = [
26 {
27 title: '菜单ID',
28 dataIndex: 'id',
29 align: 'center',
30 width: 120,
31 },
32 {
33 title: '菜单名称',
34 dataIndex: 'title',
35 align: 'center',
36 width: 160,
37 },
38 {
39 title: '菜单code',
40 dataIndex: 'resourceCode',
41 align: 'center',
42 width: 160,
43 },
44 {
45 title: '操作',
46 align: 'center',
47 width: 120,
48 render(t, r) {
49 return (
50 <>
51 <Tooltip placement="top" title="编辑">
52 <EditOutlined
53 style={{ cursor: 'pointer', fontSize: 16 }}
54 onClick={() => {
55 dispatch({
56 type: 'SystemMenu/changeState',
57 payload: {
58 dataModal: {
59 modalType: 'MENU_SET_MODAL',
60 modalShow: true,
61 modalData: { ...r, isEdit: true },
62 },
63 },
64 });
65 }}
66 />
67 </Tooltip>
68 <Divider type="vertical" style={{ margin: '0 16px' }} />
69 <Tooltip
70 placement="top"
71 title="删除"
72 onClick={() => {
73 Modal.confirm({
74 title: '删除',
75 icon: <ExclamationCircleOutlined />,
76 content: '确定删除该菜单吗?',
77 centered: true,
78 onOk() {
79 dispatch({
80 type: 'SystemMenu/delSystemMenu',
81 payload: { id: r.id },
82 });
83 },
84 onCancel() {},
85 });
86 }}
87 >
88 <DeleteOutlined style={{ cursor: 'pointer', fontSize: 16 }} />
89 </Tooltip>
90 </>
91 );
92 },
93 },
94 ];
95 return <Table rowKey="id" dataSource={dataMenuInfo} columns={columns} pagination={false} />;
96 };
97
98 /* Main */
99 const SystemMenu = props => {
100 const { dispatch, dataModal, dataMenuInfo } = props;
101
102 useEffect(() => {
103 dispatch({ type: 'SystemMenu/getAllSystemMenu' });
104 }, []);
105
106 // 关闭弹框
107 const handleCancelModal = () => {
108 dispatch({ type: 'SystemMenu/cancelModal' });
109 };
110
111 // 点击保存
112 const handleOk = values => {};
113
114 return (
115 <StyledPageContainer>
116 <StyledPageHeader>
117 <Button
118 type="primary"
119 onClick={() => {
120 dispatch({
121 type: 'SystemMenu/changeState',
122 payload: {
123 dataModal: {
124 modalType: 'MENU_SET_MODAL',
125 modalShow: true,
126 modalData: {},
127 },
128 },
129 });
130 }}
131 >
132 新增菜单
133 </Button>
134 </StyledPageHeader>
135 <StyledPageContent>
136 <StyledPageFlex>
137 <StyledPageLeft>
138 <MenuTree />
139 </StyledPageLeft>
140 <StyledPageRight>
141 <Card bordered={false}>
142 <div className="mt-16">
143 <DataTable dispatch={dispatch} dataMenuInfo={dataMenuInfo} />
144 </div>
145 </Card>
146 </StyledPageRight>
147 </StyledPageFlex>
148 </StyledPageContent>
149 <ModalSetMenu
150 dataModal={dataModal}
151 handleCancelModal={handleCancelModal}
152 handleOk={handleOk}
153 />
154 </StyledPageContainer>
155 );
156 };
157
158 export default connect(({ SystemMenu }) => ({
159 ...SystemMenu,
160 }))(SystemMenu);
1 import * as services from '@/services/menu';
2 import { message } from 'antd';
3 import { getPermissionList } from '@/utils/menu';
4
5 export default {
6 namespace: 'SystemMenu',
7 state: {
8 dataSystemMenu: [], // 层级菜单
9 dataLevelMenu: [], // 铺平菜单
10 dataMenuInfo: [], // 菜单详情
11 dataExpandedKeys: [], // 菜单code
12 dataModal: {
13 modalType: '',
14 modalShow: false,
15 modalData: {},
16 },
17 },
18 effects: {
19 /* 获取菜单层级 */
20 *getAllSystemMenu({ payload }, { call, put, select }) {
21 const { dataMenuInfo } = yield select(state => state.SystemMenu);
22 try {
23 const res = yield call(services.getAllMenu);
24 if (res.code === 0) {
25 const { dataMenu, dataExpandedKeys } = getPermissionList(res.data || []);
26 yield put({
27 type: 'changeState',
28 payload: {
29 dataSystemMenu: res.data || [],
30 dataLevelMenu: dataMenu,
31 dataExpandedKeys,
32 },
33 });
34 // 处理菜单选中进行编辑、删除的情况
35 if (payload && payload.isEdit && dataMenuInfo.length) {
36 const selectMenuInfo = dataMenu.find(
37 item => item.resourceCode === dataMenuInfo[0].resourceCode,
38 );
39 yield put({
40 type: 'changeState',
41 payload: {
42 dataMenuInfo: [{ ...selectMenuInfo, children: undefined }],
43 },
44 });
45 } else if (payload && payload.isDel && dataMenuInfo.length) {
46 const selectMenuInfo = dataMenu.find(
47 item => item.resourceCode === dataMenuInfo[0].resourceCode,
48 );
49 yield put({
50 type: 'changeState',
51 payload: {
52 dataMenuInfo: selectMenuInfo ? [{ ...selectMenuInfo, children: undefined }] : [],
53 },
54 });
55 }
56 }
57 } catch (err) {
58 console.error(err);
59 }
60 },
61 /* 新增、编辑菜单 */
62 *updateSystemMenu({ payload }, { call, put, select }) {
63 try {
64 const res = yield call(services[!payload.id ? 'addMenu' : 'updateMenu'], payload);
65 if (res.code === 0) {
66 message.success(!payload.id ? '新增成功' : '编辑成功');
67 yield put({ type: 'cancelModal' });
68 yield put({ type: 'getAllSystemMenu', payload: { isEdit: payload.id ? true : false } });
69 }
70 } catch (err) {
71 console.error(err);
72 }
73 },
74 /* 删除菜单 */
75 *delSystemMenu({ payload }, { call, put, select }) {
76 try {
77 const res = yield call(services.delMenu, payload);
78 if (res.code === 0) {
79 message.success('删除成功');
80 yield put({ type: 'getAllSystemMenu', payload: { isDel: true } });
81 }
82 } catch (err) {
83 console.error(err);
84 }
85 },
86 },
87 reducers: {
88 changeState(state, { payload }) {
89 return {
90 ...state,
91 ...payload,
92 };
93 },
94 cancelModal(state, { payload }) {
95 return {
96 ...state,
97 dataModal: {
98 modalType: '',
99 modalShow: false,
100 modalData: {},
101 },
102 };
103 },
104 },
105 };
1 /**
2 * Author: Charles
3 * Date: 2022.9.13
4 * Description: [更新角色]
5 */
6 import React, { useEffect, useState } from 'react';
7 import { connect } from 'umi';
8 import { Modal, Form, Input, Button, TreeSelect } from 'antd';
9
10 const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
11
12 const ModalSetRole = props => {
13 const [form] = Form.useForm();
14 const {
15 dispatch,
16 dataUserMenu,
17 handleCancelModal,
18 handleOk,
19 dataRoleMenuId,
20 dataModal: {
21 modalType,
22 modalShow,
23 modalData: { id, name, description },
24 },
25 } = props;
26
27 useEffect(() => {
28 if (modalType === 'ROLE_SET_MODAL' && modalShow) {
29 dispatch({ type: 'Role/getUserMenu' });
30 if (id) {
31 dispatch({ type: 'Role/getRoleMenuId', payload: { id } });
32 }
33 form.resetFields();
34 form.setFieldsValue({
35 name,
36 description,
37 });
38 }
39 }, [modalType, modalShow]);
40
41 const [value, setValue] = useState([]);
42
43 useEffect(() => {
44 form.setFieldsValue({
45 menuIds: dataRoleMenuId,
46 });
47 }, [dataRoleMenuId]);
48
49 // treeSelect配置
50 const tProps = {
51 treeData: dataUserMenu,
52 value: dataRoleMenuId || [],
53 onChange: () => {},
54 treeCheckable: true,
55 showCheckedStrategy: TreeSelect.SHOW_ALL,
56 placeholder: '请选择菜单权限',
57 style: {
58 width: '100%',
59 },
60 fieldNames: {
61 label: 'title',
62 value: 'id',
63 },
64 };
65
66 /* 点击保存 */
67 const handleSave = () => {
68 form.validateFields().then(values => {
69 console.log(values, 'values');
70 handleOk({ id, ...values });
71 });
72 };
73
74 return (
75 <Modal
76 title={id ? '编辑角色' : '新增角色'}
77 placement="right"
78 width={700}
79 maskClosable={false}
80 onCancel={handleCancelModal}
81 visible={modalType === 'ROLE_SET_MODAL' && modalShow}
82 footer={
83 <div
84 style={{
85 textAlign: 'right',
86 }}
87 >
88 <Button onClick={handleCancelModal} className="mr-10">
89 取消
90 </Button>
91 <Button onClick={handleSave} type="primary">
92 保存
93 </Button>
94 </div>
95 }
96 >
97 <Form form={form} {...formItemLayout} name="role_set_modal">
98 <Form.Item
99 name="name"
100 label="角色名称"
101 rules={[
102 { required: true, message: '请输入角色名称,由2~10位非空格的字符组成' },
103 { pattern: /^[\S]{2,10}$/, message: '角色名称由2~10位非空格的字符组成' },
104 ]}
105 >
106 <Input placeholder="请输入角色名称" />
107 </Form.Item>
108 <Form.Item
109 name="description"
110 label="角色描述"
111 rules={[
112 { required: false, message: '请输入角色描述' },
113 { max: 30, message: '角色描述字符长度不能超过30' },
114 ]}
115 >
116 <Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} placeholder="请输入角色描述" />
117 </Form.Item>
118 <Form.Item
119 name="menuIds"
120 label="菜单权限"
121 rules={[{ required: true, message: '请选择菜单权限' }]}
122 >
123 <TreeSelect {...tProps} />
124 </Form.Item>
125 </Form>
126 </Modal>
127 );
128 };
129
130 export default connect(({ Role }) => ({
131 ...Role,
132 }))(ModalSetRole);
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 { Card, Form, Input, Button, Table, Divider, Tooltip, Modal } from 'antd';
9 import { EditOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
10 import { paginations } from '@/constants';
11 import ModalUpdateRole from './ModalUpdateRole';
12 import { StyledPageContainer, StyledPageHeader, StyledPageContent } from '@/components/style';
13
14 const FormItem = Form.Item;
15
16 /* SearchForm */
17 const SearchForm = props => {
18 const [form] = Form.useForm();
19 const { dataSearch, handleReset, handleFinish } = props;
20
21 useEffect(() => {
22 form.setFieldsValue({
23 ...dataSearch,
24 });
25 }, [dataSearch]);
26
27 /* 点击搜索 */
28
29 const onFinish = values => {
30 handleFinish(values);
31 };
32
33 /* 点击重置 */
34 const onReset = () => {
35 handleReset();
36 };
37
38 return (
39 <Form name="Form_Role" layout="inline" form={form} onFinish={onFinish}>
40 <FormItem label="角色名称" name="search">
41 <Input placeholder="请输入角色名称" style={{ width: '220px' }} />
42 </FormItem>
43 <FormItem>
44 <Button type="primary" htmlType="submit" className="mr-15">
45 搜索
46 </Button>
47 <Button className="mr-15" htmlType="button" onClick={onReset}>
48 重置
49 </Button>
50 </FormItem>
51 </Form>
52 );
53 };
54
55 /* DataTable */
56 const DataTable = props => {
57 const {
58 dispatch,
59 loading,
60 handleGetList,
61 dataRole: { records = [], current, size, total },
62 } = props;
63
64 /* 点击分页 */
65 const handlePageChange = (current, size) => {
66 handleGetList(current, size);
67 };
68
69 const columns = [
70 {
71 title: '序号',
72 dataIndex: 'id',
73 align: 'center',
74 width: 120,
75 render(t, r, idx) {
76 return (current - 1) * size + idx + 1;
77 },
78 },
79 {
80 title: '角色名称',
81 dataIndex: 'name',
82 align: 'center',
83 width: 160,
84 },
85 {
86 title: '角色描述',
87 dataIndex: 'description',
88 align: 'center',
89 width: 200,
90 render: (t, r) => {
91 return t || '-';
92 },
93 },
94 {
95 title: '创建时间',
96 dataIndex: 'createTime',
97 align: 'center',
98 width: 200,
99 render(t, r, idx) {
100 return t || '-';
101 },
102 },
103 {
104 title: '操作',
105 align: 'center',
106 width: 150,
107 render(t, r) {
108 return (
109 <>
110 <Tooltip placement="top" title="编辑">
111 <EditOutlined
112 style={{ cursor: 'pointer', fontSize: 16 }}
113 onClick={() => {
114 dispatch({
115 type: 'Role/changeState',
116 payload: {
117 dataModal: {
118 modalType: 'ROLE_SET_MODAL',
119 modalShow: true,
120 modalData: r,
121 },
122 },
123 });
124 }}
125 />
126 </Tooltip>
127 <Divider type="vertical" style={{ margin: '0 16px' }} />
128 <Tooltip
129 placement="top"
130 title="删除"
131 onClick={() => {
132 Modal.confirm({
133 title: '删除',
134 icon: <ExclamationCircleOutlined />,
135 content: '确定删除该角色吗?',
136 centered: true,
137 onOk() {
138 dispatch({
139 type: 'Role/delRole',
140 payload: { id: r.id },
141 });
142 },
143 onCancel() {},
144 });
145 }}
146 >
147 <DeleteOutlined style={{ cursor: 'pointer', fontSize: 16 }} />
148 </Tooltip>
149 </>
150 );
151 },
152 },
153 ];
154 const pagination = {
155 ...paginations,
156 total: total,
157 pageSize: size,
158 current,
159 showSizeChanger: total > 10,
160 onChange: (current, pageSize) => {
161 handlePageChange(current, pageSize);
162 },
163 onShowSizeChange: (current, pageSize) => {
164 handlePageChange(1, pageSize);
165 },
166 showTotal(total) {
167 return `总共 ${total} 条数据`;
168 },
169 };
170 return (
171 <Table
172 rowKey="id"
173 loading={loading}
174 dataSource={records}
175 columns={columns}
176 pagination={pagination}
177 scroll={{ y: `calc(100vh - 353px)` }}
178 />
179 );
180 };
181
182 /* Main */
183 const Role = props => {
184 const {
185 dispatch,
186 loading,
187 dataSearch,
188 dataModal,
189 currentUser,
190 dataRole,
191 dataRole: { size },
192 } = props;
193
194 useEffect(() => {
195 handleGetList(1, 10);
196 }, []);
197
198 /* 账号列表 */
199 const handleGetList = (current, size) => {
200 dispatch({
201 type: 'Role/getRoleList',
202 payload: {
203 current: current || 1,
204 size: size || 10,
205 },
206 });
207 };
208
209 /* 点击搜索 */
210 const handleFinish = values => {
211 const { status, search } = values;
212 dispatch({
213 type: 'Role/changeState',
214 payload: {
215 dataSearch: {
216 search,
217 status,
218 },
219 },
220 });
221 handleGetList(0, size);
222 };
223
224 /* 点击重置 */
225 const handleReset = () => {
226 dispatch({ type: 'Role/resetSearch' });
227 handleGetList(0, 10);
228 };
229
230 // 关闭弹框
231 const handleCancelModal = () => {
232 dispatch({ type: 'Role/cancelModal' });
233 };
234
235 // 点击保存
236 const handleOk = values => {
237 dispatch({ type: 'Role/updateRole', payload: { ...values } });
238 };
239
240 return (
241 <StyledPageContainer>
242 <StyledPageHeader>
243 <Button
244 type="primary"
245 onClick={() => {
246 dispatch({
247 type: 'Role/changeState',
248 payload: {
249 dataModal: {
250 modalType: 'ROLE_SET_MODAL',
251 modalShow: true,
252 modalData: {},
253 },
254 },
255 });
256 }}
257 >
258 新增角色
259 </Button>
260 </StyledPageHeader>
261 <StyledPageContent>
262 <Card bordered={false}>
263 <SearchForm
264 dataSearch={dataSearch}
265 handleReset={handleReset}
266 handleFinish={handleFinish}
267 />
268 <div className="mt-16">
269 <DataTable
270 dispatch={dispatch}
271 loading={loading}
272 handleGetList={handleGetList}
273 dataRole={dataRole}
274 currentUser={currentUser}
275 />
276 </div>
277 </Card>
278 </StyledPageContent>
279 <ModalUpdateRole
280 dataModal={dataModal}
281 handleCancelModal={handleCancelModal}
282 handleOk={handleOk}
283 />
284 </StyledPageContainer>
285 );
286 };
287
288 export default connect(({ Role, user, loading }) => ({
289 ...Role,
290 loading: loading.effects['Role/getRoleList'],
291 }))(Role);
1 import { message } from 'antd';
2 import * as services from '@/services/role';
3
4 /* SerachParams */
5 const staticSearch = {
6 search: undefined,
7 current: 1,
8 size: 10,
9 };
10
11 export default {
12 namespace: 'Role',
13 state: {
14 dataSearch: {
15 ...staticSearch,
16 },
17 dataRole: {
18 records: [],
19 current: 1,
20 size: 10,
21 total: 0,
22 totalPage: 0,
23 },
24 dataModal: {
25 modalType: '',
26 modalShow: false,
27 modalData: {},
28 },
29 dataUserMenu: [],
30 dataRoleMenuId: [],
31 },
32 effects: {
33 /* 获取角色列表 */
34 *getRoleList({ payload }, { call, put, select }) {
35 const { dataSearch } = yield select(state => state.Role);
36 try {
37 const res = yield call(services.getRoleList, {
38 current: 1,
39 size: 10,
40 ...dataSearch,
41 ...payload,
42 });
43 if (res.code === 0) {
44 res.data.records = res.data.records || [];
45 yield put({
46 type: 'changeState',
47 payload: {
48 dataRole: res.data,
49 },
50 });
51 }
52 } catch (err) {
53 console.error(err);
54 }
55 },
56 /* 新增、修改角色 */
57 *updateRole({ payload }, { call, put, select }) {
58 const { id } = payload;
59 const {
60 dataRole: { current, size },
61 } = yield select(state => state.Role);
62 const {
63 currentUser: { roleId },
64 } = yield select(state => state.user);
65 try {
66 const res = yield call(services[!id ? 'addRole' : 'updateRole'], payload);
67 if (res.code === 0) {
68 message.success(!id ? '新增成功' : '修改成功');
69 yield put({
70 type: 'getRoleList',
71 payload: { size, current },
72 });
73 yield put({ type: 'cancelModal' });
74 if (roleId === id) {
75 setTimeout(() => {
76 window.location.href = '/user/login';
77 }, 500);
78 }
79 }
80 } catch (err) {
81 console.error(err);
82 }
83 },
84 /* 删除角色 */
85 *delRole({ payload }, { call, put, select }) {
86 const {
87 dataRole: { records = [], current, size },
88 } = yield select(state => state.Role);
89 try {
90 const res = yield call(services.delRole, payload);
91 if (res.code === 0) {
92 message.success('删除成功');
93 yield put({
94 type: 'getRoleList',
95 payload: {
96 size,
97 current: records.length === 1 ? (current === 1 ? 1 : current - 1) : current,
98 },
99 });
100 }
101 } catch (err) {
102 console.error(err);
103 }
104 },
105 /* 获取用户菜单 */
106 *getUserMenu({ payload }, { call, put, select }) {
107 try {
108 const res = yield call(services.getUserMenu);
109 if (res.code === 0) {
110 yield put({
111 type: 'changeState',
112 payload: {
113 dataUserMenu: res.data || [],
114 },
115 });
116 }
117 } catch (err) {
118 console.error(err);
119 }
120 },
121 /* 获取角色菜单id集合 */
122 *getRoleMenuId({ payload }, { call, put, select }) {
123 try {
124 const res = yield call(services.getRoleMenuId, payload);
125 if (res.code === 0) {
126 yield put({
127 type: 'changeState',
128 payload: {
129 dataRoleMenuId: res.data.menuId || [],
130 },
131 });
132 }
133 } catch (err) {
134 console.error(err);
135 }
136 },
137 },
138 reducers: {
139 changeState(state, { payload }) {
140 return {
141 ...state,
142 ...payload,
143 };
144 },
145 cancelModal(state, { payload }) {
146 return {
147 ...state,
148 dataModal: {
149 modalType: '',
150 modalShow: false,
151 modalData: {},
152 },
153 };
154 },
155 resetSearch(state, { payload }) {
156 return {
157 ...state,
158 dataSearch: {
159 ...staticSearch,
160 },
161 };
162 },
163 },
164 };
1 /**
2 * Author: llw
3 * Date: 2020-7-16
4 * Description: [登录页面]
5 */
6 import React, { useEffect } from 'react';
7 import { connect } from 'umi';
8 import { IconFontConfig } from '@/common';
9 import { Form, Button, Input } from 'antd';
10 import loginless from './index.less';
11 import {aesEncrypt} from '@/utils/encrypt';
12
13 const Loginform = props => {
14 const { loading, dispatch } = props;
15
16
17 const handleSubmit = values => {
18 const loginData = {
19 username: values.username,
20 password: aesEncrypt(values.password)
21 };
22 dispatch({
23 type: 'login/login',
24 payload: loginData,
25 });
26 };
27
28 return (
29 <div className={loginless.main}>
30 <Form onFinish={handleSubmit}>
31 <Form.Item
32 name="username"
33 rules={[{ required: true, message: '请输入您的用户名' }]}
34 >
35 <Input
36 size="large"
37 placeholder="请输入您的用户名"
38 prefix={<IconFontConfig type="icon-user" />}
39 />
40 </Form.Item>
41 <Form.Item
42 name="password"
43 rules={[{ required: true, message: '请输入您的密码' }]}
44 >
45 <Input.Password
46 size="large"
47 visibilityToggle={false}
48 placeholder="请输入您的密码"
49 prefix={<IconFontConfig type="icon-password" />}
50 />
51 </Form.Item>
52 <Button
53 block
54 size="large"
55 type="primary"
56 htmlType="submit"
57 loading={loading}
58 className={loginless.loginbtn}
59 >
60 登录
61 </Button>
62 </Form>
63 </div>
64 );
65 };
66
67 export default connect(({ login, loading }) => ({
68 login,
69 loading: loading.effects['login/login'],
70 }))(Loginform);
1 .title {
2 font-size: 15px;
3 font-weight: 500;
4 color: rgba(51, 51, 51, 1);
5 margin-bottom: 25px;
6 }
7 .main {
8 width: 100%;
9 padding: 40px 25px;
10 .ipticon {
11 width: auto;
12 height: 18px;
13 margin-right: 11px;
14 }
15
16 :global(.ant-form-item) {
17 height: 62px;
18 margin-bottom: 0;
19 input {
20 font-size: 14px;
21 padding-left: 3px;
22 }
23 input:-webkit-autofill,
24 textarea:-webkit-autofill,
25 select:-webkit-autofill {
26 background-color: #fff !important;
27 background-image: none;
28 }
29 input:-webkit-autofill:hover {
30 background-color: #fff !important;
31 -webkit-box-shadow: 0 0 0px 1000px white inset;
32 }
33 input:-webkit-autofill {
34 background-color: #fff !important;
35 -webkit-box-shadow: 0 0 0px 1000px white inset;
36 }
37 input:-webkit-autofill:focus {
38 background-color: #fff !important;
39 /* style code */
40 -webkit-box-shadow: 0 0 0px 1000px white inset;
41 }
42 }
43 }
44
45 .loginbtn {
46 letter-spacing: -1px;
47 font-size: 15px;
48 margin-top: 10px;
49 height: 44px;
50 }
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 { Table, Tooltip } from 'antd';
9 import { paginations, mapStatus, mapRiskType, mapRiskSourceType } from '@/constants';
10 import { StyledEllipsisWrap } from '@/components/style';
11 import moment from 'moment';
12
13 /* DataTable */
14 const DataTable = props => {
15 const {
16 loading,
17 handleGetList,
18 dataRisk: { records = [], current, size, total },
19 } = props;
20
21 /* 点击分页 */
22 const handlePageChange = (current, size) => {
23 handleGetList(current, size);
24 };
25
26 const columns = [
27 {
28 title: '序号',
29 dataIndex: 'id',
30 align: 'center',
31 fixed: 'left',
32 width: 80,
33 render(t, r, idx) {
34 return (current - 1) * size + idx + 1;
35 },
36 },
37 {
38 title: '风险源名称',
39 dataIndex: 'riskName',
40 align: 'center',
41 width: 160,
42 render: (t, r) => {
43 return (
44 <Tooltip placement="top" title={t}>
45 <StyledEllipsisWrap maxLine={1}>{t}</StyledEllipsisWrap>
46 </Tooltip>
47 );
48 },
49 },
50 {
51 title: '风险源地址',
52 dataIndex: 'riskUrl',
53 align: 'center',
54 width: 160,
55 render: (t, r) => {
56 return (
57 <Tooltip placement="top" title={t}>
58 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
59 </Tooltip>
60 );
61 },
62 },
63 {
64 title: '风险站点',
65 dataIndex: 'riskHost',
66 align: 'center',
67 width: 160,
68 render: (t, r) => {
69 return (
70 <Tooltip placement="top" title={t}>
71 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
72 </Tooltip>
73 );
74 },
75 },
76 {
77 title: '关键词',
78 dataIndex: 'riskKeyword',
79 align: 'center',
80 width: 260,
81 render: (t, r) => {
82 return (
83 <Tooltip placement="top" title={t}>
84 <StyledEllipsisWrap maxLine={2}>{t}</StyledEllipsisWrap>
85 </Tooltip>
86 );
87 },
88 },
89 {
90 title: '风险类型',
91 dataIndex: 'riskType',
92 align: 'center',
93 width: 160,
94 render: (t, r) => {
95 return mapRiskType[t] ? mapRiskType[t].label : '-';
96 },
97 },
98 {
99 title: '风险源类型',
100 dataIndex: 'fileFormat',
101 align: 'center',
102 width: 160,
103 render: (t, r) => {
104 return mapRiskSourceType[t] ? mapRiskSourceType[t].label : '-';
105 },
106 },
107 {
108 title: '检测时间',
109 dataIndex: 'checkTime',
110 align: 'center',
111 width: 160,
112 render: (t, r) => {
113 return t ? moment(t).format('YYYY-MM-DD HH:mm:ss') : '-';
114 },
115 },
116 {
117 title: '处理状态',
118 dataIndex: 'status',
119 align: 'center',
120 fixed: 'right',
121 width: 100,
122 render: (t, r) => {
123 return mapStatus[t] ? mapStatus[t].label : '-';
124 },
125 },
126 ];
127 const pagination = {
128 ...paginations,
129 total: total,
130 pageSize: size,
131 current,
132 showSizeChanger: total > 10,
133 onChange: (current, pageSize) => {
134 handlePageChange(current, pageSize);
135 },
136 onShowSizeChange: (current, pageSize) => {
137 handlePageChange(1, pageSize);
138 },
139 showTotal(total) {
140 return `总共 ${total} 条数据`;
141 },
142 };
143 return (
144 <Table
145 rowKey="id"
146 loading={loading}
147 dataSource={records}
148 columns={columns}
149 pagination={pagination}
150 scroll={{ x: 1500, y: `calc(100vh - 400px)` }}
151 />
152 );
153 };
154
155 /* Main */
156 const RiskBlock = props => {
157 const {
158 dispatch,
159 loading,
160 loadingUpload,
161 dataSearch,
162 dataRisk,
163 dataRisk: { size },
164 } = props;
165
166 useEffect(() => {
167 handleGetList(1, 10);
168 }, []);
169
170 /* 账号列表 */
171 const handleGetList = (current, size) => {
172 dispatch({
173 type: 'RiskBlock/getEventRiskList',
174 payload: {
175 current: current || 1,
176 size: size || 10,
177 },
178 });
179 };
180
181 return (
182 <div className="mt-16">
183 <DataTable loading={loading} handleGetList={handleGetList} dataRisk={dataRisk} />
184 </div>
185 );
186 };
187
188 export default connect(({ RiskBlock, loading }) => ({
189 ...RiskBlock,
190 loading: loading.effects['RiskBlock/getEventRiskList'],
191 }))(RiskBlock);
1 import * as services from '@/services/risk';
2
3 export default {
4 namespace: 'RiskBlock',
5 state: {
6 dataRisk: {
7 records: [],
8 current: 1,
9 size: 10,
10 total: 0,
11 totalPage: 0,
12 },
13 },
14 effects: {
15 /* 获取风险列表 */
16 *getEventRiskList({ payload }, { call, put, select }) {
17 try {
18 const res = yield call(services.getEventRiskList, {
19 current: 1,
20 size: 10,
21 ...payload,
22 });
23 if (res.code === 0) {
24 res.data.records = res.data.records || [];
25 yield put({
26 type: 'changeState',
27 payload: {
28 dataRisk: res.data,
29 },
30 });
31 }
32 } catch (err) {
33 console.error(err);
34 }
35 },
36 },
37 reducers: {
38 changeState(state, { payload }) {
39 return {
40 ...state,
41 ...payload,
42 };
43 },
44 },
45 };
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta
7 name="viewport"
8 content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
9 />
10 <title>一网打“净”数智在线</title>
11 <link rel="icon" href="/favicon.ico" />
12 <!-- 一些CDN加载 -->
13 <script src="https://cdn.jsdelivr.net/combine/npm/react@16.8.6/umd/react.<%= context.env === 'production' ? 'production.min' : 'development'%>.js,npm/react-dom@16.8.6/umd/react-dom.<%= context.env === 'production' ? 'production.min' : 'development'%>.js"></script>
14 <script src="https://cdn.jsdelivr.net/npm/bizcharts@3.5.7/umd/BizCharts.min.js"></script>
15 <script src="https://cdn.jsdelivr.net/npm/@antv/data-set@0.10.2/build/data-set.min.js"></script>
16 <script src="https://cdn.jsdelivr.net/npm/mockjs@1.0.1-beta3/dist/mock.min.js"></script>
17 <script>
18 (function(w, d, s, q, i) {
19 w[q] = w[q] || [];
20 var f = d.getElementsByTagName(s)[0],
21 j = d.createElement(s);
22 j.async = true;
23 j.id = 'beacon-aplus';
24 j.src = 'https://alidt.alicdn.com/alilog/mlog/aplus_cloud.js';
25 f.parentNode.insertBefore(j, f);
26 })(window, document, 'script', 'aplus_queue');
27
28 aplus_queue.push({
29 action: 'aplus.setMetaInfo',
30 arguments: ['aplus-rhost-v', 'alog.zjzwfw.gov.cn'],
31 });
32 aplus_queue.push({
33 action: 'aplus.setMetaInfo',
34 arguments: ['aplus-rhost-g', 'alog.zjzwfw.gov.cn'],
35 });
36
37 var u = navigator.userAgent;
38 var isAndroid = u.indexOf('Android') > -1;
39 var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
40
41 aplus_queue.push({
42 action: 'aplus.setMetaInfo',
43 arguments: ['appId', isAndroid ? '28302650' : isIOS ? '28328447' : '47130293'],
44 });
45 </script>
46 </head>
47 <body>
48 <noscript>Out-of-the-box mid-stage front/design solution!</noscript>
49 <div id="root">
50 <style>
51 .page-loading-warp {
52 padding: 120px;
53 display: flex;
54 justify-content: center;
55 align-items: center;
56 }
57 .ant-spin {
58 -webkit-box-sizing: border-box;
59 box-sizing: border-box;
60 margin: 0;
61 padding: 0;
62 color: rgba(0, 0, 0, 0.65);
63 font-size: 14px;
64 font-variant: tabular-nums;
65 line-height: 1.5;
66 list-style: none;
67 -webkit-font-feature-settings: 'tnum';
68 font-feature-settings: 'tnum';
69 position: absolute;
70 display: none;
71 color: #1890ff;
72 text-align: center;
73 vertical-align: middle;
74 opacity: 0;
75 -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
76 transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
77 transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
78 transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
79 -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
80 }
81
82 .ant-spin-spinning {
83 position: static;
84 display: inline-block;
85 opacity: 1;
86 }
87
88 .ant-spin-dot {
89 position: relative;
90 display: inline-block;
91 font-size: 20px;
92 width: 20px;
93 height: 20px;
94 }
95
96 .ant-spin-dot-item {
97 position: absolute;
98 display: block;
99 width: 9px;
100 height: 9px;
101 background-color: #1890ff;
102 border-radius: 100%;
103 -webkit-transform: scale(0.75);
104 -ms-transform: scale(0.75);
105 transform: scale(0.75);
106 -webkit-transform-origin: 50% 50%;
107 -ms-transform-origin: 50% 50%;
108 transform-origin: 50% 50%;
109 opacity: 0.3;
110 -webkit-animation: antSpinMove 1s infinite linear alternate;
111 animation: antSpinMove 1s infinite linear alternate;
112 }
113
114 .ant-spin-dot-item:nth-child(1) {
115 top: 0;
116 left: 0;
117 }
118
119 .ant-spin-dot-item:nth-child(2) {
120 top: 0;
121 right: 0;
122 -webkit-animation-delay: 0.4s;
123 animation-delay: 0.4s;
124 }
125
126 .ant-spin-dot-item:nth-child(3) {
127 right: 0;
128 bottom: 0;
129 -webkit-animation-delay: 0.8s;
130 animation-delay: 0.8s;
131 }
132
133 .ant-spin-dot-item:nth-child(4) {
134 bottom: 0;
135 left: 0;
136 -webkit-animation-delay: 1.2s;
137 animation-delay: 1.2s;
138 }
139
140 .ant-spin-dot-spin {
141 -webkit-transform: rotate(45deg);
142 -ms-transform: rotate(45deg);
143 transform: rotate(45deg);
144 -webkit-animation: antRotate 1.2s infinite linear;
145 animation: antRotate 1.2s infinite linear;
146 }
147
148 .ant-spin-lg .ant-spin-dot {
149 font-size: 32px;
150 width: 32px;
151 height: 32px;
152 }
153
154 .ant-spin-lg .ant-spin-dot i {
155 width: 14px;
156 height: 14px;
157 }
158
159 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
160 .ant-spin-blur {
161 background: #fff;
162 opacity: 0.5;
163 }
164 }
165
166 @-webkit-keyframes antSpinMove {
167 to {
168 opacity: 1;
169 }
170 }
171
172 @keyframes antSpinMove {
173 to {
174 opacity: 1;
175 }
176 }
177
178 @-webkit-keyframes antRotate {
179 to {
180 -webkit-transform: rotate(405deg);
181 transform: rotate(405deg);
182 }
183 }
184
185 @keyframes antRotate {
186 to {
187 -webkit-transform: rotate(405deg);
188 transform: rotate(405deg);
189 }
190 }
191 </style>
192 <div class="page-loading-warp">
193 <div class="ant-spin ant-spin-lg ant-spin-spinning">
194 <span class="ant-spin-dot ant-spin-dot-spin"
195 ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
196 ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
197 ></span>
198 </div>
199 </div>
200 </div>
201 </body>
202 </html>
1 /* eslint-disable eslint-comments/disable-enable-pair */
2 /* eslint-disable no-restricted-globals */
3 /* eslint-disable no-underscore-dangle */
4 /* globals workbox */
5 workbox.core.setCacheNameDetails({
6 prefix: 'antd-pro',
7 suffix: 'v1',
8 });
9 // Control all opened tabs ASAP
10 workbox.clientsClaim();
11
12 /**
13 * Use precaching list generated by workbox in build process.
14 * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
15 */
16 workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
17
18 /**
19 * Register a navigation route.
20 * https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
21 */
22 workbox.routing.registerNavigationRoute('/index.html');
23
24 /**
25 * Use runtime cache:
26 * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
27 *
28 * Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
29 * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
30 */
31
32 /**
33 * Handle API requests
34 */
35 workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
36
37 /**
38 * Handle third party requests
39 */
40 workbox.routing.registerRoute(
41 /^https:\/\/gw.alipayobjects.com\//,
42 workbox.strategies.networkFirst(),
43 );
44 workbox.routing.registerRoute(
45 /^https:\/\/cdnjs.cloudflare.com\//,
46 workbox.strategies.networkFirst(),
47 );
48 workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
49
50 /**
51 * Response to client after skipping waiting with MessageChannel
52 */
53 addEventListener('message', event => {
54 const replyPort = event.ports[0];
55 const message = event.data;
56 if (replyPort && message && message.type === 'skip-waiting') {
57 event.waitUntil(
58 self.skipWaiting().then(
59 () =>
60 replyPort.postMessage({
61 error: null,
62 }),
63 error =>
64 replyPort.postMessage({
65 error,
66 }),
67 ),
68 );
69 }
70 });
1 import request from '@/utils/request';
2
3 // 账号列表
4 export async function getAccountList(params) {
5 return request('/v1/user/page', { params, methods: 'GET' });
6 }
7
8 // 新增账号
9 export async function addAccount(data) {
10 return request('/v1/user', { data });
11 }
12
13 // 修改账号
14 export async function updateAccount(data) {
15 const { id } = data;
16 return request(`/v1/user/${id}`, { data: { ...data, id: undefined } });
17 }
18
19 // 重置密码
20 export async function resetPassword(data) {
21 const { id } = data;
22 return request(`/v1/user/${id}/password/reset`, { data: { ...data, id: undefined } });
23 }
24
25 // 角色下拉
26 export async function getRoleSelect(params) {
27 return request('/role/all', { params, methods: 'GET' });
28 }
29
30 // 状态修改
31 export async function changeStatus(data) {
32 const { id } = data;
33 return request(`/v1/user/${id}/status`, { data });
34 }
1 import request from '@/utils/request';
2
3 // 应用列表
4 export async function getApplicationList(data) {
5 return request('/web/tenant/list', { data });
6 }
7
8 // 删除应用
9 export async function delApplication(data) {
10 return request('web/tenant/delete', { data });
11 }
12
13 // 新增应用
14 export async function addApplication(data) {
15 return request('web/tenant/add', { data });
16 }
17
18 // 修改应用
19 export async function updateApplication(data) {
20 return request('web/tenant/update', { data });
21 }
22
23 // 应用详情
24 export async function getApplicationInfo(data) {
25 return request(`web/tenant/get?id=${data.id}`, { methods: 'get' });
26 }
1 import request from '@/utils/request';
2
3 // 场所网站备案信息
4 export async function getPlaceList(params) {
5 return request('/place/list', { params, methods: 'GET' });
6 }
7
8 // 行政执法案件
9 export async function getEventIllegalList(params) {
10 return request('/eventIllegal/list', { params, methods: 'GET' });
11 }
12
13 // 投诉举报事件
14 export async function getComplaintList(params) {
15 return request('/complaints/list', { params, methods: 'GET' });
16 }
17
18 // 基层网络事件
19 export async function getNetworkEventList(params) {
20 return request('/baseGrid/list', { params, methods: 'GET' });
21 }
22
23 // 场所导入
24 export async function importPlace(data) {
25 return request('/place/import', { data, requestType: 'form' });
26 }
27
28 // 场所导出模版
29 export async function exportPlaceTemplate(params) {
30 return request('/place/template', { params, methods: 'GET', responseType: 'blob' });
31 }
32
33 // 行政执法案件导入
34 export async function importEventIllegal(data) {
35 return request('/eventIllegal/import', { data, requestType: 'form' });
36 }
37
38 // 行政执法案件导出模版
39 export async function exportEventIllegalTemplate(params) {
40 return request('/eventIllegal/template', { params, methods: 'GET', responseType: 'blob' });
41 }
42
43 // 基层网格搜索下拉框
44 export async function getBaseGridSponsor(params) {
45 return request('/baseGrid/getSponsor', { params, methods: 'GET' });
46 }
47
48 // 投诉举报事件详情
49 export async function getComplaintsDetail(params) {
50 return request('/complaints/selectById', { params, methods: 'GET' });
51 }
52
53 // 基层网络详情
54 export async function getBaseGridDetail(params) {
55 return request('/baseGrid/getById', { params, methods: 'GET' });
56 }
57
58 // 行政执法案件详情
59 export async function getEventIllegalDetail(params) {
60 return request('/eventIllegal/selectById', { params, methods: 'GET' });
61 }
1 import request from '@/utils/request';
2
3 // 事件列表
4 export async function getEventList(params) {
5 return request('/event/list', { params, methods: 'GET' });
6 }
7
8 // 事件详情
9 export async function getEventDetail(params) {
10 return request('/event/detail', { params, methods: 'GET' });
11 }
12
13 // 事件风险列表
14 export async function getEventRiskList(params) {
15 return request('/risk/getEventRiskList', { params, methods: 'GET' });
16 }
17
18 // 事件日志
19 export async function getEventRiskLog(params) {
20 return request(`/event/${params?.eventId}/log/list`, { params, methods: 'GET' });
21 }
1 import request from '@/utils/request';
2 // 登录
3 export async function queryLogin(data) {
4 return request('/v1/user/auth/login', {
5 data,
6 headers: { 'Content-Type': 'application/json' },
7 });
8 }
9
10 // 退出登录
11 export async function loginOut(data) {
12 return request('/v1/user/auth/logout', { data });
13 }
1 import request from '@/utils/request';
2
3 // 新增菜单
4 export async function addMenu(data) {
5 return request('/menu', { data });
6 }
7
8 // 更新菜单
9 export async function updateMenu(data) {
10 const { id } = data;
11 return request(`/menu/${id}`, { data });
12 }
13
14 // 删除菜单
15 export async function delMenu(data) {
16 const { id } = data;
17 return request(`/menu/${id}/delete`);
18 }
19
20 // 获取所有的菜单
21 export async function getAllMenu(params) {
22 return request('/menu/tree', { params, methods: 'GET' });
23 }
1 import request from '@/utils/request';
2
3 // 风险列表
4 export async function getRiskList(params) {
5 return request('/risk/list', { params, methods: 'GET' });
6 }
7
8 // 风险导入
9 export async function importRisk(data) {
10 return request('/risk/import', { data, requestType: 'form' });
11 }
12
13 // 风险导出模版
14 export async function exportRiskTemplate(params) {
15 return request('/risk/template', { params, methods: 'GET', responseType: 'blob' });
16 }
17
18 // 事件
19 export async function getEventRiskList(params) {
20 const url = window.location.href;
21 const index = url.lastIndexOf('/');
22 const serialNumber = url.substring(index + 1, url.length);
23 if (!serialNumber) return false;
24 return request(`/event/sn/${serialNumber}`, { params, methods: 'GET' });
25 }
1 import request from '@/utils/request';
2
3 // 角色列表
4 export async function getRoleList(params) {
5 return request('/role', { params, methods: 'GET' });
6 }
7
8 // 新增角色
9 export async function addRole(data) {
10 return request('/role', { data });
11 }
12
13 // 修改角色
14 export async function updateRole(data) {
15 const { id } = data;
16 return request(`/role/${id}`, { data });
17 }
18
19 // 删除角色
20 export async function delRole(data) {
21 const { id } = data;
22 return request(`/role/${id}/delete`);
23 }
24
25 // 获取用户权限
26 export async function getUserMenu(params) {
27 return request('/menu/tree', { params, methods: 'GET' });
28 }
29
30 // 获取用户的meunId
31 export async function getRoleMenuId(params) {
32 const { id } = params;
33 return request(`/role/${id}`, { methods: 'GET' });
34 }
1 import request from '@/utils/request';
2 // 用户信息
3 export async function getUserInfo(params) {
4 return request('/v1/user/auth/me', { params, methods: 'GET' });
5 }
6
7 // 修改密码
8 export async function updatePassword(data) {
9 return request('/v1/user/password/reset', { data, methods: 'POST' });
10 }
1 // from vben
2 import { encrypt, decrypt } from 'crypto-js/aes';
3 import { parse } from 'crypto-js/enc-utf8';
4 import pkcs7 from 'crypto-js/pad-pkcs7';
5 import ECB from 'crypto-js/mode-ecb';
6 import UTF8 from 'crypto-js/enc-utf8';
7
8 const key = 'Q7mh4Gb!cl&\\*{[rz7j>DLIP$g7gYOBv';
9
10 export function aesEncrypt(cipherText) {
11 return encrypt(cipherText, parse(key), {
12 mode: ECB,
13 padding: pkcs7,
14 }).toString()
15 }
16
17 export function aesDecrypt(cipherText) {
18 return decrypt(cipherText, parse(key), {
19 mode: ECB,
20 padding: pkcs7,
21 }).toString(UTF8);
22 }
1 import { HomeOutlined } from '@ant-design/icons';
2 const mapsIcons = {
3 HomeOutlined: <HomeOutlined />,
4 };
5 export function getDataMenus(menus) {
6 //过滤非菜单类型START
7 function getMenu(menus) {
8 menus = menus.filter(_ => {
9 if (_.subList.length > 0) {
10 _.children = getMenu(_.subList);
11 _.name = _.name;
12 _.path = _.children.length ? _.children[0].url : _.url;
13 _.locale = `menu${_.url.replace(/\//g, '.')}`;
14 _.icon = mapsIcons[_.icon];
15 }
16 return _.type === 'MENU';
17 });
18 return menus;
19 }
20 menus = getMenu(menus);
21 //END
22 return menus;
23 }
24
25 /**
26 * 获得处理后的菜单
27 */
28 export const getPermissionList = (data = []) => {
29 let dataMenu = []; // 菜单集合
30 let dataExpandedKeys = []; // 需要展开数的集合
31 function filterPermission(data) {
32 data.map(item => {
33 item.key = item.resourceCode;
34 dataExpandedKeys.push(item.resourceCode);
35 dataMenu.push(item);
36 if (item.children && item.children.length) {
37 filterPermission(item.children);
38 }
39 });
40 }
41 filterPermission(data);
42 return { dataMenu, dataExpandedKeys };
43 };
1 /**
2 * request 网络请求工具
3 * 更详细的 api 文档: https://github.com/umijs/umi-request
4 */
5 import { extend } from 'umi-request';
6 import { message, notification } from 'antd';
7 import { urlConfig } from '@/common';
8 import defaultSettings from '../../config/defaultSettings';
9 const { tokenKey } = defaultSettings;
10
11 const codeMessage = {
12 200: '服务器成功返回请求的数据。',
13 201: '新建或修改数据成功。',
14 202: '一个请求已经进入后台排队(异步任务)。',
15 204: '删除数据成功。',
16 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
17 401: '登录已超时, 请重新登录',
18 403: '用户得到授权,但是访问是被禁止的。',
19 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
20 406: '请求的格式不可得。',
21 410: '请求的资源被永久删除,且不会再得到的。',
22 422: '当创建一个对象时,发生一个验证错误。',
23 500: '服务器发生错误,请检查服务器。',
24 502: '网关错误。',
25 503: '服务不可用,服务器暂时过载或维护。',
26 504: '网关超时。',
27 };
28 /**
29 * 异常处理程序
30 */
31
32 const errorHandler = error => {
33 const { response } = error;
34
35 if (response && response.status) {
36 const errorText = codeMessage[response.status] || response.statusText;
37 const { status, url } = response;
38 notification.error({
39 message: `请求错误 ${status}: ${url}`,
40 description: errorText,
41 });
42 }
43 };
44 /**
45 * 配置request请求时的默认参数
46 */
47
48 const request = extend({
49 errorHandler,
50 // 默认错误处理
51 credentials: 'include',
52 prefix: urlConfig.URL_API, //接口公共
53 method: 'post', // 设置默认的请求类型
54 });
55
56 //请求拦截
57 request.interceptors.request.use((url, options) => {
58 return {
59 options: {
60 ...options,
61 headers: {
62 ...options.headers,
63 Authorization: localStorage.getItem(tokenKey) || '',
64 },
65 method: options.methods ? options.methods : 'post',
66 },
67 };
68 });
69 //响应拦截
70 request.interceptors.response.use(async response => {
71 if (
72 response.url.indexOf('/risk/template') > -1 ||
73 response.url.indexOf('/place/template') > -1 ||
74 response.url.indexOf('/eventIllegal/template') > -1
75 ) {
76 return response;
77 } else {
78 const data = await response?.clone()?.json();
79 if ([4001, 40002].includes(data.code)) {
80 window.location.href = '/user/login';
81 } else if (data.code === 0) {
82 return response;
83 } else {
84 message.error(`${data.message}`);
85 }
86 return response;
87 }
88 });
89 export default request;
1 import { parse } from 'querystring';
2 import pathRegexp from 'path-to-regexp';
3 import { isArray } from 'util';
4 import { IMG_URL } from '@/common';
5 import { phoneReg } from '@/constants/reg';
6 import { message } from 'antd';
7 // 获取七牛图片
8 export function getImgUrl(imgname) {
9 return `${IMG_URL}${imgname}`;
10 }
11
12 // 获取七牛图片的文件名
13 export function getFileName(fileurl) {
14 let index1 = fileurl.lastIndexOf('/');
15 let index2 = fileurl.length;
16 fileurl = fileurl.substring(index1 + 1, index2);
17 return fileurl;
18 }
19 // 获取文件类型
20 export function getFileType(file) {
21 let filename = file;
22 let index1 = filename.lastIndexOf('.');
23 let index2 = filename.length;
24 let type = filename.substring(index1, index2);
25 return type;
26 }
27
28 /**
29 * 将对象转换为以key对应的值为内容的数组
30 * @param {Object} enums (将要转换的对象)
31 */
32 export const objToArray = (enums = {}) => {
33 const arr = [];
34 Object.keys(enums).forEach(key => {
35 arr.push(enums[key]);
36 });
37 return arr;
38 };
39 /**
40 * 将数组转换为以指定字段为key的对象
41 * @param {Array} arrs (将要转换的数组)
42 * @param {String} key (以哪个字段作为对象的key)
43 */
44 export const arrayToObj = (arrs = [], key = 'id') => {
45 const params = {};
46 for (let i = 0, len = arrs.length; i < len; i++) {
47 const item = arrs[i];
48 params[item[key]] = item;
49 }
50 return params;
51 };
52
53 // 将数字转换成金额显示
54 export const toMoney = num => {
55 const type = Object.prototype.toString.call(num);
56
57 switch (type) {
58 case '[object Number]': {
59 num = num.toFixed(2).replace('.00', '');
60 break;
61 }
62 case '[object String]': {
63 num -= 0;
64 num = num.toFixed(2).replace('.00', '');
65 break;
66 }
67 default: {
68 num = '--';
69 }
70 }
71 return num;
72 };
73
74 /**
75 *
76 * @param {*生成的独立id的长度} n
77 * @param {*和将要生成的随机数作比较去重的id集合} arr
78 * @param {*可用于生成随机数id的字符集合} str
79 */
80 export const getUniqueId = (n, arr = [], comb = '123456789') => {
81 const random = n => {
82 let str = comb;
83 let result = '';
84 for (let i = 0; i < n; i++) {
85 result += str[parseInt(Math.random() * str.length)];
86 }
87
88 if (arr.includes(result)) {
89 random(n);
90 } else {
91 return result;
92 }
93 };
94
95 return random(n);
96 };
97
98 export const isPhone = phone => phoneReg.test(phone);
99
100 /**
101 * 判断数组里面是不是有重复项
102 */
103 export const hasDuplicates = arr => {
104 return _.uniq(arr).length !== arr.length;
105 };
106
107 /**
108 * 获取权限配置数据
109 */
110 export const getAuthData = (data = [], authObj) => {
111 let dataMenu = []; // 菜单集合(不包含按钮菜单)
112 let dataExpandedKeys = []; // 需要展开数的集合
113 let { parentCode } = authObj;
114 if (!parentCode) {
115 data.push({ ...authObj });
116 }
117 function authFilter(data) {
118 data = data.map(item => {
119 if (item.code === parentCode) {
120 item.subPermissionList.push({ ...authObj });
121 }
122 if (item.type !== 3) {
123 dataMenu.push({
124 name: item.name,
125 code: item.code,
126 url: item.url,
127 });
128 }
129 dataExpandedKeys.push(item.code);
130
131 const nextSubPermissionList = authFilter(item.subPermissionList);
132 return {
133 ...item,
134 subPermissionList: nextSubPermissionList.length > 0 ? nextSubPermissionList : [],
135 };
136 });
137 return data;
138 }
139 data = authFilter(data);
140 return {
141 data,
142 dataMenu,
143 dataExpandedKeys,
144 };
145 };
146
147 /**
148 * 编辑、删除权限
149 */
150 export const getDelAuthData = (data = [], authObj, operateType) => {
151 let { code, parentCode, oldCode } = authObj;
152 let index = 0; // 次数
153 function authFilter(data) {
154 data = data
155 .filter(_ => {
156 if (_.subPermissionList.length > 0) {
157 authFilter(_.subPermissionList);
158 }
159 return operateType === 'EDIT' ? _.code : _.code !== code;
160 })
161 .map(item => {
162 /* 编辑时菜单没有改变 */
163 if (!oldCode && code === item.code && operateType === 'EDIT') {
164 item = { ...authObj };
165 }
166 /* 编辑时改变了菜单 */
167 if (oldCode && item.code === oldCode) {
168 let subData =
169 item.subPermissionList.length > 1
170 ? item.subPermissionList.filter(subItem => {
171 return subItem.code !== code;
172 })
173 : [];
174 item.subPermissionList = subData;
175 }
176 if (index < 1 && oldCode && item.code === parentCode && operateType === 'EDIT') {
177 index = index + 1;
178 item.subPermissionList.push({ ...authObj, oldCode: undefined });
179 }
180
181 const nextSubPermissionList = authFilter(item.subPermissionList);
182 return {
183 ...item,
184 subPermissionList: nextSubPermissionList.length > 0 ? nextSubPermissionList : [],
185 };
186 });
187 return data;
188 }
189 data = authFilter(data);
190 const { dataMenu, dataExpandedKeys } = getEditCode(data);
191 return {
192 data,
193 dataMenu,
194 dataExpandedKeys,
195 };
196 };
197
198 /* 获取编辑之后的菜单和Code */
199 export const getEditCode = (data = []) => {
200 let dataMenu = []; // 菜单集合(不包含按钮菜单)
201 let dataExpandedKeys = []; // 需要展开数的集合
202 function allFilter(data) {
203 data.map(item => {
204 if (item.type !== 3) {
205 dataMenu.push({
206 name: item.name,
207 code: item.code,
208 url: item.url,
209 });
210 }
211 dataExpandedKeys.push(item.code);
212 allFilter(item.subPermissionList);
213 });
214 }
215 allFilter(data);
216 return { dataMenu, dataExpandedKeys };
217 };
218
219 /**
220 * 获取所有的code和除按钮菜单的菜单名称
221 */
222 export const getAllMenuParams = (data = []) => {
223 let allCode = [];
224 let allMenuName = [];
225 function allFilter(data) {
226 data.map(item => {
227 if (item.type !== 3) {
228 allMenuName.push(item.name);
229 }
230 allCode.push(item.code);
231 allFilter(item.subPermissionList);
232 });
233 }
234 allFilter(data);
235 return { allCode, allMenuName };
236 };
237
238 /**
239 * 获得权限列表
240 */
241 export const getPermissionList = (data = [], entry) => {
242 let dataMenu = []; // 菜单集合(不包含按钮菜单)
243 let dataExpandedKeys = []; // 需要展开数的集合
244 function filterPermission(data) {
245 data.map(item => {
246 if (entry) {
247 item.type =
248 item.type === 3 || item.type === 'BTN'
249 ? 3
250 : (item.type === 'MENU' || item.type === 1) && item.parentId === 0
251 ? 1
252 : 2;
253 item.key = item.code;
254 dataExpandedKeys.push(item.code);
255 if (item.type !== 3 && item.type !== 'BTN') {
256 dataMenu.push({
257 name: item.name,
258 code: item.code,
259 url: item.url,
260 });
261 }
262 /* 加上parentCode */
263 if (item.subPermissionList.length) {
264 item.subPermissionList.map(_ => {
265 _.parentCode = item.code;
266 });
267 }
268 } else {
269 item.type = item.type === 3 || item.type === 'BTN' ? 'BTN' : 'MENU';
270 if (item.parentCode) {
271 delete item.parentCode;
272 }
273 delete item.key;
274 }
275 filterPermission(item.subPermissionList);
276 });
277 }
278 filterPermission(data);
279 return !entry ? data : { data, dataMenu, dataExpandedKeys };
280 };
281
282 /* 获取拖拽之后的数据 */
283 export const getDragData = (data = []) => {
284 let dataMenu = []; // 菜单集合(不包含按钮菜单)
285 let dataExpandedKeys = []; // 需要展开数的集合
286 let isFlag = false;
287 let dragData = data.map(item => {
288 if (item.type === 3) {
289 message.warning('按钮不能做为一级菜单使用');
290 isFlag = true;
291 }
292 return {
293 ...item,
294 type: 1,
295 parentId: 0,
296 parentCode: undefined,
297 };
298 });
299 function allFilter(data) {
300 data = data.map(item => {
301 item.subPermissionList.map(_ => {
302 if (_.parentId === 0) {
303 message.warning('一级菜单不能做为子菜单');
304 isFlag = true;
305 }
306 });
307 if (item.type !== 1) {
308 item.subPermissionList.map(_ => {
309 _.parentCode = item.code;
310 });
311 }
312 if (item.type !== 3) {
313 dataMenu.push({
314 name: item.name,
315 code: item.code,
316 url: item.url,
317 });
318 }
319 if (item.type === 3 && item.subPermissionList.length) {
320 message.warning('按钮下面不能有子类');
321 isFlag = true;
322 }
323 dataExpandedKeys.push(item.code);
324
325 const nextSubPermissionList = allFilter(item.subPermissionList);
326 return {
327 ...item,
328 subPermissionList: nextSubPermissionList.length > 0 ? nextSubPermissionList : [],
329 };
330 });
331 return data;
332 }
333 data = allFilter(dragData);
334 let dataDragAuth = !isFlag ? data : [];
335 return { dataDragAuth, dataMenu, dataExpandedKeys };
336 };
337
338 // 导出文件(流的形式)
339 export const ExportFile = (name, fn, params = {}) => {
340 fn(params)
341 .then(resp => {
342 const blob = new Blob([resp]);
343 let downloadElement = document.createElement('a');
344 const href = window.URL.createObjectURL(blob); //创建下载的链接
345 downloadElement.href = href;
346 downloadElement.download = `${name}.xlsx`;
347 document.body.appendChild(downloadElement);
348 downloadElement.click();
349 document.body.removeChild(downloadElement);
350 window.URL.revokeObjectURL(href);
351 })
352 .catch(e => {
353 message.error(`导出错误: e`);
354 console.error(e);
355 });
356 };
1 /* eslint-disable eslint-comments/disable-enable-pair */
2 /* eslint-disable @typescript-eslint/no-var-requires */
3 /* eslint-disable eslint-comments/no-unlimited-disable */
4 const { spawn } = require('child_process');
5 // eslint-disable-next-line import/no-extraneous-dependencies
6 const { kill } = require('cross-port-killer');
7
8 const env = Object.create(process.env);
9 env.BROWSER = 'none';
10 env.TEST = true;
11 env.UMI_UI = 'none';
12 env.PROGRESS = 'none';
13 // flag to prevent multiple test
14 let once = false;
15
16 const startServer = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['start'], {
17 env,
18 });
19
20 startServer.stderr.on('data', data => {
21 // eslint-disable-next-line
22 console.log(data.toString());
23 });
24
25 startServer.on('exit', () => {
26 kill(process.env.PORT || 8000);
27 });
28
29 console.log('Starting development server for e2e tests...');
30 startServer.stdout.on('data', data => {
31 console.log(data.toString());
32 // hack code , wait umi
33 if (
34 (!once && data.toString().indexOf('Compiled successfully') >= 0) ||
35 data.toString().indexOf('Theme generated successfully') >= 0
36 ) {
37 // eslint-disable-next-line
38 once = true;
39 console.log('Development server is started, ready to run tests.');
40 const testCmd = spawn(
41 /^win/.test(process.platform) ? 'npm.cmd' : 'npm',
42 ['test', '--', '--maxWorkers=1', '--runInBand'],
43 {
44 stdio: 'inherit',
45 },
46 );
47 testCmd.on('exit', code => {
48 startServer.kill();
49 process.exit(code);
50 });
51 }
52 });
1 import 'jsdom-global/register';
2
3 // browserMocks.js
4 const localStorageMock = (() => {
5 let store = {};
6
7 return {
8 getItem(key) {
9 return store[key] || null;
10 },
11 setItem(key, value) {
12 store[key] = value.toString();
13 },
14 clear() {
15 store = {};
16 },
17 };
18 })();
19
20 Object.defineProperty(window, 'localStorage', {
21 value: localStorageMock,
22 });
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!