peixue-dev/peidu/uniapp/distributor-package/pages/distributor/poster.vue

599 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>