From b2dd2681a2698ac18af55876e8174e0e022c8e6c Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Tue, 26 May 2026 16:39:30 +0800 Subject: [PATCH] commit --- ...osalBranchLeaderSuffixAuditController.java | 145 ++++ .../proposal/MProposalCaseController.java | 116 +++ .../proposal/MProposalCaseReadController.java | 68 ++ .../proposal/MProposalCaseUnitController.java | 155 ++++ .../MProposalDelegationController.java | 13 +- .../MProposalFeedbackScoreController.java | 92 +++ .../constants/ProposalToDoHandler.java | 4 +- .../transact/ProposalCaseController.java | 26 +- .../transact/ProposalCaseReadController.java | 114 +-- .../services/ProposalCaseReadService.java | 28 + .../services/ProposalCaseService.java | 8 + .../services/ProposalFeedBackService.java | 29 + .../impl/ProposalCaseReadServiceImpl.java | 119 +++ .../impl/ProposalCaseServiceImpl.java | 25 + .../impl/ProposalFeedBackServiceImpl.java | 148 ++++ .../static/assets/mobile/css/list-card.css | 161 ++++ .../assets/mobile/css/universal-info.css | 448 +++++++++++ .../proposal/mobile/ProposalInfo.vue | 720 ++++++++++++------ src/main/resources/views/mobile/platform.html | 2 + .../branchLeaderSuffixAudit/audit.html | 152 ++++ .../branchLeaderSuffixAudit/list.html | 196 +++++ .../views/mobile/proposal/case/audit.html | 511 +++++++++++++ .../views/mobile/proposal/case/list.html | 193 +++++ .../views/mobile/proposal/caseRead/audit.html | 147 ++++ .../views/mobile/proposal/caseRead/list.html | 184 +++++ .../views/mobile/proposal/caseUnit/audit.html | 515 +++++++++++++ .../views/mobile/proposal/caseUnit/list.html | 193 +++++ .../mobile/proposal/delegation/audit.html | 186 +++-- .../mobile/proposal/delegation/list.html | 251 +++--- .../views/mobile/proposal/feedback/audit.html | 189 +++++ .../views/mobile/proposal/feedback/list.html | 195 +++++ .../views/mobile/proposal/seconded/audit.html | 94 ++- .../views/mobile/proposal/seconded/list.html | 269 +++---- .../welfare/project/ProjectMange.html | 147 +++- 34 files changed, 5058 insertions(+), 785 deletions(-) create mode 100644 src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalBranchLeaderSuffixAuditController.java create mode 100644 src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseController.java create mode 100644 src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseReadController.java create mode 100644 src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseUnitController.java create mode 100644 src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalFeedbackScoreController.java create mode 100644 src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseReadService.java create mode 100644 src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseReadServiceImpl.java create mode 100644 src/main/resources/static/assets/mobile/css/list-card.css create mode 100644 src/main/resources/static/assets/mobile/css/universal-info.css create mode 100644 src/main/resources/views/mobile/proposal/branchLeaderSuffixAudit/audit.html create mode 100644 src/main/resources/views/mobile/proposal/branchLeaderSuffixAudit/list.html create mode 100644 src/main/resources/views/mobile/proposal/case/audit.html create mode 100644 src/main/resources/views/mobile/proposal/case/list.html create mode 100644 src/main/resources/views/mobile/proposal/caseRead/audit.html create mode 100644 src/main/resources/views/mobile/proposal/caseRead/list.html create mode 100644 src/main/resources/views/mobile/proposal/caseUnit/audit.html create mode 100644 src/main/resources/views/mobile/proposal/caseUnit/list.html create mode 100644 src/main/resources/views/mobile/proposal/feedback/audit.html create mode 100644 src/main/resources/views/mobile/proposal/feedback/list.html diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalBranchLeaderSuffixAuditController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalBranchLeaderSuffixAuditController.java new file mode 100644 index 0000000..fad59a0 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalBranchLeaderSuffixAuditController.java @@ -0,0 +1,145 @@ +package io.v.nutz.zhgh.mobile.proposal; + +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.sys.models.Sys_signature; +import io.v.nutz.sys.services.SysSignatureService; +import io.v.nutz.web.commons.utils.ShiroUtil; +import io.v.nutz.zhgh.proposal.constants.ProposalState; +import io.v.nutz.zhgh.proposal.constants.ProposalToDoHandler; +import io.v.nutz.zhgh.proposal.models.ProposalAudit; +import io.v.nutz.zhgh.proposal.models.ProposalBranchLeaderSuffixAudit; +import io.v.nutz.zhgh.proposal.models.ProposalInfo; +import io.v.nutz.zhgh.proposal.models.ProposalReply; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.models.ProposalUndertake; +import io.v.nutz.zhgh.proposal.services.ProposalBranchLeaderSuffixAuditService; +import io.v.nutz.zhgh.proposal.services.ProposalInfoService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +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; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +/** + * 手机端分管校领导审批承办答复。 + */ +@IocBean +@Ok("json:full") +@At("/mobile/proposal/branchLeaderSuffixAudit") +public class MProposalBranchLeaderSuffixAuditController { + + @Inject + private Dao dao; + + @Inject + private ProposalInfoService proposalInfoService; + + @Inject + private ProposalBranchLeaderSuffixAuditService proposalBranchLeaderSuffixAuditService; + + @Inject + private SysSignatureService sysSignatureService; + + @At("") + @Ok("beetl:/mobile/proposal/branchLeaderSuffixAudit/list.html") + @RequiresPermissions("proposal.transact.branchLeaderSuffixAudit") + public void index() { + } + + @At("/view") + @Ok("beetl:/mobile/proposal/branchLeaderSuffixAudit/audit.html") + @RequiresPermissions("proposal.transact.branchLeaderSuffixAudit") + public void view() { + } + + /** + * 手机端分管校领导审批列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、排序字段等; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已完成分管校领导审批; + * 返回值:分页结果,list 中包含提案基础信息、承办单位、审批状态、pblsaId 等字段。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.branchLeaderSuffixAudit") + public Object pageData(PageForm page, ProposalSearch search) { + Cnd cnd = Cnd.NEW(); + proposalInfoService.sortAndSearch(cnd, page, search); + return proposalBranchLeaderSuffixAuditService.pageData(page, cnd, search); + } + + /** + * 查询手机端分管校领导审批办理信息。 + * 参数说明: + * pblsaId:proposal_branch_leader_suffix_audit 表主键; + * 返回值:NutMap,包含 proposalId、underTakeId、underTakeNames、stateCode、isAudit,用于详情页判断是否显示审核表单。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.branchLeaderSuffixAudit") + public Object getAuditInfo(@Param("pblsaId") String pblsaId) { + ProposalBranchLeaderSuffixAudit suffixAudit = dao.fetch(ProposalBranchLeaderSuffixAudit.class, pblsaId); + if (suffixAudit == null) { + return NutMap.NEW(); + } + + ProposalInfo proposalInfo = dao.fetch(ProposalInfo.class, suffixAudit.getProposalId()); + ProposalUndertake undertake = dao.fetch(ProposalUndertake.class, suffixAudit.getUnderTakeId()); + ProposalReply reply = dao.fetch(ProposalReply.class, + Cnd.where("proposalId", "=", suffixAudit.getProposalId()) + .and("replyUnitId", "=", suffixAudit.getUnderTakeId())); + + return NutMap.NEW() + .addv("pblsaId", suffixAudit.getId()) + .addv("proposalId", suffixAudit.getProposalId()) + .addv("underTakeId", suffixAudit.getUnderTakeId()) + .addv("underTakeNames", undertake == null ? "" : undertake.getUnitName()) + .addv("undertakeType", reply == null ? "" : reply.getUndertakeType()) + .addv("stateCode", proposalInfo == null ? null : proposalInfo.getStateCode()) + .addv("isAudit", suffixAudit.getIsAudit()); + } + + /** + * 手机端提交分管校领导审批。 + * 参数说明: + * sign:签名图片数据,可为空,传入后保存为签名记录并写入审核记录 signId; + * audit:ProposalAudit 审核对象,需包含 proposalId、username、loginName、auditTime、opinion 等字段; + * pblsaId:proposal_branch_leader_suffix_audit 表主键,用于定位本次分管校领导审批记录; + * 返回值:无业务数据,成功时由 ViReturn 包装为成功响应。 + */ + @At + @POST + @ViReturn + @RequiresPermissions("proposal.transact.branchLeaderSuffixAudit") + @Aop(TransAop.READ_COMMITTED) + public Object doAudit(@Param(value = "sign", required = false) String sign, + @Param("audit") ProposalAudit audit, + @Param("pblsaId") String pblsaId) { + String signId = null; + if (StrUtil.isNotBlank(sign)) { + signId = sysSignatureService.insert(new Sys_signature(sign)).getId(); + } + audit.setUserId(ShiroUtil.getUserId()); + audit.setSignId(signId); + dao.insert(audit); + + dao.update(ProposalBranchLeaderSuffixAudit.class, + Chain.make("isAudit", true).add("auditId", audit.getId()), + Cnd.where("id", "=", pblsaId)); + + dao.update(ProposalInfo.class, Chain.make("stateCode", ProposalState.FEEDBACKSCORE), Cnd.where("id", "=", audit.getProposalId())); + ProposalToDoHandler.COMPLETE_BRANCH_LEADER_SUFFIX_AUDIT_TASK.exec(audit.getProposalId(), NutMap.NEW().addv("pblsaId", pblsaId).addv("opinion", audit.getOpinion())); + ProposalToDoHandler.CREATE_FEEDBACK_TASK.exec(audit.getProposalId(), null); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseController.java new file mode 100644 index 0000000..883ae09 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseController.java @@ -0,0 +1,116 @@ +package io.v.nutz.zhgh.mobile.proposal; + +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.sys.models.Sys_signature; +import io.v.nutz.zhgh.proposal.models.ProposalAudit; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalCaseService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Dao; +import org.nutz.ioc.aop.Aop; +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 org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 手机端提案工作组预立案审核。 + */ +@IocBean +@Ok("json:full") +@At("/mobile/proposal/case") +public class MProposalCaseController { + + @Inject + private ProposalCaseService proposalCaseService; + + @Inject + private Dao dao; + + @At("") + @Ok("beetl:/mobile/proposal/case/list.html") + @RequiresPermissions("proposal.transact.case") + public void index() { + } + + @At("/view") + @Ok("beetl:/mobile/proposal/case/audit.html") + @RequiresPermissions("proposal.transact.case") + public void view() { + } + + /** + * 手机端预立案审核列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、排序字段等; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已完成预立案审核; + * 返回值:分页结果,list 中包含提案基础信息、状态、类型、届次、代表团、立案结果等字段。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.case") + public Object pageData(PageForm page, ProposalSearch search) { + return proposalCaseService.pageData(page, search); + } + + /** + * 手机端承办单位下拉数据。 + * 参数说明:无; + * 返回值:承办单位列表,每项包含 id、unitName、unitCode、leader 等字段,leader 为该承办单位对应领导姓名。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.case") + public Object getUnderTakeAndLeader() { + return proposalCaseService.getUnderTakeAndLeader(); + } + + /** + * 手机端提交预立案审核。 + * 参数说明: + * audit:ProposalAudit 审核对象,包含 username、loginName、auditTime、opinion 等审核字段; + * resultCode:立案结果,取 proposal_result 字典 code,如 determine、opinion、notGive; + * determineTypeCode:立案类型,可为空; + * typeId:提案类型 id; + * hostUnit:主办单位 id; + * helpUnit:协办单位 id 数组,可为空; + * sign:签名图片数据,可为空; + * proposalIds:审核提案 id 数组,手机端单条审核时传当前提案 id; + * auditType:审核类型,手机端单条审核传 one; + * flag:审核动作,1 为提交通过,2 为退回修改; + * 返回值:无业务数据,成功时由 ViReturn 包装为成功响应。 + */ + @At("/audit") + @POST + @ViReturn + @RequiresPermissions("proposal.transact.case") + @Aop(TransAop.READ_COMMITTED) + public Object audit(@Param("audit") ProposalAudit audit, + @Param("resultCode") String resultCode, + @Param(value = "determineTypeCode", required = false) String determineTypeCode, + @Param("typeId") String typeId, + @Param("hostUnit") String hostUnit, + @Param(value = "helpUnit", required = false) String[] helpUnit, + @Param(value = "sign", required = false) String sign, + @Param("proposalIds") String[] proposalIds, + @Param("auditType") String auditType, + @Param("flag") int flag) { + if (StrUtil.isNotBlank(sign)) { + String signId = dao.insert(new Sys_signature(sign)).getId(); + audit.setSignId(signId); + } + audit.setFlag(flag == 1); + List helpUnitList = helpUnit == null ? Collections.emptyList() : Arrays.asList(helpUnit); + proposalCaseService.audit(Arrays.asList(proposalIds), resultCode, determineTypeCode, hostUnit, helpUnitList, typeId, audit, auditType, flag); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseReadController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseReadController.java new file mode 100644 index 0000000..4194adb --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseReadController.java @@ -0,0 +1,68 @@ +package io.v.nutz.zhgh.mobile.proposal; + +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalCaseReadService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +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 org.nutz.mvc.annotation.Param; + +/** + * 手机端提案工作组成员意见审核。 + */ +@IocBean +@Ok("json:full") +@At("/mobile/proposal/caseRead") +public class MProposalCaseReadController { + + @Inject + private ProposalCaseReadService proposalCaseReadService; + + @At("") + @Ok("beetl:/mobile/proposal/caseRead/list.html") + @RequiresPermissions("proposal.transact.caseRead") + public void index() { + } + + @At("/view") + @Ok("beetl:/mobile/proposal/caseRead/audit.html") + @RequiresPermissions("proposal.transact.caseRead") + public void view() { + } + + /** + * 手机端审核列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、排序字段等; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已提交成员意见; + * 返回值:分页结果,list 中包含提案基础信息、状态、类型、届次、代表团、立案结果以及当前用户是否已发表意见 isOpinion。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseRead") + public Object pageData(PageForm page, ProposalSearch search) { + return proposalCaseReadService.pageData(page, search); + } + + /** + * 提交手机端成员意见。 + * 参数说明: + * sign:签名图片数据,保存后写入审核记录 signId; + * audit:ProposalAudit 的 JSON 字符串,需包含 proposalId、username、loginName、auditTime、opinion、other、determineTypeCode 等审核字段; + * proposalId:被审核的提案 id; + * 返回值:无业务数据,成功时由 ViReturn 包装为成功响应。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseRead") + public Object putSuggestion(@Param(value = "sign", required = false) String sign, + @Param("audit") String audit, + @Param("proposalId") String proposalId) { + proposalCaseReadService.putSuggestion(sign, audit, proposalId); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseUnitController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseUnitController.java new file mode 100644 index 0000000..9cbe5be --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalCaseUnitController.java @@ -0,0 +1,155 @@ +package io.v.nutz.zhgh.mobile.proposal; + +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.base.utils.Roles; +import io.v.nutz.sys.models.Sys_signature; +import io.v.nutz.sys.models.Sys_user_role; +import io.v.nutz.zhgh.proposal.models.ProposalAudit; +import io.v.nutz.zhgh.proposal.models.ProposalReply; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalCaseConfirmService; +import io.v.nutz.zhgh.proposal.services.ProposalCaseService; +import io.v.nutz.zhgh.proposal.services.ProposalInfoService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.FieldFilter; +import org.nutz.dao.util.Daos; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 手机端提案工作组正式立案审核。 + */ +@IocBean +@Ok("json:full") +@At("/mobile/proposal/caseUnit") +public class MProposalCaseUnitController { + + @Inject + private ProposalCaseConfirmService proposalCaseConfirmService; + + @Inject + private ProposalCaseService proposalCaseService; + + @Inject + private ProposalInfoService proposalInfoService; + + @Inject + private Dao dao; + + @At("") + @Ok("beetl:/mobile/proposal/caseUnit/list.html") + @RequiresPermissions("proposal.transact.caseUnit") + public void index() { + } + + @At("/view") + @Ok("beetl:/mobile/proposal/caseUnit/audit.html") + @RequiresPermissions("proposal.transact.caseUnit") + public void view() { + } + + /** + * 手机端正式立案审核列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、排序字段等; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已完成正式立案审核; + * 返回值:分页结果,list 中包含提案基础信息、状态、类型、届次、代表团、立案结果、审核状态等字段。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseUnit") + public Object pageData(PageForm page, ProposalSearch search) { + return proposalCaseConfirmService.pageData(page, search); + } + + /** + * 手机端承办单位下拉数据。 + * 参数说明:无; + * 返回值:承办单位列表,每项包含 id、unitName、unitCode、leader 等字段,leader 为该承办单位对应领导姓名。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseUnit") + public Object getUnderTakeAndLeader() { + return proposalCaseService.getUnderTakeAndLeader(); + } + + /** + * 查询该提案当前已分配的承办单位。 + * 参数说明: + * proposalId:提案 id; + * 返回值:承办单位列表,每项包含 replyUnitId 与 undertakeType,undertakeType 为 1 表示主办、2 表示协办。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseUnit") + public Object queryUnderTakeByProposalId(String proposalId) { + FieldFilter ff = FieldFilter.create(ProposalReply.class, "^replyUnitId|undertakeType$"); + return Daos.ext(proposalInfoService.dao(), ff).query(ProposalReply.class, Cnd.where("proposalId", "=", proposalId)); + } + + /** + * 检查主办单位是否已配置答复人和分管领导。 + * 参数说明: + * hostUnit:主办单位 id; + * 返回值:boolean,true 表示该单位至少配置了承办人和分管领导角色,可提交正式立案。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.caseUnit") + public Object checkUnderTakeUser(String hostUnit) { + String[] roleIds = Lang.array(Roles.CONTRACTOR_PERSON, Roles.IN_CHARGE_LEADER); + int count = dao.count(Sys_user_role.class, Cnd.where("cbdwid", "=", hostUnit).and("roleId", "in", roleIds)); + return count >= 2; + } + + /** + * 手机端提交正式立案审核。 + * 参数说明: + * audit:ProposalAudit 审核对象,包含 username、loginName、auditTime、opinion 等审核字段; + * resultCode:立案结果,取 proposal_result 字典 code,如 determine、opinion、notGive; + * determineTypeCode:立案类型,可为空; + * typeId:提案类型 id; + * hostUnit:主办单位 id; + * helpUnit:协办单位 id 数组,可为空; + * sign:签名图片数据,可为空; + * proposalIds:审核提案 id 数组,手机端单条审核时传当前提案 id,若存在并案则传并案提案 id 集合; + * 返回值:无业务数据,成功时由 ViReturn 包装为成功响应。 + */ + @At("/audit") + @POST + @ViReturn + @RequiresPermissions("proposal.transact.caseUnit") + @Aop(TransAop.READ_COMMITTED) + public Object audit(@Param("audit") ProposalAudit audit, + @Param("resultCode") String resultCode, + @Param(value = "determineTypeCode", required = false) String determineTypeCode, + @Param(value = "typeId", required = false) String typeId, + @Param("hostUnit") String hostUnit, + @Param(value = "helpUnit", required = false) String[] helpUnit, + @Param(value = "sign", required = false) String sign, + @Param("proposalIds") String[] proposalIds) { + if (StrUtil.isNotBlank(sign)) { + String signId = dao.insert(new Sys_signature(sign)).getId(); + audit.setSignId(signId); + } + List helpUnitList = helpUnit == null ? Collections.emptyList() : Arrays.asList(helpUnit); + proposalCaseConfirmService.audit(Arrays.asList(proposalIds), resultCode, determineTypeCode, hostUnit, helpUnitList, typeId, audit); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalDelegationController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalDelegationController.java index c362a04..02a4dd2 100644 --- a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalDelegationController.java +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalDelegationController.java @@ -2,6 +2,7 @@ package io.v.nutz.zhgh.mobile.proposal; import io.v.nutz.base.annontation.ViReturn; import io.v.nutz.base.utils.Roles; +import io.v.nutz.base.utils.Vi; import io.v.nutz.base.dao.CndPlus; import io.v.nutz.base.query.PageForm; import io.v.nutz.zhgh.proposal.constants.ProposalState; @@ -9,6 +10,7 @@ import io.v.nutz.zhgh.proposal.models.ProposalSearch; import io.v.nutz.zhgh.proposal.services.ProposalInfoService; import io.v.nutz.web.commons.utils.ShiroUtil; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; @@ -53,8 +55,17 @@ public class MProposalDelegationController { if (!ShiroUtil.hasAnyRoles("tamange,sysadmin")) { Sql sql = Sqls.create("SELECT dbtid FROM sys_user_role sur LEFT JOIN proposal_info info ON info.teacherMeetingId = sur.jdhid WHERE sur.roleId = @roleId AND sur.userId = @userId").setParam("roleId", Roles.DBT_TZ).setParam("userId", userId); cnd.and("info.delegationId", "IN", sql); + cnd.and(Cnd.exps("info.delegationUnionId", "=", Vi.getUnionId()).or("info.delegationUnionId", "is", null)); + } + if (Strings.isNotBlank(search.getIsAudit())) { + if (search.getIsAudit().equals("true")) { + cnd.and(Cnd.exps("info.stateCode", ">", ProposalState.DELEGATION)); + } else { + cnd.and("info.stateCode", "=", ProposalState.DELEGATION); + } + } else { + cnd.and(Cnd.exps("info.stateCode", ">=", ProposalState.DELEGATION)); } - cnd.and("info.stateCode", search.getIsAudit().equals("true") ? ">" : "=", ProposalState.DELEGATION); if (Strings.isNotBlank(search.getSearchKeyWord())) { SqlExpressionGroup sqlExpressionGroup = new SqlExpressionGroup(); sqlExpressionGroup.andLike("info.proposalName", search.getSearchKeyWord()); diff --git a/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalFeedbackScoreController.java b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalFeedbackScoreController.java new file mode 100644 index 0000000..1b0b19a --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/mobile/proposal/MProposalFeedbackScoreController.java @@ -0,0 +1,92 @@ +package io.v.nutz.zhgh.mobile.proposal; + +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.zhgh.proposal.models.ProposalFeedback; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalFeedBackService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +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.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +/** + * 手机端提案反馈评价。 + */ +@IocBean +@Ok("json:full") +@At("/mobile/proposal/feedback") +public class MProposalFeedbackScoreController { + + @Inject + private ProposalFeedBackService proposalFeedBackService; + + @At("") + @Ok("beetl:/mobile/proposal/feedback/list.html") + @RequiresPermissions("proposal.transact.feedback") + public void index() { + } + + @At("/view") + @Ok("beetl:/mobile/proposal/feedback/audit.html") + @RequiresPermissions("proposal.transact.feedback") + public void view() { + } + + /** + * 手机端反馈评价列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、searchKeyWord; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已反馈; + * 返回值:分页结果,list 中为 NutMap,用于手机端列表展示和待反馈状态判断。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.feedback") + public Object pageData(PageForm page, ProposalSearch search) { + return proposalFeedBackService.pageData(page, search); + } + + /** + * 查询手机端反馈办理信息。 + * 参数说明: + * proposalId:proposal_info 表主键; + * 返回值:NutMap,包含 proposalId、stateCode、handle,用于详情页判断是否显示反馈表单。 + */ + @At + @ViReturn + @RequiresPermissions("proposal.transact.feedback") + public Object getFeedbackInfo(@Param("proposalId") String proposalId) { + return proposalFeedBackService.getFeedbackInfo(proposalId); + } + + /** + * 手机端提交反馈评价。 + * 参数说明: + * feedBack:ProposalFeedback 反馈对象,需包含 proposalId、feedbackCode、feedbackOpinion; + * scoreSign:反馈人签名图片数据,可为空,传入后保存到 scoreSignId; + * 返回值:无业务数据,成功时由 ViReturn 包装为成功响应。 + */ + @At + @POST + @ViReturn + @RequiresPermissions("proposal.transact.feedback") + @Aop(TransAop.READ_COMMITTED) + public Object doFeedback(@Param("feedBack") ProposalFeedback proposalFeedback, + @Param(value = "scoreSign", required = false) String scoreSign) { + if (proposalFeedback == null || StrUtil.isBlank(proposalFeedback.getProposalId())) { + throw new RuntimeException("提案信息不能为空"); + } + if (StrUtil.isBlank(proposalFeedback.getFeedbackCode())) { + throw new RuntimeException("请选择办理结果评价"); + } + proposalFeedBackService.doFeedback(proposalFeedback, scoreSign); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/proposal/constants/ProposalToDoHandler.java b/src/main/java/io/v/nutz/zhgh/proposal/constants/ProposalToDoHandler.java index 1df3b2f..a7d2a71 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/constants/ProposalToDoHandler.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/constants/ProposalToDoHandler.java @@ -69,8 +69,8 @@ public enum ProposalToDoHandler { List.of(loginName), "/platform/proposal/transact/seconded", "/platform/proposal/transact/seconded", - "/platform/proposal/transact/seconded", - "/platform/proposal/transact/seconded"); + "/mobile/proposal/seconded", + "/mobile/proposal/seconded"); } localProcessService.updateProcessNodeName("proposal_info@" + proposalId, "邀请附议人"); } diff --git a/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseController.java b/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseController.java index 64e3fff..c71aa32 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseController.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseController.java @@ -4,18 +4,16 @@ import cn.hutool.core.util.StrUtil; import io.v.nutz.base.annontation.ViReturn; import io.v.nutz.base.query.PageForm; import io.v.nutz.sys.services.SysLocalProcessService; +import io.v.nutz.sys.models.Sys_signature; import io.v.nutz.zhgh.proposal.models.ProposalAudit; import io.v.nutz.zhgh.proposal.models.ProposalSearch; import io.v.nutz.zhgh.proposal.services.ProposalAuditService; import io.v.nutz.zhgh.proposal.services.ProposalCaseService; import io.v.nutz.zhgh.proposal.services.ProposalInfoService; -import io.v.nutz.sys.models.Sys_signature; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Dao; -import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -73,27 +71,7 @@ public class ProposalCaseController { @ViReturn @RequiresPermissions(value = {"proposal.transact.case", "offerAdvice.schoolUnionAudit"}, logical = Logical.OR) public Object getUnderTakeAndLeader() { - Sql sql = Sqls.create(""" - SELECT - pu.*, - ( - SELECT - GROUP_CONCAT( su.username ) - FROM - sys_user_role sur - LEFT JOIN sys_user su ON sur.userId = su.id - WHERE - sur.cbdwid = pu.id - AND sur.roleId = '153cba3fb88b4c88b7c1002f0eb63749' - ) leader - FROM - proposal_undertake pu - WHERE - pu.isEnable = 1 - ORDER BY - pu.unitCode ASC - """); - return proposalInfoService.listMap(sql); + return proposalCaseService.getUnderTakeAndLeader(); } /** diff --git a/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseReadController.java b/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseReadController.java index c98577b..6191bd4 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseReadController.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/controller/transact/ProposalCaseReadController.java @@ -1,38 +1,23 @@ package io.v.nutz.zhgh.proposal.controller.transact; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import cn.wizzer.framework.page.Pagination; import io.v.nutz.base.annontation.ViReturn; import io.v.nutz.base.utils.MsgApi; import io.v.nutz.base.utils.Roles; -import io.v.nutz.base.dao.CndPlus; import io.v.nutz.base.query.PageForm; -import io.v.nutz.zhgh.proposal.constants.ProposalConstant; -import io.v.nutz.zhgh.proposal.constants.ProposalState; -import io.v.nutz.zhgh.proposal.constants.ProposalStateDirectionEnum; -import io.v.nutz.zhgh.proposal.constants.ProposalToDoHandler; import io.v.nutz.zhgh.proposal.models.ProposalAudit; -import io.v.nutz.zhgh.proposal.models.ProposalInfo; import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalCaseReadService; import io.v.nutz.zhgh.proposal.services.ProposalInfoService; -import io.v.nutz.sys.models.Sys_signature; import io.v.nutz.sys.models.Sys_user_role; import io.v.nutz.sys.models.User; -import io.v.nutz.sys.services.SysSignatureService; -import io.v.nutz.web.commons.utils.ShiroUtil; import io.v.nutz.zhgh.proposal.services.ProposalProcessService; import org.apache.shiro.authz.annotation.RequiresPermissions; 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.Static; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.json.Json; import org.nutz.lang.Lang; -import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -51,6 +36,8 @@ public class ProposalCaseReadController { private ProposalInfoService proposalInfoService; @Inject private ProposalProcessService proposalProcessService; + @Inject + private ProposalCaseReadService proposalCaseReadService; @Inject private Dao dao; @@ -65,73 +52,7 @@ public class ProposalCaseReadController { @ViReturn @RequiresPermissions("proposal.transact.caseRead") public Object pageData(PageForm page, ProposalSearch search) { - CndPlus cnd = CndPlus.create(); - Sql sql = Sqls.create(""" - SELECT - info.*, - su.username, - su.loginname, - state.stateName, - state.stateColor, - type.typeCode, - type.typeName, - jdh.jdhallname meetingName, - dbt.dbtname delegationName, - dbt.`code` delegationCode, - manner.`name` mannerName, - result.`name` resultName, - $temp_sql - FROM - proposal_info info - LEFT JOIN `user` su ON su.id = info.createUser - LEFT JOIN proposal_state state ON state.stateCode = info.stateCode - LEFT JOIN proposal_type type ON type.id = info.typeId - LEFT JOIN jdh_jdhxx jdh ON jdh.id = info.teacherMeetingId - LEFT JOIN jdh_dbt dbt ON dbt.id = info.delegationId - LEFT JOIN sys_dict manner ON manner.`code` = info.mannerCode - LEFT JOIN sys_dict result ON result.`code` = info.resultCode - $condition - """).setParam("userId", ShiroUtil.getPrincipalProperty("id")); - - - /* StringBuffer tempStr = new StringBuffer(""" - (JSON_CONTAINS(JSON_EXTRACT( membersOpinions, '$[*].userId' ), '\\"%s\\"') = 1) AS isOpinion - """.formatted(ShiroUtil.getUserId())); - sql.setVar("temp_sql", tempStr);*/ - - StringBuffer tempStr = new StringBuffer(""" - FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions )is not null isOpinion - """.formatted(ShiroUtil.getUserId())); - sql.setVar("temp_sql", tempStr); - - if (Strings.isNotBlank(search.getIsAudit())) { - if (search.getIsAudit().equals("true")) { - cnd.and(new Static("FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions )is not null".formatted(ShiroUtil.getUserId()))); -// cnd.and(new Static("JSON_CONTAINS(JSON_EXTRACT( info.membersOpinions, '$[*].userId' ), '\"%s\"') = 1".formatted(ShiroUtil.getUserId()))); - } else { - cnd.and(new Static("FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions ) is null".formatted(ShiroUtil.getUserId()))); - -// cnd.and(new Static("(JSON_CONTAINS(JSON_EXTRACT( info.membersOpinions, '$[*].userId' ), '\"%s\"') = 0 or JSON_CONTAINS(JSON_EXTRACT( info.membersOpinions, '$[*].userId' ), '\"%s\"') is null)".formatted(ShiroUtil.getUserId(), ShiroUtil.getUserId()))); - } - } - cnd.and("info.stateCode", ">", ProposalState.DELEGATION); - cnd.asc("info.stateCode"); -// proposalInfoService.sortAndSearch(cnd,page,search); - Pagination pagination = (Pagination) proposalInfoService.initSql(sql, page, search, cnd); - -// List list = pagination.getList(); -// for (NutMap map : list) { -// if (StrUtil.isNotBlank(search.getIsAudit()) && "true".equals(search.getIsAudit())) { -// String membersOpinions = map.getString("membersOpinions"); -// List mlist = Json.fromJsonAsList(String.class, membersOpinions); -// map.putAll(getCommitteeOpinion(mlist)); -// } else { -// map.putAll(NutMap.NEW().addv("caseNum", "暂无") -// .addv("opinionNum", "暂无").addv("notGiveNum", "暂无")); -// } -// } -// pagination.setList(list); - return pagination; + return proposalCaseReadService.pageData(page, search); } @@ -153,9 +74,6 @@ public class ProposalCaseReadController { } - @Inject - private SysSignatureService sysSignatureService; - /** * 委员会成员意见 * @@ -167,29 +85,7 @@ public class ProposalCaseReadController { @ViReturn @RequiresPermissions("proposal.transact.caseRead") public Object putSuggestion(String sign, @Param("audit") String audit, @Param("proposalId") String proposalId) { - ProposalInfo proposalInfo = proposalInfoService.fetch(proposalId); - String signId = sysSignatureService.insert(new Sys_signature(sign)).getId(); - ProposalAudit proposalAudit = Json.fromJson(ProposalAudit.class, audit); - proposalAudit.setSignId(signId); - proposalAudit.setUserId(ShiroUtil.getUserId()); - if (!proposalAudit.getOther().equals(ProposalConstant.DETERMINE)) { - proposalAudit.setDetermineTypeCode(null); - } - - dao.insert(proposalAudit); - - /*List membersOpinions = proposalInfo.getMembersOpinions() == null ? new ArrayList<>() : proposalInfo.getMembersOpinions(); - membersOpinions.add(NutMap.NEW().addv("userId", ShiroUtil.getUserId()).addv("auditId", insertAudit.getId())); - proposalInfo.setMembersOpinions(membersOpinions);*/ - List membersOpinions = proposalInfo.getMembersOpinions() == null ? new ArrayList<>() : proposalInfo.getMembersOpinions(); - membersOpinions.add(proposalAudit.getId()); - proposalInfo.setMembersOpinions(membersOpinions); - proposalInfoService.updateIgnoreNull(proposalInfo); -// // 提案小组组长审核后,修改提案状态之后,流程开始往下走 -// proposalInfo.setStateCode(proposalProcessService.getStateCode(proposalId, ProposalStateDirectionEnum.NEXT, false)); -// ProposalToDoHandler.COMPLETE_PROPOSAL_TEAM_LEADER_REVIEW_TASK.exec(proposalInfo.getId(), NutMap.NEW().setv("opinion", "提案工作组组长审阅完成")); -// // 创建校党委审议待办 -// ProposalToDoHandler.CREATE_CASE_TASK.exec(proposalInfo.getId(), NutMap.NEW().setv("opinion", "提案工作组组长审阅完成")); + proposalCaseReadService.putSuggestion(sign, audit, proposalId); return null; } diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseReadService.java b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseReadService.java new file mode 100644 index 0000000..c6de83a --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseReadService.java @@ -0,0 +1,28 @@ +package io.v.nutz.zhgh.proposal.services; + +import io.v.nutz.base.query.PageForm; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; + +/** + * 提案工作组成员意见审核公共服务。 + */ +public interface ProposalCaseReadService { + + /** + * 查询提案工作组成员意见列表。 + * + * @param page 分页参数,包含 pageNumber、pageSize、排序字段等 + * @param search 查询参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已提交成员意见 + * @return 分页结果,list 中包含提案基础信息、状态、类型、届次、代表团、立案结果以及当前用户是否已发表意见 isOpinion + */ + Object pageData(PageForm page, ProposalSearch search); + + /** + * 保存提案工作组成员意见。 + * + * @param sign 签名图片数据,保存后关联到审核记录 + * @param audit ProposalAudit 的 JSON 字符串,包含审核人、审核时间、审核意见、立案意见等字段 + * @param proposalId 提案 id + */ + void putSuggestion(String sign, String audit, String proposalId); +} diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseService.java b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseService.java index 9b5c63b..f946d24 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseService.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalCaseService.java @@ -5,6 +5,7 @@ import io.v.nutz.base.query.PageForm; import io.v.nutz.zhgh.proposal.models.ProposalAudit; import io.v.nutz.zhgh.proposal.models.ProposalSearch; import io.v.nutz.base.service.ViService; +import org.nutz.lang.util.NutMap; import java.util.List; import java.util.Map; @@ -23,6 +24,13 @@ public interface ProposalCaseService extends ViService { */ Pagination pageData(PageForm pageForm, ProposalSearch search); + /** + * 获取开启的承办单位及单位领导信息。 + * + * @return 承办单位列表,包含承办单位基础字段以及 leader 领导姓名串 + */ + List getUnderTakeAndLeader(); + /** * 立案 diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalFeedBackService.java b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalFeedBackService.java index af5e091..05d44c3 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalFeedBackService.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/ProposalFeedBackService.java @@ -1,13 +1,42 @@ package io.v.nutz.zhgh.proposal.services; import io.v.nutz.zhgh.proposal.models.ProposalFeedback; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; import io.v.nutz.base.service.ViService; +import io.v.nutz.base.query.PageForm; +import cn.wizzer.framework.page.Pagination; import org.nutz.lang.util.NutMap; import java.util.List; public interface ProposalFeedBackService extends ViService { + /** + * 手机端反馈评价列表。 + * 参数说明: + * page:分页参数,包含 pageNumber、pageSize、searchKeyWord 等; + * search:筛选参数,meetingId 为届次、proposalTypeId 为提案类型、proposalResultCode 为立案结果、isAudit 表示是否已反馈; + * 返回值:分页结果,list 中为 NutMap,包含提案基础字段和 isFeedback。 + */ + Pagination pageData(PageForm page, ProposalSearch search); + + /** + * 查询手机端反馈办理状态。 + * 参数说明: + * proposalId:proposal_info 表主键; + * 返回值:NutMap,包含 stateCode 和 handle,handle 为 true 时手机端展示反馈表单。 + */ + NutMap getFeedbackInfo(String proposalId); + + /** + * 提交提案反馈评价。 + * 参数说明: + * proposalFeedback:反馈实体,必须包含 proposalId、feedbackCode、feedbackOpinion 等反馈内容; + * scoreSign:反馈人签名图片数据,可为空; + * 返回值:无业务数据,成功后更新提案流程状态并处理待办。 + */ + void doFeedback(ProposalFeedback proposalFeedback, String scoreSign); + /** * @param proposalId 提案id * @return diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseReadServiceImpl.java b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseReadServiceImpl.java new file mode 100644 index 0000000..a45f765 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseReadServiceImpl.java @@ -0,0 +1,119 @@ +package io.v.nutz.zhgh.proposal.services.impl; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.base.dao.CndPlus; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.zhgh.proposal.constants.ProposalConstant; +import io.v.nutz.zhgh.proposal.constants.ProposalState; +import io.v.nutz.zhgh.proposal.models.ProposalAudit; +import io.v.nutz.zhgh.proposal.models.ProposalInfo; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; +import io.v.nutz.zhgh.proposal.services.ProposalCaseReadService; +import io.v.nutz.zhgh.proposal.services.ProposalInfoService; +import io.v.nutz.sys.models.Sys_signature; +import io.v.nutz.sys.services.SysSignatureService; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.Static; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.Strings; + +import java.util.ArrayList; +import java.util.List; + +/** + * 提案工作组成员意见审核公共服务实现。 + */ +@IocBean +public class ProposalCaseReadServiceImpl implements ProposalCaseReadService { + + @Inject + private ProposalInfoService proposalInfoService; + + @Inject + private SysSignatureService sysSignatureService; + + @Inject + private Dao dao; + + @Override + public Object pageData(PageForm page, ProposalSearch search) { + CndPlus cnd = CndPlus.create(); + Sql sql = Sqls.create(""" + SELECT + info.*, + su.username, + su.loginname, + state.stateName, + state.stateColor, + type.typeCode, + type.typeName, + jdh.jdhallname meetingName, + dbt.dbtname delegationName, + dbt.`code` delegationCode, + manner.`name` mannerName, + result.`name` resultName, + $temp_sql + FROM + proposal_info info + LEFT JOIN `user` su ON su.id = info.createUser + LEFT JOIN proposal_state state ON state.stateCode = info.stateCode + LEFT JOIN proposal_type type ON type.id = info.typeId + LEFT JOIN jdh_jdhxx jdh ON jdh.id = info.teacherMeetingId + LEFT JOIN jdh_dbt dbt ON dbt.id = info.delegationId + LEFT JOIN sys_dict manner ON manner.`code` = info.mannerCode + LEFT JOIN sys_dict result ON result.`code` = info.resultCode + $condition + """).setParam("userId", ShiroUtil.getPrincipalProperty("id")); + + // 与 PC 端原查询保持一致:当前用户的 proposal_audit 记录必须存在于 membersOpinions 中才算已提交成员意见。 + String userId = ShiroUtil.getUserId(); + sql.setVar("temp_sql", """ + FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions )is not null isOpinion + """.formatted(userId)); + + if (Strings.isNotBlank(search.getIsAudit())) { + if ("true".equals(search.getIsAudit())) { + cnd.and(new Static("FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions )is not null".formatted(userId))); + } else { + cnd.and(new Static("FIND_IN_SET(( SELECT id FROM proposal_audit WHERE proposalId = info.id AND userId = '%s' AND other IS NOT NULL limit 1), info.membersOpinions ) is null".formatted(userId))); + } + } + cnd.and("info.stateCode", ">", ProposalState.DELEGATION); + cnd.asc("info.stateCode"); + Pagination pagination = (Pagination) proposalInfoService.initSql(sql, page, search, cnd); + return pagination; + } + + /** + * 保存提案工作组成员意见,并把审核记录 id 追加到提案 membersOpinions。 + * sign 为签名图片数据,audit 为 ProposalAudit JSON,proposalId 为提案 id; + * audit.other 表示立案意见,非确定立案时会清空 determineTypeCode,避免残留立案类型。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void putSuggestion(String sign, String audit, String proposalId) { + ProposalInfo proposalInfo = proposalInfoService.fetch(proposalId); + String signId = sysSignatureService.insert(new Sys_signature(sign)).getId(); + ProposalAudit proposalAudit = Json.fromJson(ProposalAudit.class, audit); + proposalAudit.setSignId(signId); + proposalAudit.setUserId(ShiroUtil.getUserId()); + if (!proposalAudit.getOther().equals(ProposalConstant.DETERMINE)) { + proposalAudit.setDetermineTypeCode(null); + } + + dao.insert(proposalAudit); + + // membersOpinions 保存审核记录 id,保持 PC 端和手机端列表判断、详情展示一致。 + List membersOpinions = proposalInfo.getMembersOpinions() == null ? new ArrayList<>() : proposalInfo.getMembersOpinions(); + membersOpinions.add(proposalAudit.getId()); + proposalInfo.setMembersOpinions(membersOpinions); + proposalInfoService.updateIgnoreNull(proposalInfo); + } +} diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseServiceImpl.java b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseServiceImpl.java index 2629f40..33dde98 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseServiceImpl.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalCaseServiceImpl.java @@ -117,6 +117,31 @@ public class ProposalCaseServiceImpl extends ViServiceImpl implements ProposalCa return pagination; } + @Override + public List getUnderTakeAndLeader() { + Sql sql = Sqls.create(""" + SELECT + pu.*, + ( + SELECT + GROUP_CONCAT( su.username ) + FROM + sys_user_role sur + LEFT JOIN sys_user su ON sur.userId = su.id + WHERE + sur.cbdwid = pu.id + AND sur.roleId = '153cba3fb88b4c88b7c1002f0eb63749' + ) leader + FROM + proposal_undertake pu + WHERE + pu.isEnable = 1 + ORDER BY + pu.unitCode ASC + """); + return listMap(sql); + } + @Override @Aop(TransAop.READ_COMMITTED) public void audit(List proposalIds, String resultCode, String determineTypeCode, String hostUnit, List helpUnits, String typeId, ProposalAudit audit, String auditType, int flag) { diff --git a/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalFeedBackServiceImpl.java b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalFeedBackServiceImpl.java index bc363b9..3959d9a 100644 --- a/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalFeedBackServiceImpl.java +++ b/src/main/java/io/v/nutz/zhgh/proposal/services/impl/ProposalFeedBackServiceImpl.java @@ -1,13 +1,31 @@ package io.v.nutz.zhgh.proposal.services.impl; +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.base.query.PageForm; import io.v.nutz.zhgh.proposal.models.ProposalFeedback; +import io.v.nutz.zhgh.proposal.models.ProposalInfo; +import io.v.nutz.zhgh.proposal.models.ProposalReply; +import io.v.nutz.zhgh.proposal.models.ProposalSearch; import io.v.nutz.zhgh.proposal.models.ProposalUndertake; +import io.v.nutz.zhgh.proposal.constants.ProposalConstant; +import io.v.nutz.zhgh.proposal.constants.ProposalState; +import io.v.nutz.zhgh.proposal.constants.ProposalToDoHandler; import io.v.nutz.zhgh.proposal.services.ProposalFeedBackService; import io.v.nutz.base.service.impl.ViServiceImpl; +import io.v.nutz.sys.models.Sys_signature; +import io.v.nutz.sys.services.SysSignatureService; +import io.v.nutz.web.commons.utils.ShiroUtil; +import io.v.nutz.base.utils.DateUtil; +import org.nutz.aop.interceptor.ioc.TransAop; +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.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.json.Json; import org.nutz.lang.Lang; @@ -23,6 +41,136 @@ public class ProposalFeedBackServiceImpl extends ViServiceImpl super(dao); } + @Inject + private SysSignatureService sysSignatureService; + + @Override + public Pagination pageData(PageForm page, ProposalSearch search) { + Sql sql = Sqls.create(""" + SELECT + info.*, + su.username, + su.loginname, + state.stateName, + state.stateColor, + type.typeName, + jdh.jdhallname meetingName, + dbt.dbtname delegationName, + manner.`name` mannerName, + result.`name` resultName, + ( + SELECT count(1) > 0 + FROM proposal_conjoin + WHERE JSON_CONTAINS(proposalIds, JSON_QUOTE(info.id)) + ) AS isConjoin, + ( + SELECT count(1) + FROM proposal_feedback pf + WHERE pf.proposalId = info.id + ) AS feedbackCount + FROM + proposal_info info + LEFT JOIN `user` su ON su.id = info.createUser + LEFT JOIN proposal_state state ON state.stateCode = info.stateCode + LEFT JOIN proposal_type type ON type.id = info.typeId + LEFT JOIN jdh_jdhxx jdh ON jdh.id = info.teacherMeetingId + LEFT JOIN jdh_dbt dbt ON dbt.id = info.delegationId + LEFT JOIN sys_dict manner ON manner.`code` = info.mannerCode + LEFT JOIN sys_dict result ON result.`code` = info.resultCode + $condition + """); + + Cnd cnd = Cnd.NEW(); + if (!ShiroUtil.hasAnyRoles("sysadmin, tamange,A06")) { + cnd.and("info.createUser", "=", ShiroUtil.getUserId()); + } + if (StrUtil.isNotBlank(search.getSearchKeyWord())) { + SqlExpressionGroup keywordGroup = new SqlExpressionGroup(); + keywordGroup.orLike("info.proposalName", search.getSearchKeyWord()); + keywordGroup.orLike("info.proposalCode", search.getSearchKeyWord()); + cnd.and(keywordGroup); + } + cnd.andEX("info.teacherMeetingId", "=", search.getMeetingId()); + cnd.andEX("info.typeId", "=", search.getProposalTypeId()); + cnd.andEX("info.resultCode", "=", search.getProposalResultCode()); + + if (StrUtil.isBlank(search.getIsAudit())) { + SqlExpressionGroup allGroup = new SqlExpressionGroup(); + allGroup.or("info.stateCode", "=", ProposalState.FEEDBACKSCORE); + allGroup.or("info.stateCode", ">", ProposalState.FEEDBACKSCORE); + allGroup.orGT("(SELECT count(1) FROM proposal_feedback pf WHERE pf.proposalId = info.id)", 0); + cnd.and(allGroup); + } else if ("true".equals(search.getIsAudit())) { + SqlExpressionGroup feedbackGroup = new SqlExpressionGroup(); + feedbackGroup.or("info.stateCode", ">", ProposalState.FEEDBACKSCORE); + feedbackGroup.orGT("(SELECT count(1) FROM proposal_feedback pf WHERE pf.proposalId = info.id)", 0); + cnd.and(feedbackGroup); + } else if ("false".equals(search.getIsAudit())) { + cnd.and("info.stateCode", "=", ProposalState.FEEDBACKSCORE); + } + cnd.asc("info.proposalCode").desc("info.createTime"); + sql.setCondition(cnd); + + Pagination pagination = listPageMap(page.getPageNumber(), page.getPageSize(), sql); + List list = pagination.getList(NutMap.class); + list.forEach(v -> { + int feedbackCount = v.getInt("feedbackCount", 0); + Integer stateCode = v.getInt("stateCode"); + v.setv("isFeedback", feedbackCount > 0 || (stateCode != null && stateCode > ProposalState.FEEDBACKSCORE)); + }); + pagination.setList(list); + return pagination; + } + + @Override + public NutMap getFeedbackInfo(String proposalId) { + ProposalInfo proposalInfo = dao().fetch(ProposalInfo.class, Cnd.where("id", "=", proposalId)); + return NutMap.NEW() + .addv("proposalId", proposalId) + .addv("stateCode", proposalInfo == null ? null : proposalInfo.getStateCode()) + .addv("handle", proposalInfo != null && proposalInfo.getStateCode() != null && proposalInfo.getStateCode().equals(ProposalState.FEEDBACKSCORE)); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void doFeedback(ProposalFeedback proposalFeedback, String scoreSign) { + if (StrUtil.isNotBlank(scoreSign)) { + Sys_signature signature = sysSignatureService.insert(new Sys_signature(scoreSign)); + proposalFeedback.setScoreSignId(signature.getId()); + } + + int feedbackCount = count(Cnd.where("proposalId", "=", proposalFeedback.getProposalId())); + proposalFeedback.setFeedbackPerson(ShiroUtil.getUserId()); + proposalFeedback.setFeedbackNumber(feedbackCount + 1); + proposalFeedback.setFeedbackTime(DateUtil.getDateTime()); + insert(proposalFeedback); + + ProposalToDoHandler.COMPLETE_FEEDBACK_TASK.exec(proposalFeedback.getProposalId(), NutMap.NEW().addv("opinion", proposalFeedback.getFeedbackOpinion())); + + ProposalReply lastReply = dao().fetch(ProposalReply.class, Cnd.where("proposalId", "=", proposalFeedback.getProposalId()).and("undertakeType", "=", 1)); + if (lastReply == null) { + throw new RuntimeException("未找到主办单位办理记录,无法提交反馈评价"); + } + // 不满意时需要主办单位二次答复,其他评价结果直接结束当前提案流程。 + if (ProposalConstant.NOT_SATISFIED.equals(proposalFeedback.getFeedbackCode())) { + dao().update(ProposalInfo.class, Chain.make("stateCode", ProposalState.UNITREPLY), Cnd.where("id", "=", proposalFeedback.getProposalId())); + + ProposalReply newReply = new ProposalReply(); + newReply.setProposalId(lastReply.getProposalId()); + newReply.setUndertakeType(lastReply.getUndertakeType()); + newReply.setReplyUnitId(lastReply.getReplyUnitId()); + newReply.setReplyNumber(lastReply.getReplyNumber() + 1); + newReply.setIsReply(false); + newReply.setIsLeaderAudit(false); + dao().insert(newReply); + + ProposalToDoHandler.CREATE_UNDERTAKE_TASK.exec(proposalFeedback.getProposalId(), null); + } else { + dao().update(ProposalInfo.class, Chain.make("stateCode", ProposalState.FINISH), Cnd.where("id", "=", lastReply.getProposalId())); + ProposalToDoHandler.COMPLETE_PROCESS.exec(lastReply.getProposalId(), null); + } + } + @Override public List getFeedbackList(String proposalId) { Sql sql = Sqls.create(""" diff --git a/src/main/resources/static/assets/mobile/css/list-card.css b/src/main/resources/static/assets/mobile/css/list-card.css new file mode 100644 index 0000000..b7d475b --- /dev/null +++ b/src/main/resources/static/assets/mobile/css/list-card.css @@ -0,0 +1,161 @@ +.list-card { + --card-accent: #2563eb; + --card-bg: #ffffff; + --card-title: #111827; + --card-label: #8e969f; + --card-value: #374151; + --card-line: #f3f4f6; + --card-shadow: 0 14px 34px rgba(15, 23, 42, 0.07); + --badge-bg: #eef2ff; + --badge-color: #3730a3; + margin: 12px 14px; + padding: 15px 16px; + background: var(--card-bg); + border-radius: 14px; + box-shadow: var(--card-shadow); + line-height: 1.5; + font-size: 13px; + overflow: hidden; +} + +.list-card:active { + background: #fbfcfe; + transform: scale(0.998); +} + +.list-card .card-header { + position: relative; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 1px 0 13px 12px; +} + +.list-card .card-header::before { + content: ""; + position: absolute; + left: 0; + top: 3px; + width: 3px; + height: 18px; + border-radius: 999px; + background: var(--card-accent); +} + +.list-card .card-header::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 1px; + background: var(--card-line); +} + +.list-card .card-title { + flex: 1; + min-width: 0; + color: var(--card-title); + font-size: 15px; + font-weight: 700; + line-height: 22px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.list-card .card-body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + column-gap: 18px; + row-gap: 10px; + padding: 13px 0; +} + +.list-card .card-item { + min-width: 0; + display: flex; + align-items: baseline; + gap: 2px; +} + +.list-card .card-label { + flex: none; + color: var(--card-label); +} + +.list-card .card-value { + min-width: 0; + color: var(--card-value); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.list-card .card-footer { + position: relative; + padding-top: 12px; + color: var(--card-value); +} + +.list-card .card-footer::before { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 0; + height: 1px; + background: var(--card-line); +} + +.list-card .badge { + flex: none; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 50px; + padding: 3px 9px; + border-radius: 999px; + color: var(--badge-color); + background: var(--badge-bg); + font-size: 12px; + font-weight: 600; + line-height: 16px; + white-space: nowrap; +} + +.list-card .badge-draft { + --badge-bg: #F1F5F9; + --badge-color: #64748B; +} + +.list-card .badge-green { + --badge-bg: #e7f7ee; + --badge-color: #047857; +} + +.list-card .badge-blue { + --badge-bg: #eaf3ff; + --badge-color: #1d4ed8; +} + +.list-card .badge-gray { + --badge-bg: #f2f4f7; + --badge-color: #667085; +} + +.list-card .badge-red { + --badge-bg: #fff0ef; + --badge-color: #b42318; +} + +@media (max-width: 360px) { + .list-card { + padding: 14px; + } + + .list-card .card-body { + grid-template-columns: minmax(0, 1fr); + } +} diff --git a/src/main/resources/static/assets/mobile/css/universal-info.css b/src/main/resources/static/assets/mobile/css/universal-info.css new file mode 100644 index 0000000..0805a96 --- /dev/null +++ b/src/main/resources/static/assets/mobile/css/universal-info.css @@ -0,0 +1,448 @@ +.info-page-container { + min-height: 100%; + padding: 1px 0 12px; + background: #F7F8FA; +} + +.info-card { + margin: 12px; + padding: 16px; + background: #ffffff; + border-radius: 16px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.02); +} + +.info-section-title { + position: relative; + display: flex; + align-items: center; + min-height: 22px; + margin-bottom: 16px; + padding-left: 12px; + color: #1D2129; + font-size: 16px; + font-weight: 700; + line-height: 22px; +} + +.info-section-title::before { + content: ""; + position: absolute; + left: 0; + top: 4px; + width: 4px; + height: 14px; + border-radius: 999px; + background: #1890ff; +} + +.info-row { + display: flex; + justify-content: space-between; + gap: 12px; + line-height: 22px; +} + +.info-row + .info-row { + margin-top: 12px; +} + +.info-label { + flex: 0 0 90px; + color: #86909C; + font-size: 14px; +} + +.info-value { + flex: 1; + min-width: 0; + color: #1D2129; + font-size: 14px; + font-weight: 600; + text-align: right; + word-break: break-all; +} + +.info-value.is-long { + text-align: left; + white-space: normal; +} + +.info-long-block { + margin-top: 14px; + padding-top: 14px; + border-top: 1px solid #F2F3F5; +} + +.info-long-title { + margin-bottom: 8px; + color: #86909C; + font-size: 14px; +} + +.info-long-content { + color: #1D2129; + font-size: 14px; + font-weight: 500; + line-height: 1.7; + word-break: break-word; +} + +.info-input-area { + margin-top: 12px; +} + +.info-input-area .van-cell-group, +.info-input-area .van-cell { + background: transparent; +} + +.info-input-area .van-cell::after, +.info-input-area .van-cell-group::after { + border: 0; +} + +.info-input-area .van-field { + margin-top: 10px; + padding: 12px; + border-radius: 8px; + background: #F2F3F5; +} + +.info-input-area .van-field__label { + color: #4E5969; + font-size: 14px; + font-weight: 600; +} + +.info-input-area .van-field__control { + color: #1D2129; + font-size: 14px; + background: transparent; +} + +.info-input-title { + margin-bottom: 10px; + color: #1D2129; + font-size: 15px; + font-weight: 700; +} + +.info-cell-form { + background: #ffffff; +} + +.info-cell-form .info-row, +.info-cell-form .info-picker-card .van-field, +.info-cell-form > .van-field { + margin: 0; + padding: 13px 0; + border-bottom: 1px solid #F2F3F5; + border-radius: 0; + background: #ffffff; + line-height: 22px; +} + +.info-cell-form .info-row + .info-row { + margin-top: 0; +} + +.info-cell-form .info-label, +.info-cell-form .van-field__label, +.info-cell-form .info-picker-label { + flex: 0 0 90px; + width: 90px; + margin-right: 12px; + color: #86909c; + font-size: 14px; + font-weight: 400; + line-height: 22px; +} + +.info-cell-form .info-value, +.info-cell-form .van-field__control { + color: #1d2129; + font-size: 14px; + font-weight: 500; + line-height: 22px; + text-align: right; +} + +.info-cell-form .info-value.is-long { + text-align: right; +} + +.info-cell-form .van-field__control::placeholder { + color: #c9cdd4; + font-weight: 400; +} + +.info-cell-form .van-field__right-icon { + color: #86909c; +} + +.info-cell-form .van-cell--required::before { + top: 50%; + left: -8px; + transform: translateY(-50%); +} + +.info-cell-form .info-multi-value-field .van-field__body { + align-items: flex-start; +} + +.info-cell-form .info-multi-value-field .van-field__right-icon { + padding-top: 1px; +} + +.info-multi-value { + flex: 1; + min-width: 0; + color: #1d2129; + font-size: 14px; + font-weight: 500; + line-height: 22px; + text-align: right; + white-space: normal; + word-break: break-all; +} + +.info-multi-placeholder { + color: #c9cdd4; + font-weight: 400; +} + +.info-cell-form .van-radio-group { + width: 100%; + padding-top: 2px; +} + +.info-cell-form .van-radio { + line-height: 22px; +} + +.info-textarea-field { + padding-top: 12px; +} + +.info-textarea-label { + margin-bottom: 6px; + color: #86909c; + font-size: 14px; + line-height: 22px; +} + +.info-required-label::before { + content: "*"; + margin-right: 2px; + color: #ee0a24; +} + +.info-cell-form .info-textarea-field .van-field { + margin: 0; + padding: 12px; + border: 0; + border-radius: 8px; + background: #f2f3f5; +} + +.info-cell-form .info-textarea-field .van-field__control { + min-height: 96px; + text-align: left; +} + +.info-cell-form .info-textarea-field .van-field__word-limit { + color: #c9cdd4; + font-size: 12px; +} + +.info-footer-actions { + padding: 20px 0 4px; +} + +.info-cell-form .info-footer-actions .van-button { + height: 44px; + border-radius: 8px; + font-size: 15px; + font-weight: 500; +} + +.info-cell-form .info-ghost-button { + border: 1px solid #4e5969; + background: #ffffff; + color: #4e5969; +} + +.info-cell-form .info-primary-button { + border: 0; + color: #ffffff; +} + +.unit-select-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + height: 44px; +} + +.unit-select-title { + color: #1D2129; + font-size: 16px; + font-weight: 600; +} + +.unit-select-cancel, +.unit-select-confirm { + height: 100%; + padding: 0 16px; + border: 0; + background: transparent; + font-size: 14px; +} + +.unit-select-cancel { + color: #969799; +} + +.unit-select-confirm { + color: #576b95; +} + +.unit-select-list { + max-height: 320px; + overflow-y: auto; + padding: 4px 16px 16px; +} + +.unit-select-list .van-checkbox { + align-items: flex-start; + overflow: visible; +} + +.unit-select-list .van-checkbox__label { + flex: 1; + min-width: 0; + white-space: normal; + word-break: break-all; + line-height: 22px; +} + +.info-sign-image { + overflow: hidden; + margin-top: 12px; + padding: 10px; + border-radius: 12px; + background: #F7F8FA; +} + +.info-record + .info-record { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid #F2F3F5; +} + +.info-record-title { + margin-bottom: 12px; + color: #4E5969; + font-size: 14px; + font-weight: 700; +} + +.info-collapse { + overflow: hidden; + border-radius: 12px; + background: #F7F8FA; +} + +.info-inner-collapse { + margin-top: 14px; +} + +.info-collapse .van-cell { + padding: 12px 14px; + background: transparent; +} + +.info-collapse .van-cell::after, +.info-collapse .van-collapse-item__wrapper::after { + border: 0; +} + +.info-collapse .van-cell__title { + min-width: 0; + color: #1D2129; + font-size: 14px; + font-weight: 700; +} + +.info-collapse-title { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + min-width: 0; + padding-right: 8px; +} + +.info-collapse-title-text { + overflow: hidden; + min-width: 0; + text-overflow: ellipsis; + white-space: nowrap; +} + +.info-collapse .van-collapse-item__content { + padding: 12px 14px 14px; + background: #ffffff; +} + +.info-collapse-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 0; +} + +.info-collapse-row + .info-collapse-row, +.info-collapse-group + .info-collapse-group { + border-top: 1px solid #F2F3F5; +} + +.info-collapse-group { + padding: 12px 0; +} + +.info-collapse-group:first-child { + padding-top: 0; +} + +.info-collapse-group:last-child { + padding-bottom: 0; +} + +.info-status { + flex: 0 0 auto; + padding: 3px 9px; + border-radius: 999px; + color: #64748B; + font-size: 12px; + font-weight: 600; + line-height: 18px; + background: #F1F5F9; +} + +.info-status.is-success { + color: #16A34A; + background: #ECFDF3; +} + +.info-status.is-danger { + color: #EF4444; + background: #FEF2F2; +} + +.info-status.is-muted { + color: #64748B; + background: #F1F5F9; +} diff --git a/src/main/resources/static/components/proposal/mobile/ProposalInfo.vue b/src/main/resources/static/components/proposal/mobile/ProposalInfo.vue index 6c7bcee..7643f52 100644 --- a/src/main/resources/static/components/proposal/mobile/ProposalInfo.vue +++ b/src/main/resources/static/components/proposal/mobile/ProposalInfo.vue @@ -1,245 +1,477 @@