完成70%
This commit is contained in:
@@ -1,478 +1,11 @@
|
||||
<!-- 页面编号:G-10;用途:管理员入谱申请审核与空/失败状态。 -->
|
||||
<!-- 页面编号:G-10;用途:加入申请审核。缺申请列表 DTO/ID 来源时保持关闭。 -->
|
||||
<template>
|
||||
<view class="review-page">
|
||||
<GenealogyPageBackground />
|
||||
<view class="review-page__header">
|
||||
<PageHeader
|
||||
title="入谱审核"
|
||||
action="说明"
|
||||
custom-back
|
||||
@action="showHelp"
|
||||
@back="requestBack"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="review-content"
|
||||
:class="{
|
||||
'review-state--list': reviewState === 'list',
|
||||
'review-state--empty': reviewState === 'empty',
|
||||
'review-state--error': reviewState === 'error',
|
||||
'review-state--no-permission': reviewState === 'no-permission',
|
||||
}"
|
||||
>
|
||||
<view v-if="reviewState !== 'loading'" class="review-intro">
|
||||
<text>核实亲属关系后再决定</text>
|
||||
<text>当前只预览审核交互,不会提交服务器</text>
|
||||
</view>
|
||||
|
||||
<template v-if="reviewState === 'list'">
|
||||
<view
|
||||
v-for="item in applications"
|
||||
:key="item.id"
|
||||
class="application-card"
|
||||
>
|
||||
<view class="application-card__body">
|
||||
<text class="application-card__name">{{ item.name }}</text>
|
||||
<text class="application-card__phone">{{ item.phone }}</text>
|
||||
<text class="application-card__time">{{ item.appliedAt }}</text>
|
||||
<text class="application-card__relation">{{ item.relation }}</text>
|
||||
<view v-if="item.status === 'PENDING'" class="review-actions">
|
||||
<AppButton
|
||||
compact
|
||||
type="secondary"
|
||||
label="预览拒绝"
|
||||
@click="confirmAudit(item, false)"
|
||||
/>
|
||||
<AppButton
|
||||
compact
|
||||
label="预览通过"
|
||||
@click="confirmAudit(item, true)"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="review-result"
|
||||
:class="
|
||||
item.status === 'APPROVED'
|
||||
? 'review-result--approved'
|
||||
: 'review-result--rejected'
|
||||
"
|
||||
>
|
||||
<text class="application-card__status">{{
|
||||
item.status === "APPROVED" ? "已通过" : "已拒绝"
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<AppLoading
|
||||
v-else-if="reviewState === 'loading'"
|
||||
text="正在整理审核申请"
|
||||
description="请稍候,正在读取待审核记录。"
|
||||
/>
|
||||
<view v-else class="review-state-card">
|
||||
<view class="review-state-card__copy">
|
||||
<text>{{ stateTitle }}</text>
|
||||
<text>{{ stateCopy }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<AppButton
|
||||
v-if="reviewState === 'error'"
|
||||
class="review-page__retry"
|
||||
label="重新查看"
|
||||
@click="loadApplications({ genealogyId })"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<AppDialog
|
||||
:visible="!!confirmation || helpVisible"
|
||||
:title="
|
||||
helpVisible
|
||||
? '审核说明'
|
||||
: confirmation?.approved
|
||||
? '预览通过效果?'
|
||||
: '预览拒绝效果?'
|
||||
"
|
||||
:message="
|
||||
helpVisible
|
||||
? '请核对申请人的姓名、亲属关系和补充说明,仅确认与本家谱存在真实关系的申请。'
|
||||
: confirmation?.approved
|
||||
? '当前只更新本页本地预览,不会让申请人成为成员,也不会提交服务器。'
|
||||
: '当前只更新本页本地预览,不会通知申请人,也不会提交服务器。'
|
||||
"
|
||||
:confirm-text="
|
||||
helpVisible
|
||||
? '我知道了'
|
||||
: confirmation?.approved
|
||||
? '查看通过效果'
|
||||
: '查看拒绝效果'
|
||||
"
|
||||
:show-cancel="!!confirmation"
|
||||
compact-actions
|
||||
:close-on-mask="false"
|
||||
@confirm="helpVisible ? closeDialog() : applyAudit()"
|
||||
@cancel="closeDialog"
|
||||
>
|
||||
<view v-if="confirmation && !confirmation.approved" class="rejection-field">
|
||||
<text>拒绝原因</text>
|
||||
<textarea
|
||||
id="g10-rejection-reason"
|
||||
v-model="rejectionReason"
|
||||
auto-height
|
||||
maxlength="120"
|
||||
placeholder="请说明需要补充或核实的信息"
|
||||
:aria-invalid="!!rejectionError"
|
||||
aria-describedby="g10-rejection-error"
|
||||
:focus="rejectionFocused"
|
||||
@input="clearRejectionError"
|
||||
/>
|
||||
<text
|
||||
v-if="rejectionError"
|
||||
id="g10-rejection-error"
|
||||
class="rejection-field__error"
|
||||
role="alert"
|
||||
>{{ rejectionError }}</text
|
||||
>
|
||||
</view>
|
||||
</AppDialog>
|
||||
|
||||
<view v-if="feedbackVisible" class="review-feedback">
|
||||
<text>{{ feedbackMessage }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="review-page"><GenealogyPageBackground /><view class="page-header"><PageHeader title="申请审核" custom-back @back="returnToGenealogies" /></view><view class="page-content"><view class="state-card"><text>申请审核待后端字段合同</text><text>审核 operation 存在,但没有可消费的申请列表 DTO 或稳定申请 ID 来源;页面不再展示 fixture 申请或本地通过/拒绝预览。</text><AppButton block label="返回我的家谱" @click="returnToGenealogies" /></view></view></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { getGenealogyFixtureAccess } from "@/data/mock.js";
|
||||
import { handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const applications = ref([]);
|
||||
const reviewSamples = [
|
||||
{
|
||||
id: "review-1",
|
||||
name: "汤志成",
|
||||
phone: "139****6421",
|
||||
relation: "自述为汤正华堂侄 · 祖居洛阳",
|
||||
appliedAt: "今天 10:24",
|
||||
status: "PENDING",
|
||||
},
|
||||
{
|
||||
id: "review-2",
|
||||
name: "汤雨薇",
|
||||
phone: "136****2798",
|
||||
relation: "自述为汤正国之女 · 已补充长辈姓名",
|
||||
appliedAt: "昨天 18:02",
|
||||
status: "PENDING",
|
||||
},
|
||||
];
|
||||
const genealogyId = ref("");
|
||||
const reviewState = ref("loading");
|
||||
const errorMessage = ref("");
|
||||
const confirmation = ref(null);
|
||||
const rejectionReason = ref("");
|
||||
const rejectionError = ref("");
|
||||
const rejectionFocused = ref(false);
|
||||
const helpVisible = ref(false);
|
||||
const feedbackVisible = ref(false);
|
||||
const feedbackMessage = ref("");
|
||||
let feedbackTimer = null;
|
||||
onUnload(() => {
|
||||
const timer = feedbackTimer;
|
||||
feedbackTimer = null;
|
||||
if (timer) clearTimeout(timer);
|
||||
});
|
||||
const stateTitle = computed(() =>
|
||||
reviewState.value === "empty"
|
||||
? "暂无待审核申请"
|
||||
: reviewState.value === "no-permission"
|
||||
? "当前账号无审核权限"
|
||||
: "审核列表暂时无法读取",
|
||||
);
|
||||
const stateCopy = computed(() =>
|
||||
reviewState.value === "empty"
|
||||
? "新的入谱申请会在这里出现,并同步发送消息提醒。"
|
||||
: reviewState.value === "no-permission"
|
||||
? "只有家谱所有者或具备审核权限的管理员可以处理申请。"
|
||||
: errorMessage.value || "请检查网络或家谱权限后重试。",
|
||||
);
|
||||
|
||||
const loadApplications = (query = {}) => {
|
||||
reviewState.value = "loading";
|
||||
errorMessage.value = "";
|
||||
genealogyId.value = String(query.genealogyId || "");
|
||||
if (!genealogyId.value) {
|
||||
errorMessage.value = "没有找到当前家谱,请从家谱总览进入。";
|
||||
reviewState.value = "error";
|
||||
return;
|
||||
}
|
||||
if (
|
||||
getGenealogyFixtureAccess(genealogyId.value).accessRole !== "owner"
|
||||
) {
|
||||
reviewState.value = "no-permission";
|
||||
return;
|
||||
}
|
||||
if (query.state === "loading") {
|
||||
reviewState.value = "loading";
|
||||
return;
|
||||
}
|
||||
if (query.state === "empty") {
|
||||
reviewState.value = "empty";
|
||||
return;
|
||||
}
|
||||
if (query.state === "error") {
|
||||
reviewState.value = "error";
|
||||
return;
|
||||
}
|
||||
if (query.state === "no-permission") {
|
||||
reviewState.value = "no-permission";
|
||||
return;
|
||||
}
|
||||
applications.value = reviewSamples.map((item) => ({ ...item }));
|
||||
reviewState.value = "list";
|
||||
};
|
||||
|
||||
onLoad(loadApplications);
|
||||
const confirmAudit = (item, approved) => {
|
||||
rejectionReason.value = "";
|
||||
rejectionError.value = "";
|
||||
rejectionFocused.value = false;
|
||||
confirmation.value = { item, approved };
|
||||
};
|
||||
const closeDialog = () => {
|
||||
confirmation.value = null;
|
||||
rejectionReason.value = "";
|
||||
rejectionError.value = "";
|
||||
rejectionFocused.value = false;
|
||||
helpVisible.value = false;
|
||||
};
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
transientOpen: Boolean(confirmation.value) || helpVisible.value,
|
||||
"close-transient": closeDialog,
|
||||
});
|
||||
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
|
||||
const showFeedback = (message) => {
|
||||
feedbackMessage.value = message;
|
||||
feedbackVisible.value = true;
|
||||
if (feedbackTimer) clearTimeout(feedbackTimer);
|
||||
const timer = setTimeout(() => {
|
||||
if (feedbackTimer !== timer) return;
|
||||
feedbackVisible.value = false;
|
||||
feedbackTimer = null;
|
||||
}, 1800);
|
||||
feedbackTimer = timer;
|
||||
};
|
||||
const clearRejectionError = () => {
|
||||
rejectionError.value = "";
|
||||
};
|
||||
const applyAudit = async () => {
|
||||
const current = confirmation.value;
|
||||
if (!current) return;
|
||||
if (!current.approved && !rejectionReason.value.trim()) {
|
||||
rejectionError.value = "请填写拒绝原因,方便申请人补充资料";
|
||||
rejectionFocused.value = false;
|
||||
await nextTick();
|
||||
rejectionFocused.value = true;
|
||||
return;
|
||||
}
|
||||
current.item.status = current.approved ? "APPROVED" : "REJECTED";
|
||||
if (!current.approved) current.item.rejectionReason = rejectionReason.value.trim();
|
||||
closeDialog();
|
||||
showFeedback(
|
||||
`本地审核预览已更新,尚未提交服务器 · ${current.approved ? "已通过" : "已拒绝"}`,
|
||||
);
|
||||
};
|
||||
const showHelp = () => {
|
||||
helpVisible.value = true;
|
||||
};
|
||||
import AppButton from "@/components/AppButton.vue"; import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue"; import PageHeader from "@/components/PageHeader.vue"; import { returnTo } from "@/utils/navigation.js"; const returnToGenealogies=()=>returnTo("G01");
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||||
|
||||
.review-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
background: $paper;
|
||||
}
|
||||
.review-page__header {
|
||||
z-index: 1;
|
||||
}
|
||||
.rejection-field { width: 100%; margin: 20rpx 0; text-align: left; }
|
||||
.rejection-field > text:first-child { display: block; color: $ink; font-size: 23rpx; font-weight: 700; }
|
||||
.rejection-field textarea { @include adaptive.adaptive-genealogy-form-field; width: 100%; min-height: 110rpx; margin-top: 10rpx; padding: 18rpx; color: $ink; font-size: 23rpx; line-height: 1.55; }
|
||||
.rejection-field__error { display: block; margin-top: 8rpx; color: $brand-red; font-size: 21rpx; }
|
||||
.review-content {
|
||||
z-index: 1;
|
||||
padding: 24rpx 24rpx 60rpx;
|
||||
}
|
||||
.review-intro {
|
||||
margin: 0 8rpx 24rpx;
|
||||
padding-bottom: 22rpx;
|
||||
background: url("/static/assets/modules/genealogy/transparent/section-divider.png") bottom center / 100% 14rpx no-repeat;
|
||||
}
|
||||
.review-intro text {
|
||||
display: block;
|
||||
}
|
||||
.review-intro text:first-child {
|
||||
color: $ink;
|
||||
font-family: "STKaiti", "KaiTi", serif;
|
||||
font-size: 29rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.review-intro text:nth-child(2) {
|
||||
margin-top: 8rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.application-card {
|
||||
@include adaptive.adaptive-genealogy-list-card;
|
||||
width: 100%;
|
||||
min-height: 218rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.application-card__body {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-height: 182rpx;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
gap: 8rpx 0;
|
||||
padding: 28rpx 28rpx 22rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.application-card__name {
|
||||
grid-column: 1;
|
||||
color: $ink;
|
||||
font-family: "STKaiti", "KaiTi", serif;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.application-card__phone {
|
||||
grid-column: 2;
|
||||
margin-left: 14rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
.application-card__time {
|
||||
grid-column: 3;
|
||||
justify-self: end;
|
||||
margin-top: 3rpx;
|
||||
color: #998873;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
.application-card__relation {
|
||||
display: block;
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 2;
|
||||
margin-top: 8rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.review-actions {
|
||||
display: flex;
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 3;
|
||||
justify-self: end;
|
||||
gap: 12rpx;
|
||||
}
|
||||
.review-actions .app-button {
|
||||
width: 148rpx;
|
||||
min-height: 68rpx;
|
||||
}
|
||||
.review-result {
|
||||
@include adaptive.adaptive-scroll-button(primary);
|
||||
display: grid;
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 3;
|
||||
justify-self: end;
|
||||
width: 308rpx;
|
||||
min-height: 68rpx;
|
||||
}
|
||||
.application-card__status {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
grid-area: 1 / 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: $brand-red;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.review-result--approved .application-card__status {
|
||||
color: #fff9ed;
|
||||
}
|
||||
.review-result--approved {
|
||||
border-image-source: url("/static/assets/foundation/transparent/a01-scroll-primary-v3.png");
|
||||
}
|
||||
.review-result--rejected .application-card__status {
|
||||
color: $ink;
|
||||
}
|
||||
.review-result--rejected {
|
||||
border-image-source: url("/static/assets/foundation/transparent/a01-scroll-secondary-v3.png");
|
||||
}
|
||||
.review-state-card {
|
||||
@include adaptive.adaptive-genealogy-list-card;
|
||||
width: 100%;
|
||||
min-height: 228rpx;
|
||||
margin-top: 70rpx;
|
||||
}
|
||||
.review-state-card__copy {
|
||||
display: flex;
|
||||
min-height: 228rpx;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 48rpx 12%;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
.review-state-card__copy text:first-child {
|
||||
color: $ink;
|
||||
font-family: "STKaiti", "KaiTi", serif;
|
||||
font-size: 31rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.review-state-card__copy text:last-child {
|
||||
margin-top: 15rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.review-page__retry {
|
||||
width: 420rpx;
|
||||
min-height: 82rpx;
|
||||
margin: 32rpx auto 0;
|
||||
}
|
||||
.review-feedback {
|
||||
@include adaptive.adaptive-feedback-toast;
|
||||
position: fixed;
|
||||
z-index: 40;
|
||||
top: 140rpx;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
min-width: 250rpx;
|
||||
min-height: 76rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 36rpx;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.review-feedback text {
|
||||
color: $ink;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
.review-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{box-sizing:border-box;min-height:340rpx;padding:78rpx 52rpx 50rpx;text-align:center;@include adaptive.adaptive-genealogy-state-panel}.state-card text{display:block}.state-card 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{margin-top:28rpx}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user