init
0 parents
Showing
106 changed files
with
4799 additions
and
0 deletions
.editorconfig
0 → 100644
.env.development
0 → 100644
.env.production
0 → 100644
.gitignore
0 → 100644
| 1 | .DS_Store | ||
| 2 | node_modules | ||
| 3 | /dist | ||
| 4 | |||
| 5 | # local env files | ||
| 6 | .env.local | ||
| 7 | .env.*.local | ||
| 8 | |||
| 9 | |||
| 10 | # Log files | ||
| 11 | npm-debug.log* | ||
| 12 | yarn-debug.log* | ||
| 13 | yarn-error.log* | ||
| 14 | |||
| 15 | # Editor directories and files | ||
| 16 | .history | ||
| 17 | .idea | ||
| 18 | .vscode | ||
| 19 | *.suo | ||
| 20 | *.ntvs* | ||
| 21 | *.njsproj | ||
| 22 | *.sln | ||
| 23 | *.sw? | ||
| 24 | package-lock.json |
LICENSE
0 → 100644
| 1 | MIT License | ||
| 2 | |||
| 3 | Copyright (c) 2017-present PanJiaChen | ||
| 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. |
README.md
0 → 100644
| 1 | # 安装依赖 | ||
| 2 | npm install | ||
| 3 | |||
| 4 | # 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 | ||
| 5 | npm install --registry=https://registry.npm.taobao.org | ||
| 6 | |||
| 7 | # 启动服务 | ||
| 8 | npm run dev | ||
| 9 | ``` | ||
| 10 | |||
| 11 | 浏览器访问 http://localhost:8888 | ||
| 12 | |||
| 13 | ## 发布 | ||
| 14 | |||
| 15 | |||
| 16 | # 构建生产环境 | ||
| 17 | npm run build | ||
| 18 | ``` | ||
| 19 | |||
| 20 | ## 其它 | ||
| 21 | |||
| 22 | ```bash | ||
| 23 | # 预览发布环境效果 | ||
| 24 | npm run preview | ||
| 25 | |||
| 26 | # 预览发布环境效果 + 静态资源分析 | ||
| 27 | npm run preview -- --report | ||
| 28 | |||
| 29 |
babel.config.js
0 → 100644
| 1 | module.exports = { | ||
| 2 | presets: [ | ||
| 3 | // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app | ||
| 4 | // '@vue/cli-plugin-babel/preset' | ||
| 5 | [ | ||
| 6 | "@vue/app", | ||
| 7 | { | ||
| 8 | "useBuiltIns": "entry", | ||
| 9 | polyfills: [ | ||
| 10 | 'es6.promise', | ||
| 11 | 'es6.symbol' | ||
| 12 | ] | ||
| 13 | } | ||
| 14 | ] | ||
| 15 | ], | ||
| 16 | 'env': { | ||
| 17 | 'development': { | ||
| 18 | // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). | ||
| 19 | // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. | ||
| 20 | // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html | ||
| 21 | 'plugins': ['dynamic-import-node'] | ||
| 22 | } | ||
| 23 | } | ||
| 24 | } |
package.json
0 → 100644
| 1 | { | ||
| 2 | "name": "js.rent", | ||
| 3 | "version": "4.4.0", | ||
| 4 | "description": "HOUTAI", | ||
| 5 | "author": "psh", | ||
| 6 | "scripts": { | ||
| 7 | "dev": "vue-cli-service serve --mode development", | ||
| 8 | "dev-test": "vue-cli-service serve --mode test", | ||
| 9 | "dev-demonstration": "vue-cli-service serve --mode demonstration", | ||
| 10 | "build": "vue-cli-service build --mode build-development", | ||
| 11 | "build-test": "vue-cli-service build --mode build-test", | ||
| 12 | "build-demonstration": "vue-cli-service build --mode build-demonstration" | ||
| 13 | }, | ||
| 14 | "dependencies": { | ||
| 15 | "@babel/polyfill": "^7.12.1", | ||
| 16 | "axios": "^0.21.1", | ||
| 17 | "babel-polyfill": "^6.26.0", | ||
| 18 | "core-js": "^3.6.5", | ||
| 19 | "js-cookie": "2.2.0", | ||
| 20 | "lodash": "^4.17.21", | ||
| 21 | "node-sass": "^4.14.1", | ||
| 22 | "normalize.css": "7.0.0", | ||
| 23 | "nprogress": "0.2.0", | ||
| 24 | "vue": "2.6.10", | ||
| 25 | "vue-pdf": "^4.3.0", | ||
| 26 | "vue-router": "3.0.2", | ||
| 27 | "vuedraggable": "2.20.0", | ||
| 28 | "vuex": "3.1.0" | ||
| 29 | }, | ||
| 30 | "devDependencies": { | ||
| 31 | "@vue/cli-plugin-babel": "4.4.4", | ||
| 32 | "@vue/cli-plugin-unit-jest": "4.4.4", | ||
| 33 | "@vue/cli-service": "4.4.4", | ||
| 34 | "@vue/test-utils": "1.0.0-beta.29", | ||
| 35 | "autoprefixer": "9.5.1", | ||
| 36 | "babel-plugin-dynamic-import-node": "2.3.3", | ||
| 37 | "chalk": "2.4.2", | ||
| 38 | "connect": "3.6.6", | ||
| 39 | "element-ui": "^2.15.6", | ||
| 40 | "html-webpack-plugin": "3.2.0", | ||
| 41 | "runjs": "4.3.2", | ||
| 42 | "sass-loader": "8.0.2", | ||
| 43 | "sass-resources-loader": "^2.2.1", | ||
| 44 | "script-ext-html-webpack-plugin": "2.1.3", | ||
| 45 | "serve-static": "1.13.2", | ||
| 46 | "style-resources-loader": "^1.4.1", | ||
| 47 | "svg-sprite-loader": "4.1.3", | ||
| 48 | "svgo": "^2.3.1", | ||
| 49 | "vue-template-compiler": "2.6.10" | ||
| 50 | }, | ||
| 51 | "browserslist": [ | ||
| 52 | "> 1%", | ||
| 53 | "last 2 versions" | ||
| 54 | ], | ||
| 55 | "bugs": { | ||
| 56 | "url": "https://github.com/PanJiaChen/vue-element-admin/issues" | ||
| 57 | }, | ||
| 58 | "engines": { | ||
| 59 | "node": ">=8.9", | ||
| 60 | "npm": ">= 3.0.0" | ||
| 61 | }, | ||
| 62 | "keywords": [ | ||
| 63 | "vue", | ||
| 64 | "admin", | ||
| 65 | "dashboard", | ||
| 66 | "element-ui", | ||
| 67 | "boilerplate", | ||
| 68 | "admin-template", | ||
| 69 | "management-system" | ||
| 70 | ], | ||
| 71 | "license": "MIT", | ||
| 72 | "husky": { | ||
| 73 | "hooks": { | ||
| 74 | "pre-commit": "lint-staged" | ||
| 75 | } | ||
| 76 | }, | ||
| 77 | "repository": { | ||
| 78 | "type": "git", | ||
| 79 | "url": "git+https://github.com/PanJiaChen/vue-element-admin.git" | ||
| 80 | } | ||
| 81 | } |
public/favicon.ico
0 → 100644
No preview for this file type
public/index.html
0 → 100644
| 1 | <!DOCTYPE html> | ||
| 2 | <html> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | ||
| 6 | <meta name="renderer" content="webkit"> | ||
| 7 | <meta name="referrer" content="no-referrer" /> | ||
| 8 | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | ||
| 9 | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||
| 10 | <title><%= webpackConfig.name %></title> | ||
| 11 | </head> | ||
| 12 | <body> | ||
| 13 | <div id="app"></div> | ||
| 14 | <!-- built files will be auto injected --> | ||
| 15 | |||
| 16 | </body> | ||
| 17 | </html> |
src/api/config.js
0 → 100644
| 1 | export const httpStatus = (() => { | ||
| 2 | const status = { | ||
| 3 | OK: { code: 200, text: 'OK', description: '' }, | ||
| 4 | CREATED: { code: 201, text: 'CREATED', description: '' }, | ||
| 5 | DELETED: { code: 204, text: 'DELETED', description: '' }, | ||
| 6 | NOT_MODIFIED: { code: 304, text: 'NOT MODIFIED', description: '' }, | ||
| 7 | BAD_REQUEST: { code: 400, text: 'BAD REQUEST', description: '不正确的请求信息' }, | ||
| 8 | METHOD_NOT_ALLOWED: { code: 405, text: 'METHOD NOT ALLOWED', description: '请求方法不支持' }, | ||
| 9 | UNSUPPORTED_MEDIA_TYPE: { code: 415, text: 'UNSUPPORTED MEDIA TYPE', description: '媒体类型不支持' }, | ||
| 10 | INTERNAL_SERVER_ERROR: { code: 500, text: 'INTERNAL SERVER_ERROR', description: '服务器内部错误' }, | ||
| 11 | PAGE_NOT_FOUND: { code: 404, text: 'PAGE NOT FOUND', description: '网络资源无法访问' }, | ||
| 12 | NOT_AUTHORIZED: { code: 401, text: 'NOT AUTHORIZED', description: '未经授权的访问' }, | ||
| 13 | FORBIDDEN: { code: 403, text: 'FORBIDDEN', description: '禁止访问' }, | ||
| 14 | UNPROCESSABLE_ENTITY: { code: 422, text: 'UNPROCESSABLE ENTITY', description: '' }, | ||
| 15 | SESSION_TIME_OUT: { code: 419, text: 'SESSION_TIME_OUT', description: '会话超时' }, | ||
| 16 | LOGIN_FAILURE:{ code: 11001, text: 'LOGIN_FAILURE', description: '登录失效' }, | ||
| 17 | } | ||
| 18 | status.CODES = { | ||
| 19 | /** | ||
| 20 | * 成功 | ||
| 21 | */ | ||
| 22 | SUCCESS: { | ||
| 23 | 200: status.OK, | ||
| 24 | 201: status.CREATED, | ||
| 25 | 204: status.DELETED, | ||
| 26 | 304: status.NOT_MODIFIED | ||
| 27 | }, | ||
| 28 | /** | ||
| 29 | * 程序错误或恶意攻击 | ||
| 30 | */ | ||
| 31 | PROGRAM_ERROR: { | ||
| 32 | 400: status.BAD_REQUEST, | ||
| 33 | 405: status.METHOD_NOT_ALLOWED, | ||
| 34 | 415: status.UNSUPPORTED_MEDIA_TYPE, | ||
| 35 | 500: status.INTERNAL_SERVER_ERROR | ||
| 36 | }, | ||
| 37 | /** | ||
| 38 | * 网络访问错误 | ||
| 39 | */ | ||
| 40 | NETWORK_ERROR: { | ||
| 41 | 404: status.PAGE_NOT_FOUND | ||
| 42 | }, | ||
| 43 | /** | ||
| 44 | * 权限错误 | ||
| 45 | * TODO 419未确定 | ||
| 46 | */ | ||
| 47 | AUTH_ERROR: { | ||
| 48 | 401: status.NOT_AUTHORIZED, | ||
| 49 | 403: status.FORBIDDEN, | ||
| 50 | 419: status.SESSION_TIME_OUT | ||
| 51 | }, | ||
| 52 | /** | ||
| 53 | * 正常交互错误 | ||
| 54 | */ | ||
| 55 | COMMUNICATION_ERROR: { | ||
| 56 | 422: status.UNPROCESSABLE_ENTITY | ||
| 57 | }, | ||
| 58 | /** | ||
| 59 | * 登录失效 | ||
| 60 | */ | ||
| 61 | LOGIN_FAILURE: { | ||
| 62 | 11001: status.LOGIN_FAILURE | ||
| 63 | } | ||
| 64 | } | ||
| 65 | return status | ||
| 66 | })() |
src/api/dictionaries.js
0 → 100644
| 1 | import request from '@/utils/request' | ||
| 2 | |||
| 3 | class dictionaries { | ||
| 4 | // 获取所有字典接口 | ||
| 5 | async getDictionary(data){ | ||
| 6 | return request({ | ||
| 7 | url: '/system/sysDictionary/getDictionary', | ||
| 8 | method: 'get', | ||
| 9 | params: data, | ||
| 10 | }) | ||
| 11 | } | ||
| 12 | // 获取字典根据字典标识码 | ||
| 13 | async getDdicById (id) { | ||
| 14 | return request({ | ||
| 15 | url: '/system/sysDictionary/getDdicById', | ||
| 16 | method: 'get', | ||
| 17 | params: { | ||
| 18 | id: id | ||
| 19 | }, | ||
| 20 | }) | ||
| 21 | } | ||
| 22 | //入住管理 导出 | ||
| 23 | async checkexportData(data){ | ||
| 24 | return request({ | ||
| 25 | url:'/rent/zLiveManage/exportData', | ||
| 26 | method:'post', | ||
| 27 | data, | ||
| 28 | responseType: 'blob' | ||
| 29 | }) | ||
| 30 | } | ||
| 31 | // 获取字典根据字典名称 | ||
| 32 | async getDdicByMC (mc) { | ||
| 33 | return request({ | ||
| 34 | url: '/system/sysDictionary/getDdicByMC', | ||
| 35 | method: 'get', | ||
| 36 | params: { | ||
| 37 | mc: mc | ||
| 38 | }, | ||
| 39 | }) | ||
| 40 | } | ||
| 41 | // | ||
| 42 | async getDetail () { | ||
| 43 | return request({ | ||
| 44 | url: '/system/sysUser/getAddressInfo', | ||
| 45 | method: 'get', | ||
| 46 | }) | ||
| 47 | } | ||
| 48 | // 根据市id,获取行政区-县区 | ||
| 49 | async getDistByCity () { | ||
| 50 | return request({ | ||
| 51 | url: '/xzq/getDistByCity', | ||
| 52 | method: 'get', | ||
| 53 | params: { | ||
| 54 | id: '350200000000' | ||
| 55 | }, | ||
| 56 | }) | ||
| 57 | } | ||
| 58 | // 行政区-县区-街道 | ||
| 59 | async getXzq () { | ||
| 60 | return request({ | ||
| 61 | url: '/xzq/getXzq', | ||
| 62 | method: 'get' | ||
| 63 | }) | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | // 联想查询接口 | ||
| 68 | async searchDepartmentByKeys () { | ||
| 69 | return request({ | ||
| 70 | url: '/resource/fDepartment/searchDepartmentByKeys', | ||
| 71 | method: 'get', | ||
| 72 | }) | ||
| 73 | } | ||
| 74 | async getAllDepartment(){ | ||
| 75 | return request({ | ||
| 76 | url:'/resource/fDepartment/getAllDepartment', | ||
| 77 | method:'get' | ||
| 78 | }) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | export default new dictionaries() |
src/api/login.js
0 → 100644
| 1 | import request from '@/utils/request' | ||
| 2 | |||
| 3 | |||
| 4 | export function doLogin (data) { | ||
| 5 | return request({ | ||
| 6 | method: 'post', | ||
| 7 | url: '/doLogin', | ||
| 8 | data | ||
| 9 | }) | ||
| 10 | } | ||
| 11 | |||
| 12 | export function verifyCode () { | ||
| 13 | return request({ | ||
| 14 | method: 'get', | ||
| 15 | url: '/verifyCode', | ||
| 16 | }) | ||
| 17 | } | ||
| 18 | // 获取当前用户登录的信息 | ||
| 19 | export function getCurrentUserInfo () { | ||
| 20 | return request({ | ||
| 21 | method: 'get', | ||
| 22 | url: '/getSysUserDo', | ||
| 23 | }) | ||
| 24 | } | ||
| 25 | |||
| 26 | // 忘记密码 | ||
| 27 | export function forgetPassword (data) { | ||
| 28 | return request({ | ||
| 29 | method: 'post', | ||
| 30 | url: '/system/sysUser/forgetPassword', | ||
| 31 | data | ||
| 32 | }) | ||
| 33 | } | ||
| 34 | |||
| 35 | // 入驻 | ||
| 36 | export function settle (data) { | ||
| 37 | return request({ | ||
| 38 | method: 'post', | ||
| 39 | url: '/settle', | ||
| 40 | data: data | ||
| 41 | }) | ||
| 42 | } | ||
| 43 | |||
| 44 | // 获取当前登录用户有权限的分店列表 | ||
| 45 | export function getOrganizationUserList () { | ||
| 46 | return request({ | ||
| 47 | url: '/getOrganizationUserList', | ||
| 48 | method: 'get' | ||
| 49 | }) | ||
| 50 | } | ||
| 51 | |||
| 52 | // 重新设置当前登录用户信息 | ||
| 53 | export function setOrganizationId (organizationId) { | ||
| 54 | return request({ | ||
| 55 | url: '/setOrganizationId', | ||
| 56 | method: 'get', | ||
| 57 | params: { | ||
| 58 | organizationId: organizationId | ||
| 59 | } | ||
| 60 | }) | ||
| 61 | } |
src/api/user.js
0 → 100644
| 1 | import request from '@/utils/request' | ||
| 2 | export function login (data) { | ||
| 3 | return request({ | ||
| 4 | url: '/doLogin', | ||
| 5 | method: 'post', | ||
| 6 | data | ||
| 7 | }) | ||
| 8 | } | ||
| 9 | |||
| 10 | export function getMenuInfo () { | ||
| 11 | return request({ | ||
| 12 | url: '/getWebMenuRole', | ||
| 13 | method: 'get', | ||
| 14 | }) | ||
| 15 | } | ||
| 16 | |||
| 17 | export function logout () { | ||
| 18 | return request({ | ||
| 19 | url: '/admin/logout', | ||
| 20 | method: 'post' | ||
| 21 | }) | ||
| 22 | } |
src/assets/401_images/401.gif
0 → 100644
160 KB
src/assets/404_images/404.png
0 → 100644
95.8 KB
src/assets/404_images/404_cloud.png
0 → 100644
4.65 KB
src/assets/global_images/logo.png
0 → 100644
6.3 KB
src/components/Breadcrumb/index.vue
0 → 100644
| 1 | <!--面包屑 --> | ||
| 2 | <template> | ||
| 3 | <el-breadcrumb class="app-breadcrumb" separator=">"> | ||
| 4 | <transition-group name="breadcrumb"> | ||
| 5 | <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"> | ||
| 6 | <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ | ||
| 7 | item.meta.title | ||
| 8 | }}</span> | ||
| 9 | <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> | ||
| 10 | </el-breadcrumb-item> | ||
| 11 | </transition-group> | ||
| 12 | </el-breadcrumb> | ||
| 13 | </template> | ||
| 14 | |||
| 15 | <script> | ||
| 16 | export default { | ||
| 17 | data () { | ||
| 18 | return { | ||
| 19 | levelList: null | ||
| 20 | } | ||
| 21 | }, | ||
| 22 | watch: { | ||
| 23 | $route (route) { | ||
| 24 | // if you go to the redirect page, do not update the breadcrumbs | ||
| 25 | if (route.path.startsWith('/redirect/')) { | ||
| 26 | return | ||
| 27 | } | ||
| 28 | this.getBreadcrumb() | ||
| 29 | } | ||
| 30 | }, | ||
| 31 | created () { | ||
| 32 | this.getBreadcrumb() | ||
| 33 | }, | ||
| 34 | methods: { | ||
| 35 | getBreadcrumb () { | ||
| 36 | // only show routes with meta.title | ||
| 37 | let matched = this.$route.matched.filter(item => item.meta && item.meta.title) | ||
| 38 | const first = matched[0] | ||
| 39 | |||
| 40 | if (!this.isDashboard(first)) { | ||
| 41 | matched = [{ path: '/home', meta: { title: '首页' } }].concat(matched) | ||
| 42 | } | ||
| 43 | |||
| 44 | this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) | ||
| 45 | }, | ||
| 46 | isDashboard (route) { | ||
| 47 | const name = route && route.name | ||
| 48 | if (!name) { | ||
| 49 | return false | ||
| 50 | } | ||
| 51 | return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase() | ||
| 52 | }, | ||
| 53 | pathCompile (path) { | ||
| 54 | // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561 | ||
| 55 | const { params } = this.$route | ||
| 56 | var toPath = pathToRegexp.compile(path) | ||
| 57 | return toPath(params) | ||
| 58 | }, | ||
| 59 | handleLink (item) { | ||
| 60 | const { redirect, path } = item | ||
| 61 | if (redirect) { | ||
| 62 | this.$router.push(redirect) | ||
| 63 | return | ||
| 64 | } | ||
| 65 | this.$router.push(this.pathCompile(path)) | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | </script> | ||
| 70 | <style lang="scss" scoped> | ||
| 71 | .app-breadcrumb.el-breadcrumb { | ||
| 72 | display: inline-block; | ||
| 73 | font-size: 14px; | ||
| 74 | line-height: 43px; | ||
| 75 | margin-left: 8px; | ||
| 76 | |||
| 77 | .no-redirect { | ||
| 78 | cursor: text; | ||
| 79 | } | ||
| 80 | |||
| 81 | /deep/.el-breadcrumb__separator { | ||
| 82 | font-weight: 500; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | </style> |
src/components/Hamburger/index.vue
0 → 100644
| 1 | <!--控制左侧菜单收缩显示--> | ||
| 2 | <template> | ||
| 3 | <div style="padding: 0 15px;" @click="toggleClick"> | ||
| 4 | <svg-icon :class="{'is-active':isActive}" class="hamburg" icon-class="hamburg" /> | ||
| 5 | </div> | ||
| 6 | </template> | ||
| 7 | |||
| 8 | <script> | ||
| 9 | export default { | ||
| 10 | name: 'Hamburger', | ||
| 11 | props: { | ||
| 12 | isActive: { | ||
| 13 | type: Boolean, | ||
| 14 | default: false | ||
| 15 | } | ||
| 16 | }, | ||
| 17 | methods: { | ||
| 18 | toggleClick() { | ||
| 19 | this.$emit('toggleClick') | ||
| 20 | } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | </script> | ||
| 24 | |||
| 25 | <style scoped> | ||
| 26 | .hamburg { | ||
| 27 | display: inline-block; | ||
| 28 | vertical-align: middle; | ||
| 29 | width: 20px; | ||
| 30 | height: 20px; | ||
| 31 | transition: all 0.2s ease-in; | ||
| 32 | } | ||
| 33 | |||
| 34 | .hamburg.is-active { | ||
| 35 | transform: rotate(180deg); | ||
| 36 | } | ||
| 37 | </style> |
src/components/SvgIcon/index.vue
0 → 100644
| 1 | <!--显示svg文件图标--> | ||
| 2 | <template> | ||
| 3 | <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" /> | ||
| 4 | <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"> | ||
| 5 | <use :xlink:href="iconName" /> | ||
| 6 | </svg> | ||
| 7 | </template> | ||
| 8 | |||
| 9 | <script> | ||
| 10 | // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage | ||
| 11 | import { isExternal } from '@/utils/validate' | ||
| 12 | |||
| 13 | export default { | ||
| 14 | name: 'SvgIcon', | ||
| 15 | props: { | ||
| 16 | iconClass: { | ||
| 17 | type: String, | ||
| 18 | required: true | ||
| 19 | }, | ||
| 20 | className: { | ||
| 21 | type: String, | ||
| 22 | default: '' | ||
| 23 | } | ||
| 24 | }, | ||
| 25 | computed: { | ||
| 26 | isExternal() { | ||
| 27 | return isExternal(this.iconClass) | ||
| 28 | }, | ||
| 29 | iconName() { | ||
| 30 | return `#icon-${this.iconClass}` | ||
| 31 | }, | ||
| 32 | svgClass() { | ||
| 33 | if (this.className) { | ||
| 34 | return 'svg-icon ' + this.className | ||
| 35 | } else { | ||
| 36 | return 'svg-icon' | ||
| 37 | } | ||
| 38 | }, | ||
| 39 | styleExternalIcon() { | ||
| 40 | return { | ||
| 41 | mask: `url(${this.iconClass}) no-repeat 50% 50%`, | ||
| 42 | '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%` | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | </script> | ||
| 48 | |||
| 49 | <style scoped> | ||
| 50 | .svg-icon { | ||
| 51 | width: 1em; | ||
| 52 | height: 1em; | ||
| 53 | vertical-align: -0.15em; | ||
| 54 | fill: currentColor; | ||
| 55 | overflow: hidden; | ||
| 56 | } | ||
| 57 | |||
| 58 | .svg-external-icon { | ||
| 59 | background-color: currentColor; | ||
| 60 | mask-size: cover!important; | ||
| 61 | display: inline-block; | ||
| 62 | } | ||
| 63 | </style> |
src/components/dialogBox/index.scss
0 → 100644
| 1 | .dialogBox { | ||
| 2 | border-radius: 8px; | ||
| 3 | overflow: hidden; | ||
| 4 | background: #FFFFFF; | ||
| 5 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.10); | ||
| 6 | |||
| 7 | .dialog_title { | ||
| 8 | display: flex; | ||
| 9 | position: relative; | ||
| 10 | top: -2px; | ||
| 11 | |||
| 12 | b { | ||
| 13 | flex: 1; | ||
| 14 | width: 100%; | ||
| 15 | display: flex; | ||
| 16 | align-items: center; | ||
| 17 | justify-content: center; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | .el-dialog__header { | ||
| 22 | height: 50px; | ||
| 23 | background: #FCFDFD; | ||
| 24 | border-radius: 4px 4px 0 0; | ||
| 25 | position: relative; | ||
| 26 | } | ||
| 27 | |||
| 28 | .dialog_full { | ||
| 29 | position: absolute; | ||
| 30 | top: 0; | ||
| 31 | right: 6%; | ||
| 32 | } | ||
| 33 | |||
| 34 | .el-dialog__body { | ||
| 35 | max-height: 88vh; | ||
| 36 | overflow-y: scroll; | ||
| 37 | overflow-x: hidden; | ||
| 38 | } | ||
| 39 | |||
| 40 | .dialog_footer { | ||
| 41 | flex-direction: column; | ||
| 42 | |||
| 43 | .dialog_button { | ||
| 44 | margin-top: 8px; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | .el-dialog__wrapper { | ||
| 50 | overflow: hidden; | ||
| 51 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/dialogBox/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <el-dialog :visible.sync="dialogVisible" v-dialogDrag :width="width" @close="closeDialog('ruleForm', !showEnter)" | ||
| 3 | :fullscreen="fullscreen" top="0" :append-to-body="true" :lock-scroll="true" :close-on-click-modal="false" | ||
| 4 | custom-class="dialogBox" :destroy-on-close="true" :class="[customClass]" id="dialogBox" ref="dialogBox"> | ||
| 5 | <div slot="title" class="dialog_title" ref="dialogTitle"> | ||
| 6 | <b>{{ title }}</b> | ||
| 7 | <div v-if="isFullscreen" class="dialog_full"> | ||
| 8 | <i class="el-icon-rank" v-if="fullscreen" @click="handleFullscreen"></i> | ||
| 9 | <i class="el-icon-full-screen" v-else @click="handleFullscreen" /> | ||
| 10 | </div> | ||
| 11 | </div> | ||
| 12 | <div class="dialogBox-content" :style="{ height: scrollerHeight ? scrollerHeight : 'auto' }" :key="key"> | ||
| 13 | <slot></slot> | ||
| 14 | </div> | ||
| 15 | <div slot="footer" class="dialog_footer" ref="dialogFooter" v-if="isButton"> | ||
| 16 | <div class="dialog_button" v-if="normal"> | ||
| 17 | <el-button @click="closeDialog('ruleForm',)" v-if="isReset && !isSave && showEnter">确定</el-button> | ||
| 18 | <el-button @click="closeDialog('ruleForm', showEnter)" v-if="isReset">取消</el-button> | ||
| 19 | <el-button type="primary" plain @click="submitForm('ruleForm')" v-if="isSave" :loading="saveloding"> | ||
| 20 | {{ saveButton }}</el-button> | ||
| 21 | |||
| 22 | </div> | ||
| 23 | <div class="dialog_button" v-else> | ||
| 24 | <el-button @click="closeDiaActivity(true)">确定</el-button> | ||
| 25 | <el-button @click="closeDiaActivity(false)">取消</el-button> | ||
| 26 | </div> | ||
| 27 | </div> | ||
| 28 | </el-dialog> | ||
| 29 | </template> | ||
| 30 | <script> | ||
| 31 | export default { | ||
| 32 | props: { | ||
| 33 | activity: { | ||
| 34 | type: Boolean, | ||
| 35 | default: false, | ||
| 36 | }, | ||
| 37 | normal: { | ||
| 38 | type: Boolean, | ||
| 39 | default: true, | ||
| 40 | }, | ||
| 41 | showEnter: { | ||
| 42 | type: Boolean, | ||
| 43 | default: true, | ||
| 44 | }, | ||
| 45 | isButton: { | ||
| 46 | type: Boolean, | ||
| 47 | default: true, | ||
| 48 | }, | ||
| 49 | multiple: { | ||
| 50 | type: Boolean, | ||
| 51 | default: false, | ||
| 52 | }, | ||
| 53 | width: { | ||
| 54 | type: String, | ||
| 55 | default: '70%', | ||
| 56 | }, | ||
| 57 | title: { | ||
| 58 | type: String, | ||
| 59 | default: '', | ||
| 60 | }, | ||
| 61 | customClass: { | ||
| 62 | type: String, | ||
| 63 | default: '', | ||
| 64 | }, | ||
| 65 | topHeight: { | ||
| 66 | type: String, | ||
| 67 | default: '0', | ||
| 68 | }, | ||
| 69 | isFullscreen: { | ||
| 70 | type: Boolean, | ||
| 71 | default: true, | ||
| 72 | }, | ||
| 73 | isSave: { | ||
| 74 | type: Boolean, | ||
| 75 | default: true, | ||
| 76 | }, | ||
| 77 | saveButton: { | ||
| 78 | type: String, | ||
| 79 | default: '提交', | ||
| 80 | }, | ||
| 81 | isReset: { | ||
| 82 | type: Boolean, | ||
| 83 | default: true, | ||
| 84 | }, | ||
| 85 | saveloding: { | ||
| 86 | type: Boolean, | ||
| 87 | default: false, | ||
| 88 | }, | ||
| 89 | }, | ||
| 90 | data () { | ||
| 91 | return { | ||
| 92 | key: 0, | ||
| 93 | dialogVisible: false, | ||
| 94 | fullscreen: false, | ||
| 95 | scrollerHeight: '', | ||
| 96 | } | ||
| 97 | }, | ||
| 98 | methods: { | ||
| 99 | isShow () { | ||
| 100 | this.dialogVisible = true | ||
| 101 | }, | ||
| 102 | isHide () { | ||
| 103 | this.dialogVisible = false | ||
| 104 | this.key++ | ||
| 105 | }, | ||
| 106 | handleFullscreen () { | ||
| 107 | this.fullscreen = !this.fullscreen | ||
| 108 | let height = document.getElementById('dialogBox').clientHeight | ||
| 109 | if (!this.fullscreen) { | ||
| 110 | this.scrollerHeight = false | ||
| 111 | } else { | ||
| 112 | this.scrollerHeight = (window.innerHeight - 180) + 'px' | ||
| 113 | } | ||
| 114 | }, | ||
| 115 | submitForm (ruleForm) { | ||
| 116 | if (!this.multiple) { | ||
| 117 | this.$parent.submitForm(ruleForm) | ||
| 118 | } else { | ||
| 119 | this.$emit('submitForm', ruleForm); | ||
| 120 | } | ||
| 121 | }, | ||
| 122 | closeDialog (ruleForm, flag) { | ||
| 123 | console.log(456789, this.multiple) | ||
| 124 | this.key++ | ||
| 125 | if (!this.multiple) { | ||
| 126 | if (this.$parent.closeDialog) { | ||
| 127 | // console.log(1) | ||
| 128 | this.$parent.closeDialog(ruleForm) | ||
| 129 | } else { | ||
| 130 | // console.log(2) | ||
| 131 | this.dialogVisible = false; | ||
| 132 | } | ||
| 133 | } else { | ||
| 134 | this.$emit('closeDialog', ruleForm, flag); | ||
| 135 | } | ||
| 136 | }, | ||
| 137 | closeDiaActivity (flag) { | ||
| 138 | this.$emit('closeDialog', flag); | ||
| 139 | } | ||
| 140 | }, | ||
| 141 | } | ||
| 142 | </script> | ||
| 143 | <style rel="stylesheet/scss" lang="scss" > | ||
| 144 | @import "./index.scss"; | ||
| 145 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/dialogBox/redeme.md
0 → 100644
| 1 | ## 这个是弹框组件,对于element自带的组件进行封装,方便修改全局样式做统一操作 | ||
| 2 | ### 使用时在组件中引用 | ||
| 3 | |||
| 4 | ``` | ||
| 5 | import dialogBox from '@/components/dialogBox/index' | ||
| 6 | |||
| 7 | <dialogBox ref="dialog" title="标题"> | ||
| 8 | **在这里面写弹框内容** | ||
| 9 | </dialogBox> | ||
| 10 | *在父组件中使用 的按钮提交方法* | ||
| 11 | submitForm(ruleForm) { | ||
| 12 | |||
| 13 | } | ||
| 14 | ``` | ||
| 15 | ##### 如果有多个弹框 | ||
| 16 | |||
| 17 | ``` | ||
| 18 | import dialogBox from '@/components/dialogBox/index' | ||
| 19 | |||
| 20 | <dialogBox ref="dialog" @submitForm="自定义方法" title="标题" :multiple="true"> | ||
| 21 | **在这里面写弹框内容** | ||
| 22 | </dialogBox> | ||
| 23 | *在父组件中使用 的按钮提交方法* | ||
| 24 | 自定义方法(ruleForm) { | ||
| 25 | |||
| 26 | } | ||
| 27 | ``` | ||
| 28 | |||
| 29 | ## 打开该dialog: | ||
| 30 | this.$refs.dialog.isShow(); | ||
| 31 | ## 隐藏该dialog: | ||
| 32 | this.$refs.dialog.isHide(); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/lb-table/forced.js
0 → 100644
| 1 | export default { | ||
| 2 | selection: { | ||
| 3 | renderHeader: (h, { store }) => { | ||
| 4 | return ( | ||
| 5 | <el-checkbox | ||
| 6 | disabled={store.states.data && store.states.data.length === 0} | ||
| 7 | indeterminate={ | ||
| 8 | store.states.selection.length > 0 && !store.states.isAllSelected | ||
| 9 | } | ||
| 10 | nativeOn-click={store.toggleAllSelection} | ||
| 11 | value={store.states.isAllSelected} | ||
| 12 | /> | ||
| 13 | ) | ||
| 14 | }, | ||
| 15 | renderCell: (h, { row, column, store, $index }) => { | ||
| 16 | return ( | ||
| 17 | <el-checkbox | ||
| 18 | nativeOn-click={event => event.stopPropagation()} | ||
| 19 | value={store.isSelected(row)} | ||
| 20 | disabled={ | ||
| 21 | column.selectable | ||
| 22 | ? !column.selectable.call(null, row, $index) | ||
| 23 | : false | ||
| 24 | } | ||
| 25 | on-input={() => { | ||
| 26 | store.commit('rowSelectedChanged', row) | ||
| 27 | }} | ||
| 28 | /> | ||
| 29 | ) | ||
| 30 | }, | ||
| 31 | sortable: false, | ||
| 32 | resizable: false | ||
| 33 | }, | ||
| 34 | index: { | ||
| 35 | renderHeader: (h, scope) => { | ||
| 36 | return <span>{scope.column.label || '#'}</span> | ||
| 37 | }, | ||
| 38 | renderCell: (h, { $index, column }) => { | ||
| 39 | let i = $index + 1 | ||
| 40 | const index = column.index | ||
| 41 | |||
| 42 | if (typeof index === 'number') { | ||
| 43 | i = $index + index | ||
| 44 | } else if (typeof index === 'function') { | ||
| 45 | i = index($index) | ||
| 46 | } | ||
| 47 | |||
| 48 | return <div>{i}</div> | ||
| 49 | }, | ||
| 50 | sortable: false | ||
| 51 | }, | ||
| 52 | expand: { | ||
| 53 | renderHeader: (h, scope) => { | ||
| 54 | return <span>{scope.column.label || ''}</span> | ||
| 55 | }, | ||
| 56 | renderCell: (h, { row, store }, proxy) => { | ||
| 57 | const expanded = store.states.expandRows.indexOf(row) > -1 | ||
| 58 | return ( | ||
| 59 | <div | ||
| 60 | class={ | ||
| 61 | 'el-table__expand-icon ' + | ||
| 62 | (expanded ? 'el-table__expand-icon--expanded' : '') | ||
| 63 | } | ||
| 64 | on-click={e => proxy.handleExpandClick(row, e)} | ||
| 65 | > | ||
| 66 | <i class='el-icon el-icon-arrow-right' /> | ||
| 67 | </div> | ||
| 68 | ) | ||
| 69 | }, | ||
| 70 | sortable: false, | ||
| 71 | resizable: false, | ||
| 72 | className: 'el-table__expand-column' | ||
| 73 | } | ||
| 74 | } |
src/components/lb-table/index.js
0 → 100644
src/components/lb-table/lb-column.vue
0 → 100644
| 1 | /* | ||
| 2 | * FileName: lb-column.vue | ||
| 3 | * Remark: element-column | ||
| 4 | * Project: lb-element-table | ||
| 5 | * Author: 任超 | ||
| 6 | * File Created: Tuesday, 19th March 2019 9:58:23 am | ||
| 7 | * Last Modified: Tuesday, 19th March 2019 10:14:42 am | ||
| 8 | * Modified By: 任超 | ||
| 9 | */ | ||
| 10 | |||
| 11 | <template> | ||
| 12 | <el-table-column v-bind="$attrs" | ||
| 13 | v-on="$listeners" | ||
| 14 | :prop="column.prop" | ||
| 15 | :label="column.label" | ||
| 16 | :type="column.type" | ||
| 17 | :index="column.index" | ||
| 18 | :column-key="column.columnKey" | ||
| 19 | :width="column.width" | ||
| 20 | :min-width="setColumnWidth(column.label)" | ||
| 21 | :fixed="column.fixed" | ||
| 22 | :scoped-slot="column.renderHeader" | ||
| 23 | :sortable="column.sortable || false" | ||
| 24 | :sort-method="column.sortMethod" | ||
| 25 | :sort-by="column.sortBy" | ||
| 26 | :sort-orders="column.sortOrders" | ||
| 27 | :resizable="column.resizable || true" | ||
| 28 | :formatter="column.formatter" | ||
| 29 | :show-overflow-tooltip="column.showOverflowTooltip != null ? column.showOverflowTooltip : true" | ||
| 30 | :align="column.align!=null ? column.align : 'left'" | ||
| 31 | :header-align="column.headerAlign || headerAlign || column.align || align || 'left'" | ||
| 32 | :class-name="column.className" | ||
| 33 | :label-class-name="column.labelClassName" | ||
| 34 | :selectable="column.selectable" | ||
| 35 | :reserve-selection="column.reserveSelection || false" | ||
| 36 | :filters="column.filters" | ||
| 37 | :filter-placement="column.filterPlacement" | ||
| 38 | :filter-multiple="column.filterMultiple" | ||
| 39 | :filter-method="column.filterMethod" | ||
| 40 | :filtered-value="column.filteredValue"> | ||
| 41 | <template slot="header" | ||
| 42 | slot-scope="scope"> | ||
| 43 | <lb-render v-if="column.renderHeader" | ||
| 44 | :scope="scope" | ||
| 45 | :render="column.renderHeader"> | ||
| 46 | </lb-render> | ||
| 47 | <span v-else>{{ scope.column.label }}</span> | ||
| 48 | </template> | ||
| 49 | |||
| 50 | <template slot-scope="scope"> | ||
| 51 | <lb-render :scope="scope" | ||
| 52 | :render="column.render"> | ||
| 53 | </lb-render> | ||
| 54 | </template> | ||
| 55 | |||
| 56 | <template v-if="column.children"> | ||
| 57 | <lb-column v-for="(col, index) in column.children" | ||
| 58 | :key="index" | ||
| 59 | :column="col"> | ||
| 60 | </lb-column> | ||
| 61 | </template> | ||
| 62 | </el-table-column> | ||
| 63 | </template> | ||
| 64 | |||
| 65 | <script> | ||
| 66 | import LbRender from './lb-render' | ||
| 67 | import forced from './forced.js' | ||
| 68 | export default { | ||
| 69 | name: 'LbColumn', | ||
| 70 | props: { | ||
| 71 | column: Object, | ||
| 72 | headerAlign: String, | ||
| 73 | align: String | ||
| 74 | }, | ||
| 75 | components: { | ||
| 76 | LbRender | ||
| 77 | }, | ||
| 78 | methods: { | ||
| 79 | |||
| 80 | setColumnWidth(str) { | ||
| 81 | let columnWidth = 0; | ||
| 82 | for (let char of str) { | ||
| 83 | if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) { | ||
| 84 | // 如果是英文字符,为字符分配10个单位宽度,单位宽度可根据字体大小调整 | ||
| 85 | columnWidth += 10 | ||
| 86 | } else if (char >= '\u4e00' && char <= '\u9fa5') { | ||
| 87 | // 如果是中文字符,为字符分配14个单位宽度,单位宽度可根据字体大小调整 | ||
| 88 | columnWidth += 14 | ||
| 89 | } else { | ||
| 90 | // 其他种类字符,为字符分配10个单位宽度,单位宽度可根据字体大小调整 | ||
| 91 | columnWidth += 10 | ||
| 92 | } | ||
| 93 | } | ||
| 94 | if (columnWidth < 120) { | ||
| 95 | // 设置最小宽度 | ||
| 96 | columnWidth = 120 | ||
| 97 | } | ||
| 98 | return columnWidth + 'px' | ||
| 99 | }, | ||
| 100 | setColumn () { | ||
| 101 | if (this.column.type) { | ||
| 102 | this.column.renderHeader = forced[this.column.type].renderHeader | ||
| 103 | this.column.render = this.column.render || forced[this.column.type].renderCell | ||
| 104 | } | ||
| 105 | if (this.column.formatter) { | ||
| 106 | this.column.render = (h, scope) => { | ||
| 107 | return <span>{ scope.column.formatter(scope.row, scope.column, scope.row, scope.$index) }</span> | ||
| 108 | } | ||
| 109 | } | ||
| 110 | if (!this.column.render) { | ||
| 111 | this.column.render = (h, scope) => { | ||
| 112 | return <span>{ scope.row[scope.column.property] }</span> | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | }, | ||
| 117 | watch: { | ||
| 118 | column: { | ||
| 119 | handler () { | ||
| 120 | this.setColumn() | ||
| 121 | }, | ||
| 122 | immediate: true | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | </script> |
src/components/lb-table/lb-render.vue
0 → 100644
| 1 | /* | ||
| 2 | * FileName: lb-render.vue | ||
| 3 | * Remark: 自定义render | ||
| 4 | * Project: lb-element-table | ||
| 5 | * Author: 任超 | ||
| 6 | * File Created: Tuesday, 19th March 2019 10:15:30 am | ||
| 7 | * Last Modified: Tuesday, 19th March 2019 10:15:32 am | ||
| 8 | * Modified By: 任超 | ||
| 9 | */ | ||
| 10 | <script> | ||
| 11 | export default { | ||
| 12 | name: 'LbRender', | ||
| 13 | functional: true, | ||
| 14 | props: { | ||
| 15 | scope: Object, | ||
| 16 | render: Function | ||
| 17 | }, | ||
| 18 | render: (h, ctx) => { | ||
| 19 | return ctx.props.render ? ctx.props.render(h, ctx.props.scope) : '' | ||
| 20 | } | ||
| 21 | } | ||
| 22 | </script> |
src/components/lb-table/lb-table.vue
0 → 100644
| 1 | /* | ||
| 2 | * FileName: lb-table.vue | ||
| 3 | * Remark: element table | ||
| 4 | * Project: lb-element-table | ||
| 5 | * Author: 任超 | ||
| 6 | * File Created: Tuesday, 19th March 2019 9:55:27 am | ||
| 7 | * Last Modified: Tuesday, 19th March 2019 9:55:34 am | ||
| 8 | * Modified By: 任超 | ||
| 9 | */ | ||
| 10 | |||
| 11 | <template> | ||
| 12 | <div :class="['lb-table', customClass]"> | ||
| 13 | <el-table ref="elTable" :row-class-name="tableRowClassName" :show-header='showHeader' | ||
| 14 | :header-cell-style="{ background: '#F8FAFA' }" v-bind="$attrs" :height="tableHeight" v-on="$listeners" :data="data" | ||
| 15 | style="width: 100%" :span-method="this.merge ? this.mergeMethod : this.spanMethod"> | ||
| 16 | <lb-column v-bind="$attrs" v-for="(item, index) in column" :key="index" :column="item"> | ||
| 17 | </lb-column> | ||
| 18 | </el-table> | ||
| 19 | <br> | ||
| 20 | <el-pagination class="lb-table-pagination" v-if="pagination" v-bind="$attrs" v-on="$listeners" background | ||
| 21 | :page-sizes="[10, 15, 20, 50]" layout="total, sizes, prev, pager, next" @current-change="paginationCurrentChange" | ||
| 22 | :style="{ 'margin-top': paginationTop, 'text-align': paginationAlign }"> | ||
| 23 | </el-pagination> | ||
| 24 | </div> | ||
| 25 | </template> | ||
| 26 | |||
| 27 | <script> | ||
| 28 | import LbColumn from './lb-column' | ||
| 29 | export default { | ||
| 30 | props: { | ||
| 31 | column: Array, | ||
| 32 | data: Array, | ||
| 33 | spanMethod: Function, | ||
| 34 | pagination: { | ||
| 35 | type: Boolean, | ||
| 36 | default: true, | ||
| 37 | }, | ||
| 38 | border: { | ||
| 39 | type: Boolean, | ||
| 40 | default: true, | ||
| 41 | }, | ||
| 42 | showHeader: { | ||
| 43 | type: Boolean, | ||
| 44 | default: true, | ||
| 45 | }, | ||
| 46 | paginationTop: { | ||
| 47 | type: String, | ||
| 48 | default: '0', | ||
| 49 | }, | ||
| 50 | heightNum: { | ||
| 51 | type: Number, | ||
| 52 | default: 405, | ||
| 53 | }, | ||
| 54 | heightNumSetting: { | ||
| 55 | type: Boolean, | ||
| 56 | default: false, | ||
| 57 | }, | ||
| 58 | customClass: { | ||
| 59 | type: String, | ||
| 60 | default: '', | ||
| 61 | }, | ||
| 62 | paginationAlign: { | ||
| 63 | type: String, | ||
| 64 | default: 'left', | ||
| 65 | }, | ||
| 66 | merge: Array, | ||
| 67 | }, | ||
| 68 | components: { | ||
| 69 | LbColumn, | ||
| 70 | }, | ||
| 71 | data () { | ||
| 72 | return { | ||
| 73 | tableHeight: '100%', | ||
| 74 | mergeLine: {}, | ||
| 75 | mergeIndex: {}, | ||
| 76 | } | ||
| 77 | }, | ||
| 78 | created () { | ||
| 79 | this.getMergeArr(this.data, this.merge) | ||
| 80 | this.getHeight() | ||
| 81 | |||
| 82 | }, | ||
| 83 | mounted () { | ||
| 84 | }, | ||
| 85 | computed: { | ||
| 86 | dataLength () { | ||
| 87 | return [] || this.data.length | ||
| 88 | }, | ||
| 89 | }, | ||
| 90 | methods: { | ||
| 91 | tableRowClassName ({ row, rowIndex }) { | ||
| 92 | if (rowIndex % 2 === 1) { | ||
| 93 | return 'interlaced'; | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | getHeight () { | ||
| 97 | if (this.heightNumSetting) { | ||
| 98 | this.tableHeight = this.heightNum + 'px' | ||
| 99 | } else { | ||
| 100 | this.tableHeight = window.innerHeight - this.heightNum + 'px' | ||
| 101 | } | ||
| 102 | }, | ||
| 103 | changeHeight (heightNum) { | ||
| 104 | this.tableHeight = heightNum + 'px' | ||
| 105 | }, | ||
| 106 | clearSelection () { | ||
| 107 | this.$refs.elTable.clearSelection() | ||
| 108 | }, | ||
| 109 | toggleRowSelection (row, selected) { | ||
| 110 | this.$refs.elTable.toggleRowSelection(row, selected) | ||
| 111 | }, | ||
| 112 | toggleAllSelection () { | ||
| 113 | this.$refs.elTable.toggleAllSelection() | ||
| 114 | }, | ||
| 115 | toggleRowExpansion (row, expanded) { | ||
| 116 | this.$refs.elTable.toggleRowExpansion(row, expanded) | ||
| 117 | }, | ||
| 118 | setCurrentRow (row) { | ||
| 119 | this.$refs.elTable.setCurrentRow(row) | ||
| 120 | }, | ||
| 121 | clearSort () { | ||
| 122 | this.$refs.elTable.clearSort() | ||
| 123 | }, | ||
| 124 | clearFilter (columnKey) { | ||
| 125 | this.$refs.elTable.clearFilter(columnKey) | ||
| 126 | }, | ||
| 127 | doLayout () { | ||
| 128 | this.$refs.elTable.doLayout() | ||
| 129 | }, | ||
| 130 | sort (prop, order) { | ||
| 131 | this.$refs.elTable.sort(prop, order) | ||
| 132 | }, | ||
| 133 | paginationCurrentChange (val) { | ||
| 134 | this.$emit('p-current-change', val) | ||
| 135 | }, | ||
| 136 | getMergeArr (tableData, merge) { | ||
| 137 | if (!merge) return | ||
| 138 | this.mergeLine = {} | ||
| 139 | this.mergeIndex = {} | ||
| 140 | merge.forEach((item, k) => { | ||
| 141 | tableData.forEach((data, i) => { | ||
| 142 | if (i === 0) { | ||
| 143 | this.mergeIndex[item] = this.mergeIndex[item] || [] | ||
| 144 | this.mergeIndex[item].push(1) | ||
| 145 | this.mergeLine[item] = 0 | ||
| 146 | } else { | ||
| 147 | if (data[item] === tableData[i - 1][item]) { | ||
| 148 | this.mergeIndex[item][this.mergeLine[item]] += 1 | ||
| 149 | this.mergeIndex[item].push(0) | ||
| 150 | } else { | ||
| 151 | this.mergeIndex[item].push(1) | ||
| 152 | this.mergeLine[item] = i | ||
| 153 | } | ||
| 154 | } | ||
| 155 | }) | ||
| 156 | }) | ||
| 157 | }, | ||
| 158 | mergeMethod ({ row, column, rowIndex, columnIndex }) { | ||
| 159 | const index = this.merge.indexOf(column.property) | ||
| 160 | if (index > -1) { | ||
| 161 | const _row = this.mergeIndex[this.merge[index]][rowIndex] | ||
| 162 | const _col = _row > 0 ? 1 : 0 | ||
| 163 | return { | ||
| 164 | rowspan: _row, | ||
| 165 | colspan: _col, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | }, | ||
| 169 | }, | ||
| 170 | watch: { | ||
| 171 | merge () { | ||
| 172 | this.getMergeArr(this.data, this.merge) | ||
| 173 | }, | ||
| 174 | dataLength () { | ||
| 175 | this.getMergeArr(this.data, this.merge) | ||
| 176 | } | ||
| 177 | }, | ||
| 178 | } | ||
| 179 | </script> | ||
| 180 | <style rel="stylesheet/scss" lang="scss" > | ||
| 181 | .lb-table { | ||
| 182 | .interlaced { | ||
| 183 | background: #FCFDFD; | ||
| 184 | ; | ||
| 185 | border: 1px solid #ECECEE; | ||
| 186 | } | ||
| 187 | |||
| 188 | .el-table { | ||
| 189 | border: 1px solid #ECECEE; | ||
| 190 | border-radius: 4px 4px 0 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | .el-table::before { | ||
| 194 | display: none; | ||
| 195 | } | ||
| 196 | |||
| 197 | .el-table--enable-row-hover .el-table__body tr:hover>td { | ||
| 198 | background: #FBFCFD !important; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | .el-table th>.cell { | ||
| 203 | padding-left: 20px; | ||
| 204 | } | ||
| 205 | |||
| 206 | .el-table .cell { | ||
| 207 | padding-left: 20px; | ||
| 208 | } | ||
| 209 | </style> |
src/components/lb-table/redeme.md
0 → 100644
src/directive/dragDialog/index.js
0 → 100644
| 1 | import Vue from "vue"; | ||
| 2 | |||
| 3 | /* | ||
| 4 | |||
| 5 | * 使用方法 | ||
| 6 | |||
| 7 | * 将以下代码复制到一个js文件中,然后在入口文件main.js中import引入即可; | ||
| 8 | |||
| 9 | * 给elementUI的dialog上加上 v-dialogDrag 指令就可以实现弹窗的全屏和拉伸了。 | ||
| 10 | |||
| 11 | * 给dialog设置 :close-on-click-modal="false" , 禁止点击遮罩层关闭弹出层 | ||
| 12 | |||
| 13 | * 如果是form表单,不要将提交等按钮放置el-form-item,以免在上下拉伸时被隐藏 | ||
| 14 | |||
| 15 | */ | ||
| 16 | |||
| 17 | // v-dialogDrag: 弹窗拖拽+水平方向伸缩 | ||
| 18 | |||
| 19 | Vue.directive('dialogDrag', { | ||
| 20 | bind(el, binding, vnode, oldVnode) { | ||
| 21 | const dialogHeaderEl = el.querySelector('.el-dialog__header'); | ||
| 22 | const dragDom = el.querySelector('.el-dialog'); | ||
| 23 | //dialogHeaderEl.style.cursor = 'move'; | ||
| 24 | dialogHeaderEl.style.cssText += ';cursor:move;' | ||
| 25 | dragDom.style.cssText += ';top:0px;' | ||
| 26 | |||
| 27 | // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); | ||
| 28 | const sty = (function () { | ||
| 29 | if (window.document.currentStyle) { | ||
| 30 | return (dom, attr) => dom.currentStyle[attr]; | ||
| 31 | } else { | ||
| 32 | return (dom, attr) => getComputedStyle(dom, false)[attr]; | ||
| 33 | } | ||
| 34 | })() | ||
| 35 | |||
| 36 | dialogHeaderEl.onmousedown = (e) => { | ||
| 37 | // 鼠标按下,计算当前元素距离可视区的距离 | ||
| 38 | const disX = e.clientX - dialogHeaderEl.offsetLeft; | ||
| 39 | const disY = e.clientY - dialogHeaderEl.offsetTop; | ||
| 40 | |||
| 41 | const screenWidth = document.body.clientWidth; // body当前宽度 | ||
| 42 | const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取) | ||
| 43 | |||
| 44 | const dragDomWidth = dragDom.offsetWidth; // 对话框宽度 | ||
| 45 | const dragDomheight = dragDom.offsetHeight; // 对话框高度 | ||
| 46 | |||
| 47 | const minDragDomLeft = dragDom.offsetLeft; | ||
| 48 | const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; | ||
| 49 | |||
| 50 | const minDragDomTop = dragDom.offsetTop; | ||
| 51 | const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; | ||
| 52 | |||
| 53 | |||
| 54 | // 获取到的值带px 正则匹配替换 | ||
| 55 | let styL = sty(dragDom, 'left'); | ||
| 56 | let styT = sty(dragDom, 'top'); | ||
| 57 | |||
| 58 | // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px | ||
| 59 | if (styL.includes('%')) { | ||
| 60 | styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100); | ||
| 61 | styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100); | ||
| 62 | } else { | ||
| 63 | styL = +styL.replace(/\px/g, ''); | ||
| 64 | styT = +styT.replace(/\px/g, ''); | ||
| 65 | }; | ||
| 66 | |||
| 67 | document.onmousemove = function (e) { | ||
| 68 | // 通过事件委托,计算移动的距离 | ||
| 69 | let left = e.clientX - disX; | ||
| 70 | let top = e.clientY - disY; | ||
| 71 | |||
| 72 | // 边界处理 | ||
| 73 | if (-(left) > minDragDomLeft) { | ||
| 74 | left = -(minDragDomLeft); | ||
| 75 | } else if (left > maxDragDomLeft) { | ||
| 76 | left = maxDragDomLeft; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (-(top) > minDragDomTop) { | ||
| 80 | top = -(minDragDomTop); | ||
| 81 | } else if (top > maxDragDomTop) { | ||
| 82 | top = maxDragDomTop; | ||
| 83 | } | ||
| 84 | |||
| 85 | // 移动当前元素 | ||
| 86 | dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`; | ||
| 87 | }; | ||
| 88 | |||
| 89 | document.onmouseup = function (e) { | ||
| 90 | document.onmousemove = null; | ||
| 91 | document.onmouseup = null; | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | }) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/directive/el-input/index.js
0 → 100644
| 1 | |||
| 2 | import onlyNumber from './onlyNumber' | ||
| 3 | const install = Vue => { | ||
| 4 | Vue.directive('onlyNumber', onlyNumber) | ||
| 5 | } | ||
| 6 | /* | ||
| 7 | Vue.use( plugin ) | ||
| 8 | 安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。 | ||
| 9 | 如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。 | ||
| 10 | 该方法需要在调用 new Vue() 之前被调用。 | ||
| 11 | 当 install 方法被同一个插件多次调用,插件将只会被安装一次。 | ||
| 12 | */ | ||
| 13 | |||
| 14 | if (window.Vue) { | ||
| 15 | window['onlyNumber'] = onlyNumber | ||
| 16 | Vue.use(install); // eslint-disable-line | ||
| 17 | } | ||
| 18 | |||
| 19 | onlyNumber.install = install | ||
| 20 | export default onlyNumber | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/directive/el-input/onlyNumber.js
0 → 100644
| 1 | export default { | ||
| 2 | inserted (el, vDir, vNode) { | ||
| 3 | // vDir.value 有指令的参数 | ||
| 4 | let content; | ||
| 5 | //按键按下=>只允许输入 数字/小数点 | ||
| 6 | el.addEventListener("keypress", event => { | ||
| 7 | let e = event || window.event; | ||
| 8 | let inputKey = String.fromCharCode(typeof e.charCode === 'number' ? e.charCode : e.keyCode); | ||
| 9 | let re = /\d|\./; | ||
| 10 | content = e.target.value; | ||
| 11 | //定义方法,阻止输入 | ||
| 12 | function preventInput () { | ||
| 13 | if (e.preventDefault) { | ||
| 14 | e.preventDefault(); | ||
| 15 | } else { | ||
| 16 | e.returnValue = false; | ||
| 17 | } | ||
| 18 | } | ||
| 19 | if (!re.test(inputKey) && !e.ctrlKey) { | ||
| 20 | preventInput(); | ||
| 21 | } else if (content.indexOf(".") > 0 && inputKey == ".") { | ||
| 22 | //已有小数点,再次输入小数点 | ||
| 23 | preventInput(); | ||
| 24 | } | ||
| 25 | }); | ||
| 26 | //按键弹起=>并限制最大最小 | ||
| 27 | el.addEventListener("keyup", event => { | ||
| 28 | let e = event || window.event; | ||
| 29 | content = parseFloat(e.target.value); | ||
| 30 | let arg_max = ""; | ||
| 31 | let arg_min = ""; | ||
| 32 | if (vDir.value) { | ||
| 33 | arg_max = parseFloat(vDir.value.max); | ||
| 34 | arg_min = parseFloat(vDir.value.min); | ||
| 35 | } | ||
| 36 | if (arg_max && content > arg_max) { | ||
| 37 | e.target.value = arg_max; | ||
| 38 | content = arg_max; | ||
| 39 | } | ||
| 40 | if (arg_min && content < arg_min) { | ||
| 41 | e.target.value = arg_min; | ||
| 42 | content = arg_min; | ||
| 43 | } | ||
| 44 | if (!content) { | ||
| 45 | content = ''; | ||
| 46 | } | ||
| 47 | }); | ||
| 48 | //失去焦点=>保留指定位小数 | ||
| 49 | el.addEventListener("keydown", event => {//此处会在 el-input 的 @change 后执行 | ||
| 50 | let e = event || window.event; | ||
| 51 | let key = e.key | ||
| 52 | if (vDir.value.precision == 0) { | ||
| 53 | // 不允许输入'e'和'.' | ||
| 54 | if (key === 'e' || key === '.') { | ||
| 55 | e.returnValue = false | ||
| 56 | return false | ||
| 57 | } | ||
| 58 | }else { | ||
| 59 | if (e.target.value != '') { | ||
| 60 | let arg_precision = 0;//默认保留至整数 | ||
| 61 | if (vDir.value.precision) { | ||
| 62 | arg_precision = parseFloat(vDir.value.precision); | ||
| 63 | } | ||
| 64 | let reg = new RegExp(`^\\d*(\\.?\\d{0,${arg_precision-1}})`, 'g') | ||
| 65 | e.target.value = (e.target.value.match(reg)[0]) || null | ||
| 66 | } | ||
| 67 | } | ||
| 68 | // content = parseFloat(e.target.value); | ||
| 69 | // if (!content) { | ||
| 70 | // content = 0.00; | ||
| 71 | // } | ||
| 72 | // if (e.target.value != '') { | ||
| 73 | // let arg_precision = 0;//默认保留至整数 | ||
| 74 | // if (vDir.value.precision) { | ||
| 75 | // arg_precision = parseFloat(vDir.value.precision); | ||
| 76 | // } | ||
| 77 | // e.target.value = content.toFixed(arg_precision); | ||
| 78 | // } | ||
| 79 | // -- callback写法1 | ||
| 80 | // vNode.data.model.callback = ()=>{ | ||
| 81 | // e.target.value = content.toFixed(arg_precision) | ||
| 82 | // } | ||
| 83 | // vNode.data.model.callback(); | ||
| 84 | // -- callback 写法2 | ||
| 85 | // vNode.data.model.callback( | ||
| 86 | // e.target.value = content.toFixed(arg_precision) | ||
| 87 | // ) | ||
| 88 | }) | ||
| 89 | } | ||
| 90 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/filters/index.js
0 → 100644
| 1 | import store from '@/store' | ||
| 2 | import dictionaries from '@/api/dictionaries' | ||
| 3 | |||
| 4 | // 证件种类全局过滤器 | ||
| 5 | export function cardTypeFilter (value) { | ||
| 6 | var name | ||
| 7 | if (store.getters.cardTypeOption.length == 0) { | ||
| 8 | dictionaries.getDdicByMC('证件种类').then((res) => { | ||
| 9 | store.dispatch('dictionaries/getCardType', res.result) | ||
| 10 | }) | ||
| 11 | } | ||
| 12 | store.getters.cardTypeOption.map((item) => { | ||
| 13 | if (item.id == value) { | ||
| 14 | name = item.name | ||
| 15 | } | ||
| 16 | }); | ||
| 17 | if (name) { | ||
| 18 | return name | ||
| 19 | } else { | ||
| 20 | return '暂无' | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | // 全部字典 | ||
| 25 | export function stsInfo (value) { | ||
| 26 | if (store.getters.stsInfo.length == 0) { | ||
| 27 | dictionaries.getDictionary().then(res => { | ||
| 28 | store.dispatch('dictionariesAll/getDicData', res.result) | ||
| 29 | }) | ||
| 30 | } | ||
| 31 | } |
src/icons/index.js
0 → 100644
src/icons/svg/hamburg.svg
0 → 100644
| 1 | <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1629181827644" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5380" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M170.666667 156.444444h682.666666a42.666667 42.666667 0 0 1 0 85.333334H170.666667a42.666667 42.666667 0 1 1 0-85.333334zM448.056889 440.888889H853.333333a42.666667 42.666667 0 0 1 0 85.333333H448.056889a42.666667 42.666667 0 0 1 0-85.333333zM170.666667 725.333333h682.666666a42.666667 42.666667 0 1 1 0 85.333334H170.666667a42.666667 42.666667 0 1 1 0-85.333334z" p-id="5381" fill="#686666"></path><path d="M290.304 493.738667L156.501333 560.64a11.377778 11.377778 0 0 1-16.497777-10.183111V416.654222a11.377778 11.377778 0 0 1 16.497777-10.24l133.802667 66.958222a11.377778 11.377778 0 0 1 0 20.366223z" p-id="5382" fill="#686666"></path></svg> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/icons/svg/hamburger.svg
0 → 100644
| 1 | <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1628839541389" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2080" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M408 442h480a8 8 0 0 0 8-8v-56a8 8 0 0 0-8-8H408a8 8 0 0 0-8 8v56a8 8 0 0 0 8 8z m-8 204a8 8 0 0 0 8 8h480a8 8 0 0 0 8-8v-56a8 8 0 0 0-8-8H408a8 8 0 0 0-8 8v56z m504-486H120a8 8 0 0 0-8 8v56a8 8 0 0 0 8 8h784a8 8 0 0 0 8-8v-56a8 8 0 0 0-8-8z m0 632H120a8 8 0 0 0-8 8v56a8 8 0 0 0 8 8h784a8 8 0 0 0 8-8v-56a8 8 0 0 0-8-8zM142.4 642.1L298.7 519a8.8 8.8 0 0 0 0-13.9L142.4 381.9a8.9 8.9 0 0 0-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" p-id="2081" fill="#686666"></path></svg> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/icons/svgo.yml
0 → 100644
src/layout/components/AppMain.vue
0 → 100644
| 1 | <template> | ||
| 2 | <section class="app-main"> | ||
| 3 | <transition name="fade-transform" mode="out-in"> | ||
| 4 | <keep-alive :include="cachedViews"> | ||
| 5 | <router-view :key="key" /> | ||
| 6 | </keep-alive> | ||
| 7 | </transition> | ||
| 8 | </section> | ||
| 9 | </template> | ||
| 10 | <script> | ||
| 11 | export default { | ||
| 12 | name: 'AppMain', | ||
| 13 | computed: { | ||
| 14 | cachedViews () { | ||
| 15 | return this.$store.state.tagsView.cachedViews | ||
| 16 | }, | ||
| 17 | key () { | ||
| 18 | return this.$route.path | ||
| 19 | }, | ||
| 20 | }, | ||
| 21 | } | ||
| 22 | </script> | ||
| 23 | |||
| 24 | <style lang="scss" scoped> | ||
| 25 | .hasTagsView { | ||
| 26 | .app-main { | ||
| 27 | height: calc(100% - 88px); | ||
| 28 | overflow-x: auto; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/layout/components/Navbar.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div class="navbar"> | ||
| 3 | <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" | ||
| 4 | @toggleClick="toggleSideBar" /> | ||
| 5 | <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> | ||
| 6 | <div class="right-menu"> | ||
| 7 | <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click" @command="handleCommand"> | ||
| 8 | <div class="avatar-wrapper"> | ||
| 9 | <span style="padding-right:10px">{{ name }}</span> | ||
| 10 | <img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" /> | ||
| 11 | </div> | ||
| 12 | <el-dropdown-menu slot="dropdown"> | ||
| 13 | <el-dropdown-item command="a">个人中心</el-dropdown-item> | ||
| 14 | <el-dropdown-item command="f">退出登录</el-dropdown-item> | ||
| 15 | </el-dropdown-menu> | ||
| 16 | </el-dropdown> | ||
| 17 | </div> | ||
| 18 | </div> | ||
| 19 | </template> | ||
| 20 | <script> | ||
| 21 | import { mapGetters } from 'vuex' | ||
| 22 | import Breadcrumb from '@/components/Breadcrumb' | ||
| 23 | import Hamburger from '@/components/Hamburger' | ||
| 24 | import { getSession } from '@/utils/session' | ||
| 25 | export default { | ||
| 26 | components: { | ||
| 27 | Breadcrumb, | ||
| 28 | Hamburger | ||
| 29 | }, | ||
| 30 | created () { | ||
| 31 | this.init(); | ||
| 32 | }, | ||
| 33 | computed: { | ||
| 34 | ...mapGetters(['sidebar', 'avatar', 'name']) | ||
| 35 | }, | ||
| 36 | methods: { | ||
| 37 | init () { | ||
| 38 | let userInfo = getSession('userInfo'); | ||
| 39 | if (userInfo && userInfo.userValid === 0) { | ||
| 40 | this.userValid = 0; | ||
| 41 | } | ||
| 42 | }, | ||
| 43 | toggleSideBar () { | ||
| 44 | this.$store.dispatch('app/toggleSideBar') | ||
| 45 | }, | ||
| 46 | searchMessageCenter () { | ||
| 47 | this.$router.push({ name: 'messagecenter' }) | ||
| 48 | }, | ||
| 49 | handleCommand (command) { | ||
| 50 | if (command == 'a') { | ||
| 51 | //个人中心 | ||
| 52 | this.$router.push({ name: 'personal' }) | ||
| 53 | } else if (command == 'f') { | ||
| 54 | // 退出 | ||
| 55 | this.$store.dispatch('user/logout') | ||
| 56 | this.$router.push(`/login?redirect=${this.$route.fullPath}`) | ||
| 57 | window.sessionStorage.clear() | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | </script> | ||
| 63 | <style lang="scss" scoped> | ||
| 64 | .el-dropdown-menu { | ||
| 65 | padding: 0 !important; | ||
| 66 | border: 1px solid #EBEEF5; | ||
| 67 | box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.12); | ||
| 68 | border-radius: 4px 0 0 4px 4px; | ||
| 69 | |||
| 70 | .el-dropdown-menu__item { | ||
| 71 | text-align: center; | ||
| 72 | margin-top: 0 !important; | ||
| 73 | font-size: 14px; | ||
| 74 | font-family: PingFangSC-Regular, PingFang SC; | ||
| 75 | font-weight: 400; | ||
| 76 | color: #4A4A4A; | ||
| 77 | width: 140px; | ||
| 78 | height: 36px; | ||
| 79 | line-height: 36px; | ||
| 80 | } | ||
| 81 | |||
| 82 | .el-dropdown-menu__item:nth-child(6) { | ||
| 83 | border-top: 1px solid #EBEEF5; | ||
| 84 | } | ||
| 85 | |||
| 86 | .popper__arrow { | ||
| 87 | top: -11px !important; | ||
| 88 | left: 110px !important; | ||
| 89 | transform: rotate(0deg) scale(2); | ||
| 90 | } | ||
| 91 | |||
| 92 | .el-dropdown-menu__item:not(.is-disabled):hover, | ||
| 93 | .el-dropdown-menu__item:focus { | ||
| 94 | background: #F6F7F9; | ||
| 95 | color: #4A4A4A; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | .navbar { | ||
| 100 | height: 40px; | ||
| 101 | overflow: hidden; | ||
| 102 | position: relative; | ||
| 103 | background: #fff; | ||
| 104 | box-shadow: 0 1px 0px rgba(0, 21, 41, 0.08); | ||
| 105 | |||
| 106 | .hamburger-container { | ||
| 107 | line-height: 43px; | ||
| 108 | height: 100%; | ||
| 109 | float: left; | ||
| 110 | cursor: pointer; | ||
| 111 | transition: background 0.3s; | ||
| 112 | -webkit-tap-highlight-color: transparent; | ||
| 113 | |||
| 114 | &:hover { | ||
| 115 | background: rgba(0, 0, 0, 0.025); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | .breadcrumb-container { | ||
| 120 | float: left; | ||
| 121 | } | ||
| 122 | |||
| 123 | .right-menu { | ||
| 124 | float: right; | ||
| 125 | height: 100%; | ||
| 126 | line-height: 50px; | ||
| 127 | |||
| 128 | .organization-item { | ||
| 129 | margin-right: 40px; | ||
| 130 | margin-top: -40px !important; | ||
| 131 | } | ||
| 132 | |||
| 133 | .item { | ||
| 134 | margin-right: 40px; | ||
| 135 | margin-top: -20px; | ||
| 136 | line-height: 18.4px; | ||
| 137 | cursor: pointer; | ||
| 138 | position: relative; | ||
| 139 | |||
| 140 | .item-box { | ||
| 141 | position: absolute; | ||
| 142 | top: -5px; | ||
| 143 | left: 3px; | ||
| 144 | width: 100%; | ||
| 145 | min-width: 25px; | ||
| 146 | height: 25px; | ||
| 147 | cursor: pointer; | ||
| 148 | z-index: 100; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | &:focus { | ||
| 153 | outline: none; | ||
| 154 | } | ||
| 155 | |||
| 156 | .right-menu-item { | ||
| 157 | display: inline-block; | ||
| 158 | padding: 0 8px; | ||
| 159 | height: 100%; | ||
| 160 | font-size: 18px; | ||
| 161 | color: #5a5e66; | ||
| 162 | vertical-align: text-bottom; | ||
| 163 | |||
| 164 | &.hover-effect { | ||
| 165 | cursor: pointer; | ||
| 166 | transition: background 0.3s; | ||
| 167 | |||
| 168 | &:hover { | ||
| 169 | background: rgba(0, 0, 0, 0.025); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | .avatar-container { | ||
| 175 | margin-right: 20px; | ||
| 176 | |||
| 177 | .avatar-wrapper { | ||
| 178 | position: relative; | ||
| 179 | display: flex; | ||
| 180 | height: 40px; | ||
| 181 | align-items: center; | ||
| 182 | |||
| 183 | .user-avatar { | ||
| 184 | cursor: pointer; | ||
| 185 | width: 35px; | ||
| 186 | height: 35px; | ||
| 187 | border-radius: 50%; | ||
| 188 | } | ||
| 189 | |||
| 190 | .el-icon-caret-bottom { | ||
| 191 | cursor: pointer; | ||
| 192 | position: absolute; | ||
| 193 | right: -15px; | ||
| 194 | top: 17px; | ||
| 195 | font-size: 12px; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | /*2.隐藏滚动条,太丑了*/ | ||
| 202 | .el-drawer__container ::-webkit-scrollbar { | ||
| 203 | display: none; | ||
| 204 | |||
| 205 | } | ||
| 206 | |||
| 207 | .el-form-item--small .el-form-item__label { | ||
| 208 | width: 80px !important; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | </style> |
src/layout/components/Sidebar/FixiOSBug.js
0 → 100644
| 1 | export default { | ||
| 2 | computed: { | ||
| 3 | device() { | ||
| 4 | return this.$store.state.app.device | ||
| 5 | } | ||
| 6 | }, | ||
| 7 | mounted() { | ||
| 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug | ||
| 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 | ||
| 10 | this.fixBugIniOS() | ||
| 11 | }, | ||
| 12 | methods: { | ||
| 13 | fixBugIniOS() { | ||
| 14 | const $subMenu = this.$refs.subMenu | ||
| 15 | if ($subMenu) { | ||
| 16 | const handleMouseleave = $subMenu.handleMouseleave | ||
| 17 | $subMenu.handleMouseleave = (e) => { | ||
| 18 | if (this.device === 'mobile') { | ||
| 19 | return | ||
| 20 | } | ||
| 21 | handleMouseleave(e) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } |
src/layout/components/Sidebar/Item.vue
0 → 100644
| 1 | <script> | ||
| 2 | export default { | ||
| 3 | name: 'MenuItem', | ||
| 4 | functional: true, | ||
| 5 | props: { | ||
| 6 | icon: { | ||
| 7 | type: String, | ||
| 8 | default: '' | ||
| 9 | }, | ||
| 10 | title: { | ||
| 11 | type: String, | ||
| 12 | default: '' | ||
| 13 | } | ||
| 14 | }, | ||
| 15 | render(h, context) { | ||
| 16 | const { icon, title } = context.props | ||
| 17 | const vnodes = [] | ||
| 18 | |||
| 19 | if (icon) { | ||
| 20 | if (icon.includes('el-icon')) { | ||
| 21 | vnodes.push(<i class={[icon, 'sub-el-icon']} />) | ||
| 22 | } else { | ||
| 23 | vnodes.push(<svg-icon icon-class={icon}/>) | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | if (title) { | ||
| 28 | vnodes.push(<span slot='title'>{(title)}</span>) | ||
| 29 | } | ||
| 30 | return vnodes | ||
| 31 | } | ||
| 32 | } | ||
| 33 | </script> | ||
| 34 | |||
| 35 | <style scoped> | ||
| 36 | .sub-el-icon { | ||
| 37 | color: currentColor; | ||
| 38 | width: 1em; | ||
| 39 | height: 1em; | ||
| 40 | } | ||
| 41 | </style> |
src/layout/components/Sidebar/Link.vue
0 → 100644
| 1 | <template> | ||
| 2 | <component :is="type" v-bind="linkProps(to)"> | ||
| 3 | <slot /> | ||
| 4 | </component> | ||
| 5 | </template> | ||
| 6 | |||
| 7 | <script> | ||
| 8 | import { isExternal } from '@/utils/validate' | ||
| 9 | |||
| 10 | export default { | ||
| 11 | props: { | ||
| 12 | to: { | ||
| 13 | type: String, | ||
| 14 | required: true | ||
| 15 | } | ||
| 16 | }, | ||
| 17 | computed: { | ||
| 18 | isExternal() { | ||
| 19 | return isExternal(this.to) | ||
| 20 | }, | ||
| 21 | type() { | ||
| 22 | if (this.isExternal) { | ||
| 23 | return 'a' | ||
| 24 | } | ||
| 25 | return 'router-link' | ||
| 26 | } | ||
| 27 | }, | ||
| 28 | methods: { | ||
| 29 | linkProps(to) { | ||
| 30 | if (this.isExternal) { | ||
| 31 | return { | ||
| 32 | href: to, | ||
| 33 | target: '_blank', | ||
| 34 | rel: 'noopener' | ||
| 35 | } | ||
| 36 | } | ||
| 37 | return { | ||
| 38 | to: to | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | </script> |
src/layout/components/Sidebar/Logo.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div class="sidebar-logo-container" | ||
| 3 | :class="{'collapse':collapse}"> | ||
| 4 | <transition name="sidebarLogoFade"> | ||
| 5 | <router-link v-if="collapse" | ||
| 6 | key="collapse" | ||
| 7 | class="sidebar-logo-link" | ||
| 8 | to="/"> | ||
| 9 | <img v-if="logo" | ||
| 10 | :src="logo" | ||
| 11 | class="sidebar-logo"> | ||
| 12 | <h1 v-else | ||
| 13 | class="sidebar-title"> | ||
| 14 | {{ title }} | ||
| 15 | </h1> | ||
| 16 | </router-link> | ||
| 17 | <router-link v-else | ||
| 18 | key="expand" | ||
| 19 | class="sidebar-logo-link" | ||
| 20 | to="/"> | ||
| 21 | <img v-if="logo" | ||
| 22 | :src="logo" | ||
| 23 | class="sidebar-logo"> | ||
| 24 | <h1 class="sidebar-title"> | ||
| 25 | 厦门市房屋租赁<br>管理服务系统 | ||
| 26 | </h1> | ||
| 27 | </router-link> | ||
| 28 | </transition> | ||
| 29 | </div> | ||
| 30 | </template> | ||
| 31 | |||
| 32 | <script> | ||
| 33 | import logonImg from '@/assets/global_images/logo.png' | ||
| 34 | export default { | ||
| 35 | name: 'SidebarLogo', | ||
| 36 | props: { | ||
| 37 | collapse: { | ||
| 38 | type: Boolean, | ||
| 39 | required: true | ||
| 40 | } | ||
| 41 | }, | ||
| 42 | data() { | ||
| 43 | return { | ||
| 44 | title: '厦门市房屋数据服务平台', | ||
| 45 | logo: logonImg | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | </script> | ||
| 50 | |||
| 51 | <style lang="scss" scoped> | ||
| 52 | .sidebarLogoFade-enter-active { | ||
| 53 | transition: opacity 1.5s; | ||
| 54 | } | ||
| 55 | |||
| 56 | .sidebarLogoFade-enter, | ||
| 57 | .sidebarLogoFade-leave-to { | ||
| 58 | opacity: 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | .sidebar-logo-container { | ||
| 62 | position: relative; | ||
| 63 | width: 100%; | ||
| 64 | text-align: center; | ||
| 65 | overflow: hidden; | ||
| 66 | height: 150px; | ||
| 67 | |||
| 68 | & .sidebar-logo-link { | ||
| 69 | height: 100%; | ||
| 70 | width: 100%; | ||
| 71 | & .sidebar-logo { | ||
| 72 | width: 41px; | ||
| 73 | height: 39px; | ||
| 74 | vertical-align: middle; | ||
| 75 | // margin-left: 47px; | ||
| 76 | // margin-right: 48px; | ||
| 77 | margin-top: 22px; | ||
| 78 | } | ||
| 79 | |||
| 80 | & .sidebar-title { | ||
| 81 | // display: inline-block; | ||
| 82 | margin: 0; | ||
| 83 | margin-top: 10px; | ||
| 84 | margin-bottom: 20px; | ||
| 85 | color: #fff; | ||
| 86 | font-weight: 600; | ||
| 87 | line-height: 25px; | ||
| 88 | font-size: 16px; | ||
| 89 | font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; | ||
| 90 | vertical-align: middle; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | &.collapse { | ||
| 95 | .sidebar-logo { | ||
| 96 | margin-right: 0px; | ||
| 97 | width: 32.8px; | ||
| 98 | height: 31.2px; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | </style> |
| 1 | <template> | ||
| 2 | <div v-if="!item.hidden"> | ||
| 3 | <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> | ||
| 4 | <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> | ||
| 5 | <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> | ||
| 6 | <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" class="menu-icon" /> | ||
| 7 | </el-menu-item> | ||
| 8 | </app-link> | ||
| 9 | </template> | ||
| 10 | |||
| 11 | <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> | ||
| 12 | <template slot="title"> | ||
| 13 | <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> | ||
| 14 | </template> | ||
| 15 | <sidebar-item | ||
| 16 | v-for="child in item.children" | ||
| 17 | :key="child.path" | ||
| 18 | :is-nest="true" | ||
| 19 | :item="child" | ||
| 20 | :base-path="resolvePath(child.path)" | ||
| 21 | class="nest-menu" | ||
| 22 | /> | ||
| 23 | </el-submenu> | ||
| 24 | </div> | ||
| 25 | </template> | ||
| 26 | |||
| 27 | <script> | ||
| 28 | import path from 'path' | ||
| 29 | import { isExternal } from '@/utils/validate' | ||
| 30 | import Item from './Item' | ||
| 31 | import AppLink from './Link' | ||
| 32 | import FixiOSBug from './FixiOSBug' | ||
| 33 | |||
| 34 | export default { | ||
| 35 | name: 'SidebarItem', | ||
| 36 | components: { Item, AppLink }, | ||
| 37 | mixins: [FixiOSBug], | ||
| 38 | props: { | ||
| 39 | // route object | ||
| 40 | item: { | ||
| 41 | type: Object, | ||
| 42 | required: true | ||
| 43 | }, | ||
| 44 | isNest: { | ||
| 45 | type: Boolean, | ||
| 46 | default: false | ||
| 47 | }, | ||
| 48 | basePath: { | ||
| 49 | type: String, | ||
| 50 | default: '' | ||
| 51 | } | ||
| 52 | }, | ||
| 53 | data() { | ||
| 54 | // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 | ||
| 55 | // TODO: refactor with render function | ||
| 56 | this.onlyOneChild = null | ||
| 57 | return {} | ||
| 58 | }, | ||
| 59 | methods: { | ||
| 60 | hasOneShowingChild(children = [], parent) { | ||
| 61 | const showingChildren = children.filter(item => { | ||
| 62 | if (item.hidden) { | ||
| 63 | return false | ||
| 64 | } else { | ||
| 65 | // Temp set(will be used if only has one showing child) | ||
| 66 | this.onlyOneChild = item | ||
| 67 | return true | ||
| 68 | } | ||
| 69 | }) | ||
| 70 | |||
| 71 | // When there is only one child router, the child router is displayed by default | ||
| 72 | if (showingChildren.length === 1) { | ||
| 73 | return true | ||
| 74 | } | ||
| 75 | |||
| 76 | // Show parent if there are no child router to display | ||
| 77 | if (showingChildren.length === 0) { | ||
| 78 | this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } | ||
| 79 | return true | ||
| 80 | } | ||
| 81 | |||
| 82 | return false | ||
| 83 | }, | ||
| 84 | resolvePath(routePath) { | ||
| 85 | if (isExternal(routePath)) { | ||
| 86 | return routePath | ||
| 87 | } | ||
| 88 | if (isExternal(this.basePath)) { | ||
| 89 | return this.basePath | ||
| 90 | } | ||
| 91 | return path.resolve(this.basePath, routePath) | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | </script> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/layout/components/Sidebar/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div :class="{'has-logo':showLogo}"> | ||
| 3 | <logo v-if="showLogo" :collapse="isCollapse" /> | ||
| 4 | <el-scrollbar wrap-class="scrollbar-wrapper"> | ||
| 5 | <el-menu :default-active="activeMenu" :collapse="isCollapse" | ||
| 6 | :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="true" | ||
| 7 | :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="vertical"> | ||
| 8 | <!-- 权限菜单 --> | ||
| 9 | <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" /> | ||
| 10 | <!-- 菜单全部展示 --> | ||
| 11 | <!-- <sidebar-item v-for="route in asyncRoutes" :key="route.path" :item="route" | ||
| 12 | :base-path="route.path" /> --> | ||
| 13 | </el-menu> | ||
| 14 | </el-scrollbar> | ||
| 15 | </div> | ||
| 16 | </template> | ||
| 17 | |||
| 18 | <script> | ||
| 19 | import { mapGetters } from 'vuex' | ||
| 20 | import Logo from './Logo' | ||
| 21 | import SidebarItem from './SidebarItem' | ||
| 22 | import variables from '@/styles/variables.scss' | ||
| 23 | import { asyncRoutes } from '@/router' | ||
| 24 | export default { | ||
| 25 | components: { SidebarItem, Logo }, | ||
| 26 | computed: { | ||
| 27 | ...mapGetters(['permission_routes', 'sidebar']), | ||
| 28 | activeMenu() { | ||
| 29 | const route = this.$route | ||
| 30 | const { meta, path } = route | ||
| 31 | // if set path, the sidebar will highlight the path you set | ||
| 32 | if (meta.activeMenu) { | ||
| 33 | return meta.activeMenu | ||
| 34 | } | ||
| 35 | return path | ||
| 36 | }, | ||
| 37 | showLogo() { | ||
| 38 | return this.$store.state.settings.sidebarLogo | ||
| 39 | }, | ||
| 40 | variables() { | ||
| 41 | return variables | ||
| 42 | }, | ||
| 43 | isCollapse() { | ||
| 44 | return !this.sidebar.opened | ||
| 45 | }, | ||
| 46 | asyncRoutes() { | ||
| 47 | return asyncRoutes | ||
| 48 | }, | ||
| 49 | }, | ||
| 50 | } | ||
| 51 | </script> |
| 1 | <template> | ||
| 2 | <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll"> | ||
| 3 | <slot /> | ||
| 4 | </el-scrollbar> | ||
| 5 | </template> | ||
| 6 | |||
| 7 | <script> | ||
| 8 | const tagAndTagSpacing = 4 // tagAndTagSpacing | ||
| 9 | |||
| 10 | export default { | ||
| 11 | name: 'ScrollPane', | ||
| 12 | data() { | ||
| 13 | return { | ||
| 14 | left: 0 | ||
| 15 | } | ||
| 16 | }, | ||
| 17 | computed: { | ||
| 18 | scrollWrapper() { | ||
| 19 | return this.$refs.scrollContainer.$refs.wrap | ||
| 20 | } | ||
| 21 | }, | ||
| 22 | mounted() { | ||
| 23 | this.scrollWrapper.addEventListener('scroll', this.emitScroll, true) | ||
| 24 | }, | ||
| 25 | beforeDestroy() { | ||
| 26 | this.scrollWrapper.removeEventListener('scroll', this.emitScroll) | ||
| 27 | }, | ||
| 28 | methods: { | ||
| 29 | handleScroll(e) { | ||
| 30 | const eventDelta = e.wheelDelta || -e.deltaY * 40 | ||
| 31 | const $scrollWrapper = this.scrollWrapper | ||
| 32 | $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 | ||
| 33 | }, | ||
| 34 | emitScroll() { | ||
| 35 | this.$emit('scroll') | ||
| 36 | }, | ||
| 37 | moveToTarget(currentTag) { | ||
| 38 | const $container = this.$refs.scrollContainer.$el | ||
| 39 | const $containerWidth = $container.offsetWidth | ||
| 40 | const $scrollWrapper = this.scrollWrapper | ||
| 41 | const tagList = this.$parent.$refs.tag | ||
| 42 | |||
| 43 | let firstTag = null | ||
| 44 | let lastTag = null | ||
| 45 | |||
| 46 | // find first tag and last tag | ||
| 47 | if (tagList.length > 0) { | ||
| 48 | firstTag = tagList[0] | ||
| 49 | lastTag = tagList[tagList.length - 1] | ||
| 50 | } | ||
| 51 | |||
| 52 | if (firstTag === currentTag) { | ||
| 53 | $scrollWrapper.scrollLeft = 0 | ||
| 54 | } else if (lastTag === currentTag) { | ||
| 55 | $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth | ||
| 56 | } else { | ||
| 57 | // find preTag and nextTag | ||
| 58 | const currentIndex = tagList.findIndex(item => item === currentTag) | ||
| 59 | const prevTag = tagList[currentIndex - 1] | ||
| 60 | const nextTag = tagList[currentIndex + 1] | ||
| 61 | |||
| 62 | // the tag's offsetLeft after of nextTag | ||
| 63 | const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing | ||
| 64 | |||
| 65 | // the tag's offsetLeft before of prevTag | ||
| 66 | const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing | ||
| 67 | |||
| 68 | if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { | ||
| 69 | $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth | ||
| 70 | } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { | ||
| 71 | $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | </script> | ||
| 78 | |||
| 79 | <style lang="scss" scoped> | ||
| 80 | .scroll-container { | ||
| 81 | white-space: nowrap; | ||
| 82 | position: relative; | ||
| 83 | overflow: hidden; | ||
| 84 | width: 100%; | ||
| 85 | ::v-deep { | ||
| 86 | .el-scrollbar__bar { | ||
| 87 | bottom: 0px; | ||
| 88 | width: 0; | ||
| 89 | } | ||
| 90 | .el-scrollbar__wrap { | ||
| 91 | height: 48px; | ||
| 92 | line-height: 48px; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | </style> |
src/layout/components/TagsView/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div id="tags-view-container" class="tags-view-container"> | ||
| 3 | <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll"> | ||
| 4 | <p class="pane-mask-left"></p> | ||
| 5 | <router-link v-for="tag in visitedViews" ref="tag" :key="tag.path" :class="isActive(tag) ? 'active' : ''" | ||
| 6 | :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" tag="span" class="tags-view-item" | ||
| 7 | @click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''" | ||
| 8 | @contextmenu.prevent.native="openMenu(tag, $event)"> | ||
| 9 | {{ tag.title }} | ||
| 10 | <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> | ||
| 11 | </router-link> | ||
| 12 | <p class="pane-mask-right"></p> | ||
| 13 | </scroll-pane> | ||
| 14 | <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu"> | ||
| 15 | <li @click="refreshSelectedTag(selectedTag)">刷新</li> | ||
| 16 | <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li> | ||
| 17 | <li @click="closeOthersTags">关闭其他</li> | ||
| 18 | <li @click="closeAllTags(selectedTag)">关闭全部</li> | ||
| 19 | </ul> | ||
| 20 | </div> | ||
| 21 | </template> | ||
| 22 | |||
| 23 | <script> | ||
| 24 | import ScrollPane from './ScrollPane' | ||
| 25 | import path from 'path' | ||
| 26 | |||
| 27 | export default { | ||
| 28 | components: { ScrollPane }, | ||
| 29 | data () { | ||
| 30 | return { | ||
| 31 | visible: false, | ||
| 32 | top: 0, | ||
| 33 | left: 0, | ||
| 34 | selectedTag: {}, | ||
| 35 | affixTags: [] | ||
| 36 | } | ||
| 37 | }, | ||
| 38 | computed: { | ||
| 39 | visitedViews () { | ||
| 40 | return this.$store.state.tagsView.visitedViews | ||
| 41 | }, | ||
| 42 | routes () { | ||
| 43 | return this.$store.state.permission.routes | ||
| 44 | } | ||
| 45 | }, | ||
| 46 | watch: { | ||
| 47 | $route () { | ||
| 48 | this.addTags() | ||
| 49 | this.moveToCurrentTag() | ||
| 50 | }, | ||
| 51 | visible (value) { | ||
| 52 | if (value) { | ||
| 53 | document.body.addEventListener('click', this.closeMenu) | ||
| 54 | } else { | ||
| 55 | document.body.removeEventListener('click', this.closeMenu) | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }, | ||
| 59 | mounted () { | ||
| 60 | this.initTags() | ||
| 61 | this.addTags() | ||
| 62 | }, | ||
| 63 | methods: { | ||
| 64 | isActive (route) { | ||
| 65 | return route.path === this.$route.path | ||
| 66 | }, | ||
| 67 | isAffix (tag) { | ||
| 68 | return tag.meta && tag.meta.affix | ||
| 69 | }, | ||
| 70 | filterAffixTags (routes, basePath = '/') { | ||
| 71 | let tags = [] | ||
| 72 | routes.forEach(route => { | ||
| 73 | if (route.meta && route.meta.affix) { | ||
| 74 | const tagPath = path.resolve(basePath, route.path) | ||
| 75 | tags.push({ | ||
| 76 | fullPath: tagPath, | ||
| 77 | path: tagPath, | ||
| 78 | name: route.name, | ||
| 79 | meta: { ...route.meta } | ||
| 80 | }) | ||
| 81 | } | ||
| 82 | if (route.children) { | ||
| 83 | const tempTags = this.filterAffixTags(route.children, route.path) | ||
| 84 | if (tempTags.length >= 1) { | ||
| 85 | tags = [...tags, ...tempTags] | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }) | ||
| 89 | return tags | ||
| 90 | }, | ||
| 91 | initTags () { | ||
| 92 | const affixTags = (this.affixTags = this.filterAffixTags(this.routes)) | ||
| 93 | for (const tag of affixTags) { | ||
| 94 | // Must have tag name | ||
| 95 | if (tag.name) { | ||
| 96 | this.$store.dispatch('tagsView/addVisitedView', tag) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | }, | ||
| 100 | addTags () { | ||
| 101 | const { name } = this.$route | ||
| 102 | if (name) { | ||
| 103 | this.$store.dispatch('tagsView/addView', this.$route) | ||
| 104 | console.log(this.visitedViews, name); | ||
| 105 | } | ||
| 106 | return false | ||
| 107 | }, | ||
| 108 | moveToCurrentTag () { | ||
| 109 | const tags = this.$refs.tag | ||
| 110 | this.$nextTick(() => { | ||
| 111 | for (const tag of tags) { | ||
| 112 | if (tag.to.path === this.$route.path) { | ||
| 113 | this.$refs.scrollPane.moveToTarget(tag) | ||
| 114 | // when query is different then update | ||
| 115 | if (tag.to.fullPath !== this.$route.fullPath) { | ||
| 116 | this.$store.dispatch('tagsView/updateVisitedView', this.$route) | ||
| 117 | } | ||
| 118 | break | ||
| 119 | } | ||
| 120 | } | ||
| 121 | }) | ||
| 122 | }, | ||
| 123 | refreshSelectedTag (view) { | ||
| 124 | this.$store.dispatch('tagsView/delCachedView', view).then(() => { | ||
| 125 | const { fullPath } = view | ||
| 126 | this.$nextTick(() => { | ||
| 127 | this.$router.replace({ | ||
| 128 | path: '/redirect' + fullPath | ||
| 129 | }) | ||
| 130 | }) | ||
| 131 | }) | ||
| 132 | }, | ||
| 133 | closeSelectedTag (view) { | ||
| 134 | this.$store | ||
| 135 | .dispatch('tagsView/delView', view) | ||
| 136 | .then(({ visitedViews }) => { | ||
| 137 | if (this.isActive(view)) { | ||
| 138 | this.toLastView(visitedViews, view) | ||
| 139 | } | ||
| 140 | }) | ||
| 141 | }, | ||
| 142 | closeOthersTags () { | ||
| 143 | this.$router.push(this.selectedTag) | ||
| 144 | this.$store | ||
| 145 | .dispatch('tagsView/delOthersViews', this.selectedTag) | ||
| 146 | .then(() => { | ||
| 147 | this.moveToCurrentTag() | ||
| 148 | }) | ||
| 149 | }, | ||
| 150 | closeAllTags (view) { | ||
| 151 | this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { | ||
| 152 | if (this.affixTags.some(tag => tag.path === view.path)) { | ||
| 153 | return | ||
| 154 | } | ||
| 155 | this.toLastView(visitedViews, view) | ||
| 156 | }) | ||
| 157 | }, | ||
| 158 | toLastView (visitedViews, view) { | ||
| 159 | const latestView = visitedViews.slice(-1)[0] | ||
| 160 | if (latestView) { | ||
| 161 | this.$router.push(latestView.fullPath) | ||
| 162 | } else { | ||
| 163 | // now the default is to redirect to the home page if there is no tags-view, | ||
| 164 | // you can adjust it according to your needs. | ||
| 165 | if (view.name === 'Dashboard') { | ||
| 166 | // to reload home page | ||
| 167 | this.$router.replace({ path: '/redirect' + view.fullPath }) | ||
| 168 | } else { | ||
| 169 | this.$router.push('/') | ||
| 170 | } | ||
| 171 | } | ||
| 172 | }, | ||
| 173 | openMenu (tag, e) { | ||
| 174 | const menuMinWidth = 95 | ||
| 175 | const offsetLeft = this.$el.getBoundingClientRect().left // container margin left | ||
| 176 | const offsetWidth = this.$el.offsetWidth // container width | ||
| 177 | const maxLeft = offsetWidth - menuMinWidth // left boundary | ||
| 178 | const left = e.clientX - offsetLeft + 210 // 15: margin right | ||
| 179 | |||
| 180 | if (left > maxLeft) { | ||
| 181 | this.left = maxLeft | ||
| 182 | } else { | ||
| 183 | this.left = left | ||
| 184 | } | ||
| 185 | |||
| 186 | this.top = e.clientY | ||
| 187 | this.visible = true | ||
| 188 | this.selectedTag = tag | ||
| 189 | }, | ||
| 190 | closeMenu () { | ||
| 191 | this.visible = false | ||
| 192 | }, | ||
| 193 | handleScroll () { | ||
| 194 | this.closeMenu() | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | </script> | ||
| 199 | |||
| 200 | <style lang="scss" scoped> | ||
| 201 | .tags-view-container { | ||
| 202 | height: 48px; | ||
| 203 | line-height: 48px; | ||
| 204 | width: 100%; | ||
| 205 | background: #fff; | ||
| 206 | border-bottom: 1px solid #E4EBF4; | ||
| 207 | box-shadow: 0 1px 3px 0 rgba(147, 173, 209, 0.12); | ||
| 208 | |||
| 209 | .tags-view-wrapper { | ||
| 210 | .el-scrollbar__view { | ||
| 211 | position: relative; | ||
| 212 | |||
| 213 | .pane-mask-left, | ||
| 214 | .pane-mask-right { | ||
| 215 | position: absolute; | ||
| 216 | top: 8px; | ||
| 217 | height: 33px; | ||
| 218 | z-index: 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | .pane-mask-left { | ||
| 222 | width: 15px; | ||
| 223 | left: 0; | ||
| 224 | background-image: linear-gradient(270deg, rgba(255, 255, 255, 0.00) 20%, #FFFFFF 33%); | ||
| 225 | } | ||
| 226 | |||
| 227 | .pane-mask-right { | ||
| 228 | width: 30px; | ||
| 229 | right: 0; | ||
| 230 | background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.00) 5%, #FFFFFF 30%); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | .tags-view-item { | ||
| 235 | display: inline-block; | ||
| 236 | position: relative; | ||
| 237 | cursor: pointer; | ||
| 238 | height: 31px; | ||
| 239 | line-height: 29px; | ||
| 240 | border: 1px solid #e4ebf4; | ||
| 241 | color: #686666; | ||
| 242 | background: #fff; | ||
| 243 | padding: 0 10px 0 10px; | ||
| 244 | font-size: 16px; | ||
| 245 | margin-top: 5px; | ||
| 246 | border-radius: 4px; | ||
| 247 | |||
| 248 | &:first-of-type { | ||
| 249 | margin-left: 15px; | ||
| 250 | } | ||
| 251 | |||
| 252 | &:last-of-type { | ||
| 253 | margin-right: 15px; | ||
| 254 | } | ||
| 255 | |||
| 256 | &.active { | ||
| 257 | border-color: #0f93f6; | ||
| 258 | color: #4a4a4a; | ||
| 259 | // &::before { | ||
| 260 | // content: ''; | ||
| 261 | // background: #0F93F6; | ||
| 262 | // display: inline-block; | ||
| 263 | // width: 8px; | ||
| 264 | // height: 8px; | ||
| 265 | // border-radius: 50%; | ||
| 266 | // position: relative; | ||
| 267 | // margin-right: 2px; | ||
| 268 | // } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | .contextmenu { | ||
| 274 | margin: 0; | ||
| 275 | background: #fff; | ||
| 276 | z-index: 3000; | ||
| 277 | position: absolute; | ||
| 278 | list-style-type: none; | ||
| 279 | padding: 5px 0; | ||
| 280 | font-size: 12px; | ||
| 281 | font-weight: 400; | ||
| 282 | color: #333; | ||
| 283 | border: 1px solid #EBEEF5; | ||
| 284 | box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.12); | ||
| 285 | border-radius: 4px 0 0 4px 4px; | ||
| 286 | |||
| 287 | li { | ||
| 288 | height: 29px; | ||
| 289 | line-height: 29px; | ||
| 290 | margin: 0; | ||
| 291 | padding: 0px 16px; | ||
| 292 | cursor: pointer; | ||
| 293 | |||
| 294 | &:hover { | ||
| 295 | background: #F6F7F9; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | </style> | ||
| 301 | |||
| 302 | <style lang="scss"> | ||
| 303 | //reset element css of el-icon-close | ||
| 304 | .tags-view-wrapper { | ||
| 305 | .tags-view-item { | ||
| 306 | text-align: justify; | ||
| 307 | margin-right: 8px; | ||
| 308 | |||
| 309 | .el-icon-close { | ||
| 310 | height: 16px; | ||
| 311 | width: 16px; | ||
| 312 | position: relative; | ||
| 313 | border-radius: 50%; | ||
| 314 | text-align: center; | ||
| 315 | line-height: 16px; | ||
| 316 | transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
| 317 | transform-origin: 100% 50%; | ||
| 318 | color: #686666; | ||
| 319 | font-size: 14px; | ||
| 320 | |||
| 321 | &:before { | ||
| 322 | transform: scale(0.8); | ||
| 323 | display: inline-block; | ||
| 324 | } | ||
| 325 | |||
| 326 | &:hover { | ||
| 327 | background-color: #b4bccc; | ||
| 328 | color: #fff; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | </style> |
src/layout/components/index.js
0 → 100644
src/layout/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div :class="classObj" class="app-wrapper"> | ||
| 3 | <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" /> | ||
| 4 | <sidebar class="sidebar-container" /> | ||
| 5 | <div :class="{ hasTagsView: needTagsView }" class="main-container"> | ||
| 6 | <div :class="{ 'fixed-header': fixedHeader }"> | ||
| 7 | <navbar /> | ||
| 8 | <tags-view v-if="needTagsView" /> | ||
| 9 | </div> | ||
| 10 | <app-main /> | ||
| 11 | </div> | ||
| 12 | </div> | ||
| 13 | </template> | ||
| 14 | <script> | ||
| 15 | // import RightPanel from '@/components/RightPanel' | ||
| 16 | import { AppMain, Navbar, Sidebar, TagsView } from './components' | ||
| 17 | import ResizeMixin from './mixin/ResizeHandler' | ||
| 18 | import { mapState } from 'vuex' | ||
| 19 | export default { | ||
| 20 | name: 'Layout', | ||
| 21 | components: { | ||
| 22 | AppMain, | ||
| 23 | Navbar, | ||
| 24 | Sidebar, | ||
| 25 | TagsView | ||
| 26 | }, | ||
| 27 | mixins: [ResizeMixin], | ||
| 28 | computed: { | ||
| 29 | ...mapState({ | ||
| 30 | sidebar: state => state.app.sidebar, | ||
| 31 | device: state => state.app.device, | ||
| 32 | needTagsView: state => state.settings.tagsView, | ||
| 33 | fixedHeader: state => state.settings.fixedHeader | ||
| 34 | }), | ||
| 35 | classObj () { | ||
| 36 | return { | ||
| 37 | hideSidebar: !this.sidebar.opened, | ||
| 38 | openSidebar: this.sidebar.opened, | ||
| 39 | withoutAnimation: this.sidebar.withoutAnimation, | ||
| 40 | mobile: this.device === 'mobile' | ||
| 41 | } | ||
| 42 | } | ||
| 43 | }, | ||
| 44 | methods: { | ||
| 45 | handleClickOutside () { | ||
| 46 | this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | </script> | ||
| 51 | <style lang="scss" scoped> | ||
| 52 | @import "~@/styles/mixin.scss"; | ||
| 53 | @import "~@/styles/variables.scss"; | ||
| 54 | |||
| 55 | .app-wrapper { | ||
| 56 | @include clearfix; | ||
| 57 | position: relative; | ||
| 58 | height: 100%; | ||
| 59 | width: 100%; | ||
| 60 | |||
| 61 | &.mobile.openSidebar { | ||
| 62 | position: fixed; | ||
| 63 | top: 0; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | .drawer-bg { | ||
| 68 | background: #000; | ||
| 69 | opacity: 0.3; | ||
| 70 | width: 100%; | ||
| 71 | top: 0; | ||
| 72 | height: 100%; | ||
| 73 | position: absolute; | ||
| 74 | z-index: 999; | ||
| 75 | } | ||
| 76 | |||
| 77 | .fixed-header { | ||
| 78 | width: 100%; | ||
| 79 | transition: width 0.28s; | ||
| 80 | } | ||
| 81 | |||
| 82 | .el-dropdown-menu--small { | ||
| 83 | padding: 0; | ||
| 84 | width: 5px; | ||
| 85 | } | ||
| 86 | </style> |
src/layout/mixin/ResizeHandler.js
0 → 100644
| 1 | import store from '@/store' | ||
| 2 | |||
| 3 | const { body } = document | ||
| 4 | const WIDTH = 992 // refer to Bootstrap's responsive design | ||
| 5 | |||
| 6 | export default { | ||
| 7 | watch: { | ||
| 8 | $route(route) { | ||
| 9 | if (this.device === 'mobile' && this.sidebar.opened) { | ||
| 10 | store.dispatch('app/closeSideBar', { withoutAnimation: false }) | ||
| 11 | } | ||
| 12 | } | ||
| 13 | }, | ||
| 14 | beforeMount() { | ||
| 15 | window.addEventListener('resize', this.$_resizeHandler) | ||
| 16 | }, | ||
| 17 | beforeDestroy() { | ||
| 18 | window.removeEventListener('resize', this.$_resizeHandler) | ||
| 19 | }, | ||
| 20 | mounted() { | ||
| 21 | const isMobile = this.$_isMobile() | ||
| 22 | if (isMobile) { | ||
| 23 | store.dispatch('app/toggleDevice', 'mobile') | ||
| 24 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) | ||
| 25 | } | ||
| 26 | }, | ||
| 27 | methods: { | ||
| 28 | // use $_ for mixins properties | ||
| 29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential | ||
| 30 | $_isMobile() { | ||
| 31 | const rect = body.getBoundingClientRect() | ||
| 32 | return rect.width - 1 < WIDTH | ||
| 33 | }, | ||
| 34 | $_resizeHandler() { | ||
| 35 | if (!document.hidden) { | ||
| 36 | const isMobile = this.$_isMobile() | ||
| 37 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') | ||
| 38 | |||
| 39 | if (isMobile) { | ||
| 40 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } |
src/main.js
0 → 100644
| 1 | |||
| 2 | import 'babel-polyfill' | ||
| 3 | import Vue from 'vue' | ||
| 4 | import App from './App' | ||
| 5 | import 'normalize.css/normalize.css' // a modern alternative to CSS resets | ||
| 6 | import './directive/dragDialog'; | ||
| 7 | import Element from 'element-ui' | ||
| 8 | import './styles/element-variables.scss' | ||
| 9 | import '@/styles/index.scss' // global css | ||
| 10 | |||
| 11 | import onlyNumber from '@/directive/el-input'; //添加此行=>自定义全局指令 | ||
| 12 | |||
| 13 | import lbTable from './components/lb-table/index'; | ||
| 14 | |||
| 15 | import store from './store' | ||
| 16 | import router from './router' | ||
| 17 | import _ from 'lodash' | ||
| 18 | |||
| 19 | import './icons' // icon | ||
| 20 | import './permission' // permission control | ||
| 21 | import * as filters from './filters' // global filters | ||
| 22 | Vue.use(lbTable); | ||
| 23 | |||
| 24 | Vue.use(onlyNumber); | ||
| 25 | Vue.use(Element, { | ||
| 26 | size: 'small' // set element-ui default size | ||
| 27 | }) | ||
| 28 | Object.keys(filters).forEach(key => { | ||
| 29 | Vue.filter(key, filters[key]) | ||
| 30 | }) | ||
| 31 | Vue.config.productionTip = false | ||
| 32 | new Vue({ | ||
| 33 | el: '#app', | ||
| 34 | router, | ||
| 35 | store, | ||
| 36 | render: h => h(App) | ||
| 37 | }) |
src/permission.js
0 → 100644
| 1 | import router from './router' | ||
| 2 | import store from './store' | ||
| 3 | import { getMenuInfo } from '@/api/user' | ||
| 4 | import NProgress from 'nprogress' // progress bar | ||
| 5 | import 'nprogress/nprogress.css' // progress bar style | ||
| 6 | import getPageTitle from '@/utils/get-page-title' | ||
| 7 | import Cookies from 'js-cookie' | ||
| 8 | |||
| 9 | NProgress.configure({ showSpinner: false }) // NProgress Configuration | ||
| 10 | const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist | ||
| 11 | |||
| 12 | router.beforeEach(async (to, from, next) => { | ||
| 13 | NProgress.start() | ||
| 14 | document.title = getPageTitle(to.meta.title) | ||
| 15 | const hasInfo = Cookies.get("userInfo") | ||
| 16 | if (hasInfo) { | ||
| 17 | if (to.path === '/login') { | ||
| 18 | next({ path: '/' }) | ||
| 19 | NProgress.done() | ||
| 20 | } else { | ||
| 21 | if (whiteList.indexOf(to.path) !== -1) { | ||
| 22 | next() | ||
| 23 | } else { | ||
| 24 | let hasAddRoute = store.state.permission.addRoutes | ||
| 25 | if (hasAddRoute) { | ||
| 26 | next() | ||
| 27 | } else { | ||
| 28 | const { result: getMenuData } = await getMenuInfo() | ||
| 29 | const accessRoutes = await store.dispatch('permission/generateRoutes', getMenuData) | ||
| 30 | router.addRoutes(accessRoutes) | ||
| 31 | // 解决页面刷新报404问题,过滤登录页重复访问 | ||
| 32 | const routeTo = Cookies.get('routerTo') | ||
| 33 | if (routeTo.indexOf('login') > -1) { | ||
| 34 | // 解决登录成功进入404界面 | ||
| 35 | next('/') | ||
| 36 | } else { | ||
| 37 | next(routeTo) | ||
| 38 | } | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } else { | ||
| 43 | if (whiteList.indexOf(to.path) !== -1) { | ||
| 44 | next() | ||
| 45 | } else { | ||
| 46 | // other pages that do not have permission to access are redirected to the login page. | ||
| 47 | next(`/login?redirect=${to.path}`) | ||
| 48 | NProgress.done() | ||
| 49 | } | ||
| 50 | } | ||
| 51 | }) | ||
| 52 | router.afterEach(to => { | ||
| 53 | // 解决刷新页面报404问题 | ||
| 54 | sessionStorage.setItem('routerTo', to.fullPath) | ||
| 55 | Cookies.set("routerTo", to.fullPath) | ||
| 56 | NProgress.done() | ||
| 57 | }) |
src/router/index.js
0 → 100644
| 1 | import Vue from 'vue' | ||
| 2 | import Router from 'vue-router' | ||
| 3 | |||
| 4 | Vue.use(Router) | ||
| 5 | /* Layout */ | ||
| 6 | import Layout from '@/layout' | ||
| 7 | |||
| 8 | /* Router Modules */ | ||
| 9 | // import componentsRouter from './modules/components' | ||
| 10 | |||
| 11 | export const constantRoutes = [ | ||
| 12 | { | ||
| 13 | path: '/redirect', | ||
| 14 | component: Layout, | ||
| 15 | hidden: true, | ||
| 16 | children: [ | ||
| 17 | { | ||
| 18 | path: '/redirect/:path(.*)', | ||
| 19 | component: () => import('@/views/redirect/index') | ||
| 20 | } | ||
| 21 | ] | ||
| 22 | }, | ||
| 23 | { | ||
| 24 | path: '/login', | ||
| 25 | component: () => import('@/views/login/login'), | ||
| 26 | hidden: true | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | path: '/auth-redirect', | ||
| 30 | component: () => import('@/views/login/auth-redirect'), | ||
| 31 | hidden: true | ||
| 32 | }, | ||
| 33 | { | ||
| 34 | path: '/404', | ||
| 35 | component: () => import('@/views/error-page/404'), | ||
| 36 | hidden: true | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | path: '/401', | ||
| 40 | component: () => import('@/views/error-page/401'), | ||
| 41 | hidden: true | ||
| 42 | }, | ||
| 43 | { | ||
| 44 | path: '/', | ||
| 45 | component: Layout, | ||
| 46 | redirect: '/home', | ||
| 47 | hidden: true, | ||
| 48 | children: [ | ||
| 49 | { | ||
| 50 | path: 'home', | ||
| 51 | component: () => import('@/views/home/index'), | ||
| 52 | name: 'Dashboard', | ||
| 53 | meta: { title: '工作台', icon: 'dashboard', affix: true } | ||
| 54 | } | ||
| 55 | ] | ||
| 56 | }, | ||
| 57 | // 404 page must be placed at the end !!! | ||
| 58 | { path: '*', redirect: '/404', hidden: true } | ||
| 59 | ] | ||
| 60 | |||
| 61 | /** | ||
| 62 | * asyncRoutes | ||
| 63 | * the routes that need to be dynamically loaded based on user roles | ||
| 64 | */ | ||
| 65 | export const asyncRoutes = [ | ||
| 66 | { | ||
| 67 | path: '/housedev', | ||
| 68 | id: '3', | ||
| 69 | parentId: null, | ||
| 70 | component: Layout, | ||
| 71 | meta: { title: '房源开发', icon: 'fykf' }, | ||
| 72 | redirect: '/housedev/yzfyxsgl', | ||
| 73 | alwaysShow: true, | ||
| 74 | name: 'housedev', | ||
| 75 | children: [ | ||
| 76 | { | ||
| 77 | path: 'yzfyxsgl', | ||
| 78 | id: '4', | ||
| 79 | parentId: '3', | ||
| 80 | component: () => import('@/views/housedev/yzfyxsgl/index.vue'), | ||
| 81 | name: 'yzfyxsgl', | ||
| 82 | meta: { title: '业主房源线索' } | ||
| 83 | } | ||
| 84 | ] | ||
| 85 | } | ||
| 86 | ] | ||
| 87 | |||
| 88 | const createRouter = () => | ||
| 89 | new Router({ | ||
| 90 | scrollBehavior: () => ({ y: 0 }), | ||
| 91 | routes: [...constantRoutes] | ||
| 92 | }) | ||
| 93 | |||
| 94 | const router = createRouter() | ||
| 95 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 | ||
| 96 | export function resetRouter () { | ||
| 97 | const newRouter = createRouter() | ||
| 98 | router.matcher = newRouter.matcher // reset router | ||
| 99 | } | ||
| 100 | |||
| 101 | export default router |
src/settings.js
0 → 100644
| 1 | module.exports = { | ||
| 2 | title: '管理系统', | ||
| 3 | /** | ||
| 4 | * @type {boolean} true | false | ||
| 5 | * @description Whether show the settings right-panel | ||
| 6 | */ | ||
| 7 | showSettings: true, | ||
| 8 | |||
| 9 | /** | ||
| 10 | * @type {boolean} true | false | ||
| 11 | * @description Whether need tagsView | ||
| 12 | */ | ||
| 13 | tagsView: true, | ||
| 14 | |||
| 15 | /** | ||
| 16 | * @type {boolean} true | false | ||
| 17 | * @description Whether fix the header | ||
| 18 | */ | ||
| 19 | fixedHeader: true, | ||
| 20 | |||
| 21 | /** | ||
| 22 | * @type {boolean} true | false | ||
| 23 | * @description Whether show the logo in sidebar | ||
| 24 | */ | ||
| 25 | sidebarLogo: true, | ||
| 26 | |||
| 27 | /** | ||
| 28 | * @type {string | array} 'production' | ['production', 'development'] | ||
| 29 | * @description Need show err logs component. | ||
| 30 | * The default is only used in the production env | ||
| 31 | * If you want to also use it in dev, you can pass ['production', 'development'] | ||
| 32 | */ | ||
| 33 | errorLog: 'production' | ||
| 34 | } |
src/store/getters.js
0 → 100644
| 1 | const getters = { | ||
| 2 | sidebar: state => state.app.sidebar, | ||
| 3 | size: state => state.app.size, | ||
| 4 | visitedViews: state => state.tagsView.visitedViews, | ||
| 5 | cachedViews: state => state.tagsView.cachedViews, | ||
| 6 | token: state => state.user.token, | ||
| 7 | avatar: state => state.user.avatar, | ||
| 8 | name: state => state.user.name, | ||
| 9 | introduction: state => state.user.introduction, | ||
| 10 | permission_routes: state => state.permission.routes, | ||
| 11 | addRoutes: state => state.permission.addRoutes, | ||
| 12 | errorLogs: state => state.errorLog.logs, | ||
| 13 | fdglSaveInfo: state => state.organizationmanage.fdgl, | ||
| 14 | cardTypeOption: state => state.dictionaries.cardTypeOption, | ||
| 15 | xingzuoTypeOption: state => state.dictionaries.xingzuoTypeOption, | ||
| 16 | sysInfo: state => state.dictionariesAll.sysInfo, | ||
| 17 | centralizationData: state => state.housemap.centralizationData, | ||
| 18 | distributedData: state => state.housemap.distributedData, | ||
| 19 | } | ||
| 20 | export default getters |
src/store/index.js
0 → 100644
| 1 | import Vue from 'vue' | ||
| 2 | import Vuex from 'vuex' | ||
| 3 | import getters from './getters' | ||
| 4 | |||
| 5 | Vue.use(Vuex) | ||
| 6 | // https://webpack.js.org/guides/dependency-management/#requirecontext | ||
| 7 | const modulesFiles = require.context('./modules', true, /\.js$/) | ||
| 8 | |||
| 9 | // you do not need `import app from './modules/app'` | ||
| 10 | // it will auto require all vuex module from modules file | ||
| 11 | const modules = modulesFiles.keys().reduce((modules, modulePath) => { | ||
| 12 | // set './app.js' => 'app' | ||
| 13 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') | ||
| 14 | const value = modulesFiles(modulePath) | ||
| 15 | modules[moduleName] = value.default | ||
| 16 | return modules | ||
| 17 | }, {}) | ||
| 18 | |||
| 19 | const store = new Vuex.Store({ | ||
| 20 | modules, | ||
| 21 | getters | ||
| 22 | }) | ||
| 23 | |||
| 24 | export default store |
src/store/modules/app.js
0 → 100644
| 1 | import Cookies from 'js-cookie' | ||
| 2 | |||
| 3 | const state = { | ||
| 4 | sidebar: { | ||
| 5 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, | ||
| 6 | withoutAnimation: false | ||
| 7 | }, | ||
| 8 | device: 'desktop', | ||
| 9 | // size: Cookies.get('size') || 'default' | ||
| 10 | size: 'small' | ||
| 11 | } | ||
| 12 | |||
| 13 | const mutations = { | ||
| 14 | TOGGLE_SIDEBAR: state => { | ||
| 15 | state.sidebar.opened = !state.sidebar.opened | ||
| 16 | state.sidebar.withoutAnimation = false | ||
| 17 | if (state.sidebar.opened) { | ||
| 18 | Cookies.set('sidebarStatus', 1) | ||
| 19 | } else { | ||
| 20 | Cookies.set('sidebarStatus', 0) | ||
| 21 | } | ||
| 22 | }, | ||
| 23 | CLOSE_SIDEBAR: (state, withoutAnimation) => { | ||
| 24 | Cookies.set('sidebarStatus', 0) | ||
| 25 | state.sidebar.opened = false | ||
| 26 | state.sidebar.withoutAnimation = withoutAnimation | ||
| 27 | }, | ||
| 28 | TOGGLE_DEVICE: (state, device) => { | ||
| 29 | state.device = device | ||
| 30 | }, | ||
| 31 | SET_SIZE: (state, size) => { | ||
| 32 | state.size = size | ||
| 33 | Cookies.set('size', size) | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | const actions = { | ||
| 38 | toggleSideBar ({ commit }) { | ||
| 39 | commit('TOGGLE_SIDEBAR') | ||
| 40 | }, | ||
| 41 | closeSideBar ({ commit }, { withoutAnimation }) { | ||
| 42 | commit('CLOSE_SIDEBAR', withoutAnimation) | ||
| 43 | }, | ||
| 44 | toggleDevice ({ commit }, device) { | ||
| 45 | commit('TOGGLE_DEVICE', device) | ||
| 46 | }, | ||
| 47 | setSize ({ commit }, size) { | ||
| 48 | commit('SET_SIZE', size) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | export default { | ||
| 53 | namespaced: true, | ||
| 54 | state, | ||
| 55 | mutations, | ||
| 56 | actions | ||
| 57 | } |
src/store/modules/permission.js
0 → 100644
| 1 | import { asyncRoutes, constantRoutes, resetRouter } from '@/router' | ||
| 2 | |||
| 3 | const state = { | ||
| 4 | routes: [], | ||
| 5 | addRoutes: false, | ||
| 6 | } | ||
| 7 | |||
| 8 | const mutations = { | ||
| 9 | SET_ROUTES: (state, routes) => { | ||
| 10 | state.addRoutes = true | ||
| 11 | state.routes = constantRoutes.concat(routes) | ||
| 12 | }, | ||
| 13 | RESET_ROUTE: (state) => { | ||
| 14 | state.addRoutes = false | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | const actions = { | ||
| 19 | // 添加全部菜单 | ||
| 20 | generateRoutes ({ commit }, getMenuInfo) { | ||
| 21 | return new Promise(resolve => { | ||
| 22 | // 将路由树数据转成数组结构 | ||
| 23 | let arr1 = [] | ||
| 24 | dfs(_.cloneDeep(asyncRoutes), node => arr1.push(node)) | ||
| 25 | _.each(arr1, i => { | ||
| 26 | i.parentId = i.parentId ? i.parentId : null | ||
| 27 | }) | ||
| 28 | //lodash intersectionBy方法取交集,并以参数1的数据返回 | ||
| 29 | let permission_arr = _.intersectionBy(arr1, getMenuInfo, 'id') | ||
| 30 | // 将权限菜单数组转成路由树数据结构 | ||
| 31 | let permission_tree = array2Tree(permission_arr, null) | ||
| 32 | commit('SET_ROUTES', permission_tree) | ||
| 33 | resolve(permission_tree) | ||
| 34 | }) | ||
| 35 | }, | ||
| 36 | // 重置路由 | ||
| 37 | resetRoutes ({ commit }) { | ||
| 38 | commit('RESET_ROUTE') | ||
| 39 | } | ||
| 40 | } | ||
| 41 | // 树转数组 | ||
| 42 | function dfs (root, fVisit) { | ||
| 43 | let stack = Array.isArray(root) ? [...root] : [root]; | ||
| 44 | while (stack.length) { | ||
| 45 | let node = stack.pop(); | ||
| 46 | fVisit && fVisit(node); | ||
| 47 | let children = node.children; | ||
| 48 | if (children && children.length) { | ||
| 49 | for (let i = children.length - 1; i >= 0; i--) stack.push(children[i]); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | // 数组转树 | ||
| 54 | //需要插入父节点id,pid为null或'',就是找root节点,然后root节点再去找自己的子节点 | ||
| 55 | function array2Tree (data, pid) { | ||
| 56 | let res = []; | ||
| 57 | data.forEach(item => { | ||
| 58 | if (item.parentId === pid) { | ||
| 59 | let itemChildren = array2Tree(data, item.id); | ||
| 60 | if (itemChildren.length) item.children = itemChildren; | ||
| 61 | res.push(item); | ||
| 62 | } | ||
| 63 | }); | ||
| 64 | // 菜单数据反转,保持一致 | ||
| 65 | res.reverse() | ||
| 66 | _.each(res, c => { | ||
| 67 | if (c.children && c.children.length > 0) { | ||
| 68 | c.children.reverse() | ||
| 69 | } | ||
| 70 | }) | ||
| 71 | return res; | ||
| 72 | } | ||
| 73 | |||
| 74 | export default { | ||
| 75 | namespaced: true, | ||
| 76 | state, | ||
| 77 | mutations, | ||
| 78 | actions | ||
| 79 | } |
src/store/modules/settings.js
0 → 100644
| 1 | import variables from '@/styles/element-variables.scss' | ||
| 2 | import defaultSettings from '@/settings' | ||
| 3 | |||
| 4 | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings | ||
| 5 | |||
| 6 | const state = { | ||
| 7 | theme: variables.theme, | ||
| 8 | showSettings: showSettings, | ||
| 9 | tagsView: tagsView, | ||
| 10 | fixedHeader: fixedHeader, | ||
| 11 | sidebarLogo: sidebarLogo | ||
| 12 | } | ||
| 13 | |||
| 14 | const mutations = { | ||
| 15 | CHANGE_SETTING: (state, { key, value }) => { | ||
| 16 | if (state.hasOwnProperty(key)) { | ||
| 17 | state[key] = value | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | const actions = { | ||
| 23 | changeSetting ({ commit }, data) { | ||
| 24 | commit('CHANGE_SETTING', data) | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | export default { | ||
| 29 | namespaced: true, | ||
| 30 | state, | ||
| 31 | mutations, | ||
| 32 | actions | ||
| 33 | } | ||
| 34 |
src/store/modules/tagsView.js
0 → 100644
| 1 | const state = { | ||
| 2 | visitedViews: [], | ||
| 3 | cachedViews: ['leaseDetails'] | ||
| 4 | } | ||
| 5 | |||
| 6 | const mutations = { | ||
| 7 | ADD_VISITED_VIEW: (state, view) => { | ||
| 8 | if (state.visitedViews.some(v => v.path === view.path)) return | ||
| 9 | state.visitedViews.push( | ||
| 10 | Object.assign({}, view, { | ||
| 11 | title: view.meta.title || 'no-name' | ||
| 12 | }) | ||
| 13 | ) | ||
| 14 | }, | ||
| 15 | ADD_CACHED_VIEW: (state, view) => { | ||
| 16 | if (state.cachedViews.includes(view.name)) return | ||
| 17 | if (!view.meta.noCache) { | ||
| 18 | state.cachedViews.push(view.name) | ||
| 19 | } | ||
| 20 | }, | ||
| 21 | |||
| 22 | DEL_VISITED_VIEW: (state, view) => { | ||
| 23 | for (const [i, v] of state.visitedViews.entries()) { | ||
| 24 | if (v.path === view.path) { | ||
| 25 | state.visitedViews.splice(i, 1) | ||
| 26 | break | ||
| 27 | } | ||
| 28 | } | ||
| 29 | }, | ||
| 30 | DEL_CACHED_VIEW: (state, view) => { | ||
| 31 | const index = state.cachedViews.indexOf(view.name) | ||
| 32 | index > -1 && state.cachedViews.splice(index, 1) | ||
| 33 | }, | ||
| 34 | |||
| 35 | DEL_OTHERS_VISITED_VIEWS: (state, view) => { | ||
| 36 | state.visitedViews = state.visitedViews.filter(v => { | ||
| 37 | return v.meta.affix || v.path === view.path | ||
| 38 | }) | ||
| 39 | }, | ||
| 40 | DEL_OTHERS_CACHED_VIEWS: (state, view) => { | ||
| 41 | const index = state.cachedViews.indexOf(view.name) | ||
| 42 | if (index > -1) { | ||
| 43 | state.cachedViews = state.cachedViews.slice(index, index + 1) | ||
| 44 | } else { | ||
| 45 | // if index = -1, there is no cached tags | ||
| 46 | state.cachedViews = [] | ||
| 47 | } | ||
| 48 | }, | ||
| 49 | |||
| 50 | DEL_ALL_VISITED_VIEWS: state => { | ||
| 51 | // keep affix tags | ||
| 52 | const affixTags = state.visitedViews.filter(tag => tag.meta.affix) | ||
| 53 | state.visitedViews = affixTags | ||
| 54 | }, | ||
| 55 | DEL_ALL_CACHED_VIEWS: state => { | ||
| 56 | state.cachedViews = [] | ||
| 57 | }, | ||
| 58 | |||
| 59 | UPDATE_VISITED_VIEW: (state, view) => { | ||
| 60 | for (let v of state.visitedViews) { | ||
| 61 | if (v.path === view.path) { | ||
| 62 | v = Object.assign(v, view) | ||
| 63 | break | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | const actions = { | ||
| 70 | addView ({ dispatch }, view) { | ||
| 71 | dispatch('addVisitedView', view) | ||
| 72 | dispatch('addCachedView', view) | ||
| 73 | }, | ||
| 74 | addVisitedView ({ commit }, view) { | ||
| 75 | commit('ADD_VISITED_VIEW', view) | ||
| 76 | }, | ||
| 77 | addCachedView ({ commit }, view) { | ||
| 78 | commit('ADD_CACHED_VIEW', view) | ||
| 79 | }, | ||
| 80 | |||
| 81 | delView ({ dispatch, state }, view) { | ||
| 82 | return new Promise(resolve => { | ||
| 83 | dispatch('delVisitedView', view) | ||
| 84 | dispatch('delCachedView', view) | ||
| 85 | resolve({ | ||
| 86 | visitedViews: [...state.visitedViews], | ||
| 87 | cachedViews: [...state.cachedViews] | ||
| 88 | }) | ||
| 89 | }) | ||
| 90 | }, | ||
| 91 | delVisitedView ({ commit, state }, view) { | ||
| 92 | return new Promise(resolve => { | ||
| 93 | commit('DEL_VISITED_VIEW', view) | ||
| 94 | resolve([...state.visitedViews]) | ||
| 95 | }) | ||
| 96 | }, | ||
| 97 | delCachedView ({ commit, state }, view) { | ||
| 98 | return new Promise(resolve => { | ||
| 99 | commit('DEL_CACHED_VIEW', view) | ||
| 100 | resolve([...state.cachedViews]) | ||
| 101 | }) | ||
| 102 | }, | ||
| 103 | |||
| 104 | delOthersViews ({ dispatch, state }, view) { | ||
| 105 | return new Promise(resolve => { | ||
| 106 | dispatch('delOthersVisitedViews', view) | ||
| 107 | dispatch('delOthersCachedViews', view) | ||
| 108 | resolve({ | ||
| 109 | visitedViews: [...state.visitedViews], | ||
| 110 | cachedViews: [...state.cachedViews] | ||
| 111 | }) | ||
| 112 | }) | ||
| 113 | }, | ||
| 114 | delOthersVisitedViews ({ commit, state }, view) { | ||
| 115 | return new Promise(resolve => { | ||
| 116 | commit('DEL_OTHERS_VISITED_VIEWS', view) | ||
| 117 | resolve([...state.visitedViews]) | ||
| 118 | }) | ||
| 119 | }, | ||
| 120 | delOthersCachedViews ({ commit, state }, view) { | ||
| 121 | return new Promise(resolve => { | ||
| 122 | commit('DEL_OTHERS_CACHED_VIEWS', view) | ||
| 123 | resolve([...state.cachedViews]) | ||
| 124 | }) | ||
| 125 | }, | ||
| 126 | |||
| 127 | delAllViews ({ dispatch, state }, view) { | ||
| 128 | return new Promise(resolve => { | ||
| 129 | dispatch('delAllVisitedViews', view) | ||
| 130 | dispatch('delAllCachedViews', view) | ||
| 131 | resolve({ | ||
| 132 | visitedViews: [...state.visitedViews], | ||
| 133 | cachedViews: [...state.cachedViews] | ||
| 134 | }) | ||
| 135 | }) | ||
| 136 | }, | ||
| 137 | delAllVisitedViews ({ commit, state }) { | ||
| 138 | return new Promise(resolve => { | ||
| 139 | commit('DEL_ALL_VISITED_VIEWS') | ||
| 140 | resolve([...state.visitedViews]) | ||
| 141 | }) | ||
| 142 | }, | ||
| 143 | delAllCachedViews ({ commit, state }) { | ||
| 144 | return new Promise(resolve => { | ||
| 145 | commit('DEL_ALL_CACHED_VIEWS') | ||
| 146 | resolve([...state.cachedViews]) | ||
| 147 | }) | ||
| 148 | }, | ||
| 149 | |||
| 150 | updateVisitedView ({ commit }, view) { | ||
| 151 | commit('UPDATE_VISITED_VIEW', view) | ||
| 152 | } | ||
| 153 | } | ||
| 154 | export default { | ||
| 155 | namespaced: true, | ||
| 156 | state, | ||
| 157 | mutations, | ||
| 158 | actions | ||
| 159 | } |
src/store/modules/user.js
0 → 100644
| 1 | import { login } from '@/api/user' | ||
| 2 | import store from '../index' | ||
| 3 | import router, { resetRouter } from '@/router' | ||
| 4 | import { Message } from "element-ui"; | ||
| 5 | import Cookies from 'js-cookie'; | ||
| 6 | const state = { | ||
| 7 | name: Cookies.get('username'), | ||
| 8 | avatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', | ||
| 9 | introduction: '', | ||
| 10 | userInfo: null | ||
| 11 | } | ||
| 12 | const mutations = { | ||
| 13 | SET_INTRODUCTION: (state, introduction) => { | ||
| 14 | state.introduction = introduction | ||
| 15 | }, | ||
| 16 | SET_NAME: (state, name) => { | ||
| 17 | state.name = name | ||
| 18 | }, | ||
| 19 | SET_AVATAR: (state, avatar) => { | ||
| 20 | state.avatar = avatar | ||
| 21 | }, | ||
| 22 | SET_USERINFO: (state, userInfo) => { | ||
| 23 | state.userInfo = userInfo | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | const actions = { | ||
| 28 | // user login | ||
| 29 | login ({ commit }, userInfo) { | ||
| 30 | const { username, password, code, num, location } = userInfo | ||
| 31 | return new Promise((resolve, reject) => { | ||
| 32 | login({ username: username.trim(), password: password, code: code, location: location }).then(response => { | ||
| 33 | const { result, code, msg } = response | ||
| 34 | if (code === 200) { | ||
| 35 | Cookies.set('token', result.token) | ||
| 36 | commit('SET_USERINFO', result.userInfo) | ||
| 37 | Cookies.set("userInfo", result.userInfo.id, { expires: 720 }) | ||
| 38 | Cookies.set("username", result.userInfo.username, { expires: 720 }) | ||
| 39 | commit('SET_NAME', result.userInfo.username) | ||
| 40 | } else { | ||
| 41 | Message.error(msg); | ||
| 42 | } | ||
| 43 | resolve(response) | ||
| 44 | }).catch(error => { | ||
| 45 | reject(error) | ||
| 46 | }) | ||
| 47 | }) | ||
| 48 | }, | ||
| 49 | // user logout | ||
| 50 | logout ({ commit, state, dispatch }) { | ||
| 51 | return new Promise((resolve, reject) => { | ||
| 52 | // logout(state.token).then(() => { | ||
| 53 | commit('SET_USERINFO', null) | ||
| 54 | Cookies.remove("userInfo") | ||
| 55 | Cookies.remove("routerTo") | ||
| 56 | Cookies.remove('token') | ||
| 57 | resetRouter() | ||
| 58 | store.dispatch('permission/resetRoutes') | ||
| 59 | // reset visited views and cached views | ||
| 60 | // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 | ||
| 61 | dispatch('tagsView/delAllViews', null, { root: true }) | ||
| 62 | resolve() | ||
| 63 | }) | ||
| 64 | }, | ||
| 65 | // remove token | ||
| 66 | resetToken ({ commit }) { | ||
| 67 | return new Promise(resolve => { | ||
| 68 | commit('SET_USERINFO', null) | ||
| 69 | Cookies.remove("userInfo") | ||
| 70 | resolve() | ||
| 71 | }) | ||
| 72 | }, | ||
| 73 | } | ||
| 74 | export default { | ||
| 75 | namespaced: true, | ||
| 76 | state, | ||
| 77 | mutations, | ||
| 78 | actions | ||
| 79 | } |
src/styles/btn.scss
0 → 100644
| 1 | @import './variables.scss'; | ||
| 2 | |||
| 3 | @mixin colorBtn($color) { | ||
| 4 | background: $color; | ||
| 5 | color:#fff; | ||
| 6 | |||
| 7 | &:hover { | ||
| 8 | color: #fff; | ||
| 9 | background-color: $color; | ||
| 10 | |||
| 11 | &:before, | ||
| 12 | &:after { | ||
| 13 | background: $color; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | .blue-btn { | ||
| 19 | @include colorBtn($blue) | ||
| 20 | } | ||
| 21 | |||
| 22 | .light-blue-btn { | ||
| 23 | @include colorBtn($light-blue) | ||
| 24 | } | ||
| 25 | |||
| 26 | .red-btn { | ||
| 27 | @include colorBtn($red) | ||
| 28 | } | ||
| 29 | |||
| 30 | .pink-btn { | ||
| 31 | @include colorBtn($pink) | ||
| 32 | } | ||
| 33 | |||
| 34 | .green-btn { | ||
| 35 | @include colorBtn($green) | ||
| 36 | } | ||
| 37 | |||
| 38 | .tiffany-btn { | ||
| 39 | @include colorBtn($tiffany) | ||
| 40 | } | ||
| 41 | |||
| 42 | .yellow-btn { | ||
| 43 | @include colorBtn($yellow) | ||
| 44 | } | ||
| 45 | |||
| 46 | .pan-btn { | ||
| 47 | font-size: 14px; | ||
| 48 | color: #fff; | ||
| 49 | padding: 14px 36px; | ||
| 50 | border-radius: 8px; | ||
| 51 | border: none; | ||
| 52 | outline: none; | ||
| 53 | transition: 600ms ease all; | ||
| 54 | position: relative; | ||
| 55 | display: inline-block; | ||
| 56 | |||
| 57 | &:hover { | ||
| 58 | background: #fff; | ||
| 59 | |||
| 60 | &:before, | ||
| 61 | &:after { | ||
| 62 | width: 100%; | ||
| 63 | transition: 600ms ease all; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | &:before, | ||
| 68 | &:after { | ||
| 69 | content: ''; | ||
| 70 | position: absolute; | ||
| 71 | top: 0; | ||
| 72 | right: 0; | ||
| 73 | height: 2px; | ||
| 74 | width: 0; | ||
| 75 | transition: 400ms ease all; | ||
| 76 | } | ||
| 77 | |||
| 78 | &::after { | ||
| 79 | right: inherit; | ||
| 80 | top: inherit; | ||
| 81 | left: 0; | ||
| 82 | bottom: 0; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | .custom-button { | ||
| 87 | display: inline-block; | ||
| 88 | line-height: 1; | ||
| 89 | white-space: nowrap; | ||
| 90 | cursor: pointer; | ||
| 91 | background: #fff; | ||
| 92 | color: #fff; | ||
| 93 | -webkit-appearance: none; | ||
| 94 | text-align: center; | ||
| 95 | box-sizing: border-box; | ||
| 96 | outline: 0; | ||
| 97 | margin: 0; | ||
| 98 | padding: 10px 15px; | ||
| 99 | font-size: 14px; | ||
| 100 | border-radius: 4px; | ||
| 101 | } | ||
| 102 | // 搜索框按钮的位置 | ||
| 103 | .btnCol{ | ||
| 104 | box-sizing: border-box; | ||
| 105 | text-align: right; | ||
| 106 | } | ||
| 107 | |||
| 108 | // 默认白色按钮样式 | ||
| 109 | .el-button--default{ | ||
| 110 | background: #FBFCFD; | ||
| 111 | border: 1px solid #E4EBF4; | ||
| 112 | border-radius: 4px; | ||
| 113 | } |
src/styles/dialog.scss
0 → 100644
| 1 | .dialog_title { | ||
| 2 | @flex(); | ||
| 3 | justify-content: space-between; | ||
| 4 | width: 100%; | ||
| 5 | } | ||
| 6 | .el-dialog__header { | ||
| 7 | margin-bottom: 10px; | ||
| 8 | color: #4A4A4A; | ||
| 9 | border-bottom: 1px solid #E4EBF4; | ||
| 10 | } | ||
| 11 | .el-dialog__body { | ||
| 12 | padding-top: 10px; | ||
| 13 | padding-bottom: 0; | ||
| 14 | } | ||
| 15 | .el-dialog__headerbtn .el-dialog__close { | ||
| 16 | color: #6B7A99 !important; | ||
| 17 | position: relative; | ||
| 18 | top: -2px; | ||
| 19 | } | ||
| 20 | .dialog_footer { | ||
| 21 | @flex-center(); | ||
| 22 | } | ||
| 23 | .el-form-item { | ||
| 24 | @flex(); | ||
| 25 | width: 100%; | ||
| 26 | } | ||
| 27 | .el-dialog__wrapper { | ||
| 28 | @flex-center(); | ||
| 29 | } | ||
| 30 | .el-dialog { | ||
| 31 | margin: 0; | ||
| 32 | } |
src/styles/element-ui.scss
0 → 100644
| 1 | // cover some element-ui styles | ||
| 2 | |||
| 3 | .el-breadcrumb__inner, | ||
| 4 | .el-breadcrumb__inner a { | ||
| 5 | font-weight: 400 !important; | ||
| 6 | color: #686666; | ||
| 7 | } | ||
| 8 | |||
| 9 | .el-upload { | ||
| 10 | input[type="file"] { | ||
| 11 | display: none !important; | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | .el-upload__input { | ||
| 16 | display: none; | ||
| 17 | } | ||
| 18 | |||
| 19 | .cell { | ||
| 20 | .el-tag { | ||
| 21 | margin-right: 0px; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | .small-padding { | ||
| 26 | .cell { | ||
| 27 | padding-left: 5px; | ||
| 28 | padding-right: 5px; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | .fixed-width { | ||
| 33 | .el-button--mini { | ||
| 34 | padding: 7px 10px; | ||
| 35 | min-width: 60px; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | .status-col { | ||
| 40 | .cell { | ||
| 41 | padding: 0 10px; | ||
| 42 | text-align: center; | ||
| 43 | |||
| 44 | .el-tag { | ||
| 45 | margin-right: 0px; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | // to fixed https://github.com/ElemeFE/element/issues/2461 | ||
| 51 | .el-dialog { | ||
| 52 | transform: none; | ||
| 53 | left: 0; | ||
| 54 | position: relative; | ||
| 55 | margin: 0 auto; | ||
| 56 | } | ||
| 57 | |||
| 58 | // refine element ui upload | ||
| 59 | .upload-container { | ||
| 60 | .el-upload { | ||
| 61 | width: 100%; | ||
| 62 | |||
| 63 | .el-upload-dragger { | ||
| 64 | width: 100%; | ||
| 65 | height: 200px; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | // dropdown | ||
| 71 | .el-dropdown-menu { | ||
| 72 | a { | ||
| 73 | display: block | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | // fix date-picker ui bug in filter-item | ||
| 78 | .el-range-editor.el-input__inner { | ||
| 79 | display: inline-flex !important; | ||
| 80 | } | ||
| 81 | |||
| 82 | // to fix el-date-picker css style | ||
| 83 | .el-range-separator { | ||
| 84 | box-sizing: content-box; | ||
| 85 | } | ||
| 86 | |||
| 87 | .el-submenu__icon-arrow { | ||
| 88 | margin-top: -5px; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* --------------进度条美化---------------- */ | ||
| 92 | ::-webkit-scrollbar { | ||
| 93 | width: 7px; | ||
| 94 | height: 7px; | ||
| 95 | } | ||
| 96 | |||
| 97 | ::-webkit-scrollbar-track { | ||
| 98 | width: 7px; | ||
| 99 | background-color: rgba(255, 255, 255, 0); | ||
| 100 | -webkit-border-radius: 6px; | ||
| 101 | -moz-border-radius: 6px; | ||
| 102 | border-radius: 6px; | ||
| 103 | } | ||
| 104 | |||
| 105 | ::-webkit-scrollbar-thumb { | ||
| 106 | background-color: rgb(207, 208, 209); | ||
| 107 | background-clip: padding-box; | ||
| 108 | min-height: 28px; | ||
| 109 | -webkit-border-radius: 6px; | ||
| 110 | -moz-border-radius: 6px; | ||
| 111 | border-radius: 6px; | ||
| 112 | } | ||
| 113 | |||
| 114 | ::-webkit-scrollbar-thumb:hover { | ||
| 115 | background-color: rgb(162, 164, 167); | ||
| 116 | } | ||
| 117 | |||
| 118 | // element 样式补丁 | ||
| 119 | .el-menu--horizontal { | ||
| 120 | border-bottom: none !important; | ||
| 121 | } | ||
| 122 | |||
| 123 | // 按钮型tab样式 | ||
| 124 | .btnTab { | ||
| 125 | .el-tabs__nav.is-top { | ||
| 126 | line-height: 36px; | ||
| 127 | } | ||
| 128 | |||
| 129 | .el-tabs__item { | ||
| 130 | height: 36px; | ||
| 131 | line-height: 36px; | ||
| 132 | } | ||
| 133 | |||
| 134 | .el-tabs__item:first-child { | ||
| 135 | border-radius: 4px 0 0 4px; | ||
| 136 | } | ||
| 137 | |||
| 138 | .el-tabs__item:last-child { | ||
| 139 | border-radius: 0 4px 4px 0; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | .el-radio-group { | ||
| 144 | .el-radio-button__inner { | ||
| 145 | height: 36px; | ||
| 146 | line-height: 36px; | ||
| 147 | padding: 0 20px; | ||
| 148 | font-size: 14px; | ||
| 149 | } | ||
| 150 | |||
| 151 | .el-radio-button:first-child { | ||
| 152 | border-radius: 4px 0 0 4px; | ||
| 153 | } | ||
| 154 | |||
| 155 | .el-radio-button:last-child { | ||
| 156 | border-radius: 0 4px 4px 0; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | .el-tabs__item:focus.is-active.is-focus:not(:active) { | ||
| 161 | box-shadow: none !important; | ||
| 162 | } | ||
| 163 | |||
| 164 | // element table 不能对齐 | ||
| 165 | .el-table th.gutter { | ||
| 166 | display: table-cell !important; | ||
| 167 | } | ||
| 168 | |||
| 169 | // input type=number 上下箭头 | ||
| 170 | input::-webkit-outer-spin-button, | ||
| 171 | input::-webkit-inner-spin-button { | ||
| 172 | -webkit-appearance: none; | ||
| 173 | } | ||
| 174 | |||
| 175 | input[type="number"] { | ||
| 176 | -moz-appearance: textfield; | ||
| 177 | } | ||
| 178 | |||
| 179 | // Radio 单选框升级之后的问题 | ||
| 180 | .el-radio { | ||
| 181 | margin-right: 0 !important; | ||
| 182 | } | ||
| 183 | |||
| 184 | // Divider 分割线 样式的修改 | ||
| 185 | .el-divider--horizontal { | ||
| 186 | margin: 10px 0 !important; | ||
| 187 | } | ||
| 188 | |||
| 189 | .el-row { | ||
| 190 | margin-bottom: 0 !important; | ||
| 191 | } | ||
| 192 | |||
| 193 | // form | ||
| 194 | .el-form-item__content { | ||
| 195 | margin-left: 0 !important; | ||
| 196 | } | ||
| 197 | |||
| 198 | .el-icon-full-screen, | ||
| 199 | .el-icon-rank { | ||
| 200 | cursor: pointer; | ||
| 201 | } | ||
| 202 | |||
| 203 | // element table 选中 颜色 | ||
| 204 | .el-table--enable-row-hover .el-table__body tr:hover>td { | ||
| 205 | background-color: #FCFDFD; | ||
| 206 | } | ||
| 207 | |||
| 208 | .el-table__body .el-table__row.hover-row td { | ||
| 209 | background-color: #FCFDFD; | ||
| 210 | } | ||
| 211 | |||
| 212 | .el-table tbody tr:hover>td { | ||
| 213 | background-color: #FCFDFD; | ||
| 214 | } | ||
| 215 | |||
| 216 | // 表格样式 | ||
| 217 | .el-table th { | ||
| 218 | background-color: #FBFCFD !important; | ||
| 219 | height: 48px !important; | ||
| 220 | font-size: 14px; | ||
| 221 | color: #4A4A4A; | ||
| 222 | } | ||
| 223 | |||
| 224 | .el-table th.is-leaf, | ||
| 225 | .el-table td { | ||
| 226 | border-bottom: 1px solid #E4EBF4; | ||
| 227 | } | ||
| 228 | |||
| 229 | .el-table tr:nth-child(even) { | ||
| 230 | background: #FCFDFD !important; | ||
| 231 | } | ||
| 232 | |||
| 233 | .el-table tr td { | ||
| 234 | font-size: 14px; | ||
| 235 | color: #7A7A7A; | ||
| 236 | } | ||
| 237 | |||
| 238 | .lb-table .el-table { | ||
| 239 | border: 1px solid #E4EBF4; | ||
| 240 | border-radius: 4px 4px 0 0; | ||
| 241 | } | ||
| 242 | |||
| 243 | // 表格暂无数据样式 | ||
| 244 | .el-table__empty-text { | ||
| 245 | height: 200px; | ||
| 246 | line-height: 300px; | ||
| 247 | background: url('./images/table-bg.png') 50% 50% no-repeat; | ||
| 248 | background-size: 100px; | ||
| 249 | letter-spacing: 2px; | ||
| 250 | color: #aaa; | ||
| 251 | } | ||
| 252 | |||
| 253 | .el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip { | ||
| 254 | display: none !important | ||
| 255 | } | ||
| 256 | |||
| 257 | .el-message-box__btns { | ||
| 258 | display: flex; | ||
| 259 | flex-direction: row-reverse; | ||
| 260 | } | ||
| 261 | |||
| 262 | .el-message-box__btns .el-button--primary { | ||
| 263 | margin-right: 10px; | ||
| 264 | } | ||
| 265 | |||
| 266 | // 全局修改button颜色 | ||
| 267 | .el-button--primary { | ||
| 268 | background-color: #2FA5FF; | ||
| 269 | } | ||
| 270 | |||
| 271 | .el-button--primary.is-plain { | ||
| 272 | background: rgba(15, 147, 246, 0.12); | ||
| 273 | border: 1px solid #0F93F6; | ||
| 274 | color: #0F93F6; | ||
| 275 | } | ||
| 276 | |||
| 277 | .el-button--warning { | ||
| 278 | background-color: #FFB135; | ||
| 279 | } | ||
| 280 | |||
| 281 | .el-form-item__content { | ||
| 282 | //width: 100%; | ||
| 283 | flex: 1; | ||
| 284 | } | ||
| 285 | |||
| 286 | .el-submenu__title { | ||
| 287 | .svg-icon { | ||
| 288 | position: relative; | ||
| 289 | top: 2px; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | .el-date-editor .el-input__prefix, | ||
| 294 | .el-select .el-input .el-select__caret, | ||
| 295 | .el-date-editor .el-range__icon { | ||
| 296 | color: #686666; | ||
| 297 | } | ||
| 298 | |||
| 299 | // 日期选择-日历图标位置 | ||
| 300 | .el-date-editor .el-input__prefix { | ||
| 301 | left: auto; | ||
| 302 | right: 5px; | ||
| 303 | } | ||
| 304 | |||
| 305 | .el-input--prefix .el-input__inner { | ||
| 306 | padding-left: 17px; | ||
| 307 | } | ||
| 308 | |||
| 309 | .el-input__suffix-inner .el-icon-circle-close { | ||
| 310 | position: absolute; | ||
| 311 | right: 20px; | ||
| 312 | } | ||
| 313 | |||
| 314 | // 日期范围-日历图标位置 | ||
| 315 | .el-date-editor .el-range__icon { | ||
| 316 | position: absolute; | ||
| 317 | right: 5px; | ||
| 318 | } | ||
| 319 | |||
| 320 | .el-date-editor .el-icon-circle-close { | ||
| 321 | position: absolute; | ||
| 322 | right: 20px; | ||
| 323 | } | ||
| 324 | |||
| 325 | .el-date-editor.el-date-editor--daterange .el-icon-circle-close { | ||
| 326 | position: absolute; | ||
| 327 | right: 25px; | ||
| 328 | } | ||
| 329 | |||
| 330 | // 表格分页 | ||
| 331 | .el-pagination.is-background .btn-prev, | ||
| 332 | .el-pagination.is-background .btn-next, | ||
| 333 | .el-pagination.is-background .el-pager li, | ||
| 334 | .el-pagination .el-select .el-input .el-input__inner { | ||
| 335 | background-color: #fff !important; | ||
| 336 | border: 1px solid #E4EBF4 !important; | ||
| 337 | border-radius: 5px !important; | ||
| 338 | } | ||
| 339 | |||
| 340 | .el-pagination.is-background .el-pager li.active { | ||
| 341 | background-color: #0F93F6 !important; | ||
| 342 | border: 1px solid #0F93F6 !important; | ||
| 343 | color: #fff !important; | ||
| 344 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/styles/element-variables.scss
0 → 100644
| 1 | /** | ||
| 2 | * I think element-ui's default theme color is too light for long-term use. | ||
| 3 | * So I modified the default color and you can modify it to your liking. | ||
| 4 | **/ | ||
| 5 | |||
| 6 | /* theme color */ | ||
| 7 | $--color-primary: #0F93F6; | ||
| 8 | $--color-success: #67C23B; | ||
| 9 | $--color-warning: #E6A23C; | ||
| 10 | $--color-danger: #F46C6C; | ||
| 11 | // $--color-info: #1E1E1E; | ||
| 12 | |||
| 13 | $--button-font-weight: 400; | ||
| 14 | |||
| 15 | $--border-color-light: #dfe4ed; | ||
| 16 | $--border-color-lighter: #e6ebf5; | ||
| 17 | |||
| 18 | $--table-border: 1px solid #dfe6ec; | ||
| 19 | |||
| 20 | /* icon font path, required */ | ||
| 21 | $--font-path: "~element-ui/lib/theme-chalk/fonts"; | ||
| 22 | |||
| 23 | @import "~element-ui/packages/theme-chalk/src/index"; | ||
| 24 | |||
| 25 | // the :export directive is the magic sauce for webpack | ||
| 26 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass | ||
| 27 | :export { | ||
| 28 | theme: $--color-primary; | ||
| 29 | } |
src/styles/images/table-bg.png
0 → 100644
43.2 KB
src/styles/index.scss
0 → 100644
| 1 | @import './variables.scss'; | ||
| 2 | @import './mixin.scss'; | ||
| 3 | @import './transition.scss'; | ||
| 4 | @import './element-ui.scss'; | ||
| 5 | @import './sidebar.scss'; | ||
| 6 | @import './dialog.scss'; | ||
| 7 | @import './btn.scss'; | ||
| 8 | |||
| 9 | /* CSS 初始化 */ | ||
| 10 | html, | ||
| 11 | body, | ||
| 12 | div, | ||
| 13 | span, | ||
| 14 | object, | ||
| 15 | iframe, | ||
| 16 | h1, | ||
| 17 | h2, | ||
| 18 | h3, | ||
| 19 | h4, | ||
| 20 | h5, | ||
| 21 | h6, | ||
| 22 | p, | ||
| 23 | blockquote, | ||
| 24 | pre, | ||
| 25 | abbr, | ||
| 26 | address, | ||
| 27 | cite, | ||
| 28 | code, | ||
| 29 | del, | ||
| 30 | dfn, | ||
| 31 | em, | ||
| 32 | img, | ||
| 33 | ins, | ||
| 34 | kbd, | ||
| 35 | q, | ||
| 36 | samp, | ||
| 37 | small, | ||
| 38 | strong, | ||
| 39 | sub, | ||
| 40 | sup, | ||
| 41 | var, | ||
| 42 | b, | ||
| 43 | i, | ||
| 44 | dl, | ||
| 45 | dt, | ||
| 46 | dd, | ||
| 47 | ol, | ||
| 48 | ul, | ||
| 49 | li, | ||
| 50 | fieldset, | ||
| 51 | form, | ||
| 52 | label, | ||
| 53 | legend, | ||
| 54 | table, | ||
| 55 | caption, | ||
| 56 | tbody, | ||
| 57 | tfoot, | ||
| 58 | thead, | ||
| 59 | tr, | ||
| 60 | th, | ||
| 61 | td, | ||
| 62 | article, | ||
| 63 | aside, | ||
| 64 | canvas, | ||
| 65 | details, | ||
| 66 | figcaption, | ||
| 67 | figure, | ||
| 68 | footer, | ||
| 69 | header, | ||
| 70 | hgroup, | ||
| 71 | menu, | ||
| 72 | nav, | ||
| 73 | section, | ||
| 74 | summary, | ||
| 75 | time, | ||
| 76 | mark, | ||
| 77 | audio, | ||
| 78 | video { | ||
| 79 | margin: 0; | ||
| 80 | padding: 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | body { | ||
| 84 | height: 100%; | ||
| 85 | min-height: 100% !important; | ||
| 86 | -moz-osx-font-smoothing: grayscale; | ||
| 87 | -webkit-font-smoothing: antialiased; | ||
| 88 | text-rendering: optimizeLegibility; | ||
| 89 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; | ||
| 90 | } | ||
| 91 | |||
| 92 | html { | ||
| 93 | height: 100%; | ||
| 94 | box-sizing: border-box; | ||
| 95 | } | ||
| 96 | |||
| 97 | #app { | ||
| 98 | height: 100%; | ||
| 99 | } | ||
| 100 | |||
| 101 | *, | ||
| 102 | *:before, | ||
| 103 | *:after { | ||
| 104 | box-sizing: inherit; | ||
| 105 | } | ||
| 106 | |||
| 107 | .no-padding { | ||
| 108 | padding: 0px !important; | ||
| 109 | } | ||
| 110 | |||
| 111 | .padding-content { | ||
| 112 | padding: 4px 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | a:focus, | ||
| 116 | a:active { | ||
| 117 | outline: none; | ||
| 118 | } | ||
| 119 | |||
| 120 | a, | ||
| 121 | a:focus, | ||
| 122 | a:hover { | ||
| 123 | cursor: pointer; | ||
| 124 | color: inherit; | ||
| 125 | text-decoration: none; | ||
| 126 | } | ||
| 127 | |||
| 128 | div:focus { | ||
| 129 | outline: none; | ||
| 130 | } | ||
| 131 | |||
| 132 | .fr { | ||
| 133 | float: right; | ||
| 134 | } | ||
| 135 | |||
| 136 | .fl { | ||
| 137 | float: left; | ||
| 138 | } | ||
| 139 | |||
| 140 | .pr-5 { | ||
| 141 | padding-right: 5px; | ||
| 142 | } | ||
| 143 | |||
| 144 | .pl-5 { | ||
| 145 | padding-left: 5px; | ||
| 146 | } | ||
| 147 | |||
| 148 | .block { | ||
| 149 | display: block; | ||
| 150 | } | ||
| 151 | |||
| 152 | .pointer { | ||
| 153 | cursor: pointer; | ||
| 154 | } | ||
| 155 | |||
| 156 | .inlineBlock { | ||
| 157 | display: block; | ||
| 158 | } | ||
| 159 | |||
| 160 | .clearfix { | ||
| 161 | &:after { | ||
| 162 | visibility: hidden; | ||
| 163 | display: block; | ||
| 164 | font-size: 0; | ||
| 165 | content: " "; | ||
| 166 | clear: both; | ||
| 167 | height: 0; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | aside { | ||
| 172 | background: #eef1f6; | ||
| 173 | padding: 8px 24px; | ||
| 174 | margin-bottom: 20px; | ||
| 175 | border-radius: 2px; | ||
| 176 | display: block; | ||
| 177 | line-height: 32px; | ||
| 178 | font-size: 16px; | ||
| 179 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; | ||
| 180 | color: #2c3e50; | ||
| 181 | -webkit-font-smoothing: antialiased; | ||
| 182 | -moz-osx-font-smoothing: grayscale; | ||
| 183 | |||
| 184 | a { | ||
| 185 | color: #337ab7; | ||
| 186 | cursor: pointer; | ||
| 187 | |||
| 188 | &:hover { | ||
| 189 | color: rgb(32, 160, 255); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | //main-container全局样式 | ||
| 195 | .app-container { | ||
| 196 | padding: 20px; | ||
| 197 | } | ||
| 198 | |||
| 199 | .components-container { | ||
| 200 | margin: 30px 50px; | ||
| 201 | position: relative; | ||
| 202 | } | ||
| 203 | |||
| 204 | .pagination-container { | ||
| 205 | margin-top: 30px; | ||
| 206 | } | ||
| 207 | |||
| 208 | .text-center { | ||
| 209 | text-align: center | ||
| 210 | } | ||
| 211 | |||
| 212 | .sub-navbar { | ||
| 213 | height: 50px; | ||
| 214 | line-height: 50px; | ||
| 215 | position: relative; | ||
| 216 | width: 100%; | ||
| 217 | text-align: right; | ||
| 218 | padding-right: 20px; | ||
| 219 | transition: 600ms ease position; | ||
| 220 | background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); | ||
| 221 | |||
| 222 | .subtitle { | ||
| 223 | font-size: 20px; | ||
| 224 | color: #fff; | ||
| 225 | } | ||
| 226 | |||
| 227 | &.draft { | ||
| 228 | background: #d0d0d0; | ||
| 229 | } | ||
| 230 | |||
| 231 | &.deleted { | ||
| 232 | background: #d0d0d0; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | .link-type, | ||
| 237 | .link-type:focus { | ||
| 238 | color: #337ab7; | ||
| 239 | cursor: pointer; | ||
| 240 | |||
| 241 | &:hover { | ||
| 242 | color: rgb(32, 160, 255); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | .filter-container { | ||
| 247 | padding-bottom: 10px; | ||
| 248 | |||
| 249 | .filter-item { | ||
| 250 | display: inline-block; | ||
| 251 | vertical-align: middle; | ||
| 252 | margin-bottom: 10px; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | //refine vue-multiselect plugin | ||
| 257 | .multiselect { | ||
| 258 | line-height: 16px; | ||
| 259 | } | ||
| 260 | |||
| 261 | .multiselect--active { | ||
| 262 | z-index: 1000 !important; | ||
| 263 | } | ||
| 264 | |||
| 265 | .delete-button { | ||
| 266 | line-height: 32px; | ||
| 267 | cursor: pointer; | ||
| 268 | color: #FF7115; | ||
| 269 | font-size: 20px; | ||
| 270 | margin-right: 5px; | ||
| 271 | |||
| 272 | } | ||
| 273 | |||
| 274 | .add-button { | ||
| 275 | line-height: 32px; | ||
| 276 | color: #2FA5FF; | ||
| 277 | cursor: pointer; | ||
| 278 | font-size: 20PX; | ||
| 279 | } | ||
| 280 | |||
| 281 | // 遮罩层样式设置 | ||
| 282 | .dialog_Style { | ||
| 283 | .el-dialog { | ||
| 284 | width: 40% !important; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | .description { | ||
| 289 | .el-form-item__content { | ||
| 290 | width: 75%; | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | // 弹窗组件 input样式 | ||
| 295 | .header-title { | ||
| 296 | margin-bottom: 18px; | ||
| 297 | line-height: 20px; | ||
| 298 | height: 20px; | ||
| 299 | font-size: 15px; | ||
| 300 | font-weight: bolder; | ||
| 301 | width: 200px; | ||
| 302 | margin-left: 45px; | ||
| 303 | } | ||
| 304 | |||
| 305 | .detailTree { | ||
| 306 | width: 188px !important; | ||
| 307 | } | ||
| 308 | |||
| 309 | // 禁用 | ||
| 310 | .disabled { | ||
| 311 | color: #87adf3; | ||
| 312 | background-color: #fff; | ||
| 313 | cursor: not-allowed | ||
| 314 | } | ||
| 315 | |||
| 316 | .bad { | ||
| 317 | color: #f00; | ||
| 318 | background-color: #fff; | ||
| 319 | cursor: not-allowed | ||
| 320 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/styles/mixin.scss
0 → 100644
| 1 | @mixin clearfix { | ||
| 2 | &:after { | ||
| 3 | content: ""; | ||
| 4 | display: table; | ||
| 5 | clear: both; | ||
| 6 | } | ||
| 7 | } | ||
| 8 | |||
| 9 | @mixin scrollBar { | ||
| 10 | &::-webkit-scrollbar-track-piece { | ||
| 11 | background: #d3dce6; | ||
| 12 | } | ||
| 13 | |||
| 14 | &::-webkit-scrollbar { | ||
| 15 | width: 6px; | ||
| 16 | } | ||
| 17 | |||
| 18 | &::-webkit-scrollbar-thumb { | ||
| 19 | background: #99a9bf; | ||
| 20 | border-radius: 20px; | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | @mixin relative { | ||
| 25 | position: relative; | ||
| 26 | width: 100%; | ||
| 27 | height: 100%; | ||
| 28 | } | ||
| 29 | |||
| 30 | @mixin pct($pct) { | ||
| 31 | width: #{$pct}; | ||
| 32 | position: relative; | ||
| 33 | margin: 0 auto; | ||
| 34 | } | ||
| 35 | |||
| 36 | @mixin triangle($width, $height, $color, $direction) { | ||
| 37 | $width: $width/2; | ||
| 38 | $color-border-style: $height solid $color; | ||
| 39 | $transparent-border-style: $width solid transparent; | ||
| 40 | height: 0; | ||
| 41 | width: 0; | ||
| 42 | |||
| 43 | @if $direction==up { | ||
| 44 | border-bottom: $color-border-style; | ||
| 45 | border-left: $transparent-border-style; | ||
| 46 | border-right: $transparent-border-style; | ||
| 47 | } @else if $direction==right { | ||
| 48 | border-left: $color-border-style; | ||
| 49 | border-top: $transparent-border-style; | ||
| 50 | border-bottom: $transparent-border-style; | ||
| 51 | } @else if $direction==down { | ||
| 52 | border-top: $color-border-style; | ||
| 53 | border-left: $transparent-border-style; | ||
| 54 | border-right: $transparent-border-style; | ||
| 55 | } @else if $direction==left { | ||
| 56 | border-right: $color-border-style; | ||
| 57 | border-top: $transparent-border-style; | ||
| 58 | border-bottom: $transparent-border-style; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | @mixin flex { | ||
| 62 | display: flex; | ||
| 63 | } | ||
| 64 | @mixin flex-center-row { | ||
| 65 | display: flex; | ||
| 66 | align-items: center; | ||
| 67 | flex-direction: row; | ||
| 68 | } | ||
| 69 | @mixin flex-center-col { | ||
| 70 | display: flex; | ||
| 71 | align-items: center; | ||
| 72 | flex-direction: column; | ||
| 73 | } | ||
| 74 | @mixin flex-center { | ||
| 75 | display: flex; | ||
| 76 | justify-content: center; | ||
| 77 | align-items: center; | ||
| 78 | } |
src/styles/sidebar.scss
0 → 100644
| 1 | #app { | ||
| 2 | .main-container { | ||
| 3 | height: 100%; | ||
| 4 | transition: margin-left 0.28s; | ||
| 5 | margin-left: $sideBarWidth; | ||
| 6 | } | ||
| 7 | |||
| 8 | .sidebar-container { | ||
| 9 | transition: width 0.28s; | ||
| 10 | width: $sideBarWidth !important; | ||
| 11 | // background-color: $menuBg; | ||
| 12 | height: 100%; | ||
| 13 | position: fixed; | ||
| 14 | font-size: 0px; | ||
| 15 | top: 0; | ||
| 16 | bottom: 0; | ||
| 17 | left: 0; | ||
| 18 | z-index: 1001; | ||
| 19 | background-image: linear-gradient(180deg, #25A0FE 2%, #43BDFF 100%); | ||
| 20 | border-radius: 0 12px 12px 0; | ||
| 21 | overflow: hidden; | ||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | // reset element-ui css | ||
| 27 | .horizontal-collapse-transition { | ||
| 28 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, | ||
| 29 | 0s padding-right ease-in-out; | ||
| 30 | } | ||
| 31 | |||
| 32 | .scrollbar-wrapper { | ||
| 33 | overflow-x: hidden !important; | ||
| 34 | overflow-y: auto; | ||
| 35 | margin-right: 0 !important; | ||
| 36 | |||
| 37 | &::-webkit-scrollbar { | ||
| 38 | display: none; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | .el-scrollbar__bar.is-vertical { | ||
| 43 | right: 0px; | ||
| 44 | } | ||
| 45 | |||
| 46 | .el-scrollbar { | ||
| 47 | height: 100%; | ||
| 48 | |||
| 49 | &::-webkit-scrollbar { | ||
| 50 | display: none; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | &.has-logo { | ||
| 55 | .el-scrollbar { | ||
| 56 | height: calc(100% - 150px); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | .is-horizontal { | ||
| 61 | display: none; | ||
| 62 | } | ||
| 63 | |||
| 64 | a { | ||
| 65 | display: inline-block; | ||
| 66 | width: 100%; | ||
| 67 | overflow: hidden; | ||
| 68 | } | ||
| 69 | |||
| 70 | .svg-icon { | ||
| 71 | margin-right: 10px; | ||
| 72 | } | ||
| 73 | |||
| 74 | .sub-el-icon { | ||
| 75 | margin-right: 12px; | ||
| 76 | margin-left: -2px; | ||
| 77 | } | ||
| 78 | |||
| 79 | .el-menu { | ||
| 80 | background-color: transparent !important; | ||
| 81 | border: none; | ||
| 82 | height: 100%; | ||
| 83 | // overflow-y: auto; | ||
| 84 | width: 100% !important; | ||
| 85 | } | ||
| 86 | |||
| 87 | // menu hover | ||
| 88 | .el-menu--collapse .el-submenu__title, | ||
| 89 | .el-menu--collapse .submenu-title-noDropdown { | ||
| 90 | margin-left: 0px !important; | ||
| 91 | } | ||
| 92 | |||
| 93 | // 有子级 | ||
| 94 | .el-submenu__title { | ||
| 95 | margin-left: 13px; | ||
| 96 | padding-left: 10px !important; | ||
| 97 | color: $menuText; | ||
| 98 | background-color: transparent !important; | ||
| 99 | |||
| 100 | &:hover { | ||
| 101 | color: #1ea6f8 !important; | ||
| 102 | background-color: #fff !important; | ||
| 103 | |||
| 104 | .svg-icon, | ||
| 105 | i, | ||
| 106 | span { | ||
| 107 | color: #1ea6f8 !important; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | // 没有子级 | ||
| 113 | .submenu-title-noDropdown { | ||
| 114 | background-color: transparent !important; | ||
| 115 | margin-left: 20px; | ||
| 116 | padding-left: 10px !important; | ||
| 117 | color: $menuText; | ||
| 118 | |||
| 119 | &:hover { | ||
| 120 | color: #1ea6f8 !important; | ||
| 121 | background-color: #fff !important; | ||
| 122 | |||
| 123 | .svg-icon { | ||
| 124 | color: #1ea6f8 !important; | ||
| 125 | } | ||
| 126 | |||
| 127 | i { | ||
| 128 | color: #1ea6f8 !important; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | .submenu-title-noDropdown.is-active, | ||
| 134 | .el-submenu__title.is-active { | ||
| 135 | color: #1ea6f8 !important; | ||
| 136 | background-color: #fff !important; | ||
| 137 | |||
| 138 | .svg-icon { | ||
| 139 | color: #1ea6f8 !important; | ||
| 140 | } | ||
| 141 | |||
| 142 | i { | ||
| 143 | color: #1ea6f8 !important; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | .submenu-title-noDropdown, | ||
| 148 | .el-submenu__title { | ||
| 149 | font-weight: 600; | ||
| 150 | font-size: $sideBarFontSize; | ||
| 151 | border-radius: 10px 0px 0px 10px; | ||
| 152 | |||
| 153 | >i { | ||
| 154 | color: $subMenuActiveText !important; | ||
| 155 | transform: rotate(90deg); | ||
| 156 | -webkit-transform: rotate(90deg); | ||
| 157 | -moz-transform: rotate(90deg); | ||
| 158 | -ms-transform: rotate(90deg); | ||
| 159 | -o-transform: rotate(90deg); | ||
| 160 | margin-right: 10px; | ||
| 161 | } | ||
| 162 | |||
| 163 | .svg-icon { | ||
| 164 | font-size: 18px; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow { | ||
| 169 | transform: rotateZ(0deg) !important; | ||
| 170 | -webkit-transform: rotateZ(0deg) !important; | ||
| 171 | -moz-transform: rotateZ(0deg) !important; | ||
| 172 | -ms-transform: rotateZ(0deg) !important; | ||
| 173 | -o-transform: rotateZ(0deg) !important; | ||
| 174 | } | ||
| 175 | |||
| 176 | .is-active>.el-submenu__title { | ||
| 177 | color: #fff !important; | ||
| 178 | } | ||
| 179 | |||
| 180 | & .nest-menu .el-submenu>.el-submenu__title, | ||
| 181 | & .el-submenu .el-menu-item { | ||
| 182 | &.is-active { | ||
| 183 | background-color: #fff !important; | ||
| 184 | color: #1ea6f8 !important; | ||
| 185 | } | ||
| 186 | |||
| 187 | &:hover { | ||
| 188 | background-color: #fff !important; | ||
| 189 | color: #1ea6f8 !important; | ||
| 190 | } | ||
| 191 | |||
| 192 | // min-width: $sideBarWidth !important; | ||
| 193 | min-width: 152px !important; | ||
| 194 | background-color: transparent !important; | ||
| 195 | font-weight: 600; | ||
| 196 | font-size: $sideBarFontSize; | ||
| 197 | padding: 0 0 0 45px !important; | ||
| 198 | border-radius: 10px 0px 0px 10px; | ||
| 199 | margin-left: 13px; | ||
| 200 | |||
| 201 | &:hover { | ||
| 202 | background-color: #fff !important; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | .hideSidebar { | ||
| 208 | .sidebar-container { | ||
| 209 | width: 54px !important; | ||
| 210 | } | ||
| 211 | |||
| 212 | .main-container { | ||
| 213 | margin-left: 54px; | ||
| 214 | } | ||
| 215 | |||
| 216 | .submenu-title-noDropdown { | ||
| 217 | padding: 0 !important; | ||
| 218 | position: relative; | ||
| 219 | |||
| 220 | .el-tooltip { | ||
| 221 | padding: 0 !important; | ||
| 222 | |||
| 223 | .svg-icon { | ||
| 224 | margin-left: 16px; | ||
| 225 | } | ||
| 226 | |||
| 227 | .sub-el-icon { | ||
| 228 | margin-left: 19px; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | .el-submenu { | ||
| 234 | overflow: hidden; | ||
| 235 | |||
| 236 | &>.el-submenu__title { | ||
| 237 | padding: 0 !important; | ||
| 238 | |||
| 239 | .svg-icon { | ||
| 240 | margin-left: 16px; | ||
| 241 | } | ||
| 242 | |||
| 243 | .sub-el-icon { | ||
| 244 | margin-left: 19px; | ||
| 245 | } | ||
| 246 | |||
| 247 | .el-submenu__icon-arrow { | ||
| 248 | display: none; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | .el-menu--collapse { | ||
| 254 | .el-submenu { | ||
| 255 | &>.el-submenu__title { | ||
| 256 | &>span { | ||
| 257 | height: 0; | ||
| 258 | width: 0; | ||
| 259 | overflow: hidden; | ||
| 260 | visibility: hidden; | ||
| 261 | display: inline-block; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | .el-menu--collapse .el-menu .el-submenu { | ||
| 269 | min-width: $sideBarWidth !important; | ||
| 270 | } | ||
| 271 | |||
| 272 | // mobile responsive | ||
| 273 | .mobile { | ||
| 274 | .main-container { | ||
| 275 | margin-left: 0px; | ||
| 276 | } | ||
| 277 | |||
| 278 | .sidebar-container { | ||
| 279 | transition: transform 0.28s; | ||
| 280 | width: $sideBarWidth !important; | ||
| 281 | } | ||
| 282 | |||
| 283 | &.hideSidebar { | ||
| 284 | .sidebar-container { | ||
| 285 | pointer-events: none; | ||
| 286 | transition-duration: 0.3s; | ||
| 287 | transform: translate3d(-$sideBarWidth, 0, 0); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | .withoutAnimation { | ||
| 293 | |||
| 294 | .main-container, | ||
| 295 | .sidebar-container { | ||
| 296 | transition: none; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | // when menu collapsed | ||
| 302 | .el-menu--vertical { | ||
| 303 | &>.el-menu { | ||
| 304 | .svg-icon { | ||
| 305 | margin-right: 16px; | ||
| 306 | } | ||
| 307 | |||
| 308 | .sub-el-icon { | ||
| 309 | margin-right: 12px; | ||
| 310 | margin-left: -2px; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | .nest-menu .el-submenu>.el-submenu__title, | ||
| 315 | .el-menu-item { | ||
| 316 | width: calc(100% - 12px); | ||
| 317 | border-top-right-radius: 8px; | ||
| 318 | border-bottom-right-radius: 8px; | ||
| 319 | font-weight: 600 !important; | ||
| 320 | font-size: 15px !important; | ||
| 321 | background-color: #32ACFE !important; | ||
| 322 | color: #fff !important; | ||
| 323 | |||
| 324 | &:hover { | ||
| 325 | // you can use $subMenuHover | ||
| 326 | // background-color: $menuHover !important; | ||
| 327 | // opacity: .9; | ||
| 328 | background-color: #fff !important; | ||
| 329 | color: #32ACFE !important; | ||
| 330 | |||
| 331 | .svg-icon, | ||
| 332 | i, | ||
| 333 | span { | ||
| 334 | color: #32ACFE !important; | ||
| 335 | |||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | // the scroll bar appears when the subMenu is too long | ||
| 341 | >.el-menu--popup { | ||
| 342 | max-height: 100vh; | ||
| 343 | overflow-y: auto; | ||
| 344 | background-color: #32ACFE !important; | ||
| 345 | border-top-right-radius: 8px; | ||
| 346 | border-bottom-right-radius: 8px; | ||
| 347 | min-width: 140px; | ||
| 348 | padding: 12px 0; | ||
| 349 | |||
| 350 | &::-webkit-scrollbar-track-piece { | ||
| 351 | background: #d3dce6; | ||
| 352 | } | ||
| 353 | |||
| 354 | &::-webkit-scrollbar { | ||
| 355 | width: 6px; | ||
| 356 | } | ||
| 357 | |||
| 358 | &::-webkit-scrollbar-thumb { | ||
| 359 | background: #99a9bf; | ||
| 360 | border-radius: 20px; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/styles/transition.scss
0 → 100644
| 1 | // global transition css | ||
| 2 | |||
| 3 | /* fade */ | ||
| 4 | .fade-enter-active, | ||
| 5 | .fade-leave-active { | ||
| 6 | transition: opacity 0.28s; | ||
| 7 | } | ||
| 8 | |||
| 9 | .fade-enter, | ||
| 10 | .fade-leave-active { | ||
| 11 | opacity: 0; | ||
| 12 | } | ||
| 13 | |||
| 14 | /* fade-transform */ | ||
| 15 | .fade-transform-leave-active, | ||
| 16 | .fade-transform-enter-active { | ||
| 17 | transition: all .5s; | ||
| 18 | } | ||
| 19 | |||
| 20 | .fade-transform-enter { | ||
| 21 | opacity: 0; | ||
| 22 | transform: translateX(-30px); | ||
| 23 | } | ||
| 24 | |||
| 25 | .fade-transform-leave-to { | ||
| 26 | opacity: 0; | ||
| 27 | transform: translateX(30px); | ||
| 28 | } | ||
| 29 | |||
| 30 | /* breadcrumb transition */ | ||
| 31 | .breadcrumb-enter-active, | ||
| 32 | .breadcrumb-leave-active { | ||
| 33 | transition: all .5s; | ||
| 34 | } | ||
| 35 | |||
| 36 | .breadcrumb-enter, | ||
| 37 | .breadcrumb-leave-active { | ||
| 38 | opacity: 0; | ||
| 39 | transform: translateX(20px); | ||
| 40 | } | ||
| 41 | |||
| 42 | .breadcrumb-move { | ||
| 43 | transition: all .5s; | ||
| 44 | } | ||
| 45 | |||
| 46 | .breadcrumb-leave-active { | ||
| 47 | position: absolute; | ||
| 48 | } |
src/styles/variables.scss
0 → 100644
| 1 | // base color | ||
| 2 | $blue:#324157; | ||
| 3 | $light-blue:#3A71A8; | ||
| 4 | $red:#C03639; | ||
| 5 | $pink: #E65D6E; | ||
| 6 | $green: #30B08F; | ||
| 7 | $tiffany: #4AB7BD; | ||
| 8 | $yellow:#FEC171; | ||
| 9 | $panGreen: #30B08F; | ||
| 10 | |||
| 11 | // sidebar | ||
| 12 | $menuText:#ffffff; | ||
| 13 | $menuActiveText:#ffffff; | ||
| 14 | $subMenuActiveText:#ffffff; // https://github.com/ElemeFE/element/issues/12951 | ||
| 15 | |||
| 16 | $menuBg:#3983fc; | ||
| 17 | $menuHover:#2e69ca; | ||
| 18 | |||
| 19 | $subMenuBg:#3983fc; | ||
| 20 | $subMenuHover:#2e69ca; | ||
| 21 | |||
| 22 | $sideBarWidth: 160px; | ||
| 23 | $sideBarFontSize:15px; | ||
| 24 | |||
| 25 | // the :export directive is the magic sauce for webpack | ||
| 26 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass | ||
| 27 | :export { | ||
| 28 | menuText: $menuText; | ||
| 29 | menuActiveText: $menuActiveText; | ||
| 30 | subMenuActiveText: $subMenuActiveText; | ||
| 31 | menuBg: $menuBg; | ||
| 32 | menuHover: $menuHover; | ||
| 33 | subMenuBg: $subMenuBg; | ||
| 34 | subMenuHover: $subMenuHover; | ||
| 35 | sideBarWidth: $sideBarWidth; | ||
| 36 | sideBarFontSize:$sideBarFontSize; | ||
| 37 | } |
src/utils/get-page-title.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/utils/request.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/utils/session.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/utils/validate.js
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/error-page/401.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/error-page/404.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/home/index.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/housedev/yzfyxsgl/index.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/login/auth-redirect.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/login/components/JcRange.vue
0 → 100644
This diff is collapsed.
Click to expand it.
13.1 KB
src/views/login/img/back_img@2x 2.png
0 → 100644
729 KB
src/views/login/img/bazaaz.png
0 → 100644
2.58 MB
src/views/login/img/code.png
0 → 100644
8.19 KB
src/views/login/img/erweima.png
0 → 100644
65.2 KB
src/views/login/img/jiancelogo.png
0 → 100644
76.9 KB
src/views/login/img/jiarr.png
0 → 100644
2.68 KB
src/views/login/img/jieshao.png
0 → 100644
2.48 KB
src/views/login/img/login-title.png
0 → 100644
65.8 KB
src/views/login/img/login_computer.png
0 → 100644
11.6 KB
src/views/login/img/qiye.png
0 → 100644
4.13 KB
src/views/login/img/shichang.png
0 → 100644
4.46 KB
src/views/login/img/shichanglogo.png
0 → 100644
65.7 KB
src/views/login/img/xieyi.png
0 → 100644
2.53 KB
src/views/login/img/xinBg.png
0 → 100644
1.34 MB
src/views/login/img/xinlogo.png
0 → 100644
56.4 KB
src/views/login/img/yonghu.png
0 → 100644
4.84 KB
src/views/login/img/zhengfu.png
0 → 100644
4.27 KB
src/views/login/img/zhinan.png
0 → 100644
2.91 KB
src/views/login/login.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/redirect/index.vue
0 → 100644
This diff is collapsed.
Click to expand it.
vue.config.js
0 → 100644
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment