feat(event): 添加酒局编辑删除功能并优化状态管理
- 移除 UniAppWebSocket 类实现,使用独立的 websocket 模块 - 添加 UpdateEvent 和 DeleteEvent API 接口 - 在 EventCard 组件中添加发起人管理操作按钮 - 实现酒局状态数字枚举到字符串的映射转换 - 在 circle 页面集成编辑删除事件处理 - 重构 event-create 页面支持编辑模式 - 在 event-detail 页面添加发起人操作区域 - 实现删除确认对话框和页面返回刷新逻辑
This commit is contained in:
+14
-136
@@ -107,144 +107,9 @@ function uploadRequest(url, params) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 uni-app WebSocket API 实现的 WebSocket 类
|
|
||||||
* 符合之前定义的 WebsocketInterface(无类型约束)
|
|
||||||
*/
|
|
||||||
class UniAppWebSocket {
|
|
||||||
/**
|
|
||||||
* 构造函数:传入 WebSocket 服务器地址
|
|
||||||
* @param {string} host - 完整的 WebSocket URL,例如 'wss://example.com/ws'
|
|
||||||
*/
|
|
||||||
constructor(host) {
|
|
||||||
this.host = host;
|
|
||||||
// 事件回调属性(外部可赋值)
|
|
||||||
this.close = null; // 可赋值为函数,同时也可用主动关闭连接
|
|
||||||
this.onopen = null;
|
|
||||||
this.onmessage = null;
|
|
||||||
// 内部 SocketTask 实例
|
|
||||||
this.socketTask = null;
|
|
||||||
// 初始化连接
|
|
||||||
this._initSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 WebSocket 连接(私有方法,约定以 _ 开头)
|
|
||||||
*/
|
|
||||||
_initSocket() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
// 创建 SocketTask
|
|
||||||
this.socketTask = uni.connectSocket({
|
|
||||||
url: this.host,
|
|
||||||
success() {
|
|
||||||
// 连接创建成功(但尚未打开)
|
|
||||||
},
|
|
||||||
fail(err) {
|
|
||||||
// 连接创建失败,触发错误事件
|
|
||||||
const errorEvent = { type: 'error', error: err, target: self };
|
|
||||||
self.onerror(errorEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听打开事件
|
|
||||||
this.socketTask.onOpen((res) => {
|
|
||||||
if (self.onopen) {
|
|
||||||
// 构造一个简单的事件对象
|
|
||||||
const event = { type: 'open', target: self, ...res };
|
|
||||||
self.onopen(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听消息事件
|
|
||||||
this.socketTask.onMessage((res) => {
|
|
||||||
if (self.onmessage) {
|
|
||||||
// res.data 是消息数据
|
|
||||||
const event = {
|
|
||||||
type: 'message',
|
|
||||||
data: res.data,
|
|
||||||
target: self
|
|
||||||
};
|
|
||||||
self.onmessage(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听关闭事件
|
|
||||||
this.socketTask.onClose((res) => {
|
|
||||||
// 构造关闭事件
|
|
||||||
const event = {
|
|
||||||
type: 'close',
|
|
||||||
code: res.code,
|
|
||||||
reason: res.reason,
|
|
||||||
wasClean: res.wasClean,
|
|
||||||
target: self
|
|
||||||
};
|
|
||||||
// 调用 onclose 方法(可被重写)
|
|
||||||
self.onclose(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听错误事件
|
|
||||||
this.socketTask.onError((err) => {
|
|
||||||
const errorEvent = { type: 'error', error: err, target: self };
|
|
||||||
self.onerror(errorEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将 close 属性设置为一个可调用函数,用于主动断开连接
|
|
||||||
// 同时也满足接口的 “回调类型” 定义
|
|
||||||
this.close = function(ev) {
|
|
||||||
if (self.socketTask) {
|
|
||||||
const code = ev && ev.code ? ev.code : 1000;
|
|
||||||
const reason = ev && ev.reason ? ev.reason : '';
|
|
||||||
self.socketTask.close({ code, reason });
|
|
||||||
}
|
|
||||||
// 如果作为回调函数,返回 null 即可(无实际意义)
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭事件处理方法(可被外部重写)
|
|
||||||
* @param {Object} event - 关闭事件对象
|
|
||||||
*/
|
|
||||||
onclose(event) {
|
|
||||||
// 默认空实现,你可以在外部覆盖此方法
|
|
||||||
// console.log('连接已关闭', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误事件处理方法(可被外部重写)
|
|
||||||
* @param {Object} event - 错误事件对象
|
|
||||||
*/
|
|
||||||
onerror(event) {
|
|
||||||
// 默认空实现
|
|
||||||
// console.error('连接错误', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送数据
|
|
||||||
* @param {string|ArrayBuffer|Blob} data - 要发送的数据
|
|
||||||
* @throws {Error} 如果 WebSocket 未初始化
|
|
||||||
*/
|
|
||||||
send(data) {
|
|
||||||
if (!this.socketTask) {
|
|
||||||
throw new Error('WebSocket 未初始化,无法发送数据');
|
|
||||||
}
|
|
||||||
this.socketTask.send({
|
|
||||||
data: data,
|
|
||||||
success() {
|
|
||||||
// 发送成功,可添加日志
|
|
||||||
},
|
|
||||||
fail(err) {
|
|
||||||
// 发送失败,触发错误事件
|
|
||||||
const errorEvent = { type: 'error', error: err, target: this };
|
|
||||||
this.onerror(errorEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 初始化 SDK 实例
|
// 初始化 SDK 实例
|
||||||
|
// (WebSocket 适配器使用 common/uni-websocket.js,已修复 wx.connectSocket 降级问题)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
const client = new HaveADrink({
|
const client = new HaveADrink({
|
||||||
host: API_HOST,
|
host: API_HOST,
|
||||||
@@ -397,6 +262,19 @@ export async function CreateEvent({ title, time, location, maxPeople, note, lati
|
|||||||
return { data: res }
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 修改酒局(仅发起人可改,后端会校验归属) */
|
||||||
|
export async function UpdateEvent({ eventId, title, time, location, maxPeople, note, latitude, longitude }) {
|
||||||
|
const geo = (latitude != null && longitude != null) ? { latitude, longitude } : undefined
|
||||||
|
const res = await client.UpdateEvent({ id: eventId, title, time, location, maxPeople, geo, note })
|
||||||
|
return { data: res }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除酒局(仅发起人可删,后端会校验归属) */
|
||||||
|
export async function DeleteEvent({ eventId }) {
|
||||||
|
const res = await client.DeleteEvent({ id: eventId })
|
||||||
|
return { data: res }
|
||||||
|
}
|
||||||
|
|
||||||
/** 报名酒局 */
|
/** 报名酒局 */
|
||||||
export async function JoinEvent({ eventId }) {
|
export async function JoinEvent({ eventId }) {
|
||||||
const res = await client.JoinEvent({ id: eventId })
|
const res = await client.JoinEvent({ id: eventId })
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="event-card" @click="$emit('item-click', event)">
|
<view class="event-card" @click="$emit('item-click', event)">
|
||||||
<!-- 状态标签 -->
|
<!-- 状态标签 -->
|
||||||
<view class="event-status" :class="'status-' + event.status">
|
<view class="event-status" :class="'status-' + statusKey">
|
||||||
<text class="event-status-text">{{ statusLabel }}</text>
|
<text class="event-status-text">{{ statusLabel }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -32,7 +32,16 @@
|
|||||||
</view>
|
</view>
|
||||||
<text class="event-org-name">{{ event.organizer ? event.organizer.nickname : '' }}</text>
|
<text class="event-org-name">{{ event.organizer ? event.organizer.nickname : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="event-action" v-if="event.status === 'open'">
|
<!-- 发起人:编辑/删除 -->
|
||||||
|
<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" v-if="event.isJoined">✓ 已报名</text>
|
||||||
<text class="event-action-text event-action-join" v-else>报名</text>
|
<text class="event-action-text event-action-join" v-else>报名</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -44,14 +53,21 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'EventCard',
|
name: 'EventCard',
|
||||||
props: {
|
props: {
|
||||||
event: { type: Object, default: () => ({}) }
|
event: { type: Object, default: () => ({}) },
|
||||||
|
/** 是否为发起人(可编辑/删除) */
|
||||||
|
canManage: { type: Boolean, default: false }
|
||||||
},
|
},
|
||||||
emits: ['item-click'],
|
emits: ['item-click', 'edit', 'delete'],
|
||||||
computed: {
|
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() {
|
statusLabel() {
|
||||||
const val = String(this.event.status).toLowerCase()
|
|
||||||
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||||||
return map[val] || '报名中'
|
return map[this.statusKey] || '报名中'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,4 +198,29 @@ export default {
|
|||||||
background: linear-gradient(135deg, $amber, $amber-deep);
|
background: linear-gradient(135deg, $amber, $amber-deep);
|
||||||
font-weight: $fw-bold;
|
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>
|
</style>
|
||||||
|
|||||||
+27
-1
@@ -109,6 +109,9 @@
|
|||||||
v-for="evt in events"
|
v-for="evt in events"
|
||||||
:key="evt.id"
|
:key="evt.id"
|
||||||
:event="evt"
|
:event="evt"
|
||||||
|
:can-manage="evt.isOrganizer === true"
|
||||||
|
@edit="goEditEvent"
|
||||||
|
@delete="confirmDeleteEvent"
|
||||||
@item-click="goEventDetail"
|
@item-click="goEventDetail"
|
||||||
/>
|
/>
|
||||||
<EmptyState
|
<EmptyState
|
||||||
@@ -134,7 +137,7 @@ import FeedCard from '../../components/FeedCard.vue'
|
|||||||
import FriendItem from '../../components/FriendItem.vue'
|
import FriendItem from '../../components/FriendItem.vue'
|
||||||
import EventCard from '../../components/EventCard.vue'
|
import EventCard from '../../components/EventCard.vue'
|
||||||
import EmptyState from '../../components/EmptyState.vue'
|
import EmptyState from '../../components/EmptyState.vue'
|
||||||
import { GetCircleFeeds, LikeFeed, UnlikeFeed, DeleteFeed, GetFriends, GetFriendRequests, GetEvents, GetUnreadCount } from '../../common/api'
|
import { GetCircleFeeds, LikeFeed, UnlikeFeed, DeleteFeed, GetFriends, GetFriendRequests, GetEvents, DeleteEvent, GetUnreadCount } from '../../common/api'
|
||||||
import wsManager from '../../common/websocket'
|
import wsManager from '../../common/websocket'
|
||||||
import { getMyInviteCode } from '../../common/invite'
|
import { getMyInviteCode } from '../../common/invite'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
@@ -375,6 +378,29 @@ export default {
|
|||||||
goEventDetail(evt) {
|
goEventDetail(evt) {
|
||||||
uni.navigateTo({ url: `/pages/event-detail/event-detail?id=${evt.id}` })
|
uni.navigateTo({ url: `/pages/event-detail/event-detail?id=${evt.id}` })
|
||||||
},
|
},
|
||||||
|
/** 编辑自己发起的酒局(复用发起页的编辑模式) */
|
||||||
|
goEditEvent(evt) {
|
||||||
|
uni.navigateTo({ url: `/pages/event-create/event-create?id=${evt.id}` })
|
||||||
|
},
|
||||||
|
/** 删除自己发起的酒局(二次确认) */
|
||||||
|
confirmDeleteEvent(evt) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除酒局',
|
||||||
|
content: '确定要删除这场酒局吗?删除后不可恢复',
|
||||||
|
confirmText: '删除',
|
||||||
|
confirmColor: '#FF6B6B',
|
||||||
|
success: async (res) => {
|
||||||
|
if (!res.confirm) return
|
||||||
|
try {
|
||||||
|
await DeleteEvent({ eventId: evt.id })
|
||||||
|
this.events = this.events.filter(e => String(e.id) !== String(evt.id))
|
||||||
|
uni.showToast({ title: '已删除', icon: 'none' })
|
||||||
|
} catch (e) {
|
||||||
|
// httpRequest 已全局弹错误提示(如非本人酒局后端会拒绝)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
goCreateEvent() {
|
goCreateEvent() {
|
||||||
uni.navigateTo({ url: '/pages/event-create/event-create' })
|
uni.navigateTo({ url: '/pages/event-create/event-create' })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<view class="create-back" @click="goBack">
|
<view class="create-back" @click="goBack">
|
||||||
<text class="create-back-icon">‹</text>
|
<text class="create-back-icon">‹</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="create-nav-title">发起酒局</text>
|
<text class="create-nav-title">{{ isEdit ? '编辑酒局' : '发起酒局' }}</text>
|
||||||
<view class="create-nav-ph"></view>
|
<view class="create-nav-ph"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 发布按钮 -->
|
<!-- 发布/保存按钮 -->
|
||||||
<button class="create-submit" :class="{ disabled: !canSubmit }" @click="handleSubmit">
|
<button class="create-submit" :class="{ disabled: !canSubmit }" @click="handleSubmit">
|
||||||
发布酒局
|
{{ isEdit ? '保存修改' : '发布酒局' }}
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { CreateEvent } from '../../common/api'
|
import { CreateEvent, UpdateEvent, GetEventDetail } from '../../common/api'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -132,12 +132,24 @@ export default {
|
|||||||
maxPeople: 6,
|
maxPeople: 6,
|
||||||
note: ''
|
note: ''
|
||||||
},
|
},
|
||||||
submitting: false
|
submitting: false,
|
||||||
|
// 编辑模式:携带酒局ID时进入编辑态
|
||||||
|
editId: '',
|
||||||
|
loadingDetail: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
if (options && options.id) {
|
||||||
|
this.editId = options.id
|
||||||
|
this.loadEventDetail()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isEdit() {
|
||||||
|
return !!this.editId
|
||||||
|
},
|
||||||
canSubmit() {
|
canSubmit() {
|
||||||
return this.form.title.trim() && this.form.date && this.form.time && this.form.location.trim() && !this.submitting
|
return this.form.title.trim() && this.form.date && this.form.time && this.form.location.trim() && !this.submitting && !this.loadingDetail
|
||||||
},
|
},
|
||||||
locationMarkers() {
|
locationMarkers() {
|
||||||
if (!this.form.latitude) return []
|
if (!this.form.latitude) return []
|
||||||
@@ -155,6 +167,35 @@ export default {
|
|||||||
goBack() {
|
goBack() {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
},
|
},
|
||||||
|
/** 编辑模式:拉取酒局详情回填表单 */
|
||||||
|
async loadEventDetail() {
|
||||||
|
this.loadingDetail = true
|
||||||
|
try {
|
||||||
|
const res = await GetEventDetail({ eventId: this.editId })
|
||||||
|
const evt = res.data || {}
|
||||||
|
this.form.title = evt.title || ''
|
||||||
|
// 后端 time 格式如 "2026-08-08 20:00:00",拆分为日期/时间两个选择器字段
|
||||||
|
const timeStr = String(evt.time || '')
|
||||||
|
const m = timeStr.match(/^(\d{4}-\d{2}-\d{2})[ T]?(\d{2}:\d{2})?/)
|
||||||
|
if (m) {
|
||||||
|
this.form.date = m[1]
|
||||||
|
this.form.time = m[2] || ''
|
||||||
|
}
|
||||||
|
this.form.location = evt.location || ''
|
||||||
|
this.form.address = evt.address || ''
|
||||||
|
// 兼容顶层字段与 geo 对象两种返回
|
||||||
|
const geo = evt.geo || {}
|
||||||
|
this.form.latitude = evt.latitude != null ? evt.latitude : (geo.latitude != null ? geo.latitude : null)
|
||||||
|
this.form.longitude = evt.longitude != null ? evt.longitude : (geo.longitude != null ? geo.longitude : null)
|
||||||
|
this.form.maxPeople = Number(evt.maxPeople) || this.form.maxPeople
|
||||||
|
this.form.note = evt.note || ''
|
||||||
|
} catch (e) {
|
||||||
|
// 加载失败时返回上一页,避免空表单误改
|
||||||
|
uni.showToast({ title: '加载酒局失败', icon: 'none' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 800)
|
||||||
|
}
|
||||||
|
this.loadingDetail = false
|
||||||
|
},
|
||||||
onDateChange(e) {
|
onDateChange(e) {
|
||||||
this.form.date = e.detail.value
|
this.form.date = e.detail.value
|
||||||
},
|
},
|
||||||
@@ -201,18 +242,24 @@ export default {
|
|||||||
async handleSubmit() {
|
async handleSubmit() {
|
||||||
if (!this.canSubmit) return
|
if (!this.canSubmit) return
|
||||||
this.submitting = true
|
this.submitting = true
|
||||||
|
const payload = {
|
||||||
|
title: this.form.title,
|
||||||
|
time: `${this.form.date} ${this.form.time}:00`,
|
||||||
|
location: this.form.location,
|
||||||
|
address: this.form.address,
|
||||||
|
latitude: this.form.latitude,
|
||||||
|
longitude: this.form.longitude,
|
||||||
|
maxPeople: this.form.maxPeople,
|
||||||
|
note: this.form.note
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await CreateEvent({
|
if (this.isEdit) {
|
||||||
title: this.form.title,
|
await UpdateEvent({ eventId: this.editId, ...payload })
|
||||||
time: `${this.form.date} ${this.form.time}:00`,
|
uni.showToast({ title: '修改已保存 ✅', icon: 'none' })
|
||||||
location: this.form.location,
|
} else {
|
||||||
address: this.form.address,
|
await CreateEvent(payload)
|
||||||
latitude: this.form.latitude,
|
uni.showToast({ title: '酒局已发布 🎉', icon: 'none' })
|
||||||
longitude: this.form.longitude,
|
}
|
||||||
maxPeople: this.form.maxPeople,
|
|
||||||
note: this.form.note
|
|
||||||
})
|
|
||||||
uni.showToast({ title: '酒局已发布 🎉', icon: 'none' })
|
|
||||||
setTimeout(() => uni.navigateBack(), 800)
|
setTimeout(() => uni.navigateBack(), 800)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 全局拦截器已弹出错误提示
|
// 全局拦截器已弹出错误提示
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<scroll-view class="evt-scroll" scroll-y>
|
<scroll-view class="evt-scroll" scroll-y>
|
||||||
<view class="evt-body" v-if="event">
|
<view class="evt-body" v-if="event">
|
||||||
<!-- 状态 -->
|
<!-- 状态 -->
|
||||||
<view class="evt-status" :class="'status-' + event.status">
|
<view class="evt-status" :class="'status-' + statusKey">
|
||||||
<text class="evt-status-text">{{ statusLabel }}</text>
|
<text class="evt-status-text">{{ statusLabel }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -94,42 +94,47 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部操作栏 -->
|
<!-- 底部操作栏 -->
|
||||||
<view class="evt-action-bar" v-if="event">
|
<view class="evt-action-bar" v-if="event">
|
||||||
<button
|
<button
|
||||||
v-if="(event.status === 'open' || event.status === 'upcoming') && !event.isJoined && !event.isOrganizer"
|
v-if="canJoin && !event.isJoined && !event.isOrganizer"
|
||||||
class="evt-btn evt-btn-primary"
|
class="evt-btn evt-btn-primary"
|
||||||
@click="handleJoin"
|
@click="handleJoin"
|
||||||
>
|
>
|
||||||
报名参加
|
报名参加
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="(event.status === 'open' || event.status === 'upcoming') && event.isJoined && !event.isOrganizer"
|
v-if="canJoin && event.isJoined && !event.isOrganizer"
|
||||||
class="evt-btn evt-btn-ghost"
|
class="evt-btn evt-btn-ghost"
|
||||||
@click="handleQuit"
|
@click="handleQuit"
|
||||||
>
|
>
|
||||||
取消报名
|
取消报名
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="event.status === 'ongoing' && event.isJoined"
|
v-if="statusKey === 'ongoing' && event.isJoined"
|
||||||
class="evt-btn evt-btn-primary"
|
class="evt-btn evt-btn-primary"
|
||||||
@click="handleCheckIn"
|
@click="handleCheckIn"
|
||||||
>
|
>
|
||||||
到场签到
|
到场签到
|
||||||
</button>
|
</button>
|
||||||
<view v-if="event.status === 'ended'" class="evt-ended-tip">
|
<view v-if="statusKey === 'ended' && !event.isOrganizer" class="evt-ended-tip">
|
||||||
<text class="evt-ended-text">酒局已结束</text>
|
<text class="evt-ended-text">酒局已结束</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="event.isOrganizer" class="evt-ended-tip">
|
<!-- 发起人:编辑/删除自己的酒局 -->
|
||||||
<text class="evt-ended-text">你是发起人</text>
|
<view v-if="event.isOrganizer" class="evt-org-actions">
|
||||||
|
<button class="evt-btn evt-btn-ghost" @click="goEdit">
|
||||||
|
✏️ 编辑酒局
|
||||||
|
</button>
|
||||||
|
<button class="evt-btn evt-btn-danger" @click="confirmDelete">
|
||||||
|
🗑️ 删除酒局
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GetEventDetail, JoinEvent, QuitEvent, CheckInEvent } from '../../common/api'
|
import { GetEventDetail, JoinEvent, QuitEvent, CheckInEvent, DeleteEvent } from '../../common/api'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -143,11 +148,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
statusLabel() {
|
/** 后端状态为数字枚举:1=报名中 2=待开始 3=进行中 4=已结束(兼容字符串返回) */
|
||||||
|
statusKey() {
|
||||||
if (!this.event) return ''
|
if (!this.event) return ''
|
||||||
const val = String(this.event.status).toLowerCase()
|
const map = { 1: 'open', 2: 'upcoming', 3: 'ongoing', 4: 'ended' }
|
||||||
|
const raw = this.event.status
|
||||||
|
return map[Number(raw)] || String(raw).toLowerCase()
|
||||||
|
},
|
||||||
|
/** 报名中/待开始阶段可报名或取消 */
|
||||||
|
canJoin() {
|
||||||
|
return this.statusKey === 'open' || this.statusKey === 'upcoming'
|
||||||
|
},
|
||||||
|
statusLabel() {
|
||||||
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||||||
return map[val] || '报名中'
|
return map[this.statusKey] || '报名中'
|
||||||
},
|
},
|
||||||
eventMarkers() {
|
eventMarkers() {
|
||||||
if (!this.event || !this.event.latitude) return []
|
if (!this.event || !this.event.latitude) return []
|
||||||
@@ -165,6 +179,12 @@ export default {
|
|||||||
this.eventId = options.id || ''
|
this.eventId = options.id || ''
|
||||||
this.loadDetail()
|
this.loadDetail()
|
||||||
},
|
},
|
||||||
|
onShow() {
|
||||||
|
// 从编辑页返回时刷新详情
|
||||||
|
if (this.eventId && this._hasLoaded) {
|
||||||
|
this.loadDetail()
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
@@ -173,10 +193,34 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const res = await GetEventDetail({ eventId: this.eventId })
|
const res = await GetEventDetail({ eventId: this.eventId })
|
||||||
this.event = res.data
|
this.event = res.data
|
||||||
|
this._hasLoaded = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('加载酒局详情失败', e)
|
console.warn('加载酒局详情失败', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** 发起人编辑酒局(复用发起页的编辑模式) */
|
||||||
|
goEdit() {
|
||||||
|
uni.navigateTo({ url: `/pages/event-create/event-create?id=${this.eventId}` })
|
||||||
|
},
|
||||||
|
/** 发起人删除酒局(二次确认,成功后返回上一页) */
|
||||||
|
confirmDelete() {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除酒局',
|
||||||
|
content: '确定要删除这场酒局吗?删除后不可恢复',
|
||||||
|
confirmText: '删除',
|
||||||
|
confirmColor: '#FF6B6B',
|
||||||
|
success: async (res) => {
|
||||||
|
if (!res.confirm) return
|
||||||
|
try {
|
||||||
|
await DeleteEvent({ eventId: this.eventId })
|
||||||
|
uni.showToast({ title: '已删除', icon: 'none' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 800)
|
||||||
|
} catch (e) {
|
||||||
|
// httpRequest 已全局弹错误提示
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
async handleJoin() {
|
async handleJoin() {
|
||||||
try {
|
try {
|
||||||
await JoinEvent({ eventId: this.eventId })
|
await JoinEvent({ eventId: this.eventId })
|
||||||
@@ -525,4 +569,19 @@ export default {
|
|||||||
font-size: $fs-base;
|
font-size: $fs-base;
|
||||||
color: $text-tertiary;
|
color: $text-tertiary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.evt-org-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: $sp-md;
|
||||||
|
|
||||||
|
.evt-btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.evt-btn-danger {
|
||||||
|
background: rgba(255,107,107,0.1);
|
||||||
|
color: $coral;
|
||||||
|
border: 1rpx solid rgba(255,107,107,0.25);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user