Browse Source

jeecg-boot 1.0版本发布

pull/123/head
zhangdaihao 6 years ago
parent
commit
52c8b47879
  1. 1
      111
  2. 147
      README.md
  3. 39
      ant-design-jeecg-vue/.editorconfig
  4. 1
      ant-design-jeecg-vue/.gitattributes
  5. 21
      ant-design-jeecg-vue/.gitignore
  6. 5
      ant-design-jeecg-vue/.prettierrc
  7. 21
      ant-design-jeecg-vue/LICENSE
  8. 147
      ant-design-jeecg-vue/README.md
  9. 5
      ant-design-jeecg-vue/babel.config.js
  10. 24
      ant-design-jeecg-vue/idea.config.js
  11. 98
      ant-design-jeecg-vue/package.json
  12. BIN
      ant-design-jeecg-vue/public/avatar2.jpg
  13. 7684
      ant-design-jeecg-vue/public/color.less
  14. 238
      ant-design-jeecg-vue/public/index.html
  15. BIN
      ant-design-jeecg-vue/public/logo.png
  16. 1
      ant-design-jeecg-vue/public/v2.js
  17. 44
      ant-design-jeecg-vue/src/App.vue
  18. 129
      ant-design-jeecg-vue/src/api/api.js
  19. 10
      ant-design-jeecg-vue/src/api/index.js
  20. 49
      ant-design-jeecg-vue/src/api/login.js
  21. 99
      ant-design-jeecg-vue/src/api/manage.js
  22. 69
      ant-design-jeecg-vue/src/assets/background.svg
  23. 21
      ant-design-jeecg-vue/src/assets/less/index.less
  24. BIN
      ant-design-jeecg-vue/src/assets/logo.png
  25. 43
      ant-design-jeecg-vue/src/assets/logo.svg
  26. 46
      ant-design-jeecg-vue/src/components/AvatarList/Item.vue
  27. 100
      ant-design-jeecg-vue/src/components/AvatarList/List.vue
  28. 4
      ant-design-jeecg-vue/src/components/AvatarList/index.js
  29. 60
      ant-design-jeecg-vue/src/components/AvatarList/index.less
  30. 111
      ant-design-jeecg-vue/src/components/ChartCard.vue
  31. 103
      ant-design-jeecg-vue/src/components/CountDown/CountDown.vue
  32. 3
      ant-design-jeecg-vue/src/components/CountDown/index.js
  33. 63
      ant-design-jeecg-vue/src/components/Ellipsis/Ellipsis.vue
  34. 3
      ant-design-jeecg-vue/src/components/Ellipsis/index.js
  35. 54
      ant-design-jeecg-vue/src/components/NumberInfo/NumberInfo.vue
  36. 3
      ant-design-jeecg-vue/src/components/NumberInfo/index.js
  37. 55
      ant-design-jeecg-vue/src/components/NumberInfo/index.less
  38. 41
      ant-design-jeecg-vue/src/components/Trend/Trend.vue
  39. 3
      ant-design-jeecg-vue/src/components/Trend/index.js
  40. 42
      ant-design-jeecg-vue/src/components/Trend/index.less
  41. 25
      ant-design-jeecg-vue/src/components/_util/StringUtil.js
  42. 12
      ant-design-jeecg-vue/src/components/_util/util.js
  43. 57
      ant-design-jeecg-vue/src/components/chart/Bar.vue
  44. 67
      ant-design-jeecg-vue/src/components/chart/Liquid.vue
  45. 67
      ant-design-jeecg-vue/src/components/chart/MiniArea.vue
  46. 68
      ant-design-jeecg-vue/src/components/chart/MiniBar.vue
  47. 75
      ant-design-jeecg-vue/src/components/chart/MiniProgress.vue
  48. 68
      ant-design-jeecg-vue/src/components/chart/Radar.vue
  49. 77
      ant-design-jeecg-vue/src/components/chart/RankList.vue
  50. 64
      ant-design-jeecg-vue/src/components/chart/TransferBar.vue
  51. 82
      ant-design-jeecg-vue/src/components/chart/Trend.vue
  52. 13
      ant-design-jeecg-vue/src/components/chart/chart.scss
  53. 47
      ant-design-jeecg-vue/src/components/dict/DictSelectTag.vue
  54. 61
      ant-design-jeecg-vue/src/components/dict/DictSelectUtil.js
  55. 36
      ant-design-jeecg-vue/src/components/dict/README.md
  56. 22
      ant-design-jeecg-vue/src/components/dict/UserInfoTag.vue
  57. 7
      ant-design-jeecg-vue/src/components/dict/index.js
  58. 4
      ant-design-jeecg-vue/src/components/index.less
  59. 74
      ant-design-jeecg-vue/src/components/jeecg/JDate.vue
  60. 43
      ant-design-jeecg-vue/src/components/jeecg/README.md
  61. 60
      ant-design-jeecg-vue/src/components/layouts/BasicLayout.vue
  62. 16
      ant-design-jeecg-vue/src/components/layouts/BlankLayout.vue
  63. 47
      ant-design-jeecg-vue/src/components/layouts/IframePageView.vue
  64. 85
      ant-design-jeecg-vue/src/components/layouts/PageView.vue
  65. 17
      ant-design-jeecg-vue/src/components/layouts/RouteView.vue
  66. 213
      ant-design-jeecg-vue/src/components/layouts/TabLayout.vue
  67. 150
      ant-design-jeecg-vue/src/components/layouts/UserLayout.vue
  68. 8
      ant-design-jeecg-vue/src/components/layouts/index.js
  69. 71
      ant-design-jeecg-vue/src/components/menu/Contextmenu.vue
  70. 62
      ant-design-jeecg-vue/src/components/menu/SideMenu.vue
  71. 162
      ant-design-jeecg-vue/src/components/menu/index.js
  72. 51
      ant-design-jeecg-vue/src/components/page/GlobalFooter.vue
  73. 127
      ant-design-jeecg-vue/src/components/page/GlobalHeader.vue
  74. 596
      ant-design-jeecg-vue/src/components/page/GlobalLayout.vue
  75. 238
      ant-design-jeecg-vue/src/components/page/PageHeader.vue
  76. 127
      ant-design-jeecg-vue/src/components/page/PageLayout.vue
  77. 58
      ant-design-jeecg-vue/src/components/page/SHeaderNotice.vue
  78. 314
      ant-design-jeecg-vue/src/components/setting/SettingDrawer.vue
  79. 38
      ant-design-jeecg-vue/src/components/setting/SettingItem.vue
  80. 292
      ant-design-jeecg-vue/src/components/table/README.md
  81. 252
      ant-design-jeecg-vue/src/components/table/StandardTable.vue
  82. 265
      ant-design-jeecg-vue/src/components/table/index.js
  83. 48
      ant-design-jeecg-vue/src/components/tools/Breadcrumb.vue
  84. 147
      ant-design-jeecg-vue/src/components/tools/DetailList.vue
  85. 32
      ant-design-jeecg-vue/src/components/tools/FooterToolBar.vue
  86. 67
      ant-design-jeecg-vue/src/components/tools/HeadInfo.vue
  87. 85
      ant-design-jeecg-vue/src/components/tools/HeaderNotice.vue
  88. 33
      ant-design-jeecg-vue/src/components/tools/Logo.vue
  89. 89
      ant-design-jeecg-vue/src/components/tools/TwoStepCaptcha.vue
  90. 93
      ant-design-jeecg-vue/src/components/tools/UserMenu.vue
  91. 95
      ant-design-jeecg-vue/src/components/tools/setting.js
  92. 394
      ant-design-jeecg-vue/src/config/router.config.js
  93. 31
      ant-design-jeecg-vue/src/defaultSettings.js
  94. 61
      ant-design-jeecg-vue/src/main.js
  95. 75
      ant-design-jeecg-vue/src/permission.js
  96. 139
      ant-design-jeecg-vue/src/router/README.md
  97. 12
      ant-design-jeecg-vue/src/router/index.js
  98. 17
      ant-design-jeecg-vue/src/store/getters.js
  99. 27
      ant-design-jeecg-vue/src/store/index.js
  100. 121
      ant-design-jeecg-vue/src/store/modules/app.js
  101. Some files were not shown because too many files have changed in this diff Show More

1
111

@ -1 +0,0 @@
1111

147
README.md

@ -0,0 +1,147 @@
Jeecg-Boot 快速开发平台(前后端分离版本)
===============
当前最新版本: 1.0(发布日期:20190225)
项目介绍:
-----------------------------------
Jeecg-boot 一个全新的版本,采用前后端分离方案,提供强大代码生成器的快速开发平台
前端页面代码和后端功能代码一键生成,不需要写任何代码,保持jeecg一贯的强大!!
技术架构:
-----------------------------------
后端技术: SpringBoot + Mybatis-plus + Shiro + Jwt + Swagger-ui + Redis
前端技术: Ant-design-vue + Vue + Webpack
其他技术: Druid(数据库连接池)、Logback(日志工具) 、poi(Excel工具)、
Quartz(定时任务)、lombok(简化代码)
项目构建: Maven、Jdk8
前端开发必读文档:
前端UI组件: Ant Design of Vue
https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn
报表UI组件:viser-vue
https://viserjs.github.io/demo.html#/viser/bar/basic-bar
VUE基础知识:
https://cn.vuejs.org/v2/guide
Overview
----
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Vue 版
[预览地址](http://boot.jeecg.org) **附带一些后台基础用到的列表展示例子**
效果抢先看:
1. 系统效果
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154007_icdX.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25153956_Q752.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201901/07154149_555Q.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154209_qlCg.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154251_XoW9.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154331_0ndT.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154414_ckFS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155155_Hm6H.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155213_T04n.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155224_MRLU.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155234_7zCP.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155242_K7Sw.png "在这里输入图片标题")
技术文档
-----------------------------------
* [在线演示](http://boot.jeecg.org)
* [官方文档](http://jeecg-boot.mydoc.io)
* QQ交流群:284271917
前端开发环境和依赖
----
- node
- yarn
- webpack
- eslint
- @vue/cli 3.2.1
- [ant-design-vue](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
项目下载和运行
----
- 拉取项目代码
```bash
git clone https://github.com/zhangdaiscott/jeecg-boot.git
cd jeecg-boot/ant-design-jeecg-vue
```
- 安装依赖
```
yarn install
```
- 开发模式运行
```
yarn run serve
```
- 编译项目
```
yarn run build
```
- Lints and fixes files
```
yarn run lint
```
其他说明
----
- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请更新您的 cli
- 关闭 Eslint (不推荐) 移除 `package.json``eslintConfig` 整个节点代码
- 修改 Ant Design 配色,在文件 `vue.config.js` 中,其他 less 变量覆盖参考 [ant design](https://ant.design/docs/react/customize-theme-cn) 官方说明
```ecmascript 6
css: {
loaderOptions: {
less: {
modifyVars: {
/* less 变量覆盖,用于自定义 ant design 主题 */
'primary-color': '#F5222D',
'link-color': '#F5222D',
'border-radius-base': '4px',
},
javascriptEnabled: true,
}
}
}
```
附属文档
----
- [路由/菜单说明](https://github.com/sendya/ant-design-pro-vue/blob/master/src/router/README.md)
- [ANTD 默认配置项](https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js)
- 其他待补充...
备注
----
> @vue/cli 升级后,eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules

39
ant-design-jeecg-vue/.editorconfig

@ -0,0 +1,39 @@
[*]
charset=utf-8
end_of_line=crlf
insert_final_newline=false
indent_style=space
indent_size=2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style=space
indent_size=2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style=space
indent_size=2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2
[*.svg]
indent_style=space
indent_size=2
[*.js.map]
indent_style=space
indent_size=2
[*.less]
indent_style=space
indent_size=2
[*.vue]
indent_style=space
indent_size=2
[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2

1
ant-design-jeecg-vue/.gitattributes vendored

@ -0,0 +1 @@
public/* linguist-vendored

21
ant-design-jeecg-vue/.gitignore vendored

@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

5
ant-design-jeecg-vue/.prettierrc

@ -0,0 +1,5 @@
{
"printWidth": 120,
"semi": false,
"singleQuote": true
}

21
ant-design-jeecg-vue/LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Anan Yang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

147
ant-design-jeecg-vue/README.md

@ -0,0 +1,147 @@
Jeecg-Boot 快速开发平台(前后端分离版本)
===============
当前最新版本: 1.0(发布日期:20190225)
项目介绍:
-----------------------------------
Jeecg-boot 一个全新的版本,采用前后端分离方案,提供强大代码生成器的快速开发平台
前端页面代码和后端功能代码一键生成,不需要写任何代码,保持jeecg一贯的强大!!
技术架构:
-----------------------------------
后端技术: SpringBoot + Mybatis-plus + Shiro + Jwt + Swagger-ui + Redis
前端技术: Ant-design-vue + Vue + Webpack
其他技术: Druid(数据库连接池)、Logback(日志工具) 、poi(Excel工具)、
Quartz(定时任务)、lombok(简化代码)
项目构建: Maven、Jdk8
前端开发必读文档:
前端UI组件: Ant Design of Vue
https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn
报表UI组件:viser-vue
https://viserjs.github.io/demo.html#/viser/bar/basic-bar
VUE基础知识:
https://cn.vuejs.org/v2/guide
Overview
----
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Vue 版
[预览地址](http://boot.jeecg.org) **附带一些后台基础用到的列表展示例子**
效果抢先看:
1. 系统效果
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154007_icdX.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25153956_Q752.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201901/07154149_555Q.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154209_qlCg.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154251_XoW9.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154331_0ndT.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25154414_ckFS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155155_Hm6H.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155213_T04n.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155224_MRLU.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155234_7zCP.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201902/25155242_K7Sw.png "在这里输入图片标题")
技术文档
-----------------------------------
* [在线演示](http://boot.jeecg.org)
* [官方文档](http://jeecg-boot.mydoc.io)
* QQ交流群:284271917
前端开发环境和依赖
----
- node
- yarn
- webpack
- eslint
- @vue/cli 3.2.1
- [ant-design-vue](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
项目下载和运行
----
- 拉取项目代码
```bash
git clone https://github.com/zhangdaiscott/jeecg-boot.git
cd jeecg-boot/ant-design-jeecg-vue
```
- 安装依赖
```
yarn install
```
- 开发模式运行
```
yarn run serve
```
- 编译项目
```
yarn run build
```
- Lints and fixes files
```
yarn run lint
```
其他说明
----
- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请更新您的 cli
- 关闭 Eslint (不推荐) 移除 `package.json``eslintConfig` 整个节点代码
- 修改 Ant Design 配色,在文件 `vue.config.js` 中,其他 less 变量覆盖参考 [ant design](https://ant.design/docs/react/customize-theme-cn) 官方说明
```ecmascript 6
css: {
loaderOptions: {
less: {
modifyVars: {
/* less 变量覆盖,用于自定义 ant design 主题 */
'primary-color': '#F5222D',
'link-color': '#F5222D',
'border-radius-base': '4px',
},
javascriptEnabled: true,
}
}
}
```
附属文档
----
- [路由/菜单说明](https://github.com/sendya/ant-design-pro-vue/blob/master/src/router/README.md)
- [ANTD 默认配置项](https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js)
- 其他待补充...
备注
----
> @vue/cli 升级后,eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules

5
ant-design-jeecg-vue/babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

24
ant-design-jeecg-vue/idea.config.js

@ -0,0 +1,24 @@
'use strict'
const path = require('path')
function resolve (dir) {
return path.join(__dirname, '.', dir)
}
module.exports = {
context: path.resolve(__dirname, './'),
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'config': resolve('config'),
'@': resolve('src'),
'@views': resolve('src/views'),
'@comp': resolve('src/components'),
'@core': resolve('src/core'),
'@utils': resolve('src/utils'),
'@entry': resolve('src/entry'),
'@router': resolve('src/router'),
'@store': resolve('src/store')
}
},
}

98
ant-design-jeecg-vue/package.json

@ -0,0 +1,98 @@
{
"name": "vue-antd-pro",
"version": "1.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e"
},
"dependencies": {
"@antv/data-set": "^0.10.1",
"ant-design-vue": "^1.3.1",
"axios": "^0.18.0",
"dayjs": "^1.8.0",
"enquire.js": "^2.1.6",
"js-cookie": "^2.2.0",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"nprogress": "^0.2.0",
"viser-vue": "^2.4.4",
"vue": "^2.5.22",
"vue-class-component": "^6.0.0",
"vue-cropper": "^0.4.8",
"vue-i18n": "^8.7.0",
"vue-ls": "^3.2.0",
"vue-print-nb": "^1.0.3",
"vue-property-decorator": "^7.3.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-class": "^0.3.1"
},
"devDependencies": {
"@babel/polyfill": "^7.2.5",
"@vue/cli-plugin-babel": "^3.3.0",
"@vue/cli-plugin-eslint": "^3.3.0",
"@vue/cli-service": "^3.3.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.12.0",
"eslint-plugin-vue": "^5.1.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"node-sass": "^4.11.0",
"sass-loader": "^7.0.1",
"vue-template-compiler": "^2.5.22"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/strongly-recommended",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {
"generator-star-spacing": "off",
"no-mixed-operators": 0,
"vue/max-attributes-per-line": [
2,
{
"singleline": 5,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}
],
"vue/attribute-hyphenation": 0,
"vue/html-self-closing": 0,
"vue/component-name-in-template-casing": 0,
"vue/html-closing-bracket-spacing": 0,
"vue/singleline-html-element-content-newline": 0,
"vue/no-unused-components": 0,
"vue/multiline-html-element-content-newline": 0,
"vue/no-use-v-if-with-v-for": 0,
"vue/html-closing-bracket-newline": 0,
"vue/no-parsing-error": 0,
"no-console": 0
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

BIN
ant-design-jeecg-vue/public/avatar2.jpg vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

7684
ant-design-jeecg-vue/public/color.less vendored

File diff suppressed because it is too large Load Diff

238
ant-design-jeecg-vue/public/index.html vendored

@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Jeecg-Boot 快速开发平台(Ant Design Vue)</title>
<link rel="icon" href="<%= BASE_URL %>logo.png">
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script>
<style>
html,
body,
#app {
height: 100%;
margin: 0px;
padding: 0px;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 120px;
height: 120px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 1 */
border-top-color: #FFF;
-webkit-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 2s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 2s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
/* COLOR 2 */
border-top-color: #FFF;
-webkit-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-moz-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 3s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 3s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
/* COLOR 3 */
-moz-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-o-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-ms-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
-webkit-animation: spin 1.5s linear infinite;
/* Chrome, Opera 15+, Safari 5+ */
animation: spin 1.5s linear infinite;
/* Chrome, Firefox 16+, IE 10+, Opera */
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg);
/* IE 9 */
transform: rotate(0deg);
/* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg);
/* IE 9 */
transform: rotate(360deg);
/* Firefox 16+, IE 10+, Opera */
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #49a9ee;
/* Old browsers */
z-index: 1000;
-webkit-transform: translateX(0);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(0);
/* IE 9 */
transform: translateX(0);
/* Firefox 16+, IE 10+, Opera */
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
/* Loaded */
.loaded #loader-wrapper .loader-section.section-left {
-webkit-transform: translateX(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(-100%);
/* IE 9 */
transform: translateX(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: translateX(100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(100%);
/* IE 9 */
transform: translateX(100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%);
/* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateY(-100%);
/* IE 9 */
transform: translateY(-100%);
/* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
/* JavaScript Turned Off */
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#loader-wrapper .load_title {
font-family: 'Open Sans';
color: #FFF;
font-size: 14px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
#loader-wrapper .load_title span {
font-weight: normal;
font-style: italic;
font-size: 14px;
color: #FFF;
opacity: 0.5;
}
</style>
</head>
<body>
<!-- built files will be auto injected -->
<div id="app">
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载 Jeecg-Boot 快速开发平台(Ant Design Vue),请耐心等待
</div>
</div>
</div>
</body>
</html>

BIN
ant-design-jeecg-vue/public/logo.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

1
ant-design-jeecg-vue/public/v2.js vendored

File diff suppressed because one or more lines are too long

44
ant-design-jeecg-vue/src/App.vue

@ -0,0 +1,44 @@
<template>
<a-locale-provider :locale="locale">
<div id="app">
<router-view/>
</div>
</a-locale-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import enquireScreen from '@/utils/device'
export default {
data () {
return {
locale: zhCN,
}
},
created () {
let that = this
enquireScreen(deviceType => {
// tablet
if (deviceType === 0) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
}
// mobile
else if (deviceType === 1) {
that.$store.commit('TOGGLE_DEVICE', 'mobile')
that.$store.dispatch('setSidebar', false)
}
else {
that.$store.commit('TOGGLE_DEVICE', 'desktop')
that.$store.dispatch('setSidebar', true)
}
})
}
}
</script>
<style>
#app {
height: 100%;
}
</style>

129
ant-design-jeecg-vue/src/api/api.js

@ -0,0 +1,129 @@
import { getAction,deleteAction,putAction,postAction} from '@/api/manage'
//根路径
const doMian = "/jeecg-boot/";
//图片预览请求地址
const imgView = "http://127.0.0.1:8080/jeecg-boot/sys/common/view/";
//角色管理
const addRole = (params)=>postAction("/sys/role/add",params);
const editRole = (params)=>putAction("/sys/role/edit",params);
const getRoleList = (params)=>getAction("/sys/role/list",params);
const deleteRole = (params)=>deleteAction("/sys/role/delete",params);
const deleteRoleList = (params)=>deleteAction("/sys/role/deleteBatch",params);
const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
const queryall = (params)=>getAction("/sys/role/queryall",params);
//用户管理
const addUser = (params)=>postAction("/sys/user/add",params);
const editUser = (params)=>putAction("/sys/user/edit",params);
const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
const getUserList = (params)=>getAction("/sys/user/list",params);
const deleteUser = (params)=>deleteAction("/sys/user/delete",params);
const deleteUserList = (params)=>deleteAction("/sys/user/deleteBatch",params);
const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
//验证用户账号是否唯一
const checkUsername = (params)=>getAction("/sys/user/checkOnlyUser",params);
//改变密码
const changPassword = (params)=>putAction("/sys/user/changPassword",params);
//权限管理
const addPermission= (params)=>postAction("/sys/permission/add",params);
const editPermission= (params)=>putAction("/sys/permission/edit",params);
const getPermissionList = (params)=>getAction("/sys/permission/list",params);
const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
const queryPermissionsByUser = (params)=>getAction("/sys/permission/queryByUser",params);
const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
// 部门管理
const queryDepartTreeList = (params)=>getAction("/sysdepart/sysDepart/queryTreeList",params);
const queryIdTree = (params)=>getAction("/sysdepart/sysDepart/queryIdTree",params);
const queryParentName = (params)=>getAction("/sysdepart/sysDepart/queryParentName",params);
const searchByKeywords = (params)=>getAction("/sysdepart/sysDepart/searchBy",params);
//日志管理
const getLogList = (params)=>getAction("/sys/log/list",params);
const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
//数据字典
const addDict = (params)=>postAction("/sys/dict/add",params);
const editDict = (params)=>putAction("/sys/dict/edit",params);
const getDictList = (params)=>getAction("/sys/dict/list",params);
const treeList = (params)=>getAction("/sys/dict/treeList",params);
const delDict = (params)=>deleteAction("/sys/dict/delete",params);
const getDictItemList = (params)=>getAction("/sys/dictItem/list",params);
const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
const delDictItem = (params)=>deleteAction("/sys/dictItem/delete",params);
const delDictItemList = (params)=>deleteAction("/sys/dictItem/deleteBatch",params);
//字典标签专用(通过code获取字典数组)
export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
//系统通告
const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",params);
const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",params);
//获取系统访问量
const getLoginfo = (params)=>getAction("/sys/loginfo",params);
export {
imgView,
doMian,
addRole,
editRole,
getRoleList,
deleteRole,
deleteRoleList,
checkRoleCode,
addUser,
editUser,
queryUserRole,
queryall,
getUserList,
deleteUser,
deleteUserList,
frozenBatch,
checkUsername,
changPassword,
getPermissionList,
deletePermission,
deletePermissionList,
addPermission,
editPermission,
queryTreeList,
queryListAsync,
queryRolePermission,
saveRolePermission,
queryPermissionsByUser,
loadAllRoleIds,
queryDepartTreeList,
queryIdTree,
queryParentName,
searchByKeywords,
getLogList,
deleteLog,
deleteLogList,
getDictList,
addDict,
editDict,
delDict,
treeList,
getDictItemList,
addDictItem,
editDictItem,
delDictItem,
delDictItemList,
doReleaseData,
doReovkeData,
getLoginfo
}

10
ant-design-jeecg-vue/src/api/index.js

@ -0,0 +1,10 @@
const api = {
Login: '/sys/login',
Logout: '/auth/logout',
ForgePassword: '/auth/forge-password',
Register: '/auth/register',
SendSms: '/account/sms',
// get my info
UserInfo: '/user/info'
}
export default api

49
ant-design-jeecg-vue/src/api/login.js

@ -0,0 +1,49 @@
import api from './index'
import { axios } from '@/utils/request'
/**
* login func
* parameter: {
* username: '',
* password: '',
* remember_me: true,
* captcha: '12345'
* }
* @param parameter
* @returns {*}
*/
export function login(parameter) {
return axios({
url: '/sys/login',
method: 'post',
data: parameter
})
}
export function getSmsCaptcha(parameter) {
return axios({
url: api.SendSms,
method: 'post',
data: parameter
})
}
export function getInfo() {
return axios({
url: '/api/user/info',
method: 'get',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
export function logout() {
return axios({
url: '/api/auth/logout',
method: 'post',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

99
ant-design-jeecg-vue/src/api/manage.js

@ -0,0 +1,99 @@
import { axios } from '@/utils/request'
const api = {
user: '/api/user',
role: '/api/role',
service: '/api/service',
permission: '/api/permission',
permissionNoPager: '/api/permission/no-pager'
}
export default api
//post
export function postAction(url,parameter) {
return axios({
url: url,
method:'post' ,
data: parameter
})
}
//post method= {post | put}
export function httpAction(url,parameter,method) {
return axios({
url: url,
method:method ,
data: parameter
})
}
//put
export function putAction(url,parameter) {
return axios({
url: url,
method:'put',
data: parameter
})
}
//get
export function getAction(url,parameter) {
return axios({
url: url,
method: 'get',
params: parameter
})
}
//deleteAction
export function deleteAction(url,parameter) {
return axios({
url: url,
method: 'delete',
params: parameter
})
}
export function getUserList(parameter) {
return axios({
url: api.user,
method: 'get',
params: parameter
})
}
export function getRoleList(parameter) {
return axios({
url: api.role,
method: 'get',
params: parameter
})
}
export function getServiceList(parameter) {
return axios({
url: api.service,
method: 'get',
params: parameter
})
}
export function getPermissions(parameter) {
return axios({
url: api.permissionNoPager,
method: 'get',
params: parameter
})
}
// id == 0 add post
// id != 0 update put
export function saveService(parameter) {
return axios({
url: api.service,
method: parameter.id == 0 ? 'post' : 'put',
data: parameter
})
}

69
ant-design-jeecg-vue/src/assets/background.svg

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

21
ant-design-jeecg-vue/src/assets/less/index.less

@ -0,0 +1,21 @@
.search{
margin-bottom: 54px;
}
.fold{
width: calc(100% - 216px);
display: inline-block
}
.operator{
margin-bottom: 18px;
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
.operator button {
margin-right: 5px;
}
i {
cursor: pointer;
}

BIN
ant-design-jeecg-vue/src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

43
ant-design-jeecg-vue/src/assets/logo.svg

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
<title>Group 28 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
<stop stop-color="#4285EB" offset="0%"></stop>
<stop stop-color="#2EC7FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
<stop stop-color="#29CDFF" offset="0%"></stop>
<stop stop-color="#148EFF" offset="37.8600687%"></stop>
<stop stop-color="#0A60FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
<stop stop-color="#FA816E" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="41.472606%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
<linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
<stop stop-color="#FA8E7D" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="51.2635191%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo" transform="translate(-20.000000, -20.000000)">
<g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
<g id="Group-27-Copy-3">
<g id="Group-25" fill-rule="nonzero">
<g id="2">
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
</g>
<path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
</g>
<ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

46
ant-design-jeecg-vue/src/components/AvatarList/Item.vue

@ -0,0 +1,46 @@
<template>
<tooltip v-if="tips !== ''">
<template slot="title">{{ tips }}</template>
<avatar :size="avatarSize" :src="src" />
</tooltip>
<avatar v-else :size="avatarSize" :src="src" />
</template>
<script>
import Avatar from 'ant-design-vue/es/avatar'
import Tooltip from 'ant-design-vue/es/tooltip'
export default {
name: "AvatarItem",
components: {
Avatar,
Tooltip
},
props: {
tips: {
type: String,
default: '',
required: false
},
src: {
type: String,
default: ''
}
},
data () {
return {
size: this.$parent.size
}
},
computed: {
avatarSize () {
return this.size !== 'mini' && this.size || 20
}
},
watch: {
'$parent.size' (val) {
this.size = val
}
}
}
</script>

100
ant-design-jeecg-vue/src/components/AvatarList/List.vue

@ -0,0 +1,100 @@
<!--
<template>
<div :class="[prefixCls]">
<ul>
<slot></slot>
<template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template>
<template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength">
<avatar-item :size="size">
<avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar>
</avatar-item>
</template>
</ul>
</div>
</template>
-->
<script>
import Avatar from 'ant-design-vue/es/avatar'
import AvatarItem from './Item'
import { filterEmpty } from '@/components/_util/util'
export default {
AvatarItem,
name: "AvatarList",
components: {
Avatar,
AvatarItem
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-avatar-list'
},
/**
* 头像大小 类型: largesmall mini, default
* 默认值: default
*/
size: {
type: [String, Number],
default: 'default'
},
/**
* 要显示的最大项目
*/
maxLength: {
type: Number,
default: 0
},
/**
* 多余的项目风格
*/
excessItemsStyle: {
type: Object,
default: () => {
return {
color: '#f56a00',
backgroundColor: '#fde3cf'
}
}
}
},
data () {
return {}
},
methods: {
getItems(items) {
const classString = {
[`${this.prefixCls}-item`]: true,
[`${this.size}`]: true
}
if (this.maxLength > 0) {
items = items.slice(0, this.maxLength)
items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>))
}
const itemList = items.map((item) => (
<li class={ classString }>{ item }</li>
))
return itemList
}
},
render () {
const { prefixCls, size } = this.$props
const classString = {
[`${prefixCls}`]: true,
[`${size}`]: true,
}
const items = filterEmpty(this.$slots.default)
const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null
return (
<div class={ classString }>
{ itemsDom }
</div>
)
}
}
</script>

4
ant-design-jeecg-vue/src/components/AvatarList/index.js

@ -0,0 +1,4 @@
import AvatarList from './List'
import "./index.less"
export default AvatarList

60
ant-design-jeecg-vue/src/components/AvatarList/index.less

@ -0,0 +1,60 @@
@import "../index";
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
.@{avatar-list-prefix-cls} {
display: inline-block;
ul {
list-style: none;
display: inline-block;
padding: 0;
margin: 0 0 0 8px;
font-size: 0;
}
}
.@{avatar-list-item-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
margin-left: -8px;
width: @avatar-size-base;
height: @avatar-size-base;
:global {
.ant-avatar {
border: 1px solid #fff;
cursor: pointer;
}
}
&.large {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
&.small {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
&.mini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
}

111
ant-design-jeecg-vue/src/components/ChartCard.vue

@ -0,0 +1,111 @@
<template>
<a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
<div class="chart-card-header">
<div class="meta">
<span class="chart-card-title">{{ title }}</span>
<span class="chart-card-action">
<slot name="action"></slot>
</span>
</div>
<div class="total"><span>{{ total }}</span></div>
</div>
<div class="chart-card-content">
<div class="content-fix">
<slot></slot>
</div>
</div>
<div class="chart-card-footer">
<div class="field">
<slot name="footer"></slot>
</div>
</div>
</a-card>
</template>
<script>
export default {
name: "ChartCard",
props: {
title: {
type: String,
default: ''
},
total: {
type: String,
default: ''
},
loading: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss" scoped>
.chart-card-header {
position: relative;
overflow: hidden;
width: 100%;
.meta {
position: relative;
overflow: hidden;
width: 100%;
color: rgba(0, 0, 0, .45);
font-size: 14px;
line-height: 22px;
}
}
.chart-card-action {
cursor: pointer;
position: absolute;
top: 0;
right: 0;
}
.chart-card-footer {
border-top: 1px solid #e8e8e8;
padding-top: 9px;
margin-top: 8px;
> * {
position: relative;
}
.field {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
}
.chart-card-content {
margin-bottom: 12px;
position: relative;
height: 46px;
width: 100%;
.content-fix {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
}
}
.total {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
color: #000;
margin-top: 4px;
margin-bottom: 0;
font-size: 30px;
line-height: 38px;
height: 38px;
}
</style>

103
ant-design-jeecg-vue/src/components/CountDown/CountDown.vue

@ -0,0 +1,103 @@
<template>
<span>
{{ lastTime | format }}
</span>
</template>
<script>
function fixedZero(val) {
return val * 1 < 10 ? `0${val}` : val;
}
export default {
name: "CountDown",
props: {
format: {
type: Function,
default: undefined
},
target: {
type: [Date, Number],
required: true,
},
onEnd: {
type: Function,
default: () => {
}
}
},
data() {
return {
dateTime: '0',
originTargetTime: 0,
lastTime: 0,
timer: 0,
interval: 1000
}
},
filters: {
format(time) {
const hours = 60 * 60 * 1000;
const minutes = 60 * 1000;
const h = Math.floor(time / hours);
const m = Math.floor((time - h * hours) / minutes);
const s = Math.floor((time - h * hours - m * minutes) / 1000);
return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
}
},
created() {
this.initTime()
this.tick()
},
methods: {
initTime() {
let lastTime = 0;
let targetTime = 0;
this.originTargetTime = this.target
try {
if (Object.prototype.toString.call(this.target) === '[object Date]') {
targetTime = this.target
} else {
targetTime = new Date(this.target).getTime()
}
} catch (e) {
throw new Error('invalid target prop')
}
lastTime = targetTime - new Date().getTime();
this.lastTime = lastTime < 0 ? 0 : lastTime
},
tick() {
const {onEnd} = this
this.timer = setTimeout(() => {
if (this.lastTime < this.interval) {
clearTimeout(this.timer)
this.lastTime = 0
if (typeof onEnd === 'function') {
onEnd();
}
} else {
this.lastTime -= this.interval
this.tick()
}
}, this.interval)
}
},
beforeUpdate () {
if (this.originTargetTime !== this.target) {
this.initTime()
}
},
beforeDestroy() {
clearTimeout(this.timer)
}
}
</script>
<style scoped>
</style>

3
ant-design-jeecg-vue/src/components/CountDown/index.js

@ -0,0 +1,3 @@
import CountDown from './CountDown'
export default CountDown

63
ant-design-jeecg-vue/src/components/Ellipsis/Ellipsis.vue

@ -0,0 +1,63 @@
<script>
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'
/*
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
const TooltipOverlayStyle = {
overflowWrap: 'break-word',
wordWrap: 'break-word',
};
*/
export default {
name: 'Ellipsis',
components: {
Tooltip
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean
},
length: {
type: Number,
required: true
},
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
},
methods: {
getStrDom (str) {
return (
<span>{ cutStrByFullLength(str, this.length) + '...' }</span>
)
},
getTooltip ( fullStr) {
return (
<Tooltip>
<template slot="title">{ fullStr }</template>
{ this.getStrDom(fullStr) }
</Tooltip>
)
}
},
render () {
const { tooltip, length } = this.$props
let str = this.$slots.default.map(vNode => vNode.text).join("")
const strDom = tooltip && getStrFullLength(str) > length ? this.getTooltip(str) : this.getStrDom(str);
return (
strDom
)
}
}
</script>

3
ant-design-jeecg-vue/src/components/Ellipsis/index.js

@ -0,0 +1,3 @@
import Ellipsis from './Ellipsis'
export default Ellipsis

54
ant-design-jeecg-vue/src/components/NumberInfo/NumberInfo.vue

@ -0,0 +1,54 @@
<template>
<div :class="[prefixCls]">
<slot name="subtitle">
<div :class="[`${prefixCls}-subtitle`]">{{ typeof subTitle === 'string' ? subTitle : subTitle() }}</div>
</slot>
<div class="number-info-value">
<span>{{ total }}</span>
<span class="sub-total">
{{ subTotal }}
<icon :type="`caret-${status}`" />
</span>
</div>
</div>
</template>
<script>
import Icon from 'ant-design-vue/es/icon'
export default {
name: 'NumberInfo',
props: {
prefixCls: {
type: String,
default: 'ant-pro-number-info'
},
total: {
type: Number,
required: true
},
subTotal: {
type: Number,
required: true
},
subTitle: {
type: [String, Function],
default: ''
},
status: {
type: String,
default: 'up'
}
},
components: {
Icon
},
data () {
return {}
}
}
</script>
<style lang="less" scoped>
@import "index";
</style>

3
ant-design-jeecg-vue/src/components/NumberInfo/index.js

@ -0,0 +1,3 @@
import NumberInfo from './NumberInfo'
export default NumberInfo

55
ant-design-jeecg-vue/src/components/NumberInfo/index.less

@ -0,0 +1,55 @@
@import "../index";
@numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info";
.@{numberInfo-prefix-cls} {
.ant-pro-number-info-subtitle {
color: @text-color-secondary;
font-size: @font-size-base;
height: 22px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
.number-info-value {
margin-top: 4px;
font-size: 0;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
& > span {
color: @heading-color;
display: inline-block;
line-height: 32px;
height: 32px;
font-size: 24px;
margin-right: 32px;
}
.sub-total {
color: @text-color-secondary;
font-size: @font-size-lg;
vertical-align: top;
margin-right: 0;
i {
font-size: 12px;
transform: scale(0.82);
margin-left: 4px;
}
:global {
.anticon-caret-up {
color: @red-6;
}
.anticon-caret-down {
color: @green-6;
}
}
}
}
}

41
ant-design-jeecg-vue/src/components/Trend/Trend.vue

@ -0,0 +1,41 @@
<template>
<div :class="[prefixCls, reverseColor && 'reverse-color' ]">
<span>
<slot name="term"></slot>
<span class="item-text">
<slot></slot>
</span>
</span>
<span :class="[flag]"><a-icon :type="`caret-${flag}`"/></span>
</div>
</template>
<script>
export default {
name: "Trend",
props: {
prefixCls: {
type: String,
default: 'ant-pro-trend'
},
/**
* 上升下降标识up|down
*/
flag: {
type: String,
required: true
},
/**
* 颜色反转
*/
reverseColor: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="less" scoped>
@import "index";
</style>

3
ant-design-jeecg-vue/src/components/Trend/index.js

@ -0,0 +1,3 @@
import Trend from './Trend.vue'
export default Trend

42
ant-design-jeecg-vue/src/components/Trend/index.less

@ -0,0 +1,42 @@
@import "../index";
@trend-prefix-cls: ~"@{ant-pro-prefix}-trend";
.@{trend-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
line-height: 22px;
.up,
.down {
margin-left: 4px;
position: relative;
top: 1px;
i {
font-size: 12px;
transform: scale(0.83);
}
}
.item-text {
display: inline-block;
margin-left: 8px;
color: rgba(0,0,0,.85);
}
.up {
color: @red-6;
}
.down {
color: @green-6;
top: -1px;
}
&.reverse-color .up {
color: @green-6;
}
&.reverse-color .down {
color: @red-6;
}
}

25
ant-design-jeecg-vue/src/components/_util/StringUtil.js

@ -0,0 +1,25 @@
export const getStrFullLength = (str = '') =>
str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
return pre + 1
}
return pre + 2
}, 0)
export const cutStrByFullLength = (str = '', maxLength) => {
let showLength = 0
return str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
showLength += 1
} else {
showLength += 2
}
if (showLength <= maxLength) {
return pre + cur
}
return pre
}, '')
}

12
ant-design-jeecg-vue/src/components/_util/util.js

@ -0,0 +1,12 @@
/**
* components util
*/
/**
* 清理空值对象
* @param children
* @returns {*[]}
*/
export function filterEmpty (children = []) {
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
}

57
ant-design-jeecg-vue/src/components/chart/Bar.vue

@ -0,0 +1,57 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="data"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const data = []
for (let i = 0; i < 12; i += 1) {
data.push({
x: `${i + 1}`,
y: Math.floor(Math.random() * 1000) + 200
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
export default {
name: "Bar",
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
data,
scale,
tooltip
}
}
}
</script>

67
ant-design-jeecg-vue/src/components/chart/Liquid.vue

@ -0,0 +1,67 @@
<template>
<div>
<v-chart
:forceFit="true"
:height="height"
:width="width"
:data="data"
:scale="scale"
:padding="0">
<v-tooltip />
<v-interval
:shape="['liquid-fill-gauge']"
position="transfer*value"
color=""
:v-style="{
lineWidth: 10,
opacity: 0.75
}"
:tooltip="[
'transfer*value',
(transfer, value) => {
return {
name: transfer,
value,
};
},
]"
></v-interval>
<v-guide
v-for="(row, index) in data"
:key="index"
type="text"
:top="true"
:position="{
gender: row.transfer,
value: 45
}"
:content="row.value + '%'"
:v-style="{
fontSize: 100,
textAlign: 'center',
opacity: 0.75,
}"
/>
</v-chart>
</div>
</template>
<script>
export default {
name: "Liquid",
props: {
height: {
type: Number,
default: 0
},
width: {
type: Number,
default: 0
}
}
}
</script>
<style scoped>
</style>

67
ant-design-jeecg-vue/src/components/chart/MiniArea.vue

@ -0,0 +1,67 @@
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 0, 18, 0]">
<v-tooltip />
<v-smooth-area position="x*y" />
</v-chart>
</div>
</div>
</template>
<script>
import moment from 'dayjs'
const data = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
}
console.log("123321",data)
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
export default {
name: "MiniArea",
props:{
datasource:{
type: Array,
default:()=>[]
}
},
created(){
if(this.datasource.length==0){
this.datasource = data;
}
},
data () {
return {
tooltip,
scale,
height: 100
}
}
}
</script>
<style lang="scss" scoped>
@import "chart";
</style>

68
ant-design-jeecg-vue/src/components/chart/MiniBar.vue

@ -0,0 +1,68 @@
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="datasource" :padding="[36, 5, 18, 5]">
<v-tooltip />
<v-bar position="x*y" />
</v-chart>
</div>
</div>
</template>
<script>
import moment from 'dayjs'
const data = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 30
}]
export default {
name: "MiniBar",
props:{
datasource:{
type: Array,
default:()=>[]
}
},
created(){
if(this.datasource.length==0){
this.datasource = data;
}
},
data () {
return {
data,
tooltip,
scale,
height: 100
}
}
}
</script>
<style lang="scss" scoped>
@import "chart";
</style>

75
ant-design-jeecg-vue/src/components/chart/MiniProgress.vue

@ -0,0 +1,75 @@
<template>
<div class="chart-mini-progress">
<div class="target" :style="{ left: target + '%'}">
<span :style="{ backgroundColor: color }" />
<span :style="{ backgroundColor: color }"/>
</div>
<div class="progress-wrapper">
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
</div>
</div>
</template>
<script>
export default {
name: "MiniProgress",
props: {
target: {
type: Number,
default: 0
},
height: {
type: String,
default: '10px'
},
color: {
type: String,
default: '#13C2C2'
},
percentage: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss" scoped>
.chart-mini-progress {
padding: 5px 0;
position: relative;
width: 100%;
.target {
position: absolute;
top: 0;
bottom: 0;
span {
border-radius: 100px;
position: absolute;
top: 0;
left: 0;
height: 4px;
width: 2px;
&:last-child {
top: auto;
bottom: 0;
}
}
}
.progress-wrapper {
background-color: #f5f5f5;
position: relative;
.progress {
transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
border-radius: 1px 0 0 1px;
background-color: #1890ff;
width: 0;
height: 100%;
}
}
}
</style>

68
ant-design-jeecg-vue/src/components/chart/Radar.vue

@ -0,0 +1,68 @@
<template>
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
<v-tooltip></v-tooltip>
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
<v-legend dataKey="user" marker="circle" :offset="30" />
<v-coord type="polar" radius="0.8" />
<v-line position="item*score" color="user" :size="2" />
<v-point position="item*score" color="user" :size="4" shape="circle" />
</v-chart>
</template>
<script>
const axis1Opts = {
dataKey: 'item',
line: null,
tickLine: null,
grid: {
lineStyle: {
lineDash: null
},
hideFirstLine: false
}
}
const axis2Opts = {
dataKey: 'score',
line: null,
tickLine: null,
grid: {
type: 'polygon',
lineStyle: {
lineDash: null
}
}
}
const scale = [
{
dataKey: 'score',
min: 0,
max: 80
}, {
dataKey: 'user',
alias: '类型'
}
]
export default {
name: 'Radar',
props: {
data: {
type: Array,
default: null,
}
},
data () {
return {
axis1Opts,
axis2Opts,
scale
}
}
}
</script>
<style scoped>
</style>

77
ant-design-jeecg-vue/src/components/chart/RankList.vue

@ -0,0 +1,77 @@
<template>
<div class="rank">
<h4 class="title">{{ title }}</h4>
<ul class="list">
<li :key="index" v-for="(item, index) in list">
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
<span>{{ item.name }}</span>
<span>{{ item.total }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "RankList",
// ['title', 'list']
props: {
title: {
type: String,
default: ''
},
list: {
type: Array,
default: null
}
}
}
</script>
<style lang="scss" scoped>
.rank {
padding: 0 32px 32px 72px;
.list {
margin: 25px 0 0;
padding: 0;
list-style: none;
li {
margin-top: 16px;
span {
color: rgba(0, 0, 0, .65);
font-size: 14px;
line-height: 22px;
&:first-child {
background-color: #f5f5f5;
border-radius: 20px;
display: inline-block;
font-size: 12px;
font-weight: 600;
margin-right: 24px;
height: 20px;
line-height: 20px;
width: 20px;
text-align: center;
}
&.active {
background-color: #314659;
color: #fff;
}
&:last-child {
float: right;
}
}
}
}
}
.mobile .rank {
padding: 0 32px 32px 32px;
}
</style>

64
ant-design-jeecg-vue/src/components/chart/TransferBar.vue

@ -0,0 +1,64 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="data"
:scale="scale"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
title: '日期(天)',
alias: '日期(天)',
min: 2
}, {
dataKey: 'y',
title: '流量(Gb)',
alias: '流量(Gb)',
min: 1
}]
export default {
name: "Bar",
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
data: [],
scale,
tooltip
}
},
created () {
this.getMonthBar()
},
methods: {
getMonthBar() {
this.$http.get('/analysis/month-bar')
.then(res => {
this.data = res.result
})
}
}
}
</script>

82
ant-design-jeecg-vue/src/components/chart/Trend.vue

@ -0,0 +1,82 @@
<template>
<div class="chart-trend">
{{ term }}
<span>{{ rate }}%</span>
<span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
</div>
</template>
<script>
export default {
name: "Trend",
props: {
term: {
type: String,
default: '',
required: true
},
percentage: {
type: Number,
default: null
},
type: {
type: Boolean,
default: null
},
target: {
type: Number,
default: 0
},
value: {
type: Number,
default: 0
},
fixed: {
type: Number,
default: 2
}
},
data () {
return {
trend: this.type && 'up' || 'down',
rate: this.percentage
}
},
created () {
let type = this.type === null ? this.value >= this.target : this.type
this.trend = type ? 'up' : 'down';
this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
}
}
</script>
<style lang="scss" scoped>
.chart-trend {
display: inline-block;
font-size: 14px;
line-height: 22px;
.trend-icon {
font-size: 12px;
&.up, &.down {
margin-left: 4px;
position: relative;
top: 1px;
i {
font-size: 12px;
transform: scale(.83);
}
}
&.up {
color: #f5222d;
}
&.down {
color: #52c41a;
top: -1px;
}
}
}
</style>

13
ant-design-jeecg-vue/src/components/chart/chart.scss

@ -0,0 +1,13 @@
.antv-chart-mini {
position: relative;
width: 100%;
.chart-wrapper {
position: absolute;
bottom: -28px;
width: 100%;
/* margin: 0 -5px;
overflow: hidden;*/
}
}

47
ant-design-jeecg-vue/src/components/dict/DictSelectTag.vue

@ -0,0 +1,47 @@
<template>
<a-select :placeholder="placeholder" :value="value" @change="handleInput">
<a-select-option value="">请选择</a-select-option>
<a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</template>
<script>
import {ajaxGetDictItems} from '@/api/api'
export default {
name: "DictSelectTag",
props: {
dictCode: String,
placeholder: String,
value: String,// 1. value prop
},
data() {
return {
dictOptions: [],
}
},
created() {
console.log(this.dictCode);
//
this.initDictData();
},
methods: {
initDictData() {
//Code,
ajaxGetDictItems(this.dictCode, null).then((res) => {
if (res.success) {
// console.log(res.result);
this.dictOptions = res.result;
}
})
},
handleInput(val) {
console.log(val);
this.$emit('input', val); // 2. input
}
}
}
</script>
<style scoped>
</style>

61
ant-design-jeecg-vue/src/components/dict/DictSelectUtil.js

@ -0,0 +1,61 @@
/**
* 字典 util
* author: scott
* date: 20190109
*/
import {ajaxGetDictItems} from '@/api/api'
import {getAction} from '@/api/manage'
/**
* 获取字典数组
* @param dictCode 字典Code
* @return List<Map>
*/
export async function initDictOptions(dictCode) {
if (!dictCode) {
return '字典Code不能为空!';
}
//获取字典数组
let res = await ajaxGetDictItems(dictCode);
return res;
}
/**
* 字典值替换文本通用方法
* @param dictOptions 字典数组
* @param text 字典值
* @return String
*/
export function filterDictText(dictOptions, text) {
let re = "";
dictOptions.forEach(function (option) {
if (text === option.value) {
re = option.text;
}
});
return re;
}
/**
* 翻译字段值对应的文本
* @param children
* @returns string
*/
export async function ajaxFilterDictText(dictCode, key) {
if (!dictCode) {
return '字典Code不能为空!';
}
//console.log(`key : ${key}`);
if (!key) {
return '';
}
//通过请求读取字典文本
let res = await getAction(`/sys/dict/getDictText/${dictCode}/${key}`);
if (res.success) {
// console.log('restult: '+ res.result);
return res.result;
} else {
return '';
}
}

36
ant-design-jeecg-vue/src/components/dict/README.md

@ -0,0 +1,36 @@
DictSelectTag 组件说明
===
例子
----
<DictSelectTag v-model="queryParam.sex" placeholder="请输入用户性别" dictCode="sex"/>
DictSelectUtil.js 列表字典函数用法说明
===
例子
----
第一步: 引入依赖方法
import {initDictOptions, filterDictText} from '@/components/dict/DictSelectUtil'
第二步: 在created()初始化方法执行字典配置方法
//初始化字典配置
this.initDictConfig();
第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
//sexDictOptions 自行定义
initDictConfig() {
//初始化字典 - 性别
initDictOptions('sex').then((res) => {
if (res.success) {
this.sexDictOptions = res.result;
}
});
},
第四步:实现字段的customRender方法
customRender: (text, record, index) => {
//字典值替换通用方法
return filterDictText(this.sexDictOptions, text);
}

22
ant-design-jeecg-vue/src/components/dict/UserInfoTag.vue

@ -0,0 +1,22 @@
<template>
<span>
<!--// 1input-->
<!--<input type="text" :value="value" @input="updateVal($event.target.value)">-->
<a-input :placeholder="placeholder" :value="value" @input="updateVal($event.target.value)"></a-input>
</span>
</template>
<script>
export default {
props: {
value: String,
placeholder: String
},
methods: {
updateVal: function(val) {
// 2input
this.$emit('input', val);
}
}
}
</script>

7
ant-design-jeecg-vue/src/components/dict/index.js

@ -0,0 +1,7 @@
import T from './DictSelectTag.vue'
const DictSelectTag = {
install: function (Vue) {
Vue.component('DictSelectTag',T);
}
}
export default DictSelectTag;

4
ant-design-jeecg-vue/src/components/index.less

@ -0,0 +1,4 @@
@import "~ant-design-vue/lib/style/index";
// The prefix to use on all css classes from ant-pro.
@ant-pro-prefix : ant-pro;

74
ant-design-jeecg-vue/src/components/jeecg/JDate.vue

@ -0,0 +1,74 @@
<template>
<a-date-picker
:disabled="readOnly"
:placeholder="placeholder"
@change="handleDateChange"
:value="momVal"
:showTime="showTime"
:format="dateFormat"
/>
</template>
<script>
import moment from 'moment'
export default {
name: 'JDate',
props: {
placeholder:{
type: String,
default: '',
required: false
},
value:{
type: String,
default: '',
required: false
},
dateFormat:{
type: String,
default: 'YYYY-MM-DD',
required: false
},
triggerChange:{
type: Boolean,
required: false,
default: false
},
readOnly:{
type: Boolean,
required: false,
default: false
},
showTime:{
type: Boolean,
required: false,
default: false
}
},
data () {
let dateStr = this.value;
return {
decorator:"",
momVal:!dateStr?null:moment(dateStr,this.dateFormat)
}
},
watch: {
value (val) {
if(!val){
this.momVal = null
}else{
this.momVal = moment(val,this.dateFormat)
}
}
},
methods: {
moment,
handleDateChange(mom,dateStr){
if(this.triggerChange){
this.$emit('change', dateStr);
}else{
this.$emit('input', dateStr);
}
}
}
}
</script>

43
ant-design-jeecg-vue/src/components/jeecg/README.md

@ -0,0 +1,43 @@
日期组件
说明:antd-vue日期组件需要用moment中转一下,用起来不是很方便,特二次封装,使用时只需要传字符串即可
====
参数说明
----
placeholder:placeholder
readOnly:true/false
value:绑定v-model或是v-decorator后不需要设置
showTime:是否展示时间true/false
dateFormat:日期格式 默认'YYYY-MM-DD' 若showTime设置为true则需要将其设置成对应的时间格式(如:YYYY-MM-DD HH:mm:ss)
triggerChange:触发组件值改变的事件是否是change,当使用v-decorator时且没有设置decorator的option.trigger为input需要设置该值为true
使用示例
----
1.组件带有v-model的使用方法
<j-date v-model="dateStr"></j-date>
2.组件带有v-decorator的使用方法
a).设置trigger-change属性为true
<j-date :trigger-change="true" v-decorator="['dateStr',{}]"></j-date>
b).设置decorator的option.trigger为input
<j-date v-decorator="['dateStr',{trigger:'input'}]"></j-date>
3.其他使用
添加style
<j-date v-model="dateStr" style="width:100%"></j-date>
添加placeholder
<j-date v-model="dateStr" placeholder="请输入dateStr"></j-date>
添加readOnly
<j-date v-model="dateStr" :read-only="true"></j-date>
备注:
script内需引入jdate
<script>
import JDate from '@/components/jeecg/JDate'
export default {
name: "demo",
components: {
JDate
},
....
</script>

60
ant-design-jeecg-vue/src/components/layouts/BasicLayout.vue

@ -0,0 +1,60 @@
<template>
<global-layout>
<transition name="page-transition">
<keep-alive v-if="keepAlive">
<router-view />
</keep-alive>
<router-view v-else />
</transition>
</global-layout>
</template>
<script>
import GlobalLayout from '@/components/page/GlobalLayout'
export default {
name: "BasicLayout",
components: {
GlobalLayout
},
data () {
return {
}
},
computed: {
keepAlive () {
return this.$route.meta.keepAlive
}
},
methods: {
},
}
</script>
<style lang="scss">
/*
* The following styles are auto-applied to elements with
* transition="page-transition" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the page transition by editing
* these styles.
*/
.page-transition-enter {
opacity: 0;
}
.page-transition-leave-active {
opacity: 0;
}
.page-transition-enter .page-transition-container,
.page-transition-leave-active .page-transition-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
</style>

16
ant-design-jeecg-vue/src/components/layouts/BlankLayout.vue

@ -0,0 +1,16 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
name: "BlankLayout",
}
</script>
<style scoped>
</style>

47
ant-design-jeecg-vue/src/components/layouts/IframePageView.vue

@ -0,0 +1,47 @@
<template>
<iframe :id="id" :src="url" frameborder="0" width="100%" height="800px" scrolling="auto"></iframe>
</template>
<script>
import PageLayout from '../page/PageLayout'
import RouteView from './RouteView'
export default {
name: "IframePageContent",
data () {
return {
url: "",
id:""
}
},
created () {
this.goUrl()
},
updated () {
this.goUrl()
},
watch: {
$route(to, from) {
this.goUrl();
}
},
methods: {
goUrl () {
let url = this.$route.meta.url
let id = this.$route.path
this.id = id
//url = "http://www.baidu.com"
console.log("------url------"+url)
if (url !== null && url !== undefined) {
this.url = url;
//window.open(this.url);
}
}
}
}
</script>
<style>
</style>

85
ant-design-jeecg-vue/src/components/layouts/PageView.vue

@ -0,0 +1,85 @@
<template>
<page-layout :desc="description" :title="getTitle" :link-list="linkList" :search="search" :tabs="tabs">
<div slot="extra" class="extra-img">
<img :src="extraImage"/>
</div>
<!-- keep-alive -->
<route-view ref="content"></route-view>
</page-layout>
</template>
<script>
import PageLayout from '../page/PageLayout'
import RouteView from './RouteView'
export default {
name: "PageContent",
components: {
RouteView,
PageLayout
},
data () {
return {
title: '',
description: '',
linkList: [],
extraImage: '',
search: false,
tabs: {}
}
},
mounted () {
this.getPageHeaderInfo()
},
updated () {
this.getPageHeaderInfo()
},
computed: {
getTitle () {
return this.$route.meta.title
}
},
methods: {
getPageHeaderInfo () {
// eslint-disable-next-line
this.title = this.$route.meta.title
// route-view ref
const content = this.$refs.content && this.$refs.content.$children[0]
if (content) {
this.description = content.description
this.linkList = content.linkList
this.extraImage = content.extraImage
this.search = content.search == true ? true : false
this.tabs = content.tabs
}
}
}
}
</script>
<style lang="scss" scoped>
.extra-img {
margin-top: -60px;
text-align: center;
width: 195px;
img {
width: 100%;
}
}
.mobile {
.extra-img{
margin-top: 0;
text-align: center;
width: 96px;
img{
width: 100%;
}
}
}
</style>

17
ant-design-jeecg-vue/src/components/layouts/RouteView.vue

@ -0,0 +1,17 @@
<template>
<keep-alive v-if="keepAlive">
<router-view />
</keep-alive>
<router-view v-else />
</template>
<script>
export default {
name: "RouteView",
computed: {
keepAlive () {
return this.$route.meta.keepAlive
}
},
}
</script>

213
ant-design-jeecg-vue/src/components/layouts/TabLayout.vue

@ -0,0 +1,213 @@
<template>
<global-layout>
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
<a-tabs
@contextmenu.native="e => onContextmenu(e)"
v-if="multipage"
:active-key="activePage"
style="margin-top: -8px; margin-bottom: -10px"
:hide-add="true"
type="editable-card"
@change="changePage"
@edit="editPage">
<a-tab-pane :id="page.fullPath" :key="page.fullPath" v-for="page in pageList">
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
</a-tab-pane>
</a-tabs>
<transition name="page-toggle">
<keep-alive v-if="multipage">
<router-view />
</keep-alive>
<router-view v-else />
</transition>
</global-layout>
</template>
<script>
import GlobalLayout from '@/components/page/GlobalLayout'
import Contextmenu from '@/components/menu/Contextmenu'
const indexKey="/dashboard/analysis"
export default {
name: "TabLayout",
components: {
GlobalLayout,
Contextmenu
},
data () {
return {
pageList: [],
linkList: [],
activePage: '',
menuVisible: false,
menuItemList: [
{ key: '1', icon: 'arrow-left', text: '关闭左侧' },
{ key: '2', icon: 'arrow-right', text: '关闭右侧' },
{ key: '3', icon: 'close', text: '关闭其它' }
]
}
},
computed: {
multipage () {
return this.$store.state.app.multipage
}
},
created () {
this.pageList.push(this.$route)
this.linkList.push(this.$route.fullPath)
this.activePage = this.$route.fullPath
},
watch: {
'$route': function (newRoute) {
this.activePage = newRoute.fullPath
if (!this.multipage) {
this.linkList = [newRoute.fullPath]
this.pageList = [newRoute]
} else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
this.linkList.push(newRoute.fullPath)
this.pageList.push(newRoute)
}else if(this.linkList.indexOf(newRoute.fullPath) >= 0){
let oldIndex = this.linkList.indexOf(newRoute.fullPath);
this.pageList.splice(oldIndex,1,newRoute);
}
},
'activePage': function (key) {
let index = this.linkList.lastIndexOf(key);
var waitRouter = this.pageList[index];
this.$router.push({
path: waitRouter.path,
name: waitRouter.name,
params: waitRouter.params
});
},
'multipage': function (newVal) {
if (!newVal) {
this.linkList = [this.$route.fullPath]
this.pageList = [this.$route]
}
}
},
methods: {
changePage (key) {
this.activePage = key
},
editPage (key, action) {
this[action](key)
},
remove (key) {
if(key==indexKey){
this.$message.warning('首页不能关闭!')
return
}
if (this.pageList.length === 1) {
this.$message.warning('这是最后一页,不能再关闭了啦')
return
}
this.pageList = this.pageList.filter(item => item.fullPath !== key)
let index = this.linkList.indexOf(key)
this.linkList = this.linkList.filter(item => item !== key)
index = index >= this.linkList.length ? this.linkList.length - 1 : index
this.activePage = this.linkList[index]
},
onContextmenu (e) {
const pagekey = this.getPageKey(e.target)
if (pagekey !== null) {
e.preventDefault()
this.menuVisible = true
}
},
getPageKey (target, depth) {
depth = depth || 0
if (depth > 2) {
return null
}
let pageKey = target.getAttribute('pagekey')
pageKey = pageKey || (target.previousElementSibling ? target.previousElementSibling.getAttribute('pagekey') : null)
return pageKey || (target.firstElementChild ? this.getPageKey(target.firstElementChild, ++depth) : null)
},
onMenuSelect (key, target) {
let pageKey = this.getPageKey(target)
switch (key) {
case '1':
this.closeLeft(pageKey)
break
case '2':
this.closeRight(pageKey)
break
case '3':
this.closeOthers(pageKey)
break
default:
break
}
},
closeOthers (pageKey) {
let index = this.linkList.indexOf(pageKey)
if(pageKey==indexKey){
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.activePage = this.linkList[0]
}else{
let indexContent = this.pageList.slice(0,1)[0]
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.linkList.unshift(indexKey)
this.pageList.unshift(indexContent)
this.activePage = this.linkList[1]
}
},
closeLeft (pageKey) {
if(pageKey==indexKey){
return
}
let tempList = [...this.pageList];
let indexContent = tempList.slice(0,1)[0]
let index = this.linkList.indexOf(pageKey)
this.linkList = this.linkList.slice(index)
this.pageList = this.pageList.slice(index)
this.linkList.unshift(indexKey)
this.pageList.unshift(indexContent)
if (this.linkList.indexOf(this.activePage) < 0) {
this.activePage = this.linkList[0]
}
},
closeRight (pageKey) {
let index = this.linkList.indexOf(pageKey)
this.linkList = this.linkList.slice(0, index + 1)
this.pageList = this.pageList.slice(0, index + 1)
if (this.linkList.indexOf(this.activePage < 0)) {
this.activePage = this.linkList[this.linkList.length - 1]
}
}
},
}
</script>
<style lang="scss">
/*
* The following styles are auto-applied to elements with
* transition="page-transition" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the page transition by editing
* these styles.
*/
.page-transition-enter {
opacity: 0;
}
.page-transition-leave-active {
opacity: 0;
}
.page-transition-enter .page-transition-container,
.page-transition-leave-active .page-transition-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
/*美化弹出Tab样式*/
.ant-tabs-nav-container {
margin-top: 4px;
}
</style>

150
ant-design-jeecg-vue/src/components/layouts/UserLayout.vue

@ -0,0 +1,150 @@
<template>
<div id="userLayout" :class="['user-layout-wrapper', device]">
<div class="container">
<div class="top">
<div class="header">
<a href="/">
<img src="~@/assets/logo.svg" class="logo" alt="logo">
<span class="title">Jeecg Boot</span>
</a>
</div>
<div class="desc">
Jeecg Boot 是中国最具影响力的 企业级 快速开发平台
</div>
</div>
<route-view></route-view>
<div class="footer">
<div class="links">
<a href="_self">帮助</a>
<a href="_self">隐私</a>
<a href="_self">条款</a>
</div>
<div class="copyright">
Copyright &copy; 2019 <a href="http://www.jeecg.org" target="_blank">JEECG开源社区</a> 出品
</div>
</div>
</div>
</div>
</template>
<script>
import RouteView from "@/components/layouts/RouteView"
import { mixinDevice } from '@/utils/mixin.js'
export default {
name: "UserLayout",
components: { RouteView },
mixins: [mixinDevice],
data () {
return {}
},
mounted () {
document.body.classList.add('userLayout')
},
beforeDestroy () {
document.body.classList.remove('userLayout')
},
}
</script>
<style lang="scss" scoped>
#userLayout.user-layout-wrapper {
height: 100%;
&.mobile {
.container {
.main {
max-width: 368px;
width: 98%;
}
}
}
.container {
width: 100%;
min-height: 100%;
background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
background-size: 100%;
padding: 110px 0 144px;
position: relative;
a {
text-decoration: none;
}
.top {
text-align: center;
.header {
height: 44px;
line-height: 44px;
.badge {
position: absolute;
display: inline-block;
line-height: 1;
vertical-align: middle;
margin-left: -12px;
margin-top: -10px;
opacity: 0.8;
}
.logo {
height: 44px;
vertical-align: top;
margin-right: 16px;
border-style: none;
}
.title {
font-size: 33px;
color: rgba(0, 0, 0, .85);
font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-weight: 600;
position: relative;
top: 2px;
}
}
.desc {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
margin-top: 12px;
margin-bottom: 40px;
}
}
.main {
min-width: 260px;
width: 368px;
margin: 0 auto;
}
.footer {
position: absolute;
width: 100%;
bottom: 0;
padding: 0 16px;
margin: 48px 0 24px;
text-align: center;
.links {
margin-bottom: 8px;
font-size: 14px;
a {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}
}
</style>

8
ant-design-jeecg-vue/src/components/layouts/index.js

@ -0,0 +1,8 @@
import UserLayout from '@/components/layouts/UserLayout'
import BlankLayout from '@/components/layouts/BlankLayout'
import BasicLayout from '@/components/layouts/BasicLayout'
import RouteView from '@/components/layouts/RouteView'
import PageView from '@/components/layouts/PageView'
import TabLayout from '@/components/layouts/TabLayout'
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView, TabLayout }

71
ant-design-jeecg-vue/src/components/menu/Contextmenu.vue

@ -0,0 +1,71 @@
<template>
<a-menu :style="style" class="contextmenu" v-show="visible" @click="handleClick" :selectedKeys="selectedKeys">
<a-menu-item :key="item.key" v-for="item in itemList">
<a-icon role="menuitemicon" v-if="item.icon" :type="item.icon" />{{ item.text }}
</a-menu-item>
</a-menu>
</template>
<script>
export default {
name: 'Contextmenu',
props: {
visible: {
type: Boolean,
required: false,
default: false
},
itemList: {
type: Array,
required: true,
default: () => []
}
},
data () {
return {
left: 0,
top: 0,
target: null,
selectedKeys: []
}
},
computed: {
style () {
return {
left: this.left + 'px',
top: this.top + 'px'
}
}
},
created () {
window.addEventListener('mousedown', e => this.closeMenu(e))
window.addEventListener('contextmenu', e => this.setPosition(e))
},
methods: {
closeMenu (e) {
if (['menuitemicon', 'menuitem'].indexOf(e.target.getAttribute('role')) < 0) {
this.$emit('update:visible', false)
}
},
setPosition (e) {
this.left = e.clientX
this.top = e.clientY
this.target = e.target
},
handleClick ({key}) {
this.$emit('select', key, this.target)
this.$emit('update:visible', false)
}
}
}
</script>
<style lang="less" scoped>
.contextmenu{
position: fixed;
z-index: 1;
border: 1px solid #9e9e9e;
border-radius: 4px;
box-shadow: 2px 2px 10px #aaaaaa !important;
}
</style>

62
ant-design-jeecg-vue/src/components/menu/SideMenu.vue

@ -0,0 +1,62 @@
<template>
<a-layout-sider
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
width="248px"
:collapsible="collapsible"
v-model="collapsed"
:trigger="null">
<logo />
<s-menu
:collapsed="collapsed"
:menu="menus"
:theme="theme"
@select="onSelect"
:mode="mode"
style="padding: 16px 0px;"></s-menu>
</a-layout-sider>
</template>
<script>
import ALayoutSider from "ant-design-vue/es/layout/Sider"
import Logo from '../tools/Logo'
import SMenu from './index'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
name: "SideMenu",
components: { ALayoutSider, Logo, SMenu },
mixins: [mixin, mixinDevice],
props: {
mode: {
type: String,
required: false,
default: 'inline'
},
theme: {
type: String,
required: false,
default: 'dark'
},
collapsible: {
type: Boolean,
required: false,
default: false
},
collapsed: {
type: Boolean,
required: false,
default: false
},
menus: {
type: Array,
required: true
}
},
methods: {
onSelect (obj) {
this.$emit('menuSelect', obj)
}
}
}
</script>

162
ant-design-jeecg-vue/src/components/menu/index.js

@ -0,0 +1,162 @@
import Menu from 'ant-design-vue/es/menu'
import Icon from 'ant-design-vue/es/icon'
const { Item, SubMenu } = Menu
export default {
name: 'SMenu',
props: {
menu: {
type: Array,
required: true
},
theme: {
type: String,
required: false,
default: 'dark'
},
mode: {
type: String,
required: false,
default: 'inline'
},
collapsed: {
type: Boolean,
required: false,
default: false
}
},
data () {
return {
openKeys: [],
selectedKeys: [],
cachedOpenKeys: []
}
},
computed: {
rootSubmenuKeys: (vm) => {
let keys = []
vm.menu.forEach(item => keys.push(item.path))
return keys
}
},
created () {
this.updateMenu()
},
watch: {
collapsed (val) {
if (val) {
this.cachedOpenKeys = this.openKeys
this.openKeys = []
} else {
this.openKeys = this.cachedOpenKeys
}
},
'$route': function () {
this.updateMenu()
}
},
methods: {
renderIcon: function (h, icon) {
return icon === 'none' || icon === undefined ? null
: h(Icon, { props: { type: icon !== undefined ? icon : '' } })
},
renderMenuItem: function (h, menu, pIndex, index) {
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index },
[
h(
'router-link',
{ attrs: { to: { name: menu.name } } },
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
)
]
)
},
renderSubMenu: function (h, menu, pIndex, index) {
const this2_ = this;
let subItem = [ h('span',
{ slot: 'title' },
[
this.renderIcon(h, menu.meta.icon),
h('span', [ menu.meta.title ])
]
) ]
let itemArr = []
let pIndex_ = pIndex + '_' + index
if (!menu.alwaysShow) {
menu.children.forEach(function (item, i) {
itemArr.push(this2_.renderItem(h, item, pIndex_, i))
})
}
return h(
SubMenu,
{ key: menu.path ? menu.path : 'submenu_' + pIndex + '_' + index },
subItem.concat(itemArr)
)
},
renderItem: function (h, menu, pIndex, index) {
if (!menu.hidden) {
return menu.children && !menu.alwaysShow ? this.renderSubMenu(h, menu, pIndex, index) : this.renderMenuItem(h, menu, pIndex, index)
}
},
renderMenu: function (h, menuTree) {
const this2_ = this
let menuArr = []
menuTree.forEach(function (menu, i) {
if (!menu.hidden) {
menuArr.push(this2_.renderItem(h, menu, '0', i))
}
})
return menuArr
},
onOpenChange (openKeys) {
const latestOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1)
if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
this.openKeys = openKeys
} else {
this.openKeys = latestOpenKey ? [ latestOpenKey ] : []
}
},
updateMenu () {
let routes = this.$route.matched.concat()
if (routes.length >= 4 && this.$route.meta.hidden) {
routes.pop()
this.selectedKeys = [ routes[2].path ]
} else {
this.selectedKeys = [ routes.pop().path ]
}
let openKeys = []
if (this.mode === 'inline') {
routes.forEach((item) => {
openKeys.push(item.path)
})
}
this.collapsed ? this.cachedOpenKeys = openKeys : this.openKeys = openKeys
}
},
render (h) {
return h(
Menu,
{
props: {
theme: this.$props.theme,
mode: this.$props.mode,
openKeys: this.openKeys,
selectedKeys: this.selectedKeys
},
on: {
openChange: this.onOpenChange,
select: (obj) => {
this.selectedKeys = obj.selectedKeys
this.$emit('select', obj)
}
}
}, this.renderMenu(h, this.menu)
)
}
}

51
ant-design-jeecg-vue/src/components/page/GlobalFooter.vue

@ -0,0 +1,51 @@
<template>
<div class="footer">
<div class="links">
<a href="http://www.jeecg.org" target="_blank">JEECG 首页</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">
<a-icon type="github"/>
</a>
<a href="https://ant.design/">Ant Design</a>
<a href="https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/">Vue Antd</a>
</div>
<div class="copyright">
Copyright
<a-icon type="copyright"/>
2019 <span>JEECG开源社区 出品</span>
</div>
</div>
</template>
<script>
export default {
name: "LayoutFooter"
}
</script>
<style lang="scss" scoped>
.footer {
padding: 0 16px;
margin: 48px 0 24px;
text-align: center;
.links {
margin-bottom: 8px;
a {
color: rgba(0, 0, 0, .45);
&:hover {
color: rgba(0, 0, 0, .65);
}
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, .45);
font-size: 14px;
}
}
</style>

127
ant-design-jeecg-vue/src/components/page/GlobalHeader.vue

@ -0,0 +1,127 @@
<template>
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :style="{ padding: '0' }">
<div v-if="mode === 'sidemenu'" class="header">
<a-icon
v-if="device==='mobile'"
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click.native="toggle"></a-icon>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click.native="toggle"/>
<span>欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
<user-menu></user-menu>
</div>
<div v-else :class="['top-nav-header-index', theme]">
<div class="header-index-wide">
<div class="header-index-left">
<logo class="top-nav-header" :show-title="device !== 'mobile'" />
<s-menu
v-if="device !== 'mobile'"
mode="horizontal"
:menu="menus"
:theme="theme"
></s-menu>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click.native="toggle"></a-icon>
</div>
<user-menu class="header-index-right"></user-menu>
</div>
</div>
</a-layout-header>
</template>
<script>
import UserMenu from '../tools/UserMenu'
import SMenu from '../menu/'
import Logo from '../tools/Logo'
import { mixin } from '@/utils/mixin.js'
export default {
name: "GlobalHeader",
components: {
UserMenu,
SMenu,
Logo
},
mixins: [mixin],
props: {
mode: {
type: String,
// sidemenu, topmenu
default: 'sidemenu'
},
menus: {
type: Array,
required: true
},
theme: {
type: String,
required: false,
default: 'dark'
},
collapsed: {
type: Boolean,
required: false,
default: false
},
device: {
type: String,
required: false,
default: 'desktop'
}
},
data() {
return {
headerBarFixed: false,
}
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
},
methods: {
handleScroll () {
if (this.autoHideHeader) {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop > 100) {
this.headerBarFixed = true
} else {
this.headerBarFixed = false
}
} else {
this.headerBarFixed = false
}
},
toggle() {
this.$emit('toggle')
}
}
}
</script>
<style lang="scss" scoped>
/* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
.layout .top-nav-header-index .header-index-wide {
margin-left: 20px
}
.layout .header {
height: 59px;
}
.ant-layout-header {
height: 59px;
line-height: 59px;
}
.layout .top-nav-header-index .header-index-wide .ant-menu.ant-menu-horizontal {
height: 59px;
line-height: 59px;
}
/* update_end author:scott date:20190220 for: 缩小首页布局顶部的高度*/
</style>

596
ant-design-jeecg-vue/src/components/page/GlobalLayout.vue

@ -0,0 +1,596 @@
<template>
<a-layout class="layout" :class="[device]">
<template v-if="layoutMode === 'sidemenu'">
<a-drawer
v-if="device === 'mobile'"
:wrapClassName="'drawer-sider ' + navTheme"
placement="left"
@close="() => this.collapsed = false"
:closable="false"
:visible="collapsed"
>
<side-menu
mode="inline"
:menus="menus"
@menuSelect="menuSelect"
:theme="navTheme"
:collapsed="false"
:collapsible="true"></side-menu>
</a-drawer>
<side-menu
v-else
mode="inline"
:menus="menus"
:theme="navTheme"
:collapsed="collapsed"
:collapsible="true"></side-menu>
</template>
<!-- 下次优化这些代码 -->
<template v-else>
<a-drawer
v-if="device === 'mobile'"
:wrapClassName="'drawer-sider ' + navTheme"
placement="left"
@close="() => this.collapsed = false"
:closable="false"
:visible="collapsed"
>
<side-menu
mode="inline"
:menus="menus"
@menuSelect="menuSelect"
:theme="navTheme"
:collapsed="false"
:collapsible="true"></side-menu>
</a-drawer>
</template>
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: fixSiderbar && isDesktop() ? `${sidebarOpened ? 256 : 80}px` : '0' }">
<!-- layout header -->
<global-header
:mode="layoutMode"
:menus="menus"
:theme="navTheme"
:collapsed="collapsed"
:device="device"
@toggle="toggle"
/>
<!-- layout content -->
<a-layout-content :style="{ margin: '10px 24px 0', height: '100%', paddingTop: fixedHeader ? '64px' : '0' }">
<slot></slot>
</a-layout-content>
<!-- layout footer -->
<a-layout-footer style="padding: 0px">
<global-footer />
</a-layout-footer>
</a-layout>
<setting-drawer></setting-drawer>
</a-layout>
</template>
<script>
import SideMenu from '@/components/menu/SideMenu'
import GlobalHeader from '@/components/page/GlobalHeader'
import GlobalFooter from '@/components/page/GlobalFooter'
import SettingDrawer from '@/components/setting/SettingDrawer'
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapState, mapActions } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
name: "GlobalLayout",
components: {
SideMenu,
GlobalHeader,
GlobalFooter,
SettingDrawer
},
mixins: [mixin, mixinDevice],
data () {
return {
collapsed: false,
menus: []
}
},
computed: {
...mapState({
//
mainMenu: state => state.permission.addRouters,
})
},
watch: {
sidebarOpened(val) {
this.collapsed = !val
},
},
created() {
this.menus = this.mainMenu.find((item) => item.path === '/').children
},
methods: {
...mapActions(['setSidebar']),
toggle() {
this.collapsed = !this.collapsed
this.setSidebar(!this.collapsed)
triggerWindowResizeEvent()
},
menuSelect() {
if (!this.isDesktop()) {
this.collapsed = false
}
}
},
}
</script>
<style lang="scss">
body {
//
overflow-y: scroll;
&.colorWeak {
filter: invert(80%);
}
}
.layout {
min-height: 100vh;
overflow-x: hidden;
&.mobile {
.ant-layout-content {
.content {
margin: 24px 0 0;
}
}
/**
* ant-table-wrapper
* 覆盖的表格手机模式样式如果想修改在手机上表格最低宽度可以在这里改动
*/
.ant-table-wrapper {
.ant-table-content {
overflow-y: auto;
}
.ant-table-body {
min-width: 800px;
}
}
.sidemenu {
.ant-header-fixedHeader {
&.ant-header-side-opened, &.ant-header-side-closed {
width: 100%
}
}
}
.topmenu {
/* 必须为 topmenu 才能启用流式布局 */
&.content-width-Fluid {
.header-index-wide {
margin-left: 0;
}
}
}
}
&.ant-layout-has-sider {
flex-direction: row;
}
.trigger {
font-size: 20px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color .3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.topmenu {
.ant-header-fixedHeader {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: 100%;
transition: width .2s;
&.ant-header-side-opened {
width: 100%;
}
&.ant-header-side-closed {
width: 100%;
}
}
/* 必须为 topmenu 才能启用流式布局 */
&.content-width-Fluid {
.header-index-wide {
max-width: unset;
margin-left: 24px;
}
.page-header-index-wide {
max-width: unset;
}
}
}
.sidemenu {
.ant-header-fixedHeader {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: 100%;
transition: width .2s;
&.ant-header-side-opened {
width: calc(100% - 256px)
}
&.ant-header-side-closed {
width: calc(100% - 80px)
}
}
}
.header {
height: 64px;
padding: 0 12px 0 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
position: relative;
}
.header, .top-nav-header-index {
.user-wrapper {
float: right;
height: 100%;
.action {
cursor: pointer;
padding: 0 12px;
display: inline-block;
transition: all .3s;
height: 100%;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
.avatar {
margin: 20px 8px 20px 0;
color: #1890ff;
background: hsla(0, 0%, 100%, .85);
vertical-align: middle;
}
.icon {
font-size: 16px;
padding: 4px;
}
}
}
&.dark {
.user-wrapper {
.action {
color: rgba(255, 255, 255, 0.85);
&:hover {
background: rgba(255, 255, 255, 0.16);
}
}
}
}
}
&.mobile {
.top-nav-header-index {
.header-index-wide {
.header-index-left {
.trigger {
color: rgba(255, 255, 255, 0.85);
padding: 0 12px;
}
.logo.top-nav-header {
text-align: center;
width: 56px;
line-height: 58px;
}
}
}
&.light {
.header-index-wide {
.header-index-left {
.trigger {
color: rgba(0, 0, 0, 0.65);
}
}
}
//
}
}
}
&.tablet {
// overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
.top-nav-header-index {
.header-index-wide {
.header-index-left {
.logo > a {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
}
}
}
.top-nav-header-index {
box-shadow: 0 1px 4px rgba(0,21,41,.08);
position: relative;
transition: background .3s,width .2s;
.header-index-wide {
max-width: 1200px;
margin: auto;
padding-left: 0;
display: flex;
height: 64px;
.ant-menu.ant-menu-horizontal {
border: none;
height: 64px;
line-height: 64px;
}
.header-index-left {
flex: 1 1;
display: flex;
.logo.top-nav-header {
width: 165px;
height: 64px;
position: relative;
line-height: 64px;
transition: all .3s;
overflow: hidden;
img {
display: inline-block;
vertical-align: middle;
height: 32px;
}
h1 {
color: #fff;
display: inline-block;
vertical-align: top;
font-size: 16px;
margin: 0 0 0 12px;
font-weight: 400;
}
}
}
.header-index-right {
float: right;
height: 64px;
overflow: hidden;
}
}
&.light {
background-color: #fff;
.header-index-wide {
.header-index-left {
.logo {
h1 {
color: #002140;
}
}
}
}
}
}
//
.layout-content {
margin: 24px 24px 0px;
height: 100%;
height: 64px;
padding: 0 12px 0 0;
}
}
.topmenu {
.page-header-index-wide {
max-width: 1200px;
margin: 0 auto;
}
}
// drawer-sider
.ant-drawer.drawer-sider {
.sider {
box-shadow: none;
}
&.dark {
.ant-drawer-content {
background-color: rgb(0, 21, 41);
}
}
&.light {
box-shadow: none;
.ant-drawer-content {
background-color: #fff;
}
}
.ant-drawer-body {
padding: 0
}
}
//
.sider {
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
position: relative;
z-index: 10;
&.ant-fixed-sidemenu {
position: fixed;
height: 100%;
}
.logo {
height: 64px;
position: relative;
line-height: 64px;
padding-left: 24px;
-webkit-transition: all .3s;
transition: all .3s;
background: #002140;
overflow: hidden;
img, h1 {
display: inline-block;
vertical-align: middle;
}
img {
height: 32px;
}
h1 {
color: #fff;
font-size: 20px;
margin: 0 0 0 12px;
font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-weight: 600;
}
}
&.light {
background-color: #fff;
box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05);
.logo {
background: #fff;
box-shadow: 1px 1px 0px 0px #e8e8e8;
h1 {
color: unset;
}
}
.ant-menu-light {
border-right-color: transparent;
}
}
}
//
.user-dropdown-menu-wrapper.ant-dropdown-menu {
padding: 4px 0;
.ant-dropdown-menu-item {
width: 160px;
}
.ant-dropdown-menu-item > .anticon:first-child,
.ant-dropdown-menu-item > a > .anticon:first-child,
.ant-dropdown-menu-submenu-title > .anticon:first-child
.ant-dropdown-menu-submenu-title > a > .anticon:first-child {
min-width: 12px;
margin-right: 8px;
}
}
//
.table-alert {
margin-bottom: 16px;
}
.table-page-search-wrapper {
.ant-form-inline {
.ant-form-item {
display: flex;
margin-bottom: 24px;
margin-right: 0;
.ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
vertical-align: middle;
}
>.ant-form-item-label {
line-height: 32px;
padding-right: 8px;
width: auto;
}
.ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
}
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
.content {
.table-operator {
margin-bottom: 18px;
button {
margin-right: 8px;
}
}
}
</style>

238
ant-design-jeecg-vue/src/components/page/PageHeader.vue

@ -0,0 +1,238 @@
<template>
<div class="page-header">
<div class="page-header-index-wide">
<a-breadcrumb class="breadcrumb">
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
<router-link v-if="item.name != name" :to="{ path: item.path }">
{{ item.meta.title }}
</router-link>
<span v-else>{{ item.meta.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
<div class="detail">
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
<div class="row">
<img v-if="logo" :src="logo" class="logo"/>
<h1 v-if="title" class="title">{{ title }}</h1>
<div class="action">
<slot name="action"></slot>
</div>
</div>
<div class="row">
<div v-if="avatar" class="avatar">
<a-avatar :src="avatar"/>
</div>
<div v-if="this.$slots.content" class="headerContent">
<slot name="content"></slot>
</div>
<div v-if="this.$slots.extra" class="extra">
<slot name="extra"></slot>
</div>
</div>
<div>
<slot name="pageMenu"></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Breadcrumb from '@/components/tools/Breadcrumb'
export default {
name: "PageHeader",
components: {
"s-breadcrumb": Breadcrumb
},
props: {
title: {
type: String,
default: '',
required: false
},
breadcrumb: {
type: Array,
default: null,
required: false
},
logo: {
type: String,
default: '',
required: false
},
avatar: {
type: String,
default: '',
required: false
}
},
data() {
return {
name: '',
breadList: [],
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
this.breadList = []
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}})
this.name = this.$route.name
this.$route.matched.forEach((item) => {
// item.name !== 'index' && this.breadList.push(item)
this.breadList.push(item)
})
}
},
watch: {
$route() {
this.getBreadcrumb()
}
}
}
</script>
<style lang="scss" scoped>
.page-header {
background: #fff;
padding: 16px 32px 0;
border-bottom: 1px solid #e8e8e8;
.breadcrumb {
margin-bottom: 16px;
}
.detail {
display: flex;
/*margin-bottom: 16px;*/
.avatar {
flex: 0 1 72px;
margin: 0 24px 8px 0;
& > span {
border-radius: 72px;
display: block;
width: 72px;
height: 72px;
}
}
.main {
width: 100%;
flex: 0 1 auto;
.row {
display: flex;
width: 100%;
.avatar {
margin-bottom: 16px;
}
}
.title {
font-size: 20px;
font-weight: 500;
font-size: 20px;
line-height: 28px;
font-weight: 500;
color: rgba(0,0,0,.85);
margin-bottom: 16px;
flex: auto;
}
.logo {
width: 28px;
height: 28px;
border-radius: 4px;
margin-right: 16px;
}
.content, .headerContent {
flex: auto;
color: rgba(0,0,0,.45);
line-height: 22px;
.link {
margin-top: 16px;
line-height: 24px;
a {
font-size: 14px;
margin-right: 32px;
}
}
}
.extra {
flex: 0 1 auto;
margin-left: 88px;
min-width: 242px;
text-align: right;
}
.action {
margin-left: 56px;
min-width: 266px;
flex: 0 1 auto;
text-align: right;
&:empty {
display: none;
}
}
}
}
}
.mobile .page-header {
.main {
.row {
flex-wrap: wrap;
.avatar {
flex: 0 1 25%;
margin: 0 2% 8px 0;
}
.content, .headerContent {
flex: 0 1 70%;
.link {
margin-top: 16px;
line-height: 24px;
a {
font-size: 14px;
margin-right: 10px;
}
}
}
.extra {
flex: 1 1 auto;
margin-left: 0;
min-width: 0;
text-align: right;
}
.action {
margin-left: unset;
min-width: 266px;
flex: 0 1 auto;
text-align: left;
margin-bottom: 12px;
&:empty {
display: none;
}
}
}
}
}
</style>

127
ant-design-jeecg-vue/src/components/page/PageLayout.vue

@ -0,0 +1,127 @@
<template>
<div :style="!$route.meta.pageHeader ? 'margin: -16px -24px 0px;' : null">
<!-- pageHeader , route meta hideHeader:true on hide -->
<page-header v-if="!$route.meta.pageHeader" :title="title" :logo="logo" :avatar="avatar">
<slot slot="action" name="action"></slot>
<slot slot="content" name="headerContent"></slot>
<div slot="content" v-if="!this.$slots.headerContent && desc">
<p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ desc }}</p>
<div class="link">
<template v-for="(link, index) in linkList">
<a :key="index" :href="link.href">
<a-icon :type="link.icon"/>
<span>{{ link.title }}</span>
</a>
</template>
</div>
</div>
<slot slot="extra" name="extra"></slot>
<div slot="pageMenu">
<div class="page-menu-search" v-if="search">
<a-input-search style="width: 80%; max-width: 522px;" placeholder="请输入..." size="large" enterButton="搜索" />
</div>
<div class="page-menu-tabs" v-if="tabs && tabs.items">
<!-- @change="callback" :activeKey="activeKey" -->
<a-tabs :tabBarStyle="{margin: 0}" @change="tabs.callback" :activeKey="tabs.active()">
<a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
</a-tabs>
</div>
</div>
</page-header>
<div class="content">
<div :class="['page-header-index-wide']">
<slot></slot>
</div>
</div>
</div>
</template>
<script>
import PageHeader from './PageHeader'
export default {
name: "LayoutContent",
components: {
PageHeader
},
// ['desc', 'logo', 'title', 'avatar', 'linkList', 'extraImage']
props: {
desc: {
type: String,
default: null
},
logo: {
type: String,
default: null
},
title: {
type: String,
default: null
},
avatar: {
type: String,
default: null
},
linkList: {
type: Array,
default: null
},
extraImage: {
type: String,
default: null
},
search: {
type: Boolean,
default: false
},
tabs: {
type: Object,
default: () => {}
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.content {
margin: 24px 24px 0;
.link {
margin-top: 16px;
&:not(:empty) {
margin-bottom: 16px;
}
a {
margin-right: 32px;
height: 24px;
line-height: 24px;
display: inline-block;
i {
font-size: 24px;
margin-right: 8px;
vertical-align: middle;
}
span {
height: 24px;
line-height: 24px;
display: inline-block;
vertical-align: middle;
}
}
}
}
.page-menu-search {
text-align: center;
margin-bottom: 16px;
}
.page-menu-tabs {
margin-top: 48px;
}
.page-header[data-v-6740ec88] {
margin: 0px 24px 0;
}
</style>

58
ant-design-jeecg-vue/src/components/page/SHeaderNotice.vue

@ -0,0 +1,58 @@
<template>
<a-popover trigger="click" placement="bottomRight" :overlayStyle="{ width: '300px' }">
<template slot="content">
<a-spin :spinning="loadding">
<a-tabs>
<a-tab-pane v-for="(tab, k) in tabs" :tab="tab.title" :key="k">
</a-tab-pane>
</a-tabs>
</a-spin>
</template>
<span @click="fetchNotice" class="header-notice">
<a-badge count="12">
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
</a-badge>
</span>
</a-popover>
</template>
<script>
export default {
name: "HeaderNotice",
props: {
tabs: {
type: Array,
default: null,
required: true
}
},
data () {
return {
loadding: false
}
},
methods: {
fetchNotice () {
if (this.loadding) {
this.loadding = false
return
}
this.loadding = true
setTimeout(() => {
this.loadding = false
}, 2000)
}
}
}
</script>
<style lang="scss" scoped>
.header-notice{
display: inline-block;
transition: all 0.3s;
span {
vertical-align: initial;
}
}
</style>

314
ant-design-jeecg-vue/src/components/setting/SettingDrawer.vue

@ -0,0 +1,314 @@
<template>
<div class="setting-drawer">
<a-drawer
width="300"
placement="right"
:closable="false"
@close="onClose"
:visible="visible"
:style="{}"
>
<div class="setting-drawer-index-content">
<div :style="{ marginBottom: '24px' }">
<h3 class="setting-drawer-index-title">整体风格设置</h3>
<div class="setting-drawer-index-blockChecbox">
<a-tooltip>
<template slot="title">
暗色菜单风格
</template>
<div class="setting-drawer-index-item" @click="handleMenuTheme('dark')">
<img src="https://gw.alipayobjects.com/zos/rmsportal/LCkqqYNmvBEbokSDscrm.svg" alt="dark">
<div class="setting-drawer-index-selectIcon" v-if="navTheme === 'dark'">
<a-icon type="check"/>
</div>
</div>
</a-tooltip>
<a-tooltip>
<template slot="title">
亮色菜单风格
</template>
<div class="setting-drawer-index-item" @click="handleMenuTheme('light')">
<img src="https://gw.alipayobjects.com/zos/rmsportal/jpRkZQMyYRryryPNtyIC.svg" alt="light">
<div class="setting-drawer-index-selectIcon" v-if="navTheme !== 'dark'">
<a-icon type="check"/>
</div>
</div>
</a-tooltip>
</div>
</div>
<div :style="{ marginBottom: '24px' }">
<h3 class="setting-drawer-index-title">主题色</h3>
<div style="height: 20px">
<a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index">
<template slot="title">
{{ item.key }}
</template>
<a-tag :color="item.color" @click="changeColor(item.color)">
<a-icon type="check" v-if="item.color === primaryColor"></a-icon>
</a-tag>
</a-tooltip>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<h3 class="setting-drawer-index-title">导航模式</h3>
<div class="setting-drawer-index-blockChecbox">
<a-tooltip>
<template slot="title">
侧边栏导航
</template>
<div class="setting-drawer-index-item" @click="handleLayout('sidemenu')">
<img src="https://gw.alipayobjects.com/zos/rmsportal/JopDzEhOqwOjeNTXkoje.svg" alt="sidemenu">
<div class="setting-drawer-index-selectIcon" v-if="layoutMode === 'sidemenu'">
<a-icon type="check"/>
</div>
</div>
</a-tooltip>
<a-tooltip>
<template slot="title">
顶部栏导航
</template>
<div class="setting-drawer-index-item" @click="handleLayout('topmenu')">
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDNDBbriJhLwuqMoxcAr.svg" alt="topmenu">
<div class="setting-drawer-index-selectIcon" v-if="layoutMode !== 'sidemenu'">
<a-icon type="check"/>
</div>
</div>
</a-tooltip>
</div>
<div :style="{ marginTop: '24px' }">
<a-list :split="false">
<a-list-item>
<a-tooltip slot="actions">
<template slot="title">
该设定仅 [顶部栏导航] 时有效
</template>
<a-select size="small" style="width: 80px;" :defaultValue="contentWidth" @change="handleContentWidthChange">
<a-select-option value="Fixed">固定</a-select-option>
<a-select-option value="Fluid" v-if="layoutMode !== 'sidemenu'">流式</a-select-option>
</a-select>
</a-tooltip>
<a-list-item-meta>
<div slot="title">内容区域宽度</div>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-switch slot="actions" size="small" :defaultChecked="fixedHeader" @change="handleFixedHeader" />
<a-list-item-meta>
<div slot="title">固定 Header</div>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-switch slot="actions" size="small" :disabled="!fixedHeader" :defaultChecked="autoHideHeader" @change="handleFixedHeaderHidden" />
<a-list-item-meta>
<div slot="title" :style="{ textDecoration: !fixedHeader ? 'line-through' : 'unset' }">下滑时隐藏 Header</div>
</a-list-item-meta>
</a-list-item>
<a-list-item >
<a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :defaultChecked="fixSiderbar" @change="handleFixSiderbar" />
<a-list-item-meta>
<div slot="title" :style="{ textDecoration: layoutMode === 'topmenu' ? 'line-through' : 'unset' }">固定侧边菜单</div>
</a-list-item-meta>
</a-list-item>
</a-list>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<h3 class="setting-drawer-index-title">其他设置</h3>
<div>
<a-list :split="false">
<a-list-item>
<a-switch slot="actions" size="small" :defaultChecked="colorWeak" @change="onColorWeak" />
<a-list-item-meta>
<div slot="title">色弱模式</div>
</a-list-item-meta>
</a-list-item>
</a-list>
</div>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<a-alert type="warning">
<span slot="message">
配置栏只在开发环境用于预览生产环境不会展现请手动修改配置文件
<a href="https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js" target="_blank">src/defaultSettings.js</a>
</span>
</a-alert>
</div>
</div>
<div class="setting-drawer-index-handle" @click="toggle">
<a-icon type="setting" v-if="!visible"/>
<a-icon type="close" v-else/>
</div>
</a-drawer>
</div>
</template>
<script>
import DetailList from '@/components/tools/DetailList'
import SettingItem from '@/components/setting/SettingItem'
import config from '@/defaultSettings'
import { updateTheme, updateColorWeak, colorList } from '@/components/tools/setting'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
components: {
DetailList,
SettingItem
},
mixins: [mixin, mixinDevice],
data() {
return {
visible: true,
colorList,
}
},
watch: {
},
mounted () {
const vm = this
setTimeout(() => {
vm.visible = false
}, 16)
//
if (this.primaryColor !== config.primaryColor) {
updateTheme(this.primaryColor)
}
if (this.colorWeak !== config.colorWeak) {
updateColorWeak(this.colorWeak)
}
},
methods: {
showDrawer() {
this.visible = true
},
onClose() {
this.visible = false
},
toggle() {
this.visible = !this.visible
},
onColorWeak (checked) {
this.$store.dispatch('ToggleWeak', checked)
updateColorWeak(checked)
},
handleMenuTheme (theme) {
this.$store.dispatch('ToggleTheme', theme)
},
handleLayout (mode) {
this.$store.dispatch('ToggleLayoutMode', mode)
//
//
this.handleFixSiderbar(false);
},
handleContentWidthChange (type) {
this.$store.dispatch('ToggleContentWidth', type)
},
changeColor (color) {
if (this.primaryColor !== color) {
this.$store.dispatch('ToggleColor', color)
updateTheme(color)
}
},
handleFixedHeader (fixed) {
this.$store.dispatch('ToggleFixedHeader', fixed)
},
handleFixedHeaderHidden (autoHidden) {
this.$store.dispatch('ToggleFixedHeaderHidden', autoHidden)
},
handleFixSiderbar (fixed) {
if (this.layoutMode === 'topmenu') {
this.$store.dispatch('ToggleFixSiderbar', false)
return;
}
this.$store.dispatch('ToggleFixSiderbar', fixed)
}
},
}
</script>
<style lang="scss" scoped>
.setting-drawer-index-content {
.setting-drawer-index-blockChecbox {
display: flex;
.setting-drawer-index-item {
margin-right: 16px;
position: relative;
border-radius: 4px;
cursor: pointer;
img {
width: 48px;
}
.setting-drawer-index-selectIcon {
position: absolute;
top: 0;
right: 0;
width: 100%;
padding-top: 15px;
padding-left: 24px;
height: 100%;
color: #1890ff;
font-size: 14px;
font-weight: 700;
}
}
}
.setting-drawer-theme-color-colorBlock {
width: 20px;
height: 20px;
border-radius: 2px;
float: left;
cursor: pointer;
margin-right: 8px;
padding-left: 0px;
padding-right: 0px;
text-align: center;
color: #fff;
font-weight: 700;
i {
font-size: 14px;
}
}
}
.setting-drawer-index-handle {
position: absolute;
top: 240px;
background: #1890ff;
width: 48px;
height: 48px;
right: 300px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
pointer-events: auto;
z-index: 1001;
text-align: center;
font-size: 16px;
border-radius: 4px 0 0 4px;
i {
color: rgb(255, 255, 255);
font-size: 20px;
}
}
</style>

38
ant-design-jeecg-vue/src/components/setting/SettingItem.vue

@ -0,0 +1,38 @@
<template>
<div class="setting-drawer-index-item">
<h3 class="setting-drawer-index-title">{{ title }}</h3>
<slot></slot>
<a-divider v-if="divider"/>
</div>
</template>
<script>
export default {
name: "SettingItem",
props: {
title: {
type: String,
default: ''
},
divider: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss" scoped>
.setting-drawer-index-item {
margin-bottom: 24px;
.setting-drawer-index-title {
font-size: 14px;
color: rgba(0, 0, 0, .85);
line-height: 22px;
margin-bottom: 12px;
}
}
</style>

292
ant-design-jeecg-vue/src/components/table/README.md

@ -0,0 +1,292 @@
Table 重封装组件说明
====
封装说明
----
> 基础的使用方式与 API 与 [官方版(Table)](https://vuecomponent.github.io/ant-design-vue/components/table-cn/) 本一致,在其基础上,封装了加载数据的方法。
>
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 Table 组件传递绑定 `:data="Promise"` 对象即可
例子1
----
(基础使用)
```vue
<template>
<s-table
ref="table"
:rowKey="(record) => record.data.id"
size="default"
:columns="columns"
:data="loadData"
>
</s-table>
</template>
<script>
import STable from '@/components/table/'
export default {
components: {
STable
},
data() {
return {
columns: [
{
title: '规则编号',
dataIndex: 'no'
},
{
title: '描述',
dataIndex: 'description'
},
{
title: '服务调用次数',
dataIndex: 'callNo',
sorter: true,
needTotal: true,
customRender: (text) => text + ' 次'
},
{
title: '状态',
dataIndex: 'status',
needTotal: true
},
{
title: '更新时间',
dataIndex: 'updatedAt',
sorter: true
}
],
// 查询条件参数
queryParam: {},
// 加载数据方法 必须为 Promise 对象
loadData: parameter => {
return this.$http.get('/service', {
params: Object.assign(parameter, this.queryParam)
}).then(res => {
return res.result
})
},
}
}
}
</script>
```
例子2
----
(简单的表格,最后一列是各种操作)
```vue
<template>
<s-table
ref="table"
size="default"
:columns="columns"
:data="loadData"
>
<span slot="action" slot-scope="text, record">
<a>编辑</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">
更多 <a-icon type="down"/>
</a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;">1st menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">2nd menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">3rd menu item</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
</s-table>
</template>
<script>
import STable from '@/components/table/'
export default {
components: {
STable
},
data() {
return {
columns: [
{
title: '规则编号',
dataIndex: 'no'
},
{
title: '描述',
dataIndex: 'description'
},
{
title: '服务调用次数',
dataIndex: 'callNo',
},
{
title: '状态',
dataIndex: 'status',
},
{
title: '更新时间',
dataIndex: 'updatedAt',
},
{
table: '操作',
dataIndex: 'action',
scopedSlots: {customRender: 'action'},
}
],
// 查询条件参数
queryParam: {},
// 加载数据方法 必须为 Promise 对象
loadData: parameter => {
return this.$http.get('/service', {
params: Object.assign(parameter, this.queryParam)
}).then(res => {
return res.result
})
},
}
},
methods: {
edit(row) {
// axios 发送数据到后端 修改数据成功后
// 调用 refresh() 重新加载列表数据
// 这里 setTimeout 模拟发起请求的网络延迟..
setTimeout(() => {
this.$refs.table.refresh()
}, 1500)
}
}
}
</script>
```
内置方法
----
通过 `this.$refs.table` 调用
`this.$refs.table.refresh()` 刷新列表 (用户新增/修改数据后,重载列表数据)
> 注意:要调用 `refresh()` 需要给表格组件设定 `ref`
注意事项
----
> 你可能需要为了与后端提供的接口返回结果一致而去修改以下代码:
(需要注意的是,这里的修改是全局性的,意味着整个项目所有使用该 table 组件都需要遵守这个返回结果定义的字段。)
修改 `@/components/table/index.js` 第 106 行起
```javascript
result.then(r => {
this.localPagination = Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
});
!r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
this.localDataSource = r.data; // 返回结果中的数组数据
this.localLoading = false
});
```
返回 JSON 例子:
```json
{
"message": "",
"result": {
"data": [{
id: 1,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
title: 'Alipay',
description: '那是一种内在的东西, 他们到达不了,也无法触及的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 2,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
title: 'Angular',
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 3,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
title: 'Ant Design',
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 4,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
title: 'Ant Design Pro',
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 5,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
title: 'Bootstrap',
description: '凛冬将至',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 6,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png',
title: 'Vue',
description: '生命就像一盒巧克力,结果往往出人意料',
status: 1,
updatedAt: '2018-07-26 00:00:00'
}
],
"pageSize": 10,
"pageNo": 0,
"totalPage": 6,
"totalCount": 57
},
"status": 200,
"timestamp": 1534955098193
}
```
更新时间
----
该文档最后更新于: 2018-10-31 PM 08:15

252
ant-design-jeecg-vue/src/components/table/StandardTable.vue

@ -0,0 +1,252 @@
<template>
<div class="standard-table">
<div class="alert">
<a-alert type="info" :show-icon="true">
<div slot="message">
已选择&nbsp;<a style="font-weight: 600">{{ selectedRows.length }}</a>&nbsp;&nbsp;
<template v-for="(item, index) in needTotalList" v-if="item.needTotal">
{{ item.title }} 总计&nbsp;
<a :key="index" style="font-weight: 600">
{{ item.customRender ? item.customRender(item.total) : item.total }}
</a>&nbsp;&nbsp;
</template>
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
</a-alert>
</div>
<a-table
:size="size"
:bordered="bordered"
:loading="loading"
:columns="columns"
:dataSource="current"
:rowKey="rowKey"
:pagination="pagination"
:rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: updateSelect }"
>
</a-table>
</div>
</template>
<script>
export default {
name: "StandardTable",
// props: ['bordered', 'loading', 'columns', 'data', 'rowKey', 'pagination', 'selectedRows'],
props: {
/**
* 数据加载函数返回值必须是 Promise
* 默认情况下必须传递 data 参数
* 如果使用本地数据渲染表格业务代码中将获取本地数据包装为 Promise 即可
*
* currentData 用于向外暴露表格当前渲染的数据
* 业务开发中也可以直接修改 currentData从而重新渲染表格仅推荐用于客户端排序数据过滤等场景
*/
data: {
type: Function,
required: true
},
dataSource: {
type: Array,
default () {
return []
}
},
columns: {
type: Array,
required: true
},
/* pagination: {
type: Object,
default () {
return {}
}
},*/
pageSize: {
type: Number,
default: 10
},
pageNum: {
type: Number,
default: 1
},
pageSizeOptions: {
type: Array,
default () {
return ['10', '20', '30', '40', '50']
}
},
responseParamsName: {
type: Object,
default () {
return {}
}
},
bordered: {
type: Boolean,
default: false
},
/**
* 表格大小风格default, middle, small
*/
size: {
type: String,
default: 'default'
},
rowKey: {
type: String,
default: ''
},
selectedRows: {
type: Array,
default: null
}
},
data () {
return {
needTotalList: [],
selectedRowKeys: [],
loading: true,
total: 0,
pageNumber: this.pageNum,
currentPageSize: this.pageSize,
defaultCurrent: 1,
sortParams: {},
current: [],
pagination: {},
paramsName: {},
}
},
created () {
//
this.paramsName = Object.assign(
{},
{
pageNumber: "pageNo",
pageSize: "pageSize",
total: "totalCount",
results: "data",
sortColumns: "sortColumns"
},
this.responseParamsName
);
this.needTotalList = this.initTotalList(this.columns)
// load data
this.loadData( { pageNum: this.pageNumber } )
},
methods: {
updateSelect (selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
let list = this.needTotalList
this.needTotalList = list.map(item => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
return sum + val[item.dataIndex]
}, 0)
}
})
this.$emit('change', selectedRowKeys, selectedRows)
},
initTotalList (columns) {
const totalList = []
columns.forEach(column => {
if (column.needTotal) {
totalList.push({ ...column, total: 0 })
}
})
return totalList
},
loadData (params) {
let that = this
that.loading = true
params = Object.assign({}, params)
const remoteParams = Object.assign({}, that.sortParams)
remoteParams[that.paramsName.pageNumber] = params.pageNum || that.pageNumber
remoteParams[that.paramsName.pageSize] = params.pageSize || that.currentPageSize
if (params.pageNum) {
that.pageNumber = params.pageNum
}
if (params.pageSize) {
that.currentPageSize = params.pageSize
}
let dataPromise = that.data(remoteParams)
dataPromise.then( response => {
if (!response) {
that.loading = false
return
}
let results = response[that.paramsName.results]
results = (results instanceof Array && results) || []
that.current = results
that.$emit("update:currentData", that.current.slice())
that.$emit("dataloaded", that.current.slice())
that.total = response[that.paramsName.total] * 1
that.pagination = that.pager()
that.loading = false
}, () => {
// error callback
that.loading = false
})
},
// eslint-disable-next-line
onPagerChange (page, pageSize) {
this.pageNumber = page
this.loadData({ pageNum: page })
},
onPagerSizeChange (current, size) {
this.currentPageSize = size
/*
if (current === this.pageNumber) this.loadData()
console.log('page-size-change', current, size)
*/
},
onClearSelected () {
this.selectedRowKeys = []
this.updateSelect([], [])
},
pager () {
return {
total: this.total,
showTotal: total => `共有 ${total}`,
showSizeChanger: true,
pageSizeOptions: this.pageSizeOptions,
pageSize: this.pageSize,
defaultCurrent: this.defaultCurrent,
onChange: this.onPagerChange,
onShowSizeChange: this.onPagerSizeChange
}
}
},
watch: {
'selectedRows': function (selectedRows) {
this.needTotalList = this.needTotalList.map(item => {
return {
...item,
total: selectedRows.reduce( (sum, val) => {
return sum + val[item.dataIndex]
}, 0)
}
})
}
}
}
</script>
<style scoped>
.alert {
margin-bottom: 16px;
}
</style>

265
ant-design-jeecg-vue/src/components/table/index.js

@ -0,0 +1,265 @@
import T from "ant-design-vue/es/table/Table";
import get from "lodash.get"
export default {
data() {
return {
needTotalList: [],
selectedRows: [],
selectedRowKeys: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, T.props.pagination)
};
},
props: Object.assign({}, T.props, {
rowKey: {
type: [String, Function],
default: 'id'
},
data: {
type: Function,
required: true
},
pageNum: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
showSizeChanger: {
type: Boolean,
default: true
},
showAlertInfo: {
type: Boolean,
default: false
},
showPagination: {
default: 'auto'
}
}),
watch: {
'localPagination.current'(val) {
this.$router.push({
name: this.$route.name,
params: Object.assign({}, this.$route.params, {
pageNo: val
}),
});
},
pageNum(val) {
Object.assign(this.localPagination, {
current: val
});
},
pageSize(val) {
console.log('pageSize:', val)
Object.assign(this.localPagination, {
pageSize: val
});
},
showSizeChanger(val) {
console.log('showSizeChanger', val)
Object.assign(this.localPagination, {
showSizeChanger: val
});
}
},
created() {
this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {
current: this.pageNum,
pageSize: this.pageSize,
showSizeChanger: this.showSizeChanger
});
this.needTotalList = this.initTotalList(this.columns)
this.loadData();
},
methods: {
refresh() {
this.loadData();
},
loadData(pagination, filters, sorter) {
this.localLoading = true
var result = this.data(
Object.assign({
pageNo: (pagination && pagination.current) ||
this.localPagination.current,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
},
(sorter && sorter.field && {
sortField: sorter.field
}) || {},
(sorter && sorter.order && {
sortOrder: sorter.order
}) || {}, {
...filters
}
)
);
if (result instanceof Promise) {
result.then(r => {
this.localPagination = Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
});
!r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
this.localDataSource = r.data; // 返回结果中的数组数据
this.localLoading = false
});
}
},
initTotalList(columns) {
const totalList = []
columns && columns instanceof Array && columns.forEach(column => {
if (column.needTotal) {
totalList.push({ ...column,
total: 0
})
}
})
return totalList
},
updateSelect(selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
let list = this.needTotalList
this.needTotalList = list.map(item => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
let total = sum + get(val, item.dataIndex)
return isNaN(total) ? 0 : total
}, 0)
}
})
// this.$emit('change', selectedRowKeys, selectedRows)
},
updateEdit() {
this.selectedRows = []
},
onClearSelected() {
this.selectedRowKeys = []
this.updateSelect([], [])
},
renderMsg(h) {
const _vm = this
let d = []
// 构建 已选择
d.push(
h('span', {
style: {
marginRight: '12px'
}
}, ['已选择 ', h('a', {
style: {
fontWeight: 600
}
}, this.selectedRows.length)])
);
// 构建 列统计
this.needTotalList.map(item => {
d.push(h('span', {
style: {
marginRight: '12px'
}
},
[
`${ item.title }总计 `,
h('a', {
style: {
fontWeight: 600
}
}, `${ !item.customRender ? item.total : item.customRender(item.total) }`)
]))
});
// 构建 清空选择
d.push(h('a', {
style: {
marginLeft: '24px'
},
on: {
click: _vm.onClearSelected
}
}, '清空'))
return d
},
renderAlert(h) {
return h('span', {
slot: 'message'
}, this.renderMsg(h))
},
},
render(h) {
const _vm = this
let props = {},
localKeys = Object.keys(this.$data);
Object.keys(T.props).forEach(k => {
let localKey = `local${k.substring(0,1).toUpperCase()}${k.substring(1)}`;
if (localKeys.includes(localKey)) {
return props[k] = _vm[localKey];
}
return props[k] = _vm[k];
})
// 显示信息提示
if (this.showAlertInfo) {
props.rowSelection = {
selectedRowKeys: this.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
_vm.updateSelect(selectedRowKeys, selectedRows)
_vm.$emit('onSelect', { selectedRowKeys: selectedRowKeys, selectedRows: selectedRows })
}
};
return h('div', {}, [
h("a-alert", {
style: {
marginBottom: '16px'
},
props: {
type: 'info',
showIcon: true
}
}, [_vm.renderAlert(h)]),
h("a-table", {
tag: "component",
attrs: props,
on: {
change: _vm.loadData
},
scopedSlots: this.$scopedSlots
}, this.$slots.default)
]);
}
return h("a-table", {
tag: "component",
attrs: props,
on: {
change: _vm.loadData
},
scopedSlots: this.$scopedSlots
}, this.$slots.default);
}
};

48
ant-design-jeecg-vue/src/components/tools/Breadcrumb.vue

@ -0,0 +1,48 @@
<template>
<a-breadcrumb class="breadcrumb">
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
<router-link v-if="item.name != name" :to="{ path: item.path }">
{{ item.meta.title }}
</router-link>
<span v-else>{{ item.meta.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script>
export default {
data() {
return {
name: '',
breadList: [],
}
},
created () {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
console.log('this.$route.matched', this.$route.matched)
this.breadList = []
this.breadList.push({ name: 'dashboard', path: '/dashboard/', meta: { title: '首页' } })
this.name = this.$route.name
this.$route.matched.forEach((item) => {
// item.meta.name === 'dashboard' ? item.path = '/dashboard' : this.$route.path === item.path
this.breadList.push(item)
})
}
},
watch: {
$route() {
this.getBreadcrumb()
}
}
}
</script>
<style scoped>
</style>

147
ant-design-jeecg-vue/src/components/tools/DetailList.vue

@ -0,0 +1,147 @@
<template>
<div :class="['detail-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
<div v-if="title" class="title">{{ title }}</div>
<a-row>
<slot></slot>
</a-row>
</div>
</template>
<script>
import { Col } from 'ant-design-vue/es/grid/'
const Item = {
name: 'DetailListItem',
props: {
term: {
type: String,
default: '',
required: false
},
},
inject: {
col: {
type: Number
}
},
render () {
return (
<Col {...{props: responsive[this.col]}}>
<div class="term">{this.$props.term}</div>
<div class="content">{this.$slots.default}</div>
</Col>
)
}
}
const responsive = {
1: { xs: 24 },
2: { xs: 24, sm: 12 },
3: { xs: 24, sm: 12, md: 8 },
4: { xs: 24, sm: 12, md: 6 }
}
export default {
name: "DetailList",
Item: Item,
components: {
Col
},
props: {
title: {
type: String,
default: '',
required: false
},
col: {
type: Number,
required: false,
default: 3
},
size: {
type: String,
required: false,
default: 'large'
},
layout: {
type: String,
required: false,
default: 'horizontal'
}
},
provide () {
return {
col: this.col > 4 ? 4 : this.col
}
}
}
</script>
<style lang="scss">
.detail-list {
.title {
color: rgba(0,0,0,.85);
font-size: 14px;
font-weight: 500;
margin-bottom: 16px;
}
.term {
color: rgba(0,0,0,.85);
display: table-cell;
line-height: 20px;
margin-right: 8px;
padding-bottom: 16px;
white-space: nowrap;
&:after {
content: ":";
margin: 0 8px 0 2px;
position: relative;
top: -.5px;
}
}
.content {
color: rgba(0,0,0,.65);
display: table-cell;
line-height: 22px;
padding-bottom: 16px;
width: 100%;
}
&.small {
.title {
font-size: 14px;
color: rgba(0, 0, 0, .65);
font-weight: normal;
margin-bottom: 12px;
}
.term, .content {
padding-bottom: 8px;
}
}
&.large {
.term, .content {
padding-bottom: 16px;
}
.title {
font-size: 16px;
}
}
&.vertical {
.term {
padding-bottom: 8px;
}
.term, .content {
display: block;
}
}
}
</style>

32
ant-design-jeecg-vue/src/components/tools/FooterToolBar.vue

@ -0,0 +1,32 @@
<template>
<div class="toolbar">
<div style="float: left">
<slot name="extra"></slot>
</div>
<div style="float: right">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "FooterToolBar"
}
</script>
<style lang="scss" scoped>
.toolbar {
position: fixed;
width: 100%;
bottom: 0;
right: 0;
height: 56px;
line-height: 56px;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
background: #fff;
border-top: 1px solid #e8e8e8;
padding: 0 24px;
z-index: 9;
}
</style>

67
ant-design-jeecg-vue/src/components/tools/HeadInfo.vue

@ -0,0 +1,67 @@
<template>
<div class="head-info" :class="center && 'center'">
<span>{{ title }}</span>
<p>{{ content }}</p>
<em v-if="bordered"/>
</div>
</template>
<script>
export default {
name: "HeadInfo",
props: {
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
bordered: {
type: Boolean,
default: false
},
center: {
type: Boolean,
default: true
}
}
}
</script>
<style lang="scss" scoped>
.head-info {
position: relative;
text-align: left;
padding: 0 32px 0 0;
min-width: 125px;
&.center {
text-align: center;
padding: 0 32px;
}
span {
color: rgba(0, 0, 0, .45);
display: inline-block;
font-size: 14px;
line-height: 22px;
margin-bottom: 4px;
}
p {
color: rgba(0, 0, 0, .85);
font-size: 24px;
line-height: 32px;
margin: 0;
}
em {
background-color: #e8e8e8;
position: absolute;
height: 56px;
width: 1px;
top: 0;
right: 0;
}
}
</style>

85
ant-design-jeecg-vue/src/components/tools/HeaderNotice.vue

@ -0,0 +1,85 @@
<template>
<a-popover
trigger="click"
placement="bottomRight"
:autoAdjustOverflow="true"
:arrowPointAtCenter="true"
overlayClassName="header-notice-wrapper"
:overlayStyle="{ width: '300px', top: '50px' }">
<template slot="content">
<a-spin :spinning="loadding">
<a-tabs>
<a-tab-pane tab="通知" key="1">
<a-list>
<a-list-item>
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane tab="消息" key="2">
123
</a-tab-pane>
<a-tab-pane tab="待办" key="3">
123
</a-tab-pane>
</a-tabs>
</a-spin>
</template>
<span @click="fetchNotice" class="header-notice">
<a-badge count="12">
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
</a-badge>
</span>
</a-popover>
</template>
<script>
export default {
name: "HeaderNotice",
data () {
return {
loadding: false
}
},
methods: {
fetchNotice () {
if (this.loadding) {
this.loadding = false
return
}
this.loadding = true
setTimeout(() => {
this.loadding = false
}, 2000)
}
}
}
</script>
<style lang="css">
.header-notice-wrapper {
top: 50px !important;
}
</style>
<style lang="scss" scoped>
.header-notice{
display: inline-block;
transition: all 0.3s;
span {
vertical-align: initial;
}
}
</style>

33
ant-design-jeecg-vue/src/components/tools/Logo.vue

@ -0,0 +1,33 @@
<template>
<div class="logo">
<router-link :to="{name:'dashboard'}">
<img src="~@/assets/logo.svg" alt="logo">
<h1 v-if="showTitle">{{ title }}</h1>
</router-link>
</div>
</template>
<script>
export default {
name: "Logo",
props: {
title: {
type: String,
default: 'Jeecg-Boot Pro',
required: false
},
showTitle: {
type: Boolean,
default: true,
required: false
}
}
}
</script>
<style lang="scss" scoped>
/*缩小首页布 局顶部的高度*/
.sider .logo {
height: 59px!important;
line-height: 59px!important;
}
</style>

89
ant-design-jeecg-vue/src/components/tools/TwoStepCaptcha.vue

@ -0,0 +1,89 @@
<template>
<!-- 两步验证 -->
<a-modal
centered
v-model="visible"
@cancel="handleCancel"
:maskClosable="false"
>
<div slot="title" :style="{ textAlign: 'center' }">两步验证</div>
<template slot="footer">
<div :style="{ textAlign: 'center' }">
<a-button key="back" @click="handleCancel">返回</a-button>
<a-button key="submit" type="primary" :loading="stepLoading" @click="handleStepOk">
继续
</a-button>
</div>
</template>
<a-spin :spinning="stepLoading">
<a-form layout="vertical" :auto-form-create="(form)=>{this.form = form}">
<div class="step-form-wrapper">
<p style="text-align: center" v-if="!stepLoading">请在手机中打开 Google Authenticator 或两步验证 APP<br />输入 6 位动态码</p>
<p style="text-align: center" v-else>正在验证..<br/>请稍后</p>
<a-form-item
:style="{ textAlign: 'center' }"
hasFeedback
fieldDecoratorId="stepCode"
:fieldDecoratorOptions="{rules: [{ required: true, message: '请输入 6 位动态码!', pattern: /^\d{6}$/, len: 6 }]}"
>
<a-input :style="{ textAlign: 'center' }" @keyup.enter.native="handleStepOk" placeholder="000000" />
</a-form-item>
<p style="text-align: center">
<a @click="onForgeStepCode">遗失手机?</a>
</p>
</div>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
return {
stepLoading: false,
form: null
};
},
methods: {
handleStepOk() {
const vm = this
this.stepLoading = true
this.form.validateFields((err, values) => {
if (!err) {
console.log('values', values)
setTimeout( () => {
vm.stepLoading = false
vm.$emit('success', { values })
}, 2000)
return;
}
this.stepLoading = false
this.$emit('error', { err })
})
},
handleCancel () {
this.visible = false
this.$emit('cancel')
},
onForgeStepCode() {
}
}
};
</script>
<style lang="scss" scoped>
.step-form-wrapper {
margin: 0 auto;
width: 80%;
max-width: 400px;
}
</style>

93
ant-design-jeecg-vue/src/components/tools/UserMenu.vue

@ -0,0 +1,93 @@
<template>
<div class="user-wrapper">
<span class="action">
<a-icon type="question-circle-o"></a-icon>
</span>
<header-notice class="action"/>
<a-dropdown>
<span class="action ant-dropdown-link user-dropdown-menu">
<a-avatar class="avatar" size="small" :src="getAvatar()"/>
<span>欢迎您{{ nickname() }}</span>
</span>
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
<a-menu-item key="0">
<router-link :to="{ name: 'account-center' }">
<a-icon type="user"/>
<span>个人中心</span>
</router-link>
</a-menu-item>
<a-menu-item key="1">
<router-link :to="{ name: 'account-settings' }">
<a-icon type="setting"/>
<span>账户设置</span>
</router-link>
</a-menu-item>
<!-- <a-menu-item key="2" disabled>
<a-icon type="setting"/>
<span>测试</span>
</a-menu-item>
<a-menu-divider/>
<a-menu-item key="3">
<a href="javascript:;" @click="handleLogout">
<a-icon type="logout"/>
<span>退出登录</span>
</a>
</a-menu-item>-->
</a-menu>
</a-dropdown>
<span class="action">
<a class="logout_title" href="javascript:;" @click="handleLogout">
<a-icon type="logout"/>
<span> 退出登录</span>
</a>
</span>
</div>
</template>
<script>
import HeaderNotice from './HeaderNotice'
import { mapActions, mapGetters } from 'vuex'
import {imgView} from '@/api/api'
export default {
name: "UserMenu",
components: {
HeaderNotice
},
methods: {
...mapActions(["Logout"]),
...mapGetters(["nickname", "avatar"]),
getAvatar(){
console.log('url = '+ imgView+this.avatar())
return imgView+this.avatar()
},
handleLogout() {
const that = this
this.$confirm({
title: '提示',
content: '真的要注销登录吗 ?',
onOk() {
return that.Logout({}).then(() => {
window.location.reload()
}).catch(err => {
that.$message.error({
title: '错误',
description: err.message
})
})
},
onCancel() {
},
});
},
}
}
</script>
<style scoped>
.logout_title{
color: rgba(0, 0, 0, 0.65);
text-decoration:none;
}
</style>

95
ant-design-jeecg-vue/src/components/tools/setting.js

@ -0,0 +1,95 @@
import { message } from 'ant-design-vue/es';
// import defaultSettings from '../defaultSettings';
let lessNodesAppended;
const colorList = [
{
key: '薄暮', color: '#F5222D',
},
{
key: '火山', color: '#FA541C',
},
{
key: '日暮', color: '#FAAD14',
},
{
key: '明青', color: '#13C2C2',
},
{
key: '极光绿', color: '#52C41A',
},
{
key: '拂晓蓝(默认)', color: '#1890FF',
},
{
key: '极客蓝', color: '#2F54EB',
},
{
key: '酱紫', color: '#722ED1',
},
];
const updateTheme = primaryColor => {
// Don't compile less in production!
/* if (process.env.NODE_ENV === 'production') {
return;
} */
// Determine if the component is remounted
if (!primaryColor) {
return;
}
const hideMessage = message.loading('正在编译主题!', 0);
function buildIt() {
if (!window.less) {
return;
}
setTimeout(() => {
window.less
.modifyVars({
'@primary-color': primaryColor,
})
.then(() => {
hideMessage();
})
.catch(() => {
message.error('Failed to update theme');
hideMessage();
});
}, 200);
}
if (!lessNodesAppended) {
// insert less.js and color.less
const lessStyleNode = document.createElement('link');
const lessConfigNode = document.createElement('script');
const lessScriptNode = document.createElement('script');
lessStyleNode.setAttribute('rel', 'stylesheet/less');
lessStyleNode.setAttribute('href', '/color.less');
lessConfigNode.innerHTML = `
window.less = {
async: true,
env: 'production',
javascriptEnabled: true
};
`;
lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';
lessScriptNode.async = true;
lessScriptNode.onload = () => {
buildIt();
lessScriptNode.onload = null;
};
document.body.appendChild(lessStyleNode);
document.body.appendChild(lessConfigNode);
document.body.appendChild(lessScriptNode);
lessNodesAppended = true;
} else {
buildIt();
}
};
const updateColorWeak = colorWeak => {
// document.body.className = colorWeak ? 'colorWeak' : '';
colorWeak ? document.body.classList.add('colorWeak') : document.body.classList.remove('colorWeak')
};
export { updateTheme, colorList, updateColorWeak }

394
ant-design-jeecg-vue/src/config/router.config.js

@ -0,0 +1,394 @@
import { UserLayout, TabLayout, RouteView, BlankLayout, PageView } from '@/components/layouts'
/**
* 走菜单走权限控制
* @type {[null,null]}
*/
export const asyncRouterMap = [
{
path: '/',
name: 'index',
component: TabLayout,
meta: { title: '首页' },
redirect: '/dashboard/workplace',
children: [
//系统管理
{
path: '/system',
name: 'system',
redirect: '/isystem/user',
component: RouteView,
meta: { title: '系统管理', icon: 'dashboard', permission: [ 'dashboard' ] },
children: [
{
path: '/system/user',
name: 'user',
component: () => import('@/views/system/UserList'),
meta: { title: '用户管理', permission: [ 'dashboard' ] }
},
{
path: '/system/role',
name: 'role',
component: () => import('@/views/system/RoleList'),
meta: { title: '角色管理', permission: [ 'dashboard' ] }
},
{
path: '/system/log',
name: 'log',
component: () => import('@/views/system/LogList'),
meta: { title: '日志管理', permission: [ 'dashboard' ] }
},
]
},
// dashboard
{
path: '/dashboard',
name: 'dashboard',
redirect: '/dashboard/workplace',
component: RouteView,
meta: { title: '仪表盘', icon: 'dashboard', permission: [ 'dashboard' ] },
children: [
{
path: '/dashboard/analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: { title: '分析页', permission: [ 'dashboard' ] }
},
{
path: '/dashboard/monitor',
name: 'Monitor',
hidden: true,
component: () => import('@/views/dashboard/Monitor'),
meta: { title: '监控页', permission: [ 'dashboard' ] }
},
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: { title: '工作台', permission: [ 'dashboard' ] }
}
]
},
//jeecg
{
path: '/jeecg',
name: 'jeecg',
redirect: '/jeecg',
component: RouteView,
meta: { title: 'JEECG案例', icon: 'dashboard', permission: [ 'dashboard' ] },
children: [
{
path: '/jeecg/JeecgDemoList',
name: 'DemoList',
component: () => import('@/views/jeecg/JeecgDemoList'),
meta: { title: '列表例子', permission: [ 'dashboard' ] }
},
{
path: '/jeecg/helloworld',
name: 'helloworld',
hidden : true,
component: () => import('@/views/jeecg/helloworld'),
meta: { title: 'helloworld', permission: [ 'dashboard' ] }
}
]
},
// forms
{
path: '/form',
redirect: '/form/basic-form',
component: PageView,
meta: { title: '表单页', icon: 'form', permission: [ 'form' ] },
children: [
{
path: '/form/base-form',
name: 'BaseForm',
component: () => import('@/views/form/BasicForm'),
meta: { title: '基础表单', permission: [ 'form' ] }
},
{
path: '/form/step-form',
name: 'StepForm',
component: () => import('@/views/form/stepForm/StepForm'),
meta: { title: '分步表单', permission: [ 'form' ] }
},
{
path: '/form/advanced-form',
name: 'AdvanceForm',
component: () => import('@/views/form/advancedForm/AdvancedForm'),
meta: { title: '高级表单', permission: [ 'form' ] }
}
]
},
// list
{
path: '/list',
name: 'list',
component: PageView,
redirect: '/list/query-list',
meta: { title: '列表页', icon: 'table', permission: [ 'table' ] },
children: [
{
path: '/list/query-list',
name: 'QueryList',
component: () => import('@/views/list/TableList'),
meta: { title: '查询表格', permission: [ 'table' ] }
},
{
path: '/list/edit-table',
name: 'EditList',
component: () => import('@/views/list/TableInnerEditList'),
meta: { title: '内联编辑表格', permission: [ 'table' ] }
},
{
path: '/list/user-list',
name: 'UserList',
component: () => import('@/views/list/UserList'),
meta: { title: '用户列表', permission: [ 'table' ] }
},
{
path: '/list/role-list',
name: 'RoleList',
component: () => import('@/views/list/RoleList'),
meta: { title: '角色列表', permission: [ 'table' ] }
},
{
path: '/list/permission-list',
name: 'PermissionList',
component: () => import('@/views/list/PermissionList'),
meta: { title: '权限列表', permission: [ 'table' ] }
},
{
path: '/list/basic-list',
name: 'BasicList',
component: () => import('@/views/list/StandardList'),
meta: { title: '标准列表', permission: [ 'table' ] }
},
{
path: '/list/card',
name: 'CardList',
component: () => import('@/views/list/CardList'),
meta: { title: '卡片列表', permission: [ 'table' ] }
},
{
path: '/list/search',
name: 'SearchList',
component: () => import('@/views/list/search/SearchLayout'),
redirect: '/list/search/article',
meta: { title: '搜索列表', permission: [ 'table' ] },
children: [
{
path: '/list/search/article',
name: 'SearchArticles',
component: () => import('../views/list/TableList'),
meta: { title: '搜索列表(文章)', permission: [ 'table' ] }
},
{
path: '/list/search/project',
name: 'SearchProjects',
component: () => import('../views/list/TableList'),
meta: { title: '搜索列表(项目)', permission: [ 'table' ] }
},
{
path: '/list/search/application',
name: 'SearchApplications',
component: () => import('../views/list/TableList'),
meta: { title: '搜索列表(应用)', permission: [ 'table' ] }
},
]
},
]
},
// profile
{
path: '/profile',
name: 'profile',
component: RouteView,
redirect: '/profile/basic',
meta: { title: '详情页', icon: 'profile', permission: [ 'profile' ] },
children: [
{
path: '/profile/basic',
name: 'ProfileBasic',
component: () => import('@/views/profile/basic/Index'),
meta: { title: '基础详情页', permission: [ 'profile' ] }
},
{
path: '/profile/advanced',
name: 'ProfileAdvanced',
component: () => import('@/views/profile/advanced/Advanced'),
meta: { title: '高级详情页', permission: [ 'profile' ] }
}
]
},
// result
{
path: '/result',
name: 'result',
component: PageView,
redirect: '/result/success',
meta: { title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },
children: [
{
path: '/result/success',
name: 'ResultSuccess',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
meta: { title: '成功', hiddenHeaderContent: true, permission: [ 'result' ] }
},
{
path: '/result/fail',
name: 'ResultFail',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
meta: { title: '失败', hiddenHeaderContent: true, permission: [ 'result' ] }
}
]
},
// Exception
{
path: '/exception',
name: 'exception',
component: RouteView,
redirect: '/exception/403',
meta: { title: '异常页', icon: 'warning', permission: [ 'exception' ] },
children: [
{
path: '/exception/403',
name: 'Exception403',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
meta: { title: '403', permission: [ 'exception' ] }
},
{
path: '/exception/404',
name: 'Exception404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
meta: { title: '404', permission: [ 'exception' ] }
},
{
path: '/exception/500',
name: 'Exception500',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
meta: { title: '500', permission: [ 'exception' ] }
}
]
},
// account
{
path: '/account',
component: RouteView,
name: 'account',
meta: { title: '个人页', icon: 'user', keepAlive: true, permission: [ 'user' ] },
children: [
{
path: '/account/center',
name: 'center',
component: () => import('@/views/account/center/Index'),
meta: { title: '个人中心', keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings',
name: 'settings',
component: () => import('@/views/account/settings/Index'),
meta: { title: '个人设置', hideHeader: true, keepAlive: true, permission: [ 'user' ] },
redirect: '/account/settings/base',
alwaysShow: true,
children: [
{
path: '/account/settings/base',
name: 'BaseSettings',
component: () => import('@/views/account/settings/BaseSetting'),
meta: { title: '基本设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/security',
name: 'SecuritySettings',
component: () => import('@/views/account/settings/Security'),
meta: { title: '安全设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/custom',
name: 'CustomSettings',
component: () => import('@/views/account/settings/Custom'),
meta: { title: '个性化设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/binding',
name: 'BindingSettings',
component: () => import('@/views/account/settings/Binding'),
meta: { title: '账户绑定', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
{
path: '/account/settings/notification',
name: 'NotificationSettings',
component: () => import('@/views/account/settings/Notification'),
meta: { title: '新消息通知', hidden: true, keepAlive: true, permission: [ 'user' ] }
},
]
},
]
}
]
},
{
path: '*', redirect: '/404', hidden: true
}
]
/**
* 基础路由
* @type { *[] }
*/
export const constantRouterMap = [
{
path: '/user',
component: UserLayout,
redirect: '/user/login',
hidden: true,
children: [
{
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login')
},
{
path: 'register',
name: 'register',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Register')
},
{
path: 'register-result',
name: 'registerResult',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/RegisterResult')
}
]
},
{
path: '/test',
component: BlankLayout,
redirect: '/test/home',
children: [
{
path: 'home',
name: 'TestHome',
component: () => import('@/views/Home')
}
]
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404')
},
]

31
ant-design-jeecg-vue/src/defaultSettings.js

@ -0,0 +1,31 @@
/**
* 项目默认配置项
* primaryColor - 默认主题色
* navTheme - sidebar theme ['dark', 'light'] 两种主题
* colorWeak - 色盲模式
* layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局
* fixedHeader - 固定 Header : boolean
* fixSiderbar - 固定左侧菜单栏 boolean
* autoHideHeader - 向下滚动时隐藏 Header : boolean
* contentWidth - 内容区布局 流式 | 固定
*
* storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage)
*
*/
export default {
primaryColor: '#1890FF', // primary color of ant design
navTheme: 'light', // theme for nav menu
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu
fixedHeader: false, // sticky header
fixSiderbar: false, // sticky siderbar
autoHideHeader: false, // auto hide header
colorWeak: false,
// vue-ls options
storageOptions: {
namespace: 'pro__', // key prefix
name: 'ls', // name variable Vue.[ls] or this.[$ls],
storage: 'local', // storage name session, local, memory
}
}

61
ant-design-jeecg-vue/src/main.js

@ -0,0 +1,61 @@
import Vue from 'vue'
import App from './App.vue'
import Storage from 'vue-ls'
import router from './router'
import store from './store/'
import { VueAxios } from "@/utils/request"
import Antd from 'ant-design-vue'
import Viser from 'viser-vue'
import 'ant-design-vue/dist/antd.less'; // or 'ant-design-vue/dist/antd.less'
import '@/permission' // permission control
import '@/utils/filter' // base filter
import DictSelectTag from './components/dict/index.js'
import Print from 'vue-print-nb'
/*import '@babel/polyfill'*/
import {
ACCESS_TOKEN,
DEFAULT_COLOR,
DEFAULT_THEME,
DEFAULT_LAYOUT_MODE,
DEFAULT_COLOR_WEAK,
SIDEBAR_TYPE,
DEFAULT_FIXED_HEADER,
DEFAULT_FIXED_HEADER_HIDDEN,
DEFAULT_FIXED_SIDEMENU,
DEFAULT_CONTENT_WIDTH_TYPE
} from "@/store/mutation-types"
import config from '@/defaultSettings'
import hasPermission from '@/utils/hasPermission'
Vue.config.productionTip = false
Vue.use(Storage, config.storageOptions)
Vue.use(Antd)
Vue.use(VueAxios, router)
Vue.use(Viser)
Vue.use(hasPermission)
Vue.use(DictSelectTag)
Vue.use(Print)
new Vue({
router,
store,
mounted () {
store.commit('SET_SIDEBAR_TYPE', Vue.ls.get(SIDEBAR_TYPE, true))
store.commit('TOGGLE_THEME', Vue.ls.get(DEFAULT_THEME, config.navTheme))
store.commit('TOGGLE_LAYOUT_MODE', Vue.ls.get(DEFAULT_LAYOUT_MODE, config.layout))
store.commit('TOGGLE_FIXED_HEADER', Vue.ls.get(DEFAULT_FIXED_HEADER, config.fixedHeader))
store.commit('TOGGLE_FIXED_SIDERBAR', Vue.ls.get(DEFAULT_FIXED_SIDEMENU, config.fixSiderbar))
store.commit('TOGGLE_CONTENT_WIDTH', Vue.ls.get(DEFAULT_CONTENT_WIDTH_TYPE, config.contentWidth))
store.commit('TOGGLE_FIXED_HEADER_HIDDEN', Vue.ls.get(DEFAULT_FIXED_HEADER_HIDDEN, config.autoHideHeader))
store.commit('TOGGLE_WEAK', Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak))
store.commit('TOGGLE_COLOR', Vue.ls.get(DEFAULT_COLOR, config.primaryColor))
store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN))
},
render: h => h(App)
}).$mount('#app')

75
ant-design-jeecg-vue/src/permission.js

@ -0,0 +1,75 @@
import Vue from 'vue'
import router from './router'
import store from './store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import notification from 'ant-design-vue/es/notification'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { generateIndexRouter } from "@/utils/util"
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/user/login', '/user/register', '/user/register-result'] // no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (Vue.ls.get(ACCESS_TOKEN)) {
/* has token */
if (to.path === '/user/login') {
next({ path: '/dashboard/workplace' })
NProgress.done()
} else {
if (store.getters.permissionList.length === 0) {
store.dispatch('GetPermissionList').then(res => {
const menuData = res.result;
console.log(res.message)
if (menuData === null || menuData === "" || menuData === undefined) {
return;
}
let constRoutes = [];
constRoutes = generateIndexRouter(menuData);
// 添加主界面路由
store.dispatch('UpdateAppRouter', { constRoutes }).then(() => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
router.addRoutes(store.getters.addRouters)
const redirect = decodeURIComponent(from.query.redirect || to.path)
if (to.path === redirect) {
// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
next({ ...to, replace: true })
} else {
// 跳转到目的路由
next({ path: redirect })
}
})
})
.catch(() => {
notification.error({
message: '系统提示',
description: '请求用户信息失败,请重试!'
})
// update-begin- --- author:scott ------ date:20190225 ---- for:Token失效跳转登录逻辑修改----
// store.dispatch('Logout').then(() => {
// next({ path: '/user/login', query: { redirect: to.fullPath } })
// })
// update-end- --- author:scott ------ date:20190225 ---- for:Token失效跳转登录逻辑修改----
})
} else {
next()
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next({ path: '/user/login', query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})

139
ant-design-jeecg-vue/src/router/README.md

@ -0,0 +1,139 @@
路由/菜单说明
====
配置文件路径
----
`@/config/router.config.js`
格式和说明
----
```javascript
/**
* 路由配置说明:
* 建议:sider menu 请不要超过三级菜单,若超过三级菜单,则应该设计为顶部主菜单 配合左侧次级菜单
*
**/
{
redirect: noredirect,
name: 'router-name',
hidden: true,
meta: {
title: 'title',
icon: 'a-icon',
keepAlive: true,
hiddenHeaderContent: true,
}
}
```
`{ Route }` 对象
| 参数 | 说明 | 类型 | 默认值 |
| -------- | ----------------------------------------- | ------- | ------ |
| hidden | 控制路由是否显示在 sidebar | boolean | falase |
| redirect | 重定向地址, 访问这个路由时,自定进行重定向 | string | - |
| name | 路由名称, 建议设置,且不能重名 | string | - |
| meta | 路由元信息(路由附带扩展信息) | object | {} |
`{ Meta }` 路由元信息对象
| 参数 | 说明 | 类型 | 默认值 |
| ------------------- | ------------------------------------------------------------ | ------- | ------ |
| title | 路由标题, 用于显示面包屑, 页面标题 *推荐设置 | string | - |
| icon | 路由在 menu 上显示的图标 | string | - |
| keepAlive | 缓存该路由 | boolean | false |
| hiddenHeaderContent | *特殊 隐藏 [PageHeader](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/layout/PageHeader.vue#L14) 组件中的页面带的 面包屑和页面标题栏 | boolean | false |
| permission | 与项目提供的权限拦截匹配的权限,如果不匹配,则会被禁止访问该路由页面 | array | [] |
路由例子
----
```ecmascript 6
const asyncRouterMap = [
{
path: '/',
name: 'index',
component: BasicLayout,
meta: { title: '首页' },
redirect: '/dashboard/analysis',
children: [
{
path: '/dashboard',
component: Layout,
name: 'dashboard',
redirect: '/dashboard/workplace',
meta: {title: '仪表盘', icon: 'dashboard', permission: ['dashboard']},
children: [
{
path: '/dashboard/analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: {title: '分析页', permission: ['dashboard']}
},
{
path: '/dashboard/monitor',
name: 'Monitor',
hidden: true,
component: () => import('@/views/dashboard/Monitor'),
meta: {title: '监控页', permission: ['dashboard']}
},
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: {title: '工作台', permission: ['dashboard']}
}
]
},
// result
{
path: '/result',
name: 'result',
component: PageView,
redirect: '/result/success',
meta: { title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },
children: [
{
path: '/result/success',
name: 'ResultSuccess',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
// 该页面隐藏面包屑和页面标题栏
meta: { title: '成功', hiddenHeaderContent: true, permission: [ 'result' ] }
},
{
path: '/result/fail',
name: 'ResultFail',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
// 该页面隐藏面包屑和页面标题栏
meta: { title: '失败', hiddenHeaderContent: true, permission: [ 'result' ] }
}
]
},
...
]
},
]
```
> 1. 请注意 `component: () => import('..') ` 方式引入路由的页面组件为 懒加载模式。具体可以看 [Vue 官方文档](https://router.vuejs.org/zh/guide/advanced/lazy-loading.html)
> 2. 增加新的路由应该增加在 '/' (index) 路由的 `children`
> 3. `permission` 可以进行自定义修改,只需要对这个模块进行自定义修改即可 [src/store/modules/permission.js#L10](https://github.com/sendya/ant-design-pro-vue/blob/master/src/store/modules/permission.js#L10)
附权限路由结构:
![权限结构](https://static-2.loacg.com/open/static/github/permissions.png)

12
ant-design-jeecg-vue/src/router/index.js

@ -0,0 +1,12 @@
import Vue from 'vue'
import Router from 'vue-router'
import { constantRouterMap } from '@/config/router.config'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})

17
ant-design-jeecg-vue/src/store/getters.js

@ -0,0 +1,17 @@
import Vue from 'vue'
import { USER_INFO} from "@/store/mutation-types"
const getters = {
device: state => state.app.device,
theme: state => state.app.theme,
color: state => state.app.color,
token: state => state.user.token,
avatar: state => {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
username: state => state.user.username,
nickname: state => {state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname},
welcome: state => state.user.welcome,
permissionList: state => state.user.permissionList,
userInfo: state => {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
addRouters: state => state.permission.addRouters
}
export default getters

27
ant-design-jeecg-vue/src/store/index.js

@ -0,0 +1,27 @@
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import user from './modules/user'
import permission from './modules/permission'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app,
user,
permission
},
state: {
},
mutations: {
},
actions: {
},
getters
})

121
ant-design-jeecg-vue/src/store/modules/app.js

@ -0,0 +1,121 @@
import Vue from 'vue'
import {
SIDEBAR_TYPE,
DEFAULT_THEME,
DEFAULT_LAYOUT_MODE,
DEFAULT_COLOR,
DEFAULT_COLOR_WEAK,
DEFAULT_FIXED_HEADER,
DEFAULT_FIXED_SIDEMENU,
DEFAULT_FIXED_HEADER_HIDDEN,
DEFAULT_CONTENT_WIDTH_TYPE
} from "@/store/mutation-types"
const app = {
state: {
sidebar: {
opened: true,
withoutAnimation: false
},
device: 'desktop',
theme: '',
layout: '',
contentWidth: '',
fixedHeader: false,
fixSiderbar: false,
autoHideHeader: false,
color: null,
weak: false,
multipage: true
},
mutations: {
SET_SIDEBAR_TYPE: (state, type) => {
state.sidebar.opened = type
Vue.ls.set(SIDEBAR_TYPE, type)
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Vue.ls.set(SIDEBAR_TYPE, true)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
TOGGLE_THEME: (state, theme) => {
// setStore('_DEFAULT_THEME', theme)
Vue.ls.set(DEFAULT_THEME, theme)
state.theme = theme
},
TOGGLE_LAYOUT_MODE: (state, layout) => {
Vue.ls.set(DEFAULT_LAYOUT_MODE, layout)
state.layout = layout
},
TOGGLE_FIXED_HEADER: (state, fixed) => {
Vue.ls.set(DEFAULT_FIXED_HEADER, fixed)
state.fixedHeader = fixed
},
TOGGLE_FIXED_SIDERBAR: (state, fixed) => {
Vue.ls.set(DEFAULT_FIXED_SIDEMENU, fixed)
state.fixSiderbar = fixed
},
TOGGLE_FIXED_HEADER_HIDDEN: (state, show) => {
Vue.ls.set(DEFAULT_FIXED_HEADER_HIDDEN, show)
state.autoHideHeader = show
},
TOGGLE_CONTENT_WIDTH: (state, type) => {
Vue.ls.set(DEFAULT_CONTENT_WIDTH_TYPE, type)
state.contentWidth = type
},
TOGGLE_COLOR: (state, color) => {
Vue.ls.set(DEFAULT_COLOR, color)
state.color = color
},
TOGGLE_WEAK: (state, flag) => {
Vue.ls.set(DEFAULT_COLOR_WEAK, flag)
state.weak = flag
},
setMultipage (state, multipage) {
state.multipage = multipage
}
},
actions: {
setSidebar: ({ commit }, type) => {
commit('SET_SIDEBAR_TYPE', type)
},
CloseSidebar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
ToggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
ToggleTheme({ commit }, theme) {
commit('TOGGLE_THEME', theme)
},
ToggleLayoutMode({ commit }, mode) {
commit('TOGGLE_LAYOUT_MODE', mode)
},
ToggleFixedHeader({ commit }, fixedHeader) {
if (!fixedHeader) {
commit('TOGGLE_FIXED_HEADER_HIDDEN', false)
}
commit('TOGGLE_FIXED_HEADER', fixedHeader)
},
ToggleFixSiderbar({ commit }, fixSiderbar) {
commit( 'TOGGLE_FIXED_SIDERBAR', fixSiderbar)
},
ToggleFixedHeaderHidden({ commit }, show) {
commit('TOGGLE_FIXED_HEADER_HIDDEN', show)
},
ToggleContentWidth({ commit }, type) {
commit('TOGGLE_CONTENT_WIDTH', type)
},
ToggleColor({ commit }, color) {
commit('TOGGLE_COLOR', color)
},
ToggleWeak({ commit }, weakFlag) {
commit('TOGGLE_WEAK', weakFlag)
}
}
}
export default app

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save