diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index 5355f05..86a6c4e 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -1991,27 +1991,19 @@ - +
- - - {{ type.name }} - - - + /> + 添加
@@ -2364,6 +2356,7 @@ import DefaultLayout from '@/layouts/DefaultLayout.vue' import Icon from '@/components/Icon.vue' import * as taskApi from '@/api/task' import * as userApi from '@/api/user' +import * as configApi from '@/api/config' import type { Task, TaskApprovalDTO, TaskProduct } from '@/api/task' import type { DesignerWithWorkload } from '@/api/user' import { useConfigStore } from '@/stores/config' @@ -2485,6 +2478,38 @@ const brands = computed(() => configStore.brands) const platforms = computed(() => configStore.platforms) const categories = computed(() => configStore.categories) +// 层级任务类型数据(用于级联选择器) +const hierarchicalTaskTypes = ref([]) + +// 递归查找任务类型(从层级数据中查找,支持三级分类) +const findTaskTypeById = (id: number, items: any[] = hierarchicalTaskTypes.value): any => { + for (const item of items) { + if (item.id === id) return item + if (item.children) { + const found = findTaskTypeById(id, item.children) + if (found) return found + } + } + return null +} + +// 构建级联选择器选项 +const taskTypeCascaderOptions = computed(() => { + const buildOptions = (items: any[]): any[] => { + return items.map(item => { + const option: any = { + value: item.id, + label: item.name + } + if (item.children && item.children.length > 0) { + option.children = buildOptions(item.children) + } + return option + }) + } + return buildOptions(hierarchicalTaskTypes.value) +}) + // 筛选条件 const filters = reactive({ taskTypeId: undefined as number | undefined, @@ -2573,7 +2598,10 @@ const getTaskTypeName = (typeId: number, taskTypeName?: string, taskSubType?: st if (taskSubType) return taskSubType // 如果有直接传入的类型名称,使用它 if (taskTypeName) return taskTypeName - // 从需求类型列表中查找 + // 从层级数据中递归查找(支持三级分类) + const typeInfo = findTaskTypeById(typeId) + if (typeInfo) return typeInfo.name + // 兼容:从平铺列表中查找 const type = demandTypes.value.find(t => t.id === typeId) return type?.name || '未知类型' } @@ -2592,6 +2620,10 @@ const parseTypeDetails = (typeDetails: string | TaskTypeDetail[] | undefined): T // 获取任务类型单位积分 const getTaskTypeUnitScore = (typeId: number) => { + // 从层级数据中递归查找(支持三级分类) + const typeInfo = findTaskTypeById(typeId) + if (typeInfo && typeInfo.unitScore) return typeInfo.unitScore + // 兼容:从平铺列表中查找 const type = demandTypes.value.find(t => t.id === typeId) return type?.unitScore || 0 } @@ -2714,6 +2746,11 @@ const isHighScoreTask = (task: TaskOrSubTask) => { const loadConfigs = async () => { try { await configStore.loadAllConfigs() + // 加载层级任务类型数据(用于级联选择器和递归查找) + const response = await configApi.getDemandTypesTree() + if (response && response.data) { + hierarchicalTaskTypes.value = response.data + } } catch (error) { console.error('加载配置数据失败:', error) } @@ -3024,7 +3061,30 @@ const approveAssignType = ref<'auto' | 'manual'>('auto') const approveTaskTypes = ref>([]) const approveDesignerId = ref(undefined) const designersList = ref([]) -const newTaskTypeId = ref(undefined) +const newTaskTypePath = ref<(string | number)[] | undefined>(undefined) + +// 级联选择器搜索过滤 +const cascaderFilter = (inputValue: string, path: any[]) => { + return path.some(option => + option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1 + ) +} + +// 添加审核弹窗中的任务类型 +const handleAddApproveTaskType = () => { + if (!newTaskTypePath.value || newTaskTypePath.value.length === 0) { + message.warning('请选择任务类型') + return + } + // 获取最后一个ID(即最深层级的类型ID) + const typeId = newTaskTypePath.value[newTaskTypePath.value.length - 1] as number + if (approveTaskTypes.value.find(t => t.typeId === typeId)) { + message.warning('该任务类型已添加') + return + } + approveTaskTypes.value.push({ typeId, quantity: 1 }) + newTaskTypePath.value = undefined +} // 格式化相对时间 const formatDeadlineRelative = (deadline: string) => { @@ -3067,17 +3127,34 @@ const showAssignDesignerModal = async (task: Task) => { approveTaskData.value = task approveAssignType.value = 'auto' approveDesignerId.value = undefined - newTaskTypeId.value = undefined + newTaskTypePath.value = undefined - // 解析任务类型 - const taskTypeIdsStr = task.taskTypeIds || task.taskTypeId?.toString() || '' - const taskTypeIdArray = taskTypeIdsStr.split(',').filter(id => id.trim()) + // 解析任务类型 - 优先从 typeDetails 获取(包含每个类型的实际数量) + let typeDetailsArray: TaskTypeDetail[] = [] + if ((task as any).typeDetails) { + try { + const details = (task as any).typeDetails + typeDetailsArray = typeof details === 'string' ? JSON.parse(details) : details + } catch (e) { + console.warn('解析 typeDetails 失败:', e) + } + } - // 初始化任务类型列表 - approveTaskTypes.value = taskTypeIdArray.map(id => ({ - typeId: parseInt(id.trim()), - quantity: task.quantity || 1 - })) + // 如果有 typeDetails,从中获取任务类型和数量 + if (typeDetailsArray.length > 0) { + approveTaskTypes.value = typeDetailsArray.map(detail => ({ + typeId: detail.typeId, + quantity: detail.quantity || 1 + })) + } else { + // 兼容旧数据:从 taskTypeIds 获取 + const taskTypeIdsStr = task.taskTypeIds || task.taskTypeId?.toString() || '' + const taskTypeIdArray = taskTypeIdsStr.split(',').filter(id => id.trim()) + approveTaskTypes.value = taskTypeIdArray.map(id => ({ + typeId: parseInt(id.trim()), + quantity: task.quantity || 1 + })) + } approveModalVisible.value = true } catch (error: any) {