feat: 项目初始化
0 parents
Showing
150 changed files
with
11803 additions
and
0 deletions
.editorconfig
0 → 100644
.eslintignore
0 → 100644
.eslintrc.js
0 → 100644
.gitignore
0 → 100644
| 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 |
.prettierignore
0 → 100644
| 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 |
.prettierrc.js
0 → 100644
.stylelintrc.js
0 → 100644
README.md
0 → 100644
| 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). |
config/config.js
0 → 100644
| 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 | }); |
config/defaultSettings.js
0 → 100644
| 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 | }; |
config/plugin.config.js
0 → 100644
| 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 | }; |
config/proxy.js
0 → 100644
| 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 | }; |
config/routerConfig/app.js
0 → 100644
| 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 | }; |
config/routerConfig/index.js
0 → 100644
| 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; |
config/routerConfig/user.js
0 → 100644
config/themePluginConfig.js
0 → 100644
| 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 | }; |
jest-puppeteer.config.js
0 → 100644
jest.config.js
0 → 100644
jsconfig.json
0 → 100644
package.json
0 → 100644
| 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 | } |
public/favicon.ico
0 → 100644
No preview for this file type
src/assets/yh-logo.png
0 → 100644
6.95 KB
src/common/index.js
0 → 100644
| 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 | }); |
src/components/Auth/AuthBlock.js
0 → 100644
| 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); |
src/components/Auth/AuthRouter.js
0 → 100644
| 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 |
src/components/CopyToClipboard.js
0 → 100644
| 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 |
src/components/FileExport.js
0 → 100644
| 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 | /** | ||
| 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); |
src/components/GlobalHeader/RightContent.jsx
0 → 100644
| 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); |
src/components/GlobalHeader/index.less
0 → 100644
| 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 | } |
src/components/HeaderDropdown/index.jsx
0 → 100644
| 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; |
src/components/HeaderDropdown/index.less
0 → 100644
| 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 | } |
src/components/Loading/index.js
0 → 100644
| 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; |
src/components/Loading/index.less
0 → 100644
| 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 |
src/components/ModalUpdatePassword/index.jsx
0 → 100644
| 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); |
src/components/PageCommons.js
0 → 100644
| 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 |
src/components/PageLoading/index.jsx
0 → 100644
src/components/QCard/index.js
0 → 100644
| 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; |
src/components/QCard/index.less
0 → 100644
src/components/style/index.tsx
0 → 100644
| 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 | `; |
src/constants/index.js
0 → 100644
| 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 | }; |
src/constants/reg.js
0 → 100644
src/e2e/__mocks__/antd-pro-merge-less.js
0 → 100644
| 1 | export default undefined; |
src/e2e/baseLayout.e2e.js
0 → 100644
| 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 | }); |
src/e2e/topMenu.e2e.js
0 → 100644
| 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 | }); |
src/global.jsx
0 → 100644
| 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 | } |
src/global.less
0 → 100644
| 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 | } |
src/layouts/BasicLayout.jsx
0 → 100644
| 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); |
src/layouts/BlankLayout.jsx
0 → 100644
src/layouts/UserLayout.jsx
0 → 100644
| 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); |
src/layouts/UserLayout.less
0 → 100644
| 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 | } |
src/locales/en-US.js
0 → 100644
| 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 | }; |
src/locales/en-US/component.js
0 → 100644
src/locales/en-US/globalHeader.js
0 → 100644
| 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 | }; |
src/locales/en-US/menu.js
0 → 100644
| 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 | }; |
src/locales/en-US/pwa.js
0 → 100644
src/locales/en-US/settingDrawer.js
0 → 100644
| 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 | }; |
src/locales/en-US/settings.js
0 → 100644
| 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 | }; |
src/locales/pt-BR.js
0 → 100644
| 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 | }; |
src/locales/pt-BR/component.js
0 → 100644
src/locales/pt-BR/globalHeader.js
0 → 100644
| 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 | }; |
src/locales/pt-BR/menu.js
0 → 100644
| 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 | }; |
src/locales/pt-BR/pwa.js
0 → 100644
| 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 | }; |
src/locales/pt-BR/settingDrawer.js
0 → 100644
| 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 | }; |
src/locales/pt-BR/settings.js
0 → 100644
| 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 | }; |
src/locales/zh-CN.js
0 → 100644
| 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 | }; |
src/locales/zh-CN/component.js
0 → 100644
src/locales/zh-CN/globalHeader.js
0 → 100644
| 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 | }; |
src/locales/zh-CN/menu.js
0 → 100644
| 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 | }; |
src/locales/zh-CN/pwa.js
0 → 100644
src/locales/zh-CN/settingDrawer.js
0 → 100644
| 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 | }; |
src/locales/zh-CN/settings.js
0 → 100644
| 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 | }; |
src/locales/zh-TW.js
0 → 100644
| 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 | }; |
src/locales/zh-TW/component.js
0 → 100644
src/locales/zh-TW/globalHeader.js
0 → 100644
| 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 | }; |
src/locales/zh-TW/menu.js
0 → 100644
| 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 | }; |
src/locales/zh-TW/pwa.js
0 → 100644
src/locales/zh-TW/settingDrawer.js
0 → 100644
| 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 | }; |
src/locales/zh-TW/settings.js
0 → 100644
| 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 | }; |
src/manifest.json
0 → 100644
| 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 | } |
src/models/global.js
0 → 100644
| 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; |
src/models/login.js
0 → 100644
| 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; |
src/models/setting.js
0 → 100644
| 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; |
src/models/user.js
0 → 100644
| 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; |
src/pages/404.jsx
0 → 100644
| 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; |
src/pages/Application/Info.js
0 → 100644
| 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 |
src/pages/Application/ModalSetAuth.js
0 → 100644
| 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 |
src/pages/Application/index.js
0 → 100644
| 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 |
src/pages/Application/models/application.js
0 → 100644
| 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 | }; |
src/pages/Data/Modal/ModalEnforcement.jsx
0 → 100644
| 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); |
src/pages/Data/Modal/ModalNetworkEvent.jsx
0 → 100644
| 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); |
src/pages/Data/complaint.jsx
0 → 100644
| 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); |
src/pages/Data/enforcement.jsx
0 → 100644
| 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); |
src/pages/Data/index.jsx
0 → 100644
| 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; |
src/pages/Data/models/complaint.js
0 → 100644
| 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 | }; |
src/pages/Data/models/enforcement.js
0 → 100644
| 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 | }; |
src/pages/Data/models/networkEvent.js
0 → 100644
| 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 | }; |
src/pages/Data/models/place.js
0 → 100644
| 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 | }; |
src/pages/Data/networkEvent.jsx
0 → 100644
| 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); |
src/pages/Data/place.jsx
0 → 100644
| 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); |
src/pages/Incident/ModalEventInfo.jsx
0 → 100644
| 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); |
src/pages/Incident/ModalEventLog.jsx
0 → 100644
| 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); |
src/pages/Incident/index.jsx
0 → 100644
| 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); |
src/pages/Incident/models/incident.js
0 → 100644
| 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 | }; |
src/pages/Modals/ModalPreImg.js
0 → 100644
| 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); |
src/pages/Risk/index.jsx
0 → 100644
| 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); |
src/pages/Risk/models/risk.js
0 → 100644
| 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 | }; |
src/pages/Screen/index.jsx
0 → 100644
| 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); |
src/pages/System/Account/index.jsx
0 → 100644
| 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); |
src/pages/System/Account/models/account.js
0 → 100644
| 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 | }; |
src/pages/System/Menu/MenuTree.jsx
0 → 100644
| 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); |
src/pages/System/Menu/ModalSetMenu.jsx
0 → 100644
| 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); |
src/pages/System/Menu/index.jsx
0 → 100644
| 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); |
src/pages/System/Menu/models/index.js
0 → 100644
| 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 | }; |
src/pages/System/Role/ModalUpdateRole.jsx
0 → 100644
| 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); |
src/pages/System/Role/index.jsx
0 → 100644
| 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); |
src/pages/System/Role/models/role.js
0 → 100644
| 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 | }; |
src/pages/User/Login/index.js
0 → 100644
| 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); |
src/pages/User/Login/index.less
0 → 100644
| 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 | } |
src/pages/User/Risk/index.jsx
0 → 100644
| 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); |
src/pages/User/Risk/models/risk.js
0 → 100644
| 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 | }; |
src/pages/document.ejs
0 → 100644
| 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> |
src/service-worker.js
0 → 100644
| 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 | }); |
src/services/account.js
0 → 100644
| 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 | } |
src/services/application.js
0 → 100644
| 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 | } |
src/services/data.js
0 → 100644
| 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 | } |
src/services/incident.js
0 → 100644
| 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 | } |
src/services/login.js
0 → 100644
| 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 | } |
src/services/menu.js
0 → 100644
| 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 | } |
src/services/risk.js
0 → 100644
| 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 | } |
src/services/role.js
0 → 100644
| 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 | } |
src/services/user.js
0 → 100644
| 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 | } |
src/utils/encrypt.js
0 → 100644
| 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 | } |
src/utils/menu.js
0 → 100644
| 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 | }; |
src/utils/request.js
0 → 100644
| 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; |
src/utils/utils.js
0 → 100644
| 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 | }; |
tests/run-tests.js
0 → 100644
| 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 | }); |
tests/setupTests.js
0 → 100644
| 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 | }); |
-
Please register or sign in to post a comment