298 lines
8.2 KiB
Vue
298 lines
8.2 KiB
Vue
<script setup>
|
|
import { ref } from "vue";
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|
import { createEvent, updateEvent, getEventDetail } from "@/api/event";
|
|
import { uploadImage } from "@/api/spot";
|
|
import { resolveImageUrl } from "@/utils/image";
|
|
|
|
const isEdit = ref(false);
|
|
const editId = ref(0);
|
|
const submitting = ref(false);
|
|
|
|
const form = ref({
|
|
title: "",
|
|
city: "",
|
|
description: "",
|
|
cover_url: "",
|
|
location_name: "",
|
|
start_date: "",
|
|
start_time: "10:00",
|
|
end_date: "",
|
|
end_time: "18:00",
|
|
max_participants: 0,
|
|
});
|
|
|
|
const coverPreview = ref("");
|
|
|
|
async function chooseCover() {
|
|
uni.chooseImage({
|
|
count: 1,
|
|
success: async (res) => {
|
|
const path = res.tempFilePaths[0];
|
|
try {
|
|
uni.showLoading({ title: "上传中..." });
|
|
const uploadRes = await uploadImage(path);
|
|
form.value.cover_url = uploadRes.url || uploadRes.path;
|
|
coverPreview.value = resolveImageUrl(form.value.cover_url);
|
|
uni.showToast({ title: "上传成功", icon: "success" });
|
|
} catch (e) {
|
|
uni.showToast({ title: "上传失败", icon: "none" });
|
|
} finally {
|
|
uni.hideLoading();
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
function validate() {
|
|
if (!form.value.title.trim()) {
|
|
uni.showToast({ title: "请输入活动标题", icon: "none" });
|
|
return false;
|
|
}
|
|
if (!form.value.city.trim()) {
|
|
uni.showToast({ title: "请输入城市", icon: "none" });
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function buildDateTime(date, time) {
|
|
if (!date) return null;
|
|
return new Date(`${date}T${time || "00:00"}:00`).toISOString();
|
|
}
|
|
|
|
async function handleSubmit() {
|
|
if (!validate()) return;
|
|
submitting.value = true;
|
|
|
|
const data = {
|
|
title: form.value.title.trim(),
|
|
city: form.value.city.trim(),
|
|
description: form.value.description.trim() || null,
|
|
cover_url: form.value.cover_url || null,
|
|
location_name: form.value.location_name.trim() || null,
|
|
max_participants: Number(form.value.max_participants) || 0,
|
|
start_time: buildDateTime(form.value.start_date, form.value.start_time),
|
|
end_time: buildDateTime(form.value.end_date, form.value.end_time),
|
|
};
|
|
|
|
try {
|
|
if (isEdit.value) {
|
|
await updateEvent(editId.value, data);
|
|
uni.showToast({ title: "更新成功", icon: "success" });
|
|
} else {
|
|
await createEvent(data);
|
|
uni.showToast({ title: "发布成功,等待审核", icon: "success" });
|
|
}
|
|
setTimeout(() => uni.navigateBack(), 1200);
|
|
} catch (e) {
|
|
uni.showToast({ title: e.message || "提交失败", icon: "none" });
|
|
} finally {
|
|
submitting.value = false;
|
|
}
|
|
}
|
|
|
|
async function loadForEdit(id) {
|
|
try {
|
|
const d = await getEventDetail(id);
|
|
form.value.title = d.title || "";
|
|
form.value.city = d.city || "";
|
|
form.value.description = d.description || "";
|
|
form.value.cover_url = d.cover_url || "";
|
|
form.value.location_name = d.location_name || "";
|
|
form.value.max_participants = d.max_participants || 0;
|
|
if (d.cover_url) coverPreview.value = resolveImageUrl(d.cover_url);
|
|
if (d.start_time) {
|
|
const st = new Date(d.start_time);
|
|
form.value.start_date = st.toISOString().substring(0, 10);
|
|
form.value.start_time = `${String(st.getHours()).padStart(2, "0")}:${String(st.getMinutes()).padStart(2, "0")}`;
|
|
}
|
|
if (d.end_time) {
|
|
const et = new Date(d.end_time);
|
|
form.value.end_date = et.toISOString().substring(0, 10);
|
|
form.value.end_time = `${String(et.getHours()).padStart(2, "0")}:${String(et.getMinutes()).padStart(2, "0")}`;
|
|
}
|
|
} catch (e) {
|
|
uni.showToast({ title: "加载失败", icon: "none" });
|
|
}
|
|
}
|
|
|
|
onLoad((query) => {
|
|
if (query.id) {
|
|
isEdit.value = true;
|
|
editId.value = Number(query.id);
|
|
uni.setNavigationBarTitle({ title: "编辑活动" });
|
|
loadForEdit(editId.value);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<view class="create-page">
|
|
<view class="form-card">
|
|
<view class="cover-section" @tap="chooseCover">
|
|
<image v-if="coverPreview" class="cover-preview" :src="coverPreview" mode="aspectFill" />
|
|
<view v-else class="cover-placeholder">
|
|
<uni-icons type="plusempty" size="32" color="#9ca3af" />
|
|
<text class="cover-hint">添加封面图</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label required">活动标题</text>
|
|
<input v-model="form.title" class="form-input" placeholder="如:外滩夜景约拍团" :maxlength="100" />
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label required">城市</text>
|
|
<input v-model="form.city" class="form-input" placeholder="如:上海" :maxlength="50" />
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label">活动地点</text>
|
|
<input v-model="form.location_name" class="form-input" placeholder="如:外滩观景平台" :maxlength="100" />
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label">开始日期</text>
|
|
<picker mode="date" :value="form.start_date" @change="form.start_date = $event.detail.value">
|
|
<view class="form-input picker-display">{{ form.start_date || "选择日期" }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-row">
|
|
<text class="form-label">开始时间</text>
|
|
<picker mode="time" :value="form.start_time" @change="form.start_time = $event.detail.value">
|
|
<view class="form-input picker-display">{{ form.start_time }}</view>
|
|
</picker>
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label">结束日期</text>
|
|
<picker mode="date" :value="form.end_date" @change="form.end_date = $event.detail.value">
|
|
<view class="form-input picker-display">{{ form.end_date || "选择日期" }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-row">
|
|
<text class="form-label">结束时间</text>
|
|
<picker mode="time" :value="form.end_time" @change="form.end_time = $event.detail.value">
|
|
<view class="form-input picker-display">{{ form.end_time }}</view>
|
|
</picker>
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label">人数限制</text>
|
|
<input v-model="form.max_participants" class="form-input" type="number" placeholder="0表示不限" />
|
|
</view>
|
|
|
|
<view class="form-row">
|
|
<text class="form-label">活动详情</text>
|
|
<textarea v-model="form.description" class="form-textarea" placeholder="描述活动内容、注意事项等" :maxlength="5000" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="submit-row">
|
|
<view class="submit-btn" :class="{ disabled: submitting }" @tap="handleSubmit">
|
|
{{ submitting ? "提交中..." : isEdit ? "保存修改" : "发布活动" }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.create-page {
|
|
min-height: 100vh;
|
|
background: #f5f6fa;
|
|
padding-bottom: 40rpx;
|
|
}
|
|
.form-card {
|
|
background: #fff;
|
|
margin: 16rpx 20rpx;
|
|
border-radius: 20rpx;
|
|
padding: 12rpx 28rpx;
|
|
}
|
|
|
|
.cover-section {
|
|
margin: 16rpx 0;
|
|
border-radius: 16rpx;
|
|
overflow: hidden;
|
|
}
|
|
.cover-preview {
|
|
width: 100%;
|
|
height: 300rpx;
|
|
}
|
|
.cover-placeholder {
|
|
width: 100%;
|
|
height: 300rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #f3f4f6;
|
|
border: 2rpx dashed #d1d5db;
|
|
border-radius: 16rpx;
|
|
gap: 12rpx;
|
|
}
|
|
.cover-hint {
|
|
font-size: 26rpx;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.form-row {
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f3f4f6;
|
|
}
|
|
.form-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.form-label {
|
|
font-size: 26rpx;
|
|
color: #374151;
|
|
margin-bottom: 10rpx;
|
|
display: block;
|
|
}
|
|
.form-label.required::before {
|
|
content: "* ";
|
|
color: #ef4444;
|
|
}
|
|
.form-input {
|
|
width: 100%;
|
|
height: 72rpx;
|
|
border: 1rpx solid #e5e7eb;
|
|
border-radius: 12rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 28rpx;
|
|
box-sizing: border-box;
|
|
line-height: 72rpx;
|
|
}
|
|
.picker-display {
|
|
display: flex;
|
|
align-items: center;
|
|
color: #374151;
|
|
}
|
|
.form-textarea {
|
|
width: 100%;
|
|
min-height: 200rpx;
|
|
border: 1rpx solid #e5e7eb;
|
|
border-radius: 12rpx;
|
|
padding: 16rpx 20rpx;
|
|
font-size: 28rpx;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.submit-row {
|
|
padding: 20rpx 28rpx;
|
|
}
|
|
.submit-btn {
|
|
text-align: center;
|
|
padding: 24rpx 0;
|
|
background: #6366f1;
|
|
color: #fff;
|
|
font-size: 30rpx;
|
|
font-weight: 700;
|
|
border-radius: 16rpx;
|
|
}
|
|
.submit-btn.disabled {
|
|
opacity: 0.6;
|
|
}
|
|
</style>
|