diff --git a/project.private.config.json b/project.private.config.json index 81dd7a9..8c3d1c9 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -4,11 +4,25 @@ "miniprogram": { "list": [ { + "name": "活动报名成功页", + "pathName": "pages/actResult/index", + "query": "id=59", + "scene": null, + "launchMode": "default" + }, + { + "name": "活动分享页", + "pathName": "pages/actPoster/index", + "query": "id=45", + "launchMode": "default", + "scene": null + }, + { "name": "课表", "pathName": "pages/schedule/index", "query": "", - "scene": null, - "launchMode": "default" + "launchMode": "default", + "scene": null }, { "name": "登录", diff --git a/src/app.json b/src/app.json index 5fc9f82..443e9b9 100644 --- a/src/app.json +++ b/src/app.json @@ -9,6 +9,7 @@ "pages/notice/index", "pages/actDetail/index", "pages/actResult/index", + "pages/actPoster/index", "pages/actAdd/index", "pages/actAddResult/index", "pages/noticeDetail/index", diff --git a/src/images/bg7.png b/src/images/bg7.png new file mode 100644 index 0000000..792303a Binary files /dev/null and b/src/images/bg7.png differ diff --git a/src/images/icon89.png b/src/images/icon89.png new file mode 100644 index 0000000..ff28bd8 Binary files /dev/null and b/src/images/icon89.png differ diff --git a/src/pages/act/index.scss b/src/pages/act/index.scss index 4b105cc..7381c32 100644 --- a/src/pages/act/index.scss +++ b/src/pages/act/index.scss @@ -163,9 +163,60 @@ page { left: 0; padding: 8rpx 16rpx; font-size: 22rpx; - color: rgba(255, 255, 255, 1); - background: #feb54a; border-radius: 16rpx 0rpx 16rpx 0rpx; + display: flex; + align-items: center; + gap: 8rpx; + .icon { + display: none; + width: 24rpx; + height: 24rpx; + } + &.status1 { + color: rgba(74, 184, 253, 1); + background: rgba(226, 244, 255, 1); + } + &.status2 { + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); + .icon { + display: block; + } + } + &.status3 { + color: rgba(255, 255, 255, 1); + background: rgba(111, 220, 174, 1); + .icon { + display: block; + } + } + &.status4 { + color: rgba(255, 255, 255, 1); + background: rgba(253, 91, 89, 1); + } + &.status5 { + color: rgba(255, 255, 255, 1); + background: rgba(111, 220, 174, 1); + } + &.status6 { + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); + } + &.status7 { + color: rgba(255, 255, 255, 1); + background: rgba(203, 213, 225, 1); + } + &.status8 { + color: rgba(100, 116, 139, 1); + background: rgba(233, 239, 245, 1); + } + &.status9 { + .icon { + display: block; + } + color: rgba(255, 255, 255, 1); + background: rgba(74, 184, 253, 1); + } } .p-img { border-radius: 16rpx; diff --git a/src/pages/act/index.ts b/src/pages/act/index.ts index ef8196f..e6a15f9 100644 --- a/src/pages/act/index.ts +++ b/src/pages/act/index.ts @@ -242,7 +242,7 @@ Page({ // 分类筛选:选中的分类 ID 数组(非空时传值) if (selectedCategoryIds.length > 0) { - params.categoryIds = selectedCategoryIds + params.categoryIds = selectedCategoryIds.join(',') } const res = await wx.ajax({ diff --git a/src/pages/act/index.wxml b/src/pages/act/index.wxml index 5f31317..15e2070 100644 --- a/src/pages/act/index.wxml +++ b/src/pages/act/index.wxml @@ -47,16 +47,17 @@ - + - {{item.activityStatusName}} - + + + {{item.activityStatusName}} + + {{item.regCount}}人已报名 diff --git a/src/pages/actDetail/index.scss b/src/pages/actDetail/index.scss index ad57c04..f3690d9 100644 --- a/src/pages/actDetail/index.scss +++ b/src/pages/actDetail/index.scss @@ -309,8 +309,8 @@ page { font-size: 28rpx; color: rgba(100, 116, 139, 1); } - &.active{ - .s-content{ + &.active { + .s-content { color: rgba(254, 181, 74, 1); } } @@ -359,6 +359,9 @@ page { display: flex; align-items: center; gap: 26rpx; + &:empty { + display: none; + } .com, .btn { flex: 1; diff --git a/src/pages/actDetail/index.ts b/src/pages/actDetail/index.ts index 0159cb9..2e46cc3 100644 --- a/src/pages/actDetail/index.ts +++ b/src/pages/actDetail/index.ts @@ -20,6 +20,8 @@ interface IActivityDetail { location: string organizer: string status: string + activityStatus: number + activityStatusName: string checkinType: number checkinStartAt: string checkinEndAt: string @@ -218,9 +220,11 @@ Page({ try { wx.showLoading({ title: '报名中...' }) const res = await wx.ajax({ - url: `/activity/register?id=${this.data.activityId}`, + url: `/activity/register`, method: 'POST', - data: {}, + data: { + activityId: this.data.activityId, + }, }) wx.hideLoading() @@ -298,9 +302,12 @@ Page({ // 上报分享 try { await wx.ajax({ - url: `/activity/share?id=${this.data.activityId}`, + url: `/activity/share`, method: 'POST', - data: { channel: 'friend' }, + data: { channel: 'friend', id: this.data.activityId }, + }) + wx.navigateTo({ + url: `/pages/actPoster/index?id=${this.data.activityId}`, }) } catch (err) { console.error('上报分享失败:', err) @@ -409,7 +416,7 @@ Page({ // 点赞评价 async handleLikeReview(e: WechatMiniprogram.TouchEvent) { - const { reviewId, islike } = e.currentTarget.dataset + const { id, islike } = e.currentTarget.dataset if (islike) return const { reviewList } = this.data @@ -426,18 +433,18 @@ Page({ url: `/activity/toggle-review-like`, method: 'POST', data: { - reviewId, + reviewId: id, }, }) if (res) { // 更新评价列表中的点赞状态 const updatedList = reviewList.map((item) => { - if (item.id === reviewId) { + if (item.id === id) { return { ...item, isLiked: res.isLiked, - likeCount: res.likeCount, + likeCount: item.likeCount + 1, } } return item @@ -482,7 +489,17 @@ Page({ }, // 获取活动状态文本 - getStatusText(status: string): string { + getStatusText(status: string | number): string { + // 如果是数字类型,处理 activityStatus + if (typeof status === 'number') { + const statusMap: Record = { + 5: '已发布', + 6: '进行中', + 7: '已结束', + } + return statusMap[status] || '未知状态' + } + // 如果是字符串类型,处理 status const statusMap: Record = { draft: '草稿', pending: '待审核', diff --git a/src/pages/actDetail/index.wxml b/src/pages/actDetail/index.wxml index 1c619eb..3201da0 100644 --- a/src/pages/actDetail/index.wxml +++ b/src/pages/actDetail/index.wxml @@ -107,7 +107,12 @@ {{item.createdAt}} - + {{item.likeCount}} @@ -146,21 +151,30 @@ 去评论 - 我要报名 + + + 我要报名 + + 签到 - 已报名 + + 已报名 - + diff --git a/src/pages/actPoster/index.json b/src/pages/actPoster/index.json new file mode 100644 index 0000000..fadd35e --- /dev/null +++ b/src/pages/actPoster/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "van-icon": "@vant/weapp/icon/index" + } +} diff --git a/src/pages/actPoster/index.scss b/src/pages/actPoster/index.scss new file mode 100644 index 0000000..8d545b1 --- /dev/null +++ b/src/pages/actPoster/index.scss @@ -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; + } + } +} diff --git a/src/pages/actPoster/index.ts b/src/pages/actPoster/index.ts new file mode 100644 index 0000000..1b83111 --- /dev/null +++ b/src/pages/actPoster/index.ts @@ -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 { + try { + wx.showLoading({ title: '生成中...' }) + + // 等待页面渲染完成 + await new Promise(resolve => setTimeout(resolve, 300)) + + // 获取canvas实例 + const canvasNode = await new Promise((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 {} diff --git a/src/pages/actPoster/index.wxml b/src/pages/actPoster/index.wxml new file mode 100644 index 0000000..09a2a2c --- /dev/null +++ b/src/pages/actPoster/index.wxml @@ -0,0 +1,31 @@ + + + + + + + + + 深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛深职大第十五届校园歌手大赛 + + + + 亚南邀请您参与活动 + 2023年7月15日 18:30 + + + + + + + + + + + 保存海报 + + diff --git a/src/pages/actResult/index.ts b/src/pages/actResult/index.ts index 541290e..71460fa 100644 --- a/src/pages/actResult/index.ts +++ b/src/pages/actResult/index.ts @@ -16,6 +16,8 @@ Page({ activityId: 0, detail: null as any, recommendList: [] as IActivityItem[], + isSubscribed: false, + codeUrl: '', }, onLoad(options: { id?: string }) { @@ -25,6 +27,20 @@ Page({ app.waitLogin({ type: 0 }).then(() => { this.fetchActivityDetail() this.fetchRecommendList() + this.getCode() + }) + }, + + getCode() { + wx.ajax({ + method: 'GET', + url: '/me/profile', + data: {}, + }).then((res) => { + this.setData({ + isSubscribed: res.wechatSubscribe.isSubscribed, + codeUrl: res.wechatSubscribe.qrCodeUrl, + }) }) }, @@ -80,4 +96,5 @@ Page({ }, }) -export {} \ No newline at end of file +export {} + diff --git a/src/pages/actResult/index.wxml b/src/pages/actResult/index.wxml index cbdc28f..d38e518 100644 --- a/src/pages/actResult/index.wxml +++ b/src/pages/actResult/index.wxml @@ -3,8 +3,8 @@ 报名成功 活动将于{{detail.startAt}}开始,请记得准时参加 - - + + 返回活动页 @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/src/pages/index/index.json b/src/pages/index/index.json index 2352750..e61bf15 100644 --- a/src/pages/index/index.json +++ b/src/pages/index/index.json @@ -4,6 +4,7 @@ "van-icon": "@vant/weapp/icon/index", "van-notice-bar": "@vant/weapp/notice-bar/index", "van-tab": "@vant/weapp/tab/index", - "van-tabs": "@vant/weapp/tabs/index" + "van-tabs": "@vant/weapp/tabs/index", + "pagination": "/components/pagination/index" } } diff --git a/src/pages/index/index.scss b/src/pages/index/index.scss index eef323e..f9d59c5 100644 --- a/src/pages/index/index.scss +++ b/src/pages/index/index.scss @@ -43,6 +43,18 @@ page { --notice-bar-padding: 0; --notice-bar-line-height: 40rpx; --notice-bar-height: 40rpx; + .notice-swiper { + height: 40rpx; + line-height: 40rpx; + } + .notice-text { + font-size: 28rpx; + color: rgba(71, 85, 105, 1); + line-height: 40rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } .more { color: rgba(148, 163, 184, 1); @@ -202,9 +214,60 @@ page { left: 0; padding: 8rpx 16rpx; font-size: 22rpx; - color: rgba(255, 255, 255, 1); - background: #feb54a; border-radius: 16rpx 0rpx 16rpx 0rpx; + display: flex; + align-items: center; + gap: 8rpx; + .icon { + display: none; + width: 24rpx; + height: 24rpx; + } + &.status1 { + color: rgba(74, 184, 253, 1); + background: rgba(226, 244, 255, 1); + } + &.status2 { + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); + .icon { + display: block; + } + } + &.status3 { + color: rgba(255, 255, 255, 1); + background: rgba(111, 220, 174, 1); + .icon { + display: block; + } + } + &.status4 { + color: rgba(255, 255, 255, 1); + background: rgba(253, 91, 89, 1); + } + &.status5 { + color: rgba(255, 255, 255, 1); + background: rgba(111, 220, 174, 1); + } + &.status6 { + color: rgba(255, 255, 255, 1); + background: rgba(254, 181, 74, 1); + } + &.status7 { + color: rgba(255, 255, 255, 1); + background: rgba(203, 213, 225, 1); + } + &.status8 { + color: rgba(100, 116, 139, 1); + background: rgba(233, 239, 245, 1); + } + &.status9 { + .icon { + display: block; + } + color: rgba(255, 255, 255, 1); + background: rgba(74, 184, 253, 1); + } } .p-img { border-radius: 16rpx; diff --git a/src/pages/index/index.ts b/src/pages/index/index.ts index e9618c5..998eb65 100644 --- a/src/pages/index/index.ts +++ b/src/pages/index/index.ts @@ -4,60 +4,189 @@ Page({ data: { statusBarHeight: 44, activeTab: 0, - navItems: [ - { type: 'schedule', label: '智能课表', icon: '/images/icon_schedule.png' }, - { type: 'bus', label: '校园巴士', icon: '/images/icon_bus.png' }, - { type: 'race', label: '活动报名', icon: '/images/icon_race.png' }, - { type: 'ai', label: 'AI辅导员', icon: '/images/icon_ai.png' }, - ], - tabList: [ - { label: '首页', icon: '/images/icon_home_active.png' }, - { label: '活动', icon: '/images/icon_calendar.png' }, - { label: '智能体', icon: '/images/icon_ai.png' }, - { label: '通知', icon: '/images/icon_bell.png' }, - { label: '我的', icon: '/images/icon_user.png' }, - ], - recommendAgents: [ - { id: 'ppt', name: 'PPT小助手', icon: '/images/agent_ppt.png', bgImage: '/images/activity_1.png' }, - { id: 'ielts', name: '雅思模拟考官', icon: '/images/agent_ielts.png', bgImage: '/images/activity_2.png' }, - { id: 'pdf', name: 'PDF翻译', icon: '/images/agent_pdf.png', bgImage: '/images/activity_3.png' }, - ], - recommendActivities: [ - { - id: '3', - title: '英语角交流活动', - desc: '提升口语能力', - time: '活动时间:2026-04-01~2026-05-30', - image: '/images/activity_1.png', - }, - { - id: '4', - title: '大学生创业项目大赛', - desc: '提升创新能力', - time: '活动时间:2026-04-01~2026-05-30', - image: '/images/activity_2.png', - }, - ], - hotActivities: [ - { - id: '1', - title: '计算机学院编程大赛', - image: '/images/activity_1.png', - deadline: '截止日期:2026年5月30日', - }, - { - id: '2', - title: '创新创业项目路演', - image: '/images/activity_2.png', - deadline: '截止日期:2026年6月15日', - }, - ], + // 首页聚合数据 + searchPlaceholder: '请搜索你想要的内容', + notifications: [] as any[], + quickEntries: [] as any[], + hotActivities: [] as any[], + recommendAgents: [] as any[], + bannerItems: [] as any[], + currentBannerIndex: 0, + currentBannerImage: '', + // 推荐活动分页数据 + latestActivities: [] as any[], + activityPagination: { + page: 1, + pages: 1, + count: 0, + }, + activityLoading: false, + // 智能体数据 + agentList: [] as any[], }, + bannerTimer: null as number | null, + onLoad() { const sysInfo = wx.getSystemInfoSync() this.setData({ statusBarHeight: sysInfo.statusBarHeight || 44 }) - app.waitLogin() + app.waitLogin().then(() => { + this.fetchHomeData() + this.fetchRecommendedActivities(1) + // 智能体推荐列表暂不联调 + // this.fetchAgentList() + }) + }, + + onUnload() { + if (this.bannerTimer) { + clearInterval(this.bannerTimer) + this.bannerTimer = null + } + }, + + onReachBottom() { + // 触底加载更多推荐活动 + if (this.data.activeTab === 0) { + this.loadMoreActivities() + } + }, + + fetchHomeData() { + wx.ajax({ + url: '/home/index', + method: 'GET', + }).then((res: any) => { + const data: any = {} + + // 搜索模块 + if (res.searchModule?.config?.placeholder) { + data.searchPlaceholder = res.searchModule.config.placeholder + } + + // Banner 模块 + if (res.bannerModule?.items?.length) { + data.bannerItems = res.bannerModule.items + data.currentBannerImage = res.bannerModule.items[0].imageUrl + this.startBannerRotation(res.bannerModule.items) + } + + // 快捷入口模块 + if (res.quickEntryModule?.items?.length) { + data.quickEntries = res.quickEntryModule.items + } + + // 通知栏模块 + if (res.notificationModule?.data?.length) { + data.notifications = res.notificationModule.data + } + + // 热门活动模块 - 处理mainImages JSON字符串解析 + if (res.hotActivityModule?.data?.length) { + const hotActivities = res.hotActivityModule.data.map((item: any) => { + let mainImages = item.mainImages + // 如果mainImages是字符串,解析为数组 + if (typeof mainImages === 'string') { + try { + mainImages = JSON.parse(mainImages) + } catch (e) { + console.error('解析mainImages失败', e) + mainImages = [] + } + } + return { ...item, mainImages } + }) + data.hotActivities = hotActivities + } + + // 推荐智能体模块 + if (res.recommendAgentModule?.data?.length) { + data.recommendAgents = res.recommendAgentModule.data + } + + this.setData(data) + }).catch((err) => { + console.error('获取首页数据失败', err) + }) + }, + + fetchRecommendedActivities(page: number) { + if (this.data.activityLoading) return + + this.setData({ activityLoading: true }) + + wx.ajax({ + url: '/activity/list', + method: 'GET', + data: { getRecommended: 1, page, pageSize: 10 }, + }).then((res: any) => { + const list = res.list || [] + const pagination = res.pagination || {} + + // 处理mainImages JSON字符串解析 + const activities = list.map((item: any) => { + let mainImages = item.mainImages + if (typeof mainImages === 'string') { + try { + mainImages = JSON.parse(mainImages) + } catch (e) { + console.error('解析mainImages失败', e) + mainImages = [] + } + } + return { ...item, mainImages } + }) + + // 如果是第一页,直接替换;否则追加 + const latestActivities = page === 1 ? activities : [...this.data.latestActivities, ...activities] + + this.setData({ + latestActivities, + activityPagination: { + page: pagination.page || page, + pages: pagination.totalPages || 1, + count: pagination.total || 0, + }, + activityLoading: false, + }) + }).catch((err) => { + console.error('获取推荐活动失败', err) + this.setData({ activityLoading: false }) + }) + }, + + loadMoreActivities() { + const { activityPagination, activityLoading } = this.data + if (activityLoading || activityPagination.page >= activityPagination.pages) return + + this.fetchRecommendedActivities(activityPagination.page + 1) + }, + + fetchAgentList() { + wx.ajax({ + url: '/agent/list', + method: 'GET', + data: { page: 1, pageSize: 10 }, + }).then((res: any) => { + if (res.list?.length) { + this.setData({ agentList: res.list }) + } + }).catch((err) => { + console.error('获取智能体列表失败', err) + }) + }, + + startBannerRotation(bannerItems: any[]) { + if (bannerItems.length <= 1) return + + this.bannerTimer = setInterval(() => { + const currentIndex = this.data.currentBannerIndex + const nextIndex = (currentIndex + 1) % bannerItems.length + this.setData({ + currentBannerIndex: nextIndex, + currentBannerImage: bannerItems[nextIndex].imageUrl, + }) + }, 3000) }, onSearchTap() { diff --git a/src/pages/index/index.wxml b/src/pages/index/index.wxml index 8850dde..6a48986 100644 --- a/src/pages/index/index.wxml +++ b/src/pages/index/index.wxml @@ -1,42 +1,35 @@ - 请搜索你想要的内容 + {{searchPlaceholder}} - + - + + + {{item.title}} + + - - - - 智能课表 - - - - 校园巴士 - - - - 活动报名 - - - - AI辅导员 + + + + {{item.name}} - + 热门活动 @@ -45,12 +38,12 @@ - - + + - + 推荐智能体 @@ -59,21 +52,9 @@ - - - PPT小助手 - - - - 雅思模拟考官 - - - - PDF翻译 - - - - 雅思模拟考官 + + + {{item.name}} @@ -88,23 +69,43 @@ > - + + + + + {{item.activityStatusName}} + + + + + {{item.name}} + {{item.regCount}}人已报名 + + + {{item.startAt}} + + + + + + + + + - 进行中 - + - 深职大第十五届校园歌手大赛 - 128人已报名 + {{item.name}} + {{item.brief}} - 2026.04.01-2026.05.30 + {{item.usageCount}}次使用 - 内容 2