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 {
}
-