433 lines
10 KiB
Vue
433 lines
10 KiB
Vue
<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>
|