add : 流程图开发

This commit is contained in:
ls
2025-11-05 16:13:46 +08:00
parent a76cd7a32f
commit 712aefd8d3
12 changed files with 738 additions and 161 deletions

View File

@@ -33,6 +33,18 @@ public class FlwInstanceController {
return R.ok();
}
/**
* 发起流程
*/
@RequestMapping("/startFlowInstance1")
public R startFlowInstance1(@RequestParam Map<String, Object> params){
instanceService.startFlowInstance1(params);
return R.ok();
}
/**
* 我的待办
*/
@@ -69,12 +81,49 @@ public class FlwInstanceController {
*/
@PostMapping("/completeFlow")
public R completeFlow(@RequestBody Map<String, Object> params){
R r = instanceService.completeTaskById(params);
return r;
}
/**
* 任务节点1审批通过工单完结
*/
@PostMapping("/completeFlow1")
public R completeFlow1(@RequestBody Map<String, Object> params){
R r = instanceService.completeTaskById1(params);
return r;
}
/**
* 任务节点1无法解决进入下一个节点
*/
@PostMapping("/deleteFlow1")
public R deleteFlow1(@RequestBody Map<String, Object> params){
R r = instanceService.deleteFlow1(params);
return r;
}
/**
* 终结流程
*/
@PostMapping("/deleteFlow")
public R deleteFlow(@RequestBody Map<String, Object> params){
R r = instanceService.deleteFlow(params);
return r;
}
@RequestMapping("/claimTask/{id}")
public R claimTask(@PathVariable("id") String id){
if(StringUtils.isBlank(id)){

View File

@@ -9,10 +9,14 @@ import java.util.Map;
public interface FlwInstanceService {
void startFlowInstance(Map<String, Object> params);
void startFlowInstance1(Map<String, Object> params);
R getTodoTaskList(Map<String, Object> params);
R completeTaskById(Map<String, Object> id);
R completeTaskById1(Map<String, Object> id);
void unclaimTask(String id);
void claimTask(String id);
@@ -26,4 +30,8 @@ public interface FlwInstanceService {
R getMyCompleteTaskList(Map<String, Object> params);
List<FlwHiTaskEntity> getCompleteHiTaskInstance(String id);
R deleteFlow(Map<String, Object> params);
R deleteFlow1(Map<String, Object> params);
}

View File

@@ -25,6 +25,7 @@ import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
@@ -88,7 +89,7 @@ public class FlwInstanceServiceImpl extends FlowServiceNoFactory implements FlwI
public void startFlowInstance(Map<String, Object> params) {
SysUserEntity loginUser = ShiroUtils.getUserEntity();
// 获取需要发起的流程信息
String ticketsId = (String) params.get("ticketsId");
// String ticketsId = (String) params.get("ticketsId");
Long userId = Long.valueOf((String) params.get("user1"));
Map<String, Object> variable = new HashMap<>();
// 结合传递过来的数据动态的绑定流程变量
@@ -98,27 +99,43 @@ public class FlwInstanceServiceImpl extends FlowServiceNoFactory implements FlwI
// 记录流程的发起人
identityService.setAuthenticatedUserId(ShiroUtils.getUserId().toString());
// 启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceById(defId, variable);
try {
ProcessInstance processInstance = runtimeService.startProcessInstanceById(defId, variable);
}catch (FlowableException e){
e.printStackTrace();
}
SysUserEntity user = sysUserService.getById(userId);
//更新工单审批id
TicketsEntity ticketsEntity = new TicketsEntity();
ticketsEntity.setTicketsId(Long.valueOf(ticketsId));
ticketsEntity.setStatus(TicketsStatusEnums.CHECKED.getCode());
ticketsEntity.setProcessInstance(processInstance.getProcessInstanceId());
ticketsEntity.setDeptPeople(user.getNickname());
ticketsEntity.setAssignUserId(userId);
ticketsService.updateById(ticketsEntity);
// TicketsEntity ticketsEntity = new TicketsEntity();
// ticketsEntity.setTicketsId(Long.valueOf(ticketsId));
// ticketsEntity.setStatus(TicketsStatusEnums.CHECKED.getCode());
// ticketsEntity.setProcessInstance(processInstance.getProcessInstanceId());
// ticketsEntity.setDeptPeople(user.getNickname());
// ticketsEntity.setAssignUserId(userId);
// ticketsService.updateById(ticketsEntity);
if (StrUtil.isEmpty(user.getWexinId())){
throw new RRException("企业id为空企业微信消息无法推送");
}
String accessToken = getAccessToken();
sendWeChatMessage(user.getWexinId(),"工单已推送,请登入售后管理系统处理",accessToken,ticketsId);
// sendWeChatMessage(user.getWexinId(),"工单已推送,请登入售后管理系统处理",accessToken,ticketsId);
}
@Override
public void startFlowInstance1(Map<String, Object> params) {
// 启动流程
try {
Map<String, Object> startVars = new HashMap<>();
runtimeService.startProcessInstanceById(defId, startVars); }
catch (FlowableException e){
e.printStackTrace();
}
}
/**
* 获取access_token
*/
@@ -331,7 +348,7 @@ public class FlwInstanceServiceImpl extends FlowServiceNoFactory implements FlwI
@Override
public R completeTaskById(Map<String, Object> params) {
String processInstance = (String) params.get("processInstance");
Integer ticketsId = (Integer) params.get("ticketsId");
// Integer ticketsId = (Integer) params.get("ticketsId");
if(StringUtils.isBlank(processInstance)){
return R.error("流程Id不能为空");
}
@@ -341,15 +358,74 @@ public class FlwInstanceServiceImpl extends FlowServiceNoFactory implements FlwI
taskService.complete(tasks.get(0).getId());
}
//更新工单审批id
TicketsEntity ticketsEntity = new TicketsEntity();
ticketsEntity.setTicketsId(Long.valueOf(ticketsId));
ticketsEntity.setStatus(TicketsStatusEnums.CANCEL.getCode());
ticketsEntity.setUpdateTime(new Date());
ticketsService.updateById(ticketsEntity);
// TicketsEntity ticketsEntity = new TicketsEntity();
// ticketsEntity.setTicketsId(Long.valueOf(ticketsId));
// ticketsEntity.setStatus(TicketsStatusEnums.CANCEL.getCode());
// ticketsEntity.setUpdateTime(new Date());
// ticketsService.updateById(ticketsEntity);
return R.ok("操作成功");
}
@Override
public R completeTaskById1(Map<String, Object> params) {
String processInstance = (String) params.get("processInstance");
if(StringUtils.isBlank(processInstance)){
return R.error("流程Id不能为空");
}
Task secondTask = taskService.createTaskQuery()
.processInstanceId(processInstance)
.singleResult();
Map<String, Object> secondApprovalVars = new HashMap<>();
secondApprovalVars.put("approvalResult", true);
taskService.complete(secondTask.getId(), secondApprovalVars);
return R.ok("操作成功");
}
@Override
public R deleteFlow1(Map<String, Object> params) {
String processInstance = (String) params.get("processInstance");
if(StringUtils.isBlank(processInstance)){
return R.error("流程Id不能为空");
}
Task secondTask = taskService.createTaskQuery()
.processInstanceId(processInstance)
.singleResult();
Map<String, Object> secondApprovalVars = new HashMap<>();
secondApprovalVars.put("approvalResult", false);
secondApprovalVars.put("user1", 2);
taskService.complete(secondTask.getId(), secondApprovalVars);
return R.ok("操作成功");
}
@Override
public R deleteFlow(Map<String, Object> params) {
String processInstance = (String) params.get("processInstance");
// Integer ticketsId = (Integer) params.get("ticketsId");
if(StringUtils.isBlank(processInstance)){
return R.error("流程Id不能为空");
}
//查询活跃任务
TaskQuery taskQuery = taskService.createTaskQuery().active().processInstanceId(processInstance);
List<Task> tasks = taskQuery.list();
if (tasks.isEmpty()){
return R.error("没有可终结的任务");
}
runtimeService.deleteProcessInstance(processInstance, "测试终结流程");
//更新工单审批id
// TicketsEntity ticketsEntity = new TicketsEntity();
// ticketsEntity.setTicketsId(Long.valueOf(ticketsId));
// ticketsEntity.setStatus(TicketsStatusEnums.CANCEL.getCode());
// ticketsEntity.setUpdateTime(new Date());
// ticketsService.updateById(ticketsEntity);
return R.ok("操作成功");
}
@Override
public void unclaimTask(String id) {
taskService.unclaim(id);

View File

@@ -88,7 +88,6 @@ public class FileController {
if(StringUtils.isBlank(procId)|| "null".equals(procId) || "undefined".equals(procId)){
return ;
}
byte[] bytes = instanceService.diagram(procId);
response.setContentType("image/png");
ServletOutputStream outputStream = response.getOutputStream();

View File

@@ -75,11 +75,7 @@ public class TicketsController {
@RequestMapping("/save")
//@RequiresPermissions("tickets:tickets:save")
public R save(@RequestBody TicketsEntity tickets){
SysUserEntity loginUser = ShiroUtils.getUserEntity();
tickets.setCreateTime(new Date());
tickets.setCreateUser(loginUser.getNickname());
ticketsService.save(tickets);
ticketsService.saveTicket(tickets);
return R.ok();
}

View File

@@ -1,5 +1,6 @@
package com.boge.modules.tickets.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.util.Date;
@@ -28,7 +29,7 @@ public class TicketsDTO {
/**
* 合同编号
*/
private String contractNumber;
private String contractCode;
/**
* 客户id
*/
@@ -51,9 +52,9 @@ public class TicketsDTO {
*/
private String deptPhone;
/**
* 创建者ID
* 创建者
*/
private Long createUserId;
private String createUser;
/**
* 创建时间
*/
@@ -65,12 +66,14 @@ public class TicketsDTO {
/**
* 工单状态
*/
private Integer status;
/**
* 工单关闭时间
*/
private Date updateTime;
/**
* 审批流id
*/

View File

@@ -1,5 +1,6 @@
package com.boge.modules.tickets.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -79,6 +80,8 @@ public class TicketsEntity implements Serializable {
*/
private Date updateTime;
/**
* 审批流id
*/

View File

@@ -21,5 +21,7 @@ public interface TicketsService extends IService<TicketsEntity> {
TicketsDTO getTicketsById(String ticketsId);
PageUtils queryPageByType(Map<String, Object> params);
void saveTicket(TicketsEntity tickets);
}

View File

@@ -12,11 +12,17 @@ import com.boge.modules.sys.entity.SysUserEntity;
import com.boge.modules.sys.service.SysUserRoleService;
import com.boge.modules.sys.service.impl.SysUserServiceImpl;
import com.boge.modules.tickets.dto.TicketsDTO;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.boge.modules.tickets.enums.TicketsTypeEnums;
import com.boge.modules.tickets.enums.TicketsStatusEnums;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -45,18 +51,25 @@ public class TicketsServiceImpl extends ServiceImpl<TicketsDao, TicketsEntity> i
@Autowired
private SysUserRoleService sysUserRoleService;
@Autowired
protected RuntimeService runtimeService;
@Value("${ProcessInstance.defId}")
private String defId;
@Override
public PageUtils queryPage(Map<String, Object> params) {
SysUserEntity loginUser = ShiroUtils.getUserEntity();
QueryWrapper<TicketsEntity> ticketsEntityQueryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotEmpty(loginUser)){
if (ObjectUtil.isNotEmpty(loginUser)) {
List<Long> longs = sysUserRoleService.queryRoleIdList(loginUser.getUserId());
//判断是否是超级管理员
if (longs.contains(2L)){
ticketsEntityQueryWrapper.orderBy(true, false,"create_time");
}else {
if (longs.contains(2L)) {
ticketsEntityQueryWrapper.orderBy(true, false, "create_time");
} else {
ticketsEntityQueryWrapper.eq("assign_user_id", loginUser.getUserId())
.orderBy(true, false,"create_time");;
.orderBy(true, false, "create_time");
;
}
}
@@ -71,18 +84,18 @@ public class TicketsServiceImpl extends ServiceImpl<TicketsDao, TicketsEntity> i
public PageUtils queryPageByType(Map<String, Object> params) {
SysUserEntity loginUser = ShiroUtils.getUserEntity();
QueryWrapper<TicketsEntity> ticketsEntityQueryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotEmpty(loginUser)){
if (ObjectUtil.isNotEmpty(loginUser)) {
//判断是否是超级管理员
ticketsEntityQueryWrapper.orderBy(true, false,"create_time");
if (TicketsTypeEnums.ASSIGN.getCode().equals(params.get("type"))){
ticketsEntityQueryWrapper.orderBy(true, false, "create_time");
if (TicketsTypeEnums.ASSIGN.getCode().equals(params.get("type"))) {
ticketsEntityQueryWrapper.eq("create_user", loginUser.getUsername());
}
if (TicketsTypeEnums.TO_BE_DONE.getCode().equals(params.get("type"))){
ticketsEntityQueryWrapper.eq("assign_user_id", loginUser.getUserId()).eq("status",TicketsStatusEnums.CHECKED.getCode());
if (TicketsTypeEnums.TO_BE_DONE.getCode().equals(params.get("type"))) {
ticketsEntityQueryWrapper.eq("assign_user_id", loginUser.getUserId()).eq("status", TicketsStatusEnums.CHECKED.getCode());
}
if (TicketsTypeEnums.FINISH.getCode().equals(params.get("type"))){
ticketsEntityQueryWrapper.eq("assign_user_id", loginUser.getUserId()).eq("status",TicketsStatusEnums.CANCEL.getCode());
if (TicketsTypeEnums.FINISH.getCode().equals(params.get("type"))) {
ticketsEntityQueryWrapper.eq("assign_user_id", loginUser.getUserId()).eq("status", TicketsStatusEnums.CANCEL.getCode());
}
@@ -95,6 +108,22 @@ public class TicketsServiceImpl extends ServiceImpl<TicketsDao, TicketsEntity> i
return new PageUtils(page);
}
@Override
public void saveTicket(TicketsEntity tickets) {
// 启动流程
try {
Map<String, Object> startVars = new HashMap<>();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(defId, startVars);
SysUserEntity loginUser = ShiroUtils.getUserEntity();
tickets.setCreateTime(new Date());
tickets.setCreateUser(loginUser.getNickname());
tickets.setProcessInstance(processInstance.getProcessInstanceId());
ticketsDao.insert(tickets);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public TicketsDTO getTicketsById(String ticketsId) {
TicketsDTO tickets = ticketsDao.getTicketsDTOById(ticketsId);

View File

@@ -8,12 +8,11 @@
<result property="ticketsId" column="tickets_id"/>
<result property="carType" column="car_type"/>
<result property="errorType" column="error_type"/>
<result property="contractId" column="contract_number"/>
<result property="clientId" column="client_id"/>
<result property="description" column="description"/>
<result property="deptPeople" column="dept_people"/>
<result property="deptPhone" column="dept_phone"/>
<result property="createUserId" column="create_user_id"/>
<result property="createUser" column="create_user"/>
<result property="createTime" column="create_time"/>
<result property="isCheck" column="is_check"/>
<result property="status" column="status"/>
@@ -23,8 +22,6 @@
select * from sys_tickets as a
left join sys_client as b on a.client_id = b.client_id
left join sys_car as c on a.car_type = c.car_id
left join sys_contract as d on a.contract_id = d.contract_id
where a.tickets_id = #{ticketsId}
</select>

View File

@@ -1,123 +1,516 @@
<template>
<div class="mod-config">
<el-descriptions title="工单详情" direction="vertical" :column="column" size="mini" border>
<template slot="extra">
<el-button v-if="String(ticketsData.status) === '1'" type="primary" size="small" @click="doOperate">审批</el-button>
</template>
<el-descriptions-item label="工单ID">
<el-tag size="small">{{ ticketsData.ticketsId }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="小车类型">
{{ ticketsData.carName }}
</el-descriptions-item>
<el-descriptions-item label="异常类型">
{{ ticketsData.errorType }}
</el-descriptions-item>
<el-descriptions-item label="合同编号">{{ ticketsData.contractCode }}</el-descriptions-item>
<el-descriptions-item label="客户">{{ ticketsData.clientName }}</el-descriptions-item>
<el-descriptions-item label="工单对接人">{{ ticketsData.deptPeople }}</el-descriptions-item>
<el-descriptions-item label="客户联系电话">{{ ticketsData.deptPhone }}</el-descriptions-item>
<el-descriptions-item label="创建者">{{ ticketsData.createUser}}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ ticketsData.createTime }}</el-descriptions-item>
<el-descriptions-item label="是否验收">{{ ['否', '是'][Number(ticketsData.isCheck)] }}</el-descriptions-item>
<el-descriptions-item label="工单状态">{{ statusOpt | findByValue(ticketsData.status) }}</el-descriptions-item>
<el-descriptions-item label="工单审批完成时间">{{ ticketsData.updateTime }}</el-descriptions-item>
<el-descriptions-item label="故障描述">{{ ticketsData.description }}</el-descriptions-item>
</el-descriptions>
<!-- 页面标题栏 -->
<div class="page-header">
<div class="title">工单详情</div>
<div class="meta-info">
<span class="creator">{{ ticketsData.createUser }} </span>
<span class="create-time">{{ ticketsData.createTime }}</span>
</div>
</div>
<!-- 主内容区左侧表单 + 右侧审批栏 -->
<div class="main-container">
<!-- 左侧表单区域 -->
<div class="form-container">
<!-- 公司标识 -->
<div class="company-logo">NOBLELIFT 诺力</div>
<!-- 工单表单标题 -->
<div class="form-title">工单信息</div>
<!-- 基本信息区块 -->
<div class="form-block">
<div class="block-title">基本信息</div>
<table class="info-table">
<tbody>
<tr>
<td class="label">工单ID</td>
<td class="value"><el-tag size="small">{{ ticketsData.ticketsId }}</el-tag></td>
<td class="label">小车类型</td>
<td class="value">{{ ticketsData.carName || '-' }}</td>
</tr>
<tr>
<td class="label">异常类型</td>
<td class="value">{{ ticketsData.errorType || '-' }}</td>
<td class="label">合同编号</td>
<td class="value">{{ ticketsData.contractCode || '-' }}</td>
</tr>
<tr>
<td class="label">客户</td>
<td class="value">{{ ticketsData.clientName || '-' }}</td>
<td class="label">工单对接人</td>
<td class="value">{{ ticketsData.deptPeople || '-' }}</td>
</tr>
<tr>
<td class="label">客户联系电话</td>
<td class="value">{{ ticketsData.deptPhone || '-' }}</td>
<td class="label">创建者</td>
<td class="value">{{ ticketsData.createUser || '-' }}</td>
</tr>
<tr>
<td class="label">创建时间</td>
<td class="value">{{ ticketsData.createTime || '-' }}</td>
<td class="label">是否验收</td>
<td class="value">{{ ['否', '是'][Number(ticketsData.isCheck)] || '-' }}</td>
</tr>
<tr>
<td class="label">工单状态</td>
<td class="value">{{ statusOpt | findByValue(ticketsData.status) || '-' }}</td>
<td class="label">审批完成时间</td>
<td class="value">{{ ticketsData.updateTime || '-' }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 故障描述区块 -->
<div class="form-block">
<div class="block-title">故障描述</div>
<div class="description-content">
{{ ticketsData.description || '无故障描述信息' }}
</div>
</div>
</div>
<!-- 右侧审批栏 -->
<div class="approval-sidebar">
<div class="sidebar-content">
<div class="sidebar-header">审批</div>
<!-- 审批操作按钮组 -->
<div class="approval-tools">
<el-upload
ref="upload"
:action="$baseUrl + '/api/localStorage' + '?name=' + dataForm.filename"
:limit="1"
:headers="headers"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
:on-remove="handleRemove"
:auto-upload="false"
>
<el-button slot="trigger" size="mini" type="primary">上传附件</el-button>
<div slot="tip" class="el-upload__tip">可上传任意格式文件且不超过100M</div>
</el-upload>
</div>
<!-- 处理意见输入 -->
<div class="opinion-input">
<el-input
type="textarea"
v-model="approvalForm.opinion"
placeholder="请输入处理意见"
rows="6"
>
<template slot="prepend">
<el-button type="text" icon="el-icon-edit" size="mini"></el-button>
<el-button type="text" icon="el-icon-at" size="mini"></el-button>
<el-button type="text" size="mini">常用语</el-button>
</template>
</el-input>
</div>
<!-- 附加选项 -->
<div class="approval-options">
<el-checkbox v-model="approvalForm.hideOpinion">意见隐藏</el-checkbox>
<div class="option-item">
<el-checkbox v-model="approvalForm.track">跟踪</el-checkbox>
<el-radio-group v-model="approvalForm.trackType" v-if="approvalForm.track">
<el-radio label="全部" border size="small">全部</el-radio>
<el-radio label="指定人" border size="small">指定人</el-radio>
</el-radio-group>
</div>
<el-checkbox v-model="approvalForm.archiveAfterHandle">处理后归档</el-checkbox>
</div>
<!-- 底部操作按钮固定在底部 -->
<div class="approval-actions">
<el-button size="small" @click="saveAsDraft">存为草稿</el-button>
<el-button size="small" @click="saveForLater">暂存待办</el-button>
<el-button type="primary" size="small" @click="submitApproval">提交</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
column: 0,
ticketsData: {},
statusOpt: [{value: '0', label: '未开始'}, {value: '1', label: '已指派'}, {value: '2', label: '处理中'}, {value: '3', label: '已完成'}]
}
},
created () {
if (this.isMobile() || window.innerWidth < 768) {
this.column = 2
} else {
this.column = 4
}
this.getDataList()
},
methods: {
isMobile() {
const userAgentInfo = navigator.userAgent;
const mobileAgents = [
"Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"
];
let isMobile = false;
for (let i = 0; i < mobileAgents.length; i++) {
if (userAgentInfo.indexOf(mobileAgents[i]) > -1) {
isMobile = true;
break;
}
}
return isMobile;
export default {
data() {
return {
column: 0,
ticketsData: {},
statusOpt: [
{ value: '0', label: '未开始' },
{ value: '1', label: '已指派' },
{ value: '2', label: '处理中' },
{ value: '3', label: '已完成' }
],
// 审批表单数据
approvalForm: {
result: '同意', // 默认为同意
opinion: '', // 处理意见
hideOpinion: false, // 意见隐藏
track: false, // 跟踪
trackType: '全部', // 跟踪类型
archiveAfterHandle: false // 处理后归档
},
// 获取数据列表
getDataList () {
this.$http({
url: this.$http.adornUrl(`/tickets/tickets/info/${this.$route.query.id}`),
method: 'get',
params: this.$http.adornParams({})
}).then(({data}) => {
if (data && data.code === 0) {
this.ticketsData = data.tickets
}
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
doOperate () {
this.$confirm(`确定进行审批操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/flw/instance/completeFlow'),
method: 'post',
data: this.$http.adornData({ticketsId: this.ticketsData.ticketsId, processInstance: this.ticketsData.processInstance})
}).then(({data}) => {
if (data && data.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.$router.push('/tickets-tickets')
}
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => {})
// 上传文件
dataForm: {
contractId: 0,
contractType: '',
isMaster: 0,
contractNumber: '',
clientId: '',
isAcceptance: 0,
remarks: '',
fileNo: '',
totalSum: 0,
filename: null,
storageId: null
}
}
},
created() {
// 初始化布局
this.column = this.isMobile() || window.innerWidth < 768 ? 2 : 4;
this.getDataList();
},
methods: {
// 移动端判断
isMobile() {
const userAgentInfo = navigator.userAgent;
const mobileAgents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
return mobileAgents.some(agent => userAgentInfo.indexOf(agent) > -1);
},
// 上传文件
beforeUpload(file) {
let isLt2M = true
isLt2M = file.size / 1024 / 1024 < 100
if (!isLt2M) {
this.$message.error('上传文件大小不能超过 100MB!')
return
}
this.dataForm.filename = file.name
return isLt2M
},
handleSuccess(response, file, fileList) {
this.$notify({title: '上传成功',type: 'success'})
this.dataForm.storageId = response.storageId
},
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 2500
})
},
handleRemove (file, fileList) {
this.dataForm.storageId = null
},
// 获取工单数据
getDataList() {
this.$http({
url: this.$http.adornUrl(`/tickets/tickets/info/${this.$route.query.id}`),
method: 'get',
params: this.$http.adornParams({})
}).then(({ data }) => {
if (data && data.code === 0) {
this.ticketsData = data.tickets;
}
});
},
// 提交审批
submitApproval() {
if (!this.approvalForm.opinion && this.approvalForm.result === '不同意') {
this.$message.warning('不同意时请填写处理意见');
return;
}
this.$confirm(`确定${this.approvalForm.result}该工单?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/flw/instance/completeFlow'),
method: 'post',
data: this.$http.adornData({
ticketsId: this.ticketsData.ticketsId,
processInstance: this.ticketsData.processInstance,
result: this.approvalForm.result,
opinion: this.approvalForm.opinion
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.$router.push('/tickets-tickets');
}
});
} else {
this.$message.error(data.msg || '操作失败');
}
});
}).catch(() => {});
},
// 存为草稿
saveAsDraft() {
this.$message.success('已保存为草稿');
},
// 暂存待办
saveForLater() {
this.$message.success('已暂存待办');
},
// 分页相关方法(未使用可删除)
sizeChangeHandle(val) {
this.pageSize = val;
this.pageIndex = 1;
this.getDataList();
},
currentChangeHandle(val) {
this.pageIndex = val;
this.getDataList();
}
}
};
</script>
<style scoped>
.mod-config {
padding: 30px 30px;
padding: 20px;
background-color: #f5f5f5;
min-height: 100vh;
}
.el-pagination {
margin-top: 15px;
/* 页面标题栏 */
.page-header {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e5e5e5;
}
.title {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.meta-info {
color: #666;
font-size: 14px;
}
.meta-info .creator {
margin-right: 15px;
}
/* 主内容区布局:强制两侧高度一致 */
.main-container {
display: flex;
gap: 20px;
align-items: stretch; /* 核心:左右两侧高度强制对齐 */
}
/* 左侧表单容器:高度随内容撑开,作为右侧高度基准 */
.form-container {
flex: 1;
background-color: #fff;
padding: 30px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid #eee; /* 新增边框,与右侧视觉统一 */
}
/* 公司标识 */
.company-logo {
font-size: 24px;
font-weight: bold;
color: #f37b1d; /* 诺力橙色 */
text-align: center;
margin-bottom: 20px;
}
/* 表单标题 */
.form-title {
text-align: center;
font-size: 20px;
font-weight: bold;
margin-bottom: 30px;
color: #333;
}
/* 表单区块样式 */
.form-block {
margin-bottom: 25px;
border: 1px solid #ddd;
}
.block-title {
background-color: #f8f0e3; /* 浅橙色背景 */
color: #333;
padding: 8px 15px;
font-weight: bold;
border-bottom: 1px solid #ddd;
}
/* 表格样式 */
.info-table {
width: 100%;
border-collapse: collapse;
}
.info-table tr {
border-bottom: 1px solid #f0f0f0;
}
.info-table td {
padding: 12px 15px;
vertical-align: middle;
}
.info-table .label {
width: 25%;
background-color: #fafafa;
font-weight: 500;
text-align: right;
padding-right: 20px;
border-right: 1px solid #f0f0f0;
}
.info-table .value {
width: 25%;
padding-left: 20px;
}
/* 故障描述样式 */
.description-content {
padding: 15px;
line-height: 1.6;
min-height: 80px;
}
/* 右侧审批栏:与左侧高度完全一致,优化内容分布 */
.approval-sidebar {
background-color: #fff;
padding: 30px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid #eee; /* 与左侧统一边框 */
align-self: stretch; /* 强制高度与左侧一致 */
}
/* 审批栏内部容器用flex分布内容底部按钮固定 */
.sidebar-content {
display: flex;
flex-direction: column;
height: 100%; /* 占满审批栏高度 */
gap: 15px; /* 统一内部元素间距 */
}
.sidebar-header {
font-size: 16px;
font-weight: bold;
padding: 10px 0;
border-bottom: 1px solid #e5e5e5;
color: #333;
margin-bottom: 5px; /* 缩减间距,更紧凑 */
}
/* 审批工具栏 */
.approval-tools {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 5px; /* 缩减间距 */
}
/* 审批结果选择 */
.approval-result {
display: flex;
gap: 10px;
margin-bottom: 5px;
}
.approval-result .el-radio {
flex: 1;
}
/* 意见输入框:占满中间空间 */
.opinion-input {
flex: 1; /* 让输入框自适应填充中间高度 */
min-height: 120px; /* 确保输入框有足够高度 */
}
/* 审批选项 */
.approval-options {
margin-bottom: 10px;
}
.option-item {
display: flex;
align-items: center;
gap: 10px;
margin: 8px 0; /* 缩减选项间距 */
}
.option-item .el-radio-group {
display: flex;
gap: 10px;
}
.approval-options .el-checkbox {
display: block;
margin: 8px 0; /* 缩减复选框间距 */
}
/* 底部操作按钮:固定在审批栏底部 */
.approval-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: auto; /* 推到最底部 */
padding-top: 15px;
border-top: 1px solid #eee; /* 分隔线,参考图片样式 */
}
/* 响应式适配 */
@media (max-width: 992px) {
.main-container {
flex-direction: column;
}
.approval-sidebar {
width: 100%;
margin-top: 20px; /* 垂直排列时增加间距 */
}
.info-table .label, .info-table .value {
width: 50%;
}
}
@media (max-width: 576px) {
.form-container, .approval-sidebar {
padding: 15px;
}
.info-table td {
display: block;
width: 100% !important;
text-align: left !important;
}
.info-table .label {
border-right: none;
padding-right: 0;
background-color: transparent;
font-weight: bold;
}
.info-table .value {
padding-left: 0;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px dashed #eee;
}
}
</style>

View File

@@ -148,7 +148,7 @@
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.ticketsId)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.ticketsId)">删除</el-button>
<el-button v-if="!scope.row.status" type="text" size="small" @click="startFlowHandle(scope.row.ticketsId)">指派</el-button>
<el-button type="text" size="small" @click="startFlowHandle(scope.row.processInstance)">流程进度</el-button>
<el-button type="text" size="small" @click="$router.push(`/tickets-detail?id=${scope.row.ticketsId}`)">详情</el-button>
</template>
</el-table-column>
@@ -162,6 +162,16 @@
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 流程图弹框 -->
<el-dialog
title="流程进度图"
:visible.sync="dialogVisible"
width="80%"
style="max-height: 80vh; overflow: auto;"
>
<img :src="flowImgUrl" alt="流程进度图" style="width:100%;" />
</el-dialog>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" :dictData="dictData" @refreshDataList="getDataList"></add-or-update>
<el-dialog
@@ -215,6 +225,8 @@
export default {
data () {
return {
dialogVisible: false, // 弹框显隐控制
flowImgUrl: '', // 流程图图片URL
dataForm: {
deptPeople: '',
errorType: '',
@@ -227,10 +239,11 @@
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
dictConfigs: [{url: '/car/car/list', type: 'list', value: 'carId', label: 'carName'}, {type: 'dict', code: 'error_type'}, {url: '/flow/contract/list', type: 'list', value: 'contractId', label: 'contractNumber'}, {url: '/client/client/list', type: 'list', value: 'clientId', label: 'clientName'}],
dictConfigs: [{url: '/car/car/list', type: 'list', value: 'carId', label: 'carName'}, {type: 'dict', code: 'error_type'}, {url: '/flow/projectContract/list', type: 'list', value: 'contractId', label: 'contractNumber'}, {url: '/client/client/list', type: 'list', value: 'clientId', label: 'clientName'}],
dictData: [],
dialogFormVisible: false,
flowForm:{ticketsId: null},
flowFormInsId:{processInstance: null},
dynamiForm:[],
users: [],
roles: [],
@@ -321,21 +334,30 @@
})
}).catch(() => {})
},
startFlowHandle(id){
this.flowForm = {}
this.flowForm.ticketsId = id
startFlowHandle(processInstance) {
// 传递流程实例ID作为参数假设后端需要的参数名为procid
this.$http({
url: this.$http.adornUrl('/flow/deploy/flowDef'),
url: this.$http.adornUrl('/file/fileController/downloadFlowActiveImg'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 0) {
this.dynamiForm = [...data.data]
this.users = [...data.users]
this.roles = [...data.roles]
this.dialogFormVisible = true
params: this.$http.adornParams({
procId: processInstance // 关键传递流程实例ID
}),
responseType: 'blob' // 关键声明响应类型为二进制blob
}).then((response) => {
// 成功获取二进制图片数据
this.flowImgUrl = URL.createObjectURL(response.data); // 转换为可展示的URL
this.dialogVisible = true; // 显示弹框
}).catch((error) => {
// 错误处理可能返回JSON格式的错误信息
console.error('获取流程图失败', error);
// 尝试解析错误信息若后端返回JSON错误
if (error.response && error.response.data) {
const errorData = error.response.data;
this.$message.error(errorData.msg || '获取流程图失败,请稍后重试');
} else {
this.$message.error('获取流程图失败,请稍后重试');
}
})
});
},
submitStartFlow(){
// 提交表单数据