需求:
1.用unapp及ucharts生成曲势图
2.点击横屏按钮,实现类似bilibili的横屏效果。点击返回按钮,恢复横屏效果。不需要去手机设置开“自动旋转“
3.横屏后,要铺满整个屏幕
以下代码是用deepseek生成的,但真机横屏后,趋势图不能铺满全屏,还是保留了坚屏时的宽度。请帮忙调一下。
<template>
<view class="container">
<!-- 横屏模式 -->
<view v-if="isLandscape" class="landscape-view">
<qiun-data-charts
type="line"
:chartData="chartData"
:opts="landscapeOpts"
:canvas2d="true"
canvasId="landscapeChart"
@legendTap="handleLegendTap"
/>
<button class="exit-btn" @click="toggleOrientation">返回竖屏</button>
</view>
<!-- 竖屏模式 -->
<view v-else class="portrait-view">
<qiun-data-charts
type="line"
:chartData="chartData"
:opts="portraitOpts"
:canvas2d="true"
canvasId="portraitChart"
@legendTap="handleLegendTap"
/>
<view class="control-panel">
<button class="landscape-btn" @click="toggleOrientation">横屏查看</button>
<view class="filter-row">
<picker class="time-picker" :value="timeIndex" :range="timeOptions" @change="handleTimeChange">
<view class="picker-content">
{{ timeOptions[timeIndex] }}
<view class="arrow-down"></view>
</view>
</picker>
<input
class="custom-input"
v-model="customHours"
type="number"
placeholder="自定义小时"
@blur="handleCustomInput"
/>
<button @click="loadData">查询</button>
</view>
</view>
</view>
</view>
</template>
<script>
const STORAGE_KEY = 'chartLegendStatus';
export default {
data() {
return {
isLandscape: false,
timeIndex: 3,
timeOptions: ['1小时', '4小时', '8小时', '24小时', '自定义'],
customHours: '',
chartData: {},
colorPool: [
"#1890FF","#FF6B6B","#6BCB77","#F0B400",
"#9B59B6","#FF7F50","#1ABC9C","#E74C3C"
],
portraitOpts: {
fontSize: 11,
dataLabel: false,
animation: false,
legend: {
show: true,
position: "bottom",
clickable: true,
lineHeight: 30,
maxLine: 2,
itemGap: 15
},
xAxis: {
showLabel: false,
disabled: true,
axisLine: { show: false },
splitLine: { show: false },
labelStyle: {
color: 'transparent',
fontSize: 0
},
format: () => ''
},
yAxis: {
gridType: 'dash',
dashLength: 4
},
extra: {
line: {
type: 'curve',
width: 1.5,
activeType: 'none'
}
}
},
landscapeOpts: {
fontSize: 14,
dataLabel: false,
animation: false,
padding: [15, 60, 15, 15], // 调整padding适应横屏
pixelRatio: 2,
legend: {
show: true,
position: "right",
clickable: true,
lineHeight: 20,
maxLine: 10,
itemGap: 8,
margin: 5,
formatter: name => name.length > 6 ? name.substring(0, 6) + '...' : name // 缩短长图例
},
xAxis: {
disabled: false,
showLabel: true,
axisLine: { show: true },
splitLine: { show: true },
labelCount: 8,
labelStyle: {
color: '#666',
fontSize: 10,
rotate: 45 // x轴标签旋转45度防止重叠
}
},
yAxis: {
gridType: 'dash',
dashLength: 4,
splitNumber: 5,
axisLine: { show: true },
labelStyle: {
color: '#666',
fontSize: 10
}
},
extra: {
line: {
type: 'curve',
width: 1.5,
activeType: 'none',
addLine: true
}
}
}
};
},
computed: {
filterHours() {
const preset = [1,4,8,24][this.timeIndex];
return this.timeIndex === 4 ? Math.min(720, this.customHours || 0) : preset;
}
},
mounted() {
this.loadPersistedData();
this.loadData();
// #ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary');
// #endif
},
methods: {
toggleOrientation() {
this.isLandscape = !this.isLandscape;
// #ifdef APP-PLUS
setTimeout(() => {
plus.screen.lockOrientation(
this.isLandscape ? 'landscape-primary' : 'portrait-primary'
);
}, 100);
// #endif
this.$nextTick(() => {
this.chartData = JSON.parse(JSON.stringify(this.chartData));
});
},
loadPersistedData() {
const savedStatus = uni.getStorageSync(STORAGE_KEY);
if (savedStatus) {
this.chartData.series = savedStatus;
}
},
handleTimeChange(e) {
this.timeIndex = e.detail.value;
if (this.timeIndex !== 4) this.loadData();
},
handleCustomInput() {
if (this.customHours > 0) {
this.timeIndex = 4;
this.loadData();
}
},
async loadData() {
const hours = Math.max(1, Math.min(720, this.filterHours));
const mockData = await this.getMockData(hours);
this.chartData = {
categories: mockData.categories,
series: mockData.values.map((dataArr, index) => ({
name: `传感器${index + 1}`,
data: dataArr,
color: this.colorPool[index % 8],
show: this.getSeriesVisibility(index)
}))
};
},
handleLegendTap(e) {
if (!e?.detail?.index) return;
const index = e.detail.index;
const newSeries = this.chartData.series.map((item, i) =>
i === index ? {...item, show: !item.show} : item
);
uni.setStorageSync(STORAGE_KEY, newSeries);
this.chartData = {...this.chartData, series: newSeries};
},
getSeriesVisibility(index) {
const saved = uni.getStorageSync(STORAGE_KEY);
return (saved && saved[index]) ? saved[index].show : true;
},
generateTimeLabels(hours) {
return Array.from({length: hours}, (_,i) => {
const date = new Date(Date.now() - (hours - i - 1)*3600000);
return this.formatTime(date);
});
},
generateTrendData(length) {
let current = 50;
return Array.from({length}, () => {
current += Math.random() * 4 - 2;
return Math.min(Math.max(Math.round(current), 0), 100);
});
},
formatTime(date) {
const pad = n => n.toString().padStart(2,'0');
return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
},
getMockData(hours) {
return new Promise(resolve => {
setTimeout(() => {
resolve({
categories: this.generateTimeLabels(hours),
values: Array.from({length:8}, () => this.generateTrendData(hours))
});
}, 300);
});
}
}
};
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
}
/* 竖屏样式 */
.portrait-view {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.portrait-view .qiun-data-charts {
flex: 1;
width: 100% !important;
}
.control-panel {
padding: 15px;
background: #f8f8f8;
border-top: 1px solid #eee;
}
.filter-row {
display: flex;
align-items: center;
gap: 10px;
margin-top: 15px;
}
.time-picker {
border: 1px solid #dcdfe6;
border-radius: 8rpx;
padding: 10px 15px;
flex: 1;
}
.picker-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.arrow-down {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 8px solid #999;
margin-left: 8px;
}
.custom-input {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 8rpx;
padding: 10px;
}
button {
background-color: #1890ff;
color: white;
border: none;
border-radius: 5px;
padding: 10px 15px;
font-size: 14px;
}
.landscape-btn {
background-color: #1abc9c;
width: 100%;
}
/* 横屏样式 */
.landscape-view {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: white;
z-index: 1000;
display: flex;
flex-direction: column;
}
.landscape-view .qiun-data-charts {
flex: 1;
width: 100% !important;
height: 100% !important;
}
.exit-btn {
position: absolute;
bottom: 20px;
right: 20px;
height: 40px;
min-width: 90px;
padding: 0 15px;
background-color: #ff4d4f;
color: white;
border-radius: 20px;
z-index: 1001;
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
font-size: 14px;
}
/* 强制图表尺寸 */
:deep(.qiun-data-charts),
:deep(.qiun-charts),
:deep(canvas) {
width: 100% !important;
height: 100% !important;
display: block !important;
}
</style>