This commit is contained in:
2026-05-06 21:54:30 +08:00
parent 8dd971bec6
commit cd2c90ffb4
7 changed files with 84 additions and 9 deletions
+28 -7
View File
@@ -120,7 +120,7 @@ var (
calendarAdmins []uint
)
// CalendarUpdateAdminsCash 更新客户管理员缓存
// CalendarUpdateAdminsCash
func CalendarUpdateAdminsCash() {
calendarAdmins = nil
calendarAdmins = append(calendarAdmins, 1) // id=1 系统管理员默认拥有所有权限
@@ -152,6 +152,8 @@ func ApiCalendarInit() {
Name: "calendar_admin",
Type: "usergroup",
})
CalendarUpdateAdminsCash()
}
func ApiCalendar(r *gin.RouterGroup) {
@@ -429,22 +431,31 @@ func ApiCalendar(r *gin.RouterGroup) {
}
}
// 查询日历创建者(用于判断权限)
var calendarCreatorID uint
var calendar TabCalendar
if models.DB.Where("id = ?", calendarID).First(&calendar).Error == nil {
calendarCreatorID = calendar.UserID
}
var relist []map[string]interface{}
for _, event := range events {
eventMap, _ := json.Marshal(event)
var item map[string]interface{}
json.Unmarshal(eventMap, &item)
// 可编辑条件:事件创建者 或 日历管理员
// 可编辑条件:事件创建者 或 日历创建者 或 日历管理员
canEdit := false
if isLogin {
if event.UserID == currentUserID || slices.Contains(calendarAdmins, currentUserID) {
if event.UserID == currentUserID || calendarCreatorID == currentUserID || slices.Contains(calendarAdmins, currentUserID) {
canEdit = true
}
}
item["canEdit"] = canEdit
relist = append(relist, item)
}
//fmt.Println(calendarAdmins)
//fmt.Println(calendarUserGroup)
ReturnJson(ctx, "apiOK", gin.H{"list": relist})
})
@@ -528,8 +539,13 @@ func ApiCalendar(r *gin.RouterGroup) {
oldEvent := TabCalendarEvent{}
if models.DB.Where("id = ?", eventID).First(&oldEvent).Error == nil {
// 检查权限(只有创建人可以修改)
if oldEvent.UserID != user.ID {
// 检查权限(事件创建人、日历创建人或管理员可修改)
var calendarCreatorID uint
var calendar TabCalendar
if models.DB.Where("id = ?", oldEvent.CalendarID).First(&calendar).Error == nil {
calendarCreatorID = calendar.UserID
}
if !canModifyCalendar(user.ID, oldEvent.UserID) && calendarCreatorID != user.ID {
ReturnJson(ctx, "permission_denied", nil)
return
}
@@ -591,8 +607,13 @@ func ApiCalendar(r *gin.RouterGroup) {
if err := mapstructure.Decode(data, &from); err == nil {
oldEvent := TabCalendarEvent{}
if models.DB.Where("id = ?", from.ID).First(&oldEvent).Error == nil {
// 检查权限(只有创建人可以删除)
if oldEvent.UserID != user.ID {
// 检查权限(事件创建人、日历创建人或管理员可删除)
var calendarCreatorID uint
var calendar TabCalendar
if models.DB.Where("id = ?", oldEvent.CalendarID).First(&calendar).Error == nil {
calendarCreatorID = calendar.UserID
}
if !canModifyCalendar(user.ID, oldEvent.UserID) && calendarCreatorID != user.ID {
ReturnJson(ctx, "permission_denied", nil)
return
}
+2
View File
@@ -407,6 +407,8 @@ func ApiSysAdmin(r *gin.RouterGroup) {
WarehouseUpdateAdminsCash()
case "customer_admin":
CustomerUpdateAdminsCash()
case "calendar_admin":
CalendarUpdateAdminsCash()
}
ReturnJson(ctx, "apiOK", nil)
+17
View File
@@ -594,6 +594,23 @@ func ApiUser(r *gin.RouterGroup) {
}
redata["isSysAdmin"] = isSysAdmin
// 获取用户加入的群组列表
var binds []TabUserGroupBinds
models.DB.Where("user_id = ?", user.ID).Find(&binds)
var groups []map[string]interface{}
for _, bind := range binds {
var group TabUserGroups
if models.DB.Where("id = ?", bind.GroupID).First(&group).Error == nil {
groups = append(groups, map[string]interface{}{
"id": group.ID,
"name": group.Name,
"type": group.Type,
})
}
}
redata["groups"] = groups
ReturnJson(ctx, "apiOK", redata)
}
+3 -1
View File
@@ -442,7 +442,9 @@
"remark_hint": "Remark only",
"edit_profile": "Edit Profile",
"security": "Security",
"security_description": "Manage your account security settings"
"security_description": "Manage your account security settings",
"my_groups": "My Groups",
"no_groups": "Not joined any groups yet"
},
"button": {
"submit": "Submit",
+3 -1
View File
@@ -442,7 +442,9 @@
"remark_hint": "仅备注",
"edit_profile": "编辑资料",
"security": "安全设置",
"security_description": "管理您的账户安全设置"
"security_description": "管理您的账户安全设置",
"my_groups": "我的群组",
"no_groups": "暂未加入任何群组"
},
"button": {
"submit": "提交",
+9
View File
@@ -41,6 +41,7 @@ export const useUserStore = defineStore('user', () => {
const userInfo = ref(null) // TabUserInfo_ 详情
const userCookie = ref(null) // Cookie session
const isLoggedIn = ref(false)
const groups = ref([]) // 用户加入的群组列表
// ── Getters ──
const cookieValue = computed(() => userCookie.value?.Value ?? '')
@@ -63,6 +64,9 @@ export const useUserStore = defineStore('user', () => {
// 是否系统管理员(后端直接返回)
const isSysAdmin = ref(false)
// 用户加入的群组名称列表(计算属性)
const groupNames = computed(() => groups.value.map(g => g.name))
// ── Actions ──
function login(cookie) {
userCookie.value = cookie
@@ -86,6 +90,7 @@ export const useUserStore = defineStore('user', () => {
user.value = null
userInfo.value = null
isSysAdmin.value = false
groups.value = []
isLoggedIn.value = false
removeStorage(STORAGE_KEY_COOKIE)
}
@@ -98,6 +103,8 @@ export const useUserStore = defineStore('user', () => {
userInfo.value = data.userInfo ?? null
// 存储系统管理员状态
isSysAdmin.value = data.isSysAdmin === true
// 存储用户群组列表
groups.value = data.groups ?? []
}
} catch {
// 拦截器已处理错误提示
@@ -123,6 +130,8 @@ export const useUserStore = defineStore('user', () => {
userCookie,
isLoggedIn,
isSysAdmin,
groups,
groupNames,
cookieValue,
avatarUrl,
birthday,
@@ -95,6 +95,28 @@ onMounted(() => {
</div>
</RouterLink>
</div>
<!-- My Groups -->
<div class="mt-6">
<h2 class="mb-3 text-lg font-semibold text-gray-900 dark:text-dk-text">
{{ t('settings.my_groups') }}
</h2>
<div v-if="userStore.groups.length > 0" class="flex flex-wrap gap-2">
<span
v-for="group in userStore.groups"
:key="group.id"
class="inline-flex items-center rounded-full bg-purple-100 px-3 py-1 text-sm font-medium text-purple-800 dark:bg-purple-900/30 dark:text-purple-300"
>
<svg class="mr-1.5 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
{{ group.name }}
</span>
</div>
<div v-else class="rounded-lg border border-gray-200 bg-white p-4 text-center text-sm text-gray-500 dark:border-dk-muted dark:bg-dk-card dark:text-dk-subtle">
{{ t('settings.no_groups') }}
</div>
</div>
</div>
</div>
</template>