Files
jiapuapp/pages/genealogy/g12-generation-poems.vue
T
2026-07-20 06:52:26 +08:00

513 lines
13 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-12用途字辈诗列表空状态与批量维护页面设计阶段使用本地模拟交互)。 -->
<template>
<view class="poem-page">
<GenealogyPageBackground />
<view class="poem-page__header"
><PageHeader
title="字辈诗"
:action="poemState === 'list' ? '维护' : ''"
@action="openEditor"
/></view>
<view
class="poem-panel"
:class="{
'poem-state--list': poemState === 'list',
'poem-state--empty': poemState === 'empty',
'poem-state--edit': poemState === 'edit',
'poem-state--error': poemState === 'error',
'poem-state--no-permission': poemState === 'no-permission',
}"
>
<image
class="poem-panel__skin"
src="/static/assets/modules/genealogy/transparent/g01-empty-panel-frame.png"
mode="scaleToFill"
/>
<AppLoading
v-if="poemState === 'loading'"
text="正在整理字辈诗"
description="请稍候,正在读取家谱字序。"
/>
<view v-else-if="poemState === 'list'" class="poem-list">
<text class="poem-list__eyebrow">汤氏家谱 · 传承字序</text>
<text class="poem-list__title">启宗敦本继世传芳</text>
<text class="poem-list__copy"
>按世代查看字辈当前家谱使用到字辈</text
>
<view class="poem-rows">
<view
v-for="item in poemRows"
:key="item.generationNo"
class="poem-row"
:class="{ 'poem-row--current': item.current }"
>
<image
src="/static/assets/modules/genealogy/transparent/g-form-field-frame.png"
mode="scaleToFill"
/>
<text class="poem-row__number"> {{ item.generationNo }} </text>
<text class="poem-row__character">{{ item.character }}</text>
<text class="poem-row__status">{{
item.current ? "当前字辈" : "传承字序"
}}</text>
</view>
</view>
<view class="poem-action" @click="openEditor">
<image
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/><text>维护字辈诗</text>
</view>
</view>
<view v-else-if="poemState === 'edit'" class="poem-editor">
<text class="poem-list__eyebrow">批量维护</text>
<text class="poem-list__title">录入连续字辈</text>
<text class="poem-list__copy"
>按照接口支持的连续文本录入每个汉字对应一代保存前可预览新字序</text
>
<view class="poem-field">
<image
src="/static/assets/modules/genealogy/transparent/g-form-field-frame.png"
mode="scaleToFill"
/>
<text>字辈内容</text>
<textarea
v-model="poemDraft"
maxlength="24"
placeholder="例如:启宗敦本继世传芳"
placeholder-class="poem-placeholder"
@input="poemError = ''"
/>
</view>
<text v-if="poemError" class="poem-field-error">{{ poemError }}</text>
<view class="poem-policy">
<text>缺失旧世代时</text>
<view
class="poem-policy__option"
@click="stopMissingOldGeneration = !stopMissingOldGeneration"
>
<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/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/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/><text>保存字辈</text>
</view>
</view>
</view>
<view v-else class="poem-state-card">
<text class="poem-list__eyebrow">{{
poemState === "empty"
? "尚未建立字辈"
: poemState === "no-permission"
? "权限不足"
: "字辈暂不可用"
}}</text>
<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/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/transparent/a01-scroll-secondary-v3.png"
mode="aspectFit"
/>
<text>字辈预览已更新</text>
</view>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad, onUnload } from "@dcloudio/uni-app";
import AppLoading from "@/components/AppLoading.vue";
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const genealogyId = ref("");
const poemState = ref("loading");
const poemError = ref("");
const feedbackVisible = ref(false);
let feedbackTimer = null;
const poemDraft = ref("启宗敦本继世传芳");
const stopMissingOldGeneration = ref(true);
const poemRows = ref([
{ generationNo: 12, character: "启", current: false },
{ generationNo: 13, character: "宗", current: false },
{ generationNo: 14, character: "敦", current: true },
{ generationNo: 15, character: "本", current: false },
]);
const previewCharacters = computed(() =>
poemDraft.value.trim().split("").join(" · "),
);
onLoad((query) => {
genealogyId.value = query.genealogyId || "";
poemState.value =
query.state === "loading"
? "loading"
: query.state === "empty"
? "empty"
: query.state === "edit"
? "edit"
: query.state === "no-permission"
? "no-permission"
: query.state === "error" || !genealogyId.value
? "error"
: "list";
});
onUnload(() => {
if (feedbackTimer) clearTimeout(feedbackTimer);
});
const openEditor = () => {
poemState.value = "edit";
};
const savePoems = () => {
const characters = poemDraft.value.trim().split("").filter(Boolean);
if (!characters.length) {
poemError.value = "请录入字辈内容";
return;
}
if (poemDraft.value.trim() === "失败") {
poemError.value = "字辈保存失败,请稍后重试";
return;
}
poemRows.value = characters
.slice(0, 4)
.map((character, index) => ({
generationNo: 12 + index,
character,
current: index === 2,
}));
poemState.value = "list";
feedbackVisible.value = true;
feedbackTimer = setTimeout(() => {
feedbackVisible.value = false;
feedbackTimer = null;
}, 1800);
};
</script>
<style scoped lang="scss">
.poem-page {
position: relative;
min-height: 100vh;
overflow-x: hidden;
background: $paper;
}
.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%;
}
.poem-list,
.poem-editor,
.poem-state-card {
position: absolute;
inset: 7.5% 8%;
}
.poem-list__eyebrow {
display: block;
color: $brand-red;
font-size: 23rpx;
letter-spacing: 3rpx;
}
.poem-list__title {
display: block;
margin-top: 11rpx;
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 35rpx;
font-weight: 700;
}
.poem-list__copy {
display: block;
margin-top: 10rpx;
color: $ink-muted;
font-size: 24rpx;
line-height: 1.55;
}
.poem-rows {
margin-top: 18rpx;
}
.poem-row {
position: relative;
height: 72rpx;
margin-top: 10rpx;
}
.poem-row image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.poem-row__number {
position: absolute;
top: 24rpx;
left: 24rpx;
z-index: 1;
color: $ink-muted;
font-size: 23rpx;
}
.poem-row__character {
position: absolute;
top: 14rpx;
left: 48%;
z-index: 1;
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 32rpx;
font-weight: 700;
}
.poem-row__status {
position: absolute;
top: 25rpx;
right: 22rpx;
z-index: 1;
color: $ink-muted;
font-size: 22rpx;
}
.poem-row--current .poem-row__character,
.poem-row--current .poem-row__status {
color: $brand-red;
}
.poem-action {
position: relative;
width: 100%;
height: 76rpx;
margin-top: 20rpx;
}
.poem-action image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.poem-action text {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #fff9ed;
font-size: 25rpx;
font-weight: 700;
letter-spacing: 2rpx;
}
.poem-field {
position: relative;
height: 118rpx;
margin-top: 22rpx;
}
.poem-field image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.poem-field > text {
position: absolute;
top: 47rpx;
left: 24rpx;
z-index: 1;
color: $ink;
font-size: 22rpx;
font-weight: 700;
}
.poem-field textarea {
position: absolute;
top: 0;
right: 20rpx;
bottom: 0;
left: 164rpx;
z-index: 1;
box-sizing: border-box;
height: 118rpx;
padding-top: 34rpx;
color: $ink;
font-size: 22rpx;
line-height: 1.5;
}
.poem-placeholder {
color: #a79884;
}
.poem-field-error {
display: block;
margin-top: 4rpx;
color: $brand-red;
font-size: 24rpx;
line-height: 34rpx;
text-align: right;
}
.poem-policy {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 22rpx;
}
.poem-policy > text {
color: $ink;
font-size: 24rpx;
font-weight: 700;
}
.poem-policy__option {
position: relative;
width: 248rpx;
height: 62rpx;
}
.poem-policy__option image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.poem-policy__option text {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: $ink;
font-size: 23rpx;
font-weight: 700;
}
.poem-policy__option .poem-policy__option-text--active {
color: #fff9ed;
}
.poem-preview {
display: block;
margin-top: 22rpx;
color: $ink-muted;
font-size: 23rpx;
line-height: 1.6;
}
.poem-editor__actions {
display: flex;
gap: 14rpx;
margin-top: 18rpx;
}
.poem-editor__actions .poem-action {
width: calc(50% - 7rpx);
margin-top: 0;
}
.poem-action .poem-action__secondary {
color: $ink;
}
.poem-state-card {
top: 29%;
text-align: center;
}
.poem-state-card .poem-list__eyebrow {
text-align: center;
}
.poem-state-card .poem-list__copy {
margin-top: 22rpx;
}
.poem-state-card .poem-action {
width: 420rpx;
max-width: 100%;
margin: 34rpx auto 0;
}
.poem-feedback {
position: fixed;
z-index: 30;
top: 138rpx;
left: 50%;
display: flex;
min-width: 260rpx;
min-height: 74rpx;
align-items: center;
justify-content: center;
padding: 0 34rpx;
transform: translateX(-50%);
}
.poem-feedback image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.poem-feedback text {
position: relative;
z-index: 1;
color: $ink;
font-size: 22rpx;
}
@media (min-width: 400px) {
.poem-panel {
width: calc(100% - 48rpx);
}
.poem-list,
.poem-editor,
.poem-state-card {
right: 9%;
left: 9%;
}
}
</style>