feat: 移动端全局人名映射 + 协同留言板 UI
- detail.vue: 留言板聊天室 UI + 乐观更新 + loadUsers 填充全局字典 - WorkspaceArea.vue: 任务列表/上游/下游负责人全部走 formatUserName - TreeCanvas.vue: 节点负责人 + 嵌套协助提示走 formatUserName - TaskSwipeCards.vue: 卡片负责人走 formatUserName - 留言板头像取中文名末字(formatUserAvatar)
This commit is contained in:
@ -44,7 +44,7 @@
|
||||
<view class="tc-meta">
|
||||
<view class="tc-meta-row">
|
||||
<text class="tc-meta-label">👤 负责人</text>
|
||||
<text class="tc-meta-val">{{ card.assignee_id || '未分配' }}</text>
|
||||
<text class="tc-meta-val">{{ formatUserName(card.assignee_id) || '未分配' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatUserName } from "../../../utils/format";
|
||||
export default {
|
||||
name: "TaskSwipeCards",
|
||||
props: {
|
||||
@ -116,14 +117,12 @@ export default {
|
||||
tasks.forEach(t => { flatMap[t.id] = t; flatten(t.child_tasks); });
|
||||
};
|
||||
flatten(this.product.task_tree);
|
||||
const isMainFn = (t) => !t.parent_task_id || t.task_type === 'TRANSFER' || t.task_type === 'RECOVERY';
|
||||
const traverse = (tasks, prefix) => {
|
||||
if (!tasks) return;
|
||||
let spawnIndex = 0;
|
||||
tasks.forEach((t) => {
|
||||
const parent = t.parent_task_id ? flatMap[t.parent_task_id] : null;
|
||||
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); }
|
||||
if (isMainFn(t)) { map[t.id] = '主分支'; traverse(t.child_tasks, prefix); }
|
||||
else { spawnIndex++; const num = prefix ? `${prefix}.${spawnIndex}` : `${spawnIndex}`; map[t.id] = `分支 ${num}`; traverse(t.child_tasks, num); }
|
||||
});
|
||||
};
|
||||
@ -134,14 +133,14 @@ export default {
|
||||
const result = [];
|
||||
if (!this.product || !this.product.task_tree) return result;
|
||||
|
||||
// 分支0:主分支
|
||||
const mainLane = { _key: 'main', label: '主分支', cards: [], _cardIdx: 0 };
|
||||
const isMainFn = (t) => !t.parent_task_id || t.task_type === 'TRANSFER' || t.task_type === 'RECOVERY';
|
||||
|
||||
const sortTasks = (tasks) => {
|
||||
if (!tasks) return [];
|
||||
return [...tasks].sort((a, b) => {
|
||||
const aMain = (!a.parent_task_id || a.task_type === 'TRANSFER' || a.task_type === 'RECOVERY') ? -1 : 1;
|
||||
const bMain = (!b.parent_task_id || b.task_type === 'TRANSFER' || b.task_type === 'RECOVERY') ? -1 : 1;
|
||||
const aMain = isMainFn(a) ? -1 : 1;
|
||||
const bMain = isMainFn(b) ? -1 : 1;
|
||||
if (aMain !== bMain) return aMain - bMain;
|
||||
return new Date(a.created_at) - new Date(b.created_at);
|
||||
});
|
||||
@ -152,17 +151,13 @@ export default {
|
||||
const sorted = sortTasks(taskList);
|
||||
|
||||
for (const t of sorted) {
|
||||
const isMain = !t.parent_task_id || t.task_type === 'TRANSFER' || t.task_type === 'RECOVERY'
|
||||
|| (!t.task_type && t.parent_task_id);
|
||||
|
||||
if (isMain) {
|
||||
if (isMainFn(t)) {
|
||||
lane.cards.push({ ...t, _key: t.id + '_' + lane._key, _isMain: lane._key === 'main' });
|
||||
// 递归:本分支下一步 = TRANSFER 子节点
|
||||
if (t.child_tasks && t.child_tasks.length) {
|
||||
const nextMain = t.child_tasks.filter(c =>
|
||||
c.task_type === 'TRANSFER' || c.task_type === 'RECOVERY' || !c.task_type);
|
||||
c.task_type === 'TRANSFER' || c.task_type === 'RECOVERY');
|
||||
followMain(nextMain, lane);
|
||||
// SPAWN 子节点 → 新分支
|
||||
// SPAWN 子节点 → 新分支(含嵌套 spawn)
|
||||
const spawns = t.child_tasks.filter(c => c.task_type === 'SPAWN');
|
||||
for (const sc of spawns) {
|
||||
const blabel = this.branchLabelMap[sc.id] || '分支';
|
||||
@ -190,6 +185,7 @@ export default {
|
||||
} catch (e) { /* ignore */ }
|
||||
},
|
||||
methods: {
|
||||
formatUserName,
|
||||
onLaneSwipe(e) { this.currentLane = e.detail.current; },
|
||||
onCardSwipe(e, laneIdx) {
|
||||
if (this.lanes[laneIdx]) {
|
||||
|
||||
Reference in New Issue
Block a user