feat: chat integration, tenant admin spec, backend chat service, miniprogram updates, DEMO moved to tmp, XCX-TEST removed, migrations & docs
This commit is contained in:
412
tmp/DEMO-miniprogram/miniprogram/pages/task-list/task-list.wxml
Normal file
412
tmp/DEMO-miniprogram/miniprogram/pages/task-list/task-list.wxml
Normal file
@@ -0,0 +1,412 @@
|
||||
<!-- 任务列表页 — 2026-03-13 全量重写,1:1 对齐 H5 原型 task-list.html -->
|
||||
<!-- AI_CHANGELOG
|
||||
| 日期 | Prompt | 变更 |
|
||||
|------|--------|------|
|
||||
| 2026-03-13 | 重写 task-list 页面 1:1 还原 H5 原型 | 全量重写 WXML |
|
||||
| 2026-03-13 | banner 错位重做 | 移除通用 banner 组件,页面内实现完整 banner |
|
||||
| 2026-03-13 | 背景+纹理合并 SVG | 渐变+纹理+光晕合并为 SVG |
|
||||
| 2026-03-13 | 5项修复+精确还原 | 恢复纹理CSS层、盖戳改回CSS实现、头像引用修复、abandoned标签恢复CSS灰化、全量line-height校准 |
|
||||
-->
|
||||
<view class="page-task-list">
|
||||
|
||||
<!-- ====== 顶部 Banner 区域 — 对齐 H5 .banner-bg.theme-blue.texture-aurora ====== -->
|
||||
<!-- CHANGE 2026-03-13 | banner 背景:SVG 做渐变底图 + CSS 做纹理叠加(SVG pattern 在小程序中不渲染) -->
|
||||
<view class="banner-area">
|
||||
<image class="banner-bg-img" src="/assets/images/banner-bg-combined.svg" mode="aspectFill" />
|
||||
<!-- 纹理层:CSS repeating-linear-gradient 实现斜线网格,SVG pattern 在小程序 image 中不生效 -->
|
||||
<view class="banner-texture"></view>
|
||||
|
||||
<!-- 用户信息区 — H5: .px-5.pt-10.pb-3 -->
|
||||
<view class="user-info-section">
|
||||
<view class="user-info-row">
|
||||
<!-- 头像 — H5: w-14 h-14 rounded-2xl bg-white/20 -->
|
||||
<view class="avatar-wrap">
|
||||
<image src="/assets/images/avatar-coach.png" mode="aspectFill" class="avatar-img" />
|
||||
</view>
|
||||
<!-- 姓名+标签+门店 -->
|
||||
<view class="user-detail">
|
||||
<view class="user-name-row">
|
||||
<text class="user-name">{{userName}}</text>
|
||||
<text class="user-role-tag">{{userRole}}</text>
|
||||
</view>
|
||||
<view class="user-store-row">
|
||||
<text class="user-store">{{storeName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 业绩进度卡片 — H5: .mx-4 > .bg-white/15.backdrop-blur-md.rounded-2xl -->
|
||||
<view class="perf-card">
|
||||
<!-- L1: 跳档提示 -->
|
||||
<view class="perf-l1">
|
||||
<view class="perf-l1-left">
|
||||
<text class="perf-label">距离{{perfData.nextTierHours}}小时仅剩</text>
|
||||
<text class="perf-accent">{{perfData.remainHours}}小时</text>
|
||||
</view>
|
||||
<view class="perf-l1-right" bindtap="onPerformanceTap">
|
||||
<text class="perf-secondary">查看详情</text>
|
||||
<t-icon name="chevron-right" size="22rpx" color="rgba(255,255,255,0.7)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- L2: 5段档位进度条组件 -->
|
||||
<view class="perf-l2">
|
||||
<perf-progress-bar
|
||||
filledPct="{{perfData.filledPct}}"
|
||||
clampedSparkPct="{{perfData.clampedSparkPct}}"
|
||||
currentTier="{{perfData.currentTier}}"
|
||||
ticks="{{perfData.ticks}}"
|
||||
shineRunning="{{perfData.shineRunning}}"
|
||||
sparkRunning="{{perfData.sparkRunning}}"
|
||||
shineDurMs="{{perfData.shineDurMs}}"
|
||||
sparkDurMs="{{perfData.sparkDurMs}}"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- L3: 课时 + 红戳 + 奖金 -->
|
||||
<view class="perf-l3">
|
||||
<view class="perf-l3-left">
|
||||
<view class="perf-hours-wrap">
|
||||
<view class="perf-hours-row">
|
||||
<text class="hours-green">{{perfData.basicHours}}</text>
|
||||
<text class="hours-sep">|</text>
|
||||
<text class="hours-yellow">{{perfData.bonusHours}}</text>
|
||||
<text class="hours-sep">|</text>
|
||||
<text class="hours-white">{{perfData.totalHours}}</text>
|
||||
</view>
|
||||
<view class="hours-label-row">
|
||||
<text class="hours-label">基础课 | 激励课 | 全部</text>
|
||||
</view>
|
||||
<!-- 红戳徽章 — SVG 实现 -->
|
||||
<image
|
||||
class="stamp-badge {{stampAnimated ? 'stamp-animate' : ''}}"
|
||||
wx:if="{{perfData.tierCompleted}}"
|
||||
src="/assets/images/stamp-badge.svg"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="perf-l3-right">
|
||||
<view class="bonus-wrap">
|
||||
<text class="bonus-amount">{{perfData.bonusMoney}}</text>
|
||||
<text class="bonus-unit">元</text>
|
||||
</view>
|
||||
<view class="bonus-label-row">
|
||||
<text class="bonus-label">达{{perfData.nextTierHours}}h即得</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- L4: 预计收入 -->
|
||||
<view class="perf-l4">
|
||||
<text class="perf-l4-label">{{perfData.incomeMonth}}预计收入 | 比{{perfData.prevMonth}}同期</text>
|
||||
<view class="perf-l4-right" bindtap="onPerformanceTap">
|
||||
<text class="income-value">¥{{perfData.incomeFormatted}}</text>
|
||||
<text class="income-trend {{perfData.incomeTrendDir === 'down' ? 'trend-down' : ''}}">{{perfData.incomeTrend}}</text>
|
||||
<t-icon name="chevron-right" size="28rpx" color="rgba(255,255,255,0.7)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== Loading 骨架屏 ====== -->
|
||||
<view class="state-loading" wx:if="{{pageState === 'loading'}}">
|
||||
<view class="loading-placeholder" wx:for="{{[1,2,3]}}" wx:key="*this">
|
||||
<view class="ph-line ph-line--title"></view>
|
||||
<view class="ph-line ph-line--body"></view>
|
||||
<view class="ph-line ph-line--short"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 空状态 ====== -->
|
||||
<view class="state-empty" wx:if="{{pageState === 'empty'}}">
|
||||
<text class="empty-text">暂无待办任务</text>
|
||||
</view>
|
||||
|
||||
<!-- ====== Error 状态 ====== -->
|
||||
<view class="state-error" wx:if="{{pageState === 'error'}}">
|
||||
<text class="error-text">加载失败,请重试</text>
|
||||
<view class="retry-btn" bindtap="onRetry">
|
||||
<text class="retry-btn-text">重试</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 任务列表区域 ====== -->
|
||||
<view class="task-section" wx:if="{{pageState === 'normal'}}">
|
||||
<!-- 标题行 -->
|
||||
<view class="section-header">
|
||||
<text class="section-title">今日 客户维护</text>
|
||||
<text class="section-count">共 {{taskCount}} 项</text>
|
||||
</view>
|
||||
|
||||
<!-- 📌 置顶区域 -->
|
||||
<view class="task-group" wx:if="{{pinnedTasks.length > 0}}">
|
||||
<view class="group-label-row">
|
||||
<text class="group-label group-label--pinned">📌 置顶</text>
|
||||
<text class="group-count">{{pinnedTasks.length}}项</text>
|
||||
</view>
|
||||
<view class="task-card-list">
|
||||
<view
|
||||
class="task-card {{item.isPinned ? 'task-card--pinned' : ''}} task-card--{{item.taskType}}"
|
||||
wx:for="{{pinnedTasks}}" wx:key="id"
|
||||
hover-class="task-card--hover" hover-stay-time="100"
|
||||
data-id="{{item.id}}" data-tasktype="{{item.taskType}}"
|
||||
data-group="pinned" data-index="{{index}}"
|
||||
bindtap="onTaskTap" bindlongpress="onTaskLongPress"
|
||||
>
|
||||
<view class="card-body">
|
||||
<view class="card-row-1">
|
||||
<view class="task-type-tag task-type-tag--{{item.taskType}}">
|
||||
<text class="tag-text">{{item.taskTypeLabel}}</text>
|
||||
</view>
|
||||
<text class="customer-name">{{item.customerName}}</text>
|
||||
<heart-icon score="{{item.heartScore}}" size="small" />
|
||||
<text class="note-indicator" wx:if="{{item.hasNote}}">📝</text>
|
||||
<text class="overdue-badge" wx:if="{{item.deadlineStyle === 'danger'}}">{{item.deadlineLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-2">
|
||||
<text class="visit-text">最近到店:{{item.lastVisitDays}}天前 · 余额:{{item.balanceLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-deadline" wx:if="{{item.deadlineLabel && item.deadlineLabel !== '--' && item.deadlineStyle !== 'danger'}}">
|
||||
<text class="deadline-text deadline-text--{{item.deadlineStyle}}">{{item.deadlineLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-3">
|
||||
<ai-inline-icon color="{{aiColor}}" />
|
||||
<text class="ai-suggestion-text">{{item.aiSuggestion}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-arrow">
|
||||
<t-icon name="chevron-right" size="36rpx" color="#c5c5c5" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 一般任务区域 -->
|
||||
<view class="task-group" wx:if="{{normalTasks.length > 0}}">
|
||||
<view class="group-label-row">
|
||||
<text class="group-label group-label--normal">正常任务</text>
|
||||
<text class="group-count">{{normalTasks.length}}项</text>
|
||||
</view>
|
||||
<view class="task-card-list">
|
||||
<view
|
||||
class="task-card task-card--{{item.taskType}}"
|
||||
wx:for="{{normalTasks}}" wx:key="id"
|
||||
hover-class="task-card--hover" hover-stay-time="100"
|
||||
data-id="{{item.id}}" data-tasktype="{{item.taskType}}"
|
||||
data-group="normal" data-index="{{index}}"
|
||||
bindtap="onTaskTap" bindlongpress="onTaskLongPress"
|
||||
>
|
||||
<view class="card-body">
|
||||
<view class="card-row-1">
|
||||
<view class="task-type-tag task-type-tag--{{item.taskType}}">
|
||||
<text class="tag-text">{{item.taskTypeLabel}}</text>
|
||||
</view>
|
||||
<text class="customer-name">{{item.customerName}}</text>
|
||||
<heart-icon score="{{item.heartScore}}" size="small" />
|
||||
<text class="note-indicator" wx:if="{{item.hasNote}}">📝</text>
|
||||
<text class="overdue-badge" wx:if="{{item.deadlineStyle === 'danger'}}">{{item.deadlineLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-2">
|
||||
<text class="visit-text">最近到店:{{item.lastVisitDays}}天前 · 余额:{{item.balanceLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-deadline" wx:if="{{item.deadlineLabel && item.deadlineLabel !== '--' && item.deadlineStyle !== 'danger'}}">
|
||||
<text class="deadline-text deadline-text--{{item.deadlineStyle}}">{{item.deadlineLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-3">
|
||||
<ai-inline-icon color="{{aiColor}}" />
|
||||
<text class="ai-suggestion-text">{{item.aiSuggestion}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-arrow">
|
||||
<t-icon name="chevron-right" size="36rpx" color="#c5c5c5" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已放弃区域 -->
|
||||
<view class="task-group" wx:if="{{abandonedTasks.length > 0}}">
|
||||
<view class="group-label-row">
|
||||
<text class="group-label group-label--abandoned">已放弃</text>
|
||||
<text class="group-count">{{abandonedTasks.length}}项</text>
|
||||
</view>
|
||||
<view class="task-card-list">
|
||||
<view
|
||||
class="task-card task-card--abandoned"
|
||||
wx:for="{{abandonedTasks}}" wx:key="id"
|
||||
hover-class="task-card--hover" hover-stay-time="100"
|
||||
data-id="{{item.id}}" data-tasktype="{{item.taskType}}"
|
||||
data-group="abandoned" data-index="{{index}}"
|
||||
bindtap="onTaskTap" bindlongpress="onTaskLongPress"
|
||||
>
|
||||
<view class="card-body">
|
||||
<view class="card-row-1">
|
||||
<!-- CHANGE 2026-03-13 | abandoned 标签保留原始类型标签,通过 CSS 灰化(对齐 H5 行为) -->
|
||||
<view class="task-type-tag task-type-tag--{{item.taskType}} task-type-tag--abandoned">
|
||||
<text class="tag-text">{{item.taskTypeLabel}}</text>
|
||||
</view>
|
||||
<text class="customer-name customer-name--abandoned">{{item.customerName}}</text>
|
||||
<heart-icon score="{{item.heartScore}}" size="small" />
|
||||
</view>
|
||||
<view class="card-row-2">
|
||||
<text class="visit-text visit-text--abandoned">最近到店:{{item.lastVisitDays}}天前 · 余额:{{item.balanceLabel}}</text>
|
||||
</view>
|
||||
<view class="card-row-abandon" wx:if="{{item.abandonReason}}">
|
||||
<text class="abandon-reason">放弃原因:{{item.abandonReason}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-arrow">
|
||||
<t-icon name="chevron-right" size="36rpx" color="#c5c5c5" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view class="load-more" wx:if="{{!hasMore}}">
|
||||
<text class="load-more-text">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== P3: 长按上下文菜单 ====== -->
|
||||
<view class="ctx-overlay {{contextMenuVisible ? 'ctx-overlay--active' : ''}}" bindtap="onCloseContextMenu"></view>
|
||||
<view class="ctx-menu {{contextMenuVisible ? 'ctx-menu--active' : ''}}"
|
||||
style="left:{{contextMenuX}}px;top:{{contextMenuY}}px" catchtap="noop">
|
||||
<!-- 已放弃任务:显示"取消放弃" -->
|
||||
<block wx:if="{{contextMenuTarget.isAbandoned}}">
|
||||
<view class="ctx-item" hover-class="ctx-item--hover" bindtap="onCtxCancelAbandon">
|
||||
<text class="ctx-emoji">↩️</text>
|
||||
<text class="ctx-text">取消放弃</text>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 一般/置顶任务:显示标准菜单 -->
|
||||
<block wx:else>
|
||||
<view class="ctx-item" hover-class="ctx-item--hover" bindtap="onCtxPin">
|
||||
<text class="ctx-emoji">📌</text>
|
||||
<text class="ctx-text">{{contextMenuTarget.isPinned ? '取消置顶' : '置顶'}}</text>
|
||||
</view>
|
||||
<view class="ctx-item" hover-class="ctx-item--hover" bindtap="onCtxNote">
|
||||
<text class="ctx-emoji">📝</text>
|
||||
<text class="ctx-text">备注</text>
|
||||
</view>
|
||||
<view class="ctx-item" hover-class="ctx-item--hover" bindtap="onCtxAI">
|
||||
<text class="ctx-emoji">🤖</text>
|
||||
<text class="ctx-text">问问AI助手</text>
|
||||
</view>
|
||||
<view class="ctx-item" hover-class="ctx-item--hover" bindtap="onCtxAbandon">
|
||||
<text class="ctx-emoji">🗑️</text>
|
||||
<text class="ctx-text">放弃任务</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- ====== P4: 放弃弹窗 ====== -->
|
||||
<abandon-modal
|
||||
visible="{{abandonModalVisible}}"
|
||||
customerName="{{abandonTarget.customerName || ''}}"
|
||||
bind:confirm="onAbandonConfirm"
|
||||
bind:cancel="onAbandonCancel"
|
||||
/>
|
||||
|
||||
<!-- 备注弹窗 -->
|
||||
<note-modal
|
||||
visible="{{noteModalVisible}}"
|
||||
customerName="{{noteTarget.customerName || ''}}"
|
||||
showExpandBtn="{{true}}"
|
||||
showRating="{{true}}"
|
||||
bind:confirm="onNoteConfirm"
|
||||
bind:cancel="onNoteCancel"
|
||||
/>
|
||||
|
||||
<!-- AI 悬浮按钮 -->
|
||||
<ai-float-button />
|
||||
|
||||
<!-- 开发调试 FAB -->
|
||||
<dev-fab />
|
||||
|
||||
<!-- ====== 调试面板 ====== -->
|
||||
<view class="debug-panel {{showDebugPanel ? 'debug-panel--visible' : ''}}" catchtap="noop">
|
||||
<view class="debug-header">
|
||||
<text class="debug-title">🔧 调试工具</text>
|
||||
<view class="debug-close" bindtap="toggleDebugPanel" hover-class="debug-close--hover">
|
||||
<t-icon name="close" size="32rpx" color="#5e5e5e" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 课时进度控制 -->
|
||||
<view class="debug-section">
|
||||
<view class="debug-label-row">
|
||||
<text class="debug-label">📊 当前课时总量:</text>
|
||||
<text class="debug-value-chip">{{debugTotalHours}}h</text>
|
||||
</view>
|
||||
<slider
|
||||
class="debug-slider"
|
||||
min="0" max="220" step="1"
|
||||
value="{{debugTotalHours}}"
|
||||
bindchange="onDebugTotalHours"
|
||||
activeColor="#10b981"
|
||||
backgroundColor="#e7e7e7"
|
||||
block-size="24"
|
||||
/>
|
||||
<view class="debug-tick-row">
|
||||
<text class="debug-tick">0</text>
|
||||
<text class="debug-tick debug-tick--key">100</text>
|
||||
<text class="debug-tick debug-tick--key debug-tick--current">130</text>
|
||||
<text class="debug-tick debug-tick--key">160</text>
|
||||
<text class="debug-tick debug-tick--key">190</text>
|
||||
<text class="debug-tick">220</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基础 / 激励课时分配 -->
|
||||
<view class="debug-section">
|
||||
<view class="debug-label-row">
|
||||
<text class="debug-label">🟢 基础课时:</text>
|
||||
<text class="debug-value-chip debug-chip--green">{{debugBasicHours}}h</text>
|
||||
</view>
|
||||
<slider
|
||||
class="debug-slider"
|
||||
min="0" max="220" step="0.5"
|
||||
value="{{debugBasicHours}}"
|
||||
bindchange="onDebugBasicHours"
|
||||
activeColor="#6ee7b7"
|
||||
backgroundColor="#e7e7e7"
|
||||
block-size="22"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="debug-section">
|
||||
<view class="debug-label-row">
|
||||
<text class="debug-label">🟡 激励课时:</text>
|
||||
<text class="debug-value-chip debug-chip--yellow">{{debugBonusHours}}h</text>
|
||||
</view>
|
||||
<slider
|
||||
class="debug-slider"
|
||||
min="0" max="80" step="0.5"
|
||||
value="{{debugBonusHours}}"
|
||||
bindchange="onDebugBonusHours"
|
||||
activeColor="#fbbf24"
|
||||
backgroundColor="#e7e7e7"
|
||||
block-size="22"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 档位进度预设 -->
|
||||
<view class="debug-section">
|
||||
<text class="debug-label">🎯 快速预设档位:</text>
|
||||
<view class="debug-btn-group">
|
||||
<view class="debug-btn {{debugPreset === 0 ? 'debug-btn--active' : ''}}" bindtap="onDebugPreset" data-preset="0">未完成</view>
|
||||
<view class="debug-btn {{debugPreset === 1 ? 'debug-btn--active' : ''}}" bindtap="onDebugPreset" data-preset="1">达100h</view>
|
||||
<view class="debug-btn {{debugPreset === 2 ? 'debug-btn--active' : ''}}" bindtap="onDebugPreset" data-preset="2">达130h</view>
|
||||
<view class="debug-btn {{debugPreset === 3 ? 'debug-btn--active' : ''}}" bindtap="onDebugPreset" data-preset="3">达160h</view>
|
||||
<view class="debug-btn {{debugPreset === 4 ? 'debug-btn--active' : ''}}" bindtap="onDebugPreset" data-preset="4">满档220h</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 调试触发按钮 -->
|
||||
<view class="debug-trigger {{showDebugPanel ? 'debug-trigger--active' : ''}}" bindtap="toggleDebugPanel" hover-class="debug-trigger--hover">
|
||||
<text class="debug-trigger-icon">🔧</text>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user