Files
hejiu/pages/event-create/event-create.vue
T
cg d335ec290a refactor(app): 重构应用主题和页面结构
- 移除动态主题切换功能,改为全局统一深色主题
- 实现自定义TabBar组件替换原生tabBar,解决iOS闪白问题
- 创建main容器页面统一管理三个tab页面的生命周期和状态
- 将所有页面的onShow/onHide等生命周期方法迁移到子组件内部
- 更新页面路径从index改为main,并调整路由跳转逻辑
- 移除theme-mixin和相关主题工具函数
- 统一页面背景色设置,优化用户体验一致性
2026-09-23 20:56:14 +08:00

536 lines
13 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="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">{{ isEdit ? '编辑酒局' : '发起酒局' }}</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">
{{ isEdit ? '保存修改' : '发布酒局' }}
</button>
</view>
</scroll-view>
</view>
</template>
<script>
import { CreateEvent, UpdateEvent, GetEventDetail } from '../../common/api'
export default {
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,
// 编辑模式:携带酒局ID时进入编辑态
editId: '',
loadingDetail: false
}
},
onLoad(options) {
if (options && options.id) {
this.editId = options.id
this.loadEventDetail()
}
},
computed: {
isEdit() {
return !!this.editId
},
canSubmit() {
return this.form.title.trim() && this.form.date && this.form.time && this.form.location.trim() && !this.submitting && !this.loadingDetail
},
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()
},
/** 编辑模式:拉取酒局详情回填表单 */
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) {
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
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 {
if (this.isEdit) {
await UpdateEvent({ eventId: this.editId, ...payload })
uni.showToast({ title: '修改已保存 ✅', icon: 'none' })
} else {
await CreateEvent(payload)
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>