feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"navigationBarTitleText": "服务记录",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {
|
||||
"ai-float-button": "/components/ai-float-button/ai-float-button",
|
||||
"dev-fab": "/components/dev-fab/dev-fab",
|
||||
"service-record-card": "/components/service-record-card/service-record-card",
|
||||
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-empty": "tdesign-miniprogram/empty/empty"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
import { mockCustomerDetail, mockCustomers } from '../../utils/mock-data'
|
||||
import type { ConsumptionRecord } from '../../utils/mock-data'
|
||||
import { sortByTimestamp } from '../../utils/sort'
|
||||
|
||||
/** 服务记录(对齐 task-detail ServiceRecord 结构,复用 service-record-card 组件) */
|
||||
interface ServiceRecord extends ConsumptionRecord {
|
||||
/** 台桌号,如 "A12号台" */
|
||||
table: string
|
||||
/** 课程类型标签,如 "基础课" */
|
||||
type: string
|
||||
/** 课程样式 class 后缀:basic / vip / tip / recharge */
|
||||
typeClass: 'basic' | 'vip' | 'tip' | 'recharge'
|
||||
/** 卡片类型:course=普通课,recharge=充值提成 */
|
||||
recordType: 'course' | 'recharge'
|
||||
/** 折算后小时数(原始数字,组件负责加 h 后缀) */
|
||||
duration: number
|
||||
/** 折算前小时数(原始数字,组件负责加 h 后缀) */
|
||||
durationRaw: number
|
||||
/** 到手金额(原始数字,组件负责加 ¥ 前缀) */
|
||||
income: number
|
||||
/** 是否预估金额 */
|
||||
isEstimate: boolean
|
||||
/** 商品/饮品描述 */
|
||||
drinks: string
|
||||
/** 显示用日期,如 "2月5日 15:00 - 17:00" */
|
||||
date: string
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
/** 页面状态 */
|
||||
pageState: 'loading' as 'loading' | 'empty' | 'error' | 'normal',
|
||||
/** 客户 ID */
|
||||
customerId: '',
|
||||
/** 客户名 */
|
||||
customerName: '',
|
||||
/** 客户名首字 */
|
||||
customerInitial: '',
|
||||
/** 客户电话(脱敏) */
|
||||
customerPhone: '139****5678',
|
||||
/** 客户电话(完整,查看后显示) */
|
||||
customerPhoneFull: '13900005678',
|
||||
/** 手机号是否已展开 */
|
||||
phoneVisible: false,
|
||||
/** 累计服务次数 */
|
||||
totalServiceCount: 0,
|
||||
/** 关系指数 */
|
||||
relationIndex: '0.85',
|
||||
/** 当前月份标签 */
|
||||
monthLabel: '',
|
||||
/** 当前年 */
|
||||
currentYear: 2026,
|
||||
/** 当前月 */
|
||||
currentMonth: 2,
|
||||
/** 最小年月(数据起始) */
|
||||
minYearMonth: 202601,
|
||||
/** 最大年月(当前月) */
|
||||
maxYearMonth: 202602,
|
||||
/** 是否可切换上月 */
|
||||
canPrev: true,
|
||||
/** 是否可切换下月 */
|
||||
canNext: false,
|
||||
/** 月度统计 */
|
||||
monthCount: '6次',
|
||||
monthHours: '11.5h',
|
||||
monthRelation: '0.85',
|
||||
/** 当前月的服务记录 */
|
||||
records: [] as ServiceRecord[],
|
||||
/** 所有记录(原始) */
|
||||
allRecords: [] as ConsumptionRecord[],
|
||||
/** 是否还有更多 */
|
||||
hasMore: false,
|
||||
/** 加载更多中 */
|
||||
loadingMore: false,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const id = options?.customerId || options?.id || ''
|
||||
this.setData({ customerId: id })
|
||||
this.loadData(id)
|
||||
},
|
||||
|
||||
/** 加载数据 */
|
||||
loadData(id: string) {
|
||||
this.setData({ pageState: 'loading' })
|
||||
|
||||
setTimeout(() => {
|
||||
// TODO: 替换为真实 API 调用
|
||||
const customer = mockCustomers.find((c) => c.id === id)
|
||||
const detail = customer
|
||||
? { ...mockCustomerDetail, id: customer.id, name: customer.name }
|
||||
: mockCustomerDetail
|
||||
|
||||
const allRecords = sortByTimestamp(detail.consumptionRecords || [], 'date') as ConsumptionRecord[]
|
||||
const name = detail.name || '客户'
|
||||
|
||||
this.setData({
|
||||
customerName: name,
|
||||
customerInitial: name[0] || '?',
|
||||
allRecords,
|
||||
totalServiceCount: allRecords.length,
|
||||
})
|
||||
|
||||
this.updateMonthView()
|
||||
}, 400)
|
||||
},
|
||||
|
||||
/** 根据当前月份筛选并更新视图 */
|
||||
updateMonthView() {
|
||||
const { currentYear, currentMonth, allRecords } = this.data
|
||||
const monthLabel = `${currentYear}年${currentMonth}月`
|
||||
|
||||
// 筛选当月记录
|
||||
const monthPrefix = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
|
||||
const monthRecords = allRecords.filter((r) => r.date.startsWith(monthPrefix))
|
||||
|
||||
// 转换为展示格式(对齐 task-detail ServiceRecord,复用 service-record-card 组件)
|
||||
// income / duration 均传原始数字,由组件统一加 ¥ 和 h
|
||||
const records: ServiceRecord[] = monthRecords.map((r) => {
|
||||
const d = new Date(r.date)
|
||||
const month = d.getMonth() + 1
|
||||
const day = d.getDate()
|
||||
const dateLabel = `${month}月${day}日`
|
||||
const timeRange = this.generateTimeRange(r.duration)
|
||||
const isRecharge = r.project.includes('充值')
|
||||
return {
|
||||
...r,
|
||||
table: this.getTableNo(r.id),
|
||||
type: this.getTypeLabel(r.project),
|
||||
typeClass: this.getTypeClass(r.project) as 'basic' | 'vip' | 'tip' | 'recharge',
|
||||
recordType: (isRecharge ? 'recharge' : 'course') as 'course' | 'recharge',
|
||||
duration: isRecharge ? 0 : parseFloat((r.duration / 60).toFixed(1)),
|
||||
durationRaw: 0,
|
||||
income: r.amount,
|
||||
isEstimate: false,
|
||||
drinks: '',
|
||||
date: isRecharge ? dateLabel : `${dateLabel} ${timeRange}`,
|
||||
}
|
||||
})
|
||||
|
||||
// 月度统计
|
||||
const totalMinutes = monthRecords.reduce((sum, r) => sum + r.duration, 0)
|
||||
const monthCount = monthRecords.length + '次'
|
||||
const monthHours = (totalMinutes / 60).toFixed(1) + 'h'
|
||||
|
||||
// 边界判断
|
||||
const yearMonth = currentYear * 100 + currentMonth
|
||||
const canPrev = yearMonth > this.data.minYearMonth
|
||||
const canNext = yearMonth < this.data.maxYearMonth
|
||||
|
||||
const isEmpty = records.length === 0 && allRecords.length === 0
|
||||
|
||||
this.setData({
|
||||
monthLabel,
|
||||
records,
|
||||
monthCount,
|
||||
monthHours,
|
||||
canPrev,
|
||||
canNext,
|
||||
pageState: isEmpty ? 'empty' : 'normal',
|
||||
})
|
||||
},
|
||||
|
||||
/** 生成模拟时间段 */
|
||||
generateTimeRange(durationMin: number): string {
|
||||
const startHour = 14 + Math.floor(Math.random() * 6)
|
||||
const endMin = startHour * 60 + durationMin
|
||||
const endHour = Math.floor(endMin / 60)
|
||||
const endMinute = endMin % 60
|
||||
return `${startHour}:00 - ${endHour}:${String(endMinute).padStart(2, '0')}`
|
||||
},
|
||||
|
||||
/** 课程类型标签 */
|
||||
getTypeLabel(project: string): string {
|
||||
if (project.includes('小组')) return '小组课'
|
||||
if (project.includes('1v1')) return '基础课'
|
||||
if (project.includes('充值')) return '充值'
|
||||
if (project.includes('斯诺克')) return '斯诺克'
|
||||
return '基础课'
|
||||
},
|
||||
|
||||
/** 课程类型样式(对齐 service-record-card typeClass prop)*/
|
||||
getTypeClass(project: string): string {
|
||||
if (project.includes('充值')) return 'recharge'
|
||||
if (project.includes('小组')) return 'vip'
|
||||
if (project.includes('斯诺克')) return 'vip'
|
||||
return 'basic'
|
||||
},
|
||||
|
||||
/** 模拟台号 */
|
||||
getTableNo(id: string): string {
|
||||
const tables = ['A12号台', '3号台', 'VIP1号房', '5号台', 'VIP2号房', '8号台']
|
||||
const idx = parseInt(id.replace(/\D/g, '') || '0', 10) % tables.length
|
||||
return tables[idx]
|
||||
},
|
||||
|
||||
/** 切换到上一月 */
|
||||
onPrevMonth() {
|
||||
if (!this.data.canPrev) return
|
||||
let { currentYear, currentMonth } = this.data
|
||||
currentMonth--
|
||||
if (currentMonth < 1) {
|
||||
currentMonth = 12
|
||||
currentYear--
|
||||
}
|
||||
this.setData({ currentYear, currentMonth })
|
||||
this.updateMonthView()
|
||||
},
|
||||
|
||||
/** 切换到下一月 */
|
||||
onNextMonth() {
|
||||
if (!this.data.canNext) return
|
||||
let { currentYear, currentMonth } = this.data
|
||||
currentMonth++
|
||||
if (currentMonth > 12) {
|
||||
currentMonth = 1
|
||||
currentYear++
|
||||
}
|
||||
this.setData({ currentYear, currentMonth })
|
||||
this.updateMonthView()
|
||||
},
|
||||
|
||||
/** 下拉刷新 */
|
||||
onPullDownRefresh() {
|
||||
this.loadData(this.data.customerId)
|
||||
setTimeout(() => wx.stopPullDownRefresh(), 600)
|
||||
},
|
||||
|
||||
/** 重试 */
|
||||
onRetry() {
|
||||
this.loadData(this.data.customerId)
|
||||
},
|
||||
|
||||
/** 触底加载 */
|
||||
onReachBottom() {
|
||||
// Mock 阶段数据有限,不做分页
|
||||
if (this.data.loadingMore || !this.data.hasMore) return
|
||||
this.setData({ loadingMore: true })
|
||||
setTimeout(() => {
|
||||
this.setData({ loadingMore: false, hasMore: false })
|
||||
}, 500)
|
||||
},
|
||||
|
||||
/** 查看/隐藏手机号 */
|
||||
onTogglePhone() {
|
||||
this.setData({ phoneVisible: !this.data.phoneVisible })
|
||||
},
|
||||
|
||||
/** 复制手机号 */
|
||||
onCopyPhone() {
|
||||
const phone = this.data.customerPhoneFull
|
||||
wx.setClipboardData({
|
||||
data: phone,
|
||||
success: () => {
|
||||
wx.showToast({ title: '手机号码已复制', icon: 'none' })
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/** 返回上一页 */
|
||||
onBack() {
|
||||
wx.navigateBack()
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,123 @@
|
||||
<!-- 加载态(toast 浮层,不白屏) -->
|
||||
<view class="g-toast-loading" wx:if="{{pageState === 'loading'}}">
|
||||
<view class="g-toast-loading-inner">
|
||||
<t-loading theme="circular" size="40rpx" />
|
||||
<text class="g-toast-loading-text">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空态 -->
|
||||
<view class="page-empty" wx:elif="{{pageState === 'empty'}}">
|
||||
<t-empty description="暂无服务记录" />
|
||||
</view>
|
||||
|
||||
<!-- 错误态 -->
|
||||
<view class="page-error" wx:elif="{{pageState === 'error'}}">
|
||||
<t-icon name="close-circle" size="120rpx" color="#e34d59" />
|
||||
<text class="error-text">加载失败,请点击重试</text>
|
||||
<view class="retry-btn" hover-class="retry-btn--hover" bindtap="onRetry">重试</view>
|
||||
</view>
|
||||
|
||||
<!-- 正常态 -->
|
||||
<block wx:elif="{{pageState === 'normal'}}">
|
||||
|
||||
<!-- Banner(复用 task-detail 样式)-->
|
||||
<view class="banner-area">
|
||||
<image src="/assets/images/banner-bg-coral-aurora.svg" class="banner-bg-svg" mode="scaleToFill" />
|
||||
<view class="banner-content">
|
||||
<view class="customer-info">
|
||||
<view class="avatar-box">
|
||||
<text class="avatar-text">{{customerInitial}}</text>
|
||||
</view>
|
||||
<view class="info-right">
|
||||
<view class="name-row">
|
||||
<text class="customer-name">{{customerName}}</text>
|
||||
<view class="name-badges">
|
||||
<text class="name-badge">服务 <text class="badge-highlight">{{totalServiceCount}}</text> 次</text>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="sub-stats">
|
||||
<view class="sub-info">
|
||||
<text class="phone">{{phoneVisible ? customerPhoneFull : customerPhone}}</text>
|
||||
<view class="phone-toggle-btn" bindtap="{{phoneVisible ? 'onCopyPhone' : 'onTogglePhone'}}" hover-class="phone-toggle-btn--hover">
|
||||
<text class="phone-toggle-text">{{phoneVisible ? '复制' : '查看'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 月份切换 -->
|
||||
<view class="month-switcher">
|
||||
<view class="month-btn {{canPrev ? '' : 'disabled'}}" bindtap="onPrevMonth">
|
||||
<t-icon name="chevron-left" size="32rpx" color="{{canPrev ? '#777777' : '#dcdcdc'}}" />
|
||||
</view>
|
||||
<text class="month-label">{{monthLabel}}</text>
|
||||
<view class="month-btn {{canNext ? '' : 'disabled'}}" bindtap="onNextMonth">
|
||||
<t-icon name="chevron-right" size="32rpx" color="{{canNext ? '#777777' : '#dcdcdc'}}" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 月度统计概览 -->
|
||||
<view class="month-summary">
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">本月服务</text>
|
||||
<text class="summary-value">{{monthCount}}</text>
|
||||
</view>
|
||||
<view class="summary-divider"></view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">服务时长</text>
|
||||
<text class="summary-value value-primary">{{monthHours}}</text>
|
||||
</view>
|
||||
<view class="summary-divider"></view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">关系指数</text>
|
||||
<text class="summary-value value-warning">{{monthRelation}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 记录列表(service-record-card 组件)-->
|
||||
<view class="records-container">
|
||||
|
||||
<!-- 无当月记录 -->
|
||||
<view class="no-month-data" wx:if="{{records.length === 0}}">
|
||||
<t-icon name="chart-bar" size="100rpx" color="#dcdcdc" />
|
||||
<text class="no-month-text">本月暂无服务记录</text>
|
||||
</view>
|
||||
|
||||
<service-record-card
|
||||
wx:for="{{records}}"
|
||||
wx:key="id"
|
||||
time="{{item.date}}"
|
||||
course-label="{{item.type}}"
|
||||
type-class="{{item.typeClass}}"
|
||||
type="{{item.recordType}}"
|
||||
table-no="{{item.table}}"
|
||||
hours="{{item.duration}}"
|
||||
hours-raw="{{item.durationRaw}}"
|
||||
drinks="{{item.drinks}}"
|
||||
income="{{item.income}}"
|
||||
is-estimate="{{item.isEstimate}}"
|
||||
/>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<view class="list-footer" wx:if="{{records.length > 0}}">
|
||||
<text class="footer-text">— 已加载全部记录 —</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view class="loading-more" wx:if="{{loadingMore}}">
|
||||
<t-loading theme="circular" size="40rpx" />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- AI 悬浮按钮 -->
|
||||
<ai-float-button customerId="{{customerId}}" />
|
||||
|
||||
</block>
|
||||
|
||||
<dev-fab />
|
||||
@@ -0,0 +1,285 @@
|
||||
/* pages/customer-service-records/customer-service-records.wxss */
|
||||
|
||||
page {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
/* ========== 页面状态 ========== */
|
||||
.page-loading,
|
||||
.page-empty,
|
||||
.page-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
gap: 24rpx;
|
||||
}
|
||||
.error-text {
|
||||
font-size: 26rpx;
|
||||
color: var(--color-error, #e34d59);
|
||||
}
|
||||
.retry-btn {
|
||||
margin-top: 8rpx;
|
||||
padding: 16rpx 48rpx;
|
||||
background: var(--color-primary, #0052d9);
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
border-radius: 22rpx;
|
||||
}
|
||||
.retry-btn--hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ========== Banner(与 task-detail 一致)========== */
|
||||
.banner-area {
|
||||
position: relative;
|
||||
height: 202rpx;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #ff6b4a, #ff8a65);
|
||||
}
|
||||
|
||||
.banner-bg-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 270%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 44rpx 0 36rpx 44rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.customer-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28rpx;
|
||||
padding-top: 8rpx;
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
border-radius: 30rpx;
|
||||
background: rgba(255, 255, 255, 0.20);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.info-right {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30rpx;
|
||||
margin-bottom: 14rpx;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.name-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
flex-shrink: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.name-badge {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
padding: 4rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.badge-highlight {
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.customer-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.customer-phone {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.sub-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.sub-stat {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.70);
|
||||
}
|
||||
|
||||
.stat-highlight {
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.sub-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.phone {
|
||||
font-size: 26rpx;
|
||||
line-height: 36rpx;
|
||||
color: rgba(255, 255, 255, 0.70);
|
||||
}
|
||||
|
||||
.phone-toggle-btn {
|
||||
padding: 4rpx 14rpx;
|
||||
background: rgba(255, 255, 255, 0.20);
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.phone-toggle-text {
|
||||
font-size: 22rpx;
|
||||
line-height: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.90);
|
||||
}
|
||||
|
||||
.phone-toggle-btn--hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ========== 月份切换 ========== */
|
||||
.month-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 48rpx;
|
||||
background: #ffffff;
|
||||
padding: 24rpx 32rpx;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
.month-btn {
|
||||
padding: 12rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.month-btn.disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.month-label {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #242424;
|
||||
}
|
||||
|
||||
/* ========== 月度统计 ========== */
|
||||
.month-summary {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
background: #ffffff;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
.summary-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
}
|
||||
.summary-label {
|
||||
font-size: 20rpx;
|
||||
color: #a6a6a6;
|
||||
line-height: 29rpx;
|
||||
}
|
||||
.summary-value {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #242424;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
.value-primary { color: #0052d9; }
|
||||
.value-warning { color: #ed7b2f; }
|
||||
.summary-divider {
|
||||
width: 2rpx;
|
||||
height: 64rpx;
|
||||
background: #eeeeee;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
/* ========== 记录列表 ========== */
|
||||
.records-container {
|
||||
padding: 24rpx 30rpx;
|
||||
padding-bottom: 100rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
/* 无当月数据 */
|
||||
.no-month-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 0;
|
||||
gap: 20rpx;
|
||||
}
|
||||
.no-month-text {
|
||||
font-size: 26rpx;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
|
||||
/* 底部提示 */
|
||||
.list-footer {
|
||||
text-align: center;
|
||||
padding: 20rpx 0 8rpx;
|
||||
}
|
||||
.footer-text {
|
||||
font-size: 20rpx;
|
||||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.loading-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
Reference in New Issue
Block a user