fix: 曲线图功能优化,放大全屏显示优化。

This commit is contained in:
tianyongbao
2026-01-19 11:19:51 +08:00
parent 8084b705a6
commit 02f4f6ecd8
5 changed files with 223 additions and 185 deletions

View File

@@ -2,4 +2,5 @@ export default definePageConfig({
navigationBarTitleText: '曲线图', navigationBarTitleText: '曲线图',
navigationStyle: 'custom', navigationStyle: 'custom',
enablePullDownRefresh: false, enablePullDownRefresh: false,
pageOrientation: 'landscape',
}) })

View File

@@ -1,5 +1,5 @@
<template> <template>
<view class="chart-fullscreen-page"> <view class="chart-fullscreen-page" :style="pageStyle">
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<view class="header"> <view class="header">
<view class="back-btn" @click="goBack"> <view class="back-btn" @click="goBack">
@@ -34,18 +34,19 @@
</view> </view>
<!-- 图表容器 --> <!-- 图表容器 -->
<view class="chart-container"> <view class="chart-container" :style="chartContainerStyle">
<canvas <canvas
class="chart-canvas" class="chart-canvas"
canvas-id="fullscreen-chart" type="2d"
id="fullscreen-chart" id="fullscreen-chart"
:style="canvasStyle"
></canvas> ></canvas>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { deviceHistory } from '@/api/device'; import { deviceHistory } from '@/api/device';
import { formatDateMin } from '@/utils/tools'; import { formatDateMin } from '@/utils/tools';
@@ -60,7 +61,43 @@ let chartInstance: any = null;
const serialNum = ref(''); const serialNum = ref('');
const dataType = ref(1); // 1-溶解氧, 2-水温, 3-饱和度, 4-PH, 5-盐度 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(() => { onMounted(() => {
// 获取屏幕尺寸
const sysInfo = Taro.getSystemInfoSync();
screenWidth.value = sysInfo.windowWidth;
screenHeight.value = sysInfo.windowHeight;
// 获取页面参数 // 获取页面参数
const instance = Taro.getCurrentInstance(); const instance = Taro.getCurrentInstance();
const params = instance.router?.params || {}; const params = instance.router?.params || {};
@@ -85,29 +122,22 @@ onUnmounted(() => {
} }
}); });
// 图表尺寸
const chartWidth = ref(0);
const chartHeight = ref(0);
const chartPixelRatio = ref(1);
// 初始化图表 // 初始化图表
function initChart() { function initChart() {
// 横屏后的尺寸计算: const sysInfo = Taro.getSystemInfoSync();
// 页面宽度 = 屏幕高度100vh // 横屏模式windowWidth 是横屏后的宽度较大windowHeight 是横屏后的高度(较小
// 页面高度 = 屏幕宽度100vw // header(44px) + time-selector(44px) = 88px
// header(50px) + time-selector(50px) = 100px chartWidth.value = sysInfo.windowWidth - 30;
chartHeight.value = sysInfo.windowHeight - 100;
const screenWidth = window.innerWidth; // 屏幕宽度 chartPixelRatio.value = sysInfo.pixelRatio || 1;
const screenHeight = window.innerHeight; // 屏幕高度
// 横屏后:
const cWidth = screenHeight - 40; // 页面宽度(100vh) - padding
const cHeight = screenWidth - 120; // 页面高度(100vw) - header - time-selector - padding
console.log('📊 横屏图表尺寸计算:', {
屏幕宽: screenWidth,
屏幕高: screenHeight,
图表宽: cWidth,
图表高: cHeight
});
// 加载数据 // 加载数据
loadChartData(cWidth, cHeight, 1); loadChartData();
} }
// 返回上一页 // 返回上一页
@@ -125,18 +155,11 @@ function goHome() {
// 切换天数 // 切换天数
function changeDay(day: number) { function changeDay(day: number) {
dayType.value = day; dayType.value = day;
loadChartData();
// 重新计算尺寸
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
const cWidth = screenHeight - 40;
const cHeight = screenWidth - 120;
loadChartData(cWidth, cHeight, 1);
} }
// 加载图表数据 // 加载图表数据
function loadChartData(cWidth: number, cHeight: number, pixelRatio: number) { function loadChartData() {
if (!serialNum.value) return; if (!serialNum.value) return;
const now = new Date(); const now = new Date();
@@ -161,19 +184,22 @@ function loadChartData(cWidth: number, cHeight: number, pixelRatio: number) {
} }
// 绘制图表 // 绘制图表
drawChart(cWidth, cHeight, pixelRatio); drawChart();
}).catch(() => { }).catch(() => {
Taro.hideLoading(); Taro.hideLoading();
}); });
} }
// 绘制图表 // 绘制图表
function drawChart(cWidth: number, cHeight: number, pixelRatio: number) { function drawChart() {
if (!chartData.value || chartData.value.length === 0) { if (!chartData.value || chartData.value.length === 0) {
console.log('❌ 数据为空');
return; return;
} }
const cWidth = chartWidth.value;
const cHeight = chartHeight.value;
const pixelRatio = chartPixelRatio.value;
const fieldMap: any = { const fieldMap: any = {
1: 'dissolvedOxygen', 1: 'dissolvedOxygen',
2: 'temperature', 2: 'temperature',
@@ -200,13 +226,12 @@ function drawChart(cWidth: number, cHeight: number, pixelRatio: number) {
const timeStr = item.time || item.createTime || ''; const timeStr = item.time || item.createTime || '';
const timeParts = timeStr.split(' '); const timeParts = timeStr.split(' ');
if (timeParts.length > 1) { if (timeParts.length > 1) {
const datePart = timeParts[0]; // YYYY-MM-DD const datePart = timeParts[0];
const timePart = timeParts[1]; // HH:mm:ss.0 const timePart = timeParts[1];
// 提取 MM-DD HH:mm
const dateMatch = datePart.match(/\d{4}-(\d{2})-(\d{2})/); const dateMatch = datePart.match(/\d{4}-(\d{2})-(\d{2})/);
const monthDay = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}` : ''; const monthDay = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}` : '';
const hourMin = timePart.substring(0, 5); // HH:mm const hourMin = timePart.substring(0, 5);
const formattedTime = `${monthDay} ${hourMin}`; const formattedTime = `${monthDay} ${hourMin}`;
xAxisData.push(formattedTime); xAxisData.push(formattedTime);
@@ -215,174 +240,180 @@ function drawChart(cWidth: number, cHeight: number, pixelRatio: number) {
seriesData.push(value !== null && value !== undefined ? Number(value) : 0); seriesData.push(value !== null && value !== undefined ? Number(value) : 0);
}); });
// H5 环境,延迟获取 Canvas 确保 DOM 渲染完成 Taro.nextTick(() => {
setTimeout(() => { const env = Taro.getEnv();
// Taro 在 H5 下会在容器内部生成 <canvas> 标签
const canvasElement = document.querySelector('#fullscreen-chart canvas') as HTMLCanvasElement;
if (!canvasElement) { if (env === Taro.ENV_TYPE.WEB) {
console.error('❌ Canvas 元素未找到'); // H5 环境
console.log('尝试查找所有 canvas...'); setTimeout(() => {
const allCanvas = document.querySelectorAll('canvas'); const canvasElement = document.querySelector('#fullscreen-chart canvas') as HTMLCanvasElement;
console.log('页面中所有 canvas:', allCanvas); if (!canvasElement) return;
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' },
},
});
});
} }
});
console.log('✅ 找到 Canvas 元素:', canvasElement);
const ctx = canvasElement.getContext('2d');
if (!ctx) {
console.error('❌ 无法获取 Canvas Context');
return;
}
console.log('✅ 获取 Context 成功');
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: ['#1890FF'],
padding: [15, 30, 35, 45],
enableScroll: false,
boundaryGap: 'justify',
legend: {
show: false
},
dataLabel: false,
xAxis: {
disableGrid: true,
axisLine: true,
type: 'grid',
gridType: 'dash',
itemCount: 0,
scrollShow: false,
scrollAlign: 'left',
labelCount: 10,
rotateLabel: true,
rotateAngle: 25,
},
yAxis: {
disableGrid: false,
gridType: 'dash',
tofix: 2,
},
extra: {
line: {
type: 'curve',
width: 2,
activeType: 'hollow',
linearType: 'custom',
},
},
});
console.log('✅ 图表绘制完成');
}, 300);
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss">
.chart-fullscreen-page { .chart-fullscreen-page {
position: fixed;
top: 50%;
left: 50%;
width: 100vh; // 横屏后的宽度 = 屏幕高度
height: 100vw; // 横屏后的高度 = 屏幕宽度
transform: translate(-50%, -50%) rotate(90deg);
background: #fff; background: #fff;
display: flex;
flex-direction: column;
z-index: 9999;
} }
.header { .header {
height: 50px; height: 44px;
background: #fff; background: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
padding: 0 15px; padding: 0 15px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
flex-shrink: 0; flex-shrink: 0;
}
.back-btn {
width: 40px; .back-btn {
height: 40px; width: 44px;
display: flex; height: 44px;
align-items: center; display: flex;
justify-content: center; align-items: center;
justify-content: center;
.icon { }
font-size: 28px;
color: #333; .back-btn .icon {
} font-size: 24px;
} color: #333;
}
.title {
font-size: 28px; .title {
font-weight: 600; flex: 1;
color: #333; text-align: center;
} font-size: 16px;
font-weight: 600;
.home-btn { color: #333;
width: 40px; margin-right: 44px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
.home-icon {
font-size: 24px;
}
}
} }
.time-selector { .time-selector {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 10px; padding: 8px 15px;
gap: 12px;
background: #fff; background: #fff;
flex-shrink: 0; flex-shrink: 0;
height: 50px; height: 44px;
border-bottom: 1px solid #eee;
.time-item { }
padding: 8px 24px;
background: #f5f5f5; .time-item {
border-radius: 6px; padding: 6px 20px;
font-size: 24px; margin: 0 8px;
color: #666; background: #f5f5f5;
border-radius: 4px;
&.active { font-size: 14px;
background: #1890FF; color: #666;
color: #fff; }
}
} .time-item.active {
background: #09B8C2;
color: #fff;
} }
.chart-container { .chart-container {
flex: 1;
position: relative;
background: #fff; background: #fff;
padding: 10px; }
.chart-canvas { .chart-canvas {
width: 100%; display: block;
height: 100%;
}
} }
</style> </style>

View File

@@ -726,6 +726,11 @@ const props = defineProps({
required: true, required: true,
default: 1, default: 1,
}, },
// 公告弹窗是否打开
noticeOpen: {
type: Boolean,
default: false,
},
}); });
const pondDeviceRes = ref<any>(); const pondDeviceRes = ref<any>();
const emit = defineEmits(["getPondInfo", "getRes", "render", "addDevice"]); const emit = defineEmits(["getPondInfo", "getRes", "render", "addDevice"]);
@@ -1419,7 +1424,7 @@ function createCharts(x, series, ids) {
animation: true, animation: true,
background: "#FFFFFF", background: "#FFFFFF",
color: ["#17B4B2", "#5470c6", "#fac858"], color: ["#17B4B2", "#5470c6", "#fac858"],
padding: [15, 30, 35, 45], padding: [15, 50, 35, 15],
enableScroll: false, enableScroll: false,
boundaryGap: "justify", boundaryGap: "justify",
update: true, update: true,
@@ -1437,9 +1442,9 @@ function createCharts(x, series, ids) {
itemCount: 0, itemCount: 0,
scrollShow: false, scrollShow: false,
scrollAlign: "left", scrollAlign: "left",
labelCount: 7, labelCount: 6,
rotateLabel: true, rotateLabel: true,
rotateAngle: 30, rotateAngle: 45,
marginTop: 10, marginTop: 10,
}, },
yAxis: { yAxis: {

View File

@@ -307,6 +307,7 @@
@addDevice="addDevice" @addDevice="addDevice"
ref="pondChildInfo" ref="pondChildInfo"
type="1" type="1"
:noticeOpen="noticeOpen"
/> />
</nut-col> </nut-col>
</nut-row> </nut-row>

View File

@@ -12,8 +12,8 @@ const getBaseUrl = () => {
BASE_URL = 'http://127.0.0.1:8080' // 本地调试后端地址 BASE_URL = 'http://127.0.0.1:8080' // 本地调试后端地址
} else { } else {
// BASE_URL = 'https://api.yuceyun.cn' //填写你的请求域名 // BASE_URL = 'https://api.yuceyun.cn' //填写你的请求域名
// BASE_URL = 'https://www.qdintc.com/fishery-api' // 测试环境 BASE_URL = 'https://www.qdintc.com/fishery-api' // 测试环境
BASE_URL = 'http://127.0.0.1:8080' // 本地调试后端地址 // BASE_URL = 'http://127.0.0.1:8080' // 本地调试后端地址
// BASE_URL = 'http://47.102.210.182:6678' //测试环境 // BASE_URL = 'http://47.102.210.182:6678' //测试环境
} }
return BASE_URL return BASE_URL