录音时长趋势图

This commit is contained in:
zhonghua.li
2026-02-07 22:44:11 +08:00
parent bf51c2afc4
commit 705ee2dff7
5 changed files with 328 additions and 51 deletions

View File

@@ -55,9 +55,13 @@
<!-- 图表区域 -->
<view class="chart-section">
<text class="section-title">时长趋势图</text>
<view class="chart-placeholder">
<text class="placeholder-text">📊 图表区域</text>
<text class="placeholder-desc">此处将显示录音时长趋势图表</text>
<view class="chart-container">
<canvas
canvas-id="trendChart"
id="trendChart"
class="chart-canvas"
:style="{ width: chartWidth + 'px', height: chartHeight + 'px' }"
></canvas>
</view>
</view>
@@ -140,12 +144,17 @@ export default {
totalDuration: '0 分钟',
recordingCount: '0',
dataList: [],
loading: false
loading: false,
chartWidth: 0,
chartHeight: 300,
chartData: []
}
},
onLoad() {
// 页面加载时获取数据
this.loadData();
// 初始化图表尺寸
this.initChartSize();
},
onReady() {
// 页面渲染完成后,查询实际导航栏高度并更新位置
@@ -378,11 +387,11 @@ export default {
this.processStatisticsData(statisticsList);
// 刷新成功后显示提示
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
} else {
throw new Error('请求失败,状态码: ' + res.statusCode);
@@ -469,6 +478,165 @@ export default {
dataListCount: this.dataList.length,
statisticsListLength: statisticsList.length
});
// 准备图表数据并绘制
this.prepareChartData(statisticsList);
},
/**
* 初始化图表尺寸
*/
initChartSize() {
// 获取屏幕宽度
const systemInfo = uni.getSystemInfoSync();
const screenWidth = systemInfo.windowWidth || 375;
// 图表宽度 = 屏幕宽度 - 左右边距(60rpx = 30px) - 卡片内边距(60rpx = 30px)
this.chartWidth = screenWidth - 60;
},
/**
* 准备图表数据
*/
prepareChartData(statisticsList) {
if (!statisticsList || statisticsList.length === 0) {
this.chartData = [];
return;
}
// 按日期正序排列(从旧到新,用于图表显示)
const sortedList = [...statisticsList].sort((a, b) => {
const dateA = new Date(a.statisticsDate);
const dateB = new Date(b.statisticsDate);
return dateA - dateB;
});
// 提取日期和时长数据
this.chartData = sortedList.map(item => ({
date: item.statisticsDate,
duration: item.salesTotalDuration || 0
}));
// 延迟绘制确保canvas已渲染
this.$nextTick(() => {
setTimeout(() => {
this.drawChart();
}, 100);
});
},
/**
* 绘制趋势图
*/
drawChart() {
if (!this.chartData || this.chartData.length === 0) {
return;
}
const ctx = uni.createCanvasContext('trendChart', this);
const width = this.chartWidth;
const height = this.chartHeight;
// 图表边距
const padding = {
top: 40,
right: 20,
bottom: 50,
left: 50
};
// 绘图区域
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
// 清空画布
ctx.clearRect(0, 0, width, height);
// 计算数据范围
const durations = this.chartData.map(item => item.duration);
const maxDuration = Math.max(...durations, 1);
const minDuration = Math.min(...durations, 0);
const range = maxDuration - minDuration || 1;
// 计算Y轴刻度向上取整到合适的值
const yMax = Math.ceil(maxDuration / 100) * 100;
const yStep = yMax / 5;
// 绘制Y轴刻度和标签
ctx.setFillStyle('#666666');
ctx.setFontSize(10);
for (let i = 0; i <= 5; i++) {
const y = padding.top + chartHeight - (chartHeight / 5) * i;
const value = Math.round(yStep * i);
ctx.fillText(value.toString(), 5, y + 4);
// 绘制网格线
ctx.setStrokeStyle('#e0e0e0');
ctx.setLineWidth(1);
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(padding.left + chartWidth, y);
ctx.stroke();
}
// 绘制X轴日期标签
const dataCount = this.chartData.length;
const xStep = chartWidth / Math.max(dataCount - 1, 1);
this.chartData.forEach((item, index) => {
const x = padding.left + xStep * index;
const date = new Date(item.date);
const month = date.getMonth() + 1;
const day = date.getDate();
const label = `${month}/${day}`;
ctx.setFillStyle('#666666');
ctx.setFontSize(10);
ctx.fillText(label, x - 15, height - padding.bottom + 20);
});
// 绘制折线
ctx.setStrokeStyle('#007aff');
ctx.setLineWidth(2);
ctx.beginPath();
this.chartData.forEach((item, index) => {
const x = padding.left + xStep * index;
const y = padding.top + chartHeight - ((item.duration / yMax) * chartHeight);
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// 绘制数据点
ctx.setFillStyle('#007aff');
this.chartData.forEach((item, index) => {
const x = padding.left + xStep * index;
const y = padding.top + chartHeight - ((item.duration / yMax) * chartHeight);
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.fill();
});
// 绘制坐标轴
ctx.setStrokeStyle('#333333');
ctx.setLineWidth(1);
// Y轴
ctx.beginPath();
ctx.moveTo(padding.left, padding.top);
ctx.lineTo(padding.left, padding.top + chartHeight);
ctx.stroke();
// X轴
ctx.beginPath();
ctx.moveTo(padding.left, padding.top + chartHeight);
ctx.lineTo(padding.left + chartWidth, padding.top + chartHeight);
ctx.stroke();
ctx.draw();
}
}
}
@@ -694,21 +862,15 @@ export default {
border-bottom: 1rpx solid #e9ecef;
}
.chart-placeholder {
padding: 80rpx 30rpx;
text-align: center;
.chart-container {
padding: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
.placeholder-text {
.chart-canvas {
display: block;
font-size: 48rpx;
margin-bottom: 20rpx;
}
.placeholder-desc {
display: block;
font-size: 28rpx;
color: #666666;
}
.list-item {