opt: 附件上传/删除

This commit is contained in:
2025-11-21 17:02:48 +08:00
parent 5af4d27532
commit 738deb04df
15 changed files with 376 additions and 36 deletions

View File

@@ -111,5 +111,16 @@ public interface DevFileApi {
*/ */
JSONObject getFileInfoById(String id); JSONObject getFileInfoById(String id);
/**
* 通过文件地址获取文件信息
* @param fileAddress 文件地址
* @return
*/
JSONObject getFileInfoByUrl(String fileAddress); JSONObject getFileInfoByUrl(String fileAddress);
/**
* 通过文件id删除文件
* @param fileId
*/
void deleteById(String fileId);
} }

View File

@@ -91,4 +91,9 @@ public class DevFileApiProvider implements DevFileApi {
.map(JSONUtil::parseObj) .map(JSONUtil::parseObj)
.orElse(new JSONObject()); .orElse(new JSONObject());
} }
@Override
public void deleteById(String fileId) {
devFileService.removeById(fileId);
}
} }

View File

@@ -15,29 +15,29 @@ package org.nl.pmm.modular.projectfile.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import org.nl.common.annotation.CommonLog;
import org.nl.common.pojo.CommonResult;
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
import org.nl.pmm.modular.projectfile.param.*; import org.nl.pmm.modular.projectfile.param.*;
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.nl.common.annotation.CommonLog;
import org.nl.common.pojo.CommonResult;
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List; import java.util.List;
/** /**
* 项目附件表控制器 * 项目附件表控制器
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Tag(name = "项目附件表控制器") @Tag(name = "项目附件表控制器")
@RestController @RestController
@@ -51,7 +51,7 @@ public class ProjectFileController {
* 获取项目附件表分页 * 获取项目附件表分页
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "获取项目附件表分页") @Operation(summary = "获取项目附件表分页")
@SaCheckPermission("/pmm/projectfile/page") @SaCheckPermission("/pmm/projectfile/page")
@@ -64,7 +64,7 @@ public class ProjectFileController {
* 添加项目附件表 * 添加项目附件表
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "添加项目附件表") @Operation(summary = "添加项目附件表")
@CommonLog("添加项目附件表") @CommonLog("添加项目附件表")
@@ -79,7 +79,7 @@ public class ProjectFileController {
* 编辑项目附件表 * 编辑项目附件表
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "编辑项目附件表") @Operation(summary = "编辑项目附件表")
@CommonLog("编辑项目附件表") @CommonLog("编辑项目附件表")
@@ -94,22 +94,23 @@ public class ProjectFileController {
* 删除项目附件表 * 删除项目附件表
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "删除项目附件表") @Operation(summary = "删除项目附件表")
@CommonLog("删除项目附件表") @CommonLog("删除项目附件表")
@SaCheckPermission("/pmm/projectfile/delete") @SaCheckPermission("/pmm/projectfile/delete")
@PostMapping("/pmm/projectfile/delete") @PostMapping("/pmm/projectfile/delete")
public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空") public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
List<ProjectFileIdParam> projectFileIdParamList) { List<ProjectFileIdParam> projectFileIdParamList) {
projectFileService.delete(projectFileIdParamList); projectFileService.delete(projectFileIdParamList);
return CommonResult.ok(); return CommonResult.ok();
} }
/** /**
* 删除项目附件表 * 删除项目附件表
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "保存项目附件表") @Operation(summary = "保存项目附件表")
@CommonLog("保存项目附件表") @CommonLog("保存项目附件表")
@@ -125,7 +126,7 @@ public class ProjectFileController {
* 获取项目附件表详情 * 获取项目附件表详情
* *
* @author liyongde * @author liyongde
* @date 2025/11/17 20:24 * @date 2025/11/17 20:24
*/ */
@Operation(summary = "获取项目附件表详情") @Operation(summary = "获取项目附件表详情")
@SaCheckPermission("/pmm/projectfile/detail") @SaCheckPermission("/pmm/projectfile/detail")
@@ -133,4 +134,19 @@ public class ProjectFileController {
public CommonResult<ProjectFile> detail(@Valid ProjectFileIdParam projectFileIdParam) { public CommonResult<ProjectFile> detail(@Valid ProjectFileIdParam projectFileIdParam) {
return CommonResult.data(projectFileService.detail(projectFileIdParam)); return CommonResult.data(projectFileService.detail(projectFileIdParam));
} }
/**
* 删除项目附件表
*
* @author liyongde
* @date 2025/11/17 20:24
*/
@Operation(summary = "删除项目附件表")
@CommonLog("删除项目附件表")
@SaCheckPermission("/pmm/projectfile/delete-file")
@PostMapping("/pmm/projectfile/delete-file")
public CommonResult<String> deleteFile(@RequestBody ProjectFileParam param) {
projectFileService.deleteAttachment(param);
return CommonResult.ok();
}
} }

View File

@@ -0,0 +1,12 @@
package org.nl.pmm.modular.projectfile.param;
import lombok.Data;
/**
* @Author: lyd
* @Date: 2025/11/21
*/
@Data
public class ProjectFileParam {
private String id;
}

View File

@@ -79,4 +79,6 @@ public interface ProjectFileService extends IService<ProjectFile> {
List<ProjectAttachmentVo> getFileByProjectId(String projectId); List<ProjectAttachmentVo> getFileByProjectId(String projectId);
void bindProjectFile(ProjectFileAddParam param); void bindProjectFile(ProjectFileAddParam param);
void deleteAttachment(ProjectFileParam param);
} }

View File

@@ -17,6 +17,7 @@ import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -28,10 +29,7 @@ import org.nl.dev.api.DevFileApi;
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo; import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
import org.nl.pmm.modular.projectfile.entity.ProjectFile; import org.nl.pmm.modular.projectfile.entity.ProjectFile;
import org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper; import org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper;
import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam; import org.nl.pmm.modular.projectfile.param.*;
import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
import org.nl.pmm.modular.projectfile.service.ProjectFileService; import org.nl.pmm.modular.projectfile.service.ProjectFileService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -111,4 +109,12 @@ public class ProjectFileServiceImpl extends ServiceImpl<ProjectFileMapper, Proje
param.setFileAddress(file.getStr("downloadPath")); param.setFileAddress(file.getStr("downloadPath"));
add(param); add(param);
} }
@Override
public void deleteAttachment(ProjectFileParam param) {
// 删除文件
devFileApi.deleteById(param.getId());
// 删除附件表记录
remove(new LambdaQueryWrapper<ProjectFile>().eq(ProjectFile::getFileId, param.getId()));
}
} }

View File

@@ -16,6 +16,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import org.nl.pmm.modular.projectstage.param.*;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -24,10 +25,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.nl.common.annotation.CommonLog; import org.nl.common.annotation.CommonLog;
import org.nl.common.pojo.CommonResult; import org.nl.common.pojo.CommonResult;
import org.nl.pmm.modular.projectstage.entity.ProjectStage; import org.nl.pmm.modular.projectstage.entity.ProjectStage;
import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam;
import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
import org.nl.pmm.modular.projectstage.service.ProjectStageService; import org.nl.pmm.modular.projectstage.service.ProjectStageService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -91,6 +88,20 @@ public class ProjectStageController {
projectStageService.edit(projectStageEditParam); projectStageService.edit(projectStageEditParam);
return CommonResult.ok(); return CommonResult.ok();
} }
/**
* 编辑项目阶段
*
* @author liyongde
* @date 2025/11/17 20:32
*/
@Operation(summary = "编辑项目阶段")
@CommonLog("编辑项目阶段")
// @SaCheckPermission("/pmm/projectstage/batch-edit")
@PostMapping("/pmm/projectstage/batch-edit")
public CommonResult<String> batchEdit(@RequestBody @Valid List<ProjectStageAddBatchParam> param) {
projectStageService.batchEdit(param);
return CommonResult.ok();
}
/** /**
* 删除项目阶段 * 删除项目阶段

View File

@@ -0,0 +1,31 @@
package org.nl.pmm.modular.projectstage.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @Author: lyd
* @Date: 2025/11/19
*/
@Data
public class ProjectStageAddBatchParam {
/** 阶段名称 */
@Schema(description = "阶段名称")
private String stageName;
/** 阶段序号 */
@Schema(description = "阶段序号")
private String stageSeq;
/** 项目id */
@Schema(description = "项目id")
private String projectId;
/** 阶段id */
@Schema(description = "阶段id")
private String stageId;
/** 是否删除 */
@Schema(description = "是否删除")
private Boolean isDeleted;
}

View File

@@ -15,10 +15,7 @@ package org.nl.pmm.modular.projectstage.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.pmm.modular.projectstage.entity.ProjectStage; import org.nl.pmm.modular.projectstage.entity.ProjectStage;
import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam; import org.nl.pmm.modular.projectstage.param.*;
import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
import java.util.List; import java.util.List;
@@ -84,4 +81,6 @@ public interface ProjectStageService extends IService<ProjectStage> {
* @return * @return
*/ */
List<ProjectStage> getByProjectId(String projectId); List<ProjectStage> getByProjectId(String projectId);
void batchEdit(List<ProjectStageAddBatchParam> param);
} }

View File

@@ -20,6 +20,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.nl.pmm.modular.projectstage.param.*;
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
import org.nl.pmm.modular.stagedetail.service.StageDetailService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.nl.common.enums.CommonSortOrderEnum; import org.nl.common.enums.CommonSortOrderEnum;
@@ -27,13 +31,12 @@ import org.nl.common.exception.CommonException;
import org.nl.common.page.CommonPageRequest; import org.nl.common.page.CommonPageRequest;
import org.nl.pmm.modular.projectstage.entity.ProjectStage; import org.nl.pmm.modular.projectstage.entity.ProjectStage;
import org.nl.pmm.modular.projectstage.mapper.ProjectStageMapper; import org.nl.pmm.modular.projectstage.mapper.ProjectStageMapper;
import org.nl.pmm.modular.projectstage.param.ProjectStageAddParam;
import org.nl.pmm.modular.projectstage.param.ProjectStageEditParam;
import org.nl.pmm.modular.projectstage.param.ProjectStageIdParam;
import org.nl.pmm.modular.projectstage.param.ProjectStagePageParam;
import org.nl.pmm.modular.projectstage.service.ProjectStageService; import org.nl.pmm.modular.projectstage.service.ProjectStageService;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* 项目阶段Service接口实现类 * 项目阶段Service接口实现类
@@ -44,6 +47,9 @@ import java.util.List;
@Service @Service
public class ProjectStageServiceImpl extends ServiceImpl<ProjectStageMapper, ProjectStage> implements ProjectStageService { public class ProjectStageServiceImpl extends ServiceImpl<ProjectStageMapper, ProjectStage> implements ProjectStageService {
@Resource
private StageDetailService stageDetailService;
@Override @Override
public Page<ProjectStage> page(ProjectStagePageParam projectStagePageParam) { public Page<ProjectStage> page(ProjectStagePageParam projectStagePageParam) {
QueryWrapper<ProjectStage> queryWrapper = new QueryWrapper<ProjectStage>().checkSqlInjection(); QueryWrapper<ProjectStage> queryWrapper = new QueryWrapper<ProjectStage>().checkSqlInjection();
@@ -100,4 +106,31 @@ public class ProjectStageServiceImpl extends ServiceImpl<ProjectStageMapper, Pro
.orderByAsc(ProjectStage::getStageSeq); .orderByAsc(ProjectStage::getStageSeq);
return list(lambdaQueryWrapper); return list(lambdaQueryWrapper);
} }
@Override
public void batchEdit(List<ProjectStageAddBatchParam> param) {
// 获取新增的 / 修改的
List<ProjectStageAddBatchParam> addItems = param.stream().filter(p ->
(ObjectUtil.isEmpty(p.getStageId()) && !p.getIsDeleted())
|| (ObjectUtil.isNotEmpty(p.getStageId()) && !p.getIsDeleted())).toList();
if (addItems.size() > 0) {
List<ProjectStage> projectStageList = new ArrayList<>();
for (ProjectStageAddBatchParam addItem : addItems) {
ProjectStage projectStage = BeanUtil.toBean(addItem, ProjectStage.class);
projectStageList.add(projectStage);
}
saveOrUpdateBatch(projectStageList);
}
// 获取删除的
List<ProjectStageAddBatchParam> deletedItems = param.stream().filter(p -> ObjectUtil.isNotEmpty(p.getStageId()) && p.getIsDeleted()).toList();
if (deletedItems.size() > 0) {
List<String> ids = new ArrayList<>();
for (ProjectStageAddBatchParam deletedItem : deletedItems) {
ids.add(deletedItem.getStageId());
}
// 删除明细
stageDetailService.remove(new LambdaQueryWrapper<StageDetail>().in(StageDetail::getStageId, ids));
removeBatchByIds(ids);
}
}
} }

View File

@@ -28,5 +28,9 @@ export default {
// 获取项目附件表详情 // 获取项目附件表详情
bindProjectFile(data) { bindProjectFile(data) {
return request('bindProjectFile', data, 'post') return request('bindProjectFile', data, 'post')
},
// 获取项目附件表详情
deleteFile(data) {
return request('delete-file', data, 'post')
} }
} }

View File

@@ -24,5 +24,9 @@ export default {
// 获取项目阶段详情 // 获取项目阶段详情
projectStageDetail(data) { projectStageDetail(data) {
return request('detail', data, 'get') return request('detail', data, 'get')
},
// 获取项目阶段详情
batchEdit(data) {
return request('batch-edit', data, 'post')
} }
} }

View File

@@ -0,0 +1,153 @@
<template>
<xn-form-container title="编辑项目阶段" :width="700" v-model:open="open" :destroy-on-close="true" @close="onClose">
<div class="stage-form">
<div class="stage-form__header">
<div>阶段名称</div>
<div>阶段序号</div>
<div>操作</div>
</div>
<div v-if="!visibleStages.length" class="stage-form__empty">
<a-empty description="暂无阶段,请新增一行" />
</div>
<div v-for="(stage, index) in visibleStages" :key="stage._key ?? index" class="stage-form__row">
<a-input v-model:value="stage.stageName" placeholder="请输入阶段名称" allow-clear :maxlength="50" />
<a-input-number v-model:value="stage.stageSeq" placeholder="请输入阶段序号" :min="1" style="width: 100%" />
<a-space>
<a-tooltip title="删除当前行以及需求">
<a-button type="link" danger @click="removeRow(stage)">删除</a-button>
</a-tooltip>
</a-space>
</div>
<a-button type="dashed" block @click="addRow"> 新增一行 </a-button>
</div>
<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="projectStageForm">
import projectStageApi from '@/api/pmm/projectStageApi'
import { createVNode } from 'vue'
import { Modal } from 'ant-design-vue'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { cloneDeep } from 'lodash-es'
// 抽屉状态
const open = ref(false)
const emit = defineEmits({ successful: null })
// 表单数据
const formData = ref([])
const projectId = ref('')
const visibleStages = computed(() => formData.value.filter((item) => !item.isDeleted))
const submitLoading = ref(false)
const createEmptyStage = () => ({
stageId: '',
stageName: '',
projectId: projectId,
stageSeq: visibleStages.value.length + 1,
isDeleted: false,
_key: `${Date.now()}-${Math.random()}`
})
// 打开抽屉
const onOpen = (record) => {
if (record.length > 0) {
projectId.value = record[0].projectId
}
open.value = true
const list = Array.isArray(record) ? record : []
formData.value = cloneDeep(list).map((item, index) => ({
stageId: item.stageId ?? '',
stageName: item.stageName ?? '',
projectId: item.projectId ?? '',
stageSeq: item.stageSeq ?? index + 1,
isDeleted: item.isDeleted ?? false,
_key: `${Date.now()}-${index}`
}))
if (!formData.value.length) {
formData.value.push(createEmptyStage())
}
}
// 关闭抽屉
const onClose = () => {
formData.value = []
open.value = false
}
const addRow = () => {
formData.value.push(createEmptyStage())
}
const removeRow = (stage) => {
stage.isDeleted = true
if (!visibleStages.value.length) {
formData.value.push(createEmptyStage())
}
}
const submitStages = () => {
submitLoading.value = true
const submitList = formData.value.map(({ _key, ...rest }) => rest)
projectStageApi
.batchEdit(submitList)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
submitLoading.value = false
})
}
// 验证并提交数据
const onSubmit = () => {
const deletedStageCount = formData.value.filter((item) => item.stageId && item.isDeleted).length
const addedStageCount = formData.value.filter((item) => !item.stageId && !item.isDeleted).length
const content = `将删除${deletedStageCount}个阶段以及该阶段的需求,新增${addedStageCount}个阶段,是否继续?`
Modal.confirm({
title: '提示',
content,
icon: createVNode(ExclamationCircleOutlined),
okText: '确定',
cancelText: '取消',
onOk: () => {
submitStages()
}
})
}
// 抛出函数
defineExpose({
onOpen
})
</script>
<style scoped>
.stage-form {
display: flex;
flex-direction: column;
gap: 12px;
}
.stage-form__header,
.stage-form__row {
display: grid;
grid-template-columns: 1fr 1fr 120px;
gap: 12px;
align-items: center;
}
.stage-form__header {
font-weight: 600;
color: var(--text-color);
}
.stage-form__empty {
padding: 24px 0;
border: 1px dashed var(--border-color-split);
border-radius: 4px;
text-align: center;
}
.stage-form__row {
padding: 12px;
border: 1px solid var(--border-color-split);
border-radius: 4px;
}
</style>

View File

@@ -60,6 +60,7 @@
<span class="hours-label">计划工时</span> <span class="hours-label">计划工时</span>
<span class="hours-value">{{ plannedHours }}</span> <span class="hours-value">{{ plannedHours }}</span>
<a-button type="primary" class="create-stage-btn" @click="formRef.onOpen(projectInfo.projectId)">创建项目阶段</a-button> <a-button type="primary" class="create-stage-btn" @click="formRef.onOpen(projectInfo.projectId)">创建项目阶段</a-button>
<a-button type="primary" class="create-stage-btn" @click="form2Ref.onOpen(stageList)">编辑项目阶段</a-button>
</div> </div>
</a-card> </a-card>
@@ -156,6 +157,14 @@
class="attachment-item" class="attachment-item"
@click="handlePreview(file)" @click="handlePreview(file)"
> >
<a-button
type="text"
size="small"
class="attachment-delete-btn"
@click.stop="handleAttachmentDelete(file)"
>
<CloseOutlined />
</a-button>
<div class="attachment-thumb"> <div class="attachment-thumb">
<img :src="getAttachmentThumbnail(file)" :alt="file.name" /> <img :src="getAttachmentThumbnail(file)" :alt="file.name" />
</div> </div>
@@ -173,6 +182,7 @@
<preview v-else ref="previewRef" @goBack="handlePreviewClose" /> <preview v-else ref="previewRef" @goBack="handlePreviewClose" />
<stage-add-dialog ref="formRef" @successful="fetchDetail"/> <stage-add-dialog ref="formRef" @successful="fetchDetail"/>
<fj-upload-form ref="uploadFormRef" @successful="fetchAttachments"/> <fj-upload-form ref="uploadFormRef" @successful="fetchAttachments"/>
<batch-stage-add-dialog ref="form2Ref" @successful="fetchDetail"/>
</div> </div>
</template> </template>
@@ -180,10 +190,13 @@
import { computed, nextTick, ref, watch } from 'vue' import { computed, nextTick, ref, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import projectApi from '@/api/pmm/projectApi' import projectApi from '@/api/pmm/projectApi'
import { message } from 'ant-design-vue' import projectFileApi from '@/api/pmm/projectFileApi'
import { Modal, message } from 'ant-design-vue'
import StageAddDialog from "@/views/pmm/project/request/stageAddDialog.vue"; import StageAddDialog from "@/views/pmm/project/request/stageAddDialog.vue";
import Preview from '@/views/dev/file/preview.vue' import Preview from '@/views/dev/file/preview.vue'
import FjUploadForm from "@/views/pmm/project/request/fjUploadForm.vue"; import FjUploadForm from "@/views/pmm/project/request/fjUploadForm.vue";
import BatchStageAddDialog from "@/views/pmm/project/request/batchStageAddDialog.vue";
import { CloseOutlined } from '@ant-design/icons-vue'
interface AttachmentItem { interface AttachmentItem {
id: number | string id: number | string
@@ -199,6 +212,7 @@
const stageList = ref<any[]>([]) const stageList = ref<any[]>([])
const plannedHours = ref(0) const plannedHours = ref(0)
const formRef = ref() const formRef = ref()
const form2Ref = ref()
const previewRef = ref() const previewRef = ref()
const uploadFormRef = ref() const uploadFormRef = ref()
const previewVisible = ref(false) const previewVisible = ref(false)
@@ -324,7 +338,7 @@ const attachmentList = ref<AttachmentItem[]>([])
return total return total
} }
// 计算计划工时(所有阶段的总天数 * 24 // 计算计划工时(所有阶段的总天数 * 8
const calculatePlannedHours = (stages: any[]) => { const calculatePlannedHours = (stages: any[]) => {
let total = 0 let total = 0
stages.forEach((stage) => { stages.forEach((stage) => {
@@ -332,7 +346,7 @@ const attachmentList = ref<AttachmentItem[]>([])
total += getStageTotalDays(stage.descriptions) total += getStageTotalDays(stage.descriptions)
} }
}) })
return total * 24 return total * 8
} }
// 获取项目需求明细 // 获取项目需求明细
@@ -512,6 +526,29 @@ watch(projectId, () => {
const handlePreviewClose = () => { const handlePreviewClose = () => {
previewVisible.value = false previewVisible.value = false
} }
const handleAttachmentDelete = (file: AttachmentItem) => {
const param = {
id: file.id
}
Modal.confirm({
title: '确认删除该附件?',
content: `附件:${file.name}`,
okText: '确认',
cancelText: '取消',
onOk: () => {
projectFileApi
.deleteFile(param)
.then(() => {
message.success(`已确认删除:${file.name}`)
fetchAttachments()
})
},
onCancel: () => {
console.log('取消删除附件:', file)
}
})
}
</script> </script>
<style scoped> <style scoped>
@@ -669,6 +706,23 @@ watch(projectId, () => {
position: relative; position: relative;
} }
.attachment-delete-btn {
position: absolute;
right: 4px;
top: 4px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
color: #999;
}
.attachment-delete-btn:hover {
color: #ff4d4f;
}
.attachment-item:hover { .attachment-item:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transform: translateY(-2px); transform: translateY(-2px);

View File

@@ -49,7 +49,6 @@
const onOpen = (record) => { const onOpen = (record) => {
open.value = true open.value = true
if (record) { if (record) {
console.log(record)
formData.value.projectId = record formData.value.projectId = record
} }
} }