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:
Neo
2026-04-06 00:03:48 +08:00
parent 70324d8542
commit 6f8f12314f
515 changed files with 76604 additions and 7456 deletions

View File

@@ -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 板块;筛选联动调 _loadDataareaOptions 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_flatmapExpenseItems/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 返回 camelCasegiftRows[].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 | P1redirectTo 替代 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()
},
})

View File

@@ -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>

View File

@@ -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;
}