完成80%
This commit is contained in:
@@ -259,6 +259,7 @@
|
||||
<view class="add-dialog__heading">
|
||||
<text class="dialog-title">添加家谱</text>
|
||||
<text class="dialog-copy">建议先搜索已有家谱,避免重复创建</text>
|
||||
<text v-if="quota" class="dialog-copy">{{ quota.createRemaining === -1 ? '当前可继续创建家谱' : `还可创建 ${quota.createRemaining} 部家谱` }}</text>
|
||||
<view
|
||||
class="add-dialog__close"
|
||||
role="button"
|
||||
@@ -285,6 +286,7 @@
|
||||
block
|
||||
type="secondary"
|
||||
label="继续创建家谱"
|
||||
:disabled="quota?.canCreate === false"
|
||||
@click="createGenealogy"
|
||||
/>
|
||||
</view>
|
||||
@@ -380,8 +382,14 @@ const switcherVisible = ref(false);
|
||||
const selectedGenealogyId = ref(null);
|
||||
const listScrollCommand = ref(0);
|
||||
const currentListScrollTop = ref(0);
|
||||
const unreadCount = ref(0);
|
||||
const quota = ref(null);
|
||||
const listRequestController = createRequestController();
|
||||
const unreadRequestController = createRequestController();
|
||||
const quotaRequestController = createRequestController();
|
||||
let loadGeneration = 0;
|
||||
let unreadLoadGeneration = 0;
|
||||
let quotaLoadGeneration = 0;
|
||||
let pageActive = true;
|
||||
let presentationState = "default";
|
||||
let skipNextShowRefresh = true;
|
||||
@@ -454,11 +462,39 @@ const loadGenealogies = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadUnreadCount = async () => {
|
||||
const generation = ++unreadLoadGeneration;
|
||||
unreadRequestController.abort();
|
||||
try {
|
||||
const count = await appApi.getUnreadNotificationCount({
|
||||
requestController: unreadRequestController,
|
||||
});
|
||||
if (pageActive && generation === unreadLoadGeneration) unreadCount.value = count;
|
||||
} catch (error) {
|
||||
if (!isRequestCancelled(error)) unreadCount.value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const loadQuota = async () => {
|
||||
const generation = ++quotaLoadGeneration;
|
||||
quotaRequestController.abort();
|
||||
try {
|
||||
const result = await appApi.getGenealogyQuota({
|
||||
requestController: quotaRequestController,
|
||||
});
|
||||
if (pageActive && generation === quotaLoadGeneration) quota.value = result;
|
||||
} catch (error) {
|
||||
if (!isRequestCancelled(error) && pageActive && generation === quotaLoadGeneration) quota.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
onLoad((query) => {
|
||||
syncEmptyStateFromRoute(query);
|
||||
requestedGenealogyId.value = String(query?.genealogyId || "");
|
||||
if (presentationState === "default") {
|
||||
loadGenealogies();
|
||||
loadUnreadCount();
|
||||
loadQuota();
|
||||
} else {
|
||||
reconcilePageGenealogyContext();
|
||||
}
|
||||
@@ -480,15 +516,20 @@ onShow(() => {
|
||||
return;
|
||||
}
|
||||
if (presentationState === "default") loadGenealogies();
|
||||
if (presentationState === "default") loadUnreadCount();
|
||||
if (presentationState === "default") loadQuota();
|
||||
});
|
||||
|
||||
onUnload(() => {
|
||||
pageActive = false;
|
||||
loadGeneration += 1;
|
||||
unreadLoadGeneration += 1;
|
||||
quotaLoadGeneration += 1;
|
||||
listRequestController.abort();
|
||||
unreadRequestController.abort();
|
||||
quotaRequestController.abort();
|
||||
});
|
||||
|
||||
const unreadCount = computed(() => 0);
|
||||
const hasGenealogies = computed(
|
||||
() => !forceEmptyState.value && list.value.length > 0,
|
||||
);
|
||||
@@ -725,11 +766,16 @@ const openShortcut = (key) => {
|
||||
align-items: center;
|
||||
}
|
||||
.current-name {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
color: $ink;
|
||||
font-family: "STKaiti", "KaiTi", serif;
|
||||
font-size: 50rpx;
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.current-switch-copy {
|
||||
flex: 0 0 auto;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<view class="create-page">
|
||||
<GenealogyPageBackground />
|
||||
<PageHeader title="创建家谱" custom-back @back="backToGenealogies" />
|
||||
<PageHeader title="创建家谱" custom-back @back="requestBack" />
|
||||
|
||||
<view class="page-content">
|
||||
<view class="create-card">
|
||||
@@ -111,19 +111,34 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<AppDialog
|
||||
:visible="discardDialogVisible"
|
||||
eyebrow="尚未保存"
|
||||
title="要放弃本次创建吗"
|
||||
message="返回后,本次填写的家谱信息不会保留。"
|
||||
cancel-text="继续填写"
|
||||
confirm-text="放弃并返回"
|
||||
show-cancel
|
||||
:close-on-mask="false"
|
||||
@cancel="cancelDiscard"
|
||||
@confirm="confirmDiscard"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { onUnload } from "@dcloudio/uni-app";
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { onBackPress, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
|
||||
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
|
||||
import { GENEALOGY_ACCESS_PRESET, GENEALOGY_ACCESS_PRESET_OPTIONS, toApiGenealogyAccess } from "@/utils/genealogy-contracts.js";
|
||||
import { isImagePickCancelled, pickAndUploadImage, toConsumerOssId } from "@/utils/resumable-image-upload.js";
|
||||
import { finishPage, returnTo } from "@/utils/navigation.js";
|
||||
import { finishPage, handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const form = reactive({
|
||||
surname: "",
|
||||
@@ -150,8 +165,22 @@ const regionPickerIndexes = ref([0]);
|
||||
const regionPickerError = ref("");
|
||||
const regionLoading = ref(false);
|
||||
const regionPickerOpen = ref(false);
|
||||
const discardDialogVisible = ref(false);
|
||||
const regionPickerIndicatorStyle = "height: 104rpx; border-top: 1px solid rgba(159, 23, 15, .46); border-bottom: 1px solid rgba(159, 23, 15, .46); background: rgba(159, 23, 15, .08);";
|
||||
let pageActive = true;
|
||||
const hasDraft = computed(() =>
|
||||
Object.entries(form).some(([key, value]) =>
|
||||
key === "accessPreset"
|
||||
? value !== GENEALOGY_ACCESS_PRESET.MEMBER_ONLY
|
||||
: String(value).trim(),
|
||||
) || Boolean(selectedRegion.value?.regionCode) || Boolean(coverOssId.value),
|
||||
);
|
||||
const discardConfirmation = createDiscardConfirmation((visible) => {
|
||||
discardDialogVisible.value = visible;
|
||||
});
|
||||
const requestDiscardConfirmation = discardConfirmation.request;
|
||||
const confirmDiscard = discardConfirmation.confirm;
|
||||
const cancelDiscard = discardConfirmation.cancel;
|
||||
|
||||
const clearFieldError = (field) => {
|
||||
fieldErrors[field] = "";
|
||||
@@ -259,9 +288,17 @@ onMounted(() => {
|
||||
loadRegionRoot();
|
||||
});
|
||||
|
||||
const backToGenealogies = () => {
|
||||
if (!isSubmitting.value && !isUploading.value) return returnTo("G01");
|
||||
};
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
transientOpen: regionPickerOpen.value || discardDialogVisible.value,
|
||||
dirty: hasDraft.value,
|
||||
submitting: isSubmitting.value || isUploading.value,
|
||||
"close-transient": () => regionPickerOpen.value ? closeRegionPicker() : cancelDiscard(),
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": requestDiscardConfirmation,
|
||||
});
|
||||
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
|
||||
const uploadCover = async () => {
|
||||
if (isUploading.value || isSubmitting.value) return;
|
||||
@@ -321,6 +358,7 @@ onUnload(() => {
|
||||
pageActive = false;
|
||||
createController.abort();
|
||||
regionController.abort();
|
||||
discardConfirmation.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -381,10 +381,10 @@ const applyToJoin = () => {
|
||||
}
|
||||
.overview-ready {
|
||||
display: flex;
|
||||
min-height: min(483px, calc(100vw * 1.3422));
|
||||
min-height: min(540px, calc(100vw * 1.5));
|
||||
flex-direction: column;
|
||||
gap: 34rpx;
|
||||
padding: 58rpx 0 42rpx;
|
||||
padding: 10rpx 0 42rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.overview-hero {
|
||||
@@ -410,14 +410,21 @@ const applyToJoin = () => {
|
||||
margin-top: 6rpx;
|
||||
color: #e9dcc1;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.55;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
.overview-hero__stats {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
gap: 18rpx;
|
||||
margin-top: 12rpx;
|
||||
color: #dbc990;
|
||||
font-size: 22rpx;
|
||||
transform: translateY(-18rpx);
|
||||
}
|
||||
.overview-hero__stats + .overview-hero__stats { margin-left: 18rpx; }
|
||||
.overview-actions {
|
||||
display: grid;
|
||||
min-height: 386rpx;
|
||||
@@ -543,7 +550,7 @@ const applyToJoin = () => {
|
||||
}
|
||||
.overview-public {
|
||||
display: flex;
|
||||
min-height: min(483px, calc(100vw * 1.3422));
|
||||
min-height: min(540px, calc(100vw * 1.5));
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
padding: 58rpx 0 28rpx;
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
</view>
|
||||
|
||||
<view class="poem-panel" :class="`poem-state--${poemState}`">
|
||||
<AppLoading v-if="poemState === 'loading'" text="正在读取字辈诗" description="正在读取后端已声明的世代、字辈和状态字段。" />
|
||||
<AppLoading v-if="poemState === 'loading'" text="正在读取字辈诗" description="正在整理家谱的传承字序。" />
|
||||
|
||||
<view v-else-if="poemState === 'list'" class="poem-list">
|
||||
<text class="poem-list__eyebrow">{{ genealogyName }} · 传承字序</text>
|
||||
<text class="poem-list__title">{{ generationRangeTitle }}</text>
|
||||
<text class="poem-list__copy">接口未声明“当前世代”字段,页面只按后端返回的世代、文字和状态展示。</text>
|
||||
<text class="poem-list__copy">按已记录的世代和字辈展示传承字序。</text>
|
||||
<view class="poem-rows">
|
||||
<view v-for="item in visiblePoemRows" :key="item.poemId" class="poem-row" :class="{ 'poem-row--disabled': item.status === GENERATION_POEM_STATUS.DISABLED }">
|
||||
<text class="poem-row__number">第 {{ item.generationNo }} 世</text>
|
||||
@@ -136,8 +136,8 @@ const previewSummary = computed(() => {
|
||||
return `服务端预览:新增 ${preview.value.createCount} 条,更新 ${preview.value.updateCount} 条,保留 ${preview.value.keepCount} 条,停用 ${preview.value.disableCount} 条。`;
|
||||
});
|
||||
const stateCopy = computed(() => poemState.value === "empty"
|
||||
? "正常字辈读取未返回条目。维护列表由后端单独鉴权,点击后才会判断是否可维护。"
|
||||
: "未取得可消费的服务端响应,页面不会用本地字辈或本地权限替代。",
|
||||
? "当前尚未添加可用的字辈,可进入维护页面查看。"
|
||||
: "暂未读取到可展示的字辈,请稍后重试。",
|
||||
);
|
||||
const isDirty = computed(() => Boolean(
|
||||
poemState.value === "edit" && editorSnapshot.value && (
|
||||
|
||||
Reference in New Issue
Block a user