60%
This commit is contained in:
@@ -1,19 +1,13 @@
|
||||
<!-- 页面编号:F-01;用途:家族动态入口。列表 DTO 缺失时不展示 fixture 条目。 -->
|
||||
<template>
|
||||
<view class="family-page" :class="`feed-state--${feedState}`">
|
||||
<ModulePageBackground module="family" />
|
||||
<view class="family-page__header"><PageHeader root title="家族动态" :action="hasValidContext ? '发布' : ''" @action="toPublish" /></view>
|
||||
<view class="feed-content">
|
||||
<view class="feed-heading"><text>家族圈</text><text>家宴、通知与共同记忆</text></view>
|
||||
<template v-if="hasValidContext">
|
||||
<view class="feed-source-note"><text>动态列表接口当前没有声明条目 DTO;页面不猜测标题、正文、发布人或时间,已停止展示本地动态。</text></view>
|
||||
<view class="feed-shortcuts">
|
||||
<view v-for="item in shortcuts" :key="item.key" class="feed-shortcut" @click="openSection(item.key)"><text>{{ item.label }}</text></view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="feed-state-card">
|
||||
<view class="feed-state-card__copy"><text>{{ stateCopy.title }}</text><text>{{ stateCopy.copy }}</text></view>
|
||||
</view>
|
||||
<view v-if="hasValidContext" class="feed-shortcuts"><view v-for="item in shortcuts" :key="item.key" class="feed-shortcut" @click="openSection(item.key)"><text>{{ item.label }}</text></view></view>
|
||||
<AppLoading v-if="feedState === 'loading'" text="正在读取家族动态" description="请稍候,正在整理家族近况。" />
|
||||
<view v-else-if="feedState === 'list'" class="feed-list"><view v-for="item in feeds" :key="item.id" class="feed-card" @click="openFeed(item)"><text class="feed-card__publisher">{{ item.publisher }}</text><text class="feed-card__content">{{ item.content }}</text><text class="feed-card__meta">{{ feedTypeLabel(item.type) }} · {{ item.time }}</text></view></view>
|
||||
<view v-else class="feed-state-card"><view class="feed-state-card__copy"><text>{{ stateCopy.title }}</text><text>{{ stateCopy.copy }}</text></view></view>
|
||||
<view class="feed-action" @click="handlePrimaryAction"><text>{{ stateCopy.action }}</text></view>
|
||||
</view>
|
||||
<AppTabbar active="family" />
|
||||
@@ -22,93 +16,29 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import AppTabbar from "@/components/AppTabbar.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { appApi, isRequestCancelled } from "@/utils/api.js";
|
||||
import { genealogyContext } from "@/utils/genealogy-context.js";
|
||||
import { goRoot, openPage } from "@/utils/navigation.js";
|
||||
|
||||
const genealogyId = ref("");
|
||||
const hasValidContext = ref(false);
|
||||
const feedState = ref("unavailable");
|
||||
const stateCopy = computed(() => {
|
||||
if (!hasValidContext.value) {
|
||||
return {
|
||||
title: "请先选择有效家谱",
|
||||
copy: "家族动态必须归属明确家谱,页面不会展示其他家谱的内容。",
|
||||
action: "返回我的家谱",
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: "动态列表待后端字段合同",
|
||||
copy: "已找到动态列表 operation,但响应只声明通用对象数组;待后端提供动态 ID、标题、正文、发布人和时间字段后再恢复列表与详情入口。",
|
||||
action: "发布家族动态",
|
||||
};
|
||||
});
|
||||
const shortcuts = [
|
||||
{ key: "articles", label: "谱文" },
|
||||
{ key: "albums", label: "相册" },
|
||||
{ key: "rituals", label: "礼仪" },
|
||||
{ key: "memos", label: "备忘" },
|
||||
{ key: "people", label: "人物录" },
|
||||
{ key: "gifts", label: "贺礼簿" },
|
||||
{ key: "merits", label: "功德录" },
|
||||
{ key: "videos", label: "家族视频" },
|
||||
];
|
||||
|
||||
onLoad((query) => {
|
||||
const hasRouteIdentity = Object.prototype.hasOwnProperty.call(query || {}, "genealogyId");
|
||||
const resolvedGenealogyId = hasRouteIdentity
|
||||
? String(query?.genealogyId || "")
|
||||
: String(genealogyContext.getCurrentGenealogyId() || "");
|
||||
genealogyId.value = resolvedGenealogyId;
|
||||
hasValidContext.value = /^[1-9]\d*$/.test(resolvedGenealogyId);
|
||||
feedState.value = hasValidContext.value ? "unavailable" : "error";
|
||||
if (!hasRouteIdentity && hasValidContext.value) {
|
||||
goRoot("F01", { genealogyId: resolvedGenealogyId }).catch(() => {
|
||||
hasValidContext.value = false;
|
||||
feedState.value = "error";
|
||||
});
|
||||
}
|
||||
});
|
||||
const toPublish = () => hasValidContext.value
|
||||
? openPage("F02", { genealogyId: genealogyId.value }, "F01")
|
||||
: goRoot("G01");
|
||||
const openSection = (key) => {
|
||||
const routes = {
|
||||
articles: "F04",
|
||||
albums: "F07",
|
||||
rituals: "R05",
|
||||
memos: "R10",
|
||||
people: "R01",
|
||||
gifts: "R03",
|
||||
merits: "R11",
|
||||
videos: "F10",
|
||||
};
|
||||
return openPage(routes[key], { genealogyId: genealogyId.value }, "F01");
|
||||
};
|
||||
const handlePrimaryAction = () => hasValidContext.value ? toPublish() : goRoot("G01");
|
||||
const genealogyId = ref(""); const hasValidContext = ref(false); const feedState = ref("loading"); const feeds = ref([]); let active = true;
|
||||
const feedTypeLabel = (type) => String(type || "").trim().toLowerCase() === "text" ? "文字动态" : "家族动态";
|
||||
const shortcuts = [{ key: "articles", label: "谱文" }, { key: "albums", label: "相册" }, { key: "rituals", label: "礼仪" }, { key: "memos", label: "备忘" }, { key: "people", label: "人物录" }, { key: "gifts", label: "贺礼簿" }, { key: "merits", label: "功德录" }, { key: "videos", label: "家族视频" }];
|
||||
const stateCopy = computed(() => !hasValidContext.value ? { title: "请先选择有效家谱", copy: "家族动态必须归属明确家谱。", action: "返回我的家谱" } : feedState.value === "empty" ? { title: "还没有家族动态", copy: "发布第一条动态,记录家族此刻。", action: "发布家族动态" } : feedState.value === "error" ? { title: "动态暂时无法读取", copy: "请稍后重新读取。", action: "重新读取" } : { title: "", copy: "", action: "发布家族动态" });
|
||||
const loadFeeds = async () => { if (!hasValidContext.value) return; feedState.value = "loading"; try { const rows = await appApi.getFeeds(genealogyId.value); if (!active) return; feeds.value = rows; feedState.value = feeds.value.length ? "list" : "empty"; } catch (error) { if (!active || isRequestCancelled(error)) return; feedState.value = "error"; } };
|
||||
onLoad((query) => { const supplied = Object.prototype.hasOwnProperty.call(query || {}, "genealogyId"); genealogyId.value = supplied ? String(query.genealogyId || "") : String(genealogyContext.getCurrentGenealogyId() || ""); hasValidContext.value = /^[1-9]\d*$/.test(genealogyId.value); if (hasValidContext.value) void loadFeeds(); else feedState.value = "error"; });
|
||||
onShow(() => { if (hasValidContext.value) void loadFeeds(); });
|
||||
const toPublish = () => hasValidContext.value ? openPage("F02", { genealogyId: genealogyId.value }, "F01") : goRoot("G01");
|
||||
const openFeed = (item) => openPage("F03", { genealogyId: genealogyId.value, feedId: item.id }, "F01");
|
||||
const openSection = (key) => { const routes = { articles: "F04", albums: "F07", rituals: "R05", memos: "R10", people: "R01", gifts: "R03", merits: "R11", videos: "F10" }; return openPage(routes[key], { genealogyId: genealogyId.value }, "F01"); };
|
||||
const handlePrimaryAction = () => feedState.value === "error" ? loadFeeds() : toPublish();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||||
.family-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
|
||||
.family-page__header, .feed-content { z-index: 1; }
|
||||
.feed-content { flex: 1; padding: 24rpx 24rpx 190rpx; }
|
||||
.feed-heading text { display: block; }
|
||||
.feed-heading text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 31rpx; font-weight: 700; }
|
||||
.feed-heading text:last-child { margin-top: 6rpx; color: $ink-muted; font-size: 23rpx; }
|
||||
.feed-source-note { margin-top: 10rpx; padding: 12rpx 16rpx; border: 1rpx solid rgba(128, 106, 81, .22); border-radius: 10rpx; background: rgba(255, 255, 255, .56); }
|
||||
.feed-source-note text { display: block; color: $ink-muted; font-size: 20rpx; line-height: 1.5; }
|
||||
.feed-shortcuts { @include adaptive.adaptive-scroll-button(secondary); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); width: 100%; min-height: 48px; margin-top: 15rpx; }
|
||||
.feed-shortcut { width: 100%; height: 48px; min-height: 44px; }
|
||||
.feed-shortcut text { display: flex; align-items: center; justify-content: center; height: 100%; color: $ink; font-size: 22rpx; font-weight: 700; }
|
||||
.feed-state-card { @include adaptive.adaptive-family-letter; display: flex; width: 100%; min-height: 230rpx; flex-direction: column; justify-content: center; margin-top: 70rpx; box-sizing: border-box; }
|
||||
.feed-state-card__copy { padding: 23% 10%; box-sizing: border-box; text-align: center; }
|
||||
.feed-state-card text { display: block; }
|
||||
.feed-state-card text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 31rpx; font-weight: 700; }
|
||||
.feed-state-card text:last-child { margin-top: 13rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.45; }
|
||||
.feed-action { @include adaptive.adaptive-scroll-button(primary); width: 514rpx; max-width: 100%; min-height: 76rpx; margin: 19rpx auto 0; }
|
||||
.feed-action text { display: flex; align-items: center; justify-content: center; height: 100%; color: #fff9ed; font-size: 24rpx; font-weight: 700; }
|
||||
.family-page{display:flex;min-height:100vh;flex-direction:column;background:$paper}.family-page__header,.feed-content{z-index:1}.feed-content{flex:1;padding:24rpx 24rpx 190rpx}.feed-heading text{display:block}.feed-heading text:first-child{color:$ink;font-family:STKaiti,KaiTi,serif;font-size:31rpx;font-weight:700}.feed-heading text:last-child{margin-top:6rpx;color:$ink-muted;font-size:23rpx}.feed-shortcuts{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:15rpx}.feed-shortcut{min-height:48px;display:flex;align-items:center;justify-content:center}.feed-shortcut text{color:$ink;font-size:22rpx;font-weight:700}.feed-list{margin-top:22rpx}.feed-card,.feed-state-card{@include adaptive.adaptive-family-letter;box-sizing:border-box;margin-top:16rpx;padding:30rpx}.feed-card text,.feed-state-card text{display:block}.feed-card__publisher{color:$brand-red;font-size:22rpx}.feed-card__content{margin-top:12rpx;color:$ink;font-size:28rpx;line-height:1.55}.feed-card__meta{margin-top:12rpx;color:$ink-muted;font-size:21rpx}.feed-state-card{min-height:230rpx;display:flex;align-items:center;justify-content:center;text-align:center}.feed-state-card text:first-child{color:$ink;font-size:31rpx;font-weight:700}.feed-state-card text:last-child{margin-top:13rpx;color:$ink-muted;font-size:23rpx;line-height:1.45}.feed-action{@include adaptive.adaptive-scroll-button(primary);width:514rpx;max-width:100%;min-height:76rpx;margin:19rpx auto}.feed-action text{display:flex;height:100%;align-items:center;justify-content:center;color:#fff9ed;font-size:24rpx;font-weight:700}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user