Files
jiapuapp/pages/genealogy/g08-join-application.vue
T
2026-07-27 17:30:01 +08:00

276 lines
7.1 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用途 APP GenealogyJoinApplyBody 提交加入申请 -->
<template>
<view class="join-page">
<GenealogyPageBackground />
<view class="page-header"
><PageHeader title="申请加入家谱" custom-back @back="requestBack"
/></view>
<view class="page-content">
<view v-if="!hasValidContext" class="state-card">
<text>申请入口无效</text>
<text>没有取得可申请的家谱标识</text>
<AppButton block label="返回搜索家谱" @click="backToSearch" />
</view>
<view v-else-if="state === 'success'" class="state-card">
<text>申请已提交</text>
<text>服务端已确认本次加入申请可返回继续查看公开家谱</text>
<AppButton block label="返回搜索家谱" @click="backToSearch" />
</view>
<view v-else class="join-form">
<view class="form-heading">
<text>申请加入</text>
<text>{{ genealogyName || "当前家谱" }}</text>
</view>
<view v-for="field in fields" :key="field.key" class="form-field">
<text>{{ field.label }}</text>
<textarea
v-if="field.multiline"
v-model.trim="form[field.key]"
auto-height
:maxlength="field.maxlength"
:placeholder="field.placeholder"
:disabled="state === 'submitting'"
@input="error = ''"
/>
<input
v-else
v-model.trim="form[field.key]"
:type="field.inputType || 'text'"
:maxlength="field.maxlength"
:placeholder="field.placeholder"
:disabled="state === 'submitting'"
@input="error = ''"
/>
</view>
<text v-if="error" class="form-error">{{ error }}</text>
<AppButton
block
:disabled="state === 'submitting'"
:label="state === 'submitting' ? '正在提交' : '提交申请'"
@click="submit"
/>
</view>
</view>
<AppDialog
:visible="discardVisible"
:close-on-mask="false"
eyebrow="放弃确认"
title="放弃当前申请?"
message="当前填写内容尚未提交,离开后会清除。"
confirm-text="确认放弃"
cancel-text="继续填写"
show-cancel
@confirm="confirmDiscard"
@cancel="cancelDiscard"
/>
</view>
</template>
<script setup>
import { computed, onUnmounted, reactive, ref } from "vue";
import { onBackPress, onLoad } 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 { handleBackPress, returnTo, runBackGuard } from "@/utils/navigation.js";
const genealogyId = ref("");
const genealogyName = ref("");
const state = ref("form");
const error = ref("");
const discardVisible = ref(false);
const form = reactive({
applicantName: "",
phone: "",
relationDesc: "",
applyReason: "",
inviterUserId: "",
});
const fields = [
{
key: "applicantName",
label: "申请人姓名",
maxlength: 50,
placeholder: "填写您的姓名",
},
{
key: "phone",
label: "手机号",
inputType: "number",
maxlength: 11,
placeholder: "填写联系电话",
},
{
key: "relationDesc",
label: "关系说明",
maxlength: 100,
placeholder: "例如:本族成员",
},
{
key: "applyReason",
label: "申请理由",
multiline: true,
maxlength: 500,
placeholder: "说明申请加入的原因",
},
{
key: "inviterUserId",
label: "邀请人编号",
inputType: "number",
maxlength: 20,
placeholder: "如有邀请人可填写编号",
},
];
const controller = createRequestController();
const hasValidContext = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
const dirty = computed(() => Object.values(form).some((value) => value.trim()));
const confirmation = createDiscardConfirmation((visible) => {
discardVisible.value = visible;
});
const confirmDiscard = confirmation.confirm;
const cancelDiscard = confirmation.cancel;
onLoad((query) => {
genealogyId.value = String(query?.genealogyId || "");
genealogyName.value = String(query?.genealogyName || "");
});
const backToSearch = () => returnTo("G06");
const submit = async () => {
if (state.value === "submitting" || !hasValidContext.value) return;
state.value = "submitting";
error.value = "";
try {
await appApi.applyToJoin(
genealogyId.value,
{ ...form },
{ requestController: controller },
);
state.value = "success";
} catch (cause) {
if (!isRequestCancelled(cause)) {
state.value = "form";
error.value = cause?.message || "申请提交失败,请稍后重试。";
}
}
};
const requestBack = () =>
runBackGuard({
transientOpen: discardVisible.value,
dirty: dirty.value && state.value === "form",
submitting: state.value === "submitting",
"close-transient": cancelDiscard,
"block-submitting": () => true,
"confirm-discard": confirmation.request,
});
onBackPress((event) => handleBackPress(event, requestBack));
onUnmounted(() => {
controller.abort();
confirmation.dispose();
});
</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;
}
.page-header,
.page-content {
z-index: 1;
}
.page-content {
padding: 18rpx 24rpx 72rpx;
}
.state-card,
.join-form {
@include adaptive.adaptive-genealogy-state-panel;
}
.state-card {
min-height: 340rpx;
padding: 78rpx 52rpx 50rpx;
text-align: center;
}
.state-card text {
display: block;
}
.state-card text:first-child,
.form-heading text:first-child {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 35rpx;
font-weight: 700;
}
.state-card text:nth-child(2) {
margin-top: 18rpx;
color: $ink-muted;
font-size: 24rpx;
line-height: 1.65;
}
.state-card .app-button,
.join-form .app-button {
margin-top: 28rpx;
}
.join-form {
padding: 30rpx;
}
.form-heading {
display: flex;
flex-direction: column;
gap: 8rpx;
padding-bottom: 22rpx;
border-bottom: 1rpx solid rgba(128, 89, 49, 0.24);
}
.form-heading text:last-child {
color: $brand-red;
font-size: 24rpx;
}
.form-field {
margin-top: 22rpx;
}
.form-field > text {
display: block;
color: $ink;
font-size: 24rpx;
font-weight: 700;
}
.form-field input,
.form-field textarea {
@include adaptive.adaptive-genealogy-form-field;
box-sizing: border-box;
display: block;
width: 100%;
margin-top: 10rpx;
padding: 16rpx 22rpx;
color: $ink;
font-size: 24rpx;
}
.form-field input {
min-height: 76rpx;
padding-top: 0;
padding-bottom: 0;
}
.form-field textarea {
min-height: 120rpx;
line-height: 1.55;
}
.form-error {
display: block;
margin-top: 16rpx;
color: $brand-red;
font-size: 22rpx;
line-height: 1.45;
}
</style>