feat: 移动端双Token队列拦截器 + 通知列表 + 登录页 + TabBar红点 + v1.0.1

This commit is contained in:
2026-08-07 11:44:20 +08:00
parent 721cfe1504
commit 0df7215134
13 changed files with 1013 additions and 206 deletions

View File

@ -49,7 +49,7 @@
<text class="tag-branch">{{ branchLabelsMap[lockedTask.id] || '' }}</text>
<text class="fc-status">{{ statusLabel(lockedTask.status) }}</text>
<text v-if="lockedTask.is_rework" class="tag tag-rework-sm">⚠返工</text>
<text v-if="lockedTask.parent_task_id && lockedTask.status === 'WIP'" class="sub-branch-end"
<text v-if="lockedTask.parent_task_id && lockedTask.task_type === 'SPAWN' && lockedTask.status === 'WIP'" class="sub-branch-end"
@tap="$emit('action', { task: lockedTask, type: 'end' })">🛑 结束协助</text>
</view>
<text class="fc-name">{{ lockedTask.task_name }}</text>
@ -110,11 +110,29 @@ export default {
product: { type: Object, default: null },
currentUserId: { type: String, default: "" },
currentUsername: { type: String, default: "" },
initialLockTaskId: { type: String, default: "" },
},
emits: ["action", "viewRecords"],
data() {
return { lockedTaskId: null };
},
watch: {
initialLockTaskId: {
immediate: true,
handler(id) {
if (id && this.focusTasks.some(t => t.id === id)) {
this.lockedTaskId = id;
}
},
},
product: {
handler() {
if (this.initialLockTaskId && this.focusTasks.some(t => t.id === this.initialLockTaskId)) {
this.lockedTaskId = this.initialLockTaskId;
}
},
},
},
computed: {
taskMap() {
const m = {};
@ -133,8 +151,8 @@ export default {
};
if (this.product) walk(this.product.task_tree);
result.sort((a, b) => {
const aIsMain = !a.parent_task_id || a.task_type !== 'SPAWN';
const bIsMain = !b.parent_task_id || b.task_type !== 'SPAWN';
const aIsMain = !a.parent_task_id || a.task_type === 'TRANSFER' || a.task_type === 'RECOVERY';
const bIsMain = !b.parent_task_id || b.task_type === 'TRANSFER' || b.task_type === 'RECOVERY';
if (aIsMain && !bIsMain) return -1;
if (!aIsMain && bIsMain) return 1;
return new Date(a.created_at) - new Date(b.created_at);
@ -154,11 +172,31 @@ export default {
branchLabelsMap() {
const map = {};
if (!this.product || !this.product.task_tree) return map;
// 拍平树结构,方便随时查父节点状态
const flatMap = {};
const flatten = (tasks) => {
if (!tasks) return;
tasks.forEach(t => { flatMap[t.id] = t; flatten(t.child_tasks); });
};
flatten(this.product.task_tree);
const traverse = (tasks, prefix) => {
if (!tasks) return;
let spawnIndex = 0;
tasks.forEach((t) => {
if (t.task_type !== 'SPAWN') {
const parent = t.parent_task_id ? flatMap[t.parent_task_id] : null;
// 🚀 终极防御判定:
// 1. 没有父节点(打底任务) -> 主分支
// 2. 明确的基因 (TRANSFER/RECOVERY) -> 主分支
// 3. 兜底:如果基因丢失,但它的父节点已经完工(COMPLETED),那它必然是接力的转交任务 -> 主分支
const isMain = !t.parent_task_id
|| t.task_type === 'TRANSFER'
|| t.task_type === 'RECOVERY'
|| (!t.task_type && parent && (parent.status === 'COMPLETED' || parent.status === 'CANCELED'));
if (isMain) {
map[t.id] = '主分支';
traverse(t.child_tasks, prefix);
} else {