493 lines
13 KiB
Vue
493 lines
13 KiB
Vue
<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> |