重构: flex布局撑满全屏 + 记录区下沉到子树底部 + 底部Footer钉死
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
|
||||
<!-- 卡片 -->
|
||||
<view :class="['tnode-card', statusColor(task.status), { 'is-rework': task.is_rework }]">
|
||||
<!-- 信息区 -->
|
||||
<!-- 第一层:任务名称、标签、状态 Badge、负责人 -->
|
||||
<view class="tnode-body">
|
||||
<view class="tnode-name-row">
|
||||
<text v-if="task.is_rework" class="tag tag-rework">⚠返工</text>
|
||||
@ -15,39 +15,7 @@
|
||||
<text v-if="task.assignee_id">负责人: {{ task.assignee_id }}</text>
|
||||
<text v-if="task.reject_reason" class="reject-reason">驳回: {{ task.reject_reason }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 进度记录 — 只展示最新一条 + 查看全部入口 -->
|
||||
<view v-if="task.records && task.records.length" class="records-area">
|
||||
<!-- 查看全部记录按钮 -->
|
||||
<view class="records-bar" @tap.stop="$emit('viewRecords', task)">
|
||||
<text class="records-bar-icon">📋</text>
|
||||
<text class="records-bar-text">查看全部记录 ({{ task.records.length }})</text>
|
||||
<text class="records-bar-arrow">›</text>
|
||||
</view>
|
||||
<!-- 最新一条预览 -->
|
||||
<view class="record-item">
|
||||
<view class="record-top">
|
||||
<text class="record-time">{{ formatTime(task.records[0].created_at) }}</text>
|
||||
<view v-if="canEditRecord" class="record-actions">
|
||||
<text class="rec-act" @tap.stop="$emit('editRecord', { task, record: task.records[0] })">✏️</text>
|
||||
<text class="rec-act" @tap.stop="confirmDelete(task.records[0])">🗑️</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="task.records[0].remark" class="record-remark">{{ task.records[0].remark }}</text>
|
||||
<view v-if="task.records[0].images && task.records[0].images.length" class="record-images">
|
||||
<image
|
||||
v-for="(img, i) in task.records[0].images"
|
||||
:key="i"
|
||||
:src="imageUrl(img)"
|
||||
class="record-thumb"
|
||||
mode="aspectFill"
|
||||
@tap.stop="previewImage(task.records[0].images, i)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态标签 -->
|
||||
<text :class="['badge', statusColor(task.status)]">{{ statusLabel(task.status) }}</text>
|
||||
</view>
|
||||
@ -64,7 +32,7 @@
|
||||
@tap.stop="$emit('action', { task, type: 'record' })">📝 记录/拍照</button>
|
||||
</view>
|
||||
|
||||
<!-- 递归子树 -->
|
||||
<!-- 第二层:递归子任务树 -->
|
||||
<view v-if="task.child_tasks && task.child_tasks.length" class="tnode-children">
|
||||
<TaskTreeNode
|
||||
v-for="(child, idx) in task.child_tasks"
|
||||
@ -79,6 +47,37 @@
|
||||
@viewRecords="(t) => $emit('viewRecords', t)"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 第三层(最底部):进度记录 — 最新一条 + 查看全部入口 -->
|
||||
<view v-if="task.records && task.records.length" class="records-area">
|
||||
<!-- 查看全部记录按钮 -->
|
||||
<view class="records-bar" @tap.stop="$emit('viewRecords', task)">
|
||||
<text class="records-bar-icon">📋</text>
|
||||
<text class="records-bar-text">查看全部记录 ({{ task.records.length }})</text>
|
||||
<text class="records-bar-arrow">›</text>
|
||||
</view>
|
||||
<!-- 最新一条预览 -->
|
||||
<view class="record-item">
|
||||
<view class="record-top">
|
||||
<text class="record-time">{{ formatTime(task.records[0].created_at) }}</text>
|
||||
<view v-if="canEditRecord" class="record-actions">
|
||||
<text class="rec-act" @tap.stop="$emit('editRecord', { task, record: task.records[0] })">✏️</text>
|
||||
<text class="rec-act" @tap.stop="confirmDelete(task.records[0])">🗑️</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="task.records[0].remark" class="record-remark">{{ task.records[0].remark }}</text>
|
||||
<view v-if="task.records[0].images && task.records[0].images.length" class="record-images">
|
||||
<image
|
||||
v-for="(img, i) in task.records[0].images"
|
||||
:key="i"
|
||||
:src="imageUrl(img)"
|
||||
class="record-thumb"
|
||||
mode="aspectFill"
|
||||
@tap.stop="previewImage(task.records[0].images, i)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="page-container">
|
||||
<!-- ======== 加载/错误 ======== -->
|
||||
<view v-if="loading" class="loading">加载中...</view>
|
||||
<view v-if="error" class="error-box">{{ error }}</view>
|
||||
@ -56,13 +56,13 @@
|
||||
<!-- ================================================================ -->
|
||||
<!-- 任务树(撑满剩余空间,可滚动) -->
|
||||
<!-- ================================================================ -->
|
||||
<view class="card card-tree">
|
||||
<view class="card-header">
|
||||
<text class="card-title">🌿 任务流转树</text>
|
||||
<text class="task-count">{{ countTasks(product.task_tree) }} 个任务</text>
|
||||
</view>
|
||||
<view class="tree-scroll-area">
|
||||
<view class="card card-tree">
|
||||
<view class="card-header">
|
||||
<text class="card-title">🌿 任务流转树</text>
|
||||
<text class="task-count">{{ countTasks(product.task_tree) }} 个任务</text>
|
||||
</view>
|
||||
|
||||
<view class="tree-scroll">
|
||||
<!-- 0任务 → 发起首道工序 -->
|
||||
<view v-if="!product.task_tree || !product.task_tree.length" class="zero-task">
|
||||
<text class="zero-icon">📋</text>
|
||||
@ -86,6 +86,24 @@
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- 底部操作栏(钉死,不随滚动) -->
|
||||
<!-- ================================================================ -->
|
||||
<view v-if="activeTask" class="footer-actions">
|
||||
<button v-if="activeTask.status === 'WIP'" class="footer-btn footer-transfer"
|
||||
@tap="handleTaskAction({ task: activeTask, type: 'transfer' })">
|
||||
🔄 完工转交
|
||||
</button>
|
||||
<button v-if="activeTask.status === 'WIP'" class="footer-btn footer-record"
|
||||
@tap="handleTaskAction({ task: activeTask, type: 'record' })">
|
||||
📝 记录/拍照
|
||||
</button>
|
||||
<button v-if="activeTask.status === 'PENDING'" class="footer-btn footer-receive"
|
||||
@tap="handleTaskAction({ task: activeTask, type: 'receive' })">
|
||||
✅ 接收任务
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================ -->
|
||||
@ -335,6 +353,21 @@ export default {
|
||||
const u = this.userOptions.find(u => u.id === this.transferForm.selectedUserId);
|
||||
return u ? u.name : "";
|
||||
},
|
||||
activeTask() {
|
||||
// 查找树中第一个 WIP 或 PENDING 任务作为底部 Footer 的操作目标
|
||||
const findActive = (tasks) => {
|
||||
if (!tasks) return null;
|
||||
for (const t of tasks) {
|
||||
if (t.status === 'WIP' || t.status === 'PENDING') return t;
|
||||
if (t.child_tasks && t.child_tasks.length) {
|
||||
const found = findActive(t.child_tasks);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return this.product ? findActive(this.product.task_tree) : null;
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
this.loadUsers();
|
||||
@ -661,7 +694,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page { height: 100vh; display: flex; flex-direction: column; padding: 16px; padding-bottom: 0; overflow: hidden; box-sizing: border-box; }
|
||||
.page-container { height: 100vh; display: flex; flex-direction: column; background-color: #f3f4f6; box-sizing: border-box; padding: 16px; padding-bottom: 0; overflow: hidden; }
|
||||
.loading { text-align: center; padding: 48px 0; color: #6b7280; }
|
||||
.error-box { padding: 12px; border-radius: 10px; background: #fef2f2; color: #dc2626; font-size: 13px; border: 1px solid #fecaca; }
|
||||
|
||||
@ -675,10 +708,10 @@ export default {
|
||||
|
||||
/* 卡片 */
|
||||
.card { background: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); flex-shrink: 0; }
|
||||
/* 任务树卡片 → 撑满剩余空间 */
|
||||
.card-tree { flex: 1; display: flex; flex-direction: column; min-height: 0; margin-bottom: 0; overflow: hidden; }
|
||||
/* 任务树滚动区域 → 撑满剩余空间 */
|
||||
.tree-scroll-area { flex: 1; overflow-y: auto; min-height: 0; -webkit-overflow-scrolling: touch; }
|
||||
.card-tree { margin-bottom: 0; }
|
||||
.card-tree .card-header { flex-shrink: 0; }
|
||||
.tree-scroll { flex: 1; overflow-y: auto; min-height: 0; -webkit-overflow-scrolling: touch; }
|
||||
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
||||
.card-header-right { display: flex; align-items: center; gap: 8px; }
|
||||
.card-title { font-size: 15px; font-weight: 700; }
|
||||
@ -762,6 +795,17 @@ export default {
|
||||
background: #f9fafb; color: #6b7280; font-size: 14px; line-height: 42px; margin: 8px 0; }
|
||||
.btn-upload[disabled] { opacity: 0.5; }
|
||||
|
||||
/* 底部操作栏 (钉死在屏幕最下方) */
|
||||
.footer-actions {
|
||||
display: flex; gap: 20rpx; padding: 20rpx 30rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
background: #ffffff; box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05); z-index: 99; flex-shrink: 0;
|
||||
}
|
||||
.footer-btn { flex: 1; height: 88rpx; border: none; border-radius: 12rpx; font-size: 30rpx; font-weight: 700; line-height: 88rpx; }
|
||||
.footer-btn::after { border: none; }
|
||||
.footer-transfer { background: #dcfce7; color: #16a34a; }
|
||||
.footer-record { background: #eff6ff; color: #2563eb; }
|
||||
.footer-receive { background: #dbeafe; color: #1d4ed8; }
|
||||
|
||||
/* Picker 选择器 */
|
||||
.form-item { margin: 10px 0; }
|
||||
.form-label { font-size: 14px; font-weight: 600; color: #374151; display: block; margin-bottom: 4px; }
|
||||
|
||||
Reference in New Issue
Block a user