bm-bmt/pages/assets/bmt-sign-agreement.vue

426 lines
10 KiB
Vue

<template>
<view class="asset-page asset-theme bmt-agree-page">
<asset-page-shell title="签约协议" />
<view v-if="signStatus">
<view class="bmt-agree-content glass-panel">
<scroll-view class="bmt-agree-scroll" scroll-y :style="{ height: scrollViewHeight + 'px' }"
:scroll-with-animation="false" :show-scrollbar="false" @scroll="handleScroll">
<text class="bmt-agree-title">{{ signData.name }}</text>
<view class="bmt-agree-desc">
<text class="bmt-agree-text">{{ signData.content || "暂无协议内容" }}</text>
<view v-if="!isReachBottom" class="bmt-agree-tip">请阅读到底部后签约</view>
<view class="bmt-agree-padding"></view>
</view>
</scroll-view>
</view>
<view class="bmt-agree-bottom">
<text class="bmt-agree-bottom__hint">{{ isReachBottom ? "已阅读到底部" : "向下滑动阅读签约" }}</text>
<view class="bmt-agree-bottom__row">
<view class="bmt-agree-btn bmt-agree-btn--ghost" @click="cancel">取消</view>
<view class="bmt-agree-btn bmt-agree-btn--primary"
:class="{ 'is-disabled': !isReachBottom || signing }" @click="toSign">
{{ signing ? "签约中..." : "确认签约" }}
</view>
</view>
<view class="bmt-agree-copy">
<image class="bmt-agree-copy__icon" src="https://imgs.agrimedia.cn/copy-icon-sign.png" mode="aspectFit" />
<text class="bmt-agree-copy__text">易保全在线签约系统</text>
</view>
</view>
</view>
<view v-else class="bmt-agree-success">
<image class="bmt-agree-success__image" src="https://imgs.agrimedia.cn/bm-bmt/success.png" mode="aspectFit" />
<text class="bmt-agree-success__text">恭喜签约完成</text>
<view class="bmt-agree-success__btn" @click="compute">完成</view>
</view>
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import { checkBmtSignCondition, startBmtSigning } from "../../api/bmt";
const READ_BOTTOM_THRESHOLD = 15;
export default {
components: {
AssetPageShell,
},
data() {
return {
signData: {
name: "",
content: "",
},
signStatus: true,
signing: false,
isReachBottom: false,
scrollViewHeight: 0,
scrollTimer: null,
contractId: "",
};
},
onLoad() {
this.loadAgreement();
},
onReady() {
this.$nextTick(() => {
setTimeout(() => {
this.calcScrollViewHeight();
}, 100);
});
},
onShow() {
this.$nextTick(() => {
setTimeout(() => {
this.calcScrollViewHeight();
}, 300);
});
},
onUnload() {
if (this.scrollTimer) {
clearTimeout(this.scrollTimer);
}
},
methods: {
async loadAgreement() {
try {
// 协议内容与签约 id 都取自 go_to_sign_check_bmt
const data =
(await checkBmtSignCondition({}, {
showLoading: true,
loadingText: "加载中",
})) || {};
this.contractId = String(data.id || "");
this.signData = {
name: String(data.name || "签约协议"),
content: String(data.content || ""),
};
this.$nextTick(() => {
setTimeout(() => {
this.calcScrollViewHeight();
setTimeout(() => {
this.calcScrollViewHeight();
}, 200);
}, 100);
});
} catch (error) {
uni.showToast({
title: (error && error.message) || "获取签约信息失败,请重试",
icon: "none",
});
}
},
calcScrollViewHeight() {
const systemInfo = uni.getSystemInfoSync();
const windowHeight = Number(systemInfo.windowHeight || 0);
const query = uni.createSelectorQuery().in(this);
query.select(".bmt-agree-content").boundingClientRect();
query.select(".bmt-agree-desc").boundingClientRect();
query.select(".bmt-agree-bottom").boundingClientRect();
query.exec((res) => {
const contentRect = res && res[0];
const descRect = res && res[1];
const bottomRect = res && res[2];
const top = contentRect && contentRect.top ? Number(contentRect.top) : 0;
const bottomHeight =
bottomRect && bottomRect.height ? Number(bottomRect.height) : 150;
const available = windowHeight - top - bottomHeight - 16;
this.scrollViewHeight = Math.max(available, 200);
// 内容不足以滚动时,直接视为已读到底部,避免短协议无法签约
const descHeight = descRect && descRect.height ? Number(descRect.height) : 0;
if (descHeight && descHeight <= this.scrollViewHeight + READ_BOTTOM_THRESHOLD) {
this.isReachBottom = true;
}
});
},
handleScroll(event) {
if (this.scrollTimer) {
clearTimeout(this.scrollTimer);
}
this.scrollTimer = setTimeout(() => {
this.checkScrollBottom(event);
}, 50);
},
checkScrollBottom(event) {
if (this.isReachBottom) {
return;
}
const detail = (event && event.detail) || {};
const scrollTop = Number(detail.scrollTop || 0);
const scrollHeight = Number(detail.scrollHeight || 0);
const clientHeight = this.scrollViewHeight;
const scrollPercent = scrollHeight
? (scrollTop + clientHeight) / scrollHeight
: 1;
if (
scrollTop + clientHeight >= scrollHeight - READ_BOTTOM_THRESHOLD ||
scrollPercent >= 0.95
) {
this.isReachBottom = true;
this.$nextTick(() => {
setTimeout(() => {
this.calcScrollViewHeight();
}, 50);
});
uni.showToast({
title: "已阅读到底部",
icon: "none",
duration: 1000,
});
}
},
cancel() {
uni.navigateBack({
delta: 1,
});
},
async toSign() {
if (!this.isReachBottom || this.signing) {
return;
}
this.signing = true;
try {
// 签约 id 取自 go_to_sign_check_bmt
const result =
(await startBmtSigning(
{
id: this.contractId,
},
{
showLoading: true,
loadingText: "签约中",
},
)) || {};
console.log("[BMT签约] 协议页 start_signing_bmt_v2 返回:", result.raw);
if (Number(result.status) === 0 && result.saasShortLink) {
this.navigateToExternalUrl(result.saasShortLink);
return;
}
this.signStatus = false;
} catch (error) {
this.signing = false;
uni.showToast({
title: (error && error.message) || "签约失败,请重试",
icon: "none",
});
}
},
navigateToExternalUrl(targetUrl) {
if (!targetUrl) {
return;
}
if (typeof window !== "undefined" && window.location) {
window.location.href = targetUrl;
return;
}
if (
typeof plus !== "undefined" &&
plus.webview &&
plus.webview.currentWebview
) {
const currentWebview = plus.webview.currentWebview();
if (currentWebview && typeof currentWebview.loadURL === "function") {
currentWebview.loadURL(targetUrl);
return;
}
}
uni.navigateTo({
url: targetUrl,
});
},
compute() {
uni.navigateBack({
delta: 1,
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.bmt-agree-page {
min-height: 100vh;
}
.bmt-agree-content {
margin: 12rpx 24rpx;
padding: 28rpx 26rpx;
}
.bmt-agree-scroll {
box-sizing: border-box;
}
.bmt-agree-title {
display: block;
margin-bottom: 30rpx;
font-size: 34rpx;
font-weight: 700;
text-align: center;
color: $asset-text-main;
}
.bmt-agree-desc {
box-sizing: border-box;
}
.bmt-agree-text {
display: block;
font-size: 26rpx;
line-height: 1.8;
white-space: pre-wrap;
word-break: break-word;
color: $asset-text-muted;
}
.bmt-agree-tip {
margin-top: 24rpx;
padding-top: 30rpx;
border-top: 1px solid rgba(255, 255, 255, 0.08);
text-align: center;
font-size: 24rpx;
color: $asset-text-subtle;
}
.bmt-agree-padding {
height: 1rpx;
opacity: 0;
}
.bmt-agree-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom));
border-top: 1px solid rgba(255, 255, 255, 0.08);
background: #191e32;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.18);
z-index: 30;
}
.bmt-agree-bottom__hint {
display: block;
margin-bottom: 20rpx;
text-align: center;
font-size: 24rpx;
color: $asset-text-subtle;
}
.bmt-agree-bottom__row {
display: flex;
align-items: center;
}
.bmt-agree-btn {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 30rpx;
font-weight: 600;
}
.bmt-agree-btn+.bmt-agree-btn {
margin-left: 20rpx;
}
.bmt-agree-btn--ghost {
border: 1px solid rgba(255, 255, 255, 0.16);
background: rgba(255, 255, 255, 0.06);
color: $asset-text-main;
}
.bmt-agree-btn--primary {
background: linear-gradient(135deg, #7e6cff 0%, #6b56f6 100%);
box-shadow: 0 14rpx 24rpx rgba(93, 79, 225, 0.22);
color: #ffffff;
}
.bmt-agree-btn--primary.is-disabled {
opacity: 0.5;
}
.bmt-agree-copy {
display: flex;
align-items: center;
justify-content: center;
margin-top: 16rpx;
}
.bmt-agree-copy__icon {
width: 112rpx;
height: 32rpx;
margin-right: 10rpx;
}
.bmt-agree-copy__text {
font-size: 24rpx;
color: $asset-text-subtle;
}
.bmt-agree-success {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 220rpx;
}
.bmt-agree-success__image {
width: 180rpx;
height: 180rpx;
}
.bmt-agree-success__text {
margin-top: 48rpx;
font-size: 36rpx;
font-weight: 500;
color: $asset-text-main;
}
.bmt-agree-success__btn {
display: flex;
align-items: center;
justify-content: center;
width: 328rpx;
height: 80rpx;
margin-top: 60rpx;
border-radius: 40rpx;
background: linear-gradient(135deg, #7e6cff 0%, #6b56f6 100%);
box-shadow: 0 14rpx 24rpx rgba(93, 79, 225, 0.22);
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
</style>