feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
<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 72rpx;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user