fix: 合并待审核页面,将子任务组显示在待审核任务标签页中
1. 删除单独的'待我审核'标签页 2. 修改loadReviewTasks同时加载主任务和子任务组 3. 修改卡片模板支持显示子任务组 4. 添加子任务组的审核和驳回方法 5. 更新待审核任务数量统计
This commit is contained in:
parent
0c12b28f7a
commit
c2938dee89
@ -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<>();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user