792 lines
22 KiB
Vue
792 lines
22 KiB
Vue
<template>
|
|
<div class="compose-container">
|
|
<!-- 深空背景 -->
|
|
<div class="space-background">
|
|
<div class="stars" ref="stars"></div>
|
|
</div>
|
|
|
|
<!-- 顶部导航 -->
|
|
<div class="header glass-card">
|
|
<van-icon name="arrow-left" size="24" @click="goBack" />
|
|
<h2>撰写未来邮件</h2>
|
|
<div></div>
|
|
</div>
|
|
|
|
<!-- 撰写表单 -->
|
|
<div class="compose-content">
|
|
<div class="form-section glass-card p-20">
|
|
<!-- 收件人选择 -->
|
|
<div class="form-group">
|
|
<h3 class="font-semibold">收件人</h3>
|
|
<van-radio-group v-model="recipientType" direction="horizontal">
|
|
<van-radio name="SELF">自己</van-radio>
|
|
<van-radio name="SPECIFIC">他人</van-radio>
|
|
<van-radio name="PUBLIC">任意有缘人</van-radio>
|
|
</van-radio-group>
|
|
|
|
<van-field
|
|
v-if="recipientType === 'SPECIFIC'"
|
|
v-model="recipientEmail"
|
|
placeholder="收件人邮箱"
|
|
class="custom-field mt-10"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 发送时间选择 -->
|
|
<div class="form-group mt-20">
|
|
<h3 class="font-semibold">发送时间</h3>
|
|
<van-radio-group v-model="timeType" direction="horizontal">
|
|
<van-radio name="preset">预设时间</van-radio>
|
|
<van-radio name="custom">自定义</van-radio>
|
|
<van-radio name="condition">条件触发</van-radio>
|
|
</van-radio-group>
|
|
|
|
<div v-if="timeType === 'preset'" class="preset-options mt-10">
|
|
<van-button
|
|
v-for="option in presetTimeOptions"
|
|
:key="option.value"
|
|
:type="selectedPresetTime === option.value ? 'primary' : 'default'"
|
|
round
|
|
size="small"
|
|
class="preset-button"
|
|
@click="selectPresetTime(option.value)"
|
|
>
|
|
{{ option.label }}
|
|
</van-button>
|
|
</div>
|
|
|
|
<van-datetime-picker
|
|
v-if="timeType === 'custom'"
|
|
v-model="customDeliveryDate"
|
|
type="date"
|
|
:min-date="minDate"
|
|
class="custom-date-picker mt-10"
|
|
/>
|
|
|
|
<div v-if="timeType === 'condition'" class="condition-options mt-10">
|
|
<van-cell-group>
|
|
<van-cell title="地点触发" is-link @click="showLocationPicker = true" />
|
|
<van-cell title="事件触发" is-link @click="showEventPicker = true" />
|
|
</van-cell-group>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 邮件内容 -->
|
|
<div class="form-group mt-20">
|
|
<h3 class="font-semibold">邮件内容</h3>
|
|
<van-field
|
|
v-model="subject"
|
|
placeholder="标题"
|
|
class="custom-field"
|
|
/>
|
|
<van-field
|
|
v-model="content"
|
|
type="textarea"
|
|
placeholder="写下你想对未来的自己说的话..."
|
|
rows="8"
|
|
autosize
|
|
class="custom-field mt-10"
|
|
/>
|
|
|
|
<!-- 附件和多媒体 -->
|
|
<div class="media-options mt-10">
|
|
<van-uploader :after-read="afterRead" class="media-uploader">
|
|
<van-button icon="photo-o" type="primary" plain round size="small">
|
|
添加图片
|
|
</van-button>
|
|
</van-uploader>
|
|
<van-button icon="volume-o" type="primary" plain round size="small" class="ml-10">
|
|
添加语音
|
|
</van-button>
|
|
<van-button icon="video-o" type="primary" plain round size="small" class="ml-10">
|
|
添加视频
|
|
</van-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AI助手 -->
|
|
<div class="form-group mt-20">
|
|
<h3 class="font-semibold">AI写作助手</h3>
|
|
<van-cell-group>
|
|
<van-cell title="生成开头" is-link @click="generateOpening" />
|
|
<van-cell title="内容建议" is-link @click="generateSuggestions" />
|
|
<van-cell title="情感分析" is-link @click="analyzeEmotion" />
|
|
</van-cell-group>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 底部操作按钮 -->
|
|
<div class="footer-actions">
|
|
<van-button round block class="save-button" @click="saveDraft">
|
|
存入胶囊
|
|
</van-button>
|
|
<van-button round block type="primary" class="send-button" @click="sendMail">
|
|
发送至未来
|
|
</van-button>
|
|
</div>
|
|
|
|
<!-- 地点选择弹窗 -->
|
|
<van-popup v-model:show="showLocationPicker" position="bottom">
|
|
<van-area
|
|
:area-list="areaList"
|
|
@confirm="onLocationConfirm"
|
|
@cancel="showLocationPicker = false"
|
|
/>
|
|
</van-popup>
|
|
|
|
<!-- 事件选择弹窗 -->
|
|
<van-popup v-model:show="showEventPicker" position="bottom" :style="{ height: '50%' }">
|
|
<div class="event-picker">
|
|
<h3>选择触发事件</h3>
|
|
<van-cell-group>
|
|
<van-cell
|
|
v-for="event in triggerEvents"
|
|
:key="event.id"
|
|
:title="event.name"
|
|
:label="event.description"
|
|
@click="selectEvent(event)"
|
|
/>
|
|
</van-cell-group>
|
|
</div>
|
|
</van-popup>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, onMounted, computed } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { showLoadingToast, showSuccessToast, showFailToast, closeToast, Dialog } from 'vant'
|
|
|
|
export default {
|
|
name: 'Compose',
|
|
setup() {
|
|
const router = useRouter()
|
|
const stars = ref(null)
|
|
|
|
// 表单数据
|
|
const recipientType = ref('SELF') // 对应API的SELF, SPECIFIC, PUBLIC
|
|
const recipientEmail = ref('')
|
|
const timeType = ref('preset') // preset, custom, condition
|
|
const selectedPresetTime = ref('1year')
|
|
const customDeliveryDate = ref(new Date(Date.now() + 365 * 24 * 60 * 60 * 1000))
|
|
const subject = ref('')
|
|
const content = ref('')
|
|
const attachments = ref([]) // 附件列表
|
|
const isEncrypted = ref(false) // 是否加密
|
|
const capsuleStyle = ref('default') // 胶囊样式
|
|
|
|
// 弹窗控制
|
|
const showLocationPicker = ref(false)
|
|
const showEventPicker = ref(false)
|
|
const selectedLocation = ref(null) // 选中的地点
|
|
const selectedEvent = ref(null) // 选中的触发事件
|
|
|
|
// 最小日期为明天
|
|
const minDate = computed(() => {
|
|
const tomorrow = new Date()
|
|
tomorrow.setDate(tomorrow.getDate() + 1)
|
|
return tomorrow
|
|
})
|
|
|
|
// 预设时间选项
|
|
const presetTimeOptions = [
|
|
{ label: '1天后', value: '1day' },
|
|
{ label: '1周后', value: '1week' },
|
|
{ label: '1个月后', value: '1month' },
|
|
{ label: '1年后', value: '1year' },
|
|
{ label: '5年后', value: '5years' },
|
|
{ label: '10年后', value: '10years' }
|
|
]
|
|
|
|
// 触发事件选项
|
|
const triggerEvents = [
|
|
{
|
|
id: 1,
|
|
name: '人类登陆火星',
|
|
description: '当检测到相关新闻时触发',
|
|
keywords: ['火星', '登陆', '太空探索'],
|
|
type: 'SPACE_EVENT'
|
|
},
|
|
{
|
|
id: 2,
|
|
name: '获得理想工作',
|
|
description: '当您更新个人资料为在职状态时触发',
|
|
keywords: ['工作', '职业', '就业'],
|
|
type: 'CAREER_EVENT'
|
|
},
|
|
{
|
|
id: 3,
|
|
name: '结婚纪念日',
|
|
description: '在每年的结婚纪念日触发',
|
|
keywords: ['结婚', '纪念日', '婚礼'],
|
|
type: 'PERSONAL_EVENT'
|
|
},
|
|
{
|
|
id: 4,
|
|
name: '孩子出生',
|
|
description: '当您添加家庭成员信息时触发',
|
|
keywords: ['孩子', '出生', '家庭'],
|
|
type: 'FAMILY_EVENT'
|
|
}
|
|
]
|
|
|
|
// 模拟地区数据
|
|
const areaList = {
|
|
province_list: {
|
|
110000: '北京市',
|
|
120000: '天津市',
|
|
310000: '上海市',
|
|
440000: '广东省',
|
|
330000: '浙江省',
|
|
320000: '江苏省'
|
|
},
|
|
city_list: {
|
|
110100: '北京市',
|
|
120100: '天津市',
|
|
310100: '上海市',
|
|
440100: '广州市',
|
|
440300: '深圳市',
|
|
330100: '杭州市',
|
|
320100: '南京市'
|
|
},
|
|
county_list: {
|
|
110101: '东城区',
|
|
110102: '西城区',
|
|
440103: '荔湾区',
|
|
440304: '福田区',
|
|
330102: '上城区',
|
|
320102: '玄武区'
|
|
}
|
|
}
|
|
|
|
// 生成星空背景
|
|
const generateStars = () => {
|
|
if (!stars.value) return
|
|
|
|
const starsContainer = stars.value
|
|
const starCount = 100
|
|
|
|
for (let i = 0; i < starCount; i++) {
|
|
const star = document.createElement('div')
|
|
star.className = 'star'
|
|
|
|
// 随机位置
|
|
const left = Math.random() * 100
|
|
const top = Math.random() * 100
|
|
|
|
// 随机大小
|
|
const size = Math.random() * 3 + 1
|
|
|
|
// 随机动画延迟
|
|
const delay = Math.random() * 4
|
|
|
|
star.style.left = `${left}%`
|
|
star.style.top = `${top}%`
|
|
star.style.width = `${size}px`
|
|
star.style.height = `${size}px`
|
|
star.style.animationDelay = `${delay}s`
|
|
|
|
starsContainer.appendChild(star)
|
|
}
|
|
}
|
|
|
|
// 返回上一页
|
|
const goBack = () => {
|
|
router.back()
|
|
}
|
|
|
|
// 选择预设时间
|
|
const selectPresetTime = (value) => {
|
|
selectedPresetTime.value = value
|
|
}
|
|
|
|
// 地点选择确认
|
|
const onLocationConfirm = (values) => {
|
|
showLocationPicker.value = false
|
|
const locationName = values.map(item => item.name).join('/')
|
|
selectedLocation.value = {
|
|
city: values[1]?.name || '',
|
|
province: values[0]?.name || '',
|
|
district: values[2]?.name || ''
|
|
}
|
|
showFailToast(`已选择地点: ${locationName}`)
|
|
}
|
|
|
|
// 选择触发事件
|
|
const selectEvent = (event) => {
|
|
showEventPicker.value = false
|
|
selectedEvent.value = event
|
|
showFailToast(`已选择触发事件: ${event.name}`)
|
|
}
|
|
|
|
// 文件上传后处理
|
|
const afterRead = (file) => {
|
|
// 这里应该调用文件上传API
|
|
// 模拟上传成功
|
|
const attachment = {
|
|
type: 'IMAGE',
|
|
url: URL.createObjectURL(file.file),
|
|
thumbnail: URL.createObjectURL(file.file),
|
|
size: file.file.size
|
|
}
|
|
attachments.value.push(attachment)
|
|
showSuccessToast(`已添加图片: ${file.file.name}`)
|
|
}
|
|
|
|
// AI生成开头
|
|
const generateOpening = async () => {
|
|
try {
|
|
showLoadingToast({
|
|
message: '生成中...',
|
|
forbidClick: true,
|
|
})
|
|
|
|
// 模拟API调用延迟
|
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
|
|
// 模拟AI生成的开头
|
|
const opennings = [
|
|
"亲爱的未来的我,",
|
|
"时光荏苒,当你读到这封信时,",
|
|
"写给多年后的自己,",
|
|
"未来的我,你好!",
|
|
"此刻的我,想对未来的你说:"
|
|
]
|
|
|
|
const randomIndex = Math.floor(Math.random() * opennings.length)
|
|
content.value = opennings[randomIndex] + "\n\n"
|
|
|
|
closeToast()
|
|
showSuccessToast('已生成开头')
|
|
} catch (error) {
|
|
closeToast()
|
|
showFailToast('生成失败,请重试')
|
|
}
|
|
}
|
|
|
|
// AI生成内容建议
|
|
const generateSuggestions = async () => {
|
|
try {
|
|
showLoadingToast({
|
|
message: '生成中...',
|
|
forbidClick: true,
|
|
})
|
|
|
|
// 模拟API调用延迟
|
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
|
|
// 模拟AI生成的内容建议
|
|
const suggestions = [
|
|
"你可以分享当前的生活状态、工作情况,以及对未来的期望和梦想。也可以记录下此刻的心情和思考,让未来的你能够回忆起这段时光。",
|
|
"考虑写下你现在的目标和计划,以及希望未来的自己已经实现了哪些。也可以询问未来的你某些问题的答案,比如'你幸福吗?'、'你成为想成为的人了吗?'。",
|
|
"你可以描述当前的世界、科技、文化等,让未来的你能够对比时代的变迁。也可以分享一些珍贵的回忆和瞬间,这些对你来说可能意义非凡。",
|
|
"写下你对未来的预测和想象,无论是对个人生活还是对整个世界。这些预测在将来读来会非常有趣,看看你猜对了多少。"
|
|
]
|
|
|
|
const randomIndex = Math.floor(Math.random() * suggestions.length)
|
|
|
|
closeToast()
|
|
Dialog.alert({
|
|
title: '内容建议',
|
|
message: suggestions[randomIndex],
|
|
})
|
|
} catch (error) {
|
|
closeToast()
|
|
showFailToast('生成失败,请重试')
|
|
}
|
|
}
|
|
|
|
// AI情感分析
|
|
const analyzeEmotion = async () => {
|
|
if (!content.value) {
|
|
showFailToast('请先填写邮件内容')
|
|
return
|
|
}
|
|
|
|
try {
|
|
showLoadingToast({
|
|
message: '分析中...',
|
|
forbidClick: true,
|
|
})
|
|
|
|
// 模拟API调用延迟
|
|
await new Promise(resolve => setTimeout(resolve, 1500))
|
|
|
|
// 模拟情感分析结果
|
|
const sentiments = ['POSITIVE', 'NEUTRAL', 'EMOTIONAL', 'HOPEFUL']
|
|
const emotions = [
|
|
{ type: 'HAPPY', score: 0.7 },
|
|
{ type: 'HOPEFUL', score: 0.8 },
|
|
{ type: 'NOSTALGIC', score: 0.5 }
|
|
]
|
|
|
|
const randomSentiment = sentiments[Math.floor(Math.random() * sentiments.length)]
|
|
const emotionTypes = emotions.map(e => e.type).join('、')
|
|
|
|
const summaries = [
|
|
"这封信充满了对未来的期待和希望,表达了积极向上的情感。",
|
|
"文字中透露出对过去时光的怀念和对未来的思考。",
|
|
"这封信情感真挚,表达了内心深处的感受和想法。",
|
|
"文字中既有对现实的思考,也有对未来的憧憬和规划。"
|
|
]
|
|
|
|
const randomSummary = summaries[Math.floor(Math.random() * summaries.length)]
|
|
|
|
closeToast()
|
|
Dialog.alert({
|
|
title: '情感分析',
|
|
message: `情感倾向: ${randomSentiment}\n主要情感: ${emotionTypes}\n分析: ${randomSummary}`,
|
|
})
|
|
} catch (error) {
|
|
closeToast()
|
|
showFailToast('分析失败,请重试')
|
|
}
|
|
}
|
|
|
|
// 构建邮件数据
|
|
const buildMailData = () => {
|
|
// 计算发送时间
|
|
let sendTime
|
|
let triggerType = 'TIME'
|
|
let triggerCondition = {}
|
|
|
|
if (timeType.value === 'preset') {
|
|
const now = new Date()
|
|
sendTime = new Date(now)
|
|
|
|
switch (selectedPresetTime.value) {
|
|
case '1day':
|
|
sendTime.setDate(now.getDate() + 1)
|
|
break
|
|
case '1week':
|
|
sendTime.setDate(now.getDate() + 7)
|
|
break
|
|
case '1month':
|
|
sendTime.setMonth(now.getMonth() + 1)
|
|
break
|
|
case '1year':
|
|
sendTime.setFullYear(now.getFullYear() + 1)
|
|
break
|
|
case '5years':
|
|
sendTime.setFullYear(now.getFullYear() + 5)
|
|
break
|
|
case '10years':
|
|
sendTime.setFullYear(now.getFullYear() + 10)
|
|
break
|
|
}
|
|
} else if (timeType.value === 'custom') {
|
|
sendTime = customDeliveryDate.value
|
|
} else if (timeType.value === 'condition') {
|
|
triggerType = selectedLocation.value ? 'LOCATION' : 'EVENT'
|
|
|
|
if (selectedLocation.value) {
|
|
triggerCondition.location = selectedLocation.value
|
|
}
|
|
|
|
if (selectedEvent.value) {
|
|
triggerCondition.event = {
|
|
keywords: selectedEvent.value.keywords,
|
|
type: selectedEvent.value.type
|
|
}
|
|
}
|
|
}
|
|
|
|
// 格式化收件人类型
|
|
let recipientTypeFormatted = recipientType.value
|
|
|
|
return {
|
|
title: subject.value,
|
|
content: content.value,
|
|
recipientType: recipientTypeFormatted,
|
|
recipientEmail: recipientType.value === 'SPECIFIC' ? recipientEmail.value : undefined,
|
|
sendTime: sendTime ? sendTime.toISOString() : undefined,
|
|
triggerType,
|
|
triggerCondition,
|
|
attachments: attachments.value,
|
|
isEncrypted: isEncrypted.value,
|
|
capsuleStyle: capsuleStyle.value
|
|
}
|
|
}
|
|
|
|
// 保存草稿
|
|
const saveDraft = async () => {
|
|
if (!subject.value) {
|
|
showFailToast('请填写邮件标题')
|
|
return
|
|
}
|
|
|
|
try {
|
|
showLoadingToast({
|
|
message: '保存中...',
|
|
forbidClick: true,
|
|
})
|
|
|
|
// 模拟API调用延迟
|
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
|
|
// 保存到本地存储作为草稿
|
|
const mailData = buildMailData()
|
|
const drafts = JSON.parse(localStorage.getItem('draftMails') || '[]')
|
|
drafts.push({
|
|
...mailData,
|
|
id: Date.now().toString(),
|
|
status: 'DRAFT',
|
|
createdAt: new Date().toISOString()
|
|
})
|
|
localStorage.setItem('draftMails', JSON.stringify(drafts))
|
|
|
|
closeToast()
|
|
showSuccessToast('草稿已保存')
|
|
router.back()
|
|
} catch (error) {
|
|
closeToast()
|
|
const errorMessage = error.response?.data?.message || '保存失败,请重试'
|
|
showFailToast(errorMessage)
|
|
}
|
|
}
|
|
|
|
// 发送邮件
|
|
const sendMail = async () => {
|
|
if (!subject.value) {
|
|
showFailToast('请填写邮件标题')
|
|
return
|
|
}
|
|
|
|
if (!content.value) {
|
|
showFailToast('请填写邮件内容')
|
|
return
|
|
}
|
|
|
|
if (recipientType.value === 'SPECIFIC' && !recipientEmail.value) {
|
|
showFailToast('请填写收件人邮箱')
|
|
return
|
|
}
|
|
|
|
try {
|
|
showLoadingToast({
|
|
message: '发送中...',
|
|
forbidClick: true,
|
|
})
|
|
|
|
// 模拟API调用延迟
|
|
await new Promise(resolve => setTimeout(resolve, 1500))
|
|
|
|
const mailData = buildMailData()
|
|
|
|
// 保存到本地存储作为已发送邮件
|
|
const sentMails = JSON.parse(localStorage.getItem('sentMails') || '[]')
|
|
sentMails.push({
|
|
...mailData,
|
|
id: Date.now().toString(),
|
|
status: 'PENDING',
|
|
createdAt: new Date().toISOString()
|
|
})
|
|
localStorage.setItem('sentMails', JSON.stringify(sentMails))
|
|
|
|
closeToast()
|
|
|
|
// 计算发送时间用于显示
|
|
let deliveryDate
|
|
if (timeType.value === 'preset' || timeType.value === 'custom') {
|
|
deliveryDate = new Date(mailData.sendTime)
|
|
} else {
|
|
deliveryDate = new Date()
|
|
deliveryDate.setFullYear(deliveryDate.getFullYear() + 1) // 默认显示一年后
|
|
}
|
|
|
|
Dialog.confirm({
|
|
title: '邮件已发送',
|
|
message: `您的邮件将在${deliveryDate.toLocaleDateString()}送达,是否返回首页?`,
|
|
confirmButtonText: '返回首页',
|
|
cancelButtonText: '继续撰写',
|
|
})
|
|
.then(() => {
|
|
router.push('/home')
|
|
})
|
|
.catch(() => {
|
|
// 继续撰写
|
|
})
|
|
} catch (error) {
|
|
closeToast()
|
|
const errorMessage = error.response?.data?.message || '发送失败,请重试'
|
|
showFailToast(errorMessage)
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
generateStars()
|
|
})
|
|
|
|
return {
|
|
stars,
|
|
recipientType,
|
|
recipientEmail,
|
|
timeType,
|
|
selectedPresetTime,
|
|
customDeliveryDate,
|
|
minDate,
|
|
presetTimeOptions,
|
|
subject,
|
|
content,
|
|
showLocationPicker,
|
|
showEventPicker,
|
|
triggerEvents,
|
|
areaList,
|
|
goBack,
|
|
selectPresetTime,
|
|
onLocationConfirm,
|
|
selectEvent,
|
|
afterRead,
|
|
generateOpening,
|
|
generateSuggestions,
|
|
analyzeEmotion,
|
|
saveDraft,
|
|
sendMail
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.compose-container {
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 15px 20px;
|
|
margin: 15px;
|
|
z-index: 10;
|
|
}
|
|
|
|
.header h2 {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.compose-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 0 15px;
|
|
}
|
|
|
|
.form-section {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group h3 {
|
|
margin: 0 0 10px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.custom-field {
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.custom-field :deep(.van-field__control) {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* 单选按钮选中样式 */
|
|
:deep(.van-radio-group) {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
:deep(.van-radio) {
|
|
margin-right: 15px;
|
|
}
|
|
|
|
:deep(.van-radio__icon) {
|
|
font-size: 18px;
|
|
}
|
|
|
|
:deep(.van-radio__icon--checked .van-icon) {
|
|
background-color: #4285f4;
|
|
border-color: #4285f4;
|
|
box-shadow: 0 0 8px rgba(66, 133, 244, 0.5);
|
|
}
|
|
|
|
:deep(.van-radio__label) {
|
|
color: var(--text-primary);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.preset-options {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.preset-button {
|
|
margin-bottom: 10px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.preset-button.van-button--primary {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 4px 12px rgba(66, 133, 244, 0.4);
|
|
}
|
|
|
|
.custom-date-picker {
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.media-options {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.media-uploader {
|
|
display: inline-block;
|
|
}
|
|
|
|
.footer-actions {
|
|
padding: 15px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.save-button {
|
|
height: 50px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.send-button {
|
|
height: 50px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.preset-button {
|
|
margin: 5px;
|
|
}
|
|
|
|
.media-uploader .van-button {
|
|
/* 按钮样式已由全局样式处理 */
|
|
}
|
|
|
|
.event-picker {
|
|
padding: 20px;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.event-picker h3 {
|
|
margin: 0 0 15px;
|
|
font-size: 18px;
|
|
}
|
|
</style> |