3 Commits
Author SHA1 Message Date
c-zhouhf1 2353998794 生日祝福修改 2026-09-10 17:24:55 +08:00
c-zhouhf1 3fc338af1e 生日祝福修改 2026-09-10 14:33:15 +08:00
c-zhouhf1 5ea061ee8e 福利按品牌导出没有收货地址 2026-09-09 14:04:57 +08:00
17 changed files with 575 additions and 123 deletions
@@ -3,10 +3,6 @@ package com.budwk.app.sys.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
@@ -49,7 +45,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
;
@@ -60,8 +55,6 @@ import java.util.Map;
@At("/platform/sys/unit")
public class SysUnitController {
private static final Log log = Logs.get();
/** 浙江交通职业技术学院在 sys_unit 中的单位主键,作为单位树未指定 pid 时的默认根。 */
private static final String DEFAULT_UNIT_ROOT_ID = "4133012036";
@Inject
private SysUnitService sysUnitService;
@Inject
@@ -78,20 +71,17 @@ public class SysUnitController {
}
/**
* @param pageForm 分页参数,pageNumber 为页码,pageSize 为每页条数
* @param unitName 单位名称模糊查询条件,可为空
* @param unitId 上级单位ID,可为空;为空时保留原有单位范围
* @return Resultdata.list 为按生日祝福编号排序的单位,data.totalCount 为总条数
*/
@At("/pageData")
@Ok("json")
@SaCheckLogin
public Object pageData(PageForm pageForm, String unitName, String unitId) {
Cnd cnd = Cnd.NEW();
// cnd.and("parentId", "is not", null).andEX("unitLevel", "=", unitLevel).asc("unitLevel").asc("unitcode");
cnd.and("parentId", "is not", null);
cnd.andEX("parentId", "=", unitId);
// cnd.and("unitTypeCode", "=", "1");
cnd.asc("unitcode");
cnd.and(Cnd.likeEX("name", unitName));
Pagination<Sys_unit> listPage = sysUnitService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), Sys_unit.class, cnd);
return Result.success(listPage);
return Result.success(sysUnitService.pageUnits(pageForm, unitName, unitId));
}
@At("/userPageData")
@@ -297,43 +287,34 @@ public class SysUnitController {
}
}
/**
* @param pid 单位树根ID;为空时使用学校默认根节点
* @param req 当前请求上下文
* @return Resultdata 为含 id、parentId、name、unitTypeCode、children 的单位树列表
*/
@At("/tree")
@Ok("json")
@SaCheckLogin
public Object tree(@Param("pid") String pid, HttpServletRequest req) {
try {
String rootId = StrUtil.blankToDefault(pid, DEFAULT_UNIT_ROOT_ID);
String virtualRootId = "root";
Cnd cnd = Cnd.NEW();
cnd.asc("unitcode");
List<Sys_unit> list = sysUnitService.query(cnd);
List<TreeNode<String>> nodeList = CollUtil.newArrayList();
for (int i = 0; i < list.size(); i++) {
Sys_unit unit = list.get(i);
/*
* 单位根节点存在 id 与 parentId 都为 0 的自引用数据。
* 构建树时把当前查询根挂到虚拟根下,避免“中国地质大学”和 parentId=0 的学院被构造成同级。
*/
String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId();
nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i)
.setExtra(
Map.of(
"unitTypeCode", unit.getUnitTypeCode()
)
));
}
List<Tree<String>> treeList = TreeUtil.build(nodeList, virtualRootId);
return Result.success(treeList);
return Result.success(sysUnitService.getUnitTree(pid));
} catch (Exception e) {
log.error("单位树加载失败,路径=/platform/sys/unit/treepid=" + pid, e);
return Result.error();
}
}
/**
* @param unit 单位表单;name/unitcode 为名称和编码,aliasName 为最长100字简称,birthdaySortNo 为可空整数编号
* @param parentId 上级单位ID,新建时决定所属单位
* @param req 请求上下文;编辑时 birthdaySortNo 传空表示清空,未传表示保留
* @return Resultcode=0 表示保存成功,msg 为操作提示
*/
@At
@Ok("json")
@SaCheckPermission("sys.manager.unit.add")
@SLog(tag = "新建单位", msg = "单位名称:${args[0].name}")
@Aop(TransAop.READ_COMMITTED)
public Object addDo(@Param("..") Sys_unit unit, @Param("parentId") String parentId, HttpServletRequest req) {
try {
if ("root".equals(parentId)) {
@@ -343,6 +324,7 @@ public class SysUnitController {
sysUnitService.save(unit, parentId);
return Result.success();
} catch (Exception e) {
log.error("单位保存失败,接口=addDo,单位ID=" + unit.getId(), e);
return Result.error();
}
}
@@ -373,16 +355,24 @@ public class SysUnitController {
}
}
/**
* @param unit 单位表单;name/unitcode 为名称和编码,aliasName 为最长100字简称,birthdaySortNo 为可空整数编号
* @param parentId 上级单位ID,新建时决定所属单位
* @param req 请求上下文;编辑时 birthdaySortNo 传空表示清空,未传表示保留
* @return Resultcode=0 表示保存成功,msg 为操作提示
*/
@At
@Ok("json")
@SaCheckPermission("sys.manager.unit.edit")
@SLog(tag = "编辑单位", msg = "单位名称:${args[0].name}")
@Aop(TransAop.READ_COMMITTED)
public Object editDo(@Param("..") Sys_unit unit, @Param("parentId") String parentId, HttpServletRequest req) {
try {
unit.setUpdatedBy(SecurityUtil.getUserId());
sysUnitService.updateIgnoreNull(unit);
sysUnitService.updateUnit(unit, req.getParameter("birthdaySortNo") != null);
return Result.success();
} catch (Exception e) {
log.error("单位保存失败,接口=editDo,单位ID=" + unit.getId(), e);
return Result.error();
}
}
@@ -1,5 +1,7 @@
package com.budwk.app.sys.services;
import cn.hutool.core.lang.tree.Tree;
import java.util.List;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
@@ -9,6 +11,27 @@ import com.budwk.app.sys.models.Sys_unit;
* Created by wizzer on 2016/12/22.
*/
public interface SysUnitService extends BaseService<Sys_unit> {
/**
* 按生日祝福编号升序分页;空编号排后,同编号和空编号按单位编码升序。
* @param pageForm 分页参数,pageNumber 为页码,pageSize 为每页条数
* @param unitName 单位名称模糊查询条件,可为空
* @param unitId 上级单位ID,可为空;为空时查询 parentId 非空的单位
* @return 分页结果,list 为当前页单位,totalCount 为符合条件的单位总数
*/
Pagination<Sys_unit> pageUnits(PageForm pageForm, String unitName, String unitId);
/**
* @param pid 查询根单位ID;为空时使用学校默认根
* @return 单位树,节点包含 id、parentId、name、unitTypeCode 和 children;类别允许为空
*/
List<Tree<String>> getUnitTree(String pid);
/**
* @param unit 待编辑单位,id 必填;aliasName 为简称,birthdaySortNo 为可空整数
* @param birthdaySortNoProvided 请求是否提交排序编号;true 时允许将原编号清空,false 时保留原值
*/
void updateUnit(Sys_unit unit, boolean birthdaySortNoProvided);
/**
* 分页查询指定分工会的组成单位。
*
@@ -101,6 +101,11 @@ public class SysMsgServiceImpl extends BaseServiceImpl<Sys_msg> implements SysMs
}
});
// false 仅保存并通知站内消息,供业务自行按渠道发送,避免再次触发学校平台组合推送。
if (!isExternal) {
return sysMsg;
}
//发送学校平台消息
ThreadUtil.execute(() -> {
// 46表示短信和钉钉组合发送;按照消息中心要求,组合中包含短信时手机号必填。
@@ -1,6 +1,13 @@
package com.budwk.app.sys.services.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.constant.RedisConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
@@ -25,6 +32,69 @@ import org.nutz.plugins.wkcache.annotation.CacheRemoveAll;
@IocBean(args = {"refer:dao"})
@CacheDefaults(cacheName = RedisConstant.PLATFORM_REDIS_WKCACHE_PREFIX + "sys_unit",isHash = true)
public class SysUnitServiceImpl extends BaseServiceImpl<Sys_unit> implements SysUnitService {
/** 学校单位主键,未指定 pid 时作为单位树的默认根。 */
private static final String DEFAULT_UNIT_ROOT_ID = "4133012036";
/**
* 单位列表与树共用数据库排序:有编号的优先(含0),编号升序后以单位编码升序兜底。
* @return 仅包含排序规则的新条件对象,可继续追加各自的查询条件
*/
private Cnd unitBirthdayOrder() {
Cnd cnd = Cnd.NEW();
cnd.asc("CASE WHEN birthdaySortNo IS NULL THEN 1 ELSE 0 END")
.asc("birthdaySortNo").asc("unitcode");
return cnd;
}
/** 保留单位名称与上级单位筛选条件,在数据库分页前完成统一排序。 */
@Override
public Pagination<Sys_unit> pageUnits(PageForm pageForm, String unitName, String unitId) {
Cnd cnd = unitBirthdayOrder();
cnd.and("parentId", "is not", null);
cnd.andEX("parentId", "=", unitId);
cnd.and(Cnd.likeEX("name", unitName));
return listPage(pageForm.getPageNumber(), pageForm.getPageSize(), Sys_unit.class, cnd);
}
/** 构建单位树时保留类别空值,避免历史数据因 Map.of 禁止 null 而导致整棵树加载失败。 */
@Override
public List<Tree<String>> getUnitTree(String pid) {
String rootId = StrUtil.blankToDefault(pid, DEFAULT_UNIT_ROOT_ID);
String virtualRootId = "root";
List<Sys_unit> list = query(unitBirthdayOrder());
List<TreeNode<String>> nodeList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
Sys_unit unit = list.get(i);
/*
* 单位根节点存在 id 与 parentId 都为 0 的自引用数据。
* 构建树时把当前查询根挂到虚拟根下,保证当前根节点与其下属单位的层级关系。
*/
String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId();
// 使用查询顺序作为权重,树构建后同级节点仍遵循生日祝福编号排序。
nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i)
.setExtra(
new HashMap<>(java.util.Collections.singletonMap("unitTypeCode", unit.getUnitTypeCode()))
));
}
List<Tree<String>> treeList = TreeUtil.build(nodeList, virtualRootId);
return treeList;
}
/** 编辑单位时保留原有非空更新规则,仅对明确提交的空排序编号执行清空。 */
@Override
@Aop(TransAop.READ_COMMITTED)
public void updateUnit(Sys_unit unit, boolean birthdaySortNoProvided) {
if (unit == null || StrUtil.isBlank(unit.getId())) {
throw new BaseException("单位ID不能为空");
}
updateIgnoreNull(unit);
// 未提交字段的其他调用方不受影响;显式空值需要绕过 updateIgnoreNull。
if (birthdaySortNoProvided && unit.getBirthdaySortNo() == null) {
update(Chain.make("birthdaySortNo", null), Cnd.where("id", "=", unit.getId()));
}
}
public SysUnitServiceImpl(Dao dao) {
super(dao);
}
@@ -141,6 +141,10 @@ public class View_user {
@Column
private String unionCode;
/** 用户视图中的所属校区值,供变更表单回显及原值比较使用。 */
@Column
private String campus;
@Column
private String campusId;
@@ -2,6 +2,13 @@ package com.budwk.app.zhgh.staffmanage.birthday.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.annotation.RepeatSubmit;
import com.budwk.app.base.annotation.SLog;
import cn.hutool.core.util.StrUtil;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.mvc.annotation.POST;
import org.nutz.mvc.annotation.Param;
import com.budwk.app.zhgh.staffmanage.birthday.param.UserBirthdayPageForm;
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayService;
import io.swagger.annotations.Api;
@@ -23,6 +30,27 @@ import javax.servlet.http.HttpServletResponse;
@Api(tags = "生日祝福数据导出")
public class UserBirthdayExportController {
/**
* 修改生日列表中指定人员的所属校区,复用生日导出权限。
*
* @param userId 列表行的人员 ID;工号、姓名由服务端查询,不接收前端覆盖
* @param campusId 校区编码,仅允许 hz(杭州校区)或 cx(长兴校区)
* @return Resultcode=0 表示保存成功,msg 为操作提示,无额外业务数据
*/
@At
@POST
@RepeatSubmit
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("staff.birthday.export")
@SLog(tag = "生日校区维护", msg = "修改人员生日校区")
public Result saveCampus(@Param("userId") String userId, @Param("campusId") String campusId) {
if (StrUtil.isBlank(userId) || !StrUtil.equalsAny(campusId, "hz", "cx")) {
return Result.error("请选择人员和有效的所属校区");
}
userBirthdayService.saveCampus(userId, campusId);
return Result.success("所属校区修改成功");
}
@Inject
private UserBirthdayService userBirthdayService;
@@ -22,6 +22,15 @@ public interface UserBirthdayService extends BaseService<Sys_user> {
*/
Pagination pageData(UserBirthdayPageForm pageForm);
/**
* 按人员工号新增或更新生日校区标记,保留现有备注及创建信息。
*
* @param userId 当前操作者可管理的生日会员 ID
* @param campusId hz 表示杭州校区,cx 表示长兴校区
* @throws com.budwk.app.base.exception.BaseException 参数无效或人员不在可管理范围时抛出
*/
void saveCampus(String userId, String campusId);
/**
* 导出符合查询条件的生日会员。
*
@@ -4,13 +4,19 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.zhgh.staffmanage.birthday.models.UserBirthdayCampus;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.sys.models.Sys_msg;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysMsgService;
import com.budwk.app.web.commons.base.Globals;
import com.budwk.app.zhgh.staffmanage.birthday.models.UserBirthdayConfig;
import com.budwk.app.zhgh.staffmanage.birthday.param.UserBirthdayPageForm;
import com.budwk.app.zhgh.staffmanage.birthday.param.UserBirthdaySendMsgForm;
@@ -63,6 +69,9 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
@Inject
private SysMsgService sysMsgService;
@Inject
private SmsService smsService;
@Inject
private UserBirthdayMsgLogService userBirthdayMsgLogService;
@@ -75,6 +84,60 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), buildBirthdaySql(pageForm));
}
/**
* 校验人员范围并保存生日校区;锁定人员行,使同一人员的并发保存串行执行。
*
* @param userId 列表人员 ID,须属于当前操作者可查询的会员且已维护生日
* @param campusId hz(杭州校区)或 cx(长兴校区),名称由后端确定
* @throws BaseException 人员无效、超出管理范围或校区编码无效
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveCampus(String userId, String campusId) {
if (StrUtil.isBlank(userId) || !StrUtil.equalsAny(campusId, "hz", "cx")) {
throw new BaseException("请选择人员和有效的所属校区");
}
// 即使尚未创建校区标记,也通过已存在的人员行互斥,避免并发插入重复工号标记。
Sql lockSql = Sqls.create("SELECT id FROM sys_user WHERE id = @userId FOR UPDATE");
lockSql.params().set("userId", userId);
lockSql.setCallback(Sqls.callback.str());
dao().execute(lockSql);
if (lockSql.getObject(String.class) == null) {
throw new BaseException("人员不存在");
}
// 复用生日列表的组织权限条件,范围由登录身份确定,不信任客户端传入的组织信息。
Cnd scope = Cnd.where("id", "=", userId).and("member", "=", 1).and("birthday", "is not", null);
new UserBirthdayPageForm().buildSearch(scope, "");
View_user user = dao().fetch(View_user.class, scope);
if (user == null || StrUtil.isBlank(user.getLoginname())) {
throw new BaseException("人员不在可管理的生日会员范围内");
}
List<UserBirthdayCampus> records = dao().query(UserBirthdayCampus.class,
Cnd.where("loginname", "=", user.getLoginname()).desc("updatedAt").asc("id"));
UserBirthdayCampus campus = records.stream()
.filter(item -> !Boolean.TRUE.equals(item.getDelFlag()))
.findFirst().orElse(records.isEmpty() ? new UserBirthdayCampus() : records.get(0));
boolean insert = StrUtil.isBlank(campus.getId());
campus.setLoginname(user.getLoginname());
campus.setUsername(user.getUsername());
campus.setCampusId(campusId);
campus.setCampusName("cx".equals(campusId) ? "长兴校区" : "杭州校区");
campus.setDelFlag(false);
if (insert) {
dao().insert(campus);
} else {
dao().update(campus);
}
// 当前工号若已有重复有效记录,仅保留本次更新的记录,其余逻辑删除,避免列表联表重复。
for (UserBirthdayCampus duplicate : records) {
if (!duplicate.getId().equals(campus.getId()) && !Boolean.TRUE.equals(duplicate.getDelFlag())) {
duplicate.setDelFlag(true);
dao().update(duplicate);
}
}
}
@Override
public void exportXlsx(UserBirthdayPageForm pageForm, HttpServletResponse response) {
try {
@@ -95,7 +158,19 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
// 当前列表按查询校区生成顶部标题,未指定校区时使用全校标题。
exportParams.setTitle(buildSignatureTitle(getCampusDisplayName(pageForm.getCampusName())));
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, list)) {
// 调整当前工作簿的全部字体,保留各字体原有的加粗、颜色和字体名称。
for (int fontIndex = 0; fontIndex < workbook.getNumberOfFontsAsInt(); fontIndex++) {
workbook.getFontAt(fontIndex).setFontHeightInPoints((short) 16);
}
// 字号增大后为每行保留至少 28 磅高度,已有更高的标题或数据行不缩小。
for (Sheet sheet : workbook) {
for (Row row : sheet) {
row.setHeightInPoints(Math.max(row.getHeightInPoints(), 28));
}
}
CommonDownloadUtil.download("生日祝福人员名单.xlsx", workbook, response);
}
} catch (Exception e) {
@@ -433,9 +508,9 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
sheet.setColumnWidth(7, 16 * 256);
CellStyle titleStyle = createCellStyle(workbook, true, (short) 16, false);
CellStyle subtitleStyle = createCellStyle(workbook, false, (short) 12, false);
CellStyle headerStyle = createCellStyle(workbook, true, (short) 11, true);
CellStyle contentStyle = createCellStyle(workbook, false, (short) 11, true);
CellStyle subtitleStyle = createCellStyle(workbook, false, (short) 16, false);
CellStyle headerStyle = createCellStyle(workbook, true, (short) 16, true);
CellStyle contentStyle = createCellStyle(workbook, false, (short) 16, true);
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(28);
@@ -443,13 +518,13 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 7));
Row subtitleRow = sheet.createRow(1);
subtitleRow.setHeightInPoints(22);
subtitleRow.setHeightInPoints(28);
createCell(subtitleRow, 0, buildSignatureSubtitle(displayPeriod), subtitleStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 7));
String[] headers = {"序号", "部门", "姓名", "签名", "序号", "部门", "姓名", "签名"};
Row headerRow = sheet.createRow(2);
headerRow.setHeightInPoints(22);
headerRow.setHeightInPoints(28);
for (int i = 0; i < headers.length; i++) {
createCell(headerRow, i, headers[i], headerStyle);
}
@@ -457,7 +532,7 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
int leftSize = (list.size() + 1) / 2;
for (int i = 0; i < leftSize; i++) {
Row row = sheet.createRow(i + 3);
row.setHeightInPoints(22);
row.setHeightInPoints(28);
fillSignatureRow(row, 0, i, getListItem(list, i), contentStyle);
fillSignatureRow(row, 4, i + leftSize, getListItem(list, i + leftSize), contentStyle);
}
@@ -551,10 +626,10 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
}
if (start.getYear() == end.getYear()) {
return start.format(DateTimeFormatter.ofPattern("yyyy年M月")) + "-" +
end.format(DateTimeFormatter.ofPattern("M月"));
end.format(DateTimeFormatter.ofPattern("M月"));
}
return start.format(DateTimeFormatter.ofPattern("yyyy年M月")) + "-" +
end.format(DateTimeFormatter.ofPattern("yyyy年M月"));
end.format(DateTimeFormatter.ofPattern("yyyy年M月"));
}
private String buildSignatureDisplayPeriod(LocalDate start, LocalDate end) {
@@ -563,10 +638,10 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
}
if (start.getYear() == end.getYear()) {
return start.format(DateTimeFormatter.ofPattern("yyyy年M")) + "-" +
end.format(DateTimeFormatter.ofPattern("M月"));
end.format(DateTimeFormatter.ofPattern("M月"));
}
return start.format(DateTimeFormatter.ofPattern("yyyy年M月")) + "-" +
end.format(DateTimeFormatter.ofPattern("yyyy年M月"));
end.format(DateTimeFormatter.ofPattern("yyyy年M月"));
}
private String buildSignatureDateDisplayPeriod(LocalDate start, LocalDate end) {
@@ -575,21 +650,22 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
}
if (isSameMonth(start, end)) {
return start.format(DateTimeFormatter.ofPattern("yyyy年M月d日")) + "-" +
end.format(DateTimeFormatter.ofPattern("d日"));
end.format(DateTimeFormatter.ofPattern("d日"));
}
if (start.getYear() == end.getYear()) {
return start.format(DateTimeFormatter.ofPattern("yyyy年M月d日")) + "-" +
end.format(DateTimeFormatter.ofPattern("M月d日"));
end.format(DateTimeFormatter.ofPattern("M月d日"));
}
return start.format(DateTimeFormatter.ofPattern("yyyy年M月d日")) + "-" +
end.format(DateTimeFormatter.ofPattern("yyyy年M月d日"));
end.format(DateTimeFormatter.ofPattern("yyyy年M月d日"));
}
/** 统一列表和签名表的顶部标题,校区取按钮参数或列表筛选条件。 */
private String buildSignatureTitle(String campusDisplayName) {
if (StrUtil.isBlank(campusDisplayName)) {
return "浙江交通职业技术学院职工生日蛋糕券发放名单";
return "浙江交通职业技术学院职工座谈会及生日蛋糕券发放名单";
}
return "浙江交通职业技术学院" + campusDisplayName + "职工生日蛋糕券发放名单";
return "浙江交通职业技术学院" + campusDisplayName + "职工座谈会及生日蛋糕券发放名单";
}
private String buildSignatureSubtitle(String displayPeriod) {
@@ -643,8 +719,9 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
/**
* 将生日消息写入当前项目消息中心并生成发送记录。
* 发送内容只包含标题和正文,不向站内、短信或钉钉渠道传递生日页面链接。
* 单人、批量和自动发送共用此入口:站内保留跳转地址,短信仅发祝福正文,钉钉正文附带生日链接。
*/
@Aop(TransAop.READ_COMMITTED)
private int sendMessages(List<String> loginNames, String title, String content,
String pushBy, String pushByName, String pushType) {
List<String> recipients = loginNames == null ? List.of() : loginNames.stream()
@@ -655,17 +732,50 @@ public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implement
return 0;
}
// 链接携带发送时的福利贺卡文件 ID;未配置时仍进入生日页面,由页面展示未配置提示。
String fileId = StrUtil.blankToDefault(getConfig().getBirthdayUrl(), "");
String link = StrUtil.removeSuffix(Globals.AppDomain, "/")
+ "/platform/staffManage/birthday/manage/h5";
Sys_msg message = new Sys_msg();
message.setTitle(title);
message.setNote(content);
message.setUrl(link);
message.setType("user");
message.setSendType("show");
message.setSendAt(Times.getTS());
message.setCreatedBy(pushBy);
sysMsgService.saveMsg(message, recipients.toArray(String[]::new), true);
userBirthdayMsgLogService.insertLogs(recipients, title, content, "",
pushBy, pushByName, pushType);
// 短信接收人需携带工号和手机号,沿用消息中心按工号分组的查询范围。
Sql receiverSql = Sqls.create("SELECT loginname, mobile FROM vw_user $condition");
receiverSql.setCondition(Cnd.where("loginname", "in", recipients).groupBy("loginname"));
List<NutMap> receivers = listMap(receiverSql).stream()
.map(user -> NutMap.NEW()
.addv("userId", user.getString("loginname"))
.addv("mobile", user.getString("mobile"))
.addv("email", "")
.addv("flag", 0))
.toList();
if (!receivers.isEmpty()) {
// 分别提交渠道任务,任一渠道失败不会阻断另一渠道;实际结果由学校消息服务分别留存。
ThreadUtil.execute(() -> {
boolean success = smsService.sendMsg("4", null, receivers, title, content, null, null);
if (!success) {
log.warn("生日短信发送未成功,消息ID:{},请查看学校平台发送记录", message.getId());
}
});
ThreadUtil.execute(() -> {
// 当前钉钉使用文本消息,链接明确写入正文,跳转参数留空以避免平台重复追加。
boolean success = smsService.sendMsg("6", null, receivers, title, content, null, link);
if (!success) {
log.warn("生日钉钉发送未成功,消息ID:{},请查看学校平台发送记录", message.getId());
} else {
userBirthdayMsgLogService.insertLogs(recipients, title, content, link,
pushBy, pushByName, pushType);
}
});
}
return recipients.size();
}
}
@@ -87,6 +87,7 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
u.unitName,
u.unionId,
u.unionName,
u.campus,
u.birthday,
u.manyUnit,
u.member,
@@ -267,6 +268,7 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
NutMap newMap = Lang.obj2nutmap(record);
// 原数据
NutMap sourceMap = Lang.obj2nutmap(info);
// 原数据已映射 campus,直接与表单同名字段比较,避免被 campusId 覆盖而误报异动。
List<NutMap> changeList = new ArrayList<>();
for (String fieldName : allowChangeFieldNames) {
@@ -1,5 +1,7 @@
package com.budwk.app.zhgh.welfare.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.welfare.service.WelfareProjectService;
@@ -27,6 +29,7 @@ public class WelfareNoticeController {
@At
@Ok("beetl:/platform/zhghh5/welfare/notice/detail.html")
@ApiOperation("福利通知附件详情页")
@SaCheckLogin
public void index() {
}
@@ -38,6 +41,7 @@ public class WelfareNoticeController {
*/
@At
@ApiOperation("查询福利通知附件详情")
@SaCheckLogin
public Result detailData(@Valid String id) {
if (StrUtil.isBlank(id)) {
return Result.error("福利项目ID不能为空");
@@ -586,6 +586,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
u.loginname AS loginName,
u.username AS userName,
wpus.mobile,
wpus.userName AS recipient,
wl.userState,
wl.personType,
wl.welfareUnionName,
@@ -635,12 +636,23 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
return unionId;
}
/** 将历史收货信息拆为 V3 导出所需的收货人、联系方式和收货地址三列。 */
/**
* 填充品牌导出的收货信息。当前选择记录分别保存收货人、手机号和纯地址文本;
* 历史记录可能将三项信息拼接在 receiveAddress 中,因此继续兼容旧格式解析。
*/
private void fillV3ReceiveAddress(NutMap row) {
String mobile = row.getString("mobile");
if (StrUtil.isNotBlank(mobile)) {
row.put("phone", mobile);
}
String receiveAddress = row.getString("receiveAddress");
if (StrUtil.isBlank(receiveAddress)) {
return;
}
// 新数据的 receiveAddress 仅保存地址正文,可直接作为收货地址导出。
row.put("address", receiveAddress);
Map<String, String> values = new HashMap<>();
for (String pair : receiveAddress.split("[,]")) {
String[] keyValue = pair.split("[:]", 2);
@@ -648,9 +660,19 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
values.put(keyValue[0].trim(), keyValue[1].trim());
}
}
row.put("recipient", values.get(""));
row.put("phone", values.get("联系方式"));
row.put("address", values.get("收货地址"));
String recipient = StrUtil.blankToDefault(values.get("收件人"), values.get(""));
String phone = StrUtil.blankToDefault(values.get("联系方式"),
StrUtil.blankToDefault(values.get("手机号码"), values.get("手机号")));
String address = StrUtil.blankToDefault(values.get("收货地址"), values.get("详细地址"));
if (StrUtil.isNotBlank(recipient)) {
row.put("recipient", recipient);
}
if (StrUtil.isNotBlank(phone)) {
row.put("phone", phone);
}
if (StrUtil.isNotBlank(address)) {
row.put("address", address);
}
}
/**
+89 -9
View File
@@ -1,17 +1,85 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false">
<!-- 定义日志文件的存储路径 -->
<!-- 日志存储路径 -->
<property name="LOG_HOME" value="./logs"/>
<!-- 控制台输出配置 -->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志文件配置 -->
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)start #
# #
#################################################################################
-->
<!-- 所有级别日志写入同一个文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- 框架日志级别(按需调整) -->
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- root loggerDEBUG 级别,输出到控制台和全量文件 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL_FILE"/>
</root>
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)end #
# #
#################################################################################
-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 start #
# #
#################################################################################
-->
<!-- DEBUG级别日志文件配置 -->
<!--<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; INFO级别日志文件配置 &ndash;&gt;
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<encoder>
@@ -24,11 +92,11 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- ERROR级别日志文件配置 -->
&lt;!&ndash; ERROR级别日志文件配置 &ndash;&gt;
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<encoder>
@@ -41,19 +109,31 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<logger name="java" additivity="false" />
&lt;!&ndash; 框架日志级别 &ndash;&gt;
&lt;!&ndash; <logger name="java" additivity="false" />&ndash;&gt;
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- 日志级别和appender的关联 -->
&lt;!&ndash; 日志级别和appender的关联 &ndash;&gt;
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</root>-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 end #
# #
#################################################################################
-->
</configuration>
@@ -16,6 +16,8 @@ const UNIT_MANAGE_TEMPLATE = {
<template v-slot="scope">{{scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</template>
</el-table-column>
<el-table-column label="单位名称" prop="name"></el-table-column>
<el-table-column label="简称" prop="aliasName" min-width="120"></el-table-column>
<el-table-column label="生日祝福排序编号" prop="birthdaySortNo" width="160"></el-table-column>
<el-table-column label="单位代码" prop="unitcode" width="200"></el-table-column>
<el-table-column label="单位等级" prop="unitLevel" width="100"></el-table-column>
<el-table-column label="操作" width="200px">
@@ -29,16 +31,21 @@ const UNIT_MANAGE_TEMPLATE = {
</el-card>
<el-dialog title="设置单位" :visible.sync="dialogVisible" :close-on-click-modal="false" width="40%" top="2%">
<el-form :model="formData" ref="form" :rules="formRules" size="small" label-width="80px">
<el-form :model="formData" ref="form" :rules="formRules" size="small" label-width="150px">
<el-form-item label="上级单位">
<el-input maxlength="100" disabled :value="currentData?.name" type="text"></el-input>
</el-form-item>
<el-form-item prop="name" label="单位名称">
<el-input maxlength="100" placeholder="请输入单位名称" v-model="formData.name" auto-complete="off" tabindex="2" type="text"></el-input>
</el-form-item>
<!-- <el-form-item prop="aliasName" label="单位别名">-->
<!-- <el-input maxlength="100" placeholder="请输入单位别名" v-model="formData.aliasName" auto-complete="off" tabindex="3" type="text"></el-input>-->
<!-- </el-form-item>-->
<!-- 简称与生日祝福排序编号复用单位实体字段;排序编号允许留空,控件与其他表单项等宽对齐。 -->
<el-form-item prop="aliasName" label="简称">
<el-input maxlength="100" placeholder="请输入简称" v-model="formData.aliasName" clearable></el-input>
</el-form-item>
<el-form-item prop="birthdaySortNo" label="生日祝福排序编号">
<el-input-number v-model="formData.birthdaySortNo" :precision="0" :step="1" style="width: 100%"
:min="-2147483648" :max="2147483647" placeholder="请输入排序编号"></el-input-number>
</el-form-item>
<el-form-item prop="unitcode" label="单位编码">
<el-input maxlength="100" placeholder="请输入单位编码" v-model="formData.unitcode" auto-complete="off" tabindex="4" type="text"></el-input>
</el-form-item>
@@ -75,10 +82,11 @@ const UNIT_MANAGE_TEMPLATE = {
<!-- </el-form-item>-->
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="doHandle">确 定</el-button>
<el-button @click="closeDialog" :disabled="formLoading">取 消</el-button>
<el-button type="primary" @click="doHandle" :loading="formLoading">确 定</el-button>
</span>
</el-dialog>
<slot></slot>
</div>
`,
props: {
@@ -111,6 +119,7 @@ const UNIT_MANAGE_TEMPLATE = {
},
tableData: [],
dialogVisible: false,
formLoading: false,
formData: {},
formRules: {
name: [{ required: true, message: "必填", trigger: "blur" }],
@@ -129,51 +138,71 @@ const UNIT_MANAGE_TEMPLATE = {
},
computed: {},
methods: {
// 通过组件方法关闭弹框,确保 $set 使用当前组件实例。
closeDialog() {
this.$set(this, "dialogVisible", false)
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
openAdd() {
this.dialogVisible = true
this.formData = {} //打开新增窗口,表单先清空
// 新建时清空扩展字段,避免沿用上一次编辑的值。
this.$set(this, "formData", { aliasName: "", birthdaySortNo: undefined })
this.parentUnit = []
this.options = []
},
doHandle() {
if (this.formLoading) return
this.$confirm("确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
const self = this
const url = this.formData.id ? "/platform/sys/unit/editDo" : "/platform/sys/unit/addDo"
this.$refs["form"].validate(async function (valid) {
if (valid) {
if (self.currentNode) {
self.formData.parentId = self.currentData.id
}
const resp = await self.$axios.post(url, self.formData)
if (resp.code === 0) {
self.$message.success(resp.msg)
self.doSearch()
self.dialogVisible = false
self.$emit("refresh", null)
} else {
self.$message.warning(resp.msg)
}
this.$refs["form"].validate((valid) => {
if (!valid || this.formLoading) return
const url = this.formData.id ? "/platform/sys/unit/editDo" : "/platform/sys/unit/addDo"
if (this.currentNode) {
this.$set(this.formData, "parentId", this.currentData.id)
}
// 明确提交空字符串表示清空;避免 undefined 被请求序列化忽略。
const params = Object.assign({}, this.formData, {
aliasName: this.formData.aliasName || "",
birthdaySortNo: this.formData.birthdaySortNo == null ? "" : this.formData.birthdaySortNo
})
this.formLoading = true
this.$axios.post(url, params).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
this.$set(this, "dialogVisible", false)
this.$emit("refresh", null)
} else {
this.$message.warning(resp.msg)
}
}).finally(() => {
this.formLoading = false
})
})
})
},
async openEdit(id) {
console.log(this.currentData)
const resp = await this.$axios.post("/platform/sys/unit/edit/" + id)
if (resp.code === 0) {
this.formData = resp.data
this.dialogVisible = true
} else {
this.$message.warning(resp.msg)
}
openEdit(id) {
if (this.formLoading) return
this.formLoading = true
this.$axios.post("/platform/sys/unit/edit/" + id).then((resp) => {
if (resp.code === 0 && resp.data) {
this.$set(this, "formData", resp.data)
// 兼容历史空值,保留编号 0;数字控件使用 undefined 显示空输入。
this.$set(this.formData, "aliasName", resp.data.aliasName || "")
this.$set(this.formData, "birthdaySortNo", resp.data.birthdaySortNo == null ? undefined : resp.data.birthdaySortNo)
this.$set(this, "dialogVisible", true)
} else {
this.$message.warning(resp.msg)
}
}).finally(() => {
this.formLoading = false
})
},
async doDelete(id) {
const confirm = await this.$confirm("此操作将永久删除, 是否继续?", "提示", {
@@ -55,10 +55,36 @@ layout("/layouts/platform.html"){
:label="column.label" :width="column.width" :fixed="column.fixed"
:sortable="column.sortable"
header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" fixed="right" width="150" align="center">
<template slot-scope="{row}">
<el-button type="text" size="small" @click="openCampusForm(row)">修改所属校区</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
<el-dialog title="修改所属校区" :visible.sync="campusDialogVisible" width="480px"
:close-on-click-modal="false" :close-on-press-escape="!formLoading" :show-close="!formLoading">
<el-form ref="campusFormRef" :model="campusForm" :rules="campusRules" label-width="100px">
<el-form-item label="工号">
<el-input :value="campusForm.loginname" readonly></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input :value="campusForm.username" readonly></el-input>
</el-form-item>
<el-form-item label="所属校区" prop="campusId">
<el-select v-model="campusForm.campusId" :disabled="formLoading" placeholder="请选择所属校区" style="width:100%">
<el-option v-for="item in campuses" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<div class="text-muted">此设置用于生日名单及签名表。</div>
</el-form>
<template slot="footer">
<el-button :disabled="formLoading" @click="closeCampusForm">取消</el-button>
<el-button type="primary" :loading="formLoading" @click="saveCampus">保存</el-button>
</template>
</el-dialog>
</guava>
</div>
@@ -86,6 +112,11 @@ layout("/layouts/platform.html"){
},
queryForm: {userStates:[],personTypes:[]},
birthdayRange: [],
campusDialogVisible: false,
campusForm: {userId:"",loginname:"",username:"",campusId:""},
campusRules: {
campusId: [{required:true,message:"请选择所属校区",trigger:"change"}]
},
campuses: [
{id:"hz",name:"杭州校区"},
{id:"cx",name:"长兴校区"}
@@ -109,6 +140,48 @@ layout("/layouts/platform.html"){
}
},
methods: {
// 回显列表中实际生效的生日校区,包括未设置标记时按单位推导出的校区。
openCampusForm(row) {
const campus = this.campuses.find(item => item.name === row.campusName || item.id === row.campusName)
this.$set(this, "campusForm", {
userId: row.id,
loginname: row.loginname,
username: row.username,
campusId: campus ? campus.id : ""
})
this.$set(this, "campusDialogVisible", true)
this.$nextTick(() => {
if (this.$refs.campusFormRef) this.$refs.campusFormRef.clearValidate()
})
},
// 保存中禁止关闭,避免请求未完成时切换到其他人员。
closeCampusForm() {
if (!this.formLoading) this.$set(this, "campusDialogVisible", false)
},
// 仅提交人员 ID 和校区编码,工号、姓名及校区名称由后端读取并校验。
saveCampus() {
if (this.formLoading) return
this.$refs.campusFormRef.validate((valid) => {
if (!valid) return
this.$set(this, "formLoading", true)
this.$axios.post("/platform/staffManage/birthday/export/saveCampus", {
userId: this.campusForm.userId,
campusId: this.campusForm.campusId
}).then((res) => {
if (res.code === 0) {
this.$message.success("所属校区修改成功")
this.$set(this, "campusDialogVisible", false)
this.doSearch()
} else {
this.$message.error(res.msg || "所属校区修改失败")
}
}).catch(() => {
this.$message.error("所属校区修改失败,请稍后重试")
}).finally(() => {
this.$set(this, "formLoading", false)
})
})
},
initOrganizationOptions() {
if (this.$auth.hasRoleOr("SYSADMIN, SCHOOL_UNION_ADMIN, SCHOOL_UNION_MEMBER_ADMIN")) {
this.$businessTool.listUnion().then((data) => {
@@ -110,7 +110,7 @@ const MEMBER_CHANGE = {
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item v-else label="所属校区">
<el-descriptions-item v-else-if="!replaceWelfareWithCampus" label="所属校区">
<el-form-item prop="campus">
<dict-select style="width: 100%" placeholder="请选择所属校区" v-model="formData.campus"
:disabled="allowFields('campus')" code="USER_CAMPUS"></dict-select>
@@ -196,7 +196,14 @@ const MEMBER_CHANGE = {
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="福利会员状态">
<!-- 管理页在福利会员位置展示校区,保留三级单位;其他调用方沿用原字段布局。 -->
<el-descriptions-item v-if="replaceWelfareWithCampus" label="所属校区">
<el-form-item prop="campus">
<dict-select style="width: 100%" placeholder="请选择所属校区" v-model="formData.campus"
:disabled="allowFields('campus')" code="USER_CAMPUS"></dict-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item v-else label="福利会员状态">
<el-form-item prop="welfareMember">
<el-radio-group v-model="formData.welfareMember" size="small" class="welfare-member-radio-group"
style="display: flex; align-items: center; flex-wrap: nowrap; white-space: nowrap;">
@@ -288,6 +295,8 @@ const MEMBER_CHANGE = {
props: {
id: { type: String, default: '' },
showThreeUnit: { type: Boolean, default: false },
// 仅管理页启用福利会员与校区的展示替换,默认保持共用页面行为。
replaceWelfareWithCampus: { type: Boolean, default: false },
},
mixins: [initTableMixins],
store,
@@ -406,9 +415,7 @@ const MEMBER_CHANGE = {
.then((resp) => {
if (resp.code === 0) {
this.threeUnits = resp.data || []
if (this.threeUnits.length === 0) {
this.$message.warning("当前所属单位未配置三级单位")
}
// 未配置三级单位时保持空选项,允许继续编辑其他人员信息。
} else {
this.$message.error(resp.msg || "三级单位查询失败")
}
@@ -525,6 +532,7 @@ const MEMBER_CHANGE = {
this.$set(this.formData, "position", position)
this.$set(this.formData, "education", education)
this.$set(this.formData, "academicDegree", academicDegree)
// 与编辑接口及变更记录统一使用 campus,正确回显当前人员所属校区。
this.$set(this.formData, "campus", campus)
this.$set(this.formData, "threeUnitId", threeUnitId)
this.$set(this.formData, "userState", userState)
@@ -185,16 +185,9 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="福利会员" prop="member"
<!-- 所属校区展示列表接口返回的 campus 字段。 -->
<el-table-column align="center" header-align="center" label="所属校区" prop="campus"
show-overflow-tooltip sortable>
<template scope="{row}">
<span class="text-success " v-if="row.welfareMember==1">
<i class="fa fa-circle ml5"></i>
</span>
<span class="text-danger" v-else>
<i class="fa fa-circle ml5"></i>
</span>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="操作"
@@ -227,7 +220,7 @@ layout("/layouts/platform.html"){
</template>
<template #public>
<member-change ref="memberChangeRef" :show-three-unit="true" @do-back="$refs.guava.index()" @do-submit="doSubmit"></member-change>
<member-change ref="memberChangeRef" :show-three-unit="true" :replace-welfare-with-campus="true" @do-back="$refs.guava.index()" @do-submit="doSubmit"></member-change>
</template>
<template #edit>
@@ -22,7 +22,7 @@ layout("/layouts/platform_h5.html"){
@keyframes confetti-fall { 0% { transform: translate3d(0,-20px,0) rotate(0); opacity: 0; }
12% { opacity: 1; } 100% { transform: translate3d(35px,62vh,0) rotate(540deg); opacity: 0; } }
.greeting-zone { position: relative; z-index: 4; height: 48%; display: flex; flex-direction: column; align-items: center;
justify-content: flex-start; padding: 0 24px calc(20px + env(safe-area-inset-bottom)); box-sizing: border-box; }
justify-content: flex-start; padding: 24px 24px calc(20px + env(safe-area-inset-bottom)); box-sizing: border-box; /* 下移祝福卡片,为蛋糕底部留出间距。 */ }
.greeting-card { width: 100%; box-sizing: border-box; padding: 20px 20px 16px; border: 1px solid rgba(164,60,26,.3);
border-radius: 18px; background: rgba(255,249,228,.92); box-shadow: 0 12px 30px rgba(109,29,10,.22);
text-align: center; animation: card-enter .7s ease-out both; }
@@ -115,7 +115,7 @@ layout("/layouts/platform_h5.html"){
生日快乐,幸福安康!
</div>
</div>
<button class="receive-button" @click="openCoupon">领取生日福利</button>
<button v-if="hasCouponId" class="receive-button" @click="openCoupon">领取生日福利</button>
</div>
<button type="button" class="music-button" :class="{playing:musicPlaying}"
@@ -165,6 +165,8 @@ layout("/layouts/platform_h5.html"){
return {
appName: "${AppName!}",
userName: "${userName!}",
// 仅地址参数 id 非空时提供福利入口,后台配置的贺卡不作为按钮显示依据。
hasCouponId: "${fileId!}".trim().length > 0,
pageLoading: false,
showMusicPrompt: false,
musicPromptResolved: false,
@@ -230,7 +232,7 @@ layout("/layouts/platform_h5.html"){
}
},
openCoupon() {
if (this.showCoupon) return
if (!this.hasCouponId || this.showCoupon) return
this.$set(this, "showCoupon", true)
window.h5PopupHistory.open(this.popupHistoryToken, "birthdayCoupon")
},