235 lines
6.3 KiB
Vue
235 lines
6.3 KiB
Vue
<!-- 页面编号:M-10;用途:协议、隐私、版本与退出登录。 -->
|
||
<template>
|
||
<view
|
||
class="about-page"
|
||
:class="{ 'about-state--logging-out': logoutSubmitting }"
|
||
>
|
||
<ModulePageBackground module="profile" />
|
||
<view class="page-layer"
|
||
><PageHeader title="关于家谱" custom-back @back="requestBack"
|
||
/></view>
|
||
<view class="page-content page-layer">
|
||
<view class="brand-card"
|
||
><image
|
||
src="/static/assets/foundation/transparent/brand-seal.png"
|
||
mode="aspectFit"
|
||
/><text>家谱</text><text>传承每一段值得珍藏的家族记忆</text
|
||
><text>版本 {{ appVersion }}</text></view
|
||
>
|
||
<view class="settings-list">
|
||
<view
|
||
v-for="item in agreementItems"
|
||
:key="item.key"
|
||
class="settings-row"
|
||
role="button"
|
||
:aria-label="item.label"
|
||
@click="openAgreement(item)"
|
||
><view
|
||
><text>{{ item.label }}</text
|
||
><text>{{ item.note }}</text></view
|
||
><image
|
||
src="/static/assets/foundation/transparent/chevron-right.png"
|
||
mode="aspectFit"
|
||
/></view>
|
||
</view>
|
||
<AppButton
|
||
block
|
||
type="secondary"
|
||
label="退出登录"
|
||
@click="logoutVisible = true"
|
||
/>
|
||
</view>
|
||
<AppDialog
|
||
:visible="agreementVisible"
|
||
:close-on-mask="false"
|
||
eyebrow="协议与说明"
|
||
:title="activeAgreement.label"
|
||
:message="activeAgreement.copy"
|
||
confirm-text="关闭"
|
||
@confirm="agreementVisible = false"
|
||
@close="agreementVisible = false"
|
||
/>
|
||
<AppDialog
|
||
:visible="logoutVisible"
|
||
eyebrow="账号操作"
|
||
title="确认退出登录?"
|
||
message="退出后需要重新验证账号;本机保存的密码不会被保留。"
|
||
:confirm-text="logoutSubmitting ? '正在退出' : '确认退出'"
|
||
cancel-text="取消"
|
||
show-cancel
|
||
:close-on-mask="false"
|
||
@confirm="confirmLogout"
|
||
@cancel="logoutVisible = false"
|
||
@close="logoutVisible = false"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { reactive, ref } from "vue";
|
||
import { onBackPress, onUnload } from "@dcloudio/uni-app";
|
||
import AppButton from "@/components/AppButton.vue";
|
||
import AppDialog from "@/components/AppDialog.vue";
|
||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||
import PageHeader from "@/components/PageHeader.vue";
|
||
import manifest from "@/manifest.json";
|
||
import { appApi, createRequestController } from "@/utils/api.js";
|
||
import { goRoot, handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||
import { session } from "@/utils/session.js";
|
||
|
||
const appVersion = manifest.versionName;
|
||
const agreementItems = [
|
||
{
|
||
key: "terms",
|
||
label: "用户协议",
|
||
note: "了解账号与服务使用规则",
|
||
copy: "用户协议正文将在正式协议服务接入后展示。当前页面不代表最终法律文本。",
|
||
},
|
||
{
|
||
key: "privacy",
|
||
label: "隐私政策",
|
||
note: "了解个人资料如何使用与保护",
|
||
copy: "隐私政策正文将在正式协议服务接入后展示。敏感资料默认按权限与脱敏规则展示。",
|
||
},
|
||
{
|
||
key: "version",
|
||
label: "版本说明",
|
||
note: `当前版本 ${appVersion}`,
|
||
copy: `当前安装版本为 ${appVersion}。基础版本采用浅色国风主题。`,
|
||
},
|
||
];
|
||
const activeAgreement = reactive({ label: "", copy: "" });
|
||
const agreementVisible = ref(false);
|
||
const logoutVisible = ref(false);
|
||
const logoutSubmitting = ref(false);
|
||
const logoutRequestController = createRequestController();
|
||
const openAgreement = (item) => {
|
||
activeAgreement.label = item.label;
|
||
activeAgreement.copy = item.copy;
|
||
agreementVisible.value = true;
|
||
};
|
||
const confirmLogout = async () => {
|
||
if (logoutSubmitting.value) return false;
|
||
logoutSubmitting.value = true;
|
||
try {
|
||
await appApi.logout({ requestController: logoutRequestController });
|
||
} catch {
|
||
// 退出请求的结果未知时仍必须撤销本机会话,不能保留旧授权或自动重试。
|
||
} finally {
|
||
session.clear();
|
||
logoutVisible.value = false;
|
||
logoutSubmitting.value = false;
|
||
}
|
||
return goRoot("A01");
|
||
};
|
||
const closeActiveDialog = () => {
|
||
if (logoutVisible.value) logoutVisible.value = false;
|
||
else agreementVisible.value = false;
|
||
};
|
||
const requestBack = () =>
|
||
runBackGuard({
|
||
transientOpen: agreementVisible.value || logoutVisible.value,
|
||
"close-transient": closeActiveDialog,
|
||
});
|
||
onBackPress((event) => handleBackPress(event, requestBack));
|
||
onUnload(() => logoutRequestController.abort());
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@import "../../styles/adaptive-frame-profiles.scss";
|
||
.about-page {
|
||
display: flex;
|
||
min-height: 100vh;
|
||
flex-direction: column;
|
||
background: $paper;
|
||
}
|
||
.page-layer {
|
||
z-index: 1;
|
||
}
|
||
.page-content {
|
||
flex: 1;
|
||
padding: 28rpx 30rpx 72rpx;
|
||
}
|
||
.brand-card {
|
||
@include adaptive-profile-summary;
|
||
display: flex;
|
||
min-height: 300rpx;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 36rpx 44rpx;
|
||
text-align: center;
|
||
}
|
||
.brand-card image {
|
||
width: 92rpx;
|
||
height: 106rpx;
|
||
}
|
||
.brand-card text {
|
||
display: block;
|
||
}
|
||
.brand-card text:nth-child(2) {
|
||
margin-top: 6rpx;
|
||
color: $ink;
|
||
font-family: STKaiti, KaiTi, serif;
|
||
font-size: clamp(20px, 40rpx, 26px);
|
||
font-weight: 700;
|
||
letter-spacing: 4rpx;
|
||
}
|
||
.brand-card text:nth-child(3) {
|
||
margin-top: 8rpx;
|
||
color: $ink-muted;
|
||
font-size: clamp(14px, 22rpx, 17px);
|
||
line-height: 1.5;
|
||
}
|
||
.brand-card text:last-child {
|
||
margin-top: 10rpx;
|
||
color: $ink-muted;
|
||
font-size: clamp(13px, 20rpx, 16px);
|
||
}
|
||
.settings-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
margin-top: 22rpx;
|
||
}
|
||
.settings-row {
|
||
@include adaptive-profile-field;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 32rpx;
|
||
min-height: 104rpx;
|
||
align-items: center;
|
||
gap: 18rpx;
|
||
padding: 18rpx 30rpx;
|
||
}
|
||
.settings-row view {
|
||
min-width: 0;
|
||
}
|
||
.settings-row text {
|
||
display: block;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.settings-row text:first-child {
|
||
color: $ink;
|
||
font-size: clamp(15px, 24rpx, 18px);
|
||
font-weight: 700;
|
||
}
|
||
.settings-row text:last-child {
|
||
margin-top: 6rpx;
|
||
color: $ink-muted;
|
||
font-size: clamp(13px, 20rpx, 16px);
|
||
}
|
||
.settings-row image {
|
||
width: 30rpx;
|
||
height: 30rpx;
|
||
}
|
||
.page-content > .app-button {
|
||
margin-top: 28rpx;
|
||
}
|
||
@media (max-width: 340px) {
|
||
.page-content {
|
||
padding-right: 22rpx;
|
||
padding-left: 22rpx;
|
||
}
|
||
}
|
||
</style>
|