276d8e3e by 赵千

init

0 parents
Showing 106 changed files with 4799 additions and 0 deletions
1 # https://editorconfig.org
2 root = true
3
4 [*]
5 charset = utf-8
6 indent_style = space
7 indent_size = 2
8 end_of_line = lf
9 insert_final_newline = true
10 trim_trailing_whitespace = true
11
12 [*.md]
13 insert_final_newline = false
14 trim_trailing_whitespace = false
1 # just a flag
2 ENV = 'development'
3 NODE_ENV=development
4 # base api
5 VUE_APP_BASE_API = '/dev-api'
6
7 # 开发环境
8 VUE_APP_API_BASE_URL = 'http://192.168.2.38:18010'
1 # just a flag
2 ENV = 'demonstration'
3 NODE_ENV=development
4 # base api
5 VUE_APP_BASE_API = '/dev-api'
6
7 # 演示,正式后端
8 VUE_APP_API_BASE_URL = 'http://39.103.195.0/dev-api'
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
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.
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
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 }
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 }
1 module.exports = {
2 plugins: {
3 autoprefixer: {}
4 },
5 }
No preview for this file type
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>
1 <template>
2 <div id="app">
3 <router-view />
4 </div>
5 </template>
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 })()
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()
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 }
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 }
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>
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>
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>
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
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
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
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 }
1 import lbTable from './lb-table.vue'
2 export default {
3 install:(Vue) => {
4 Vue.component('lbTable',lbTable);
5 }
6 }
...\ No newline at end of file ...\ No newline at end of file
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>
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>
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>
1 ## 这是对于element-table 进行的二次封装
2
3 ### 文档地址
4
5 <!-- table 已经全局注册不需要每个页面单独注册 -->
6
7 [Windows/Mac/Linux 全平台客户端](https://github.liubing.me/lb-element-table/zh/guide/)
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
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
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
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 }
1 import Vue from 'vue'
2 import SvgIcon from '@/components/SvgIcon'// svg component
3 // register globally
4 Vue.component('svg-icon', SvgIcon)
5 const req = require.context('./svg', false, /\.svg$/)
6 const requireAll = requireContext => requireContext.keys().map(requireContext)
7 requireAll(req)
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
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
1 # replace default config
2
3 # multipass: true
4 # full: true
5
6 plugins:
7
8 # - name
9 #
10 # or:
11 # - name: false
12 # - name: true
13 #
14 # or:
15 # - name:
16 # param1: 1
17 # param2: 2
18
19 - removeAttrs:
20 attrs:
21 - 'fill'
22 - 'fill-rule'
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
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>
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 }
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>
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>
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
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>
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>
1 export { default as AppMain } from './AppMain'
2 export { default as Navbar } from './Navbar'
3 export { default as Sidebar } from './Sidebar/index.vue'
4 export { default as TagsView } from './TagsView/index.vue'
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>
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 }
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 })
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 })
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
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 }
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
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
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 }
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 }
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
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 }
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 }
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 }
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 }
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
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 }
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
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 }
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
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 }
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 }