feat:树形数据

This commit is contained in:
2026-01-20 16:16:55 +08:00
parent bbb42cd1e6
commit 53a3b96fd3
11 changed files with 581 additions and 27 deletions

View File

@@ -161,7 +161,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
ProjectRequestVo.RequestDescription req = new ProjectRequestVo.RequestDescription();
BeanUtil.copyProperties(detail, req);
req.setDays(CommonTimeFormatUtil.calculateDaysBetween(req.getStartTime(), req.getEndTime()));
if (ObjectUtil.isEmpty(req.getParentDetailId())) {
if ("0".equals(req.getParentDetailId())) {
requestDescriptions.add(req);
}
for (StageDetail stageDetail : details) {

View File

@@ -16,24 +16,22 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.nl.pmm.modular.stagedetail.entity.vo.TreeStageDetailVo;
import org.nl.pmm.modular.stagedetail.param.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.nl.common.annotation.CommonLog;
import org.nl.common.pojo.CommonResult;
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
import org.nl.pmm.modular.stagedetail.service.StageDetailService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
/**
* 项目阶段明细控制器
@@ -120,4 +118,24 @@ public class StageDetailController {
public CommonResult<StageDetail> detail(@Valid StageDetailIdParam stageDetailIdParam) {
return CommonResult.data(stageDetailService.detail(stageDetailIdParam));
}
/**
* 查询基础类型
* @param detailTreeParam
* @return
*/
@GetMapping("/pmm/stagedetail/loadClass")
public CommonResult<List<TreeStageDetailVo>> loadClass(StageDetailTreeParam detailTreeParam) {
return CommonResult.data(stageDetailService.loadClass(detailTreeParam));
}
/**
* 获取当前层级以及所有父类层级
* @param detailTreeParam
* @return
*/
@GetMapping("/pmm/stagedetail/superior")
public CommonResult<List<TreeStageDetailVo>> superior(StageDetailTreeParam detailTreeParam) {
return CommonResult.data(stageDetailService.getSuperior(detailTreeParam));
}
}

View File

@@ -0,0 +1,43 @@
package org.nl.pmm.modular.stagedetail.entity.vo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
*
* @author: lyd
* @date: 2026/1/20
*/
@Data
public class TreeStageDetailVo {
/** 明细id */
private String detailId;
/** 阶段id */
private String stageId;
/** 父明细id */
private String parentDetailId;
/** 序号 */
private String seq;
/** 功能模块 */
private String modelName;
/** 功能明细 */
private String modelDetail;
/** 是否为叶子节点 */
private Boolean isLeaf;
/** 子节点 */
private List<TreeStageDetailVo> children;
}

View File

@@ -14,6 +14,10 @@ package org.nl.pmm.modular.stagedetail.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
import org.nl.pmm.modular.stagedetail.entity.vo.TreeStageDetailVo;
import org.nl.pmm.modular.stagedetail.param.StageDetailTreeParam;
import java.util.List;
/**
* 项目阶段明细Mapper接口
@@ -22,4 +26,8 @@ import org.nl.pmm.modular.stagedetail.entity.StageDetail;
* @date 2025/11/17 20:34
**/
public interface StageDetailMapper extends BaseMapper<StageDetail> {
/**
* 树形加载
*/
List<TreeStageDetailVo> loadClass(StageDetailTreeParam detailTreeParam);
}

View File

@@ -2,4 +2,27 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.nl.pmm.modular.stagedetail.mapper.StageDetailMapper">
<select id="loadClass" parameterType="org.nl.pmm.modular.stagedetail.param.StageDetailTreeParam"
resultType="org.nl.pmm.modular.stagedetail.entity.vo.TreeStageDetailVo">
select
detail_id as detailId,
stage_id as stageId,
parent_detail_id as parentDetailId,
seq,
model_name as modelName,
model_detail as modelDetail,
case
when exists(select 1 from pmm_stage_detail psd where psd.parent_detail_id = pmm_stage_detail.detail_id)
then false else true end as isLeaf
from pmm_stage_detail
where stage_id = #{stageId}
<if test="parentDetailId == null or parentDetailId == ''">
and parent_detail_id is null
</if>
<if test="parentDetailId != null and parentDetailId != ''">
and parent_detail_id = #{parentDetailId}
</if>
order by seq, detail_id
</select>
</mapper>

View File

@@ -12,13 +12,14 @@
*/
package org.nl.pmm.modular.stagedetail.param;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -71,4 +72,36 @@ public class StageDetailAddParam {
@Schema(description = "结束时间")
private Date endTime;
/** 创建时间 */
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 创建人 */
@Schema(description = "创建人")
@TableField(fill = FieldFill.INSERT)
@Trans(type = TransType.RPC, targetClassName = "org.nl.sys.modular.user.entity.SysUser", fields = "name", alias = "createUser", ref = "createUserName")
private String createUser;
/** 创建人名称 */
@Schema(description = "创建人名称")
@TableField(exist = false)
private String createUserName;
/** 更新时间 */
@Schema(description = "更新时间")
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
/** 更新人 */
@Schema(description = "更新人")
@TableField(fill = FieldFill.UPDATE)
@Trans(type = TransType.RPC, targetClassName = "org.nl.sys.modular.user.entity.SysUser", fields = "name", alias = "updateUser", ref = "updateUserName")
private String updateUser;
/** 更新人名称 */
@Schema(description = "更新人名称")
@TableField(exist = false)
private String updateUserName;
}

View File

@@ -0,0 +1,21 @@
package org.nl.pmm.modular.stagedetail.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
*
* @author: lyd
* @date: 2026/1/20
*/
@Data
public class StageDetailTreeParam {
/** 阶段id */
@Schema(description = "阶段id")
private String stageId;
/** 父明细id */
@Schema(description = "父明细id")
private String parentDetailId;
}

View File

@@ -15,10 +15,8 @@ package org.nl.pmm.modular.stagedetail.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
import org.nl.pmm.modular.stagedetail.entity.vo.TreeStageDetailVo;
import org.nl.pmm.modular.stagedetail.param.*;
import java.util.List;
@@ -79,4 +77,13 @@ public interface StageDetailService extends IService<StageDetail> {
StageDetail queryEntity(String id);
List<StageDetail> getByStageId(String stageId);
/**
* 获取当前父类的所有下一子集数据
* @param detailTreeParam
* @return
*/
List<TreeStageDetailVo> loadClass(StageDetailTreeParam detailTreeParam);
List<TreeStageDetailVo> getSuperior(StageDetailTreeParam detailTreeParam);
}

View File

@@ -20,6 +20,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.nl.pmm.modular.stagedetail.entity.vo.TreeStageDetailVo;
import org.nl.pmm.modular.stagedetail.param.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.nl.common.enums.CommonSortOrderEnum;
@@ -27,10 +29,6 @@ import org.nl.common.exception.CommonException;
import org.nl.common.page.CommonPageRequest;
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
import org.nl.pmm.modular.stagedetail.mapper.StageDetailMapper;
import org.nl.pmm.modular.stagedetail.param.StageDetailAddParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailEditParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailIdParam;
import org.nl.pmm.modular.stagedetail.param.StageDetailPageParam;
import org.nl.pmm.modular.stagedetail.service.StageDetailService;
import java.util.List;
@@ -100,4 +98,21 @@ public class StageDetailServiceImpl extends ServiceImpl<StageDetailMapper, Stage
.orderByAsc(StageDetail::getSeq);
return list(lam);
}
@Override
public List<TreeStageDetailVo> loadClass(StageDetailTreeParam detailTreeParam) {
return this.baseMapper.loadClass(detailTreeParam);
}
/**
* 获取当前对应父节点的所有子节点,以及所有的父节点数据
* <p>例当前是顶层则返回所有当前stageId的顶层parentDetailId
* 如果当前是第三层则获取当前节点的所有同父节点的数据以及所有第二层和所有第一层并且保持上下级关系子节点放到children字段中。
* @param detailTreeParam
* @return
*/
@Override
public List<TreeStageDetailVo> getSuperior(StageDetailTreeParam detailTreeParam) {
return List.of();
}
}

View File

@@ -0,0 +1,312 @@
<template>
<xn-form-container
:title="formData.detailId ? '编辑项目需求' : '增加项目需求'"
:width="700"
v-model:open="open"
:destroy-on-close="true"
@close="onClose"
>
<a-form
ref="formRef"
class="request-form"
:model="formData"
:rules="formRules"
layout="horizontal"
:label-col="{ flex: '96px' }"
:wrapper-col="{ flex: 1 }"
:label-align="'right'"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="功能模块:" name="modelName">
<a-input v-model:value="formData.modelName" placeholder="请输入功能模块" allow-clear :maxlength="50" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="功能明细:" name="modelDetail">
<a-input v-model:value="formData.modelDetail" placeholder="请输入功能明细" allow-clear :maxlength="50" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item label="描述:" name="modelDescription">
<a-textarea
v-model:value="formData.modelDescription"
placeholder="请输入描述"
:auto-size="{ minRows: 3, maxRows: 6 }"
:maxlength="500"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="优先级:" name="priority">
<a-select v-model:value="formData.priority" placeholder="请选择优先级" allow-clear>
<a-select-option v-for="item in priorityOptions" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="计划天数:" name="days">
<a-input-number v-model:value="formData.days" :min="1" :precision="0" style="width: 100%" placeholder="请输入计划天数" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="序号:" name="seq">
<a-input-number v-model:value="formData.seq" :min="1" :precision="0" style="width: 100%" placeholder="请输入序号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="计划开始:" name="startTime">
<a-date-picker v-model:value="formData.startTime" value-format="YYYY-MM-DD" style="width: 100%" placeholder="请选择开始日期" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="计划结束:" name="endTime">
<a-date-picker v-model:value="formData.endTime" value-format="YYYY-MM-DD" style="width: 100%" placeholder="自动计算" disabled />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="顶级类目:" name="isTop">
<a-radio-group v-model:value="formData.isTop" @change="onTopChange">
<a-radio :value="true"></a-radio>
<a-radio :value="false"></a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<a-col :span="12" v-if="formData.isTop === false">
<a-form-item label="上级类目:" name="parentDetailId">
<a-tree-select
v-model:value="formData.parentDetailId"
:tree-data="parentTreeData"
placeholder="请选择上级类目"
allow-clear
tree-default-expand-all
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item label="备注:" name="remark">
<a-textarea
v-model:value="formData.remark"
placeholder="请输入备注"
:auto-size="{ minRows: 2, maxRows: 4 }"
:maxlength="200"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
</template>
</xn-form-container>
</template>
<script setup name="addRequestDialog">
import dayjs from 'dayjs'
import { cloneDeep } from 'lodash-es'
import { required } from '@/utils/formRules'
import projectApi from '@/api/pmm/projectApi'
import stageDetailApi from '@/api/pmm/stageDetailApi'
const open = ref(false)
const emit = defineEmits({ successful: null })
const formRef = ref()
const submitLoading = ref(false)
const parentTreeData = ref([])
const priorityOptions = [
{ value: 'P0', label: 'P0紧急' },
{ value: 'P1', label: 'P1' },
{ value: 'P2', label: 'P2' },
{ value: 'P3', label: 'P3' }
]
const defaultForm = () => ({
detailId: '',
projectId: '',
stageId: '',
modelName: '',
modelDetail: '',
modelDescription: '',
seq: 99,
priority: undefined,
remark: '',
days: undefined,
startTime: '',
endTime: '',
isTop: true,
parentDetailId: 0
})
const formData = ref(defaultForm())
const formRules = {
modelDetail: [required('请输入功能明细')],
modelDescription: [required('请输入描述')],
priority: [required('请选择优先级')],
seq: [required('请输入序号')],
days: [required('请输入计划天数')],
startTime: [required('请选择计划开始时间')],
parentDetailId: [
{
validator: async (_rule, value) => {
if (formData.value.isTop === false && !value) {
return Promise.reject(new Error('请选择上级类目'))
}
return Promise.resolve()
},
trigger: 'change'
}
]
}
const buildTree = (nodes) => {
const list = Array.isArray(nodes) ? nodes : []
const byId = new Map()
const roots = []
list.forEach((item) => {
const id = item?.detailId
if (!id) return
const titleParts = []
if (item.modelName) titleParts.push(item.modelName)
if (item.modelDetail) titleParts.push(item.modelDetail)
const title = titleParts.length ? titleParts.join(' - ') : String(id)
byId.set(id, { title, value: id, key: id, children: [], raw: item })
})
byId.forEach((node) => {
const parentId = node.raw?.parentDetailId
if (parentId && byId.has(parentId)) {
byId.get(parentId).children.push(node)
} else {
roots.push(node)
}
})
return roots
}
const loadParentTree = async (projectId) => {
if (!projectId) {
parentTreeData.value = []
return
}
const data = await projectApi.requestDetail({ projectId })
const stages = Array.isArray(data) ? data : []
const flat = stages.flatMap((s) => (Array.isArray(s?.descriptions) ? s.descriptions : []))
parentTreeData.value = buildTree(flat)
}
const recomputeEndTime = () => {
const { startTime, days } = formData.value
if (!startTime || !days) {
formData.value.endTime = ''
return
}
formData.value.endTime = dayjs(startTime).add(Number(days), 'day').format('YYYY-MM-DD')
}
watch(
() => [formData.value.startTime, formData.value.days],
() => {
recomputeEndTime()
}
)
const onTopChange = () => {
if (formData.value.isTop === true) {
formData.value.parentDetailId = 0
}
}
// 打开
const onOpen = async (payload = {}) => {
open.value = true
formData.value = Object.assign(defaultForm(), cloneDeep(payload || {}))
if (typeof formData.value.isTop !== 'boolean') {
formData.value.isTop = !formData.value.parentDetailId
}
if (formData.value.isTop) {
formData.value.parentDetailId = 0
}
await loadParentTree(formData.value.projectId)
recomputeEndTime()
}
// 关闭
const onClose = () => {
formRef.value?.resetFields?.()
formData.value = defaultForm()
parentTreeData.value = []
open.value = false
}
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
submitLoading.value = true
const formDataParam = cloneDeep(formData.value)
// 顶级 parentDetailId 置 0
if (formDataParam.isTop) {
formDataParam.parentDetailId = 0
}
delete formDataParam.isTop
// 后端期望可解析的 Date 格式,补全时分秒
const formatDateTime = (val) => (val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '')
formDataParam.startTime = formatDateTime(formDataParam.startTime)
formDataParam.endTime = formatDateTime(formDataParam.endTime)
stageDetailApi
.stageDetailSubmitForm(formDataParam, formDataParam.detailId)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
defineExpose({
onOpen
})
</script>
<style scoped>
/* 让横向表单在各种控件(含 textarea下都保持统一对齐 */
.request-form :deep(.ant-form-item-label > label) {
height: 32px;
line-height: 32px;
}
.request-form :deep(.ant-form-item-control) {
line-height: 32px;
}
.request-form :deep(.ant-form-item-control-input) {
min-height: 32px;
}
</style>

View File

@@ -75,7 +75,7 @@
<template #title>
<div class="stage-title-wrapper">
<span>{{ getStageNumber(stage.stageSeq) }}阶段 {{ stage.stageName }}</span>
<a-button type="primary" class="create-requirement-btn">创建需求</a-button>
<a-button type="primary" class="create-requirement-btn" @click="openCreateRequest(stage)">创建需求</a-button>
</div>
</template>
<a-table
@@ -116,16 +116,16 @@
{{ record.days }}
</template>
<template v-if="column.dataIndex === 'action'">
<a-button type="link" size="small" class="action-btn">
<a-button type="link" size="small" class="action-btn" @click="openAddSibling(record)">
新增同级
</a-button>
<a-button type="link" size="small" class="action-btn">
<a-button type="link" size="small" class="action-btn" @click="openAddChild(record)">
新增子级
</a-button>
<a-button type="link" size="small" class="action-btn">
<a-button type="link" size="small" class="action-btn" @click="openEditRequest(record)">
修改需求
</a-button>
<a-button type="link" size="small" class="action-btn">
<a-button type="link" size="small" class="action-btn" @click="deleteRequest(record)">
删除需求
</a-button>
</template>
@@ -183,6 +183,7 @@
<stage-add-dialog ref="formRef" @successful="fetchDetail"/>
<fj-upload-form ref="uploadFormRef" @successful="fetchAttachments"/>
<batch-stage-add-dialog ref="form2Ref" @successful="fetchDetail"/>
<add-request-dialog ref="requestFormRef" @successful="fetchDetail" />
</div>
</template>
@@ -191,8 +192,10 @@
import { useRoute } from 'vue-router'
import projectApi from '@/api/pmm/projectApi'
import projectFileApi from '@/api/pmm/projectFileApi'
import stageDetailApi from '@/api/pmm/stageDetailApi'
import { Modal, message } from 'ant-design-vue'
import StageAddDialog from "@/views/pmm/project/request/stageAddDialog.vue";
import AddRequestDialog from "@/views/pmm/project/request/addRequestDialog.vue";
import Preview from '@/views/dev/file/preview.vue'
import FjUploadForm from "@/views/pmm/project/request/fjUploadForm.vue";
import BatchStageAddDialog from "@/views/pmm/project/request/batchStageAddDialog.vue";
@@ -213,6 +216,7 @@
const plannedHours = ref(0)
const formRef = ref()
const form2Ref = ref()
const requestFormRef = ref()
const previewRef = ref()
const uploadFormRef = ref()
const previewVisible = ref(false)
@@ -360,8 +364,15 @@ const attachmentList = ref<AttachmentItem[]>([])
.requestDetail({ projectId: projectId.value })
.then((data) => {
if (Array.isArray(data)) {
stageList.value = data
plannedHours.value = calculatePlannedHours(data)
const normalized = data.map((stage: any) => {
const descriptions = Array.isArray(stage?.descriptions) ? stage.descriptions : []
return {
...stage,
descriptions: descriptions.map((d: any) => ({ ...d, stageId: stage.stageId }))
}
})
stageList.value = normalized
plannedHours.value = calculatePlannedHours(normalized)
} else {
stageList.value = []
message.warning('数据格式错误')
@@ -549,6 +560,69 @@ watch(projectId, () => {
}
})
}
// 创建需求(不携带 parentDetailId
const openCreateRequest = (stage: any) => {
if (!projectId.value) return
requestFormRef.value?.onOpen?.({
projectId: projectId.value,
stageId: stage?.stageId,
isTop: true
})
}
// 新增同级:带过去 parentDetailId等同 record.parentDetailId
const openAddSibling = (record: any) => {
if (!projectId.value) return
const parentDetailId = record?.parentDetailId || ''
requestFormRef.value?.onOpen?.({
projectId: projectId.value,
stageId: record?.stageId,
isTop: !parentDetailId,
parentDetailId
})
}
// 新增子级:把自己的 detailId 作为 parentDetailId
const openAddChild = (record: any) => {
if (!projectId.value) return
requestFormRef.value?.onOpen?.({
projectId: projectId.value,
stageId: record?.stageId,
isTop: false,
parentDetailId: record?.detailId
})
}
// 编辑
const openEditRequest = (record: any) => {
if (!projectId.value) return
const parentDetailId = record?.parentDetailId || ''
requestFormRef.value?.onOpen?.({
...record,
projectId: projectId.value,
isTop: !parentDetailId,
parentDetailId
})
}
// 删除
const deleteRequest = (record: any) => {
if (!record?.detailId) return
Modal.confirm({
title: '确认删除该需求?',
content: `需求:${record?.modelDetail || record?.modelName || record?.detailId}`,
okText: '确认',
cancelText: '取消',
onOk: () => {
const params = [{ detailId: record.detailId }]
return stageDetailApi.stageDetailDelete(params).then(() => {
message.success('删除成功')
fetchDetail()
})
}
})
}
</script>
<style scoped>