Add 卓米会 mini program and admin server.
Initial commit of the WeChat mini program, Express backend, and admin console. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
const media = require('../../utils/media')
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
activity: {
|
||||
type: Object,
|
||||
value: null
|
||||
},
|
||||
showPast: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
data: {
|
||||
poster: '',
|
||||
video: '',
|
||||
ended: false
|
||||
},
|
||||
observers: {
|
||||
activity(activity) {
|
||||
const item = activity || {}
|
||||
this.setData({
|
||||
poster: media.absUrl(item.poster || item.cover),
|
||||
video: media.absUrl(item.video),
|
||||
ended: item.status === 'past'
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onMap() {
|
||||
this.triggerEvent('map')
|
||||
},
|
||||
onSignup() {
|
||||
if (!this.data.activity || this.data.ended) return
|
||||
this.triggerEvent('signup')
|
||||
},
|
||||
onPast() {
|
||||
this.triggerEvent('past')
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<view class="board" wx:if="{{activity}}">
|
||||
<view class="banner">
|
||||
<video
|
||||
wx:if="{{video}}"
|
||||
class="banner-media"
|
||||
src="{{video}}"
|
||||
controls
|
||||
show-center-play-btn
|
||||
object-fit="cover"
|
||||
poster="{{poster}}"
|
||||
/>
|
||||
<block wx:else>
|
||||
<image wx:if="{{poster}}" class="banner-media" src="{{poster}}" mode="aspectFill" />
|
||||
<view class="banner-shade"></view>
|
||||
<text class="banner-title">{{activity.title}}</text>
|
||||
</block>
|
||||
<view class="badge" wx:if="{{activity.isCurrent}}">当前活动</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<text class="brand">{{activity.brandName}}</text>
|
||||
<text wx:if="{{activity.brandDesc}}" class="desc">{{activity.brandDesc}}</text>
|
||||
</view>
|
||||
|
||||
<view class="card info">
|
||||
<view class="row">
|
||||
<text class="label">活动名称</text>
|
||||
<text class="value">{{activity.title}}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">活动时间</text>
|
||||
<text class="value">{{activity.startAt || '待定'}}</text>
|
||||
</view>
|
||||
<view class="row last">
|
||||
<text class="label">会场地址</text>
|
||||
<text class="value">{{activity.address || '待定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn green" hover-class="btn-pressed" bindtap="onMap">📍 一键导航</view>
|
||||
<view
|
||||
class="btn teal {{ended ? 'disabled' : ''}}"
|
||||
hover-class="{{ended ? 'none' : 'btn-pressed'}}"
|
||||
bindtap="onSignup"
|
||||
>{{ended ? '活动已结束' : '立即报名'}}</view>
|
||||
|
||||
<view wx:if="{{showPast}}" class="more" bindtap="onPast">查看往期活动 ›</view>
|
||||
</view>
|
||||
@@ -0,0 +1,139 @@
|
||||
.board {
|
||||
padding: 28rpx 32rpx 48rpx;
|
||||
}
|
||||
|
||||
.banner {
|
||||
position: relative;
|
||||
height: 320rpx;
|
||||
border-radius: 28rpx;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #d5dbe3 0%, #8e98a6 100%);
|
||||
}
|
||||
|
||||
.banner-media,
|
||||
.banner-shade {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.banner-shade {
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, 0.28) 100%);
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
left: 24rpx;
|
||||
top: 24rpx;
|
||||
z-index: 2;
|
||||
height: 48rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 24rpx;
|
||||
background: rgba(55, 65, 81, 0.45);
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
position: absolute;
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
bottom: 32rpx;
|
||||
z-index: 1;
|
||||
color: #ffffff;
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-top: 24rpx;
|
||||
padding: 32rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: block;
|
||||
color: #128a80;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.desc {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
color: #6b7280;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 8rpx 32rpx;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24rpx;
|
||||
padding: 28rpx 0;
|
||||
border-bottom: 1rpx dashed #e5e7eb;
|
||||
}
|
||||
|
||||
.row.last {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
flex-shrink: 0;
|
||||
color: #9ca3af;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #111827;
|
||||
font-size: 28rpx;
|
||||
text-align: right;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 96rpx;
|
||||
margin-top: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-pressed {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.green {
|
||||
background: #07c160;
|
||||
}
|
||||
|
||||
.teal {
|
||||
background: #0d8b87;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
|
||||
.more {
|
||||
margin-top: 36rpx;
|
||||
padding: 8rpx 0;
|
||||
text-align: center;
|
||||
color: #9ca3af;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user