fix: 完善子任务组审核卡片,显示与主任务相同的完整信息

1. 后端增强getPendingApprovalGroups返回完整数据:
   - 品牌、类目、平台、SPU信息
   - 发起人姓名和部门
   - 任务类型明细
   - 预览图和附件
   - 计算子任务组总积分
   - 前置任务的素材下载链接(流水线继承)

2. 前端修改:
   - 子任务组使用与主任务相同的数据结构
   - 卡片显示完整的任务信息
   - 显示前置素材下载按钮
This commit is contained in:
Your Name 2026-01-07 19:34:28 +08:00
parent c2938dee89
commit d94395e07e
2 changed files with 155 additions and 37 deletions

View File

@ -1069,13 +1069,95 @@ public class TaskItemService {
groupInfo.put("taskTitle", mainTask.getTitle());
groupInfo.put("taskNo", mainTask.getTaskNo());
groupInfo.put("groupNo", item.getGroupNo());
groupInfo.put("groupId", item.getGroupNo()); // 前端使用groupId作为唯一标识
groupInfo.put("groupId", item.getGroupNo());
groupInfo.put("itemCount", 1);
groupInfo.put("createTime", item.getCreateTime());
// 添加主任务的完整信息
groupInfo.put("brandId", mainTask.getBrandId());
groupInfo.put("brandName", mainTask.getBrandName());
groupInfo.put("categoryId", mainTask.getCategoryId());
groupInfo.put("categoryName", mainTask.getCategoryName());
groupInfo.put("platformId", mainTask.getPlatformId());
groupInfo.put("platformName", mainTask.getPlatformName());
groupInfo.put("spuInfo", mainTask.getSpuInfo());
groupInfo.put("quantity", mainTask.getQuantity());
groupInfo.put("previewImage", mainTask.getPreviewImage());
groupInfo.put("attachmentUrl", mainTask.getAttachmentUrl());
groupInfo.put("remark", mainTask.getRemark());
groupInfo.put("typeDetails", mainTask.getTypeDetails());
// 发起人信息
groupInfo.put("publisherId", mainTask.getPublisherId());
if (mainTask.getPublisherId() != null) {
com.designhub.entity.User publisher = userMapper.selectById(mainTask.getPublisherId());
if (publisher != null) {
groupInfo.put("publisherName", publisher.getRealName());
groupInfo.put("publisherDeptName", publisher.getDeptName());
}
}
// 子任务的任务类型信息
groupInfo.put("taskTypeId", item.getTaskTypeId());
if (item.getTaskTypeId() != null) {
com.designhub.entity.DemandType taskType = demandTypeMapper.selectById(item.getTaskTypeId());
if (taskType != null) {
groupInfo.put("taskTypeName", taskType.getName());
}
}
// 计算子任务组总积分
groupInfo.put("totalScore", item.getScore() != null ? item.getScore() : 0);
// 获取前置子任务的素材下载链接(流水线继承)
if (item.getGroupNo() != null && item.getGroupNo() > 1) {
// 查找前一组已完成的子任务的下载链接
List<TaskItem> prevGroupItems = taskItemMapper.selectList(
new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getTaskId, item.getTaskId())
.eq(TaskItem::getGroupNo, item.getGroupNo() - 1)
.eq(TaskItem::getStatus, "completed")
);
if (!prevGroupItems.isEmpty()) {
// 收集所有前置子任务的下载链接
List<String> prevDownloadUrls = prevGroupItems.stream()
.filter(pi -> pi.getDownloadUrl() != null && !pi.getDownloadUrl().isEmpty())
.map(TaskItem::getDownloadUrl)
.collect(java.util.stream.Collectors.toList());
if (!prevDownloadUrls.isEmpty()) {
groupInfo.put("prevDownloadUrls", prevDownloadUrls);
}
}
}
// 存储子任务列表,用于显示详情
List<Map<String, Object>> itemsList = new java.util.ArrayList<>();
Map<String, Object> itemInfo = new HashMap<>();
itemInfo.put("id", item.getId());
itemInfo.put("itemNo", item.getItemNo());
itemInfo.put("taskTypeId", item.getTaskTypeId());
itemInfo.put("score", item.getScore());
itemsList.add(itemInfo);
groupInfo.put("items", itemsList);
groupMap.put(key, groupInfo);
} else {
Map<String, Object> groupInfo = groupMap.get(key);
groupInfo.put("itemCount", (Integer) groupInfo.get("itemCount") + 1);
// 累加积分
Integer currentScore = (Integer) groupInfo.get("totalScore");
Integer itemScore = item.getScore() != null ? item.getScore() : 0;
groupInfo.put("totalScore", currentScore + itemScore);
// 添加到子任务列表
@SuppressWarnings("unchecked")
List<Map<String, Object>> itemsList = (List<Map<String, Object>>) groupInfo.get("items");
Map<String, Object> itemInfo = new HashMap<>();
itemInfo.put("id", item.getId());
itemInfo.put("itemNo", item.getItemNo());
itemInfo.put("taskTypeId", item.getTaskTypeId());
itemInfo.put("score", item.getScore());
itemsList.add(itemInfo);
}
}
}

View File

@ -554,21 +554,17 @@
<!-- 任务信息 -->
<div class="task-info-wrapper">
<div class="task-info task-info-3col">
<div v-if="task._itemType !== 'taskGroup'" class="info-item">
<div class="info-item">
<span class="label">任务类型</span>
<span class="value">{{ getTaskTypeName(task.taskTypeId, task.taskTypeName, task.taskSubType) }}</span>
</div>
<div v-if="task._itemType === 'taskGroup'" class="info-item">
<span class="label">子任务数量</span>
<span class="value">{{ task._groupData?.itemCount || 0 }} 个</span>
</div>
<div class="info-item">
<span class="label">预估积分</span>
<span class="value text-brand">{{ task.totalScore || task.score || task._groupData?.totalScore || 0 }} 分</span>
<span class="value text-brand">{{ task.totalScore || task.score || 0 }} 分</span>
</div>
<div v-if="task._itemType !== 'taskGroup'" class="info-item">
<span class="label">数量</span>
<span class="value">{{ task.quantity || '--' }} 个</span>
<div class="info-item">
<span class="label">{{ task._itemType === 'taskGroup' ? '子任务数' : '数量' }}</span>
<span class="value">{{ task._itemType === 'taskGroup' ? (task._groupData?.itemCount || 1) : (task.quantity || '--') }} 个</span>
</div>
</div>
<!-- 预览图 -->
@ -580,8 +576,8 @@
</div>
</div>
<!-- 任务类型明细(仅主任务) -->
<div v-if="task._itemType !== 'taskGroup' && parseTypeDetails(task.typeDetails).length > 0" class="type-details-section">
<!-- 任务类型明细 -->
<div v-if="parseTypeDetails(task.typeDetails).length > 0" class="type-details-section">
<div class="type-details-title">任务类型明细</div>
<div class="type-details-list">
<div v-for="(detail, idx) in parseTypeDetails(task.typeDetails)" :key="idx" class="type-detail-item">
@ -594,12 +590,6 @@
<!-- 任务详情 -->
<div class="task-details">
<div class="detail-item">
<span class="icon">
<Icon name="hash" size="sm" color="#31f08b" />
</span>
<span>任务编号: {{ task.taskNo || task._groupData?.taskNo || '--' }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup'" class="detail-item">
<span class="icon">
<Icon name="user" size="sm" color="#31f08b" />
</span>
@ -611,43 +601,43 @@
</span>
<span>接单人: {{ task.receiverName || '--' }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.brandName" class="detail-item">
<div v-if="task.brandName" class="detail-item">
<span class="icon">
<Icon name="tag" size="sm" color="#31f08b" />
</span>
<span>品牌: {{ task.brandName }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.categoryName" class="detail-item">
<div v-if="task.categoryName" class="detail-item">
<span class="icon">
<Icon name="folder" size="sm" color="#31f08b" />
</span>
<span>类目: {{ task.categoryName }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.platformName" class="detail-item">
<div v-if="task.platformName" class="detail-item">
<span class="icon">
<Icon name="monitor" size="sm" color="#31f08b" />
</span>
<span>平台: {{ task.platformName }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.spuInfo" class="detail-item">
<div v-if="task.spuInfo" class="detail-item">
<span class="icon">
<Icon name="package" size="sm" color="#31f08b" />
</span>
<span>SPU: {{ task.spuInfo }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.taskSubType" class="detail-item">
<div v-if="task.taskSubType" class="detail-item">
<span class="icon">
<Icon name="list" size="sm" color="#31f08b" />
</span>
<span>子类型: {{ task.taskSubType }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.deliveryPath" class="detail-item">
<div v-if="task.deliveryPath" class="detail-item">
<span class="icon">
<Icon name="link" size="sm" color="#31f08b" />
</span>
<span>交付路径: {{ task.deliveryPath }}</span>
</div>
<div v-if="task._itemType !== 'taskGroup' && task.attachmentUrl" class="detail-item">
<div v-if="task.attachmentUrl" class="detail-item">
<span class="icon">
<Icon name="paperclip" size="sm" color="#31f08b" />
</span>
@ -664,6 +654,30 @@
</a>
</span>
</div>
<!-- 前置素材下载(流水线继承) -->
<div v-if="task._itemType === 'taskGroup' && task.prevDownloadUrls && task.prevDownloadUrls.length > 0" class="detail-item">
<span class="icon">
<Icon name="download" size="sm" color="#31f08b" />
</span>
<span>前置素材:
<a
v-for="(url, index) in task.prevDownloadUrls"
:key="index"
:href="url"
target="_blank"
class="attachment-link download-link"
@click.stop
>
下载{{ index + 1 }}
</a>
</span>
</div>
</div>
<!-- 任务编号(底部) -->
<div class="task-no-section">
<Icon name="hash" size="sm" color="#31f08b" />
<span>{{ task.taskNo || '--' }}</span>
</div>
<!-- 操作按钮 -->
@ -3846,7 +3860,7 @@ const loadReviewTasks = async () => {
})
}
// 处理子任务组(如果有)
// 处理子任务组(如果有)- 使用与主任务相同的数据结构
if (groupsResponse && groupsResponse.code === 200 && groupsResponse.data) {
for (const group of groupsResponse.data) {
allItems.push({
@ -3854,8 +3868,30 @@ const loadReviewTasks = async () => {
title: group.taskTitle || '子任务组',
taskNo: group.taskNo || '',
score: group.totalScore || 0,
totalScore: group.totalScore || 0,
createTime: group.createTime,
_itemType: 'taskGroup', // 标记类型
// 继承主任务的完整信息
brandId: group.brandId,
brandName: group.brandName,
categoryId: group.categoryId,
categoryName: group.categoryName,
platformId: group.platformId,
platformName: group.platformName,
spuInfo: group.spuInfo,
quantity: group.itemCount || 1, // 子任务数量作为数量显示
previewImage: group.previewImage,
attachmentUrl: group.attachmentUrl,
remark: group.remark,
typeDetails: group.typeDetails,
taskTypeId: group.taskTypeId,
taskTypeName: group.taskTypeName,
publisherId: group.publisherId,
publisherName: group.publisherName,
publisherDeptName: group.publisherDeptName,
// 前置任务的素材下载链接
prevDownloadUrls: group.prevDownloadUrls || [],
// 标记类型
_itemType: 'taskGroup',
_groupData: group // 保存原始子任务组数据
})
}
@ -4616,16 +4652,16 @@ const loadAllTabCounts = async () => {
)
} else {
// 普通用户:只计算主任务
promises.push(
taskApi.getAllPendingApprovalTasks({
page: 1,
pageSize: 1
}).then(res => {
if (res.data && res.data.total !== undefined) {
reviewTotal.value = res.data.total
}
}).catch(() => {})
)
promises.push(
taskApi.getAllPendingApprovalTasks({
page: 1,
pageSize: 1
}).then(res => {
if (res.data && res.data.total !== undefined) {
reviewTotal.value = res.data.total
}
}).catch(() => {})
)
}
// 4. 已完成任务数量