379 lines
11 KiB
Vue
379 lines
11 KiB
Vue
<!-- 页面编号: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>
|