You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1275 lines
33 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view>
<topnav :title='pageTitle' @tohome="tohome"></topnav>
<view class="content">
<view class="orderBox">
<orderinfo :info="order"></orderinfo>
</view>
<view class="pageTitle">
协议签名
</view>
<view class="signatureBox">
<view class="signatureItem">
<view class="signatureTitle">护工签名</view>
<view class="signatureArea">
<!-- 未签名时显示开始签名按钮弹出全屏签名 -->
<view v-if="!paramedicSignImage" class="signatureStart" @click="openSignature('paramedic')">
开始签名
</view>
<!-- 签名图片 - 已签名时显示 -->
<image
v-if="paramedicSignImage"
:src="paramedicSignImage"
class="signatureImage"
:style="getSignatureImageStyle(paramedicSignImage)"
mode="aspectFit">
</image>
</view>
<view class="signatureActions">
<text class="btnSave" @click="openSignature('paramedic')" v-if="!paramedicSignImage">开始签名</text>
<text class="btnResign" @click="resignSignature('paramedic')" v-if="paramedicSignImage">重新签名</text>
</view>
</view>
<view class="signatureItem">
<view class="signatureTitle">客户签名</view>
<view class="signatureArea">
<!-- 未签名时显示开始签名按钮(弹出全屏签名) -->
<view v-if="!customerSignImage" class="signatureStart" @click="openSignature('customer')">
开始签名
</view>
<!-- 签名图片 - 已签名时显示 -->
<image
v-if="customerSignImage"
:src="customerSignImage"
class="signatureImage"
:style="getSignatureImageStyle(customerSignImage)"
mode="aspectFit">
</image>
</view>
<view class="signatureActions">
<text class="btnSave" @click="openSignature('customer')" v-if="!customerSignImage">开始签名</text>
<text class="btnResign" @click="resignSignature('customer')" v-if="customerSignImage">重新签名</text>
</view>
</view>
<!-- <view class="signatureItem">
<view class="signatureTitle">公司签名</view>
<view class="signatureArea">
<image
:src="companySignImage"
class="signatureImage"
mode="aspectFit">
</image>
</view>
<view class="signatureActions">
<text class="btnSave" style="opacity:.6;">已使用公司签名</text>
</view>
</view> -->
</view>
<!-- 预览协议按钮 -->
<view class="previewSection">
<view class="previewBtn" @click="previewAgreement">
预览协议
</view>
</view>
</view>
<view class="bottom">
<view class="bottomLeft">
</view>
<view class="bottomRight">
<view class="btnCancel btn" @click="bindCancel">取消</view>
<view class="btnSubmit btn" @click="bindsubmitFun">提交</view>
</view>
</view>
<!-- 协议预览弹窗 -->
<uni-popup ref="agreementPopup" type="center" :mask-click="true" class="fullscreen-popup" :safe-area="false">
<view class="agreementPopup">
<view class="popupHeader">
<text class="popupTitle">协议预览</text>
<text class="popupClose" @click="closeAgreementPopup">×</text>
</view>
<scroll-view class="popupContent" v-if="agreementHtml" scroll-y="true" style="max-height: 100vh;">
<div class="agreementContent" v-html="agreementHtml"></div>
</scroll-view>
<view class="popupContent" v-else>
<view class="noAgreement">
<text>还未上传协议,请联系管理员补充协议</text>
</view>
</view>
<view class="popupFooter" v-if="agreementHtml">
<view class="btnGenerate"
@click="handleDirectSubmit"
:class="{ disabled: isGenerating }"
:disabled="isGenerating">
{{ generateButtonText }}
</view>
</view>
</view>
</uni-popup>
<!-- 全屏签名弹窗 -->
<uni-popup ref="signaturePopup" type="center" :mask-click="false" class="fullscreen-popup" :safe-area="false">
<view class="signaturePopup">
<view class="popupHeader">
<text class="popupTitle">{{ signaturePopupTitle }}</text>
<text class="popupClose" @click="closeSignaturePopup">×</text>
</view>
<view class="popupContent">
<view class="signatureWorkspace">
<view class="signatureInlineToast" v-if="signatureToastVisible">{{ signatureToastText }}</view>
<view class="signatureCanvasWrap">
<l-signature
ref="popupSignatureRef"
:penColor="penColor"
:penSize="penSize"
:openSmooth="openSmooth"
:boundingBox="boundingBox"
disableScroll
class="signatureCanvas">
</l-signature>
</view>
<view class="signatureControlsRow">
<view class="btnRow btnRowClear" @click="clearPopupSignature"></view>
<view class="btnRow btnRowReset" @click="resetPopupSignature"></view>
<view class="btnRow btnRowSave" @click="savePopupSignature"></view>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
var util = require("../../../../utils/util.js");
export default {
data() {
return {
pageTitle: "签订协议",
order: {},
id: "",
// 签名相关
paramedicSignPath: "",
customerSignPath: "",
companySignPath: "",
paramedicSignId: "",
customerSignId: "",
companySignId: "",
paramedicSignImage: "", // 护工签名图片
customerSignImage: "", // 客户签名图片
companySignImage: '', // 公司签名图片(固定)
// 协议预览相关
agreementHtml: "", // 协议HTML内容
pendingAgreementHtml: "", // 待提交的HTML内容包含完整头部
// 签名组件配置
penColor: '#000',
penSize: 3,
openSmooth: true,
boundingBox: true,
// 按钮状态
isGenerating: false, // 是否正在提交
generateButtonText: '直接提交', // 按钮文本
// 全屏签名弹窗
currentSignatureType: '',
signaturePopupTitle: '',
signatureToastVisible: false,
signatureToastText: ''
}
},
onLoad: function(option) {
var that = this;
that.id = option.id;
util.getOrderInfo(option.id, function(r) {
that.order = r.data;
// 初始化公司签名(固定图片)
that.initCompanySign();
}, function(r) {
});
},
methods: {
// 规范化URL去除域名后的多余斜杠以及 //storage -> /storage
normalizeUrl: function(url){
if(!url){ return url; }
let u = String(url);
// 把 :// 后面可能出现的多个 / 压缩成一个 /
u = u.replace(/(:\/\/[^/]*?)\/+/, '$1/');
// 把 //storage 归一为 /storage
u = u.replace(/\/+storage\//, '/storage/');
return u;
},
// 初始化公司签名(不需要手写)
initCompanySign: function(){
// 使用指定的公司签名图片地址
this.companySignImage = 'https://tiantianxinye.365care.langye.net/h5/static/companySign.png';
this.companySignId = 'STATIC_COMPANY_SIGN';
},
tohome:function(){
uni.navigateTo({
url:"../../../../pages/index/index"
})
},
// 显示签名弹窗内的提示
showSignatureToast: function(text){
this.signatureToastText = text || '';
this.signatureToastVisible = true;
clearTimeout(this._signatureToastTimer);
this._signatureToastTimer = setTimeout(()=>{
this.signatureToastVisible = false;
}, 1600);
},
bindCancel: function(e) {
uni.navigateBack({
})
},
bindsubmitFun: function(e) {
var that = this;
// 检查是否所有签名都已完成(公司签名固定,不再校验 companySignId
if (!that.paramedicSignId || !that.customerSignId) {
util.alert("请完成所有签名");
return false;
}
uni.showModal({
title: '提示',
content: "确认签订协议?",
confirmText: "确认",
confirmColor: "#000",
cancelColor: "#eee",
success(res) {
if (res.confirm) {
that.submitAgreement();
}
}
});
},
// 处理直接提交(从预览弹窗中调用)
handleDirectSubmit: function() {
var that = this;
// 防重复点击
if (that.isGenerating) {
util.alert('正在提交协议,请稍候...');
return;
}
// 设置按钮状态
that.isGenerating = true;
that.generateButtonText = '提交中...';
// 构建完整的HTML内容
const fullHtml = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>协议</title>
</head>
<body style="font-family: 'SimSun', 'Microsoft YaHei', 'WenQuanYi Zen Hei', sans-serif;">
${that.agreementHtml}
</body>
</html>`;
// 设置待提交的HTML内容
that.pendingAgreementHtml = fullHtml;
// 关闭弹窗并提交
that.closeAgreementPopup();
// 调用提交方法
that.submitAgreement();
},
// 打开全屏签名
openSignature: function(type){
this.currentSignatureType = type;
const typeNames = {
'paramedic': '护工签名',
'customer': '客户签名',
'company': '公司签名'
};
this.signaturePopupTitle = typeNames[type] || '签名';
this.$nextTick(()=>{
this.$refs.signaturePopup.open();
// 清空一次画布
setTimeout(()=>{
if(this.$refs.popupSignatureRef){
this.$refs.popupSignatureRef.clear();
}
}, 50);
});
},
// 关闭全屏签名
closeSignaturePopup: function(){
if(this.$refs.signaturePopup){
this.$refs.signaturePopup.close();
}
},
// 清除弹窗中的签名
clearPopupSignature: function(){
if(this.$refs.popupSignatureRef){
this.$refs.popupSignatureRef.clear();
}
},
// 重置清空画布并清除当前签名人的已保存签名ID与图片
resetPopupSignature: function(){
this.clearPopupSignature();
const type = this.currentSignatureType;
switch(type){
case 'paramedic':
this.paramedicSignId = '';
this.paramedicSignImage = '';
break;
case 'customer':
this.customerSignId = '';
this.customerSignImage = '';
break;
case 'company':
this.companySignId = '';
this.companySignImage = '';
break;
}
// 签名被重置后,若已有协议,需清除以避免不一致
this.autoClearAgreement();
},
// 保存弹窗中的签名
savePopupSignature: function(){
const type = this.currentSignatureType;
if(!type){
return;
}
if(this.$refs.popupSignatureRef){
// 若未签字,禁止保存
try{
if (typeof this.$refs.popupSignatureRef.isEmpty === 'function' && this.$refs.popupSignatureRef.isEmpty()){
this.showSignatureToast('请先签名后再保存');
return;
}
}catch(e){
// 兼容无isEmpty方法的实现继续后续流程
}
this.$refs.popupSignatureRef.canvasToTempFilePath({
quality: 0.9,
success: (res)=>{
// 直接使用原图上传,不进行旋转
this.rotateImageToLandscape(res.tempFilePath, (rotatedDataUrl)=>{
const uploadPath = rotatedDataUrl || res.tempFilePath;
this.uploadSignature(uploadPath, type, ()=>{
this.closeSignaturePopup();
// 清理已生成协议
this.checkAndClearAgreement(type);
});
});
},
fail: ()=>{
util.alert('签名保存失败');
}
});
}
},
// 不进行图片旋转,直接使用原图
rotateImageToLandscape: function(filePath, callback){
console.log('使用原图,不进行旋转:', filePath);
// 直接返回 null表示使用原图
callback(null);
},
// 清除签名
clearSignature: function(type) {
const refName = type + 'SignatureRef';
if (this.$refs[refName]) {
this.$refs[refName].clear();
this.setSignPath(type, "");
}
},
// 获取签名图片样式(正向显示)
getSignatureImageStyle: function(imagePath) {
// 统一使用正向显示,不进行旋转
return {
width: '120px',
height: '60px'
};
},
// 保存签名
saveSignature: function(type) {
const refName = type + 'SignatureRef';
if (this.$refs[refName]) {
console.log('开始保存签名:', type);
this.$refs[refName].canvasToTempFilePath({
quality: 0.8,
success: (res) => {
console.log('签名保存成功:', type, res);
this.setSignPath(type, res.tempFilePath);
// 保存成功后立即上传到接口
console.log('开始上传签名到接口:', type, res.tempFilePath);
this.uploadSignature(res.tempFilePath, type, (signId) => {
console.log('签名上传完成:', type, signId);
// 上传成功后清除签名路径,图片会自动显示
this.setSignPath(type, "");
// 将英文type转换为中文显示
const typeNames = {
'paramedic': '护工',
'customer': '客户',
'company': '公司'
};
const typeName = typeNames[type] || type;
util.alert(typeName + '签名上传成功');
// 检查是否需要清除已生成的协议
this.checkAndClearAgreement(type);
});
},
fail: (err) => {
console.log('签名保存失败:', type, err);
util.alert(type + '签名保存失败');
}
});
} else {
console.log('签名组件引用不存在:', refName);
}
},
// 重新签名
resignSignature: function(type) {
console.log('重新签名:', type);
// 清除签名ID和图片
switch(type) {
case 'paramedic':
this.paramedicSignId = "";
this.paramedicSignImage = "";
break;
case 'customer':
this.customerSignId = "";
this.customerSignImage = "";
break;
case 'company':
this.companySignId = "";
this.companySignImage = "";
break;
}
// 清除签名路径
this.setSignPath(type, "");
// 将英文type转换为中文显示
const typeNames = {
'paramedic': '护工',
'customer': '客户',
'company': '公司'
};
const typeName = typeNames[type] || type;
// 自动清除已生成的协议
this.autoClearAgreement();
// 立即弹出对应的签名区域
this.openSignature(type);
},
// 签名保存事件处理
onSignatureSave: function(e) {
const type = e.currentTarget.dataset.type;
console.log('签名保存事件:', type);
},
// 设置签名路径
setSignPath: function(type, path) {
switch(type) {
case 'paramedic':
this.paramedicSignPath = path;
break;
case 'customer':
this.customerSignPath = path;
break;
case 'company':
this.companySignPath = path;
break;
}
},
// 上传单个签名
uploadSignature: function(filePath, type, callback) {
var that = this;
var user = uni.getStorageSync('userInfo');
if (!user || !user.access_token) {
util.alert('用户未登录或token无效');
return;
}
console.log('开始上传签名:', type, filePath);
// 支持 dataURL 和本地临时路径
const doUpload = (pathToUpload) => {
uni.uploadFile({
url: util.HOST + 'manager/upload-image',
filePath: pathToUpload,
name: 'file',
formData: {
'token': user.access_token,
'folder': 'public'
},
success(res) {
console.log('签名上传响应:', res);
console.log('响应状态码:', res.statusCode);
console.log('响应数据类型:', typeof res.data);
console.log('响应数据内容:', res.data);
if (res.statusCode == 200 || res.statusCode == "200") {
try {
// 尝试解析响应数据
let mod;
if (typeof res.data === 'string') {
mod = JSON.parse(res.data);
} else if (typeof res.data === 'object') {
mod = res.data;
} else {
throw new Error('响应数据格式不支持');
}
console.log('解析后的数据:', mod);
// 检查必要字段
if (!mod.id || !mod.public_path) {
console.log('响应数据缺少必要字段:', mod);
util.alert('签名上传失败:响应数据格式不完整');
return;
}
// 直接设置签名ID和图片路径
switch(type) {
case 'paramedic':
that.paramedicSignId = mod.id;
that.paramedicSignImage = util.HOST + mod.public_path;
break;
case 'customer':
that.customerSignId = mod.id;
that.customerSignImage = util.HOST + mod.public_path;
break;
case 'company':
that.companySignId = mod.id;
that.companySignImage = util.HOST + mod.public_path;
break;
}
callback(mod.id);
} catch (error) {
console.log('解析上传响应失败:', error);
console.log('原始响应数据:', res.data);
// util.alert('签名上传失败:响应格式错误 - ' + error.message);
}
} else {
console.log('签名上传失败,状态码:', res.statusCode);
util.alert('签名上传失败:' + res.statusCode);
}
},
fail(res) {
console.log('签名上传失败:', res);
util.alert('签名上传失败:网络错误');
}
});
};
if (typeof filePath === 'string' && filePath.startsWith('data:')) {
// 直接上传 dataURL
doUpload(filePath);
} else {
// 本地路径按原逻辑上传
doUpload(filePath);
}
},
// 提交协议
submitAgreement: function() {
var that = this;
// 检查必要参数
if (!that.paramedicSignId || !that.customerSignId || !that.companySignId) {
util.alert("请完成所有签名");
return;
}
// 检查是否有待提交的HTML内容如果没有则提示
if (!that.pendingAgreementHtml && !that.agreementHtml) {
util.alert("请先预览协议");
return;
}
// 构建完整的HTML内容如果还没有
const html = that.pendingAgreementHtml || `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>协议</title>
</head>
<body style="font-family: 'SimSun', 'Microsoft YaHei', 'WenQuanYi Zen Hei', sans-serif;">
${that.agreementHtml}
</body>
</html>`;
// 重置按钮状态
that.isGenerating = false;
that.generateButtonText = '直接提交';
util.request({
api: 'manager/create-order-agreements',
method: 'POST',
data: {
order_id: that.id,
paramedic_id: that.order.paramedic_id,
paramedic_sign_id: that.paramedicSignId,
customer_id: that.order.customer_id,
customer_sign_id: that.customerSignId,
// 后端若需要可传固定占位值
company_sign_id: that.companySignId || 'STATIC_COMPANY_SIGN',
html: html
},
utilSuccess: function(r) {
util.alert("协议签订成功");
uni.navigateTo({
url: "/pages/order/order"
});
},
utilFail: function(r) {
util.alert(r);
}
});
},
// 预览协议
previewAgreement: function() {
var that = this;
// 检查三个签名是否都已完成(公司签名固定,不再校验 companySignId
if (!that.paramedicSignId || !that.customerSignId) {
util.alert('请先完成所有签名');
return;
}
// 检查是否有协议内容
if (!that.order.project || !that.order.project.content) {
util.alert('还未上传协议,请联系管理员补充协议');
return;
}
// 处理协议内容,替换占位符
that.processAgreementContent();
// 打开预览弹窗
that.$refs.agreementPopup.open();
},
// 关闭协议预览弹窗
closeAgreementPopup: function() {
this.$refs.agreementPopup.close();
},
// 处理协议内容,替换占位符
processAgreementContent: function() {
var that = this;
let content = that.order.project.content;
// 获取签名图片的样式(正向显示)
const getSignatureImgStyle = function(imagePath) {
if (!imagePath) return '';
// 统一使用正向显示,不进行旋转
return 'vertical-align: middle; display: inline-block; width: 50px; height: 20px; margin: 0 5px;';
};
// 定义占位符映射
const placeholders = {
'paramedic_sign': that.paramedicSignImage ? `<img src="${that.paramedicSignImage}" alt="护工签名" style="${getSignatureImgStyle(that.paramedicSignImage)}">` : '',
'customer_sign': that.customerSignImage ? `<img src="${that.customerSignImage}" alt="客户签名" style="${getSignatureImgStyle(that.customerSignImage)}">` : '',
'company_sign': `<img class="company-stamp" src="${that.companySignImage}" alt="公司签名" style="display: inline-block;width: 80px;height: 80px;position: relative;left: -150px;margin: 0 5px;vertical-align: middle;">`,
'manage_sign': `<img class="company-stamp" src="${that.companySignImage}" alt="公司签名" style="display: inline-block; width: 80px;height: 80px;position: relative;left: -150px;margin: 0 5px;vertical-align: middle;">`,
'paramedic_sign2': that.paramedicSignImage ? `<img src="${that.paramedicSignImage}" alt="护工签名" style="${getSignatureImgStyle(that.paramedicSignImage)} margin-right: 54px;">` : '',
'customer_sign2': that.customerSignImage ? `<img src="${that.customerSignImage}" alt="客户签名" style="${getSignatureImgStyle(that.customerSignImage)} margin-right: 54px;">` : '',
'manage_sign2': `<img class="company-stamp" src="${that.companySignImage}" alt="公司签名" style="display: inline-block; width: 80px;height: 80px;position: relative;left: -150px;vertical-align: middle; margin: 0 5px; margin-right: 54px;">`,
'paramedic_sign_id': that.paramedicSignId || '',
'customer_sign_id': that.customerSignId || '',
'company_sign_id': that.companySignId || 'STATIC_COMPANY_SIGN',
'days': that.order.days || '',
'price': that.order.price || '',
'total': that.order.total || '',
'range_mobile': (that.order.project && that.order.project.range_mobile) || '',
'complaint_mobile': (that.order.project && that.order.project.complaint_mobile) || '',
'today_date': (() => {
const today = new Date();
return today.getFullYear() + '年' + (today.getMonth() + 1) + '月' + today.getDate() + '日';
})()
};
// 替换所有占位符包括被HTML标签包裹的情况
Object.keys(placeholders).forEach(key => {
// 匹配 {key} 或 {<span>key</span>} 等复杂情况
const regex = new RegExp(`\\{([^}]*${key}[^}]*)\\}`, 'g');
const matches = content.match(regex);
if (matches && matches.length > 0) {
content = content.replace(regex, placeholders[key]);
}
});
// 清理text-wrap-mode: nowrap样式
content = content.replace(/text-wrap-mode:\s*nowrap;?/gi, '');
content = content.replace(/text-wrap-mode:\s*nowrap/gi, '');
that.agreementHtml = content;
},
// 预览协议
previewAgreement: function() {
var that = this;
// 检查三个签名是否都已完成(公司签名固定,不再校验 companySignId
if (!that.paramedicSignId || !that.customerSignId) {
util.alert('请先完成所有签名');
return;
}
// 检查是否有协议内容
if (!that.order.project || !that.order.project.content) {
util.alert('还未上传协议,请联系管理员补充协议');
return;
}
// 处理协议内容,替换占位符
that.processAgreementContent();
// 打开预览弹窗
that.$refs.agreementPopup.open();
},
}
}
</script>
<style lang="scss" scoped>
::v-deep .nav{
z-index: 998 !important;
}
.content {
padding: 20rpx;
padding-bottom: 120rpx;
}
.pageTitle {
font-size: 32rpx;
font-weight: bold;
margin: 30rpx 0 20rpx 0;
color: #333;
}
.signatureBox {
background: #fff;
border-radius: 12rpx;
padding: 20rpx;
}
.signatureItem {
margin-bottom: 30rpx;
}
.signatureTitle {
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
}
.signatureImage {
width: 100%;
height: 200rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
background-color: #f9f9f9;
}
.signatureArea {
height: 200rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
background-color: #fff;
position: relative;
display: flex;
justify-content: center;
}
.signatureStart {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #0DC99E;
font-size: 28rpx;
}
.signatureActions {
margin-top: 15rpx;
display: flex;
justify-content: space-between;
}
.btnClear, .btnSave, .btnResign {
padding: 10rpx 20rpx;
border-radius: 6rpx;
font-size: 24rpx;
}
.btnClear {
color: #666;
background: #f0f0f0;
}
.btnSave {
color: #fff;
background: #0DC99E;
}
.btnResign {
color: #fff;
background: #FF9500;
}
.previewSection {
margin-top: 30rpx;
padding: 20rpx;
background: #fff;
border-radius: 12rpx;
display: flex;
justify-content: center;
}
.previewBtn {
background-color: #0DC99E;
color: #fff;
padding: 10px 20px;
border-radius: 6px;
border: none;
font-size: 16px;
cursor: pointer;
margin-right: 10px;
}
.previewBtn:hover {
background-color: #0bb08a;
}
.agreementPreviewSection {
margin-top: 20px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
z-index: 9999;
}
.sectionTitle {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 15rpx;
}
.agreementImageContainer {
height: 300rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 8rpx;
}
.agreementImage {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.agreementActions {
margin-top: 15rpx;
display: flex;
justify-content: flex-end;
}
.btnRegenerate {
padding: 10rpx 20rpx;
border-radius: 6rpx;
font-size: 24rpx;
color: #fff;
background: #FF9500;
}
/* 弹窗样式 */
.fullscreen-popup {
width: 100vw !important;
height: 100vh !important;
max-width: none !important;
left: 0 !important;
top: 0 !important;
transform: none !important;
}
/* 预览协议弹窗样式 */
.fullscreen-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 9999;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* 确保在移动设备上可以滚动 */
-webkit-overflow-scrolling: touch;
overflow: hidden;
}
/* 全屏签名弹窗内部 */
.signaturePopup {
width: 100vw;
height: 90vh;
background: #fff;
display: flex;
flex-direction: column;
position: relative;
z-index: 9999;
}
.signatureWorkspace{display:flex;flex-direction:column;gap:20rpx;position:relative;height:100%;}
/* 下方按钮区域:横向排布 */
.signatureControlsRow{display:flex;align-items:center;justify-content:center;gap:20rpx;padding:20rpx 0;}
.btnRow{display:flex;align-items:center;justify-content:center;padding: 20rpx 40rpx;border-radius: 999rpx;text-align:center;font-size: 28rpx;min-width:120rpx;}
.btnRowClear{background:#f0f0f0;color:#333;border:2rpx solid #c84f3d;}
.btnRowSave{background:#0DC99E;color:#fff;}
.btnRowReset{background:#FFEFD9;color:#c84f3d;border:2rpx solid #f3c7a5;}
.signatureCanvasWrap{flex:1;}
.signatureCanvas {width: 100%;height: calc(100% - 100rpx)!important;background: #fff;border: 1px solid #eee;border-radius: 8rpx;}
/* 弹窗内提示,层级高于画布 */
.signatureInlineToast{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background: rgba(0,0,0,0.75);color:#fff;padding: 16rpx 24rpx;border-radius: 10rpx;font-size: 26rpx;z-index: 10;}
.agreementPopup {
width: 100vw;
height: 100vh;
background: #fff;
display: flex;
flex-direction: column;
position: relative;
z-index: 9999;
/* 微信浏览器滚动优化 */
-webkit-overflow-scrolling: touch;
overflow: hidden;
/* 确保在微信浏览器中正常显示 */
-webkit-transform: translateZ(0);
transform: translateZ(0);
/* 防止微信浏览器的滚动穿透 */
overscroll-behavior: contain;
}
.popupHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1px solid #eee;
background: #f8f8f8;
flex-shrink: 0;
/* 确保头部固定 */
position: sticky;
top: 0;
z-index: 10;
}
.popupTitle {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.popupClose {
font-size: 40rpx;
color: #999;
cursor: pointer;
padding: 10rpx 15rpx;
background: #f0f0f0;
border-radius: 50%;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.popupClose:hover {
background: #e0e0e0;
color: #666;
}
.popupContent {
flex: 1;
height: calc(100vh - 120rpx); /* 减去头部和底部的高度 */
overflow-y: auto;
overflow-x: hidden;
padding: 20rpx;
/* 微信浏览器滚动优化 */
-webkit-overflow-scrolling: touch;
/* 允许触摸滚动 */
touch-action: pan-y;
/* 设置滚动条样式 */
scrollbar-width: thin;
scrollbar-color: #ccc transparent;
/* 微信浏览器特殊处理 */
position: relative;
/* 确保内容可以滚动 */
overscroll-behavior: contain;
}
/* Webkit浏览器的滚动条样式 */
.popupContent::-webkit-scrollbar {
width: 6px;
}
.popupContent::-webkit-scrollbar-track {
background: transparent;
}
.popupContent::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
.popupContent::-webkit-scrollbar-thumb:hover {
background: #999;
}
.agreementContent {
font-size: 28rpx;
line-height: 1.6;
color: #333;
word-wrap: break-word;
word-break: break-all;
overflow-wrap: break-word;
/* 确保在移动设备上内容可以正常显示和滚动 */
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
text-size-adjust: 100%;
// height:100vh;
// overflow: scroll;
}
.agreementContent * {
max-width: 100% !important;
box-sizing: border-box !important;
/* 确保所有元素都能正常显示 */
word-wrap: break-word !important;
overflow-wrap: break-word !important;
}
.agreementContent img {
max-width: 50px !important;
height: 20px !important;
display: inline-block !important;
margin: 0 5px !important;
vertical-align: middle !important;
}
.agreementContent p, .agreementContent div {
margin: 10rpx 0 !important;
padding: 0 !important;
}
.agreementContent span {
word-break: break-all !important;
}
.noAgreement {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 28rpx;
}
.popupFooter {
padding: 20rpx;
border-top: 1px solid #eee;
display: flex;
justify-content: center;
flex-shrink: 0;
/* 确保底部固定 */
position: sticky;
bottom: 0;
background: #fff;
z-index: 10;
}
.btnGenerate {
padding: 15rpx 30rpx;
border-radius: 8rpx;
font-size: 28rpx;
color: #fff;
background: #0DC99E;
}
.btnGenerate.disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.bottom {
background: #FFFFFF;
box-shadow: 0 -2rpx 12rpx 0 rgba(0, 0, 0, 0.16), inset 0 1rpx 0 0 #E4E4E4;
width: 100%;
height: 100rpx;
position: fixed;
bottom: 0;
left: 0;
display: flex;
justify-content: space-between;
}
.bottom .bottomLeft {
display: flex;
align-items: center;
}
.bottom .bottomRight {
display: flex;
flex: 1;
}
.btn {
width: 50%;
line-height: 100rpx;
text-align: center;
line-height: 100rpx;
font-family: SourceHanSansCN-Medium;
font-size: 32rpx;
letter-spacing: 0;
}
.btnCancel {
color: #666666;
background: #F0F0F0;
}
.btnSubmit {
color: #FFFFFF;
background: #0DC99E;
}
/* 移动设备优化 */
@media screen and (max-width: 768px) {
.agreementPopup {
width: 100vw;
height: 100vh;
max-height: 100vh;
}
.popupContent {
padding: 15rpx;
/* 移动设备上的滚动优化 */
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
.agreementContent {
font-size: 26rpx;
line-height: 1.5;
}
.popupHeader {
padding: 15rpx;
}
.popupFooter {
padding: 15rpx;
}
}
/* 小屏幕设备优化 */
@media screen and (max-width: 480px) {
.agreementContent {
font-size: 24rpx;
line-height: 1.4;
}
.popupContent {
padding: 10rpx;
}
.popupHeader {
padding: 10rpx;
}
.popupFooter {
padding: 10rpx;
}
}
/* 微信浏览器特殊优化 */
@supports (-webkit-touch-callout: none) {
.popupContent {
/* 强制启用硬件加速 */
-webkit-transform: translateZ(0);
transform: translateZ(0);
/* 微信浏览器滚动优化 */
-webkit-overflow-scrolling: touch;
/* 防止滚动卡顿 */
will-change: scroll-position;
/* 确保内容可以滚动 */
overflow-y: auto;
height: calc(100vh - 120rpx);
}
.agreementPopup {
/* 防止微信浏览器的滚动穿透 */
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* 微信浏览器特殊处理 */
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
/* 微信浏览器中的滚动条优化 */
.popupContent::-webkit-scrollbar {
width: 4px;
}
.popupContent::-webkit-scrollbar-track {
background: transparent;
}
.popupContent::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.3);
border-radius: 2px;
}
}
</style>