2025-06-30 09:38:03 +08:00

630 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import jnpf from '@/utils/jnpf'
import {
geojson,
getAtlas
} from '@/api/portal/portal.js'
import {
getDataInterfaceRes
} from '@/api/common'
let timer;
export default {
data() {
return {
show: false,
mapData: [],
mapList: [],
mapType: '',
markPoints: [],
pointLoading: false,
opts: {
update: true,
fontSize: 10,
padding: [40, 15, 30, 15],
extra: {
map: {
mercator: true,
},
},
},
regionStep: [],
stepMapList: [],
option: {}
}
},
created() {
this.init()
if (this.config.dataType === 'dynamic') {
uni.$off('proRefresh')
uni.$on('proRefresh', () => {
this.handelChart()
})
}
},
methods: {
init() {
if (timer) clearInterval(timer)
this.handelChart()
if (!this.config.allRefresh.autoRefresh && this.config?.refresh?.autoRefresh) {
timer = setInterval(this.handelChart, this.config.refresh.autoRefreshTime * 60000)
}
},
async handelChart() {
this.customEchartsKey()
if (this.config.dataType === 'dynamic') {
if (!this.config.propsApi) return
const query = {
paramList: this.config.templateJson
};
getDataInterfaceRes(this.config.propsApi, query).then(res => {
this.config.option.defaultValue = this.handleMappingConfig(res.data, this.config)
this.handleAttrs()
})
} else {
this.handleAttrs()
}
},
// 自定义echarts属性取app的
customEchartsKey() {
this.config.option = {
...this.config.option,
...this.config.appOption
}
if (['customEcharts'].includes(this.config.jnpfKey)) {
this.config.dataType = this.config.appDataType
this.config.propsApi = this.config.appPropsApi
this.config.propsName = this.config.appPropsName
this.config.templateJson = this.config.appTemplateJson
this.config.refresh = this.config.appRefresh ? this.config.appRefresh : {}
}
this.config.option.urlAddress = this.config.option.appUrlAddress
},
// 处理字段映射
handleMappingConfig(resultData, config) {
let defaultValue = undefined
if (['rankList', 'barChart', 'pieChart', 'tableList'].includes(this.config.jnpfKey)) {
defaultValue = this.config.option.defaultValue || []
}
let mappingConfig = config.mappingConfig
let result = []
resultData.forEach(data => {
let temp = {}
for (let item of mappingConfig) {
if (item.field === '系列') {
temp['type'] = item.value ? data[item.value] : data['type']
}
if (item.field === '维度') {
temp['name'] = item.value ? data[item.value] : data['name']
}
if (item.field === '数值') {
temp['value'] = item.value ? data[item.value] : data['value']
}
if (item.field === '最大值') {
temp['max'] = item.value ? data[item.value] : data['max']
}
if (item.field === '时间戳') {
temp['timestamp'] = item.value ? data[item.value] : data['timestamp']
}
if (item.field === '经度') {
temp['long'] = item.value ? data[item.value] : data['long']
}
if (item.field === '纬度') {
temp['lat'] = item.value ? data[item.value] : data['lat']
}
}
result.push(temp)
})
return result
},
handleParam(e) {
let jnpfKey = this.config.jnpfKey
if (jnpfKey == 'customEcharts') return
let item = {}
let choose = e.opts.series && e.opts.series[0]
let chooseIndex = e.currentIndex['index']
let name = ''
let type = ''
let value = ''
if (jnpfKey === "barChart") {
name = e.opts.categories[chooseIndex]
value = choose.data[chooseIndex]
type = choose.name
}
if (jnpfKey === "pieChart") {
name = choose.name
value = choose.data
let series = this.option.chartData.series && this.option.chartData.series[0]
type = series.name
}
if (jnpfKey === "lineChart") {
name = e.opts.categories[chooseIndex]
value = choose.data[chooseIndex]
type = choose.name
}
if (jnpfKey === "radarChart") {
chooseIndex = e.currentIndex
name = e.opts.categories[chooseIndex]
value = choose.data[chooseIndex]
type = choose.name
}
if (jnpfKey === "mapChart" && choose.properties.childrenNum == 0) {
name = choose.properties.name
item.long = choose.properties.center[0]
item.lat = choose.properties.center[1]
}
if (chooseIndex < 0 && jnpfKey !== "mapChart") return
if (jnpfKey === "mapChart" && choose.properties.childrenNum != 0) return
item.name = name
item.value = value
item.type = type
item.urlAddress = this.config.option.urlAddress
this.jnpf.solveAddressParam(item, this.config)
this.jnpf.jumpLink(item.urlAddress)
},
handleAttrs() {
let chartTitle = {
titleText: this.config.option.titleText, //主标题
titleTextStyleColor: this.config.option.titleTextStyleColor, //主标题字体颜色
titleTextStyleFontSize: this.config.option.titleTextStyleFontSize * 2 + 'rpx', //主标题字体大小[12-25px]
titleTextStyleFontWeight: this.config.option.titleTextStyleFontWeight ? 700 : 0, //主标题是否加粗
titleLeft: this.config.option.titleLeft === 'left' ? 'flex-start' : this.config.option
.titleLeft === 'right' ? 'flex-end' : 'center', //主子标题位置[left,center,right]
titleBgColor: this.config.option.titleBgColor, //主子标题背景色[rgba(),#303133]
// 图表副标题设置
titleSubtext: this.config.option.titleSubtext, //子标题
titleSubtextStyleColor: this.config.option.titleSubtextStyleColor, //子标题字体颜色
titleSubtextStyleFontSize: this.config.option.titleSubtextStyleFontSize * 2 +
'rpx', //子标题字体大小[12-25px]
titleSubtextStyleFontWeight: this.config.option.titleSubtextStyleFontWeight ? 700 : 0, //子标题是否加粗
}
if (['customEcharts'].includes(this.config.jnpfKey)) {
this.$nextTick(() => {
this.show = true
this.key = +new Date()
})
return
}
let defVal = this.config.option.defaultValue
let chartVal = JSON.parse(JSON.stringify(defVal)) || []
let typeArr = Array.from(new Set(chartVal.map((item) => item.type)))
let axisData = Array.from(new Set(chartVal.map((item) => item.name)))
let seriesData = []
let colorList = [];
let chartsType = "";
let Ymin = []; //y轴最小
let Ymax = []; //y轴最大
let type = "";
let yAxis = {};
let xAxis = {};
if (!['mapChart', 'customEcharts'].includes(this.config.jnpfKey)) {
typeArr.forEach((title, index) => {
const type = this.getType(title, this.config)
let obj = {
name: title,
type: type
}
if (this.config.option.seriesLabelShow) {
obj.textSize = (this.config.option.seriesLabelFontSize / 2) < 12 ? 12 : this.config
.option
.seriesLabelFontSize / 2
obj.textColor = this.config.option.seriesLabelColor
}
let chartArr = chartVal.filter((item) => title === item.type)
if (this.config.jnpfKey === 'barChart' || this.config.jnpfKey === 'lineChart' || this
.config
.jnpfKey ==
'radarChart') {
obj['data'] = chartArr.map((item) => item.value)
} else {
obj['data'] = chartArr.map((item) => {
return {
value: item.value,
name: item.name,
}
})
if (this.config.option.showZero) obj['data'] = obj['data'].filter((item) => item
.value != 0)
this.key = +new Date
}
Ymin.push(Math.min(...obj['data']))
Ymax.push(Math.max(...obj['data']))
seriesData.push(obj);
})
xAxis = {
disabled: !this.config.option.xAxisShow, //不绘制X轴
axisLine: this.config.option.xAxisShow, //绘制坐标轴轴线
axisLineColor: !this.config.option.xAxisShow ? '#fff' : this.config.option
.xAxisAxisLineLineStyleColor, //坐标轴轴线颜色,默认#CCCCCC
title: "", //X轴标题
titleFontSize: this.config.option.xAxisNameTextStyleFontSize, //标题字体大小
titleFontColor: this.config.option.xAxisNameTextStyleColor, //X轴名称字体颜色
titleOffsetY: -20, //标题纵向偏移距离,负数为向上偏移,正数向下偏移
titleOffsetX: -300, //标题横向偏移距离,负数为向左偏移,正数向右偏移
fontSize: (this.config.option.xAxisAxisLabelTextStyleFontSize / 2) < 14 ? 14 : this.config
.option
.xAxisAxisLabelTextStyleFontSize / 2, //X轴标签字体大小
fontColor: this.config.option.xAxisAxisLabelTextStyleColor, //X轴标签字体颜色
rotateAngle: this.config.option.xAxisAxisLabelRotate, ////X轴标签角度
rotateLabel: this.config.option.xAxisAxisLabelRotate > 0 ? true : false, //【旋转】数据点(刻度点)文字
gridColor: this.config.option.xAxisSplitLineLineStyleColor, //纵向网格颜色,默认#CCCCCC
splitNumber: 4, //X轴网格数量纵向网格数量(竖着的)
disableGrid: !this.config.option.xAxisShow ? !this.config.option.xAxisShow : !this.config
.option.xAxisSplitLineShow, //不绘制纵向网格(即默认绘制网格)
scrollShow: axisData.length > 5 ? true : false, //是否显示滚动条,配合拖拽滚动使用(即仅在启用 enableScroll 时有效
scrollAlign: "left", //滚动条初始位置
scrollColor: "#A6A6A6", //滚动条颜色,默认#A6A6A6
scrollBackgroundColor: "#EFEBEF", //滚动条底部背景颜色,默认#EFEBEF
itemCount: axisData.length > 5 ? 6 : 5, //单屏数据密度即图表可视区域内显示的X轴数据点数量仅在启用enableScroll时有效
};
yAxis = {
data: [{
position: "left",
title: "",
min: this.config.option.styleType == 6 ? -jnpf.toRound(Math.abs(Math.min(...
Ymin)
.toString().length > 2 ? Math.min(...Ymin) - 200 : Math
.min(
...Ymin) - 50)) : 0,
max: jnpf.toRound(Math.max(...Ymax)),
axisLine: true, //坐标轴轴线是否显示(数据还能显示)
axisLineColor: this.config.option
.yAxisAxisLineLineStyleColor, //坐标轴轴线颜色,默认#CCCCCC
disabled: !this.config.option.yAxisShow, //不绘制Y轴刻度和轴线都不绘制
fontColor: this.config.option
.yAxisAxisLabelTextStyleColor, //数据点(刻度点)字体颜色,默认#666666
fontSize: (this.config.option.yAxisAxisLabelTextStyleFontSize / 2) < 12 ? 12 : this
.config
.option.yAxisAxisLabelTextStyleFontSize / 2, //数据点(刻度点)字体颜色,默认#666666
}],
padding: 10, //多个Y轴间的间距
gridSet: "number", //横向向网格数量类型设置,可选值,'auto','array'
disableGrid: !this.config.option.yAxisSplitLineShow, //不绘制横向向网格(即默认绘制网格)
splitNumber: 4, //【指定数量】的横向向网格数量此数量与Y轴数据点是否为小数有关如果指定了max请指定为能被max-min整除的数值
gridType: "solid", //横向向网格线型
gridColor: this.config.option.yAxisSplitLineLineStyleColor, //横向网格颜色,默认#CCCCCC
};
this.config.option.colorList.forEach((o, i) => {
if (o.color1) colorList.push(o.color1)
})
}
let opts = {
color: colorList, //主题颜色16进制颜色格式Array格式
padding: [15, 15, 0, 15], //画布填充边距[上,右,下,左]Array格式
enableScroll: axisData.length > 5 ? true :
false, //开启图表可拖拽滚动开启后ontouch需要赋值为trueX轴配置里需要配置itemCount单屏幕数据点数量
dataLabel: this.config.option.seriesLabelShow, //是否显示图表区域内数据点上方的数据文案
legend: {
fontColor: this.config.jnpfKey === 'pieChart' ? "#666666" : "",
show: this.config.option.legendShow, //是否显示图例标识
position: 'top',
float: 'right',
fontSize: (this.config.option.legendTextStyleFontSize / 2) < 14 ? 14 : this.config.option
.legendTextStyleFontSize / 2
},
extra: {
tooltip: {
showBox: this.config.option.tooltipShow, //提示语显示
fontSize: (this.config.option.tooltipTextStyleFontSize / 2) < 14 ? 14 : this.config
.option
.tooltipTextStyleFontSize / 2, //提示语字体大小
fontColor: this.config.option.tooltipTextStyleColor, //提示语字体颜色
bgColor: this.config.option.tooltipBgColor || '#000000' //提示窗口的背景颜色
},
mix: {
column: {}
},
column: {
type: 'group',
width: this.config.option.seriesBarWidth, //柱体宽度
activeBgColor: "#000000",
activeBgOpacity: 0.08,
linearType: "none",
barBorderRadius: [this.config.option.seriesItemStyleBarBorderRadius, this.config.option
.seriesItemStyleBarBorderRadius, this.config.option
.seriesItemStyleBarBorderRadius,
this.config.option.seriesItemStyleBarBorderRadius
], //自定义4个圆角半径[左上,右上,右下,左下]
seriesGap: 5, //多series每个柱子之间的间距
customColor: colorList, //扩展渐变色
barBorderCircle: false
}
}
}
if (this.config.jnpfKey === 'barChart') {
if (this.config.option.styleType == 5 || this.config.option.styleType == 1 ||
this.config.option.styleType == 4 || this.config.option.styleType == 6) {
type = 'group'
if (this.config.option.styleType == 6) {
opts.extra.column.barBorderRadius = []
// opts.extra.column.width = 50
// yAxis.splitNumber = (yAxis.data[0].max / 500) + (Math.abs(yAxis.data[0].min) / 500)
}
} else {
type = 'stack'
opts.extra.mix.column = {
width: this.config.option.seriesBarWidth, //柱体宽度
barBorderCircle: false, //启用分组柱状图半圆边框
barBorderRadius: [this.config.option.seriesItemStyleBarBorderRadius, this.config.option
.seriesItemStyleBarBorderRadius, this.config.option
.seriesItemStyleBarBorderRadius,
this.config.option.seriesItemStyleBarBorderRadius
], //自定义4个圆角半径[左上,右上,右下,左下]
}
}
chartsType = this.config.option.styleType == 7 ? 'mix' : 'column'
opts.xAxis = {
...xAxis
}
opts.yAxis = {
...yAxis
}
opts.extra.column.type = type
} else if (this.config.jnpfKey === "pieChart") {
chartsType = 'pie'
opts.fontColor = this.config.option.seriesLabelColor
opts.fontSize = (this.config.option.seriesLabelFontSize / 2) < 14 ? 14 : this.config.option
.seriesLabelFontSize / 2
let pieChartObj = {
borderColor: "#FFFFFF",
borderWidth: 3,
activeOpacity: 0.5,
offsetAngle: -90,
labelWidth: 15,
border: false,
}
let pieChartObj2 = {
offsetX: 0,
offsetY: 0,
name: "",
}
if (this.config.option.styleType == 1) {
if (this.config.option.roseType) chartsType = 'rose'
opts.extra[chartsType] = {
...pieChartObj
}
} else {
chartsType = 'ring';
opts.title = {
fontSize: 15,
color: "#666666",
...pieChartObj2
};
opts.subtitle = {
fontSize: 25,
color: "#7cb5ec",
...pieChartObj2
}
opts.extra[chartsType] = {
ringWidth: 60,
activeRadius: 10,
...pieChartObj
}
}
} else if (this.config.jnpfKey === "lineChart") {
chartsType = this.config.option.areaStyle ? 'area' : 'line'
type = this.config.option.styleType == 2 ? 'curve' : this.config.option.styleType == 3 ? 'step' :
'straight'
let lineChartObj = {
type: type,
width: this.config.option.seriesLineStyleWidth
}
opts.extra[chartsType] = {
...lineChartObj
}
opts.xAxis = {
...xAxis
}
opts.yAxis = {
...yAxis
}
} else if (this.config.jnpfKey === "radarChart") {
chartsType = "radar";
type = this.config.option.styleType == 1 ? chartsType : 'circle'
opts.fontSize = (this.config.option.radarAxisNameFontSize / 2) < 14 ? 14 : this.config.option
.radarAxisNameFontSize / 2
opts.fontColor = this.config.option.seriesLabelColor
opts.extra[chartsType] = {
gridType: type,
gridColor: "#CCCCCC",
gridCount: 5,
opacity: 0.2,
max: 200,
labelShow: true,
border: true,
max: 100,
labelColor: this.config.option.radarAxisNameColor
}
} else {
this.getAtlas()
chartsType = "map";
this.config.option.markPoints = []
opts = {
update: true,
fontSize: this.config.option.geoLabelFontSize,
padding: [15, 15, 30, 15],
dataLabel: this.config.option.geoLabelShow,
fontColor: this.config.option.geoLabelColor,
extra: {
tooltip: {
showBox: this.config.option.tooltipShow,
fontColor: this.config.option.tooltipTextStyleColor || '#000',
//fontSize: this.config.option.tooltipTextStyleFontSize,
bgColor: this.config.option.tooltipBgColor || '#fff',
bgOpacity: 1
},
map: {
mercator: false,
border: true,
borderWidth: this.config.option.geoBorderWidth / 2,
borderColor: this.config.option.geoBorderColor,
activeBorderColor: "#F04864",
activeFillColor: "#FACC14",
activeFillOpacity: 1,
active: true,
activeTextColor: "#FFFFFF",
fillOpacity: 1
},
},
}
this.config.option.defaultValue.forEach(o => {
this.config.option.markPoints.push({
latitude: o.lat,
longitude: o.long,
name: o.name,
value: o.value
})
})
}
let chartData = {
categories: axisData,
series: seriesData,
opts,
type: chartsType
}
if (chartsType != 'map') {
if (chartTitle.titleText && chartTitle.titleSubtext) {
chartData.opts.legend.padding = 50
} else if (chartTitle.titleText && !chartTitle.titleSubtext) {
chartData.opts.legend.padding = 30
} else {
chartData.opts.legend.padding = 5
}
}
this.config.option.chartData = chartData
this.config.option.chartTitle = chartTitle
this.option = this.config.option
this.$nextTick(() => {
this.show = true
this.key = +new Date()
})
},
/* 获取地图树 */
getAtlas() {
getAtlas().then(res => {
this.mapList = jnpf.treeToArray(res.data);
this.regionStep = []
this.drawChina()
})
},
/* 获取地图数据 */
drawChina(type) {
if (Array.isArray(this.option.mapType) && !type) this.mapType = this.option.mapType[this.option.mapType
.length - 1]
geojson(this.mapType).then(res => {
let series = JSON.parse(JSON.stringify(res.data.features)) || [];
for (var i = 0; i < series.length; i++) {
if (series[i].geometry.type === 'Polygon' && !type) {
if (this.mapType == 150000) {
series[i].geometry.coordinates = series[i].geometry.coordinates
} else {
series[i].geometry.coordinates = [series[i].geometry.coordinates]
}
}
series[i].value = Math.floor(Math.random() * 1000)
series[i].color = this.option.geoAreaColor
}
this.mapData = series
this.stepMapList.push(this.mapData)
this.option.chartData.series = series
this.key = +new Date()
})
},
/* 地图点击路径 */
regionStepClick(e, index) {
if (index == 0) {
this.stepMapList = []
this.regionStep = []
for (let i = 0; i < this.mapList.length; i++) {
if (this.mapList[i].enCode == this.option.mapType[this.option.mapType.length - 1]) {
this.regionStep.push({
name: this.mapList[i].fullName,
adcode: this.mapList[i].enCode
})
}
}
this.drawChina()
return
}
this.mapType = e.adcode
this.stepMapList.splice(index + 1, this.regionStep.length - index - 1)
this.regionStep.splice(index + 1, this.regionStep.length - index - 1)
this.drawChina(1)
},
/* 地图点击 */
getIndex(e) {
// 处理跳转参数
this.handleParam(e)
let current = this.mapList.filter(o => o.enCode == this.option.mapType[this.option.mapType.length - 1])
if (!this.config.option.drillDown) return
let mapData = this.mapData[e.currentIndex] ? this.mapData[e.currentIndex] : []
this.mapType = mapData.properties.adcode
let acroutes = mapData.properties.acroutes
let selectName = mapData.properties.name
let adcode = mapData.properties.adcode
this.regionStep.unshift({
name: current[0].fullName,
adcode: current[0].enCode
})
this.regionStep.push({
name: selectName,
adcode: adcode
})
//对象数组去重
this.regionStep = this.regionStep.filter((a, b, c) => {
return c.findIndex(x => x.name === a.name) === b
})
this.pointLoading = false
this.drawChina(1)
},
getType(title) {
if (this.config.jnpfKey == 'barChart') {
if (this.config.option.styleType == 7) {
const arr = this.config.option.barType.find(ele => title == ele.title)
if (arr && arr.type) {
if (arr.type == 'bar') return 'column'
return arr.type
}
}
return 'column'
} else if (this.config.jnpfKey == 'lineChart') {
return 'line'
} else if (this.config.jnpfKey == 'pieChart') {
return 'pie'
} else {
return 'radar'
}
},
/* 设置地图点 */
setPoints(e) {
const mapData = e.opts.chartData.mapData;
this.option.markPoints = this.option.markPoints.slice(0, this.option.appShowNumber)
for (var i = 0; i < this.option.markPoints.length; i++) {
const points = this.coordinateToPoint(this.option.markPoints[i].longitude, this.option.markPoints[i]
.latitude, mapData
.bounds,
mapData.scale, mapData.xoffset, mapData.yoffset, mapData.mercator)
this.option.markPoints[i].x = points.x;
this.option.markPoints[i].y = points.y;
}
this.pointLoading = true
this.$refs.charts.key = +new Date();
},
/* 经纬度转画布坐标 */
coordinateToPoint(longitude, latitude, bounds, scale, xoffset, yoffset, mercator) {
var x = longitude;
var y = latitude;
if (mercator == true) {
x = longitude * 20037508.34 / 130;
y = Math.log(Math.tan((90 + latitude) * Math.PI / 360)) / (Math.PI / 180);
y = y * 20037508.34 / 180;
}
return {
x: (x - bounds.xMin) * scale + xoffset,
y: (bounds.yMax - y) * scale + yoffset
};
}
}
}