2025-07-19 20:00:08 +08:00

493 lines
13 KiB
Vue
Raw Permalink 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" :style="containerStyle">
<common-header title="发布活动" theme="activity" @back="goBack" />
<view class="add-content">
<!-- 表单卡片 -->
<view class="form-card">
<uni-forms ref="form" :model="formData" :rules="rules" label-width="120">
<!-- 基础信息区域 -->
<view class="form-section">
<view class="section-title">
<uni-icons type="info" size="16" color="#ff5722" />
<text>基础信息</text>
</view>
<uni-forms-item label="活动标题" name="title" required>
<uni-easyinput
v-model="formData.title"
placeholder="请输入活动标题"
maxlength="50"
:clearable="true" />
</uni-forms-item>
<uni-forms-item label="活动描述" name="description">
<uni-easyinput
v-model="formData.description"
type="textarea"
placeholder="请输入活动描述,让用户了解活动详情"
maxlength="200"
:auto-height="true" />
</uni-forms-item>
</view>
<!-- 时间设置区域 -->
<view class="form-section">
<view class="section-title">
<uni-icons type="calendar" size="16" color="#ff5722" />
<text>时间设置</text>
</view>
<uni-forms-item label="活动时间" name="startTime" required>
<view class="datetime-wrapper">
<uni-datetime-picker
v-model="formData.startTime"
type="datetime"
placeholder="选择开始时间"
:clear-icon="true" />
<uni-icons type="calendar-filled" size="16" color="#999" />
</view>
</uni-forms-item>
<uni-forms-item label="结束时间" name="endTime" required>
<view class="datetime-wrapper">
<uni-datetime-picker
v-model="formData.endTime"
type="datetime"
placeholder="选择结束时间"
:clear-icon="true" />
<uni-icons type="calendar-filled" size="16" color="#999" />
</view>
</uni-forms-item>
</view>
<!-- 活动设置区域 -->
<view class="form-section">
<view class="section-title">
<uni-icons type="gear" size="16" color="#ff5722" />
<text>活动设置</text>
</view>
<uni-forms-item label="活动类型" name="type" required>
<uni-data-select
v-model="formData.type"
:localdata="typeOptions"
placeholder="请选择活动类型" />
</uni-forms-item>
<uni-forms-item label="参与限制" name="participantLimit">
<view class="number-input-wrapper">
<uni-number-box
v-model="formData.participantLimit"
:min="0"
:max="9999"
placeholder="0" />
<text class="input-suffix">0表示无限制</text>
</view>
</uni-forms-item>
</view>
<!-- 详细规则区域 -->
<view class="form-section">
<view class="section-title">
<uni-icons type="list" size="16" color="#ff5722" />
<text>详细规则</text>
</view>
<uni-forms-item label="活动规则" name="rules">
<uni-easyinput
v-model="formData.rules"
type="textarea"
placeholder="请输入活动规则,如参与条件、活动流程等"
maxlength="500"
:auto-height="true" />
</uni-forms-item>
<uni-forms-item label="活动奖励" name="reward">
<uni-easyinput
v-model="formData.reward"
placeholder="请输入活动奖励,如优惠券、积分、礼品等"
maxlength="100"
:clearable="true" />
</uni-forms-item>
</view>
</uni-forms>
</view>
<!-- 操作按钮区域 -->
<view class="action-section">
<uni-button
class="action-btn draft-btn"
@click="saveDraft">
<uni-icons type="folder" size="18" color="#666" />
保存草稿
</uni-button>
<uni-button
class="action-btn publish-btn"
type="primary"
@click="submitForm">
<uni-icons type="paperplane" size="18" color="#fff" />
发布活动
</uni-button>
</view>
</view>
</view>
</template>
<script>
import CommonHeader from '@/components/common-header/common-header.vue'
import { createActivity } from '@/api/brewery/activity'
export default {
components: {
CommonHeader
},
data() {
return {
headerHeight: 96, // 默认头部高度
formData: {
title: '',
description: '',
startTime: '',
endTime: '',
type: '',
rules: '',
reward: '',
participantLimit: 0,
status: 0
},
typeOptions: [
{ value: '1', text: '限时抢购' },
{ value: '2', text: '满减活动' },
{ value: '3', text: '新人礼包' },
{ value: '4', text: '积分兑换' }
],
rules: {
title: {
rules: [
{ required: true, errorMessage: '请输入活动标题' },
{ minLength: 2, maxLength: 50, errorMessage: '标题长度在2-50字符之间' }
]
},
description: {
rules: [
{ maxLength: 200, errorMessage: '描述不能超过200字符' }
]
},
startTime: {
rules: [{ required: true, errorMessage: '请选择开始时间' }]
},
endTime: {
rules: [
{ required: true, errorMessage: '请选择结束时间' },
{
validateFunction: (rule, value, data) => {
if (data.startTime && value && new Date(value) <= new Date(data.startTime)) {
return '结束时间必须晚于开始时间'
}
return true
}
}
]
},
type: {
rules: [{ required: true, errorMessage: '请选择活动类型' }]
},
rules: {
rules: [
{ maxLength: 500, errorMessage: '规则不能超过500字符' }
]
},
reward: {
rules: [
{ maxLength: 100, errorMessage: '奖励描述不能超过100字符' }
]
}
}
}
},
computed: {
containerStyle() {
return {
paddingTop: this.headerHeight + 'px'
}
}
},
onLoad() {
this.listenHeaderHeight()
this.calculateHeaderHeight()
},
onUnload() {
uni.$off('header-height-updated', this.onHeaderHeightUpdated)
},
methods: {
goBack() {
uni.navigateBack()
},
async submitForm() {
try {
await this.$refs.form.validate()
this.formData.status = 1
await this.saveActivity()
} catch (error) {
console.log('表单验证失败', error)
}
},
async saveDraft() {
this.formData.status = 0
await this.saveActivity()
},
async saveActivity() {
try {
await createActivity(this.formData)
this.$modal.showToast('保存成功')
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (error) {
this.$modal.showToast('保存失败')
}
},
// 监听头部高度变化
listenHeaderHeight() {
uni.$on('header-height-updated', this.onHeaderHeightUpdated)
},
// 头部高度更新处理
onHeaderHeightUpdated(data) {
this.headerHeight = data.headerHeight
},
// 直接计算头部高度
calculateHeaderHeight() {
try {
const systemInfo = uni.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 0
let navBarHeight = 44
// #ifdef MP-WEIXIN
try {
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
if (menuButtonInfo) {
const topGap = menuButtonInfo.top - statusBarHeight
const bottomGap = topGap
navBarHeight = topGap + menuButtonInfo.height + bottomGap
}
} catch (e) {
console.warn('获取胶囊按钮信息失败:', e)
}
// #endif
// 确保最小高度
if (navBarHeight < 44) {
navBarHeight = 44
}
this.headerHeight = statusBarHeight + navBarHeight
} catch (e) {
console.warn('计算头部高度失败:', e)
this.headerHeight = 96 // 使用默认值
}
}
}
}
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: linear-gradient(180deg, #f8f9fa 0%, #f5f7fa 100%);
box-sizing: border-box;
// 头部间距将通过动态样式设置
// 如果动态样式不生效,使用默认值
padding-top: 96px;
}
.add-content {
padding: 0 20rpx 40rpx;
}
.form-card {
background: #fff;
border-radius: 24rpx;
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
overflow: hidden;
}
.form-section {
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.section-title {
display: flex;
align-items: center;
gap: 12rpx;
padding: 32rpx 32rpx 24rpx;
font-size: 32rpx;
font-weight: 600;
color: #1a1a1a;
background: linear-gradient(90deg, rgba(255, 87, 34, 0.05) 0%, rgba(255, 87, 34, 0.02) 100%);
border-bottom: 1rpx solid #f5f5f5;
text {
flex: 1;
}
}
}
// 表单项样式优化
::v-deep .uni-forms-item {
padding: 24rpx 32rpx;
border-bottom: 1rpx solid #f8f9fa;
&:last-child {
border-bottom: none;
}
.uni-forms-item__label {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.uni-forms-item__content {
.uni-easyinput__content {
border: 1rpx solid #e5e7eb;
border-radius: 12rpx;
padding: 16rpx;
font-size: 28rpx;
transition: all 0.3s ease;
&:focus-within {
border-color: #ff5722;
box-shadow: 0 0 0 3rpx rgba(255, 87, 34, 0.1);
}
}
.uni-datetime-picker {
border: 1rpx solid #e5e7eb;
border-radius: 12rpx;
padding: 16rpx;
font-size: 28rpx;
transition: all 0.3s ease;
&:focus-within {
border-color: #ff5722;
box-shadow: 0 0 0 3rpx rgba(255, 87, 34, 0.1);
}
}
}
}
.datetime-wrapper {
position: relative;
display: flex;
align-items: center;
.uni-datetime-picker {
flex: 1;
}
.uni-icons {
position: absolute;
right: 16rpx;
pointer-events: none;
}
}
.number-input-wrapper {
display: flex;
align-items: center;
gap: 16rpx;
.uni-number-box {
flex: 1;
}
.input-suffix {
font-size: 26rpx;
color: #666;
white-space: nowrap;
}
}
.action-section {
display: flex;
gap: 24rpx;
margin-top: 32rpx;
padding: 0 20rpx;
.action-btn {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
transition: all 0.3s ease;
border: none;
&.draft-btn {
background: #f8f9fa;
color: #666;
border: 2rpx solid #e9ecef;
&:active {
background: #e9ecef;
transform: scale(0.98);
}
}
&.publish-btn {
background: linear-gradient(45deg, #ff5722, #ff7043);
color: #fff;
box-shadow: 0 8rpx 24rpx rgba(255, 87, 34, 0.4);
&:active {
transform: scale(0.98);
box-shadow: 0 4rpx 16rpx rgba(255, 87, 34, 0.5);
}
}
}
}
// 响应式适配
@media (max-width: 750rpx) {
.form-section .section-title {
padding: 24rpx 24rpx 20rpx;
font-size: 30rpx;
}
::v-deep .uni-forms-item {
padding: 20rpx 24rpx;
}
.action-section {
padding: 0 16rpx;
gap: 20rpx;
.action-btn {
height: 80rpx;
font-size: 30rpx;
}
}
}
</style>