├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── quasar.conf.js ├── screenshot ├── 1.jpg ├── 10.jpg ├── 11.jpg ├── 12.jpg ├── 13.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg └── flowchart.png ├── src ├── App.vue ├── assets │ ├── 403.gif │ ├── quasar-logo-full.svg │ └── sad.svg ├── components │ ├── .gitkeep │ ├── DemoBlock.vue │ ├── FtyBreadcrumbs.vue │ ├── FtyMenu.vue │ ├── FtyModuleTabs.vue │ └── ScrollPane.vue ├── css │ ├── app.styl │ └── themes │ │ ├── common.variables.styl │ │ ├── variables.ios.styl │ │ └── variables.mat.styl ├── default-access-menu.js ├── i18n │ ├── en-us │ │ └── index.js │ ├── index.js │ ├── pt-br │ │ └── index.js │ └── zh-hans │ │ └── index.js ├── index.template.html ├── layouts │ ├── common.vue │ ├── components │ │ └── TagsView.vue │ └── default.vue ├── libs │ ├── auth.js │ ├── loading.js │ ├── permission.js │ ├── request.js │ ├── three │ │ └── three.js │ └── util.js ├── pages │ ├── 403.vue │ ├── cms │ │ ├── comment.vue │ │ ├── post.vue │ │ └── post_edit.vue │ ├── develop │ │ ├── business │ │ │ └── sku.vue │ │ └── official │ │ │ ├── button-group.md │ │ │ ├── button.md │ │ │ ├── dropdown-button.md │ │ │ └── toolbar.md │ ├── empty.vue │ ├── index.vue │ ├── login.vue │ ├── organization │ │ ├── department.vue │ │ └── position.vue │ ├── other │ │ └── requestlog.vue │ ├── permission │ │ ├── function.vue │ │ ├── role.vue │ │ ├── rolepermission.vue │ │ ├── roleuser.vue │ │ └── userrole.vue │ ├── system │ │ └── menu.vue │ └── user │ │ ├── user.vue │ │ └── userinfo.vue ├── plugins │ ├── .gitkeep │ ├── demo-block.js │ ├── i18n.js │ ├── permission.js │ └── vuelidate.js ├── router │ ├── index.js │ └── routes.js ├── service │ ├── cms │ │ └── post.js │ ├── login.js │ ├── other │ │ ├── requestlog.js │ │ └── resetdb.js │ ├── permission │ │ ├── function.js │ │ └── role.js │ ├── system │ │ └── menu.js │ └── user │ │ └── user.js ├── statics │ ├── icons │ │ ├── apple-icon-152x152.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-192x192.png │ │ ├── icon-256x256.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ └── ms-icon-144x144.png │ └── quasar-logo.png └── store │ ├── getters.js │ ├── index.js │ └── modules │ ├── app.js │ └── user.js └── strip-tags.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env", {"modules": false} ], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .quasar 2 | .DS_Store 3 | .thumbs.db 4 | node_modules 5 | /dist 6 | /src-cordova/platforms 7 | /src-cordova/plugins 8 | /src-cordova/www 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 若邪 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | 代码太丑,不建议照搬,可以看一看 RBAC 的实现思路,这是通用的。 8 | 9 | ## vue-quasar-admin 10 |   [Quasar-Framework](https://quasar-framework.org/) 是一款基于vue.js开发的开源的前端框架, 它能帮助web开发者快速创建以下网站:响应式网站,渐进式应用,手机应用(通过Cordova),跨平台应用(通过Electron)。 11 |   Quasar允许开发者在只写一次代码的情况下发布到多个平台 website, PWA ,Mobile App 和 Electron App 在使用Quasar的时候, 你甚至不需要Hammerjs, Momentjs, 或者Bootstrap, Quasar框架内包含了已经这些东西,你可以很简单就使用到。 12 |   [vue-quasar-admin](http://jaycewu.coding.me/vue-quasar-admin)是基于Quasar-Framework搭建的一套包含通用权限控制的后台管理系统(目前只针对PC端)。 13 | 14 | [![](https://ci.appveyor.com/api/projects/status/github/wjkang/vue-quasar-admin?branch=master&svg=true)]() 15 | [![vue](https://img.shields.io/badge/vue-2.5.16-brightgreen.svg)](https://github.com/vuejs/vue) 16 | [![quasar framework](https://img.shields.io/badge/quasar-0.15.14-brightgreen.svg)](https://quasar-framework.org/) 17 | [![MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)]() 18 | 19 | [online demo ](http://jaycewu.coding.me/vue-quasar-admin) 20 | 21 | 登录账号: 22 | 23 | admin 123 24 | 25 | test 123456 26 | 27 | website_admin 123456 28 | 29 | 请不要随意修改账号名称,其他操作随意,可通过右上角"数据初始化"按钮初始化数据 30 | 31 | ## 系统流程图 32 | 33 | ![](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/flowchart.png) 34 | 35 | ## 功能与特点 36 | 37 | - 真实后端数据支持 38 | - 登录/登出 39 | - 灵活的九宫格布局 40 | - 收缩左侧菜单栏 41 | - tag标签导航 42 | - 面包屑 43 | - 全屏/退出全屏 44 | - 动态菜单 45 | - 菜单按模块划分 46 | - 通用权限控制 47 | - 菜单级权限控制 48 | - 接口级权限控制 49 | - 元素级权限控制 50 | - 全局可配置loading效果 51 | - 网络异常处理 52 | - 模块 53 | - 系统模块 54 | - 系统设置 55 | - 菜单管理 56 | - 权限管理 57 | - 功能管理 58 | - 角色管理 59 | - 角色权限管理 60 | - 角色用户管理 61 | - 用户角色管理 62 | - 组织架构 63 | - 部门管理 64 | - 职位管理 65 | - 用户管理 66 | - 网站模块 67 | - CMS 68 | - 文章管理 69 | - 开发模块 70 | - 官方组件 71 | - 。。。 72 | - 业务组件 73 | - sku 74 | - 审计日志 75 | - 数据初始化 76 | 77 | ## 文件结构 78 | ```shell 79 | . 80 | ├── .quasar Quasar CLI生成的配置 81 | └── src 82 | ├── assets 资源文件 83 | ├── components 自定义组件 84 | ├── css 样式文件 85 | ├── layout 布局组件 86 | ├── libs 工具方法 87 | ├── router 路由配置 88 | ├── store 状态管理 89 | ├── service API管理 90 | ├── plugins 需要全局注册的组件、指令、插件 91 | └── pages 92 | ├── cms 93 | │ └── 文章管理 94 | ├── develop 95 | │ ├── 官方组件 96 | │ └── 业务组件 97 | ├── organization 98 | │ ├── 部门管理 99 | │ └── 职位管理 100 | ├── other 101 | │ └── 审计日志 102 | ├── permission 103 | │ ├── 功能管理 104 | │ ├── 角色管理 105 | │ ├── 角色权限管理 106 | │ ├── 角色用户管理 107 | │ └── 用户角色管理 108 | ├── system 109 | │ ├── 菜单管理 110 | ├── user 111 | │ └── 用户管理 112 | ├── 403 无权限页面 113 | ├── index 首页 114 | └── login 登录页 115 | 116 | ``` 117 | 118 | ## 安装使用 119 | 120 | ## Install 121 | ```bush 122 | npm install -g vue-cli 123 | ``` 124 | ```bush 125 | npm install -g quasar-cli 126 | ``` 127 | ```bush 128 | npm install 129 | ``` 130 | ## Run 131 | ### Development 132 | ```bush 133 | quasar dev 134 | ``` 135 | ### Production(Build) 136 | ```bush 137 | quasar build 138 | ``` 139 | 140 | ### 安装后台程序 141 | 142 | 143 | [后台程序](https://github.com/wjkang/quasar-admin-server) 144 | 145 | ```bush 146 | git clone https://github.com/wjkang/quasar-admin-server.git 147 | ``` 148 | 149 | ## Install 150 | ```bush 151 | npm install 152 | ``` 153 | ## Run 154 | ### Development 155 | ```bush 156 | npm run start 157 | ``` 158 | ### Production(Build) 159 | ```bush 160 | npm run production 161 | ``` 162 | 后端程序使用[koa2](https://github.com/koajs/koa)构建,并且使用[lowdb](https://github.com/typicode/lowdb)持久化数据到JSON文件(使用JSON文件存储是为了快速构建demo)。 163 | 164 | ## 功能开发步骤(以文章管理为例) 165 | - 前端 166 | - 定义功能码: 167 | - post_view -文章列表查看 168 | - post_edit -文章编辑 169 | - post_del -文章删除 170 | - 新建文章列表页 post.vue 171 | - 新增路由 172 | - 使用菜单管理功能新增"文章管理"的相关菜单,菜单名称必须与上一步新增的路由的name字段一致。权限码填定义好的"文章列表查看"功能对应的权限码(菜单级权限控制) 173 | - 使用功能管理在新建的菜单下录入定义好的功能名称及功能码 174 | - 配置角色与用户 175 | - 在角色权限管理为相应的角色设置功能权限 176 | - 后端 177 | - db.json文件新增文章存储结构 178 | - services下新增postService.js,编写对db.json文件的操作 179 | - controllers下新增post.js,引入postService.js做相关操作 180 | - main-routes.js 增加相关路由,使用PermissionCheck中间件进行后端接口级的权限控制(可使用功能码或角色码) 181 | - 前端 182 | - service下新增post.js,配置API相关操作,配置loading字段实现自定义loading效果,permission字段可配置功能编码与角色编码(实现前端接口级权限控制) 183 | - 回到post.vue文件,引入API访问文件,编写业务代码 184 | - 使用v-permission指令控制页面元素的是否显示,可使用功能编码与角色编码(实现元素级权限控制) 185 | - store app模块下配置dontCache,控制页面是否缓存 186 | 187 | 可多细节可查看源码 188 | 189 | ## 效果展示 190 | 191 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/1.jpg) 192 | 193 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/2.jpg) 194 | 195 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/3.jpg) 196 | 197 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/4.jpg) 198 | 199 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/5.jpg) 200 | 201 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/6.jpg) 202 | 203 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/7.jpg) 204 | 205 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/8.jpg) 206 | 207 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/9.jpg) 208 | 209 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/10.jpg) 210 | 211 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/11.jpg) 212 | 213 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/12.jpg) 214 | 215 | ![image](https://raw.githubusercontent.com/wjkang/vue-quasar-admin/master/screenshot/13.jpg) 216 | 217 | 218 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-quasar-admin", 3 | "version": "1.0.0", 4 | "description": "A Quasar Framework app", 5 | "productName": "vue-quasar-admin", 6 | "cordovaId": "org.cordova.quasar.app", 7 | "author": "wjk <977865769@qq.com>", 8 | "private": true, 9 | "scripts": { 10 | "test": "echo \"No test specified\" && exit 0" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.18.0", 14 | "mavon-editor": "^2.6.3", 15 | "vue-i18n": "^8.1.0", 16 | "vuelidate": "^0.6.2" 17 | }, 18 | "devDependencies": { 19 | "cheerio": "^1.0.0-rc.2", 20 | "markdown-it-container": "^2.0.0", 21 | "quasar-cli": "^0.15.14", 22 | "vue-markdown-loader": "^2.4.1" 23 | }, 24 | "engines": { 25 | "node": ">= 8.9.0", 26 | "npm": ">= 5.6.0" 27 | }, 28 | "browserslist": [ 29 | "> 1%", 30 | "last 2 versions", 31 | "not ie <= 10" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | // Configuration for your app 2 | const path = require('path') 3 | 4 | function resolve(dir) { 5 | return path.join(__dirname, '.', dir) 6 | } 7 | 8 | var md = require('markdown-it')(); 9 | var striptags = require('./strip-tags'); 10 | var wrap = function (render) { 11 | return function () { 12 | return render.apply(this, arguments) 13 | .replace('', ''); 15 | }; 16 | }; 17 | 18 | function convert(str) { 19 | str = str.replace(/(&#x)(\w{4});/gi, function ($0) { 20 | return String.fromCharCode(parseInt(encodeURIComponent($0).replace(/(%26%23x)(\w{4})(%3B)/g, '$2'), 16)); 21 | }); 22 | return str; 23 | } 24 | 25 | module.exports = function (ctx) { 26 | return { 27 | // app plugins (/src/plugins) 28 | plugins: [ 29 | 'i18n', 30 | 'vuelidate', 31 | 'permission', 32 | 'demo-block' 33 | ], 34 | css: [ 35 | 'app.styl' 36 | ], 37 | extras: [ 38 | ctx.theme.mat ? 'roboto-font' : null, 39 | 'material-icons', 40 | // 'ionicons', 41 | // 'mdi', 42 | 'fontawesome' 43 | ], 44 | supportIE: false, 45 | vendor: { 46 | add: [], 47 | remove: [] 48 | }, 49 | build: { 50 | env: ctx.dev 51 | ? { // so on dev we'll have 52 | API: JSON.stringify('http://localhost:3000') 53 | } 54 | : { // and on build (production): 55 | API: JSON.stringify('http://69.171.69.13:3000') 56 | }, 57 | scopeHoisting: true, 58 | vueRouterMode: 'hash', 59 | // gzip: true, 60 | // analyze: true, 61 | // extractCSS: false, 62 | // useNotifier: false, 63 | extendWebpack(cfg) { 64 | cfg.module.rules.push({ 65 | test: /\.md$/, 66 | loader: 'vue-markdown-loader', 67 | options: { 68 | preventExtract: true, 69 | use: [ 70 | [require('markdown-it-container'), 'demo', { 71 | validate: function (params) { 72 | return params.trim().match(/^demo\s*(.*)$/); 73 | }, 74 | 75 | render: function (tokens, idx) { 76 | var m = tokens[idx].info.trim().match(/^demo\s*(.*)$/); 77 | if (tokens[idx].nesting === 1) { 78 | var description = (m && m.length > 1) ? m[1] : ''; 79 | var content = tokens[idx + 1].content; 80 | var html = convert(striptags.strip(content, ['script', 'style'])).replace(/(<[^>]*)=""(?=.*>)/g, '$1'); 81 | var script = striptags.fetch(content, 'script'); 82 | var style = striptags.fetch(content, 'style'); 83 | var jsfiddle = { html: html, script: script, style: style }; 84 | jsfiddle = md.utils.escapeHtml(JSON.stringify(jsfiddle)); 85 | return ` 86 |
${html}
87 |
88 | `; 89 | } else { 90 | return '
\n'; 91 | } 92 | } 93 | }], 94 | [require('markdown-it-container'), 'tip'], 95 | [require('markdown-it-container'), 'warning'] 96 | ], 97 | preprocess: function (MarkdownIt, source) { 98 | MarkdownIt.renderer.rules.table_open = function () { 99 | return ''; 100 | }; 101 | MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence); 102 | return source; 103 | } 104 | } 105 | }), 106 | cfg.resolve.alias = { 107 | ...cfg.resolve.alias, // This adds the existing alias 108 | 109 | // Add you own alias like this 110 | '@': resolve('src') 111 | } 112 | } 113 | }, 114 | devServer: { 115 | // https: true, 116 | // port: 8080, 117 | open: true // opens browser window automatically 118 | }, 119 | framework: 'all', //--- includes everything; for dev only! 120 | // framework: { 121 | // components: [ 122 | // 'QLayout', 123 | // 'QLayoutHeader', 124 | // 'QLayoutDrawer', 125 | // 'QPageContainer', 126 | // 'QPage', 127 | // 'QToolbar', 128 | // 'QToolbarTitle', 129 | // 'QBtn', 130 | // 'QIcon', 131 | // 'QList', 132 | // 'QListHeader', 133 | // 'QItem', 134 | // 'QItemMain', 135 | // 'QItemSide', 136 | // 'QCollapsible' 137 | // ], 138 | // directives: [ 139 | // 'Ripple' 140 | // ], 141 | // // Quasar plugins 142 | // plugins: [ 143 | // 'Notify' 144 | // ] 145 | // }, 146 | animations: 'all',// --- includes all animations 147 | // animations: [ 148 | // ], 149 | pwa: { 150 | cacheExt: 'js,html,css,ttf,eot,otf,woff,woff2,json,svg,gif,jpg,jpeg,png,wav,ogg,webm,flac,aac,mp4,mp3', 151 | manifest: { 152 | // name: 'Quasar App', 153 | // short_name: 'Quasar-PWA', 154 | // description: 'Best PWA App in town!', 155 | display: 'standalone', 156 | orientation: 'portrait', 157 | background_color: '#ffffff', 158 | theme_color: '#027be3', 159 | icons: [ 160 | { 161 | 'src': 'statics/icons/icon-128x128.png', 162 | 'sizes': '128x128', 163 | 'type': 'image/png' 164 | }, 165 | { 166 | 'src': 'statics/icons/icon-192x192.png', 167 | 'sizes': '192x192', 168 | 'type': 'image/png' 169 | }, 170 | { 171 | 'src': 'statics/icons/icon-256x256.png', 172 | 'sizes': '256x256', 173 | 'type': 'image/png' 174 | }, 175 | { 176 | 'src': 'statics/icons/icon-384x384.png', 177 | 'sizes': '384x384', 178 | 'type': 'image/png' 179 | }, 180 | { 181 | 'src': 'statics/icons/icon-512x512.png', 182 | 'sizes': '512x512', 183 | 'type': 'image/png' 184 | } 185 | ] 186 | } 187 | }, 188 | cordova: { 189 | // id: 'org.cordova.quasar.app' 190 | }, 191 | electron: { 192 | extendWebpack(cfg) { 193 | // do something with cfg 194 | }, 195 | packager: { 196 | // OS X / Mac App Store 197 | // appBundleId: '', 198 | // appCategoryType: '', 199 | // osxSign: '', 200 | // protocol: 'myapp://path', 201 | 202 | // Window only 203 | // win32metadata: { ... } 204 | } 205 | }, 206 | 207 | // leave this here for Quasar CLI 208 | starterKit: '1.0.2' 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /screenshot/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/1.jpg -------------------------------------------------------------------------------- /screenshot/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/10.jpg -------------------------------------------------------------------------------- /screenshot/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/11.jpg -------------------------------------------------------------------------------- /screenshot/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/12.jpg -------------------------------------------------------------------------------- /screenshot/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/13.jpg -------------------------------------------------------------------------------- /screenshot/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/2.jpg -------------------------------------------------------------------------------- /screenshot/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/3.jpg -------------------------------------------------------------------------------- /screenshot/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/4.jpg -------------------------------------------------------------------------------- /screenshot/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/5.jpg -------------------------------------------------------------------------------- /screenshot/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/6.jpg -------------------------------------------------------------------------------- /screenshot/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/7.jpg -------------------------------------------------------------------------------- /screenshot/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/8.jpg -------------------------------------------------------------------------------- /screenshot/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/9.jpg -------------------------------------------------------------------------------- /screenshot/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/screenshot/flowchart.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/assets/403.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/assets/403.gif -------------------------------------------------------------------------------- /src/assets/sad.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/components/.gitkeep -------------------------------------------------------------------------------- /src/components/DemoBlock.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | 28 | -------------------------------------------------------------------------------- /src/components/FtyBreadcrumbs.vue: -------------------------------------------------------------------------------- 1 | 10 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/FtyMenu.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/FtyModuleTabs.vue: -------------------------------------------------------------------------------- 1 | 6 | 36 | -------------------------------------------------------------------------------- /src/components/ScrollPane.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 58 | 59 | -------------------------------------------------------------------------------- /src/css/app.styl: -------------------------------------------------------------------------------- 1 | // app global css 2 | -------------------------------------------------------------------------------- /src/css/themes/common.variables.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Stylus variables found in Quasar's source Stylus files. Setting 5 | // variables before Quasar's Stylus will use these variables rather than 6 | // Quasar's default Stylus variable values. Stylus variables specific 7 | // to the themes belong in either the variables.ios.styl or variables.mat.styl files. 8 | 9 | // Check documentation for full list of Quasar variables 10 | 11 | 12 | // App Shared Color Variables 13 | // -------------------------------------------------- 14 | // It's highly recommended to change the default colors 15 | // to match your app's branding. 16 | 17 | //$primary = #027be3 18 | $primary = #41b883 19 | $secondary = #26A69A 20 | $tertiary = #555 21 | 22 | $neutral = #E0E1E2 23 | $positive = #21BA45 24 | $negative = #DB2828 25 | $info = #31CCEC 26 | $warning = #F2C037 27 | -------------------------------------------------------------------------------- /src/css/themes/variables.ios.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // iOS only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/css/themes/variables.mat.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // Material only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/default-access-menu.js: -------------------------------------------------------------------------------- 1 | export const defaultAccessMenu = [ 2 | { 3 | path: "", 4 | icon: "settings", 5 | title: "Website", 6 | name: "website", 7 | leftMemu: true, 8 | children: [ 9 | { 10 | path: "/cms", 11 | icon: "settings", 12 | title: "CMS", 13 | name: "CMS", 14 | leftMemu: true, 15 | children: [ 16 | { 17 | path: "article", 18 | icon: "settings", 19 | title: "Articles", 20 | name: "article", 21 | leftMemu: true 22 | } 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | path: "", 29 | icon: "settings", 30 | title: "System", 31 | name: "system", 32 | leftMemu: true, 33 | children: [ 34 | { 35 | path: "/system", 36 | icon: "settings", 37 | title: "System settings", 38 | name: "system settings", 39 | leftMemu: true, 40 | children: [ 41 | { 42 | path: "menu", 43 | icon: "settings", 44 | title: "Menu management", 45 | name: "menu", 46 | leftMemu: true 47 | } 48 | ] 49 | }, 50 | { 51 | path: "/permission", 52 | icon: "settings", 53 | title: "Authority management", 54 | name: "permission", 55 | leftMemu: true, 56 | children: [ 57 | { 58 | path: "function", 59 | icon: "settings", 60 | title: "Function management", 61 | name: "function", 62 | leftMemu: true 63 | }, 64 | { 65 | path: "role", 66 | icon: "settings", 67 | title: "Role management", 68 | name: "role", 69 | leftMemu: true 70 | }, 71 | { 72 | path: "rolepermission", 73 | icon: "settings", 74 | title: "Role rights management", 75 | name: "rolepermission", 76 | leftMemu: true 77 | }, 78 | { 79 | path: "roleuser", 80 | icon: "settings", 81 | title: "Role user management", 82 | name: "roleuser", 83 | leftMemu: true 84 | }, 85 | { 86 | path: "userrole", 87 | icon: "settings", 88 | title: "Role user management", 89 | name: "userrole", 90 | leftMemu: true 91 | } 92 | ] 93 | }, 94 | { 95 | path: "/organization", 96 | icon: "settings", 97 | title: "Organization", 98 | name: "organization", 99 | leftMemu: true, 100 | children: [ 101 | { 102 | path: "department", 103 | icon: "settings", 104 | title: "Department", 105 | name: "department", 106 | leftMemu: true 107 | }, 108 | { 109 | path: "position", 110 | icon: "settings", 111 | title: "Position management", 112 | name: "position", 113 | leftMemu: true 114 | } 115 | ] 116 | }, 117 | { 118 | path: "/user", 119 | icon: "settings", 120 | title: "User Management", 121 | name: "user", 122 | leftMemu: true, 123 | children: [ 124 | { 125 | path: "index", 126 | icon: "settings", 127 | title: "User Management", 128 | name: "user_index", 129 | leftMemu: true 130 | } 131 | ] 132 | } 133 | ] 134 | }, 135 | { 136 | path: "/", 137 | name: "otherRouter", 138 | leftMemu: false, 139 | children: [ 140 | { 141 | path: "home", 142 | title: "Home", 143 | name: "home_index" 144 | }, 145 | { 146 | path: "userinfo", 147 | title: "Profile / User info", 148 | name: "userinfo" 149 | } 150 | ] 151 | } 152 | ] 153 | -------------------------------------------------------------------------------- /src/i18n/en-us/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | successfulinitdata: "Initialization is successful, please refresh the browser", 3 | loginexpired: "Login information has expired, please login again!", 4 | ID: "Actions", 5 | } 6 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import enUS from './en-us' 2 | import ptBR from './pt-br' 3 | import zhHans from './zh-hans' 4 | 5 | export default { 6 | 'en-us': enUS, 7 | 'pt-br': ptBR, 8 | 'zh-hans': zhHans 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/pt-br/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | Loading: "Carregando", 3 | successfulinitdata: "Inicialização realizada com sucesso, por favor atualize o navegador", 4 | 5 | // User/Login 6 | Username: "Usuário", 7 | Password: "Senha", 8 | Login: "Entrar", 9 | Reset: "Limpar", 10 | Logout: "Sair", 11 | "Please input Username": "Por favor informe o nome do usuário", 12 | "Account password cannot be empty": "A senha precisa ser informada", 13 | loginexpired: "Login expirou, por favor faça login novamente!", 14 | Email: "Email", 15 | "User Email": "Email do usuário", 16 | "Account name": "Conta de usuário", 17 | 18 | // Menu 19 | "System settings": "Configurações do sistema", 20 | "Article management": "Cadastro de artigos", 21 | "Authorization management": "Cadastro de autorizações", 22 | "Department management": "Cadastro de departamentos", 23 | "Function management": "Cadastro de funções", 24 | "Menu management": "Cadastro de menus", 25 | "Position management": "Cadastro de posições", 26 | "Role management": "Cadastro de grupos", 27 | "Role user management": "Cadastro de usuários dos grupos", 28 | "Role rights management": "Cadastro de permissões de usuários", 29 | "User Management": "Cadastro de usuários", 30 | "User role management": "Cadastro de grupos de usuários", 31 | "Business component": "Componente de negócio", 32 | "Official component": "Componente oficial", 33 | "Personal center": "Centro pessoal", 34 | "Website module administrator": "Administrador do Website", 35 | Organization: "Organização", 36 | Button: "Botão", 37 | Buttons: "Botões", 38 | "Button-Group": "Grupo de botões", 39 | "Dropdown Button": "Botão Dropdown", 40 | Navigation: "Navegação", 41 | Toolbar: "Barra de ferramentas", 42 | 43 | "Add top menu": "Adicionar menu superior", 44 | "Add a submenu": "Adicionar um submenu", 45 | "Add menu": "Adicionar menu", 46 | "Edit menu": "Editar menu", 47 | 48 | "Module name": "Nome do módulo", 49 | "Function name": "Nome da função", 50 | "Function code": "Código da função", 51 | "Function description": "Descrição da função", 52 | "Selection module": "Módulo de seleção", 53 | "Role name": "Nome do grupo", 54 | "Role code": "Código do grupo", 55 | "Role description": "Descrição do grupo", 56 | "Role list": "Lista dos grupos", 57 | "List of roles": "Lista de grupos", 58 | "Editing role": "Edição de grupo", 59 | "Module function": "Função do módulo", 60 | "Edit user": "Editar usuário", 61 | "Authority code": "Código de autorização", 62 | "Display on the left": "Exibir a esquerda", 63 | Lock: "Bloquear", 64 | "User list": "Lista de usuários", 65 | "User under": "Usuários", 66 | 67 | // Outros 68 | Other: "Outro", 69 | System: "Sistema", 70 | Website: "Website", 71 | Development: "Desenvolvimento", 72 | Home: "Início", 73 | "Home notification": "Aviso inicial", 74 | User: "Usuário", 75 | Ok: "Ok", 76 | Cancel: "Cancelar", 77 | Add: "Adicionar", 78 | "Added successfully": "Adicionado com sucesso", 79 | Edit: "Editar", 80 | Editing: "Edição", 81 | Delete: "Excluir", 82 | Deleted: "Excluído", 83 | "Batch deletion": "Exclusão em lote", 84 | "Confirm the deletion?": "Você confirma a exclusão?", 85 | "Confirm the bulk delete operation?": "Você confirma a operação de exclusão em lote?", 86 | "Successfully deleted": "Excluído com sucesso", 87 | "Successfully removed": "Removído com sucesso", 88 | "Batch delete succeeded": "Exclusão em lote realizada com sucesso", 89 | Save: "Gravar", 90 | "Saved successfully": "Gravado com sucesso", 91 | Search: "Pesquisar", 92 | Select: "Selecionar", 93 | "Select icon": "Selecione o ícone", 94 | Permission: "Permissão", 95 | Name: "Nome", 96 | Title: "Título", 97 | Phone: "Telefone", 98 | Draft: "Rascunho", 99 | Published: "Publicado", 100 | Classification: "Classificação", 101 | Tags: "Marcadores", 102 | Status: "Situação", 103 | Sort: "Ordem", 104 | "Published date": "Publicação", 105 | "Created date": "Criação", 106 | "Updated date": "Atualização", 107 | Created: "Criação", 108 | ID: "Ações", 109 | Brief: "Resumo", 110 | "System label": "Label de sistema", 111 | Keywords: "Palavras chave", 112 | Release: "Publicação", 113 | "Release time": "Hora de publicação", 114 | "Front end": "Interface", 115 | "Back end": "Retaguarda", 116 | Life: "Permanente", 117 | Method: "Método", 118 | Request: "Requisição", 119 | "Time (ms)": "Tempo (ms)", 120 | "You don't have permission to go to this page": "Você não tem permissão para acessar esta página", 121 | "Or you can go": "Ou você pode ir", 122 | "Back to home": "De volta para o início", 123 | "This page has nothing": "Esta página está vazia", 124 | "Audit log": "Log de auditoria", 125 | "Zhang San": "Zhang San", 126 | 127 | "System error": "Erro de sistema", 128 | "Network timeout": "Timeout de rede", 129 | "Network error": "Erro de rede", 130 | "No data": "Sem dados", 131 | "Rows per page": "Registros por página", 132 | 133 | "Label option": "Opções", 134 | "Close other": "Fechar outros", 135 | "Close all": "Fechar todos", 136 | "Request log": "Log de requisições", 137 | "Initialization Data": "Inicialização dos Dados", 138 | "User info": "Informações do usuário", 139 | "Layout management": "Cadastro de layout", 140 | "Confirm execution": "Confirma execução", 141 | "No request permission": "Sem permissão para a requisição", 142 | Test: "Teste", 143 | 144 | // CMS 145 | "The title can not be blank": "O título não pode ficar em branco", 146 | "Brief description cannot be empty": "O resumo não pode ficar vazio", 147 | "The content can not be blank": "O conteúdo não pode ficar em branco", 148 | "Classification cannot be empty": "A classificação não pode ficar vazia", 149 | 150 | // SKU 151 | "Place of delivery": "Local de entrega", 152 | "Custom name": "Nome personalizado", 153 | Image: "Imagem", 154 | Color: "Cor", 155 | Material: "Material", 156 | "The retail price in the table below is the price of the product that is ultimately displayed to the buyer": 157 | "O preço de varejo na tabela abaixo é o preço do produto que é exibido ao comprador", 158 | "Set retail price in bulk": "Definir preço de varejo a granel", 159 | Define: "Definir", 160 | "Set inventory in batches": "Definir inventário em lotes", 161 | "Retail price": "Preço no varejo", 162 | "In stock": "Em estoque", 163 | "Commodity code": "Código da mercadoria", 164 | Package: "Pacote", 165 | 166 | "Aluminum": "Alumínio", 167 | "Canvas": "Canvas", 168 | "Cotton": "Algodão", 169 | "Twill fabric": "Tecido de sarja", 170 | "Leather": "Couro", 171 | "Microfiber": "Microfibra", 172 | "Synthetic rubber": "Borracha sintética", 173 | "Nylon": "Nilon", 174 | "Plastic": "Plástico", 175 | "Polyester": "Poliester", 176 | "Silica gel": "Silica gel", 177 | "Vinyl": "Vinil", 178 | "Genuine leather": "Couro legítimo", 179 | "Beige": "Bege", 180 | "Black": "Preto", 181 | "Blue": "Azul", 182 | "Sky blue": "Azul céu", 183 | "Brown": "Marrom", 184 | "Transparent": "Transparente", 185 | "Gold": "Ouro", 186 | "Gray": "Cinza", 187 | "Dark gray": "Cinza escuro", 188 | "Green": "Verde", 189 | "ArmyGreen": "Verde exército", 190 | "Ivory white": "Marfim", 191 | "Khaki": "Caqui", 192 | "Multicolor": "Colorido", 193 | "Orange": "Laranja", 194 | "Pink": "Rosa choque", 195 | "Purple": "Púrpura", 196 | "Violet": "Violeta", 197 | "Red": "Vermelho", 198 | "Silver": "Prata", 199 | "White": "Branco", 200 | "Yellow": "Amarelo", 201 | "Rose": "Rosa", 202 | "Crimson": "Carmesim", 203 | "Dark blue": "Azul escuro", 204 | "Matte Black": "Preto fosco", 205 | "Shell + film": "Shell + filme", 206 | "Shell + lanyard": "Shell + cordão", 207 | } 208 | -------------------------------------------------------------------------------- /src/i18n/zh-hans/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | Loading: "Loading", 3 | successfulinitdata: "初始化成功,请刷新浏览器", 4 | 5 | // User/Login 6 | Username: "账号", 7 | Password: "密码", 8 | Login: "Login", 9 | Reset: "Reset", 10 | Logout: "退出", 11 | "Please input Username": "请输入账号", 12 | "Account password cannot be empty": "账号密码不能为空", 13 | loginexpired: "登陆信息已过期,请重新登陆!", 14 | Email: "邮箱", 15 | "User Email": "用户邮箱", 16 | "Account name": "账号名称", 17 | 18 | // Menu 19 | "System settings": "系统设置", 20 | "Article management": "文章管理", 21 | "Authorization management": "权限管理", 22 | "Department management": "部门管理", 23 | "Function management": "功能管理", 24 | "Menu management": "菜单管理", 25 | "Position management": "职位管理", 26 | "Role management": "角色管理", 27 | "Role user management": "角色用户管理", 28 | "Role rights management": "角色权限管理", 29 | "User Management": "用户管理", 30 | "User role management": "用户角色管理", 31 | "Business component": "业务组件", 32 | "Official component": "官方组件", 33 | "Personal center": "个人中心", 34 | "Website module administrator": "网站模块管理员", 35 | Organization: "组织架构", 36 | Button: "Button", 37 | Buttons: "Buttons", 38 | "Button-Group": "Button-Group", 39 | "Dropdown Button": "Dropdown Button", 40 | Navigation: "Navigation", 41 | Toolbar: "Toolbar", 42 | 43 | "Add top menu": "新增顶级菜单", 44 | "Add a submenu": "新增子菜单", 45 | "Add menu": "新增菜单", 46 | "Edit menu": "编辑菜单", 47 | 48 | "Module name": "模块名称", 49 | "Function name": "功能名称", 50 | "Function code": "功能编码", 51 | "Function description": "功能描述", 52 | "Selection module": "选择模块", 53 | "Role name": "角色名称", 54 | "Role code": "角色编码", 55 | "Role description": "角色描述", 56 | "Role list": "角色列表", 57 | "List of roles": "所属角色列表", 58 | "Editing role": "编辑角色", 59 | "Module function": "模块功能", 60 | "Edit user": "编辑用户", 61 | "Authority code": "权限码", 62 | "Display on the left": "是否左侧显示", 63 | Lock: "是否锁定", 64 | "User list": "用户列表", 65 | "User under": "下的用户", 66 | 67 | // Others 68 | Other: "其它", 69 | System: "系统", 70 | Website: "网站", 71 | Development: "开发", 72 | Home: "首页", 73 | "Home notification": "首页通知", 74 | User: "用户", 75 | Ok: "确定", 76 | Cancel: "取消", 77 | Add: "新增", 78 | "Added successfully": "添加成功", 79 | Edit: "编辑", 80 | Editing: "正在编辑", 81 | Delete: "删除", 82 | Deleted: "已删除", 83 | "Batch deletion": "批量删除", 84 | "Confirm the deletion?": "确认执行删除操作?", 85 | "Confirm the bulk delete operation?": "确认执行批量删除操作?", 86 | "Successfully deleted": "删除成功", 87 | "Successfully removed": "移除成功", 88 | "Batch delete succeeded": "批量删除成功", 89 | Save: "保存", 90 | "Saved successfully": "保存成功", 91 | Search: "查询", 92 | Select: "选择", 93 | "Select icon": "选择图标", 94 | Permission: "权限", 95 | Name: "名称", 96 | Title: "标题", 97 | Phone: "Phone", 98 | Draft: "草稿", 99 | Published: "已发布", 100 | Classification: "分类", 101 | Tags: "标签", 102 | Status: "状态", 103 | Sort: "排序", 104 | "Published date": "发布日期", 105 | "Created date": "创建日期", 106 | "Updated date": "更新日期", 107 | Created: "时间", 108 | ID: "操作", 109 | Brief: "简述", 110 | "System label": "系统标签", 111 | Keywords: "关键词", 112 | Release: "发布", 113 | "Release time": "发布时间", 114 | "Front end": "前端", 115 | "Back end": "后端", 116 | Life: "生活", 117 | Method: "方法", 118 | Request: "请求", 119 | "Time (ms)": "耗时(ms)", 120 | "You don't have permission to go to this page": "你没有权限去该页面", 121 | "Or you can go": "或者你可以去", 122 | "Back to home": "回首页", 123 | "This page has nothing": "这个页面什么也没有", 124 | "Audit log": "审计日志", 125 | "Zhang San": "张三", 126 | 127 | "System error": "系统错误", 128 | "Network timeout": "网络超时", 129 | "Network error": "网络错误", 130 | "No data": "没有数据", 131 | "Rows per page": "每页数据", 132 | 133 | "Label option": "标签选项", 134 | "Close other": "关闭其他", 135 | "Close all": "关闭所有", 136 | "Request log": "审计日志", 137 | "Initialization Data": "初始化数据", 138 | "User info": "个人中心", 139 | "Layout management": "布局管理", 140 | "Confirm execution": "确认执行操作", 141 | "No request permission": "没有请求权限", 142 | Test: "测试", 143 | 144 | // CMS 145 | "The title can not be blank": "标题不能为空", 146 | "Brief description cannot be empty": "简述不能为空", 147 | "The content can not be blank": "内容不能为空", 148 | "Classification cannot be empty": "分类不能为空", 149 | 150 | // SKU 151 | "Place of delivery": "发货地", 152 | "Custom name": "自定义名称", 153 | Image: "图片", 154 | Color: "颜色", 155 | Material: "材质", 156 | "The retail price in the table below is the price of the product that is ultimately displayed to the buyer": 157 | "下表的零售价是最终展示给买家的产品价格", 158 | "Set retail price in bulk": "批量设置零售价", 159 | Define: "确定", 160 | "Set inventory in batches": "批量设置库存", 161 | "Retail price": "零售价", 162 | "In stock": "库存", 163 | "Commodity code": "商品编码", 164 | Package: "套餐", 165 | 166 | "Aluminum": "铝", 167 | "Canvas": "帆布", 168 | "Cotton": "棉布", 169 | "Twill fabric": "斜纹布", 170 | "Leather": "皮革", 171 | "Microfiber": "微纤维", 172 | "Synthetic rubber": "合成橡胶", 173 | "Nylon": "尼龙", 174 | "Plastic": "塑料", 175 | "Polyester": "涤纶", 176 | "Silica gel": "硅胶", 177 | "Vinyl": "Vinyl", 178 | "Genuine Leather": "真皮", 179 | "Beige": "米色", 180 | "Black": "黑色", 181 | "Blue": "蓝色", 182 | "Sky blue": "天蓝色", 183 | "Brown": "褐色", 184 | "Transparent": "透明", 185 | "Gold": "金色", 186 | "Gray": "灰色", 187 | "Dark gray": "深灰色", 188 | "Green": "绿色", 189 | "ArmyGreen": "军绿色", 190 | "Ivory white": "象牙白", 191 | "Khaki": "卡其色", 192 | "Multicolor": "多色", 193 | "Orange": "橙色", 194 | "Pink": "粉色", 195 | "Purple": "紫色", 196 | "Violet": "紫罗兰", 197 | "Red": "红色", 198 | "Silver": "银色", 199 | "White": "白色", 200 | "Yellow": "黄色", 201 | "Rose": "玫瑰色", 202 | "Crimson": "深红", 203 | "Dark blue": "深蓝", 204 | "Matte Black": "磨砂黑", 205 | "Shell + film": "壳+贴膜", 206 | "Shell + lanyard": "壳+挂绳", 207 | } 208 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%= htmlWebpackPlugin.options.productName %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% if (htmlWebpackPlugin.options.ctx.mode.pwa) { %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | <% } %> 31 | 32 | <%= htmlWebpackPlugin.options.headScripts %> 33 | 34 | 38 | <% if (!['cordova', 'electron'].includes(htmlWebpackPlugin.options.ctx.modeName) && htmlWebpackPlugin.options.ctx.prod) { 39 | for (var chunk of webpack.chunks) { 40 | for (var file of chunk.files) { 41 | if (file.match(/\.(js|css)$/)) { %> 42 | 43 | <% }}}} %> 44 | 100 | 101 | 102 | 103 | <% if (!htmlWebpackPlugin.options.ctx.mode.electron) { %> 104 | 107 | <% } %> 108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | 116 |
117 | 118 | 119 | <%= htmlWebpackPlugin.options.bodyScripts %> 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/layouts/components/TagsView.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 114 | 115 | 130 | -------------------------------------------------------------------------------- /src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 80 | 81 | 83 | -------------------------------------------------------------------------------- /src/libs/auth.js: -------------------------------------------------------------------------------- 1 | 2 | const TokenKey = 'fty-admin-token' 3 | 4 | export function getToken() { 5 | return localStorage.getItem(TokenKey) 6 | } 7 | 8 | export function setToken(token) { 9 | localStorage.setItem(TokenKey,token) 10 | } 11 | 12 | export function removeToken() { 13 | localStorage.removeItem(TokenKey) 14 | } -------------------------------------------------------------------------------- /src/libs/loading.js: -------------------------------------------------------------------------------- 1 | import { Loading, QSpinnerGears,QSpinnerHourglass } from 'quasar' 2 | 3 | let loading = { 4 | } 5 | let lastRequest = new Date('2018'); 6 | 7 | loading.show = function (config) { 8 | if (config&&config.loading) { 9 | let now = new Date(); 10 | let ms = now - lastRequest; 11 | lastRequest = now; 12 | if (ms > 2000) {//相隔两秒的请求才重新显示loading 13 | if (config.loading == "gears") { 14 | Loading.show({ 15 | spinner: QSpinnerGears, 16 | message: '', 17 | messageColor: 'white', 18 | spinnerSize: 100, 19 | spinnerColor: 'white', 20 | customClass : '' 21 | }) 22 | }else if(config.loading=="hourglass") 23 | { 24 | Loading.show({ 25 | // spinner: QSpinnerHourglass, 26 | message: '', 27 | messageColor: 'white', 28 | spinnerSize: 100, 29 | spinnerColor: 'white', 30 | customClass : '' 31 | }) 32 | } 33 | } 34 | } 35 | 36 | } 37 | 38 | loading.hide = function (config) { 39 | if (config&&config.loading) { 40 | setTimeout(() => { 41 | Loading.hide() 42 | }, 1000) 43 | } 44 | } 45 | 46 | export default loading; 47 | -------------------------------------------------------------------------------- /src/libs/permission.js: -------------------------------------------------------------------------------- 1 | let permission = { 2 | 3 | } 4 | 5 | permission.check = function (config) { 6 | if (config.permission && config.permission.length > 0) { 7 | let needPermissions = config.permission; 8 | let permissions = JSON.parse(localStorage.getItem('permission')); 9 | let isAdmin = localStorage.getItem('isAdmin'); 10 | let hasPermission = permissions.some(s => { 11 | return needPermissions.indexOf(s) > -1; 12 | }) 13 | if (!hasPermission && isAdmin == 0) { 14 | return false 15 | } 16 | } 17 | return true 18 | } 19 | 20 | export default permission -------------------------------------------------------------------------------- /src/libs/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Router from '@/router/index'; 3 | import { getToken, removeToken } from '@/libs/auth' 4 | import loading from '@/libs/loading' 5 | import permission from '@/libs/permission' 6 | import { Notify } from 'quasar' 7 | 8 | // create an axios instance 9 | const service = axios.create({ 10 | baseURL: process.env.API, // api的base_url 11 | timeout: 20000 // request timeout 12 | }) 13 | 14 | // request interceptor 15 | service.interceptors.request.use(config => { 16 | // Do something before request is sent 17 | if (!permission.check(config)) { 18 | Notify.create({ 19 | message: this.$t("No request permission") 20 | }) 21 | throw "403" 22 | } 23 | loading.show(config) 24 | let token = getToken() 25 | if (token) { 26 | config.headers['Authorization'] = 'Bearer ' + token// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改 27 | } 28 | return config 29 | }, error => { 30 | // Do something with request error 31 | //console.log(error) // for debug 32 | Promise.reject(error) 33 | }) 34 | 35 | // respone interceptor 36 | service.interceptors.response.use( 37 | response => { 38 | loading.hide(response.config) 39 | const res = response.data; 40 | if (res.statusCode !== 200) { 41 | Notify.create({ 42 | message: res.msg 43 | }) 44 | return Promise.reject('error'); 45 | } else { 46 | 47 | return response; 48 | } 49 | }, 50 | error => { 51 | loading.hide(error.config) 52 | if (error.response && error.response.status === 401) { 53 | removeToken(); 54 | if (error.config.url.indexOf("logout") === -1) { 55 | Notify.create({ 56 | message: this.$t('loginexpired') 57 | }) 58 | } 59 | setTimeout(() => { 60 | Router.push({ 61 | name: "login" 62 | }); 63 | }, 1000) 64 | 65 | } else if (error.response && error.response.status === 500) { 66 | Notify.create({ 67 | message: this.$t('System error') + '!', 68 | position: 'bottom-right' 69 | }) 70 | } else if (error.message.indexOf("timeout")>-1) { 71 | Notify.create({ 72 | message: this.$t('Network timeout') + '!', 73 | position: 'bottom-right' 74 | }) 75 | } 76 | else if (error == "403") { 77 | 78 | } else { 79 | Notify.create({ 80 | message: this.$t('Network error') + '!', 81 | position: 'bottom-right' 82 | }) 83 | } 84 | return Promise.reject(error) 85 | 86 | }) 87 | 88 | export default service 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/libs/util.js: -------------------------------------------------------------------------------- 1 | let util = { 2 | 3 | }; 4 | util.title = function (title) { 5 | title = title || 'vue.quasar.admin'; 6 | window.document.title = title; 7 | }; 8 | 9 | util.getMenuByName = function (name, menulist) { 10 | let menu = {}; 11 | let forFn = function (name, menulist) { 12 | for (var item of menulist) { 13 | if (item.name === name) { 14 | menu = item; 15 | } else { 16 | if (item.children && item.children.length > 0) { 17 | forFn(name, item.children) 18 | } 19 | } 20 | if (menu.name) { 21 | break; 22 | } 23 | } 24 | } 25 | forFn(name, menulist); 26 | return menu; 27 | } 28 | 29 | util.oneOf = function (ele, targetArr) { 30 | if (targetArr.indexOf(ele) >= 0) { 31 | return true; 32 | } else { 33 | return false; 34 | } 35 | }; 36 | util.getParentMenusByName = function (openAccesseMenu, name) { 37 | let temp = []; 38 | let forFn = function (openAccesseMenu, name) { 39 | for (var item of openAccesseMenu) { 40 | if (item.name === name && item.path !== "/") { 41 | temp.push(item); 42 | forFn(openAccesseMenu, item.parentName); 43 | } 44 | } 45 | }; 46 | forFn(openAccesseMenu, name); 47 | temp.reverse() 48 | return temp; 49 | }; 50 | 51 | util.handleTitle = function (vm, item) { 52 | return item.title; 53 | }; 54 | 55 | util.setCurrentPath = function (vm, name) { 56 | let openAccesseMenu = vm.$store.state.app.openAccessMenu; 57 | let menulistWithParent = util.getParentMenusByName(openAccesseMenu, name); 58 | let currentPathArr = []; 59 | if (!menulistWithParent.some(item => { 60 | return item.name === "home_index" 61 | })) { 62 | currentPathArr.push({ 63 | title: vm.$t("Home"), 64 | path: "/home", 65 | name: 'home_index' 66 | }); 67 | } 68 | currentPathArr.push(...menulistWithParent); 69 | vm.$store.commit('setCurrentPath', currentPathArr); 70 | }; 71 | util.setCurrentModule = function (vm, name) { 72 | let openAccesseMenu = vm.$store.state.app.openAccessMenu; 73 | let moduleList = vm.$store.state.app.moduleList; 74 | let currentModule = vm.$store.state.app.currentModule; 75 | let menulistWithParent = util.getParentMenusByName(openAccesseMenu, name); 76 | if (menulistWithParent.length > 0) { 77 | let moduleName = menulistWithParent[0].name; 78 | if (moduleList.some(item => { 79 | return item.name === moduleName 80 | }) && currentModule !== moduleName) { 81 | vm.$store.dispatch('changeModule', moduleName); 82 | } 83 | } 84 | 85 | }; 86 | util.toDefaultPage = function (menulist, to, routes, next) { 87 | if (to.path == "/") { 88 | next({ name: 'home_index', replace: true }) 89 | } 90 | else { 91 | next() 92 | } 93 | }; 94 | util.openAccesseMenu = function (accesseMenu) { 95 | let openAccesseMenu = []; 96 | let forFn = function (menulist, parentName) { 97 | for (var item of menulist) { 98 | item.parentName = parentName; 99 | openAccesseMenu.push(item) 100 | if (item.children && item.children.length > 0) { 101 | forFn(item.children, item.name) 102 | } 103 | } 104 | } 105 | forFn(accesseMenu, ''); 106 | return openAccesseMenu; 107 | } 108 | 109 | util.openNewPage = function (vm, name, argu, query) { 110 | let pageOpenedList = vm.$store.state.app.pageOpenedList; 111 | let openedPageLen = pageOpenedList.length; 112 | let i = 0; 113 | let tagHasOpened = false; 114 | while (i < openedPageLen) { 115 | if (name === pageOpenedList[i].name) { // 页面已经打开 116 | vm.$store.commit('pageOpenedList', { 117 | index: i, 118 | argu: argu, 119 | query: query 120 | }); 121 | tagHasOpened = true; 122 | break; 123 | } 124 | i++; 125 | } 126 | if (!tagHasOpened) { 127 | let tags = vm.$store.state.app.openAccessMenu.filter((item) => { 128 | return name === item.name; 129 | }); 130 | if (tags.length > 0) { 131 | let tag = tags[0]; 132 | if (argu) { 133 | tag.argu = argu; 134 | } 135 | if (query) { 136 | tag.query = query; 137 | } 138 | vm.$store.commit('increateTag', tag); 139 | } 140 | } 141 | }; 142 | 143 | export default util; 144 | -------------------------------------------------------------------------------- /src/pages/403.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 38 | 39 | 72 | -------------------------------------------------------------------------------- /src/pages/cms/comment.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/pages/cms/post.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 203 | -------------------------------------------------------------------------------- /src/pages/cms/post_edit.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 265 | 272 | -------------------------------------------------------------------------------- /src/pages/develop/official/button-group.md: -------------------------------------------------------------------------------- 1 | :::demo 2 | ```html 3 |

Examples of group buttons. Endless possibilities.

4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 | 13 | 14 | 15 | 16 | 17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 |

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |

33 | 34 | 35 | 36 | 37 | 38 | 39 |

40 | 41 | 42 | 43 | 44 | 45 | 46 |

47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Photos 57 | February 22, 2016 58 | 59 | 60 | 61 | 62 | Files 63 | 64 | 65 | 66 | Vacation 67 | February 22, 2016 68 | 69 | 70 | 71 | 72 | 73 | 74 | ``` 75 | ::: 76 | -------------------------------------------------------------------------------- /src/pages/develop/official/dropdown-button.md: -------------------------------------------------------------------------------- 1 | :::demo 2 | ```html 3 |

These are just a few examples

4 | 5 | 6 | 7 | 8 | 9 | Photos 10 | February 22, 2016 11 | 12 | 13 | 14 | 15 | Files 16 | 17 | 18 | 19 | Vacation 20 | February 22, 2016 21 | 22 | 23 | 24 | 25 | 26 | 27 |

Split with Glossy effect

28 | 29 | 30 | 31 | 32 | 33 | Photos 34 | February 22, 2016 35 | 36 | 37 | 38 | 39 | Files 40 | 41 | 42 | 43 | Vacation 44 | February 22, 2016 45 | 46 | 47 | 48 | 49 | 50 | 51 |

Outline

52 | 53 | 54 | 55 | 56 | 57 | Photos 58 | February 22, 2016 59 | 60 | 61 | 62 | 63 | Files 64 | 65 | 66 | 67 | Vacation 68 | February 22, 2016 69 | 70 | 71 | 72 | 73 | 74 | 75 |

Push type with icon and v-model

76 | 77 | 78 | 79 | 80 | 81 | Photos 82 | February 22, 2016 83 | 84 | 85 | 86 | 87 | Files 88 | 89 | 90 | 91 | Vacation 92 | February 22, 2016 93 | 94 | 95 | 96 | 97 | 98 | 99 |

Push type with icon and v-model

100 | 101 | 102 | 103 | 104 | 105 | Photos 106 | February 22, 2016 107 | 108 | 109 | 110 | 111 | Files 112 | 113 | 114 | 115 | Vacation 116 | February 22, 2016 117 | 118 | 119 | 120 | 121 | 122 | ``` 123 | ::: 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/pages/develop/official/toolbar.md: -------------------------------------------------------------------------------- 1 | :::demo 2 | ```html 3 |

4 | Toolbars are mainly used in Layout headers and footers, but they can be used in your Page view too. 5 |

6 | 7 | 8 | 9 | 10 | Toolbar 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | Toolbar 21 | 22 | 23 | 24 | 25 |

26 | They come in all colors. 27 |

28 | 29 | 30 | 31 | 32 | Toolbar 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Toolbar 41 | Subtitle 42 | 43 | 44 | 45 | 46 | 47 | 48 | 2 49 | 50 | 51 | Long title for Toolbar. Very very very very very very long title. 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Toolbar 62 | 63 | 64 | 65 | 66 | 67 |

68 | And inverted: 69 |

70 | 71 | 72 | 73 | 74 | Toolbar 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Toolbar 83 | Subtitle 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Long title for Toolbar. Very very very very very very long title. 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Toolbar 102 | 103 | 104 | 105 | 106 | ``` 107 | ::: 108 | -------------------------------------------------------------------------------- /src/pages/empty.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 26 | 27 | 60 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 38 | 39 | 128 | -------------------------------------------------------------------------------- /src/pages/login.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 170 | 171 | 221 | -------------------------------------------------------------------------------- /src/pages/organization/department.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/pages/organization/position.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/pages/other/requestlog.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 110 | -------------------------------------------------------------------------------- /src/pages/permission/function.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 328 | -------------------------------------------------------------------------------- /src/pages/permission/role.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 234 | -------------------------------------------------------------------------------- /src/pages/permission/rolepermission.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 248 | -------------------------------------------------------------------------------- /src/pages/permission/roleuser.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 245 | -------------------------------------------------------------------------------- /src/pages/permission/userrole.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 245 | -------------------------------------------------------------------------------- /src/pages/user/user.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 248 | -------------------------------------------------------------------------------- /src/pages/user/userinfo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/plugins/.gitkeep -------------------------------------------------------------------------------- /src/plugins/demo-block.js: -------------------------------------------------------------------------------- 1 | import DemoBlock from '@/components/DemoBlock.vue' 2 | 3 | export default ({ Vue }) => { 4 | Vue.component('demo-block', DemoBlock); 5 | } -------------------------------------------------------------------------------- /src/plugins/i18n.js: -------------------------------------------------------------------------------- 1 | // we import the external package 2 | import VueI18n from 'vue-i18n' 3 | 4 | // let's say we have a file in /src/i18n containing the language pack 5 | import messages from 'src/i18n' 6 | 7 | export default ({ app, router, store, Vue }) => { 8 | // we tell Vue to use our Vue package: 9 | Vue.use(VueI18n) 10 | 11 | // Set i18n instance on app; 12 | // We inject it into root component by doing so; 13 | // new Vue({..., i18n: ... }).$mount(...) 14 | 15 | app.i18n = new VueI18n({ 16 | locale: 'zh-hans', // zh-hans, en-us, pt-br 17 | fallbackLocale: 'zh-hans', 18 | messages 19 | }) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/plugins/permission.js: -------------------------------------------------------------------------------- 1 | export default ({ app, router, store, Vue }) => { 2 | Vue.directive('permission', { 3 | bind: function (el, binding, vnode) { 4 | let needPermissions = binding.value; 5 | let permissions = JSON.parse(localStorage.getItem('permission')); 6 | let isAdmin=localStorage.getItem('isAdmin'); 7 | let hasPermission = permissions.some(s => { 8 | return needPermissions.indexOf(s) > -1; 9 | }) 10 | if (!hasPermission&&isAdmin==0) { 11 | el.style.display = "none"; 12 | } 13 | } 14 | }) 15 | } -------------------------------------------------------------------------------- /src/plugins/vuelidate.js: -------------------------------------------------------------------------------- 1 | import Vuelidate from 'vuelidate' 2 | 3 | export default ({ Vue }) => { 4 | Vue.use(Vuelidate) 5 | } -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Util from '@/libs/util' 4 | import { routes } from './routes' 5 | import { getToken } from '@/libs/auth' 6 | import store from '@/store' 7 | 8 | Vue.use(VueRouter) 9 | 10 | const Router = new VueRouter({ 11 | /* 12 | * NOTE! Change Vue Router mode from quasar.conf.js -> build -> vueRouterMode 13 | * 14 | * If you decide to go with "history" mode, please also set "build.publicPath" 15 | * to something other than an empty string. 16 | * Example: '/' instead of '' 17 | */ 18 | 19 | // Leave as is and change from quasar.conf.js instead! 20 | mode: process.env.VUE_ROUTER_MODE, 21 | base: process.env.VUE_ROUTER_BASE, 22 | scrollBehavior: () => ({ y: 0 }), 23 | routes 24 | }) 25 | 26 | const whiteList = ['/login', '/403']// 设置白名单,避免死循环 27 | 28 | function hasPermission(router, accessMenu) { 29 | if (whiteList.indexOf(router.path) !== -1) { 30 | return true; 31 | } 32 | let menu = Util.getMenuByName(router.name, accessMenu); 33 | if (menu.name) { 34 | return true; 35 | } 36 | return false; 37 | 38 | } 39 | 40 | //地址栏改变,比$route(to)先触发 41 | Router.beforeEach(async (to, from, next) => { 42 | if (getToken()) { 43 | let userInfo = store.state.user.userInfo; 44 | if (!userInfo.name) { 45 | try { 46 | await store.dispatch("GetUserInfo") 47 | await store.dispatch('updateAccessMenu') 48 | if (to.path === '/login') { 49 | next({ name: 'home_index' }) 50 | } else { 51 | //Util.toDefaultPage([...routers], to.name, router, next); 52 | next({ ...to, replace: true })//菜单权限更新完成,重新进一次当前路由 53 | } 54 | } 55 | catch (e) { 56 | if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 57 | next() 58 | } else { 59 | next('/login') 60 | } 61 | } 62 | } else { 63 | if (to.path === '/login') { 64 | next({ name: 'home_index' }) 65 | } else { 66 | if (hasPermission(to, store.getters.accessMenu)) { 67 | Util.toDefaultPage(store.getters.accessMenu,to, routes, next); 68 | } else { 69 | next({ path: '/403',replace:true }) 70 | } 71 | } 72 | } 73 | } else { 74 | if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 75 | next() 76 | } else { 77 | next('/login') 78 | } 79 | } 80 | let menu = Util.getMenuByName(to.name, store.getters.accessMenu); 81 | Util.title(menu.title); 82 | }); 83 | 84 | Router.afterEach((to) => { 85 | window.scrollTo(0, 0); 86 | }); 87 | 88 | export default Router 89 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | 2 | const layout = () => import('layouts/common'); 3 | 4 | //不作为Main组件的子页面展示的页面单独写,如下 5 | const loginRouter = { 6 | path: '/login', 7 | name: 'login', 8 | component: () => import('pages/login') 9 | }; 10 | 11 | 12 | const page403 = { 13 | path: '/403', 14 | name: '403', 15 | component: () => import('pages/403') 16 | }; 17 | 18 | // 作为layout组件的子页面展示但是不在左侧菜单显示的路由写在otherRouter里(children至少包含一个子路由) 19 | const otherRouter = { 20 | path: '/', 21 | name: 'otherRouter', 22 | component: layout, 23 | children: [ 24 | { 25 | path: 'home', 26 | name: 'home_index', 27 | component: () => import('pages/index') 28 | }, 29 | { 30 | path: 'userinfo', 31 | name: 'userinfo', 32 | component: () => import('pages/user/userinfo') 33 | }, 34 | { 35 | path: 'requestlog', 36 | name: 'requestlog', 37 | component: () => import('pages/other/requestlog') 38 | } 39 | ] 40 | }; 41 | // 作为layout组件的子页面展示并且在左侧菜单显示的路由写在appRouter里(children至少包含一个子路由) 42 | const appRouter = [ 43 | { 44 | path: '/cms', 45 | name: 'CMS', 46 | component: layout, 47 | children: [ 48 | { 49 | path: 'article', 50 | name: 'article', 51 | component: () => import('pages/cms/post.vue') 52 | }, 53 | { 54 | path: 'article/:id', 55 | name: 'post_edit', 56 | component: () => import('pages/cms/post_edit.vue') 57 | }, 58 | { 59 | path: 'comment', 60 | name: 'comment', 61 | component: () => import('pages/cms/comment.vue') 62 | }, 63 | ] 64 | }, 65 | { 66 | path: '/system', 67 | name: 'system settings', 68 | component: layout, 69 | children: [ 70 | { 71 | path: 'menu', 72 | name: 'menu', 73 | component: () => import('pages/system/menu.vue') 74 | } 75 | ] 76 | }, 77 | { 78 | path: '/permission', 79 | name: 'permission', 80 | component: layout, 81 | children: [ 82 | { 83 | path: 'function', 84 | name: 'function', 85 | component: () => import('pages/permission/function.vue') 86 | }, 87 | { 88 | path: 'role', 89 | name: 'role', 90 | component: () => import('pages/permission/role.vue') 91 | }, 92 | { 93 | path: 'rolepermission', 94 | name: 'rolepermission', 95 | component: () => import('pages/permission/rolepermission.vue') 96 | }, 97 | { 98 | path: 'roleuser', 99 | name: 'roleuser', 100 | component: () => import('pages/permission/roleuser.vue') 101 | }, 102 | { 103 | path: 'userrole', 104 | name: 'userrole', 105 | component: () => import('pages/permission/userrole.vue') 106 | } 107 | ] 108 | }, 109 | { 110 | path: '/organization', 111 | name: 'organization', 112 | component: layout, 113 | children: [ 114 | { 115 | path: 'department', 116 | name: 'department', 117 | component: () => import('pages/organization/department.vue') 118 | }, 119 | { 120 | path: 'position', 121 | name: 'position', 122 | component: () => import('pages/organization/position.vue') 123 | } 124 | ] 125 | }, 126 | { 127 | path: '/user', 128 | name: 'user', 129 | component: layout, 130 | children: [ 131 | { 132 | path: 'index', 133 | name: 'user_index', 134 | component: () => import('pages/user/user.vue') 135 | } 136 | ] 137 | }, 138 | { 139 | path: '/business', 140 | name: 'business', 141 | component: layout, 142 | children: [ 143 | { 144 | path: 'sku', 145 | name: 'sku', 146 | component: () => import('pages/develop/business/sku.vue') 147 | } 148 | ] 149 | } 150 | ]; 151 | 152 | const developRouter = [ 153 | { 154 | path: '/official', 155 | name: 'official', 156 | component: layout, 157 | children: [ 158 | { 159 | path: 'button', 160 | name: 'button', 161 | component: () => import('pages/develop/official/button.md') 162 | }, 163 | { 164 | path: 'button-group', 165 | name: 'button-group', 166 | component: () => import('pages/develop/official/button-group.md') 167 | }, 168 | { 169 | path: 'dropdown-button', 170 | name: 'dropdown-button', 171 | component: () => import('pages/develop/official/dropdown-button.md') 172 | }, 173 | { 174 | path: 'toolbar', 175 | name: 'toolbar', 176 | component: () => import('pages/develop/official/toolbar.md') 177 | } 178 | ] 179 | } 180 | ] 181 | 182 | // 所有上面定义的路由都要写在下面的routers里 183 | export const routes = [ 184 | loginRouter, 185 | page403, 186 | otherRouter, 187 | ...appRouter, 188 | ...developRouter 189 | ]; 190 | 191 | -------------------------------------------------------------------------------- /src/service/cms/post.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | 3 | export function getPostPagedList(query) { 4 | return request({ 5 | url: '/post/pagedlist', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function getPost(id) { 12 | return request({ 13 | url: '/post/' + id, 14 | method: 'get', 15 | loading: "hourglass" 16 | }) 17 | } 18 | 19 | export function savePost(post) { 20 | return request({ 21 | url: '/post/save', 22 | method: 'post', 23 | data: post, 24 | loading: "hourglass" 25 | }) 26 | } 27 | 28 | export function getTopPostByQuery(query) { 29 | return request({ 30 | url: '/post/top', 31 | method: 'get', 32 | params: query, 33 | loading: "gears" 34 | }) 35 | } -------------------------------------------------------------------------------- /src/service/login.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | import qs from 'qs' 3 | 4 | export function loginByUsername(username, password) { 5 | const data = { 6 | username, 7 | password 8 | } 9 | return request({ 10 | url: '/auth/login', 11 | method: 'post', 12 | data: qs.stringify(data) 13 | }) 14 | } 15 | 16 | export function logout() { 17 | return request({ 18 | url: '/auth/logout', 19 | method: 'post', 20 | loading:"hourglass" 21 | }) 22 | } 23 | 24 | export function getUserInfo() { 25 | return request({ 26 | url: '/user/info', 27 | method: 'get', 28 | loading:"gears" 29 | }) 30 | } -------------------------------------------------------------------------------- /src/service/other/requestlog.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | 3 | export function getRequestLogPagedList(query) { 4 | return request({ 5 | url: '/requestlog/pagedlist', 6 | method: 'get', 7 | params: query 8 | }) 9 | } -------------------------------------------------------------------------------- /src/service/other/resetdb.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | 3 | export function resetDb() { 4 | return request({ 5 | url: '/resetdb', 6 | method: 'post', 7 | loading:"hourglass" 8 | }) 9 | } -------------------------------------------------------------------------------- /src/service/permission/function.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | import qs from 'qs' 3 | 4 | export function getMenuListFunctionCode() { 5 | return request({ 6 | url: '/function/menulistfunctioncode', 7 | method: 'get', 8 | loading:"gears" 9 | }) 10 | } 11 | 12 | export function getFunctionPagedList(query) { 13 | return request({ 14 | url: '/function/pagedlist', 15 | method: 'get', 16 | params: query 17 | }) 18 | } 19 | 20 | export function delFunction(id) { 21 | return request({ 22 | url: '/function/del', 23 | method: 'delete', 24 | params: id, 25 | loading:"hourglass" 26 | }) 27 | } 28 | 29 | export function delFunctions(ids) { 30 | return request({ 31 | url: '/function/batchdel', 32 | method: 'delete', 33 | params:ids, 34 | loading:"hourglass" 35 | }) 36 | } 37 | 38 | export function saveFunction(data) { 39 | return request({ 40 | url: '/function/save', 41 | method: 'post', 42 | data: data, 43 | loading:'hourglass' 44 | }) 45 | } -------------------------------------------------------------------------------- /src/service/permission/role.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | 3 | export function getRolePagedList(query) { 4 | return request({ 5 | url: '/role/pagedlist', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function delRole(id) { 12 | return request({ 13 | url: '/role/del', 14 | method: 'delete', 15 | params: id, 16 | loading:"hourglass" 17 | }) 18 | } 19 | 20 | export function delRoles(ids) { 21 | return request({ 22 | url: '/role/batchdel', 23 | method: 'delete', 24 | params:ids, 25 | loading:"hourglass" 26 | }) 27 | } 28 | 29 | export function saveRole(data) { 30 | return request({ 31 | url: '/role/save', 32 | method: 'post', 33 | data: data, 34 | loading:"hourglass" 35 | }) 36 | } 37 | 38 | export function savePermission(data){ 39 | return request({ 40 | url: '/role/savepermission', 41 | method: 'post', 42 | data: data, 43 | loading:"hourglass" 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/service/system/menu.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | import qs from 'qs' 3 | 4 | export function getAccessMemu() { 5 | return request({ 6 | url: '/menu/getaccessmenu', 7 | method: 'get', 8 | loading:"gears" 9 | }) 10 | } 11 | 12 | export function getAllMenu(){ 13 | return request({ 14 | url: '/menu', 15 | method: 'get', 16 | loading:"gears" 17 | }) 18 | } 19 | 20 | export function saveMenu(menu){ 21 | return request({ 22 | url: '/menu/savemenu', 23 | method: 'post', 24 | data:menu, 25 | loading:"hourglass", 26 | permission:["menu_edit"] 27 | }) 28 | } 29 | 30 | export function getMenuFunctions(menuId){ 31 | return request({ 32 | url: '/menu/menufunctions', 33 | method: 'get', 34 | params:menuId, 35 | loading:"gears" 36 | }) 37 | } 38 | 39 | export function getIcons(){ 40 | return request({ 41 | url: '/icons', 42 | method: 'get', 43 | loading:"gears" 44 | }) 45 | } -------------------------------------------------------------------------------- /src/service/user/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/libs/request' 2 | 3 | export function getUserList() { 4 | return request({ 5 | url: '/user/userlist', 6 | method: 'get' 7 | }) 8 | } 9 | export function getUserPagedList(query) { 10 | return request({ 11 | url: '/user/pagedlist', 12 | method: 'get', 13 | params: query 14 | }) 15 | } 16 | 17 | export function delUser(id) { 18 | return request({ 19 | url: '/user/del', 20 | method: 'delete', 21 | params: id, 22 | loading:"hourglass" 23 | }) 24 | } 25 | 26 | export function delUsers(ids) { 27 | return request({ 28 | url: '/user/batchdel', 29 | method: 'delete', 30 | params:ids, 31 | loading:"hourglass" 32 | }) 33 | } 34 | 35 | export function saveUser(data) { 36 | return request({ 37 | url: '/user/save', 38 | method: 'post', 39 | data: data, 40 | loading:"hourglass" 41 | }) 42 | } 43 | 44 | export function editRoleUser(data){ 45 | return request({ 46 | url: '/user/editroleuser', 47 | method: 'post', 48 | data: data, 49 | loading:"hourglass" 50 | }) 51 | } -------------------------------------------------------------------------------- /src/statics/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /src/statics/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/favicon-16x16.png -------------------------------------------------------------------------------- /src/statics/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/favicon-32x32.png -------------------------------------------------------------------------------- /src/statics/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/statics/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/statics/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/icon-256x256.png -------------------------------------------------------------------------------- /src/statics/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/statics/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/statics/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /src/statics/quasar-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/vue-quasar-admin/9769a6fdd59ec2965797ce5cc0543fb363fb660c/src/statics/quasar-logo.png -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | cachePage: state => state.app.cachePage, 3 | pageOpenedList: state => state.app.pageOpenedList, 4 | currentPath:state=>state.app.currentPath, 5 | accessMenu:state=>state.app.accessMenu, 6 | } 7 | export default getters 8 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import app from './modules/app'; 5 | import user from './modules/user'; 6 | import getters from './getters' 7 | 8 | Vue.use(Vuex); 9 | 10 | const store = new Vuex.Store({ 11 | mutations: { 12 | // 13 | }, 14 | actions: { 15 | 16 | }, 17 | modules: { 18 | app, 19 | user 20 | }, 21 | getters 22 | }); 23 | 24 | export default store; 25 | -------------------------------------------------------------------------------- /src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | import Util from '@/libs/util'; 2 | import { getAccessMemu } from '@/service/system/menu' 3 | import { defaultAccessMenu } from '@/default-access-menu' 4 | 5 | const app = { 6 | state: { 7 | currentPath: [ 8 | { 9 | title: 'Home', 10 | path: '', 11 | name: 'home_index' 12 | } 13 | ], // 面包屑数组 14 | pageOpenedList: [ 15 | { 16 | title: 'Home', 17 | path: '', 18 | name: 'home_index' 19 | } 20 | ],//打开的标签 21 | cachePage: [],//缓存的页面 22 | dontCache: ["home_index"], // 在这里定义你不想要缓存的页面的name属性值 23 | currentModule: '',//当前模块 24 | accessMenu: [],//可访问的菜单, 25 | openAccessMenu: [],//展开的可访问的菜单(子级包含父级name) 26 | moduleList: [],//模块列表 27 | moduleMenu: [],//模块菜单 28 | }, 29 | mutations: { 30 | setCurrentPath(state, pathArr) { 31 | state.currentPath = pathArr; 32 | }, 33 | setCurrentModule(state, module) { 34 | state.currentModule = module; 35 | }, 36 | setAccessMenu(state, list) { 37 | state.accessMenu = list; 38 | }, 39 | setOpenAccessMenu(state, list) { 40 | state.openAccessMenu = list; 41 | }, 42 | setModuleList(state, list) { 43 | state.moduleList = list 44 | }, 45 | setModuleMenu(state, list) { 46 | state.moduleMenu = list 47 | }, 48 | pageOpenedList(state, get) { 49 | let openedPage = state.pageOpenedList[get.index]; 50 | if (get.argu) { 51 | openedPage.argu = get.argu; 52 | } 53 | if (get.query) { 54 | openedPage.query = get.query; 55 | } 56 | //替换新标签 57 | state.pageOpenedList.splice(get.index, 1, openedPage); 58 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 59 | }, 60 | increateTag(state, tagObj) { 61 | if (!Util.oneOf(tagObj.name, state.dontCache)) { 62 | state.cachePage.push(tagObj.name); 63 | localStorage.cachePage = JSON.stringify(state.cachePage); 64 | } 65 | state.pageOpenedList.push(tagObj); 66 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 67 | }, 68 | closePage(state, name) { 69 | for (const [i, v] of state.cachePage.entries()) { 70 | if (v === name) { 71 | state.cachePage.splice(i, 1) 72 | break 73 | } 74 | } 75 | for (const [i, v] of state.pageOpenedList.entries()) { 76 | if (v.name === name) { 77 | state.pageOpenedList.splice(i, 1) 78 | break 79 | } 80 | } 81 | }, 82 | closePages(state, data) { 83 | let type = data.type; 84 | let name = data.name; 85 | if (type === "all") { 86 | state.pageOpenedList = [{ 87 | title: 'Home', 88 | path: '', 89 | name: 'home_index' 90 | }] 91 | state.cachePage = [] 92 | } else { 93 | for (const [i, v] of state.cachePage.entries()) { 94 | if (v === name) { 95 | state.cachePage = state.cachePage.splice(i, 1) 96 | break 97 | } 98 | } 99 | for (const [i, v] of state.pageOpenedList.entries()) { 100 | if (v.name === name) { 101 | state.pageOpenedList = state.pageOpenedList.splice(i, 1, ) 102 | if (name !== "home_index") { 103 | state.pageOpenedList.unshift({ 104 | title: 'Home', 105 | path: '', 106 | name: 'home_index' 107 | }) 108 | } 109 | break 110 | } 111 | } 112 | } 113 | } 114 | }, 115 | actions: { 116 | async updateAccessMenu({ commit }) { 117 | let accesseMenu = defaultAccessMenu; 118 | try { 119 | let response = await getAccessMemu(); 120 | accesseMenu = response.data.data; 121 | } catch (e) { 122 | 123 | } 124 | let openAccesseMenu = Util.openAccesseMenu(accesseMenu); 125 | let moduleList = accesseMenu.filter(item => { 126 | return item.leftMemu 127 | }); 128 | let currentModule = moduleList[0].name; 129 | let moduleMenu = moduleList[0].children; 130 | 131 | commit('setModuleMenu', moduleMenu); 132 | commit('setCurrentModule', currentModule); 133 | commit('setModuleList', moduleList); 134 | commit('setAccessMenu', accesseMenu); 135 | commit('setOpenAccessMenu', openAccesseMenu); 136 | }, 137 | changeModule({ commit, state }, module) { 138 | let accesseMenu = state.accessMenu; 139 | let moduleList = accesseMenu.filter(item => { 140 | return item.leftMemu && item.name === module 141 | }); 142 | let moduleMenu = moduleList[0].children; 143 | commit('setModuleMenu', moduleMenu); 144 | commit('setCurrentModule', module); 145 | }, 146 | closePage({ commit, state }, name) { 147 | return new Promise((resolve) => { 148 | commit('closePage', name) 149 | resolve([...state.pageOpenedList]) 150 | }) 151 | }, 152 | closePages({ commit, state }, data) { 153 | return new Promise((resolve) => { 154 | commit('closePages', data) 155 | resolve([...state.pageOpenedList]) 156 | }) 157 | } 158 | } 159 | }; 160 | 161 | export default app; 162 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import * as authService from '@/libs/auth' 2 | import * as loginService from '@/service/login' 3 | 4 | const user = { 5 | state: { 6 | token: authService.getToken(), 7 | userInfo: { 8 | name: '', 9 | avatar: '' 10 | } 11 | }, 12 | mutations: { 13 | SET_TOKEN(state, token) { 14 | state.token = token 15 | }, 16 | SET_USERINFO(state, userInfo) { 17 | state.userInfo = userInfo 18 | } 19 | }, 20 | actions: { 21 | //用户名登录 22 | LoginByUserName({ commit }, userinfo) { 23 | const username = userinfo.username.trim() 24 | return new Promise((resolve, reject) => { 25 | loginService.loginByUsername(username, userinfo.password).then(response => { 26 | const data = response.data.data 27 | commit('SET_TOKEN', data.accessToken) 28 | authService.setToken(data.accessToken) 29 | resolve() 30 | }).catch(error => { 31 | reject(error) 32 | }) 33 | }) 34 | }, 35 | // 登出 36 | LogOut({ commit }, state) { 37 | return new Promise((resolve, reject) => { 38 | loginService.logout().then((response) => { 39 | commit('SET_TOKEN', '') 40 | commit('SET_USERINFO', { 41 | name: '', 42 | avatar: '' 43 | }) 44 | authService.removeToken() 45 | resolve() 46 | }).catch(error => { 47 | commit('SET_TOKEN', '') 48 | commit('SET_USERINFO', { 49 | name: '', 50 | avatar: '' 51 | }) 52 | authService.removeToken() 53 | reject(error) 54 | }) 55 | }) 56 | }, 57 | GetUserInfo({ commit }, state) { 58 | return new Promise((resolve, reject) => { 59 | loginService.getUserInfo().then((response) => { 60 | const data = response.data.data 61 | const userInfo = { 62 | name: data.userName, 63 | avatar: data.avatarUrl 64 | } 65 | commit('SET_USERINFO', userInfo) 66 | let userRole = data.userRole 67 | let userPermission = data.userPermission 68 | let permission = [...userRole, ...userPermission] 69 | let isAdmin = data.isAdmin 70 | localStorage.setItem("permission", JSON.stringify(permission)) 71 | localStorage.setItem("isAdmin", isAdmin) 72 | resolve(data) 73 | }).catch(error => { 74 | reject(error) 75 | }) 76 | }) 77 | } 78 | } 79 | }; 80 | 81 | export default user; 82 | -------------------------------------------------------------------------------- /strip-tags.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * strip-tags 3 | * 4 | * Copyright (c) 2015 Jon Schlinkert, contributors. 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var cheerio = require('cheerio'); 11 | 12 | exports.strip = function(str, tags) { 13 | var $ = cheerio.load(str, {decodeEntities: false}); 14 | 15 | if (!tags || tags.length === 0) { 16 | return str; 17 | } 18 | 19 | tags = !Array.isArray(tags) ? [tags] : tags; 20 | var len = tags.length; 21 | 22 | while (len--) { 23 | $(tags[len]).remove(); 24 | } 25 | 26 | return $("body").html(); 27 | }; 28 | 29 | exports.fetch = function(str, tag) { 30 | var $ = cheerio.load(str, {decodeEntities: false}); 31 | if (!tag) return str; 32 | 33 | return $(tag).html(); 34 | }; --------------------------------------------------------------------------------