录音时长趋势图
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user