微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,10 @@
{
"navigationBarTitleText": "任务",
"enablePullDownRefresh": true,
"usingComponents": {
"banner": "/components/banner/banner",
"heart-icon": "/components/heart-icon/heart-icon",
"hobby-tag": "/components/hobby-tag/hobby-tag",
"ai-float-button": "/components/ai-float-button/ai-float-button"
}
}

View File

@@ -0,0 +1,123 @@
import { mockTasks, mockPerformance } from '../../utils/mock-data'
import type { Task } from '../../utils/mock-data'
import { getTaskTypeColor } from '../../utils/task'
/** 任务类型 → 详情页路由映射 */
const DETAIL_ROUTE_MAP: Record<string, string> = {
callback: '/pages/task-detail-callback/task-detail-callback',
priority_recall: '/pages/task-detail-priority/task-detail-priority',
relationship: '/pages/task-detail-relationship/task-detail-relationship',
}
/** 为任务附加颜色信息 */
function enrichTask(task: Task) {
return {
...task,
typeColor: getTaskTypeColor(task.taskType),
}
}
Page({
data: {
/** 页面状态loading / empty / normal */
pageState: 'loading' as 'loading' | 'empty' | 'normal',
/** 任务列表(附带颜色) */
tasks: [] as ReturnType<typeof enrichTask>[],
/** 任务总数 */
taskCount: 0,
/** Banner 指标 */
bannerMetrics: [] as Array<{ label: string; value: string }>,
/** Banner 标题 */
bannerTitle: '',
/** 是否还有更多数据(模拟分页) */
hasMore: true,
},
onLoad() {
this.loadData()
},
onShow() {
// TabBar 页面每次显示时可刷新
},
/** 下拉刷新 */
onPullDownRefresh() {
this.loadData(() => {
wx.stopPullDownRefresh()
})
},
/** 触底加载更多 */
onReachBottom() {
if (!this.data.hasMore) return
// Mock无更多数据
this.setData({ hasMore: false })
wx.showToast({ title: '没有更多了', icon: 'none' })
},
/** 加载数据 */
loadData(cb?: () => void) {
this.setData({ pageState: 'loading' })
// 模拟网络延迟
setTimeout(() => {
const pending = mockTasks.filter((t) => t.status === 'pending')
const enriched = pending.map(enrichTask)
const perf = mockPerformance
const bannerTitle = `${perf.currentTier}`
const bannerMetrics = [
{ label: '本月收入', value: `¥${perf.monthlyIncome.toLocaleString()}` },
{ label: '今日服务', value: `${perf.todayServiceCount}` },
{ label: '距下一档', value: `¥${perf.nextTierGap.toLocaleString()}` },
]
this.setData({
pageState: enriched.length > 0 ? 'normal' : 'empty',
tasks: enriched,
taskCount: enriched.length,
bannerTitle,
bannerMetrics,
hasMore: true,
})
cb?.()
}, 600)
},
/** 点击任务卡片 → 跳转详情 */
onTaskTap(e: WechatMiniprogram.TouchEvent) {
const { id, tasktype } = e.currentTarget.dataset
const route = DETAIL_ROUTE_MAP[tasktype] || '/pages/task-detail/task-detail'
wx.navigateTo({
url: `${route}?id=${id}`,
fail: () => wx.showToast({ title: '页面跳转失败', icon: 'none' }),
})
},
/** 长按任务卡片 → 操作菜单 */
onTaskLongPress(e: WechatMiniprogram.TouchEvent) {
const { id, name } = e.currentTarget.dataset
wx.showActionSheet({
itemList: ['查看详情', '标记完成', '添加备注'],
success: (res) => {
switch (res.tapIndex) {
case 0: {
// 查看详情 — 复用点击逻辑
const { tasktype } = e.currentTarget.dataset
const route = DETAIL_ROUTE_MAP[tasktype] || '/pages/task-detail/task-detail'
wx.navigateTo({ url: `${route}?id=${id}` })
break
}
case 1:
wx.showToast({ title: `已标记「${name}」完成`, icon: 'success' })
break
case 2:
wx.showToast({ title: `为「${name}」添加备注`, icon: 'none' })
break
}
},
})
},
})

View File

@@ -0,0 +1,88 @@
<!-- 任务列表页 -->
<view class="page-task-list">
<!-- ====== 顶部绩效 Banner ====== -->
<banner theme="blue" title="{{bannerTitle}}" metrics="{{bannerMetrics}}" />
<!-- ====== 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:elif="{{pageState === 'empty'}}">
<t-icon name="task" size="160rpx" color="#dcdcdc" />
<text class="empty-text">暂无待办任务</text>
</view>
<!-- ====== 正常态:任务列表 ====== -->
<view class="task-section" wx:elif="{{pageState === 'normal'}}">
<!-- 标题行 -->
<view class="section-header">
<text class="section-title">今日 客户维护</text>
<text class="section-count">共 {{taskCount}} 项</text>
</view>
<!-- 任务卡片列表 -->
<view
class="task-card"
wx:for="{{tasks}}"
wx:key="id"
data-id="{{item.id}}"
data-name="{{item.customerName}}"
data-tasktype="{{item.taskType}}"
bindtap="onTaskTap"
bindlongpress="onTaskLongPress"
>
<!-- 左侧彩色边条 -->
<view class="card-border" style="background-color: {{item.typeColor}};"></view>
<view class="card-body">
<!-- 第一行:标签 + 客户名 + 爱心 + 备注 -->
<view class="card-row-1">
<view class="task-type-tag" style="background-color: {{item.typeColor}};">
<text class="tag-text">{{item.taskTypeLabel}}</text>
</view>
<text class="customer-name">{{item.customerName}}</text>
<heart-icon score="{{item.heartScore}}" />
<text class="note-indicator" wx:if="{{item.hasNote}}">📝</text>
<text class="pin-indicator" wx:if="{{item.isPinned}}">📌</text>
</view>
<!-- 第二行:截止时间 -->
<view class="card-row-2">
<text class="deadline-text">截止:{{item.deadline}}</text>
</view>
<!-- 第三行:喜好标签 -->
<view class="card-row-3" wx:if="{{item.hobbies.length > 0}}">
<hobby-tag
wx:for="{{item.hobbies}}"
wx:for-item="hobby"
wx:key="*this"
type="{{hobby}}"
/>
</view>
</view>
<!-- 右侧箭头 -->
<view class="card-arrow">
<text class="arrow-icon"></text>
</view>
</view>
<!-- 加载更多提示 -->
<view class="load-more" wx:if="{{!hasMore}}">
<text class="load-more-text">— 没有更多了 —</text>
</view>
</view>
<!-- ====== AI 悬浮按钮 ====== -->
<ai-float-button visible="{{pageState !== 'loading'}}" bottom="{{200}}" />
</view>
<dev-fab />

View File

@@ -0,0 +1,173 @@
/* 任务列表页样式 */
.page-task-list {
min-height: 100vh;
background-color: var(--color-gray-1);
padding-bottom: 180rpx; /* 为 AI 悬浮按钮 + 安全区留空 */
}
/* ====== Loading 骨架屏 ====== */
.state-loading {
padding: 32rpx;
}
.loading-placeholder {
background: #fff;
border-radius: var(--radius-lg);
padding: 32rpx;
margin-bottom: 24rpx;
}
.ph-line {
height: 24rpx;
border-radius: 12rpx;
background: linear-gradient(90deg, var(--color-gray-2) 25%, var(--color-gray-1) 50%, var(--color-gray-2) 75%);
background-size: 400% 100%;
animation: shimmer 1.5s infinite;
margin-bottom: 20rpx;
}
.ph-line--title {
width: 40%;
height: 32rpx;
}
.ph-line--body {
width: 80%;
}
.ph-line--short {
width: 55%;
margin-bottom: 0;
}
@keyframes shimmer {
0% { background-position: 100% 0; }
100% { background-position: -100% 0; }
}
/* ====== 空状态 ====== */
.state-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 200rpx;
}
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 24rpx;
opacity: 0.5;
}
.empty-text {
font-size: var(--font-sm);
color: var(--color-gray-6);
}
/* ====== 任务区域 ====== */
.task-section {
padding: 32rpx;
}
/* 标题行 */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.section-title {
font-size: var(--font-base);
font-weight: 600;
color: var(--color-gray-13);
}
.section-count {
font-size: var(--font-sm);
color: var(--color-gray-6);
}
/* ====== 任务卡片 ====== */
.task-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
margin-bottom: 24rpx;
overflow: hidden;
}
.task-card:active {
opacity: 0.85;
}
/* 左侧彩色边条 */
.card-border {
width: 8rpx;
flex-shrink: 0;
}
/* 卡片主体 */
.card-body {
flex: 1;
padding: 28rpx 24rpx;
min-width: 0; /* 防止溢出 */
}
/* 第一行 */
.card-row-1 {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12rpx;
margin-bottom: 12rpx;
}
.task-type-tag {
padding: 4rpx 16rpx;
border-radius: var(--radius-sm);
}
.tag-text {
font-size: var(--font-xs);
color: #fff;
font-weight: 500;
}
.customer-name {
font-size: var(--font-base);
font-weight: 600;
color: var(--color-gray-13);
}
.note-indicator,
.pin-indicator {
font-size: var(--font-sm);
}
/* 第二行 */
.card-row-2 {
margin-bottom: 12rpx;
}
.deadline-text {
font-size: var(--font-sm);
color: var(--color-gray-7);
}
/* 第三行:喜好标签 */
.card-row-3 {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
/* 右侧箭头 */
.card-arrow {
display: flex;
align-items: center;
padding-right: 24rpx;
flex-shrink: 0;
}
.arrow-icon {
font-size: 40rpx;
color: var(--color-gray-5);
}
/* ====== 加载更多 ====== */
.load-more {
text-align: center;
padding: 32rpx 0;
}
.load-more-text {
font-size: var(--font-xs);
color: var(--color-gray-6);
}