276d8e3e by 赵千

init

0 parents
Showing 106 changed files with 5961 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 }
1 // 获取浏览器便签标题
2 import defaultSettings from '@/settings'
3
4 const title = defaultSettings.title || '厦门市房屋数据服务平台'
5
6 export default function getPageTitle(pageTitle) {
7 if (pageTitle) {
8 return `${pageTitle} - ${title}`
9 }
10 return `${title}`
11 }
1 import axios from 'axios'
2 import { MessageBox, Message } from 'element-ui'
3 import store from '@/store'
4 import Cookies from 'js-cookie';
5 const CONTENT_TYPE = "application/json";
6 // create an axios instance
7 const service = axios.create({
8 baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
9 // withCredentials: true, // send cookies when cross-domain requests
10 headers: {
11 "content-type": CONTENT_TYPE,
12 },
13 timeout: 50000 // request timeout
14 })
15
16 // request interceptor
17 service.interceptors.request.use(
18 config => {
19 config.headers['token'] = Cookies.get('token')
20 return config
21 },
22 error => {
23 console.log(error) // for debug
24 return Promise.reject(error)
25 }
26 )
27
28 // response interceptor
29 service.interceptors.response.use(
30 response => {
31 const res = response.data
32 const { code } = res
33 if (code === undefined) {
34 return res
35 }
36 // if the custom code is not 20000, it is judged as an error.
37 if (code !== 200) {
38 if (code === 600) {
39 Message({
40 message: res.message || 'Error',
41 type: 'error',
42 duration: 5 * 1000
43 })
44 } else if (code === 402) {
45 MessageBox.confirm('登录已失效,可以取消继续留在该页面,或者重新登录', '确定登出', {
46 confirmButtonText: '重新登录',
47 cancelButtonText: '取消',
48 type: 'warning'
49 }).then(() => {
50 store.dispatch('user/resetToken').then(() => {
51 location.reload()// 为了重新实例化vue-router对象 避免bug
52 })
53 })
54 } else if (code === 401) {
55 store.dispatch('user/resetToken').then(() => {
56 location.reload()// 为了重新实例化vue-router对象 避免bug
57 })
58 } else if (code === 403) {
59 if (res.message == 'Bad credentials') {
60 Message({
61 message: '账号、密码输入有误',
62 type: 'error',
63 duration: 5 * 1000
64 })
65 } else {
66 Message({
67 message: res.message,
68 type: 'error',
69 duration: 5 * 1000
70 })
71 }
72
73 }
74 else {
75 Message({
76 message: '服务器异常,请联系管理员',
77 type: 'error',
78 duration: 5 * 1000
79 })
80 }
81 // 401:未登录;
82 return Promise.reject('error')
83 } else {
84 return res
85 }
86
87 },
88 error => {
89 let str = '600'; //新增了token 返回状态码为600
90 if (error.toString().indexOf(str) !== -1) {
91 MessageBox.confirm('登录已失效,可以取消继续留在该页面,或者重新登录', '确定登出', {
92 confirmButtonText: '重新登录',
93 cancelButtonText: '取消',
94 type: 'warning'
95 }).then(() => {
96 store.dispatch('user/resetToken').then(() => {
97 location.reload()// 为了重新实例化vue-router对象 避免bug
98 })
99 })
100 } else {
101 Message({
102 message: '服务器异常,请联系管理员',
103 type: 'error',
104 duration: 5 * 1000
105 })
106 }
107 return Promise.reject(error)
108 }
109 )
110 export default service
1 // setSession
2 export function setSession (key, value, time) {
3 time = time ? Date.parse(new Date()) + time * 1000 * 60 : null
4 let params = {
5 value: value,
6 time: time ? time : null
7 }
8 window.sessionStorage.setItem(key, JSON.stringify(params))
9 }
10 // getSession
11 export function getSession (key) {
12 let params = window.sessionStorage.getItem(key)
13 if (!params) return null
14 params = JSON.parse(params)
15 if (params.time) {
16 if (params.time > Date.parse(new Date)) {
17 return params.value
18 }
19 window.sessionStorage.removeItem(key)
20 return null
21 }
22 return params.value
23 }
24 export function removeSession (key) {
25 return sessionStorage.removeItem(key)
26 }
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * @param {string} path
3 * @returns {Boolean}
4 */
5 export function isExternal (path) {
6 return /^(https?:|mailto:|tel:)/.test(path)
7 }
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div class="errPage-container">
3 <el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
4 返回
5 </el-button>
6 <el-row>
7 <el-col :span="12">
8 <h1 class="text-jumbo text-ginormous">
9 Oops!
10 </h1>
11 gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
12 <h2>你没有权限去该页面</h2>
13 <h6>如有不满请联系你领导</h6>
14 <ul class="list-unstyled">
15 <li>或者你可以去:</li>
16 <li class="link-type">
17 <router-link to="/home">
18 回首页
19 </router-link>
20 </li>
21 <li class="link-type">
22 <a href="https://www.taobao.com/">随便看看</a>
23 </li>
24 <li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
25 </ul>
26 </el-col>
27 <el-col :span="12">
28 <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
29 </el-col>
30 </el-row>
31 <el-dialog :visible.sync="dialogVisible" title="随便看">
32 <img :src="ewizardClap" class="pan-img">
33 </el-dialog>
34 </div>
35 </template>
36
37 <script>
38 import errGif from '@/assets/401_images/401.gif'
39
40 export default {
41 name: 'Page401',
42 data() {
43 return {
44 errGif: errGif + '?' + +new Date(),
45 ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
46 dialogVisible: false
47 }
48 },
49 methods: {
50 back() {
51 if (this.$route.query.noGoBack) {
52 this.$router.push({ path: '/dashboard' })
53 } else {
54 this.$router.go(-1)
55 }
56 }
57 }
58 }
59 </script>
60
61 <style lang="scss" scoped>
62 .errPage-container {
63 width: 800px;
64 max-width: 100%;
65 margin: 100px auto;
66 .pan-back-btn {
67 background: #008489;
68 color: #fff;
69 border: none!important;
70 }
71 .pan-gif {
72 margin: 0 auto;
73 display: block;
74 }
75 .pan-img {
76 display: block;
77 margin: 0 auto;
78 width: 100%;
79 }
80 .text-jumbo {
81 font-size: 60px;
82 font-weight: 700;
83 color: #484848;
84 }
85 .list-unstyled {
86 font-size: 14px;
87 li {
88 padding-bottom: 5px;
89 }
90 a {
91 color: #008489;
92 text-decoration: none;
93 &:hover {
94 text-decoration: underline;
95 }
96 }
97 }
98 }
99 </style>
1 <template>
2 <div class="wscn-http404-container">
3 <div class="wscn-http404">
4 <div class="pic-404">
5 <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
6 <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
7 <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
8 <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
9 </div>
10 <div class="bullshit">
11 <div class="bullshit__oops">OOPS!</div>
12 <div class="bullshit__info">All rights reserved
13 <a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
14 </div>
15 <div class="bullshit__headline">{{ message }}</div>
16 <div class="bullshit__info">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div>
17 <a @click='nextTo' class="bullshit__return-home">回到主页</a>
18 </div>
19 </div>
20 </div>
21 </template>
22
23 <script>
24
25 export default {
26 name: 'Page404',
27 computed: {
28 message() {
29 return 'The webmaster said that you can not enter this page...'
30 }
31 },
32 methods:{
33 nextTo(){
34 this.$router.push({
35 path: this.redirect || '/',
36 query: this.otherQuery
37 })
38 },
39 }
40 }
41 </script>
42
43 <style lang="scss" scoped>
44 .wscn-http404-container{
45 transform: translate(-50%,-50%);
46 position: absolute;
47 top: 40%;
48 left: 50%;
49 }
50 .wscn-http404 {
51 position: relative;
52 width: 1200px;
53 padding: 0 50px;
54 overflow: hidden;
55 .pic-404 {
56 position: relative;
57 float: left;
58 width: 600px;
59 overflow: hidden;
60 &__parent {
61 width: 100%;
62 }
63 &__child {
64 position: absolute;
65 &.left {
66 width: 80px;
67 top: 17px;
68 left: 220px;
69 opacity: 0;
70 animation-name: cloudLeft;
71 animation-duration: 2s;
72 animation-timing-function: linear;
73 animation-fill-mode: forwards;
74 animation-delay: 1s;
75 }
76 &.mid {
77 width: 46px;
78 top: 10px;
79 left: 420px;
80 opacity: 0;
81 animation-name: cloudMid;
82 animation-duration: 2s;
83 animation-timing-function: linear;
84 animation-fill-mode: forwards;
85 animation-delay: 1.2s;
86 }
87 &.right {
88 width: 62px;
89 top: 100px;
90 left: 500px;
91 opacity: 0;
92 animation-name: cloudRight;
93 animation-duration: 2s;
94 animation-timing-function: linear;
95 animation-fill-mode: forwards;
96 animation-delay: 1s;
97 }
98 @keyframes cloudLeft {
99 0% {
100 top: 17px;
101 left: 220px;
102 opacity: 0;
103 }
104 20% {
105 top: 33px;
106 left: 188px;
107 opacity: 1;
108 }
109 80% {
110 top: 81px;
111 left: 92px;
112 opacity: 1;
113 }
114 100% {
115 top: 97px;
116 left: 60px;
117 opacity: 0;
118 }
119 }
120 @keyframes cloudMid {
121 0% {
122 top: 10px;
123 left: 420px;
124 opacity: 0;
125 }
126 20% {
127 top: 40px;
128 left: 360px;
129 opacity: 1;
130 }
131 70% {
132 top: 130px;
133 left: 180px;
134 opacity: 1;
135 }
136 100% {
137 top: 160px;
138 left: 120px;
139 opacity: 0;
140 }
141 }
142 @keyframes cloudRight {
143 0% {
144 top: 100px;
145 left: 500px;
146 opacity: 0;
147 }
148 20% {
149 top: 120px;
150 left: 460px;
151 opacity: 1;
152 }
153 80% {
154 top: 180px;
155 left: 340px;
156 opacity: 1;
157 }
158 100% {
159 top: 200px;
160 left: 300px;
161 opacity: 0;
162 }
163 }
164 }
165 }
166 .bullshit {
167 position: relative;
168 float: left;
169 width: 300px;
170 padding: 30px 0;
171 overflow: hidden;
172 &__oops {
173 font-size: 32px;
174 font-weight: bold;
175 line-height: 40px;
176 color: #1482f0;
177 opacity: 0;
178 margin-bottom: 20px;
179 animation-name: slideUp;
180 animation-duration: 0.5s;
181 animation-fill-mode: forwards;
182 }
183 &__headline {
184 font-size: 20px;
185 line-height: 24px;
186 color: #222;
187 font-weight: bold;
188 opacity: 0;
189 margin-bottom: 10px;
190 animation-name: slideUp;
191 animation-duration: 0.5s;
192 animation-delay: 0.1s;
193 animation-fill-mode: forwards;
194 }
195 &__info {
196 font-size: 13px;
197 line-height: 21px;
198 color: grey;
199 opacity: 0;
200 margin-bottom: 30px;
201 animation-name: slideUp;
202 animation-duration: 0.5s;
203 animation-delay: 0.2s;
204 animation-fill-mode: forwards;
205 }
206 &__return-home {
207 display: block;
208 float: left;
209 width: 110px;
210 height: 36px;
211 background: #1482f0;
212 border-radius: 100px;
213 text-align: center;
214 color: #ffffff;
215 opacity: 0;
216 font-size: 14px;
217 line-height: 36px;
218 cursor: pointer;
219 animation-name: slideUp;
220 animation-duration: 0.5s;
221 animation-delay: 0.3s;
222 animation-fill-mode: forwards;
223 }
224 @keyframes slideUp {
225 0% {
226 transform: translateY(60px);
227 opacity: 0;
228 }
229 100% {
230 transform: translateY(0);
231 opacity: 1;
232 }
233 }
234 }
235 }
236 </style>
1 <template>
2 <div class="dashboard-container">
3 11111111
4 </div>
5 </template>
6
7 <script>
8 export default {
9 name: 'Dashboard',
10 data () {
11 return {
12 currentRole: 'adminDashboard'
13 }
14 },
15 computed: {
16 // ...mapGetters([
17 // 'roles'
18 // ])
19 },
20 created () {
21 // 可实现不同角色配置不同首页
22 // if (!this.roles.includes('admin')) {
23 // this.currentRole = 'editorDashboard'
24 // }
25 },
26 methods: {
27
28 }
29 }
30 </script>
31 <style >
32 .dashboard-container {
33 height: 100%;
34 }
35 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div class='该组件名称'></div>
3 </template>
4 <script>
5 export default {
6 components: {},
7 props: {},
8 name: '该组件名称',
9 data () {
10 return {
11
12 };
13 }
14 }
15 </script>
16 <style scoped lang='scss'>
17 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <script>
2 export default {
3 name: 'AuthRedirect',
4 created() {
5 const hash = window.location.search.slice(1)
6 if (window.localStorage) {
7 window.localStorage.setItem('x-admin-oauth-code', hash)
8 window.close()
9 }
10 },
11 render: function(h) {
12 return h() // avoid warning message
13 }
14 }
15 </script>
1 <template>
2 <div class="jc-component__range">
3 <div v-loading="loading" class="jc-range" :class="rangeStatus ? 'success' : ''">
4 <i @mousedown="rangeMove" :class="rangeStatus ? successIcon : startIcon"></i>
5 {{ rangeStatus ? successText : startText }}
6 </div>
7 </div>
8 </template>
9
10 <script>
11 import * as login from '@/api/login'
12 export default {
13 props: {
14 // 成功之后的函数
15 successFun: {
16 type: Function,
17 },
18 //成功图标
19 successIcon: {
20 type: String,
21 default: 'el-icon-check',
22 },
23 //成功文字
24 successText: {
25 type: String,
26 default: '验证成功',
27 },
28 //开始的图标
29 startIcon: {
30 type: String,
31 default: 'el-icon-right',
32 },
33 //开始的文字
34 startText: {
35 type: String,
36 default: '请按住滑块,拖动到最右边',
37 },
38 //失败之后的函数
39 errorFun: {
40 type: Function,
41 },
42 //或者用值来进行监听
43 status: {
44 type: String,
45 },
46 },
47 data () {
48 return {
49 loading: false,
50 disX: 0,
51 rangeStatus: false,
52 str: {},
53 }
54 },
55 methods: {
56 moveFn (e) {
57 let ele = e.target
58 ele.style.transition = '.5s all'
59 ele.style.transform = 'translateX(0)'
60 this.rangeStatus = false
61 // 执行成功的函数
62 this.errorFun()
63 },
64 //滑块移动
65 rangeMove (e) {
66 this.str = e
67 let ele = e.target
68 let startX = e.clientX
69 let eleWidth = ele.offsetWidth
70 let parentWidth = ele.parentElement.offsetWidth
71 let MaxX = parentWidth - eleWidth
72
73 if (this.rangeStatus) {
74 //不运行
75 return false
76 }
77 document.onmousemove = (e) => {
78 let endX = e.clientX
79 this.disX = endX - startX
80 if (this.disX <= 0) {
81 this.disX = 0
82 }
83 if (this.disX >= MaxX - eleWidth) {
84 //减去滑块的宽度,体验效果更好
85 this.disX = MaxX
86 }
87 ele.style.transition = '.1s all'
88 ele.style.background = 'red!important'
89 ele.style.transform = 'translateX(' + this.disX + 'px)'
90 e.preventDefault()
91 }
92
93 document.onmouseup = async () => {
94
95 if (this.disX !== MaxX) {
96 ele.style.transition = '.5s all'
97 ele.style.transform = 'translateX(0)'
98 // 执行成功的函数
99 this.errorFun && this.errorFun()
100 }
101 else {
102 this.successFun && this.successFun()
103 this.rangeStatus = true
104 this.loading = true
105 if (this.status) {
106 this.$parent[this.status] = true
107 }
108 }
109 document.onmousemove = null
110 document.onmouseup = null
111 }
112 },
113 },
114 }
115 </script>
116
117 <style lang="scss" scoped>
118 @mixin jc-flex {
119 display: flex;
120 justify-content: center;
121 align-items: center;
122 }
123
124 .jc-component__range {
125 .jc-range {
126 background-color: #F7FAFE;
127 border: 1px solid #D8E0E9;
128 position: relative;
129 transition: 1s all;
130 user-select: none;
131 color: #9B9B9B;
132 display: flex;
133 // justify-content: center;
134 padding-left: 50px;
135 align-items: center;
136 height: 40px;
137
138 /*no*/
139 &.success {
140 background-color: #D2F5EF;
141 color: #4caf50;
142 border: 1px solid #99D5D0;
143 font-size: 16px;
144 justify-content: center;
145 padding-left: 0px;
146
147 i {
148 color: #ffffff;
149 background-color: #51CCBF;
150 }
151 }
152
153 i {
154 position: absolute;
155 left: 0;
156 width: 38px;
157 /*no*/
158 height: 40px;
159
160 background-color: #fff;
161 border: 1px solid #d8d8d8;
162 cursor: pointer;
163 font-size: 14px;
164 color: #829ABA;
165 @include jc-flex;
166 }
167 }
168 }
169 </style>
170 <style lang="scss" scoped>
171 /deep/.el-loading-spinner {
172 top: 62%;
173
174 .circular {
175 height: 35px !important;
176 width: 30px !important;
177 }
178 }
179
180 /deep/.el-loading-mask {
181 background-color: rgba(255, 255, 255, 1);
182 }
183 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div class="login">
3 <div class="login-header-title">
4 <img src="./img/login-title.png">
5 </div>
6 <div class="login-content_code">
7 <div class="hidden-bos">
8 <img src='./img/72054ab975174650a84d0a975de6712.png' @click.stop="Codeclick('2')" alt="">
9 </div>
10 <div class="login-form">
11 <h3 class="login-title ">
12 <p>{{ activeName }}</p>
13 </h3>
14 <el-form :model="ruleForm" :rules="rules" size="large" ref="ruleForm">
15 <el-form-item prop="username">
16 <el-input type="text" placeholder="请输入账号/手机号" v-model="ruleForm.username">
17 <svg-icon slot="prefix" icon-class="username" />
18 </el-input>
19 </el-form-item>
20 <el-form-item prop="password">
21 <el-input type="password" placeholder="请输入登录密码" v-model="ruleForm.password">
22 <svg-icon slot="prefix" icon-class="password" />
23 </el-input>
24 </el-form-item>
25 <el-form-item prop="code">
26 <JcRange ref='jcrangeRef' :successFun="verificate_sucess" :errorFun="verificate_error"></JcRange>
27 </el-form-item>
28 <el-form-item class="login-button-form">
29 <el-button type="primary" class="submit-button" @click="submitForm('ruleForm')">登录
30 </el-button>
31 </el-form-item>
32 </el-form>
33 </div>
34 </div>
35 <div class="login-footer">
36 Copyright © 2021-2025闽ICP备20013799号-2
37 </div>
38 </div>
39 </template>
40 <script>
41 import * as login from '@/api/login'
42 import JcRange from './components/JcRange'
43 import dialogBox from '@/components/dialogBox/index';
44 export default {
45 components: {
46 JcRange,
47 dialogBox
48 },
49 data () {
50 return {
51 activeName: '登录',
52 ruleForm: {
53 username: '',
54 password: '',
55 location: '',
56 code: ''
57 },
58 rules: {
59 username: [
60 { required: true, message: '请输入账号/手机号', trigger: 'blur' },
61 ],
62 password: [
63 {
64 required: true,
65 message: '请输入登录密码',
66 trigger: 'blur'
67 }
68 ],
69 code: [
70 {
71 required: true,
72 message: '请滑动完成验证',
73 trigger: 'blur'
74 }
75 ]
76 },
77 loading: false,
78 redirect: undefined,
79 otherQuery: {}
80 }
81 },
82 watch: {
83 $route: {
84 handler: function (route) {
85 const query = route.query
86 if (query) {
87 this.redirect = query.redirect
88 this.otherQuery = this.getOtherQuery(query)
89 }
90 },
91 immediate: true
92 },
93
94 },
95 methods: {
96 async getcode () {
97 try {
98 const res = await login.verifyCode()
99 this.ruleForm.code = res
100 if (this.ruleForm.code !== '') {
101 this.$refs.jcrangeRef.loading = false
102 }
103 } catch (error) {
104 this.$refs.jcrangeRef.loading = true
105 this.$message.error('获取验证码失败')
106 }
107 },
108 handleCode () {
109 this.getcode()
110 },
111 submitForm (formName) {
112 this.$refs[formName].validate(async valid => {
113 if (valid) {
114 this.$store.dispatch('user/login', this.ruleForm).then((res) => {
115 this.$router.push({
116 path: this.redirect || '/',
117 query: this.otherQuery
118 })
119 }).catch(e => {
120 this.$refs.jcrangeRef.disX = 0
121 this.$refs.jcrangeRef.moveFn(this.$refs.jcrangeRef.str)
122 })
123 } else {
124 return false
125 }
126 })
127 },
128 getOtherQuery (query) {
129 return Object.keys(query).reduce((acc, cur) => {
130 if (cur !== 'redirect') {
131 acc[cur] = query[cur]
132 }
133 return acc
134 }, {})
135 },
136
137 // 验证码
138 verificate_sucess () {
139 this.getcode()
140 },
141 verificate_error () {
142 this.ruleForm.code = ''
143 }
144 }
145 }
146 </script>
147 <style lang="scss" scoped>
148 .login {
149 width: 100%;
150 height: 100%;
151 background: url("./img/xinBg.png") no-repeat;
152 background-size: 100% auto;
153 display: flex;
154 align-items: center;
155 flex-direction: column;
156 position: relative;
157
158 .login-header-title {
159 width: 40%;
160 margin: 5% 0 40px 0;
161 min-height: 65px;
162
163 img {
164 width: 100%;
165 }
166 }
167
168 .login-content_code {
169 width: 25%;
170 background-color: #ffffff;
171 box-shadow: 0px 0px 30px 0px rgba(130, 154, 186, 0.2);
172 border-radius: 8px;
173 background-repeat: no-repeat;
174 background-position: top right;
175 position: relative;
176
177 .hidden-box {
178 text-align: right;
179 cursor: pointer;
180 position: absolute;
181 right: 0;
182 top: 0;
183
184 img {
185 width: 45%;
186 }
187 }
188
189 .hidden-bos {
190 text-align: right;
191 cursor: pointer;
192 position: absolute;
193 right: 0;
194 top: 0;
195
196 img {
197 width: 45%;
198 }
199 }
200
201 .login-form {
202 width: 100%;
203 min-height: 385px;
204 padding: 20px 30px 0 30px;
205 box-sizing: border-box;
206
207 .code-item {
208 display: flex;
209 align-items: center;
210 height: 40px;
211 width: 100%;
212
213 img {
214 width: 100px;
215 height: 40px;
216 float: right;
217 cursor: pointer;
218 }
219 }
220
221 /deep/.el-input__prefix {
222 display: flex;
223 align-items: center;
224 justify-content: center;
225 margin-left: 10px;
226 }
227
228 /deep/.el-input__prefix:after {
229 content: "";
230 position: absolute;
231 right: -9px;
232 top: 13px;
233 height: 14px;
234 border-right: 1px solid #d9d9d9;
235 }
236
237 /deep/.el-input__inner {
238 padding-left: 50px;
239 }
240
241 .code-input {
242 position: relative;
243
244 /deep/.el-input__inner {
245 border-right: none;
246 }
247
248 .verification-code {
249 width: 96px;
250 height: 31px;
251 }
252
253 /deep/.el-input-group__append {
254 border-left: none;
255 background: none;
256 }
257 }
258
259 .login-title {
260 margin-bottom: 20px;
261 text-align: center;
262
263 p {
264 font-size: 20px;
265 color: #8a8a8a;
266 width: 25%;
267 margin: 0 auto;
268 border-bottom: 2px solid #0f93f6;
269 padding-bottom: 10px;
270 }
271 }
272 }
273 }
274 }
275 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <script>
2 export default {
3 created() {
4 const { params, query } = this.$route
5 const { path } = params
6 this.$router.replace({ path: '/' + path, query })
7 },
8 render: function(h) {
9 return h() // avoid warning message
10 }
11 }
12 </script>
1 'use strict'
2 const path = require('path')
3 const defaultSettings = require('./src/settings.js')
4
5 function resolve (dir) {
6 return path.join(__dirname, dir)
7 }
8 const name = defaultSettings.title || '管理系统' // page title
9 const port = process.env.port || process.env.npm_config_port || 8888 // dev port
10
11 // All configuration item explanations can be find in https://cli.vuejs.org/config/
12 module.exports = {
13 /**
14 * You will need to set publicPath if you plan to deploy your site under a sub path,
15 * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
16 * then publicPath should be set to "/bar/".
17 * In most cases please use '/' !!!
18 * Detail: https://cli.vuejs.org/config/#publicpath
19 */
20 // 加载资源的路径
21 publicPath: '/',
22 // 设置项目打包生成的文件的存储目录,可以是静态路径也可以是相对路径
23 outputDir: 'dist',
24 // 设置放置打包生成的静态资源 (js、css、img、fonts) 的目录
25 assetsDir: 'static',
26 lintOnSave: process.env.NODE_ENV === 'development',
27 productionSourceMap: false,
28 // webpack开发服务器
29 devServer: {
30 port: port,
31 open: true,
32 disableHostCheck: true,
33 overlay: {
34 warnings: false,
35 errors: true
36 },
37 proxy: {
38 // 以下是演示环境
39 '/dev-api': {
40 target: process.env.VUE_APP_API_BASE_URL,//本机服务
41 ws: false,
42 changeOrigin: true,
43 pathRewrite: {
44 '^/dev-api': '' // 需要rewrite的,
45 }
46 }
47
48 },
49 },
50 css: {
51 extract: true, // 是否使用css分离插件 ExtractTextPlugin
52 sourceMap: false, // 开启 CSS source maps?
53 modules: false // 启用 CSS modules for all css / pre-processor files.
54 },
55 // configureWebpack通过操作对象的形式,来修改默认的webpack配置
56 configureWebpack: {
57 name: name,
58 resolve: {
59 alias: {
60 '@': resolve('src')
61 }
62 },
63 //devtool: '#eval-source-map' //测试
64 },
65 // chainWebpack通过链式编程的形式,来修改默认的webpack配置
66 chainWebpack (config) {
67 config.entry.app = ['babel-polyfill', './src/main.js'];
68 // it can improve the speed of the first screen, it is recommended to turn on preload
69 // it can improve the speed of the first screen, it is recommended to turn on preload
70 config.plugin('preload').tap(() => [
71 {
72 rel: 'preload',
73 // to ignore runtime.js
74 // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
75 fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
76 include: 'initial'
77 }
78 ])
79
80 // when there are many pages, it will cause too many meaningless requests
81 config.plugins.delete('prefetch')
82 config.module
83 .rule('svg')
84 .exclude.add(resolve('src/icons'))
85 .end()
86 config.module
87 .rule('icons')
88 .test(/\.svg$/)
89 .include.add(resolve('src/icons'))
90 .end()
91 .use('svg-sprite-loader')
92 .loader('svg-sprite-loader')
93 .options({
94 symbolId: 'icon-[name]'
95 })
96 .end()
97 config
98 .when(process.env.NODE_ENV !== 'development',
99 config => {
100 config
101 .plugin('ScriptExtHtmlWebpackPlugin')
102 .after('html')
103 .use('script-ext-html-webpack-plugin', [{
104 // `runtime` must same as runtimeChunk name. default is `runtime`
105 inline: /runtime\..*\.js$/
106 }])
107 .end()
108 config
109 .optimization.splitChunks({
110 chunks: 'all',
111 cacheGroups: {
112 libs: {
113 name: 'chunk-libs',
114 test: /[\\/]node_modules[\\/]/,
115 priority: 10,
116 chunks: 'initial' // only package third parties that are initially dependent
117 },
118 elementUI: {
119 name: 'chunk-elementUI', // split elementUI into a single package
120 priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
121 test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
122 },
123 commons: {
124 name: 'chunk-commons',
125 test: resolve('src/components'), // can customize your rules
126 minChunks: 3, // minimum common number
127 priority: 5,
128 reuseExistingChunk: true
129 }
130 }
131 })
132 config.optimization.runtimeChunk('single')
133 }
134 )
135 }
136 }