415 lines
11 KiB
Vue
415 lines
11 KiB
Vue
<!-- 页面编号:A-06;用途:只承接账号冻结、停用与风险限制等阻断登录状态。 -->
|
||
<template>
|
||
<AuthPageShell class="auth-page status-page">
|
||
<view class="status-content">
|
||
<view class="page-heading">
|
||
<view class="back-button" hover-class="tap-fade" @click="requestBack">
|
||
<image
|
||
class="back-button__icon"
|
||
src="/static/assets/foundation/transparent/chevron-right.png"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
<text class="page-title">登录受限</text>
|
||
<text class="page-subtitle">请根据当前账号状态完成恢复</text>
|
||
<image
|
||
class="status-divider"
|
||
src="/static/assets/modules/auth/transparent/a01-vnext-divider-v1.png"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
|
||
<view class="status-body">
|
||
<image
|
||
class="status-icon"
|
||
src="/static/assets/foundation/transparent/auth-login-outline.png"
|
||
mode="aspectFit"
|
||
/>
|
||
<text class="status-eyebrow">{{ currentState.eyebrow }}</text>
|
||
<text class="status-title">{{ currentState.title }}</text>
|
||
<text class="status-description">{{ currentState.description }}</text>
|
||
|
||
<view class="status-details">
|
||
<view class="status-detail">
|
||
<text class="status-detail__label">{{
|
||
currentState.reasonLabel
|
||
}}</text>
|
||
<text class="status-detail__value">{{ currentState.reason }}</text>
|
||
</view>
|
||
<view class="status-detail">
|
||
<text class="status-detail__label">{{
|
||
currentState.impactLabel
|
||
}}</text>
|
||
<text class="status-detail__value">{{ currentState.impact }}</text>
|
||
</view>
|
||
<view class="status-detail">
|
||
<text class="status-detail__label">{{
|
||
currentState.recoveryLabel
|
||
}}</text>
|
||
<text class="status-detail__value">{{
|
||
currentState.recovery
|
||
}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
class="status-primary"
|
||
hover-class="button-hover"
|
||
@click="openRecovery"
|
||
>
|
||
<image
|
||
class="status-primary__skin"
|
||
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
|
||
mode="aspectFit"
|
||
/>
|
||
<text class="status-primary__copy">查看恢复方式</text>
|
||
</view>
|
||
<view class="status-secondary" hover-class="tap-fade" @click="requestBack"
|
||
>返回登录</view
|
||
>
|
||
</view>
|
||
|
||
<template #overlay>
|
||
<view
|
||
v-if="recoveryVisible"
|
||
class="recovery-layer"
|
||
@click="closeRecovery"
|
||
>
|
||
<view class="recovery-dialog" @click.stop>
|
||
<image
|
||
class="recovery-dialog__skin"
|
||
src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png"
|
||
mode="aspectFit"
|
||
/>
|
||
<view class="recovery-dialog__content">
|
||
<text class="recovery-title">{{ currentState.recoveryTitle }}</text>
|
||
<text class="recovery-copy">{{ currentState.recoveryDetail }}</text>
|
||
<text class="recovery-note"
|
||
>账号恢复服务将在功能阶段接入,当前仅审核页面样式与状态位置。</text
|
||
>
|
||
<view
|
||
class="recovery-action"
|
||
hover-class="button-hover"
|
||
@click="closeRecovery"
|
||
>
|
||
<image
|
||
class="recovery-action__skin"
|
||
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
|
||
mode="aspectFit"
|
||
/>
|
||
<text class="recovery-action__copy">我知道了</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</AuthPageShell>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from "vue";
|
||
import { onBackPress, onLoad } from "@dcloudio/uni-app";
|
||
import AuthPageShell from "@/components/AuthPageShell.vue";
|
||
import { goRoot, handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||
|
||
const status = ref("risk");
|
||
const recoveryVisible = ref(false);
|
||
|
||
// A06 阻断状态的唯一合同;普通登录错误必须留在 A01 就近处理。
|
||
const statusConfig = {
|
||
frozen: {
|
||
eyebrow: "账号冻结",
|
||
title: "账号已被冻结",
|
||
description: "当前账号因安全审核暂时无法登录。",
|
||
reasonLabel: "限制原因",
|
||
reason: "账号进入人工安全审核",
|
||
impactLabel: "影响范围",
|
||
impact: "暂时无法进入家谱及管理资料",
|
||
recoveryLabel: "恢复方式",
|
||
recovery: "提交账号申诉并等待审核结果",
|
||
recoveryTitle: "申请解除冻结",
|
||
recoveryDetail: "请准备注册手机号与身份说明,提交后由平台进行人工核验。",
|
||
},
|
||
disabled: {
|
||
eyebrow: "账号停用",
|
||
title: "账号已被停用",
|
||
description: "当前账号处于停用状态,登录入口已关闭。",
|
||
reasonLabel: "限制原因",
|
||
reason: "账号已执行停用处理",
|
||
impactLabel: "影响范围",
|
||
impact: "无法登录,原有家谱资料不会被删除",
|
||
recoveryLabel: "恢复方式",
|
||
recovery: "联系平台核实停用原因与恢复条件",
|
||
recoveryTitle: "联系平台核实",
|
||
recoveryDetail:
|
||
"请提供注册手机号和账号归属信息,平台核实后告知是否可以恢复。",
|
||
},
|
||
risk: {
|
||
eyebrow: "风险限制",
|
||
title: "账号存在安全风险",
|
||
description: "系统检测到异常登录,为保护家谱资料已暂停本次登录。",
|
||
reasonLabel: "限制原因",
|
||
reason: "登录环境或操作行为存在异常",
|
||
impactLabel: "影响范围",
|
||
impact: "本次登录被阻断,账号资料保持不变",
|
||
recoveryLabel: "恢复方式",
|
||
recovery: "完成身份核验后重新登录",
|
||
recoveryTitle: "完成身份核验",
|
||
recoveryDetail: "请使用注册手机号完成身份核验;核验通过后可重新尝试登录。",
|
||
},
|
||
};
|
||
|
||
const currentState = computed(() => statusConfig[status.value]);
|
||
|
||
const resolveStatus = (options = {}) => {
|
||
const requestedStatus = options.status || "risk";
|
||
status.value = Object.prototype.hasOwnProperty.call(
|
||
statusConfig,
|
||
requestedStatus,
|
||
)
|
||
? requestedStatus
|
||
: "risk";
|
||
recoveryVisible.value = false;
|
||
};
|
||
|
||
onLoad(resolveStatus);
|
||
|
||
const openRecovery = () => {
|
||
recoveryVisible.value = true;
|
||
};
|
||
const closeRecovery = () => {
|
||
recoveryVisible.value = false;
|
||
};
|
||
const requestBack = () => {
|
||
if (recoveryVisible.value) {
|
||
return runBackGuard({
|
||
transientOpen: true,
|
||
"close-transient": closeRecovery,
|
||
});
|
||
}
|
||
return goRoot("A01");
|
||
};
|
||
|
||
onBackPress((event) => handleBackPress(event, requestBack));
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.status-content {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
max-width: 480px;
|
||
min-width: 0;
|
||
margin: 0 auto;
|
||
padding: clamp(10px, 2.2vh, 20px) clamp(24px, 8.5vw, 44px);
|
||
}
|
||
.page-heading {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr);
|
||
grid-template-rows: auto auto auto;
|
||
justify-items: center;
|
||
min-height: 154rpx;
|
||
}
|
||
.back-button {
|
||
display: flex;
|
||
grid-row: 1 / -1;
|
||
grid-column: 1;
|
||
align-self: start;
|
||
justify-self: start;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: var(--app-touch-min);
|
||
height: var(--app-touch-min);
|
||
margin-top: 10rpx;
|
||
}
|
||
.back-button__icon {
|
||
width: 42rpx;
|
||
height: 42rpx;
|
||
transform: scaleX(-1);
|
||
}
|
||
.page-title {
|
||
grid-column: 1;
|
||
grid-row: 1;
|
||
color: #9f170f;
|
||
font-size: clamp(29px, 58rpx, 36px);
|
||
font-weight: 700;
|
||
letter-spacing: 7rpx;
|
||
}
|
||
.page-subtitle {
|
||
grid-column: 1;
|
||
grid-row: 2;
|
||
margin-top: 12rpx;
|
||
color: #806c58;
|
||
font-size: clamp(15px, 25rpx, 18px);
|
||
letter-spacing: 1rpx;
|
||
}
|
||
.status-divider {
|
||
grid-column: 1;
|
||
grid-row: 3;
|
||
width: 300rpx;
|
||
height: 50rpx;
|
||
margin-top: 2rpx;
|
||
filter: brightness(0.68) saturate(1.5) contrast(1.2);
|
||
}
|
||
|
||
.status-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding-top: 6rpx;
|
||
text-align: center;
|
||
}
|
||
.status-icon {
|
||
width: 94rpx;
|
||
height: 94rpx;
|
||
opacity: 0.82;
|
||
}
|
||
.status-eyebrow {
|
||
margin-top: 10rpx;
|
||
color: #a7160c;
|
||
font-size: clamp(14px, 22rpx, 17px);
|
||
letter-spacing: 3rpx;
|
||
}
|
||
.status-title {
|
||
margin-top: 10rpx;
|
||
color: #3f2c1d;
|
||
font-size: clamp(20px, 40rpx, 26px);
|
||
font-weight: 700;
|
||
letter-spacing: 3rpx;
|
||
}
|
||
.status-description {
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
margin-top: 12rpx;
|
||
color: #786654;
|
||
font-size: clamp(15px, 24rpx, 18px);
|
||
line-height: 1.65;
|
||
}
|
||
.status-details {
|
||
width: 100%;
|
||
margin-top: 22rpx;
|
||
border-top: 1rpx solid #cfaa70;
|
||
}
|
||
.status-detail {
|
||
display: flex;
|
||
box-sizing: border-box;
|
||
min-height: 78rpx;
|
||
padding: 16rpx 0;
|
||
border-bottom: 1rpx solid rgba(207, 170, 112, 0.72);
|
||
text-align: left;
|
||
}
|
||
.status-detail__label {
|
||
flex: 0 0 116rpx;
|
||
color: #9f170f;
|
||
font-size: clamp(14px, 23rpx, 17px);
|
||
}
|
||
.status-detail__value {
|
||
flex: 1;
|
||
color: #5f4a38;
|
||
font-size: clamp(14px, 23rpx, 17px);
|
||
line-height: max(1.35em, clamp(19px, 34rpx, 24px));
|
||
}
|
||
|
||
.status-primary {
|
||
display: grid;
|
||
place-items: center;
|
||
min-height: var(--app-touch-min);
|
||
margin-top: 28rpx;
|
||
}
|
||
.status-primary__skin,
|
||
.recovery-action__skin {
|
||
grid-area: 1 / 1;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
.status-primary__copy {
|
||
z-index: 1;
|
||
grid-area: 1 / 1;
|
||
color: #fffaf0;
|
||
font-size: clamp(20px, 38rpx, 26px);
|
||
letter-spacing: 5rpx;
|
||
}
|
||
.status-secondary {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: var(--app-touch-min);
|
||
color: #a7160c;
|
||
font-size: clamp(15px, 24rpx, 18px);
|
||
}
|
||
|
||
.recovery-layer {
|
||
position: fixed;
|
||
z-index: 20;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-sizing: border-box;
|
||
padding: 42rpx;
|
||
background: rgba(35, 18, 10, 0.62);
|
||
}
|
||
.recovery-dialog {
|
||
display: grid;
|
||
width: 620rpx;
|
||
min-height: 520rpx;
|
||
max-height: calc(var(--app-viewport-height) - 40px);
|
||
}
|
||
.recovery-dialog__skin {
|
||
grid-area: 1 / 1;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
.recovery-dialog__content {
|
||
z-index: 1;
|
||
display: flex;
|
||
grid-area: 1 / 1;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
min-height: 100%;
|
||
overflow-y: auto;
|
||
padding: 58rpx 58rpx 36rpx;
|
||
text-align: center;
|
||
}
|
||
.recovery-title {
|
||
color: #8f160f;
|
||
font-size: clamp(22px, 42rpx, 28px);
|
||
font-weight: 700;
|
||
letter-spacing: 4rpx;
|
||
}
|
||
.recovery-copy {
|
||
margin-top: 20rpx;
|
||
color: #513a28;
|
||
font-size: clamp(16px, 26rpx, 20px);
|
||
line-height: max(1.35em, clamp(20px, 40rpx, 26px));
|
||
}
|
||
.recovery-note {
|
||
margin-top: 12rpx;
|
||
color: #8d7965;
|
||
font-size: clamp(13px, 20rpx, 16px);
|
||
line-height: max(1.35em, clamp(17px, 31rpx, 22px));
|
||
}
|
||
.recovery-action {
|
||
display: grid;
|
||
place-items: center;
|
||
width: 100%;
|
||
min-height: 80rpx;
|
||
margin-top: 22rpx;
|
||
}
|
||
.recovery-action__copy {
|
||
z-index: 1;
|
||
grid-area: 1 / 1;
|
||
color: #fffaf0;
|
||
font-size: clamp(16px, 29rpx, 20px);
|
||
letter-spacing: 4rpx;
|
||
}
|
||
.tap-fade,
|
||
.button-hover {
|
||
opacity: 0.72;
|
||
}
|
||
</style>
|