Files
fishery-app/src/components/other/chartFullscreen.vue

420 lines
9.8 KiB
Vue
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.
<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>