diff --git a/.workbuddy/memory/2026-05-06.md b/.workbuddy/memory/2026-05-06.md index 50d9e60..2cde560 100644 --- a/.workbuddy/memory/2026-05-06.md +++ b/.workbuddy/memory/2026-05-06.md @@ -66,3 +66,21 @@ - 新增 `showDeleteModal` + `deletingCalendar` 状态 - `deleteCalendar()` 改为打开确认弹窗,`confirmDelete()` 执行实际删除 API - i18n 新增 `calendar.confirm_delete_message`:zh-CN「确定要删除日历「{name}」吗?此操作不可撤销。」,en 英文版 + +## 新增日历管理页面 /calendars/admin + +**功能**:系统管理员查看所有日历列表,包含日程数量、创建者、创建时间。 + +**新增文件**: +- `src/views/calendar/CalendarAdminList.vue` - 日历管理列表组件 + +**路由修改** `src/router/index.js`: +- 新增 `/calendars/admin` 路由,指向 `CalendarAdminList.vue` +- 设置 `meta: { requireSysAdmin: true }` 要求管理员权限 + +**SysAdminView.vue**: +- `tabs` 数组新增 `{ id: 'calendar', label: t('calendar.admin_title'), to: '/calendars/admin' }` + +**i18n 新增**: +- `zh-CN.json`: `calendar.admin_title = "日历管理"`, `calendar.event_count = "日程数量"` +- `en.json`: `calendar.admin_title = "Calendar Admin"`, `calendar.event_count = "Event Count"` diff --git a/backend/my_work/routers/apiCalendar.go b/backend/my_work/routers/apiCalendar.go index 469b74e..ca12de9 100644 --- a/backend/my_work/routers/apiCalendar.go +++ b/backend/my_work/routers/apiCalendar.go @@ -111,6 +111,10 @@ type fromDeleteCalendarEvent struct { ID uint `json:"id" binding:"required"` } +type fromRestoreCalendar struct { + ID uint `json:"id" binding:"required"` +} + var ( calendarUserGroup TabUserGroups calendarAdmins []uint @@ -280,6 +284,72 @@ func ApiCalendar(r *gin.RouterGroup) { } }) + // 获取所有日历(包括已删除的,系统管理员专用) + r.POST("/calendar/list_all", func(ctx *gin.Context) { + isAuth, _, _ := AuthenticationAuthority(ctx) + if !isAuth { + ReturnJson(ctx, "userCookieError", nil) + return + } + + // 使用 Unscoped 查询所有日历(包括软删除的) + var calendars []TabCalendar + models.DB.Unscoped().Order("created_at DESC").Find(&calendars) + + type CalendarWithEdit struct { + TabCalendar + CanEdit bool `json:"canEdit"` + } + var result []CalendarWithEdit + for _, cal := range calendars { + result = append(result, CalendarWithEdit{ + TabCalendar: cal, + CanEdit: true, + }) + } + ReturnJson(ctx, "apiOK", gin.H{"list": result}) + }) + + // 恢复已删除的日历 + r.POST("/calendar/restore", func(ctx *gin.Context) { + isAuth, user, data := AuthenticationAuthority(ctx) + if !isAuth { + ReturnJson(ctx, "userCookieError", nil) + return + } + + var from fromRestoreCalendar + if err := mapstructure.Decode(data, &from); err != nil { + ReturnJson(ctx, "jsonErr", nil) + return + } + + // 使用 Unscoped 查询(包括软删除的) + var calendar TabCalendar + if models.DB.Unscoped().Where("id = ?", from.ID).First(&calendar).Error != nil { + ReturnJson(ctx, "calendar_not_find", nil) + return + } + + // 恢复软删除(将 deleted_at 设为 NULL) + if models.DB.Unscoped().Model(&calendar).Update("deleted_at", nil).Error != nil { + ReturnJson(ctx, "apiErr", nil) + return + } + + // 记录日志 + newContent, _ := json.Marshal(calendar) + log := TabCalendarLog{ + CalendarID: calendar.ID, + UserID: user.ID, + ActionType: "restore", + NewContent: string(newContent), + IP: ctx.ClientIP(), + } + models.DB.Create(&log) + ReturnJson(ctx, "apiOK", nil) + }) + // 删除日历 r.POST("/calendar/delete", func(ctx *gin.Context) { isAuth, user, data := AuthenticationAuthority(ctx) diff --git a/frontend/ops_vue_js/src/api/calendar.js b/frontend/ops_vue_js/src/api/calendar.js index 4d23479..796b34d 100644 --- a/frontend/ops_vue_js/src/api/calendar.js +++ b/frontend/ops_vue_js/src/api/calendar.js @@ -11,6 +11,11 @@ export const calendarApi = { return api.post('/calendar/calendar/list', {}) }, + // 获取所有日历(包括已删除的,系统管理员专用) + getAllCalendars() { + return api.post('/calendar/calendar/list_all', {}) + }, + // 更新日历 updateCalendar(data) { return api.post('/calendar/calendar/update', data) @@ -21,6 +26,11 @@ export const calendarApi = { return api.post('/calendar/calendar/delete', { id }) }, + // 恢复已删除的日历 + restoreCalendar(id) { + return api.post('/calendar/calendar/restore', { id }) + }, + // 获取日历事件 getEvents(data) { return api.post('/calendar/calendar/events', data) diff --git a/frontend/ops_vue_js/src/i18n/en.json b/frontend/ops_vue_js/src/i18n/en.json index 45e1c1c..2932a1f 100644 --- a/frontend/ops_vue_js/src/i18n/en.json +++ b/frontend/ops_vue_js/src/i18n/en.json @@ -643,6 +643,13 @@ "event_save_success": "Event saved successfully", "event_delete_success": "Event deleted successfully", "confirm_delete_event": "Are you sure you want to delete this event?", - "is_public_event": "Public Event" + "is_public_event": "Public Event", + "admin_title": "Calendar Admin", + "event_count": "Event Count", + "restore": "Restore", + "restore_success": "Restored successfully", + "deleted": "Deleted", + "confirm_restore": "Are you sure you want to restore this calendar?", + "confirm_restore_message": "Are you sure you want to restore calendar '{name}'?" } } diff --git a/frontend/ops_vue_js/src/i18n/zh-CN.json b/frontend/ops_vue_js/src/i18n/zh-CN.json index d432344..8cee082 100644 --- a/frontend/ops_vue_js/src/i18n/zh-CN.json +++ b/frontend/ops_vue_js/src/i18n/zh-CN.json @@ -644,6 +644,13 @@ "event_save_success": "事件保存成功", "event_delete_success": "事件删除成功", "confirm_delete_event": "确定要删除此事件吗?", - "is_public_event": "公共日程" + "is_public_event": "公共日程", + "admin_title": "日历管理", + "event_count": "日程数量", + "restore": "恢复", + "restore_success": "恢复成功", + "deleted": "已删除", + "confirm_restore": "确定要恢复此日历吗?", + "confirm_restore_message": "确定要恢复日历「{name}」吗?" } } diff --git a/frontend/ops_vue_js/src/router/index.js b/frontend/ops_vue_js/src/router/index.js index cb69a49..e90ad30 100644 --- a/frontend/ops_vue_js/src/router/index.js +++ b/frontend/ops_vue_js/src/router/index.js @@ -129,6 +129,12 @@ const router = createRouter({ name: 'customer-edit', component: () => import('@/views/customer/CustomerFormPage.vue'), }, + { + path: 'calendars/admin', + name: 'calendars-admin', + component: () => import('@/views/calendar/CalendarAdminList.vue'), + meta: { requireSysAdmin: true }, + }, { path: 'calendars', name: 'calendars', diff --git a/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue b/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue new file mode 100644 index 0000000..bd12ab3 --- /dev/null +++ b/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue @@ -0,0 +1,464 @@ + + + diff --git a/frontend/ops_vue_js/src/views/sysadmin/SysAdminView.vue b/frontend/ops_vue_js/src/views/sysadmin/SysAdminView.vue index 4670c0d..617e9b3 100644 --- a/frontend/ops_vue_js/src/views/sysadmin/SysAdminView.vue +++ b/frontend/ops_vue_js/src/views/sysadmin/SysAdminView.vue @@ -31,6 +31,7 @@ const tabs = [ { id: 'logs', label: t('sysadmin.tab_logs') }, { id: 'operation_logs', label: t('sysadmin.tab_operation_logs') }, { id: 'customer', label: t('customer.title'), to: '/customer' }, + { id: 'calendar', label: t('calendar.admin_title'), to: '/calendars/admin' }, ] async function fetchSysAdmins() {