feat: 支持三级分类结构 - 一级(顶级)/二级(设置部门)/三级(设置分值)
This commit is contained in:
parent
6c85f4ca4d
commit
874abf35cf
@ -90,20 +90,24 @@
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">需求类型管理</h2>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="showDemandTypeModal(null, null)">
|
||||
<a-button type="primary" @click="showDemandTypeModal(null, 'level1')">
|
||||
<template #icon><Icon name="plus" size="sm" /></template>
|
||||
添加大分类
|
||||
添加一级分类
|
||||
</a-button>
|
||||
<a-button @click="showDemandTypeModal(null, 'sub')" :disabled="parentDemandTypes.length === 0">
|
||||
<a-button @click="showDemandTypeModal(null, 'level2')" :disabled="level1DemandTypes.length === 0">
|
||||
<template #icon><Icon name="plus" size="sm" /></template>
|
||||
添加子分类
|
||||
添加二级分类
|
||||
</a-button>
|
||||
<a-button @click="showDemandTypeModal(null, 'level3')" :disabled="level2DemandTypes.length === 0">
|
||||
<template #icon><Icon name="plus" size="sm" /></template>
|
||||
添加三级分类
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-alert type="info" show-icon style="margin-bottom: 16px;">
|
||||
<template #message>
|
||||
<span>大分类用于设置可接单部门限制,子分类继承大分类的部门设置。单价分值支持小数(如0.35)</span>
|
||||
<span>支持三级分类:一级分类(顶级) → 二级分类(设置接单部门) → 三级分类(设置单价分值)。三级分类继承二级分类的部门设置。</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
|
||||
@ -115,32 +119,38 @@
|
||||
:pagination="{ pageSize: 20 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'parentName'">
|
||||
<template v-if="column.key === 'level'">
|
||||
<a-tag v-if="getDemandTypeLevel(record) === 1" color="blue">一级</a-tag>
|
||||
<a-tag v-else-if="getDemandTypeLevel(record) === 2" color="green">二级</a-tag>
|
||||
<a-tag v-else color="orange">三级</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'parentName'">
|
||||
<span v-if="record.parentId">{{ record.parentName || '--' }}</span>
|
||||
<a-tag v-else color="blue">大分类</a-tag>
|
||||
<span v-else class="text-muted">--</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'name'">
|
||||
<span :style="{ paddingLeft: record.parentId ? '20px' : '0' }">
|
||||
<span v-if="record.parentId">└ </span>
|
||||
<span :style="{ paddingLeft: (getDemandTypeLevel(record) - 1) * 20 + 'px' }">
|
||||
<span v-if="getDemandTypeLevel(record) > 1">└ </span>
|
||||
{{ record.name || '--' }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'unitScore'">
|
||||
<span v-if="record.parentId">{{ record.unitScore ?? '--' }}</span>
|
||||
<span v-if="getDemandTypeLevel(record) === 3">{{ record.unitScore ?? '--' }}</span>
|
||||
<span v-else class="text-muted">--</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'estimatedHours'">
|
||||
<span v-if="record.parentId">{{ record.estimatedHours ?? '--' }}</span>
|
||||
<span v-if="getDemandTypeLevel(record) === 3">{{ record.estimatedHours ?? '--' }}</span>
|
||||
<span v-else class="text-muted">--</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'allowedDepts'">
|
||||
<template v-if="!record.parentId">
|
||||
<template v-if="getDemandTypeLevel(record) === 2">
|
||||
<span v-if="record.allowedDeptIds">
|
||||
{{ getDeptNamesFromIds(record.allowedDeptIds) }}
|
||||
</span>
|
||||
<span v-else class="text-muted">所有接单部门</span>
|
||||
</template>
|
||||
<span v-else class="text-muted">继承大分类</span>
|
||||
<span v-else-if="getDemandTypeLevel(record) === 3" class="text-muted">继承二级</span>
|
||||
<span v-else class="text-muted">--</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-switch
|
||||
@ -152,15 +162,23 @@
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="showDemandTypeModal(record)">编辑</a-button>
|
||||
<a-button
|
||||
v-if="!record.parentId"
|
||||
v-if="getDemandTypeLevel(record) === 1"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="showDemandTypeModal(null, 'sub', record.id)"
|
||||
@click="showDemandTypeModal(null, 'level2', record.id)"
|
||||
>
|
||||
添加子分类
|
||||
添加二级
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="getDemandTypeLevel(record) === 2"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="showDemandTypeModal(null, 'level3', record.id)"
|
||||
>
|
||||
添加三级
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
:title="record.parentId ? '确定删除此子分类?' : '确定删除此大分类及其所有子分类?'"
|
||||
:title="getDemandTypeLevel(record) === 1 ? '确定删除?将同时删除所有下级分类' : getDemandTypeLevel(record) === 2 ? '确定删除?将同时删除所有三级分类' : '确定删除此分类?'"
|
||||
@confirm="deleteItem('demandType', record.id)"
|
||||
>
|
||||
<a-button type="link" danger size="small">删除</a-button>
|
||||
@ -1169,15 +1187,15 @@
|
||||
width="600px"
|
||||
>
|
||||
<a-form :model="formData.demandType" layout="vertical">
|
||||
<!-- 选择所属大分类(仅子分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.isSubType" label="所属大分类" required>
|
||||
<!-- 选择所属一级分类(二级分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.addLevel === 'level2'" label="所属一级分类" required>
|
||||
<a-select
|
||||
v-model:value="formData.demandType.parentId"
|
||||
placeholder="请选择所属大分类"
|
||||
placeholder="请选择所属一级分类"
|
||||
:disabled="!!editingItem"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="parent in parentDemandTypes"
|
||||
v-for="parent in level1DemandTypes"
|
||||
:key="parent.id"
|
||||
:value="parent.id"
|
||||
>
|
||||
@ -1186,12 +1204,29 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 选择所属二级分类(三级分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.addLevel === 'level3'" label="所属二级分类" required>
|
||||
<a-select
|
||||
v-model:value="formData.demandType.parentId"
|
||||
placeholder="请选择所属二级分类"
|
||||
:disabled="!!editingItem"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="parent in level2DemandTypes"
|
||||
:key="parent.id"
|
||||
:value="parent.id"
|
||||
>
|
||||
{{ getLevel2DisplayName(parent) }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="类型名称" required>
|
||||
<a-input v-model:value="formData.demandType.name" placeholder="请输入类型名称" />
|
||||
</a-form-item>
|
||||
|
||||
<!-- 单价分值(仅子分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.isSubType" label="单价分值" required>
|
||||
<!-- 单价分值(仅三级分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.addLevel === 'level3'" label="单价分值" required>
|
||||
<a-input-number
|
||||
v-model:value="formData.demandType.unitScore"
|
||||
:min="0"
|
||||
@ -1202,8 +1237,8 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 预计时长(仅子分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.isSubType" label="预计时长(小时)">
|
||||
<!-- 预计时长(仅三级分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.addLevel === 'level3'" label="预计时长(小时)">
|
||||
<a-input-number
|
||||
v-model:value="formData.demandType.estimatedHours"
|
||||
:min="0"
|
||||
@ -1217,8 +1252,8 @@
|
||||
<a-textarea v-model:value="formData.demandType.description" :rows="3" placeholder="请输入描述" />
|
||||
</a-form-item>
|
||||
|
||||
<!-- 可接单部门(仅大分类显示) -->
|
||||
<a-form-item v-if="!formData.demandType.isSubType" label="可接单部门">
|
||||
<!-- 可接单部门(仅二级分类显示) -->
|
||||
<a-form-item v-if="formData.demandType.addLevel === 'level2'" label="可接单部门">
|
||||
<a-select
|
||||
v-model:value="formData.demandType.departmentIds"
|
||||
mode="multiple"
|
||||
@ -1242,14 +1277,21 @@
|
||||
</a-select>
|
||||
<div class="form-tip">
|
||||
<Icon name="info" size="sm" color="#6B6B6B" style="margin-right: 4px;" />
|
||||
选择父级部门会自动包含所有子部门。子分类会继承大分类的部门设置。
|
||||
选择父级部门会自动包含所有子部门。三级分类会继承此设置。
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 子分类提示 -->
|
||||
<a-alert v-if="formData.demandType.isSubType" type="info" show-icon>
|
||||
<!-- 一级分类提示 -->
|
||||
<a-alert v-if="formData.demandType.addLevel === 'level1'" type="info" show-icon>
|
||||
<template #message>
|
||||
<span>子分类将继承大分类的可接单部门设置</span>
|
||||
<span>一级分类是顶级分类,用于组织二级分类</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
|
||||
<!-- 三级分类提示 -->
|
||||
<a-alert v-if="formData.demandType.addLevel === 'level3'" type="info" show-icon>
|
||||
<template #message>
|
||||
<span>三级分类将继承二级分类的可接单部门设置</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
</a-form>
|
||||
@ -1950,7 +1992,7 @@ const defaultDepartmentForm = (): DepartmentForm => ({
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
demandType: {} as Partial<DemandType> & { departmentIds?: number[], isSubType?: boolean },
|
||||
demandType: {} as Partial<DemandType> & { departmentIds?: number[], addLevel?: string },
|
||||
taskSubType: {} as Partial<TaskSubType>,
|
||||
brand: {} as Partial<Brand>,
|
||||
platform: {} as Partial<Platform>,
|
||||
@ -2069,11 +2111,35 @@ const receivingDepartments = computed(() => {
|
||||
return departments.value.filter(dept => dept.deptType === 'receiving')
|
||||
})
|
||||
|
||||
// 大分类列表(parentId为null的需求类型)
|
||||
const parentDemandTypes = computed(() => {
|
||||
// 一级分类列表(parentId为null的需求类型)
|
||||
const level1DemandTypes = computed(() => {
|
||||
return demandTypes.value.filter(dt => !dt.parentId)
|
||||
})
|
||||
|
||||
// 二级分类列表(父级是一级分类的)
|
||||
const level2DemandTypes = computed(() => {
|
||||
const level1Ids = level1DemandTypes.value.map(dt => dt.id)
|
||||
return demandTypes.value.filter(dt => dt.parentId && level1Ids.includes(dt.parentId))
|
||||
})
|
||||
|
||||
// 三级分类列表(父级是二级分类的)
|
||||
const level3DemandTypes = computed(() => {
|
||||
const level2Ids = level2DemandTypes.value.map(dt => dt.id)
|
||||
return demandTypes.value.filter(dt => dt.parentId && level2Ids.includes(dt.parentId))
|
||||
})
|
||||
|
||||
// 兼容旧代码
|
||||
const parentDemandTypes = level2DemandTypes
|
||||
|
||||
// 获取需求类型的层级
|
||||
const getDemandTypeLevel = (record: DemandType): number => {
|
||||
if (!record.parentId) return 1
|
||||
const parent = demandTypes.value.find(dt => dt.id === record.parentId)
|
||||
if (!parent) return 1
|
||||
if (!parent.parentId) return 2
|
||||
return 3
|
||||
}
|
||||
|
||||
// 扁平化的部门选项(带层级显示)
|
||||
const flatDepartmentOptions = computed(() => {
|
||||
const receiving = departments.value.filter(dept => dept.deptType === 'receiving')
|
||||
@ -2142,9 +2208,20 @@ const getDeptNamesFromIds = (idsStr: string) => {
|
||||
// 获取需求类型弹窗标题
|
||||
const getDemandTypeModalTitle = () => {
|
||||
if (editingItem.value) {
|
||||
return formData.demandType.parentId ? '编辑子分类' : '编辑大分类'
|
||||
const level = getDemandTypeLevel(editingItem.value as DemandType)
|
||||
if (level === 1) return '编辑一级分类'
|
||||
if (level === 2) return '编辑二级分类'
|
||||
return '编辑三级分类'
|
||||
}
|
||||
return formData.demandType.isSubType ? '添加子分类' : '添加大分类'
|
||||
if (formData.demandType.addLevel === 'level1') return '添加一级分类'
|
||||
if (formData.demandType.addLevel === 'level2') return '添加二级分类'
|
||||
return '添加三级分类'
|
||||
}
|
||||
|
||||
// 获取二级分类的显示名称(包含一级分类)
|
||||
const getLevel2DisplayName = (level2: DemandType) => {
|
||||
const level1 = level1DemandTypes.value.find(dt => dt.id === level2.parentId)
|
||||
return level1 ? `${level1.name} / ${level2.name}` : level2.name
|
||||
}
|
||||
|
||||
const leaderOptions = computed(() =>
|
||||
@ -2176,14 +2253,15 @@ const getLimitPeriodLabel = (value?: string) => {
|
||||
// 表格列定义
|
||||
const demandTypeColumns = [
|
||||
{ title: 'ID', dataIndex: 'id', key: 'id', width: 60 },
|
||||
{ title: '分类', key: 'parentName', width: 100 },
|
||||
{ title: '级别', key: 'level', width: 70 },
|
||||
{ title: '上级', key: 'parentName', width: 100 },
|
||||
{ title: '类型名称', dataIndex: 'name', key: 'name' },
|
||||
{ title: '单价分值', dataIndex: 'unitScore', key: 'unitScore', width: 100 },
|
||||
{ title: '预计时长(h)', dataIndex: 'estimatedHours', key: 'estimatedHours', width: 110 },
|
||||
{ title: '预计时长(h)', dataIndex: 'estimatedHours', key: 'estimatedHours', width: 100 },
|
||||
{ title: '可接单部门', key: 'allowedDepts', ellipsis: true },
|
||||
{ title: '排序', dataIndex: 'sortOrder', key: 'sortOrder', width: 60 },
|
||||
{ title: '状态', key: 'status', width: 80 },
|
||||
{ title: '操作', key: 'actions', width: 200 }
|
||||
{ title: '状态', key: 'status', width: 70 },
|
||||
{ title: '操作', key: 'actions', width: 220 }
|
||||
]
|
||||
|
||||
const taskSubTypeColumns = [
|
||||
@ -2535,20 +2613,20 @@ const showDemandTypeModal = async (item?: DemandType | null, type?: string | nul
|
||||
const departmentIds = item.allowedDeptIds
|
||||
? item.allowedDeptIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id))
|
||||
: []
|
||||
const level = getDemandTypeLevel(item)
|
||||
formData.demandType = {
|
||||
...item,
|
||||
departmentIds,
|
||||
isSubType: !!item.parentId
|
||||
addLevel: level === 1 ? 'level1' : level === 2 ? 'level2' : 'level3'
|
||||
}
|
||||
} else {
|
||||
// 新建模式
|
||||
const isSubType = type === 'sub'
|
||||
formData.demandType = {
|
||||
sortOrder: 0,
|
||||
isEnabled: 1,
|
||||
departmentIds: [],
|
||||
isSubType,
|
||||
parentId: isSubType ? (defaultParentId || null) : null
|
||||
addLevel: type || 'level1',
|
||||
parentId: defaultParentId || null
|
||||
}
|
||||
}
|
||||
|
||||
@ -2627,14 +2705,16 @@ const closeModal = (type: string) => {
|
||||
// 保存操作
|
||||
const saveDemandType = async () => {
|
||||
try {
|
||||
// 子分类必须选择父级
|
||||
if (formData.demandType.isSubType && !formData.demandType.parentId) {
|
||||
message.error('请选择所属大分类')
|
||||
const addLevel = formData.demandType.addLevel || 'level1'
|
||||
|
||||
// 二级、三级分类必须选择父级
|
||||
if ((addLevel === 'level2' || addLevel === 'level3') && !formData.demandType.parentId) {
|
||||
message.error(addLevel === 'level2' ? '请选择所属一级分类' : '请选择所属二级分类')
|
||||
return
|
||||
}
|
||||
|
||||
// 子分类必须填写单价分值
|
||||
if (formData.demandType.isSubType && (formData.demandType.unitScore === null || formData.demandType.unitScore === undefined)) {
|
||||
// 三级分类必须填写单价分值
|
||||
if (addLevel === 'level3' && (formData.demandType.unitScore === null || formData.demandType.unitScore === undefined)) {
|
||||
message.error('请输入单价分值')
|
||||
return
|
||||
}
|
||||
@ -2642,16 +2722,16 @@ const saveDemandType = async () => {
|
||||
// 构建保存数据,确保字段名正确
|
||||
const saveData: Partial<DemandType> = {
|
||||
name: formData.demandType.name,
|
||||
// 大分类不设置单价,子分类才设置
|
||||
unitScore: formData.demandType.isSubType ? formData.demandType.unitScore : 0,
|
||||
estimatedHours: formData.demandType.isSubType ? formData.demandType.estimatedHours : null,
|
||||
// 只有三级分类设置单价
|
||||
unitScore: addLevel === 'level3' ? formData.demandType.unitScore : 0,
|
||||
estimatedHours: addLevel === 'level3' ? formData.demandType.estimatedHours : null,
|
||||
description: formData.demandType.description,
|
||||
sortOrder: formData.demandType.sortOrder ?? 0,
|
||||
isEnabled: formData.demandType.isEnabled ?? 1,
|
||||
// 父级ID(子分类需要设置)
|
||||
parentId: formData.demandType.isSubType ? formData.demandType.parentId : null,
|
||||
// 将部门ID数组转换为逗号分隔的字符串(仅大分类需要设置)
|
||||
allowedDeptIds: !formData.demandType.isSubType && formData.demandType.departmentIds?.length > 0
|
||||
// 父级ID(二级、三级分类需要设置)
|
||||
parentId: (addLevel === 'level2' || addLevel === 'level3') ? formData.demandType.parentId : null,
|
||||
// 将部门ID数组转换为逗号分隔的字符串(仅二级分类需要设置)
|
||||
allowedDeptIds: addLevel === 'level2' && formData.demandType.departmentIds?.length > 0
|
||||
? formData.demandType.departmentIds.join(',')
|
||||
: null
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user