Files
jiapuapp/pages/genealogy/g08-join-application.vue
T
2026-07-21 20:59:10 +08:00

379 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 页面编号G-08用途申请加入家谱与提交成功/失败状态 -->
<template>
<view class="join-page">
<GenealogyPageBackground />
<view class="join-page__header"
><PageHeader :title="sourceContract.headerTitle"
/></view>
<view
class="join-panel"
:class="{
'join-state--form': joinState === 'form',
'join-state--success': joinState === 'success',
'join-state--error': joinState === 'error',
}"
>
<view v-if="joinState === 'form'" class="join-form">
<text class="join-form__eyebrow">{{ sourceContract.eyebrow }}</text>
<text class="join-form__title"
>{{ sourceContract.formTitle }} {{ genealogyName }}</text
>
<text class="join-form__copy">{{ sourceContract.formCopy }}</text>
<text v-if="previousNotice" class="join-form__previous">{{
previousNotice
}}</text>
<view class="join-field">
<text>真实姓名</text
><input
v-model="form.realName"
placeholder="请输入真实姓名"
placeholder-class="join-placeholder"
@input="clearFieldError('realName')"
/>
</view>
<text v-if="fieldErrors.realName" class="join-field-error">{{
fieldErrors.realName
}}</text>
<view class="join-field">
<text>与家谱关系</text
><input
v-model="form.relation"
placeholder="例如:某某某堂侄"
placeholder-class="join-placeholder"
@input="clearFieldError('relation')"
/>
</view>
<text v-if="fieldErrors.relation" class="join-field-error">{{
fieldErrors.relation
}}</text>
<text class="relation-help"
>请以家谱中一位已知长辈为参照例如某某某堂侄</text
>
<view class="join-field join-field--message">
<text>{{ sourceContract.thirdFieldLabel }}</text
><textarea
v-model="form.message"
auto-height
maxlength="80"
:placeholder="sourceContract.thirdFieldPlaceholder"
placeholder-class="join-placeholder"
/>
</view>
<text class="join-form__note">{{ sourceContract.note }}</text>
<view class="join-action" @click="submitJoin">
<text>{{
isSubmitting
? sourceContract.submittingLabel
: sourceContract.submitLabel
}}</text>
</view>
</view>
<view v-else class="join-result">
<text class="join-result__eyebrow">{{
joinState === "success"
? sourceContract.successEyebrow
: sourceContract.errorEyebrow
}}</text>
<text class="join-result__title">{{
joinState === "success"
? sourceContract.successTitle
: sourceContract.errorTitle
}}</text>
<text class="join-result__copy">{{ resultCopy }}</text>
<view
class="join-action"
@click="joinState === 'success' ? completeFlow() : retryForm()"
>
<text>{{
joinState === "success" ? sourceContract.nextLabel : "重新填写"
}}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const genealogyId = ref("");
const source = ref("search");
const previousRelation = ref("");
const genealogyName = ref("这部家谱");
const genealogyPreview = {
1001: "汤氏家谱",
1002: "汝南汤氏家谱",
};
const joinState = ref("form");
const isSubmitting = ref(false);
const errorMessage = ref("");
const form = reactive({ realName: "", relation: "", message: "" });
const fieldErrors = reactive({ realName: "", relation: "" });
const previousNotice = computed(() =>
previousRelation.value === "rejected"
? "上次申请未通过,请补充更准确的长辈姓名、祖居地或支系信息。"
: previousRelation.value === "removed"
? "你曾退出或被移出这部家谱,请重新确认身份关系后申请。"
: previousRelation.value === "withdrawn"
? "上次申请已撤回;如仍希望加入,请重新确认关系并提交。"
: "",
);
const sourceContract = computed(() =>
source.value === "invite"
? {
headerTitle: "确认关系并加入",
eyebrow: "邀请码直接加入",
formTitle: "确认加入",
formCopy:
"请填写真实身份和亲属关系;提交后直接加入,无需等待管理员审核。",
thirdFieldLabel: "补充信息",
thirdFieldPlaceholder: "选填:补充祖居地、长辈姓名等信息",
note: "邀请码来源提交后直接加入,无需等待审核。",
submitLabel: "确认加入",
submittingLabel: "正在加入…",
successEyebrow: "已加入家谱",
successTitle: "关系信息已提交",
errorEyebrow: "加入未完成",
errorTitle: "暂时无法加入家谱",
nextLabel: "返回我的家谱",
successCopy: `已直接加入“${genealogyName.value}”,无需等待审核;返回后将刷新并选中这部家谱。`,
}
: {
headerTitle: "申请加入家谱",
eyebrow: "公开家谱入谱申请",
formTitle: "申请加入",
formCopy: "请填写真实身份和亲属关系,管理员审核后会通过消息告知结果。",
thirdFieldLabel: "申请说明",
thirdFieldPlaceholder: "补充祖居地、长辈姓名等核验信息",
note: "提交后可在“我的申请”中查看审核进度。",
submitLabel: "提交申请",
submittingLabel: "正在提交…",
successEyebrow: "申请已送达",
successTitle: "等待管理员核实亲属关系",
errorEyebrow: "申请未提交",
errorTitle: "暂时无法提交申请",
nextLabel: "查看我的申请",
successCopy: `“${genealogyName.value}”的管理员会在核实后给出结果,请留意消息中心。`,
},
);
const resultCopy = computed(() =>
joinState.value === "success"
? sourceContract.value.successCopy
: errorMessage.value ||
"请检查网络后重新填写;未成功提交的内容不会进入审核列表。",
);
onLoad((query) => {
genealogyId.value = query.genealogyId || "";
source.value = query.source === "invite" ? "invite" : "search";
previousRelation.value = ["rejected", "removed", "withdrawn"].includes(query.previous)
? query.previous
: "";
if (query.state === "success") {
joinState.value = "success";
return;
}
if (!genealogyId.value) {
errorMessage.value = "没有找到要申请加入的家谱,请先返回公开家谱检索。";
joinState.value = "error";
return;
}
genealogyName.value = query.genealogyName
? decodeURIComponent(query.genealogyName)
: genealogyPreview[genealogyId.value] || "这部家谱";
});
const submitJoin = () => {
if (isSubmitting.value) return;
fieldErrors.realName = form.realName.trim() ? "" : "请填写真实姓名";
fieldErrors.relation = form.relation.trim() ? "" : "请填写与家谱的关系";
if (fieldErrors.realName || fieldErrors.relation) return;
isSubmitting.value = true;
setTimeout(() => {
joinState.value = form.realName.trim() === "失败" ? "error" : "success";
if (joinState.value === "error")
errorMessage.value =
source.value === "invite"
? "邀请码加入暂未完成,请稍后重试。"
: "申请暂未提交,请稍后重试。";
isSubmitting.value = false;
}, 280);
};
const clearFieldError = (field) => {
fieldErrors[field] = "";
};
const retryForm = () => {
joinState.value = "form";
errorMessage.value = "";
};
const toMyApplications = () =>
uni.redirectTo({ url: "/pages/genealogy/g09-my-applications" });
const toMyGenealogies = () =>
uni.reLaunch({
url: `/pages/genealogy/g01-my-genealogies?genealogyId=${genealogyId.value}`,
});
const completeFlow = () =>
source.value === "invite" ? toMyGenealogies() : toMyApplications();
</script>
<style scoped lang="scss">
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
.join-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.join-page__header {
z-index: 3;
}
.join-panel {
@include adaptive.adaptive-genealogy-state-panel;
z-index: 2;
width: calc(100% - 32rpx);
min-height: min(590px, calc((100vw - 16px) * 1.337));
margin: 22rpx auto 0;
padding: 70rpx 8%;
box-sizing: border-box;
}
.join-form {
min-width: 0;
}
.join-form__eyebrow,
.join-result__eyebrow {
display: block;
color: $brand-red;
font-size: 23rpx;
letter-spacing: 3rpx;
}
.join-form__title,
.join-result__title {
display: block;
margin-top: 12rpx;
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 36rpx;
font-weight: 700;
}
.join-form__copy,
.join-result__copy {
display: block;
margin-top: 12rpx;
color: $ink-muted;
font-size: 22rpx;
line-height: 1.6;
}
.join-field {
@include adaptive.adaptive-genealogy-form-field;
display: grid;
min-height: 92rpx;
margin-top: 18rpx;
}
.join-form__previous {
display: block;
margin-top: 14rpx;
color: $brand-red;
font-size: 22rpx;
line-height: 1.55;
}
.join-field > text {
z-index: 1;
grid-area: 1 / 1;
align-self: center;
margin-left: 24rpx;
color: $ink;
font-size: 23rpx;
font-weight: 700;
}
.join-field input,
.join-field textarea {
z-index: 1;
grid-area: 1 / 1;
width: auto;
min-width: 0;
min-height: 92rpx;
margin-right: 20rpx;
margin-left: 170rpx;
color: $ink;
font-size: 23rpx;
line-height: 92rpx;
}
.join-field textarea {
box-sizing: border-box;
padding-top: 26rpx;
line-height: 1.5;
}
.join-placeholder {
color: #a79884;
}
.join-field-error {
display: block;
margin-top: 3rpx;
color: $brand-red;
font-size: 24rpx;
line-height: 34rpx;
text-align: right;
}
.relation-help {
display: block;
margin-top: 5rpx;
color: #8e7b67;
font-size: 19rpx;
line-height: 29rpx;
}
.join-form__note {
display: block;
margin-top: 18rpx;
color: $ink-muted;
font-size: 21rpx;
text-align: center;
}
.join-action {
display: flex;
width: 100%;
min-height: 82rpx;
align-items: center;
justify-content: center;
margin-top: 20rpx;
background: url("/static/assets/foundation/transparent/a01-scroll-primary-v3.png")
center / contain no-repeat;
}
.join-action text {
z-index: 1;
color: #fff9ed;
font-size: 27rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.join-result {
text-align: center;
}
.join-state--success,
.join-state--error {
padding: 170rpx 12% 70rpx;
}
.join-result__eyebrow {
text-align: center;
}
.join-result__copy {
margin-top: 22rpx;
}
.join-result .join-action {
width: 420rpx;
max-width: 100%;
margin: 34rpx auto 0;
}
@media (min-width: 400px) {
.join-panel {
width: calc(100% - 48rpx);
}
}
</style>