420 lines
9.8 KiB
Vue
420 lines
9.8 KiB
Vue
<template>
|
||
<view class="chart-fullscreen-page" :style="pageStyle">
|
||
<!-- 顶部标题栏 -->
|
||
<view class="header">
|
||
<view class="back-btn" @click="goBack">
|
||
<text class="icon">←</text>
|
||
</view>
|
||
<view class="title">{{ chartTitle }}</view>
|
||
</view>
|
||
|
||
<!-- 时间选择 -->
|
||
<view class="time-selector">
|
||
<view
|
||
class="time-item"
|
||
:class="{ active: dayType === 1 }"
|
||
@click="changeDay(1)"
|
||
>
|
||
1天
|
||
</view>
|
||
<view
|
||
class="time-item"
|
||
:class="{ active: dayType === 3 }"
|
||
@click="changeDay(3)"
|
||
>
|
||
3天
|
||
</view>
|
||
<view
|
||
class="time-item"
|
||
:class="{ active: dayType === 7 }"
|
||
@click="changeDay(7)"
|
||
>
|
||
7天
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 图表容器 -->
|
||
<view class="chart-container" :style="chartContainerStyle">
|
||
<canvas
|
||
class="chart-canvas"
|
||
type="2d"
|
||
id="fullscreen-chart"
|
||
:style="canvasStyle"
|
||
></canvas>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||
import Taro from '@tarojs/taro';
|
||
import { deviceHistory } from '@/api/device';
|
||
import { formatDateMin } from '@/utils/tools';
|
||
import uCharts from '@/utils/js-sdk/u-charts/u-charts.js';
|
||
|
||
const dayType = ref(1);
|
||
const chartTitle = ref('溶解氧曲线图');
|
||
const chartData = ref<any[]>([]);
|
||
let chartInstance: any = null;
|
||
|
||
// 接收的参数
|
||
const serialNum = ref('');
|
||
const dataType = ref(1); // 1-溶解氧, 2-水温, 3-饱和度, 4-PH, 5-盐度
|
||
|
||
// 屏幕尺寸
|
||
const screenWidth = ref(375);
|
||
const screenHeight = ref(667);
|
||
|
||
// 页面样式(小程序通过配置自动横屏)
|
||
const pageStyle = computed(() => {
|
||
return {
|
||
width: '100%',
|
||
height: '100%',
|
||
background: '#fff',
|
||
display: 'flex',
|
||
flexDirection: 'column' as const,
|
||
};
|
||
});
|
||
|
||
const chartContainerStyle = computed(() => {
|
||
return {
|
||
flex: '1',
|
||
padding: '10px 15px',
|
||
background: '#fff',
|
||
overflow: 'hidden',
|
||
};
|
||
});
|
||
|
||
const canvasStyle = computed(() => {
|
||
return {
|
||
width: chartWidth.value + 'px',
|
||
height: chartHeight.value + 'px',
|
||
};
|
||
});
|
||
|
||
onMounted(() => {
|
||
// 获取屏幕尺寸
|
||
const sysInfo = Taro.getSystemInfoSync();
|
||
screenWidth.value = sysInfo.windowWidth;
|
||
screenHeight.value = sysInfo.windowHeight;
|
||
|
||
// 获取页面参数
|
||
const instance = Taro.getCurrentInstance();
|
||
const params = instance.router?.params || {};
|
||
|
||
serialNum.value = params.serialNum || '';
|
||
dataType.value = Number(params.dataType) || 1;
|
||
dayType.value = Number(params.dayType) || 1;
|
||
|
||
// 设置标题
|
||
const titles = ['', '溶解氧曲线图', '水温曲线图', '饱和度曲线图', 'PH曲线图', '盐度曲线图'];
|
||
chartTitle.value = titles[dataType.value] || '曲线图';
|
||
|
||
// 初始化图表
|
||
setTimeout(() => {
|
||
initChart();
|
||
}, 500);
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
if (chartInstance) {
|
||
chartInstance = null;
|
||
}
|
||
});
|
||
|
||
// 图表尺寸
|
||
const chartWidth = ref(0);
|
||
const chartHeight = ref(0);
|
||
const chartPixelRatio = ref(1);
|
||
|
||
// 初始化图表
|
||
function initChart() {
|
||
const sysInfo = Taro.getSystemInfoSync();
|
||
// 横屏模式,windowWidth 是横屏后的宽度(较大),windowHeight 是横屏后的高度(较小)
|
||
// header(44px) + time-selector(44px) = 88px
|
||
chartWidth.value = sysInfo.windowWidth - 30;
|
||
chartHeight.value = sysInfo.windowHeight - 100;
|
||
chartPixelRatio.value = sysInfo.pixelRatio || 1;
|
||
|
||
// 加载数据
|
||
loadChartData();
|
||
}
|
||
|
||
// 返回上一页
|
||
function goBack() {
|
||
Taro.navigateBack();
|
||
}
|
||
|
||
// 返回首页
|
||
function goHome() {
|
||
Taro.switchTab({
|
||
url: '/pages/main/home'
|
||
});
|
||
}
|
||
|
||
// 切换天数
|
||
function changeDay(day: number) {
|
||
dayType.value = day;
|
||
loadChartData();
|
||
}
|
||
|
||
// 加载图表数据
|
||
function loadChartData() {
|
||
if (!serialNum.value) return;
|
||
|
||
const now = new Date();
|
||
const start = new Date(now.getTime() - dayType.value * 24 * 60 * 60 * 1000);
|
||
|
||
const params = {
|
||
serialNum: serialNum.value,
|
||
startTime: formatDateMin(start),
|
||
endTime: formatDateMin(now),
|
||
intervalType: dayType.value === 1 ? 1 : dayType.value === 3 ? 2 : 3,
|
||
};
|
||
|
||
Taro.showLoading({ title: '加载中...' });
|
||
|
||
deviceHistory(params).then((res: any) => {
|
||
Taro.hideLoading();
|
||
|
||
if (Array.isArray(res)) {
|
||
chartData.value = res;
|
||
} else if (res.code == 200) {
|
||
chartData.value = res.data || [];
|
||
}
|
||
|
||
// 绘制图表
|
||
drawChart();
|
||
}).catch(() => {
|
||
Taro.hideLoading();
|
||
});
|
||
}
|
||
|
||
// 绘制图表
|
||
function drawChart() {
|
||
if (!chartData.value || chartData.value.length === 0) {
|
||
return;
|
||
}
|
||
|
||
const cWidth = chartWidth.value;
|
||
const cHeight = chartHeight.value;
|
||
const pixelRatio = chartPixelRatio.value;
|
||
|
||
const fieldMap: any = {
|
||
1: 'dissolvedOxygen',
|
||
2: 'temperature',
|
||
3: 'saturability',
|
||
4: 'ph',
|
||
5: 'salinity',
|
||
};
|
||
|
||
const nameMap: any = {
|
||
1: '溶解氧',
|
||
2: '水温',
|
||
3: '饱和度',
|
||
4: 'PH',
|
||
5: '盐度',
|
||
};
|
||
|
||
const fieldName = fieldMap[dataType.value] || 'dissolvedOxygen';
|
||
const seriesName = nameMap[dataType.value] || '溶解氧';
|
||
|
||
const xAxisData: string[] = [];
|
||
const seriesData: number[] = [];
|
||
|
||
chartData.value.forEach((item: any) => {
|
||
const timeStr = item.time || item.createTime || '';
|
||
const timeParts = timeStr.split(' ');
|
||
if (timeParts.length > 1) {
|
||
const datePart = timeParts[0];
|
||
const timePart = timeParts[1];
|
||
|
||
const dateMatch = datePart.match(/\d{4}-(\d{2})-(\d{2})/);
|
||
const monthDay = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}` : '';
|
||
const hourMin = timePart.substring(0, 5);
|
||
|
||
const formattedTime = `${monthDay} ${hourMin}`;
|
||
xAxisData.push(formattedTime);
|
||
}
|
||
const value = item[fieldName];
|
||
seriesData.push(value !== null && value !== undefined ? Number(value) : 0);
|
||
});
|
||
|
||
Taro.nextTick(() => {
|
||
const env = Taro.getEnv();
|
||
|
||
if (env === Taro.ENV_TYPE.WEB) {
|
||
// H5 环境
|
||
setTimeout(() => {
|
||
const canvasElement = document.querySelector('#fullscreen-chart canvas') as HTMLCanvasElement;
|
||
if (!canvasElement) return;
|
||
|
||
const ctx = canvasElement.getContext('2d');
|
||
if (!ctx) return;
|
||
|
||
chartInstance = new uCharts({
|
||
type: 'line',
|
||
context: ctx,
|
||
width: cWidth,
|
||
height: cHeight,
|
||
categories: xAxisData,
|
||
series: [{ name: seriesName, data: seriesData }],
|
||
pixelRatio: 1,
|
||
dataPointShape: false,
|
||
animation: true,
|
||
background: '#FFFFFF',
|
||
color: ['#17B4B2'],
|
||
padding: [15, 15, 30, 40],
|
||
enableScroll: false,
|
||
boundaryGap: 'justify',
|
||
legend: { show: false },
|
||
dataLabel: false,
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
type: 'grid',
|
||
gridType: 'dash',
|
||
scrollShow: false,
|
||
labelCount: 8,
|
||
rotateLabel: true,
|
||
rotateAngle: 30,
|
||
},
|
||
yAxis: {
|
||
disableGrid: false,
|
||
gridType: 'dash',
|
||
tofix: 2,
|
||
},
|
||
extra: {
|
||
line: { type: 'curve', width: 2, activeType: 'hollow' },
|
||
},
|
||
});
|
||
}, 300);
|
||
} else {
|
||
// 小程序环境
|
||
const query = Taro.createSelectorQuery();
|
||
query.select('#fullscreen-chart')
|
||
.fields({ node: true, size: true })
|
||
.exec((res) => {
|
||
if (!res || !res[0] || !res[0].node) return;
|
||
|
||
const canvas = res[0].node;
|
||
const ctx = canvas.getContext('2d');
|
||
if (!ctx) return;
|
||
|
||
canvas.width = cWidth * pixelRatio;
|
||
canvas.height = cHeight * pixelRatio;
|
||
|
||
chartInstance = new uCharts({
|
||
type: 'line',
|
||
context: ctx,
|
||
width: cWidth * pixelRatio,
|
||
height: cHeight * pixelRatio,
|
||
categories: xAxisData,
|
||
series: [{ name: seriesName, data: seriesData }],
|
||
pixelRatio: pixelRatio,
|
||
dataPointShape: false,
|
||
animation: true,
|
||
background: '#FFFFFF',
|
||
color: ['#17B4B2'],
|
||
padding: [15, 15, 30, 40],
|
||
enableScroll: false,
|
||
boundaryGap: 'justify',
|
||
legend: { show: false },
|
||
dataLabel: false,
|
||
xAxis: {
|
||
disableGrid: true,
|
||
axisLine: true,
|
||
type: 'grid',
|
||
gridType: 'dash',
|
||
scrollShow: false,
|
||
labelCount: 6,
|
||
rotateLabel: true,
|
||
rotateAngle: 20,
|
||
marginTop: 5,
|
||
},
|
||
yAxis: {
|
||
disableGrid: false,
|
||
gridType: 'dash',
|
||
tofix: 2,
|
||
},
|
||
extra: {
|
||
line: { type: 'curve', width: 2, activeType: 'hollow' },
|
||
},
|
||
});
|
||
});
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.chart-fullscreen-page {
|
||
background: #fff;
|
||
}
|
||
|
||
.header {
|
||
height: 44px;
|
||
background: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 15px;
|
||
border-bottom: 1px solid #eee;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.back-btn {
|
||
width: 44px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.back-btn .icon {
|
||
font-size: 24px;
|
||
color: #333;
|
||
}
|
||
|
||
.title {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-right: 44px;
|
||
}
|
||
|
||
.time-selector {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 8px 15px;
|
||
background: #fff;
|
||
flex-shrink: 0;
|
||
height: 44px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.time-item {
|
||
padding: 6px 20px;
|
||
margin: 0 8px;
|
||
background: #f5f5f5;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
.time-item.active {
|
||
background: #09B8C2;
|
||
color: #fff;
|
||
}
|
||
|
||
.chart-container {
|
||
background: #fff;
|
||
}
|
||
|
||
.chart-canvas {
|
||
display: block;
|
||
}
|
||
</style>
|