修改首页地图

This commit is contained in:
2026-02-08 21:51:10 +08:00
parent fb33e5a1f6
commit 13ee9e66cc
7 changed files with 292 additions and 126 deletions

1
data/ems/china.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"name" : "上动EMS", "name" : "上动EMS",
"appid" : "__UNI__B330617", "appid" : "__UNI__5FBB073",
"description" : "", "description" : "",
"versionName" : "1.2.0", "versionName" : "1.2.0",
"versionCode" : "100", "versionCode" : "100",

View File

@ -127,16 +127,16 @@
"iconPath": "static/images/tabbar/home.png", "iconPath": "static/images/tabbar/home.png",
"selectedIconPath": "static/images/tabbar/home_.png", "selectedIconPath": "static/images/tabbar/home_.png",
"text": "首页" "text": "首页"
}, {
"pagePath": "pages/ticket/list",
"iconPath": "static/images/tabbar/ticket.png",
"selectedIconPath": "static/images/tabbar/ticket_.png",
"text": "工单"
}, { }, {
"pagePath": "pages/work/index", "pagePath": "pages/work/index",
"iconPath": "static/images/tabbar/work.png", "iconPath": "static/images/tabbar/work.png",
"selectedIconPath": "static/images/tabbar/work_.png", "selectedIconPath": "static/images/tabbar/work_.png",
"text": "工作台" "text": "工作台"
}, {
"pagePath": "pages/ticket/list",
"iconPath": "static/images/tabbar/ticket.png",
"selectedIconPath": "static/images/tabbar/ticket_.png",
"text": "工单"
}, { }, {
"pagePath": "pages/mine/index", "pagePath": "pages/mine/index",
"iconPath": "static/images/tabbar/mine.png", "iconPath": "static/images/tabbar/mine.png",

View File

@ -2,17 +2,21 @@
<view class="home-container"> <view class="home-container">
<view class="site-sections-list"> <view class="site-sections-list">
<view class="site-title">站点选择</view> <view class="site-title">站点选择</view>
<checkbox-group class="site-radio" @change="onSiteChange"> <radio-group class="site-radio" @change="onSiteChange">
<label v-for="item in siteOptions" :key="item.value" class="radio-item" <label v-for="item in siteOptions" :key="item.value" class="radio-item"
:class="{ active: selectedSiteIds.includes(item.value), disabled: item.disable }"> :class="{ active: item.value === siteId, disabled: item.disable }">
<checkbox class="radio" :value="item.value" :disabled="item.disable" <radio class="radio" :value="item.value" :disabled="item.disable"
:checked="selectedSiteIds.includes(item.value)" color="#547ef4" /> :checked="item.value === siteId" color="#547ef4" />
<text class="radio-text">{{ item.text }}</text> <text class="radio-text">{{ item.text }}</text>
</label> </label>
</checkbox-group> </radio-group>
</view> </view>
<view class="base-info"> <view class="base-info">
<view class="map-card">
<image v-if="mapUrl" class="site-map" :src="mapUrl" mode="aspectFill"></image>
<view v-else class="map-empty">暂无站点位置</view>
</view>
<view class="total-card"> <view class="total-card">
<view class="total-header"> <view class="total-header">
<view class="title">总累计运行数据</view> <view class="title">总累计运行数据</view>
@ -36,7 +40,7 @@
<uni-section title="收入曲线" type="line" class="sections-list"> <uni-section title="收入曲线" type="line" class="sections-list">
<view style="width:100%;height: 220px;"> <view style="width:100%;height: 220px;">
<qiun-data-charts type="line" :chartData="revenueChartData" :optsWatch='false' <qiun-data-charts type="column" :chartData="revenueChartData" :optsWatch='true'
:inScrollView="true" :pageScrollTop="pageScrollTop" :opts="revenueOptions" :ontouch="true" /> :inScrollView="true" :pageScrollTop="pageScrollTop" :opts="revenueOptions" :ontouch="true" />
</view> </view>
</uni-section> </uni-section>
@ -63,7 +67,8 @@
return { return {
pageScrollTop: 0, pageScrollTop: 0,
siteOptions: [], siteOptions: [],
selectedSiteIds: [], siteId: '',
mapUrl: '',
runningInfo: {}, runningInfo: {},
revenueChartData: {}, revenueChartData: {},
revenueOptions: { revenueOptions: {
@ -79,18 +84,7 @@
disabled: false, disabled: false,
splitNumber: 4 splitNumber: 4
}, },
extra: { extra: {}
line: {
type: "curve",
width: 2
},
area: {
type: "curve",
opacity: 0.2,
addLine: true,
gradient: true
}
}
}, },
sjglData: [{ sjglData: [{
title: "今日充电量kWh", title: "今日充电量kWh",
@ -159,16 +153,19 @@
return list return list
}, },
onSiteChange(e) { onSiteChange(e) {
const values = e.detail.value || [] const value = e.detail.value
this.selectedSiteIds = values if (value === this.siteId) return
this.siteId = value
this.updateSiteInfo() this.updateSiteInfo()
}, },
updateSiteInfo() { updateSiteInfo() {
if (!this.selectedSiteIds || this.selectedSiteIds.length === 0) { if (!this.siteId) {
this.runningInfo = {} this.runningInfo = {}
this.revenueChartData = {} this.revenueChartData = {}
this.mapUrl = ''
return return
} }
this.updateMapCenter()
this.getRunningInfo() this.getRunningInfo()
this.getRevenueChartData() this.getRevenueChartData()
}, },
@ -179,66 +176,107 @@
return { return {
text: item.siteName, text: item.siteName,
value: item.siteId, value: item.siteId,
longitude: Number(item.longitude || 0),
latitude: Number(item.latitude || 0),
disable: !this.belongSite || this.belongSite.length === 0 || this.belongSite.includes('all') ? false : !this disable: !this.belongSite || this.belongSite.length === 0 || this.belongSite.includes('all') ? false : !this
.belongSite.includes(item.siteId) .belongSite.includes(item.siteId)
} }
}) })
const defaultSite = this.siteOptions.find(item => !item.disable)?.value || '' this.siteId = this.siteOptions.find(item => !item.disable)?.value || ''
this.selectedSiteIds = defaultSite ? [defaultSite] : [] this.siteId && this.updateSiteInfo()
this.selectedSiteIds.length > 0 && this.updateSiteInfo()
}) })
}, },
updateMapCenter() {
const site = this.siteOptions.find(item => item.value === this.siteId)
if (!site || !site.latitude || !site.longitude) {
this.mapUrl = ''
return
}
const lat = Number(site.latitude)
const lon = Number(site.longitude)
const zoom = 12
const width = 640
const height = 360
const tk = '01e99ab4472430e1c7dbfe4b5db99787'
const layers = 'vec_c,cva_c'
this.mapUrl = `https://api.tianditu.gov.cn/staticimage?center=${lon},${lat}&width=${width}&height=${height}&zoom=${zoom}&layers=${layers}&markers=${lon},${lat}&tk=${tk}`
},
getRunningInfo() { getRunningInfo() {
const sumFields = ['totalRevenue', ...this.sjglData.map(item => item.attr)] getDzjkHomeView({
const requests = this.selectedSiteIds.map(siteId => getDzjkHomeView({ siteId: this.siteId
siteId }).then(response => {
}).then(response => response?.data || {})) this.runningInfo = response?.data || {}
Promise.all(requests).then(list => {
const result = {}
sumFields.forEach(key => {
result[key] = list.reduce((sum, item) => {
const value = Number(item?.[key] || 0)
return sum + (Number.isFinite(value) ? value : 0)
}, 0)
})
this.runningInfo = result
}) })
}, },
getRevenueChartData() { getRevenueChartData() {
const [startTime, endTime] = this.getLastDaysRange(7) const [startTime, endTime] = this.getLastDaysRange(7)
const dateList = this.buildDateList(startTime, endTime) const dateList = this.buildDateList(startTime, endTime)
const requests = this.selectedSiteIds.map(siteId => getAmmeterRevenueData({ const requests = [getAmmeterRevenueData({
siteId, siteId: this.siteId,
startTime, startTime,
endTime, endTime,
pageSize: 200, pageSize: 200,
pageNum: 1 pageNum: 1
}).then(response => ({ }).then(response => response?.rows || [])]
siteId,
rows: response?.rows || []
})))
Promise.all(requests).then(list => { Promise.all(requests).then(list => {
const rows = list[0] || []
const categories = dateList.map(day => day.slice(5)) const categories = dateList.map(day => day.slice(5))
const series = list.map(item => { const sumMap = {}
const sumMap = {} dateList.forEach(day => {
dateList.forEach(day => { sumMap[day] = 0
sumMap[day] = 0
})
item.rows.forEach(row => {
const day = row.dataTime || row.statisDate || row.date
if (!day) return
if (!Object.prototype.hasOwnProperty.call(sumMap, day)) {
sumMap[day] = 0
}
const value = Number(row.actualRevenue || row.revenue || 0)
sumMap[day] += Number.isFinite(value) ? value : 0
})
const name = this.siteOptions.find(opt => opt.value === item.siteId)?.text || item.siteId
return {
name,
data: dateList.map(day => Number((sumMap[day] || 0).toFixed(2)))
}
}) })
rows.forEach(row => {
const day = row.dataTime || row.statisDate || row.date
if (!day) return
if (!Object.prototype.hasOwnProperty.call(sumMap, day)) {
sumMap[day] = 0
}
const value = Number(row.actualRevenue || row.revenue || 0)
sumMap[day] += Number.isFinite(value) ? value : 0
})
const series = [{
name: '收入',
data: dateList.map(day => Number((sumMap[day] || 0).toFixed(2)))
}]
const values = series[0].data
let minVal = Math.min(...values)
let maxVal = Math.max(...values)
if (!Number.isFinite(minVal)) minVal = 0
if (!Number.isFinite(maxVal)) maxVal = 0
if (minVal === maxVal) {
minVal -= 1
maxVal += 1
}
const lower = Math.min(minVal, 0)
const upper = Math.max(maxVal, 0)
const padding = Math.max((upper - lower) * 0.1, 1)
this.revenueOptions = {
...this.revenueOptions,
yAxis: {
...(this.revenueOptions.yAxis || {}),
data: [{
min: lower - padding,
max: upper + padding,
format: (val) => Number(val).toFixed(2)
}]
},
extra: {
...(this.revenueOptions.extra || {}),
markLine: {
type: 'solid',
data: [{
value: 0,
lineColor: '#ff4d4f',
showLabel: true,
labelText: '0',
labelFontColor: '#ff4d4f',
labelBgColor: '#fff1f0',
labelBgOpacity: 0.9,
labelAlign: 'left'
}]
}
}
}
this.revenueChartData = JSON.parse(JSON.stringify({ this.revenueChartData = JSON.parse(JSON.stringify({
categories, categories,
series series
@ -278,13 +316,16 @@
.home-container { .home-container {
background-color: #fff; background-color: #fff;
padding-top: 0;
} }
.site-sections-list { .site-sections-list {
background: linear-gradient(to right, #547ef4, #679ff5); background: linear-gradient(to right, #547ef4, #679ff5);
padding: 30rpx 30rpx 100rpx; padding: 30rpx 30rpx;
padding-bottom: 100rpx;
color: #fff; color: #fff;
.site-title { .site-title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 600; font-weight: 600;
@ -336,6 +377,28 @@
padding: 30rpx; padding: 30rpx;
background-color: #fff; background-color: #fff;
.map-card {
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
margin-top: 30rpx;
background: #f3f5f8;
.site-map {
width: 100%;
height: 220px;
}
.map-empty {
height: 220px;
display: flex;
align-items: center;
justify-content: center;
color: #8a8f98;
font-size: 24rpx;
}
}
.total-card { .total-card {
margin-top: 30rpx; margin-top: 30rpx;
border-radius: 16rpx; border-radius: 16rpx;

View File

@ -1,6 +1,7 @@
<template> <template>
<view class="container"> <view class="container">
<view class="status-bar"></view> <view class="status-bar"></view>
<view class="page-title">工单</view>
<view class="btn-list"> <view class="btn-list">
<uni-row> <uni-row>
<uni-col :span="12"> <uni-col :span="12">
@ -129,7 +130,7 @@
.btn-list { .btn-list {
position: fixed; position: fixed;
top: var(--status-bar-height); top: calc(var(--status-bar-height) + 56rpx);
left: 0; left: 0;
width: 100%; width: 100%;
z-index: 2; z-index: 2;
@ -153,10 +154,24 @@
} }
.content { .content {
padding: 80px 30rpx 120rpx 30rpx; padding: 120px 30rpx 120rpx 30rpx;
z-index: 1; z-index: 1;
} }
.page-title {
position: fixed;
top: var(--status-bar-height);
left: 0;
width: 100%;
z-index: 3;
padding: 16rpx 30rpx 10rpx;
font-size: 32rpx;
font-weight: 700;
color: #19242d;
background: #ffffff;
text-align: center;
}
// 工单列表 // 工单列表
.item-list { .item-list {
color: #4b4951; color: #4b4951;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 373 KiB

View File

@ -638,6 +638,14 @@ export default {
methods: { methods: {
beforeInit(){ beforeInit(){
this.mixinDatacomErrorMessage = null; this.mixinDatacomErrorMessage = null;
console.log('[map-debug] beforeInit', {
echarts: this.echarts,
type: this.type,
hasEopts: !!this.eopts,
eoptsMapName: this.eopts && this.eopts.mapName,
hasGeo: !!(this.eopts && this.eopts.mapGeoJSON),
chartSeriesLen: this.chartData && this.chartData.series ? this.chartData.series.length : -1
});
if (typeof this.chartData === 'object' && this.chartData != null && this.chartData.series !== undefined && this.chartData.series.length > 0) { if (typeof this.chartData === 'object' && this.chartData != null && this.chartData.series !== undefined && this.chartData.series.length > 0) {
//拷贝一下chartData为了opts变更后统一数据来源 //拷贝一下chartData为了opts变更后统一数据来源
this.drawData = deepCloneAssign({}, this.chartData); this.drawData = deepCloneAssign({}, this.chartData);
@ -784,6 +792,14 @@ export default {
}, },
checkData(anyData) { checkData(anyData) {
let cid = this.cid let cid = this.cid
console.log('[map-debug] checkData', {
cid,
echarts: this.echarts,
type: this.type,
eoptsMapName: this.eopts && this.eopts.mapName,
hasGeo: !!(this.eopts && this.eopts.mapGeoJSON),
dataSeriesLen: anyData && anyData.series ? anyData.series.length : -1
});
//复位opts或eopts //复位opts或eopts
if(this.echarts === true){ if(this.echarts === true){
cfe.option[cid] = deepCloneAssign({}, this.eopts); cfe.option[cid] = deepCloneAssign({}, this.eopts);
@ -916,6 +932,12 @@ export default {
cfe.option[cid].tooltipFormat = this.tooltipFormat; cfe.option[cid].tooltipFormat = this.tooltipFormat;
cfe.option[cid].tooltipCustom = this.tooltipCustom; cfe.option[cid].tooltipCustom = this.tooltipCustom;
cfe.option[cid].lastDrawTime = this.lastDrawTime; cfe.option[cid].lastDrawTime = this.lastDrawTime;
console.log('[map-debug] set echartsOpts', {
cid,
type: cfe.option[cid].type,
mapName: cfe.option[cid].mapName,
hasGeo: !!cfe.option[cid].mapGeoJSON
});
this.echartsOpts = deepCloneAssign({}, cfe.option[cid]); this.echartsOpts = deepCloneAssign({}, cfe.option[cid]);
} else { } else {
cfu.option[cid].rotateLock = cfu.option[cid].rotate; cfu.option[cid].rotateLock = cfu.option[cid].rotate;
@ -1273,7 +1295,8 @@ export default {
methods: { methods: {
//==============以下是ECharts的方法==================== //==============以下是ECharts的方法====================
ecinit(newVal, oldVal, owner, instance){ ecinit(newVal, oldVal, owner, instance){
let cid = JSON.stringify(newVal.id) let cid = JSON.stringify(newVal.id || newVal.canvasId || (owner && owner.cid) || (instance && instance.cid))
console.log('[map-debug] ecinit', { cid, type: newVal.type, hasMapName: !!newVal.mapName, hasGeo: !!newVal.mapGeoJSON });
this.rid = cid this.rid = cid
that[cid] = this.$ownerInstance || instance that[cid] = this.$ownerInstance || instance
let eopts = JSON.parse(JSON.stringify(newVal)) let eopts = JSON.parse(JSON.stringify(newVal))
@ -1285,7 +1308,7 @@ export default {
cfe.option[cid] = rddeepCloneAssign({}, eopts); cfe.option[cid] = rddeepCloneAssign({}, eopts);
} }
let newData = eopts.chartData; let newData = eopts.chartData;
if(newData){ if(newData && !eopts.skipChartData){
//挂载categories和series //挂载categories和series
if(cfe.option[cid].xAxis && cfe.option[cid].xAxis.type && cfe.option[cid].xAxis.type === 'category'){ if(cfe.option[cid].xAxis && cfe.option[cid].xAxis.type && cfe.option[cid].xAxis.type === 'category'){
cfe.option[cid].xAxis.data = newData.categories cfe.option[cid].xAxis.data = newData.categories
@ -1297,13 +1320,25 @@ export default {
for (var i = 0; i < newData.series.length; i++) { for (var i = 0; i < newData.series.length; i++) {
cfe.option[cid].seriesTemplate = cfe.option[cid].seriesTemplate ? cfe.option[cid].seriesTemplate : {} cfe.option[cid].seriesTemplate = cfe.option[cid].seriesTemplate ? cfe.option[cid].seriesTemplate : {}
let Template = rddeepCloneAssign({},cfe.option[cid].seriesTemplate,newData.series[i]) let Template = rddeepCloneAssign({},cfe.option[cid].seriesTemplate,newData.series[i])
cfe.option[cid].series.push(Template) cfe.option[cid].series.push(Template)
}
}
if (typeof window.echarts === 'object' && eopts.mapName && eopts.mapGeoJSON) {
try {
if (!window.echarts.getMap(eopts.mapName)) {
window.echarts.registerMap(eopts.mapName, eopts.mapGeoJSON);
}
} catch (e) {
console.log('[map-debug] registerMap error', e);
} }
} }
if (typeof window.echarts === 'object') { if (typeof window.echarts === 'object') {
console.log('[map-debug] echarts exists, init');
this.newEChart() this.newEChart()
}else{ }else{
console.log('[map-debug] echarts not found, load script');
const script = document.createElement('script') const script = document.createElement('script')
// #ifdef APP-VUE // #ifdef APP-VUE
script.src = './uni_modules/qiun-data-charts/static/app-plus/echarts.min.js' script.src = './uni_modules/qiun-data-charts/static/app-plus/echarts.min.js'
@ -1324,8 +1359,19 @@ export default {
}, },
newEChart(){ newEChart(){
let cid = this.rid let cid = this.rid
console.log('[map-debug] newEChart', { cid });
if (cfe.option[cid] && cfe.option[cid].mapName && cfe.option[cid].mapGeoJSON) {
try {
if (!echarts.getMap(cfe.option[cid].mapName)) {
echarts.registerMap(cfe.option[cid].mapName, cfe.option[cid].mapGeoJSON);
}
} catch (e) {
console.log('[map-debug] registerMap error in newEChart', e);
}
}
if(cfe.instance[cid] === undefined){ if(cfe.instance[cid] === undefined){
cfe.instance[cid] = echarts.init(that[cid].$el.children[0]) cfe.instance[cid] = echarts.init(that[cid].$el.children[0])
console.log('[map-debug] echarts init instance created');
//ontap开启后才触发click事件 //ontap开启后才触发click事件
if(cfe.option[cid].ontap === true){ if(cfe.option[cid].ontap === true){
cfe.instance[cid].on('click', resdata => { cfe.instance[cid].on('click', resdata => {
@ -1345,6 +1391,7 @@ export default {
} }
}, },
updataEChart(cid,option){ updataEChart(cid,option){
console.log('[map-debug] updateEChart', { cid, hasSeries: !!option.series, seriesLen: option.series ? option.series.length : 0 });
//替换option内format属性为formatter的预定义方法 //替换option内format属性为formatter的预定义方法
option = rdformatterAssign(option,cfe.formatter) option = rdformatterAssign(option,cfe.formatter)
if(option.tooltip){ if(option.tooltip){