修改完成待测试
This commit is contained in:
@@ -30,12 +30,32 @@
|
||||
><AppButton block label="新建礼仪活动" @click="createCeremony"
|
||||
/></view>
|
||||
<view v-else class="ceremony-list"
|
||||
><view
|
||||
><BatchManagementBar
|
||||
v-if="deletableCeremonies.length"
|
||||
resource-name="礼仪活动"
|
||||
:active="ceremonyBatch.selectionMode.value"
|
||||
:selected-count="ceremonyBatch.selectedCount.value"
|
||||
:all-selected="ceremonyBatch.allSelected.value"
|
||||
:busy="ceremonyBatch.deleting.value"
|
||||
@start="ceremonyBatch.enterSelectionMode"
|
||||
@finish="ceremonyBatch.exitSelectionMode"
|
||||
@toggle-all="ceremonyBatch.toggleAll"
|
||||
@delete="ceremonyBatch.requestDelete"
|
||||
/>
|
||||
<text v-if="ceremonyBatch.notice.value" class="batch-notice" role="status">{{ ceremonyBatch.notice.value }}</text>
|
||||
<text v-if="ceremonyBatch.error.value" class="batch-error" role="alert">{{ ceremonyBatch.error.value }}</text>
|
||||
<view
|
||||
v-for="item in ceremonies"
|
||||
:key="item.id"
|
||||
class="ceremony-card"
|
||||
@click="openCeremony(item)"
|
||||
><image
|
||||
@click="handleCeremonyClick(item)"
|
||||
><BatchSelectionMark
|
||||
v-if="ceremonyBatch.selectionMode.value && item.canDelete"
|
||||
:selected="ceremonyBatch.isSelected(item)"
|
||||
:label="`礼仪活动:${item.title}`"
|
||||
@toggle="ceremonyBatch.toggleSelection(item)"
|
||||
/>
|
||||
<image
|
||||
v-if="item.coverFile?.accessUrl"
|
||||
class="ceremony-card__cover"
|
||||
:src="item.coverFile.accessUrl"
|
||||
@@ -50,6 +70,18 @@
|
||||
></view
|
||||
>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="ceremonyBatch.confirmationVisible.value"
|
||||
:close-on-mask="false"
|
||||
eyebrow="批量删除"
|
||||
title="将选中的礼仪活动移入回收站?"
|
||||
:message="ceremonyBatch.confirmationMessage.value"
|
||||
:confirm-text="ceremonyBatch.deleting.value ? '正在删除' : '移入回收站'"
|
||||
cancel-text="继续选择"
|
||||
show-cancel
|
||||
@confirm="ceremonyBatch.confirmDelete"
|
||||
@cancel="ceremonyBatch.cancelDelete"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -57,7 +89,10 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import BatchManagementBar from "@/components/BatchManagementBar.vue";
|
||||
import BatchSelectionMark from "@/components/BatchSelectionMark.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import {
|
||||
@@ -65,6 +100,7 @@ import {
|
||||
isRequestCancelled
|
||||
} from "@/services/api/request-controller.js";
|
||||
import { ceremonyApi } from "@/services/api/ceremony-service.js";
|
||||
import { useBatchDeletion } from "@/composables/use-batch-deletion.js";
|
||||
import { formatMinuteTimestamp } from "@/utils/display-time.js";
|
||||
import { goBack, openPage, returnTo } from "@/utils/navigation/gateway.js";
|
||||
|
||||
@@ -72,8 +108,22 @@ const genealogyId = ref("");
|
||||
const ceremonies = ref([]);
|
||||
const ceremonyListState = ref("loading");
|
||||
const ceremonyListRequestController = createRequestController();
|
||||
const ceremonyDeleteRequestController = createRequestController();
|
||||
let pageActive = true;
|
||||
const valid = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
|
||||
const ceremonyBatch = useBatchDeletion({
|
||||
items: ceremonies,
|
||||
deleteOne: (ceremony) =>
|
||||
ceremonyApi.deleteCeremony(genealogyId.value, ceremony.id, {
|
||||
requestController: ceremonyDeleteRequestController,
|
||||
}),
|
||||
resourceName: "礼仪活动",
|
||||
isActive: () => pageActive,
|
||||
onEmpty: () => {
|
||||
ceremonyListState.value = "empty";
|
||||
},
|
||||
});
|
||||
const deletableCeremonies = ceremonyBatch.deletableItems;
|
||||
const loadCeremonies = async () => {
|
||||
if (!valid.value) return;
|
||||
ceremonyListRequestController.abort();
|
||||
@@ -84,6 +134,7 @@ const loadCeremonies = async () => {
|
||||
});
|
||||
if (!pageActive) return;
|
||||
ceremonies.value = rows;
|
||||
ceremonyBatch.exitSelectionMode();
|
||||
ceremonyListState.value = ceremonies.value.length ? "ready" : "empty";
|
||||
} catch (error) {
|
||||
if (!pageActive || isRequestCancelled(error)) return;
|
||||
@@ -91,7 +142,7 @@ const loadCeremonies = async () => {
|
||||
}
|
||||
};
|
||||
const returnToFamily = () =>
|
||||
valid.value ? returnTo("F01", { genealogyId: genealogyId.value }) : goBack();
|
||||
valid.value ? returnTo("G05", { genealogyId: genealogyId.value }) : goBack();
|
||||
const createCeremony = () =>
|
||||
valid.value
|
||||
? openPage("R07", { genealogyId: genealogyId.value, mode: "create" }, "R05")
|
||||
@@ -102,6 +153,13 @@ const openCeremony = (item) =>
|
||||
{ genealogyId: genealogyId.value, ceremonyId: item.id },
|
||||
"R05",
|
||||
);
|
||||
const handleCeremonyClick = (item) => {
|
||||
if (ceremonyBatch.selectionMode.value && item?.canDelete) {
|
||||
ceremonyBatch.toggleSelection(item);
|
||||
return;
|
||||
}
|
||||
openCeremony(item);
|
||||
};
|
||||
onLoad((query) => {
|
||||
genealogyId.value = String(query?.genealogyId || "");
|
||||
if (valid.value) loadCeremonies();
|
||||
@@ -112,6 +170,7 @@ onShow(() => {
|
||||
onUnload(() => {
|
||||
pageActive = false;
|
||||
ceremonyListRequestController.abort();
|
||||
ceremonyDeleteRequestController.abort();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -156,6 +215,14 @@ onUnload(() => {
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
.batch-notice,
|
||||
.batch-error {
|
||||
display: block;
|
||||
font-size: clamp(13px, 21rpx, 16px);
|
||||
line-height: 1.55;
|
||||
}
|
||||
.batch-notice { color: #426538; }
|
||||
.batch-error { color: $brand-red; }
|
||||
.ceremony-card {
|
||||
display: flex;
|
||||
min-height: 138rpx;
|
||||
|
||||
@@ -134,6 +134,24 @@
|
||||
</view>
|
||||
<text v-if="uploadError" class="error">{{ uploadError }}</text>
|
||||
</view>
|
||||
<view v-if="!isEdit" class="field field--password">
|
||||
<text>内容密码(选填)</text>
|
||||
<text class="person-option-note">填写后会先创建记录,再立即启用密码保护;设置失败时会保留记录并明确提示。</text>
|
||||
<input
|
||||
v-model="contentPassword"
|
||||
password
|
||||
maxlength="128"
|
||||
placeholder="请输入8至128位内容密码"
|
||||
@input="error = ''"
|
||||
/>
|
||||
<input
|
||||
v-model="contentPasswordConfirm"
|
||||
password
|
||||
maxlength="128"
|
||||
placeholder="请再次输入内容密码"
|
||||
@input="error = ''"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="error" class="error">{{ error }}</text>
|
||||
<view class="form-actions"
|
||||
><AppButton
|
||||
@@ -167,9 +185,21 @@
|
||||
><AppButton block label="新建成长记录" @click="openCreate"
|
||||
/></view>
|
||||
<view v-else class="record-list">
|
||||
<text v-if="saveNotice" class="save-notice"
|
||||
>成长记录已保存。</text
|
||||
>
|
||||
<BatchManagementBar
|
||||
v-if="deletableGrowthRecords.length"
|
||||
resource-name="成长记录"
|
||||
:active="growthBatch.selectionMode.value"
|
||||
:selected-count="growthBatch.selectedCount.value"
|
||||
:all-selected="growthBatch.allSelected.value"
|
||||
:busy="growthBatch.deleting.value"
|
||||
@start="growthBatch.enterSelectionMode"
|
||||
@finish="growthBatch.exitSelectionMode"
|
||||
@toggle-all="growthBatch.toggleAll"
|
||||
@delete="growthBatch.requestDelete"
|
||||
/>
|
||||
<text v-if="growthBatch.notice.value" class="save-notice" role="status">{{ growthBatch.notice.value }}</text>
|
||||
<text v-if="growthBatch.error.value" class="delete-error" role="alert">{{ growthBatch.error.value }}</text>
|
||||
<text v-if="saveNotice" class="save-notice">{{ saveNotice }}</text>
|
||||
<text v-if="deleteError" class="delete-error">{{ deleteError }}</text>
|
||||
<view class="record-filters">
|
||||
<picker :range="filterPersonLabels" :value="filterPersonIndex" @change="selectFilterPerson">
|
||||
@@ -185,9 +215,15 @@
|
||||
:key="item.id"
|
||||
class="record-card"
|
||||
role="button"
|
||||
:aria-label="`查看${item.title}详情`"
|
||||
@click="openRecordDetail(item)"
|
||||
:aria-label="growthBatch.selectionMode.value && item.canDelete ? `${growthBatch.isSelected(item) ? '取消选择' : '选择'}${item.title}` : `查看${item.title}详情`"
|
||||
@click="handleGrowthRecordClick(item)"
|
||||
>
|
||||
<BatchSelectionMark
|
||||
v-if="growthBatch.selectionMode.value && item.canDelete"
|
||||
:selected="growthBatch.isSelected(item)"
|
||||
:label="`成长记录:${item.title}`"
|
||||
@toggle="growthBatch.toggleSelection(item)"
|
||||
/>
|
||||
<view
|
||||
><text>{{ item.title }}</text
|
||||
><text
|
||||
@@ -197,14 +233,14 @@
|
||||
><text v-if="item.content">{{ item.content }}</text></view
|
||||
>
|
||||
<AppButton
|
||||
v-if="item.canEdit && personOptionsState === 'ready' && growthTypeOptionsState === 'ready'"
|
||||
v-if="!growthBatch.selectionMode.value && item.canEdit && personOptionsState === 'ready' && growthTypeOptionsState === 'ready'"
|
||||
compact
|
||||
type="secondary"
|
||||
label="编辑"
|
||||
@click.stop="openEdit(item)"
|
||||
/>
|
||||
<AppButton
|
||||
v-if="item.canDelete"
|
||||
v-if="!growthBatch.selectionMode.value && item.canDelete"
|
||||
compact
|
||||
type="secondary"
|
||||
label="删除"
|
||||
@@ -219,6 +255,17 @@
|
||||
@busy-change="detailBusy = $event"
|
||||
@transient-change="detailTransientOpen = $event"
|
||||
/>
|
||||
<AppDialog
|
||||
:visible="growthBatch.confirmationVisible.value"
|
||||
title="将选中的成长记录移入回收站?"
|
||||
:message="growthBatch.confirmationMessage.value"
|
||||
:confirm-text="growthBatch.deleting.value ? '正在删除' : '移入回收站'"
|
||||
cancel-text="继续选择"
|
||||
show-cancel
|
||||
:close-on-mask="false"
|
||||
@confirm="growthBatch.confirmDelete"
|
||||
@cancel="growthBatch.cancelDelete"
|
||||
/>
|
||||
<AppDialog
|
||||
:visible="discardVisible"
|
||||
title="放弃成长记录?"
|
||||
@@ -250,6 +297,8 @@ import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import BatchManagementBar from "@/components/BatchManagementBar.vue";
|
||||
import BatchSelectionMark from "@/components/BatchSelectionMark.vue";
|
||||
import GrowthRecordDetailDialog from "@/components/records/GrowthRecordDetailDialog.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
@@ -261,6 +310,7 @@ import { businessDictionaryApi } from "@/services/api/business-dictionary-servic
|
||||
import { lifeRecordApi } from "@/services/api/life-record-service.js";
|
||||
import { lineageApi } from "@/services/api/lineage-service.js";
|
||||
import { getRequestErrorMessage } from "@/services/api/request-error-message.js";
|
||||
import { useBatchDeletion } from "@/composables/use-batch-deletion.js";
|
||||
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
|
||||
import { formatMinuteTimestamp } from "@/utils/display-time.js";
|
||||
import { createNonIdempotentWriteGuard } from "@/utils/non-idempotent-write-guard.js";
|
||||
@@ -277,7 +327,7 @@ const personId = ref("");
|
||||
const view = ref("list");
|
||||
const listState = ref("loading");
|
||||
const records = ref([]);
|
||||
const saveNotice = ref(false);
|
||||
const saveNotice = ref("");
|
||||
const submitting = ref(false);
|
||||
const uploading = ref(false);
|
||||
const error = ref("");
|
||||
@@ -299,6 +349,8 @@ const personOptions = ref([{ value: "", label: "不关联人物" }]);
|
||||
const defaultLineagePersonId = ref("");
|
||||
const growthTypeOptionsState = ref("loading");
|
||||
const growthTypeOptions = ref([]);
|
||||
const contentPassword = ref("");
|
||||
const contentPasswordConfirm = ref("");
|
||||
const form = reactive({
|
||||
lineagePersonId: "",
|
||||
recordType: "",
|
||||
@@ -314,6 +366,7 @@ const recordListRequestController = createRequestController();
|
||||
const growthEditorDetailRequestController = createRequestController();
|
||||
const growthMediaUploadRequestController = createRequestController();
|
||||
const growthRecordSaveRequestController = createRequestController();
|
||||
const growthPasswordRequestController = createRequestController();
|
||||
const personOptionsController = createRequestController();
|
||||
const growthTypeRequestController = createRequestController();
|
||||
const growthRecordDeleteController = createRequestController();
|
||||
@@ -356,6 +409,20 @@ const filteredRecords = computed(() => records.value.filter((item) =>
|
||||
(!filterPersonId.value || (filterPersonId.value === "NONE" ? !item.lineagePersonId : item.lineagePersonId === filterPersonId.value)) &&
|
||||
(!filterType.value || item.type === filterType.value),
|
||||
));
|
||||
const growthBatch = useBatchDeletion({
|
||||
items: records,
|
||||
visibleItems: filteredRecords,
|
||||
deleteOne: (record) =>
|
||||
lifeRecordApi.deleteGrowthRecord(genealogyId.value, record.id, {
|
||||
requestController: growthRecordDeleteController,
|
||||
}),
|
||||
resourceName: "成长记录",
|
||||
isActive: () => pageActive,
|
||||
onEmpty: () => {
|
||||
listState.value = "empty";
|
||||
},
|
||||
});
|
||||
const deletableGrowthRecords = growthBatch.deletableItems;
|
||||
const mediaOssIds = computed(() =>
|
||||
mediaReceipts.value.map((item) => item.ossId).join(","),
|
||||
);
|
||||
@@ -376,7 +443,9 @@ const dirty = computed(() =>
|
||||
field !== "lineagePersonId" && String(value).trim(),
|
||||
) ||
|
||||
form.lineagePersonId !== defaultLineagePersonId.value ||
|
||||
mediaReceipts.value.length > 0,
|
||||
mediaReceipts.value.length > 0 ||
|
||||
contentPassword.value ||
|
||||
contentPasswordConfirm.value,
|
||||
);
|
||||
const confirmation = createDiscardConfirmation((visible) => {
|
||||
discardVisible.value = visible;
|
||||
@@ -400,6 +469,8 @@ const resetForm = () => {
|
||||
remindClock: "",
|
||||
});
|
||||
mediaReceipts.value = [];
|
||||
contentPassword.value = "";
|
||||
contentPasswordConfirm.value = "";
|
||||
editingRecord.value = null;
|
||||
formBaseline.value = "";
|
||||
error.value = "";
|
||||
@@ -456,6 +527,7 @@ const loadRecords = async () => {
|
||||
});
|
||||
if (!pageActive) return;
|
||||
records.value = rows;
|
||||
growthBatch.exitSelectionMode();
|
||||
listState.value = records.value.length ? "ready" : "empty";
|
||||
} catch (cause) {
|
||||
if (!pageActive || isRequestCancelled(cause)) return;
|
||||
@@ -464,7 +536,7 @@ const loadRecords = async () => {
|
||||
};
|
||||
const openCreate = () => {
|
||||
if (!valid.value) return;
|
||||
saveNotice.value = false;
|
||||
saveNotice.value = "";
|
||||
resetForm();
|
||||
view.value = "form";
|
||||
};
|
||||
@@ -563,15 +635,24 @@ const selectGrowthType = (event) => {
|
||||
};
|
||||
const selectFilterPerson = (event) => {
|
||||
filterPersonId.value = filterPersonOptions.value[Number(event.detail.value)]?.value || "";
|
||||
growthBatch.exitSelectionMode();
|
||||
};
|
||||
const selectFilterType = (event) => {
|
||||
filterType.value = filterTypeOptions.value[Number(event.detail.value)]?.value || "";
|
||||
growthBatch.exitSelectionMode();
|
||||
};
|
||||
const openRecordDetail = (record) =>
|
||||
growthRecordDetailDialog.value?.open({
|
||||
...record,
|
||||
typeLabel: growthRecordTypeLabel(record),
|
||||
});
|
||||
const handleGrowthRecordClick = (record) => {
|
||||
if (growthBatch.selectionMode.value && record?.canDelete) {
|
||||
growthBatch.toggleSelection(record);
|
||||
return;
|
||||
}
|
||||
openRecordDetail(record);
|
||||
};
|
||||
const uploadImage = async () => {
|
||||
if (uploading.value || submitting.value) return;
|
||||
uploading.value = true;
|
||||
@@ -595,6 +676,16 @@ const saveGrowthRecord = async () => {
|
||||
error.value = "请填写记录标题";
|
||||
return;
|
||||
}
|
||||
if (contentPassword.value || contentPasswordConfirm.value) {
|
||||
if (contentPassword.value.length < 8 || contentPassword.value.length > 128) {
|
||||
error.value = "内容密码必须为8至128位。";
|
||||
return;
|
||||
}
|
||||
if (contentPassword.value !== contentPasswordConfirm.value) {
|
||||
error.value = "两次输入的内容密码不一致。";
|
||||
return;
|
||||
}
|
||||
}
|
||||
const { recordClock, remindDate, remindClock, ...recordForm } = form;
|
||||
const payload = {
|
||||
...recordForm,
|
||||
@@ -619,6 +710,7 @@ const saveGrowthRecord = async () => {
|
||||
|
||||
submitting.value = true;
|
||||
error.value = "";
|
||||
let passwordSetupNotice = "";
|
||||
try {
|
||||
if (editingRecord.value) {
|
||||
await lifeRecordApi.updateGrowthRecord(
|
||||
@@ -628,14 +720,30 @@ const saveGrowthRecord = async () => {
|
||||
{ requestController: growthRecordSaveRequestController },
|
||||
);
|
||||
} else {
|
||||
await lifeRecordApi.createGrowthRecord(genealogyId.value, payload, {
|
||||
const createdRecord = await lifeRecordApi.createGrowthRecord(genealogyId.value, payload, {
|
||||
requestController: growthRecordSaveRequestController,
|
||||
});
|
||||
if (!pageActive) return;
|
||||
if (contentPassword.value) {
|
||||
try {
|
||||
await lifeRecordApi.setGrowthRecordPassword(
|
||||
genealogyId.value,
|
||||
createdRecord.id,
|
||||
contentPassword.value,
|
||||
{ requestController: growthPasswordRequestController },
|
||||
);
|
||||
passwordSetupNotice = "成长记录已保存,并已启用内容密码。";
|
||||
} catch (passwordError) {
|
||||
if (!pageActive || isRequestCancelled(passwordError)) return;
|
||||
const failureCopy = getRequestErrorMessage(passwordError, "内容密码设置失败");
|
||||
passwordSetupNotice = `成长记录已经创建,但${failureCopy}。请打开详情重新设置;当前内容可能尚未受到密码保护。`;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!pageActive) return;
|
||||
resetForm();
|
||||
view.value = "list";
|
||||
saveNotice.value = true;
|
||||
saveNotice.value = passwordSetupNotice || "成长记录已保存。";
|
||||
await loadRecords();
|
||||
} catch (cause) {
|
||||
if (!pageActive) return;
|
||||
@@ -708,7 +816,13 @@ const deleteRecord = async () => {
|
||||
}
|
||||
};
|
||||
const requestBack = () =>
|
||||
detailTransientOpen.value
|
||||
growthBatch.deleting.value
|
||||
? true
|
||||
: growthBatch.confirmationVisible.value
|
||||
? (growthBatch.cancelDelete(), true)
|
||||
: growthBatch.selectionMode.value
|
||||
? (growthBatch.exitSelectionMode(), true)
|
||||
: detailTransientOpen.value
|
||||
? (growthRecordDetailDialog.value?.closeTransient(), true)
|
||||
: view.value !== "form"
|
||||
? goBack()
|
||||
@@ -745,6 +859,7 @@ onUnload(() => {
|
||||
growthEditorDetailRequestController.abort();
|
||||
growthMediaUploadRequestController.abort();
|
||||
growthRecordSaveRequestController.abort();
|
||||
growthPasswordRequestController.abort();
|
||||
personOptionsController.abort();
|
||||
growthTypeRequestController.abort();
|
||||
growthRecordDeleteController.abort();
|
||||
@@ -822,6 +937,9 @@ onUnload(() => {
|
||||
.field textarea {
|
||||
min-height: 150rpx;
|
||||
}
|
||||
.field--password input + input {
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
.field--picker picker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
+106
-20
@@ -94,18 +94,46 @@
|
||||
><AppButton block :label="`新建${recordName}`" @click="openCreate"
|
||||
/></view>
|
||||
<view v-else class="memo-list">
|
||||
<BatchManagementBar
|
||||
v-if="deletableMemos.length"
|
||||
:resource-name="recordName"
|
||||
:active="memoBatch.selectionMode.value"
|
||||
:selected-count="memoBatch.selectedCount.value"
|
||||
:all-selected="memoBatch.allSelected.value"
|
||||
:busy="memoBatch.deleting.value"
|
||||
@start="memoBatch.enterSelectionMode"
|
||||
@finish="memoBatch.exitSelectionMode"
|
||||
@toggle-all="memoBatch.toggleAll"
|
||||
@delete="memoBatch.requestDelete"
|
||||
/>
|
||||
<text v-if="memoBatch.notice.value" class="save-notice" role="status">{{ memoBatch.notice.value }}</text>
|
||||
<text v-if="memoBatch.error.value" class="delete-error" role="alert">{{ memoBatch.error.value }}</text>
|
||||
<text v-if="saveNotice" class="save-notice"
|
||||
>{{ recordName }}已保存。</text
|
||||
>
|
||||
<text v-if="deleteError" class="delete-error">{{ deleteError }}</text>
|
||||
<view v-for="item in memos" :key="item.id" class="memo-card" role="button" :aria-label="`查看${item.title}详情`" @click="openMemoDetail(item)">
|
||||
<view class="memo-card__copy">
|
||||
<text>{{ item.title }}</text>
|
||||
<text v-if="item.remindTime">{{ item.remindTime }}</text>
|
||||
<text v-if="item.createTime">创建于 {{ formatMinuteTimestamp(item.createTime) }}</text>
|
||||
<text v-if="item.content" class="memo-card__content">{{ item.content }}</text>
|
||||
<view v-for="item in memos" :key="item.id" class="memo-card" role="button" :aria-label="memoBatch.selectionMode.value && item.canDelete ? `${memoBatch.isSelected(item) ? '取消选择' : '选择'}${item.title}` : `查看${item.title}详情`" @click="handleMemoClick(item)">
|
||||
<BatchSelectionMark
|
||||
v-if="memoBatch.selectionMode.value && item.canDelete"
|
||||
:selected="memoBatch.isSelected(item)"
|
||||
:label="`${recordName}:${item.title}`"
|
||||
@toggle="memoBatch.toggleSelection(item)"
|
||||
/>
|
||||
<view class="memo-card__main">
|
||||
<image
|
||||
v-if="item.mediaFiles?.[0]?.accessUrl"
|
||||
class="memo-card__cover"
|
||||
:src="item.mediaFiles[0].accessUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="memo-card__copy">
|
||||
<text>{{ item.title }}</text>
|
||||
<text v-if="item.remindTime">{{ item.remindTime }}</text>
|
||||
<text v-if="item.createTime">创建于 {{ formatMinuteTimestamp(item.createTime) }}</text>
|
||||
<text v-if="item.content" class="memo-card__content">{{ item.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="item.canEdit || item.canDelete" class="memo-card__actions">
|
||||
<view v-if="!memoBatch.selectionMode.value && (item.canEdit || item.canDelete)" class="memo-card__actions">
|
||||
<AppButton
|
||||
v-if="item.canEdit"
|
||||
compact
|
||||
@@ -124,6 +152,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="memoBatch.confirmationVisible.value"
|
||||
:title="`将选中的${recordName}移入回收站?`"
|
||||
:message="memoBatch.confirmationMessage.value"
|
||||
:confirm-text="memoBatch.deleting.value ? '正在删除' : '移入回收站'"
|
||||
cancel-text="继续选择"
|
||||
show-cancel
|
||||
:close-on-mask="false"
|
||||
@confirm="memoBatch.confirmDelete"
|
||||
@cancel="memoBatch.cancelDelete"
|
||||
/>
|
||||
<AppDialog
|
||||
:visible="Boolean(detailTarget)"
|
||||
:eyebrow="`${recordName}详情`"
|
||||
@@ -178,8 +217,11 @@ import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import BatchManagementBar from "@/components/BatchManagementBar.vue";
|
||||
import BatchSelectionMark from "@/components/BatchSelectionMark.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { useBatchDeletion } from "@/composables/use-batch-deletion.js";
|
||||
import {
|
||||
createRequestController,
|
||||
isRequestCancelled
|
||||
@@ -233,6 +275,19 @@ const memoDetailRequestController = createRequestController();
|
||||
const memoCreateGuard = createNonIdempotentWriteGuard();
|
||||
let pageActive = true;
|
||||
const valid = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
|
||||
const memoBatch = useBatchDeletion({
|
||||
items: memos,
|
||||
deleteOne: (memo) =>
|
||||
lifeRecordApi.deleteMemo(genealogyId.value, memo.id, {
|
||||
requestController: memoDeletionRequestController,
|
||||
}),
|
||||
resourceName: "记录",
|
||||
isActive: () => pageActive,
|
||||
onEmpty: () => {
|
||||
listState.value = "empty";
|
||||
},
|
||||
});
|
||||
const deletableMemos = memoBatch.deletableItems;
|
||||
const isBenefactorMode = computed(() => memoType.value === MEMO_TYPE.BENEFACTOR);
|
||||
const recordName = computed(() => isBenefactorMode.value ? "家族恩人" : "家族备忘");
|
||||
const pageTitle = computed(() => recordName.value);
|
||||
@@ -285,6 +340,7 @@ const loadMemos = async () => {
|
||||
});
|
||||
if (!pageActive) return;
|
||||
memos.value = rows.filter((memo) => memo.memoType === memoType.value);
|
||||
memoBatch.exitSelectionMode();
|
||||
listState.value = memos.value.length ? "ready" : "empty";
|
||||
if (pendingMemoId.value) {
|
||||
const target = memos.value.find((item) => item.id === pendingMemoId.value);
|
||||
@@ -372,6 +428,13 @@ const openMemoDetail = async (memo) => {
|
||||
detailError.value = getRequestErrorMessage(cause, "完整备忘读取失败,请稍后重试。");
|
||||
}
|
||||
};
|
||||
const handleMemoClick = (memo) => {
|
||||
if (memoBatch.selectionMode.value && memo?.canDelete) {
|
||||
memoBatch.toggleSelection(memo);
|
||||
return;
|
||||
}
|
||||
openMemoDetail(memo);
|
||||
};
|
||||
const closeMemoDetail = () => {
|
||||
if (detailState.value === "loading") return;
|
||||
detailTarget.value = null;
|
||||
@@ -503,19 +566,30 @@ const deleteMemo = async () => {
|
||||
if (pageActive) deleting.value = false;
|
||||
}
|
||||
};
|
||||
const requestBack = () =>
|
||||
detailTarget.value
|
||||
? (closeMemoDetail(), true)
|
||||
: view.value !== "form"
|
||||
? goBack()
|
||||
: runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: dirty.value,
|
||||
submitting: submitting.value || uploading.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": confirmation.request,
|
||||
});
|
||||
const requestBack = () => {
|
||||
if (memoBatch.deleting.value) return true;
|
||||
if (memoBatch.confirmationVisible.value) {
|
||||
memoBatch.cancelDelete();
|
||||
return true;
|
||||
}
|
||||
if (memoBatch.selectionMode.value) {
|
||||
memoBatch.exitSelectionMode();
|
||||
return true;
|
||||
}
|
||||
if (detailTarget.value) {
|
||||
closeMemoDetail();
|
||||
return true;
|
||||
}
|
||||
if (view.value !== "form") return goBack();
|
||||
return runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: dirty.value,
|
||||
submitting: submitting.value || uploading.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": confirmation.request,
|
||||
});
|
||||
};
|
||||
|
||||
onLoad((query) => {
|
||||
genealogyId.value = String(query?.genealogyId || "");
|
||||
@@ -708,10 +782,22 @@ onUnload(() => {
|
||||
min-height: 128rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
}
|
||||
.memo-card__main {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 18rpx;
|
||||
}
|
||||
.memo-card__copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.memo-card__cover {
|
||||
width: 148rpx;
|
||||
height: 148rpx;
|
||||
flex: 0 0 148rpx;
|
||||
border-radius: 8rpx;
|
||||
background: rgba(128, 89, 49, .12);
|
||||
}
|
||||
.memo-card__actions {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
+110
-24
@@ -100,6 +100,20 @@
|
||||
><AppButton block label="新建功德记录" @click="openCreate"
|
||||
/></view>
|
||||
<view v-else class="merit-list">
|
||||
<BatchManagementBar
|
||||
v-if="deletableMerits.length"
|
||||
resource-name="功德记录"
|
||||
:active="meritBatch.selectionMode.value"
|
||||
:selected-count="meritBatch.selectedCount.value"
|
||||
:all-selected="meritBatch.allSelected.value"
|
||||
:busy="meritBatch.deleting.value"
|
||||
@start="meritBatch.enterSelectionMode"
|
||||
@finish="meritBatch.exitSelectionMode"
|
||||
@toggle-all="meritBatch.toggleAll"
|
||||
@delete="meritBatch.requestDelete"
|
||||
/>
|
||||
<text v-if="meritBatch.notice.value" class="save-notice" role="status">{{ meritBatch.notice.value }}</text>
|
||||
<text v-if="meritBatch.error.value" class="delete-error" role="alert">{{ meritBatch.error.value }}</text>
|
||||
<text v-if="saveNotice" class="save-notice"
|
||||
>{{ saveNotice }}</text
|
||||
>
|
||||
@@ -107,15 +121,29 @@
|
||||
>共 {{ merits.length }} 笔,金额合计 ¥{{ totalAmount }}</text
|
||||
>
|
||||
<text v-if="deleteError" class="delete-error">{{ deleteError }}</text>
|
||||
<view v-for="item in merits" :key="item.id" class="merit-card" role="button" :aria-label="`查看${item.title}详情`" @click="openMeritDetail(item)">
|
||||
<view>
|
||||
<text>{{ item.title }}</text>
|
||||
<text>{{ item.donor }}{{ item.typeLabel ? ` · ${item.typeLabel}` : "" }}</text>
|
||||
<text v-if="item.time">{{ item.time }}</text>
|
||||
<text v-if="item.createTime">创建于 {{ formatMinuteTimestamp(item.createTime) }}</text>
|
||||
<text v-if="item.content">{{ item.content }}</text>
|
||||
<view v-for="item in merits" :key="item.id" class="merit-card" role="button" :aria-label="meritBatch.selectionMode.value && item.canDelete ? `${meritBatch.isSelected(item) ? '取消选择' : '选择'}${item.title}` : `查看${item.title}详情`" @click="handleMeritClick(item)">
|
||||
<BatchSelectionMark
|
||||
v-if="meritBatch.selectionMode.value && item.canDelete"
|
||||
:selected="meritBatch.isSelected(item)"
|
||||
:label="`功德记录:${item.title}`"
|
||||
@toggle="meritBatch.toggleSelection(item)"
|
||||
/>
|
||||
<view class="merit-card__main">
|
||||
<image
|
||||
v-if="item.mediaFiles?.[0]?.accessUrl"
|
||||
class="merit-card__cover"
|
||||
:src="item.mediaFiles[0].accessUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="merit-card__copy">
|
||||
<text>{{ item.title }}</text>
|
||||
<text>{{ item.donor }}{{ item.typeLabel ? ` · ${item.typeLabel}` : "" }}</text>
|
||||
<text v-if="item.time">{{ item.time }}</text>
|
||||
<text v-if="item.createTime">创建于 {{ formatMinuteTimestamp(item.createTime) }}</text>
|
||||
<text v-if="item.content">{{ item.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="merit-card__amount">
|
||||
<view v-if="!meritBatch.selectionMode.value" class="merit-card__amount">
|
||||
<text>¥{{ item.amount }}</text>
|
||||
<AppButton
|
||||
v-if="item.canEdit"
|
||||
@@ -135,6 +163,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="meritBatch.confirmationVisible.value"
|
||||
title="将选中的功德记录移入回收站?"
|
||||
:message="meritBatch.confirmationMessage.value"
|
||||
:confirm-text="meritBatch.deleting.value ? '正在删除' : '移入回收站'"
|
||||
cancel-text="继续选择"
|
||||
show-cancel
|
||||
:close-on-mask="false"
|
||||
@confirm="meritBatch.confirmDelete"
|
||||
@cancel="meritBatch.cancelDelete"
|
||||
/>
|
||||
<AppDialog
|
||||
:visible="Boolean(detailTarget)"
|
||||
eyebrow="功德详情"
|
||||
@@ -199,9 +238,12 @@ import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import BatchManagementBar from "@/components/BatchManagementBar.vue";
|
||||
import BatchSelectionMark from "@/components/BatchSelectionMark.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { businessDictionaryApi } from "@/services/api/business-dictionary-service.js";
|
||||
import { useBatchDeletion } from "@/composables/use-batch-deletion.js";
|
||||
import {
|
||||
createRequestController,
|
||||
isRequestCancelled
|
||||
@@ -263,6 +305,19 @@ const meritTypeRequestController = createRequestController();
|
||||
const meritRecordCreateGuard = createNonIdempotentWriteGuard();
|
||||
let pageActive = true;
|
||||
const valid = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
|
||||
const meritBatch = useBatchDeletion({
|
||||
items: merits,
|
||||
deleteOne: (merit) =>
|
||||
lifeRecordApi.deleteMeritRecord(genealogyId.value, merit.id, {
|
||||
requestController: meritDeletionRequestController,
|
||||
}),
|
||||
resourceName: "功德记录",
|
||||
isActive: () => pageActive,
|
||||
onEmpty: () => {
|
||||
listState.value = "empty";
|
||||
},
|
||||
});
|
||||
const deletableMerits = meritBatch.deletableItems;
|
||||
const isEdit = computed(() => Boolean(editingMerit.value));
|
||||
const mediaOssIds = computed(() => mediaReceipts.value.map((item) => item.ossId).join(","));
|
||||
const formSnapshot = computed(() => JSON.stringify({ ...form, mediaOssIds: mediaOssIds.value }));
|
||||
@@ -338,6 +393,7 @@ const loadMerits = async () => {
|
||||
});
|
||||
if (!pageActive) return;
|
||||
merits.value = rows;
|
||||
meritBatch.exitSelectionMode();
|
||||
listState.value = merits.value.length ? "ready" : "empty";
|
||||
} catch (cause) {
|
||||
if (!pageActive || isRequestCancelled(cause)) return;
|
||||
@@ -437,6 +493,13 @@ const openMeritDetail = async (merit) => {
|
||||
detailError.value = getRequestErrorMessage(cause, "完整记录读取失败,请稍后重试。");
|
||||
}
|
||||
};
|
||||
const handleMeritClick = (merit) => {
|
||||
if (meritBatch.selectionMode.value && merit?.canDelete) {
|
||||
meritBatch.toggleSelection(merit);
|
||||
return;
|
||||
}
|
||||
openMeritDetail(merit);
|
||||
};
|
||||
const closeMeritDetail = () => {
|
||||
if (detailState.value === "loading") return;
|
||||
detailTarget.value = null;
|
||||
@@ -591,19 +654,30 @@ const deleteMerit = async () => {
|
||||
if (pageActive) deleting.value = false;
|
||||
}
|
||||
};
|
||||
const requestBack = () =>
|
||||
detailTarget.value
|
||||
? (closeMeritDetail(), true)
|
||||
: view.value !== "form"
|
||||
? goBack()
|
||||
: runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: dirty.value,
|
||||
submitting: submitting.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": confirmation.request,
|
||||
});
|
||||
const requestBack = () => {
|
||||
if (meritBatch.deleting.value) return true;
|
||||
if (meritBatch.confirmationVisible.value) {
|
||||
meritBatch.cancelDelete();
|
||||
return true;
|
||||
}
|
||||
if (meritBatch.selectionMode.value) {
|
||||
meritBatch.exitSelectionMode();
|
||||
return true;
|
||||
}
|
||||
if (detailTarget.value) {
|
||||
closeMeritDetail();
|
||||
return true;
|
||||
}
|
||||
if (view.value !== "form") return goBack();
|
||||
return runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: dirty.value,
|
||||
submitting: submitting.value,
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": confirmation.request,
|
||||
});
|
||||
};
|
||||
|
||||
onLoad((query) => {
|
||||
genealogyId.value = String(query?.genealogyId || "");
|
||||
@@ -782,21 +856,33 @@ onUnload(() => {
|
||||
gap: 18rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
}
|
||||
.merit-card > view {
|
||||
.merit-card__main {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 18rpx;
|
||||
}
|
||||
.merit-card__copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.merit-card__cover {
|
||||
width: 148rpx;
|
||||
height: 148rpx;
|
||||
flex: 0 0 148rpx;
|
||||
border-radius: 8rpx;
|
||||
background: rgba(128, 89, 49, .12);
|
||||
}
|
||||
.merit-card text {
|
||||
display: block;
|
||||
}
|
||||
.merit-card > view text:first-child {
|
||||
.merit-card__copy text:first-child {
|
||||
color: $ink;
|
||||
font-family: STKaiti, KaiTi, serif;
|
||||
font-size: clamp(17px, 30rpx, 22px);
|
||||
font-weight: 700;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.merit-card > view text:not(:first-child) {
|
||||
.merit-card__copy text:not(:first-child) {
|
||||
margin-top: 7rpx;
|
||||
color: $ink-muted;
|
||||
font-size: clamp(13px, 21rpx, 16px);
|
||||
|
||||
@@ -30,8 +30,28 @@
|
||||
><AppButton block label="新建往来记录" @click="createRelative"
|
||||
/></view>
|
||||
<view v-else class="record-list">
|
||||
<BatchManagementBar
|
||||
v-if="deletableRecords.length"
|
||||
resource-name="往来记录"
|
||||
:active="recordBatch.selectionMode.value"
|
||||
:selected-count="recordBatch.selectedCount.value"
|
||||
:all-selected="recordBatch.allSelected.value"
|
||||
:busy="recordBatch.deleting.value"
|
||||
@start="recordBatch.enterSelectionMode"
|
||||
@finish="recordBatch.exitSelectionMode"
|
||||
@toggle-all="recordBatch.toggleAll"
|
||||
@delete="recordBatch.requestDelete"
|
||||
/>
|
||||
<text v-if="recordBatch.notice.value" class="batch-notice" role="status">{{ recordBatch.notice.value }}</text>
|
||||
<text v-if="recordBatch.error.value" class="delete-error" role="alert">{{ recordBatch.error.value }}</text>
|
||||
<text v-if="deleteError" class="delete-error">{{ deleteError }}</text>
|
||||
<view v-for="item in records" :key="item.id" class="record-card" role="button" :aria-label="`查看${item.name}的往来详情`" @click="openRecordDetail(item)">
|
||||
<view v-for="item in records" :key="item.id" class="record-card" role="button" :aria-label="recordBatch.selectionMode.value && item.canDelete ? `${recordBatch.isSelected(item) ? '取消选择' : '选择'}${item.name}` : `查看${item.name}的往来详情`" @click="handleRecordClick(item)">
|
||||
<BatchSelectionMark
|
||||
v-if="recordBatch.selectionMode.value && item.canDelete"
|
||||
:selected="recordBatch.isSelected(item)"
|
||||
:label="`往来记录:${item.name}`"
|
||||
@toggle="recordBatch.toggleSelection(item)"
|
||||
/>
|
||||
<image
|
||||
v-if="item.mediaFiles?.[0]?.accessUrl"
|
||||
class="record-card__cover"
|
||||
@@ -48,7 +68,7 @@
|
||||
><text v-if="item.createTime">创建于 {{ formatMinuteTimestamp(item.createTime) }}</text
|
||||
><text v-if="item.content">{{ item.content }}</text></view
|
||||
>
|
||||
<view class="record-card__amount">
|
||||
<view v-if="!recordBatch.selectionMode.value" class="record-card__amount">
|
||||
<text v-if="item.amount">¥{{ item.amount }}</text>
|
||||
<AppButton
|
||||
v-if="item.canEdit"
|
||||
@@ -68,6 +88,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="recordBatch.confirmationVisible.value"
|
||||
title="将选中的往来记录移入回收站?"
|
||||
:message="recordBatch.confirmationMessage.value"
|
||||
:confirm-text="recordBatch.deleting.value ? '正在删除' : '移入回收站'"
|
||||
cancel-text="继续选择"
|
||||
show-cancel
|
||||
:close-on-mask="false"
|
||||
@confirm="recordBatch.confirmDelete"
|
||||
@cancel="recordBatch.cancelDelete"
|
||||
/>
|
||||
<AppDialog
|
||||
:visible="Boolean(detailTarget)"
|
||||
eyebrow="往来详情"
|
||||
@@ -113,8 +144,11 @@ import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppLoading from "@/components/AppLoading.vue";
|
||||
import BatchManagementBar from "@/components/BatchManagementBar.vue";
|
||||
import BatchSelectionMark from "@/components/BatchSelectionMark.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { useBatchDeletion } from "@/composables/use-batch-deletion.js";
|
||||
import {
|
||||
createRequestController,
|
||||
isRequestCancelled
|
||||
@@ -139,6 +173,19 @@ const detailError = ref("");
|
||||
const relativeRecordDetailController = createRequestController();
|
||||
let isPageActive = true;
|
||||
const valid = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
|
||||
const recordBatch = useBatchDeletion({
|
||||
items: records,
|
||||
deleteOne: (record) =>
|
||||
lifeRecordApi.deleteRelativeRecord(genealogyId.value, record.id, {
|
||||
requestController: relativeRecordDeleteController,
|
||||
}),
|
||||
resourceName: "往来记录",
|
||||
isActive: () => isPageActive,
|
||||
onEmpty: () => {
|
||||
relativeRecordListState.value = "empty";
|
||||
},
|
||||
});
|
||||
const deletableRecords = recordBatch.deletableItems;
|
||||
const loadRecords = async () => {
|
||||
if (!valid.value) return;
|
||||
relativeRecordListController.abort();
|
||||
@@ -149,6 +196,7 @@ const loadRecords = async () => {
|
||||
});
|
||||
if (!isPageActive) return;
|
||||
records.value = rows;
|
||||
recordBatch.exitSelectionMode();
|
||||
relativeRecordListState.value = records.value.length ? "ready" : "empty";
|
||||
} catch (error) {
|
||||
if (!isPageActive || isRequestCancelled(error)) return;
|
||||
@@ -156,7 +204,7 @@ const loadRecords = async () => {
|
||||
}
|
||||
};
|
||||
const returnToFamily = () =>
|
||||
valid.value ? returnTo("F01", { genealogyId: genealogyId.value }) : goBack();
|
||||
valid.value ? returnTo("G05", { genealogyId: genealogyId.value }) : goBack();
|
||||
const createRelative = () =>
|
||||
valid.value
|
||||
? openPage("R04", { genealogyId: genealogyId.value, mode: "create" }, "R03")
|
||||
@@ -188,6 +236,13 @@ const openRecordDetail = async (record) => {
|
||||
detailError.value = getRequestErrorMessage(error, "完整记录读取失败,请稍后重试。");
|
||||
}
|
||||
};
|
||||
const handleRecordClick = (record) => {
|
||||
if (recordBatch.selectionMode.value && record?.canDelete) {
|
||||
recordBatch.toggleSelection(record);
|
||||
return;
|
||||
}
|
||||
openRecordDetail(record);
|
||||
};
|
||||
const closeRecordDetail = () => {
|
||||
if (detailState.value === "loading") return;
|
||||
detailTarget.value = null;
|
||||
@@ -211,7 +266,15 @@ const closeDeleteConfirmation = () => {
|
||||
deleteTarget.value = null;
|
||||
};
|
||||
const requestBack = () => {
|
||||
if (deleting.value || detailState.value === "loading") return true;
|
||||
if (deleting.value || recordBatch.deleting.value || detailState.value === "loading") return true;
|
||||
if (recordBatch.confirmationVisible.value) {
|
||||
recordBatch.cancelDelete();
|
||||
return true;
|
||||
}
|
||||
if (recordBatch.selectionMode.value) {
|
||||
recordBatch.exitSelectionMode();
|
||||
return true;
|
||||
}
|
||||
if (deleteConfirmationVisible.value) {
|
||||
closeDeleteConfirmation();
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user