Files
jiapuapp/pages/records/r10-memo-list.vue
T
2026-07-21 07:53:08 +08:00

247 lines
6.5 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-10用途家族备忘列表完成状态与同页新增 -->
<template>
<view class="memo-page" :class="stateClasses">
<ModulePageBackground module="records" />
<view class="page-header">
<PageHeader title="家族备忘" action="新增" @action="createMemo" />
</view>
<view v-if="memoState === 'loading'" class="page-loading">
<AppLoading
text="正在读取家族备忘"
description="请稍候,正在整理待办事项。"
/>
</view>
<view v-else class="page-content">
<template v-if="memoState === 'ready' && memos.length">
<view
v-for="memo in memos"
:key="memo.id"
class="memo-card"
:class="{ 'memo-card--done': memo.done }"
@click="toggleMemo(memo)"
>
<view>
<text>{{ memo.done ? "已完成" : "待办理" }}</text>
<text>{{ memo.due }}</text>
</view>
<text>{{ memo.title }}</text>
<text>{{ memo.description }}</text>
<text>{{ memo.done ? "点击恢复待办" : "点击标记完成" }}</text>
</view>
<AppButton block label="新增备忘" @click="createMemo" />
</template>
<view v-else class="state-card">
<text>
{{ memoState === "error" ? "家族备忘暂不可用" : "还没有备忘" }}
</text>
<text>
{{
memoState === "error"
? "请稍后重新查看,已有备忘不会受到影响。"
: "把需要家人共同记住的事情写在这里。"
}}
</text>
<AppButton
:type="memoState === 'error' ? 'secondary' : 'primary'"
block
:label="memoState === 'error' ? '重新查看' : '新增备忘'"
@click="memoState === 'error' ? (memoState = 'ready') : createMemo()"
/>
</view>
</view>
<AppDialog
:visible="dialogVisible"
eyebrow="家族备忘"
title="新增一项备忘"
confirm-text="保存备忘"
cancel-text="取消"
show-cancel
@confirm="saveMemo"
@cancel="dialogVisible = false"
>
<view class="dialog-form">
<input v-model="memoForm.title" placeholder="备忘标题" />
<input v-model="memoForm.due" placeholder="截止日期或时间" />
<textarea
v-model="memoForm.description"
auto-height
placeholder="补充具体事项"
/>
<text v-if="formError">{{ formError }}</text>
</view>
</AppDialog>
<AppToast :visible="toastVisible" message="备忘已更新" />
</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 AppToast from "@/components/AppToast.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const memos = ref([
{
id: 1,
title: "修谱资料整理",
due: "本月底前",
description: "补充老照片中的人物姓名和拍摄时间。",
done: false,
},
{
id: 2,
title: "重阳敬老活动",
due: "10 月 11 日上午",
description: "在祠堂集合,并确认接送长辈的车辆。",
done: true,
},
]);
const memoState = ref("loading");
const dialogVisible = ref(false);
const toastVisible = ref(false);
const formError = ref("");
const memoForm = reactive({ title: "", due: "", description: "" });
let timer = null;
const stateClasses = computed(() => ({
"memo-state--loading": memoState.value === "loading",
"memo-state--empty": memoState.value === "empty",
"memo-state--error": memoState.value === "error",
}));
onLoad((q) => {
memoState.value = ["loading", "empty", "error"].includes(q.state)
? q.state
: "ready";
});
const showToast = () => {
toastVisible.value = true;
if (timer) clearTimeout(timer);
timer = setTimeout(() => (toastVisible.value = false), 1800);
};
const toggleMemo = (memo) => {
memo.done = !memo.done;
showToast();
};
const createMemo = () => {
Object.assign(memoForm, { title: "", due: "", description: "" });
formError.value = "";
dialogVisible.value = true;
};
const saveMemo = () => {
if (!memoForm.title.trim()) {
formError.value = "请填写备忘标题";
return;
}
memos.value.unshift({ id: Date.now(), ...memoForm, done: false });
memoState.value = "ready";
dialogVisible.value = false;
showToast();
};
onUnmounted(() => {
if (timer) clearTimeout(timer);
});
</script>
<style scoped lang="scss">
.memo-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.page-header,
.page-loading,
.page-content {
z-index: 1;
}
.page-loading {
min-height: calc(100vh - 100rpx);
}
.page-content {
display: flex;
flex-direction: column;
gap: 16rpx;
padding: 18rpx 24rpx 72rpx;
}
.memo-card,
.state-card {
box-sizing: border-box;
padding: 34rpx 46rpx;
background: url("/static/assets/modules/records/transparent/module-content-frame.png")
center/100% 100% no-repeat;
}
.memo-card > view {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 6rpx 18rpx;
color: $brand-red;
font-size: 20rpx;
}
.memo-card > text,
.state-card > text {
display: block;
}
.memo-card > text:nth-child(2) {
margin-top: 8rpx;
color: $ink;
font-size: 31rpx;
font-weight: 700;
}
.memo-card > text:nth-child(3) {
margin-top: 9rpx;
color: $ink-muted;
font-size: 23rpx;
line-height: 1.55;
}
.memo-card > text:last-child {
margin-top: 10rpx;
color: $brand-red;
font-size: 20rpx;
}
.memo-card--done {
opacity: 0.68;
}
.state-card {
min-height: 340rpx;
padding-top: 78rpx;
text-align: center;
}
.state-card > text:first-child {
color: $ink;
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;
}
.dialog-form {
width: 100%;
margin: 18rpx 0;
}
.dialog-form input,
.dialog-form textarea {
width: 100%;
min-height: 68rpx;
margin-top: 9rpx;
padding: 13rpx 20rpx;
box-sizing: border-box;
color: $ink;
font-size: 23rpx;
background: url("/static/assets/modules/records/transparent/module-field-frame.png")
center/100% 100% no-repeat;
}
.dialog-form text {
display: block;
margin-top: 8rpx;
color: $brand-red;
font-size: 20rpx;
}
</style>