Compare commits

...
10 Commits
Author SHA1 Message Date
c-zhouhf1 36ac0aad69 commit 2026-06-22 14:58:03 +08:00
= a2e5e7d56f commit 2026-06-12 09:03:33 +08:00
c-zhouhf1 8315cda9a7 手机端签字不自动回显 2026-06-10 16:49:10 +08:00
= 7672709749 commit 2026-06-08 11:14:14 +08:00
= a5556a1f3d commit 2026-06-05 17:16:20 +08:00
= 67ea2030b5 commit 2026-06-01 11:25:27 +08:00
= 175c8de61a commit 2026-05-27 15:48:54 +08:00
= faa7fca17a Merge branch 'main' of https://dd3skj.picp.vip/zhouhefeng/zhgh_whcp_new 2026-05-26 16:39:03 +08:00
= 0fb3c1b6e5 commit 2026-05-26 16:38:18 +08:00
c-zhouhf1 23a5fb4c3f 手机端签字不自动回显 2026-05-21 17:21:51 +08:00
27 changed files with 751 additions and 109 deletions
+1 -1
View File
@@ -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>v4</artifactId>
<artifactId>zhgh_whcp</artifactId>
<version>5.6.0-plus</version>
<build>
<resources>
@@ -2,6 +2,7 @@ package com.budwk.app.sys.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RedisConstant;
import com.budwk.app.base.result.Result;
@@ -79,13 +80,20 @@ public class SysSignatureController {
@ApiOperation("手机扫描电脑端二维码签字-保存签字信息")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result saveSignature(@Valid String id, @Param("file") TempFile tempFile) {
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
log.info("扫码临时签字开始,userId={}, loginname={}, signId={}, writeTarget=redis", userId, loginname, id);
if (ObjectUtil.isEmpty(tempFile)) {
log.warn("扫码临时签字失败,上传文件为空,userId={}, loginname={}, signId={}", userId, loginname, id);
return Result.error("保存错误,请联系管理员");
}
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), tempFile);
redisService.setex(RedisConstant.SIGNATURE_PREFIX + SecurityUtil.getUserId() + ":" + id, 60 * 10, url);
String redisKey = RedisConstant.SIGNATURE_PREFIX + userId + ":" + id;
redisService.setex(redisKey, 60 * 10, url);
log.info("扫码临时签字上传完成,userId={}, loginname={}, signId={}, redisKey={}, url={}, writeSysUserSignature=false",
userId, loginname, id, redisKey, url);
ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + SecurityUtil.getUserLoginname() + ":*");
ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + loginname + ":*");
ScanResult<String> scan = null;
do {
scan = redisService.scan(scan == null ? ScanParams.SCAN_POINTER_START : scan.getStringCursor(), match);
@@ -102,7 +110,12 @@ public class SysSignatureController {
@SaCheckLogin
@ApiOperation("手机扫描电脑端二维码签字-电脑端获取签字信息")
public Result getSignature(@Valid String id) {
String signature = redisService.get(RedisConstant.SIGNATURE_PREFIX + SecurityUtil.getUserId() + ":" + id);
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
String redisKey = RedisConstant.SIGNATURE_PREFIX + userId + ":" + id;
String signature = redisService.get(redisKey);
log.info("扫码临时签字读取,userId={}, loginname={}, signId={}, redisKey={}, hasSignature={}",
userId, loginname, id, redisKey, StrUtil.isNotBlank(signature));
return Result.success().addData(signature);
}
@@ -110,7 +123,12 @@ public class SysSignatureController {
@SaCheckLogin
@ApiOperation("手机扫描电脑端二维码签字-电脑端清除签字信息")
public Result clearSignature(@Valid String id) {
redisService.del(RedisConstant.SIGNATURE_PREFIX + SecurityUtil.getUserId() + ":" + id);
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
String redisKey = RedisConstant.SIGNATURE_PREFIX + userId + ":" + id;
redisService.del(redisKey);
log.info("扫码临时签字清理,userId={}, loginname={}, signId={}, redisKey={}, clearTarget=redis",
userId, loginname, id, redisKey);
return Result.success();
}
@@ -119,7 +137,12 @@ public class SysSignatureController {
@ApiOperation("手机端签字-保存签字信息(直接返回url)")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result saveH5Signature(@Param("file") TempFile tempFile) {
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
log.info("H5业务签字上传开始,userId={}, loginname={}, writeSysUserSignature=false", userId, loginname);
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), tempFile);
log.info("H5业务签字上传完成,userId={}, loginname={}, url={}, writeSysUserSignature=false",
userId, loginname, url);
return Result.success().addData(url);
}
@@ -130,16 +153,22 @@ public class SysSignatureController {
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
@Aop(TransAop.READ_COMMITTED)
public Result update(@Param("file") TempFile tempFile) {
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
log.info("个人签名更新开始,userId={}, loginname={}, writeTarget=sys_user_signature", userId, loginname);
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), tempFile);
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class);
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class, Cnd.where(Sys_user_signature::getUserId, "=", userId));
boolean exists = ObjectUtil.isNotNull(sysUserSignature);
if (ObjectUtil.isNull(sysUserSignature)) {
sysUserSignature = new Sys_user_signature();
}
sysUserSignature.setSignature(url);
sysUserSignature.setUserId(SecurityUtil.getUserId());
sysUserSignature.setUserId(userId);
dao.insertOrUpdate(sysUserSignature);
log.info("个人签名更新完成,userId={}, loginname={}, existsBefore={}, signatureId={}, url={}",
userId, loginname, exists, sysUserSignature.getId(), url);
wkWebSocketUtil.fire(SecurityUtil.getUserLoginname(),Json.toJson(NutMap.NEW().addv("action","pc-scan-code-manage-signature").addv("url",url)));
wkWebSocketUtil.fire(loginname,Json.toJson(NutMap.NEW().addv("action","pc-scan-code-manage-signature").addv("url",url)));
return Result.success();
}
@@ -148,7 +177,15 @@ public class SysSignatureController {
@SaCheckLogin
@ApiOperation("电脑端我的电子签名获取用户签字")
public Result get() {
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class, Cnd.where(Sys_user_signature::getUserId, "=", SecurityUtil.getUserId()));
String userId = SecurityUtil.getUserId();
String loginname = SecurityUtil.getUserLoginname();
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class, Cnd.where(Sys_user_signature::getUserId, "=", userId));
log.info("个人签名查询,userId={}, loginname={}, found={}, signatureId={}, hasSignature={}",
userId,
loginname,
ObjectUtil.isNotNull(sysUserSignature),
ObjectUtil.isNotNull(sysUserSignature) ? sysUserSignature.getId() : null,
ObjectUtil.isNotNull(sysUserSignature) && StrUtil.isNotBlank(sysUserSignature.getSignature()));
return Result.success(sysUserSignature);
}
@@ -309,8 +309,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
String decodePwd = Base64Decoder.decodeStr(passowrd);
String hashedPassword = PwdUtil.getPassword(decodePwd, user.getSalt());
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
if (Globals.sso){
throw new BaseException("用户名或者密码不正确");
}
}
user = this.fetchLinks(user, "unit");
if (Lang.isNotEmpty(user.getUnit()) && StrUtil.isNotBlank(user.getUnit().getUnionId())) {
Sys_union union = dao().fetch(Sys_union.class, user.getUnit().getUnionId());
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.dayofficework.qsv.h5controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.result.Result;
@@ -12,6 +13,8 @@ import com.budwk.app.zhgh.dayofficework.qsv.param.QsvAnswerParam;
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvUserAnswerRecordService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
@@ -36,10 +39,12 @@ public class H5QsvSurveyController {
@At("")
@Ok("beetl:/platform/zhghh5/dayofficework/qsv/survey/index.html")
@SaCheckLogin
public void index() {
}
@At
@SaCheckLogin
public Result subjects(String activityId) {
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
@@ -78,12 +83,15 @@ public class H5QsvSurveyController {
}
@At
@SaCheckLogin
public Result answerRecord(@Valid String answerRecordId) {
QsvUserAnswerRecord record = dao.fetch(QsvUserAnswerRecord.class, answerRecordId);
return Result.success(record);
}
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
public Result submitAnswer(@Valid @Param("answer") QsvAnswerParam qsvAnswerParam) {
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, qsvAnswerParam.getAnswerRecordId());
JSONObject extJson = answerRecord.getExtJson();
@@ -382,8 +382,8 @@ public class CondolenceMineController {
approval.put("date", DateUtil.format(v.getFinishTime(), "yyyy年MM月dd日"));
approval.put("user", taskFormData.getStr("tf_userName"));
if (StrUtil.isNotBlank(taskFormData.getStr("tf_sign"))) {
approval.put("sign", sysOfficeTemplateUtil.createPictureRenderData(taskFormData.getStr("tf_sign")));
if (StrUtil.isNotBlank(taskFormData.getStr("tf_userSign"))) {
approval.put("sign", sysOfficeTemplateUtil.createPictureRenderData(taskFormData.getStr("tf_userSign")));
}
approval.put("opinion", taskFormData.getStr("tf_opinion"));
docData.put("fgh", approval);
@@ -399,8 +399,8 @@ public class CondolenceMineController {
approval.put("date", DateUtil.format(v.getFinishTime(), "yyyy年MM月dd日"));
approval.put("user", taskFormData.getStr("tf_userName"));
if (StrUtil.isNotBlank(taskFormData.getStr("tf_sign"))) {
approval.put("sign", sysOfficeTemplateUtil.createPictureRenderData(taskFormData.getStr("tf_sign")));
if (StrUtil.isNotBlank(taskFormData.getStr("tf_userSign"))) {
approval.put("sign", sysOfficeTemplateUtil.createPictureRenderData(taskFormData.getStr("tf_userSign")));
}
approval.put("opinion", taskFormData.getStr("tf_opinion"));
docData.put("xzx", approval);
@@ -105,7 +105,8 @@ public class MemberApplyMineController {
""");
Cnd cnd = Cnd.NEW();
MemberApplyPageForm.buildSearch(cnd, pageForm);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name())) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name(),
RoleConstant.SCHOOL_UNION_CHAIRMAN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
cnd.and("info.unionId", "=", SecurityUtil.getUnionId());
} else {
@@ -144,7 +145,7 @@ public class MemberApplyMineController {
*/
@At
@ApiOperation("获取申请信息")
@SaCheckPermission(value = {"member.apply.mine", "member.apply.branchUnionApproval", "member.apply.branchUnionApproval"}, mode = SaMode.OR)
@SaCheckPermission(value = {"member.apply.mine", "member.apply.query", "member.apply.branchUnionApproval", "member.apply.branchUnionApproval"}, mode = SaMode.OR)
public Result findMemberApplyRecord(@Valid String id) {
MemberApplyRecord record = dao.fetch(MemberApplyRecord.class, id);
return Result.success().addData(record);
@@ -0,0 +1,109 @@
package com.budwk.app.zhgh.staffmanage.member.controller.apply;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberApplyPageForm;
import com.budwk.app.zhgh.staffmanage.member.service.MemberCommonService;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
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.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
/**
* 会员入会申请查询统计
*/
@IocBean
@Ok("json:full")
@At("/platform/member/apply/query")
public class MemberApplyQueryController {
@Inject
private MemberCommonService memberCommonService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/member/apply/query/index.html")
@SaCheckPermission("member.apply.query")
public void index() {
}
@At
@ApiOperation("会员入会申请查询统计列表")
@SaCheckPermission("member.apply.query")
public Result pageData(MemberApplyPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.userName,
info.loginName,
info.unitName,
info.unionName,
info.userState,
info.preparedBy,
info.personType,
info.applyDateTime,
info.sign,
info.nativePlace,
info.jobCategory,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
GROUP_CONCAT(DISTINCT ta.actorName, '(', ta.actorAccount, ')' ) AS auditUser,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask' AND taskState in (10,20)) AS startTaskId
FROM
member_apply_record 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 = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
MemberApplyPageForm.buildSearch(cnd, pageForm);
// 查询统计只展示流程已完成的入会申请,避免未完结流程进入统计口径。
cnd.and("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode());
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("info.applyDateTime");
} else {
cnd.orderBy("info." + pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = memberCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success().addData(pagination);
}
/**
* 导出入会申请表
*
* @param id 入会申请记录id
* @param response 文件响应对象
*/
@At
@Ok("void")
@ApiOperation("导出入会申请表")
@SaCheckPermission("member.apply.query")
public void exportApplyDocx(String id, HttpServletResponse response) {
memberCommonService.exportApplyDocx(id, response);
}
}
@@ -233,6 +233,11 @@ public class MemberApplyRecord extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 100)
private String sign;
@Column
@Comment("照片")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String photo;
@Column
@Comment("来源,高校编码")
@ColDefine(type = ColType.VARCHAR, width = 50)
@@ -40,6 +40,15 @@ import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import lombok.extern.slf4j.Slf4j;
import org.ddr.poi.html.HtmlRenderPolicy;
import org.apache.poi.xwpf.usermodel.LineSpacingRule;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.*;
import org.nutz.dao.sql.Sql;
@@ -52,10 +61,10 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
@@ -77,6 +86,13 @@ import java.util.zip.ZipOutputStream;
@Slf4j
public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implements MemberCommonService {
private static final int MEMBER_APPLY_PHOTO_WIDTH_PIXEL = 100;
private static final int MEMBER_APPLY_PHOTO_HEIGHT_PIXEL = 140;
private static final int MEMBER_APPLY_PHOTO_WIDTH_EMU = Units.pixelToEMU(MEMBER_APPLY_PHOTO_WIDTH_PIXEL);
private static final int MEMBER_APPLY_PHOTO_HEIGHT_EMU = Units.pixelToEMU(MEMBER_APPLY_PHOTO_HEIGHT_PIXEL);
private static final int MEMBER_APPLY_PHOTO_SIZE_TOLERANCE_EMU = Units.pixelToEMU(2);
private static final double MEMBER_APPLY_PHOTO_PARAGRAPH_LINE_POINT = MEMBER_APPLY_PHOTO_HEIGHT_PIXEL * 0.75D;
@Inject
private SysDictService sysDictService;
@Inject
@@ -624,7 +640,7 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
docData.put("nativePlace", member.getNativePlace());
docData.put("marriage", member.getMarriage());
docData.put("specialty", StrUtil.isNotBlank(member.getSpecialty()) ? HtmlUtil.cleanHtmlTag(member.getSpecialty()) : "");
docData.put("specialty", buildMemberApplyDocHtml(member.getSpecialty()));
docData.put("jobCategory", member.getJobCategory());
docData.put("idCard", member.getIdCard());
@@ -633,7 +649,8 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
docData.put("arrivalAtSchoolDate", StrUtil.isNotBlank(member.getArrivalAtSchoolDate()) ? DateUtil.parse(member.getArrivalAtSchoolDate()).toString("yyyy-MM-dd") : "");
docData.put("homeAddress", member.getHomeAddress());
docData.put("personalData", sysOfficeTemplateUtil.convertRichTextToDocText(member.getPersonalData()));
docData.put("personalData", buildMemberApplyDocHtml(member.getPersonalData()));
docData.put("photo", sysOfficeTemplateUtil.createPictureRenderData(MEMBER_APPLY_PHOTO_WIDTH_PIXEL, MEMBER_APPLY_PHOTO_HEIGHT_PIXEL, member.getPhoto()));
docData.put("sign", sysOfficeTemplateUtil.createPictureRenderData(member.getSign()));
docData.put("applyDateTime", member.getApplyDateTime() != null ? DateUtil.format(member.getApplyDateTime(), "yyyy年MM月dd日") : "");
@@ -702,16 +719,109 @@ public class MemberManageServiceImpl extends BaseServiceImpl<Sys_user> implement
Configure config = Configure.builder()
.bind("personalData", htmlRenderPolicy)
.bind("specialty", htmlRenderPolicy)
.build();
try {
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("member_apply_form"), config)
.render(docData)
.write(response.getOutputStream());
} catch (IOException e) {
XWPFTemplate template = XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("member_apply_form"), config)
.render(docData);
resizeMemberApplyPhotoRow(template.getXWPFDocument());
template.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出会员入会申请表失败,ID: {}, 错误信息: {}", id, e.getMessage());
throw new RuntimeException("导出文件失败", e);
}
}
/**
* 入会申请导出时统一转为 HTML 片段,历史富文本保留样式,普通文本保留换行。
*/
private String buildMemberApplyDocHtml(String text) {
if (StrUtil.isBlank(text)) {
return "";
}
String docText = sysOfficeTemplateUtil.convertRichTextToDocText(text);
if (containsHtmlTag(docText)) {
return docText;
}
String escapeText = HtmlUtil.escape(docText)
.replace("\r\n", "\n")
.replace("\r", "\n")
.replace("\n", "<br/>");
return "<p>" + escapeText + "</p>";
}
/**
* 判断内容是否包含 HTML 标签,避免把历史富文本当普通文本转义。
*/
private boolean containsHtmlTag(String text) {
return StrUtil.isNotBlank(text) && text.matches("(?s).*<\\s*[a-zA-Z][^>]*>.*");
}
/**
* Keep the member photo row tall enough so Word does not clip the default inline picture.
*/
private void resizeMemberApplyPhotoRow(XWPFDocument document) {
if (document == null) {
return;
}
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
adjustMemberApplyPhotoParagraph(row);
}
}
}
/**
* Match only the two-inch member photo by rendered picture size, avoiding signature pictures.
*/
private boolean adjustMemberApplyPhotoParagraph(XWPFTableRow row) {
if (row == null) {
return false;
}
boolean found = false;
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph paragraph : cell.getParagraphs()) {
for (XWPFRun run : paragraph.getRuns()) {
if (runContainsMemberApplyPhoto(run)) {
adjustMemberApplyPhotoParagraphStyle(paragraph);
found = true;
}
}
}
}
return found;
}
/**
* Keep the inline photo visible without changing the merged cell vertical anchor.
*/
private void adjustMemberApplyPhotoParagraphStyle(XWPFParagraph paragraph) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setSpacingBefore(0);
paragraph.setSpacingAfter(0);
paragraph.setSpacingBeforeLines(0);
paragraph.setSpacingAfterLines(0);
paragraph.setSpacingBetween(MEMBER_APPLY_PHOTO_PARAGRAPH_LINE_POINT, LineSpacingRule.AT_LEAST);
}
private boolean runContainsMemberApplyPhoto(XWPFRun run) {
if (run == null || run.getCTR() == null) {
return false;
}
for (CTDrawing drawing : run.getCTR().getDrawingArray()) {
for (CTInline inline : drawing.getInlineArray()) {
if (inline.getExtent() != null && isMemberApplyPhotoSize(inline.getExtent().getCx(), inline.getExtent().getCy())) {
return true;
}
}
}
return false;
}
private boolean isMemberApplyPhotoSize(long widthEmu, long heightEmu) {
return Math.abs(widthEmu - MEMBER_APPLY_PHOTO_WIDTH_EMU) <= MEMBER_APPLY_PHOTO_SIZE_TOLERANCE_EMU
&& Math.abs(heightEmu - MEMBER_APPLY_PHOTO_HEIGHT_EMU) <= MEMBER_APPLY_PHOTO_SIZE_TOLERANCE_EMU;
}
}
+89 -9
View File
@@ -1,17 +1,85 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false">
<!-- 定义日志文件的存储路径 -->
<!-- 日志存储路径 -->
<property name="LOG_HOME" value="./logs"/>
<!-- 控制台输出配置 -->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志文件配置 -->
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)start #
# #
#################################################################################
-->
<!-- 所有级别日志写入同一个文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- 框架日志级别(按需调整) -->
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- root loggerDEBUG 级别,输出到控制台和全量文件 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL_FILE"/>
</root>
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)end #
# #
#################################################################################
-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 start #
# #
#################################################################################
-->
<!-- DEBUG级别日志文件配置 -->
<!--<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; INFO级别日志文件配置 &ndash;&gt;
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<encoder>
@@ -24,11 +92,11 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- ERROR级别日志文件配置 -->
&lt;!&ndash; ERROR级别日志文件配置 &ndash;&gt;
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<encoder>
@@ -41,19 +109,31 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<logger name="java" additivity="false" />
&lt;!&ndash; 框架日志级别 &ndash;&gt;
&lt;!&ndash; <logger name="java" additivity="false" />&ndash;&gt;
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- 日志级别和appender的关联 -->
&lt;!&ndash; 日志级别和appender的关联 &ndash;&gt;
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</root>-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 end #
# #
#################################################################################
-->
</configuration>
@@ -15,7 +15,7 @@
<script>
module.exports = {
name: "h5",
name: "h5Signature",
components: {
signature: httpVueLoader("/components/plugins/sysSignature/index.vue?v=" + new Date().getTime())
},
@@ -64,7 +64,7 @@ layout("/layouts/platform.html"){
clearable
remote
reserve-keyword
placeholder="请输入姓名或工号查询"
placeholder="请点击选择证明人(需为工会委员)"
:remote-method="createRemoteMethod(certifierUserOptions)"
@change="certifierUserChange">
<el-option
@@ -129,7 +129,7 @@ layout("/layouts/platform.html"){
<el-descriptions-item label="收款账户" v-if="formData.type !== '1a2f7e8decb649a6ba8ca76e2af07779'">
<el-form-item prop="bankCardNumber" label="收款账户">
<el-input maxlength="20" v-model="formData.bankCardNumber" placeholder="请填写收款账户"
<el-input maxlength="20" v-model="formData.bankCardNumber" placeholder="负责慰问的经办人工资卡号"
></el-input>
</el-form-item>
</el-descriptions-item>
@@ -137,7 +137,7 @@ layout("/layouts/platform.html"){
<el-descriptions-item label="户名" v-if="formData.type !== '1a2f7e8decb649a6ba8ca76e2af07779'">
<el-form-item prop="bankUserName" label="户名">
<el-input maxlength="20" v-model="formData.bankUserName"
placeholder="请填写户名"
placeholder="负责慰问的经办人姓名"
></el-input>
</el-form-item>
</el-descriptions-item>
@@ -145,7 +145,7 @@ layout("/layouts/platform.html"){
<el-descriptions-item label="开户行" v-if="formData.type !== '1a2f7e8decb649a6ba8ca76e2af07779'">
<el-form-item prop="bankOfDeposit" label="开户行">
<el-input maxlength="20" v-model="formData.bankOfDeposit"
placeholder="请填写开户行"
placeholder="工资卡银行"
></el-input>
</el-form-item>
</el-descriptions-item>
@@ -177,7 +177,7 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="当年第几次住院">
<el-descriptions-item label="当年第几次住院" v-if="formData.typeCode !== '3'">
<el-form-item prop="thisYearHospitalizationNum" label="当年第几次住院">
<el-input-number style="width: 100%" v-model="formData.thisYearHospitalizationNum"
placeholder="请输入当年第几次住院"
@@ -337,6 +337,9 @@ layout("/layouts/platform.html"){
this.$set(this.formData, "money", this.chooseType.money)
this.$set(this.formData, "way", this.chooseType.way)
this.$set(this.formData, "typeCode", this.chooseType.code)
if (this.chooseType.code === '3') {
this.$set(this.formData, "thisYearHospitalizationNum", null)
}
}
},
onSave() {
@@ -145,6 +145,8 @@ layout("/layouts/platform.html"){
})
},
handleTaskAction(val) {
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
@@ -163,6 +165,8 @@ layout("/layouts/platform.html"){
}
})
})
}
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
@@ -32,7 +32,7 @@ const condolenceInfo = {
<template v-if="['2','3'].includes(viewData.typeCode)">
<el-descriptions-item label="入院时间">{{ viewData.hospitalizationTime }}</el-descriptions-item>
<el-descriptions-item label="出院时间">{{ viewData.leaveHospitalTime }}</el-descriptions-item>
<el-descriptions-item label="当年第几次住院">{{ viewData.thisYearHospitalizationNum }}
<el-descriptions-item label="当年第几次住院" v-if="viewData.typeCode !== '3'">{{ viewData.thisYearHospitalizationNum }}
</el-descriptions-item>
</template>
<el-descriptions-item label="申请事由" :span="3">
@@ -64,7 +64,7 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column prop="typeName" label="慰问类型"></el-table-column>
<!-- <el-table-column prop="createTime" label="申请时间"></el-table-column>-->
<!-- <el-table-column prop="createTime" label="申请时间"></el-table-column>-->
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
<template slot-scope="{row}">
@@ -151,6 +151,7 @@ layout("/layouts/platform.html"){
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
userId: row.certifierUserId,
taskName: row.curTaskName
}
this.$refs.condolenceInfoRef.onOpen(row)
@@ -197,11 +198,11 @@ layout("/layouts/platform.html"){
})
},
queryCondolenceType() {
this.$axios.post('/platform/condolence/type/queryCondolenceType')
this.$axios.post("/platform/condolence/type/queryCondolenceType")
.then((resp) => {
this.typeOptions = resp.data
})
},
}
},
@@ -54,14 +54,19 @@ const INFO = {
<el-empty v-else :image-size="50" description="暂无家庭成员信息"></el-empty>
</el-descriptions-item>
<el-descriptions-item label="个人学习及工作经历" :span="3">
<div v-if="viewData.personalData" class="text-left" v-html="viewData.personalData"></div>
<div v-if="viewData.personalData" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.personalData"></div>
<el-tag type="warning" v-else>暂无</el-tag>
</el-descriptions-item>
<el-descriptions-item label="特长及获奖情况" :span="3">
<div v-if="viewData.specialty" class="text-left" v-html="viewData.specialty"></div>
<div v-if="viewData.specialty" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.specialty"></div>
<el-tag type="warning" v-else>暂无</el-tag>
</el-descriptions-item>
<el-descriptions-item label="照片" :span="3" v-if="viewData.photo">
<el-image :src="viewData.photo"
style="width: 120px;height: 150px"
fit="cover"></el-image>
</el-descriptions-item>
<el-descriptions-item label="签字信息" :span="3" v-if="viewData.sign">
<el-image :src="viewData.sign"
@@ -0,0 +1,114 @@
<!--#
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="申请列表"></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="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"
>
<template scope="{row}" v-if="column.prop=='loginName'">
<el-link @click="openView(row)" type="primary">{{row.loginName}}</el-link>
</template>
<template scope="{row}" v-else-if="column.prop=='instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template>
<template scope="{row}" v-else-if="column.prop=='taskName'">
{{row.taskName}}
<template v-if="row.auditUser">
-{{row.auditUser}}
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">
查看
</el-button>
<el-button @click="exportApplyDocx(row.id)" size="mini" type="primary">
导出申请表
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #view>
<info ref="info"></info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
<!--#include("../common/commonQuery.js"){}#-->
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
data() {
return {
tableColumns: [
{ prop: "loginName", label: "工号", sortable: true },
{ prop: "userName", label: "姓名", sortable: true },
{ prop: "userState", label: "在职状态", sortable: true },
{ prop: "nativePlace", label: "籍贯", sortable: true },
{ prop: "unionName", label: "所属工会", sortable: true },
{ prop: "unitName", label: "所属单位", sortable: true },
{ prop: "instanceState", label: "流程状态" }
]
}
},
components: {
"info": INFO,
"common-query": COMMON_QUERY
},
methods: {
exportApplyDocx(id) {
this.$downLoad("/platform/member/apply/query/exportApplyDocx", { id: id })
},
openView(row) {
this.$refs.guava.view(() => {
this.$refs.info.onOpen(row)
})
},
search(pageForm) {
if (pageForm) {
this.pageForm = Object.assign({}, this.pageForm, pageForm)
}
this.doSearch()
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -193,13 +193,28 @@ layout("/layouts/platform.html"){
<el-descriptions-item label="个人学习及工作经历" :span="3">
<el-form-item prop="personalData" label="个人学习及工作经历">
<text-editor v-model="formData.personalData"></text-editor>
<el-input type="textarea" :rows="4" v-model="formData.personalData"
placeholder="请输入个人学习及工作经历"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="特长及获奖情况" :span="3">
<el-form-item prop="specialty" label="特长及获奖情况">
<text-editor v-model="formData.specialty"></text-editor>
<el-input type="textarea" :rows="4" v-model="formData.specialty"
maxlength="100" show-word-limit
placeholder="请输入特长及获奖情况"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="照片" :span="3">
<el-form-item prop="photo" label="照片">
<file-upload
:value.sync="formData.photo"
:upload_number="1"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
></file-upload>
</el-form-item>
</el-descriptions-item>
@@ -259,7 +274,29 @@ layout("/layouts/platform.html"){
sex: [{ required: false, message: "必填", trigger: ["change", "blur"] }],
isVoluntary: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
sign: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
photo: [{ required: true, message: "请上传照片", trigger: ["change", "blur"] }],
homeAddress: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
families: [{
validator: (rule, value, callback) => {
// 提交时家庭主要成员至少填写一条,并校验核心成员信息。
if (!value || value.length === 0) {
callback(new Error("请添加家庭主要成员"))
return
}
const hasIncomplete = value.some(item => {
item = item || {}
return !String(item.relation || "").trim()
|| !String(item.name || "").trim()
|| !String(item.unit || "").trim()
})
if (hasIncomplete) {
callback(new Error("请完善家庭主要成员的关系、姓名、工作单位"))
return
}
callback()
},
trigger: ["change", "blur"]
}],
personalData: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
specialty: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
marriage: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
@@ -388,6 +425,8 @@ layout("/layouts/platform.html"){
})
},
onFinishTask() {
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
@@ -406,6 +445,8 @@ layout("/layouts/platform.html"){
loading.close()
})
})
}
})
},
async init() {
let user
@@ -40,8 +40,19 @@
</el-table>
<el-empty v-else :image-size="50" description="暂无家庭成员信息"></el-empty>
</el-descriptions-item>
<el-descriptions-item label="个人简况" :span="3">
<div v-if="viewData.personalData" class="text-left" v-html="viewData.personalData"></div>
<el-descriptions-item label="个人学习及工作经历" :span="3">
<div v-if="viewData.personalData" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.personalData"></div>
<el-tag type="warning" v-else>暂无</el-tag>
</el-descriptions-item>
<el-descriptions-item label="特长及获奖情况" :span="3">
<div v-if="viewData.specialty" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.specialty"></div>
<el-tag type="warning" v-else>暂无</el-tag>
</el-descriptions-item>
<el-descriptions-item label="照片" :span="3">
<el-image v-if="viewData.photo" :src="viewData.photo"
style="width: 120px;height: 150px"
fit="cover"></el-image>
<el-tag type="warning" v-else>暂无</el-tag>
</el-descriptions-item>
@@ -104,7 +104,7 @@ layout("/layouts/platform_h5.html"){
required
readonly
:rules="[{ required: true }]"
placeholder="请点击选择证明人"
placeholder="请点击选择证明人(需为工会委员)"
@click="certifierUserSelectShow = true"
is-link
></van-field>
@@ -201,7 +201,7 @@ layout("/layouts/platform_h5.html"){
maxlength="20"
clearable
name="bankCardNumber"
placeholder="请填写收款账户"
placeholder="负责慰问的经办人工资银行卡号"
:rules="[{ required: true }]"
required
></van-field>
@@ -210,7 +210,7 @@ layout("/layouts/platform_h5.html"){
v-if="formData.type !== '1a2f7e8decb649a6ba8ca76e2af07779'"
v-model="formData.bankUserName"
label="户名"
placeholder="请填写户名"
placeholder="负责慰问的经办人姓名"
:rules="[{ required: true }]"
required
maxlength="20"
@@ -224,7 +224,7 @@ layout("/layouts/platform_h5.html"){
type="textarea"
rows="4"
autosize
placeholder="请填写开户行"
placeholder="工资卡银行"
:rules="[{ required: true }]"
required
name="bankOfDeposit"
@@ -279,6 +279,7 @@ layout("/layouts/platform_h5.html"){
</van-popup>
<van-field type="digit" label="当年第几次住院"
v-if="formData.typeCode !== '3'"
:rules="[{ required: true }]"
required
placeholder="请输入当年第几次住院"
@@ -305,7 +306,13 @@ layout("/layouts/platform_h5.html"){
{{ '(附件说明:' + chooseType.uploadFileDesc + '' }}
</span>
</template>
<van-field class="direction-column-field" name="avatar" label="">
<van-field
class="direction-column-field"
name="files"
label=""
required
:rules="[{ required: true, message: '请上传附件' }]"
>
<template #input>
<h5-file-upload
slot="input"
@@ -490,6 +497,9 @@ layout("/layouts/platform_h5.html"){
this.$set(this.formData, "money", this.chooseType.money)
this.$set(this.formData, "way", this.chooseType.way)
this.$set(this.formData, "typeCode", this.chooseType.code)
if (this.chooseType.code === '3') {
this.$set(this.formData, "thisYearHospitalizationNum", null)
}
}
},
onTypeConfirm(o) {
@@ -71,7 +71,10 @@ layout("/layouts/platform_h5.html"){
show-word-limit
></van-field>
<van-cell-group title="电子签名" class="form-section">
<van-field class="more-text" name="tf_userSign" label="">
<van-field class="more-text" name="tf_userSign" label=""
v-model="formData.tf_userSign"
:rules="[{ required: true, message: '请填写电子签名' }]"
required>
<template #input>
<h5-signature v-model="formData.tf_userSign" slot="input"></h5-signature>
</template>
@@ -133,14 +136,21 @@ layout("/layouts/platform_h5.html"){
onApproval(row) {
this.showApprovalForm = true
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName
}
taskName: row.curTaskName,
tf_opinion: "",
tf_userSign: ""
})
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
// 电子签名组件使用插槽输入,手动校验防止空签名提交
if (!this.formData.tf_userSign) {
this.$toast.fail("请填写电子签名")
return
}
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
@@ -33,7 +33,7 @@ const condolenceInfo = {
<template v-if="['2','3'].includes(viewData.typeCode)">
<van-cell title="入院时间">{{ viewData.hospitalizationTime }}</van-cell>
<van-cell title="出院时间">{{ viewData.leaveHospitalTime }}</van-cell>
<van-cell title="当年第几次住院(次)">{{ viewData.thisYearHospitalizationNum }}</van-cell>
<van-cell title="当年第几次住院(次)" v-if="viewData.typeCode !== '3'">{{ viewData.thisYearHospitalizationNum }}</van-cell>
</template>
<van-cell class="direction-column-cell" title="申请事由">
{{ viewData.remark || '暂无' }}
@@ -27,7 +27,8 @@ layout("/layouts/platform_h5.html"){
</van-sticky>
<table-list api="/platform/condolence/helpUserSign/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
<table-list api="/platform/condolence/helpUserSign/pageData" :page_form.sync="pageForm" ref="tableListRef"
@ready="onReady">
<template v-slot="{index,row}">
<table-column label="慰问对象">{{row.helpUserName}}</table-column>
<table-column label="慰问对象工号">{{row.helpLoginName}}</table-column>
@@ -106,7 +107,7 @@ layout("/layouts/platform_h5.html"){
approvalText: "0",
approval: false,
year: new Date().getFullYear(),
type: null,
type: null
},
typeOptions: [],
formData: {},
@@ -137,12 +138,13 @@ layout("/layouts/platform_h5.html"){
this.$refs.infoRef.onOpen(row)
this.formData = {
processTaskId: row.taskId,
userId: row.certifierUserId,
taskName: row.curTaskName
}
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
await this.$refs.formRef.validate()
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
@@ -167,7 +169,7 @@ layout("/layouts/platform_h5.html"){
}
},
async queryCondolenceType() {
const res = await this.$axios.post('/platform/condolence/type/queryCondolenceType')
const res = await this.$axios.post("/platform/condolence/type/queryCondolenceType")
return res.data
},
doSearch() {
@@ -100,7 +100,10 @@ layout("/layouts/platform_h5.html"){
show-word-limit
></van-field>
<van-cell-group title="电子签名" class="form-section">
<van-field class="more-text" name="tf_userSign" label="">
<van-field class="more-text" name="tf_userSign" label=""
v-model="formData.tf_userSign"
:rules="[{ required: true, message: '请填写电子签名' }]"
required>
<template #input>
<h5-signature v-model="formData.tf_userSign" slot="input"></h5-signature>
</template>
@@ -164,17 +167,24 @@ layout("/layouts/platform_h5.html"){
this.showApprovalForm = true
this.condolenceType = row.type
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName,
bankCardNumber: row.bankCardNumber || '',
bankUserName: row.bankUserName || '',
bankOfDeposit: row.bankOfDeposit || ''
}
bankOfDeposit: row.bankOfDeposit || '',
tf_opinion: "",
tf_userSign: ""
})
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
// 电子签名组件使用插槽输入,手动校验防止空签名提交
if (!this.formData.tf_userSign) {
this.$toast.fail("请填写电子签名")
return
}
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
@@ -71,7 +71,10 @@ layout("/layouts/platform_h5.html"){
show-word-limit
></van-field>
<van-cell-group title="电子签名" class="form-section">
<van-field class="more-text" name="tf_userSign" label="">
<van-field class="more-text" name="tf_userSign" label=""
v-model="formData.tf_userSign"
:rules="[{ required: true, message: '请填写电子签名' }]"
required>
<template #input>
<h5-signature v-model="formData.tf_userSign" slot="input"></h5-signature>
</template>
@@ -133,15 +136,22 @@ layout("/layouts/platform_h5.html"){
onApproval(row) {
this.showApprovalForm = true
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName,
id: row.id
}
id: row.id,
tf_opinion: "",
tf_userSign: ""
})
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
// 电子签名组件使用插槽输入,手动校验防止空签名提交
if (!this.formData.tf_userSign) {
this.$toast.fail("请填写电子签名")
return
}
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
@@ -63,18 +63,24 @@ const INFO = {
<van-cell title="个人学习及工作经历">
<template #label>
<div v-if="viewData.personalData" class="text-left" v-html="viewData.personalData"></div>
<div v-if="viewData.personalData" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.personalData"></div>
<span style="font-size: 14px" v-else>无数据</span>
</template>
</van-cell>
<van-cell title="特长及获奖情况">
<template #label>
<div v-if="viewData.specialty" class="text-left" v-html="viewData.specialty"></div>
<div v-if="viewData.specialty" class="text-left w-e-text member-apply-rich-text" style="white-space: pre-wrap; word-break: break-all;" v-html="viewData.specialty"></div>
<span style="font-size: 14px" v-else>无数据</span>
</template>
</van-cell>
<van-cell title="照片" v-if="viewData.photo">
<van-image :src="viewData.photo"
style="width: 120px;height: 150px"
fit="cover"></van-image>
</van-cell>
<van-cell title="签字" >
<van-image :src="viewData.sign"
v-if="viewData.sign"
@@ -288,11 +288,48 @@ layout("/layouts/platform_h5.html"){
</van-cell-group>
<van-cell-group title="个人学习及工作经历" class="form-section">
<text-editor v-model="formData.personalData"></text-editor>
<van-field
v-model="formData.personalData"
name="personalData"
rows="4"
label=""
type="textarea"
placeholder="请输入个人学习及工作经历"
required
:rules="[{ required: true, message: '请填写个人学习及工作经历' }]"
></van-field>
</van-cell-group>
<van-cell-group title="特长及获奖情况" class="form-section">
<text-editor v-model="formData.specialty"></text-editor>
<van-field
v-model="formData.specialty"
name="specialty"
rows="4"
label=""
type="textarea"
maxlength="100"
show-word-limit
placeholder="请输入特长及获奖情况"
required
:rules="[{ required: true, message: '请填写特长及获奖情况' }]"
></van-field>
</van-cell-group>
<van-cell-group title="照片" class="form-section">
<van-field class="direction-column-field" name="photo" label=""
required :rules="[{ required: true, message: '请上传照片' }]">
<template #input>
<h5-file-upload
slot="input"
:value.sync="formData.photo"
:upload_number="1"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
complete_result
></h5-file-upload>
</template>
</van-field>
</van-cell-group>
<van-cell-group title="签字" class="form-section">
@@ -401,6 +438,9 @@ layout("/layouts/platform_h5.html"){
onSubmit() {
this.$refs.formRef.validate().then(() => {
if (!this.validateFamilies()) {
return
}
if (!this.formData.personalData) {
this.$toast.fail("请填写个人学习及工作经历")
return
@@ -442,6 +482,9 @@ layout("/layouts/platform_h5.html"){
onFinishTask() {
this.$refs.formRef.validate().then(() => {
if (!this.validateFamilies()) {
return
}
if (!this.formData.personalData) {
this.$toast.fail("请填写个人学习及工作经历")
return
@@ -482,6 +525,26 @@ layout("/layouts/platform_h5.html"){
})
},
validateFamilies() {
// 提交时家庭主要成员至少填写一条,并校验核心成员信息。
const families = this.formData.families || []
if (families.length === 0) {
this.$toast.fail("请添加家庭主要成员")
return false
}
const hasIncomplete = families.some(item => {
item = item || {}
return !String(item.relation || "").trim()
|| !String(item.name || "").trim()
|| !String(item.unit || "").trim()
})
if (hasIncomplete) {
this.$toast.fail("请完善家庭主要成员的关系、姓名、工作单位")
return false
}
return true
},
onDateConfirm(value) {
this.formData.birthday = value
this.showDatePicker = false