Files
CosScene/clients/pages/shooting/create.vue
T
2026-05-09 16:40:29 +08:00

332 lines
8.3 KiB
Vue

<script setup>
import { ref, computed } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { createShooting, updateShooting, getShootingDetail } from "@/api/shooting";
const isEdit = ref(false);
const editId = ref(0);
const submitting = ref(false);
const form = ref({
title: "",
city: "",
description: "",
style: "",
shoot_date: "",
is_free: false,
budget_min: "",
budget_max: "",
role_needed: "photographer",
max_applicants: 1,
contact_info: "",
});
const roleOptions = [
{ label: "摄影师", value: "photographer" },
{ label: "Coser", value: "cosplayer" },
{ label: "不限", value: "both" },
];
const roleIndex = computed({
get() {
return roleOptions.findIndex((o) => o.value === form.value.role_needed);
},
set(idx) {
form.value.role_needed = roleOptions[idx].value;
},
});
function onDateChange(e) {
form.value.shoot_date = e.detail.value;
}
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;
}
if (!form.value.is_free) {
const min = Number(form.value.budget_min);
const max = Number(form.value.budget_max);
if (min && max && min > max) {
uni.showToast({ title: "最低预算不能高于最高预算", icon: "none" });
return false;
}
}
return true;
}
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,
style: form.value.style.trim() || null,
is_free: form.value.is_free,
role_needed: form.value.role_needed,
max_applicants: Number(form.value.max_applicants) || 1,
contact_info: form.value.contact_info.trim() || null,
};
if (form.value.shoot_date) {
data.shoot_date = new Date(form.value.shoot_date).toISOString();
}
if (!form.value.is_free) {
data.budget_min = form.value.budget_min ? Number(form.value.budget_min) : null;
data.budget_max = form.value.budget_max ? Number(form.value.budget_max) : null;
}
try {
if (isEdit.value) {
await updateShooting(editId.value, data);
uni.showToast({ title: "更新成功", icon: "success" });
} else {
await createShooting(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 getShootingDetail(id);
form.value.title = d.title || "";
form.value.city = d.city || "";
form.value.description = d.description || "";
form.value.style = d.style || "";
form.value.is_free = d.is_free || false;
form.value.budget_min = d.budget_min ? String(d.budget_min) : "";
form.value.budget_max = d.budget_max ? String(d.budget_max) : "";
form.value.role_needed = d.role_needed || "photographer";
form.value.max_applicants = d.max_applicants || 1;
form.value.contact_info = d.contact_info || "";
if (d.shoot_date) {
form.value.shoot_date = d.shoot_date.substring(0, 10);
}
} 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="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.style"
class="form-input"
placeholder="如:古风、JK、日系清新"
:maxlength="50"
/>
</view>
<view class="form-row">
<text class="form-label">拍摄日期</text>
<picker mode="date" :value="form.shoot_date" @change="onDateChange">
<view class="form-input picker-display">
{{ form.shoot_date || "选择日期(可选)" }}
</view>
</picker>
</view>
<view class="form-row">
<text class="form-label">需要角色</text>
<picker
:range="roleOptions"
range-key="label"
:value="roleIndex"
@change="roleIndex = $event.detail.value"
>
<view class="form-input picker-display">
{{ roleOptions[roleIndex]?.label || "选择" }}
</view>
</picker>
</view>
<view class="form-row">
<text class="form-label">招募人数</text>
<input
v-model="form.max_applicants"
class="form-input"
type="number"
placeholder="默认1人"
/>
</view>
<view class="form-row switch-row">
<text class="form-label">互免约拍</text>
<switch :checked="form.is_free" @change="form.is_free = $event.detail.value" color="#6366f1" />
</view>
<template v-if="!form.is_free">
<view class="form-row">
<text class="form-label">预算下限</text>
<input
v-model="form.budget_min"
class="form-input"
type="digit"
placeholder="最低价格(选填)"
/>
</view>
<view class="form-row">
<text class="form-label">预算上限</text>
<input
v-model="form.budget_max"
class="form-input"
type="digit"
placeholder="最高价格(选填)"
/>
</view>
</template>
<view class="form-row">
<text class="form-label">联系方式</text>
<input
v-model="form.contact_info"
class="form-input"
placeholder="微信/QQ/手机号(仅对方可见)"
:maxlength="100"
/>
</view>
<view class="form-row">
<text class="form-label">详细描述</text>
<textarea
v-model="form.description"
class="form-textarea"
placeholder="描述拍摄需求、时间安排等"
:maxlength="2000"
/>
</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;
}
.form-row {
padding: 20rpx 0;
border-bottom: 1rpx solid #f3f4f6;
}
.form-row:last-child {
border-bottom: none;
}
.switch-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.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>