feat(crm,wms): 重构客诉流程,优化节点与展示逻辑

1.  重构客诉受理流程:改为质量部+生产部并行处理,后续流转至陈总审批、吴部长处理
2.  移除原有的下发、驳回相关接口与逻辑
3.  更新全流程状态文案、标签与步骤展示
4.  调整页面筛选条件与操作按钮
5.  新增陈总审批表单与对应逻辑
This commit is contained in:
王文昊
2026-07-11 10:11:02 +08:00
parent 2a611bad19
commit 3c506e8ca6
13 changed files with 213 additions and 293 deletions

View File

@@ -99,55 +99,14 @@ public class TsComplaintAcceptController extends BaseController {
} }
/** /**
* 意见下发修改flow_status=2按字典部门创建代办任务 * 发起流程:创建质量部+生产部并行任务
* *
* @param acceptId 受理单ID * @param acceptId 受理单ID
*/ */
@Log(title = "意见下", businessType = BusinessType.UPDATE) @Log(title = "起流程", businessType = BusinessType.UPDATE)
@PostMapping("/opinionDispatch/{acceptId}") @PostMapping("/opinionDispatch/{acceptId}")
public R<Void> opinionDispatch(@NotNull(message = "主键不能为空") public R<Void> opinionDispatch(@NotNull(message = "主键不能为空")
@PathVariable Long acceptId) { @PathVariable Long acceptId) {
return toAjax(iTsComplaintAcceptService.opinionDispatch(acceptId)); return toAjax(iTsComplaintAcceptService.opinionDispatch(acceptId));
} }
/**
* 反馈下发修改flow_status=4按字典部门创建执行反馈记录
*
* @param acceptId 受理单ID
*/
@Log(title = "反馈下发", businessType = BusinessType.UPDATE)
@PostMapping("/feedbackDispatch")
public R<Void> feedbackDispatch(@RequestParam Long acceptId,
@RequestParam String deptIds) {
List<Long> deptIdList = Arrays.stream(deptIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
return toAjax(iTsComplaintAcceptService.feedbackDispatch(acceptId, deptIdList));
}
/**
* 意见驳回当前部门taskStatus→1、rejectMark→1主表flowStatus→1其他部门rejectMark→2
*
* @param taskId 代办任务ID
* @param reason 驳回意见填入deptOpinion
*/
@Log(title = "意见驳回", businessType = BusinessType.UPDATE)
@PostMapping("/opinionReject/{taskId}")
public R<Void> opinionReject(@NotNull(message = "主键不能为空")
@PathVariable Long taskId,
@RequestParam String reason) {
return toAjax(iTsComplaintAcceptService.opinionReject(taskId, reason));
}
/**
* 反馈驳回当前部门executeStatus→1、rejectMark→1主表flowStatus→3其他部门rejectMark→2
*
* @param relId 执行反馈记录ID
* @param reason 驳回原因填入executeResult
*/
@Log(title = "反馈驳回", businessType = BusinessType.UPDATE)
@PostMapping("/feedbackReject/{relId}")
public R<Void> feedbackReject(@NotNull(message = "主键不能为空")
@PathVariable Long relId,
@RequestParam String reason) {
return toAjax(iTsComplaintAcceptService.feedbackReject(relId, reason));
}
} }

View File

@@ -48,22 +48,7 @@ public interface ITsComplaintAcceptService {
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/** /**
* 意见下发:修改flow_status=2按字典flow_coil_objection部门创建代办任务 * 发起流程:创建质量部+生产部并行任务,更新flowStatus=1
*/ */
Boolean opinionDispatch(Long acceptId); Boolean opinionDispatch(Long acceptId);
/**
* 反馈下发修改flow_status=4按传入部门创建执行反馈记录
*/
Boolean feedbackDispatch(Long acceptId, List<Long> deptIds);
/**
* 意见驳回taskStatus→1、rejectMark→1主表flowStatus→1其他task的rejectMark→2
*/
Boolean opinionReject(Long taskId, String reason);
/**
* 反馈驳回executeStatus→1、rejectMark→1主表flowStatus→3其他rel的rejectMark→2
*/
Boolean feedbackReject(Long relId, String reason);
} }

View File

@@ -96,7 +96,7 @@ public class TsComplaintAcceptServiceImpl implements ITsComplaintAcceptService {
} }
/** /**
* 新增投诉受理单主 * 新增投诉受理单主(创建后自动发起流程)
*/ */
@Override @Override
public Boolean insertByBo(TsComplaintAcceptBo bo) { public Boolean insertByBo(TsComplaintAcceptBo bo) {
@@ -115,6 +115,8 @@ public class TsComplaintAcceptServiceImpl implements ITsComplaintAcceptService {
}).collect(java.util.stream.Collectors.toList()); }).collect(java.util.stream.Collectors.toList());
tsAcceptCoilRelMapper.insertBatch(relList); tsAcceptCoilRelMapper.insertBatch(relList);
} }
// 自动创建质量部+生产部并行任务,发起流程
opinionDispatch(add.getAcceptId());
} }
return flag; return flag;
} }
@@ -130,133 +132,34 @@ public class TsComplaintAcceptServiceImpl implements ITsComplaintAcceptService {
} }
/** /**
* 意见下 * 发起流程
* 修改受理单flow_status=2按字典flow_coil_objection的部门创建代办任务 * 创建质量部(deptId=2) + 生产部(deptId=1) 两个并行代办任务
* 更新受理单flow_status=1质量部·生产部处理中
*/ */
@Override @Override
public Boolean opinionDispatch(Long acceptId) { public Boolean opinionDispatch(Long acceptId) {
if (acceptId == null) { if (acceptId == null) {
return false; return false;
} }
// 从sys_dict_data直查根据dict_type=flow_coil_objection获取部门ID列表 // 1. 创建质量部task deptId=2
List<SysDictData> dictList = sysDictDataService.selectDictDataByTypeRealtime("flow_coil_objection"); TsComplaintTask qaTask = new TsComplaintTask();
if (dictList == null || dictList.isEmpty()) { qaTask.setAcceptId(acceptId);
return false; qaTask.setDeptId(2L);
} qaTask.setTaskStatus(0L);
// 更新受理单流程状态为2部门意见填写中 qaTask.setRejectMark(0L);
tsComplaintTaskMapper.insert(qaTask);
// 2. 创建生产部task deptId=1
TsComplaintTask prodTask = new TsComplaintTask();
prodTask.setAcceptId(acceptId);
prodTask.setDeptId(1L);
prodTask.setTaskStatus(0L);
prodTask.setRejectMark(0L);
tsComplaintTaskMapper.insert(prodTask);
// 3. 更新受理单流程状态为1质量部·生产部处理中
LambdaUpdateWrapper<TsComplaintAccept> uw = Wrappers.lambdaUpdate(); LambdaUpdateWrapper<TsComplaintAccept> uw = Wrappers.lambdaUpdate();
uw.eq(TsComplaintAccept::getAcceptId, acceptId) uw.eq(TsComplaintAccept::getAcceptId, acceptId)
.set(TsComplaintAccept::getFlowStatus, 2L);
baseMapper.update(null, uw);
// 按部门创建代办任务
for (SysDictData dict : dictList) {
TsComplaintTask task = new TsComplaintTask();
task.setAcceptId(acceptId);
task.setDeptId(Long.valueOf(dict.getDictValue()));
task.setTaskStatus(0L);
tsComplaintTaskMapper.insert(task);
}
return true;
}
/**
* 反馈下发
* 修改受理单flow_status=4按传入部门创建执行反馈记录
*/
@Override
public Boolean feedbackDispatch(Long acceptId, List<Long> deptIds) {
if (acceptId == null || deptIds == null || deptIds.isEmpty()) {
return false;
}
// 更新受理单流程状态为4方案下发执行反馈中
LambdaUpdateWrapper<TsComplaintAccept> uw = Wrappers.lambdaUpdate();
uw.eq(TsComplaintAccept::getAcceptId, acceptId)
.set(TsComplaintAccept::getFlowStatus, 4L);
baseMapper.update(null, uw);
// 按部门创建执行反馈记录
for (Long deptId : deptIds) {
TsPlanExecuteRel rel = new TsPlanExecuteRel();
rel.setAcceptId(acceptId);
rel.setDeptId(deptId);
rel.setExecuteStatus(0L);
tsPlanExecuteRelMapper.insert(rel);
}
return true;
}
/**
* 意见驳回
* 当前tasktaskStatus→1已审核、rejectMark→1本部门驳回
* 主表flowStatus→1待审核
* 其他task若rejectMark!=1则→2存在部门驳回
*/
@Override
public Boolean opinionReject(Long taskId, String reason) {
if (taskId == null) {
return false;
}
TsComplaintTask currentTask = tsComplaintTaskMapper.selectById(taskId);
if (currentTask == null) {
return false;
}
Long acceptId = currentTask.getAcceptId();
// 1. 更新当前tasktaskStatus=1, rejectMark=1
LambdaUpdateWrapper<TsComplaintTask> taskUw = Wrappers.lambdaUpdate();
taskUw.eq(TsComplaintTask::getTaskId, taskId)
.set(TsComplaintTask::getTaskStatus, 1L)
.set(TsComplaintTask::getRejectMark, 1L)
.set(TsComplaintTask::getDeptOpinion, reason);
tsComplaintTaskMapper.update(null, taskUw);
// 2. 主表flowStatus→1待审核
LambdaUpdateWrapper<TsComplaintAccept> acceptUw = Wrappers.lambdaUpdate();
acceptUw.eq(TsComplaintAccept::getAcceptId, acceptId)
.set(TsComplaintAccept::getFlowStatus, 1L); .set(TsComplaintAccept::getFlowStatus, 1L);
baseMapper.update(null, acceptUw); baseMapper.update(null, uw);
// 3. 同acceptId下其他task若rejectMark!=1则→2
LambdaUpdateWrapper<TsComplaintTask> otherUw = Wrappers.lambdaUpdate();
otherUw.eq(TsComplaintTask::getAcceptId, acceptId)
.ne(TsComplaintTask::getTaskId, taskId)
.ne(TsComplaintTask::getRejectMark, 1L)
.set(TsComplaintTask::getRejectMark, 2L);
tsComplaintTaskMapper.update(null, otherUw);
return true;
}
/**
* 反馈驳回
* 当前relexecuteStatus→1已反馈、rejectMark→1本部门驳回
* 主表flowStatus→3待总负责人汇总方案
* 其他rel若rejectMark!=1则→2存在部门驳回
*/
@Override
public Boolean feedbackReject(Long relId, String reason) {
if (relId == null) {
return false;
}
TsPlanExecuteRel currentRel = tsPlanExecuteRelMapper.selectById(relId);
if (currentRel == null) {
return false;
}
Long acceptId = currentRel.getAcceptId();
// 1. 更新当前relexecuteStatus=1, rejectMark=1
LambdaUpdateWrapper<TsPlanExecuteRel> relUw = Wrappers.lambdaUpdate();
relUw.eq(TsPlanExecuteRel::getRelId, relId)
.set(TsPlanExecuteRel::getExecuteStatus, 1L)
.set(TsPlanExecuteRel::getRejectMark, 1L)
.set(TsPlanExecuteRel::getExecuteResult, reason);
tsPlanExecuteRelMapper.update(null, relUw);
// 2. 主表flowStatus→3待总负责人汇总方案
LambdaUpdateWrapper<TsComplaintAccept> acceptUw = Wrappers.lambdaUpdate();
acceptUw.eq(TsComplaintAccept::getAcceptId, acceptId)
.set(TsComplaintAccept::getFlowStatus, 3L);
baseMapper.update(null, acceptUw);
// 3. 同acceptId下其他rel若rejectMark!=1则→2
LambdaUpdateWrapper<TsPlanExecuteRel> otherUw = Wrappers.lambdaUpdate();
otherUw.eq(TsPlanExecuteRel::getAcceptId, acceptId)
.ne(TsPlanExecuteRel::getRelId, relId)
.ne(TsPlanExecuteRel::getRejectMark, 1L)
.set(TsPlanExecuteRel::getRejectMark, 2L);
tsPlanExecuteRelMapper.update(null, otherUw);
return true; return true;
} }

View File

@@ -157,10 +157,10 @@ public class TsComplaintTaskServiceImpl implements ITsComplaintTaskService {
} }
/** /**
* 同步受理单流程状态 * 同步受理单流程状态 — 串行推进逻辑
* 查看当前受理单下所有代办任务: * Step 1: 质量部(deptId=2) + 生产部(deptId=1) 都完成 → 创建陈总task(deptId=4), flowStatus=2
* - 所有task_status都为1 → flow_status = 3待总负责人汇总方案 * Step 2: 陈总完成 → 创建吴部长task(deptId=3), flowStatus=3
* flow_status=2由意见下发接口设置此处不再处理 * Step 3: 吴部长完成 → flowStatus=4 已办结
*/ */
private void syncAcceptFlowStatus(Long acceptId) { private void syncAcceptFlowStatus(Long acceptId) {
if (acceptId == null) { if (acceptId == null) {
@@ -169,19 +169,72 @@ public class TsComplaintTaskServiceImpl implements ITsComplaintTaskService {
// 查询该受理单下所有未删除的代办任务 // 查询该受理单下所有未删除的代办任务
List<TsComplaintTask> taskList = baseMapper.selectList( List<TsComplaintTask> taskList = baseMapper.selectList(
Wrappers.<TsComplaintTask>lambdaQuery() Wrappers.<TsComplaintTask>lambdaQuery()
.eq(TsComplaintTask::getAcceptId, acceptId).eq(TsComplaintTask::getRejectMark,0) .eq(TsComplaintTask::getAcceptId, acceptId)
); );
// 判断所有任务是否都已完成taskStatus = 1 if (taskList.isEmpty()) {
boolean allCompleted = !taskList.isEmpty() && taskList.stream() return;
.allMatch(task -> task.getTaskStatus() != null && task.getTaskStatus() == 1L); }
if (allCompleted) { // 按 deptId 分组查询各节点状态
// 更新受理单流程状态为3待总负责人汇总方案 Map<Long, List<TsComplaintTask>> taskByDept = taskList.stream()
.collect(Collectors.groupingBy(TsComplaintTask::getDeptId));
// Step 1: 质量部(deptId=2) 和 生产部(deptId=1) 是否都完成
boolean qaDone = taskByDept.containsKey(2L) &&
taskByDept.get(2L).stream().allMatch(t -> t.getTaskStatus() != null && t.getTaskStatus() == 1L);
boolean prodDone = taskByDept.containsKey(1L) &&
taskByDept.get(1L).stream().allMatch(t -> t.getTaskStatus() != null && t.getTaskStatus() == 1L);
if (qaDone && prodDone) {
// 两者都完成 → 检查陈总节点(deptId=4)是否存在
boolean chenExists = taskByDept.containsKey(4L);
if (!chenExists) {
// 创建陈总task deptId=4
TsComplaintTask chenTask = new TsComplaintTask();
chenTask.setAcceptId(acceptId);
chenTask.setDeptId(4L);
chenTask.setRejectMark(0L);
chenTask.setTaskStatus(0L);
baseMapper.insert(chenTask);
updateFlowStatus(acceptId, 2L); // 待陈总审批
} else {
// 陈总节点已存在,检查是否完成
boolean chenDone = taskByDept.get(4L).stream()
.allMatch(t -> t.getTaskStatus() != null && t.getTaskStatus() == 1L);
if (chenDone) {
// 陈总完成 → 检查吴部长节点(deptId=3)是否存在
boolean wuExists = taskByDept.containsKey(3L);
if (!wuExists) {
// 创建吴部长task deptId=3
TsComplaintTask wuTask = new TsComplaintTask();
wuTask.setAcceptId(acceptId);
wuTask.setDeptId(3L);
wuTask.setRejectMark(0L);
wuTask.setTaskStatus(0L);
baseMapper.insert(wuTask);
updateFlowStatus(acceptId, 3L); // 待吴部长处理
} else {
// 吴部长已完成 → 办结
boolean wuDone = taskByDept.get(3L).stream()
.allMatch(t -> t.getTaskStatus() != null && t.getTaskStatus() == 1L);
if (wuDone) {
updateFlowStatus(acceptId, 4L); // 已办结
}
}
}
}
}
// 质/产未全部完成时不做任何事,等待
}
/**
* 更新受理单流程状态
*/
private void updateFlowStatus(Long acceptId, Long status) {
LambdaUpdateWrapper<TsComplaintAccept> uw = Wrappers.lambdaUpdate(); LambdaUpdateWrapper<TsComplaintAccept> uw = Wrappers.lambdaUpdate();
uw.eq(TsComplaintAccept::getAcceptId, acceptId) uw.eq(TsComplaintAccept::getAcceptId, acceptId)
.set(TsComplaintAccept::getFlowStatus, 3L); .set(TsComplaintAccept::getFlowStatus, status);
tsComplaintAcceptMapper.update(null, uw); tsComplaintAcceptMapper.update(null, uw);
} }
}
/** /**
* 保存前的数据校验 * 保存前的数据校验

View File

@@ -44,15 +44,3 @@ export function opinionDispatch(acceptId) {
method: 'post' method: 'post'
}) })
} }
export function feedbackDispatch(acceptId, deptIds) {
return request({ url: '/flow/complaintAccept/feedbackDispatch', method: 'post', params: { acceptId, deptIds } })
}
export function opinionReject(taskId, reason) {
return request({ url: '/flow/complaintAccept/opinionReject/' + taskId, method: 'post', params: { reason } })
}
export function feedbackReject(relId, reason) {
return request({ url: '/flow/complaintAccept/feedbackReject/' + relId, method: 'post', params: { reason } })
}

View File

@@ -37,9 +37,9 @@
<el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip></el-table-column> <el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip></el-table-column>
<el-table-column label="状态" align="center" prop="objectionStatus"> <el-table-column label="状态" align="center" prop="objectionStatus">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag> <el-tag v-if="scope.row.objectionStatus === 0" type="warning">待处理</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 1" type="success">处理</el-tag> <el-tag v-else-if="scope.row.objectionStatus === 1" type="success">反馈</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">关闭</el-tag> <el-tag v-else-if="scope.row.objectionStatus === 2" type="info">办结</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="钢卷信息" align="center" prop="coilList" width="200" show-overflow-tooltip> <el-table-column label="钢卷信息" align="center" prop="coilList" width="200" show-overflow-tooltip>

View File

@@ -37,9 +37,9 @@
<el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip></el-table-column> <el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip></el-table-column>
<el-table-column label="状态" align="center" prop="objectionStatus"> <el-table-column label="状态" align="center" prop="objectionStatus">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag> <el-tag v-if="scope.row.objectionStatus === 0" type="warning">待处理</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 1" type="success">处理</el-tag> <el-tag v-else-if="scope.row.objectionStatus === 1" type="success">反馈</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">关闭</el-tag> <el-tag v-else-if="scope.row.objectionStatus === 2" type="info">办结</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="钢卷信息" align="center" prop="coilList" width="200" show-overflow-tooltip> <el-table-column label="钢卷信息" align="center" prop="coilList" width="200" show-overflow-tooltip>

View File

@@ -104,10 +104,10 @@ const NODE_EVENT_CONFIG = {
], ],
afterSales: [ afterSales: [
{ id: 'A', label: '创建售后单', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/index' } }, { id: 'A', label: '创建售后单', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/index' } },
{ id: 'C', label: '生产部出具处理意见', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } }, { id: 'C', label: '质量部处理', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } },
{ id: 'D', label: '质量部出具处理意见', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } }, { id: 'D', label: '生产部处理', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } },
{ id: 'E', label: '销售部出具处理意见', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } }, { id: 'G', label: '陈总审批', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/summary' } },
{ id: 'G', label: '售后负责人汇总', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/summary' } }, { id: 'H', label: '吴部长(销售部)处理', handler: 'handleOpen', params: { componentPath: 'wms/post/objection/opinion' } },
], ],
inventoryCheck: [ inventoryCheck: [
{ id: 'A', label: '创建盘库计划', handler: 'handleOpen', params: { componentPath: 'wms/post/InvCount/index' } }, { id: 'A', label: '创建盘库计划', handler: 'handleOpen', params: { componentPath: 'wms/post/InvCount/index' } },
@@ -245,16 +245,15 @@ graph TD
afterSales: ` afterSales: `
graph TD graph TD
A["<b>创建售后单</b><br/>填写基本信息<br/>选择需售后处理的钢卷"]:::step A["<b>创建售后单</b><br/>销售内勤发起"]:::step
A --> B["<b>多部门并行处理</b>"]:::fork A --> B["<b>质量部·生产部</b><br/>并行处理"]:::fork
B --> C["<b>生产部</b><br/>出具处理意见"]:::dept1 B --> C["<b>质量部</b><br/>出具处理意见"]:::dept1
B --> D["<b>质量部</b><br/>出具处理意见"]:::dept2 B --> D["<b>生产部</b><br/>出具处理意见"]:::dept2
B --> E["<b>销售部</b><br/>出具处理意见"]:::dept3 C --> F{"两部门<br/>均已完成?"}:::decision
C --> F{"三个部门<br/>全部提交?"}:::decision
D --> F D --> F
E --> F F -->|是| G["<b>陈总审批</b>"]:::dept3
F -->|已全部提交| G["<b>售后负责人</b><br/>汇总各部门意见<br/>直接办结归档"]:::approve G --> H["<b>吴部长</b><br/>(销售部)<br/>处理客诉"]:::approve
G --> J(["<b>售后单封存</b><br/>流程结束"]):::end H --> J(["<b>售后单封存</b><br/>流程结束"]):::end
classDef step fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px classDef step fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px
classDef fork fill:#f0f5ff,stroke:#409eff,color:#303133,stroke-width:2px classDef fork fill:#f0f5ff,stroke:#409eff,color:#303133,stroke-width:2px

View File

@@ -68,7 +68,25 @@
</el-form-item> </el-form-item>
</template> </template>
<!-- 销售部 deptId=3 --> <!-- 陈总审批 deptId=4 -->
<template v-else-if="deptId === 4">
<el-form-item label="审阅意见" prop="reviewOpinion">
<el-input v-model="formData.reviewOpinion" type="textarea" :rows="4"
placeholder="请审阅质量部、生产部意见后填写审批意见" @input="emitJson" />
</el-form-item>
<el-form-item label="审批结论" prop="approvalResult">
<el-radio-group v-model="formData.approvalResult" @change="emitJson">
<el-radio label="approved">批准</el-radio>
<el-radio label="rejected">驳回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="补充说明" prop="supplement">
<el-input v-model="formData.supplement" type="textarea" :rows="2"
placeholder="如有补充说明请填写" @input="emitJson" />
</el-form-item>
</template>
<!-- 销售部(吴部长) deptId=3 -->
<template v-else> <template v-else>
<el-form-item label="处理意见" prop="handlingOpinion"> <el-form-item label="处理意见" prop="handlingOpinion">
<el-input v-model="formData.handlingOpinion" type="textarea" :rows="4" placeholder="请输入处理意见" @input="emitJson" /> <el-input v-model="formData.handlingOpinion" type="textarea" :rows="4" placeholder="请输入处理意见" @input="emitJson" />
@@ -128,6 +146,8 @@ export default {
return { internalInvestigation: '', internalConfirmer: '', internalConfirmDate: '', externalInvestigation: '', externalConfirmer: '', externalConfirmDate: '', responsibleUnit: '', receiver: '', acceptDate: '' }; return { internalInvestigation: '', internalConfirmer: '', internalConfirmDate: '', externalInvestigation: '', externalConfirmer: '', externalConfirmDate: '', responsibleUnit: '', receiver: '', acceptDate: '' };
} else if (this.deptId === 2) { } else if (this.deptId === 2) {
return { cause: '', escapeReason: '', correctiveAction: '', rectifyDate: '', supervisorOpinion: '' }; return { cause: '', escapeReason: '', correctiveAction: '', rectifyDate: '', supervisorOpinion: '' };
} else if (this.deptId === 4) {
return { reviewOpinion: '', approvalResult: 'approved', supplement: '' };
} else { } else {
return { handlingOpinion: '', leaderOpinion: '' }; return { handlingOpinion: '', leaderOpinion: '' };
} }

View File

@@ -5,10 +5,10 @@
<span>流程总览 <span class="en-sub">· Process Overview</span></span> <span>流程总览 <span class="en-sub">· Process Overview</span></span>
</div> </div>
<el-steps :active="activeStep" align-center class="flow-steps"> <el-steps :active="activeStep" align-center class="flow-steps">
<el-step title="待审核" icon="el-icon-document" /> <el-step title="质量部·生产部" icon="el-icon-s-operation" />
<el-step title="意见填写" icon="el-icon-edit-outline" /> <el-step title="陈总审批" icon="el-icon-s-check" />
<el-step title="汇总方案" icon="el-icon-s-data" /> <el-step title="吴部长处理" icon="el-icon-s-custom" />
<el-step title="全部办结" icon="el-icon-circle-check" /> <el-step title="办结" icon="el-icon-circle-check" />
</el-steps> </el-steps>
<div class="current-status"> <div class="current-status">
<span class="status-label">当前阶段</span> <span class="status-label">当前阶段</span>
@@ -33,7 +33,7 @@ export default {
computed: { computed: {
/** /**
* el-steps active 从 0 开始。 * el-steps active 从 0 开始。
* 1=待审核→0, 2=意见填写→1, 3=汇总方案→2, 4=全部办结→3 * 1=技术部·生产部→0, 2=陈总审批→1, 3=吴部长处理→2, 4=办结→3
*/ */
activeStep() { activeStep() {
if (this.flowStatus == null) return -1; if (this.flowStatus == null) return -1;
@@ -43,18 +43,18 @@ export default {
}, },
flowStatusText() { flowStatusText() {
const map = { const map = {
1: '待审核', 1: '质量部·生产部处理中',
2: '意见填写中', 2: '待陈总审批',
3: '待汇总方案', 3: '待吴部长处理',
4: '全部办结' 4: '办结'
}; };
return map[this.flowStatus] || '未知'; return map[this.flowStatus] || '未知';
}, },
tagType() { tagType() {
const map = { const map = {
1: 'info', 1: 'warning',
2: 'warning', 2: 'primary',
3: '', 3: 'warning',
4: 'success' 4: 'success'
}; };
return map[this.flowStatus] || ''; return map[this.flowStatus] || '';

View File

@@ -12,10 +12,10 @@
</div> </div>
<el-select v-model="queryParams.flowStatus" placeholder="全部阶段" clearable size="mini" @change="handleQuery" <el-select v-model="queryParams.flowStatus" placeholder="全部阶段" clearable size="mini" @change="handleQuery"
class="header-filter"> class="header-filter">
<el-option label="待审核" :value="1" /> <el-option label="质量部·生产部处理" :value="1" />
<el-option label="意见填写中" :value="2" /> <el-option label="待陈总审批" :value="2" />
<el-option label="待汇总方案" :value="3" /> <el-option label="待吴部长处理" :value="3" />
<el-option label="全部办结" :value="4" /> <el-option label="办结" :value="4" />
</el-select> </el-select>
</div> </div>
@@ -35,10 +35,10 @@
<span class="item-sub">{{ parseTime(item.complaintDate, '{y}-{m}-{d}') }}</span> <span class="item-sub">{{ parseTime(item.complaintDate, '{y}-{m}-{d}') }}</span>
</div> </div>
<div class="item-meta"> <div class="item-meta">
<el-tag v-if="item.flowStatus === 1" type="info" size="mini">待审核</el-tag> <el-tag v-if="item.flowStatus === 1" type="warning" size="mini">质量部·生产部处理中</el-tag>
<el-tag v-else-if="item.flowStatus === 2" type="warning" size="mini">意见填写中</el-tag> <el-tag v-else-if="item.flowStatus === 2" type="primary" size="mini">待陈总审批</el-tag>
<el-tag v-else-if="item.flowStatus === 3" size="mini">汇总方案</el-tag> <el-tag v-else-if="item.flowStatus === 3" type="warning" size="mini">吴部长处理</el-tag>
<el-tag v-else-if="item.flowStatus === 4" type="success" size="mini">全部办结</el-tag> <el-tag v-else-if="item.flowStatus === 4" type="success" size="mini">办结</el-tag>
</div> </div>
<div class="item-actions"> <div class="item-actions">
<el-button size="mini" type="text" icon="el-icon-download" <el-button size="mini" type="text" icon="el-icon-download"
@@ -72,10 +72,6 @@
<template #actions> <template #actions>
<el-button size="mini" type="text" icon="el-icon-download" <el-button size="mini" type="text" icon="el-icon-download"
@click="handleExportPdf(currentRow)" title="导出PDF">导出</el-button> @click="handleExportPdf(currentRow)" title="导出PDF">导出</el-button>
<el-button v-if="currentRow.flowStatus === 1" size="mini" type="primary" plain
icon="el-icon-s-promotion" @click="handleOpinionDispatch">意见下发</el-button>
<el-button v-if="false" size="mini" type="warning" plain
icon="el-icon-s-promotion" @click="handleFeedbackDispatch">执行下发</el-button>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" <el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail"
title="刷新详情">刷新</el-button> title="刷新详情">刷新</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)">编辑</el-button> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)">编辑</el-button>

View File

@@ -110,7 +110,6 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="form-actions"> <div class="form-actions">
<el-button :loading="rejectLoading" type="danger" @click="handleReject"> </el-button>
<el-button :loading="submitLoading" type="primary" @click="submitOpinion"> </el-button> <el-button :loading="submitLoading" type="primary" @click="submitOpinion"> </el-button>
</div> </div>
</div> </div>
@@ -124,7 +123,7 @@
<script> <script>
import { listComplaintTask, getComplaintTask, updateComplaintTask } from "@/api/flow/complaintTask"; import { listComplaintTask, getComplaintTask, updateComplaintTask } from "@/api/flow/complaintTask";
import { getComplaintAccept, opinionReject } from "@/api/flow/complaintAccept"; import { getComplaintAccept } from "@/api/flow/complaintAccept";
import { listAcceptCoilRel } from "@/api/flow/acceptCoilRel"; import { listAcceptCoilRel } from "@/api/flow/acceptCoilRel";
import DragResizePanel from "@/components/DragResizePanel/index.vue"; import DragResizePanel from "@/components/DragResizePanel/index.vue";
import HeaderControlSection from "./components/HeaderControlSection.vue"; import HeaderControlSection from "./components/HeaderControlSection.vue";
@@ -145,7 +144,6 @@ export default {
detailLoading: false, detailLoading: false,
coilLoading: false, coilLoading: false,
submitLoading: false, submitLoading: false,
rejectLoading: false,
total: 0, total: 0,
queryParams: { queryParams: {
@@ -224,33 +222,19 @@ export default {
}).finally(() => { this.coilLoading = false; }); }).finally(() => { this.coilLoading = false; });
}, },
getDeptLabel() { getDeptLabel() {
const map = { 1: '生产部', 2: '质量部', 3: '销售部' }; const map = { 1: '生产部', 2: '质量部', 3: '销售部(吴部长)', 4: '陈总审批' };
return map[this.queryParams.deptId] || ''; return map[this.queryParams.deptId] || '';
}, },
canEdit(row) { canEdit(row) {
const status = (row.acceptInfo || {}).flowStatus; const status = (row.acceptInfo || {}).flowStatus;
return status === 2; // 只要节点task状态为待处理(0)就可编辑
return status >= 1 && status <= 3;
}, },
confirmCancel() { confirmCancel() {
this.$modal.confirm('确认取消?已填写的内容将不会保存。').then(() => { this.$modal.confirm('确认取消?已填写的内容将不会保存。').then(() => {
this.currentTask = {}; this.currentTask = {};
}).catch(() => {}); }).catch(() => {});
}, },
handleReject() {
this.$prompt('请输入驳回意见', '意见驳回', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'textarea',
inputValidator: (val) => val ? true : '驳回意见不能为空'
}).then(({ value }) => {
this.rejectLoading = true;
return opinionReject(this.currentTask.taskId, value);
}).then(() => {
this.$modal.msgSuccess("驳回成功");
this.getList();
this.currentTask = {};
}).catch(() => { }).finally(() => { this.rejectLoading = false; });
},
submitOpinion() { submitOpinion() {
try { try {
const data = JSON.parse(this.opinionForm.deptOpinion || '{}'); const data = JSON.parse(this.opinionForm.deptOpinion || '{}');
@@ -273,7 +257,7 @@ export default {
taskStatus: 1, taskStatus: 1,
fillTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') fillTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
}).then(() => { }).then(() => {
this.$modal.msgSuccess("意见提交成功"); this.$modal.msgSuccess("已提交,流程已流转至下一节点");
this.getList(); this.getList();
this.currentTask = {}; this.currentTask = {};
}).finally(() => { this.submitLoading = false; }); }).finally(() => { this.submitLoading = false; });

View File

@@ -6,14 +6,14 @@
<div class="panel-header"> <div class="panel-header">
<div class="header-title"> <div class="header-title">
<i class="el-icon-s-data"></i> <i class="el-icon-s-data"></i>
<span>意见汇总</span> <span>陈总审批</span>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" <el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;"
title="刷新列表"></el-button> title="刷新列表"></el-button>
</div> </div>
<el-select v-model="queryParams.flowStatus" placeholder="全部阶段" clearable size="mini" @change="handleQuery" <el-select v-model="queryParams.flowStatus" placeholder="全部阶段" clearable size="mini" @change="handleQuery"
class="header-filter"> class="header-filter">
<el-option label="待汇总方案" :value="3" /> <el-option label="待审批" :value="2" />
<el-option label="全部办结" :value="4" /> <el-option label="办结" :value="4" />
</el-select> </el-select>
</div> </div>
@@ -30,13 +30,13 @@
<span class="item-sub">{{ parseTime(item.complaintDate, '{y}-{m}-{d}') }}</span> <span class="item-sub">{{ parseTime(item.complaintDate, '{y}-{m}-{d}') }}</span>
</div> </div>
<div class="item-meta"> <div class="item-meta">
<el-tag v-if="item.flowStatus === 3" size="mini">汇总方案</el-tag> <el-tag v-if="item.flowStatus === 2" type="primary" size="mini">陈总审批</el-tag>
<el-tag v-else-if="item.flowStatus === 4" type="success" size="mini">全部办结</el-tag> <el-tag v-else-if="item.flowStatus === 4" type="success" size="mini">办结</el-tag>
</div> </div>
</div> </div>
<div v-if="dataList.length === 0 && !loading" class="list-empty"> <div v-if="dataList.length === 0 && !loading" class="list-empty">
<i class="el-icon-folder-opened"></i> <i class="el-icon-folder-opened"></i>
<span>暂无待汇总的售后单</span> <span>暂无待陈总审批的售后单</span>
</div> </div>
</div> </div>
@@ -57,8 +57,6 @@
<HeaderControlSection :complaintNo="currentRow.complaintNo" :flowStatus="currentRow.flowStatus" <HeaderControlSection :complaintNo="currentRow.complaintNo" :flowStatus="currentRow.flowStatus"
:meta="currentRow"> :meta="currentRow">
<template #actions> <template #actions>
<el-button v-if="currentRow.flowStatus === 3" size="mini" type="success" plain
icon="el-icon-circle-check" :loading="completeLoading" @click="handleComplete"> </el-button>
<el-button v-if="currentRow.flowStatus === 4" size="mini" type="primary" plain <el-button v-if="currentRow.flowStatus === 4" size="mini" type="primary" plain
icon="el-icon-download" @click="handleExportPdf">导出</el-button> icon="el-icon-download" @click="handleExportPdf">导出</el-button>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" <el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail"
@@ -85,8 +83,18 @@
<div class="section-gap" /> <div class="section-gap" />
<DepartmentOpinionSection :taskList="taskList" @refresh="refreshTaskList" /> <DepartmentOpinionSection :taskList="taskList" @refresh="refreshTaskList" />
<div class="section-gap" /> <div v-if="currentRow.flowStatus === 2" class="section-gap" />
<HandlingSchemeSection :content="currentRow.planContent" :editable="currentRow.flowStatus === 3" @save="handleSavePlan" /> <div v-if="currentRow.flowStatus === 2" class="approval-section">
<div class="section-title">
<span>审批意见 <span class="en-sub">· Approval Opinion</span></span>
</div>
<el-input v-model="approvalOpinion" type="textarea" :rows="4"
placeholder="请填写审批意见(审阅质量部、生产部意见后)" />
<div class="form-actions">
<el-button type="primary" plain icon="el-icon-circle-check" :loading="completeLoading"
@click="handleComplete">提交审批</el-button>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -98,7 +106,7 @@
<script> <script>
import { listComplaintAccept, getComplaintAccept, updateComplaintAccept } from "@/api/flow/complaintAccept"; import { listComplaintAccept, getComplaintAccept, updateComplaintAccept } from "@/api/flow/complaintAccept";
import { listComplaintTask } from "@/api/flow/complaintTask"; import { listComplaintTask, updateComplaintTask } from "@/api/flow/complaintTask";
import { listAcceptCoilRel } from "@/api/flow/acceptCoilRel"; import { listAcceptCoilRel } from "@/api/flow/acceptCoilRel";
import DragResizePanel from "@/components/DragResizePanel/index.vue"; import DragResizePanel from "@/components/DragResizePanel/index.vue";
import HeaderControlSection from "./components/HeaderControlSection.vue"; import HeaderControlSection from "./components/HeaderControlSection.vue";
@@ -106,7 +114,6 @@ import BasicInfoSection from "./components/BasicInfoSection.vue";
import ContractInfoSection from "./components/ContractInfoSection.vue"; import ContractInfoSection from "./components/ContractInfoSection.vue";
import CoilInfoSection from "./components/CoilInfoSection.vue"; import CoilInfoSection from "./components/CoilInfoSection.vue";
import DepartmentOpinionSection from "./components/DepartmentOpinionSection.vue"; import DepartmentOpinionSection from "./components/DepartmentOpinionSection.vue";
import HandlingSchemeSection from "./components/HandlingSchemeSection.vue";
import FlowOverviewSection from "./components/FlowOverviewSection.vue"; import FlowOverviewSection from "./components/FlowOverviewSection.vue";
import ExportPdfDialog from "./components/ExportPdfDialog.vue"; import ExportPdfDialog from "./components/ExportPdfDialog.vue";
@@ -115,7 +122,7 @@ export default {
components: { components: {
DragResizePanel, DragResizePanel,
HeaderControlSection, BasicInfoSection, ContractInfoSection, HeaderControlSection, BasicInfoSection, ContractInfoSection,
CoilInfoSection, DepartmentOpinionSection, HandlingSchemeSection, FlowOverviewSection, CoilInfoSection, DepartmentOpinionSection, FlowOverviewSection,
ExportPdfDialog ExportPdfDialog
}, },
dicts: ['coil_quality_status'], dicts: ['coil_quality_status'],
@@ -132,10 +139,11 @@ export default {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
complaintNo: undefined, complaintNo: undefined,
flowStatus: 3 flowStatus: 2
}, },
coilList: [], coilList: [],
taskList: [] taskList: [],
approvalOpinion: ''
}; };
}, },
created() { created() {
@@ -182,12 +190,6 @@ export default {
this.loadDetail(this.currentRow.acceptId); this.loadDetail(this.currentRow.acceptId);
} }
}, },
handleSavePlan(planContent) {
updateComplaintAccept({ acceptId: this.currentRow.acceptId, planContent }).then(() => {
this.$modal.msgSuccess("处理方案保存成功");
this.loadDetail(this.currentRow.acceptId);
});
},
refreshTaskList() { refreshTaskList() {
if (this.currentRow && this.currentRow.acceptId) { if (this.currentRow && this.currentRow.acceptId) {
listComplaintTask({ acceptId: this.currentRow.acceptId, pageNum: 1, pageSize: 999 }).then(r => { listComplaintTask({ acceptId: this.currentRow.acceptId, pageNum: 1, pageSize: 999 }).then(r => {
@@ -196,12 +198,34 @@ export default {
} }
}, },
handleComplete() { handleComplete() {
this.$modal.confirm('确认将此售后单办结?办结后部门将无法再修改意见。').then(() => { if (!this.approvalOpinion) {
this.$modal.msgWarning('请填写审批意见');
return;
}
this.$modal.confirm('确认提交审批?提交后流程将流转至吴部长处理。').then(() => {
this.completeLoading = true; this.completeLoading = true;
return updateComplaintAccept({ acceptId: this.currentRow.acceptId, flowStatus: 4 }); // 查找陈总的task (deptId=4)
return listComplaintTask({ acceptId: this.currentRow.acceptId, pageNum: 1, pageSize: 999 });
}).then(r => {
const taskList = r.rows || [];
const chenTask = taskList.find(t => t.deptId === 4);
if (chenTask) {
return updateComplaintTask({
taskId: chenTask.taskId,
acceptId: this.currentRow.acceptId,
deptId: 4,
taskStatus: 1,
deptOpinion: this.approvalOpinion,
fillTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
});
} else {
this.$modal.msgError("未找到陈总审批任务");
throw new Error('no chen task');
}
}).then(() => { }).then(() => {
this.$modal.msgSuccess("已办结"); this.$modal.msgSuccess("审批已提交,已流转至吴部长处理");
this.completeLoading = false; this.completeLoading = false;
this.approvalOpinion = '';
this.loadDetail(this.currentRow.acceptId); this.loadDetail(this.currentRow.acceptId);
this.getList(); this.getList();
}).catch(() => { this.completeLoading = false; }); }).catch(() => { this.completeLoading = false; });
@@ -441,6 +465,15 @@ export default {
color: #3a3a3a !important; color: #3a3a3a !important;
} }
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 16px 0 0;
border-top: 1px solid #e0dcd6;
margin-top: 12px;
}
.right-panel .el-divider--horizontal { .right-panel .el-divider--horizontal {
margin: 8px 0 4px; margin: 8px 0 4px;
background-color: #e0dcd6; background-color: #e0dcd6;