import { mockCoaches } from '../../utils/mock-data' import { sortByTimestamp } from '../../utils/sort' /** 助教详情(含绩效、备注等扩展数据) */ interface CoachDetail { id: string name: string avatar: string level: string skills: string[] workYears: number customerCount: number /** 绩效指标 */ performance: { monthlyHours: number monthlySalary: number customerBalance: number tasksCompleted: number } /** 收入明细 */ income: { thisMonth: IncomeItem[] lastMonth: IncomeItem[] } /** 备注列表 */ notes: NoteItem[] } interface IncomeItem { label: string amount: string color: string } interface NoteItem { id: string content: string timestamp: string score: number customerName: string } /** 内联 Mock 数据:助教详情扩展 */ const mockCoachDetail: CoachDetail = { id: 'coach-001', name: '小燕', avatar: '/assets/images/avatar-default.png', level: '星级', skills: ['中🎱', '🎯 斯诺克'], workYears: 3, customerCount: 68, performance: { monthlyHours: 87.5, monthlySalary: 6950, customerBalance: 86200, tasksCompleted: 38, }, income: { thisMonth: [ { label: '基础课时费', amount: '¥3,500', color: 'primary' }, { label: '激励课时费', amount: '¥1,800', color: 'success' }, { label: '充值提成', amount: '¥1,200', color: 'warning' }, { label: '酒水提成', amount: '¥450', color: 'purple' }, ], lastMonth: [ { label: '基础课时费', amount: '¥3,800', color: 'primary' }, { label: '激励课时费', amount: '¥1,900', color: 'success' }, { label: '充值提成', amount: '¥1,100', color: 'warning' }, { label: '酒水提成', amount: '¥400', color: 'purple' }, ], }, notes: [ { id: 'n1', content: '本月表现优秀,客户好评率高', timestamp: '2026-03-05 14:30', score: 9, customerName: '管理员' }, { id: 'n2', content: '需要加强斯诺克教学技巧', timestamp: '2026-02-28 10:00', score: 7, customerName: '管理员' }, { id: 'n3', content: '客户王先生反馈服务态度很好', timestamp: '2026-02-20 16:45', score: 8, customerName: '王先生' }, ], } Page({ data: { /** 页面状态 */ pageState: 'loading' as 'loading' | 'empty' | 'normal', /** 助教 ID */ coachId: '', /** 助教详情 */ detail: null as CoachDetail | null, /** Banner 指标 */ bannerMetrics: [] as Array<{ label: string; value: string }>, /** 绩效指标卡片 */ perfCards: [] as Array<{ label: string; value: string; unit: string; sub: string; bgClass: string; valueColor: string }>, /** 收入明细 Tab */ incomeTab: 'this' as 'this' | 'last', /** 当前收入明细 */ currentIncome: [] as IncomeItem[], /** 当前收入合计 */ incomeTotal: '', /** 排序后的备注列表 */ sortedNotes: [] as NoteItem[], /** 备注弹窗 */ noteModalVisible: false, }, onLoad(options) { const id = options?.id || '' this.setData({ coachId: id }) this.loadData(id) }, loadData(id: string) { this.setData({ pageState: 'loading' }) setTimeout(() => { // TODO: 替换为真实 API 调用 GET /api/coaches/:id const basicCoach = mockCoaches.find((c) => c.id === id) const detail: CoachDetail = basicCoach ? { ...mockCoachDetail, id: basicCoach.id, name: basicCoach.name } : mockCoachDetail if (!detail) { this.setData({ pageState: 'empty' }) return } const bannerMetrics = [ { label: '工龄', value: `${detail.workYears}年` }, { label: '客户', value: `${detail.customerCount}人` }, ] const perfCards = [ { label: '本月定档业绩', value: `${detail.performance.monthlyHours}`, unit: 'h', sub: '折算前 89.0h', bgClass: 'perf-blue', valueColor: 'text-primary' }, { label: '本月工资(预估)', value: `¥${detail.performance.monthlySalary.toLocaleString()}`, unit: '', sub: '含预估部分', bgClass: 'perf-green', valueColor: 'text-success' }, { label: '客源储值余额', value: `¥${detail.performance.customerBalance.toLocaleString()}`, unit: '', sub: `${detail.customerCount}位客户合计`, bgClass: 'perf-orange', valueColor: 'text-warning' }, { label: '本月任务完成', value: `${detail.performance.tasksCompleted}`, unit: '个', sub: '覆盖 22 位客户', bgClass: 'perf-purple', valueColor: 'text-purple' }, ] const sorted = sortByTimestamp(detail.notes || [], 'timestamp') as NoteItem[] this.setData({ pageState: 'normal', detail, bannerMetrics, perfCards, sortedNotes: sorted, }) this.switchIncomeTab('this') }, 500) }, /** 切换收入明细 Tab */ switchIncomeTab(tab: 'this' | 'last') { const detail = this.data.detail if (!detail) return const items = tab === 'this' ? detail.income.thisMonth : detail.income.lastMonth // 计算合计(从格式化金额中提取数字) const total = items.reduce((sum, item) => { const num = parseFloat(item.amount.replace(/[¥,]/g, '')) return sum + (isNaN(num) ? 0 : num) }, 0) this.setData({ incomeTab: tab, currentIncome: items, incomeTotal: `¥${total.toLocaleString()}`, }) }, /** 点击收入 Tab */ onIncomeTabTap(e: WechatMiniprogram.CustomEvent) { const tab = e.currentTarget.dataset.tab as 'this' | 'last' this.switchIncomeTab(tab) }, /** 打开备注弹窗 */ onAddNote() { this.setData({ noteModalVisible: true }) }, /** 备注弹窗确认 */ onNoteConfirm(e: WechatMiniprogram.CustomEvent<{ score: number; content: string }>) { const { score, content } = e.detail // TODO: 替换为真实 API 调用 POST /api/xcx/notes const newNote: NoteItem = { id: `n-${Date.now()}`, content, timestamp: new Date().toISOString().slice(0, 16).replace('T', ' '), score, customerName: '我', } const notes = [newNote, ...this.data.sortedNotes] this.setData({ noteModalVisible: false, sortedNotes: notes, }) wx.showToast({ title: '备注已保存', icon: 'success' }) }, /** 备注弹窗取消 */ onNoteCancel() { this.setData({ noteModalVisible: false }) }, /** 返回 */ onBack() { wx.navigateBack() }, /** 问问助手 */ onStartChat() { const id = this.data.coachId || this.data.detail?.id || '' wx.navigateTo({ url: `/pages/chat/chat?coachId=${id}`, fail: () => wx.showToast({ title: '页面跳转失败', icon: 'none' }), }) }, })