Files
jiapuapp/pages/records/r07-ritual-editor.vue
T
2026-07-21 07:53:08 +08:00

228 lines
6.2 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.
<!-- 页面编号R-07用途礼仪创建编辑校验保存与删除确认 -->
<template>
<view class="ritual-editor-page" :class="editorClasses">
<ModulePageBackground module="records" />
<view class="page-header">
<PageHeader :title="mode === 'create' ? '新建礼仪' : '编辑礼仪'" />
</view>
<view class="page-content">
<view v-if="editorState === 'success'" class="state-card">
<text>礼仪活动已保存</text>
<text>时间地点与活动说明已整理完成</text>
<AppButton block label="返回礼仪列表" @click="backToRituals" />
</view>
<view v-else class="form-card">
<text>
{{ mode === "create" ? "安排一次家族礼仪" : "修改活动信息" }}
</text>
<view v-for="field in fields" :key="field.key" class="field-row">
<text>{{ field.label }}</text>
<textarea
v-if="field.long"
v-model="ritualForm[field.key]"
auto-height
:placeholder="`请输入${field.label}`"
/>
<input
v-else
v-model="ritualForm[field.key]"
:placeholder="`请输入${field.label}`"
/>
<text v-if="ritualErrors[field.key]">
{{ ritualErrors[field.key] }}
</text>
</view>
<text v-if="editorState === 'error'" class="save-error">
保存失败请保留内容后重试
</text>
<AppButton
block
:disabled="editorState === 'saving'"
:label="editorState === 'saving' ? '正在保存' : '保存活动'"
@click="saveRitual"
/>
<AppButton
v-if="mode === 'edit'"
type="secondary"
block
label="删除活动"
@click="confirmDelete"
/>
</view>
</view>
<AppDialog
:visible="deleteVisible"
eyebrow="删除确认"
title="删除这项礼仪活动?"
message="删除后将返回礼仪列表。"
confirm-text="确认删除"
cancel-text="保留活动"
show-cancel
@confirm="deleteRitual"
@cancel="deleteVisible = false"
/>
</view>
</template>
<script setup>
import { computed, onUnmounted, reactive, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppDialog from "@/components/AppDialog.vue";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const records = [
{
id: "501",
name: "清明祭祖",
date: "2025-04-04",
place: "汤氏宗祠",
description: "缅怀先祖,凝聚家人,共叙家风传承。",
},
];
const ritualId = ref("");
const mode = ref("create");
const editorState = ref("ready");
const deleteVisible = ref(false);
const forceSaveFailure = ref(false);
const ritualForm = reactive({ name: "", date: "", place: "", description: "" });
const ritualErrors = reactive({
name: "",
date: "",
place: "",
description: "",
});
const fields = [
{ key: "name", label: "活动名称" },
{ key: "date", label: "活动日期" },
{ key: "place", label: "举办地点" },
{ key: "description", label: "活动说明", long: true },
];
let saveTimer = null;
const editorClasses = computed(() => ({
"ritual-editor-state--saving": editorState.value === "saving",
"ritual-editor-state--error": editorState.value === "error",
}));
onLoad((q) => {
ritualId.value = String(q.ritualId || "");
mode.value = q.mode === "edit" ? "edit" : "create";
forceSaveFailure.value = q.saveResult === "error";
const selected = records.find((x) => x.id === ritualId.value);
if (selected) Object.assign(ritualForm, selected);
});
const validateRitual = () => {
for (const f of fields)
ritualErrors[f.key] = String(ritualForm[f.key]).trim()
? ""
: `请填写${f.label}`;
return fields.every((f) => !ritualErrors[f.key]);
};
const saveRitual = () => {
if (editorState.value === "saving" || !validateRitual()) return;
editorState.value = "saving";
saveTimer = setTimeout(() => {
editorState.value = forceSaveFailure.value ? "error" : "success";
forceSaveFailure.value = false;
}, 320);
};
const confirmDelete = () => {
deleteVisible.value = true;
};
const deleteRitual = () => {
deleteVisible.value = false;
backToRituals();
};
const backToRituals = () =>
uni.redirectTo({ url: "/pages/records/r05-ritual-list" });
onUnmounted(() => {
if (saveTimer) clearTimeout(saveTimer);
});
</script>
<style scoped lang="scss">
.ritual-editor-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.page-header,
.page-content {
z-index: 1;
}
.page-content {
padding: 18rpx 24rpx 72rpx;
}
.form-card,
.state-card {
box-sizing: border-box;
padding: 46rpx;
background: url("/static/assets/modules/records/transparent/module-content-frame.png")
center/100% 100% no-repeat;
}
.form-card > text:first-child,
.state-card > text:first-child {
display: block;
color: $ink;
font-size: 34rpx;
font-weight: 700;
}
.field-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: center;
gap: 10rpx 18rpx;
min-height: 82rpx;
margin-top: 14rpx;
padding: 14rpx 24rpx;
box-sizing: border-box;
background: url("/static/assets/modules/records/transparent/module-field-frame.png")
center/100% 100% no-repeat;
}
.field-row > text:first-child {
color: $ink;
font-size: 23rpx;
font-weight: 700;
}
.field-row input,
.field-row textarea {
width: auto;
min-width: 0;
color: $ink;
font-size: 23rpx;
text-align: right;
}
.field-row textarea {
min-height: 76rpx;
line-height: 1.5;
}
.field-row > text:last-child {
grid-column: 1/-1;
color: $brand-red;
font-size: 20rpx;
text-align: right;
}
.form-card .app-button {
margin-top: 18rpx;
}
.save-error {
display: block;
margin-top: 14rpx;
color: $brand-red;
font-size: 22rpx;
}
.state-card {
min-height: 340rpx;
padding-top: 80rpx;
text-align: center;
}
.state-card > text:nth-child(2) {
display: block;
margin-top: 18rpx;
color: $ink-muted;
font-size: 24rpx;
}
.state-card .app-button {
margin-top: 28rpx;
}
</style>