feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"navigationBarTitleText": "客户详情",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {
|
||||
"coach-level-tag": "/components/coach-level-tag/coach-level-tag",
|
||||
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"note-modal": "/components/note-modal/note-modal",
|
||||
"ai-float-button": "/components/ai-float-button/ai-float-button",
|
||||
"dev-fab": "/components/dev-fab/dev-fab",
|
||||
"clue-card": "/components/clue-card/clue-card",
|
||||
"ai-title-badge": "/components/ai-title-badge/ai-title-badge"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
import { mockCustomerDetail } from "../../utils/mock-data"
|
||||
|
||||
interface ConsumptionRecord {
|
||||
id: string
|
||||
type: "table" | "shop" | "recharge"
|
||||
date: string
|
||||
tableName?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
duration?: string
|
||||
tableFee?: number
|
||||
tableOrigPrice?: number
|
||||
coaches?: Array<{
|
||||
name: string
|
||||
level: string
|
||||
levelColor: string
|
||||
courseType: string
|
||||
hours: string
|
||||
perfHours?: string
|
||||
fee: number
|
||||
}>
|
||||
foodAmount?: number
|
||||
foodOrigPrice?: number
|
||||
totalAmount?: number
|
||||
totalOrigPrice?: number
|
||||
payMethod?: string
|
||||
rechargeAmount?: number
|
||||
}
|
||||
|
||||
const mockRecords: ConsumptionRecord[] = [
|
||||
{
|
||||
id: "r1",
|
||||
type: "table",
|
||||
date: "2026-02-05",
|
||||
tableName: "A12号台",
|
||||
startTime: "21:30",
|
||||
endTime: "00:50",
|
||||
duration: "3h 20min",
|
||||
tableFee: 180,
|
||||
tableOrigPrice: 240,
|
||||
coaches: [
|
||||
{ name: "小燕", level: "senior", levelColor: "pink", courseType: "基础课", hours: "2.5h", fee: 200 },
|
||||
{ name: "Amy", level: "junior", levelColor: "green", courseType: "激励课", hours: "0.5h", perfHours: "1h", fee: 50 },
|
||||
],
|
||||
foodAmount: 210,
|
||||
foodOrigPrice: 260,
|
||||
totalAmount: 640,
|
||||
totalOrigPrice: 750,
|
||||
},
|
||||
{
|
||||
id: "r2",
|
||||
type: "table",
|
||||
date: "2026-02-01",
|
||||
tableName: "888号台",
|
||||
startTime: "14:00",
|
||||
endTime: "16:00",
|
||||
duration: "2h 00min",
|
||||
tableFee: 120,
|
||||
coaches: [
|
||||
{ name: "泡芙", level: "middle", levelColor: "purple", courseType: "激励课", hours: "1.5h", perfHours: "2h", fee: 100 },
|
||||
],
|
||||
totalAmount: 220,
|
||||
},
|
||||
{
|
||||
id: "r3",
|
||||
type: "shop",
|
||||
date: "2026-01-28",
|
||||
coaches: [
|
||||
{ name: "小燕", level: "senior", levelColor: "pink", courseType: "基础课", hours: "1h", fee: 100 },
|
||||
],
|
||||
foodAmount: 180,
|
||||
totalAmount: 280,
|
||||
},
|
||||
]
|
||||
|
||||
Page({
|
||||
data: {
|
||||
pageState: "loading" as "loading" | "empty" | "error" | "normal",
|
||||
detail: {
|
||||
id: "cust_001",
|
||||
name: "王先生",
|
||||
avatarChar: "王",
|
||||
phone: "13812345678",
|
||||
balance: "8,600",
|
||||
consumption60d: "2,800",
|
||||
idealInterval: "7天",
|
||||
daysSinceVisit: "12天",
|
||||
},
|
||||
phoneVisible: false,
|
||||
aiColor: "indigo" as "red" | "orange" | "yellow" | "blue" | "indigo" | "purple",
|
||||
aiInsight: {
|
||||
summary: "高价值 VIP 客户,月均到店 4-5 次,偏好夜场中式台球,近期对斯诺克产生兴趣。社交属性强,常带固定球搭子,有拉新能力。储值余额充足,对促销活动响应积极。",
|
||||
strategies: [
|
||||
{ color: "green", text: "最后到店距今 12 天,超出理想间隔 7 天,建议尽快安排助教小燕主动联系召回" },
|
||||
{ color: "amber", text: "客户提到想练斯诺克走位,可推荐斯诺克专项课程包,结合储值优惠提升客单价" },
|
||||
{ color: "pink", text: "社交属性强,可邀请参加门店球友赛事活动,带动球搭子到店消费" },
|
||||
],
|
||||
},
|
||||
clues: [
|
||||
{
|
||||
category: "客户\n基础",
|
||||
categoryColor: "primary",
|
||||
text: "🎂 生日 3月15日 · VIP会员 · 注册2年",
|
||||
source: "系统",
|
||||
},
|
||||
{
|
||||
category: "消费\n习惯",
|
||||
categoryColor: "success",
|
||||
text: "🌙 常来夜场 · 月均4-5次",
|
||||
source: "系统",
|
||||
},
|
||||
{
|
||||
category: "消费\n习惯",
|
||||
categoryColor: "success",
|
||||
text: "💰 高客单价",
|
||||
source: "系统",
|
||||
detail: "近60天场均消费 ¥420,高于门店均值 ¥180;偏好夜场时段,酒水附加消费占比 35%",
|
||||
},
|
||||
{
|
||||
category: "玩法\n偏好",
|
||||
categoryColor: "purple",
|
||||
text: "🎱 偏爱中式 · 斯诺克进阶中",
|
||||
source: "系统",
|
||||
},
|
||||
{
|
||||
category: "促销\n接受",
|
||||
categoryColor: "warning",
|
||||
text: "🍷 爱点酒水套餐 · 对储值活动敏感",
|
||||
source: "系统",
|
||||
detail: "最近3次到店均点了酒水套餐;上次 ¥5000 储值活动当天即充值,对满赠类活动响应率高",
|
||||
},
|
||||
{
|
||||
category: "社交\n关系",
|
||||
categoryColor: "pink",
|
||||
text: "👥 常带朋友 · 固定球搭子2人",
|
||||
source: "系统",
|
||||
detail: "近60天 80% 的到店为多人局,常与「李哥」「阿杰」同行;曾介绍2位新客办卡",
|
||||
},
|
||||
{
|
||||
category: "重要\n反馈",
|
||||
categoryColor: "error",
|
||||
text: "⚠️ 上次提到想练斯诺克走位,对球桌维护质量比较在意,建议优先安排VIP房",
|
||||
source: "小燕",
|
||||
},
|
||||
],
|
||||
coachTasks: [
|
||||
{
|
||||
name: "小燕",
|
||||
level: "senior",
|
||||
levelColor: "pink",
|
||||
taskType: "高优先召回",
|
||||
taskColor: "red",
|
||||
bgClass: "coach-card-red",
|
||||
status: "normal",
|
||||
lastService: "02-20 21:30 · 2.5h",
|
||||
metrics: [
|
||||
{ label: "近60天次数", value: "18次", color: "primary" },
|
||||
{ label: "总时长", value: "17h" },
|
||||
{ label: "次均时长", value: "0.9h", color: "warning" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "泡芙",
|
||||
level: "middle",
|
||||
levelColor: "purple",
|
||||
taskType: "优先召回",
|
||||
taskColor: "orange",
|
||||
bgClass: "coach-card-orange",
|
||||
status: "pinned",
|
||||
lastService: "02-15 14:00 · 1.5h",
|
||||
metrics: [
|
||||
{ label: "近60天次数", value: "12次", color: "primary" },
|
||||
{ label: "总时长", value: "11h" },
|
||||
{ label: "次均时长", value: "0.9h", color: "warning" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Amy",
|
||||
level: "junior",
|
||||
levelColor: "green",
|
||||
taskType: "关系构建",
|
||||
taskColor: "pink",
|
||||
bgClass: "coach-card-pink",
|
||||
status: "normal",
|
||||
lastService: "02-10 19:00 · 1.0h",
|
||||
metrics: [
|
||||
{ label: "近60天次数", value: "8次", color: "primary" },
|
||||
{ label: "总时长", value: "6h" },
|
||||
{ label: "次均时长", value: "0.75h", color: "warning" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Lucy",
|
||||
level: "senior",
|
||||
levelColor: "pink",
|
||||
taskType: "客户回访",
|
||||
taskColor: "teal",
|
||||
bgClass: "coach-card-teal",
|
||||
status: "abandoned",
|
||||
lastService: "01-28 20:30 · 2.0h",
|
||||
metrics: [
|
||||
{ label: "近60天次数", value: "6次", color: "primary" },
|
||||
{ label: "总时长", value: "9h" },
|
||||
{ label: "次均时长", value: "1.5h", color: "warning" },
|
||||
],
|
||||
},
|
||||
],
|
||||
favoriteCoaches: [
|
||||
{
|
||||
emoji: "❤️",
|
||||
name: "小燕",
|
||||
relationIndex: "9.2",
|
||||
indexColor: "success",
|
||||
bgClass: "fav-card-pink",
|
||||
stats: [
|
||||
{ label: "基础", value: "12h", color: "primary" },
|
||||
{ label: "激励", value: "5h", color: "warning" },
|
||||
{ label: "上课", value: "18次" },
|
||||
{ label: "充值", value: "¥5,000", color: "success" },
|
||||
],
|
||||
},
|
||||
{
|
||||
emoji: "💛",
|
||||
name: "泡芙",
|
||||
relationIndex: "7.8",
|
||||
indexColor: "warning",
|
||||
bgClass: "fav-card-amber",
|
||||
stats: [
|
||||
{ label: "基础", value: "8h", color: "primary" },
|
||||
{ label: "激励", value: "3h", color: "warning" },
|
||||
{ label: "上课", value: "12次" },
|
||||
{ label: "充值", value: "¥3,000", color: "success" },
|
||||
],
|
||||
},
|
||||
],
|
||||
consumptionRecords: mockRecords,
|
||||
loadingMore: false,
|
||||
noteModalVisible: false,
|
||||
sortedNotes: [
|
||||
{ id: 'n1', tagLabel: '管理员', createdAt: '2026-03-05 14:30', content: '本月到店积极,对斯诺克课程感兴趣,建议持续跟进推荐相关课程包' },
|
||||
{ id: 'n2', tagLabel: '小燕', createdAt: '2026-02-20 16:45', content: '客户反馈服务态度很好,提到下次想带朋友一起来' },
|
||||
{ id: 'n3', tagLabel: '管理员', createdAt: '2026-02-10 10:00', content: '上次储值活动当天即充值 ¥5000,对满赠类活动响应积极' },
|
||||
] as Array<{ id: string; tagLabel: string; createdAt: string; content: string }>,
|
||||
},
|
||||
|
||||
onLoad(options: any) {
|
||||
// 随机 AI 配色
|
||||
const aiColors = ['red', 'orange', 'yellow', 'blue', 'indigo', 'purple'] as const
|
||||
const aiColor = aiColors[Math.floor(Math.random() * aiColors.length)]
|
||||
this.setData({ aiColor })
|
||||
this.loadDetail()
|
||||
},
|
||||
|
||||
loadDetail() {
|
||||
this.setData({ pageState: "normal" })
|
||||
},
|
||||
|
||||
onRetry() {
|
||||
this.loadDetail()
|
||||
},
|
||||
|
||||
/** 查看/隐藏手机号 */
|
||||
onTogglePhone() {
|
||||
this.setData({ phoneVisible: !this.data.phoneVisible })
|
||||
},
|
||||
|
||||
/** 复制手机号 */
|
||||
onCopyPhone() {
|
||||
const phone = this.data.detail.phone
|
||||
wx.setClipboardData({
|
||||
data: phone,
|
||||
success: () => {
|
||||
wx.showToast({ title: '手机号码已复制', icon: 'none' })
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
onViewServiceRecords() {
|
||||
wx.navigateTo({ url: "/pages/customer-service-records/customer-service-records" })
|
||||
},
|
||||
|
||||
onStartChat() {
|
||||
wx.navigateTo({ url: "/pages/chat/chat" })
|
||||
},
|
||||
|
||||
onAddNote() {
|
||||
this.setData({ noteModalVisible: true })
|
||||
},
|
||||
|
||||
onNoteConfirm(e: any) {
|
||||
this.setData({ noteModalVisible: false })
|
||||
},
|
||||
|
||||
onNoteCancel() {
|
||||
this.setData({ noteModalVisible: false })
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,323 @@
|
||||
<!-- pages/customer-detail/customer-detail.wxml -->
|
||||
<!-- 加载态(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-icon name="info-circle" size="120rpx" color="#c5c5c5" />
|
||||
<text class="empty-text">未找到客户信息</text>
|
||||
</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" bindtap="onRetry" hover-class="retry-btn--hover">点击重试</view>
|
||||
</view>
|
||||
|
||||
<block wx:elif="{{pageState === 'normal'}}">
|
||||
<!-- Banner 区域 — SVG 做渐变底图 -->
|
||||
<view class="banner-section">
|
||||
<image class="banner-bg-img" src="/assets/images/banner-bg-dark-gold-aurora.svg" mode="widthFix" />
|
||||
<view class="banner-overlay">
|
||||
<!-- 客户头部信息 -->
|
||||
<view class="customer-header">
|
||||
<view class="avatar-box">
|
||||
<text class="avatar-text">{{detail.avatarChar}}</text>
|
||||
</view>
|
||||
<view class="info-right">
|
||||
<view class="name-row">
|
||||
<text class="customer-name">{{detail.name}}</text>
|
||||
</view>
|
||||
<view class="sub-info">
|
||||
<text class="phone">{{phoneVisible ? detail.phone : '138****5678'}}</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>
|
||||
|
||||
<!-- Banner 统计 -->
|
||||
<view class="banner-stats">
|
||||
<view class="stat-item stat-border">
|
||||
<text class="stat-value stat-green">¥{{detail.balance}}</text>
|
||||
<text class="stat-label">储值余额</text>
|
||||
</view>
|
||||
<view class="stat-item stat-border">
|
||||
<text class="stat-value">¥{{detail.consumption60d}}</text>
|
||||
<text class="stat-label">60天消费</text>
|
||||
</view>
|
||||
<view class="stat-item stat-border">
|
||||
<text class="stat-value">{{detail.idealInterval}}</text>
|
||||
<text class="stat-label">理想间隔</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value stat-amber">{{detail.daysSinceVisit}}</text>
|
||||
<text class="stat-label">距今到店</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<view class="main-content" >
|
||||
<!-- AI 智能洞察 -->
|
||||
<view class="ai-insight-card">
|
||||
<view class="ai-insight-header">
|
||||
<view class="ai-icon-box">
|
||||
<image class="ai-icon-img" src="/assets/icons/ai-robot.svg" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="ai-insight-label">AI 智能洞察</text>
|
||||
</view>
|
||||
<view class="ai-insight-summary-v">
|
||||
<text class="ai-insight-summary">{{aiInsight.summary}}</text>
|
||||
</view>
|
||||
<view class="ai-strategy-box">
|
||||
<text class="strategy-title">当前推荐策略</text>
|
||||
<view class="strategy-list">
|
||||
<view class="strategy-item strategy-item-{{item.color}}" wx:for="{{aiInsight.strategies}}" wx:key="index" wx:if="{{index < aiInsight.strategies.length - 1}}">
|
||||
<text class="strategy-text">{{item.text}}</text>
|
||||
</view>
|
||||
<view class="strategy-item strategy-item-{{item.color}} strategy-item-last" wx:for="{{aiInsight.strategies}}" wx:key="index" wx:if="{{index === aiInsight.strategies.length - 1}}">
|
||||
<text class="strategy-text">{{item.text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 维客线索 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="section-title title-green">维客线索</text>
|
||||
<ai-title-badge color="{{aiColor}}" />
|
||||
</view>
|
||||
<view class="clue-list">
|
||||
<clue-card
|
||||
wx:for="{{clues}}"
|
||||
wx:key="index"
|
||||
tag="{{item.category}}"
|
||||
category="{{item.categoryColor}}"
|
||||
emoji=""
|
||||
title="{{item.text}}"
|
||||
source="By:{{item.source}}"
|
||||
content="{{item.detail}}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 助教任务 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="section-title title-blue">助教任务分配</text>
|
||||
<text class="header-hint">当前进行中</text>
|
||||
</view>
|
||||
<view class="coach-task-list">
|
||||
<view class="coach-task-card {{item.bgClass}}" wx:for="{{coachTasks}}" wx:key="index">
|
||||
<view class="coach-task-top">
|
||||
<view class="coach-name-row">
|
||||
<text class="coach-name">{{item.name}}</text>
|
||||
<coach-level-tag level="{{item.level}}" shadowColor="rgba(0,0,0,0)" />
|
||||
</view>
|
||||
<view class="coach-task-right">
|
||||
<text class="coach-task-type type-{{item.taskColor}}">{{item.taskType}}</text>
|
||||
<text class="coach-task-status status-{{item.status}}" wx:if="{{item.status !== 'normal'}}">
|
||||
<text wx:if="{{item.status === 'pinned'}}">📌 置顶</text>
|
||||
<text wx:elif="{{item.status === 'abandoned'}}">❌ 已放弃</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="coach-last-service">上次服务:{{item.lastService}}</text>
|
||||
<view class="coach-metrics">
|
||||
<view class="coach-metric" wx:for="{{item.metrics}}" wx:for-item="m" wx:key="label">
|
||||
<text class="metric-label">{{m.label}}</text>
|
||||
<text class="metric-value {{m.color ? 'text-' + m.color : ''}}">{{m.value}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最喜欢的助教 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="section-title title-pink">最喜欢的助教</text>
|
||||
<text class="header-hint">近60天</text>
|
||||
</view>
|
||||
<view class="fav-coach-list">
|
||||
<view class="fav-coach-card {{item.bgClass}}" wx:for="{{favoriteCoaches}}" wx:key="index">
|
||||
<view class="fav-coach-top">
|
||||
<view class="fav-coach-name">
|
||||
<text class="fav-emoji">{{item.emoji}}</text>
|
||||
<text class="fav-name">{{item.name}}</text>
|
||||
</view>
|
||||
<view class="fav-index">
|
||||
<text class="fav-index-label">关系指数</text>
|
||||
<text class="fav-index-value text-{{item.indexColor}}">{{item.relationIndex}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="fav-period">近60天</text>
|
||||
<view class="fav-stats">
|
||||
<view class="fav-stat" wx:for="{{item.stats}}" wx:for-item="s" wx:key="label">
|
||||
<text class="fav-stat-label">{{s.label}}</text>
|
||||
<text class="fav-stat-value {{s.color ? 'text-' + s.color : ''}}">{{s.value}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 消费记录 -->
|
||||
<view class="card">
|
||||
<view class="card-header" bindtap="onViewServiceRecords" hover-class="card-header--hover">
|
||||
<text class="section-title title-orange">消费记录</text>
|
||||
<t-icon name="chevron-right" size="40rpx" color="#a6a6a6" />
|
||||
</view>
|
||||
|
||||
<view class="record-list" wx:if="{{consumptionRecords.length > 0}}">
|
||||
<block wx:for="{{consumptionRecords}}" wx:key="id">
|
||||
|
||||
<!-- 台桌结账 -->
|
||||
<view class="record-card" wx:if="{{item.type === 'table'}}">
|
||||
<view class="record-card-header record-header-blue">
|
||||
<view class="record-project">
|
||||
<view class="record-dot record-dot-blue"></view>
|
||||
<text class="record-project-name record-name-blue">{{item.tableName}}</text>
|
||||
</view>
|
||||
<text class="record-date">{{item.date}}</text>
|
||||
</view>
|
||||
<view class="record-time-row">
|
||||
<view class="record-time-left">
|
||||
<text class="record-time-text">{{item.startTime}}</text>
|
||||
<text class="record-time-arrow">→</text>
|
||||
<text class="record-time-text">{{item.endTime}}</text>
|
||||
<text class="record-duration-tag">{{item.duration}}</text>
|
||||
</view>
|
||||
<view class="record-fee-right">
|
||||
<text class="record-fee-amount">¥{{item.tableFee}}</text>
|
||||
<text class="record-fee-orig" wx:if="{{item.tableOrigPrice}}">¥{{item.tableOrigPrice}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-coaches" wx:if="{{item.coaches && item.coaches.length > 0}}">
|
||||
<view class="record-coach-grid">
|
||||
<view class="record-coach-card" wx:for="{{item.coaches}}" wx:for-item="c" wx:key="name">
|
||||
<view class="record-coach-name-row">
|
||||
<text class="record-coach-name">{{c.name}}</text>
|
||||
<coach-level-tag level="{{c.level}}" shadowColor="rgba(0,0,0,0)" />
|
||||
</view>
|
||||
<text class="record-coach-type">{{c.courseType}} · {{c.hours}}</text>
|
||||
<view class="record-coach-bottom">
|
||||
<text class="record-coach-perf" wx:if="{{c.perfHours}}">定档绩效:{{c.perfHours}}</text>
|
||||
<text class="record-coach-fee">¥{{c.fee}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-food-row" wx:if="{{item.foodAmount > 0}}">
|
||||
<text class="record-food-label">🍷 食品酒水</text>
|
||||
<view class="record-food-right">
|
||||
<text class="record-food-amount">¥{{item.foodAmount}}</text>
|
||||
<text class="record-fee-orig" wx:if="{{item.foodOrigPrice}}">¥{{item.foodOrigPrice}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-total-row" wx:if="{{item.totalAmount}}">
|
||||
<text class="record-total-label">总金额</text>
|
||||
<view class="record-total-right">
|
||||
<text class="record-total-amount">¥{{item.totalAmount}}</text>
|
||||
<text class="record-fee-orig" wx:if="{{item.totalOrigPrice}}">¥{{item.totalOrigPrice}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商城订单 -->
|
||||
<view class="record-card" wx:elif="{{item.type === 'shop'}}">
|
||||
<view class="record-card-header record-header-green">
|
||||
<view class="record-project">
|
||||
<view class="record-dot record-dot-green"></view>
|
||||
<text class="record-project-name record-name-green">商城订单</text>
|
||||
</view>
|
||||
<text class="record-date">{{item.date}}</text>
|
||||
</view>
|
||||
<view class="record-coaches" wx:if="{{item.coaches && item.coaches.length > 0}}">
|
||||
<view class="record-coach-grid">
|
||||
<view class="record-coach-card" wx:for="{{item.coaches}}" wx:for-item="c" wx:key="name">
|
||||
<view class="record-coach-name-row">
|
||||
<text class="record-coach-name">{{c.name}}</text>
|
||||
<coach-level-tag level="{{c.level}}" shadowColor="rgba(0,0,0,0)" />
|
||||
</view>
|
||||
<text class="record-coach-type">{{c.courseType}} · {{c.hours}}</text>
|
||||
<view class="record-coach-bottom">
|
||||
<text class="record-coach-perf" wx:if="{{c.perfHours}}">定档绩效:{{c.perfHours}}</text>
|
||||
<text class="record-coach-fee">¥{{c.fee}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-food-row" wx:if="{{item.foodAmount > 0}}">
|
||||
<text class="record-food-label">🍷 食品酒水</text>
|
||||
<text class="record-food-amount">¥{{item.foodAmount}}</text>
|
||||
</view>
|
||||
<view class="record-total-row" wx:if="{{item.totalAmount}}">
|
||||
<text class="record-total-label">总金额</text>
|
||||
<text class="record-total-amount">¥{{item.totalAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<view class="record-loading-more" wx:if="{{loadingMore}}">
|
||||
<t-loading theme="circular" size="40rpx" text="加载更多..." />
|
||||
</view>
|
||||
|
||||
<view class="record-empty" wx:if="{{consumptionRecords.length === 0 && !loadingMore}}">
|
||||
<t-icon name="info-circle" size="80rpx" color="#dcdcdc" />
|
||||
<text class="empty-hint">暂无消费记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注记录 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<text class="section-title title-orange">备注记录</text>
|
||||
<text class="header-hint">共 {{sortedNotes.length}} 条</text>
|
||||
</view>
|
||||
<view class="note-list" wx:if="{{sortedNotes.length > 0}}">
|
||||
<view class="note-item" wx:for="{{sortedNotes}}" wx:key="id">
|
||||
<view class="note-top">
|
||||
<text class="note-author">{{item.tagLabel}}</text>
|
||||
<text class="note-time">{{item.createdAt}}</text>
|
||||
</view>
|
||||
<text class="note-content">{{item.content}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="note-empty" wx:else>
|
||||
<t-icon name="edit-1" size="80rpx" color="#dcdcdc" />
|
||||
<text class="empty-hint">暂无备注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar safe-area-bottom">
|
||||
<view class="btn-chat" bindtap="onStartChat" hover-class="btn-chat--hover">
|
||||
<t-icon name="chat" size="36rpx" color="#ffffff" />
|
||||
<text>问问助手</text>
|
||||
</view>
|
||||
<view class="btn-note" bindtap="onAddNote" hover-class="btn-note--hover">
|
||||
<t-icon name="edit-1" size="36rpx" color="#242424" />
|
||||
<text>备注</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注弹窗 -->
|
||||
<note-modal visible="{{noteModalVisible}}" customerName="{{detail.name}}" showExpandBtn="{{false}}" showRating="{{false}}" bind:confirm="onNoteConfirm" bind:cancel="onNoteCancel" />
|
||||
|
||||
<!-- AI 悬浮按钮 -->
|
||||
<ai-float-button customerId="{{detail.id}}" />
|
||||
</block>
|
||||
|
||||
<dev-fab />
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user