- 移除 UniAppWebSocket 类实现,使用独立的 websocket 模块 - 添加 UpdateEvent 和 DeleteEvent API 接口 - 在 EventCard 组件中添加发起人管理操作按钮 - 实现酒局状态数字枚举到字符串的映射转换 - 在 circle 页面集成编辑删除事件处理 - 重构 event-create 页面支持编辑模式 - 在 event-detail 页面添加发起人操作区域 - 实现删除确认对话框和页面返回刷新逻辑
227 lines
5.3 KiB
Vue
227 lines
5.3 KiB
Vue
<template>
|
||
<view class="event-card" @click="$emit('item-click', event)">
|
||
<!-- 状态标签 -->
|
||
<view class="event-status" :class="'status-' + statusKey">
|
||
<text class="event-status-text">{{ statusLabel }}</text>
|
||
</view>
|
||
|
||
<!-- 标题 -->
|
||
<text class="event-title">{{ event.title }}</text>
|
||
|
||
<!-- 信息行 -->
|
||
<view class="event-info">
|
||
<view class="event-info-row">
|
||
<text class="event-info-icon">🕐</text>
|
||
<text class="event-info-text">{{ event.time }}</text>
|
||
</view>
|
||
<view class="event-info-row">
|
||
<text class="event-info-icon">📍</text>
|
||
<text class="event-info-text">{{ event.location }}</text>
|
||
</view>
|
||
<view class="event-info-row">
|
||
<text class="event-info-icon">👥</text>
|
||
<text class="event-info-text">{{ event.joined }}/{{ event.maxPeople }} 人已报名</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部:发起人 + 操作 -->
|
||
<view class="event-footer">
|
||
<view class="event-organizer">
|
||
<view class="event-org-avatar">
|
||
<text class="event-org-text">{{ event.organizer ? event.organizer.nickname[0] : '酒' }}</text>
|
||
</view>
|
||
<text class="event-org-name">{{ event.organizer ? event.organizer.nickname : '' }}</text>
|
||
</view>
|
||
<!-- 发起人:编辑/删除 -->
|
||
<view class="event-manage" v-if="canManage" @click.stop>
|
||
<view class="event-manage-btn" @click.stop="$emit('edit', event)">
|
||
<text class="event-manage-text">✏️ 编辑</text>
|
||
</view>
|
||
<view class="event-manage-btn" @click.stop="$emit('delete', event)">
|
||
<text class="event-manage-text event-manage-del">🗑️ 删除</text>
|
||
</view>
|
||
</view>
|
||
<view class="event-action" v-else-if="statusKey === 'open' || statusKey === 'upcoming'">
|
||
<text class="event-action-text" v-if="event.isJoined">✓ 已报名</text>
|
||
<text class="event-action-text event-action-join" v-else>报名</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'EventCard',
|
||
props: {
|
||
event: { type: Object, default: () => ({}) },
|
||
/** 是否为发起人(可编辑/删除) */
|
||
canManage: { type: Boolean, default: false }
|
||
},
|
||
emits: ['item-click', 'edit', 'delete'],
|
||
computed: {
|
||
/** 后端状态为数字枚举:1=报名中 2=待开始 3=进行中 4=已结束(兼容字符串返回) */
|
||
statusKey() {
|
||
const map = { 1: 'open', 2: 'upcoming', 3: 'ongoing', 4: 'ended' }
|
||
const raw = this.event.status
|
||
return map[Number(raw)] || String(raw).toLowerCase()
|
||
},
|
||
statusLabel() {
|
||
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||
return map[this.statusKey] || '报名中'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.event-card {
|
||
background: $bg-card;
|
||
border-radius: $radius-xl;
|
||
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
|
||
padding: $sp-xl;
|
||
margin-bottom: $sp-md;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&:active {
|
||
background: $bg-card-alt;
|
||
}
|
||
}
|
||
|
||
.event-status {
|
||
display: inline-flex;
|
||
padding: 6rpx 16rpx;
|
||
border-radius: $radius-full;
|
||
margin-bottom: $sp-md;
|
||
|
||
&.status-open {
|
||
background: rgba(94,198,160,0.12);
|
||
}
|
||
&.status-upcoming {
|
||
background: rgba(139,133,184,0.12);
|
||
}
|
||
&.status-ongoing {
|
||
background: rgba(232,168,56,0.12);
|
||
}
|
||
&.status-ended {
|
||
background: $bg-elevated;
|
||
}
|
||
}
|
||
|
||
.event-status-text {
|
||
font-size: $fs-xs;
|
||
font-weight: $fw-medium;
|
||
|
||
.status-open & { color: $mint; }
|
||
.status-upcoming & { color: $lavender; }
|
||
.status-ongoing & { color: $amber; }
|
||
.status-ended & { color: $text-tertiary; }
|
||
}
|
||
|
||
.event-title {
|
||
display: block;
|
||
font-size: $fs-lg;
|
||
font-weight: $fw-bold;
|
||
color: $text-primary;
|
||
margin-bottom: $sp-lg;
|
||
}
|
||
|
||
.event-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $sp-sm;
|
||
margin-bottom: $sp-lg;
|
||
}
|
||
|
||
.event-info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $sp-sm;
|
||
}
|
||
|
||
.event-info-icon {
|
||
font-size: $fs-sm;
|
||
width: 36rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.event-info-text {
|
||
font-size: $fs-sm;
|
||
color: $text-secondary;
|
||
}
|
||
|
||
.event-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding-top: $sp-md;
|
||
border-top: 1rpx solid var(--divider-color, rgba(255,255,255,0.08));
|
||
}
|
||
|
||
.event-organizer {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $sp-sm;
|
||
}
|
||
|
||
.event-org-avatar {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background: $bg-elevated;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.event-org-text {
|
||
font-size: $fs-xs;
|
||
font-weight: $fw-bold;
|
||
color: $lavender;
|
||
}
|
||
|
||
.event-org-name {
|
||
font-size: $fs-sm;
|
||
color: $text-tertiary;
|
||
}
|
||
|
||
.event-action-text {
|
||
font-size: $fs-sm;
|
||
color: $text-tertiary;
|
||
padding: 8rpx 24rpx;
|
||
border-radius: $radius-full;
|
||
background: $bg-elevated;
|
||
}
|
||
|
||
.event-action-join {
|
||
color: $text-on-amber;
|
||
background: linear-gradient(135deg, $amber, $amber-deep);
|
||
font-weight: $fw-bold;
|
||
}
|
||
|
||
.event-manage {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $sp-md;
|
||
}
|
||
|
||
.event-manage-btn {
|
||
padding: 8rpx 20rpx;
|
||
border-radius: $radius-full;
|
||
background: $bg-elevated;
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.event-manage-text {
|
||
font-size: $fs-sm;
|
||
color: $text-secondary;
|
||
}
|
||
|
||
.event-manage-del {
|
||
color: $coral;
|
||
}
|
||
</style>
|