活动列表动态界面

This commit is contained in:
minimaxagent1 2025-08-04 10:04:26 +08:00
parent 720a6d34f5
commit 0ef5aad9d4
6 changed files with 666 additions and 82 deletions

251
api/activity/README.md Normal file
View File

@ -0,0 +1,251 @@
# 活动API文档
## 概述
活动API提供了寺庙活动相关的所有接口包括活动列表、详情、报名等功能。
## 接口列表
### 1. 获取活动列表
**接口地址:** `GET /app/activitie/list`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| templeId | string | 否 | 寺庙ID |
| status | string | 否 | 活动状态 |
| pageNum | number | 否 | 页码默认1 |
| pageSize | number | 否 | 每页数量默认20 |
**响应示例:**
```json
{
"msg": "操作成功",
"code": 200,
"data": [
{
"id": "1",
"templeId": "12",
"title": "初一祈福",
"imgUrl": "https://api.ccttiot.com/image.jpg",
"content": "活动主题: 初一祈福,诸事圆满,心生欢喜",
"actStartTime": "2025-08-13 00:00:00",
"actEndTime": "2025-09-16 00:00:00",
"status": "1",
"address": "福建省宁德市福鼎市太姥山镇",
"phone": "13800138000",
"attendance": 100,
"readNum": 500,
"type": "1",
"currentBooking": 50
}
]
}
```
### 2. 获取活动详情
**接口地址:** `GET /app/activitie/detail/{id}`
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | string | 是 | 活动ID |
**响应示例:**
```json
{
"msg": "操作成功",
"code": 200,
"data": {
"id": "1",
"templeId": "12",
"title": "初一祈福",
"imgUrl": "https://api.ccttiot.com/image.jpg",
"content": "活动详细内容...",
"actStartTime": "2025-08-13 00:00:00",
"actEndTime": "2025-09-16 00:00:00",
"status": "1",
"address": "福建省宁德市福鼎市太姥山镇",
"phone": "13800138000",
"attendance": 100,
"readNum": 500,
"type": "1",
"currentBooking": 50
}
}
```
### 3. 报名活动
**接口地址:** `POST /app/activitie/register`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| activityId | string | 是 | 活动ID |
| userId | string | 是 | 用户ID |
| userName | string | 是 | 用户姓名 |
| phone | string | 是 | 联系电话 |
**响应示例:**
```json
{
"msg": "报名成功",
"code": 200,
"data": {
"registrationId": "123",
"activityId": "1",
"userId": "456",
"status": "success"
}
}
```
### 4. 取消报名
**接口地址:** `POST /app/activitie/cancel/{id}`
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | string | 是 | 报名记录ID |
**响应示例:**
```json
{
"msg": "取消成功",
"code": 200,
"data": null
}
```
### 5. 获取我的报名记录
**接口地址:** `GET /app/activitie/my-registrations`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | number | 否 | 页码默认1 |
| pageSize | number | 否 | 每页数量默认20 |
**响应示例:**
```json
{
"msg": "操作成功",
"code": 200,
"data": [
{
"id": "123",
"activityId": "1",
"activityTitle": "初一祈福",
"registrationTime": "2025-08-10 10:00:00",
"status": "confirmed"
}
]
}
```
## 数据字段说明
### 活动状态 (status)
| 值 | 说明 |
|----|------|
| "0" | 已结束 |
| "1" | 报名中 |
| "2" | 进行中 |
| "3" | 已结束 |
### 活动类型 (type)
| 值 | 说明 |
|----|------|
| "1" | 祈福活动 |
| "2" | 法会活动 |
| "3" | 禅修活动 |
| "4" | 捐赠活动 |
| "5" | 义工活动 |
## 使用示例
```javascript
import activityApi from '@/api/activity/activity.js'
// 获取活动列表
const getActivities = async () => {
try {
const response = await activityApi.getActivityList({
pageNum: 1,
pageSize: 20
})
if (response.code === 200) {
console.log('活动列表:', response.data)
}
} catch (error) {
console.error('获取活动列表失败:', error)
}
}
// 报名活动
const registerActivity = async (activityId, userInfo) => {
try {
const response = await activityApi.registerActivity({
activityId,
userId: userInfo.id,
userName: userInfo.userName,
phone: userInfo.phone
})
if (response.code === 200) {
console.log('报名成功')
}
} catch (error) {
console.error('报名失败:', error)
}
}
```
## 错误处理
所有接口都会返回统一的错误格式:
```json
{
"msg": "错误信息",
"code": 错误码,
"data": null
}
```
常见错误码:
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权,需要登录 |
| 403 | 权限不足 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
## 注意事项
1. 所有接口都需要在请求头中携带有效的token除了登录相关接口
2. 活动列表支持分页查询建议每页不超过50条
3. 报名活动前需要确保用户已登录
4. 活动状态会根据时间自动更新
5. 图片URL为完整路径可直接使用

79
api/activity/activity.js Normal file
View File

@ -0,0 +1,79 @@
import request from '../../utils/request'
// 活动相关API
export default {
/**
* 获取活动列表
* @param {Object} params - 查询参数
* @param {string} params.templeId - 寺庙ID
* @param {string} params.status - 活动状态
* @param {number} params.pageNum - 页码
* @param {number} params.pageSize - 每页数量
* @returns {Promise} 活动列表数据
*/
getActivityList(params = {}) {
return request({
url: '/app/activitie/list',
method: 'GET',
params: {
pageNum: 1,
pageSize: 20,
...params
}
})
},
/**
* 获取活动详情
* @param {string} id - 活动ID
* @returns {Promise} 活动详情数据
*/
getActivityDetail(id) {
return request({
url: `/app/activitie/detail/${id}`,
method: 'GET'
})
},
/**
* 报名活动
* @param {Object} data - 报名数据
* @param {string} data.activityId - 活动ID
* @param {string} data.userId - 用户ID
* @param {string} data.userName - 用户姓名
* @param {string} data.phone - 联系电话
* @returns {Promise} 报名结果
*/
registerActivity(data) {
return request({
url: '/app/activitie/register',
method: 'POST',
data
})
},
/**
* 取消报名
* @param {string} id - 报名记录ID
* @returns {Promise} 取消结果
*/
cancelRegistration(id) {
return request({
url: `/app/activitie/cancel/${id}`,
method: 'POST'
})
},
/**
* 获取我的活动报名记录
* @param {Object} params - 查询参数
* @returns {Promise} 报名记录列表
*/
getMyRegistrations(params = {}) {
return request({
url: '/app/activitie/my-registrations',
method: 'GET',
params
})
}
}

View File

@ -25,8 +25,8 @@
<!-- 活动状态 -->
<view class="activity-status">
<text class="status-text" :class="getStatusClass(activity.status)">
{{ getStatusText(activity.status) }}
<text class="status-text" :class="statusClass">
{{ statusText }}
</text>
</view>
@ -89,17 +89,18 @@ export default {
ACTIVITY_STATUS
}
},
computed: {
//
statusText() {
return ACTIVITY_STATUS_TEXT[this.activity.status] || '未知状态'
},
//
statusClass() {
return ACTIVITY_STATUS_CLASS[this.activity.status] || ''
}
},
methods: {
//
getStatusText(status) {
return ACTIVITY_STATUS_TEXT[status] || '未知状态'
},
//
getStatusClass(status) {
return ACTIVITY_STATUS_CLASS[status] || ''
},
//
getDefaultImage() {
return DEFAULT_ACTIVITY_IMAGES[this.activity.type] || '/static/image/a1.png'

View File

@ -9,8 +9,23 @@
loading-text="加载中..."
/>
<!-- 错误状态 -->
<status-display
v-if="error"
type="error"
:error-text="error"
@retry="loadPageData"
/>
<!-- 空状态 -->
<status-display
v-if="!loading && !error && activityList.length === 0"
type="empty"
empty-text="暂无活动"
/>
<!-- 活动卡片列表 -->
<view class="activity-list">
<view class="activity-list" v-if="!loading && !error && activityList.length > 0">
<activity-card
v-for="activity in activityList"
:key="activity.id"
@ -29,6 +44,8 @@ import CommonEnum from "../../enum/common";
import { ACTIVITY_STATUS, ACTIVITY_TYPE } from "../../enum/activity";
import StatusDisplay from "../../components/status-display/status-display.vue";
import ActivityCard from "../../components/activity-card/activity-card.vue";
import activityApi from "../../api/activity/activity.js";
import activityFormatter from "../../utils/activity-data-formatter.js";
export default {
components: {
@ -39,78 +56,90 @@ export default {
return {
CommonEnum,
loading: false,
activityList: [
{
id: '1',
name: '初一祈福',
theme: '活动主题: 初一祈福,诸事圆满,心生欢喜',
type: ACTIVITY_TYPE.PRAYER,
status: ACTIVITY_STATUS.REGISTERING,
time: '2025年10月03日 周四 10:00~11:00',
location: '福建省泉州市惠安县西北街北门小坪顶',
backgroundImage: '/static/image/a1.png'
},
{
id: '2',
name: '腊八福粥 随缘随喜',
theme: '活动主题: 腊八福粥,随缘随喜',
type: ACTIVITY_TYPE.CEREMONY,
status: ACTIVITY_STATUS.REGISTERING,
time: '2025年10月03日 周四 10:00~11:00',
location: '福建省泉州市惠安县西北街北门小坪顶',
backgroundImage: '/static/image/a2.png'
},
{
id: '3',
name: '佛诞节法会',
theme: '活动主题: 庆祝佛陀诞辰,弘扬佛法',
type: ACTIVITY_TYPE.CEREMONY,
status: ACTIVITY_STATUS.ONGOING,
time: '2025年10月15日 周三 09:00~12:00',
location: '福建省泉州市惠安县西北街北门小坪顶',
backgroundImage: '/static/image/a3.png'
},
{
id: '4',
name: '禅修体验',
theme: '活动主题: 静心禅修,寻找内心平静',
type: ACTIVITY_TYPE.MEDITATION,
status: ACTIVITY_STATUS.FINISHED,
time: '2025年09月20日 周六 14:00~16:00',
location: '福建省泉州市惠安县西北街北门小坪顶',
backgroundImage: '/static/image/a4.png'
},
{
id: '5',
name: '慈善捐赠',
theme: '活动主题: 慈悲为怀,广种福田',
type: ACTIVITY_TYPE.DONATION,
status: ACTIVITY_STATUS.REGISTERING,
time: '2025年10月25日 周六 09:00~17:00',
location: '福建省泉州市惠安县西北街北门小坪顶',
backgroundImage: '/static/image/a5.png'
}
]
error: '',
activityList: [],
//
pageNum: 1,
pageSize: 20,
hasMore: true
}
},
onLoad() {
//
this.loadPageData()
},
//
onPullDownRefresh() {
this.refreshData()
},
//
onReachBottom() {
if (this.hasMore && !this.loading) {
this.loadMoreData()
}
},
methods: {
//
async loadPageData() {
this.loading = true
this.error = ''
try {
// TODO: API
// const response = await getPageData()
// this.activityList = response.data
//
setTimeout(() => {
this.loading = false
}, 1000)
const response = await activityApi.getActivityList({
pageNum: this.pageNum,
pageSize: this.pageSize
})
if (response.code === 200 && response.data) {
//
const formattedData = activityFormatter.formatActivityList(response.data)
this.activityList = formattedData
//
this.hasMore = response.data.length >= this.pageSize
} else {
this.error = response.msg || '获取活动列表失败'
}
} catch (error) {
console.error('获取页面数据失败:', error)
console.error('获取活动列表失败:', error)
this.error = '网络错误,请稍后重试'
} finally {
this.loading = false
uni.stopPullDownRefresh()
}
},
//
async refreshData() {
this.pageNum = 1
this.hasMore = true
await this.loadPageData()
},
//
async loadMoreData() {
if (!this.hasMore || this.loading) return
this.loading = true
try {
const nextPage = this.pageNum + 1
const response = await activityApi.getActivityList({
pageNum: nextPage,
pageSize: this.pageSize
})
if (response.code === 200 && response.data) {
const formattedData = activityFormatter.formatActivityList(response.data)
this.activityList = [...this.activityList, ...formattedData]
this.pageNum = nextPage
this.hasMore = response.data.length >= this.pageSize
} else {
this.hasMore = false
}
} catch (error) {
console.error('加载更多活动失败:', error)
this.hasMore = false
} finally {
this.loading = false
}
},
@ -126,17 +155,63 @@ export default {
},
//
handleRegister(activity) {
async handleRegister(activity) {
console.log('报名活动:', activity)
//
const userInfo = uni.getStorageSync('userInfo')
if (!userInfo) {
uni.showModal({
title: '提示',
content: '请先登录后再报名活动',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
//
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
return
}
//
uni.showModal({
title: '确认报名',
content: `确定要报名参加"${activity.name}"活动吗?`,
success: (res) => {
success: async (res) => {
if (res.confirm) {
try {
const response = await activityApi.registerActivity({
activityId: activity.id,
userId: userInfo.id,
userName: userInfo.userName || userInfo.nickName,
phone: userInfo.phone
})
if (response.code === 200) {
uni.showToast({
title: '报名成功',
icon: 'success'
})
//
this.refreshData()
} else {
uni.showToast({
title: response.msg || '报名失败',
icon: 'none'
})
}
} catch (error) {
console.error('报名失败:', error)
uni.showToast({
title: '报名失败,请稍后重试',
icon: 'none'
})
}
}
}
})
@ -147,12 +222,13 @@ export default {
<style lang="scss">
page {
background: #F5F0E7;
background: #FAF8F3;
align-items: center;
justify-content: center;
flex-direction: column;
}
.header {
width: 100%;
min-height: 100vh;

View File

@ -0,0 +1,174 @@
import { ACTIVITY_STATUS, ACTIVITY_TYPE } from '../enum/activity'
/**
* 活动数据格式化工具
*/
export default {
/**
* 格式化活动列表数据
* @param {Array} apiData - API返回的活动数据
* @returns {Array} 格式化后的活动数据
*/
formatActivityList(apiData) {
if (!Array.isArray(apiData)) {
return []
}
return apiData.map(item => this.formatActivityItem(item))
},
/**
* 格式化单个活动数据
* @param {Object} item - API返回的单个活动数据
* @returns {Object} 格式化后的活动数据
*/
formatActivityItem(item) {
if (!item) return null
return {
id: item.id || '',
name: item.title || '未命名活动',
theme: item.content ? `活动主题: ${item.content}` : '',
type: this.getActivityType(item.type),
status: this.getActivityStatus(item.status, item.actStartTime, item.actEndTime),
time: this.formatActivityTime(item.actStartTime, item.actEndTime),
location: item.address || '',
backgroundImage: item.imgUrl || this.getDefaultImageByType(item.type),
// 额外信息
templeId: item.templeId,
currentBooking: item.currentBooking || 0,
attendance: item.attendance || 0,
readNum: item.readNum || 0,
phone: item.phone,
lon: item.lon,
lat: item.lat
}
},
/**
* 获取活动类型
* @param {string} type - API返回的类型
* @returns {string} 标准化的活动类型
*/
getActivityType(type) {
if (!type) return ACTIVITY_TYPE.PRAYER
const typeMap = {
'1': ACTIVITY_TYPE.PRAYER, // 祈福活动
'2': ACTIVITY_TYPE.CEREMONY, // 法会活动
'3': ACTIVITY_TYPE.MEDITATION, // 禅修活动
'4': ACTIVITY_TYPE.DONATION, // 捐赠活动
'5': ACTIVITY_TYPE.VOLUNTEER // 义工活动
}
return typeMap[type] || ACTIVITY_TYPE.PRAYER
},
/**
* 获取活动状态
* @param {string} status - API返回的状态
* @param {string} startTime - 开始时间
* @param {string} endTime - 结束时间
* @returns {string} 标准化的活动状态
*/
getActivityStatus(status, startTime, endTime) {
if (!status || status === '0') {
return ACTIVITY_STATUS.FINISHED
}
// 如果有时间信息,根据时间判断状态
if (startTime && endTime) {
const now = new Date()
const start = new Date(startTime)
const end = new Date(endTime)
if (now < start) {
return ACTIVITY_STATUS.REGISTERING
} else if (now >= start && now <= end) {
return ACTIVITY_STATUS.ONGOING
} else {
return ACTIVITY_STATUS.FINISHED
}
}
// 默认根据status判断
const statusMap = {
'1': ACTIVITY_STATUS.REGISTERING,
'2': ACTIVITY_STATUS.ONGOING,
'3': ACTIVITY_STATUS.FINISHED
}
return statusMap[status] || ACTIVITY_STATUS.REGISTERING
},
/**
* 格式化活动时间
* @param {string} startTime - 开始时间
* @param {string} endTime - 结束时间
* @returns {string} 格式化后的时间字符串
*/
formatActivityTime(startTime, endTime) {
if (!startTime) return ''
try {
const start = new Date(startTime)
const end = endTime ? new Date(endTime) : null
// 格式化日期
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const weekday = weekdays[date.getDay()]
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}${month}${day}${weekday} ${hours}:${minutes}`
}
let timeStr = formatDate(start)
// 如果有结束时间,添加结束时间
if (end && end.getTime() !== start.getTime()) {
const endHours = String(end.getHours()).padStart(2, '0')
const endMinutes = String(end.getMinutes()).padStart(2, '0')
timeStr += `~${endHours}:${endMinutes}`
}
return timeStr
} catch (error) {
console.error('格式化时间失败:', error)
return startTime
}
},
/**
* 根据类型获取默认图片
* @param {string} type - 活动类型
* @returns {string} 默认图片路径
*/
getDefaultImageByType(type) {
const typeMap = {
'1': '/static/image/a1.png', // 祈福活动
'2': '/static/image/a2.png', // 法会活动
'3': '/static/image/a3.png', // 禅修活动
'4': '/static/image/a4.png', // 捐赠活动
'5': '/static/image/a5.png' // 义工活动
}
return typeMap[type] || '/static/image/a1.png'
},
/**
* 验证活动数据是否有效
* @param {Object} activity - 活动数据
* @returns {boolean} 是否有效
*/
isValidActivity(activity) {
return activity &&
activity.id &&
activity.title &&
activity.status !== '0'
}
}

View File

@ -17,7 +17,7 @@ import {
// 环境配置
const ENV_CONFIG = {
develop: { // 开发环境
baseUrl: 'http://192.168.2.7:4501',
baseUrl: 'http://192.168.2.112:4501',
},
trial: { // 体验版
baseUrl: 'https://testlu.chuangtewl.com/prod-api',
@ -378,3 +378,6 @@ export {
}
// Loading管理相关函数已从loading-manager.js导入
// 默认导出request函数方便API文件导入
export default request