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>
|