验收完成40%

This commit is contained in:
2026-07-17 08:08:31 +08:00
parent 23cfd365a1
commit 887481a4ab
172 changed files with 855 additions and 577 deletions
+169 -144
View File
@@ -1,16 +1,10 @@
<!-- 页面编号G-01用途我的家谱列表与首次分流本轮仍待用户重新审核 -->
<template>
<view class="page-shell genealogy-index">
<image
class="page-paper-texture"
src="/static/assets/foundation/opaque/page-paper.jpg"
mode="scaleToFill"
/>
<image
class="page-footer-landscape"
src="/static/assets/foundation/transparent/footer-mountain-bamboo.png"
mode="scaleToFill"
/>
<view
class="page-shell genealogy-index"
:class="{ 'genealogy-index--split': isListLayout }"
>
<GenealogyPageBackground />
<PageHeader
root
title="我的家谱"
@@ -31,14 +25,15 @@
<text class="state-title">暂时无法读取家谱</text>
<text class="state-copy">网络或服务暂不可用请稍后重新查看</text>
<view class="state-retry" hover-class="action-hover" @click="retryLoad">
<image class="state-retry__skin" src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image class="state-retry__skin" src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text class="state-retry__copy">重新加载</text>
</view>
</view>
</view>
<template v-else-if="hasGenealogies">
<view class="current-slip" @click="openSwitcher">
<view class="genealogy-fixed-zone">
<view class="current-slip" @click="openSwitcher">
<image
class="current-frame"
src="/static/assets/modules/genealogy/transparent/current-slip-frame.png"
@@ -87,90 +82,98 @@
<text class="current-meta-item-text">管理员</text>
</view>
</view>
</view>
<view class="shortcut-grid">
<view
v-for="item in shortcuts"
:key="item.key"
class="shortcut-item"
@click="openShortcut(item.key)"
>
<image class="shortcut-icon" :src="item.icon" mode="aspectFit" />
<text class="shortcut-label">{{ item.label }}</text>
</view>
</view>
<image
class="section-divider"
src="/static/assets/modules/genealogy/transparent/section-divider.png"
mode="scaleToFill"
/>
<view class="genealogy-lower">
<view v-if="createdGenealogies.length" class="list-section">
<view class="section-heading"><text>我创建的</text></view>
<GenealogyCard
v-for="item in createdGenealogies"
:key="item.id"
:genealogy="item"
role="管理员"
:selected="item.id === currentGenealogy.id"
@select="openGenealogy"
/>
</view>
<view v-if="joinedGenealogies.length" class="list-section">
<view class="section-heading"><text>我加入的</text></view>
<GenealogyCard
v-for="item in joinedGenealogies"
:key="item.id"
:genealogy="item"
role="成员"
@select="openGenealogy"
/>
</view>
<view class="list-section application-section">
<view class="section-heading"><text>加入申请</text></view>
<view class="shortcut-grid">
<view
v-for="item in applicationRecords"
:key="item.id"
class="application-record"
hover-class="action-hover"
@click="openApplication(item)"
v-for="item in shortcuts"
:key="item.key"
class="shortcut-item"
@click="openShortcut(item.key)"
>
<image class="application-record__skin" src="/static/assets/modules/genealogy/transparent/list-slip-frame.png" mode="scaleToFill" />
<view class="application-record__main">
<view class="application-record__title-row">
<text class="application-record__name">{{ item.name }}</text>
<text class="application-record__status" :class="`application-record__status--${item.tone}`">{{ item.statusLabel }}</text>
</view>
<text class="application-record__copy">{{ item.description }}</text>
</view>
<image class="application-record__chevron" src="/static/assets/foundation/transparent/chevron-right.png" mode="aspectFit" />
<image class="shortcut-icon" :src="item.icon" mode="aspectFit" />
<text class="shortcut-label">{{ item.label }}</text>
</view>
</view>
<view class="create-action" @click="openAddDialog">
<image
class="create-cloud"
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit"
/>
<image
class="create-icon"
src="/static/assets/modules/genealogy/transparent/add.png"
mode="aspectFit"
/>
<text>添加家谱</text>
<image
class="create-cloud create-cloud--right"
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit"
/>
</view>
<image
class="section-divider"
src="/static/assets/modules/genealogy/transparent/section-divider.png"
mode="scaleToFill"
/>
</view>
<scroll-view
class="genealogy-list-scroll"
scroll-y
:scroll-top="listScrollCommand"
@scroll="handleListScroll"
>
<view class="genealogy-lower">
<view v-if="createdGenealogies.length" class="list-section">
<view class="section-heading"><text>我创建的</text></view>
<GenealogyCard
v-for="item in createdGenealogies"
:key="item.id"
:genealogy="item"
role="管理员"
:selected="item.id === currentGenealogy.id"
@select="openGenealogy"
/>
</view>
<view v-if="joinedGenealogies.length" class="list-section">
<view class="section-heading"><text>我加入的</text></view>
<GenealogyCard
v-for="item in joinedGenealogies"
:key="item.id"
:genealogy="item"
role="成员"
@select="openGenealogy"
/>
</view>
<view class="list-section application-section">
<view class="section-heading"><text>加入申请</text></view>
<view
v-for="item in applicationRecords"
:key="item.id"
class="application-record"
hover-class="action-hover"
@click="openApplication(item)"
>
<image class="application-record__skin" src="/static/assets/modules/genealogy/transparent/list-slip-frame.png" mode="scaleToFill" />
<view class="application-record__main">
<view class="application-record__title-row">
<text class="application-record__name">{{ item.name }}</text>
<text class="application-record__status" :class="`application-record__status--${item.tone}`">{{ item.statusLabel }}</text>
</view>
<text class="application-record__copy">{{ item.description }}</text>
</view>
<image class="application-record__chevron" src="/static/assets/foundation/transparent/chevron-right.png" mode="aspectFit" />
</view>
</view>
<view class="create-action" @click="openAddDialog">
<image
class="create-cloud"
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit"
/>
<image
class="create-icon"
src="/static/assets/modules/genealogy/transparent/add.png"
mode="aspectFit"
/>
<text>添加家谱</text>
<image
class="create-cloud create-cloud--right"
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit"
/>
</view>
</view>
</scroll-view>
</template>
<view v-else class="genealogy-empty-state">
@@ -203,8 +206,8 @@
>
<image
class="empty-search-action__skin"
src="/static/assets/foundation/opaque/a01-primary-button.png"
mode="scaleToFill"
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/>
<text class="empty-search-action__copy empty-search-action__copy--primary">搜索家谱</text>
</view>
@@ -215,8 +218,8 @@
>
<image
class="empty-invite-action__skin"
src="/static/assets/foundation/opaque/a01-secondary-button.png"
mode="scaleToFill"
src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png"
mode="aspectFit"
/>
<text class="empty-invite-action__copy">邀请码加入</text>
</view>
@@ -231,19 +234,13 @@
<view v-if="addDialogVisible" class="add-dialog-layer" @click="closeAddDialog">
<view class="add-dialog" @click.stop>
<image class="add-dialog__skin" src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png" mode="scaleToFill" />
<image class="add-dialog__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="add-dialog__content">
<text class="dialog-title">添加家谱</text>
<text class="dialog-copy">建议先搜索已有家谱避免重复创建</text>
<view class="dialog-primary" @click="applyToJoin">
<image class="dialog-action__skin" src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<text class="dialog-primary__copy">搜索家谱</text>
</view>
<view class="dialog-secondary" @click="joinByInvite">
<image class="dialog-action__skin" src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
<text>邀请码加入</text>
</view>
<view class="dialog-create" @click="createGenealogy">确认没有现有家谱继续创建</view>
<AppButton block label="搜索家谱" @click="applyToJoin" />
<AppButton block type="secondary" label="邀请码加入" @click="joinByInvite" />
<AppButton block type="secondary" label="确认没有现有家谱,继续创建" @click="createGenealogy" />
<view class="dialog-close" @click="closeAddDialog">取消</view>
</view>
</view>
@@ -251,7 +248,7 @@
<view v-if="switcherVisible" class="genealogy-switcher-layer" @click="closeSwitcher">
<view class="genealogy-switcher" @click.stop>
<image class="genealogy-switcher__skin" src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png" mode="scaleToFill" />
<image class="genealogy-switcher__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="genealogy-switcher__content">
<text class="dialog-title">切换当前家谱</text>
<view
@@ -277,10 +274,12 @@
</template>
<script setup>
import { computed, ref } from "vue";
import { computed, nextTick, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import AppTabbar from "@/components/AppTabbar.vue";
import AppButton from "@/components/AppButton.vue";
import GenealogyCard from "@/components/GenealogyCard.vue";
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { genealogies, notifications } from "@/data/mock.js";
@@ -291,6 +290,8 @@ const forceEmptyState = ref(false);
const addDialogVisible = ref(false);
const switcherVisible = ref(false);
const selectedGenealogyId = ref(genealogies[0]?.id || null);
const listScrollCommand = ref(0);
const currentListScrollTop = ref(0);
const readPresentationState = () => {
if (typeof location !== "undefined") {
@@ -322,6 +323,9 @@ const unreadCount = computed(
() => notifications.filter((item) => item.unread).length,
);
const hasGenealogies = computed(() => !forceEmptyState.value && list.value.length > 0);
const isListLayout = computed(
() => !isLoading.value && !hasError.value && hasGenealogies.value,
);
const createdGenealogies = computed(() =>
list.value.filter((item, index) => index === 0),
);
@@ -376,9 +380,19 @@ const openAddDialog = () => { addDialogVisible.value = true; };
const closeAddDialog = () => { addDialogVisible.value = false; };
const openSwitcher = () => { switcherVisible.value = true; };
const closeSwitcher = () => { switcherVisible.value = false; };
const selectGenealogy = (genealogy) => {
const handleListScroll = (event) => {
currentListScrollTop.value = Number(event?.detail?.scrollTop || 0);
};
const resetListScroll = async () => {
listScrollCommand.value = currentListScrollTop.value;
await nextTick();
listScrollCommand.value = 0;
currentListScrollTop.value = 0;
};
const selectGenealogy = async (genealogy) => {
selectedGenealogyId.value = genealogy.id;
closeSwitcher();
await resetListScroll();
};
const openApplication = (record) => {
const statusQuery = record.id === "pending" ? "pending" : record.id;
@@ -408,37 +422,40 @@ const openShortcut = (key) => {
background: #f9f6ef;
}
.page-paper-texture {
position: absolute;
top: calc(124rpx + var(--status-bar-height, 0px));
right: 0;
bottom: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
opacity: 0.72;
pointer-events: none;
}
.page-footer-landscape {
position: absolute;
right: 0;
bottom: 112rpx;
left: 0;
z-index: 1;
width: 100%;
height: 480rpx;
opacity: 0.5;
pointer-events: none;
}
.genealogy-content {
position: relative;
z-index: 2;
padding: 24rpx 32rpx 176rpx;
}
.genealogy-index--split {
display: flex;
height: 100vh;
min-height: 0;
flex-direction: column;
padding-bottom: calc(112rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.genealogy-index--split .genealogy-content {
display: flex;
min-height: 0;
flex: 1;
flex-direction: column;
padding-bottom: 0;
}
.genealogy-fixed-zone {
flex: 0 0 auto;
}
.genealogy-list-scroll {
width: 100%;
height: 0;
min-height: 0;
flex: 1;
}
.current-slip {
position: relative;
display: block;
@@ -531,8 +548,9 @@ const openShortcut = (key) => {
flex-wrap: nowrap;
justify-content: space-between;
margin-top: 0;
color: $ink-muted;
font-size: 25rpx;
color: #62584c;
font-size: 27rpx;
font-weight: 500;
line-height: 1.4;
}
@@ -635,10 +653,23 @@ const openShortcut = (key) => {
.application-record__main { position: relative; z-index: 1; min-width: 0; flex: 1; }
.application-record__title-row { display: flex; align-items: center; justify-content: space-between; }
.application-record__name { overflow: hidden; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 32rpx; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
.application-record__status { flex: 0 0 auto; margin-left: 16rpx; color: #9a641f; font-size: 23rpx; }
.application-record__status {
flex: 0 0 auto;
margin-left: 16rpx;
color: #7f4f16;
font-size: 27rpx;
font-weight: 600;
}
.application-record__status--rejected { color: #a7160c; }
.application-record__status--muted { color: $ink-muted; }
.application-record__copy { display: block; margin-top: 10rpx; color: $ink-muted; font-size: 23rpx; }
.application-record__status--muted { color: #62584c; }
.application-record__copy {
display: block;
margin-top: 10rpx;
color: #62584c;
font-size: 28rpx;
font-weight: 500;
line-height: 1.4;
}
.application-record__chevron { position: relative; z-index: 1; width: 32rpx; height: 32rpx; margin-left: 14rpx; }
.create-action {
@@ -862,14 +893,8 @@ const openShortcut = (key) => {
.genealogy-switcher__content { position: relative; z-index: 1; display: flex; min-height: 720rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 78rpx 58rpx 50rpx; }
.dialog-title { color: $brand-red; font-family: "STKaiti", "KaiTi", serif; font-size: 42rpx; font-weight: 700; letter-spacing: 4rpx; }
.dialog-copy { margin-top: 18rpx; color: $ink-muted; font-size: 23rpx; }
.dialog-primary,
.dialog-secondary { position: relative; display: flex; width: 100%; height: 98rpx; align-items: center; justify-content: center; overflow: hidden; font-family: "STKaiti", "KaiTi", serif; font-size: 29rpx; }
.dialog-primary { margin-top: 42rpx; color: #fff9ec; }
.dialog-secondary { margin-top: 16rpx; color: #7b4e24; }
.dialog-action__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.dialog-primary__copy,
.dialog-secondary > text { position: relative; z-index: 1; }
.dialog-create { display: flex; min-height: 76rpx; align-items: center; margin-top: 8rpx; color: $brand-red; font-size: 23rpx; }
.add-dialog__content > .app-button:first-of-type { margin-top: 34rpx; }
.add-dialog__content > .app-button + .app-button { margin-top: 10rpx; }
.dialog-close { display: flex; min-height: 72rpx; align-items: center; justify-content: center; margin-top: auto; color: $ink-muted; font-size: 24rpx; }
.switcher-item { display: flex; width: 100%; min-height: 108rpx; align-items: center; justify-content: space-between; box-sizing: border-box; padding: 18rpx 12rpx; border-bottom: 1rpx solid rgba(181, 138, 75, 0.42); }
.switcher-item:first-of-type { margin-top: 28rpx; }
+11 -45
View File
@@ -1,17 +1,7 @@
<!-- 页面编号G-03用途创建家谱与录入首代人物的同路由两步流程 -->
<template>
<view class="flow-page">
<!-- 纸纹页头和山水均由完整位图承载流程内容只叠加文字和控件 -->
<image
class="page-paper-texture"
src="/static/assets/foundation/opaque/page-paper.jpg"
mode="scaleToFill"
/>
<image
class="page-footer-landscape"
src="/static/assets/foundation/transparent/footer-mountain-bamboo.png"
mode="scaleToFill"
/>
<GenealogyPageBackground />
<view class="flow-header">
<image
@@ -81,8 +71,8 @@
<view class="flow-primary-action" hover-class="action-hover" @click="submitCreate">
<image
class="flow-primary-action__skin"
src="/static/assets/foundation/opaque/a01-primary-button.png"
mode="scaleToFill"
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/>
<text class="flow-primary-action__copy">{{ createState === 'submitting' ? '正在创建…' : '创建并录入首代' }}</text>
</view>
@@ -118,8 +108,8 @@
<view class="flow-primary-action" hover-class="action-hover" @click="submitAncestor">
<image
class="flow-primary-action__skin"
src="/static/assets/foundation/opaque/a01-primary-button.png"
mode="scaleToFill"
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/>
<text class="flow-primary-action__copy">{{ ancestorState === 'submitting' ? '正在保存…' : '保存并进入家谱' }}</text>
</view>
@@ -129,16 +119,16 @@
<view v-if="duplicateReminderVisible" class="duplicate-reminder-layer" @click="closeDuplicateReminder">
<view class="duplicate-reminder" @click.stop>
<image class="duplicate-reminder__skin" src="/static/assets/modules/genealogy/opaque/g03-create-flow-panel.png" mode="scaleToFill" />
<image class="duplicate-reminder__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="duplicate-reminder__content">
<text class="duplicate-reminder__title">先确认是否已有家谱</text>
<text class="duplicate-reminder__copy">同一家族可能已经建谱建议先搜索谱名地区和堂号避免重复创建</text>
<view class="duplicate-reminder__search" @click="searchExistingGenealogy">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="aspectFit" />
<text>先搜索已有家谱</text>
</view>
<view class="duplicate-reminder__confirm" @click="confirmCreate">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>确认没有继续创建</text>
</view>
<view class="duplicate-reminder__cancel" @click="closeDuplicateReminder">返回修改</view>
@@ -148,12 +138,12 @@
<view v-if="ancestorState === 'success'" class="flow-success-layer">
<view class="flow-success-dialog">
<image class="flow-success-dialog__skin" src="/static/assets/modules/genealogy/opaque/g03-create-flow-panel.png" mode="scaleToFill" />
<image class="flow-success-dialog__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="flow-success-dialog__content">
<text class="flow-success-dialog__title">家谱创建完成</text>
<text class="flow-success-dialog__copy">首代人物已保存接下来进入家谱总览继续完善资料</text>
<view class="flow-success-dialog__action" @click="enterOverview">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>进入家谱总览</text>
</view>
</view>
@@ -165,6 +155,7 @@
<script setup>
import { computed, onMounted, onUnmounted, reactive, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
const currentStep = ref("create");
const genealogyId = ref("");
@@ -305,31 +296,6 @@ const enterOverview = () => uni.redirectTo({ url: `/pages/genealogy/g05-genealog
background: #f9f6ef;
}
.page-paper-texture {
position: absolute;
top: 112rpx;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
opacity: 0.74;
pointer-events: none;
}
.page-footer-landscape {
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
width: 100%;
height: 430rpx;
opacity: 0.42;
pointer-events: none;
}
.flow-header {
position: relative;
z-index: 3;
+4 -6
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-05用途家谱总览与加载空数据失败状态 -->
<template>
<view class="overview-page">
<image class="overview-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="overview-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="overview-page__header">
<PageHeader :title="viewMode === 'public' ? '家谱公开预览' : '家谱总览'" :action="overviewState === 'ready' && viewMode === 'member' && accessRole === 'owner' ? '管理' : ''" @action="toSettings" />
</view>
@@ -66,7 +65,7 @@
<text>{{ genealogy.publicDescription }}</text>
</view>
<view class="overview-public__action" @click="applyToJoin">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>申请加入这部家谱</text>
</view>
</view>
@@ -80,7 +79,7 @@
<text class="overview-state-panel__title">{{ stateTitle }}</text>
<text class="overview-state-panel__copy">{{ stateCopy }}</text>
<view v-if="overviewState !== 'loading'" class="overview-state-panel__action" @click="overviewState === 'error' ? reloadOverview() : toGenealogies()">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>{{ overviewState === 'error' ? '重新查看' : '返回我的家谱' }}</text>
</view>
</view>
@@ -91,6 +90,7 @@
<script setup>
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const genealogy = ref(null)
@@ -168,8 +168,6 @@ const applyToJoin = () => uni.navigateTo({ url: `/pages/genealogy/g08-join-appli
<style scoped lang="scss">
.overview-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.overview-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.overview-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.overview-page__header { position: relative; z-index: 3; }
.overview-surface { position: relative; z-index: 2; width: 100%; height: min(483px, calc(100vw * 1.3422)); }
.overview-surface__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
+2 -4
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-06用途搜索家谱邀请码加入双模式定位目标家谱 -->
<template>
<view class="search-page">
<image class="search-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="scaleToFill" />
<image class="search-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="search-page__header"><PageHeader title="加入家谱" /></view>
<view class="search-page__content">
@@ -130,6 +129,7 @@
<script setup>
import { computed, ref } from 'vue'
import { onLoad, onUnload, onShow } from '@dcloudio/uni-app'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const mode = ref('search')
@@ -220,8 +220,6 @@ const confirmInvite = () => uni.navigateTo({ url: `/pages/genealogy/g08-join-app
<style scoped lang="scss">
.search-page { position: relative; min-height: 100vh; overflow-x: 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: fixed; 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; }
+4 -6
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-08用途申请加入家谱与提交成功/失败状态 -->
<template>
<view class="join-page">
<image class="join-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="join-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="join-page__header"><PageHeader :title="sourceContract.headerTitle" /></view>
<view class="join-panel" :class="{
@@ -35,7 +34,7 @@
<text class="join-form__note">{{ sourceContract.note }}</text>
<view class="join-action" @click="submitJoin">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>{{ isSubmitting ? sourceContract.submittingLabel : sourceContract.submitLabel }}</text>
</view>
</view>
@@ -45,7 +44,7 @@
<text class="join-result__title">{{ joinState === 'success' ? sourceContract.successTitle : sourceContract.errorTitle }}</text>
<text class="join-result__copy">{{ resultCopy }}</text>
<view class="join-action" @click="joinState === 'success' ? completeFlow() : retryForm()">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>{{ joinState === 'success' ? sourceContract.nextLabel : '重新填写' }}</text>
</view>
</view>
@@ -56,6 +55,7 @@
<script setup>
import { computed, reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const genealogyId = ref('')
@@ -143,8 +143,6 @@ const completeFlow = () => source.value === 'invite' ? toMyGenealogies() : toMyA
<style scoped lang="scss">
.join-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.join-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.join-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.join-page__header { position: relative; z-index: 3; }
.join-panel { position: relative; z-index: 2; width: calc(100% - 32rpx); height: min(590px, calc((100vw - 16px) * 1.337)); margin: 22rpx auto 0; }
.join-panel__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
+15 -27
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-09用途我的家谱申请列表与空/失败状态 -->
<template>
<view class="application-page">
<image class="application-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="application-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="application-page__header"><PageHeader title="我的申请" /></view>
<view class="application-content" :class="{
@@ -35,30 +34,30 @@
</view>
</view>
<view v-if="applicationState !== 'list'" class="application-page__action" @click="applicationState === 'error' ? loadApplications({}) : toSearch()">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>{{ applicationState === 'error' ? '重新查看' : '查找公开家谱' }}</text>
</view>
</view>
<view v-if="withdrawTarget" class="withdraw-layer" @click="cancelWithdraw">
<view class="withdraw-dialog" @click.stop>
<image class="withdraw-dialog__skin" src="/static/assets/modules/genealogy/opaque/application-status-card.png" mode="scaleToFill" />
<view class="withdraw-dialog__content">
<text class="withdraw-dialog__title">撤回加入申请</text>
<text class="withdraw-dialog__copy">确认撤回对{{ withdrawTarget.genealogyName }}的申请撤回后如需加入可重新提交</text>
<view class="withdraw-dialog__actions">
<view class="withdraw-action" @click="cancelWithdraw">暂不撤回</view>
<view class="withdraw-action withdraw-action--danger" @click="confirmWithdraw">确认撤回</view>
</view>
</view>
</view>
</view>
<AppDialog
:visible="!!withdrawTarget"
title="撤回加入申请"
:message="withdrawTarget ? `确认撤回对“${withdrawTarget.genealogyName}”的申请?撤回后如需加入,可重新提交。` : ''"
confirm-text="确认撤回"
cancel-text="暂不撤回"
show-cancel
@confirm="confirmWithdraw"
@cancel="cancelWithdraw"
@close="cancelWithdraw"
/>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AppDialog from '@/components/AppDialog.vue'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const applications = ref([])
@@ -116,8 +115,6 @@ const toSearch = () => uni.navigateTo({ url: '/pages/genealogy/g06-search-geneal
<style scoped lang="scss">
.application-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.application-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.application-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.application-page__header { position: relative; z-index: 3; }
.application-content { position: relative; z-index: 2; padding: 28rpx 24rpx 60rpx; }
.application-intro { display: flex; align-items: baseline; justify-content: space-between; margin: 0 8rpx 20rpx; }
@@ -142,13 +139,4 @@ const toSearch = () => uni.navigateTo({ url: '/pages/genealogy/g06-search-geneal
.application-page__action { position: relative; width: 420rpx; height: 82rpx; margin: 34rpx auto 0; }
.application-page__action image { position: absolute; inset: 0; width: 100%; height: 100%; }
.application-page__action text { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; height: 100%; color: #fff9ed; font-size: 26rpx; font-weight: 700; }
.withdraw-layer { position: fixed; z-index: 30; inset: 0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 42rpx; background: rgba(34, 20, 12, .58); }
.withdraw-dialog { position: relative; width: 100%; max-width: 660rpx; min-height: 390rpx; }
.withdraw-dialog__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.withdraw-dialog__content { position: relative; z-index: 1; display: flex; min-height: 390rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 74rpx 60rpx 46rpx; text-align: center; }
.withdraw-dialog__title { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 36rpx; font-weight: 700; }
.withdraw-dialog__copy { margin-top: 24rpx; color: $ink-muted; font-size: 23rpx; line-height: 38rpx; }
.withdraw-dialog__actions { display: flex; width: 100%; margin-top: auto; border-top: 1rpx solid rgba(181, 138, 75, .42); }
.withdraw-action { display: flex; flex: 1; min-height: 76rpx; align-items: center; justify-content: center; color: $ink-muted; font-size: 23rpx; }
.withdraw-action--danger { border-left: 1rpx solid rgba(181, 138, 75, .42); color: $brand-red; }
</style>
+17 -38
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-10用途管理员入谱申请审核与空/失败状态 -->
<template>
<view class="review-page">
<image class="review-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="review-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="review-page__header"><PageHeader title="入谱审核" action="说明" @action="showHelp" /></view>
<view class="review-content" :class="{
@@ -27,10 +26,10 @@
<text class="application-card__relation">{{ item.relation }}</text>
<view v-if="item.status === 'PENDING'" class="review-actions">
<view class="review-action" @click="confirmAudit(item, false)">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" /><text>拒绝</text>
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="aspectFit" /><text>拒绝</text>
</view>
<view class="review-action" @click="confirmAudit(item, true)">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" /><text>通过</text>
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" /><text>通过</text>
</view>
</view>
<text v-else class="application-card__status">{{ item.status === 'APPROVED' ? '已通过' : '已拒绝' }}</text>
@@ -46,33 +45,23 @@
</view>
</view>
<view v-if="reviewState === 'error'" class="review-page__retry" @click="loadApplications({ genealogyId })">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" /><text>重新查看</text>
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" /><text>重新查看</text>
</view>
</view>
<view v-if="confirmation || helpVisible" class="review-dialog-layer" @click="closeDialog">
<view class="review-dialog" @click.stop>
<image class="review-dialog__skin" src="/static/assets/modules/genealogy/opaque/application-status-card.png" mode="scaleToFill" />
<view class="review-dialog__content">
<template v-if="helpVisible">
<text class="review-dialog__title">审核说明</text>
<text class="review-dialog__copy">请核对申请人的姓名亲属关系和补充说明仅确认与本家谱存在真实关系的申请</text>
<view class="review-dialog__single" @click="closeDialog">我知道了</view>
</template>
<template v-else>
<text class="review-dialog__title">{{ confirmation.approved ? '确认通过申请?' : '确认拒绝申请?' }}</text>
<text class="review-dialog__copy">{{ confirmation.approved ? '通过后,申请人将成为本家谱成员。' : '拒绝后,申请人会收到审核结果,并可修改后重新申请。' }}</text>
<view class="review-dialog__actions">
<view class="review-dialog__action" @click="closeDialog">取消</view>
<view class="review-dialog__action review-dialog__action--primary" @click="applyAudit">{{ confirmation.approved ? '确认通过' : '确认拒绝' }}</view>
</view>
</template>
</view>
</view>
</view>
<AppDialog
:visible="!!confirmation || helpVisible"
:title="helpVisible ? '审核说明' : confirmation?.approved ? '确认通过申请?' : '确认拒绝申请?'"
:message="helpVisible ? '请核对申请人的姓名、亲属关系和补充说明,仅确认与本家谱存在真实关系的申请。' : confirmation?.approved ? '通过后,申请人将成为本家谱成员。' : '拒绝后,申请人会收到审核结果,并可修改后重新申请。'"
:confirm-text="helpVisible ? '我知道了' : confirmation?.approved ? '确认通过' : '确认拒绝'"
:show-cancel="!!confirmation"
@confirm="helpVisible ? closeDialog() : applyAudit()"
@cancel="closeDialog"
@close="closeDialog"
/>
<view v-if="feedbackVisible" class="review-feedback">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-toast-v3.png" mode="scaleToFill" />
<text>{{ feedbackMessage }}</text>
</view>
</view>
@@ -81,6 +70,8 @@
<script setup>
import { computed, ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import AppDialog from '@/components/AppDialog.vue'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
import { genealogyContext } from '@/utils/genealogy-context.js'
@@ -141,8 +132,6 @@ const showHelp = () => { helpVisible.value = true }
<style scoped lang="scss">
.review-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.review-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.review-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.review-page__header { position: relative; z-index: 3; }
.review-content { position: relative; z-index: 2; padding: 24rpx 24rpx 60rpx; }
.review-intro { position: relative; margin: 0 8rpx 24rpx; padding-bottom: 22rpx; }
@@ -171,16 +160,6 @@ const showHelp = () => { helpVisible.value = true }
.review-page__retry { position: relative; width: 420rpx; height: 82rpx; margin: 32rpx auto 0; }
.review-page__retry image { position: absolute; inset: 0; width: 100%; height: 100%; }
.review-page__retry text { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; height: 100%; color: #fff9ed; font-size: 26rpx; font-weight: 700; }
.review-dialog-layer { position: fixed; z-index: 30; inset: 0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 42rpx; background: rgba(34, 20, 12, .58); }
.review-dialog { position: relative; width: 100%; max-width: 660rpx; min-height: 410rpx; }
.review-dialog__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.review-dialog__content { position: relative; z-index: 1; display: flex; min-height: 410rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 72rpx 60rpx 42rpx; text-align: center; }
.review-dialog__title { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 36rpx; font-weight: 700; }
.review-dialog__copy { margin-top: 24rpx; color: $ink-muted; font-size: 23rpx; line-height: 38rpx; }
.review-dialog__actions { display: flex; width: 100%; margin-top: auto; border-top: 1rpx solid rgba(181, 138, 75, .42); }
.review-dialog__action { display: flex; flex: 1; min-height: 74rpx; align-items: center; justify-content: center; color: $ink-muted; font-size: 23rpx; }
.review-dialog__action--primary { border-left: 1rpx solid rgba(181, 138, 75, .42); color: $brand-red; font-weight: 700; }
.review-dialog__single { display: flex; min-height: 76rpx; align-items: center; margin-top: auto; color: $brand-red; font-size: 24rpx; font-weight: 700; }
.review-feedback { position: fixed; z-index: 40; top: 140rpx; left: 50%; display: flex; min-width: 250rpx; min-height: 76rpx; align-items: center; justify-content: center; padding: 0 36rpx; transform: translateX(-50%); }
.review-feedback image { position: absolute; inset: 0; width: 100%; height: 100%; }
.review-feedback text { position: relative; z-index: 1; color: $ink; font-size: 23rpx; }
+6 -8
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-11用途家谱设置公开范围与访问说明页面设计阶段使用本地模拟交互 -->
<template>
<view class="settings-page">
<image class="settings-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="settings-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="settings-page__header"><PageHeader title="家谱设置" /></view>
<view class="settings-panel" :class="{
@@ -29,7 +28,7 @@
<text class="visibility-block__label">公开范围</text>
<view class="visibility-options">
<view v-for="option in visibilityOptions" :key="option.value" class="visibility-option" @click="genealogyDraft.visibility = option.value">
<image :src="genealogyDraft.visibility === option.value ? '/static/assets/foundation/opaque/a01-primary-button.png' : '/static/assets/foundation/opaque/a01-secondary-button.png'" mode="scaleToFill" />
<image :src="genealogyDraft.visibility === option.value ? '/static/assets/foundation/transparent/a01-scroll-primary-v3.png' : '/static/assets/foundation/transparent/a01-scroll-secondary-v3.png'" mode="aspectFit" />
<text :class="{ 'visibility-option__text--active': genealogyDraft.visibility === option.value }">{{ option.label }}</text>
</view>
</view>
@@ -43,7 +42,7 @@
</view>
<view class="settings-action" @click="saveSettings">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>保存设置</text>
</view>
</view>
@@ -53,13 +52,13 @@
<text class="settings-result__title">{{ settingsState === 'success' ? '新的访问规则已经生效' : settingsState === 'no-permission' ? '当前账号不能修改家谱' : '暂时无法打开家谱设置' }}</text>
<text class="settings-result__copy">{{ settingsState === 'success' ? `${genealogyDraft.name} · ${visibilityLabel}` : settingsState === 'no-permission' ? '只有家谱所有者可以修改名称、公开范围和访问说明。' : '请从家谱总览重新进入,当前修改不会被保留。' }}</text>
<view class="settings-action" @click="settingsState = 'form'">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<text>{{ settingsState === 'success' ? '继续调整' : '重新查看' }}</text>
</view>
</view>
</view>
<view v-if="feedbackVisible" class="settings-feedback">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="aspectFit" />
<text>设置已保存</text>
</view>
</view>
@@ -68,6 +67,7 @@
<script setup>
import { computed, reactive, ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const genealogyId = ref('')
@@ -108,8 +108,6 @@ const saveSettings = () => {
<style scoped lang="scss">
.settings-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.settings-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.settings-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.settings-page__header { position: relative; z-index: 3; }
.settings-panel { position: relative; z-index: 2; width: calc(100% - 32rpx); height: min(620px, calc((100vw - 16px) * 1.43)); margin: 18rpx auto 0; }
.settings-panel__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
+8 -10
View File
@@ -1,8 +1,7 @@
<!-- 页面编号G-12用途字辈诗列表空状态与批量维护页面设计阶段使用本地模拟交互 -->
<template>
<view class="poem-page">
<image class="poem-page__paper" src="/static/assets/foundation/opaque/page-paper.jpg" mode="aspectFill" />
<image class="poem-page__footer" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png" mode="widthFix" />
<GenealogyPageBackground />
<view class="poem-page__header"><PageHeader title="字辈诗" :action="poemState === 'list' ? '维护' : ''" @action="openEditor" /></view>
<view class="poem-panel" :class="{
@@ -27,7 +26,7 @@
</view>
</view>
<view class="poem-action" @click="openEditor">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" /><text>维护字辈诗</text>
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" /><text>维护字辈诗</text>
</view>
</view>
@@ -44,17 +43,17 @@
<view class="poem-policy">
<text>缺失旧世代时</text>
<view class="poem-policy__option" @click="stopMissingOldGeneration = !stopMissingOldGeneration">
<image :src="stopMissingOldGeneration ? '/static/assets/foundation/opaque/a01-primary-button.png' : '/static/assets/foundation/opaque/a01-secondary-button.png'" mode="scaleToFill" />
<image :src="stopMissingOldGeneration ? '/static/assets/foundation/transparent/a01-scroll-primary-v3.png' : '/static/assets/foundation/transparent/a01-scroll-secondary-v3.png'" mode="aspectFit" />
<text :class="{ 'poem-policy__option-text--active': stopMissingOldGeneration }">{{ stopMissingOldGeneration ? '停止并提醒' : '继续补录' }}</text>
</view>
</view>
<text class="poem-preview">预览{{ previewCharacters || '尚未录入字辈' }}</text>
<view class="poem-editor__actions">
<view class="poem-action" @click="poemState = 'list'">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" /><text class="poem-action__secondary">取消</text>
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="aspectFit" /><text class="poem-action__secondary">取消</text>
</view>
<view class="poem-action" @click="savePoems">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" /><text>保存字辈</text>
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" /><text>保存字辈</text>
</view>
</view>
</view>
@@ -64,12 +63,12 @@
<text class="poem-list__title">{{ poemState === 'empty' ? '从第一段传承字序开始' : poemState === 'no-permission' ? '当前账号不能维护字辈' : '暂时无法读取字辈诗' }}</text>
<text class="poem-list__copy">{{ poemState === 'empty' ? '管理员可以一次录入连续字辈,系统会按世代拆分展示。' : poemState === 'no-permission' ? '普通成员可以查看字辈,但只有具备管理权限的成员可以维护。' : '请从家谱总览重新进入,或稍后再试。' }}</text>
<view class="poem-action" @click="poemState === 'empty' ? openEditor() : poemState = 'list'">
<image src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" /><text>{{ poemState === 'empty' ? '开始录入' : '重新查看' }}</text>
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" /><text>{{ poemState === 'empty' ? '开始录入' : '重新查看' }}</text>
</view>
</view>
</view>
<view v-if="feedbackVisible" class="poem-feedback">
<image src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="aspectFit" />
<text>字辈预览已更新</text>
</view>
</view>
@@ -78,6 +77,7 @@
<script setup>
import { computed, ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import GenealogyPageBackground from '@/components/GenealogyPageBackground.vue'
import PageHeader from '@/components/PageHeader.vue'
const genealogyId = ref('')
@@ -121,8 +121,6 @@ const savePoems = () => {
<style scoped lang="scss">
.poem-page { position: relative; min-height: 100vh; overflow-x: hidden; background: $paper; }
.poem-page__paper { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: .98; pointer-events: none; }
.poem-page__footer { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; width: 100%; opacity: .13; pointer-events: none; }
.poem-page__header { position: relative; z-index: 3; }
.poem-panel { position: relative; z-index: 2; width: calc(100% - 32rpx); height: min(650px, calc((100vw - 16px) * 1.5)); margin: 18rpx auto 0; }
.poem-panel__skin { position: absolute; inset: 0; width: 100%; height: 100%; }