diff --git a/backend/my_work/routers/apiCalendar.go b/backend/my_work/routers/apiCalendar.go index 5c308cf..d63a822 100644 --- a/backend/my_work/routers/apiCalendar.go +++ b/backend/my_work/routers/apiCalendar.go @@ -286,27 +286,51 @@ func ApiCalendar(r *gin.RouterGroup) { } }) - // 获取所有日历(包括已删除的,系统管理员专用) + // 获取所有日历(包括已删除的,管理员专用) r.POST("/calendar/list_all", func(ctx *gin.Context) { - isAuth, _, _ := AuthenticationAuthority(ctx) + isAuth, user, _ := AuthenticationAuthority(ctx) if !isAuth { ReturnJson(ctx, "userCookieError", nil) return } + // 限制只有日历管理员可访问 + if !slices.Contains(calendarAdmins, user.ID) { + ReturnJson(ctx, "permission_denied", nil) + return + } + // 使用 Unscoped 查询所有日历(包括软删除的) var calendars []TabCalendar models.DB.Unscoped().Order("created_at DESC").Find(&calendars) + // 一次性查询所有日历的事件数量(仅统计未删除的事件) + type calendarEventCount struct { + CalendarID uint `gorm:"column:calendar_id"` + Cnt int `gorm:"column:cnt"` + } + var rows []calendarEventCount + models.DB.Model(&TabCalendarEvent{}). + Select("calendar_id, COUNT(*) as cnt"). + Where("deleted_at IS NULL"). + Group("calendar_id"). + Scan(&rows) + eventCountMap := make(map[uint]int) + for _, row := range rows { + eventCountMap[row.CalendarID] = row.Cnt + } + type CalendarWithEdit struct { TabCalendar - CanEdit bool `json:"canEdit"` + CanEdit bool `json:"canEdit"` + EventCount int `json:"event_count"` } var result []CalendarWithEdit for _, cal := range calendars { result = append(result, CalendarWithEdit{ TabCalendar: cal, CanEdit: true, + EventCount: eventCountMap[cal.ID], }) } ReturnJson(ctx, "apiOK", gin.H{"list": result}) @@ -320,6 +344,12 @@ func ApiCalendar(r *gin.RouterGroup) { return } + // 限制只有日历管理员可操作 + if !slices.Contains(calendarAdmins, user.ID) { + ReturnJson(ctx, "permission_denied", nil) + return + } + var from fromRestoreCalendar if err := mapstructure.Decode(data, &from); err != nil { ReturnJson(ctx, "jsonErr", nil) diff --git a/frontend/ops_vue_js/src/i18n/en.json b/frontend/ops_vue_js/src/i18n/en.json index d96777f..45d52f2 100644 --- a/frontend/ops_vue_js/src/i18n/en.json +++ b/frontend/ops_vue_js/src/i18n/en.json @@ -444,7 +444,8 @@ "security": "Security", "security_description": "Manage your account security settings", "my_groups": "My Groups", - "no_groups": "Not joined any groups yet" + "no_groups": "Not joined any groups yet", + "admin_panel": "Admin" }, "button": { "submit": "Submit", @@ -656,6 +657,7 @@ "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}'?" + "confirm_restore_message": "Are you sure you want to restore calendar '{name}'?", + "admin_panel": "Admin" } } diff --git a/frontend/ops_vue_js/src/i18n/zh-CN.json b/frontend/ops_vue_js/src/i18n/zh-CN.json index a113cf5..35ec349 100644 --- a/frontend/ops_vue_js/src/i18n/zh-CN.json +++ b/frontend/ops_vue_js/src/i18n/zh-CN.json @@ -444,7 +444,8 @@ "security": "安全设置", "security_description": "管理您的账户安全设置", "my_groups": "我的群组", - "no_groups": "暂未加入任何群组" + "no_groups": "暂未加入任何群组", + "admin_panel": "管理" }, "button": { "submit": "提交", @@ -657,6 +658,7 @@ "restore_success": "恢复成功", "deleted": "已删除", "confirm_restore": "确定要恢复此日历吗?", - "confirm_restore_message": "确定要恢复日历「{name}」吗?" + "confirm_restore_message": "确定要恢复日历「{name}」吗?", + "admin_panel": "管理" } } diff --git a/frontend/ops_vue_js/src/stores/user.js b/frontend/ops_vue_js/src/stores/user.js index 3afdd63..7c5fd6a 100644 --- a/frontend/ops_vue_js/src/stores/user.js +++ b/frontend/ops_vue_js/src/stores/user.js @@ -64,6 +64,11 @@ export const useUserStore = defineStore('user', () => { // 是否系统管理员(后端直接返回) const isSysAdmin = ref(false) + // 是否为日历管理员(在 calendar_admin 群组中) + const isCalendarAdmin = computed(() => + groups.value.some(g => g.name === 'calendar_admin') + ) + // 用户加入的群组名称列表(计算属性) const groupNames = computed(() => groups.value.map(g => g.name)) @@ -130,6 +135,7 @@ export const useUserStore = defineStore('user', () => { userCookie, isLoggedIn, isSysAdmin, + isCalendarAdmin, groups, groupNames, cookieValue, diff --git a/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue b/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue index bd12ab3..7fc16e6 100644 --- a/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue +++ b/frontend/ops_vue_js/src/views/calendar/CalendarAdminList.vue @@ -15,7 +15,6 @@ const usersStore = useUsersStore() const calendars = ref([]) const loading = ref(false) -const eventCounts = ref({}) // calendarId -> event count // 编辑相关 const showEditModal = ref(false) @@ -54,8 +53,6 @@ async function fetchCalendars() { usersStore.fetchUser(cal.UserID) } }) - // 获取每个日历的事件数量 - fetchEventCounts() } } catch { // 拦截器已处理 @@ -64,35 +61,6 @@ async function fetchCalendars() { } } -async function fetchEventCounts() { - // 获取一年前到现在的事件,用于统计 - const now = new Date() - const oneYearAgo = new Date() - oneYearAgo.setFullYear(now.getFullYear() - 1) - - const startStr = oneYearAgo.toISOString().split('T')[0] - const endStr = now.toISOString().split('T')[0] - - try { - const { errCode, data } = await calendarApi.getEvents({ - start_date: startStr, - end_date: endStr - }) - if (errCode === 0 && data.list) { - // 按日历ID统计事件数量 - const counts = {} - data.list.forEach(event => { - if (event.CalendarID) { - counts[event.CalendarID] = (counts[event.CalendarID] || 0) + 1 - } - }) - eventCounts.value = counts - } - } catch { - // 忽略错误 - } -} - function getCreatorName(userID) { return usersStore.getUsernameFromUserID(userID) || '...' } @@ -304,7 +272,7 @@ onMounted(fetchCalendars)
- {{ eventCounts[calendar.ID] || 0 }} + {{ calendar.event_count ?? 0 }}
diff --git a/frontend/ops_vue_js/src/views/calendar/CalendarList.vue b/frontend/ops_vue_js/src/views/calendar/CalendarList.vue index a9b8b35..64826e9 100644 --- a/frontend/ops_vue_js/src/views/calendar/CalendarList.vue +++ b/frontend/ops_vue_js/src/views/calendar/CalendarList.vue @@ -1,17 +1,20 @@