diff --git a/src/Login.vue b/src/Login.vue index ca00f492..5d813f70 100644 --- a/src/Login.vue +++ b/src/Login.vue @@ -27,7 +27,7 @@ class="inputstyle login__button" @click="login" style=" font-size: 16px;" - >登陆{{$t('common.login')}} @@ -38,8 +38,6 @@ - - diff --git a/src/components/charts/chart-options.js b/src/components/charts/chart-options.js index 616a5ea2..562dc65b 100644 --- a/src/components/charts/chart-options.js +++ b/src/components/charts/chart-options.js @@ -45,6 +45,38 @@ const line = { } ] } +const lineWithStatistics = { + xAxis: { + type: 'time' + }, + yAxis: { + type: 'value' + }, + legend: { + show: true, + top: 'top', + orient: 'horizontal', + icon: 'circle', + itemGap: 20, + itemWidth: 10, + textStyle: { + padding: [0, 0, 0, 5], + fontSize: 14 + } + }, + axisLabel: { + fontSize: 14 + }, + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + data: [] + } + ] +} const lineStack = { xAxis: { type: 'time' @@ -144,7 +176,8 @@ const pieWithTable = { } const typeOptionMappings = [ { value: 11, option: line }, // 常规折线图 - { value: 13, option: lineStack }, // 常规折线图 + { value: 12, option: lineWithStatistics }, // 带统计表格的折线图 + { value: 13, option: lineStack }, // 折线堆叠图 { value: 31, option: pieWithTable } // 常规折线图 ] const typeCategory = { diff --git a/src/components/charts/panel.scss b/src/components/charts/panel.scss index f8e62092..0a451b3a 100644 --- a/src/components/charts/panel.scss +++ b/src/components/charts/panel.scss @@ -1,4 +1,4 @@ -.cn-panel { +.cn-panel, .cn-panel>.cn-chart__tabs>.el-tabs__content>.el-tab-pane { display: grid; grid-template-columns: repeat(30, 1fr); grid-auto-flow: row; diff --git a/src/main.js b/src/main.js index 413f8d88..9f5ac1e5 100644 --- a/src/main.js +++ b/src/main.js @@ -36,7 +36,6 @@ app.directive('has', hasPermission) // 注册指令 app.directive('click-outside', clickOutside) app.directive('ele-click-outside', ClickOutside) app.directive('cancel', cancelWithChange) - app.config.globalProperties.$_ = _ app.mixin(commonMixin) diff --git a/src/permission.js b/src/permission.js index a7ec9aa9..663b379a 100644 --- a/src/permission.js +++ b/src/permission.js @@ -1,7 +1,8 @@ import router from './router' import store from './store' import { ElMessage } from 'element-plus' -import { getConfigJson, getPermission, loadIso36112 } from '@/utils/api' +import { getConfigJson, getPermission } from '@/utils/api' +import { loadGeoData } from '@/utils/tools' import axios from 'axios' import { storageKey } from '@/utils/constants' import { loadI18n } from '@/i18n' @@ -11,9 +12,7 @@ const permissionWhiteList = [...loginWhiteList] // 权限白名单 router.beforeEach(async (to, from, next) => { // 加载iso-3166-2资源 - if (!sessionStorage.getItem(storageKey.iso36112)) { - loadIso36112() - } + loadGeoData() // 加载baseUrl if (!axios.defaults.baseURL) { const config = await getConfigJson() diff --git a/src/utils/api.js b/src/utils/api.js index 7af38a53..5fc4feb9 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -5,7 +5,7 @@ */ import { get, post } from '@/utils/http' import { sortByOrderNum } from '@/permission' -import {storageKey} from "@/utils/constants"; +import { storageKey, iso36112 } from '@/utils/constants' export const api = { // 系统相关 @@ -79,8 +79,12 @@ export async function getI18n () { return await request } -export function loadIso36112 () { - get(`${process.env.BASE_URL}geojson/data/countriesWithCapital.json`).then(response => { - sessionStorage.setItem(storageKey.iso36112, JSON.stringify(response)) +/* 获得原始的3611-2 json字符串数据 */ +export async function getIso36112JsonData (suffix) { + const request = new Promise(resolve => { + get(`${process.env.BASE_URL}geojson/${suffix}`).then(response => { + resolve(JSON.stringify(response)) + }) }) + return await request } diff --git a/src/utils/constants.js b/src/utils/constants.js index 4100f0bc..7dd27eb5 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -1,7 +1,8 @@ export const defaultPageSize = 20 export const storageKey = { - iso36112: 'cn-iso3611-2', + iso36112Capital: 'cn-iso3611-2-capital', + iso36112WorldLow: 'cn-iso3611-2-world-low', i18n: 'cn-i18n', language: 'cn-language', timezone: 'cn-timezone', @@ -17,6 +18,11 @@ export const storageKey = { unsavedChange: 'cn-unsaved-change' } +export const iso36112 = { + [storageKey.iso36112Capital]: 'data/countriesWithCapital.json', + [storageKey.iso36112WorldLow]: 'worldChinaLow.json' +} + // 统一定义跳转来源 export const fromRoute = { trafficSummary: 'trafficSummary', diff --git a/src/utils/tools.js b/src/utils/tools.js index 78c8d9c2..9af4af9e 100644 --- a/src/utils/tools.js +++ b/src/utils/tools.js @@ -1,8 +1,8 @@ import { ElMessageBox } from 'element-plus' import i18n from '@/i18n' import _ from 'lodash' -import { storageKey } from '@/utils/constants' -import { getCache, setCache } from '@/utils/cache' +import { storageKey, iso36112 } from '@/utils/constants' +import { getIso36112JsonData } from '@/utils/api' export const tableSort = { // 是否需要排序 @@ -399,7 +399,46 @@ export function humpToLine (name) { return name.replace(/([A-Z])/g, '_$1').toLowerCase() } -export function getCapitalGeo (countryId) { - !getCache('capitalGeo') && setCache('capitalGeo', JSON.parse(sessionStorage.getItem(storageKey.iso36112))) - return getCache('capitalGeo')[countryId] +// 加载geo数据 +export function loadGeoData (key) { + const keys = [] + if (key) { + keys.push(key) + } else { + keys.push(storageKey.iso36112Capital) + keys.push(storageKey.iso36112WorldLow) + } + keys.forEach(async k => { + if (!localStorage.getItem(k)) { + const data = await getIso36112JsonData(iso36112[k]) + if (data) { + localStorage.setItem(k, JSON.stringify(data)) + } + } + }) + return localStorage.getItem(key) +} + +/* 返回geodata对象 */ +export function getGeoData (key) { + const data = localStorage.getItem(key) + if (data) { + return JSONParse(data) + } else { + return JSONParse(loadGeoData(key)) + } +} + +export function getCapitalGeo (countryId) { + const data = getGeoData(storageKey.iso36112Capital) + return data[countryId] +} + +export function JSONParse (data) { + const firstParse = JSON.parse(data) + if (typeof firstParse === 'string') { + return JSON.parse(firstParse) + } else { + return firstParse + } } diff --git a/src/views/charts/Chart.vue b/src/views/charts/Chart.vue index 35fe29da..34484b32 100644 --- a/src/views/charts/Chart.vue +++ b/src/views/charts/Chart.vue @@ -5,12 +5,21 @@ class="cn-chart cn-chart__title" :style="computePosition">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}} - + v-model="activeTab" + @tab-click="changeTab" + :style="computePosition" + > + + + + { - const data = response.data.result - data.forEach(r => { - const serverCountryCapital = r.serverId && getCapitalGeo(r.serverId) - const clientCountryCapital = r.clientId && getCapitalGeo(r.clientId) - serverCountryCapital && (r.serverLongitude = serverCountryCapital.capitalLongitude) - serverCountryCapital && (r.serverLatitude = serverCountryCapital.capitalLatitude) - clientCountryCapital && (r.clientLongitude = clientCountryCapital.capitalLongitude) - clientCountryCapital && (r.clientLatitude = clientCountryCapital.capitalLatitude) - }) - if (this.isMapLine) { - const lineSeries = this.myChart.series.push(new am4Maps.MapLineSeries()) - const gradient = new am4Core.LinearGradient() - gradient.stops.push({ color: am4Core.color() }) - gradient.stops.push({ color: am4Core.color() }) - gradient.stops.push({ color: am4Core.color() }) + if (response.code === 200) { + const data = response.data.result + data.forEach(r => { + const serverCountryCapital = r.serverId && getCapitalGeo(r.serverId) + const clientCountryCapital = r.clientId && getCapitalGeo(r.clientId) + serverCountryCapital && (r.serverLongitude = serverCountryCapital.capitalLongitude) + serverCountryCapital && (r.serverLatitude = serverCountryCapital.capitalLatitude) + clientCountryCapital && (r.clientLongitude = clientCountryCapital.capitalLongitude) + clientCountryCapital && (r.clientLatitude = clientCountryCapital.capitalLatitude) + }) + if (this.isMapLine) { + const lineSeries = this.myChart.series.push(new am4Maps.MapLineSeries()) + const gradient = new am4Core.LinearGradient() + gradient.stops.push({ color: am4Core.color() }) + gradient.stops.push({ color: am4Core.color() }) + gradient.stops.push({ color: am4Core.color() }) - const lineTemplate = lineSeries.mapLines.template - lineTemplate.stroke = am4Core.color('#A258EC') - lineTemplate.line.nonScalingStroke = true - lineTemplate.line.strokeDasharray = '4 3' - lineTemplate.nonScalingStroke = true - lineTemplate.arrow.nonScaling = true - lineTemplate.arrow.width = 4 - lineTemplate.arrow.height = 6 - lineSeries.data = [ - { - multiGeoLine: data.map(d => { - return [ - { - latitude: parseFloat(d.serverLatitude), - longitude: parseFloat(d.serverLongitude) - }, - { - latitude: parseFloat(d.clientLatitude), - longitude: parseFloat(d.clientLongitude) - } - ] + const lineTemplate = lineSeries.mapLines.template + lineTemplate.stroke = am4Core.color('#A258EC') + lineTemplate.line.nonScalingStroke = true + lineTemplate.line.strokeDasharray = '4 3' + lineTemplate.nonScalingStroke = true + lineTemplate.arrow.nonScaling = true + lineTemplate.arrow.width = 4 + lineTemplate.arrow.height = 6 + lineSeries.data = [ + { + multiGeoLine: data.map(d => { + return [ + { + latitude: parseFloat(d.serverLatitude), + longitude: parseFloat(d.serverLongitude) + }, + { + latitude: parseFloat(d.clientLatitude), + longitude: parseFloat(d.clientLongitude) + } + ] + }) + } + ] + + const imageSeries = this.myChart.series.push(new am4Maps.MapImageSeries()) + imageSeries.dataFields.value = 'sessions' + const imageSeriesTemplate = imageSeries.mapImages.template + const circle = imageSeriesTemplate.createChild(am4Core.Circle) + + circle.fillOpacity = 0.7 + circle.nonScaling = true + circle.tooltipText = '{title}' + const radiusHeat = imageSeries.heatRules.push({ + target: circle, + property: 'radius', + min: 8, + max: 30 + }) + const colorHeat = imageSeries.heatRules.push({ + target: circle, + property: 'fill', + min: am4Core.color('#D2A8FF'), + max: am4Core.color('#A258EC') + }) + imageSeriesTemplate.propertyFields.latitude = 'latitude' + imageSeriesTemplate.propertyFields.longitude = 'longitude' + + const pointData = [] + data.forEach(d => { + pointData.push({ + ...d, + latitude: parseFloat(d.serverLatitude), + longitude: parseFloat(d.serverLongitude), + title: this.getTitle(d) + }) + pointData.push({ + ...d, + latitude: parseFloat(d.clientLatitude), + longitude: parseFloat(d.clientLongitude), + title: this.getTitle(d) }) - } - ] - - const imageSeries = this.myChart.series.push(new am4Maps.MapImageSeries()) - imageSeries.dataFields.value = 'sessions' - const imageSeriesTemplate = imageSeries.mapImages.template - const circle = imageSeriesTemplate.createChild(am4Core.Circle) - - circle.fillOpacity = 0.7 - circle.nonScaling = true - circle.tooltipText = '{title}' - const radiusHeat = imageSeries.heatRules.push({ - target: circle, - property: 'radius', - min: 8, - max: 30 - }) - const colorHeat = imageSeries.heatRules.push({ - target: circle, - property: 'fill', - min: am4Core.color('#D2A8FF'), - max: am4Core.color('#A258EC') - }) - imageSeriesTemplate.propertyFields.latitude = 'latitude' - imageSeriesTemplate.propertyFields.longitude = 'longitude' - - const pointData = [] - data.forEach(d => { - pointData.push({ - ...d, - latitude: parseFloat(d.serverLatitude), - longitude: parseFloat(d.serverLongitude), - title: this.getTitle(d) }) - pointData.push({ - ...d, - latitude: parseFloat(d.clientLatitude), - longitude: parseFloat(d.clientLongitude), - title: this.getTitle(d) - }) - }) - imageSeries.data = pointData + imageSeries.data = pointData + } } }) } @@ -269,42 +279,51 @@ export default { this.myChart = echarts.init(dom) if (chartParams) { if (this.isEchartsWithTable) { - const queryParams = { startTime: 1593070343, endTime: this.endTime, limit: 10 } // 统计数据的查询参数 - const tableQueryParams = { startTime: 1593070343, endTime: this.endTime, limit: 10 } // 统计数据的查询参数 + const queryParams = { startTime: parseInt(this.startTime / 1000), endTime: parseInt(this.endTime / 1000), limit: 10 } // 统计数据的查询参数 + const tableQueryParams = { startTime: parseInt(this.startTime / 1000), endTime: this.endTime, limit: 10 } // 统计数据的查询参数 get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => { - const data = response.data.result - this.chartOption.legend.formatter = (name) => { // 根据图表宽 显示legend的字数 - let str = name - const length = Math.floor(dom.offsetWidth / 75) - if (name.length > length) { - str = name.substring(0, length - 3) + '...' + if (response.code === 200) { + const data = response.data.result + this.chartOption.legend.formatter = (name) => { // 根据图表宽 显示legend的字数 + let str = name + const length = Math.floor(dom.offsetWidth / 75) + if (name.length > length) { + str = name.substring(0, length - 3) + '...' + } + return str } - return str - } - this.chartOption.series[0].data = data.map(d => { - return { - data: d, - name: d[chartParams.nameColumn], - value: parseInt(d[chartParams.valueColumn]) + this.chartOption.series[0].data = data.map(d => { + return { + data: d, + name: d[chartParams.nameColumn], + value: parseInt(d[chartParams.valueColumn]) + } + }) + if (this.chartOption.series[0].data && this.chartOption.series[0].data.length > 10) { // pieWithTable 图例超过10个改为滚动显示 + this.chartOption.legend.type = 'scroll' } - }) - if (this.chartOption.series[0].data && this.chartOption.series[0].data.length > 10) { // pieWithTable 图例超过10个改为滚动显示 - this.chartOption.legend.type = 'scroll' + this.myChart.setOption(this.chartOption) + this.$nextTick(() => { + this.myChart.resize() + }) + tableQueryParams[chartParams.nameColumn] = data[0][chartParams.nameColumn] + get(replaceUrlPlaceholder(chartParams.urlTable, tableQueryParams)).then(response2 => { + if (response2.code === 200) { + this.pieTableData = response2.data.result + } + }) } - this.myChart.setOption(this.chartOption) - tableQueryParams[chartParams.nameColumn] = data[0][chartParams.nameColumn] - get(replaceUrlPlaceholder(chartParams.urlTable, tableQueryParams)).then(response2 => { - this.pieTableData = response2.data.result - }) }) this.myChart.on('click', function (echartParams) { get(replaceUrlPlaceholder(chartParams.urlTable, tableQueryParams)).then(response => { - this.pieTableData = response.data.result + if (response.code === 200) { + this.pieTableData = response.data.result + } }) }) } else { - const queryParams = { startTime: 1593070343, endTime: this.endTime } + const queryParams = { startTime: parseInt(this.startTime / 1000), endTime: parseInt(this.endTime / 1000) } get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => { if (response.code === 200) { const seriesTemplate = this.chartOption.series[0] @@ -316,13 +335,15 @@ export default { } }) } - this.myChart.setOption(this.chartOption) + this.$nextTick(() => { + this.myChart.resize() + }) }) } } } else if (this.isTable) { if (chartParams) { - const queryParams = { startTime: 1593070343, endTime: this.endTime, limit: 100, order: 'sessions' } + const queryParams = { startTime: parseInt(this.startTime / 1000), endTime: parseInt(this.endTime / 1000), limit: 100, order: 'sessions' } get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => { if (response.code === 200) { const tableColumns = new Set() @@ -340,13 +361,17 @@ export default { } } else if (this.isSingleValue) { if (chartParams) { - const queryParams = { startTime: 1593070343, endTime: this.endTime } + const queryParams = { startTime: parseInt(this.startTime / 1000), endTime: parseInt(this.endTime / 1000) } get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => { if (response.code === 200) { this.singleValue = response.data.result } }) } + } else if (this.isTabs) { + if (!this.$_.isEmpty(this.chartInfo.children)) { + this.activeTab = `${this.chartInfo.children[0].id}` + } } }, getTitle (data) { @@ -355,6 +380,9 @@ export default { Sessions: ${data.sessions} Bytes: ${data.bytes}` }, + changeTab (tab) { + this.activeTab = tab.paneName + }, initMap (id) { const chart = am4Core.create(id, am4Maps.MapChart) chart.geodata = am4GeoDataWorldLow @@ -412,6 +440,16 @@ export default { } -