feat:优化初版
This commit is contained in:
Generated
+1
@@ -0,0 +1 @@
|
|||||||
|
南邮工会羽毛球报名
|
||||||
@@ -52,14 +52,16 @@ public class ClientAuthServiceImpl implements ClientAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loginBody.getLoginMethod().equals("sms")) {
|
if (loginBody.getLoginMethod().equals("sms")) {
|
||||||
// 测试阶段允许已注册手机号直接登录;恢复正式登录时必须重新启用短信验证码校验。
|
validateService.checkSMSCode(loginBody.getPhone(), loginBody.getSmsCode());
|
||||||
// validateService.checkSMSCode(loginBody.getPhone(), loginBody.getSmsCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginBody.getLoginMethod().equals("password")) {
|
if (loginBody.getLoginMethod().equals("password")) {
|
||||||
|
if (Strings.isBlank(loginBody.getPassword())) {
|
||||||
|
throw new BaseException("请输入密码");
|
||||||
|
}
|
||||||
String hashedPassword = PwdUtil.getPassword(loginBody.getPassword(), user.getSalt());
|
String hashedPassword = PwdUtil.getPassword(loginBody.getPassword(), user.getSalt());
|
||||||
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||||
// throw new BaseException("密码不正确");
|
throw new BaseException("密码不正确");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -59,6 +59,7 @@ public class ClientActivityController {
|
|||||||
t1.signStartTime,
|
t1.signStartTime,
|
||||||
t1.signEndTime,
|
t1.signEndTime,
|
||||||
t1.coHostUnit,
|
t1.coHostUnit,
|
||||||
|
t1.hostUnit,
|
||||||
t1.cover
|
t1.cover
|
||||||
FROM
|
FROM
|
||||||
activity t1
|
activity t1
|
||||||
|
|||||||
-1
@@ -66,7 +66,6 @@ public class ClientGroupController {
|
|||||||
LEFT JOIN act_user t2 ON t1.id = t2.activityId
|
LEFT JOIN act_user t2 ON t1.id = t2.activityId
|
||||||
WHERE
|
WHERE
|
||||||
t2.userId = @userId
|
t2.userId = @userId
|
||||||
AND t2.isSignIn =1
|
|
||||||
""").setParam("userId", StpClientUtil.STP.getLoginIdAsLong());
|
""").setParam("userId", StpClientUtil.STP.getLoginIdAsLong());
|
||||||
List<NutMap> list = clientActivityService.listMap(sql);
|
List<NutMap> list = clientActivityService.listMap(sql);
|
||||||
return Result.success(list);
|
return Result.success(list);
|
||||||
|
|||||||
+22
-1
@@ -1,5 +1,6 @@
|
|||||||
package com.budwk.app.client.module.activity.service.impl;
|
package com.budwk.app.client.module.activity.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdcardUtil;
|
||||||
import com.budwk.app.base.exception.BaseException;
|
import com.budwk.app.base.exception.BaseException;
|
||||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||||
import com.budwk.app.client.auth.models.Client_user;
|
import com.budwk.app.client.auth.models.Client_user;
|
||||||
@@ -144,6 +145,11 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
oldUser = oldUserByPhone.get(user.getPhone());
|
oldUser = oldUserByPhone.get(user.getPhone());
|
||||||
}
|
}
|
||||||
mergeHiddenFields(user, oldUser, requiredMap);
|
mergeHiddenFields(user, oldUser, requiredMap);
|
||||||
|
// 无手机号人员再次提交时沿用本人旧记录关联的账号,不信任客户端传入的账号ID。
|
||||||
|
user.setUserId(oldUser == null ? null : oldUser.getUserId());
|
||||||
|
if (!conditionMatches(user, RegistrationFieldDefinition.PHONE)) {
|
||||||
|
user.setPhone(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiredMap.containsKey("personType") || requiredMap.containsKey("projectType")) {
|
if (requiredMap.containsKey("personType") || requiredMap.containsKey("projectType")) {
|
||||||
@@ -175,6 +181,7 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
|
|
||||||
// 检查是否有重复的手机号
|
// 检查是否有重复的手机号
|
||||||
boolean hasDuplicatePhone = userList.stream()
|
boolean hasDuplicatePhone = userList.stream()
|
||||||
|
.filter(item -> Strings.isNotBlank(item.getPhone()))
|
||||||
.collect(Collectors.groupingBy(ActUser::getPhone, Collectors.counting()))
|
.collect(Collectors.groupingBy(ActUser::getPhone, Collectors.counting()))
|
||||||
.values().stream()
|
.values().stream()
|
||||||
.anyMatch(count -> count > 1);
|
.anyMatch(count -> count > 1);
|
||||||
@@ -191,7 +198,10 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
|
|
||||||
// 首先检查报名人员是否已经在用户表中
|
// 首先检查报名人员是否已经在用户表中
|
||||||
for (ActUser user : userList) {
|
for (ActUser user : userList) {
|
||||||
Client_user clientUser = dao().fetch(Client_user.class, Cnd.where(Client_user::getPhone, "=", user.getPhone()));
|
// 未兼任教练或领队的运动员允许不填手机号,不能用空手机号查找并错误关联其他人的账号。
|
||||||
|
Client_user clientUser = Strings.isNotBlank(user.getPhone())
|
||||||
|
? dao().fetch(Client_user.class, Cnd.where(Client_user::getPhone, "=", user.getPhone()))
|
||||||
|
: (user.getUserId() == null ? null : dao().fetch(Client_user.class, user.getUserId()));
|
||||||
if (clientUser == null) {
|
if (clientUser == null) {
|
||||||
// 创建用户
|
// 创建用户
|
||||||
clientUser = new Client_user();
|
clientUser = new Client_user();
|
||||||
@@ -259,6 +269,11 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
if (Boolean.TRUE.equals(entry.getValue()) && isEmptyValue(value)) {
|
if (Boolean.TRUE.equals(entry.getValue()) && isEmptyValue(value)) {
|
||||||
throw new BaseException("请填写" + definition.getFieldLabel());
|
throw new BaseException("请填写" + definition.getFieldLabel());
|
||||||
}
|
}
|
||||||
|
// 服务端复核身份证,防止绕过页面校验提交无效号码。
|
||||||
|
if ("idCard".equals(entry.getKey()) && !isEmptyValue(value)
|
||||||
|
&& (String.valueOf(value).length() != 18 || !IdcardUtil.isValidCard18(String.valueOf(value)))) {
|
||||||
|
throw new BaseException("请输入正确的18位身份证号码");
|
||||||
|
}
|
||||||
if (!isEmptyValue(value)
|
if (!isEmptyValue(value)
|
||||||
&& !activityRegistrationFieldService.isValidDictValue(activityId, entry.getKey(), String.valueOf(value))) {
|
&& !activityRegistrationFieldService.isValidDictValue(activityId, entry.getKey(), String.valueOf(value))) {
|
||||||
throw new BaseException(definition.getFieldLabel() + "不属于当前可用的数据字典选项");
|
throw new BaseException(definition.getFieldLabel() + "不属于当前可用的数据字典选项");
|
||||||
@@ -274,6 +289,10 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
if (Strings.isBlank(definition.getParentField())) {
|
if (Strings.isBlank(definition.getParentField())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// 人员类型为字典多选,按名称解析编码后匹配任意目标类型。
|
||||||
|
if ("personType".equals(definition.getParentField())) {
|
||||||
|
return activityRegistrationFieldService.matchesPersonTypes(user.getPersonType(), definition.getShowWhenValue());
|
||||||
|
}
|
||||||
Object parentValue = getFieldValue(user, definition.getParentField());
|
Object parentValue = getFieldValue(user, definition.getParentField());
|
||||||
return parentValue != null && definition.getShowWhenValue().equals(String.valueOf(parentValue));
|
return parentValue != null && definition.getShowWhenValue().equals(String.valueOf(parentValue));
|
||||||
}
|
}
|
||||||
@@ -292,6 +311,7 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
case "unitName" -> user.getUnitName();
|
case "unitName" -> user.getUnitName();
|
||||||
case "position" -> user.getPosition();
|
case "position" -> user.getPosition();
|
||||||
case "phone" -> user.getPhone();
|
case "phone" -> user.getPhone();
|
||||||
|
case "idCard" -> user.getIdCard();
|
||||||
case "birthday" -> user.getBirthday();
|
case "birthday" -> user.getBirthday();
|
||||||
case "isStay" -> user.getIsStay();
|
case "isStay" -> user.getIsStay();
|
||||||
case "isContacts" -> user.getIsContacts();
|
case "isContacts" -> user.getIsContacts();
|
||||||
@@ -321,6 +341,7 @@ public class ClientActivityServiceImpl extends BaseServiceImpl<Activity> impleme
|
|||||||
case "projectType" -> target.setProjectType(source == null ? null : source.getProjectType());
|
case "projectType" -> target.setProjectType(source == null ? null : source.getProjectType());
|
||||||
case "weight" -> target.setWeight(source == null ? null : source.getWeight());
|
case "weight" -> target.setWeight(source == null ? null : source.getWeight());
|
||||||
case "position" -> target.setPosition(source == null ? null : source.getPosition());
|
case "position" -> target.setPosition(source == null ? null : source.getPosition());
|
||||||
|
case "idCard" -> target.setIdCard(source == null ? null : source.getIdCard());
|
||||||
case "birthday" -> target.setBirthday(source == null ? null : source.getBirthday());
|
case "birthday" -> target.setBirthday(source == null ? null : source.getBirthday());
|
||||||
case "isStay" -> target.setIsStay(source == null ? null : source.getIsStay());
|
case "isStay" -> target.setIsStay(source == null ? null : source.getIsStay());
|
||||||
case "isContacts" -> target.setIsContacts(source == null ? false : source.getIsContacts());
|
case "isContacts" -> target.setIsContacts(source == null ? false : source.getIsContacts());
|
||||||
|
|||||||
+14
@@ -187,6 +187,20 @@ public class ActGroupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按单位对活动全部有效报名人员进行分组。
|
||||||
|
*
|
||||||
|
* @param activityId 活动ID
|
||||||
|
* @return 分组成功结果;数据不完整时由业务异常返回错误提示
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@SaCheckPermission("activity.group")
|
||||||
|
@SLog(tag = "活动分组", msg = "按单位分组")
|
||||||
|
public Result groupByUnit(Long activityId) {
|
||||||
|
actGroupService.groupByUnit(activityId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@SaCheckPermission("activity.group")
|
@SaCheckPermission("activity.group")
|
||||||
public Result queryUnGroupUser(String activityId) {
|
public Result queryUnGroupUser(String activityId) {
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -255,10 +258,53 @@ public class ActUserController {
|
|||||||
|
|
||||||
Sql sql = actUserService.getSignUpSql(pageForm);
|
Sql sql = actUserService.getSignUpSql(pageForm);
|
||||||
List<NutMap> nutMaps = actUserService.listMap(sql);
|
List<NutMap> nutMaps = actUserService.listMap(sql);
|
||||||
|
// 历史表列名为ProjectType,统一导出键名供项目排序及名称转换使用。
|
||||||
|
for (NutMap row : nutMaps) {
|
||||||
|
if (row.getString("projectType", "").isEmpty()) {
|
||||||
|
row.put("projectType", row.getString("ProjectType", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 导出按单位、领队、教练、项目、男女分组;人员类型按字典名称解析,兼任类型也参与排序。
|
||||||
|
Map<String, Integer> personTypeOrder = nutMaps.stream()
|
||||||
|
.map(row -> row.getString("personType", "")).distinct()
|
||||||
|
.collect(Collectors.toMap(type -> type, type ->
|
||||||
|
activityRegistrationFieldService.matchesPersonTypes(type, "领队") ? 0
|
||||||
|
: activityRegistrationFieldService.matchesPersonTypes(type, "教练") ? 1 : 2));
|
||||||
|
nutMaps.sort(Comparator.comparing((NutMap row) -> row.getString("unitName", ""))
|
||||||
|
.thenComparing(row -> row.getString("unitId", ""))
|
||||||
|
.thenComparingInt(row -> personTypeOrder.get(row.getString("personType", "")))
|
||||||
|
.thenComparing(row -> Arrays.stream(row.getString("projectType", "").split(","))
|
||||||
|
.map(String::trim).filter(value -> !value.isEmpty()).distinct().sorted()
|
||||||
|
.collect(Collectors.joining(",")))
|
||||||
|
.thenComparingInt(row -> "男".equals(row.getString("sex")) ? 0
|
||||||
|
: "女".equals(row.getString("sex")) ? 1 : 2));
|
||||||
|
// 多选字典编码转换为名称;保留无法匹配的旧编码,避免历史报名信息在导出时丢失。
|
||||||
|
for (NutMap field : activityRegistrationFieldService.getFieldConfig(pageForm.getActivityId(), false, true)) {
|
||||||
|
String fieldKey = field.getString("fieldKey");
|
||||||
|
if (!"personType".equals(fieldKey) && !"projectType".equals(fieldKey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Map<String, String> optionNames = new HashMap<>();
|
||||||
|
if (field.get("options") instanceof List<?> options) {
|
||||||
|
for (Object option : options) {
|
||||||
|
if (option instanceof Map<?, ?> optionMap) {
|
||||||
|
optionNames.put(String.valueOf(optionMap.get("value")), String.valueOf(optionMap.get("label")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (NutMap row : nutMaps) {
|
||||||
|
row.put(fieldKey + "Desc", Arrays.stream(row.getString(fieldKey, "").split(","))
|
||||||
|
.map(String::trim).filter(value -> !value.isEmpty())
|
||||||
|
.map(value -> optionNames.getOrDefault(value, value))
|
||||||
|
.collect(Collectors.joining("、")));
|
||||||
|
}
|
||||||
|
}
|
||||||
List<ExcelExportEntity> entityList = new ArrayList<>() {{
|
List<ExcelExportEntity> entityList = new ArrayList<>() {{
|
||||||
// add(new ExcelExportEntity("报名号", "actUserNo", 20));
|
// add(new ExcelExportEntity("报名号", "actUserNo", 20));
|
||||||
add(new ExcelExportEntity("姓名", "userName", 20));
|
add(new ExcelExportEntity("姓名", "userName", 20));
|
||||||
add(new ExcelExportEntity("性别", "sex", 20));
|
add(new ExcelExportEntity("性别", "sex", 20));
|
||||||
|
add(new ExcelExportEntity("人员类型", "personTypeDesc", 20));
|
||||||
|
add(new ExcelExportEntity("项目类型", "projectTypeDesc", 30));
|
||||||
add(new ExcelExportEntity("身份证号码", "idCard", 20));
|
add(new ExcelExportEntity("身份证号码", "idCard", 20));
|
||||||
add(new ExcelExportEntity("联系方式", "phone", 20));
|
add(new ExcelExportEntity("联系方式", "phone", 20));
|
||||||
add(new ExcelExportEntity("职务", "position", 20));
|
add(new ExcelExportEntity("职务", "position", 20));
|
||||||
|
|||||||
+4
-3
@@ -14,11 +14,11 @@ public enum RegistrationFieldDefinition {
|
|||||||
USER_NAME("userName", "姓名", "input", true, true, true, null, null, null),
|
USER_NAME("userName", "姓名", "input", true, true, true, null, null, null),
|
||||||
SEX("sex", "性别", "radio", true, true, false, null, null, null),
|
SEX("sex", "性别", "radio", true, true, false, null, null, null),
|
||||||
PERSON_TYPE("personType", "人员类型", "dict-checkbox", false, false, false, null, null, "person_type"),
|
PERSON_TYPE("personType", "人员类型", "dict-checkbox", false, false, false, null, null, "person_type"),
|
||||||
PROJECT_TYPE("projectType", "项目类型", "dict-checkbox", false, false, false, null, null, "project_type"),
|
PROJECT_TYPE("projectType", "项目类型", "dict-checkbox", false, true, false, "personType", "运动员", "project_type"),
|
||||||
WEIGHT("weight", "体重(KG)", "number", true, true, false, null, null, null),
|
WEIGHT("weight", "体重(KG)", "number", true, true, false, null, null, null),
|
||||||
UNIT_NAME("unitName", "单位名称", "readonly", true, true, true, null, null, null),
|
UNIT_NAME("unitName", "单位名称", "readonly", true, true, true, null, null, null),
|
||||||
POSITION("position", "职务", "input", true, true, false, null, null, null),
|
POSITION("position", "职务", "input", true, true, false, null, null, null),
|
||||||
PHONE("phone", "联系电话", "tel", true, true, true, null, null, null),
|
PHONE("phone", "联系电话", "tel", true, true, true, "personType", "教练,领队", null),
|
||||||
BIRTHDAY("birthday", "出生日期", "date", false, false, false, null, null, null),
|
BIRTHDAY("birthday", "出生日期", "date", false, false, false, null, null, null),
|
||||||
IS_STAY("isStay", "是否住宿", "boolean-radio", true, true, false, null, null, null),
|
IS_STAY("isStay", "是否住宿", "boolean-radio", true, true, false, null, null, null),
|
||||||
IS_CONTACTS("isContacts", "是否联络员", "boolean-radio", true, true, false, null, null, null),
|
IS_CONTACTS("isContacts", "是否联络员", "boolean-radio", true, true, false, null, null, null),
|
||||||
@@ -29,7 +29,8 @@ public enum RegistrationFieldDefinition {
|
|||||||
IS_SHOW_SKILL("isShowSkill", "是否展示才艺", "boolean-radio", false, false, false, null, null, null),
|
IS_SHOW_SKILL("isShowSkill", "是否展示才艺", "boolean-radio", false, false, false, null, null, null),
|
||||||
SKILL_NAME("skillName", "才艺名称", "input", false, false, false, "isShowSkill", "true", null),
|
SKILL_NAME("skillName", "才艺名称", "input", false, false, false, "isShowSkill", "true", null),
|
||||||
EQUIP("equip", "所需器材", "input", false, false, false, "isShowSkill", "true", null),
|
EQUIP("equip", "所需器材", "input", false, false, false, "isShowSkill", "true", null),
|
||||||
IS_PARTY("isParty", "参加茶叙会", "boolean-radio", true, true, false, null, null, null);
|
IS_PARTY("isParty", "参加茶叙会", "boolean-radio", true, true, false, null, null, null),
|
||||||
|
ID_CARD("idCard", "身份证", "input", true, true, true, "personType", "运动员", null);
|
||||||
|
|
||||||
private final String fieldKey;
|
private final String fieldKey;
|
||||||
private final String fieldLabel;
|
private final String fieldLabel;
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ public class ActUser extends BaseModel {
|
|||||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||||
private String position;
|
private String position;
|
||||||
|
|
||||||
// @Column
|
@Column
|
||||||
// @Comment("身份证号码")
|
@Comment("身份证号码")
|
||||||
// @ColDefine(type = ColType.VARCHAR, width = 20)
|
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||||
// private String idCard;
|
private String idCard;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Comment("出生日期")
|
@Comment("出生日期")
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ public interface ActGroupService extends BaseService<ActGroup> {
|
|||||||
*/
|
*/
|
||||||
void groupSetting(ActGroupVo groupVo);
|
void groupSetting(ActGroupVo groupVo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按单位重建活动全部有效报名人员的分组,同一单位只生成一个组。
|
||||||
|
*
|
||||||
|
* @param activityId 活动ID;不限制人员签到状态
|
||||||
|
* @throws com.budwk.app.base.exception.BaseException 活动或报名数据不满足分组条件时抛出
|
||||||
|
*/
|
||||||
|
void groupByUnit(Long activityId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置未分组人员
|
* 设置未分组人员
|
||||||
* @param unGroupUserVo
|
* @param unGroupUserVo
|
||||||
|
|||||||
+9
@@ -54,6 +54,15 @@ public interface ActivityRegistrationFieldService extends BaseService<ActRegistr
|
|||||||
*/
|
*/
|
||||||
boolean isValidPersonTypeCombination(String value);
|
boolean isValidPersonTypeCombination(String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典名称判断多选人员类型是否满足字段联动条件。
|
||||||
|
*
|
||||||
|
* @param personType 逗号分隔的人员类型字典编码
|
||||||
|
* @param labels 逗号分隔的人员类型名称,匹配任意一项即满足条件
|
||||||
|
* @return 已选人员类型中包含目标类型时返回true
|
||||||
|
*/
|
||||||
|
boolean matchesPersonTypes(String personType, String labels);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取同一活动、同一单位的人员类型人数限制状态。
|
* 获取同一活动、同一单位的人员类型人数限制状态。
|
||||||
*
|
*
|
||||||
|
|||||||
+58
@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
|||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.IdcardUtil;
|
import cn.hutool.core.util.IdcardUtil;
|
||||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||||
|
import com.budwk.app.base.exception.BaseException;
|
||||||
import com.budwk.app.module.activity.models.*;
|
import com.budwk.app.module.activity.models.*;
|
||||||
import com.budwk.app.module.activity.service.ActGroupService;
|
import com.budwk.app.module.activity.service.ActGroupService;
|
||||||
import com.budwk.app.module.activity.vo.ActGroupVo;
|
import com.budwk.app.module.activity.vo.ActGroupVo;
|
||||||
@@ -16,6 +17,7 @@ import org.nutz.dao.sql.Sql;
|
|||||||
import org.nutz.ioc.aop.Aop;
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -133,6 +135,62 @@ public class ActGroupServiceImpl extends BaseServiceImpl<ActGroup> implements Ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将活动全部有效报名人员按单位ID重建为一单位一组,并初始化各项目得分记录。
|
||||||
|
*
|
||||||
|
* @param activityId 活动ID
|
||||||
|
* @throws BaseException 活动不存在、无有效报名人员或人员缺少单位、关联账号时抛出
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void groupByUnit(Long activityId) {
|
||||||
|
if (activityId == null || dao().fetch(Activity.class,
|
||||||
|
Cnd.where(Activity::getId, "=", activityId).and(Activity::getDelFlag, "=", false)) == null) {
|
||||||
|
throw new BaseException("请选择有效活动");
|
||||||
|
}
|
||||||
|
List<ActUser> users = dao().query(ActUser.class,
|
||||||
|
Cnd.where(ActUser::getActivityId, "=", activityId).and(ActUser::getDelFlag, "=", false)
|
||||||
|
.asc(ActUser::getUnitName).asc(ActUser::getId));
|
||||||
|
if (users.isEmpty()) {
|
||||||
|
throw new BaseException("当前活动暂无有效报名人员");
|
||||||
|
}
|
||||||
|
Map<String, List<ActUser>> unitUsers = new LinkedHashMap<>();
|
||||||
|
for (ActUser user : users) {
|
||||||
|
if (Strings.isBlank(user.getUnitId()) || user.getUserId() == null) {
|
||||||
|
throw new BaseException("报名人员【" + user.getUserName() + "】缺少所属单位或关联账号,请补齐后再分组");
|
||||||
|
}
|
||||||
|
unitUsers.computeIfAbsent(user.getUnitId(), key -> new ArrayList<>()).add(user);
|
||||||
|
}
|
||||||
|
List<ActivityItem> items = dao().query(ActivityItem.class, Cnd.where(ActivityItem::getActivityId, "=", activityId));
|
||||||
|
// 完成数据检查后再覆盖旧分组,整个重建过程在同一事务内完成。
|
||||||
|
dao().clear(ActItemGroupScore.class, Cnd.where(ActItemGroupScore::getActivityId, "=", activityId));
|
||||||
|
dao().clear(ActGroupUser.class, Cnd.where(ActGroupUser::getActivityId, "=", activityId));
|
||||||
|
dao().clear(ActUnGroupUser.class, Cnd.where(ActUnGroupUser::getActivityId, "=", activityId));
|
||||||
|
dao().clear(ActGroup.class, Cnd.where(ActGroup::getActivityId, "=", activityId));
|
||||||
|
int sort = 1;
|
||||||
|
for (List<ActUser> members : unitUsers.values()) {
|
||||||
|
ActGroup group = new ActGroup();
|
||||||
|
group.setActivityId(activityId);
|
||||||
|
group.setGroupName(Strings.sBlank(members.get(0).getUnitName(), members.get(0).getUnitId()));
|
||||||
|
group.setSort(sort++);
|
||||||
|
group.setIsOpen(false);
|
||||||
|
group.setGroupUsers(members.stream().map(ActUser::getUserId).distinct().map(userId -> {
|
||||||
|
ActGroupUser member = new ActGroupUser();
|
||||||
|
member.setActivityId(activityId);
|
||||||
|
member.setUserId(userId);
|
||||||
|
return member;
|
||||||
|
}).collect(Collectors.toList()));
|
||||||
|
dao().insertWith(group, "groupUsers");
|
||||||
|
for (ActivityItem item : items) {
|
||||||
|
ActItemGroupScore score = new ActItemGroupScore();
|
||||||
|
score.setActivityId(activityId);
|
||||||
|
score.setItemId(item.getId());
|
||||||
|
score.setGroupId(group.getId());
|
||||||
|
dao().insert(score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void unGroupUserSetting(ActUnGroupUserVo unGroupUserVo) {
|
public void unGroupUserSetting(ActUnGroupUserVo unGroupUserVo) {
|
||||||
|
|||||||
+71
-11
@@ -51,12 +51,41 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
.collect(Collectors.toMap(ActRegistrationField::getFieldKey, item -> item, (left, right) -> left,
|
.collect(Collectors.toMap(ActRegistrationField::getFieldKey, item -> item, (left, right) -> left,
|
||||||
LinkedHashMap::new));
|
LinkedHashMap::new));
|
||||||
boolean useDefault = storedFields.isEmpty();
|
boolean useDefault = storedFields.isEmpty();
|
||||||
|
ActRegistrationField identityField = storedMap.get("idCard");
|
||||||
|
boolean legacyIdentitySort = identityField == null || identityField.getSort() == null
|
||||||
|
|| identityField.getSort() == 0;
|
||||||
|
// 兼容身份证原来排在0的活动配置,仅在旧顺序下为第6位腾出位置,避免重复顺延。
|
||||||
|
if (legacyIdentitySort) {
|
||||||
|
for (ActRegistrationField field : storedMap.values()) {
|
||||||
|
if (field.getSort() != null && field.getSort() >= 6) {
|
||||||
|
field.setSort(field.getSort() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (identityField != null) {
|
||||||
|
identityField.setSort(6);
|
||||||
|
}
|
||||||
|
if (storedMap.containsKey("phone")) {
|
||||||
|
storedMap.get("phone").setSort(7);
|
||||||
|
}
|
||||||
|
if (storedMap.containsKey("position")) {
|
||||||
|
storedMap.get("position").setSort(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 旧活动也补齐新增固定字段及其父字段,使人员类型联动无需重新保存即可生效。
|
||||||
|
for (RegistrationFieldDefinition definition : RegistrationFieldDefinition.values()) {
|
||||||
|
if (definition.isLocked() || useDefault && definition.isDefaultSelected()) {
|
||||||
|
storedMap.putIfAbsent(definition.getFieldKey(), buildDefaultField(definition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String fieldKey : new LinkedHashSet<>(storedMap.keySet())) {
|
||||||
|
addParentField(storedMap, RegistrationFieldDefinition.fromKey(fieldKey));
|
||||||
|
}
|
||||||
List<NutMap> result = new ArrayList<>();
|
List<NutMap> result = new ArrayList<>();
|
||||||
int defaultSort = 1;
|
int defaultSort = 1;
|
||||||
for (RegistrationFieldDefinition definition : RegistrationFieldDefinition.values()) {
|
for (RegistrationFieldDefinition definition : RegistrationFieldDefinition.values()) {
|
||||||
boolean projectTypeField = RegistrationFieldDefinition.PROJECT_TYPE == definition;
|
boolean projectTypeField = RegistrationFieldDefinition.PROJECT_TYPE == definition;
|
||||||
ActRegistrationField stored = storedMap.get(definition.getFieldKey());
|
ActRegistrationField stored = storedMap.get(definition.getFieldKey());
|
||||||
boolean selected = useDefault ? definition.isDefaultSelected() : stored != null;
|
boolean selected = stored != null;
|
||||||
// 多项目、无项目或唯一项目未配置有效类型时,历史配置也不再向客户端开放。
|
// 多项目、无项目或唯一项目未配置有效类型时,历史配置也不再向客户端开放。
|
||||||
if (projectTypeField && !projectTypeContext.configurable) {
|
if (projectTypeField && !projectTypeContext.configurable) {
|
||||||
selected = false;
|
selected = false;
|
||||||
@@ -66,13 +95,14 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boolean required = stored == null ? definition.isDefaultRequired() : Boolean.TRUE.equals(stored.getRequired());
|
boolean required = stored == null ? definition.isDefaultRequired() : Boolean.TRUE.equals(stored.getRequired());
|
||||||
int sort = stored == null || stored.getSort() == null ? defaultSort : stored.getSort();
|
int sort = stored == null || stored.getSort() == null
|
||||||
|
? buildDefaultField(definition).getSort() : stored.getSort();
|
||||||
NutMap item = NutMap.NEW()
|
NutMap item = NutMap.NEW()
|
||||||
.addv("fieldKey", definition.getFieldKey())
|
.addv("fieldKey", definition.getFieldKey())
|
||||||
.addv("fieldLabel", definition.getFieldLabel())
|
.addv("fieldLabel", definition.getFieldLabel())
|
||||||
.addv("componentType", definition.getComponentType())
|
.addv("componentType", definition.getComponentType())
|
||||||
.addv("selected", selected)
|
.addv("selected", selected)
|
||||||
.addv("required", definition.isLocked() || required)
|
.addv("required", definition.isLocked() || projectTypeField || required)
|
||||||
.addv("locked", definition.isLocked())
|
.addv("locked", definition.isLocked())
|
||||||
.addv("sort", sort)
|
.addv("sort", sort)
|
||||||
.addv("parentField", definition.getParentField())
|
.addv("parentField", definition.getParentField())
|
||||||
@@ -130,16 +160,13 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
RegistrationFieldDefinition definition = RegistrationFieldDefinition.fromKey(field.getFieldKey());
|
RegistrationFieldDefinition definition = RegistrationFieldDefinition.fromKey(field.getFieldKey());
|
||||||
field.setId(null);
|
field.setId(null);
|
||||||
field.setActivityId(activityId);
|
field.setActivityId(activityId);
|
||||||
field.setRequired(definition.isLocked() || Boolean.TRUE.equals(field.getRequired()));
|
field.setRequired(definition.isLocked() || definition == RegistrationFieldDefinition.PROJECT_TYPE || Boolean.TRUE.equals(field.getRequired()));
|
||||||
field.setSort(field.getSort() == null ? defaultSort : field.getSort());
|
field.setSort(field.getSort() == null ? buildDefaultField(definition).getSort() : field.getSort());
|
||||||
saveList.add(field);
|
saveList.add(field);
|
||||||
defaultSort++;
|
defaultSort++;
|
||||||
}
|
}
|
||||||
saveList.sort(Comparator.comparing(ActRegistrationField::getSort));
|
saveList.sort(Comparator.comparing(ActRegistrationField::getSort));
|
||||||
// 允许管理端用数字快速调整顺序;保存时归一化为连续序号,避免重复排序值导致展示不稳定。
|
// 保留配置序号,避免未启用字段被跳过后将身份证的第6位重新编号。
|
||||||
for (int index = 0; index < saveList.size(); index++) {
|
|
||||||
saveList.get(index).setSort(index + 1);
|
|
||||||
}
|
|
||||||
dao().insert(saveList);
|
dao().insert(saveList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +194,22 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
return splitMultiValue(value).stream().allMatch(enabledValues::contains);
|
return splitMultiValue(value).stream().allMatch(enabledValues::contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典名称判断多选人员类型是否满足字段联动条件。
|
||||||
|
*
|
||||||
|
* @param personType 逗号分隔的人员类型字典编码
|
||||||
|
* @param labels 逗号分隔的人员类型名称,匹配任意一项即满足条件
|
||||||
|
* @return 已选人员类型中包含目标类型时返回true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matchesPersonTypes(String personType, String labels) {
|
||||||
|
List<String> selected = splitMultiValue(personType);
|
||||||
|
List<String> expectedLabels = splitMultiValue(labels);
|
||||||
|
return getEnabledDictOptions(RegistrationFieldDefinition.PERSON_TYPE.getDictCode()).stream()
|
||||||
|
.anyMatch(option -> expectedLabels.contains(option.getString("label"))
|
||||||
|
&& selected.contains(option.getString("value")));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidPersonTypeCombination(String value) {
|
public boolean isValidPersonTypeCombination(String value) {
|
||||||
if (Strings.isBlank(value)) {
|
if (Strings.isBlank(value)) {
|
||||||
@@ -366,7 +409,17 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mixedMaleCount != mixedFemaleCount) {
|
if (mixedMaleCount != mixedFemaleCount) {
|
||||||
throw new BaseException("混合双打须按一男一女配对,请按规则重新选择");
|
String currentSelection = "当前已选择" + mixedMaleCount + "名男选手、" + mixedFemaleCount + "名女选手";
|
||||||
|
if (mixedMaleCount == 2 && mixedFemaleCount == 0) {
|
||||||
|
currentSelection = "当前两名选手均为男选手,未选择女选手";
|
||||||
|
} else if (mixedMaleCount == 0 && mixedFemaleCount == 2) {
|
||||||
|
currentSelection = "当前两名选手均为女选手,未选择男选手";
|
||||||
|
}
|
||||||
|
// 差额按当前全部混双报名人数计算,明确未能配对的性别和人数。
|
||||||
|
String missingSex = mixedMaleCount > mixedFemaleCount ? "女" : "男";
|
||||||
|
int missingCount = Math.abs(mixedMaleCount - mixedFemaleCount);
|
||||||
|
throw new BaseException("混合双打:" + currentSelection + ",按现有人数配对还缺少"
|
||||||
|
+ missingCount + "名" + missingSex + "选手。请调整报名选择,确保每对为一男一女");
|
||||||
}
|
}
|
||||||
validateProjectTypeRegistrationLimits(activityId, users, options);
|
validateProjectTypeRegistrationLimits(activityId, users, options);
|
||||||
}
|
}
|
||||||
@@ -541,7 +594,14 @@ public class ActivityRegistrationFieldServiceImpl extends BaseServiceImpl<ActReg
|
|||||||
ActRegistrationField field = new ActRegistrationField();
|
ActRegistrationField field = new ActRegistrationField();
|
||||||
field.setFieldKey(definition.getFieldKey());
|
field.setFieldKey(definition.getFieldKey());
|
||||||
field.setRequired(definition.isDefaultRequired());
|
field.setRequired(definition.isDefaultRequired());
|
||||||
field.setSort(definition.ordinal() + 1);
|
field.setSort(switch (definition) {
|
||||||
|
case UNIT_NAME -> 5;
|
||||||
|
case ID_CARD -> 6;
|
||||||
|
case PHONE -> 7;
|
||||||
|
case POSITION -> 8;
|
||||||
|
default -> definition.ordinal() >= RegistrationFieldDefinition.BIRTHDAY.ordinal()
|
||||||
|
? definition.ordinal() + 2 : definition.ordinal() + 1;
|
||||||
|
});
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class SysUserController {
|
|||||||
try {
|
try {
|
||||||
Sys_user user = sysUserService.fetch(id);
|
Sys_user user = sysUserService.fetch(id);
|
||||||
String salt = R.UU32();
|
String salt = R.UU32();
|
||||||
String pwd = R.captchaNumber(6);
|
String pwd = "wdxxmm@123";
|
||||||
String hashedPasswordBase64 = PwdUtil.getPassword(pwd, salt);
|
String hashedPasswordBase64 = PwdUtil.getPassword(pwd, salt);
|
||||||
sysUserService.update(Chain.make("salt", salt).add("password", hashedPasswordBase64), Cnd.where("id", "=", id));
|
sysUserService.update(Chain.make("salt", salt).add("password", hashedPasswordBase64), Cnd.where("id", "=", id));
|
||||||
sysUserService.deleteCache(user.getId());
|
sysUserService.deleteCache(user.getId());
|
||||||
|
|||||||
@@ -261,9 +261,12 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
|
|||||||
if (user.isDisabled()) {
|
if (user.isDisabled()) {
|
||||||
throw new BaseException("用户被禁用");
|
throw new BaseException("用户被禁用");
|
||||||
}
|
}
|
||||||
|
if (Strings.isBlank(passowrd)) {
|
||||||
|
throw new BaseException("请输入密码");
|
||||||
|
}
|
||||||
String hashedPassword = PwdUtil.getPassword(passowrd, user.getSalt());
|
String hashedPassword = PwdUtil.getPassword(passowrd, user.getSalt());
|
||||||
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||||
// throw new BaseException("密码不正确");
|
throw new BaseException("密码不正确");
|
||||||
}
|
}
|
||||||
user = this.fetchLinks(user, "unit");
|
user = this.fetchLinks(user, "unit");
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ layout("/layouts/platform_1.html"){
|
|||||||
<el-card shadow="never" class="mt-3">
|
<el-card shadow="never" class="mt-3">
|
||||||
<el-row type="flex" style="justify-content: flex-start" class="mb-3">
|
<el-row type="flex" style="justify-content: flex-start" class="mb-3">
|
||||||
<el-button type="primary" size="small" @click="openGrouping">开始分组</el-button>
|
<el-button type="primary" size="small" @click="openGrouping">开始分组</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="unitGrouping" @click="groupByUnit">按单位分组</el-button>
|
||||||
<!-- <div v-if="unGroupUser && unGroupUser.length > 0" style="margin-left: 10px; display: inline-block;">-->
|
<!-- <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">-->
|
<!-- <el-button type="warning" size="small" style="position: relative;" @click="doUnGroupUserSetting">-->
|
||||||
<!-- 未分组人员-->
|
<!-- 未分组人员-->
|
||||||
@@ -139,7 +140,8 @@ layout("/layouts/platform_1.html"){
|
|||||||
year: new Date().getFullYear() + ""
|
year: new Date().getFullYear() + ""
|
||||||
},
|
},
|
||||||
activityList: [],
|
activityList: [],
|
||||||
unGroupUser: []
|
unGroupUser: [],
|
||||||
|
unitGrouping: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -223,6 +225,36 @@ layout("/layouts/platform_1.html"){
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 按单位重建会清空旧分组及得分,操作前明确提示并防止重复提交。
|
||||||
|
async groupByUnit() {
|
||||||
|
if (this.unitGrouping) return
|
||||||
|
if (!this.pageForm.activityId) {
|
||||||
|
this.$message.error("请选择活动")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const activityId = this.pageForm.activityId
|
||||||
|
try {
|
||||||
|
await this.$confirm('将全部有效报名人员按单位分组,同一单位生成一个组。此操作会清空该活动现有分组及项目得分,是否继续?', '按单位分组', {
|
||||||
|
type: 'warning', confirmButtonText: '确定分组', cancelButtonText: '取消'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.unitGrouping = true
|
||||||
|
try {
|
||||||
|
const resp = await $.post('/platform/activity/group/groupByUnit', {activityId})
|
||||||
|
if (resp.code === 0) {
|
||||||
|
this.$message.success('按单位分组成功')
|
||||||
|
await this.successGroup()
|
||||||
|
} else {
|
||||||
|
this.$message.error(resp.msg || '按单位分组失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('按单位分组失败')
|
||||||
|
} finally {
|
||||||
|
this.unitGrouping = false
|
||||||
|
}
|
||||||
|
},
|
||||||
// 开始分组
|
// 开始分组
|
||||||
openGrouping(){
|
openGrouping(){
|
||||||
if (!this.pageForm.activityId) {
|
if (!this.pageForm.activityId) {
|
||||||
|
|||||||
+11
-3
@@ -8,7 +8,7 @@ const registrationFieldSetting = {
|
|||||||
:before-close="handleClose">
|
:before-close="handleClose">
|
||||||
<div class="registration-field-setting" v-loading="loading">
|
<div class="registration-field-setting" v-loading="loading">
|
||||||
<el-alert
|
<el-alert
|
||||||
title="姓名、联系电话和单位名称为系统固定字段;关联字段会自动保持完整的父子关系。"
|
title="身份证、姓名、联系电话和单位名称为系统固定字段;关联字段会自动保持完整的父子关系。"
|
||||||
type="info"
|
type="info"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
show-icon>
|
show-icon>
|
||||||
@@ -46,7 +46,7 @@ const registrationFieldSetting = {
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="scope.row.required"
|
v-model="scope.row.required"
|
||||||
:disabled="!scope.row.selected || scope.row.locked || scope.row.configurable === false">
|
:disabled="!scope.row.selected || scope.row.locked || scope.row.fieldKey === 'projectType' || scope.row.configurable === false">
|
||||||
</el-switch>
|
</el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -55,7 +55,7 @@ const registrationFieldSetting = {
|
|||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="scope.row.sort"
|
v-model="scope.row.sort"
|
||||||
:disabled="!scope.row.selected || scope.row.configurable === false"
|
:disabled="!scope.row.selected || scope.row.configurable === false"
|
||||||
:min="1"
|
:min="0"
|
||||||
:max="99"
|
:max="99"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
size="small">
|
size="small">
|
||||||
@@ -121,6 +121,11 @@ const registrationFieldSetting = {
|
|||||||
this.selectParent(row)
|
this.selectParent(row)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (this.fieldList.some((item) => item.parentField === row.fieldKey && item.locked)) {
|
||||||
|
this.$set(row, "selected", true)
|
||||||
|
this.$message.warning("系统固定字段依赖此字段,不能取消显示")
|
||||||
|
return
|
||||||
|
}
|
||||||
this.unselectChildren(row.fieldKey)
|
this.unselectChildren(row.fieldKey)
|
||||||
},
|
},
|
||||||
selectParent(row) {
|
selectParent(row) {
|
||||||
@@ -223,6 +228,9 @@ const registrationFieldSetting = {
|
|||||||
return row.dictCode ? "数据字典:" + row.dictCode : "固定表单字段"
|
return row.dictCode ? "数据字典:" + row.dictCode : "固定表单字段"
|
||||||
},
|
},
|
||||||
dependencyLabel(row) {
|
dependencyLabel(row) {
|
||||||
|
if (row.parentField === "personType") {
|
||||||
|
return "当人员类型为" + row.showWhenValue.split(",").join("或者") + "时才需要填写"
|
||||||
|
}
|
||||||
if (!row.parentField) {
|
if (!row.parentField) {
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -683,13 +683,13 @@ layout("/layouts/platform_1.html"){
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 查询面板和代报名弹窗共用启用单位列表,并按单位名称排序,便于快速检索。
|
// 查询面板和代报名弹窗共用启用单位列表,按数据库序号location升序排列。
|
||||||
getUnits(){
|
getUnits(){
|
||||||
this.unitLoading = true
|
this.unitLoading = true
|
||||||
$.post('/open/common/units').then((resp) => {
|
$.post('/open/common/units').then((resp) => {
|
||||||
if (resp.code === 0) {
|
if (resp.code === 0) {
|
||||||
const units = (resp.data || []).slice().sort((left, right) => {
|
const units = (resp.data || []).slice().sort((left, right) => {
|
||||||
return String(left.name || '').localeCompare(String(right.name || ''), 'zh-CN')
|
return Number(left.location ?? 0) - Number(right.location ?? 0)
|
||||||
})
|
})
|
||||||
this.$set(this, 'units', units)
|
this.$set(this, 'units', units)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export default {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 活动详情富文本保留自身字体及继承关系,其他区域沿用原有默认字体。 */
|
||||||
|
:where(*:not(.activity-rich-content *)) {
|
||||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
"enable": false
|
"enable": false
|
||||||
},
|
},
|
||||||
"h5": {
|
"h5": {
|
||||||
"title": "趣味运动会",
|
"title": "赛事管理",
|
||||||
"router": {
|
"router": {
|
||||||
"mode": "history"
|
"mode": "history"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
},
|
},
|
||||||
"tabBar": {
|
"tabBar": {
|
||||||
"color": "#000",
|
"color": "#000",
|
||||||
"selectedColor": "#f45a5a",
|
"selectedColor": "#1989fa",
|
||||||
"borderStyle": "white",
|
"borderStyle": "white",
|
||||||
"backgroundColor": "#ffffff",
|
"backgroundColor": "#ffffff",
|
||||||
"height": "60px",
|
"height": "60px",
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
"selectedText": "\ue64d",
|
"selectedText": "\ue64d",
|
||||||
"fontSize": "17px",
|
"fontSize": "17px",
|
||||||
"color": "#333",
|
"color": "#333",
|
||||||
"selectedColor": "#f45a5a"
|
"selectedColor": "#1989fa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
"selectedText": "\ue8b3",
|
"selectedText": "\ue8b3",
|
||||||
"fontSize": "17px",
|
"fontSize": "17px",
|
||||||
"color": "#333",
|
"color": "#333",
|
||||||
"selectedColor": "#f45a5a"
|
"selectedColor": "#1989fa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
"selectedText": "\ue600",
|
"selectedText": "\ue600",
|
||||||
"fontSize": "17px",
|
"fontSize": "17px",
|
||||||
"color": "#333",
|
"color": "#333",
|
||||||
"selectedColor": "#f45a5a"
|
"selectedColor": "#1989fa"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<view class="event-info-section">
|
<view class="event-info-section">
|
||||||
<!-- 报名时间 -->
|
<!-- 报名时间 -->
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<t-icon name="calendar" size="20px"/>
|
<CalendarIcon size="20px"/>
|
||||||
<view class="info-text-container">
|
<view class="info-text-container">
|
||||||
<text class="info-text-title">报名时间</text>
|
<text class="info-text-title">报名时间</text>
|
||||||
<text class="info-text">{{ formatDate(detailData.signStartTime) }} 至
|
<text class="info-text">{{ formatDate(detailData.signStartTime) }} 至
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<!-- 活动时间 -->
|
<!-- 活动时间 -->
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<t-icon name="alarm" size="20px"/>
|
<AlarmIcon size="20px"/>
|
||||||
<view class="info-text-container">
|
<view class="info-text-container">
|
||||||
<text class="info-text-title">活动时间</text>
|
<text class="info-text-title">活动时间</text>
|
||||||
<text class="info-text">{{ formatDate(detailData.startTime) }} 至 {{
|
<text class="info-text">{{ formatDate(detailData.startTime) }} 至 {{
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
<!-- 活动地点 -->
|
<!-- 活动地点 -->
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<t-icon name="location" size="20px"/>
|
<LocationIcon size="20px"/>
|
||||||
<view class="info-text-container">
|
<view class="info-text-container">
|
||||||
<text class="info-text-title">活动地点</text>
|
<text class="info-text-title">活动地点</text>
|
||||||
<text class="info-text">{{ detailData.address }}</text>
|
<text class="info-text">{{ detailData.address }}</text>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<!-- 活动详情区域 -->
|
<!-- 活动详情区域 -->
|
||||||
<view class="activity-detail-section">
|
<view class="activity-detail-section">
|
||||||
<view class="section-title">活动详情</view>
|
<view class="section-title">活动详情</view>
|
||||||
<rich-text class="rich-content" :nodes="detailData.content"></rich-text>
|
<rich-text class="rich-content activity-rich-content" :nodes="detailData.content"></rich-text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 比赛项目 -->
|
<!-- 比赛项目 -->
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
<view class="event-item-info">
|
<view class="event-item-info">
|
||||||
<view class="event-item-section">
|
<view class="event-item-section">
|
||||||
<view class="event-item-section-title">
|
<view class="event-item-section-title">
|
||||||
<t-icon name="flag" size="16px" color="var(--td-brand-color-5)"/>
|
<FlagIcon size="16px" color="var(--td-brand-color-5)"/>
|
||||||
<text>比赛方法</text>
|
<text>比赛方法</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="event-item-section-content">{{ item.form }}</view>
|
<view class="event-item-section-content">{{ item.form }}</view>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
<view class="event-item-section">
|
<view class="event-item-section">
|
||||||
<view class="event-item-section-title">
|
<view class="event-item-section-title">
|
||||||
<t-icon name="chart-bar" size="16px" color="var(--td-brand-color-5)"/>
|
<ChartBarIcon size="16px" color="var(--td-brand-color-5)"/>
|
||||||
<text>评分标准</text>
|
<text>评分标准</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="event-item-section-content">{{ item.rule }}</view>
|
<view class="event-item-section-content">{{ item.rule }}</view>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
<view class="event-item-section">
|
<view class="event-item-section">
|
||||||
<view class="event-item-section-title">
|
<view class="event-item-section-title">
|
||||||
<t-icon name="help-circle" size="16px" color="var(--td-brand-color-5)"/>
|
<HelpCircleIcon size="16px" color="var(--td-brand-color-5)"/>
|
||||||
<text>比赛规则</text>
|
<text>比赛规则</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="event-item-section-content">{{ item.ruleForm }}</view>
|
<view class="event-item-section-content">{{ item.ruleForm }}</view>
|
||||||
@@ -125,9 +125,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
// 固定图标按文件引入,避免通用 Icon 加载整套图标。
|
||||||
|
import AlarmIcon from 'tdesign-icons-vue-next/esm/components/alarm.js';
|
||||||
|
import CalendarIcon from 'tdesign-icons-vue-next/esm/components/calendar.js';
|
||||||
|
import ChartBarIcon from 'tdesign-icons-vue-next/esm/components/chart-bar.js';
|
||||||
|
import FlagIcon from 'tdesign-icons-vue-next/esm/components/flag.js';
|
||||||
|
import HelpCircleIcon from 'tdesign-icons-vue-next/esm/components/help-circle.js';
|
||||||
|
import LocationIcon from 'tdesign-icons-vue-next/esm/components/location.js';
|
||||||
import {ref, onMounted, computed} from 'vue';
|
import {ref, onMounted, computed} from 'vue';
|
||||||
import {onLoad} from '@dcloudio/uni-app';
|
import {onLoad} from '@dcloudio/uni-app';
|
||||||
import {Toast} from 'tdesign-mobile-vue';
|
import {ToastPlugin as Toast} from 'tdesign-mobile-vue/esm/toast/index.js';
|
||||||
import {useRequest} from "@/utils/request";
|
import {useRequest} from "@/utils/request";
|
||||||
import {config} from "@/utils/config";
|
import {config} from "@/utils/config";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@@ -281,7 +288,7 @@ onLoad((option) => {
|
|||||||
.event-tag {
|
.event-tag {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgba(var(--td-brand-color-5), 0.1);
|
background-color: var(--td-brand-color-light);
|
||||||
color: var(--td-brand-color-5);
|
color: var(--td-brand-color-5);
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
padding: 6rpx 12rpx;
|
padding: 6rpx 12rpx;
|
||||||
@@ -364,6 +371,20 @@ onLoad((option) => {
|
|||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端覆盖富文本内联大字号,仅调整字号和行高,不改写字体、字重或颜色。 */
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.rich-content {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.75;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
|
||||||
|
:deep(*) {
|
||||||
|
font-size: 15px !important;
|
||||||
|
line-height: 1.75 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 比赛项目 */
|
/* 比赛项目 */
|
||||||
.event-items-scroll {
|
.event-items-scroll {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ async function publishMoment() {
|
|||||||
|
|
||||||
.location-icon {
|
.location-icon {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
color: #ff5777;
|
color: var(--td-brand-color);
|
||||||
margin-right: 10rpx;
|
margin-right: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ async function publishMoment() {
|
|||||||
|
|
||||||
.location-icon {
|
.location-icon {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
color: #ff5777;
|
color: var(--td-brand-color);
|
||||||
margin-right: 10rpx;
|
margin-right: 10rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<view class="time">{{ moment(item.publish).format('MM-DD HH:mm') }}</view>
|
<view class="time">{{ moment(item.publish).format('MM-DD HH:mm') }}</view>
|
||||||
<view @click="onPraise(item)">
|
<view @click="onPraise(item)">
|
||||||
<t-badge :count="item.praiseCount" :offset="[-2, -2]" class="badge-item" :showZero="true">
|
<t-badge :count="item.praiseCount" :offset="[-2, -2]" class="badge-item" :showZero="true">
|
||||||
<t-icon :style="item.praiseCount > 0 ? 'color: red' : ''" name="thumb-up-2-filled"/>
|
<t-icon :style="item.praiseCount > 0 ? 'color: var(--td-brand-color)' : ''" name="thumb-up-2-filled"/>
|
||||||
</t-badge>
|
</t-badge>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||||
<t-icon name="castle-7"></t-icon>
|
<t-icon name="castle-7"></t-icon>
|
||||||
{{ item.coHostUnit }}
|
{{ item.hostUnit }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -67,7 +67,12 @@
|
|||||||
<view>
|
<view>
|
||||||
<!-- 100人已报名-->
|
<!-- 100人已报名-->
|
||||||
</view>
|
</view>
|
||||||
<t-button theme="primary" shape="round" size="small">立即报名</t-button>
|
<view style="display: flex;gap: 10px" @click.stop>
|
||||||
|
<t-button theme="default" variant="outline" shape="round" size="small"
|
||||||
|
@click="navigateToDetail(item.id)"><t-icon name="browse" size="16px" style="margin-right: 4px"/>查看详情</t-button>
|
||||||
|
<t-button theme="primary" shape="round" size="small"
|
||||||
|
@click="navigateToSignUp(item.id)"><t-icon name="edit" size="16px" style="margin-right: 4px"/>立即报名</t-button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -204,6 +209,13 @@ const navigateToDetail = (id) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 报名按钮直达表单,活动ID沿用详情页的报名入口参数。
|
||||||
|
const navigateToSignUp = (id) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/activity/registration-form/index?id=${id}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getListData();
|
getListData();
|
||||||
@@ -263,8 +275,8 @@ onMounted(() => {
|
|||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
background-color: rgba(244, 90, 90, 0.12);
|
background-color: rgba(25, 137, 250, 0.12);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ onMounted(async () => {
|
|||||||
.bg-circle {
|
.bg-circle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(135deg, #f45a5a, #f78484);
|
background: linear-gradient(135deg, var(--td-brand-color), var(--td-brand-color-4));
|
||||||
opacity: 0.06;
|
opacity: 0.06;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.rank-header {
|
.rank-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: linear-gradient(135deg, #f45a5a, #f78484);
|
background: linear-gradient(135deg, var(--td-brand-color), var(--td-brand-color-4));
|
||||||
height: 200rpx;
|
height: 200rpx;
|
||||||
padding: 20rpx 32rpx;
|
padding: 20rpx 32rpx;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -235,7 +235,7 @@ onMounted(async () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 4rpx 20rpx rgba(244, 90, 90, 0.3);
|
box-shadow: 0 4rpx 20rpx rgba(25, 137, 250, 0.3);
|
||||||
border-bottom-left-radius: 30rpx;
|
border-bottom-left-radius: 30rpx;
|
||||||
border-bottom-right-radius: 30rpx;
|
border-bottom-right-radius: 30rpx;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -292,7 +292,7 @@ onMounted(async () => {
|
|||||||
padding: 24rpx 16rpx;
|
padding: 24rpx 16rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #666;
|
color: #666;
|
||||||
border-bottom: 1px solid rgba(244, 90, 90, 0.1);
|
border-bottom: 1px solid rgba(25, 137, 250, 0.1);
|
||||||
background-color: rgba(255, 255, 255, 0.8);
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
margin: 16rpx;
|
margin: 16rpx;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
@@ -353,8 +353,8 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-team {
|
.my-team {
|
||||||
background-color: rgba(244, 90, 90, 0.05);
|
background-color: rgba(25, 137, 250, 0.05);
|
||||||
border-left: 8rpx solid #f45a5a;
|
border-left: 8rpx solid var(--td-brand-color);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -367,7 +367,7 @@ onMounted(async () => {
|
|||||||
width: 60rpx;
|
width: 60rpx;
|
||||||
height: 60rpx;
|
height: 60rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: rgba(244, 90, 90, 0.1);
|
background-color: rgba(25, 137, 250, 0.1);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,8 +389,8 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-rank-number {
|
.my-rank-number {
|
||||||
background-color: rgba(244, 90, 90, 0.1);
|
background-color: rgba(25, 137, 250, 0.1);
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-info {
|
.team-info {
|
||||||
@@ -432,9 +432,9 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.my-team-tag {
|
.my-team-tag {
|
||||||
margin-left: 10rpx;
|
margin-left: 10rpx;
|
||||||
background-color: rgba(244, 90, 90, 0.1);
|
background-color: rgba(25, 137, 250, 0.1);
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
border: 1px solid rgba(244, 90, 90, 0.2);
|
border: 1px solid rgba(25, 137, 250, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
@@ -454,14 +454,14 @@ onMounted(async () => {
|
|||||||
.progress-bar-fill {
|
.progress-bar-fill {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(to right, #f45a5a, #f78484);
|
background: linear-gradient(to right, var(--td-brand-color), var(--td-brand-color-4));
|
||||||
border-radius: 3rpx;
|
border-radius: 3rpx;
|
||||||
transition: width 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
transition: width 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.score-value {
|
.score-value {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,8 +477,8 @@ onMounted(async () => {
|
|||||||
width: 90rpx;
|
width: 90rpx;
|
||||||
height: 90rpx;
|
height: 90rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(135deg, #f45a5a, #f78484);
|
background: linear-gradient(135deg, var(--td-brand-color), var(--td-brand-color-4));
|
||||||
box-shadow: 0 4rpx 20rpx rgba(244, 90, 90, 0.4);
|
box-shadow: 0 4rpx 20rpx rgba(25, 137, 250, 0.4);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -489,15 +489,15 @@ onMounted(async () => {
|
|||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
box-shadow: 0 4rpx 20rpx rgba(244, 90, 90, 0.4);
|
box-shadow: 0 4rpx 20rpx rgba(25, 137, 250, 0.4);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
box-shadow: 0 4rpx 25rpx rgba(244, 90, 90, 0.6);
|
box-shadow: 0 4rpx 25rpx rgba(25, 137, 250, 0.6);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
box-shadow: 0 4rpx 20rpx rgba(244, 90, 90, 0.4);
|
box-shadow: 0 4rpx 20rpx rgba(25, 137, 250, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
color: rgba(244, 90, 90, 0.5);
|
color: rgba(25, 137, 250, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-text {
|
.empty-text {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container">
|
<view class="page-container" :class="{ 'input-editing': inputEditing }" :style="inputViewportStyle">
|
||||||
<!-- 人员选项卡区域 -->
|
<!-- 人员选项卡区域 -->
|
||||||
<view class="tabs-container">
|
<view class="tabs-container">
|
||||||
<view class="tabs-header">
|
<view class="tabs-header">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-x="true" class="tabs-scroll-view" show-scrollbar="false">
|
<scroll-view scroll-x="true" class="tabs-scroll-view" show-scrollbar="false" :scroll-left="tabsScrollLeft" scroll-with-animation>
|
||||||
<view class="tabs">
|
<view class="tabs">
|
||||||
<view
|
<view
|
||||||
v-for="(user, index) in formData.userList"
|
v-for="(user, index) in formData.userList"
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
<view class="form-block">
|
<view class="form-block">
|
||||||
<view class="form-block-title">
|
<view class="form-block-title">
|
||||||
<t-icon name="usergroup"></t-icon>
|
<UsergroupIcon ></UsergroupIcon>
|
||||||
<text>报名人员信息</text>
|
<text>报名人员信息</text>
|
||||||
<text class="form-subtitle">
|
<text class="form-subtitle">
|
||||||
{{ formData.userList.length > 0 ? activeTabIndex + 1 : 0 }}/{{ formData.userList.length }}
|
{{ formData.userList.length > 0 ? activeTabIndex + 1 : 0 }}/{{ formData.userList.length }}
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
v-show="activeTabIndex === index">
|
v-show="activeTabIndex === index">
|
||||||
<t-form-item v-if="isFieldVisible('userName')" label="姓名" :name="`userList[${index}].userName`"
|
<t-form-item v-if="isFieldVisible('userName')" label="姓名" :name="`userList[${index}].userName`"
|
||||||
:rules="fieldRules('userName', '请输入姓名')">
|
:rules="fieldRules('userName', '请输入姓名')">
|
||||||
<t-input v-model="user.userName" borderless placeholder="请输入内容"></t-input>
|
<t-input v-model="user.userName" borderless placeholder="请输入内容" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('sex')" label="性别" :name="`userList[${index}].sex`"
|
<t-form-item v-if="isFieldVisible('sex')" label="性别" :name="`userList[${index}].sex`"
|
||||||
:rules="fieldRules('sex', '请选择性别')">
|
:rules="fieldRules('sex', '请选择性别')">
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
:disabled="isPersonTypeOptionDisabled(user, option)"/>
|
:disabled="isPersonTypeOptionDisabled(user, option)"/>
|
||||||
</t-checkbox-group>
|
</t-checkbox-group>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('projectType')" label="项目类型"
|
<t-form-item v-if="isFieldVisible('projectType', user)" label="项目类型"
|
||||||
:name="'userList[' + index + '].projectType'"
|
:name="'userList[' + index + '].projectType'"
|
||||||
:rules="fieldRules('projectType', '请选择项目类型')">
|
:rules="fieldRules('projectType', '请选择项目类型')">
|
||||||
<t-checkbox-group v-model="user.projectType" class="radio-box dict-radio-box"
|
<t-checkbox-group v-model="user.projectType" class="radio-box dict-radio-box"
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('weight')" label="体重(KG)" :name="`userList[${index}].weight`"
|
<t-form-item v-if="isFieldVisible('weight')" label="体重(KG)" :name="`userList[${index}].weight`"
|
||||||
:rules="fieldRules('weight', '请输入体重拔河比赛分组需要')">
|
:rules="fieldRules('weight', '请输入体重拔河比赛分组需要')">
|
||||||
<t-input placeholder="拔河比赛分组需要" type="number" borderless v-model="user.weight"></t-input>
|
<t-input placeholder="拔河比赛分组需要" type="number" borderless v-model="user.weight" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('unitName')" label="单位名称"
|
<t-form-item v-if="isFieldVisible('unitName')" label="单位名称"
|
||||||
:name="`userList[${index}].unitName`"
|
:name="`userList[${index}].unitName`"
|
||||||
@@ -127,22 +127,24 @@
|
|||||||
<t-form-item v-if="isFieldVisible('unitName')" label="单位简称">
|
<t-form-item v-if="isFieldVisible('unitName')" label="单位简称">
|
||||||
<t-input :model-value="unitAliasName" borderless readonly></t-input>
|
<t-input :model-value="unitAliasName" borderless readonly></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
|
<t-form-item v-if="isFieldVisible('idCard', user)" label="身份证" :name="`userList[${index}].idCard`"
|
||||||
|
:rules="fieldRules('idCard', '请输入身份证号码', [{validator: validateIdCard, message: '请输入正确的18位身份证号码'}])">
|
||||||
|
<t-input v-model="user.idCard" borderless placeholder="请输入身份证号码" :maxlength="18"
|
||||||
|
@focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item v-if="isFieldVisible('phone', user)" label="联系电话" :name="`userList[${index}].phone`"
|
||||||
|
:rules="fieldRules('phone', '请输入联系电话', [{telnumber:true, message: '请输入正确的联系电话'}])">
|
||||||
|
<t-input v-model="user.phone" type="tel" inputmode="numeric" :maxlength="11" borderless placeholder="请输入联系电话"
|
||||||
|
@focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('position')" label="职务" :name="`userList[${index}].position`"
|
<t-form-item v-if="isFieldVisible('position')" label="职务" :name="`userList[${index}].position`"
|
||||||
:rules="fieldRules('position', '请输入职务')">
|
:rules="fieldRules('position', '请输入职务')">
|
||||||
<t-input v-model="user.position" borderless placeholder="请输入职务"></t-input>
|
<t-input v-model="user.position" borderless placeholder="请输入职务" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
|
||||||
<!-- <t-form-item label="身份证号" :name="`userList[${index}].idCard`"-->
|
|
||||||
<!-- :rules="[{required:true,message: '请输入身份证号码'},{idcard:true, message: '请输入正确的身份证号码'}]">-->
|
|
||||||
<!-- <t-input v-model="user.idCard" borderless placeholder="请输入身份证号码"></t-input>-->
|
|
||||||
<!-- </t-form-item>-->
|
|
||||||
<t-form-item v-if="isFieldVisible('phone')" label="联系电话" :name="`userList[${index}].phone`"
|
|
||||||
:rules="fieldRules('phone', '请输入联系电话', [{telnumber:true, message: '请输入正确的联系电话'}])">
|
|
||||||
<t-input v-model="user.phone" borderless placeholder="请输入联系电话"></t-input>
|
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('birthday')" label="出生日期"
|
<t-form-item v-if="isFieldVisible('birthday')" label="出生日期"
|
||||||
:name="'userList[' + index + '].birthday'"
|
:name="'userList[' + index + '].birthday'"
|
||||||
:rules="fieldRules('birthday', '请选择出生日期')">
|
:rules="fieldRules('birthday', '请选择出生日期')">
|
||||||
<t-input v-model="user.birthday" type="date" borderless placeholder="请选择出生日期"></t-input>
|
<t-input v-model="user.birthday" type="date" borderless placeholder="请选择出生日期" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('isStay')" label="是否住宿" :name="`userList[${index}].isStay`"
|
<t-form-item v-if="isFieldVisible('isStay')" label="是否住宿" :name="`userList[${index}].isStay`"
|
||||||
:rules="fieldRules('isStay', '请选择是否住宿')">
|
:rules="fieldRules('isStay', '请选择是否住宿')">
|
||||||
@@ -168,7 +170,7 @@
|
|||||||
|
|
||||||
<t-form-item v-if="isFieldVisible('carNo') && user.traffic === '自驾'" label="车牌号"
|
<t-form-item v-if="isFieldVisible('carNo') && user.traffic === '自驾'" label="车牌号"
|
||||||
:name="`userList[${index}].carNo`" :rules="fieldRules('carNo', '请输入车牌号')">
|
:name="`userList[${index}].carNo`" :rules="fieldRules('carNo', '请输入车牌号')">
|
||||||
<t-input v-model="user.carNo" borderless placeholder="请输入车牌号"></t-input>
|
<t-input v-model="user.carNo" borderless placeholder="请输入车牌号" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
|
|
||||||
<t-form-item v-if="isFieldVisible('isStation') && user.traffic === '公共交通'" label="是否接站"
|
<t-form-item v-if="isFieldVisible('isStation') && user.traffic === '公共交通'" label="是否接站"
|
||||||
@@ -182,7 +184,7 @@
|
|||||||
<t-form-item v-if="isFieldVisible('stationAddress') && user.traffic === '公共交通' && user.isStation"
|
<t-form-item v-if="isFieldVisible('stationAddress') && user.traffic === '公共交通' && user.isStation"
|
||||||
label="接站地址" :name="`userList[${index}].stationAddress`"
|
label="接站地址" :name="`userList[${index}].stationAddress`"
|
||||||
:rules="fieldRules('stationAddress', '请输入接站地址')">
|
:rules="fieldRules('stationAddress', '请输入接站地址')">
|
||||||
<t-input v-model="user.stationAddress" borderless placeholder="请输入接站地址" maxlength="50"></t-input>
|
<t-input v-model="user.stationAddress" borderless placeholder="请输入接站地址" maxlength="50" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
|
|
||||||
|
|
||||||
@@ -196,11 +198,11 @@
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('skillName') && user.isShowSkill" label="才艺名称"
|
<t-form-item v-if="isFieldVisible('skillName') && user.isShowSkill" label="才艺名称"
|
||||||
:name="`userList[${index}].skillName`" :rules="fieldRules('skillName', '请填写才艺名称')">
|
:name="`userList[${index}].skillName`" :rules="fieldRules('skillName', '请填写才艺名称')">
|
||||||
<t-input v-model="user.skillName" borderless placeholder="请输入才艺名称"></t-input>
|
<t-input v-model="user.skillName" borderless placeholder="请输入才艺名称" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('equip') && user.isShowSkill" label="所需器材"
|
<t-form-item v-if="isFieldVisible('equip') && user.isShowSkill" label="所需器材"
|
||||||
:name="`userList[${index}].equip`" :rules="fieldRules('equip', '请填写才艺所需器材')">
|
:name="`userList[${index}].equip`" :rules="fieldRules('equip', '请填写才艺所需器材')">
|
||||||
<t-input v-model="user.equip" borderless placeholder="请填写才艺所需器材"></t-input>
|
<t-input v-model="user.equip" borderless placeholder="请填写才艺所需器材" @focus="handleIdentityInputFocus" @blur="handleIdentityInputBlur"></t-input>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isFieldVisible('isParty')" label="参加茶叙会" :name="`userList[${index}].isParty`"
|
<t-form-item v-if="isFieldVisible('isParty')" label="参加茶叙会" :name="`userList[${index}].isParty`"
|
||||||
:rules="fieldRules('isParty', '请选择是否参加茶叙会')">
|
:rules="fieldRules('isParty', '请选择是否参加茶叙会')">
|
||||||
@@ -268,7 +270,7 @@
|
|||||||
<view class="notice-popup">
|
<view class="notice-popup">
|
||||||
<view class="notice-popup-header">
|
<view class="notice-popup-header">
|
||||||
<view class="notice-popup-icon">
|
<view class="notice-popup-icon">
|
||||||
<t-icon name="info-circle" size="24px" color="#ff6b35"></t-icon>
|
<InfoCircleIcon size="24px" color="#ff6b35"></InfoCircleIcon>
|
||||||
</view>
|
</view>
|
||||||
<text class="notice-popup-title">重要提醒</text>
|
<text class="notice-popup-title">重要提醒</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -287,9 +289,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, computed} from 'vue'
|
// 固定图标按文件引入,避免通用 Icon 加载整套图标。
|
||||||
|
import InfoCircleIcon from 'tdesign-icons-vue-next/esm/components/info-circle.js';
|
||||||
|
import UsergroupIcon from 'tdesign-icons-vue-next/esm/components/usergroup.js';
|
||||||
|
import {ref, computed, nextTick, onMounted, onBeforeUnmount, watch} from 'vue'
|
||||||
import {useRequest} from '@/utils/request';
|
import {useRequest} from '@/utils/request';
|
||||||
import {Dialog, Toast} from "tdesign-mobile-vue";
|
import {DialogPlugin as Dialog} from 'tdesign-mobile-vue/esm/dialog/index.js';
|
||||||
|
import {ToastPlugin as Toast} from 'tdesign-mobile-vue/esm/toast/index.js';
|
||||||
import {onLoad} from "@dcloudio/uni-app";
|
import {onLoad} from "@dcloudio/uni-app";
|
||||||
import {useUserStore} from "@/store";
|
import {useUserStore} from "@/store";
|
||||||
import {showConfirmWithHistory} from "@/utils/popupHistory";
|
import {showConfirmWithHistory} from "@/utils/popupHistory";
|
||||||
@@ -302,6 +308,43 @@ const userStore = useUserStore()
|
|||||||
const unitAliasName = computed(() => userStore?.userInfo?.unit?.aliasName || '未设置')
|
const unitAliasName = computed(() => userStore?.userInfo?.unit?.aliasName || '未设置')
|
||||||
|
|
||||||
// 后端不可用时仍按历史字段展示,保证已有活动的报名页面不会变成空表单。
|
// 后端不可用时仍按历史字段展示,保证已有活动的报名页面不会变成空表单。
|
||||||
|
// H5键盘弹出时为当前输入项让出空间,并跟随实际可视窗口高度变化。
|
||||||
|
const inputEditing = ref(false)
|
||||||
|
const inputViewportHeight = ref(0)
|
||||||
|
const inputViewportStyle = computed(() => inputEditing.value && inputViewportHeight.value
|
||||||
|
? {'--input-viewport-height': inputViewportHeight.value + 'px'} : {})
|
||||||
|
let focusedIdentityInput = null
|
||||||
|
let inputScrollTimer = null
|
||||||
|
const updateInputViewport = () => {
|
||||||
|
if (typeof window === 'undefined' || !inputEditing.value) return
|
||||||
|
inputViewportHeight.value = window.visualViewport?.height || window.innerHeight
|
||||||
|
clearTimeout(inputScrollTimer)
|
||||||
|
inputScrollTimer = setTimeout(() => {
|
||||||
|
focusedIdentityInput?.scrollIntoView({block: 'center', inline: 'nearest', behavior: 'auto'})
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
const handleIdentityInputFocus = (_value, context) => {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
focusedIdentityInput = context?.e?.target
|
||||||
|
inputEditing.value = true
|
||||||
|
nextTick(updateInputViewport)
|
||||||
|
}
|
||||||
|
const handleIdentityInputBlur = () => {
|
||||||
|
clearTimeout(inputScrollTimer)
|
||||||
|
focusedIdentityInput = null
|
||||||
|
inputEditing.value = false
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
window.visualViewport?.addEventListener('resize', updateInputViewport)
|
||||||
|
window.addEventListener('resize', updateInputViewport)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearTimeout(inputScrollTimer)
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
window.visualViewport?.removeEventListener('resize', updateInputViewport)
|
||||||
|
window.removeEventListener('resize', updateInputViewport)
|
||||||
|
})
|
||||||
const registrationFields = ref([
|
const registrationFields = ref([
|
||||||
{fieldKey: 'userName', required: true, options: []},
|
{fieldKey: 'userName', required: true, options: []},
|
||||||
{fieldKey: 'sex', required: true, options: []},
|
{fieldKey: 'sex', required: true, options: []},
|
||||||
@@ -338,10 +381,33 @@ const projectTypeRuleStatus = ref({
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
rules: []
|
rules: []
|
||||||
})
|
})
|
||||||
const isFieldVisible = (fieldKey) => Boolean(registrationFieldMap.value[fieldKey])
|
// 人员类型按字典编码多选匹配,兼任运动员时同时显示身份证和联系电话。
|
||||||
|
const isFieldVisible = (fieldKey, user) => {
|
||||||
|
const field = registrationFieldMap.value[fieldKey]
|
||||||
|
if (!field) return false
|
||||||
|
if (!user || field.parentField !== 'personType') return true
|
||||||
|
const labels = field.showWhenValue.split(',')
|
||||||
|
const selected = parseMultiValue(user.personType)
|
||||||
|
return fieldOptions('personType').some((option) => labels.includes(option.label) && selected.includes(option.value))
|
||||||
|
}
|
||||||
const fieldOptions = (fieldKey) => registrationFieldMap.value[fieldKey]?.options || []
|
const fieldOptions = (fieldKey) => registrationFieldMap.value[fieldKey]?.options || []
|
||||||
// 红色必填标识与前端必填校验统一读取当前活动的报名字段配置,避免显示字段被全局标记为必填。
|
// 红色必填标识与前端必填校验统一读取当前活动的报名字段配置,避免显示字段被全局标记为必填。
|
||||||
const isFieldRequired = (fieldKey) => Boolean(registrationFieldMap.value[fieldKey]?.required)
|
const isFieldRequired = (fieldKey) => Boolean(registrationFieldMap.value[fieldKey]?.required)
|
||||||
|
// 校验出生日期及末位校验码,避免仅检查位数而接受无效身份证号码。
|
||||||
|
const validateIdCard = (value) => {
|
||||||
|
if (value == null || value === '') return true
|
||||||
|
const idCard = String(value).toUpperCase()
|
||||||
|
if (!/^[1-9]\d{16}[\dX]$/.test(idCard)) return false
|
||||||
|
const year = Number(idCard.slice(6, 10))
|
||||||
|
const month = Number(idCard.slice(10, 12))
|
||||||
|
const day = Number(idCard.slice(12, 14))
|
||||||
|
const birthday = new Date(year, month - 1, day)
|
||||||
|
if (year < 1800 || birthday.getFullYear() !== year || birthday.getMonth() !== month - 1
|
||||||
|
|| birthday.getDate() !== day || birthday.getTime() > Date.now()) return false
|
||||||
|
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
|
||||||
|
const sum = weights.reduce((total, weight, index) => total + Number(idCard[index]) * weight, 0)
|
||||||
|
return '10X98765432'[sum % 11] === idCard[17]
|
||||||
|
}
|
||||||
const fieldRules = (fieldKey, message, extraRules = []) => {
|
const fieldRules = (fieldKey, message, extraRules = []) => {
|
||||||
return [{required: isFieldRequired(fieldKey), message}, ...extraRules]
|
return [{required: isFieldRequired(fieldKey), message}, ...extraRules]
|
||||||
}
|
}
|
||||||
@@ -525,10 +591,12 @@ const handleSexChange = (user) => {
|
|||||||
|
|
||||||
// 当前选中的选项卡索引
|
// 当前选中的选项卡索引
|
||||||
const activeTabIndex = ref(0)
|
const activeTabIndex = ref(0)
|
||||||
|
const tabsScrollLeft = ref(0)
|
||||||
|
|
||||||
// 切换选项卡
|
// 切换选项卡
|
||||||
function switchTab(index) {
|
function switchTab(index) {
|
||||||
activeTabIndex.value = index
|
activeTabIndex.value = index
|
||||||
|
nextTick(() => scrollToTab(index))
|
||||||
// 添加滚动表单区域到顶部
|
// 添加滚动表单区域到顶部
|
||||||
scrollFormToTop()
|
scrollFormToTop()
|
||||||
}
|
}
|
||||||
@@ -543,7 +611,7 @@ function addNewUser() {
|
|||||||
unitName: userStore?.userInfo?.unit?.name,
|
unitName: userStore?.userInfo?.unit?.name,
|
||||||
position: null,
|
position: null,
|
||||||
birthday: null,
|
birthday: null,
|
||||||
// idCard: null,
|
idCard: null,
|
||||||
phone: null,
|
phone: null,
|
||||||
isStay: null,
|
isStay: null,
|
||||||
isContacts: false,
|
isContacts: false,
|
||||||
@@ -566,42 +634,33 @@ function addNewUser() {
|
|||||||
}, 10)
|
}, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 滚动到指定选项卡
|
// 控制标签栏自身的滚动位置:中间人员居中,最后一人靠右,边界不足时自动限位。
|
||||||
function scrollToTab(index) {
|
async function scrollToTab(index) {
|
||||||
|
await nextTick()
|
||||||
|
if (inputEditing.value || !formData.value.userList.length) return
|
||||||
const query = uni.createSelectorQuery()
|
const query = uni.createSelectorQuery()
|
||||||
query.selectAll('.tab').boundingClientRect()
|
query.selectAll('.tab').boundingClientRect()
|
||||||
query.select('.tabs-scroll-view').boundingClientRect()
|
query.select('.tabs-scroll-view').boundingClientRect()
|
||||||
query.exec((res) => {
|
query.select('.tabs').boundingClientRect()
|
||||||
if (res && res[0] && res[0][index] && res[1]) {
|
query.select('.tabs-scroll-view').scrollOffset()
|
||||||
const tabElement = res[0][index]
|
query.exec(async (res) => {
|
||||||
const scrollView = res[1]
|
if (inputEditing.value || index !== activeTabIndex.value || !res?.[0]?.[index] || !res[1]?.width || !res[2] || !res[3]) return
|
||||||
const scrollLeft = tabElement.left - scrollView.left - (scrollView.width / 2) + (tabElement.width / 2)
|
const tab = res[0][index]
|
||||||
uni.pageScrollTo({
|
const viewport = res[1]
|
||||||
scrollLeft: scrollLeft > 0 ? scrollLeft : 0,
|
const currentLeft = res[3].scrollLeft || 0
|
||||||
duration: 300
|
const maxLeft = Math.max(0, res[2].width - viewport.width)
|
||||||
})
|
const target = index === formData.value.userList.length - 1 ? maxLeft
|
||||||
}
|
: Math.min(maxLeft, Math.max(0, currentLeft + tab.left - viewport.left - (viewport.width - tab.width) / 2))
|
||||||
|
// 用户手动滑动后先同步真实位置,保证相同目标值仍能触发重新定位。
|
||||||
|
tabsScrollLeft.value = currentLeft
|
||||||
|
await nextTick()
|
||||||
|
if (!inputEditing.value && index === activeTabIndex.value) tabsScrollLeft.value = target
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 滚动到最右边
|
// 新增人员后沿用选中标签定位逻辑。
|
||||||
function scrollToRight() {
|
function scrollToRight() {
|
||||||
const query = uni.createSelectorQuery()
|
scrollToTab(activeTabIndex.value)
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 滚动表单区域到顶部
|
// 滚动表单区域到顶部
|
||||||
@@ -644,6 +703,10 @@ const formRef = ref(null)
|
|||||||
const formData = ref({
|
const formData = ref({
|
||||||
userList: []
|
userList: []
|
||||||
})
|
})
|
||||||
|
// 增删人员、姓名改变或键盘收起都会改变标签布局,渲染后重新定位当前人员。
|
||||||
|
watch([activeTabIndex, inputEditing, () => formData.value.userList.map(user => user.userName)], () => {
|
||||||
|
if (!inputEditing.value) scrollToTab(activeTabIndex.value)
|
||||||
|
}, {flush: 'post'})
|
||||||
// 后端返回当前单位已保存总数;编辑原报名时先扣除旧名单,再合并当前页面草稿人数,避免重复累计。
|
// 后端返回当前单位已保存总数;编辑原报名时先扣除旧名单,再合并当前页面草稿人数,避免重复累计。
|
||||||
const savedUnitRegistrationCount = ref(0)
|
const savedUnitRegistrationCount = ref(0)
|
||||||
const originalSignupUserCount = ref(0)
|
const originalSignupUserCount = ref(0)
|
||||||
@@ -681,7 +744,7 @@ onLoad(async (option) => {
|
|||||||
unitName: userStore?.userInfo?.unit?.name,
|
unitName: userStore?.userInfo?.unit?.name,
|
||||||
position: null,
|
position: null,
|
||||||
birthday: userStore.userInfo.birthday,
|
birthday: userStore.userInfo.birthday,
|
||||||
// idCard: userStore.userInfo.idCard,
|
idCard: null,
|
||||||
phone: userStore.userInfo.phone,
|
phone: userStore.userInfo.phone,
|
||||||
isStay: null,
|
isStay: null,
|
||||||
isContacts: false,
|
isContacts: false,
|
||||||
@@ -1088,7 +1151,7 @@ function closeNoticePopup() {
|
|||||||
.person-counter {
|
.person-counter {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background-color: #f45a5a;
|
background-color: var(--td-brand-color);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1130,15 +1193,15 @@ function closeNoticePopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-add-btn {
|
.header-add-btn {
|
||||||
background: linear-gradient(135deg, #f45a5a, #ff7676);
|
background: linear-gradient(135deg, var(--td-brand-color), var(--td-brand-color-4));
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 2px 6px rgba(244, 90, 90, 0.25);
|
box-shadow: 0 2px 6px rgba(25, 137, 250, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-remove-btn {
|
.header-remove-btn {
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
background-color: #fff1f0;
|
background-color: var(--td-brand-color-light);
|
||||||
border: 1px solid rgba(244, 90, 90, 0.35);
|
border: 1px solid rgba(25, 137, 250, 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-action-btn:active {
|
.header-action-btn:active {
|
||||||
@@ -1159,12 +1222,15 @@ function closeNoticePopup() {
|
|||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 90px;
|
min-width: 90px;
|
||||||
@@ -1202,8 +1268,8 @@ function closeNoticePopup() {
|
|||||||
|
|
||||||
.tab.active {
|
.tab.active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #f45a5a;
|
background-color: var(--td-brand-color);
|
||||||
box-shadow: 0 3px 8px rgba(244, 90, 90, 0.25);
|
box-shadow: 0 3px 8px rgba(25, 137, 250, 0.25);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1213,7 +1279,7 @@ function closeNoticePopup() {
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: linear-gradient(90deg, transparent, rgba(244, 90, 90, 0.3), transparent);
|
background: linear-gradient(90deg, transparent, rgba(25, 137, 250, 0.3), transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.act-info {
|
.act-info {
|
||||||
@@ -1259,8 +1325,27 @@ function closeNoticePopup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 输入期间收起非输入区域,避免与键盘共同占据屏幕;16px防止iOS聚焦自动放大。 */
|
||||||
|
.page-container.input-editing {
|
||||||
|
height: calc(var(--input-viewport-height, 100vh) - var(--window-top, 44px));
|
||||||
|
}
|
||||||
|
.input-editing .tabs-container,
|
||||||
|
.input-editing .footer,
|
||||||
|
.input-editing .quick-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.input-editing .form-area {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
.form-area :deep(input) {
|
||||||
|
font-size: 16px;
|
||||||
|
scroll-margin-block: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-area {
|
.form-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-bottom: 130px;
|
margin-bottom: 130px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
@@ -1418,7 +1503,7 @@ function closeNoticePopup() {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: rgba(244, 90, 90, 0.9);
|
background-color: rgba(25, 137, 250, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1429,7 +1514,7 @@ function closeNoticePopup() {
|
|||||||
|
|
||||||
.quick-nav-btn:active {
|
.quick-nav-btn:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
background-color: rgba(244, 90, 90, 1);
|
background-color: rgba(25, 137, 250, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 协议弹窗样式 */
|
/* 协议弹窗样式 */
|
||||||
|
|||||||
@@ -141,14 +141,14 @@ const navigateToEdit = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: rgba(255, 95, 92, 0.1);
|
background-color: rgba(25, 137, 250, 0.1);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|
||||||
.t-icon {
|
.t-icon {
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,13 +171,13 @@ const navigateToEdit = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.t-button--primary) {
|
:deep(.t-button--primary) {
|
||||||
background-color: #ff5f5c !important;
|
background-color: var(--td-brand-color) !important;
|
||||||
border-color: #ff5f5c !important;
|
border-color: var(--td-brand-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.t-button--default) {
|
:deep(.t-button--default) {
|
||||||
border-color: #ff5f5c !important;
|
border-color: var(--td-brand-color) !important;
|
||||||
color: #ff5f5c !important;
|
color: var(--td-brand-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -550,7 +550,7 @@ const submitRegistration = async () => {
|
|||||||
|
|
||||||
.picker-subtitle {
|
.picker-subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ onLoad(() => {
|
|||||||
|
|
||||||
.picker-subtitle {
|
.picker-subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
@@ -403,7 +403,7 @@ onLoad(() => {
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 16px rgba(255, 95, 92, 0.08);
|
box-shadow: 0 4px 16px rgba(25, 137, 250, 0.08);
|
||||||
margin: 12px 0 20px;
|
margin: 12px 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,8 +411,8 @@ onLoad(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: linear-gradient(to right, rgba(255, 95, 92, 0.05), rgba(255, 95, 92, 0.02));
|
background: linear-gradient(to right, rgba(25, 137, 250, 0.05), rgba(25, 137, 250, 0.02));
|
||||||
border-bottom: 1px solid rgba(255, 95, 92, 0.1);
|
border-bottom: 1px solid rgba(25, 137, 250, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-logo {
|
.team-logo {
|
||||||
@@ -424,14 +424,14 @@ onLoad(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 2px solid #fff;
|
border: 2px solid #fff;
|
||||||
box-shadow: 0 2px 8px rgba(255, 95, 92, 0.15);
|
box-shadow: 0 2px 8px rgba(25, 137, 250, 0.15);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-logo i {
|
.team-logo i {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-logo img {
|
.team-logo img {
|
||||||
@@ -472,12 +472,12 @@ onLoad(() => {
|
|||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
background-color: rgba(255, 95, 92, 0.1);
|
background-color: rgba(25, 137, 250, 0.1);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,12 +528,12 @@ onLoad(() => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #fff8f8;
|
background-color: #f5faff;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-desc i {
|
.team-desc i {
|
||||||
color: #ffdddd;
|
color: var(--td-brand-color-2);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
@@ -548,9 +548,9 @@ onLoad(() => {
|
|||||||
.register-btn {
|
.register-btn {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
background-color: #ff5f5c;
|
background-color: var(--td-brand-color);
|
||||||
border-color: #ff5f5c;
|
border-color: var(--td-brand-color);
|
||||||
box-shadow: 0 6px 16px rgba(255, 95, 92, 0.2);
|
box-shadow: 0 6px 16px rgba(25, 137, 250, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 成员列表 */
|
/* 成员列表 */
|
||||||
@@ -579,7 +579,7 @@ onLoad(() => {
|
|||||||
|
|
||||||
.section-title i {
|
.section-title i {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,8 +589,8 @@ onLoad(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 24px;
|
min-width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
background-color: #fff0f0;
|
background-color: var(--td-brand-color-light);
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -647,11 +647,11 @@ onLoad(() => {
|
|||||||
.role-card {
|
.role-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #fff8f8;
|
background-color: #f5faff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
box-shadow: 0 4px 12px rgba(255, 95, 92, 0.05);
|
box-shadow: 0 4px 12px rgba(25, 137, 250, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-icon {
|
.role-icon {
|
||||||
@@ -664,7 +664,7 @@ onLoad(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
//box-shadow: 0 4px 12px rgba(255, 95, 92, 0.2);
|
//box-shadow: 0 4px 12px rgba(255, 95, 92, 0.2);
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,8 +689,8 @@ onLoad(() => {
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-color: #ff5f5c;
|
border-color: var(--td-brand-color);
|
||||||
color: #ff5f5c;
|
color: var(--td-brand-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 弹窗内容样式 */
|
/* 弹窗内容样式 */
|
||||||
|
|||||||
+220
-162
@@ -1,12 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container">
|
<view class="page-container">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<image class="header-bg"
|
<image
|
||||||
src="/static/img/index4.png"
|
class="header-bg"
|
||||||
mode="aspectFill"></image>
|
src="/static/img/index4.jpg"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<!-- html文字临时屏蔽
|
||||||
<view v-if="currentActivityTitle" class="header-activity-title">
|
<view v-if="currentActivityTitle" class="header-activity-title">
|
||||||
{{ displayActivityTitle }}
|
{{ displayActivityTitle }}
|
||||||
</view>
|
</view>
|
||||||
|
-->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="menu-container">
|
<view class="menu-container">
|
||||||
@@ -16,54 +20,79 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="menu-text">赛事报名</view>
|
<view class="menu-text">赛事报名</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" @click="navigateTo('/pages/activity/scan-sign/index')">
|
|
||||||
<view class="menu-icon-wrapper orange-bg">
|
|
||||||
<view class="menu-icon qrcode-icon"></view>
|
|
||||||
</view>
|
|
||||||
<view class="menu-text">扫码签到</view>
|
|
||||||
</view>
|
|
||||||
<view class="menu-item" @click="navigateTo('/pages/activity/team/index')">
|
<view class="menu-item" @click="navigateTo('/pages/activity/team/index')">
|
||||||
<view class="menu-icon-wrapper yellow-bg">
|
<view class="menu-icon-wrapper yellow-bg">
|
||||||
<view class="menu-icon team-icon"></view>
|
<view class="menu-icon team-icon"></view>
|
||||||
<!-- <view class="badge">3</view>-->
|
<!-- <view class="badge">3</view>-->
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-text">我的团队</view>
|
<view class="menu-text">我的团队</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" @click="navigateTo('/pages/activity/team-form/index')">
|
<!-- 暂未开放的快捷入口保留展示,不绑定跳转事件。 -->
|
||||||
|
<view class="menu-item menu-item-disabled" aria-disabled="true">
|
||||||
<view class="menu-icon-wrapper blue-bg">
|
<view class="menu-icon-wrapper blue-bg">
|
||||||
<view class="menu-icon star-icon"></view>
|
<view class="menu-icon star-icon"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-text">项目报名</view>
|
<view class="menu-text">项目报名</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="menu-item" @click="navigateTo('/pages/activity/dynamic/index')">-->
|
<view class="menu-item menu-item-disabled" aria-disabled="true">
|
||||||
<!-- <view class="menu-icon-wrapper blue-bg">-->
|
<view class="menu-icon-wrapper orange-bg">
|
||||||
<!-- <view class="menu-icon star-icon"></view>-->
|
<view class="menu-icon qrcode-icon"></view>
|
||||||
<!-- </view>-->
|
</view>
|
||||||
<!-- <view class="menu-text">精彩瞬间</view>-->
|
<view class="menu-text">扫码签到</view>
|
||||||
<!-- </view>-->
|
</view>
|
||||||
|
<!-- <view class="menu-item" @click="navigateTo('/pages/activity/dynamic/index')">-->
|
||||||
|
<!-- <view class="menu-icon-wrapper blue-bg">-->
|
||||||
|
<!-- <view class="menu-icon star-icon"></view>-->
|
||||||
|
<!-- </view>-->
|
||||||
|
<!-- <view class="menu-text">精彩瞬间</view>-->
|
||||||
|
<!-- </view>-->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 精选内容区 -->
|
<!-- 精选内容区 -->
|
||||||
<view class="content-section">
|
<view class="content-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<view class="section-title">活动详情</view>
|
<view class="section-title">活动详情</view>
|
||||||
<view class="section-more" @click="navigateTo('/pages/activity/list/index')">更多</view>
|
<view
|
||||||
|
class="section-more"
|
||||||
|
@click="navigateTo('/pages/activity/list/index')"
|
||||||
|
>更多</view
|
||||||
|
>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<t-swiper
|
<t-swiper
|
||||||
v-if="actList.length > 0"
|
v-if="actList.length > 0"
|
||||||
:autoplay="actList.length > 1"
|
:autoplay="actList.length > 1"
|
||||||
:navigation="{ type: 'fraction', paginationPosition: 'bottom-right', showControls: true }"
|
:navigation="{
|
||||||
|
type: 'fraction',
|
||||||
|
paginationPosition: 'bottom-right',
|
||||||
|
showControls: true,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<t-swiper-item
|
||||||
|
v-for="(item, index) in actList"
|
||||||
|
:key="index"
|
||||||
|
style="height: 192px"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="activity-list"
|
||||||
|
@click="navigateTo('/pages/activity/detail/index?id=' + item.id)"
|
||||||
>
|
>
|
||||||
<t-swiper-item v-for="(item, index) in actList" :key="index" style="height: 192px">
|
|
||||||
<view class="activity-list" @click="navigateTo('/pages/activity/detail/index?id=' + item.id)">
|
|
||||||
<view class="activity-item">
|
<view class="activity-item">
|
||||||
<t-image :src="getActivityCoverUrl(item.cover)" shape="round"
|
<t-image
|
||||||
class="activity-image" fit="fill"/>
|
:src="getActivityCoverUrl(item.cover)"
|
||||||
|
:lazy="index > 0"
|
||||||
|
shape="round"
|
||||||
|
class="activity-image"
|
||||||
|
fit="fill"
|
||||||
|
/>
|
||||||
<view class="activity-info">
|
<view class="activity-info">
|
||||||
<view class="activity-title">{{ item.title }}</view>
|
<view class="activity-title">{{ item.title }}</view>
|
||||||
<view class="activity-meta">
|
<view class="activity-meta">
|
||||||
<text class="activity-time">{{ moment(item.startTime).format('M月DD日') + '-' + moment(item.endTime).format('DD日') }}</text>
|
<text class="activity-time">{{
|
||||||
|
moment(item.startTime).format('M月DD日') +
|
||||||
|
'-' +
|
||||||
|
moment(item.endTime).format('DD日')
|
||||||
|
}}</text>
|
||||||
<text class="activity-location">{{ item.address }}</text>
|
<text class="activity-location">{{ item.address }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -71,11 +100,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</t-swiper-item>
|
</t-swiper-item>
|
||||||
</t-swiper>
|
</t-swiper>
|
||||||
<t-empty
|
<t-empty v-if="actList.length === 0" description="暂无近期活动" />
|
||||||
v-if="actList.length === 0"
|
|
||||||
description="暂无近期活动"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="content-section">
|
<view class="content-section">
|
||||||
@@ -84,14 +109,23 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="activity-list" v-if="wonderfulMomentList.length > 0">
|
<view class="activity-list" v-if="wonderfulMomentList.length > 0">
|
||||||
<view class="moment-item" v-for="item in wonderfulMomentList" :key="item.id"
|
<view
|
||||||
@click="handleWonderfulMoment(item)">
|
class="moment-item"
|
||||||
<t-image :src=config.fileDomain+item.cover shape="round"
|
v-for="item in wonderfulMomentList"
|
||||||
class="moment-image" fit="fill"/>
|
:key="item.id"
|
||||||
|
@click="handleWonderfulMoment(item)"
|
||||||
|
>
|
||||||
|
<t-image
|
||||||
|
:src="config.fileDomain + item.cover"
|
||||||
|
lazy
|
||||||
|
shape="round"
|
||||||
|
class="moment-image"
|
||||||
|
fit="fill"
|
||||||
|
/>
|
||||||
<view class="moment-content">
|
<view class="moment-content">
|
||||||
<view class="moment-title">{{ item.title }}</view>
|
<view class="moment-title">{{ item.title }}</view>
|
||||||
<view class="moment-sub-content">
|
<view class="moment-sub-content">
|
||||||
<view style="float: right;">{{ item.time }}</view>
|
<view style="float: right">{{ item.time }}</view>
|
||||||
<view>阅读 {{ item.readCount }}</view>
|
<view>阅读 {{ item.readCount }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -101,113 +135,121 @@
|
|||||||
v-if="wonderfulMomentList.length === 0"
|
v-if="wonderfulMomentList.length === 0"
|
||||||
description="暂无精彩瞬间"
|
description="暂无精彩瞬间"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, onMounted, ref} from "vue";
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import {onLoad, onShow} from "@dcloudio/uni-app";
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
import {Toast} from "tdesign-mobile-vue";
|
import { Toast } from 'tdesign-mobile-vue'
|
||||||
import {useRequest} from "@/utils/request";
|
import { useRequest } from '@/utils/request'
|
||||||
import moment from "moment";
|
import moment from 'moment'
|
||||||
import {config} from "@/utils/config";
|
import { config } from '@/utils/config'
|
||||||
|
|
||||||
const request = useRequest()
|
const request = useRequest()
|
||||||
|
|
||||||
const actList = ref([])
|
const actList = ref([])
|
||||||
const wonderfulMomentList = ref([])
|
const wonderfulMomentList = ref([])
|
||||||
// 首页头图展示当前最新开启活动的标题,数据口径与登录页保持一致。
|
// 首页头图展示当前最新开启活动的标题,数据口径与登录页保持一致。
|
||||||
const currentActivityTitle = ref('')
|
const currentActivityTitle = ref('')
|
||||||
// 当前活动标题在“(公司)”后分行展示,其他标题仍保留原文并按容器宽度自然换行。
|
// 当前活动标题在“(公司)”后分行展示,其他标题仍保留原文并按容器宽度自然换行。
|
||||||
const displayActivityTitle = computed(() => {
|
const displayActivityTitle = computed(() => {
|
||||||
return currentActivityTitle.value.replace('(公司)', '(公司)\n')
|
return currentActivityTitle.value.replace('(公司)', '(公司)\n')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 后台仅保存封面相对路径,首页通过文件预览接口读取活动管理中上传的实际图片。
|
// 后台仅保存封面相对路径,首页通过文件预览接口读取活动管理中上传的实际图片。
|
||||||
const getActivityCoverUrl = (cover) => {
|
const getActivityCoverUrl = (cover) => {
|
||||||
if (!cover) {
|
if (!cover) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
return '/api/open/file/upload/download?inline=true&filepath=' + encodeURIComponent(cover)
|
return (
|
||||||
}
|
'/api/open/file/upload/download?inline=true&filepath=' +
|
||||||
|
encodeURIComponent(cover)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function navigateTo(url) {
|
function navigateTo(url) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url
|
url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivity() {
|
function getActivity() {
|
||||||
request.post('/platform/activity/manage/getActivity').then(res=>{
|
request.post('/platform/activity/manage/getActivity').then((res) => {
|
||||||
if(res.code === 0){
|
if (res.code === 0) {
|
||||||
actList.value = res.data
|
actList.value = res.data
|
||||||
}else{
|
} else {
|
||||||
Toast({message: res.msg, theme: 'error'});
|
Toast({ message: res.msg, theme: 'error' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公开接口返回当前最新开启活动信息;请求失败时不显示标题,也不阻断首页其他内容。
|
// 公开接口返回当前最新开启活动信息;请求失败时不显示标题,也不阻断首页其他内容。
|
||||||
function getLatestActivityTitle() {
|
function getLatestActivityTitle() {
|
||||||
request.get('/open/common/latestActivityOrganizer').then(({code, data}) => {
|
request
|
||||||
|
.get('/open/common/latestActivityOrganizer')
|
||||||
|
.then(({ code, data }) => {
|
||||||
if (code === 0 && data) {
|
if (code === 0 && data) {
|
||||||
currentActivityTitle.value = data.title || ''
|
currentActivityTitle.value = data.title || ''
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.error('获取当前活动标题失败', error)
|
console.error('获取当前活动标题失败', error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWonderfulMoment() {
|
function getWonderfulMoment() {
|
||||||
request.post('/platform/activity/wonderfulMoment/getWonderfulMoment').then(res=>{
|
request
|
||||||
if(res.code === 0){
|
.post('/platform/activity/wonderfulMoment/getWonderfulMoment')
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
wonderfulMomentList.value = res.data
|
wonderfulMomentList.value = res.data
|
||||||
}else{
|
} else {
|
||||||
Toast({message: res.msg, theme: 'error'});
|
Toast({ message: res.msg, theme: 'error' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleWonderfulMoment(row) {
|
function handleWonderfulMoment(row) {
|
||||||
request.post('/platform/activity/wonderfulMoment/handleWonderfulMoment/' + row.id).then(res=>{
|
request
|
||||||
if(res.code === 0){
|
.post(
|
||||||
|
'/platform/activity/wonderfulMoment/handleWonderfulMoment/' + row.id,
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
getWonderfulMoment()
|
getWonderfulMoment()
|
||||||
navigateTo('/pages/activity/wonderfulMoment/index?id=' + row.id)
|
navigateTo('/pages/activity/wonderfulMoment/index?id=' + row.id)
|
||||||
}else{
|
} else {
|
||||||
Toast({message: res.msg, theme: 'error'});
|
Toast({ message: res.msg, theme: 'error' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
getLatestActivityTitle()
|
getLatestActivityTitle()
|
||||||
getActivity()
|
getActivity()
|
||||||
getWonderfulMoment()
|
getWonderfulMoment()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
uni-page-body {
|
uni-page-body {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
// 头部区域样式
|
// 头部区域样式
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 245px;
|
height: 245px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-bg {
|
.header-bg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-activity-title {
|
.header-activity-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 61%;
|
top: 61%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -221,10 +263,10 @@ uni-page-body {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
text-shadow: 0 1px 3px rgba(122, 24, 24, 0.45);
|
text-shadow: 0 1px 3px rgba(122, 24, 24, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 菜单区域样式
|
// 菜单区域样式
|
||||||
.menu-container {
|
.menu-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -233,16 +275,16 @@ uni-page-body {
|
|||||||
margin: -30px 15px 15px;
|
margin: -30px 15px 15px;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 22%;
|
width: 22%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon-wrapper {
|
.menu-icon-wrapper {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -251,49 +293,49 @@ uni-page-body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.red-bg {
|
.red-bg {
|
||||||
background: linear-gradient(135deg, #ff7676, #f45a5a);
|
background: linear-gradient(135deg, var(--td-brand-color-4), var(--td-brand-color));
|
||||||
}
|
}
|
||||||
|
|
||||||
.orange-bg {
|
.orange-bg {
|
||||||
background: linear-gradient(135deg, #ffa76a, #f89f58);
|
background: linear-gradient(135deg, #ffa76a, #f89f58);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yellow-bg {
|
.yellow-bg {
|
||||||
background: linear-gradient(135deg, #ffd166, #f8c158);
|
background: linear-gradient(135deg, #ffd166, #f8c158);
|
||||||
}
|
}
|
||||||
|
|
||||||
.blue-bg {
|
.blue-bg {
|
||||||
background: linear-gradient(135deg, #72a1ff, #5c7cfa);
|
background: linear-gradient(135deg, #72a1ff, #5c7cfa);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trophy-icon {
|
.trophy-icon {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M19 5h-2V3H7v2H5c-1.1 0-2 .9-2 2v1c0 2.55 1.92 4.63 4.39 4.94.63 1.5 1.98 2.63 3.61 2.96V19H7v2h10v-2h-4v-3.1c1.63-.33 2.98-1.46 3.61-2.96C19.08 12.63 21 10.55 21 8V7c0-1.1-.9-2-2-2zM5 8V7h2v3.82C5.84 10.4 5 9.3 5 8zm14 0c0 1.3-.84 2.4-2 2.82V7h2v1z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M19 5h-2V3H7v2H5c-1.1 0-2 .9-2 2v1c0 2.55 1.92 4.63 4.39 4.94.63 1.5 1.98 2.63 3.61 2.96V19H7v2h10v-2h-4v-3.1c1.63-.33 2.98-1.46 3.61-2.96C19.08 12.63 21 10.55 21 8V7c0-1.1-.9-2-2-2zM5 8V7h2v3.82C5.84 10.4 5 9.3 5 8zm14 0c0 1.3-.84 2.4-2 2.82V7h2v1z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-icon {
|
.qrcode-icon {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-icon {
|
.team-icon {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-icon {
|
.star-icon {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
@@ -308,39 +350,55 @@ uni-page-body {
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: 0 2px 4px rgba(255, 77, 79, 0.3);
|
box-shadow: 0 2px 4px rgba(255, 77, 79, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-text {
|
.menu-text {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内容区域样式
|
.menu-item-disabled {
|
||||||
.content-section {
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-disabled .menu-icon-wrapper::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: rgba(255, 255, 255, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-disabled .menu-text {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容区域样式
|
||||||
|
.content-section {
|
||||||
margin: 0 0 15px;
|
margin: 0 0 15px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title::before {
|
.section-title::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -348,75 +406,75 @@ uni-page-body {
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: linear-gradient(to bottom, #ff6b6b, #e83a3a);
|
background: linear-gradient(to bottom, var(--td-brand-color-4), var(--td-brand-color-active));
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-more {
|
.section-more {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 活动列表样式
|
// 活动列表样式
|
||||||
.activity-list {
|
.activity-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-item {
|
.activity-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-item:last-child {
|
.activity-item:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-image {
|
.activity-image {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-info {
|
.activity-info {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-title {
|
.activity-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-meta {
|
.activity-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-time {
|
.activity-time {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
.moment-image {
|
.moment-image {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 90px;
|
height: 90px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
.moment-item {
|
.moment-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.moment-title {
|
.moment-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.moment-content {
|
.moment-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.moment-sub-content {
|
.moment-sub-content {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,15 +6,15 @@
|
|||||||
<pattern id="pattern-hex" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"
|
<pattern id="pattern-hex" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"
|
||||||
patternTransform="scale(0.8)">
|
patternTransform="scale(0.8)">
|
||||||
<rect x="0" y="0" width="10" height="10" fill="none"/>
|
<rect x="0" y="0" width="10" height="10" fill="none"/>
|
||||||
<path d="M0,5 L2.5,0 L7.5,0 L10,5 L7.5,10 L2.5,10 Z" stroke-width="0.5" stroke="rgba(244, 90, 90, 0.2)"
|
<path d="M0,5 L2.5,0 L7.5,0 L10,5 L7.5,10 L2.5,10 Z" stroke-width="0.5" stroke="rgba(25, 137, 250, 0.2)"
|
||||||
fill="rgba(244, 90, 90, 0.1)">
|
fill="rgba(25, 137, 250, 0.1)">
|
||||||
<animate attributeName="stroke-opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite"/>
|
<animate attributeName="stroke-opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite"/>
|
||||||
</path>
|
</path>
|
||||||
</pattern>
|
</pattern>
|
||||||
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-hex)"/>
|
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-hex)"/>
|
||||||
|
|
||||||
<g fill="none" stroke-width="0.5">
|
<g fill="none" stroke-width="0.5">
|
||||||
<path d="M20,50 Q60,20 100,50 T180,50 T260,50" stroke="rgba(244, 90, 90, 0.6)">
|
<path d="M20,50 Q60,20 100,50 T180,50 T260,50" stroke="rgba(25, 137, 250, 0.6)">
|
||||||
<animate attributeName="d"
|
<animate attributeName="d"
|
||||||
values="M20,50 Q60,20 100,50 T180,50 T260,50;
|
values="M20,50 Q60,20 100,50 T180,50 T260,50;
|
||||||
M20,50 Q60,80 100,50 T180,50 T260,50;
|
M20,50 Q60,80 100,50 T180,50 T260,50;
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
dur="10s" repeatCount="indefinite"/>
|
dur="10s" repeatCount="indefinite"/>
|
||||||
</path>
|
</path>
|
||||||
|
|
||||||
<path d="M20,70 Q60,40 100,70 T180,70 T260,70" stroke="rgba(244, 90, 90, 0.4)">
|
<path d="M20,70 Q60,40 100,70 T180,70 T260,70" stroke="rgba(25, 137, 250, 0.4)">
|
||||||
<animate attributeName="d"
|
<animate attributeName="d"
|
||||||
values="M20,70 Q60,40 100,70 T180,70 T260,70;
|
values="M20,70 Q60,40 100,70 T180,70 T260,70;
|
||||||
M20,70 Q60,100 100,70 T180,70 T260,70;
|
M20,70 Q60,100 100,70 T180,70 T260,70;
|
||||||
@@ -31,19 +31,19 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<circle cx="50" cy="50" r="1" fill="rgba(244, 90, 90, 0.8)">
|
<circle cx="50" cy="50" r="1" fill="rgba(25, 137, 250, 0.8)">
|
||||||
<animate attributeName="cx" values="50;250;50" dur="20s" repeatCount="indefinite"/>
|
<animate attributeName="cx" values="50;250;50" dur="20s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="cy" values="50;30;80;50" dur="16s" repeatCount="indefinite"/>
|
<animate attributeName="cy" values="50;30;80;50" dur="16s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="r" values="1;1.5;1" dur="5s" repeatCount="indefinite"/>
|
<animate attributeName="r" values="1;1.5;1" dur="5s" repeatCount="indefinite"/>
|
||||||
</circle>
|
</circle>
|
||||||
|
|
||||||
<circle cx="150" cy="30" r="1" fill="rgba(244, 90, 90, 0.8)">
|
<circle cx="150" cy="30" r="1" fill="rgba(25, 137, 250, 0.8)">
|
||||||
<animate attributeName="cx" values="150;100;200;150" dur="25s" repeatCount="indefinite"/>
|
<animate attributeName="cx" values="150;100;200;150" dur="25s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="cy" values="30;60;20;30" dur="18s" repeatCount="indefinite"/>
|
<animate attributeName="cy" values="30;60;20;30" dur="18s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="r" values="1;1.8;1" dur="6s" repeatCount="indefinite"/>
|
<animate attributeName="r" values="1;1.8;1" dur="6s" repeatCount="indefinite"/>
|
||||||
</circle>
|
</circle>
|
||||||
|
|
||||||
<circle cx="250" cy="70" r="1" fill="rgba(244, 90, 90, 0.8)">
|
<circle cx="250" cy="70" r="1" fill="rgba(25, 137, 250, 0.8)">
|
||||||
<animate attributeName="cx" values="250;150;250" dur="22s" repeatCount="indefinite"/>
|
<animate attributeName="cx" values="250;150;250" dur="22s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="cy" values="70;40;70" dur="15s" repeatCount="indefinite"/>
|
<animate attributeName="cy" values="70;40;70" dur="15s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="r" values="1;1.2;1" dur="4s" repeatCount="indefinite"/>
|
<animate attributeName="r" values="1;1.2;1" dur="4s" repeatCount="indefinite"/>
|
||||||
@@ -89,8 +89,7 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 测试阶段临时关闭登录验证码输入和发送入口;恢复正式登录时需取消本段注释。 -->
|
<!-- 登录验证码输入和发送入口 -->
|
||||||
<!--
|
|
||||||
<view class="input-box" v-if="loginForm.loginMethod==='sms'">
|
<view class="input-box" v-if="loginForm.loginMethod==='sms'">
|
||||||
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
||||||
<input
|
<input
|
||||||
@@ -104,7 +103,6 @@
|
|||||||
{{ countDownText }}
|
{{ countDownText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- 密码输入框 -->
|
<!-- 密码输入框 -->
|
||||||
<view class="input-box" v-if="loginForm.loginMethod==='password'">
|
<view class="input-box" v-if="loginForm.loginMethod==='password'">
|
||||||
@@ -260,8 +258,7 @@ const startCountDown = () => {
|
|||||||
|
|
||||||
// 在页面挂载时恢复倒计时状态
|
// 在页面挂载时恢复倒计时状态
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 测试阶段不启用登录验证码,因此不再恢复验证码倒计时状态。
|
restoreCountdownState()
|
||||||
// restoreCountdownState()
|
|
||||||
getLatestActivityOrganizer()
|
getLatestActivityOrganizer()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -274,7 +271,6 @@ onBeforeUnmount(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 获取登录短信验证码:手机号通过路径参数传递,接口返回 code 和 msg 表示发送结果。
|
// 获取登录短信验证码:手机号通过路径参数传递,接口返回 code 和 msg 表示发送结果。
|
||||||
/* 测试阶段临时关闭登录验证码发送;恢复正式登录时需同时恢复验证码输入和后端校验。
|
|
||||||
const getSmsCode = () => {
|
const getSmsCode = () => {
|
||||||
if (!loginForm.phone) {
|
if (!loginForm.phone) {
|
||||||
Toast('请输入手机号码');
|
Toast('请输入手机号码');
|
||||||
@@ -295,21 +291,19 @@ const getSmsCode = () => {
|
|||||||
Toast({message: msg, theme: 'error'})
|
Toast({message: msg, theme: 'error'})
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
Toast({message: error.message, theme: 'error'})
|
Toast({message: error?.message || error?.msg || error?.errMsg || '验证码发送失败,请稍后重试', theme: 'error'})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
const handleLogin = () => {
|
const handleLogin = () => {
|
||||||
if (!loginForm.phone) {
|
if (!loginForm.phone) {
|
||||||
Toast('请输入手机号码');
|
Toast('请输入手机号码');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 测试阶段允许已注册手机号直接登录,暂不校验验证码是否填写。
|
if (!loginForm.smsCode.trim() && loginForm.loginMethod === 'sms') {
|
||||||
// if (!loginForm.smsCode && loginForm.loginMethod === 'sms') {
|
Toast('请输入验证码');
|
||||||
// Toast('请输入验证码');
|
return
|
||||||
// return
|
}
|
||||||
// }
|
|
||||||
if (!loginForm.password && loginForm.loginMethod === 'password') {
|
if (!loginForm.password && loginForm.loginMethod === 'password') {
|
||||||
Toast('请输入密码');
|
Toast('请输入密码');
|
||||||
return
|
return
|
||||||
@@ -354,32 +348,25 @@ function navigateToRegister() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-container {
|
.login-container {
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f4f2f2;
|
background: #f4f2f2;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-area {
|
.title-area {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 30vh;
|
// 固定顶部高度,长表单通过整页滚动展示,避免挤压标题区域。
|
||||||
|
height: 260px;
|
||||||
|
flex-shrink: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: linear-gradient(135deg, #7a1818 0%, #d03e3e 50%, #f45a5a 100%);
|
background: linear-gradient(135deg, #00468c 0%, #0873d9 50%, var(--td-brand-color) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tech-animation {
|
.tech-animation {
|
||||||
@@ -403,7 +390,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-title {
|
.main-title {
|
||||||
font-size: 34px;
|
font-size: 28px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -429,7 +416,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-card {
|
.login-card {
|
||||||
flex: 1;
|
flex: 1 0 auto;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 30px 30px 0 0;
|
border-radius: 30px 30px 0 0;
|
||||||
margin-top: -30px;
|
margin-top: -30px;
|
||||||
@@ -455,9 +442,9 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background-color: #fff0f0;
|
background-color: var(--td-brand-color-light);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -475,7 +462,7 @@ body {
|
|||||||
|
|
||||||
.tab.active {
|
.tab.active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #f45a5a;
|
background-color: var(--td-brand-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
.form-container {
|
||||||
@@ -510,7 +497,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.verify-code-btn {
|
.verify-code-btn {
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
@@ -522,7 +509,7 @@ body {
|
|||||||
.submit-btn {
|
.submit-btn {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: #f45a5a;
|
background-color: var(--td-brand-color);
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -538,13 +525,14 @@ body {
|
|||||||
color: #666;
|
color: #666;
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
color: #f45a5a;
|
color: var(--td-brand-color);
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 底部信息样式 */
|
/* 底部信息样式 */
|
||||||
.footer-info {
|
.footer-info {
|
||||||
|
flex-shrink: 0;
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
<view style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||||
<t-icon name="castle-7"></t-icon>
|
<t-icon name="castle-7"></t-icon>
|
||||||
{{ item.coHostUnit }}
|
{{ item.hostUnit }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ async function updateAvatar(avatar) {
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="logout-btn">
|
<view class="logout-btn">
|
||||||
<t-button theme="danger" size="large" block @click="handleLogout">退出登录</t-button>
|
<t-button theme="primary" size="large" block @click="handleLogout">退出登录</t-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="footer">
|
<view class="footer">
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 922 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB |
+20
-20
@@ -1,14 +1,14 @@
|
|||||||
:root, :root[theme-mode="light"] {
|
:root, :root[theme-mode="light"] {
|
||||||
--td-brand-color-1: #fff0ed;
|
--td-brand-color-1: #e8f3ff;
|
||||||
--td-brand-color-2: #ffd8d3;
|
--td-brand-color-2: #cfe7ff;
|
||||||
--td-brand-color-3: #ffb8b1;
|
--td-brand-color-3: #a4d2ff;
|
||||||
--td-brand-color-4: #ff928c;
|
--td-brand-color-4: #70b7ff;
|
||||||
--td-brand-color-5: #ff5f5c;
|
--td-brand-color-5: #1989fa;
|
||||||
--td-brand-color-6: #df3d3f;
|
--td-brand-color-6: #0873d9;
|
||||||
--td-brand-color-7: #b6292d;
|
--td-brand-color-7: #005bb5;
|
||||||
--td-brand-color-8: #8f121b;
|
--td-brand-color-8: #00468c;
|
||||||
--td-brand-color-9: #6b000a;
|
--td-brand-color-9: #003568;
|
||||||
--td-brand-color-10: #490005;
|
--td-brand-color-10: #002447;
|
||||||
--td-brand-color-light: var(--td-brand-color-1);
|
--td-brand-color-light: var(--td-brand-color-1);
|
||||||
--td-brand-color-focus: var(--td-brand-color-2);
|
--td-brand-color-focus: var(--td-brand-color-2);
|
||||||
--td-brand-color-disabled: var(--td-brand-color-3);
|
--td-brand-color-disabled: var(--td-brand-color-3);
|
||||||
@@ -119,16 +119,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root[theme-mode="dark"] {
|
:root[theme-mode="dark"] {
|
||||||
--td-brand-color-1: #ff505020;
|
--td-brand-color-1: #1989fa20;
|
||||||
--td-brand-color-2: #6b000a;
|
--td-brand-color-2: #003568;
|
||||||
--td-brand-color-3: #8f121b;
|
--td-brand-color-3: #00468c;
|
||||||
--td-brand-color-4: #b6292d;
|
--td-brand-color-4: #005bb5;
|
||||||
--td-brand-color-5: #df3d3f;
|
--td-brand-color-5: #0873d9;
|
||||||
--td-brand-color-6: #ff5050;
|
--td-brand-color-6: #1989fa;
|
||||||
--td-brand-color-7: #ff928c;
|
--td-brand-color-7: #70b7ff;
|
||||||
--td-brand-color-8: #ffb8b1;
|
--td-brand-color-8: #a4d2ff;
|
||||||
--td-brand-color-9: #ffd8d3;
|
--td-brand-color-9: #cfe7ff;
|
||||||
--td-brand-color-10: #fff0ed;
|
--td-brand-color-10: #e8f3ff;
|
||||||
--td-brand-color-light: var(--td-brand-color-1);
|
--td-brand-color-light: var(--td-brand-color-1);
|
||||||
--td-brand-color-focus: var(--td-brand-color-2);
|
--td-brand-color-focus: var(--td-brand-color-2);
|
||||||
--td-brand-color-disabled: var(--td-brand-color-3);
|
--td-brand-color-disabled: var(--td-brand-color-3);
|
||||||
|
|||||||
Reference in New Issue
Block a user