Merge branch 'main' of https://dd3skj.picp.vip/JyuHsin/zhgh_jshvc
# Conflicts: # src/main/resources/application-dev.yaml
@@ -2,7 +2,7 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.budwk</groupId>
|
||||
<artifactId>zhgh_jshvc</artifactId>
|
||||
<artifactId>mini</artifactId>
|
||||
<version>5.6.0-plus</version>
|
||||
<build>
|
||||
<resources>
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.budwk.app.sys.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.constant.RedisConstant;
|
||||
import com.budwk.app.base.result.Result;
|
||||
@@ -10,6 +9,7 @@ import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
|
||||
import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.integration.jedis.pubsub.PubSubService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
@@ -22,10 +22,13 @@ import org.nutz.mvc.upload.UploadAdaptor;
|
||||
import redis.clients.jedis.ScanParams;
|
||||
import redis.clients.jedis.ScanResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/sys/h5ScanCodeUploadFile")
|
||||
@Ok("json:full")
|
||||
@Api(tags = "h5扫码上传文件")
|
||||
@Slf4j
|
||||
public class SysH5ScanCodeUploadFileController {
|
||||
|
||||
@Inject
|
||||
@@ -55,6 +58,8 @@ public class SysH5ScanCodeUploadFileController {
|
||||
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), file);
|
||||
return Result.success().addData(url);
|
||||
} catch (Exception e) {
|
||||
log.error("h5扫码上传文件失败,loginName={}, fileName={}, fileSize={}",
|
||||
getCurrentLoginName(), getUploadFileName(file), getUploadFileSize(file), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -63,26 +68,59 @@ public class SysH5ScanCodeUploadFileController {
|
||||
@At
|
||||
@SLog(tag = "文件管理", msg = "同步到PC")
|
||||
@SaCheckLogin
|
||||
public Result syncPc(@Param("files") JSONObject[] jsonObject, @Param("timestamp") String timestamp) {
|
||||
public Result syncPc(@Param("files") String files, @Param("timestamp") String timestamp) {
|
||||
try {
|
||||
if(ObjectUtil.isEmpty(jsonObject)){
|
||||
if(ObjectUtil.isEmpty(files)){
|
||||
return Result.error("请选择要同步的文件");
|
||||
}
|
||||
if(ObjectUtil.isEmpty(timestamp)){
|
||||
return Result.error("二维码已失效,请刷新电脑端二维码后重试");
|
||||
}
|
||||
|
||||
// 手机端以表单字符串提交附件 JSON,后端显式解析,避免参数自动转换失败导致 500。
|
||||
List<NutMap> fileList = Json.fromJsonAsList(NutMap.class, files);
|
||||
if(ObjectUtil.isEmpty(fileList)){
|
||||
return Result.error("请选择要同步的文件");
|
||||
}
|
||||
|
||||
ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + SecurityUtil.getUserLoginname() + ":*");
|
||||
ScanResult<String> scan = null;
|
||||
int sendCount = 0;
|
||||
do {
|
||||
scan = redisService.scan(scan == null ? ScanParams.SCAN_POINTER_START : scan.getStringCursor(), match);
|
||||
for (String key : scan.getResult()) {
|
||||
NutMap data = NutMap.NEW().addv("action", "h5-scan-code-upload-file-" + timestamp).addv("files", jsonObject);
|
||||
NutMap data = NutMap.NEW().addv("action", "h5-scan-code-upload-file-" + timestamp).addv("files", fileList);
|
||||
pubSubService.fire(key, Json.toJson(data));
|
||||
sendCount++;
|
||||
}
|
||||
} while (!scan.isCompleteIteration());
|
||||
|
||||
if(sendCount == 0){
|
||||
return Result.error("未找到电脑端上传窗口,请保持电脑端二维码页面打开");
|
||||
}
|
||||
|
||||
return Result.success().addData(null);
|
||||
} catch (Exception e) {
|
||||
log.error("h5扫码上传文件同步到PC失败,loginName={}, timestamp={}, filesLength={}",
|
||||
getCurrentLoginName(), timestamp, ObjectUtil.isEmpty(files) ? 0 : files.length(), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getCurrentLoginName() {
|
||||
try {
|
||||
return SecurityUtil.getUserLoginname();
|
||||
} catch (Exception e) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private String getUploadFileName(TempFile file) {
|
||||
return file == null ? "" : file.getSubmittedFileName();
|
||||
}
|
||||
|
||||
private long getUploadFileSize(TempFile file) {
|
||||
return file == null ? 0 : file.getSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.budwk.app.task.job.staffmanage;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.sys.models.Sys_msg;
|
||||
import com.budwk.app.sys.services.SysMsgService;
|
||||
import com.budwk.app.web.commons.base.Globals;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayConfig;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayMsgLogService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Times;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福定时任务,每天向当天生日的会员发送祝福消息。
|
||||
*/
|
||||
@IocBean
|
||||
public class UserBirthdayJob implements Job {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private SysMsgService sysMsgService;
|
||||
|
||||
@Inject
|
||||
private UserBirthdayMsgLogService userBirthdayMsgLogService;
|
||||
|
||||
/**
|
||||
* 查询当天生日会员并发送生日贺卡链接。
|
||||
*/
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
loginname,
|
||||
username,
|
||||
unitName,
|
||||
unionName,
|
||||
mobile
|
||||
FROM
|
||||
vw_user
|
||||
WHERE
|
||||
MONTH(birthday) = MONTH(CURDATE())
|
||||
AND DAY(birthday) = DAY(CURDATE())
|
||||
AND member = 1
|
||||
AND birthday IS NOT NULL
|
||||
""");
|
||||
sql.setCallback(Sqls.callback.maps());
|
||||
dao.execute(sql);
|
||||
List<NutMap> users = sql.getList(NutMap.class);
|
||||
if (CollUtil.isEmpty(users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserBirthdayConfig config = dao.fetch(UserBirthdayConfig.class, Cnd.NEW().desc("updatedAt"));
|
||||
String link = Globals.AppDomain + "/platform/staffManage/birthday/manage/h5?id=" + StrUtil.blankToDefault(config == null ? "" : config.getBirthdayUrl(), "");
|
||||
List<String> loginNames = users.stream().map(user -> user.getString("loginname")).distinct().toList();
|
||||
String title = "请点击查收您的生日祝福";
|
||||
String content = "校工会祝您:生日快乐,幸福安康!";
|
||||
|
||||
Sys_msg sysMsg = new Sys_msg();
|
||||
sysMsg.setTitle(title);
|
||||
sysMsg.setNote(content);
|
||||
sysMsg.setUrl(link);
|
||||
sysMsg.setType("user");
|
||||
sysMsg.setSendType("hide");
|
||||
sysMsg.setWechatEnterprise(true);
|
||||
sysMsg.setSendAt(Times.getTS());
|
||||
sysMsg.setCreatedBy("system");
|
||||
sysMsgService.saveMsg(sysMsg, loginNames.toArray(String[]::new), true);
|
||||
userBirthdayMsgLogService.insertLogs(users, title, content, "", link);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@@ -75,6 +76,12 @@ public class ActivityReimbursementApplyController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.apply")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/apply/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activityReimbursement.apply")
|
||||
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/apply/form.html")
|
||||
@@ -84,7 +91,7 @@ public class ActivityReimbursementApplyController {
|
||||
|
||||
@At
|
||||
@ApiOperation("保存申请")
|
||||
@SaCheckPermission("member.apply.submit")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "保存申请,申请人: ${args[0].username}")
|
||||
public Result save(@Param("data") ActivityReimbursementInfo activityReimbursementInfo) {
|
||||
activityReimbursementInfo.setUserId(SecurityUtil.getUserId());
|
||||
@@ -113,7 +120,7 @@ public class ActivityReimbursementApplyController {
|
||||
@At
|
||||
@ApiOperation("提交申请")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("member.apply.submit")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "提交申请,申请人: ${args[0].username}")
|
||||
public Result submit(@Param("data") ActivityReimbursementInfo activityReimbursementInfo) {
|
||||
activityReimbursementInfo.setUserId(SecurityUtil.getUserId());
|
||||
@@ -160,7 +167,7 @@ public class ActivityReimbursementApplyController {
|
||||
@At
|
||||
@ApiOperation("重新提交申请")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("member.apply.submit")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "重新提交申请,申请人: ${args[0].username}")
|
||||
public Result submitAgain(@Param("data") ActivityReimbursementInfo activityReimbursementInfo, @Param("taskId") Long taskId) {
|
||||
if (StrUtil.isBlank(activityReimbursementInfo.getDeclareId())) {
|
||||
@@ -191,7 +198,7 @@ public class ActivityReimbursementApplyController {
|
||||
|
||||
@At
|
||||
@ApiOperation("获取当前用户活动报销")
|
||||
@SaCheckPermission("activityReimbursement.apply")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
public Result getActivityReimbursementByUser(String id, String outlayManageSource, String clubId) {
|
||||
String activityType = "";
|
||||
if (StrUtil.isNotBlank(id)) {
|
||||
@@ -286,7 +293,7 @@ public class ActivityReimbursementApplyController {
|
||||
|
||||
@At
|
||||
@ApiOperation("获取收款人卡号")
|
||||
@SaCheckPermission("activityReimbursement.apply")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
public Result getCardNumberByPayeeId(String username) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -315,7 +322,7 @@ public class ActivityReimbursementApplyController {
|
||||
|
||||
@At
|
||||
@ApiOperation("获取申报的记录")
|
||||
@SaCheckPermission("activityReimbursement.apply")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
public Result getBudgetByYear(String outlayManageSource, String clubId) {
|
||||
List<NutMap> budgetMoney = activityReimbursementService.getBudgetMoney(outlayManageSource, clubId);
|
||||
return Result.success(budgetMoney);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
@@ -13,6 +14,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
@@ -43,6 +45,12 @@ public class ActivityReimbursementBranchUnionController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.branchUnion")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/branchUnion/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activityReimbursement.branchUnion")
|
||||
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/branchunion/form.html")
|
||||
@@ -52,7 +60,7 @@ public class ActivityReimbursementBranchUnionController {
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,分工会审核列表")
|
||||
@SaCheckPermission("activityReimbursement.branchUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.branchUnion", "h5.activityReimbursement.branchUnion"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -111,6 +119,8 @@ public class ActivityReimbursementBranchUnionController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
@@ -15,6 +16,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -43,9 +45,15 @@ public class ActivityReimbursementCashierController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.cashier")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/cashier/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,出纳支付列表")
|
||||
@SaCheckPermission("activityReimbursement.cashier")
|
||||
@SaCheckPermission(value = {"activityReimbursement.cashier", "h5.activityReimbursement.cashier"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -112,6 +120,8 @@ public class ActivityReimbursementCashierController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
@@ -13,6 +14,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
@@ -43,6 +45,12 @@ public class ActivityReimbursementClubPrincipalController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.clubPrincipal")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/clubPrincipal/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activityReimbursement.clubPrincipal")
|
||||
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/clubprincipal/form.html")
|
||||
@@ -52,7 +60,7 @@ public class ActivityReimbursementClubPrincipalController {
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,协会负责人审核列表")
|
||||
@SaCheckPermission("activityReimbursement.clubPrincipal")
|
||||
@SaCheckPermission(value = {"activityReimbursement.clubPrincipal", "h5.activityReimbursement.clubPrincipal"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -111,6 +119,8 @@ public class ActivityReimbursementClubPrincipalController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -22,6 +23,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -58,9 +60,15 @@ public class ActivityReimbursementDeputySchoolUnionController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.deputySchoolUnion")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/deputySchoolUnion/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,校工会副主席审核")
|
||||
@SaCheckPermission("activityReimbursement.deputySchoolUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.deputySchoolUnion", "h5.activityReimbursement.deputySchoolUnion"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -131,6 +139,8 @@ public class ActivityReimbursementDeputySchoolUnionController {
|
||||
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
@@ -140,7 +150,7 @@ public class ActivityReimbursementDeputySchoolUnionController {
|
||||
} else {
|
||||
cnd.orderBy("info." + pageParam.getPageOrderName(), PageUtil.getOrder(pageParam.getPageOrderBy()));
|
||||
}
|
||||
cnd.groupBy("info.id");
|
||||
cnd.groupBy("t.id");
|
||||
sql.setCondition(cnd);
|
||||
Pagination<NutMap> pagination = activityReimbursementService.listPageMap(pageParam.getPageNumber(), pageParam.getPageSize(), sql);
|
||||
return Result.success().addData(pagination);
|
||||
@@ -148,7 +158,7 @@ public class ActivityReimbursementDeputySchoolUnionController {
|
||||
|
||||
@At
|
||||
@ApiOperation("预算/报销汇总表")
|
||||
@SaCheckPermission("activityReimbursement.deputySchoolUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.deputySchoolUnion", "h5.activityReimbursement.deputySchoolUnion"}, mode = SaMode.OR)
|
||||
public Result budgetSummaryData(CommonPageParam pageParam) {
|
||||
if (pageParam.getYear() == null) {
|
||||
return Result.error("请选择年度");
|
||||
@@ -168,7 +178,7 @@ public class ActivityReimbursementDeputySchoolUnionController {
|
||||
|
||||
@At
|
||||
@SLog(tag = "报销", msg = "校工会副主席审核了一条记录")
|
||||
@SaCheckPermission("activityReimbursement.deputySchoolUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.deputySchoolUnion", "h5.activityReimbursement.deputySchoolUnion"}, mode = SaMode.OR)
|
||||
public Result doReview(@Param("data") String param, String id) {
|
||||
Dict args = Json.fromJson(Dict.class, param);
|
||||
if (args.getInt("submitType") == 1) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controlle
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.convert.NumberChineseFormatter;
|
||||
import cn.hutool.core.convert.NumberWordFormatter;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
@@ -89,9 +90,15 @@ public class ActivityReimbursementMineController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.mine")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/mine/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,我的申请列表")
|
||||
@SaCheckPermission("activityReimbursement.mine")
|
||||
@SaCheckPermission(value = {"activityReimbursement.mine", "h5.activityReimbursement.mine"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -215,7 +222,7 @@ public class ActivityReimbursementMineController {
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("activityReimbursement.mine")
|
||||
@SaCheckPermission(value = {"activityReimbursement.mine", "h5.activityReimbursement.mine"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动报销", msg = "导出报销凭证")
|
||||
public void doExportReimbursement(@Valid String id, HttpServletResponse response) {
|
||||
HashMap<String, Object> docData = new HashMap<>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
@@ -15,6 +16,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -45,9 +47,15 @@ public class ActivityReimbursementSchoolFinanceController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.schoolFinance")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/schoolFinance/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,校工会财务审核")
|
||||
@SaCheckPermission("activityReimbursement.schoolFinance")
|
||||
@SaCheckPermission(value = {"activityReimbursement.schoolFinance", "h5.activityReimbursement.schoolFinance"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -114,6 +122,8 @@ public class ActivityReimbursementSchoolFinanceController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
@@ -15,6 +16,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -46,6 +48,12 @@ public class ActivityReimbursementSchoolPrincipalController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.schoolPrincipal")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/schoolPrincipal/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activityReimbursement.schoolPrincipal")
|
||||
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/schoolprincipal/form.html")
|
||||
@@ -55,7 +63,7 @@ public class ActivityReimbursementSchoolPrincipalController {
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,校工会负责人审核列表")
|
||||
@SaCheckPermission("activityReimbursement.schoolPrincipal")
|
||||
@SaCheckPermission(value = {"activityReimbursement.schoolPrincipal", "h5.activityReimbursement.schoolPrincipal"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -123,6 +131,8 @@ public class ActivityReimbursementSchoolPrincipalController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Exps;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
@@ -61,6 +62,12 @@ public class ActivityReimbursementSchoolUnionController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.activityReimbursement.schoolUnion")
|
||||
@Ok("beetl:/platform/zhghh5/activity/declarereimbursement/reimbursement/schoolUnion/index.html")
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("activityReimbursement.schoolUnion")
|
||||
@Ok("beetl:/platform/zhgh/activity/declarereimbursement/reimbursement/schoolunion/form.html")
|
||||
@@ -70,7 +77,7 @@ public class ActivityReimbursementSchoolUnionController {
|
||||
|
||||
@At
|
||||
@ApiOperation("活动报销,校工会审核列表")
|
||||
@SaCheckPermission("activityReimbursement.schoolUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.schoolUnion", "h5.activityReimbursement.schoolUnion"}, mode = SaMode.OR)
|
||||
public Result pageData(CommonPageParam pageParam) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -139,6 +146,8 @@ public class ActivityReimbursementSchoolUnionController {
|
||||
|
||||
if (pageParam.getAudit()) {
|
||||
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
|
||||
// 已审核列表只展示同一流程实例同一节点最新的历史任务,避免退回重提后重复显示。
|
||||
cnd.and(Exps.inSql("t.id", "SELECT MAX(latestTask.id) FROM wf_process_task latestTask WHERE latestTask.processInstanceId = t.processInstanceId AND latestTask.taskName = t.taskName AND latestTask.taskState IN (" + ProcessTaskStateEnum.FINISHED.getCode() + "," + ProcessTaskStateEnum.WITHDRAW.getCode() + "," + ProcessTaskStateEnum.INTERRUPT.getCode() + ")"));
|
||||
} else {
|
||||
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
|
||||
}
|
||||
@@ -157,7 +166,7 @@ public class ActivityReimbursementSchoolUnionController {
|
||||
|
||||
@At
|
||||
@SLog(tag = "报销", msg = "校工会审核了一条记录")
|
||||
@SaCheckPermission("activityReimbursement.schoolUnion")
|
||||
@SaCheckPermission(value = {"activityReimbursement.schoolUnion", "h5.activityReimbursement.schoolUnion"}, mode = SaMode.OR)
|
||||
public Result doReview(@Param("data") String param, String id) {
|
||||
activityReimbursementSchoolUnionService.doReview(param, id);
|
||||
return Result.success();
|
||||
|
||||
@@ -1,31 +1,17 @@
|
||||
package com.budwk.app.zhgh.dayofficework.buildHome.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.base.utils.SysOfficeTemplateUtil;
|
||||
import com.budwk.app.sys.models.Sys_union;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.models.BuildHomeLittleHouse;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.param.BuildHomeLittleHousePageForm;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.service.BuildHomeLittleHouseService;
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
@@ -34,12 +20,8 @@ import org.nutz.mvc.annotation.Ok;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/buildHome/littleHouse")
|
||||
@@ -48,10 +30,9 @@ import java.util.stream.IntStream;
|
||||
@Api(value = "建家小家建设情况")
|
||||
public class BuildHomeLittleHouseController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
@Inject("refer:buildHomeLittleHouseServiceImpl")
|
||||
private BuildHomeLittleHouseService buildHomeLittleHouseService;
|
||||
|
||||
@Inject
|
||||
private SysOfficeTemplateUtil sysOfficeTemplateUtil;
|
||||
|
||||
@@ -65,41 +46,47 @@ public class BuildHomeLittleHouseController {
|
||||
@At
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result pageData(BuildHomeLittleHousePageForm pageForm) {
|
||||
Sql sql = Sqls.create("select * from build_home_little_house $condition");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("unionId", "=", SecurityUtil.getUnionId());
|
||||
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
||||
cnd.where().andLike("unitName", pageForm.getSearchKeyword());
|
||||
}
|
||||
cnd.asc("sortNum");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
return Result.success(buildHomeLittleHouseService.pageData(pageForm));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result currentUserOrg() {
|
||||
return Result.success(buildHomeLittleHouseService.currentUserOrg());
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result save(BuildHomeLittleHouse house) {
|
||||
if (house.getId() == null) {
|
||||
house.setUnionId(SecurityUtil.getUnionId());
|
||||
house.setUnionName(SecurityUtil.getUnionId());
|
||||
}
|
||||
dao.insertOrUpdate(house);
|
||||
buildHomeLittleHouseService.saveHouse(house);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At("/delete/?")
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result delete(@Valid Long id) {
|
||||
dao.delete(BuildHomeLittleHouse.class, id);
|
||||
buildHomeLittleHouseService.delete(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At("/detail/?")
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result detail(@Valid Long id) {
|
||||
BuildHomeLittleHouse house = dao.fetch(BuildHomeLittleHouse.class, id);
|
||||
return Result.success(house);
|
||||
return Result.success(buildHomeLittleHouseService.fetch(id));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result saveCoordinate(Long id, Double mapX, Double mapY) {
|
||||
buildHomeLittleHouseService.saveCoordinate(id, mapX, mapY);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("buildHome.littleHouse")
|
||||
public Result clearCoordinate(Long id) {
|
||||
buildHomeLittleHouseService.clearCoordinate(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At("/exportXlsx")
|
||||
@@ -107,23 +94,7 @@ public class BuildHomeLittleHouseController {
|
||||
@Ok("void")
|
||||
public void exportXlsx(HttpServletResponse response) {
|
||||
try {
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
Cnd cnd = Cnd.where("unionId", "=", unionId);
|
||||
|
||||
Sys_union union = dao.fetch(Sys_union.class, unionId);
|
||||
if (union == null) {
|
||||
throw new BaseException("未找到ID为 " + unionId + " 的工会信息");
|
||||
}
|
||||
|
||||
// 准备文档数据Map
|
||||
Map<String, Object> docMap = new HashMap<>();
|
||||
docMap.put("unionName", union.getName()); // 工会名称
|
||||
docMap.put("date", DateUtil.today()); // 当前日期
|
||||
|
||||
// 查询小家建设数据并设置序号
|
||||
List<BuildHomeLittleHouse> list = dao.query(BuildHomeLittleHouse.class, cnd);
|
||||
IntStream.range(0, list.size()).forEach(i -> list.get(i).setIndex(i + 1)); // 为每条数据设置序号
|
||||
docMap.put("list", list); // 将数据列表放入Map
|
||||
Map<String, Object> docMap = buildHomeLittleHouseService.buildExportData();
|
||||
|
||||
// 配置表格渲染策略
|
||||
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.budwk.app.zhgh.dayofficework.buildHome.models;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("build_home_little_house")
|
||||
@@ -22,6 +25,11 @@ public class BuildHomeLittleHouse extends BaseModel {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String unitName;
|
||||
|
||||
@Column
|
||||
@Comment("单位ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String unitId;
|
||||
|
||||
@Column
|
||||
@Comment("分工会ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@@ -52,6 +60,21 @@ public class BuildHomeLittleHouse extends BaseModel {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String openTime;
|
||||
|
||||
@Column
|
||||
@Comment("图片视频资料")
|
||||
@ColDefine(type = ColType.MYSQL_JSON)
|
||||
private List<JSONObject> mediaFiles;
|
||||
|
||||
@Column
|
||||
@Comment("地图X坐标百分比")
|
||||
@ColDefine(type = ColType.FLOAT, width = 8, precision = 4)
|
||||
private Double mapX;
|
||||
|
||||
@Column
|
||||
@Comment("地图Y坐标百分比")
|
||||
@ColDefine(type = ColType.FLOAT, width = 8, precision = 4)
|
||||
private Double mapY;
|
||||
|
||||
@Column
|
||||
@Comment("排序")
|
||||
@ColDefine(type = ColType.INT)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.budwk.app.zhgh.dayofficework.buildHome.service;
|
||||
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.models.BuildHomeLittleHouse;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.param.BuildHomeLittleHousePageForm;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface BuildHomeLittleHouseService extends BaseService<BuildHomeLittleHouse> {
|
||||
|
||||
/**
|
||||
* 分页查询小家建设数据,系统管理员查看全部,其他用户查看本分工会。
|
||||
*
|
||||
* @param pageForm 查询分页参数。
|
||||
* @return 小家建设分页数据。
|
||||
*/
|
||||
Pagination pageData(BuildHomeLittleHousePageForm pageForm);
|
||||
|
||||
/**
|
||||
* 获取当前登录用户所属工会和单位,用于新增小家记录时自动带出组织信息。
|
||||
*
|
||||
* @return 当前用户组织信息。
|
||||
*/
|
||||
Map<String, String> currentUserOrg();
|
||||
|
||||
/**
|
||||
* 保存小家建设记录,新增时写入当前用户组织,编辑时保留原组织归属。
|
||||
*
|
||||
* @param house 小家建设记录。
|
||||
*/
|
||||
void saveHouse(BuildHomeLittleHouse house);
|
||||
|
||||
/**
|
||||
* 保存小家地图坐标,仅允许系统管理员操作。
|
||||
*
|
||||
* @param id 小家记录ID。
|
||||
* @param mapX 地图X坐标百分比。
|
||||
* @param mapY 地图Y坐标百分比。
|
||||
*/
|
||||
void saveCoordinate(Long id, Double mapX, Double mapY);
|
||||
|
||||
/**
|
||||
* 清空小家地图坐标,仅允许系统管理员操作。
|
||||
*
|
||||
* @param id 小家记录ID。
|
||||
*/
|
||||
void clearCoordinate(Long id);
|
||||
|
||||
/**
|
||||
* 准备小家建设汇总导出数据。
|
||||
*
|
||||
* @return 模板渲染数据。
|
||||
*/
|
||||
Map<String, Object> buildExportData();
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.budwk.app.zhgh.dayofficework.buildHome.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_union;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.models.BuildHomeLittleHouse;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.param.BuildHomeLittleHousePageForm;
|
||||
import com.budwk.app.zhgh.dayofficework.buildHome.service.BuildHomeLittleHouseService;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class BuildHomeLittleHouseServiceImpl extends BaseServiceImpl<BuildHomeLittleHouse> implements BuildHomeLittleHouseService {
|
||||
|
||||
public BuildHomeLittleHouseServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination pageData(BuildHomeLittleHousePageForm pageForm) {
|
||||
Sql sql = Sqls.create("select * from build_home_little_house $condition");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (!isSysAdmin()) {
|
||||
cnd.and("unionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
||||
SqlExpressionGroup searchGroup = new SqlExpressionGroup();
|
||||
searchGroup.orLike("unitName", pageForm.getSearchKeyword());
|
||||
searchGroup.orLike("unionName", pageForm.getSearchKeyword());
|
||||
cnd.where().and(searchGroup);
|
||||
}
|
||||
cnd.asc("sortNum");
|
||||
sql.setCondition(cnd);
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> currentUserOrg() {
|
||||
Map<String, String> currentUserOrg = new HashMap<>();
|
||||
View_user user = dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
|
||||
|
||||
String unionId = user != null && StrUtil.isNotBlank(user.getUnionId()) ? user.getUnionId() : SecurityUtil.getUnionId();
|
||||
String unionName = user != null ? user.getUnionName() : null;
|
||||
if (StrUtil.isBlank(unionName) && StrUtil.isNotBlank(unionId)) {
|
||||
Sys_union union = dao().fetch(Sys_union.class, unionId);
|
||||
unionName = union != null ? union.getName() : unionId;
|
||||
}
|
||||
|
||||
currentUserOrg.put("unionId", unionId);
|
||||
currentUserOrg.put("unionName", unionName);
|
||||
currentUserOrg.put("unitId", user != null ? user.getUnitId() : SecurityUtil.getUnitId());
|
||||
currentUserOrg.put("unitName", user != null ? user.getUnitName() : null);
|
||||
return currentUserOrg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveHouse(BuildHomeLittleHouse house) {
|
||||
if (house.getId() == null) {
|
||||
fillCurrentUserOrg(house);
|
||||
} else {
|
||||
keepOriginalOrg(house);
|
||||
}
|
||||
dao().insertOrUpdate(house);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveCoordinate(Long id, Double mapX, Double mapY) {
|
||||
if (!isSysAdmin()) {
|
||||
throw new BaseException("仅系统管理员可以采集坐标");
|
||||
}
|
||||
BuildHomeLittleHouse house = fetchRequiredHouse(id);
|
||||
if (!isValidCoordinate(mapX) || !isValidCoordinate(mapY)) {
|
||||
throw new BaseException("坐标范围必须在0到100之间");
|
||||
}
|
||||
dao().update(BuildHomeLittleHouse.class, Chain.make("mapX", mapX).add("mapY", mapY), Cnd.where("id", "=", house.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCoordinate(Long id) {
|
||||
if (!isSysAdmin()) {
|
||||
throw new BaseException("仅系统管理员可以清空坐标");
|
||||
}
|
||||
BuildHomeLittleHouse house = fetchRequiredHouse(id);
|
||||
dao().update(BuildHomeLittleHouse.class, Chain.make("mapX", null).add("mapY", null), Cnd.where("id", "=", house.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> buildExportData() {
|
||||
boolean sysAdmin = isSysAdmin();
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (!sysAdmin) {
|
||||
cnd.and("unionId", "=", unionId);
|
||||
}
|
||||
cnd.asc("sortNum");
|
||||
|
||||
Sys_union union = dao().fetch(Sys_union.class, unionId);
|
||||
if (!sysAdmin && union == null) {
|
||||
throw new BaseException("未找到ID为 " + unionId + " 的工会信息");
|
||||
}
|
||||
|
||||
List<BuildHomeLittleHouse> list = query(cnd);
|
||||
IntStream.range(0, list.size()).forEach(i -> list.get(i).setIndex(i + 1));
|
||||
|
||||
Map<String, Object> docMap = new HashMap<>();
|
||||
docMap.put("unionName", sysAdmin ? "全部工会" : union.getName());
|
||||
docMap.put("date", DateUtil.today());
|
||||
docMap.put("list", list);
|
||||
return docMap;
|
||||
}
|
||||
|
||||
private void fillCurrentUserOrg(BuildHomeLittleHouse house) {
|
||||
Map<String, String> currentUserOrg = currentUserOrg();
|
||||
house.setUnionId(currentUserOrg.get("unionId"));
|
||||
house.setUnionName(currentUserOrg.get("unionName"));
|
||||
house.setUnitId(currentUserOrg.get("unitId"));
|
||||
house.setUnitName(currentUserOrg.get("unitName"));
|
||||
}
|
||||
|
||||
private void keepOriginalOrg(BuildHomeLittleHouse house) {
|
||||
BuildHomeLittleHouse oldHouse = dao().fetch(BuildHomeLittleHouse.class, house.getId());
|
||||
if (oldHouse == null) {
|
||||
fillCurrentUserOrg(house);
|
||||
return;
|
||||
}
|
||||
house.setUnionId(oldHouse.getUnionId());
|
||||
house.setUnionName(oldHouse.getUnionName());
|
||||
house.setUnitId(oldHouse.getUnitId());
|
||||
house.setUnitName(oldHouse.getUnitName());
|
||||
}
|
||||
|
||||
private BuildHomeLittleHouse fetchRequiredHouse(Long id) {
|
||||
if (id == null) {
|
||||
throw new BaseException("小家记录ID不能为空");
|
||||
}
|
||||
BuildHomeLittleHouse house = dao().fetch(BuildHomeLittleHouse.class, id);
|
||||
if (house == null) {
|
||||
throw new BaseException("未找到小家记录");
|
||||
}
|
||||
return house;
|
||||
}
|
||||
|
||||
private boolean isSysAdmin() {
|
||||
return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name());
|
||||
}
|
||||
|
||||
private boolean isValidCoordinate(Double value) {
|
||||
return value != null && value >= 0 && value <= 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.budwk.app.zhgh.dayofficework.caredata;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.dayofficework.caredata.service.CareDataLeaderService;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/careData/leader")
|
||||
@Api("领导驾驶舱")
|
||||
@Ok("json:full")
|
||||
public class CareDataLeaderCon {
|
||||
|
||||
@Inject("refer:careDataLeaderServiceImpl")
|
||||
private CareDataLeaderService careDataLeaderService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/indexOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("proposal")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/proposalOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void proposal() {
|
||||
}
|
||||
|
||||
@At("condolence")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/condolenceOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void condolence() {
|
||||
}
|
||||
|
||||
@At("baseUnion")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/baseUnionOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void baseUnion() {
|
||||
}
|
||||
|
||||
@At("club")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/clubOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void club() {
|
||||
}
|
||||
|
||||
@At("dataMetric")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/dataMetricOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void dataMetric() {
|
||||
}
|
||||
|
||||
@At("assetData")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/assetDataOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void assetData() {
|
||||
}
|
||||
|
||||
@At("staffHome")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/careData/leader/staffHomeOverlay.html")
|
||||
@SaCheckPermission("careData.union")
|
||||
public void staffHome() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result proposalData(String sessionId) {
|
||||
return Result.success(careDataLeaderService.proposalData(sessionId));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result condolenceData(Integer year) {
|
||||
return Result.success(careDataLeaderService.condolenceData(year));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result baseUnionData() {
|
||||
return Result.success(careDataLeaderService.baseUnionData());
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result clubData() {
|
||||
return Result.success(careDataLeaderService.clubData());
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result dataMetricData(Integer year) {
|
||||
return Result.success(careDataLeaderService.dataMetricData(year));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result assetDataData() {
|
||||
return Result.success(careDataLeaderService.assetDataData());
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("careData.union")
|
||||
public Result staffHomeData() {
|
||||
return Result.success(careDataLeaderService.staffHomeData());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.budwk.app.zhgh.dayofficework.careData;
|
||||
package com.budwk.app.zhgh.dayofficework.caredata;
|
||||
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.budwk.app.zhgh.dayofficework.caredata.service;
|
||||
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
/**
|
||||
* 领导驾驶舱数据服务。
|
||||
*/
|
||||
public interface CareDataLeaderService {
|
||||
|
||||
/**
|
||||
* 查询提案概览、办结和满意率数据。
|
||||
*
|
||||
* @param sessionId 教代会届次ID,为空时取最新届次。
|
||||
* @return 提案概览数据,包含 session、overview、caseResults。
|
||||
*/
|
||||
NutMap proposalData(String sessionId);
|
||||
|
||||
/**
|
||||
* 查询慰问类型排行数据。
|
||||
*
|
||||
* @param year 年份,为空时取当前年份。
|
||||
* @return 慰问排行数据。
|
||||
*/
|
||||
NutMap condolenceData(Integer year);
|
||||
|
||||
/**
|
||||
* 查询分工会会员数排行。
|
||||
*
|
||||
* @return 分工会会员统计数据。
|
||||
*/
|
||||
NutMap baseUnionData();
|
||||
|
||||
/**
|
||||
* 查询协会人数排行。
|
||||
*
|
||||
* @return 协会人员统计数据。
|
||||
*/
|
||||
NutMap clubData();
|
||||
|
||||
/**
|
||||
* 查询驾驶舱中部指标数据。
|
||||
*
|
||||
* @param year 年份,为空时取当前年份。
|
||||
* @return 预算、疗休养、荣誉、困难帮扶和报销统计数据。
|
||||
*/
|
||||
NutMap dataMetricData(Integer year);
|
||||
|
||||
/**
|
||||
* 查询资产使用状态分布。
|
||||
*
|
||||
* @return 资产使用状态统计数据。
|
||||
*/
|
||||
NutMap assetDataData();
|
||||
|
||||
/**
|
||||
* 查询职工小家地图点位数据。
|
||||
*
|
||||
* @return 职工小家列表数据。
|
||||
*/
|
||||
NutMap staffHomeData();
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
package com.budwk.app.zhgh.dayofficework.caredata.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.dayofficework.caredata.service.CareDataLeaderService;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class CareDataLeaderServiceImpl implements CareDataLeaderService {
|
||||
|
||||
private static final int FINISHED_STATE = 20;
|
||||
|
||||
private final Dao dao;
|
||||
|
||||
public CareDataLeaderServiceImpl(Dao dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap proposalData(String sessionId) {
|
||||
String currentSessionId = StrUtil.blankToDefault(sessionId, latestSessionId());
|
||||
if (StrUtil.isBlank(currentSessionId)) {
|
||||
return NutMap.NEW();
|
||||
}
|
||||
|
||||
NutMap session = sessionInfo(currentSessionId);
|
||||
List<NutMap> caseResults = caseResultData(currentSessionId);
|
||||
NutMap overview = overviewData(currentSessionId, caseResults);
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("session", session)
|
||||
.addv("overview", overview)
|
||||
.addv("caseResults", caseResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap condolenceData(Integer year) {
|
||||
int targetYear = year == null ? LocalDate.now().getYear() : year;
|
||||
List<NutMap> rows = condolenceTypeRows(targetYear);
|
||||
long total = rows.stream().mapToLong(row -> row.getLong("count", 0L)).sum();
|
||||
List<NutMap> sortedRows = new ArrayList<>(rows);
|
||||
sortedRows.sort((left, right) -> Long.compare(right.getLong("count", 0L), left.getLong("count", 0L)));
|
||||
List<NutMap> topRows = new ArrayList<>();
|
||||
int index = 1;
|
||||
for (NutMap row : sortedRows) {
|
||||
if (index > 5) {
|
||||
break;
|
||||
}
|
||||
long count = row.getLong("count", 0L);
|
||||
topRows.add(NutMap.NEW()
|
||||
.addv("index", index++)
|
||||
.addv("typeId", row.getString("id", ""))
|
||||
.addv("typeCode", row.getString("code", ""))
|
||||
.addv("name", row.getString("name", ""))
|
||||
.addv("count", count)
|
||||
.addv("rate", percent(count, total))
|
||||
.addv("rateValue", percentValue(count, total)));
|
||||
}
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("year", targetYear)
|
||||
.addv("total", total)
|
||||
.addv("types", topRows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap baseUnionData() {
|
||||
return NutMap.NEW().addv("unions", baseUnionRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap clubData() {
|
||||
return NutMap.NEW().addv("clubs", clubRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap dataMetricData(Integer year) {
|
||||
int targetYear = year == null ? LocalDate.now().getYear() : year;
|
||||
return NutMap.NEW()
|
||||
.addv("year", targetYear)
|
||||
.addv("budgetTotal", schoolBudgetTotal(targetYear))
|
||||
.addv("tourCount", tourCount(targetYear))
|
||||
.addv("honorCount", honorCount(targetYear))
|
||||
.addv("difficultCount", difficultCount(targetYear))
|
||||
.addv("reimburseTotal", reimburseTotal(targetYear));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap assetDataData() {
|
||||
List<NutMap> states = assetUsageStateRows();
|
||||
long total = states.stream().mapToLong(row -> row.getLong("value", 0L)).sum();
|
||||
return NutMap.NEW()
|
||||
.addv("total", total)
|
||||
.addv("states", states);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap staffHomeData() {
|
||||
return NutMap.NEW().addv("houses", littleHouseRows());
|
||||
}
|
||||
|
||||
private List<NutMap> littleHouseRows() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
unionName,
|
||||
unitName,
|
||||
address,
|
||||
mediaFiles,
|
||||
mapX,
|
||||
mapY,
|
||||
sortNum
|
||||
FROM build_home_little_house
|
||||
WHERE mapX IS NOT NULL
|
||||
AND mapY IS NOT NULL
|
||||
$unionFilter
|
||||
ORDER BY sortNum ASC, id ASC
|
||||
""");
|
||||
setUnionFilter(sql, "unionId", null);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private String latestSessionId() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT id
|
||||
FROM teacher_congress_session
|
||||
ORDER BY startDate DESC
|
||||
LIMIT 1
|
||||
""");
|
||||
return firstMap(sql).getString("id", "");
|
||||
}
|
||||
|
||||
private List<NutMap> condolenceTypeRows(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.id,
|
||||
t.code,
|
||||
t.name,
|
||||
COUNT(info.id) AS count
|
||||
FROM condolence_type t
|
||||
LEFT JOIN condolence info ON info.type = t.id
|
||||
AND YEAR(info.createTime) = @year
|
||||
$unionFilter
|
||||
WHERE t.enable = 1
|
||||
GROUP BY t.id, t.code, t.name, t.sortNum
|
||||
ORDER BY count DESC, t.sortNum ASC, t.code ASC
|
||||
""");
|
||||
sql.setParam("year", year);
|
||||
setUnionFilter(sql, "info.applyUnionId", null);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> baseUnionRows() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
un.id,
|
||||
un.name AS unionname,
|
||||
un.unionCode,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM vw_user u
|
||||
WHERE u.member = 1
|
||||
AND u.unionId = un.id
|
||||
) AS value
|
||||
FROM sys_union un
|
||||
WHERE un.delFlag = 0
|
||||
$unionFilter
|
||||
ORDER BY un.unionCode
|
||||
""");
|
||||
setUnionFilter(sql, "un.id", null);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> clubRows() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
c.id,
|
||||
c.clubName,
|
||||
c.clubCode,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM club_user cu
|
||||
WHERE cu.clubId = c.id
|
||||
AND cu.delFlag = 0
|
||||
) AS value
|
||||
FROM sys_club c
|
||||
WHERE c.delFlag = 0
|
||||
AND c.dismiss = 0
|
||||
ORDER BY c.clubCode
|
||||
""");
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> assetUsageStateRows() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
IFNULL(NULLIF(TRIM(t1.assetUsageStateName), ''), @emptyName) AS name,
|
||||
SUM(IFNULL(t1.assetQuantity, 0)) AS value
|
||||
FROM `asset` t1
|
||||
WHERE 1 = 1
|
||||
$unionFilter
|
||||
GROUP BY IFNULL(NULLIF(TRIM(t1.assetUsageStateName), ''), @emptyName)
|
||||
ORDER BY value DESC, name ASC
|
||||
""");
|
||||
sql.setParam("emptyName", "未填写");
|
||||
setUnionFilter(sql, "t1.assetUseUnionId", null);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private BigDecimal schoolBudgetTotal(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT IFNULL(SUM(totalQuota), 0) AS total
|
||||
FROM outlay_manage_school
|
||||
WHERE delFlag = 0
|
||||
AND `year` = @year
|
||||
""");
|
||||
sql.setParam("year", year);
|
||||
return decimalValue(firstMap(sql), "total");
|
||||
}
|
||||
|
||||
private long tourCount(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT COUNT(DISTINCT info.id) AS count
|
||||
FROM recuperation_enroll info
|
||||
WHERE info.isNormal = 1
|
||||
AND info.stateId NOT IN (2715, 2725, 2735)
|
||||
AND YEAR(info.signingUptime) = @year
|
||||
$unionFilter
|
||||
""");
|
||||
// 当前项目没有参考项目的 tour_ledger 表,疗休养人数改用现有报名表统计正常且未被驳回的数据。
|
||||
sql.setParam("year", year);
|
||||
setUnionFilter(sql, "info.takePartInUnionId", null);
|
||||
return firstMap(sql).getLong("count", 0L);
|
||||
}
|
||||
|
||||
private long honorCount(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT COUNT(h.id) AS count
|
||||
FROM honor h
|
||||
WHERE h.delFlag = 0
|
||||
AND YEAR(h.grantDate) = @year
|
||||
$unionFilter
|
||||
""");
|
||||
sql.setParam("year", year);
|
||||
setUnionFilter(sql, "h.unionId", null);
|
||||
return firstMap(sql).getLong("count", 0L);
|
||||
}
|
||||
|
||||
private long difficultCount(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT COUNT(DISTINCT info.id) AS count
|
||||
FROM difficult_help_info info
|
||||
INNER JOIN wf_process_instance ins ON ins.businessNo = info.id
|
||||
WHERE info.delFlag = 0
|
||||
AND ins.state = @finished
|
||||
AND YEAR(info.applyTime) = @year
|
||||
$unionFilter
|
||||
""");
|
||||
sql.setParam("year", year);
|
||||
sql.setParam("finished", FINISHED_STATE);
|
||||
setUnionFilter(sql, "info.unionId", null);
|
||||
return firstMap(sql).getLong("count", 0L);
|
||||
}
|
||||
|
||||
private BigDecimal reimburseTotal(int year) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT IFNULL(SUM(IFNULL(info.money, 0)), 0) AS total
|
||||
FROM union_reimburse info
|
||||
WHERE YEAR(info.createTime) = @year
|
||||
$unionFilter
|
||||
""");
|
||||
// 当前项目 union_reimburse 没有参考项目的 realMoney、condolenceMoney、stateId 字段,金额按现有 money 字段统计。
|
||||
sql.setParam("year", year);
|
||||
setUnionFilter(sql, "info.unionId", null);
|
||||
return decimalValue(firstMap(sql), "total");
|
||||
}
|
||||
|
||||
private NutMap sessionInfo(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT id, fullName
|
||||
FROM teacher_congress_session
|
||||
WHERE id = @sessionId
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
return firstMap(sql);
|
||||
}
|
||||
|
||||
private NutMap overviewData(String sessionId, List<NutMap> caseResults) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
COUNT(info.id) AS total,
|
||||
SUM(CASE WHEN ins.state = @finished THEN 1 ELSE 0 END) AS doneCount
|
||||
FROM proposal_info info
|
||||
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
|
||||
WHERE info.sessionId = @sessionId
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
sql.setParam("finished", FINISHED_STATE);
|
||||
NutMap overview = firstMap(sql);
|
||||
|
||||
long filedCount = 0L;
|
||||
long rejectedCount = 0L;
|
||||
long suggestionCount = 0L;
|
||||
for (NutMap item : caseResults) {
|
||||
String name = item.getString("name", "");
|
||||
long count = item.getLong("count", 0L);
|
||||
if (containsAny(name, "不予", "不立案")) {
|
||||
rejectedCount += count;
|
||||
} else if (containsAny(name, "意见", "建议")) {
|
||||
suggestionCount += count;
|
||||
} else if (name.contains("立案")) {
|
||||
filedCount += count;
|
||||
}
|
||||
}
|
||||
|
||||
long total = overview.getLong("total", 0L);
|
||||
long doneCount = overview.getLong("doneCount", 0L);
|
||||
long satisfiedCount = satisfiedCount(sessionId);
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("total", total)
|
||||
.addv("filedCount", filedCount)
|
||||
.addv("suggestionCount", suggestionCount)
|
||||
.addv("rejectedCount", rejectedCount)
|
||||
.addv("doneCount", doneCount)
|
||||
.addv("satisfiedRate", percent(satisfiedCount, doneCount));
|
||||
}
|
||||
|
||||
private List<NutMap> caseResultData(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
d.code,
|
||||
d.name,
|
||||
COUNT(info.id) AS count
|
||||
FROM sys_dict parent
|
||||
INNER JOIN sys_dict d ON d.parentId = parent.id AND d.disabled = 0
|
||||
LEFT JOIN proposal_info info ON info.caseFilingResult = d.code AND info.sessionId = @sessionId
|
||||
WHERE parent.code = 'PROPOSAL_CASE_FILING_RESULT'
|
||||
GROUP BY d.id, d.code, d.name, d.location
|
||||
ORDER BY d.location
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private long satisfiedCount(String sessionId) {
|
||||
List<NutMap> dicts = feedbackDicts();
|
||||
List<NutMap> rows = feedbackRows(sessionId);
|
||||
return dicts.stream()
|
||||
.filter(dict -> {
|
||||
String name = dict.getString("name", "");
|
||||
return name.contains("满意") && !name.contains("不满意");
|
||||
})
|
||||
.mapToLong(dict -> rows.stream()
|
||||
.filter(row -> dict.getString("code", "").equals(row.getString("feedbackCode", "")))
|
||||
.count())
|
||||
.sum();
|
||||
}
|
||||
|
||||
private List<NutMap> feedbackDicts() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT d.code, d.name
|
||||
FROM sys_dict parent
|
||||
INNER JOIN sys_dict d ON d.parentId = parent.id AND d.disabled = 0
|
||||
WHERE parent.code = 'PROPOSAL_FEEDBACK'
|
||||
ORDER BY d.location
|
||||
""");
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
private List<NutMap> feedbackRows(String sessionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t.variable
|
||||
FROM proposal_info info
|
||||
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
|
||||
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id
|
||||
AND t.taskState = 20
|
||||
AND t.taskName = 'feedback'
|
||||
WHERE ins.state = @finished
|
||||
AND info.sessionId = @sessionId
|
||||
GROUP BY info.id, t.variable
|
||||
""");
|
||||
sql.setParam("sessionId", sessionId);
|
||||
sql.setParam("finished", FINISHED_STATE);
|
||||
List<NutMap> rawRows = listMap(sql);
|
||||
List<NutMap> rows = new ArrayList<>();
|
||||
for (NutMap row : rawRows) {
|
||||
String variableStr = row.getString("variable", "");
|
||||
if (StrUtil.isBlank(variableStr)) {
|
||||
continue;
|
||||
}
|
||||
NutMap variable = Json.fromJson(NutMap.class, variableStr);
|
||||
rows.add(NutMap.NEW().addv("feedbackCode", variable.getString("tf_feedback", "")));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private void setUnionFilter(Sql sql, String columnName, String varName) {
|
||||
String targetVarName = StrUtil.blankToDefault(varName, "unionFilter");
|
||||
if (canViewAllUnionData()) {
|
||||
sql.setVar(targetVarName, "");
|
||||
} else {
|
||||
sql.setVar(targetVarName, "AND " + columnName + " = @unionId");
|
||||
sql.setParam("unionId", SecurityUtil.getUnionId());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canViewAllUnionData() {
|
||||
return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
|
||||
}
|
||||
|
||||
private BigDecimal decimalValue(NutMap map, String key) {
|
||||
BigDecimal value = map.getAs(key, BigDecimal.class);
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private List<NutMap> listMap(Sql sql) {
|
||||
sql.setCallback(Sqls.callback.maps());
|
||||
dao.execute(sql);
|
||||
return sql.getList(NutMap.class);
|
||||
}
|
||||
|
||||
private NutMap firstMap(Sql sql) {
|
||||
sql.setCallback(Sqls.callback.map());
|
||||
dao.execute(sql);
|
||||
NutMap map = sql.getObject(NutMap.class);
|
||||
return map == null ? NutMap.NEW() : map;
|
||||
}
|
||||
|
||||
private boolean containsAny(String text, String... keywords) {
|
||||
if (StrUtil.isBlank(text)) {
|
||||
return false;
|
||||
}
|
||||
for (String keyword : keywords) {
|
||||
if (text.contains(keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String percent(long count, long total) {
|
||||
if (total <= 0) {
|
||||
return "0%";
|
||||
}
|
||||
BigDecimal value = BigDecimal.valueOf(count)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.divide(BigDecimal.valueOf(total), 1, RoundingMode.HALF_UP);
|
||||
return value.stripTrailingZeros().toPlainString() + "%";
|
||||
}
|
||||
|
||||
private BigDecimal percentValue(long count, long total) {
|
||||
if (total <= 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return BigDecimal.valueOf(count)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.divide(BigDecimal.valueOf(total), 1, RoundingMode.HALF_UP)
|
||||
.stripTrailingZeros();
|
||||
}
|
||||
}
|
||||
@@ -70,12 +70,12 @@ public class QsvQuizRankServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord>
|
||||
List<ExcelExportEntity> exportEntities = new ArrayList<>();
|
||||
exportEntities.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
exportEntities.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||
exportEntities.add(new ExcelExportEntity("所属工会", "unionName", 30));
|
||||
exportEntities.add(new ExcelExportEntity("所属单位", "unitName", 30));
|
||||
exportEntities.add(new ExcelExportEntity("性别", "sex", 20));
|
||||
exportEntities.add(new ExcelExportEntity("单位", "unitName", 30));
|
||||
exportEntities.add(new ExcelExportEntity("分工会", "unionName", 30));
|
||||
exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 30));
|
||||
if ("SCHEDULED".equals(mode) && pageForm.getAttemptDate() != null) {
|
||||
exportEntities.add(new ExcelExportEntity("答题日期", "attemptDate", 20));
|
||||
exportEntities.add(new ExcelExportEntity("答题时间", "submitTime", 20));
|
||||
exportEntities.add(new ExcelExportEntity("当天得分", "totalScore", 20));
|
||||
} else {
|
||||
exportEntities.add(new ExcelExportEntity("得分", "sumScore", 20));
|
||||
@@ -100,6 +100,7 @@ public class QsvQuizRankServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord>
|
||||
u.mobile,
|
||||
t1.totalScore,
|
||||
t1.attemptDate,
|
||||
t1.submitTime,
|
||||
sum(t1.totalScore) as sumScore
|
||||
FROM
|
||||
`qsv_user_answer_record` t1
|
||||
@@ -133,7 +134,9 @@ public class QsvQuizRankServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord>
|
||||
}
|
||||
|
||||
cnd.groupBy("t1.userId");
|
||||
cnd.desc("t1.totalScore");
|
||||
cnd.asc("t1.unionName");
|
||||
cnd.asc("t1.unitName");
|
||||
cnd.asc("t1.loginName");
|
||||
|
||||
Sql sql = Sqls.create(sqlBuilder.toString());
|
||||
sql.setCondition(cnd);
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.BirthdayGreetingSendVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayPageVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
/**
|
||||
* 移动端生日祝福墙控制器,提供最近生日人员、赠礼和贺卡详情能力。
|
||||
*/
|
||||
@IocBean
|
||||
@At("/mobile/birthday/greeting")
|
||||
@Ok("json:full")
|
||||
@Api(tags = "移动端生日祝福墙")
|
||||
public class BirthdayGreetingsController {
|
||||
|
||||
@Inject
|
||||
private UserBirthdayService userBirthdayService;
|
||||
|
||||
/**
|
||||
* 最近生日人员页面。
|
||||
*/
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhghh5/birthday/greeting.html")
|
||||
@SaCheckLogin
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 我收到的祝福页面。
|
||||
*/
|
||||
@At("/greeting_info")
|
||||
@Ok("beetl:/platform/zhghh5/birthday/greeting_info.html")
|
||||
@SaCheckLogin
|
||||
public void info() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 赠送祝福页面。
|
||||
*/
|
||||
@At("/greeting_list")
|
||||
@Ok("beetl:/platform/zhghh5/birthday/greeting_list.html")
|
||||
@SaCheckLogin
|
||||
public void list() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 贺卡详情页面。
|
||||
*/
|
||||
@At("/hk")
|
||||
@Ok("beetl:/platform/zhghh5/birthday/hk.html")
|
||||
@SaCheckLogin
|
||||
public void hk() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最近生日人员。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result pageData(UserBirthdayPageVO pageForm, String searchKeyWord) {
|
||||
return Result.success(userBirthdayService.listGreetingUsers(pageForm, searchKeyWord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询勾选的祝福对象。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result loadList(String greetingResult) {
|
||||
return Result.success(userBirthdayService.loadGreetingUsers(greetingResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询祝福礼物列表。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result giftList() {
|
||||
return Result.success(userBirthdayService.giftList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存赠送祝福记录。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result sendBlessing(BirthdayGreetingSendVO vo) {
|
||||
try {
|
||||
userBirthdayService.sendBlessing(vo);
|
||||
return Result.success();
|
||||
} catch (RuntimeException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户收到的祝福。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result giftInfo(UserBirthdayPageVO pageForm, String username) {
|
||||
return Result.success(userBirthdayService.giftInfo(pageForm, username));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单条贺卡祝福内容。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result findOne(String id) {
|
||||
return Result.success(userBirthdayService.findGreetingInfo(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayConfig;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayPageVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdaySendMsgVO;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 生日祝福管理控制器,提供 PC 管理端列表、配置、发送和导出接口。
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@At("/platform/staffManage/birthday/manage")
|
||||
@Api(tags = "生日祝福管理")
|
||||
public class UserBirthdayManageController {
|
||||
|
||||
@Inject
|
||||
private UserBirthdayService userBirthdayService;
|
||||
|
||||
/**
|
||||
* PC 端生日祝福管理页。
|
||||
*/
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/birthday/manage/index.html")
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* H5 生日贺卡页,文件 ID 从消息链接透传给页面。
|
||||
*/
|
||||
@At("/h5")
|
||||
@Ok("beetl:/platform/zhghh5/staffmanage/birthday/index.html")
|
||||
@SaCheckLogin
|
||||
public void h5(String id, HttpServletRequest request) {
|
||||
// H5 页面需要服务端注入默认值,避免模板变量缺失时 Beetl 渲染失败。
|
||||
request.setAttribute("fileId", id == null ? "" : id);
|
||||
request.setAttribute("userName", SecurityUtil.getUserUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询生日人员分页数据。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public Result pageData(UserBirthdayPageVO page) {
|
||||
return Result.success(userBirthdayService.pageBirthdayUsers(page));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出生日人员列表。
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public void doExport(UserBirthdayPageVO page, HttpServletResponse response) {
|
||||
userBirthdayService.exportXlsx(page, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按当前查询条件批量发送生日祝福。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public Result sendMsgByQueryUsers(UserBirthdaySendMsgVO vo) {
|
||||
try {
|
||||
userBirthdayService.sendMsgByQueryUsers(vo);
|
||||
return Result.success();
|
||||
} catch (RuntimeException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给指定职工发送生日祝福。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public Result sendMsgByUser(UserBirthdaySendMsgVO vo) {
|
||||
try {
|
||||
userBirthdayService.sendMsgByUser(vo);
|
||||
return Result.success();
|
||||
} catch (RuntimeException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生日贺卡配置,PC 管理端和 H5 展示页均会读取。
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result getConfig() {
|
||||
return Result.success(userBirthdayService.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存生日贺卡配置。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("staffManage.birthday.manage")
|
||||
public Result saveOrModifyConfig(UserBirthdayConfig config) {
|
||||
userBirthdayService.saveOrModifyConfig(config);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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.zhgh.staffmanage.birthday.service.UserBirthdayMsgLogService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayMsgLogPageVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
/**
|
||||
* 生日祝福消息日志控制器。
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@At("/platform/staffManage/birthday/msgLog")
|
||||
@Api(tags = "生日祝福消息日志")
|
||||
public class UserBirthdayMsgLogController {
|
||||
|
||||
@Inject
|
||||
private UserBirthdayMsgLogService userBirthdayMsgLogService;
|
||||
|
||||
/**
|
||||
* PC 端生日祝福发送日志页。
|
||||
*/
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/birthday/msglog/index.html")
|
||||
@SaCheckPermission("staffManage.birthday.msgLog")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询生日祝福发送日志分页数据。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("staffManage.birthday.msgLog")
|
||||
public Result pageData(UserBirthdayMsgLogPageVO page) {
|
||||
return Result.success(userBirthdayMsgLogService.pageMsgLogs(page));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.model;
|
||||
|
||||
import com.budwk.app.sys.models.Sys_file;
|
||||
import lombok.Data;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福墙礼物配置,用于移动端选择祝福礼物。
|
||||
*/
|
||||
@Data
|
||||
@Table("blessing_gift")
|
||||
@Comment("生日祝福墙礼物")
|
||||
public class BlessingGift {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("礼物名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String giftName;
|
||||
|
||||
@Column
|
||||
@Comment("礼物文件名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String fileName;
|
||||
|
||||
@Column
|
||||
@Comment("礼物图片")
|
||||
@ColDefine(type = ColType.MYSQL_JSON)
|
||||
private List<Sys_file> files;
|
||||
|
||||
@Column
|
||||
@Comment("排序字段")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 10)
|
||||
private String sortField;
|
||||
|
||||
@Column
|
||||
@Comment("创建人")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String createBy;
|
||||
|
||||
@Column
|
||||
@Comment("创建时间")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String createDate;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
/**
|
||||
* 生日祝福墙人员中间表,保存当期可被赠送祝福的生日人员。
|
||||
*/
|
||||
@Data
|
||||
@Table("greeting")
|
||||
@Comment("生日祝福墙人员")
|
||||
public class Greeting {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("生日月日,格式MMdd")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 4)
|
||||
private String birthday;
|
||||
|
||||
@Column
|
||||
@Comment("职工ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String userId;
|
||||
|
||||
@Column
|
||||
@Comment("职工姓名")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String username;
|
||||
|
||||
@Column
|
||||
@Comment("职工工号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 120)
|
||||
private String loginname;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
/**
|
||||
* 生日祝福墙赠礼记录,保存赠送人与接收人的祝福内容。
|
||||
*/
|
||||
@Data
|
||||
@Table("greeting_info")
|
||||
@Comment("生日祝福墙赠礼记录")
|
||||
public class GreetingInfo {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("赠送人ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String presenter;
|
||||
|
||||
@Column
|
||||
@Comment("接收人ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String received;
|
||||
|
||||
@Column
|
||||
@Comment("礼物ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String giftId;
|
||||
|
||||
@Column
|
||||
@Comment("赠送时间")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String giftTime;
|
||||
|
||||
@Column
|
||||
@Comment("祝福语")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
private String message;
|
||||
|
||||
@Column
|
||||
@Comment("是否发送贺卡")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
private Boolean isSend;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.model;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
/**
|
||||
* 生日祝福配置,保存移动端贺卡展示图等全局配置。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("user_birthday_config")
|
||||
@Comment("生日祝福配置")
|
||||
public class UserBirthdayConfig extends BaseModel {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("生日贺卡文件ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 64)
|
||||
private String birthdayUrl;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.model;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
/**
|
||||
* 生日祝福消息发送日志,记录每次发送到个人的消息内容和链接。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("user_birthday_msg_log")
|
||||
@Comment("生日祝福消息日志")
|
||||
public class UserBirthdayMsgLog extends BaseModel {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("推送时间")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String pushTime;
|
||||
|
||||
@Column
|
||||
@Comment("消息标题")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String msgTitle;
|
||||
|
||||
@Column
|
||||
@Comment("消息内容")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String msgContent;
|
||||
|
||||
@Column
|
||||
@Comment("接收人")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String receiveBy;
|
||||
|
||||
@Column
|
||||
@Comment("接收人姓名")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String receiveName;
|
||||
|
||||
@Column
|
||||
@Comment("推送人")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String pushBy;
|
||||
|
||||
@Column
|
||||
@Comment("推送人姓名")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String pushByName;
|
||||
|
||||
@Column
|
||||
@Comment("推送类型")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String pushType;
|
||||
|
||||
@Column
|
||||
@Comment("卡片链接")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 500)
|
||||
private String link;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.service;
|
||||
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayMsgLog;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayMsgLogPageVO;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福消息日志服务。
|
||||
*/
|
||||
public interface UserBirthdayMsgLogService extends BaseService<UserBirthdayMsgLog> {
|
||||
|
||||
/**
|
||||
* 分页查询生日祝福消息日志,并在查询层补齐页面展示所需的人员和推送字段别名。
|
||||
*
|
||||
* @param page 日志查询分页参数
|
||||
* @return 消息日志分页数据
|
||||
*/
|
||||
Pagination pageMsgLogs(UserBirthdayMsgLogPageVO page);
|
||||
|
||||
/**
|
||||
* 批量记录生日消息发送日志。
|
||||
*
|
||||
* @param users 接收人列表,至少包含 loginname 和 username
|
||||
* @param title 消息标题
|
||||
* @param content 消息内容
|
||||
* @param imageUrl 消息图片
|
||||
* @param link 跳转链接
|
||||
*/
|
||||
void insertLogs(List<NutMap> users, String title, String content, String imageUrl, String link);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.service;
|
||||
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayConfig;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.BirthdayGreetingSendVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayPageVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdaySendMsgVO;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福业务服务,统一承载 PC 管理端、H5 贺卡、移动端祝福墙所需业务。
|
||||
*/
|
||||
public interface UserBirthdayService extends BaseService<Sys_user> {
|
||||
|
||||
/**
|
||||
* 构造生日人员通用查询 SQL。
|
||||
*
|
||||
* @param pageVO 查询参数
|
||||
* @return 可直接执行的 Nutz SQL
|
||||
*/
|
||||
Sql commonSql(UserBirthdayPageVO pageVO);
|
||||
|
||||
/**
|
||||
* 查询生日人员分页数据。
|
||||
*
|
||||
* @param pageVO 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
Pagination pageBirthdayUsers(UserBirthdayPageVO pageVO);
|
||||
|
||||
/**
|
||||
* 导出生日人员列表。
|
||||
*
|
||||
* @param pageVO 查询参数
|
||||
* @param response 下载响应
|
||||
*/
|
||||
void exportXlsx(UserBirthdayPageVO pageVO, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 按当前筛选条件批量发送生日祝福。
|
||||
*
|
||||
* @param vo 发送参数和筛选条件
|
||||
*/
|
||||
void sendMsgByQueryUsers(UserBirthdaySendMsgVO vo);
|
||||
|
||||
/**
|
||||
* 给指定职工发送生日祝福。
|
||||
*
|
||||
* @param vo 发送参数
|
||||
*/
|
||||
void sendMsgByUser(UserBirthdaySendMsgVO vo);
|
||||
|
||||
/**
|
||||
* 获取当前生日贺卡配置,若不存在则返回空配置对象。
|
||||
*
|
||||
* @return 生日配置
|
||||
*/
|
||||
UserBirthdayConfig getConfig();
|
||||
|
||||
/**
|
||||
* 保存或更新生日贺卡配置。
|
||||
*
|
||||
* @param config 生日配置
|
||||
*/
|
||||
void saveOrModifyConfig(UserBirthdayConfig config);
|
||||
|
||||
/**
|
||||
* 查询移动端祝福墙最近生日人员。
|
||||
*
|
||||
* @param pageVO 分页参数
|
||||
* @param searchKeyword 姓名或工号关键字
|
||||
* @return 人员列表
|
||||
*/
|
||||
List<NutMap> listGreetingUsers(UserBirthdayPageVO pageVO, String searchKeyword);
|
||||
|
||||
/**
|
||||
* 查询已勾选的祝福对象。
|
||||
*
|
||||
* @param greetingResult 逗号分隔的用户 ID
|
||||
* @return 人员列表
|
||||
*/
|
||||
List<NutMap> loadGreetingUsers(String greetingResult);
|
||||
|
||||
/**
|
||||
* 查询祝福礼物列表。
|
||||
*
|
||||
* @return 礼物列表
|
||||
*/
|
||||
List<NutMap> giftList();
|
||||
|
||||
/**
|
||||
* 保存移动端祝福墙赠礼记录。
|
||||
*
|
||||
* @param vo 赠礼参数
|
||||
*/
|
||||
void sendBlessing(BirthdayGreetingSendVO vo);
|
||||
|
||||
/**
|
||||
* 查询当前用户收到的祝福礼物。
|
||||
*
|
||||
* @param pageVO 分页参数
|
||||
* @param username 赠送人姓名
|
||||
* @return 分页结果
|
||||
*/
|
||||
Pagination giftInfo(UserBirthdayPageVO pageVO, String username);
|
||||
|
||||
/**
|
||||
* 查询单条祝福记录,用于贺卡详情页展示。
|
||||
*
|
||||
* @param id 祝福记录 ID
|
||||
* @return 祝福记录
|
||||
*/
|
||||
NutMap findGreetingInfo(String id);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayMsgLog;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayMsgLogService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayMsgLogPageVO;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福消息日志服务实现。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class UserBirthdayMsgLogServiceImpl extends BaseServiceImpl<UserBirthdayMsgLog> implements UserBirthdayMsgLogService {
|
||||
|
||||
public UserBirthdayMsgLogServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询消息日志列表,通过 SQL 别名适配页面字段,不修改生日日志表或系统人员表结构。
|
||||
*/
|
||||
@Override
|
||||
public Pagination pageMsgLogs(UserBirthdayMsgLogPageVO page) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
l.id,
|
||||
vu.loginname AS loginname,
|
||||
IFNULL(vu.username, l.receiveName) AS username,
|
||||
vu.unionName AS unionName,
|
||||
vu.unitName AS unitName,
|
||||
l.msgTitle,
|
||||
l.msgContent,
|
||||
l.pushByName,
|
||||
l.pushTime,
|
||||
l.pushType,
|
||||
l.link
|
||||
FROM
|
||||
user_birthday_msg_log l
|
||||
LEFT JOIN vw_user vu ON vu.id = l.receiveBy
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
page.buildSearch(cnd, "l.", "vu.");
|
||||
cnd.desc("l.pushTime");
|
||||
sql.setCondition(cnd);
|
||||
return listPageMap(page.getPageNumber(), page.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量记录发送日志,便于管理端追踪生日祝福触达情况。
|
||||
*/
|
||||
@Override
|
||||
public void insertLogs(List<NutMap> users, String title, String content, String imageUrl, String link) {
|
||||
if (users == null || users.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String pushBy;
|
||||
String pushByName;
|
||||
String pushType;
|
||||
// 根据是否存在当前登录上下文区分手动推送和系统推送,避免定时任务保存日志失败。
|
||||
try {
|
||||
pushBy = SecurityUtil.getUserId();
|
||||
pushByName = SecurityUtil.getUserUsername();
|
||||
pushType = "\u624b\u52a8\u63a8\u9001";
|
||||
} catch (Exception e) {
|
||||
pushBy = "system";
|
||||
pushByName = "\u7cfb\u7edf\u63a8\u9001";
|
||||
pushType = "\u7cfb\u7edf\u63a8\u9001";
|
||||
}
|
||||
String pushTime = DateUtil.now();
|
||||
String finalPushBy = pushBy;
|
||||
String finalPushByName = pushByName;
|
||||
String finalPushType = pushType;
|
||||
List<UserBirthdayMsgLog> logs = users.stream().map(user -> {
|
||||
UserBirthdayMsgLog msgLog = new UserBirthdayMsgLog();
|
||||
msgLog.setId(R.UU32());
|
||||
msgLog.setReceiveBy(user.getString("id"));
|
||||
msgLog.setReceiveName(user.getString("username"));
|
||||
msgLog.setMsgTitle(title);
|
||||
msgLog.setMsgContent(content);
|
||||
msgLog.setPushBy(finalPushBy);
|
||||
msgLog.setPushByName(finalPushByName);
|
||||
msgLog.setPushType(finalPushType);
|
||||
msgLog.setPushTime(pushTime);
|
||||
msgLog.setLink(link);
|
||||
return msgLog;
|
||||
}).toList();
|
||||
dao().fastInsert(logs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.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.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
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.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.web.commons.base.Globals;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.GreetingInfo;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.model.UserBirthdayConfig;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayMsgLogService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.service.UserBirthdayService;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.BirthdayGreetingSendVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdayPageVO;
|
||||
import com.budwk.app.zhgh.staffmanage.birthday.vo.UserBirthdaySendMsgVO;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
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.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Times;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日祝福业务服务实现,适配当前项目的用户视图、消息服务和文件预览路径。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class UserBirthdayServiceImpl extends BaseServiceImpl<Sys_user> implements UserBirthdayService {
|
||||
|
||||
public UserBirthdayServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Inject
|
||||
private SysMsgService sysMsgService;
|
||||
|
||||
@Inject
|
||||
private UserBirthdayMsgLogService userBirthdayMsgLogService;
|
||||
|
||||
/**
|
||||
* 构造生日人员通用 SQL,按距离下一次生日的天数升序排列。
|
||||
*/
|
||||
@Override
|
||||
public Sql commonSql(UserBirthdayPageVO page) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.id,
|
||||
u.loginname,
|
||||
u.username,
|
||||
u.sex,
|
||||
DATE_FORMAT(u.birthday, '%Y-%m-%d') AS birthday,
|
||||
u.mobile,
|
||||
u.unitName,
|
||||
u.unionName,
|
||||
u.userState,
|
||||
u.personType,
|
||||
u.preparedBy,
|
||||
DATEDIFF(
|
||||
CASE
|
||||
WHEN DATE_FORMAT(u.birthday, '%m-%d') >= DATE_FORMAT(NOW(), '%m-%d')
|
||||
THEN CONCAT(YEAR(NOW()), '-', DATE_FORMAT(u.birthday, '%m-%d'))
|
||||
ELSE CONCAT(YEAR(NOW()) + 1, '-', DATE_FORMAT(u.birthday, '%m-%d'))
|
||||
END,
|
||||
CURDATE()
|
||||
) AS daysUntilBirthday
|
||||
FROM
|
||||
vw_user u
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("u.birthday", "is not", null);
|
||||
cnd.and("u.member", "=", true);
|
||||
page.buildSearch(cnd, "u.");
|
||||
cnd.asc("daysUntilBirthday");
|
||||
sql.setCondition(cnd);
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行生日人员分页查询。
|
||||
*/
|
||||
@Override
|
||||
public Pagination pageBirthdayUsers(UserBirthdayPageVO pageVO) {
|
||||
return listPageMap(pageVO.getPageNumber(), pageVO.getPageSize(), commonSql(pageVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按管理端当前筛选条件导出生日人员,强制使用 XSSF 格式。
|
||||
*/
|
||||
@Override
|
||||
public void exportXlsx(UserBirthdayPageVO pageVO, HttpServletResponse response) {
|
||||
Sql sql = commonSql(pageVO);
|
||||
List<NutMap> list = listMap(sql);
|
||||
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("工号", "loginname", 20));
|
||||
entities.add(new ExcelExportEntity("姓名", "username", 20));
|
||||
entities.add(new ExcelExportEntity("性别", "sex", 10));
|
||||
entities.add(new ExcelExportEntity("联系方式", "mobile", 20));
|
||||
entities.add(new ExcelExportEntity("出生年月", "birthday", 20));
|
||||
entities.add(new ExcelExportEntity("生日倒计时(天)", "daysUntilBirthday", 20));
|
||||
entities.add(new ExcelExportEntity("在职状态", "userState", 20));
|
||||
entities.add(new ExcelExportEntity("人员类型", "personType", 20));
|
||||
entities.add(new ExcelExportEntity("人员性质", "preparedBy", 20));
|
||||
entities.add(new ExcelExportEntity("所属工会", "unionName", 20));
|
||||
entities.add(new ExcelExportEntity("所属单位", "unitName", 20));
|
||||
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list);
|
||||
CommonDownloadUtil.download("生日祝福人员.xlsx", workbook, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按筛选条件发送生日祝福,并写入发送日志。
|
||||
*/
|
||||
@Override
|
||||
public void sendMsgByQueryUsers(UserBirthdaySendMsgVO vo) {
|
||||
validateSendParam(vo);
|
||||
List<NutMap> users = listMap(commonSql(vo));
|
||||
if (CollUtil.isEmpty(users)) {
|
||||
throw new RuntimeException("未查询到消息接收人");
|
||||
}
|
||||
sendBirthdayMessage(users, vo.getTitle(), vo.getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 给单个用户发送生日祝福,并写入发送日志。
|
||||
*/
|
||||
@Override
|
||||
public void sendMsgByUser(UserBirthdaySendMsgVO vo) {
|
||||
validateSendParam(vo);
|
||||
if (StrUtil.isBlank(vo.getUserId())) {
|
||||
throw new RuntimeException("未获取到消息接收人");
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
loginname,
|
||||
username
|
||||
FROM
|
||||
vw_user
|
||||
WHERE
|
||||
id = @id
|
||||
""").setParam("id", vo.getUserId());
|
||||
List<NutMap> users = listMap(sql);
|
||||
if (CollUtil.isEmpty(users)) {
|
||||
throw new RuntimeException("未获取到消息接收人");
|
||||
}
|
||||
sendBirthdayMessage(users, vo.getTitle(), vo.getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新生日配置,没有配置时返回空对象,便于前端直接绑定。
|
||||
*/
|
||||
@Override
|
||||
public UserBirthdayConfig getConfig() {
|
||||
UserBirthdayConfig config = dao().fetch(UserBirthdayConfig.class, Cnd.NEW().desc("updatedAt"));
|
||||
return ObjectUtil.defaultIfNull(config, new UserBirthdayConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存生日配置,存在 ID 时更新,否则新增。
|
||||
*/
|
||||
@Override
|
||||
public void saveOrModifyConfig(UserBirthdayConfig config) {
|
||||
if (StrUtil.isBlank(config.getId())) {
|
||||
dao().insert(config);
|
||||
} else {
|
||||
dao().updateIgnoreNull(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询祝福墙最近生日人员,排除当前登录人,避免自己给自己送祝福。
|
||||
*/
|
||||
@Override
|
||||
public List<NutMap> listGreetingUsers(UserBirthdayPageVO pageVO, String searchKeyword) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
g.id,
|
||||
g.birthday,
|
||||
g.userId,
|
||||
g.username,
|
||||
g.loginname,
|
||||
u.unitName
|
||||
FROM
|
||||
greeting g
|
||||
LEFT JOIN vw_user u ON g.userId = u.id
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.where().and("g.userId", "!=", SecurityUtil.getUserId());
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike("g.username", searchKeyword);
|
||||
group.orLike("g.loginname", searchKeyword);
|
||||
cnd.and(group);
|
||||
}
|
||||
cnd.asc("g.birthday");
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据移动端勾选结果查询祝福对象。
|
||||
*/
|
||||
@Override
|
||||
public List<NutMap> loadGreetingUsers(String greetingResult) {
|
||||
List<String> userIds = splitIds(greetingResult);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return List.of();
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
birthday,
|
||||
userId,
|
||||
username,
|
||||
loginname
|
||||
FROM
|
||||
greeting
|
||||
$condition
|
||||
""");
|
||||
sql.setCondition(Cnd.where("userId", "in", userIds));
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询祝福墙礼物列表。
|
||||
*/
|
||||
@Override
|
||||
public List<NutMap> giftList() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
bg.*
|
||||
FROM
|
||||
blessing_gift bg
|
||||
$condition
|
||||
""");
|
||||
sql.setCondition(Cnd.NEW().asc("bg.sortField"));
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存移动端赠礼记录,逐个接收人生成一条祝福数据。
|
||||
*/
|
||||
@Override
|
||||
public void sendBlessing(BirthdayGreetingSendVO vo) {
|
||||
List<String> userIds = splitIds(vo.getGreetingResult());
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
throw new RuntimeException("请选择祝福对象");
|
||||
}
|
||||
String currentUserId = SecurityUtil.getUserId();
|
||||
String today = DateUtil.formatDate(new java.util.Date());
|
||||
List<GreetingInfo> greetingInfos = userIds.stream().map(userId -> {
|
||||
GreetingInfo greetingInfo = new GreetingInfo();
|
||||
greetingInfo.setPresenter(currentUserId);
|
||||
greetingInfo.setReceived(userId);
|
||||
greetingInfo.setGiftId(vo.getGiftId());
|
||||
greetingInfo.setGiftTime(today);
|
||||
greetingInfo.setMessage(vo.getMessage());
|
||||
greetingInfo.setIsSend(Boolean.TRUE.equals(vo.getIsSend()));
|
||||
return greetingInfo;
|
||||
}).toList();
|
||||
dao().insert(greetingInfos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户收到的祝福礼物,支持按赠送人姓名模糊筛选。
|
||||
*/
|
||||
@Override
|
||||
public Pagination giftInfo(UserBirthdayPageVO pageVO, String username) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
gi.id,
|
||||
su.username,
|
||||
bg.files,
|
||||
gi.giftTime,
|
||||
gi.message,
|
||||
su.unitName AS name,
|
||||
gi.isSend
|
||||
FROM
|
||||
greeting_info gi
|
||||
LEFT JOIN vw_user su ON gi.presenter = su.id
|
||||
LEFT JOIN blessing_gift bg ON gi.giftId = bg.id
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("gi.received", "=", SecurityUtil.getUserId());
|
||||
if (StrUtil.isNotBlank(username)) {
|
||||
cnd.where().andLike("su.username", username);
|
||||
}
|
||||
cnd.desc("gi.giftTime");
|
||||
sql.setCondition(cnd);
|
||||
return listPageMap(pageVO.getPageNumber(), pageVO.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单条祝福记录,供贺卡详情页展示赠送人和祝福语。
|
||||
*/
|
||||
@Override
|
||||
public NutMap findGreetingInfo(String id) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
su.username,
|
||||
gi.message
|
||||
FROM
|
||||
greeting_info gi
|
||||
LEFT JOIN vw_user su ON gi.presenter = su.id
|
||||
WHERE
|
||||
gi.id = @id
|
||||
""").setParam("id", id);
|
||||
List<NutMap> list = listMap(sql);
|
||||
return CollUtil.isEmpty(list) ? NutMap.NEW() : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验消息标题和内容,避免生成空消息。
|
||||
*/
|
||||
private void validateSendParam(UserBirthdaySendMsgVO vo) {
|
||||
if (StrUtil.isBlank(vo.getTitle())) {
|
||||
throw new RuntimeException("请输入标题");
|
||||
}
|
||||
if (StrUtil.isBlank(vo.getContent())) {
|
||||
throw new RuntimeException("请输入发送内容");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一发送生日消息并记录日志,保证单人和批量发送行为一致。
|
||||
*/
|
||||
private void sendBirthdayMessage(List<NutMap> users, String title, String content) {
|
||||
UserBirthdayConfig config = getConfig();
|
||||
String link = Globals.AppDomain + "/platform/staffManage/birthday/manage/h5?id=" + StrUtil.blankToDefault(config.getBirthdayUrl(), "");
|
||||
List<String> loginNames = users.stream().map(user -> user.getString("loginname")).filter(StrUtil::isNotBlank).distinct().toList();
|
||||
if (CollUtil.isEmpty(loginNames)) {
|
||||
throw new RuntimeException("未获取到有效消息接收人");
|
||||
}
|
||||
|
||||
Sys_msg sysMsg = new Sys_msg();
|
||||
sysMsg.setTitle(title);
|
||||
sysMsg.setNote(content);
|
||||
sysMsg.setUrl(link);
|
||||
sysMsg.setType("user");
|
||||
sysMsg.setSendType("hide");
|
||||
sysMsg.setWechatEnterprise(true);
|
||||
sysMsg.setSendAt(Times.getTS());
|
||||
sysMsg.setCreatedBy(SecurityUtil.getUserId());
|
||||
sysMsgService.saveMsg(sysMsg, loginNames.toArray(String[]::new), true);
|
||||
userBirthdayMsgLogService.insertLogs(users, title, content, "", link);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拆分移动端传入的逗号分隔 ID,过滤空值。
|
||||
*/
|
||||
private List<String> splitIds(String ids) {
|
||||
if (StrUtil.isBlank(ids)) {
|
||||
return List.of();
|
||||
}
|
||||
return Arrays.stream(ids.split(",")).filter(StrUtil::isNotBlank).distinct().toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 移动端祝福墙赠礼参数。
|
||||
*/
|
||||
@Data
|
||||
public class BirthdayGreetingSendVO {
|
||||
|
||||
/**
|
||||
* 逗号分隔的接收人 ID 列表。
|
||||
*/
|
||||
private String greetingResult;
|
||||
|
||||
/**
|
||||
* 礼物 ID、祝福语和是否同步发送贺卡。
|
||||
*/
|
||||
private String giftId;
|
||||
private String message;
|
||||
private Boolean isSend;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.vo;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
|
||||
/**
|
||||
* 生日祝福消息日志查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserBirthdayMsgLogPageVO extends PageForm {
|
||||
|
||||
/**
|
||||
* 按接收人工号、姓名、标题进行模糊检索。
|
||||
*/
|
||||
private String keyword;
|
||||
private String unionId;
|
||||
private String unitId;
|
||||
|
||||
/**
|
||||
* 构造日志列表查询条件。
|
||||
*
|
||||
* @param cnd Nutz 查询条件对象
|
||||
*/
|
||||
public void buildSearch(Cnd cnd) {
|
||||
buildSearch(cnd, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造日志列表查询条件,prefix 用于适配带表别名的 SQL 查询。
|
||||
*
|
||||
* @param cnd Nutz 查询条件对象
|
||||
* @param prefix 字段前缀,例如 l.
|
||||
*/
|
||||
public void buildSearch(Cnd cnd, String prefix) {
|
||||
buildSearch(cnd, prefix, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造日志列表查询条件,分别适配日志表别名和人员视图别名。
|
||||
*
|
||||
* @param cnd Nutz 查询条件对象
|
||||
* @param logPrefix 日志表字段前缀,例如 l.
|
||||
* @param userPrefix 人员视图字段前缀,例如 vu.
|
||||
*/
|
||||
public void buildSearch(Cnd cnd, String logPrefix, String userPrefix) {
|
||||
String alias = StrUtil.blankToDefault(logPrefix, "");
|
||||
String key = StrUtil.blankToDefault(keyword, getSearchKeyword());
|
||||
if (StrUtil.isNotBlank(key)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike(StrUtil.blankToDefault(userPrefix, "") + "loginname", key);
|
||||
group.orLike(alias + "receiveName", key);
|
||||
group.orLike(alias + "msgTitle", key);
|
||||
cnd.and(group);
|
||||
}
|
||||
String userAlias = StrUtil.blankToDefault(userPrefix, "");
|
||||
cnd.andEX(userAlias + "unionId", "=", unionId);
|
||||
cnd.andEX(userAlias + "unitId", "=", unitId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.vo;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.dao.util.cri.Static;
|
||||
import org.nutz.json.Json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生日人员分页查询参数,统一承载 PC 列表、导出和批量发送的筛选条件。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserBirthdayPageVO extends PageForm {
|
||||
|
||||
/**
|
||||
* 按工号、姓名、手机号进行模糊检索。
|
||||
*/
|
||||
private String keyword;
|
||||
|
||||
/**
|
||||
* 指定工会、单位、人员类型等筛选条件。
|
||||
*/
|
||||
private String unionId;
|
||||
private String unitId;
|
||||
private String personType;
|
||||
private String userState;
|
||||
private String sex;
|
||||
private String preparedBy;
|
||||
private String userStates;
|
||||
private String personTypes;
|
||||
private String preparedBys;
|
||||
|
||||
/**
|
||||
* 生日月份,支持一次查询多个月份。
|
||||
*/
|
||||
private List<Integer> birthMonths;
|
||||
|
||||
/**
|
||||
* 构造生日人员查询条件,prefix 用于适配不同 SQL 表别名。
|
||||
*
|
||||
* @param cnd Nutz 查询条件对象
|
||||
* @param prefix 字段前缀,例如 u.
|
||||
*/
|
||||
public void buildSearch(Cnd cnd, String prefix) {
|
||||
String alias = StrUtil.blankToDefault(prefix, "");
|
||||
String key = StrUtil.blankToDefault(keyword, getSearchKeyword());
|
||||
if (StrUtil.isNotBlank(key)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike(alias + "loginname", key);
|
||||
group.orLike(alias + "username", key);
|
||||
group.orLike(alias + "mobile", key);
|
||||
cnd.and(group);
|
||||
}
|
||||
cnd.andEX(alias + "unionId", "=", unionId);
|
||||
cnd.andEX(alias + "unitId", "=", unitId);
|
||||
cnd.andEX(alias + "personType", "=", personType);
|
||||
cnd.andEX(alias + "personType", "in", parseMultiValues(personTypes));
|
||||
cnd.andEX(alias + "userState", "=", userState);
|
||||
cnd.andEX(alias + "userState", "in", parseMultiValues(userStates));
|
||||
cnd.andEX(alias + "sex", "=", sex);
|
||||
cnd.andEX(alias + "preparedBy", "=", preparedBy);
|
||||
cnd.andEX(alias + "preparedBy", "in", parseMultiValues(preparedBys));
|
||||
if (CollUtil.isNotEmpty(birthMonths)) {
|
||||
cnd.and(new Static("MONTH(" + alias + "birthday) IN (" + CollUtil.join(birthMonths, ",") + ")"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页面多选组件传入的 JSON 数组字符串转换为查询条件列表,兼容生日模块迁移后的前端字段命名。
|
||||
*/
|
||||
private List<String> parseMultiValues(String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
List<String> values = Json.fromJsonAsList(String.class, value);
|
||||
return CollUtil.isEmpty(values) ? null : values;
|
||||
} catch (RuntimeException e) {
|
||||
List<String> values = new ArrayList<>();
|
||||
values.add(value);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.budwk.app.zhgh.staffmanage.birthday.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 生日祝福发送参数,支持单人发送和按当前查询条件批量发送。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserBirthdaySendMsgVO extends UserBirthdayPageVO {
|
||||
|
||||
/**
|
||||
* 单人发送时接收人的用户 ID。
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 消息标题和内容。
|
||||
*/
|
||||
private String title;
|
||||
private String content;
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.6 MiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,430 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="204px" height="204px" viewBox="0 0 204 204" enable-background="new 0 0 204 204" xml:space="preserve"> <image id="image0" width="204" height="204" x="0" y="0"
|
||||
href="data:image/png;base64,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" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 557 KiB |
|
After Width: | Height: | Size: 557 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 994 KiB |
|
After Width: | Height: | Size: 43 KiB |
@@ -6,6 +6,7 @@
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:on-preview="handlePreview"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="beforeUpload"
|
||||
@@ -26,6 +27,7 @@
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:before-upload="beforeUpload"
|
||||
@@ -67,6 +69,7 @@
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
@@ -205,15 +208,15 @@ module.exports = {
|
||||
|
||||
let tips = null
|
||||
if (uploadNumber) {
|
||||
tips = `只能上传<span style="color: red">${uploadNumber}</span>个文件;`
|
||||
tips = '只能上传<span style="color: red">' + uploadNumber + '</span>个文件;'
|
||||
}
|
||||
if (fileAccept) {
|
||||
tips = tips + `只能上传<span style="color: red">${fileAccept}</span>文件;`
|
||||
tips = tips + '只能上传<span style="color: red">' + fileAccept + '</span>文件;'
|
||||
} else {
|
||||
tips = tips + '不限格式;'
|
||||
}
|
||||
if (fileSize) {
|
||||
tips = tips + `单个文件大小不能超过<span style="color: red">${(fileSize / 1024 / 1024).toFixed(2)}</span>M;`
|
||||
tips = tips + '单个文件大小不能超过<span style="color: red">' + (fileSize / 1024 / 1024).toFixed(2) + '</span>M;'
|
||||
}
|
||||
return tips
|
||||
},
|
||||
@@ -341,7 +344,7 @@ module.exports = {
|
||||
if (valid) {
|
||||
return true
|
||||
}
|
||||
this.$message.error(`文件只能是 ${this.fileAccept} 格式!`)
|
||||
this.$message.error("文件只能是 " + this.fileAccept + " 格式!")
|
||||
return false
|
||||
},
|
||||
|
||||
@@ -441,15 +444,15 @@ module.exports = {
|
||||
let result = []
|
||||
if (response && response.code === 0 && file.status === "success") {
|
||||
fileList.forEach((f) => {
|
||||
if(f.response.data?.includes('webvpn')) {
|
||||
if(f.response && f.response.data && f.response.data.includes('webvpn')) {
|
||||
const index = f.response.data.indexOf('/platform')
|
||||
f.response.data = f.response.data.substring(index)
|
||||
this.$set(f.response, "data", f.response.data.substring(index))
|
||||
}
|
||||
result.push(f)
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
fileList = fileList.splice(file, 1)
|
||||
this.$message.error(response && response.msg ? response.msg : "文件上传失败,请稍后重试")
|
||||
fileList = this.filterFileList(fileList, file)
|
||||
}
|
||||
if (result.length > 0) {
|
||||
if (this.upload_result_category === "interval") {
|
||||
@@ -484,6 +487,40 @@ module.exports = {
|
||||
this.$emit("update:value", fileList)
|
||||
},
|
||||
|
||||
handleError(error, file, fileList) {
|
||||
this.$message.error(this.getUploadErrorMessage(error))
|
||||
this.$emit("update:value", this.filterFileList(fileList, file))
|
||||
},
|
||||
|
||||
filterFileList(fileList, file) {
|
||||
if (!Array.isArray(fileList)) {
|
||||
return []
|
||||
}
|
||||
return fileList.filter((item) => {
|
||||
return !file || item.uid !== file.uid
|
||||
})
|
||||
},
|
||||
|
||||
getUploadErrorMessage(error) {
|
||||
if (error && error.response && error.response.data && error.response.data.msg) {
|
||||
return error.response.data.msg
|
||||
}
|
||||
if (error && error.status) {
|
||||
return "文件上传失败,服务器返回" + error.status
|
||||
}
|
||||
return "文件上传失败,请检查文件后重试"
|
||||
},
|
||||
|
||||
buildQrCodeAddress(timestamp) {
|
||||
const currentLength = Array.isArray(this.fileList) ? this.fileList.length : 0
|
||||
const remainNumber = this.upload_number ? Math.max(this.upload_number - currentLength, 0) : 0
|
||||
let address = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + encodeURIComponent(timestamp)
|
||||
address = address + "&upload_number=" + encodeURIComponent(remainNumber)
|
||||
address = address + "&upload_size=" + encodeURIComponent(this.upload_size || "")
|
||||
address = address + "&accept=" + encodeURIComponent(this.fileAccept || "")
|
||||
return address
|
||||
},
|
||||
|
||||
// 通过DOM获取上传的文件
|
||||
uploadFileList() {
|
||||
if (this.fileList) {
|
||||
@@ -546,7 +583,7 @@ module.exports = {
|
||||
|
||||
mounted() {
|
||||
const timestamp = new Date().getTime()
|
||||
this.qrCodeAddress = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + timestamp
|
||||
this.$set(this, "qrCodeAddress", this.buildQrCodeAddress(timestamp))
|
||||
console.info("qrCodeAddress:", this.qrCodeAddress)
|
||||
// 可选:轮询或 watch ready 状态
|
||||
const checkReady = () => {
|
||||
@@ -588,7 +625,7 @@ module.exports = {
|
||||
|
||||
if (validFiles.length < data.files.length) {
|
||||
data.files = validFiles
|
||||
this.$message.error(`文件只能是 ${this.fileAccept} 格式,已自动过滤掉不符合要求文件!`)
|
||||
this.$message.error("文件只能是 " + this.fileAccept + " 格式,已自动过滤掉不符合要求文件!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html lang="${lang,escape}">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>数智工会领导驾驶舱</title>
|
||||
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/css/root.css?v=20260524"/>
|
||||
<script src="${base!}/assets/platform/plugins/vue/vue.js?v=20260524"></script>
|
||||
<script src="${base!}/assets/platform/plugins/axios/axios.js?v=20260524"></script>
|
||||
<script src="${base!}/assets/platform/plugins/jquery/jquery.js?v=20260524"></script>
|
||||
<script src="${base!}/assets/platform/js/util/commonUtil.js?v=20260524"></script>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
Vue.config.devtools = false
|
||||
|
||||
const store = {
|
||||
state: {
|
||||
user: JSON.parse(window.sessionStorage.getItem("user") || "null") || {permissions: [], roles: []}
|
||||
}
|
||||
}
|
||||
|
||||
window.ELEMENT = window.ELEMENT || {
|
||||
Message: {
|
||||
error(message) {
|
||||
console.error(message || "操作失败")
|
||||
}
|
||||
},
|
||||
Loading: {
|
||||
service() {
|
||||
return {
|
||||
close() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vue.prototype.$commonUtil = commonUtil
|
||||
Vue.prototype.$auth = commonUtil.authService()
|
||||
Vue.prototype.$axios = commonUtil.axiosService()
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
${layoutContent!}
|
||||
</body>
|
||||
</html>
|
||||
@@ -477,10 +477,51 @@
|
||||
.v4-user-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
gap: 18px;
|
||||
height: 100%;
|
||||
max-width: 355px;
|
||||
max-width: 520px;
|
||||
flex-shrink: 0;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.v4-retire-system-link {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 90px;
|
||||
height: 30px;
|
||||
padding: 0 14px;
|
||||
border-radius: 18px;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(90deg, #f5a300 0%, #ff8e00 42%, #ff4b16 100%);
|
||||
border: 1px solid rgba(255, 247, 184, 0.9);
|
||||
box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.48), 0 2px 6px rgba(141, 53, 0, 0.25);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.v4-retire-system-link::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.v4-retire-system-link:hover {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.55), 0 3px 9px rgba(141, 53, 0, 0.32);
|
||||
}
|
||||
|
||||
.v4-notification {
|
||||
@@ -613,7 +654,12 @@
|
||||
}
|
||||
|
||||
.v4-user-section {
|
||||
gap: 16px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.v4-retire-system-link {
|
||||
min-width: 78px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,6 +738,9 @@
|
||||
</div>
|
||||
|
||||
<div class="v4-user-section">
|
||||
<a class="v4-retire-system-link" href="http://192.168.73.133:8081/platform/login">
|
||||
离退休系统
|
||||
</a>
|
||||
<div class="v4-user-info">
|
||||
<span class="v4-user-name">${@auth.getPrincipalProperty('username')} (${@auth.getPrincipalProperty('loginname')})</span>
|
||||
<!-- <i class="fa fa-angle-down"></i> -->
|
||||
|
||||
@@ -44,6 +44,13 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
margin-top: 12px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.van-uploader__tip {
|
||||
color: var(--color-primary);
|
||||
font-size: 14px;
|
||||
@@ -78,8 +85,9 @@
|
||||
></van-uploader>
|
||||
|
||||
<div class="upload-button">
|
||||
<van-button block type="primary" @click="onSubmit">同步到电脑</van-button>
|
||||
<van-button block type="primary" :disabled="submitting" @click="onSubmit">同步到电脑</van-button>
|
||||
</div>
|
||||
<div class="upload-tip">{{ uploadTips }}</div>
|
||||
</div>
|
||||
</body>
|
||||
<script nonce="${cspNonce!}">
|
||||
@@ -93,22 +101,97 @@
|
||||
// accept: "image/*",
|
||||
accept: "*",
|
||||
upload_number: 9,
|
||||
upload_size: 1024 * 1024 * 10,
|
||||
submitting: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uploadTips() {
|
||||
const sizeText = (this.upload_size / 1024 / 1024).toFixed(2)
|
||||
const acceptText = this.accept && this.accept !== "*" ? this.accept : "不限格式"
|
||||
return "最多选择" + this.upload_number + "个文件,支持" + acceptText + ",单个文件不超过" + sizeText + "M"
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initUploadConfig()
|
||||
},
|
||||
methods: {
|
||||
GetQueryString(name) {
|
||||
return new URLSearchParams(window.location.search).get(name)
|
||||
},
|
||||
initUploadConfig() {
|
||||
const accept = this.GetQueryString("accept")
|
||||
const uploadNumber = parseInt(this.GetQueryString("upload_number"), 10)
|
||||
const uploadSize = parseInt(this.GetQueryString("upload_size"), 10)
|
||||
if (accept) {
|
||||
this.$set(this, "accept", accept)
|
||||
}
|
||||
if (!isNaN(uploadNumber)) {
|
||||
this.$set(this, "upload_number", uploadNumber)
|
||||
}
|
||||
if (!isNaN(uploadSize) && uploadSize > 0) {
|
||||
this.$set(this, "upload_size", uploadSize)
|
||||
}
|
||||
},
|
||||
beforeRead(file) {
|
||||
const files = Array.isArray(file) ? file : [file]
|
||||
if (this.fileList.length + files.length > this.upload_number) {
|
||||
this.$toast.fail("最多只能上传" + this.upload_number + "个文件")
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const validMsg = this.validateFile(files[i])
|
||||
if (validMsg) {
|
||||
this.$toast.fail(validMsg)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
afterRead(file) {},
|
||||
|
||||
buildUploadPromises(file) {
|
||||
return
|
||||
validateFile(file) {
|
||||
if (!file) {
|
||||
return "请选择要上传的文件"
|
||||
}
|
||||
if (file.size > this.upload_size) {
|
||||
return "文件过大,请上传小于" + (this.upload_size / 1024 / 1024).toFixed(2) + "M的文件"
|
||||
}
|
||||
if (!this.accept || this.accept === "*") {
|
||||
return ""
|
||||
}
|
||||
const fileName = file.name || ""
|
||||
const dotIndex = fileName.lastIndexOf(".")
|
||||
if (dotIndex === -1) {
|
||||
return "文件必须包含有效的扩展名"
|
||||
}
|
||||
const fileType = fileName.substring(dotIndex).toUpperCase()
|
||||
const acceptTypes = this.accept
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((type) => type.trim().toUpperCase())
|
||||
if (acceptTypes.indexOf(fileType) === -1) {
|
||||
return "文件只能是 " + this.accept + " 格式"
|
||||
}
|
||||
return ""
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
if (this.fileList.length === 0) {
|
||||
this.$toast("请先选择文件")
|
||||
return
|
||||
}
|
||||
|
||||
const pendingFiles = this.fileList.filter((f) => {
|
||||
return f.file
|
||||
})
|
||||
|
||||
if (pendingFiles.length === 0) {
|
||||
this.syncPc()
|
||||
return
|
||||
}
|
||||
|
||||
this.$set(this, "submitting", true)
|
||||
const loading = this.$toast({
|
||||
duration: 0,
|
||||
overlay: true,
|
||||
@@ -118,37 +201,39 @@
|
||||
})
|
||||
|
||||
Promise.all(
|
||||
this.fileList.map((f) => {
|
||||
pendingFiles.map((f) => {
|
||||
const formData = new FormData()
|
||||
formData.append("file", f.file)
|
||||
return axios.post("/platform/sys/h5ScanCodeUploadFile/upload", formData).then((axiosResp) => {
|
||||
const resp = axiosResp.data
|
||||
if (resp.code === 0) {
|
||||
console.log(resp)
|
||||
f.name = f.file.name
|
||||
f.size = f.file.size
|
||||
f.status = null
|
||||
f.url = resp.data
|
||||
f.response = resp
|
||||
f.percentage = 100
|
||||
f.isImage = true
|
||||
this.$set(f, "name", f.file.name)
|
||||
this.$set(f, "size", f.file.size)
|
||||
this.$set(f, "status", null)
|
||||
this.$set(f, "url", resp.data)
|
||||
this.$set(f, "response", resp)
|
||||
this.$set(f, "percentage", 100)
|
||||
this.$set(f, "isImage", this.isImageFile(f.file.name))
|
||||
delete f.file
|
||||
delete f.content
|
||||
console.log(f)
|
||||
} else {
|
||||
f.status = "fail"
|
||||
this.$set(f, "status", "fail")
|
||||
return Promise.reject(new Error(resp.msg || "文件上传失败"))
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.$set(f, "status", "fail")
|
||||
return Promise.reject(error)
|
||||
})
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
loading.close()
|
||||
console.log(this.fileList)
|
||||
this.syncPc()
|
||||
})
|
||||
.catch((error) => {
|
||||
loading.close()
|
||||
this.$toast.fail(error)
|
||||
this.$set(this, "submitting", false)
|
||||
this.$toast.fail(this.getErrorMessage(error, "文件上传失败"))
|
||||
})
|
||||
},
|
||||
|
||||
@@ -157,15 +242,16 @@
|
||||
this.$toast('请先选择文件')
|
||||
return
|
||||
}
|
||||
console.log(this.fileList)
|
||||
|
||||
this.$set(this, "submitting", true)
|
||||
const params = new URLSearchParams()
|
||||
params.append("files", JSON.stringify(this.fileList))
|
||||
params.append("timestamp", this.GetQueryString("timestamp") || "")
|
||||
|
||||
axios
|
||||
.post(
|
||||
"/platform/sys/h5ScanCodeUploadFile/syncPc",
|
||||
{
|
||||
files: JSON.stringify(this.fileList),
|
||||
timestamp: this.GetQueryString("timestamp")
|
||||
},
|
||||
params.toString(),
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
|
||||
@@ -176,9 +262,33 @@
|
||||
if (resp.data.code === 0) {
|
||||
this.$toast.success("上传成功,请关注电脑端信息")
|
||||
} else {
|
||||
this.$toast.fail(resp.data.msg)
|
||||
this.$toast.fail(resp.data.msg || "同步到电脑失败")
|
||||
}
|
||||
this.$set(this, "submitting", false)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.fail(this.getErrorMessage(error, "同步到电脑失败"))
|
||||
this.$set(this, "submitting", false)
|
||||
})
|
||||
},
|
||||
|
||||
isImageFile(fileName) {
|
||||
const name = fileName || ""
|
||||
const suffix = name.substring(name.lastIndexOf(".") + 1).toLowerCase()
|
||||
return ["jpg", "jpeg", "png", "gif", "svg"].indexOf(suffix) !== -1
|
||||
},
|
||||
|
||||
getErrorMessage(error, defaultMsg) {
|
||||
if (error && error.response && error.response.data && error.response.data.msg) {
|
||||
return error.response.data.msg
|
||||
}
|
||||
if (error && error.message && error.message !== "Network Error") {
|
||||
return defaultMsg + ":" + error.message
|
||||
}
|
||||
if (error && error.response && error.response.status) {
|
||||
return defaultMsg + ",服务器返回" + error.response.status
|
||||
}
|
||||
return defaultMsg + ",请稍后重试"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,69 @@
|
||||
const form = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" :close-on-click-modal="false">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px">
|
||||
<el-form-item label="单位" prop="unitName">
|
||||
<el-input v-model="formData.unitName" placeholder="请输入单位" maxlength="100"
|
||||
show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地点" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入地点" maxlength="100"
|
||||
show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="面积" prop="area">
|
||||
<el-input v-model="formData.area" placeholder="请输入面积" maxlength="10"
|
||||
show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="设施" prop="facility">
|
||||
<el-input v-model="formData.facility" type="textarea" :rows="5" placeholder="请输入设施"
|
||||
maxlength="1000" show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开放时间" prop="openTime">
|
||||
<el-input v-model="formData.openTime" placeholder="请输入开放时间"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
`,
|
||||
template: [
|
||||
"<el-dialog :title=\"formData.id ? '编辑' : '新增'\" :visible.sync=\"dialogVisible\" :close-on-click-modal=\"false\" width=\"60%\">",
|
||||
" <el-form ref=\"formRef\" :model=\"formData\" :rules=\"rules\" label-width=\"110px\">",
|
||||
" <el-row :gutter=\"20\">",
|
||||
" <el-col :span=\"12\">",
|
||||
" <el-form-item label=\"当前工会\" prop=\"unionName\">",
|
||||
" <el-input v-model=\"formData.unionName\" disabled placeholder=\"当前登录人所在工会\"></el-input>",
|
||||
" </el-form-item>",
|
||||
" </el-col>",
|
||||
" <el-col :span=\"12\">",
|
||||
" <el-form-item label=\"当前单位\" prop=\"unitName\">",
|
||||
" <el-input v-model=\"formData.unitName\" disabled placeholder=\"当前登录人所在单位\"></el-input>",
|
||||
" </el-form-item>",
|
||||
" </el-col>",
|
||||
" </el-row>",
|
||||
" <el-form-item label=\"地点\" prop=\"address\">",
|
||||
" <el-input v-model=\"formData.address\" placeholder=\"请输入地点\" maxlength=\"100\" show-word-limit></el-input>",
|
||||
" </el-form-item>",
|
||||
" <el-form-item label=\"面积\" prop=\"area\">",
|
||||
" <el-input v-model=\"formData.area\" placeholder=\"请输入面积\" maxlength=\"10\" show-word-limit></el-input>",
|
||||
" </el-form-item>",
|
||||
" <el-form-item label=\"设施\" prop=\"facility\">",
|
||||
" <el-input v-model=\"formData.facility\" type=\"textarea\" :rows=\"5\" placeholder=\"请输入设施\" maxlength=\"1000\" show-word-limit></el-input>",
|
||||
" </el-form-item>",
|
||||
" <el-form-item label=\"开放时间\" prop=\"openTime\">",
|
||||
" <el-input v-model=\"formData.openTime\" placeholder=\"请输入开放时间\"></el-input>",
|
||||
" </el-form-item>",
|
||||
" <el-form-item label=\"图片/视频资料\" prop=\"mediaFiles\">",
|
||||
" <file-upload",
|
||||
" :value.sync=\"formData.mediaFiles\"",
|
||||
" :upload_number=\"10\"",
|
||||
" upload_mode=\"drag\"",
|
||||
" upload_result_category=\"array\"",
|
||||
" complete_result",
|
||||
" :accept=\"mediaAccept\">",
|
||||
" </file-upload>",
|
||||
" </el-form-item>",
|
||||
" <el-row :gutter=\"20\">",
|
||||
" <el-col :span=\"12\">",
|
||||
" <el-form-item label=\"地图X坐标\" prop=\"mapX\">",
|
||||
" <el-input v-model=\"formData.mapX\" disabled placeholder=\"请通过坐标按钮采集\"></el-input>",
|
||||
" </el-form-item>",
|
||||
" </el-col>",
|
||||
" <el-col :span=\"12\">",
|
||||
" <el-form-item label=\"地图Y坐标\" prop=\"mapY\">",
|
||||
" <el-input v-model=\"formData.mapY\" disabled placeholder=\"请通过坐标按钮采集\"></el-input>",
|
||||
" </el-form-item>",
|
||||
" </el-col>",
|
||||
" </el-row>",
|
||||
" </el-form>",
|
||||
" <div slot=\"footer\" class=\"dialog-footer\">",
|
||||
" <el-button @click=\"closeDialog\">取消</el-button>",
|
||||
" <el-button type=\"primary\" @click=\"onSubmit\">确定</el-button>",
|
||||
" </div>",
|
||||
"</el-dialog>"
|
||||
].join(""),
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
formData: {},
|
||||
currentUserOrg: {},
|
||||
mediaAccept: ".jpg,.jpeg,.png,.mp4,.mov",
|
||||
rules: {
|
||||
unitName: [{ required: true, message: "请输入单位", trigger: "blur" }],
|
||||
unitName: [{ required: true, message: "未获取到当前单位", trigger: "blur" }],
|
||||
unionName: [{ required: true, message: "未获取到当前工会", trigger: "blur" }],
|
||||
address: [{ required: true, message: "请输入地点", trigger: "blur" }],
|
||||
area: [{ required: true, message: "请输入面积", trigger: "blur" }],
|
||||
facility: [{ required: true, message: "请输入设施", trigger: "blur" }],
|
||||
@@ -42,23 +72,30 @@ const form = {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(id) {
|
||||
async onOpen(id) {
|
||||
this.$set(this, "dialogVisible", true)
|
||||
this.resetForm()
|
||||
await this.loadCurrentUserOrg()
|
||||
if (id) {
|
||||
this.dialogVisible = true
|
||||
this.resetForm()
|
||||
$.post("/platform/buildHome/littleHouse/detail/" + id).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.formData = resp.data
|
||||
this.$set(this, "formData", Object.assign({}, this.currentUserOrg, resp.data, {
|
||||
mediaFiles: this.parseFiles(resp.data.mediaFiles)
|
||||
}))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.dialogVisible = true
|
||||
this.formData = {}
|
||||
this.$set(this, "formData", Object.assign({}, this.currentUserOrg, {
|
||||
mediaFiles: []
|
||||
}))
|
||||
this.$nextTick(() => {
|
||||
this.$refs.formRef.clearValidate()
|
||||
})
|
||||
}
|
||||
},
|
||||
closeDialog() {
|
||||
this.$set(this, "dialogVisible", false)
|
||||
},
|
||||
resetForm() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.formRef) {
|
||||
@@ -67,14 +104,49 @@ const form = {
|
||||
}
|
||||
})
|
||||
},
|
||||
async loadCurrentUserOrg() {
|
||||
const resp = await $.post("/platform/buildHome/littleHouse/currentUserOrg")
|
||||
if (resp.code === 0) {
|
||||
this.$set(this, "currentUserOrg", resp.data || {})
|
||||
}
|
||||
},
|
||||
parseFiles(value) {
|
||||
if (!value) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
},
|
||||
getFileExt(file) {
|
||||
const name = file.name || file.url || (file.response && file.response.data) || file.data || ""
|
||||
const index = name.lastIndexOf(".")
|
||||
return index > -1 ? name.substring(index + 1).toLowerCase() : ""
|
||||
},
|
||||
validateMediaFiles(files) {
|
||||
const acceptExts = this.mediaAccept.split(",").map(v => v.replace(".", "").toLowerCase())
|
||||
return (files || []).every(file => acceptExts.includes(this.getFileExt(file)))
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
$.post("/platform/buildHome/littleHouse/save", this.formData).then((resp) => {
|
||||
if (!this.validateMediaFiles(this.formData.mediaFiles)) {
|
||||
this.$message.warning("仅支持上传 jpg、jpeg、png、mp4、mov 格式文件")
|
||||
return
|
||||
}
|
||||
const data = Object.assign({}, this.formData, {
|
||||
mediaFiles: JSON.stringify(this.formData.mediaFiles || [])
|
||||
})
|
||||
$.post("/platform/buildHome/littleHouse/save", data).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.$emit("refresh")
|
||||
this.dialogVisible = false
|
||||
this.closeDialog()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ layout("/layouts/platform.html"){
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="单位名称">
|
||||
<el-input placeholder="单位名称" clearable v-model="pageForm.searchKeyword"></el-input>
|
||||
<search-item label="单位/工会名称">
|
||||
<el-input placeholder="单位/工会名称" clearable v-model="pageForm.searchKeyword"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
@@ -18,6 +18,7 @@ layout("/layouts/platform.html"){
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" ref="table" row-key="id" style="width: 100%">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="80px"></el-table-column>
|
||||
<el-table-column label="工会" prop="unionName" sortable width="220px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="单位" prop="unitName" sortable width="200px"></el-table-column>
|
||||
<el-table-column label="活动场所基本情况">
|
||||
<el-table-column label="地点" prop="address" sortable width="200px"></el-table-column>
|
||||
@@ -25,8 +26,9 @@ layout("/layouts/platform.html"){
|
||||
<el-table-column label="设施" prop="facility" sortable></el-table-column>
|
||||
<el-table-column label="开放时间" prop="openTime" sortable width="200px"></el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="200px">
|
||||
<el-table-column label="操作" fixed="right" width="280px">
|
||||
<template scope="{row}">
|
||||
<el-button v-if="$auth.hasRole('SYSADMIN')" size="mini" type="warning" @click="openCoordinate(row)">坐标</el-button>
|
||||
<el-button size="mini" type="primary" @click="$refs.littleHouseFormRef.onOpen(row.id)">修改</el-button>
|
||||
<el-button size="mini" type="danger" @click="onDelete(row.id)">删除</el-button>
|
||||
</template>
|
||||
@@ -63,12 +65,78 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
})
|
||||
},
|
||||
openCoordinate(row) {
|
||||
if (!row || !row.id) {
|
||||
this.$message.warning("未获取到小家记录")
|
||||
return
|
||||
}
|
||||
if (this.hasCoordinate(row)) {
|
||||
this.$confirm(
|
||||
"当前记录已有坐标,是修改还是清空?",
|
||||
"提示",
|
||||
{
|
||||
type: "warning",
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: "修改坐标",
|
||||
cancelButtonText: "清空坐标"
|
||||
}
|
||||
).then(() => {
|
||||
this.doOpenCoordinate(row)
|
||||
}).catch((action) => {
|
||||
if (action === "cancel") {
|
||||
this.clearCoordinate(row)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
this.doOpenCoordinate(row)
|
||||
},
|
||||
hasCoordinate(row) {
|
||||
return row
|
||||
&& row.mapX !== null
|
||||
&& row.mapX !== undefined
|
||||
&& row.mapX !== ""
|
||||
&& row.mapY !== null
|
||||
&& row.mapY !== undefined
|
||||
&& row.mapY !== ""
|
||||
},
|
||||
doOpenCoordinate(row) {
|
||||
const params = [
|
||||
"coordinateMode=1",
|
||||
"houseId=" + encodeURIComponent(row.id),
|
||||
"t=" + Date.now()
|
||||
]
|
||||
window.open("/platform/careData/leader?" + params.join("&"), "_blank")
|
||||
},
|
||||
clearCoordinate(row) {
|
||||
$.post("/platform/buildHome/littleHouse/clearCoordinate", { id: row.id }).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success("坐标已清空")
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCoordinateMessage(event) {
|
||||
if (event.origin !== window.location.origin) {
|
||||
return
|
||||
}
|
||||
const data = event.data || {}
|
||||
if (data.type !== "littleHouseCoordinateSaved") {
|
||||
return
|
||||
}
|
||||
this.$message.success("坐标采集成功")
|
||||
this.doSearch()
|
||||
},
|
||||
onExport() {
|
||||
this.$downLoad("/platform/buildHome/littleHouse/exportXlsx")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.addEventListener("message", this.handleCoordinateMessage)
|
||||
this.pageData()
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("message", this.handleCoordinateMessage)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<script src="${base!}/assets/platform/plugins/echarts/echarts.min.js?v=20260527"></script>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.asset-data-panel {
|
||||
position: absolute;
|
||||
left: 1.35%;
|
||||
top: 69.6%;
|
||||
width: 20.82%;
|
||||
height: 28.06%;
|
||||
background: url("${base!}/assets/platform/images/careData/leader/asset-data-panel.png?v=20260527") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.asset-data-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.asset-data-chart {
|
||||
position: absolute;
|
||||
left: 3%;
|
||||
top: 14%;
|
||||
width: 94%;
|
||||
height: 74%;
|
||||
}
|
||||
|
||||
.asset-data-note {
|
||||
position: absolute;
|
||||
left: 8%;
|
||||
right: 8%;
|
||||
bottom: 4.5%;
|
||||
overflow: hidden;
|
||||
color: #315d86;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.asset-data-empty {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 48%;
|
||||
z-index: 2;
|
||||
color: rgba(36, 71, 105, 0.82);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="asset-data-panel">
|
||||
<div class="asset-data-title">资产数据</div>
|
||||
<div ref="usageChart" class="asset-data-chart"></div>
|
||||
<div class="asset-data-empty" v-if="!chartRows.length">暂无数据</div>
|
||||
<div class="asset-data-note">不同颜色代表不同使用状况</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
chartRows: [],
|
||||
fullscreen: false,
|
||||
chartColors: ["#1c8df4", "#35c982", "#f3a64f", "#8a7cff", "#f05d80", "#2ec7c9", "#b6a2de"]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadAssetData()
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
window.addEventListener("resize", this.resizeChart)
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("resize", this.resizeChart)
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.$set(this, "chart", null)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
if (!window.echarts || !this.$refs.usageChart) {
|
||||
return
|
||||
}
|
||||
this.$set(this, "chart", echarts.init(this.$refs.usageChart))
|
||||
this.renderChart()
|
||||
},
|
||||
loadAssetData() {
|
||||
this.$axios.post("/platform/careData/leader/assetDataData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "chartRows", (data.states || [])
|
||||
.map(item => ({
|
||||
name: item.name || "未填写",
|
||||
value: Number(item.value || 0)
|
||||
}))
|
||||
.filter(item => item.value > 0))
|
||||
this.renderChart()
|
||||
})
|
||||
},
|
||||
renderChart() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
const total = this.chartRows.reduce((sum, item) => sum + item.value, 0)
|
||||
this.chart.setOption({
|
||||
color: this.chartColors,
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{b}<br/>{c} ({d}%)"
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
right: "3%",
|
||||
top: "24%",
|
||||
itemWidth: 8,
|
||||
itemHeight: 8,
|
||||
itemGap: 9,
|
||||
textStyle: {
|
||||
color: "#315d86",
|
||||
fontSize: 12,
|
||||
lineHeight: 14
|
||||
},
|
||||
formatter(name) {
|
||||
return name.length > 5 ? name.slice(0, 5) + "..." : name
|
||||
}
|
||||
},
|
||||
graphic: {
|
||||
type: "text",
|
||||
left: "28%",
|
||||
top: "47%",
|
||||
style: {
|
||||
text: total ? total + "\n件" : "",
|
||||
fill: "#1683f4",
|
||||
fontSize: 15,
|
||||
fontWeight: 700,
|
||||
textAlign: "center"
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: "使用状况",
|
||||
type: "pie",
|
||||
radius: ["32%", "56%"],
|
||||
center: ["32%", "52%"],
|
||||
avoidLabelOverlap: true,
|
||||
minAngle: 8,
|
||||
itemStyle: {
|
||||
borderColor: "rgba(255,255,255,0.9)",
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
color: "#244769",
|
||||
fontSize: 11,
|
||||
formatter: "{d}%"
|
||||
},
|
||||
labelLine: {
|
||||
length: 8,
|
||||
length2: 4,
|
||||
lineStyle: {
|
||||
color: "rgba(36, 71, 105, 0.55)"
|
||||
}
|
||||
},
|
||||
data: this.chartRows
|
||||
}]
|
||||
}, true)
|
||||
},
|
||||
resizeChart() {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,257 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.base-union-panel {
|
||||
position: absolute;
|
||||
right: calc(1.55% - 2px);
|
||||
top: calc(60.05% + 95px);
|
||||
width: calc(28.2% - 140px);
|
||||
height: 28.1%;
|
||||
background: url("${base!}/assets/platform/images/careData/leader/base-union-panel.png?v=20260526") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.base-union-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.base-union-list {
|
||||
position: absolute;
|
||||
left: calc(8.5% - 15px);
|
||||
top: 20.2%;
|
||||
width: calc(83% + 35px);
|
||||
height: 68.5%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.base-union-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.base-union-list::-webkit-scrollbar-track {
|
||||
background: rgba(110, 178, 237, 0.22);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.base-union-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(49, 142, 231, 0.76);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.base-union-row {
|
||||
position: relative;
|
||||
flex: 0 0 17.2%;
|
||||
height: 17.2%;
|
||||
min-height: 31px;
|
||||
background-position: center center;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
color: #244769;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.base-union-row:nth-child(2n + 1) {
|
||||
background-image: url("${base!}/assets/platform/images/careData/leader/base-union-row-a.png?v=20260526");
|
||||
}
|
||||
|
||||
.base-union-row:nth-child(2n) {
|
||||
background-image: url("${base!}/assets/platform/images/careData/leader/base-union-row-b.png?v=20260526");
|
||||
}
|
||||
|
||||
.base-union-name {
|
||||
position: absolute;
|
||||
left: 18%;
|
||||
top: 50%;
|
||||
width: 52%;
|
||||
overflow: hidden;
|
||||
transform: translateY(-50%);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.base-union-count {
|
||||
position: absolute;
|
||||
right: 9.5%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #1d82e8;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.base-union-count span {
|
||||
margin-left: 3px;
|
||||
color: #4e6d8d;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.base-union-empty {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 48%;
|
||||
color: rgba(36, 71, 105, 0.82);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="base-union-panel">
|
||||
<div class="base-union-title">基层工会</div>
|
||||
<div class="base-union-list" v-if="displayUnions.length">
|
||||
<div class="base-union-row" v-for="item in displayUnions" :key="item.unionCode || item.id">
|
||||
<div class="base-union-name" :title="item.unionname">{{ item.unionname || "--" }}</div>
|
||||
<div class="base-union-count">{{ item.value || 0 }}<span>人</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-union-empty" v-else>暂无数据</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
unions: [],
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
displayUnions() {
|
||||
return this.unions
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadBaseUnionData()
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
loadBaseUnionData() {
|
||||
this.$axios.post("/platform/careData/leader/baseUnionData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "unions", data.unions || [])
|
||||
})
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,252 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.club-panel {
|
||||
position: absolute;
|
||||
right: calc(1.55% - 3px);
|
||||
top: calc(38.2% + 3px);
|
||||
width: calc(28.2% - 145px);
|
||||
height: 28.1%;
|
||||
background: url("${base!}/assets/platform/images/careData/leader/base-union-panel.png?v=20260526") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.club-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.club-list {
|
||||
position: absolute;
|
||||
left: calc(8.5% - 14px);
|
||||
top: 20.2%;
|
||||
width: calc(83% + 29px);
|
||||
height: 68.5%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.club-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.club-list::-webkit-scrollbar-track {
|
||||
background: rgba(110, 178, 237, 0.22);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.club-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(49, 142, 231, 0.76);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.club-row {
|
||||
position: relative;
|
||||
flex: 0 0 17.2%;
|
||||
height: 17.2%;
|
||||
min-height: 31px;
|
||||
background-position: center center;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
color: #244769;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.club-row:nth-child(2n + 1) {
|
||||
background-image: url("${base!}/assets/platform/images/careData/leader/base-union-row-a.png?v=20260526");
|
||||
}
|
||||
|
||||
.club-row:nth-child(2n) {
|
||||
background-image: url("${base!}/assets/platform/images/careData/leader/base-union-row-b.png?v=20260526");
|
||||
}
|
||||
|
||||
.club-name {
|
||||
position: absolute;
|
||||
left: 18%;
|
||||
top: 50%;
|
||||
width: 52%;
|
||||
overflow: hidden;
|
||||
transform: translateY(-50%);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.club-count {
|
||||
position: absolute;
|
||||
right: 9.5%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #1d82e8;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.club-count span {
|
||||
margin-left: 3px;
|
||||
color: #4e6d8d;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.club-empty {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 48%;
|
||||
color: rgba(36, 71, 105, 0.82);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="club-panel">
|
||||
<div class="club-title">协会社团</div>
|
||||
<div class="club-list" v-if="clubs.length">
|
||||
<div class="club-row" v-for="item in clubs" :key="item.clubCode || item.id">
|
||||
<div class="club-name" :title="item.clubName">{{ item.clubName || "--" }}</div>
|
||||
<div class="club-count">{{ item.value || 0 }}<span>人</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="club-empty" v-else>暂无数据</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
clubs: [],
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadClubData()
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
loadClubData() {
|
||||
this.$axios.post("/platform/careData/leader/clubData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "clubs", data.clubs || [])
|
||||
})
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,282 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.condolence-panel {
|
||||
position: absolute;
|
||||
left: calc(1.35% - 5px);
|
||||
top: calc(38.9% - 28px);
|
||||
width: calc(20.82% - 6px);
|
||||
height: calc(27.65% + 70px);
|
||||
background: url("${base!}/assets/platform/images/careData/leader/condolence-panel.png?v=20260526") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.condolence-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.condolence-chart {
|
||||
position: absolute;
|
||||
left: 4%;
|
||||
top: calc(18% + 10px);
|
||||
width: 92%;
|
||||
height: 72%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-around;
|
||||
padding: 2.5% 2.2% 7.5%;
|
||||
}
|
||||
|
||||
.condolence-bar-item {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 16%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.condolence-value {
|
||||
position: absolute;
|
||||
bottom: calc(17% + var(--bar-height) + 5px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: #243f5d;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.condolence-bar {
|
||||
position: absolute;
|
||||
bottom: 17%;
|
||||
left: 50%;
|
||||
width: 42%;
|
||||
height: var(--bar-height);
|
||||
min-height: 0;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(180deg, rgba(95, 183, 255, 0.96), rgba(35, 126, 238, 0.88));
|
||||
border: 1px solid rgba(208, 241, 255, 0.66);
|
||||
box-shadow: inset 0 0 10px rgba(215, 244, 255, 0.4), 0 0 10px rgba(58, 152, 245, 0.46);
|
||||
}
|
||||
|
||||
.condolence-bar-item:nth-child(2n) .condolence-bar {
|
||||
background: linear-gradient(180deg, rgba(90, 219, 162, 0.94), rgba(40, 177, 103, 0.86));
|
||||
box-shadow: inset 0 0 10px rgba(224, 255, 239, 0.36), 0 0 10px rgba(54, 190, 118, 0.36);
|
||||
}
|
||||
|
||||
.condolence-bar-item:nth-child(3) .condolence-bar {
|
||||
background: linear-gradient(180deg, rgba(255, 183, 109, 0.94), rgba(229, 132, 66, 0.86));
|
||||
box-shadow: inset 0 0 10px rgba(255, 236, 211, 0.34), 0 0 10px rgba(227, 142, 76, 0.36);
|
||||
}
|
||||
|
||||
.condolence-label {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 56px;
|
||||
overflow: hidden;
|
||||
transform: translateX(-50%);
|
||||
color: #244769;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.68);
|
||||
}
|
||||
|
||||
.condolence-empty {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 43%;
|
||||
color: rgba(36, 71, 105, 0.82);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="condolence-panel">
|
||||
<div class="condolence-title">慰问汇总</div>
|
||||
<div class="condolence-chart">
|
||||
<div class="condolence-empty" v-if="!hasData">暂无数据</div>
|
||||
<div class="condolence-bar-item" v-for="item in displayTypes" :key="item.index" :style="{ '--bar-height': barHeight(item) }">
|
||||
<div class="condolence-value">{{ item.rate || "0%" }}</div>
|
||||
<div class="condolence-bar"></div>
|
||||
<div class="condolence-label" :title="item.name">{{ shortName(item.name) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
types: [],
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasData() {
|
||||
return this.types.some(item => Number(item.count || 0) > 0)
|
||||
},
|
||||
maxCount() {
|
||||
return Math.max(...this.types.map(item => Number(item.count || 0)), 0)
|
||||
},
|
||||
displayTypes() {
|
||||
const rows = this.types.slice(0, 5)
|
||||
while (rows.length < 5) {
|
||||
rows.push({
|
||||
index: rows.length + 1,
|
||||
name: "",
|
||||
rate: "0%",
|
||||
rateValue: 0
|
||||
})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadCondolenceData()
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
loadCondolenceData() {
|
||||
this.$axios.post("/platform/careData/leader/condolenceData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "types", data.types || [])
|
||||
})
|
||||
},
|
||||
shortName(name) {
|
||||
if (!name) {
|
||||
return "--"
|
||||
}
|
||||
return name.length > 4 ? name.slice(0, 4) : name
|
||||
},
|
||||
barHeight(item) {
|
||||
const count = Number(item.count || 0)
|
||||
if (count <= 0 || this.maxCount <= 0) {
|
||||
return "0%"
|
||||
}
|
||||
return Math.min(76, Math.max(8, count / this.maxCount * 76)) + "%"
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,248 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.data-metric-panel {
|
||||
position: absolute;
|
||||
right: calc(1.55% - 2px);
|
||||
top: 8.2%;
|
||||
width: calc(28.2% - 140px);
|
||||
height: 28.1%;
|
||||
background: url("${base!}/assets/platform/images/careData/leader/data-metric-panel.png?v=20260527") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.data-metric-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-metric-graph {
|
||||
position: absolute;
|
||||
left: calc(8% + 5px);
|
||||
top: 21%;
|
||||
width: calc(84% + 5px);
|
||||
height: calc(66% + 20px);
|
||||
background: url("${base!}/assets/platform/images/careData/leader/data-metric-graph.png?v=20260527") center center / contain no-repeat;
|
||||
}
|
||||
|
||||
.data-metric-point {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 28%;
|
||||
min-width: 62px;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #1c79dc;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
text-align: center;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 8px rgba(255, 255, 255, 0.9), 0 0 10px rgba(72, 168, 255, 0.42);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.data-metric-point-center {
|
||||
width: 34%;
|
||||
}
|
||||
|
||||
.data-metric-label {
|
||||
margin-top: 2px;
|
||||
overflow: hidden;
|
||||
color: #315d86;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-metric-value {
|
||||
overflow: hidden;
|
||||
color: #1683f4;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 17px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-metric-point-center .data-metric-value {
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.data-metric-unit {
|
||||
margin-left: 2px;
|
||||
color: #4e6d8d;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="data-metric-panel">
|
||||
<div class="data-metric-title">数据指标</div>
|
||||
<div class="data-metric-graph">
|
||||
<div
|
||||
class="data-metric-point"
|
||||
v-for="item in dataMetricPoints"
|
||||
:key="item.key"
|
||||
:class="{ 'data-metric-point-center': item.center }"
|
||||
:style="{ left: item.left, top: item.top }">
|
||||
<div class="data-metric-value">{{ item.value }}<span class="data-metric-unit">{{ item.unit }}</span></div>
|
||||
<div class="data-metric-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
dataMetric: {},
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dataMetricPoints() {
|
||||
return [
|
||||
{key: "tourCount", label: "疗休养人数", value: this.displayNumber(this.dataMetric.tourCount), unit: "人", left: "12%", top: "19%"},
|
||||
{key: "honorCount", label: "劳模先进", value: this.displayNumber(this.dataMetric.honorCount), unit: "人", left: "88%", top: "19%"},
|
||||
{key: "difficultCount", label: "困难人数", value: this.displayNumber(this.dataMetric.difficultCount), unit: "人", left: "12%", top: "74%"},
|
||||
{key: "reimburseTotal", label: "报销总数", value: this.displayMoneyWan(this.dataMetric.reimburseTotal), unit: "万元", left: "88%", top: "74%"},
|
||||
{key: "budgetTotal", label: "预算总额", value: this.displayMoneyWan(this.dataMetric.budgetTotal), unit: "万元", left: "50%", top: "50%", center: true}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDataMetricData()
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
loadDataMetricData() {
|
||||
this.$axios.post("/platform/careData/leader/dataMetricData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
this.$set(this, "dataMetric", res.data || {})
|
||||
})
|
||||
},
|
||||
displayNumber(value) {
|
||||
return Number(value || 0)
|
||||
},
|
||||
displayMoneyWan(value) {
|
||||
const amount = Number(value || 0) / 10000
|
||||
if (amount >= 100) {
|
||||
return Math.round(amount).toString()
|
||||
}
|
||||
if (amount >= 10) {
|
||||
return amount.toFixed(1).replace(/\.0$/, "")
|
||||
}
|
||||
return amount.toFixed(2).replace(/\.?0+$/, "")
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,599 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #071b36;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.leader-screen {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
min-width: 1280px;
|
||||
min-height: 720px;
|
||||
overflow: hidden;
|
||||
color: #eaf7ff;
|
||||
background:
|
||||
radial-gradient(circle at 50% 45%, rgba(255, 255, 255, 0.62) 0, rgba(135, 202, 255, 0.4) 26%, transparent 58%),
|
||||
linear-gradient(135deg, #74b8f2 0%, #9acbfb 48%, #6aaee9 100%);
|
||||
}
|
||||
|
||||
.leader-screen::before,
|
||||
.leader-screen::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leader-screen::before {
|
||||
border: 2px solid rgba(219, 245, 255, 0.58);
|
||||
box-shadow: inset 0 0 34px rgba(24, 126, 220, 0.34);
|
||||
}
|
||||
|
||||
.leader-screen::after {
|
||||
background:
|
||||
linear-gradient(rgba(255, 255, 255, 0.16) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.12) 1px, transparent 1px);
|
||||
background-size: 78px 78px;
|
||||
mask-image: radial-gradient(circle at 50% 50%, #000 0, transparent 76%);
|
||||
}
|
||||
|
||||
.screen-header {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 82px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.screen-header::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 22%;
|
||||
right: 22%;
|
||||
height: 72px;
|
||||
border: 2px solid rgba(216, 246, 255, 0.58);
|
||||
border-top: none;
|
||||
border-radius: 0 0 88px 88px;
|
||||
background: linear-gradient(180deg, rgba(39, 144, 255, 0.42), rgba(27, 110, 205, 0.12));
|
||||
box-shadow: 0 12px 28px rgba(23, 94, 180, 0.28), inset 0 -12px 24px rgba(159, 230, 255, 0.36);
|
||||
}
|
||||
|
||||
.screen-title {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
color: #f8fdff;
|
||||
font-size: 36px;
|
||||
line-height: 58px;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 10px rgba(40, 140, 232, 0.9);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.weather-box,
|
||||
.time-box {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
z-index: 3;
|
||||
color: rgba(255, 255, 255, 0.94);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.weather-box {
|
||||
left: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.weather-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 35%, #ffe599, #ff9d32 58%, #74c8ff 60%, #d9f4ff 100%);
|
||||
box-shadow: 0 0 14px rgba(255, 207, 99, 0.45);
|
||||
}
|
||||
|
||||
.time-box {
|
||||
right: 28px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.screen-body {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: calc(100vh - 92px);
|
||||
padding: 6px 26px 22px;
|
||||
display: grid;
|
||||
grid-template-columns: 400px minmax(520px, 1fr) 400px;
|
||||
grid-template-rows: 300px 1fr 300px;
|
||||
gap: 18px 22px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
border: 1px solid rgba(232, 249, 255, 0.62);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(77, 162, 229, 0.08));
|
||||
box-shadow: inset 0 0 24px rgba(255, 255, 255, 0.18), 0 10px 28px rgba(25, 103, 183, 0.16);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel::before,
|
||||
.panel::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
width: 42px;
|
||||
height: 2px;
|
||||
background: #ffe078;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.panel::before {
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.panel::after {
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #f8fdff;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(90deg, rgba(31, 126, 238, 0.18), rgba(54, 159, 255, 0.62), rgba(31, 126, 238, 0.18));
|
||||
text-shadow: 0 0 8px rgba(60, 150, 240, 0.78);
|
||||
}
|
||||
|
||||
.proposal-panel {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px 20px;
|
||||
padding: 18px 16px 14px;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
position: relative;
|
||||
height: 62px;
|
||||
padding: 10px 12px 9px 70px;
|
||||
border: 1px solid rgba(227, 248, 255, 0.42);
|
||||
background: linear-gradient(90deg, rgba(231, 248, 255, 0.34), rgba(139, 205, 255, 0.12));
|
||||
box-shadow: inset 0 -8px 12px rgba(32, 124, 218, 0.16);
|
||||
}
|
||||
|
||||
.metric-item::after {
|
||||
content: "↑";
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: 8px;
|
||||
color: rgba(47, 142, 224, 0.72);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
top: 12px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #f4fbff 0, #55c8ff 42%, rgba(34, 139, 227, 0.16) 68%);
|
||||
box-shadow: 0 0 14px rgba(77, 187, 255, 0.7);
|
||||
}
|
||||
|
||||
.metric-icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
right: 11px;
|
||||
top: 9px;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
background: #1d97e6;
|
||||
box-shadow: -7px 9px 0 -2px #1d97e6, 7px 9px 0 -2px #1d97e6;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: rgba(38, 74, 112, 0.86);
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
margin-top: 3px;
|
||||
color: #1677d8;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.metric-value small {
|
||||
margin-left: 3px;
|
||||
color: rgba(49, 93, 132, 0.76);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.center-map {
|
||||
grid-column: 2;
|
||||
grid-row: 1 / 3;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.orbit {
|
||||
position: absolute;
|
||||
width: min(78%, 760px);
|
||||
aspect-ratio: 1;
|
||||
border: 1px dashed rgba(255, 255, 255, 0.6);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 60px rgba(255, 255, 255, 0.22);
|
||||
}
|
||||
|
||||
.map-shape {
|
||||
position: relative;
|
||||
width: min(76%, 760px);
|
||||
height: min(58%, 430px);
|
||||
border-radius: 46% 54% 47% 53% / 38% 45% 55% 62%;
|
||||
background:
|
||||
radial-gradient(circle at 74% 33%, rgba(243, 251, 255, 0.95) 0, rgba(68, 168, 255, 0.88) 22%, transparent 23%),
|
||||
radial-gradient(circle at 50% 52%, #5fb3ff 0, #2786f0 58%, #1b69cf 100%);
|
||||
box-shadow: 0 18px 38px rgba(35, 115, 210, 0.36), 0 0 38px rgba(255, 255, 255, 0.6);
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.map-shape::before,
|
||||
.map-shape::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: rgba(37, 129, 232, 0.9);
|
||||
box-shadow: 0 0 22px rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.map-shape::before {
|
||||
right: -62px;
|
||||
top: 92px;
|
||||
width: 120px;
|
||||
height: 132px;
|
||||
border-radius: 42% 58% 55% 45%;
|
||||
}
|
||||
|
||||
.map-shape::after {
|
||||
right: 42px;
|
||||
bottom: -42px;
|
||||
width: 55px;
|
||||
height: 82px;
|
||||
border-radius: 50%;
|
||||
transform: rotate(18deg);
|
||||
}
|
||||
|
||||
.center-session {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: rgba(20, 76, 134, 0.8);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholder-list {
|
||||
padding: 18px 22px;
|
||||
}
|
||||
|
||||
.placeholder-row {
|
||||
display: grid;
|
||||
grid-template-columns: 34px 1fr 48px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 40px;
|
||||
color: rgba(39, 73, 111, 0.82);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.placeholder-index {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
background: rgba(58, 151, 236, 0.72);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.placeholder-track {
|
||||
height: 5px;
|
||||
background: rgba(255, 255, 255, 0.38);
|
||||
}
|
||||
|
||||
.placeholder-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #41c79f, #3a96ec);
|
||||
}
|
||||
|
||||
.right-circle {
|
||||
height: calc(100% - 40px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.circle-core {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
background: radial-gradient(circle, #e8fbff 0, #42bbff 45%, #1f75da 100%);
|
||||
box-shadow: 0 0 30px rgba(38, 135, 230, 0.46);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.circle-core strong {
|
||||
font-size: 34px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.bottom-nav {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 14px;
|
||||
transform: translateX(-50%);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.nav-dot {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(238, 251, 255, 0.72);
|
||||
background: radial-gradient(circle, #ecfbff 0, #59bfff 52%, #2675d4 100%);
|
||||
box-shadow: 0 0 18px rgba(32, 126, 220, 0.42);
|
||||
}
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
.screen-body {
|
||||
grid-template-columns: 360px minmax(480px, 1fr) 360px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
gap: 12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="leader-screen" v-cloak>
|
||||
<header class="screen-header">
|
||||
<div class="weather-box">
|
||||
<span class="weather-icon"></span>
|
||||
<span>智慧工会 · 领导驾驶舱</span>
|
||||
</div>
|
||||
<h1 class="screen-title">数智工会领导驾驶舱</h1>
|
||||
<div class="time-box">
|
||||
<div>{{ currentDate }}</div>
|
||||
<div>{{ currentTime }}</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="screen-body">
|
||||
<section class="panel proposal-panel">
|
||||
<div class="panel-title">提案征集与办理</div>
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">提案总数</div>
|
||||
<div class="metric-value">{{ overview.total || 0 }}<small>件</small></div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">立案数量</div>
|
||||
<div class="metric-value">{{ overview.filedCount || 0 }}<small>件</small></div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">意见建议梳理</div>
|
||||
<div class="metric-value">{{ overview.suggestionCount || 0 }}<small>件</small></div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">不予立案</div>
|
||||
<div class="metric-value">{{ overview.rejectedCount || 0 }}<small>件</small></div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">办结数量</div>
|
||||
<div class="metric-value">{{ overview.doneCount || 0 }}<small>件</small></div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-icon"></span>
|
||||
<div class="metric-label">满意率</div>
|
||||
<div class="metric-value">{{ overview.satisfiedRate || '0%' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="center-map">
|
||||
<div class="center-session">{{ session.fullName || '当前届次' }}</div>
|
||||
<div class="orbit"></div>
|
||||
<div class="map-shape"></div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">专题指标总览</div>
|
||||
<div class="right-circle">
|
||||
<div class="circle-core">
|
||||
<strong>{{ overview.total || 0 }}</strong>
|
||||
<span>提案指标</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">办理进度排行</div>
|
||||
<div class="placeholder-list">
|
||||
<div class="placeholder-row" v-for="(item, index) in progressRows" :key="item.name">
|
||||
<span class="placeholder-index">{{ pad(index + 1) }}</span>
|
||||
<div>
|
||||
<div>{{ item.name }}</div>
|
||||
<div class="placeholder-track"><div class="placeholder-bar" :style="{width: item.rate}"></div></div>
|
||||
</div>
|
||||
<span>{{ item.rate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">专题模块预留</div>
|
||||
<div class="placeholder-list">
|
||||
<div class="placeholder-row" v-for="(item, index) in reserveRows" :key="item">
|
||||
<span class="placeholder-index">{{ pad(index + 1) }}</span>
|
||||
<div>{{ item }}</div>
|
||||
<span>待接入</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">数据趋势预留</div>
|
||||
<div class="placeholder-list">
|
||||
<div class="placeholder-row" v-for="item in trendRows" :key="item.name">
|
||||
<span class="placeholder-index">{{ item.index }}</span>
|
||||
<div>
|
||||
<div>{{ item.name }}</div>
|
||||
<div class="placeholder-track"><div class="placeholder-bar" :style="{width: item.rate}"></div></div>
|
||||
</div>
|
||||
<span>{{ item.rate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">重点数据预留</div>
|
||||
<div class="placeholder-list">
|
||||
<div class="placeholder-row" v-for="item in keyRows" :key="item.name">
|
||||
<span class="placeholder-index">{{ item.index }}</span>
|
||||
<div>{{ item.name }}</div>
|
||||
<span>{{ item.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
<span class="nav-dot"></span>
|
||||
<span class="nav-dot"></span>
|
||||
<span class="nav-dot"></span>
|
||||
<span class="nav-dot"></span>
|
||||
<span class="nav-dot"></span>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
timer: null,
|
||||
currentDate: "",
|
||||
currentTime: "",
|
||||
session: {},
|
||||
overview: {},
|
||||
progressRows: [
|
||||
{name: "提案征集", rate: "87%"},
|
||||
{name: "立案办理", rate: "82%"},
|
||||
{name: "承办答复", rate: "68%"},
|
||||
{name: "结果反馈", rate: "43%"},
|
||||
{name: "满意评价", rate: "36%"}
|
||||
],
|
||||
reserveRows: ["职工关爱专题", "工会经费专题", "活动服务专题", "荣誉建设专题"],
|
||||
trendRows: [
|
||||
{index: "01", name: "年度提案趋势", rate: "78%"},
|
||||
{index: "02", name: "办理效率趋势", rate: "64%"},
|
||||
{index: "03", name: "满意度趋势", rate: "72%"}
|
||||
],
|
||||
keyRows: [
|
||||
{index: "01", name: "专题指标标题", value: "256"},
|
||||
{index: "02", name: "专题指标标题", value: "205"},
|
||||
{index: "03", name: "专题指标标题", value: "123"}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateTime()
|
||||
this.$set(this, "timer", setInterval(this.updateTime, 1000))
|
||||
this.loadProposalData()
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
loadProposalData() {
|
||||
this.$axios.post("/platform/careData/leader/proposalData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "session", data.session || {})
|
||||
this.$set(this, "overview", data.overview || {})
|
||||
})
|
||||
},
|
||||
updateTime() {
|
||||
const now = new Date()
|
||||
const week = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][now.getDay()]
|
||||
this.$set(this, "currentDate", now.getFullYear() + "-" + this.pad(now.getMonth() + 1) + "-" + this.pad(now.getDate()) + " " + week)
|
||||
this.$set(this, "currentTime", this.pad(now.getHours()) + ":" + this.pad(now.getMinutes()) + ":" + this.pad(now.getSeconds()))
|
||||
},
|
||||
pad(value) {
|
||||
return String(value).padStart(2, "0")
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,142 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
position: absolute;
|
||||
min-width: 86px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #1b83e9;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.75);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.metric-value::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -2px -8px;
|
||||
z-index: -1;
|
||||
background: rgba(189, 224, 252, 0.54);
|
||||
filter: blur(3px);
|
||||
}
|
||||
|
||||
.metric-value small {
|
||||
margin-left: 3px;
|
||||
color: rgba(43, 105, 159, 0.82);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.metric-total {
|
||||
left: 5.8%;
|
||||
top: 16.7%;
|
||||
}
|
||||
|
||||
.metric-filed {
|
||||
left: 15.9%;
|
||||
top: 16.7%;
|
||||
}
|
||||
|
||||
.metric-suggestion {
|
||||
left: 5.8%;
|
||||
top: 24.2%;
|
||||
}
|
||||
|
||||
.metric-rejected {
|
||||
left: 15.9%;
|
||||
top: 24.2%;
|
||||
}
|
||||
|
||||
.metric-done {
|
||||
left: 5.8%;
|
||||
top: 31.7%;
|
||||
}
|
||||
|
||||
.metric-satisfied {
|
||||
left: 15.9%;
|
||||
top: 31.7%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<div class="metric-value metric-total">{{ displayNumber(overview.total) }}<small>件</small></div>
|
||||
<div class="metric-value metric-filed">{{ displayNumber(overview.filedCount) }}<small>件</small></div>
|
||||
<div class="metric-value metric-suggestion">{{ displayNumber(overview.suggestionCount) }}<small>件</small></div>
|
||||
<div class="metric-value metric-rejected">{{ displayNumber(overview.rejectedCount) }}<small>件</small></div>
|
||||
<div class="metric-value metric-done">{{ displayNumber(overview.doneCount) }}<small>件</small></div>
|
||||
<div class="metric-value metric-satisfied">{{ overview.satisfiedRate || "0%" }}</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
overview: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadProposalData()
|
||||
},
|
||||
methods: {
|
||||
loadProposalData() {
|
||||
this.$axios.post("/platform/careData/leader/proposalData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "overview", data.overview || {})
|
||||
})
|
||||
},
|
||||
displayNumber(value) {
|
||||
return Number(value || 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,260 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.proposal-panel {
|
||||
position: absolute;
|
||||
left: 1.35%;
|
||||
top: 8.2%;
|
||||
width: 20.82%;
|
||||
height: 27.65%;
|
||||
background: url("${base!}/assets/platform/images/careData/leader/proposal-panel.png?v=20260526") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.proposal-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1.3%;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.78);
|
||||
}
|
||||
|
||||
.proposal-card-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.proposal-card {
|
||||
position: absolute;
|
||||
width: 44%;
|
||||
height: 19.2%;
|
||||
background: var(--card-bg) center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.proposal-card-total {
|
||||
left: calc(4.3% - 2px);
|
||||
top: 18.5%;
|
||||
}
|
||||
|
||||
.proposal-card-filed {
|
||||
left: 51.7%;
|
||||
top: 18.5%;
|
||||
}
|
||||
|
||||
.proposal-card-total {
|
||||
height: calc(25.6% + 3px);
|
||||
}
|
||||
|
||||
.proposal-card-filed {
|
||||
height: calc(25.6% + 2px);
|
||||
}
|
||||
|
||||
.proposal-card-suggestion {
|
||||
left: calc(4.3% - 1px);
|
||||
top: calc(41.3% + 10px);
|
||||
height: calc(19.8% + 20px);
|
||||
}
|
||||
|
||||
.proposal-card-rejected {
|
||||
left: calc(51.7% + 2px);
|
||||
top: calc(41.3% + 10px);
|
||||
height: calc(19.8% + 20px);
|
||||
}
|
||||
|
||||
.proposal-card-done {
|
||||
left: calc(4.3% - 1px);
|
||||
top: calc(65.2% + 20px);
|
||||
height: calc(19.8% + 20px);
|
||||
}
|
||||
|
||||
.proposal-card-satisfied {
|
||||
left: 51.7%;
|
||||
top: calc(65.2% + 20px);
|
||||
height: calc(19.8% + 20px);
|
||||
}
|
||||
|
||||
.proposal-label {
|
||||
position: absolute;
|
||||
left: 38.5%;
|
||||
top: 20%;
|
||||
max-width: 50%;
|
||||
overflow: hidden;
|
||||
color: #304f70;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
letter-spacing: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.proposal-value {
|
||||
position: absolute;
|
||||
left: calc(38.5% + 23px);
|
||||
top: 48%;
|
||||
max-width: 50%;
|
||||
overflow: hidden;
|
||||
color: #1683f4;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 8px rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="proposal-panel">
|
||||
<div class="proposal-title">提案征集和办理</div>
|
||||
<div class="proposal-card-grid">
|
||||
<article class="proposal-card" v-for="item in proposalMetrics" :key="item.key" :class="'proposal-card-' + item.key" :style="{ '--card-bg': 'url(' + item.bg + ')' }">
|
||||
<div class="proposal-label">{{ item.label }}</div>
|
||||
<div class="proposal-value">{{ item.value }}</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
overview: {},
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
proposalMetrics() {
|
||||
const imagePath = "${base!}/assets/platform/images/careData/leader/"
|
||||
return [
|
||||
{key: "total", label: "提案总数", value: this.displayNumber(this.overview.total), bg: imagePath + "proposal-card-total.png?v=20260526"},
|
||||
{key: "filed", label: "立案数量", value: this.displayNumber(this.overview.filedCount), bg: imagePath + "proposal-card-filed.png?v=20260526"},
|
||||
{key: "suggestion", label: "意见建议", value: this.displayNumber(this.overview.suggestionCount), bg: imagePath + "proposal-card-suggestion.png?v=20260526"},
|
||||
{key: "rejected", label: "不予立案", value: this.displayNumber(this.overview.rejectedCount), bg: imagePath + "proposal-card-rejected.png?v=20260526"},
|
||||
{key: "done", label: "办结数量", value: this.displayNumber(this.overview.doneCount), bg: imagePath + "proposal-card-done.png?v=20260526"},
|
||||
{key: "satisfied", label: "满意率", value: this.overview.satisfiedRate || "0%", bg: imagePath + "proposal-card-satisfied.png?v=20260526"}
|
||||
]
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadProposalData()
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
loadProposalData() {
|
||||
this.$axios.post("/platform/careData/leader/proposalData").then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
this.$set(this, "overview", data.overview || {})
|
||||
})
|
||||
},
|
||||
displayNumber(value) {
|
||||
return Number(value || 0)
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,173 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_leader_dashboard.html"){
|
||||
#-->
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
font-family: "Microsoft YaHei", "Microsoft JhengHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background: #050f1f;
|
||||
}
|
||||
|
||||
.dashboard-stage {
|
||||
position: relative;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
background: url("${base!}/assets/platform/images/careData/leader/leader-bg.jpg?v=20260524") center center / 100% 100% no-repeat;
|
||||
}
|
||||
|
||||
.staff-home-panel {
|
||||
position: absolute;
|
||||
left: 24.1%;
|
||||
top: 22.5%;
|
||||
width: 53.1%;
|
||||
height: 63.5%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.staff-home-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
min-width: 168px;
|
||||
height: 30px;
|
||||
padding: 0 28px;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(90deg, rgba(78, 161, 246, 0.08), rgba(79, 169, 255, 0.88), rgba(78, 161, 246, 0.08));
|
||||
border-top: 1px solid rgba(214, 241, 255, 0.7);
|
||||
border-bottom: 1px solid rgba(83, 173, 255, 0.48);
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 0 8px rgba(255, 255, 255, 0.85), 0 0 12px rgba(57, 151, 255, 0.72);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.staff-home-map-wrap {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 30px;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle at center, rgba(181, 223, 255, 0.3), rgba(54, 145, 235, 0.08) 56%, rgba(54, 145, 235, 0));
|
||||
}
|
||||
|
||||
.staff-home-map {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -40px;
|
||||
top: -100px;
|
||||
width: calc(100% + 100px);
|
||||
height: calc(100% + 100px);
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
object-fit: fill;
|
||||
filter: drop-shadow(0 0 18px rgba(91, 177, 255, 0.52));
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
position: fixed;
|
||||
right: 18px;
|
||||
bottom: 18px;
|
||||
z-index: 20;
|
||||
height: 34px;
|
||||
min-width: 86px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid rgba(220, 246, 255, 0.72);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 101, 188, 0.58);
|
||||
color: #f4fbff;
|
||||
font-size: 13px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 14px rgba(64, 161, 255, 0.35);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
background: rgba(20, 126, 220, 0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="dashboard-wrap" v-cloak>
|
||||
<main class="dashboard-stage">
|
||||
<section class="staff-home-panel">
|
||||
<div class="staff-home-title">职工小家</div>
|
||||
<div class="staff-home-map-wrap">
|
||||
<img class="staff-home-map" src="${base!}/assets/platform/images/careData/leader/staff-home-campus-map.png?v=20260527" alt="">
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<button class="fullscreen-btn" type="button" @click="toggleFullscreen">{{ fullscreen ? "退出全屏" : "全屏显示" }}</button>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
fullscreen: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.addEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("fullscreenchange", this.syncFullscreenState)
|
||||
document.removeEventListener("webkitfullscreenchange", this.syncFullscreenState)
|
||||
},
|
||||
methods: {
|
||||
toggleFullscreen() {
|
||||
if (this.fullscreen) {
|
||||
const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen
|
||||
exitFullscreen && exitFullscreen.call(document)
|
||||
return
|
||||
}
|
||||
const target = document.documentElement
|
||||
const requestFullscreen = target.requestFullscreen || target.webkitRequestFullscreen
|
||||
if (requestFullscreen) {
|
||||
const result = requestFullscreen.call(target)
|
||||
if (result && result.catch) {
|
||||
result.catch(() => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
syncFullscreenState() {
|
||||
this.$set(this, "fullscreen", !!(document.fullscreenElement || document.webkitFullscreenElement))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -67,7 +67,7 @@ layout("/layouts/platform.html"){
|
||||
<el-table-column label="所属工会" prop="unionName" sortable></el-table-column>
|
||||
<el-table-column label="所属单位" prop="unitName" sortable></el-table-column>
|
||||
<el-table-column label="联系方式" prop="mobile" sortable></el-table-column>
|
||||
<el-table-column label="答题日期" prop="attemptDate" sortable></el-table-column>
|
||||
<el-table-column label="答题时间" prop="submitTime" sortable></el-table-column>
|
||||
<el-table-column label="得分" prop="totalScore" sortable></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="100px" v-if="$auth.hasPermission('qsv.quiz.rank.delete')">
|
||||
<template slot-scope="{row}">
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
const COMMON_QUERY = {
|
||||
template: [
|
||||
'',
|
||||
' <div class="search">',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">姓名/工号</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-input placeholder="请输入姓名或工号查询" clearable v-model="pageForm.searchKeyword"></el-input>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">所属工会</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-select @change="flushUnits" @clear="flushUnits" clearable filterable',
|
||||
' placeholder="请选择所属工会" style="width: 100%;" v-model="pageForm.unionId">',
|
||||
' <el-option :key="item.id" :label="item.unionname" :value="item.id"',
|
||||
' v-for="item in unions"></el-option>',
|
||||
' </el-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">所属单位</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-select clearable filterable placeholder="请选择所属单位" style="width: 100%"',
|
||||
' v-model="pageForm.unitId">',
|
||||
' <el-option :key="item.id" :label="item.name" :value="item.id"',
|
||||
' v-for="item in units"></el-option>',
|
||||
' </el-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">在职状态</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <dict-select v-model="pageForm.userStates" style="width: 100%" placeholder="在职状态" @change="doSearch"',
|
||||
' code="USER_STATE" multiple></dict-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">人员类型</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <dict-select v-model="pageForm.personTypes" style="width: 100%" placeholder="人员类型" @change="doSearch"',
|
||||
' code="PERSON_TYPE" multiple></dict-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">人员性质</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <dict-select v-model="pageForm.preparedBys" style="width: 100%" placeholder="人员性质" @change="doSearch"',
|
||||
' code="PREPARED_BY" multiple></dict-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">生日日期</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-date-picker :picker-options="pickerOptions" @change="changeDateRangeChange"',
|
||||
' align="right" end-placeholder="结束日期" format="yyyy-MM-dd"',
|
||||
' range-separator="-" start-placeholder="开始日期" style="width: 100%"',
|
||||
' type="datetimerange" v-model="pageForm.changeDateRange"',
|
||||
' value-format="yyyy-MM-dd"></el-date-picker>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-query">',
|
||||
' <el-button type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' '
|
||||
].join(''),
|
||||
data() {
|
||||
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: [],
|
||||
units: [],
|
||||
pageForm: {
|
||||
searchKeyword: '',
|
||||
unionId: '',
|
||||
unitId: '',
|
||||
userStates: [],
|
||||
personTypes: [],
|
||||
preparedBys: [],
|
||||
changeDateRange: [],
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doSearch(){
|
||||
const pageForm = clone(this.pageForm)
|
||||
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
|
||||
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
|
||||
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
|
||||
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(){
|
||||
if (this.$auth.hasRoleOr('sysadmin, SchoolUnionMemberAdmin, SchoolUnionAdmin')) {
|
||||
this.unions = await getUnions(this.pageForm.unionId)
|
||||
this.units = await getUnits()
|
||||
} else {
|
||||
const user = JSON.parse(window.sessionStorage.getItem('user'))
|
||||
this.unions = await getUnions(user.union.id)
|
||||
this.units = await getUnits(user.union.id)
|
||||
}
|
||||
},
|
||||
async flushUnits(){
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.units = []
|
||||
if (this.pageForm.unionId) {
|
||||
this.units = await getUnits(this.pageForm.unionId)
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.initData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<common-query ref="commonQueryRef" @search="search"></common-query>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool label="数据列表(默认展示近15天的数据,搜索会忽略年份)">
|
||||
<el-button type="primary" size="small" @click="doExport">导出Excel</el-button>
|
||||
<el-button type="primary" size="small" @click="openSendMsgByQuery">发送通知</el-button>
|
||||
<el-button type="primary" size="small" @click="openPicSet">配置图片</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" ref="table"
|
||||
row-key="id" style="width: 100%">
|
||||
<el-table-column :index="indexMethod"
|
||||
header-align="center"
|
||||
label="序号" type="index" width="80px"></el-table-column>
|
||||
<el-table-column :label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
:width="column.width"
|
||||
header-align="center"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns">
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="250px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="openView(row)" size="mini" type="primary">人员信息</el-button>
|
||||
<el-button @click="openSendMsgByUser(row)" size="mini" type="primary">发送通知</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<template #view>
|
||||
<member-info ref="memberInfoRef"></member-info>
|
||||
</template>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="sendMsgByQueryDialogVisible"
|
||||
title="请在下方填写需要发送的内容"
|
||||
width="50%"
|
||||
>
|
||||
<el-form :model="sendMsgByQueryFormData" label-width="80px" ref="sendMsgByQueryForm">
|
||||
|
||||
<el-form-item
|
||||
:rules="[{required: true, message: '请输入发送标题', trigger: ['blur', 'change']}]"
|
||||
label="发送标题"
|
||||
prop="title">
|
||||
<el-input
|
||||
:rows="6"
|
||||
placeholder="请输入发送标题"
|
||||
v-model="sendMsgByQueryFormData.title">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:rules="[{required: true, message: '请选择发送内容', trigger: ['blur', 'change']}]"
|
||||
label="发送内容"
|
||||
prop="content">
|
||||
<el-input
|
||||
:rows="6"
|
||||
placeholder="请输入内容"
|
||||
type="textarea"
|
||||
v-model="sendMsgByQueryFormData.content">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<span class="dialog-footer" slot="footer">
|
||||
<el-button @click="sendMsgByQueryDialogVisible = false">取 消</el-button>
|
||||
<el-button @click="sendMsgByQueryUsers" type="primary">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 根据行内数据发送消息 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="sendMsgByUserDialogVisible"
|
||||
title="请在下方填写需要发送的内容"
|
||||
width="50%"
|
||||
>
|
||||
<el-form :model="sendMsgByUserFormData" label-width="80px" ref="sendForm">
|
||||
<el-form-item label="发送对象" prop="userInfo">
|
||||
<el-input placeholder="请输入发送对象" readonly v-model="sendMsgByUserFormData.userInfo"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:rules="[{required: true, message: '请输入发送标题', trigger: ['blur', 'change']}]"
|
||||
label="发送标题"
|
||||
prop="title">
|
||||
<el-input
|
||||
placeholder="请输入发送标题"
|
||||
v-model="sendMsgByUserFormData.title">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:rules="[{required: true, message: '请选择发送内容', trigger: ['blur', 'change']}]"
|
||||
label="发送内容"
|
||||
prop="content">
|
||||
<el-input
|
||||
:rows="6"
|
||||
placeholder="请输入内容"
|
||||
type="textarea"
|
||||
v-model="sendMsgByUserFormData.content">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span class="dialog-footer" slot="footer">
|
||||
<el-button @click="sendMsgByUserDialogVisible = false">取 消</el-button>
|
||||
<el-button @click="sendMsgByUser" type="primary">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="dialogVisible"
|
||||
title="生日贺卡图片设置"
|
||||
width="50%"
|
||||
>
|
||||
<el-form :model="formData" label-width="80px" ref="formRef">
|
||||
<el-form-item label="背景图片" prop="picUrl">
|
||||
<file-upload :upload_number="1"
|
||||
:upload_size="1024 * 1024 * 5"
|
||||
:value.sync="formData.picUrl"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="id"></file-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="贺卡图片" prop="birthdayUrl">
|
||||
<file-upload :upload_number="1"
|
||||
:upload_size="1024 * 1024 * 5"
|
||||
:value.sync="formData.birthdayUrl"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="id"></file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span class="dialog-footer" slot="footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button @click="doSubmit" type="primary">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<!--#include("commonQuery.js"){}#-->
|
||||
<!--#include("/platform/zhgh/staffmanage/member/common/info/memberInfo.js"){}#-->
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {},
|
||||
|
||||
tableColumns: [
|
||||
{ prop: "loginname", label: "工号", sortable: true },
|
||||
{ prop: "username", label: "姓名", sortable: true },
|
||||
{ prop: "sex", label: "性别", sortable: true },
|
||||
{ prop: "mobile", label: "联系方式", sortable: true },
|
||||
{ prop: "birthday", label: "出生年月", sortable: true },
|
||||
{ prop: "daysUntilBirthday", label: "生日倒计时(天)", sortable: true },
|
||||
{ prop: "userState", label: "在职状态", sortable: true, width: "100px" },
|
||||
{ prop: "personType", label: "人员类型", sortable: true, width: "120px" },
|
||||
{ prop: "preparedBy", label: "人员性质", sortable: true, width: "120px" },
|
||||
{ prop: "unionName", label: "所属工会", sortable: true, width: "160px" },
|
||||
{ prop: "unitName", label: "所属单位", sortable: true, width: "160px" }
|
||||
],
|
||||
|
||||
// 根据查询条件推送消息相关
|
||||
sendMsgByQueryDialogVisible: false,
|
||||
sendMsgByQueryFormData: {},
|
||||
|
||||
// 根据行内发送消息相关
|
||||
sendMsgByUserDialogVisible: false,
|
||||
sendMsgByUserFormData: {},
|
||||
|
||||
// 图片配置
|
||||
dialogVisible: false,
|
||||
formData: {
|
||||
picUrl: '',
|
||||
birthdayUrl: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'common-query': COMMON_QUERY,
|
||||
'member-info': MEMBER_INFO,
|
||||
},
|
||||
methods: {
|
||||
async openPicSet() {
|
||||
const resp = await $.post('/platform/staffManage/birthday/manage/getConfig')
|
||||
if (resp.code === 0) {
|
||||
this.formData = resp.data
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
doSubmit() {
|
||||
this.$confirm('确定要提交吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const resp = await $.post('/platform/staffManage/birthday/manage/saveOrModifyConfig', this.formData)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success('保存成功')
|
||||
this.dialogVisible = false
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
openSendMsgByUser(row) {
|
||||
this.sendMsgByUserFormData.userId = row.id
|
||||
this.sendMsgByUserFormData.userInfo = row.username
|
||||
this.sendMsgByUserDialogVisible = true
|
||||
},
|
||||
async sendMsgByUser() {
|
||||
const valid = await this.$refs['sendForm'].validate()
|
||||
if (!valid) return
|
||||
const confirm = await this.$confirm('您确定要向' + this.sendMsgByUserFormData.userInfo + '数据发送信息吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonTest: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
const resp = await $.post('/platform/staffManage/birthday/manage/sendMsgByUser',
|
||||
this.sendMsgByUserFormData
|
||||
)
|
||||
if (resp.code === 0) {
|
||||
this.sendMsgByUserDialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
openSendMsgByQuery() {
|
||||
this.sendMsgByQueryFormData = JSON.parse(JSON.stringify(this.pageForm))
|
||||
this.sendMsgByQueryDialogVisible = true
|
||||
},
|
||||
async sendMsgByQueryUsers() {
|
||||
const valid = await this.$refs['sendMsgByQueryForm'].validate()
|
||||
if (!valid) return
|
||||
const confirm = await this.$confirm('您确定根据查询条件发送信息吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonTest: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
const resp = await $.post('/platform/staffManage/birthday/manage/sendMsgByQueryUsers', this.sendMsgByQueryFormData)
|
||||
if (resp.code === 0) {
|
||||
this.sendMsgByQueryDialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
doExport() {
|
||||
this.$downLoad("/platform/staffManage/birthday/manage/doExport", this.pageForm)
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.view()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.memberInfoRef.onOpen(row.id)
|
||||
})
|
||||
},
|
||||
search(pageForm){
|
||||
this.pageForm = { ...this.pageForm, ...pageForm }
|
||||
this.pageData()
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,74 @@
|
||||
const COMMON_QUERY = {
|
||||
template: [
|
||||
'',
|
||||
' <div class="search">',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">姓名/工号</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-input placeholder="请输入姓名或工号查询" clearable v-model="pageForm.searchKeyword"></el-input>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">所属工会</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-select @change="flushUnits" @clear="flushUnits" clearable filterable',
|
||||
' placeholder="请选择所属工会" style="width: 100%;" v-model="pageForm.unionId">',
|
||||
' <el-option :key="item.id" :label="item.unionname" :value="item.id"',
|
||||
' v-for="item in unions"></el-option>',
|
||||
' </el-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-item">',
|
||||
' <div class="search-item-label">所属单位</div>',
|
||||
' <div class="search-item-option">',
|
||||
' <el-select clearable filterable placeholder="请选择所属单位" style="width: 100%"',
|
||||
' v-model="pageForm.unitId">',
|
||||
' <el-option :key="item.id" :label="item.name" :value="item.id"',
|
||||
' v-for="item in units"></el-option>',
|
||||
' </el-select>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="search-query">',
|
||||
' <el-button type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' '
|
||||
].join(''),
|
||||
data() {
|
||||
return {
|
||||
|
||||
unions: [],
|
||||
units: [],
|
||||
pageForm: {
|
||||
searchKeyword: '',
|
||||
unionId: '',
|
||||
unitId: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doSearch(){
|
||||
this.$emit('search', this.pageForm)
|
||||
},
|
||||
async initData(){
|
||||
if (this.$auth.hasRoleOr('sysadmin, SchoolUnionMemberAdmin, SchoolUnionAdmin')) {
|
||||
this.unions = await getUnions(this.pageForm.unionId)
|
||||
this.units = await getUnits()
|
||||
} else {
|
||||
const user = JSON.parse(window.sessionStorage.getItem('user'))
|
||||
this.unions = await getUnions(user.union.id)
|
||||
this.units = await getUnits(user.union.id)
|
||||
}
|
||||
},
|
||||
async flushUnits(){
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.units = []
|
||||
if (this.pageForm.unionId) {
|
||||
this.units = await getUnits(this.pageForm.unionId)
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.initData()
|
||||
}
|
||||
}
|
||||