import { webview } from '@kit.ArkWeb'; import { http } from '@kit.NetworkKit'; import CarbonInfoInterface from '../model/CarbonInfoInterface'; import StatisticalInterface from '../model/StatisticalInterface'; import DeviceListInterface from '../model/DeviceListInterface'; import { getCarbonInfo, getCarbonList, getDeviceList } from '../utils/ApiService'; // 监测点数据接口 interface MonitorPoint { name: string; lat: number; lng: number; status: number; color: string; } /** * 碳汇计算组件 */ @Component export struct CarbonCalc { // 图表控制器 controller: webview.WebviewController = new webview.WebviewController(); // 状态数据 @State carbonInfo: CarbonInfoInterface | null = null; @State carbonTrend: StatisticalInterface[] = []; @State monitorPoints: MonitorPoint[] = []; @State isLoading: boolean = true; @State isWebViewReady: boolean = false; @State avgTemp: number = 25.0; // 模拟数据 private mockCarbonInfo: CarbonInfoInterface = { carbonSequestration: 125.6, ph: 7.4, waterTemperature: 25.2 }; private mockTrend: number[] = [98, 105, 112, 118, 122, 126]; private mockPoints: MonitorPoint[] = [ { name: '长江监测点A', lat: 30.5, lng: 114.3, status: 1, color: '#00FF88' }, { name: '长江监测点B', lat: 31.2, lng: 115.1, status: 0, color: '#FF6B6B' }, { name: '长江监测点C', lat: 29.8, lng: 113.5, status: 1, color: '#00FF88' }, { name: '长江监测点D', lat: 32.0, lng: 118.7, status: 1, color: '#00FF88' } ]; aboutToAppear(): void { this.loadData(); setTimeout(() => { this.isWebViewReady = true; this.updateChart(); }, 1000); } loadData(): void { this.carbonInfo = this.mockCarbonInfo; this.monitorPoints = this.mockPoints; this.avgTemp = this.mockCarbonInfo.waterTemperature; this.isLoading = false; // 尝试获取真实数据 getCarbonInfo().then((result) => { if (result && result.data) { this.carbonInfo = result.data; this.avgTemp = result.data.waterTemperature; } }).catch(() => { // 使用模拟数据 }); getCarbonList().then((result) => { if (result && result.data && result.data.length > 0) { this.carbonTrend = result.data; } }).catch(() => { // 使用模拟数据 }); getDeviceList().then((result) => { if (result && result.data && result.data.length > 0) { this.parseDeviceData(result.data); } }).catch(() => { // 使用模拟数据 }); } parseDeviceData(devices: DeviceListInterface[]): void { const points: MonitorPoint[] = []; for (let i: number = 0; i < devices.length; i++) { const device: DeviceListInterface = devices[i]; const coords: string[] = device.latitudeLongitude.split(','); if (coords.length >= 2) { const lat: number = parseFloat(coords[0]); const lng: number = parseFloat(coords[1]); points.push({ name: device.name, lat: lat, lng: lng, status: device.status, color: device.status === 1 ? '#00FF88' : '#FF6B6B' }); } } if (points.length > 0) { this.monitorPoints = points; } } updateChart(): void { if (!this.isWebViewReady) return; try { const jsCode: string = `updateData([${this.mockTrend.join(',')}])`; this.controller.runJavaScript(jsCode); } catch (e) { console.error('图表更新失败'); } } build() { Column() { // 标题 Row() { Text('碳汇计算') .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#FFFFFF') Blank() Text('水体固碳能力') .fontSize(11) .fontColor('rgba(255,255,255,0.5)') } .width('92%') .padding({ top: 12, bottom: 8 }) // ===== 上部分:统计卡片 ===== Row({ space: 8 }) { // 上月平均温度 Column({ space: 4 }) { Text('上月平均温度') .fontSize(11) .fontColor('rgba(255,255,255,0.6)') Text(this.avgTemp.toFixed(1) + '°C') .fontSize(18) .fontWeight(600) .fontColor('#FFE66D') Row() { Circle().width(4).height(4).fill('#FFE66D') Text('正常') .fontSize(9) .fontColor('rgba(255,255,255,0.5)') .margin({ left: 3 }) } } .width('31%') .height(70) .padding(8) .borderRadius(12) .border({ width: 1, color: 'rgba(255,230,109,0.3)' }) .backgroundColor('rgba(255,230,109,0.08)') // 上月碳汇能力 Column({ space: 4 }) { Text('上月碳汇能力') .fontSize(11) .fontColor('rgba(255,255,255,0.6)') Text((this.carbonInfo?.carbonSequestration ?? 0).toFixed(1) + 'kg') .fontSize(18) .fontWeight(600) .fontColor('#00FF88') Row() { Circle().width(4).height(4).fill('#00FF88') Text('良好') .fontSize(9) .fontColor('rgba(255,255,255,0.5)') .margin({ left: 3 }) } } .width('31%') .height(70) .padding(8) .borderRadius(12) .border({ width: 1, color: 'rgba(0,255,136,0.3)' }) .backgroundColor('rgba(0,255,136,0.08)') // 上月平均pH Column({ space: 4 }) { Text('上月平均pH') .fontSize(11) .fontColor('rgba(255,255,255,0.6)') Text((this.carbonInfo?.ph ?? 0).toFixed(1)) .fontSize(18) .fontWeight(600) .fontColor('#4ECDC4') Row() { Circle().width(4).height(4).fill('#4ECDC4') Text('达标') .fontSize(9) .fontColor('rgba(255,255,255,0.5)') .margin({ left: 3 }) } } .width('31%') .height(70) .padding(8) .borderRadius(12) .border({ width: 1, color: 'rgba(78,205,196,0.3)' }) .backgroundColor('rgba(78,205,196,0.08)') } .width('92%') .justifyContent(FlexAlign.SpaceBetween) // ===== 中间部分:碳汇趋势折线图 ===== Column({ space: 6 }) { Row() { Text('碳汇趋势') .fontSize(13) .fontWeight(500) .fontColor('#FFFFFF') Blank() Text('近6个月') .fontSize(10) .fontColor('rgba(255,255,255,0.5)') } .width('100%') Web({ src: $rawfile('chart/carbon_chart.html'), controller: this.controller }) .width('100%') .height(160) .backgroundColor('transparent') .javaScriptAccess(true) } .width('92%') .padding(10) .margin({ top: 8 }) .borderRadius(12) .border({ width: 1, color: 'rgba(22,119,255,0.25)' }) .backgroundColor('rgba(13,40,71,0.5)') // ===== 下部分:监测点分布地图 ===== Column({ space: 8 }) { // 地图标题 Row() { Text('监测点分布') .fontSize(13) .fontWeight(500) .fontColor('#FFFFFF') Blank() Row({ space: 6 }) { Row({ space: 3 }) { Circle().width(5).height(5).fill('#00FF88') Text('正常') .fontSize(9) .fontColor('rgba(255,255,255,0.6)') } Row({ space: 3 }) { Circle().width(5).height(5).fill('#FF6B6B') Text('异常') .fontSize(9) .fontColor('rgba(255,255,255,0.6)') } } } .width('100%') // 监测点地图区域(图片占位符,后期添加图片后取消注释) Image($rawfile('ces/all_point.png')) .width('100%') .height(180) .borderRadius(12) .border({ width: 1, color: 'rgba(22,119,255,0.3)' }) // 临时占位区域(待添加图片) // Column() // .width('100%') // .height(180) // .linearGradient({ // angle: 135, // colors: [['#1a3a5c', 0], ['#0d2847', 1]] // }) // .borderRadius(12) // .border({ width: 1, color: 'rgba(22,119,255,0.3)' }) // 监测点列表 Row({ space: 10 }) { ForEach(this.monitorPoints, (point: MonitorPoint) => { Row({ space: 4 }) { Circle() .width(6) .height(6) .fill(point.color) Text(point.name) .fontSize(10) .fontColor('rgba(255,255,255,0.8)') } .padding({ left: 6, right: 6, top: 4, bottom: 4 }) .borderRadius(8) .backgroundColor('rgba(22,119,255,0.1)') }) } .width('100%') .justifyContent(FlexAlign.Center) } .width('92%') .layoutWeight(1) .margin({ top: 8 }) .padding(10) .borderRadius(12) .border({ width: 1, color: 'rgba(22,119,255,0.25)' }) .backgroundColor('rgba(13,40,71,0.4)') } .width('100%') .height('100%') .linearGradient({ angle: 180, colors: [['#0a1628', 0], ['#0d2847', 0.5], ['#1677FF', 1]] }) } }