|
@@ -1,10 +1,20 @@
|
|
|
<template>
|
|
<template>
|
|
|
<view class="container" :style="{ '--font-scale': fontScale }">
|
|
<view class="container" :style="{ '--font-scale': fontScale }">
|
|
|
- <!-- 顶部:任务归属说明(只显示/操作当前登录用户本人的任务) -->
|
|
|
|
|
- <view class="owner-bar">
|
|
|
|
|
- <text class="owner-label">当前账号</text>
|
|
|
|
|
- <text class="owner-name">{{ selfMember ? selfMember.nickname : '我' }}</text>
|
|
|
|
|
- <text class="owner-tip">仅显示并可操作本人的任务</text>
|
|
|
|
|
|
|
+ <!-- 页头:当前账号 + 成员选择横向滚动条 -->
|
|
|
|
|
+ <view class="page-header">
|
|
|
|
|
+ <view class="header-account">
|
|
|
|
|
+ <text class="account-label">当前账号</text>
|
|
|
|
|
+ <text class="account-name">{{ selfMember ? selfMember.nickname : '我' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="header-tip">可查看家人的任务,仅能完成自己的</view>
|
|
|
|
|
+ <scroll-view scroll-x class="member-scroll">
|
|
|
|
|
+ <view class="member-chips">
|
|
|
|
|
+ <view class="member-chip" :class="{ active: !selectedMemberId }" @click="selectMember(null)">本人</view>
|
|
|
|
|
+ <view class="member-chip" v-for="member in familyMembers" :key="member.id" :class="{ active: selectedMemberId && String(selectedMemberId) === String(member.id) }" @click="selectMember(member.id)">
|
|
|
|
|
+ {{ member.nickname }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- Tab 切换 -->
|
|
<!-- Tab 切换 -->
|
|
@@ -23,85 +33,119 @@
|
|
|
|
|
|
|
|
<!-- 待接受任务 -->
|
|
<!-- 待接受任务 -->
|
|
|
<view class="task-list" v-if="activeTab === 'pending'">
|
|
<view class="task-list" v-if="activeTab === 'pending'">
|
|
|
- <view class="task-item assigned" v-for="task in pendingTasks" :key="task.id">
|
|
|
|
|
- <view class="task-left">
|
|
|
|
|
- <view class="task-title">
|
|
|
|
|
- {{ task.title }}
|
|
|
|
|
- <text v-if="task.memberOnly === 1" class="member-badge">会员任务</text>
|
|
|
|
|
|
|
+ <view class="task-card" v-for="task in pendingTasks" :key="task.id">
|
|
|
|
|
+ <view class="card-accent-line"></view>
|
|
|
|
|
+ <view class="card-body">
|
|
|
|
|
+ <view class="card-left">
|
|
|
|
|
+ <view class="card-top-row">
|
|
|
|
|
+ <text class="card-type-icon" v-if="task.category === '小游戏类'">🎮</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'cart' || task.actionType === 'order'">🛒</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'focus'">⏱️</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'survey'">📝</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'article'">📖</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'activity'">🎯</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.requireInput === 1">✍️</text>
|
|
|
|
|
+ <text class="card-title">{{ task.title }}</text>
|
|
|
|
|
+ <text class="member-pill" v-if="task.memberOnly === 1">会员</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="card-meta">
|
|
|
|
|
+ <text class="points-pill">+{{ task.points }} CF</text>
|
|
|
|
|
+ <text class="meta-dot">·</text>
|
|
|
|
|
+ <text class="card-creator">创建人:{{ task.creatorName || '家人' }}</text>
|
|
|
|
|
+ <text class="meta-dot">·</text>
|
|
|
|
|
+ <text class="card-deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="task-meta">
|
|
|
|
|
- <text class="points">+{{ task.points }}CF</text>
|
|
|
|
|
- <text class="creator">创建人:{{ task.creatorName || '家人' }}</text>
|
|
|
|
|
- <text class="deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
|
|
|
|
|
+ <view class="card-actions">
|
|
|
|
|
+ <button class="btn-accept" @click="acceptTask(task)">接受</button>
|
|
|
|
|
+ <button class="btn-reject" @click="showRejectModal(task)">拒绝</button>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="task-actions">
|
|
|
|
|
- <button class="btn-accept" @click="acceptTask(task)">接受</button>
|
|
|
|
|
- <button class="btn-reject" @click="showRejectModal(task)">拒绝</button>
|
|
|
|
|
- </view>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="empty" v-if="!loading && pendingTasks.length === 0">
|
|
|
|
|
- <text>暂无待接受任务</text>
|
|
|
|
|
|
|
+ <view class="empty-state" v-if="!loading && pendingTasks.length === 0">
|
|
|
|
|
+ <view class="empty-icon">📭</view>
|
|
|
|
|
+ <text class="empty-main">暂无待接受任务</text>
|
|
|
|
|
+ <text class="empty-sub">收到任务后会出现在这里</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 今日任务 -->
|
|
<!-- 今日任务 -->
|
|
|
<view class="task-list" v-if="activeTab === 'today'">
|
|
<view class="task-list" v-if="activeTab === 'today'">
|
|
|
- <view class="task-item" v-for="task in todayTasks" :key="task.id" :class="{ completed: task.status === 'completed', locked: task.status === 'locked' }">
|
|
|
|
|
- <view class="task-left">
|
|
|
|
|
- <view class="task-title">
|
|
|
|
|
- {{ task.title }}
|
|
|
|
|
- <text v-if="task.memberOnly === 1" class="member-badge">会员任务</text>
|
|
|
|
|
- <text v-if="task.category === '小游戏类'" class="minigame-badge">🎮</text>
|
|
|
|
|
- <text v-if="task.actionType === 'cart' || task.actionType === 'order'" class="minigame-badge">🛒</text>
|
|
|
|
|
- <text v-if="task.actionType === 'focus'" class="minigame-badge">⏱️</text>
|
|
|
|
|
- <text v-if="task.actionType === 'survey'" class="minigame-badge">📝</text>
|
|
|
|
|
- <text v-if="task.actionType === 'article'" class="minigame-badge">📖</text>
|
|
|
|
|
- <text v-if="task.actionType === 'activity'" class="minigame-badge">🎯</text>
|
|
|
|
|
- <text v-if="task.requireInput === 1" class="minigame-badge">✍️</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="task-meta">
|
|
|
|
|
- <text class="points">+{{ task.points }}CF</text>
|
|
|
|
|
- <text class="deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <!-- 进度型任务:展示进度条 -->
|
|
|
|
|
- <view class="progress-wrap" v-if="task.isDailyProgress === 1">
|
|
|
|
|
- <view class="progress-bar">
|
|
|
|
|
- <view class="progress-fill" :style="{ width: getProgressPercent(task) + '%' }"></view>
|
|
|
|
|
|
|
+ <view class="task-card" v-for="task in todayTasks" :key="task.id" :class="{ completed: task.status === 'completed', locked: task.status === 'locked' }">
|
|
|
|
|
+ <view class="card-accent-line" :class="'line-' + task.status"></view>
|
|
|
|
|
+ <view class="card-body">
|
|
|
|
|
+ <view class="card-left">
|
|
|
|
|
+ <view class="card-top-row">
|
|
|
|
|
+ <text class="card-type-icon" v-if="task.category === '小游戏类'">🎮</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'cart' || task.actionType === 'order'">🛒</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'focus'">⏱️</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'survey'">📝</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'article'">📖</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'activity'">🎯</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.requireInput === 1">✍️</text>
|
|
|
|
|
+ <text class="card-title">{{ task.title }}</text>
|
|
|
|
|
+ <text class="member-pill" v-if="task.memberOnly === 1">会员</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="card-meta">
|
|
|
|
|
+ <text class="points-pill">+{{ task.points }} CF</text>
|
|
|
|
|
+ <text class="meta-dot">·</text>
|
|
|
|
|
+ <text class="card-deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <!-- 进度型任务:展示进度条 -->
|
|
|
|
|
+ <view class="progress-wrap" v-if="task.isDailyProgress === 1">
|
|
|
|
|
+ <view class="progress-track">
|
|
|
|
|
+ <view class="progress-fill" :style="{ width: getProgressPercent(task) + '%' }"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="progress-text">{{ task.progress || 0 }}/{{ task.targetValue || 1 }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <text class="progress-text">{{ task.progress || 0 }}/{{ task.targetValue || 1 }}</text>
|
|
|
|
|
|
|
+ <text v-if="task.status === 'locked'" class="lock-tip">🔒 需先完成前置任务后解锁</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="card-right">
|
|
|
|
|
+ <button v-if="task.status === 'pending' || task.status === 'in_progress'" class="btn-complete" @click="handleTaskAction(task)">{{ getActionLabel(task) }}</button>
|
|
|
|
|
+ <view v-else-if="task.status === 'completed'" class="badge badge-completed">已完成</view>
|
|
|
|
|
+ <view v-else-if="task.status === 'pending_review'" class="badge badge-review">待审核</view>
|
|
|
|
|
+ <view v-else class="badge badge-lock">🔒 未解锁</view>
|
|
|
</view>
|
|
</view>
|
|
|
- <text v-if="task.status === 'locked'" class="lock-tip">🔒 需先完成前置任务后解锁</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="task-right" v-if="task.status === 'pending' || task.status === 'in_progress'">
|
|
|
|
|
- <button class="btn-complete" @click="handleTaskAction(task)">{{ getActionLabel(task) }}</button>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="completed-badge" v-else-if="task.status === 'completed'">已完成</view>
|
|
|
|
|
- <view class="review-badge" v-else-if="task.status === 'pending_review'">待审核</view>
|
|
|
|
|
- <view class="lock-badge" v-else>🔒 未解锁</view>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="empty" v-if="!loading && todayTasks.length === 0">
|
|
|
|
|
- <text>暂无今日任务</text>
|
|
|
|
|
|
|
+ <view class="empty-state" v-if="!loading && todayTasks.length === 0">
|
|
|
|
|
+ <view class="empty-icon">✅</view>
|
|
|
|
|
+ <text class="empty-main">今日任务已全部完成</text>
|
|
|
|
|
+ <text class="empty-sub">继续保持,明天继续加油!</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 我创建的任务 -->
|
|
<!-- 我创建的任务 -->
|
|
|
<view class="task-list" v-if="activeTab === 'created'">
|
|
<view class="task-list" v-if="activeTab === 'created'">
|
|
|
- <view class="task-item" v-for="task in createdTasks" :key="task.id">
|
|
|
|
|
- <view class="task-left">
|
|
|
|
|
- <view class="task-title">
|
|
|
|
|
- {{ task.title }}
|
|
|
|
|
- <text v-if="task.memberOnly === 1" class="member-badge">会员任务</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="task-meta">
|
|
|
|
|
- <text class="points">+{{ task.points }}CF</text>
|
|
|
|
|
- <text class="assignee">{{ task.executorName || '未分配' }}</text>
|
|
|
|
|
- <text class="status-tag" :class="'status-' + task.status">{{ statusLabel(task.status) }}</text>
|
|
|
|
|
|
|
+ <view class="task-card" v-for="task in createdTasks" :key="task.id">
|
|
|
|
|
+ <view class="card-accent-line"></view>
|
|
|
|
|
+ <view class="card-body card-body-row">
|
|
|
|
|
+ <view class="card-left">
|
|
|
|
|
+ <view class="card-top-row">
|
|
|
|
|
+ <text class="card-type-icon" v-if="task.category === '小游戏类'">🎮</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'cart' || task.actionType === 'order'">🛒</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'focus'">⏱️</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'survey'">📝</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'article'">📖</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.actionType === 'activity'">🎯</text>
|
|
|
|
|
+ <text class="card-type-icon" v-else-if="task.requireInput === 1">✍️</text>
|
|
|
|
|
+ <text class="card-title">{{ task.title }}</text>
|
|
|
|
|
+ <text class="member-pill" v-if="task.memberOnly === 1">会员</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="card-meta">
|
|
|
|
|
+ <text class="points-pill">+{{ task.points }} CF</text>
|
|
|
|
|
+ <text class="meta-dot">·</text>
|
|
|
|
|
+ <text class="card-assignee">{{ task.executorName || '未分配' }}</text>
|
|
|
|
|
+ <text class="meta-dot">·</text>
|
|
|
|
|
+ <text class="status-tag" :class="'status-' + task.status">{{ statusLabel(task.status) }}</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="empty" v-if="!loading && createdTasks.length === 0">
|
|
|
|
|
- <text>暂无创建的任务</text>
|
|
|
|
|
|
|
+ <view class="empty-state" v-if="!loading && createdTasks.length === 0">
|
|
|
|
|
+ <view class="empty-icon">📋</view>
|
|
|
|
|
+ <text class="empty-main">暂无创建的任务</text>
|
|
|
|
|
+ <text class="empty-sub">点击底部按钮创建新任务</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
@@ -219,6 +263,7 @@ export default {
|
|
|
isParent: false,
|
|
isParent: false,
|
|
|
isMember: false,
|
|
isMember: false,
|
|
|
selfMemberId: null,
|
|
selfMemberId: null,
|
|
|
|
|
+ selectedMemberId: null,
|
|
|
currentUserId: null,
|
|
currentUserId: null,
|
|
|
familyMembers: [],
|
|
familyMembers: [],
|
|
|
showAddModal: false,
|
|
showAddModal: false,
|
|
@@ -277,6 +322,12 @@ export default {
|
|
|
}
|
|
}
|
|
|
this.selfMemberId = null
|
|
this.selfMemberId = null
|
|
|
},
|
|
},
|
|
|
|
|
+ // 切换今日任务的查看成员:选中成员的任务列表(选中本人/不传 memberId 由后端按 JWT 兜底)
|
|
|
|
|
+ selectMember(memberId) {
|
|
|
|
|
+ this.selectedMemberId = memberId || null
|
|
|
|
|
+ this.activeTab = 'today'
|
|
|
|
|
+ this.loadTodayTasks(this.selectedMemberId)
|
|
|
|
|
+ },
|
|
|
// 是否本人的任务:兜底防呆(列表本身已按本人拉取,防御脏数据)
|
|
// 是否本人的任务:兜底防呆(列表本身已按本人拉取,防御脏数据)
|
|
|
isOwnTask(task) {
|
|
isOwnTask(task) {
|
|
|
if (!task || this.selfMemberId == null || task.familyMemberId == null) return true
|
|
if (!task || this.selfMemberId == null || task.familyMemberId == null) return true
|
|
@@ -323,10 +374,11 @@ export default {
|
|
|
console.error('加载待接受任务失败', e)
|
|
console.error('加载待接受任务失败', e)
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- async loadTodayTasks() {
|
|
|
|
|
|
|
+ async loadTodayTasks(memberId) {
|
|
|
try {
|
|
try {
|
|
|
- // 只拉取本人的今日任务(不传 memberId 时后端按 JWT 兜底)
|
|
|
|
|
- const res = await getTodayTasks(this.selfMemberId)
|
|
|
|
|
|
|
+ // 选中成员 → 传该成员 memberId;选中本人 → 传 selfMemberId(后端亦按 JWT currentMemberId 兜底)
|
|
|
|
|
+ var targetMemberId = arguments.length > 0 ? memberId : this.selectedMemberId
|
|
|
|
|
+ const res = await getTodayTasks(targetMemberId)
|
|
|
this.todayTasks = res.data || []
|
|
this.todayTasks = res.data || []
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.error('加载今日任务失败', e)
|
|
console.error('加载今日任务失败', e)
|
|
@@ -366,14 +418,22 @@ export default {
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
const res = await completeTaskApi(task.id, this.selfMemberId, '')
|
|
const res = await completeTaskApi(task.id, this.selfMemberId, '')
|
|
|
|
|
+ if (res && res.code !== 200) {
|
|
|
|
|
+ // B2:后端明确返回错误(任务不存在/无权完成/状态已变更),杜绝静默失败
|
|
|
|
|
+ uni.showToast({ title: (res && res.message) || '完成失败', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
if (res.data && res.data.needReview) {
|
|
if (res.data && res.data.needReview) {
|
|
|
uni.showToast({ title: '已提交,待审核', icon: 'none' })
|
|
uni.showToast({ title: '已提交,待审核', icon: 'none' })
|
|
|
- } else {
|
|
|
|
|
|
|
+ } else if (res.data) {
|
|
|
uni.showToast({ title: `获得 ${res.data.pointsEarned} CF`, icon: 'success' })
|
|
uni.showToast({ title: `获得 ${res.data.pointsEarned} CF`, icon: 'success' })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '只能完成自己的任务', icon: 'none' })
|
|
|
}
|
|
}
|
|
|
this.loadTodayTasks()
|
|
this.loadTodayTasks()
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.error('完成任务失败', e)
|
|
console.error('完成任务失败', e)
|
|
|
|
|
+ uni.showToast({ title: '完成失败,请重试', icon: 'none' })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
/** 按钮文案:按 action_type / start_required / require_input 分派 */
|
|
/** 按钮文案:按 action_type / start_required / require_input 分派 */
|
|
@@ -473,11 +533,14 @@ export default {
|
|
|
uni.navigateTo({ url: '/pages/shop/cart?taskId=' + taskId })
|
|
uni.navigateTo({ url: '/pages/shop/cart?taskId=' + taskId })
|
|
|
}
|
|
}
|
|
|
} else if (at === 'diet') {
|
|
} else if (at === 'diet') {
|
|
|
- uni.navigateTo({ url: '/pages/growth/diet-checkin?taskId=' + taskId })
|
|
|
|
|
|
|
+ var mIdT = (task.familyMemberId || task.executorId || '')
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/growth/diet-checkin?taskId=' + taskId + (mIdT ? '&memberId=' + mIdT : '') })
|
|
|
} else if (at === 'sleep') {
|
|
} else if (at === 'sleep') {
|
|
|
- uni.navigateTo({ url: '/pages/growth/sleep-checkin?taskId=' + taskId })
|
|
|
|
|
|
|
+ var mIdTs = (task.familyMemberId || task.executorId || '')
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/growth/sleep-checkin?taskId=' + taskId + (mIdTs ? '&memberId=' + mIdTs : '') })
|
|
|
} else if (at === 'exercise') {
|
|
} else if (at === 'exercise') {
|
|
|
- uni.navigateTo({ url: '/pages/growth/exercise-checkin?taskId=' + taskId })
|
|
|
|
|
|
|
+ var mIdTe = (task.familyMemberId || task.executorId || '')
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/growth/exercise-checkin?taskId=' + taskId + (mIdTe ? '&memberId=' + mIdTe : '') })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
closeInputModal() {
|
|
closeInputModal() {
|
|
@@ -496,10 +559,17 @@ export default {
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
const res = await completeTaskApi(task.id, this.selfMemberId, '', this.inputContent, 'text')
|
|
const res = await completeTaskApi(task.id, this.selfMemberId, '', this.inputContent, 'text')
|
|
|
|
|
+ if (res && res.code !== 200) {
|
|
|
|
|
+ uni.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
|
|
|
|
|
+ this.closeInputModal()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
if (res.data && res.data.needReview) {
|
|
if (res.data && res.data.needReview) {
|
|
|
uni.showToast({ title: '已提交,待审核', icon: 'none' })
|
|
uni.showToast({ title: '已提交,待审核', icon: 'none' })
|
|
|
- } else {
|
|
|
|
|
|
|
+ } else if (res.data) {
|
|
|
uni.showToast({ title: `获得 ${res.data.pointsEarned} CF`, icon: 'success' })
|
|
uni.showToast({ title: `获得 ${res.data.pointsEarned} CF`, icon: 'success' })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '只能完成自己的任务', icon: 'none' })
|
|
|
}
|
|
}
|
|
|
this.closeInputModal()
|
|
this.closeInputModal()
|
|
|
this.loadTodayTasks()
|
|
this.loadTodayTasks()
|
|
@@ -642,55 +712,93 @@ export default {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
|
|
|
+/* ============ 间距节奏统一:所有区块间距为 8 的倍数 ============ */
|
|
|
.container {
|
|
.container {
|
|
|
- padding: 20rpx 30rpx 120rpx;
|
|
|
|
|
|
|
+ padding: 16rpx 32rpx 120rpx;
|
|
|
background: $app-bg;
|
|
background: $app-bg;
|
|
|
min-height: 100vh;
|
|
min-height: 100vh;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 当前账号提示条(任务只归属当前登录用户本人) */
|
|
|
|
|
-.owner-bar {
|
|
|
|
|
|
|
+/* ============ 页头:当前账号 + 成员选择(置顶) ============ */
|
|
|
|
|
+.page-header {
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: $uni-radius-lg;
|
|
|
|
|
+ padding: $uni-spacing-md;
|
|
|
|
|
+ box-shadow: $uni-shadow-sm;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.header-account {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- margin: 8rpx 0 16rpx;
|
|
|
|
|
- padding: 14rpx 24rpx;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
|
|
|
|
|
|
+ margin-bottom: $uni-spacing-sm;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.owner-label {
|
|
|
|
|
|
|
+.account-label {
|
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
color: $app-surface;
|
|
color: $app-surface;
|
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
border-radius: 8rpx;
|
|
border-radius: 8rpx;
|
|
|
padding: 4rpx 12rpx;
|
|
padding: 4rpx 12rpx;
|
|
|
- margin-right: 16rpx;
|
|
|
|
|
|
|
+ margin-right: $uni-spacing-sm;
|
|
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.owner-name {
|
|
|
|
|
- font-size: calc(28rpx * var(--font-scale, 1));
|
|
|
|
|
|
|
+.account-name {
|
|
|
|
|
+ font-size: calc(30rpx * var(--font-scale, 1));
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
color: $facade-text-strong;
|
|
color: $facade-text-strong;
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
text-overflow: ellipsis;
|
|
|
white-space: nowrap;
|
|
white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.owner-tip {
|
|
|
|
|
|
|
+.header-tip {
|
|
|
|
|
+ display: block;
|
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
color: $facade-text-muted;
|
|
color: $facade-text-muted;
|
|
|
|
|
+ margin-bottom: $uni-spacing-sm;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 成员选择横向滚动条 */
|
|
|
|
|
+.member-scroll {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.member-chips {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding-bottom: 4rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.member-chip {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $facade-text-body;
|
|
|
|
|
+ background: $facade-fill-gray-100;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
|
|
+ padding: 10rpx 24rpx;
|
|
|
|
|
+ margin-right: $uni-spacing-sm;
|
|
|
|
|
+ transition: background $dur-state $ease-standard, color $dur-state $ease-standard;
|
|
|
|
|
+
|
|
|
|
|
+ &.active {
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* ============ Tab 栏:胶囊切换 ============ */
|
|
|
.tab-bar {
|
|
.tab-bar {
|
|
|
- margin-top: 16rpx;
|
|
|
|
|
display: flex;
|
|
display: flex;
|
|
|
background: $app-surface;
|
|
background: $app-surface;
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- padding: 10rpx;
|
|
|
|
|
- margin-bottom: 30rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
|
|
+ border-radius: $uni-radius-lg;
|
|
|
|
|
+ padding: 8rpx;
|
|
|
|
|
+ margin: $uni-spacing-md 0;
|
|
|
|
|
+ box-shadow: $uni-shadow-sm;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.tab-item {
|
|
.tab-item {
|
|
@@ -698,17 +806,19 @@ export default {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
- padding: 20rpx 0;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
|
|
+ height: 64rpx;
|
|
|
|
|
+ border-radius: $uni-radius-sm;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
-}
|
|
|
|
|
|
|
+ transition: background $dur-state $ease-standard;
|
|
|
|
|
|
|
|
-.tab-item.active {
|
|
|
|
|
- background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
|
|
+ &.active {
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(249, 115, 22, 0.25);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.tab-text {
|
|
.tab-text {
|
|
|
- font-size: 28rpx;
|
|
|
|
|
|
|
+ font-size: calc(26rpx * var(--font-scale, 1));
|
|
|
color: $facade-text-body;
|
|
color: $facade-text-body;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -719,195 +829,266 @@ export default {
|
|
|
|
|
|
|
|
.tab-badge {
|
|
.tab-badge {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
- top: 8rpx;
|
|
|
|
|
- right: 20rpx;
|
|
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ right: 16rpx;
|
|
|
background: $status-danger;
|
|
background: $status-danger;
|
|
|
color: $app-surface;
|
|
color: $app-surface;
|
|
|
- font-size: 20rpx;
|
|
|
|
|
|
|
+ font-size: 18rpx;
|
|
|
min-width: 28rpx;
|
|
min-width: 28rpx;
|
|
|
height: 28rpx;
|
|
height: 28rpx;
|
|
|
line-height: 28rpx;
|
|
line-height: 28rpx;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
- border-radius: 14rpx;
|
|
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
padding: 0 6rpx;
|
|
padding: 0 6rpx;
|
|
|
|
|
+ transform: translateY(-8rpx);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* ============ 任务列表统一容器 ============ */
|
|
|
.task-list {
|
|
.task-list {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
- gap: 20rpx;
|
|
|
|
|
|
|
+ gap: $uni-spacing-md;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
|
|
+/* ============ 任务卡片 ============ */
|
|
|
|
|
+.task-card {
|
|
|
|
|
+ position: relative;
|
|
|
background: $app-surface;
|
|
background: $app-surface;
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 28rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
|
|
|
|
|
|
+ border-radius: $uni-radius-lg;
|
|
|
|
|
+ box-shadow: $uni-shadow-sm;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+
|
|
|
|
|
+ &.completed {
|
|
|
|
|
+ opacity: 0.65;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.locked {
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-item.completed {
|
|
|
|
|
- opacity: 0.7;
|
|
|
|
|
|
|
+/* 左侧色条(按状态区分) */
|
|
|
|
|
+.card-accent-line {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ width: 5rpx;
|
|
|
|
|
+
|
|
|
|
|
+ &.line-completed { background: $status-success; }
|
|
|
|
|
+ &.line-locked { background: $facade-text-gray-400; }
|
|
|
|
|
+ &.line-pending_review { background: $status-warning; }
|
|
|
|
|
+ &.line-in_progress { background: $brand-primary; }
|
|
|
|
|
+ &.line-assigned { background: $brand-primary; }
|
|
|
|
|
+ &:not(.line-completed):not(.line-locked):not(.line-pending_review):not(.line-in_progress):not(.line-assigned) {
|
|
|
|
|
+ background: $brand-primary; /* pending / 默认 */
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-item.locked {
|
|
|
|
|
- opacity: 0.55;
|
|
|
|
|
|
|
+.card-body {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: $uni-spacing-lg;
|
|
|
|
|
+ gap: $uni-spacing-lg;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-item.assigned {
|
|
|
|
|
- border-left: 6rpx solid $brand-primary;
|
|
|
|
|
|
|
+.card-body-row {
|
|
|
|
|
+ flex-direction: row;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-left {
|
|
|
|
|
|
|
+.card-left {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
min-width: 0;
|
|
|
- padding-right: 20rpx;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-title {
|
|
|
|
|
|
|
+/* 标题行:类型图标 + 标题 + 会员药丸 */
|
|
|
|
|
+.card-top-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: $uni-spacing-xs;
|
|
|
|
|
+ margin-bottom: $uni-spacing-sm;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-type-icon {
|
|
|
|
|
+ font-size: calc(28rpx * var(--font-scale, 1));
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-title {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
font-size: calc(30rpx * var(--font-scale, 1));
|
|
font-size: calc(30rpx * var(--font-scale, 1));
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
font-weight: 500;
|
|
font-weight: 500;
|
|
|
- margin-bottom: 12rpx;
|
|
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.member-badge {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
|
|
+.member-pill {
|
|
|
|
|
+ font-size: calc(20rpx * var(--font-scale, 1));
|
|
|
color: $brand-primary;
|
|
color: $brand-primary;
|
|
|
background: $uni-primary-pale;
|
|
background: $uni-primary-pale;
|
|
|
- padding: 2rpx 10rpx;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
- margin-left: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.minigame-badge {
|
|
|
|
|
- margin-left: 8rpx;
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-meta {
|
|
|
|
|
|
|
+/* 元信息:积分药丸 + 分隔点 + 创建人/执行人/截止 */
|
|
|
|
|
+.card-meta {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
- gap: 16rpx;
|
|
|
|
|
- font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: $uni-spacing-xs;
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
color: $facade-text-muted;
|
|
color: $facade-text-muted;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.points {
|
|
|
|
|
|
|
+.points-pill {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
+ font-weight: 600;
|
|
|
color: $brand-primary;
|
|
color: $brand-primary;
|
|
|
- font-weight: 500;
|
|
|
|
|
|
|
+ background: $brand-primary-soft;
|
|
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.deadline, .creator, .assignee {
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
|
|
+.meta-dot {
|
|
|
|
|
+ color: $facade-text-gray-400;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.lock-tip {
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-top: 8rpx;
|
|
|
|
|
- font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
- color: $facade-text-gray-400;
|
|
|
|
|
|
|
+.card-creator,
|
|
|
|
|
+.card-assignee,
|
|
|
|
|
+.card-deadline {
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* 进度条 */
|
|
|
.progress-wrap {
|
|
.progress-wrap {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- margin-top: 12rpx;
|
|
|
|
|
|
|
+ margin-top: $uni-spacing-sm;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.progress-bar {
|
|
|
|
|
|
|
+.progress-track {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
- height: 12rpx;
|
|
|
|
|
|
|
+ height: 10rpx;
|
|
|
background: $facade-fill-gray-100;
|
|
background: $facade-fill-gray-100;
|
|
|
- border-radius: 6rpx;
|
|
|
|
|
|
|
+ border-radius: 5rpx;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
- margin-right: 12rpx;
|
|
|
|
|
|
|
+ margin-right: $uni-spacing-sm;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.progress-fill {
|
|
.progress-fill {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
- border-radius: 6rpx;
|
|
|
|
|
- transition: width 0.3s ease;
|
|
|
|
|
|
|
+ border-radius: 5rpx;
|
|
|
|
|
+ transition: width $dur-structural $ease-standard;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.progress-text {
|
|
.progress-text {
|
|
|
- font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
|
|
+ font-size: calc(20rpx * var(--font-scale, 1));
|
|
|
color: $brand-primary;
|
|
color: $brand-primary;
|
|
|
white-space: nowrap;
|
|
white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.task-right {
|
|
|
|
|
|
|
+.lock-tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: $uni-spacing-xs;
|
|
|
|
|
+ font-size: calc(20rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $facade-text-gray-400;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* ============ 卡片右侧:操作区 ============ */
|
|
|
|
|
+.card-right {
|
|
|
flex-shrink: 0;
|
|
flex-shrink: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.card-actions {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: $uni-spacing-sm;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 主按钮:品牌渐变(完成/接受) */
|
|
|
.btn-complete {
|
|
.btn-complete {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
color: $app-surface;
|
|
color: $app-surface;
|
|
|
- font-size: 26rpx;
|
|
|
|
|
|
|
+ font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
+ font-weight: 500;
|
|
|
padding: 0 28rpx;
|
|
padding: 0 28rpx;
|
|
|
height: 64rpx;
|
|
height: 64rpx;
|
|
|
line-height: 64rpx;
|
|
line-height: 64rpx;
|
|
|
border-radius: 32rpx;
|
|
border-radius: 32rpx;
|
|
|
border: none;
|
|
border: none;
|
|
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(249, 115, 22, 0.25);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.task-actions {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 12rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+.btn-complete::after { border: none; }
|
|
|
|
|
|
|
|
.btn-accept {
|
|
.btn-accept {
|
|
|
- background: $status-success;
|
|
|
|
|
|
|
+ background: linear-gradient(135deg, $status-success, $status-success-light-3);
|
|
|
color: $app-surface;
|
|
color: $app-surface;
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- padding: 0 24rpx;
|
|
|
|
|
- height: 56rpx;
|
|
|
|
|
- line-height: 56rpx;
|
|
|
|
|
- border-radius: 28rpx;
|
|
|
|
|
|
|
+ font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ padding: 0 32rpx;
|
|
|
|
|
+ height: 60rpx;
|
|
|
|
|
+ line-height: 60rpx;
|
|
|
|
|
+ border-radius: 30rpx;
|
|
|
border: none;
|
|
border: none;
|
|
|
}
|
|
}
|
|
|
|
|
+.btn-accept::after { border: none; }
|
|
|
|
|
|
|
|
|
|
+/* 次按钮:描边(拒绝) */
|
|
|
.btn-reject {
|
|
.btn-reject {
|
|
|
- background: $facade-fill-gray-100;
|
|
|
|
|
|
|
+ background: $app-surface;
|
|
|
color: $facade-text-body;
|
|
color: $facade-text-body;
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- padding: 0 24rpx;
|
|
|
|
|
- height: 56rpx;
|
|
|
|
|
- line-height: 56rpx;
|
|
|
|
|
- border-radius: 28rpx;
|
|
|
|
|
- border: none;
|
|
|
|
|
|
|
+ font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
+ padding: 0 32rpx;
|
|
|
|
|
+ height: 60rpx;
|
|
|
|
|
+ line-height: 58rpx;
|
|
|
|
|
+ border-radius: 30rpx;
|
|
|
|
|
+ border: 2rpx solid $facade-gray-200;
|
|
|
}
|
|
}
|
|
|
|
|
+.btn-reject::after { border: none; }
|
|
|
|
|
|
|
|
-.completed-badge, .review-badge, .lock-badge {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- padding: 8rpx 16rpx;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
|
|
+/* ============ 状态徽章(统一色系与圆角) ============ */
|
|
|
|
|
+.badge {
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
+ padding: 8rpx 18rpx;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
|
|
+ font-weight: 500;
|
|
|
flex-shrink: 0;
|
|
flex-shrink: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.completed-badge {
|
|
|
|
|
|
|
+.badge-completed {
|
|
|
background: $status-success-tint;
|
|
background: $status-success-tint;
|
|
|
color: $status-success;
|
|
color: $status-success;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.review-badge {
|
|
|
|
|
|
|
+.badge-review {
|
|
|
background: $uni-primary-pale;
|
|
background: $uni-primary-pale;
|
|
|
color: $brand-primary;
|
|
color: $brand-primary;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.lock-badge {
|
|
|
|
|
|
|
+.badge-lock {
|
|
|
background: $facade-fill-gray-100;
|
|
background: $facade-fill-gray-100;
|
|
|
color: $facade-text-gray-400;
|
|
color: $facade-text-gray-400;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* 我创建的 tab 状态小标签 */
|
|
|
.status-tag {
|
|
.status-tag {
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- padding: 2rpx 10rpx;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
|
|
+ font-size: calc(20rpx * var(--font-scale, 1));
|
|
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
background: $facade-fill-gray-100;
|
|
background: $facade-fill-gray-100;
|
|
|
color: $facade-text-body;
|
|
color: $facade-text-body;
|
|
|
}
|
|
}
|
|
@@ -920,13 +1101,29 @@ export default {
|
|
|
.status-rejected { background: $status-danger-soft; color: $status-danger; }
|
|
.status-rejected { background: $status-danger-soft; color: $status-danger; }
|
|
|
.status-locked { background: $facade-fill-gray-100; color: $facade-text-gray-400; }
|
|
.status-locked { background: $facade-fill-gray-100; color: $facade-text-gray-400; }
|
|
|
|
|
|
|
|
-.empty {
|
|
|
|
|
|
|
+/* ============ 空态 ============ */
|
|
|
|
|
+.empty-state {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
|
|
|
|
+ flex-direction: column;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- padding: 80rpx 0;
|
|
|
|
|
- color: $facade-text-gray-400;
|
|
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 96rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-icon {
|
|
|
|
|
+ font-size: calc(72rpx * var(--font-scale, 1));
|
|
|
|
|
+ margin-bottom: $uni-spacing-lg;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-main {
|
|
|
font-size: calc(28rpx * var(--font-scale, 1));
|
|
font-size: calc(28rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $facade-text-body;
|
|
|
|
|
+ margin-bottom: $uni-spacing-xs;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-sub {
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.add-btn {
|
|
.add-btn {
|