├── .babelrc ├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .lintstagedrc ├── .travis.yml ├── LICENSE ├── _config.yml ├── app ├── apis │ ├── __mocks__ │ │ └── manage.js │ ├── common.js │ └── manage.js ├── client.js ├── components │ ├── draw │ │ ├── README.md │ │ ├── __mocks__ │ │ │ └── draw.js │ │ ├── draw.js │ │ ├── draw.less │ │ └── draw2.0.js │ ├── logo │ │ ├── logo.css │ │ └── logo.js │ └── tableList │ │ └── tableList.js ├── configs │ ├── __mocks__ │ │ └── ajax.js │ ├── ajax.js │ ├── common.js │ ├── config.js │ ├── regular.config.js │ ├── router.config.js │ └── socket.js ├── images │ ├── default.png │ ├── icon │ │ └── menu │ │ │ ├── default.png │ │ │ ├── icon_duty.png │ │ │ ├── icon_pgmb.png │ │ │ ├── icon_pgmx.png │ │ │ ├── icon_statistics.png │ │ │ ├── icon_xtxg.png │ │ │ ├── moduleManage.png │ │ │ ├── roleManage.png │ │ │ └── userManage.png │ ├── login.png │ ├── logo.png │ ├── navcontrol.png │ ├── photo_default.jpg │ ├── user.jpg │ └── user.png ├── index.html ├── middleware │ ├── configureStore.js │ ├── history.js │ ├── index.js │ ├── logger.js │ └── router.js ├── mocks │ ├── apis │ │ ├── base │ │ │ ├── index.js │ │ │ ├── login.js │ │ │ ├── logout.js │ │ │ ├── menu.js │ │ │ └── staff.js │ │ ├── fail.js │ │ ├── success.js │ │ ├── sys │ │ │ ├── moduleManage │ │ │ │ ├── fetchModuleDetail.js │ │ │ │ ├── fetchModuleList.js │ │ │ │ └── index.js │ │ │ ├── roleManage │ │ │ │ ├── fetchButtonList.js │ │ │ │ ├── fetchModuleListInRole.js │ │ │ │ ├── fetchRoleDetail.js │ │ │ │ ├── fetchTreeList.js │ │ │ │ └── index.js │ │ │ └── userManage │ │ │ │ ├── fetchRoleList.js │ │ │ │ ├── fetchUserDepttList.js │ │ │ │ ├── fetchUserDetail.js │ │ │ │ ├── fetchUserList.js │ │ │ │ └── index.js │ │ └── tableList.js │ ├── http.js │ ├── interfaceFilter.js │ └── interfaceMap.js ├── pages │ ├── base │ │ ├── app.js │ │ ├── app │ │ │ ├── header.js │ │ │ ├── leftNav.js │ │ │ ├── modal │ │ │ │ ├── editPassword.js │ │ │ │ └── userInfo.js │ │ │ └── tabList.js │ │ ├── developing.js │ │ ├── example.js │ │ ├── index.js │ │ ├── login.js │ │ ├── notfound.js │ │ ├── socket.js │ │ └── socketReceive.js │ ├── example.js │ ├── menu │ │ ├── echarts.js │ │ ├── editor.js │ │ └── index.js │ └── set │ │ ├── index.js │ │ ├── moduleManage │ │ ├── index.js │ │ ├── modal │ │ │ ├── addButtonModal.js │ │ │ ├── buttonModal.js │ │ │ └── moduleAdd.js │ │ └── moduleList.js │ │ ├── roleManage │ │ ├── index.js │ │ ├── modal │ │ │ ├── buttonModal.js │ │ │ └── roleAdd.js │ │ ├── peopleTreeList.js │ │ ├── roleCheckbox.js │ │ ├── roleList.js │ │ └── roleModuleList.js │ │ └── userManage │ │ ├── index.js │ │ ├── modal │ │ ├── addPolice.js │ │ └── selectRole.js │ │ └── treeList.js ├── redux │ ├── actions │ │ ├── common.js │ │ └── tabList.js │ └── reducers │ │ ├── common.js │ │ ├── index.js │ │ └── tabList.js ├── resource │ └── iconfont │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff └── styles │ ├── RichEditor.less │ ├── base.less │ ├── login.less │ ├── personalCenter.less │ ├── set.less │ └── theme.less ├── cypress.config.js ├── cypress ├── e2e │ ├── 1-getting-started │ │ └── todo.cy.js │ └── 2-advanced-examples │ │ ├── actions.cy.js │ │ ├── aliasing.cy.js │ │ ├── assertions.cy.js │ │ ├── connectors.cy.js │ │ ├── cookies.cy.js │ │ ├── cypress_api.cy.js │ │ ├── files.cy.js │ │ ├── location.cy.js │ │ ├── misc.cy.js │ │ ├── navigation.cy.js │ │ ├── network_requests.cy.js │ │ ├── querying.cy.js │ │ ├── spies_stubs_clocks.cy.js │ │ ├── storage.cy.js │ │ ├── traversal.cy.js │ │ ├── utilities.cy.js │ │ ├── viewport.cy.js │ │ ├── waiting.cy.js │ │ └── window.cy.js ├── fixtures │ └── example.json └── support │ ├── commands.js │ └── e2e.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── readme.md ├── scripts ├── webpack.base.config.js ├── webpack.dev.config.js ├── webpack.dll.config.js ├── webpack.prod.config.js └── webpack.testing.config.js └── test ├── Enzyme.js └── setCenter └── sys ├── moduleManage ├── index.spec.js ├── module │ ├── addButtonModal.spec.js │ ├── addmodal.spec.js │ └── buttonModal.spec.js └── moduleList.spec.js ├── roleManage ├── index.spec.js ├── modal │ ├── buttonModal.spec.js │ └── roleAdd.spec.js ├── roleCheckbox.spec.js └── roleModuleList.spec.js └── userManage ├── index.spec.js └── modal └── addPolice.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { 4 | "useBuiltIns": "usage", 5 | "targets": { 6 | "chrome": "49", 7 | "ie": "11" 8 | }, 9 | "corejs": 2 10 | }], 11 | "@babel/preset-react" 12 | ], 13 | "plugins": [ 14 | ["@babel/plugin-transform-runtime",{ 15 | "absoluteRuntime": false, 16 | "corejs": 2, 17 | "helpers": true, 18 | "regenerator": true, 19 | "useESModules": false 20 | }], 21 | [ 22 | "@babel/plugin-proposal-decorators", 23 | { 24 | "legacy": true 25 | } 26 | ], 27 | "@babel/plugin-proposal-class-properties", 28 | // "transform-decorators-legacy", 29 | // "@babel/plugin-syntax-dynamic-import", 30 | ["import", { 31 | "libraryName": "antd", 32 | "libraryDirectory": "es", 33 | "style": true // `style: true` 会加载 less 文件 34 | }] 35 | ] 36 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | references: 4 | container_config: &container_config 5 | docker: 6 | - image: circleci/node:8 7 | working_directory: ~/react 8 | 9 | attach_workspace: &attach_workspace 10 | attach_workspace: 11 | at: ~/react 12 | 13 | react_16: &react_16 14 | environment: 15 | REACT: 16 16 | 17 | workflow: &workflow 18 | jobs: 19 | - setup: 20 | filters: 21 | branches: 22 | ignore: gh-pages 23 | # - testing: 24 | # requires: 25 | # - setup 26 | - lint: 27 | requires: 28 | - setup 29 | 30 | 31 | jobs: 32 | setup: 33 | <<: *container_config 34 | steps: 35 | - checkout 36 | - run: node -v 37 | - run: npm -v 38 | - run: npm install 39 | - run: npm run dll 40 | - run: 41 | command: | 42 | set +eo 43 | npm ls 44 | true 45 | - persist_to_workspace: 46 | root: ~/react 47 | paths: 48 | - node_modules 49 | 50 | # testing: 51 | # <<: *container_config 52 | # steps: 53 | # - checkout 54 | # - *attach_workspace 55 | # - run: npm run testing 56 | # - run: node ./scripts/webpack.testing.config.js 57 | # - persist_to_workspace: 58 | # root: ~/react 59 | # paths: 60 | # - dist 61 | 62 | 63 | lint: 64 | <<: *container_config 65 | steps: 66 | - checkout 67 | - *attach_workspace 68 | - run: npm run lint 69 | 70 | workflows: 71 | version: 2 72 | build_test: 73 | <<: *workflow 74 | nightly: 75 | <<: *workflow 76 | triggers: 77 | - schedule: 78 | cron: "0 0 * * *" 79 | filters: 80 | branches: 81 | only: 82 | - master 83 | 84 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # /node_modules/* and /bower_components/* ignored by default 2 | 3 | /* 4 | !app 5 | 6 | /test 7 | 8 | /app/components/draw/__mocks__/draw.js 9 | 10 | /app/configs/__mocks__/ajax.js 11 | /app/resource 12 | app/images 13 | 14 | # app/configs 15 | # app/components 16 | # app/pages -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "settings": { 9 | "import/core-modules": [ 10 | "components", 11 | "actions", 12 | "api", 13 | "reducers", 14 | "utils", 15 | "constants" 16 | ] 17 | }, 18 | "parser": "babel-eslint", 19 | "parserOptions": { 20 | "ecmaFeatures": { 21 | "jsx": true, 22 | "experimentalObjectRestSpread": true 23 | } 24 | }, 25 | "rules": { 26 | "linebreak-style": "off", 27 | "func-names": 0, 28 | "max-len": [ 29 | "warn", 30 | 200, 31 | 4, 32 | { 33 | "comments": 150 34 | } 35 | ], 36 | "indent": [ 37 | "error", 38 | 2, 39 | { 40 | "SwitchCase": 1 41 | } 42 | ], 43 | "react/jsx-indent": [ 44 | 2, 45 | 2 46 | ], 47 | "semi": 0, 48 | "react/sort-comp": 0, 49 | "react/prop-types": 0, 50 | "react/prefer-es6-class": 0, 51 | "react/prefer-stateless-function": 0, 52 | "react/jsx-first-prop-new-line": 0, 53 | "react/jsx-filename-extension": 0, 54 | "no-return-assign": 0, 55 | "react/no-multi-comp": 0, 56 | "array-callback-return": 0, 57 | "no-underscore-dangle": 0, 58 | "no-bitwise": [ 59 | "error", 60 | { 61 | "allow": [ 62 | "~" 63 | ] 64 | } 65 | ], 66 | "no-plusplus": 1, 67 | "no-unused-expressions": [ 68 | "warn", 69 | { 70 | "allowShortCircuit": true, 71 | "allowTernary": true 72 | } 73 | ], 74 | "import/no-unresolved": 0, 75 | "import/no-extraneous-dependencies": 0, 76 | "jsx-a11y/no-static-element-interactions": 0, 77 | "jsx-a11y/img-has-alt": 0, 78 | "no-unused-vars": [ 79 | "warn", 80 | { 81 | "vars": "all", 82 | "args": "none" 83 | } 84 | ], 85 | "react/no-unused-state": [ 86 | "warn" 87 | ], 88 | "no-param-reassign": [ 89 | "error", 90 | { 91 | "props": false 92 | } 93 | ], 94 | "object-shorthand": 0, 95 | "jsx-a11y/anchor-is-valid": 0, 96 | "react/no-array-index-key": 0, 97 | "jsx-a11y/click-events-have-key-events": 0, 98 | "import/extensions": 0, 99 | "no-debugger": "off", 100 | "react/jsx-closing-tag-location": 0, 101 | "import/prefer-default-export": 0, 102 | "react/forbid-prop-types": 1, 103 | "class-methods-use-this": 0, 104 | "consistent-return": 1, 105 | "import/first": 1, 106 | "no-console":"off", 107 | "prefer-destructuring": [ 108 | "warn" 109 | ], 110 | "object-curly-newline": [ 111 | "error", 112 | { 113 | "minProperties": 5, 114 | "consistent": true, 115 | "multiline":true 116 | } 117 | ] 118 | }, 119 | "plugins": [ 120 | "jsx-a11y" 121 | ] 122 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js eol=lf 2 | *.json eol=lf 3 | *.jsx eol=lf 4 | *.ts eol=lf 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # vscode 7 | jsconfig.json 8 | dist 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | #mac 45 | .DS_Store 46 | 47 | #vscode 48 | .vscode 49 | /.idea/ 50 | 51 | #eslint 52 | .eslintcache 53 | 54 | #cashe 55 | .cache 56 | 57 | #don't upload node_modules.rar 58 | node_modules.rar 59 | 60 | #don't upload react.rar 61 | react.rar 62 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "linters": { 3 | "app/**/*.js": [ 4 | "npm run lint", 5 | "git add" 6 | ] 7 | } 8 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '9' 4 | cache: 5 | directories: 6 | - node_modules 7 | 8 | install: 9 | - npm install 10 | 11 | script: 'true' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present, doupi, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/apis/__mocks__/manage.js: -------------------------------------------------------------------------------- 1 | 2 | import { createApi } from '@ajax' 3 | import { mockURL, path } from '@configs/config' 4 | 5 | const prefix = 'usercenter' 6 | const option = { baseURL: mockURL } 7 | // 模块管理 8 | export const fetchModuleList = createApi(`${path}/${prefix}/resource/list`, option) // 获取模块列表 9 | export const fetchModuleDelete = createApi(`${path}/${prefix}/resource/delete`, option) // 删除模块 10 | export const fetchModuleDetail = createApi(`${path}/${prefix}/resource/detail`, option) // 获取模块详情 11 | export const fetchChangeModuleStatus = createApi(`${path}/${prefix}/resource/updateStatus`, option) // 修改模块显隐状态 12 | export const fetchModuleUpdateDetail = createApi(`${path}/${prefix}/resource/update`, option) // 修改模块详情 13 | export const fetchModuleAdd = createApi(`${path}/${prefix}/resource/save`, option) // 新增模块 14 | export const fetchButtonList = createApi(`${path}/${prefix}/resource/button/list`, 'fetchButtonList') // 按钮权限列表 15 | 16 | // 角色管理 17 | export const fetchRoleList = createApi(`${path}/${prefix}/role/list`, 'fetchRoleList') // 角色列表 18 | export const fetchRoleAdd = createApi(`${path}/${prefix}/role/save`, option) // 保存角色 19 | export const fetchRoleUpdate = createApi(`${path}/${prefix}/role/update`, option) // 角色编辑 20 | export const fetchRoleDetail = createApi(`${path}/${prefix}/role/detail`, option) // 已选择的菜单以及按钮 21 | export const fetchRoleDelete = createApi(`${path}/${prefix}/role/delete`, option) // 删除角色 22 | export const fetchModuleListInRole = createApi(`${path}/${prefix}/role/resList`, 'fetchModuleListInRole') // 已选择的模块 23 | export const fetchUpdateRoleRes = createApi(`${path}/${prefix}/role/updateRes`, option) // 更新已选择的模块 24 | 25 | export const fetchRoleDeletePeople = createApi(`${path}/${prefix}/user/removeRole`, option) 26 | export const fetchUpdateButton = createApi(`${path}/${prefix}/role/updateButton`, option) 27 | export const fetchTreeList = createApi(`${path}/${prefix}/role/resTree`, 'fetchTreeList') 28 | // 用户管理 29 | export const fetchUserDepttList = createApi(`${path}/${prefix}/dept/list`, 'fetchUserDepttList') // 获取用户管理左侧分类列表 30 | export const fetchUserList = createApi(`${path}/${prefix}/user/list`, 'fetchUserList') // 获取用户列表 31 | export const fetchUserDetail = createApi(`${path}/${prefix}/user/detail`, option) // 获取用户详情 32 | export const fetchUserDetailUpdate = createApi(`${path}/${prefix}/user/update`, option) // 修改用户详情 33 | export const fetchUserAdd = createApi(`${path}/${prefix}/user/save`, option) // 新增用户 34 | export const synUser = createApi(`${path}/${prefix}/user/synUser`, option) // 同步用户 35 | export const fetchUserSetRole = createApi(`${path}/${prefix}/user/updateRole`, option) // 修改用户角色 36 | export const fetchUserDelete = createApi(`${path}/${prefix}/user/delete`, option) // 删除用户 37 | export const fetchChangeUserStatus = createApi(`${path}/${prefix}/user/updateStatus`, option) // 设置用户是否冻结状态 38 | -------------------------------------------------------------------------------- /app/apis/common.js: -------------------------------------------------------------------------------- 1 | 2 | import { createApi } from '@ajax' 3 | import { mockURL, /* baseURL, */ path } from '@config' 4 | 5 | const prefix = 'usercenter' 6 | const option = { baseURL: mockURL } 7 | 8 | export const login = createApi(`${path}/${prefix}/login`, option) // 登陆 9 | export const logout = createApi(`${path}/${prefix}/logout`, option) // 登出 10 | export const loginByTicket = createApi(`${path}/${prefix}/loginByTicket`, option) // 通过ticket登陆 11 | export const loginByKey = createApi(`${path}/service/pagerservice/checkKey`, option) // 通过key进入项目 12 | export const staff = createApi(`${path}/${prefix}/user/userInfo`, option) // 用户信息 13 | export const synUser = createApi(`${path}/${prefix}/user/synUser`, option)// 同步用户 14 | export const menu = createApi(`${path}/${prefix}/user/userMenu`, option) // 获取菜单 15 | export const getLevel = createApi(`${path}/${prefix}/user/getLevel`, option) // 当前用户的等级 16 | export const getBtns = createApi(`${path}/${prefix}/resource/listByPid`, option) // 获取菜单id 17 | export const getAllRetrieval = createApi(`${path}/data/sys/retrieval/queryAllRetrievald`) // 获取gForm2.0头部搜索 18 | -------------------------------------------------------------------------------- /app/apis/manage.js: -------------------------------------------------------------------------------- 1 | 2 | import { createApi } from '@ajax' 3 | import { mockURL, /* baseURL, */ path } from '@config' 4 | 5 | const prefix = 'usercenter' 6 | const option = { baseURL: mockURL } 7 | 8 | // 模块管理 9 | export const fetchModuleList = createApi(`${path}/${prefix}/resource/list`, option) // 获取模块列表 10 | export const fetchModuleDelete = createApi(`${path}/${prefix}/resource/delete`, option) // 删除模块 11 | export const fetchModuleDetail = createApi(`${path}/${prefix}/resource/detail`, option) // 获取模块详情 12 | export const fetchChangeModuleStatus = createApi(`${path}/${prefix}/resource/updateStatus`, option) // 修改模块显隐状态 13 | export const fetchModuleUpdateDetail = createApi(`${path}/${prefix}/resource/update`, option) // 修改模块详情 14 | export const fetchModuleAdd = createApi(`${path}/${prefix}/resource/save`, option) // 新增模块 15 | export const fetchButtonList = createApi(`${path}/${prefix}/resource/button/list`, option) // 按钮权限列表 16 | 17 | // 角色管理 18 | export const fetchRoleList = createApi(`${path}/${prefix}/role/list`, option) // 角色列表 19 | export const fetchRoleAdd = createApi(`${path}/${prefix}/role/save`, option) // 保存角色 20 | export const fetchRoleUpdate = createApi(`${path}/${prefix}/role/update`, option) // 角色编辑 21 | export const fetchRoleDetail = createApi(`${path}/${prefix}/role/detail`, option) // 已选择的菜单以及按钮 22 | export const fetchRoleDelete = createApi(`${path}/${prefix}/role/delete`, option) // 删除角色 23 | export const fetchModuleListInRole = createApi(`${path}/${prefix}/role/resList`, option) // 已选择的模块 24 | export const fetchUpdateRoleRes = createApi(`${path}/${prefix}/role/updateRes`, option) // 更新已选择的模块 25 | 26 | export const fetchRoleDeletePeople = createApi(`${path}/${prefix}/user/removeRole`, option) 27 | export const fetchUpdateButton = createApi(`${path}/${prefix}/role/updateButton`, option) 28 | export const fetchTreeList = createApi(`${path}/${prefix}/role/resTree`, option) 29 | // 用户管理 30 | export const fetchUserDepttList = createApi(`${path}/${prefix}/dept/list`, option) // 获取用户管理左侧分类列表 31 | export const fetchUserList = createApi(`${path}/${prefix}/user/list`, option) // 获取用户列表 32 | export const fetchUserDetail = createApi(`${path}/${prefix}/user/detail`, option) // 获取用户详情 33 | export const fetchUserDetailUpdate = createApi(`${path}/${prefix}/user/update`, option) // 修改用户详情 34 | export const fetchUserAdd = createApi(`${path}/${prefix}/user/save`, option) // 新增用户 35 | export const synUser = createApi(`${path}/${prefix}/user/synUser`, option) // 同步用户 36 | export const fetchUserSetRole = createApi(`${path}/${prefix}/user/updateRole`, option) // 修改用户角色 37 | export const fetchUserDelete = createApi(`${path}/${prefix}/user/delete`, option) // 删除用户 38 | export const fetchChangeUserStatus = createApi(`${path}/${prefix}/user/updateStatus`, option) // 设置用户是否冻结状态 39 | -------------------------------------------------------------------------------- /app/client.js: -------------------------------------------------------------------------------- 1 | // import '@babel/polyfill' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom' 4 | import { Provider } from 'react-redux' 5 | import { hot } from 'react-hot-loader/root' 6 | import '@config' 7 | import Routes from '@configs/router.config' 8 | import configure from '@middleware/configureStore' 9 | 10 | const HotRoutes = hot(Routes) 11 | const store = configure({ }) 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('root'), 17 | ) 18 | -------------------------------------------------------------------------------- /app/components/draw/README.md: -------------------------------------------------------------------------------- 1 | ## drawde(抽屉组件) 2 | #### props 3 | | 属性名 | 类型 | 默认值 | 是否必填 | 描述 | 4 | | -------- | ----------------- | ------ | ---- | ------------------------------- | 5 | | visible | String | 无 | 是 | 组件是否显示 | 6 | | title | String | '标题' | 否 | 抽屉标题 | 7 | | footer | String\|ReactNode | 无 | 否 | 组件底部内容 | 8 | | onCancel | Function | 无 | 是 | 关闭组件的回调方法,将组件设置的visible设置为false | 9 | | size | String | 'base' | 否 | 组件的大小,有sm,base,lg共3个值 | 10 | 11 | #### 调用示例 12 | 13 | ```javascript 14 | { 15 | this.state.drawVisible ? 16 | this.setState({ drawVisible: false })} 21 | size="lg" 22 | > 23 |
111
24 |
25 | : null 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /app/components/draw/draw2.0.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 韩卿 3 | * @Date: 2017-08-20 16:07:21 4 | * @Last Modified by: dupi 5 | * @Last Modified time: 2018-10-22 17:10:34 6 | */ 7 | 8 | 9 | import React, { Component } from 'react' 10 | import ReactDOM from 'react-dom' 11 | import PropTypes from 'prop-types'; 12 | // import configure from '../../store/configureStore' 13 | 14 | import './draw.less' 15 | 16 | // const store = configure({ config: global.$GLOBALCONFIG }) 17 | 18 | // 声明组件 并对外输出 19 | export default class Drawer extends Component { 20 | // 初始化页面常量 绑定事件方法 21 | constructor(props, context) { 22 | super(props, context) 23 | this.state = { 24 | // activeTab: 'pop' , 25 | drawTrasformClass: '', 26 | maskTrasformClass: '', 27 | drawerSizeClass: 'modal-base', 28 | // drawerSizeClassList: ['modal-base', 'modal-sm', 'modal-lg'], 29 | } 30 | } 31 | 32 | componentWillMount() { 33 | const { 34 | size = 'default', 35 | } = this.props 36 | this.getDrawerSize(size) 37 | this.setTrasformClass() 38 | } 39 | 40 | 41 | // 组件已经加载到dom中 42 | componentDidMount() { 43 | const { 44 | visible = true, 45 | } = this.props 46 | if (visible) { 47 | this.initDrawer() 48 | } 49 | } 50 | 51 | // 监测visible属性, 52 | componentWillReceiveProps(nextProps) { 53 | /* const { 54 | visible = true, 55 | } = this.props 56 | console.log(visible) 57 | console.log(nextProps) 58 | if (visible || nextProps.visible) { 59 | this.initDrawer() 60 | } else { 61 | this.removeDrawer() 62 | } */ 63 | } 64 | 65 | componentDidUpdate() { 66 | this.renderDrawer() 67 | } 68 | 69 | componentWillUnmount() { 70 | // ut(() => { 71 | ReactDOM.unmountComponentAtNode(this.popup) 72 | // }, 300) 73 | } 74 | 75 | // 初始化抽屉 76 | initDrawer = () => { 77 | this.popup = document.createElement('div') 78 | this.popup.setAttribute('class', 'drawers') 79 | this.renderDrawer() 80 | document.body.appendChild(this.popup) 81 | this.setTrasformClass() 82 | } 83 | 84 | // 抽屉添加动画效果class的设置 85 | setTrasformClass = () => { 86 | const { 87 | visible = true, 88 | } = this.props 89 | if (visible) { 90 | this.setState({ 91 | drawTrasformClass: 'draw-enter', 92 | maskTrasformClass: 'mask-enter', 93 | }, () => 94 | setTimeout(() => { 95 | this.setState({ 96 | drawTrasformClass: '', 97 | maskTrasformClass: '', 98 | }) 99 | }, 300)) 100 | } 101 | } 102 | 103 | // 移除弹窗 104 | removeDrawer = () => { 105 | this.setState({ 106 | drawTrasformClass: 'draw-leave', 107 | maskTrasformClass: 'mask-leave', 108 | }, () => 109 | setTimeout(() => { 110 | this.setState({ 111 | drawTrasformClass: '', 112 | maskTrasformClass: '', 113 | }) 114 | document.body.removeChild(this.popup) 115 | ReactDOM.unmountComponentAtNode(this.popup) 116 | 117 | this.props.onCancel() 118 | }, 200)) 119 | } 120 | 121 | // 判断抽屉的尺寸class 122 | getDrawerSize = (size) => { 123 | switch (size) { 124 | case 'sm': 125 | this.setState({ 126 | drawerSizeClass: 'drawer-sm', 127 | }) 128 | break 129 | case 'lg': 130 | this.setState({ 131 | drawerSizeClass: 'drawer-lg', 132 | }) 133 | break 134 | default: 135 | this.setState({ 136 | drawerSizeClass: 'drawer-base', 137 | }) 138 | break 139 | } 140 | } 141 | 142 | // 将弹窗内容插入到指定dom中 143 | renderDrawer() { 144 | const { 145 | title = '标题', 146 | footer = null, 147 | } = this.props 148 | const { 149 | drawTrasformClass, 150 | maskTrasformClass, 151 | drawerSizeClass, 152 | } = this.state 153 | 154 | ReactDOM.render( 155 |
156 |
this.removeDrawer()} /> 157 |
158 |
159 |
160 | 163 |
164 |
{title}
165 |
166 | 167 | {this.props.children} 168 | 169 |
170 | {footer} 171 |
172 |
173 |
174 |
175 |
, 176 | this.popup, 177 | ) 178 | } 179 | 180 | render() { 181 | return null 182 | } 183 | } 184 | 185 | Drawer.contextTypes = { 186 | form: PropTypes.object, 187 | vertical: PropTypes.bool, 188 | store: PropTypes.object, 189 | }; 190 | 191 | class AntModalBody extends Component { 192 | getChildContext() { 193 | return { form: this.props.context.form, vertical: this.props.context.vertical, store: this.props.context.store } 194 | } 195 | render() { 196 | return ( 197 |
198 | {this.props.children} 199 |
200 | ) 201 | } 202 | } 203 | 204 | AntModalBody.childContextTypes = { 205 | form: PropTypes.object, 206 | vertical: PropTypes.string, 207 | store: PropTypes.object, 208 | } 209 | -------------------------------------------------------------------------------- /app/components/logo/logo.css: -------------------------------------------------------------------------------- 1 | .logo-gather-demo-edit-wrapper { 2 | position: absolute; 3 | bottom: 0; 4 | width: 100%; 5 | background: #f1f1f1; 6 | padding: 0 5%; 7 | line-height: 45px; 8 | } 9 | 10 | .logo-gather-demo-edit-wrapper ul { 11 | display: block; 12 | width: 100%; 13 | overflow: hidden; 14 | } 15 | 16 | .logo-gather-demo-edit-wrapper ul li:first-child { 17 | margin-left: 0; 18 | } 19 | 20 | .logo-gather-demo-edit-wrapper ul li { 21 | float: left; 22 | vertical-align: middle; 23 | margin: 0 5px; 24 | } 25 | 26 | .logo-gather-demo-wrapper { 27 | position: absolute; 28 | z-index: 10; 29 | top: 0; 30 | /*margin:0 auto;*/ 31 | left: 0; 32 | background: transparent; 33 | overflow: hidden; 34 | height: 100%; 35 | width: 100%; 36 | } 37 | 38 | .logo-gather-demo-wrapper .point-wrapper { 39 | position: absolute; 40 | } 41 | 42 | .logo-gather-demo-wrapper .point { 43 | border-radius: 100%; 44 | } 45 | 46 | .logo-gather-demo-wrapper .right-side { 47 | width: 1038px; 48 | height: 280px; 49 | position: absolute; 50 | right: 0; 51 | top: -60px; 52 | bottom: 0; 53 | left: 0; 54 | margin: auto; 55 | } 56 | 57 | .logo-gather-demo-wrapper .right-side * { 58 | pointer-events: none; 59 | } 60 | 61 | @media screen and (max-width: 414px) { 62 | .exhibition-details-demo { 63 | overflow: hidden; 64 | } 65 | 66 | .logo-gather-demo-edit-wrapper { 67 | transform: translateY(100%); 68 | transition: transform .45s ease-in-out; 69 | } 70 | 71 | .logo-gather-demo-edit-wrapper.open{ 72 | transform: translateY(0); 73 | } 74 | 75 | .logo-gather-demo-edit-wrapper .anticon-down{ 76 | transition: transform .45s ease-in-out; 77 | } 78 | 79 | .logo-gather-demo-edit-wrapper.open .anticon-down{ 80 | transform: rotate(180deg); 81 | } 82 | 83 | .logo-gather-demo-edit-wrapper > div { 84 | width: 90%; 85 | line-height: 24px !important; 86 | margin-bottom: 5px; 87 | } 88 | 89 | .exhibition-details-demo .edit-button{ 90 | position: absolute; 91 | top: -20px; 92 | width: 30px; 93 | height: 20px; 94 | border-radius: 30px 30px 0 0; 95 | background: #f1f1f1; 96 | text-align: center; 97 | left: 0; 98 | right: 0; 99 | margin: auto; 100 | box-shadow: 0 -5px 5px rgba(0, 0, 0, 0.15); 101 | } 102 | 103 | .logo-gather-demo-edit-wrapper ul { 104 | margin: 5px auto; 105 | } 106 | 107 | .phone-float-none { 108 | clear: both; 109 | margin-left: 0 !important; 110 | } 111 | 112 | .none { 113 | display: none; 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /app/components/tableList/tableList.js: -------------------------------------------------------------------------------- 1 | /* 2 | 如果要使用不回行的table,那么就要设置table的className为nowrap,并且不能对td设置特定的宽度,且scroll={{ y: true, x: true }}的X值必须为true 3 | */ 4 | 5 | import React, { Component } from 'react' 6 | import { Table, Pagination } from 'antd' 7 | 8 | export default class TableList extends Component { 9 | componentDidMount() { 10 | this.tableWidthAdaptive() 11 | } 12 | 13 | componentWillUnmount() { 14 | clearInterval(this.t) 15 | } 16 | 17 | // 动态计算td的宽度 18 | tableWidthAdaptive = () => { 19 | if (this.props.className && this.props.className.indexOf('nowrap') > -1) { 20 | this.t = setInterval(() => { // 通过定时器循环的方式,看看真实节点是否加载到dom中了 21 | const tds = document.querySelector('.ant-table-row') && document.querySelector('.ant-table-row').querySelectorAll('td') 22 | const ths = document.querySelectorAll('.ant-table-header th') 23 | if (tds && tds.length) { 24 | clearInterval(this.t) 25 | for (let i = 0; i < tds.length; i += 1) { 26 | const tdw = tds[i].offsetWidth 27 | const thw = ths[i].offsetWidth 28 | const w = (tdw > thw) ? tdw : thw 29 | tds[i].style.minWidth = `${w}px` 30 | ths[i].style.minWidth = `${w}px` 31 | } 32 | } 33 | }, 100) 34 | } 35 | } 36 | 37 | render() { 38 | const { 39 | currentPage, 40 | pageSize, 41 | totalCount, 42 | onShowSizeChange, 43 | onChange, 44 | columns, 45 | } = this.props 46 | const hasMultiHead = columns.filter(one => !!one.children).length > 0 47 | return ( 48 |
49 | 56 | { currentPage ? 57 | `共 ${_totalCount} 条`} 64 | current={currentPage || 1} 65 | pageSize={pageSize || 10} 66 | {...this.props} 67 | /> : null 68 | } 69 | 70 | ) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/configs/__mocks__/ajax.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import fetchUserList from '../../mocks/apis/sys/userManage/fetchUserList'; 3 | import fetchUserDepttList from '../../mocks/apis/sys/userManage/fetchUserDepttList'; 4 | import fetchButtonList from '../../mocks/apis/sys/roleManage/fetchButtonList'; 5 | import fetchRoleList from '../../mocks/apis/sys/userManage/fetchRoleList'; 6 | import fetchTreeList from '../../mocks/apis/sys/roleManage/fetchTreeList'; 7 | import fetchModuleListInRole from '../../mocks/apis/sys/roleManage/fetchModuleListInRole'; 8 | 9 | // 创建发起api的启动器 10 | export const createApi = function (api, type) { 11 | fetchUserList.data.totalCount = null; 12 | if (type == 'fetchUserList') { 13 | return backState(fetchUserList); 14 | } 15 | if (type == 'fetchUserDepttList') { 16 | return backState(fetchUserDepttList); 17 | } 18 | if (type == 'fetchButtonList') { 19 | return backState(fetchButtonList); 20 | } 21 | if (type == 'fetchRoleList') { 22 | return backState(fetchRoleList); 23 | } 24 | if (type == 'fetchTreeList') { 25 | return backState(fetchTreeList); 26 | } 27 | fetchModuleListInRole.data.list[0].status = true; 28 | if (type == 'fetchModuleListInRole') { 29 | return backState(fetchModuleListInRole); 30 | } 31 | const param = { 32 | data: { 33 | list: [], 34 | }, 35 | msg: '操作成功', 36 | status: 1, 37 | }; 38 | return backState(param); 39 | }; 40 | function backState(param) { 41 | return (data, callback, callback2) => { 42 | callback(param); 43 | if (callback2) { 44 | callback2(param); 45 | } 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /app/configs/config.js: -------------------------------------------------------------------------------- 1 | 2 | export const set = 'set$' 3 | export const brandName = 'React' // slogan 4 | 5 | // 开发环境默认配置 6 | let _serverIp = 'http://192.168.1.222' 7 | let _port = '1111' 8 | let _baseURL = `${_serverIp}:${_port}` 9 | let _mockURL = 'http://localhost:1111/' 10 | 11 | if (process.env.NODE_ENV === 'testing') { // 测试环境 12 | _mockURL = 'http://localhost:1111/' 13 | _port = '1111' 14 | _baseURL = `${_serverIp}:${_port}` 15 | } 16 | if (process.env.NODE_ENV === 'production') { // 发布环境 17 | _port = '1111' 18 | _serverIp = 'http://192.168.1.123' 19 | _baseURL = `${_serverIp}:${_port}` 20 | } 21 | 22 | export const serverIp = _serverIp 23 | export const path = '/mock' 24 | export const timeout = '15000' // 接口超时限制(ms) 25 | export const baseURL = _baseURL 26 | export const mockURL = _mockURL 27 | -------------------------------------------------------------------------------- /app/configs/regular.config.js: -------------------------------------------------------------------------------- 1 | // 常用的正则规则 2 | // eslint-disable-next-line 3 | export const regExpConfig = { 4 | IDcard: /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/, // 身份证 5 | mobile: /^1([3|4|5|7|8|])\d{9}$/, // 手机号码 6 | telephone: /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/, // 固定电话 7 | num: /^[0-9]*$/, // 数字 8 | phoneNo: /(^1([3|4|5|7|8|])\d{9}$)|(^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$)/, // 电话或者手机 9 | policeNo: /^[0-9A-Za-z]{4,10}$/, // 账号4-10位数字或字母组成 10 | pwd: /^[0-9A-Za-z]{6,16}$/, // 密码由6-16位数字或者字母组成 11 | isNumAlpha: /^[0-9A-Za-z]*$/, // 字母或数字 12 | isAlpha: /^[a-zA-Z]*$/, // 是否字母 13 | isNumAlphaCn: /^[0-9a-zA-Z\u4E00-\uFA29]*$/, // 是否数字或字母或汉字 14 | isPostCode: /^[\d-]*$/i, // 是否邮编 15 | isNumAlphaUline: /^[0-9a-zA-Z_]*$/, // 是否数字、字母或下划线 16 | isNumAndThanZero: /^([1-9]\d*(\.\d+)?|0)$/, // 是否为整数且大于0/^[1-9]\d*(\.\d+)?$/ 17 | isNormalEncode: /^(\w||[\u4e00-\u9fa5]){0,}$/, // 是否为非特殊字符(包括数字字母下划线中文) 18 | isTableName: /^[a-zA-Z][A-Za-z0-9#$_-]{0,29}$/, // 表名 19 | isInt: /^-?\d+$/, // 整数 20 | isTableOtherName: /^[\u4e00-\u9fa5]{0,20}$/, // 别名 21 | // isText_30: /^(\W|\w{1,2}){0,15}$/, // 正则 22 | // isText_20: /^(\W|\w{1,2}){0,10}$/, // 正则 23 | isText_30: /^(\W|\w{1}){0,30}$/, // 匹配30个字符,字符可以使字母、数字、下划线、非字母,一个汉字算1个字符 24 | isText_50: /^(\W|\w{1}){0,50}$/, // 匹配50个字符,字符可以使字母、数字、下划线、非字母,一个汉字算1个字符 25 | isText_20: /^(\W|\w{1}){0,20}$/, // 匹配20个字符,字符可以使字母、数字、下划线、非字母,一个汉字算1个字符 26 | isText_100: /^(\W|\w{1}){0,100}$/, // 匹配100个字符,字符可以使字母、数字、下划线、非字母,一个汉字算1个字符 27 | isText_250: /^(\W|\w{1}){0,250}$/, // 匹配250个字符,字符可以使字母、数字、下划线、非字母,一个汉字算1个字符 28 | isNotChina: /^[^\u4e00-\u9fa5]{0,}$/, // 不为中文 IDcard: /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/, // 身份证 29 | IDcardAndAdmin: /^(([1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X))|(admin))$/, // 身份证或者是admin账号 30 | IDcardTrim: /^\s*(([1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3})|([1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X))|(admin))\s*$/, // 身份证 31 | num1: /^[1-9]*$/, // 数字 32 | companyNO: /^qqb_[0-9a-zA-Z_]{1,}$/, // 公司人员账号 33 | imgType: /image\/(png|jpg|jpeg|gif)$/, // 上传图片类型 34 | isChina: /^[\u4e00-\u9fa5]{2,8}$/, 35 | isNozeroNumber: /^\+?[1-9]\d*$/, // 大于零的正整数 36 | float: /^\d+(\.?|(\.\d+)?)$/, // 匹配正整数或者小数 或者0.这个特殊值 37 | } 38 | 39 | -------------------------------------------------------------------------------- /app/configs/router.config.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Router, Route, IndexRoute, hashHistory/* , Redirect */ } from 'react-router' 3 | import { isLogin } from '@configs/common' 4 | import { set } from '@config' 5 | 6 | import * as base from '@pages/base' // 基础 7 | import * as sysSet from '@pages/set' // 设置中心-系统设置 8 | import * as menu from '@pages/menu' // 菜单 9 | 10 | export default () => ( 11 | 12 | 13 | 14 | 15 | {/* */} 16 | {/** *菜单 开始 */} 17 | 18 | 19 | {/** *菜单 结束 */} 20 | {/** *系统设置 开始 */} 21 | 22 | 23 | 24 | {/** *系统设置 结束 */} 25 | 26 | 27 | 28 | 29 | ) 30 | -------------------------------------------------------------------------------- /app/configs/socket.js: -------------------------------------------------------------------------------- 1 | import io from 'socket.io-client'; 2 | // import queryString from 'query-string'; 3 | 4 | // const parsed = queryString.parse(window.location.search); 5 | 6 | class Socket { 7 | constructor(ua = 'control', disable = false) { 8 | this.uid = this.random(); 9 | // this.group = parsed.group || 'prod'; 10 | this.disable = disable; 11 | this.socket = io('http://47.102.198.102:8080/', { 12 | transports: ['websocket'], 13 | query: { 14 | ua, 15 | group: 'prod', 16 | }, 17 | }); 18 | this.isSpeaker = false; 19 | this.speakerHandlers = []; 20 | 21 | this.socket.on('room', ({ speaker }) => { 22 | this.isSpeaker = speaker === this.socket.id; 23 | this.speakerHandlers.forEach((cb) => { 24 | if (typeof cb === 'function') { 25 | cb(this.isSpeaker); 26 | } 27 | }); 28 | }); 29 | } 30 | 31 | random = () => Date.now() + Math.random(); 32 | 33 | registerSpeakerHandler = (callback) => { 34 | this.unregisterSpeakerHandler(callback); 35 | this.speakerHandlers.push(callback); 36 | }; 37 | 38 | unregisterSpeakerHandler = (callback) => { 39 | let i = -1; 40 | this.speakerHandlers.some((cb, index) => { 41 | if (cb === callback) { 42 | i = index; 43 | return true; 44 | } 45 | return false; 46 | }); 47 | if (i >= 0) { 48 | this.speakerHandlers.splice(i, 1); 49 | } 50 | }; 51 | 52 | dispatch = (data) => { 53 | // if (!this.isSpeaker && !data.speaker) { 54 | // return Promise.resolve(); 55 | // } 56 | 57 | data.uid = this.uid; 58 | data.timestamp = this.random(); 59 | this.socket.emit('dispatch', data); 60 | if (data.speaker) { 61 | return new Promise((resolve, reject) => { 62 | this.registerSpeakerHandler((r) => { 63 | if (r) { 64 | resolve(); 65 | } else { 66 | reject(); 67 | } 68 | }); 69 | }); 70 | } 71 | return new Promise((resolve) => { 72 | const callback = (res) => { 73 | if (this.uid === res.uid && res.timestamp === data.timestamp) { 74 | this.socket.off('dispatch', callback); 75 | resolve(); 76 | } 77 | }; 78 | this.socket.on('dispatch', callback); 79 | }); 80 | }; 81 | 82 | sent = (data, force) => { 83 | if (!this.isSpeaker && !force) { 84 | return; 85 | } 86 | data.uid = this.uid; 87 | data.group = this.group; 88 | this.socket.emit('msg', data); 89 | }; 90 | 91 | on = (name, call) => { 92 | if (!this.disable) { 93 | this.socket.on(name, call); 94 | } 95 | }; 96 | 97 | close = () => { 98 | this.socket.close(); 99 | }; 100 | 101 | send = (data) => { 102 | this.dispatch({ 103 | source: 'control', 104 | targets: ['control'], 105 | type: 'dispatch', 106 | payload: data, 107 | }) 108 | } 109 | } 110 | 111 | const socket = new Socket('control', window.location.pathname === '/console'); 112 | export const MsgSocket = Socket; 113 | 114 | export default socket; 115 | -------------------------------------------------------------------------------- /app/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/default.png -------------------------------------------------------------------------------- /app/images/icon/menu/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/default.png -------------------------------------------------------------------------------- /app/images/icon/menu/icon_duty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/icon_duty.png -------------------------------------------------------------------------------- /app/images/icon/menu/icon_pgmb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/icon_pgmb.png -------------------------------------------------------------------------------- /app/images/icon/menu/icon_pgmx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/icon_pgmx.png -------------------------------------------------------------------------------- /app/images/icon/menu/icon_statistics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/icon_statistics.png -------------------------------------------------------------------------------- /app/images/icon/menu/icon_xtxg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/icon_xtxg.png -------------------------------------------------------------------------------- /app/images/icon/menu/moduleManage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/moduleManage.png -------------------------------------------------------------------------------- /app/images/icon/menu/roleManage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/roleManage.png -------------------------------------------------------------------------------- /app/images/icon/menu/userManage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/icon/menu/userManage.png -------------------------------------------------------------------------------- /app/images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/login.png -------------------------------------------------------------------------------- /app/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/logo.png -------------------------------------------------------------------------------- /app/images/navcontrol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/navcontrol.png -------------------------------------------------------------------------------- /app/images/photo_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/photo_default.jpg -------------------------------------------------------------------------------- /app/images/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/user.jpg -------------------------------------------------------------------------------- /app/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/images/user.png -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React 6 | 7 | 8 |
9 | <% for(var i = 0; i < htmlWebpackPlugin.options.dlls.length;i++){%> 10 | 11 | <%}%> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/middleware/configureStore.js: -------------------------------------------------------------------------------- 1 | 2 | import { createStore, applyMiddleware } from 'redux' 3 | import thunkMiddleware from 'redux-thunk' 4 | import rootReducer from '@reducers' 5 | import { logger, /* router, */ reduxRouterMiddleware } from './index' 6 | 7 | const nextReducer = require('@reducers') 8 | 9 | export default function configure(initialState) { 10 | // console.log('initialState', initialState) 11 | const create = window.devToolsExtension 12 | ? window.devToolsExtension()(createStore) 13 | : createStore 14 | 15 | const createStoreWithMiddleware = applyMiddleware( 16 | reduxRouterMiddleware, 17 | thunkMiddleware, 18 | logger, 19 | // router, 20 | )(create) 21 | 22 | const store = createStoreWithMiddleware(rootReducer, initialState) 23 | 24 | if (module.hot) { 25 | module.hot.accept('@reducers', () => { 26 | store.replaceReducer(nextReducer) 27 | }) 28 | } 29 | 30 | return store 31 | } 32 | -------------------------------------------------------------------------------- /app/middleware/history.js: -------------------------------------------------------------------------------- 1 | import { useRouterHistory } from 'react-router' 2 | import createHashHistory from 'history/lib/createHashHistory' 3 | 4 | // export default useRouterHistory(createHashHistory)() 5 | const history = useRouterHistory(createHashHistory)({}) 6 | export default history 7 | -------------------------------------------------------------------------------- /app/middleware/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { routerMiddleware } from 'react-router-redux' 3 | import logger from './logger' 4 | import history from './history' 5 | // import router from './router' 6 | 7 | const reduxRouterMiddleware = routerMiddleware(history) 8 | 9 | export { 10 | reduxRouterMiddleware, 11 | logger, 12 | // router, 13 | } 14 | -------------------------------------------------------------------------------- /app/middleware/logger.js: -------------------------------------------------------------------------------- 1 | 2 | /* eslint-disable */ 3 | export default store => next => (action) => { 4 | // console.log(action) 5 | return next(action) 6 | } 7 | -------------------------------------------------------------------------------- /app/middleware/router.js: -------------------------------------------------------------------------------- 1 | import { browserHistory } from 'react-router' 2 | import { routerMiddleware } from 'react-router-redux' 3 | 4 | export default routerMiddleware(browserHistory) 5 | -------------------------------------------------------------------------------- /app/mocks/apis/base/index.js: -------------------------------------------------------------------------------- 1 | const login = require('./login') 2 | const logout = require('./logout') 3 | const staff = require('./staff') 4 | const menu = require('./menu') 5 | 6 | module.exports = { login, logout, staff, menu } 7 | -------------------------------------------------------------------------------- /app/mocks/apis/base/login.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | ticket: 'ticket', 4 | token: '1111', 5 | }, 6 | msg: '操作成功', 7 | status: 1, 8 | } 9 | -------------------------------------------------------------------------------- /app/mocks/apis/base/logout.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | }, 5 | msg: '退出成功!', 6 | status: 1, 7 | } 8 | -------------------------------------------------------------------------------- /app/mocks/apis/base/menu.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | list: [ 5 | 6 | { 7 | id: 10063, 8 | resName: '概览', 9 | resKey: 'desk$/index', 10 | resIcon: 'pgmb', 11 | }, 12 | // { 13 | // id: 10064, 14 | // resName: 'socket接收', 15 | // resKey: 'socketReceive', 16 | // resIcon: 'pgmb', 17 | // }, 18 | { 19 | id: 600110233, 20 | resName: '图表', 21 | resKey: 'echarts', 22 | resIcon: 'statistics', 23 | }, 24 | { 25 | id: 100631, 26 | resName: '编辑器', 27 | resKey: 'editor', 28 | resIcon: 'duty', 29 | }, 30 | 31 | { 32 | id: 10062, 33 | resName: '设置中心', 34 | children: [ 35 | { 36 | id: 10108, 37 | resName: '用户管理', 38 | resKey: 'set$/userManage', 39 | resIcon: 'userManage', 40 | }, 41 | { 42 | id: 10109, 43 | resName: '角色管理', 44 | resKey: 'set$/roleManage', 45 | resIcon: 'roleManage', 46 | }, 47 | { 48 | id: 10110, 49 | resName: '权限管理', 50 | resKey: 'set$/moduleManage', 51 | resIcon: 'moduleManage', 52 | }, 53 | ], 54 | resKey: 'set$', 55 | resIcon: 'xtxg', 56 | }, 57 | ], 58 | }, 59 | msg: '操作成功', 60 | status: 1, 61 | } 62 | -------------------------------------------------------------------------------- /app/mocks/apis/base/staff.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | id: 1, 5 | username: 'admin', 6 | password: '121212', 7 | chineseName: '管理员', 8 | idcardNo: '000000000000000000', 9 | policeCode: '000000', 10 | deptCode: '370200000000', 11 | gender: 1, 12 | email: 'abc@abc.com', 13 | phoneNo: '15100000005', 14 | duty: '超级管理员', 15 | address: 'address', 16 | remark: 'remarl', 17 | type: 0, 18 | status: 0, 19 | roles: [ 20 | { 21 | id: 1, 22 | roleName: '超级管理员', 23 | resources: [], 24 | }, 25 | ], 26 | deptName: '杭州市', 27 | ticket: '.2XxGlEuidOmAoYIdSo6pQIlGbQSh83U7p4eJsoTO-70', 28 | gxdwdm: '370200000000', 29 | deptLevel: '1', 30 | defaultDeptCode: '370200000000', 31 | defaultXzqhCode: '370200', 32 | }, 33 | msg: '操作成功', 34 | status: 1, 35 | } 36 | -------------------------------------------------------------------------------- /app/mocks/apis/fail.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | 5 | }, 6 | msg: '操作失败', 7 | status: 0, 8 | } 9 | -------------------------------------------------------------------------------- /app/mocks/apis/success.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | 5 | }, 6 | msg: '操作成功', 7 | status: 1, 8 | } 9 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/moduleManage/fetchModuleDetail.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | id: 2, 5 | resName: '基础信息', 6 | resType: 1, 7 | resModule: 'inedx', 8 | resKey: 'menu2', 9 | resIcon: 'null', 10 | sort: 1, 11 | status: 0, 12 | }, 13 | msg: '操作成功', 14 | status: 1, 15 | } 16 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/moduleManage/fetchModuleList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | list: [ 5 | { 6 | id: 10060, 7 | resName: '工作台', 8 | children: [ 9 | { 10 | id: 10063, 11 | resName: '概览', 12 | resKey: 'desk$/index', 13 | resIcon: 'cleanAccess', 14 | }, 15 | ], 16 | resKey: 'desk$', 17 | resIcon: 'home', 18 | }, 19 | { 20 | id: 10062, 21 | resName: '设置中心', 22 | children: [ 23 | { 24 | id: 10108, 25 | resName: '用户管理', 26 | resKey: 'set$/userManage', 27 | resIcon: 'userManage', 28 | }, 29 | { 30 | id: 10109, 31 | resName: '角色管理', 32 | resKey: 'set$/roleManage', 33 | resIcon: 'roleManage', 34 | }, 35 | { 36 | id: 10110, 37 | resName: '权限管理', 38 | resKey: 'set$/moduleManage', 39 | resIcon: 'unitCount', 40 | }, 41 | ], 42 | resKey: 'set$', 43 | resIcon: 'set', 44 | }, 45 | ], 46 | }, 47 | msg: '操作成功', 48 | status: 1, 49 | } 50 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/moduleManage/index.js: -------------------------------------------------------------------------------- 1 | const fetchModuleList = require('./fetchModuleList') 2 | const fetchModuleDelete = require('../../success') 3 | const fetchModuleDetail = require('./fetchModuleDetail') 4 | const fetchChangeModuleStatus = require('../../success') 5 | const fetchModuleUpdateDetail = require('../../success') 6 | const fetchModuleAdd = require('../../success') 7 | 8 | 9 | module.exports = { 10 | fetchModuleList, 11 | fetchModuleDelete, 12 | fetchModuleDetail, 13 | fetchChangeModuleStatus, 14 | fetchModuleUpdateDetail, 15 | fetchModuleAdd, 16 | } 17 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/roleManage/fetchButtonList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | list: [ 5 | { 6 | id: 134, 7 | resName: '新增附属区苑', 8 | resType: 3, 9 | parentId: 4, 10 | roleEntities: [], 11 | resKey: 'gardenAdd', 12 | sort: 0, 13 | status: 0, 14 | }, 15 | { 16 | id: 137, 17 | resName: '新增附属区苑1', 18 | resType: 3, 19 | parentId: 4, 20 | roleEntities: [], 21 | resKey: 'gardenAdd', 22 | sort: 0, 23 | status: 0, 24 | }, 25 | ], 26 | }, 27 | msg: '操作成功', 28 | status: 1, 29 | } 30 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/roleManage/fetchModuleListInRole.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | list: [ 4 | { 5 | id: 10060, 6 | resName: '工作台', 7 | children: [ 8 | { 9 | id: 10063, 10 | resName: '概览', 11 | resKey: 'desk$/index', 12 | resIcon: 'cleanAccess', 13 | resType: 1, 14 | status: 0, 15 | parentId: 10060, 16 | }, 17 | ], 18 | resKey: 'desk$', 19 | resIcon: 'home', 20 | resType: 1, 21 | status: 0, 22 | }, 23 | { 24 | id: 10062, 25 | resName: '设置中心', 26 | children: [ 27 | { 28 | id: 10108, 29 | resName: '用户管理', 30 | resKey: 'set$/userManage', 31 | resIcon: 'userManage', 32 | parentId: 10062, 33 | children: [ 34 | { 35 | id: 11003, 36 | resName: '看看', 37 | resKey: 'desk$/index', 38 | resIcon: 'cleanAccess', 39 | parentId: 10108, 40 | children: [ 41 | { 42 | id: 11004, 43 | resName: '测试', 44 | resKey: 'desk$/index', 45 | resIcon: 'cleanAccess', 46 | parentId: 11003, 47 | }, 48 | ], 49 | }, 50 | ], 51 | }, 52 | { 53 | id: 10109, 54 | resName: '角色管理', 55 | resKey: 'set$/roleManage', 56 | resIcon: 'roleManage', 57 | parentId: 10062, 58 | }, 59 | { 60 | id: 10110, 61 | resName: '权限管理', 62 | resKey: 'set$/moduleManage', 63 | resIcon: 'unitCount', 64 | parentId: 10062, 65 | }, 66 | ], 67 | resKey: 'set$', 68 | resIcon: 'set', 69 | resType: 1, 70 | status: 0, 71 | }, 72 | ], 73 | }, 74 | msg: '', 75 | errorCode: '', 76 | status: 1, 77 | }; 78 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/roleManage/fetchRoleDetail.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | id: 2, 5 | roleName: '超级管理员', 6 | sort: 0, 7 | type: 0, 8 | resourceIds: [ 9 | 10060, 10 | 10063, 11 | 10062, 12 | 10108, 13 | 10109, 14 | 10110, 15 | ], 16 | }, 17 | msg: '操作成功', 18 | errorCode: '', 19 | status: 1, 20 | } 21 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/roleManage/fetchTreeList.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | list: [ 4 | { 5 | id: 10060, 6 | resName: '工作台', 7 | children: [ 8 | { 9 | id: 10063, 10 | resName: '概览', 11 | resKey: 'desk$/index', 12 | resIcon: 'cleanAccess', 13 | parentId: 10060, 14 | }, 15 | ], 16 | resKey: 'desk$', 17 | resIcon: 'home', 18 | }, 19 | { 20 | id: 10062, 21 | resName: '设置中心', 22 | children: [ 23 | { 24 | id: 10108, 25 | resName: '用户管理', 26 | resKey: 'set$/userManage', 27 | resIcon: 'userManage', 28 | parentId: 10062, 29 | buttons: [ 30 | { 31 | id: 432, 32 | resName: '上传照片', 33 | resType: 3, 34 | parentId: 427, 35 | resKey: 'upload', 36 | status: 0, 37 | }, 38 | { 39 | id: 433, 40 | resName: '查看照片', 41 | resType: 3, 42 | parentId: 427, 43 | resKey: 'view', 44 | status: 0, 45 | }, 46 | { 47 | id: 434, 48 | resName: '删除照片', 49 | resType: 3, 50 | parentId: 427, 51 | resKey: 'delete', 52 | status: 0, 53 | }, 54 | ], 55 | }, 56 | { 57 | id: 10109, 58 | resName: '角色管理', 59 | resKey: 'set$/roleManage', 60 | resIcon: 'roleManage', 61 | parentId: 10062, 62 | }, 63 | { 64 | id: 10110, 65 | resName: '权限管理', 66 | resKey: 'set$/moduleManage', 67 | resIcon: 'unitCount', 68 | parentId: 10062, 69 | }, 70 | ], 71 | resKey: 'set$', 72 | resIcon: 'set', 73 | }, 74 | ], 75 | }, 76 | msg: '', 77 | errorCode: '', 78 | status: 1, 79 | }; 80 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/roleManage/index.js: -------------------------------------------------------------------------------- 1 | const fetchRoleAdd = require('../../success') 2 | const fetchRoleUpdate = require('../../success') 3 | const fetchRoleDelete = require('../../success') 4 | const fetchUpdateRoleRes = require('../../success') 5 | const fetchTreeList = require('./fetchTreeList') 6 | const fetchModuleListInRole = require('./fetchModuleListInRole') 7 | const fetchRoleDetail = require('./fetchRoleDetail') 8 | const fetchButtonList = require('./fetchButtonList') 9 | const fetchRoleDeletePeople = require('../../success') 10 | const fetchUpdateButton = require('../../success') 11 | 12 | 13 | module.exports = { 14 | fetchRoleAdd, 15 | fetchRoleUpdate, 16 | fetchRoleDelete, 17 | fetchUpdateRoleRes, 18 | fetchTreeList, 19 | fetchModuleListInRole, 20 | fetchRoleDetail, 21 | fetchButtonList, 22 | fetchRoleDeletePeople, 23 | fetchUpdateButton, 24 | } 25 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/userManage/fetchRoleList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | list: [ 5 | { 6 | id: 1, 7 | roleName: '超级管理员', 8 | sort: 0, 9 | type: 0, 10 | resources: [], 11 | }, 12 | { 13 | id: 10080, 14 | roleName: '开发账号', 15 | sort: 2, 16 | resources: [], 17 | }, 18 | { 19 | id: 10060, 20 | roleName: '测试', 21 | sort: 3, 22 | resources: [], 23 | }, 24 | { 25 | id: 10100, 26 | roleName: '演示账号', 27 | sort: 3, 28 | resources: [], 29 | }, 30 | ], 31 | }, 32 | msg: '', 33 | errorCode: '', 34 | status: 1, 35 | } 36 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/userManage/fetchUserDepttList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | list: [ 5 | { 6 | id: '1', 7 | children: [ 8 | { 9 | id: '10416', 10 | parentCode: '370200000000', 11 | children: [ 12 | { 13 | id: '10541', 14 | parentCode: '370202000000', 15 | deptName: '文一路', 16 | deptCode: '370202140000', 17 | }, 18 | { 19 | id: '10401', 20 | parentCode: '370202000000', 21 | deptName: '文二路', 22 | deptCode: '370202150000', 23 | }, 24 | { 25 | id: '10398', 26 | parentCode: '370202000000', 27 | children: [ 28 | { 29 | id: '10628', 30 | parentCode: '370202230000', 31 | deptName: '文三路', 32 | deptCode: '370202230001', 33 | }, 34 | { 35 | id: '10629', 36 | parentCode: '370202230000', 37 | deptName: '文晖路', 38 | deptCode: '370202230002', 39 | }, 40 | ], 41 | deptName: '古翠路', 42 | deptCode: '370202230000', 43 | }, 44 | { 45 | id: '10537', 46 | parentCode: '370202000000', 47 | deptName: '丰潭路', 48 | deptCode: '370202240000', 49 | }, 50 | ], 51 | deptName: '下城区', 52 | deptCode: '370202000000', 53 | }, 54 | ], 55 | deptName: '杭州市', 56 | deptCode: '370200000000', 57 | }, 58 | ], 59 | }, 60 | msg: '', 61 | errorCode: '', 62 | status: 1, 63 | } 64 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/userManage/fetchUserDetail.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | id: 10240, 5 | username: 'tgramxs', 6 | password: '123456', 7 | chineseName: '销售部', 8 | idcardNo: '332527198010230505', 9 | deptCode: '370200000000', 10 | phoneNo: '18969784568', 11 | status: 0, 12 | roleIds: [ 13 | 10100, 14 | ], 15 | gxdwdm: '370200000000', 16 | }, 17 | msg: '', 18 | errorCode: '', 19 | status: 1, 20 | } 21 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/userManage/fetchUserList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | pageSize: 10, 5 | pageNo: 1, 6 | totalCount: 10, 7 | list: [ 8 | { 9 | id: 10240, 10 | username: 'tgramxs', 11 | password: '123456', 12 | chineseName: '销售部', 13 | idcardNo: '332527198010230505', 14 | deptCode: '370200000000', 15 | phoneNo: '18969784568', 16 | status: 0, 17 | roles: [ 18 | { 19 | id: 10100, 20 | roleName: '演示账号', 21 | resources: [], 22 | }, 23 | ], 24 | gxdwdm: '370200000000', 25 | }, 26 | { 27 | id: 10198, 28 | username: 'qingdaosj', 29 | password: '888888', 30 | chineseName: '市局', 31 | idcardNo: '412722196302151222', 32 | deptCode: '370200000000', 33 | phoneNo: '15236985623', 34 | status: 1, 35 | roles: [ 36 | { 37 | id: 1, 38 | roleName: '超级管理员', 39 | resources: [], 40 | }, 41 | ], 42 | gxdwdm: '370200000000', 43 | }, 44 | { 45 | id: 10184, 46 | username: 'sjsjsj', 47 | password: '888888', 48 | chineseName: '测试sj', 49 | idcardNo: '332623196801254521', 50 | deptCode: '370200000000', 51 | phoneNo: '15821456854', 52 | type: 0, 53 | status: 1, 54 | roles: [ 55 | { 56 | id: 1, 57 | roleName: '超级管理员', 58 | resources: [], 59 | }, 60 | ], 61 | gxdwdm: '370200000000', 62 | }, 63 | { 64 | id: 10180, 65 | username: 'sj666', 66 | password: '888888', 67 | chineseName: 'sj-admin', 68 | idcardNo: '612527199310150000', 69 | deptCode: '370200000000', 70 | phoneNo: '15522223333', 71 | type: 0, 72 | status: 0, 73 | roles: [ 74 | { 75 | id: 1, 76 | roleName: '超级管理员', 77 | resources: [], 78 | }, 79 | ], 80 | gxdwdm: '370200000000', 81 | }, 82 | { 83 | id: 10178, 84 | username: 'zhlsj1', 85 | password: '888888', 86 | chineseName: 'zhlsj1', 87 | idcardNo: '330881187609090044', 88 | deptCode: '370200000000', 89 | phoneNo: '13200000000', 90 | type: 0, 91 | status: 0, 92 | roles: [ 93 | { 94 | id: 1, 95 | roleName: '超级管理员', 96 | resources: [], 97 | }, 98 | ], 99 | gxdwdm: '370200000000', 100 | }, 101 | { 102 | id: 10177, 103 | username: 'zhlsj', 104 | password: '888888', 105 | chineseName: 'zhlsj', 106 | idcardNo: '330881187609090033', 107 | deptCode: '370200000000', 108 | phoneNo: '15600000000', 109 | type: 0, 110 | status: 0, 111 | roles: [ 112 | { 113 | id: 1, 114 | roleName: '超级管理员', 115 | resources: [], 116 | }, 117 | ], 118 | gxdwdm: '370200000000', 119 | }, 120 | { 121 | id: 2, 122 | username: '111111', 123 | password: '111111', 124 | chineseName: '管理员', 125 | idcardNo: '000000000000000001', 126 | policeCode: '000000', 127 | deptCode: '370200000000', 128 | gender: 1, 129 | email: 'abc@abc.com', 130 | phoneNo: '15100000000', 131 | type: 0, 132 | status: 0, 133 | gxdwdm: '370200000000', 134 | }, 135 | ], 136 | totalPage: 1, 137 | }, 138 | msg: '', 139 | errorCode: '', 140 | status: 1, 141 | } 142 | -------------------------------------------------------------------------------- /app/mocks/apis/sys/userManage/index.js: -------------------------------------------------------------------------------- 1 | const fetchUserDepttList = require('./fetchUserDepttList') 2 | const fetchRoleList = require('./fetchRoleList') 3 | const fetchUserList = require('./fetchUserList') 4 | const fetchUserDetail = require('./fetchUserDetail') 5 | const fetchUserDetailUpdate = require('../../success') 6 | const fetchUserAdd = require('../../success') 7 | const fetchUserDelete = require('../../success') 8 | const fetchUserSetRole = require('../../success') 9 | const fetchChangeUserStatus = require('../../success') 10 | const synUser = require('../../success') 11 | 12 | 13 | module.exports = { 14 | fetchUserDepttList, 15 | fetchRoleList, 16 | fetchUserList, 17 | fetchUserDetail, 18 | fetchUserDetailUpdate, 19 | fetchUserAdd, 20 | fetchUserDelete, 21 | fetchUserSetRole, 22 | fetchChangeUserStatus, 23 | synUser, 24 | } 25 | -------------------------------------------------------------------------------- /app/mocks/apis/tableList.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | data: { 4 | totalCount: 100, 5 | currentPage: 1, 6 | pageSize: 10, 7 | 'list|10': [ 8 | { 9 | 'id|+1': 1, 10 | 'status|1': [1, 2, 3], 11 | 'name|1': ['特朗普', '奥巴马', '老布什'], 12 | }, 13 | ], 14 | }, 15 | msg: '', 16 | errorCode: '', 17 | status: 1, 18 | } 19 | -------------------------------------------------------------------------------- /app/mocks/http.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const _map = require('./interfaceMap') 3 | const _filter = require('./interfaceFilter') 4 | const Mock = require('mockjs') 5 | 6 | http.createServer((req, res) => { 7 | res.writeHead(200, { 8 | 'Content-Type': 'application/json;charset=utf-8', 9 | 'Access-Control-Allow-Origin': req.headers.origin, 10 | 'Access-Control-Allow-Methods': '*', 11 | 'Access-Control-Allow-Headers': '*', 12 | 'Access-Control-Allow-Credentials': true, 13 | 'Cache-Control': 'no-cache,no-store', // clear cache 14 | }) 15 | if (req.method === 'OPTIONS') { 16 | res.end(null) 17 | } 18 | if (req.method === 'POST') { 19 | let postData = '' 20 | req.addListener('data', dataBuffer => postData += dataBuffer) 21 | req.addListener('end', () => { 22 | console.log('url=>', req.url) 23 | postData = JSON.parse(postData) 24 | const originData = _map[req.url] 25 | ? Mock.mock(_map[req.url]) 26 | : '' 27 | const data = typeof (_filter[req.url]) === 'function' 28 | ? _filter[req.url](originData, postData) 29 | : originData 30 | // const data = originData 31 | setTimeout(() => { 32 | res.end(JSON.stringify(data)) 33 | }, parseInt(((Math.random() - 0.5) + 1) * 500, 10)) // 随机数 34 | }) 35 | } 36 | }).listen(1111) 37 | console.log('listening port 1111') 38 | -------------------------------------------------------------------------------- /app/mocks/interfaceFilter.js: -------------------------------------------------------------------------------- 1 | const suffix = '.json' 2 | const prefix = '/kjdsj' 3 | 4 | module.exports = { 5 | [`${prefix}/dataManage/buildingAddress/fetchHouseCheckList${suffix}`]: function (mockData, request) { 6 | let jd = 120.000000000001 7 | let wd = 36.26404109130858 8 | mockData.data.list = mockData.data.list.map((one) => { 9 | jd += 0.001 10 | wd += 0.0000000001 11 | return Object.assign({}, one, { jd: String(jd), wd: String(wd), jwd: `${jd},${wd}` }) 12 | }) 13 | return mockData 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /app/mocks/interfaceMap.js: -------------------------------------------------------------------------------- 1 | 2 | const path = '/mock' 3 | 4 | // #region 5 | const base = require('./apis/base') // 基础的接口 6 | const userManage = require('./apis/sys/userManage') // 用户管理 7 | const roleManage = require('./apis/sys/roleManage') // 角色管理 8 | const moduleManage = require('./apis/sys/moduleManage') // 模块管理 9 | // #endregion 10 | 11 | module.exports = { 12 | // #region 收起所有 13 | 14 | // #region 公用 15 | [`${path}/usercenter/login`]: base.login, // 登录 16 | [`${path}/usercenter/user/userMenu`]: base.menu, // 菜单 17 | [`${path}/usercenter/user/userInfo`]: base.staff, // 用户信息 18 | [`${path}/usercenter/logout`]: base.logout, // 退出 19 | // #endregion 20 | 21 | // #region 用户管理 22 | [`${path}/usercenter/role/list`]: userManage.fetchRoleList, // 角色列表 23 | [`${path}/usercenter/dept/list`]: userManage.fetchUserDepttList, // 部门列表 24 | [`${path}/usercenter/user/list`]: userManage.fetchUserList, // 用户列表 25 | [`${path}/usercenter/user/detail`]: userManage.fetchUserDetail, // 获取用户详情 26 | [`${path}/usercenter/user/update`]: userManage.fetchUserDetailUpdate, // 修改用户详情 27 | [`${path}/usercenter/user/save`]: userManage.fetchUserAdd, // 新增用户 28 | [`${path}/usercenter/user/synUser`]: userManage.synUser, // 新增用户 29 | [`${path}/usercenter/user/updateRole`]: userManage.fetchUserSetRole, // 修改用户角色 30 | [`${path}/usercenter/user/delete`]: userManage.fetchUserDelete, // 删除用户 31 | [`${path}/usercenter/user/updateStatus`]: userManage.fetchChangeUserStatus, // 设置用户是否冻结状态 32 | // #endregion 33 | 34 | // #region 角色管理 35 | [`${path}/usercenter/role/save`]: roleManage.fetchRoleAdd, // 保存角色 36 | [`${path}/usercenter/role/delete`]: roleManage.fetchRoleDelete, // 删除角色 37 | [`${path}/usercenter/role/update`]: roleManage.fetchRoleUpdate, // 角色编辑 38 | [`${path}/usercenter/role/resTree`]: roleManage.fetchTreeList, // 角色列表 39 | [`${path}/usercenter/role/resList`]: roleManage.fetchModuleListInRole, // 已选择的模块 40 | [`${path}/usercenter/role/detail`]: roleManage.fetchRoleDetail, // 已选择的菜单以及按钮 41 | [`${path}/usercenter/resource/button/list`]: roleManage.fetchButtonList, // 模块的按钮列表 42 | [`${path}/usercenter/user/removeRole`]: roleManage.fetchRoleDeletePeople, // 删除用户 43 | [`${path}/usercenter/role/updateButton`]: roleManage.fetchUpdateButton, // 更新按钮 44 | [`${path}/usercenter/role/updateRes`]: roleManage.fetchUpdateRoleRes, // 更新已选择模块 45 | // #endregion 46 | 47 | // #region 模块管理 48 | [`${path}/usercenter/resource/list`]: moduleManage.fetchModuleList, // 获取模块列表 49 | [`${path}/usercenter/resource/delete`]: moduleManage.fetchModuleDelete, // 删除模块 50 | [`${path}/usercenter/resource/detail`]: moduleManage.fetchModuleDetail, // 获取模块详情 51 | [`${path}/usercenter/resource/updateStatus`]: moduleManage.fetchChangeModuleStatus, // 修改模块显隐状态 52 | [`${path}/usercenter/resource/update`]: moduleManage.fetchModuleUpdateDetail, // 修改模块详情 53 | [`${path}/usercenter/resource/save`]: moduleManage.fetchModuleAdd, // 新增模块 54 | // #endregion 55 | 56 | // #endregion 57 | } 58 | -------------------------------------------------------------------------------- /app/pages/base/app/modal/editPassword.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { connect } from 'react-redux' 3 | import { Button, Form, Input, /* Select, */ Modal, Row, Col, message } from 'antd' 4 | import { regExpConfig } from '@reg' 5 | import md5 from 'md5' 6 | import { 7 | fetchPassword, 8 | } from '@actions/common' 9 | 10 | const FormItem = Form.Item 11 | // const Option = Select.Option 12 | 13 | // 连接公用常量、后端返回的数据方法 并放置在props里面调用 14 | @connect((state, props) => ({ 15 | config: state.config, 16 | })) 17 | 18 | @Form.create({}) 19 | 20 | export default class index extends Component { 21 | constructor(props) { 22 | super(props) 23 | this.state = { 24 | confirmDirty: false, 25 | } 26 | this.handleSubmit = this.handleSubmit.bind(this) 27 | this.checkConfirm = this.checkConfirm.bind(this) 28 | this.checkPassword = this.checkPassword.bind(this) 29 | } 30 | 31 | 32 | // 组件已经加载到dom中 33 | componentDidMount() { 34 | 35 | } 36 | 37 | // 提交表单数据 38 | handleSubmit(e) { 39 | e.preventDefault() 40 | this.props.form.validateFields((errors, fieldsValue) => { 41 | if (errors) { 42 | return 43 | } 44 | 45 | if (fieldsValue.password) { 46 | // if (process.env.NODE_ENV === 'production') { 47 | // fieldsValue.password = fieldsValue.password 48 | // } else { 49 | fieldsValue.password = md5(fieldsValue.password) 50 | // } 51 | } 52 | const values = { 53 | oldPwd: fieldsValue.oldPwd ? fieldsValue.oldPwd : '', 54 | password: fieldsValue.password ? fieldsValue.password : '', 55 | }; 56 | this.submitLoading = true 57 | this.props.dispatch(fetchPassword({ 58 | ...values, 59 | }, (res) => { 60 | message.success(res.msg) 61 | this.submitLoading = false 62 | this.setState({}) 63 | this.props.onCancel() 64 | }, (res) => { 65 | message.warning(res.msg) 66 | this.props.form.setFields({ oldPwd: '', password: '', confirm: '' }) 67 | this.submitLoading = false 68 | this.setState({}) 69 | })) 70 | 71 | // this.props.form.resetFields() 72 | }); 73 | } 74 | 75 | checkPassword = (rule, value, callback) => { 76 | const { form } = this.props 77 | if (value && value !== form.getFieldValue('password')) { 78 | callback('两次输入的密码不一致') 79 | } else { 80 | callback() 81 | } 82 | } 83 | 84 | checkConfirm = (rule, value, callback) => { 85 | const { form } = this.props 86 | if (value && this.state.confirmDirty) { 87 | form.validateFields(['confirm'], { force: true }) 88 | } 89 | callback() 90 | } 91 | 92 | // 弹窗的footer 93 | renderFooter() { 94 | return ( 95 |
96 | 97 | 98 |
99 | ) 100 | } 101 | 102 | render() { 103 | // const { imageUrl } = this.state 104 | const { getFieldDecorator } = this.props.form 105 | const formItemLayout = { 106 | labelCol: { span: 6 }, 107 | wrapperCol: { span: 12 }, 108 | hasFeedback: true, 109 | } 110 | return ( 111 | 118 |
119 |
120 | 121 |
122 | 123 | {getFieldDecorator('oldPwd', { 124 | rules: [ 125 | { required: true, message: '请输入密码!' }, 126 | { pattern: regExpConfig.pwd, message: '请输入6-16位数字或者字母!' }, 127 | { validator: this.checkConfirm }, 128 | ], 129 | // validateTrigger: 'onBlur', 130 | })()} 131 | 132 | 133 | 134 | 135 | 136 | 137 | {getFieldDecorator('password', { 138 | rules: [ 139 | { required: true, message: '请输入密码!' }, 140 | { pattern: regExpConfig.pwd, message: '请输入6-16位数字或者字母!' }, 141 | { validator: this.checkConfirm }, 142 | ], 143 | // validateTrigger: 'onBlur', 144 | })()} 145 | 146 | 147 | 148 | 149 | 150 | 151 | {getFieldDecorator('confirm', { 152 | rules: [ 153 | { required: true, message: '请输入密码!' }, 154 | { pattern: regExpConfig.pwd, message: '请输入6-16位数字或者字母!' }, 155 | { validator: this.checkPassword }, 156 | ], 157 | // validateTrigger: 'onBlur', 158 | })()} 159 | 160 | 161 | 162 | 163 | 164 | 165 | ) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/pages/base/app/tabList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bindActionCreators } from 'redux' 3 | import { connect } from 'react-redux' 4 | import { routerActions } from 'react-router-redux' 5 | import { is } from 'immutable' 6 | import { Tabs } from 'antd' 7 | import { updateTabChecked, deleteTabFromList } from '@actions/tabList' 8 | 9 | const { TabPane } = Tabs 10 | 11 | @connect( 12 | (state, props) => ({ tabList: state.tabListResult }), 13 | dispatch => ({ 14 | actions: bindActionCreators(routerActions, dispatch), 15 | dispatch: dispatch, 16 | }), 17 | ) 18 | export default class TabList extends Component { 19 | constructor(props) { 20 | super(props) 21 | this.onChange = this.onChange.bind(this); 22 | this.onEdit = this.onEdit.bind(this); 23 | } 24 | componentDidMount() { 25 | // console.log('this.props', this.props); 26 | } 27 | onChange(activeKey) { 28 | const { actions } = this.props; 29 | this.props.dispatch(updateTabChecked({ activeKey: activeKey })) 30 | actions.push(activeKey) 31 | } 32 | onEdit(targetKey, action) { 33 | this[action](targetKey); 34 | } 35 | remove(targetKey) { 36 | const { actions, tabList } = this.props; 37 | let delIndex 38 | let activeKey 39 | 40 | if (targetKey === tabList.activeKey) { 41 | tabList.list.map((tab, index) => { 42 | // eslint-disable-next-line 43 | tab.key === targetKey ? delIndex = index : null; 44 | }); 45 | // eslint-disable-next-line no-nested-ternary 46 | activeKey = tabList.list[delIndex + 1] ? 47 | tabList.list[delIndex + 1].key : (tabList.list[delIndex - 1] ? 48 | tabList.list[delIndex - 1].key : ''); 49 | actions.push(activeKey); 50 | } 51 | this.props.dispatch(deleteTabFromList({ targetKey: targetKey })); 52 | } 53 | shouldComponentUpdate(nextProps, nextState) { 54 | const thisProps = this.props || {}; 55 | 56 | if (Object.keys(thisProps).length !== Object.keys(nextProps).length) { 57 | return true; 58 | } 59 | // eslint-disable-next-line no-restricted-syntax 60 | for (const key in nextProps) { 61 | if (thisProps[key] !== nextProps[key] || !is(thisProps[key], nextProps[key])) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | render() { 68 | const { tabList } = this.props 69 | return ( 70 | 77 | { 78 | tabList.list.map(tab => 79 | {tab.content}) 80 | } 81 | 82 | ) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/pages/base/developing.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Progress } from 'antd' 3 | 4 | // 声明组件 并对外输出 5 | export default class developing extends Component { 6 | // 初始化页面常量 绑定事件方法 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | // activeTab: 'pop' , 11 | } 12 | } 13 | 14 | 15 | // 组件已经加载到dom中 16 | componentDidMount() { 17 | 18 | } 19 | 20 | 21 | render() { 22 | return ( 23 |
24 | '即将上线,敬请期待...'} 28 | width={200} 29 | status="active" 30 | /> 31 |
32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/pages/base/example.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | // import PropTypes from 'prop-types' 3 | import { Button } from 'antd' 4 | // import {connect} from 'react-redux' 5 | // import {} from '@actions/xxx' 6 | // import Socket from '@configs/socket' 7 | 8 | // @connect((storeState)=>({})) 9 | 10 | export default class app extends Component { 11 | static defaultProps = { 12 | } 13 | 14 | static propTypes = { 15 | } 16 | 17 | constructor(props) { 18 | super(props) 19 | this.state = {} 20 | } 21 | 22 | componentDidMount() { } 23 | 24 | // #region vscode 1.17的收缩代码块功能 业务代码 25 | 26 | 27 | // #endregion 28 | 29 | // 发送socket数据 30 | onClickSend = () => { 31 | // Socket.send({ type: 'receive/hello3', data: { name: 'dupi' } }) 32 | } 33 | 34 | render() { 35 | return ( 36 |
37 | 示范页面 38 |
39 | 40 |
41 |
42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/pages/base/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import developing from './developing' 4 | import example from './example' 5 | import login from './login' 6 | import notfound from './notfound' 7 | import app from './app' 8 | // import socketReceive from './socketReceive' 9 | 10 | export { developing, example, /* socketReceive, */ login, notfound, app } 11 | -------------------------------------------------------------------------------- /app/pages/base/notfound.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Link, hashHistory } from 'react-router' 3 | import { Progress, Button } from 'antd' 4 | 5 | // 声明组件 并对外输出 6 | export default class notfound extends Component { 7 | // 初始化页面常量 绑定事件方法 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | // activeTab: 'pop' , 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 | '404'} 22 | width={200} 23 | status="active" 24 | /> 25 | 26 |
27 |

跳转至首页

28 |

跳转至登陆页

29 | 30 |
31 |
32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/pages/base/socket.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hy 3 | * @Date: 2019-05-24 14:46:23 4 | * @Last Modified by: dupi 5 | * @Last Modified time: 2019-09-19 14:19:56 6 | */ 7 | 8 | // socket on 9 | 10 | import React, { Component } from 'react' 11 | import socket from '@configs/socket' 12 | import { socketReceive } from '@actions/common' 13 | import { connect } from 'react-redux' 14 | 15 | @connect(() => ({})) 16 | export default class SocketOn extends Component { 17 | componentDidMount() { 18 | console.log('socket didmount') 19 | this.init() 20 | } 21 | 22 | init = () => { 23 | const callback = (res) => { 24 | this.props.dispatch(socketReceive(res)) 25 | }; 26 | socket.on('dispatch', callback); 27 | } 28 | 29 | render() { 30 | return null 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/pages/base/socketReceive.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | // import PropTypes from 'prop-types' 3 | import { Button } from 'antd' 4 | import { connect } from 'react-redux' 5 | // import {} from '@actions/xxx' 6 | // import Socket from '@configs/socket' 7 | 8 | @connect(store => ({ 9 | // socketCollection: store.socketCollection, 10 | })) 11 | 12 | export default class app extends Component { 13 | static defaultProps = { 14 | } 15 | 16 | static propTypes = { 17 | } 18 | 19 | constructor(props) { 20 | super(props) 21 | this.state = {} 22 | } 23 | 24 | componentDidMount() { } 25 | 26 | // #region vscode 1.17的收缩代码块功能 业务代码 27 | 28 | 29 | // #endregion 30 | 31 | // 发送socket数据 32 | onClickSend = () => { 33 | // Socket.dispatch({ type: 'receive/hello2' }) 34 | } 35 | 36 | render() { 37 | // const { socketCollection = {} } = this.props 38 | return ( 39 |
40 | socket receive 页面示例 41 |
42 |
收到数据:
43 |
44 |             
45 |               {
46 |                 // JSON.stringify(socketCollection, null, 2)
47 |               }
48 |             
49 |           
50 |
51 |
52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/pages/example.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | // import PropTypes from 'prop-types' 3 | import { } from 'antd' 4 | // import {connect} from 'react-redux' 5 | // import {} from '@actions/xxx' 6 | 7 | // @connect((storeState)=>({})) 8 | 9 | export default class app extends Component { 10 | static defaultProps={ 11 | } 12 | 13 | static propTypes = { 14 | } 15 | 16 | constructor(props) { 17 | super(props) 18 | this.state = {} 19 | } 20 | 21 | componentDidMount() {} 22 | 23 | // #region vscode 1.17的收缩代码块功能 业务代码 24 | 25 | 26 | // #endregion 27 | 28 | render() { 29 | return ( 30 |
31 | 示范页面excample 32 |
33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/pages/menu/index.js: -------------------------------------------------------------------------------- 1 | 2 | import echarts from './echarts' 3 | import editor from './editor' 4 | 5 | export { 6 | echarts, editor, 7 | } 8 | -------------------------------------------------------------------------------- /app/pages/set/index.js: -------------------------------------------------------------------------------- 1 | 2 | import '@styles/set.less' 3 | import userManage from './userManage' 4 | import roleManage from './roleManage' 5 | import moduleManage from './moduleManage' 6 | 7 | export { userManage, roleManage, moduleManage } 8 | -------------------------------------------------------------------------------- /app/pages/set/moduleManage/modal/addButtonModal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Button, Form, Input } from 'antd'; 3 | import { connect } from 'react-redux'; 4 | import { regExpConfig } from '@reg'; 5 | import Drawer from '@components/draw/draw'; 6 | 7 | const FormItem = Form.Item; 8 | 9 | 10 | // @connect((state, props) => ({ 11 | // config: state.config 12 | // })) 13 | @Form.create() 14 | 15 | // 声明组件 并对外输出 16 | export default class Pop extends Component { 17 | // 初始化页面常量 绑定事件方法 18 | constructor(props) { 19 | super(props); 20 | this.state = {}; 21 | this.handleSubmit = this.handleSubmit.bind(this); 22 | } 23 | 24 | componentWillMount() { 25 | this.props.form.resetFields(); 26 | } 27 | 28 | // 组件已经加载到dom中 29 | componentDidMount() {} 30 | 31 | handleSubmit(e) { 32 | e.preventDefault(); 33 | this.props.form.validateFields((errors, values) => { 34 | if (errors) { 35 | return; 36 | } 37 | values.resType = 3; 38 | if (this.props.state === 'edit') { 39 | values.id = this.props.buttonEditData.id; 40 | } 41 | this.props.handleAdd(values); 42 | }); 43 | } 44 | 45 | footer() { 46 | const { onCancel } = this.props; 47 | return ( 48 |
49 | 52 | 53 |
54 | ); 55 | } 56 | 57 | render() { 58 | const { visible, onCancel, title, buttonEditData } = this.props; 59 | const { getFieldDecorator } = this.props.form; 60 | const formItemLayout = { 61 | labelCol: { span: 5 }, 62 | wrapperCol: { span: 17 }, 63 | }; 64 | return ( 65 | 72 |
73 |
78 | {/* 79 | {getFieldDecorator('parentId', { 80 | initialValue: this.props.pid || '', 81 | })( 82 | 83 | )} 84 | */} 85 | 86 | {getFieldDecorator('resName', { 87 | rules: [{ required: true, message: '请输入按钮名称' }], 88 | initialValue: buttonEditData.resName || '', 89 | })()} 90 | 91 | 92 | {getFieldDecorator('sort', { 93 | rules: [ 94 | { required: true, message: '请输入排序数字' }, 95 | { pattern: regExpConfig.num, message: '请输入数字' }, 96 | ], 97 | initialValue: `${buttonEditData.sort || '0'}`, 98 | })()} 99 | 100 | {/* 101 | {getFieldDecorator('resModule', { 102 | rules: [ 103 | { required: true, message: '请输入模块名称' }, 104 | ], 105 | initialValue: buttonEditData.resModule || '', 106 | })( 107 | 108 | )} 109 | */} 110 | 111 | {getFieldDecorator('resKey', { 112 | rules: [{ required: true, message: '请输入关键字' }], 113 | initialValue: `${buttonEditData.resKey || ''}`, 114 | })()} 115 | 116 | {/* 117 | {getFieldDecorator('resIcon', { 118 | rules: [ 119 | { 120 | required: true, message: '请输入图标名称', 121 | }, 122 | { pattern: regExpConfig.isNumAlpha, message: '图标名称格式不正确' }, 123 | ], 124 | initialValue: `${this.props.values.resIcon || ''}`, 125 | })( 126 | 127 | )} 128 | 129 | 130 | {getFieldDecorator('resType', { 131 | rules: [ 132 | { required: true, message: '请选择类型' }, 133 | ], 134 | initialValue: `${this.props.values.resType || ''}`, 135 | })( 136 | 141 | )} 142 | */} 143 | 144 |
145 |
146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/pages/set/moduleManage/modal/buttonModal.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import { connect } from 'react-redux' 4 | import { Button, message, Badge, Popconfirm } from 'antd' 5 | import TableList from '@tableList' 6 | import Drawer from '@components/draw/draw' 7 | import { 8 | // fetchButtonList, 9 | fetchModuleDelete, 10 | fetchChangeModuleStatus, 11 | } from '@apis/manage' 12 | 13 | // 连接公用常量、后端返回的数据方法 并放置在props里面调用 14 | // @connect((state, props) => ({ 15 | // config: state.config, 16 | // })) 17 | 18 | // 声明组件 并对外输出 19 | export default class pop extends Component { 20 | // 初始化页面常量 绑定事件方法 21 | constructor(props) { 22 | super(props) 23 | this.state = { 24 | // selectedRowKeys: [], 25 | // loading: false, 26 | // dataSource: [], 27 | } 28 | this.deleteButton = this.deleteButton.bind(this) 29 | } 30 | 31 | componentWillMount() { 32 | // this.getList() 33 | } 34 | 35 | // 组件已经加载到dom中 36 | componentDidMount() {} 37 | 38 | componentWillReceiveProps(nextProps) { 39 | // this.getList() 40 | } 41 | 42 | // 删除 43 | deleteButton = (id) => { 44 | fetchModuleDelete({ id: id }, (result) => { 45 | message.success(result.msg) 46 | this.props.updateList() 47 | }) 48 | } 49 | 50 | // 上线下线 51 | showOrHide=(id, val) => { 52 | fetchChangeModuleStatus({ id: id, status: val }, (result) => { 53 | this.props.updateList() 54 | }) 55 | } 56 | 57 | column() { 58 | const self = this 59 | const { editButton } = self.props 60 | const configArr = [ 61 | { 62 | title: '按钮名称', 63 | dataIndex: 'resName', 64 | key: 'resName', 65 | width: '40%', 66 | }, 67 | { 68 | title: '状态', 69 | dataIndex: 'status', 70 | key: 'status', 71 | width: '20%', 72 | render: (text, record, index) => { 73 | if (text === 1) { 74 | return 75 | } 76 | return 77 | }, 78 | }, 79 | { 80 | title: '操作', 81 | dataIndex: 'caozuo', 82 | key: 'caozuo', 83 | width: '40%', 84 | render: (text, record, index) => ( 85 | 86 | self.showOrHide(record.id, `${record.status}`)} 89 | > 90 | {record.status !== 1 ? '下线' : '上线'} 91 | 92 | 93 | editButton(record)}>修改 94 | 95 | self.deleteButton(record.id)}> 96 | 删除 97 | 98 | 99 | ), 100 | }, 101 | ] 102 | return configArr 103 | } 104 | 105 | footer() { 106 | const { addButton } = this.props 107 | return ( 108 |
109 | 110 |
111 | ) 112 | } 113 | 114 | render() { 115 | const { 116 | visible, cancelButton, listLoading, dataSource, 117 | } = this.props 118 | // const { dataSource } = this.state 119 | return ( 120 | 128 |
129 | 135 |
136 |
137 | ) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/pages/set/moduleManage/moduleList.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React, { Component } from 'react' 4 | import { Table, Popconfirm } from 'antd' 5 | 6 | export default class ModuleList extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = {} 10 | } 11 | 12 | renderColumn() { 13 | const { 14 | onDelete, onModify, onUpdataStatus, onAddNode, buttonList, 15 | } = this.props 16 | return [ 17 | { 18 | title: '功能', 19 | dataIndex: 'resName', 20 | width: '40%', 21 | key: 'resName', 22 | render: function (text, record, index) { 23 | return ( 24 | {text} 25 | ) 26 | }, 27 | }, 28 | { 29 | title: '操作', 30 | width: '40%', 31 | key: 'operation', 32 | render: (text, record, index) => ( 33 | 34 | onAddNode(record.id)}>新增 35 | 36 | onModify(record.id, record.parentid)}>修改 37 | { 38 | text.children && text.children.length > 0 ? 39 | null : 40 | 41 | 42 | onDelete(record.id)}> 43 | 删除 44 | 45 | 46 | } 47 | { 48 | record.resName !== '模块管理' ? ( 49 | 50 | 51 | onUpdataStatus(text.id, `${text.status ? 0 : 1}`)}> 52 | {text.status ? '显示模块' : '隐藏模块'} 53 | 54 | ) : null 55 | } 56 | 57 | buttonList(record.id, record.parentid)}>按钮权限 58 | 59 | ), 60 | }, 61 | { 62 | title: '状态', 63 | width: '20%', 64 | render: function (text, record, index) { 65 | return ( 66 | record.resName !== '模块管理' ? ( 67 | {record.status ? 未上线 : 已上线} 68 | ) : 已上线 69 | ) 70 | }, 71 | }, 72 | ] 73 | } 74 | 75 | render() { 76 | const { dataSource, loading/* , scroll */ } = this.props 77 | return ( 78 |
79 |
89 | 90 | ) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/pages/set/roleManage/modal/buttonModal.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import { Button, Spin, Row, Col } from 'antd' 4 | import Drawer from '@components/draw/draw' 5 | import { 6 | fetchButtonList, 7 | fetchChangeModuleStatus, 8 | } from '@apis/manage' 9 | 10 | // 声明组件 并对外输出 11 | export default class pop extends Component { 12 | // 初始化页面常量 绑定事件方法 13 | constructor(props) { 14 | super(props) 15 | this.state = { 16 | selectedRowKeys: [], 17 | loading: false, 18 | dataSource: [], 19 | } 20 | this.change = this.change.bind(this) 21 | this.saveChecked = this.saveChecked.bind(this) 22 | this.selectChecked = this.selectChecked.bind(this) 23 | } 24 | 25 | componentWillMount() { 26 | this.state.selectedRowKeys = this.props.checkedIdArr[this.props.itemId] || [] 27 | this.getList() 28 | } 29 | 30 | getList() { 31 | this.setState({ 32 | loading: true, 33 | }, () => { 34 | fetchButtonList({ id: this.props.itemId }, (result) => { 35 | const data = result.data.list 36 | const dataSource = [] 37 | data.map((item) => { 38 | if (item.status === 0) { 39 | const { selectedRowKeys } = this.state 40 | selectedRowKeys.map((key) => { 41 | if (item.id === key) { 42 | item.checked = true 43 | } 44 | }) 45 | dataSource.push(item) 46 | } 47 | }) 48 | this.setState({ 49 | loading: false, 50 | dataSource: dataSource, 51 | }) 52 | }) 53 | }) 54 | } 55 | 56 | // componentWillReceiveProps(nextProps) { 57 | // this.getList() 58 | // } 59 | 60 | // 上线下线 61 | // showOrHide(id, val) { 62 | // fetchChangeModuleStatus({ id: id, status: val }, (result) => { 63 | // this.getList() 64 | // }) 65 | // } 66 | 67 | selectChecked() { 68 | // const checkedArr = [] 69 | this.state.dataSource.map((item) => { 70 | item.checked = true 71 | // checkedArr.push(item.id) 72 | }) 73 | this.setState({}) 74 | // this.props.saveChecked(checkedArr) 75 | } 76 | 77 | saveChecked() { 78 | // const { selectedRowKeys } = this.state 79 | // if (selectedRowKeys.length === 0) { 80 | // message.info('请选择可供用户使用的按钮权限') 81 | // return 82 | // } 83 | const checkedArr = [] 84 | this.state.dataSource.map((item) => { 85 | if (item.checked) { 86 | checkedArr.push(item.id) 87 | } 88 | }) 89 | this.props.saveChecked(checkedArr) 90 | } 91 | 92 | footer() { 93 | const { cancelButton } = this.props 94 | return ( 95 |
96 | 97 | 98 | 99 |
100 | ) 101 | } 102 | 103 | change(id, index) { 104 | const data = this.state.dataSource[index] 105 | data.checked = !data.checked 106 | this.setState({}) 107 | } 108 | 109 | render() { 110 | const { visible, cancelButton, title } = this.props 111 | const { loading, dataSource } = this.state 112 | return ( 113 | 120 | 121 |
122 | 123 | {dataSource.map((arr, i) => 124 | (
125 | 132 | ))} 133 | 134 | 135 | 136 | 137 | ) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/pages/set/roleManage/modal/roleAdd.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React, { Component } from 'react' 4 | import { Button, Form, Input, /* Modal, */ Select, message } from 'antd' 5 | import { regExpConfig } from '@reg' 6 | import Drawer from '@components/draw/draw' 7 | import { 8 | fetchRoleAdd, 9 | fetchRoleUpdate, 10 | } from '@apis/manage' 11 | 12 | const FormItem = Form.Item 13 | const { Option } = Select 14 | 15 | 16 | @Form.create({}) 17 | 18 | export default class Index extends Component { 19 | constructor(props) { 20 | super(props) 21 | this.state = { 22 | loading: false, 23 | } 24 | this.handleSubmit = this.handleSubmit.bind(this) 25 | } 26 | 27 | componentDidMount() { 28 | this.props.form.resetFields() 29 | this.props.form.setFieldsValue({ 30 | roleName: this.props.value.roleName, 31 | sort: `${this.props.value.sort}`, 32 | tjFlag: this.props.value.tjFlag !== undefined ? String(this.props.value.tjFlag) : '1', 33 | }) 34 | } 35 | 36 | handleSubmit(e) { 37 | e.preventDefault(); 38 | this.props.form.validateFields((errors, values) => { 39 | if (errors) { 40 | return; 41 | } 42 | this.setState({ loading: true }) 43 | if (this.props.type === 'modify') { 44 | fetchRoleUpdate({ ...values, id: this.props.modifyId }, (res) => { 45 | message.success(res.msg) 46 | this.props.handleOk(false) 47 | }) 48 | } else { 49 | fetchRoleAdd(values, (res) => { 50 | message.success(res.msg) 51 | this.props.handleOk(false) 52 | }) 53 | } 54 | this.setState({ loading: false }) 55 | }); 56 | } 57 | 58 | footer() { 59 | const { loading } = this.state 60 | return ( 61 |
62 | 63 | 64 |
65 | ) 66 | } 67 | 68 | render() { 69 | const { 70 | visible, onCancel, title, 71 | } = this.props 72 | const { getFieldDecorator } = this.props.form 73 | const formItemLayout = { 74 | labelCol: { span: 5 }, 75 | wrapperCol: { span: 17 }, 76 | } 77 | return ( 78 | 85 |
86 |
87 | 88 | {getFieldDecorator('roleName', { 89 | rules: [ 90 | { required: true, message: '请输入角色名称' }, 91 | // { pattern: regExpConfig.isNormalEncode, message: '请输入非特殊字符' }, 92 | ], 93 | })()} 94 | 95 | 96 | {getFieldDecorator('sort', { 97 | rules: [ 98 | { required: true, message: '请输入排序数字' }, 99 | { pattern: regExpConfig.num, message: '请输入数字' }, 100 | ], 101 | })()} 102 | 103 | 104 | {getFieldDecorator('tjFlag', { 105 | initialValue: '1', 106 | rules: [ 107 | { required: true, message: '请选择是否统计' }, 108 | ], 109 | })()} 113 | 114 | 115 | 116 |
117 |
118 | ) 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /app/pages/set/roleManage/peopleTreeList.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import TableList from '@tableList' 4 | 5 | export default class app extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = {} 9 | } 10 | 11 | componentWillMount() {} 12 | 13 | componentDidMount() { 14 | } 15 | 16 | // #region 收缩业务代码功能 17 | 18 | columns() { 19 | return ( 20 | [{ 21 | title: '功能', 22 | dataIndex: 'resName', 23 | key: 'resName', 24 | width: '30%', 25 | }, { 26 | title: '已选模块', 27 | dataIndex: 'checkedArr', 28 | key: 'checkedArr', 29 | width: '40%', 30 | }] 31 | ) 32 | } 33 | 34 | // #endregion 35 | 36 | render() { 37 | const { 38 | dataSource, 39 | } = this.props 40 | return ( 41 |
42 | 49 |
50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/pages/set/roleManage/roleCheckbox.js: -------------------------------------------------------------------------------- 1 | import { Checkbox } from 'antd'; 2 | import React from 'react'; 3 | 4 | class RoleCheckbox extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | isChecked: false, 9 | }; 10 | this.onChange = this.onChange.bind(this); 11 | } 12 | 13 | componentWillMount() { 14 | this.setState({ 15 | isChecked: this.props.defaultChecked, 16 | }); 17 | } 18 | 19 | componentWillReceiveProps(nextProps) { 20 | if (this.props.defaultChecked !== nextProps.defaultChecked) { 21 | this.setState({ 22 | isChecked: nextProps.defaultChecked, 23 | }); 24 | } 25 | } 26 | 27 | // #region 收缩业务代码功能 28 | 29 | onChange(e) { 30 | const item = this.props.checkItem; 31 | this.setState({ 32 | isChecked: e.target.checked, 33 | }); 34 | this.props.onChecked(item, e.target.checked); 35 | } 36 | 37 | // #endregion 38 | 39 | render() { 40 | return ( 41 | 46 | {this.state.isChecked ? '已开通' : '未开通'} 47 | 48 | ); 49 | } 50 | } 51 | export default RoleCheckbox; 52 | -------------------------------------------------------------------------------- /app/pages/set/roleManage/roleList.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import { Icon, Popconfirm } from 'antd' 4 | 5 | export default class app extends Component { 6 | constructor(props) { 7 | super(props) 8 | this.state = { 9 | currentId: '', 10 | } 11 | } 12 | 13 | componentWillMount() { 14 | if (this.props.roles.length > 0) { 15 | this.setState({ 16 | currentId: this.props.roles[0].id || 0, 17 | }) 18 | } 19 | } 20 | 21 | componentDidMount() { 22 | 23 | } 24 | 25 | componentWillReceiveProps(nextProps) { 26 | if (this.state.currentId === '' && nextProps.roles.length > 0) { 27 | this.setState({ 28 | currentId: nextProps.roles[0].id || 0, 29 | }) 30 | } 31 | } 32 | 33 | // #region 收缩业务代码功能 34 | 35 | // 角色名点击 36 | roleNameClick = (roleid, roleType) => { 37 | this.setState({ currentId: roleid }) 38 | this.props.onCurrentIndex(roleid, roleType) 39 | } 40 | 41 | // 改变选中li的样式 42 | checkTitleId = id => 43 | // console.log(id) 44 | (id === this.state.currentId ? 'active cell-layout' : 'cell-layout') 45 | 46 | 47 | // 角色修改 48 | roleModify = (info) => { 49 | this.props.onRoleModify(info) 50 | } 51 | 52 | // 角色删除 53 | onDelete = (info) => { 54 | this.state.currentId = '' 55 | this.props.handleRoleDelete(info) 56 | } 57 | 58 | // render roleNodes 59 | renderRoleNodes = () => { 60 | const { roles, btnRights } = this.props 61 | return roles.map((item, index) => 62 | (
  • 63 | this.roleNameClick(item.id, item.type)}>{item.roleName} 64 | 65 | { 66 | btnRights.edit ? 67 | this.roleModify(item.id)} /> : null 68 | } 69 | { 70 | btnRights.deleteRole ? 71 | this.onDelete(item.id)}> 72 | 73 | 74 | : null 75 | } 76 | 77 |
  • )) 78 | } 79 | 80 | // #endregion 81 | 82 | render() { 83 | return ( 84 | 87 | ) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/pages/set/userManage/modal/selectRole.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import { Radio, Button, Modal, message } from 'antd' 4 | import { fetchUserSetRole } from '@apis/manage' 5 | 6 | const RadioGroup = Radio.Group; 7 | const RadioButton = Radio.Button; 8 | 9 | export default class roleSelect extends Component { 10 | constructor(props) { 11 | super(props) 12 | this.state = { 13 | loading: false, 14 | checkedValues: '', 15 | } 16 | this.handleSubmit = this.handleSubmit.bind(this) 17 | this.onChange = this.onChange.bind(this) 18 | } 19 | 20 | componentWillMount() { 21 | this.setState({ checkedValues: this.props.values.roleid }) 22 | } 23 | 24 | componentDidMount() { 25 | 26 | } 27 | 28 | onChange(e) { 29 | this.setState({ checkedValues: e.target.value }) 30 | } 31 | 32 | handleSubmit() { 33 | this.setState({ loading: true }) 34 | this.props.dispatch(fetchUserSetRole({ 35 | roleid: this.state.checkedValues, 36 | id: this.props.currPeopleId, 37 | }, (res) => { 38 | message.success(res.msg) 39 | this.setState({ loading: false }) 40 | this.props.handleOkRole() 41 | })) 42 | } 43 | 44 | footer() { 45 | return ( 46 |
    47 | 48 | 49 |
    50 | ) 51 | } 52 | 53 | render() { 54 | const { select, values, visible, onCancel } = this.props 55 | const selectNodes = select.map((item, index) => 56 | {item.name} ) 57 | return ( 58 | 65 |
    66 | 67 | {selectNodes} 68 | 69 |
    70 |
    71 | ) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/pages/set/userManage/treeList.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import { Tree } from 'antd' 4 | 5 | const { TreeNode } = Tree 6 | 7 | export default class TreeList extends Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | // expandedKeys: ['123'], 12 | defaultExpandedKeys: ['123'], 13 | deptCode: props.curDeptCode, 14 | } 15 | this.handleOnSelect = this.handleOnSelect.bind(this) 16 | } 17 | 18 | componentDidMount() { 19 | 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | if (nextProps.curDeptCode !== this.props.curDeptCode) { 24 | this.setState({ deptCode: nextProps.curDeptCode }) 25 | } 26 | } 27 | 28 | 29 | // 展开事件 30 | onExpand = (expandedKeys) => { 31 | this.setState({ expandedKeys }) 32 | } 33 | 34 | // 选中事件 35 | handleOnSelect(info, Nodes) { 36 | if (Nodes && Nodes.selectedNodes[0] && Nodes.selectedNodes[0].props && Nodes.selectedNodes[0].props.title) { 37 | const { title } = Nodes.selectedNodes[0].props 38 | this.props.onSelect(info, title) 39 | } else { 40 | this.props.onSelect() 41 | } 42 | } 43 | 44 | render() { 45 | const { trees } = this.props 46 | const loop = (data = []) => data.map((item) => { 47 | if (item.children && item.children.length) { 48 | return {loop(item.children)} 49 | } 50 | return 51 | }) 52 | const treeNodes = loop(trees) 53 | 54 | return ( 55 |
    56 | 62 | {treeNodes} 63 | 64 |
    65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/redux/actions/common.js: -------------------------------------------------------------------------------- 1 | 2 | import { createAction } from 'redux-actions' 3 | import * as common from '@apis/common' 4 | import { createAjaxAction } from '@configs/common' 5 | 6 | 7 | // login 登陆 8 | export const requestLogin = createAction('request login') 9 | export const recevieLogin = createAction('receive login') 10 | export const login = createAjaxAction(common.login, requestLogin, recevieLogin) 11 | 12 | // gFormCache gfor2.0m的缓存 13 | export const setGformCache2 = createAction('set gform cache2') 14 | export const clearGformCache2 = createAction('clear gform cache2') 15 | 16 | // socket receive 17 | // export const socketReceive = createAction('socketReceive') 18 | -------------------------------------------------------------------------------- /app/redux/actions/tabList.js: -------------------------------------------------------------------------------- 1 | import { createAction } from 'redux-actions' 2 | 3 | export const requestTabList = createAction('request tab list') 4 | export const updateTabList = createAction('update tab list') 5 | export const updateTabChecked = createAction('update tab checked') 6 | export const deleteTabFromList = createAction('delete tab from list'); 7 | -------------------------------------------------------------------------------- /app/redux/reducers/common.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from 'redux-actions' 2 | 3 | // 登陆返回结果 4 | const loginState = () => ({}) 5 | export const loginResponse = handleActions({ 6 | 'request login'(state, action) { 7 | return { ...state, loading: true } 8 | }, 9 | 'receive login'(state, action) { 10 | // eslint-disable-next-line no-unused-vars 11 | const { req, res } = action.payload 12 | return { data: res, loading: false } 13 | }, 14 | }, loginState()) 15 | 16 | 17 | // gForm2.0缓存 18 | const cache2 = () => ({}) 19 | export const gFormCache2 = handleActions({ 20 | 'set gform cache2'(state, action) { 21 | const { cacheKey, cacheContent } = action.payload 22 | if (cacheKey === undefined) { 23 | throw new Error('cacheKey不能是undefined') 24 | } 25 | if (cacheContent === undefined) { 26 | throw new Error('cacheContent不能是undefined') 27 | } 28 | state[cacheKey] = { ...state[cacheKey], ...cacheContent } 29 | return { ...state } 30 | }, 31 | 'clear gform cache2'(state, action) { 32 | return cache2() 33 | }, 34 | }, cache2()) 35 | 36 | 37 | // gForm2.0头部搜索类别 38 | const allRetrievalState = { 39 | list: [], 40 | } 41 | export const allRetrievalResult = handleActions({ 42 | 'request all retrieval'(state, action) { 43 | return { ...state, loading: true } 44 | }, 45 | 'receive all retrieval'(state, action) { 46 | // eslint-disable-next-line no-unused-vars 47 | const { req, res } = action.payload 48 | return { ...res.data, loading: false } 49 | }, 50 | }, allRetrievalState) 51 | 52 | 53 | // socket相关操作 54 | /* export const socketCollection = handleActions({ 55 | 'socketReceive'(state, action) { 56 | // eslint-disable-next-line no-unused-vars 57 | const data = action.payload 58 | return { data } 59 | }, 60 | }, {}) */ 61 | -------------------------------------------------------------------------------- /app/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { routerReducer as routing } from 'react-router-redux' 2 | import { combineReducers } from 'redux' 3 | 4 | import * as tabList from './tabList' 5 | import * as common from './common' 6 | 7 | const rootReducer = combineReducers({ 8 | routing, 9 | config: (state = {}) => state, 10 | ...tabList, 11 | ...common, 12 | }) 13 | 14 | export default rootReducer 15 | -------------------------------------------------------------------------------- /app/redux/reducers/tabList.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from 'redux-actions' 2 | 3 | const tabList = JSON.parse(sessionStorage.getItem('tabList')) 4 | 5 | const initialState = { 6 | list: tabList ? tabList.list : [], 7 | activeKey: tabList ? tabList.activeKey : '', 8 | } 9 | 10 | const tabListResult = handleActions({ 11 | 'request tab list'(state, action) { 12 | return { ...state, loading: false } 13 | }, 14 | 'update tab list'(state, action) { 15 | const data = action.payload 16 | const findList = state.list.find(tab => tab.key === data.key) 17 | const list = findList === undefined ? [...state.list, data] : state.list 18 | sessionStorage.setItem('tabList', JSON.stringify({ list, activeKey: data.key, loading: false })) 19 | return { list, activeKey: data.key, loading: false } 20 | }, 21 | 'update tab checked'(state, action) { 22 | const { activeKey } = action.payload; 23 | sessionStorage.setItem('tabList', JSON.stringify({ ...state, activeKey, loading: false })) 24 | return { ...state, activeKey, loading: false } 25 | }, 26 | 'delete tab from list'(state, action) { 27 | const { targetKey } = action.payload 28 | const list = [] 29 | let delIndex = 0 30 | let { activeKey } = state 31 | state.list.map((tab, index) => { 32 | tab.key === targetKey ? delIndex = index : list.push(tab) 33 | }) 34 | if (state.activeKey === targetKey) { 35 | // eslint-disable-next-line no-nested-ternary 36 | activeKey = list[delIndex] ? list[delIndex].key : 37 | (list[delIndex - 1] ? list[delIndex - 1].key : '') 38 | } 39 | sessionStorage.setItem('tabList', JSON.stringify({ list, activeKey, loading: false })) 40 | return { list, activeKey, loading: false } 41 | }, 42 | }, initialState) 43 | 44 | export { tabListResult as default } 45 | -------------------------------------------------------------------------------- /app/resource/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/resource/iconfont/iconfont.eot -------------------------------------------------------------------------------- /app/resource/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/resource/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /app/resource/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duxianwei520/react/314b0d6db29f6c6201a11f4851bc0a82c7b48fd6/app/resource/iconfont/iconfont.woff -------------------------------------------------------------------------------- /app/styles/RichEditor.less: -------------------------------------------------------------------------------- 1 | .flexcolumn{ flex:1; display: flex; flex-direction: column;} 2 | 3 | .RichEditor-root { .flexcolumn; 4 | background: #fff; 5 | border: 1px solid #ddd; 6 | font-family: 'Georgia', serif; 7 | font-size: 14px; 8 | padding: 16px; 9 | // overflow: auto; 10 | } 11 | 12 | .RichEditor-editor {.flexcolumn; 13 | border-top: 1px solid #ddd; 14 | cursor: text; 15 | font-size: 16px; 16 | margin-top: 10px; 17 | } 18 | 19 | .DraftEditor-root{.flexcolumn; overflow-y: auto; overflow-x: hidden;} 20 | 21 | .RichEditor-editor .public-DraftEditorPlaceholder-root, 22 | .RichEditor-editor .public-DraftEditor-content { 23 | margin: 0 -15px -15px; 24 | padding: 15px; 25 | } 26 | 27 | .RichEditor-editor .public-DraftEditor-content { 28 | min-height: 100px; 29 | } 30 | 31 | .RichEditor-hidePlaceholder .public-DraftEditorPlaceholder-root { 32 | display: none; 33 | } 34 | 35 | .RichEditor-editor .RichEditor-blockquote { 36 | border-left: 5px solid #eee; 37 | color: #666; 38 | font-family: 'Hoefler Text', 'Georgia', serif; 39 | font-style: italic; 40 | margin: 16px 0; 41 | padding: 10px 20px; 42 | } 43 | 44 | .RichEditor-editor .public-DraftStyleDefault-pre { 45 | background-color: rgba(0, 0, 0, 0.05); 46 | font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace; 47 | font-size: 16px; 48 | padding: 20px; 49 | } 50 | 51 | .RichEditor-controls { 52 | font-family: 'Helvetica', sans-serif; 53 | font-size: 14px; 54 | margin-bottom: 5px; 55 | user-select: none; 56 | } 57 | 58 | .RichEditor-styleButton { 59 | color: #999; 60 | cursor: pointer; 61 | margin-right: 16px; 62 | padding: 2px 0; 63 | display: inline-block; 64 | } 65 | 66 | .RichEditor-activeButton { 67 | color: #5890ff; 68 | } 69 | -------------------------------------------------------------------------------- /app/styles/login.less: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import './theme.less'; 4 | 5 | .flexcolumn{display: flex; flex:1; flex-direction: column;} 6 | 7 | .root{height: 100%; 8 | .login-container{.flexcolumn;height: 100%; /* background: url(../images/bg.jpg) no-repeat 0 0; */ background-size: cover; 9 | .extraLink{ position: absolute; z-index: 999; right: @space-big; top: @space-big; display: none; 10 | a{ color: #eee; margin-left: @space-big; font-size: 16px; 11 | &:hover{color: #fff;} 12 | } 13 | } 14 | .login-header{flex: 3;background: #2d333e;color: #fff;display: flex;align-items: center;justify-content: center;font-size: 36px;letter-spacing: 10px; position: relative; 15 | .slogan{ position: absolute; z-index: 1000; bottom: 40px; margin-top: -10px; width: 100%; left: 0; 16 | div{ width: 100%; text-align: center; } 17 | .title{ font-size: 50px; 18 | .en{ display: block; font-size: 20px;} 19 | } 20 | .logo{height: 54px;margin-right: 10px;vertical-align: middle;margin-bottom: 8px;} 21 | } 22 | } 23 | .login-main{flex:3;display: flex;flex-direction: column;justify-content: center; 24 | 25 | .ant-row{ 26 | .ant-col-8{ 27 | .ant-form-item{display: flex;flex-direction: column;align-items: center; 28 | .ant-form-item-control-wrapper{width: 60%; 29 | .ant-form-item-control {text-align: right; 30 | .ant-form-explain{text-align: left;} 31 | .ant-input-group-wrapper{width: 100%; 32 | .ant-input-group-addon{font-size: 16px;color:#000;} 33 | } 34 | .ant-btn{width: 100%;} 35 | >a{color:@text-color-bold;font-weight: bold;} 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | .login-footer{flex: 1;display: flex;align-items: center;justify-content: center;color: @text-color-secondary;flex-direction:column;font-size: 14px; display: none; 43 | a{align-self: flex-end;margin-right: 5%;color:@text-color-bold;font-weight: bold;} 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/styles/personalCenter.less: -------------------------------------------------------------------------------- 1 | @import './theme.less'; 2 | .changePswWrap{position: relative; 3 | .changePsw{margin: 0px auto;background: #42485B;border-radius: 2px;position: absolute;z-index: 2;width: 100%;left: 0;top: -57px;cursor: pointer; 4 | .changePsw_title{padding: 16px;font-size:16px;color: #c0c0c0;border-bottom: 1px solid #565861; 5 | } 6 | .changePsw_title+form{margin-top:26px; 7 | .ant-input{background-color: rgba(255,255,255,0.66);border-radius:2px;color: #4A4A4A; 8 | &:focus{box-shadow: none;} 9 | } 10 | } 11 | .changePsw_btngroup{text-align: right;padding-bottom: 16px; 12 | button{ 13 | margin-right:26px; 14 | } 15 | } 16 | .changePsw_btn{background: #42485B!important;border:none!important;} 17 | } 18 | } 19 | .changePsw_in{ 20 | background: #42485B; 21 | font-size: 16px; 22 | .enter{ 23 | cursor: pointer; 24 | color: #008fe3; 25 | font-style: normal; 26 | } 27 | } -------------------------------------------------------------------------------- /app/styles/set.less: -------------------------------------------------------------------------------- 1 | @import './theme.less'; 2 | .flexcolumn{display: flex; flex:1; flex-direction: column;} 3 | .page-usermanage{ 4 | .page-header{flex: initial;/* margin-top: @space-big */;/* margin-left:@space-big; */ } 5 | .page-content .ant-pagination { padding-bottom: @space-big * 2; right: @space-big * 2;} 6 | .page-content{flex: auto; 7 | .table-content{margin-top:0;} 8 | } 9 | .page-body{ border:@border-split; flex-direction: row;} 10 | .page-title{border-bottom: @border-split;} 11 | .page-body-main{.flexcolumn;} 12 | .ant-layout-sider-children{.flexcolumn; height: 100%; 13 | .ant-spin-nested-loading{.flexcolumn; 14 | .ant-spin-container{.flexcolumn; 15 | .page-title{border-right: @border-split;} 16 | .treeside{ .flexcolumn;margin-bottom: 0;background: @component-background; border-right: @border-split; padding:@space-big;overflow: auto; 17 | > div{.flexcolumn; } 18 | .ant-tree-switcher-noop{ display: none;} 19 | .ant-tree {max-width: 190px; 20 | li { margin: 0; position: relative; padding: 1px 0; 21 | .ant-tree-node-content-wrapper{ width: 100%; padding:7.5px 5px; margin-left: 2px; font-size: 13px; color: #4A4A4A; 22 | &:hover { background-color: rgba(90, 141, 239, 0.12); 23 | .right-icon { display: block; } 24 | } 25 | } 26 | .ant-tree-child-tree{ position: relative; } 27 | .ant-tree-child-tree li:after{ 28 | content: ''; position: absolute; left: -13px; top: -18px; bottom: 17px; border-left: 1px solid @border-color-split; 29 | } 30 | .ant-tree-child-tree li:before{ 31 | content: ''; position: absolute; left: -12px; top: 18px; width: 12px; border-top: 1px solid @border-color-split; 32 | } 33 | .ant-tree-switcher.ant-tree-switcher_close, 34 | .ant-tree-switcher.ant-tree-switcher_open { 35 | cursor: pointer; display: inline-block; width: 12px; height: 12px; text-align: center; background: #fff; z-index: 9; position: relative; 36 | } 37 | } 38 | .right-icon { float: right; margin-right: 20px; display: none; 39 | i { margin-left: @space-base; color: @blue; 40 | &:hover { color: @blue; } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | .ant-layout-content{.flexcolumn;} 49 | } 50 | 51 | // 角色管理 52 | .page-rolemanage{ 53 | .page-footer{margin: 0 -@space-big;} 54 | .roleModuleList{ 55 | tr{ 56 | td:first-child{ text-align: left; padding-left: @space-base;; 57 | span{ white-space: nowrap;} 58 | } 59 | } 60 | } 61 | .page-content{ 62 | .ant-spin-nested-loading{.flexcolumn; 63 | .ant-spin-container{.flexcolumn;} 64 | } 65 | } 66 | .ant-layout-content{.flexcolumn; 67 | .color-red{color:@red;} 68 | .color-green{color:@green;} 69 | .table-checkbox{display: inline-block;} 70 | } 71 | .ant-layout-sider-children{.flexcolumn; padding:@space-big; border-right: @border-split; 72 | .ant-input-group-addon{ cursor: pointer;} 73 | .ant-spin-nested-loading{.flexcolumn; 74 | .ant-spin-container{.flexcolumn; 75 | .treeside{ .flexcolumn;margin-bottom: 0;background: @component-background; border-right: 0; padding:0;overflow: auto; 76 | .ant-tree li{border: 0;} 77 | .roleslist {overflow-y: auto; overflow-x: hidden; 78 | .title{ padding-bottom: @space-base; display: block; color: @text-color-bold; font-size: @font-size-lg; border-bottom: @border-base; margin:@space-base 0 0 @space-sm;} 79 | li{ padding:@space-sm 0 @space-sm @space-base; text-align: left; position: relative; line-height: @line-height-normal; display: flex; 80 | a{ color: @text-color-bold;} 81 | .name{ display: block; flex:1} 82 | &:hover{ background: @hover-background; 83 | .icons{ 84 | display: inline-block; 85 | } 86 | } 87 | } 88 | .icons{display: none; top: @space-normal; right:@space-sm; white-space: nowrap; 89 | i{ padding: @space-sm; color: #fff; margin-right: @space-sm; color: @text-color; 90 | &:hover{color: @blue;} 91 | } 92 | } 93 | .active{background: @hover-background;} 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | // 模块管理 102 | .page-modulemanage{ 103 | .ant-table-body{ 104 | .ant-table-tbody{ 105 | tr{ 106 | td:first-child{ text-align: left; padding-left: 12%; 107 | span{ white-space: nowrap;} 108 | } 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /app/styles/theme.less: -------------------------------------------------------------------------------- 1 | // reset antd 2 | @input-height-base : 28px; 3 | @input-height-lg : 28px; 4 | @btn-height-base : 28px; 5 | @pagination-item-size : 28px; 6 | @font-size-base : 12px; 7 | // 全局常用颜色的定义 8 | @icon-url : "../resource/iconfont/iconfont"; 9 | @white : #fff; // 白色 10 | @green : #69b721;//绿色 11 | @red : #ff0000;//红色 12 | @blue : #5a8def;//蓝色 13 | @yellow : #eca42b;//黄色 14 | @gray : #b2b2b2;//灰色色 15 | @orange : #ee860b; // 橙色 16 | 17 | // 全局按钮的颜色 主要用来重置antd的主题 18 | @primary-color : #4da9ec; 19 | @info-color : @primary-color; 20 | @success-color : @green; 21 | @error-color : @red; 22 | @highlight-color : @red; 23 | @warning-color : @yellow; 24 | 25 | // 全局默认字体颜色、二级标题颜色、背景颜色、 26 | @body-background : #fff; // 默认背景白色 用来重置antd的默认样式 27 | @background-color-base : #f8f9fd; // 灰白背景的颜色 接近白色 28 | @background-color-content : #e7ebee; // 灰白背景的颜色 更加灰 29 | @background-color-hover : rgba(90,141,239,0.2); // 元素hover的背景颜色 30 | @background-color-active : rgba(90,141,239,0.2); // 元素active的背景颜色 31 | @component-background : #fff; // 常用组件背景颜色 主要是重置antd默认样式 32 | @title-background : #f6f8fc; // 常用标题的淡蓝色背景色 33 | @hover-background : rgba(90, 141, 239, 0.2); // 常用标题的淡蓝色背景色 34 | @navbar-background : #373D41; // 左侧导航背景颜色 35 | @nav-background : #42485B; // 左侧导航背景颜色 36 | @nav-item-background : #333745; // 左侧导航打开项背景色 37 | 38 | // 全局字体定义 39 | @font-family : 'Helvetica Neue For Number,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif'; 40 | @code-family : Consolas, Menlo, Courier, monospace; 41 | 42 | // 全局文字颜色 43 | @text-color-bold : #333; 44 | @text-color : #666; 45 | @text-color-secondary : #999; 46 | 47 | // 全局字体大小 48 | @font-size-base : 12px; 49 | @font-size-lg : 14px; 50 | @font-size-hg : 18px; 51 | 52 | // 全局圆角的定义 53 | @border-radius-base : 2px; 54 | @border-radius-sm : 2px; 55 | 56 | // 边框颜色以及圆角 57 | @border-color-base : #dfe4ea; // 常见灰色边框颜色的定义 58 | @border-color-split : #dfe4ea; // 常见淡蓝色边框颜色的定义 59 | @border-color-dark : #777C8D; // 常见淡蓝色边框颜色的定义 60 | @border-lightBlack : 1px solid #565c6d; // 常见弹框里的边框颜色 61 | @border-base : 1px solid @border-color-base; // 常见灰色边框线的简写方式 62 | @border-split : 1px solid @border-color-split; // 常见淡蓝色边框线的简写方式 63 | @border-split-dashed : 1px dashed @border-color-split; // 常见淡蓝色边框线的简写方式 64 | @border-dark : 1px solid @border-color-dark; // 常见淡蓝色边框线的简写方式 65 | 66 | 67 | // 全局常用行高的定义 68 | @line-height-sm : 28px; 69 | @line-height-normal : 30px; 70 | @line-height-base : 1.5; 71 | @line-height-big : 30px; 72 | @line-height-lg : 36px; 73 | @line-height-hg : 40px; 74 | 75 | // 全局所有的padding或者margin的常用间隔 76 | @space-sm : 4px; 77 | @space-base : 8px; 78 | @space-middle : 12px; 79 | @space-normal : 14px; 80 | @space-big : 16px; 81 | @space-lg : 24px; 82 | @space-hg : 32px; 83 | 84 | @scroll-width : 14px; // 全局滚动条的宽度 85 | 86 | // 全局结构宽度高度的定义 87 | @g-left-width-base : 142px; 88 | @g-left-width-mini : 50px; 89 | @g-middle-width : 280px; 90 | @g-header-height : 64px; 91 | @g-nav-height : 44px; 92 | 93 | @g-header-bg-color : #233344; 94 | @g-title-bg-color : #ebf5fd; 95 | @form-header-color : #fafdff; 96 | 97 | // 网站自定义的icon前缀 98 | @qqbicon-prefix : qqbicon; -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("cypress"); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/aliasing.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Aliasing', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/aliasing') 6 | }) 7 | 8 | it('.as() - alias a DOM element for later use', () => { 9 | // https://on.cypress.io/as 10 | 11 | // Alias a DOM element for use later 12 | // We don't have to traverse to the element 13 | // later in our code, we reference it with @ 14 | 15 | cy.get('.as-table').find('tbody>tr') 16 | .first().find('td').first() 17 | .find('button').as('firstBtn') 18 | 19 | // when we reference the alias, we place an 20 | // @ in front of its name 21 | cy.get('@firstBtn').click() 22 | 23 | cy.get('@firstBtn') 24 | .should('have.class', 'btn-success') 25 | .and('contain', 'Changed') 26 | }) 27 | 28 | it('.as() - alias a route for later use', () => { 29 | // Alias the route to wait for its response 30 | cy.intercept('GET', '**/comments/*').as('getComment') 31 | 32 | // we have code that gets a comment when 33 | // the button is clicked in scripts.js 34 | cy.get('.network-btn').click() 35 | 36 | // https://on.cypress.io/wait 37 | cy.wait('@getComment').its('response.statusCode').should('eq', 200) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/connectors.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Connectors', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/connectors') 6 | }) 7 | 8 | it('.each() - iterate over an array of elements', () => { 9 | // https://on.cypress.io/each 10 | cy.get('.connectors-each-ul>li') 11 | .each(($el, index, $list) => { 12 | console.log($el, index, $list) 13 | }) 14 | }) 15 | 16 | it('.its() - get properties on the current subject', () => { 17 | // https://on.cypress.io/its 18 | cy.get('.connectors-its-ul>li') 19 | // calls the 'length' property yielding that value 20 | .its('length') 21 | .should('be.gt', 2) 22 | }) 23 | 24 | it('.invoke() - invoke a function on the current subject', () => { 25 | // our div is hidden in our script.js 26 | // $('.connectors-div').hide() 27 | cy.get('.connectors-div').should('be.hidden') 28 | 29 | // https://on.cypress.io/invoke 30 | // call the jquery method 'show' on the 'div.container' 31 | cy.get('.connectors-div').invoke('show') 32 | 33 | cy.get('.connectors-div').should('be.visible') 34 | }) 35 | 36 | it('.spread() - spread an array as individual args to callback function', () => { 37 | // https://on.cypress.io/spread 38 | const arr = ['foo', 'bar', 'baz'] 39 | 40 | cy.wrap(arr).spread((foo, bar, baz) => { 41 | expect(foo).to.eq('foo') 42 | expect(bar).to.eq('bar') 43 | expect(baz).to.eq('baz') 44 | }) 45 | }) 46 | 47 | describe('.then()', () => { 48 | it('invokes a callback function with the current subject', () => { 49 | // https://on.cypress.io/then 50 | cy.get('.connectors-list > li') 51 | .then(($lis) => { 52 | expect($lis, '3 items').to.have.length(3) 53 | expect($lis.eq(0), 'first item').to.contain('Walk the dog') 54 | expect($lis.eq(1), 'second item').to.contain('Feed the cat') 55 | expect($lis.eq(2), 'third item').to.contain('Write JavaScript') 56 | }) 57 | }) 58 | 59 | it('yields the returned value to the next command', () => { 60 | cy.wrap(1) 61 | .then((num) => { 62 | expect(num).to.equal(1) 63 | 64 | return 2 65 | }) 66 | .then((num) => { 67 | expect(num).to.equal(2) 68 | }) 69 | }) 70 | 71 | it('yields the original subject without return', () => { 72 | cy.wrap(1) 73 | .then((num) => { 74 | expect(num).to.equal(1) 75 | // note that nothing is returned from this callback 76 | }) 77 | .then((num) => { 78 | // this callback receives the original unchanged value 1 79 | expect(num).to.equal(1) 80 | }) 81 | }) 82 | 83 | it('yields the value yielded by the last Cypress command inside', () => { 84 | cy.wrap(1) 85 | .then((num) => { 86 | expect(num).to.equal(1) 87 | // note how we run a Cypress command 88 | // the result yielded by this Cypress command 89 | // will be passed to the second ".then" 90 | cy.wrap(2) 91 | }) 92 | .then((num) => { 93 | // this callback receives the value yielded by "cy.wrap(2)" 94 | expect(num).to.equal(2) 95 | }) 96 | }) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/cookies.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Cookies', () => { 4 | beforeEach(() => { 5 | Cypress.Cookies.debug(true) 6 | 7 | cy.visit('https://example.cypress.io/commands/cookies') 8 | 9 | // clear cookies again after visiting to remove 10 | // any 3rd party cookies picked up such as cloudflare 11 | cy.clearCookies() 12 | }) 13 | 14 | it('cy.getCookie() - get a browser cookie', () => { 15 | // https://on.cypress.io/getcookie 16 | cy.get('#getCookie .set-a-cookie').click() 17 | 18 | // cy.getCookie() yields a cookie object 19 | cy.getCookie('token').should('have.property', 'value', '123ABC') 20 | }) 21 | 22 | it('cy.getCookies() - get browser cookies for the current domain', () => { 23 | // https://on.cypress.io/getcookies 24 | cy.getCookies().should('be.empty') 25 | 26 | cy.get('#getCookies .set-a-cookie').click() 27 | 28 | // cy.getCookies() yields an array of cookies 29 | cy.getCookies().should('have.length', 1).should((cookies) => { 30 | // each cookie has these properties 31 | expect(cookies[0]).to.have.property('name', 'token') 32 | expect(cookies[0]).to.have.property('value', '123ABC') 33 | expect(cookies[0]).to.have.property('httpOnly', false) 34 | expect(cookies[0]).to.have.property('secure', false) 35 | expect(cookies[0]).to.have.property('domain') 36 | expect(cookies[0]).to.have.property('path') 37 | }) 38 | }) 39 | 40 | it('cy.getAllCookies() - get all browser cookies', () => { 41 | // https://on.cypress.io/getallcookies 42 | cy.getAllCookies().should('be.empty') 43 | 44 | cy.setCookie('key', 'value') 45 | cy.setCookie('key', 'value', { domain: '.example.com' }) 46 | 47 | // cy.getAllCookies() yields an array of cookies 48 | cy.getAllCookies().should('have.length', 2).should((cookies) => { 49 | // each cookie has these properties 50 | expect(cookies[0]).to.have.property('name', 'key') 51 | expect(cookies[0]).to.have.property('value', 'value') 52 | expect(cookies[0]).to.have.property('httpOnly', false) 53 | expect(cookies[0]).to.have.property('secure', false) 54 | expect(cookies[0]).to.have.property('domain') 55 | expect(cookies[0]).to.have.property('path') 56 | 57 | expect(cookies[1]).to.have.property('name', 'key') 58 | expect(cookies[1]).to.have.property('value', 'value') 59 | expect(cookies[1]).to.have.property('httpOnly', false) 60 | expect(cookies[1]).to.have.property('secure', false) 61 | expect(cookies[1]).to.have.property('domain', '.example.com') 62 | expect(cookies[1]).to.have.property('path') 63 | }) 64 | }) 65 | 66 | it('cy.setCookie() - set a browser cookie', () => { 67 | // https://on.cypress.io/setcookie 68 | cy.getCookies().should('be.empty') 69 | 70 | cy.setCookie('foo', 'bar') 71 | 72 | // cy.getCookie() yields a cookie object 73 | cy.getCookie('foo').should('have.property', 'value', 'bar') 74 | }) 75 | 76 | it('cy.clearCookie() - clear a browser cookie', () => { 77 | // https://on.cypress.io/clearcookie 78 | cy.getCookie('token').should('be.null') 79 | 80 | cy.get('#clearCookie .set-a-cookie').click() 81 | 82 | cy.getCookie('token').should('have.property', 'value', '123ABC') 83 | 84 | // cy.clearCookies() yields null 85 | cy.clearCookie('token').should('be.null') 86 | 87 | cy.getCookie('token').should('be.null') 88 | }) 89 | 90 | it('cy.clearCookies() - clear browser cookies for the current domain', () => { 91 | // https://on.cypress.io/clearcookies 92 | cy.getCookies().should('be.empty') 93 | 94 | cy.get('#clearCookies .set-a-cookie').click() 95 | 96 | cy.getCookies().should('have.length', 1) 97 | 98 | // cy.clearCookies() yields null 99 | cy.clearCookies() 100 | 101 | cy.getCookies().should('be.empty') 102 | }) 103 | 104 | it('cy.clearAllCookies() - clear all browser cookies', () => { 105 | // https://on.cypress.io/clearallcookies 106 | cy.getAllCookies().should('be.empty') 107 | 108 | cy.setCookie('key', 'value') 109 | cy.setCookie('key', 'value', { domain: '.example.com' }) 110 | 111 | cy.getAllCookies().should('have.length', 2) 112 | 113 | // cy.clearAllCookies() yields null 114 | cy.clearAllCookies() 115 | 116 | cy.getAllCookies().should('be.empty') 117 | }) 118 | }) 119 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/files.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /// JSON fixture file can be loaded directly using 4 | // the built-in JavaScript bundler 5 | const requiredExample = require('../../fixtures/example') 6 | 7 | context('Files', () => { 8 | beforeEach(() => { 9 | cy.visit('https://example.cypress.io/commands/files') 10 | 11 | // load example.json fixture file and store 12 | // in the test context object 13 | cy.fixture('example.json').as('example') 14 | }) 15 | 16 | it('cy.fixture() - load a fixture', () => { 17 | // https://on.cypress.io/fixture 18 | 19 | // Instead of writing a response inline you can 20 | // use a fixture file's content. 21 | 22 | // when application makes an Ajax request matching "GET **/comments/*" 23 | // Cypress will intercept it and reply with the object in `example.json` fixture 24 | cy.intercept('GET', '**/comments/*', { fixture: 'example.json' }).as('getComment') 25 | 26 | // we have code that gets a comment when 27 | // the button is clicked in scripts.js 28 | cy.get('.fixture-btn').click() 29 | 30 | cy.wait('@getComment').its('response.body') 31 | .should('have.property', 'name') 32 | .and('include', 'Using fixtures to represent data') 33 | }) 34 | 35 | it('cy.fixture() or require - load a fixture', function () { 36 | // we are inside the "function () { ... }" 37 | // callback and can use test context object "this" 38 | // "this.example" was loaded in "beforeEach" function callback 39 | expect(this.example, 'fixture in the test context') 40 | .to.deep.equal(requiredExample) 41 | 42 | // or use "cy.wrap" and "should('deep.equal', ...)" assertion 43 | cy.wrap(this.example) 44 | .should('deep.equal', requiredExample) 45 | }) 46 | 47 | it('cy.readFile() - read file contents', () => { 48 | // https://on.cypress.io/readfile 49 | 50 | // You can read a file and yield its contents 51 | // The filePath is relative to your project's root. 52 | cy.readFile(Cypress.config('configFile')).then((config) => { 53 | expect(config).to.be.an('string') 54 | }) 55 | }) 56 | 57 | it('cy.writeFile() - write to a file', () => { 58 | // https://on.cypress.io/writefile 59 | 60 | // You can write to a file 61 | 62 | // Use a response from a request to automatically 63 | // generate a fixture file for use later 64 | cy.request('https://jsonplaceholder.cypress.io/users') 65 | .then((response) => { 66 | cy.writeFile('cypress/fixtures/users.json', response.body) 67 | }) 68 | 69 | cy.fixture('users').should((users) => { 70 | expect(users[0].name).to.exist 71 | }) 72 | 73 | // JavaScript arrays and objects are stringified 74 | // and formatted into text. 75 | cy.writeFile('cypress/fixtures/profile.json', { 76 | id: 8739, 77 | name: 'Jane', 78 | email: 'jane@example.com', 79 | }) 80 | 81 | cy.fixture('profile').should((profile) => { 82 | expect(profile.name).to.eq('Jane') 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/location.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Location', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/location') 6 | }) 7 | 8 | it('cy.hash() - get the current URL hash', () => { 9 | // https://on.cypress.io/hash 10 | cy.hash().should('be.empty') 11 | }) 12 | 13 | it('cy.location() - get window.location', () => { 14 | // https://on.cypress.io/location 15 | cy.location().should((location) => { 16 | expect(location.hash).to.be.empty 17 | expect(location.href).to.eq('https://example.cypress.io/commands/location') 18 | expect(location.host).to.eq('example.cypress.io') 19 | expect(location.hostname).to.eq('example.cypress.io') 20 | expect(location.origin).to.eq('https://example.cypress.io') 21 | expect(location.pathname).to.eq('/commands/location') 22 | expect(location.port).to.eq('') 23 | expect(location.protocol).to.eq('https:') 24 | expect(location.search).to.be.empty 25 | }) 26 | }) 27 | 28 | it('cy.url() - get the current URL', () => { 29 | // https://on.cypress.io/url 30 | cy.url().should('eq', 'https://example.cypress.io/commands/location') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/misc.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Misc', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/misc') 6 | }) 7 | 8 | it('.end() - end the command chain', () => { 9 | // https://on.cypress.io/end 10 | 11 | // cy.end is useful when you want to end a chain of commands 12 | // and force Cypress to re-query from the root element 13 | cy.get('.misc-table').within(() => { 14 | // ends the current chain and yields null 15 | cy.contains('Cheryl').click().end() 16 | 17 | // queries the entire table again 18 | cy.contains('Charles').click() 19 | }) 20 | }) 21 | 22 | it('cy.exec() - execute a system command', () => { 23 | // execute a system command. 24 | // so you can take actions necessary for 25 | // your test outside the scope of Cypress. 26 | // https://on.cypress.io/exec 27 | 28 | // we can use Cypress.platform string to 29 | // select appropriate command 30 | // https://on.cypress/io/platform 31 | cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) 32 | 33 | // on CircleCI Windows build machines we have a failure to run bash shell 34 | // https://github.com/cypress-io/cypress/issues/5169 35 | // so skip some of the tests by passing flag "--env circle=true" 36 | const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle') 37 | 38 | if (isCircleOnWindows) { 39 | cy.log('Skipping test on CircleCI') 40 | 41 | return 42 | } 43 | 44 | // cy.exec problem on Shippable CI 45 | // https://github.com/cypress-io/cypress/issues/6718 46 | const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable') 47 | 48 | if (isShippable) { 49 | cy.log('Skipping test on ShippableCI') 50 | 51 | return 52 | } 53 | 54 | cy.exec('echo Jane Lane') 55 | .its('stdout').should('contain', 'Jane Lane') 56 | 57 | if (Cypress.platform === 'win32') { 58 | cy.exec(`print ${Cypress.config('configFile')}`) 59 | .its('stderr').should('be.empty') 60 | } else { 61 | cy.exec(`cat ${Cypress.config('configFile')}`) 62 | .its('stderr').should('be.empty') 63 | 64 | cy.exec('pwd') 65 | .its('code').should('eq', 0) 66 | } 67 | }) 68 | 69 | it('cy.focused() - get the DOM element that has focus', () => { 70 | // https://on.cypress.io/focused 71 | cy.get('.misc-form').find('#name').click() 72 | cy.focused().should('have.id', 'name') 73 | 74 | cy.get('.misc-form').find('#description').click() 75 | cy.focused().should('have.id', 'description') 76 | }) 77 | 78 | context('Cypress.Screenshot', function () { 79 | it('cy.screenshot() - take a screenshot', () => { 80 | // https://on.cypress.io/screenshot 81 | cy.screenshot('my-image') 82 | }) 83 | 84 | it('Cypress.Screenshot.defaults() - change default config of screenshots', function () { 85 | Cypress.Screenshot.defaults({ 86 | blackout: ['.foo'], 87 | capture: 'viewport', 88 | clip: { x: 0, y: 0, width: 200, height: 200 }, 89 | scale: false, 90 | disableTimersAndAnimations: true, 91 | screenshotOnRunFailure: true, 92 | onBeforeScreenshot () { }, 93 | onAfterScreenshot () { }, 94 | }) 95 | }) 96 | }) 97 | 98 | it('cy.wrap() - wrap an object', () => { 99 | // https://on.cypress.io/wrap 100 | cy.wrap({ foo: 'bar' }) 101 | .should('have.property', 'foo') 102 | .and('include', 'bar') 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/navigation.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Navigation', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io') 6 | cy.get('.navbar-nav').contains('Commands').click() 7 | cy.get('.dropdown-menu').contains('Navigation').click() 8 | }) 9 | 10 | it('cy.go() - go back or forward in the browser\'s history', () => { 11 | // https://on.cypress.io/go 12 | 13 | cy.location('pathname').should('include', 'navigation') 14 | 15 | cy.go('back') 16 | cy.location('pathname').should('not.include', 'navigation') 17 | 18 | cy.go('forward') 19 | cy.location('pathname').should('include', 'navigation') 20 | 21 | // clicking back 22 | cy.go(-1) 23 | cy.location('pathname').should('not.include', 'navigation') 24 | 25 | // clicking forward 26 | cy.go(1) 27 | cy.location('pathname').should('include', 'navigation') 28 | }) 29 | 30 | it('cy.reload() - reload the page', () => { 31 | // https://on.cypress.io/reload 32 | cy.reload() 33 | 34 | // reload the page without using the cache 35 | cy.reload(true) 36 | }) 37 | 38 | it('cy.visit() - visit a remote url', () => { 39 | // https://on.cypress.io/visit 40 | 41 | // Visit any sub-domain of your current domain 42 | 43 | // Pass options to the visit 44 | cy.visit('https://example.cypress.io/commands/navigation', { 45 | timeout: 50000, // increase total time for the visit to resolve 46 | onBeforeLoad (contentWindow) { 47 | // contentWindow is the remote page's window object 48 | expect(typeof contentWindow === 'object').to.be.true 49 | }, 50 | onLoad (contentWindow) { 51 | // contentWindow is the remote page's window object 52 | expect(typeof contentWindow === 'object').to.be.true 53 | }, 54 | }) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/querying.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Querying', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/querying') 6 | }) 7 | 8 | // The most commonly used query is 'cy.get()', you can 9 | // think of this like the '$' in jQuery 10 | 11 | it('cy.get() - query DOM elements', () => { 12 | // https://on.cypress.io/get 13 | 14 | cy.get('#query-btn').should('contain', 'Button') 15 | 16 | cy.get('.query-btn').should('contain', 'Button') 17 | 18 | cy.get('#querying .well>button:first').should('contain', 'Button') 19 | // ↲ 20 | // Use CSS selectors just like jQuery 21 | 22 | cy.get('[data-test-id="test-example"]').should('have.class', 'example') 23 | 24 | // 'cy.get()' yields jQuery object, you can get its attribute 25 | // by invoking `.attr()` method 26 | cy.get('[data-test-id="test-example"]') 27 | .invoke('attr', 'data-test-id') 28 | .should('equal', 'test-example') 29 | 30 | // or you can get element's CSS property 31 | cy.get('[data-test-id="test-example"]') 32 | .invoke('css', 'position') 33 | .should('equal', 'static') 34 | 35 | // or use assertions directly during 'cy.get()' 36 | // https://on.cypress.io/assertions 37 | cy.get('[data-test-id="test-example"]') 38 | .should('have.attr', 'data-test-id', 'test-example') 39 | .and('have.css', 'position', 'static') 40 | }) 41 | 42 | it('cy.contains() - query DOM elements with matching content', () => { 43 | // https://on.cypress.io/contains 44 | cy.get('.query-list') 45 | .contains('bananas') 46 | .should('have.class', 'third') 47 | 48 | // we can pass a regexp to `.contains()` 49 | cy.get('.query-list') 50 | .contains(/^b\w+/) 51 | .should('have.class', 'third') 52 | 53 | cy.get('.query-list') 54 | .contains('apples') 55 | .should('have.class', 'first') 56 | 57 | // passing a selector to contains will 58 | // yield the selector containing the text 59 | cy.get('#querying') 60 | .contains('ul', 'oranges') 61 | .should('have.class', 'query-list') 62 | 63 | cy.get('.query-button') 64 | .contains('Save Form') 65 | .should('have.class', 'btn') 66 | }) 67 | 68 | it('.within() - query DOM elements within a specific element', () => { 69 | // https://on.cypress.io/within 70 | cy.get('.query-form').within(() => { 71 | cy.get('input:first').should('have.attr', 'placeholder', 'Email') 72 | cy.get('input:last').should('have.attr', 'placeholder', 'Password') 73 | }) 74 | }) 75 | 76 | it('cy.root() - query the root DOM element', () => { 77 | // https://on.cypress.io/root 78 | 79 | // By default, root is the document 80 | cy.root().should('match', 'html') 81 | 82 | cy.get('.query-ul').within(() => { 83 | // In this within, the root is now the ul DOM element 84 | cy.root().should('have.class', 'query-ul') 85 | }) 86 | }) 87 | 88 | it('best practices - selecting elements', () => { 89 | // https://on.cypress.io/best-practices#Selecting-Elements 90 | cy.get('[data-cy=best-practices-selecting-elements]').within(() => { 91 | // Worst - too generic, no context 92 | cy.get('button').click() 93 | 94 | // Bad. Coupled to styling. Highly subject to change. 95 | cy.get('.btn.btn-large').click() 96 | 97 | // Average. Coupled to the `name` attribute which has HTML semantics. 98 | cy.get('[name=submission]').click() 99 | 100 | // Better. But still coupled to styling or JS event listeners. 101 | cy.get('#main').click() 102 | 103 | // Slightly better. Uses an ID but also ensures the element 104 | // has an ARIA role attribute 105 | cy.get('#main[role=button]').click() 106 | 107 | // Much better. But still coupled to text content that may change. 108 | cy.contains('Submit').click() 109 | 110 | // Best. Insulated from all changes. 111 | cy.get('[data-cy=submit]').click() 112 | }) 113 | }) 114 | }) 115 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/storage.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Local Storage / Session Storage', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/storage') 6 | }) 7 | // Although localStorage is automatically cleared 8 | // in between tests to maintain a clean state 9 | // sometimes we need to clear localStorage manually 10 | 11 | it('cy.clearLocalStorage() - clear all data in localStorage for the current origin', () => { 12 | // https://on.cypress.io/clearlocalstorage 13 | cy.get('.ls-btn').click().should(() => { 14 | expect(localStorage.getItem('prop1')).to.eq('red') 15 | expect(localStorage.getItem('prop2')).to.eq('blue') 16 | expect(localStorage.getItem('prop3')).to.eq('magenta') 17 | }) 18 | 19 | // clearLocalStorage() yields the localStorage object 20 | cy.clearLocalStorage().should((ls) => { 21 | expect(ls.getItem('prop1')).to.be.null 22 | expect(ls.getItem('prop2')).to.be.null 23 | expect(ls.getItem('prop3')).to.be.null 24 | }) 25 | 26 | cy.get('.ls-btn').click().should(() => { 27 | expect(localStorage.getItem('prop1')).to.eq('red') 28 | expect(localStorage.getItem('prop2')).to.eq('blue') 29 | expect(localStorage.getItem('prop3')).to.eq('magenta') 30 | }) 31 | 32 | // Clear key matching string in localStorage 33 | cy.clearLocalStorage('prop1').should((ls) => { 34 | expect(ls.getItem('prop1')).to.be.null 35 | expect(ls.getItem('prop2')).to.eq('blue') 36 | expect(ls.getItem('prop3')).to.eq('magenta') 37 | }) 38 | 39 | cy.get('.ls-btn').click().should(() => { 40 | expect(localStorage.getItem('prop1')).to.eq('red') 41 | expect(localStorage.getItem('prop2')).to.eq('blue') 42 | expect(localStorage.getItem('prop3')).to.eq('magenta') 43 | }) 44 | 45 | // Clear keys matching regex in localStorage 46 | cy.clearLocalStorage(/prop1|2/).should((ls) => { 47 | expect(ls.getItem('prop1')).to.be.null 48 | expect(ls.getItem('prop2')).to.be.null 49 | expect(ls.getItem('prop3')).to.eq('magenta') 50 | }) 51 | }) 52 | 53 | it('cy.getAllLocalStorage() - get all data in localStorage for all origins', () => { 54 | // https://on.cypress.io/getalllocalstorage 55 | cy.get('.ls-btn').click() 56 | 57 | // getAllLocalStorage() yields a map of origins to localStorage values 58 | cy.getAllLocalStorage().should((storageMap) => { 59 | expect(storageMap).to.deep.equal({ 60 | // other origins will also be present if localStorage is set on them 61 | 'https://example.cypress.io': { 62 | 'prop1': 'red', 63 | 'prop2': 'blue', 64 | 'prop3': 'magenta', 65 | }, 66 | }) 67 | }) 68 | }) 69 | 70 | it('cy.clearAllLocalStorage() - clear all data in localStorage for all origins', () => { 71 | // https://on.cypress.io/clearalllocalstorage 72 | cy.get('.ls-btn').click() 73 | 74 | // clearAllLocalStorage() yields null 75 | cy.clearAllLocalStorage().should(() => { 76 | expect(sessionStorage.getItem('prop1')).to.be.null 77 | expect(sessionStorage.getItem('prop2')).to.be.null 78 | expect(sessionStorage.getItem('prop3')).to.be.null 79 | }) 80 | }) 81 | 82 | it('cy.getAllSessionStorage() - get all data in sessionStorage for all origins', () => { 83 | // https://on.cypress.io/getallsessionstorage 84 | cy.get('.ls-btn').click() 85 | 86 | // getAllSessionStorage() yields a map of origins to sessionStorage values 87 | cy.getAllSessionStorage().should((storageMap) => { 88 | expect(storageMap).to.deep.equal({ 89 | // other origins will also be present if sessionStorage is set on them 90 | 'https://example.cypress.io': { 91 | 'prop4': 'cyan', 92 | 'prop5': 'yellow', 93 | 'prop6': 'black', 94 | }, 95 | }) 96 | }) 97 | }) 98 | 99 | it('cy.clearAllSessionStorage() - clear all data in sessionStorage for all origins', () => { 100 | // https://on.cypress.io/clearallsessionstorage 101 | cy.get('.ls-btn').click() 102 | 103 | // clearAllSessionStorage() yields null 104 | cy.clearAllSessionStorage().should(() => { 105 | expect(sessionStorage.getItem('prop4')).to.be.null 106 | expect(sessionStorage.getItem('prop5')).to.be.null 107 | expect(sessionStorage.getItem('prop6')).to.be.null 108 | }) 109 | }) 110 | }) 111 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/traversal.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Traversal', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/traversal') 6 | }) 7 | 8 | it('.children() - get child DOM elements', () => { 9 | // https://on.cypress.io/children 10 | cy.get('.traversal-breadcrumb') 11 | .children('.active') 12 | .should('contain', 'Data') 13 | }) 14 | 15 | it('.closest() - get closest ancestor DOM element', () => { 16 | // https://on.cypress.io/closest 17 | cy.get('.traversal-badge') 18 | .closest('ul') 19 | .should('have.class', 'list-group') 20 | }) 21 | 22 | it('.eq() - get a DOM element at a specific index', () => { 23 | // https://on.cypress.io/eq 24 | cy.get('.traversal-list>li') 25 | .eq(1).should('contain', 'siamese') 26 | }) 27 | 28 | it('.filter() - get DOM elements that match the selector', () => { 29 | // https://on.cypress.io/filter 30 | cy.get('.traversal-nav>li') 31 | .filter('.active').should('contain', 'About') 32 | }) 33 | 34 | it('.find() - get descendant DOM elements of the selector', () => { 35 | // https://on.cypress.io/find 36 | cy.get('.traversal-pagination') 37 | .find('li').find('a') 38 | .should('have.length', 7) 39 | }) 40 | 41 | it('.first() - get first DOM element', () => { 42 | // https://on.cypress.io/first 43 | cy.get('.traversal-table td') 44 | .first().should('contain', '1') 45 | }) 46 | 47 | it('.last() - get last DOM element', () => { 48 | // https://on.cypress.io/last 49 | cy.get('.traversal-buttons .btn') 50 | .last().should('contain', 'Submit') 51 | }) 52 | 53 | it('.next() - get next sibling DOM element', () => { 54 | // https://on.cypress.io/next 55 | cy.get('.traversal-ul') 56 | .contains('apples').next().should('contain', 'oranges') 57 | }) 58 | 59 | it('.nextAll() - get all next sibling DOM elements', () => { 60 | // https://on.cypress.io/nextall 61 | cy.get('.traversal-next-all') 62 | .contains('oranges') 63 | .nextAll().should('have.length', 3) 64 | }) 65 | 66 | it('.nextUntil() - get next sibling DOM elements until next el', () => { 67 | // https://on.cypress.io/nextuntil 68 | cy.get('#veggies') 69 | .nextUntil('#nuts').should('have.length', 3) 70 | }) 71 | 72 | it('.not() - remove DOM elements from set of DOM elements', () => { 73 | // https://on.cypress.io/not 74 | cy.get('.traversal-disabled .btn') 75 | .not('[disabled]').should('not.contain', 'Disabled') 76 | }) 77 | 78 | it('.parent() - get parent DOM element from DOM elements', () => { 79 | // https://on.cypress.io/parent 80 | cy.get('.traversal-mark') 81 | .parent().should('contain', 'Morbi leo risus') 82 | }) 83 | 84 | it('.parents() - get parent DOM elements from DOM elements', () => { 85 | // https://on.cypress.io/parents 86 | cy.get('.traversal-cite') 87 | .parents().should('match', 'blockquote') 88 | }) 89 | 90 | it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => { 91 | // https://on.cypress.io/parentsuntil 92 | cy.get('.clothes-nav') 93 | .find('.active') 94 | .parentsUntil('.clothes-nav') 95 | .should('have.length', 2) 96 | }) 97 | 98 | it('.prev() - get previous sibling DOM element', () => { 99 | // https://on.cypress.io/prev 100 | cy.get('.birds').find('.active') 101 | .prev().should('contain', 'Lorikeets') 102 | }) 103 | 104 | it('.prevAll() - get all previous sibling DOM elements', () => { 105 | // https://on.cypress.io/prevall 106 | cy.get('.fruits-list').find('.third') 107 | .prevAll().should('have.length', 2) 108 | }) 109 | 110 | it('.prevUntil() - get all previous sibling DOM elements until el', () => { 111 | // https://on.cypress.io/prevuntil 112 | cy.get('.foods-list').find('#nuts') 113 | .prevUntil('#veggies').should('have.length', 3) 114 | }) 115 | 116 | it('.siblings() - get all sibling DOM elements', () => { 117 | // https://on.cypress.io/siblings 118 | cy.get('.traversal-pills .active') 119 | .siblings().should('have.length', 2) 120 | }) 121 | }) 122 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/utilities.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Utilities', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/utilities') 6 | }) 7 | 8 | it('Cypress._ - call a lodash method', () => { 9 | // https://on.cypress.io/_ 10 | cy.request('https://jsonplaceholder.cypress.io/users') 11 | .then((response) => { 12 | let ids = Cypress._.chain(response.body).map('id').take(3).value() 13 | 14 | expect(ids).to.deep.eq([1, 2, 3]) 15 | }) 16 | }) 17 | 18 | it('Cypress.$ - call a jQuery method', () => { 19 | // https://on.cypress.io/$ 20 | let $li = Cypress.$('.utility-jquery li:first') 21 | 22 | cy.wrap($li) 23 | .should('not.have.class', 'active') 24 | .click() 25 | .should('have.class', 'active') 26 | }) 27 | 28 | it('Cypress.Blob - blob utilities and base64 string conversion', () => { 29 | // https://on.cypress.io/blob 30 | cy.get('.utility-blob').then(($div) => { 31 | // https://github.com/nolanlawson/blob-util#imgSrcToDataURL 32 | // get the dataUrl string for the javascript-logo 33 | return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous') 34 | .then((dataUrl) => { 35 | // create an element and set its src to the dataUrl 36 | let img = Cypress.$('', { src: dataUrl }) 37 | 38 | // need to explicitly return cy here since we are initially returning 39 | // the Cypress.Blob.imgSrcToDataURL promise to our test 40 | // append the image 41 | $div.append(img) 42 | 43 | cy.get('.utility-blob img').click() 44 | .should('have.attr', 'src', dataUrl) 45 | }) 46 | }) 47 | }) 48 | 49 | it('Cypress.minimatch - test out glob patterns against strings', () => { 50 | // https://on.cypress.io/minimatch 51 | let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', { 52 | matchBase: true, 53 | }) 54 | 55 | expect(matching, 'matching wildcard').to.be.true 56 | 57 | matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', { 58 | matchBase: true, 59 | }) 60 | 61 | expect(matching, 'comments').to.be.false 62 | 63 | // ** matches against all downstream path segments 64 | matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', { 65 | matchBase: true, 66 | }) 67 | 68 | expect(matching, 'comments').to.be.true 69 | 70 | // whereas * matches only the next path segment 71 | 72 | matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', { 73 | matchBase: false, 74 | }) 75 | 76 | expect(matching, 'comments').to.be.false 77 | }) 78 | 79 | it('Cypress.Promise - instantiate a bluebird promise', () => { 80 | // https://on.cypress.io/promise 81 | let waited = false 82 | 83 | /** 84 | * @return Bluebird 85 | */ 86 | function waitOneSecond () { 87 | // return a promise that resolves after 1 second 88 | return new Cypress.Promise((resolve, reject) => { 89 | setTimeout(() => { 90 | // set waited to true 91 | waited = true 92 | 93 | // resolve with 'foo' string 94 | resolve('foo') 95 | }, 1000) 96 | }) 97 | } 98 | 99 | cy.then(() => { 100 | // return a promise to cy.then() that 101 | // is awaited until it resolves 102 | return waitOneSecond().then((str) => { 103 | expect(str).to.eq('foo') 104 | expect(waited).to.be.true 105 | }) 106 | }) 107 | }) 108 | }) 109 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/viewport.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | context('Viewport', () => { 3 | beforeEach(() => { 4 | cy.visit('https://example.cypress.io/commands/viewport') 5 | }) 6 | 7 | it('cy.viewport() - set the viewport size and dimension', () => { 8 | // https://on.cypress.io/viewport 9 | 10 | cy.get('#navbar').should('be.visible') 11 | cy.viewport(320, 480) 12 | 13 | // the navbar should have collapse since our screen is smaller 14 | cy.get('#navbar').should('not.be.visible') 15 | cy.get('.navbar-toggle').should('be.visible').click() 16 | cy.get('.nav').find('a').should('be.visible') 17 | 18 | // lets see what our app looks like on a super large screen 19 | cy.viewport(2999, 2999) 20 | 21 | // cy.viewport() accepts a set of preset sizes 22 | // to easily set the screen to a device's width and height 23 | 24 | // We added a cy.wait() between each viewport change so you can see 25 | // the change otherwise it is a little too fast to see :) 26 | 27 | cy.viewport('macbook-15') 28 | cy.wait(200) 29 | cy.viewport('macbook-13') 30 | cy.wait(200) 31 | cy.viewport('macbook-11') 32 | cy.wait(200) 33 | cy.viewport('ipad-2') 34 | cy.wait(200) 35 | cy.viewport('ipad-mini') 36 | cy.wait(200) 37 | cy.viewport('iphone-6+') 38 | cy.wait(200) 39 | cy.viewport('iphone-6') 40 | cy.wait(200) 41 | cy.viewport('iphone-5') 42 | cy.wait(200) 43 | cy.viewport('iphone-4') 44 | cy.wait(200) 45 | cy.viewport('iphone-3') 46 | cy.wait(200) 47 | 48 | // cy.viewport() accepts an orientation for all presets 49 | // the default orientation is 'portrait' 50 | cy.viewport('ipad-2', 'portrait') 51 | cy.wait(200) 52 | cy.viewport('iphone-4', 'landscape') 53 | cy.wait(200) 54 | 55 | // The viewport will be reset back to the default dimensions 56 | // in between tests (the default can be set in cypress.config.{js|ts}) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/waiting.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | context('Waiting', () => { 3 | beforeEach(() => { 4 | cy.visit('https://example.cypress.io/commands/waiting') 5 | }) 6 | // BE CAREFUL of adding unnecessary wait times. 7 | // https://on.cypress.io/best-practices#Unnecessary-Waiting 8 | 9 | // https://on.cypress.io/wait 10 | it('cy.wait() - wait for a specific amount of time', () => { 11 | cy.get('.wait-input1').type('Wait 1000ms after typing') 12 | cy.wait(1000) 13 | cy.get('.wait-input2').type('Wait 1000ms after typing') 14 | cy.wait(1000) 15 | cy.get('.wait-input3').type('Wait 1000ms after typing') 16 | cy.wait(1000) 17 | }) 18 | 19 | it('cy.wait() - wait for a specific route', () => { 20 | // Listen to GET to comments/1 21 | cy.intercept('GET', '**/comments/*').as('getComment') 22 | 23 | // we have code that gets a comment when 24 | // the button is clicked in scripts.js 25 | cy.get('.network-btn').click() 26 | 27 | // wait for GET comments/1 28 | cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /cypress/e2e/2-advanced-examples/window.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | context('Window', () => { 4 | beforeEach(() => { 5 | cy.visit('https://example.cypress.io/commands/window') 6 | }) 7 | 8 | it('cy.window() - get the global window object', () => { 9 | // https://on.cypress.io/window 10 | cy.window().should('have.property', 'top') 11 | }) 12 | 13 | it('cy.document() - get the document object', () => { 14 | // https://on.cypress.io/document 15 | cy.document().should('have.property', 'charset').and('eq', 'UTF-8') 16 | }) 17 | 18 | it('cy.title() - get the title', () => { 19 | // https://on.cypress.io/title 20 | cy.title().should('include', 'Kitchen Sink') 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -------------------------------------------------------------------------------- /cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ], 5 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![CircleCI branch](https://img.shields.io/circleci/project/github/duxianwei520/react/master.svg?style=flat-square)](https://circleci.com/gh/duxianwei520/react) 2 | [![GitHub forks](https://img.shields.io/github/forks/duxianwei520/react.svg)](https://github.com/duxianwei520/react/network) 3 | [![GitHub stars](https://img.shields.io/github/stars/duxianwei520/react.svg)](https://github.com/duxianwei520/react/stargazers) 4 | [![GitHub issues](https://img.shields.io/github/issues/duxianwei520/react.svg)](https://github.com/duxianwei520/react/issues) 5 | [![GitHub license](https://img.shields.io/github/license/duxianwei520/react.svg)](https://github.com/duxianwei520/react/blob/master/LICENSE) 6 | [![Coverage Status](https://coveralls.io/repos/github/duxianwei520/react/badge.svg)](https://coveralls.io/github/duxianwei520/react) 7 | 8 | ## 项目技术栈 9 | 10 | node10.15.3 + react@16.12.0 + redux@3.7.2 + react-router@3.2.0 + webpack@4.41.2 + axios@0.19.0 + less@2.7.1 + antd@3.25.2 11 | 12 | ## 交流 13 | QQ群:159697743 14 | 15 | ## 项目运行 16 | 17 | 18 | ``` 19 | git clone --depth 1 https://github.com/duxianwei520/react.git 20 | 21 | cd react (进入项目) 22 | 23 | npm install (安装依赖包) 24 | 25 | npm start (启动服务) 26 | 27 | ``` 28 | 29 | ### 如果有小伙伴因为网络原因npm包下载不下来,那么可以最好挂在一个vpn之类的去下载,cnpm不靠谱,不推荐使用 30 | 31 | 32 | ## screenshots 33 | 34 | 35 | ### login 36 | 37 | 38 | 39 | ### echart 40 | 41 | 42 | 43 | 44 | ### set center 45 | 46 | 47 | 48 | 49 | ### webpack bundle analysis 50 | 51 | 52 | 53 | ### build dist folder 54 | 55 | 56 | 57 | 58 | 最后的构建命令 59 | ``` 60 | npm run build (正式环境的打包部署) 61 | npm run testing (测试环境的打包部署命令,可以根据具体需求自行配置修改) 62 | 63 | ``` 64 | 65 | 服务端返回的数据格式也是标准的json,如下所示 66 | 67 | ``` 68 | { 69 | data: { 70 | totalCount: 100, 71 | currentPage: 1, 72 | pageSize: 10, 73 | 'list': [ 74 | ], 75 | }, 76 | msg: '', 77 | status: 1, 78 | } 79 | 80 | ``` 81 | 所有异步请求返回都会经过configs里面的ajax.js做处理,如果请求没有任何问题,那status返回值是1; 82 | 如果请求错误,比如说参数错误或者其他报错之类的,那status返回值就是0; 83 | 如果status值是-1,表示登录超时,那么就会跳出登录。 84 | 这些参数都可以根据实际情况进行调整,报错或者成功的提示信息放在msg里面返回。 85 | 当前项目集成了完整的用户管理、角色管理、模块管理等基本的权限管理功能,小伙伴们一定要同时启动npm run mock才可以看到噢 86 | 87 | 这个react的项目我有在跟nodejs的express框架配合做接口的开发,可以不靠后端输出数据库真实的数据,仓库地址在 88 | 89 | ``` 90 | https://github.com/duxianwei520/express 91 | 92 | ``` 93 | 还有一个原生的nodejs版本的,仓库库地址是 94 | 95 | ``` 96 | https://github.com/duxianwei520/node 97 | 98 | ``` 99 | 基本功能差不多,目前实现了注册登录以及获取用户信息等3个接口的真实api 100 | 101 | 102 | ## 说明 103 | 104 | > 如有问题请直接在 Issues 中提,或者您发现问题并有非常好的解决方案,欢迎 PR 👍 105 | 106 | ### 大部分人项目启动不起来的原因,绝大部分的情况都是npm依赖包安装的时候有些依赖包没有下载完全,当前的demo肯定是可以跑起来的 107 | 108 | ### 取消http请求示例: 109 | ``` 110 | import axios from 'axios' 111 | const axiosHandle = axios.CancelToken.source() 112 | 113 | login(){ 114 | this.props.dispatch(fetchLogin(values, (res) => {},(error)=>{},axiosHandle) 115 | 取消请求的操作 116 | setTimeout(() => { 117 | axiosHandle.cancel('手动取消。') 118 | }, 3000) 119 | } 120 | 121 | ``` 122 | 123 | 124 | ## 功能一览 125 | - [√] 登录,以及登录权限控制 126 | - [√] 项目公用npm模块dll化 127 | - [√] redux完整示范 128 | - [√] mockjs模拟后端返回接口 129 | - [√] axios异步请求跨域的设置 130 | - [√] 实时的webpack包大小预览,方便优化 131 | - [√] draftjs编辑器 132 | - [√] cypress自动化测试 133 | 134 | 135 | 136 | ## License 137 | 138 | [MIT](https://github.com/duxianwei520/react/blob/master/LICENSE) 139 | 140 | 141 | ## 交流 142 | 想跟其他的使用react的小伙伴们交流的话,可以加入我创建的reactQQ群:159697743 -------------------------------------------------------------------------------- /scripts/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require('path') 3 | const webpack = require('webpack') 4 | const merge = require('webpack-merge') 5 | const webpackConfigBase = require('./webpack.base.config') 6 | const HtmlWebpackPlugin = require('html-webpack-plugin') 7 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 8 | const os = require('os') 9 | let selfIp 10 | try { 11 | // selfIp = os.networkInterfaces()['WLAN'][1].address 12 | selfIp = getIpAddress() 13 | } catch (e) { 14 | selfIp = 'localhost' 15 | } 16 | 17 | const PORT = 8888 18 | // 精确的获取本机ip地址 19 | function getIpAddress () { 20 | const interfaces = require('os').networkInterfaces 21 | for (let devName in interfaces) { 22 | const iface = interfaces[devName] 23 | for (let i = 0; i < iface.length; i += 1) { 24 | let alias = iface[i] 25 | if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { 26 | return alias.address 27 | } 28 | } 29 | } 30 | } 31 | 32 | function resolve(relatedPath) { 33 | return path.join(__dirname, relatedPath) 34 | } 35 | const webpackConfigDev = { 36 | mode: 'development', 37 | plugins: [ 38 | // 定义环境变量为开发环境 39 | new webpack.DefinePlugin({ 40 | 'process.env.NODE_ENV': JSON.stringify('development'), 41 | IS_DEVELOPMETN: true, 42 | }), 43 | // 将打包后的资源注入到html文件内 44 | new HtmlWebpackPlugin({ 45 | template: resolve('../app/index.html'), 46 | dlls: [], 47 | }), 48 | new CleanWebpackPlugin(), 49 | new webpack.HotModuleReplacementPlugin() 50 | ], 51 | devtool: 'cheap-module-eval-source-map', 52 | devServer: { 53 | contentBase: resolve('../app'), 54 | historyApiFallback: false, 55 | open: true, 56 | hot: true, 57 | host: selfIp, 58 | port: PORT, 59 | }, 60 | } 61 | 62 | module.exports = merge(webpackConfigBase, webpackConfigDev) 63 | -------------------------------------------------------------------------------- /scripts/webpack.dll.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 4 | 5 | module.exports = { 6 | mode:"production", 7 | entry: { 8 | vendor: [ 9 | // '@babel/polyfill', 10 | 'react', 11 | 'react-dom', 12 | 'react-router', 13 | 'axios', 14 | 'lodash' 15 | ], 16 | redux: [ 17 | 'redux', 18 | 'redux-thunk', 19 | 'react-redux', 20 | 'react-router-redux', 21 | ], 22 | }, 23 | devtool: 'source-mapcheap-module-eval-souce-map', 24 | output: { 25 | filename: '[name].dll.js', 26 | path: path.join(__dirname, '../app/resource/dll'), 27 | library: '[name]_[hash]', 28 | }, 29 | performance: false, 30 | plugins: [ 31 | new CleanWebpackPlugin({ 32 | cleanOnceBeforeBuildPatterns:[path.join(__dirname, '../app/resource/dll')], 33 | verbose:true, 34 | }), 35 | // 定义环境变量为开发环境 36 | new webpack.DefinePlugin({ 37 | 'process.env.NODE_ENV': JSON.stringify('production'), 38 | IS_DEVELOPMETN: true, 39 | }), 40 | 41 | // 使用插件 DllPlugin 42 | new webpack.DllPlugin({ 43 | path: path.join(__dirname, '../app/resource/dll', '[name].manifest.json'), 44 | // This must match the output.library option above 45 | name: '[name]_[hash]', 46 | context: __dirname 47 | }), 48 | 49 | 50 | ] 51 | }; -------------------------------------------------------------------------------- /scripts/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | 2 | const webpack = require('webpack') 3 | const path = require('path') 4 | const merge = require('webpack-merge') 5 | const webpackConfigBase = require('./webpack.base.config') 6 | const Copy = require('copy-webpack-plugin') 7 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') 8 | const HtmlWebpackPlugin = require('html-webpack-plugin') 9 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 10 | const TerserJSPlugin = require("terser-webpack-plugin"); 11 | const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); 12 | 13 | function resolve(relatedPath) { 14 | return path.join(__dirname, relatedPath) 15 | } 16 | 17 | const webpackConfigProd = { 18 | mode: 'production', 19 | output: { 20 | publicPath: './', 21 | }, 22 | devtool: 'cheap-module-source-map', 23 | optimization: { 24 | minimizer: [ 25 | new TerserJSPlugin({ // 多进程压缩 26 | // 设置缓存目录 27 | cache: path.resolve('.cache'), 28 | parallel: 4,// 开启多进程压缩 29 | // sourceMap, 30 | terserOptions: { 31 | compress: { 32 | // 删除所有的 `console` 语句 33 | drop_console: true, 34 | }, 35 | }, 36 | }), 37 | ] 38 | }, 39 | plugins: [ 40 | // 定义环境变量为开发环境 41 | new webpack.DefinePlugin({ 42 | 'process.env.NODE_ENV': JSON.stringify('production'), 43 | IS_DEVELOPMETN: false, 44 | }), 45 | // 将打包后的资源注入到html文件内 46 | new HtmlWebpackPlugin({ 47 | // inject: true, // will inject the main bundle to index.html 48 | template: resolve('../app/index.html'), 49 | // mapConfig:'http://192.168.0.1/map_config.js', 50 | // 这里列出要加入html中的js文件 注释不用dll 51 | dlls: [ 52 | // './resource/dll/vendor.dll.js', 53 | // './resource/dll/redux.dll.js', 54 | ], 55 | }), 56 | // 分析代码 57 | new BundleAnalyzerPlugin({ analyzerMode: 'static' }), 58 | new Copy([ 59 | // { from: './app/resource/dll', to: '../dist/resource/dll' }, 60 | ]), 61 | new OptimizeCSSAssetsPlugin(), 62 | new CleanWebpackPlugin(), 63 | ], 64 | } 65 | 66 | module.exports = merge(webpackConfigBase, webpackConfigProd) 67 | -------------------------------------------------------------------------------- /scripts/webpack.testing.config.js: -------------------------------------------------------------------------------- 1 | 2 | const webpack = require('webpack') 3 | const path = require('path') 4 | const merge = require('webpack-merge') 5 | const webpackConfigBase = require('./webpack.base.config') 6 | const Copy = require('copy-webpack-plugin') 7 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') 8 | const HtmlWebpackPlugin = require('html-webpack-plugin') 9 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 10 | 11 | function resolve(relatedPath) { 12 | return path.join(__dirname, relatedPath) 13 | } 14 | 15 | const webpackConfigProd = { 16 | mode: 'development', 17 | output: { 18 | publicPath: './', 19 | }, 20 | plugins: [ 21 | // 定义环境变量为开发环境 22 | new webpack.DefinePlugin({ 23 | 'process.env.NODE_ENV': JSON.stringify('testing'), 24 | IS_DEVELOPMETN: false, 25 | }), 26 | // 将打包后的资源注入到html文件内 27 | new HtmlWebpackPlugin({ 28 | // inject: true, // will inject the main bundle to index.html 29 | template: resolve('../app/index.html'), 30 | // mapConfig:'http://192.168.0.1/map_config.js', 注释不用dll 31 | // 这里列出要加入html中的js文件 32 | dlls: [ 33 | // './resource/dll/vendor.dll.js', 34 | // './resource/dll/redux.dll.js', 35 | ], 36 | }), 37 | 38 | // 分析代码 39 | new BundleAnalyzerPlugin({ analyzerMode: 'static' }), 40 | new Copy([ 41 | // { from: './app/resource/dll', to: '../dist/resource/dll' }, 42 | ]), 43 | new CleanWebpackPlugin(), 44 | ], 45 | } 46 | 47 | module.exports = merge(webpackConfigBase, webpackConfigProd) 48 | -------------------------------------------------------------------------------- /test/Enzyme.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | Enzyme.configure({ 5 | adapter: new Adapter(), 6 | }); 7 | 8 | export default Enzyme; 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/setCenter/sys/moduleManage/index.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../Enzyme.js"; 3 | import RoleManage from "../../../../app/pages/set/roleManage/index"; 4 | import ModuleManage from "../../../../app/pages/set/moduleManage/index"; 5 | import AddModal from "../../../../app/pages/set/moduleManage/modal/moduleAdd"; 6 | import fetchTreeList from "../../../../app/mocks/apis/sys/roleManage/fetchTreeList"; 7 | import ButtonModal from "../../../../app/pages/set/moduleManage/modal/buttonModal"; 8 | jest.mock("../../../../app/pages/set/moduleManage/modal/addButtonModal"); 9 | 10 | let form; 11 | let moduleManage = Enzyme.mount(); 12 | console.log(moduleManage) 13 | moduleManage.setState({ tableDataSource: fetchTreeList.data.list }); 14 | jest.mock("../../../../app/configs/ajax"); 15 | jest.mock("../../../../app/apis/manage.js"); 16 | //页面树 17 | test("tree", () => { 18 | //进入页面渲染 19 | expect(moduleManage.find("tbody tr").length).toBe( 20 | fetchTreeList.data.list.length 21 | ); 22 | //点击tree效果 23 | moduleManage 24 | .find("tbody tr") 25 | .at(0) 26 | .find("span") 27 | .at(1) 28 | .simulate("click"); 29 | expect(moduleManage.find("tbody tr").length).toBe( 30 | fetchTreeList.data.list.length + fetchTreeList.data.list[0].children.length 31 | ); 32 | //状态以上线的类名 33 | expect( 34 | moduleManage 35 | .find(".success") 36 | .at(0) 37 | .text() 38 | ).toBe("已上线"); 39 | }); 40 | test("cancelButton", () => { 41 | moduleManage.instance().cancelButton(); 42 | moduleManage.instance().handleCancel(); 43 | moduleManage.setState({ buttonEditState: "add" }); 44 | moduleManage.instance().handleAdd({}); 45 | 46 | }); 47 | //新增模块 48 | test("addButton/editButton", () => { 49 | moduleManage.find("button").simulate("click"); 50 | //新建按钮点击 51 | expect(moduleManage.state("Visible")).toBe(true); 52 | //新建子菜单按钮点击 53 | moduleManage 54 | .find("tbody tr") 55 | .at(0) 56 | .find("a") 57 | .at(0) 58 | .simulate("click"); 59 | expect(moduleManage.state("title")).toBe("新增子菜单"); 60 | //权限按钮点击 61 | moduleManage 62 | .find("tbody tr") 63 | .at(0) 64 | .find("a") 65 | .at(3) 66 | .simulate("click"); 67 | expect(moduleManage.state("buttonVisible")).toBe(true); 68 | }); 69 | -------------------------------------------------------------------------------- /test/setCenter/sys/moduleManage/module/addButtonModal.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createStore } from "redux"; 3 | import { Provider } from "react-redux"; 4 | import Enzyme from "../../../../Enzyme.js"; 5 | import RoleManage from "../../../../../app/pages/set/roleManage/index"; 6 | import ModuleManage from "../../../../../app/pages/set/moduleManage/index"; 7 | import AddButtonModal from "../../../../../app/pages/set/moduleManage/modal/addButtonModal"; 8 | import fetchButtonList from "../../../../../app/mocks/apis/sys/roleManage/fetchButtonList"; 9 | import rootReducer from "../../../../../app/redux/reducers"; 10 | 11 | const moduleManage = Enzyme.mount(); 12 | jest.mock("../../../../../app/configs/ajax"); 13 | jest.mock("../../../../../app/apis/manage.js"); 14 | jest.mock("../../../../../app/components/draw/draw"); 15 | // jest.mock("../../../../../app/pages/set/moduleManage/modal/addButtonModal"); 16 | 17 | let store; 18 | let wrapper; 19 | let form; 20 | //新增 参数 21 | const addParam = { 22 | buttonEditData: {}, 23 | title: "新增按钮权限", 24 | onCancel: moduleManage.instance().handleCancel, 25 | handleAdd: moduleManage.instance().handleAdd, 26 | state: "add", 27 | visible: true 28 | }; 29 | store = createStore(rootReducer); 30 | wrapper = Enzyme.mount( 31 | 32 | (form = node)} /> 33 | 34 | ); 35 | //当前页面的WrappedComponent 36 | const ButtonModal = wrapper.prop("children").type.WrappedComponent; 37 | 38 | test("add", () => { 39 | moduleManage.setState({ buttonEditState: "add" }); 40 | const buttonModal = Enzyme.mount( 41 | 42 | ); 43 | buttonModal 44 | .find(".ant-modal-footer button") 45 | .at(0) 46 | .simulate("click"); 47 | }); 48 | //编辑 49 | test("edit", () => { 50 | moduleManage.setState({ buttonEditState: "edit" }); 51 | const editParam = { 52 | buttonEditData: fetchButtonList.data.list[0], 53 | handleOk: moduleManage.instance().handleOk, 54 | title: "修改按钮权限", 55 | onCancel: moduleManage.instance().handleCancel, 56 | handleAdd: moduleManage.instance().handleAdd, 57 | state: "edit" 58 | }; 59 | const buttonModal = Enzyme.mount( 60 | 61 | ); 62 | buttonModal 63 | .find(".ant-modal-footer button") 64 | .at(0) 65 | .simulate("click"); 66 | buttonModal 67 | .find(".ant-modal-footer button") 68 | .at(0) 69 | .simulate("click"); 70 | buttonModal 71 | .prop("form") 72 | .setFieldsValue({ resName: fetchButtonList.data.list[0].resName }); 73 | buttonModal 74 | .prop("form") 75 | .setFieldsValue({ sort: fetchButtonList.data.list[0].sort }); 76 | buttonModal 77 | .prop("form") 78 | .setFieldsValue({ resKey: fetchButtonList.data.list[0].resKey }); 79 | buttonModal 80 | .find(".ant-modal-footer button") 81 | .at(0) 82 | .simulate("click"); 83 | }); 84 | -------------------------------------------------------------------------------- /test/setCenter/sys/moduleManage/module/addmodal.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../../Enzyme.js"; 3 | import RoleManage from "../../../../../app/pages/set/roleManage/index"; 4 | import ModuleManage from "../../../../../app/pages/set/moduleManage/index"; 5 | import AddModal from "../../../../../app/pages/set/moduleManage/modal/moduleAdd"; 6 | import fetchModuleDetail from "../../../../../app/mocks/apis/sys/moduleManage/fetchModuleDetail"; 7 | 8 | const moduleManage = Enzyme.mount(); 9 | jest.mock("../../../../../app/configs/ajax"); 10 | jest.mock("../../../../../app/apis/manage.js"); 11 | jest.mock("../../../../../app/components/draw/draw"); 12 | jest.mock("../../../../../app/pages/set/moduleManage/modal/addButtonModal"); 13 | 14 | let form; 15 | const ModalParam = { 16 | handleOk: moduleManage.instance().handleOk, 17 | visible: true, 18 | title: moduleManage.state("title"), 19 | pid: "", 20 | itemId: "", 21 | values: [], 22 | type: "add", 23 | onCancel: moduleManage.instance().handleCancel 24 | }; 25 | 26 | function modalParam(ModalParam) { 27 | Enzyme.mount( 28 | (form = node)} {...ModalParam} /> 29 | ); 30 | const addModal = Enzyme.mount( 31 | 32 | ); 33 | return addModal; 34 | } 35 | test("add", () => { 36 | const add = { 37 | handleOk: moduleManage.instance().handleOk, 38 | title: "新增菜单", 39 | Visible: true, 40 | pid: "", 41 | itemId: 10063, 42 | type: "add", 43 | values: {} 44 | }; 45 | const addModal = modalParam(add); 46 | addModal 47 | .find(".ant-modal-footer button") 48 | .at(0) 49 | .simulate("click"); 50 | addModal 51 | .prop("form") 52 | .setFieldsValue({ parentId: fetchModuleDetail.data.deptCode }); 53 | addModal 54 | .prop("form") 55 | .setFieldsValue({ resName: fetchModuleDetail.data.resName }); 56 | addModal 57 | .prop("form") 58 | .setFieldsValue({ sort: fetchModuleDetail.data.sort }); 59 | addModal 60 | .prop("form") 61 | .setFieldsValue({ resModule: fetchModuleDetail.data.resModule }); 62 | addModal 63 | .prop("form") 64 | .setFieldsValue({ resKey: fetchModuleDetail.data.resKey }); 65 | addModal 66 | .prop("form") 67 | .setFieldsValue({ resIcon: fetchModuleDetail.data.resIcon }); 68 | addModal 69 | .find(".ant-modal-footer button") 70 | .at(0) 71 | .simulate("click"); 72 | }); 73 | test("edit", () => { 74 | //编辑参数 75 | const editParam = { 76 | handleOk: moduleManage.instance().handleOk, 77 | title: "修改菜单", 78 | Visible: true, 79 | pid: "", 80 | itemId: 10063, 81 | type: "modify", 82 | values: fetchModuleDetail.data 83 | }; 84 | const editModal = modalParam(editParam); 85 | //表单验证 86 | editModal.prop("form").validateFields((err, values) => { 87 | expect(values.parentId).toBe(editModal.prop("pid")); 88 | expect(values.resName).toBe(fetchModuleDetail.data.resName); 89 | expect(values.sort).toBe(String(fetchModuleDetail.data.sort)); 90 | expect(values.resModule).toBe(fetchModuleDetail.data.resModule); 91 | expect(values.resKey).toBe(fetchModuleDetail.data.resKey); 92 | expect(values.resIcon).toBe(fetchModuleDetail.data.resIcon); 93 | }); 94 | //提交 95 | editModal 96 | .find(".ant-modal-footer button") 97 | .at(0) 98 | .simulate("click"); 99 | 100 | }); 101 | -------------------------------------------------------------------------------- /test/setCenter/sys/moduleManage/module/buttonModal.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../../Enzyme.js"; 3 | import RoleManage from "../../../../../app/pages/set/roleManage/index"; 4 | import ModuleManage from "../../../../../app/pages/set/moduleManage/index"; 5 | import ButtonModal from "../../../../../app/pages/set/moduleManage/modal/buttonModal"; 6 | import fetchButtonList from "../../../../../app/mocks/apis/sys/roleManage/fetchButtonList"; 7 | 8 | jest.mock("../../../../../app/configs/ajax"); 9 | jest.mock("../../../../../app/apis/manage.js"); 10 | jest.mock("../../../../../app/components/draw/draw"); 11 | jest.mock("../../../../../app/pages/set/moduleManage/modal/addButtonModal"); 12 | 13 | const moduleManage = Enzyme.mount(); 14 | function modalParam(ModalParam) { 15 | return Enzyme.mount(); 16 | } 17 | fetchButtonList.data.list[0].status = 1; 18 | //页面表格渲染 19 | const editParam = { 20 | Visible: true, 21 | pid: "", 22 | itemId: 10063, 23 | dataSource: fetchButtonList.data.list, 24 | listLoading: true, 25 | title: "模块按钮权限列表", 26 | addButton: moduleManage.instance().addButton, 27 | updateList: moduleManage.instance().getButtonList, 28 | editButton: moduleManage.instance().editButton 29 | }; 30 | const BtnModal = modalParam(editParam); 31 | 32 | test("delete", () => { 33 | BtnModal.find("tbody tr") 34 | .at(0) 35 | .find("a") 36 | .at(2) 37 | .simulate("click"); 38 | BtnModal.find("Popconfirm button") 39 | .at(1) 40 | .simulate("click"); 41 | }); 42 | test("edit", () => { 43 | BtnModal.find("tbody tr") 44 | .at(0) 45 | .find("a") 46 | .at(1) 47 | .simulate("click"); 48 | }); 49 | test("showOrHide", () => { 50 | BtnModal.find("tbody tr") 51 | .at(0) 52 | .find("a") 53 | .at(0) 54 | .simulate("click"); 55 | BtnModal.find("Popconfirm button") 56 | .at(1) 57 | .simulate("click"); 58 | }); 59 | test("add", () => { 60 | BtnModal.find(".ant-modal-footer button") 61 | .at(0) 62 | .simulate("click"); 63 | }); 64 | //表格数据 65 | test("module list", () => { 66 | expect(BtnModal.find("tbody tr").length).toBe( 67 | fetchButtonList.data.list.length 68 | ); 69 | }); 70 | -------------------------------------------------------------------------------- /test/setCenter/sys/moduleManage/moduleList.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../Enzyme.js"; 3 | import RoleManage from "../../../../app/pages/set/roleManage/index"; 4 | import ModuleManage from "../../../../app/pages/set/moduleManage/index"; 5 | import AddModal from "../../../../app/pages/set/moduleManage/modal/moduleAdd"; 6 | import fetchModuleList from "../../../../app/mocks/apis/sys/moduleManage/fetchModuleList"; 7 | import ModuleList from "../../../../app/pages/set/moduleManage/moduleList"; 8 | let form; 9 | let moduleManage = Enzyme.mount(); 10 | moduleManage.setState({ tableDataSource: fetchModuleList.data.list }); 11 | jest.mock("../../../../app/configs/ajax"); 12 | jest.mock("../../../../app/apis/manage.js"); 13 | fetchModuleList.data.list[0].status = true; 14 | fetchModuleList.data.list[1].resName = "模块管理"; 15 | const param = { 16 | dataSource: fetchModuleList.data.list, 17 | loading: true, 18 | onDelete: moduleManage.instance().handleDelete, 19 | onModify: moduleManage.instance().handleModify, 20 | onUpdataStatus: moduleManage.instance().handleChangeStatus, 21 | onAddNode: moduleManage.instance().handleAddNode, 22 | buttonList: moduleManage.instance().buttonList 23 | }; 24 | let moduleList = Enzyme.mount(); 25 | console.log(moduleList) 26 | fetchModuleList.data.list[1].resName = "123"; 27 | moduleList.setProps({ dataSource: fetchModuleList.data.list }); 28 | test("addButton", () => { 29 | moduleList 30 | .find("tbody tr") 31 | .at(0) 32 | .find("a") 33 | .at(0) 34 | .simulate("click"); 35 | expect(moduleManage.state("title")).toBe("新增子菜单"); 36 | }); 37 | test("editButton", () => { 38 | moduleList 39 | .find("tbody tr") 40 | .at(0) 41 | .find("a") 42 | .at(1) 43 | .simulate("click"); 44 | expect(moduleManage.state("title")).toBe("修改菜单"); 45 | }); 46 | test("UpdataStatus", () => { 47 | moduleList 48 | .find("tbody tr") 49 | .at(1) 50 | .find("a") 51 | .at(2) 52 | .simulate("click"); 53 | moduleList 54 | .find("Popconfirm button") 55 | .at(1) 56 | .simulate("click"); 57 | }); 58 | test("delete", () => { 59 | //点击tree效果 60 | moduleList 61 | .find("tbody tr") 62 | .at(0) 63 | .find("span") 64 | .at(1) 65 | .simulate("click"); 66 | //点击子元素的删除按钮 67 | moduleList 68 | .find("tbody tr") 69 | .at(1) 70 | .find("a") 71 | .at(2) 72 | .simulate("click"); 73 | moduleList 74 | .find("Popconfirm button") 75 | .at(1) 76 | .simulate("click"); 77 | }); 78 | -------------------------------------------------------------------------------- /test/setCenter/sys/roleManage/modal/buttonModal.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../../Enzyme.js"; 3 | import RoleManage from "../../../../../app/pages/set/roleManage/index"; 4 | import ButtonModal from "../../../../../app/pages/set/roleManage/modal/buttonModal"; 5 | jest.mock("../../../../../app/configs/ajax"); 6 | jest.mock("../../../../../app/apis/manage.js"); 7 | jest.mock("../../../../../app/components/draw/draw"); 8 | let form1; 9 | Enzyme.mount( (form1 = node)} />); 10 | let roleManage = Enzyme.mount( 11 | 12 | ); 13 | const callback = {}; 14 | let form = {}; 15 | 16 | const param = { 17 | title: "按钮权限列表", 18 | type: "add", 19 | pid: 10062, 20 | itemId: 1, 21 | cancelButton: roleManage.instance().cancelButton, 22 | saveChecked: roleManage.instance().saveChecked, 23 | checkedIdArr: [] 24 | }; 25 | let buttonModal = Enzyme.mount(); 26 | test("全选按钮", () => { 27 | buttonModal 28 | .find(".ant-modal-footer button") 29 | .at(0) 30 | .simulate("click"); 31 | }); 32 | buttonModal.setState({ 33 | selectedRowKeys: [134] 34 | }); 35 | test("初始化", () => { 36 | buttonModal.instance().getList(); 37 | }); 38 | test("确定按钮", () => { 39 | buttonModal 40 | .find(".ant-modal-footer button") 41 | .at(1) 42 | .simulate("click"); 43 | }); 44 | test("页面按钮点击选中", () => { 45 | buttonModal 46 | .find(".ant-spin-container button") 47 | .at(0) 48 | .simulate("click"); 49 | }); 50 | -------------------------------------------------------------------------------- /test/setCenter/sys/roleManage/modal/roleAdd.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../../Enzyme.js"; 3 | import RoleManage from "../../../../../app/pages/set/roleManage/index"; 4 | import RoleAdd from "../../../../../app/pages/set/roleManage/modal/roleAdd"; 5 | import fetchRoleDetail from "../../../../../app/mocks/apis/sys/roleManage/fetchRoleDetail"; 6 | jest.mock("../../../../../app/configs/ajax"); 7 | jest.mock("../../../../../app/apis/manage.js"); 8 | jest.mock("../../../../../app/components/draw/draw"); 9 | let form1; 10 | Enzyme.mount( (form1 = node)} />); 11 | let roleManage = Enzyme.mount( 12 | 13 | ); 14 | const callback = {}; 15 | let form = {}; 16 | fetchRoleDetail.data.tjFlag='123' 17 | const param = { 18 | visible: true, 19 | title: "修改角色", 20 | onCancel: roleManage.instance().handleCancel, 21 | handleOk: roleManage.instance().handleOk, 22 | value: fetchRoleDetail.data, 23 | type: "modify", 24 | modifyId: 1 25 | }; 26 | let Form = Enzyme.mount( 27 | (form = node)} /> 28 | ); 29 | 30 | let roleAdd = Enzyme.mount( 31 | 32 | ); 33 | test("编辑提交", () => { 34 | const event = { 35 | preventDefault: function() {} 36 | }; 37 | roleAdd.instance().handleSubmit(event); 38 | expect(roleAdd.find(".ant-modal-title").text()).toBe("修改角色"); 39 | }); 40 | test("新增提交", () => { 41 | const event = { 42 | preventDefault: function() {} 43 | }; 44 | roleAdd.setProps({ 45 | visible: true, 46 | title: "新增角色", 47 | onCancel: roleManage.instance().handleCancel, 48 | handleOk: roleManage.instance().handleOk, 49 | value: { roleName: "", sort: "", tjFlag: "123" }, 50 | type: "add" 51 | }); 52 | roleAdd 53 | .find(".ant-modal-footer button") 54 | .at(0) 55 | .simulate("click"); 56 | expect(roleAdd.find(".ant-modal-title").text()).toBe("新增角色"); 57 | roleAdd.prop("form").setFieldsValue({ roleName: "" }); 58 | roleAdd 59 | .find(".ant-modal-footer button") 60 | .at(0) 61 | .simulate("click"); 62 | roleAdd 63 | .find(".ant-modal-footer button") 64 | .at(0) 65 | .simulate("click"); 66 | }); 67 | 68 | -------------------------------------------------------------------------------- /test/setCenter/sys/roleManage/roleCheckbox.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../Enzyme.js"; 3 | import RoleManage from "../../../../app/pages/set/roleManage/index"; 4 | import fetchRoleList from "../../../../app/mocks/apis/sys/userManage/fetchRoleList"; 5 | import UserList from "../../../../app/mocks/apis/sys/userManage/fetchUserList"; 6 | import fetchTreeList from "../../../../app/mocks/apis/sys/roleManage/fetchTreeList"; 7 | import RoleModuleList from "../../../../app/pages/set/roleManage/roleModuleList"; 8 | 9 | let form; 10 | Enzyme.mount( (form = node)} />); 11 | let roleManage = Enzyme.mount( 12 | 13 | ); 14 | 15 | 16 | test("roleCheckbox", () => { 17 | const listParam = { 18 | dataSource: [], 19 | checkedId: [] 20 | }; 21 | const roleModule = Enzyme.mount(); 22 | roleModule.setProps({ 23 | dataSource: fetchTreeList.data.list, 24 | checkedId: [10062, 10063, 10108, 10109, 10110] 25 | }); 26 | roleModule 27 | .find("tbody tr") 28 | .at(1) 29 | .find("span") 30 | .at(1) 31 | .simulate("click"); 32 | }); 33 | -------------------------------------------------------------------------------- /test/setCenter/sys/roleManage/roleModuleList.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../Enzyme.js"; 3 | import RoleManage from "../../../../app/pages/set/roleManage/index"; 4 | import fetchRoleList from "../../../../app/mocks/apis/sys/userManage/fetchRoleList"; 5 | import UserList from "../../../../app/mocks/apis/sys/userManage/fetchUserList"; 6 | import fetchTreeList from "../../../../app/mocks/apis/sys/roleManage/fetchTreeList"; 7 | import fetchModuleListInRole from "../../../../app/mocks/apis/sys/roleManage/fetchModuleListInRole"; 8 | import RoleModuleList from "../../../../app/pages/set/roleManage/roleModuleList"; 9 | import RoleCheckbox from "../../../../app/pages/set/roleManage/roleCheckbox"; 10 | jest.mock("../../../../app/configs/ajax"); 11 | jest.mock("../../../../app/apis/manage.js"); 12 | let form; 13 | Enzyme.mount( (form = node)} />); 14 | let roleManage = Enzyme.mount( 15 | 16 | ); 17 | 18 | describe("roleModuleList", () => { 19 | const listParam = { 20 | dataSource: [], 21 | checkedId: [], 22 | onCheckModify: roleManage.instance().handleCheckModify, 23 | buttonList:roleManage.instance().buttonList 24 | }; 25 | const roleModule = Enzyme.mount(); 26 | roleModule.setProps({ 27 | dataSource: fetchModuleListInRole.data.list, 28 | checkedId: [10062, 10063, 10108, 10109, 10110, 10118, 11003] 29 | }); 30 | test("rolecheckbox", () => { 31 | const checkboxParam = { 32 | checkItem: fetchModuleListInRole.data.list[1].children[1], 33 | onChecked: roleModule.instance().onChecked, 34 | defaultChecked: false, 35 | nowRoleName: undefined 36 | }; 37 | const roleCheckbox = Enzyme.mount(); 38 | roleCheckbox.setProps({ defaultChecked: true }); 39 | const event = { 40 | target: { 41 | checked: false 42 | } 43 | }; 44 | roleCheckbox.instance().onChange(event); 45 | //未开通的点击成已开通 46 | event.target.checked = true; 47 | roleCheckbox.setProps({ 48 | checkItem: fetchModuleListInRole.data.list[1] 49 | }); 50 | roleCheckbox.instance().onChange(event); 51 | 52 | event.target.checked = false; 53 | //onInArray函数 54 | roleCheckbox.setProps({ 55 | checkItem: fetchModuleListInRole.data.list[1].children[2] 56 | }); 57 | roleModule.setState({ checkedIds: null }); 58 | roleCheckbox.instance().onChange(event); 59 | roleModule.setState({ 60 | checkedIds: [10062, 10108] 61 | }); 62 | roleCheckbox.setProps({ 63 | checkItem: fetchModuleListInRole.data.list[1].children[0] 64 | }); 65 | roleCheckbox.instance().onChange(event); 66 | 67 | //fatherArr.length === 1判断 68 | 69 | fetchModuleListInRole.data.list[1].children.pop(); 70 | fetchModuleListInRole.data.list[1].children.pop(); 71 | roleCheckbox.setProps({ 72 | checkItem: fetchModuleListInRole.data.list[1].children[0] 73 | }); 74 | event.target.checked = false; 75 | roleCheckbox.instance().onChange(event); 76 | event.target.checked = true; 77 | roleCheckbox.instance().onChange(event); 78 | //拥有两个父级 79 | event.target.checked = false; 80 | roleCheckbox.setProps({ 81 | checkItem: fetchModuleListInRole.data.list[1].children[0].children[0] 82 | }); 83 | roleCheckbox.instance().onChange(event); 84 | }); 85 | //按钮权限 86 | test('buttonList',()=>{ 87 | roleModule.find('tbody tr').at(0).find('a').simulate('click') 88 | }) 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /test/setCenter/sys/userManage/index.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../Enzyme.js"; 3 | import UserName from "../../../../app/pages/set/userManage/index"; 4 | import TreeList from "../../../../app/pages/set/userManage/treeList"; 5 | import UserList from "../../../../app/mocks/apis/sys/userManage/fetchUserList"; 6 | import AddPolice from "../../../../app/pages/set/userManage/modal/addPolice"; 7 | import userDeptResult from "../../../../app/mocks/apis/sys/userManage/fetchUserDepttList"; 8 | import TableList from "../../../../app/components/tableList/tableList"; 9 | jest.mock("../../../../app/configs/ajax"); 10 | jest.mock("../../../../app/apis/manage"); 11 | 12 | let form; 13 | Enzyme.mount( (form = node)} />); 14 | let UserManage = Enzyme.mount( 15 | 16 | ); 17 | const param = { 18 | deptCode: "370200000000" 19 | }; 20 | UserManage.setState({ 21 | searchKey: param, 22 | userListResult: UserList.data, 23 | userDeptResult: userDeptResult.data, 24 | btnRights: { 25 | view: false, 26 | freeze: false, 27 | delete: false, 28 | edit: false, 29 | add: false 30 | } // 按钮权限的数组 31 | }); 32 | 33 | UserManage.setState({ 34 | btnRights: { 35 | view: true, 36 | freeze: true, 37 | delete: true, 38 | edit: true, 39 | add: true 40 | } // 按钮权限的数组 41 | }); 42 | // 搜索按钮点击 43 | test("search", () => { 44 | const event = { 45 | preventDefault: function() {} 46 | }; 47 | UserManage.instance().handleSearch(event); 48 | }); 49 | 50 | // 新增 51 | test("handleChangeStatusAdd", () => { 52 | UserManage.setState({ 53 | searchKey: { 54 | deptCode: null 55 | } 56 | }); 57 | UserManage.find("Button") 58 | .at(1) 59 | .simulate("click"); 60 | 61 | UserManage.setState({ 62 | searchKey: { 63 | deptCode: "370200000000" 64 | } 65 | }); 66 | UserManage.find("Button") 67 | .at(1) 68 | .simulate("click"); 69 | expect(UserManage.state().moduletitle).toBe("新增"); 70 | }); 71 | 72 | //点击用户详情 73 | test("handleChangeStatusEdit", () => { 74 | UserManage.setState({ 75 | searchKey: param, 76 | userListResult: UserList.data, 77 | userDeptResult: userDeptResult.data, 78 | PoliceAddVisible: false, 79 | moduletype: "edit", 80 | userRoleSetResult: {} 81 | }); 82 | UserManage.find("tbody tr") 83 | .at(0) 84 | .find("a") 85 | .at(0) 86 | .simulate("click"); 87 | expect(UserManage.state().PoliceAddVisible).toBe(true); 88 | UserManage.instance().handleCancel(); 89 | }); 90 | 91 | //冻结账户 92 | test("handleChangeStatus", () => { 93 | UserManage.find("tbody tr") 94 | .at(0) 95 | .find("a") 96 | .at(1) 97 | .simulate("click"); 98 | UserManage.find("Popconfirm button") 99 | .at(1) 100 | .simulate("click"); 101 | UserManage.find("tbody tr") 102 | .at(1) 103 | .find("a") 104 | .at(1) 105 | .simulate("click"); 106 | UserManage.find("Popconfirm button") 107 | .at(1) 108 | .simulate("click"); 109 | }); 110 | 111 | // 同步人员 112 | test("synchronize", () => { 113 | UserManage.find(".page-footer Button") 114 | .at(1) 115 | .simulate("click"); 116 | }); 117 | //页码改变 118 | test("pagenext", () => { 119 | UserManage.instance().pageChange(2); 120 | expect(UserManage.state().searchKey.pageNo).toBe(2); 121 | UserManage.instance().pageSizeChange({}, 20); 122 | expect(UserManage.state().searchKey.pageSize).toBe(20); 123 | }); 124 | // treelist模块 125 | describe("treeList", () => { 126 | const treeListParam = { 127 | curDeptCode: "370200000000", 128 | onSelect: UserManage.instance().onSelect 129 | }; 130 | const treeList = Enzyme.mount(); 131 | //树点击事件 132 | treeList.setProps({ 133 | trees: userDeptResult.data.list 134 | }); 135 | it("treeClick", () => { 136 | treeList 137 | .find("li") 138 | .at(0) 139 | .find("span") 140 | .at(0) 141 | .simulate("click"); 142 | expect(treeList.state("expandedKeys")).toContain( 143 | userDeptResult.data.list[0].deptCode 144 | ); 145 | }); 146 | //树内容点击事件 147 | it("treeListClick", () => { 148 | treeList 149 | .find("li") 150 | .at(0) 151 | .find("span") 152 | .at(1) 153 | .simulate("click"); 154 | treeList 155 | .find("li") 156 | .at(1) 157 | .find("span") 158 | .at(1) 159 | .simulate("click"); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /test/setCenter/sys/userManage/modal/addPolice.spec.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Enzyme from "../../../../Enzyme.js"; 3 | import UserName from "../../../../../app/pages/set/userManage/index"; 4 | import AddPolice from "../../../../../app/pages/set/userManage/modal/addPolice"; 5 | import roleList from "../../../../../app/mocks/apis/sys/userManage/fetchRoleList"; 6 | import fetchUserDetail from "../../../../../app/mocks/apis/sys/userManage/fetchUserDetail"; 7 | import UserList from "../../../../../app/mocks/apis/sys/userManage/fetchUserList"; 8 | 9 | import Draw from "../../../../../app/components/draw/draw"; 10 | jest.mock("../../../../../app/configs/ajax"); 11 | 12 | 13 | 14 | jest.mock("../../../../../app/components/draw/draw"); 15 | let form1; 16 | Enzyme.mount( (form1 = node)} />); 17 | let UserManage = Enzyme.mount( 18 | 19 | ); 20 | const callback = {}; 21 | 22 | let form = {}; 23 | const param = { 24 | deptId: "370200000000", 25 | handleOk: UserManage.instance().handleOk, 26 | title: "新增", 27 | type: "add", 28 | visible: true, 29 | roleList: roleList.data.list, 30 | values: {}, 31 | onCancel: UserManage.instance().onCancel 32 | }; 33 | UserList.data.totalCount = 10; 34 | UserManage.setState({ 35 | userListResult: UserList.data 36 | }); 37 | let Form = Enzyme.mount( 38 | (form = node)} 42 | /> 43 | ); 44 | 45 | let policeFrom = Enzyme.mount( 46 | 51 | ); 52 | //新增弹出框基础信息验证 53 | test("addInit", () => { 54 | UserManage.setState({ 55 | moduletype: "add", 56 | userListResult: UserList.data 57 | }); 58 | policeFrom 59 | .find("button") 60 | .at(2) 61 | .simulate("click"); 62 | policeFrom 63 | .prop("form") 64 | .setFieldsValue({ chineseName: fetchUserDetail.data.chineseName }); 65 | policeFrom 66 | .prop("form") 67 | .setFieldsValue({ password: fetchUserDetail.data.password }); 68 | policeFrom 69 | .prop("form") 70 | .setFieldsValue({ username: fetchUserDetail.data.username }); 71 | policeFrom 72 | .prop("form") 73 | .setFieldsValue({ idcardNo: fetchUserDetail.data.idcardNo }); 74 | policeFrom 75 | .prop("form") 76 | .setFieldsValue({ deptCode: fetchUserDetail.data.deptCode }); 77 | policeFrom 78 | .prop("form") 79 | .setFieldsValue({ phoneNo: fetchUserDetail.data.phoneNo }); 80 | policeFrom 81 | .prop("form") 82 | .setFieldsValue({ gxdwdm: fetchUserDetail.data.gxdwdm }); 83 | policeFrom 84 | .prop("form") 85 | .setFieldsValue({ roleIds: fetchUserDetail.data.roleIds }); 86 | expect(policeFrom.find("Drawer").props().title).toBe("新增"); 87 | expect(policeFrom.find("Drawer").props().visible).toBe(param.visible); 88 | policeFrom 89 | .find("button") 90 | .at(2) 91 | .simulate("click"); 92 | }); 93 | //编辑框基础信息验证 94 | test("editInit", () => { 95 | UserManage.setState({ 96 | moduletype: "edit", 97 | userListResult: UserList.data 98 | }); 99 | const param = { 100 | deptId: "370200000000", 101 | handleOk: UserManage.instance().handleOk, 102 | title: "编辑", 103 | type: "edit", 104 | visible: true, 105 | roleList: roleList.data.list, 106 | values: fetchUserDetail.data 107 | }; 108 | policeFrom.setProps(param); 109 | expect(policeFrom.find("Drawer").props().title).toBe("编辑"); 110 | policeFrom 111 | .find("button") 112 | .at(2) 113 | .simulate("click"); 114 | policeFrom 115 | .find("button") 116 | .at(3) 117 | .simulate("click"); 118 | }); 119 | --------------------------------------------------------------------------------