init
This commit is contained in:
@@ -4,16 +4,13 @@ module.exports = {
|
|||||||
es2021: true
|
es2021: true
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
'plugin:vue/essential',
|
'plugin:vue/vue3-essential',
|
||||||
'standard'
|
'standard'
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 12,
|
ecmaVersion: 12,
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
plugins: [
|
|
||||||
'vue'
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
eqeqeq: 0, // 关闭必须使用全等
|
eqeqeq: 0, // 关闭必须使用全等
|
||||||
'no-extend-native': 0,
|
'no-extend-native': 0,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "sn",
|
"name": "cn",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -14,13 +14,14 @@
|
|||||||
"element-plus": "^1.0.2-beta.44",
|
"element-plus": "^1.0.2-beta.44",
|
||||||
"lib-flexible": "^0.3.2",
|
"lib-flexible": "^0.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"moment-timezone": "^0.5.33",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"postcss-px2rem-exclude": "0.0.6",
|
"postcss-px2rem-exclude": "0.0.6",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"sass-resources-loader": "^2.2.1",
|
"sass-resources-loader": "^2.2.1",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vue-grid-layout": "^2.3.12",
|
"vue-grid-layout": "^2.3.12",
|
||||||
"vue-i18n": "^8.24.4",
|
"vue-i18n": "^9.1.6",
|
||||||
"vue-router": "^4.0.8",
|
"vue-router": "^4.0.8",
|
||||||
"vuex": "^4.0.1"
|
"vuex": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
autoprefixer: {},
|
autoprefixer: {}
|
||||||
/* 'postcss-px2rem-exclude': {
|
/* 'postcss-px2rem-exclude': {
|
||||||
remUnit: 16,
|
remUnit: 16,
|
||||||
exclude: /node_modules/i
|
exclude: /node_modules/i
|
||||||
|
|||||||
1
public/config.json
Normal file
1
public/config.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"baseUrl": "http://192.168.44.53:8090/", "version": "2.0.2021.05.11.19.43"}
|
||||||
10
src/App.vue
10
src/App.vue
@@ -4,7 +4,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App'
|
name: 'App',
|
||||||
|
setup () {
|
||||||
|
get(`${process.env.BASE_URL}config.json?Timestamp=${new Date().getTime()}`).then(config => {
|
||||||
|
axios.defaults.baseURL = config.baseUrl
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div></div>
|
<div>
|
||||||
|
<el-input v-model="username"></el-input>
|
||||||
|
<el-input v-model="pin"></el-input>
|
||||||
|
<button type="button" @click="login">Login</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
import { post } from '@/utils/http'
|
||||||
|
import bus from '@/utils/bus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login'
|
name: 'Login',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
username: 'admin',
|
||||||
|
pin: 'Nezha2021'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['loginSuccess']),
|
||||||
|
login () {
|
||||||
|
post('sys/login', { username: this.username, pin: this.pin }).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.loginSuccess(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
@import './font/iconfont';
|
@import './font/iconfont';
|
||||||
@import './theme';
|
@import './theme';
|
||||||
@import './common';
|
@import './common';
|
||||||
|
@import './rightBoxCommon';
|
||||||
|
@import './tableCommon';
|
||||||
312
src/assets/css/rightBoxCommon.scss
Normal file
312
src/assets/css/rightBoxCommon.scss
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
.right-box, .right-sub-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 50px;
|
||||||
|
padding: 0;
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
width: 700px;
|
||||||
|
box-shadow: 0 0 5px #ccc;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 410;
|
||||||
|
|
||||||
|
.el-date-editor {
|
||||||
|
.el-input__inner {
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 60px;
|
||||||
|
padding: 0 20px;
|
||||||
|
border-bottom: 1px solid $--right-box-border-color;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.header__title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.header__operation {
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box__container {
|
||||||
|
.right-box-form{
|
||||||
|
width: calc(100% - 60px);
|
||||||
|
}
|
||||||
|
height: calc(100% - 130px);
|
||||||
|
padding: 0 30px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
padding: 5px 70px 4px 15px;
|
||||||
|
}
|
||||||
|
.container__form-width.container__form{
|
||||||
|
.input-box {
|
||||||
|
.el-textarea {
|
||||||
|
.el-textarea__inner {
|
||||||
|
width: 530px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 5px 70px 4px 10px;
|
||||||
|
}
|
||||||
|
.el-input__count {
|
||||||
|
right: -40px;
|
||||||
|
line-height: 29px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item__content {
|
||||||
|
.el-input-group--prepend {
|
||||||
|
width: 626px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.input-box {
|
||||||
|
.el-textarea {
|
||||||
|
.el-textarea__inner {
|
||||||
|
width: 517px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 5px 70px 4px 10px;
|
||||||
|
}
|
||||||
|
.el-input__count {
|
||||||
|
right: -40px;
|
||||||
|
line-height: 29px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form-row-item {
|
||||||
|
.input-box {
|
||||||
|
.el-textarea {
|
||||||
|
.el-textarea__inner {
|
||||||
|
width: 466px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 5px 70px 4px 10px;
|
||||||
|
}
|
||||||
|
.el-input__count {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item {
|
||||||
|
.el-input__count {
|
||||||
|
line-height: 29px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item {
|
||||||
|
.el-input--small.not-fixed-height {
|
||||||
|
height: 32px;
|
||||||
|
.el-input__count {
|
||||||
|
line-height: 29px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-input__inner, .el-textarea__inner {
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: $--border-radius-primary;
|
||||||
|
border: 1px solid $--right-box-border-color;
|
||||||
|
}
|
||||||
|
.el-textarea__inner {
|
||||||
|
padding: 5px 70px 4px 15px;
|
||||||
|
}
|
||||||
|
.el-form {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
.el-form-item__label{
|
||||||
|
padding-bottom: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner:hover {
|
||||||
|
border-color: darken($--right-box-border-color, 10%);
|
||||||
|
}
|
||||||
|
.el-input__inner:focus {
|
||||||
|
border-color: darken($--right-box-border-color, 20%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus, .el-message-box__input input.invalid, .el-message-box__input input.invalid:focus {
|
||||||
|
border-color: #F56C6C
|
||||||
|
}
|
||||||
|
|
||||||
|
.form__sub-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
background-color: #F6F6F6;
|
||||||
|
}
|
||||||
|
/* 虚线框类型的form-item */
|
||||||
|
.form__dotted-item {
|
||||||
|
padding: 10px 10px 6px 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px dashed $--border-color-primary;
|
||||||
|
border-radius: $--border-radius-primary;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.el-form-item__label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.form__labels-label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form__create-btn {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 300px;
|
||||||
|
height: 28px;
|
||||||
|
border: 1px solid var(--theme-color-light-71);
|
||||||
|
border-radius: $--border-radius-primary;
|
||||||
|
background-color: var(--theme-color-light-98);
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--theme-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form__flex-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.one-third-form-item-left{
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(50% - 5px);
|
||||||
|
}
|
||||||
|
.one-third-form-item-right{
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(50% - 5px);
|
||||||
|
}
|
||||||
|
.form-item--half-width-other-two{
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
.form-item--half-width-other{
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box__footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 70px;
|
||||||
|
box-shadow: -3px 0 8px -3px rgba(205,205,205,0.77);
|
||||||
|
|
||||||
|
.footer__btn {
|
||||||
|
margin: 0 15px;
|
||||||
|
height: 30px;
|
||||||
|
min-width: 74px;
|
||||||
|
padding: 0 15px;
|
||||||
|
color: white;
|
||||||
|
background-color: var(--theme-color);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color linear .2s, color linear .1s;
|
||||||
|
}
|
||||||
|
.footer__btn:hover:not(.footer__btn--disabled) {
|
||||||
|
background-color: var(--theme-color-light-20);
|
||||||
|
}
|
||||||
|
.footer__btn--light {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid $--border-color-primary;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.footer__btn.footer__btn--light:hover:not(.footer__btn--disabled) {
|
||||||
|
background-color: white;
|
||||||
|
border-color: var(--theme-color-light-50);
|
||||||
|
color: var(--theme-color);
|
||||||
|
}
|
||||||
|
.footer__btn--disabled {
|
||||||
|
opacity: .6;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 隐藏label新增按钮处级联选择器的input */
|
||||||
|
.hide-casc-input {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.hide-input {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 300px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label__multi-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.right-box__select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.right-box-select-dropdown {
|
||||||
|
width: 625px;
|
||||||
|
}
|
||||||
|
.limit-height .el-cascader-menu {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-items--half-width-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.form-item--half-width {
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nz-icon-minus-position {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
height: 100%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
justify-content: space-between;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.form-item--end-with-btn { // 末尾留出btn宽度空间的form item
|
||||||
|
|
||||||
|
}
|
||||||
|
.el-form-item__content .el-autocomplete .el-input-group {
|
||||||
|
vertical-align: unset;
|
||||||
|
}
|
||||||
381
src/assets/css/tableCommon.scss
Normal file
381
src/assets/css/tableCommon.scss
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/*列表table通用样式*/
|
||||||
|
.list-page {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
|
||||||
|
.main-list {
|
||||||
|
background-color: white;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.main-container {
|
||||||
|
padding: 10px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
&>div {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.main-list.main-list-with-sub {
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
.main-modal {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.top-tools {
|
||||||
|
display: flex;
|
||||||
|
align-items : center;
|
||||||
|
position: relative;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 20px;
|
||||||
|
|
||||||
|
&.top-tools--sub {
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 70px 0 15px;
|
||||||
|
height: 44px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #E6EAED;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-right {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.top-tool-left {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.top-tool-btn-group {
|
||||||
|
display: flex;
|
||||||
|
.top-tool-btn:not(:last-of-type):not(:first-of-type) {
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.top-tool-btn:first-of-type:not(:last-of-type) {
|
||||||
|
border-radius: $--button-border-radius 0 0 $--button-border-radius;
|
||||||
|
}
|
||||||
|
.top-tool-btn:last-of-type:not(:first-of-type) {
|
||||||
|
border-radius: 0 $--button-border-radius $--button-border-radius 0;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-tool-btn {
|
||||||
|
height: 32px;
|
||||||
|
width: 36px;
|
||||||
|
border: 1px solid $--border-color-primary;
|
||||||
|
outline: none;
|
||||||
|
border-radius: $--button-border-radius;
|
||||||
|
background-color: $--button-gray-background-color;
|
||||||
|
transition: background-color linear .1s;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $--button-gray-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-tool-btn.top-tool-btn--text {
|
||||||
|
padding: 0 8px;
|
||||||
|
width: unset;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.top-tool-btn:hover:not(.cn-btn-disabled) {
|
||||||
|
background-color: $--button-gray-hover-background-color;
|
||||||
|
}
|
||||||
|
.top-tool-btn:focus:not(.cn-btn-disabled), .top-tool-btn.is-focus {
|
||||||
|
background-color: $--button-gray-hover-background-color;
|
||||||
|
border: 1px solid #FBCEA4 !important;
|
||||||
|
i {
|
||||||
|
color: $--button-gray-active-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-tool-btn--delete.top-tool-btn:focus:not(.cn-btn-disabled) {
|
||||||
|
background-color: $--button-gray-hover-background-color;
|
||||||
|
border-color: #FFC4B9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #F0745A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-tool-btn--dropdown {
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
min-width: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-tools--sub {
|
||||||
|
.top-tool-left {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.sub-list-title {
|
||||||
|
width: 200px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #202F3F;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.sub-list-tabs {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.sub-list-tab {
|
||||||
|
height: 100%;
|
||||||
|
width: 120px;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 40px;
|
||||||
|
|
||||||
|
&.sub-list-tab--active {
|
||||||
|
border-bottom: 2px solid $--color-primary;
|
||||||
|
color: #FA901C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 上滑resize工具条 */
|
||||||
|
.sub-list-resize, .sub-list-resize-copy {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 9px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: inset 0 1px 0 0 #BEBEBE;
|
||||||
|
background-color: #e6eaed;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none;
|
||||||
|
color: #5f6368;
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
.sub-list-window-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 14px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
height: 44px;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.window-control-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.window-control-btn>i {
|
||||||
|
color: #999999;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
.window-control-btn>i:hover {
|
||||||
|
color: $--color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 上滑resize工具条--end */
|
||||||
|
.cn-table {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: auto;
|
||||||
|
height: calc(100% - 58px);
|
||||||
|
|
||||||
|
.el-table:not(.chart-table) {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
border: 1px solid $--right-box-border-color;
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
|
.caret-wrapper {
|
||||||
|
height: 23px;
|
||||||
|
.sort-caret.ascending {
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
.sort-caret.descending {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table-column--selection {
|
||||||
|
width: 55px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid $--right-box-border-color;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
border-color: $--right-box-border-color;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: #F9F9F9;
|
||||||
|
}
|
||||||
|
.el-table__header th:first-of-type {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
.gutter {
|
||||||
|
position: fixed;
|
||||||
|
right: 31px;
|
||||||
|
height: 49px;
|
||||||
|
border-bottom: 1px solid $--right-box-border-color;
|
||||||
|
background-color: white;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body tr:hover>td, .el-table__body tr.hover-row.current-row>td,
|
||||||
|
.el-table__body tr.hover-row.el-table__row--striped.current-row>td,
|
||||||
|
.el-table__body tr.hover-row.el-table__row--striped>td, .el-table__body tr.hover-row>td {
|
||||||
|
background-color: var(--theme-color-light-96) !important;
|
||||||
|
}
|
||||||
|
.table-operation-title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table-operation-items {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
.table-operation-item {
|
||||||
|
display: flex;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: $--button-border-radius;
|
||||||
|
outline: none;
|
||||||
|
transition: background-color linear .1s;
|
||||||
|
}
|
||||||
|
.table-operation-item.table-operation-item--disable{
|
||||||
|
border: 1px solid $--border-color-primary;
|
||||||
|
background-color: #DEDEDE;
|
||||||
|
opacity: 0.6;
|
||||||
|
outline: none;
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&>.table-operation-item {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: $--button-border-radius;
|
||||||
|
background-color: $--button-primary-background-color;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s;
|
||||||
|
i {
|
||||||
|
color: $--button-primary-color;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&>.table-operation-item:hover {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
.table-operation-item.table-operation-item--more {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid $--border-color-primary;
|
||||||
|
width: 30px;
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* start--覆盖el-table边框、gutter等样式 */
|
||||||
|
.el-table__body-wrapper, .el-table__fixed-body-wrapper {
|
||||||
|
box-shadow: 1px 0 $--right-box-border-color;
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-table__body-wrapper .is-hidden, .el-table__header-wrapper .is-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.cn-table {
|
||||||
|
.el-table--border td {
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
/* 最后一列用box-shadow模拟边框 */
|
||||||
|
.el-table:not(.no-operation):not(.chart-table).el-table--border .el-table__body-wrapper td:nth-last-child(2) {
|
||||||
|
box-shadow: 1px 0 $--right-box-border-color;
|
||||||
|
}
|
||||||
|
.el-table:not(.no-operation):not(.chart-table).el-table--border .el-table__header-wrapper th:nth-last-child(3) {
|
||||||
|
border-right: none !important;
|
||||||
|
box-shadow: 1px 0 $--right-box-border-color;
|
||||||
|
}
|
||||||
|
.el-table__fixed-body-wrapper {
|
||||||
|
td:not(.is-hidden) {
|
||||||
|
border-left: 1px solid $--right-box-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-table__fixed-header-wrapper {
|
||||||
|
th:not(.is-hidden) {
|
||||||
|
border-left: 1px solid $--right-box-border-color;
|
||||||
|
}
|
||||||
|
th:last-of-type {
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-table--border:not(.chart-table)::after, .el-table--group:not(.chart-table)::after {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* end--覆盖el-table边框、gutter等样式 */
|
||||||
|
.pagination-bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 8px;
|
||||||
|
height: 48px;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.operation-dropdown-text {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
}
|
||||||
|
.click-search-dropdown {
|
||||||
|
width: calc(100% - 300px) !important;
|
||||||
|
left: 270px !important;
|
||||||
|
margin-top: -3px !important;
|
||||||
|
box-shadow: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 0;
|
||||||
|
border-color: #c7c7c7;
|
||||||
|
|
||||||
|
.popper__arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.el-cascader-menu__list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-popper.el-cascader__dropdown.click-search-dropdown::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
height: 1px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
/*** 定义自定义变量和重写element-ui变量 ***/
|
/*** 定义自定义变量和重写element-ui变量 ***/
|
||||||
|
|
||||||
/** 自定义变量 **/
|
|
||||||
|
|
||||||
/** 重写element-ui变量 **/
|
/** 重写element-ui变量 **/
|
||||||
|
|
||||||
$--color-primary: #0091ff; // 主题色
|
$--color-primary: #0091ff; // 主题色
|
||||||
|
|
||||||
/* menu相关 */
|
/* menu相关 */
|
||||||
@@ -11,6 +10,55 @@ $--menu-hover-background-color: #000C18; // menu背景色
|
|||||||
$--menu-item-font-color: #BEBEBE; // menu字色
|
$--menu-item-font-color: #BEBEBE; // menu字色
|
||||||
$--menu-item-hover-fill: $--color-primary; // menu鼠标悬浮、激活时背景色
|
$--menu-item-hover-fill: $--color-primary; // menu鼠标悬浮、激活时背景色
|
||||||
|
|
||||||
|
/** 自定义变量 **/
|
||||||
|
:root {
|
||||||
|
--theme-color: #FA901C; // 默认主题色,下方深化、浅化的色值默认值是写死的;用户自定义修改主题色后,由js计算新值
|
||||||
|
--theme-color-dark-10: #E18219; // 默认主题色 深10%
|
||||||
|
--theme-color-light-10: #FA9B33; // 默认主题色 浅10%
|
||||||
|
--theme-color-light-20: #FBA649; // 默认主题色 浅20%
|
||||||
|
--theme-color-light-30: #FBB160; // 默认主题色 浅30%
|
||||||
|
--theme-color-light-40: #FBBC77; // 默认主题色 浅40%
|
||||||
|
--theme-color-light-50: #FCC88D; // 默认主题色 浅50%
|
||||||
|
--theme-color-light-60: #FCD4A4; // 默认主题色 浅60%
|
||||||
|
--theme-color-light-71: #FFDFBD; // 默认主题色 浅71%
|
||||||
|
--theme-color-light-80: #FFEAD2; // 默认主题色 浅80%
|
||||||
|
--theme-color-light-90: #FFF5E8; // 默认主题色 浅90%
|
||||||
|
--theme-color-light-96: #FFFBF6; // 默认主题色 浅90%
|
||||||
|
--theme-color-light-98: #FFFCF8; // 默认主题色 浅98%
|
||||||
|
}
|
||||||
|
|
||||||
|
$--border-color-primary: #DEDEDE;
|
||||||
|
$--border-radius-primary: 2px;
|
||||||
|
|
||||||
|
$--right-box-border-color: #E7EAED;
|
||||||
|
|
||||||
|
/* 按钮 */
|
||||||
|
$--button-border-radius: $--border-color-primary; // 按钮圆角
|
||||||
|
|
||||||
|
$--button-primary-color: #FFF; // 普通按钮字色
|
||||||
|
$--button-primary-background-color: $--color-primary; // 普通按钮背景色
|
||||||
|
$--button-hover-tint-percent: 20%; // 非灰色按钮在鼠标hover时背景色变浅的幅度
|
||||||
|
$--button-active-shade-percent: 0; // 非灰色按钮在focus时背景色变深的幅度
|
||||||
|
|
||||||
|
$--button-gray-color: #666; // 灰色按钮字色
|
||||||
|
$--button-gray-hover-color: $--button-gray-color; // 灰色按钮hover字色
|
||||||
|
$--button-gray-active-color: $--color-primary; // 灰色按钮focus字色
|
||||||
|
$--button-gray-background-color: #F9F9F9; // 灰色按钮背景色
|
||||||
|
$--button-gray-hover-background-color: #FFF; // 灰色按钮hover背景色
|
||||||
|
$--button-gray-active-background-color: $--button-gray-hover-background-color; // 灰色按钮focus背景色
|
||||||
|
$--button-gray-border-color: $--border-color-primary; // 灰色按钮边框色
|
||||||
|
$--button-gray-hover-border-color: $--button-gray-border-color; // 灰色按钮hover边框色
|
||||||
|
$--button-gray-active-border-color-tint-percent: 30%; // 灰色按钮在focus时边框色相对于主题色变浅的幅度
|
||||||
|
|
||||||
|
$--color-danger: #DE5D3F; //全局警告色红色
|
||||||
|
$--color-success: #23BF9A; //全局正常色绿色
|
||||||
|
$--color-warning: $--color-primary; //全局警告橙色
|
||||||
|
$--color-suspended: #9e9c98; //全局停用色灰色
|
||||||
|
$--color-monitor: #98AEC5; //全局停用色灰色
|
||||||
|
|
||||||
/** 改变 icon 字体路径变量,并引入element-ui变量文件 **/
|
/** 改变 icon 字体路径变量,并引入element-ui变量文件 **/
|
||||||
$--font-path: '~element-plus/lib/theme-chalk/fonts';
|
$--font-path: '~element-plus/lib/theme-chalk/fonts';
|
||||||
@import "~element-plus/packages/theme-chalk/src/index";
|
@import "~element-plus/packages/theme-chalk/src/index";
|
||||||
|
:export {
|
||||||
|
themeColor: $--color-primary;
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="left-menu">
|
<div class="left-menu">
|
||||||
<el-menu :collapse="isShrink" active-text-color="#ffffff" class="header-logo" text-color="#ffffff">
|
<el-menu
|
||||||
|
:collapse="isShrink"
|
||||||
|
active-text-color="#ffffff"
|
||||||
|
class="header-logo"
|
||||||
|
text-color="#ffffff">
|
||||||
<el-menu-item index="logo">
|
<el-menu-item index="logo">
|
||||||
<div id="home-to-overview" class="logo link">
|
<div id="home-to-overview" class="logo link">
|
||||||
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>
|
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>
|
||||||
@@ -8,26 +12,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<el-menu :collapse="isShrink" :default-active="route" class="menu-list" mode="vertical" unique-opened @select="jump">
|
<el-menu
|
||||||
|
:collapse="isShrink"
|
||||||
|
:default-active="route"
|
||||||
|
class="menu-list"
|
||||||
|
mode="vertical"
|
||||||
|
unique-opened
|
||||||
|
@select="jump">
|
||||||
<template v-for="(menu, index) in menuList">
|
<template v-for="(menu, index) in menuList">
|
||||||
<el-submenu v-if="menu.children && menu.children.length > 0" :key="index" :index="`${index}`">
|
<el-submenu
|
||||||
|
v-if="menu.children && menu.children.length > 0"
|
||||||
|
:key="index"
|
||||||
|
:index="`${index}`">
|
||||||
<template #title>
|
<template #title>
|
||||||
<i :class="menu.icon"></i>
|
<i :class="menu.icon"></i>
|
||||||
<span>{{menu.name}}</span>
|
<span>{{menu.name}}</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="(secondMenu, secondIndex) in menu.children">
|
<template v-for="(secondMenu, secondIndex) in menu.children">
|
||||||
<template v-if="secondMenu.children && secondMenu.children.length > 0">
|
<template v-if="secondMenu.children && secondMenu.children.length > 0">
|
||||||
<el-submenu :key="secondIndex" :index="`${index}-${secondIndex}`">
|
<el-submenu
|
||||||
|
:key="secondIndex"
|
||||||
|
:index="`${index}-${secondIndex}`">
|
||||||
<span slot="title" class="data-column__span">{{secondMenu.name}}</span>
|
<span slot="title" class="data-column__span">{{secondMenu.name}}</span>
|
||||||
<el-menu-item v-for="(thirdMenu, thirdIndex) in secondMenu.children" :key="`${index}-${secondIndex}-${thirdIndex}`" :index="thirdMenu.route">{{thirdMenu.name}}</el-menu-item>
|
<el-menu-item
|
||||||
|
v-for="(thirdMenu, thirdIndex) in secondMenu.children"
|
||||||
|
:key="`${index}-${secondIndex}-${thirdIndex}`"
|
||||||
|
:index="thirdMenu.route">
|
||||||
|
{{thirdMenu.name}}
|
||||||
|
</el-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :key="secondIndex" :index="secondMenu.route">{{secondMenu.name}}</el-menu-item>
|
<el-menu-item
|
||||||
|
:key="secondIndex"
|
||||||
|
:index="secondMenu.route">
|
||||||
|
{{secondMenu.name}}
|
||||||
|
</el-menu-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
<el-menu-item v-else :key="index + 'a'" :index="menu.route">
|
<el-menu-item
|
||||||
|
v-else
|
||||||
|
:key="index + 'a'"
|
||||||
|
:index="menu.route">
|
||||||
<i :class="menu.icon"></i>
|
<i :class="menu.icon"></i>
|
||||||
<span slot="title" class="data-column__span">{{menu.name}}</span>
|
<span slot="title" class="data-column__span">{{menu.name}}</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@@ -126,11 +153,16 @@ export default {
|
|||||||
.el-menu-item.is-active {
|
.el-menu-item.is-active {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
// el-submenu active且open背景色
|
|
||||||
|
|
||||||
|
// el-submenu active且open背景色
|
||||||
.el-submenu__title:not(.is-active):hover, .el-menu-item:not(.is-active):hover, .el-menu-item:not(.is-active):focus {
|
.el-submenu__title:not(.is-active):hover, .el-menu-item:not(.is-active):hover, .el-menu-item:not(.is-active):focus {
|
||||||
background-color: mix($--color-white, $--menu-background-color, 7%) !important;
|
background-color: mix($--color-white, $--menu-background-color, 7%) !important;
|
||||||
}
|
}
|
||||||
|
.is-active.is-opened {
|
||||||
|
.el-submenu__title, .el-menu-item:not(.is-active) {
|
||||||
|
background-color: $--menu-hover-background-color !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
.el-menu-item {
|
.el-menu-item {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
200
src/components/rightBox/settings/UserBox.vue
Normal file
200
src/components/rightBox/settings/UserBox.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<div v-clickoutside="{object: editObject, func: esc}" class="right-box right-box-user">
|
||||||
|
<div class="right-box__header">
|
||||||
|
<div class="header__title">{{editObject.id ? $t('config.user.editUser') : $t('config.user.createUser')}}</div>
|
||||||
|
<div class="header__operation">
|
||||||
|
<span v-cancel="{object: editObject, func: esc}"><i class="cn-icon cn-icon-close"></i></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-box__container">
|
||||||
|
<div class="container__form">
|
||||||
|
<el-form ref="userForm" :model="editObject" :rules="editObject.id ? rules2 : rules" label-position="top" label-width="120px">
|
||||||
|
<!--name-->
|
||||||
|
<el-form-item :label="$t('config.user.name')" prop="name">
|
||||||
|
<el-input id="account-input-name" v-model="editObject.name" :disabled="editObject.username==='admin' && editObject.id === 1"
|
||||||
|
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--username-->
|
||||||
|
<el-form-item :label="$t('config.user.username')" prop="username">
|
||||||
|
<el-input id="account-input-username" v-model="editObject.username" :disabled="editObject.username==='admin' && editObject.id === 1"
|
||||||
|
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--password-->
|
||||||
|
<el-form-item :label="$t('config.user.pin')" prop="pin">
|
||||||
|
<el-input id="account-input-password" v-model="editObject.pin" maxlength="64" placeholder=""
|
||||||
|
show-word-limit size="small" type="password" @blur="pinBlur" autocomplete="new-password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--pinChange-->
|
||||||
|
<el-form-item :label="$t('config.user.confirmPin')" label-width="200px" prop="pinChange">
|
||||||
|
<el-input id="account-input-pinChange" v-model="editObject.pinChange" maxlength="64" placeholder=""
|
||||||
|
show-word-limit size="small" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--email-->
|
||||||
|
<el-form-item label="E-mail" prop="email">
|
||||||
|
<el-input id="account-input-email" v-model="editObject.email" maxlength="64" show-word-limit placeholder="" size="small" type="text"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--mobile-->
|
||||||
|
<el-form-item :label="$t('config.user.mobile')" prop="mobile">
|
||||||
|
<el-input id="account-input-mobile" v-model.number="editObject.mobile" maxlength="64" show-word-limit placeholder="" size="small" type="text"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!--roles-->
|
||||||
|
<el-form-item :label="$t('config.user.roles')" prop="roleIds">
|
||||||
|
<el-select id="account-input-roleIds"
|
||||||
|
v-model="editObject.roleIds"
|
||||||
|
:disabled="(editObject.username === 'admin') && editObject.id === 1"
|
||||||
|
class="right-box__select"
|
||||||
|
clearable
|
||||||
|
collapse-tags
|
||||||
|
placeholder=""
|
||||||
|
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||||
|
size="small"
|
||||||
|
@change="()=>{ this.$forceUpdate() }">
|
||||||
|
<template v-for="role in roleData" :key="role.id">
|
||||||
|
<el-option :label="role.name" :value="role.id"></el-option>
|
||||||
|
</template>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<!--enable-->
|
||||||
|
<el-form-item :label="$t('config.user.enable')">
|
||||||
|
<el-switch id="account-input-status" v-model="editObject.status" :disabled="isCurrentUser(editObject.username) || (editObject.username==='admin' && editObject.id==1) " active-color="#ee9d3f" active-value="1"
|
||||||
|
inactive-value="0">
|
||||||
|
</el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="editObject.id" :label="$t('config.user.createTime')">
|
||||||
|
<div class="right-box-form-content-txt">{{editObject.createAt}}</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-box__footer">
|
||||||
|
<button id="asset-edit-cancel" v-cancel="{object: editObject, func: esc}" class="footer__btn footer__btn--light">
|
||||||
|
<span>{{$t('overall.cancel')}}</span>
|
||||||
|
</button>
|
||||||
|
<button id="asset-edit-save" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
|
||||||
|
<span>{{$t('overall.save')}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import rightBoxMixin from '@/mixins/rightBox'
|
||||||
|
import { get, post, put } from '@/utils/http'
|
||||||
|
export default {
|
||||||
|
name: 'UserBox',
|
||||||
|
mixins: [rightBoxMixin],
|
||||||
|
data () {
|
||||||
|
const validatePin = (rule, value, callback) => { // 确认密码的二次校验
|
||||||
|
if (value === '' && this.editObject.pin) {
|
||||||
|
callback(new Error(this.$t('config.user.inputConfirmPin')))
|
||||||
|
} else if (value !== this.editObject.pin) {
|
||||||
|
callback(new Error(this.$t('config.user.confirmPinErr')))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: 'sys/user',
|
||||||
|
rules: { // 表单校验规则
|
||||||
|
name: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
pin: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
pinChange: [
|
||||||
|
{ validator: validatePin, trigger: 'blur' },
|
||||||
|
{ required: true, message: '', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
roleIds: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ type: 'email', message: this.$t('validate.email') }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
rules2: { // 表单校验规则
|
||||||
|
username: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
pinChange: [
|
||||||
|
{ validator: validatePin, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
roleIds: [
|
||||||
|
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ type: 'email', message: this.$t('validate.email') }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup () {
|
||||||
|
let roleData = []
|
||||||
|
const getRoleData = async () => {
|
||||||
|
await get('sys/role?pageSize=-1').then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
roleData = response.data.list
|
||||||
|
} else {
|
||||||
|
this.$message.error('load roles faild')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
roleData,
|
||||||
|
getRoleData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isCurrentUser () {
|
||||||
|
return function (username) {
|
||||||
|
return localStorage.getItem('cn-username') === username
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 密码失去焦点 检验确认密码 */
|
||||||
|
pinBlur () {
|
||||||
|
if (this.editObject.pin && this.editObject.pinChange) {
|
||||||
|
this.$refs.userForm.validateField('pinChange')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
save () {
|
||||||
|
if (this.blockOperation.save) { return }
|
||||||
|
this.blockOperation.save = true
|
||||||
|
|
||||||
|
this.$refs.userForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.editObject.id) {
|
||||||
|
put(this.url, this.editObject).then(res => {
|
||||||
|
this.blockOperation.save = false
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||||
|
this.esc(true)
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
post(this.url, this.editObject).then(res => {
|
||||||
|
this.blockOperation.save = false
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||||
|
this.esc(true)
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.blockOperation.save = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
95
src/components/table/CnDataList.vue
Normal file
95
src/components/table/CnDataList.vue
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="from" class="list-page">
|
||||||
|
<!-- 主页面 -->
|
||||||
|
<div class="main-list">
|
||||||
|
<!-- 顶部工具栏 -->
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="top-tools">
|
||||||
|
<div class="top-tool-left" style="min-width: 300px">
|
||||||
|
<slot name="top-tool-left"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="top-tool-right">
|
||||||
|
<div v-if="showLayout.indexOf('searchInput') > -1" class="top-tool-search margin-r-20"></div>
|
||||||
|
<slot name="top-tool-right"></slot>
|
||||||
|
<button v-if="showLayout.indexOf('elementSet') > -1" class="top-tool-btn"
|
||||||
|
type="button" @click="tools.showCustomTableTitle = true">
|
||||||
|
<i class="cn-icon-gear cn-icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cn-table">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div class="cn-pagination">
|
||||||
|
<slot name="pagination"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 自定义table列 -->
|
||||||
|
<transition name="el-zoom-in-top">
|
||||||
|
<column-customize
|
||||||
|
v-if="tools.showCustomTableTitle"
|
||||||
|
:tableId="tableId"
|
||||||
|
ref="customTableTitle"
|
||||||
|
:custom-table-title="customTableTitle"
|
||||||
|
:original-table-title="tableTitle"
|
||||||
|
@close="tools.showCustomTableTitle = false"
|
||||||
|
@update="updateCustomTableTitle"
|
||||||
|
></column-customize>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import columnCustomize from '@/components/table/ColumnCustomize'
|
||||||
|
import { fromRoute } from '@/utils/constants'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'cnDataList',
|
||||||
|
components: {
|
||||||
|
columnCustomize
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
from: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
tableId: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
tableTitle: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
customTableTitle: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: Array,
|
||||||
|
default () { return [] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
fromRoute: fromRoute,
|
||||||
|
tools: {
|
||||||
|
showCustomTableTitle: false // 自定义列弹框是否显示
|
||||||
|
},
|
||||||
|
showLayout: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateCustomTableTitle (custom) {
|
||||||
|
this.$emit('update:customTableTitle', custom)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
layout: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
this.showLayout = [...n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
172
src/components/table/ColumnCustomize.vue
Normal file
172
src/components/table/ColumnCustomize.vue
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pop-custom">
|
||||||
|
<div class="pop-title">{{$t('overall.select')}}</div>
|
||||||
|
<div class="pop-box custom-labels">
|
||||||
|
<div style="height: 100%; overflow: auto;">
|
||||||
|
<!--NotSet 为true不可设置-->
|
||||||
|
<div
|
||||||
|
v-for="(item,index) in custom"
|
||||||
|
:key="index"
|
||||||
|
class="custom-label"
|
||||||
|
@click="handler(item,index)"
|
||||||
|
:id="'element-set-el-'+index"
|
||||||
|
>
|
||||||
|
<i class="nz-icon nz-icon-check" v-if="!allowedAll && !item.allowed && (index === 0 || index === 1 || item.visibility === 'disabled')"></i>
|
||||||
|
<i v-else class="nz-icon nz-icon-check" v-show="item.show"></i>
|
||||||
|
<span>{{item.label}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="custom-bottom-btns">
|
||||||
|
<button v-if="isCancel" :id="tableId+'-element-set-none'" class="nz-btn nz-btn-size-small-new nz-btn-style-light-new is-cancel" type="button" @click="batchHandler(false)">
|
||||||
|
<span class="top-tool-btn-txt">{{$t('overall.clear')}}</span>
|
||||||
|
</button>
|
||||||
|
<button v-if="!isCancel" :id="tableId+'-element-set-all'" class="nz-btn nz-btn-size-small-new nz-btn-style-light-new" type="button" @click="batchHandler(true)">
|
||||||
|
<span class="top-tool-btn-txt">{{$t('overall.all')}}</span>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<button :id="tableId+'-element-set-esc'" class="nz-btn nz-btn-size-small-new nz-btn-style-light-new" type="button" @click="esc">
|
||||||
|
<span class="top-tool-btn-txt">{{$t('overall.esc')}}</span>
|
||||||
|
</button>
|
||||||
|
<button :id="tableId+'-element-set-save'" class="nz-btn nz-btn-size-small-new nz-btn-style-normal-new" type="button" @click="save">
|
||||||
|
<span class="top-tool-btn-txt">{{$t('overall.save')}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
customTableTitle: Array, // 自定义的title
|
||||||
|
originalTableTitle: Array, // 原始title
|
||||||
|
tableId: String,
|
||||||
|
allowedAll: { default: false }
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
custom: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
const localStorageTitle = JSON.parse(localStorage.getItem('nz-tableTitle-' + localStorage.getItem('nz-username') + '-' + this.tableId))
|
||||||
|
if (localStorageTitle) {
|
||||||
|
localStorage.setItem('nz-tableTitle-' + localStorage.getItem('nz-username') + '-' + this.tableId, JSON.stringify(localStorageTitle))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
customTableTitle: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
this.custom = JSON.parse(JSON.stringify(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 悬浮点击空白隐藏
|
||||||
|
esc () {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
// 全选all true 或者全取消cancel false按钮
|
||||||
|
batchHandler (state) {
|
||||||
|
for (let index = 0; index < this.custom.length; index++) {
|
||||||
|
if (this.custom[index].type !== 'title') {
|
||||||
|
if ((index === 0 || index === 1 || this.custom[index].NotSet)) {
|
||||||
|
this.custom[index].show = true
|
||||||
|
} else {
|
||||||
|
this.custom[index].show = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 单选
|
||||||
|
handler (val, index) {
|
||||||
|
if (!this.allowedAll && !val.allowed && (index === 0 || index === 1 || val.NotSet)) {
|
||||||
|
} else {
|
||||||
|
this.custom[index].show = !this.custom[index].show
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 点击第二个cancel
|
||||||
|
save () {
|
||||||
|
this.$emit('update', this.custom)
|
||||||
|
localStorage.setItem(
|
||||||
|
'nz-tableTitle-' + localStorage.getItem('nz-username') + '-' + this.tableId,
|
||||||
|
JSON.stringify(this.custom)
|
||||||
|
)
|
||||||
|
this.esc()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 点击all是否是全部取消选中,true为是
|
||||||
|
isCancel () {
|
||||||
|
let isCancel = true
|
||||||
|
for (let i = 0; i < this.custom.length; i++) {
|
||||||
|
if (!this.custom[i].show && this.custom[i].type !== 'title') {
|
||||||
|
isCancel = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isCancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.pop-custom {
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
position: absolute;
|
||||||
|
top: 55px;
|
||||||
|
right: 20px;
|
||||||
|
width: 200px;
|
||||||
|
color: #606266;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 999999;
|
||||||
|
}
|
||||||
|
.pop-custom-explore {
|
||||||
|
top: 33px;
|
||||||
|
}
|
||||||
|
.relative-position .pop-custom {
|
||||||
|
top: 33px;
|
||||||
|
}
|
||||||
|
.custom-labels {
|
||||||
|
margin-top: 12px;
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
.custom-labels i {
|
||||||
|
color: #04b330;
|
||||||
|
font-size: 14px;
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
top: 6px;
|
||||||
|
}
|
||||||
|
.custom-label {
|
||||||
|
padding: 2px 0 2px 25px;
|
||||||
|
position: relative;
|
||||||
|
cursor: default;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.custom-title{
|
||||||
|
padding: 2px 0 2px 2px;
|
||||||
|
}
|
||||||
|
.custom-label-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background: #f1f3f4;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.custom-bottom-btns {
|
||||||
|
margin-top: 7px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.unshow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
142
src/components/table/settings/UserTable.vue
Normal file
142
src/components/table/settings/UserTable.vue
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<el-table
|
||||||
|
id="userTable"
|
||||||
|
ref="dataTable"
|
||||||
|
:data="tableData"
|
||||||
|
:height="height"
|
||||||
|
border
|
||||||
|
@header-dragend="dragend"
|
||||||
|
@sort-change="tableDataSort"
|
||||||
|
@selection-change="selectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
:resizable="false"
|
||||||
|
align="center"
|
||||||
|
type="selection"
|
||||||
|
width="55">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index) in customTableTitle"
|
||||||
|
:key="`col-${index}`"
|
||||||
|
:fixed="item.fixed"
|
||||||
|
:label="item.label"
|
||||||
|
:min-width="`${item.minWidth}`"
|
||||||
|
:prop="item.prop"
|
||||||
|
:resizable="true"
|
||||||
|
:sort-orders="['ascending', 'descending']"
|
||||||
|
:sortable="item.sortable"
|
||||||
|
:width="`${item.width}`"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span class="data-column__span">{{item.label}}</span>
|
||||||
|
<div class="col-resize-area"></div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" :column="item">
|
||||||
|
<template v-if="item.prop === 'roles'">
|
||||||
|
<template v-if="scope.row[item.prop]">
|
||||||
|
{{scope.row[item.prop].map(t=>t.name).join(',')}}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>-</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'status'">
|
||||||
|
</template>
|
||||||
|
<span v-else>{{scope.row[item.prop]}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:resizable="false"
|
||||||
|
:width="operationWidth"
|
||||||
|
fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<div class="table-operation-title">{{$t('overall.option')}}</div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="table-operation-items">
|
||||||
|
<button class="table-operation-item" @click="tableOperation(['edit', scope.row])"><i class="nz-icon nz-icon-view1"></i></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import table from '@/mixins/table'
|
||||||
|
import { put } from '@/utils/http'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'userTable',
|
||||||
|
mixins: [table],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tableTitle: [ // 原始table列
|
||||||
|
{
|
||||||
|
label: 'ID',
|
||||||
|
prop: 'id',
|
||||||
|
show: true,
|
||||||
|
width: 80,
|
||||||
|
sortable: 'custom'
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.name'),
|
||||||
|
prop: 'name',
|
||||||
|
show: true,
|
||||||
|
width: 150,
|
||||||
|
sortable: 'custom'
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.username'),
|
||||||
|
prop: 'username',
|
||||||
|
show: true,
|
||||||
|
width: 150
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.roles'),
|
||||||
|
prop: 'roles',
|
||||||
|
show: true,
|
||||||
|
width: 150
|
||||||
|
}, {
|
||||||
|
label: 'E-mail',
|
||||||
|
prop: 'email',
|
||||||
|
show: true,
|
||||||
|
minWidth: 150
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.lastLoginTime'),
|
||||||
|
prop: 'lastLoginTime',
|
||||||
|
show: true,
|
||||||
|
width: 200
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.lastLoginIp'),
|
||||||
|
prop: 'lastLoginIp',
|
||||||
|
show: true,
|
||||||
|
width: 150
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.source'),
|
||||||
|
prop: 'source',
|
||||||
|
show: true,
|
||||||
|
width: 150
|
||||||
|
}, {
|
||||||
|
label: this.$t('config.user.enable'),
|
||||||
|
prop: 'status',
|
||||||
|
show: true,
|
||||||
|
width: 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
statusChange (user) {
|
||||||
|
if (user.roles) {
|
||||||
|
user.roleIds = user.roles.map(t => t.id)
|
||||||
|
}
|
||||||
|
put(this.url, user).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.rightBox.show = false
|
||||||
|
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||||
|
} else {
|
||||||
|
this.$message.error(response.msg)
|
||||||
|
}
|
||||||
|
this.$emit('reload')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
6
src/i18n/index.js
Normal file
6
src/i18n/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
const i18n = createI18n({
|
||||||
|
locale: localStorage.getItem('cn-language') || 'en',
|
||||||
|
messages: {}
|
||||||
|
})
|
||||||
|
export default i18n
|
||||||
20
src/main.js
20
src/main.js
@@ -1,8 +1,13 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import router from './router'
|
import _ from 'lodash'
|
||||||
import store from './store'
|
|
||||||
import App from './App.vue'
|
|
||||||
|
|
||||||
|
import router from '@/router'
|
||||||
|
import store from '@/store'
|
||||||
|
import App from '@/App.vue'
|
||||||
|
import { hasPermission } from '@/permission'
|
||||||
|
import commonMixin from '@/mixins/common'
|
||||||
|
import { cancelWithChange, clickOutside } from '@/utils/tools'
|
||||||
|
import i18n from '@/i18n'
|
||||||
import '@/assets/css/main.scss' // 样式入口
|
import '@/assets/css/main.scss' // 样式入口
|
||||||
|
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
@@ -11,5 +16,14 @@ const app = createApp(App)
|
|||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(store)
|
app.use(store)
|
||||||
app.use(ElementPlus)
|
app.use(ElementPlus)
|
||||||
|
app.use(i18n)
|
||||||
|
app.use(_)
|
||||||
|
|
||||||
|
app.directive('has', hasPermission) // 注册指令
|
||||||
|
app.directive('click-outside', clickOutside)
|
||||||
|
app.directive('cancel', cancelWithChange)
|
||||||
|
|
||||||
|
app.mixin(commonMixin)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
export default app
|
||||||
|
|||||||
35
src/mixins/common.js
Normal file
35
src/mixins/common.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import theme from '@/assets/css/theme.scss'
|
||||||
|
import { hasButton } from '@/permission'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
blockOperation: {
|
||||||
|
save: false,
|
||||||
|
import: false,
|
||||||
|
delete: false,
|
||||||
|
refresh: false,
|
||||||
|
query: false
|
||||||
|
},
|
||||||
|
theme: theme // scss主题变量
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
$nextTick: nextTick,
|
||||||
|
hasButton (code) {
|
||||||
|
return hasButton(this.$store.getters.buttonList, code)
|
||||||
|
},
|
||||||
|
isBuiltIn (row) {
|
||||||
|
return (row.buildIn && row.buildIn === 1) || (row.builtIn && row.builtIn === 1)
|
||||||
|
},
|
||||||
|
unblockOperation () {
|
||||||
|
this.blockOperation = {
|
||||||
|
save: false,
|
||||||
|
import: false,
|
||||||
|
delete: false,
|
||||||
|
refresh: false,
|
||||||
|
query: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/mixins/dataList.js
Normal file
179
src/mixins/dataList.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import { tableSort } from '@/utils/tools'
|
||||||
|
import { defaultPageSize, fromRoute, position } from '@/utils/constants'
|
||||||
|
import { get, del } from '@/utils/http'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
fromRoute: fromRoute,
|
||||||
|
// 侧滑
|
||||||
|
rightBox: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
pageObj: { // 分页对象
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: defaultPageSize,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
/* 工具参数 */
|
||||||
|
tools: {
|
||||||
|
loading: true, // 是否显示table加载动画
|
||||||
|
customTableTitle: [] // 自定义列工具的数据
|
||||||
|
},
|
||||||
|
mainTableHeight: position.tableHeight.normal, // 主列表table高度
|
||||||
|
batchDeleteObjs: [],
|
||||||
|
object: {},
|
||||||
|
searchLabel: ref({}),
|
||||||
|
|
||||||
|
tableData: [],
|
||||||
|
scrollbarWrap: null,
|
||||||
|
delFlag: false,
|
||||||
|
operationWidth: '165' // 操作列宽
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sortableShow: tableSort.sortableShow,
|
||||||
|
propTitle: tableSort.propTitle,
|
||||||
|
asce: tableSort.asce,
|
||||||
|
desc: tableSort.desc,
|
||||||
|
strToDate: tableSort.strToDate,
|
||||||
|
tableOperation ([command, row]) {
|
||||||
|
switch (command) {
|
||||||
|
case 'edit': {
|
||||||
|
this.edit(row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'delete': {
|
||||||
|
this.del(row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'copy': {
|
||||||
|
this.copy(row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectionChange (objs) {
|
||||||
|
this.batchDeleteObjs = objs
|
||||||
|
},
|
||||||
|
getTableData (params) {
|
||||||
|
if (params) {
|
||||||
|
this.searchLabel = { ...this.searchLabel, ...params }
|
||||||
|
}
|
||||||
|
this.searchLabel = { ...this.searchLabel, ...this.pageObj }
|
||||||
|
this.tools.loading = true
|
||||||
|
get(this.url, this.searchLabel).then(response => {
|
||||||
|
this.tools.loading = false
|
||||||
|
if (response.code === 200) {
|
||||||
|
for (let i = 0; i < response.data.list.length; i++) {
|
||||||
|
response.data.list[i].status = response.data.list[i].status + ''
|
||||||
|
}
|
||||||
|
this.tableData = response.data.list
|
||||||
|
this.pageObj.total = response.data.total
|
||||||
|
// TODO 回到顶部
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
del (row) {
|
||||||
|
this.$confirm(this.$t('tip.confirmDelete'), {
|
||||||
|
confirmButtonText: this.$t('tip.yes'),
|
||||||
|
cancelButtonText: this.$t('tip.no'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
del(this.url + '?ids=' + row.id).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.delFlag = true
|
||||||
|
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||||
|
this.getTableData()
|
||||||
|
} else {
|
||||||
|
this.$message.error(response.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
newObject () {
|
||||||
|
return JSON.parse(JSON.stringify(this.blankObject))
|
||||||
|
},
|
||||||
|
pageNo (val) {
|
||||||
|
this.pageObj.pageNo = val
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
pageSize (val) {
|
||||||
|
this.pageObj.pageSize = val
|
||||||
|
localStorage.setItem('cn-pageSize-' + localStorage.getItem('cn-username') + '-' + this.tableId, val)
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
add () {
|
||||||
|
this.object = this.newObject()
|
||||||
|
this.rightBox.show = true
|
||||||
|
},
|
||||||
|
closeRightBox (refresh) {
|
||||||
|
this.rightBox.show = false
|
||||||
|
if (refresh) {
|
||||||
|
this.delFlag = true
|
||||||
|
this.getTableData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edit (u) {
|
||||||
|
get(`${this.url}/${u.id}`).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.object = response.data
|
||||||
|
this.rightBox.show = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
copy (u) {
|
||||||
|
this.object = { ...u, name: 'Copy from ' + u.name, id: '' }
|
||||||
|
this.rightBox.show = true
|
||||||
|
},
|
||||||
|
esc () {
|
||||||
|
this.rightBox.show = false
|
||||||
|
},
|
||||||
|
dragend () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.dataTable.$refs.dataTable.doLayout()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tableDataSort (orderBy) {
|
||||||
|
this.$set(this.searchLabel, 'orderBy', orderBy)
|
||||||
|
this.getTableData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
tableData: {
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
if (n.length === 0 && this.pageObj.pageNo > 1) {
|
||||||
|
this.pageNo(this.pageObj.pageNo - 1)
|
||||||
|
}
|
||||||
|
// TODO 不是删除时回到顶部
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'tools.customTableTitle': {
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
this.dragend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const pageSize = localStorage.getItem('cn-pageSize-' + localStorage.getItem('cn-username') + '-' + this.tableId)
|
||||||
|
if (pageSize && pageSize !== 'undefined') {
|
||||||
|
this.pageObj.pageSize = pageSize
|
||||||
|
}
|
||||||
|
let localStorageTableTitle = localStorage.getItem('cn-tableTitle-' + localStorage.getItem('cn-username') + '-' + this.tableId)
|
||||||
|
localStorageTableTitle = localStorageTableTitle ? JSON.parse(localStorageTableTitle) : this.$refs.dataTable.tableTitle
|
||||||
|
this.tools.customTableTitle = this.$refs.dataTable.tableTitle.map((item, index) => { // 修复切换中英文的问题
|
||||||
|
item.show = localStorageTableTitle[index].show
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
if (localStorageTableTitle && (localStorageTableTitle.length > this.$refs.dataTable.tableTitle.length)) {
|
||||||
|
const arr = localStorageTableTitle.splice(this.$refs.dataTable.tableTitle.length, localStorageTableTitle.length)
|
||||||
|
this.tools.customTableTitle = this.tools.customTableTitle.concat(arr)
|
||||||
|
}
|
||||||
|
this.getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/mixins/rightBox.js
Normal file
31
src/mixins/rightBox.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
object: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
editObject: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickOutside () {
|
||||||
|
this.esc(false)
|
||||||
|
},
|
||||||
|
/* 关闭弹框 */
|
||||||
|
esc (refresh) {
|
||||||
|
this.unblockOperation()
|
||||||
|
this.$emit('close', refresh)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
object: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler (n) {
|
||||||
|
this.editObject = JSON.parse(JSON.stringify(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/mixins/table.js
Normal file
64
src/mixins/table.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
tableData: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
customTableTitle: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
tableId: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
operationWidth: '165' // 操作列宽
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
tableOperation ([command, row, param]) {
|
||||||
|
switch (command) {
|
||||||
|
case 'recordTab': {
|
||||||
|
this.$emit('showBottomBox', 'recordTab', row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'endpointQuery': {
|
||||||
|
this.$emit('showBottomBox', 'endpointQuery', row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'fastSilence': {
|
||||||
|
this.$emit('addSilence', row, param)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
this.$emit(command, row)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectionChange (objs) {
|
||||||
|
this.$emit('selectionChange', objs)
|
||||||
|
},
|
||||||
|
dragend () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.dataTable.doLayout()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tableDataSort (item) {
|
||||||
|
let orderBy = ''
|
||||||
|
if (item.order === 'ascending') {
|
||||||
|
orderBy = item.prop
|
||||||
|
}
|
||||||
|
if (item.order === 'descending') {
|
||||||
|
orderBy = '-' + item.prop
|
||||||
|
}
|
||||||
|
this.$emit('orderBy', orderBy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
129
src/permission.js
Normal file
129
src/permission.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import { get, post } from './utils/http'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const loginWhiteList = ['/login'] // 免登陆白名单
|
||||||
|
const permissionWhiteList = [...loginWhiteList] // 权限白名单
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (sessionStorage.getItem('cn-token')) {
|
||||||
|
if (permissionWhiteList.indexOf(to.path) !== -1) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
new Promise(resolve => {
|
||||||
|
if (store.getters.menuList.length === 0) {
|
||||||
|
get(`${process.env.BASE_URL}config.json?Timestamp=${new Date().getTime()}`).then(config => {
|
||||||
|
post(config.baseUrl + 'sys/user/permissions', { token: sessionStorage.getItem('cn-token') }).then(res => {
|
||||||
|
store.commit('setMenuList', sortByOrderNum(res.data.menus))
|
||||||
|
store.commit('setButtonList', res.data.buttons)
|
||||||
|
store.commit('setRoleList', res.data.roles)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
if (to.path) {
|
||||||
|
if (hasMenu(store.getters.menuList, to.path)) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
ElMessage.error('No access') // TODO 国际化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (loginWhiteList.indexOf(to.path) !== -1) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
next({ path: '/login' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// menuList中是否包含route权限
|
||||||
|
export function hasMenu (menuList, route) {
|
||||||
|
return menuList.some(menu => {
|
||||||
|
if (menu.route === route) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
if (menu.children) {
|
||||||
|
if (hasMenu(menu.children, route)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasButton (buttonList, code) {
|
||||||
|
return buttonList.some(button => button === code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用法 v-has="code" | v-has="[code...]" 任意匹配一个 | v-has:all="[code...]" 全匹配
|
||||||
|
export const hasPermission = {
|
||||||
|
beforeMount (el, binding) {
|
||||||
|
// 节点权限处理
|
||||||
|
const buttonCode = binding.value
|
||||||
|
const arg = binding.arg
|
||||||
|
if (buttonCode) {
|
||||||
|
if (buttonCode instanceof Array) {
|
||||||
|
let has = true
|
||||||
|
if (arg && arg === 'all') { // 全匹配
|
||||||
|
buttonCode.forEach(button => {
|
||||||
|
if (has) {
|
||||||
|
has = hasButton(store.getters.buttonList, button)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else { // 任意匹配
|
||||||
|
has = buttonCode.some(button => {
|
||||||
|
return hasButton(store.getters.buttonList, button)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!has) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
}
|
||||||
|
} else { // 单个匹配
|
||||||
|
if (!hasButton(store.getters.buttonList, buttonCode)) {
|
||||||
|
el.parentNode && el.parentNode.removeChild(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据orderNum排序
|
||||||
|
export function sortByOrderNum (menuList) {
|
||||||
|
const r = menuList.sort((a, b) => {
|
||||||
|
return a.orderNum - b.orderNum
|
||||||
|
})
|
||||||
|
r.forEach(menu => {
|
||||||
|
if (menu.children && getChildMenu(menu).length > 0) {
|
||||||
|
menu.children = menu.children.sort((a, b) => {
|
||||||
|
return a.orderNum - b.orderNum
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChildMenu (menu) {
|
||||||
|
return menu.children.filter(m => m.type === 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 获取第一个菜单 */
|
||||||
|
export function getWelcomeMenu (menu) {
|
||||||
|
for (let i = 0; i < menu.length; i++) {
|
||||||
|
const m = menu[i]
|
||||||
|
if (m.type === 1) {
|
||||||
|
if (m.children && getChildMenu(m).length > 0) {
|
||||||
|
return getWelcomeMenu(m.children)
|
||||||
|
} else {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,16 +11,20 @@ const routes = [
|
|||||||
component: () => import('@/components/layout/Home'),
|
component: () => import('@/components/layout/Home'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/traffic',
|
path: '/trafficSummary',
|
||||||
component: () => import('@/views/dashboards/TrafficSummary')
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/na',
|
path: '/networkAppPerformance',
|
||||||
component: () => import('@/views/dashboards/TrafficSummary')
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dns',
|
path: '/dnsServiceInsights',
|
||||||
component: () => import('@/views/dashboards/TrafficSummary')
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/user',
|
||||||
|
component: () => import('@/views/settings/User')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const store = createStore({
|
|||||||
mutations: {
|
mutations: {
|
||||||
isShrink (state) {
|
isShrink (state) {
|
||||||
state.isShrink = !state.isShrink
|
state.isShrink = !state.isShrink
|
||||||
localStorage.setItem('nz-left-menu-shrink', state.isShrink)
|
localStorage.setItem('cn-left-menu-shrink', state.isShrink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
|
import { post } from '@/utils/http'
|
||||||
|
import router from '@/router'
|
||||||
|
import { sortByOrderNum, getWelcomeMenu } from '@/permission'
|
||||||
|
import moment from 'moment-timezone'
|
||||||
|
import bus from '@/utils/bus'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
state() {
|
state () {
|
||||||
return {
|
return {
|
||||||
menuList: [],
|
menuList: [],
|
||||||
buttonList: [],
|
buttonList: [],
|
||||||
@@ -24,65 +31,7 @@ const user = {
|
|||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
menuList (state) {
|
menuList (state) {
|
||||||
const menuList = JSON.parse(`[
|
return state.menuList
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "dashboards",
|
|
||||||
"code": "dashboard",
|
|
||||||
"i18n": "dashboard.title",
|
|
||||||
"parentId": 0,
|
|
||||||
"perms": "",
|
|
||||||
"type": 1,
|
|
||||||
"route": "",
|
|
||||||
"orderNum": 1,
|
|
||||||
"icon": "cn-icon cn-icon-menu-dashboard",
|
|
||||||
"required": "",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Traffic summary",
|
|
||||||
"code": "overview",
|
|
||||||
"i18n": "dashboard.overview.title",
|
|
||||||
"parentId": 1,
|
|
||||||
"perms": "",
|
|
||||||
"type": 1,
|
|
||||||
"route": "/traffic",
|
|
||||||
"orderNum": 1,
|
|
||||||
"icon": "",
|
|
||||||
"required": "",
|
|
||||||
"children": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Network & Application performance",
|
|
||||||
"code": "panel",
|
|
||||||
"i18n": "dashboard.panel.title",
|
|
||||||
"parentId": 1,
|
|
||||||
"perms": "",
|
|
||||||
"type": 1,
|
|
||||||
"route": "/na",
|
|
||||||
"orderNum": 2,
|
|
||||||
"icon": "",
|
|
||||||
"required": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"name": "DNS service insights",
|
|
||||||
"code": "explore",
|
|
||||||
"i18n": "dashboard.metricPreview.title",
|
|
||||||
"parentId": 1,
|
|
||||||
"perms": "",
|
|
||||||
"type": 1,
|
|
||||||
"route": "/dns",
|
|
||||||
"orderNum": 3,
|
|
||||||
"icon": "",
|
|
||||||
"required": "",
|
|
||||||
"children": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]`)
|
|
||||||
return menuList
|
|
||||||
},
|
},
|
||||||
buttonList (state) {
|
buttonList (state) {
|
||||||
return state.buttonList
|
return state.buttonList
|
||||||
@@ -93,11 +42,37 @@ const user = {
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
loginSuccess (store, res) {
|
loginSuccess (store, res) {
|
||||||
|
sessionStorage.setItem('cn-token', res.data.token)
|
||||||
|
localStorage.setItem('cn-sys-name', res.data.systemName)
|
||||||
|
if (res.systemLogo) {
|
||||||
|
localStorage.setItem('cn-sys-logo', res.data.systemLogo)
|
||||||
|
}
|
||||||
|
localStorage.setItem('cn-sys-timezone', res.data.timezone)
|
||||||
|
localStorage.setItem('timezone-offset', moment.tz(res.data.timezone).format('Z'))
|
||||||
|
|
||||||
|
post('/sys/user/permissions', { token: res.data.token }).then(res => {
|
||||||
|
const menuList = sortByOrderNum(res.data.menus)
|
||||||
|
store.commit('setMenuList', menuList)
|
||||||
|
store.commit('setButtonList', res.data.buttons)
|
||||||
|
store.commit('setRoleList', res.data.roles)
|
||||||
|
|
||||||
|
const welcomeMenu = getWelcomeMenu(menuList)
|
||||||
|
if (welcomeMenu) {
|
||||||
|
router.push({
|
||||||
|
path: welcomeMenu.route,
|
||||||
|
query: {
|
||||||
|
t: +new Date()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage.error('No menu') // TODO 国际化
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
logoutSuccess (store, res) {
|
logoutSuccess (store, res) {
|
||||||
sessionStorage.removeItem('nz-username')
|
sessionStorage.removeItem('cn-username')
|
||||||
localStorage.removeItem('nz-username')
|
localStorage.removeItem('cn-username')
|
||||||
sessionStorage.removeItem('nz-token')
|
sessionStorage.removeItem('cn-token')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/utils/bus.js
Normal file
5
src/utils/bus.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
|
||||||
|
export default createApp({
|
||||||
|
name: 'bus'
|
||||||
|
})
|
||||||
13
src/utils/constants.js
Normal file
13
src/utils/constants.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export const defaultPageSize = 20
|
||||||
|
|
||||||
|
// 统一定义跳转来源
|
||||||
|
export const fromRoute = {
|
||||||
|
trafficSummary: 'trafficSummary',
|
||||||
|
user: 'user'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const position = {
|
||||||
|
tableHeight: {
|
||||||
|
normal: 'calc(100% - 48px)' // 常规高度,特例在下方定义
|
||||||
|
}
|
||||||
|
}
|
||||||
274
src/utils/date-util.js
Normal file
274
src/utils/date-util.js
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
// 获取初始化时间,默认最近一周
|
||||||
|
import moment from 'moment-timezone'
|
||||||
|
|
||||||
|
Date.prototype.setStart = function () {
|
||||||
|
this.setHours(0)
|
||||||
|
this.setMinutes(0)
|
||||||
|
this.setSeconds(0)
|
||||||
|
}
|
||||||
|
Date.prototype.setEnd = function () {
|
||||||
|
this.setHours(23)
|
||||||
|
this.setMinutes(59)
|
||||||
|
this.setSeconds(59)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefaultDate () {
|
||||||
|
let start = this.getDays(-7)
|
||||||
|
let end = this.getDays(0)
|
||||||
|
start.setStart()
|
||||||
|
end.setEnd()
|
||||||
|
// let start = this.getHoursTime(-1);
|
||||||
|
// let end = this.getHoursTime(0);
|
||||||
|
start = this.timeFormate(start, 'yyyy-MM-dd hh:mm:ss')
|
||||||
|
end = this.timeFormate(end, 'yyyy-MM-dd hh:mm:ss')
|
||||||
|
this.selectDate = [start, end]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHoursTime (hours) {
|
||||||
|
const today = new Date().getTime()
|
||||||
|
const date = new Date(today + (hours * 60 * 60 * 1000))
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化日期
|
||||||
|
export function getDays (days) {
|
||||||
|
const today = new Date().getTime()
|
||||||
|
return new Date(today + (days * 24 * 60 * 60 * 1000))
|
||||||
|
}
|
||||||
|
export function formatDate (date, type) {
|
||||||
|
const yy = date.getFullYear()
|
||||||
|
const dateM = date.getMonth() + 1
|
||||||
|
const mm = dateM > 9 ? dateM : `0${dateM}`
|
||||||
|
const dateD = date.getDate()
|
||||||
|
const dd = dateD > 9 ? dateD : `0${dateD}`
|
||||||
|
if (type) {
|
||||||
|
return `${yy}${type}${mm}${type}${dd}`
|
||||||
|
}
|
||||||
|
return `${yy}${mm}${dd}`
|
||||||
|
}
|
||||||
|
export function timeFormate (date, fmt = 'yyyy-MM-dd hh:mm:ss') {
|
||||||
|
const time = new Date(date)
|
||||||
|
let fm = fmt
|
||||||
|
// fmt 自定义格式,如:yy-MM-dd
|
||||||
|
let week = ''
|
||||||
|
switch (time.getDay()) {
|
||||||
|
case 0:
|
||||||
|
week = '周日'
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
week = '周一'
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
week = '周二'
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
week = '周三'
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
week = '周四'
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
week = '周五'
|
||||||
|
break
|
||||||
|
case 6:
|
||||||
|
week = '周六'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
week = ''
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const o = {
|
||||||
|
'M+': time.getMonth() + 1, // 月份
|
||||||
|
'd+': time.getDate(), // 日
|
||||||
|
hh: time.getHours(), // 小时
|
||||||
|
'm+': time.getMinutes(), // 分
|
||||||
|
's+': time.getSeconds(), // 秒
|
||||||
|
'q+': Math.floor((time.getMonth() + 3) / 3), // 季度
|
||||||
|
S: time.getMilliseconds(), // 毫秒
|
||||||
|
w: week
|
||||||
|
}
|
||||||
|
if (/(y+)/.test(fm)) {
|
||||||
|
fm = fm.replace(RegExp.$1, (time.getFullYear().toString()).substr(4 - RegExp.$1.length))
|
||||||
|
}
|
||||||
|
Object.keys(o).forEach((k) => {
|
||||||
|
if (new RegExp(`(${k})`).test(fm)) {
|
||||||
|
fm = fm.replace(RegExp.$1, (RegExp.$1.length === 1)
|
||||||
|
? (o[k])
|
||||||
|
: ((`00${o[k]}`).substr((`${o[k]}`).length)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return fm
|
||||||
|
}
|
||||||
|
// 格式化tag为字符串表达式
|
||||||
|
export function tagsToString (metric, arr) {
|
||||||
|
let str = metric
|
||||||
|
let sepStr = ''
|
||||||
|
arr.forEach((item, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
str += '{'
|
||||||
|
if (item.value.length === 1) {
|
||||||
|
str += `${item.name}='${item.value.join('|')}'`
|
||||||
|
sepStr = ','
|
||||||
|
} else if (item.value.length > 1) {
|
||||||
|
str += `${item.name}=~'${item.value.join('|')}'`
|
||||||
|
sepStr = ','
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (item.value.length === 1) {
|
||||||
|
str += sepStr + `${item.name}='${item.value.join('|')}'`
|
||||||
|
sepStr = ','
|
||||||
|
} else if (item.value.length > 1) {
|
||||||
|
str += sepStr + `${item.name}=~'${item.value.join('|')}'`
|
||||||
|
sepStr = ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (str.indexOf('{') > -1) {
|
||||||
|
str += '}'
|
||||||
|
}
|
||||||
|
if (str.endsWith('{}')) {
|
||||||
|
str = str.substring(0, str.indexOf('{'))
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
export function getStep (startTime, endTime) {
|
||||||
|
const start = new Date(startTime)
|
||||||
|
const end = new Date(endTime)
|
||||||
|
let step = '15s'
|
||||||
|
const numInterval = end.getTime() - start.getTime()
|
||||||
|
const oneDay = 86400000
|
||||||
|
const sevenDay = 604800000
|
||||||
|
const thirtyDay = 2592000000
|
||||||
|
if (numInterval < oneDay) { // 小于1天,step为15s
|
||||||
|
step = '15s'
|
||||||
|
} else if (numInterval < sevenDay) {
|
||||||
|
step = '5m'
|
||||||
|
} else if (numInterval < thirtyDay) {
|
||||||
|
step = '10m'
|
||||||
|
} else {
|
||||||
|
step = '30m'
|
||||||
|
}
|
||||||
|
return step
|
||||||
|
}
|
||||||
|
export function isEmptyObject (obj) {
|
||||||
|
if (obj) {
|
||||||
|
let name = ''
|
||||||
|
// eslint-disable-next-line
|
||||||
|
for (name in obj) { return false; }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
export function validateEmail (rule, value, callback) {
|
||||||
|
if (value === '') {
|
||||||
|
callback(new Error('请输入邮箱'))
|
||||||
|
} else if (!this.emailReg.test(value)) {
|
||||||
|
callback(new Error('邮箱格式不正确'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function getNumStr (num) {
|
||||||
|
if (num >= 1000) {
|
||||||
|
const kbNum = num / 1000
|
||||||
|
if (kbNum >= 1000) {
|
||||||
|
const mbNum = kbNum / 1000
|
||||||
|
if (mbNum > 1000) {
|
||||||
|
const gbNum = mbNum / 1000
|
||||||
|
if (gbNum > 1000) {
|
||||||
|
const tbNum = gbNum / 1000
|
||||||
|
if (tbNum > 1000) {
|
||||||
|
const pbNum = tbNum / 1000
|
||||||
|
return `${pbNum.toFixed(2)}PB`
|
||||||
|
}
|
||||||
|
return `${tbNum.toFixed(2)}TB`
|
||||||
|
}
|
||||||
|
return `${gbNum.toFixed(2)}GB`
|
||||||
|
}
|
||||||
|
return `${mbNum.toFixed(2)}MB`
|
||||||
|
}
|
||||||
|
return `${kbNum.toFixed(2)}KB`
|
||||||
|
}
|
||||||
|
return num.toFixed(2)
|
||||||
|
}
|
||||||
|
// 将本地时区转为系统配置的时区
|
||||||
|
export function computeTimezone (sourceTime) {
|
||||||
|
let offset = localStorage.getItem('cn-sys-timezone')
|
||||||
|
offset = moment.tz(offset).format('Z')
|
||||||
|
if (offset && offset !== 'undefined') {
|
||||||
|
offset = Number.parseInt(offset)
|
||||||
|
const date = new Date(sourceTime)
|
||||||
|
const localOffset = date.getTimezoneOffset() * 60 * 1000 // 默认 一分钟显示时区偏移的结果
|
||||||
|
const utcTime = sourceTime + localOffset
|
||||||
|
return utcTime + (offset * 60 * 60 * 1000)
|
||||||
|
} else {
|
||||||
|
return sourceTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将本地时区转为系统配置的时区
|
||||||
|
export function computeTimezoneTime (sourceTime) {
|
||||||
|
let offset = localStorage.getItem('cn-sys-timezone')
|
||||||
|
offset = moment.tz(offset).format('Z')
|
||||||
|
if (offset && offset !== 'undefined') {
|
||||||
|
offset = Number.parseInt(offset)
|
||||||
|
const date = new Date(sourceTime)
|
||||||
|
const localOffset = date.getTimezoneOffset() * 60 * 1000 // 默认 一分钟显示时区偏移的结果
|
||||||
|
const utcTime = date.getTime() + localOffset
|
||||||
|
return utcTime + (offset * 60 * 60 * 1000)
|
||||||
|
} else {
|
||||||
|
return sourceTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function getTimezontDateRange (offset = -1) {
|
||||||
|
return [
|
||||||
|
new Date(new Date(this.computeTimezone(new Date().getTime())).setHours(new Date(this.computeTimezone(new Date().getTime())).getHours() + offset)),
|
||||||
|
new Date(this.computeTimezone(new Date().getTime()))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
export function getOffsetTimezoneData (offset = 0) {
|
||||||
|
return new Date(this.computeTimezone(new Date().getTime())).setHours(new Date(this.computeTimezone(new Date().getTime())).getHours() + offset)
|
||||||
|
}
|
||||||
|
export function debounce (fn, delay) {
|
||||||
|
// 记录上一次的延时器
|
||||||
|
let timer = null
|
||||||
|
delay = delay || 200
|
||||||
|
return function () {
|
||||||
|
const args = arguments
|
||||||
|
const that = this
|
||||||
|
// 清除上一次延时器
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(that, args)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function UTCTimeToConfigTimezone (utcTime) {
|
||||||
|
let offset = localStorage.getItem('cn-sys-timezone')
|
||||||
|
offset = moment.tz(offset).format('Z')
|
||||||
|
if (offset && offset !== 'undefined') {
|
||||||
|
let time = utcTime
|
||||||
|
if (typeof time === 'string' && /(\d+?-){2}\d+?\s(\d+?:)*\d+/.test(time)) {
|
||||||
|
time = new Date(time).getTime()
|
||||||
|
}
|
||||||
|
offset = Number.parseInt(offset)
|
||||||
|
time += offset * 60 * 60 * 1000
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
return utcTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function configTimezoneToUTCTime (configTime) {
|
||||||
|
let offset = localStorage.getItem('cn-sys-timezone')
|
||||||
|
offset = moment.tz(offset).format('Z')
|
||||||
|
if (offset && offset !== 'undefined') {
|
||||||
|
let time = configTime
|
||||||
|
if (typeof time === 'string' && /(\d+?-){2}\d+?\s(\d+?:)*\d+/.test(time)) {
|
||||||
|
time = new Date(time).getTime()
|
||||||
|
}
|
||||||
|
offset = Number.parseInt(offset)
|
||||||
|
time -= offset * 60 * 60 * 1000
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
return configTime
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/utils/http.js
Normal file
111
src/utils/http.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
axios.interceptors.request.use(config => {
|
||||||
|
const token = sessionStorage.getItem('cn-token')
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = token // 请求头token
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
err => Promise.reject(err)
|
||||||
|
)
|
||||||
|
const accountErrorCode = [518003, 518004, 518005, 518006, 518007, 518008] // 账号锁定等
|
||||||
|
const licenceErrorCode = [711001]
|
||||||
|
|
||||||
|
// 若get请求的url中带问号,则将url上的参数截取,改为对象形式传参
|
||||||
|
axios.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
if (config.method === 'get') {
|
||||||
|
const url = config.url
|
||||||
|
const index = url.indexOf('?')
|
||||||
|
if (index > -1) {
|
||||||
|
const pre = url.substring(0, index)
|
||||||
|
const suf = url.substring(index + 1, url.length)
|
||||||
|
const paramsArr = suf.split('&')
|
||||||
|
const params = {}
|
||||||
|
paramsArr.forEach(p => {
|
||||||
|
const i = p.indexOf('=')
|
||||||
|
if (i > -1) {
|
||||||
|
params[p.substring(0, i)] = p.substring(i + 1, p.length)
|
||||||
|
} else {
|
||||||
|
params[p] = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
config = { ...config, url: pre, params: params }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
)
|
||||||
|
axios.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
if (licenceErrorCode.indexOf(response.data.code) !== -1) {
|
||||||
|
window.location.href = '/'
|
||||||
|
} else if (response.status === 200) {
|
||||||
|
if (accountErrorCode.indexOf(response.data.code) !== -1) {
|
||||||
|
window.location.href = '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export function get (url, params) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
axios.get(url, {
|
||||||
|
params: params
|
||||||
|
}).then(response => {
|
||||||
|
resolve(response.data)
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.response) {
|
||||||
|
resolve(err.response.data)
|
||||||
|
} else if (err.message) {
|
||||||
|
resolve(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function post (url, params, headers) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
axios.post(url, params, { headers: headers }).then(response => {
|
||||||
|
resolve(response.data, response)
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.response) {
|
||||||
|
resolve(err.response.data)
|
||||||
|
} else if (err.message) {
|
||||||
|
resolve(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function put (url, params, headers) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
axios.put(url, params, { headers: headers }).then(response => {
|
||||||
|
resolve(response.data, response)
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.response) {
|
||||||
|
resolve(err.response.data)
|
||||||
|
} else if (err.message) {
|
||||||
|
resolve(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function del (url, params) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
axios.delete(url, params).then(response => {
|
||||||
|
resolve(response.data, response)
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.response) {
|
||||||
|
resolve(err.response.data)
|
||||||
|
} else if (err.message) {
|
||||||
|
resolve(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
371
src/utils/tools.js
Normal file
371
src/utils/tools.js
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
|
export const tableSort = {
|
||||||
|
// 是否需要排序
|
||||||
|
sortableShow (prop, from) {
|
||||||
|
switch (prop) {
|
||||||
|
case 'state': {
|
||||||
|
if (from === 'operationlog' || from === 'alertSilence') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'startAt': {
|
||||||
|
if (from === 'alertSilence') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'id':
|
||||||
|
case 'alertRule':
|
||||||
|
case 'severity':
|
||||||
|
case 'endAt':
|
||||||
|
case 'ID':
|
||||||
|
case 'HOST':
|
||||||
|
case 'SN':
|
||||||
|
case 'assetType':
|
||||||
|
case 'purchaseDate':
|
||||||
|
case 'pingStatus':
|
||||||
|
case 'dataCenter':
|
||||||
|
case 'cabinet':
|
||||||
|
case 'model':
|
||||||
|
case 'principal':
|
||||||
|
case 'asset':
|
||||||
|
case 'port':
|
||||||
|
case 'project':
|
||||||
|
case 'module':
|
||||||
|
case 'type':
|
||||||
|
case 'name':
|
||||||
|
case 'area':
|
||||||
|
case 'vendor':
|
||||||
|
case 'filename':
|
||||||
|
case 'updateAt':
|
||||||
|
case 'username':
|
||||||
|
case 'ip':
|
||||||
|
case 'operation':
|
||||||
|
case 'createDate':
|
||||||
|
case 'time':
|
||||||
|
case 'host':
|
||||||
|
case 'protocol':
|
||||||
|
case 'user':
|
||||||
|
case 'cmd':
|
||||||
|
case 'alertName':
|
||||||
|
case 'threshold':
|
||||||
|
case 'idc':
|
||||||
|
case 'alertNum':
|
||||||
|
case 'gname':
|
||||||
|
return 'custom'
|
||||||
|
default : return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// prop字段
|
||||||
|
propTitle (prop, from) {
|
||||||
|
switch (from) {
|
||||||
|
case 'asset':
|
||||||
|
switch (prop) {
|
||||||
|
case 'ID': return 'ass.id'
|
||||||
|
case 'HOST': return 'ass.host'
|
||||||
|
case 'SN': return 'ass.sn'
|
||||||
|
case 'assetType': return 'sdtt.value'
|
||||||
|
case 'purchaseDate': return 'ass.purchase_date'
|
||||||
|
case 'state': return 'ass.state'
|
||||||
|
case 'pingStatus': return 'assp.rtt'
|
||||||
|
case 'dataCenter': return 'idc.name'
|
||||||
|
case 'cabinet': return 'cab.name'
|
||||||
|
case 'model': return 'mo.name'
|
||||||
|
case 'vendor': return 'sdt.value'
|
||||||
|
case 'principal': return 'su.username'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'alertMessage':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'am.id'
|
||||||
|
case 'state': return 'am.state'
|
||||||
|
case 'alertRule': return 'ar.alert_name'
|
||||||
|
case 'severity': return 'am.severity'
|
||||||
|
case 'startAt': return 'am.start_at'
|
||||||
|
case 'endAt': return 'am.end_at'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'project':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'e.id'
|
||||||
|
case 'asset': return 'a.host'
|
||||||
|
case 'port': return 'e.port'
|
||||||
|
case 'project': return 'p.name'
|
||||||
|
case 'module': return 'm.name'
|
||||||
|
case 'type': return 'm.type'
|
||||||
|
case 'state' :return 'es.state'
|
||||||
|
// case 'path': return'e.path';
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'dc':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'i.id'
|
||||||
|
case 'name': return 'i.name'
|
||||||
|
case 'area': return 'sa.name'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'endpointTab':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'e.id'
|
||||||
|
case 'asset': return 'a.host'
|
||||||
|
case 'port': return 'e.port'
|
||||||
|
case 'project': return 'p.name'
|
||||||
|
case 'module': return 'm.name'
|
||||||
|
case 'type': return 'm.type'
|
||||||
|
case 'state' :return 'es.state'
|
||||||
|
// case 'path': return'e.path';
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'model':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'mo.id'
|
||||||
|
case 'name': return 'mo.name'
|
||||||
|
case 'type': return 'dictt.value'
|
||||||
|
case 'vendor': return 'dict.value'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'promServer':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'id'
|
||||||
|
case 'idc': return 'idc_id'
|
||||||
|
case 'host': return 'host'
|
||||||
|
case 'port': return 'port'
|
||||||
|
case 'type': return 'type'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'mib':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'sm.id'
|
||||||
|
case 'name': return 'sm.name'
|
||||||
|
case 'filename': return 'sm.file_name'
|
||||||
|
case 'updateAt': return 'sm.update_at'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'operationlog':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'sl.id'
|
||||||
|
case 'username': return 'su.username'
|
||||||
|
case 'ip': return 'sl.ip'
|
||||||
|
case 'operation': return 'sl.operation'
|
||||||
|
case 'type': return 'sl.type'
|
||||||
|
case 'createDate': return 'sl.create_date'
|
||||||
|
case 'time': return 'sl.time'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'temrminallog':
|
||||||
|
switch (prop) {
|
||||||
|
case 'protocol': return 'protocol'
|
||||||
|
case 'startTime': return 'startTime'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'alertRules':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'ar.id'
|
||||||
|
case 'alertName': return 'ar.alert_name'
|
||||||
|
case 'threshold': return 'ar.threshold'
|
||||||
|
case 'severity': return 'ar.severity'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
case 'exprTemp':
|
||||||
|
switch (prop) {
|
||||||
|
case 'id': return 'id'
|
||||||
|
case 'name': return 'name'
|
||||||
|
case 'gname': return 'gname'
|
||||||
|
default : return prop
|
||||||
|
}
|
||||||
|
default: return prop
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 本地正序
|
||||||
|
asce (prop) {
|
||||||
|
return function (obj1, obj2) {
|
||||||
|
const { val1, val2 } = this.format(prop, obj1, obj2)
|
||||||
|
if (val1 < val2) {
|
||||||
|
return -1
|
||||||
|
} else if (val1 > val2) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 本地倒序
|
||||||
|
desc (prop) {
|
||||||
|
return function (obj1, obj2) {
|
||||||
|
const { val1, val2 } = this.format(prop, obj1, obj2)
|
||||||
|
if (val1 < val2) {
|
||||||
|
return 1
|
||||||
|
} else if (val1 > val2) {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
format (prop, obj1, obj2) {
|
||||||
|
let val1 = obj1[prop]
|
||||||
|
let val2 = obj2[prop]
|
||||||
|
if (!isNaN(Number(val1)) && !isNaN(Number(val2)) && prop !== 'time') {
|
||||||
|
val1 = Number(val1)
|
||||||
|
val2 = Number(val2)
|
||||||
|
}
|
||||||
|
if (prop === 'time') {
|
||||||
|
val1 = tableSort.strTodate(val1)
|
||||||
|
val2 = tableSort.strTodate(val2)
|
||||||
|
}
|
||||||
|
if (prop === 'element') {
|
||||||
|
if (val1.alias) {
|
||||||
|
val1 = JSON.stringify(obj1[prop].alias).replace(/\s*/g, '')
|
||||||
|
} else {
|
||||||
|
val1 = JSON.stringify(obj1[prop].element).replace(/\s*/g, '')
|
||||||
|
}
|
||||||
|
if (val2.alias) {
|
||||||
|
val2 = JSON.stringify(obj2[prop].alias).replace(/\s*/g, '')
|
||||||
|
} else {
|
||||||
|
val2 = JSON.stringify(obj2[prop].element).replace(/\s*/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { val1, val2 }
|
||||||
|
},
|
||||||
|
// 转化时间字符串为时间戳
|
||||||
|
strToDate (str) {
|
||||||
|
let date = str.trim()
|
||||||
|
date = date.substring(0, 19)
|
||||||
|
date = date.replace(/-/g, '/') // 必须把日期'-'转为'/'
|
||||||
|
return new Date(date).getTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* cancel提醒保存指令 */
|
||||||
|
export const cancelWithChange = {
|
||||||
|
mounted (el, binding) {
|
||||||
|
if (!binding.value || !binding.value.object) return
|
||||||
|
const unsavedChange = localStorage.getItem('nz-unsaved-change')
|
||||||
|
const oldValue = JSON.parse(JSON.stringify(binding.value.object))
|
||||||
|
function domClick (e) {
|
||||||
|
const newValue = JSON.parse(JSON.stringify(binding.value.object))
|
||||||
|
if (unsavedChange === 'on' && !isEqual(oldValue, newValue)) {
|
||||||
|
ElMessageBox.confirm(i18n.t('tip.confirmCancel'), {
|
||||||
|
confirmButtonText: i18n.t('tip.yes'),
|
||||||
|
cancelButtonText: i18n.t('tip.no'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
if (binding.value.func) {
|
||||||
|
binding.value.func()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
binding.value.func()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
el.__vueDomClick__ = domClick
|
||||||
|
el.addEventListener('click', domClick)
|
||||||
|
},
|
||||||
|
unmounted (el, binding) {
|
||||||
|
// 解除事件监听
|
||||||
|
document.removeEventListener('click', el.__vueDomClick__)
|
||||||
|
delete el.__vueDomClick__
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* clickOutside指令 */
|
||||||
|
const exceptClassName = ['prevent-click-outside'] // clickOutside排除的class(白名单)
|
||||||
|
export const clickOutside = {
|
||||||
|
// 初始化指令
|
||||||
|
mounted (el, binding) {
|
||||||
|
if (!binding.expression) return
|
||||||
|
const unsavedChange = localStorage.getItem('cn-unsaved-change')
|
||||||
|
let oldValue
|
||||||
|
try {
|
||||||
|
oldValue = JSON.parse(JSON.stringify(binding.value.object))
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
function documentHandler (e) {
|
||||||
|
if (el.contains(e.target)) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
let flag = true
|
||||||
|
const path = e.path || (e.composedPath && e.composedPath())
|
||||||
|
// eslint-disable-next-line no-labels
|
||||||
|
top: for (let i = 0; i < path.length; i++) {
|
||||||
|
for (let j = 0; j < exceptClassName.length; j++) {
|
||||||
|
if (path[i].className && path[i].className.indexOf && path[i].className.indexOf(exceptClassName[j]) !== -1) {
|
||||||
|
flag = false
|
||||||
|
// eslint-disable-next-line no-labels
|
||||||
|
break top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!flag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (oldValue) {
|
||||||
|
const newValue = JSON.parse(JSON.stringify(binding.value.oldValue))
|
||||||
|
if (unsavedChange === 'on' && !isEqual(oldValue, newValue)) {
|
||||||
|
ElMessageBox.confirm('Confirm?', { // TODO 国际化
|
||||||
|
confirmButtonText: 'Yes', // TODO 国际化
|
||||||
|
cancelButtonText: 'No', // TODO 国际化
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
if (binding.value.func) {
|
||||||
|
binding.value.func()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
binding.value.func()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (binding.arg) {
|
||||||
|
binding.value(e, binding.arg)
|
||||||
|
} else {
|
||||||
|
if (binding.value) {
|
||||||
|
binding.value(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
|
||||||
|
el.__vueClickOutside__ = documentHandler
|
||||||
|
document.addEventListener('mousedown', documentHandler)
|
||||||
|
},
|
||||||
|
unmounted (el, binding) {
|
||||||
|
// 解除事件监听
|
||||||
|
document.removeEventListener('mousedown', el.__vueClickOutside__)
|
||||||
|
delete el.__vueClickOutside__
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEqual (o1, o2) {
|
||||||
|
const isEqualForInner = function (obj1, obj2) {
|
||||||
|
const o1 = obj1 instanceof Object
|
||||||
|
const o2 = obj2 instanceof Object
|
||||||
|
if (!o1 || !o2) {
|
||||||
|
return obj1 === obj2
|
||||||
|
}
|
||||||
|
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr of Object.keys(obj1)) {
|
||||||
|
const t1 = obj1[attr] instanceof Object
|
||||||
|
const t2 = obj2[attr] instanceof Object
|
||||||
|
if (t1 && t2) {
|
||||||
|
if (!isEqualForInner(obj1[attr], obj2[attr])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if (obj1[attr] !== obj2[attr]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return isEqualForInner(o1, o2)
|
||||||
|
}
|
||||||
99
src/views/settings/User.vue
Normal file
99
src/views/settings/User.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<cn-data-list
|
||||||
|
ref="dataList"
|
||||||
|
v-model:custom-table-title="tools.customTableTitle"
|
||||||
|
:api="url"
|
||||||
|
:from="fromRoute.user"
|
||||||
|
:layout="['columnCustomize']"
|
||||||
|
>
|
||||||
|
<template #top-tool-right>
|
||||||
|
<button
|
||||||
|
id="account-add"
|
||||||
|
v-has="'user_add'"
|
||||||
|
class="top-tool-btn margin-r-10"
|
||||||
|
type="button"
|
||||||
|
@click="add"
|
||||||
|
>
|
||||||
|
<i class="cn-icon-create-square cn-icon"/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<user-table
|
||||||
|
ref="dataTable"
|
||||||
|
v-loading="tools.loading"
|
||||||
|
:api="url"
|
||||||
|
:custom-table-title="tools.customTableTitle"
|
||||||
|
:height="mainTableHeight"
|
||||||
|
:table-data="tableData"
|
||||||
|
@del="del"
|
||||||
|
@edit="edit"
|
||||||
|
@orderBy="tableDataSort"
|
||||||
|
@reload="getTableData"
|
||||||
|
@selectionChange="selectionChange"
|
||||||
|
@showBottomBox="(targetTab, object) => { $refs.dataList.showBottomBox(targetTab, object) }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #pagination>
|
||||||
|
<!-- <Pagination ref="Pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></Pagination> -->
|
||||||
|
</template>
|
||||||
|
</cn-data-list>
|
||||||
|
<transition name="right-box">
|
||||||
|
<user-box
|
||||||
|
v-if="rightBox.show"
|
||||||
|
:object="object"
|
||||||
|
@close="closeRightBox"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import cnDataList from '@/components/table/CnDataList'
|
||||||
|
import dataListMixin from '@/mixins/dataList'
|
||||||
|
import userTable from '@/components/table/settings/UserTable'
|
||||||
|
import userBox from '@/components/rightBox/settings/UserBox'
|
||||||
|
import { put } from '@/utils/http'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'User',
|
||||||
|
components: {
|
||||||
|
cnDataList,
|
||||||
|
userTable,
|
||||||
|
userBox
|
||||||
|
},
|
||||||
|
mixins: [dataListMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
url: 'sys/user',
|
||||||
|
blankObject: { // 空白对象
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
email: '',
|
||||||
|
pin: '',
|
||||||
|
mobile: '',
|
||||||
|
status: '1',
|
||||||
|
roleIds: '',
|
||||||
|
pinChange: ''
|
||||||
|
},
|
||||||
|
tableId: 'userTable'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
statusChange (user) {
|
||||||
|
if (user.roles) {
|
||||||
|
user.roleIds = user.roles.map(t => t.id)
|
||||||
|
}
|
||||||
|
put(this.url, user).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.rightBox.show = false
|
||||||
|
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||||
|
} else {
|
||||||
|
this.$message.error(response.msg)
|
||||||
|
}
|
||||||
|
this.getTableData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user