fix: 合并待审核页面,将子任务组显示在待审核任务标签页中

1. 删除单独的'待我审核'标签页
2. 修改loadReviewTasks同时加载主任务和子任务组
3. 修改卡片模板支持显示子任务组
4. 添加子任务组的审核和驳回方法
5. 更新待审核任务数量统计
This commit is contained in:
Your Name 2026-01-07 17:37:04 +08:00
parent 0c12b28f7a
commit c2938dee89
3 changed files with 336 additions and 320 deletions

View File

@ -323,8 +323,8 @@ public class TaskItemController {
if (leadingDepts.isEmpty()) {
// 不是任何部门的负责人,返回空列表
log.info("用户不是任何部门的负责人,返回空列表");
return Result.success(new java.util.ArrayList<>());
}
return Result.success(new java.util.ArrayList<>());
}
// 合并所有负责部门的待审核子任务组
List<Map<String, Object>> allGroups = new java.util.ArrayList<>();

View File

@ -518,18 +518,18 @@ public class TaskItemService {
&& !notifiedLeaderIds.contains(receiverDept.getLeaderId())) {
notifiedLeaderIds.add(receiverDept.getLeaderId());
Notification notification = new Notification();
notification.setReceiverId(receiverDept.getLeaderId());
notification.setNotificationType("task_approval");
notification.setTitle("新子任务组待审核");
notification.setContent("任务《" + mainTask.getTitle() + "》第 " + nextGroupNo + " 组共 " +
nextGroupItems.size() + " 个子任务已解锁,请审核并分配!");
notification.setRelatedId(item.getTaskId());
notification.setRelatedType("task");
notification.setPriority(2); // 重要
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
Notification notification = new Notification();
notification.setReceiverId(receiverDept.getLeaderId());
notification.setNotificationType("task_approval");
notification.setTitle("新子任务组待审核");
notification.setContent("任务《" + mainTask.getTitle() + "》第 " + nextGroupNo + " 组共 " +
nextGroupItems.size() + " 个子任务已解锁,请审核并分配!");
notification.setRelatedId(item.getTaskId());
notification.setRelatedType("task");
notification.setPriority(2); // 重要
notification.setPushDingtalk(1); // 推送到钉钉
notificationService.sendNotification(notification);
log.info("已通知接单部门负责人(部门: {}, 负责人ID: {})审核第 {} 组子任务",
receiverDept.getDeptName(), receiverDept.getLeaderId(), nextGroupNo);
}
@ -711,7 +711,7 @@ public class TaskItemService {
// 兼容:也检查主任务的 receiver_dept_id
if (!hasPermission && mainTask.getReceiverDeptId() != null) {
com.designhub.entity.Department receiverDept = departmentMapper.selectById(mainTask.getReceiverDeptId());
com.designhub.entity.Department receiverDept = departmentMapper.selectById(mainTask.getReceiverDeptId());
if (receiverDept != null && approverId.equals(receiverDept.getLeaderId())) {
hasPermission = true;
}

View File

@ -532,15 +532,19 @@
<div class="task-header">
<h3 class="task-title">{{ task.title }}</h3>
<div class="task-badges">
<span class="badge badge-review">
<span v-if="task._itemType === 'taskGroup'" class="badge badge-subtask">
<Icon name="layers" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
子任务组 - 第{{ task._groupData?.groupNo }}组
</span>
<span v-else class="badge badge-review">
<Icon name="file-text" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
待审核
</span>
</div>
</div>
<!-- 流程阶段 -->
<div v-if="getProcessStage(task)" class="process-stage">
<!-- 流程阶段(仅主任务) -->
<div v-if="task._itemType !== 'taskGroup' && getProcessStage(task)" class="process-stage">
<span class="stage-icon">
<Icon name="clock" size="sm" color="#31f08b" />
</span>
@ -550,17 +554,21 @@
<!-- 任务信息 -->
<div class="task-info-wrapper">
<div class="task-info task-info-3col">
<div class="info-item">
<span class="label">任务类型</span>
<div v-if="task._itemType !== 'taskGroup'" class="info-item">
<span class="label">任务类型</span>
<span class="value">{{ getTaskTypeName(task.taskTypeId, task.taskTypeName, task.taskSubType) }}</span>
</div>
<div class="info-item">
<span class="label">预估积分</span>
<span class="value text-brand">{{ task.totalScore || task.score }} 分</span>
</div>
<div class="info-item">
<span class="label">数量</span>
<span class="value">{{ task.quantity || '--' }} 个</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>
</div>
<div v-if="task._itemType !== 'taskGroup'" class="info-item">
<span class="label">数量</span>
<span class="value">{{ task.quantity || '--' }} 个</span>
</div>
</div>
<!-- 预览图 -->
@ -572,8 +580,8 @@
</div>
</div>
<!-- 任务类型明细 -->
<div v-if="parseTypeDetails(task.typeDetails).length > 0" class="type-details-section">
<!-- 任务类型明细(仅主任务) -->
<div v-if="task._itemType !== 'taskGroup' && 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">
@ -586,54 +594,60 @@
<!-- 任务详情 -->
<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>
<span>发起人: {{ task.publisherName || '--' }}</span>
</div>
<div class="detail-item">
<div v-if="task._itemType !== 'taskGroup'" class="detail-item">
<span class="icon">
<Icon name="user" size="sm" color="#31f08b" />
</span>
<span>接单人: {{ task.receiverName || '--' }}</span>
</div>
<div v-if="task.brandName" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.categoryName" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.platformName" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.spuInfo" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.taskSubType" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.deliveryPath" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && 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.attachmentUrl" class="detail-item">
<div v-if="task._itemType !== 'taskGroup' && task.attachmentUrl" class="detail-item">
<span class="icon">
<Icon name="paperclip" size="sm" color="#31f08b" />
</span>
@ -654,20 +668,32 @@
<!-- 操作按钮 -->
<div class="task-actions">
<a-button size="small" @click="handleViewTaskDetail(task)">查看详情</a-button>
<a-button v-if="canApproveTask(task)" type="primary" size="small" @click="handleApproveTask(task)">
通过审核
</a-button>
<a-button v-if="canRejectTask(task)" danger size="small" @click="handleRejectTask(task)">
驳回任务
</a-button>
<template v-if="task._itemType === 'taskGroup'">
<a-button type="primary" size="small" @click="handleApproveTaskGroupFromReview(task)">
<Icon name="check" size="sm" style="margin-right: 4px;" />
审核通过
</a-button>
<a-button danger size="small" @click="handleRejectTaskGroupFromReview(task)">
<Icon name="x" size="sm" style="margin-right: 4px;" />
驳回
</a-button>
</template>
<template v-else>
<a-button size="small" @click="handleViewTaskDetail(task)">查看详情</a-button>
<a-button v-if="canApproveTask(task)" type="primary" size="small" @click="handleApproveTask(task)">
通过审核
</a-button>
<a-button v-if="canRejectTask(task)" danger size="small" @click="handleRejectTask(task)">
驳回任务
</a-button>
</template>
</div>
<!-- 时间信息 -->
<div class="task-footer">
<div class="footer-item">
<Icon name="calendar" size="sm" color="#31f08b" />
<span style="color: #ffffff;">提交: {{ formatDate(task.submitTime || task.updateTime) }}</span>
<span style="color: #ffffff;">提交: {{ formatDate(task.submitTime || task.updateTime || task.createTime) }}</span>
</div>
</div>
</div>
@ -1748,220 +1774,6 @@
/>
</div>
</a-tab-pane>
<!-- 待我审核任务标签页 -->
<a-tab-pane key="pendingApproval" v-if="isDepartmentLeader">
<template #tab>
<span>待我审核 <span class="tab-count">({{ pendingApprovalTotal }})</span></span>
</template>
<div class="search-filter-bar chart-card">
<div class="search-row">
<a-input-search
v-model:value="approvalFilters.keyword"
placeholder="搜索任务标题、任务编号..."
class="search-input"
@search="loadPendingApprovalTasks"
@pressEnter="loadPendingApprovalTasks"
/>
<span class="stats-text">共 {{ pendingApprovalTotal }} 个待审核任务</span>
</div>
</div>
<!-- 任务列表 - 使用统一的任务卡片样式 -->
<div class="task-grid">
<div
v-for="item in pendingApprovalTasks"
:key="`${item.type}-${item.id}`"
class="task-card chart-card approval-card"
@click="item.type === 'task' ? handleTaskClick(item.data) : null"
>
<!-- 任务标题 -->
<div class="task-header">
<h3 class="task-title">{{ item.title }}</h3>
<div class="task-badges">
<span v-if="item.type === 'taskGroup'" class="badge badge-subtask">
<Icon name="layers" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
子任务组
</span>
<span v-else class="badge badge-approval">
<Icon name="file-text" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
{{ getApprovalStatusText(item.data.approvalStatus) }}
</span>
<span v-if="item.isUrgent" class="badge badge-urgent">
<Icon name="fire" size="sm" color="#31f08b" style="margin-right: 4px; vertical-align: middle;" />
紧急
</span>
</div>
</div>
<!-- 流程阶段(仅主任务) -->
<div v-if="item.type === 'task' && getProcessStage(item.data)" class="process-stage">
<span class="stage-icon">
<Icon name="clock" size="sm" color="#31f08b" />
</span>
<span class="stage-text">{{ getProcessStage(item.data) }}</span>
</div>
<!-- 任务信息 -->
<div class="task-info-wrapper">
<div class="task-info">
<div v-if="item.type === 'task'" class="info-item">
<span class="label">任务类型</span>
<span class="value">{{ getTaskTypeName(item.data.taskTypeId) }}</span>
</div>
<div v-if="item.type === 'taskGroup'" class="info-item">
<span class="label">子任务数量</span>
<span class="value">{{ item.data.items?.length || 0 }} 个</span>
</div>
<div class="info-item">
<span class="label">预估积分</span>
<span class="value text-brand">{{ item.score }} 分</span>
</div>
<div v-if="item.type === 'task'" class="info-item">
<span class="label">数量</span>
<span class="value">{{ item.data.quantity || '--' }} 个</span>
</div>
</div>
<!-- 预览图 -->
<div v-if="item.data?.previewImage" class="preview-thumbnail">
<img :src="item.data.previewImage" alt="预览图" class="thumbnail-img" />
<div class="preview-popup">
<img :src="item.data.previewImage" alt="预览图" />
</div>
</div>
</div>
<!-- 任务详情 -->
<div class="task-details">
<div class="detail-item">
<span class="icon">
<Icon name="hash" size="sm" color="#31f08b" />
</span>
<span>任务编号: {{ item.taskNo }}</span>
</div>
<div v-if="item.publisherName" class="detail-item">
<span class="icon">
<Icon name="user" size="sm" color="#31f08b" />
</span>
<span>发起人: {{ item.publisherName || '未知' }}</span>
</div>
<div v-if="item.publisherDeptName" class="detail-item">
<span class="icon">
<Icon name="users" size="sm" color="#31f08b" />
</span>
<span>发起部门: {{ item.publisherDeptName }}</span>
</div>
<template v-if="item.type === 'task'">
<div v-if="item.data.brandName" class="detail-item">
<span class="icon">
<Icon name="tag" size="sm" color="#31f08b" />
</span>
<span>品牌: {{ item.data.brandName }}</span>
</div>
<div v-if="item.data.categoryName" class="detail-item">
<span class="icon">
<Icon name="folder" size="sm" color="#31f08b" />
</span>
<span>类目: {{ item.data.categoryName }}</span>
</div>
<div v-if="item.data.platformName" class="detail-item">
<span class="icon">
<Icon name="monitor" size="sm" color="#31f08b" />
</span>
<span>平台: {{ item.data.platformName }}</span>
</div>
<div v-if="item.data.spuInfo" class="detail-item">
<span class="icon">
<Icon name="package" size="sm" color="#31f08b" />
</span>
<span>SPU: {{ item.data.spuInfo }}</span>
</div>
<div v-if="item.data.remark" class="detail-item">
<span class="icon">
<Icon name="message-square" size="sm" color="#31f08b" />
</span>
<span>备注: {{ item.data.remark }}</span>
</div>
<div v-if="item.data.attachmentUrl" class="detail-item">
<span class="icon">
<Icon name="paperclip" size="sm" color="#31f08b" />
</span>
<span>附件:
<a
v-for="(url, index) in (item.data.attachmentUrl || '').split(',').filter(u => u.trim())"
:key="index"
:href="url.trim()"
target="_blank"
class="attachment-link"
@click.stop
>
附件{{ index + 1 }}
</a>
</span>
</div>
<div v-if="item.deadline" class="detail-item">
<span class="icon">
<Icon name="clock" size="sm" color="#31f08b" />
</span>
<span>截止: {{ formatDeadline(item.deadline) }}</span>
</div>
<div v-if="item.createTime" class="detail-item">
<span class="icon">
<Icon name="calendar" size="sm" color="#31f08b" />
</span>
<span>发起时间: {{ formatTime(item.createTime) }}</span>
</div>
</template>
</div>
<!-- 审核操作按钮 -->
<div class="task-actions" @click.stop>
<button
v-if="item.type === 'task'"
class="btn btn-secondary"
style="flex: 1"
@click.stop="handleTaskClick(item.data)"
>
查看详情
</button>
<button
class="btn btn-primary"
style="flex: 1"
@click="item.type === 'task' ? handleApproveTwoLevelTask(item.data) : handleApproveTaskGroup(item)"
:disabled="approvalLoading"
>
<Icon name="check" size="sm" style="margin-right: 4px;" />
审核通过
</button>
<button
class="btn btn-danger"
style="flex: 1"
@click="item.type === 'task' ? handleRejectTwoLevelTask(item.data) : handleRejectTaskGroup(item)"
:disabled="approvalLoading"
>
<Icon name="x" size="sm" style="margin-right: 4px;" />
驳回
</button>
</div>
</div>
<!-- 空状态 -->
<a-empty
v-if="!loading && pendingApprovalTasks.length === 0"
description="暂无待审核任务"
style="margin-top: 60px;"
>
<template #image>
<Icon name="inbox" size="xxl" color="#d9d9d9" />
</template>
</a-empty>
<!-- 加载状态 -->
<div v-if="loading" class="loading-container">
<a-spin size="large" tip="加载中..." />
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
@ -1986,20 +1798,20 @@
<div style="display: flex; flex-direction: column; gap: 4px;">
<span style="color: #fff; font-weight: 500; font-size: 14px;">{{ getTaskTypeFullPath(item.typeId)?.name || getTaskTypeName(item.typeId) }}</span>
<span style="color: #888; font-size: 12px;">{{ getTaskTypeFullPath(item.typeId)?.fullPath || '' }}</span>
</div>
</div>
</div>
<div class="type-controls" style="display: flex; align-items: center; gap: 12px;">
<div style="display: flex; align-items: center; background: #31f08b22; padding: 4px 10px; border-radius: 4px;">
<span style="color: #31f08b; font-weight: 600; font-size: 14px;">{{ getTaskTypeUnitScore(item.typeId) }} 分/个</span>
</div>
<div style="display: flex; align-items: center;">
<span class="label" style="color: #888; margin-right: 8px; font-size: 12px;">数量</span>
<a-input-number
v-model:value="item.quantity"
:min="1"
size="small"
<span class="label" style="color: #888; margin-right: 8px; font-size: 12px;">数量</span>
<a-input-number
v-model:value="item.quantity"
:min="1"
size="small"
style="width: 70px;"
/>
/>
</div>
<a-button
type="text"
@ -3039,8 +2851,6 @@ const handleTabChange = (key: string) => {
loadInProgressTasks()
} else if (key === 'review') {
loadReviewTasks()
} else if (key === 'pendingApproval') {
loadPendingApprovalTasks()
} else {
loadTasks()
}
@ -3580,6 +3390,179 @@ const handleRejectTaskGroup = async (item: ApprovalItem) => {
})
}
// 从待审核任务页面审核子任务组
const handleApproveTaskGroupFromReview = async (task: any) => {
const groupData = task._groupData
if (!groupData) {
message.error('子任务组数据不完整')
return
}
try {
// 获取设计师列表及工作量
const designersWithWorkload = await userApi.getDesignersWithWorkload()
let assignType = ref<'auto' | 'manual'>('auto')
let selectedDesignerId = ref<number | undefined>(undefined)
Modal.confirm({
title: '审核子任务组',
width: 600,
content: h('div', { style: 'padding: 10px 0;' }, [
h('p', { style: 'margin-bottom: 16px; color: #666; font-size: 13px;' },
`即将审核通过子任务组"${task.title} - 第${groupData.groupNo}组",共${groupData.itemCount || 0}个子任务。`
),
h('p', { style: 'margin-bottom: 12px; font-weight: 500;' }, '请选择接单方式:'),
h(RadioGroup, {
value: assignType.value,
onChange: (e: any) => {
assignType.value = e.target.value
if (e.target.value === 'auto') {
selectedDesignerId.value = undefined
}
}
}, () => [
h(Radio, { value: 'auto', style: { display: 'block', marginBottom: '12px' } }, () => '发布到任务大厅(设计师抢单)'),
h(Radio, { value: 'manual', style: { display: 'block', marginBottom: '12px' } }, () => '指定设计师接单')
]),
h('div', {
style: {
display: assignType.value === 'manual' ? 'block' : 'none',
marginTop: '16px',
marginLeft: '24px'
}
}, [
h('p', { style: 'margin-bottom: 8px; font-size: 13px; color: #666;' }, '选择设计师:'),
h(RadioGroup, {
value: selectedDesignerId.value,
onChange: (e: any) => {
selectedDesignerId.value = e.target.value
},
style: { width: '100%' }
}, () => designersWithWorkload.map(designer => {
const workloadColor = designer.inProgressCount === 0 ? '#52c41a'
: designer.inProgressCount <= 3 ? '#faad14'
: '#f5222d'
return h(Radio, {
value: designer.id,
style: {
display: 'block',
marginBottom: '8px',
padding: '8px',
border: '1px solid #d9d9d9',
borderRadius: '4px'
}
}, () => h('div', { style: 'display: flex; justify-content: space-between; align-items: center;' }, [
h('span', {}, `${designer.realName} (${designer.deptName || '未知部门'})`),
h('span', {
style: {
fontSize: '12px',
color: workloadColor
}
}, `当前${designer.inProgressCount}个任务`)
]))
}))
])
]),
okText: '确认审核',
cancelText: '取消',
onOk: async () => {
if (assignType.value === 'manual' && !selectedDesignerId.value) {
message.warning('请选择设计师')
return Promise.reject()
}
try {
approvalLoading.value = true
await taskApi.approveTaskGroup(groupData.taskId, {
groupNo: groupData.groupNo,
assignedReceiverId: assignType.value === 'manual' ? selectedDesignerId.value : undefined
})
if (assignType.value === 'manual') {
const designer = designersWithWorkload.find(d => d.id === selectedDesignerId.value)
message.success(`已审核通过并指定给设计师 ${designer?.realName}`)
} else {
message.success('已审核通过,子任务组已发布到任务大厅')
}
// 刷新列表和数量
loadReviewTasks()
loadAllTabCounts()
loadDashboardStats()
} catch (error: any) {
console.error('审核失败:', error)
message.error(error.msg || error.message || '审核失败')
return Promise.reject(error)
} finally {
approvalLoading.value = false
}
}
})
} catch (error: any) {
console.error('获取设计师列表失败:', error)
message.error('获取设计师列表失败')
}
}
// 从待审核任务页面驳回子任务组
const handleRejectTaskGroupFromReview = async (task: any) => {
const groupData = task._groupData
if (!groupData) {
message.error('子任务组数据不完整')
return
}
let rejectReason = ''
Modal.confirm({
title: '驳回子任务组',
content: h('div', { style: 'padding: 10px 0;' }, [
h('p', { style: 'margin-bottom: 12px; color: #666;' },
`即将驳回子任务组"${task.title} - 第${groupData.groupNo}组",共${groupData.itemCount || 0}个子任务。`
),
h('p', { style: 'margin-bottom: 10px; color: #666;' }, '请输入驳回原因:'),
h(Textarea, {
rows: 4,
placeholder: '请详细说明驳回原因',
maxlength: 500,
showCount: true,
onChange: (e: any) => {
rejectReason = e.target.value
}
})
]),
okText: '确认驳回',
cancelText: '取消',
okType: 'danger',
onOk: async () => {
if (!rejectReason || !rejectReason.trim()) {
message.warning('请输入驳回原因')
return Promise.reject()
}
try {
approvalLoading.value = true
await taskApi.rejectTaskGroup(groupData.taskId, groupData.groupNo, rejectReason.trim())
message.success('已驳回子任务组')
// 刷新列表和数量
loadReviewTasks()
loadAllTabCounts()
loadDashboardStats()
} catch (error: any) {
console.error('驳回失败:', error)
message.error(error.msg || error.message || '驳回失败')
} finally {
approvalLoading.value = false
}
}
})
}
// 已完成任务分页变化
const handleCompletedPageChange = (page: number) => {
completedPagination.page = page
@ -3828,7 +3811,7 @@ const mapTaskItemToTask = (item: any) => ({
receiverName: item.receiverName // 接单人姓名(部门负责人查看时显示)
})
// 加载待审核任务(所有处于审批流程中的任务)
// 加载待审核任务(所有处于审批流程中的任务 + 部门负责人的子任务组)
const loadReviewTasks = async () => {
reviewLoading.value = true
try {
@ -3840,41 +3823,73 @@ const loadReviewTasks = async () => {
// 添加筛选条件
if (reviewFilters.taskTypeId) params.taskTypeId = reviewFilters.taskTypeId
// 调用新接口:获取所有处于审批流程中的任务
const response = await taskApi.getAllPendingApprovalTasks(params)
// 并行加载:主任务 + 子任务组(如果是部门负责人)
const promises: Promise<any>[] = [
taskApi.getAllPendingApprovalTasks(params)
]
let tasks = response.data?.records || []
reviewTotal.value = response.data?.total || 0
// 如果是部门负责人,还要加载待审核的子任务组
if (isDepartmentLeader.value) {
promises.push(taskApi.getPendingApprovalTaskGroups())
}
const [tasksResponse, groupsResponse] = await Promise.all(promises)
let allItems: any[] = []
// 处理主任务
const tasks = tasksResponse.data?.records || []
for (const task of tasks) {
allItems.push({
...task,
_itemType: 'task' // 标记类型
})
}
// 处理子任务组(如果有)
if (groupsResponse && groupsResponse.code === 200 && groupsResponse.data) {
for (const group of groupsResponse.data) {
allItems.push({
id: `group-${group.taskId}-${group.groupNo}`,
title: group.taskTitle || '子任务组',
taskNo: group.taskNo || '',
score: group.totalScore || 0,
createTime: group.createTime,
_itemType: 'taskGroup', // 标记类型
_groupData: group // 保存原始子任务组数据
})
}
}
// 前端筛选(因为新接口只支持taskTypeId筛选)
if (reviewFilters.keyword) {
const keyword = reviewFilters.keyword.trim().toLowerCase()
tasks = tasks.filter((task: Task) =>
task.title?.toLowerCase().includes(keyword) ||
task.taskNo?.toLowerCase().includes(keyword) ||
task.spuInfo?.toLowerCase().includes(keyword) ||
task.publisherName?.toLowerCase().includes(keyword)
allItems = allItems.filter((item: any) =>
item.title?.toLowerCase().includes(keyword) ||
item.taskNo?.toLowerCase().includes(keyword) ||
item.spuInfo?.toLowerCase().includes(keyword) ||
item.publisherName?.toLowerCase().includes(keyword)
)
}
if (reviewFilters.brandId) {
tasks = tasks.filter((task: Task) => task.brandId === reviewFilters.brandId)
allItems = allItems.filter((item: any) => item._itemType === 'taskGroup' || item.brandId === reviewFilters.brandId)
}
if (reviewFilters.categoryId) {
tasks = tasks.filter((task: Task) => task.categoryId === reviewFilters.categoryId)
allItems = allItems.filter((item: any) => item._itemType === 'taskGroup' || item.categoryId === reviewFilters.categoryId)
}
if (reviewFilters.platformId) {
tasks = tasks.filter((task: Task) => task.platformId === reviewFilters.platformId)
allItems = allItems.filter((item: any) => item._itemType === 'taskGroup' || item.platformId === reviewFilters.platformId)
}
if (reviewFilters.minScore !== undefined) {
tasks = tasks.filter((task: Task) => (task.score || 0) >= reviewFilters.minScore!)
allItems = allItems.filter((item: any) => (item.score || 0) >= reviewFilters.minScore!)
}
if (reviewFilters.maxScore !== undefined) {
tasks = tasks.filter((task: Task) => (task.score || 0) <= reviewFilters.maxScore!)
allItems = allItems.filter((item: any) => (item.score || 0) <= reviewFilters.maxScore!)
}
if (reviewFilters.timeRange) {
@ -3889,22 +3904,22 @@ const loadReviewTasks = async () => {
} else {
startTime = now.subtract(1, 'year')
}
tasks = tasks.filter((task: Task) =>
task.createTime && dayjs(task.createTime).isAfter(startTime)
allItems = allItems.filter((item: any) =>
item.createTime && dayjs(item.createTime).isAfter(startTime)
)
}
// 排序
if (reviewFilters.sortBy === 'score') {
tasks.sort((a: Task, b: Task) => (b.score || 0) - (a.score || 0))
allItems.sort((a: any, b: any) => (b.score || 0) - (a.score || 0))
} else {
tasks.sort((a: Task, b: Task) =>
allItems.sort((a: any, b: any) =>
dayjs(b.createTime || 0).valueOf() - dayjs(a.createTime || 0).valueOf()
)
}
reviewTaskList.value = tasks
reviewTotal.value = tasks.length
reviewTaskList.value = allItems
reviewTotal.value = allItems.length
} catch (error) {
console.error('加载待审核任务失败:', error)
message.error('加载待审核任务失败')
@ -4581,17 +4596,37 @@ const loadAllTabCounts = async () => {
}).catch(() => {})
)
// 3. 待审核任务数量(所有人可见)
promises.push(
taskApi.getAllPendingApprovalTasks({
page: 1,
pageSize: 1
}).then(res => {
if (res.data && res.data.total !== undefined) {
reviewTotal.value = res.data.total
}
}).catch(() => {})
)
// 3. 待审核任务数量(主任务 + 子任务组)
if (isDepartmentLeader.value) {
// 部门负责人:同时计算主任务和子任务组
promises.push(
Promise.all([
taskApi.getAllPendingApprovalTasks({ page: 1, pageSize: 1 }),
taskApi.getPendingApprovalTaskGroups()
]).then(([tasksRes, groupsRes]) => {
let count = 0
if (tasksRes.data && tasksRes.data.total !== undefined) {
count += tasksRes.data.total
}
if (groupsRes.code === 200 && groupsRes.data) {
count += groupsRes.data.length
}
reviewTotal.value = count
}).catch(() => {})
)
} else {
// 普通用户:只计算主任务
promises.push(
taskApi.getAllPendingApprovalTasks({
page: 1,
pageSize: 1
}).then(res => {
if (res.data && res.data.total !== undefined) {
reviewTotal.value = res.data.total
}
}).catch(() => {})
)
}
// 4. 已完成任务数量
promises.push(
@ -4611,25 +4646,6 @@ const loadAllTabCounts = async () => {
}).catch(() => {})
)
// 6. 待我审核任务数量(仅部门负责人)- 需要同时计算主任务和子任务组
if (isDepartmentLeader.value) {
promises.push(
Promise.all([
taskApi.getPendingApprovalTasks(),
taskApi.getPendingApprovalTaskGroups()
]).then(([tasksRes, groupsRes]) => {
let count = 0
if (tasksRes.code === 200) {
count += (tasksRes.data || []).length
}
if (groupsRes.code === 200) {
count += (groupsRes.data || []).length
}
pendingApprovalTotal.value = count
}).catch(() => {})
)
}
await Promise.all(promises)
} catch (error) {
console.error('加载Tab数量统计失败:', error)