From c2938dee8973f54dab150cd7e888bfb38538574e Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 7 Jan 2026 17:37:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=90=88=E5=B9=B6=E5=BE=85=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E9=A1=B5=E9=9D=A2=EF=BC=8C=E5=B0=86=E5=AD=90=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=BB=84=E6=98=BE=E7=A4=BA=E5=9C=A8=E5=BE=85=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E4=BB=BB=E5=8A=A1=E6=A0=87=E7=AD=BE=E9=A1=B5=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 删除单独的'待我审核'标签页 2. 修改loadReviewTasks同时加载主任务和子任务组 3. 修改卡片模板支持显示子任务组 4. 添加子任务组的审核和驳回方法 5. 更新待审核任务数量统计 --- .../controller/TaskItemController.java | 4 +- .../designhub/service/TaskItemService.java | 26 +- frontend/src/views/TaskHall.vue | 626 +++++++++--------- 3 files changed, 336 insertions(+), 320 deletions(-) diff --git a/backend/src/main/java/com/designhub/controller/TaskItemController.java b/backend/src/main/java/com/designhub/controller/TaskItemController.java index 75cd672..d861649 100644 --- a/backend/src/main/java/com/designhub/controller/TaskItemController.java +++ b/backend/src/main/java/com/designhub/controller/TaskItemController.java @@ -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> allGroups = new java.util.ArrayList<>(); diff --git a/backend/src/main/java/com/designhub/service/TaskItemService.java b/backend/src/main/java/com/designhub/service/TaskItemService.java index 12213fc..8437988 100644 --- a/backend/src/main/java/com/designhub/service/TaskItemService.java +++ b/backend/src/main/java/com/designhub/service/TaskItemService.java @@ -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; } diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index a6e880f..f338dbf 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -532,15 +532,19 @@

{{ task.title }}

- + + + 子任务组 - 第{{ task._groupData?.groupNo }}组 + + 待审核
- -
+ +
@@ -550,17 +554,21 @@
-
- 任务类型 +
+ 任务类型 {{ getTaskTypeName(task.taskTypeId, task.taskTypeName, task.taskSubType) }} -
-
- 预估积分 - {{ task.totalScore || task.score }} 分 -
-
- 数量 - {{ task.quantity || '--' }} 个 +
+
+ 子任务数量 + {{ task._groupData?.itemCount || 0 }} 个 +
+
+ 预估积分 + {{ task.totalScore || task.score || task._groupData?.totalScore || 0 }} 分 +
+
+ 数量 + {{ task.quantity || '--' }} 个
@@ -572,8 +580,8 @@
- -
+ +
任务类型明细
@@ -586,54 +594,60 @@
+ + + + 任务编号: {{ task.taskNo || task._groupData?.taskNo || '--' }} +
+
发起人: {{ task.publisherName || '--' }}
-
+
接单人: {{ task.receiverName || '--' }}
-
+
品牌: {{ task.brandName }}
-
+
类目: {{ task.categoryName }}
-
+
平台: {{ task.platformName }}
-
+
SPU: {{ task.spuInfo }}
-
+
子类型: {{ task.taskSubType }}
-
+
交付路径: {{ task.deliveryPath }}
-
+
@@ -654,20 +668,32 @@
- 查看详情 - - 通过审核 - - - 驳回任务 - + +
@@ -1748,220 +1774,6 @@ />
- - - - -
-
- - 共 {{ pendingApprovalTotal }} 个待审核任务 -
-
- - -
-
- -
-

{{ item.title }}

-
- - - 子任务组 - - - - {{ getApprovalStatusText(item.data.approvalStatus) }} - - - - 紧急 - -
-
- - -
- - - - {{ getProcessStage(item.data) }} -
- - -
-
-
- 任务类型 - {{ getTaskTypeName(item.data.taskTypeId) }} -
-
- 子任务数量 - {{ item.data.items?.length || 0 }} 个 -
-
- 预估积分 - {{ item.score }} 分 -
-
- 数量 - {{ item.data.quantity || '--' }} 个 -
-
- -
- 预览图 -
- 预览图 -
-
-
- - -
-
- - - - 任务编号: {{ item.taskNo }} -
-
- - - - 发起人: {{ item.publisherName || '未知' }} -
-
- - - - 发起部门: {{ item.publisherDeptName }} -
- -
- - -
- - - -
-
- - - - - - - -
- -
-
-
@@ -1986,20 +1798,20 @@
{{ getTaskTypeFullPath(item.typeId)?.name || getTaskTypeName(item.typeId) }} {{ getTaskTypeFullPath(item.typeId)?.fullPath || '' }} -
+
{{ getTaskTypeUnitScore(item.typeId) }} 分/个
- 数量 - 数量 + + />
{ 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(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[] = [ + 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)