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