Ship signup sheet, subscribe reminders, cover crop, and theme updates.

Wire real WeChat app config, prefer activity cover over posters, and add admin cover cropping for the home banner.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michelle Zan
2026-10-08 13:06:43 +08:00
co-authored by Cursor
parent cd05113755
commit 0ca2e87bf2
35 changed files with 968 additions and 184 deletions
+9 -3
View File
@@ -5,7 +5,8 @@ const activityUtil = require('../../utils/activity')
Page({
data: {
activity: null,
failed: false
failed: false,
signupOpen: false
},
onLoad(query) {
@@ -36,8 +37,13 @@ Page({
activityUtil.openMap(this.data.activity)
},
goSignup() {
activityUtil.goSignup(this.data.activity)
openSignup() {
if (!this.data.activity || this.data.activity.status === 'past') return
this.setData({ signupOpen: true })
},
closeSignup() {
this.setData({ signupOpen: false })
},
goHome() {
+2 -1
View File
@@ -1,6 +1,7 @@
{
"navigationBarTitleText": "卓米会",
"usingComponents": {
"activity-board": "/components/activity-board/index"
"activity-board": "/components/activity-board/index",
"signup-sheet": "/components/signup-sheet/index"
}
}
+13 -7
View File
@@ -1,10 +1,16 @@
<activity-board
wx:if="{{activity}}"
activity="{{activity}}"
show-past="{{false}}"
bind:map="openMap"
bind:signup="goSignup"
/>
<block wx:if="{{activity}}">
<activity-board
activity="{{activity}}"
show-past="{{false}}"
bind:map="openMap"
bind:signup="openSignup"
/>
<signup-sheet
show="{{signupOpen}}"
activity-id="{{activity.id}}"
bind:close="closeSignup"
/>
</block>
<view wx:elif="{{failed}}" class="empty">
<text>活动不存在或已下线</text>
<view class="home" bindtap="goHome">回到首页</view>
+10 -3
View File
@@ -5,7 +5,8 @@ const fallback = require('../../utils/fallback')
Page({
data: {
activity: fallback.CURRENT
activity: fallback.CURRENT,
signupOpen: false
},
onLoad(query) {
@@ -34,8 +35,14 @@ Page({
activityUtil.openMap(this.data.activity)
},
goSignup() {
activityUtil.goSignup(this.data.activity)
openSignup() {
if (!this.data.activity || !this.data.activity.id) return
if (this.data.activity.status === 'past') return
this.setData({ signupOpen: true })
},
closeSignup() {
this.setData({ signupOpen: false })
},
goPast() {
+2 -1
View File
@@ -2,6 +2,7 @@
"navigationBarTitleText": "卓米会",
"enablePullDownRefresh": true,
"usingComponents": {
"activity-board": "/components/activity-board/index"
"activity-board": "/components/activity-board/index",
"signup-sheet": "/components/signup-sheet/index"
}
}
+6 -1
View File
@@ -2,6 +2,11 @@
activity="{{activity}}"
show-past="{{true}}"
bind:map="openMap"
bind:signup="goSignup"
bind:signup="openSignup"
bind:past="goPast"
/>
<signup-sheet
show="{{signupOpen}}"
activity-id="{{activity.id}}"
bind:close="closeSignup"
/>
+18
View File
@@ -12,12 +12,29 @@ function chooseOne(mediaType) {
success: (res) => {
const file = res.tempFiles && res.tempFiles[0]
if (file && file.tempFilePath) resolve(file.tempFilePath)
else reject(new Error('未选择文件'))
},
fail: reject
})
})
}
// Crop to a wide banner ratio before upload (closest built-in scale is 16:9).
function cropCover(filePath) {
return new Promise((resolve, reject) => {
if (!wx.cropImage) {
resolve(filePath)
return
}
wx.cropImage({
src: filePath,
cropScale: '16:9',
success: (res) => resolve(res.tempFilePath || filePath),
fail: () => resolve(filePath)
})
})
}
function uploadWithLoading(filePath) {
wx.showLoading({ title: '上传中', mask: true })
return api.uploadMedia(filePath).then(
@@ -71,6 +88,7 @@ Page({
pickCover() {
chooseOne('image')
.then(cropCover)
.then(uploadWithLoading)
.then((data) => this.setData({ cover: data.url, coverPreview: media.absUrl(data.url) }))
.catch((error) => {
+3 -1
View File
@@ -4,7 +4,9 @@ const activityUtil = require('../../utils/activity')
function decorate(item) {
const cover = item.cover || item.poster || ''
return Object.assign({}, item, { coverUrl: cover ? media.absUrl(cover) : '' })
return Object.assign({}, item, {
coverUrl: cover ? media.absUrl(cover) : ''
})
}
Page({
+5
View File
@@ -4,6 +4,11 @@
<view class="btn del {{deleting ? 'active' : ''}}" bindtap="onDelete">{{deleting ? '完成' : '删除'}}</view>
</view>
<view class="section-head">
<view class="section-bar"></view>
<text class="section-title">往期活动</text>
</view>
<view wx:if="{{loaded && !list.length}}" class="empty">还没有往期活动</view>
<view
+23 -2
View File
@@ -5,7 +5,28 @@
.actions {
display: flex;
gap: 20rpx;
margin-bottom: 4rpx;
margin-bottom: 28rpx;
}
.section-head {
display: flex;
align-items: center;
gap: 14rpx;
margin-bottom: 8rpx;
}
.section-bar {
width: 8rpx;
height: 32rpx;
border-radius: 4rpx;
background: #0d8b87;
}
.section-title {
color: #111827;
font-size: 32rpx;
font-weight: 700;
line-height: 1.2;
}
.btn {
@@ -35,7 +56,7 @@
}
.empty {
margin-top: 160rpx;
margin-top: 120rpx;
text-align: center;
color: #9ca3af;
font-size: 28rpx;
+5 -61
View File
@@ -1,71 +1,15 @@
const api = require('../../utils/api')
const authUtil = require('../../utils/auth')
const activityUtil = require('../../utils/activity')
Page({
data: {
name: '',
phone: '',
company: '',
submitting: false
activityId: ''
},
onLoad(query) {
this.activityId = (query && query.id) || ''
api
.profile()
.then((profile) => {
if (!profile) return
this.setData({
name: this.data.name || profile.name || '',
phone: this.data.phone || profile.phone || '',
company: this.data.company || profile.company || ''
})
})
.catch(() => {})
this.setData({ activityId: (query && query.id) || '' })
},
onName(event) {
this.setData({ name: event.detail.value })
onClose() {
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
},
onPhone(event) {
this.setData({ phone: event.detail.value })
},
onCompany(event) {
this.setData({ company: event.detail.value })
},
submit() {
if (this.data.submitting) return
const name = this.data.name.trim()
const phone = this.data.phone.trim()
const company = this.data.company.trim()
if (!name || !phone) {
wx.showToast({ title: '请填写姓名和手机号', icon: 'none' })
return
}
if (!/^1\d{10}$/.test(phone)) {
wx.showToast({ title: '请填写正确的手机号', icon: 'none' })
return
}
this.setData({ submitting: true })
api
.signup({
activityId: this.activityId,
name: name,
phone: phone,
company: company,
sourceSharer: authUtil.sourceSharer()
})
.then(() => {
wx.showToast({ title: '报名成功', icon: 'success' })
setTimeout(() => wx.navigateBack(), 600)
})
.catch((error) => {
this.setData({ submitting: false })
activityUtil.toast(error)
})
}
onSuccess() {}
})
+4 -1
View File
@@ -1,3 +1,6 @@
{
"navigationBarTitleText": "立即报名"
"navigationBarTitleText": "活动报名",
"usingComponents": {
"signup-sheet": "/components/signup-sheet/index"
}
}
+6 -12
View File
@@ -1,12 +1,6 @@
<view class="page">
<view class="card">
<text class="label">姓名</text>
<input class="input" placeholder="怎么称呼你" placeholder-class="placeholder" value="{{name}}" maxlength="20" bindinput="onName" />
<text class="label">手机号</text>
<input class="input" type="number" placeholder="用于活动通知" placeholder-class="placeholder" value="{{phone}}" maxlength="11" bindinput="onPhone" />
<text class="label">公司 / 机构</text>
<input class="input" placeholder="选填" placeholder-class="placeholder" value="{{company}}" maxlength="40" bindinput="onCompany" />
</view>
<view class="save {{submitting ? 'busy' : ''}}" bindtap="submit">{{submitting ? '提交中…' : '提交报名'}}</view>
<text class="hint">手机号仅用于本次活动通知。</text>
</view>
<signup-sheet
show="{{true}}"
activity-id="{{activityId}}"
bind:close="onClose"
bind:success="onSuccess"
/>
+2 -51
View File
@@ -1,52 +1,3 @@
.page {
padding: 28rpx 32rpx 48rpx;
}
.card {
padding: 12rpx 32rpx 28rpx;
background: #ffffff;
border-radius: 28rpx;
}
.label {
display: block;
margin-top: 28rpx;
color: #6b7280;
font-size: 26rpx;
}
.input {
height: 88rpx;
margin-top: 8rpx;
border-bottom: 1rpx solid #eef0f3;
font-size: 32rpx;
}
.placeholder {
color: #c4c9d1;
}
.save {
height: 96rpx;
margin-top: 36rpx;
border-radius: 20rpx;
background: #128a80;
color: #ffffff;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.save.busy {
opacity: 0.7;
}
.hint {
display: block;
margin-top: 20rpx;
text-align: center;
color: #9ca3af;
font-size: 24rpx;
page {
background: transparent;
}