302 lines
13 KiB
Plaintext
302 lines
13 KiB
Plaintext
<!-- 客户看板页 — 忠于 H5 原型,8 维度卡片模板 -->
|
||
|
||
<!-- 加载态 -->
|
||
<view class="page-loading" wx:if="{{pageState === 'loading'}}">
|
||
<t-loading theme="circular" size="70rpx" text="加载中..." />
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="page-empty" wx:elif="{{pageState === 'empty'}}">
|
||
<t-empty description="暂无客户数据" />
|
||
</view>
|
||
|
||
<!-- 正常态 -->
|
||
<block wx:else>
|
||
<!-- 顶部看板 Tab -->
|
||
<view class="board-tabs">
|
||
<view class="board-tab" data-tab="finance" bindtap="onTabChange">
|
||
<text>财务</text>
|
||
</view>
|
||
<view class="board-tab board-tab--active" data-tab="customer">
|
||
<text>客户</text>
|
||
</view>
|
||
<view class="board-tab" data-tab="coach" bindtap="onTabChange">
|
||
<text>助教</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 筛选区域 -->
|
||
<view class="filter-bar {{filterBarVisible ? '' : 'filter-bar--hidden'}}">
|
||
<view class="filter-bar-inner">
|
||
<view class="filter-item filter-item--wide">
|
||
<filter-dropdown
|
||
label="最应召回"
|
||
options="{{dimensionOptions}}"
|
||
value="{{selectedDimension}}"
|
||
bind:change="onDimensionChange"
|
||
/>
|
||
</view>
|
||
<view class="filter-item">
|
||
<filter-dropdown
|
||
label="全部"
|
||
options="{{projectOptions}}"
|
||
value="{{selectedProject}}"
|
||
bind:change="onProjectChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 列表头部 -->
|
||
<view class="list-header">
|
||
<view class="list-header-left">
|
||
<text class="list-header-title">客户列表</text>
|
||
<text class="list-header-sub">· 前100名</text>
|
||
</view>
|
||
<text class="list-header-count">共{{totalCount}}名客户</text>
|
||
</view>
|
||
|
||
<!-- 客户列表 -->
|
||
<view class="customer-list">
|
||
<view
|
||
class="customer-card"
|
||
wx:for="{{customers}}"
|
||
wx:key="id"
|
||
data-id="{{item.id}}"
|
||
bindtap="onCustomerTap"
|
||
>
|
||
<!-- ===== 卡片头部:头像 + 姓名 + 右侧指标 ===== -->
|
||
<view class="card-header">
|
||
<view class="card-avatar {{item.avatarCls}}">
|
||
<text class="avatar-text">{{item.initial}}</text>
|
||
</view>
|
||
<text class="card-name" wx:if="{{dimType !== 'freq60'}}">{{item.name}}</text>
|
||
<!-- 最频繁:姓名 + 下方小字垂直排列 -->
|
||
<view class="card-name-group" wx:if="{{dimType === 'freq60'}}">
|
||
<text class="card-name">{{item.name}}</text>
|
||
<view class="card-name-sub">
|
||
<text class="mid-text" style="white-space: nowrap;">平均间隔 <text class="mid-primary">{{item.avgInterval}}</text></text>
|
||
<text class="mid-text" style="margin-left: 16rpx; white-space: nowrap;">60天消费 <text class="mid-dark">{{item.spend60d}}</text></text>
|
||
</view>
|
||
</view>
|
||
<view class="card-header-spacer"></view>
|
||
|
||
<!-- 最应召回:理想/已过/超期 -->
|
||
<view class="header-metrics" wx:if="{{dimType === 'recall'}}">
|
||
<text class="metric-gray">理想 <text class="metric-dark">{{item.idealDays}}天</text></text>
|
||
<text class="metric-gray">已过 <text class="metric-error">{{item.elapsedDays}}天</text></text>
|
||
<view class="overdue-tag {{item.overdueDays > 7 ? 'overdue-tag--danger' : 'overdue-tag--warn'}}">
|
||
<text>超期 {{item.overdueDays}}天</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最大消费潜力:频率/客单/余额标签 -->
|
||
<view class="header-metrics" wx:elif="{{dimType === 'potential'}}">
|
||
<view class="potential-tag potential-tag--{{tag.theme}}" wx:for="{{item.potentialTags}}" wx:for-item="tag" wx:key="text">
|
||
<text>{{tag.text}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最高余额:最近到店/理想 -->
|
||
<view class="header-metrics" wx:elif="{{dimType === 'balance'}}">
|
||
<text class="metric-gray">最近到店 <text class="metric-dark">{{item.lastVisit}}</text></text>
|
||
<text class="metric-gray">理想 <text class="metric-dark">{{item.idealDays}}天</text></text>
|
||
</view>
|
||
|
||
<!-- 最近充值:最近到店/理想 -->
|
||
<view class="header-metrics" wx:elif="{{dimType === 'recharge'}}">
|
||
<text class="metric-gray">最近到店 <text class="metric-dark">{{item.lastVisit}}</text></text>
|
||
<text class="metric-gray">理想 <text class="metric-dark">{{item.idealDays}}天</text></text>
|
||
</view>
|
||
|
||
<!-- 最频繁近60天:右上角大字到店次数 -->
|
||
<view class="header-metrics header-metrics--freq" wx:elif="{{dimType === 'freq60'}}">
|
||
<view class="freq-big-num">
|
||
<text class="freq-big-val">{{item.visits60d}}</text>
|
||
<text class="freq-big-unit">次</text>
|
||
</view>
|
||
<text class="freq-big-label">60天到店</text>
|
||
</view>
|
||
|
||
<!-- 最专一近60天:右上角 ❤️ 助教名 + 关系指数 -->
|
||
<view class="header-metrics header-metrics--loyal" wx:elif="{{dimType === 'loyal'}}">
|
||
<heart-icon score="{{item.topCoachHeart}}" />
|
||
<text class="loyal-top-name">{{item.topCoachName}}</text>
|
||
<text class="loyal-top-score">{{item.topCoachScore}}</text>
|
||
</view>
|
||
|
||
<!-- 最高消费近60天:高消费标签 -->
|
||
<view class="header-metrics" wx:elif="{{dimType === 'spend60'}}">
|
||
<view class="potential-tag potential-tag--warning" wx:if="{{item.highSpendTag}}">
|
||
<text>高消费</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最近到店:右上角大字 X天前到店 -->
|
||
<view class="header-metrics header-metrics--recent" wx:elif="{{dimType === 'recent'}}">
|
||
<view class="recent-big-num">
|
||
<text class="recent-big-val">{{item.daysAgo}}</text>
|
||
<text class="recent-big-unit">天前到店</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ===== 卡片中间行:维度特定数据 ===== -->
|
||
|
||
<!-- 最应召回:30天到店 / 余额 / 召回指数 -->
|
||
<view class="card-mid-row" wx:if="{{dimType === 'recall'}}">
|
||
<text class="mid-text">30天到店 <text class="mid-dark">{{item.visits30d}}次</text></text>
|
||
<text class="mid-text mid-ml">余额 <text class="mid-dark">{{item.balance}}</text></text>
|
||
<text class="mid-text mid-right">召回指数 <text class="mid-primary-bold">{{item.recallIndex}}</text></text>
|
||
</view>
|
||
|
||
<!-- 最大消费潜力:4 列网格(30天消费用橙色大字,和最高余额的余额值一致) -->
|
||
<view class="card-grid card-grid--4" wx:elif="{{dimType === 'potential'}}">
|
||
<view class="grid-cell">
|
||
<text class="grid-label">30天消费</text>
|
||
<text class="grid-val grid-val--warning grid-val--lg">{{item.spend30d}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">月均到店</text>
|
||
<text class="grid-val">{{item.avgVisits}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">余额</text>
|
||
<text class="grid-val grid-val--success">{{item.balance}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">次均消费</text>
|
||
<text class="grid-val">{{item.avgSpend}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最高余额:3 列网格 -->
|
||
<view class="card-grid card-grid--3" wx:elif="{{dimType === 'balance'}}">
|
||
<view class="grid-cell">
|
||
<text class="grid-label">余额</text>
|
||
<text class="grid-val grid-val--warning grid-val--lg">{{item.balance}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">月均消耗</text>
|
||
<text class="grid-val">{{item.monthlyConsume}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">可用</text>
|
||
<text class="grid-val grid-val--success">{{item.availableMonths}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最近充值:4 列网格 -->
|
||
<view class="card-grid card-grid--4" wx:elif="{{dimType === 'recharge'}}">
|
||
<view class="grid-cell">
|
||
<text class="grid-label">最后充值</text>
|
||
<text class="grid-val">{{item.lastRecharge}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">充值</text>
|
||
<text class="grid-val grid-val--success">{{item.rechargeAmount}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">60天充值</text>
|
||
<text class="grid-val">{{item.recharges60d}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">当前余额</text>
|
||
<text class="grid-val grid-val--warning">{{item.currentBalance}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最高消费近60天:3 列网格 -->
|
||
<view class="card-grid card-grid--3" wx:elif="{{dimType === 'spend60'}}">
|
||
<view class="grid-cell">
|
||
<text class="grid-label">近60天消费</text>
|
||
<text class="grid-val grid-val--warning grid-val--lg">{{item.spend60d}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">到店次数</text>
|
||
<text class="grid-val">{{item.visits60d}}</text>
|
||
</view>
|
||
<view class="grid-cell">
|
||
<text class="grid-label">次均消费</text>
|
||
<text class="grid-val">{{item.avgSpend}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最频繁近60天:无中间行(数据已在头部) -->
|
||
|
||
<!-- 最频繁:迷你柱状图(8 周) -->
|
||
<view class="mini-chart" wx:if="{{dimType === 'freq60'}}">
|
||
<view class="mini-chart-header">
|
||
<text class="mini-chart-label">8周前</text>
|
||
<text class="mini-chart-label">本周</text>
|
||
</view>
|
||
<view class="mini-chart-bars">
|
||
<view class="mini-bar-col" wx:for="{{item.weeklyVisits}}" wx:for-item="wv" wx:for-index="wIdx" wx:key="wIdx">
|
||
<view class="mini-bar" style="height:{{wv.pct}}%;opacity:{{0.2 + wv.pct * 0.006}}"></view>
|
||
</view>
|
||
</view>
|
||
<view class="mini-chart-nums">
|
||
<text class="mini-chart-num {{wIdx === item.weeklyVisits.length - 1 ? 'mini-chart-num--active' : ''}}" wx:for="{{item.weeklyVisits}}" wx:for-item="wv" wx:for-index="wIdx" wx:key="wIdx">{{wv.val}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最专一近60天:助教服务明细表 -->
|
||
<view class="loyal-table" wx:elif="{{dimType === 'loyal'}}">
|
||
<!-- 表头 -->
|
||
<view class="loyal-row loyal-row--header">
|
||
<text class="loyal-col loyal-col--name">助教</text>
|
||
<text class="loyal-col">次均时长</text>
|
||
<text class="loyal-col">服务次数</text>
|
||
<text class="loyal-col">助教消费</text>
|
||
<text class="loyal-col">关系指数</text>
|
||
</view>
|
||
<!-- 数据行 -->
|
||
<view class="loyal-row" wx:for="{{item.coachDetails}}" wx:for-item="cd" wx:key="name">
|
||
<view class="loyal-col loyal-col--name">
|
||
<heart-icon score="{{cd.heartScore}}" />
|
||
<text class="loyal-coach-name {{cd.cls}}">{{cd.name}}</text>
|
||
<text class="assistant-badge assistant-badge--follow" wx:if="{{cd.badge === '跟'}}">跟</text>
|
||
<text class="assistant-badge assistant-badge--drop" wx:elif="{{cd.badge === '弃'}}">弃</text>
|
||
</view>
|
||
<text class="loyal-col loyal-val">{{cd.avgDuration}}</text>
|
||
<text class="loyal-col loyal-val">{{cd.serviceCount}}</text>
|
||
<text class="loyal-col loyal-val">{{cd.coachSpend}}</text>
|
||
<text class="loyal-col {{cd.relationIdx >= 8 ? 'loyal-val--primary' : 'loyal-val--gray'}}">{{cd.relationIdx}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 最近到店:理想间隔 / 60天到店 / 次均消费 -->
|
||
<view class="card-mid-row" wx:elif="{{dimType === 'recent'}}">
|
||
<text class="mid-text">理想间隔 <text class="mid-dark">{{item.idealDays}}天</text></text>
|
||
<text class="mid-text mid-ml">60天到店 <text class="mid-dark">{{item.visits60d}}天</text></text>
|
||
<text class="mid-text mid-ml">次均消费 <text class="mid-dark">{{item.avgSpend}}</text></text>
|
||
</view>
|
||
|
||
<!-- ===== 卡片底部:助教行(最专一维度不显示,因为助教信息在表格中) ===== -->
|
||
<view class="card-assistant-row" wx:if="{{item.assistants && item.assistants.length > 0 && dimType !== 'loyal'}}">
|
||
<text class="assistant-label">助教:</text>
|
||
<block wx:for="{{item.assistants}}" wx:for-item="ast" wx:for-index="astIdx" wx:key="name">
|
||
<text class="assistant-sep" wx:if="{{astIdx > 0}}">|</text>
|
||
<view class="assistant-tag">
|
||
<heart-icon score="{{ast.heartScore}}" />
|
||
<text class="assistant-name {{ast.cls}}">{{ast.name}}</text>
|
||
<text class="assistant-badge assistant-badge--follow" wx:if="{{ast.badge === '跟'}}">跟</text>
|
||
<text class="assistant-badge assistant-badge--drop" wx:elif="{{ast.badge === '弃'}}">弃</text>
|
||
</view>
|
||
</block>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部安全区 -->
|
||
<view class="safe-bottom"></view>
|
||
</block>
|
||
|
||
<!-- 自定义底部导航栏 -->
|
||
<board-tab-bar active="board" />
|
||
|
||
<!-- AI 悬浮按钮 -->
|
||
<ai-float-button bottom="{{220}}" />
|
||
|
||
<dev-fab />
|