微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,9 @@
{
"navigationBarTitleText": "业绩详情",
"usingComponents": {
"ai-float-button": "/components/ai-float-button/ai-float-button",
"metric-card": "/components/metric-card/metric-card",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-loading": "tdesign-miniprogram/loading/loading"
}
}

View File

@@ -0,0 +1,179 @@
// TODO: 联调时替换为真实 API 调用
/** 业绩明细项(本月/上月) */
interface IncomeItem {
icon: string
label: string
desc: string
value: string
}
/** 服务记录(按日期分组后的展示结构) */
interface ServiceRecord {
customerName: string
avatarChar: string
avatarGradient: string
timeRange: string
hours: string
courseType: string
courseTypeClass: string
location: string
income: string
}
interface DateGroup {
date: string
records: ServiceRecord[]
}
Page({
data: {
pageState: 'loading' as 'loading' | 'empty' | 'normal',
/** Banner 数据 */
coachName: '小燕',
coachRole: '助教',
storeName: '广州朗朗桌球',
monthlyIncome: '¥6,206',
lastMonthIncome: '¥16,880',
/** 收入档位 */
currentTier: {
basicRate: 80,
incentiveRate: 95,
},
nextTier: {
basicRate: 90,
incentiveRate: 114,
},
upgradeHoursNeeded: 15,
upgradeBonus: 800,
/** 本月业绩明细 */
incomeItems: [] as IncomeItem[],
monthlyTotal: '¥6,950.5',
/** 服务记录 */
thisMonthRecords: [] as DateGroup[],
thisMonthRecordsExpanded: false,
/** 默认显示前 N 条日期组 */
visibleRecordGroups: 2,
/** 新客列表 */
newCustomers: [] as Array<{ name: string; avatarChar: string; gradient: string; lastService: string; count: number }>,
newCustomerExpanded: false,
/** 常客列表 */
regularCustomers: [] as Array<{ name: string; avatarChar: string; gradient: string; hours: number; income: string; count: number }>,
regularCustomerExpanded: false,
},
onLoad() {
this.loadData()
},
loadData() {
this.setData({ pageState: 'loading' })
setTimeout(() => {
// TODO: 替换为真实 API
const incomeItems: IncomeItem[] = [
{ icon: '🎱', label: '基础课', desc: '80元/h × 75h', value: '¥6,000' },
{ icon: '⭐', label: '激励课', desc: '95.05元/h × 10h', value: '¥950.5' },
{ icon: '💰', label: '充值激励', desc: '客户充值返佣', value: '¥500' },
{ icon: '🏆', label: 'TOP3 销冠奖', desc: '全店业绩前三名奖励', value: '继续努力' },
]
const gradients = [
'from-blue', 'from-pink', 'from-teal', 'from-green',
'from-orange', 'from-purple', 'from-violet', 'from-amber',
]
// 模拟服务记录按日期分组
const thisMonthRecords: DateGroup[] = [
{
date: '2月7日',
records: [
{ customerName: '王先生', avatarChar: '王', avatarGradient: gradients[0], timeRange: '20:00-22:00', hours: '2.0h', courseType: '基础课', courseTypeClass: 'tag-basic', location: '3号台', income: '¥160' },
{ customerName: '李女士', avatarChar: '李', avatarGradient: gradients[1], timeRange: '16:00-18:00', hours: '2.0h', courseType: '包厢课', courseTypeClass: 'tag-vip', location: 'VIP1号房', income: '¥190' },
],
},
{
date: '2月6日',
records: [
{ customerName: '张先生', avatarChar: '张', avatarGradient: gradients[2], timeRange: '19:00-21:00', hours: '2.0h', courseType: '基础课', courseTypeClass: 'tag-basic', location: '5号台', income: '¥160' },
],
},
{
date: '2月5日',
records: [
{ customerName: '陈女士', avatarChar: '陈', avatarGradient: gradients[2], timeRange: '20:00-22:00', hours: '2.0h', courseType: '基础课', courseTypeClass: 'tag-basic', location: '2号台', income: '¥160' },
{ customerName: '赵先生', avatarChar: '赵', avatarGradient: gradients[5], timeRange: '14:00-16:00', hours: '2.0h', courseType: '基础课', courseTypeClass: 'tag-basic', location: '7号台', income: '¥160' },
],
},
{
date: '2月4日',
records: [
{ customerName: '孙先生', avatarChar: '孙', avatarGradient: gradients[6], timeRange: '19:00-21:00', hours: '2.0h', courseType: '包厢课', courseTypeClass: 'tag-vip', location: 'VIP2号房', income: '¥190' },
{ customerName: '吴女士', avatarChar: '吴', avatarGradient: gradients[2], timeRange: '15:00-17:00', hours: '2.0h', courseType: '基础课', courseTypeClass: 'tag-basic', location: '1号台', income: '¥160' },
],
},
]
const newCustomers = [
{ name: '王先生', avatarChar: '王', gradient: gradients[0], lastService: '2月7日', count: 2 },
{ name: '李女士', avatarChar: '李', gradient: gradients[1], lastService: '2月7日', count: 1 },
{ name: '刘先生', avatarChar: '刘', gradient: gradients[4], lastService: '2月6日', count: 1 },
]
const regularCustomers = [
{ name: '张先生', avatarChar: '张', gradient: gradients[2], hours: 12, income: '¥960', count: 6 },
{ name: '陈女士', avatarChar: '陈', gradient: gradients[2], hours: 10, income: '¥800', count: 5 },
{ name: '赵先生', avatarChar: '赵', gradient: gradients[5], hours: 8, income: '¥640', count: 4 },
{ name: '孙先生', avatarChar: '孙', gradient: gradients[6], hours: 6, income: '¥570', count: 3 },
]
this.setData({
pageState: 'normal',
incomeItems,
thisMonthRecords,
newCustomers,
regularCustomers,
})
}, 500)
},
/** 展开/收起本月服务记录 */
toggleThisMonthRecords() {
this.setData({ thisMonthRecordsExpanded: !this.data.thisMonthRecordsExpanded })
},
/** 查看全部 → 跳转业绩明细 */
goToRecords() {
wx.navigateTo({ url: '/pages/performance-records/performance-records' })
},
/** 展开/收起新客列表 */
toggleNewCustomer() {
this.setData({ newCustomerExpanded: !this.data.newCustomerExpanded })
},
/** 展开/收起常客列表 */
toggleRegularCustomer() {
this.setData({ regularCustomerExpanded: !this.data.regularCustomerExpanded })
},
/** 点击客户卡片 → 跳转任务详情 */
onCustomerTap(e: WechatMiniprogram.TouchEvent) {
const { name } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/task-detail/task-detail?customerName=${name}`,
fail: () => wx.showToast({ title: '页面跳转失败', icon: 'none' }),
})
},
/** 点击收入概览卡片 → 跳转业绩记录 */
onIncomeCardTap() {
wx.navigateTo({ url: '/pages/performance-records/performance-records' })
},
})

View File

@@ -0,0 +1,272 @@
<!-- 加载态 -->
<view class="page-loading" wx:if="{{pageState === 'loading'}}">
<t-loading theme="circular" size="80rpx" text="加载中..." />
</view>
<block wx:elif="{{pageState === 'empty'}}">
<view class="page-empty">
<t-icon name="chart-bar" size="120rpx" color="#dcdcdc" />
<text class="empty-text">暂无业绩数据</text>
</view>
</block>
<block wx:else>
<!-- Banner -->
<view class="banner-section">
<!-- banner 背景用 CSS 渐变替代图片 -->
<view class="banner-bg-gradient"></view>
<view class="banner-content">
<!-- 个人信息 -->
<view class="coach-info">
<view class="coach-avatar">
<text class="avatar-emoji">👤</text>
</view>
<view class="coach-meta">
<view class="coach-name-row">
<text class="coach-name">{{coachName}}</text>
<text class="coach-role-tag">{{coachRole}}</text>
</view>
<text class="coach-store">{{storeName}}</text>
</view>
</view>
<!-- 核心收入数据 -->
<view class="income-overview" bindtap="onIncomeCardTap">
<view class="income-card">
<text class="income-label">本月预计收入</text>
<text class="income-value">{{monthlyIncome}}</text>
</view>
<view class="income-card">
<text class="income-label">上月收入</text>
<text class="income-value income-highlight">{{lastMonthIncome}}</text>
</view>
</view>
</view>
</view>
<!-- 收入情况 -->
<view class="section-card">
<view class="section-title">
<view class="title-dot dot-primary"></view>
<text>收入情况</text>
</view>
<!-- 当前档位 -->
<view class="tier-card tier-current">
<view class="tier-badge badge-current">当前档位</view>
<view class="tier-row">
<view class="tier-icon-label">
<text class="tier-emoji">📊</text>
<text class="tier-label tier-label-green">当前档位</text>
</view>
<view class="tier-rates">
<view class="rate-item">
<view class="rate-value-row">
<text class="rate-value rate-green">{{currentTier.basicRate}}</text>
<text class="rate-unit rate-green-light">元/h</text>
</view>
<text class="rate-desc rate-green-light">基础课到手</text>
</view>
<view class="rate-divider"></view>
<view class="rate-item">
<view class="rate-value-row">
<text class="rate-value rate-green">{{currentTier.incentiveRate}}</text>
<text class="rate-unit rate-green-light">元/h</text>
</view>
<text class="rate-desc rate-green-light">激励课到手</text>
</view>
</view>
</view>
</view>
<!-- 下一阶段 -->
<view class="tier-card tier-next">
<view class="tier-badge badge-next">下一阶段</view>
<view class="tier-row">
<view class="tier-icon-label">
<text class="tier-emoji">🎯</text>
<text class="tier-label tier-label-yellow">下一阶段</text>
</view>
<view class="tier-rates">
<view class="rate-item">
<view class="rate-value-row">
<text class="rate-value rate-yellow">{{nextTier.basicRate}}</text>
<text class="rate-unit rate-yellow-light">元/h</text>
</view>
<text class="rate-desc rate-yellow-light">基础课到手</text>
</view>
<view class="rate-divider rate-divider-yellow"></view>
<view class="rate-item">
<view class="rate-value-row">
<text class="rate-value rate-yellow">{{nextTier.incentiveRate}}</text>
<text class="rate-unit rate-yellow-light">元/h</text>
</view>
<text class="rate-desc rate-yellow-light">激励课到手</text>
</view>
</view>
</view>
</view>
<!-- 升级提示 -->
<view class="upgrade-hint">
<view class="upgrade-left">
<text class="upgrade-emoji">⏱️</text>
<view class="upgrade-text">
<text class="upgrade-label">距离下一阶段</text>
<view class="upgrade-hours">
<text>需完成 </text>
<text class="upgrade-hours-num">{{upgradeHoursNeeded}}</text>
<text> 小时</text>
</view>
</view>
</view>
<view class="upgrade-bonus">
<text class="bonus-label">到达即得</text>
<text class="bonus-value">{{upgradeBonus}}元</text>
</view>
</view>
</view>
<!-- 本月业绩 -->
<view class="section-card">
<view class="section-title">
<view class="title-dot dot-success"></view>
<text>本月业绩 预估</text>
</view>
<view class="income-list">
<view class="income-row" wx:for="{{incomeItems}}" wx:key="label">
<view class="income-row-left">
<view class="income-icon-box">
<text>{{item.icon}}</text>
</view>
<view class="income-info">
<text class="income-item-label">{{item.label}}</text>
<text class="income-item-desc">{{item.desc}}</text>
</view>
</view>
<text class="income-item-value">{{item.value}}</text>
</view>
</view>
<!-- 合计 -->
<view class="income-total">
<text class="total-label">本月合计 预估</text>
<text class="total-value">{{monthlyTotal}}</text>
</view>
<!-- 服务记录明细 -->
<view class="service-records-section">
<view class="service-records-header">
<text class="service-records-emoji">📋</text>
<text class="service-records-title">我的服务记录明细</text>
</view>
<block wx:for="{{thisMonthRecords}}" wx:key="date" wx:if="{{thisMonthRecordsExpanded || index < visibleRecordGroups}}">
<view class="date-divider">
<text class="dd-date">{{item.date}}</text>
</view>
<view class="record-item" wx:for="{{item.records}}" wx:for-item="rec" wx:key="customerName">
<view class="record-avatar avatar-{{rec.avatarGradient}}">
<text>{{rec.avatarChar}}</text>
</view>
<view class="record-content">
<view class="record-top">
<view class="record-name-time">
<text class="record-name">{{rec.customerName}}</text>
<text class="record-time">{{rec.timeRange}}</text>
</view>
<text class="record-hours">{{rec.hours}}</text>
</view>
<view class="record-bottom">
<view class="record-tags">
<text class="course-tag {{rec.courseTypeClass}}">{{rec.courseType}}</text>
<text class="record-location">{{rec.location}}</text>
</view>
<text class="record-income">我的预估收入 <text class="record-income-val">{{rec.income}}</text></text>
</view>
</view>
</view>
</block>
<view class="records-toggle" bindtap="toggleThisMonthRecords" wx:if="{{thisMonthRecords.length > visibleRecordGroups}}">
<text>{{thisMonthRecordsExpanded ? '收起' : '展开更多'}}</text>
<t-icon name="{{thisMonthRecordsExpanded ? 'chevron-up' : 'chevron-down'}}" size="28rpx" />
</view>
<view class="records-view-all" bindtap="goToRecords">
<text>查看全部</text>
<t-icon name="chevron-right" size="32rpx" color="#0052d9" />
</view>
</view>
</view>
<!-- 我的新客 -->
<view class="section-card">
<view class="section-title">
<view class="title-dot dot-cyan"></view>
<text>我的新客</text>
</view>
<view class="customer-list">
<view
class="customer-item"
wx:for="{{newCustomers}}"
wx:key="name"
wx:if="{{newCustomerExpanded || index < 2}}"
data-name="{{item.name}}"
bindtap="onCustomerTap"
>
<view class="customer-avatar avatar-{{item.gradient}}">
<text>{{item.avatarChar}}</text>
</view>
<view class="customer-info">
<text class="customer-name">{{item.name}}</text>
<text class="customer-detail">最近服务: {{item.lastService}} · {{item.count}}次</text>
</view>
<t-icon name="chevron-right" size="32rpx" color="#c5c5c5" />
</view>
</view>
<view class="toggle-btn" bindtap="toggleNewCustomer" wx:if="{{newCustomers.length > 2}}">
<text>{{newCustomerExpanded ? '收起 ↑' : '查看更多 ↓'}}</text>
</view>
</view>
<!-- 我的常客 -->
<view class="section-card">
<view class="section-title">
<view class="title-dot dot-pink"></view>
<text>我的常客</text>
</view>
<view class="customer-list">
<view
class="customer-item"
wx:for="{{regularCustomers}}"
wx:key="name"
wx:if="{{regularCustomerExpanded || index < 2}}"
data-name="{{item.name}}"
bindtap="onCustomerTap"
>
<view class="customer-avatar avatar-{{item.gradient}}">
<text>{{item.avatarChar}}</text>
</view>
<view class="customer-info">
<text class="customer-name">{{item.name}}</text>
<text class="customer-detail">{{item.count}}次 · {{item.hours}}h · {{item.income}}</text>
</view>
<t-icon name="chevron-right" size="32rpx" color="#c5c5c5" />
</view>
</view>
<view class="toggle-btn" bindtap="toggleRegularCustomer" wx:if="{{regularCustomers.length > 2}}">
<text>{{regularCustomerExpanded ? '收起 ↑' : '查看更多 ↓'}}</text>
</view>
</view>
</block>
<!-- AI 悬浮按钮 -->
<ai-float-button bottom="{{120}}" />
<dev-fab />

View File

@@ -0,0 +1,657 @@
/* ============================================
* 加载态 / 空态
* ============================================ */
.page-loading {
display: flex;
justify-content: center;
align-items: center;
height: 60vh;
}
.page-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
gap: 24rpx;
}
.empty-text {
font-size: var(--font-sm);
color: var(--color-gray-6);
}
/* ============================================
* Banner
* ============================================ */
.banner-section {
position: relative;
width: 100%;
overflow: hidden;
}
.banner-bg-gradient {
width: 100%;
height: 480rpx;
background: linear-gradient(135deg, #0052d9, #0080ff);
display: block;
}
.banner-content {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 24rpx 40rpx 32rpx;
}
.coach-info {
display: flex;
align-items: center;
gap: 24rpx;
margin-bottom: 40rpx;
margin-top: 16rpx;
}
.coach-avatar {
width: 112rpx;
height: 112rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.2);
overflow: hidden;
flex-shrink: 0;
}
.avatar-img {
width: 100%;
height: 100%;
}
.avatar-emoji {
font-size: 56rpx;
line-height: 1;
}
.coach-meta {
flex: 1;
}
.coach-name-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 8rpx;
}
.coach-name {
font-size: 40rpx;
font-weight: 600;
color: #ffffff;
}
.coach-role-tag {
padding: 4rpx 16rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
font-size: var(--font-xs);
color: #ffffff;
}
.coach-store {
font-size: var(--font-sm);
color: rgba(255, 255, 255, 0.7);
}
/* 收入概览卡片 */
.income-overview {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24rpx;
}
.income-card {
background: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
padding: 24rpx;
text-align: center;
backdrop-filter: blur(4px);
}
.income-label {
display: block;
font-size: var(--font-xs);
color: rgba(255, 255, 255, 0.85);
margin-bottom: 8rpx;
}
.income-value {
display: block;
font-size: var(--font-2xl);
font-weight: 700;
color: #ffffff;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
.income-highlight {
color: #a7f3d0;
}
/* ============================================
* 通用 Section 卡片
* ============================================ */
.section-card {
background: #ffffff;
border-radius: 32rpx;
padding: 32rpx;
margin: 24rpx 24rpx 0;
box-shadow: var(--shadow-lg);
}
.section-title {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 24rpx;
font-size: var(--font-sm);
font-weight: 600;
color: var(--color-gray-13);
}
.title-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
}
.dot-primary { background: var(--color-primary); }
.dot-success { background: var(--color-success); }
.dot-cyan { background: #06b6d4; }
.dot-pink { background: #ec4899; }
/* ============================================
* 收入档位
* ============================================ */
.tier-card {
position: relative;
padding: 24rpx;
border-radius: 24rpx;
margin-bottom: 20rpx;
}
.tier-current {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
border: 2rpx solid #86efac;
}
.tier-next {
background: linear-gradient(135deg, #fefce8 0%, #fef9c3 100%);
border: 2rpx solid #fde047;
}
.tier-badge {
position: absolute;
top: -16rpx;
right: 24rpx;
padding: 4rpx 20rpx;
border-radius: 20rpx;
font-size: 20rpx;
font-weight: 600;
color: #ffffff;
}
.badge-current {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
}
.badge-next {
background: linear-gradient(135deg, #eab308 0%, #ca8a04 100%);
}
.tier-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.tier-icon-label {
display: flex;
align-items: center;
gap: 12rpx;
}
.tier-emoji {
font-size: 48rpx;
}
.tier-label {
font-size: var(--font-sm);
font-weight: 500;
}
.tier-label-green { color: #15803d; }
.tier-label-yellow { color: #a16207; }
.tier-rates {
display: flex;
align-items: center;
gap: 24rpx;
}
.rate-item {
text-align: center;
width: 128rpx;
}
.rate-value-row {
display: flex;
align-items: baseline;
justify-content: center;
gap: 4rpx;
}
.rate-value {
font-size: 36rpx;
font-weight: 700;
}
.rate-unit {
font-size: var(--font-xs);
}
.rate-desc {
font-size: 20rpx;
margin-top: 4rpx;
display: block;
}
.rate-green { color: #15803d; }
.rate-green-light { color: #16a34a; }
.rate-yellow { color: #a16207; }
.rate-yellow-light { color: #ca8a04; }
.rate-divider {
width: 2rpx;
height: 64rpx;
background: #bbf7d0;
}
.rate-divider-yellow {
background: #fef08a;
}
/* 升级提示 */
.upgrade-hint {
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(to right, #eff6ff, #eef2ff);
border-radius: 24rpx;
padding: 24rpx;
border: 2rpx solid #bfdbfe;
}
.upgrade-left {
display: flex;
align-items: center;
gap: 12rpx;
}
.upgrade-emoji {
font-size: 36rpx;
}
.upgrade-label {
font-size: var(--font-xs);
color: var(--color-gray-9);
display: block;
}
.upgrade-hours {
font-size: var(--font-sm);
font-weight: 600;
color: #1d4ed8;
}
.upgrade-hours-num {
font-size: var(--font-base);
}
.upgrade-bonus {
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(to right, #fbbf24, #f97316);
color: #ffffff;
padding: 12rpx 24rpx;
border-radius: 16rpx;
}
.bonus-label {
font-size: 20rpx;
opacity: 0.9;
}
.bonus-value {
font-size: 36rpx;
font-weight: 700;
}
/* ============================================
* 本月业绩明细
* ============================================ */
.income-list {
margin-bottom: 16rpx;
}
.income-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 2rpx solid var(--color-gray-1);
}
.income-row:last-child {
border-bottom: none;
}
.income-row-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.income-icon-box {
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
background: var(--color-gray-1);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--font-sm);
}
.income-info {
display: flex;
flex-direction: column;
}
.income-item-label {
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-gray-13);
}
.income-item-desc {
font-size: 20rpx;
color: var(--color-gray-5);
margin-top: 4rpx;
}
.income-item-value {
font-size: var(--font-base);
font-weight: 700;
color: var(--color-gray-13);
font-variant-numeric: tabular-nums;
}
.income-total {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 16rpx;
border-top: 2rpx solid var(--color-gray-1);
}
.total-label {
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-gray-7);
}
.total-value {
font-size: 40rpx;
font-weight: 700;
color: var(--color-success);
font-variant-numeric: tabular-nums;
}
/* ============================================
* 服务记录
* ============================================ */
.service-records-section {
margin-top: 32rpx;
margin: 32rpx -32rpx -32rpx;
padding: 32rpx;
background: rgba(243, 243, 243, 0.7);
border-radius: 0 0 32rpx 32rpx;
}
.service-records-header {
display: flex;
align-items: center;
gap: 8rpx;
margin-bottom: 8rpx;
}
.service-records-emoji {
font-size: var(--font-sm);
}
.service-records-title {
font-size: var(--font-sm);
font-weight: 600;
color: var(--color-gray-13);
}
.date-divider {
padding: 20rpx 0 8rpx;
}
.dd-date {
font-size: 22rpx;
color: var(--color-gray-7);
font-weight: 500;
}
.record-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 16rpx 0;
}
.record-avatar {
width: 76rpx;
height: 76rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: var(--font-sm);
font-weight: 500;
flex-shrink: 0;
}
/* 头像渐变色 */
.avatar-from-blue { background: linear-gradient(135deg, #60a5fa, #6366f1); }
.avatar-from-pink { background: linear-gradient(135deg, #f472b6, #f43f5e); }
.avatar-from-teal { background: linear-gradient(135deg, #2dd4bf, #10b981); }
.avatar-from-green { background: linear-gradient(135deg, #4ade80, #14b8a6); }
.avatar-from-orange { background: linear-gradient(135deg, #fb923c, #f59e0b); }
.avatar-from-purple { background: linear-gradient(135deg, #c084fc, #8b5cf6); }
.avatar-from-violet { background: linear-gradient(135deg, #a78bfa, #7c3aed); }
.avatar-from-amber { background: linear-gradient(135deg, #fbbf24, #eab308); }
.record-content {
flex: 1;
min-width: 0;
}
.record-top {
display: flex;
align-items: center;
justify-content: space-between;
}
.record-name-time {
display: flex;
align-items: center;
gap: 12rpx;
min-width: 0;
}
.record-name {
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-gray-13);
flex-shrink: 0;
}
.record-time {
font-size: var(--font-xs);
color: var(--color-gray-6);
}
.record-hours {
font-size: var(--font-sm);
font-weight: 700;
color: #059669;
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}
.record-bottom {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8rpx;
}
.record-tags {
display: flex;
align-items: center;
gap: 12rpx;
}
.course-tag {
padding: 2rpx 12rpx;
border-radius: 8rpx;
font-size: 22rpx;
font-weight: 500;
}
.tag-basic {
background: #ecfdf5;
color: #15803d;
}
.tag-vip {
background: #eff6ff;
color: #1d4ed8;
}
.tag-tip {
background: #fffbeb;
color: #a16207;
}
.record-location {
font-size: var(--font-xs);
color: var(--color-gray-7);
}
.record-income {
font-size: 22rpx;
color: var(--color-gray-5);
flex-shrink: 0;
}
.record-income-val {
font-weight: 500;
color: var(--color-gray-9);
}
.records-toggle {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 16rpx 0;
font-size: var(--font-sm);
color: var(--color-gray-7);
}
.records-view-all {
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
padding: 12rpx 0;
font-size: var(--font-sm);
color: var(--color-primary);
font-weight: 500;
}
/* ============================================
* 新客 / 常客列表
* ============================================ */
.customer-list {
margin-bottom: 8rpx;
}
.customer-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 16rpx 0;
border-bottom: 2rpx solid var(--color-gray-1);
}
.customer-item:last-child {
border-bottom: none;
}
.customer-avatar {
width: 76rpx;
height: 76rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: var(--font-sm);
font-weight: 500;
flex-shrink: 0;
}
.customer-info {
flex: 1;
min-width: 0;
}
.customer-name {
display: block;
font-size: var(--font-sm);
font-weight: 500;
color: var(--color-gray-13);
}
.customer-detail {
display: block;
font-size: var(--font-xs);
color: var(--color-gray-6);
margin-top: 4rpx;
}
.toggle-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 12rpx 0;
font-size: var(--font-sm);
color: var(--color-gray-7);
}