631 lines
18 KiB
Vue
Raw Normal View History

2025-12-08 20:03:34 +08:00
<template>
<BasicLayout>
2025-12-20 21:46:38 +08:00
<!-- 统一的滚动容器包含编辑组件和流程组件 -->
<scroll-view scroll-y class="unified-scroll">
<!-- 引入编辑组件不包含滚动 -->
<JfEdit v-if="isLoginReady" ref="jfEditRef" :jf-id="jfId" @saved="handleDataSaved" @loaded="handleJfEditLoaded" />
<!-- 审批流程展示 -->
<view class="flow-section" v-if="jfId">
<LcglSp :yw-id="jfId" yw-type="JF" :key="jfId" />
2025-12-08 20:03:34 +08:00
</view>
2025-12-20 21:46:38 +08:00
</scroll-view>
2025-12-08 20:03:34 +08:00
<template #bottom>
<YwConfirm
v-if="showButton"
2025-12-20 21:46:38 +08:00
:spApi="wrappedJfSpApi"
:stopApi="wrappedJfStopApi"
:transferApi="wrappedJfTransferApi"
2025-12-08 20:03:34 +08:00
:params="spParams"
2025-12-20 21:46:38 +08:00
:autoToMessage="false"
2025-12-08 20:03:34 +08:00
:showXt="false"
2025-12-20 21:46:38 +08:00
:showReject="!isYiban"
:showTransfer="false"
:showApprove="!isYiban"
2025-12-08 20:03:34 +08:00
:showReturn="false"
:showStop="true"
:showXtDk="false"
2025-12-20 21:46:38 +08:00
approveText="通过"
@submit="handleSubmit"
@reject="handleReject"
@transfer="handleTransfer"
@stop="handleStop"
2025-12-08 20:03:34 +08:00
/>
</template>
2025-12-20 21:46:38 +08:00
<!-- 遮罩层 -->
<view v-if="loading" class="loading-mask">
<view class="loading-content">
<view class="loading-spinner"></view>
<text class="loading-text">{{ loadingText }}</text>
</view>
</view>
2025-12-08 20:03:34 +08:00
</BasicLayout>
</template>
<script setup lang="ts">
2025-12-20 21:46:38 +08:00
import { onLoad, onBackPress } from "@dcloudio/uni-app";
2025-12-08 20:03:34 +08:00
import { ref, computed } from "vue";
import dayjs from "dayjs";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
import LcglSp from "@/components/LcglSp/index.vue";
import YwConfirm from "@/pages/components/YwConfirm/index.vue";
2025-12-20 21:46:38 +08:00
import JfEdit from "./JfEdit.vue";
import { jfFlowByIdApi, jfSpApi, jfTransferApi, jfStopApi, jfSaveApi } from "@/api/base/jfApi";
import { xxtsFindByIdApi } from "@/api/base/xxtsApi";
import { useUserStore } from "@/store/modules/user";
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
const { loginByOpenId, getJs } = useUserStore();
2025-12-08 20:03:34 +08:00
const jfId = ref<string>("");
const jfInfo = ref<any>({});
const showButton = ref<boolean>(false);
2025-12-20 21:46:38 +08:00
const isLoginReady = ref<boolean>(false); // 登录是否准备就绪
// 编辑组件的引用,用于获取表单数据
const jfEditRef = ref<any>(null);
// 遮罩层状态
const loading = ref<boolean>(false);
const loadingText = ref<string>("处理中...");
// 数据加载状态
const jfInfoLoaded = ref<boolean>(false); // 流程数据是否已加载
const jfEditLoaded = ref<boolean>(false); // 编辑组件数据是否已加载
// 是否已办dbZt === "B" 表示已办)
const dbZtFromUrl = ref<string>("");
// 直接用一个 ref 保存 xxtsId
const xxtsId = ref<string>("");
const isYiban = computed(() => {
return dbZtFromUrl.value === "B";
});
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 审批参数(只包含审批相关字段,业务字段通过 save 接口保存)
const spParams = computed(() => {
return {
xxtsId: xxtsId.value,
ywId: jfId.value,
// 业务字段已通过 save 接口保存,这里不再传递
};
});
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 读取流程详情(用于审批时获取最新数据)
2025-12-08 20:03:34 +08:00
const getJfInfo = async () => {
try {
const res: any = await jfFlowByIdApi({ id: jfId.value });
if (res && res.resultCode === 1 && res.result) {
jfInfo.value = res.result.jfInfo || {};
const spResult = jfInfo.value?.spResult;
2025-12-20 21:46:38 +08:00
const spJd = jfInfo.value?.spJd;
// 已完结或驳回:隐藏审批按钮
if (spResult === "C" || (spJd === "Z" && spResult === "B")) {
showButton.value = false;
} else if (spResult && spResult !== "A" && dbZtFromUrl.value === "A") {
// 历史逻辑:当流程已完成且待办状态关闭时,不显示审批按钮
2025-12-08 20:03:34 +08:00
showButton.value = false;
} else {
showButton.value = true;
}
} else {
showButton.value = false;
}
2025-12-20 21:46:38 +08:00
jfInfoLoaded.value = true;
checkAllDataLoaded();
2025-12-08 20:03:34 +08:00
} catch (error) {
console.error("获取积分流程失败:", error);
showButton.value = false;
2025-12-20 21:46:38 +08:00
jfInfoLoaded.value = true;
checkAllDataLoaded();
2025-12-08 20:03:34 +08:00
}
};
2025-12-20 21:46:38 +08:00
// 获取编辑组件的表单数据
const getJfEditFormData = () => {
if (!jfEditRef.value) {
console.warn('JfEdit 组件引用不存在');
return null;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
// 通过 ref 访问子组件的 formData
const formData = jfEditRef.value.formData;
if (!formData) {
console.warn('无法从 JfEdit 组件获取 formData');
return null;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
console.log('获取到表单数据:', formData);
return formData;
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 保存编辑组件数据(审批前调用)
const saveJfDataBeforeApprove = async () => {
console.log('开始保存编辑组件数据...');
console.log('jfEditRef.value:', jfEditRef.value);
// 显示遮罩层
loading.value = true;
loadingText.value = '正在保存数据...';
try {
// 等待一下,确保组件已挂载
await new Promise(resolve => setTimeout(resolve, 100));
const formData = getJfEditFormData();
if (!formData) {
console.error('无法获取表单数据');
loading.value = false;
return { success: false, message: '无法获取表单数据,请确保编辑组件已加载' };
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
// 验证必要字段
if (!formData.jfTypeId) {
console.warn('表单数据缺少 jfTypeId');
loading.value = false;
return { success: false, message: '请先选择业绩类别' };
}
const jfTypeId = typeof formData.jfTypeId === 'object' && formData.jfTypeId !== null
? formData.jfTypeId.value
: formData.jfTypeId;
let hjtime = formData.hjtime;
if (hjtime && hjtime.length === 7 && hjtime.match(/^\d{4}-\d{2}$/)) {
hjtime = hjtime + '-01';
}
let fileUrl = '';
let fileName = '';
let fileFormat = '';
if (formData.attachments && formData.attachments.length > 0) {
const urls: string[] = [];
const names: string[] = [];
const exts: string[] = [];
formData.attachments.forEach((att: any) => {
if (att.url) urls.push(att.url);
if (att.name) names.push(att.name);
const ext = (att.name || '').split('.').pop()?.toLowerCase() || '';
if (ext) exts.push(ext);
});
fileUrl = urls.join(',');
fileName = names.join(',');
fileFormat = exts.join(',');
}
// 获取当前用户信息,设置考核人
const js = getJs;
let khJsId = '';
let khjsxm = '';
if (js) {
khJsId = js.id || '';
khjsxm = js.jsxm || js.xm || '';
}
// 设置考核时间为今天
const today = new Date();
const khTime = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const submitData: any = {
...formData,
id: jfId.value, // 确保有ID
jfTypeId: jfTypeId,
hjtime: hjtime,
fileUrl: fileUrl,
fileName: fileName,
fileFormat: fileFormat,
khJsId: khJsId,
khjsxm: khjsxm,
khTime: khTime,
};
delete submitData.attachments;
// 处理学生获奖名单:转换为 studentList 格式(只保存有效状态的学生)
// 注意:需要判断是否需要保存学生名单(根据 showStudentAward 或 formData 中的标识)
if (formData.hjmdList && Array.isArray(formData.hjmdList) && formData.hjmdList.length > 0) {
const validStudents = formData.hjmdList.filter((s: any) => s.status !== 'D');
submitData.studentList = validStudents.map((student: any) => {
// 如果没有 bc根据 njbc、njmc、bjmc 组合生成
let bc = student.bc;
if (!bc && (student.njmc || student.njbc || student.bjmc)) {
const parts: string[] = [];
if (student.njmc) parts.push(student.njmc);
if (student.njbc) parts.push(`(${student.njbc})`);
if (student.bjmc) parts.push(student.bjmc);
bc = parts.join('');
}
return {
id: student.id, // 编辑时保留ID
xsId: student.xsId,
xsxm: student.xsxm,
njId: student.njId,
njmcId: student.njmcId,
bjId: student.bjId,
njbc: student.njbc,
njmc: student.njmc,
bjmc: student.bjmc,
bc: bc || '',
status: student.status || 'A',
remark: student.remark || ''
};
});
} else {
// 如果没有学生,传递空数组(用于删除所有学生)
submitData.studentList = [];
}
console.log('准备调用保存接口,数据:', submitData);
const res: any = await jfSaveApi(submitData);
console.log('保存接口返回:', res);
if (res && res.resultCode === 1) {
console.log('数据保存成功(包含学生获奖名单)');
// 保存成功后,更新 jfInfo 用于审批参数
await getJfInfo();
loading.value = false;
return { success: true };
} else {
console.error('数据保存失败:', res?.resultMsg);
loading.value = false;
return { success: false, message: res?.resultMsg || '保存失败' };
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
} catch (error) {
console.error('保存数据失败:', error);
loading.value = false;
return { success: false, message: '保存数据失败' };
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 包装审批接口,先保存数据再审批
const wrappedJfSpApi = async (params: any) => {
console.log('审批接口被调用,参数:', params);
try {
// 先保存编辑组件的数据
console.log('步骤1: 保存编辑组件数据');
const saveResult = await saveJfDataBeforeApprove();
if (!saveResult.success) {
console.error('保存数据失败:', saveResult.message);
loading.value = false;
uni.showToast({
title: saveResult.message || '保存数据失败',
icon: 'none',
duration: 2000
});
throw new Error(saveResult.message || '保存数据失败');
}
console.log('步骤2: 数据保存成功,开始审批');
loadingText.value = '正在审批...';
// 保存成功后,再调用审批接口(只传递审批相关参数,不传递业务字段)
const spParams = {
xxtsId: params.xxtsId,
ywId: params.ywId,
spStatus: params.spStatus,
spRemark: params.spRemark
};
console.log('步骤3: 调用审批接口,参数:', spParams);
const result = await jfSpApi(spParams);
console.log('审批接口返回:', result);
loading.value = false;
return result;
} catch (error: any) {
console.error('审批流程出错:', error);
loading.value = false;
throw error;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 包装转办接口,先保存数据再转办
const wrappedJfTransferApi = async (params: any) => {
console.log('转办接口被调用,参数:', params);
try {
// 先保存编辑组件的数据
console.log('步骤1: 保存编辑组件数据');
const saveResult = await saveJfDataBeforeApprove();
if (!saveResult.success) {
console.error('保存数据失败:', saveResult.message);
loading.value = false;
uni.showToast({
title: saveResult.message || '保存数据失败',
icon: 'none',
duration: 2000
});
throw new Error(saveResult.message || '保存数据失败');
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
console.log('步骤2: 数据保存成功,开始转办');
loadingText.value = '正在转办...';
// 保存成功后,再调用转办接口
const transferParams = {
xxtsId: params.xxtsId,
ywId: params.ywId,
zbrIds: params.zbrIds,
csrIds: params.csrIds,
spRemark: params.spRemark,
};
console.log('步骤3: 调用转办接口,参数:', transferParams);
const result = await jfTransferApi(transferParams);
console.log('转办接口返回:', result);
loading.value = false;
return result;
} catch (error: any) {
console.error('转办流程出错:', error);
loading.value = false;
throw error;
}
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 包装终止接口,先保存数据再终止
const wrappedJfStopApi = async (params: any) => {
console.log('终止接口被调用,参数:', params);
try {
// 先保存编辑组件的数据
console.log('步骤1: 保存编辑组件数据');
const saveResult = await saveJfDataBeforeApprove();
if (!saveResult.success) {
console.error('保存数据失败:', saveResult.message);
loading.value = false;
uni.showToast({
title: saveResult.message || '保存数据失败',
icon: 'none',
duration: 2000
});
throw new Error(saveResult.message || '保存数据失败');
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
console.log('步骤2: 数据保存成功,开始终止');
loadingText.value = '正在终止...';
// 保存成功后,再调用终止接口
const stopParams = {
xxtsId: params.xxtsId,
ywId: params.ywId,
spRemark: params.spRemark,
};
console.log('步骤3: 调用终止接口,参数:', stopParams);
const result = await jfStopApi(stopParams);
console.log('终止接口返回:', result);
loading.value = false;
return result;
} catch (error: any) {
console.error('终止流程出错:', error);
loading.value = false;
throw error;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 处理编辑组件数据加载完成
const handleJfEditLoaded = () => {
jfEditLoaded.value = true;
checkAllDataLoaded();
};
// 检查所有数据是否已加载完成
const checkAllDataLoaded = () => {
if (jfInfoLoaded.value && jfEditLoaded.value) {
// 延迟一点时间,确保界面渲染完成
setTimeout(() => {
loading.value = false;
}, 300);
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 处理数据保存后的回调
const handleDataSaved = async () => {
// 数据保存后,重新获取最新数据用于审批
await getJfInfo();
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 处理审批操作完成后的跳转
const handleSubmit = () => {
navigateToIndex();
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
const handleReject = () => {
navigateToIndex();
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
const handleTransfer = () => {
navigateToIndex();
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
const handleStop = () => {
navigateToIndex();
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 跳转回审批列表页面
const navigateToIndex = () => {
setTimeout(() => {
uni.redirectTo({
url: '/pages/view/routine/JiFenPingJia/jfsp/index'
});
}, 500);
};
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 返回按钮监听(仅 App 端生效H5 端已在 ImagePreviewWithRotate 组件内部处理)
onBackPress(() => {
// 检查 JfEdit 子组件是否正在预览图片
if (jfEditRef.value && jfEditRef.value.certificateUploadRef) {
const certUpload = jfEditRef.value.certificateUploadRef;
const isPreviewing = certUpload.isPreviewing;
if (isPreviewing) {
// 如果正在预览图片,关闭预览并阻止返回
certUpload.closePreview();
return true; // 阻止默认返回行为
}
}
// 没有预览图片,允许正常返回
return false;
});
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
onLoad(async (options: any) => {
if (!options || !options.id) {
uni.showToast({ title: "缺少积分ID", icon: "none" });
setTimeout(() => uni.navigateBack(), 1500);
return;
}
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 如果有 openId先进行认证防止 token 过期)
if (options.openId) {
try {
const loginSuccess = await loginByOpenId(options.openId);
if (!loginSuccess) {
// 登录失败,停止后续流程
return;
}
// 等待一下,确保 token 已保存到 store 中
await new Promise(resolve => setTimeout(resolve, 100));
} catch (e) {
console.error("openId认证失败:", e);
// 认证失败,停止后续流程
return;
}
}
// 标记登录准备就绪,允许组件开始加载数据(无论是否有 openId
isLoginReady.value = true;
// 显示遮罩层
loading.value = true;
loadingText.value = '正在加载数据...';
// 重置加载状态
jfInfoLoaded.value = false;
jfEditLoaded.value = false;
let actualJfId = options.id; // 默认使用传入的 ID
// 如果是从待办进入的from=db需要先获取 xxts 信息,然后获取业务 ID
if (options.from === 'db') {
try {
// 从后端根据 url 中的 idxxts ID查询待办信息
const xxtsRes = await xxtsFindByIdApi({ id: options.id });
if (xxtsRes && xxtsRes.result) {
const xxts = xxtsRes.result;
// 直接保存 xxtsId
if (xxts.id) {
xxtsId.value = xxts.id;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
// 从 xxts 中获取业务 IDxxzbId
if (xxts.xxzbId) {
actualJfId = xxts.xxzbId;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
// 从 xxts 获取 dbZt
if (xxts.dbZt) {
dbZtFromUrl.value = xxts.dbZt;
2025-12-08 20:03:34 +08:00
}
2025-12-20 21:46:38 +08:00
}
} catch (error) {
console.error('获取待办信息失败:', error);
// 如果获取失败,继续使用原始 ID
}
} else {
// 如果不是从待办进入,检查 URL 参数中是否有 xxtsId
if (options.xxtsId) {
xxtsId.value = options.xxtsId;
}
// 从 URL 参数获取 dbZt如果传递了的话
if (options.dbZt) {
dbZtFromUrl.value = options.dbZt;
}
}
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
// 使用实际的业务 ID
jfId.value = actualJfId;
getJfInfo();
});
</script>
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
<style scoped lang="scss">
.unified-scroll {
flex: 1;
height: calc(100vh - 120rpx);
overflow-y: auto;
}
2025-12-08 20:03:34 +08:00
2025-12-20 21:46:38 +08:00
.flow-section {
padding: 0;
background-color: #f5f5f5;
margin-top: 30rpx;
// 确保流程组件与编辑组件有间距
:deep(.lcgl-info) {
padding-top: 0;
margin-top: 0;
padding-left: 0;
padding-right: 0;
}
// 流程组件的 section 也要移除左右边距
:deep(.info-section) {
border-radius: 0;
margin-left: 0;
margin-right: 0;
}
}
// 统一滚动容器的样式
.unified-scroll {
background-color: #f5f5f5;
}
// 遮罩层样式
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #ffffff;
border-radius: 16rpx;
padding: 60rpx 80rpx;
min-width: 300rpx;
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 6rpx solid #f3f3f3;
border-top: 6rpx solid #007aff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 30rpx;
}
.loading-text {
font-size: 28rpx;
color: #333333;
text-align: center;
2025-12-08 20:03:34 +08:00
}
}
}
2025-12-20 21:46:38 +08:00
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
2025-12-08 20:03:34 +08:00
</style>