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

433 lines
10 KiB
Vue
Raw Permalink 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="asset-page asset-theme bmt-sign-page">
<asset-page-shell title="BMT签约" />
<view class="asset-scroll bmt-sign-scroll">
<view class="bmt-sign-hero glass-panel">
<view class="bmt-sign-hero__head">
<view class="bmt-sign-hero__badge">
<text class="bmt-sign-hero__badge-text">R</text>
</view>
<text class="bmt-sign-hero__title">BMT 电子签约</text>
</view>
<text class="bmt-sign-hero__desc">
签约完成后即可正常使用 BMT 相关服务
</text>
</view>
<view class="bmt-sign-card glass-panel">
<view class="bmt-sign-card__row">
<text class="bmt-sign-card__label">签约信息</text>
<text class="bmt-sign-card__value" :class="status.signInfoComplete === 1 ? 'success-text' : 'warning-text'">{{
status.signInfoComplete === 1 ? "已完善" : "待补充"
}}</text>
</view>
<view class="bmt-sign-card__row">
<text class="bmt-sign-card__label">合同状态</text>
<text class="bmt-sign-card__value" :class="contractToneClass">{{
contractStatusText
}}</text>
</view>
<text v-if="isLoading" class="bmt-sign-card__loading">正在查询签约状态…</text>
<view class="bmt-sign-card__actions">
<view v-if="status.signInfoComplete !== 1" class="bmt-sign-button bmt-sign-button--primary"
@click="goToSignInfo">
去补充签约信息
</view>
<view class="bmt-sign-button bmt-sign-button--ghost" @click="refresh">
重新查询
</view>
</view>
</view>
</view>
<asset-confirm-popup :visible="signPopupVisible" title="BMT签约" :message="signTips"
:confirm-text="signButtonText" cancel-text="取消" :show-close="false" @cancel="handleCancel"
@confirm="handleStartSigning" />
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import {
fetchBmtContractStatus,
checkBmtSignCondition,
startBmtSigning,
} from "../../api/bmt";
const SIGN_INFO_PAGE = "/pages/assets/bmt-sign-info";
const TIPS_FIELD_KEYS = ["tips", "tip", "sign_tips"];
const CONTRACT_TYPE_KEYS = ["contract_type"];
const SIGN_URL_KEYS = [
"url",
"sign_url",
"redirect_url",
"h5_url",
"link",
"jump_url",
];
function pickFirstValue(source, keys) {
if (!source || typeof source !== "object") {
return "";
}
for (let i = 0; i < keys.length; i += 1) {
const value = source[keys[i]];
if (value !== undefined && value !== null && value !== "") {
return value;
}
}
return "";
}
export default {
components: {
AssetPageShell,
AssetConfirmPopup,
},
data() {
return {
isLoading: true,
isFetching: false,
isStarting: false,
hasShown: false,
hasAutoRedirected: false,
signPopupVisible: false,
signTips: "",
signButtonText: "签约新协议",
signContractType: 0,
rawStatus: {},
status: {
signInfoComplete: 1,
contractStatus: 1,
contractType: 0,
tips: "",
},
};
},
computed: {
contractStatusText() {
return Number(this.status.contractStatus) === 0 ? "待签约" : "已签约";
},
contractToneClass() {
return Number(this.status.contractStatus) === 0
? "warning-text"
: "success-text";
},
},
onLoad() {
this.loadStatus(true, true);
},
onShow() {
// 从「补充签约信息」页返回时刷新状态,首次进入不重复请求
if (this.hasShown) {
this.loadStatus(false, false);
return;
}
this.hasShown = true;
},
methods: {
normalizeStatus(payload) {
const source = payload && typeof payload === "object" ? payload : {};
const rawComplete = pickFirstValue(source, ["sign_info_complete"]);
const signInfoComplete =
rawComplete === "" ? 1 : Number(rawComplete) === 1 ? 1 : 0;
return {
signInfoComplete: signInfoComplete,
contractStatus: Number(
pickFirstValue(source, ["contract_status"]) || 0,
),
contractType: Number(pickFirstValue(source, CONTRACT_TYPE_KEYS) || 0),
tips: String(pickFirstValue(source, TIPS_FIELD_KEYS) || ""),
};
},
async loadStatus(showLoading, isInitial) {
if (this.isFetching) {
return;
}
this.isFetching = true;
if (showLoading) {
this.isLoading = true;
}
try {
const response = await fetchBmtContractStatus(
showLoading
? {
showLoading: true,
loadingText: "查询中",
}
: null,
);
this.rawStatus = response || {};
const status = this.normalizeStatus(response);
this.status = status;
// sign_info_complete 为 0:签约信息不完整,首次进入自动去补充信息
if (status.signInfoComplete !== 1) {
if (isInitial && !this.hasAutoRedirected) {
this.hasAutoRedirected = true;
this.goToSignInfo();
}
return;
}
// contract_status 为 0:需要签约,弹框引导
if (Number(status.contractStatus) === 0) {
await this.openSignPopup(status);
}
} catch (error) {
uni.showToast({
title: error.message || "签约状态查询失败",
icon: "none",
});
} finally {
this.isFetching = false;
this.isLoading = false;
}
},
refresh() {
this.loadStatus(true, false);
},
goToSignInfo() {
const data = encodeURIComponent(JSON.stringify(this.rawStatus || {}));
uni.navigateTo({
url: SIGN_INFO_PAGE + "?data=" + data,
});
},
async openSignPopup(status) {
let tips = status.tips;
let contractType = status.contractType;
let checkData = {};
// 签约前置校验,失败时退回状态接口返回的提示,不阻断弹框
try {
checkData = await checkBmtSignCondition(
{},
{
showLoading: true,
loadingText: "校验中",
},
);
} catch (error) {
checkData = {};
}
const checkTips = String(pickFirstValue(checkData, TIPS_FIELD_KEYS) || "");
const checkType = pickFirstValue(checkData, CONTRACT_TYPE_KEYS);
if (checkTips) {
tips = checkTips;
}
if (checkType !== "") {
contractType = Number(checkType) === 1 ? 1 : 0;
}
this.signTips = tips || "您尚未签署 BMT 相关协议,请完成签约后继续使用。";
this.signContractType = contractType;
this.signButtonText = contractType === 1 ? "更新协议" : "签约新协议";
this.signPopupVisible = true;
},
async handleStartSigning() {
if (this.isStarting) {
return;
}
this.isStarting = true;
try {
const result = await startBmtSigning(
{
contract_type: this.signContractType,
},
{
showLoading: true,
loadingText: "发起签约",
},
);
this.signPopupVisible = false;
const targetUrl = String(pickFirstValue(result, SIGN_URL_KEYS) || "");
if (targetUrl) {
this.openSignUrl(targetUrl);
return;
}
uni.showToast({
title: "签约已发起",
icon: "none",
});
this.loadStatus(false, false);
} catch (error) {
uni.showToast({
title: error.message || "发起签约失败",
icon: "none",
});
} finally {
this.isStarting = false;
}
},
openSignUrl(targetUrl) {
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,
});
},
handleCancel() {
this.signPopupVisible = false;
if (getCurrentPages().length > 1) {
uni.navigateBack();
}
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.bmt-sign-page {
min-height: 100vh;
}
.bmt-sign-scroll {
padding: 12rpx 24rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.bmt-sign-hero {
padding: 32rpx;
}
.bmt-sign-hero__head {
display: flex;
align-items: center;
}
.bmt-sign-hero__badge {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
height: 60rpx;
margin-right: 16rpx;
border-radius: 50%;
background: #ff5a5a;
}
.bmt-sign-hero__badge-text {
font-size: 32rpx;
font-weight: 800;
color: #ffffff;
}
.bmt-sign-hero__title {
font-size: 34rpx;
font-weight: 700;
color: $asset-text-main;
}
.bmt-sign-hero__desc {
display: block;
margin-top: 18rpx;
font-size: 26rpx;
line-height: 1.6;
color: $asset-text-subtle;
}
.bmt-sign-card {
margin-top: 24rpx;
padding: 12rpx 28rpx 28rpx;
}
.bmt-sign-card__row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.bmt-sign-card__label {
font-size: 28rpx;
color: $asset-text-muted;
}
.bmt-sign-card__value {
font-size: 30rpx;
font-weight: 600;
}
.bmt-sign-card__loading {
display: block;
margin-top: 24rpx;
font-size: 24rpx;
color: $asset-text-subtle;
}
.bmt-sign-card__actions {
display: flex;
flex-direction: column;
margin-top: 32rpx;
}
.bmt-sign-card__actions .bmt-sign-button+.bmt-sign-button {
margin-top: 20rpx;
}
.bmt-sign-button {
display: flex;
align-items: center;
justify-content: center;
height: 84rpx;
border-radius: 999rpx;
font-size: 30rpx;
font-weight: 600;
}
.bmt-sign-button--primary {
background: linear-gradient(135deg, #7e6cff 0%, #6b56f6 100%);
box-shadow: 0 14rpx 24rpx rgba(93, 79, 225, 0.22);
color: #ffffff;
}
.bmt-sign-button--ghost {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.06);
color: $asset-text-main;
}
</style>