Files
hejiu/pages/event-create/event-create.vue
T
cg c7c023975d feat(circle): 集成HaveADrink SDK实现酒友圈功能
- 集成HaveADrink SDK版本从1.0.4升级至1.0.8
- 实现酒友圈API接口,替换原有的mock数据
- 添加地理位置权限配置(requiredPrivateInfos)
- 重构API调用方式,统一使用client实例调用真实接口
- 更新WebSocket协议与SDK保持一致,支持聊天、输入状态、已读回执等功能
- 实现游标分页获取动态信息流
- 添加文件上传API支持图片上传
- 优化API错误处理,支持业务级错误提示
- 调整酒局状态显示,新增upcoming待开始状态
- 优化自我感觉标签映射逻辑,支持更多状态类型
- 更新websocket连接认证方式,使用Authorization header传递token
- 添加服务端连接确认和被踢下线事件处理
2026-07-22 23:03:40 +08:00

491 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view :class="themeClass" class="page-container create-page">
<!-- 顶部导航 -->
<view class="create-nav" :style="{ paddingTop: navPaddingTop }">
<view class="create-nav-inner">
<view class="create-back" @click="goBack">
<text class="create-back-icon"></text>
</view>
<text class="create-nav-title">发起酒局</text>
<view class="create-nav-ph"></view>
</view>
</view>
<scroll-view class="create-scroll" scroll-y>
<view class="create-body">
<!-- 标题 -->
<view class="form-group">
<text class="form-label">酒局名称</text>
<input
class="form-input"
v-model="form.title"
placeholder="例如:周五夜·老地方火锅局"
placeholder-class="form-placeholder"
:maxlength="30"
/>
</view>
<!-- 时间 -->
<view class="form-group">
<text class="form-label">时间</text>
<picker mode="date" :value="form.date" @change="onDateChange">
<view class="form-picker">
<text :class="form.date ? 'form-picker-text' : 'form-picker-ph'">{{ form.date || '选择日期' }}</text>
<text class="form-picker-arrow"></text>
</view>
</picker>
<picker mode="time" :value="form.time" @change="onTimeChange">
<view class="form-picker" style="margin-top: 16rpx;">
<text :class="form.time ? 'form-picker-text' : 'form-picker-ph'">{{ form.time || '选择时间' }}</text>
<text class="form-picker-arrow"></text>
</view>
</picker>
</view>
<!-- 地点 -->
<view class="form-group">
<text class="form-label">地点</text>
<!-- 已选地点显示地图预览 -->
<view v-if="form.latitude" class="location-preview">
<map
class="location-map"
:latitude="form.latitude"
:longitude="form.longitude"
:markers="locationMarkers"
:scale="15"
:show-location="false"
@click="chooseLocation"
></map>
<view class="location-info">
<text class="location-name">{{ form.location }}</text>
<text class="location-address">{{ form.address || '' }}</text>
</view>
<view class="location-change" @click="chooseLocation">
<text class="location-change-text">重新选择</text>
</view>
</view>
<!-- 未选地点点击选择 -->
<view v-else class="location-picker" @click="chooseLocation">
<text class="location-picker-icon">📍</text>
<text class="location-picker-text">点击选择地点</text>
<text class="location-picker-arrow"></text>
</view>
</view>
<!-- 人数 -->
<view class="form-group">
<text class="form-label">人数上限</text>
<view class="form-stepper">
<view class="stepper-btn" @click="changePeople(-1)">
<text class="stepper-btn-text"></text>
</view>
<text class="stepper-value">{{ form.maxPeople }}</text>
<view class="stepper-btn" @click="changePeople(1)">
<text class="stepper-btn-text">+</text>
</view>
</view>
</view>
<!-- 备注 -->
<view class="form-group">
<text class="form-label">备注选填</text>
<textarea
class="form-textarea"
v-model="form.note"
placeholder="例如:自带酒水,AA制餐费"
placeholder-class="form-placeholder"
:maxlength="200"
/>
</view>
<!-- 发布按钮 -->
<button class="create-submit" :class="{ disabled: !canSubmit }" @click="handleSubmit">
发布酒局
</button>
</view>
</scroll-view>
</view>
</template>
<script>
import { CreateEvent } from '../../common/api'
import themeMixin from '../../common/theme-mixin'
export default {
mixins: [themeMixin],
data() {
const menuBtn = uni.getMenuButtonBoundingClientRect()
const now = new Date()
const pad = n => String(n).padStart(2, '0')
const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
const curTime = `${pad(now.getHours())}:${pad(now.getMinutes())}`
return {
navPaddingTop: menuBtn.top + 'px',
form: {
title: '',
date: today,
time: curTime,
location: '',
address: '',
latitude: null,
longitude: null,
maxPeople: 6,
note: ''
},
submitting: false
}
},
computed: {
canSubmit() {
return this.form.title.trim() && this.form.date && this.form.time && this.form.location.trim() && !this.submitting
},
locationMarkers() {
if (!this.form.latitude) return []
return [{
id: 1,
latitude: this.form.latitude,
longitude: this.form.longitude,
title: this.form.location,
width: 28,
height: 38
}]
}
},
methods: {
goBack() {
uni.navigateBack()
},
onDateChange(e) {
this.form.date = e.detail.value
},
onTimeChange(e) {
this.form.time = e.detail.value
},
chooseLocation() {
// 先获取用户当前位置,以当前位置为中心打开地图选点
uni.getLocation({
type: 'gcj02',
success: (loc) => {
this.openLocationPicker(loc.latitude, loc.longitude)
},
fail: () => {
// 定位失败时仍打开选点(地图默认位置)
this.openLocationPicker()
}
})
},
openLocationPicker(latitude, longitude) {
const options = {
success: (res) => {
this.form.location = res.name || res.address || ''
this.form.address = res.address || ''
this.form.latitude = res.latitude
this.form.longitude = res.longitude
},
fail: () => {
// 用户取消,不做处理
}
}
if (latitude && longitude) {
options.latitude = latitude
options.longitude = longitude
}
uni.chooseLocation(options)
},
changePeople(delta) {
const val = this.form.maxPeople + delta
if (val >= 2 && val <= 50) {
this.form.maxPeople = val
}
},
async handleSubmit() {
if (!this.canSubmit) return
this.submitting = true
try {
await CreateEvent({
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
})
uni.showToast({ title: '酒局已发布 🎉', icon: 'none' })
setTimeout(() => uni.navigateBack(), 800)
} catch (e) {
// 全局拦截器已弹出错误提示
}
this.submitting = false
}
}
}
</script>
<style lang="scss" scoped>
.create-page {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.create-nav {
background: $bg-base;
padding-left: $sp-lg;
padding-right: $sp-lg;
}
.create-nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
}
.create-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: $bg-card;
&:active { background: $bg-card-alt; }
}
.create-back-icon {
font-size: $fs-xl;
color: $text-primary;
font-weight: $fw-bold;
}
.create-nav-title {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.create-nav-ph {
width: 64rpx;
}
.create-scroll {
flex: 1;
height: 0;
}
.create-body {
padding: $sp-lg;
padding-bottom: calc(env(safe-area-inset-bottom) + #{$sp-2xl});
}
.form-group {
margin-bottom: $sp-xl;
}
.form-label {
display: block;
font-size: $fs-sm;
font-weight: $fw-medium;
color: $text-secondary;
margin-bottom: $sp-md;
}
.form-input {
width: 100%;
height: 88rpx;
padding: 0 $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
font-size: $fs-base;
color: $text-primary;
box-sizing: border-box;
}
.form-placeholder {
color: $text-tertiary;
}
.form-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
}
.form-picker-text {
font-size: $fs-base;
color: $text-primary;
}
.form-picker-ph {
font-size: $fs-base;
color: $text-tertiary;
}
.form-picker-arrow {
font-size: $fs-xl;
color: $text-tertiary;
}
.form-textarea {
width: 100%;
min-height: 160rpx;
padding: $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
font-size: $fs-base;
color: $text-primary;
line-height: $lh-normal;
box-sizing: border-box;
}
/* 地点选择器 */
.location-picker {
display: flex;
align-items: center;
gap: $sp-md;
height: 96rpx;
padding: 0 $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 2rpx dashed var(--border-dashed, rgba(255,255,255,0.12));
&:active {
background: $bg-card-alt;
}
}
.location-picker-icon {
font-size: $fs-lg;
}
.location-picker-text {
flex: 1;
font-size: $fs-base;
color: $text-tertiary;
}
.location-picker-arrow {
font-size: $fs-xl;
color: $text-tertiary;
}
/* 地图预览 */
.location-preview {
border-radius: $radius-lg;
overflow: hidden;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
}
.location-map {
width: 100%;
height: 280rpx;
}
.location-info {
display: flex;
flex-direction: column;
gap: 6rpx;
padding: $sp-md $sp-lg;
background: $bg-card;
}
.location-name {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.location-address {
font-size: $fs-xs;
color: $text-tertiary;
}
.location-change {
display: flex;
align-items: center;
justify-content: center;
padding: $sp-sm;
background: $bg-card-alt;
&:active {
background: $bg-elevated;
}
}
.location-change-text {
font-size: $fs-sm;
color: $amber;
font-weight: $fw-medium;
}
/* 人数步进器 */
.form-stepper {
display: flex;
align-items: center;
gap: $sp-xl;
}
.stepper-btn {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: $bg-card;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
display: flex;
align-items: center;
justify-content: center;
&:active { background: $bg-card-alt; }
}
.stepper-btn-text {
font-size: $fs-xl;
color: $text-primary;
font-weight: $fw-bold;
}
.stepper-value {
font-size: $fs-xl;
font-weight: $fw-black;
color: $amber;
min-width: 60rpx;
text-align: center;
}
/* 提交 */
.create-submit {
width: 100%;
height: 96rpx;
line-height: 96rpx;
text-align: center;
background: linear-gradient(135deg, $amber, $amber-deep);
color: $text-on-amber;
font-size: $fs-lg;
font-weight: $fw-bold;
border-radius: $radius-full;
border: none;
margin-top: $sp-xl;
&::after { border: none; }
&.disabled {
opacity: 0.4;
}
&:active {
transform: scale(0.98);
}
}
</style>