提交 2bc4bc79 authored 作者: liuluyu's avatar liuluyu

更新计划执行流程

上级 4fa74ba6
import { defHttp } from '/@/utils/http/axios';
// 我的发起的流程
export function myProcessList(query) {
return defHttp.get({
url: '/flowable/task/myProcess',
params: query
})
params: query,
});
}
export function flowFormData(query) {
alert(JSON.stringify(query))
return defHttp.get({
url: '/flowable/task/flowFormData',
params: query
})
params: query,
});
}
export function flowTaskInfo(query) {
return defHttp.get({
url: '/flowable/task/flowTaskInfo',
params: query
})
params: query,
});
}
// 完成任务
export function complete(data) {
return defHttp.post({
url: '/flowable/task/complete',
data: data
})
data: data,
});
}
// 取消申请
export function stopProcess(data) {
return defHttp.post({
url: '/flowable/task/stopProcess',
data: data
})
data: data,
});
}
// 驳回任务
export function rejectTask(data) {
return defHttp.post({
url: '/flowable/task/reject',
data: data
})
data: data,
});
}
// 可退回任务列表
export function returnList(data) {
return defHttp.post({
url: '/flowable/task/returnList',
data: data
})
data: data,
});
}
// 部署流程实例
export function deployStart(deployId) {
return defHttp.get({
url: '/flowable/process/startFlow',
params: {deployId:deployId}
})
params: { deployId: deployId },
});
}
// 查询流程定义详细
export function getDeployment(id) {
return defHttp.get({
url: '/system/deployment',
params: {id:id}
})
params: { id: id },
});
}
// 新增流程定义
export function addDeployment(data) {
return defHttp.post({
url: '/system/deployment',
data: data
})
data: data,
});
}
// 修改流程定义
export function updateDeployment(data) {
return defHttp.post({
url: '/system/deployment',
data: data
})
data: data,
});
}
// 删除流程定义
export function delDeployment(id) {
return defHttp.delete({
url: '/system/deployment',
params: {id:id}
})
params: { id: id },
});
}
// 导出流程定义
export function exportDeployment(query) {
return defHttp.get({
url: '/system/deployment/export',
params: query
})
params: query,
});
}
/**
* 计划执行表单状态管理
* 用于在不同页面之间共享表单保存方法
*/
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const usePlanFormStore = defineStore('planForm', () => {
// 存储表单的 ref 回调
const submitCallback = ref<() => Promise<any> | null>(null);
const formData = ref<any>(null);
/**
* 注册表单提交回调(在 StPlanExcuteForm 中调用)
* @param callback - 表单的 submitForm 方法
*/
const registerSubmitCallback = (callback: () => Promise<any>) => {
submitCallback.value = callback;
console.log('[PlanFormStore] 已注册表单保存回调');
};
/**
* 注册表单数据(用于跨页面访问)
* @param data - 表单数据
*/
const setFormData = (data: any) => {
formData.value = data;
};
/**
* 获取表单数据
*/
const getFormData = () => {
return formData.value;
};
/**
* 执行表单保存(在 TodoIndex 中调用)
*/
const submitPlanForm = async () => {
if (!submitCallback.value) {
console.warn('[PlanFormStore] 未注册表单保存回调');
return null;
}
try {
console.log('[PlanFormStore] 开始执行表单保存...');
const result = await submitCallback.value();
console.log('[PlanFormStore] 表单保存成功:', result);
return result;
} catch (error) {
console.error('[PlanFormStore] 表单保存失败:', error);
throw error;
}
};
/**
* 清空回调(可选)
*/
const clearCallback = () => {
submitCallback.value = null;
console.log('[PlanFormStore] 已清空表单保存回调');
};
return {
submitCallback,
formData,
registerSubmitCallback,
setFormData,
getFormData,
submitPlanForm,
clearCallback,
};
});
......@@ -28,20 +28,16 @@
<div class="next-node-info">
<span class="next-node-label">下一节点:</span>
<a-select
v-model:value="nextNodeNameSele"
placeholder="请选择下一个节点"
style="width: 100%"
@change="handleChange"
v-if="shownextNodeNameSele"
>
<a-select-option
v-for="item in tableOptions"
:key="item.id"
:value="item.value"
v-model:value="nextNodeNameSele"
placeholder="请选择下一个节点"
style="width: 100%"
@change="handleChange"
v-if="shownextNodeNameSele"
>
{{ item.name }}
</a-select-option>
</a-select>
<a-select-option v-for="item in tableOptions" :key="item.id" :value="item.value">
{{ item.name }}
</a-select-option>
</a-select>
<a-tag color="blue" class="next-node-tag" v-if="shownextNodeNametext">
{{ nextNodeName || '等待确定下一节点' }}
......@@ -216,7 +212,7 @@
// API
import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { complete, flowTaskForm, getNextFlowNode,getMyTaskFlow} from '/@/components/Process/api/todo';
import { complete, flowTaskForm, getNextFlowNode, getMyTaskFlow } from '/@/components/Process/api/todo';
import { flowTaskInfo } from '/@/components/Process/api/process';
// 组件
......@@ -229,6 +225,7 @@
import { useForm, BasicForm } from '/@/components/Form';
import { useModal } from '/@/components/Modal';
import dayjs from 'dayjs';
import { usePlanFormStore } from '/@/store/modules/planFormStore';
// 响应式数据
const activeName = ref('1');
......@@ -240,7 +237,6 @@
const loadingFlowChart = ref(false);
const formKey = ref(0);
const showApprovalUi = ref(true);
const flowData = ref<any>({});
const flowRecordList = ref<any[]>([]);
......@@ -253,7 +249,7 @@
const shownextNodeNameSele = ref(true);
const shownextNodeNametext = ref(false);
const res0 = ref();
const res0 = ref();
const res1 = ref();
const res3 = ref();
......@@ -281,6 +277,7 @@
// 弹窗
const refApprove = ref(null);
const refReject = ref(null);
const planFormStore = usePlanFormStore();
const [registerApprove, { openModal: openApproveModal }] = useModal();
const [registerReject, { openModal: openRejectModal }] = useModal();
......@@ -393,7 +390,6 @@
let { taskId } = taskForm;
if (!taskId) {
// try to resolve taskId by business id from workFlowData
const dataId = workFlowData.value?.dataId || workFlowData.value?.id || workFlowData.value?.businessId;
......@@ -417,7 +413,7 @@
const resData = await flowTaskForm({ taskId });
const { flowForm = {} } = resData;
formTp.value = flowForm.formTp;
if (flowForm.formTp == '1') {
await refCruInnerForm.value.iniData(flowForm);
} else {
......@@ -427,7 +423,7 @@
const connector = flowForm.formUrl.includes('?') ? '&' : '?';
formUrl.value = flowForm.formUrl + connector + formUrlpar + '=' + formUrlparval;
//formUrl.value = flowForm.formUrl + '?' + formUrlpar + '=' + formUrlparval;
console.error('表单url:', formUrl.value);
console.error('表单url:', formUrl.value);
}
formError.value = false;
} catch (error) {
......@@ -509,6 +505,18 @@
if (sending.value) return;
try {
sending.value = true;
// 如果有计划执行表单,先保存表单数据
try {
const result = await planFormStore.submitPlanForm();
if (result) {
message.success('执行计划已保存');
}
} catch (error) {
console.warn('执行计划保存出错,继续发送任务:', error);
// 继续发送任务,不中断流程
}
const submitData = {
instanceId: taskForm.instanceId,
deployId: taskForm.deployId,
......@@ -517,38 +525,37 @@
values: {},
};
//找到选择的下一步节点
if(nextNodenum.value>1){
submitData.values['userTaskid'] = nextNodeNameSelevue.value;
if (nextNodenum.value > 1) {
submitData.values['userTaskid'] = nextNodeNameSelevue.value;
}
if (!showApprovalUi.value) {
await complete(submitData);
emit('callback');
return false;
}
const formData = await validate();
Object.assign(submitData, formData);
submitData.comment = submitData.comment || '';
//alert("dd"+isFixed.value);
// if (!isFixed.value) {
//alert("dd"+userType.value);
if (userType.value === 'user') {
//alert("dd"+isFixed.value);
// if (!isFixed.value) {
//alert("dd"+userType.value);
if (userType.value === 'user') {
submitData.values['approval'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['approval'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['approval'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
}
} else if (formData.checkSendRole) {
submitData.values['approval'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
// }
console.log("执行发送 ",submitData);
} else if (formData.checkSendRole) {
submitData.values['approval'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
// }
console.log('执行发送 ', submitData);
// 执行发送
const result = await complete(submitData);
......@@ -582,60 +589,54 @@ console.log("执行发送 ",submitData);
};
const handleChange = (value: string) => {
console.log(`selected ${value}`);
nextNodeNameSelevue.value=value;
console.log(" nextNodeNameSelevue.value ",nextNodeNameSelevue.value);
if(value==0){
ChangeSelectNodeAfter(res0);
}
if(value==1){
ChangeSelectNodeAfter(res1);
}
if(value==2){
ChangeSelectNodeAfter(res2);
}
console.log(`selected ${value}`);
nextNodeNameSelevue.value = value;
console.log(' nextNodeNameSelevue.value ', nextNodeNameSelevue.value);
if (value == 0) {
ChangeSelectNodeAfter(res0);
}
if (value == 1) {
ChangeSelectNodeAfter(res1);
}
if (value == 2) {
ChangeSelectNodeAfter(res2);
}
};
function ChangeSelectNodeAfter(res) {
isFixed.value = res.dataType == 'fixed' ? true : false;
if (isFixed.value && !isApproval.value) {
showApprovalUi.value;
}
function ChangeSelectNodeAfter(res) {
isFixed.value = res.dataType == 'fixed' ? true : false;
if (isFixed.value && !isApproval.value) {
showApprovalUi.value;
}
nextNode.value = res;
nextNodeName.value = res.userTask?.name || '';
nextNode.value = res;
nextNodeName.value = res.userTask?.name || '';
//alert(""+res.dataType);
//alert(""+res.dataType);
if (res.type === 'role') {
removeSchemaByFiled('checkSendUser');
if (res.dataType === 'fixed' && res.userTask?.candidateGroups) {
setFieldsValue({
checkSendRole: res.userTask.candidateGroups,
});
}
} else {
removeSchemaByFiled('checkSendRole');
if (res.dataType === 'fixed' && res.userTask?.assignee) {
setFieldsValue({
checkSendUser: res.userTask.assignee,
});
}
}
}
if (res.type === 'role') {
removeSchemaByFiled('checkSendUser');
if (res.dataType === 'fixed' && res.userTask?.candidateGroups) {
setFieldsValue({
checkSendRole: res.userTask.candidateGroups,
});
}
} else {
removeSchemaByFiled('checkSendRole');
if (res.dataType === 'fixed' && res.userTask?.assignee) {
setFieldsValue({
checkSendUser: res.userTask.assignee,
});
}
}
}
const iniData = async (data: any) => {
try {
// 设置工作流数据
workFlowData.value = data;
console.log("why iniData",data);
console.log('why iniData', data);
// 更新任务表单
Object.assign(taskForm, {
......@@ -657,69 +658,59 @@ console.log("执行发送 ",submitData);
if (taskForm.taskId) {
const reslist = await getNextFlowNode({ taskId: taskForm.taskId });
console.log("getNextFlowNode reslist ",reslist);
console.log('getNextFlowNode reslist ', reslist);
if (reslist == null) {
nextNodeName.value = '结束';
removeSchemaByFiled('checkSendRole');
removeSchemaByFiled('checkSendUser');
showApprovalUi.value = isApproval.value;
shownextNodeNameSele.value =false;
shownextNodeNametext.value =true;
return false;
}else{
shownextNodeNameSele.value =true;
shownextNodeNametext.value =false;
nextNodenum.value=reslist.length;
//why工作流修改
// 使用 for 循环赋值
for (let i = 0; i <reslist.length; i++) {
let resb = reslist[i];
shownextNodeNameSele.value = false;
shownextNodeNametext.value = true;
// 注意:不要在这里 return,要让代码继续执行后续的初始化
} else {
shownextNodeNameSele.value = true;
shownextNodeNametext.value = false;
nextNodenum.value = reslist.length;
//why工作流修改
// 使用 for 循环赋值
for (let i = 0; i < reslist.length; i++) {
let resb = reslist[i];
tableOptions.value.push({
id: `${i}`,
name: `${resb.userTask.name}`,
value: `${i}`
value: `${i}`,
});
}
nextNodeNameSele.value = tableOptions.value[0].name;
// tableOptions.value = res.userTaskList;
const res = reslist[0]; // 获取第一行数据
console.log("res ",res);
ChangeSelectNodeAfter(res);
if(reslist.length==1){
res0.value = reslist[0];
}
if(reslist.length==2){
res0.value = reslist[0];
res1.value = reslist[1];
}
if(reslist.length==3){
res0.value = reslist[0];
res1.value = reslist[1];
res2.value = reslist[2];
}
}
// tableOptions.value = res.userTaskList;
const res = reslist[0]; // 获取第一行数据
console.log('res ', res);
ChangeSelectNodeAfter(res);
if (reslist.length == 1) {
res0.value = reslist[0];
}
if (reslist.length == 2) {
res0.value = reslist[0];
res1.value = reslist[1];
}
if (reslist.length == 3) {
res0.value = reslist[0];
res1.value = reslist[1];
res2.value = reslist[2];
}
}
}
// 加载流转记录
await loadFlowRecord();
// 加载当前表单
if (activeName.value === '1') {
await initializeCruInnerForm();
}
// 主动初始化表单(不依赖标签页状态,确保抽屉打开时表单立即显示)
await initializeCruInnerForm();
} catch (error) {
console.error('初始化数据失败:', error);
message.error('初始化失败,请刷新页面重试');
......
......@@ -100,7 +100,7 @@
class="custom-class"
root-class-name="root-class-name"
:root-style="{ color: 'blue' }"
title="流程详情111"
title="流程详情"
placement="right"
width="90%"
style="margin: 0px; padding: 0px"
......
<template>
<div style="background-color: #fff; padding: 100px">
<a-form ref="formRef" :model="formData" :label-col="{ span: 4 }" :wrapper-col="{ span: 8 }">
<!-- 执行状态 -->
<a-form-item label="执行状态" prop="executeStatus">
<a-select v-model:value="formData.executeStatus" placeholder="请选择执行状态">
<a-select-option value="0">未开始</a-select-option>
<a-select-option value="1">进行中</a-select-option>
<a-select-option value="2">已完成</a-select-option>
<a-select-option value="3">已暂停</a-select-option>
</a-select>
</a-form-item>
<!-- 实际开始时间 -->
<a-form-item label="实际开始时间" prop="actualStartTime">
<a-date-picker v-model="formData.actualStartTime" type="datetime" placeholder="选择时间"></a-date-picker>
</a-form-item>
<!-- 实际结束时间 -->
<a-form-item label="实际结束时间" prop="actualEndTime">
<a-date-picker v-model="formData.actualEndTime" type="datetime" placeholder="选择时间"></a-date-picker>
</a-form-item>
<!-- 执行记录 -->
<a-form-item label="执行记录" prop="executeRecord">
<a-textarea v-model="formData.executeRecord" :rows="4" placeholder="请输入执行记录"></a-textarea>
</a-form-item>
<!-- 附件 -->
<a-form-item label="附件" prop="attachments">
<!-- <JUpload v-model:value="formModel.fileUploadPath" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." :disabled="isDetail" /> -->
</a-form-item>
<!-- 操作按钮 -->
<a-form-item :wrapper-col="{ offset: 6 }">
<a-button type="primary" @click="submitForm">保存</a-button>
<a-button style="margin-left: 40px" @click="resetForm">重置</a-button>
</a-form-item>
</a-form>
<a-spin :spinning="loading">
<a-form ref="formRef" :model="formData" :label-col="{ span: 4 }" :wrapper-col="{ span: 8 }">
<!-- 执行状态 -->
<a-form-item label="执行状态" prop="executeStatus">
<a-select v-model:value="formData.executeStatus" placeholder="请选择执行状态">
<a-select-option value="0">未开始</a-select-option>
<a-select-option value="1">进行中</a-select-option>
<a-select-option value="2">已完成</a-select-option>
<a-select-option value="3">已暂停</a-select-option>
</a-select>
</a-form-item>
<!-- 实际开始时间 -->
<a-form-item label="实际开始时间" prop="actualStartTime">
<a-date-picker v-model:value="formData.actualStartTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
</a-form-item>
<!-- 实际结束时间 -->
<a-form-item label="实际结束时间" prop="actualEndTime" :rules="endDateRules">
<a-date-picker v-model:value="formData.actualEndTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
</a-form-item>
<!-- 执行记录 -->
<a-form-item label="执行记录" prop="executeEcord">
<a-textarea v-model:value="formData.executeEcord" :rows="4" placeholder="请输入执行记录"></a-textarea>
</a-form-item>
<!-- 附件 -->
<a-form-item label="附件" prop="attachments">
<JUpload v-model:value="formData.attachments" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." :disabled="isDetail" />
</a-form-item>
<!-- 操作按钮 -->
<a-form-item :wrapper-col="{ offset: 6 }">
<a-button type="primary" @click="submitForm" :loading="submitting">保存</a-button>
<a-button style="margin-left: 40px" @click="resetForm" :disabled="submitting">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
</div>
</template>
<script setup>
import { ref } from 'vue';
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue';
import { message } from 'ant-design-vue';
import dayjs from 'dayjs';
import { saveOrUpdate } from '../StPlanMan.api';
import { defHttp } from '/@/utils/http/axios';
import { useRoute } from 'vue-router';
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
import { usePlanFormStore } from '/@/store/modules/planFormStore';
const route = useRoute();
const formRef = ref();
const loading = ref(false);
const submitting = ref(false);
const planId = ref('');
const planFormStore = usePlanFormStore();
const formData = ref({
id: '',
executeStatus: '',
actualStartTime: '',
actualEndTime: '',
executeRecord: '',
actualStartTime: null,
actualEndTime: null,
executeEcord: '',
attachments: [],
});
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
console.log('Form submitted:', formData.value);
// 提交逻辑
// 监听formData变化,用于调试
watch(
() => formData.value,
(newVal) => {
// 调试用,删除时可注释
},
{ deep: true }
);
// 安全解析JSON
const safeJsonParse = (str) => {
if (!str) return [];
if (Array.isArray(str)) return str;
try {
return JSON.parse(str);
} catch (e) {
return [];
}
};
// 初始化表单数据
const initFormData = async () => {
try {
loading.value = true;
// 从URL获取id
const id = route.query.id as string;
if (!id) {
message.warning('未获取到计划ID');
return;
}
planId.value = id;
formData.value.id = id;
// 根据id查询现有数据
const timestamp = new Date().getTime();
const queryUrl = '/plan.main/stPlanMan/queryById';
const data = await defHttp.get({
url: queryUrl,
params: { id, _t: timestamp },
});
// 使用查询到的数据初始化表单,只保留执行相关字段
if (data) {
formData.value = {
id: data.id || '',
executeStatus: data.executeStatus || '',
actualStartTime: data.actualStartTime ? dayjs(data.actualStartTime) : null,
actualEndTime: data.actualEndTime ? dayjs(data.actualEndTime) : null,
executeEcord: data.executeEcord || '',
// 安全解析附件字段(可能是字符串或数组)
attachments: safeJsonParse(data.attachments),
};
}
} catch (error) {
message.error('初始化表单数据失败');
} finally {
loading.value = false;
}
};
// 结束日期验证规则
const endDateRules = [
{
validator: (rule, value) => {
if (!value) {
return Promise.resolve();
}
if (!formData.value.actualStartTime) {
return Promise.resolve();
}
// 比较日期:结束日期不能早于开始日期
const endTime = dayjs(value);
const startTime = dayjs(formData.value.actualStartTime);
if (endTime.isBefore(startTime, 'day')) {
return Promise.reject(new Error('结束日期不能早于开始日期'));
}
return Promise.resolve();
},
trigger: 'change',
},
];
const submitForm = async () => {
try {
// 验证表单
try {
await formRef.value.validate();
} catch (validateError) {
return;
}
if (!formData.value.id) {
message.error('缺少计划ID,无法保存');
return;
}
submitting.value = true;
// 格式化日期为字符串(仅保留日期,不含时分秒)
const formatDate = (date) => {
if (!date) return '';
// 如果是Dayjs对象
if (date && typeof date.format === 'function') {
return date.format('YYYY-MM-DD');
}
// 如果是Date对象
if (date instanceof Date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 如果已是字符串
return String(date);
};
// 准备提交数据,将数组类型转换为字符串,日期格式化
const submitData = {
id: formData.value.id,
executeStatus: formData.value.executeStatus || '',
actualStartTime: formatDate(formData.value.actualStartTime),
actualEndTime: formatDate(formData.value.actualEndTime),
executeEcord: formData.value.executeEcord || '',
// 将附件数组转换为JSON字符串(如果是数组)或保留原值(如果已是字符串)
attachments: Array.isArray(formData.value.attachments) ? JSON.stringify(formData.value.attachments) : formData.value.attachments || '',
};
// 调用 saveOrUpdate 接口,isUpdate=true表示执行更新操作
const response = await saveOrUpdate(submitData, true);
if (response) {
console.log(response);
}
});
} catch (error) {
console.error('保存失败:', error);
// message.error('保存失败,请检查表单输入');
} finally {
submitting.value = false;
}
};
const resetForm = () => {
formRef.value.resetFields();
// 重置为初始化的数据
initFormData();
};
// 1 接收 id 2 保存调用 StPlanMan.api 的 saveOrUpdate
// 组件挂载时初始化表单
onMounted(() => {
initFormData();
// 向 Pinia store 注册表单保存方法
planFormStore.registerSubmitCallback(submitForm);
console.log('[StPlanExcuteForm] 已向 store 注册表单保存回调');
});
// 暴露方法给外部组件调用
defineExpose({
submitForm,
resetForm,
initFormData,
});
</script>
<style scoped></style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论