..
This commit is contained in:
+15
@@ -11,8 +11,10 @@ import com.budwk.app.module.activity.models.Activity;
|
||||
import com.budwk.app.web.commons.auth.satoken.StpClientUtil;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.FieldFilter;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.Daos;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -165,5 +167,18 @@ public class ClientActivityController {
|
||||
return Result.success(actUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户协议
|
||||
*/
|
||||
@At("/agreement/?")
|
||||
public Result agreement(Long activityId) {
|
||||
Dao extDao = Daos.ext(dao, FieldFilter.create(Activity.class, "protocol"));
|
||||
Activity activity = extDao.fetch(Activity.class, activityId);
|
||||
if (activity == null) {
|
||||
return Result.success();
|
||||
}
|
||||
return Result.success().addData(activity.getProtocol());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+74
-8
@@ -7,9 +7,11 @@ import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.module.activity.models.ActGroup;
|
||||
import com.budwk.app.module.activity.models.ActGroupUser;
|
||||
import com.budwk.app.module.activity.models.ActItemGroupScore;
|
||||
import com.budwk.app.module.activity.models.ActivityItem;
|
||||
import com.budwk.app.module.activity.service.ActGroupService;
|
||||
import com.budwk.app.module.activity.service.ActUserService;
|
||||
import com.budwk.app.module.activity.vo.ActGroupVo;
|
||||
import com.budwk.app.module.activity.vo.ActManualGroupVo;
|
||||
import com.budwk.app.module.activity.vo.ActUnGroupUserVo;
|
||||
import com.budwk.app.web.commons.slog.annotation.SLog;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -29,6 +31,7 @@ import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* @version 1.0
|
||||
@@ -59,7 +62,7 @@ public class ActGroupController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
public Result pageData(PageForm pageForm, String activityId, String groupName){
|
||||
public Result pageData(PageForm pageForm, String activityId, String groupName) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
ag.*,
|
||||
@@ -95,12 +98,13 @@ public class ActGroupController {
|
||||
|
||||
/**
|
||||
* 查询签到人员
|
||||
*
|
||||
* @param activityId
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
public Result querySingUpData(String activityId){
|
||||
public Result querySingUpData(String activityId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
act.id AS activityId,
|
||||
@@ -136,7 +140,7 @@ public class ActGroupController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
public Result queryGroupList(String activityId){
|
||||
public Result queryGroupList(String activityId) {
|
||||
List<ActGroup> query = dao.query(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
|
||||
return Result.success().addData(query);
|
||||
}
|
||||
@@ -144,7 +148,7 @@ public class ActGroupController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
public Result queryGroupUser(String activityId, String groupId){
|
||||
public Result queryGroupUser(String activityId, String groupId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.*,
|
||||
@@ -167,7 +171,7 @@ public class ActGroupController {
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
@SLog(tag = "活动分组", msg = "随机分组")
|
||||
public Result groupSetting(ActGroupVo actGroupVo){
|
||||
public Result groupSetting(ActGroupVo actGroupVo) {
|
||||
// 查询所有签到的用户,按照分组参数进行分组
|
||||
actGroupService.groupSetting(actGroupVo);
|
||||
return Result.success();
|
||||
@@ -176,8 +180,8 @@ public class ActGroupController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
public Result queryUnGroupUser(String activityId){
|
||||
Sql sql = Sqls.create("""
|
||||
public Result queryUnGroupUser(String activityId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.*
|
||||
FROM
|
||||
@@ -193,8 +197,70 @@ public class ActGroupController {
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
@SLog(tag = "活动分组", msg = "设置未分组人员")
|
||||
public Result unGroupUserSetting(ActUnGroupUserVo actUnGroupUserVo){
|
||||
public Result unGroupUserSetting(ActUnGroupUserVo actUnGroupUserVo) {
|
||||
actGroupService.unGroupUserSetting(actUnGroupUserVo);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
@SLog(tag = "活动分组", msg = "查询未分组人员")
|
||||
public Result unGroupUsers(Long activityId) {
|
||||
List<NutMap> list = actGroupService.unGroupUsers(activityId);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
@SLog(tag = "活动分组", msg = "查询分组信息及组员")
|
||||
public Result listGroup(Long activityId) {
|
||||
List<NutMap> list = actGroupService.listGroupAndUsers(activityId);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.group")
|
||||
@SLog(tag = "活动分组", msg = "手动设置分组")
|
||||
public Result manualSetGroup(@Param("actGroups") ActGroup[] actGroups, @Param("activityId") Long activityId) {
|
||||
dao.clear(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
|
||||
dao.clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", activityId));
|
||||
|
||||
for (int i = 0; i < actGroups.length; i++) {
|
||||
actGroups[i].setActivityId(activityId);
|
||||
actGroups[i].setSort(i + 1);
|
||||
dao.insert(actGroups[i]);
|
||||
for (ActGroupUser user : actGroups[i].getGroupUsers()) {
|
||||
user.setActivityId(activityId);
|
||||
user.setGroupId(actGroups[i].getId());
|
||||
if (user.getUserId() == null) {
|
||||
return Result.error("用户ID不能为空");
|
||||
}
|
||||
}
|
||||
dao.insert(actGroups[i].getGroupUsers());
|
||||
}
|
||||
|
||||
|
||||
// 查询项目列表
|
||||
List<ActivityItem> items = dao.query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", activityId));
|
||||
|
||||
// 清空分组积分
|
||||
dao.clear(ActItemGroupScore.class, Cnd.where(ActItemGroupScore::getActivityId, "=", activityId));
|
||||
|
||||
// 往项目得分表里面插记录
|
||||
List<ActItemGroupScore> itemGroupScoreList = new ArrayList<>();
|
||||
for (ActGroup group : actGroups) {
|
||||
// 构建小组项目得分表
|
||||
List<ActItemGroupScore> groupScoreList = items.stream().map(item -> {
|
||||
ActItemGroupScore itemGroupScore = new ActItemGroupScore();
|
||||
itemGroupScore.setActivityId(activityId);
|
||||
itemGroupScore.setItemId(item.getId());
|
||||
itemGroupScore.setGroupId(group.getId());
|
||||
return itemGroupScore;
|
||||
}).toList();
|
||||
itemGroupScoreList.addAll(groupScoreList);
|
||||
}
|
||||
dao.insert(itemGroupScoreList);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.budwk.app.module.activity.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.module.activity.models.ActRankScore;
|
||||
import com.budwk.app.module.activity.service.ActRankScoreService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/activity/rankScore")
|
||||
@Slf4j
|
||||
@Ok("json:full")
|
||||
@Api(tags = "活动名次积分对应")
|
||||
public class ActRankScoreController {
|
||||
|
||||
@Inject
|
||||
private ActRankScoreService actRankScoreService;
|
||||
|
||||
@At("")
|
||||
@SaCheckPermission("activity.rankScore")
|
||||
@Ok("beetl:/platform/activity/rankScore/index.html")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.rankScore")
|
||||
@ApiOperation(value = "分页查询", nickname = "/pageData", httpMethod = "GET")
|
||||
public Result pageData(PageForm pageForm) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.desc(ActRankScore::getScore);
|
||||
Pagination pagination = actRankScoreService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.rankScore")
|
||||
@ApiOperation(value = "保存", nickname = "/save", httpMethod = "POST")
|
||||
public Result save(@Param("actRankScore") ActRankScore[] actRankScore) {
|
||||
// 名次不能重复
|
||||
long count = Arrays.stream(actRankScore).map(ActRankScore::getRanking).distinct().count();
|
||||
if (count != actRankScore.length) {
|
||||
return Result.error("名次不能重复");
|
||||
}
|
||||
|
||||
List<String> rankingList = Arrays.stream(actRankScore).map(ActRankScore::getRanking).toList();
|
||||
if (actRankScoreService.count(Cnd.where(ActRankScore::getRanking, "in", rankingList)) > 0) {
|
||||
return Result.error("名次已存在");
|
||||
}
|
||||
|
||||
actRankScoreService.insertOrUpdate(actRankScore);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.rankScore")
|
||||
@ApiOperation(value = "更新", nickname = "/update", httpMethod = "POST")
|
||||
public Result update(ActRankScore actRankScore){
|
||||
// 名次不能重复
|
||||
long count = actRankScoreService.count(Cnd.where(ActRankScore::getRanking, "=", actRankScore.getRanking())
|
||||
.and(ActRankScore::getId, "!=", actRankScore.getId()));
|
||||
if (count > 0) {
|
||||
return Result.error("名次已存在");
|
||||
}
|
||||
actRankScoreService.updateIgnoreNull(actRankScore);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation(value = "删除", nickname = "/delete", httpMethod = "POST")
|
||||
public Result delete(Long id) {
|
||||
actRankScoreService.delete(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.budwk.app.module.activity.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.module.activity.models.ActGroup;
|
||||
import com.budwk.app.module.activity.models.ActItemGroupScore;
|
||||
import com.budwk.app.module.activity.models.ActRankScore;
|
||||
import com.budwk.app.module.activity.models.ActivityItem;
|
||||
import com.budwk.app.module.activity.service.ActivityManageService;
|
||||
import com.budwk.app.web.commons.slog.annotation.SLog;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Chain;
|
||||
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.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/activity/scoreEntry")
|
||||
@Ok("json:full")
|
||||
@Api(tags = "成绩录入", value = "/platform/activity/scoreEntry")
|
||||
public class ActScoreEntryController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ActivityManageService activityService;
|
||||
|
||||
@At("")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@Ok("beetl:/platform/activity/scoreEntry/index.html")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/list-activity")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@ApiOperation(value = "获取活动列表", nickname = "/list-activity", httpMethod = "GET")
|
||||
public Result listAct(Integer year) {
|
||||
Sql sql = Sqls.create("select id,title from activity $condition");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.andEX("YEAR(startTime)", "=", year);
|
||||
sql.setCondition(cnd);
|
||||
return Result.success(activityService.listMap(sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队伍
|
||||
*/
|
||||
@At("/list-group/?")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@ApiOperation(value = "获取队伍", nickname = "/list-group", httpMethod = "GET")
|
||||
public Result listGroup(Long activityId) {
|
||||
List<ActGroup> groupList = activityService.dao().query(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
|
||||
return Result.success(groupList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目列表
|
||||
*/
|
||||
@At("/list-item/?")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@ApiOperation(value = "获取项目列表", nickname = "/list-item", httpMethod = "GET")
|
||||
public Result listItem(Long activityId) {
|
||||
List<ActivityItem> items = dao.query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", activityId).asc(ActivityItem::getSort));
|
||||
return Result.success(items);
|
||||
}
|
||||
|
||||
|
||||
@At("/list-item-score/?")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@ApiOperation(value = "获取队伍项目名次得分", nickname = "/list-item-score", httpMethod = "GET")
|
||||
public Result listItemScore(Long activityId) {
|
||||
List<ActItemGroupScore> scoreList = dao.query(ActItemGroupScore.class, Cnd.where(ActItemGroupScore::getActivityId, "=", activityId));
|
||||
return Result.success(scoreList);
|
||||
}
|
||||
|
||||
@At("/list-rank-score")
|
||||
@SaCheckPermission("activity.scoreEntry")
|
||||
@ApiOperation(value = "获取名次积分对应", nickname = "/list-rank-score", httpMethod = "GET")
|
||||
public Result listRankScore() {
|
||||
List<ActRankScore> rankScores = dao.query(ActRankScore.class, Cnd.NEW().desc(ActRankScore::getScore));
|
||||
return Result.success(rankScores);
|
||||
}
|
||||
|
||||
|
||||
@At("/save-score")
|
||||
@SaCheckPermission("activity.group")
|
||||
@ApiOperation(value = "保存成绩", nickname = "/save-score", httpMethod = "POST")
|
||||
public Result saveScore(@Param("scores") ActItemGroupScore[] actItemGroupScore) {
|
||||
// 更新分数和名次
|
||||
dao.updateIgnoreNull(actItemGroupScore);
|
||||
|
||||
// 更新总得分
|
||||
// ActGroup actGroup = dao.fetch(ActGroup.class, actItemGroupScore.getGroupId());
|
||||
// BigDecimal newTotalScore = actGroup.getTotalScore().add(actItemGroupScore.getScore());
|
||||
// actGroup.setTotalScore(newTotalScore);
|
||||
// dao.update(actGroup);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+20
-10
@@ -71,7 +71,7 @@ public class ActUserController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
public Result pageData(ActUserPageForm pageForm){
|
||||
public Result pageData(ActUserPageForm pageForm) {
|
||||
Sql sql = actUserService.getSignUpSql(pageForm);
|
||||
Pagination pagination = actUserService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
@@ -83,7 +83,7 @@ public class ActUserController {
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("activity.user")
|
||||
@SLog(tag = "报名人员管理", msg = "电脑端替他人报名")
|
||||
public Result doAddActUser(ClientActivitySignVO vo){
|
||||
public Result doAddActUser(ClientActivitySignVO vo) {
|
||||
try {
|
||||
lock.lock();
|
||||
// 报名时间
|
||||
@@ -158,7 +158,8 @@ public class ActUserController {
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
@SLog(tag = "报名人员管理", msg = "删除报名人员")
|
||||
public Result doDeleteActUser(Long id){
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doDeleteActUser(Long id) {
|
||||
dao.delete(ActUser.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
@@ -167,7 +168,7 @@ public class ActUserController {
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
@SLog(tag = "报名人员管理", msg = "修改报名人员信息")
|
||||
public Result doModifyActUser(ActUser actUser){
|
||||
public Result doModifyActUser(ActUser actUser) {
|
||||
dao.updateIgnoreNull(actUser);
|
||||
return Result.success();
|
||||
}
|
||||
@@ -175,14 +176,23 @@ public class ActUserController {
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
@SLog(tag = "报名人员管理", msg = "设置已签到")
|
||||
public Result proxySignIn(Long id){
|
||||
dao.update(ActUser.class, Chain.make("isSignIn",1).add("signInTime",LocalDateTime.now()),Cnd.where(ActUser::getId,"=",id));
|
||||
public Result proxySignIn(Long id) {
|
||||
dao.update(ActUser.class, Chain.make("isSignIn", 1).add("signInTime", LocalDateTime.now()), Cnd.where(ActUser::getId, "=", id));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
public Result findSignUserInfo(Long id){
|
||||
@SLog(tag = "报名人员管理", msg = "取消签到")
|
||||
public Result cancelSignIn(Long id) {
|
||||
dao.update(ActUser.class, Chain.make("isSignIn", 0).add("signInTime", null), Cnd.where(ActUser::getId, "=", id));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
public Result findSignUserInfo(Long id) {
|
||||
return Result.success(dao.fetch(ActUser.class, Cnd.where(ActUser::getId, "=", id)));
|
||||
}
|
||||
|
||||
@@ -192,7 +202,7 @@ public class ActUserController {
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
public Result getActivityList(String year){
|
||||
public Result getActivityList(String year) {
|
||||
List<Activity> activityList = dao.query(Activity.class, Cnd.NEW().andEX("YEAR(startTime)", "=", year).and(Activity::getDelFlag, "=", false));
|
||||
return Result.success(activityList);
|
||||
}
|
||||
@@ -200,7 +210,7 @@ public class ActUserController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activity.user")
|
||||
public Result queryUser(String query, String activityId){
|
||||
public Result queryUser(String query, String activityId) {
|
||||
Sql sql = Sqls.create("SELECT id,username,phone FROM client_user $condition");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
@@ -239,7 +249,7 @@ public class ActUserController {
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, nutMaps);
|
||||
workbook.write(response.getOutputStream());
|
||||
}catch (Exception e){
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
|
||||
@Data
|
||||
@Table("act_rank_score")
|
||||
@Comment("项目名次积分对应表")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||
public class ActRankScore extends BaseModel {
|
||||
|
||||
@Id
|
||||
@Comment("ID")
|
||||
private Long id;
|
||||
|
||||
@Column
|
||||
@Comment("名次")
|
||||
private String ranking;
|
||||
|
||||
@Column
|
||||
@Comment("积分")
|
||||
@ColDefine(customType = "decimal", width = 10, precision = 2)
|
||||
private BigDecimal score;
|
||||
|
||||
}
|
||||
@@ -122,4 +122,27 @@ public class ActUser extends BaseModel {
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("0")
|
||||
private Boolean isGrant;
|
||||
|
||||
@Column
|
||||
@Comment("是否展示才艺")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("0")
|
||||
private Boolean isShowSkill;
|
||||
|
||||
@Column
|
||||
@Comment("才艺名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String skillName;
|
||||
|
||||
@Column
|
||||
@Comment("所需器材")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String equip;
|
||||
|
||||
@Column
|
||||
@Comment("是否参加嘉年华")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("0")
|
||||
private Boolean isParty;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.module.activity.models.ActGroup;
|
||||
import com.budwk.app.module.activity.vo.ActGroupVo;
|
||||
import com.budwk.app.module.activity.vo.ActUnGroupUserVo;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName ActGroupService
|
||||
@@ -26,4 +29,16 @@ public interface ActGroupService extends BaseService<ActGroup> {
|
||||
* @param unGroupUserVo
|
||||
*/
|
||||
void unGroupUserSetting(ActUnGroupUserVo unGroupUserVo);
|
||||
|
||||
/**
|
||||
* 查询未分组人员
|
||||
* @param activityId
|
||||
*/
|
||||
List<NutMap> unGroupUsers(Long activityId);
|
||||
|
||||
/**
|
||||
* 查询分组信息及组员
|
||||
* @param activityId
|
||||
*/
|
||||
List<NutMap> listGroupAndUsers(Long activityId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.budwk.app.module.activity.service;
|
||||
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.module.activity.models.ActRankScore;
|
||||
|
||||
public interface ActRankScoreService extends BaseService<ActRankScore> {
|
||||
}
|
||||
+198
-156
@@ -1,5 +1,6 @@
|
||||
package com.budwk.app.module.activity.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.module.activity.models.*;
|
||||
import com.budwk.app.module.activity.service.ActGroupService;
|
||||
@@ -12,6 +13,7 @@ import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -27,187 +29,227 @@ import java.util.stream.IntStream;
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class ActGroupServiceImpl extends BaseServiceImpl<ActGroup> implements ActGroupService {
|
||||
|
||||
public ActGroupServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
public ActGroupServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void groupSetting(ActGroupVo groupVo) {
|
||||
// 删除已经存在的分组信息
|
||||
dao().clear(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActItemGroupScore.class, Cnd.where(ActItemGroupScore::getActivityId, "=", groupVo.getActivityId()));
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void groupSetting(ActGroupVo groupVo) {
|
||||
// 删除已经存在的分组信息
|
||||
dao().clear(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", groupVo.getActivityId()));
|
||||
dao().clear(ActItemGroupScore.class, Cnd.where(ActItemGroupScore::getActivityId, "=", groupVo.getActivityId()));
|
||||
|
||||
// 查询报名了并且已经完成签到的人员
|
||||
List<ActUser> signUpUserList = dao().query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", groupVo.getActivityId())
|
||||
.and(ActUser::getIsSignIn, "=", 1));
|
||||
// 查询报名了并且已经完成签到的人员
|
||||
List<ActUser> signUpUserList = dao().query(ActUser.class, Cnd.where(ActUser::getActivityId, "=", groupVo.getActivityId())
|
||||
.and(ActUser::getIsSignIn, "=", 1));
|
||||
|
||||
int size = signUpUserList.size();
|
||||
Integer oneGroupLimitUserNum = groupVo.getOneGroupLimitUserNum();
|
||||
int size = signUpUserList.size();
|
||||
Integer oneGroupLimitUserNum = groupVo.getOneGroupLimitUserNum();
|
||||
|
||||
int result = Math.floorDiv(size, oneGroupLimitUserNum);
|
||||
// 初始化分组
|
||||
List<List<ActUser>> groups = new ArrayList<>();
|
||||
for (int i = 0; i < result; i++) {
|
||||
groups.add(new ArrayList<>());
|
||||
}
|
||||
int result = Math.floorDiv(size, oneGroupLimitUserNum);
|
||||
// 初始化分组
|
||||
List<List<ActUser>> groups = new ArrayList<>();
|
||||
for (int i = 0; i < result; i++) {
|
||||
groups.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
// 分离男女用户
|
||||
Map<String, List<ActUser>> genderMap = signUpUserList.stream()
|
||||
.collect(Collectors.groupingBy(user -> "男".equals(user.getSex()) ? "男" : "女"));
|
||||
// 分离男女用户
|
||||
Map<String, List<ActUser>> genderMap = signUpUserList.stream()
|
||||
.collect(Collectors.groupingBy(user -> "男".equals(user.getSex()) ? "男" : "女"));
|
||||
|
||||
List<ActUser> males = genderMap.getOrDefault("男", Collections.emptyList());
|
||||
List<ActUser> females = genderMap.getOrDefault("女", Collections.emptyList());
|
||||
List<ActUser> males = genderMap.getOrDefault("男", Collections.emptyList());
|
||||
List<ActUser> females = genderMap.getOrDefault("女", Collections.emptyList());
|
||||
|
||||
// 随机化列表
|
||||
Collections.shuffle(males);
|
||||
Collections.shuffle(females);
|
||||
// 随机化列表
|
||||
Collections.shuffle(males);
|
||||
Collections.shuffle(females);
|
||||
|
||||
// 尝试分配
|
||||
List<ActUser> unassignedUsers = new ArrayList<>();
|
||||
// 尝试分配
|
||||
List<ActUser> unassignedUsers = new ArrayList<>();
|
||||
|
||||
int maleIndex = 0, femaleIndex = 0;
|
||||
while (maleIndex < males.size() || femaleIndex < females.size()) {
|
||||
boolean assigned = false;
|
||||
for (List<ActUser> group : groups) {
|
||||
if (group.size() < groupVo.getOneGroupLimitUserNum()) {
|
||||
int currentMaleCount = (int) group.stream().filter(u -> "男".equals(u.getSex())).count();
|
||||
int currentFemaleCount = (int) group.stream().filter(u -> "女".equals(u.getSex())).count();
|
||||
int maleIndex = 0, femaleIndex = 0;
|
||||
while (maleIndex < males.size() || femaleIndex < females.size()) {
|
||||
boolean assigned = false;
|
||||
for (List<ActUser> group : groups) {
|
||||
if (group.size() < groupVo.getOneGroupLimitUserNum()) {
|
||||
int currentMaleCount = (int) group.stream().filter(u -> "男".equals(u.getSex())).count();
|
||||
int currentFemaleCount = (int) group.stream().filter(u -> "女".equals(u.getSex())).count();
|
||||
|
||||
if (maleIndex < males.size() && (groupVo.getManUserNum() == -1 || currentMaleCount < groupVo.getManUserNum())) {
|
||||
group.add(males.get(maleIndex++));
|
||||
assigned = true;
|
||||
} else if (femaleIndex < females.size() && (groupVo.getWomanUserNum() == -1 || currentFemaleCount < groupVo.getWomanUserNum())) {
|
||||
group.add(females.get(femaleIndex++));
|
||||
assigned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!assigned) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (maleIndex < males.size() && (groupVo.getManUserNum() == -1 || currentMaleCount < groupVo.getManUserNum())) {
|
||||
group.add(males.get(maleIndex++));
|
||||
assigned = true;
|
||||
} else if (femaleIndex < females.size() && (groupVo.getWomanUserNum() == -1 || currentFemaleCount < groupVo.getWomanUserNum())) {
|
||||
group.add(females.get(femaleIndex++));
|
||||
assigned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!assigned) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unassignedUsers.addAll(males.subList(maleIndex, males.size()));
|
||||
unassignedUsers.addAll(females.subList(femaleIndex, females.size()));
|
||||
unassignedUsers.addAll(males.subList(maleIndex, males.size()));
|
||||
unassignedUsers.addAll(females.subList(femaleIndex, females.size()));
|
||||
|
||||
// 往组别表里面添加记录
|
||||
List<ActGroup> groupList = IntStream.range(0, groups.size())
|
||||
.mapToObj(index -> {
|
||||
ActGroup actGroup = new ActGroup();
|
||||
actGroup.setActivityId(groupVo.getActivityId());
|
||||
actGroup.setGroupName("第" + (index + 1) + "队");
|
||||
actGroup.setSort(index + 1);
|
||||
// 往组别表里面添加记录
|
||||
List<ActGroup> groupList = IntStream.range(0, groups.size())
|
||||
.mapToObj(index -> {
|
||||
ActGroup actGroup = new ActGroup();
|
||||
actGroup.setActivityId(groupVo.getActivityId());
|
||||
actGroup.setGroupName("第" + (index + 1) + "队");
|
||||
actGroup.setSort(index + 1);
|
||||
|
||||
List<ActGroupUser> userList = groups.get(index).stream().map(v -> {
|
||||
ActGroupUser user = new ActGroupUser();
|
||||
user.setUserId(v.getUserId());
|
||||
user.setActivityId(groupVo.getActivityId());
|
||||
return user;
|
||||
}).collect(Collectors.toList());
|
||||
List<ActGroupUser> userList = groups.get(index).stream().map(v -> {
|
||||
ActGroupUser user = new ActGroupUser();
|
||||
user.setUserId(v.getUserId());
|
||||
user.setActivityId(groupVo.getActivityId());
|
||||
return user;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
actGroup.setGroupUsers(userList);
|
||||
return actGroup;
|
||||
}).toList();
|
||||
actGroup.setGroupUsers(userList);
|
||||
return actGroup;
|
||||
}).toList();
|
||||
|
||||
|
||||
// 所有项目
|
||||
List<ActivityItem> actItemList = dao().query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", groupVo.getActivityId()));
|
||||
// 往项目得分表里面插记录
|
||||
List<ActItemGroupScore> itemGroupScoreList = new ArrayList<>();
|
||||
// 所有项目
|
||||
List<ActivityItem> actItemList = dao().query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", groupVo.getActivityId()));
|
||||
// 往项目得分表里面插记录
|
||||
List<ActItemGroupScore> itemGroupScoreList = new ArrayList<>();
|
||||
|
||||
for (ActGroup group : groupList) {
|
||||
dao().insertWith(group, "groupUsers");
|
||||
for (ActGroup group : groupList) {
|
||||
dao().insertWith(group, "groupUsers");
|
||||
|
||||
// 构建小组项目得分表
|
||||
List<ActItemGroupScore> groupScoreList = actItemList.stream().map(item -> {
|
||||
ActItemGroupScore itemGroupScore = new ActItemGroupScore();
|
||||
itemGroupScore.setActivityId(groupVo.getActivityId());
|
||||
itemGroupScore.setItemId(item.getId());
|
||||
itemGroupScore.setGroupId(group.getId());
|
||||
return itemGroupScore;
|
||||
}).toList();
|
||||
itemGroupScoreList.addAll(groupScoreList);
|
||||
}
|
||||
dao().insert(itemGroupScoreList);
|
||||
// 构建小组项目得分表
|
||||
List<ActItemGroupScore> groupScoreList = actItemList.stream().map(item -> {
|
||||
ActItemGroupScore itemGroupScore = new ActItemGroupScore();
|
||||
itemGroupScore.setActivityId(groupVo.getActivityId());
|
||||
itemGroupScore.setItemId(item.getId());
|
||||
itemGroupScore.setGroupId(group.getId());
|
||||
return itemGroupScore;
|
||||
}).toList();
|
||||
itemGroupScoreList.addAll(groupScoreList);
|
||||
}
|
||||
dao().insert(itemGroupScoreList);
|
||||
|
||||
// 添加未能正确分组的人员
|
||||
List<ActUnGroupUser> userList = unassignedUsers.stream().map(v -> {
|
||||
ActUnGroupUser user = new ActUnGroupUser();
|
||||
user.setUserId(v.getUserId());
|
||||
user.setActivityId(groupVo.getActivityId());
|
||||
return user;
|
||||
}).toList();
|
||||
dao().insert(userList);
|
||||
}
|
||||
// 添加未能正确分组的人员
|
||||
List<ActUnGroupUser> userList = unassignedUsers.stream().map(v -> {
|
||||
ActUnGroupUser user = new ActUnGroupUser();
|
||||
user.setUserId(v.getUserId());
|
||||
user.setActivityId(groupVo.getActivityId());
|
||||
return user;
|
||||
}).toList();
|
||||
dao().insert(userList);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void unGroupUserSetting(ActUnGroupUserVo unGroupUserVo) {
|
||||
// 查询还没有分组的人员
|
||||
List<ActUnGroupUser> query = dao().query(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
Set<Long> userIds = query.stream().map(ActUnGroupUser::getUserId).collect(Collectors.toSet());
|
||||
// 删除这些人员的分组信息
|
||||
dao().clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()).and(ActGroupUser::getUserId, "in", userIds));
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void unGroupUserSetting(ActUnGroupUserVo unGroupUserVo) {
|
||||
// 查询还没有分组的人员
|
||||
List<ActUnGroupUser> query = dao().query(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
Set<Long> userIds = query.stream().map(ActUnGroupUser::getUserId).collect(Collectors.toSet());
|
||||
// 删除这些人员的分组信息
|
||||
dao().clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()).and(ActGroupUser::getUserId, "in", userIds));
|
||||
|
||||
if (unGroupUserVo.getIsSeparateGroup() != null && unGroupUserVo.getIsSeparateGroup()) {
|
||||
// 构建分组用户
|
||||
List<ActGroupUser> userList = query.stream().map(v -> {
|
||||
ActGroupUser user = new ActGroupUser();
|
||||
user.setActivityId(unGroupUserVo.getActivityId());
|
||||
user.setUserId(v.getUserId());
|
||||
if (v.getUserId().equals(unGroupUserVo.getLeaderId())) {
|
||||
user.setIsLeader(true);
|
||||
}
|
||||
return user;
|
||||
}).toList();
|
||||
if (unGroupUserVo.getIsSeparateGroup() != null && unGroupUserVo.getIsSeparateGroup()) {
|
||||
// 构建分组用户
|
||||
List<ActGroupUser> userList = query.stream().map(v -> {
|
||||
ActGroupUser user = new ActGroupUser();
|
||||
user.setActivityId(unGroupUserVo.getActivityId());
|
||||
user.setUserId(v.getUserId());
|
||||
if (v.getUserId().equals(unGroupUserVo.getLeaderId())) {
|
||||
user.setIsLeader(true);
|
||||
}
|
||||
return user;
|
||||
}).toList();
|
||||
|
||||
// 查询出这个活动下最大的排序
|
||||
ActGroup sort = dao().fetch(ActGroup.class, Cnd.NEW().desc("sort"));
|
||||
// 查询出这个活动下最大的排序
|
||||
ActGroup sort = dao().fetch(ActGroup.class, Cnd.NEW().desc("sort"));
|
||||
|
||||
// 单独分组构建
|
||||
ActGroup actGroup = new ActGroup();
|
||||
actGroup.setActivityId(unGroupUserVo.getActivityId());
|
||||
actGroup.setGroupName(unGroupUserVo.getGroupName());
|
||||
actGroup.setSort(sort.getSort() + 1);
|
||||
actGroup.setGroupUsers(userList);
|
||||
dao().insertWith(actGroup, "groupUsers");
|
||||
// 单独分组构建
|
||||
ActGroup actGroup = new ActGroup();
|
||||
actGroup.setActivityId(unGroupUserVo.getActivityId());
|
||||
actGroup.setGroupName(unGroupUserVo.getGroupName());
|
||||
actGroup.setSort(sort.getSort() + 1);
|
||||
actGroup.setGroupUsers(userList);
|
||||
dao().insertWith(actGroup, "groupUsers");
|
||||
|
||||
// 同时还要往小组得分记录表中查活动项目的数据
|
||||
List<ActivityItem> itemList = dao().query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
// 构建小组项目得分表
|
||||
List<ActItemGroupScore> scoreList = itemList.stream().map(v -> {
|
||||
ActItemGroupScore itemGroupScore = new ActItemGroupScore();
|
||||
itemGroupScore.setActivityId(unGroupUserVo.getActivityId());
|
||||
itemGroupScore.setItemId(v.getId());
|
||||
itemGroupScore.setGroupId(actGroup.getId());
|
||||
return itemGroupScore;
|
||||
}).toList();
|
||||
dao().insert(scoreList);
|
||||
} else {
|
||||
// 不单独分组,分配到其他队伍
|
||||
List<ActGroupUser> unGroupUser = unGroupUserVo.getUnGroupUser();
|
||||
Map<Long, ActGroupUser> map = unGroupUser.stream().collect(Collectors.toMap(ActGroupUser::getUserId, v -> v));
|
||||
// 同时还要往小组得分记录表中查活动项目的数据
|
||||
List<ActivityItem> itemList = dao().query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
// 构建小组项目得分表
|
||||
List<ActItemGroupScore> scoreList = itemList.stream().map(v -> {
|
||||
ActItemGroupScore itemGroupScore = new ActItemGroupScore();
|
||||
itemGroupScore.setActivityId(unGroupUserVo.getActivityId());
|
||||
itemGroupScore.setItemId(v.getId());
|
||||
itemGroupScore.setGroupId(actGroup.getId());
|
||||
return itemGroupScore;
|
||||
}).toList();
|
||||
dao().insert(scoreList);
|
||||
} else {
|
||||
// 不单独分组,分配到其他队伍
|
||||
List<ActGroupUser> unGroupUser = unGroupUserVo.getUnGroupUser();
|
||||
Map<Long, ActGroupUser> map = unGroupUser.stream().collect(Collectors.toMap(ActGroupUser::getUserId, v -> v));
|
||||
|
||||
List<ActGroupUser> userList = query.stream().map(v -> {
|
||||
// 要保证传递过来的数据中,不在数据表中存储的这些人员不插入
|
||||
ActGroupUser groupUser = map.get(v.getUserId());
|
||||
if (groupUser != null) {
|
||||
groupUser.setUserId(v.getUserId());
|
||||
groupUser.setActivityId(unGroupUserVo.getActivityId());
|
||||
groupUser.setGroupId(groupUser.getGroupId());
|
||||
return groupUser;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).toList();
|
||||
dao().insert(userList);
|
||||
}
|
||||
// 分完之后,删除这个活动下的未分组人员
|
||||
dao().clear(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
}
|
||||
List<ActGroupUser> userList = query.stream().map(v -> {
|
||||
// 要保证传递过来的数据中,不在数据表中存储的这些人员不插入
|
||||
ActGroupUser groupUser = map.get(v.getUserId());
|
||||
if (groupUser != null) {
|
||||
groupUser.setUserId(v.getUserId());
|
||||
groupUser.setActivityId(unGroupUserVo.getActivityId());
|
||||
groupUser.setGroupId(groupUser.getGroupId());
|
||||
return groupUser;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).toList();
|
||||
dao().insert(userList);
|
||||
}
|
||||
// 分完之后,删除这个活动下的未分组人员
|
||||
dao().clear(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", unGroupUserVo.getActivityId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> unGroupUsers(Long activityId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
au.*
|
||||
FROM
|
||||
act_user au
|
||||
LEFT JOIN act_group_user agu ON au.userId = agu.userId
|
||||
AND agu.activityId = @activityId
|
||||
WHERE
|
||||
au.activityId = @activityId
|
||||
AND au.isSignIn = 1
|
||||
AND agu.userId IS NULL
|
||||
""");
|
||||
sql.params().set("activityId", activityId);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> listGroupAndUsers(Long activityId) {
|
||||
List<ActGroup> groups = dao().query(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.*,
|
||||
augu.groupId
|
||||
FROM
|
||||
act_group_user augu
|
||||
LEFT JOIN act_user u ON u.userId = augu.userId
|
||||
WHERE augu.activityId = @activityId
|
||||
""");
|
||||
sql.setParam("activityId", activityId);
|
||||
List<NutMap> users = listMap(sql);
|
||||
|
||||
List<NutMap> groupFull = BeanUtil.copyToList(groups, NutMap.class);
|
||||
for (NutMap group : groupFull) {
|
||||
group.setv("members", users.stream().filter(v -> v.getLong("groupId") == group.getLong("id")).toList());
|
||||
}
|
||||
return groupFull;
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.budwk.app.module.activity.service.impl;
|
||||
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.module.activity.models.ActRankScore;
|
||||
import com.budwk.app.module.activity.service.ActRankScoreService;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class ActRankScoreServiceImpl extends BaseServiceImpl<ActRankScore> implements ActRankScoreService {
|
||||
public ActRankScoreServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.budwk.app.module.activity.vo;
|
||||
|
||||
import com.budwk.app.module.activity.models.ActGroupUser;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ActManualGroupVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long groupName;
|
||||
|
||||
private List<ActGroupUser> members;
|
||||
}
|
||||
@@ -38,7 +38,8 @@ public class SysHomeController {
|
||||
// return "beetl:/platform/sys/dashboard.html";
|
||||
// }
|
||||
// return "beetl:/platform/sys/home.html";
|
||||
return "beetl:/platform/activity/manage/index.html";
|
||||
// return "beetl:/platform/activity/manage/index.html";
|
||||
return ">>:/platform/activity/manage";
|
||||
}
|
||||
|
||||
@At("/403")
|
||||
|
||||
@@ -60,6 +60,6 @@
|
||||
|
||||
.el-button--primary {
|
||||
color: #fff;
|
||||
background-color: #1867b0;
|
||||
border-color: #1867b0;
|
||||
background-color: #3E54AC;
|
||||
border-color: #3E54AC;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Vue.mixin({
|
||||
mounted() {
|
||||
if (this.$options.style) {
|
||||
const styleId = `style-${this._uid}`
|
||||
const style = document.createElement("style")
|
||||
style.id = styleId
|
||||
//支持函数和字符串
|
||||
const css = typeof this.$options.style === "function" ? this.$options.style.call(this) : this.$options.style
|
||||
style.textContent = css
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.$options.style) {
|
||||
const styleId = `style-${this._uid}`
|
||||
const style = document.getElementById(styleId)
|
||||
if (style) {
|
||||
document.head.removeChild(style)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
Vue.mixin({
|
||||
mounted() {
|
||||
if (this.$options.style) {
|
||||
const styleId = `style-${this._uid}`
|
||||
const style = document.createElement("style")
|
||||
style.id = styleId
|
||||
//支持函数和字符串
|
||||
const css = typeof this.$options.style === "function" ? this.$options.style.call(this) : this.$options.style
|
||||
style.textContent = css
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.$options.style) {
|
||||
const styleId = `style-${this._uid}`
|
||||
const style = document.getElementById(styleId)
|
||||
if (style) {
|
||||
document.head.removeChild(style)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -39,8 +39,11 @@
|
||||
<script src="${base!}/assets/platform/plugins/echarts/echarts.min.js"></script>
|
||||
<!-- qrcode -->
|
||||
<script src="${base!}/assets/platform/plugins/qrcode/qrcode.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.14.0/Sortable.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.24.3/vuedraggable.umd.min.js"></script>
|
||||
<!-- 自定义工具类 -->
|
||||
<script src="${base!}/assets/platform/module/mixins/initTableMixins.js?v=1.0.0"></script>
|
||||
<script src="${base!}/assets/platform/module/mixins/styleMixin.js"></script>
|
||||
<!-- main -->
|
||||
<script src="${base!}/assets/platform/js/main.js?v=1.0.1"></script>
|
||||
|
||||
|
||||
@@ -58,14 +58,19 @@ layout("/layouts/platform_1.html"){
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
|
||||
<el-card shadow="never" class="mt-3">
|
||||
<el-row type="flex" style="justify-content: flex-start" class="mb-3">
|
||||
<el-button type="primary" size="small" @click="openGrouping">开始分组</el-button>
|
||||
<div v-if="unGroupUser && unGroupUser.length > 0" style="margin-left: 10px; display: inline-block;">
|
||||
<el-button type="warning" size="small" style="position: relative;" @click="doUnGroupUserSetting">
|
||||
未分组人员
|
||||
<span class="un_group_user_span">{{ unGroupUser.length }}</span>
|
||||
<!-- <div v-if="unGroupUser && unGroupUser.length > 0" style="margin-left: 10px; display: inline-block;">-->
|
||||
<!-- <el-button type="warning" size="small" style="position: relative;" @click="doUnGroupUserSetting">-->
|
||||
<!-- 未分组人员-->
|
||||
<!-- <span class="un_group_user_span">{{ unGroupUser.length }}</span>-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </div>-->
|
||||
<div style="margin-left: 10px; display: inline-block;">
|
||||
<el-button type="warning" size="small" style="position: relative;" @click="$refs.manualGroupRef.onOpen(pageForm.activityId)">
|
||||
手动分组
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
@@ -96,16 +101,19 @@ layout("/layouts/platform_1.html"){
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
|
||||
<group-setting ref="groupSettingRef" @success="successGroup"></group-setting>
|
||||
|
||||
|
||||
<un-group-user ref="unGroupUserRef" @success="successGroup"></un-group-user>
|
||||
|
||||
|
||||
<manual-group ref="manualGroupRef" @success="successGroup"></manual-group>
|
||||
|
||||
<view-info ref="viewInfoRef"></view-info>
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('groupSetting.js'){}#-->
|
||||
<!--#include('unGroupUser.js'){}#-->
|
||||
<!--#include('manualGroup.js'){}#-->
|
||||
<!--#include('viewInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
@@ -113,6 +121,7 @@ layout("/layouts/platform_1.html"){
|
||||
components: {
|
||||
"group-setting": GROUP_SETTING,
|
||||
"un-group-user": UN_GROUP_USER,
|
||||
"manual-group": manualGroup,
|
||||
"view-info": VIEW_INFO
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -0,0 +1,749 @@
|
||||
const manualGroup = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-dialog title="手动分组" :visible.sync="groupingDialog" width="80%" class="manual-group-dialog" fullscreen>
|
||||
<div class="manual-group-content-wrap">
|
||||
<div class="personnel-pool">
|
||||
<div class="pool-header">
|
||||
<h3>人员池</h3>
|
||||
</div>
|
||||
<div class="pool-content">
|
||||
<div class="search-wrapper">
|
||||
<i class="el-icon-search search-icon"></i>
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="搜索人员..."
|
||||
clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div class="personnel-list">
|
||||
<draggable
|
||||
:list="filteredPersonnel"
|
||||
:group="{name:'people', pull: pullFunction, put: true}"
|
||||
class="dragArea"
|
||||
ghost-class="ghost"
|
||||
@end="onDragEnd"
|
||||
@add="onPersonnelAdd">
|
||||
<div v-for="(person, index) in filteredPersonnel" :key="index" class="person-card">
|
||||
<div class="person-header">
|
||||
<div class="person-name">{{ person.userName }}</div>
|
||||
<div class="person-gender" :class="person.sex === '男' ? 'male' : 'female'">
|
||||
{{ person.sex }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="person-unit">{{ person.unitName }}</div>
|
||||
</div>
|
||||
<div v-if="filteredPersonnel.length === 0" class="no-members">
|
||||
<i class="el-icon-warning-outline" style="margin-right: 5px;"></i> 无可用人员
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<i class="el-icon-user stats-icon"></i>
|
||||
共 {{ availablePersonnel.length }} 人可分配
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="groups-area">
|
||||
<div class="group-container-header">
|
||||
<h3>分组管理</h3>
|
||||
<div class="create-text" @click="addNewGroup">
|
||||
<i class="el-icon-plus"></i> 创建新分组
|
||||
</div>
|
||||
</div>
|
||||
<div class="groups-container">
|
||||
<div v-for="(group, groupIndex) in groups" :key="groupIndex" class="group-card">
|
||||
<div class="group-header">
|
||||
<div class="group-title">
|
||||
<el-input
|
||||
v-model="group.groupName"
|
||||
placeholder="请输入组名"
|
||||
size="small"
|
||||
class="group-title-input">
|
||||
</el-input>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
circle
|
||||
@click="removeGroup(groupIndex)">
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group-content">
|
||||
<div>
|
||||
<div class="group-members-header">
|
||||
<div class="members-title">
|
||||
队员 <span class="members-count">{{ group.members.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<draggable
|
||||
v-model="group.members"
|
||||
:group="{name: 'people', pull: true, put: true}"
|
||||
class="group-members"
|
||||
ghost-class="ghost"
|
||||
@end="onDragEnd"
|
||||
@remove="onMemberRemove">
|
||||
<div v-for="(member, memberIndex) in group.members" :key="memberIndex"
|
||||
class="person-card">
|
||||
<div class="person-header">
|
||||
<div class="person-name">{{ member.userName }}</div>
|
||||
<div class="person-gender"
|
||||
:class="member.sex === '男' ? 'male' : 'female'">{{ member.sex }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="person-unit">{{ member.unitName }}</div>
|
||||
</div>
|
||||
<div v-if="group.members.length === 0" class="no-members">
|
||||
<i class="el-icon-d-arrow-left"></i> 拖动人员至此添加队员
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="groups.length === 0" class="empty-groups">
|
||||
<i class="el-icon-collection empty-icon"></i>
|
||||
<div class="empty-text">暂无分组,请点击上方"创建新分组"按钮开始组建团队</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="groupingDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
groupingDialog: false,
|
||||
searchQuery: "",
|
||||
availablePersonnel: [],
|
||||
groups: [],
|
||||
activityId: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredPersonnel() {
|
||||
if (!this.searchQuery) {
|
||||
return this.availablePersonnel
|
||||
}
|
||||
|
||||
const query = this.searchQuery.toLowerCase()
|
||||
return this.availablePersonnel.filter(person => {
|
||||
return person.userName?.toLowerCase().includes(query) ||
|
||||
person.unitName?.toLowerCase().includes(query)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 控制拖动行为
|
||||
pullFunction() {
|
||||
return "clone" // 使用克隆模式,这样拖动不会改变原始数组
|
||||
},
|
||||
async onOpen(activityId) {
|
||||
this.groupingDialog = true
|
||||
this.activityId = activityId
|
||||
|
||||
$.post("/platform/activity/group/unGroupUsers", { activityId }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.availablePersonnel = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
|
||||
$.post("/platform/activity/group/listGroup", { activityId }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.groups = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
// 准备每个小组的数据
|
||||
const groupData = this.groups.map(group => {
|
||||
return {
|
||||
id: group.id,
|
||||
groupName: group.groupName,
|
||||
groupUsers: group.members.map(member => ({
|
||||
userId: member.userId,
|
||||
groupId: group.id,
|
||||
userName: member.userName
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
// 打印每个小组的数据
|
||||
console.log("分组数据:", JSON.stringify(groupData, null, 2))
|
||||
|
||||
// 检查是否有空组名
|
||||
const emptyNameGroup = this.groups.find(group => !group.groupName || group.groupName.trim() === "")
|
||||
if (emptyNameGroup) {
|
||||
this.$message.warning("存在未命名的分组,请为所有分组命名")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查重复组名
|
||||
const duplicateNameGroup = this.groups.find(group => {
|
||||
const groupName = group.groupName.trim()
|
||||
return groupName && this.groups.some(g => g !== group && g.groupName.trim() === groupName)
|
||||
})
|
||||
if (duplicateNameGroup) {
|
||||
this.$message.warning("存在重复的分组名称,请修改分组名称")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有空组
|
||||
const emptyGroup = this.groups.find(group => group.members.length === 0)
|
||||
if (emptyGroup) {
|
||||
this.$confirm("分组" + emptyGroup.groupName + "中没有成员,是否继续保存?", "提示", {
|
||||
confirmButtonText: "继续保存",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.saveGroups(groupData)
|
||||
}).catch(() => {
|
||||
|
||||
})
|
||||
} else {
|
||||
this.saveGroups(groupData)
|
||||
}
|
||||
},
|
||||
async saveGroups(groupData) {
|
||||
// 显示加载状态
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "保存中...",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)"
|
||||
})
|
||||
|
||||
const { code, msg } = await $.post("/platform/activity/group/manualSetGroup", {
|
||||
actGroups: JSON.stringify(groupData),
|
||||
activityId: this.activityId
|
||||
})
|
||||
loading.close()
|
||||
if (code === 0) {
|
||||
this.groupingDialog = false
|
||||
this.$message.success(msg)
|
||||
this.$emit("success")
|
||||
} else {
|
||||
this.$message.error(msg)
|
||||
}
|
||||
},
|
||||
onDragEnd(evt) {
|
||||
if (evt.from.className.includes("dragArea") && evt.to.className.includes("group-members")) {
|
||||
// 从人员池拖到分组时,需要从availablePersonnel中移除
|
||||
const draggedPerson = this.filteredPersonnel[evt.oldIndex]
|
||||
const indexInAvailable = this.availablePersonnel.findIndex(p => p.userId === draggedPerson.userId)
|
||||
|
||||
if (indexInAvailable > -1) {
|
||||
this.availablePersonnel.splice(indexInAvailable, 1)
|
||||
}
|
||||
|
||||
// 设置目标组的groupId
|
||||
const targetGroup = this.findGroupByDomElement(evt.to)
|
||||
if (targetGroup) {
|
||||
// 找到刚刚拖入的成员并设置其groupId
|
||||
const newIndex = evt.newIndex
|
||||
if (targetGroup.members[newIndex]) {
|
||||
targetGroup.members[newIndex].groupId = targetGroup.id
|
||||
}
|
||||
}
|
||||
|
||||
} else if (evt.from.className.includes("group-members") && evt.to.className.includes("group-members")) {
|
||||
// 在分组之间拖动时,确保正确设置groupId
|
||||
const targetGroup = this.findGroupByDomElement(evt.to)
|
||||
if (targetGroup) {
|
||||
// 找到刚刚拖入的成员并设置其groupId
|
||||
const newIndex = evt.newIndex
|
||||
if (targetGroup.members[newIndex]) {
|
||||
targetGroup.members[newIndex].groupId = targetGroup.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保所有组内都没有重复的人员
|
||||
this.$nextTick(() => {
|
||||
this.removeDuplicates()
|
||||
})
|
||||
},
|
||||
removeDuplicates() {
|
||||
// 收集所有已分配人员的ID
|
||||
const assignedIds = new Set()
|
||||
|
||||
// 检查每个组的成员
|
||||
this.groups.forEach(group => {
|
||||
// 检查组内成员是否有重复
|
||||
const uniqueMembers = []
|
||||
group.members.forEach(member => {
|
||||
if (!assignedIds.has(member.userId)) {
|
||||
assignedIds.add(member.userId)
|
||||
uniqueMembers.push(member)
|
||||
}
|
||||
})
|
||||
|
||||
group.members = uniqueMembers
|
||||
})
|
||||
|
||||
// 更新availablePersonnel,移除已分配的人员
|
||||
this.availablePersonnel = this.availablePersonnel.filter(person => !assignedIds.has(person.userId))
|
||||
},
|
||||
addNewGroup() {
|
||||
this.groups.push({
|
||||
id: Date.now(), // 临时ID,后端会重新分配
|
||||
groupName: "分组 " + (this.groups.length + 1),
|
||||
activityId: this.activityId,
|
||||
members: []
|
||||
})
|
||||
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: "已创建新分组",
|
||||
type: "success"
|
||||
})
|
||||
},
|
||||
removeGroup(index) {
|
||||
this.$confirm("确定要删除该分组吗?所有队员将返回人员池", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
// 将组内人员返回到人员池
|
||||
const group = this.groups[index]
|
||||
group.members.forEach(member => {
|
||||
this.availablePersonnel.push({ ...member })
|
||||
})
|
||||
|
||||
// 删除组
|
||||
this.groups.splice(index, 1)
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "删除成功!"
|
||||
})
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
},
|
||||
onPersonnelAdd(evt) {
|
||||
// 当成员被拖回人员池时,会触发此方法
|
||||
// 不需要特殊处理,因为:
|
||||
// 1. Vue自动处理了filteredPersonnel的更新
|
||||
// 2. 源组的成员会在拖动结束时自动移除
|
||||
// 3. removeDuplicates方法会确保没有重复
|
||||
|
||||
// 确保availablePersonnel同步更新
|
||||
if (!this.availablePersonnel.some(p => p.userId === this.filteredPersonnel[evt.newIndex].userId)) {
|
||||
this.availablePersonnel.push(this.filteredPersonnel[evt.newIndex])
|
||||
}
|
||||
},
|
||||
findGroupByDomElement(element) {
|
||||
// 通过DOM元素查找对应的分组数据
|
||||
const groupIndex = this.findGroupIndexByDomElement(element)
|
||||
return groupIndex !== -1 ? this.groups[groupIndex] : null
|
||||
},
|
||||
findGroupIndexByDomElement(element) {
|
||||
// 查找包含该DOM元素的分组索引
|
||||
const groupCard = this.findParentWithClass(element, "group-card")
|
||||
if (!groupCard) return -1
|
||||
|
||||
const groupCards = Array.from(document.querySelectorAll(".group-card"))
|
||||
return groupCards.indexOf(groupCard)
|
||||
},
|
||||
findParentWithClass(element, className) {
|
||||
// 查找具有特定类名的父元素
|
||||
let parent = element
|
||||
while (parent && !parent.classList.contains(className)) {
|
||||
parent = parent.parentElement
|
||||
}
|
||||
return parent
|
||||
},
|
||||
onMemberRemove(evt) {
|
||||
// 当成员被从分组移除时触发(可能是拖到人员池或其他分组)
|
||||
// 如果是拖到人员池,我们需要确保availablePersonnel中添加了这个成员
|
||||
const groupIndex = this.findGroupIndexByDomElement(evt.from)
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.manual-group-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.manual-group-content-wrap {
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
gap: 20px;
|
||||
min-height: 500px;
|
||||
height: calc(100vh - 115px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.personnel-pool {
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pool-header, .group-container-header {
|
||||
background-color: #3E54AC;
|
||||
color: white;
|
||||
padding: 12px 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.pool-header h3, .group-container-header h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pool-content {
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.person-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
cursor: move;
|
||||
border-left: 3px solid #7286D3;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.person-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(114, 134, 211, 0.12), transparent);
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.person-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.person-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.person-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.person-name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #2B3A55;
|
||||
}
|
||||
|
||||
.person-gender {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.male {
|
||||
background-color: #7286D3;
|
||||
}
|
||||
|
||||
.female {
|
||||
background-color: #F87474;
|
||||
}
|
||||
|
||||
.person-unit {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.person-unit::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: #64748b;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.groups-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.groups-container {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
grid-auto-rows: min-content;
|
||||
align-content: start;
|
||||
align-items: start;
|
||||
justify-items: stretch;
|
||||
grid-auto-flow: row;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.group-card {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
max-height: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: 5px solid #3E54AC;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 15px;
|
||||
background-color: var(--primary-light);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-title-input .el-input__inner {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2B3A55;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.group-title-input .el-input__inner:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.group-content {
|
||||
padding: 15px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 150px;
|
||||
/*max-height: 384px;*/
|
||||
}
|
||||
|
||||
.group-leader {
|
||||
background: #f0f9ff;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
border-left: 3px solid #36AE7C;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.leader-badge {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
background-color: #36AE7C;
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
padding: 2px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.group-members-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.members-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #2B3A55;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.members-count {
|
||||
background-color: var(--primary-light);
|
||||
color: #3E54AC;
|
||||
border-radius: 20px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.group-members {
|
||||
min-height: 50px;
|
||||
padding: 8px 0;
|
||||
/*max-height: 250px;*/
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
opacity: 0.4;
|
||||
background: #e0f2fe !important;
|
||||
border: 1px dashed #7286D3 !important;
|
||||
}
|
||||
|
||||
.no-members {
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
border: 2px dashed #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-top: 12px;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--primary-light);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stats-icon {
|
||||
margin-right: 8px;
|
||||
color: #3E54AC;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-wrapper .el-input__inner {
|
||||
border-radius: 25px;
|
||||
padding-left: 35px;
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #94a3b8;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.create-text {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.2s ease;
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
height: 24px; /* 与header高度匹配 */
|
||||
}
|
||||
|
||||
.create-text:hover {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.create-text:active {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.create-text i {
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.group-leader, .leader-badge, .leader-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.empty-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 50px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 50px;
|
||||
color: #cbd5e1;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #64748b;
|
||||
font-size: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.personnel-list {
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
padding-right: 5px;
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_1.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.el-card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.el-card__header {
|
||||
background: #3E54AC;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin: 15px 0;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rank-medal {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rank-medal-1 {
|
||||
background-color: #f5c518;
|
||||
box-shadow: 0 2px 4px rgba(245, 197, 24, 0.4);
|
||||
}
|
||||
|
||||
.rank-medal-2 {
|
||||
background-color: #c0c0c0;
|
||||
box-shadow: 0 2px 4px rgba(192, 192, 192, 0.4);
|
||||
}
|
||||
|
||||
.rank-medal-3 {
|
||||
background-color: #cd7f32;
|
||||
box-shadow: 0 2px 4px rgba(205, 127, 50, 0.4);
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-weight: bold;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.empty-data i {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.batch-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
<el-card shadow="hover" class="" style="width: 800px;margin: 0 auto">
|
||||
<div slot="header" class="clearfix">
|
||||
<span><i class="el-icon-trophy"></i> 名次积分管理</span>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<el-button-group>
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAddMultiple">批量添加</el-button>
|
||||
<el-button type="success" size="small" icon="el-icon-refresh" @click="doSearch">刷新数据</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="tableLoading" :data="tableData" border stripe>
|
||||
<el-table-column label="名次">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="scope.row.ranking === '第一名' || scope.row.ranking === '冠军' || scope.row.ranking === '1'">
|
||||
<span class="rank-medal rank-medal-1">1</span>
|
||||
</template>
|
||||
<template v-else-if="scope.row.ranking === '第二名' || scope.row.ranking === '亚军' || scope.row.ranking === '2'">
|
||||
<span class="rank-medal rank-medal-2">2</span>
|
||||
</template>
|
||||
<template v-else-if="scope.row.ranking === '第三名' || scope.row.ranking === '季军' || scope.row.ranking === '3'">
|
||||
<span class="rank-medal rank-medal-3">3</span>
|
||||
</template>
|
||||
<span>{{ scope.row.ranking }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="积分">
|
||||
<template slot-scope="scope">
|
||||
<span class="score-value">{{ scope.row.score }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="primary" icon="el-icon-edit" circle @click="handleEdit(scope.row)"></el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" circle @click="handleDelete(scope.row)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
|
||||
<template v-if="tableData.length === 0">
|
||||
<div class="empty-data">
|
||||
<i class="el-icon-document"></i>
|
||||
<p>暂无名次积分数据,请点击"批量添加"按钮添加</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="编辑名次积分" :visible.sync="editDialogVisible" width="30%">
|
||||
<el-form :model="editForm" :rules="rules" ref="editForm" label-width="80px">
|
||||
<el-form-item label="名次" prop="ranking">
|
||||
<el-input v-model="editForm.ranking" placeholder="请输入名次,可以是中文" prefix-icon="el-icon-trophy"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="积分" prop="score">
|
||||
<el-input-number v-model="editForm.score" :precision="2" :step="1" :min="0"
|
||||
style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitEditForm">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="批量添加名次积分" :visible.sync="batchAddDialogVisible" width="800px">
|
||||
<el-form :model="batchForm" ref="batchForm">
|
||||
<div class="batch-actions">
|
||||
<div>
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" @click="addBatchRow">添加一行</el-button>
|
||||
<el-button type="danger" size="small" icon="el-icon-delete" @click="clearBatchRows">清空</el-button>
|
||||
</div>
|
||||
<el-button-group>
|
||||
<el-button size="small" type="primary" plain @click="addPresetRanks('123')">添加前三名</el-button>
|
||||
<el-button size="small" type="primary" plain @click="addPresetRanks('champion')">添加冠亚季军</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
<el-table :data="batchForm.items" border style="width: 100%" stripe>
|
||||
<el-table-column label="名次" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'items.' + scope.$index + '.ranking'" :rules="rules.ranking"
|
||||
style="margin-bottom: 0;">
|
||||
<el-input v-model="scope.row.ranking" placeholder="请输入名次" prefix-icon="el-icon-trophy"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="积分">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item :prop="'items.' + scope.$index + '.score'" :rules="rules.score"
|
||||
style="margin-bottom: 0;">
|
||||
<el-input-number v-model="scope.row.score" :precision="2" :step="1" :min="0"
|
||||
controls-position="right" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" circle
|
||||
@click="removeBatchRow(scope.$index)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt-2" v-if="batchError">
|
||||
<el-alert :title="batchError" type="error" show-icon></el-alert>
|
||||
</div>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="batchAddDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitBatchForm">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
editDialogVisible: false,
|
||||
batchAddDialogVisible: false,
|
||||
editForm: {
|
||||
id: null,
|
||||
ranking: "",
|
||||
score: 0
|
||||
},
|
||||
batchForm: {
|
||||
items: []
|
||||
},
|
||||
batchError: "",
|
||||
rules: {
|
||||
ranking: [
|
||||
{ required: true, message: "请输入名次", trigger: "blur" }
|
||||
],
|
||||
score: [
|
||||
{ required: true, message: "请输入积分", trigger: "blur" },
|
||||
{ type: "number", message: "积分必须是数字", trigger: "blur" }
|
||||
]
|
||||
},
|
||||
editIndex: -1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleAddMultiple() {
|
||||
this.batchForm.items = [this.createEmptyBatchItem()]
|
||||
this.batchError = ""
|
||||
this.batchAddDialogVisible = true
|
||||
},
|
||||
createEmptyBatchItem() {
|
||||
return {
|
||||
id: null,
|
||||
ranking: "",
|
||||
score: 0
|
||||
}
|
||||
},
|
||||
addBatchRow() {
|
||||
this.batchForm.items.push(this.createEmptyBatchItem())
|
||||
},
|
||||
removeBatchRow(index) {
|
||||
this.batchForm.items.splice(index, 1)
|
||||
},
|
||||
clearBatchRows() {
|
||||
this.batchForm.items = []
|
||||
},
|
||||
addPresetRanks(type) {
|
||||
if (type === '123') {
|
||||
this.batchForm.items = [
|
||||
{ id: null, ranking: "第一名", score: 100 },
|
||||
{ id: null, ranking: "第二名", score: 80 },
|
||||
{ id: null, ranking: "第三名", score: 60 }
|
||||
]
|
||||
} else if (type === 'champion') {
|
||||
this.batchForm.items = [
|
||||
{ id: null, ranking: "冠军", score: 100 },
|
||||
{ id: null, ranking: "亚军", score: 80 },
|
||||
{ id: null, ranking: "季军", score: 60 }
|
||||
]
|
||||
}
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.editForm = JSON.parse(JSON.stringify(row))
|
||||
this.editIndex = this.tableData.findIndex(item => item.id === row.id)
|
||||
this.editDialogVisible = true
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm("确定要删除这条记录吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
if (row.id) {
|
||||
$.post("/platform/activity/rankScore/delete", { id: row.id }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const index = this.tableData.findIndex(item => item === row)
|
||||
if (index !== -1) {
|
||||
this.tableData.splice(index, 1)
|
||||
this.$message.success("删除成功")
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
submitEditForm() {
|
||||
this.$refs.editForm.validate((valid) => {
|
||||
if (valid) {
|
||||
$.post("/platform/activity/rankScore/update").then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("修改成功")
|
||||
this.editDialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
submitBatchForm() {
|
||||
if (this.batchForm.items.length === 0) {
|
||||
this.$message.error("请至少添加一条记录")
|
||||
return
|
||||
}
|
||||
|
||||
this.$refs.batchForm.validate((valid) => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查新添加的记录中是否有重复的名次
|
||||
const allRankings = this.batchForm.items.map(item => item.ranking)
|
||||
const uniqueRankings = [...new Set(allRankings)]
|
||||
if (allRankings.length !== uniqueRankings.length) {
|
||||
this.batchError = "批量添加的数据中存在重复的名次,请检查"
|
||||
return
|
||||
}
|
||||
|
||||
$.post("/platform/activity/rankScore/save", { actRankScore: JSON.stringify(this.batchForm.items) }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.batchAddDialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
save() {
|
||||
// 检查是否有重复的名次
|
||||
const rankings = this.tableData.map(item => item.ranking)
|
||||
const uniqueRankings = [...new Set(rankings)]
|
||||
if (rankings.length !== uniqueRankings.length) {
|
||||
this.$message.error("存在重复的名次,请检查后重新提交")
|
||||
return
|
||||
}
|
||||
|
||||
$.post("/platform/activity/rankScore/save", { actRankScore: JSON.stringify(this.tableData) }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,551 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_1.html"){
|
||||
#-->
|
||||
<div id="app">
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">年度</div>
|
||||
<div class="search-item-option">
|
||||
<el-date-picker
|
||||
:clearable="false"
|
||||
placeholder="选择年"
|
||||
style="width: 100%"
|
||||
@change="listAct"
|
||||
type="year"
|
||||
v-model="pageForm.year"
|
||||
value-format="yyyy"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">所属活动</div>
|
||||
<div class="search-item-option">
|
||||
<el-select v-model="pageForm.activityId" placeholder="请选择活动" @change="activityChange">
|
||||
<el-option
|
||||
v-for="item in activityOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-query">
|
||||
<el-button type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div class="score-entry-container mt-3">
|
||||
<div class="score-entry-header">
|
||||
<div class="header-title">活动项目名次设置</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!pageForm.activityId" class="empty-state">
|
||||
<i class="el-icon-data-analysis empty-icon"></i>
|
||||
<div class="empty-text">请先选择活动</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="itemOptions.length === 0" class="empty-state">
|
||||
<i class="el-icon-collection empty-icon"></i>
|
||||
<div class="empty-text">暂无项目数据</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="score-tabs-container">
|
||||
<el-tabs type="border-card" class="custom-tabs">
|
||||
<el-tab-pane v-for="item in itemOptions" :key="item.id" :label="item.name">
|
||||
<div class="item-info">
|
||||
<div class="item-details">
|
||||
<div class="detail-item">
|
||||
<i class="el-icon-aim detail-icon"></i>
|
||||
<span class="detail-label">项目形式:</span>
|
||||
<span class="detail-content">{{item.form}}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="el-icon-set-up detail-icon"></i>
|
||||
<span class="detail-label">项目规则:</span>
|
||||
<span class="detail-content">{{item.rule}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button type="primary" size="small" @click="saveItemScores(item.id)" :loading="item.saving">
|
||||
<i class="el-icon-check"></i> 保存此项目分数
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="teams-table-container">
|
||||
<el-table
|
||||
:data="groupOptions"
|
||||
border
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
:header-cell-style="{background:'#f5f7fa', color: '#606266'}"
|
||||
class="custom-table">
|
||||
<el-table-column label="队伍名称" width="150">
|
||||
<template slot-scope="scope">
|
||||
<div class="team-name">
|
||||
<i class="el-icon-user-solid team-icon"></i>
|
||||
{{ scope.row.groupName }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名次设置" width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-select
|
||||
v-model="getScoreRecord(item.id, scope.row.id).rankingObj"
|
||||
placeholder="请选择名次"
|
||||
@change="updateScore(item.id, scope.row.id)"
|
||||
class="rank-select">
|
||||
<el-option
|
||||
v-for="(rank, index) in rankScoreOptions"
|
||||
:key="rank.id"
|
||||
:label="rank.ranking"
|
||||
:value="index + 1">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="得分">
|
||||
<template slot-scope="scope">
|
||||
<div class="score-value"
|
||||
:class="{'has-score': getScoreRecord(item.id, scope.row.id).score}">
|
||||
{{ getScoreRecord(item.id, scope.row.id).score || '-' }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" width="250">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="getScoreRecord(item.id, scope.row.id).remark"
|
||||
placeholder="请输入备注"
|
||||
type="textarea"
|
||||
class="remark-input"
|
||||
:rows="2">
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
year: new Date().getFullYear() + ""
|
||||
},
|
||||
activityOptions: [],
|
||||
groupOptions: [],
|
||||
itemOptions: [],
|
||||
itemScoreList: [],
|
||||
rankScoreOptions: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取活动
|
||||
listAct() {
|
||||
$.get("/platform/activity/scoreEntry/list-activity", this.pageForm).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.activityOptions = res.data
|
||||
if (this.activityOptions.length > 0) {
|
||||
this.pageForm.activityId = this.activityOptions[0].id
|
||||
this.activityChange()
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取队伍
|
||||
async listGroup() {
|
||||
const {
|
||||
code,
|
||||
data,
|
||||
msg
|
||||
} = await $.get("/platform/activity/scoreEntry/list-group/" + this.pageForm.activityId)
|
||||
if (code === 0) {
|
||||
this.groupOptions = data
|
||||
} else {
|
||||
this.$message.error(msg)
|
||||
}
|
||||
},
|
||||
// 获取项目
|
||||
async listItem() {
|
||||
const {
|
||||
code,
|
||||
data,
|
||||
msg
|
||||
} = await $.get("/platform/activity/scoreEntry/list-item/" + this.pageForm.activityId)
|
||||
if (code === 0) {
|
||||
// 添加保存状态标志
|
||||
this.itemOptions = data.map(item => ({
|
||||
...item,
|
||||
saving: false
|
||||
}))
|
||||
} else {
|
||||
this.$message.error(msg)
|
||||
}
|
||||
},
|
||||
// 获取名次积分对应
|
||||
async listRankScore() {
|
||||
const {
|
||||
code,
|
||||
data,
|
||||
msg
|
||||
} = await $.get("/platform/activity/scoreEntry/list-rank-score")
|
||||
if (code === 0) {
|
||||
this.rankScoreOptions = data
|
||||
} else {
|
||||
this.$message.error(msg)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取项目得分列表
|
||||
async listItemScore() {
|
||||
const {
|
||||
code,
|
||||
data,
|
||||
msg
|
||||
} = await $.get("/platform/activity/scoreEntry/list-item-score/" + this.pageForm.activityId)
|
||||
if (code === 0) {
|
||||
this.itemScoreList = data
|
||||
|
||||
// 为每条记录添加rankingObj属性,用于下拉框绑定
|
||||
this.itemScoreList.forEach(score => {
|
||||
score.rankingObj = score.ranking || null
|
||||
})
|
||||
} else {
|
||||
this.$message.error(msg)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取特定项目和队伍的得分记录
|
||||
getScoreRecord(itemId, groupId) {
|
||||
// 在已有的记录中查找
|
||||
let record = this.itemScoreList.find(score =>
|
||||
score.itemId === itemId && score.groupId === groupId
|
||||
)
|
||||
|
||||
// 如果没有找到,创建一个新记录
|
||||
if (!record) {
|
||||
record = {
|
||||
activityId: this.pageForm.activityId,
|
||||
itemId: itemId,
|
||||
groupId: groupId,
|
||||
score: null,
|
||||
ranking: null,
|
||||
rankingObj: null,
|
||||
remark: ""
|
||||
}
|
||||
this.itemScoreList.push(record)
|
||||
}
|
||||
|
||||
// 确保remark字段存在
|
||||
if (!record.hasOwnProperty("remark")) {
|
||||
record.remark = ""
|
||||
}
|
||||
|
||||
// 确保rankingObj字段存在
|
||||
if (!record.hasOwnProperty("rankingObj")) {
|
||||
record.rankingObj = record.ranking
|
||||
}
|
||||
|
||||
return record
|
||||
},
|
||||
|
||||
// 更新分数
|
||||
updateScore(itemId, groupId) {
|
||||
const record = this.getScoreRecord(itemId, groupId)
|
||||
const rankingValue = record.rankingObj
|
||||
|
||||
// 同步ranking字段和rankingObj字段
|
||||
record.ranking = rankingValue
|
||||
|
||||
if (rankingValue) {
|
||||
// 查找对应名次的分数
|
||||
const rankScore = this.rankScoreOptions[rankingValue - 1]
|
||||
if (rankScore) {
|
||||
record.score = rankScore.score
|
||||
}
|
||||
} else {
|
||||
record.score = null
|
||||
}
|
||||
},
|
||||
|
||||
// 保存单个项目的分数
|
||||
async saveItemScores(itemId) {
|
||||
if (!this.pageForm.activityId) {
|
||||
this.$message.warning("请先选择活动")
|
||||
return
|
||||
}
|
||||
|
||||
// 找到当前项目
|
||||
const currentItem = this.itemOptions.find(item => item.id === itemId)
|
||||
if (!currentItem) return
|
||||
|
||||
// 设置保存状态
|
||||
currentItem.saving = true
|
||||
|
||||
try {
|
||||
// 过滤出当前项目的有效分数记录
|
||||
const validScores = this.itemScoreList.filter(score =>
|
||||
score.itemId === itemId && score.ranking !== null
|
||||
)
|
||||
|
||||
// 转换为后端需要的数据格式
|
||||
const postData = validScores.map(score => ({
|
||||
id: score.id,
|
||||
activityId: score.activityId,
|
||||
itemId: score.itemId,
|
||||
groupId: score.groupId,
|
||||
score: score.score,
|
||||
ranking: score.ranking
|
||||
}))
|
||||
|
||||
const { code, msg } = await $.post("/platform/activity/scoreEntry/save-score", {
|
||||
scores: JSON.stringify(postData)
|
||||
})
|
||||
|
||||
if (code === 0) {
|
||||
this.$message.success(currentItem.name+'项目分数保存成功')
|
||||
await this.listItemScore()
|
||||
} else {
|
||||
this.$message.error(msg || "保存失败")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.$message.error("保存失败:" + (error.message || "未知错误"))
|
||||
} finally {
|
||||
currentItem.saving = false
|
||||
}
|
||||
},
|
||||
|
||||
// 活动改变
|
||||
async activityChange() {
|
||||
this.loading = true
|
||||
try {
|
||||
await this.listGroup()
|
||||
await this.listItem()
|
||||
await this.listItemScore()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 搜索
|
||||
doSearch() {
|
||||
if (this.pageForm.activityId) {
|
||||
this.activityChange()
|
||||
} else {
|
||||
this.$message.warning("请先选择活动")
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listAct()
|
||||
this.listRankScore()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.filter-card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important;
|
||||
}
|
||||
|
||||
.score-entry-container {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.score-entry-header {
|
||||
background-color: #3E54AC;
|
||||
color: white;
|
||||
padding: 12px 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__header {
|
||||
background-color: #f8fafc;
|
||||
padding: 0 15px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item.is-active {
|
||||
color: #3E54AC;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__nav-wrap::after {
|
||||
background-color: #e2e8f0;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__active-bar {
|
||||
background-color: #3E54AC;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
background-color: #f8fafc;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item-details {
|
||||
line-height: 1.6;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
margin-left: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
color: #3E54AC;
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: 600;
|
||||
color: #2B3A55;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
color: #64748b;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.teams-table-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.custom-table {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.team-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
color: #2B3A55;
|
||||
}
|
||||
|
||||
.team-icon {
|
||||
color: #7286D3;
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.rank-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rank-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.score-value.has-score {
|
||||
color: #36AE7C;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.remark-input .el-textarea__inner {
|
||||
border-radius: 4px;
|
||||
resize: none;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
.remark-input .el-textarea__inner:focus {
|
||||
border-color: #7286D3;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 60px;
|
||||
color: #cbd5e1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #64748b;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -97,20 +97,20 @@ layout("/layouts/platform_1.html"){
|
||||
{{row.isStay?'住宿':'不住宿'}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物品发放" prop="isGrant">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.isGrant" type="success">已发放</el-tag>
|
||||
<el-tag v-else type="danger">未发放</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="200">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="!row.isGrant || row.isGrant === 0" type="primary" size="mini"
|
||||
@click="doGrant(row, true)">设为已发放物品
|
||||
</el-button>
|
||||
<el-button v-else type="danger" size="mini" @click="doGrant(row, false)">设为未发放物品</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="物品发放" prop="isGrant">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <el-tag v-if="scope.row.isGrant" type="success">已发放</el-tag>-->
|
||||
<!-- <el-tag v-else type="danger">未发放</el-tag>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="操作" fixed="right" width="200">-->
|
||||
<!-- <template #default="{row}">-->
|
||||
<!-- <el-button v-if="!row.isGrant || row.isGrant === 0" type="primary" size="mini"-->
|
||||
<!-- @click="doGrant(row, true)">设为已发放物品-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button v-else type="danger" size="mini" @click="doGrant(row, false)">设为未发放物品</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
@@ -76,6 +76,7 @@ layout("/layouts/platform_1.html"){
|
||||
</el-row>
|
||||
<el-table v-loading="tableLoading" show-overflow-tooltip :data="tableData">
|
||||
<el-table-column label="序号" type="index" header-align="center" align="center"
|
||||
:index="indexMethod"
|
||||
width="50"></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName"></el-table-column>
|
||||
<el-table-column label="性别" prop="sex"></el-table-column>
|
||||
@@ -93,6 +94,7 @@ layout("/layouts/platform_1.html"){
|
||||
<el-table-column label="操作" fixed="right" width="300">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="mini" @click="proxySignIn(scope.row.id)" v-if="!scope.row.isSignIn">签到</el-button>
|
||||
<el-button type="primary" size="mini" @click="cancelSignIn(scope.row.id)" v-if="scope.row.isSignIn">取消签到</el-button>
|
||||
<el-button type="primary" size="mini" @click="openModify(scope.row)">修改</el-button>
|
||||
<el-button type="danger" size="mini" @click="doRemove(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
@@ -466,7 +468,22 @@ layout("/layouts/platform_1.html"){
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.warning('操作失败')
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelSignIn(id){
|
||||
this.$confirm('您确定要取消签到吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const resp = await $.post('/platform/activity/user/cancelSignIn', {id})
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
}else{
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ layout("/layouts/platform_1.html"){
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mt-3">
|
||||
<el-table :key="tableKey" :data="tableData" style="width: 100%" size="small"
|
||||
<el-table :key="tableKey" :data="tableData" style="width: 100%"
|
||||
:highlight-current-row="true" row-key="id" lazy
|
||||
:load="loadChild">
|
||||
<el-table-column label="菜单名称" header-align="center" prop="name"
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
package com.budwk.app;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.utils.PwdUtil;
|
||||
import com.budwk.app.sys.models.Sys_dict;
|
||||
import com.budwk.app.sys.services.SysDictService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import lombok.Data;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.nutz.boot.test.junit4.NbJUnit4Runner;
|
||||
@@ -11,8 +20,14 @@ import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@IocBean
|
||||
@RunWith(NbJUnit4Runner.class)
|
||||
@@ -24,7 +39,7 @@ public class Test1 {
|
||||
private SysDictService sysDictService;
|
||||
|
||||
@Test
|
||||
public void to01(){
|
||||
public void to01() {
|
||||
String str = """
|
||||
'旅行', '摄影', '音乐', '电影', '美食', '健身', '阅读', '跑步','游泳', '爬山', '滑雪', '烹饪', '绘画', '书法', '手工', '舞蹈','瑜伽', '钢琴', '吉他', '写作', '篮球', '足球', '网球', '羽毛球','动漫', '游戏', '宠物', '园艺', '咖啡', '茶艺', '插花', '收藏','志愿服务', '数码', '科技', '投资', '理财', '历史', '哲学', '心理学'
|
||||
""";
|
||||
@@ -42,11 +57,56 @@ public class Test1 {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void t002(){
|
||||
String salt = R.UU32();
|
||||
System.out.println(salt);
|
||||
String password = PwdUtil.getPassword("dd3s@#2025", salt);
|
||||
System.out.println(password);
|
||||
public void t002() {
|
||||
List<ExcelUser> list = ExcelImportUtil.importExcel(new File("C:\\Users\\jug\\Desktop\\工作簿3.xlsx"), ExcelUser.class, new ImportParams());
|
||||
|
||||
List<ExcelUser> list2 = ExcelImportUtil.importExcel(new File("D:\\downloads\\会员名单 (3).xls"), ExcelUser.class, new ImportParams());
|
||||
|
||||
|
||||
List<NutMap> changeUsers = new ArrayList<>();
|
||||
|
||||
// 找出list中的人员在list2中单位不一样的数据
|
||||
list.forEach(e -> {
|
||||
Optional<ExcelUser> first = list2.stream().filter(e2 -> e2.getGh().equals(e.getGh())).findFirst();
|
||||
if (first.isPresent()) {
|
||||
String dw = StrUtil.blankToDefault(first.get().getDw(), "");
|
||||
if (!dw.equals(e.getDw())) {
|
||||
// System.out.println("姓名:" + e.getName() + ",工号:" + e.getGh() + ",原单位:" + e.getDw() + ",现单位:" + dw);
|
||||
changeUsers.add(NutMap.NEW()
|
||||
.addv("name", e.getName())
|
||||
.addv("gh", e.getGh())
|
||||
.addv("ydw", e.getDw())
|
||||
.addv("xdw", dw));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ArrayList<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("姓名", "name"));
|
||||
entities.add(new ExcelExportEntity("工号", "gh"));
|
||||
entities.add(new ExcelExportEntity("原单位", "ydw"));
|
||||
entities.add(new ExcelExportEntity("现单位", "xdw"));
|
||||
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, changeUsers);
|
||||
try {
|
||||
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\jug\\Desktop\\单位变动人员.xlsx");
|
||||
workbook.write(outputStream);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ExcelUser {
|
||||
@Excel(name = "工号")
|
||||
private String gh;
|
||||
|
||||
@Excel(name = "姓名")
|
||||
private String name;
|
||||
|
||||
@Excel(name = "单位")
|
||||
private String dw;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 人员选项卡区域 -->
|
||||
<view class="tabs-container">
|
||||
<view class="tabs-header">
|
||||
<view class="tabs-title">
|
||||
<text class="title-text">报名人员</text>
|
||||
<view class="header-actions">
|
||||
<view class="person-counter">
|
||||
<text class="counter-text">{{ formData.userList.length }}人</text>
|
||||
</view>
|
||||
<view class="header-add-btn" @click="addNewUser">
|
||||
<t-icon name="add" size="16px"></t-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-x="true" class="tabs-scroll-view" show-scrollbar="false">
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="(user, index) in formData.userList"
|
||||
:key="index"
|
||||
class="tab"
|
||||
:class="{ active: activeTabIndex === index }"
|
||||
@click="switchTab(index)"
|
||||
>
|
||||
<view class="tab-content">
|
||||
<view class="tab-icon" v-if="activeTabIndex === index">
|
||||
<t-icon name="user" size="16px"></t-icon>
|
||||
</view>
|
||||
<text class="tab-text">{{ user.userName || `人员${index + 1}` }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="tabs-indicator"></view>
|
||||
</view>
|
||||
|
||||
<view class="form-area">
|
||||
<t-form
|
||||
ref="formRef"
|
||||
@@ -9,6 +45,7 @@
|
||||
label-align="left"
|
||||
scroll-to-first-error="auto"
|
||||
labelWidth="90px"
|
||||
:requiredMark="true"
|
||||
>
|
||||
<!-- <view class="form-block">-->
|
||||
<!-- <view class="form-block-title">-->
|
||||
@@ -29,20 +66,19 @@
|
||||
<view class="form-block">
|
||||
<view class="form-block-title">
|
||||
<t-icon name="usergroup"></t-icon>
|
||||
报名人员信息
|
||||
</view>
|
||||
<view v-for="(user,index) in formData.userList" :key="index" class="user-card">
|
||||
<view class="user-card-header">
|
||||
<view class="user-card-header-title">
|
||||
<span>{{ index + 1 }}</span>
|
||||
</view>
|
||||
<view class="remove" @click="removeUser(index)">
|
||||
<text>报名人员信息</text>
|
||||
<text class="form-subtitle">{{ activeTabIndex + 1 }}/{{ formData.userList.length }}</text>
|
||||
<view class="user-actions">
|
||||
<view class="remove-user" @click="removeUser(activeTabIndex)" v-if="formData.userList.length > 1">
|
||||
<t-icon name="delete" size="18px"></t-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 所有人员的表单,使用v-show控制显示 -->
|
||||
<view v-for="(user, index) in formData.userList" :key="index" class="form-fields-container" v-show="activeTabIndex === index">
|
||||
<t-form-item label="姓名" :name="`userList[${index}].userName`"
|
||||
:rules="[{required:true,message: '请输入姓名'}]">
|
||||
:rules="[{required:true,message: '请输入姓名'}]">
|
||||
<t-input v-model="user.userName" borderless placeholder="请输入内容"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="性别" :name="`userList[${index}].sex`" :rules="[{required:true,message: '请选择性别'}]">
|
||||
@@ -51,47 +87,65 @@
|
||||
<t-radio :block="false" label="女" value="女"/>
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<t-form-item label="单位名称">
|
||||
<t-input v-model="user.unitName" borderless placeholder="请输入单位名称" readonly></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="职务" :name="`userList[${index}].position`"
|
||||
:rules="[{required:true,message: '请输入职务'}]">
|
||||
:rules="[{required:true,message: '请输入职务'}]">
|
||||
<t-input v-model="user.position" borderless placeholder="请输入职务"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="身份证号" :name="`userList[${index}].idCard`"
|
||||
:rules="[{required:true,message: '请输入身份证号码'},{idcard:true, message: '请输入正确的身份证号码'}]">
|
||||
:rules="[{required:true,message: '请输入身份证号码'},{idcard:true, message: '请输入正确的身份证号码'}]">
|
||||
<t-input v-model="user.idCard" borderless placeholder="请输入身份证号码"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="联系电话" :name="`userList[${index}].phone`"
|
||||
:rules="[{required:true,message: '请输入联系电话'},{telnumber:true, message: '请输入正确的联系电话'}]">
|
||||
:rules="[{required:true,message: '请输入联系电话'},{telnumber:true, message: '请输入正确的联系电话'}]">
|
||||
<t-input v-model="user.phone" borderless placeholder="请输入联系电话"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="是否住宿" :name="`userList[${index}].isStay`"
|
||||
:rules="[{required:true,message: '请选择是否住宿'}]">
|
||||
:rules="[{required:true,message: '请选择是否住宿'}]">
|
||||
<t-radio-group v-model="user.isStay" class="radio-box">
|
||||
<t-radio :block="false" name="radio" :value="true" label="是"/>
|
||||
<t-radio :block="false" name="radio" :value="false" label="否"/>
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<t-form-item label="是否联系人" :name="`userList[${index}].isContacts`"
|
||||
:rules="[{required:true,message: '请选择是否为联系人'}]">
|
||||
:rules="[{required:true,message: '请选择是否为联系人'}]">
|
||||
<t-radio-group v-model="user.isContacts" class="radio-box">
|
||||
<t-radio :block="false" name="radio" :value="true" label="是"/>
|
||||
<t-radio :block="false" name="radio" :value="false" label="否"/>
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<t-form-item label="展示才艺" :name="`userList[${index}].isShowSkill`" :rules="[{required:true,message: '请选择是否展示才艺'}]">
|
||||
<t-radio-group v-model="user.isShowSkill" class="radio-box">
|
||||
<t-radio :block="false" name="radio" :value="true" label="是"/>
|
||||
<t-radio :block="false" name="radio" :value="false" label="否"/>
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<template v-if="`userList[${index}].isShowSkill`">
|
||||
<t-form-item label="才艺名称" :name="`userList[${index}].skillName`" :rules="[{required:user.isShowSkill===true,message: '请填写才艺名称'}]">
|
||||
<t-input v-model="user.skillName" borderless placeholder="请输入才艺名称"></t-input>
|
||||
</t-form-item>
|
||||
<t-form-item label="所需器材" :name="`userList[${index}].equip`" :rules="[{required:user.isShowSkill===true,message: '请填写才艺所需器材'}]">
|
||||
<t-input v-model="user.equip" borderless placeholder="请填写才艺所需器材"></t-input>
|
||||
</t-form-item>
|
||||
</template>
|
||||
<t-form-item label="参加嘉年华" :name="`userList[${index}].isParty`" :rules="[{required:true,message: '请选择是否参加嘉年华'}]">
|
||||
<t-radio-group v-model="user.isParty" class="radio-box">
|
||||
<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>
|
||||
</view>
|
||||
</t-form>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮添加按钮 -->
|
||||
<view class="float-add-btn" @click="formData.userList.push({})">
|
||||
<t-icon name="plus" size="24px" color="#fff"></t-icon>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="agreement-checkbox">
|
||||
<t-checkbox label="多选" icon="rectangle" v-model:checked="agreementChecked" default-checked>
|
||||
<label>我已阅读并同意
|
||||
<text class="agreement-link">《参赛承诺书》</text>
|
||||
<text class="agreement-link" @click="showAgreementPopup">《参赛承诺书》</text>
|
||||
</label>
|
||||
</t-checkbox>
|
||||
</view>
|
||||
@@ -112,12 +166,32 @@
|
||||
:steps="guideSteps"
|
||||
>
|
||||
</t-guide>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 协议确认弹窗 -->
|
||||
<t-popup v-model:visible="agreementPopupVisible" placement="center" :close-on-overlay-click="false">
|
||||
<view class="agreement-popup">
|
||||
<view class="agreement-popup-header">
|
||||
<text class="agreement-popup-title">参赛条款与免责声明</text>
|
||||
</view>
|
||||
<view class="agreement-popup-content">
|
||||
<view class="agreement-popup-text">
|
||||
<!-- 阅读并同意本承诺书内容,点击"确定并报名"即表示您已充分理解并接受所有条款,承担相应法律责任。-->
|
||||
<rich-text :nodes="agreementContent"></rich-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="agreement-popup-footer">
|
||||
<t-button variant="outline" block @click="closeAgreementPopup" class="agreement-btn">取消</t-button>
|
||||
<t-button theme="primary" block :disabled="!canConfirmAgreement" @click="confirmAgreement" class="agreement-btn">
|
||||
{{ canConfirmAgreement ? '确定并报名' : `请阅读(${agreementCountdown}秒)` }}
|
||||
</t-button>
|
||||
</view>
|
||||
</view>
|
||||
</t-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue'
|
||||
import {ref, computed} from 'vue'
|
||||
import {useRequest} from '@/utils/request';
|
||||
import {Dialog, Toast} from "tdesign-mobile-vue";
|
||||
import {onLoad} from "@dcloudio/uni-app";
|
||||
@@ -126,19 +200,84 @@ import {useUserStore} from "@/store";
|
||||
const request = useRequest()
|
||||
const userStore= useUserStore()
|
||||
|
||||
// 当前选中的选项卡索引
|
||||
const activeTabIndex = ref(0)
|
||||
|
||||
// 切换选项卡
|
||||
function switchTab(index) {
|
||||
activeTabIndex.value = index
|
||||
}
|
||||
|
||||
// 添加新用户
|
||||
function addNewUser() {
|
||||
formData.value.userList.push({
|
||||
userName: '',
|
||||
sex: null,
|
||||
unitName: userStore?.userInfo?.unit?.name,
|
||||
position: null,
|
||||
idCard: null,
|
||||
phone: null,
|
||||
isStay: null,
|
||||
isContacts: false,
|
||||
isShowSkill: false,
|
||||
skillName: null,
|
||||
equip: null,
|
||||
isParty: false
|
||||
})
|
||||
// 切换到新添加的用户选项卡
|
||||
setTimeout(() => {
|
||||
activeTabIndex.value = formData.value.userList.length - 1
|
||||
// 滚动到最右边
|
||||
scrollToRight()
|
||||
}, 10)
|
||||
}
|
||||
|
||||
// 滚动到指定选项卡
|
||||
function scrollToTab(index) {
|
||||
const query = uni.createSelectorQuery()
|
||||
query.selectAll('.tab').boundingClientRect()
|
||||
query.select('.tabs-scroll-view').boundingClientRect()
|
||||
query.exec((res) => {
|
||||
if (res && res[0] && res[0][index] && res[1]) {
|
||||
const tabElement = res[0][index]
|
||||
const scrollView = res[1]
|
||||
const scrollLeft = tabElement.left - scrollView.left - (scrollView.width / 2) + (tabElement.width / 2)
|
||||
uni.pageScrollTo({
|
||||
scrollLeft: scrollLeft > 0 ? scrollLeft : 0,
|
||||
duration: 300
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 滚动到最右边
|
||||
function scrollToRight() {
|
||||
const query = uni.createSelectorQuery()
|
||||
query.select('.tabs-scroll-view').boundingClientRect()
|
||||
query.exec((res) => {
|
||||
if (res && res[0]) {
|
||||
const scrollView = res[0]
|
||||
const scrollWidth = scrollView.scrollWidth
|
||||
const clientWidth = scrollView.width
|
||||
const scrollLeft = scrollWidth - clientWidth
|
||||
if (scrollLeft > 0) {
|
||||
uni.pageScrollTo({
|
||||
scrollLeft: scrollLeft,
|
||||
duration: 300
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 引导部分
|
||||
const guideCurrent = ref(-1)
|
||||
const guideSteps = [
|
||||
{
|
||||
element:()=> document.querySelector('.float-add-btn'),
|
||||
element:()=> document.querySelector('.header-add-btn'),
|
||||
title: '添加活动报名人员',
|
||||
placement: 'top'
|
||||
},
|
||||
{
|
||||
element:()=> document.querySelector('.t-form'),
|
||||
title: '填写报名信息',
|
||||
placement: 'top'
|
||||
},
|
||||
{
|
||||
element:()=> document.querySelector('.footer'),
|
||||
title: '提交报名信息',
|
||||
@@ -152,25 +291,52 @@ const isSignUp = ref(null)
|
||||
const agreementChecked = ref(false)
|
||||
const formRef = ref(null)
|
||||
const formData = ref({
|
||||
userList: [{}]
|
||||
userList: []
|
||||
})
|
||||
// 协议弹窗相关
|
||||
const agreementPopupVisible = ref(false)
|
||||
const agreementCountdown = ref(5)
|
||||
const countdownTimer = ref(null)
|
||||
const canConfirmAgreement = ref(false)
|
||||
const agreementContent = ref(null)
|
||||
|
||||
onLoad((option) => {
|
||||
onLoad(async (option) => {
|
||||
if (option.id) {
|
||||
actId.value = option.id
|
||||
formData.value.activityId = option.id
|
||||
listSignUpUser()
|
||||
getIsSignUp()
|
||||
await listSignUpUser()
|
||||
await getIsSignUp()
|
||||
await getAgreement()
|
||||
// 没报名进行引导
|
||||
if(formData.value.userList.length === 0){
|
||||
if(!formData.value.userList || formData.value.userList.length === 0){
|
||||
guideCurrent.value = 0
|
||||
//formData.value.unitName = userStore.userInfo?.unit.name
|
||||
// 默认是本人的信息
|
||||
formData.value.userList.push({
|
||||
userName: userStore.userInfo.userName,
|
||||
sex: userStore.userInfo.sex,
|
||||
unitName: userStore?.userInfo?.unit?.name,
|
||||
position: null,
|
||||
idCard: userStore.userInfo.idCard,
|
||||
phone: userStore.userInfo.phone,
|
||||
isStay: null
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Toast({message: 'ID不存在', theme: 'error'});
|
||||
}
|
||||
})
|
||||
|
||||
// 查询参赛承诺书
|
||||
async function getAgreement() {
|
||||
try {
|
||||
const {code, data} = await request.get('/client/activity/agreement/' + actId.value)
|
||||
if (code === 0 && data) {
|
||||
agreementContent.value = data
|
||||
}
|
||||
}catch (e){
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询报名人员信息
|
||||
async function listSignUpUser() {
|
||||
@@ -216,28 +382,86 @@ async function validaForm() {
|
||||
// 承诺书
|
||||
async function validateAgreement() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (agreementChecked.value) {
|
||||
resolve(true)
|
||||
return
|
||||
}
|
||||
// Always show the popup regardless of checkbox state
|
||||
// if (agreementChecked.value) {
|
||||
// resolve(true)
|
||||
// return
|
||||
// }
|
||||
|
||||
Dialog.confirm({
|
||||
title: '提示',
|
||||
content: '参赛条款与免责声明:阅读并同意本承诺书内容,点击"确定并报名"即表示您已充分理解并接受所有条款,承担相应法律责任。',
|
||||
confirmBtn: '确定并报名',
|
||||
cancelBtn: '取消',
|
||||
onConfirm: () => {
|
||||
agreementChecked.value = true
|
||||
resolve(true)
|
||||
},
|
||||
onCancel: () => {
|
||||
agreementChecked.value = false
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
// 重置倒计时
|
||||
agreementCountdown.value = 5
|
||||
canConfirmAgreement.value = false
|
||||
|
||||
// 显示弹窗
|
||||
agreementPopupVisible.value = true
|
||||
|
||||
// 启动倒计时
|
||||
startAgreementCountdown(resolve)
|
||||
})
|
||||
}
|
||||
|
||||
// 启动协议倒计时
|
||||
function startAgreementCountdown(resolvePromise) {
|
||||
// 清除可能存在的旧计时器
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
|
||||
// 设置新的计时器
|
||||
countdownTimer.value = setInterval(() => {
|
||||
agreementCountdown.value--
|
||||
|
||||
if (agreementCountdown.value <= 0) {
|
||||
// 倒计时结束,允许确认
|
||||
clearInterval(countdownTimer.value)
|
||||
canConfirmAgreement.value = true
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
// 保存 Promise 的 resolve 函数,以便在确认或取消时使用
|
||||
window._resolveAgreementPromise = resolvePromise
|
||||
}
|
||||
|
||||
// 确认协议
|
||||
function confirmAgreement() {
|
||||
// 只有在倒计时结束后才能确认
|
||||
if (!canConfirmAgreement.value) return
|
||||
|
||||
// 关闭弹窗
|
||||
agreementPopupVisible.value = false
|
||||
|
||||
// 清除计时器
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
|
||||
// 标记已同意协议
|
||||
agreementChecked.value = true
|
||||
|
||||
// 解析 Promise
|
||||
if (window._resolveAgreementPromise) {
|
||||
window._resolveAgreementPromise(true)
|
||||
window._resolveAgreementPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭协议弹窗
|
||||
function closeAgreementPopup() {
|
||||
// 关闭弹窗
|
||||
agreementPopupVisible.value = false
|
||||
|
||||
// 清除计时器
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
|
||||
// 解析 Promise 为 false(未同意)
|
||||
if (window._resolveAgreementPromise) {
|
||||
window._resolveAgreementPromise(false)
|
||||
window._resolveAgreementPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
async function onSubmit() {
|
||||
const formRes = await validaForm()
|
||||
@@ -287,12 +511,177 @@ async function removeUser(index) {
|
||||
Toast('至少需要一个报名人员')
|
||||
return
|
||||
}
|
||||
formData.value.userList.splice(index, 1)
|
||||
|
||||
Dialog.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除该报名人员吗?',
|
||||
confirmBtn: '确定',
|
||||
cancelBtn: '取消',
|
||||
onConfirm: () => {
|
||||
formData.value.userList.splice(index, 1)
|
||||
// 如果删除的是当前选中的选项卡,则选中第一个选项卡
|
||||
if (index === activeTabIndex.value) {
|
||||
activeTabIndex.value = 0
|
||||
}
|
||||
// 如果删除的是前面的选项卡,则当前选中的选项卡索引减1
|
||||
else if (index < activeTabIndex.value) {
|
||||
activeTabIndex.value--
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 显示协议弹窗
|
||||
function showAgreementPopup() {
|
||||
// 重置倒计时
|
||||
agreementCountdown.value = 5
|
||||
canConfirmAgreement.value = false
|
||||
|
||||
// 显示弹窗
|
||||
agreementPopupVisible.value = true
|
||||
|
||||
// 启动倒计时
|
||||
startAgreementCountdown(() => {
|
||||
// 这里不需要处理,因为协议弹窗会自动显示
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tabs-container {
|
||||
flex-shrink: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tabs-header {
|
||||
padding: 10px 16px 5px;
|
||||
}
|
||||
|
||||
.tabs-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.person-counter {
|
||||
background-color: #f45a5a;
|
||||
border-radius: 12px;
|
||||
padding: 2px 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.counter-text {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-add-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #f45a5a, #ff7676);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 6px rgba(244, 90, 90, 0.25);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.header-add-btn:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
.tabs-scroll-view {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.tabs-scroll-view::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
padding: 10px 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 90px;
|
||||
height: 35px;
|
||||
padding: 0 14px;
|
||||
margin: 0 5px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 19px;
|
||||
transition: all 0.25s ease;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
margin-right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #fff;
|
||||
background-color: #f45a5a;
|
||||
box-shadow: 0 3px 8px rgba(244, 90, 90, 0.25);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tabs-indicator {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(244, 90, 90, 0.3), transparent);
|
||||
}
|
||||
|
||||
.act-info {
|
||||
position: fixed;
|
||||
top: 44px;
|
||||
@@ -340,6 +729,7 @@ async function removeUser(index) {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 130px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.form-block {
|
||||
@@ -347,6 +737,7 @@ async function removeUser(index) {
|
||||
background: white;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
|
||||
.form-block-title {
|
||||
font-size: 16px;
|
||||
@@ -355,83 +746,45 @@ async function removeUser(index) {
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.t-icon {
|
||||
margin-right: 5px;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card {
|
||||
background: white;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
border-left: 3px solid #e74c3c;
|
||||
padding-left: 12px;
|
||||
|
||||
.user-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.user-card-header-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.remove {
|
||||
color: #e74c3c;
|
||||
//font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
.form-subtitle {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-left: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.float-add-btn {
|
||||
position: fixed;
|
||||
right: 32rpx;
|
||||
bottom: 160rpx;
|
||||
width: 110rpx;
|
||||
height: 110rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #e74c3c;
|
||||
.form-fields-container {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0.6; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
transition: transform 0.2s;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.float-add-btn:active {
|
||||
transform: scale(0.95);
|
||||
.remove-user {
|
||||
padding: 5px;
|
||||
color: #f45a5a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -439,12 +792,95 @@ async function removeUser(index) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0 16px 16px;
|
||||
background-color: white;
|
||||
box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: white;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.agreement-checkbox {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.agreement-link {
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.quick-nav {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 90;
|
||||
}
|
||||
|
||||
.quick-nav-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(244, 90, 90, 0.9);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
transition: transform 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.quick-nav-btn:active {
|
||||
transform: scale(0.95);
|
||||
background-color: rgba(244, 90, 90, 1);
|
||||
}
|
||||
|
||||
/* 协议弹窗样式 */
|
||||
.agreement-popup {
|
||||
width: 85vw;
|
||||
max-width: 320px;
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.agreement-popup-header {
|
||||
padding: 20px 16px 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.agreement-popup-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.agreement-popup-content {
|
||||
padding: 20px 16px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.agreement-popup-text {
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
color: #666;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.agreement-popup-footer {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.agreement-btn {
|
||||
height: 44px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -78,12 +78,7 @@ const navigateToEdit = () => {
|
||||
|
||||
<style scoped lang="less">
|
||||
.page-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 50px;
|
||||
background-color: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 成功内容区域 */
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
<view class="title-content">
|
||||
<text class="main-title">第二届“钟声杯”直属工会趣味运动会</text>
|
||||
<text class="main-title">第二届"钟声杯"直属工会趣味运动会</text>
|
||||
<view class="sub-title-container">
|
||||
<text class="sub-title">欢迎使用</text>
|
||||
</view>
|
||||
@@ -128,6 +128,13 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="footer-info">
|
||||
<view class="footer-item">主办单位:浙江省教育工会</view>
|
||||
<view class="footer-item">承办单位:杭州医学院工会</view>
|
||||
<view class="footer-item">技术支持:武汉地大三思科技有限公司</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -290,7 +297,7 @@ async function handleLogin() {
|
||||
Toast({message: '登录成功', theme: 'success'})
|
||||
const redirect = uni.getStorageSync("REDIRECT_URL")
|
||||
if(redirect){
|
||||
if(redirect.startsWith('/pages/index/index') || redirect.startsWith('/pages/profile/index')){
|
||||
if(redirect.startsWith('/pages/index/index') || redirect.startsWith('/pages/activity/rank/index') || redirect.startsWith('/pages/profile/index')){
|
||||
uni.switchTab({
|
||||
url: redirect
|
||||
})
|
||||
@@ -515,4 +522,17 @@ body {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部信息样式 */
|
||||
.footer-info {
|
||||
padding: 20px 30px;
|
||||
text-align: center;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
|
||||
<view class="title-content">
|
||||
<text class="main-title">第二届“钟声杯”直属工会趣味运动会</text>
|
||||
<text class="main-title">第二届"钟声杯"直属工会趣味运动会</text>
|
||||
<view class="sub-title-container">
|
||||
<text class="sub-title">用户注册</text>
|
||||
</view>
|
||||
@@ -134,6 +134,13 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="footer-info">
|
||||
<view class="footer-item">主办单位:浙江省教育工会</view>
|
||||
<view class="footer-item">承办单位:杭州医学院工会</view>
|
||||
<view class="footer-item">技术支持:武汉地大三思科技有限公司</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义单位选择弹窗 -->
|
||||
<t-popup v-model="showUnitPicker" placement="bottom">
|
||||
<view class="custom-picker">
|
||||
@@ -687,4 +694,17 @@ onMounted(() => {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 底部信息样式 */
|
||||
.footer-info {
|
||||
padding: 20px 30px;
|
||||
text-align: center;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,7 +34,8 @@ export default defineConfig({
|
||||
},*/
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://119.91.224.39:9090/',
|
||||
// target: 'http://119.91.224.39:9090/',
|
||||
target: 'http://192.168.118.1:9090/',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user