修改完成

This commit is contained in:
2026-07-21 07:53:03 +08:00
parent 01d246c47b
commit ff60119277
172 changed files with 7982 additions and 2999 deletions
+27 -57
View File
@@ -22,11 +22,6 @@
><text>汤氏家族圈</text><text>家宴通知与共同记忆</text></view
>
<view v-if="feedState !== 'loading'" class="feed-shortcuts">
<image
class="feed-shortcuts__skin"
src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png"
mode="scaleToFill"
/>
<view
v-for="item in shortcuts"
:key="item.key"
@@ -42,11 +37,6 @@
class="feed-card"
@click="openDetail(item)"
>
<image
class="feed-card__skin"
src="/static/assets/modules/family/transparent/f01-family-letter-card.png"
mode="scaleToFill"
/>
<view class="feed-card__copy">
<text class="feed-card__meta"
>{{ item.tag }} · {{ item.time }}</text
@@ -58,11 +48,7 @@
</view>
</template>
<view v-else-if="feedState !== 'loading'" class="feed-state-card">
<image
src="/static/assets/modules/family/transparent/f01-family-letter-card.png"
mode="scaleToFill"
/>
<view
<view class="feed-state-card__copy"
><text>{{
feedState === "empty" ? "还没有家族动态" : "家族动态暂不可用"
}}</text
@@ -77,10 +63,7 @@
v-if="feedState !== 'loading'"
class="feed-action"
@click="feedState === 'error' ? (feedState = 'list') : toPublish()"
><image
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/><text>{{
><text>{{
feedState === "error" ? "重新查看" : "发布家族动态"
}}</text></view
>
@@ -121,6 +104,10 @@ const shortcuts = [
{ key: "albums", label: "相册" },
{ key: "rituals", label: "礼仪" },
{ key: "memos", label: "备忘" },
{ key: "people", label: "人物录" },
{ key: "gifts", label: "贺礼簿" },
{ key: "merits", label: "功德录" },
{ key: "videos", label: "家族视频" },
];
onLoad((query) => {
genealogyId.value =
@@ -146,23 +133,29 @@ const openSection = (key) => {
albums: "/pages/family/f07-album-list",
rituals: "/pages/records/r05-ritual-list",
memos: "/pages/records/r10-memo-list",
people: "/pages/records/r01-people-list",
gifts: "/pages/records/r03-gift-list",
merits: "/pages/records/r11-merit-records",
videos: "/pages/family/f10-video-list",
};
uni.navigateTo({ url: routes[key] });
uni.navigateTo({
url: `${routes[key]}?genealogyId=${genealogyId.value}`,
});
};
</script>
<style scoped lang="scss">
.family-page {
position: relative;
display: flex;
min-height: 100vh;
overflow: hidden;
flex-direction: column;
background: $paper;
}
.family-page__header,
.feed-content {
position: relative;
z-index: 2;
z-index: 1;
}
.feed-content {
flex: 1;
padding: 24rpx 24rpx 190rpx;
}
.feed-heading text {
@@ -180,23 +173,14 @@ const openSection = (key) => {
font-size: 23rpx;
}
.feed-shortcuts {
position: relative;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
width: 100%;
height: 48px;
min-height: 48px;
margin-top: 15rpx;
}
.feed-shortcuts__skin {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: url("/static/assets/foundation/transparent/a01-scroll-secondary-v3.png") center / 100% 100% no-repeat;
}
.feed-shortcut {
position: relative;
z-index: 1;
width: 100%;
height: 48px;
min-height: 44px;
@@ -212,28 +196,17 @@ const openSection = (key) => {
}
.feed-card,
.feed-state-card {
position: relative;
display: flex;
width: 100%;
height: 230rpx;
min-height: 230rpx;
min-height: 98px;
flex-direction: column;
justify-content: center;
margin-top: 17rpx;
box-sizing: border-box;
}
.feed-card__skin,
.feed-state-card > image,
.feed-action image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: url("/static/assets/modules/family/transparent/f01-family-letter-card.png") center / 100% 100% no-repeat;
}
.feed-card__copy {
position: relative;
z-index: 1;
padding: 15% 9%;
}
.feed-card text {
@@ -265,9 +238,7 @@ const openSection = (key) => {
.feed-state-card {
margin-top: 70rpx;
}
.feed-state-card > view {
position: relative;
z-index: 1;
.feed-state-card__copy {
padding: 23% 10%;
box-sizing: border-box;
text-align: center;
@@ -288,14 +259,13 @@ const openSection = (key) => {
line-height: 1.45;
}
.feed-action {
position: relative;
width: 100%;
height: 76rpx;
margin-top: 19rpx;
width: 514rpx;
max-width: 100%;
min-height: 76rpx;
margin: 19rpx auto 0;
background: url("/static/assets/foundation/transparent/a01-scroll-primary-v3.png") center / 100% 100% no-repeat;
}
.feed-action text {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
+8 -35
View File
@@ -10,19 +10,13 @@
><ModulePageBackground module="family" /><view class="publish-page__header"
><PageHeader title="发布动态" /></view
><view class="publish-panel"
><image
class="publish-panel__skin"
src="/static/assets/modules/family/transparent/module-content-frame.png"
mode="scaleToFill" /><view
><view
v-if="publishState === 'form'"
class="publish-form"
><text>记录此刻</text
><text>分享通知活动家族故事或一段共同记忆</text
><view class="publish-field"
><image
src="/static/assets/modules/family/transparent/module-field-frame.png"
mode="scaleToFill"
/><textarea
><textarea
v-model="content"
maxlength="300"
placeholder="写下想对家人说的话"
@@ -87,15 +81,14 @@ onUnmounted(() => {
</script>
<style scoped lang="scss">
.publish-page {
position: relative;
display: flex;
min-height: 100vh;
overflow: hidden;
flex-direction: column;
background: $paper;
}
.publish-page__header,
.publish-panel {
position: relative;
z-index: 2;
z-index: 1;
}
.publish-panel {
width: calc(100% - 32rpx);
@@ -103,18 +96,7 @@ onUnmounted(() => {
margin: 18rpx auto 0;
padding: 9%;
box-sizing: border-box;
}
.publish-panel__skin {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.publish-form,
.publish-result {
position: relative;
z-index: 1;
background: url("/static/assets/modules/family/transparent/module-content-frame.png") center / 100% 100% no-repeat;
}
.publish-form > text,
.publish-result > text {
@@ -135,25 +117,16 @@ onUnmounted(() => {
line-height: 1.6;
}
.publish-field {
position: relative;
display: flex;
min-height: 250rpx;
margin-top: 24rpx;
padding: 25rpx;
box-sizing: border-box;
}
.publish-field image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat;
}
.publish-field textarea {
position: relative;
z-index: 1;
width: 100%;
height: 200rpx;
min-height: 200rpx;
color: $ink;
font-size: 24rpx;
line-height: 1.7;
+151 -3
View File
@@ -1,5 +1,153 @@
<!-- 页面编号F-03用途动态详情评论点赞与删除确认 -->
<template><ModulePage page-id="f03" /></template>
<!-- 页面编号F-03用途动态详情评论与内容失效状态 -->
<template>
<view class="feed-detail-page" :class="{ 'feed-state--expired': feedState === 'expired', 'feed-state--error': feedState === 'error', 'feed-state--ready': feedState === 'ready' }">
<ModulePageBackground module="family" />
<view class="feed-detail-header"><PageHeader title="动态详情" /></view>
<view v-if="feedState === 'loading'" class="feed-detail-loading">
<AppLoading text="正在读取家族动态" description="请稍候,正在整理正文与家人评论。" />
</view>
<view v-else class="feed-detail-content">
<template v-if="feedState === 'ready'">
<view class="feed-article-card">
<text class="feed-article-card__meta">{{ currentFeed.tag }} · {{ currentFeed.time }}</text>
<text class="feed-article-card__title">{{ currentFeed.title }}</text>
<text class="feed-article-card__body">{{ currentFeed.content }}</text>
<text class="feed-article-card__author">发布人{{ currentFeed.author }}</text>
</view>
<view class="feed-comments-panel">
<view class="feed-comments-heading">
<text>家人评论</text><text>{{ feedComments.length }} </text>
</view>
<view v-for="comment in feedComments" :key="comment.id" class="feed-comment-card">
<view><text>{{ comment.author }}</text><text>{{ comment.time }}</text></view>
<text>{{ comment.content }}</text>
</view>
<view v-if="!feedComments.length" class="feed-comments-empty">
<text>还没有评论</text><text>写下第一句祝福或共同记忆</text>
</view>
</view>
<view class="feed-comment-form" :class="{ 'comment-state--saving': commentState === 'saving', 'comment-state--error': commentState === 'error' }">
<text>写下评论</text>
<textarea v-model="commentDraft" auto-height maxlength="240" placeholder="对家人说点什么" />
<text v-if="commentError" class="feed-comment-error">{{ commentError }}</text>
<AppButton block :disabled="commentState === 'saving'" :label="commentState === 'saving' ? '正在发送' : '发送评论'" @click="submitComment" />
</view>
</template>
<view v-else class="feed-state-card">
<text>{{ feedState === 'expired' ? '动态已失效' : '动态暂不可用' }}</text>
<text>{{ feedState === 'expired' ? '这条动态可能已被发布人删除,请返回家族圈查看其他内容。' : '请稍后重新查看,已有家族记录不会受到影响。' }}</text>
<AppButton :type="feedState === 'error' ? 'secondary' : 'primary'" block :label="feedState === 'error' ? '重新查看' : '返回家族圈'" @click="feedState === 'error' ? restoreFeed() : backToFamily()" />
</view>
</view>
<AppToast :visible="toastVisible" message="评论已发送" />
</view>
</template>
<script setup>
import ModulePage from "@/components/ModulePage.vue";
import { onUnmounted, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const feedRecords = [
{ id: "1", tag: "团圆记忆", time: "今天 10:24", title: "端午家宴", content: "今年端午全家相聚,长辈讲起祖居旧事,孩子们也为大家拍下了新的全家福。饭后我们把照片和口述片段整理进家族档案,让这份热闹成为往后仍能翻看的共同记忆。", author: "汤正国" },
{ id: "2", tag: "家族通知", time: "昨天 18:02", title: "修谱资料征集", content: "请家人补充老照片中的人物姓名、拍摄时间和地点。无法确认的信息也可以先写下线索,由熟悉往事的长辈共同核对。", author: "谱主" },
];
const feedId = ref("1");
const currentFeed = ref(feedRecords[0]);
const feedState = ref("loading");
const commentState = ref("idle");
const commentDraft = ref("");
const commentError = ref("");
const toastVisible = ref(false);
const forceCommentFailure = ref(false);
const feedComments = ref([
{ id: 1, author: "汤淑华", time: "今天 10:42", content: "一家人能常常相聚,就是最珍贵的福气。" },
{ id: 2, author: "汤文清", time: "今天 11:08", content: "照片已经整理好了,晚些时候放进春节团圆相册。" },
]);
let submitTimer = null;
let toastTimer = null;
onLoad((query) => {
feedId.value = String(query.feedId || "1");
const selected = feedRecords.find((item) => item.id === feedId.value);
currentFeed.value = selected || feedRecords[0];
forceCommentFailure.value = query.commentResult === "error";
feedState.value = ["loading", "error", "expired"].includes(query.state)
? query.state
: selected
? "ready"
: "expired";
});
const submitComment = () => {
if (commentState.value === "saving") return;
const content = commentDraft.value.trim();
if (!content) {
commentError.value = "请先写下评论内容";
return;
}
commentError.value = "";
commentState.value = "saving";
submitTimer = setTimeout(() => {
if (forceCommentFailure.value) {
commentState.value = "error";
commentError.value = "评论发送失败,请保留文字后重试";
forceCommentFailure.value = false;
return;
}
feedComments.value.push({ id: Date.now(), author: "我", time: "刚刚", content });
commentDraft.value = "";
commentState.value = "idle";
toastVisible.value = true;
toastTimer = setTimeout(() => { toastVisible.value = false; }, 1800);
}, 320);
};
const restoreFeed = () => { feedState.value = "ready"; };
const backToFamily = () => uni.redirectTo({ url: "/pages/family/f01-family-feed" });
onUnmounted(() => {
if (submitTimer) clearTimeout(submitTimer);
if (toastTimer) clearTimeout(toastTimer);
});
</script>
<style scoped lang="scss">
.feed-detail-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.feed-detail-header, .feed-detail-loading, .feed-detail-content { z-index: 1; }
.feed-detail-loading { min-height: calc(100vh - 100rpx); }
.feed-detail-content { padding: 18rpx 24rpx 72rpx; }
.feed-article-card, .feed-comments-panel, .feed-comment-form, .feed-state-card { width: 100%; box-sizing: border-box; background: url("/static/assets/modules/family/transparent/module-content-frame.png") center / 100% 100% no-repeat; }
.feed-article-card { padding: 46rpx 48rpx 42rpx; }
.feed-article-card text, .feed-state-card > text { display: block; }
.feed-article-card__meta, .feed-article-card__author { color: $ink-muted; font-size: 22rpx; }
.feed-article-card__title { margin-top: 12rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 36rpx; font-weight: 700; }
.feed-article-card__body { margin-top: 18rpx; color: $ink; font-size: 25rpx; line-height: 1.75; }
.feed-article-card__author { margin-top: 22rpx; }
.feed-comments-panel { margin-top: 18rpx; padding: 38rpx 38rpx 34rpx; }
.feed-comments-heading { display: flex; align-items: center; justify-content: space-between; color: $brand-red; font-size: 24rpx; font-weight: 700; }
.feed-comments-heading text:last-child { color: $ink-muted; font-size: 21rpx; font-weight: 400; }
.feed-comment-card { margin-top: 16rpx; padding: 20rpx 22rpx; background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat; }
.feed-comment-card > view { display: flex; justify-content: space-between; gap: 18rpx; color: $ink-muted; font-size: 20rpx; }
.feed-comment-card > text { display: block; margin-top: 9rpx; color: $ink; font-size: 24rpx; line-height: 1.55; }
.feed-comments-empty { padding: 34rpx 10rpx 20rpx; text-align: center; }
.feed-comments-empty text { display: block; color: $ink-muted; font-size: 23rpx; line-height: 1.6; }
.feed-comments-empty text:first-child { color: $ink; font-size: 28rpx; font-weight: 700; }
.feed-comment-form { margin-top: 18rpx; padding: 38rpx 40rpx 42rpx; }
.feed-comment-form > text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 30rpx; font-weight: 700; }
.feed-comment-form textarea { width: auto; min-width: 0; min-height: 126rpx; margin-top: 16rpx; padding: 20rpx 22rpx; box-sizing: border-box; color: $ink; font-size: 24rpx; line-height: 1.55; background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat; }
.feed-comment-error { display: block; margin-top: 10rpx; color: $brand-red; font-size: 22rpx; }
.feed-comment-form .app-button { margin-top: 22rpx; }
.feed-state-card { min-height: 340rpx; margin-top: 36rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.feed-state-card > text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 35rpx; font-weight: 700; }
.feed-state-card > text:nth-child(2) { margin-top: 18rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
.feed-state-card .app-button { margin-top: 30rpx; }
</style>
+115 -3
View File
@@ -1,5 +1,117 @@
<!-- 页面编号F-04用途谱文分类与文章列表 -->
<template><ModulePage page-id="f04" /></template>
<!-- 页面编号F-04用途谱文分类搜索列表与新建入口 -->
<template>
<view class="article-list-page" :class="{ 'article-list-state--loading': listState === 'loading', 'article-list-state--empty': listState === 'empty', 'article-list-state--error': listState === 'error' }">
<ModulePageBackground module="family" />
<view class="article-list-header"><PageHeader title="谱文" action="新建" @action="createArticle" /></view>
<view v-if="listState === 'loading'" class="article-list-loading">
<AppLoading text="正在整理家族谱文" description="请稍候,正在读取家训、往事与序言。" />
</view>
<view v-else class="article-list-content">
<template v-if="listState === 'ready'">
<view class="article-search">
<input v-model="keyword" placeholder="搜索标题、作者或正文" confirm-type="search" />
</view>
<view class="article-categories">
<view class="article-categories__row">
<view v-for="category in articleCategories" :key="category" class="article-category" :class="{ 'article-category--active': activeCategory === category }" @click="activeCategory = category"><text>{{ category }}</text></view>
</view>
</view>
<view v-if="filteredArticles.length" class="article-list">
<view v-for="article in filteredArticles" :key="article.id" class="article-card" role="button" :aria-label="`查看谱文${article.title}`" @click="openArticle(article)">
<text class="article-card__category">{{ article.category }}</text>
<text class="article-card__title">{{ article.title }}</text>
<text class="article-card__summary">{{ article.summary }}</text>
<view class="article-card__meta"><text>{{ article.author }}</text><text>{{ article.updatedAt }}</text></view>
</view>
<AppButton block label="新建谱文" @click="createArticle" />
</view>
<view v-else class="article-list-state-card">
<text>{{ keyword || activeCategory !== '全部' ? '没有找到相关谱文' : '还没有谱文' }}</text>
<text>{{ keyword || activeCategory !== '全部' ? '换一个关键词或分类继续查找。' : '从第一篇家训、序言或家族往事开始记录。' }}</text>
<AppButton block :label="keyword || activeCategory !== '全部' ? '清空筛选' : '新建谱文'" @click="keyword || activeCategory !== '全部' ? resetFilters() : createArticle()" />
</view>
</template>
<view v-else class="article-list-state-card">
<text>{{ listState === 'empty' ? '还没有谱文' : '谱文列表暂不可用' }}</text>
<text>{{ listState === 'empty' ? '记录第一篇家风家训或家族往事。' : '请稍后重新查看,已有谱文不会受到影响。' }}</text>
<AppButton :type="listState === 'error' ? 'secondary' : 'primary'" block :label="listState === 'error' ? '重新查看' : '新建谱文'" @click="listState === 'error' ? restoreArticles() : createArticle()" />
</view>
</view>
</view>
</template>
<script setup>
import ModulePage from "@/components/ModulePage.vue";
import { computed, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const articleCategories = ["全部", "家风家训", "家族往事", "族谱序言"];
const baseArticles = [
{ id: "101", category: "家风家训", title: "孝友传家的日常", summary: "从敬老、睦亲与守信的小事里,看见家风如何代代相传。", author: "汤文正", updatedAt: "今天更新" },
{ id: "102", category: "家族往事", title: "祖居门前的那棵桂花树", summary: "长辈口述的旧居记忆,以及每年中秋一家人相聚的故事。", author: "汤淑华", updatedAt: "昨天更新" },
{ id: "103", category: "族谱序言", title: "续修族谱序", summary: "说明本次续修的缘起、资料来源与共同参与的家人。", author: "谱主", updatedAt: "5 月 12 日" },
];
const articles = ref([...baseArticles]);
const activeCategory = ref("全部");
const keyword = ref("");
const listState = ref("loading");
const filteredArticles = computed(() => {
const term = keyword.value.trim().toLowerCase();
return articles.value.filter((article) => {
const categoryMatched = activeCategory.value === "全部" || article.category === activeCategory.value;
const keywordMatched = !term || `${article.title} ${article.summary} ${article.author}`.toLowerCase().includes(term);
return categoryMatched && keywordMatched;
});
});
onLoad((query) => {
const count = Math.max(1, Math.min(Number(query.count) || baseArticles.length, 50));
articles.value = Array.from({ length: count }, (_, index) => ({
...baseArticles[index % baseArticles.length],
id: String(101 + index),
title: count > baseArticles.length ? `${baseArticles[index % baseArticles.length].title}(第 ${index + 1} 篇)` : baseArticles[index].title,
}));
listState.value = ["loading", "empty", "error"].includes(query.state) ? query.state : "ready";
});
const openArticle = (article) => uni.navigateTo({ url: `/pages/family/f05-article-detail?articleId=${article.id}` });
const createArticle = () => uni.navigateTo({ url: "/pages/family/f06-article-editor?mode=create" });
const restoreArticles = () => { listState.value = "ready"; };
const resetFilters = () => { keyword.value = ""; activeCategory.value = "全部"; };
</script>
<style scoped lang="scss">
.article-list-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.article-list-header, .article-list-loading, .article-list-content { z-index: 1; }
.article-list-loading { min-height: calc(100vh - 100rpx); }
.article-list-content { padding: 18rpx 24rpx 72rpx; }
.article-search { min-height: 82rpx; padding: 12rpx 26rpx; box-sizing: border-box; background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat; }
.article-search input { width: 100%; min-height: 58rpx; color: $ink; font-size: 24rpx; }
.article-categories { width: 100%; margin-top: 16rpx; }
.article-categories__row { display: flex; flex-wrap: wrap; gap: 12rpx; padding: 2rpx 4rpx 8rpx; }
.article-category { min-height: 58rpx; padding: 0 28rpx; color: $ink-muted; font-size: 23rpx; background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat; }
.article-category text { display: flex; min-height: 58rpx; align-items: center; }
.article-category--active { color: $brand-red; font-weight: 700; }
.article-list { display: flex; flex-direction: column; gap: 16rpx; margin-top: 10rpx; }
.article-card, .article-list-state-card { width: 100%; box-sizing: border-box; background: url("/static/assets/modules/family/transparent/module-content-frame.png") center / 100% 100% no-repeat; }
.article-card { min-height: 196rpx; padding: 34rpx 46rpx 30rpx; }
.article-card > text { display: block; }
.article-card__category { color: $brand-red; font-size: 21rpx; font-weight: 700; letter-spacing: 2rpx; }
.article-card__title { margin-top: 7rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 31rpx; font-weight: 700; }
.article-card__summary { margin-top: 10rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.55; }
.article-card__meta { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6rpx 18rpx; margin-top: 12rpx; color: $ink-muted; font-size: 20rpx; }
.article-list > .app-button { margin-top: 8rpx; }
.article-list-state-card { min-height: 340rpx; margin-top: 30rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.article-list-state-card > text { display: block; }
.article-list-state-card > text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 35rpx; font-weight: 700; }
.article-list-state-card > text:nth-child(2) { margin-top: 18rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
.article-list-state-card .app-button { margin-top: 28rpx; }
</style>
+111 -3
View File
@@ -1,5 +1,113 @@
<!-- 页面编号F-05用途谱文详情 -->
<template><ModulePage page-id="f05" /></template>
<!-- 页面编号F-05用途谱文正文收藏编辑与受控状态 -->
<template>
<view class="article-detail-page" :class="articleStateClasses">
<ModulePageBackground module="family" />
<view class="article-detail-header"><PageHeader title="谱文详情" /></view>
<view v-if="articleState === 'loading'" class="article-detail-loading">
<AppLoading text="正在读取谱文" description="请稍候,正在整理正文与收录信息。" />
</view>
<view v-else class="article-detail-content">
<template v-if="articleState === 'ready'">
<view class="article-paper">
<text class="article-paper__category">{{ article.category }}</text>
<text class="article-paper__title">{{ article.title }}</text>
<view class="article-paper__meta"><text>{{ article.author }}</text><text>{{ article.updatedAt }}</text></view>
<view class="article-paper__body">
<text v-for="(paragraph, index) in articleParagraphs" :key="index">{{ paragraph }}</text>
</view>
</view>
<view class="article-actions">
<AppButton block :label="favorite ? '已收藏谱文' : '收藏谱文'" @click="toggleFavorite" />
<AppButton type="secondary" block label="编辑谱文" @click="editArticle" />
</view>
</template>
<view v-else class="article-state-card">
<text>{{ stateCopy.title }}</text>
<text>{{ stateCopy.copy }}</text>
<AppButton :type="articleState === 'error' ? 'secondary' : 'primary'" block :label="stateCopy.action" @click="handleStateAction" />
</view>
</view>
<AppToast :visible="toastVisible" :message="favorite ? '已收藏谱文' : '已取消收藏'" />
</view>
</template>
<script setup>
import ModulePage from "@/components/ModulePage.vue";
import { computed, onUnmounted, reactive, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const articleRecords = [
{ id: "101", category: "家风家训", title: "孝友传家的日常", author: "汤文正", updatedAt: "2024 年 5 月 12 日", paragraphs: ["孝友传家,不只在族谱序言里,也在一家人每日的言行中。长辈以宽厚待晚辈,晚辈以耐心照料长辈,亲友之间守信互助,便是最朴素也最长久的家风。", "勤俭并非一味节省,而是珍惜所得、量入为出,也愿意在家人需要时伸出援手。家中每一代人都可以用自己的方式,把这份分寸与担当继续传下去。", "敬祖睦宗,最终是为了让今天的家人彼此认识、彼此关心。记录姓名与世代之外,也应留下真实的生活、共同经历和温暖记忆。"] },
{ id: "102", category: "家族往事", title: "祖居门前的那棵桂花树", author: "汤淑华", updatedAt: "2024 年 5 月 10 日", paragraphs: ["祖居门前曾有一棵桂花树。每到中秋,院里都是清甜的香气,远道回来的家人也总能循着那股味道找到家门。", "后来房屋几经修缮,桂花树仍被大家小心保留下来。它见过孩子长大,也见过长辈把往事一遍遍讲给后来人。"] },
{ id: "103", category: "族谱序言", title: "续修族谱序", author: "谱主", updatedAt: "2024 年 5 月 8 日", paragraphs: ["本次续修以旧谱、碑记、户籍资料和长辈口述为基础,由家人共同核对补充。凡暂不能确认之处,均保留来源和疑问,留待后续查证。", "愿这份记录不仅理清世系,也能保存家风、人物与共同记忆。"] },
];
const articleId = ref("101");
const article = reactive({ ...articleRecords[0] });
const articleState = ref("loading");
const favorite = ref(false);
const toastVisible = ref(false);
let toastTimer = null;
const articleParagraphs = computed(() => article.paragraphs || []);
const articleStateClasses = computed(() => ({
[`article-state--${articleState.value}`]: true,
"article-state--expired": articleState.value === "expired",
"article-state--privacy": articleState.value === "privacy",
"article-state--error": articleState.value === "error",
}));
const stateCopy = computed(() => ({
expired: { title: "这篇谱文已无法查看", copy: "内容可能已被作者删除或取消公开,请返回谱文列表查看其他内容。", action: "返回谱文列表" },
privacy: { title: "这篇谱文暂未公开", copy: "作者仅向有权限的家人开放正文,请返回谱文列表查看其他内容。", action: "返回谱文列表" },
error: { title: "谱文暂不可用", copy: "请稍后重新查看,已有谱文不会受到影响。", action: "重新查看" },
}[articleState.value] || {}));
onLoad((query) => {
articleId.value = String(query.articleId || "101");
const selected = articleRecords.find((item) => item.id === articleId.value);
if (selected) Object.assign(article, selected);
articleState.value = ["loading", "error", "expired", "privacy"].includes(query.state)
? query.state
: selected
? "ready"
: "expired";
});
const toggleFavorite = () => {
favorite.value = !favorite.value;
toastVisible.value = true;
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(() => { toastVisible.value = false; }, 1800);
};
const editArticle = () => uni.navigateTo({ url: `/pages/family/f06-article-editor?mode=edit&articleId=${articleId.value}` });
const backToArticles = () => uni.redirectTo({ url: "/pages/family/f04-article-list" });
const handleStateAction = () => { if (articleState.value === "error") articleState.value = "ready"; else backToArticles(); };
onUnmounted(() => { if (toastTimer) clearTimeout(toastTimer); });
</script>
<style scoped lang="scss">
.article-detail-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.article-detail-header, .article-detail-loading, .article-detail-content { z-index: 1; }
.article-detail-loading { min-height: calc(100vh - 100rpx); }
.article-detail-content { padding: 18rpx 24rpx 72rpx; }
.article-paper, .article-state-card { width: 100%; box-sizing: border-box; background: url("/static/assets/modules/family/transparent/module-content-frame.png") center / 100% 100% no-repeat; }
.article-paper { padding: 50rpx 48rpx 54rpx; }
.article-paper > text { display: block; }
.article-paper__category { color: $brand-red; font-size: 22rpx; font-weight: 700; letter-spacing: 3rpx; }
.article-paper__title { margin-top: 12rpx; color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 39rpx; font-weight: 700; line-height: 1.35; }
.article-paper__meta { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6rpx 18rpx; margin-top: 16rpx; color: $ink-muted; font-size: 21rpx; }
.article-paper__body { margin-top: 28rpx; }
.article-paper__body text { display: block; margin-top: 18rpx; color: $ink; font-size: 25rpx; line-height: 1.85; text-align: justify; }
.article-actions { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14rpx; margin-top: 18rpx; }
.article-state-card { min-height: 350rpx; margin-top: 36rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.article-state-card > text { display: block; }
.article-state-card > text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 35rpx; font-weight: 700; }
.article-state-card > text:nth-child(2) { margin-top: 18rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
.article-state-card .app-button { margin-top: 30rpx; }
@media (max-width: 340px) { .article-actions { grid-template-columns: minmax(0, 1fr); } }
</style>
+30 -35
View File
@@ -47,7 +47,6 @@
class="editor-control"
:class="{ 'editor-control--error': fieldErrors.title }"
>
<image :src="fieldFrame" mode="scaleToFill" />
<input
v-model="form.title"
maxlength="40"
@@ -67,7 +66,6 @@
class="editor-control"
:class="{ 'editor-control--error': fieldErrors.category }"
>
<image :src="fieldFrame" mode="scaleToFill" />
<input
v-model="form.category"
maxlength="20"
@@ -87,7 +85,6 @@
class="editor-control editor-control--textarea"
:class="{ 'editor-control--error': fieldErrors.content }"
>
<image :src="fieldFrame" mode="scaleToFill" />
<textarea
v-model="form.content"
maxlength="1200"
@@ -127,8 +124,6 @@ import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const fieldFrame =
"/static/assets/modules/family/transparent/module-field-frame.png";
const draftFixture = {
title: "汤氏家训辑录",
category: "家风家训",
@@ -136,6 +131,9 @@ const draftFixture = {
"孝友传家,勤俭立业;敬祖睦宗,诚实待人。愿后人常怀感恩,彼此扶持。",
};
const editorState = ref("form");
const articleId = ref("");
const editorMode = ref("create");
const simulateSaveFailure = ref(false);
const allowedStates = new Set([
"draft",
"loading",
@@ -156,8 +154,8 @@ const actionLabel = computed(() =>
: "保存谱文",
);
const fillDraft = (failed = false) => {
Object.assign(form, draftFixture, failed ? { title: "保存失败" } : {});
const fillDraft = () => {
Object.assign(form, draftFixture);
};
const showAllFieldErrors = () => {
fieldErrors.title = "请填写文章标题";
@@ -166,9 +164,15 @@ const showAllFieldErrors = () => {
};
onLoad((query) => {
articleId.value = query.articleId || "";
editorMode.value = articleId.value || query.mode === "edit" ? "edit" : "create";
const requestedState = allowedStates.has(query.state) ? query.state : "form";
if (["draft", "saving", "error"].includes(requestedState)) {
fillDraft(requestedState === "error");
simulateSaveFailure.value = requestedState === "error";
if (
editorMode.value === "edit" ||
["draft", "saving", "error"].includes(requestedState)
) {
fillDraft();
}
if (requestedState === "validation") showAllFieldErrors();
editorState.value = requestedState;
@@ -197,12 +201,18 @@ const submit = () => {
}
editorState.value = "saving";
saveTimer = setTimeout(() => {
editorState.value =
form.title.trim() === "保存失败" ? "error" : "success";
editorState.value = simulateSaveFailure.value ? "error" : "success";
simulateSaveFailure.value = false;
saveTimer = null;
}, 320);
};
const returnToList = () => uni.navigateBack();
const returnToList = () =>
uni.redirectTo({
url:
editorMode.value === "edit" && articleId.value
? `/pages/family/f05-article-detail?articleId=${articleId.value}`
: "/pages/family/f04-article-list",
});
onUnload(() => {
if (saveTimer) clearTimeout(saveTimer);
@@ -211,16 +221,15 @@ onUnload(() => {
<style scoped lang="scss">
.article-editor-page {
position: relative;
display: flex;
min-height: 100vh;
overflow-x: hidden;
flex-direction: column;
background: $paper;
}
.article-editor-page__header,
.article-editor-loading,
.article-editor-content {
position: relative;
z-index: 2;
z-index: 1;
}
.article-editor-loading {
padding-top: 150rpx;
@@ -230,7 +239,6 @@ onUnload(() => {
}
.editor-panel,
.editor-result-card {
position: relative;
width: 100%;
border: 20rpx solid transparent;
border-image-source: url("/static/assets/modules/family/transparent/module-content-frame.png");
@@ -240,8 +248,6 @@ onUnload(() => {
box-sizing: border-box;
}
.editor-panel__body {
position: relative;
z-index: 1;
padding: 38rpx 34rpx 42rpx;
}
.editor-eyebrow {
@@ -279,22 +285,13 @@ onUnload(() => {
font-weight: 700;
}
.editor-control {
position: relative;
display: flex;
height: 82rpx;
min-height: 82rpx;
align-items: center;
}
.editor-control > image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat;
}
.editor-control input,
.editor-control textarea {
position: relative;
z-index: 1;
box-sizing: border-box;
width: 100%;
color: $ink;
@@ -302,17 +299,17 @@ onUnload(() => {
}
.editor-control input {
width: 100%;
height: 82rpx;
min-height: 82rpx;
padding: 0 28rpx;
}
.editor-control--textarea {
height: 220rpx;
min-height: 220rpx;
padding: 20rpx 24rpx;
box-sizing: border-box;
}
.editor-control textarea {
width: 100%;
height: 180rpx;
min-height: 180rpx;
line-height: 1.65;
}
.editor-control--error {
@@ -350,8 +347,6 @@ onUnload(() => {
min-height: 420rpx;
}
.editor-result-card__body {
position: relative;
z-index: 1;
padding: 112rpx 52rpx 68rpx;
text-align: center;
}
+129 -3
View File
@@ -1,5 +1,131 @@
<!-- 页面编号F-07用途家族相册列表 -->
<template><ModulePage page-id="f07" /></template>
<!-- 页面编号F-07用途家族相册列表创建与受控状态 -->
<template>
<view class="album-list-page" :class="albumStateClasses">
<ModulePageBackground module="family" />
<view class="album-list-header"><PageHeader title="家族相册" action="新建" @action="createAlbum" /></view>
<view v-if="albumState === 'loading'" class="album-list-loading">
<AppLoading text="正在整理家族相册" description="请稍候,正在读取照片与更新时间。" />
</view>
<view v-else class="album-list-content">
<template v-if="albumState === 'ready'">
<view class="album-list-lead"><text>让每一张照片都回到家人身边</text><text> {{ albums.length }} 本相册</text></view>
<view v-if="albums.length" class="album-list">
<view v-for="album in albums" :key="album.id" class="album-card" role="button" :aria-label="`打开相册${album.name}`" @click="openAlbum(album)">
<view class="album-card__media"><image class="album-card__cover" :src="album.cover" mode="aspectFill" :alt="album.name" /></view>
<view class="album-card__copy">
<text>{{ album.name }}</text>
<text>{{ album.photoCount }} 张照片 · {{ album.updatedAt }}</text>
<text>{{ album.description }}</text>
</view>
</view>
<AppButton block label="新建相册" @click="createAlbum" />
</view>
<view v-else class="album-state-card">
<text>还没有相册</text><text>创建一本相册把团圆成长与祖居旧影整理在一起</text>
<AppButton block label="新建相册" @click="createAlbum" />
</view>
</template>
<view v-else class="album-state-card">
<text>{{ albumState === 'empty' ? '还没有相册' : '相册暂不可用' }}</text>
<text>{{ albumState === 'empty' ? '从第一本团圆相册开始收集家族影像。' : '请稍后重新查看,已有照片不会受到影响。' }}</text>
<AppButton :type="albumState === 'error' ? 'secondary' : 'primary'" block :label="albumState === 'error' ? '重新查看' : '新建相册'" @click="albumState === 'error' ? restoreAlbums() : createAlbum()" />
</view>
</view>
<AppDialog :visible="dialogVisible" eyebrow="新建相册" title="为家人整理一段影像" message="相册创建后可继续添加照片和说明。" confirm-text="创建相册" cancel-text="取消" show-cancel @confirm="confirmCreateAlbum" @cancel="closeCreateDialog">
<view class="album-dialog-field">
<text>相册名称</text>
<input v-model="albumNameDraft" maxlength="30" placeholder="例如:春节团圆" />
<text v-if="albumNameError">{{ albumNameError }}</text>
</view>
</AppDialog>
<AppToast :visible="toastVisible" message="相册已创建" />
</view>
</template>
<script setup>
import ModulePage from "@/components/ModulePage.vue";
import { computed, onUnmounted, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppDialog from "@/components/AppDialog.vue";
import AppLoading from "@/components/AppLoading.vue";
import AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const baseAlbums = [
{ id: "201", name: "2024 春节团圆", photoCount: 18, updatedAt: "今天更新", description: "三代家人的团圆饭与院前合影", cover: "/static/assets/modules/family/f08/f08-reunion-hero.png" },
{ id: "202", name: "祖居旧影", photoCount: 32, updatedAt: "5 月 10 日更新", description: "祖居、旧物与长辈珍藏的老照片", cover: "/static/assets/modules/family/f08/f08-ancestral-home.png" },
{ id: "203", name: "儿童成长", photoCount: 46, updatedAt: "持续更新", description: "记录孩子们每一个值得珍藏的瞬间", cover: "/static/assets/modules/family/f08/f08-family-portrait.png" },
];
const albums = ref([...baseAlbums]);
const albumState = ref("loading");
const dialogVisible = ref(false);
const albumNameDraft = ref("");
const albumNameError = ref("");
const toastVisible = ref(false);
let toastTimer = null;
const albumStateClasses = computed(() => ({
[`album-list-state--${albumState.value}`]: true,
"album-state--empty": albumState.value === "empty",
"album-list-state--loading": albumState.value === "loading",
"album-list-state--error": albumState.value === "error",
}));
onLoad((query) => {
const count = Math.max(1, Math.min(Number(query.count) || baseAlbums.length, 30));
albums.value = Array.from({ length: count }, (_, index) => ({
...baseAlbums[index % baseAlbums.length],
id: String(201 + index),
name: count > baseAlbums.length ? `${baseAlbums[index % baseAlbums.length].name}${index + 1}` : baseAlbums[index].name,
}));
albumState.value = ["loading", "empty", "error"].includes(query.state) ? query.state : "ready";
});
const openAlbum = (album) => uni.navigateTo({ url: `/pages/family/f08-album-detail?albumId=${album.id}` });
const createAlbum = () => { albumNameDraft.value = ""; albumNameError.value = ""; dialogVisible.value = true; };
const closeCreateDialog = () => { dialogVisible.value = false; };
const confirmCreateAlbum = () => {
const name = albumNameDraft.value.trim();
if (!name) { albumNameError.value = "请填写相册名称"; return; }
albums.value.unshift({ id: String(Date.now()), name, photoCount: 0, updatedAt: "刚刚创建", description: "等待添加第一张照片", cover: "/static/assets/modules/family/f08/f08-reunion-hero.png" });
albumState.value = "ready";
dialogVisible.value = false;
toastVisible.value = true;
toastTimer = setTimeout(() => { toastVisible.value = false; }, 1800);
};
const restoreAlbums = () => { albumState.value = "ready"; };
onUnmounted(() => { if (toastTimer) clearTimeout(toastTimer); });
</script>
<style scoped lang="scss">
.album-list-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.album-list-header, .album-list-loading, .album-list-content { z-index: 1; }
.album-list-loading { min-height: calc(100vh - 100rpx); }
.album-list-content { padding: 18rpx 24rpx 72rpx; }
.album-list-lead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8rpx 18rpx; min-height: 62rpx; padding: 0 20rpx; color: $ink-muted; font-size: 22rpx; background: url("/static/assets/modules/genealogy/transparent/section-divider.png") center / 100% 100% no-repeat; }
.album-list { display: flex; flex-direction: column; gap: 16rpx; margin-top: 16rpx; }
.album-card, .album-state-card { width: 100%; box-sizing: border-box; background: url("/static/assets/modules/family/transparent/module-content-frame.png") center / 100% 100% no-repeat; }
.album-card { display: grid; grid-template-columns: minmax(150rpx, 0.7fr) minmax(0, 1.3fr); min-height: 210rpx; gap: 22rpx; padding: 30rpx 38rpx; }
.album-card__media { width: 100%; aspect-ratio: 4 / 3; align-self: center; }
.album-card__cover { display: block; width: 100%; height: 100%; }
.album-card__copy { align-self: center; min-width: 0; }
.album-card__copy text { display: block; overflow-wrap: anywhere; }
.album-card__copy text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 30rpx; font-weight: 700; }
.album-card__copy text:nth-child(2) { margin-top: 9rpx; color: $brand-red; font-size: 21rpx; }
.album-card__copy text:last-child { margin-top: 8rpx; color: $ink-muted; font-size: 22rpx; line-height: 1.5; }
.album-list > .app-button { margin-top: 8rpx; }
.album-state-card { min-height: 340rpx; margin-top: 30rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.album-state-card > text { display: block; }
.album-state-card > text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 35rpx; font-weight: 700; }
.album-state-card > text:nth-child(2) { margin-top: 18rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
.album-state-card .app-button { margin-top: 28rpx; }
.album-dialog-field { width: 100%; margin: 24rpx 0; text-align: left; }
.album-dialog-field > text:first-child { display: block; color: $ink; font-size: 23rpx; font-weight: 700; }
.album-dialog-field input { width: 100%; min-height: 76rpx; margin-top: 10rpx; padding: 0 22rpx; box-sizing: border-box; color: $ink; font-size: 24rpx; background: url("/static/assets/modules/family/transparent/module-field-frame.png") center / 100% 100% no-repeat; }
.album-dialog-field > text:last-child { display: block; margin-top: 8rpx; color: $brand-red; font-size: 21rpx; }
@media (max-width: 340px) { .album-card { grid-template-columns: 130rpx minmax(0, 1fr); gap: 16rpx; padding-right: 30rpx; padding-left: 30rpx; } }
</style>
+11 -22
View File
@@ -68,7 +68,7 @@
</view>
</view>
<view class="album-upload-action" @click="showUploadNotice">
<view class="album-upload-action" @click="toUpload">
<AppButton block label="添加照片" />
</view>
</template>
@@ -93,24 +93,20 @@
</view>
</view>
<AppToast :visible="uploadNoticeVisible" :message="uploadNoticeMessage" />
</view>
</template>
<script setup>
import { ref } from "vue";
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
import { onBackPress, onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const albumState = ref("normal");
const albumId = ref("");
const previewVisible = ref(false);
const previewIndex = ref(0);
const uploadNoticeVisible = ref(false);
const uploadNoticeMessage = "上传照片将在入口接通后开放";
let uploadNoticeTimer;
const photos = [
{ src: "/static/assets/modules/family/f08/f08-reunion-hero.png", alt: "春节团圆时三代家人的合影", caption: "除夕团圆 · 2024" },
@@ -131,19 +127,17 @@ const closePreview = () => {
albumState.value = "normal";
};
const showUploadNotice = () => {
uploadNoticeVisible.value = true;
clearTimeout(uploadNoticeTimer);
uploadNoticeTimer = setTimeout(() => {
uploadNoticeVisible.value = false;
}, 1800);
};
const toUpload = () =>
uni.navigateTo({
url: `/pages/family/f09-media-upload?albumId=${albumId.value}`,
});
const returnToAlbums = () => {
uni.redirectTo({ url: "/pages/family/f07-album-list" });
};
onLoad((query) => {
albumId.value = query.albumId || "reunion";
albumState.value =
query.state === "empty"
? "empty"
@@ -161,22 +155,18 @@ onBackPress(() => {
return true;
});
onUnload(() => {
clearTimeout(uploadNoticeTimer);
});
</script>
<style scoped lang="scss">
.album-detail-page {
position: relative;
display: flex;
min-height: 100vh;
overflow-x: hidden;
flex-direction: column;
background: $paper;
}
.album-detail-header,
.album-detail-content {
position: relative;
z-index: 2;
z-index: 1;
}
.album-detail-content {
padding: 22rpx 24rpx calc(48rpx + env(safe-area-inset-bottom));
@@ -265,7 +255,6 @@ onUnload(() => {
}
.album-empty-state,
.album-expired-state {
position: relative;
width: 100%;
min-height: 430rpx;
box-sizing: border-box;
+11 -9
View File
@@ -237,6 +237,7 @@ const mockLibrary = [
];
const selectedPhotos = ref([]);
const albumId = ref("");
const activePhoto = computed(
() => selectedPhotos.value[activePhotoIndex.value] || null,
);
@@ -322,10 +323,13 @@ const retryFailed = () => {
};
const returnToAlbum = () => {
uni.redirectTo({ url: "/pages/family/f08-album-detail" });
uni.redirectTo({
url: `/pages/family/f08-album-detail?albumId=${albumId.value}`,
});
};
onLoad((query) => {
albumId.value = query.albumId || "reunion";
uploadState.value = ["permission", "selected", "uploading", "error", "success"].includes(query.state)
? query.state
: "initial";
@@ -339,21 +343,19 @@ onLoad((query) => {
<style scoped lang="scss">
.media-upload-page {
position: relative;
display: flex;
min-height: 100vh;
overflow-x: hidden;
flex-direction: column;
background: $paper;
}
.media-upload-header,
.media-upload-content {
position: relative;
z-index: 2;
z-index: 1;
}
.media-upload-content {
padding: 20rpx 24rpx calc(48rpx + env(safe-area-inset-bottom));
}
.media-album-card {
position: relative;
display: grid;
min-height: 154rpx;
box-sizing: border-box;
@@ -420,7 +422,6 @@ onLoad((query) => {
}
.media-photo-tile,
.media-add-tile {
position: relative;
min-width: 0;
min-height: 44px;
overflow: hidden;
@@ -429,6 +430,9 @@ onLoad((query) => {
border: 2rpx solid rgba(179, 133, 63, 0.62);
background: rgba(247, 241, 231, 0.88);
}
.media-photo-tile {
position: relative;
}
.media-photo-tile--active {
outline: 4rpx solid rgba(159, 44, 35, 0.78);
outline-offset: -4rpx;
@@ -570,7 +574,6 @@ onLoad((query) => {
.media-batch-field textarea,
.media-photo-field textarea {
width: 100%;
height: 92rpx;
min-height: 92rpx;
margin-top: 12rpx;
box-sizing: border-box;
@@ -601,7 +604,6 @@ onLoad((query) => {
}
.media-permission-card,
.media-success-card {
position: relative;
min-height: 410rpx;
margin-top: 24rpx;
box-sizing: border-box;
+7 -16
View File
@@ -46,56 +46,47 @@ const returnToFamily = () => {
<style scoped lang="scss">
.video-status-page {
position: relative;
min-height: 100vh;
overflow-x: hidden;
background: $paper;
}
.video-status-header,
.video-status-content {
position: relative;
z-index: 2;
}
.video-status-content {
padding: 16rpx 26rpx 56rpx;
}
.video-status-lead {
position: relative;
height: 56rpx;
display: grid;
min-height: 56rpx;
margin: 0 12rpx 18rpx;
color: $ink-muted;
font-size: 23rpx;
text-align: center;
}
.video-status-lead image {
position: absolute;
inset: 0;
grid-area: 1 / 1;
width: 100%;
height: 100%;
}
.video-status-lead text {
position: relative;
z-index: 1;
display: flex;
grid-area: 1 / 1;
align-items: center;
justify-content: center;
height: 100%;
}
.video-status-card {
position: relative;
display: grid;
width: 100%;
min-height: 330rpx;
text-align: center;
}
.video-status-card__skin {
position: absolute;
inset: 0;
grid-area: 1 / 1;
width: 100%;
height: 100%;
}
.video-status-card__body {
position: relative;
z-index: 1;
grid-area: 1 / 1;
padding: 70rpx 60rpx 48rpx;
}
.video-status-card__eyebrow,