提交 c2023d3b authored 作者: liuluyu's avatar liuluyu

更新流程记录表单

上级 5cf77944
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
<!-- <template #footer> <!-- <template #footer>
<div class="drawer-footer"> <div class="drawer-footer">
<a-button @click="handleClose">取消123</a-button> <a-button @click="handleClose">取消</a-button>
<a-button type="primary" :loading="submitLoading" @click="handleSubmit"> 提交 </a-button> <a-button type="primary" :loading="submitLoading" @click="handleSubmit"> 提交 </a-button>
</div> </div>
</template> --> </template> -->
......
...@@ -89,12 +89,10 @@ ...@@ -89,12 +89,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue'; import { ref, reactive, nextTick } from 'vue';
import { FileTextOutlined, UserOutlined, CalendarOutlined, ClockCircleOutlined, FormOutlined } from '@ant-design/icons-vue'; import { FileTextOutlined, UserOutlined, CalendarOutlined, ClockCircleOutlined, FormOutlined } from '@ant-design/icons-vue';
import { flowRecord } from '/@/components/Process/api/finished'; import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition'; import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { findFlowFormVal } from '/@/components/Process/api/todo'; import { findFlowFormVal } from '/@/components/Process/api/todo';
import { flowFormData } from '/@/components/Process/api/process'; import { flowFormData } from '/@/components/Process/api/process';
import BpmnViewer from '/@/components/Process/viewer/index.vue'; import BpmnViewer from '/@/components/Process/viewer/index.vue';
import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue'; import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue';
...@@ -133,7 +131,6 @@ ...@@ -133,7 +131,6 @@
const refInnerForm = ref(); const refInnerForm = ref();
// 遮罩层 // 遮罩层
const loading = ref(true);
const flowRecordList = ref<FlowRecordItem[]>([]); const flowRecordList = ref<FlowRecordItem[]>([]);
const taskForm = reactive<TaskForm>({ const taskForm = reactive<TaskForm>({
returnTaskShow: false, returnTaskShow: false,
......
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</a-card> </a-card>
<ShowFormModal @register="registerShowFormModal" @submit="handleShowFormSubmit" /> <ShowFormModal @register="registerShowFormModal" />
</div> </div>
</template> </template>
...@@ -229,36 +229,55 @@ ...@@ -229,36 +229,55 @@
}; };
const showNodeFormData = (data) => { const showNodeFormData = (data) => {
// 调试:打印传入的数据结构
console.log('showNodeFormData 传入数据:', data);
console.log('showNodeFormData taskDataObj:', taskDataObj.value);
// Build workflowNodes list for the drawer. Adjust component paths as needed. // Build workflowNodes list for the drawer. Adjust component paths as needed.
const nodes = [ const nodes = [
{ id: 'stplanman', name: '主表单', formUrl: '/views/project/plan/components/StPlanManForm.vue' }, { id: 'stplanman', name: '主表单', formUrl: '/project/plan/components/StPlanManForm.vue' },
{ id: 'stplanexcute', name: '执行表单', formUrl: '/views/project/plan/components/StPlanExcuteForm.vue' }, { id: 'stplanexcute', name: '执行表单', formUrl: '/project/plan/components/StPlanExcuteForm.vue' },
]; ];
// pick which node to show — customize the rule as necessary // pick which node to show — customize the rule as necessary
const currentIndex = (data?.taskName || '').includes('执行') ? 1 : 0; const currentIndex = (data?.taskName || '').includes('执行') ? 1 : 0;
// externalFormData: map node id -> form data object // 获取当前节点
const externalFormData = { const currentNode = nodes[currentIndex];
[nodes[currentIndex].id]: data?.data || {},
// 获取业务数据ID:优先级为 id -> dataId -> procInsId
let businessDataId = taskDataObj.value?.id || taskDataObj.value?.dataId || taskDataObj.value?.procInsId;
console.log('获取到的业务数据ID:', businessDataId);
// 准备表单数据
const formData = {
id: businessDataId || '',
// 将taskDataObj中的数据合并到formData中
...taskDataObj.value,
}; };
console.log('准备的表单数据:', formData);
// externalFormData: map node id -> form data object
const externalFormData = {};
// 所有节点都传递相同的formData,包括可编辑节点和只读节点
nodes.forEach((node) => {
externalFormData[node.id] = formData;
});
console.log('传递给ShowFormModal的externalFormData:', externalFormData);
showFormModalApi.openModal(true, { showFormModalApi.openModal(true, {
workflowNodes: nodes, workflowNodes: nodes,
currentNodeIndex: currentIndex, currentNodeIndex: currentIndex,
externalFormData, externalFormData,
title: data?.taskName || '表单', title: data?.taskName || '表单',
showFooter: true, showFooter: false,
}); });
}; };
// Handle submit event emitted from ShowFormModal
const handleShowFormSubmit = (payload) => {
console.log('ShowFormModal submit payload:', payload);
// payload contains: { nodeId, nodeName, formData, procDefId, formComponent }
// Add your handling logic here (e.g., call API, close modal, refresh list)
};
defineExpose({ defineExpose({
iniData, iniData,
}); });
......
...@@ -8,10 +8,10 @@ ...@@ -8,10 +8,10 @@
<!-- 表单主体 --> <!-- 表单主体 -->
<div class="form-body"> <div class="form-body">
<a-form ref="formRef" :model="formData" layout="vertical" class="styled-form"> <a-form ref="formRef" :model="formFields" layout="vertical" class="styled-form">
<!-- 执行状态 --> <!-- 执行状态 -->
<a-form-item label="执行状态" name="executeStatus"> <a-form-item label="执行状态" name="executeStatus">
<a-select v-model:value="formData.executeStatus" placeholder="请选择执行状态"> <a-select v-model:value="formFields.executeStatus" placeholder="请选择执行状态">
<a-select-option value="0"> <a-select-option value="0">
<span class="status-option"> <span class="status-option">
<span class="status-indicator pending"></span> <span class="status-indicator pending"></span>
...@@ -42,22 +42,22 @@ ...@@ -42,22 +42,22 @@
<!-- 时间选择区域 --> <!-- 时间选择区域 -->
<div class="form-row"> <div class="form-row">
<a-form-item label="实际开始时间" name="actualStartTime" class="form-item-half"> <a-form-item label="实际开始时间" name="actualStartTime" class="form-item-half">
<a-date-picker v-model:value="formData.actualStartTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker> <a-date-picker v-model:value="formFields.actualStartTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
</a-form-item> </a-form-item>
<a-form-item label="实际结束时间" name="actualEndTime" class="form-item-half" :rules="endDateRules"> <a-form-item label="实际结束时间" name="actualEndTime" class="form-item-half" :rules="endDateRules">
<a-date-picker v-model:value="formData.actualEndTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker> <a-date-picker v-model:value="formFields.actualEndTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
</a-form-item> </a-form-item>
</div> </div>
<!-- 执行记录 --> <!-- 执行记录 -->
<a-form-item label="执行记录" name="executeEcord"> <a-form-item label="执行记录" name="executeEcord">
<a-textarea v-model:value="formData.executeEcord" :rows="3" placeholder="请输入执行记录和详细说明..." show-count :maxlength="500" /> <a-textarea v-model:value="formFields.executeEcord" :rows="3" placeholder="请输入执行记录和详细说明..." show-count :maxlength="500" />
</a-form-item> </a-form-item>
<!-- 附件上传 --> <!-- 附件上传 -->
<a-form-item label="相关附件" name="attachments"> <a-form-item label="相关附件" name="attachments">
<JUpload v-model:value="formData.attachments" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." /> <JUpload v-model:value="formFields.attachments" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." />
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>
...@@ -90,7 +90,8 @@ ...@@ -90,7 +90,8 @@
const planFormStore = usePlanFormStore(); const planFormStore = usePlanFormStore();
const isInitialized = ref(false); const isInitialized = ref(false);
const formData = reactive({ // 本地表单数据 - 使用 formFields 作为内部状态
const formFields = reactive({
id: '', id: '',
executeStatus: '', executeStatus: '',
actualStartTime: null as any, actualStartTime: null as any,
...@@ -100,19 +101,76 @@ ...@@ -100,19 +101,76 @@
}); });
const props = defineProps({ const props = defineProps({
initialData: { formData: {
// 改名:不使用 formData 作为 prop 名
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
disabled: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
}); });
// 安全日期转换函数 - 从字符串或Date或dayjs对象转换为dayjs对象
const safeDateParse = (dateValue) => {
if (!dateValue) return null;
// 如果已经是dayjs对象,直接返回
if (dateValue && typeof dateValue.format === 'function' && dateValue.isValid && dateValue.isValid()) {
return dateValue;
}
// 如果是Date对象,转换为dayjs
if (dateValue instanceof Date) {
return dayjs(dateValue);
}
// 如果是字符串,尝试解析
if (typeof dateValue === 'string' && dateValue.trim()) {
try {
const parsed = dayjs(dateValue);
if (parsed.isValid()) {
return parsed;
}
} catch (e) {
console.error('日期解析失败:', dateValue, e);
return null;
}
}
return null;
};
// 安全解析JSON
const safeJsonParse = (str) => {
if (!str) return [];
if (Array.isArray(str)) return str;
try {
return JSON.parse(str);
} catch (e) {
return [];
}
};
// sync incoming prop into internal reactive formData // sync incoming prop into internal reactive formData
watch( watch(
() => props.initialData, () => props.formData,
(v) => { (v) => {
if (v && Object.keys(v).length > 0) { if (v && Object.keys(v).length > 0) {
Object.assign(formData, v); const preparationData = {
id: v.id || '',
executeStatus: v.executeStatus || '',
actualStartTime: safeDateParse(v.actualStartTime),
actualEndTime: safeDateParse(v.actualEndTime),
executeEcord: v.executeEcord || '',
attachments: safeJsonParse(v.attachments),
};
Object.assign(formFields, preparationData);
console.log('从props同步表单数据:', preparationData);
} }
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
...@@ -121,25 +179,20 @@ ...@@ -121,25 +179,20 @@
// 监听formData变化,用于调试 // 监听formData变化,用于调试
watch( watch(
() => formData, () => formFields,
(newVal) => {}, (newVal) => {},
{ deep: true, immediate: true } { deep: true, immediate: 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 (id?: string) => { const initFormData = async (id?: string) => {
try { try {
// 优先检查props.formData中是否已有完整数据和id
if (props.formData && props.formData.id && Object.keys(props.formData).length > 1) {
console.log('表单数据已从props传入,使用props数据,跳过queryById');
return; // 数据已通过watch同步到formData
}
// 检查是否已正在加载,避免重复请求 // 检查是否已正在加载,避免重复请求
if (loading.value && planId.value === (id || route.query.id)) { if (loading.value && planId.value === (id || route.query.id)) {
return; return;
...@@ -151,8 +204,8 @@ ...@@ -151,8 +204,8 @@
// 从参数或URL获取id // 从参数或URL获取id
const targetId = id || (route.query.id as string); const targetId = id || (route.query.id as string);
if (!targetId) { if (!targetId) {
message.warning('未获取到计划ID');
console.warn('[v0] 未获取到计划ID, id参数:', id, '路由ID:', route.query.id); console.warn('[v0] 未获取到计划ID, id参数:', id, '路由ID:', route.query.id);
loading.value = false;
return; return;
} }
...@@ -172,14 +225,14 @@ ...@@ -172,14 +225,14 @@
const newFormData = { const newFormData = {
id: data.id || '', id: data.id || '',
executeStatus: data.executeStatus || '', executeStatus: data.executeStatus || '',
actualStartTime: data.actualStartTime ? dayjs(data.actualStartTime) : null, actualStartTime: safeDateParse(data.actualStartTime),
actualEndTime: data.actualEndTime ? dayjs(data.actualEndTime) : null, actualEndTime: safeDateParse(data.actualEndTime),
executeEcord: data.executeEcord || '', executeEcord: data.executeEcord || '',
// 安全解析附件字段(可能是字符串或数组) // 安全解析附件字段(可能是字符串或数组)
attachments: safeJsonParse(data.attachments), attachments: safeJsonParse(data.attachments),
}; };
// 直接替换整个 ref 值,确保触发响应式更新 // 直接替换整个 ref 值,确保触发响应式更新
Object.assign(formData, newFormData); Object.assign(formFields, newFormData);
// 强制等待 DOM 更新 // 强制等待 DOM 更新
await nextTick(); await nextTick();
planFormStore.updateFormDataCache(newFormData); planFormStore.updateFormDataCache(newFormData);
...@@ -187,7 +240,10 @@ ...@@ -187,7 +240,10 @@
} }
} catch (error: any) { } catch (error: any) {
console.error('初始化表单数据失败:', error); console.error('初始化表单数据失败:', error);
// 只有在从URL获取id时才提示错误
if (id || route.query.id) {
message.error('初始化表单数据失败'); message.error('初始化表单数据失败');
}
planFormStore.formLoadingState.isError = true; planFormStore.formLoadingState.isError = true;
planFormStore.formLoadingState.errorMessage = error?.message || '加载失败'; planFormStore.formLoadingState.errorMessage = error?.message || '加载失败';
} finally { } finally {
...@@ -205,12 +261,12 @@ ...@@ -205,12 +261,12 @@
if (!value) { if (!value) {
return Promise.resolve(); return Promise.resolve();
} }
if (!formData.actualStartTime) { if (!formFields.actualStartTime) {
return Promise.resolve(); return Promise.resolve();
} }
// 比较日期:结束日期不能早于开始日期 // 比较日期:结束日期不能早于开始日期
const endTime = dayjs(value); const endTime = dayjs(value);
const startTime = dayjs(formData.actualStartTime); const startTime = dayjs(formFields.actualStartTime);
if (endTime.isBefore(startTime, 'day')) { if (endTime.isBefore(startTime, 'day')) {
return Promise.reject(new Error('结束日期不能早于开始日期')); return Promise.reject(new Error('结束日期不能早于开始日期'));
} }
...@@ -229,7 +285,7 @@ ...@@ -229,7 +285,7 @@
return; return;
} }
if (!formData.id) { if (!formFields.id) {
message.error('缺少计划ID,无法保存'); message.error('缺少计划ID,无法保存');
return; return;
} }
...@@ -260,13 +316,13 @@ ...@@ -260,13 +316,13 @@
// 准备提交数据,将数组类型转换为字符串,日期格式化 // 准备提交数据,将数组类型转换为字符串,日期格式化
const submitData = { const submitData = {
id: formData.id, id: formFields.id,
executeStatus: formData.executeStatus || '', executeStatus: formFields.executeStatus || '',
actualStartTime: formatDate(formData.actualStartTime), actualStartTime: formatDate(formFields.actualStartTime),
actualEndTime: formatDate(formData.actualEndTime), actualEndTime: formatDate(formFields.actualEndTime),
executeEcord: formData.executeEcord || '', executeEcord: formFields.executeEcord || '',
// 将附件数组转换为JSON字符串(如果是数组)或保留原值(如果已是字符串) // 将附件数组转换为JSON字符串(如果是数组)或保留原值(如果已是字符串)
attachments: Array.isArray(formData.attachments) ? JSON.stringify(formData.attachments) : formData.attachments || '', attachments: Array.isArray(formFields.attachments) ? JSON.stringify(formFields.attachments) : formFields.attachments || '',
}; };
// 调用 saveOrUpdate 接口,isUpdate=true表示执行更新操作 // 调用 saveOrUpdate 接口,isUpdate=true表示执行更新操作
...@@ -275,7 +331,7 @@ ...@@ -275,7 +331,7 @@
if (response) { if (response) {
// message.success('保存成功'); // message.success('保存成功');
// 更新缓存 // 更新缓存
planFormStore.updateFormDataCache(formData); planFormStore.updateFormDataCache(formFields);
} }
} catch (error: any) { } catch (error: any) {
console.error('保存失败:', error); console.error('保存失败:', error);
...@@ -297,6 +353,14 @@ ...@@ -297,6 +353,14 @@
// 等待 iframe DOM 完全渲染 // 等待 iframe DOM 完全渲染
await nextTick(); await nextTick();
// 优先检查props.formData是否已有完整数据
if (props.formData && props.formData.id && Object.keys(props.formData).length > 1) {
console.log('props.formData已有完整数据,直接使用,不再查询接口');
initFormData();
planFormStore.registerSubmitCallback(submitForm);
return;
}
// 从 URL 直接解析 id 参数(iframe 场景) // 从 URL 直接解析 id 参数(iframe 场景)
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const idFromUrl = urlParams.get('id'); const idFromUrl = urlParams.get('id');
...@@ -352,7 +416,7 @@ ...@@ -352,7 +416,7 @@
resetForm, resetForm,
initFormData, initFormData,
isInitialized, isInitialized,
getFormData: () => toRaw(formData), getFormData: () => toRaw(formFields),
}); });
</script> </script>
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { BasicForm, useForm } from '/@/components/Form/index'; import { BasicForm, useForm } from '/@/components/Form/index';
import { computed, ref, onMounted, watchEffect, toRaw, watch } from 'vue'; import { computed, ref, onMounted, watchEffect, toRaw, watch, nextTick } from 'vue';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { getBpmFormSchema } from '../StPlanMan.data'; import { getBpmFormSchema } from '../StPlanMan.data';
import { saveOrUpdate } from '../StPlanMan.api'; import { saveOrUpdate } from '../StPlanMan.api';
...@@ -76,13 +76,26 @@ ...@@ -76,13 +76,26 @@
// watch incoming prop and populate the form when provided // watch incoming prop and populate the form when provided
watch( watch(
() => props.formData, () => props.formData,
(v) => { async (v) => {
if (v && Object.keys(v).length > 0) { if (v && Object.keys(v).length > 0) {
formData.value = { ...v }; formData.value = { ...v };
// 延迟调用setFieldsValue,确保表单已经初始化
// 当选项卡切换时,表单可能还没有完全准备好
await nextTick();
try { try {
setFieldsValue(v); await setFieldsValue(v);
console.log('setFieldsValue 成功:', v);
} catch (e) { } catch (e) {
console.warn('setFieldsValue failed:', e); console.warn('setFieldsValue failed (first attempt):', e);
// 如果第一次失败,延迟后重试
await new Promise((resolve) => setTimeout(resolve, 100));
try {
await setFieldsValue(v);
console.log('setFieldsValue 成功 (retry):', v);
} catch (e2) {
console.warn('setFieldsValue failed (retry):', e2);
}
} }
} }
}, },
...@@ -145,12 +158,25 @@ ...@@ -145,12 +158,25 @@
// 初始化表单数据 // 初始化表单数据
const initFormData = async () => { const initFormData = async () => {
try { try {
// 如果props.formData已经有数据,说明是从流程表单打开,不需要再调用接口
if (props.formData && Object.keys(props.formData).length > 0 && props.formData.id) {
console.log('表单数据已从props传入,跳过queryById接口调用');
return;
}
loading.value = true; loading.value = true;
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
let tid = toRaw(route.query).id; let tid = toRaw(route.query).id;
// 如果URL中也没有id,则不调用接口
if (!tid) {
console.warn('未找到id参数,既非URL query参数也非props传入');
loading.value = false;
return;
}
const queryByIdUrl = '/plan.main/stPlanMan/queryById'; const queryByIdUrl = '/plan.main/stPlanMan/queryById';
const data = await defHttp.get({ const data = await defHttp.get({
url: queryByIdUrl, url: queryByIdUrl,
...@@ -167,7 +193,10 @@ ...@@ -167,7 +193,10 @@
await setProps({ disabled: formDisabled.value }); await setProps({ disabled: formDisabled.value });
} catch (error) { } catch (error) {
console.error('初始化表单数据失败:', error); console.error('初始化表单数据失败:', error);
// 只有在非预期情况下才提示错误
if (!props.formData?.id) {
createMessage.error('初始化表单数据失败'); createMessage.error('初始化表单数据失败');
}
} finally { } finally {
loading.value = false; loading.value = false;
} }
...@@ -192,7 +221,12 @@ ...@@ -192,7 +221,12 @@
}; };
onMounted(() => { onMounted(() => {
// 如果formData已经通过props传入,则不需要再调用initFormData
if (props.formData && Object.keys(props.formData).length > 0 && props.formData.id) {
console.log('form data已通过props传入,跳过接口调用');
} else {
initFormData(); initFormData();
}
}); });
// expose getFormData so parent modal can read current values // expose getFormData so parent modal can read current values
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论