This commit is contained in:
2025-04-28 18:08:54 +08:00
parent 12c3afb4a6
commit abcf3549df
14 changed files with 702 additions and 85 deletions
@@ -88,10 +88,26 @@ public class ClientActivityController {
@At("/list-signup-user/?")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result listSignUpUser(Long activityId) {
List<ActUser> list = dao.query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong()));
SqlExpressionGroup group = new SqlExpressionGroup();
group.or(ActUser::getUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
group.or(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
List<ActUser> list = dao.query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(group));
return Result.success(list);
}
/**
* 是否报名
*/
@At("/is-sign-up/?")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result isSignUp(Long activityId) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or(ActUser::getUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
group.or(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong());
int count = dao.count(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(group));
return Result.success().addData(count > 0);
}
/**
* 查询我报名的活动
*/
@@ -0,0 +1,82 @@
package com.budwk.app.client.module.activity.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.result.Result;
import com.budwk.app.client.module.activity.service.ClientActivityService;
import com.budwk.app.module.activity.models.ActGroup;
import com.budwk.app.module.activity.models.ActUser;
import com.budwk.app.web.commons.auth.satoken.StpClientUtil;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.List;
/**
* @author zhf
* @date 2025/4/28 17:35
* @description 我的团队
*/
@IocBean
@At("/client/group")
@Ok("json:full")
public class ClientGroupController {
@Inject
private Dao dao;
@Inject
private ClientActivityService clientActivityService;
/**
* 查询我的活动
* @return
*/
@At("/list-act")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result listAct(){
Sql sql = Sqls.create("""
SELECT
t1.id,
t1.title,
t1.address,
t1.startTime,
t1.endTime,
t1.signStartTime,
t1.signEndTime
FROM
activity t1
LEFT JOIN act_user t2 ON t1.id = t2.activityId
WHERE
t2.userId = @userId
AND t2.isSignIn =1
""").setParam("userId", StpClientUtil.STP.getLoginIdAsLong());
List<NutMap> list = clientActivityService.listMap(sql);
return Result.success(list);
}
@At("/info/?")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result info(Long activityId){
ActGroup group = dao.fetch(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
return Result.success();
}
/**
* 查询团队成员
*/
@At("/list-team-user/?")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result listTeamUser(Long activityId){
List<ActUser> list = dao.query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(ActUser::getIsSignIn, "=", 1));
return Result.success(list);
}
}
@@ -0,0 +1,77 @@
package com.budwk.app.client.module.activity.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.page.PageForm;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.client.module.activity.service.ClientActivityService;
import com.budwk.app.web.commons.auth.satoken.StpClientUtil;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.adaptor.JsonAdaptor;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
/**
* @author zhf
* @date 2025/4/28 15:59
* @description 我的
*/
@IocBean
@At("/client/profile")
@Ok("json:full")
public class ClientProfileController {
@Inject
private ClientActivityService clientActivityService;
@Inject
private Dao dao;
@At("/list-act")
@AdaptBy(type = JsonAdaptor.class)
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result listAct(PageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
t1.id,
t1.title,
t1.address,
t1.startTime,
t1.endTime,
t1.signStartTime,
t1.signEndTime,
t1.cover,
t1.coHostUnit
FROM
activity t1
LEFT JOIN act_user t2 ON t1.id=t2.activityId
$condition
""");
Cnd cnd = Cnd.NEW();
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("t2.userId", "=", StpClientUtil.STP.getLoginIdAsLong());
group.or("t2.signUpUserId", "=", StpClientUtil.STP.getLoginIdAsLong());
cnd.groupBy("t1.id");
cnd.and(group);
sql.setCondition(cnd);
Pagination pagination = clientActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 取消报名
*/
@At("/cancel-sign-up/?")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result cancelSignUp(Long activityId) {
clientActivityService.cancelSignUp(activityId);
return Result.success();
}
}
@@ -25,4 +25,8 @@ public interface ClientActivityService extends BaseService<Activity> {
void signIn(Long activityId);
/**
* 取消报名
*/
void cancelSignUp(Long activityId);
}
@@ -14,6 +14,7 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import java.time.LocalDate;
@@ -49,7 +50,7 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
List<ActUser> userList = signVO.getUserList();
// 首先检查报名人员是否已经在用户表中
for (ActUser user : userList) {
Client_user clientUser = dao().fetch(Client_user.class, Cnd.where(Client_user::getIdCard, "=", user.getIdCard()));
Client_user clientUser = dao().fetch(Client_user.class, Cnd.where(Client_user::getIdCard, "=", user.getIdCard()).or(Client_user::getPhone, "=", user.getPhone()));
if (clientUser == null) {
// 创建用户
clientUser = new Client_user();
@@ -100,4 +101,13 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
actUser.setSignInTime(LocalDateTime.now());
dao().update(actUser, "isSignIn|signInTime");
}
@Override
public void cancelSignUp(Long activityId) {
ActUser actUser = dao().fetch(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong()));
if (Lang.isEmpty(actUser)) {
throw new BaseException("请联系报名人取消报名!");
}
dao().clear(ActUser.class, Cnd.where(ActUser::getActivityId, "=", activityId).and(ActUser::getSignUpUserId, "=", StpClientUtil.STP.getLoginIdAsLong()));
}
}
@@ -5,6 +5,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import java.math.BigDecimal;
/**
* @version 1.0
* @Author zzr
@@ -14,7 +16,7 @@ import org.nutz.dao.entity.annotation.*;
*/
@Data
@Comment("活动队伍")
//@Table("act_group")
@Table("act_group")
@EqualsAndHashCode(callSuper = true)
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class ActGroup extends BaseModel {
@@ -23,18 +25,24 @@ public class ActGroup extends BaseModel {
@Comment("ID")
private Long id;
@Column
@Comment("活动ID")
@ColDefine(customType = "bigint")
private Long activityId;
@Column
@Comment("队伍名称")
@ColDefine(type = ColType.VARCHAR, width = 255)
private String groupName;
@Column
@Comment("领队Id")
@ColDefine(customType = "bigint")
private Long groupLeaderId;
@Comment("队伍总得分")
@ColDefine(customType = "decimal", width = 10, precision = 2)
private BigDecimal totalScore;
@Column
@Comment("领队名称")
@ColDefine(type = ColType.VARCHAR, width = 128)
private Long groupLeaderName;
@Comment("队伍总排名")
@ColDefine(type = ColType.INT)
private Integer totalRank;
}
@@ -3,10 +3,7 @@ package com.budwk.app.module.activity.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.entity.annotation.*;
/**
* @version 1.0
@@ -17,7 +14,7 @@ import org.nutz.dao.entity.annotation.TableMeta;
*/
@Data
@Comment("活动队伍人员")
//@Table("act_group_user")
@Table("act_group_user")
@EqualsAndHashCode(callSuper = true)
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class ActGroupUser extends BaseModel {
@@ -25,4 +22,19 @@ public class ActGroupUser extends BaseModel {
@Id
@Comment("ID")
private Long id;
@Column
@Comment("队伍Id")
@ColDefine(customType = "bigint")
private Long groupId;
@Column
@Comment("用户id")
@ColDefine(customType = "bigint")
private Long userId;
@Column
@Comment("是否领队")
@ColDefine(type = ColType.BOOLEAN)
private Boolean isLeader;
}
@@ -0,0 +1,49 @@
package com.budwk.app.module.activity.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import java.math.BigDecimal;
/**
* @author zhf
* @date 2025/4/28 17:55
* @description
*/
@Data
@Comment("活动项目队伍得分表")
@Table("act_item_group_score")
@EqualsAndHashCode(callSuper = true)
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class ActItemGroupScore extends BaseModel {
@Id
@Comment("ID")
private Long id;
@Column
@Comment("活动ID")
@ColDefine(customType = "bigint")
private Long activityId;
@Column
@Comment("活动项目ID")
@ColDefine(customType = "bigint")
private Long itemId;
@Column
@Comment("队伍ID")
@ColDefine(customType = "bigint")
private Long groupId;
@Column
@Comment("队伍得分")
@ColDefine(customType = "decimal", width = 10, precision = 2)
private BigDecimal score;
@Column
@Comment("队伍排名")
private Integer rank;
}
@@ -5,6 +5,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import java.time.LocalDateTime;
@Data
@Table("act_item_user")
@Comment("项目报名人员")
+115 -59
View File
@@ -17,25 +17,30 @@
<view class="event-info-section">
<!-- 报名时间 -->
<view class="info-item">
<t-icon name="calendar" size="20px" />
<t-icon name="calendar" size="20px"/>
<view class="info-text-container">
<text class="info-text-title">报名时间</text>
<text class="info-text">{{ formatDate(detailData.signStartTime) }} 至 {{ formatDate(detailData.signEndTime) }}</text>
<text class="info-text">{{ formatDate(detailData.signStartTime) }} 至
{{ formatDate(detailData.signEndTime) }}
</text>
</view>
</view>
<!-- 活动时间 -->
<view class="info-item">
<t-icon name="alarm" size="20px" />
<t-icon name="alarm" size="20px"/>
<view class="info-text-container">
<text class="info-text-title">活动时间</text>
<text class="info-text">{{ formatDate(detailData.startTime) }} 至 {{ formatDate(detailData.endTime) }}</text>
<text class="info-text">{{ formatDate(detailData.startTime) }} 至 {{
formatDate(detailData.endTime)
}}
</text>
</view>
</view>
<!-- 活动地点 -->
<view class="info-item">
<t-icon name="location" size="20px" />
<t-icon name="location" size="20px"/>
<view class="info-text-container">
<text class="info-text-title">活动地点</text>
<text class="info-text">{{ detailData.address }}</text>
@@ -59,7 +64,8 @@
:navigation="{ type: 'fraction', paginationPosition: 'bottom-right' }"
height="500px"
>
<t-swiper-item v-for="(item, index) in detailData.items" :key="index" style="height: 192px" class="event-item">
<t-swiper-item v-for="(item, index) in detailData.items" :key="index" style="height: 192px"
class="event-item">
<view>
<view class="event-item-header">
<view class="event-item-index">{{ index + 1 }}</view>
@@ -80,7 +86,7 @@
<view class="event-item-info">
<view class="event-item-section">
<view class="event-item-section-title">
<t-icon name="flag" size="16px" color="var(--td-brand-color-5)" />
<t-icon name="flag" size="16px" color="var(--td-brand-color-5)"/>
<text>活动形式</text>
</view>
<view class="event-item-section-content">{{ item.form }}</view>
@@ -88,7 +94,7 @@
<view class="event-item-section">
<view class="event-item-section-title">
<t-icon name="chart-bar" size="16px" color="var(--td-brand-color-5)" />
<t-icon name="chart-bar" size="16px" color="var(--td-brand-color-5)"/>
<text>评分规则</text>
</view>
<view class="event-item-section-content">{{ item.rule }}</view>
@@ -99,57 +105,60 @@
</t-swiper-item>
</t-swiper>
<!-- <view class="event-items">-->
<!-- <view v-for="(item, index) in detailData.items" :key="item.id" class="event-item">-->
<!-- <view class="event-item-header">-->
<!-- <view class="event-item-index">{{ index + 1 }}</view>-->
<!-- <text class="event-item-name">{{ item.name }}</text>-->
<!-- </view>-->
<!-- -->
<!-- <view class="event-item-content">-->
<!-- &lt;!&ndash; 项目图片区域 &ndash;&gt;-->
<!-- &lt;!&ndash; v-if="item.img"&ndash;&gt;-->
<!-- &lt;!&ndash; :src="item.img"&ndash;&gt;-->
<!-- <image-->
<!-- src="https://api.zzzmh.cn/v2/bz/v3/getUrl/c9d3deb2880411ebb6edd017c2d2eca220"-->
<!-- mode="aspectFill" -->
<!-- class="event-item-image"-->
<!-- />-->
<!-- -->
<!-- &lt;!&ndash; 项目信息区域 &ndash;&gt;-->
<!-- <view class="event-item-info">-->
<!-- <view class="event-item-section">-->
<!-- <view class="event-item-section-title">-->
<!-- <t-icon name="flag" size="16px" color="var(&#45;&#45;td-brand-color-5)" />-->
<!-- <text>活动形式</text>-->
<!-- </view>-->
<!-- <view class="event-item-section-content">{{ item.form }}</view>-->
<!-- </view>-->
<!-- -->
<!-- <view class="event-item-section">-->
<!-- <view class="event-item-section-title">-->
<!-- <t-icon name="chart-bar" size="16px" color="var(&#45;&#45;td-brand-color-5)" />-->
<!-- <text>评分规则</text>-->
<!-- </view>-->
<!-- <view class="event-item-section-content">{{ item.rule }}</view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="event-items">-->
<!-- <view v-for="(item, index) in detailData.items" :key="item.id" class="event-item">-->
<!-- <view class="event-item-header">-->
<!-- <view class="event-item-index">{{ index + 1 }}</view>-->
<!-- <text class="event-item-name">{{ item.name }}</text>-->
<!-- </view>-->
<!-- -->
<!-- <view class="event-item-content">-->
<!-- &lt;!&ndash; 项目图片区域 &ndash;&gt;-->
<!-- &lt;!&ndash; v-if="item.img"&ndash;&gt;-->
<!-- &lt;!&ndash; :src="item.img"&ndash;&gt;-->
<!-- <image-->
<!-- src="https://api.zzzmh.cn/v2/bz/v3/getUrl/c9d3deb2880411ebb6edd017c2d2eca220"-->
<!-- mode="aspectFill" -->
<!-- class="event-item-image"-->
<!-- />-->
<!-- -->
<!-- &lt;!&ndash; 项目信息区域 &ndash;&gt;-->
<!-- <view class="event-item-info">-->
<!-- <view class="event-item-section">-->
<!-- <view class="event-item-section-title">-->
<!-- <t-icon name="flag" size="16px" color="var(&#45;&#45;td-brand-color-5)" />-->
<!-- <text>活动形式</text>-->
<!-- </view>-->
<!-- <view class="event-item-section-content">{{ item.form }}</view>-->
<!-- </view>-->
<!-- -->
<!-- <view class="event-item-section">-->
<!-- <view class="event-item-section-title">-->
<!-- <t-icon name="chart-bar" size="16px" color="var(&#45;&#45;td-brand-color-5)" />-->
<!-- <text>评分规则</text>-->
<!-- </view>-->
<!-- <view class="event-item-section-content">{{ item.rule }}</view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
</view>
</view>
<!-- 底部操作栏 -->
<view class="footer-bar">
<t-button theme="primary" block @click="navigateToSignUp">立即报名</t-button>
<!-- <t-button theme="primary" block @click="navigateToSignUp">立即报名</t-button>-->
<t-button theme="primary" block :disabled="bottomButton.disabled" @click="bottomButton.onClick">
{{ bottomButton.text }}
</t-button>
</view>
</template>
<!-- 加载中状态 -->
<template v-else>
<view class="loading-container">
<t-loading theme="dots" size="40rpx" />
<t-loading theme="dots" size="40rpx"/>
<text>加载中...</text>
</view>
</template>
@@ -157,13 +166,14 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { Toast } from 'tdesign-mobile-vue';
import { useRequest } from "@/utils/request";
import {ref, onMounted, computed} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
const request = useRequest();
const actId = ref(null);
const isSignUp = ref(null);
const detailData = ref(null);
// 格式化日期
@@ -177,6 +187,37 @@ const formatDate = (dateStr) => {
}
};
// 底部按钮文字
const bottomButton = computed(() => {
const {signStartTime, signEndTime} = detailData.value
if (signStartTime && signEndTime) {
const now = new Date();
const startTime = new Date(signStartTime);
const endTime = new Date(signEndTime);
if (now >= startTime && now <= endTime) {
return {
text: isSignUp.value ? '修改报名信息' : '立即报名',
disabled: false,
onClick: navigateToSignUp
}
} else if (now < startTime) {
return {
text: `报名开始时间:${formatDate(signStartTime)}`,
disabled: true,
onClick: () => {
}
}
} else {
return {
text: `报名结束时间:${formatDate(signEndTime)}`,
disabled: true,
onClick: () => {
}
}
}
}
})
// 跳转到报名页面
const navigateToSignUp = () => {
if (!actId.value) return;
@@ -194,27 +235,42 @@ const getActivityDetail = async (id) => {
duration: 0
});
const { code, data } = await request.get(`/client/activity/get/${id}`);
const {code, data} = await request.get(`/client/activity/get/${id}`);
if (code === 0 && data) {
detailData.value = data;
} else {
Toast({ message: '获取活动详情失败', theme: 'error' });
Toast({message: '获取活动详情失败', theme: 'error'});
}
loading.clear();
} catch (error) {
console.error('获取活动详情错误', error);
Toast({ message: '获取活动详情失败', theme: 'error' });
Toast({message: '获取活动详情失败', theme: 'error'});
}
};
// 判断是否报名
async function getIsSignUp() {
try {
const {code, data} = await request.get('/client/activity/is-sign-up/' + actId.value)
if (code === 0) {
isSignUp.value = data
} else {
Toast({message: '判断是否报名失败', theme: 'error'});
}
} catch (e) {
console.error(e)
}
}
// 页面加载
onLoad((option) => {
if (option.id) {
actId.value = option.id;
getActivityDetail(option.id);
getIsSignUp();
} else {
Toast({ message: '未找到活动ID', theme: 'error' });
Toast({message: '未找到活动ID', theme: 'error'});
}
});
</script>
@@ -461,14 +517,14 @@ onLoad((option) => {
}
}
::v-deep .t-swiper__container{
::v-deep .t-swiper__container {
//min-height: 300rpx!important;
height: unset;
}
::v-deep .t-swiper-item{
::v-deep .t-swiper-item {
//height: 100%!important;
height: unset!important;
height: unset !important;
}
</style>
+1 -4
View File
@@ -55,7 +55,7 @@
</view>
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<t-icon name="castle-7"></t-icon>
{{ item.address }}
{{ item.coHostUnit }}
</view>
</view>
</view>
@@ -63,9 +63,6 @@
<view>
100人已报名
</view>
<!-- <image src="https://assets.cumen.fun/upload/017cdaf1-c5fa-788e-eea2-d0ee249f902b/01892580-5db0-be0b-5643-35d6c5a83591"-->
<!-- style="width: 105px;height: 40px"-->
<!-- ></image>-->
<t-button theme="primary" shape="round" size="small">立即报名</t-button>
</view>
</view>
@@ -74,8 +74,8 @@
<t-form-item label="是否住宿" :name="`userList[${index}].isStay`"
:rules="[{required:true,message: '请选择是否住宿'}]">
<t-radio-group v-model="user.isStay" class="radio-box">
<t-radio :block="false" name="radio" value="1" label="是"/>
<t-radio :block="false" name="radio" value="0" label="否"/>
<t-radio :block="false" name="radio" :value="true" label="是"/>
<t-radio :block="false" name="radio" :value="false" label="否"/>
</t-radio-group>
</t-form-item>
</view>
@@ -101,7 +101,7 @@
shape="round"
@click="onSubmit"
>
提交报名
{{ isSignUp ? '重新提交报名信息' : '提交报名'}}
</t-button>
</view>
</view>
@@ -115,6 +115,7 @@ import {onLoad} from "@dcloudio/uni-app";
const request = useRequest()
const actId = ref(null)
const isSignUp = ref(null)
const agreementChecked = ref(false)
const formRef = ref(null)
const formData = ref({
@@ -127,7 +128,7 @@ const formData = ref({
position: "测试",
idCard: "421087200004040010",
phone: "18899998888",
isStay: "1"
isStay: true
}]
})
@@ -136,6 +137,7 @@ onLoad((option) => {
actId.value = option.id
formData.value.activityId = option.id
listSignUpUser()
getIsSignUp()
} else {
Toast({message: 'ID不存在', theme: 'error'});
}
@@ -158,6 +160,20 @@ async function listSignUpUser() {
}
}
// 判断是否报名
async function getIsSignUp() {
try {
const {code, data} = await request.get('/client/activity/is-sign-up/' + actId.value)
if (code === 0) {
isSignUp.value = data
} else {
Toast({message: '判断是否报名失败', theme: 'error'});
}
} catch (e) {
console.error(e)
}
}
// 表单验证
async function validaForm() {
return new Promise((resolve, reject) => {
+291
View File
@@ -0,0 +1,291 @@
<template>
<view class="page-container">
<view class="header">
<view class="search-container">
<t-search
v-model="pageForm.searchKeyword"
placeholder="搜索活动名称"
shape="round"
class="search-input"
@onSearch="handleSearch"
/>
</view>
</view>
<scroll-view
scroll-y
class="list-scroll-view"
@scrolltolower="handleScrollToBottom"
refresher-enabled
:refresher-triggered="refresherTriggered"
@refresherrefresh="handleRefresh"
refresher-background="#f2f3f5"
>
<view class="list-container">
<view
class="card-item"
v-for="item in dataList"
:key="item.id"
@click="navigateToDetail(item.id)"
>
<view>
<view style="display: flex;align-items: center;margin-bottom: 10px">
<image
src="https://assets.cumen.fun/upload/017cdaf1-c5fa-788e-eea2-d0ee249f902b/0187376a-3cfa-07b3-3cd6-7479bf463865?x-oss-process=style/720-jpg"
style="width: 30px;height: 30px;flex-shrink: 0"
></image>
<view
style="margin-left: 10px;font-weight: 600;white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
{{ item.title }}
</view>
</view>
<view style="display: flex;">
<image :src="item.cover" mode="aspectFill"
style="flex-shrink:0;width: 120px;height: 90px;border-radius: 5px;"></image>
<view
style="margin-left:10px;flex:1;overflow:hidden;display: flex;flex-direction: column;justify-content:space-around;color: #534d4d;">
<view
style="display: flex; align-items: center; column-gap: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<t-icon name="time"></t-icon>
{{ moment(item.startTime).format('MM-DD') }}
</view>
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<t-icon name="location"></t-icon>
{{ item.address }}
</view>
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<t-icon name="castle-7"></t-icon>
{{ item.coHostUnit }}
</view>
</view>
</view>
<view
style="border-top: 1px solid #f0f0f0;margin-top: 10px;padding-top: 12px;display: flex;align-items: center;justify-content: space-between">
<view>
</view>
<t-button theme="primary" shape="round" size="small" v-if="canCancelSignUp(item)"
@click.stop="cancelSignUp(item)">取消报名
</t-button>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading-container">
<t-loading v-if="loading" text="加载中..." layout="vertical" theme="circular" size="40rpx"/>
<t-divider v-else-if="!hasMore && dataList.length > 0" content="没有更多活动了"/>
</view>
<!-- 空状态 -->
<t-empty
v-if="!loading && dataList.length === 0"
description="暂无活动信息"
/>
</scroll-view>
</view>
</template>
<script setup>
import {ref, reactive, onMounted} from 'vue';
import {Dialog, Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import moment from "moment";
// 活动状态筛选选项
const statusOptions = [
{value: '', label: '全部'},
{value: 'notStartSign', label: '未开始报名'},
{value: 'signing', label: '报名中'},
{value: 'notStartActivity', label: '活动未开始'},
{value: 'activitying', label: '活动进行中'},
{value: 'ended', label: '已结束'}
];
// 页面表单数据
const pageForm = reactive({
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
status: '' // 全部
});
// 数据与状态
const dataList = ref([]);
const loading = ref(false);
const hasMore = ref(true);
const refresherTriggered = ref(false);
// 选择状态筛选
const selectStatusFilter = (status) => {
pageForm.status = status;
handleSearch();
};
// 获取列表数据
const getListData = async (isRefresh = false) => {
if (loading.value) return;
loading.value = true;
if (isRefresh) {
pageForm.pageNumber = 1;
}
// 延时1秒,模拟网络请求
await new Promise(resolve => setTimeout(resolve, 1000));
try {
const response = await useRequest().post('/client/profile/list-act', {
...pageForm
});
if (response.code === 0) {
const {list, totalCount} = response.data;
if (isRefresh) {
dataList.value = list;
} else {
dataList.value = [...dataList.value, ...list];
}
pageForm.totalCount = totalCount;
hasMore.value = dataList.value.length < totalCount;
pageForm.pageNumber++;
} else {
Toast({message: '获取活动列表失败', theme: 'error'});
}
} catch (error) {
console.error('获取活动列表失败', error);
Toast({message: '获取活动列表失败', theme: 'error'});
} finally {
loading.value = false;
// 重置下拉刷新状态
if (refresherTriggered.value) {
refresherTriggered.value = false;
}
}
};
//下拉刷新
const handleRefresh = () => {
if (loading.value) {
refresherTriggered.value = false;
return;
}
refresherTriggered.value = true;
pageForm.pageNumber = 1;
getListData(true);
};
// 搜索处理
const handleSearch = () => {
pageForm.pageNumber = 1;
hasMore.value = true;
dataList.value = [];
getListData(true);
};
// 触底加载更多
const handleScrollToBottom = () => {
if (loading.value || !hasMore.value) return;
getListData();
};
// 能否取消报名
const canCancelSignUp = (item) => {
const {signStartTime, signEndTime} = item;
return moment().isBetween(signStartTime, signEndTime);
};
// 取消报名
const cancelSignUp = async (item) => {
Dialog.confirm({
title: '提示',
content: '确定要取消报名吗?',
confirmBtn: '确定',
cancelBtn: '取消',
onConfirm: async () => {
try {
const {code, msg} = await useRequest().post('/client/profile/cancel-sign-up/' + item.id);
if (code === 0) {
Toast({message: '取消报名成功', theme: 'success'});
getListData(true);
} else {
Toast({message: msg, theme: 'error'});
}
} catch (error) {
Toast({message: '取消报名失败', theme: 'error'});
}
}
})
}
// 跳转详情
const navigateToDetail = (id) => {
uni.navigateTo({
url: `/pages/activity/detail/index?id=${id}`
});
};
// 初始化
onMounted(() => {
getListData();
});
</script>
<style lang="less" scoped>
.header {
background-color: #fff;
padding: 16rpx 0;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
z-index: 10;
}
.search-container {
padding: 0 24rpx;
}
.search-input {
width: 100%;
}
.list-scroll-view {
flex: 1;
height: 0;
}
.list-container {
display: flex;
flex-direction: column;
padding: 20rpx 24rpx;
}
.card-item {
width: 100%;
margin-bottom: 24rpx;
border-radius: 16rpx;
padding: 24rpx;
overflow: hidden;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
transition: transform 0.3s;
//background: url("https://assets.cumen.fun/upload/017cdaf1-c5fa-788e-eea2-d0ee249f902b/0189257e-fc75-5b89-b900-104c706f2d63") top / 100% auto no-repeat,
//linear-gradient(to right, white, #ecb5b5);
//background: no-repeat, linear-gradient(to right, white, #f6d4d4);
background: #ffffff;
&:active {
transform: scale(0.98);
}
}
.loading-container {
padding: 40rpx 0;
text-align: center;
}
</style>
+1 -4
View File
@@ -87,15 +87,12 @@ onMounted(() => {
<t-cell
title="已报名活动"
arrow
@click="navigateTo('/pages/activity/list/index?type=enrolled')"
@click="navigateTo('/pages/profile/act')"
hover
>
<template #leftIcon>
<t-icon name="calendar" size="24"/>
</template>
<template #rightIcon>
<view class="badge">{{ activities.enrolled }}</view>
</template>
</t-cell>
<t-cell