fix: bug fixes and UX improvements (v0.3.5)

- Fix clipboard copy error in HTTP environment with execCommand fallback
- Fix team invite page not loading user groups, always showing "join group first"
- Fix JoinGroupPage isMember check using group object instead of user ID
- Fix cancelRSVP deleting all users' RSVP records instead of current user's
- Fix event detail not loading event data itself
- Fix event comment avatar URL missing PocketBase baseUrl prefix
- Fix event creation missing endTime > startTime validation
- Fix event manage/delete permission split (creator+owner vs creator+owner)
- Fix event create button only visible to admins, now all members can create
- Fix event expand not subscribing to comments/RSVP realtime updates
- Fix event relative time not using status field
- Remove duplicate create/join group buttons from header and welcome bar
- Refactor team invite link to use API function

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
congsh
2026-04-21 22:19:18 +08:00
parent 2fec2108ca
commit a062889a11
13 changed files with 125 additions and 52 deletions
+22 -8
View File
@@ -9,7 +9,7 @@ import type { Event } from '@/types'
import { Plus } from '@element-plus/icons-vue'
import CreateEventDialog from './CreateEventDialog.vue'
import EventCard from './EventCard.vue'
import { subscribeEvents } from '@/api/events'
import { subscribeEvents, subscribeEventComments, subscribeEventRSVPs } from '@/api/events'
const route = useRoute()
const eventStore = useEventStore()
@@ -20,9 +20,11 @@ const showCreateDialog = ref(false)
const editingEvent = ref<Event | null>(null)
const expandedEventId = ref<string | null>(null)
const canManage = computed(() => groupStore.canManageGroup)
const isMember = computed(() => !!groupStore.currentGroup)
let unsubscribeFn: (() => void) | null = null
let unsubscribeCommentsFn: (() => void) | null = null
let unsubscribeRSVPsFn: (() => void) | null = null
onMounted(async () => {
await eventStore.loadEvents(groupId)
@@ -35,19 +37,31 @@ onMounted(async () => {
})
onUnmounted(() => {
if (unsubscribeFn) {
unsubscribeFn()
unsubscribeFn = null
}
if (unsubscribeFn) unsubscribeFn()
if (unsubscribeCommentsFn) unsubscribeCommentsFn()
if (unsubscribeRSVPsFn) unsubscribeRSVPsFn()
unsubscribeFn = null
unsubscribeCommentsFn = null
unsubscribeRSVPsFn = null
eventStore.clear()
})
async function toggleExpand(event: Event) {
if (expandedEventId.value === event.id) {
expandedEventId.value = null
if (unsubscribeCommentsFn) { unsubscribeCommentsFn(); unsubscribeCommentsFn = null }
if (unsubscribeRSVPsFn) { unsubscribeRSVPsFn(); unsubscribeRSVPsFn = null }
} else {
expandedEventId.value = event.id
await eventStore.loadCommentsAndRSVPs(event.id)
if (unsubscribeCommentsFn) unsubscribeCommentsFn()
if (unsubscribeRSVPsFn) unsubscribeRSVPsFn()
unsubscribeCommentsFn = await subscribeEventComments(event.id, () => {
eventStore.loadCommentsAndRSVPs(event.id)
})
unsubscribeRSVPsFn = await subscribeEventRSVPs(event.id, () => {
eventStore.loadCommentsAndRSVPs(event.id)
})
}
}
@@ -99,7 +113,7 @@ function onDialogClosed() {
<div class="event-list">
<div class="list-header">
<h2 class="list-title">群组活动</h2>
<button v-if="canManage" class="create-btn" @click="showCreateDialog = true">
<button v-if="isMember" class="create-btn" @click="showCreateDialog = true">
<el-icon><Plus /></el-icon> 发起活动
</button>
</div>
@@ -111,7 +125,7 @@ function onDialogClosed() {
<div v-else-if="eventStore.events.length === 0" class="empty-state">
<p>暂无活动</p>
<p v-if="canManage" class="empty-hint">点击右上角发起一个活动吧</p>
<p v-if="isMember" class="empty-hint">点击右上角发起一个活动吧</p>
</div>
<div v-else class="event-sections">