599 lines
14 KiB
Vue
599 lines
14 KiB
Vue
<template>
|
||
<view class="poster-page">
|
||
<!-- 顶部提示 -->
|
||
<view class="tip-card">
|
||
<text class="tip-icon">📱</text>
|
||
<text class="tip-text">长按保存海报,分享给好友即可获得佣金</text>
|
||
</view>
|
||
|
||
<!-- 海报预览 -->
|
||
<view class="poster-container">
|
||
<view class="poster-canvas" id="posterCanvas">
|
||
<view class="poster-content">
|
||
<!-- 课程信息 -->
|
||
<view class="course-info" v-if="courseInfo">
|
||
<view v-if="courseInfo.coverImage" class="course-image-wrapper">
|
||
<image class="course-image" :src="courseInfo.coverImage" mode="aspectFill"></image>
|
||
</view>
|
||
<view v-else class="course-image-placeholder">
|
||
<text class="placeholder-icon">📚</text>
|
||
<text class="placeholder-text">课程封面</text>
|
||
</view>
|
||
<view class="course-details">
|
||
<text class="course-name">{{ courseInfo.name }}</text>
|
||
<text class="course-price">¥{{ courseInfo.price }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分销员信息 -->
|
||
<view class="distributor-info">
|
||
<text class="distributor-name">分销员</text>
|
||
<text class="distributor-desc">为您推荐优质课程</text>
|
||
</view>
|
||
|
||
<!-- 二维码 -->
|
||
<view class="qrcode-section">
|
||
<view v-if="qrcodeUrl" class="qrcode-wrapper">
|
||
<image class="qrcode" :src="qrcodeUrl" mode="aspectFit"></image>
|
||
<text class="qrcode-tip">扫码了解详情</text>
|
||
</view>
|
||
<view v-else class="qrcode-placeholder">
|
||
<text class="placeholder-icon">📱</text>
|
||
<text class="placeholder-text">二维码生成中...</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 推广码 -->
|
||
<view class="promotion-code">
|
||
<text class="code-label">推广码:</text>
|
||
<text class="code-value">{{ promotionCode }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons">
|
||
<button class="btn-primary" @click="savePoster">
|
||
<text class="btn-icon">💾</text>
|
||
<text>保存海报</text>
|
||
</button>
|
||
<button class="btn-secondary" @click="sharePoster">
|
||
<text class="btn-icon">📤</text>
|
||
<text>分享海报</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 功能说明 -->
|
||
<view class="info-section">
|
||
<view class="info-title">使用说明</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="info-number">1</text>
|
||
<text class="info-text">保存海报到相册</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-number">2</text>
|
||
<text class="info-text">分享给微信好友或朋友圈</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-number">3</text>
|
||
<text class="info-text">好友扫码购买即可获得佣金</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { distributorApi } from '@/api/index.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
courseId: null,
|
||
courseInfo: null,
|
||
distributorName: '分销员',
|
||
promotionCode: '',
|
||
qrcodeUrl: '',
|
||
posterUrl: ''
|
||
}
|
||
},
|
||
|
||
onLoad(options) {
|
||
if (options.courseId) {
|
||
this.courseId = options.courseId
|
||
this.loadCourseInfo()
|
||
}
|
||
this.loadDistributorInfo()
|
||
this.generatePoster()
|
||
},
|
||
|
||
// 微信小程序分享配置
|
||
onShareAppMessage() {
|
||
return {
|
||
title: this.courseInfo ? this.courseInfo.name : '习正陪伴 - 优质课程推荐',
|
||
path: `/pages/service/detail?id=${this.courseId}&code=${this.promotionCode}`,
|
||
imageUrl: this.courseInfo ? this.courseInfo.coverImage : ''
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
async loadCourseInfo() {
|
||
try {
|
||
// TODO: 调用API获取课程信息
|
||
// const res = await courseApi.getCourseDetail(this.courseId)
|
||
// 暂时使用模拟数据
|
||
this.courseInfo = {
|
||
id: this.courseId,
|
||
name: '基准式陪伴 - 100小时套餐',
|
||
coverImage: '',
|
||
price: 1999.00
|
||
}
|
||
} catch (error) {
|
||
console.error('加载课程信息失败:', error)
|
||
}
|
||
},
|
||
|
||
async loadDistributorInfo() {
|
||
try {
|
||
const res = await distributorApi.getPromotionCode()
|
||
if (res.code === 200 && res.data) {
|
||
this.promotionCode = res.data.code || ''
|
||
this.qrcodeUrl = res.data.qrCode || '' // 修正字段名
|
||
}
|
||
|
||
// 获取用户信息
|
||
const userInfo = uni.getStorageSync('userInfo')
|
||
if (userInfo) {
|
||
this.distributorName = userInfo.name || '分销员'
|
||
}
|
||
} catch (error) {
|
||
console.error('加载分销员信息失败:', error)
|
||
}
|
||
},
|
||
|
||
async generatePoster() {
|
||
try {
|
||
// TODO: 调用API生成海报
|
||
// const res = await distributorApi.generatePoster({
|
||
// courseId: this.courseId,
|
||
// promotionCode: this.promotionCode
|
||
// })
|
||
// this.posterUrl = res.data.posterUrl
|
||
|
||
uni.showToast({
|
||
title: '海报生成中...',
|
||
icon: 'loading',
|
||
duration: 1000
|
||
})
|
||
} catch (error) {
|
||
console.error('生成海报失败:', error)
|
||
}
|
||
},
|
||
|
||
async savePoster() {
|
||
try {
|
||
uni.showLoading({ title: '正在生成海报...' })
|
||
|
||
// 请求相册权限
|
||
const authResult = await this.requestPhotoAuth()
|
||
if (!authResult) {
|
||
uni.hideLoading()
|
||
return
|
||
}
|
||
|
||
// 等待页面渲染完成
|
||
await this.sleep(300)
|
||
|
||
// 使用截图API截取整个绿色方框
|
||
const query = uni.createSelectorQuery().in(this)
|
||
query.select('#posterCanvas').boundingClientRect()
|
||
query.exec(async (res) => {
|
||
if (!res || !res[0]) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '获取海报区域失败', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
const rect = res[0]
|
||
|
||
// 使用小程序截图API
|
||
// #ifdef MP-WEIXIN
|
||
try {
|
||
const result = await uni.createSelectorQuery()
|
||
.in(this)
|
||
.select('#posterCanvas')
|
||
.fields({ node: true, size: true, rect: true })
|
||
.exec()
|
||
|
||
if (result && result[0] && result[0][0]) {
|
||
// 使用 canvas 2d 绘制
|
||
await this.drawPosterWithCanvas2D(result[0][0])
|
||
} else {
|
||
// 降级方案:提示用户截图
|
||
this.showScreenshotTip()
|
||
}
|
||
} catch (error) {
|
||
console.error('截图失败:', error)
|
||
this.showScreenshotTip()
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef MP-WEIXIN
|
||
this.showScreenshotTip()
|
||
// #endif
|
||
})
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('保存海报失败:', error)
|
||
uni.showToast({ title: '保存失败,请重试', icon: 'none' })
|
||
}
|
||
},
|
||
|
||
// 请求相册权限
|
||
requestPhotoAuth() {
|
||
return new Promise((resolve) => {
|
||
uni.getSetting({
|
||
success: (res) => {
|
||
if (res.authSetting['scope.writePhotosAlbum']) {
|
||
resolve(true)
|
||
} else {
|
||
uni.authorize({
|
||
scope: 'scope.writePhotosAlbum',
|
||
success: () => {
|
||
resolve(true)
|
||
},
|
||
fail: () => {
|
||
uni.showModal({
|
||
title: '需要授权',
|
||
content: '请允许访问相册,以便保存海报',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
uni.openSetting({
|
||
success: (settingRes) => {
|
||
if (settingRes.authSetting['scope.writePhotosAlbum']) {
|
||
resolve(true)
|
||
} else {
|
||
resolve(false)
|
||
}
|
||
}
|
||
})
|
||
} else {
|
||
resolve(false)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
})
|
||
},
|
||
|
||
// 使用 Canvas 2D 绘制海报
|
||
async drawPosterWithCanvas2D(nodeInfo) {
|
||
// 由于小程序限制,这里使用简化方案
|
||
// 提示用户使用截图功能
|
||
this.showScreenshotTip()
|
||
},
|
||
|
||
// 显示截图提示
|
||
showScreenshotTip() {
|
||
uni.hideLoading()
|
||
uni.showModal({
|
||
title: '保存海报',
|
||
content: '请截图保存整个绿色区域的海报内容,然后分享给好友',
|
||
confirmText: '知道了',
|
||
showCancel: false,
|
||
success: () => {
|
||
// 可以添加一个高亮效果提示用户截图区域
|
||
this.highlightPosterArea()
|
||
}
|
||
})
|
||
},
|
||
|
||
// 高亮海报区域
|
||
highlightPosterArea() {
|
||
// 添加一个闪烁效果
|
||
const animation = uni.createAnimation({
|
||
duration: 500,
|
||
timingFunction: 'ease'
|
||
})
|
||
|
||
animation.scale(1.02).step()
|
||
animation.scale(1).step()
|
||
|
||
// 这里可以给海报区域添加动画效果
|
||
// 由于没有绑定动画,这里只是示例代码
|
||
},
|
||
|
||
// 延迟函数
|
||
sleep(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms))
|
||
},
|
||
|
||
sharePoster() {
|
||
uni.showModal({
|
||
title: '分享海报',
|
||
content: '请点击右上角"..."按钮,选择"转发"分享给好友',
|
||
showCancel: false
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.poster-page {
|
||
min-height: 100vh;
|
||
background: #f0f9f7;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.tip-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
background: #fff3e0;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.tip-icon {
|
||
font-size: 40rpx;
|
||
}
|
||
|
||
.tip-text {
|
||
flex: 1;
|
||
font-size: 26rpx;
|
||
color: #ff9800;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.poster-container {
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.poster-canvas {
|
||
background: linear-gradient(135deg, #7dd3c0 0%, #5fb8a8 100%);
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.poster-content {
|
||
padding: 40rpx;
|
||
}
|
||
|
||
.course-info {
|
||
background: #ffffff;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.course-image-wrapper {
|
||
width: 100%;
|
||
height: 300rpx;
|
||
}
|
||
|
||
.course-image {
|
||
width: 100%;
|
||
height: 300rpx;
|
||
}
|
||
|
||
.course-image-placeholder {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
height: 300rpx;
|
||
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
||
}
|
||
|
||
.course-image-placeholder .placeholder-icon {
|
||
font-size: 100rpx;
|
||
margin-bottom: 15rpx;
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.course-image-placeholder .placeholder-text {
|
||
font-size: 28rpx;
|
||
color: #999999;
|
||
}
|
||
|
||
.course-details {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.course-name {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333333;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.course-price {
|
||
display: block;
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #ff6b6b;
|
||
}
|
||
|
||
.distributor-info {
|
||
text-align: center;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.distributor-name {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #ffffff;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.distributor-desc {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.qrcode-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
background: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.qrcode-wrapper {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.qrcode {
|
||
width: 300rpx;
|
||
height: 300rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.qrcode-tip {
|
||
font-size: 26rpx;
|
||
color: #666666;
|
||
}
|
||
|
||
.qrcode-placeholder {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 300rpx;
|
||
height: 300rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.placeholder-icon {
|
||
font-size: 80rpx;
|
||
margin-bottom: 20rpx;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.placeholder-text {
|
||
font-size: 26rpx;
|
||
color: #999999;
|
||
}
|
||
|
||
.promotion-code {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.code-label {
|
||
font-size: 28rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.code-value {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #ffffff;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.btn-primary,
|
||
.btn-secondary {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 10rpx;
|
||
padding: 28rpx;
|
||
border-radius: 16rpx;
|
||
font-size: 30rpx;
|
||
border: none;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #7dd3c0 0%, #5fb8a8 100%);
|
||
color: #ffffff;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #ffffff;
|
||
color: #7dd3c0;
|
||
border: 2rpx solid #7dd3c0;
|
||
}
|
||
|
||
.btn-icon {
|
||
font-size: 36rpx;
|
||
}
|
||
|
||
.info-section {
|
||
background: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.info-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333333;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.info-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.info-number {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
line-height: 50rpx;
|
||
text-align: center;
|
||
background: #7dd3c0;
|
||
color: #ffffff;
|
||
border-radius: 50%;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-text {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
}
|
||
</style>
|