Files
jiapuapp/components/ModulePage.vue
T
2026-07-14 07:01:45 +08:00

84 lines
10 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="module-page" :class="`module-page--${page.template}`">
<!-- 背景层复用已验收的宣纸淡墨山水资产保持全量页面视觉一致 -->
<image class="module-page__paper" :src="isAuth ? '/static/assets/foundation/opaque/auth-page-paper.jpg' : '/static/assets/foundation/opaque/page-paper.jpg'" mode="aspectFill" />
<image class="module-page__scenery" src="/static/assets/foundation/transparent/auth-ink-scenery.png" mode="aspectFill" />
<PageHeader :title="page.title" :root="isRoot" :action="page.number" />
<view class="module-page__content" :class="{ 'module-page__content--tab': page.tab }">
<view class="page-intro">
<image class="page-intro__seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
<view><text class="page-intro__number">{{ page.number }}</text><text class="page-intro__subtitle">{{ page.subtitle }}</text></view>
</view>
<!-- 表单与认证母版保留真实 input便于后续接入接口 -->
<view v-if="page.template === 'auth' || page.template === 'form'" class="form-card paper-card">
<view v-for="field in page.fields" :key="field" class="form-row"><text>{{ field }}</text><input :placeholder="`请输入${field}`" placeholder-class="form-placeholder" /></view>
<text v-if="page.note" class="form-note">{{ page.note }}</text>
<view class="primary-action" @click="showPending"><text>{{ page.action }}</text></view>
</view>
<!-- 列表母版用于家谱成员文章相册礼仪及记录 -->
<view v-else-if="page.template === 'list'" class="content-stack">
<view v-for="item in page.sections" :key="item[0]" class="list-card paper-card" @click="showPending"><view class="list-card__mark">谱</view><view class="list-card__copy"><text>{{ item[0] }}</text><text>{{ item[1] }}</text></view><text class="list-card__arrow"></text></view>
<view class="primary-action primary-action--compact" @click="showPending"><text>{{ page.action }}</text></view>
</view>
<!-- 详情母版强调一项主题信息与分段资料 -->
<view v-else-if="page.template === 'detail'" class="content-stack">
<view class="detail-hero paper-card"><text class="detail-hero__seal">{{ page.number.slice(0, 1) }}</text><text class="detail-hero__title">{{ page.title }}</text><text>{{ page.subtitle }}</text></view>
<view v-for="item in page.sections" :key="item[0]" class="detail-card paper-card"><text>{{ item[0] }}</text><text>{{ item[1] }}</text></view>
<view class="primary-action" @click="showPending"><text>{{ page.action }}</text></view>
</view>
<!-- 时间轴母版用于申请成长和人生事件 -->
<view v-else-if="page.template === 'timeline'" class="timeline paper-card">
<view v-for="item in page.sections" :key="item[0]" class="timeline__item"><view class="timeline__dot"></view><view><text>{{ item[0] }}</text><text>{{ item[1] }}</text></view></view>
<view class="primary-action primary-action--compact" @click="showPending"><text>{{ page.action }}</text></view>
</view>
<!-- 设置母版提供清晰的分组入口和状态说明 -->
<view v-else-if="page.template === 'settings'" class="settings paper-card">
<view v-for="item in page.sections" :key="item[0]" class="settings__row" @click="showPending"><view><text>{{ item[0] }}</text><text>{{ item[1] }}</text></view><text></text></view>
<view class="primary-action primary-action--compact" @click="showPending"><text>{{ page.action }}</text></view>
</view>
<!-- 待接入状态使用题签式留白避免裸页面 -->
<view v-else class="status-card paper-card"><text class="status-card__badge">{{ page.badge }}</text><text class="status-card__lead">{{ page.lead }}</text><text class="status-card__note">{{ page.note }}</text><view class="primary-action" @click="showPending"><text>{{ page.action }}</text></view></view>
</view>
<AppTabbar v-if="page.tab" :active="page.tab" />
</view>
</template>
<script setup>
import { computed } from 'vue'
import PageHeader from '@/components/PageHeader.vue'
import AppTabbar from '@/components/AppTabbar.vue'
import { pageCatalog } from '@/data/page-catalog.js'
const props = defineProps({ pageId: { type: String, required: true } })
const page = computed(() => pageCatalog[props.pageId])
const isAuth = computed(() => page.value.template === 'auth')
const isRoot = computed(() => false)
const showPending = () => uni.showToast({ title: '该功能正在完善中', icon: 'none' })
</script>
<style scoped lang="scss">
.module-page { position: relative; min-height: 100vh; overflow: hidden; background: $paper; }
.module-page__paper, .module-page__scenery { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.module-page__paper { opacity: .98; }.module-page__scenery { opacity: .32; }
.module-page__content { position: relative; z-index: 1; min-height: calc(100vh - 104rpx); padding: 34rpx 28rpx calc(64rpx + env(safe-area-inset-bottom)); box-sizing: border-box; }
.module-page__content--tab { padding-bottom: calc(190rpx + env(safe-area-inset-bottom)); }
.page-intro { display: flex; align-items: center; min-height: 104rpx; margin: 0 10rpx 26rpx; }
.page-intro__seal { width: 74rpx; height: 74rpx; margin-right: 14rpx; }.page-intro__number, .page-intro__subtitle { display: block; }
.page-intro__number { color: $brand-red; font-family: STKaiti, KaiTi, serif; font-size: 27rpx; font-weight: 700; letter-spacing: 3rpx; }.page-intro__subtitle { margin-top: 6rpx; color: $ink-muted; font-size: 22rpx; }
.paper-card { position: relative; border: 1rpx solid $gold-soft; background: rgba(255, 250, 240, .9); box-shadow: inset 0 0 0 6rpx rgba(255, 255, 255, .48); }
.form-card { padding: 16rpx 30rpx 34rpx; }.form-row { display: flex; align-items: center; min-height: 104rpx; border-bottom: 1rpx solid #ead9ba; }.form-row text { width: 142rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 29rpx; }.form-row input { min-width: 0; flex: 1; color: $ink; font-size: 27rpx; }.form-placeholder { color: #b3a895; }.form-note { display: block; margin-top: 22rpx; color: $ink-muted; font-size: 21rpx; text-align: center; }
.primary-action { position: relative; display: flex; height: 100rpx; align-items: center; justify-content: center; margin-top: 34rpx; overflow: hidden; background-image: url('/static/assets/foundation/opaque/a01-primary-button.png'); background-position: center; background-repeat: no-repeat; background-size: 100% 100%; color: #fff9ec; font-family: STKaiti, KaiTi, serif; font-size: 34rpx; font-weight: 700; letter-spacing: 6rpx; }.primary-action--compact { height: 82rpx; font-size: 28rpx; }
.content-stack { display: flex; flex-direction: column; gap: 20rpx; }.list-card { display: flex; min-height: 132rpx; align-items: center; padding: 16rpx 22rpx; }.list-card__mark { display: flex; width: 74rpx; height: 86rpx; align-items: center; justify-content: center; margin-right: 18rpx; border: 2rpx solid $brand-red; color: $brand-red; font-family: STKaiti, KaiTi, serif; font-size: 29rpx; font-weight: 700; }.list-card__copy { min-width: 0; flex: 1; }.list-card__copy text, .detail-card text, .timeline__item text, .settings__row text { display: block; }.list-card__copy text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 32rpx; font-weight: 700; }.list-card__copy text:last-child { margin-top: 9rpx; color: $ink-muted; font-size: 21rpx; }.list-card__arrow { color: $gold; font-size: 44rpx; }
.detail-hero { display: flex; flex-direction: column; align-items: center; padding: 38rpx 30rpx; text-align: center; }.detail-hero__seal { display: flex; width: 80rpx; height: 96rpx; align-items: center; justify-content: center; border: 2rpx solid $brand-red; color: $brand-red; font-family: STKaiti, KaiTi, serif; font-size: 38rpx; font-weight: 700; }.detail-hero__title { margin: 20rpx 0 12rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 40rpx; font-weight: 700; }.detail-hero > text:last-child { color: $ink-muted; font-size: 22rpx; }.detail-card { padding: 28rpx; }.detail-card text:first-child { color: $brand-red; font-family: STKaiti, KaiTi, serif; font-size: 29rpx; font-weight: 700; }.detail-card text:last-child { margin-top: 14rpx; color: $ink-muted; font-size: 25rpx; line-height: 1.8; }
.timeline { padding: 12rpx 28rpx 30rpx; }.timeline__item { position: relative; display: flex; gap: 22rpx; min-height: 112rpx; padding: 30rpx 0 0; }.timeline__item:not(:last-of-type)::before { position: absolute; top: 55rpx; left: 9rpx; width: 2rpx; height: 78rpx; background: $gold-soft; content: ''; }.timeline__dot { width: 20rpx; height: 20rpx; margin-top: 8rpx; border-radius: 50%; background: $brand-red; }.timeline__item text:first-child { color: $ink; font-size: 29rpx; font-weight: 700; }.timeline__item text:last-child { margin-top: 9rpx; color: $ink-muted; font-size: 22rpx; }
.settings { padding: 0 28rpx 28rpx; }.settings__row { display: flex; min-height: 116rpx; align-items: center; justify-content: space-between; border-bottom: 1rpx solid #ead9ba; }.settings__row text:first-child { color: $ink; font-size: 29rpx; }.settings__row text:last-child { margin-top: 8rpx; color: $ink-muted; font-size: 21rpx; }.settings__row > text { color: $gold; font-size: 42rpx; }.status-card { display: flex; min-height: 540rpx; flex-direction: column; align-items: center; justify-content: center; padding: 40rpx; text-align: center; }.status-card__badge { display: flex; width: 116rpx; height: 136rpx; align-items: center; justify-content: center; border: 2rpx solid $brand-red; color: $brand-red; font-family: STKaiti, KaiTi, serif; font-size: 50rpx; font-weight: 700; }.status-card__lead { margin-top: 30rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 36rpx; font-weight: 700; }.status-card__note { margin-top: 16rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.7; }
</style>