Files
jiapuapp/pages/genealogy/g06-search-genealogies.vue
T
2026-07-14 17:36:43 +08:00

117 lines
7.6 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.
<!-- 页面编号G-06用途公开家谱检索统一承载初始加载结果与无结果状态 -->
<template>
<view class="search-page">
<!-- 底图只提供完整纸纹不以 CSS 补色或绘制装饰 -->
<image class="search-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="search-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<view class="search-page__header"><PageHeader title="查找家谱" /></view>
<view class="search-page__content">
<!-- 选定题签稿题签位图只提供材质品牌谱印和标题保留为真实应用元素 -->
<view class="search-title-strip">
<image class="search-title-strip__skin" src="/static/assets/modules/genealogy/opaque/g06-search-title-strip.png" mode="scaleToFill" />
<image class="search-title-strip__seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
<text class="search-title-strip__text">公开家谱检索</text>
</view>
<view class="search-controls">
<view class="search-field">
<image class="search-field__skin" src="/static/assets/modules/genealogy/opaque/g06-search-input-wide.png" mode="scaleToFill" />
<input v-model="keyword" class="search-input" placeholder="输入姓氏、谱名或地区" placeholder-class="search-input__placeholder" @confirm="search" />
</view>
<view class="search-action" @click="search">
<image class="search-action__skin" src="/static/assets/modules/genealogy/opaque/g06-search-button.png" mode="scaleToFill" />
<text class="search-action__label">搜索</text>
</view>
</view>
<image class="search-divider" src="/static/assets/modules/genealogy/transparent/section-divider.png" mode="scaleToFill" />
<!-- 同一路由内以数据状态切换避免为结果或空结果新增占位页面 -->
<view v-if="searchState === 'loading'" class="search-status search-loading">
<text>正在整理可申请加入的家谱</text>
</view>
<view v-else-if="searchState === 'initial'" class="search-status search-initial">
<text class="search-status__lead">从姓氏谱名或祖居地开始</text>
<text class="search-status__copy">找到合适的家谱后可申请加入并查阅</text>
<image class="search-status__hall" src="/static/assets/foundation/transparent/root-header-hall.png" mode="aspectFit" />
</view>
<view v-else-if="searchState === 'results'" class="search-results">
<view class="search-results__head"><text>检索到 {{ results.length }} 部可申请的家谱</text><text>点选后填写申请</text></view>
<GenealogyCard v-for="item in results" :key="item.id" :genealogy="item" role="可申请" @select="openApply" />
</view>
<view v-else class="search-status search-empty">
<image class="search-status__cloud" src="/static/assets/modules/genealogy/transparent/create-cloud.png" mode="aspectFit" />
<text class="search-status__lead">暂未找到公开家谱</text>
<text class="search-status__copy">可换一个姓氏谱名或祖居地再试</text>
</view>
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import PageHeader from '@/components/PageHeader.vue'
import GenealogyCard from '@/components/GenealogyCard.vue'
import { appApi } from '@/utils/api.js'
const keyword = ref('')
const allResults = ref([])
const results = ref([])
const searchState = ref('initial')
onMounted(async () => {
searchState.value = 'loading'
allResults.value = await appApi.getPublicGenealogies()
searchState.value = 'initial'
})
const search = () => {
const value = keyword.value.trim()
results.value = value
? allResults.value.filter((item) => `${item.name}${item.surname}${item.location}`.includes(value))
: allResults.value
searchState.value = results.value.length ? 'results' : 'empty'
}
const openApply = (genealogy) => uni.navigateTo({ url: `/pages/genealogy/g08-join-application?genealogyId=${genealogy.id}` })
</script>
<style scoped lang="scss">
/* G06 的完整检索位图与叠放控件样式必须作为同一 SFC 样式模块刷新。 */
.search-page { position: relative; min-height: 100vh; overflow: hidden; background: $paper; }
.search-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.search-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .15; pointer-events: none; }
.search-page__header { position: relative; z-index: 2; }
.search-page__content { position: relative; z-index: 3; min-height: calc(100vh - 104rpx); padding: 30rpx 24rpx calc(58rpx + env(safe-area-inset-bottom)); box-sizing: border-box; }
.search-title-strip { position: relative; width: 100%; height: 138rpx; }
.search-title-strip__skin { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.search-title-strip__seal { position: absolute; top: 27rpx; left: 52rpx; z-index: 1; width: 72rpx; height: 84rpx; }
.search-title-strip__text { position: absolute; top: 0; right: 170rpx; bottom: 0; left: 156rpx; z-index: 1; display: flex; align-items: center; justify-content: center; color: #f5d596; font-family: 'STKaiti', 'KaiTi', serif; font-size: 39rpx; font-weight: 700; letter-spacing: 5rpx; }
.search-controls { display: flex; align-items: center; gap: 20rpx; margin-top: 48rpx; }
.search-field { position: relative; width: 430rpx; height: 95rpx; }
.search-field__skin, .search-action__skin { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.search-input { position: relative; z-index: 1; width: 100%; height: 100%; padding: 0 30rpx; box-sizing: border-box; color: $ink; font-size: 26rpx; }
.search-input__placeholder { color: #ab9c83; }
.search-action { position: relative; display: flex; width: 200rpx; height: 88rpx; align-items: center; justify-content: center; }
.search-action__label { position: relative; z-index: 1; color: #fff4dc; font-family: 'STKaiti', 'KaiTi', serif; font-size: 31rpx; font-weight: 700; letter-spacing: 7rpx; }
.search-divider { display: block; width: 100%; height: 30rpx; margin: 40rpx 0 34rpx; }
.search-status { display: flex; min-height: 190rpx; flex-direction: column; align-items: center; justify-content: center; padding: 10rpx 28rpx 24rpx; box-sizing: border-box; text-align: center; }
.search-status__cloud { width: 92rpx; height: 46rpx; margin-bottom: 4rpx; opacity: .78; }
.search-status__lead { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 29rpx; font-weight: 700; letter-spacing: 2rpx; }
.search-status__copy { margin-top: 12rpx; color: $ink-muted; font-size: 22rpx; }
.search-status__hall { width: 330rpx; height: 132rpx; margin-top: 330rpx; opacity: .22; filter: grayscale(1); }
.search-loading { min-height: 168rpx; color: $ink-muted; font-size: 24rpx; }
.search-results { display: flex; flex-direction: column; gap: 18rpx; margin-top: 6rpx; }
.search-results__head { display: flex; align-items: center; justify-content: space-between; padding: 0 16rpx; color: $ink-muted; font-size: 22rpx; }
.search-results__head text:first-child { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 27rpx; font-weight: 700; }
@media screen and (max-width: 340px) {
.search-page__content { padding-right: 20rpx; padding-left: 20rpx; }
.search-title-strip__seal { left: 44rpx; }
.search-title-strip__text { right: 148rpx; left: 138rpx; font-size: 37rpx; }
.search-field { width: 430rpx; }
.search-action { width: 200rpx; height: 88rpx; }
}
</style>