完成40%
This commit is contained in:
@@ -121,7 +121,7 @@
|
||||
>
|
||||
<view class="genealogy-lower">
|
||||
<view v-if="createdGenealogies.length" class="list-section">
|
||||
<view class="section-heading"><text>我创建的</text></view>
|
||||
<view class="section-heading"><text>我管理的</text></view>
|
||||
<GenealogyCard
|
||||
v-for="item in createdGenealogies"
|
||||
:key="item.id"
|
||||
@@ -144,7 +144,10 @@
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="list-section application-section">
|
||||
<view
|
||||
v-if="applicationRecords.length"
|
||||
class="list-section application-section"
|
||||
>
|
||||
<view class="section-heading"><text>加入申请</text></view>
|
||||
<view
|
||||
v-for="item in applicationRecords"
|
||||
@@ -350,7 +353,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
import { onBackPress, onLoad } from "@dcloudio/uni-app";
|
||||
import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import AppTabbar from "@/components/AppTabbar.vue";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
@@ -358,17 +361,16 @@ import GenealogyCard from "@/components/GenealogyCard.vue";
|
||||
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import {
|
||||
findGenealogyFixture,
|
||||
genealogies,
|
||||
getGenealogyFixtureAccess,
|
||||
listNotificationFixtures,
|
||||
} from "@/data/mock.js";
|
||||
appApi,
|
||||
createRequestController,
|
||||
isRequestCancelled,
|
||||
} from "@/utils/api.js";
|
||||
import { genealogyContext } from "@/utils/genealogy-context.js";
|
||||
import { handleBackPress, openPage, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const isLoading = ref(false);
|
||||
const hasError = ref(false);
|
||||
const list = ref(genealogies);
|
||||
const list = ref([]);
|
||||
const forceEmptyState = ref(false);
|
||||
const contextInvalidated = ref(false);
|
||||
const contextReconcileFailed = ref(false);
|
||||
@@ -378,9 +380,14 @@ const switcherVisible = ref(false);
|
||||
const selectedGenealogyId = ref(null);
|
||||
const listScrollCommand = ref(0);
|
||||
const currentListScrollTop = ref(0);
|
||||
const listRequestController = createRequestController();
|
||||
let loadGeneration = 0;
|
||||
let pageActive = true;
|
||||
let presentationState = "default";
|
||||
let skipNextShowRefresh = true;
|
||||
|
||||
const syncEmptyStateFromRoute = (query = {}) => {
|
||||
const presentationState = ["empty", "loading", "error"].includes(
|
||||
presentationState = ["empty", "loading", "error"].includes(
|
||||
query?.state,
|
||||
)
|
||||
? query.state
|
||||
@@ -419,15 +426,59 @@ const reconcilePageGenealogyContext = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadGenealogies = async () => {
|
||||
const generation = ++loadGeneration;
|
||||
listRequestController.abort();
|
||||
isLoading.value = true;
|
||||
hasError.value = false;
|
||||
try {
|
||||
const result = await appApi.getMyGenealogies({
|
||||
requestController: listRequestController,
|
||||
});
|
||||
if (!pageActive || generation !== loadGeneration) return;
|
||||
list.value = result;
|
||||
reconcilePageGenealogyContext();
|
||||
} catch (error) {
|
||||
if (
|
||||
!pageActive ||
|
||||
generation !== loadGeneration ||
|
||||
isRequestCancelled(error)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
hasError.value = true;
|
||||
} finally {
|
||||
if (pageActive && generation === loadGeneration) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onLoad((query) => {
|
||||
syncEmptyStateFromRoute(query);
|
||||
requestedGenealogyId.value = String(query?.genealogyId || "");
|
||||
reconcilePageGenealogyContext();
|
||||
if (presentationState === "default") {
|
||||
loadGenealogies();
|
||||
} else {
|
||||
reconcilePageGenealogyContext();
|
||||
}
|
||||
});
|
||||
|
||||
const unreadCount = computed(
|
||||
() => listNotificationFixtures().filter((item) => item.unread).length,
|
||||
);
|
||||
onShow(() => {
|
||||
if (skipNextShowRefresh) {
|
||||
skipNextShowRefresh = false;
|
||||
return;
|
||||
}
|
||||
if (presentationState === "default") loadGenealogies();
|
||||
});
|
||||
|
||||
onUnload(() => {
|
||||
pageActive = false;
|
||||
loadGeneration += 1;
|
||||
listRequestController.abort();
|
||||
});
|
||||
|
||||
const unreadCount = computed(() => 0);
|
||||
const hasGenealogies = computed(
|
||||
() => !forceEmptyState.value && list.value.length > 0,
|
||||
);
|
||||
@@ -439,14 +490,10 @@ const isListLayout = computed(
|
||||
hasGenealogies.value,
|
||||
);
|
||||
const createdGenealogies = computed(() =>
|
||||
list.value.filter(
|
||||
(item) => getGenealogyFixtureAccess(item.id).accessRole === "owner",
|
||||
),
|
||||
list.value.filter((item) => item.accessRole === "owner"),
|
||||
);
|
||||
const joinedGenealogies = computed(() =>
|
||||
list.value.filter(
|
||||
(item) => getGenealogyFixtureAccess(item.id).accessRole === "member",
|
||||
),
|
||||
list.value.filter((item) => item.accessRole === "member"),
|
||||
);
|
||||
const availableGenealogies = computed(() => list.value);
|
||||
const currentGenealogy = computed(
|
||||
@@ -456,41 +503,14 @@ const currentGenealogy = computed(
|
||||
) || null,
|
||||
);
|
||||
const isCurrentGenealogyOwner = computed(
|
||||
() =>
|
||||
getGenealogyFixtureAccess(currentGenealogy.value?.id).accessRole ===
|
||||
"owner",
|
||||
() => currentGenealogy.value?.accessRole === "owner",
|
||||
);
|
||||
const currentRoleLabel = computed(() =>
|
||||
isCurrentGenealogyOwner.value ? "管理员" : "成员",
|
||||
);
|
||||
|
||||
// 仅用于页面样式阶段覆盖加入申请的关键状态,不作为接口数据。
|
||||
const applicationRecords = [
|
||||
{
|
||||
id: "pending",
|
||||
genealogyId: "2003",
|
||||
statusLabel: "审核中",
|
||||
tone: "pending",
|
||||
description: "申请已提交,等待管理员审核",
|
||||
},
|
||||
{
|
||||
id: "rejected",
|
||||
genealogyId: "2004",
|
||||
statusLabel: "被拒绝",
|
||||
tone: "rejected",
|
||||
description: "可修改关系说明后重新申请",
|
||||
},
|
||||
{
|
||||
id: "removed",
|
||||
genealogyId: "2005",
|
||||
statusLabel: "已退出",
|
||||
tone: "muted",
|
||||
description: "如需恢复成员身份,可重新申请加入",
|
||||
},
|
||||
].map((record) => ({
|
||||
...record,
|
||||
name: findGenealogyFixture(record.genealogyId)?.name || "未知家谱",
|
||||
}));
|
||||
// 普通加入申请批次接通前不展示本地伪记录。
|
||||
const applicationRecords = ref([]);
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
@@ -603,13 +623,7 @@ const openApplication = (record) => {
|
||||
);
|
||||
};
|
||||
const retryLoad = () => {
|
||||
if (contextReconcileFailed.value) {
|
||||
hasError.value = false;
|
||||
reconcilePageGenealogyContext();
|
||||
return;
|
||||
}
|
||||
hasError.value = false;
|
||||
isLoading.value = false;
|
||||
loadGenealogies();
|
||||
};
|
||||
|
||||
const openShortcut = (key) => {
|
||||
|
||||
@@ -31,16 +31,19 @@
|
||||
>
|
||||
<text class="overview-hero__title">{{ genealogy.name }}</text>
|
||||
<text class="overview-hero__motto">{{
|
||||
genealogy.motto || "敦亲睦族,敬祖传家。"
|
||||
genealogy.intro || "简介待补充"
|
||||
}}</text>
|
||||
<view class="overview-hero__stats">
|
||||
<text>共 {{ genealogy.memberCount || 0 }} 人</text>
|
||||
<text>已激活 {{ genealogy.activeCount || 0 }} 人</text>
|
||||
<text v-if="genealogy.personCount !== null"
|
||||
>世系 {{ genealogy.personCount }} 人</text
|
||||
>
|
||||
<text>{{ getGenealogyAccessPresetLabel(genealogy.accessPreset) }}</text>
|
||||
</view>
|
||||
<view class="overview-hero__stats">
|
||||
<text>始祖 {{ genealogy.ancestorName }}</text>
|
||||
<text>更新于 {{ genealogy.updatedAt }}</text>
|
||||
<text v-if="genealogy.joinTime"
|
||||
>加入于 {{ formatGenealogyTime(genealogy.joinTime) }}</text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -194,11 +197,15 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { onBackPress, onLoad } from "@dcloudio/uni-app";
|
||||
import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { findGenealogyFixture, getGenealogyFixtureAccess } from "@/data/mock.js";
|
||||
import {
|
||||
appApi,
|
||||
createRequestController,
|
||||
isRequestCancelled,
|
||||
} from "@/utils/api.js";
|
||||
import { getGenealogyAccessPresetLabel } from "@/utils/genealogy-contracts.js";
|
||||
import {
|
||||
goBack,
|
||||
@@ -216,6 +223,10 @@ const viewMode = ref("member");
|
||||
const accessRole = ref("guest");
|
||||
const publicRelation = ref("unknown");
|
||||
const publicCanApply = ref(false);
|
||||
const overviewRequestController = createRequestController();
|
||||
let loadGeneration = 0;
|
||||
let pageActive = true;
|
||||
let skipNextShowRefresh = true;
|
||||
const publicActionLabel = computed(() => {
|
||||
if (publicRelation.value === "pending") return "查看申请进度";
|
||||
if (!publicCanApply.value) return "";
|
||||
@@ -249,7 +260,12 @@ const stateCopy = computed(
|
||||
"",
|
||||
);
|
||||
|
||||
const loadGenealogy = (query = {}) => {
|
||||
const formatGenealogyTime = (value) =>
|
||||
typeof value === "string" && value.length >= 10 ? value.slice(0, 10) : value;
|
||||
|
||||
const loadGenealogy = async (query = {}) => {
|
||||
const generation = ++loadGeneration;
|
||||
overviewRequestController.abort();
|
||||
overviewState.value = "loading";
|
||||
loadError.value = "";
|
||||
genealogy.value = null;
|
||||
@@ -273,28 +289,47 @@ const loadGenealogy = (query = {}) => {
|
||||
}
|
||||
if (query.state === "loading") return;
|
||||
|
||||
const fixture = findGenealogyFixture(genealogyId.value);
|
||||
if (!fixture) {
|
||||
overviewState.value = "no-permission";
|
||||
return;
|
||||
try {
|
||||
const result = await appApi.getOverview(genealogyId.value, {
|
||||
requestController: overviewRequestController,
|
||||
});
|
||||
if (!pageActive || generation !== loadGeneration) return;
|
||||
genealogy.value = result;
|
||||
viewMode.value = "member";
|
||||
accessRole.value = result.accessRole;
|
||||
overviewState.value = "ready";
|
||||
} catch (error) {
|
||||
if (
|
||||
!pageActive ||
|
||||
generation !== loadGeneration ||
|
||||
isRequestCancelled(error)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if ([401, 403, 404].includes(error?.httpStatus)) {
|
||||
overviewState.value = "no-permission";
|
||||
return;
|
||||
}
|
||||
loadError.value = error?.message || "家谱概览读取失败,请稍后重试。";
|
||||
overviewState.value = "error";
|
||||
}
|
||||
|
||||
const access = getGenealogyFixtureAccess(genealogyId.value);
|
||||
if (!access.canView) {
|
||||
overviewState.value = "no-permission";
|
||||
return;
|
||||
}
|
||||
genealogy.value = { ...fixture };
|
||||
viewMode.value = access.viewMode;
|
||||
accessRole.value = access.accessRole;
|
||||
publicRelation.value = access.relation;
|
||||
publicCanApply.value = access.canApply;
|
||||
overviewState.value = "ready";
|
||||
};
|
||||
|
||||
const requestBack = () => goBack();
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
onLoad(loadGenealogy);
|
||||
onShow(() => {
|
||||
if (skipNextShowRefresh) {
|
||||
skipNextShowRefresh = false;
|
||||
return;
|
||||
}
|
||||
if (genealogyId.value) loadGenealogy({ genealogyId: genealogyId.value });
|
||||
});
|
||||
onUnload(() => {
|
||||
pageActive = false;
|
||||
loadGeneration += 1;
|
||||
overviewRequestController.abort();
|
||||
});
|
||||
const reloadOverview = () => loadGenealogy({ genealogyId: genealogyId.value });
|
||||
const toGenealogies = () => returnTo("G01", {});
|
||||
const toTree = () =>
|
||||
|
||||
Reference in New Issue
Block a user