fix: 修复审核弹窗任务类型显示和部门负责人看任务卡片问题

1. 审核弹窗任务类型显示增加完整路径(父级名称)和单位积分
2. 修复级联选择器弹窗为深色主题
3. 添加task_item.receiver_dept_id字段支持部门负责人查看部门成员任务
4. 更新grabItem和分配任务逻辑设置receiver_dept_id
This commit is contained in:
Your Name 2026-01-07 15:02:29 +08:00
parent c87c646d4d
commit 0ef762baa4
4 changed files with 124 additions and 15 deletions

View File

@ -76,6 +76,11 @@ public class TaskItem implements Serializable {
*/
private String receiverName;
/**
* 接单部门ID
*/
private Long receiverDeptId;
/**
* 状态:locked/pending/in_progress/completed/rejected
*/

View File

@ -163,6 +163,7 @@ public interface TaskItemMapper extends BaseMapper<TaskItem> {
@Update("UPDATE task_item SET " +
"receiver_id = #{receiverId}, " +
"receiver_name = #{receiverName}, " +
"receiver_dept_id = #{receiverDeptId}, " +
"status = 'in_progress', " +
"update_time = NOW() " +
"WHERE id = #{itemId} " +
@ -171,7 +172,8 @@ public interface TaskItemMapper extends BaseMapper<TaskItem> {
"AND receiver_id IS NULL")
int grabItem(@Param("itemId") Long itemId,
@Param("receiverId") Long receiverId,
@Param("receiverName") String receiverName);
@Param("receiverName") String receiverName,
@Param("receiverDeptId") Long receiverDeptId);
/**
* 解锁子任务

View File

@ -209,8 +209,8 @@ public class TaskItemService {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "该子任务已被接单");
}
// 乐观锁抢单
int result = taskItemMapper.grabItem(itemId, userId, user.getRealName());
// 乐观锁抢单(同时设置接单部门ID)
int result = taskItemMapper.grabItem(itemId, userId, user.getRealName(), user.getDeptId());
if (result == 0) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "抢单失败,该子任务可能已被他人接单");
}
@ -619,6 +619,7 @@ public class TaskItemService {
for (TaskItem item : groupItems) {
item.setStatus("in_progress");
item.setReceiverId(assignedReceiverId);
item.setReceiverDeptId(receiver.getDeptId()); // 设置接单部门ID
item.setUpdateTime(LocalDateTime.now());
taskItemMapper.updateById(item);
}

View File

@ -1968,18 +1968,26 @@
<span class="section-title" style="font-weight: 600; color: #fff;">编辑任务类型</span>
</div>
<div class="task-type-list" style="background: #141414; padding: 12px; border-radius: 8px;">
<div v-for="(item, index) in approveTaskTypes" :key="index" class="task-type-item" style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 8px; background: #262626; border-radius: 4px;">
<div class="type-info">
<a-tag color="#303030" style="color: #fff; border: 1px solid #444;">{{ getTaskTypeName(item.typeId) }}</a-tag>
<div v-for="(item, index) in approveTaskTypes" :key="index" class="task-type-item" style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 12px; background: #262626; border-radius: 6px;">
<div class="type-info" style="flex: 1; min-width: 0;">
<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 class="type-controls" 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"
style="width: 60px; margin-right: 8px;"
/>
<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"
style="width: 70px;"
/>
</div>
<a-button
type="text"
danger
@ -2001,7 +2009,7 @@
allow-clear
change-on-select
:show-search="{ filter: cascaderFilter }"
:dropdown-style="{ background: '#262626' }"
popup-class-name="dark-cascader-popup"
/>
<a-button type="primary" ghost style="border-color: #31f08b; color: #31f08b;" @click="handleAddApproveTaskType">
<Icon name="plus" size="sm" style="margin-right: 4px" /> 添加
@ -2492,6 +2500,25 @@ const findTaskTypeById = (id: number, items: any[] = hierarchicalTaskTypes.value
return null
}
// 获取任务类型的完整路径信息(包含父级名称和积分)
const getTaskTypeFullPath = (id: number, items: any[] = hierarchicalTaskTypes.value, path: string[] = []): { name: string, fullPath: string, unitScore: number } | null => {
for (const item of items) {
const currentPath = [...path, item.name]
if (item.id === id) {
return {
name: item.name,
fullPath: currentPath.join(' > '),
unitScore: item.unitScore || 0
}
}
if (item.children) {
const found = getTaskTypeFullPath(id, item.children, currentPath)
if (found) return found
}
}
return null
}
// 构建级联选择器选项
const taskTypeCascaderOptions = computed(() => {
const buildOptions = (items: any[]): any[] => {
@ -5838,5 +5865,79 @@ onUnmounted(() => {
:deep(.approve-modal) .ant-btn-text.ant-btn-danger:hover {
background-color: rgba(255, 77, 79, 0.1);
}
/* 级联选择器深色主题 - 组件内部 */
:deep(.approve-modal) .ant-cascader {
.ant-select-selector {
background-color: #141414 !important;
border-color: #303030 !important;
color: #fff !important;
}
&:hover .ant-select-selector {
border-color: #31f08b !important;
}
.ant-select-selection-placeholder {
color: #666 !important;
}
.ant-select-selection-item {
color: #fff !important;
}
}
</style>
<!-- 全局样式:级联选择器弹窗深色主题(弹窗渲染在body上) -->
<style>
.dark-cascader-popup {
background-color: #1f1f1f !important;
border-radius: 8px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5) !important;
}
.dark-cascader-popup .ant-cascader-menu {
background-color: #1f1f1f !important;
border-color: #303030 !important;
}
.dark-cascader-popup .ant-cascader-menu-item {
color: #fff !important;
padding: 8px 12px;
}
.dark-cascader-popup .ant-cascader-menu-item:hover {
background-color: rgba(49, 240, 139, 0.1) !important;
}
.dark-cascader-popup .ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled),
.dark-cascader-popup .ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled):hover {
background-color: rgba(49, 240, 139, 0.2) !important;
font-weight: 500;
}
.dark-cascader-popup .ant-cascader-menu-item-expand-icon {
color: #888 !important;
}
.dark-cascader-popup .ant-cascader-menu-item-content {
color: #fff !important;
}
/* 搜索框深色主题 */
.dark-cascader-popup .ant-cascader-input {
background-color: #262626 !important;
border-color: #303030 !important;
color: #fff !important;
}
.dark-cascader-popup .ant-cascader-input::placeholder {
color: #666 !important;
}
/* 空状态 */
.dark-cascader-popup .ant-cascader-empty {
color: #888 !important;
}
</style>