init
This commit is contained in:
@@ -4,16 +4,13 @@ module.exports = {
|
||||
es2021: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'plugin:vue/vue3-essential',
|
||||
'standard'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: [
|
||||
'vue'
|
||||
],
|
||||
rules: {
|
||||
eqeqeq: 0, // 关闭必须使用全等
|
||||
'no-extend-native': 0,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "sn",
|
||||
"name": "cn",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -14,13 +14,14 @@
|
||||
"element-plus": "^1.0.2-beta.44",
|
||||
"lib-flexible": "^0.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"node-sass": "^4.14.1",
|
||||
"postcss-px2rem-exclude": "0.0.6",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sass-resources-loader": "^2.2.1",
|
||||
"vue": "^3.0.0",
|
||||
"vue-grid-layout": "^2.3.12",
|
||||
"vue-i18n": "^8.24.4",
|
||||
"vue-i18n": "^9.1.6",
|
||||
"vue-router": "^4.0.8",
|
||||
"vuex": "^4.0.1"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
autoprefixer: {}
|
||||
/* 'postcss-px2rem-exclude': {
|
||||
remUnit: 16,
|
||||
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>
|
||||
</template>
|
||||
<script>
|
||||
import { get } from '@/utils/http'
|
||||
import axios from 'axios'
|
||||
|
||||
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>
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
<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>
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex'
|
||||
import { post } from '@/utils/http'
|
||||
import bus from '@/utils/bus'
|
||||
|
||||
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>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import './font/iconfont';
|
||||
@import './theme';
|
||||
@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变量 **/
|
||||
|
||||
$--color-primary: #0091ff; // 主题色
|
||||
|
||||
/* menu相关 */
|
||||
@@ -11,6 +10,55 @@ $--menu-hover-background-color: #000C18; // menu背景色
|
||||
$--menu-item-font-color: #BEBEBE; // 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变量文件 **/
|
||||
$--font-path: '~element-plus/lib/theme-chalk/fonts';
|
||||
@import "~element-plus/packages/theme-chalk/src/index";
|
||||
:export {
|
||||
themeColor: $--color-primary;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<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">
|
||||
<div id="home-to-overview" class="logo link">
|
||||
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>
|
||||
@@ -8,26 +12,49 @@
|
||||
</div>
|
||||
</el-menu-item>
|
||||
</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">
|
||||
<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>
|
||||
<i :class="menu.icon"></i>
|
||||
<span>{{menu.name}}</span>
|
||||
</template>
|
||||
<template v-for="(secondMenu, secondIndex) in menu.children">
|
||||
<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>
|
||||
<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>
|
||||
</template>
|
||||
<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>
|
||||
</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>
|
||||
<span slot="title" class="data-column__span">{{menu.name}}</span>
|
||||
</el-menu-item>
|
||||
@@ -126,11 +153,16 @@ export default {
|
||||
.el-menu-item.is-active {
|
||||
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 {
|
||||
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 {
|
||||
overflow: hidden;
|
||||
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 router from './router'
|
||||
import store from './store'
|
||||
import App from './App.vue'
|
||||
import _ from 'lodash'
|
||||
|
||||
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 ElementPlus from 'element-plus'
|
||||
@@ -11,5 +16,14 @@ const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
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')
|
||||
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'),
|
||||
children: [
|
||||
{
|
||||
path: '/traffic',
|
||||
path: '/trafficSummary',
|
||||
component: () => import('@/views/dashboards/TrafficSummary')
|
||||
},
|
||||
{
|
||||
path: '/na',
|
||||
path: '/networkAppPerformance',
|
||||
component: () => import('@/views/dashboards/TrafficSummary')
|
||||
},
|
||||
{
|
||||
path: '/dns',
|
||||
path: '/dnsServiceInsights',
|
||||
component: () => import('@/views/dashboards/TrafficSummary')
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
component: () => import('@/views/settings/User')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const store = createStore({
|
||||
mutations: {
|
||||
isShrink (state) {
|
||||
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 = {
|
||||
state() {
|
||||
state () {
|
||||
return {
|
||||
menuList: [],
|
||||
buttonList: [],
|
||||
@@ -24,65 +31,7 @@ const user = {
|
||||
},
|
||||
getters: {
|
||||
menuList (state) {
|
||||
const menuList = JSON.parse(`[
|
||||
{
|
||||
"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
|
||||
return state.menuList
|
||||
},
|
||||
buttonList (state) {
|
||||
return state.buttonList
|
||||
@@ -93,11 +42,37 @@ const user = {
|
||||
},
|
||||
actions: {
|
||||
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) {
|
||||
sessionStorage.removeItem('nz-username')
|
||||
localStorage.removeItem('nz-username')
|
||||
sessionStorage.removeItem('nz-token')
|
||||
sessionStorage.removeItem('cn-username')
|
||||
localStorage.removeItem('cn-username')
|
||||
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