Browse Source
1. 新增actPoster页面用于生成和分享活动海报 2. 修复活动分类筛选参数格式问题,改为用逗号连接的字符串 3. 优化活动列表和详情页的活动状态标签样式,新增状态分类配色 4. 完善活动报名、签到的逻辑判断条件 5. 优化首页布局,替换静态数据为接口请求的动态数据 6. 优化活动详情页的评论点赞逻辑和分享跳转逻辑 7. 新增活动报名成功页的用户订阅状态判断和二维码展示master
20 changed files with 798 additions and 138 deletions
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,6 @@ |
|||||||
|
{ |
||||||
|
"component": true, |
||||||
|
"usingComponents": { |
||||||
|
"van-icon": "@vant/weapp/icon/index" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
.page-back { |
||||||
|
font-size: 32rpx; |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
.page { |
||||||
|
height: 1627rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
padding: 396rpx 70rpx 0; |
||||||
|
.poster { |
||||||
|
display: block; |
||||||
|
height: 458rpx; |
||||||
|
border-radius: 32rpx; |
||||||
|
} |
||||||
|
.title { |
||||||
|
margin-top: 32rpx; |
||||||
|
font-size: 40rpx; |
||||||
|
line-height: 56rpx; |
||||||
|
color: rgba(17, 24, 39, 1); |
||||||
|
font-weight: bold; |
||||||
|
height: 168rpx; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
display: -webkit-box; |
||||||
|
-webkit-box-orient: vertical; |
||||||
|
-webkit-line-clamp: 3; |
||||||
|
} |
||||||
|
.user { |
||||||
|
margin-top: 74rpx; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 20rpx; |
||||||
|
.avatar { |
||||||
|
flex-shrink: 0; |
||||||
|
width: 92rpx; |
||||||
|
height: 92rpx; |
||||||
|
border-radius: 50%; |
||||||
|
} |
||||||
|
.wrap { |
||||||
|
flex: 1; |
||||||
|
.name { |
||||||
|
font-size: 28rpx; |
||||||
|
color: rgba(74, 184, 253, 1); |
||||||
|
line-height: 38rpx; |
||||||
|
} |
||||||
|
.date { |
||||||
|
margin-top: 8rpx; |
||||||
|
font-size: 24rpx; |
||||||
|
color: rgba(148, 163, 184, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
.code { |
||||||
|
margin-right: 12rpx; |
||||||
|
flex-shrink: 0; |
||||||
|
width: 104rpx; |
||||||
|
height: 104rpx; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
.footer { |
||||||
|
position: fixed; |
||||||
|
top: 1330rpx; |
||||||
|
left: 0; |
||||||
|
width: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
padding: 0 40rpx; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 30rpx; |
||||||
|
.btn { |
||||||
|
flex: 1; |
||||||
|
height: 96rpx; |
||||||
|
background: #ffffff; |
||||||
|
border-radius: 16rpx 16rpx 16rpx 16rpx; |
||||||
|
font-size: 32rpx; |
||||||
|
color: rgba(74, 184, 253, 1); |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
.share-btn { |
||||||
|
// 重置button默认样式 |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
border: none; |
||||||
|
line-height: normal; |
||||||
|
background: #ffffff; |
||||||
|
&::after { |
||||||
|
border: none; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,220 @@ |
|||||||
|
Page({ |
||||||
|
data: { |
||||||
|
posterImage: '', // 生成的海报图片路径
|
||||||
|
// Canvas尺寸 - 根据页面实际尺寸计算(rpx转px,750rpx = 375px)
|
||||||
|
canvasWidth: 375, |
||||||
|
canvasHeight: 814, // 1627rpx ≈ 813.5px
|
||||||
|
}, |
||||||
|
|
||||||
|
onLoad(_options: { id?: string }) { |
||||||
|
// 可以根据活动ID获取活动信息
|
||||||
|
}, |
||||||
|
|
||||||
|
onReady() { |
||||||
|
// 页面渲染完成
|
||||||
|
}, |
||||||
|
|
||||||
|
// 绘制海报
|
||||||
|
async drawPoster(): Promise<string> { |
||||||
|
try { |
||||||
|
wx.showLoading({ title: '生成中...' }) |
||||||
|
|
||||||
|
// 等待页面渲染完成
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300)) |
||||||
|
|
||||||
|
// 获取canvas实例
|
||||||
|
const canvasNode = await new Promise<any>((resolve, reject) => { |
||||||
|
const query = this.createSelectorQuery() |
||||||
|
query.select('#posterCanvas') |
||||||
|
.fields({ node: true, size: true }) |
||||||
|
.exec((res) => { |
||||||
|
if (res && res[0] && res[0].node) { |
||||||
|
resolve(res[0]) |
||||||
|
} else { |
||||||
|
console.error('Canvas查询失败,结果:', res) |
||||||
|
reject(new Error('Canvas未找到或未渲染')) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
const canvas = canvasNode.node |
||||||
|
const ctx = canvas.getContext('2d') |
||||||
|
|
||||||
|
// 设置canvas尺寸
|
||||||
|
const dpr = wx.getSystemInfoSync().pixelRatio |
||||||
|
canvas.width = this.data.canvasWidth * dpr |
||||||
|
canvas.height = this.data.canvasHeight * dpr |
||||||
|
ctx.scale(dpr, dpr) |
||||||
|
|
||||||
|
// rpx转px计算(750rpx = 375px)
|
||||||
|
const rpx2px = (rpx: number) => rpx * 0.5 |
||||||
|
|
||||||
|
// 绘制背景 - 覆盖整个Canvas
|
||||||
|
const bgImage = canvas.createImage() |
||||||
|
bgImage.src = `${this.data.imageUrl}bg7.png?t=${this.data.Timestamp}` |
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
bgImage.onload = resolve |
||||||
|
bgImage.onerror = reject |
||||||
|
}) |
||||||
|
ctx.drawImage(bgImage, 0, 0, this.data.canvasWidth, this.data.canvasHeight) |
||||||
|
|
||||||
|
// 绘制海报图片
|
||||||
|
// 页面样式:padding: 396rpx 70rpx 0; poster高度458rpx
|
||||||
|
const posterTop = rpx2px(396) // 198px
|
||||||
|
const posterLeft = rpx2px(70) // 35px
|
||||||
|
const posterWidth = this.data.canvasWidth - posterLeft * 2 // 305px
|
||||||
|
const posterHeight = rpx2px(458) // 229px
|
||||||
|
|
||||||
|
const posterImage = canvas.createImage() |
||||||
|
posterImage.src = `${this.data.imageUrl}bg1.png?t=${this.data.Timestamp}` |
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
posterImage.onload = resolve |
||||||
|
posterImage.onerror = reject |
||||||
|
}) |
||||||
|
ctx.drawImage(posterImage, posterLeft, posterTop, posterWidth, posterHeight) |
||||||
|
|
||||||
|
// 绘制标题
|
||||||
|
// 页面样式:margin-top: 32rpx; font-size: 40rpx; line-height: 56rpx; height: 168rpx
|
||||||
|
const titleTop = posterTop + posterHeight + rpx2px(32) // 198 + 229 + 16 = 443px
|
||||||
|
const titleFontSize = rpx2px(40) // 20px
|
||||||
|
const titleLineHeight = rpx2px(56) // 28px
|
||||||
|
const titleMaxWidth = posterWidth // 305px
|
||||||
|
|
||||||
|
ctx.fillStyle = 'rgba(17, 24, 39, 1)' |
||||||
|
ctx.font = `bold ${titleFontSize}px sans-serif` |
||||||
|
ctx.textAlign = 'left' |
||||||
|
const title = '深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛' |
||||||
|
// 文字换行处理
|
||||||
|
this.drawText(ctx, title, posterLeft, titleTop + titleFontSize, titleMaxWidth, titleLineHeight) |
||||||
|
|
||||||
|
// 绘制用户信息区域
|
||||||
|
// 页面样式:margin-top: 74rpx; avatar: 92rpx; gap: 20rpx
|
||||||
|
const userTop = titleTop + rpx2px(168) + rpx2px(74) // 443 + 84 + 37 = 564px
|
||||||
|
const avatarSize = rpx2px(92) // 46px
|
||||||
|
const gap = rpx2px(20) // 10px
|
||||||
|
|
||||||
|
const avatarImage = canvas.createImage() |
||||||
|
avatarImage.src = `${this.data.imageUrl}bg1.png?t=${this.data.Timestamp}` |
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
avatarImage.onload = resolve |
||||||
|
avatarImage.onerror = reject |
||||||
|
}) |
||||||
|
// 绘制头像(圆形)
|
||||||
|
ctx.save() |
||||||
|
ctx.beginPath() |
||||||
|
ctx.arc(posterLeft + avatarSize / 2, userTop + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI) |
||||||
|
ctx.clip() |
||||||
|
ctx.drawImage(avatarImage, posterLeft, userTop, avatarSize, avatarSize) |
||||||
|
ctx.restore() |
||||||
|
|
||||||
|
// 绘制用户名和日期
|
||||||
|
// 页面样式:name font-size: 28rpx; date font-size: 24rpx; margin-top: 8rpx
|
||||||
|
const nameLeft = posterLeft + avatarSize + gap // 35 + 46 + 10 = 91px
|
||||||
|
const nameFontSize = rpx2px(28) // 14px
|
||||||
|
const dateFontSize = rpx2px(24) // 12px
|
||||||
|
const dateMarginTop = rpx2px(8) // 4px
|
||||||
|
|
||||||
|
ctx.fillStyle = 'rgba(74, 184, 253, 1)' |
||||||
|
ctx.font = `${nameFontSize}px sans-serif` |
||||||
|
ctx.textAlign = 'left' |
||||||
|
ctx.fillText('亚南邀请您参与活动', nameLeft, userTop + nameFontSize + 5) |
||||||
|
|
||||||
|
ctx.fillStyle = 'rgba(148, 163, 184, 1)' |
||||||
|
ctx.font = `${dateFontSize}px sans-serif` |
||||||
|
ctx.fillText('2023年7月15日 18:30', nameLeft, userTop + nameFontSize + dateMarginTop + dateFontSize + 5) |
||||||
|
|
||||||
|
// 绘制二维码
|
||||||
|
// 页面样式:code: 104rpx; margin-right: 12rpx
|
||||||
|
const qrSize = rpx2px(104) // 52px
|
||||||
|
const qrRightMargin = rpx2px(12) // 6px
|
||||||
|
const qrLeft = this.data.canvasWidth - posterLeft - qrSize - qrRightMargin // 375 - 35 - 52 - 6 = 282px
|
||||||
|
|
||||||
|
const qrImage = canvas.createImage() |
||||||
|
qrImage.src = `${this.data.imageUrl}bg1.png?t=${this.data.Timestamp}` |
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
qrImage.onload = resolve |
||||||
|
qrImage.onerror = reject |
||||||
|
}) |
||||||
|
ctx.drawImage(qrImage, qrLeft, userTop, qrSize, qrSize) |
||||||
|
|
||||||
|
wx.hideLoading() |
||||||
|
|
||||||
|
// 将canvas转化为图片
|
||||||
|
const tempFilePath = await wx.canvasToTempFilePath({ |
||||||
|
canvas, |
||||||
|
width: this.data.canvasWidth, |
||||||
|
height: this.data.canvasHeight, |
||||||
|
destWidth: this.data.canvasWidth * dpr, |
||||||
|
destHeight: this.data.canvasHeight * dpr, |
||||||
|
}) |
||||||
|
|
||||||
|
this.setData({ posterImage: tempFilePath.tempFilePath }) |
||||||
|
return tempFilePath.tempFilePath |
||||||
|
} catch (error) { |
||||||
|
wx.hideLoading() |
||||||
|
console.error('绘制海报失败', error) |
||||||
|
throw error |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 文字换行绘制
|
||||||
|
drawText(ctx: any, text: string, x: number, y: number, maxWidth: number, lineHeight: number) { |
||||||
|
const chars = text.split('') |
||||||
|
let line = '' |
||||||
|
let lineCount = 0 |
||||||
|
|
||||||
|
for (let i = 0; i < chars.length; i++) { |
||||||
|
const testLine = line + chars[i] |
||||||
|
const metrics = ctx.measureText(testLine) |
||||||
|
if (metrics.width > maxWidth && i > 0) { |
||||||
|
ctx.fillText(line, x, y + lineCount * lineHeight) |
||||||
|
line = chars[i] |
||||||
|
lineCount++ |
||||||
|
} else { |
||||||
|
line = testLine |
||||||
|
} |
||||||
|
} |
||||||
|
ctx.fillText(line, x, y + lineCount * lineHeight) |
||||||
|
}, |
||||||
|
|
||||||
|
// 保存海报
|
||||||
|
async handleSavePoster() { |
||||||
|
try { |
||||||
|
const posterPath = await this.drawPoster() |
||||||
|
|
||||||
|
// 保存图片到相册
|
||||||
|
await wx.saveImageToPhotosAlbum({ filePath: posterPath }) |
||||||
|
wx.showToast({ title: '保存成功', icon: 'success' }) |
||||||
|
} catch (error: any) { |
||||||
|
console.error('保存海报失败', error) |
||||||
|
if (error.errMsg && error.errMsg.includes('auth deny')) { |
||||||
|
wx.showModal({ |
||||||
|
title: '提示', |
||||||
|
content: '需要授权保存图片到相册', |
||||||
|
success: (res) => { |
||||||
|
if (res.confirm) { |
||||||
|
wx.openSetting() |
||||||
|
} |
||||||
|
}, |
||||||
|
}) |
||||||
|
} else { |
||||||
|
wx.showToast({ title: '保存失败', icon: 'error' }) |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 分享给好友
|
||||||
|
onShareAppMessage() { |
||||||
|
return { |
||||||
|
title: '深职大第十五届校园歌手大赛', |
||||||
|
path: '/pages/actDetail/index?id=123', |
||||||
|
imageUrl: this.data.posterImage || '', |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
handleBack() { |
||||||
|
wx.navigateBack() |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
export {} |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
<navbar fixed customStyle="background:{{background}};"> |
||||||
|
<van-icon class="page-back" name="arrow-left" slot="left" bind:tap="handleBack" /> |
||||||
|
</navbar> |
||||||
|
|
||||||
|
<!-- 海报展示区域 --> |
||||||
|
<view class="poster-container" id="posterContainer"> |
||||||
|
<view class="page" style="background: url('{{imageUrl}}bg7.png?t={{Timestamp}}') no-repeat top center/100%"> |
||||||
|
<image class="poster" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> |
||||||
|
<view class="title">深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛</view> |
||||||
|
<view class="user"> |
||||||
|
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> |
||||||
|
<view class="wrap"> |
||||||
|
<view class="name">亚南邀请您参与活动</view> |
||||||
|
<view class="date">2023年7月15日 18:30</view> |
||||||
|
</view> |
||||||
|
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> |
||||||
|
</view> |
||||||
|
</view> |
||||||
|
</view> |
||||||
|
|
||||||
|
<!-- Canvas用于绘制海报 - 确保在页面中可见以便正确渲染 --> |
||||||
|
<canvas |
||||||
|
type="2d" |
||||||
|
id="posterCanvas" |
||||||
|
style="position: fixed; left: 0; top: 100vh; width: 375px; height: 814px; visibility: hidden;" |
||||||
|
></canvas> |
||||||
|
|
||||||
|
<view class="footer"> |
||||||
|
<view class="btn" bind:tap="handleSavePoster">保存海报</view> |
||||||
|
<button class="btn share-btn" open-type="share">分享给好友</button> |
||||||
|
</view> |
||||||
Loading…
Reference in new issue