feat: 任务类型选择改为三级级联选择
This commit is contained in:
parent
b20c9e1361
commit
65ed9e4d6d
@ -112,7 +112,7 @@ public class ConfigService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建需求类型树形结构
|
||||
* 构建需求类型树形结构(支持多级)
|
||||
*/
|
||||
private List<DemandType> buildDemandTypeTree(List<DemandType> allTypes) {
|
||||
// 获取所有根节点(parentId为null)
|
||||
@ -120,16 +120,30 @@ public class ConfigService {
|
||||
.filter(dt -> dt.getParentId() == null)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 为每个根节点填充子节点
|
||||
// 递归为每个节点填充子节点
|
||||
for (DemandType root : roots) {
|
||||
List<DemandType> children = allTypes.stream()
|
||||
.filter(dt -> root.getId().equals(dt.getParentId()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
root.setChildren(children);
|
||||
fillChildren(root, allTypes);
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归填充子节点
|
||||
*/
|
||||
private void fillChildren(DemandType parent, List<DemandType> allTypes) {
|
||||
List<DemandType> children = allTypes.stream()
|
||||
.filter(dt -> parent.getId().equals(dt.getParentId()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
if (!children.isEmpty()) {
|
||||
parent.setChildren(children);
|
||||
// 递归填充每个子节点的子节点
|
||||
for (DemandType child : children) {
|
||||
fillChildren(child, allTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取需求类型
|
||||
|
||||
@ -56,23 +56,26 @@
|
||||
class="task-type-item"
|
||||
>
|
||||
<div class="task-type-row">
|
||||
<div class="task-type-select">
|
||||
<a-select
|
||||
v-model:value="item.taskTypeId"
|
||||
placeholder="请选择任务类型"
|
||||
<div class="task-type-select task-type-cascader">
|
||||
<a-cascader
|
||||
v-model:value="item.taskTypePath"
|
||||
:options="taskTypeCascaderOptions"
|
||||
placeholder="请选择任务类型(一级→二级→三级)"
|
||||
size="large"
|
||||
:loading="loading"
|
||||
allow-clear
|
||||
@change="calculateScore"
|
||||
expand-trigger="hover"
|
||||
:show-search="{ filter: cascaderFilter }"
|
||||
@change="(value: any) => onTaskTypeChange(value, item, index)"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="type in taskTypes"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
>
|
||||
{{ type.name }} ({{ type.unitScore }}分/个, {{ type.estimatedHours }}小时)
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<template #displayRender="{ labels }">
|
||||
<span>{{ labels.join(' / ') }}</span>
|
||||
</template>
|
||||
</a-cascader>
|
||||
<div v-if="item.taskTypeId && getTaskTypeInfo(item.taskTypeId)" class="task-type-score-tip">
|
||||
<span class="score-badge">{{ getTaskTypeInfo(item.taskTypeId)?.unitScore }}分/个</span>
|
||||
<span class="hour-badge">{{ getTaskTypeInfo(item.taskTypeId)?.estimatedHours }}小时</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-type-quantity">
|
||||
<a-input-number
|
||||
@ -545,10 +548,20 @@ const previewUploading = ref(false)
|
||||
// 任务类型项接口
|
||||
interface TaskTypeItem {
|
||||
taskTypeId: number | undefined
|
||||
taskTypePath?: (string | number)[] // 级联选择路径 [level1Id, level2Id, level3Id]
|
||||
quantity: number
|
||||
receiverId?: number // 指定接收人ID
|
||||
}
|
||||
|
||||
// 级联选择器选项接口
|
||||
interface CascaderOption {
|
||||
value: number
|
||||
label: string
|
||||
children?: CascaderOption[]
|
||||
unitScore?: number
|
||||
estimatedHours?: number
|
||||
}
|
||||
|
||||
// 设计师列表缓存 { deptId: designers[] }
|
||||
const designersCache = reactive<Record<number, any[]>>({})
|
||||
|
||||
@ -557,7 +570,7 @@ const formData = reactive({
|
||||
title: '',
|
||||
isPipeline: true, // 固定为流水线任务
|
||||
pipelineMode: 'sequential' as 'sequential' | 'parallel', // 固定为顺序流水线
|
||||
taskTypes: [{ taskTypeId: undefined, quantity: 1, receiverId: undefined }] as TaskTypeItem[], // 任务类型数组
|
||||
taskTypes: [{ taskTypeId: undefined, taskTypePath: undefined, quantity: 1, receiverId: undefined }] as TaskTypeItem[], // 任务类型数组
|
||||
taskSubType: '', // 动态加载,初始为空
|
||||
brandId: undefined,
|
||||
platformId: undefined,
|
||||
@ -651,6 +664,83 @@ const brands = computed(() => configStore.brands)
|
||||
const platforms = computed(() => configStore.platforms)
|
||||
const categories = computed(() => configStore.categories)
|
||||
|
||||
// 层级任务类型数据
|
||||
const hierarchicalTaskTypes = ref<any[]>([])
|
||||
|
||||
// 加载层级任务类型数据
|
||||
const loadHierarchicalTaskTypes = async () => {
|
||||
try {
|
||||
const response = await configApi.getDemandTypesTree()
|
||||
if (response && response.data) {
|
||||
hierarchicalTaskTypes.value = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载层级任务类型失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建级联选择器选项(三级结构)
|
||||
const taskTypeCascaderOptions = computed<CascaderOption[]>(() => {
|
||||
const buildOptions = (items: any[]): CascaderOption[] => {
|
||||
return items.map(item => {
|
||||
const option: CascaderOption = {
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
unitScore: item.unitScore,
|
||||
estimatedHours: item.estimatedHours
|
||||
}
|
||||
|
||||
// 如果有子级,递归构建
|
||||
if (item.children && item.children.length > 0) {
|
||||
option.children = buildOptions(item.children)
|
||||
}
|
||||
|
||||
return option
|
||||
})
|
||||
}
|
||||
|
||||
return buildOptions(hierarchicalTaskTypes.value)
|
||||
})
|
||||
|
||||
// 级联选择器搜索过滤
|
||||
const cascaderFilter = (inputValue: string, path: CascaderOption[]) => {
|
||||
return path.some(option =>
|
||||
option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
|
||||
)
|
||||
}
|
||||
|
||||
// 任务类型级联选择变更
|
||||
const onTaskTypeChange = (value: (string | number)[] | undefined, item: TaskTypeItem, index: number) => {
|
||||
if (value && value.length === 3) {
|
||||
// 选择了三级分类,设置taskTypeId为三级分类的ID
|
||||
item.taskTypeId = value[2] as number
|
||||
item.taskTypePath = value
|
||||
} else {
|
||||
// 未选择完整路径
|
||||
item.taskTypeId = undefined
|
||||
item.taskTypePath = value
|
||||
}
|
||||
calculateScore()
|
||||
}
|
||||
|
||||
// 获取任务类型信息(根据ID从平铺的taskTypes中查找)
|
||||
const getTaskTypeInfo = (taskTypeId: number) => {
|
||||
// 从平铺列表中查找
|
||||
return taskTypes.value.find((t: any) => t.id === taskTypeId)
|
||||
}
|
||||
|
||||
// 从平铺列表中根据ID查找任务类型(递归查找层级数据)
|
||||
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 loadDesignersForItem = async (item: TaskTypeItem) => {
|
||||
if (!item.taskTypeId) return
|
||||
@ -694,6 +784,7 @@ const getDesignersForItem = (item: TaskTypeItem) => {
|
||||
const addTaskType = () => {
|
||||
formData.taskTypes.push({
|
||||
taskTypeId: undefined,
|
||||
taskTypePath: undefined,
|
||||
quantity: 1,
|
||||
receiverId: undefined
|
||||
})
|
||||
@ -1428,7 +1519,8 @@ onMounted(async () => {
|
||||
loadConfigData(),
|
||||
loadTeamScore(),
|
||||
loadTaskSubTypes(),
|
||||
loadReleaseTypeConfig()
|
||||
loadReleaseTypeConfig(),
|
||||
loadHierarchicalTaskTypes()
|
||||
])
|
||||
|
||||
// 检查是否是编辑模式
|
||||
@ -1678,6 +1770,36 @@ onUnmounted(() => {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.task-type-cascader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
:deep(.ant-cascader) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.task-type-score-tip {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
|
||||
.score-badge {
|
||||
color: $primary;
|
||||
background: rgba($primary, 0.1);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hour-badge {
|
||||
color: $text-secondary;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-type-quantity {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user