consumer-app/pages/activities/postMessage.vue

618 lines
16 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="post-message-page">
<!-- 头部区域 -->
<NavHeader title="发布消息" />
<!-- 表单内容区 -->
<scroll-view class="form-content" scroll-y="true">
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<!-- 标题 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">标题</text>
<input
class="input-field"
type="text"
v-model="formData.title"
placeholder="请输入消息标题"
maxlength="100"
/>
</view>
</view>
<!-- 摘要 -->
<view class="form-item">
<view class="textarea-wrapper">
<text class="textarea-label">摘要 <text class="optional">(选填)</text></text>
<textarea
class="textarea-field"
v-model="formData.summary"
placeholder="请输入消息摘要..."
maxlength="200"
:auto-height="true"
></textarea>
<view class="char-count">
<text>{{ formData.summary.length }}/200</text>
</view>
</view>
</view>
<!-- 内容 -->
<view class="form-item">
<view class="textarea-wrapper">
<text class="textarea-label">内容 <text class="optional">(选填)</text></text>
<textarea
class="textarea-field"
v-model="formData.content"
placeholder="请输入消息详细内容..."
maxlength="2000"
:auto-height="true"
></textarea>
<view class="char-count">
<text>{{ formData.content.length }}/2000</text>
</view>
</view>
</view>
</view>
<!-- 封面图片 -->
<view class="form-section">
<view class="section-title">封面图片</view>
<view class="form-item">
<view class="upload-wrapper">
<text class="upload-label">封面图片 <text class="optional">(选填)</text></text>
<view class="upload-box" @click="chooseCoverImage">
<image
v-if="formData.coverUrl"
class="uploaded-image"
:src="formData.coverUrl"
mode="aspectFill"
></image>
<view v-else class="upload-placeholder">
<text class="upload-icon">+</text>
<text class="upload-text">点击上传封面</text>
</view>
</view>
</view>
</view>
</view>
<!-- 其他设置 -->
<view class="form-section">
<view class="section-title">其他设置</view>
<!-- 跳转链接 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">跳转链接 <text class="optional">(选填)</text></text>
<input
class="input-field"
type="text"
v-model="formData.jumpUrl"
placeholder="请输入跳转链接https://www.example.com"
/>
</view>
</view>
<!-- 消息类型 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">消息类型 <text class="optional">(选填)</text></text>
<picker
mode="selector"
:range="messageTypeOptions"
range-key="label"
:value="messageTypeIndex"
@change="handleMessageTypeChange"
>
<view class="picker-view">
<text :class="['picker-text', messageTypeIndex === -1 ? 'placeholder' : '']">
{{ messageTypeIndex !== -1 ? messageTypeOptions[messageTypeIndex].label : '请选择消息类型' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
</view>
<!-- 等级 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">等级 <text class="optional">(选填)</text></text>
<picker
mode="selector"
:range="gradeOptions"
range-key="label"
:value="gradeIndex"
@change="handleGradeChange"
>
<view class="picker-view">
<text :class="['picker-text', gradeIndex === -1 ? 'placeholder' : '']">
{{ gradeIndex !== -1 ? gradeOptions[gradeIndex].label : '请选择等级' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
</view>
<!-- 开始时间 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">开始时间 <text class="optional">(选填)</text></text>
<picker
mode="date"
:value="startTimeDisplay"
@change="handleStartTimeChange"
>
<view class="picker-view">
<text :class="['picker-text', !startTimeDisplay ? 'placeholder' : '']">
{{ startTimeDisplay || '请选择开始时间' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
</view>
<!-- 结束时间 -->
<view class="form-item">
<view class="input-wrapper">
<text class="input-label">结束时间 <text class="optional">(选填)</text></text>
<picker
mode="date"
:value="endTimeDisplay"
@change="handleEndTimeChange"
>
<view class="picker-view">
<text :class="['picker-text', !endTimeDisplay ? 'placeholder' : '']">
{{ endTimeDisplay || '请选择结束时间' }}
</text>
<text class="picker-arrow"></text>
</view>
</picker>
</view>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button
class="submit-btn"
:class="{ disabled: loading }"
:disabled="loading"
@click="handleSubmit"
>
{{ loading ? '发布中...' : '发布' }}
</button>
</view>
</scroll-view>
</view>
</template>
<script>
import { createLaborUnionMessage } from '@/api/profile.js'
import NavHeader from "@/components/NavHeader/NavHeader.vue";
export default {
components: {
NavHeader
},
data() {
return {
loading: false,
messageTypeIndex: -1,
messageTypeOptions: [
{ label: '类型1', value: 1 },
{ label: '类型2', value: 2 },
{ label: '类型3', value: 3 }
],
gradeIndex: -1,
gradeOptions: [
{ label: '普通', value: 0 },
{ label: '重要', value: 1 }
],
formData: {
title: '',
summary: '',
content: '',
coverUrl: '',
jumpUrl: '',
messageType: null,
grade: null,
startTime: '',
endTime: ''
}
}
},
computed: {
// 开始时间显示(从 ISO 格式提取日期部分)
startTimeDisplay() {
if (!this.formData.startTime) return ''
return this.formData.startTime.split('T')[0]
},
// 结束时间显示(从 ISO 格式提取日期部分)
endTimeDisplay() {
if (!this.formData.endTime) return ''
return this.formData.endTime.split('T')[0]
}
},
onLoad() {
},
methods: {
// 选择封面图片
chooseCoverImage() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 先显示本地预览
this.formData.coverUrl = tempFilePath
// 上传图片到服务器
this.uploadImage(tempFilePath)
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({
title: '选择图片失败',
icon: 'none'
})
}
})
},
// 上传图片
uploadImage(filePath) {
uni.showLoading({
title: '上传中...',
mask: true
})
// 获取token
const token = uni.getStorageSync('token')
const BASE_URL = 'https://siji.chenjuncn.top'
uni.uploadFile({
url: `${BASE_URL}/app-api/infra/file/upload`,
filePath: filePath,
name: 'file',
header: {
'Authorization': `Bearer ${token}`,
'tenant-id': '1'
},
success: (res) => {
uni.hideLoading()
try {
const data = JSON.parse(res.data)
if (data.code === 200 || data.code === 0) {
// 上传成功保存图片URL
const imageUrl = data.data?.url || data.data || data.url
if (imageUrl) {
this.formData.coverUrl = imageUrl
uni.showToast({
title: '上传成功',
icon: 'success',
duration: 1500
})
} else {
throw new Error('上传成功但未返回图片地址')
}
} else {
throw new Error(data.message || data.msg || '上传失败')
}
} catch (error) {
console.error('解析上传结果失败:', error)
uni.showToast({
title: '上传失败,请重试',
icon: 'none'
})
// 上传失败,清除预览
this.formData.coverUrl = ''
}
},
fail: (err) => {
uni.hideLoading()
console.error('上传图片失败:', err)
uni.showToast({
title: '上传失败,请检查网络',
icon: 'none'
})
// 上传失败,清除预览
this.formData.coverUrl = ''
}
})
},
// 消息类型选择
handleMessageTypeChange(e) {
this.messageTypeIndex = e.detail.value
this.formData.messageType = this.messageTypeOptions[e.detail.value].value
},
// 等级选择
handleGradeChange(e) {
this.gradeIndex = e.detail.value
this.formData.grade = this.gradeOptions[e.detail.value].value
},
// 开始时间选择
handleStartTimeChange(e) {
// 将日期转换为 ISO 格式 (date-time)
const date = e.detail.value
this.formData.startTime = date ? `${date}T00:00:00` : ''
},
// 结束时间选择
handleEndTimeChange(e) {
// 将日期转换为 ISO 格式 (date-time)
const date = e.detail.value
this.formData.endTime = date ? `${date}T23:59:59` : ''
},
// 提交表单
async handleSubmit() {
try {
this.loading = true
// 构建提交数据
const submitData = {
title: this.formData.title ? this.formData.title.trim() : '',
summary: this.formData.summary ? this.formData.summary.trim() : '',
content: this.formData.content ? this.formData.content.trim() : '',
coverUrl: this.formData.coverUrl || '',
jumpUrl: this.formData.jumpUrl ? this.formData.jumpUrl.trim() : '',
messageType: this.formData.messageType,
sourceType: 1, // 会员发布
grade: this.formData.grade,
startTime: this.formData.startTime || null,
endTime: this.formData.endTime || null
}
// 移除空值
Object.keys(submitData).forEach(key => {
if (submitData[key] === '' || submitData[key] === null || submitData[key] === undefined) {
delete submitData[key]
}
})
// 调用接口
const res = await createLaborUnionMessage(submitData)
uni.showToast({
title: '发布成功',
icon: 'success'
})
// 延迟返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (error) {
console.error('发布消息失败:', error)
uni.showToast({
title: error.message || '发布失败,请重试',
icon: 'none'
})
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.post-message-page {
min-height: 100vh;
background: #e2e8f1;
display: flex;
flex-direction: column;
}
.form-content {
flex: 1;
height: 0;
padding: 0 30rpx 40rpx;
box-sizing: border-box;
}
.form-section {
margin-top: 30rpx;
margin-bottom: 40rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin-bottom: 24rpx;
padding-left: 10rpx;
}
}
.form-item {
margin-bottom: 30rpx;
.input-wrapper {
position: relative;
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx 24rpx;
display: flex;
align-items: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
.input-label {
width: 160rpx;
font-size: 28rpx;
color: #333333;
font-weight: 500;
flex-shrink: 0;
.optional {
color: #999999;
font-weight: 400;
font-size: 24rpx;
}
}
.input-field {
flex: 1;
font-size: 28rpx;
color: #1a1819;
}
.picker-view {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
.picker-text {
font-size: 28rpx;
color: #1a1819;
&.placeholder {
color: #999999;
}
}
.picker-arrow {
font-size: 40rpx;
color: #cccccc;
line-height: 1;
}
}
}
.textarea-wrapper {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
.textarea-label {
display: block;
font-size: 28rpx;
color: #333333;
font-weight: 500;
margin-bottom: 20rpx;
.optional {
color: #999999;
font-weight: 400;
font-size: 24rpx;
}
}
.textarea-field {
width: 100%;
min-height: 200rpx;
font-size: 28rpx;
color: #1a1819;
line-height: 1.6;
box-sizing: border-box;
}
.char-count {
display: flex;
justify-content: flex-end;
margin-top: 16rpx;
font-size: 24rpx;
color: #999999;
}
}
.upload-wrapper {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
.upload-label {
display: block;
font-size: 28rpx;
color: #333333;
font-weight: 500;
margin-bottom: 20rpx;
.optional {
color: #999999;
font-weight: 400;
font-size: 24rpx;
}
}
.upload-box {
width: 100%;
height: 400rpx;
border: 2rpx dashed #d0d0d0;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #fafafa;
position: relative;
overflow: hidden;
.uploaded-image {
width: 100%;
height: 100%;
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16rpx;
.upload-icon {
font-size: 60rpx;
color: #999999;
line-height: 1;
}
.upload-text {
font-size: 24rpx;
color: #999999;
}
}
}
}
}
.submit-section {
margin-top: 40rpx;
padding-bottom: 40rpx;
.submit-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #004294 0%, #0066cc 100%);
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none;
display: flex;
align-items: center;
justify-content: center;
&::after {
border: none;
}
&.disabled {
background: #cccccc;
color: #999999;
}
}
}
</style>