├── README.md ├── fe ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .roadhogrc.js ├── LICENSE ├── README.md ├── package.json ├── src │ ├── components │ │ ├── DataTable │ │ │ ├── AnimTableBody.js │ │ │ ├── DataTable.js │ │ │ ├── DataTable.less │ │ │ └── package.json │ │ ├── DownloadButton │ │ │ ├── icon.js │ │ │ └── index.js │ │ ├── DropOption │ │ │ ├── DropOption.js │ │ │ └── package.json │ │ ├── Editor │ │ │ ├── Editor.js │ │ │ ├── Editor.less │ │ │ └── package.json │ │ ├── FilterItem │ │ │ ├── FilterItem.js │ │ │ ├── FilterItem.less │ │ │ └── package.json │ │ ├── Iconfont │ │ │ ├── Iconfont.js │ │ │ ├── iconfont.less │ │ │ └── package.json │ │ ├── InputClose │ │ │ ├── InputClose.js │ │ │ ├── InputClose.less │ │ │ └── package.json │ │ ├── Layout │ │ │ ├── Bread.js │ │ │ ├── Bread.less │ │ │ ├── Footer.js │ │ │ ├── Footer.less │ │ │ ├── Header.js │ │ │ ├── Header.less │ │ │ ├── Layout.less │ │ │ ├── Menu.js │ │ │ ├── Sider.js │ │ │ ├── Sider.less │ │ │ └── index.js │ │ ├── Loader │ │ │ ├── Loader.js │ │ │ ├── Loader.less │ │ │ └── package.json │ │ ├── Search │ │ │ ├── Search.js │ │ │ ├── Search.less │ │ │ └── package.json │ │ ├── SearchTree │ │ │ ├── SearchTree.js │ │ │ ├── SearchTree.json │ │ │ └── SearchTree.less │ │ ├── index.js │ │ └── layer │ │ │ ├── layer.js │ │ │ ├── layer.less │ │ │ └── package.json │ ├── entry.ejs │ ├── index.js │ ├── models │ │ ├── app.js │ │ ├── common.js │ │ ├── dashboard.js │ │ ├── login.js │ │ ├── sys │ │ │ ├── account.js │ │ │ ├── menu.js │ │ │ ├── permission.js │ │ │ └── role.js │ │ └── user.js │ ├── public │ │ ├── QR_code.jpg │ │ ├── antd │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ └── iconfont.woff │ │ ├── bg_image.jpg │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── logo.png │ ├── router.js │ ├── routes │ │ ├── app.js │ │ ├── app.less │ │ ├── dashboard │ │ │ ├── components │ │ │ │ ├── browser.js │ │ │ │ ├── browser.less │ │ │ │ ├── comments.js │ │ │ │ ├── comments.less │ │ │ │ ├── completed.js │ │ │ │ ├── completed.less │ │ │ │ ├── cpu.js │ │ │ │ ├── cpu.less │ │ │ │ ├── index.js │ │ │ │ ├── numberCard.js │ │ │ │ ├── numberCard.less │ │ │ │ ├── quote.js │ │ │ │ ├── quote.less │ │ │ │ ├── recentSales.js │ │ │ │ ├── recentSales.less │ │ │ │ ├── sales.js │ │ │ │ ├── sales.less │ │ │ │ ├── user-background.png │ │ │ │ ├── user.js │ │ │ │ ├── user.less │ │ │ │ ├── weather.js │ │ │ │ └── weather.less │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── error │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── login │ │ │ ├── index.js │ │ │ └── index.less │ │ └── sys │ │ │ ├── account │ │ │ ├── Filter.js │ │ │ ├── List.js │ │ │ ├── List.less │ │ │ ├── Modal.js │ │ │ ├── Modal.less │ │ │ └── index.js │ │ │ ├── menu │ │ │ ├── Form.js │ │ │ ├── Form.less │ │ │ ├── Tree.js │ │ │ └── index.js │ │ │ ├── permission │ │ │ ├── Form.js │ │ │ ├── Form.less │ │ │ ├── Tree.js │ │ │ └── index.js │ │ │ └── role │ │ │ ├── Filter.js │ │ │ ├── List.js │ │ │ ├── List.less │ │ │ ├── Modal.js │ │ │ ├── Modal.less │ │ │ └── index.js │ ├── services │ │ ├── app.js │ │ ├── common.js │ │ ├── dashboard.js │ │ ├── login.js │ │ ├── sys │ │ │ ├── account.js │ │ │ ├── dataSync.js │ │ │ ├── menu.js │ │ │ ├── permission.js │ │ │ ├── role.js │ │ │ └── schedule.js │ │ ├── user.js │ │ ├── users.js │ │ └── weather.js │ ├── svg │ │ ├── CDR.svg │ │ ├── cute │ │ │ ├── congratulations.svg │ │ │ ├── cry.svg │ │ │ ├── kiss.svg │ │ │ ├── leisurely.svg │ │ │ ├── notice.svg │ │ │ ├── proud.svg │ │ │ ├── shy.svg │ │ │ ├── sweat.svg │ │ │ └── think.svg │ │ └── emoji │ │ │ ├── smirking.svg │ │ │ ├── surprised.svg │ │ │ ├── tired.svg │ │ │ ├── tongue.svg │ │ │ ├── unamused.svg │ │ │ ├── vomiting.svg │ │ │ ├── wink.svg │ │ │ └── zombie.svg │ ├── themes │ │ ├── default.less │ │ ├── index.less │ │ ├── mixin.less │ │ └── vars.less │ └── utils │ │ ├── city.js │ │ ├── config.js │ │ ├── date.js │ │ ├── index.js │ │ ├── request.js │ │ └── theme.js ├── theme.config.js └── webpack.config.js ├── pom.xml ├── sql └── aas_db.sql └── src └── main ├── java └── me │ └── hrps │ └── aas │ ├── Application.java │ ├── core │ ├── config │ │ ├── ShrioConfig.java │ │ └── WebConfig.java │ ├── constant │ │ └── AppConsts.java │ ├── exception │ │ ├── ServiceException.java │ │ └── ValidatorException.java │ ├── mvc │ │ └── advice │ │ │ ├── MyResponseBodyAdvice.java │ │ │ └── ServiceExceptionHandler.java │ ├── mybatis │ │ └── domain │ │ │ ├── PageFilter.java │ │ │ └── TableResponse.java │ ├── shiro │ │ ├── domain │ │ │ └── ShiroUser.java │ │ ├── filter │ │ │ ├── MyFormAuthenticationFilter.java │ │ │ ├── MyLogoutFilter.java │ │ │ └── MyUserFilter.java │ │ └── realm │ │ │ └── ShiroDbRealm.java │ └── utils │ │ ├── Digests.java │ │ ├── Exceptions.java │ │ ├── PasswordUtils.java │ │ ├── Securitys.java │ │ └── Utils.java │ └── web │ └── sys │ ├── controller │ ├── AccountController.java │ └── SysController.java │ ├── domain │ ├── SysAccount.java │ ├── SysAccountExample.java │ ├── SysAccountRoleExample.java │ ├── SysAccountRoleKey.java │ ├── SysMenu.java │ ├── SysMenuExample.java │ ├── SysPermission.java │ ├── SysPermissionExample.java │ ├── SysRole.java │ ├── SysRoleExample.java │ ├── SysRolePermissionExample.java │ └── SysRolePermissionKey.java │ ├── filter │ ├── AccountFilter.java │ └── RoleFilter.java │ ├── mapper │ ├── SysAccountMapper.java │ ├── SysAccountRoleMapper.java │ ├── SysMenuMapper.java │ ├── SysPermissionMapper.java │ ├── SysRoleMapper.java │ └── SysRolePermissionMapper.java │ ├── qo │ └── TreeSortQO.java │ ├── service │ ├── AccountService.java │ └── SysService.java │ └── vo │ ├── MenuPermissionTreeVO.java │ ├── MenuVO.java │ ├── SysAccountVO.java │ ├── SysRoleVO.java │ └── UserVO.java └── resources ├── application.yml ├── mybatis ├── SysAccountMapper.xml ├── SysAccountRoleMapper.xml ├── SysMenuMapper.xml ├── SysPermissionMapper.xml ├── SysRoleMapper.xml └── SysRolePermissionMapper.xml ├── mybatisGeneratorConfig.xml └── security └── ehcache-shiro.xml /README.md: -------------------------------------------------------------------------------- 1 | # Antd Admin Springboot 2 | 3 | [![React](https://img.shields.io/badge/react-^15.6.1-brightgreen.svg?style=flat-square)](https://github.com/facebook/react) 4 | [![Ant Design](https://img.shields.io/badge/ant--design-^2.11.2-yellowgreen.svg?style=flat-square)](https://github.com/ant-design/ant-design) 5 | [![dva](https://img.shields.io/badge/dva-^2.0.1-orange.svg?style=flat-square)](https://github.com/dvajs/dva) 6 | [![](https://img.shields.io/badge/spring--boot-1.5.3.RELEASE-green.svg)](https://github.com/spring-projects/spring-boot) 7 | 8 | [![](https://img.shields.io/github/issues/javahuang/antd-admin-springboot.svg)](https://github.com/javahuang/antd-admin-springboot/issues) 9 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/javahuang/antd-admin-springboot/pulls) 10 | [![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) 11 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com) 12 | 13 | 演示地址 14 | 15 | admin/hello,请不要修改管理员账号的密码。 16 | 17 | ## 特性 18 | 19 | 原数据库使用的是 oracle,整个代码我重新整理一遍,但迁移稍微仓促,没有测试,页面有不少小的 bug,后期有时间我再一一修正 20 | 21 | - 前端基于最新版本的 [antd-admin](https://github.com/zuiidea/antd-admin) 22 | - 后端基于 springboot,同时整合了 shiro、mybatis,数据库使用的 mysql 23 | - 后端添加了自动分页插件 24 | - 基于 shiro 的 RBAC 权限控制,能动态配置菜单、 角色、权限等(用户对应角色(1对多),角色对应权限(1对多),菜单对应权限(1对1)) 25 | - 统一异常处理及返回数据统一封装 26 | - 后端 RESTful api 设计 27 | - 开发、部署简单,能实现前后端同时开发且无需额外修改配置 28 | - ... 29 | 30 | 31 | ### 目录结构 32 | 前端目录结构参见 [目录结构](https://github.com/zuiidea/antd-admin#目录结构) 33 | 34 | 35 | ### 快速开始 36 | 37 | 克隆项目文件: 38 | 39 | ```bash 40 | git clone https://github.com/javahuang/antd-admin-springboot 41 | ``` 42 | 43 | 进入目录安装依赖: 44 | 45 | ```bash 46 | # 前端 47 | # 开始前请确保没有安装roadhog、webpack到NPM全局目录 48 | npm i 或者 yarn install 49 | 50 | # 后台 51 | # 根据 pom.xml 配置下载 maven 依赖包 52 | ``` 53 | 54 | 开发: 55 | 56 | ```bash 57 | # 前端 58 | npm run build:dll #第一次npm run dev时需运行此命令,使开发时编译更快 59 | npm run dev 60 | 打开 http://localhost:8000 61 | 62 | # 后台 63 | # 直接运行 me.hrps.aas.Application-main() 方法启动后台服务 64 | ``` 65 | 66 | 构建: 67 | 68 | ```bash 69 | # 前端 70 | npm run build 71 | 将会打包至dist/{version}目录 #package.json里version字段 72 | 将 dist 目录下面所有文件拷贝到 src/main/resources/static 目录下面 73 | 74 | # 后台 75 | mvn clean install 76 | ``` 77 | 78 | 部署: 79 | 80 | ```bash 81 | # 上一步会在生成 war/ROOT.war 82 | # 方式1,直接运行 war 包 83 | nohup java -jar ROOT.war & 84 | # 方式2,丢到 tomcat 的 webapps 目录下面 85 | ``` 86 | 87 | 88 | -------------------------------------------------------------------------------- /fe/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /fe/.eslintignore: -------------------------------------------------------------------------------- 1 | src/**/*-test.js 2 | src/public -------------------------------------------------------------------------------- /fe/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "semi": [ 5 | 2, 6 | "never" 7 | ], 8 | "no-console": 0, 9 | "comma-dangle": [ 10 | 2, 11 | "always-multiline" 12 | ], 13 | "max-len": 0, 14 | "react/jsx-first-prop-new-line": 0, 15 | "react/jsx-filename-extension": 0, 16 | "space-before-function-paren": [ 17 | 2, 18 | "always" 19 | ], 20 | "no-unused-expressions": [ 21 | 0, 22 | { 23 | "allowShortCircuit": true, 24 | "allowTernary": true 25 | } 26 | ], 27 | "arrow-body-style": [ 28 | 0, 29 | "never" 30 | ], 31 | "func-names": 0, 32 | "prefer-const": 0, 33 | "no-extend-native": 0, 34 | "no-param-reassign": 0, 35 | "no-restricted-syntax": 0, 36 | "no-eval": 0, 37 | "no-continue": 0, 38 | "no-script-url": "off", 39 | "react/jsx-no-bind": 0, 40 | "no-unused-vars": [ 41 | 2, 42 | { 43 | "ignoreRestSiblings": true 44 | } 45 | ], 46 | "no-underscore-dangle": 0, 47 | "global-require": 0, 48 | "import/no-unresolved": 0, 49 | "import/extensions": 0, 50 | "jsx-a11y/href-no-hash": 0, 51 | "react/no-array-index-key": 0, 52 | "react/require-default-props": 0, 53 | "react/forbid-prop-types": 0, 54 | "react/no-string-refs": 0, 55 | "react/no-find-dom-node": 0, 56 | "import/no-extraneous-dependencies": 0, 57 | "import/prefer-default-export": 0, 58 | "react/no-danger": 0, 59 | "jsx-a11y/no-static-element-interactions": 0 60 | }, 61 | "parser": "babel-eslint", 62 | "parserOptions": { 63 | "sourceType": "module", 64 | "ecmaVersion": 8, 65 | "ecmaFeatures": { 66 | "jsx": true, 67 | "experimentalObjectRestSpread": true 68 | } 69 | }, 70 | "globals": { 71 | "document": true, 72 | "window": true 73 | }, 74 | "env": { 75 | "browser": true 76 | }, 77 | "settings": { 78 | "import/resolver": "node" 79 | } 80 | } -------------------------------------------------------------------------------- /fe/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | yarn.lock 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /fe/.roadhogrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { version } = require('./package.json') 3 | 4 | const svgSpriteDirs = [ 5 | path.resolve(__dirname, 'src/svg/'), 6 | require.resolve('antd').replace(/index\.js$/, ''), 7 | ] 8 | 9 | export default { 10 | entry: 'src/index.js', 11 | svgSpriteLoaderDirs: svgSpriteDirs, 12 | theme: "./theme.config.js", 13 | publicPath: `/${version}/`, 14 | outputPath: `./dist/${version}`, 15 | "extraBabelPlugins": ["transform-runtime"], 16 | "env": { 17 | "development": { 18 | "extraBabelPlugins": [ 19 | "dva-hmr", 20 | ["import", { 21 | "libraryName": "antd", 22 | "style": true 23 | }] 24 | ], 25 | "proxy": [{ 26 | "context": ['/api/v1', '/common'], 27 | "target": "http://localhost:7000/", 28 | "changeOrigin": true, 29 | "pathRewrite": { "^/api/v1": "" } 30 | }], 31 | }, 32 | "production": { 33 | "extraBabelPlugins": [ 34 | ["import", { "libraryName": "antd", "style": true }] 35 | ] 36 | } 37 | }, 38 | "dllPlugin": { 39 | exclude: ["babel-runtime", "roadhog", "cross-env"], 40 | inclue: ["dva/router", "dva/saga", "dva/fetch"] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /fe/LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2016 zuiidea (zuiiidea@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /fe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "antd-admin-springboot", 4 | "version": "4.3.4", 5 | "dependencies": { 6 | "antd": "^2.13.0", 7 | "array-to-tree": "^2.2.1", 8 | "axios": "^0.19.2", 9 | "babel-runtime": "^6.26.0", 10 | "classnames": "^2.2.5", 11 | "cross-env": "^5.0.5", 12 | "draftjs-to-markdown": "^0.4.4", 13 | "dva": "^2.0.1", 14 | "dva-loading": "^0.2.0", 15 | "ejs-loader": "^0.3.0", 16 | "echarts-for-react": "^1.4.4", 17 | "history": "^4.7.2", 18 | "js-beautify": "^1.6.14", 19 | "jsonp": "^0.2.1", 20 | "less-vars-to-js": "^1.1.2", 21 | "lodash": "^4.17.4", 22 | "moment": "^2.18.1", 23 | "nprogress": "^0.2.0", 24 | "qs": "^6.5.1", 25 | "query-string": "^5.0.0", 26 | "rc-tween-one": "^1.3.1", 27 | "react": "^15.6.1", 28 | "react-countup": "1.3.0", 29 | "react-dom": "^15.6.1", 30 | "react-draft-wysiwyg": "^1.10.8", 31 | "react-helmet": "^5.2.0" 32 | }, 33 | "devDependencies": { 34 | "babel-eslint": "^7.2.3", 35 | "babel-plugin-dev-expression": "^0.2.1", 36 | "babel-plugin-dva-hmr": "^0.3.2", 37 | "babel-plugin-import": "^1.4.0", 38 | "babel-plugin-transform-runtime": "^6.23.0", 39 | "babel-polyfill": "^6.26.0", 40 | "copy-webpack-plugin": "^4.0.1", 41 | "draftjs-to-html": "^0.7.5", 42 | "dva-model-extend": "^0.1.1", 43 | "eslint": "^4.1.1", 44 | "eslint-config-airbnb": "^15.0.2", 45 | "eslint-import-resolver-node": "^0.3.1", 46 | "eslint-loader": "^1.9.0", 47 | "eslint-plugin-import": "^2.6.1", 48 | "eslint-plugin-jsx-a11y": "^6.0.2", 49 | "eslint-plugin-react": "^7.1.0", 50 | "html-webpack-plugin": "^2.29.0", 51 | "mockjs": "^1.0.1-beta3", 52 | "rc-checkbox": "^2.0.3", 53 | "redbox-react": "^1.5.0", 54 | "roadhog": "^1.2.2", 55 | "recharts": "^0.19.0" 56 | }, 57 | "pre-commit": [ 58 | "lint" 59 | ], 60 | "scripts": { 61 | "dev": "cross-env BROWSER=none HOST=0.0.0.0 roadhog server", 62 | "lint": "eslint --fix --ext .js src", 63 | "build": "roadhog build", 64 | "build:dll": "roadhog buildDll" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /fe/src/components/DataTable/AnimTableBody.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { TweenOneGroup } from 'rc-tween-one' 4 | 5 | const enterAnim = [ 6 | { 7 | opacity: 0, 8 | x: 30, 9 | backgroundColor: '#fffeee', 10 | duration: 0, 11 | }, { 12 | height: 0, 13 | duration: 200, 14 | type: 'from', 15 | delay: 250, 16 | ease: 'easeOutQuad', 17 | onComplete: (e) => { 18 | e.target.style.height = 'auto' 19 | }, 20 | }, { 21 | opacity: 1, 22 | x: 0, 23 | duration: 250, 24 | ease: 'easeOutQuad', 25 | }, { 26 | delay: 1000, 27 | backgroundColor: '#fff', 28 | }, 29 | ] 30 | 31 | const leaveAnim = [ 32 | { 33 | duration: 250, 34 | x: -30, 35 | opacity: 0, 36 | }, { 37 | height: 0, 38 | duration: 200, 39 | ease: 'easeOutQuad', 40 | }, 41 | ] 42 | 43 | const AnimTableBody = ({ body, page = 1, current }) => { 44 | if (current !== +page) { 45 | return body 46 | } 47 | 48 | return ( 49 | 56 | {body.props.children} 57 | 58 | ) 59 | } 60 | 61 | AnimTableBody.propTypes = { 62 | body: PropTypes.element, 63 | page: PropTypes.any, 64 | current: PropTypes.number.isRequired, 65 | } 66 | 67 | export default AnimTableBody 68 | -------------------------------------------------------------------------------- /fe/src/components/DataTable/DataTable.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Table } from 'antd' 4 | import { request } from '../../utils' 5 | import lodash from 'lodash' 6 | import './DataTable.less' 7 | 8 | class DataTable extends React.Component { 9 | constructor(props) { 10 | super(props) 11 | const { 12 | dataSource, pagination = { 13 | showSizeChanger: true, 14 | showQuickJumper: true, 15 | showTotal: total => `共 ${total} 条`, 16 | current: 1, 17 | total: 100 18 | }, 19 | } = props 20 | this.state = { 21 | loading: false, 22 | dataSource, 23 | fetchData: {}, 24 | pagination, 25 | } 26 | } 27 | 28 | componentDidMount() { 29 | if (this.props.fetch) { 30 | this.fetch() 31 | } 32 | } 33 | 34 | componentWillReceiveProps(nextProps) { 35 | const staticNextProps = lodash.cloneDeep(nextProps) 36 | delete staticNextProps.columns 37 | const { columns, ...otherProps } = this.props 38 | 39 | if (!lodash.isEqual(staticNextProps, otherProps)) { 40 | this.props = nextProps 41 | this.fetch() 42 | } 43 | } 44 | 45 | handleTableChange = (pagination, filters, sorter) => { 46 | const pager = this.state.pagination 47 | pager.current = pagination.current 48 | this.setState({ 49 | pagination: pager, 50 | fetchData: { 51 | results: pagination.pageSize, 52 | page: pagination.current, 53 | sortField: sorter.field, 54 | sortOrder: sorter.order, 55 | ...filters, 56 | }, 57 | }, () => { 58 | this.fetch() 59 | }) 60 | } 61 | 62 | fetch = () => { 63 | const { fetch: { url, data, dataKey } } = this.props 64 | const { fetchData } = this.state 65 | this.setState({ loading: true }) 66 | this.promise = request({ 67 | url, 68 | data: { 69 | ...data, 70 | ...fetchData, 71 | }, 72 | }).then((result) => { 73 | if (!this.refs.DataTable) { 74 | return 75 | } 76 | const { pagination } = this.state 77 | pagination.total = result.total || pagination.total 78 | this.setState({ 79 | loading: false, 80 | dataSource: dataKey ? result[dataKey] : result.data, 81 | pagination, 82 | }) 83 | }) 84 | } 85 | 86 | render() { 87 | const { fetch, ...tableProps } = this.props 88 | const { loading, dataSource, pagination } = this.state 89 | 90 | return () 99 | } 100 | } 101 | 102 | 103 | DataTable.propTypes = { 104 | fetch: PropTypes.object, 105 | rowKey: PropTypes.string, 106 | pagination: React.PropTypes.oneOfType([ 107 | React.PropTypes.bool, 108 | React.PropTypes.object, 109 | ]), 110 | columns: PropTypes.array, 111 | dataSource: PropTypes.array, 112 | } 113 | 114 | export default DataTable 115 | -------------------------------------------------------------------------------- /fe/src/components/DataTable/DataTable.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javahuang/antd-admin-springboot/87186297e8676e320dc03d8271fe72f10fa60ff2/fe/src/components/DataTable/DataTable.less -------------------------------------------------------------------------------- /fe/src/components/DataTable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DataTable", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./DataTable.js" 6 | } 7 | -------------------------------------------------------------------------------- /fe/src/components/DownloadButton/icon.js: -------------------------------------------------------------------------------- 1 | module.exports = ( 2 | '' 3 | + '' 4 | + '' 5 | + '' 6 | + '' 7 | + '' 8 | ); 9 | -------------------------------------------------------------------------------- /fe/src/components/DownloadButton/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import DownloadIcon from './icon'; 4 | 5 | class DownloadButton extends Component { 6 | constructor() { 7 | super(); 8 | } 9 | 10 | render() { 11 | return ( 12 | 34 | ) 35 | } 36 | } 37 | 38 | export default DownloadButton; 39 | -------------------------------------------------------------------------------- /fe/src/components/DropOption/DropOption.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Dropdown, Button, Icon, Menu } from 'antd' 4 | 5 | const DropOption = ({ onMenuClick, menuOptions = [], buttonStyle, dropdownProps }) => { 6 | const menu = menuOptions.map(item => {item.name}) 7 | return ({menu}} 9 | {...dropdownProps} 10 | > 11 | 15 | ) 16 | } 17 | 18 | DropOption.propTypes = { 19 | onMenuClick: PropTypes.func, 20 | menuOptions: PropTypes.array.isRequired, 21 | buttonStyle: PropTypes.object, 22 | dropdownProps: PropTypes.object, 23 | } 24 | 25 | export default DropOption 26 | -------------------------------------------------------------------------------- /fe/src/components/DropOption/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DropOption", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./DropOption.js" 6 | } 7 | -------------------------------------------------------------------------------- /fe/src/components/Editor/Editor.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Editor } from 'react-draft-wysiwyg' 3 | import styles from './Editor.less' 4 | import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' 5 | 6 | 7 | const DraftEditor = (props) => { 8 | return () 9 | } 10 | 11 | export default DraftEditor 12 | -------------------------------------------------------------------------------- /fe/src/components/Editor/Editor.less: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | height: 500px; 3 | 4 | :global { 5 | .rdw-dropdownoption-default { 6 | padding: 6px; 7 | } 8 | 9 | .rdw-dropdown-optionwrapper { 10 | box-sizing: content-box; 11 | width: 100%; 12 | border-radius: 0 0 2px 2px; 13 | } 14 | 15 | .rdw-inline-wrapper { 16 | flex-wrap: wrap; 17 | margin-bottom: 0; 18 | 19 | .rdw-option-wrapper { 20 | margin-bottom: 6px; 21 | } 22 | } 23 | 24 | .rdw-option-active { 25 | box-shadow: 1px 1px 0 #e8e8e8 inset; 26 | } 27 | 28 | .rdw-dropdown-optionwrapper { 29 | &:hover { 30 | box-shadow: none; 31 | } 32 | } 33 | 34 | .rdw-colorpicker-option { 35 | box-shadow: none; 36 | } 37 | 38 | .rdw-colorpicker-modal, 39 | .rdw-embedded-modal, 40 | .rdw-emoji-modal, 41 | .rdw-image-modal, 42 | .rdw-link-modal { 43 | box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); 44 | } 45 | 46 | .rdw-colorpicker-modal, 47 | .rdw-embedded-modal, 48 | .rdw-link-modal { 49 | height: auto; 50 | } 51 | 52 | .rdw-emoji-modal { 53 | width: 214px; 54 | } 55 | 56 | .rdw-colorpicker-modal { 57 | width: auto; 58 | } 59 | 60 | .rdw-embedded-modal-btn, 61 | .rdw-image-modal-btn, 62 | .rdw-link-modal-btn { 63 | height: 32px; 64 | margin-top: 12px; 65 | } 66 | 67 | .rdw-embedded-modal-input, 68 | .rdw-embedded-modal-size-input, 69 | .rdw-link-modal-input { 70 | padding: 2px 6px; 71 | height: 32px; 72 | } 73 | 74 | .rdw-dropdown-selectedtext { 75 | color: #000; 76 | } 77 | 78 | .rdw-dropdown-wrapper, 79 | .rdw-option-wrapper { 80 | min-width: 36px; 81 | transition: all 0.2s ease; 82 | height: 30px; 83 | 84 | &:active { 85 | box-shadow: 1px 1px 0 #e8e8e8 inset; 86 | } 87 | 88 | &:hover { 89 | box-shadow: 1px 1px 0 #e8e8e8; 90 | } 91 | } 92 | 93 | .rdw-dropdown-wrapper { 94 | min-width: 60px; 95 | } 96 | 97 | .rdw-editor-main { 98 | box-sizing: border-box; 99 | } 100 | } 101 | 102 | .editor { 103 | border: 1px solid #F1F1F1; 104 | padding: 5px; 105 | border-radius: 2px; 106 | height: auto; 107 | min-height: 200px; 108 | } 109 | 110 | .toolbar { 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /fe/src/components/Editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Editor", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Editor.js" 6 | } 7 | -------------------------------------------------------------------------------- /fe/src/components/FilterItem/FilterItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styles from './FilterItem.less' 4 | 5 | const FilterItem = ({ 6 | label = '', 7 | children, 8 | }) => { 9 | const labelArray = label.split('') 10 | return ( 11 |
12 | {labelArray.length > 0 13 | ?
14 | {labelArray.map((item, index) => {item})} 15 |
16 | : ''} 17 |
18 | {children} 19 |
20 |
21 | ) 22 | } 23 | 24 | FilterItem.propTypes = { 25 | label: PropTypes.string, 26 | children: PropTypes.any, 27 | } 28 | 29 | export default FilterItem 30 | -------------------------------------------------------------------------------- /fe/src/components/FilterItem/FilterItem.less: -------------------------------------------------------------------------------- 1 | .filterItem { 2 | display: flex; 3 | justify-content: space-between; 4 | 5 | .labelWrap { 6 | width: 64px; 7 | line-height: 28px; 8 | margin-right: 12px; 9 | justify-content: space-between; 10 | display: flex; 11 | overflow: hidden; 12 | } 13 | 14 | .item { 15 | flex: 1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fe/src/components/FilterItem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FilterItem", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./FilterItem.js" 6 | } 7 | -------------------------------------------------------------------------------- /fe/src/components/Iconfont/Iconfont.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import './iconfont.less' 4 | 5 | const Iconfont = ({ type, colorful }) => { 6 | if (colorful) { 7 | return (
key} dataSource={data} /> 36 | } 37 | 38 | Browser.propTypes = { 39 | data: PropTypes.array, 40 | } 41 | 42 | export default Browser 43 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/browser.less: -------------------------------------------------------------------------------- 1 | .percent { 2 | text-align: right !important; 3 | } 4 | 5 | .name { 6 | text-align: left !important; 7 | } 8 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/comments.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Table, Tag } from 'antd' 4 | import styles from './comments.less' 5 | import { color } from '../../../utils' 6 | 7 | const status = { 8 | 1: { 9 | color: color.green, 10 | text: 'APPROVED', 11 | }, 12 | 2: { 13 | color: color.yellow, 14 | text: 'PENDING', 15 | }, 16 | 3: { 17 | color: color.red, 18 | text: 'REJECTED', 19 | }, 20 | } 21 | 22 | function Comments ({ data }) { 23 | const columns = [ 24 | { 25 | title: 'avatar', 26 | dataIndex: 'avatar', 27 | width: 48, 28 | className: styles.avatarcolumn, 29 | render: text => , 30 | }, { 31 | title: 'content', 32 | dataIndex: 'content', 33 | render: (text, it) => (
34 |
{it.name}
35 |

{it.content}

36 |
37 | {status[it.status].text} 38 | {it.date} 39 |
40 |
), 41 | }, 42 | ] 43 | return ( 44 |
45 |
key} dataSource={data.filter((item, key) => key < 3)} /> 46 | 47 | ) 48 | } 49 | 50 | Comments.propTypes = { 51 | data: PropTypes.array, 52 | } 53 | 54 | export default Comments 55 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/comments.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .comments { 4 | :global .ant-table-thead > tr > th { 5 | background: #fff; 6 | border-bottom: solid 1px @border-color-base; 7 | } 8 | 9 | .avatar { 10 | width: 48px; 11 | height: 48px; 12 | background-position: center; 13 | background-size: cover; 14 | border-radius: 50%; 15 | background: #f8f8f8; 16 | display: inline-block; 17 | } 18 | 19 | .content { 20 | text-align: left; 21 | color: #757575; 22 | } 23 | 24 | .date { 25 | color: #a3a3a3; 26 | line-height: 30px; 27 | } 28 | 29 | .daterow { 30 | display: flex; 31 | justify-content: space-between; 32 | } 33 | 34 | .name { 35 | font-size: 14px; 36 | color: #474747; 37 | text-align: left; 38 | } 39 | 40 | .avatarcolumn { 41 | vertical-align: top; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/completed.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import classnames from 'classnames' 4 | import styles from './completed.less' 5 | import { color } from '../../../utils' 6 | import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' 7 | 8 | function Completed ({ data }) { 9 | return ( 10 |
11 |
TEAM TOTAL COMPLETED
12 | 13 | 14 | { 16 | const { payload } = prop 17 | return (
    18 | {payload.map((item, key) =>
  • {item.value}
  • )} 19 |
) 20 | }} 21 | /> 22 | 23 | 24 | 25 | { 28 | const list = content.payload.map((item, key) =>
  • {`${item.name}:${item.value}`}
  • ) 29 | return (

    {content.label}

    30 |
      {list}
    31 |
    ) 32 | }} 33 | /> 34 | 35 | 36 |
    37 |
    38 |
    39 | ) 40 | } 41 | 42 | Completed.propTypes = { 43 | data: PropTypes.array, 44 | } 45 | 46 | export default Completed 47 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/completed.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .sales { 4 | .title { 5 | margin-left: 32px; 6 | font-size: 16px; 7 | } 8 | } 9 | 10 | .radiusdot { 11 | width: 12px; 12 | height: 12px; 13 | margin-right: 8px; 14 | border-radius: 50%; 15 | display: inline-block; 16 | } 17 | 18 | .legend { 19 | color: @dark-half; 20 | text-align: right; 21 | color: #999; 22 | font-size: 14px; 23 | 24 | li { 25 | height: 48px; 26 | line-height: 48px; 27 | display: inline-block; 28 | 29 | & + li { 30 | margin-left: 24px; 31 | } 32 | } 33 | } 34 | 35 | .tooltip { 36 | background: #fff; 37 | padding: 20px; 38 | font-size: 14px; 39 | 40 | .tiptitle { 41 | font-weight: 700; 42 | font-size: 16px; 43 | margin-bottom: 8px; 44 | } 45 | 46 | .tipitem { 47 | height: 32px; 48 | line-height: 32px; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/cpu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import CountUp from 'react-countup' 4 | import { LineChart, Line, XAxis, YAxis, CartesianGrid, ResponsiveContainer } from 'recharts' 5 | import { color } from '../../../utils' 6 | import styles from './cpu.less' 7 | 8 | const countUpProps = { 9 | start: 0, 10 | duration: 2.75, 11 | useEasing: true, 12 | useGrouping: true, 13 | separator: ',', 14 | } 15 | 16 | function Cpu ({ usage, space, cpu, data }) { 17 | return (
    18 |
    19 |
    20 |

    usage

    21 |

    26 |
    27 |
    28 |

    space

    29 |

    34 |
    35 |
    36 |

    cpu

    37 |

    42 |
    43 |
    44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
    ) 53 | } 54 | 55 | Cpu.propTypes = { 56 | data: PropTypes.array, 57 | usage: PropTypes.number, 58 | space: PropTypes.number, 59 | cpu: PropTypes.number, 60 | } 61 | 62 | export default Cpu 63 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/cpu.less: -------------------------------------------------------------------------------- 1 | .cpu { 2 | .number { 3 | display: flex; 4 | height: 64px; 5 | justify-content: space-between; 6 | margin-bottom: 32px; 7 | 8 | .item { 9 | text-align: center; 10 | height: 64px; 11 | width: 100%; 12 | position: relative; 13 | 14 | & + .item { 15 | &::before { 16 | content: ''; 17 | display: block; 18 | width: 1px; 19 | height: 40px; 20 | position: absolute; 21 | background: #f5f5f5; 22 | top: 12px; 23 | } 24 | } 25 | 26 | p { 27 | color: #757575; 28 | 29 | &:first-child { 30 | font-size: 16px; 31 | } 32 | 33 | &:last-child { 34 | font-size: 20px; 35 | font-weight: 700; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/index.js: -------------------------------------------------------------------------------- 1 | import NumberCard from './numberCard' 2 | import Quote from './quote' 3 | import Sales from './sales' 4 | import Weather from './weather' 5 | import RecentSales from './recentSales' 6 | import Comments from './comments' 7 | import Completed from './completed' 8 | import Browser from './browser' 9 | import Cpu from './cpu' 10 | import User from './user' 11 | 12 | export { NumberCard, Quote, Sales, Weather, RecentSales, Comments, Completed, Browser, Cpu, User } 13 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/numberCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Icon, Card } from 'antd' 4 | import CountUp from 'react-countup' 5 | import styles from './numberCard.less' 6 | 7 | function NumberCard ({ icon, color, title, number, countUp }) { 8 | return ( 9 | 10 | 11 |
    12 |

    {title || 'No Title'}

    13 |

    14 | 23 |

    24 |
    25 |
    26 | ) 27 | } 28 | 29 | NumberCard.propTypes = { 30 | icon: PropTypes.string, 31 | color: PropTypes.string, 32 | title: PropTypes.string, 33 | number: PropTypes.number, 34 | countUp: PropTypes.object, 35 | } 36 | 37 | export default NumberCard 38 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/numberCard.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .numberCard { 4 | padding: 32px; 5 | margin-bottom: 24px; 6 | cursor: pointer; 7 | 8 | .iconWarp { 9 | font-size: 54px; 10 | float: left; 11 | } 12 | 13 | .content { 14 | width: 100%; 15 | padding-left: 78px; 16 | 17 | .title { 18 | line-height: 16px; 19 | font-size: 16px; 20 | margin-bottom: 8px; 21 | height: 16px; 22 | .text-overflow(); 23 | } 24 | 25 | .number { 26 | line-height: 32px; 27 | font-size: 24px; 28 | height: 32px; 29 | .text-overflow(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/quote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styles from './quote.less' 4 | 5 | function Quote ({ name, content, title, avatar }) { 6 | return ( 7 |
    8 |
    9 | {content} 10 |
    11 |
    12 |
    13 |

    -{name}-

    14 |

    {title}

    15 |
    16 |
    17 |
    18 |
    19 | ) 20 | } 21 | 22 | Quote.propTypes = { 23 | name: PropTypes.string, 24 | content: PropTypes.string, 25 | title: PropTypes.string, 26 | avatar: PropTypes.string, 27 | } 28 | 29 | export default Quote 30 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/quote.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .quote { 4 | color: #fff; 5 | height: 100%; 6 | width: 100%; 7 | padding: 24px; 8 | font-size: 16px; 9 | font-weight: 700; 10 | 11 | .inner { 12 | text-overflow: ellipsis; 13 | word-wrap: normal; 14 | display: -webkit-box; 15 | -webkit-box-orient: vertical; 16 | -webkit-line-clamp: 4; 17 | overflow: hidden; 18 | text-indent: 24px; 19 | } 20 | 21 | .footer { 22 | position: relative; 23 | margin-top: 14px; 24 | 25 | .description { 26 | width: 100%; 27 | 28 | p { 29 | overflow: hidden; 30 | text-overflow: ellipsis; 31 | white-space: nowrap; 32 | margin-right: 64px; 33 | text-align: right; 34 | 35 | &:last-child { 36 | font-weight: 100; 37 | } 38 | } 39 | } 40 | 41 | .avatar { 42 | width: 48px; 43 | height: 48px; 44 | background-position: center; 45 | background-size: cover; 46 | border-radius: 50%; 47 | position: absolute; 48 | right: 0; 49 | top: 0; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/recentSales.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Table, Tag } from 'antd' 4 | import styles from './recentSales.less' 5 | import { color } from '../../../utils' 6 | 7 | const status = { 8 | 1: { 9 | color: color.green, 10 | text: 'SALE', 11 | }, 12 | 2: { 13 | color: color.yellow, 14 | text: 'REJECT', 15 | }, 16 | 3: { 17 | color: color.red, 18 | text: 'TAX', 19 | }, 20 | 4: { 21 | color: color.blue, 22 | text: 'EXTENDED', 23 | }, 24 | } 25 | 26 | function RecentSales ({ data }) { 27 | const columns = [ 28 | { 29 | title: 'NAME', 30 | dataIndex: 'name', 31 | }, { 32 | title: 'STATUS', 33 | dataIndex: 'status', 34 | render: text => {status[text].text}, 35 | }, { 36 | title: 'DATE', 37 | dataIndex: 'date', 38 | render: text => new Date(text).format('yyyy-MM-dd'), 39 | }, { 40 | title: 'PRICE', 41 | dataIndex: 'price', 42 | render: (text, it) => ${text}, 43 | }, 44 | ] 45 | return ( 46 |
    47 |
    key} dataSource={data.filter((item, key) => key < 5)} /> 48 | 49 | ) 50 | } 51 | 52 | RecentSales.propTypes = { 53 | data: PropTypes.array, 54 | } 55 | 56 | export default RecentSales 57 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/recentSales.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .recentsales { 4 | :global .ant-table-thead > tr > th { 5 | background: #fff; 6 | border-bottom: solid 1px @border-color-base; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/sales.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' 4 | import classnames from 'classnames' 5 | import styles from './sales.less' 6 | import { color } from '../../../utils' 7 | 8 | 9 | function Sales ({ data }) { 10 | return ( 11 |
    12 |
    Dashboard
    13 | 14 | 15 | { 17 | const { payload } = prop 18 | return (
      19 | {payload.map((item, key) =>
    • {item.value}
    • )} 20 |
    ) 21 | }} 22 | /> 23 | 24 | 25 | 26 | { 29 | const list = content.payload.map((item, key) =>
  • {`${item.name}:${item.value}`}
  • ) 31 | return

    {content.label}

    32 |
      {list}
    33 |
    34 | }} 35 | /> 36 | 37 | 38 | 39 |
    40 |
    41 |
    42 | ) 43 | } 44 | 45 | Sales.propTypes = { 46 | data: PropTypes.array, 47 | } 48 | 49 | export default Sales 50 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/sales.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .sales { 4 | .title { 5 | margin-left: 32px; 6 | font-size: 16px; 7 | } 8 | } 9 | 10 | .radiusdot { 11 | width: 12px; 12 | height: 12px; 13 | margin-right: 8px; 14 | border-radius: 50%; 15 | display: inline-block; 16 | } 17 | 18 | .legend { 19 | color: @dark-half; 20 | text-align: right; 21 | color: #999; 22 | font-size: 14px; 23 | 24 | li { 25 | height: 48px; 26 | line-height: 48px; 27 | display: inline-block; 28 | 29 | & + li { 30 | margin-left: 24px; 31 | } 32 | } 33 | } 34 | 35 | .tooltip { 36 | background: #fff; 37 | padding: 20px; 38 | font-size: 14px; 39 | 40 | .tiptitle { 41 | font-weight: 700; 42 | font-size: 16px; 43 | margin-bottom: 8px; 44 | } 45 | 46 | .tipitem { 47 | height: 32px; 48 | line-height: 32px; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/user-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javahuang/antd-admin-springboot/87186297e8676e320dc03d8271fe72f10fa60ff2/fe/src/routes/dashboard/components/user-background.png -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/user.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Button } from 'antd' 4 | import styles from './user.less' 5 | import CountUp from 'react-countup' 6 | import { color } from '../../../utils' 7 | const countUpProps = { 8 | start: 0, 9 | duration: 2.75, 10 | useEasing: true, 11 | useGrouping: true, 12 | separator: ',', 13 | } 14 | 15 | function User({ avatar, name, email, sales, sold }) { 16 | return (
    17 |
    18 |
    19 |
    20 |
    {name}
    21 |

    {email}

    22 |
    23 |
    24 |
    25 |
    26 |

    EARNING SALES

    27 |

    32 |
    33 |
    34 |

    ITEM SOLD

    35 |

    39 |
    40 |
    41 |
    42 | 43 |
    44 |
    ) 45 | } 46 | 47 | User.propTypes = { 48 | avatar: PropTypes.string, 49 | name: PropTypes.string, 50 | email: PropTypes.string, 51 | sales: PropTypes.number, 52 | sold: PropTypes.number, 53 | } 54 | 55 | export default User 56 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/user.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars.less"; 2 | 3 | .user { 4 | .header { 5 | display: flex; 6 | justify-content: center; 7 | text-align: center; 8 | color: #fff; 9 | height: 200px; 10 | background-image: ~ "url(./user-background.png)"; 11 | background-repeat: no-repeat; 12 | background-size: cover; 13 | align-items: center; 14 | 15 | .headerinner { 16 | z-index: 2; 17 | } 18 | 19 | &::after { 20 | content: ''; 21 | background: @purple; 22 | position: absolute; 23 | width: 100%; 24 | height: 200px; 25 | left: 0; 26 | top: 0; 27 | opacity: 0.4; 28 | z-index: 1; 29 | } 30 | 31 | .avatar { 32 | width: 64px; 33 | height: 64px; 34 | background-position: center; 35 | background-size: cover; 36 | border-radius: 50%; 37 | display: inline-block; 38 | } 39 | 40 | .name { 41 | font-size: 16px; 42 | } 43 | } 44 | 45 | .number { 46 | display: flex; 47 | height: 116px; 48 | justify-content: space-between; 49 | border-bottom: solid 1px #f5f5f5; 50 | 51 | .item { 52 | text-align: center; 53 | height: 116px; 54 | width: 100%; 55 | position: relative; 56 | padding: 30px 0; 57 | 58 | & + .item { 59 | &::before { 60 | content: ''; 61 | display: block; 62 | width: 1px; 63 | height: 116px; 64 | position: absolute; 65 | background: #f5f5f5; 66 | top: 0; 67 | } 68 | } 69 | 70 | p { 71 | color: #757575; 72 | 73 | &:first-child { 74 | font-size: 16px; 75 | } 76 | 77 | &:last-child { 78 | font-size: 20px; 79 | font-weight: 700; 80 | } 81 | } 82 | } 83 | } 84 | 85 | .footer { 86 | height: 116px; 87 | display: flex; 88 | justify-content: center; 89 | align-items: center; 90 | 91 | :global .ant-btn { 92 | color: @purple; 93 | border-color: @purple; 94 | padding: 6px 16px; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/weather.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Spin } from 'antd' 4 | import styles from './weather.less' 5 | 6 | function Weather ({ city, icon, dateTime, temperature, name, loading }) { 7 | return ( 8 | 9 |
    10 |
    11 |
    16 |

    {name}

    17 |
    18 |
    19 |

    {`${temperature}°`}

    20 |

    {city}, {dateTime}

    21 |
    22 |
    23 | ) 24 | } 25 | 26 | Weather.propTypes = { 27 | city: PropTypes.string, 28 | icon: PropTypes.string, 29 | dateTime: PropTypes.string, 30 | temperature: PropTypes.string, 31 | name: PropTypes.string, 32 | loading: PropTypes.bool, 33 | } 34 | 35 | export default Weather 36 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/components/weather.less: -------------------------------------------------------------------------------- 1 | @import "../../../themes/vars"; 2 | 3 | .weather { 4 | color: #fff; 5 | height: 204px; 6 | padding: 24px; 7 | justify-content: space-between; 8 | display: flex; 9 | font-size: 14px; 10 | 11 | .left { 12 | display: flex; 13 | flex-direction: column; 14 | width: 64px; 15 | padding-top: 55px; 16 | 17 | .icon { 18 | width: 64px; 19 | height: 64px; 20 | background-position: center; 21 | background-size: contain; 22 | } 23 | 24 | p { 25 | margin-top: 16px; 26 | } 27 | } 28 | 29 | .right { 30 | display: flex; 31 | flex-direction: column; 32 | width: 50%; 33 | 34 | .temperature { 35 | font-size: 36px; 36 | text-align: right; 37 | height: 64px; 38 | color: #fff; 39 | } 40 | 41 | .description { 42 | overflow: hidden; 43 | text-overflow: ellipsis; 44 | white-space: nowrap; 45 | text-align: right; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { connect } from 'dva' 4 | import { Row, Col, Card } from 'antd' 5 | import { NumberCard, Sales } from './components' 6 | 7 | function Dashboard ({ dashboard }) { 8 | const { sales, numbers } = dashboard 9 | const numberCards = numbers.map((item, key) => (
    10 | 11 | )) 12 | 13 | return ( 14 |
    15 | 16 | {numberCards} 17 |
    18 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | Dashboard.propTypes = { 32 | dashboard: PropTypes.object, 33 | } 34 | 35 | export default connect(({ dashboard }) => ({ dashboard }))(Dashboard) 36 | -------------------------------------------------------------------------------- /fe/src/routes/dashboard/index.less: -------------------------------------------------------------------------------- 1 | :global { 2 | .ant-card { 3 | border-radius: 0; 4 | margin-bottom: 24px; 5 | } 6 | } 7 | 8 | .weather { 9 | &:hover { 10 | box-shadow: 4px 4px 40px rgba(143, 201, 251, .6); 11 | } 12 | } 13 | 14 | .quote { 15 | &:hover { 16 | box-shadow: 4px 4px 40px rgba(246, 152, 153, .6); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fe/src/routes/error/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Icon } from 'antd' 3 | import styles from './index.less' 4 | 5 | const Error = () => (
    6 |
    7 | 8 |

    404 Not Found

    9 |
    10 |
    ) 11 | 12 | export default Error 13 | -------------------------------------------------------------------------------- /fe/src/routes/error/index.less: -------------------------------------------------------------------------------- 1 | .error { 2 | color: black; 3 | text-align: center; 4 | position: absolute; 5 | top: 30%; 6 | margin-top: -50px; 7 | left: 50%; 8 | margin-left: -100px; 9 | width: 200px; 10 | 11 | :global .anticon { 12 | font-size: 48px; 13 | margin-bottom: 16px; 14 | } 15 | 16 | h1 { 17 | font-family: cursive; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fe/src/routes/login/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { connect } from 'dva' 4 | import { Icon, Button, Checkbox, Row, Form, Input, Tabs } from 'antd' 5 | import { config } from 'utils' 6 | import styles from './index.less' 7 | 8 | const FormItem = Form.Item 9 | const TabPane = Tabs.TabPane 10 | 11 | const Login = ({ 12 | loading, 13 | dispatch, 14 | form: { 15 | getFieldDecorator, 16 | validateFieldsAndScroll, 17 | }, 18 | }) => { 19 | function handleOk () { 20 | validateFieldsAndScroll((errors, values) => { 21 | if (errors) { 22 | return 23 | } 24 | dispatch({ type: 'login/login', payload: values }) 25 | }) 26 | } 27 | 28 | return ( 29 |
    30 |
    31 |
    32 | 33 | 34 |
    35 | 36 | {getFieldDecorator('username', { 37 | rules: [{ required: true, message: '用户名不能为空' }], 38 | })( 39 | } onPressEnter={handleOk} placeholder="用户名" /> 40 | )} 41 | 42 | 43 | {getFieldDecorator('password', { 44 | rules: [{ required: true, message: '密码不能为空' }], 45 | })(} size="large" type="password" onPressEnter={handleOk} placeholder="密码" />)} 46 | 47 | 48 | {getFieldDecorator('rememberMe', { 49 | valuePropName: 'checked', 50 | initialValue: true, 51 | })( 52 | 记住我 53 | )} 54 | 忘记密码 55 | 56 | 57 | 60 | 61 | 62 | 63 |
    64 | 65 | {'qrcode'} 66 | 67 |
    68 |
    69 |
    70 |
    71 | ) 72 | } 73 | 74 | Login.propTypes = { 75 | form: PropTypes.object, 76 | dispatch: PropTypes.func, 77 | loading: PropTypes.object, 78 | } 79 | 80 | export default connect(({ loading }) => ({ loading }))(Form.create()(Login)) 81 | -------------------------------------------------------------------------------- /fe/src/routes/login/index.less: -------------------------------------------------------------------------------- 1 | 2 | .form { 3 | border-radius: 4px; 4 | background-color: rgba(25, 48, 65, 0.54); 5 | position: absolute; 6 | top: 50%; 7 | right: 120px; 8 | margin: -160px 0 0 -160px; 9 | width: 320px; 10 | height: 320px; 11 | padding: 36px; 12 | box-shadow: 0 0 100px rgba(0, 0, 0, .08); 13 | 14 | button { 15 | width: 100%; 16 | } 17 | 18 | p { 19 | color: rgb(204, 204, 204); 20 | text-align: center; 21 | margin-top: 16px; 22 | 23 | span { 24 | &:first-child { 25 | margin-right: 16px; 26 | } 27 | } 28 | } 29 | 30 | :global { 31 | .ant-tabs .ant-tabs-nav { 32 | width: 100% !important; 33 | 34 | div { 35 | color: #ded4d4; 36 | width: 50%; 37 | } 38 | 39 | } 40 | } 41 | } 42 | 43 | .ant-spin-container, 44 | .ant-spin-nested-loading { 45 | height: 100%; 46 | } 47 | 48 | .tab { 49 | text-align: center; 50 | } 51 | 52 | .color { 53 | color: #ded4d4; 54 | } 55 | 56 | .bgImage { 57 | background: url("../../public/bg_image.jpg") round; 58 | } 59 | -------------------------------------------------------------------------------- /fe/src/routes/sys/account/Filter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Form, Button, Row, Col, Input, AutoComplete } from 'antd' 4 | 5 | 6 | const ColProps = { 7 | xs: 24, 8 | sm: 12, 9 | style: { 10 | marginBottom: 16, 11 | }, 12 | } 13 | 14 | const Filter = ({ 15 | nameDataSource, 16 | onFilterChange, 17 | onAdd, 18 | changeNameDataSource, 19 | filter, 20 | form: { 21 | getFieldDecorator, 22 | getFieldsValue, 23 | setFieldsValue, 24 | }, 25 | }) => { 26 | const handleSubmit = () => { 27 | let fields = getFieldsValue() 28 | onFilterChange(fields) 29 | } 30 | 31 | const handlerSearch = (val) => { 32 | setFieldsValue({ searchId: '' }) 33 | changeNameDataSource(val) 34 | } 35 | 36 | const handlerSelect = (val) => { 37 | setFieldsValue({ searchId: val }) 38 | } 39 | 40 | 41 | const { name } = filter 42 | 43 | return ( 44 | 45 |
    46 | {getFieldDecorator('searchName', { initialValue: name })( 47 | 55 | )} 56 | {getFieldDecorator('searchId')( 57 | 58 | )} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ) 68 | } 69 | 70 | Filter.propTypes = { 71 | onAdd: PropTypes.func, 72 | isMotion: PropTypes.bool, 73 | switchIsMotion: PropTypes.func, 74 | form: PropTypes.object, 75 | filter: PropTypes.object, 76 | onFilterChange: PropTypes.func, 77 | nameDataSource: PropTypes.array, 78 | changeNameDataSource: PropTypes.func, 79 | } 80 | 81 | export default Form.create()(Filter) 82 | -------------------------------------------------------------------------------- /fe/src/routes/sys/account/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Table, Modal } from 'antd' 4 | import classnames from 'classnames' 5 | import { Link } from 'dva/router' 6 | import queryString from 'query-string' 7 | import AnimTableBody from '../../../components/DataTable/AnimTableBody' 8 | import { DropOption } from '../../../components' 9 | import styles from './List.less' 10 | 11 | const confirm = Modal.confirm 12 | 13 | const List = ({ onDeleteItem, onEditItem, isMotion, location, ...tableProps }) => { 14 | location.query = queryString.parse(location.search) 15 | const handleMenuClick = (record, e) => { 16 | if (e.key === '1') { 17 | onEditItem(record) 18 | } else if (e.key === '2') { 19 | confirm({ 20 | title: '确定删除当前数据?', 21 | onOk () { 22 | onDeleteItem(record.id) 23 | }, 24 | }) 25 | } 26 | } 27 | 28 | const columns = [ 29 | { 30 | title: 'id', 31 | dataIndex: 'id', 32 | key: 'id', 33 | }, 34 | { 35 | title: '姓名', 36 | dataIndex: 'name', 37 | key: 'name', 38 | render: (text, record) => {text}, 39 | }, { 40 | title: '登录账号', 41 | dataIndex: 'loginName', 42 | key: 'loginName', 43 | }, { 44 | title: '邮箱', 45 | dataIndex: 'email', 46 | key: 'email', 47 | }, { 48 | title: '是否管理员', 49 | dataIndex: 'isAdmin', 50 | key: 'isAdmin', 51 | render: text => ({text 52 | ? '是' 53 | : '否'}), 54 | }, { 55 | title: '角色列表', 56 | dataIndex: 'roleNames', 57 | key: 'roleNames', 58 | }, { 59 | title: '创建日期', 60 | dataIndex: 'createDatetime', 61 | key: 'createDatetime', 62 | render: text => (text ? new Date(text).format('yyyy-MM-dd') : ''), 63 | }, 64 | { 65 | title: '操作', 66 | key: 'operation', 67 | width: 100, 68 | render: (text, record) => ( 69 | handleMenuClick(record, e)} menuOptions={[{ key: '1', name: '修改' }, { key: '2', name: '删除' }]} /> 70 | ), 71 | }, 72 | ] 73 | 74 | const getBodyWrapperProps = { 75 | page: location.query.page, 76 | current: tableProps.pagination.current, 77 | } 78 | 79 | const getBodyWrapper = (body) => { 80 | return isMotion ? : body 81 | } 82 | 83 | return ( 84 |
    85 |
    record.id} 92 | getBodyWrapper={getBodyWrapper} 93 | /> 94 | 95 | ) 96 | } 97 | 98 | List.propTypes = { 99 | onDeleteItem: PropTypes.func, 100 | onEditItem: PropTypes.func, 101 | isMotion: PropTypes.bool, 102 | location: PropTypes.object, 103 | } 104 | 105 | export default List 106 | -------------------------------------------------------------------------------- /fe/src/routes/sys/account/List.less: -------------------------------------------------------------------------------- 1 | .table { 2 | td.avatar { 3 | img { 4 | border-radius: 50%; 5 | } 6 | } 7 | 8 | :global { 9 | .ant-table-tbody > tr > td, 10 | .ant-table-thead > tr > th { 11 | height: 62px; 12 | } 13 | } 14 | 15 | &.motion { 16 | :global { 17 | .ant-table-tbody > tr > td, 18 | .ant-table-thead > tr > th { 19 | &:nth-child(1) { 20 | display: none; 21 | } 22 | 23 | &:nth-child(2) { 24 | width: 10%; 25 | } 26 | 27 | &:nth-child(3) { 28 | width: 10%; 29 | } 30 | 31 | &:nth-child(4) { 32 | width: 15%; 33 | } 34 | 35 | &:nth-child(5) { 36 | width: 10%; 37 | } 38 | &:nth-child(6) { 39 | width: 25%; 40 | } 41 | &:nth-child(7) { 42 | width: 15%; 43 | } 44 | &:nth-child(8) { 45 | width: 15%; 46 | } 47 | } 48 | 49 | .ant-table-thead { 50 | & > tr { 51 | transition: none; 52 | display: block; 53 | 54 | & > th { 55 | display: inline-flex; 56 | align-items: center; 57 | justify-content: center; 58 | } 59 | } 60 | } 61 | 62 | .ant-table-tbody { 63 | & > tr { 64 | transition: none; 65 | display: block; 66 | border-bottom: 1px solid #f5f5f5; 67 | 68 | & > td { 69 | border-bottom: none; 70 | display: inline-flex; 71 | align-items: center; 72 | justify-content: center; 73 | } 74 | 75 | &.ant-table-expanded-row-level-1 > td { 76 | height: auto; 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | .hidden { 85 | display: none !important; 86 | } 87 | -------------------------------------------------------------------------------- /fe/src/routes/sys/account/Modal.less: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /fe/src/routes/sys/menu/Form.less: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /fe/src/routes/sys/menu/Tree.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Tree, Input, Popover, Button, Icon, Popconfirm } from 'antd' 4 | import arrToTree from 'array-to-tree' 5 | 6 | 7 | const TreeNode = Tree.TreeNode 8 | const Search = Input.Search 9 | 10 | 11 | const MenuTree = ({ 12 | autoExpandParent, expandedKeys, onExpand, onDrop, showOrHideForm, searchValue, onKeyChange, 13 | onAdd, onEdit, onSearch, deleteItem, treeData, 14 | }) => { 15 | const onSelect = (selectKeys) => { 16 | let currKey = selectKeys[0] 17 | onKeyChange(currKey) 18 | } 19 | 20 | // 如果操作框打开,则隐藏form 21 | const onVisibleChange = (visible) => { 22 | if (visible) { 23 | showOrHideForm(false) 24 | } 25 | } 26 | 27 | const content = (item) => { 28 | return ( 29 |
    30 |

    31 | 32 |

    33 |

    34 | 35 |

    36 |

    37 | 38 | 39 | 40 |

    41 | 44 |
    45 | ) 46 | } 47 | 48 | const searchPermission = (e) => { 49 | const value = e.target.value 50 | onSearch(value) 51 | } 52 | 53 | const loop = data => data.map((item) => { 54 | const index = item.name.search(searchValue) 55 | const beforeStr = item.name.substr(0, index) 56 | const afterStr = item.name.substr(index + searchValue.length) 57 | const title = index > -1 ? ( 58 | 59 | 60 | 61 | {beforeStr} 62 | {searchValue} 63 | {afterStr} 64 | 65 | 66 | ) : {item.name} 67 | if (item.children) { 68 | return ( 69 | 70 | {loop(item.children)} 71 | 72 | ) 73 | } 74 | return 75 | }) 76 | 77 | 78 | return ( 79 |
    80 | 81 | 90 | {loop(arrToTree(treeData, { parentProperty: 'mpid', rootID: 1 }))} 91 | 92 |
    93 | ) 94 | } 95 | 96 | MenuTree.propTypes = { 97 | autoExpandParent: PropTypes.any, 98 | expandedKeys: PropTypes.any, 99 | onExpand: PropTypes.any, 100 | onDrop: PropTypes.any, 101 | showOrHideForm: PropTypes.any, 102 | searchValue: PropTypes.any, 103 | onKeyChange: PropTypes.any, 104 | onAdd: PropTypes.any, 105 | onEdit: PropTypes.any, 106 | onSearch: PropTypes.any, 107 | deleteItem: PropTypes.any, 108 | treeData: PropTypes.any, 109 | } 110 | 111 | export default MenuTree 112 | -------------------------------------------------------------------------------- /fe/src/routes/sys/menu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Row, Col } from 'antd' 4 | import { connect } from 'dva' 5 | import MenuForm from './Form' 6 | import MenuTree from './Tree' 7 | 8 | 9 | const Menu = ({ location, dispatch, menu, loading }) => { 10 | const { 11 | expandedKeys, searchValue, autoExpandParent, 12 | treeData, menuPermissionTreeData, currItem, formVisible, 13 | } = menu 14 | 15 | const treeProps = { 16 | treeData, 17 | currItem, 18 | formVisible, 19 | expandedKeys, 20 | onKeyChange (key) { 21 | dispatch({ 22 | type: 'menu/change', 23 | payload: { 24 | key, 25 | }, 26 | }) 27 | }, 28 | showOrHideForm (showOrHide) { 29 | dispatch({ 30 | type: 'menu/showOrHideForm', 31 | payload: { 32 | showOrHide, 33 | }, 34 | }) 35 | }, 36 | onAdd (isRoot) { 37 | dispatch({ 38 | type: 'menu/onAdd', 39 | payload: { 40 | isRoot, 41 | }, 42 | }) 43 | }, 44 | onEdit () { 45 | dispatch({ 46 | type: 'menu/onEdit', 47 | }) 48 | }, 49 | deleteItem () { 50 | dispatch({ 51 | type: 'menu/delete', 52 | }) 53 | }, 54 | onSearch (val) { 55 | dispatch({ 56 | type: 'menu/onSearch', 57 | payload: { 58 | searchValue: val, 59 | }, 60 | }) 61 | }, 62 | onExpand (keys) { 63 | dispatch({ 64 | type: 'menu/onExpand', 65 | payload: { 66 | expandedKeys: keys, 67 | }, 68 | }) 69 | }, 70 | onDrop (info) { 71 | const dropKey = info.node.props.eventKey 72 | const dragKey = info.dragNode.props.eventKey 73 | const dropPos = info.node.props.pos.split('-') 74 | const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]) 75 | dispatch({ 76 | type: 'menu/sort', 77 | payload: { 78 | dropKey, 79 | dragKey, 80 | dropPosition, 81 | }, 82 | }) 83 | }, 84 | searchValue, 85 | autoExpandParent, 86 | loading: loading.effects['menu/query'], 87 | location, 88 | } 89 | 90 | 91 | const formProps = { 92 | currItem, 93 | treeData, 94 | menuPermissionTreeData, 95 | loading: loading.effects[`${currItem.id ? 'menu/update' : 'menu/create'}`], 96 | onOk (data) { 97 | if (data.id) { 98 | dispatch({ 99 | type: 'menu/update', 100 | payload: data, 101 | }) 102 | } else { 103 | dispatch({ 104 | type: 'menu/create', 105 | payload: data, 106 | }) 107 | } 108 | }, 109 | } 110 | 111 | return ( 112 |
    113 | 114 |
    115 | 116 | {formVisible && } 117 | 118 | 119 | 120 | ) 121 | } 122 | 123 | Menu.propTypes = { 124 | menu: PropTypes.object, 125 | location: PropTypes.object, 126 | dispatch: PropTypes.func, 127 | loading: PropTypes.object, 128 | } 129 | 130 | export default connect(({ menu, loading }) => ({ menu, loading }))(Menu) 131 | -------------------------------------------------------------------------------- /fe/src/routes/sys/permission/Form.less: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /fe/src/routes/sys/permission/Tree.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Tree, Input, Popover, Button, Popconfirm } from 'antd' 4 | import arrToTree from 'array-to-tree' 5 | 6 | 7 | const TreeNode = Tree.TreeNode 8 | const Search = Input.Search 9 | 10 | 11 | const PermissionTree = ({ 12 | autoExpandParent, expandedKeys, onExpand, onDrop, showOrHideForm, searchValue, onKeyChange, 13 | onAdd, onEdit, onSearch, deleteItem, treeData, 14 | }) => { 15 | const onSelect = (selectKeys) => { 16 | let currKey = selectKeys[0] 17 | onKeyChange(currKey) 18 | } 19 | 20 | // 如果操作框打开,则隐藏form 21 | const onVisibleChange = (visible) => { 22 | if (visible) { 23 | showOrHideForm(false) 24 | } 25 | } 26 | 27 | const content = (item) => { 28 | return ( 29 |
    30 |

    31 | 32 |

    33 |

    34 | 35 |

    36 |

    37 | 38 | 39 | 40 |

    41 | 44 |
    45 | ) 46 | } 47 | 48 | const searchPermission = (e) => { 49 | const value = e.target.value 50 | onSearch(value) 51 | } 52 | 53 | const loop = data => data.map((item) => { 54 | const index = item.name.search(searchValue) 55 | const beforeStr = item.name.substr(0, index) 56 | const afterStr = item.name.substr(index + searchValue.length) 57 | const title = index > -1 ? ( 58 | 59 | 60 | {beforeStr} 61 | {searchValue} 62 | {afterStr} 63 | 64 | 65 | ) : {item.name} 66 | if (item.children) { 67 | return ( 68 | 69 | {loop(item.children)} 70 | 71 | ) 72 | } 73 | return 74 | }) 75 | 76 | return ( 77 |
    78 | 79 | 88 | {loop(arrToTree(treeData, { parentProperty: 'parentId', rootID: 1 }))} 89 | 90 |
    91 | ) 92 | } 93 | 94 | PermissionTree.propTypes = { 95 | autoExpandParent: PropTypes.any, 96 | expandedKeys: PropTypes.any, 97 | onExpand: PropTypes.func, 98 | onDrop: PropTypes.func, 99 | showOrHideForm: PropTypes.any, 100 | searchValue: PropTypes.any, 101 | onKeyChange: PropTypes.any, 102 | onAdd: PropTypes.any, 103 | onEdit: PropTypes.any, 104 | onSearch: PropTypes.any, 105 | deleteItem: PropTypes.any, 106 | treeData: PropTypes.any, 107 | } 108 | 109 | export default PermissionTree 110 | -------------------------------------------------------------------------------- /fe/src/routes/sys/permission/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Row, Col } from 'antd' 4 | import { connect } from 'dva' 5 | import PermissionForm from './Form' 6 | import PermissionTree from './Tree' 7 | 8 | 9 | const Permission = ({ location, dispatch, permission, loading }) => { 10 | const { 11 | expandedKeys, searchValue, autoExpandParent, 12 | treeData, currItem, formVisible, 13 | } = permission 14 | 15 | const treeProps = { 16 | treeData, 17 | currItem, 18 | formVisible, 19 | expandedKeys, 20 | onKeyChange (key) { 21 | dispatch({ 22 | type: 'permission/change', 23 | payload: { 24 | key, 25 | }, 26 | }) 27 | }, 28 | showOrHideForm (showOrHide) { 29 | dispatch({ 30 | type: 'permission/showOrHideForm', 31 | payload: { 32 | showOrHide, 33 | }, 34 | }) 35 | }, 36 | onAdd (isRoot) { 37 | dispatch({ 38 | type: 'permission/onAdd', 39 | payload: { 40 | isRoot, 41 | }, 42 | }) 43 | }, 44 | onEdit () { 45 | dispatch({ 46 | type: 'permission/onEdit', 47 | }) 48 | }, 49 | deleteItem () { 50 | dispatch({ 51 | type: 'permission/delete', 52 | }) 53 | }, 54 | onSearch (val) { 55 | dispatch({ 56 | type: 'permission/onSearch', 57 | payload: { 58 | searchValue: val, 59 | }, 60 | }) 61 | }, 62 | onExpand (keys) { 63 | dispatch({ 64 | type: 'permission/onExpand', 65 | payload: { 66 | expandedKeys: keys, 67 | }, 68 | }) 69 | }, 70 | onDrop (info) { 71 | const dropKey = info.node.props.eventKey 72 | const dragKey = info.dragNode.props.eventKey 73 | const dropPos = info.node.props.pos.split('-') 74 | const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]) 75 | dispatch({ 76 | type: 'permission/sort', 77 | payload: { 78 | dropKey, 79 | dragKey, 80 | dropPosition, 81 | }, 82 | }) 83 | }, 84 | searchValue, 85 | autoExpandParent, 86 | loading: loading.effects['permission/query'], 87 | location, 88 | } 89 | 90 | 91 | const formProps = { 92 | currItem, 93 | treeData, 94 | loading: loading.effects[`${currItem.id ? 'permission/update' : 'permission/create'}`], 95 | onOk (data) { 96 | if (data.id) { 97 | dispatch({ 98 | type: 'permission/update', 99 | payload: data, 100 | }) 101 | } else { 102 | dispatch({ 103 | type: 'permission/create', 104 | payload: data, 105 | }) 106 | } 107 | }, 108 | } 109 | 110 | return ( 111 |
    112 | 113 |
    114 | 115 | {formVisible && } 116 | 117 | 118 | 119 | ) 120 | } 121 | 122 | Permission.propTypes = { 123 | permission: PropTypes.object, 124 | location: PropTypes.object, 125 | dispatch: PropTypes.func, 126 | loading: PropTypes.object, 127 | } 128 | 129 | export default connect(({ permission, loading }) => ({ permission, loading }))(Permission) 130 | -------------------------------------------------------------------------------- /fe/src/routes/sys/role/Filter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Form, Button, Row, Col, Input, AutoComplete } from 'antd' 4 | 5 | /** 6 | * autoComplete 插件动态数据源只能通过修改 state 7 | */ 8 | 9 | const ColProps = { 10 | xs: 24, 11 | sm: 12, 12 | style: { 13 | marginBottom: 16, 14 | }, 15 | } 16 | 17 | const Filter = ({ 18 | nameDataSource, 19 | onFilterChange, 20 | onAdd, 21 | changeNameDataSource, 22 | filter, 23 | form: { 24 | getFieldDecorator, 25 | getFieldsValue, 26 | setFieldsValue, 27 | }, 28 | }) => { 29 | const handleSubmit = () => { 30 | let fields = getFieldsValue() 31 | onFilterChange(fields) 32 | } 33 | 34 | const handlerSearch = (val) => { 35 | setFieldsValue({ searchId: '' }) 36 | changeNameDataSource(val) 37 | } 38 | 39 | const handlerSelect = (val) => { 40 | setFieldsValue({ searchId: val }) 41 | } 42 | 43 | 44 | const { name } = filter 45 | 46 | return ( 47 | 48 | 49 | {getFieldDecorator('searchName', { initialValue: name })( 50 | 58 | )} 59 | {getFieldDecorator('searchId')( 60 | 61 | )} 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | 73 | Filter.propTypes = { 74 | onAdd: PropTypes.func, 75 | isMotion: PropTypes.bool, 76 | switchIsMotion: PropTypes.func, 77 | form: PropTypes.object, 78 | filter: PropTypes.object, 79 | onFilterChange: PropTypes.func, 80 | nameDataSource: PropTypes.any, 81 | changeNameDataSource: PropTypes.func, 82 | } 83 | 84 | export default Form.create()(Filter) 85 | -------------------------------------------------------------------------------- /fe/src/routes/sys/role/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Table, Modal } from 'antd' 4 | import classnames from 'classnames' 5 | import AnimTableBody from '../../../components/DataTable/AnimTableBody' 6 | import { DropOption } from '../../../components' 7 | import styles from './List.less' 8 | 9 | const confirm = Modal.confirm 10 | 11 | const List = ({ onDeleteItem, onEditItem, isMotion, location, ...tableProps }) => { 12 | const handleMenuClick = (record, e) => { 13 | if (e.key === '1') { 14 | onEditItem(record) 15 | } else if (e.key === '2') { 16 | confirm({ 17 | title: '确定删除当前数据?', 18 | onOk () { 19 | onDeleteItem(record.id) 20 | }, 21 | }) 22 | } 23 | } 24 | 25 | const columns = [ 26 | { 27 | title: 'id', 28 | dataIndex: 'id', 29 | key: 'id', 30 | }, 31 | { 32 | title: '名称', 33 | dataIndex: 'name', 34 | key: 'name', 35 | }, 36 | { 37 | title: '权限列表', 38 | dataIndex: 'permissionNames', 39 | key: 'permissionNames', 40 | }, { 41 | title: '创建日期', 42 | dataIndex: 'createTime', 43 | key: 'createTime', 44 | render: text => (text ? new Date(text).format('yyyy-MM-dd') : ''), 45 | }, { 46 | title: '操作', 47 | key: 'operation', 48 | width: 100, 49 | render: (text, record) => ( 50 | handleMenuClick(record, e)} menuOptions={[{ key: '1', name: '修改' }, { key: '2', name: '删除' }]} /> 51 | ), 52 | }, 53 | ] 54 | 55 | const getBodyWrapperProps = { 56 | page: location.query.page, 57 | current: tableProps.pagination.current, 58 | } 59 | 60 | const getBodyWrapper = body => (isMotion ? : body) 61 | 62 | return ( 63 |
    64 |
    record.id} 71 | getBodyWrapper={getBodyWrapper} 72 | /> 73 | 74 | ) 75 | } 76 | 77 | List.propTypes = { 78 | onDeleteItem: PropTypes.func, 79 | onEditItem: PropTypes.func, 80 | isMotion: PropTypes.bool, 81 | location: PropTypes.object, 82 | } 83 | 84 | export default List 85 | -------------------------------------------------------------------------------- /fe/src/routes/sys/role/List.less: -------------------------------------------------------------------------------- 1 | .table { 2 | td.avatar { 3 | img { 4 | border-radius: 50%; 5 | } 6 | } 7 | 8 | :global { 9 | .ant-table-tbody > tr > td, 10 | .ant-table-thead > tr > th { 11 | height: 62px; 12 | } 13 | } 14 | 15 | &.motion { 16 | :global { 17 | .ant-table-tbody > tr > td, 18 | .ant-table-thead > tr > th { 19 | &:nth-child(1) { 20 | display: none; 21 | } 22 | 23 | &:nth-child(2) { 24 | width: 30%; 25 | } 26 | 27 | &:nth-child(3) { 28 | width: 20%; 29 | } 30 | 31 | &:nth-child(4) { 32 | width: 30%; 33 | } 34 | 35 | &:nth-child(5) { 36 | width: 20%; 37 | } 38 | } 39 | 40 | .ant-table-thead { 41 | & > tr { 42 | transition: none; 43 | display: block; 44 | 45 | & > th { 46 | display: inline-flex; 47 | align-items: center; 48 | justify-content: center; 49 | } 50 | } 51 | } 52 | 53 | .ant-table-tbody { 54 | & > tr { 55 | transition: none; 56 | display: block; 57 | border-bottom: 1px solid #f5f5f5; 58 | 59 | & > td { 60 | border-bottom: none; 61 | display: inline-flex; 62 | align-items: center; 63 | justify-content: center; 64 | } 65 | 66 | &.ant-table-expanded-row-level-1 > td { 67 | height: auto; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | .hidden { 76 | display: none !important; 77 | } 78 | -------------------------------------------------------------------------------- /fe/src/routes/sys/role/Modal.less: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /fe/src/services/app.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../utils' 2 | 3 | const { api } = config 4 | const { user, userLogout, userLogin } = api 5 | 6 | export async function login (params) { 7 | return request({ 8 | url: userLogin, 9 | method: 'post', 10 | data: params, 11 | }) 12 | } 13 | 14 | export async function logout (params) { 15 | return request({ 16 | url: userLogout, 17 | method: 'get', 18 | data: params, 19 | }) 20 | } 21 | 22 | export async function query (params) { 23 | return request({ 24 | url: user.replace('/:id', ''), 25 | method: 'get', 26 | data: params, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /fe/src/services/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by huangrupeng on 17/5/24. 3 | */ 4 | import { request, config } from '../utils' 5 | 6 | 7 | const { api } = config 8 | const { common } = api 9 | const { autoComplete, select } = common 10 | 11 | export async function queryAutoComplete (params) { 12 | return request({ 13 | url: autoComplete, 14 | method: 'get', 15 | data: params, 16 | }) 17 | } 18 | 19 | export async function querySelect (params) { 20 | return request({ 21 | url: select, 22 | method: 'get', 23 | data: params, 24 | }) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /fe/src/services/dashboard.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../utils' 2 | 3 | const { api } = config 4 | const { dashboard } = api 5 | 6 | export async function query (params) { 7 | return request({ 8 | url: dashboard, 9 | method: 'get', 10 | data: params, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /fe/src/services/login.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../utils' 2 | 3 | const { api } = config 4 | const { userLogin } = api 5 | 6 | export async function login (data) { 7 | return request({ 8 | url: userLogin, 9 | method: 'post', 10 | data, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /fe/src/services/sys/account.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { accounts } = sys 6 | 7 | export async function query (params) { 8 | return request({ 9 | url: accounts, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function create (params) { 16 | return request({ 17 | url: accounts.replace('/:id', ''), 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | 23 | export async function remove (params) { 24 | return request({ 25 | url: accounts, 26 | method: 'delete', 27 | data: params, 28 | }) 29 | } 30 | 31 | export async function update (params) { 32 | return request({ 33 | url: accounts, 34 | method: 'patch', 35 | data: params, 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /fe/src/services/sys/dataSync.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { dataSync, loginLog } = sys 6 | 7 | export async function query (params) { 8 | return request({ 9 | url: dataSync, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function createOrUpdate (params) { 16 | return request({ 17 | url: dataSync, 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | 23 | export async function queryLogs (params) { 24 | return request({ 25 | url: `${dataSync}/:syncId/logs`, 26 | method: 'get', 27 | data: params, 28 | }) 29 | } 30 | export async function queryLoginLog (params) { 31 | return request({ 32 | url: loginLog, 33 | method: 'get', 34 | data: params, 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /fe/src/services/sys/menu.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { menus } = sys 6 | 7 | export async function query (params) { 8 | return request({ 9 | url: menus, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function create (params) { 16 | return request({ 17 | url: menus.replace('/:id', ''), 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | 23 | export async function remove (params) { 24 | return request({ 25 | url: menus, 26 | method: 'delete', 27 | data: params, 28 | }) 29 | } 30 | 31 | export async function update (params) { 32 | return request({ 33 | url: menus, 34 | method: 'patch', 35 | data: params, 36 | }) 37 | } 38 | 39 | export async function sort (params) { 40 | return request({ 41 | url: `${menus}/sort`, 42 | method: 'patch', 43 | data: params, 44 | }) 45 | } 46 | 47 | -------------------------------------------------------------------------------- /fe/src/services/sys/permission.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { permissions } = sys 6 | 7 | export default async function query (params) { 8 | return request({ 9 | url: permissions, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function create (params) { 16 | return request({ 17 | url: permissions.replace('/:id', ''), 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | 23 | export async function remove (params) { 24 | return request({ 25 | url: permissions, 26 | method: 'delete', 27 | data: params, 28 | }) 29 | } 30 | 31 | export async function update (params) { 32 | return request({ 33 | url: permissions, 34 | method: 'patch', 35 | data: params, 36 | }) 37 | } 38 | 39 | export async function sort (params) { 40 | return request({ 41 | url: `${permissions}/sort`, 42 | method: 'patch', 43 | data: params, 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /fe/src/services/sys/role.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { roles, permissions } = sys 6 | 7 | export default async function query (params) { 8 | return request({ 9 | url: roles, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function create (params) { 16 | return request({ 17 | url: roles.replace('/:id', ''), 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | 23 | export async function remove (params) { 24 | return request({ 25 | url: roles, 26 | method: 'delete', 27 | data: params, 28 | }) 29 | } 30 | 31 | export async function update (params) { 32 | return request({ 33 | url: roles, 34 | method: 'patch', 35 | data: params, 36 | }) 37 | } 38 | 39 | export async function queryPermissions (params) { 40 | return request({ 41 | url: permissions, 42 | method: 'get', 43 | data: params, 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /fe/src/services/sys/schedule.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../../utils' 2 | 3 | const { api } = config 4 | const { sys } = api 5 | const { schedule } = sys 6 | 7 | export async function query (params) { 8 | return request({ 9 | url: schedule, 10 | method: 'get', 11 | data: params, 12 | }) 13 | } 14 | 15 | export async function update (params) { 16 | return request({ 17 | url: schedule, 18 | method: 'post', 19 | data: params, 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /fe/src/services/user.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../utils' 2 | 3 | const { api } = config 4 | const { user } = api 5 | 6 | export async function query (params) { 7 | return request({ 8 | url: user, 9 | method: 'get', 10 | data: params, 11 | }) 12 | } 13 | 14 | export async function create (params) { 15 | return request({ 16 | url: user.replace('/:id', ''), 17 | method: 'post', 18 | data: params, 19 | }) 20 | } 21 | 22 | export async function remove (params) { 23 | return request({ 24 | url: user, 25 | method: 'delete', 26 | data: params, 27 | }) 28 | } 29 | 30 | export async function update (params) { 31 | return request({ 32 | url: user, 33 | method: 'patch', 34 | data: params, 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /fe/src/services/users.js: -------------------------------------------------------------------------------- 1 | import { request, config } from '../utils' 2 | 3 | const { api } = config 4 | const { users } = api 5 | 6 | export async function query (params) { 7 | return request({ 8 | url: users, 9 | method: 'get', 10 | data: params, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /fe/src/services/weather.js: -------------------------------------------------------------------------------- 1 | import { request } from '../utils' 2 | 3 | export async function query (params) { 4 | params.key = 'i7sau1babuzwhycn' 5 | return request({ 6 | url: '/weather/now.json', 7 | method: 'get', 8 | data: params, 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /fe/src/svg/emoji/smirking.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 13 | 16 | 19 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /fe/src/svg/emoji/surprised.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /fe/src/svg/emoji/tired.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 13 | 15 | 16 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /fe/src/svg/emoji/unamused.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 13 | 16 | 19 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /fe/src/svg/emoji/wink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 13 | 14 | 17 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fe/src/themes/default.less: -------------------------------------------------------------------------------- 1 | // 本文件是对 ant-design: 2 | // https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less 3 | // 相应变量值的覆盖 4 | // 注意:只需写出要覆盖的变量即可(不需要覆盖的变量不要写) 5 | // http://www.iconfont.cn/collections/detail?cid=790 6 | @import "../../node_modules/antd/lib/style/themes/default.less"; 7 | @border-radius-base: 3px; 8 | @border-radius-sm: 2px; 9 | @shadow-color: rgba(0,0,0,0.05); 10 | @shadow-1-down: 4px 4px 40px @shadow-color; 11 | @border-color-split: #f4f4f4; 12 | @border-color-base: #e5e5e5; 13 | @menu-dark-bg: #3e3e3e; 14 | @text-color: #666; 15 | @font-family: "AvenirNext-Regular", "Helvetica Neue", "lucida grande", "PingFangHK-Light", "STHeiti", "Heiti SC", "Hiragino Sans GB", "Microsoft JhengHei", "Microsoft Yahei", SimHei, "WenQuanYi Micro Hei", "Droid Sans", "Roboto", Helvetica, Tahoma, Arial, "sans-serif"; 16 | @icon-url: "/antd/iconfont"; 17 | -------------------------------------------------------------------------------- /fe/src/themes/index.less: -------------------------------------------------------------------------------- 1 | @import "~themes/vars"; 2 | 3 | body { 4 | height: 100%; 5 | overflow-y: hidden; 6 | background-color: #f8f8f8; 7 | } 8 | 9 | ::-webkit-scrollbar-thumb { 10 | background-color: #e6e6e6; 11 | } 12 | 13 | ::-webkit-scrollbar { 14 | width: 8px; 15 | height: 8px; 16 | } 17 | 18 | :global { 19 | .ant-breadcrumb { 20 | & > span { 21 | &:last-child { 22 | color: #999; 23 | font-weight: normal; 24 | } 25 | } 26 | } 27 | 28 | .ant-breadcrumb-link { 29 | .anticon + span { 30 | margin-left: 4px; 31 | } 32 | } 33 | 34 | .ant-table { 35 | .ant-table-thead > tr > th { 36 | text-align: center; 37 | } 38 | 39 | .ant-table-tbody > tr > td { 40 | text-align: center; 41 | } 42 | 43 | &.ant-table-small { 44 | .ant-table-thead > tr > th { 45 | background: #f7f7f7; 46 | } 47 | 48 | .ant-table-body > table { 49 | padding: 0; 50 | } 51 | } 52 | } 53 | 54 | .ant-table-pagination { 55 | float: none!important; 56 | display: table; 57 | margin: 16px auto !important; 58 | } 59 | 60 | .ant-popover-inner { 61 | border: none; 62 | border-radius: 0; 63 | box-shadow: 0 0 20px rgba(100, 100, 100, 0.2); 64 | } 65 | 66 | .vertical-center-modal { 67 | display: flex; 68 | align-items: center; 69 | justify-content: center; 70 | 71 | .ant-modal { 72 | top: 0; 73 | 74 | .ant-modal-body { 75 | max-height: 80vh; 76 | overflow-y: auto; 77 | } 78 | } 79 | } 80 | 81 | .ant-form-item-control { 82 | vertical-align: middle; 83 | } 84 | 85 | .ant-modal-mask { 86 | background-color: rgba(55, 55, 55, 0.2); 87 | } 88 | 89 | .ant-modal-content { 90 | box-shadow: none; 91 | } 92 | 93 | .ant-select-dropdown-menu-item { 94 | padding: 12px 16px !important; 95 | } 96 | 97 | .margin-right { 98 | margin-right: 16px; 99 | } 100 | 101 | a:focus { 102 | text-decoration: none; 103 | } 104 | } 105 | @media (min-width: 1600px) { 106 | :global { 107 | .ant-col-xl-48 { 108 | width: 20%; 109 | } 110 | 111 | .ant-col-xl-96 { 112 | width: 40%; 113 | } 114 | } 115 | } 116 | @media (max-width: 767px) { 117 | :global { 118 | .ant-pagination-item, 119 | .ant-pagination-next, 120 | .ant-pagination-options, 121 | .ant-pagination-prev { 122 | margin-bottom: 8px; 123 | } 124 | 125 | .ant-card { 126 | .ant-card-head { 127 | padding: 0 12px; 128 | } 129 | 130 | .ant-card-body { 131 | padding: 12px; 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /fe/src/themes/mixin.less: -------------------------------------------------------------------------------- 1 | @import "./default.less"; // antd-admin 2 | @dark-half: #494949; 3 | @purple: #d897eb; 4 | @shadow-1: 4px 4px 20px 0 rgba(0, 0, 0, 0.01); 5 | @shadow-2: 4px 4px 40px 0 rgba(0, 0, 0, 0.05); 6 | @transition-ease-in: all 0.3s ease-out; 7 | @transition-ease-out: all 0.3s ease-out; 8 | @ease-in: ease-in; 9 | 10 | .text-overflow { 11 | white-space: nowrap; 12 | text-overflow: ellipsis; 13 | overflow: hidden; 14 | } 15 | -------------------------------------------------------------------------------- /fe/src/themes/vars.less: -------------------------------------------------------------------------------- 1 | @import "./default.less"; 2 | @import "./mixin.less"; 3 | -------------------------------------------------------------------------------- /fe/src/utils/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'antd admin spring-boot', 3 | version: '0.9.0', 4 | prefix: 'antdAdmin', 5 | footerText: 'Copyright © 2017. 进击的大黄 . All rights reserved', 6 | logo: '/logo.png', 7 | qrCode: '/QR_code.jpg', 8 | iconFontCSS: '/iconfont.css', 9 | iconFontJS: '/iconfont.js', 10 | CORS: [], 11 | openPages: ['/login'], 12 | api: { 13 | userLogin: '/user/login', 14 | userLogout: '/user/logout', 15 | userInfo: '/userInfo', 16 | users: '/users', 17 | user: '/user/:id', 18 | weather: '/weather', 19 | dashboard: '/dashboard', 20 | common: { 21 | autoComplete: '/common/combox/autoComplete', 22 | select: '/common/combox/select', 23 | upload: '/common/file/upload', 24 | preview: '/common/file/preview', 25 | download: '/common/file/download', 26 | }, 27 | sys: { 28 | accounts: '/sys/accounts', 29 | menus: '/sys/menus', 30 | permissions: '/sys/permissions', 31 | roles: '/sys/roles', 32 | }, 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /fe/src/utils/date.js: -------------------------------------------------------------------------------- 1 | 2 | Date.prototype.format = function() { 3 | let s = ''; 4 | const mouth = (this.getMonth() + 1)>=10?(this.getMonth() + 1):('0'+(this.getMonth() + 1)); 5 | const day = this.getDate()>=10?this.getDate():('0'+this.getDate()); 6 | s += this.getFullYear() + '-'; // 获取年份。 7 | s += mouth + "-"; // 获取月份。 8 | s += day; // 获取日。 9 | return (s); // 返回日期。 10 | }; 11 | 12 | /** 13 | * 获取两个日期(格式:yyyy-MM-dd)内所有的日期 14 | * @param begin 15 | * @param end 16 | */ 17 | const getAllByDateStr = (begin, end) => { 18 | const ab = begin.split("-"); 19 | const ae = end.split("-"); 20 | let db = new Date(); 21 | db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]); 22 | let de = new Date(); 23 | de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]); 24 | const unixDb = db.getTime(); 25 | const unixDe = de.getTime(); 26 | const result = []; 27 | for (let k = unixDb; k <= unixDe;) { 28 | result.push((new Date(parseInt(k))).format()); 29 | k = k + 24 * 60 * 60 * 1000; 30 | } 31 | return result; 32 | }; 33 | 34 | /** 35 | * 根据传入的时间毫秒数获取两个时间内所有的天数 36 | * @param begin 37 | * @param end 38 | */ 39 | const getAllByMilliSecones = (begin, end) => { 40 | const beginStr = (new Date(parseInt(begin))).format(); 41 | const endStr = (new Date(parseInt(end))).format(); 42 | const ab = beginStr.split("-"); 43 | const ae = endStr.split("-"); 44 | let db = new Date(); 45 | db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]); 46 | let de = new Date(); 47 | de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]); 48 | const unixDb = db.getTime(); 49 | const unixDe = de.getTime(); 50 | const result = []; 51 | for (let k = unixDb; k <= unixDe;) { 52 | result.push((new Date(parseInt(k))).format()); 53 | k = k + 24 * 60 * 60 * 1000; 54 | } 55 | return result; 56 | }; 57 | 58 | module.exports = { 59 | getAllByDateStr, 60 | getAllByMilliSecones, 61 | }; 62 | -------------------------------------------------------------------------------- /fe/src/utils/theme.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | color: { 3 | green: '#64ea91', 4 | blue: '#8fc9fb', 5 | purple: '#d897eb', 6 | red: '#f69899', 7 | yellow: '#f8c82e', 8 | peach: '#f797d6', 9 | borderBase: '#e5e5e5', 10 | borderSplit: '#f4f4f4', 11 | grass: '#d6fbb5', 12 | sky: '#c1e0fc', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /fe/theme.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const lessToJs = require('less-vars-to-js') 4 | 5 | module.exports = () => { 6 | const themePath = path.join(__dirname, './src/themes/default.less') 7 | return lessToJs(fs.readFileSync(themePath, 'utf8')) 8 | } 9 | -------------------------------------------------------------------------------- /fe/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | const CopyWebpackPlugin = require('copy-webpack-plugin') 3 | const webpack = require('webpack') 4 | 5 | module.exports = (webpackConfig, env) => { 6 | const production = env === 'production' 7 | // FilenameHash 8 | webpackConfig.output.chunkFilename = '[name].[chunkhash].js' 9 | 10 | if (production) { 11 | if (webpackConfig.module) { 12 | // ClassnameHash 13 | webpackConfig.module.rules.map((item) => { 14 | if (String(item.test) === '/\\.less$/' || String(item.test) === '/\\.css/') { 15 | item.use.filter(iitem => iitem.loader === 'css')[0].options.localIdentName = '[hash:base64:5]' 16 | } 17 | return item 18 | }) 19 | } 20 | webpackConfig.plugins.push( 21 | new webpack.LoaderOptionsPlugin({ 22 | minimize: true, 23 | debug: false, 24 | }) 25 | ) 26 | } 27 | 28 | webpackConfig.plugins = webpackConfig.plugins.concat([ 29 | new CopyWebpackPlugin([ 30 | { 31 | from: 'src/public', 32 | to: production ? '../' : webpackConfig.output.outputPath, 33 | }, 34 | ]), 35 | new HtmlWebpackPlugin({ 36 | template: `${__dirname}/src/entry.ejs`, 37 | filename: production ? '../index.html' : 'index.html', 38 | minify: production ? { 39 | collapseWhitespace: true, 40 | } : null, 41 | hash: true, 42 | headScripts: production ? null : ['/roadhog.dll.js'], 43 | }), 44 | new webpack.DefinePlugin({ 45 | BASE_URL: production ? JSON.stringify('') : JSON.stringify('http://localhost:8000/api/v1'), 46 | }), 47 | ]) 48 | 49 | // Alias 50 | webpackConfig.resolve.alias = { 51 | components: `${__dirname}/src/components`, 52 | utils: `${__dirname}/src/utils`, 53 | config: `${__dirname}/src/utils/config`, 54 | enums: `${__dirname}/src/utils/enums`, 55 | services: `${__dirname}/src/services`, 56 | models: `${__dirname}/src/models`, 57 | routes: `${__dirname}/src/routes`, 58 | themes: `${__dirname}/src/themes`, 59 | } 60 | 61 | return webpackConfig 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/Application.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.web.support.SpringBootServletInitializer; 7 | import org.springframework.transaction.annotation.EnableTransactionManagement; 8 | 9 | /** 10 | * Description: 11 | *
    12 |  * 
    13 | * Author: javahuang 14 | * Create: 17/9/16 上午3:59 15 | */ 16 | @SpringBootApplication 17 | @EnableTransactionManagement 18 | public class Application extends SpringBootServletInitializer { 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(Application.class, args); 22 | } 23 | 24 | @Override 25 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 26 | return application.sources(Application.class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.io.Resource; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; 10 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | 13 | /** 14 | * Description: 15 | *
    16 |  * 
    17 | * Author: javahuang 18 | * Create: 17/9/16 上午3:59 19 | */ 20 | @Configuration 21 | @RestController 22 | public class WebConfig extends WebMvcConfigurerAdapter { 23 | 24 | @Value("classpath:/static/index.html") 25 | private Resource indexHtml; 26 | 27 | // 匹配类型的静态资源都会被 ResourceHandler 来处理 28 | private static final String[] STATIC_RESOURCES = { 29 | "/**/*.css", "/**/*.js", 30 | "/**/*.jpg", "/**/*.png", "/**/*.svg", // 图片 31 | "/**/*.eot", "/**/*.ttf", "/**/*.woff" // 字体文件 32 | }; 33 | 34 | @Override 35 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 36 | registry.setOrder(-1) // 设置静态资源映射优先级高于下面配置的 @GetMapping 37 | .addResourceHandler(STATIC_RESOURCES) 38 | .addResourceLocations("classpath:/static/"); 39 | } 40 | 41 | @Override 42 | public void configurePathMatch(PathMatchConfigurer configurer) { 43 | configurer.setUseSuffixPatternMatch(false); // 默认 /index 会匹配 /index.js 44 | } 45 | 46 | /** 47 | * @return 48 | */ 49 | @GetMapping 50 | public Object index() { 51 | return ResponseEntity.ok().body(indexHtml); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/constant/AppConsts.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.constant; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  * 
    7 | * Author: javahuang 8 | * Create: 17/9/16 上午4:17 9 | */ 10 | public class AppConsts { 11 | /** 12 | * 系统里面所有 true 对应 1,数据库表字段类型为 number(1) 13 | */ 14 | public static final Short TRUE = 1; 15 | /** 16 | * 系统里面所有 false 对应 0,数据库表字段类型为 number(1) 17 | */ 18 | public static final Short FALSE = 0; 19 | 20 | /** 21 | * 菜单 22 | */ 23 | public static final Byte IS_RESOURCE_MENU = 1; 24 | /** 25 | * 有路由配置,但不在导航栏显示的菜单 26 | */ 27 | public static final String SUB_MENU = "submenu"; 28 | /** 29 | * 面包屑菜单导航父节点 ID 30 | */ 31 | public static final String MENU_ROOT_BPID = "4dcab7f2ad5f4166996de3ff910ac8c2"; 32 | 33 | /** 34 | * 登录URI 35 | */ 36 | public static final String QUERY_USER_URI = "/user"; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.exception; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  * 
    7 | * Author: javahuang 8 | * Create: 17/9/16 上午4:30 9 | */ 10 | public class ServiceException extends RuntimeException{ 11 | 12 | public ServiceException() { 13 | super(); 14 | } 15 | 16 | public ServiceException(String message) { 17 | super(message); 18 | } 19 | 20 | public ServiceException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | public ServiceException(Throwable cause) { 25 | super(cause); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/exception/ValidatorException.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.exception; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  *     表单远程校验异常
     7 |  * 
    8 | * Author: javahuang 9 | * Create: 17/9/26 下午5:38 10 | */ 11 | public class ValidatorException extends RuntimeException { 12 | 13 | public ValidatorException() { 14 | } 15 | 16 | public ValidatorException(String message) { 17 | super(message); 18 | } 19 | 20 | public ValidatorException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | public ValidatorException(Throwable cause) { 25 | super(cause); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/mvc/advice/MyResponseBodyAdvice.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.mvc.advice; 2 | 3 | import com.github.pagehelper.Page; 4 | import com.google.common.collect.Maps; 5 | import me.hrps.aas.core.mybatis.domain.TableResponse; 6 | import org.springframework.core.MethodParameter; 7 | import org.springframework.core.io.Resource; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.http.server.ServerHttpRequest; 10 | import org.springframework.http.server.ServerHttpResponse; 11 | import org.springframework.web.bind.annotation.ControllerAdvice; 12 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * Description: 19 | *
    20 |  *    包装返回资源 匹配前端
    21 |  * 
    22 | * Author: javahuang 23 | * Create: 17/9/16 上午4:21 24 | */ 25 | @ControllerAdvice 26 | public class MyResponseBodyAdvice implements ResponseBodyAdvice{ 27 | 28 | @Override 29 | public boolean supports(MethodParameter returnType, Class converterType) { 30 | return true; 31 | } 32 | 33 | @Override 34 | public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { 35 | // 静态页面无需拦截 36 | if (body instanceof Resource) { 37 | return body; 38 | } 39 | if (body == null) { 40 | return null; 41 | } 42 | // 分页 43 | if (body instanceof Page) { 44 | Page page = (Page) body; 45 | TableResponse result = new TableResponse<>(); 46 | result.setTotal(page.getTotal()); 47 | result.setList(page); 48 | return result; 49 | } 50 | // 如果返回的是数组,前端通过 data.array 来获取 51 | if (body instanceof List || body.getClass().isArray()) { 52 | Map result = Maps.newHashMap(); 53 | result.put("array", body); 54 | return result; 55 | } 56 | return body; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/mvc/advice/ServiceExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.mvc.advice; 2 | 3 | import com.google.common.collect.Maps; 4 | import me.hrps.aas.core.exception.ServiceException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.jdbc.UncategorizedSQLException; 12 | import org.springframework.web.HttpMediaTypeNotSupportedException; 13 | import org.springframework.web.bind.annotation.ExceptionHandler; 14 | import org.springframework.web.bind.annotation.RestControllerAdvice; 15 | import org.springframework.web.context.request.ServletWebRequest; 16 | import org.springframework.web.context.request.WebRequest; 17 | import sun.security.validator.ValidatorException; 18 | 19 | import java.util.Map; 20 | 21 | /** 22 | * Description: 23 | *
    24 |  *     统一处理所有的异常,方便业务类直接处理
    25 |  * 
    26 | * Author: javahuang 27 | * Create: 17/9/16 上午4:28 28 | */ 29 | @RestControllerAdvice 30 | public class ServiceExceptionHandler { 31 | 32 | private Logger logger = LoggerFactory.getLogger(ServiceException.class); 33 | 34 | private final Environment env; 35 | 36 | @Autowired 37 | public ServiceExceptionHandler(Environment env) { 38 | this.env = env; 39 | } 40 | 41 | @ExceptionHandler(Exception.class) 42 | public ResponseEntity handlerError(Exception ex, WebRequest request) { 43 | Map resp = Maps.newHashMap(); 44 | HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; 45 | String url = ((ServletWebRequest) request).getRequest().getRequestURI(); // 当前请求url 46 | String msg = env.getProperty("err.default"); 47 | if (ex instanceof UncategorizedSQLException) { 48 | // sql 异常 49 | msg = env.getProperty("err.sql"); 50 | } else if (ex instanceof ValidatorException) { 51 | // 表单校验异常 52 | msg = ex.getMessage(); 53 | status = HttpStatus.BAD_REQUEST; 54 | } else if (ex instanceof HttpMediaTypeNotSupportedException) { 55 | // 媒体类型不被支持 56 | } else { 57 | msg = ex.getMessage(); 58 | } 59 | ex.printStackTrace(); 60 | resp.put("message", msg); 61 | logger.error("url:{},error:{}", url, ex.getMessage()); 62 | return ResponseEntity.status(status).body(resp); 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/mybatis/domain/PageFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.mybatis.domain; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  * 
    7 | * Author: javahuang 8 | * Create: 2017/9/26 下午1:00 9 | */ 10 | public class PageFilter { 11 | 12 | // 当前页码 13 | private Integer page = 1; 14 | // 分页数量 15 | private Integer pageSize = 10; 16 | 17 | public Integer getPage() { 18 | return page; 19 | } 20 | 21 | public void setPage(Integer page) { 22 | this.page = page; 23 | } 24 | 25 | public Integer getPageSize() { 26 | return pageSize; 27 | } 28 | 29 | public void setPageSize(Integer pageSize) { 30 | this.pageSize = pageSize; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/mybatis/domain/TableResponse.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.mybatis.domain; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  *     封装的页面表格数据
     9 |  * 
    10 | * Author: javahuang 11 | * Create: 2017/9/26 下午1:01 12 | */ 13 | public class TableResponse { 14 | 15 | private Long total; 16 | private List list; 17 | 18 | public Long getTotal() { 19 | return total; 20 | } 21 | 22 | public void setTotal(Long total) { 23 | this.total = total; 24 | } 25 | 26 | public List getList() { 27 | return list; 28 | } 29 | 30 | public void setList(List list) { 31 | this.list = list; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/shiro/domain/ShiroUser.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.shiro.domain; 2 | 3 | import me.hrps.aas.web.sys.vo.MenuVO; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | /** 9 | * Description: 10 | *
    11 |  * 
    12 | * Author: javahuang 13 | * Create: 17/9/16 上午4:04 14 | */ 15 | public class ShiroUser implements Serializable{ 16 | 17 | private String accountId; 18 | private String loginName; 19 | private String name; 20 | private Boolean isAdmin; 21 | private transient List roleIdList; 22 | private transient List menus; 23 | private transient List roles; 24 | private transient List permissions; 25 | 26 | 27 | public String getAccountId() { 28 | return accountId; 29 | } 30 | 31 | public void setAccountId(String accountId) { 32 | this.accountId = accountId; 33 | } 34 | 35 | public String getLoginName() { 36 | return loginName; 37 | } 38 | 39 | public void setLoginName(String loginName) { 40 | this.loginName = loginName; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public boolean isAdmin() { 52 | return isAdmin; 53 | } 54 | 55 | public void setAdmin(Boolean admin) { 56 | isAdmin = admin; 57 | } 58 | 59 | public List getRoleIdList() { 60 | return roleIdList; 61 | } 62 | 63 | public void setRoleIdList(List roleIdList) { 64 | this.roleIdList = roleIdList; 65 | } 66 | 67 | public List getMenus() { 68 | return menus; 69 | } 70 | 71 | public void setMenus(List menus) { 72 | this.menus = menus; 73 | } 74 | 75 | public List getRoles() { 76 | return roles; 77 | } 78 | 79 | public void setRoles(List roles) { 80 | this.roles = roles; 81 | } 82 | 83 | public List getPermissions() { 84 | return permissions; 85 | } 86 | 87 | public void setPermissions(List permissions) { 88 | this.permissions = permissions; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/shiro/filter/MyFormAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.shiro.filter; 2 | 3 | import org.apache.shiro.authc.AuthenticationException; 4 | import org.apache.shiro.authc.AuthenticationToken; 5 | import org.apache.shiro.subject.Subject; 6 | import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 7 | 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | 11 | /** 12 | * Description: 13 | *
    14 |  *
    15 |  * 
    16 | * Author: javahuang 17 | * Create: 17/9/16 上午4:05 18 | */ 19 | public class MyFormAuthenticationFilter extends FormAuthenticationFilter { 20 | 21 | @Override 22 | protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { 23 | AuthenticationToken token = createToken(request, response); 24 | if (token == null) { 25 | String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " + 26 | "must be created in order to execute a login attempt."; 27 | throw new IllegalStateException(msg); 28 | } 29 | try { 30 | Subject subject = getSubject(request, response); 31 | subject.login(token); 32 | // 初始化授权操作 33 | subject.isPermitted(" init"); 34 | return onLoginSuccess(token, subject, request, response); 35 | } catch (AuthenticationException e) { 36 | //throw e; 37 | return onLoginFailure(token, e, request, response); 38 | } 39 | } 40 | 41 | protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, 42 | ServletRequest request, ServletResponse response) throws Exception { 43 | //we handled the success redirect directly, prevent the chain from continuing: 44 | return false; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/shiro/filter/MyLogoutFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.shiro.filter; 2 | 3 | import org.apache.shiro.session.SessionException; 4 | import org.apache.shiro.subject.Subject; 5 | import org.apache.shiro.web.filter.authc.LogoutFilter; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | 12 | /** 13 | * Description: 14 | *
    15 |  * 
    16 | * Author: javahuang 17 | * Create: 17/9/18 下午10:34 18 | */ 19 | public class MyLogoutFilter extends LogoutFilter { 20 | 21 | private Logger log = LoggerFactory.getLogger(MyLogoutFilter.class); 22 | 23 | @Override 24 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 25 | Subject subject = getSubject(request, response); 26 | //try/catch added for SHIRO-298: 27 | try { 28 | subject.logout(); 29 | } catch (SessionException ise) { 30 | log.debug("Encountered session exception during logout. This can generally safely be ignored.", ise); 31 | } 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/shiro/filter/MyUserFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.shiro.filter; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.apache.shiro.subject.Subject; 5 | import org.apache.shiro.web.filter.authc.UserFilter; 6 | import org.apache.shiro.web.servlet.ShiroHttpServletRequest; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.util.AntPathMatcher; 9 | 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * Description: 16 | *
    17 |  * 
    18 | * Author: javahuang 19 | * Create: 17/9/16 上午4:05 20 | */ 21 | public class MyUserFilter extends UserFilter { 22 | 23 | /** 24 | * 所有的静态资源请求都允许通过 25 | * 26 | * @param request 27 | * @param response 28 | * @param mappedValue 29 | * @return 30 | */ 31 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 32 | if (isLoginRequest(request, response) || isStaticResourceRequest(request)) { 33 | return true; 34 | } else { 35 | Subject subject = getSubject(request, response); 36 | return subject.getPrincipal() != null; 37 | } 38 | } 39 | 40 | @Override 41 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { 42 | HttpServletResponse httpServletResponse = (HttpServletResponse) response; 43 | httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); 44 | return false; 45 | } 46 | 47 | /** 48 | * 静态资源请求都允许通过 49 | * 50 | * @return 51 | */ 52 | private boolean isStaticResourceRequest(ServletRequest request) { 53 | String url = ((ShiroHttpServletRequest) request).getRequestURI(); 54 | if ("/".equals(url)) { 55 | return true; 56 | } 57 | String[] staticResources = 58 | {".html", ".js", ".css", 59 | ".eot", ".svg", ".ttf", ".woff", 60 | ".png", ".jpg", ".ico"}; 61 | if (StringUtils.endsWithAny(url, staticResources)) { 62 | return true; 63 | } 64 | AntPathMatcher matcher = new AntPathMatcher(); 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/shiro/realm/ShiroDbRealm.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.shiro.realm; 2 | 3 | import me.hrps.aas.core.shiro.domain.ShiroUser; 4 | import me.hrps.aas.web.sys.service.AccountService; 5 | import org.apache.shiro.authc.*; 6 | import org.apache.shiro.authz.AuthorizationInfo; 7 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 8 | import org.apache.shiro.realm.AuthorizingRealm; 9 | import org.apache.shiro.subject.PrincipalCollection; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | /** 13 | * Description: 14 | *
    15 |  *
    16 |  * 
    17 | * Author: javahuang 18 | * Create: 17/5/16 下午12:59 19 | */ 20 | public class ShiroDbRealm extends AuthorizingRealm { 21 | 22 | @Autowired 23 | private AccountService accountService; 24 | 25 | /** 26 | * 认证 27 | * 28 | * @param token 29 | * @return 30 | * @throws AuthenticationException 31 | */ 32 | @Override 33 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { 34 | UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; 35 | String username = usernamePasswordToken.getUsername(); 36 | String password = new String(usernamePasswordToken.getPassword()); 37 | return new SimpleAuthenticationInfo(accountService.login(username, password), 38 | usernamePasswordToken.getPassword(), getName()); 39 | } 40 | 41 | /** 42 | * 授权,只有代码中第一次鉴权的时候才会触发此方法的执行 43 | * 44 | * @param principals 45 | * @return 46 | */ 47 | @Override 48 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 49 | ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); 50 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 51 | info.addRoles(shiroUser.getRoles()); 52 | info.addStringPermissions(shiroUser.getPermissions()); 53 | return info; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/utils/Exceptions.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.utils; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  *     异常转化
     7 |  * 
    8 | * Author: javahuang 9 | * Create: 17/5/23 上午9:43 10 | */ 11 | public class Exceptions { 12 | 13 | /** 14 | * 将 CheckedException 转换为 UncheckedException 15 | * 16 | * @param e CheckedException 17 | * @return UncheckedException 18 | */ 19 | public static RuntimeException unchecked(Exception e) { 20 | if (e instanceof RuntimeException) { 21 | return (RuntimeException) e; 22 | } else { 23 | return new RuntimeException(e); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/utils/PasswordUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * PasswordUtil.java Create on 2012-11-19 3 | * Copyright(c) Gener-Tech Inc. 4 | * ALL Rights Reserved. 5 | */ 6 | package me.hrps.aas.core.utils; 7 | 8 | 9 | import org.apache.commons.codec.DecoderException; 10 | import org.apache.commons.codec.binary.Hex; 11 | 12 | /** 13 | * Description: 14 | *
    15 |  * 
    16 | * Author: javahuang 17 | * Create: 17/5/17 下午2:58 18 | */ 19 | public class PasswordUtils { 20 | 21 | 22 | public static byte[] getBytes(String str) { 23 | return str.getBytes(); 24 | } 25 | 26 | public static String getEncodePassWord(String passWord, byte[] salt) { 27 | byte[] hashPassword = Digests.sha1(getBytes(passWord), salt, 1024); 28 | return encodeHex(hashPassword); 29 | } 30 | 31 | public static String encodeHex(byte[] bytes) { 32 | return Hex.encodeHexString(bytes); 33 | } 34 | 35 | /** 36 | * Hex解密算法 37 | */ 38 | public static byte[] decodeHex(String input) { 39 | try { 40 | return Hex.decodeHex(input.toCharArray()); 41 | } catch (DecoderException e) { 42 | throw Exceptions.unchecked(e); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/utils/Securitys.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.utils; 2 | 3 | import me.hrps.aas.core.shiro.domain.ShiroUser; 4 | import me.hrps.aas.web.sys.vo.MenuVO; 5 | import org.apache.shiro.SecurityUtils; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Description: 11 | *
    12 |  * 
    13 | * Author: javahuang 14 | * Create: 2017/9/26 下午12:56 15 | */ 16 | public class Securitys extends SecurityUtils { 17 | 18 | public static ShiroUser getUser() { 19 | if (isAuthenticatedOrRemembered()) { 20 | return (ShiroUser) getSubject().getPrincipal(); 21 | } 22 | return new ShiroUser(); 23 | } 24 | 25 | public static boolean isAuthenticatedOrRemembered() { 26 | return getSubject().isAuthenticated() || getSubject().isRemembered(); 27 | } 28 | 29 | public static List getMenus() { 30 | return getUser().getMenus(); 31 | } 32 | 33 | public static String getName() { 34 | return getUser().getName(); 35 | } 36 | 37 | public static String getAccountId() { 38 | return getUser().getAccountId(); 39 | } 40 | 41 | public static boolean isAdmin() { 42 | return getUser().isAdmin(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/core/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.core.utils; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  *     系统常用的一些工具类
     9 |  * 
    10 | * Author: javahuang 11 | * Create: 17/9/26 下午1:40 12 | */ 13 | public class Utils { 14 | 15 | /** 16 | * @return 返回UUID作为主键 17 | */ 18 | public static String generateUUID() { 19 | return UUID.randomUUID().toString().replaceAll("-", ""); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/controller/AccountController.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.controller; 2 | 3 | import me.hrps.aas.web.sys.service.AccountService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * Description: 13 | *
    14 |  * 
    15 | * Author: javahuang 16 | * Create: 2017/9/26 下午12:53 17 | */ 18 | @RestController 19 | @RequestMapping("/user") 20 | public class AccountController { 21 | 22 | @Autowired 23 | private AccountService accountService; 24 | 25 | @RequestMapping 26 | public Map user() { 27 | return accountService.getUser(); 28 | } 29 | 30 | @PostMapping("/login") 31 | public void login(String username, String password) { 32 | accountService.login(username, password); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/domain/SysAccountRoleKey.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.domain; 2 | 3 | public class SysAccountRoleKey { 4 | private String roleId; 5 | 6 | private String accountId; 7 | 8 | public String getRoleId() { 9 | return roleId; 10 | } 11 | 12 | public void setRoleId(String roleId) { 13 | this.roleId = roleId == null ? null : roleId.trim(); 14 | } 15 | 16 | public String getAccountId() { 17 | return accountId; 18 | } 19 | 20 | public void setAccountId(String accountId) { 21 | this.accountId = accountId == null ? null : accountId.trim(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/domain/SysPermission.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.domain; 2 | 3 | import java.util.Date; 4 | 5 | public class SysPermission { 6 | private String id; 7 | 8 | private String name; 9 | 10 | private String code; 11 | 12 | private String parentId; 13 | 14 | private Byte permissionLevel; 15 | 16 | private Byte sequence; 17 | 18 | private Byte isResource; 19 | 20 | private String remark; 21 | 22 | private Date createTime; 23 | 24 | private String createBy; 25 | 26 | private Date updateTime; 27 | 28 | private String updateBy; 29 | 30 | public String getId() { 31 | return id; 32 | } 33 | 34 | public void setId(String id) { 35 | this.id = id == null ? null : id.trim(); 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public void setName(String name) { 43 | this.name = name == null ? null : name.trim(); 44 | } 45 | 46 | public String getCode() { 47 | return code; 48 | } 49 | 50 | public void setCode(String code) { 51 | this.code = code == null ? null : code.trim(); 52 | } 53 | 54 | public String getParentId() { 55 | return parentId; 56 | } 57 | 58 | public void setParentId(String parentId) { 59 | this.parentId = parentId == null ? null : parentId.trim(); 60 | } 61 | 62 | public Byte getPermissionLevel() { 63 | return permissionLevel; 64 | } 65 | 66 | public void setPermissionLevel(Byte permissionLevel) { 67 | this.permissionLevel = permissionLevel; 68 | } 69 | 70 | public Byte getSequence() { 71 | return sequence; 72 | } 73 | 74 | public void setSequence(Byte sequence) { 75 | this.sequence = sequence; 76 | } 77 | 78 | public Byte getIsResource() { 79 | return isResource; 80 | } 81 | 82 | public void setIsResource(Byte isResource) { 83 | this.isResource = isResource; 84 | } 85 | 86 | public String getRemark() { 87 | return remark; 88 | } 89 | 90 | public void setRemark(String remark) { 91 | this.remark = remark == null ? null : remark.trim(); 92 | } 93 | 94 | public Date getCreateTime() { 95 | return createTime; 96 | } 97 | 98 | public void setCreateTime(Date createTime) { 99 | this.createTime = createTime; 100 | } 101 | 102 | public String getCreateBy() { 103 | return createBy; 104 | } 105 | 106 | public void setCreateBy(String createBy) { 107 | this.createBy = createBy == null ? null : createBy.trim(); 108 | } 109 | 110 | public Date getUpdateTime() { 111 | return updateTime; 112 | } 113 | 114 | public void setUpdateTime(Date updateTime) { 115 | this.updateTime = updateTime; 116 | } 117 | 118 | public String getUpdateBy() { 119 | return updateBy; 120 | } 121 | 122 | public void setUpdateBy(String updateBy) { 123 | this.updateBy = updateBy == null ? null : updateBy.trim(); 124 | } 125 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/domain/SysRole.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.domain; 2 | 3 | import java.util.Date; 4 | 5 | public class SysRole { 6 | private String id; 7 | 8 | private String name; 9 | 10 | private String reamrk; 11 | 12 | private Byte isValid; 13 | 14 | private String helpCode; 15 | 16 | private Date createTime; 17 | 18 | private String createBy; 19 | 20 | private Date updateTime; 21 | 22 | private String updateBy; 23 | 24 | public String getId() { 25 | return id; 26 | } 27 | 28 | public void setId(String id) { 29 | this.id = id == null ? null : id.trim(); 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name == null ? null : name.trim(); 38 | } 39 | 40 | public String getReamrk() { 41 | return reamrk; 42 | } 43 | 44 | public void setReamrk(String reamrk) { 45 | this.reamrk = reamrk == null ? null : reamrk.trim(); 46 | } 47 | 48 | public Byte getIsValid() { 49 | return isValid; 50 | } 51 | 52 | public void setIsValid(Byte isValid) { 53 | this.isValid = isValid; 54 | } 55 | 56 | public String getHelpCode() { 57 | return helpCode; 58 | } 59 | 60 | public void setHelpCode(String helpCode) { 61 | this.helpCode = helpCode == null ? null : helpCode.trim(); 62 | } 63 | 64 | public Date getCreateTime() { 65 | return createTime; 66 | } 67 | 68 | public void setCreateTime(Date createTime) { 69 | this.createTime = createTime; 70 | } 71 | 72 | public String getCreateBy() { 73 | return createBy; 74 | } 75 | 76 | public void setCreateBy(String createBy) { 77 | this.createBy = createBy == null ? null : createBy.trim(); 78 | } 79 | 80 | public Date getUpdateTime() { 81 | return updateTime; 82 | } 83 | 84 | public void setUpdateTime(Date updateTime) { 85 | this.updateTime = updateTime; 86 | } 87 | 88 | public String getUpdateBy() { 89 | return updateBy; 90 | } 91 | 92 | public void setUpdateBy(String updateBy) { 93 | this.updateBy = updateBy == null ? null : updateBy.trim(); 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/domain/SysRolePermissionKey.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.domain; 2 | 3 | public class SysRolePermissionKey { 4 | private String roleId; 5 | 6 | private String permissionId; 7 | 8 | public String getRoleId() { 9 | return roleId; 10 | } 11 | 12 | public void setRoleId(String roleId) { 13 | this.roleId = roleId == null ? null : roleId.trim(); 14 | } 15 | 16 | public String getPermissionId() { 17 | return permissionId; 18 | } 19 | 20 | public void setPermissionId(String permissionId) { 21 | this.permissionId = permissionId == null ? null : permissionId.trim(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/filter/AccountFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.filter; 2 | 3 | import me.hrps.aas.core.mybatis.domain.PageFilter; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  * 
    9 | * Author: javahuang 10 | * Create: 2017/9/26 下午12:59 11 | */ 12 | public class AccountFilter extends PageFilter{ 13 | 14 | private String searchId; 15 | 16 | public String getSearchId() { 17 | return searchId; 18 | } 19 | 20 | public void setSearchId(String searchId) { 21 | this.searchId = searchId; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/filter/RoleFilter.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.filter; 2 | 3 | import me.hrps.aas.core.mybatis.domain.PageFilter; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  * 
    9 | * Author: javahuang 10 | * Create: 2017/9/26 下午1:55 11 | */ 12 | public class RoleFilter extends PageFilter{ 13 | 14 | private String searchId; 15 | 16 | public String getSearchId() { 17 | return searchId; 18 | } 19 | 20 | public void setSearchId(String searchId) { 21 | this.searchId = searchId; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysAccountMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysAccount; 4 | import me.hrps.aas.web.sys.domain.SysAccountExample; 5 | import me.hrps.aas.web.sys.filter.AccountFilter; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | 9 | import java.util.List; 10 | 11 | @Mapper 12 | public interface SysAccountMapper { 13 | long countByExample(SysAccountExample example); 14 | 15 | int deleteByExample(SysAccountExample example); 16 | 17 | int deleteByPrimaryKey(String id); 18 | 19 | int insert(SysAccount record); 20 | 21 | int insertSelective(SysAccount record); 22 | 23 | List selectByExample(SysAccountExample example); 24 | 25 | SysAccount selectByPrimaryKey(String id); 26 | 27 | int updateByExampleSelective(@Param("record") SysAccount record, @Param("example") SysAccountExample example); 28 | 29 | int updateByExample(@Param("record") SysAccount record, @Param("example") SysAccountExample example); 30 | 31 | int updateByPrimaryKeySelective(SysAccount record); 32 | 33 | int updateByPrimaryKey(SysAccount record); 34 | 35 | List selectByFilter(AccountFilter filter); 36 | 37 | List selectRoleUsers(String roleName); 38 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysAccountRoleMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysAccountRoleExample; 4 | import me.hrps.aas.web.sys.domain.SysAccountRoleKey; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface SysAccountRoleMapper { 12 | long countByExample(SysAccountRoleExample example); 13 | 14 | int deleteByExample(SysAccountRoleExample example); 15 | 16 | int deleteByPrimaryKey(SysAccountRoleKey key); 17 | 18 | int insert(SysAccountRoleKey record); 19 | 20 | int insertSelective(SysAccountRoleKey record); 21 | 22 | List selectByExample(SysAccountRoleExample example); 23 | 24 | int updateByExampleSelective(@Param("record") SysAccountRoleKey record, @Param("example") SysAccountRoleExample example); 25 | 26 | int updateByExample(@Param("record") SysAccountRoleKey record, @Param("example") SysAccountRoleExample example); 27 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysMenuMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysMenu; 4 | import me.hrps.aas.web.sys.domain.SysMenuExample; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface SysMenuMapper { 12 | long countByExample(SysMenuExample example); 13 | 14 | int deleteByExample(SysMenuExample example); 15 | 16 | int deleteByPrimaryKey(String id); 17 | 18 | int insert(SysMenu record); 19 | 20 | int insertSelective(SysMenu record); 21 | 22 | List selectByExample(SysMenuExample example); 23 | 24 | SysMenu selectByPrimaryKey(String id); 25 | 26 | int updateByExampleSelective(@Param("record") SysMenu record, @Param("example") SysMenuExample example); 27 | 28 | int updateByExample(@Param("record") SysMenu record, @Param("example") SysMenuExample example); 29 | 30 | int updateByPrimaryKeySelective(SysMenu record); 31 | 32 | int updateByPrimaryKey(SysMenu record); 33 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysPermissionMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysPermission; 4 | import me.hrps.aas.web.sys.domain.SysPermissionExample; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface SysPermissionMapper { 12 | long countByExample(SysPermissionExample example); 13 | 14 | int deleteByExample(SysPermissionExample example); 15 | 16 | int deleteByPrimaryKey(String id); 17 | 18 | int insert(SysPermission record); 19 | 20 | int insertSelective(SysPermission record); 21 | 22 | List selectByExample(SysPermissionExample example); 23 | 24 | SysPermission selectByPrimaryKey(String id); 25 | 26 | int updateByExampleSelective(@Param("record") SysPermission record, @Param("example") SysPermissionExample example); 27 | 28 | int updateByExample(@Param("record") SysPermission record, @Param("example") SysPermissionExample example); 29 | 30 | int updateByPrimaryKeySelective(SysPermission record); 31 | 32 | int updateByPrimaryKey(SysPermission record); 33 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysRoleMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysRole; 4 | import me.hrps.aas.web.sys.domain.SysRoleExample; 5 | import me.hrps.aas.web.sys.filter.RoleFilter; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | 9 | import java.util.List; 10 | 11 | @Mapper 12 | public interface SysRoleMapper { 13 | long countByExample(SysRoleExample example); 14 | 15 | int deleteByExample(SysRoleExample example); 16 | 17 | int deleteByPrimaryKey(String id); 18 | 19 | int insert(SysRole record); 20 | 21 | int insertSelective(SysRole record); 22 | 23 | List selectByExample(SysRoleExample example); 24 | 25 | SysRole selectByPrimaryKey(String id); 26 | 27 | int updateByExampleSelective(@Param("record") SysRole record, @Param("example") SysRoleExample example); 28 | 29 | int updateByExample(@Param("record") SysRole record, @Param("example") SysRoleExample example); 30 | 31 | int updateByPrimaryKeySelective(SysRole record); 32 | 33 | int updateByPrimaryKey(SysRole record); 34 | 35 | List selectByFilter(RoleFilter roleFilter); 36 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/mapper/SysRolePermissionMapper.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.mapper; 2 | 3 | import me.hrps.aas.web.sys.domain.SysRolePermissionExample; 4 | import me.hrps.aas.web.sys.domain.SysRolePermissionKey; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface SysRolePermissionMapper { 12 | long countByExample(SysRolePermissionExample example); 13 | 14 | int deleteByExample(SysRolePermissionExample example); 15 | 16 | int deleteByPrimaryKey(SysRolePermissionKey key); 17 | 18 | int insert(SysRolePermissionKey record); 19 | 20 | int insertSelective(SysRolePermissionKey record); 21 | 22 | List selectByExample(SysRolePermissionExample example); 23 | 24 | int updateByExampleSelective(@Param("record") SysRolePermissionKey record, @Param("example") SysRolePermissionExample example); 25 | 26 | int updateByExample(@Param("record") SysRolePermissionKey record, @Param("example") SysRolePermissionExample example); 27 | } -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/qo/TreeSortQO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.qo; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  * 
    7 | * Author: javahuang 8 | * Create: 2017/9/26 下午1:37 9 | */ 10 | public class TreeSortQO { 11 | 12 | private String dropKey; 13 | private String dragKey; 14 | private Integer dropPosition; 15 | 16 | public String getDropKey() { 17 | return dropKey; 18 | } 19 | 20 | public void setDropKey(String dropKey) { 21 | this.dropKey = dropKey; 22 | } 23 | 24 | public String getDragKey() { 25 | return dragKey; 26 | } 27 | 28 | public void setDragKey(String dragKey) { 29 | this.dragKey = dragKey; 30 | } 31 | 32 | public Integer getDropPosition() { 33 | return dropPosition; 34 | } 35 | 36 | public void setDropPosition(Integer dropPosition) { 37 | this.dropPosition = dropPosition; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/vo/MenuPermissionTreeVO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.vo; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  *     用于 SysPermission 数据转换成 tree-select 能够识别的数据结构
     7 |  *     类型定义为 String,是因为 TreeNode 的参数类型都是 string
     8 |  * 
    9 | * Author: javahuang 10 | * Create: 2017/9/26 下午1:43 11 | */ 12 | public class MenuPermissionTreeVO { 13 | 14 | private String value; 15 | private String label; 16 | private String parentId; 17 | 18 | public String getValue() { 19 | return value; 20 | } 21 | 22 | public void setValue(String value) { 23 | this.value = value; 24 | } 25 | 26 | public String getLabel() { 27 | return label; 28 | } 29 | 30 | public void setLabel(String label) { 31 | this.label = label; 32 | } 33 | 34 | public String getParentId() { 35 | return parentId; 36 | } 37 | 38 | public void setParentId(String parentId) { 39 | this.parentId = parentId; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/vo/MenuVO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.vo; 2 | 3 | /** 4 | * Description: 5 | *
     6 |  * 
    7 | * Author: javahuang 8 | * Create: 17/9/18 下午10:14 9 | */ 10 | public class MenuVO { 11 | 12 | private String id; 13 | private String bpid; 14 | private String mpid; 15 | private String name; 16 | private String icon; 17 | private String router; 18 | private Integer menuLevel; 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | 28 | public String getBpid() { 29 | return bpid; 30 | } 31 | 32 | public void setBpid(String bpid) { 33 | this.bpid = bpid; 34 | } 35 | 36 | public String getMpid() { 37 | return mpid; 38 | } 39 | 40 | public void setMpid(String mpid) { 41 | this.mpid = mpid; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getIcon() { 53 | return icon; 54 | } 55 | 56 | public void setIcon(String icon) { 57 | this.icon = icon; 58 | } 59 | 60 | public String getRouter() { 61 | return router; 62 | } 63 | 64 | public void setRouter(String router) { 65 | this.router = router; 66 | } 67 | 68 | public Integer getMenuLevel() { 69 | return menuLevel; 70 | } 71 | 72 | public void setMenuLevel(Integer menuLevel) { 73 | this.menuLevel = menuLevel; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/vo/SysAccountVO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.vo; 2 | 3 | import me.hrps.aas.web.sys.domain.SysAccount; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  * 
    9 | * Author: javahuang 10 | * Create: 2017/9/26 下午1:05 11 | */ 12 | public class SysAccountVO extends SysAccount{ 13 | 14 | private String roleNames; 15 | private String roleIds; 16 | 17 | public String getRoleNames() { 18 | return roleNames; 19 | } 20 | 21 | public void setRoleNames(String roleNames) { 22 | this.roleNames = roleNames; 23 | } 24 | 25 | public String getRoleIds() { 26 | return roleIds; 27 | } 28 | 29 | public void setRoleIds(String roleIds) { 30 | this.roleIds = roleIds; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/vo/SysRoleVO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.vo; 2 | 3 | import me.hrps.aas.web.sys.domain.SysRole; 4 | 5 | /** 6 | * Description: 7 | *
     8 |  *     添加角色对应的权限列表
     9 |  * 
    10 | * Author: javahuang 11 | * Create: 2017/9/26 下午2:02 12 | */ 13 | public class SysRoleVO extends SysRole{ 14 | 15 | private String permissionNames; 16 | private String permissionIds; 17 | 18 | public String getPermissionNames() { 19 | return permissionNames; 20 | } 21 | 22 | public void setPermissionNames(String permissionNames) { 23 | this.permissionNames = permissionNames; 24 | } 25 | 26 | public String getPermissionIds() { 27 | return permissionIds; 28 | } 29 | 30 | public void setPermissionIds(String permissionIds) { 31 | this.permissionIds = permissionIds; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/hrps/aas/web/sys/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package me.hrps.aas.web.sys.vo; 2 | 3 | import me.hrps.aas.core.utils.Securitys; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Description: 9 | *
    10 |  * 
    11 | * Author: javahuang 12 | * Create: 2017/9/26 下午12:55 13 | */ 14 | public class UserVO { 15 | 16 | private String name; 17 | private String accountId; 18 | private Boolean isAdmin; 19 | private List roles; 20 | private List permissions; 21 | 22 | public static UserVO buildUser() { 23 | if (!Securitys.isAuthenticatedOrRemembered()) { 24 | return null; 25 | } 26 | UserVO user = new UserVO(); 27 | user.setName(Securitys.getName()); 28 | user.setAccountId(Securitys.getAccountId()); 29 | user.setAdmin(Securitys.isAdmin()); 30 | return user; 31 | } 32 | 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public String getAccountId() { 43 | return accountId; 44 | } 45 | 46 | public void setAccountId(String accountId) { 47 | this.accountId = accountId; 48 | } 49 | 50 | public Boolean getAdmin() { 51 | return isAdmin; 52 | } 53 | 54 | public void setAdmin(Boolean admin) { 55 | isAdmin = admin; 56 | } 57 | 58 | public List getRoles() { 59 | return roles; 60 | } 61 | 62 | public void setRoles(List roles) { 63 | this.roles = roles; 64 | } 65 | 66 | public List getPermissions() { 67 | return permissions; 68 | } 69 | 70 | public void setPermissions(List permissions) { 71 | this.permissions = permissions; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | driver-class-name: com.mysql.jdbc.Driver 4 | druid: 5 | filters: wall,stat,slf4j 6 | poolPreparedStatements: true 7 | stat-view-servlet: 8 | login-username: hello 9 | login-password: hello 10 | mvc: 11 | static-path-pattern: /static/** 12 | profiles: 13 | active: development 14 | server: 15 | port: 7000 16 | session: 17 | timeout: 1800 18 | mybatis: 19 | mapper-locations: classpath:/mybatis/**/*Mapper.xml 20 | pagehelper: 21 | support-methods-arguments: true 22 | params: pageNum=page 23 | 24 | --- # 开发配置 25 | spring: 26 | profiles: development 27 | datasource: 28 | url: jdbc:mysql://localhost:3306/aas_db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false 29 | username: root 30 | password: 123456 31 | resources: 32 | chain: 33 | gzipped: true -------------------------------------------------------------------------------- /src/main/resources/mybatisGeneratorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
    47 | 48 | 49 | 50 | 51 |
    52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/resources/security/ehcache-shiro.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | --------------------------------------------------------------------------------