554 lines
12 KiB
Vue
554 lines
12 KiB
Vue
<template>
|
|
<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
|
|
v-model="sponsorName"
|
|
label="主办方"
|
|
placeholder="请选择主办方"
|
|
readonly
|
|
:label-width="60"
|
|
@click="showSponsorPicker = true"
|
|
/>
|
|
|
|
<!-- 活动选择弹窗 -->
|
|
<van-popup
|
|
v-model="showActivityPicker"
|
|
position="bottom"
|
|
round
|
|
class="activity-popup"
|
|
>
|
|
<div class="activity-picker-header">
|
|
<span class="cancel-btn" @click="showActivityPicker = false">取消</span>
|
|
<span class="title">选择活动</span>
|
|
<span class="confirm-btn" @click="showActivityPicker = false">确定</span>
|
|
</div>
|
|
<div class="scroll-wrapper">
|
|
<van-list
|
|
v-model="listLoading"
|
|
:finished="listFinished"
|
|
finished-text="没有更多活动了"
|
|
@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-time">
|
|
{{ item.Subtitle }} | {{ formatDateToShow(item.end_time) }}
|
|
</div>
|
|
</div>
|
|
</van-list>
|
|
</div>
|
|
</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 class="poster-card">
|
|
<div class="poster-content">
|
|
<img
|
|
src="https://images.health.ufutx.com/202604/13/8fa1b3080a60f97ef3f75e1370a6217c.jpeg"
|
|
alt="海报背景"
|
|
/>
|
|
<div class="poster-content-wrapper">
|
|
<div class="poster-title">
|
|
<p class="poster-title-text">{{ selectedActivity?.Subtitle || '河南·襄城站' }}</p>
|
|
</div>
|
|
<div class="sponsor-name">
|
|
<p class="name">主办:{{ sponsorName }}</p>
|
|
</div>
|
|
|
|
<div class="poster-qrcode">
|
|
<canvas ref="qrcodeCanvas" class="poster-qrcode-image"></canvas>
|
|
</div>
|
|
|
|
<div class="poster-time-wrap">
|
|
<div class="poster-date">{{ formatDateToChinese(selectedActivity?.end_time) }}</div>
|
|
<div class="poster-time">
|
|
<span class="start-time">{{ formatTime(selectedActivity?.start_time) }}</span>
|
|
<span class="end-time">{{ formatTime(selectedActivity?.end_time) }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="poster-address">
|
|
<div class="_text-warp">
|
|
<div class="_title">活动地点:</div>
|
|
<div class="_text">{{ selectedActivity?.address || '广东省深圳市南山区阳光科创' }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 操作按钮 -->
|
|
<div class="btn-group">
|
|
<button
|
|
class="btn btn-primary"
|
|
:disabled="isGenerating"
|
|
@click="generatePoster"
|
|
>
|
|
<span v-if="!isGenerating">生成海报</span>
|
|
<span v-else>生成中...</span>
|
|
</button>
|
|
<button
|
|
v-if="posterImg"
|
|
class="btn btn-secondary"
|
|
@click="downloadPoster"
|
|
>
|
|
下载海报
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 海报预览 -->
|
|
<div v-if="posterImg" class="generated-poster-preview">
|
|
<h4 class="preview-title">海报生成成功(长按保存/点击预览)</h4>
|
|
<img :src="posterImg" class="preview-img" @click="showImagePreview" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import html2canvas from 'html2canvas'
|
|
import QRCode from 'qrcode'
|
|
import axios from 'axios'
|
|
|
|
export default {
|
|
name: 'ActivityPoster',
|
|
data() {
|
|
return {
|
|
// 主办方配置
|
|
sponsorName: '友福同享(襄城县)智能科技有限公司',
|
|
sponsorList: [
|
|
'友福同享(深圳)智能科技有限公司',
|
|
'友福粤越(惠州)智能科技有限公司',
|
|
'友福同享(南昌)智能科技有限公司',
|
|
'友福同享(天津)智能科技有限公司',
|
|
'友福同享(襄城县)智能科技有限公司',
|
|
'友福同享(哈尔滨)智能科技有限公司'
|
|
],
|
|
showSponsorPicker: false,
|
|
|
|
// 活动相关
|
|
activityList: [],
|
|
selectedActivity: null,
|
|
selectedActivityTitle: '',
|
|
showActivityPicker: false,
|
|
listLoading: false,
|
|
listFinished: false,
|
|
|
|
// 海报相关
|
|
posterImg: '',
|
|
isGenerating: false,
|
|
isWeChatEnv: false
|
|
}
|
|
},
|
|
mounted() {
|
|
this.detectWeChatEnv()
|
|
this.getActivityList()
|
|
},
|
|
watch: {
|
|
selectedActivity: {
|
|
handler: async function(val) {
|
|
if (val && val.id && this.$refs.qrcodeCanvas) {
|
|
const openId = localStorage.getItem('openid') || ''
|
|
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(this.$refs.qrcodeCanvas, link, {
|
|
width: 76,
|
|
margin: 1,
|
|
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
|
|
},
|
|
|
|
// 时间格式化
|
|
formatDateToShow(timeStr) {
|
|
if (!timeStr) return ''
|
|
const date = new Date(timeStr)
|
|
const year = date.getFullYear()
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
return `${year}年${month}月${day}日`
|
|
},
|
|
formatDateToChinese(timeStr) {
|
|
if (!timeStr) {
|
|
const now = new Date()
|
|
const year = now.getFullYear()
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|
return `${year}/${month}/${day}`
|
|
}
|
|
const [datePart] = timeStr.split(' ')
|
|
const [year, month, day] = datePart.split('-')
|
|
return `${year}/${month}/${day}`
|
|
},
|
|
formatTime(timeStr) {
|
|
if (!timeStr) return '14:00'
|
|
const [, timePart] = timeStr.split(' ')
|
|
return timePart.slice(0, 5)
|
|
},
|
|
|
|
// 微信环境检测
|
|
detectWeChatEnv() {
|
|
this.isWeChatEnv = /micromessenger/.test(navigator.userAgent.toLowerCase())
|
|
},
|
|
// 图片预览
|
|
showImagePreview() {
|
|
window.open(this.posterImg)
|
|
},
|
|
|
|
// 生成海报
|
|
async generatePoster() {
|
|
if (!this.selectedActivity) {
|
|
this.$dialog.alert({ message: '请先选择活动!' })
|
|
return
|
|
}
|
|
|
|
try {
|
|
this.isGenerating = true
|
|
await this.$nextTick()
|
|
const canvas = await html2canvas(this.$refs.posterRef, {
|
|
useCORS: true,
|
|
scale: 3,
|
|
backgroundColor: null,
|
|
logging: false
|
|
})
|
|
this.posterImg = canvas.toDataURL('image/png')
|
|
this.$toast('海报生成成功')
|
|
} catch (err) {
|
|
console.error(err)
|
|
this.$dialog.alert({ message: '生成失败,请重试' })
|
|
} finally {
|
|
this.isGenerating = false
|
|
}
|
|
},
|
|
|
|
// 下载海报
|
|
downloadPoster() {
|
|
const link = document.createElement('a')
|
|
link.href = this.posterImg
|
|
const fileName = `${this.selectedActivity.title || '活动'}海报.png`
|
|
link.download = fileName.replace(/[^a-zA-Z0-9_\u4e00-\u9fa5]/g, '')
|
|
link.click()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
/* 基础样式 */
|
|
.poster-generator-container {
|
|
width: 100%;
|
|
min-height: 100vh;
|
|
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 {
|
|
width: 375px;
|
|
height: 788px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hidden-poster {
|
|
position: absolute;
|
|
left: -9999px;
|
|
top: -9999px;
|
|
z-index: -1;
|
|
}
|
|
|
|
.poster-card {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
.poster-content {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
}
|
|
|
|
/* 海报文字样式 */
|
|
.poster-title {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 262px;
|
|
width: 100%;
|
|
text-align: center;
|
|
}
|
|
|
|
.poster-title-text {
|
|
color: #0d2f73;
|
|
font-size: 20px;
|
|
width: 300px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.sponsor-name {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 446px;
|
|
width: 100%;
|
|
text-align: center;
|
|
color: #0e2965;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.poster-qrcode {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 532px;
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.poster-qrcode-image {
|
|
width: 76px;
|
|
height: 76px;
|
|
background: #fff;
|
|
padding: 3px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.poster-time-wrap {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 630px;
|
|
width: 100%;
|
|
text-align: center;
|
|
color: #0e2866;
|
|
}
|
|
|
|
.poster-date {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.poster-time {
|
|
font-size: 22px;
|
|
margin-top: 14px;
|
|
|
|
.start-time {
|
|
margin-right: 30px;
|
|
}
|
|
}
|
|
|
|
.poster-address {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 720px;
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
._text-warp {
|
|
max-width: 80%;
|
|
background: #fff;
|
|
padding: 4px 16px;
|
|
border-radius: 32px;
|
|
color: #0f2967;
|
|
display: flex;
|
|
}
|
|
|
|
._title {
|
|
width: 76px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* 按钮样式 */
|
|
.btn-group {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin: 30px 0;
|
|
}
|
|
|
|
.btn {
|
|
flex: 1;
|
|
padding: 12px 0;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 16px;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #18ca6e;
|
|
|
|
&:disabled {
|
|
background: #95d8b7;
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #2c3e50;
|
|
}
|
|
|
|
/* 预览区域 */
|
|
.generated-poster-preview {
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
text-align: center;
|
|
}
|
|
|
|
.preview-title {
|
|
color: #2c3e50;
|
|
margin-bottom: 15px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.preview-img {
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
}
|
|
</style>
|