查询的时间段改成两个字段;会员数据更新记录增加导出;场地预约增加单位预约和审核流程

This commit is contained in:
2026-09-15 17:42:21 +08:00
parent 216329a345
commit b16443e1b7
24 changed files with 564 additions and 336 deletions
@@ -56,6 +56,11 @@ public class ActivitySiteReserve extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 50) @ColDefine(type = ColType.VARCHAR, width = 50)
private String reserve_person_unit; private String reserve_person_unit;
@Column
@Comment("申请时所属单位ID,单位预约审核按此快照匹配负责人")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String reserveUnitId;
@Column @Column
@Comment("预约人联系方式") @Comment("预约人联系方式")
@ColDefine(type = ColType.VARCHAR, width = 20) @ColDefine(type = ColType.VARCHAR, width = 20)
@@ -137,7 +142,7 @@ public class ActivitySiteReserve extends BaseModel {
private String joinUser; private String joinUser;
@Column @Column
@Comment("预约类型(1.个人预约,2.分工会预约,3.协会预约)") @Comment("预约类型(1.个人预约,2.分工会预约,3.协会预约4.单位预约)")
@ColDefine(type = ColType.INT, width = 10) @ColDefine(type = ColType.INT, width = 10)
private Integer reserve_type; private Integer reserve_type;
} }
@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
*/ */
@IocBean(args = {"refer:dao"}) @IocBean(args = {"refer:dao"})
public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> { public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
public static final int UNION = 4000, CLUB = 4010, SCHOOL = 4020, PASS = 4030, REJECT = 4040, CANCEL = 4050; public static final int UNION = 4000, CLUB = 4010, SCHOOL = 4020, PASS = 4030, REJECT = 4040, CANCEL = 4050, UNIT = 4060;
/** 校工会节点统一按专属场地管理员角色分配待办和校验审核资格。 */ /** 校工会节点统一按专属场地管理员角色分配待办和校验审核资格。 */
private static final String SCHOOL_REVIEW_ROLE = "SchoolUnionActivityVenueAdmin"; private static final String SCHOOL_REVIEW_ROLE = "SchoolUnionActivityVenueAdmin";
/** 兼容现有日历组件的状态字段;名称统一读取 state,不再读取 audit_state。 */ /** 兼容现有日历组件的状态字段;名称统一读取 state,不再读取 audit_state。 */
@@ -52,24 +52,25 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
+ "order by c.name").setParam("uid", uid())); + "order by c.name").setParam("uid", uid()));
} }
/** 无入参,返回当前申请人的id/name所属分工会;采用与提交相同的user视图组织来源,未配置时返回空字符串。 */ /** 无入参,返回当前申请人的id/name所属分工会及unitId/unitName所属单位;采用与提交相同的user视图组织来源,未配置时返回空字符串。 */
public NutMap myBookingUnion() { public NutMap myBookingUnion() {
List<NutMap> rows=listMap(Sqls.create("select u.unionid id,un.unionname name from `user` u left join sys_union un on un.id=u.unionid where u.id=@id") List<NutMap> rows=listMap(Sqls.create("select u.unionid id,un.unionname name,u.unitid unitId,u.unitname unitName from `user` u left join sys_union un on un.id=u.unionid where u.id=@id")
.setParam("id",uid())); .setParam("id",uid()));
return rows.isEmpty() ? NutMap.NEW().setv("id","").setv("name","") : rows.get(0); return rows.isEmpty() ? NutMap.NEW().setv("id","").setv("name","").setv("unitId","").setv("unitName","") : rows.get(0);
} }
/** 预约类型 1 历史个人、2 分工会、3 协会,返回应进入的首个状态 ID。 */ /** 预约类型 1 历史个人、2 分工会、3 协会、4 单位,返回应进入的首个状态 ID。 */
public int firstState(Integer type) { public int firstState(Integer type) {
if (Integer.valueOf(1).equals(type)) return UNION; if (Integer.valueOf(1).equals(type)) return UNION;
if (Integer.valueOf(2).equals(type)) return UNION; if (Integer.valueOf(2).equals(type)) return UNION;
if (Integer.valueOf(3).equals(type)) return CLUB; if (Integer.valueOf(3).equals(type)) return CLUB;
if (Integer.valueOf(4).equals(type)) return UNIT;
throw new IllegalArgumentException("请选择有效的预约类型"); throw new IllegalArgumentException("请选择有效的预约类型");
} }
/** 按节点角色及组织范围查实际审核人;不允许空组织退化为全校范围。 */ /** 按节点角色及组织范围查实际审核人;不允许空组织退化为全校范围。 */
private List<NutMap> reviewers(int node, ActivitySiteReserve booking) { private List<NutMap> reviewers(int node, ActivitySiteReserve booking) {
String role = node == UNION ? "gh01" : node == CLUB ? "club01" : SCHOOL_REVIEW_ROLE; String role = node == UNION ? "gh01" : node == CLUB ? "club01" : node == UNIT ? "unit_manger" : SCHOOL_REVIEW_ROLE;
String filter = ""; String filter = "";
if (node == UNION) { if (node == UNION) {
if (Strings.isBlank(booking.getReserve_person_unionid())) throw new IllegalArgumentException("申请人未关联分工会,请先完善所属分工会"); if (Strings.isBlank(booking.getReserve_person_unionid())) throw new IllegalArgumentException("申请人未关联分工会,请先完善所属分工会");
@@ -77,11 +78,15 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
} else if (node == CLUB) { } else if (node == CLUB) {
if (Strings.isBlank(booking.getClubId())) throw new IllegalArgumentException("请选择所属协会"); if (Strings.isBlank(booking.getClubId())) throw new IllegalArgumentException("请选择所属协会");
filter = " and ur.stid=@org"; filter = " and ur.stid=@org";
} else if (node == UNIT) {
// 负责人必须绑定申请时的单位,不能使用其任职单位或仅凭角色放行。
if (Strings.isBlank(booking.getReserveUnitId())) throw new IllegalArgumentException("申请人未关联单位,请先完善所属单位");
filter = " and ur.unitid=@org and coalesce(r.disabled,0)=0";
} else if (node != SCHOOL) throw new IllegalArgumentException("当前节点不允许审核"); } else if (node != SCHOOL) throw new IllegalArgumentException("当前节点不允许审核");
return listMap(Sqls.create("select distinct u.id,u.loginname from sys_user_role ur join sys_role r on r.id=ur.roleId " return listMap(Sqls.create("select distinct u.id,u.loginname from sys_user_role ur join sys_role r on r.id=ur.roleId "
+ "join sys_user u on u.id=ur.userId where r.code=@role and coalesce(u.disabled,0)=0 " + "join sys_user u on u.id=ur.userId where r.code=@role and coalesce(u.disabled,0)=0 "
+ "and coalesce(u.delFlag,0)=0" + filter).setParam("role", role) + "and coalesce(u.delFlag,0)=0" + filter).setParam("role", role)
.setParam("org", node == UNION ? booking.getReserve_person_unionid() : booking.getClubId())); .setParam("org", node == UNION ? booking.getReserve_person_unionid() : node == UNIT ? booking.getReserveUnitId() : booking.getClubId()));
} }
/** 返回非空审核人工号名单,缺少节点负责人时中止事务并提示具体节点。 */ /** 返回非空审核人工号名单,缺少节点负责人时中止事务并提示具体节点。 */
@@ -89,20 +94,21 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
List<String> names = reviewers(node, booking).stream().map(v -> v.getString("loginname")) List<String> names = reviewers(node, booking).stream().map(v -> v.getString("loginname"))
.filter(Strings::isNotBlank).distinct().collect(Collectors.toList()); .filter(Strings::isNotBlank).distinct().collect(Collectors.toList());
if (names.isEmpty()) throw new IllegalArgumentException(node == UNION ? "所属分工会未配置可用的分工会主席" if (names.isEmpty()) throw new IllegalArgumentException(node == UNION ? "所属分工会未配置可用的分工会主席"
: node == CLUB ? "所选协会未配置可用的协会会长" : "未配置可用的校工会场地管理员"); : node == CLUB ? "所选协会未配置可用的协会会长" : node == UNIT ? "所属单位未配置可用的二级单位负责人" : "未配置可用的校工会场地管理员");
return names; return names;
} }
/** stages 是后端固定入口 union/club/school,返回节点 ID,禁止任意传入状态码。 */ /** stages 是后端固定入口 union/club/unit/school,返回节点 ID,禁止任意传入状态码。 */
public int node(String stage) { public int node(String stage) {
if ("union".equals(stage)) return UNION; if ("union".equals(stage)) return UNION;
if ("club".equals(stage)) return CLUB; if ("club".equals(stage)) return CLUB;
if ("school".equals(stage)) return SCHOOL; if ("school".equals(stage)) return SCHOOL;
if ("unit".equals(stage)) return UNIT;
throw new IllegalArgumentException("无效的审核入口"); throw new IllegalArgumentException("无效的审核入口");
} }
private String stage(int node) { return node == UNION ? "union" : node == CLUB ? "club" : "school"; } private String stage(int node) { return node == UNION ? "union" : node == CLUB ? "club" : node == UNIT ? "unit" : "school"; }
private String title(int node) { return node == UNION ? "分工会审核" : node == CLUB ? "协会审核" : "校工会审核"; } private String title(int node) { return node == UNION ? "分工会审核" : node == CLUB ? "协会审核" : node == UNIT ? "单位审核" : "校工会审核"; }
private String process(ActivitySiteReserve b) { return "activity_site@" + b.getSqid(); } private String process(ActivitySiteReserve b) { return "activity_site@" + b.getSqid(); }
/** /**
@@ -128,9 +134,11 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
form.setReserve_person_phone(user.getMobile()); form.setReserve_person_phone(user.getMobile());
// user 视图按特殊人员工会关系、所属单位计算有效分工会;原表 unionid 可能为空或为历史值。 // user 视图按特殊人员工会关系、所属单位计算有效分工会;原表 unionid 可能为空或为历史值。
// 提交时保存有效组织快照,后续待办和审核范围均使用该快照匹配主席角色。 // 提交时保存有效组织快照,后续待办和审核范围均使用该快照匹配主席角色。
List<NutMap> profile=listMap(Sqls.create("select unionid,unitname from `user` where id=@id").setParam("id",uid())); List<NutMap> profile=listMap(Sqls.create("select unionid,unitid,unitname from `user` where id=@id").setParam("id",uid()));
form.setReserve_person_unionid(profile.isEmpty() ? null : profile.get(0).getString("unionid")); form.setReserve_person_unionid(profile.isEmpty() ? null : profile.get(0).getString("unionid"));
form.setReserve_person_unit(profile.isEmpty() ? "" : profile.get(0).getString("unitname")); form.setReserve_person_unit(profile.isEmpty() ? "" : profile.get(0).getString("unitname"));
// 所属单位由登录人资料生成,不接受客户端指定其他单位。
form.setReserveUnitId(profile.isEmpty() ? null : profile.get(0).getString("unitid"));
if (first == CLUB && myClubs().stream().noneMatch(v -> v.getString("id").equals(form.getClubId()))) if (first == CLUB && myClubs().stream().noneMatch(v -> v.getString("id").equals(form.getClubId())))
throw new IllegalArgumentException("您不是所选协会的有效成员,请重新选择所属协会"); throw new IllegalArgumentException("您不是所选协会的有效成员,请重新选择所属协会");
if (first != CLUB) form.setClubId(null); if (first != CLUB) form.setClubId(null);
@@ -164,9 +172,9 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
List<ActivitySiteReserve> existing = query(Cnd.where("site_id", "=", site.getId()).and("reserve_day", "=", day) List<ActivitySiteReserve> existing = query(Cnd.where("site_id", "=", site.getId()).and("reserve_day", "=", day)
.and("start_time", "<", end).and("end_time", ">", start).and("reserve_state", "not in", Arrays.asList(REJECT, CANCEL))); .and("start_time", "<", end).and("end_time", ">", start).and("reserve_state", "not in", Arrays.asList(REJECT, CANCEL)));
if (existing.stream().anyMatch(v -> uid().equals(v.getReserve_person_id()))) throw new IllegalArgumentException("您已预约该时间段"); if (existing.stream().anyMatch(v -> uid().equals(v.getReserve_person_id()))) throw new IllegalArgumentException("您已预约该时间段");
if (!personal && !existing.isEmpty()) throw new IllegalArgumentException("该时段已有预约,协会或分工会预约需要空闲时段"); if (!personal && !existing.isEmpty()) throw new IllegalArgumentException("该时段已有预约,分工会、协会或单位预约需要空闲时段");
if (personal && (existing.stream().anyMatch(v -> !Integer.valueOf(1).equals(v.getReserve_type())) if (personal && (existing.stream().anyMatch(v -> !Integer.valueOf(1).equals(v.getReserve_type()))
|| site.getLimitNum() == null || siteInfoService.occupiedCount(existing,start,end) >= site.getLimitNum())) throw new IllegalArgumentException("该时段预约人数已满或已被分工会/协会预约"); || site.getLimitNum() == null || siteInfoService.occupiedCount(existing,start,end) >= site.getLimitNum())) throw new IllegalArgumentException("该时段预约人数已满或已被分工会协会、单位预约");
} }
String sqid = UUID.randomUUID().toString().replace("-", ""); String sqid = UUID.randomUUID().toString().replace("-", "");
for (NutMap slot : slots) { for (NutMap slot : slots) {
@@ -174,7 +182,7 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
row.setSqid(sqid); row.setSite_id(site.getId()); row.setReserve_type(form.getReserve_type()); row.setSqid(sqid); row.setSite_id(site.getId()); row.setReserve_type(form.getReserve_type());
row.setClubId(form.getClubId()); row.setReserve_person_unionid(form.getReserve_person_unionid()); row.setClubId(form.getClubId()); row.setReserve_person_unionid(form.getReserve_person_unionid());
row.setReserve_person_id(uid()); row.setReserve_person(user.getUsername()); row.setReserve_person_id(uid()); row.setReserve_person(user.getUsername());
row.setReserve_person_phone(user.getMobile()); row.setReserve_person_unit(form.getReserve_person_unit()); row.setReserve_person_phone(user.getMobile()); row.setReserve_person_unit(form.getReserve_person_unit()); row.setReserveUnitId(form.getReserveUnitId());
row.setReserve_cause(form.getReserve_cause()); row.setReserve_state(first); row.setReserve_cause(form.getReserve_cause()); row.setReserve_state(first);
row.setReserve_day(slot.getString("day")); row.setStart_time(slot.getString("start_time")); row.setEnd_time(slot.getString("end_time")); row.setReserve_day(slot.getString("day")); row.setStart_time(slot.getString("start_time")); row.setEnd_time(slot.getString("end_time"));
dao().insert(row); dao().insert(row);
@@ -240,8 +248,9 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
} }
} }
/** 返回指定审核入口的数据范围,分工会/协会使用角色关系上的组织字段。 */ /** 返回指定审核入口的数据范围,分工会协会和单位均匹配角色关系上的对应组织字段。 */
private String scope(int node) { private String scope(int node) {
if (node == UNIT) return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='unit_manger' and coalesce(r.disabled,0)=0 and ur.unitid=b.reserveUnitId)";
if (node == SCHOOL) return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='" + SCHOOL_REVIEW_ROLE + "')"; if (node == SCHOOL) return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='" + SCHOOL_REVIEW_ROLE + "')";
return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='" return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='"
+ (node == UNION ? "gh01" : "club01") + "' and " + (node == UNION ? "gh01" : "club01") + "' and "
@@ -362,7 +371,7 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
if (b == null) throw new IllegalArgumentException("预约不存在"); if (b == null) throw new IllegalArgumentException("预约不存在");
boolean allowed = uid().equals(b.getReserve_person_id()); boolean allowed = uid().equals(b.getReserve_person_id());
for (int node : new java.util.LinkedHashSet<>(Arrays.asList(firstState(b.getReserve_type()), SCHOOL))) { for (int node : new java.util.LinkedHashSet<>(Arrays.asList(firstState(b.getReserve_type()), SCHOOL))) {
if (node == UNION && Strings.isBlank(b.getReserve_person_unionid()) || node == CLUB && Strings.isBlank(b.getClubId())) continue; if (node == UNION && Strings.isBlank(b.getReserve_person_unionid()) || node == CLUB && Strings.isBlank(b.getClubId()) || node == UNIT && Strings.isBlank(b.getReserveUnitId())) continue;
allowed |= reviewers(node,b).stream().anyMatch(v -> uid().equals(v.getString("id"))); allowed |= reviewers(node,b).stream().anyMatch(v -> uid().equals(v.getString("id")));
} }
List<NutMap> history = b.getAuditList() == null ? new ArrayList<>() : b.getAuditList(); List<NutMap> history = b.getAuditList() == null ? new ArrayList<>() : b.getAuditList();
@@ -398,7 +407,7 @@ public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
listMap(Sqls.create("select id from activity_site_reserve where sqid=@sqid order by id for update").setParam("sqid",b.getSqid())); listMap(Sqls.create("select id from activity_site_reserve where sqid=@sqid order by id for update").setParam("sqid",b.getSqid()));
b = fetch(id); b = fetch(id);
if (!Integer.valueOf(node).equals(b.getReserve_state())) throw new IllegalArgumentException("申请已处理或不属于当前审核节点,请刷新列表"); if (!Integer.valueOf(node).equals(b.getReserve_state())) throw new IllegalArgumentException("申请已处理或不属于当前审核节点,请刷新列表");
if (reviewers(node,b).stream().noneMatch(v -> uid().equals(v.getString("id")))) throw new IllegalArgumentException("无权审核该分工会或协会的申请"); if (reviewers(node,b).stream().noneMatch(v -> uid().equals(v.getString("id")))) throw new IllegalArgumentException("无权审核该组织的申请");
int next = !pass ? REJECT : node == SCHOOL ? PASS : SCHOOL; int next = !pass ? REJECT : node == SCHOOL ? PASS : SCHOOL;
if (next == SCHOOL) assignments(SCHOOL,b); if (next == SCHOOL) assignments(SCHOOL,b);
Audit audit = new Audit(); Audit audit = new Audit();
@@ -207,7 +207,7 @@ public class SiteInfoMobileController {
@At("/getReserve") @At("/getReserve")
@Ok("json:full") @Ok("json:full")
@ViReturn @ViReturn
/** siteId为场地,day为日期,reserveType为1个人/2单位/3协会;返回带code/msg的可预约场次数组。 */ /** siteId为场地,day为日期,reserveType为1历史个人/2分工会/3协会/4单位;返回带code/msg的可预约场次数组。 */
public Object getReserve(String siteId, String day, Integer reserveType) { public Object getReserve(String siteId, String day, Integer reserveType) {
return siteInfoService.availableSlots(siteId,day,reserveType); return siteInfoService.availableSlots(siteId,day,reserveType);
} }
@@ -215,7 +215,7 @@ public class SiteInfoMobileController {
@At("/reserveDo") @At("/reserveDo")
@Ok("json:full") @Ok("json:full")
@ViReturn @ViReturn
/** siteId 为场地;times 为 day/start_time/end_time 数组 JSONmessage 为事由;reserve_type 为1/2/3,clubId 为协会预约所属协会;返回 sqid。 */ /** siteId 为场地;times 为 day/start_time/end_time 数组 JSONmessage 为事由;reserve_type 为1历史个人/2分工会/3协会/4单位,clubId 为协会预约所属协会;返回 sqid。 */
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public Object reserveDo(String siteId, String times, String message, Integer reserve_type, String clubId) { public Object reserveDo(String siteId, String times, String message, Integer reserve_type, String clubId) {
ActivitySiteReserve form=new ActivitySiteReserve(); ActivitySiteReserve form=new ActivitySiteReserve();
@@ -1,14 +1,9 @@
package io.v.nutz.zhgh.staffmanage.sourcechange.controller; package io.v.nutz.zhgh.staffmanage.sourcechange.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result; import cn.wizzer.framework.base.Result;
import io.v.nutz.base.annontation.ViReturn; import io.v.nutz.base.annontation.ViReturn;
import io.v.nutz.base.utils.PageUtil;
import io.v.nutz.sys.models.User; import io.v.nutz.sys.models.User;
import io.v.nutz.web.commons.slog.annotation.SLog; import io.v.nutz.web.commons.slog.annotation.SLog;
import io.v.nutz.zhgh.staffmanage.member.constant.MemberChangeType;
import io.v.nutz.zhgh.staffmanage.member.service.MemberCommonService;
import io.v.nutz.zhgh.staffmanage.sourcechange.model.SourceChangeMiddleTable; import io.v.nutz.zhgh.staffmanage.sourcechange.model.SourceChangeMiddleTable;
import io.v.nutz.zhgh.staffmanage.sourcechange.param.pageform.SourceChangePageForm; import io.v.nutz.zhgh.staffmanage.sourcechange.param.pageform.SourceChangePageForm;
import io.v.nutz.zhgh.staffmanage.sourcechange.service.SourceChangeManageService; import io.v.nutz.zhgh.staffmanage.sourcechange.service.SourceChangeManageService;
@@ -16,10 +11,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang; import org.nutz.lang.Lang;
@@ -29,7 +20,6 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** /**
* @version 1.0 * @version 1.0
@@ -47,8 +37,6 @@ public class SourceChangeManageController {
@Inject @Inject
private Dao dao; private Dao dao;
@Inject @Inject
private MemberCommonService commonService;
@Inject
private SourceChangeManageService sourceChangeManageService; private SourceChangeManageService sourceChangeManageService;
@@ -59,67 +47,29 @@ public class SourceChangeManageController {
} }
/**
* @param pageForm 列表筛选、排序及分页参数;日期字段为 yyyy-MM-ddchangeTypes 为类型编码集合
* @return 分页对象,list 为变更记录,totalCount 为符合条件的记录总数
*/
@At @At
@ViReturn @ViReturn
@RequiresPermissions("sourcechange.manage") @RequiresPermissions("sourcechange.manage")
public Object pageData(SourceChangePageForm pageForm){ public Object pageData(SourceChangePageForm pageForm){
Sql sql = Sqls.create(""" return sourceChangeManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(),
SELECT sourceChangeManageService.buildQuery(pageForm));
scmt.id, }
scmt.loginname,
scmt.username,
u.sex,
u.unitName,
u.unionName,
scmt.userState,
scmt.personType,
scmt.preparedBy,
scmt.changeTime,
scmt.changeTypes,
scmt.changeOrigin,
scmt.isOperate,
scmt.changeInfosStr,
u.id AS userId
FROM
source_change_middle_table scmt
LEFT JOIN `user` u ON scmt.loginname = u.loginname
$condition
""");
Cnd cnd = Cnd.NEW();
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username",pageForm.getSearchKeyword());
seg.orLike("u.loginname",pageForm.getSearchKeyword());
cnd.and(seg);
}
if (StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("changeTime");
}
if (Lang.isNotEmpty(pageForm.getChangeTypes())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
for (String type : pageForm.getChangeTypes()) {
seg.or(new Static("JSON_CONTAINS(scmt.changeTypes, JSON_QUOTE('%s'), '$')".formatted(type)));
}
cnd.and(seg);
}
cnd.andEX("u.unionId", "=", pageForm.getUnionId()); /**
cnd.andEX("u.unitId", "=", pageForm.getUnitId()); * 按列表筛选条件导出全部变更记录,不受分页或勾选行限制。
cnd.andEX("u.sex", "=", pageForm.getSex()); * @param pageForm 姓名工号、单位工会、人员属性、变更类型、起止日期和操作状态,与列表参数一致
cnd.andEX("scmt.personType", "=", pageForm.getPersonType()); * @param response 输出人员变更记录.xlsx,列为列表展示字段,变更类型使用中文名称
cnd.andEX("scmt.preparedBy", "=", pageForm.getPreparedBy()); */
cnd.andEX("scmt.userState", "=", pageForm.getUserState()); @At
cnd.andEX("scmt.isOperate", "=", pageForm.getIsOperate()); @Ok("void")
if (StrUtil.isAllNotBlank(pageForm.getChangeDateBefore(), pageForm.getChangeDateEnd())) { @RequiresPermissions("sourcechange.manage")
cnd.and(new Static(String.format("Date(changeTime) >= '%s' and Date(changeTime) <= '%s'", pageForm.getChangeDateBefore(), pageForm.getChangeDateEnd()))); public void doExport(SourceChangePageForm pageForm, javax.servlet.http.HttpServletResponse response) {
} else { sourceChangeManageService.exportXlsx(pageForm, response);
cnd.and("Date(changeTime)", "=", DateUtil.today());
}
sql.setCondition(cnd);
return commonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
} }
@@ -5,7 +5,9 @@ import io.v.nutz.sys.models.User;
import io.v.nutz.zhgh.data.model.UserHistory; import io.v.nutz.zhgh.data.model.UserHistory;
import io.v.nutz.zhgh.staffmanage.sourcechange.model.SourceChangeMiddleTable; import io.v.nutz.zhgh.staffmanage.sourcechange.model.SourceChangeMiddleTable;
import java.util.Date; import io.v.nutz.zhgh.staffmanage.sourcechange.param.pageform.SourceChangePageForm;
import org.nutz.dao.sql.Sql;
import javax.servlet.http.HttpServletResponse;
/** /**
* @version 1.0 * @version 1.0
@@ -16,6 +18,21 @@ import java.util.Date;
*/ */
public interface SourceChangeManageService extends BaseService<SourceChangeMiddleTable> { public interface SourceChangeManageService extends BaseService<SourceChangeMiddleTable> {
/**
* 生成列表及导出共用的查询,保留原有筛选及排序规则。
* @param pageForm 人员、日期、类型、操作状态及排序条件;分页参数由列表调用方处理
* @return 未设置分页的 SQL,结果包含人员信息、变更类型、明细及变更时间
*/
Sql buildQuery(SourceChangePageForm pageForm);
/**
* 导出当前条件下的全部变更记录。
* @param pageForm 与列表相同的筛选条件,忽略 pageNumber 和 pageSize
* @param response 输出 xlsx 文件,列与列表一致,类型编码转换为中文
*/
void exportXlsx(SourceChangePageForm pageForm, HttpServletResponse response);
/** /**
* 人工变更,做变更 * 人工变更,做变更
* @param middleTable * @param middleTable
@@ -1,6 +1,18 @@
package io.v.nutz.zhgh.staffmanage.sourcechange.service.impl; package io.v.nutz.zhgh.staffmanage.sourcechange.service.impl;
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.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import io.v.nutz.base.utils.CommonDownloadUtil;
import io.v.nutz.base.utils.PageUtil;
import io.v.nutz.zhgh.staffmanage.sourcechange.param.pageform.SourceChangePageForm;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.json.Json;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
@@ -68,6 +80,108 @@ public class SourceChangeManageServiceImpl extends BaseServiceImpl<SourceChangeM
private MsgApi msgApi; private MsgApi msgApi;
/** {@inheritDoc} */
@Override
public Sql buildQuery(SourceChangePageForm pageForm) {
// 列表和导出使用同一查询;这里不设置分页,避免导出仅包含当前页。
Sql sql = Sqls.create("""
SELECT
scmt.id,
scmt.loginname,
scmt.username,
u.sex,
u.unitName,
u.unionName,
scmt.userState,
scmt.personType,
scmt.preparedBy,
scmt.changeTime,
scmt.changeTypes,
scmt.changeOrigin,
scmt.isOperate,
scmt.changeInfosStr,
u.id AS userId
FROM
source_change_middle_table scmt
LEFT JOIN `user` u ON scmt.loginname = u.loginname
$condition
""");
Cnd cnd = Cnd.NEW();
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username",pageForm.getSearchKeyword());
seg.orLike("u.loginname",pageForm.getSearchKeyword());
cnd.and(seg);
}
if (StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("changeTime");
}
if (Lang.isNotEmpty(pageForm.getChangeTypes())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
for (String type : pageForm.getChangeTypes()) {
seg.or(new Static("JSON_CONTAINS(scmt.changeTypes, JSON_QUOTE('%s'), '$')".formatted(type)));
}
cnd.and(seg);
}
cnd.andEX("u.unionId", "=", pageForm.getUnionId());
cnd.andEX("u.unitId", "=", pageForm.getUnitId());
cnd.andEX("u.sex", "=", pageForm.getSex());
cnd.andEX("scmt.personType", "=", pageForm.getPersonType());
cnd.andEX("scmt.preparedBy", "=", pageForm.getPreparedBy());
cnd.andEX("scmt.userState", "=", pageForm.getUserState());
cnd.andEX("scmt.isOperate", "=", pageForm.getIsOperate());
if (StrUtil.isAllNotBlank(pageForm.getChangeDateBefore(), pageForm.getChangeDateEnd())) {
cnd.and(new Static(String.format("Date(changeTime) >= '%s' and Date(changeTime) <= '%s'", pageForm.getChangeDateBefore(), pageForm.getChangeDateEnd())));
} else {
cnd.and("Date(changeTime)", "=", DateUtil.today());
}
sql.setCondition(cnd);
return sql;
}
/** {@inheritDoc} */
@Override
public void exportXlsx(SourceChangePageForm pageForm, HttpServletResponse response) {
List<NutMap> rows = listMap(buildQuery(pageForm));
Map<String, String> typeNames = Arrays.stream(MemberChangeType.values())
.collect(Collectors.toMap(MemberChangeType::getType, MemberChangeType::getChangeTypeName));
for (NutMap row : rows) {
// 空类型保持空白;历史未知编码保留原值,避免导出丢失已有信息。
String rawTypes = row.getString("changeTypes");
List<String> types = StrUtil.isBlank(rawTypes) ? Collections.emptyList()
: Json.fromJsonAsList(String.class, rawTypes);
row.put("changeTypes", types == null ? "" : types.stream()
.map(type -> typeNames.getOrDefault(type, type)).collect(Collectors.joining("")));
Object changeTime = row.get("changeTime");
if (changeTime instanceof Date) {
row.put("changeTime", DateUtil.format((Date) changeTime, "yyyy-MM-dd HH:mm:ss"));
}
}
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("工号", "loginname", 20));
columns.add(new ExcelExportEntity("姓名", "username", 15));
columns.add(new ExcelExportEntity("在职状态", "userState", 15));
columns.add(new ExcelExportEntity("人员类型", "personType", 20));
// JDBC 返回的列名为 unionname,与列表字段一致;Excel 按键名取值时区分大小写。
columns.add(new ExcelExportEntity("所属工会", "unionname", 25));
columns.add(new ExcelExportEntity("变更详细信息", "changeInfosStr", 80));
columns.add(new ExcelExportEntity("变更类型", "changeTypes", 30));
columns.add(new ExcelExportEntity("变更时间", "changeTime", 25));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
// 即使没有匹配记录也生成包含表头的文件,保持下载行为一致。
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, rows)) {
CommonDownloadUtil.download("人员变更记录.xlsx", workbook, response);
} catch (Exception e) {
log.error("导出人员变更记录失败", e);
throw new RuntimeException("导出人员变更记录失败", e);
}
}
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public void doSourceChange(SourceChangeMiddleTable middleTable) { public void doSourceChange(SourceChangeMiddleTable middleTable) {
@@ -33,7 +33,7 @@
<el-tab-pane label="预约基本信息" name="2"> <el-tab-pane label="预约基本信息" name="2">
<el-descriptions class="margin-top" :column="3" border> <el-descriptions class="margin-top" :column="3" border>
<el-descriptions-item label="预约类型">{{ {1:'个人预约',2:'分工会预约',3:'协会预约'}[viewData.reserve_type] }}</el-descriptions-item> <el-descriptions-item label="预约类型">{{ {1:'个人预约',2:'分工会预约',3:'协会预约',4:'单位预约'}[viewData.reserve_type] }}</el-descriptions-item>
<el-descriptions-item label="所属协会">{{viewData.club_name || '—'}}</el-descriptions-item> <el-descriptions-item label="所属协会">{{viewData.club_name || '—'}}</el-descriptions-item>
<el-descriptions-item label="预约人">{{ viewData.reserve_person }}</el-descriptions-item> <el-descriptions-item label="预约人">{{ viewData.reserve_person }}</el-descriptions-item>
<el-descriptions-item :span="2" label="场地名称"> {{ viewData.site_name }}</el-descriptions-item> <el-descriptions-item :span="2" label="场地名称"> {{ viewData.site_name }}</el-descriptions-item>
@@ -150,12 +150,12 @@
this.clearPopupHistory(() => pjaxReplace('/mobile/index')) this.clearPopupHistory(() => pjaxReplace('/mobile/index'))
} }
}, },
typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约'}[type] || '—' }, typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约',4:'单位预约'}[type] || '—' },
filterOpened() { this.$set(this, 'filterShow', true) }, filterOpened() { this.$set(this, 'filterShow', true) },
filterClosed() { this.$set(this, 'filterShow', false) }, filterClosed() { this.$set(this, 'filterShow', false) },
canReview(row) { canReview(row) {
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1) const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage] return Number(row.reserve_state) === {union:4000,club:4010,unit:4060,school:4020}[stage]
}, },
search() { search() {
// 筛选变更使旧响应失效,避免未审核和已审核数据混入同一列表。 // 筛选变更使旧响应失效,避免未审核和已审核数据混入同一列表。
@@ -46,7 +46,7 @@ const SITE_AUDIT_INFO = {
props: {reviewApi: {type: String, required: true}}, props: {reviewApi: {type: String, required: true}},
data() { return {viewData: {}, activeName: 'basic', expanded: true, detailLoading: false, requestId: 0} }, data() { return {viewData: {}, activeName: 'basic', expanded: true, detailLoading: false, requestId: 0} },
methods: { methods: {
typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约'}[type] || '—' }, typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约',4:'单位预约'}[type] || '—' },
toggleExpanded() { this.$set(this, 'expanded', !this.expanded) }, toggleExpanded() { this.$set(this, 'expanded', !this.expanded) },
// id 为预约记录主键;返回预约字段及 auditListTable 历史,loaded 通知父页核验可审核状态。 // id 为预约记录主键;返回预约字段及 auditListTable 历史,loaded 通知父页核验可审核状态。
onOpen(id) { onOpen(id) {
@@ -270,8 +270,9 @@ layout("/mobile/platform.html"){
</van-cell> </van-cell>
<van-cell title="预约地点">{{site.name}}</van-cell> <van-cell title="预约地点">{{site.name}}</van-cell>
<van-cell title="预约类型" required :value="reserveTypeName" is-link class="reserve-choice" @click="openOptions('type')"></van-cell> <van-cell title="预约类型" required :value="reserveTypeName" is-link class="reserve-choice" @click="openOptions('type')"></van-cell>
<!-- 分工会使用后端查询的当前组织,只读显示;协会仅在协会预约时选择。 --> <!-- 分工会和单位使用后端查询的当前组织,只读显示;协会仅在协会预约时选择。 -->
<van-cell title="所属分工会" v-if="reserve_type===2" :value="unionLoading ? '加载中...' : bookingUnionName || '未配置所属分工会'"></van-cell> <van-cell title="所属分工会" v-if="reserve_type===2" :value="unionLoading ? '加载中...' : bookingUnionName || '未配置所属分工会'"></van-cell>
<van-cell title="所属单位" v-if="reserve_type===4" :value="unionLoading ? '加载中...' : bookingUnitName || '未配置所属单位'"></van-cell>
<van-cell title="所属协会" v-if="reserve_type===3" class="reserve-choice" <van-cell title="所属协会" v-if="reserve_type===3" class="reserve-choice"
:value="reserve_type===3 ? selectedClubName : myClubs.map(club=>club.name).join('、')" :value="reserve_type===3 ? selectedClubName : myClubs.map(club=>club.name).join('、')"
:is-link="reserve_type===3" @click="openOptions('club')"></van-cell> :is-link="reserve_type===3" @click="openOptions('club')"></van-cell>
@@ -342,9 +343,9 @@ layout("/mobile/platform.html"){
return { return {
historyPopupKeys: ['show','calendarVisible','optionsShow'], historyPopupKeys: ['show','calendarVisible','optionsShow'],
optionsShow: false, optionKind: 'type', optionsShow: false, optionKind: 'type',
// 新申请开放分工会协会,个人类型保留用于历史数据。 // 新申请开放分工会协会和单位,个人类型保留用于历史数据。
reserveTypeOptions: [{text:'分工会预约',value:2},{text:'协会预约',value:3}], reserveTypeOptions: [{text:'分工会预约',value:2},{text:'协会预约',value:3},{text:'单位预约',value:4}],
bookingUnionName:'', unionLoading:false, bookingUnionName:'', bookingUnitName:'', unionLoading:false,
myClubs: [], clubId: '', formLoading: false, myClubs: [], clubId: '', formLoading: false,
// 新预约须主动选择类型,初始不选中任何选项。 // 新预约须主动选择类型,初始不选中任何选项。
reserve_type: null, reserve_type: null,
@@ -492,7 +493,10 @@ layout("/mobile/platform.html"){
this.$set(this,'unionLoading',true) this.$set(this,'unionLoading',true)
$.post('/platform/activity/site/reserve/myUnion').then(res=>{ $.post('/platform/activity/site/reserve/myUnion').then(res=>{
if(this._isDestroyed || this._isBeingDestroyed)return if(this._isDestroyed || this._isBeingDestroyed)return
if(res && res.code===0)this.$set(this,'bookingUnionName',res.data && res.data.name || '') if(res && res.code===0) {
this.$set(this,'bookingUnionName',res.data && res.data.name || '')
this.$set(this,'bookingUnitName',res.data && res.data.unitName || '')
}
else vant.Toast(res && res.msg || '所属分工会加载失败') else vant.Toast(res && res.msg || '所属分工会加载失败')
},()=>{if(!this._isDestroyed && !this._isBeingDestroyed)vant.Toast('所属分工会加载失败,请重新进入')}).always(()=>{ },()=>{if(!this._isDestroyed && !this._isBeingDestroyed)vant.Toast('所属分工会加载失败,请重新进入')}).always(()=>{
if(!this._isDestroyed && !this._isBeingDestroyed)this.$set(this,'unionLoading',false) if(!this._isDestroyed && !this._isBeingDestroyed)this.$set(this,'unionLoading',false)
@@ -3,4 +3,5 @@
<!--# if(@shiro.hasPermission('activity.site.review.union')){ #--><p><a href="/platform/activity/site/review/union">分工会审核</a> · <a href="/mobile/activity/site/audit/union">手机端</a></p><!--# } #--> <!--# if(@shiro.hasPermission('activity.site.review.union')){ #--><p><a href="/platform/activity/site/review/union">分工会审核</a> · <a href="/mobile/activity/site/audit/union">手机端</a></p><!--# } #-->
<!--# if(@shiro.hasPermission('activity.site.review.club')){ #--><p><a href="/platform/activity/site/review/club">协会审核</a> · <a href="/mobile/activity/site/audit/club">手机端</a></p><!--# } #--> <!--# if(@shiro.hasPermission('activity.site.review.club')){ #--><p><a href="/platform/activity/site/review/club">协会审核</a> · <a href="/mobile/activity/site/audit/club">手机端</a></p><!--# } #-->
<!--# if(@shiro.hasPermission('activity.site.review.school')){ #--><p><a href="/platform/activity/site/review/school">校工会审核</a> · <a href="/mobile/activity/site/audit/school">手机端</a></p><!--# } #--> <!--# if(@shiro.hasPermission('activity.site.review.school')){ #--><p><a href="/platform/activity/site/review/school">校工会审核</a> · <a href="/mobile/activity/site/audit/school">手机端</a></p><!--# } #-->
<!--# if(@shiro.hasPermission('activity.site.review.unit')){ #--><p><a href="/platform/activity/site/review/unit">单位审核</a> · <a href="/mobile/activity/site/audit/unit">手机端</a></p><!--# } #-->
</body></html> </body></html>
@@ -498,12 +498,17 @@ layout("/layouts/platform.html"){
<!--<el-radio-button :label="1">个人预约</el-radio-button>--> <!--<el-radio-button :label="1">个人预约</el-radio-button>-->
<el-radio-button :label="2">分工会预约</el-radio-button> <el-radio-button :label="2">分工会预约</el-radio-button>
<el-radio-button :label="3">协会预约</el-radio-button> <el-radio-button :label="3">协会预约</el-radio-button>
<el-radio-button :label="4">单位预约</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 分工会由后端按当前申请人解析,只读展示,不接受前端指定组织。 --> <!-- 分工会由后端按当前申请人解析,只读展示,不接受前端指定组织。 -->
<el-form-item label="所属分工会" v-if="formData.reserve_type===2"> <el-form-item label="所属分工会" v-if="formData.reserve_type===2">
<el-input :value="bookingUnionName" readonly :placeholder="unionLoading ? '正在加载所属分工会' : '未配置所属分工会'"></el-input> <el-input :value="bookingUnionName" readonly :placeholder="unionLoading ? '正在加载所属分工会' : '未配置所属分工会'"></el-input>
</el-form-item> </el-form-item>
<!-- 单位取当前登录人资料,实际审核归属由后端保存单位快照。 -->
<el-form-item label="所属单位" v-if="formData.reserve_type===4">
<el-input :value="bookingUnitName" readonly :placeholder="unionLoading ? '正在加载所属单位' : '未配置所属单位'"></el-input>
</el-form-item>
<!-- 所属协会仅用于协会预约。 --> <!-- 所属协会仅用于协会预约。 -->
<el-form-item label="所属协会" v-if="formData.reserve_type===3"> <el-form-item label="所属协会" v-if="formData.reserve_type===3">
<el-select v-model="formData.clubId" placeholder="请选择所属协会" style="width:100%"> <el-select v-model="formData.clubId" placeholder="请选择所属协会" style="width:100%">
@@ -546,7 +551,7 @@ layout("/layouts/platform.html"){
max: 24, max: 24,
min: 0, min: 0,
myClubs: [], myClubs: [],
bookingUnionName:'', unionLoading:false, unionRequestVersion:0, bookingUnionName:'', bookingUnitName:'', unionLoading:false, unionRequestVersion:0,
bookingSlots:[], slotsLoading:false, slotsVersion:0, bookingSlots:[], slotsLoading:false, slotsVersion:0,
bookingTab:'time', bookingRangeStart:'', bookingRangeEnd:'', slotsLoadFailed:false, bookingTab:'time', bookingRangeStart:'', bookingRangeEnd:'', slotsLoadFailed:false,
formLoading: false, formLoading: false,
@@ -718,10 +723,14 @@ layout("/layouts/platform.html"){
const version=this.unionRequestVersion+1 const version=this.unionRequestVersion+1
this.$set(this,'unionRequestVersion',version) this.$set(this,'unionRequestVersion',version)
this.$set(this,'bookingUnionName','') this.$set(this,'bookingUnionName','')
this.$set(this,'bookingUnitName','')
this.$set(this,'unionLoading',true) this.$set(this,'unionLoading',true)
$.post('/platform/activity/site/reserve/myUnion').then(res=>{ $.post('/platform/activity/site/reserve/myUnion').then(res=>{
if(version!==this.unionRequestVersion)return if(version!==this.unionRequestVersion)return
if(res && res.code===0)this.$set(this,'bookingUnionName',res.data && res.data.name || '') if(res && res.code===0) {
this.$set(this,'bookingUnionName',res.data && res.data.name || '')
this.$set(this,'bookingUnitName',res.data && res.data.unitName || '')
}
else this.$message.error(res && res.msg || '所属分工会加载失败') else this.$message.error(res && res.msg || '所属分工会加载失败')
},()=>{if(version===this.unionRequestVersion)this.$message.error('所属分工会加载失败,请重新打开表单')}).always(()=>{ },()=>{if(version===this.unionRequestVersion)this.$message.error('所属分工会加载失败,请重新打开表单')}).always(()=>{
if(version===this.unionRequestVersion)this.$set(this,'unionLoading',false) if(version===this.unionRequestVersion)this.$set(this,'unionLoading',false)
@@ -151,11 +151,11 @@
}) })
}).catch(() => {}) }).catch(() => {})
}, },
typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约'}[type] || '—' }, typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约',4:'单位预约'}[type] || '—' },
// 以申请实际节点决定审核入口,已流转的申请只允许查看。 // 以申请实际节点决定审核入口,已流转的申请只允许查看。
canReview(row) { canReview(row) {
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1) const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage] return Number(row.reserve_state) === {union:4000,club:4010,unit:4060,school:4020}[stage]
}, },
openView(row) { openView(row) {
this.$refs.guava.view() this.$refs.guava.view()
@@ -45,7 +45,7 @@ const info = {
return {viewData: {}, activeName: 'info', detailLoading: false, requestId: 0} return {viewData: {}, activeName: 'info', detailLoading: false, requestId: 0}
}, },
methods: { methods: {
typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约'}[type] || '—' }, typeName(type) { return {1:'个人预约',2:'分工会预约',3:'协会预约',4:'单位预约'}[type] || '—' },
// id 为预约记录主键,activeName 为申请信息或审核标签;结果含预约字段及 auditListTable 审核历史。 // id 为预约记录主键,activeName 为申请信息或审核标签;结果含预约字段及 auditListTable 审核历史。
onOpen(id, activeName = 'info') { onOpen(id, activeName = 'info') {
const requestId = this.requestId + 1 const requestId = this.requestId + 1
@@ -32,18 +32,19 @@ layout("/layouts/platform.html"){
</div> </div>
</div> </div>
<div class="search-item"> <div class="search-item">
<div class="search-item-label">日期范围:</div> <div class="search-item-label">日期开始</div>
<div class="search-item-option"> <div class="search-item-option">
<el-date-picker <el-date-picker clearable type="date" style="width: 100%"
end-placeholder="结束日期" placeholder="日期开始" v-model="dateStart"
range-separator="至" value-format="yyyy-MM-dd"></el-date-picker>
start-placeholder="开始日期" </div>
type="daterange" </div>
style="width: 100%" <div class="search-item">
@change="activityDateChange" <div class="search-item-label">日期结束</div>
value-format="yyyy-MM-dd" <div class="search-item-option">
v-model="pageForm.activityDate"> <el-date-picker clearable type="date" style="width: 100%"
</el-date-picker> placeholder="日期结束" v-model="dateEnd"
value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
</div> </div>
@@ -128,6 +129,8 @@ layout("/layouts/platform.html"){
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
dateStart: "",
dateEnd: "",
tableColumns: [ tableColumns: [
{label: '姓名', prop: 'username'}, {label: '姓名', prop: 'username'},
{label: '工号', prop: 'loginname'}, {label: '工号', prop: 'loginname'},
@@ -157,7 +160,24 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
// 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
syncDateRange() {
const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的日期开始和日期结束')
return false
}
if (start && end && start > end) {
this.$message.warning('日期开始不能晚于日期结束')
return false
}
this.$set(this.pageForm, 'activityDate', start && end ? [start, end] : [])
return true
},
exportExcel() { exportExcel() {
if (!this.syncDateRange()) return
const {activityId, unionId, unitId, activityDate} = this.pageForm const {activityId, unionId, unitId, activityDate} = this.pageForm
let activityDate1 = [] let activityDate1 = []
if (activityDate && activityDate.length > 0) { if (activityDate && activityDate.length > 0) {
@@ -165,9 +185,6 @@ layout("/layouts/platform.html"){
} }
window.open(loc() + '/exportExcel?activityId=' + activityId + '&unionId=' + unionId + '&unitId=' + unitId + '&activityDate=' + activityDate1) window.open(loc() + '/exportExcel?activityId=' + activityId + '&unionId=' + unionId + '&unitId=' + unitId + '&activityDate=' + activityDate1)
}, },
activityDateChange() {
// this.$set(this.pageForm, "activityDate", [])
},
async unionChange(val) { async unionChange(val) {
this.pageForm.unitId = null this.pageForm.unitId = null
this.unitOptions = await getUnits(val) this.unitOptions = await getUnits(val)
@@ -184,22 +201,25 @@ layout("/layouts/platform.html"){
} }
}, },
pageData() { pageData() {
if (!this.syncDateRange()) return
sublime.showLoadingbar(); sublime.showLoadingbar();
this.tableLoading = true this.$set(this, "tableLoading", true)
let pageForm = clone(this.pageForm) let pageForm = clone(this.pageForm)
if (pageForm.activityDate && pageForm.activityDate.length > 0) { if (pageForm.activityDate && pageForm.activityDate.length > 0) {
pageForm.activityDate = JSON.stringify(pageForm.activityDate) pageForm.activityDate = JSON.stringify(pageForm.activityDate)
} }
$.post(loc() + "/pageData", pageForm, (data) => { return $.post(loc() + "/pageData", pageForm).then((data) => {
sublime.closeLoadingbar();
this.tableLoading = false
if (data.code === 0) { if (data.code === 0) {
this.tableData = data.data.list; this.$set(this, "tableData", data.data.list);
this.pageForm.totalCount = data.data.totalCount; this.$set(this.pageForm, "totalCount", data.data.totalCount);
} else { } else {
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}, "json"); }).always(() => {
// 成功或失败均释放查询状态,避免日期筛选请求异常后界面一直等待。
sublime.closeLoadingbar();
this.$set(this, "tableLoading", false)
});
}, },
async getActivityById(){ async getActivityById(){
@@ -12,19 +12,18 @@ layout("/layouts/platform.html"){
<template> <template>
<el-card class="mt10" shadow="never"> <el-card class="mt10" shadow="never">
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
<el-date-picker @change="getMeetingList(pageForm.meetingTime)" clearable <el-date-picker clearable type="date" style="width: 180px"
end-placeholder="结束日期" placeholder="会议开始" v-model="dateStart"
range-separator="-" value-format="yyyy-MM-dd HH:mm:ss" @change="changeMeetingDate"></el-date-picker>
start-placeholder="开始日期" <span class="mr5"></span>
type="daterange" <el-date-picker clearable type="date" style="width: 180px"
v-model="pageForm.meetingTime" placeholder="会议结束" v-model="dateEnd"
value-format="yyyy-MM-dd HH:mm:ss"> value-format="yyyy-MM-dd HH:mm:ss" @change="changeMeetingDate"></el-date-picker>
</el-date-picker>
</div> </div>
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
<el-select @change="doSearch" clearable <el-select :loading="meetingListLoading" @change="doSearch" clearable
filterable="true" filterable="true"
placeholder="请输入会议名称" placeholder="请输入会议名称"
style="width: 100%;" v-model="pageForm.meetingId"> style="width: 100%;" v-model="pageForm.meetingId">
@@ -183,6 +182,9 @@ layout("/layouts/platform.html"){
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
meetingListLoading: false,
dateStart: "",
dateEnd: "",
meetingTypeList: [], meetingTypeList: [],
unions: [], unions: [],
units: [], units: [],
@@ -222,6 +224,22 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
// 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
syncDateRange() {
const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的会议开始和会议结束')
return false
}
if (start && end && start > end) {
this.$message.warning('会议开始不能晚于会议结束')
return false
}
this.$set(this.pageForm, 'meetingTime', start && end ? [start, end] : [])
return true
},
proportionNum(joinNum, total) { proportionNum(joinNum, total) {
console.log(joinNum, total) console.log(joinNum, total)
return ((joinNum ? joinNum : 0) / total * 100).toFixed(2) + "%" return ((joinNum ? joinNum : 0) / total * 100).toFixed(2) + "%"
@@ -242,9 +260,24 @@ layout("/layouts/platform.html"){
this.pageFormUser.pageSize = val; this.pageFormUser.pageSize = val;
this.getUserData(this.meetingTimeData, this.meetingNumType) this.getUserData(this.meetingTimeData, this.meetingNumType)
}, },
async getMeetingList(meetingTime) { // 修改完整日期范围后刷新可选会议,并清除旧会议筛选,避免沿用范围外的会议。
const {data} = await $.post(loc() + "/getMeetingList", {meetingTime: JSON.stringify(meetingTime)}) changeMeetingDate() {
this.meetingList = data if (!this.syncDateRange()) return
this.$set(this.pageForm, 'meetingId', '')
return this.getMeetingList(this.pageForm.meetingTime).then(() => this.doSearch())
},
pageData() {
if (!this.syncDateRange()) return
return initTableMixins.methods.pageData.call(this)
},
getMeetingList(meetingTime) {
this.$set(this, "meetingListLoading", true)
// meetingTime 为原接口的起止时间数组,返回 data 为会议选项列表。
return $.post(loc() + "/getMeetingList", {meetingTime: JSON.stringify(meetingTime)}).then((res) => {
this.$set(this, 'meetingList', res.data)
}).always(() => {
this.$set(this, 'meetingListLoading', false)
})
/*if (this.meetingList.length > 0) { /*if (this.meetingList.length > 0) {
this.$set(this.pageForm, "meetingId", this.meetingList[0].id) this.$set(this.pageForm, "meetingId", this.meetingList[0].id)
this.pageData() this.pageData()
@@ -285,7 +318,10 @@ layout("/layouts/platform.html"){
this.meetingTypeList = await meetUtil.getAllMeetingType() this.meetingTypeList = await meetUtil.getAllMeetingType()
let format = moment().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss'); let format = moment().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss');
let format1 = moment().format('YYYY-MM-DD HH:mm:ss'); let format1 = moment().format('YYYY-MM-DD HH:mm:ss');
this.$set(this.pageForm, "meetingTime", [format, format1]) // 保留最近一年默认范围,并让两个日期框与首次请求使用相同值。
this.$set(this, 'dateStart', format)
this.$set(this, 'dateEnd', format1)
this.syncDateRange()
await this.getMeetingList(this.pageForm.meetingTime) await this.getMeetingList(this.pageForm.meetingTime)
await this.pageData() await this.pageData()
@@ -7,13 +7,13 @@ layout("/layouts/platform.html"){
<template> <template>
<el-card shadow="never"> <el-card shadow="never">
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
<el-date-picker clearable end-placeholder="结束日期" <el-date-picker clearable type="date" style="width: 180px"
range-separator="-" placeholder="创建开始" v-model="dateStart"
start-placeholder="开始日期" value-format="yyyy-MM-dd"></el-date-picker>
type="daterange" <span class="mr5"></span>
v-model="pageForm.dateRange" <el-date-picker clearable type="date" style="width: 180px"
value-format="yyyy-MM-dd"> placeholder="创建结束" v-model="dateEnd"
</el-date-picker> value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
@@ -89,6 +89,8 @@ layout("/layouts/platform.html"){
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
dateStart: "",
dateEnd: "",
unions: [], unions: [],
units: [], units: [],
pageForm: { pageForm: {
@@ -107,6 +109,22 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
// 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
syncDateRange() {
const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的创建开始和创建结束')
return false
}
if (start && end && start > end) {
this.$message.warning('创建开始不能晚于创建结束')
return false
}
this.$set(this.pageForm, 'dateRange', start && end ? [start, end] : [])
return true
},
async doSend(row) { async doSend(row) {
const confirm = await this.$confirm('您确定要发送消息吗?', '提示', { const confirm = await this.$confirm('您确定要发送消息吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@@ -151,16 +169,15 @@ layout("/layouts/platform.html"){
} }
}, },
pageData() { pageData() {
if (!this.syncDateRange()) return
sublime.showLoadingbar(); sublime.showLoadingbar();
this.tableLoading = true this.$set(this, "tableLoading", true)
const pageForm = clone(this.pageForm) const pageForm = clone(this.pageForm)
pageForm.sendTypes = JSON.stringify(pageForm.sendTypes) pageForm.sendTypes = JSON.stringify(pageForm.sendTypes)
pageForm.dateRange = JSON.stringify(pageForm.dateRange) pageForm.dateRange = JSON.stringify(pageForm.dateRange)
$.post(loc() + "/pageData", pageForm, (data) => { return $.post(loc() + "/pageData", pageForm).then((data) => {
sublime.closeLoadingbar();
this.tableLoading = false
if (data.code === 0) { if (data.code === 0) {
data.data.list.forEach(v => { data.data.list.forEach(v => {
if (v.sendTypes) { if (v.sendTypes) {
@@ -177,12 +194,16 @@ layout("/layouts/platform.html"){
v.sendTypesName = "钉钉" v.sendTypesName = "钉钉"
} }
}) })
this.tableData = data.data.list; this.$set(this, "tableData", data.data.list);
this.pageForm.totalCount = data.data.totalCount; this.$set(this.pageForm, "totalCount", data.data.totalCount);
} else { } else {
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}, "json"); }).always(() => {
// 成功或失败均释放查询状态,避免日期筛选请求异常后界面一直等待。
sublime.closeLoadingbar();
this.$set(this, "tableLoading", false)
});
}, },
async openEdit(row) { async openEdit(row) {
/* const {data} = await $.post(loc() + "/getMeetingByMsgId", {id: row.id}) /* const {data} = await $.post(loc() + "/getMeetingByMsgId", {id: row.id})
@@ -8,19 +8,18 @@ layout("/layouts/platform.html"){
<el-card shadow="never"> <el-card shadow="never">
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
<el-date-picker clearable end-placeholder="结束日期" <el-date-picker clearable type="date" style="width: 180px"
range-separator="-" placeholder="发送开始" v-model="dateStart"
start-placeholder="开始日期" value-format="yyyy-MM-dd" @change="changeMessageDate"></el-date-picker>
type="daterange" <span class="mr5"></span>
@change="getTitleList(pageForm.dateRange)" <el-date-picker clearable type="date" style="width: 180px"
v-model="pageForm.dateRange" placeholder="发送结束" v-model="dateEnd"
value-format="yyyy-MM-dd"> value-format="yyyy-MM-dd" @change="changeMessageDate"></el-date-picker>
</el-date-picker>
</div> </div>
<div class="btn-group tool-button mr10"> <div class="btn-group tool-button mr10">
<el-select placeholder="消息列表" v-model="pageForm.titleId" style="width: 100%;" <el-select :loading="titleListLoading" placeholder="消息列表" v-model="pageForm.titleId" style="width: 100%;"
clearable="true" clearable="true"
filterable="true"> filterable="true">
<el-option v-for="item in titleList" :label="item.title" :value="item.id"></el-option> <el-option v-for="item in titleList" :label="item.title" :value="item.id"></el-option>
@@ -98,6 +97,9 @@ layout("/layouts/platform.html"){
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
titleListLoading: false,
dateStart: "",
dateEnd: "",
unions: [], unions: [],
units: [], units: [],
titleList: [], titleList: [],
@@ -119,9 +121,36 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
async getTitleList(dateRange) { // 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
const {data} = await $.post(loc() + "/getTitleList", {dateRange: JSON.stringify(dateRange)}) syncDateRange() {
this.titleList = data const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的发送开始和发送结束')
return false
}
if (start && end && start > end) {
this.$message.warning('发送开始不能晚于发送结束')
return false
}
this.$set(this.pageForm, 'dateRange', start && end ? [start, end] : [])
return true
},
// 日期完整后同步消息选项;清空旧标题,避免保留范围外的消息筛选。
changeMessageDate() {
if (!this.syncDateRange()) return
this.$set(this.pageForm, 'titleId', '')
return this.getTitleList(this.pageForm.dateRange)
},
getTitleList(dateRange) {
this.$set(this, "titleListLoading", true)
// dateRange 为原接口的日期数组,返回 data 为可选消息标题列表。
return $.post(loc() + "/getTitleList", {dateRange: JSON.stringify(dateRange)}).then((res) => {
this.$set(this, 'titleList', res.data)
}).always(() => {
this.$set(this, 'titleListLoading', false)
})
}, },
async flushUnits() { async flushUnits() {
this.$set(this.pageForm, "unitId", "") this.$set(this.pageForm, "unitId", "")
@@ -132,16 +161,15 @@ layout("/layouts/platform.html"){
} }
}, },
pageData() { pageData() {
if (!this.syncDateRange()) return
sublime.showLoadingbar(); sublime.showLoadingbar();
this.tableLoading = true this.$set(this, "tableLoading", true)
const pageForm = clone(this.pageForm) const pageForm = clone(this.pageForm)
pageForm.sendTypes = JSON.stringify(pageForm.sendTypes) pageForm.sendTypes = JSON.stringify(pageForm.sendTypes)
pageForm.dateRange = JSON.stringify(pageForm.dateRange) pageForm.dateRange = JSON.stringify(pageForm.dateRange)
$.post(loc() + "/pageData", pageForm, (data) => { return $.post(loc() + "/pageData", pageForm).then((data) => {
sublime.closeLoadingbar();
this.tableLoading = false
if (data.code == 0) { if (data.code == 0) {
data.data.list.forEach(v => { data.data.list.forEach(v => {
if (v.sendTypes) { if (v.sendTypes) {
@@ -155,12 +183,16 @@ layout("/layouts/platform.html"){
v.sendTypesName = "钉钉" v.sendTypesName = "钉钉"
} }
}) })
this.tableData = data.data.list; this.$set(this, "tableData", data.data.list);
this.pageForm.totalCount = data.data.totalCount; this.$set(this.pageForm, "totalCount", data.data.totalCount);
} else { } else {
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}, "json"); }).always(() => {
// 成功或失败均释放查询状态,避免日期筛选请求异常后界面一直等待。
sublime.closeLoadingbar();
this.$set(this, "tableLoading", false)
});
}, },
}, },
async created() { async created() {
@@ -10,13 +10,19 @@ layout("/layouts/platform.html"){
<el-card shadow="never"> <el-card shadow="never">
<div class="search"> <div class="search">
<div class="search-item"> <div class="search-item">
<div class="search-item-label">报销时间:</div> <div class="search-item-label">报销开始</div>
<div class="search-item-option"> <div class="search-item-option">
<el-date-picker type="daterange" style="width: 100%" range-separator="——" <el-date-picker clearable type="date" style="width: 100%"
v-model="pageForm.reimbursementDate" placeholder="报销开始" v-model="dateStart"
value-format="yyyy-MM-dd" format="yyyy-MM-dd" value-format="yyyy-MM-dd"></el-date-picker>
start-placeholder="开始日期" </div>
end-placeholder="结束日期"></el-date-picker> </div>
<div class="search-item">
<div class="search-item-label">报销结束</div>
<div class="search-item-option">
<el-date-picker clearable type="date" style="width: 100%"
placeholder="报销结束" v-model="dateEnd"
value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
</div> </div>
@@ -171,6 +177,8 @@ layout("/layouts/platform.html"){
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
dateStart: "",
dateEnd: "",
unions: [], unions: [],
units: [], units: [],
formData: {}, formData: {},
@@ -203,6 +211,22 @@ layout("/layouts/platform.html"){
'reimbursement-new': httpVueLoader('/components/reimbursement/reimbursementNew.vue'), 'reimbursement-new': httpVueLoader('/components/reimbursement/reimbursementNew.vue'),
}, },
methods: { methods: {
// 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
syncDateRange() {
const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的报销开始和报销结束')
return false
}
if (start && end && start > end) {
this.$message.warning('报销开始不能晚于报销结束')
return false
}
this.$set(this.pageForm, 'reimbursementDate', start && end ? [start, end] : [])
return true
},
getSummaries(param) { getSummaries(param) {
const {columns, data} = param; const {columns, data} = param;
const sums = []; const sums = [];
@@ -235,6 +259,7 @@ layout("/layouts/platform.html"){
return sums; return sums;
}, },
doExportUserExcel() { doExportUserExcel() {
if (!this.syncDateRange()) return
const { const {
searchKeyword, searchKeyword,
searchName, searchName,
@@ -264,21 +289,24 @@ layout("/layouts/platform.html"){
this.units = await getUnits(this.pageForm.unionId) this.units = await getUnits(this.pageForm.unionId)
this.doSearch() this.doSearch()
}, },
pageData() {//加载分页数据 pageData() {
if (!this.syncDateRange()) return
sublime.showLoadingbar();//显示loading sublime.showLoadingbar();//显示loading
this.tabLoading = true this.$set(this, "tableLoading", true)
const pageForm = clone(this.pageForm) const pageForm = clone(this.pageForm)
pageForm.reimbursementDate = JSON.stringify(pageForm.reimbursementDate) pageForm.reimbursementDate = JSON.stringify(pageForm.reimbursementDate)
$.post(loc() + "/pageData", pageForm, (data) => { return $.post(loc() + "/pageData", pageForm).then((data) => {
sublime.closeLoadingbar();//关闭loading
this.tabLoading = false
if (data.code == 0) { if (data.code == 0) {
this.tableData = data.data.list; this.$set(this, "tableData", data.data.list);
this.pageForm.totalCount = data.data.totalCount; this.$set(this.pageForm, "totalCount", data.data.totalCount);
} else { } else {
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}, "json"); }).always(() => {
// 成功或失败均释放查询状态,避免日期筛选请求异常后界面一直等待。
sublime.closeLoadingbar();
this.$set(this, "tableLoading", false)
});
}, },
}, },
async created() { async created() {
@@ -50,12 +50,18 @@ const COMMON_QUERY = {
</div> </div>
</div> </div>
<div class="search-item"> <div class="search-item">
<div class="search-item-label">变更日期</div> <div class="search-item-label">变更开始</div>
<div class="search-item-option"> <div class="search-item-option">
<el-date-picker :picker-options="pickerOptions" @change="changeDateRangeChange" <el-date-picker @change="doSearch" placeholder="请选择变更开始" format="yyyy-MM-dd"
align="right" end-placeholder="结束日期" format="yyyy-MM-dd" style="width: 100%" type="date" v-model="pageForm.changeDateBefore"
range-separator="-" start-placeholder="开始日期" style="width: 100%" value-format="yyyy-MM-dd"></el-date-picker>
type="datetimerange" v-model="pageForm.changeDateRange" </div>
</div>
<div class="search-item">
<div class="search-item-label">变更结束</div>
<div class="search-item-option">
<el-date-picker @change="doSearch" placeholder="请选择变更结束" format="yyyy-MM-dd"
style="width: 100%" type="date" v-model="pageForm.changeDateEnd"
value-format="yyyy-MM-dd"></el-date-picker> value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
</div> </div>
@@ -68,37 +74,6 @@ const COMMON_QUERY = {
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return{ return{
pickerOptions: {
shortcuts: [
{
text: "最近一周",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit("pick", [start, end])
}
},
{
text: "最近一个月",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit("pick", [start, end])
}
},
{
text: "最近三个月",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit("pick", [start, end])
}
}
]
},
unions: [], unions: [],
units: [], units: [],
pageForm: { pageForm: {
@@ -109,31 +84,29 @@ const COMMON_QUERY = {
unitId: '', unitId: '',
userState: '', userState: '',
personType: '', personType: '',
changeDateRange: [] changeDateBefore: '',
changeDateEnd: ''
} }
} }
}, },
methods: { methods: {
setDefaultDateRange() { setDefaultDateRange() {
const end = new Date(); // 当前时间 // 最近三个月沿用90天口径;日期字符串与后端按天筛选的参数格式保持一致。
const start = new Date(); const end = moment()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); // 当前时间往前推90天 this.$set(this.pageForm, "changeDateBefore", end.clone().subtract(90, 'days').format('YYYY-MM-DD'))
this.$set(this.pageForm, "changeDateRange", [start, end]) this.$set(this.pageForm, "changeDateEnd", end.format('YYYY-MM-DD'))
this.pageForm.changeDateBefore = start
this.pageForm.changeDateEnd = end
this.doSearch()
},
changeDateRangeChange(val){
if (val && val.length > 0) {
this.pageForm.changeDateBefore = val[0]
this.pageForm.changeDateEnd = val[1]
} else {
this.pageForm.changeDateBefore = null
this.pageForm.changeDateEnd = null
}
this.doSearch() this.doSearch()
}, },
doSearch(){ doSearch(){
// 后端需要完整的起止日期;缺失时会退回仅查询当天,因此不发送不完整或倒置的范围。
if (!this.pageForm.changeDateBefore || !this.pageForm.changeDateEnd) {
this.$message.warning('请选择变更开始和变更结束')
return
}
if (this.pageForm.changeDateBefore > this.pageForm.changeDateEnd) {
this.$message.warning('变更开始不能晚于变更结束')
return
}
this.$emit('search', this.pageForm) this.$emit('search', this.pageForm)
}, },
async initData(){ async initData(){
@@ -41,18 +41,19 @@ layout("/layouts/platform.html"){
<el-link type="danger" <el-link type="danger"
v-if="changeTypeData.length && pageForm.changeTypes.length" v-if="changeTypeData.length && pageForm.changeTypes.length"
:underline="false" :underline="false"
@click="pageForm.changeTypes=[];doSearch()">清空 @click="clearChangeTypes">清空
</el-link> </el-link>
</el-col> </el-col>
</el-row> </el-row>
</el-card> </el-card>
<el-card shadow="never" class="mt10"> <el-card shadow="never" class="mt10">
<table-tool :app="this" label="变更记录(默认查询最近三个月,如需查询其他变更记录,请选择上方【变更日期】后进行搜索)"> <table-tool :app="this" label="变更记录(默认查询最近三个月,如需查询其他变更记录,请选择上方【变更开始】【变更结束】后进行搜索)">
<template #func> <template #func>
<el-button type="primary" icon="el-icon-download" size="small" @click="doExport">导出</el-button>
<el-button type="primary" style="margin-right: 10px" size="small" @click="doBatchChange">批量确认</el-button> <el-button type="primary" style="margin-right: 10px" size="small" @click="doBatchChange">批量确认</el-button>
<el-radio-group @change="search" class="mr5" size="small" <el-radio-group @change="doSearch" class="mr5" size="small"
v-model="pageForm.isOperate"> v-model="pageForm.isOperate">
<el-radio-button label="true">已操作</el-radio-button> <el-radio-button label="true">已操作</el-radio-button>
<el-radio-button label="false">未操作</el-radio-button> <el-radio-button label="false">未操作</el-radio-button>
@@ -143,6 +144,12 @@ layout("/layouts/platform.html"){
'user-change-info': USER_CHANGE_INFO 'user-change-info': USER_CHANGE_INFO
}, },
methods: { methods: {
// 使用列表已应用的筛选条件导出全部匹配记录,类型编码按原接口格式传递。
doExport() {
const pageForm = clone(this.pageForm)
pageForm.changeTypes = JSON.stringify(pageForm.changeTypes)
this.$downLoad('/platform/sourcechange/manage/doExport', pageForm)
},
/** /**
* 撤销 * 撤销
*/ */
@@ -254,11 +261,18 @@ layout("/layouts/platform.html"){
} else { } else {
this.pageForm[key].push(val) this.pageForm[key].push(val)
} }
this.pageData() // 类型筛选变化后复用 mixin 的搜索入口,重置页码再查询。
this.doSearch()
},
clearChangeTypes() {
// 清空类型与点击类型标签保持相同的分页重置行为。
this.$set(this.pageForm, 'changeTypes', [])
this.doSearch()
}, },
search(pageForm){ search(pageForm){
this.pageForm = { ...this.pageForm, ...pageForm } // 合并业务筛选后回到第一页,避免沿用旧页码导致新条件下出现空列表。
this.pageData() this.$set(this, 'pageForm', { ...this.pageForm, ...pageForm })
this.doSearch()
}, },
getChangeTypes(changeTypes){ getChangeTypes(changeTypes){
const changeTypesList = JSON.parse(changeTypes) const changeTypesList = JSON.parse(changeTypes)
@@ -286,7 +300,7 @@ layout("/layouts/platform.html"){
}, },
async created() { async created() {
this.changeTypeData = await getEnumOptions("MemberChangeType") this.changeTypeData = await getEnumOptions("MemberChangeType")
this.pageData() // 首次查询由筛选组件设置默认起止日期后触发,避免无日期请求覆盖默认范围的结果。
} }
}) })
</script> </script>
@@ -49,12 +49,18 @@ const COMMON_QUERY = {
</div> </div>
</div> </div>
<div class="search-item"> <div class="search-item">
<div class="search-item-label">生日日期</div> <div class="search-item-label">生日开始</div>
<div class="search-item-option"> <div class="search-item-option">
<el-date-picker :picker-options="pickerOptions" @change="changeDateRangeChange" <el-date-picker clearable type="date" @change="doSearch" style="width: 100%"
align="right" end-placeholder="结束日期" format="yyyy-MM-dd" placeholder="生日开始" v-model="pageForm.startDate"
range-separator="-" start-placeholder="开始日期" style="width: 100%" value-format="yyyy-MM-dd"></el-date-picker>
type="datetimerange" v-model="pageForm.changeDateRange" </div>
</div>
<div class="search-item">
<div class="search-item-label">生日结束</div>
<div class="search-item-option">
<el-date-picker clearable type="date" @change="doSearch" style="width: 100%"
placeholder="生日结束" v-model="pageForm.endDate"
value-format="yyyy-MM-dd"></el-date-picker> value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
</div> </div>
@@ -65,38 +71,6 @@ const COMMON_QUERY = {
`, `,
data() { data() {
return { return {
pickerOptions: {
shortcuts: [
{
text: "未来一周",
onClick(picker) {
const end = new Date()
const start = new Date()
end.setTime(start.getTime() + 3600 * 1000 * 24 * 7)
picker.$emit("pick", [start, end])
}
},
{
text: "未来一个月",
onClick(picker) {
const end = new Date()
const start = new Date()
end.setTime(start.getTime() + 3600 * 1000 * 24 * 30)
picker.$emit("pick", [start, end])
}
},
{
text: "未来三个月",
onClick(picker) {
const end = new Date()
const start = new Date()
end.setTime(start.getTime() + 3600 * 1000 * 24 * 90)
picker.$emit("pick", [start, end])
}
}
]
},
unions: [], unions: [],
units: [], units: [],
pageForm: { pageForm: {
@@ -106,28 +80,20 @@ const COMMON_QUERY = {
userStates: [], userStates: [],
personTypes: [], personTypes: [],
preparedBys: [], preparedBys: [],
changeDateRange: [], startDate: "",
endDate: "",
} }
} }
}, },
methods: { methods: {
doSearch(){ doSearch(){
// 生日接口按月日筛选,支持单端与跨年范围;两端为空时保留未来15天的默认查询。
const pageForm = clone(this.pageForm) const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates) pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes) pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys) pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
this.$emit('search', pageForm) this.$emit('search', pageForm)
}, },
changeDateRangeChange(val){
if (val && val.length > 0) {
this.pageForm.startDate = val[0]
this.pageForm.endDate = val[1]
} else {
this.pageForm.startDate = null
this.pageForm.endDate = null
}
this.doSearch()
},
async initData(){ async initData(){
if (this.$auth.hasRoleOr('sysadmin, SchoolUnionMemberAdmin, SchoolUnionAdmin')) { if (this.$auth.hasRoleOr('sysadmin, SchoolUnionMemberAdmin, SchoolUnionAdmin')) {
this.unions = await getUnions(this.pageForm.unionId) this.unions = await getUnions(this.pageForm.unionId)
@@ -39,15 +39,13 @@ layout("/layouts/platform.html"){
</el-select> </el-select>
</div> </div>
<div class="btn-group tool-button mt5"> <div class="btn-group tool-button mt5">
<el-date-picker <el-date-picker clearable type="date" style="width: 180px"
end-placeholder="购置日期" placeholder="购置开始" v-model="dateStart"
format="yyyy 年 MM 月 dd 日" value-format="yyyy-MM-dd"></el-date-picker>
range-separator="-" <span class="mr5"></span>
start-placeholder="购置日期" <el-date-picker clearable type="date" style="width: 180px"
type="daterange" placeholder="购置结束" v-model="dateEnd"
v-model="time" value-format="yyyy-MM-dd"></el-date-picker>
value-format="yyyy-MM-dd">
</el-date-picker>
</div> </div>
<div class="btn-group tool-button mt5"> <div class="btn-group tool-button mt5">
<el-select placeholder="请选择资产类型" v-model="pageForm.zcghlx"> <el-select placeholder="请选择资产类型" v-model="pageForm.zcghlx">
@@ -317,6 +315,8 @@ layout("/layouts/platform.html"){
el: '#app', el: '#app',
data: function () { data: function () {
return { return {
dateStart: "",
dateEnd: "",
pageForm: { pageForm: {
searchName: "", searchName: "",
searchKeyword: "", searchKeyword: "",
@@ -345,7 +345,24 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
// 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
syncDateRange() {
const start = this.dateStart
const end = this.dateEnd
if ((!start && end) || (start && !end)) {
this.$message.warning('请选择完整的购置开始和购置结束')
return false
}
if (start && end && start > end) {
this.$message.warning('购置开始不能晚于购置结束')
return false
}
this.$set(this, 'time', start && end ? [start, end] : [])
return true
},
doAllExport() { doAllExport() {
if (!this.syncDateRange()) return
const {searchKeyword, searchName, zczt, zcghlx, ghid} = this.pageForm const {searchKeyword, searchName, zczt, zcghlx, ghid} = this.pageForm
let startTime = '' let startTime = ''
let endTime = '' let endTime = ''
@@ -365,16 +382,9 @@ layout("/layouts/platform.html"){
doExport(id) { doExport(id) {
window.location.href = '/platform/zcgl/tzgl/downloadrzd?id=' + id window.location.href = '/platform/zcgl/tzgl/downloadrzd?id=' + id
}, },
doSearch: function () { doSearch() {
if (!this.syncDateRange()) return
if (this.time != "" && this.time != null) { this.$set(this.pageForm, 'pageNumber', 1)
this.pageForm.startTime = this.time[0];
this.pageForm.endTime = this.time[1];
} else {
this.pageForm.startTime = null;
this.pageForm.endTime = null;
}
this.pageForm.pageNumber = 1;
this.pageData(); this.pageData();
}, },
pageOrder: function (column) {//按字段排序 pageOrder: function (column) {//按字段排序
@@ -398,21 +408,27 @@ layout("/layouts/platform.html"){
this.pageForm.pageSize = val; this.pageForm.pageSize = val;
this.pageData(); this.pageData();
}, },
pageData: function () {//加载分页数据 pageData() {
var self = this; if (!this.syncDateRange()) return
sublime.showLoadingbar();//显示loading this.$set(this.pageForm, "startTime", this.dateStart || null)
$.post(base + "/platform/zcgl/zhcx/data", self.pageForm, function (data) { this.$set(this.pageForm, "endTime", this.dateEnd || null)
sublime.closeLoadingbar();//关闭loading sublime.showLoadingbar();
this.$set(this, "tableLoading", true)
return $.post(base + "/platform/zcgl/zhcx/data", this.pageForm).then((data) => {
if (data.code == 0) { if (data.code == 0) {
self.tableData = data.data.list; this.$set(this, "tableData", data.data.list);
self.pageForm.totalCount = data.data.totalCount; this.$set(this.pageForm, "totalCount", data.data.totalCount);
} else { } else {
self.$message({ this.$message({
message: data.msg, message: data.msg,
type: 'error' type: 'error'
}); });
} }
}, "json"); }).always(() => {
// 成功或失败均释放查询状态,避免日期筛选请求异常后界面一直等待。
sublime.closeLoadingbar();
this.$set(this, "tableLoading", false)
});
}, },
viewInfo(scope) { viewInfo(scope) {
this.viewData = JSON.parse(JSON.stringify(scope.row)); this.viewData = JSON.parse(JSON.stringify(scope.row));
@@ -9,13 +9,13 @@ layout("/layouts/platform.html"){
<div class="btn-group tool-button"> <div class="btn-group tool-button">
<div class="block"> <div class="block">
<el-date-picker clearable end-placeholder="结束日期" <el-date-picker clearable type="date" style="width: 180px"
range-separator="-" placeholder="申请开始" v-model="dateStart"
start-placeholder="开始日期" value-format="yyyy-MM-dd"></el-date-picker>
type="daterange" <span class="mr5"></span>
v-model="pageForm._dateRange" <el-date-picker clearable type="date" style="width: 180px"
value-format="yyyy-MM-dd"> placeholder="申请结束" v-model="dateEnd"
</el-date-picker> value-format="yyyy-MM-dd"></el-date-picker>
</div> </div>
</div> </div>
<div class="btn-group tool-button"> <div class="btn-group tool-button">
@@ -139,6 +139,8 @@ layout("/layouts/platform.html"){
}, },
data() { data() {
return { return {
dateStart: "",
dateEnd: "",
unions: [], unions: [],
units: [], units: [],
pageForm: { pageForm: {
@@ -152,17 +154,27 @@ layout("/layouts/platform.html"){
} }
}, },
methods: { methods: {
doSearch() { // 两个日期框合成为原接口的起止数组;两端清空表示不限定日期,单端或倒置时停止查询。
this.tabKey = new Date().getTime() syncDateRange() {
this.pageForm.pageNumber = 1 const start = this.dateStart
const end = this.dateEnd
if (this.pageForm._dateRange && this.pageForm._dateRange.length) { if ((!start && end) || (start && !end)) {
this.pageForm.dateRange = JSON.stringify(this.pageForm._dateRange) this.$message.warning('请选择完整的申请开始和申请结束')
} else { return false
this.pageForm.dateRange = ""
} }
if (start && end && start > end) {
this.$message.warning('申请开始不能晚于申请结束')
return false
}
this.$set(this.pageForm, '_dateRange', start && end ? [start, end] : [])
return true
},
this.pageData() // 日期参数同步后复用全局列表请求,搜索和分页继续使用 mixin 的逻辑。
pageData() {
if (!this.syncDateRange()) return
this.$set(this.pageForm, 'dateRange', JSON.stringify(this.pageForm._dateRange))
return initTableMixins.methods.pageData.call(this)
}, },
dropdownCommand(command) { dropdownCommand(command) {
const {type, data} = command const {type, data} = command
@@ -181,6 +193,7 @@ layout("/layouts/platform.html"){
this.$refs.viewInfoRef.onOpen(row.id) this.$refs.viewInfoRef.onOpen(row.id)
}, },
doExcel() { doExcel() {
if (!this.syncDateRange()) return
const {unionId, unitId, _dateRange} = this.pageForm const {unionId, unitId, _dateRange} = this.pageForm
window.open("/platform/difficulty/reading/doExcel?dateRange=" + JSON.stringify(_dateRange) window.open("/platform/difficulty/reading/doExcel?dateRange=" + JSON.stringify(_dateRange)
+ "&unionId=" + unionId + "&unitId=" + unitId) + "&unionId=" + unionId + "&unitId=" + unitId)