Files
jiapuapp/pages/profile/compliance-document.vue
T

235 lines
6.7 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.
<template>
<view class="document-page" :class="`document-state--${complianceDocumentState}`">
<ModulePageBackground module="profile" />
<view class="page-layer">
<PageHeader :title="fallbackTitle" custom-back @back="requestBack" />
</view>
<view class="document-content page-layer">
<AppLoading
v-if="complianceDocumentState === 'loading'"
text="正在读取内容"
description="请稍候。"
/>
<view v-else-if="complianceDocumentState === 'error'" class="document-state-card">
<text>暂时无法读取内容</text>
<text>{{ errorMessage }}</text>
<AppButton block type="secondary" label="重新加载" @click="loadDocument" />
</view>
<view v-else class="document-sheet">
<view class="document-heading">
<text>{{ document.title }}</text>
<text>版本 {{ document.versionNo }}</text>
<text v-if="document.publishedAt">发布日期{{ displayDate(document.publishedAt) }}</text>
<text v-if="document.effectiveAt">生效日期{{ displayDate(document.effectiveAt) }}</text>
</view>
<view class="document-body">
<template v-for="(block, index) in contentBlocks" :key="`${block.type}-${index}`">
<text v-if="block.type === 'title'" class="content-title">{{ block.text }}</text>
<text v-else-if="block.type === 'heading'" class="content-heading">{{ block.text }}</text>
<view v-else-if="block.type === 'list-item'" class="content-list-item">
<text>{{ block.marker }}</text>
<text>{{ block.text }}</text>
</view>
<text v-else class="content-paragraph">{{ block.text }}</text>
</template>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from "vue";
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import {
createRequestController,
isRequestCancelled
} from "@/services/api/request-controller.js";
import {
COMPLIANCE_DOCUMENT_KEY
} from "@/services/api/site-content-contract.js";
import { siteContentApi } from "@/services/api/site-content-service.js";
import { getRequestErrorMessage } from "@/services/api/request-error-message.js";
import { formatComplianceContent } from "@/utils/profile/compliance-content.js";
import { goBack, handleBackPress } from "@/utils/navigation/gateway.js";
const documentKey = ref("");
const complianceDocumentState = ref("loading");
const errorMessage = ref("");
const document = reactive({
title: "",
versionNo: "",
content: "",
publishedAt: "",
effectiveAt: "",
});
const complianceDocumentRequestController = createRequestController();
let pageActive = true;
const fallbackTitle = computed(() =>
documentKey.value === COMPLIANCE_DOCUMENT_KEY.PRIVACY_POLICY
? "隐私政策"
: "用户协议",
);
const contentBlocks = computed(() =>
formatComplianceContent(document.content, document.title),
);
const displayDate = (value) => String(value || "").slice(0, 10);
const loadDocument = async () => {
if (!Object.values(COMPLIANCE_DOCUMENT_KEY).includes(documentKey.value)) {
errorMessage.value = "文档类型不正确,请返回后重新进入。";
complianceDocumentState.value = "error";
return;
}
complianceDocumentRequestController.abort();
complianceDocumentState.value = "loading";
errorMessage.value = "";
try {
const complianceDocument = await siteContentApi.getComplianceDocument(documentKey.value, {
requestController: complianceDocumentRequestController,
});
if (!pageActive) return;
Object.assign(document, complianceDocument);
complianceDocumentState.value = "ready";
} catch (error) {
if (!pageActive || isRequestCancelled(error)) return;
errorMessage.value = getRequestErrorMessage(error, "请检查网络后重新加载。");
complianceDocumentState.value = "error";
}
};
const requestBack = () => goBack();
onLoad((options = {}) => {
documentKey.value = String(options.documentKey || "");
loadDocument();
});
onBackPress((event) => handleBackPress(event, requestBack));
onUnload(() => {
pageActive = false;
complianceDocumentRequestController.abort();
});
</script>
<style scoped lang="scss">
@import "../../styles/adaptive-frame-profiles.scss";
.document-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.page-layer {
z-index: 1;
}
.document-content {
flex: 1;
padding: 24rpx 28rpx calc(72rpx + env(safe-area-inset-bottom));
}
.document-sheet,
.document-state-card {
@include adaptive-profile-content;
}
.document-sheet {
padding: 34rpx 36rpx 48rpx;
background-color: rgba(255, 252, 245, 0.9);
}
.document-heading {
padding-bottom: 24rpx;
border-bottom: 1rpx solid rgba(181, 137, 63, 0.42);
}
.document-heading text {
display: block;
}
.document-heading text:first-child {
color: $brand-red;
font-family: STKaiti, KaiTi, serif;
font-size: clamp(21px, 38rpx, 27px);
font-weight: 700;
line-height: 1.35;
}
.document-heading text:not(:first-child) {
margin-top: 8rpx;
color: $ink-muted;
font-size: clamp(13px, 21rpx, 16px);
}
.document-body {
margin-top: 26rpx;
}
.content-title,
.content-heading,
.content-paragraph {
display: block;
overflow-wrap: anywhere;
}
.content-title {
color: $ink;
font-size: clamp(18px, 31rpx, 23px);
font-weight: 700;
line-height: 1.45;
}
.content-heading {
margin-top: 30rpx;
color: $brand-red;
font-size: clamp(17px, 28rpx, 21px);
font-weight: 700;
line-height: 1.5;
}
.content-paragraph,
.content-list-item {
margin-top: 18rpx;
color: $ink;
font-size: clamp(15px, 25rpx, 18px);
line-height: 1.8;
}
.content-list-item {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 12rpx;
}
.content-list-item text:last-child {
overflow-wrap: anywhere;
}
.document-state-card {
min-height: 320rpx;
padding: 58rpx 42rpx 42rpx;
text-align: center;
}
.document-state-card > text {
display: block;
}
.document-state-card > text:first-child {
color: $ink;
font-size: clamp(19px, 33rpx, 24px);
font-weight: 700;
}
.document-state-card > text:nth-child(2) {
margin-top: 16rpx;
color: $ink-muted;
font-size: clamp(14px, 23rpx, 17px);
line-height: 1.6;
}
.document-state-card .app-button {
margin-top: 28rpx;
}
@media (max-width: 340px) {
.document-content {
padding-right: 22rpx;
padding-left: 22rpx;
}
.document-sheet {
padding-right: 28rpx;
padding-left: 28rpx;
}
}
</style>