|
- <template>
- <view class="agreement-page">
- <!-- 加载中:协议正文有 3 万多字,等待期间给骨架反馈 -->
- <view v-if="loading" class="skeleton">
- <view v-for="i in 8" :key="i" class="skeleton-line" :class="{ short: i % 4 === 0 }"></view>
- </view>
-
- <!-- 富文本正文:统一用 uview-plus 的 up-parse 渲染,各端表现一致 -->
- <up-parse v-else-if="content" :content="content" :tag-style="tagStyle" selectable />
-
- <!-- 空态 / 失败态:内容缺失时不能是白屏 -->
- <view v-else class="empty">
- <text class="empty-title">{{ errorMsg ? '内容加载失败' : '内容暂未发布' }}</text>
- <text class="empty-desc">{{ errorMsg || '该内容正在整理中,可稍后再来查看。' }}</text>
- <view class="empty-btn" @click="loadHtml">重新加载</view>
- </view>
- </view>
- </template>
-
- <script setup>
- import { computed, ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import { commonApi } from '@/api/index.js'
- import { cleanRichText } from '@/utils/richtext.js'
-
- /**
- * 富文本标签样式重置。
- * 后台正文是 Word 导出的,标签用得很随意(p 套 span、ul 无 li 缩进、字号写死 14px),
- * 这里统一接管排版:正文 28rpx / 行高 1.8,段间距用 margin 而不是空段落。
- */
- const tagStyle = {
- p: 'margin: 0 0 20rpx; padding: 0; line-height: 1.8; font-size: 28rpx; color: #172033; word-break: break-word;',
- div: 'line-height: 1.8; font-size: 28rpx; color: #172033; word-break: break-word;',
- span: 'font-size: 28rpx; line-height: 1.8; color: #172033;',
- h1: 'margin: 34rpx 0 16rpx; padding: 0; font-size: 34rpx; font-weight: 700; line-height: 1.45; color: #172033;',
- h2: 'margin: 30rpx 0 14rpx; padding: 0; font-size: 32rpx; font-weight: 700; line-height: 1.45; color: #172033;',
- h3: 'margin: 28rpx 0 12rpx; padding: 0; font-size: 30rpx; font-weight: 700; line-height: 1.45; color: #172033;',
- h4: 'margin: 26rpx 0 12rpx; padding: 0; font-size: 29rpx; font-weight: 700; line-height: 1.45; color: #172033;',
- ul: 'margin: 16rpx 0; padding-left: 44rpx; list-style: disc;',
- ol: 'margin: 16rpx 0; padding-left: 44rpx; list-style: decimal;',
- li: 'margin: 0 0 10rpx; line-height: 1.8; font-size: 28rpx; color: #172033;',
- strong: 'font-weight: 700; color: #172033;',
- b: 'font-weight: 700; color: #172033;',
- a: 'color: #1f8cff; word-break: break-all;',
- br: 'line-height: 1.8;',
- hr: 'margin: 28rpx 0; border: none; border-top: 1rpx solid #eef2f7;',
- img: 'max-width: 100%; height: auto; border-radius: 12rpx;',
- table: 'width: 100%; margin: 20rpx 0; border-collapse: collapse; font-size: 26rpx;',
- td: 'padding: 12rpx; border: 1rpx solid #e6ebf2; line-height: 1.6;',
- th: 'padding: 12rpx; border: 1rpx solid #e6ebf2; background: #f7f9fd; font-weight: 700; line-height: 1.6;'
- }
-
- const detail = ref({})
- const loading = ref(true)
- const errorMsg = ref('')
- let configKey = ''
-
- // 清洗后的正文:去空段落、剥内联字体与颜色,排版交给上面的 tagStyle
- const content = computed(() => cleanRichText(detail.value.content))
-
- onLoad((options) => {
- configKey = (options && options.key) || ''
- loadHtml()
- })
-
- async function loadHtml() {
- if (!configKey) {
- loading.value = false
- errorMsg.value = '缺少内容标识'
- return
- }
- loading.value = true
- errorMsg.value = ''
- try {
- const res = await commonApi.getHtml({ key: configKey })
- detail.value = res.data || {}
- if (detail.value.title) {
- uni.setNavigationBarTitle({ title: detail.value.title })
- }
- } catch (e) {
- errorMsg.value = (e && e.msg) || '网络异常,请稍后重试'
- } finally {
- loading.value = false
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .agreement-page {
- min-height: 100vh;
- padding: 30rpx 30rpx 60rpx;
- background: #ffffff;
- box-sizing: border-box;
- }
-
- .skeleton-line {
- height: 30rpx;
- margin-bottom: 22rpx;
- border-radius: 8rpx;
- background: #f1f5fa;
-
- &.short { width: 60%; }
- }
-
- .empty {
- padding: 160rpx 40rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
- }
- .empty-title { color: $ai-text; font-size: 32rpx; font-weight: 700; }
- .empty-desc {
- margin-top: 14rpx;
- color: $ai-muted;
- font-size: 26rpx;
- line-height: 1.6;
- }
- .empty-btn {
- margin-top: 30rpx;
- width: 240rpx;
- height: 76rpx;
- border-radius: 999rpx;
- color: #1f8cff;
- background: #eef7ff;
- font-size: 27rpx;
- font-weight: 700;
- line-height: 76rpx;
- }
- </style>
|