fix:20260904代码扫描漏洞问题修复

This commit is contained in:
2026-09-09 10:25:53 +08:00
parent 45410cb0a3
commit 465a3257b5
61 changed files with 842 additions and 112 deletions
@@ -33,6 +33,10 @@ import org.nutz.mvc.upload.TempFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -43,6 +47,9 @@ import java.nio.file.StandardOpenOption;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -54,6 +61,9 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
static String IMG_BASE64_PATTERN = "<img\\s+[^>]*src\\s*=\\s*['\"](data:image/[^'\"]+;base64,[^'\"]+)['\"][^>]*>";
private static final int DOWNLOAD_FILE_CACHE_LIMIT = 2000;
private final Map<String, Sys_file> downloadFileCache = new ConcurrentHashMap<>();
private static final Set<String> UPLOAD_SUFFIXES = Set.of("gif", "jpg", "jpeg", "png", "doc", "docx",
"xls", "xlsx", "rar", "zip", "7z", "txt", "pdf", "pptx", "ppt", "mp4");
private static final Set<String> ACTIVE_SUFFIXES = Set.of("svg", "svgz", "html", "htm", "xhtml", "xml", "js", "mjs", "swf");
public SysFileServiceImpl(Dao dao) {
super(dao);
@@ -81,11 +91,13 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
@Override
public void download(String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
if (handleCachedFileRequest(id, request, response)) {
return;
}
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("Cache-Control", "private, no-store");
Sys_file sys_file = fetchDownloadFile(id);
if (!ObjectUtil.isEmpty(sys_file) && handleCachedImageRequest(sys_file, request, response)) {
// 历史主动内容也必须在缓存协商前拦截,避免旧 SVG 继续在业务域下执行。
if (sys_file != null && (ACTIVE_SUFFIXES.contains(StrUtil.blankToDefault(sys_file.getSuffix(), "").toLowerCase(Locale.ROOT))
|| ACTIVE_SUFFIXES.contains(FileUtil.extName(sys_file.getName()).toLowerCase(Locale.ROOT)))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "不允许访问此类型的附件");
return;
}
if (ObjectUtil.isEmpty(sys_file)) {
@@ -112,16 +124,6 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
}
}
private boolean handleCachedFileRequest(String id, HttpServletRequest request, HttpServletResponse response) {
String etag = buildFileEtag(id);
if (etag.equals(request.getHeader("If-None-Match"))) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
setImageCacheHeaders(response, etag);
return true;
}
return false;
}
private Sys_file fetchDownloadFile(String id) {
if (StrUtil.isBlank(id)) {
return null;
@@ -140,23 +142,15 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
return sysFile;
}
private boolean handleCachedImageRequest(Sys_file sysFile, HttpServletRequest request, HttpServletResponse response) {
if (!isImage(sysFile)) {
return false;
}
String etag = buildFileEtag(sysFile);
if (etag.equals(request.getHeader("If-None-Match"))) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
setImageCacheHeaders(response, etag);
return true;
}
return false;
}
private boolean writeImageResponse(Sys_file sysFile, byte[] bytes, HttpServletResponse response) throws IOException {
if (!isImage(sysFile)) {
return false;
}
String suffix = StrUtil.blankToDefault(sysFile.getSuffix(), FileUtil.extName(sysFile.getName())).toLowerCase(Locale.ROOT);
if (!isValidImage(bytes, suffix)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "图片内容与文件类型不符");
return true;
}
setImageCacheHeaders(response, buildFileEtag(sysFile));
response.setHeader("Content-Disposition", "inline;filename=" + java.net.URLEncoder.encode(sysFile.getName(), java.nio.charset.StandardCharsets.UTF_8));
response.setHeader("Content-Length", String.valueOf(bytes.length));
@@ -166,9 +160,9 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
}
private void setImageCacheHeaders(HttpServletResponse response, String etag) {
response.setHeader("Cache-Control", "public, max-age=604800, immutable");
response.setHeader("Cache-Control", "private, no-cache");
response.setHeader("ETag", etag);
response.setDateHeader("Expires", System.currentTimeMillis() + 604800000L);
response.setDateHeader("Expires", 0);
}
private String buildFileEtag(Sys_file sysFile) {
@@ -176,12 +170,12 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
}
private String buildFileEtag(String id) {
return "\"" + id + "\"";
return "\"safe-" + id + "\"";
}
private boolean isImage(Sys_file sysFile) {
String suffix = StrUtil.blankToDefault(sysFile.getSuffix(), FileUtil.extName(sysFile.getName())).toLowerCase();
return "jpg".equals(suffix) || "jpeg".equals(suffix) || "png".equals(suffix) || "gif".equals(suffix) || "webp".equals(suffix) || "bmp".equals(suffix) || "svg".equals(suffix);
return "jpg".equals(suffix) || "jpeg".equals(suffix) || "png".equals(suffix) || "gif".equals(suffix) || "webp".equals(suffix) || "bmp".equals(suffix);
}
private String getImageContentType(Sys_file sysFile) {
@@ -192,7 +186,6 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
case "gif" -> "image/gif";
case "webp" -> "image/webp";
case "bmp" -> "image/bmp";
case "svg" -> "image/svg+xml";
default -> "application/octet-stream";
};
}
@@ -305,6 +298,7 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
* 存储文件
**/
private String storageFile(String engine, TempFile file, boolean returnFileId) {
validateUpload(file);
// 如果引擎为空,默认使用本地
if (ObjectUtil.isEmpty(engine)) {
engine = SysFileEngineTypeEnum.LOCAL.getValue();
@@ -378,6 +372,53 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
}
}
/** 所有业务上传共用校验,客户端声明的 MIME 类型不能作为图片真实性依据。 */
private void validateUpload(TempFile file) {
if (file == null || StrUtil.isBlank(file.getSubmittedFileName())) {
throw new BaseException("请选择要上传的文件");
}
String suffix = FileUtil.extName(file.getSubmittedFileName()).toLowerCase(Locale.ROOT);
if (!UPLOAD_SUFFIXES.contains(suffix)) {
throw new BaseException("不允许上传此类型的文件");
}
if (Set.of("gif", "jpg", "jpeg", "png").contains(suffix)) {
try {
if (!isValidImage(Files.readAllBytes(file.getFile().toPath()), suffix)) {
throw new BaseException("图片内容与文件类型不符");
}
} catch (IOException e) {
throw new BaseException("图片读取失败");
}
}
}
/** 解码真实图片并限制像素数量,阻止伪装为图片的 SVG 和异常大尺寸图片。 */
private boolean isValidImage(byte[] bytes, String suffix) {
try (ImageInputStream input = ImageIO.createImageInputStream(new ByteArrayInputStream(bytes))) {
if (input == null) {
return false;
}
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
return false;
}
ImageReader reader = readers.next();
try {
reader.setInput(input, true, true);
String expected = "jpg".equals(suffix) ? "jpeg" : suffix;
if (!expected.equalsIgnoreCase(reader.getFormatName())) {
return false;
}
long pixels = (long) reader.getWidth(0) * reader.getHeight(0);
return pixels > 0 && pixels <= 40000000 && reader.read(0) != null;
} finally {
reader.dispose();
}
} catch (IOException | RuntimeException e) {
return false;
}
}
/**
* 存储文件
*/
@@ -10,6 +10,9 @@ import cn.dev33.satoken.servlet.model.SaStorageForServlet;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.Mvcs;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* @author wizzer@qq.com
*/
@@ -22,7 +25,20 @@ public class SaTokenContextImpl implements SaTokenContext {
@Override
public SaResponse getResponse() {
return new SaResponseForServlet(Mvcs.getResp());
// 当前 Sa-Token 版本没有 Cookie 安全属性配置,在写入登录 Cookie 时统一补齐。
return new SaResponseForServlet(new HttpServletResponseWrapper(Mvcs.getResp()) {
/**
* 限制登录 Cookie 的脚本访问,并在 HTTPS 请求中仅允许安全传输。
*
* @param cookie Sa-Token 写入或清除的 Cookie
*/
@Override
public void addCookie(Cookie cookie) {
cookie.setHttpOnly(true);
cookie.setSecure(cookie.getSecure() || Mvcs.getReq().isSecure());
super.addCookie(cookie);
}
});
}
@Override
@@ -54,7 +54,7 @@ import java.util.List;
@IocBean
@At("/platform/unionReimburse/apply")
@Api("工会报销申请")
@Ok("json:full")
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
@Slf4j
public class UnionReimburseApplyController {
@@ -151,14 +151,11 @@ public class UnionReimburseApplyController {
username as userName,
loginname as loginName,
sex,
mobile,
technicalTitle,
IFNULL(unitname, '暂无') as unitName,
unitid as unitId,
unionid as unionId,
unionname as unionName,
DATE(birthday) AS birthday,
idCard,
unionCode
from
vw_user
@@ -173,6 +170,9 @@ public class UnionReimburseApplyController {
}
cnd.and(View_user::getId, "!=", SecurityUtil.getUserId());
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
if (StrUtil.isBlank(SecurityUtil.getUnionId())) {
return Result.success(java.util.Collections.emptyList());
}
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
}
sql.setCondition(cnd);
@@ -34,7 +34,7 @@ import java.util.List;
*/
@IocBean
@At("/platform/unionReimburse/certifierUserSign")
@Ok("json:full")
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
@Api("证明人签字")
@Slf4j
public class UnionReimburseCertifierUserSignController {
@@ -68,7 +68,7 @@ import java.util.HashMap;
@IocBean
@At("/platform/unionReimburse/mine")
@Api("工会报销我的")
@Ok("json:full")
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
@Slf4j
public class UnionReimburseMineController {
@@ -29,7 +29,7 @@ import java.util.List;
@IocBean
@At("/platform/unionReimburse/review")
@Ok("json:full")
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
@Api("工会报销审核")
public class UnionReimburseReviewController {
@@ -38,7 +38,7 @@ import java.util.List;
@IocBean
@At("/platform/unionReimburse/statistics")
@Ok("json:full")
@Ok("json:{locked:'condolenceIdCard|condolenceBirthday|condolenceMobile',ignoreNull:false}")
@Api("慰问统计")
@Slf4j
public class UnionReimburseStatisticsController {
@@ -4,10 +4,12 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
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.result.Result;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_file;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.sys.services.SysFileService;
import com.budwk.app.zhgh.club.model.ClubUser;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
@@ -125,6 +127,37 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
if (oldRecord == null) {
return Result.error("未找到对应的报销记录");
}
if (!isAdmin() && !Objects.equals(oldRecord.getUserId(), SecurityUtil.getUserId())) {
return Result.error("无权修改此报销申请");
}
}
// 人员身份和敏感资料由服务端补齐,避免前端删减字段导致丢失或被篡改。
if (oldRecord == null) {
View_user handler = dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
if (handler == null) {
return Result.error("经办人信息不存在");
}
unionReimburse.setUserId(handler.getId());
unionReimburse.setUserName(handler.getUsername());
unionReimburse.setLoginName(handler.getLoginname());
unionReimburse.setUnitId(handler.getUnitId());
unionReimburse.setUnitName(handler.getUnitName());
unionReimburse.setUnionId(handler.getUnionId());
unionReimburse.setUnionName(handler.getUnionName());
} else {
// 修改历史单据时保留原经办组织,避免人员调动改变经费归属。
unionReimburse.setUserId(oldRecord.getUserId());
unionReimburse.setUserName(oldRecord.getUserName());
unionReimburse.setLoginName(oldRecord.getLoginName());
unionReimburse.setUnitId(oldRecord.getUnitId());
unionReimburse.setUnitName(oldRecord.getUnitName());
unionReimburse.setUnionId(oldRecord.getUnionId());
unionReimburse.setUnionName(oldRecord.getUnionName());
}
Result recipientResult = fillCondolencePersonalData(unionReimburse, oldRecord);
if (recipientResult != null) {
return recipientResult;
}
normalizeInvoiceDetails(unionReimburse);
@@ -179,14 +212,104 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
if (unionReimburse == null) {
return null;
}
if (!canReadApply(unionReimburse)) {
throw new BaseException("无权查看此报销申请");
}
List<UnionReimburseInvoiceDetail> invoiceDetails = this.dao().query(
UnionReimburseInvoiceDetail.class,
Cnd.where("reimburseId", "=", id).asc("createdAt")
);
unionReimburse.setInvoiceDetails(invoiceDetails == null ? new ArrayList<>() : invoiceDetails);
// 申请页和详情页共用此接口,原始身份资料仅保留在服务端业务记录中。
unionReimburse.setCondolenceIdCard(null);
unionReimburse.setCondolenceBirthday(null);
unionReimburse.setCondolenceMobile(null);
return unionReimburse;
}
/** 保留历史单据的身份资料快照,新选人员须符合人员查询的数据范围。 */
private Result fillCondolencePersonalData(UnionReimburse form, UnionReimburse oldRecord) {
form.setCondolenceIdCard(null);
form.setCondolenceBirthday(null);
form.setCondolenceMobile(null);
if (!"UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject())) {
return null;
}
if (StrUtil.isBlank(form.getCondolenceUserId())) {
return null;
}
if (oldRecord != null && Objects.equals(oldRecord.getCondolenceUserId(), form.getCondolenceUserId())) {
form.setCondolenceIdCard(oldRecord.getCondolenceIdCard());
form.setCondolenceBirthday(oldRecord.getCondolenceBirthday());
form.setCondolenceMobile(oldRecord.getCondolenceMobile());
form.setCondolenceUnionId(oldRecord.getCondolenceUnionId());
form.setCondolenceUnitId(oldRecord.getCondolenceUnitId());
form.setCondolenceUserName(oldRecord.getCondolenceUserName());
form.setCondolenceLoginName(oldRecord.getCondolenceLoginName());
form.setCondolenceUnitName(oldRecord.getCondolenceUnitName());
form.setCondolenceUnionName(oldRecord.getCondolenceUnionName());
form.setCondolenceUnionCode(oldRecord.getCondolenceUnionCode());
form.setCondolenceSex(oldRecord.getCondolenceSex());
return null;
}
View_user user = dao().fetch(View_user.class, Cnd.where("id", "=", form.getCondolenceUserId()));
if (user == null || Objects.equals(user.getId(), SecurityUtil.getUserId())
|| (!isAdmin() && (StrUtil.isBlank(SecurityUtil.getUnionId())
|| !Objects.equals(SecurityUtil.getUnionId(), user.getUnionId())))) {
return Result.error("无权选择此慰问对象");
}
form.setCondolenceUserName(user.getUsername());
form.setCondolenceLoginName(user.getLoginname());
form.setCondolenceUnitId(user.getUnitId());
form.setCondolenceUnitName(user.getUnitName());
form.setCondolenceUnionId(user.getUnionId());
form.setCondolenceUnionName(user.getUnionName());
form.setCondolenceUnionCode(user.getUnionCode());
form.setCondolenceSex(user.getSex());
form.setCondolenceIdCard(user.getIdCard());
form.setCondolenceBirthday(user.getBirthday() == null ? null : DateUtil.formatDate(user.getBirthday()));
form.setCondolenceMobile(user.getMobile());
return null;
}
/** 详情访问沿用经办、审核、统计和流程参与范围,不能仅凭单据 ID 查询。 */
private boolean canReadApply(UnionReimburse form) {
String userId = SecurityUtil.getUserId();
if (StrUtil.isBlank(userId)) {
return false;
}
if (isAdmin() || userId.equals(form.getUserId())) {
return true;
}
if (form.getStateId() != 1 && (AuthUtil.hasPermission("unionReimburse.review")
|| AuthUtil.hasPermission("h5.unionReimburse.review"))) {
return true;
}
if ((form.getStateId() == 2 || form.getStateId() == 3)
&& StrUtil.isNotBlank(SecurityUtil.getUnionId())
&& Objects.equals(SecurityUtil.getUnionId(), form.getCondolenceUnionId())
&& (AuthUtil.hasPermission("unionReimburse.statistics")
|| AuthUtil.hasPermission("h5.unionReimburse.statistics"))) {
return true;
}
// 旧流程单据允许任务参与人和抄送人回看,关系必须由数据库确认。
Sql sql = Sqls.create("""
SELECT COUNT(*) FROM wf_process_instance i
WHERE i.businessNo = @id AND (
EXISTS (SELECT 1 FROM wf_process_task t
INNER JOIN wf_process_task_actor a ON a.processTaskId = t.id
WHERE t.processInstanceId = i.id AND a.actorId = @userId)
OR EXISTS (SELECT 1 FROM wf_process_cc_instance c
WHERE c.processInstanceId = i.id AND c.actorId = @userId)
)
""");
sql.setParam("id", form.getId());
sql.setParam("userId", userId);
sql.setCallback(Sqls.callback.integer());
dao().execute(sql);
return sql.getInt() > 0;
}
@Override
public void deleteApply(String id) {
if (StrUtil.isBlank(id)) {
@@ -242,14 +365,11 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
u.username AS userName,
u.loginname AS loginName,
u.sex,
u.mobile,
u.technicalTitle,
IFNULL(u.unitname, '暂无') AS unitName,
u.unitid AS unitId,
u.unionid AS unionId,
u.unionname AS unionName,
DATE(u.birthday) AS birthday,
u.idCard,
u.unionCode
FROM
vw_user u
@@ -127,7 +127,6 @@ public class CondolenceApplyController {
username as userName,
loginname as loginName,
sex,
mobile,
technicalTitle,
IFNULL(unitname, '暂无') as unitName,
unitid as unitId,
@@ -146,6 +145,9 @@ public class CondolenceApplyController {
}
if(!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
if(AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
if (StrUtil.isBlank(SecurityUtil.getUnionId())) {
return Result.success(java.util.Collections.emptyList());
}
cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId());
} else {
cnd.and(View_user::getId, "=", SecurityUtil.getUserId());
+2 -2
View File
@@ -14,7 +14,7 @@ var ioc={
// 单个文件最大尺寸(大约的值,单位为字节,即 2097152 为 2M)
maxFileSize : 2097152,
// 正则表达式匹配可以支持的文件名
nameFilter : '^(.+[.])(gif|jpg|png|svg)$'
nameFilter : '(?i)^(.+[.])(gif|jpg|jpeg|png)$'
}
},
imageUpload : {
@@ -32,7 +32,7 @@ var ioc={
// 单个文件最大尺寸(大约的值,单位为字节,即 209715200 为 200M)
maxFileSize : 209715200,
// 正则表达式匹配可以支持的文件名
nameFilter : '^(.+[.])(gif||svg|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|mp4)$'
nameFilter : '(?i)^(.+[.])(gif|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|mp4)$'
}
},
fileUpload : {
@@ -187,31 +187,6 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="生日"
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<el-form-item prop="condolenceBirthday" label="生日">
<el-input
:value="formData.condolenceBirthday && $moment(formData.condolenceBirthday).isValid() ?$moment(formData.condolenceBirthday).format('YYYY-MM-DD') : ''"
show-word-limit placeholder="请输入生日" disabled></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="身份证号"
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<el-form-item prop="condolenceIdCard" label="身份证号">
<el-input v-model="formData.condolenceIdCard" show-word-limit
placeholder="请输入身份证号" disabled></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="联系方式"
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<el-form-item prop="condolenceMobile" label="联系方式">
<el-input v-model="formData.condolenceMobile" show-word-limit
placeholder="请输入联系方式" disabled></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="慰问类型"
v-if="formData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<el-form-item label="慰问类型" prop="condolenceTypeId">
@@ -816,7 +791,6 @@ layout("/layouts/platform.html"){
trigger: ["change", "blur"]
}, {pattern: /^([1-9]{1})(\d{15}|\d{18})$/, message: "请输入正确的银行卡号", trigger: "blur"}],
bankOfDeposit: [{required: true, message: "请填写开户行", trigger: ["change", "blur"]}],
condolenceMobile: [{pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur"}],
condolenceTime: [{required: true, message: "请选择慰问时间", trigger: ["change", "blur"]}],
// invoiceNumber: [{required: true, message: "请填写发票张数", trigger: ["change", "blur"]}],
// invoice: [{required: true, message: "请填写发票号码", trigger: ["change", "blur"]}],
@@ -1187,10 +1161,7 @@ layout("/layouts/platform.html"){
unionId,
unionName,
unionCode,
sex,
birthday,
idCard,
mobile
sex
} = user
this.$set(this.formData, "condolenceUserName", userName)
this.$set(this.formData, "condolenceLoginName", loginName)
@@ -1200,9 +1171,6 @@ layout("/layouts/platform.html"){
this.$set(this.formData, "condolenceUnionName", unionName)
this.$set(this.formData, "condolenceUnionCode", unionCode)
this.$set(this.formData, "condolenceSex", sex)
this.$set(this.formData, "condolenceBirthday", birthday)
this.$set(this.formData, "condolenceIdCard", idCard)
this.$set(this.formData, "condolenceMobile", mobile)
}
},
typeChange(val) {
@@ -86,18 +86,6 @@ const unionReimburseInfo = {
<span>{{ viewData.condolenceSex }}</span>
</el-descriptions-item>
<el-descriptions-item label="生日" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<span>{{ viewData.condolenceBirthday && $moment(viewData.condolenceBirthday).isValid() ? $moment(viewData.condolenceBirthday).format('YYYY-MM-DD') : '' }}</span>
</el-descriptions-item>
<el-descriptions-item label="身份证号" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<span>{{ viewData.condolenceIdCard }}</span>
</el-descriptions-item>
<el-descriptions-item label="联系方式" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<span>{{ viewData.condolenceMobile }}</span>
</el-descriptions-item>
<el-descriptions-item label="慰问类型" v-if="viewData.reimburseProject === 'UNION_REIMBURSE_PROJECT_1'">
<span>{{ viewData.typeName }}</span>
</el-descriptions-item>
@@ -579,9 +579,6 @@ layout("/layouts/platform_h5.html"){
</van-action-sheet>
<van-field label="性别" v-model="formData.condolenceSex" readonly placeholder="自动带出"></van-field>
<van-field label="生日" :value="formatDateText(formData.condolenceBirthday)" readonly placeholder="自动带出"></van-field>
<van-field label="身份证号" v-model="formData.condolenceIdCard" readonly placeholder="自动带出"></van-field>
<van-field label="联系方式" v-model="formData.condolenceMobile" readonly placeholder="自动带出"></van-field>
<van-field
v-model="formData.typeName"
@@ -1412,7 +1409,7 @@ layout("/layouts/platform_h5.html"){
},
normalizeContactFields() {
// 提交前裁剪联系方式空格,避免系统手机号带空格时后端格式校验失败。
const fields = ["mobile", "condolenceMobile"]
const fields = ["mobile"]
fields.forEach((fieldName) => {
if (this.formData[fieldName]) {
this.$set(this.formData, fieldName, String(this.formData[fieldName]).trim())
@@ -2209,7 +2206,6 @@ layout("/layouts/platform_h5.html"){
const selectedUser = this.condolenceOptions.find(user => user.id === o.value)
if (selectedUser) {
this.fillCondolenceUser(selectedUser)
this.$set(this.formData, "condolenceMobile", selectedUser.mobile)
}
},
onCondolenceTypeConfirm(o) {
@@ -2341,9 +2337,6 @@ layout("/layouts/platform_h5.html"){
this.$set(this.formData, "condolenceUnionName", user.unionName)
this.$set(this.formData, "condolenceUnionCode", user.unionCode)
this.$set(this.formData, "condolenceSex", user.sex)
this.$set(this.formData, "condolenceBirthday", user.birthday)
this.$set(this.formData, "condolenceIdCard", user.idCard)
this.$set(this.formData, "condolenceMobile", user.mobile)
},
// 查询慰问类型
queryCondolenceType() {
@@ -100,18 +100,6 @@ const UNION_REIMBURSE_INFO = {
<div class="union-info-label">性别</div>
<div class="union-info-value">{{ viewData.condolenceSex }}</div>
</div>
<div class="union-info-row">
<div class="union-info-label">生日</div>
<div class="union-info-value">{{ formatDateText(viewData.condolenceBirthday) }}</div>
</div>
<div class="union-info-row">
<div class="union-info-label">身份证号</div>
<div class="union-info-value">{{ viewData.condolenceIdCard }}</div>
</div>
<div class="union-info-row">
<div class="union-info-label">联系方式</div>
<div class="union-info-value">{{ viewData.condolenceMobile }}</div>
</div>
<div class="union-info-row">
<div class="union-info-label">慰问类型</div>
<div class="union-info-value">{{ viewData.typeName || viewData.condolenceTypeName }}</div>