Files
smartDriveEEUniApp/pages-subpackage/ai_features/customer_count/customer_count.vue
2026-02-08 19:13:07 +08:00

920 lines
26 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="feature-page">
<view class="page-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-bar">
<view class="nav-left" @click="goBack">
<text class="back-icon"></text>
</view>
<text class="nav-title">接待客户数统计</text>
</view>
</view>
<scroll-view class="content-scroll" scroll-y="true" :style="{ top: contentTop, bottom: contentBottom }">
<!-- 搜索区域 -->
<view class="search-toolbar">
<view class="toolbar-picker-wrapper" @click="toggleTimeRangeDropdown">
<view class="toolbar-picker-view">
<text class="toolbar-picker-text">{{ timeRangeOptions[timeRangeIndex] }}</text>
<uni-icons :type="showTimeRangeDropdown ? 'top' : 'bottom'" size="14" color="#999"></uni-icons>
</view>
<!-- 自定义下拉菜单 -->
<view class="time-range-dropdown" v-if="showTimeRangeDropdown">
<view
class="dropdown-item"
v-for="(option, index) in timeRangeOptions"
:key="index"
:class="{ 'dropdown-item-active': timeRangeIndex === index }"
@click.stop="selectTimeRange(index)">
<text>{{ option }}</text>
</view>
</view>
</view>
<view class="toolbar-actions">
<view class="toolbar-btn toolbar-btn--refresh" @click="onRefresh">
<uni-icons type="refresh" size="18" color="#2A68FF"></uni-icons>
<text>刷新</text>
</view>
</view>
</view>
<!-- 遮罩层点击关闭下拉菜单 -->
<view class="dropdown-mask" v-if="showTimeRangeDropdown" @click="closeTimeRangeDropdown"></view>
<!-- 统计卡片 -->
<view class="stats-card">
<view class="stat-item">
<text class="stat-value">{{ totalCustomers }}</text>
<text class="stat-label">累计接待客户</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ todayCustomers }}</text>
<text class="stat-label">今日接待客户</text>
</view>
</view>
<!-- 图表区域 -->
<view class="chart-section">
<text class="section-title">客户数趋势图</text>
<view class="chart-container">
<canvas
canvas-id="trendChart"
id="trendChart"
class="chart-canvas"
:style="{ width: chartWidth + 'px', height: chartHeight + 'px' }"
></canvas>
</view>
</view>
<!-- 数据列表 -->
<view class="data-list">
<text class="section-title">详细数据</text>
<view class="list-item" v-for="(item, index) in dataList" :key="index">
<view class="item-left">
<text class="item-date">{{ item.date }}</text>
<text class="item-desc">{{ item.description }}</text>
</view>
<view class="item-right">
<text class="item-count">{{ item.count }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import { post } from '@/common/request.js'
import { getApiUrl } from '@/common/config.js'
// 获取系统信息的辅助函数
function getSystemInfo() {
// #ifdef MP-WEIXIN
// 微信小程序:优先使用新的 API
if (typeof wx !== 'undefined' && wx.getWindowInfo) {
try {
const windowInfo = wx.getWindowInfo();
return {
statusBarHeight: windowInfo.statusBarHeight || 20,
windowWidth: windowInfo.windowWidth,
windowHeight: windowInfo.windowHeight
};
} catch (e) {
console.warn('[CustomerCount] Failed to use wx.getWindowInfo, fallback to getSystemInfoSync:', e);
}
}
// #endif
// 回退到旧的 API 或使用 uni-app 的 API
try {
const systemInfo = uni.getSystemInfoSync();
return {
statusBarHeight: systemInfo.statusBarHeight || 20,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight
};
} catch (e) {
console.error('[CustomerCount] Failed to get system info:', e);
return {
statusBarHeight: 20,
windowWidth: 375,
windowHeight: 667
};
}
}
export default {
data() {
// 动态计算导航栏高度
const systemInfo = getSystemInfo();
const statusBarHeight = systemInfo.statusBarHeight;
const navbarHeight = 44; // navbar高度44px
// 转换为rpx1px = 2rpx在375px设计稿下
const statusBarHeightRpx = statusBarHeight * 2;
const navbarHeightRpx = navbarHeight * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
return {
statusBarHeight: statusBarHeight, // 状态栏高度px
contentTop: totalNavbarHeight + 'rpx', // 内容区域距离顶部的距离
contentBottom: '0rpx', // 内容区域距离底部的距离(默认值,会在 onReady 中更新)
timeRangeOptions: ['1个月', '3个月', '6个月'],
timeRangeIndex: 0, // 默认选择1个月
showTimeRangeDropdown: false, // 控制下拉菜单显示
totalCustomers: '0',
todayCustomers: '0',
dataList: [],
loading: false,
chartWidth: 0,
chartHeight: 300,
chartData: []
}
},
onLoad() {
// 页面加载时获取数据
this.loadData();
// 初始化图表尺寸
this.initChartSize();
},
onReady() {
// 页面渲染完成后,查询实际导航栏高度并更新位置
this.$nextTick(() => {
// 查询导航栏实际高度
const query = uni.createSelectorQuery().in(this);
query.select('.nav-bar').boundingClientRect((data) => {
if (data && data.height) {
// 使用实际高度
const actualHeight = data.height; // 实际导航栏高度px
// 转换为rpx1px = 2rpx在375px设计稿下
const actualHeightRpx = actualHeight * 2;
const statusBarHeightRpx = this.statusBarHeight * 2;
const totalHeight = statusBarHeightRpx + actualHeightRpx;
// 稍微减小一点,确保紧贴
const adjustedHeight = Math.max(totalHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
console.log('[CustomerCount] 使用实际导航栏高度:', actualHeight, 'px =', actualHeightRpx, 'rpx, 总高度:', totalHeight, 'rpx, 调整后:', adjustedHeight, 'rpx');
} else {
// 如果查询失败,使用默认计算
const systemInfo = getSystemInfo();
const statusBarHeight = systemInfo.statusBarHeight;
const navbarHeight = 44;
const statusBarHeightRpx = statusBarHeight * 2;
const navbarHeightRpx = navbarHeight * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
const adjustedHeight = Math.max(totalNavbarHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
}
}).exec();
// 查询底部 tabbar 实际高度(如果有)
const queryTabbar = uni.createSelectorQuery().in(this);
queryTabbar.select('.tabbar').boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
// 使用实际高度
const tabbarHeight = tabbarData.height; // tabbar 实际高度px
const tabbarHeightRpx = tabbarHeight * 2;
// 稍微减小一点,确保紧贴
const adjustedBottom = Math.max(tabbarHeightRpx - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
console.log('[CustomerCount] 使用实际tabbar高度:', tabbarHeight, 'px =', tabbarHeightRpx, 'rpx, 调整后:', adjustedBottom, 'rpx');
} else {
// 如果查询失败使用默认值无tabbar
this.$set(this, 'contentBottom', '0rpx');
}
}).exec();
// 延迟再次更新确保DOM已渲染
setTimeout(() => {
// 再次查询并更新导航栏
const query2 = uni.createSelectorQuery().in(this);
query2.select('.nav-bar').boundingClientRect((data) => {
if (data && data.height) {
const actualHeight = data.height;
const actualHeightRpx = actualHeight * 2;
const statusBarHeightRpx = this.statusBarHeight * 2;
const totalHeight = statusBarHeightRpx + actualHeightRpx;
const adjustedHeight = Math.max(totalHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
console.log('[CustomerCount] 延迟更新导航栏高度:', actualHeight, 'px =', actualHeightRpx, 'rpx, 总高度:', totalHeight, 'rpx, 调整后:', adjustedHeight, 'rpx');
}
}).exec();
// 再次查询 tabbar 高度
const queryTabbar2 = uni.createSelectorQuery().in(this);
queryTabbar2.select('.tabbar').boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
const tabbarHeight = tabbarData.height;
const tabbarHeightRpx = tabbarHeight * 2;
const adjustedBottom = Math.max(tabbarHeightRpx - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
console.log('[CustomerCount] 延迟更新tabbar高度:', tabbarHeight, 'px =', tabbarHeightRpx, 'rpx, 调整后:', adjustedBottom, 'rpx');
}
}).exec();
}, 200);
});
},
methods: {
goBack() {
uni.navigateBack()
},
toggleTimeRangeDropdown() {
this.showTimeRangeDropdown = !this.showTimeRangeDropdown;
},
closeTimeRangeDropdown() {
this.showTimeRangeDropdown = false;
},
selectTimeRange(index) {
this.timeRangeIndex = index;
this.showTimeRangeDropdown = false;
// 根据选择的时间范围重新加载数据
this.loadData();
},
onRefresh() {
// 刷新数据
this.loadData();
},
/**
* 获取当前登录用户信息
*/
getCurrentUserInfo() {
try {
const loginResponse = uni.getStorageSync('backend-login-response') || {};
return {
salesId: loginResponse.userId ? String(loginResponse.userId) : null,
salesPhone: loginResponse.phone || null
};
} catch (e) {
console.error('获取用户信息失败:', e);
return { salesId: null, salesPhone: null };
}
},
/**
* 格式化客户数
*/
formatCustomerCount(count) {
if (!count || count === 0) return '0';
return String(count);
},
/**
* 格式化日期
*/
formatDate(dateStr) {
if (!dateStr) return '';
try {
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
} catch (e) {
return dateStr;
}
},
/**
* 加载统计数据
*/
async loadData() {
// 防止重复请求
if (this.loading) {
return;
}
this.loading = true;
try {
// 获取当前用户信息
const userInfo = this.getCurrentUserInfo();
const { salesId, salesPhone } = userInfo;
// 验证:至少需要提供一个查询条件
if (!salesId && !salesPhone) {
uni.showToast({
title: '无法获取用户信息',
icon: 'none',
duration: 2000
});
this.loading = false;
return;
}
// 根据 timeRangeIndex 确定 dateRangeType
// 0 -> 1个月(1), 1 -> 3个月(2), 2 -> 6个月(3)
const dateRangeType = this.timeRangeIndex + 1;
// 构建查询参数(后端使用 @RequestParam参数需要作为 URL 查询参数传递)
const queryParams = {
dateRangeType: dateRangeType
};
// 添加销售人员ID或电话号码至少提供一个
if (salesId) {
queryParams.salesId = salesId;
}
if (salesPhone) {
queryParams.salesPhone = salesPhone;
}
// 将参数转换为 URL 查询字符串
const queryString = Object.keys(queryParams)
.filter(key => queryParams[key] !== null && queryParams[key] !== undefined && queryParams[key] !== '')
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
.join('&');
// 构建完整URL使用接待客户数统计接口
// 注意如果后端接口不同需要修改此处的URL
const baseUrl = getApiUrl('/api/customer-statistics/sales');
const url = queryString ? `${baseUrl}?${queryString}` : baseUrl;
console.log('[CustomerCount] 请求参数:', queryParams);
console.log('[CustomerCount] 请求URL:', url);
// 显示加载提示
uni.showLoading({
title: '加载中...',
mask: true
});
// 发送POST请求参数已在URL的query string中data为空
const res = await post(url, {});
uni.hideLoading();
// 处理响应
if (res.statusCode === 200 && res.data) {
const result = res.data;
// 检查返回结果支持两种格式success 或 code
// code: 0 表示成功code: 200 也表示成功
const isSuccess = result.success === true || result.code === 0 || result.code === 200;
if (!isSuccess) {
const errorMsg = result.msg || result.message || '查询失败';
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
this.loading = false;
return;
}
// 获取统计数据列表
const statisticsList = result.data || [];
console.log('[CustomerCount] 返回数据:', statisticsList);
console.log('[CustomerCount] 返回结果:', result);
// 处理统计数据
this.processStatisticsData(statisticsList);
// 刷新成功后显示提示
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
} else {
throw new Error('请求失败,状态码: ' + res.statusCode);
}
} catch (error) {
uni.hideLoading();
console.error('[CustomerCount] 加载数据失败:', error);
uni.showToast({
title: error.message || '加载失败,请重试',
icon: 'none',
duration: 2000
});
} finally {
this.loading = false;
}
},
/**
* 处理统计数据
*/
processStatisticsData(statisticsList) {
if (!statisticsList || statisticsList.length === 0) {
// 没有数据时重置显示
this.totalCustomers = '0';
this.todayCustomers = '0';
this.dataList = [];
return;
}
// 注意salesTotalCustomerCount 是销售人员在所有门店的总客户数(所有记录的值都相同)
// 所以应该使用第一条记录的 salesTotalCustomerCount 作为总客户数,而不是累加
const firstItem = statisticsList[0];
const totalCustomerCount = firstItem.salesTotalCustomerCount || firstItem.salesCustomerCount || 0;
// 获取今日客户数(如果有今日数据)
const today = new Date();
const todayStr = this.formatDate(today.toISOString());
const todayItem = statisticsList.find(item => {
const itemDate = this.formatDate(item.statisticsDate);
return itemDate === todayStr;
});
const todayCustomerCount = todayItem ? (todayItem.salesCustomerCount || 0) : 0;
// 构建数据列表(展示每个门店的统计数据)
const formattedList = statisticsList.map(item => {
// 使用销售人员接待客户数作为该条记录的客户数
const customerCount = item.salesCustomerCount || 0;
// 格式化日期
const date = this.formatDate(item.statisticsDate);
// 构建描述信息(显示门店名称和日期)
let description = '';
if (item.dealershipName) {
description = `${item.dealershipName}`;
} else if (item.salesName) {
description = `${item.salesName}的接待`;
} else {
description = '客户接待统计';
}
return {
date: date,
description: description,
count: `${this.formatCustomerCount(customerCount)}`,
// 保存原始数据,方便后续使用
rawData: item
};
});
// 按日期倒序排列(最新的在前)
formattedList.sort((a, b) => {
if (a.date > b.date) return -1;
if (a.date < b.date) return 1;
return 0;
});
// 更新页面数据
this.totalCustomers = this.formatCustomerCount(totalCustomerCount);
this.todayCustomers = this.formatCustomerCount(todayCustomerCount);
this.dataList = formattedList;
console.log('[CustomerCount] 数据处理完成:', {
totalCustomers: this.totalCustomers,
todayCustomers: this.todayCustomers,
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,
count: item.salesCustomerCount || 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 counts = this.chartData.map(item => item.count);
const maxCount = Math.max(...counts, 1);
const minCount = Math.min(...counts, 0);
const range = maxCount - minCount || 1;
// 计算Y轴刻度向上取整到合适的值
const yMax = Math.ceil(maxCount / 10) * 10;
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.count / 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.count / 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();
}
}
}
</script>
<style scoped>
.feature-page {
min-height: 100vh;
background: #f8f9fa;
}
.page-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
background: #ffffff;
border-bottom: 1rpx solid #e9ecef;
}
.nav-bar {
display: flex;
align-items: center;
height: 88rpx;
padding: 0 30rpx;
}
.nav-left {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.back-icon {
font-size: 36rpx;
color: #333333;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.content-scroll {
position: absolute;
left: 0;
right: 0;
top: 0; /* 先从顶部开始,通过内联样式动态设置 */
bottom: 0; /* 先从底部开始,通过内联样式动态设置 */
/* top和bottom值通过内联样式动态设置覆盖上面的默认值 */
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box;
}
.search-toolbar {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 8rpx;
padding: 12rpx 30rpx;
margin: 20rpx 30rpx;
background-color: #F8F8FA;
border-radius: 8rpx;
border: 1rpx solid #EFEFF2;
}
.toolbar-picker-wrapper {
position: relative;
flex: 1;
min-width: 120rpx;
max-width: 200rpx;
flex-shrink: 1;
z-index: 100;
}
.toolbar-picker-view {
height: 56rpx;
line-height: 56rpx;
background-color: #F5F6FA;
border-radius: 8rpx;
padding: 0 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
.toolbar-picker-text {
font-size: 24rpx;
color: #333;
flex: 1;
}
.time-range-dropdown {
position: absolute;
top: calc(100% + 8rpx);
left: 0;
right: 0;
background-color: #FFFFFF;
border-radius: 8rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.12);
z-index: 101;
overflow: hidden;
border: 1rpx solid #EFEFF2;
}
.dropdown-item {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #F3F4F6;
transition: background-color 0.2s ease;
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-item:active {
background-color: #F9FAFB;
}
.dropdown-item text {
font-size: 24rpx;
color: #333;
}
.dropdown-item-active {
background-color: #F0F7FF;
}
.dropdown-item-active text {
color: #2A68FF;
font-weight: 500;
}
.dropdown-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 99;
}
.toolbar-actions {
display: flex;
align-items: center;
margin-left: auto;
flex-shrink: 0;
}
.toolbar-btn {
padding: 0 12rpx;
height: 56rpx;
min-width: 56rpx;
color: #2A68FF;
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
font-size: 24rpx;
font-weight: 400;
}
.toolbar-btn text {
color: #2A68FF;
}
.stats-card {
display: flex;
margin: 20rpx 30rpx;
background: #ffffff;
border-radius: 16rpx;
padding: 40rpx 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-item:not(:last-child) {
border-right: 1rpx solid #e9ecef;
}
.stat-value {
display: block;
font-size: 48rpx;
font-weight: bold;
color: #007aff;
margin-bottom: 10rpx;
}
.stat-label {
display: block;
font-size: 24rpx;
color: #666666;
}
.chart-section, .data-list {
margin: 20rpx 30rpx;
background: #ffffff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.section-title {
display: block;
font-size: 32rpx;
font-weight: bold;
color: #333333;
padding: 30rpx;
border-bottom: 1rpx solid #e9ecef;
}
.chart-container {
padding: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
.chart-canvas {
display: block;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-item:last-child {
border-bottom: none;
}
.item-left {
flex: 1;
}
.item-date {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #333333;
margin-bottom: 8rpx;
}
.item-desc {
display: block;
font-size: 24rpx;
color: #666666;
}
.item-right {
text-align: right;
}
.item-count {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #007aff;
}
</style>