Skip to content

组件:stats-row/统计数据行

字数
2683 字
阅读时间
13 分钟

MindGuard小程序的统计数据展示组件,支持多种数据格式的统一展示,包含趋势分析和交互功能,是数据可视化的核心组件。

位置与作用

出现页面

  • 今天Tab:情绪状态、任务完成度等今日数据展示
  • 个人中心:徽章数量、护心周报等统计数据
  • 周报详情:详细的健康数据分析
  • 徽章详情:成就进度和统计信息
  • 任务管理:任务完成情况和进度统计

承担任务

  • 展示各类统计数据和指标
  • 支持趋势分析和对比功能
  • 提供数据的可视化展示
  • 集成交互式数据探索功能
  • 支持多种数据格式的统一处理
  • 提供用户友好的数据解读

Props(属性)

名称类型必填默认说明
itemsArray[]统计数据项数组
layoutString'horizontal'布局方式:'horizontal' | 'vertical' | 'grid'
sizeString'medium'尺寸:'small' | 'medium' | 'large'
showTrendBooleantrue是否显示趋势指示器
clickableBooleanfalse是否可点击
borderBooleantrue是否显示边框
spacingNumber16间距大小(rpx)
themeString'light'主题:'light' | 'dark'

Slots(插槽)

插槽名用途说明
item单个数据项可自定义单个数据项的展示样式
label标签区域可自定义数据标签的显示
value数值区域可自定义数值的显示样式
trend趋势区域可自定义趋势指示器的显示
extra额外内容可添加额外的统计信息或操作

Events(事件)

事件名触发时机回调参数埋点映射
onItemClick用户点击数据项{type: 'itemClick', item: object, index: number}act_stats_item_click
onItemLongPress用户长按数据项{type: 'itemLongPress', item: object, index: number}act_stats_item_longpress
onTrendClick用户点击趋势指示器{type: 'trendClick', item: object, trend: string}act_stats_trend_click
onLoadMore用户点击加载更多{type: 'loadMore', currentItems: array}act_stats_loadmore
onRefresh用户点击刷新{type: 'refresh', timestamp: number}act_stats_refresh

Methods(外部可调)

方法名入参与返回使用示例
updateItems(items: array)入参:数据项数组;返回:voidthis.selectComponent('#stats').updateItems([...])
addItem(item: object)入参:数据项;返回:voidthis.selectComponent('#stats').addItem({...})
removeItem(index: number)入参:索引;返回:voidthis.selectComponent('#stats').removeItem(0)
refreshData()入参:无;返回:voidthis.selectComponent('#stats').refreshData()
setLoading(loading: boolean)入参:加载状态;返回:voidthis.selectComponent('#stats').setLoading(true)

Data Contract(数据契约)

输入字段

字段来源服务/集合说明
items[].label多个服务数据项标签文字
items[].value多个服务数据值(数字或字符串)
items[].unit多个服务数据单位
items[].trend多个服务趋势:'up' | 'down' | 'stable'
items[].change多个服务变化值
items[].color多个服务自定义颜色
items[].icon多个服务图标标识
items[].clickable多个服务是否可点击
items[].url多个服务跳转链接

输出/回写

字段目标集合/接口权限说明
user_events.event_typeuser_events集合统计数据交互事件记录
analytics_data.item_clicksanalytics集合数据项点击分析
user_behavior.data_exploreanalytics集合用户数据探索行为分析

视觉与交互(对齐样式规范)

尺寸与间距

  • 组件容器:宽度 100%,最小高度 120rpx
  • 数据项:最小宽度 160rpx(横向),高度 88rpx(纵向)
  • 标签文字:字体大小 24rpx,行高 32rpx
  • 数值文字:字体大小 32rpx,行高 44rpx
  • 趋势图标:24×24rpx,边距 8rpx
  • 点击区域:最小 88×88rpx

颜色(Token映射)

元素Token色值用途
组件背景--color-ink-0#F7F9FC组件主体背景
标签文字--color-ink-600#5A6672数据标签文字
数值文字--color-ink-900#2C3640主要数值文字
趋势上升--color-success-500#2AC28D上升趋势颜色
趋势下降--color-danger-500#DC2626下降趋势颜色
趋势稳定--color-warning-500#F59E0B稳定趋势颜色
边框颜色--color-ink-100#E9EDF3组件边框颜色
点击反馈--color-brand-50#EEF5FF点击反馈背景

状态变化

  • normal: 默认状态,正常显示统计数据
  • loading: 加载状态,显示加载动画
  • refreshing: 刷新状态,显示刷新动画
  • hover: 悬停状态,可点击项高亮效果
  • active: 激活状态,点击按下效果
  • disabled: 禁用状态,不可交互状态

可访问性

  • 对比度: 所有文本对比度 ≥ 4.5:1,确保清晰可读
  • 颜色区分: 除了颜色,还使用图标和文字区分趋势
  • 键盘导航: 支持Tab键导航和Enter键激活
  • 屏幕阅读器: 完整的ARIA标签,描述数据含义

性能与包体

性能优化

  • 虚拟列表: 大量数据时使用虚拟滚动技术
  • 懒加载: 图片和复杂图表按需加载
  • 防抖节流: 点击事件使用防抖,避免重复操作
  • 缓存机制: 数据缓存,减少重复请求

包体控制

  • 主包归属: 位于主包,为所有数据展示页面提供支持
  • 图标优化: 使用字体图标或内联SVG
  • 代码压缩: 组件代码已经过压缩和优化
  • 依赖控制: 最小化外部依赖,确保组件独立性

埋点(来自数据分析需求)

场景事件名参数
统计行曝光exp_stats_rowpagePath, itemCount, layout, dataType
数据项点击act_stats_item_clickitemLabel, itemValue, clickPosition, hasTrend
趋势指示点击act_stats_trend_clickitemLabel, trendType, changeValue
长按数据项act_stats_item_longpressitemLabel, itemValue, pressDuration
刷新数据act_stats_refreshpagePath, refreshType, itemCount

测试要点

单测

  • 数据格式测试: 验证不同数据格式的正确显示
  • 布局测试: 验证不同布局方式的正确排列
  • 事件测试: 验证各种用户交互事件的正确触发
  • 趋势测试: 验证趋势指示器的正确显示和计算

E2E测试

  • 数据加载: 完整的数据加载和展示流程
  • 交互探索: 用户点击和长按的探索行为
  • 刷新机制: 数据刷新和更新的正确性
  • 响应式测试: 不同屏幕尺寸的适配效果

边界测试

  • 空数据处理: 空数据或异常数据的处理
  • 大数据量: 大量数据的性能和显示效果
  • 快速操作: 快速点击和连续操作的响应
  • 异常恢复: 网络异常和数据错误的恢复

示例(WXML/WXSS 片段)

WXML 使用示例

xml
<!-- 基础使用 -->
<stats-row
  items="{{statsData}}"
  layout="horizontal"
  show-trend="{{true}}"
  clickable="{{true}}"
  bind:itemClick="onStatsItemClick"
/>

<!-- 完整配置 -->
<stats-row
  id="stats-row"
  items="{{healthStats}}"
  layout="grid"
  size="medium"
  show-trend="{{true}}"
  clickable="{{true}}"
  border="{{true}}"
  spacing="16"
  theme="light"
  bind:itemClick="onStatsItemClick"
  bind:trendClick="onTrendClick"
  bind:refresh="onRefresh"
>
  <!-- 自定义数据项 -->
  <view slot="item" class="custom-item" wx:for="{{healthStats}}" wx:key="label">
    <view class="custom-label">{{item.label}}</view>
    <view class="custom-value">{{item.value}}{{item.unit}}</view>
    <view class="custom-trend" wx:if="{{item.trend}}">
      <icon type="trend-{{item.trend}}" size="16" />
      <text>{{item.change}}</text>
    </view>
  </view>

  <!-- 自定义标签 -->
  <view slot="label" class="custom-label">
    <text>{{item.label}}</text>
    <icon type="info" size="12" wx:if="{{item.description}}" />
  </view>

  <!-- 自定义数值 -->
  <view slot="value" class="custom-value">
    <text class="number">{{item.value}}</text>
    <text class="unit">{{item.unit}}</text>
  </view>

  <!-- 自定义趋势 -->
  <view slot="trend" class="custom-trend">
    <view class="trend-icon {{item.trend}}">
      <icon type="arrow-{{item.trend}}" size="12" />
    </view>
    <text class="trend-value">{{item.change}}</text>
  </view>
</stats-row>

WXSS 样式示例

scss
/* 组件基础样式 */
.stats-row {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  background: var(--color-ink-0);
  border-radius: 16rpx;
  padding: 24rpx;
  box-sizing: border-box;
}

/* 横向布局 */
.stats-row.horizontal {
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
}

.stats-row.horizontal .stats-item {
  flex: 1;
  min-width: 160rpx;
  text-align: center;
}

/* 纵向布局 */
.stats-row.vertical {
  flex-direction: column;
  gap: 24rpx;
}

.stats-row.vertical .stats-item {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* 网格布局 */
.stats-row.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24rpx;
}

.stats-row.grid .stats-item {
  text-align: center;
}

/* 单个数据项 */
.stats-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 16rpx;
  border-radius: 12rpx;
  transition: all 0.3s ease;
  position: relative;
}

.stats-item.clickable {
  cursor: pointer;
}

.stats-item.clickable:hover {
  background: var(--color-brand-50);
  transform: translateY(-2rpx);
}

.stats-item.clickable:active {
  transform: translateY(0);
}

/* 数据标签 */
.stats-label {
  font-size: 24rpx;
  color: var(--color-ink-600);
  line-height: 32rpx;
  margin-bottom: 8rpx;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 数值区域 */
.stats-value {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 4rpx;
}

.stats-number {
  font-size: 32rpx;
  font-weight: 600;
  color: var(--color-ink-900);
  line-height: 44rpx;
}

.stats-unit {
  font-size: 20rpx;
  color: var(--color-ink-600);
  line-height: 28rpx;
}

/* 趋势指示器 */
.stats-trend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4rpx;
  margin-top: 8rpx;
  font-size: 20rpx;
  line-height: 28rpx;
}

.stats-trend.up {
  color: var(--color-success-500);
}

.stats-trend.down {
  color: var(--color-danger-500);
}

.stats-trend.stable {
  color: var(--color-warning-500);
}

.stats-trend-icon {
  width: 16rpx;
  height: 16rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stats-trend-icon.up {
  transform: rotate(-45deg);
}

.stats-trend-icon.down {
  transform: rotate(45deg);
}

/* 边框样式 */
.stats-row.with-border {
  border: 2rpx solid var(--color-ink-100);
}

.stats-row.with-border .stats-item:not(:last-child) {
  border-right: 2rpx solid var(--color-ink-100);
}

/* 尺寸变体 */
.stats-row.small .stats-label {
  font-size: 20rpx;
  line-height: 28rpx;
}

.stats-row.small .stats-number {
  font-size: 28rpx;
  line-height: 40rpx;
}

.stats-row.small .stats-unit {
  font-size: 18rpx;
  line-height: 24rpx;
}

.stats-row.small .stats-trend {
  font-size: 18rpx;
  line-height: 24rpx;
}

.stats-row.large .stats-label {
  font-size: 28rpx;
  line-height: 40rpx;
}

.stats-row.large .stats-number {
  font-size: 40rpx;
  line-height: 56rpx;
}

.stats-row.large .stats-unit {
  font-size: 24rpx;
  line-height: 32rpx;
}

.stats-row.large .stats-trend {
  font-size: 24rpx;
  line-height: 32rpx;
}

/* 加载状态 */
.stats-row.loading .stats-item {
  opacity: 0.6;
}

.stats-row.loading .stats-number {
  animation: loading-pulse 1.5s ease-in-out infinite;
}

@keyframes loading-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* 主题适配 */
.stats-row.dark {
  background: var(--color-ink-900);
  border-color: var(--color-ink-700);
}

.stats-row.dark .stats-label {
  color: var(--color-ink-300);
}

.stats-row.dark .stats-number {
  color: var(--color-ink-0);
}

.stats-row.dark .stats-unit {
  color: var(--color-ink-400);
}

.stats-row.dark .stats-item.clickable:hover {
  background: var(--color-ink-800);
}

/* 自定义样式覆盖 */
.stats-row .custom-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8rpx;
}

.stats-row .custom-label {
  display: flex;
  align-items: center;
  gap: 4rpx;
  font-weight: 500;
}

.stats-row .custom-value {
  display: flex;
  align-items: baseline;
  gap: 2rpx;
}

.stats-row .custom-value .number {
  font-weight: 700;
}

.stats-row .custom-trend {
  display: flex;
  align-items: center;
  gap: 4rpx;
  font-weight: 500;
}

.stats-row .custom-trend .trend-icon {
  width: 12rpx;
  height: 12rpx;
}

/* 响应式适配 */
@media (max-width: 750rpx) {
  .stats-row.grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 500rpx) {
  .stats-row.horizontal {
    flex-direction: column;
    gap: 16rpx;
  }

  .stats-row.grid {
    grid-template-columns: 1fr;
  }
}

版本与变更记录

版本历史

  • v1.0.0 (2025-09-22): 初始版本,支持基本统计数据显示
  • v1.1.0 (计划): 支持更多图表类型和交互功能
  • v1.2.0 (计划): 添加数据导出和分享功能
  • v2.0.0 (计划): 支持AI数据分析和智能推荐

变更说明

  • v1.0.0: 实现基础统计数据展示功能
  • v1.1.0: 增加图表类型和交互探索功能
  • v1.2.0: 添加数据导出、分享等高级功能
  • v2.0.0: 支持AI智能分析和个性化推荐

组件维护者: MindGuard前端团队 最后更新: 2025-09-22 相关文档: 数据可视化规范, 用户体验设计

贡献者

The avatar of contributor named as Cai Hongyu Cai Hongyu

文件历史

撰写