Files
Neo-ZQYY/apps/DEMO-miniprogram/miniprogram/pages/task-list/task-list.wxml

413 lines
18 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 任务列表页 — 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>