Initial commit on main branch

main
陈佳慧 2 months ago
commit aff4457858

@ -0,0 +1,31 @@
/*
* Eslint config file
* Documentation: https://eslint.org/docs/user-guide/configuring/
* Install the Eslint extension before using this feature.
*/
module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
ecmaFeatures: {
modules: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
globals: {
wx: true,
App: true,
Page: true,
getCurrentPages: true,
getApp: true,
Component: true,
requirePlugin: true,
requireMiniProgram: true,
},
// extends: 'eslint:recommended',
rules: {},
}

@ -0,0 +1,80 @@
// app.js
App({
onLaunch() {
// 监听页面不存在的情况
wx.onPageNotFound(function(res) {
console.error('Page not found:', res);
wx.redirectTo({
url: 'pages/participated/participated'
})
})
// 获取本地存储的用户信息
const userInfo = wx.getStorageSync('userInfo')
if (userInfo) {
this.globalData.userInfo = userInfo
this.globalData.isLoggedIn = true
}
// 获取本地存储的积分
const score = wx.getStorageSync('score')
if (score) {
this.globalData.score = score
}
},
onShow(options) {
// 小程序从后台进入前台时触发
console.log('小程序显示', options)
},
onHide() {
// 小程序从前台进入后台时触发
console.log('小程序隐藏')
},
globalData: {
userInfo: null,
isLoggedIn: false,
token: '',
score: 0,
activities: [],
participatedActivities: []
},
// 全局方法
setUserInfo(userInfo) {
this.globalData.userInfo = userInfo
this.globalData.isLoggedIn = true
wx.setStorageSync('userInfo', userInfo)
},
clearUserInfo() {
this.globalData.userInfo = null
this.globalData.isLoggedIn = false
this.globalData.token = ''
wx.removeStorageSync('userInfo')
},
updateScore(delta) {
this.globalData.score += delta
wx.setStorageSync('score', this.globalData.score)
},
addActivity(activity) {
this.globalData.activities.push(activity)
},
addParticipatedActivity(activity) {
this.globalData.participatedActivities.push(activity)
},
// 模拟随机点名
randomNameCall(activityId) {
// 这里应该根据 activityId 获取对应活动的参与者列表
// 为了演示,我们使用一个固定的名单
const participants = ['张三', '李四', '王五', '赵六', '钱七']
const randomIndex = Math.floor(Math.random() * participants.length)
return participants[randomIndex]
}
})

@ -0,0 +1,52 @@
{
"pages": [
"pages/login/login",
"pages/created/created",
"pages/participated/participated",
"pages/store/store",
"pages/createActivity/createActivity",
"pages/participatedDetail/participatedDetail",
"pages/test/test"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "点名小程序",
"navigationBarTextStyle": "black"
},
"tabBar": {
"color": "#999999",
"selectedColor": "#0d94ff",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/created/created",
"text": "我创建的",
"iconPath": "assets/icon-created.png",
"selectedIconPath": "assets/icon-created-active.png"
},
{
"pagePath": "pages/participated/participated",
"text": "我参与的",
"iconPath": "assets/icon-participated.png",
"selectedIconPath": "assets/icon-participated-active.png"
},
{
"pagePath": "pages/store/store",
"text": "商店",
"iconPath": "assets/icon-store.png",
"selectedIconPath": "assets/icon-store-active.png"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"networkTimeout": {
"request": 10000
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}

@ -0,0 +1,10 @@
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,12 @@
一个微信小程序
1.你是一个经验丰富的ui设计师风格简洁
2.你现在已经在一个微信小程序的项目中,因此不需要生成目录结构
3.做一个点名小程序
4.首先需要一个登录页面,用户输入账号和密码,登陆成功后进入“我创建的”页面
5.导航栏位于页面的底部,分别是我创建的,我参与的,商店
6.“我创建的”页面上方有一个搜索框下方有一个创建自定义按钮点击“创建自定义”按钮可以进入创建活动页面创建活动页面有活动名称活动描述和上传xlsx文件创建成功后返回“我创建的”页面“我创建的”页面会出现一个活动组件活动组件有活动名称和参与人数点击活动组件可以进入活动详情页
7.活动详情页有随机点名和回答问题两个按钮点击随机点名按钮后会随机抽取1个用户并显示在页面中点击回答问题按钮会随机抽取1个用户并显示在页面中按钮下方有加1分和减1分按钮点击加一分按钮后用户积分加一点击减一分按钮后用户积分减一
8.点击“我参与的”可以找到我参与的活动假设已经存在一个活动活动名称是2024软件工程K班活动人数为104人点击此活动可以进入活动参与详情页活动参与详情页有签到和“使用卡牌”两个按钮点击“签到”按钮即可签到“使用卡牌”按钮上方有一个显示框会显示需要回答问题的用户
9.点击商店按钮可以进入商店页面商店页面有4个卡牌和需要积分的数量点击购买按钮后积分减少商店页面下方有一个猜题赢积分按钮点击后会弹出一个弹窗弹窗中有一个问题用户输入答案后点击提交按钮如果答案正确则积分加一
10.背景颜色为#f4f3f3按钮颜色为#0d94ff按钮全部为圆角矩形
11.主标题字号20px,副标题字号为16px正文字号为14px按钮标题为14px

@ -0,0 +1,39 @@
Page({
data: {
activity: null,
selectedUser: null
},
onLoad(options) {
const activityId = options.id;
this.fetchActivityDetails(activityId);
},
fetchActivityDetails(id) {
// 这里应该从后端 API 获取活动详情
const mockActivity = { id: 1, name: '软件工程课程', participants: 50 };
this.setData({ activity: mockActivity });
},
randomNameCall() {
// 这里应该从后端 API 获取随机用户
const randomUser = '张三';
this.setData({ selectedUser: randomUser });
},
randomQuestion() {
// 这里应该从后端 API 获取随机用户
const randomUser = '李四';
this.setData({ selectedUser: randomUser });
},
addScore() {
// 这里应该调用后端 API 增加用户积分
wx.showToast({ title: '积分已增加', icon: 'success' });
},
minusScore() {
// 这里应该调用后端 API 减少用户积分
wx.showToast({ title: '积分已减少', icon: 'success' });
}
});

@ -0,0 +1,17 @@
<view class="container">
<view class="activity-info">
<text class="activity-name">活动名称</text>
<text class="activity-participants">参与人数: 50</text>
</view>
<view class="action-buttons">
<button class="btn-action" bindtap="randomNameCall">随机点名</button>
<button class="btn-action" bindtap="randomQuestion">回答问题</button>
</view>
<view class="selected-user" wx:if="{{selectedUser}}">
<text class="selected-user-name">{{selectedUser}}</text>
<view class="score-buttons">
<button class="btn-score" bindtap="addScore">+1分</button>
<button class="btn-score" bindtap="minusScore">-1分</button>
</view>
</view>
</view>

@ -0,0 +1,63 @@
.container {
padding: 20px;
background-color: #f4f3f3;
}
.activity-info {
background-color: white;
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
}
.activity-name {
font-size: 20px;
font-weight: bold;
}
.activity-participants {
font-size: 14px;
color: #666;
}
.action-buttons {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.btn-action {
width: 48%;
height: 40px;
background-color: #0d94ff;
color: white;
border-radius: 20px;
font-size: 14px;
}
.selected-user {
background-color: white;
padding: 15px;
border-radius: 10px;
text-align: center;
}
.selected-user-name {
font-size: 18px;
margin-bottom: 10px;
}
.score-buttons {
display: flex;
justify-content: center;
}
.btn-score {
width: 80px;
height: 30px;
background-color: #0d94ff;
color: white;
border-radius: 15px;
font-size: 14px;
margin: 0 10px;
}

@ -0,0 +1,51 @@
Page({
data: {
fileName: ''
},
onUpload() {
wx.chooseMessageFile({
count: 1,
type: 'file',
extension: ['xlsx'],
success: (res) => {
const file = res.tempFiles[0];
this.setData({
fileName: file.name
});
// 这里可以处理文件上传逻辑
console.log('选择的文件:', file);
}
});
},
onSubmit(e) {
const { activityName, activityDescription } = e.detail.value;
if (activityName && activityDescription) {
if (!this.data.fileName) {
wx.showToast({
title: '请上传 XLSX 文件',
icon: 'none',
duration: 2000
});
return;
}
// 这里应该添加创建活动的逻辑,比如调用后端 API
console.log('创建活动:', activityName, activityDescription, this.data.fileName);
wx.showToast({
title: '创建成功',
icon: 'success',
duration: 2000
});
setTimeout(() => {
wx.navigateBack();
}, 2000);
} else {
wx.showToast({
title: '请填写完整信息',
icon: 'none',
duration: 2000
});
}
}
});

@ -0,0 +1,27 @@
<view class="container">
<form bindsubmit="onSubmit">
<view class="form-group">
<text class="label">活动名称</text>
<view class="input-container">
<input name="activityName" placeholder="请输入活动名称" />
</view>
</view>
<view class="form-group">
<text class="label">活动描述</text>
<view class="input-container">
<textarea name="activityDescription" placeholder="请输入活动描述"></textarea>
</view>
</view>
<view class="form-group">
<text class="label">上传文件</text>
<button class="upload-btn" bindtap="onUpload">上传 XLSX 文件</button>
<text class="file-name" wx:if="{{fileName}}">{{fileName}}</text>
</view>
<view class="submit-container">
<button class="submit-btn" form-type="submit">创建活动</button>
</view>
</form>
</view>

@ -0,0 +1,70 @@
.container {
padding: 20px;
background-color: #f4f3f3;
min-height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.form-group {
margin-bottom: 20px;
}
.label {
display: block;
font-size: 14px;
color: #333;
margin-bottom: 5px;
}
.input-container {
background-color: #fff;
border-radius: 5px;
padding: 5px;
}
input, textarea {
width: 100%;
padding: 10px;
border: none;
font-size: 14px;
background-color: transparent;
}
textarea {
height: 100px;
}
.upload-btn {
background-color: #f0f0f0;
color: #333;
font-size: 12px;
padding: 8px 15px;
border-radius: 5px;
margin-bottom: 10px;
width: auto;
display: inline-block;
}
.file-name {
font-size: 12px;
color: #666;
margin-left: 10px;
}
.submit-container {
flex-grow: 1;
display: flex;
align-items: flex-end;
margin-top: 20px;
}
.submit-btn {
background-color: #0d94ff;
color: white;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
width: 100%;
}

@ -0,0 +1,27 @@
Page({
data: {
activities: [
{ id: 1, name: "活动1", participants: 10 },
{ id: 2, name: "活动2", participants: 20 },
{ id: 3, name: "活动3", participants: 15 }
]
},
onSearchInput(e) {
// 实现搜索功能
console.log("搜索:", e.detail.value);
},
onActivityTap(e) {
const activityId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/activityDetail/activityDetail?id=${activityId}`
});
},
onCreateActivity() {
wx.navigateTo({
url: '/pages/createActivity/createActivity'
});
}
});

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

@ -0,0 +1,18 @@
<view class="container">
<view class="search-bar">
<input type="text" placeholder="搜索活动" bindinput="onSearchInput" />
</view>
<view class="activity-list">
<block wx:for="{{activities}}" wx:key="id">
<button class="activity-item" bindtap="onActivityTap" data-id="{{item.id}}">
<view class="activity-info">
<text class="activity-name">{{item.name}}</text>
<text class="activity-participants">参与人数:{{item.participants}}</text>
</view>
</button>
</block>
</view>
<button class="create-btn" bindtap="onCreateActivity">创建自定义</button>
</view>

@ -0,0 +1,64 @@
.container {
padding: 20px;
background-color: #f4f3f3;
min-height: 100vh;
box-sizing: border-box;
}
.search-bar {
position: sticky;
top: 0;
z-index: 1000;
background-color: #f4f3f3;
padding: 10px 0;
}
.search-bar input {
width: 100%;
height: 40px;
padding: 0 15px;
border: 1px solid #ccc;
border-radius: 20px;
background-color: #fff;
}
.activity-list {
margin-top: 20px;
margin-bottom: 20px;
}
.activity-item {
width: 100%;
background-color: #fff;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-align: left;
}
.activity-info {
display: flex;
flex-direction: column;
}
.activity-name {
font-size: 16px;
font-weight: bold;
color: #333;
}
.activity-participants {
font-size: 14px;
color: #666;
margin-top: 5px;
}
.create-btn {
background-color: #0d94ff;
color: white;
border-radius: 5px;
padding: 10px 20px;
font-size: 16px;
width: 100%;
}

@ -0,0 +1,49 @@
// index.js
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
Page({
data: {
motto: 'Hello World',
userInfo: {
avatarUrl: defaultAvatarUrl,
nickName: '',
},
hasUserInfo: false,
canIUseGetUserProfile: wx.canIUse('getUserProfile'),
canIUseNicknameComp: wx.canIUse('input.type.nickname'),
},
bindViewTap() {
wx.navigateTo({
url: '../logs/logs'
})
},
onChooseAvatar(e) {
const { avatarUrl } = e.detail
const { nickName } = this.data.userInfo
this.setData({
"userInfo.avatarUrl": avatarUrl,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
onInputChange(e) {
const nickName = e.detail.value
const { avatarUrl } = this.data.userInfo
this.setData({
"userInfo.nickName": nickName,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息开发者每次通过该接口获取用户个人信息均需用户确认开发者妥善保管用户快速填写的头像昵称避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
})

@ -0,0 +1,4 @@
{
"usingComponents": {
}
}

@ -0,0 +1,27 @@
<!--index.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
<view class="container">
<view class="userinfo">
<block wx:if="{{canIUseNicknameComp && !hasUserInfo}}">
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{userInfo.avatarUrl}}"></image>
</button>
<view class="nickname-wrapper">
<text class="nickname-label">昵称</text>
<input type="nickname" class="nickname-input" placeholder="请输入昵称" bind:change="onInputChange" />
</view>
</block>
<block wx:elif="{{!hasUserInfo}}">
<button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 </button>
<view wx:else> 请使用2.10.4及以上版本基础库 </view>
</block>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
</view>
</scroll-view>

@ -0,0 +1,62 @@
/**index.wxss**/
page {
height: 100vh;
display: flex;
flex-direction: column;
}
.scrollarea {
flex: 1;
overflow-y: hidden;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
color: #aaa;
width: 80%;
}
.userinfo-avatar {
overflow: hidden;
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.usermotto {
margin-top: 200px;
}
.avatar-wrapper {
padding: 0;
width: 56px !important;
border-radius: 8px;
margin-top: 40px;
margin-bottom: 40px;
}
.avatar {
display: block;
width: 56px;
height: 56px;
}
.nickname-wrapper {
display: flex;
width: 100%;
padding: 16px;
box-sizing: border-box;
border-top: .5px solid rgba(0, 0, 0, 0.1);
border-bottom: .5px solid rgba(0, 0, 0, 0.1);
color: black;
}
.nickname-label {
width: 105px;
}
.nickname-input {
flex: 1;
}

@ -0,0 +1,41 @@
Page({
data: {
account: '',
password: ''
},
onAccountInput(e) {
this.setData({
account: e.detail.value
})
},
onPasswordInput(e) {
this.setData({
password: e.detail.value
})
},
onLogin() {
const { account, password } = this.data
if (account && password) {
// 这里应该添加实际的登录逻辑,比如调用后端 API
// 现在我们只是模拟登录成功
wx.showToast({
title: '登录成功',
icon: 'success',
duration: 2000
})
// 登录成功后跳转到"我创建的"页面
wx.switchTab({
url: '/pages/created/created'
})
} else {
wx.showToast({
title: '请输入账号和密码',
icon: 'none',
duration: 2000
})
}
}
})

@ -0,0 +1,7 @@
<view class="container">
<view class="login-form">
<input type="text" placeholder="请输入账号" bindinput="onAccountInput" />
<input type="password" placeholder="请输入密码" bindinput="onPasswordInput" />
<button bindtap="onLogin" style="background-color: #0d94ff; color: white; border-radius: 5px;">登录</button>
</view>
</view>

@ -0,0 +1,30 @@
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f4f3f3;
}
.login-form {
width: 80%;
max-width: 300px;
}
input {
width: 100%;
height: 40px;
margin-bottom: 20px;
padding: 0 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
}

@ -0,0 +1,18 @@
// logs.js
const util = require('../../utils/util.js')
Page({
data: {
logs: []
},
onLoad() {
this.setData({
logs: (wx.getStorageSync('logs') || []).map(log => {
return {
date: util.formatTime(new Date(log)),
timeStamp: log
}
})
})
}
})

@ -0,0 +1,4 @@
{
"usingComponents": {
}
}

@ -0,0 +1,6 @@
<!--logs.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
<block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log">
<view class="log-item">{{index + 1}}. {{log.date}}</view>
</block>
</scroll-view>

@ -0,0 +1,16 @@
page {
height: 100vh;
display: flex;
flex-direction: column;
}
.scrollarea {
flex: 1;
overflow-y: hidden;
}
.log-item {
margin-top: 20rpx;
text-align: center;
}
.log-item:last-child {
padding-bottom: env(safe-area-inset-bottom);
}

@ -0,0 +1,32 @@
Page({
data: {
activities: []
},
onLoad() {
this.fetchActivities();
},
fetchActivities() {
// 这里应该从后端 API 获取活动列表
const mockActivities = [
{ id: 1, name: '软件工程课程', participants: 50 },
{ id: 2, name: '数据结构讨论', participants: 30 }
];
this.setData({ activities: mockActivities });
},
onSearch(e) {
const keyword = e.detail.value;
// 实现搜索逻辑
},
goToCreateActivity() {
wx.navigateTo({ url: '/pages/createActivity/createActivity' });
},
goToActivityDetail(e) {
const activityId = e.currentTarget.dataset.id;
wx.navigateTo({ url: `/pages/activityDetail/activityDetail?id=${activityId}` });
}
});

@ -0,0 +1,12 @@
<view class="container">
<view class="search-bar">
<input type="text" placeholder="搜索活动" />
</view>
<button class="btn-create">创建自定义</button>
<view class="activity-list">
<view class="activity-item" bindtap="goToActivityDetail">
<text class="activity-name">活动名称</text>
<text class="activity-participants">参与人数: 50</text>
</view>
</view>
</view>

@ -0,0 +1,40 @@
.container {
padding: 20px;
background-color: #f4f3f3;
}
.search-bar {
width: 100%;
height: 40px;
background-color: white;
border-radius: 20px;
padding: 0 10px;
margin-bottom: 20px;
}
.btn-create {
width: 100%;
height: 40px;
background-color: #0d94ff;
color: white;
border-radius: 20px;
font-size: 14px;
margin-bottom: 20px;
}
.activity-item {
background-color: white;
padding: 15px;
border-radius: 10px;
margin-bottom: 10px;
}
.activity-name {
font-size: 16px;
font-weight: bold;
}
.activity-participants {
font-size: 14px;
color: #666;
}

@ -0,0 +1,22 @@
Page({
data: {
activities: []
},
onLoad() {
this.fetchParticipatedActivities();
},
fetchParticipatedActivities() {
// 这里应该从后端 API 获取参与的活动列表
const mockActivities = [
{ id: 1, name: '2024软件工程K班', participants: 104 }
];
this.setData({ activities: mockActivities });
},
goToParticipatedDetail(e) {
const activityId = e.currentTarget.dataset.id;
wx.navigateTo({ url: `/pages/participatedDetail/participatedDetail?id=${activityId}` });
}
});

@ -0,0 +1,8 @@
<view class="container">
<view class="activity-list">
<view class="activity-item" bindtap="goToParticipatedDetail">
<text class="activity-name">2024软件工程K班</text>
<text class="activity-participants">参与人数: 104</text>
</view>
</view>
</view>

@ -0,0 +1,21 @@
.container {
padding: 20px;
background-color: #f4f3f3;
}
.activity-item {
background-color: white;
padding: 15px;
border-radius: 10px;
margin-bottom: 10px;
}
.activity-name {
font-size: 16px;
font-weight: bold;
}
.activity-participants {
font-size: 14px;
color: #666;
}

@ -0,0 +1,52 @@
Page({
data: {
activities: [
{ id: 1, name: "2024软件工程K班", participants: 104 }
// 可以添加更多活动
],
filteredActivities: []
},
onLoad: function() {
this.setData({
filteredActivities: this.data.activities.slice(1) // 除了第一个活动外的所有活动
});
},
onSearchInput(e) {
const searchText = e.detail.value.toLowerCase();
const filtered = this.data.activities.filter(activity =>
activity.name.toLowerCase().includes(searchText)
);
this.setData({
filteredActivities: filtered
});
},
onActivitySummaryTap() {
wx.navigateTo({
url: '/pages/participatedDetail/participatedDetail?id=1',
fail: (err) => {
console.error('Navigation failed:', err);
wx.showToast({
title: '页面跳转失败',
icon: 'none'
});
}
});
},
onActivityTap(e) {
const activityId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/participatedDetail/participatedDetail?id=${activityId}`,
fail: (err) => {
console.error('Navigation failed:', err);
wx.showToast({
title: '页面跳转失败',
icon: 'none'
});
}
});
}
});

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

@ -0,0 +1,9 @@
<view class="container">
<view class="activity-list">
<view class="activity-item" bindtap="onActivityTap" data-id="1">
<text class="activity-name">2024软件工程K班</text>
<text class="activity-participants">参与人数104人</text>
</view>
<!-- 可以添加更多活动项 -->
</view>
</view>

@ -0,0 +1,92 @@
.container {
background-color: #f4f3f3;
min-height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.top-section {
padding: 20px;
}
.search-container {
margin-bottom: 15px;
}
.search-bar {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 20px;
padding: 5px 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.search-bar icon {
margin-right: 5px;
}
.search-bar input {
flex: 1;
border: none;
background: transparent;
height: 30px;
font-size: 14px;
}
.activity-summary {
background-color: #0d94ff;
color: white;
padding: 15px;
border-radius: 5px;
display: flex;
flex-direction: column;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.summary-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.summary-participants {
font-size: 14px;
}
.activity-list {
padding: 20px;
}
.activity-item {
background-color: #fff;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.activity-info {
display: flex;
flex-direction: column;
}
.activity-name {
font-size: 16px;
font-weight: bold;
color: #333;
}
.activity-participants {
font-size: 14px;
color: #666;
margin-top: 5px;
}
.no-activities {
text-align: center;
color: #999;
margin-top: 50px;
padding: 0 20px;
}

@ -0,0 +1,147 @@
const app = getApp()
Page({
data: {
activity: null,
luckyStudent: null,
isLoading: false,
studentId: null,
showAnswerModal: false
},
onLoad: function(options) {
const activityId = options.id;
// 这里应该根据 activityId 从服务器或本地存储加载活动详情
// 现在我们只是模拟一下
this.setData({
activity: { id: activityId, name: "2024软件工程K班", participants: 104 }
});
// 这里应该从全局状态或本地存储获取当前用户的 student_id
this.setData({
studentId: app.globalData.studentId || '12345' // 假设的学生ID
});
},
onSignIn: function() {
wx.request({
url: 'http://10.133.7.205:3000/update_attendance_score',
method: 'POST',
data: {
student_id: this.data.studentId
},
success: (res) => {
if (res.statusCode === 200) {
wx.showToast({
title: '签到成功',
icon: 'success'
});
} else {
wx.showToast({
title: '签到失败',
icon: 'none'
});
}
},
fail: (err) => {
console.error('API request failed:', err);
wx.showToast({
title: '网络错误',
icon: 'none'
});
}
});
},
onDrawLuckyStudent: function() {
if (this.data.isLoading) return; // 防止重复点击
this.setData({ isLoading: true });
this.drawLuckyStudentWithRetry(3); // 最多重试3次
},
drawLuckyStudentWithRetry: function(retryCount) {
wx.request({
url: 'http://10.133.7.205:3000/get_random_student',
method: 'GET',
timeout: 15000, // 增加超时时间到15秒
success: (res) => {
if (res.statusCode === 200) {
this.setData({
luckyStudent: res.data.name,
studentId: res.data.id,
isLoading: false,
showAnswerModal: true
});
wx.showToast({
title: '已抽取幸运儿',
icon: 'success'
});
} else {
this.handleDrawError('服务器返回错误', retryCount);
}
},
fail: (err) => {
console.error('API request failed:', err);
this.handleDrawError('网络请求失败', retryCount);
}
});
},
handleDrawError: function(errorMsg, retryCount) {
if (retryCount > 0) {
setTimeout(() => {
this.drawLuckyStudentWithRetry(retryCount - 1);
}, 1000); // 1秒后重试
} else {
this.setData({ isLoading: false });
wx.showModal({
title: '抽取失败',
content: `${errorMsg},请稍后再试。`,
showCancel: false
});
}
},
onAnswerCorrect: function() {
this.updateAnswerScore(true, 1);
},
onAnswerIncorrect: function() {
this.updateAnswerScore(false, 0);
},
updateAnswerScore: function(repeatedCorrectly, answerScore) {
wx.request({
url: 'http://10.133.7.205:3000/update_answer_score',
method: 'POST',
data: {
student_id: this.data.studentId,
repeated_correctly: repeatedCorrectly,
answer_score: answerScore
},
success: (res) => {
if (res.statusCode === 200) {
wx.showToast({
title: '回答已记录',
icon: 'success'
});
} else {
wx.showToast({
title: '记录失败',
icon: 'none'
});
}
this.setData({ showAnswerModal: false });
},
fail: (err) => {
console.error('API request failed:', err);
wx.showToast({
title: '网络错误',
icon: 'none'
});
this.setData({ showAnswerModal: false });
}
});
}
});

@ -0,0 +1,27 @@
<view class="container">
<view class="activity-info">
<text class="activity-name">{{activity.name}}</text>
<text class="activity-participants">参与人数:{{activity.participants}}人</text>
</view>
<view class="lucky-student" wx:if="{{luckyStudent}}">
<text>幸运儿:{{luckyStudent}}</text>
</view>
<view class="action-buttons">
<button class="action-btn" bindtap="onSignIn">签到</button>
<button class="action-btn" bindtap="onDrawLuckyStudent" disabled="{{isLoading}}">
{{isLoading ? '抽取中...' : '抽取幸运儿'}}
</button>
</view>
<view class="answer-modal" wx:if="{{showAnswerModal}}">
<view class="modal-content">
<text>{{luckyStudent}} 回答问题:</text>
<view class="answer-buttons">
<button class="answer-btn correct" bindtap="onAnswerCorrect">回答正确</button>
<button class="answer-btn incorrect" bindtap="onAnswerIncorrect">回答错误</button>
</view>
</view>
</view>
</view>

@ -0,0 +1,95 @@
.container {
padding: 20rpx;
background-color: #f4f3f3;
min-height: 100vh;
}
.activity-info {
background-color: #ffffff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
}
.activity-name {
font-size: 20px;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.activity-participants {
font-size: 16px;
color: #666;
}
.lucky-student {
background-color: #ffffff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
text-align: center;
font-size: 18px;
font-weight: bold;
color: #0d94ff;
}
.action-buttons {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
}
.action-btn {
background-color: #0d94ff;
color: #ffffff;
border-radius: 20rpx;
font-size: 14px;
padding: 10rpx 20rpx;
width: 45%;
}
.answer-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #ffffff;
border-radius: 10rpx;
padding: 20rpx;
width: 80%;
text-align: center;
}
.answer-buttons {
display: flex;
justify-content: space-around;
margin-top: 20rpx;
}
.answer-btn {
border-radius: 20rpx;
font-size: 14px;
padding: 10rpx 20rpx;
width: 45%;
}
.correct {
background-color: #4CAF50;
color: white;
}
.incorrect {
background-color: #F44336;
color: white;
}

@ -0,0 +1,60 @@
Page({
data: {
cards: [
{ id: 1, name: "卡牌1", price: 100, image: "/assets/card1.png" },
{ id: 2, name: "卡牌2", price: 200, image: "/assets/card2.png" },
{ id: 3, name: "卡牌3", price: 300, image: "/assets/card3.png" },
{ id: 4, name: "卡牌4", price: 400, image: "/assets/card4.png" }
],
userPoints: 1000 // 假设用户初始有1000积分
},
onBuy: function(e) {
const cardId = e.currentTarget.dataset.id;
const card = this.data.cards.find(c => c.id === cardId);
if (this.data.userPoints >= card.price) {
this.setData({
userPoints: this.data.userPoints - card.price
});
wx.showToast({
title: '购买成功',
icon: 'success'
});
} else {
wx.showToast({
title: '积分不足',
icon: 'none'
});
}
},
onGuess: function() {
wx.showModal({
title: '猜题',
content: '1 + 1 = ?',
editable: true,
placeholderText: '请输入答案',
confirmText: '提交',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
const answer = res.content;
if (answer === '2') {
this.setData({
userPoints: this.data.userPoints + 1
});
wx.showToast({
title: '答对了获得1积分',
icon: 'success'
});
} else {
wx.showToast({
title: '答错了,再接再厉',
icon: 'none'
});
}
}
}
});
}
});

@ -0,0 +1,24 @@
<view class="container">
<view class="user-points">
<text class="points-text">当前积分:{{userPoints}}</text>
</view>
<view class="card-list">
<view class="card-item" wx:for="{{cards}}" wx:key="id">
<text class="card-name">{{item.name}}</text>
<image class="card-image" src="{{item.image}}" mode="aspectFit"></image>
<view class="card-info">
<text class="card-price">{{item.price}} 积分</text>
<button class="buy-btn" bindtap="onBuy" data-id="{{item.id}}">
购买
</button>
</view>
</view>
</view>
<view class="bottom-section">
<button class="guess-btn" bindtap="onGuess">
猜题赢积分
</button>
</view>
</view>

@ -0,0 +1,71 @@
.container {
padding: 20rpx;
background-color: #f4f3f3;
min-height: 100vh;
box-sizing: border-box;
}
.user-points {
font-size: 16px;
margin-bottom: 20rpx;
}
.card-list {
display: flex;
flex-direction: column;
}
.card-item {
background-color: #ffffff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
align-items: center;
}
.card-name {
font-size: 20px;
font-weight: bold;
margin-bottom: 10rpx;
}
.card-image {
width: 300rpx;
height: 300rpx;
object-fit: cover;
margin: 20rpx 0;
}
.card-info {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.card-price {
font-size: 16px;
color: #666;
margin-bottom: 20rpx;
}
.buy-btn, .guess-btn {
background-color: #0d94ff;
color: #ffffff;
border-radius: 20rpx;
font-size: 14px;
padding: 10rpx 20rpx;
width: 80%;
}
.bottom-section {
margin-top: 20rpx;
width: 100%;
}
.guess-btn {
width: 100%;
}

@ -0,0 +1,6 @@
Page({
data: {} ,
onLoad: function() {
console.log('Test page loaded');
}
})

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

@ -0,0 +1 @@
<view>This is a test page</view>

@ -0,0 +1,3 @@
view {
padding: 20px;
}

@ -0,0 +1,29 @@
{
"compileType": "miniprogram",
"libVersion": "trial",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"urlCheck": false
},
"condition": {},
"editorSetting": {
"tabIndent": "auto",
"tabSize": 2
},
"appid": "wxcbc61201213ab639"
}

@ -0,0 +1,7 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "K%E7%82%B9%E5%90%8D",
"setting": {
"compileHotReLoad": true
}
}

@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

@ -0,0 +1,19 @@
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : `0${n}`
}
module.exports = {
formatTime
}
Loading…
Cancel
Save