This commit is contained in:
那些花儿
2025-05-13 18:27:15 +08:00
parent 178f9b39ff
commit 7949eec70d
31 changed files with 3052 additions and 337 deletions
@@ -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());
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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> {
}
@@ -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;
}
}
@@ -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)
}
}
}
})
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"
+66 -6
View File
@@ -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;
}
}