修改完成
This commit is contained in:
@@ -1,5 +1,215 @@
|
||||
<!-- 页面编号:T-06;用途:编辑亲属关系、冲突提示与处理。 -->
|
||||
<template><TreeMemberForm kind="relation" /></template>
|
||||
<!-- 页面编号:T-06;用途:选择两位现有成员并校正其家族关系。 -->
|
||||
<template>
|
||||
<view
|
||||
class="relationship-page"
|
||||
:class="{
|
||||
'relationship-state--form': relationshipState === 'form',
|
||||
'relationship-state--success': relationshipState === 'success',
|
||||
'relationship-state--conflict': relationshipState === 'conflict',
|
||||
'relationship-state--error': relationshipState === 'error',
|
||||
}"
|
||||
>
|
||||
<ModulePageBackground module="tree" />
|
||||
<view class="relationship-page__header"><PageHeader title="关系维护" /></view>
|
||||
<view class="relationship-panel">
|
||||
<view v-if="relationshipState === 'form'" class="relationship-form">
|
||||
<text class="form-eyebrow">亲属关系校正</text>
|
||||
<text class="form-title">确认两位成员的家族关系</text>
|
||||
<text class="form-copy">选择成员和关系类型后,先查看对世系的影响,再决定是否保存。</text>
|
||||
|
||||
<picker :range="memberLabels" :value="sourceIndex" @change="selectMember('sourceId', $event)">
|
||||
<view class="form-field form-field--picker"><text>当前成员</text><text>{{ memberName(relationshipForm.sourceId) || "请选择成员" }}</text></view>
|
||||
</picker>
|
||||
<text v-if="fieldErrors.sourceId" class="field-error">{{ fieldErrors.sourceId }}</text>
|
||||
|
||||
<picker :range="memberLabels" :value="targetIndex" @change="selectMember('targetId', $event)">
|
||||
<view class="form-field form-field--picker"><text>关联成员</text><text>{{ memberName(relationshipForm.targetId) || "请选择另一位成员" }}</text></view>
|
||||
</picker>
|
||||
<text v-if="fieldErrors.targetId" class="field-error">{{ fieldErrors.targetId }}</text>
|
||||
|
||||
<picker :range="relationshipOptions" :value="relationshipIndex" @change="selectRelationship">
|
||||
<view class="form-field form-field--picker"><text>关系类型</text><text>{{ relationshipForm.relationship || "请选择关系" }}</text></view>
|
||||
</picker>
|
||||
<text v-if="fieldErrors.relationship" class="field-error">{{ fieldErrors.relationship }}</text>
|
||||
|
||||
<view class="relationship-preview">
|
||||
<text>关系影响预览</text>
|
||||
<text>{{ relationshipPreview }}</text>
|
||||
</view>
|
||||
|
||||
<text class="form-note">父母子女关系会改变世系位置;配偶和兄弟姐妹关系不会自动改写现有父母。</text>
|
||||
<view class="form-action" @click="saveRelationship">
|
||||
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="scaleToFill" />
|
||||
<text>{{ isSubmitting ? "正在校验…" : "校验并保存关系" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="relationship-result">
|
||||
<text class="form-eyebrow">{{ resultCopy.eyebrow }}</text>
|
||||
<text class="form-title">{{ resultCopy.title }}</text>
|
||||
<text class="form-copy">{{ resultCopy.copy }}</text>
|
||||
<view v-if="relationshipState === 'conflict'" class="result-actions">
|
||||
<view class="form-action form-action--secondary" @click="relationshipState = 'form'">
|
||||
<image src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png" mode="scaleToFill" /><text>返回核对</text>
|
||||
</view>
|
||||
<view class="form-action" @click="conflictDialogVisible = true">
|
||||
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="scaleToFill" /><text>查看规则</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="form-action" @click="handleResultAction">
|
||||
<image src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="scaleToFill" />
|
||||
<text>{{ relationshipState === "success" ? "返回世系树" : "重新选择" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<AppDialog
|
||||
:visible="conflictDialogVisible"
|
||||
eyebrow="关系校验规则"
|
||||
title="为什么不能保存这段关系"
|
||||
:message="conflictReason || '同一成员不能成为自己的亲属,也不能形成上下代循环或重复父母关系。'"
|
||||
@confirm="conflictDialogVisible = false"
|
||||
@close="conflictDialogVisible = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TreeMemberForm from "@/components/tree/TreeMemberForm.vue";
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { 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";
|
||||
|
||||
const relationshipState = ref("form");
|
||||
const genealogyId = ref("");
|
||||
const personId = ref("");
|
||||
const isSubmitting = ref(false);
|
||||
const conflictDialogVisible = ref(false);
|
||||
const conflictReason = ref("");
|
||||
let submitTimer = null;
|
||||
|
||||
const memberOptions = [
|
||||
{ id: "101", name: "汤文远", generation: 12, parentId: "" },
|
||||
{ id: "102", name: "汤正国", generation: 13, parentId: "101" },
|
||||
{ id: "103", name: "汤正华", generation: 13, parentId: "101" },
|
||||
{ id: "104", name: "汤凯", generation: 14, parentId: "102" },
|
||||
{ id: "105", name: "汤悦", generation: 14, parentId: "102" },
|
||||
];
|
||||
const relationshipOptions = ["父子(当前成员为父)", "父女(当前成员为父)", "母子(当前成员为母)", "母女(当前成员为母)", "配偶", "兄弟姐妹"];
|
||||
const relationshipForm = reactive({ sourceId: "", targetId: "", relationship: "" });
|
||||
const fieldErrors = reactive({ sourceId: "", targetId: "", relationship: "" });
|
||||
const memberLabels = computed(() => memberOptions.map((item) => `${item.name} · 第 ${item.generation} 世`));
|
||||
const memberById = computed(() => new Map(memberOptions.map((item) => [item.id, item])));
|
||||
const sourceIndex = computed(() => Math.max(0, memberOptions.findIndex((item) => item.id === relationshipForm.sourceId)));
|
||||
const targetIndex = computed(() => Math.max(0, memberOptions.findIndex((item) => item.id === relationshipForm.targetId)));
|
||||
const relationshipIndex = computed(() => Math.max(0, relationshipOptions.indexOf(relationshipForm.relationship)));
|
||||
const memberName = (id) => memberById.value.get(String(id))?.name || "";
|
||||
const relationshipPreview = computed(
|
||||
() => relationshipForm.sourceId && relationshipForm.targetId && relationshipForm.relationship
|
||||
? `${memberName(relationshipForm.sourceId)}将以“${relationshipForm.relationship}”关联${memberName(relationshipForm.targetId)}。`
|
||||
: "完成三项选择后,这里会说明世系位置将如何变化。",
|
||||
);
|
||||
const resultCopy = computed(() => ({
|
||||
success: { eyebrow: "关系已保存", title: "世系关系已经更新", copy: relationshipPreview.value },
|
||||
conflict: { eyebrow: "发现关系冲突", title: "这段关系会造成世系矛盾", copy: conflictReason.value },
|
||||
error: { eyebrow: "关系未保存", title: "暂时无法完成关系调整", copy: "当前选择仍然保留,可返回后重新校验。" },
|
||||
}[relationshipState.value] || {}));
|
||||
|
||||
onLoad((query) => {
|
||||
genealogyId.value = query.genealogyId || genealogyContext.getCurrentGenealogyId() || "";
|
||||
personId.value = query.personId || "";
|
||||
relationshipForm.sourceId = personId.value && memberById.value.has(String(personId.value)) ? String(personId.value) : memberOptions[0].id;
|
||||
if (genealogyId.value) genealogyContext.setCurrentGenealogyId(genealogyId.value);
|
||||
relationshipState.value = query.state === "success" ? "success" : query.state === "conflict" ? "conflict" : query.state === "error" ? "error" : "form";
|
||||
if (relationshipState.value === "conflict") conflictReason.value = "目标成员已经存在父级关系,请先核对原关系。";
|
||||
});
|
||||
onUnload(() => { if (submitTimer) clearTimeout(submitTimer); });
|
||||
|
||||
const clearFieldError = (field) => { fieldErrors[field] = ""; };
|
||||
const selectMember = (field, event) => {
|
||||
relationshipForm[field] = memberOptions[Number(event.detail.value)]?.id || "";
|
||||
clearFieldError(field);
|
||||
};
|
||||
const selectRelationship = (event) => {
|
||||
relationshipForm.relationship = relationshipOptions[Number(event.detail.value)] || "";
|
||||
clearFieldError("relationship");
|
||||
};
|
||||
const isAncestor = (possibleAncestorId, memberId) => {
|
||||
let current = memberById.value.get(String(memberId));
|
||||
const visited = new Set();
|
||||
while (current?.parentId && !visited.has(current.id)) {
|
||||
if (current.parentId === String(possibleAncestorId)) return true;
|
||||
visited.add(current.id);
|
||||
current = memberById.value.get(current.parentId);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const validateRelationship = () => {
|
||||
fieldErrors.sourceId = relationshipForm.sourceId ? "" : "请选择当前成员";
|
||||
fieldErrors.targetId = relationshipForm.targetId ? "" : "请选择关联成员";
|
||||
fieldErrors.relationship = relationshipForm.relationship ? "" : "请选择关系类型";
|
||||
if (fieldErrors.sourceId || fieldErrors.targetId || fieldErrors.relationship) return false;
|
||||
if (relationshipForm.sourceId === relationshipForm.targetId) {
|
||||
conflictReason.value = "同一成员不能与自己建立亲属关系。";
|
||||
return false;
|
||||
}
|
||||
const isParentRelationship = relationshipForm.relationship.includes("当前成员为");
|
||||
const target = memberById.value.get(relationshipForm.targetId);
|
||||
if (isParentRelationship && isAncestor(relationshipForm.targetId, relationshipForm.sourceId)) {
|
||||
conflictReason.value = "保存后会形成上下代循环,请重新选择成员方向。";
|
||||
return false;
|
||||
}
|
||||
if (isParentRelationship && target?.parentId && target.parentId !== relationshipForm.sourceId) {
|
||||
conflictReason.value = `${target.name}已经存在父级成员,不能直接重复建立父母关系。`;
|
||||
return false;
|
||||
}
|
||||
conflictReason.value = "";
|
||||
return true;
|
||||
};
|
||||
const saveRelationship = () => {
|
||||
if (isSubmitting.value) return;
|
||||
if (!validateRelationship()) {
|
||||
if (conflictReason.value) relationshipState.value = "conflict";
|
||||
return;
|
||||
}
|
||||
isSubmitting.value = true;
|
||||
submitTimer = setTimeout(() => {
|
||||
relationshipState.value = relationshipForm.relationship === "失败" ? "error" : "success";
|
||||
isSubmitting.value = false;
|
||||
submitTimer = null;
|
||||
}, 280);
|
||||
};
|
||||
const handleResultAction = () => {
|
||||
if (relationshipState.value === "error") { relationshipState.value = "form"; return; }
|
||||
uni.navigateBack();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.relationship-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
|
||||
.relationship-page__header { z-index: 3; }
|
||||
.relationship-panel { z-index: 2; width: calc(100% - 32rpx); min-height: min(650px, calc((100vw - 16px) * 1.46)); margin: 18rpx auto 28rpx; padding: 7.5% 8%; box-sizing: border-box; background: url("/static/assets/modules/tree/transparent/t01-state-panel.png") center / 100% 100% no-repeat; }
|
||||
.form-eyebrow { display: block; color: $brand-red; font-size: 22rpx; letter-spacing: 3rpx; }
|
||||
.form-title { display: block; margin-top: 10rpx; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 34rpx; font-weight: 700; line-height: 1.35; }
|
||||
.form-copy, .form-note { display: block; margin-top: 10rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.5; }
|
||||
.form-field { display: grid; grid-template-columns: auto minmax(0, 1fr); min-height: 78rpx; align-items: center; gap: 20rpx; margin-top: 14rpx; padding: 12rpx 22rpx; box-sizing: border-box; background: url("/static/assets/modules/tree/transparent/t07-search-input-frame.png") center / 100% 100% no-repeat; }
|
||||
.form-field text:first-child { color: $ink; font-size: 24rpx; font-weight: 700; }
|
||||
.form-field text:last-child { min-width: 0; color: $ink; font-size: 24rpx; line-height: 1.45; text-align: right; }
|
||||
.field-error { display: block; margin: 5rpx 18rpx 0; color: $brand-red; font-size: 22rpx; line-height: 32rpx; }
|
||||
.relationship-preview { margin-top: 18rpx; padding: 20rpx 22rpx; background: url("/static/assets/modules/genealogy/transparent/list-slip-frame.png") center / 100% 100% no-repeat; }
|
||||
.relationship-preview text { display: block; color: $ink-muted; font-size: 23rpx; line-height: 1.5; }
|
||||
.relationship-preview text:first-child { color: $brand-red; font-weight: 700; }
|
||||
.form-note { text-align: center; }
|
||||
.form-action { display: grid; width: 100%; min-height: 76rpx; margin-top: 18rpx; }
|
||||
.form-action image, .form-action text { grid-area: 1 / 1; width: 100%; height: 100%; }
|
||||
.form-action text { z-index: 1; display: flex; align-items: center; justify-content: center; color: #fff9ed; font-size: 24rpx; font-weight: 700; }
|
||||
.form-action--secondary text { color: $ink; }
|
||||
.relationship-result { margin-top: 28%; text-align: center; }
|
||||
.relationship-result .form-eyebrow, .relationship-result .form-copy { text-align: center; }
|
||||
.relationship-result > .form-action { width: 420rpx; max-width: 100%; margin-right: auto; margin-left: auto; }
|
||||
.result-actions { display: flex; gap: 14rpx; margin-top: 24rpx; }
|
||||
.result-actions .form-action { width: calc(50% - 7rpx); margin-top: 0; }
|
||||
@media (min-width: 400px) { .relationship-panel { width: calc(100% - 48rpx); } }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user