diff --git a/nezha-fronted/package-lock.json b/nezha-fronted/package-lock.json
index 72d7a2fd9..733cf7072 100644
--- a/nezha-fronted/package-lock.json
+++ b/nezha-fronted/package-lock.json
@@ -3986,6 +3986,16 @@
"integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==",
"dev": true
},
+ "clipboard": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+ "requires": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"cliui": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
@@ -6121,6 +6131,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
+ "delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -8428,6 +8443,14 @@
"minimatch": "~3.0.2"
}
},
+ "good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+ "requires": {
+ "delegate": "^3.1.2"
+ }
+ },
"got": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz",
@@ -16336,6 +16359,11 @@
}
}
},
+ "select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+ },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -17807,6 +17835,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
+ "tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"tinycolor2": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
@@ -18486,6 +18519,14 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
},
+ "vue-clipboard2": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/vue-clipboard2/-/vue-clipboard2-0.3.3.tgz",
+ "integrity": "sha512-aNWXIL2DKgJyY/1OOeITwAQz1fHaCIGvUFHf9h8UcoQBG5a74MkdhS/xqoYe7DNZdQmZRL+TAdIbtUs9OyVjbw==",
+ "requires": {
+ "clipboard": "^2.0.0"
+ }
+ },
"vue-color": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/vue-color/-/vue-color-2.8.1.tgz",
diff --git a/nezha-fronted/src/assets/css/common.scss b/nezha-fronted/src/assets/css/common.scss
index 166462773..206e19ee5 100644
--- a/nezha-fronted/src/assets/css/common.scss
+++ b/nezha-fronted/src/assets/css/common.scss
@@ -476,3 +476,8 @@ textarea {
.ql-tooltip {
background: $--background-color-empty;
}
+
+
+.chart-time-series.hide{
+ display: none !important;
+}
diff --git a/nezha-fronted/src/assets/css/common/tableCommon.scss b/nezha-fronted/src/assets/css/common/tableCommon.scss
index 7d70ffce7..deb815e7b 100644
--- a/nezha-fronted/src/assets/css/common/tableCommon.scss
+++ b/nezha-fronted/src/assets/css/common/tableCommon.scss
@@ -88,12 +88,15 @@
background-color: $--background-color-empty;
transition:all .2s;
color: $--button-icon-color;
-
i {
font-size: 14px;
color: $--button-icon-color;
}
}
+ .top-tool-btn.active{
+ background-color: $--button-icon-active-background-color;
+ border: 1px solid $--button-icon-active-border-color !important;
+ }
.top-tool-btn.top-tool-btn--text {
padding: 0 8px;
width: unset;
diff --git a/nezha-fronted/src/assets/css/components/common/rightBox/panelBox.scss b/nezha-fronted/src/assets/css/components/common/rightBox/panelBox.scss
new file mode 100644
index 000000000..86d720ff9
--- /dev/null
+++ b/nezha-fronted/src/assets/css/components/common/rightBox/panelBox.scss
@@ -0,0 +1,217 @@
+.right-box-panel{
+ .item-receivers{
+ .el-select.el-select--small{
+ width: 100%;
+ }
+ .item-receivers-text{
+ color: $--color-text-secondary;
+ }
+ }
+ .el-input--suffix.el-date-editor--datetime .el-input__inner{
+ padding-left: 15px;
+ }
+ .form-items--half-width-group{
+ .item-receivers{
+ width: 100%;
+ .el-select.el-select--small{
+ width: 100%;
+ }
+ }
+ .form-item--half-width{
+ .el-date-editor.el-date-editor--datetime{
+ width: 100%;
+ .el-input__inner{
+ height: 32px;
+ }
+ }
+ }
+ .check-month_box{
+ .el-checkbox-group{
+ display: flex;
+ justify-content: start;
+ align-items: center;
+ flex-wrap: wrap;
+ .el-checkbox-button{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: calc(100% / 7);
+ margin-right: -2px;
+ margin-bottom: -1px;
+ border: 1px solid $--border-color-light;
+ .el-checkbox-button__inner{
+ border: 0px;
+ border-left: 0px !important;
+ border-bottom: 0px !important;
+ display: block;
+ width: 100%;
+ height: 100%;
+ background-color: $--background-color-empty;
+ border-radius: 0;
+ }
+ }
+ }
+
+ .el-checkbox-button.is-checked .el-checkbox-button__inner {
+ color: #fff;
+ border-radius: 0;
+ background-color: $--color-primary !important;
+ }
+ }
+ .range-time{
+ width: 100%;
+ .el-form-item__content{
+ height: 41px;
+ .el-radio-group{
+ height: 100%;
+ border-left: 1px solid $--border-color-light;
+ .el-radio-button__inner{
+ height: 100%;
+ line-height: 1.5;
+ font-size: 14px;
+ color: $--color-text-primary;
+ }
+ }
+ .el-checkbox-group{
+ height: 100%;
+ .el-checkbox-button{
+ margin-right: -2px;
+ .el-checkbox-button__inner{
+ background-color: $--background-color-empty;
+ border:1px solid $--border-color-light;
+ }
+ }
+ }
+ }
+ .el-radio-group{
+ display: flex;
+ border-left: 0;
+ .el-radio-button{
+ flex: 1;
+ }
+ }
+
+ .el-checkbox-button.is-checked .el-checkbox-button__inner {
+ color: #fff;
+ border-radius: 0;
+ background-color: $--color-primary !important;
+ border: 1px solid $--color-primary !important;
+ // margin-right: -1px;
+ }
+ }
+ .form-tabs{
+ width: 100%;
+ .el-tabs__nav{
+ display: flex;
+ .el-tabs__item{
+ flex: 1;
+ }
+ .el-checkbox-button.is-checked .el-checkbox-button__inner {
+ color: #fff;
+ border-radius: 0;
+ background-color: $--color-primary !important;
+ border: 1px solid $--color-primary !important;
+ // margin-right: -1px;
+ }
+ }
+ }
+ .form-day-week{
+ width: 100%;
+ .el-input-group__append{
+ border: 1px solid $--border-color-light;
+ background-color: $--right-box-sub-title-background-color;
+ }
+ }
+ }
+ .variable-box{
+ margin-bottom: 16px;
+ .variable-title{
+ border: 1px solid $--border-color-light;
+ border-radius: 2px;
+ position: relative;
+ display: flex;
+ padding: 0;
+ padding-left: 5px;
+ line-height: 30px;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ box-sizing: border-box;
+ height: 32px;
+ .variable-title-left{
+ width: 450px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ .nz-icon-arrow-down{
+ display: inline-block;
+ transition: transform .3s;
+ cursor: pointer;
+ }
+ .nz-icon-arrow-down.is-active{
+ transform: rotate(-90deg );
+ }
+ &>span{
+ font-size: 14px;
+ color: $--color-text-regular;
+ }
+ }
+ .nz-icon{
+ cursor: pointer;
+ color: $--color-text-primary;
+ }
+ .nz-icon-mimabukejian{
+ font-size: 18px;
+ }
+ }
+ .variable-title{
+ margin-bottom: 0;
+ margin-top: 10px;
+ }
+ .add-variable{
+ border: 1px solid $--border-color-light;
+ border-radius: 2px;
+ text-align: center;
+ margin-bottom: 10px;
+ padding: 3px 10px;
+ color: $--color-text-regular;
+ font-size: 14px;
+ cursor: pointer;
+ margin-top: 16px;
+ }
+ }
+
+ }
+
+.el-form-item__content{
+ text-align: unset !important;
+ .el-checkbox-button{
+ width: calc(100% / 7) !important;
+ .el-checkbox-button__inner{
+ width: 100%;
+ padding: 11px 3px;
+ }
+
+ }
+ .el-checkbox-button.is-focus .el-checkbox-button__inner{
+ border-color: $--border-color-base;
+ }
+
+ .el-tabs__item.is-top.is-active{
+ color: #fff !important;
+ background-color: $--color-primary !important;
+ }
+ .el-radio-button.el-radio-button--small.is-active{
+ .el-radio-button__inner{
+ color: #fff !important;
+ }
+ }
+}
+
+.multiTip{
+ max-width:200px;
+}
+.allOptionTip{
+ max-width:208px;
+}
+
+
\ No newline at end of file
diff --git a/nezha-fronted/src/assets/css/components/index.scss b/nezha-fronted/src/assets/css/components/index.scss
index 1f13dcb64..17add18bb 100644
--- a/nezha-fronted/src/assets/css/components/index.scss
+++ b/nezha-fronted/src/assets/css/components/index.scss
@@ -51,6 +51,7 @@
@import './common/rightBox/chartRightBox/chartRightBox.scss';
@import './common/rightBox/mibBox.scss';
@import './common/rightBox/assetBactchEditBox.scss';
+@import './common/rightBox/panelBox.scss';
@import './common/table/alert/alertMessageTable.scss';
@import './common/table/alert/alertRuleTable.scss';
@import './common/table/alert/alertSilenceTable.scss';
diff --git a/nezha-fronted/src/components/chart/chart/chartTimeSeries.vue b/nezha-fronted/src/components/chart/chart/chartTimeSeries.vue
index 7b83c7dfc..30e8b5f0c 100644
--- a/nezha-fronted/src/components/chart/chart/chartTimeSeries.vue
+++ b/nezha-fronted/src/components/chart/chart/chartTimeSeries.vue
@@ -26,7 +26,7 @@ import { formatScientificNotation } from '@/components/common/js/tools'
import chartDataFormat from '@/components/chart/chartDataFormat'
import { randomcolor } from '@/components/common/js/radomcolor/randomcolor'
import { chartLegendPlacement } from '@/components/common/js/constants'
-import { getChart, setChart } from '@/components/common/js/common'
+import { getChart, setChart, chartCache } from '@/components/common/js/common'
import { initColor } from '@/components/chart/chart/tools'
import lodash from 'lodash'
@@ -69,6 +69,36 @@ export default {
}
}
},
+ watch: {
+ // 监听当前鼠标所在的图表id变化
+ '$store.state.panel.currentMousemove': {
+ handler (n) {
+ // 判断是否是当前鼠标所在的图表
+ if (n === this.chartId) {
+ let option = {}
+ for (const key in chartCache) {
+ if (!chartCache[key] || chartCache[key].group !== 'timeSeriesGroup') {
+ continue
+ }
+ if (chartCache[key] === getChart(this.chartId)) {
+ option = {
+ tooltip: {
+ className: 'chart-time-series'
+ }
+ }
+ } else {
+ option = {
+ tooltip: {
+ className: 'chart-time-series hide'
+ }
+ }
+ }
+ chartCache[key].setOption(option)
+ }
+ }
+ }
+ }
+ },
methods: {
initChart (chartOptions = this.chartOption) {
try {
@@ -122,6 +152,22 @@ export default {
}
myChart.setOption(chartOption)
this.isInit && setChart(this.chartId, myChart) // 缓存;不使用vue的data是为避免整个chart被监听导致卡顿
+
+ this.$store.commit('setCurrentMousemove', 0)
+ if (this.isInit) {
+ // timeSeries类型图表设置group 用于多表联动
+ myChart.group = 'timeSeriesGroup'
+ myChart.getZr().on('mousemove', params => {
+ if (this.$store.state.panel.isConnect !== 'crosshair') {
+ return false
+ }
+ // crosshair 模式才会执行
+ if (this.$store.state.panel.currentMousemove !== this.chartId) {
+ this.$store.commit('setCurrentMousemove', this.chartId)
+ }
+ })
+ }
+
this.isInit = false
}, 200)
},
@@ -191,7 +237,7 @@ export default {
return { minTime, maxTime, minValue, maxValue, copies, unit, dot }
},
xAxisLabelFormatter (minTime, maxTime) {
- let self = this
+ const self = this
return function (val, index) {
const value = val * 1000
let offset = localStorage.getItem('nz-sys-timezone')
diff --git a/nezha-fronted/src/components/chart/chart/legend.vue b/nezha-fronted/src/components/chart/chart/legend.vue
index 39aecf264..d9aade502 100644
--- a/nezha-fronted/src/components/chart/chart/legend.vue
+++ b/nezha-fronted/src/components/chart/chart/legend.vue
@@ -44,6 +44,8 @@ import lodash from 'lodash'
import { getChart } from '@/components/common/js/common'
import chartDataFormat from '@/components/chart/chartDataFormat'
import { statisticsList } from '@/components/common/js/constants'
+import * as chart from 'echarts'
+import { isTimeSeries } from './tools'
export default {
name: 'chartLegend',
props: {
@@ -65,6 +67,10 @@ export default {
computed: {
isStatistics () {
return !lodash.isEmpty(this.chartInfo.param.legend.values)
+ },
+ // timeSeries类型图表联动
+ isConnect () {
+ return this.$store.state.panel.isConnect
}
},
methods: {
@@ -94,6 +100,10 @@ export default {
}
if (echarts) {
+ // 判断timeSeries类型图表 先取消多表联动
+ if (isTimeSeries(this.chartInfo.type) && (this.isConnect && this.isConnect !== 'none')) {
+ chart.disconnect('timeSeriesGroup')
+ }
if (!hasGrey) { // 1.除当前legend外全置灰
echarts.dispatchAction({
type: 'legendInverseSelect'
@@ -116,6 +126,10 @@ export default {
})
this.$set(this.isGrey, index, !this.isGrey[index])
}
+ // 判断timeSeries类型图表 建立多表联动
+ if (isTimeSeries(this.chartInfo.type) && (this.isConnect && this.isConnect !== 'none')) {
+ chart.connect('timeSeriesGroup')
+ }
if (this.chartInfo.type !== 'pie' && this.chartInfo.type !== 'bar' && this.chartInfo.type !== 'treemap') {
// 处理点击后的 Y轴
const chartInfo = this.chartInfo
diff --git a/nezha-fronted/src/components/chart/chart/options/chartTimeSeries.js b/nezha-fronted/src/components/chart/chart/options/chartTimeSeries.js
index fc47e9bf8..3fcf01ce5 100644
--- a/nezha-fronted/src/components/chart/chart/options/chartTimeSeries.js
+++ b/nezha-fronted/src/components/chart/chart/options/chartTimeSeries.js
@@ -32,14 +32,20 @@ export const chartTimeSeriesLineOption = {
}
},
tooltip: {
+ show: true,
trigger: 'axis',
confine: false,
extraCssText: 'z-index:99999999;',
z: 9,
animation: false,
appendToBody: true,
- className: 'chart-time-series'
- // formatter: 动态生成
+ className: 'chart-time-series',
+ axisPointer: {
+ type: 'cross', // 十字准星指示器,表示启用两个正交的轴的 axisPointer
+ label: {
+ show: false
+ }
+ }
},
color: initColor(),
grid: {
@@ -61,9 +67,9 @@ export const chartTimeSeriesLineOption = {
fontSize: 10
// formatter: 动态生成
},
- axisPointer: { // y轴上显示指针对应的值
- show: true
- },
+ // axisPointer: { // y轴上显示指针对应的值
+ // show: true
+ // },
splitLine: {
show: true,
lineStyle: {
diff --git a/nezha-fronted/src/components/common/js/common.js b/nezha-fronted/src/components/common/js/common.js
index 5ccc57a0b..91099ac62 100644
--- a/nezha-fronted/src/components/common/js/common.js
+++ b/nezha-fronted/src/components/common/js/common.js
@@ -16,7 +16,7 @@ export function getUUID () {
return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
}
-const chartCache = {}
+export const chartCache = {}
export function getChart (key) {
return chartCache[`chart${key}`]
diff --git a/nezha-fronted/src/components/common/rightBox/chart/publicConfig.js b/nezha-fronted/src/components/common/rightBox/chart/publicConfig.js
index 05553f0a0..6d2b9667b 100644
--- a/nezha-fronted/src/components/common/rightBox/chart/publicConfig.js
+++ b/nezha-fronted/src/components/common/rightBox/chart/publicConfig.js
@@ -362,7 +362,7 @@ export default {
showInput (index, flag) {
this.expressionsShow[index].hideInput = flag
},
- transformNumToLetter (num) { // 相当于26进制 获取id
+ transformNumToLetter (num) { // 相当于26进制 获取idaddExpression
const self = this
let letter = ''
const loopNum = parseInt(num / 26)
diff --git a/nezha-fronted/src/components/common/rightBox/panelBox.vue b/nezha-fronted/src/components/common/rightBox/panelBox.vue
index 721426ec2..b0d1661d8 100644
--- a/nezha-fronted/src/components/common/rightBox/panelBox.vue
+++ b/nezha-fronted/src/components/common/rightBox/panelBox.vue
@@ -14,6 +14,261 @@