264 lines
8.3 KiB
TypeScript
264 lines
8.3 KiB
TypeScript
// 助教看板页 — 排序×技能×时间三重筛选,4 种维度卡片
|
||
// TODO: 联调时替换 mock 数据为真实 API 调用
|
||
export {}
|
||
|
||
/** 排序维度 → 卡片模板映射 */
|
||
type DimType = 'perf' | 'salary' | 'sv' | 'task'
|
||
|
||
const SORT_TO_DIM: Record<string, DimType> = {
|
||
perf_desc: 'perf',
|
||
perf_asc: 'perf',
|
||
salary_desc: 'salary',
|
||
salary_asc: 'salary',
|
||
sv_desc: 'sv',
|
||
task_desc: 'task',
|
||
}
|
||
|
||
const SORT_OPTIONS = [
|
||
{ value: 'perf_desc', text: '定档业绩最高' },
|
||
{ value: 'perf_asc', text: '定档业绩最低' },
|
||
{ value: 'salary_desc', text: '工资最高' },
|
||
{ value: 'salary_asc', text: '工资最低' },
|
||
{ value: 'sv_desc', text: '客源储值最高' },
|
||
{ value: 'task_desc', text: '任务完成最多' },
|
||
]
|
||
|
||
const SKILL_OPTIONS = [
|
||
{ value: 'all', text: '不限' },
|
||
{ value: 'chinese', text: '🎱 中式/追分' },
|
||
{ value: 'snooker', text: '斯诺克' },
|
||
{ value: 'mahjong', text: '🀄 麻将/棋牌' },
|
||
{ value: 'karaoke', text: '🎤 团建/K歌' },
|
||
]
|
||
|
||
const TIME_OPTIONS = [
|
||
{ value: 'month', text: '本月' },
|
||
{ value: 'quarter', text: '本季度' },
|
||
{ value: 'last_month', text: '上月' },
|
||
{ value: 'last_3m', text: '前3个月(不含本月)' },
|
||
{ value: 'last_quarter', text: '上季度' },
|
||
{ value: 'last_6m', text: '最近6个月(不含本月,不支持客源储值最高)' },
|
||
]
|
||
|
||
/** 等级 → 样式类映射 */
|
||
const LEVEL_CLASS: Record<string, string> = {
|
||
'星级': 'level--star',
|
||
'高级': 'level--high',
|
||
'中级': 'level--mid',
|
||
'初级': 'level--low',
|
||
}
|
||
|
||
/** 技能 → 样式类映射 */
|
||
const SKILL_CLASS: Record<string, string> = {
|
||
'🎱': 'skill--chinese',
|
||
'斯': 'skill--snooker',
|
||
'🀄': 'skill--mahjong',
|
||
'🎤': 'skill--karaoke',
|
||
}
|
||
|
||
interface CoachItem {
|
||
id: string
|
||
name: string
|
||
initial: string
|
||
avatarGradient: string
|
||
level: string
|
||
levelClass: string
|
||
skills: Array<{ text: string; cls: string }>
|
||
topCustomers: string[]
|
||
// 定档业绩维度
|
||
perfHours: string
|
||
perfHoursBefore?: string
|
||
perfGap?: string
|
||
perfReached: boolean
|
||
// 工资维度
|
||
salary: string
|
||
salaryPerfHours: string
|
||
salaryPerfBefore?: string
|
||
// 客源储值维度
|
||
svAmount: string
|
||
svCustomerCount: string
|
||
svConsume: string
|
||
// 任务维度
|
||
taskRecall: string
|
||
taskCallback: string
|
||
}
|
||
|
||
/** Mock 数据(忠于 H5 原型 6 位助教) */
|
||
const MOCK_COACHES: CoachItem[] = [
|
||
{
|
||
id: 'c1', name: '小燕', initial: '小',
|
||
avatarGradient: 'avatar--blue',
|
||
level: '星级', levelClass: LEVEL_CLASS['星级'],
|
||
skills: [{ text: '🎱', cls: SKILL_CLASS['🎱'] }],
|
||
topCustomers: ['💖 王先生', '💖 李女士', '💛 赵总'],
|
||
perfHours: '86.2h', perfHoursBefore: '92.0h', perfGap: '距升档 13.8h', perfReached: false,
|
||
salary: '¥12,680', salaryPerfHours: '86.2h', salaryPerfBefore: '92.0h',
|
||
svAmount: '¥45,200', svCustomerCount: '18', svConsume: '¥8,600',
|
||
taskRecall: '18', taskCallback: '14',
|
||
},
|
||
{
|
||
id: 'c2', name: '泡芙', initial: '泡',
|
||
avatarGradient: 'avatar--green',
|
||
level: '高级', levelClass: LEVEL_CLASS['高级'],
|
||
skills: [{ text: '斯', cls: SKILL_CLASS['斯'] }],
|
||
topCustomers: ['💖 陈先生', '💛 刘女士', '💛 黄总'],
|
||
perfHours: '72.5h', perfHoursBefore: '78.0h', perfGap: '距升档 7.5h', perfReached: false,
|
||
salary: '¥10,200', salaryPerfHours: '72.5h', salaryPerfBefore: '78.0h',
|
||
svAmount: '¥38,600', svCustomerCount: '15', svConsume: '¥6,200',
|
||
taskRecall: '15', taskCallback: '13',
|
||
},
|
||
{
|
||
id: 'c3', name: 'Amy', initial: 'A',
|
||
avatarGradient: 'avatar--pink',
|
||
level: '星级', levelClass: LEVEL_CLASS['星级'],
|
||
skills: [{ text: '🎱', cls: SKILL_CLASS['🎱'] }, { text: '斯', cls: SKILL_CLASS['斯'] }],
|
||
topCustomers: ['💖 张先生', '💛 周女士', '💛 吴总'],
|
||
perfHours: '68.0h', perfHoursBefore: '72.5h', perfGap: '距升档 32.0h', perfReached: false,
|
||
salary: '¥9,800', salaryPerfHours: '68.0h', salaryPerfBefore: '72.5h',
|
||
svAmount: '¥32,100', svCustomerCount: '14', svConsume: '¥5,800',
|
||
taskRecall: '12', taskCallback: '13',
|
||
},
|
||
{
|
||
id: 'c4', name: 'Mia', initial: 'M',
|
||
avatarGradient: 'avatar--amber',
|
||
level: '中级', levelClass: LEVEL_CLASS['中级'],
|
||
skills: [{ text: '🀄', cls: SKILL_CLASS['🀄'] }],
|
||
topCustomers: ['💛 赵先生', '💛 吴女士', '💛 孙总'],
|
||
perfHours: '55.0h', perfGap: '距升档 5.0h', perfReached: false,
|
||
salary: '¥7,500', salaryPerfHours: '55.0h',
|
||
svAmount: '¥28,500', svCustomerCount: '12', svConsume: '¥4,100',
|
||
taskRecall: '10', taskCallback: '10',
|
||
},
|
||
{
|
||
id: 'c5', name: '糖糖', initial: '糖',
|
||
avatarGradient: 'avatar--purple',
|
||
level: '初级', levelClass: LEVEL_CLASS['初级'],
|
||
skills: [{ text: '🎱', cls: SKILL_CLASS['🎱'] }],
|
||
topCustomers: ['💛 钱先生', '💛 孙女士', '💛 周总'],
|
||
perfHours: '42.0h', perfHoursBefore: '45.0h', perfReached: true,
|
||
salary: '¥6,200', salaryPerfHours: '42.0h', salaryPerfBefore: '45.0h',
|
||
svAmount: '¥22,000', svCustomerCount: '10', svConsume: '¥3,500',
|
||
taskRecall: '8', taskCallback: '10',
|
||
},
|
||
{
|
||
id: 'c6', name: '露露', initial: '露',
|
||
avatarGradient: 'avatar--cyan',
|
||
level: '中级', levelClass: LEVEL_CLASS['中级'],
|
||
skills: [{ text: '🎤', cls: SKILL_CLASS['🎤'] }],
|
||
topCustomers: ['💛 郑先生', '💛 冯女士', '💛 陈总'],
|
||
perfHours: '38.0h', perfGap: '距升档 22.0h', perfReached: false,
|
||
salary: '¥5,100', salaryPerfHours: '38.0h',
|
||
svAmount: '¥18,300', svCustomerCount: '9', svConsume: '¥2,800',
|
||
taskRecall: '6', taskCallback: '9',
|
||
},
|
||
]
|
||
|
||
Page({
|
||
data: {
|
||
pageState: 'loading' as 'loading' | 'empty' | 'normal',
|
||
activeTab: 'coach' as 'finance' | 'customer' | 'coach',
|
||
|
||
selectedSort: 'perf_desc',
|
||
sortOptions: SORT_OPTIONS,
|
||
selectedSkill: 'all',
|
||
skillOptions: SKILL_OPTIONS,
|
||
selectedTime: 'month',
|
||
timeOptions: TIME_OPTIONS,
|
||
|
||
/** 当前维度类型,控制卡片模板 */
|
||
dimType: 'perf' as DimType,
|
||
|
||
coaches: [] as CoachItem[],
|
||
allCoaches: [] as CoachItem[],
|
||
|
||
/** 筛选栏可见性(滚动隐藏/显示) */
|
||
filterBarVisible: true,
|
||
},
|
||
|
||
_lastScrollTop: 0,
|
||
_scrollAcc: 0,
|
||
_scrollDir: null as 'up' | 'down' | null,
|
||
|
||
onLoad() {
|
||
this.loadData()
|
||
},
|
||
|
||
onPullDownRefresh() {
|
||
this.loadData()
|
||
setTimeout(() => wx.stopPullDownRefresh(), 500)
|
||
},
|
||
|
||
/** 滚动隐藏/显示筛选栏 */
|
||
onPageScroll(e: { scrollTop: number }) {
|
||
const y = e.scrollTop
|
||
const delta = y - this._lastScrollTop
|
||
this._lastScrollTop = y
|
||
|
||
if (y <= 8) {
|
||
if (!this.data.filterBarVisible) this.setData({ filterBarVisible: true })
|
||
this._scrollAcc = 0
|
||
this._scrollDir = null
|
||
return
|
||
}
|
||
|
||
if (Math.abs(delta) <= 2) return
|
||
const dir = delta > 0 ? 'down' : 'up'
|
||
if (dir !== this._scrollDir) {
|
||
this._scrollDir = dir
|
||
this._scrollAcc = 0
|
||
}
|
||
this._scrollAcc += Math.abs(delta)
|
||
|
||
const threshold = dir === 'up' ? 12 : 24
|
||
if (this._scrollAcc < threshold) return
|
||
|
||
const visible = dir === 'up'
|
||
if (this.data.filterBarVisible !== visible) {
|
||
this.setData({ filterBarVisible: visible })
|
||
}
|
||
this._scrollAcc = 0
|
||
},
|
||
|
||
loadData() {
|
||
this.setData({ pageState: 'loading' })
|
||
setTimeout(() => {
|
||
const data = MOCK_COACHES
|
||
if (!data || data.length === 0) {
|
||
this.setData({ pageState: 'empty' })
|
||
return
|
||
}
|
||
this.setData({ allCoaches: data, coaches: data, pageState: 'normal' })
|
||
}, 400)
|
||
},
|
||
|
||
onTabChange(e: WechatMiniprogram.TouchEvent) {
|
||
const tab = e.currentTarget.dataset.tab as string
|
||
if (tab === 'finance') {
|
||
wx.switchTab({ url: '/pages/board-finance/board-finance' })
|
||
} else if (tab === 'customer') {
|
||
wx.navigateTo({ url: '/pages/board-customer/board-customer' })
|
||
}
|
||
},
|
||
|
||
onSortChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||
const val = e.detail.value
|
||
this.setData({
|
||
selectedSort: val,
|
||
dimType: SORT_TO_DIM[val] || 'perf',
|
||
})
|
||
},
|
||
|
||
onSkillChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||
this.setData({ selectedSkill: e.detail.value })
|
||
},
|
||
|
||
onTimeChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||
this.setData({ selectedTime: e.detail.value })
|
||
},
|
||
|
||
onCoachTap(e: WechatMiniprogram.TouchEvent) {
|
||
const id = e.currentTarget.dataset.id as string
|
||
wx.navigateTo({ url: '/pages/coach-detail/coach-detail?id=' + id })
|
||
},
|
||
})
|