测试activityPoster
This commit is contained in:
parent
7d1f0bc651
commit
ac431927b7
@ -3,7 +3,7 @@ module.exports = {
|
|||||||
title: '',
|
title: '',
|
||||||
// baseApi: '//h5.ufutx.net/api' // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
// baseApi: '//h5.ufutx.net/api' // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
||||||
baseApi: '//sh5.ufutx.net/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
baseApi: '//sh5.ufutx.net/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
||||||
baseApiV2: '//sh5.ufutx.net/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
baseApiV2: '//love.ufutx.cn/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
||||||
baseApiV3: '//eval.fulllinkai.net/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
baseApiV3: '//eval.fulllinkai.net/api', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
||||||
// baseApi: '//api.ufutx.net/mock' // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
// baseApi: '//api.ufutx.net/mock' // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
|
||||||
// APPID: 'xxx',
|
// APPID: 'xxx',
|
||||||
|
|||||||
@ -1,92 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="poster-generator-container">
|
<div class="poster-generator-container">
|
||||||
<!-- 表单选择区域 -->
|
|
||||||
<div class="input-section">
|
|
||||||
<van-field
|
|
||||||
v-model="selectedActivityTitle"
|
|
||||||
label="选择活动"
|
|
||||||
placeholder="请选择活动"
|
|
||||||
readonly
|
|
||||||
:label-width="60"
|
|
||||||
@click="showActivityPicker = true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<van-field
|
<div class="input-card">
|
||||||
v-model="sponsorName"
|
<div class="card-header">
|
||||||
label="主办方"
|
<span class="header-icon">📋</span>
|
||||||
placeholder="请选择主办方"
|
<span class="header-title">选择活动</span>
|
||||||
readonly
|
</div>
|
||||||
:label-width="60"
|
<div class="activity-selector" @click="showActivityPicker = true">
|
||||||
@click="showSponsorPicker = true"
|
<div class="selector-label">活动名称</div>
|
||||||
/>
|
<div class="selector-value" :class="{ placeholder: !selectedActivityTitle }">
|
||||||
|
<span class="selector-text">{{ selectedActivityTitle || '点击选择活动' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="selector-arrow">
|
||||||
|
<van-icon name="arrow" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 活动选择弹窗 -->
|
<!-- 活动选择弹窗 -->
|
||||||
<van-popup
|
<van-popup v-model:show="showActivityPicker" position="bottom" round class="activity-popup">
|
||||||
v-model="showActivityPicker"
|
|
||||||
position="bottom"
|
|
||||||
round
|
|
||||||
class="activity-popup"
|
|
||||||
>
|
|
||||||
<div class="activity-picker-header">
|
<div class="activity-picker-header">
|
||||||
<span class="cancel-btn" @click="showActivityPicker = false">取消</span>
|
<span class="cancel-btn" @click="showActivityPicker = false">取消</span>
|
||||||
<span class="title">选择活动</span>
|
<span class="title">选择活动</span>
|
||||||
<span class="confirm-btn" @click="showActivityPicker = false">确定</span>
|
<span class="confirm-btn" @click="showActivityPicker = false">确定</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="scroll-wrapper">
|
<div class="scroll-wrapper">
|
||||||
<van-list
|
<van-list v-model:loading="listLoading" :finished="listFinished" finished-text="没有更多活动了"
|
||||||
v-model="listLoading"
|
@load="onLoadMore">
|
||||||
:finished="listFinished"
|
<div v-for="item in activityList" :key="item.id" class="activity-item"
|
||||||
finished-text="没有更多活动了"
|
:class="{ active: selectedActivity?.id === item.id }" @click="onSelectActivity(item)">
|
||||||
@load="onLoadMore"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="item in activityList"
|
|
||||||
:key="item.id"
|
|
||||||
class="activity-item"
|
|
||||||
@click="onSelectActivity(item)"
|
|
||||||
>
|
|
||||||
<div class="activity-title">{{ item.title }}</div>
|
<div class="activity-title">{{ item.title }}</div>
|
||||||
<div class="activity-time">
|
<div class="activity-time">{{ item.Subtitle }} | {{ formatDateToShow(item.end_time) }}</div>
|
||||||
{{ item.Subtitle }} | {{ formatDateToShow(item.end_time) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</van-list>
|
</van-list>
|
||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
|
||||||
<!-- 主办方选择弹窗 -->
|
<!-- 隐藏海报区域(样式保持原样) -->
|
||||||
<van-popup
|
|
||||||
v-model="showSponsorPicker"
|
|
||||||
position="bottom"
|
|
||||||
round
|
|
||||||
class="activity-popup"
|
|
||||||
>
|
|
||||||
<div class="activity-picker-header">
|
|
||||||
<span class="cancel-btn" @click="showSponsorPicker = false">取消</span>
|
|
||||||
<span class="title">选择主办方</span>
|
|
||||||
<span class="confirm-btn" @click="showSponsorPicker = false">确定</span>
|
|
||||||
</div>
|
|
||||||
<div class="scroll-wrapper">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in sponsorList"
|
|
||||||
:key="index"
|
|
||||||
class="activity-item"
|
|
||||||
@click="onSelectSponsor(item)"
|
|
||||||
>
|
|
||||||
<div class="activity-title">{{ item }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</van-popup>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 海报生成容器(隐藏布局) -->
|
|
||||||
<div ref="posterRef" class="poster-wrapper hidden-poster">
|
<div ref="posterRef" class="poster-wrapper hidden-poster">
|
||||||
<div class="poster-card">
|
<div class="poster-card">
|
||||||
<div class="poster-content">
|
<div class="poster-content">
|
||||||
<img
|
<img src="https://images.health.ufutx.com/202604/13/8fa1b3080a60f97ef3f75e1370a6217c.jpeg" alt="海报背景图" />
|
||||||
src="https://images.health.ufutx.com/202604/13/8fa1b3080a60f97ef3f75e1370a6217c.jpeg"
|
|
||||||
alt="海报背景"
|
|
||||||
/>
|
|
||||||
<div class="poster-content-wrapper">
|
<div class="poster-content-wrapper">
|
||||||
<div class="poster-title">
|
<div class="poster-title">
|
||||||
<p class="poster-title-text">{{ selectedActivity?.Subtitle || '河南·襄城站' }}</p>
|
<p class="poster-title-text">{{ selectedActivity?.Subtitle || '河南·襄城站' }}</p>
|
||||||
@ -117,135 +71,119 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮组 -->
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button
|
<button class="btn btn-primary" :disabled="isGenerating" @click="generatePoster">
|
||||||
class="btn btn-primary"
|
<van-loading v-if="isGenerating" size="20px" color="#fff" />
|
||||||
:disabled="isGenerating"
|
<span v-else>✨ 生成海报</span>
|
||||||
@click="generatePoster"
|
|
||||||
>
|
|
||||||
<span v-if="!isGenerating">生成海报</span>
|
|
||||||
<span v-else>生成中...</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="posterImg"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
@click="downloadPoster"
|
|
||||||
>
|
|
||||||
下载海报
|
|
||||||
</button>
|
</button>
|
||||||
|
<button v-if="posterImg && !isWeChatEnv" class="btn btn-secondary" @click="downloadPoster"> 📥 下载海报</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 海报预览 -->
|
<!-- 生成预览区域 -->
|
||||||
<div v-if="posterImg" class="generated-poster-preview">
|
<div v-if="posterImg" class="generated-poster-preview">
|
||||||
<h4 class="preview-title">海报生成成功(长按保存/点击预览)</h4>
|
<div class="preview-header">
|
||||||
<img :src="posterImg" class="preview-img" @click="showImagePreview" />
|
<div class="preview-icon">🎉</div>
|
||||||
|
<h4 class="preview-title">{{ isWeChatEnv ? '长按图片保存到手机' : '点击图片预览大图' }}</h4>
|
||||||
|
</div>
|
||||||
|
<img :src="posterImg" alt="生成的海报" class="preview-img" @click="showImagePreviewFn(posterImg)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { nextTick, onMounted, ref, watch } from 'vue'
|
||||||
import html2canvas from 'html2canvas'
|
import html2canvas from 'html2canvas'
|
||||||
|
import { showDialog, showImagePreview, showLoadingToast, showToast } from 'vant'
|
||||||
|
import requestApp from '@/utils/requestApp'
|
||||||
import QRCode from 'qrcode'
|
import QRCode from 'qrcode'
|
||||||
import axios from 'axios'
|
import { weXinShare } from '@/plugins/wxShare'
|
||||||
|
|
||||||
export default {
|
// 🔥 主办方:从活动接口自动获取,无需手动选择
|
||||||
name: 'ActivityPoster',
|
const sponsorName = ref('')
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// 主办方配置
|
|
||||||
sponsorName: '友福同享(襄城县)智能科技有限公司',
|
|
||||||
sponsorList: [
|
|
||||||
'友福同享(深圳)智能科技有限公司',
|
|
||||||
'友福粤越(惠州)智能科技有限公司',
|
|
||||||
'友福同享(南昌)智能科技有限公司',
|
|
||||||
'友福同享(天津)智能科技有限公司',
|
|
||||||
'友福同享(襄城县)智能科技有限公司',
|
|
||||||
'友福同享(哈尔滨)智能科技有限公司'
|
|
||||||
],
|
|
||||||
showSponsorPicker: false,
|
|
||||||
|
|
||||||
// 活动相关
|
const posterRef = ref(null)
|
||||||
activityList: [],
|
const posterImg = ref('')
|
||||||
selectedActivity: null,
|
const isGenerating = ref(false)
|
||||||
selectedActivityTitle: '',
|
const isWeChatEnv = ref(false)
|
||||||
showActivityPicker: false,
|
const qrcodeCanvas = ref(null)
|
||||||
listLoading: false,
|
|
||||||
listFinished: false,
|
|
||||||
|
|
||||||
// 海报相关
|
const activityList = ref([])
|
||||||
posterImg: '',
|
const selectedActivity = ref(null)
|
||||||
isGenerating: false,
|
const selectedActivityTitle = ref('')
|
||||||
isWeChatEnv: false
|
const showActivityPicker = ref(false)
|
||||||
}
|
const listLoading = ref(false)
|
||||||
},
|
const listFinished = ref(true)
|
||||||
mounted() {
|
const openId = ref('')
|
||||||
this.detectWeChatEnv()
|
|
||||||
this.getActivityList()
|
// 监听选中的活动:自动填充主办方 + 生成二维码
|
||||||
},
|
watch(selectedActivity, async (newVal) => {
|
||||||
watch: {
|
if (newVal && newVal.id && qrcodeCanvas.value) {
|
||||||
selectedActivity: {
|
// 自动从活动数据中读取主办方
|
||||||
handler: async function(val) {
|
sponsorName.value = newVal.sponsor || ''
|
||||||
if (val && val.id && this.$refs.qrcodeCanvas) {
|
openId.value = localStorage.getItem('openid')
|
||||||
const openId = localStorage.getItem('openid') || ''
|
const link = `https://love.ufutx.cn/api/official/live/wechat/FamilyAuth?merchant_id=44&serve_tab=&from_openid=${openId.value}&url=https%3A%2F%2Flove.ufutx.cn%2Fpu%2F%23%2FactivityDetails%2F${newVal.id}`
|
||||||
const link = `https://love.ufutx.cn/api/official/live/wechat/FamilyAuth?merchant_id=44&serve_tab=&from_openid=${openId}&url=https%3A%2F%2Flove.ufutx.cn%2Fpu%2F%23%2FactivityDetails%2F${val.id}`
|
await QRCode.toCanvas(qrcodeCanvas.value, link, {
|
||||||
await QRCode.toCanvas(this.$refs.qrcodeCanvas, link, {
|
|
||||||
width: 76,
|
width: 76,
|
||||||
margin: 1,
|
margin: 1,
|
||||||
color: { dark: '#000000', light: '#ffffff' }
|
color: { dark: '#000000', light: '#ffffff' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 获取活动列表
|
|
||||||
async getActivityList() {
|
|
||||||
this.listLoading = true
|
|
||||||
try {
|
|
||||||
const res = await axios({
|
|
||||||
url: '/sh5/uftx/community/activity/list',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
if (res.code === 0 && Array.isArray(res.data)) {
|
|
||||||
this.activityList = res.data
|
|
||||||
} else {
|
|
||||||
this.$toast('活动列表获取失败')
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
this.$toast('网络异常,请重试')
|
|
||||||
} finally {
|
|
||||||
this.listLoading = false
|
|
||||||
this.listFinished = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 选择活动
|
|
||||||
onSelectActivity(item) {
|
|
||||||
this.selectedActivity = item
|
|
||||||
this.selectedActivityTitle = item.title
|
|
||||||
this.showActivityPicker = false
|
|
||||||
},
|
|
||||||
// 选择主办方
|
|
||||||
onSelectSponsor(item) {
|
|
||||||
this.sponsorName = item
|
|
||||||
this.showSponsorPicker = false
|
|
||||||
},
|
|
||||||
onLoadMore() {
|
|
||||||
this.listFinished = true
|
|
||||||
},
|
|
||||||
|
|
||||||
// 时间格式化
|
const getData = () => {
|
||||||
formatDateToShow(timeStr) {
|
listLoading.value = true
|
||||||
|
weXinShare('https://image.fulllinkai.com/202310/28/88e931a50ec0a8094fb46191b389457e.png', `https://health.ufutx.cn/go_html/role_apply#/activityPoster`, '活动海报生成「saas」', '查看详情')
|
||||||
|
requestApp({ url: '/sh5/uftx/community/activity/list', method: 'get' })
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 0 && Array.isArray(res.data)) {
|
||||||
|
activityList.value = res.data
|
||||||
|
// 默认选中第一个活动,自动填充主办方
|
||||||
|
// if (res.data.length > 0) {
|
||||||
|
// onSelectActivity(res.data[0]);
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
showToast('活动列表获取失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('获取活动列表失败:', err)
|
||||||
|
showToast('活动列表获取失败,请重试')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
listLoading.value = false
|
||||||
|
listFinished.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const detectWeChatEnv = () => {
|
||||||
|
const userAgent = navigator.userAgent.toLowerCase()
|
||||||
|
isWeChatEnv.value = /micromessenger/.test(userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择活动:自动填充主办方
|
||||||
|
const onSelectActivity = (item) => {
|
||||||
|
selectedActivity.value = item
|
||||||
|
selectedActivityTitle.value = item.title
|
||||||
|
showActivityPicker.value = false
|
||||||
|
generatePoster()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoadMore = () => {
|
||||||
|
listLoading.value = false
|
||||||
|
listFinished.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDateToShow = (timeStr) => {
|
||||||
if (!timeStr) return ''
|
if (!timeStr) return ''
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr)
|
||||||
const year = date.getFullYear()
|
const year = date.getFullYear()
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
const day = String(date.getDate()).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
return `${year}年${month}月${day}日`
|
return `${year}年${month}月${day}日`
|
||||||
},
|
}
|
||||||
formatDateToChinese(timeStr) {
|
|
||||||
|
const formatDateToChinese = (timeStr) => {
|
||||||
if (!timeStr) {
|
if (!timeStr) {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const year = now.getFullYear()
|
const year = now.getFullYear()
|
||||||
@ -256,133 +194,83 @@ export default {
|
|||||||
const [datePart] = timeStr.split(' ')
|
const [datePart] = timeStr.split(' ')
|
||||||
const [year, month, day] = datePart.split('-')
|
const [year, month, day] = datePart.split('-')
|
||||||
return `${year}/${month}/${day}`
|
return `${year}/${month}/${day}`
|
||||||
},
|
}
|
||||||
formatTime(timeStr) {
|
|
||||||
|
const formatTime = (timeStr) => {
|
||||||
if (!timeStr) return '14:00'
|
if (!timeStr) return '14:00'
|
||||||
const [, timePart] = timeStr.split(' ')
|
const [, timePart] = timeStr.split(' ')
|
||||||
return timePart.slice(0, 5)
|
return timePart.slice(0, 5)
|
||||||
},
|
}
|
||||||
|
|
||||||
// 微信环境检测
|
const showImagePreviewFn = (img) => {
|
||||||
detectWeChatEnv() {
|
return showImagePreview([img])
|
||||||
this.isWeChatEnv = /micromessenger/.test(navigator.userAgent.toLowerCase())
|
}
|
||||||
},
|
|
||||||
// 图片预览
|
|
||||||
showImagePreview() {
|
|
||||||
window.open(this.posterImg)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 生成海报
|
const generatePoster = async () => {
|
||||||
async generatePoster() {
|
try {
|
||||||
if (!this.selectedActivity) {
|
if (!selectedActivity.value) {
|
||||||
this.$dialog.alert({ message: '请先选择活动!' })
|
showDialog({ message: '请先选择活动!' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
isGenerating.value = true
|
||||||
this.isGenerating = true
|
const toast = showLoadingToast({
|
||||||
await this.$nextTick()
|
message: '海报生成中...',
|
||||||
const canvas = await html2canvas(this.$refs.posterRef, {
|
forbidClick: true,
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
const posterElement = posterRef.value
|
||||||
|
if (!posterElement) {
|
||||||
|
isGenerating.value = false
|
||||||
|
toast.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = await html2canvas(posterElement, {
|
||||||
useCORS: true,
|
useCORS: true,
|
||||||
scale: 3,
|
scale: 3,
|
||||||
backgroundColor: null,
|
backgroundColor: null,
|
||||||
logging: false
|
logging: false,
|
||||||
|
imageTimeout: 10000
|
||||||
})
|
})
|
||||||
this.posterImg = canvas.toDataURL('image/png')
|
|
||||||
this.$toast('海报生成成功')
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
this.$dialog.alert({ message: '生成失败,请重试' })
|
|
||||||
} finally {
|
|
||||||
this.isGenerating = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 下载海报
|
posterImg.value = canvas.toDataURL('image/png', 1.0)
|
||||||
downloadPoster() {
|
toast.close()
|
||||||
const link = document.createElement('a')
|
showToast({ message: '海报生成成功!', icon: 'success' })
|
||||||
link.href = this.posterImg
|
} catch (err) {
|
||||||
const fileName = `${this.selectedActivity.title || '活动'}海报.png`
|
console.error('生成海报失败:', err)
|
||||||
link.download = fileName.replace(/[^a-zA-Z0-9_\u4e00-\u9fa5]/g, '')
|
showToast({ message: '生成海报失败,请重试!', icon: 'fail' })
|
||||||
link.click()
|
} finally {
|
||||||
}
|
isGenerating.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadPoster() {
|
||||||
|
if (!posterImg.value) return
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = posterImg.value
|
||||||
|
// 修复文件名括号闭合、正则多余/的问题
|
||||||
|
const fileName = `[${selectedActivity.value?.title || '活动'}]海报.png`
|
||||||
|
link.download = fileName.replace(/[\\/:*?"<>|]/g, '')
|
||||||
|
link.click()
|
||||||
|
URL.revokeObjectURL(link.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
detectWeChatEnv()
|
||||||
|
getData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
/* 基础样式 */
|
img {
|
||||||
.poster-generator-container {
|
display: inline-block !important;
|
||||||
width: 100%;
|
max-width: 100%;
|
||||||
min-height: 100vh;
|
max-height: 100%;
|
||||||
padding: 20px;
|
|
||||||
background: #f5f7fa;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-section {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 弹窗样式 */
|
|
||||||
.activity-popup {
|
|
||||||
height: 80vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-picker-header {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-btn {
|
|
||||||
color: #999;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.confirm-btn {
|
|
||||||
color: #18ca6e;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item {
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #f5f5f5;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: #f5f7fa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-title {
|
|
||||||
font-size: 15px;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 海报隐藏容器 */
|
|
||||||
.poster-wrapper {
|
.poster-wrapper {
|
||||||
width: 375px;
|
width: 375px;
|
||||||
height: 788px;
|
height: 788px;
|
||||||
@ -394,40 +282,59 @@ export default {
|
|||||||
left: -9999px;
|
left: -9999px;
|
||||||
top: -9999px;
|
top: -9999px;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-card {
|
.poster-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
color: #ffffff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #333333;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poster-content img {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
.poster-content {
|
.poster-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 海报文字样式 */
|
|
||||||
.poster-title {
|
.poster-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 262px;
|
top: 262px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
letter-spacing: 4px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 400;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-title-text {
|
.poster-title .poster-title-text {
|
||||||
color: #0d2f73;
|
text-align: center;
|
||||||
font-size: 20px;
|
|
||||||
width: 300px;
|
width: 300px;
|
||||||
margin: 0 auto;
|
color: #0d2f73;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 2px 0;
|
||||||
|
margin: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sponsor-name {
|
.sponsor-name {
|
||||||
@ -435,9 +342,15 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 446px;
|
top: 446px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
letter-spacing: 0.8px;
|
||||||
color: #0e2965;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-name .name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
color: #0e2965;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-qrcode {
|
.poster-qrcode {
|
||||||
@ -447,14 +360,16 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-qrcode-image {
|
.poster-qrcode-image {
|
||||||
width: 76px;
|
width: 76px;
|
||||||
height: 76px;
|
height: 76px;
|
||||||
background: #fff;
|
|
||||||
padding: 3px;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-time-wrap {
|
.poster-time-wrap {
|
||||||
@ -463,20 +378,25 @@ export default {
|
|||||||
top: 630px;
|
top: 630px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #0e2866;
|
justify-content: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-date {
|
.poster-date {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #0e2866;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-time {
|
.poster-time {
|
||||||
|
font-weight: 400;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
color: #0e2866;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.start-time {
|
.start-time {
|
||||||
margin-right: 30px;
|
margin-right: 30px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-address {
|
.poster-address {
|
||||||
@ -484,70 +404,314 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 720px;
|
top: 720px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
._text-warp {
|
.poster-address ._text-warp {
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
background: #fff;
|
font-size: 14px;
|
||||||
padding: 4px 16px;
|
|
||||||
border-radius: 32px;
|
|
||||||
color: #0f2967;
|
color: #0f2967;
|
||||||
|
font-weight: 500;
|
||||||
|
background: white;
|
||||||
|
border-radius: 32px;
|
||||||
|
padding: 4px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
._title {
|
.poster-address ._title {
|
||||||
width: 76px;
|
width: 76px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮样式 */
|
.poster-address ._text {
|
||||||
|
text-align: left;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== 以下为美化后的外部样式(选择器、按钮、预览区域) ========== */
|
||||||
|
.poster-generator-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px 16px 40px;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #e9eef3 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入卡片 */
|
||||||
|
.input-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.06);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-card:hover {
|
||||||
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 16px 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
background: linear-gradient(135deg, #18ca6e, #0f9d58);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 20px 12px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-selector:active {
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6b7280;
|
||||||
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-value {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f2937;
|
||||||
|
text-align: right;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-text {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
max-width: 100%;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-value.placeholder .selector-text {
|
||||||
|
color: #9ca3af;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-arrow {
|
||||||
|
color: #9ca3af;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗样式 */
|
||||||
|
.activity-popup {
|
||||||
|
height: 70vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-picker-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #9ca3af;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #18ca6e;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item:active {
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item.active {
|
||||||
|
background: #f0fdf4;
|
||||||
|
border-left: 3px solid #18ca6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮组 */
|
||||||
.btn-group {
|
.btn-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 16px;
|
||||||
margin: 30px 0;
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 12px 0;
|
padding: 14px 20px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 60px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #fff;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: #18ca6e;
|
background: linear-gradient(135deg, #18ca6e, #0f9d58);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 202, 110, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
&:disabled {
|
.btn-primary:active {
|
||||||
background: #95d8b7;
|
transform: scale(0.97);
|
||||||
|
box-shadow: 0 2px 6px rgba(24, 202, 110, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
transform: none;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: #2c3e50;
|
background: #2c3e50;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:active {
|
||||||
|
transform: scale(0.97);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 预览区域 */
|
/* 预览区域 */
|
||||||
.generated-poster-preview {
|
.generated-poster-preview {
|
||||||
background: #fff;
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 28px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 12px;
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
|
||||||
text-align: center;
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-icon {
|
||||||
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-title {
|
.preview-title {
|
||||||
color: #2c3e50;
|
font-size: 15px;
|
||||||
margin-bottom: 15px;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
color: #374151;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-img {
|
.preview-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-img:active {
|
||||||
|
transform: scale(0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 适配小屏幕 */
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.poster-generator-container {
|
||||||
|
padding: 16px 12px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ module.exports = {
|
|||||||
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
productionSourceMap: false, // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
||||||
devServer: {
|
devServer: {
|
||||||
port: 8080, // 端口
|
port: 8080, // 端口
|
||||||
open: false, // 启动后打开浏览器
|
open: true, // 启动后打开浏览器
|
||||||
overlay: {
|
overlay: {
|
||||||
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
|
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
|
||||||
warnings: false,
|
warnings: false,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user