diff --git a/src/assets/css/common.scss b/src/assets/css/common.scss index 716866eb..b80eb230 100644 --- a/src/assets/css/common.scss +++ b/src/assets/css/common.scss @@ -64,3 +64,9 @@ th *:first-letter, .el-table__empty-block { width: 100% !important; } + +/* 淡化amcharts的logo */ +g [aria-labelledby$=-title] { + opacity: 0.1; + transform: translate(calc(100% - 76px), calc(100% - 25px)); +} diff --git a/src/components/amcharts/heatLegend.js b/src/components/amcharts/heatLegend.js new file mode 100644 index 00000000..b112ccca --- /dev/null +++ b/src/components/amcharts/heatLegend.js @@ -0,0 +1,485 @@ +/** + * HeatLegend module + */ +import { __extends } from 'tslib' +/** + * ============================================================================ + * IMPORTS + * ============================================================================ + * @hidden + */ +import { Container } from '@amcharts/amcharts4/.internal/core/Container' +import { LinearGradient } from '@amcharts/amcharts4/.internal/core/rendering/fills/LinearGradient' +import { registry } from '@amcharts/amcharts4/.internal/core/Registry' +import { toColor, Color } from '@amcharts/amcharts4/.internal/core/utils/Color' +import { ListTemplate, ListDisposer } from '@amcharts/amcharts4/.internal/core/utils/List' +import { percent } from '@amcharts/amcharts4/.internal/core/utils/Percent' +import { ValueAxis } from '@amcharts/amcharts4/.internal/charts/axes/ValueAxis' +import { AxisRendererX } from '@amcharts/amcharts4/.internal/charts/axes/AxisRendererX' +import { AxisRendererY } from '@amcharts/amcharts4/.internal/charts/axes/AxisRendererY' +import * as $iter from '@amcharts/amcharts4/.internal/core/utils/Iterator' +import * as $type from '@amcharts/amcharts4/.internal/core/utils/Type' +import * as $colors from '@amcharts/amcharts4/.internal/core/utils/Colors' +import { RoundedRectangle } from '@amcharts/amcharts4/.internal/core/elements/RoundedRectangle' +/** + * ============================================================================ + * MAIN CLASS + * ============================================================================ + * @hidden + */ +/** + * This class creates a link (waved color-filled line) between two nodes in a + * Sankey Diagram. + * + * @see {@link IHeatLegendEvents} for a list of available events + * @see {@link IHeatLegendAdapters} for a list of available Adapters + * @important + */ +const HeatLegend = /** @class */ (function (_super) { + __extends(HeatLegend, _super) + /** + * Constructor + */ + function HeatLegend () { + const _this = _super.call(this) || this + _this.className = 'HeatLegend' + _this.markerContainer = _this.createChild(Container) + _this.markerContainer.shouldClone = false + _this.markerCount = 1 + // Create a template container and list for the a marker + const marker = new RoundedRectangle() + marker.minHeight = 6 + marker.minWidth = 20 + marker.interactionsEnabled = false + marker.fillOpacity = 1 + marker.cornerRadius(0, 0, 0, 0) + _this.markerContainer.minHeight = 6 + _this.markerContainer.minWidth = 20 + _this.orientation = 'horizontal' + _this.markers = new ListTemplate(marker) + _this._disposers.push(new ListDisposer(_this.markers)) + _this._disposers.push(_this.markers.template) + _this.applyTheme() + return _this + } + HeatLegend.prototype.getMinFromRules = function (property) { + const series = this.series + if (series) { + let minValue_1 + $iter.eachContinue(series.heatRules.iterator(), function (heatRule) { + if (heatRule.property == property) { + minValue_1 = heatRule.min + return false + } + return true + }) + return minValue_1 + } + } + HeatLegend.prototype.getMaxFromRules = function (property) { + const series = this.series + if (series) { + let maxValue_1 + $iter.each(series.heatRules.iterator(), function (heatRule) { + if (heatRule.property == property) { + maxValue_1 = heatRule.max + return false + } + return true + }) + return maxValue_1 + } + } + /** + * + * @ignore Exclude from docs + */ + HeatLegend.prototype.validate = function () { + _super.prototype.validate.call(this) + this.valueAxis.renderer.inversed = this.reverseOrder + const series = this.series + let minColor = this.minColor + let maxColor = this.maxColor + if (!$type.hasValue(minColor)) { + minColor = toColor(this.getMinFromRules('fill')) + } + if (!$type.hasValue(maxColor)) { + maxColor = toColor(this.getMaxFromRules('fill')) + } + if (series) { + const seriesFill = series.fill + if (!$type.hasValue(minColor) && seriesFill instanceof Color) { + minColor = seriesFill + } + if (!$type.hasValue(maxColor) && seriesFill instanceof Color) { + maxColor = seriesFill + } + } + if (!$type.hasValue(maxColor)) { + maxColor = toColor(this.getMaxFromRules('fill')) + } + let minOpacity = $type.toNumber(this.getMinFromRules('fillOpacity')) + if (!$type.isNumber(minOpacity)) { + minOpacity = 1 + } + let maxOpacity = $type.toNumber(this.getMaxFromRules('fillOpacity')) + if (!$type.isNumber(maxOpacity)) { + maxOpacity = 1 + } + let minStrokeOpacity = $type.toNumber(this.getMinFromRules('strokeOpacity')) + if (!$type.isNumber(minStrokeOpacity)) { + minStrokeOpacity = 1 + } + let maxStrokeOpacity = $type.toNumber(this.getMaxFromRules('strokeOpacity')) + if (!$type.isNumber(maxStrokeOpacity)) { + maxStrokeOpacity = 1 + } + const minStroke = toColor(this.getMinFromRules('stroke')) + const maxStroke = toColor(this.getMaxFromRules('stroke')) + // if (series) { + for (var i = 0; i < this.markerCount; i++) { + let marker = this.markers.getIndex(i) + if (!marker) { + marker = this.markers.create() + marker.parent = this.markerContainer + marker.height = percent(100) + marker.width = percent(100) + } + if (this.markerCount == 1) { + const gradient = new LinearGradient() + if (this.reverseOrder) { + gradient.addColor(maxColor, maxOpacity) + gradient.addColor(minColor, minOpacity) + } else { + gradient.addColor(minColor, minOpacity) + gradient.addColor(maxColor, maxOpacity) + } + if (this.orientation == 'vertical') { + gradient.rotation = -90 + } + marker.fill = gradient + if ($type.hasValue(minStroke) && $type.hasValue(maxStroke)) { + const strokeGradient = new LinearGradient() + if (this.reverseOrder) { + strokeGradient.addColor(maxStroke, maxStrokeOpacity) + strokeGradient.addColor(minStroke, minStrokeOpacity) + } else { + strokeGradient.addColor(minStroke, minStrokeOpacity) + strokeGradient.addColor(maxStroke, maxStrokeOpacity) + } + if (this.orientation == 'vertical') { + strokeGradient.rotation = -90 + } + marker.stroke = strokeGradient + } + } else { + let c = i + if (this.reverseOrder) { + c = this.markerCount - i - 1 + } + const color = new Color($colors.interpolate(minColor.rgb, maxColor.rgb, c / this.markerCount)) + marker.fill = color + const opacity = minOpacity + (maxOpacity - minOpacity) * c / this.markerCount + marker.fillOpacity = opacity + if ($type.hasValue(minStroke) && $type.hasValue(maxStroke)) { + const color_1 = new Color($colors.interpolate(minStroke.rgb, maxStroke.rgb, c / this.markerCount)) + marker.stroke = color_1 + const opacity_1 = minStrokeOpacity + (maxStrokeOpacity - minStrokeOpacity) * c / this.markerCount + marker.strokeOpacity = opacity_1 + } + } + } + const renderer = this.valueAxis.renderer + if (this.markerCount > 1) { + if (this.orientation == 'horizontal') { + renderer.minGridDistance = this.measuredWidth / this.markerCount + } else { + renderer.minGridDistance = this.measuredHeight / this.markerCount + } + } + this.valueAxis.invalidate() + for (var i = this.markerCount, len = this.markers.length; i < len; i++) { + this.markers.getIndex(i).parent = undefined + } + } + Object.defineProperty(HeatLegend.prototype, 'minColor', { + /** + * Returns minColor value + * @return {Color} + */ + get: function () { + return this.getPropertyValue('minColor') + }, + /** + * Min color of a heat legend. If a series is set for the legend, minColor is taken from series. + * + * @param {Color} + */ + set: function (value) { + if (!(value instanceof Color)) { + value = toColor(value) + } + this.setColorProperty('minColor', value, true) + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'maxColor', { + /** + * Returns maxColor value + * @return {Color} + */ + get: function () { + return this.getPropertyValue('maxColor') + }, + /** + * Max color of a heat legend. If a series is set for the legend, maxColor is taken from series. + * + * @param {Color} + */ + set: function (value) { + if (!(value instanceof Color)) { + value = toColor(value) + } + this.setColorProperty('maxColor', value, true) + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'markerCount', { + /** + * Returns number of color squares (markers). + * @return {number} + */ + get: function () { + return this.getPropertyValue('markerCount') + }, + /** + * Number of color squares (markers) in the heat legend. If only 1 marker is used, it will be filled with gradient. + * + * @param {number} + */ + set: function (value) { + this.setPropertyValue('markerCount', value, true) + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'minValue', { + /** + * Returns minimum value of heat legend. + * @return {number} + */ + get: function () { + return this.getPropertyValue('minValue') + }, + /** + * Minimum value of heat legend's value axis. If a series is set for the legend, min is taken from series. + * + * @param {number} + */ + set: function (value) { + this.setPropertyValue('minValue', value) + this.valueAxis.min = value + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'maxValue', { + /** + * Returns maximum value of heat legend. + * @return {number} + */ + get: function () { + return this.getPropertyValue('maxValue') + }, + /** + * Maximum value of heat legend's value axis. If a series is set for the legend, max is taken from series. + * + * @param {number} + */ + set: function (value) { + this.setPropertyValue('maxValue', value) + this.valueAxis.max = value + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'orientation', { + /** + * Returns orientation value. + * + * @return {"horizontal" | "vertical"} + */ + get: function () { + return this.getPropertyValue('orientation') + }, + /** + * Heat legend orientation. Note, if you change orientation of a heat legend, you must set value axis renderer properties after that, as with orientation renderer changes. + * + * @param {"horizontal" | "vertical"} + */ + set: function (value) { + this.setPropertyValue('orientation', value, true) + const markerContainer = this.markerContainer + const valueAxis = this.valueAxis + // HORIZONTAL + if (value == 'horizontal') { + if (!$type.hasValue(this.width)) { + this.width = 200 + } + this.height = undefined + valueAxis.width = percent(100) + valueAxis.height = undefined + valueAxis.tooltip.pointerOrientation = 'vertical' + this.layout = 'vertical' + markerContainer.width = percent(100) + markerContainer.height = undefined + if (!(valueAxis.renderer instanceof AxisRendererX)) { + valueAxis.renderer = new AxisRendererX() + } + } + // VERTICAL + else { + if (!$type.hasValue(this.height)) { + this.height = 200 + } + this.width = undefined + this.layout = 'horizontal' + markerContainer.width = undefined + markerContainer.height = percent(100) + valueAxis.height = percent(100) + valueAxis.width = undefined + valueAxis.tooltip.pointerOrientation = 'horizontal' + if (!(valueAxis.renderer instanceof AxisRendererY)) { + valueAxis.renderer = new AxisRendererY() + } + valueAxis.renderer.inside = true + valueAxis.renderer.labels.template.inside = true + this.markerContainer.reverseOrder = true + } + const renderer = valueAxis.renderer + renderer.grid.template.disabled = true + renderer.axisFills.template.disabled = true + renderer.baseGrid.disabled = true + renderer.labels.template.padding(2, 3, 2, 3) + renderer.minHeight = undefined + renderer.minWidth = undefined + this.markerContainer.layout = value + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'valueAxis', { + /** + * Returns valueAxis value. + * @return {ValueAxis} + */ + get: function () { + if (!this._valueAxis) { + this.valueAxis = this.createChild(ValueAxis) + this.valueAxis.shouldClone = false + } + return this._valueAxis + }, + /** + * Sets a value axis of heat legend. Value axis for heat legend is created automatically. + * @param {ValueAxis} + */ + set: function (valueAxis) { + this._valueAxis = valueAxis + valueAxis.parent = this + valueAxis.strictMinMax = true + this.orientation = this.orientation + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(HeatLegend.prototype, 'series', { + /** + * Returns series value. + * @return {Series} + */ + get: function () { + return this._series + }, + /** + * You can set series for heat legend. It will take min, max, minColor and maxColor values from this series. + * @param series + */ + set: function (series) { + const _this = this + this._series = series + let dataField = 'value' + try { + const dataFieldDefined = series.heatRules.getIndex(0).dataField + if (dataFieldDefined) { + dataField = dataFieldDefined + } + } catch (err) { + } + this.updateMinMax(series.dataItem.values[dataField].low, series.dataItem.values[dataField].high) + series.dataItem.events.on('calculatedvaluechanged', function (event) { + _this.updateMinMax(series.dataItem.values[dataField].low, series.dataItem.values[dataField].high) + }, undefined, false) + series.heatRules.events.on('inserted', this.invalidate, this, false) + series.heatRules.events.on('removed', this.invalidate, this, false) + }, + enumerable: true, + configurable: true + }) + /** + * Updates min/max of value axis. + * @ignore + */ + HeatLegend.prototype.updateMinMax = function (min, max) { + const valueAxis = this.valueAxis + if (!$type.isNumber(this.minValue)) { + valueAxis.min = min + valueAxis.invalidate() + } + if (!$type.isNumber(this.maxValue)) { + valueAxis.max = max + valueAxis.invalidate() + } + } + /** + * Processes JSON-based config before it is applied to the object. + * + * @ignore Exclude from docs + * @param config Config + */ + HeatLegend.prototype.processConfig = function (config) { + if (config) { + // Set up series + if ($type.hasValue(config.series) && $type.isString(config.series)) { + if ($type.isString(config.series)) { + if (this.map.hasKey(config.series)) { + config.series = this.map.getKey(config.series) + } else { + const seriesId_1 = config.series + var disposer_1 = this.map.events.on('insertKey', function (ev) { + if (ev.key == seriesId_1) { + this.series = ev.newValue + disposer_1.dispose() + } + }, this) + this._disposers.push(disposer_1) + delete config.series + } + } + } + } + _super.prototype.processConfig.call(this, config) + } + return HeatLegend +}(Container)) +export { HeatLegend } +/** + * Register class in system, so that it can be instantiated using its name from + * anywhere. + * + * @ignore + */ +registry.registeredClasses.HeatLegend = HeatLegend +// # sourceMappingURL=HeatLegend.js.map diff --git a/src/views/charts/Chart.vue b/src/views/charts/Chart.vue index a0176b29..0e80b474 100644 --- a/src/views/charts/Chart.vue +++ b/src/views/charts/Chart.vue @@ -190,6 +190,7 @@ import unitConvert, { getUnitType } from '@/utils/unit-convert' import { chartTableDefaultPageSize, chartTableTopOptions, storageKey, chartPieTableTopOptions, unitTypes } from '@/utils/constants' import { get } from '@/utils/http' import { replaceUrlPlaceholder, getCapitalGeo, getGeoData, lineToSpace } from '@/utils/tools' +import { HeatLegend } from '@/components/amcharts/heatLegend' export default { name: 'Chart', @@ -352,12 +353,6 @@ export default { const polygonSeries = chart.series.push(new am4Maps.MapPolygonSeries()) polygonSeries.useGeodata = true polygonSeries.exclude = ['AQ'] // 排除南极洲 - // 鼠标悬停提示 - /* const polygonTemplate = polygonSeries.mapPolygons.template - polygonTemplate.tooltipText = '{name}' - polygonTemplate.fontSize = '12px' - const hs = polygonTemplate.states.create('hover') - hs.properties.fill = am4Core.color('#ccc') */ return { chart, polygonSeries @@ -443,13 +438,6 @@ export default { }) imageSeries.data = pointData } else if (this.isMapBlock) { - polygonSeries.heatRules.push({ - property: 'fill', - target: polygonSeries.mapPolygons.template, - min: am4Core.color('#EABA2B'), - max: am4Core.color('#D95D41') - }) - const seriesData = data.map(r => { const value = r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions return { @@ -460,22 +448,29 @@ export default { }) polygonSeries.data = [...seriesData] const sorted = seriesData.sort((a, b) => b.value - a.value) + const allZero = this.$_.isEmpty(sorted) || Number(sorted[0].value) === 0 // 数据全为0的情况,legend只显示1个颜色 - const heatLegend = this.myChart.createChild(am4Maps.HeatLegend) - heatLegend.series = polygonSeries - heatLegend.align = 'center' - heatLegend.minValue = 0 - if (!this.$_.isEmpty(sorted)) { - heatLegend.maxValue = Number(sorted[0].value) - } + polygonSeries.heatRules.push({ + property: 'fill', + target: polygonSeries.mapPolygons.template, + min: am4Core.color('#EABA2B'), + max: allZero ? am4Core.color('#EABA2B') : am4Core.color('#D95D41') + }) + + const heatLegend = this.myChart.createChild(HeatLegend) heatLegend.markerContainer.height = 6 - heatLegend.width = am4Core.percent(25) - heatLegend.marginLeft = am4Core.percent(4) + heatLegend.series = polygonSeries + heatLegend.align = 'left' + heatLegend.markerCount = allZero ? 1 : 3 + heatLegend.minValue = 0 + heatLegend.fontSize = 12 + heatLegend.maxValue = allZero ? 1 : Number(sorted[0].value) + heatLegend.width = allZero ? am4Core.percent(10) : am4Core.percent(25) + heatLegend.marginLeft = 15 heatLegend.valign = 'bottom' const polygonTemplate = polygonSeries.mapPolygons.template polygonTemplate.tooltipText = '{name}{title}' - // polygonTemplate.numberFormatter = new am4Core.NumberFormatter().bigNumberPrefixes polygonTemplate.nonScalingStroke = true polygonTemplate.strokeWidth = 0.5 }