feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更: - backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔 - admin-web: ETL 状态页、任务管理、调度配置、登录优化 - miniprogram: 看板页面、聊天集成、UI 组件、导航更新 - etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强 - tenant-admin: 项目初始化 - db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8) - packages/shared: 枚举和工具函数更新 - tools: 数据库工具、报表生成、健康检查 - docs: PRD/架构/部署/合约文档更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,77 +1,49 @@
|
||||
/* AI_CHANGELOG
|
||||
| 日期 | Prompt | 变更 |
|
||||
|------|--------|------|
|
||||
| 2026-03-23 | 角色路由+页面权限守卫 | onShow 添加 checkPageAccess 权限守卫 |
|
||||
| 2026-03-27 | board-finance-integration T3.1-T3.3 | _loadGiftRows→_loadData 绑定全部 6 板块;筛选联动调 _loadData;areaOptions 9 项 |
|
||||
| 2026-03-27 | board-finance-integration T3.1 fix | _loadData 移除 formatMoney,所有金额/百分比字段传原始数字,由 WXS 负责格式化,修复双重格式化导致 NaN |
|
||||
| 2026-07-22 | 财务看板多问题修复 | discountRate/balanceRate ×100 修复百分比计算(后端返回小数,WXS 期望百分比数字) |
|
||||
| 2026-03-28 | 环比修复 | _loadData 绑定所有板块的 is_down/is_flat;mapExpenseItems/mapCoachTable 读取后端真实涨跌方向 |
|
||||
*/
|
||||
// 财务看板页 — 忠于 H5 原型结构
|
||||
// CHANGE 2026-07-22 | Prompt: gift-card-breakdown Task 9.1 | 直接原因: 赠送卡矩阵 giftRows 从 mock 替换为真实 API 数据
|
||||
|
||||
import { checkPageAccess, getVisibleBoardTabs } from '../../utils/auth-guard'
|
||||
import { getRandomAiColor } from '../../utils/ai-color'
|
||||
import { fetchBoardFinance } from '../../services/api'
|
||||
import { formatMoney } from '../../utils/money'
|
||||
|
||||
/** 目录板块定义 */
|
||||
function isCurrentMonthFilter(selectedTime: string): boolean {
|
||||
return selectedTime === 'month' && new Date().getDate() <= 5
|
||||
}
|
||||
|
||||
interface TocItem {
|
||||
emoji: string
|
||||
title: string
|
||||
sectionId: string
|
||||
}
|
||||
|
||||
/** 指标解释映射 */
|
||||
const tipContents: Record<string, { title: string; content: string }> = {
|
||||
occurrence: {
|
||||
title: '发生额/正价',
|
||||
content: '所有消费项目按标价计算的总金额,不扣除任何优惠。\n\n即"如果没有任何折扣,客户应付多少"。',
|
||||
},
|
||||
discount: {
|
||||
title: '总优惠',
|
||||
content: '包含会员折扣、赠送卡抵扣、团购差价等所有优惠金额。\n\n优惠越高,实际收入越低。',
|
||||
},
|
||||
confirmed: {
|
||||
title: '成交/确认收入',
|
||||
content: '发生额减去总优惠后的实际入账金额。\n\n成交收入 = 发生额 - 总优惠',
|
||||
},
|
||||
cashIn: {
|
||||
title: '实收/现金流入',
|
||||
content: '实际到账的现金,包含消费直接支付和储值充值。\n\n往期为已结算金额,本期为截至当前的发生额。',
|
||||
},
|
||||
cashOut: {
|
||||
title: '现金支出',
|
||||
content: '包含人工、房租、水电、进货等所有经营支出。',
|
||||
},
|
||||
balance: {
|
||||
title: '现金结余',
|
||||
content: '现金流入减去现金支出。\n\n现金结余 = 现金流入 - 现金支出',
|
||||
},
|
||||
rechargeActual: {
|
||||
title: '储值卡充值实收',
|
||||
content: '会员储值卡首充和续费的实际到账金额。\n\n不含赠送金额。',
|
||||
},
|
||||
firstCharge: {
|
||||
title: '首充',
|
||||
content: '新会员首次充值的金额。',
|
||||
},
|
||||
renewCharge: {
|
||||
title: '续费',
|
||||
content: '老会员续费充值的金额。',
|
||||
},
|
||||
consume: {
|
||||
title: '消耗',
|
||||
content: '会员使用储值卡消费的金额。',
|
||||
},
|
||||
cardBalance: {
|
||||
title: '储值卡总余额',
|
||||
content: '所有储值卡的剩余可用余额。',
|
||||
},
|
||||
allCardBalance: {
|
||||
title: '全类别会员卡余额合计',
|
||||
content: '储值卡 + 赠送卡(酒水卡、台费卡、抵用券)的总余额。\n\n仅供经营参考,非财务属性。',
|
||||
},
|
||||
occurrence: { title: '发生额/正价', content: '所有消费项目按标价计算的总金额,不扣除任何优惠。\n\n即"如果没有任何折扣,客户应付多少"。' },
|
||||
discount: { title: '总优惠', content: '包含会员折扣、赠送卡抵扣、团购差价等所有优惠金额。\n\n优惠越高,实际收入越低。' },
|
||||
confirmed: { title: '成交/确认收入', content: '发生额减去总优惠后的实际入账金额。\n\n成交收入 = 发生额 - 总优惠' },
|
||||
cashIn: { title: '实收/现金流入', content: '实际到账的现金,包含消费直接支付和储值充值。\n\n往期为已结算金额,本期为截至当前的发生额。' },
|
||||
cashOut: { title: '现金支出', content: '包含人工、房租、水电、进货等所有经营支出。' },
|
||||
balance: { title: '现金结余', content: '现金流入减去现金支出。\n\n现金结余 = 现金流入 - 现金支出' },
|
||||
rechargeActual: { title: '储值卡充值实收', content: '会员储值卡首充和续费的实际到账金额。\n\n不含赠送金额。' },
|
||||
firstCharge: { title: '首充', content: '新会员首次充值的金额。' },
|
||||
renewCharge: { title: '续费', content: '老会员续费充值的金额。' },
|
||||
consume: { title: '消耗', content: '会员使用储值卡消费的金额。' },
|
||||
cardBalance: { title: '储值卡总余额', content: '所有储值卡的剩余可用余额。' },
|
||||
allCardBalance: { title: '全类别会员卡余额合计', content: '储值卡 + 赠送卡(酒水卡、台费卡、抵用券)的总余额。\n\n仅供经营参考,非财务属性。' },
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
pageState: 'normal' as 'loading' | 'empty' | 'error' | 'normal',
|
||||
|
||||
/** AI 配色 */
|
||||
aiColorClass: '',
|
||||
boardTabs: [] as Array<{ key: string; label: string; active: boolean }>,
|
||||
|
||||
/** 时间筛选 */
|
||||
selectedTime: 'month',
|
||||
selectedTimeText: '本月',
|
||||
timeOptions: [
|
||||
@@ -85,7 +57,6 @@ Page({
|
||||
{ value: 'half6', text: '最近6个月不含本月' },
|
||||
],
|
||||
|
||||
/** 区域筛选 */
|
||||
selectedArea: 'all',
|
||||
selectedAreaText: '全部区域',
|
||||
areaOptions: [
|
||||
@@ -94,14 +65,16 @@ Page({
|
||||
{ value: 'hallA', text: 'A区' },
|
||||
{ value: 'hallB', text: 'B区' },
|
||||
{ value: 'hallC', text: 'C区' },
|
||||
{ value: 'vip', text: '台球包厢' },
|
||||
{ value: 'snooker', text: '斯诺克' },
|
||||
{ value: 'mahjong', text: '麻将房' },
|
||||
{ value: 'teamBuilding', text: '团建房' },
|
||||
{ value: 'ktv', text: '团建房' },
|
||||
],
|
||||
|
||||
/** 环比开关 */
|
||||
compareEnabled: false,
|
||||
isCurrentMonth: true,
|
||||
aiInsights: [] as Array<{ icon: string; text: string }>,
|
||||
|
||||
/** 目录导航 */
|
||||
tocVisible: false,
|
||||
tocItems: [
|
||||
{ emoji: '📈', title: '经营一览', sectionId: 'section-overview' },
|
||||
@@ -113,191 +86,116 @@ Page({
|
||||
] as TocItem[],
|
||||
currentSectionIndex: 0,
|
||||
|
||||
/** P1: 吸顶板块头(H5: scaleX 从左滑入,同时筛选按钮 opacity 淡出) */
|
||||
stickyHeaderVisible: false,
|
||||
stickyHeaderEmoji: '',
|
||||
stickyHeaderTitle: '',
|
||||
stickyHeaderDesc: '',
|
||||
|
||||
/** 提示弹窗 */
|
||||
tipVisible: false,
|
||||
tipTitle: '',
|
||||
tipContent: '',
|
||||
|
||||
/** 经营一览 */
|
||||
overview: {
|
||||
occurrence: '¥823,456',
|
||||
occurrenceCompare: '12.5%',
|
||||
discount: '-¥113,336',
|
||||
discountCompare: '3.2%',
|
||||
discountRate: '13.8%',
|
||||
discountRateCompare: '1.5%',
|
||||
confirmedRevenue: '¥710,120',
|
||||
confirmedCompare: '8.7%',
|
||||
cashIn: '¥698,500',
|
||||
cashInCompare: '5.3%',
|
||||
cashOut: '¥472,300',
|
||||
cashOutCompare: '2.1%',
|
||||
cashBalance: '¥226,200',
|
||||
cashBalanceCompare: '15.2%',
|
||||
balanceRate: '32.4%',
|
||||
balanceRateCompare: '3.8%',
|
||||
occurrence: 0 as any, occurrenceCompare: '', occurrenceDown: false, occurrenceFlat: false,
|
||||
discount: 0 as any, discountCompare: '', discountDown: false, discountFlat: false,
|
||||
discountRate: 0 as any, discountRateCompare: '', discountRateDown: false, discountRateFlat: false,
|
||||
confirmedRevenue: 0 as any, confirmedCompare: '', confirmedDown: false, confirmedFlat: false,
|
||||
cashIn: 0 as any, cashInCompare: '', cashInDown: false, cashInFlat: false,
|
||||
cashOut: 0 as any, cashOutCompare: '', cashOutDown: false, cashOutFlat: false,
|
||||
cashBalance: 0 as any, cashBalanceCompare: '', cashBalanceDown: false, cashBalanceFlat: false,
|
||||
balanceRate: 0 as any, balanceRateCompare: '', balanceRateDown: false, balanceRateFlat: false,
|
||||
},
|
||||
|
||||
/** 预收资产 */
|
||||
recharge: {
|
||||
actualIncome: '¥352,800',
|
||||
actualCompare: '18.5%',
|
||||
firstCharge: '¥188,500',
|
||||
firstChargeCompare: '12.3%',
|
||||
renewCharge: '¥164,300',
|
||||
renewChargeCompare: '8.7%',
|
||||
consumed: '¥238,200',
|
||||
consumedCompare: '5.2%',
|
||||
cardBalance: '¥642,600',
|
||||
cardBalanceCompare: '11.4%',
|
||||
actualIncome: 0 as any, actualCompare: '', actualDown: false,
|
||||
firstCharge: 0 as any, firstChargeCompare: '', firstChargeDown: false,
|
||||
renewCharge: 0 as any, renewChargeCompare: '', renewChargeDown: false,
|
||||
consumed: 0 as any, consumedCompare: '', consumedDown: false,
|
||||
cardBalance: 0 as any, cardBalanceCompare: '', cardBalanceDown: false,
|
||||
allCardBalance: 0 as any, allCardBalanceCompare: '', allCardBalanceDown: false,
|
||||
giftRows: [] as Array<{
|
||||
label: string; total: string; totalCompare: string;
|
||||
wine: string; wineCompare: string;
|
||||
table: string; tableCompare: string;
|
||||
coupon: string; couponCompare: string;
|
||||
label: string; total: any; totalCompare: string;
|
||||
wine: any; wineCompare: string;
|
||||
table: any; tableCompare: string;
|
||||
coupon: any; couponCompare: string;
|
||||
}>,
|
||||
allCardBalance: '¥586,500',
|
||||
allCardBalanceCompare: '6.2%',
|
||||
},
|
||||
|
||||
/** 应计收入确认 */
|
||||
revenue: {
|
||||
structureRows: [
|
||||
{ id: 'table', name: '开台与包厢', amount: '¥358,600', discount: '-¥45,200', booked: '¥313,400', bookedCompare: '9.2%' },
|
||||
{ id: 'area-a', name: 'A区', amount: '¥118,200', discount: '-¥11,600', booked: '¥106,600', bookedCompare: '12.1%', isSub: true },
|
||||
{ id: 'area-b', name: 'B区', amount: '¥95,800', discount: '-¥11,200', booked: '¥84,600', bookedCompare: '8.5%', isSub: true },
|
||||
{ id: 'area-c', name: 'C区', amount: '¥72,600', discount: '-¥11,100', booked: '¥61,500', bookedCompare: '6.3%', isSub: true },
|
||||
{ id: 'team', name: '团建区', amount: '¥48,200', discount: '-¥6,800', booked: '¥41,400', bookedCompare: '5.8%', isSub: true },
|
||||
{ id: 'mahjong', name: '麻将区', amount: '¥23,800', discount: '-¥4,500', booked: '¥19,300', bookedCompare: '-2.1%', isSub: true },
|
||||
{ id: 'coach-basic', name: '助教', desc: '基础课', amount: '¥232,500', discount: '-', booked: '¥232,500', bookedCompare: '15.3%' },
|
||||
{ id: 'coach-incentive', name: '助教', desc: '激励课', amount: '¥112,800', discount: '-', booked: '¥112,800', bookedCompare: '8.2%' },
|
||||
{ id: 'food', name: '食品酒水', amount: '¥119,556', discount: '-¥68,136', booked: '¥51,420', bookedCompare: '6.5%' },
|
||||
],
|
||||
priceItems: [
|
||||
{ name: '开台消费', value: '¥358,600', compare: '9.2%' },
|
||||
{ name: '酒水商品', value: '¥186,420', compare: '18.5%' },
|
||||
{ name: '包厢费用', value: '¥165,636', compare: '12.1%' },
|
||||
{ name: '助教服务', value: '¥112,800', compare: '15.3%' },
|
||||
],
|
||||
totalOccurrence: '¥823,456',
|
||||
totalOccurrenceCompare: '12.5%',
|
||||
discountItems: [
|
||||
{ name: '团购优惠', value: '-¥56,200', compare: '5.2%' },
|
||||
{ name: '手动调整 + 大客户优惠', value: '-¥34,800', compare: '3.1%' },
|
||||
{ name: '赠送卡抵扣', desc: '台桌卡+酒水卡+抵用券', value: '-¥22,336', compare: '8.6%' },
|
||||
{ name: '其他优惠', desc: '免单+抹零', value: '-¥0', compare: '' },
|
||||
],
|
||||
confirmedTotal: '¥710,120',
|
||||
confirmedTotalCompare: '8.7%',
|
||||
channelItems: [
|
||||
{ name: '储值卡结算冲销', value: '¥238,200', compare: '11.2%' },
|
||||
{ name: '现金/线上支付', value: '¥345,800', compare: '7.8%' },
|
||||
{ name: '团购核销确认收入', desc: '团购成交价', value: '¥126,120', compare: '5.3%' },
|
||||
],
|
||||
structureRows: [] as any[],
|
||||
priceItems: [] as any[],
|
||||
totalOccurrence: 0 as any,
|
||||
totalOccurrenceCompare: '',
|
||||
discountItems: [] as any[],
|
||||
discountTotal: 0,
|
||||
confirmedTotal: 0 as any,
|
||||
confirmedTotalCompare: '',
|
||||
channelItems: [] as any[],
|
||||
},
|
||||
|
||||
/** 现金流入 */
|
||||
cashflow: {
|
||||
consumeItems: [
|
||||
{ name: '纸币现金', desc: '柜台现金收款', value: '¥85,600', compare: '12.3%', isDown: true },
|
||||
{ name: '线上收款', desc: '微信/支付宝/刷卡 已扣除平台服务费', value: '¥260,200', compare: '8.5%', isDown: false },
|
||||
{ name: '团购平台', desc: '美团/抖音回款 已扣除平台服务费', value: '¥126,120', compare: '15.2%', isDown: false },
|
||||
],
|
||||
rechargeItems: [
|
||||
{ name: '会员充值到账', desc: '首充/续费实收', value: '¥352,800', compare: '18.5%' },
|
||||
],
|
||||
total: '¥824,720',
|
||||
totalCompare: '10.2%',
|
||||
consumeItems: [] as any[],
|
||||
rechargeItems: [] as any[],
|
||||
total: 0 as any,
|
||||
totalCompare: '',
|
||||
},
|
||||
|
||||
/** 现金流出 */
|
||||
expense: {
|
||||
operationItems: [
|
||||
{ name: '食品饮料', value: '¥108,200', compare: '4.5%', isDown: false },
|
||||
{ name: '耗材', value: '¥21,850', compare: '2.1%', isDown: true },
|
||||
{ name: '报销', value: '¥10,920', compare: '6.8%', isDown: false },
|
||||
],
|
||||
fixedItems: [
|
||||
{ name: '房租', value: '¥125,000', compare: '持平', isFlat: true },
|
||||
{ name: '水电', value: '¥24,200', compare: '3.2%', isFlat: false },
|
||||
{ name: '物业', value: '¥11,500', compare: '持平', isFlat: true },
|
||||
{ name: '人员工资', value: '¥112,000', compare: '持平', isFlat: true },
|
||||
],
|
||||
coachItems: [
|
||||
{ name: '基础课分成', value: '¥116,250', compare: '8.2%', isDown: false },
|
||||
{ name: '激励课分成', value: '¥23,840', compare: '5.6%', isDown: false },
|
||||
{ name: '充值提成', value: '¥12,640', compare: '12.3%', isDown: false },
|
||||
{ name: '额外奖金', value: '¥11,500', compare: '3.1%', isDown: true },
|
||||
],
|
||||
platformItems: [
|
||||
{ name: '汇来米', value: '¥10,680', compare: '1.5%' },
|
||||
{ name: '美团', value: '¥11,240', compare: '2.8%' },
|
||||
{ name: '抖音', value: '¥10,580', compare: '3.5%' },
|
||||
],
|
||||
total: '¥600,400',
|
||||
totalCompare: '2.1%',
|
||||
operationItems: [] as any[],
|
||||
fixedItems: [] as any[],
|
||||
coachItems: [] as any[],
|
||||
platformItems: [] as any[],
|
||||
total: 0 as any,
|
||||
totalCompare: '',
|
||||
totalDown: false,
|
||||
totalFlat: false,
|
||||
},
|
||||
|
||||
/** 助教分析 */
|
||||
coachAnalysis: {
|
||||
basic: {
|
||||
totalPay: '¥232,500',
|
||||
totalPayCompare: '15.3%',
|
||||
totalShare: '¥116,250',
|
||||
totalShareCompare: '15.3%',
|
||||
avgHourly: '¥25/h',
|
||||
avgHourlyCompare: '4.2%',
|
||||
rows: [
|
||||
{ level: '初级', pay: '¥68,600', payCompare: '12.5%', share: '¥34,300', shareCompare: '12.5%', hourly: '¥20/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
{ level: '中级', pay: '¥82,400', payCompare: '18.2%', share: '¥41,200', shareCompare: '18.2%', hourly: '¥25/h', hourlyCompare: '8.7%' },
|
||||
{ level: '高级', pay: '¥57,800', payCompare: '14.6%', share: '¥28,900', shareCompare: '14.6%', hourly: '¥30/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
{ level: '星级', pay: '¥23,700', payCompare: '3.2%', payDown: true, share: '¥11,850', shareCompare: '3.2%', shareDown: true, hourly: '¥35/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
],
|
||||
totalPay: 0 as any, totalPayCompare: '', totalPayDown: false,
|
||||
totalShare: 0 as any, totalShareCompare: '', totalShareDown: false,
|
||||
avgHourly: 0 as any, avgHourlyCompare: '', avgHourlyFlat: false,
|
||||
rows: [] as any[],
|
||||
},
|
||||
incentive: {
|
||||
totalPay: '¥112,800',
|
||||
totalPayCompare: '8.2%',
|
||||
totalShare: '¥33,840',
|
||||
totalShareCompare: '8.2%',
|
||||
avgHourly: '¥15/h',
|
||||
avgHourlyCompare: '2.1%',
|
||||
rows: [
|
||||
{ level: '初级', pay: '¥32,400', payCompare: '6.8%', share: '¥9,720', shareCompare: '6.8%', hourly: '¥12/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
{ level: '中级', pay: '¥38,600', payCompare: '10.5%', share: '¥11,580', shareCompare: '10.5%', hourly: '¥15/h', hourlyCompare: '5.2%' },
|
||||
{ level: '高级', pay: '¥28,200', payCompare: '7.3%', share: '¥8,460', shareCompare: '7.3%', hourly: '¥18/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
{ level: '星级', pay: '¥13,600', payCompare: '2.1%', payDown: true, share: '¥4,080', shareCompare: '2.1%', shareDown: true, hourly: '¥22/h', hourlyCompare: '持平', hourlyFlat: true },
|
||||
],
|
||||
totalPay: 0 as any, totalPayCompare: '', totalPayDown: false,
|
||||
totalShare: 0 as any, totalShareCompare: '', totalShareDown: false,
|
||||
avgHourly: 0 as any, avgHourlyCompare: '', avgHourlyFlat: false,
|
||||
rows: [] as any[],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// P5: AI 配色
|
||||
const aiColor = getRandomAiColor()
|
||||
this.setData({ aiColorClass: aiColor.className })
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 同步 custom-tab-bar 选中态
|
||||
const tabBar = this.getTabBar?.()
|
||||
if (tabBar) tabBar.setData({ active: 'board' })
|
||||
// 加载赠送卡矩阵真实数据
|
||||
this._loadGiftRows()
|
||||
checkPageAccess('pages/board-finance/board-finance').then((allowed) => {
|
||||
if (!allowed) return
|
||||
const TAB_LABELS: Record<string, string> = { finance: '财务', customer: '客户', coach: '助教' }
|
||||
const visibleKeys = getVisibleBoardTabs()
|
||||
this.setData({
|
||||
boardTabs: visibleKeys.map(k => ({ key: k, label: TAB_LABELS[k] || k, active: k === 'finance' })),
|
||||
})
|
||||
const tabBar = this.getTabBar?.()
|
||||
if (tabBar) tabBar.setData({ active: 'board' })
|
||||
this._loadData()
|
||||
})
|
||||
},
|
||||
|
||||
onReady() {
|
||||
// P1: 缓存各 section 的 top 位置
|
||||
this._cacheSectionPositions()
|
||||
},
|
||||
|
||||
/** P1/P2: 页面滚动监听(节流 100ms)— 匹配 H5 原型行为 */
|
||||
/* CHANGE 2026-03-13 | intent: H5 原型下滑→显示吸顶头+隐藏筛选按钮,上滑→隐藏吸顶头+恢复筛选按钮;不再使用独立的 filterBarHidden 状态 */
|
||||
onPageScroll(e: { scrollTop: number }) {
|
||||
const now = Date.now()
|
||||
if (now - this._lastScrollTime < 100) return
|
||||
@@ -307,10 +205,8 @@ Page({
|
||||
const isScrollingDown = scrollTop > this._lastScrollTop
|
||||
this._lastScrollTop = scrollTop
|
||||
|
||||
// P1: 吸顶板块头 — 与 H5 updateStickyHeader 逻辑对齐
|
||||
if (this._sectionTops.length === 0) return
|
||||
|
||||
// 偏移量:tabs(~78rpx) + filter-bar(~70rpx) 约 148rpx ≈ 93px,取 100 作为阈值
|
||||
const offset = 100
|
||||
let currentIdx = 0
|
||||
for (let i = this._sectionTops.length - 1; i >= 0; i--) {
|
||||
@@ -320,7 +216,6 @@ Page({
|
||||
}
|
||||
}
|
||||
|
||||
// H5: scrollY < 80 时隐藏吸顶头
|
||||
if (scrollTop < 80) {
|
||||
if (this.data.stickyHeaderVisible) {
|
||||
this.setData({ stickyHeaderVisible: false })
|
||||
@@ -331,7 +226,6 @@ Page({
|
||||
const toc = this.data.tocItems[currentIdx]
|
||||
|
||||
if (isScrollingDown && !this.data.stickyHeaderVisible) {
|
||||
// H5: 下滑且吸顶头未显示 → 显示吸顶头(筛选按钮通过 CSS opacity 自动淡出)
|
||||
this.setData({
|
||||
stickyHeaderVisible: true,
|
||||
stickyHeaderEmoji: toc?.emoji || '',
|
||||
@@ -340,10 +234,8 @@ Page({
|
||||
currentSectionIndex: currentIdx,
|
||||
})
|
||||
} else if (!isScrollingDown && this.data.stickyHeaderVisible) {
|
||||
// H5: 上滑且吸顶头显示 → 隐藏吸顶头(筛选按钮通过 CSS opacity 自动恢复)
|
||||
this.setData({ stickyHeaderVisible: false })
|
||||
} else if (this.data.stickyHeaderVisible && currentIdx !== this.data.currentSectionIndex) {
|
||||
// H5: 吸顶头显示时板块切换 → 更新内容
|
||||
this.setData({
|
||||
stickyHeaderEmoji: toc?.emoji || '',
|
||||
stickyHeaderTitle: toc?.title || '',
|
||||
@@ -353,12 +245,10 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
/** 缓存 section 位置(私有) */
|
||||
_sectionTops: [] as number[],
|
||||
_lastScrollTop: 0,
|
||||
_lastScrollTime: 0,
|
||||
|
||||
/** H5 原型吸顶头包含板块描述,从 data-section-desc 映射 */
|
||||
_sectionDescs: [
|
||||
'快速了解收入与现金流的整体健康度',
|
||||
'会员卡充值与余额 掌握资金沉淀',
|
||||
@@ -384,135 +274,260 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 从 Finance_Board_API 加载赠送卡矩阵数据
|
||||
* 字段映射:liquor→wine、tableFee→table、voucher→coupon
|
||||
*/
|
||||
async _loadGiftRows() {
|
||||
// CHANGE 2026-03-28 | 环比修复 | 绑定所有板块 is_down/is_flat,修复箭头方向硬编码问题
|
||||
async _loadData() {
|
||||
wx.showLoading({ title: '加载中' })
|
||||
this.setData({ pageState: 'loading' })
|
||||
try {
|
||||
const data = await fetchBoardFinance({
|
||||
time: this.data.selectedTime,
|
||||
area: this.data.selectedArea,
|
||||
compare: this.data.compareEnabled ? 1 : 0,
|
||||
})
|
||||
// API 返回 camelCase:giftRows[].liquor/tableFee/voucher,每个是 GiftCell {value, compare?, down?, flat?}
|
||||
const rechargePanel = data.rechargePanel || data
|
||||
const apiRows = rechargePanel.giftRows || []
|
||||
const giftRows = apiRows.map((row: any) => ({
|
||||
label: row.label || '',
|
||||
total: formatMoney(row.total?.value),
|
||||
totalCompare: row.total?.compare || '',
|
||||
wine: formatMoney(row.liquor?.value),
|
||||
wineCompare: row.liquor?.compare || '',
|
||||
table: formatMoney(row.tableFee?.value),
|
||||
tableCompare: row.tableFee?.compare || '',
|
||||
coupon: formatMoney(row.voucher?.value),
|
||||
couponCompare: row.voucher?.compare || '',
|
||||
|
||||
// 1. 经营一览
|
||||
const ov = data.overview || {} as any
|
||||
this.setData({
|
||||
'overview.occurrence': ov.occurrence ?? 0,
|
||||
'overview.discount': ov.discount ?? 0,
|
||||
'overview.discountRate': (ov.discountRate ?? 0) * 100,
|
||||
'overview.confirmedRevenue': ov.confirmedRevenue ?? 0,
|
||||
'overview.cashIn': ov.cashIn ?? 0,
|
||||
'overview.cashOut': ov.cashOut ?? 0,
|
||||
'overview.cashBalance': ov.cashBalance ?? 0,
|
||||
'overview.balanceRate': (ov.balanceRate ?? 0) * 100,
|
||||
'overview.occurrenceCompare': ov.occurrenceCompare || '',
|
||||
'overview.occurrenceDown': ov.occurrenceDown ?? false,
|
||||
'overview.occurrenceFlat': ov.occurrenceFlat ?? false,
|
||||
'overview.discountCompare': ov.discountCompare || '',
|
||||
'overview.discountDown': ov.discountDown ?? false,
|
||||
'overview.discountFlat': ov.discountFlat ?? false,
|
||||
'overview.discountRateCompare': ov.discountRateCompare || '',
|
||||
'overview.discountRateDown': ov.discountRateDown ?? false,
|
||||
'overview.discountRateFlat': ov.discountRateFlat ?? false,
|
||||
'overview.confirmedCompare': ov.confirmedRevenueCompare || '',
|
||||
'overview.confirmedDown': ov.confirmedRevenueDown ?? false,
|
||||
'overview.confirmedFlat': ov.confirmedRevenueFlat ?? false,
|
||||
'overview.cashInCompare': ov.cashInCompare || '',
|
||||
'overview.cashInDown': ov.cashInDown ?? false,
|
||||
'overview.cashInFlat': ov.cashInFlat ?? false,
|
||||
'overview.cashOutCompare': ov.cashOutCompare || '',
|
||||
'overview.cashOutDown': ov.cashOutDown ?? false,
|
||||
'overview.cashOutFlat': ov.cashOutFlat ?? false,
|
||||
'overview.cashBalanceCompare': ov.cashBalanceCompare || '',
|
||||
'overview.cashBalanceDown': ov.cashBalanceDown ?? false,
|
||||
'overview.cashBalanceFlat': ov.cashBalanceFlat ?? false,
|
||||
'overview.balanceRateCompare': ov.balanceRateCompare || '',
|
||||
'overview.balanceRateDown': ov.balanceRateDown ?? false,
|
||||
'overview.balanceRateFlat': ov.balanceRateFlat ?? false,
|
||||
})
|
||||
|
||||
// 2. 预收资产(area=all 时后端才返回)
|
||||
if (data.recharge) {
|
||||
const rc = data.recharge as any
|
||||
const giftRows = (rc.giftRows || []).map((row: any) => ({
|
||||
label: row.label || '',
|
||||
total: row.total?.value ?? 0,
|
||||
totalCompare: row.total?.compare || '',
|
||||
wine: row.liquor?.value ?? 0,
|
||||
wineCompare: row.liquor?.compare || '',
|
||||
table: row.tableFee?.value ?? 0,
|
||||
tableCompare: row.tableFee?.compare || '',
|
||||
coupon: row.voucher?.value ?? 0,
|
||||
couponCompare: row.voucher?.compare || '',
|
||||
}))
|
||||
this.setData({
|
||||
'recharge.actualIncome': rc.actualIncome ?? 0,
|
||||
'recharge.firstCharge': rc.firstCharge ?? 0,
|
||||
'recharge.renewCharge': rc.renewCharge ?? 0,
|
||||
'recharge.consumed': rc.consumed ?? 0,
|
||||
'recharge.cardBalance': rc.cardBalance ?? 0,
|
||||
'recharge.allCardBalance': rc.allCardBalance ?? 0,
|
||||
'recharge.giftRows': giftRows,
|
||||
'recharge.actualCompare': rc.actualIncomeCompare || '',
|
||||
'recharge.actualDown': rc.actualIncomeDown ?? false,
|
||||
'recharge.firstChargeCompare': rc.firstChargeCompare || '',
|
||||
'recharge.firstChargeDown': rc.firstChargeDown ?? false,
|
||||
'recharge.renewChargeCompare': rc.renewChargeCompare || '',
|
||||
'recharge.renewChargeDown': rc.renewChargeDown ?? false,
|
||||
'recharge.consumedCompare': rc.consumedCompare || '',
|
||||
'recharge.consumedDown': rc.consumedDown ?? false,
|
||||
'recharge.cardBalanceCompare': rc.cardBalanceCompare || '',
|
||||
'recharge.cardBalanceDown': rc.cardBalanceDown ?? false,
|
||||
'recharge.allCardBalanceCompare': rc.allCardBalanceCompare || '',
|
||||
'recharge.allCardBalanceDown': rc.allCardBalanceDown ?? false,
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 应计收入确认
|
||||
const rv = data.revenue || {} as any
|
||||
const structureRows = (rv.structureRows || []).map((r: any) => ({
|
||||
id: r.id || '', name: r.name || '', desc: r.desc || '',
|
||||
isSub: r.isSub || false,
|
||||
amount: r.amount ?? 0, discount: r.discount ?? 0,
|
||||
booked: r.booked ?? 0, bookedCompare: r.bookedCompare || '',
|
||||
}))
|
||||
this.setData({ 'recharge.giftRows': giftRows })
|
||||
const priceItems = (rv.priceItems || []).map((r: any) => ({ name: r.label || '', value: r.amount ?? 0, compare: r.compare || '' }))
|
||||
const discountItems = (rv.discountItems || []).map((r: any) => ({ name: r.label || '', desc: r.desc || '', value: r.amount ?? 0, compare: r.compare || '' }))
|
||||
const channelItems = (rv.channelItems || []).map((r: any) => ({ name: r.label || '', desc: r.desc || '', value: r.amount ?? 0, compare: r.compare || '' }))
|
||||
this.setData({
|
||||
'revenue.structureRows': structureRows,
|
||||
'revenue.priceItems': priceItems,
|
||||
'revenue.totalOccurrence': rv.totalOccurrence ?? 0,
|
||||
// CHANGE 2026-03-28 | 环比字段映射
|
||||
'revenue.totalOccurrenceCompare': rv.totalOccurrenceCompare || '',
|
||||
'revenue.discountItems': discountItems,
|
||||
'revenue.discountTotal': rv.discountTotal ?? 0,
|
||||
'revenue.confirmedTotal': rv.confirmedTotal ?? 0,
|
||||
'revenue.confirmedTotalCompare': rv.confirmedTotalCompare || '',
|
||||
'revenue.channelItems': channelItems,
|
||||
})
|
||||
|
||||
// 4. 现金流入
|
||||
const cf = data.cashflow || {} as any
|
||||
const consumeItems = (cf.consumeItems || []).map((r: any) => ({
|
||||
name: r.label || '', desc: r.desc || '', value: r.amount ?? 0, compare: r.compare || '', isDown: r.down ?? false,
|
||||
}))
|
||||
const rechargeItems = (cf.rechargeItems || []).map((r: any) => ({
|
||||
name: r.label || '', desc: r.desc || '', value: r.amount ?? 0, compare: r.compare || '',
|
||||
}))
|
||||
this.setData({
|
||||
'cashflow.consumeItems': consumeItems,
|
||||
'cashflow.rechargeItems': rechargeItems,
|
||||
'cashflow.total': cf.total ?? 0,
|
||||
// CHANGE 2026-03-28 | 环比字段映射
|
||||
'cashflow.totalCompare': cf.totalCompare || '',
|
||||
})
|
||||
|
||||
// 5. 现金流出
|
||||
const ex = data.expense || {} as any
|
||||
const mapExpenseItems = (items: any[]) => (items || []).map((r: any) => ({
|
||||
name: r.label || '', value: r.amount ?? 0,
|
||||
compare: r.compare || '',
|
||||
isDown: r.down ?? false,
|
||||
isFlat: r.flat ?? false,
|
||||
}))
|
||||
this.setData({
|
||||
'expense.operationItems': mapExpenseItems(ex.operationItems),
|
||||
'expense.fixedItems': mapExpenseItems(ex.fixedItems),
|
||||
'expense.coachItems': mapExpenseItems(ex.coachItems),
|
||||
'expense.platformItems': mapExpenseItems(ex.platformItems),
|
||||
'expense.total': ex.total ?? 0,
|
||||
'expense.totalCompare': ex.totalCompare || '',
|
||||
'expense.totalDown': ex.totalDown ?? false,
|
||||
'expense.totalFlat': ex.totalFlat ?? false,
|
||||
})
|
||||
|
||||
// 6. 助教分析
|
||||
const ca = data.coachAnalysis || {} as any
|
||||
const mapCoachTable = (table: any) => {
|
||||
if (!table) return { totalPay: 0, totalPayCompare: '', totalPayDown: false, totalShare: 0, totalShareCompare: '', totalShareDown: false, avgHourly: 0, avgHourlyCompare: '', avgHourlyFlat: false, rows: [] }
|
||||
return {
|
||||
totalPay: table.totalPay ?? 0,
|
||||
totalPayCompare: table.totalPayCompare || '',
|
||||
totalPayDown: table.totalPayDown ?? false,
|
||||
totalShare: table.totalShare ?? 0,
|
||||
totalShareCompare: table.totalShareCompare || '',
|
||||
totalShareDown: table.totalShareDown ?? false,
|
||||
avgHourly: table.avgHourly ?? 0,
|
||||
avgHourlyCompare: table.avgHourlyCompare || '',
|
||||
avgHourlyFlat: table.avgHourlyFlat ?? false,
|
||||
rows: (table.rows || []).map((r: any) => ({
|
||||
level: r.level || '',
|
||||
pay: r.pay ?? 0,
|
||||
payCompare: r.payCompare || '',
|
||||
payDown: r.payDown ?? false,
|
||||
share: r.share ?? 0,
|
||||
shareCompare: r.shareCompare || '',
|
||||
shareDown: r.shareDown ?? false,
|
||||
hourly: r.hourly ?? 0,
|
||||
hourlyCompare: r.hourlyCompare || '',
|
||||
hourlyFlat: r.hourlyFlat ?? false,
|
||||
})),
|
||||
}
|
||||
}
|
||||
this.setData({
|
||||
coachAnalysis: {
|
||||
basic: mapCoachTable(ca.basic),
|
||||
incentive: mapCoachTable(ca.incentive),
|
||||
},
|
||||
})
|
||||
|
||||
const aiInsights = (data.aiInsights || []) as Array<{ icon: string; text: string }>
|
||||
this.setData({ aiInsights, pageState: 'normal' })
|
||||
|
||||
} catch (err) {
|
||||
console.error('[board-finance] 赠送卡数据加载失败', err)
|
||||
// 加载失败时清空 giftRows,不显示 mock 数据
|
||||
this.setData({ 'recharge.giftRows': [] })
|
||||
wx.showToast({ title: '赠送卡数据加载失败', icon: 'none', duration: 2000 })
|
||||
console.error('[board-finance] 数据加载失败', err)
|
||||
this.setData({ pageState: 'error' })
|
||||
wx.showToast({ title: '数据加载失败', icon: 'none', duration: 2000 })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this._loadGiftRows()
|
||||
this._loadData()
|
||||
setTimeout(() => wx.stopPullDownRefresh(), 500)
|
||||
},
|
||||
|
||||
/** 看板 Tab 切换 */
|
||||
// CHANGE 2026-03-29 | P1:redirectTo 替代 navigateTo
|
||||
onTabChange(e: WechatMiniprogram.TouchEvent) {
|
||||
const tab = e.currentTarget.dataset.tab as string
|
||||
if (tab === 'customer') {
|
||||
wx.navigateTo({ url: '/pages/board-customer/board-customer' })
|
||||
} else if (tab === 'coach') {
|
||||
wx.navigateTo({ url: '/pages/board-coach/board-coach' })
|
||||
if (tab === 'finance') return
|
||||
const routes: Record<string, { url: string; isTab: boolean }> = {
|
||||
finance: { url: '/pages/board-finance/board-finance', isTab: true },
|
||||
customer: { url: '/pages/board-customer/board-customer', isTab: false },
|
||||
coach: { url: '/pages/board-coach/board-coach', isTab: false },
|
||||
}
|
||||
const route = routes[tab]
|
||||
if (!route) return
|
||||
if (route.isTab) { wx.switchTab({ url: route.url }) } else { wx.redirectTo({ url: route.url }) }
|
||||
},
|
||||
|
||||
/** 时间筛选变更 */
|
||||
onTimeChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||||
const value = e.detail.value
|
||||
const option = this.data.timeOptions.find(o => o.value === value)
|
||||
this.setData({
|
||||
selectedTime: value,
|
||||
selectedTimeText: option?.text || '本月',
|
||||
})
|
||||
this.setData({ selectedTime: value, selectedTimeText: option?.text || '本月', isCurrentMonth: isCurrentMonthFilter(value) })
|
||||
this._loadData()
|
||||
},
|
||||
|
||||
/** 区域筛选变更 */
|
||||
onAreaChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
||||
const value = e.detail.value
|
||||
const option = this.data.areaOptions.find(o => o.value === value)
|
||||
this.setData({
|
||||
selectedArea: value,
|
||||
selectedAreaText: option?.text || '全部区域',
|
||||
})
|
||||
// P3: 区域变更后重新缓存 section 位置(预收资产可能隐藏/显示)
|
||||
this.setData({ selectedArea: value, selectedAreaText: option?.text || '全部区域' })
|
||||
this._loadData()
|
||||
setTimeout(() => this._cacheSectionPositions(), 300)
|
||||
},
|
||||
|
||||
/** 环比开关切换 */
|
||||
toggleCompare() {
|
||||
this.setData({ compareEnabled: !this.data.compareEnabled })
|
||||
this._loadData()
|
||||
},
|
||||
|
||||
/** 目录导航开关 */
|
||||
toggleToc() {
|
||||
this.setData({ tocVisible: !this.data.tocVisible })
|
||||
},
|
||||
toggleToc() { this.setData({ tocVisible: !this.data.tocVisible }) },
|
||||
closeToc() { this.setData({ tocVisible: false }) },
|
||||
|
||||
closeToc() {
|
||||
this.setData({ tocVisible: false })
|
||||
},
|
||||
|
||||
/** 目录项点击 → 滚动到对应板块(P0: 使用 pageScrollTo 替代 scrollIntoView) */
|
||||
onTocItemTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const index = e.currentTarget.dataset.index as number
|
||||
const sectionId = this.data.tocItems[index]?.sectionId
|
||||
if (sectionId) {
|
||||
this.setData({
|
||||
tocVisible: false,
|
||||
currentSectionIndex: index,
|
||||
})
|
||||
wx.createSelectorQuery().in(this)
|
||||
.select(`#${sectionId}`)
|
||||
.boundingClientRect((rect) => {
|
||||
if (rect) {
|
||||
wx.pageScrollTo({
|
||||
scrollTop: rect.top + (this._lastScrollTop || 0) - 140,
|
||||
duration: 300,
|
||||
})
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
this.setData({ tocVisible: false, currentSectionIndex: index })
|
||||
wx.createSelectorQuery().in(this).select(`#${sectionId}`).boundingClientRect((rect) => {
|
||||
if (rect) wx.pageScrollTo({ scrollTop: rect.top + (this._lastScrollTop || 0) - 140, duration: 300 })
|
||||
}).exec()
|
||||
}
|
||||
},
|
||||
|
||||
/** 帮助图标点击 → 弹出说明 */
|
||||
onHelpTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const key = e.currentTarget.dataset.key as string
|
||||
const tip = tipContents[key]
|
||||
if (tip) {
|
||||
this.setData({
|
||||
tipVisible: true,
|
||||
tipTitle: tip.title,
|
||||
tipContent: tip.content,
|
||||
})
|
||||
}
|
||||
if (tip) this.setData({ tipVisible: true, tipTitle: tip.title, tipContent: tip.content })
|
||||
},
|
||||
|
||||
/** 关闭提示弹窗 */
|
||||
closeTip() {
|
||||
this.setData({ tipVisible: false })
|
||||
},
|
||||
closeTip() { this.setData({ tipVisible: false }) },
|
||||
|
||||
/** P4: 错误态重试 */
|
||||
onRetry() {
|
||||
this.setData({ pageState: 'normal' })
|
||||
this._loadGiftRows()
|
||||
this._loadData()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
<!-- 财务看板页 — 忠于 H5 原型结构 -->
|
||||
|
||||
<!-- 加载态(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>
|
||||
<!-- CHANGE 2026-03-28 | 加载中禁止滚动 -->
|
||||
<page-meta page-style="{{pageState === 'loading' ? 'overflow:hidden;' : ''}}" />
|
||||
<wxs src="../../utils/format.wxs" module="fmt" />
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="page-empty" wx:elif="{{pageState === 'empty'}}">
|
||||
<view class="page-empty" wx:if="{{pageState === 'empty'}}">
|
||||
<t-empty description="暂无财务数据" />
|
||||
</view>
|
||||
|
||||
@@ -23,16 +18,16 @@
|
||||
|
||||
<!-- 正常态 -->
|
||||
<block wx:else>
|
||||
<!-- 顶部看板 Tab 导航 -->
|
||||
<view class="board-tabs">
|
||||
<view class="board-tab board-tab--active" data-tab="finance">
|
||||
<text>财务</text>
|
||||
</view>
|
||||
<view class="board-tab" data-tab="customer" bindtap="onTabChange">
|
||||
<text>客户</text>
|
||||
</view>
|
||||
<view class="board-tab" data-tab="coach" bindtap="onTabChange">
|
||||
<text>助教</text>
|
||||
<!-- CHANGE 2026-03-27 | 权限改造 W5:动态看板二级 tab,均分宽度居中 -->
|
||||
<view class="board-tabs board-tabs--{{boardTabs.length}}">
|
||||
<view
|
||||
wx:for="{{boardTabs}}"
|
||||
wx:key="key"
|
||||
class="board-tab {{item.active ? 'board-tab--active' : ''}}"
|
||||
data-tab="{{item.key}}"
|
||||
bindtap="{{item.active ? '' : 'onTabChange'}}"
|
||||
>
|
||||
<text>{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -96,14 +91,14 @@
|
||||
<view class="card-header-dark">
|
||||
<text class="card-header-emoji">📈</text>
|
||||
<view class="card-header-text">
|
||||
<text class="card-header-title-dark">经营一览</text>
|
||||
<text class="card-header-title-dark">经营一览{{isCurrentMonth ? '(预估)' : ''}}</text>
|
||||
<text class="card-header-desc-dark">快速了解收入与现金流的整体健康度</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收入概览 -->
|
||||
<view class="sub-section-label">
|
||||
<text class="sub-label-text">收入概览</text>
|
||||
<text class="sub-label-text">收入记账</text>
|
||||
<text class="sub-label-desc">记账口径收入与优惠</text>
|
||||
</view>
|
||||
|
||||
@@ -113,9 +108,9 @@
|
||||
<text class="cell-label-light">发生额/正价</text>
|
||||
<view class="help-icon-light" data-key="occurrence" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-white">{{overview.occurrence}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.occurrenceCompare}}</text>
|
||||
<text class="cell-value-white">{{fmt.money(overview.occurrence)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.occurrenceCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.occurrenceCompare, overview.occurrenceDown, 'xs')}}">{{fmt.compareText(overview.occurrenceCompare, overview.occurrenceDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-cell">
|
||||
@@ -123,18 +118,18 @@
|
||||
<text class="cell-label-light">总优惠</text>
|
||||
<view class="help-icon-light" data-key="discount" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-red">{{overview.discount}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-down">↓{{overview.discountCompare}}</text>
|
||||
<text class="cell-value-red">{{fmt.money(overview.discount)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.discountCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.discountCompare, overview.discountDown, 'xs')}}">{{fmt.compareText(overview.discountCompare, overview.discountDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-cell">
|
||||
<view class="cell-label-row">
|
||||
<text class="cell-label-light">优惠占比</text>
|
||||
</view>
|
||||
<text class="cell-value-gray">{{overview.discountRate}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-down">↓{{overview.discountRateCompare}}</text>
|
||||
<text class="cell-value-gray">{{fmt.percent(overview.discountRate)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.discountRateCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.discountRateCompare, overview.discountRateDown, 'xs')}}">{{fmt.compareText(overview.discountRateCompare, overview.discountRateDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -146,19 +141,20 @@
|
||||
<view class="help-icon-light" data-key="confirmed" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<view class="confirmed-right">
|
||||
<text class="confirmed-value">{{overview.confirmedRevenue}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.confirmedCompare}}</text>
|
||||
<text class="confirmed-value">{{fmt.money(overview.confirmedRevenue)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled && overview.confirmedCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.confirmedCompare, overview.confirmedDown, 'xs')}}">{{fmt.compareText(overview.confirmedCompare, overview.confirmedDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider-light"></view>
|
||||
|
||||
<!-- 现金流水概览 -->
|
||||
<!-- 现金流水概览(仅"全部区域"时显示) -->
|
||||
<view wx:if="{{selectedArea === 'all'}}">
|
||||
<view class="sub-section-label">
|
||||
<text class="sub-label-text">现金流水概览</text>
|
||||
<text class="sub-label-desc">往期为已结算 本期为截至当前的发生额</text>
|
||||
<text class="sub-label-text">实收流水</text>
|
||||
<text class="sub-label-desc">实际有多少钱的收入。往期为已结算,本期为截至当前的发生额</text>
|
||||
</view>
|
||||
|
||||
<view class="overview-grid-2">
|
||||
@@ -167,9 +163,9 @@
|
||||
<text class="cell-label-light">实收/现金流入</text>
|
||||
<view class="help-icon-light" data-key="cashIn" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-white-sm">{{overview.cashIn}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.cashInCompare}}</text>
|
||||
<text class="cell-value-white-sm">{{fmt.money(overview.cashIn)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.cashInCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.cashInCompare, overview.cashInDown, 'xs')}}">{{fmt.compareText(overview.cashInCompare, overview.cashInDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-cell-bg">
|
||||
@@ -177,9 +173,9 @@
|
||||
<text class="cell-label-light">现金支出</text>
|
||||
<view class="help-icon-light" data-key="cashOut" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-gray-sm">{{overview.cashOut}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.cashOutCompare}}</text>
|
||||
<text class="cell-value-gray-sm">{{fmt.money(overview.cashOut)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.cashOutCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.cashOutCompare, overview.cashOutDown, 'xs')}}">{{fmt.compareText(overview.cashOutCompare, overview.cashOutDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-cell-bg">
|
||||
@@ -187,24 +183,26 @@
|
||||
<text class="cell-label-light">现金结余</text>
|
||||
<view class="help-icon-light" data-key="balance" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-white-sm">{{overview.cashBalance}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.cashBalanceCompare}}</text>
|
||||
<text class="cell-value-white-sm">{{fmt.money(overview.cashBalance)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.cashBalanceCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.cashBalanceCompare, overview.cashBalanceDown, 'xs')}}">{{fmt.compareText(overview.cashBalanceCompare, overview.cashBalanceDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-cell-bg">
|
||||
<view class="cell-label-row">
|
||||
<text class="cell-label-light">结余率</text>
|
||||
</view>
|
||||
<text class="cell-value-white-sm">{{overview.balanceRate}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up">↑{{overview.balanceRateCompare}}</text>
|
||||
<text class="cell-value-white-sm">{{fmt.percent(overview.balanceRate)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled && overview.balanceRateCompare}}">
|
||||
<text class="{{fmt.compareClass(overview.balanceRateCompare, overview.balanceRateDown, 'xs')}}">{{fmt.compareText(overview.balanceRateCompare, overview.balanceRateDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- AI 洞察 -->
|
||||
<!-- CHANGE 2026-03-12 | intent: H5 原型使用 SVG 机器人图标,不可用 emoji 替代;规范要求内联 SVG 导出为文件用 image 引用 -->
|
||||
<!-- CHANGE 2026-03-21 | P13 T6.1: AI 洞察改为动态渲染,移除硬编码文案 -->
|
||||
<view class="ai-insight-section">
|
||||
<view class="ai-insight-header">
|
||||
<view class="ai-insight-icon">
|
||||
@@ -212,11 +210,11 @@
|
||||
</view>
|
||||
<text class="ai-insight-title">AI 智能洞察</text>
|
||||
</view>
|
||||
<view class="ai-insight-body">
|
||||
<text class="ai-insight-line"><text class="ai-insight-dim">优惠率Top:</text>团购(5.2%) / 大客户(3.1%) / 赠送卡(2.5%)</text>
|
||||
<text class="ai-insight-line"><text class="ai-insight-dim">差异最大:</text>酒水(+18%) / 台桌(-5%) / 包厢(+12%)</text>
|
||||
<!-- CHANGE 2026-03-12 | intent: H5 原型第三行"充值高但消耗低"有 underline 样式 -->
|
||||
<text class="ai-insight-line"><text class="ai-insight-dim">建议关注:</text><text class="ai-insight-underline">充值高但消耗低</text>,会员活跃度需提升</text>
|
||||
<view class="ai-insight-body" wx:if="{{aiInsights.length > 0}}">
|
||||
<text class="ai-insight-line" wx:for="{{aiInsights}}" wx:key="index"><text class="ai-insight-dim">{{item.icon}} </text>{{item.text}}</text>
|
||||
</view>
|
||||
<view class="ai-insight-body" wx:else>
|
||||
<text class="ai-insight-line ai-insight-dim">暂无洞察数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -244,9 +242,9 @@
|
||||
<text class="total-balance-note">仅经营参考,非财务属性</text>
|
||||
</view>
|
||||
<view class="total-balance-right">
|
||||
<text class="total-balance-value">{{recharge.allCardBalance}}</text>
|
||||
<text class="total-balance-value">{{fmt.money(recharge.allCardBalance)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{recharge.allCardBalanceCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.allCardBalanceCompare, recharge.allCardBalanceDown, 'sm')}}">{{fmt.compareText(recharge.allCardBalanceCompare, recharge.allCardBalanceDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -259,9 +257,9 @@
|
||||
<view class="help-icon-dark" data-key="rechargeActual" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<view class="table-row-right">
|
||||
<text class="table-row-value-lg">{{recharge.actualIncome}}</text>
|
||||
<text class="table-row-value-lg">{{fmt.money(recharge.actualIncome)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{recharge.actualCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.actualCompare, recharge.actualDown, 'sm')}}">{{fmt.compareText(recharge.actualCompare, recharge.actualDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -272,9 +270,9 @@
|
||||
<text class="cell-label-sm">首充</text>
|
||||
<view class="help-icon-dark-sm" data-key="firstCharge" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-sm">{{recharge.firstCharge}}</text>
|
||||
<text class="cell-value-sm">{{fmt.money(recharge.firstCharge)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{recharge.firstChargeCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.firstChargeCompare, recharge.firstChargeDown, 'xs')}}">{{fmt.compareText(recharge.firstChargeCompare, recharge.firstChargeDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="grid-cell">
|
||||
@@ -282,9 +280,9 @@
|
||||
<text class="cell-label-sm">续费</text>
|
||||
<view class="help-icon-dark-sm" data-key="renewCharge" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-sm">{{recharge.renewCharge}}</text>
|
||||
<text class="cell-value-sm">{{fmt.money(recharge.renewCharge)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{recharge.renewChargeCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.renewChargeCompare, recharge.renewChargeDown, 'xs')}}">{{fmt.compareText(recharge.renewChargeCompare, recharge.renewChargeDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="grid-cell">
|
||||
@@ -292,9 +290,9 @@
|
||||
<text class="cell-label-sm">消耗</text>
|
||||
<view class="help-icon-dark-sm" data-key="consume" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<text class="cell-value-sm">{{recharge.consumed}}</text>
|
||||
<text class="cell-value-sm">{{fmt.money(recharge.consumed)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{recharge.consumedCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.consumedCompare, recharge.consumedDown, 'xs')}}">{{fmt.compareText(recharge.consumedCompare, recharge.consumedDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -305,9 +303,9 @@
|
||||
<view class="help-icon-dark" data-key="cardBalance" bindtap="onHelpTap">?</view>
|
||||
</view>
|
||||
<view class="table-row-right">
|
||||
<text class="table-row-value-lg">{{recharge.cardBalance}}</text>
|
||||
<text class="table-row-value-lg">{{fmt.money(recharge.cardBalance)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{recharge.cardBalanceCompare}}</text>
|
||||
<text class="{{fmt.compareClass(recharge.cardBalanceCompare, recharge.cardBalanceDown, 'sm')}}">{{fmt.compareText(recharge.cardBalanceCompare, recharge.cardBalanceDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -328,30 +326,31 @@
|
||||
<!-- 左列:标题 + 环比 / 总金额 -->
|
||||
<view class="gift-col gift-col--name">
|
||||
<view class="gift-label-line">
|
||||
<text class="gift-row-label">{{item.label}}</text>
|
||||
<text class="compare-text-up-xs" wx:if="{{compareEnabled}}">↑{{item.totalCompare}}</text>
|
||||
<text class="gift-row-label">{{fmt.safe(item.label)}}</text>
|
||||
|
||||
</view>
|
||||
<text class="gift-row-total">{{item.total}}</text>
|
||||
<text class="gift-row-total">{{fmt.safe(item.total)}}</text>
|
||||
<text class="{{fmt.compareClass(item.totalCompare, false, 'xs')}}" wx:if="{{compareEnabled}}">{{fmt.compareText(item.totalCompare, false)}}</text>
|
||||
</view>
|
||||
<!-- 酒水卡 -->
|
||||
<view class="gift-col">
|
||||
<text class="gift-col-val">{{item.wine}}</text>
|
||||
<text class="gift-col-val">{{fmt.safe(item.wine)}}</text>
|
||||
<view class="gift-label-line" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.wineCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.wineCompare, false, 'xs')}}">{{fmt.compareText(item.wineCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 台费卡 -->
|
||||
<view class="gift-col">
|
||||
<text class="gift-col-val">{{item.table}}</text>
|
||||
<text class="gift-col-val">{{fmt.safe(item.table)}}</text>
|
||||
<view class="gift-label-line" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.tableCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.tableCompare, false, 'xs')}}">{{fmt.compareText(item.tableCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 抵用券 -->
|
||||
<view class="gift-col">
|
||||
<text class="gift-col-val">{{item.coupon}}</text>
|
||||
<text class="gift-col-val">{{fmt.safe(item.coupon)}}</text>
|
||||
<view class="gift-label-line" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.couponCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.couponCompare, false, 'xs')}}">{{fmt.compareText(item.couponCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -394,12 +393,12 @@
|
||||
<text class="{{item.isSub ? 'rev-name-sub' : 'rev-name'}}">{{item.name}}</text>
|
||||
<text class="rev-name-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
</view>
|
||||
<text class="rev-col rev-val">{{item.amount}}</text>
|
||||
<text class="rev-col rev-val {{item.discount !== '-' ? 'rev-val--red' : 'rev-val--muted'}}">{{item.discount}}</text>
|
||||
<text class="rev-col rev-val">{{fmt.money(item.amount)}}</text>
|
||||
<text class="rev-col rev-val {{item.discount > 0 ? 'rev-val--red' : 'rev-val--muted'}}">{{item.discount > 0 ? fmt.negativeMoney(item.discount) : '-'}}</text>
|
||||
<view class="rev-col">
|
||||
<text class="rev-val {{item.isSub ? '' : 'rev-val--bold'}}">{{item.booked}}</text>
|
||||
<text class="rev-val {{item.isSub ? '' : 'rev-val--bold'}}">{{fmt.money(item.booked)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled && item.bookedCompare}}">
|
||||
<text class="compare-text-up-xs">↑{{item.bookedCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.bookedCompare, false, 'xs')}}">{{fmt.compareText(item.bookedCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -421,11 +420,11 @@
|
||||
<!-- 正价明细(左侧竖线) -->
|
||||
<view class="flow-detail-list">
|
||||
<view class="flow-detail-item" wx:for="{{revenue.priceItems}}" wx:key="name">
|
||||
<text class="flow-detail-name">{{item.name}}</text>
|
||||
<text class="flow-detail-name">{{fmt.safe(item.name)}}</text>
|
||||
<view class="flow-detail-right">
|
||||
<text class="flow-detail-val">{{item.value}}</text>
|
||||
<text class="flow-detail-val">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, false, 'xs')}}">{{fmt.compareText(item.compare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -437,26 +436,27 @@
|
||||
<text class="flow-total-desc">即上列正价合计</text>
|
||||
</view>
|
||||
<view class="flow-total-right">
|
||||
<text class="flow-total-value">{{revenue.totalOccurrence}}</text>
|
||||
<text class="flow-total-value">{{fmt.money(revenue.totalOccurrence)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{revenue.totalOccurrenceCompare}}</text>
|
||||
<text class="{{fmt.compareClass(revenue.totalOccurrenceCompare, false, 'sm')}}">{{fmt.compareText(revenue.totalOccurrenceCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 优惠扣减 -->
|
||||
<view class="flow-header flow-header--deduct">
|
||||
<text class="flow-header-title">优惠扣减</text>
|
||||
<text class="flow-header-total flow-header-total--red">{{fmt.negativeMoney(revenue.discountTotal)}}</text>
|
||||
</view>
|
||||
<view class="flow-detail-list">
|
||||
<view class="flow-detail-item" wx:for="{{revenue.discountItems}}" wx:key="name">
|
||||
<view class="flow-detail-name-group">
|
||||
<text class="flow-detail-name">{{item.name}}</text>
|
||||
<text class="flow-detail-name-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
<text class="flow-detail-name">{{fmt.safe(item.name)}}</text>
|
||||
<text class="flow-detail-name-desc" wx:if="{{item.desc}}">{{fmt.safe(item.desc)}}</text>
|
||||
</view>
|
||||
<view class="flow-detail-right">
|
||||
<text class="flow-detail-val {{item.value === '-¥0' ? 'flow-detail-val--muted' : 'flow-detail-val--red'}}">{{item.value}}</text>
|
||||
<text class="flow-detail-val {{item.value === 0 ? 'flow-detail-val--muted' : 'flow-detail-val--red'}}">{{fmt.negativeMoney(item.value)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled && item.compare}}">
|
||||
<text class="compare-text-down-xs">↓{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, item.isDown, 'xs')}}">{{fmt.compareText(item.compare, item.isDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -469,12 +469,13 @@
|
||||
<text class="flow-total-desc">此金额收款渠道分布如下</text>
|
||||
</view>
|
||||
<view class="flow-total-right">
|
||||
<text class="flow-total-value">{{revenue.confirmedTotal}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{revenue.confirmedTotalCompare}}</text>
|
||||
<text class="flow-total-value">{{fmt.money(revenue.confirmedTotal)}}</text>
|
||||
<view class="compare-row-standalone" wx:if="{{compareEnabled && revenue.confirmedTotalCompare}}">
|
||||
<text class="{{fmt.compareClass(revenue.confirmedTotalCompare, revenue.confirmedTotalDown, 'sm')}}">{{fmt.compareText(revenue.confirmedTotalCompare, revenue.confirmedTotalDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收款渠道 -->
|
||||
<view class="flow-header">
|
||||
<text class="flow-header-title">收款渠道明细</text>
|
||||
@@ -482,13 +483,13 @@
|
||||
<view class="flow-detail-list">
|
||||
<view class="flow-detail-item" wx:for="{{revenue.channelItems}}" wx:key="name">
|
||||
<view class="flow-detail-name-group">
|
||||
<text class="flow-detail-name">{{item.name}}</text>
|
||||
<text class="flow-detail-name-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
<text class="flow-detail-name">{{fmt.safe(item.name)}}</text>
|
||||
<text class="flow-detail-name-desc" wx:if="{{item.desc}}">{{fmt.safe(item.desc)}}</text>
|
||||
</view>
|
||||
<view class="flow-detail-right">
|
||||
<text class="flow-detail-val">{{item.value}}</text>
|
||||
<text class="flow-detail-val">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, false, 'xs')}}">{{fmt.compareText(item.compare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -499,8 +500,8 @@
|
||||
<view class="card-tear"></view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 板块 4: 现金流入 ===== -->
|
||||
<view id="section-cashflow" class="card-section">
|
||||
<!-- ===== 板块 4: 现金流入(仅"全部区域"时显示) ===== -->
|
||||
<view id="section-cashflow" class="card-section" wx:if="{{selectedArea === 'all'}}">
|
||||
<view class="card-header-light">
|
||||
<text class="card-header-emoji">🧾</text>
|
||||
<view class="card-header-text">
|
||||
@@ -515,13 +516,13 @@
|
||||
<view class="flow-item-list">
|
||||
<view class="flow-item" wx:for="{{cashflow.consumeItems}}" wx:key="name">
|
||||
<view class="flow-item-left">
|
||||
<text class="flow-item-name">{{item.name}}</text>
|
||||
<text class="flow-item-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
<text class="flow-item-name">{{fmt.safe(item.name)}}</text>
|
||||
<text class="flow-item-desc" wx:if="{{item.desc}}">{{fmt.safe(item.desc)}}</text>
|
||||
</view>
|
||||
<view class="flow-item-right">
|
||||
<text class="flow-item-value">{{item.value}}</text>
|
||||
<text class="flow-item-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.isDown ? 'compare-text-down-xs' : 'compare-text-up-xs'}}">{{item.isDown ? '↓' : '↑'}}{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, item.isDown, 'xs')}}">{{fmt.compareText(item.compare, item.isDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -533,13 +534,13 @@
|
||||
<view class="flow-item-list">
|
||||
<view class="flow-item" wx:for="{{cashflow.rechargeItems}}" wx:key="name">
|
||||
<view class="flow-item-left">
|
||||
<text class="flow-item-name">{{item.name}}</text>
|
||||
<text class="flow-item-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
<text class="flow-item-name">{{fmt.safe(item.name)}}</text>
|
||||
<text class="flow-item-desc" wx:if="{{item.desc}}">{{fmt.safe(item.desc)}}</text>
|
||||
</view>
|
||||
<view class="flow-item-right">
|
||||
<text class="flow-item-value">{{item.value}}</text>
|
||||
<text class="flow-item-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, false, 'xs')}}">{{fmt.compareText(item.compare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -549,9 +550,9 @@
|
||||
<view class="flow-sum-row">
|
||||
<text class="flow-sum-label">现金流入合计</text>
|
||||
<view class="flow-sum-right">
|
||||
<text class="flow-sum-value">{{cashflow.total}}</text>
|
||||
<text class="flow-sum-value">{{fmt.money(cashflow.total)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{cashflow.totalCompare}}</text>
|
||||
<text class="{{fmt.compareClass(cashflow.totalCompare, false, 'sm')}}">{{fmt.compareText(cashflow.totalCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -560,8 +561,8 @@
|
||||
<view class="card-tear"></view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 板块 5: 现金流出 ===== -->
|
||||
<view id="section-expense" class="card-section">
|
||||
<!-- ===== 板块 5: 现金流出(仅"全部区域"时显示) ===== -->
|
||||
<view id="section-expense" class="card-section" wx:if="{{selectedArea === 'all'}}">
|
||||
<view class="card-header-light">
|
||||
<text class="card-header-emoji">📤</text>
|
||||
<view class="card-header-text">
|
||||
@@ -575,10 +576,10 @@
|
||||
<text class="expense-group-label">进货与运营</text>
|
||||
<view class="expense-grid-3">
|
||||
<view class="expense-cell" wx:for="{{expense.operationItems}}" wx:key="name">
|
||||
<text class="expense-cell-label">{{item.name}}</text>
|
||||
<text class="expense-cell-value">{{item.value}}</text>
|
||||
<text class="expense-cell-label">{{fmt.safe(item.name)}}</text>
|
||||
<text class="expense-cell-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.isDown ? 'compare-text-down-xs' : 'compare-text-up-xs'}}">{{item.isDown ? '↓' : '↑'}}{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, item.isDown, 'xs')}}">{{fmt.compareText(item.compare, item.isDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -587,10 +588,10 @@
|
||||
<text class="expense-group-label">固定支出</text>
|
||||
<view class="expense-grid-2">
|
||||
<view class="expense-cell" wx:for="{{expense.fixedItems}}" wx:key="name">
|
||||
<text class="expense-cell-label">{{item.name}}</text>
|
||||
<text class="expense-cell-value">{{item.value}}</text>
|
||||
<text class="expense-cell-label">{{fmt.safe(item.name)}}</text>
|
||||
<text class="expense-cell-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.isFlat ? 'compare-text-flat-xs' : 'compare-text-up-xs'}}">{{item.isFlat ? '' : '↑'}}{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, false, 'xs')}}">{{fmt.compareText(item.compare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -599,10 +600,10 @@
|
||||
<text class="expense-group-label">助教薪资</text>
|
||||
<view class="expense-grid-2">
|
||||
<view class="expense-cell" wx:for="{{expense.coachItems}}" wx:key="name">
|
||||
<text class="expense-cell-label">{{item.name}}</text>
|
||||
<text class="expense-cell-value">{{item.value}}</text>
|
||||
<text class="expense-cell-label">{{fmt.safe(item.name)}}</text>
|
||||
<text class="expense-cell-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.isDown ? 'compare-text-down-xs' : 'compare-text-up-xs'}}">{{item.isDown ? '↓' : '↑'}}{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, item.isDown, 'xs')}}">{{fmt.compareText(item.compare, item.isDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -612,10 +613,10 @@
|
||||
<text class="expense-group-note">服务费在流水流入时,平台已经扣除。不产生支出流水。</text>
|
||||
<view class="expense-grid-3">
|
||||
<view class="expense-cell" wx:for="{{expense.platformItems}}" wx:key="name">
|
||||
<text class="expense-cell-label">{{item.name}}</text>
|
||||
<text class="expense-cell-value">{{item.value}}</text>
|
||||
<text class="expense-cell-label">{{fmt.safe(item.name)}}</text>
|
||||
<text class="expense-cell-value">{{fmt.money(item.value)}}</text>
|
||||
<view class="compare-row" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{item.compare}}</text>
|
||||
<text class="{{fmt.compareClass(item.compare, false, 'xs')}}">{{fmt.compareText(item.compare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -624,9 +625,9 @@
|
||||
<view class="flow-sum-row">
|
||||
<text class="flow-sum-label">支出合计</text>
|
||||
<view class="flow-sum-right">
|
||||
<text class="flow-sum-value">{{expense.total}}</text>
|
||||
<text class="flow-sum-value">{{fmt.money(expense.total)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-sm">↑{{expense.totalCompare}}</text>
|
||||
<text class="{{fmt.compareClass(expense.totalCompare, false, 'sm')}}">{{fmt.compareText(expense.totalCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -659,43 +660,43 @@
|
||||
<view class="coach-fin-row coach-fin-row--total">
|
||||
<text class="coach-fin-col coach-fin-col--name coach-fin-bold">合计</text>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-bold">{{coachAnalysis.basic.totalPay}}</text>
|
||||
<text class="coach-fin-bold">{{fmt.money(coachAnalysis.basic.totalPay)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.basic.totalPayCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.basic.totalPayCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.basic.totalPayCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-bold">{{coachAnalysis.basic.totalShare}}</text>
|
||||
<text class="coach-fin-bold">{{fmt.money(coachAnalysis.basic.totalShare)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.basic.totalShareCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.basic.totalShareCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.basic.totalShareCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-val-sm">{{coachAnalysis.basic.avgHourly}}</text>
|
||||
<text class="coach-fin-val-sm">{{fmt.money(coachAnalysis.basic.avgHourly)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.basic.avgHourlyCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.basic.avgHourlyCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.basic.avgHourlyCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 明细行 -->
|
||||
<view class="coach-fin-row coach-fin-row--detail" wx:for="{{coachAnalysis.basic.rows}}" wx:key="level">
|
||||
<text class="coach-fin-col coach-fin-col--name">{{item.level}}</text>
|
||||
<text class="coach-fin-col coach-fin-col--name">{{fmt.safe(item.level)}}</text>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-val">{{item.pay}}</text>
|
||||
<text class="coach-fin-val">{{fmt.money(item.pay)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.payDown ? 'compare-text-down-xs' : 'compare-text-up-xs'}}">{{item.payDown ? '↓' : '↑'}}{{item.payCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.payCompare, item.payDown, 'xs')}}">{{fmt.compareText(item.payCompare, item.payDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-val">{{item.share}}</text>
|
||||
<text class="coach-fin-val">{{fmt.money(item.share)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.shareDown ? 'compare-text-down-xs' : 'compare-text-up-xs'}}">{{item.shareDown ? '↓' : '↑'}}{{item.shareCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.shareCompare, item.shareDown, 'xs')}}">{{fmt.compareText(item.shareCompare, item.shareDown)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-val-sm">{{item.hourly}}</text>
|
||||
<text class="coach-fin-val-sm">{{fmt.money(item.hourly)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="{{item.hourlyFlat ? 'compare-text-flat-xs' : 'compare-text-up-xs'}}">{{item.hourlyFlat ? '' : '↑'}}{{item.hourlyCompare}}</text>
|
||||
<text class="{{fmt.compareClass(item.hourlyCompare, false, 'xs')}}">{{fmt.compareText(item.hourlyCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -713,21 +714,21 @@
|
||||
<view class="coach-fin-row coach-fin-row--incentive-total">
|
||||
<text class="coach-fin-col coach-fin-col--name coach-fin-bold">合计</text>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-bold">{{coachAnalysis.incentive.totalPay}}</text>
|
||||
<text class="coach-fin-bold">{{fmt.money(coachAnalysis.incentive.totalPay)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.incentive.totalPayCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.incentive.totalPayCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.incentive.totalPayCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-bold">{{coachAnalysis.incentive.totalShare}}</text>
|
||||
<text class="coach-fin-bold">{{fmt.money(coachAnalysis.incentive.totalShare)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.incentive.totalShareCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.incentive.totalShareCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.incentive.totalShareCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coach-fin-col">
|
||||
<text class="coach-fin-val-sm">{{coachAnalysis.incentive.avgHourly}}</text>
|
||||
<text class="coach-fin-val-sm">{{fmt.money(coachAnalysis.incentive.avgHourly)}}</text>
|
||||
<view class="compare-row-inline" wx:if="{{compareEnabled}}">
|
||||
<text class="compare-text-up-xs">↑{{coachAnalysis.incentive.avgHourlyCompare}}</text>
|
||||
<text class="{{fmt.compareClass(coachAnalysis.incentive.avgHourlyCompare, false, 'xs')}}">{{fmt.compareText(coachAnalysis.incentive.avgHourlyCompare, false)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -276,15 +276,17 @@ AI_CHANGELOG
|
||||
/* ===== 板块正文容器 ===== */
|
||||
/* CHANGE 2026-03-12 | intent: 校对 H5 p-4(16px→32rpx) */
|
||||
.section-body {
|
||||
padding: 30rpx 30rpx;
|
||||
padding: 30rpx 40rpx;
|
||||
}
|
||||
|
||||
/* ===== 子标题 ===== */
|
||||
/* CHANGE 2026-03-12 | intent: 校对 H5 gap-2(8px→16rpx)、mb-3(12px→24rpx)、padding 匹配 summary-content 16px→32rpx */
|
||||
/* CHANGE 2026-03-27 | 各 text 独立一行 */
|
||||
.sub-section-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14rpx;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6rpx;
|
||||
padding: 0rpx 30rpx 22rpx;
|
||||
}
|
||||
|
||||
@@ -688,7 +690,7 @@ AI_CHANGELOG
|
||||
/* CHANGE 2026-03-13 | intent: 校对 H5 gift-table-header padding:8px 16px→16rpx 32rpx */
|
||||
.gift-table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 1fr 1fr;
|
||||
grid-template-columns: 0.2fr 0.3fr 0.3fr 0.3fr;
|
||||
gap: 8rpx;
|
||||
padding: 14rpx 30rpx;
|
||||
background: #f0f0f0;
|
||||
@@ -728,7 +730,7 @@ AI_CHANGELOG
|
||||
/* CHANGE 2026-03-13 | intent: 校对 H5 gift-table-row padding:12px 16px→24rpx 32rpx */
|
||||
.gift-table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 1fr 1fr;
|
||||
grid-template-columns:0.2fr 0.5fr 0.5fr 0.5fr;
|
||||
gap: 8rpx;
|
||||
padding: 22rpx 30rpx;
|
||||
border-bottom: 2rpx solid #f3f3f3;
|
||||
@@ -857,7 +859,7 @@ AI_CHANGELOG
|
||||
/* CHANGE 2026-03-14 | intent: H5 gap-1=4px → spec p-1=8rpx;之前误转为 4rpx */
|
||||
.rev-table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
gap: 8rpx;
|
||||
padding: 14rpx 30rpx;
|
||||
background: #f0f0f0;
|
||||
@@ -886,9 +888,9 @@ AI_CHANGELOG
|
||||
/* CHANGE 2026-03-14 | intent: H5 gap-1=4px → spec p-1=8rpx */
|
||||
.rev-table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
gap: 8rpx;
|
||||
padding: 22rpx 30rpx;
|
||||
padding: 22rpx 18rpx;
|
||||
border-bottom: 2rpx solid #f3f3f3;
|
||||
align-items: start;
|
||||
}
|
||||
@@ -915,7 +917,7 @@ AI_CHANGELOG
|
||||
font-size: 26rpx;
|
||||
line-height: 36rpx;
|
||||
color: #8b8b8b;
|
||||
padding-left: 30rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
/* CHANGE 2026-03-14 | intent: H5 text-xs=12px → spec 22rpx */
|
||||
@@ -950,8 +952,11 @@ AI_CHANGELOG
|
||||
/* ===== 损益链流程 ===== */
|
||||
/* CHANGE 2026-03-13 | intent: 校对 H5 flow-header padding:10px 16px→20rpx 32rpx */
|
||||
.flow-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 18rpx 30rpx;
|
||||
background: #fafafa;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flow-header--deduct {
|
||||
@@ -972,7 +977,7 @@ AI_CHANGELOG
|
||||
line-height: 29rpx;
|
||||
color: #8b8b8b;
|
||||
display: block;
|
||||
margin-top: 2rpx;
|
||||
margin: 2rpx auto 2rpx 30rpx;
|
||||
}
|
||||
|
||||
/* CHANGE 2026-03-13 | intent: 校对 H5 flow-detail-list padding:12px 16px→24rpx 32rpx, margin:8px 16px→16rpx 32rpx */
|
||||
@@ -1061,7 +1066,7 @@ AI_CHANGELOG
|
||||
}
|
||||
|
||||
.flow-total-right {
|
||||
display: flex;
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
@@ -1623,3 +1628,36 @@ AI_CHANGELOG
|
||||
height: 200rpx;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
/* CHANGE 2026-03-27 | board-finance-phase2 T3 | 优惠总计行样式 */
|
||||
.flow-total-row--discount {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
margin-top: -8rpx;
|
||||
}
|
||||
.flow-total-label--red {
|
||||
color: #e74c3c;
|
||||
}
|
||||
.flow-total-value--red {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
/* CHANGE 2026-03-28 | board-finance-phase2 bugfix | 优惠扣减标题右侧总计 */
|
||||
.flow-header-total {
|
||||
margin-right: 30rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.flow-header-total--red {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
/* CHANGE 2026-03-28 | 环比持平样式(灰色无箭头) */
|
||||
.compare-text-flat-sm { font-size: 24rpx; line-height: 29rpx; color: #a6a6a6; }
|
||||
|
||||
/* CHANGE 2026-03-28 | 成交/确认收入环比独立行 */
|
||||
.compare-row-standalone {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 30rpx 12rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user