Files
jiapuapp/pages/auth/a06-auth-status.vue
T

415 lines
11 KiB
Vue
Raw 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.
<!-- 页面编号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>