Files
threeonecheck_web/pages/hiddendanger/rectification.vue
2026-06-03 10:16:37 +08:00

1053 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page padding">
<view class="padding bg-white radius">
<!-- 草稿恢复提示 -->
<view v-if="showRestoreBanner" class="bg-orange-light text-orange padding-sm radius margin-bottom flex justify-between align-center" style="font-size: 24rpx; background-color: #FFF7EB; border: 1rpx solid #FFE4CC; width: 100%; box-sizing: border-box; display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 20rpx;">
<view class="flex align-center" style="display: flex; flex-direction: row; align-items: center;">
<text class="cuIcon-info margin-right-xs" style="margin-right: 10rpx;"></text>
<text>已自动恢复您上次未提交的内容</text>
</view>
<text class="text-blue text-bold" style="cursor: pointer; padding: 0 10rpx; color: #2667E9; font-weight: bold;" @click="clearDraft(true)">清空草稿</text>
</view>
<view class="form-header margin-bottom">
<view class="form-label">
<view class="text-gray">整改方案</view>
<view class="text-red">*</view>
</view>
<button class="ai-rectify-btn" :loading="aiGenerating" :disabled="aiGenerating" @click="handleAiGenerate">
<text v-if="!aiGenerating" class="cuIcon-magic ai-btn-icon"></text>
{{ aiGenerating ? 'AI生成中...' : 'AI生成整改方案' }}
</button>
</view>
<up-textarea v-model="formData.rectifyPlan" placeholder="请输入内容" :maxlength="-1" autoHeight></up-textarea>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">整改完成情况</view>
<view class="text-red">*</view>
</view>
<up-textarea v-model="formData.rectifyResult" placeholder="请输入内容"></up-textarea>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">投资资金(计划)</view>
<view class="text-red">*</view>
</view>
<up-input v-model="formData.planCost" placeholder="请输入内容" type="number"></up-input>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">投资资金(实际)</view>
<view class="text-red">*</view>
</view>
<up-input v-model="formData.actualCost" placeholder="请输入内容" type="number"></up-input>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">限定整改时间</view>
<view class="text-red">*</view>
</view>
<up-datetime-picker hasInput :show="show" v-model="value1" mode="date"></up-datetime-picker>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">整改人员</view>
<view class="text-red">*</view>
</view>
<!-- 点击打开人员选择弹窗 -->
<view class="select-trigger" @click="showUserPopup = true">
<view class="select-content" :class="{ 'text-gray': selectedUsers.length === 0 }">
{{ selectedUsers.length > 0 ? selectedUsersText : '请选择整改人员(可多选)' }}
</view>
<text class="cuIcon-unfold"></text>
</view>
<!-- 人员多选弹窗 -->
<u-popup :show="showUserPopup" mode="bottom" round="20" @close="showUserPopup = false">
<view class="user-popup">
<view class="popup-header">
<view class="popup-title text-bold">选择整改人员</view>
<view class="popup-close" @click="showUserPopup = false">×</view>
</view>
<scroll-view class="popup-body" scroll-y="true">
<up-checkbox-group v-model="selectedUserIds" placement="column" @change="onUserChange">
<view class="user-item" v-for="item in cateList" :key="item.id">
<up-checkbox
:label="item.name"
:name="item.id"
activeColor="#2667E9"
shape="square"
></up-checkbox>
</view>
</up-checkbox-group>
</scroll-view>
<view class="popup-footer">
<button class="btn-cancel" @click="showUserPopup = false">取消</button>
<button class="btn-confirm bg-blue" @click="confirmUserSelect">确定</button>
</view>
</view>
</u-popup>
<view class="form-label margin-bottom margin-top">
<view class="text-gray">整改图片</view>
<view class="text-red">*</view>
</view>
<up-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple imageMode="aspectFill"
:maxCount="10"></up-upload>
<!-- 隐藏的 Canvas用于渲染防作弊时间戳水印 -->
<canvas canvas-id="watermarkCanvas" :width="canvasWidth" :height="canvasHeight" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px', position: 'fixed', left: '-9999px', top: '-9999px' }"></canvas>
<!-- 电子签名 -->
<view class="form-label margin-bottom margin-top flex justify-between align-center" style="width: 100%;">
<view class="flex align-center">
<view class="text-gray">电子签名</view>
<view class="text-red">*</view>
</view>
<button v-if="showCanvas" class="cu-btn sm round line-blue" style="margin: 0; padding: 0 20rpx; height: 50rpx; font-size: 22rpx;" @click="clearSignature">清除重写</button>
<button v-else class="cu-btn sm round line-blue" style="margin: 0; padding: 0 20rpx; height: 50rpx; font-size: 22rpx;" @click="reSign">重新签名</button>
</view>
<view class="signature-box margin-bottom">
<view v-if="!showCanvas" class="signature-display flex align-center justify-center">
<image :src="signatureUrl" class="signature-img" mode="aspectFit"></image>
</view>
<!-- 改为 v-if 并在人员多选弹窗打开时卸载 Canvas解决微信原生 canvas 真机穿透盖住弹窗的问题 -->
<view v-if="showCanvas && !showUserPopup" class="signature-pad-wrap">
<wd-signature
ref="signatureRef"
:width="signatureWidth"
:height="160"
backgroundColor="#f8f8f8"
penColor="#000000"
:lineWidth="3"
:enableHistory="false"
@confirm="(res) => onSignatureConfirm(res.tempFilePath)"
@start="isSignatureEmpty = false"
@signing="isSignatureEmpty = false"
@clear="isSignatureEmpty = true"
>
<template #footer></template>
</wd-signature>
</view>
</view>
<button class="bg-blue round margin-top-xl" @click="handleSubmit">{{ isEdit ? '保存修改' : '提交整改' }}</button>
</view>
</view>
</template>
<script setup>
import {ref,reactive,computed,nextTick,watch,getCurrentInstance} from 'vue'
import {onLoad} from '@dcloudio/uni-app'
import {submitRectification,getDepartmentPersonUsers,getRectifyDetail,getDeptUsersWithSubordinates,getHiddenDangerDetail,generateRectifyPlan} from '@/request/api.js'
import {baseUrl,getToken,toImageUrl,toRelativeFilePath} from '@/request/request.js'
import {addTimestampWatermark} from '@/utils/watermark.js'
// 从页面参数获取的ID
const hazardId = ref('');
const assignId = ref('');
const rectifyId = ref(''); // 整改ID编辑模式时使用
const isEdit = ref(false); // 是否为编辑模式
// 防作弊时间戳水印 Canvas 大小配置
const canvasWidth = ref(300);
const canvasHeight = ref(300);
// 电子签名相关
const showCanvas = ref(true); // 是否显示签字画板
const signatureUrl = ref(''); // 签名完整预览地址
const signatureServerPath = ref(''); // 签名服务器相对路径
const signatureWidth = ref(340); // 签名画板宽度(动态计算)
const signatureRef = ref(null); // 签名组件 ref
const isSignatureEmpty = ref(true); // 签名是否为空
const isSubmitting = ref(false); // 是否正在提交表单
// 清除画布
const clearSignature = () => {
isSignatureEmpty.value = true;
if (signatureRef.value) {
signatureRef.value.clear();
}
};
// 重新签字
const reSign = () => {
isSignatureEmpty.value = true;
showCanvas.value = true;
signatureUrl.value = '';
signatureServerPath.value = '';
nextTick(() => {
if (signatureRef.value) {
signatureRef.value.clear();
}
});
};
// 签名导出失败
const onSignatureError = (err) => {
console.error('签名绘制失败:', err);
uni.showToast({ title: '签名生成失败', icon: 'none' });
isSubmitting.value = false;
uni.hideLoading();
};
// 表单数据
const formData = reactive({
rectifyPlan: '', // 整改方案
rectifyResult: '', // 整改完成情况
planCost: '', // 投资资金(计划)
actualCost: '' // 投资资金(实际)
});
const show = ref(false);
const value1 = ref(Date.now());
const radiovalue1 = ref('');
// 整改人员(多选)
const cateList = ref([])
const showUserPopup = ref(false) // 人员选择弹窗
const selectedUserIds = ref([]) // 选中的用户ID数组
const selectedUsers = ref([]) // 选中的用户对象数组
// 选中人员的显示文本
const selectedUsersText = computed(() => {
if (selectedUsers.value.length === 0) return '';
if (selectedUsers.value.length <= 2) {
return selectedUsers.value.map(u => u.name).join('、');
}
return `${selectedUsers.value[0].name}${selectedUsers.value.length}`;
});
// checkbox变化事件
const onUserChange = (ids) => {
console.log('选中的ID:', ids);
};
// 确认选择人员
const confirmUserSelect = () => {
// 根据选中的ID获取用户对象
selectedUsers.value = cateList.value.filter(item => selectedUserIds.value.includes(item.id));
showUserPopup.value = false;
console.log('选中的整改人员:', selectedUsers.value);
};
// 获取部门人员列表
const fetchDeptUsers = async () => {
console.log('当前hazardId:', hazardId.value);
try {
const res = await getDeptUsersWithSubordinates({hazardId:hazardId.value});
if (res.code === 0 && res.data) {
// 将部门下的用户数据扁平化
const userList = [];
res.data.forEach(dept => {
if (dept.users && dept.users.length > 0) {
dept.users.forEach(user => {
userList.push({
id: String(user.userId),
name: `${user.nickName}${dept.deptName}`
});
});
}
});
cateList.value = userList;
console.log('整改人员列表:', cateList.value);
}
} catch (error) {
console.error('获取部门人员失败:', error);
}
};
// 上传图片
const fileList1 = ref([]);
// 删除图片
const deletePic = (event) => {
fileList1.value.splice(event.index, 1);
};
// 新增图】片
const afterRead = async (event) => {
// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file);
let fileListLen = fileList1.value.length;
lists.map((item) => {
fileList1.value.push({
...item,
status: 'uploading',
message: '上传中',
});
});
for (let i = 0; i < lists.length; i++) {
let watermarkedUrl = lists[i].url;
try {
const instance = getCurrentInstance();
watermarkedUrl = await addTimestampWatermark({
tempFilePath: lists[i].url,
canvasId: 'watermarkCanvas',
canvasWidthRef: canvasWidth,
canvasHeightRef: canvasHeight,
instance
});
} catch (e) {
console.error('加水印失败,将使用原图上传:', e);
}
const result = await uploadFilePromise(watermarkedUrl);
let item = fileList1.value[fileListLen];
const serverPath = typeof result === 'string'
? result
: (result?.url || result?.filePath || result?.path || '');
fileList1.value.splice(fileListLen, 1, {
...item,
status: 'success',
message: '',
url: toImageUrl(serverPath),
serverPath,
});
fileListLen++;
}
};
const uploadFilePromise = (filePath) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: baseUrl + '/frontend/attachment/upload',
filePath: filePath,
name: 'file',
header: {
'Authorization': getToken()
},
success: (res) => {
const data = JSON.parse(res.data);
if (data.code === 0) {
resolve(data.data);
} else {
reject(data.msg || '上传失败');
}
},
fail: (err) => {
console.error('上传失败:', err);
reject(err);
}
});
});
};
// 提交整改
// 真正的提交接口请求
const executeSubmit = async () => {
// 构建附件列表
const attachments = fileList1.value.map(file => {
const path = toRelativeFilePath(file.serverPath || file.filePath || file.url || '');
const fileName = path ? path.split('/').pop() : (file.name || '');
return {
fileName: fileName || '',
filePath: path || '',
fileType: file.type || 'image/png',
fileSize: file.size || 0
};
});
const params = {
hazardId: hazardId.value,
assignId: assignId.value,
rectifyPlan: formData.rectifyPlan,
rectifyResult: formData.rectifyResult,
planCost: Number(formData.planCost) || 0,
actualCost: Number(formData.actualCost) || 0,
attachments: attachments,
// 整改人员ID数组
rectifyUserIds: selectedUserIds.value.map(id => Number(id)),
signPath: signatureServerPath.value || '' // 电子签名路径
};
// 编辑模式需要传递rectifyId
if (rectifyId.value) {
params.rectifyId = rectifyId.value;
}
try {
const res = await submitRectification(params);
uni.hideLoading();
if (res.code === 0) {
clearDraft(false);
uni.showToast({
title: isEdit.value ? '保存成功' : '提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.showToast({
title: res.msg || (isEdit.value ? '保存失败' : '提交失败'),
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('提交整改失败:', error);
uni.showToast({
title: '操作失败',
icon: 'none'
});
} finally {
isSubmitting.value = false;
}
};
// 签名导出成功回调
const onSignatureConfirm = async (tempFilePath) => {
try {
// 上传签名
const res = await uploadFilePromise(tempFilePath);
const path = (res && typeof res === 'object') ? (res.url || res.filePath || '') : (res || '');
signatureServerPath.value = toRelativeFilePath(path);
signatureUrl.value = toImageUrl(signatureServerPath.value);
if (isSubmitting.value) {
await executeSubmit();
}
} catch (err) {
isSubmitting.value = false;
uni.hideLoading();
console.error('签名上传失败:', err);
uni.showToast({ title: '签名上传失败,请重试', icon: 'none' });
}
};
// 提交整改(触发签名导出或直接提交)
const handleSubmit = async () => {
if (!formData.rectifyPlan) {
uni.showToast({
title: '请输入整改方案',
icon: 'none'
});
return;
}
if (!formData.rectifyResult) {
uni.showToast({
title: '请输入整改完成情况',
icon: 'none'
});
return;
}
if (selectedUsers.value.length === 0) {
uni.showToast({
title: '请选择整改人员',
icon: 'none'
});
return;
}
// 电子签名验证与处理
if (showCanvas.value) {
if (!signatureRef.value || isSignatureEmpty.value) {
uni.showToast({
title: '请进行电子签名',
icon: 'none'
});
return;
}
isSubmitting.value = true;
uni.showLoading({ title: '正在提交...', mask: true });
// 触发组件导出,导出成功会回调 onSignatureConfirm
signatureRef.value.confirm();
} else {
// 已经有回显的签名
if (!signatureServerPath.value) {
uni.showToast({
title: '请进行电子签名',
icon: 'none'
});
return;
}
isSubmitting.value = true;
uni.showLoading({ title: '正在提交...', mask: true });
await executeSubmit();
}
};
const handleSubmitOld = async () => {
if (!formData.rectifyPlan) {
uni.showToast({
title: '请输入整改方案',
icon: 'none'
});
return;
}
if (!formData.rectifyResult) {
uni.showToast({
title: '请输入整改完成情况',
icon: 'none'
});
return;
}
if (selectedUsers.value.length === 0) {
uni.showToast({
title: '请选择整改人员',
icon: 'none'
});
return;
}
// 构建附件列表
const attachments = fileList1.value.map(file => {
const path = toRelativeFilePath(file.serverPath || file.filePath || file.url || '');
const fileName = path ? path.split('/').pop() : (file.name || '');
return {
fileName: fileName || '',
filePath: path || '',
fileType: file.type || 'image/png',
fileSize: file.size || 0
};
});
const params = {
hazardId: hazardId.value,
assignId: assignId.value,
rectifyPlan: formData.rectifyPlan,
rectifyResult: formData.rectifyResult,
planCost: Number(formData.planCost) || 0,
actualCost: Number(formData.actualCost) || 0,
attachments: attachments,
// 整改人员ID数组
rectifyUserIds: selectedUserIds.value.map(id => Number(id))
};
// 编辑模式需要传递rectifyId
if (rectifyId.value) {
params.rectifyId = rectifyId.value;
}
try {
const res = await submitRectification(params);
if (res.code === 0) {
uni.showToast({
title: isEdit.value ? '保存成功' : '提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.showToast({
title: res.msg || (isEdit.value ? '保存失败' : '提交失败'),
icon: 'none'
});
}
} catch (error) {
console.error('提交整改失败:', error);
uni.showToast({
title: '操作失败',
icon: 'none'
});
}
};
// 获取整改详情(编辑模式)
const fetchRectifyDetail = async () => {
try {
uni.showLoading({ title: '加载中...' });
const res = await getRectifyDetail({ rectifyId: rectifyId.value });
uni.hideLoading();
if (res.code === 0 && res.data) {
const data = res.data;
// 回显表单数据
formData.rectifyPlan = data.rectifyPlan || '';
formData.rectifyResult = data.rectifyResult || '';
formData.planCost = data.planCost ? String(data.planCost) : '';
formData.actualCost = data.actualCost ? String(data.actualCost) : '';
// 回显电子签名(字段名暂定为 signPath根据后端返回调整
if (data.signPath) {
signatureServerPath.value = toRelativeFilePath(data.signPath);
signatureUrl.value = toImageUrl(signatureServerPath.value);
showCanvas.value = false;
}
// 保存hazardId和assignId
hazardId.value = data.hazardId || '';
assignId.value = data.assignId || '';
await fetchDeptUsers();
// 回显附件
if (data.attachments && data.attachments.length > 0) {
fileList1.value = data.attachments.map(att => {
const serverPath = toRelativeFilePath(att.filePath);
return {
url: toImageUrl(serverPath),
status: 'success',
message: '',
name: att.fileName,
type: att.fileType,
serverPath,
};
});
}
// 回显整改人员(如果有)
if (data.memberIds) {
const memberIdArr = data.memberIds.split(',').map(id => String(id.trim()));
selectedUserIds.value = memberIdArr;
// 等人员列表加载完成后再匹配
setTimeout(() => {
selectedUsers.value = cateList.value.filter(item => memberIdArr.includes(item.id));
}, 500);
} else if (data.rectifierId) {
// 如果没有memberIds使用rectifierId
selectedUserIds.value = [String(data.rectifierId)];
setTimeout(() => {
selectedUsers.value = cateList.value.filter(item => item.id === String(data.rectifierId));
}, 500);
}
// 设置页面标题
uni.setNavigationBarTitle({ title: '编辑整改信息' });
}
} catch (error) {
uni.hideLoading();
console.error('获取整改详情失败:', error);
uni.showToast({ title: '获取详情失败', icon: 'none' });
}
};
// AI生成整改方案
const aiGenerating = ref(false);
const handleAiGenerate = async () => {
if (!hazardId.value) {
uni.showToast({ title: '缺少隐患信息', icon: 'none' });
return;
}
aiGenerating.value = true;
try {
const detailRes = await getHiddenDangerDetail({
hazardId: hazardId.value,
assignId: assignId.value
});
if (detailRes.code !== 0 || !detailRes.data) {
uni.showToast({ title: '获取隐患详情失败', icon: 'none' });
return;
}
const { title, description } = detailRes.data;
const aiRes = await generateRectifyPlan({ title, description });
if (aiRes.code === 0 && aiRes.data) {
if (aiRes.data.rawResponse) {
formData.rectifyPlan = aiRes.data.rawResponse;
}
uni.showToast({ title: 'AI生成完成', icon: 'success', duration: 2000 });
} else {
uni.showToast({ title: aiRes.msg || 'AI生成失败', icon: 'none' });
}
} catch (error) {
console.error('AI生成整改方案失败:', error);
uni.showToast({ title: 'AI生成失败请重试', icon: 'none' });
} finally {
aiGenerating.value = false;
}
};
// 草稿缓存与恢复逻辑 (移至底部以确保 formData 等响应式状态已被正常定义)
const hasDraft = ref(false);
const showRestoreBanner = ref(false); // 独立控制提示 Banner仅在初次确实从本地恢复了内容时才显示
const isRestoring = ref(false); // 正在恢复标志避免触发冗余watch
const signaturePaths = ref([]); // 缓存手写签名的绘制路径
const getDraftKey = () => `draft_rectify_${hazardId.value || ''}_${rectifyId.value || ''}`;
// 电子签名画布手写线条变动回调
const onSignatureChange = () => {
isSignatureEmpty.value = false;
signaturePaths.value = [];
if (hazardId.value || rectifyId.value) {
saveDraft();
}
};
// 保存草稿 (排除选择器人员缓存,仅缓存方案、情况、金额、图片、签名等输入信息)
const saveDraft = () => {
if (isRestoring.value) return;
const key = getDraftKey();
const hasContent = formData.rectifyPlan ||
formData.rectifyResult ||
formData.planCost ||
formData.actualCost ||
fileList1.value.length > 0 ||
signatureServerPath.value ||
signaturePaths.value.length > 0;
if (!hasContent) {
uni.removeStorageSync(key);
hasDraft.value = false;
return;
}
const data = {
formData: {
rectifyPlan: formData.rectifyPlan,
rectifyResult: formData.rectifyResult,
planCost: formData.planCost,
actualCost: formData.actualCost
},
fileList1: fileList1.value,
signatureServerPath: signatureServerPath.value,
signatureUrl: signatureUrl.value,
showCanvas: showCanvas.value,
signaturePaths: signaturePaths.value
};
uni.setStorageSync(key, JSON.stringify(data));
hasDraft.value = true;
};
// 清空草稿
const clearDraft = (showToast = true) => {
const key = getDraftKey();
uni.removeStorageSync(key);
hasDraft.value = false;
showRestoreBanner.value = false;
isRestoring.value = true;
formData.rectifyPlan = '';
formData.rectifyResult = '';
formData.planCost = '';
formData.actualCost = '';
fileList1.value = [];
signatureServerPath.value = '';
signatureUrl.value = '';
showCanvas.value = true;
signaturePaths.value = [];
if (signatureRef.value) {
signatureRef.value.clear();
}
nextTick(() => {
isRestoring.value = false;
});
if (showToast) {
uni.showToast({ title: '草稿已清空', icon: 'none' });
}
};
// 恢复草稿 (不恢复任何选择器数据)
const restoreDraft = () => {
const key = getDraftKey();
const cached = uni.getStorageSync(key);
if (cached) {
try {
const data = JSON.parse(cached);
const hasContent = data.formData.rectifyPlan ||
data.formData.rectifyResult ||
data.formData.planCost ||
data.formData.actualCost ||
(data.fileList1 && data.fileList1.length > 0) ||
data.signatureServerPath ||
(data.signaturePaths && data.signaturePaths.length > 0);
if (!hasContent) return;
isRestoring.value = true;
formData.rectifyPlan = data.formData.rectifyPlan || '';
formData.rectifyResult = data.formData.rectifyResult || '';
formData.planCost = data.formData.planCost || '';
formData.actualCost = data.formData.actualCost || '';
fileList1.value = data.fileList1 || [];
signatureServerPath.value = data.signatureServerPath || '';
signatureUrl.value = data.signatureUrl || '';
showCanvas.value = data.showCanvas !== undefined ? data.showCanvas : true;
signaturePaths.value = data.signaturePaths || [];
hasDraft.value = true;
showRestoreBanner.value = true; // 确实存在内容并恢复了,才亮起提示 Banner
// 延迟恢复签名画布线条重绘,等待 Canvas 组件完全加载完毕
if (signaturePaths.value.length > 0) {
setTimeout(() => {
if (signatureRef.value) {
isSignatureEmpty.value = false;
// wot-design-uni auto rendering
}
}, 450);
}
nextTick(() => {
isRestoring.value = false;
});
uni.showToast({
title: '已自动恢复您上次未提交的内容',
icon: 'none',
duration: 2500
});
} catch (e) {
console.error('解析草稿失败:', e);
isRestoring.value = false;
}
}
};
// 深度监听表单项和签名笔画变化,自动保存草稿
watch(
() => [
formData.rectifyPlan,
formData.rectifyResult,
formData.planCost,
formData.actualCost,
fileList1.value,
signatureServerPath.value,
signaturePaths.value
],
() => {
if (hazardId.value || rectifyId.value) {
saveDraft();
}
},
{ deep: true }
);
onLoad((options) => {
// 计算签名画布宽度
try {
const sysInfo = uni.getSystemInfoSync();
signatureWidth.value = sysInfo.windowWidth - 40;
} catch (e) {
console.error('获取系统信息失败:', e);
}
if (options.hazardId) {
hazardId.value = options.hazardId;
}
if (options.assignId) {
assignId.value = options.assignId;
}
// 在hazardId赋值后调用确保有值
if (!options.rectifyId) fetchDeptUsers();
// 编辑模式
if (options.rectifyId) {
rectifyId.value = options.rectifyId;
isEdit.value = options.isEdit === '1';
// 获取整改详情
fetchRectifyDetail();
} else {
// 页面打开时自动恢复草稿 (不包含选择器缓存以防打乱UI时钟)
restoreDraft();
}
});
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #EBF2FC;
}
.ai-rectify-btn {
display: flex;
align-items: center;
justify-content: center;
height: 60rpx;
padding: 0 24rpx;
margin: 0;
font-size: 24rpx;
color: #fff;
background: linear-gradient(135deg, #4facfe 0%, #2668EA 100%);
border-radius: 30rpx;
border: none;
white-space: nowrap;
flex-shrink: 0;
&::after {
border: none;
}
.ai-btn-icon {
margin-right: 6rpx;
font-size: 26rpx;
}
&[disabled] {
opacity: 0.7;
}
}
.form-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.form-label {
display: flex;
align-items: center;
.text-red {
margin-left: 4rpx;
line-height: 1;
}
}
.date-input {
background: #fff;
border-radius: 8rpx;
padding: 24rpx 20rpx;
margin-bottom: 20rpx;
border: 1rpx solid #F6F6F6;
text {
font-size: 28rpx;
color: #333;
}
}
// 选择触发器样式
.select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
background: #fff;
border: 1rpx solid #dcdfe6;
border-radius: 8rpx;
padding: 20rpx 24rpx;
margin-bottom: 20rpx;
.select-content {
flex: 1;
font-size: 28rpx;
color: #333;
}
}
// 人员选择弹窗
.user-popup {
background: #fff;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
.popup-title {
font-size: 32rpx;
color: #333;
}
.popup-close {
font-size: 40rpx;
color: #999;
line-height: 1;
}
}
.popup-body {
padding: 20rpx 30rpx;
max-height: 600rpx;
overflow-y: auto;
}
.user-item {
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.popup-footer {
display: flex;
border-top: 1rpx solid #eee;
padding-bottom: env(safe-area-inset-bottom);
button {
flex: 1;
height: 90rpx;
line-height: 90rpx;
border-radius: 0;
font-size: 30rpx;
&::after {
border: none;
}
}
.btn-cancel {
background: #fff;
color: #666;
}
.btn-confirm {
color: #fff;
}
}
}
// 签名相关样式
.signature-box {
width: 100%;
min-height: 240rpx;
background: #f8f8f8;
border: 1rpx dashed #dcdfe6;
border-radius: 8rpx;
margin-top: 16rpx;
.signature-img {
width: 100%;
height: 100%;
}
.signature-placeholder {
color: #909399;
font-size: 28rpx;
}
}
.signature-popup {
width: 650rpx;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
.popup-title {
font-size: 32rpx;
color: #333;
}
.popup-close {
font-size: 40rpx;
color: #999;
line-height: 1;
}
}
.popup-body {
padding: 40rpx 30rpx;
background: #fff;
}
.popup-footer {
display: flex;
padding: 0 30rpx 30rpx;
gap: 20rpx;
button {
flex: 1;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
border-radius: 40rpx;
&::after {
border: none;
}
}
.btn-cancel {
background: #f5f5f5;
color: #666;
}
.btn-confirm {
color: #fff;
}
}
}
</style>