From 1d5f0b4bc086ea13e8e9ed14227d637a93c6a306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E5=B3=B0?= Date: Wed, 6 May 2026 20:36:52 +0800 Subject: [PATCH] up --- .workbuddy/memory/2026-05-06.md | 24 ++++++++ .../src/components/ConfirmDialog.vue | 8 ++- frontend/ops_vue_js/src/i18n/en.json | 3 + frontend/ops_vue_js/src/i18n/zh-CN.json | 3 + .../src/views/calendar/CalendarDetail.vue | 59 +++++++++++++++++-- .../src/views/calendar/CalendarList.vue | 33 ++++++++--- 6 files changed, 114 insertions(+), 16 deletions(-) diff --git a/.workbuddy/memory/2026-05-06.md b/.workbuddy/memory/2026-05-06.md index 4395f28..50d9e60 100644 --- a/.workbuddy/memory/2026-05-06.md +++ b/.workbuddy/memory/2026-05-06.md @@ -42,3 +42,27 @@ **前端**:添加 `getColorByScheduleType()` 函数,`getEvents` 中使用 scheduleType 映射颜色。 **后端**:只存储 ScheduleType,不处理颜色逻辑。颜色完全由前端 `colorOptions` 控制。 + +## CalendarDetail 滚动标题快照对比 + +**功能**:每次 getEvents 存快照,数据变化或宽度变化时重新计算标题滚动。 + +**实现**: +- `pageData.lastEventsSnapshot`:存储上一次的 JSON 快照 +- `getEvents()` 末尾对比快照,变化则 `setTimeout(recalcScrollTitles, 150)` +- `ResizeObserver` 监听日历容器宽度变化,防抖 150ms 后重算 +- `applyScrollToTitle()`:清除旧状态 → 测量 overflow → 设置 `--scroll-distance` 和 `data-truncated` 属性 + +## CalendarList 编辑/删除按钮改用 canEdit + +**改动**: +- `CalendarList.vue`:编辑/删除按钮 `v-if` 条件从 `calendar.UserID === userStore.userInfo?.ID` 改为 `calendar.canEdit` +- 移除废弃的 `useUserStore` 导入 + +## CalendarList 删除改用 ConfirmDialog 组件 + +**改动**: +- `CalendarList.vue` 导入并使用 `ConfirmDialog` 组件 +- 新增 `showDeleteModal` + `deletingCalendar` 状态 +- `deleteCalendar()` 改为打开确认弹窗,`confirmDelete()` 执行实际删除 API +- i18n 新增 `calendar.confirm_delete_message`:zh-CN「确定要删除日历「{name}」吗?此操作不可撤销。」,en 英文版 diff --git a/frontend/ops_vue_js/src/components/ConfirmDialog.vue b/frontend/ops_vue_js/src/components/ConfirmDialog.vue index 704bae7..31c2289 100644 --- a/frontend/ops_vue_js/src/components/ConfirmDialog.vue +++ b/frontend/ops_vue_js/src/components/ConfirmDialog.vue @@ -15,7 +15,7 @@ * 或者作为组件使用 v-model: * */ -import { ref, watch } from "vue"; +import { ref, watch, onMounted, getCurrentInstance } from "vue"; import { useI18n } from "vue-i18n"; const props = defineProps({ @@ -47,7 +47,11 @@ const props = defineProps({ const emit = defineEmits(["update:modelValue", "confirm", "cancel"]); -const { t } = useI18n(); +// 优先用组件内 i18n,Teleport 场景下可能为空,从全局补足 +const instance = getCurrentInstance(); +const { t: componentT } = useI18n(); +const globalT = instance?.appContext?.config?.globalProperties?.$i18n?.t; +const t = (key, ...args) => componentT(key, ...args) || globalT?.(key, ...args) || key; function close() { emit("update:modelValue", false); diff --git a/frontend/ops_vue_js/src/i18n/en.json b/frontend/ops_vue_js/src/i18n/en.json index 017c91e..45e1c1c 100644 --- a/frontend/ops_vue_js/src/i18n/en.json +++ b/frontend/ops_vue_js/src/i18n/en.json @@ -1,4 +1,6 @@ { + "delete": "Delete", + "cancel": "Cancel", "common": { "actions": "Actions", "search": "Search", @@ -625,6 +627,7 @@ "update_success": "Calendar updated successfully", "delete_success": "Calendar deleted successfully", "confirm_delete": "Are you sure you want to delete this calendar?", + "confirm_delete_message": "Are you sure you want to delete calendar '{name}'? This action cannot be undone.", "loading": "Loading...", "add_event": "Add Event", "edit_event": "Edit Event", diff --git a/frontend/ops_vue_js/src/i18n/zh-CN.json b/frontend/ops_vue_js/src/i18n/zh-CN.json index d7e8bea..7c28b72 100644 --- a/frontend/ops_vue_js/src/i18n/zh-CN.json +++ b/frontend/ops_vue_js/src/i18n/zh-CN.json @@ -1,4 +1,6 @@ { + "delete": "删除", + "cancel": "取消", "common": { "actions": "操作", "search": "搜索", @@ -625,6 +627,7 @@ "update_success": "更新成功", "delete_success": "删除成功", "confirm_delete": "确定要删除此日历吗?", + "confirm_delete_message": "确定要删除日历「{name}」吗?此操作不可撤销。", "loading": "加载中...", "add_event": "添加事件", "edit_event": "编辑事件", diff --git a/frontend/ops_vue_js/src/views/calendar/CalendarDetail.vue b/frontend/ops_vue_js/src/views/calendar/CalendarDetail.vue index ae14464..8d26126 100644 --- a/frontend/ops_vue_js/src/views/calendar/CalendarDetail.vue +++ b/frontend/ops_vue_js/src/views/calendar/CalendarDetail.vue @@ -13,6 +13,7 @@ import { useUserStore } from "@/stores/user" import { calendarApi } from "@/api/calendar" import { useDateUtils } from "@/composables/useDateUtils" import DatatimePickerForFullCalendar from "@/components/datatimePickerForFullCalendar.vue" +import ConfirmDialog from "@/components/ConfirmDialog.vue" const route = useRoute() const router = useRouter() @@ -62,6 +63,8 @@ const pageData = ref({ lastEventsSnapshot: null, }) +const showDeleteModal = ref(false) + // 选中/取消选中事件 function unseleEvent(eventID) { const target = calendarOptions.value.events.find(item => item.id === eventID) @@ -187,19 +190,23 @@ async function saveEvent() { } async function deleteEvent() { - if (!confirm(t('calendar.confirm_delete_event'))) return + showDeleteModal.value = true +} +async function confirmDeleteEvent() { try { const result = await calendarApi.deleteEvent(eventData.value.id) if (result.errCode === 0) { - toast.success(t('calendar.event_delete_success')) + toast.success(t("calendar.event_delete_success")) closeEventModal() getEvents() } else { - toast.error(t('message.server_error')) + toast.error(t("message.server_error")) } } catch { // 拦截器已处理 + } finally { + showDeleteModal.value = false } } @@ -319,12 +326,20 @@ const calendarOptions = ref({ }, headerToolbar: { - left: "prevYear,prev,today,next,nextYear", - center: "title", - right: "", + left: "backToList,prevYear,prev,today,next,nextYear", + center: "myTitle", + right: "title", }, customButtons: { + backToList: { + text: t("calendar.calendars"), + click() { router.push("/calendars") }, + }, + myTitle: { + text: calendarInfo.value?.Name || "", + disabled: true, + }, prevYear: { text: t("schedule.previous_year"), click() { calendarRef.value.getApi().prevYear(); getEvents() }, @@ -439,9 +454,15 @@ const calendarOptions = ref({ }, }) +// 监听日历信息变化 +watch(calendarInfo, () => { + calendarOptions.value.customButtons.myTitle.text = calendarInfo.value?.Name || "" +}) + // 监听语言变化 watch(locale, () => { calendarOptions.value.locale = locale.value + calendarOptions.value.customButtons.backToList.text = t("calendar.calendars") calendarOptions.value.customButtons.prevYear.text = t("schedule.previous_year") calendarOptions.value.customButtons.nextYear.text = t("schedule.next_year") calendarOptions.value.customButtons.prev.text = t("schedule.previous_month") @@ -674,9 +695,35 @@ onMounted(() => { + + +