完成50%

This commit is contained in:
2026-07-23 08:23:59 +08:00
parent 9b0ad62df4
commit f1edc6b533
218 changed files with 24318 additions and 5514 deletions
+97 -44
View File
@@ -4,13 +4,13 @@
class="edit-member-page"
:class="{
'edit-state--form': editState === 'form',
'edit-state--success': editState === 'success',
'edit-state--preview': editState === 'preview',
'edit-state--error': editState === 'error',
'edit-state--no-permission': editState === 'no-permission',
}"
>
<ModulePageBackground module="tree" />
<view class="edit-member-page__header"><PageHeader title="编辑成员" /></view>
<view class="edit-member-page__header"><PageHeader title="编辑成员" custom-back @back="requestBack" /></view>
<view class="edit-member-panel">
<view v-if="editState === 'form'" class="edit-member-form">
<text class="form-eyebrow">成员档案维护</text>
@@ -45,7 +45,7 @@
<text class="form-note">隐私字段只向本人和具备维护权限的家谱管理员展示</text>
<view class="form-action" @click="saveMember">
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="scaleToFill" />
<text>{{ isSubmitting ? "正在保存…" : "保存资料" }}</text>
<text>{{ isSubmitting ? "正在校验…" : "生成本地预览" }}</text>
</view>
</view>
@@ -68,9 +68,9 @@
cancel-text="继续编辑"
confirm-text="放弃修改"
show-cancel
@close="discardDialogVisible = false"
@cancel="discardDialogVisible = false"
@confirm="discardAndBack"
:close-on-mask="false"
@cancel="cancelDiscard"
@confirm="confirmDiscard"
/>
</view>
</template>
@@ -81,7 +81,13 @@ import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
import AppDialog from "@/components/AppDialog.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { genealogyContext } from "@/utils/genealogy-context.js";
import { findTreeMemberPresentationFixture } from "@/data/mock.js";
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
import {
goBack,
handleBackPress,
runBackGuard,
} from "@/utils/navigation.js";
const editState = ref("form");
const genealogyId = ref("");
@@ -90,54 +96,93 @@ const isSubmitting = ref(false);
const discardDialogVisible = ref(false);
let submitTimer = null;
const memberFixtures = {
101: { id: 101, name: "汤文远", generation: 12, generationName: "文字辈", branch: "主支", birthDate: "1940-03-01", deathDate: "2012-08-16", summary: "一生敦亲睦族,参与整理家族旧谱。" },
102: { id: 102, name: "汤正国", generation: 13, generationName: "正字辈", branch: "长房", birthDate: "1965-05-12", deathDate: "", summary: "负责长房资料核对。" },
103: { id: 103, name: "汤正华", generation: 13, generationName: "正字辈", branch: "二房", birthDate: "1968-09-03", deathDate: "", summary: "资料仍在补充。" },
};
const fallbackMember = { id: "", name: "当前成员", generation: "待确认", generationName: "", branch: "待确认", birthDate: "", deathDate: "", summary: "" };
const originalMember = ref({ ...fallbackMember });
const originalMember = ref(null);
const baseline = ref("");
const editForm = reactive({ name: "", generationName: "", birthDate: "", deathDate: "", summary: "" });
const fieldErrors = reactive({ name: "", dates: "" });
const formSnapshot = computed(() => JSON.stringify(editForm));
const hasValidContext = computed(() =>
Boolean(genealogyId.value && personId.value && originalMember.value),
);
const isDirty = computed(
() => editState.value === "form" && baseline.value && formSnapshot.value !== baseline.value,
() =>
(editState.value === "form" || editState.value === "preview") &&
Boolean(baseline.value) &&
formSnapshot.value !== baseline.value,
);
const resultCopy = computed(() => ({
success: { eyebrow: "成员资料已更新", title: `${editForm.name}档案已保存`, copy: "返回成员档案后可以查看本次修改。", action: "返回成员档案" },
error: { eyebrow: "资料未保存", title: "暂时无法保存成员档案", copy: "当前修改仍保留,可返回表单后重试。", action: "返回修改" },
preview: { eyebrow: "本地流程预览", title: `${editForm.name}资料已通过本地校验`, copy: "尚未提交服务器,返回成员档案后不会显示本次修改。", action: "返回成员档案(不保存)" },
error: hasValidContext.value
? { eyebrow: "资料未保存", title: "暂时无法保存成员档案", copy: "当前修改仍保留,可返回表单后重试。", action: "返回修改" }
: { eyebrow: "成员入口无效", title: "没有找到要编辑的成员", copy: "请从成员档案重新进入,页面不会创建临时成员身份。", action: "返回上一页" },
"no-permission": { eyebrow: "权限不足", title: "当前账号不能编辑这位成员", copy: "本人或具备成员维护权限的家谱管理员才能修改档案。", action: "返回成员档案" },
}[editState.value] || {}));
const discardConfirmation = createDiscardConfirmation((visible) => {
discardDialogVisible.value = visible;
});
const requestDiscardConfirmation = discardConfirmation.request;
const confirmDiscard = discardConfirmation.confirm;
const cancelDiscard = discardConfirmation.cancel;
const loadMember = (id) => {
const member = memberFixtures[id] || { ...fallbackMember, id, name: "待核实成员" };
const member = findTreeMemberPresentationFixture(genealogyId.value, id);
if (!member) return false;
originalMember.value = { ...member };
Object.assign(editForm, {
name: member.name,
generationName: member.generationName,
birthDate: member.birthDate,
deathDate: member.deathDate,
summary: member.summary,
});
if (["privacy", "forbidden"].includes(member.status)) {
Object.assign(editForm, {
name: member.name,
generationName: "",
birthDate: "",
deathDate: "",
summary: "",
});
} else {
Object.assign(editForm, {
name: member.name,
generationName: member.generationName || "",
birthDate: member.birthDate || "",
deathDate: member.deathDate || "",
summary: member.summary || "",
});
}
baseline.value = formSnapshot.value;
return true;
};
onLoad((query) => {
genealogyId.value = query.genealogyId || genealogyContext.getCurrentGenealogyId() || "";
personId.value = query.personId || "";
if (genealogyId.value) genealogyContext.setCurrentGenealogyId(genealogyId.value);
loadMember(personId.value);
editState.value = query.state === "success" ? "success" : query.state === "error" ? "error" : query.state === "no-permission" ? "no-permission" : !personId.value ? "error" : "form";
genealogyId.value = String(query.genealogyId || "");
personId.value = String(query.personId || "");
const loaded = Boolean(genealogyId.value && personId.value) && loadMember(personId.value);
editState.value = !loaded
? "error"
: ["privacy", "forbidden"].includes(originalMember.value.status)
? "no-permission"
: query.state === "preview"
? "preview"
: query.state === "error"
? "error"
: "form";
});
onUnload(() => { if (submitTimer) clearTimeout(submitTimer); });
onBackPress(() => {
if (discardDialogVisible.value) { discardDialogVisible.value = false; return true; }
if (isDirty.value) { discardDialogVisible.value = true; return true; }
return false;
onUnload(() => {
const timer = submitTimer;
submitTimer = null;
if (timer) clearTimeout(timer);
discardConfirmation.dispose();
});
const requestBack = () =>
runBackGuard({
transientOpen: discardDialogVisible.value,
dirty: isDirty.value,
submitting: isSubmitting.value,
"close-transient": cancelDiscard,
"block-submitting": () => true,
"confirm-discard": requestDiscardConfirmation,
});
onBackPress((event) => handleBackPress(event, requestBack));
const clearError = (field) => { fieldErrors[field] = ""; };
const selectDate = (field, event) => {
editForm[field] = event.detail.value || "";
@@ -150,21 +195,29 @@ const validateEditForm = () => {
};
const saveMember = () => {
if (isSubmitting.value || !validateEditForm()) return;
const submitSnapshot = Object.freeze({ ...editForm });
isSubmitting.value = true;
submitTimer = setTimeout(() => {
editState.value = editForm.name.trim() === "失败" ? "error" : "success";
if (editState.value === "success") baseline.value = formSnapshot.value;
const timer = setTimeout(() => {
if (submitTimer !== timer) return;
editState.value = submitSnapshot.name.trim() === "失败" ? "error" : "preview";
isSubmitting.value = false;
submitTimer = null;
}, 280);
submitTimer = timer;
};
const returnToMember = async () => {
const confirmed = isDirty.value
? await requestDiscardConfirmation()
: true;
if (!confirmed) return false;
return goBack();
};
const handleResultAction = () => {
if (editState.value === "error") { editState.value = "form"; return; }
uni.navigateBack();
};
const discardAndBack = () => {
discardDialogVisible.value = false;
uni.navigateBack();
if (editState.value === "error" && hasValidContext.value) {
editState.value = "form";
return;
}
return returnToMember();
};
</script>