refactor(doctor/submissions): 合并到 biz_project_plan + 删除 biz_submission 死代码
- /doctor/submissions 改走 biz_project_plan,与 /manager/plans 字段/组件/状态字典完全对齐 - BizProjectPlan 加 submitter_id (BIGINT) + submitter_name (LEFT JOIN 查询字段) - mapper 改 LEFT JOIN biz_person + sys_user,COALESCE(bp.name, u.user_name) 兜底 - biz_person.user_id 加 UNIQUE 索引 (1:1 LEFT JOIN 无笛卡尔积) - controller 加 doctor 角色自动过滤: list 按 submitter_id, add/edit 强制 submitter_id - 删除 7 个 BizSubmission 死代码 (Controller/Service/Mapper/Domain/XML/Enum) + 1 utils - 路由参数 :subId → :planId (路径名 submission 保留产品文案) - 新增 migration_submission_to_projectPlan_2026_08_18.sql
This commit is contained in:
+23
@@ -8,11 +8,16 @@ import com.ruoyi.common.core.controller.BaseController;
|
|||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.business.domain.BizProjectPlan;
|
import com.ruoyi.business.domain.BizProjectPlan;
|
||||||
import com.ruoyi.business.service.IBizProjectPlanService;
|
import com.ruoyi.business.service.IBizProjectPlanService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目策划方案Controller
|
* 项目策划方案Controller
|
||||||
|
*
|
||||||
|
* 角色权限:
|
||||||
|
* - doctor: 只看自己投的稿 (submitter_id = 当前用户); 新建/编辑强制 submitter_id 写自己, status 默认 '0'
|
||||||
|
* - manager / leader / sponsor / admin: 全部可见, 不强制 submitter
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/business/projectPlan")
|
@RequestMapping("/business/projectPlan")
|
||||||
@@ -23,6 +28,11 @@ public class BizProjectPlanController extends BaseController
|
|||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public TableDataInfo list(BizProjectPlan BizProjectPlan)
|
public TableDataInfo list(BizProjectPlan BizProjectPlan)
|
||||||
{
|
{
|
||||||
|
// 医生角色: 后端兜底只查自己投的稿
|
||||||
|
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||||
|
if ("doctor".equals(roleType)) {
|
||||||
|
BizProjectPlan.setSubmitterId(SecurityUtils.getUserId());
|
||||||
|
}
|
||||||
startPage();
|
startPage();
|
||||||
List<BizProjectPlan> list = BizProjectPlanService.selectList(BizProjectPlan);
|
List<BizProjectPlan> list = BizProjectPlanService.selectList(BizProjectPlan);
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
@@ -36,12 +46,25 @@ public class BizProjectPlanController extends BaseController
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public AjaxResult add(@RequestBody BizProjectPlan BizProjectPlan)
|
public AjaxResult add(@RequestBody BizProjectPlan BizProjectPlan)
|
||||||
{
|
{
|
||||||
|
// 医生角色: 强制 submitter_id 写自己 + 状态兜底 '0' (未提交), 防止绕过
|
||||||
|
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||||
|
if ("doctor".equals(roleType)) {
|
||||||
|
BizProjectPlan.setSubmitterId(SecurityUtils.getUserId());
|
||||||
|
if (BizProjectPlan.getStatus() == null || BizProjectPlan.getStatus().isEmpty()) {
|
||||||
|
BizProjectPlan.setStatus("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
return toAjax(BizProjectPlanService.insert(BizProjectPlan));
|
return toAjax(BizProjectPlanService.insert(BizProjectPlan));
|
||||||
}
|
}
|
||||||
@Log(title = "项目策划方案", businessType = BusinessType.UPDATE)
|
@Log(title = "项目策划方案", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public AjaxResult edit(@RequestBody BizProjectPlan BizProjectPlan)
|
public AjaxResult edit(@RequestBody BizProjectPlan BizProjectPlan)
|
||||||
{
|
{
|
||||||
|
// 医生角色: 修改时也强制覆盖 submitter_id, 防止越权篡改
|
||||||
|
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||||
|
if ("doctor".equals(roleType)) {
|
||||||
|
BizProjectPlan.setSubmitterId(SecurityUtils.getUserId());
|
||||||
|
}
|
||||||
return toAjax(BizProjectPlanService.updateByPrimaryKey(BizProjectPlan));
|
return toAjax(BizProjectPlanService.updateByPrimaryKey(BizProjectPlan));
|
||||||
}
|
}
|
||||||
@Log(title = "项目策划方案", businessType = BusinessType.DELETE)
|
@Log(title = "项目策划方案", businessType = BusinessType.DELETE)
|
||||||
|
|||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
package com.ruoyi.business.controller;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
|
||||||
import com.ruoyi.business.domain.BizSubmission;
|
|
||||||
import com.ruoyi.business.enums.SubmissionStatus;
|
|
||||||
import com.ruoyi.business.service.IBizSubmissionService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 投稿Controller
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/business/submission")
|
|
||||||
public class BizSubmissionController extends BaseController
|
|
||||||
{
|
|
||||||
@Autowired
|
|
||||||
private IBizSubmissionService bizSubmissionService;
|
|
||||||
@GetMapping("/list")
|
|
||||||
public TableDataInfo list(BizSubmission bizSubmission)
|
|
||||||
{
|
|
||||||
// 后端兜底: 只查当前登录用户提交的; admin 跳过此限制
|
|
||||||
if (!SecurityUtils.isAdmin()) {
|
|
||||||
bizSubmission.setSubmitterId(SecurityUtils.getUserId());
|
|
||||||
}
|
|
||||||
startPage();
|
|
||||||
List<BizSubmission> list = bizSubmissionService.selectList(bizSubmission);
|
|
||||||
return getDataTable(list);
|
|
||||||
}
|
|
||||||
@GetMapping("/{submissionId}")
|
|
||||||
public AjaxResult getInfo(@PathVariable("submissionId") String submissionId)
|
|
||||||
{
|
|
||||||
return success(bizSubmissionService.getById(submissionId));
|
|
||||||
}
|
|
||||||
@Log(title = "投稿", businessType = BusinessType.INSERT)
|
|
||||||
@PostMapping
|
|
||||||
public AjaxResult add(@RequestBody BizSubmission bizSubmission)
|
|
||||||
{
|
|
||||||
// 后端兜底: 强制写入当前登录用户, 防止前端漏传/绕过导致列表过滤查不到
|
|
||||||
bizSubmission.setSubmitterId(SecurityUtils.getUserId());
|
|
||||||
bizSubmission.setSubmitterName(SecurityUtils.getUsername());
|
|
||||||
bizSubmission.setCreateBy(getUsername());
|
|
||||||
bizSubmission.setUpdateBy(getUsername());
|
|
||||||
// 状态: 新建默认 DRAFT(待提交); 校验必须是英文 code 之一
|
|
||||||
if (bizSubmission.getStatus() == null || bizSubmission.getStatus().isEmpty()) {
|
|
||||||
bizSubmission.setStatus(SubmissionStatus.DRAFT.getCode());
|
|
||||||
} else if (!SubmissionStatus.isValid(bizSubmission.getStatus())) {
|
|
||||||
return AjaxResult.error("状态值非法: " + bizSubmission.getStatus());
|
|
||||||
}
|
|
||||||
return toAjax(bizSubmissionService.insert(bizSubmission));
|
|
||||||
}
|
|
||||||
@Log(title = "投稿", businessType = BusinessType.UPDATE)
|
|
||||||
@PutMapping
|
|
||||||
public AjaxResult edit(@RequestBody BizSubmission bizSubmission)
|
|
||||||
{
|
|
||||||
// 修改时也强制覆盖 submitterId/submitterName, 防止越权篡改为他人
|
|
||||||
bizSubmission.setSubmitterId(SecurityUtils.getUserId());
|
|
||||||
bizSubmission.setSubmitterName(SecurityUtils.getUsername());
|
|
||||||
bizSubmission.setUpdateBy(getUsername());
|
|
||||||
// 状态校验 (英文 code)
|
|
||||||
if (bizSubmission.getStatus() != null && !bizSubmission.getStatus().isEmpty()
|
|
||||||
&& !SubmissionStatus.isValid(bizSubmission.getStatus())) {
|
|
||||||
return AjaxResult.error("状态值非法: " + bizSubmission.getStatus());
|
|
||||||
}
|
|
||||||
return toAjax(bizSubmissionService.updateByPrimaryKey(bizSubmission));
|
|
||||||
}
|
|
||||||
@Log(title = "投稿", businessType = BusinessType.DELETE)
|
|
||||||
@DeleteMapping("/{ids}")
|
|
||||||
public AjaxResult remove(@PathVariable String[] ids)
|
|
||||||
{
|
|
||||||
return toAjax(bizSubmissionService.deleteByPrimaryKeys(ids));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,10 @@ public class BizProjectPlan extends BaseEntity {
|
|||||||
/** plan_direction */
|
/** plan_direction */
|
||||||
@Excel(name = "plan_direction")
|
@Excel(name = "plan_direction")
|
||||||
private String planDirection;
|
private String planDirection;
|
||||||
|
/** 项目方向-专项计划ID (关联 biz_special_plan.id) */
|
||||||
|
private Long planDirectionId;
|
||||||
|
/** 项目方向-专项计划名称 (LEFT JOIN biz_special_plan.title, 列表展示用) */
|
||||||
|
private String planDirectionTitle;
|
||||||
/** plan_category */
|
/** plan_category */
|
||||||
@Excel(name = "plan_category")
|
@Excel(name = "plan_category")
|
||||||
private String planCategory;
|
private String planCategory;
|
||||||
@@ -61,12 +65,20 @@ public class BizProjectPlan extends BaseEntity {
|
|||||||
private String auditBy;
|
private String auditBy;
|
||||||
/** 审核时间 */
|
/** 审核时间 */
|
||||||
private String auditTime;
|
private String auditTime;
|
||||||
|
/** 投稿人用户ID (医生侧投稿归属, 经理/admin 录入为 NULL) */
|
||||||
|
private Long submitterId;
|
||||||
|
/** 投稿人姓名 (LEFT JOIN biz_person.name 优先, sys_user.user_name 兜底; biz_person.user_id 已 UNIQUE, 1:1 无笛卡尔积) */
|
||||||
|
private String submitterName;
|
||||||
public String getPlanId() { return planId; }
|
public String getPlanId() { return planId; }
|
||||||
public void setPlanId(String planId) { this.planId = planId; }
|
public void setPlanId(String planId) { this.planId = planId; }
|
||||||
public String getPlanName() { return planName; }
|
public String getPlanName() { return planName; }
|
||||||
public void setPlanName(String planName) { this.planName = planName; }
|
public void setPlanName(String planName) { this.planName = planName; }
|
||||||
public String getPlanDirection() { return planDirection; }
|
public String getPlanDirection() { return planDirection; }
|
||||||
public void setPlanDirection(String planDirection) { this.planDirection = planDirection; }
|
public void setPlanDirection(String planDirection) { this.planDirection = planDirection; }
|
||||||
|
public Long getPlanDirectionId() { return planDirectionId; }
|
||||||
|
public void setPlanDirectionId(Long planDirectionId) { this.planDirectionId = planDirectionId; }
|
||||||
|
public String getPlanDirectionTitle() { return planDirectionTitle; }
|
||||||
|
public void setPlanDirectionTitle(String planDirectionTitle) { this.planDirectionTitle = planDirectionTitle; }
|
||||||
public String getPlanCategory() { return planCategory; }
|
public String getPlanCategory() { return planCategory; }
|
||||||
public void setPlanCategory(String planCategory) { this.planCategory = planCategory; }
|
public void setPlanCategory(String planCategory) { this.planCategory = planCategory; }
|
||||||
public String getProjectForm() { return projectForm; }
|
public String getProjectForm() { return projectForm; }
|
||||||
@@ -91,4 +103,8 @@ public class BizProjectPlan extends BaseEntity {
|
|||||||
public void setUpdateBy(String updateBy) { this.updateBy = updateBy; }
|
public void setUpdateBy(String updateBy) { this.updateBy = updateBy; }
|
||||||
public Date getUpdateTime() { return updateTime; }
|
public Date getUpdateTime() { return updateTime; }
|
||||||
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
|
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
|
||||||
|
public Long getSubmitterId() { return submitterId; }
|
||||||
|
public void setSubmitterId(Long submitterId) { this.submitterId = submitterId; }
|
||||||
|
public String getSubmitterName() { return submitterName; }
|
||||||
|
public void setSubmitterName(String submitterName) { this.submitterName = submitterName; }
|
||||||
}
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package com.ruoyi.business.domain;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Date;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
|
||||||
import com.ruoyi.common.core.domain.BaseEntity;
|
|
||||||
|
|
||||||
/** 投稿对象 BizSubmission */
|
|
||||||
public class BizSubmission extends BaseEntity {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
/** subId */
|
|
||||||
private String subId;
|
|
||||||
/** title */
|
|
||||||
@Excel(name = "title")
|
|
||||||
private String title;
|
|
||||||
/** direction */
|
|
||||||
@Excel(name = "direction")
|
|
||||||
private String direction;
|
|
||||||
/** project_form */
|
|
||||||
@Excel(name = "project_form")
|
|
||||||
private String projectForm;
|
|
||||||
/** design_file_url */
|
|
||||||
@Excel(name = "design_file_url")
|
|
||||||
private String designFileUrl;
|
|
||||||
/** status */
|
|
||||||
@Excel(name = "status")
|
|
||||||
private String status;
|
|
||||||
/** remark */
|
|
||||||
@Excel(name = "remark")
|
|
||||||
private String remark;
|
|
||||||
/** create_by */
|
|
||||||
@Excel(name = "create_by")
|
|
||||||
private String createBy;
|
|
||||||
/** create_time */
|
|
||||||
@Excel(name = "create_time")
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
||||||
private Date createTime;
|
|
||||||
/** update_by */
|
|
||||||
@Excel(name = "update_by")
|
|
||||||
private String updateBy;
|
|
||||||
/** update_time */
|
|
||||||
@Excel(name = "update_time")
|
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
||||||
private Date updateTime;
|
|
||||||
/** 投稿人用户ID */
|
|
||||||
private Long submitterId;
|
|
||||||
/** 投稿人姓名 */
|
|
||||||
private String submitterName;
|
|
||||||
/** 项目类别 学术会议类/专项科研类/调研征集类/慈善帮扶类/标准制定类/患者援助类/专业培训类 */
|
|
||||||
private String projectCategory;
|
|
||||||
/** 审核意见 */
|
|
||||||
private String auditOpinion;
|
|
||||||
/** 审核人 */
|
|
||||||
private String auditBy;
|
|
||||||
/** 审核时间 */
|
|
||||||
private String auditTime;
|
|
||||||
public String getSubId() { return subId; }
|
|
||||||
public void setSubId(String subId) { this.subId = subId; }
|
|
||||||
public String getTitle() { return title; }
|
|
||||||
public void setTitle(String title) { this.title = title; }
|
|
||||||
public String getDirection() { return direction; }
|
|
||||||
public void setDirection(String direction) { this.direction = direction; }
|
|
||||||
public String getProjectForm() { return projectForm; }
|
|
||||||
public void setProjectForm(String projectForm) { this.projectForm = projectForm; }
|
|
||||||
public String getProjectCategory() { return projectCategory; }
|
|
||||||
public void setProjectCategory(String projectCategory) { this.projectCategory = projectCategory; }
|
|
||||||
public String getDesignFileUrl() { return designFileUrl; }
|
|
||||||
public void setDesignFileUrl(String designFileUrl) { this.designFileUrl = designFileUrl; }
|
|
||||||
public String getStatus() { return status; }
|
|
||||||
public void setStatus(String status) { this.status = status; }
|
|
||||||
public String getRemark() { return remark; }
|
|
||||||
public void setRemark(String remark) { this.remark = remark; }
|
|
||||||
public Long getSubmitterId() { return submitterId; }
|
|
||||||
public void setSubmitterId(Long submitterId) { this.submitterId = submitterId; }
|
|
||||||
public String getSubmitterName() { return submitterName; }
|
|
||||||
public void setSubmitterName(String submitterName) { this.submitterName = submitterName; }
|
|
||||||
public String getCreateBy() { return createBy; }
|
|
||||||
public void setCreateBy(String createBy) { this.createBy = createBy; }
|
|
||||||
public Date getCreateTime() { return createTime; }
|
|
||||||
public void setCreateTime(Date createTime) { this.createTime = createTime; }
|
|
||||||
public String getUpdateBy() { return updateBy; }
|
|
||||||
public void setUpdateBy(String updateBy) { this.updateBy = updateBy; }
|
|
||||||
public Date getUpdateTime() { return updateTime; }
|
|
||||||
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.ruoyi.business.enums;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 投稿状态枚举
|
|
||||||
*
|
|
||||||
* 数据库存储英文 code (PENDING/APPROVED/REJECTED/DRAFT), 不存中文也不存数字.
|
|
||||||
* 字典映射集中在这里避免散落. 前端 utils/submissionStatus.js 同步这份字典.
|
|
||||||
*/
|
|
||||||
public enum SubmissionStatus
|
|
||||||
{
|
|
||||||
PENDING("PENDING", "待审核"),
|
|
||||||
APPROVED("APPROVED", "审核通过"),
|
|
||||||
REJECTED("REJECTED", "已退回"),
|
|
||||||
DRAFT("DRAFT", "待提交");
|
|
||||||
|
|
||||||
private final String code;
|
|
||||||
private final String label;
|
|
||||||
|
|
||||||
SubmissionStatus(String code, String label) {
|
|
||||||
this.code = code;
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCode() { return code; }
|
|
||||||
public String getLabel() { return label; }
|
|
||||||
|
|
||||||
private static final Map<String, SubmissionStatus> BY_CODE = new HashMap<>();
|
|
||||||
static {
|
|
||||||
for (SubmissionStatus s : values()) BY_CODE.put(s.code, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SubmissionStatus fromCode(String val) {
|
|
||||||
if (val == null) return null;
|
|
||||||
return BY_CODE.get(val.trim().toUpperCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValid(String val) {
|
|
||||||
return fromCode(val) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
package com.ruoyi.business.mapper;
|
|
||||||
import java.util.List;
|
|
||||||
import com.ruoyi.business.domain.BizSubmission;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 投稿Mapper接口
|
|
||||||
*/
|
|
||||||
public interface BizSubmissionMapper
|
|
||||||
{
|
|
||||||
BizSubmission selectByPrimaryKey(String subId);
|
|
||||||
List<BizSubmission> selectList(BizSubmission entity);
|
|
||||||
int insert(BizSubmission entity);
|
|
||||||
int updateByPrimaryKey(BizSubmission entity);
|
|
||||||
int deleteByPrimaryKey(String subId);
|
|
||||||
int deleteByPrimaryKeys(String[] subIds);
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
package com.ruoyi.business.service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import com.ruoyi.business.domain.BizSubmission;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 投稿Service接口
|
|
||||||
*/
|
|
||||||
public interface IBizSubmissionService
|
|
||||||
{
|
|
||||||
BizSubmission getById(String submissionId);
|
|
||||||
List<BizSubmission> selectList(BizSubmission entity);
|
|
||||||
int insert(BizSubmission entity);
|
|
||||||
int updateByPrimaryKey(BizSubmission entity);
|
|
||||||
int deleteByPrimaryKey(String submissionId);
|
|
||||||
int deleteByPrimaryKeys(String[] submissionId);
|
|
||||||
}
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
package com.ruoyi.business.service.impl;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import com.ruoyi.business.domain.BizSubmission;
|
|
||||||
import com.ruoyi.business.mapper.BizSubmissionMapper;
|
|
||||||
import com.ruoyi.business.service.IBizSubmissionService;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class BizSubmissionServiceImpl implements IBizSubmissionService
|
|
||||||
{
|
|
||||||
@Autowired
|
|
||||||
private BizSubmissionMapper bizSubmissionMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BizSubmission getById(String submissionId)
|
|
||||||
{ return bizSubmissionMapper.selectByPrimaryKey(submissionId); }
|
|
||||||
@Override
|
|
||||||
public List<BizSubmission> selectList(BizSubmission entity)
|
|
||||||
{ return bizSubmissionMapper.selectList(entity); }
|
|
||||||
@Override
|
|
||||||
public int insert(BizSubmission entity) { com.ruoyi.common.utils.id.SnowflakeId.injectIfEmpty(entity, "subId"); return bizSubmissionMapper.insert(entity); }
|
|
||||||
@Override
|
|
||||||
public int updateByPrimaryKey(BizSubmission entity)
|
|
||||||
{ return bizSubmissionMapper.updateByPrimaryKey(entity); }
|
|
||||||
@Override
|
|
||||||
public int deleteByPrimaryKey(String submissionId)
|
|
||||||
{ return bizSubmissionMapper.deleteByPrimaryKey(submissionId); }
|
|
||||||
@Override
|
|
||||||
public int deleteByPrimaryKeys(String[] submissionId)
|
|
||||||
{ return bizSubmissionMapper.deleteByPrimaryKeys(submissionId); }
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
<id property="planId" column="plan_id" />
|
<id property="planId" column="plan_id" />
|
||||||
<result property="planName" column="plan_name" />
|
<result property="planName" column="plan_name" />
|
||||||
<result property="planDirection" column="plan_direction" />
|
<result property="planDirection" column="plan_direction" />
|
||||||
|
<result property="planDirectionId" column="plan_direction_id" />
|
||||||
|
<result property="planDirectionTitle" column="plan_direction_title" />
|
||||||
<result property="planCategory" column="plan_category" />
|
<result property="planCategory" column="plan_category" />
|
||||||
<result property="projectForm" column="project_form" />
|
<result property="projectForm" column="project_form" />
|
||||||
<result property="designFileUrl" column="design_file_url" />
|
<result property="designFileUrl" column="design_file_url" />
|
||||||
@@ -12,38 +14,59 @@
|
|||||||
<result property="isSettled" column="is_settled" />
|
<result property="isSettled" column="is_settled" />
|
||||||
<result property="projectNo" column="project_no" />
|
<result property="projectNo" column="project_no" />
|
||||||
<result property="remark" column="remark" />
|
<result property="remark" column="remark" />
|
||||||
|
<result property="submitterId" column="submitter_id" />
|
||||||
|
<result property="submitterName" column="submitter_name" />
|
||||||
<result property="createBy" column="create_by" />
|
<result property="createBy" column="create_by" />
|
||||||
<result property="createTime" column="create_time" />
|
<result property="createTime" column="create_time" />
|
||||||
<result property="updateBy" column="update_by" />
|
<result property="updateBy" column="update_by" />
|
||||||
<result property="updateTime" column="update_time" />
|
<result property="updateTime" column="update_time" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<sql id="selectFields">
|
<sql id="selectFields">
|
||||||
select plan_id, plan_name, plan_direction, plan_category, project_form, design_file_url, status, is_settled, project_no, remark, create_by, create_time, update_by, update_time
|
select p.plan_id, p.plan_name, p.plan_direction, p.plan_direction_id,
|
||||||
from biz_project_plan
|
s.title as plan_direction_title,
|
||||||
|
p.plan_category, p.project_form, p.design_file_url, p.status, p.is_settled,
|
||||||
|
p.project_no, p.remark, p.submitter_id,
|
||||||
|
COALESCE(bp.name, u.user_name) as submitter_name,
|
||||||
|
p.create_by, p.create_time, p.update_by, p.update_time
|
||||||
|
from biz_project_plan p
|
||||||
|
left join biz_special_plan s on s.id = p.plan_direction_id
|
||||||
|
left join sys_user u on u.user_id = p.submitter_id
|
||||||
|
left join biz_person bp on bp.user_id = u.user_id
|
||||||
</sql>
|
</sql>
|
||||||
<select id="selectByPrimaryKey" resultMap="BizProjectPlanResult" parameterType="String">
|
<select id="selectByPrimaryKey" resultMap="BizProjectPlanResult" parameterType="String">
|
||||||
<include refid="selectFields"/>
|
<include refid="selectFields"/>
|
||||||
where plan_id = #{planId}
|
where p.plan_id = #{planId}
|
||||||
</select>
|
</select>
|
||||||
<select id="selectList" resultMap="BizProjectPlanResult" parameterType="BizProjectPlan">
|
<select id="selectList" resultMap="BizProjectPlanResult" parameterType="BizProjectPlan">
|
||||||
<include refid="selectFields"/>
|
<include refid="selectFields"/>
|
||||||
<where>
|
<where>
|
||||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
<if test="planDirectionId != null"> and p.plan_direction_id = #{planDirectionId}</if>
|
||||||
<if test="remark != null and remark != ''"> and remark = #{remark}</if>
|
<if test="planCategory != null and planCategory != ''"> and p.plan_category = #{planCategory}</if>
|
||||||
|
<if test="submitterId != null"> and p.submitter_id = #{submitterId}</if>
|
||||||
|
<choose>
|
||||||
|
<!-- 选了具体状态: 等值匹配 -->
|
||||||
|
<when test="status != null and status != ''"> and p.status = #{status}</when>
|
||||||
|
<!-- 未选: 不加 status 过滤, 由前端按角色决定默认值 (经理侧默认查 1/2/3, 医生侧默认查全部含 0) -->
|
||||||
|
</choose>
|
||||||
|
<if test="remark != null and remark != ''"> and p.remark = #{remark}</if>
|
||||||
</where>
|
</where>
|
||||||
order by plan_id desc
|
order by p.plan_id desc
|
||||||
</select>
|
</select>
|
||||||
<insert id="insert" parameterType="BizProjectPlan">
|
<insert id="insert" parameterType="BizProjectPlan">
|
||||||
insert into biz_project_plan
|
insert into biz_project_plan
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
<if test="planId != null and planId != ''">plan_id,</if>
|
<if test="planId != null and planId != ''">plan_id,</if>
|
||||||
|
<if test="planDirectionId != null">plan_direction_id,</if>
|
||||||
<if test="status != null">status,</if>
|
<if test="status != null">status,</if>
|
||||||
<if test="remark != null">remark,</if>
|
<if test="remark != null">remark,</if>
|
||||||
|
<if test="submitterId != null">submitter_id,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="planId != null and planId != ''">#{planId},</if>
|
<if test="planId != null and planId != ''">#{planId},</if>
|
||||||
|
<if test="planDirectionId != null">#{planDirectionId},</if>
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="remark != null">#{remark},</if>
|
<if test="remark != null">#{remark},</if>
|
||||||
|
<if test="submitterId != null">#{submitterId},</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
<update id="updateByPrimaryKey" parameterType="BizProjectPlan">
|
<update id="updateByPrimaryKey" parameterType="BizProjectPlan">
|
||||||
@@ -51,6 +74,7 @@
|
|||||||
<trim prefix="SET" suffixOverrides=",">
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
<if test="planName != null and planName != ''">plan_name = #{planName},</if>
|
<if test="planName != null and planName != ''">plan_name = #{planName},</if>
|
||||||
<if test="planDirection != null and planDirection != ''">plan_direction = #{planDirection},</if>
|
<if test="planDirection != null and planDirection != ''">plan_direction = #{planDirection},</if>
|
||||||
|
<if test="planDirectionId != null">plan_direction_id = #{planDirectionId},</if>
|
||||||
<if test="planCategory != null and planCategory != ''">plan_category = #{planCategory},</if>
|
<if test="planCategory != null and planCategory != ''">plan_category = #{planCategory},</if>
|
||||||
<if test="projectForm != null and projectForm != ''">project_form = #{projectForm},</if>
|
<if test="projectForm != null and projectForm != ''">project_form = #{projectForm},</if>
|
||||||
<if test="designFileUrl != null and designFileUrl != ''">design_file_url = #{designFileUrl},</if>
|
<if test="designFileUrl != null and designFileUrl != ''">design_file_url = #{designFileUrl},</if>
|
||||||
@@ -61,6 +85,7 @@
|
|||||||
<if test="auditTime != null and auditTime != ''">audit_time = #{auditTime},</if>
|
<if test="auditTime != null and auditTime != ''">audit_time = #{auditTime},</if>
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
<if test="submitterId != null">submitter_id = #{submitterId},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where plan_id = #{planId}
|
where plan_id = #{planId}
|
||||||
</update>
|
</update>
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
||||||
<mapper namespace="com.ruoyi.business.mapper.BizSubmissionMapper">
|
|
||||||
<resultMap type="BizSubmission" id="BizSubmissionResult">
|
|
||||||
<id property="subId" column="sub_id" />
|
|
||||||
<result property="title" column="title" />
|
|
||||||
<result property="direction" column="direction" />
|
|
||||||
<result property="projectForm" column="project_form" />
|
|
||||||
<result property="projectCategory" column="project_category" />
|
|
||||||
<result property="designFileUrl" column="design_file_url" />
|
|
||||||
<result property="submitterId" column="submitter_id" />
|
|
||||||
<result property="submitterName" column="submitter_name" />
|
|
||||||
<result property="status" column="status" />
|
|
||||||
<result property="remark" column="remark" />
|
|
||||||
<result property="createBy" column="create_by" />
|
|
||||||
<result property="createTime" column="create_time" />
|
|
||||||
<result property="updateBy" column="update_by" />
|
|
||||||
<result property="updateTime" column="update_time" />
|
|
||||||
</resultMap>
|
|
||||||
<sql id="selectFields">
|
|
||||||
select sub_id, title, direction, project_form, project_category, design_file_url, submitter_id, submitter_name, status, remark, create_by, create_time, update_by, update_time
|
|
||||||
from biz_submission
|
|
||||||
</sql>
|
|
||||||
<select id="selectByPrimaryKey" resultMap="BizSubmissionResult" parameterType="String">
|
|
||||||
<include refid="selectFields"/>
|
|
||||||
where sub_id = #{subId}
|
|
||||||
</select>
|
|
||||||
<select id="selectList" resultMap="BizSubmissionResult" parameterType="BizSubmission">
|
|
||||||
<include refid="selectFields"/>
|
|
||||||
<where>
|
|
||||||
<if test="submitterId != null and submitterId != ''"> and submitter_id = #{submitterId}</if>
|
|
||||||
<if test="title != null and title != ''"> and title = #{title}</if>
|
|
||||||
<if test="direction != null and direction != ''"> and direction = #{direction}</if>
|
|
||||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
|
||||||
<if test="remark != null and remark != ''"> and remark = #{remark}</if>
|
|
||||||
</where>
|
|
||||||
order by sub_id desc
|
|
||||||
</select>
|
|
||||||
<insert id="insert" parameterType="BizSubmission">
|
|
||||||
insert into biz_submission
|
|
||||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
|
||||||
<if test="subId != null and subId != ''">sub_id,</if>
|
|
||||||
<if test="title != null">title,</if>
|
|
||||||
<if test="direction != null">direction,</if>
|
|
||||||
<if test="projectForm != null">project_form,</if>
|
|
||||||
<if test="projectCategory != null">project_category,</if>
|
|
||||||
<if test="designFileUrl != null">design_file_url,</if>
|
|
||||||
<if test="submitterId != null">submitter_id,</if>
|
|
||||||
<if test="submitterName != null">submitter_name,</if>
|
|
||||||
<if test="status != null">status,</if>
|
|
||||||
<if test="remark != null">remark,</if>
|
|
||||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
|
||||||
create_time,
|
|
||||||
<if test="updateBy != null and updateBy != ''">update_by,</if>
|
|
||||||
update_time,
|
|
||||||
</trim>
|
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
|
||||||
<if test="subId != null and subId != ''">#{subId},</if>
|
|
||||||
<if test="title != null">#{title},</if>
|
|
||||||
<if test="direction != null">#{direction},</if>
|
|
||||||
<if test="projectForm != null">#{projectForm},</if>
|
|
||||||
<if test="projectCategory != null">#{projectCategory},</if>
|
|
||||||
<if test="designFileUrl != null">#{designFileUrl},</if>
|
|
||||||
<if test="submitterId != null">#{submitterId},</if>
|
|
||||||
<if test="submitterName != null">#{submitterName},</if>
|
|
||||||
<if test="status != null">#{status},</if>
|
|
||||||
<if test="remark != null">#{remark},</if>
|
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
|
||||||
sysdate(),
|
|
||||||
<if test="updateBy != null and updateBy != ''">#{updateBy},</if>
|
|
||||||
sysdate(),
|
|
||||||
</trim>
|
|
||||||
</insert>
|
|
||||||
<update id="updateByPrimaryKey" parameterType="BizSubmission">
|
|
||||||
update biz_submission
|
|
||||||
<trim prefix="SET" suffixOverrides=",">
|
|
||||||
<if test="submitterId != null and submitterId != ''">submitter_id = #{submitterId},</if>
|
|
||||||
<if test="submitterName != null and submitterName != ''">submitter_name = #{submitterName},</if>
|
|
||||||
<if test="projectForm != null and projectForm != ''">project_form = #{projectForm},</if>
|
|
||||||
<if test="projectCategory != null and projectCategory != ''">project_category = #{projectCategory},</if>
|
|
||||||
<if test="designFileUrl != null and designFileUrl != ''">design_file_url = #{designFileUrl},</if>
|
|
||||||
<if test="auditOpinion != null and auditOpinion != ''">audit_opinion = #{auditOpinion},</if>
|
|
||||||
<if test="auditBy != null and auditBy != ''">audit_by = #{auditBy},</if>
|
|
||||||
<if test="auditTime != null and auditTime != ''">audit_time = #{auditTime},</if>
|
|
||||||
<if test="title != null">title = #{title},</if>
|
|
||||||
<if test="direction != null">direction = #{direction},</if>
|
|
||||||
<if test="status != null">status = #{status},</if>
|
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
|
||||||
update_time = sysdate(),
|
|
||||||
</trim>
|
|
||||||
where sub_id = #{subId}
|
|
||||||
</update>
|
|
||||||
<delete id="deleteByPrimaryKey" parameterType="String">
|
|
||||||
delete from biz_submission where sub_id = #{subId}
|
|
||||||
</delete>
|
|
||||||
<delete id="deleteByPrimaryKeys" parameterType="String">
|
|
||||||
delete from biz_submission where sub_id in
|
|
||||||
<foreach collection="subIds" item="subId" open="(" separator="," close=")">
|
|
||||||
#{subId}
|
|
||||||
</foreach>
|
|
||||||
</delete>
|
|
||||||
</mapper>
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 迁移脚本: biz_submission → biz_project_plan
|
||||||
|
-- 时间: 2026-08-18
|
||||||
|
-- 说明:
|
||||||
|
-- 1. 医生侧 /doctor/submissions 原本走 biz_submission 表
|
||||||
|
-- (controller: BizSubmissionController)
|
||||||
|
-- 2. 整改后与经理侧 /manager/plans 共用 biz_project_plan 表
|
||||||
|
-- 3. 测试数据不迁 (用户确认价值不高)
|
||||||
|
-- 4. 测试/生产库请先备份再执行
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- ============== 段 1: biz_project_plan 加投稿人字段 ==============
|
||||||
|
ALTER TABLE biz_project_plan
|
||||||
|
ADD COLUMN submitter_id BIGINT NULL COMMENT '投稿人用户ID (医生侧投稿归属, 经理/admin 录入为 NULL)';
|
||||||
|
|
||||||
|
CREATE INDEX idx_plan_submitter ON biz_project_plan(submitter_id);
|
||||||
|
|
||||||
|
-- ============== 段 1.5: biz_person.user_id 加 UNIQUE,保证 LEFT JOIN 1:1 无笛卡尔积 ==============
|
||||||
|
-- 当前 dump 已确认 biz_person.user_id 无重复 (12 条数据,user_id 非空 2 条且 user_id=106/107 各一条)
|
||||||
|
ALTER TABLE biz_person
|
||||||
|
ADD UNIQUE KEY uk_person_user_id (user_id);
|
||||||
|
|
||||||
|
-- ============== 段 2: 数据迁移 (测试数据不迁,故 0 行) ==============
|
||||||
|
-- 旧 biz_submission.status 是英文 enum:
|
||||||
|
-- DRAFT → '0' 未提交
|
||||||
|
-- PENDING → '1' 待审核
|
||||||
|
-- APPROVED → '2' 通过
|
||||||
|
-- REJECTED → '3' 拒绝
|
||||||
|
-- (用户已确认测试数据不迁)
|
||||||
|
|
||||||
|
-- ============== 段 3: 删 biz_submission 表 ==============
|
||||||
|
DROP TABLE IF EXISTS biz_submission;
|
||||||
@@ -44,8 +44,9 @@ const routes = [
|
|||||||
{ path: 'orgs', name: 'admin-orgs', component: () => import('@/views/admin/Orgs.vue'), meta: { title: '公司管理' } },
|
{ path: 'orgs', name: 'admin-orgs', component: () => import('@/views/admin/Orgs.vue'), meta: { title: '公司管理' } },
|
||||||
{ path: 'article', name: 'admin-article', component: () => import('@/views/admin/BizArticleAdmin.vue'), meta: { title: '协议管理' } },
|
{ path: 'article', name: 'admin-article', component: () => import('@/views/admin/BizArticleAdmin.vue'), meta: { title: '协议管理' } },
|
||||||
{ path: 'article/edit/:id', name: 'admin-article-edit', component: () => import('@/views/admin/BizArticleEdit.vue'), meta: { title: '编辑文章' } },
|
{ path: 'article/edit/:id', name: 'admin-article-edit', component: () => import('@/views/admin/BizArticleEdit.vue'), meta: { title: '编辑文章' } },
|
||||||
{ path: 'special-plan', name: 'admin-special-plan', component: () => import('@/views/admin/BizSpecialPlanAdmin.vue'), meta: { title: '七大专项计划' } },
|
{ path: 'special-plan', name: 'admin-special-plan', component: () => import('@/views/admin/BizSpecialPlanAdmin.vue'), meta: { title: '专项计划管理' } },
|
||||||
{ path: 'special-plan/edit/:id', name: 'admin-special-plan-edit', component: () => import('@/views/admin/BizSpecialPlanEdit.vue'), meta: { title: '编辑专项计划' } },
|
{ path: 'special-plan/edit/:id', name: 'admin-special-plan-edit', component: () => import('@/views/admin/BizSpecialPlanEdit.vue'), meta: { title: '编辑专项计划' } },
|
||||||
|
{ path: 'project-category', name: 'admin-project-category', component: () => import('@/views/admin/ProjectCategory.vue'), meta: { title: '项目类别管理' } },
|
||||||
{ path: 'account', name: 'admin-account', component: () => import('@/views/admin/Account.vue'), meta: { title: '账号信息' } }
|
{ path: 'account', name: 'admin-account', component: () => import('@/views/admin/Account.vue'), meta: { title: '账号信息' } }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -92,8 +93,8 @@ const routes = [
|
|||||||
{ path: 'messages', name: 'doctor-messages', component: () => import('@/views/doctor/Messages.vue'), meta: { title: '消息通知' } },
|
{ path: 'messages', name: 'doctor-messages', component: () => import('@/views/doctor/Messages.vue'), meta: { title: '消息通知' } },
|
||||||
{ path: 'submissions', name: 'doctor-submissions', component: () => import('@/views/doctor/Submissions.vue'), meta: { title: '我的项目设计投稿' } },
|
{ path: 'submissions', name: 'doctor-submissions', component: () => import('@/views/doctor/Submissions.vue'), meta: { title: '我的项目设计投稿' } },
|
||||||
{ path: 'submission/new', name: 'doctor-submission-new', component: () => import('@/views/doctor/SubmissionNew.vue'), meta: { title: '新建项目设计投稿' } },
|
{ path: 'submission/new', name: 'doctor-submission-new', component: () => import('@/views/doctor/SubmissionNew.vue'), meta: { title: '新建项目设计投稿' } },
|
||||||
{ path: 'submission/detail/:subId', name: 'doctor-submission-detail', component: () => import('@/views/doctor/SubmissionDetail.vue'), meta: { title: '投稿详情' } },
|
{ path: 'submission/detail/:planId', name: 'doctor-submission-detail', component: () => import('@/views/doctor/SubmissionDetail.vue'), meta: { title: '投稿详情' } },
|
||||||
{ path: 'submission/edit/:subId', name: 'doctor-submission-edit', component: () => import('@/views/doctor/SubmissionNew.vue'), meta: { title: '修改项目设计投稿' } },
|
{ path: 'submission/edit/:planId', name: 'doctor-submission-edit', component: () => import('@/views/doctor/SubmissionNew.vue'), meta: { title: '修改项目设计投稿' } },
|
||||||
{ path: 'account', name: 'doctor-account', component: () => import('@/views/doctor/Account.vue'), meta: { title: '账号信息' } }
|
{ path: 'account', name: 'doctor-account', component: () => import('@/views/doctor/Account.vue'), meta: { title: '账号信息' } }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// 投稿状态字典 - 与后端 SubmissionStatus 枚举同步
|
|
||||||
// value 用英文 code (PENDING/APPROVED/REJECTED/DRAFT), 前端通过 label 映射中文
|
|
||||||
// locked=true 表示锁定(不可修改/提交)
|
|
||||||
export const SUBMISSION_STATUS = {
|
|
||||||
PENDING: { code: 'PENDING', label: '待审核', type: 'warning', locked: true },
|
|
||||||
APPROVED: { code: 'APPROVED', label: '审核通过', type: 'success', locked: true },
|
|
||||||
REJECTED: { code: 'REJECTED', label: '已退回', type: 'danger', locked: false },
|
|
||||||
DRAFT: { code: 'DRAFT', label: '待提交', type: 'info', locked: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 容错: 把数据库返回的 status (字符串) 规范成 enum, 找不到原样返回
|
|
||||||
export function parseStatus(val) {
|
|
||||||
if (val === null || val === undefined || val === '') return null
|
|
||||||
const key = String(val).trim().toUpperCase()
|
|
||||||
return SUBMISSION_STATUS[key] ? key : null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取状态 label, 容错返回 val 原值
|
|
||||||
export function statusLabel(val) {
|
|
||||||
const key = parseStatus(val)
|
|
||||||
return key ? SUBMISSION_STATUS[key].label : (val ?? '')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 el-tag type
|
|
||||||
export function statusType(val) {
|
|
||||||
const key = parseStatus(val)
|
|
||||||
return key ? SUBMISSION_STATUS[key].type : 'info'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否锁定 (待审核/审核通过不可修改/提交)
|
|
||||||
export function isLocked(val) {
|
|
||||||
const key = parseStatus(val)
|
|
||||||
if (!key) return false
|
|
||||||
return SUBMISSION_STATUS[key].locked === true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 筛选项列表 (用于 el-select options)
|
|
||||||
export const STATUS_OPTIONS = Object.values(SUBMISSION_STATUS).map(s => ({
|
|
||||||
label: s.label,
|
|
||||||
value: s.code
|
|
||||||
}))
|
|
||||||
@@ -37,12 +37,12 @@
|
|||||||
<a class="more" @click.prevent="$router.push('/doctor/submissions')">更多 →</a>
|
<a class="more" @click.prevent="$router.push('/doctor/submissions')">更多 →</a>
|
||||||
</h2>
|
</h2>
|
||||||
<ul class="simple-list">
|
<ul class="simple-list">
|
||||||
<li class="simple-item" v-for="s in pendingAgreements" :key="s.subId">
|
<li class="simple-item" v-for="s in pendingAgreements" :key="s.planId">
|
||||||
<div class="item-main">
|
<div class="item-main">
|
||||||
<span class="item-title">{{ s.title }}</span>
|
<span class="item-title">{{ s.planName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="item-status" :class="{ done: s.status === 'done' }">
|
<span class="item-status" :class="{ done: s.status === '2' }">
|
||||||
{{ s.status === 'done' ? '已完成' : '待签署' }}
|
{{ s.status === '2' ? '已通过' : (s.status === '3' ? '已退回' : (s.status === '1' ? '审核中' : '待提交')) }}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="!pendingAgreements.length" class="empty">暂无待签协议</li>
|
<li v-if="!pendingAgreements.length" class="empty">暂无待签协议</li>
|
||||||
@@ -109,13 +109,13 @@ async function load() {
|
|||||||
upcomingMeetings.value = (data?.rows || []).slice(0, 5)
|
upcomingMeetings.value = (data?.rows || []).slice(0, 5)
|
||||||
} catch (e) { upcomingMeetings.value = [] }
|
} catch (e) { upcomingMeetings.value = [] }
|
||||||
|
|
||||||
// 待签署协议
|
// 待签署协议 (改走 biz_project_plan, 后端 doctor 角色已自动按当前用户过滤)
|
||||||
try {
|
try {
|
||||||
const { data } = await bizList('submission', { pageNum: 1, pageSize: 5 })
|
const { data } = await bizList('projectPlan', { pageNum: 1, pageSize: 5 })
|
||||||
pendingAgreements.value = (data?.rows || []).slice(0, 5).map(s => ({
|
pendingAgreements.value = (data?.rows || []).slice(0, 5).map(s => ({
|
||||||
subId: s.subId,
|
planId: s.planId,
|
||||||
title: s.title,
|
planName: s.planName,
|
||||||
status: s.status === '审核通过' ? 'done' : 'pending'
|
status: s.status
|
||||||
}))
|
}))
|
||||||
} catch (e) { pendingAgreements.value = [] }
|
} catch (e) { pendingAgreements.value = [] }
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,12 @@
|
|||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="策划方案名称">
|
<el-form-item label="策划方案名称">
|
||||||
<el-input :model-value="display.title" readonly />
|
<el-input :model-value="display.planName" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目方向">
|
<el-form-item label="项目方向">
|
||||||
<el-input :model-value="display.direction" readonly />
|
<el-input :model-value="display.planDirectionTitle" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目类别">
|
<el-form-item label="项目类别">
|
||||||
<el-input :model-value="display.projectCategory" readonly />
|
<el-input :model-value="display.planCategory" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -57,15 +57,18 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-tag v-if="detail.status !== null && detail.status !== undefined && detail.status !== ''" :type="statusType(detail.status)" disable-transitions>
|
<audit-status-tag v-if="detail.status" :status="detail.status" />
|
||||||
{{ statusLabel(detail.status) }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
|
<el-form-item label="投稿人">
|
||||||
|
<el-input :model-value="display.submitterName" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
<el-form-item label="投稿时间">
|
<el-form-item label="投稿时间">
|
||||||
<el-input :model-value="display.createTime" readonly />
|
<el-input :model-value="display.createTime" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -86,6 +89,7 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import AuditStatusTag from '@/components/AuditStatusTag.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -103,24 +107,13 @@ function fmtTime(t) {
|
|||||||
try { return new Date(t).toLocaleString('zh-CN', { hour12: false }) } catch { return t }
|
try { return new Date(t).toLocaleString('zh-CN', { hour12: false }) } catch { return t }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 状态 → el-tag 类型映射
|
|
||||||
const STATUS_TYPE_MAP = {
|
|
||||||
'待审核': 'warning',
|
|
||||||
'审核通过': 'success',
|
|
||||||
'已退回': 'danger',
|
|
||||||
'未结题': 'info',
|
|
||||||
'已结题': 'success'
|
|
||||||
}
|
|
||||||
function statusType(s) {
|
|
||||||
return STATUS_TYPE_MAP[s] || 'info'
|
|
||||||
}
|
|
||||||
const display = computed(() => ({
|
const display = computed(() => ({
|
||||||
title: fmt(detail.value.title),
|
planName: fmt(detail.value.planName),
|
||||||
direction: fmt(detail.value.direction),
|
planDirectionTitle: fmt(detail.value.planDirectionTitle || detail.value.planDirection),
|
||||||
projectForm: fmt(detail.value.projectForm),
|
projectForm: fmt(detail.value.projectForm),
|
||||||
projectCategory: fmt(detail.value.projectCategory),
|
planCategory: fmt(detail.value.planCategory),
|
||||||
remark: fmt(detail.value.remark),
|
remark: fmt(detail.value.remark),
|
||||||
status: fmt(detail.value.status),
|
submitterName: fmt(detail.value.submitterName),
|
||||||
createTime: fmtTime(detail.value.createTime)
|
createTime: fmtTime(detail.value.createTime)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -131,15 +124,15 @@ function goBack() {
|
|||||||
function downloadName() {
|
function downloadName() {
|
||||||
const url = detail.value.designFileUrl || ''
|
const url = detail.value.designFileUrl || ''
|
||||||
const last = url.split('/').pop()
|
const last = url.split('/').pop()
|
||||||
return last || `submission-${detail.value.subId}`
|
return last || `submission-${detail.value.planId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const subId = route.params.subId
|
const planId = route.params.planId
|
||||||
if (!subId) { ElMessage.warning('参数缺失'); goBack(); return }
|
if (!planId) { ElMessage.warning('参数缺失'); goBack(); return }
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await request.get(`/business/submission/${subId}`)
|
const res = await request.get(`/business/projectPlan/${planId}`)
|
||||||
if (res?.code === 200) {
|
if (res?.code === 200) {
|
||||||
detail.value = res.data || {}
|
detail.value = res.data || {}
|
||||||
} else {
|
} else {
|
||||||
@@ -180,12 +173,6 @@ onMounted(load)
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.remark-cell {
|
|
||||||
min-height: 110px;
|
|
||||||
line-height: 1.6;
|
|
||||||
padding: 12px 14px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
.readonly-form :deep(.el-link) { font-weight: 500; }
|
.readonly-form :deep(.el-link) { font-weight: 500; }
|
||||||
|
|
||||||
.detail-actions { margin-top: 16px; }
|
.detail-actions { margin-top: 16px; }
|
||||||
|
|||||||
@@ -10,19 +10,14 @@
|
|||||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
|
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="策划方案名称" prop="title">
|
<el-form-item label="策划方案名称" prop="planName">
|
||||||
<el-input v-model="form.title" placeholder="请输入策划方案名称" maxlength="200" show-word-limit />
|
<el-input v-model="form.planName" placeholder="请输入策划方案名称" maxlength="200" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目方向" prop="direction">
|
<el-form-item label="项目方向" prop="planDirectionId">
|
||||||
<el-select v-model="form.direction" placeholder="请选择" style="width:100%">
|
<el-select v-model="form.planDirectionId" placeholder="请选择" style="width:100%">
|
||||||
<el-option label="学术项目" value="学术项目" />
|
<el-option v-for="opt in planDirectionOptions" :key="opt.id" :label="opt.title" :value="opt.id" />
|
||||||
<el-option label="科研项目" value="科研项目" />
|
|
||||||
<el-option label="共识" value="共识" />
|
|
||||||
<el-option label="指南" value="指南" />
|
|
||||||
<el-option label="会议项目" value="会议项目" />
|
|
||||||
<el-option label="对外交流" value="对外交流" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -40,16 +35,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="项目类别" prop="projectCategory">
|
<el-form-item label="项目类别" prop="planCategory">
|
||||||
<el-select v-model="form.projectCategory" placeholder="请选择" style="width:100%">
|
<dict-select v-model="form.planCategory" dict-type="biz_project_category" value-field="label" style="width:100%" />
|
||||||
<el-option label="学术会议类" value="学术会议类" />
|
|
||||||
<el-option label="专项科研类" value="专项科研类" />
|
|
||||||
<el-option label="调研征集类" value="调研征集类" />
|
|
||||||
<el-option label="慈善帮扶类" value="慈善帮扶类" />
|
|
||||||
<el-option label="标准制定类" value="标准制定类" />
|
|
||||||
<el-option label="患者援助类" value="患者援助类" />
|
|
||||||
<el-option label="专业培训类" value="专业培训类" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -69,7 +56,7 @@
|
|||||||
:on-remove="onDesignRemove"
|
:on-remove="onDesignRemove"
|
||||||
>
|
>
|
||||||
<i class="el-icon-upload"></i>
|
<i class="el-icon-upload"></i>
|
||||||
<div class="el-upload__text">将设计文件拖到此处,或<em>点击上传</em></div>
|
<div class="el-upload__text">将设计文件拖到此处,或<em>点击上传</em></div>
|
||||||
<div class="el-upload__tip" slot="tip">支持 PDF / Word / 图片, 单文件 ≤ 20MB</div>
|
<div class="el-upload__tip" slot="tip">支持 PDF / Word / 图片, 单文件 ≤ 20MB</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -100,7 +87,9 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useUserStore } from '@/store/user'
|
import { useUserStore } from '@/store/user'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { bizAdd, bizUpdate } from '@/api/public'
|
||||||
import { uploadToOss } from '@/utils/oss'
|
import { uploadToOss } from '@/utils/oss'
|
||||||
|
import DictSelect from '@/components/DictSelect.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -111,24 +100,33 @@ const saving = ref(false)
|
|||||||
const loadingDetail = ref(false)
|
const loadingDetail = ref(false)
|
||||||
const designFileList = ref([])
|
const designFileList = ref([])
|
||||||
const designUploading = ref(false)
|
const designUploading = ref(false)
|
||||||
const isEdit = !!route.params.subId
|
const isEdit = !!route.params.planId
|
||||||
|
|
||||||
|
// 项目方向 options (复用 manager 侧接口)
|
||||||
|
const planDirectionOptions = ref([])
|
||||||
|
async function loadPlanDirectionOptions() {
|
||||||
|
try {
|
||||||
|
const { data } = await request.get('/business/specialPlan/options')
|
||||||
|
planDirectionOptions.value = (data && data.data) || data || []
|
||||||
|
} catch { planDirectionOptions.value = [] }
|
||||||
|
}
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
subId: null,
|
planId: null,
|
||||||
title: '',
|
planName: '',
|
||||||
direction: '',
|
planDirectionId: null,
|
||||||
projectForm: '',
|
projectForm: '',
|
||||||
projectCategory: '',
|
planCategory: '',
|
||||||
designFileUrl: '',
|
designFileUrl: '',
|
||||||
status: 'DRAFT',
|
status: '0',
|
||||||
remark: ''
|
remark: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
title: [{ required: true, message: '请输入策划方案名称', trigger: 'blur' }],
|
planName: [{ required: true, message: '请输入策划方案名称', trigger: 'blur' }],
|
||||||
direction: [{ required: true, message: '请选择项目方向', trigger: 'change' }],
|
planDirectionId: [{ required: true, message: '请选择项目方向', trigger: 'change' }],
|
||||||
projectForm: [{ required: true, message: '请选择项目形式', trigger: 'change' }],
|
projectForm: [{ required: true, message: '请选择项目形式', trigger: 'change' }],
|
||||||
projectCategory: [{ required: true, message: '请选择项目类别', trigger: 'change' }],
|
planCategory: [{ required: true, message: '请选择项目类别', trigger: 'change' }],
|
||||||
designFileUrl: [{ required: true, message: '请上传设计文件', trigger: 'change' }]
|
designFileUrl: [{ required: true, message: '请上传设计文件', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +149,6 @@ async function uploadDesign(opts) {
|
|||||||
ElMessage.success('设计文件上传成功')
|
ElMessage.success('设计文件上传成功')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('上传失败: ' + (e?.message || e))
|
ElMessage.error('上传失败: ' + (e?.message || e))
|
||||||
// 上传失败时清空 file-list,避免 el-upload 保留失败文件
|
|
||||||
designFileList.value = []
|
designFileList.value = []
|
||||||
} finally {
|
} finally {
|
||||||
designUploading.value = false
|
designUploading.value = false
|
||||||
@@ -164,24 +161,23 @@ function onDesignRemove() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadDetail() {
|
async function loadDetail() {
|
||||||
const subId = route.params.subId
|
const planId = route.params.planId
|
||||||
if (!subId) return
|
if (!planId) return
|
||||||
loadingDetail.value = true
|
loadingDetail.value = true
|
||||||
try {
|
try {
|
||||||
const res = await request.get(`/business/submission/${subId}`)
|
const res = await request.get(`/business/projectPlan/${planId}`)
|
||||||
if (res?.code === 200 && res.data) {
|
if (res?.code === 200 && res.data) {
|
||||||
const d = res.data
|
const d = res.data
|
||||||
form.subId = d.subId
|
form.planId = d.planId
|
||||||
form.title = d.title || ''
|
form.planName = d.planName || ''
|
||||||
form.direction = d.direction || ''
|
form.planDirectionId = d.planDirectionId ?? null
|
||||||
form.projectForm = d.projectForm || ''
|
form.projectForm = d.projectForm || ''
|
||||||
form.projectCategory = d.projectCategory || ''
|
form.planCategory = d.planCategory || ''
|
||||||
form.designFileUrl = d.designFileUrl || ''
|
form.designFileUrl = d.designFileUrl || ''
|
||||||
form.status = d.status && String(d.status).trim() ? String(d.status).trim().toUpperCase() : 'DRAFT'
|
form.status = d.status && String(d.status).trim() ? String(d.status) : '0'
|
||||||
form.remark = d.remark || ''
|
form.remark = d.remark || ''
|
||||||
// 已上传的设计文件回显到 file-list
|
|
||||||
if (d.designFileUrl) {
|
if (d.designFileUrl) {
|
||||||
const name = d.designFileUrl.split('/').pop() || `${d.title || '设计文件'}.pdf`
|
const name = d.designFileUrl.split('/').pop() || `${d.planName || '设计文件'}.pdf`
|
||||||
designFileList.value = [{ name, url: d.designFileUrl }]
|
designFileList.value = [{ name, url: d.designFileUrl }]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -202,8 +198,7 @@ function goBack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirmCancel() {
|
function confirmCancel() {
|
||||||
// 检查表单是否有内容
|
const hasContent = form.planName || form.planDirectionId || form.projectForm || form.planCategory || form.designFileUrl || form.remark
|
||||||
const hasContent = form.title || form.direction || form.projectForm || form.projectCategory || form.designFileUrl || form.remark
|
|
||||||
if (!hasContent) { goBack(); return }
|
if (!hasContent) { goBack(); return }
|
||||||
ElMessageBox.confirm('确定取消新建?未保存的内容将丢失', '提示', { type: 'warning' })
|
ElMessageBox.confirm('确定取消新建?未保存的内容将丢失', '提示', { type: 'warning' })
|
||||||
.then(() => goBack())
|
.then(() => goBack())
|
||||||
@@ -221,29 +216,19 @@ async function onSave() {
|
|||||||
try {
|
try {
|
||||||
let payload = { ...form }
|
let payload = { ...form }
|
||||||
if (!isEdit) {
|
if (!isEdit) {
|
||||||
// 新建: 强制设为 DRAFT(待提交)
|
// 新建: 强制 status='0' (未提交), submitter 后端兜底
|
||||||
payload.status = 'DRAFT'
|
payload.status = '0'
|
||||||
// 写入当前登录用户信息
|
|
||||||
payload.submitterId = userStore.user?.userId || null
|
payload.submitterId = userStore.user?.userId || null
|
||||||
payload.submitterName = userStore.user?.userName || userStore.user?.nickName || ''
|
|
||||||
} else if (!payload.status) {
|
} else if (!payload.status) {
|
||||||
// 修改: 如果状态丢失, 兜底 DRAFT(待提交)
|
payload.status = '0'
|
||||||
payload.status = 'DRAFT'
|
|
||||||
}
|
}
|
||||||
let res
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
// 修改: 走 PUT /business/submission
|
await bizUpdate('projectPlan', payload)
|
||||||
res = await request.put('/business/submission', payload)
|
|
||||||
} else {
|
} else {
|
||||||
// 新建: 走 POST /business/submission
|
await bizAdd('projectPlan', payload)
|
||||||
res = await request.post('/business/submission', payload)
|
|
||||||
}
|
|
||||||
if (res?.code === 200) {
|
|
||||||
ElMessage.success(isEdit ? '修改成功' : '新建成功')
|
|
||||||
goBack()
|
|
||||||
} else {
|
|
||||||
ElMessage.error(res?.msg || (isEdit ? '修改失败' : '保存失败'))
|
|
||||||
}
|
}
|
||||||
|
ElMessage.success(isEdit ? '修改成功' : '新建成功')
|
||||||
|
goBack()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[submission-new] save failed', e)
|
console.error('[submission-new] save failed', e)
|
||||||
ElMessage.error(e?.msg || (isEdit ? '修改失败' : '保存失败'))
|
ElMessage.error(e?.msg || (isEdit ? '修改失败' : '保存失败'))
|
||||||
@@ -253,6 +238,7 @@ async function onSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
loadPlanDirectionOptions()
|
||||||
if (isEdit) loadDetail()
|
if (isEdit) loadDetail()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,18 +3,29 @@
|
|||||||
<div class="breadcrumb">首页 / 我的项目设计投稿</div>
|
<div class="breadcrumb">首页 / 我的项目设计投稿</div>
|
||||||
|
|
||||||
<el-form inline :model="q" class="filter-form">
|
<el-form inline :model="q" class="filter-form">
|
||||||
<el-form-item label="投稿名称"><el-input v-model="q.title" placeholder="输入投稿名称" clearable style="width: 200px" /></el-form-item>
|
<el-form-item label="策划方案名称"><el-input v-model="q.planName" placeholder="输入策划方案名称" clearable style="width: 200px" /></el-form-item>
|
||||||
<el-form-item label="学科方向"><el-input v-model="q.direction" placeholder="输入学科方向" clearable style="width: 160px" /></el-form-item>
|
<el-form-item label="项目方向">
|
||||||
|
<el-select v-model="q.planDirectionId" placeholder="请选择" clearable style="width: 200px">
|
||||||
|
<el-option v-for="opt in planDirectionOptions" :key="opt.id" :label="opt.title" :value="opt.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目类别">
|
||||||
|
<dict-select v-model="q.planCategory" dict-type="biz_project_category" value-field="label" style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="形式">
|
<el-form-item label="形式">
|
||||||
<el-select v-model="q.projectForm" placeholder="请选择" clearable style="width: 160px">
|
<el-select v-model="q.projectForm" placeholder="请选择" clearable style="width: 140px">
|
||||||
<el-option label="线上" value="线上" />
|
<el-option label="线上" value="线上" />
|
||||||
<el-option label="线下" value="线下" />
|
<el-option label="线下" value="线下" />
|
||||||
<el-option label="线上+线下" value="线上+线下" />
|
<el-option label="线上+线下" value="线上+线下" />
|
||||||
|
<el-option label="其他" value="其他" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-select v-model="q.status" placeholder="请选择" clearable style="width: 180px">
|
<el-select v-model="q.status" placeholder="请选择" clearable style="width: 140px">
|
||||||
<el-option v-for="opt in STATUS_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
|
<el-option label="待提交" value="0" />
|
||||||
|
<el-option label="待审核" value="1" />
|
||||||
|
<el-option label="通过" value="2" />
|
||||||
|
<el-option label="拒绝" value="3" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注"><el-input v-model="q.remark" placeholder="输入备注" clearable style="width: 200px" /></el-form-item>
|
<el-form-item label="备注"><el-input v-model="q.remark" placeholder="输入备注" clearable style="width: 200px" /></el-form-item>
|
||||||
@@ -28,8 +39,11 @@
|
|||||||
|
|
||||||
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSelectionChange">
|
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSelectionChange">
|
||||||
<el-table-column type="selection" width="48" />
|
<el-table-column type="selection" width="48" />
|
||||||
<el-table-column prop="title" label="投稿名称" min-width="220" show-overflow-tooltip />
|
<el-table-column prop="planName" label="投稿名称" min-width="220" show-overflow-tooltip />
|
||||||
<el-table-column prop="direction" label="学科方向" width="120" />
|
<el-table-column label="项目方向" min-width="220" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ row.planDirectionTitle || row.planDirection || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="planCategory" label="项目类别" width="120" />
|
||||||
<el-table-column prop="projectForm" label="形式" width="100" />
|
<el-table-column prop="projectForm" label="形式" width="100" />
|
||||||
<el-table-column label="设计文件" width="100">
|
<el-table-column label="设计文件" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -37,11 +51,9 @@
|
|||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="100">
|
<el-table-column label="状态" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-if="row.status !== null && row.status !== undefined && row.status !== ''" :type="statusType(row.status)" disable-transitions>
|
<audit-status-tag :status="row.status" />
|
||||||
{{ statusLabel(row.status) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
|
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
|
||||||
@@ -70,24 +82,42 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import request from '@/utils/request'
|
||||||
import { bizList, bizUpdate } from '@/api/public'
|
import { bizList, bizUpdate } from '@/api/public'
|
||||||
import { STATUS_OPTIONS, statusLabel, statusType, isLocked } from '@/utils/submissionStatus'
|
import DictSelect from '@/components/DictSelect.vue'
|
||||||
|
import AuditStatusTag from '@/components/AuditStatusTag.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const q = reactive({ title: '', direction: '', projectForm: '', status: '', remark: '' })
|
const q = reactive({
|
||||||
|
planName: '',
|
||||||
|
planDirectionId: '',
|
||||||
|
planCategory: '',
|
||||||
|
projectForm: '',
|
||||||
|
status: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
const page = reactive({ pageNum: 1, pageSize: 10, total: 0 })
|
const page = reactive({ pageNum: 1, pageSize: 10, total: 0 })
|
||||||
const rows = ref([])
|
const rows = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const selected = ref([])
|
const selected = ref([])
|
||||||
|
|
||||||
|
// 项目方向 options (复用 manager 侧接口)
|
||||||
|
const planDirectionOptions = ref([])
|
||||||
|
async function loadPlanDirectionOptions() {
|
||||||
|
try {
|
||||||
|
const { data } = await request.get('/business/specialPlan/options')
|
||||||
|
planDirectionOptions.value = (data && data.data) || data || []
|
||||||
|
} catch { planDirectionOptions.value = [] }
|
||||||
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const { data } = await bizList('submission', { ...q, pageNum: page.pageNum, pageSize: page.pageSize })
|
const { data } = await bizList('projectPlan', { ...q, pageNum: page.pageNum, pageSize: page.pageSize })
|
||||||
rows.value = data?.rows || []
|
rows.value = data?.rows || []
|
||||||
page.total = data?.total || 0
|
page.total = data?.total || 0
|
||||||
} catch (e) { rows.value = []; page.total = 0 }
|
} catch (e) { rows.value = []; page.total = 0 }
|
||||||
@@ -95,8 +125,9 @@ async function load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
q.title = ''
|
q.planName = ''
|
||||||
q.direction = ''
|
q.planDirectionId = ''
|
||||||
|
q.planCategory = ''
|
||||||
q.projectForm = ''
|
q.projectForm = ''
|
||||||
q.status = ''
|
q.status = ''
|
||||||
q.remark = ''
|
q.remark = ''
|
||||||
@@ -104,14 +135,13 @@ function reset() {
|
|||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原型规则: "待审核和审核通过后, 操作只显示查看按钮, 无法进行修改"
|
// 锁定的状态 (待审核 '1' / 通过 '2') 不可修改/提交; 未提交 '0' / 拒绝 '3' 可操作
|
||||||
// 字典驱动: 通过 isLocked() 判断, true 表示锁定(待审核 0 / 审核通过 1)
|
|
||||||
function canModify(row) {
|
function canModify(row) {
|
||||||
return !isLocked(row.status)
|
return row.status !== '1' && row.status !== '2'
|
||||||
}
|
}
|
||||||
function canSubmit(row) {
|
function canSubmit(row) {
|
||||||
// 锁定的不能再次提交; 其他状态(未结题/已结题/已退回/待提交)均可提交进入待审核
|
// 已通过 '2' 无需再提交; 待审核 '1' 也不允许重复提交; 只有未提交 '0' / 拒绝 '3' 可提交
|
||||||
return !isLocked(row.status)
|
return row.status === '0' || row.status === '3'
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectionChange(arr) { selected.value = arr }
|
function onSelectionChange(arr) { selected.value = arr }
|
||||||
@@ -120,10 +150,10 @@ function onCreate() {
|
|||||||
router.push({ path: '/doctor/submission/new' })
|
router.push({ path: '/doctor/submission/new' })
|
||||||
}
|
}
|
||||||
function onView(row) {
|
function onView(row) {
|
||||||
router.push({ path: `/doctor/submission/detail/${row.subId}` })
|
router.push({ path: `/doctor/submission/detail/${row.planId}` })
|
||||||
}
|
}
|
||||||
function onEdit(row) {
|
function onEdit(row) {
|
||||||
router.push({ path: `/doctor/submission/edit/${row.subId}` })
|
router.push({ path: `/doctor/submission/edit/${row.planId}` })
|
||||||
}
|
}
|
||||||
function onDownload(row) {
|
function onDownload(row) {
|
||||||
if (!row.designFileUrl) { ElMessage.warning('该投稿暂无设计文件'); return }
|
if (!row.designFileUrl) { ElMessage.warning('该投稿暂无设计文件'); return }
|
||||||
@@ -131,13 +161,12 @@ function onDownload(row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmit(row) {
|
async function onSubmit(row) {
|
||||||
if (row.status === 'APPROVED') { ElMessage.info('该投稿已审核通过,无需重复提交'); return }
|
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确定要提交投稿「${row.title}」吗?提交后将进入审核流程`, '提交确认', { type: 'warning' })
|
await ElMessageBox.confirm(`确定要提交投稿「${row.planName}」吗?提交后将进入审核流程`, '提交确认', { type: 'warning' })
|
||||||
} catch { return }
|
} catch { return }
|
||||||
try {
|
try {
|
||||||
// 提交后状态变为 PENDING(待审核)
|
// 提交后状态变为 '1' (待审核)
|
||||||
await bizUpdate('submission', { subId: row.subId, status: 'PENDING' })
|
await bizUpdate('projectPlan', { planId: row.planId, status: '1' })
|
||||||
ElMessage.success('已提交,等待审核')
|
ElMessage.success('已提交,等待审核')
|
||||||
load()
|
load()
|
||||||
} catch (e) { ElMessage.error(e?.msg || '提交失败') }
|
} catch (e) { ElMessage.error(e?.msg || '提交失败') }
|
||||||
@@ -146,19 +175,21 @@ async function onSubmit(row) {
|
|||||||
async function onBatch() {
|
async function onBatch() {
|
||||||
if (!selected.value.length) { ElMessage.warning('请先勾选要提交的投稿'); return }
|
if (!selected.value.length) { ElMessage.warning('请先勾选要提交的投稿'); return }
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确定要批量提交选中的 ${selected.value.length} 条投稿吗?`, '批量提交', { type: 'warning' })
|
await ElMessageBox.confirm(`确定要批量提交选中的 ${selected.value.length} 条投稿吗?`, '批量提交', { type: 'warning' })
|
||||||
} catch { return }
|
} catch { return }
|
||||||
let ok = 0
|
let ok = 0
|
||||||
for (const r of selected.value) {
|
for (const r of selected.value) {
|
||||||
// 锁定状态 (PENDING 待审核 / APPROVED 审核通过) 跳过提交
|
if (!canSubmit(r)) continue
|
||||||
if (isLocked(r.status)) continue
|
try { await bizUpdate('projectPlan', { planId: r.planId, status: '1' }); ok++ } catch {}
|
||||||
try { await bizUpdate('submission', { subId: r.subId, status: 'PENDING' }); ok++ } catch {}
|
|
||||||
}
|
}
|
||||||
ElMessage.success(`已提交 ${ok} 条`)
|
ElMessage.success(`已提交 ${ok} 条`)
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
load()
|
onMounted(() => {
|
||||||
|
loadPlanDirectionOptions()
|
||||||
|
load()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user