diff --git a/src/main/java/cn/palmte/work/controller/backend/ProcessController.java b/src/main/java/cn/palmte/work/controller/backend/ProcessController.java index dd548b9..131222e 100644 --- a/src/main/java/cn/palmte/work/controller/backend/ProcessController.java +++ b/src/main/java/cn/palmte/work/controller/backend/ProcessController.java @@ -9,24 +9,21 @@ import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; -import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.math.BigDecimal; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -59,7 +56,7 @@ import cn.palmte.work.model.process.ProjectProcessRepository; import cn.palmte.work.model.process.SaleContract; import cn.palmte.work.model.process.SealTypeArray; import cn.palmte.work.model.process.form.ProcessQueryForm; -import cn.palmte.work.model.process.form.SaleContractDetailForm; +import cn.palmte.work.model.process.form.ProcessUpdateForm; import cn.palmte.work.model.process.form.SaleContractProcessForm; import cn.palmte.work.service.ProjectBudgetService; import cn.palmte.work.service.ProjectInstanceService; @@ -120,6 +117,17 @@ public class ProcessController { return "/admin/business/process-new"; } + /** + * 编辑流程 + */ + @GetMapping("/edit/{id}") + public String editProcess(Model model, @PathVariable int id) { + model.addAttribute("sealTypes", SealType.values()); + model.addAttribute("taxRate", Arrays.asList(0, 1, 3, 4, 5, 6, 9, 10, 13)); + model.addAttribute("processId", id); + return "/admin/business/process-edit"; + } + /** * 已办流程 */ @@ -232,6 +240,9 @@ public class ProcessController { // 预算毛利率 public BigDecimal budgetGrossMargin; + + public Object contract; + public ProjectProcess process; } @ResponseBody @@ -265,6 +276,46 @@ public class ProcessController { return project.getUnderwrittenMode() == 2 || project.getUnderwrittenMode() == 3 ? "是" : "否"; } + @ResponseBody + @GetMapping("/{id}") + public ProjectReturnValue get(@PathVariable int id) { + ProjectProcess process = processService.getById(id); + Project project = projectRepository.findById(process.getProjectId()); + BigDecimal repaidAmount = getRepaidAmount(id); + Object contract; + switch (process.getProcessType()) { + case sale_contract: { + contract = processService.findSaleContract(process.getId()); + break; + } + case business_procurement: { + contract = processService.findProcurementContract(process.getId()); + break; + } + default: + throw new UnsupportedOperationException("还不支持"); + } + + // 可以在对应表数据查询 是否存在再启用 + List<ProjectBudgetIncomeDetail> incomeDetails = projectBudgetService.getBudgetIncomeDetail(project); + return ProjectReturnValue.builder() + .process(process) + .contract(contract) + .isPrepaid(isPrepaid(project)) + .projectId(project.getId()) + .incomeDetails(incomeDetails) + .projectName(project.getName()) + .repaidAmount(repaidAmount + "元") + .budgetGrossMargin(project.getGrossProfitMargin()) + .projectNo(project.getProjectNo()) + .applyPersonName(process.getApplyPersonName()) + .contractAmount(project.getContractAmount()) + .terminalCustomer(project.getTerminalCustomer()) + .projectType(Enumerable.of(ProjectType.class, project.getType()).getDescription()) + .cooperationType(Enumerable.of(CooperationType.class, project.getCooperateType()).getDescription()) + .build(); + } + // 销售合同流程 @ResponseBody @@ -280,10 +331,6 @@ public class ProcessController { entity.setApplyPersonId(admin.getId()); entity.setAttachmentUri(JSON.toJSONString(form.getAttachments())); - if (entity.getStatus() == null) { - entity.setStatus(ProcessStatus.to_be_audit); - } - entityManager.persist(entity); HashMap<String, Object> variables = new HashMap<>(); @@ -306,38 +353,55 @@ public class ProcessController { } } - if (entity.getStatus() == ProcessStatus.to_be_audit) { - variables.put("process", entity); - switch (entity.getProcessType()) { - case sale_contract: - projectInstanceService.startSaleContractProcess(entity.getId(), variables); - break; - case business_procurement: - projectInstanceService.startBusinessPurchaseProcess(entity.getId(), variables); - break; - default: - throw new UnsupportedOperationException("还不支持"); + processService.updateIncomeDetails(form.getIncomeDetails()); + processService.startAuditProgress(entity, variables); + + } + + @ResponseBody + @PutMapping + @Transactional + public void update(@RequestBody @Valid ProcessUpdateForm form) throws Exception { + ProjectProcess entity = processService.getById(form.getId()); + + entity.setTaxRate(form.getTaxRate()); + entity.setContractNo(form.getContractNo()); + entity.setContractName(form.getContractName()); + + entity.setSealTypes(SealTypeArray.of(form.getSealTypes())); + entity.setApplyDept(String.join(",", form.getApplyDept())); + entity.setAttachmentUri(JSON.toJSONString(form.getAttachments())); + + entityManager.merge(entity); + + HashMap<String, Object> variables = new HashMap<>(); + switch (entity.getProcessType()) { + case sale_contract: { + SaleContract contract = processService.findSaleContract(form.getId()); + contract.setClientName(form.getClientName()); + contract.setPaymentTerms(form.getPaymentTerms()); + contract.setApplyPersonPhone(form.getApplyPersonPhone()); + + entityManager.merge(contract); + variables.put("contract", contract); + break; } + case business_procurement: { + ProcurementContract contract = processService.findProcurementContract(form.getId()); + contract.setMode(form.getProcurementMode()); + contract.setPaymentTerms(form.getPaymentTerms()); + contract.setSupplierName(form.getSupplierName()); + + entityManager.merge(contract); + variables.put("contract", contract); + break; + } + default: + throw new UnsupportedOperationException("还不支持"); } - List<SaleContractDetailForm> incomeDetails = form.getIncomeDetails(); - if (!CollectionUtils.isEmpty(incomeDetails)) { - jdbcTemplate.batchUpdate("update project_budget_income_detail set expiration_date =? where id =? ", - new BatchPreparedStatementSetter() { - - @Override - public void setValues(PreparedStatement ps, int i) throws SQLException { - SaleContractDetailForm detailForm = incomeDetails.get(i); - ps.setString(1, detailForm.getExpirationDate()); - ps.setInt(2, detailForm.getId()); - } - - @Override - public int getBatchSize() { - return incomeDetails.size(); - } - }); - } + processService.startAuditProgress(entity, variables); + processService.updateIncomeDetails(form.getIncomeDetails()); } @ResponseBody diff --git a/src/main/java/cn/palmte/work/model/process/ProcurementContract.java b/src/main/java/cn/palmte/work/model/process/ProcurementContract.java index 7f157c0..0e3fe4d 100644 --- a/src/main/java/cn/palmte/work/model/process/ProcurementContract.java +++ b/src/main/java/cn/palmte/work/model/process/ProcurementContract.java @@ -2,6 +2,8 @@ package cn.palmte.work.model.process; import org.hibernate.annotations.GenericGenerator; +import java.io.Serializable; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -21,7 +23,7 @@ import lombok.Data; @Data @Entity @Table(name = "procurement_contract") -public class ProcurementContract { +public class ProcurementContract implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/cn/palmte/work/model/process/ProjectProcess.java b/src/main/java/cn/palmte/work/model/process/ProjectProcess.java index 2a7044b..dbbe245 100644 --- a/src/main/java/cn/palmte/work/model/process/ProjectProcess.java +++ b/src/main/java/cn/palmte/work/model/process/ProjectProcess.java @@ -1,9 +1,15 @@ package cn.palmte.work.model.process; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.hibernate.annotations.GenericGenerator; +import java.io.IOException; +import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; @@ -29,7 +35,7 @@ import lombok.Data; @Data @Entity @Table(name = "project_process") -public class ProjectProcess { +public class ProjectProcess implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -66,6 +72,7 @@ public class ProjectProcess { private String contractName; // 用印类型 + @JsonSerialize(using = SealTypeArraySerializer.class) @Convert(converter = SealTypeArrayConverter.class) private SealTypeArray sealTypes; @@ -105,4 +112,13 @@ public class ProjectProcess { // 合作类型 // @Enumerated(EnumType.STRING) // private CooperationType cooperationType; + + static class SealTypeArraySerializer extends JsonSerializer<SealTypeArray> { + + @Override + public void serialize(SealTypeArray value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + serializers.defaultSerializeValue(value.getList(), gen); + } + } + } diff --git a/src/main/java/cn/palmte/work/model/process/SaleContract.java b/src/main/java/cn/palmte/work/model/process/SaleContract.java index e013ab2..0d5dd7e 100644 --- a/src/main/java/cn/palmte/work/model/process/SaleContract.java +++ b/src/main/java/cn/palmte/work/model/process/SaleContract.java @@ -2,6 +2,8 @@ package cn.palmte.work.model.process; import org.hibernate.annotations.GenericGenerator; +import java.io.Serializable; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -20,7 +22,7 @@ import lombok.Data; @Data @Entity @Table(name = "sale_contract") -public class SaleContract { +public class SaleContract implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/cn/palmte/work/model/process/SealTypeArray.java b/src/main/java/cn/palmte/work/model/process/SealTypeArray.java index 9f0c150..d61be6f 100644 --- a/src/main/java/cn/palmte/work/model/process/SealTypeArray.java +++ b/src/main/java/cn/palmte/work/model/process/SealTypeArray.java @@ -16,31 +16,30 @@ import cn.palmte.work.model.enums.SealType; * @author <a href="https://github.com/TAKETODAY">Harry Yang</a> * @since 2.0 2022/12/14 16:35 */ -//@JsonSerialize @Convert(converter = SealTypeArrayConverter.class) public class SealTypeArray { public static final SealTypeArray EMPTY = new SealTypeArray(); - private final List<SealType> sealTypes = new ArrayList<>(); + private final List<SealType> list = new ArrayList<>(); public SealTypeArray() { } - public SealTypeArray(SealType... sealTypes) { - Collections.addAll(this.sealTypes, sealTypes); + public SealTypeArray(SealType... list) { + Collections.addAll(this.list, list); } - public List<SealType> getSealTypes() { - return sealTypes; + public List<SealType> getList() { + return list; } - public void setSealTypes(List<SealType> sealTypes) { - this.sealTypes.clear(); - this.sealTypes.addAll(sealTypes); + public void setList(List<SealType> list) { + this.list.clear(); + this.list.addAll(list); } @Override public String toString() { - return sealTypes.stream() + return list.stream() .map(SealType::getDescription) .collect(Collectors.joining(",")); } @@ -59,7 +58,7 @@ public class SealTypeArray { public static SealTypeArray of(Collection<SealType> sealTypes) { SealTypeArray sealTypeArray = new SealTypeArray(); - sealTypeArray.sealTypes.addAll(sealTypes); + sealTypeArray.list.addAll(sealTypes); return sealTypeArray; } diff --git a/src/main/java/cn/palmte/work/model/process/SealTypeArrayConverter.java b/src/main/java/cn/palmte/work/model/process/SealTypeArrayConverter.java index eb36c59..e0fad17 100644 --- a/src/main/java/cn/palmte/work/model/process/SealTypeArrayConverter.java +++ b/src/main/java/cn/palmte/work/model/process/SealTypeArrayConverter.java @@ -20,7 +20,7 @@ public class SealTypeArrayConverter implements AttributeConverter<SealTypeArray, @Override public String convertToDatabaseColumn(SealTypeArray attribute) { - List<SealType> sealTypes = attribute.getSealTypes(); + List<SealType> sealTypes = attribute.getList(); if (sealTypes.isEmpty()) { return null; } diff --git a/src/main/java/cn/palmte/work/model/process/form/ProcessUpdateForm.java b/src/main/java/cn/palmte/work/model/process/form/ProcessUpdateForm.java new file mode 100644 index 0000000..151c3b5 --- /dev/null +++ b/src/main/java/cn/palmte/work/model/process/form/ProcessUpdateForm.java @@ -0,0 +1,56 @@ +package cn.palmte.work.model.process.form; + +import java.util.List; + +import javax.validation.constraints.NotNull; + +import cn.palmte.work.model.enums.ProcessType; +import cn.palmte.work.model.enums.ProcurementMode; +import cn.palmte.work.model.process.ProcessAttachment; +import lombok.Data; + +/** + * 流程合同更新 + * + * @author <a href="https://github.com/TAKETODAY">Harry Yang</a> + * @since 2.0 2022/12/23 16:39 + */ +@Data +public class ProcessUpdateForm { + + @NotNull + private Integer id; + + private String[] applyDept; + + // 合同编号 + private String contractNo; + + // 合同名称 + private String contractName; + + // 用印类型 + private String[] sealTypes; + + // 税率 + private String taxRate; + + // 收款条件 + private String paymentTerms; + // 备注 + private String remark; + + // 客户名称 + private String clientName; + + // 申请人电话 + private String applyPersonPhone; + + private ProcurementMode procurementMode; + + private String supplierName; + + private List<ProcessAttachment> attachments; + + private List<SaleContractDetailForm> incomeDetails; +} diff --git a/src/main/java/cn/palmte/work/model/process/form/SaleContractProcessForm.java b/src/main/java/cn/palmte/work/model/process/form/SaleContractProcessForm.java index 2bb95a6..e44814e 100644 --- a/src/main/java/cn/palmte/work/model/process/form/SaleContractProcessForm.java +++ b/src/main/java/cn/palmte/work/model/process/form/SaleContractProcessForm.java @@ -1,6 +1,5 @@ package cn.palmte.work.model.process.form; -import java.time.LocalDate; import java.util.List; import javax.validation.constraints.NotNull; @@ -67,6 +66,9 @@ public class SaleContractProcessForm { // 收款条件 private String paymentTerms; + // 备注 + private String remark; + private ProcessStatus status; private ProcessType processType; diff --git a/src/main/java/cn/palmte/work/service/ProjectProcessService.java b/src/main/java/cn/palmte/work/service/ProjectProcessService.java index 3a44f4c..0dd5b21 100644 --- a/src/main/java/cn/palmte/work/service/ProjectProcessService.java +++ b/src/main/java/cn/palmte/work/service/ProjectProcessService.java @@ -1,10 +1,15 @@ package cn.palmte.work.service; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.persistence.EntityManager; @@ -12,7 +17,12 @@ import javax.persistence.TypedQuery; import cn.palmte.work.model.Admin; import cn.palmte.work.model.enums.ProcessStatus; +import cn.palmte.work.model.process.ProcurementContract; +import cn.palmte.work.model.process.ProjectProcess; +import cn.palmte.work.model.process.SaleContract; +import cn.palmte.work.model.process.form.SaleContractDetailForm; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; /** * @author <a href="https://github.com/TAKETODAY">Harry Yang</a> @@ -24,6 +34,7 @@ public class ProjectProcessService { private final JdbcTemplate jdbcTemplate; private final EntityManager entityManager; + private final ProjectInstanceService projectInstanceService; /** * 更新流程 审批人,和状态 @@ -61,4 +72,85 @@ public class ProjectProcessService { jdbcTemplate.update("update project_process set `status`=? where id=?", status.getValue(), processId); } + /** + * 根据流程ID查询销售合同 + * + * @param processId 流程ID + * @return 销售合同 + */ + public SaleContract findSaleContract(int processId) { + TypedQuery<SaleContract> query = entityManager.createQuery( + "from SaleContract where processId=:processId", SaleContract.class); + query.setParameter("processId", processId); + return query.getSingleResult(); + } + + /** + * 根据流程ID查询采购合同 + * + * @param processId 流程ID + * @return 采购合同 + */ + public ProcurementContract findProcurementContract(int processId) { + TypedQuery<ProcurementContract> query = entityManager.createQuery( + "from ProcurementContract where processId=:processId", ProcurementContract.class); + query.setParameter("processId", processId); + return query.getSingleResult(); + } + + public ProjectProcess getById(int id) { + return entityManager.find(ProjectProcess.class, id); + } + + /** + * 更新 销售合同 的 质保期 + */ + public void updateIncomeDetails(List<SaleContractDetailForm> incomeDetails) { + if (!CollectionUtils.isEmpty(incomeDetails)) { + jdbcTemplate.batchUpdate("update project_budget_income_detail set expiration_date =? where id =? ", + new BatchPreparedStatementSetter() { + + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + SaleContractDetailForm detailForm = incomeDetails.get(i); + ps.setString(1, detailForm.getExpirationDate()); + ps.setInt(2, detailForm.getId()); + } + + @Override + public int getBatchSize() { + return incomeDetails.size(); + } + }); + } + } + + /** + * 发起流程审批 + */ + public void startAuditProgress(ProjectProcess entity) { + HashMap<String, Object> variables = new HashMap<>(); + startAuditProgress(entity, variables); + } + + /** + * 发起流程审批 + */ + @SneakyThrows + public void startAuditProgress(ProjectProcess entity, Map<String, Object> variables) { + if (entity.getStatus() == ProcessStatus.to_be_audit) { + variables.put("process", entity); + switch (entity.getProcessType()) { + case sale_contract: + projectInstanceService.startSaleContractProcess(entity.getId(), variables); + break; + case business_procurement: + projectInstanceService.startBusinessPurchaseProcess(entity.getId(), variables); + break; + default: + throw new UnsupportedOperationException("还不支持"); + } + } + } + } diff --git a/src/main/resources/templates/admin/business/process-detail.ftl b/src/main/resources/templates/admin/business/process-detail.ftl index 8d6daf3..d5c9ca6 100644 --- a/src/main/resources/templates/admin/business/process-detail.ftl +++ b/src/main/resources/templates/admin/business/process-detail.ftl @@ -59,7 +59,7 @@ <el-descriptions-item label="最终用户名称">${project.terminalCustomer}</el-descriptions-item> <el-descriptions-item label="用印类型"> - <#list process.sealTypes.sealTypes as item> + <#list process.sealTypes.list as item> <el-tag size="small">${item.description}</el-tag> </#list> </el-descriptions-item> diff --git a/src/main/resources/templates/admin/business/process-edit.ftl b/src/main/resources/templates/admin/business/process-edit.ftl new file mode 100644 index 0000000..38f340a --- /dev/null +++ b/src/main/resources/templates/admin/business/process-edit.ftl @@ -0,0 +1,847 @@ +<#assign base=request.contextPath /> +<#import "../../common/defaultLayout.ftl" as defaultLayout> +<@defaultLayout.layout> +<#-- <link rel="stylesheet" href="../assets/css/amazeui.switch.css"/>--> + <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> + <style> + + #businessPurchaseDetailsModal { + overflow: auto; + } + + #businessPurchaseDetailsModal > table { + + } + + #newBusinessProcurementContractProcess { + overflow: auto; + } + + .el-upload__input { + display: none !important; + } + + .el-textarea .el-input__count { + line-height: 15px; + } + + .admin-content-body { + margin-bottom: 100px; + } + + .el-table__empty-block { + height: 60px !important; + } + + .el-upload-list__item-name [class^="el-icon"] { + height: unset; + } + + .el-checkbox { + margin-right: 10px; + } + </style> + + <div class="admin-content" id="app"> + <div class="admin-content-body"> + <div class="am-cf am-padding"> + <div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">业务应用</strong> / + <small>{{subTitle}}</small></div> + </div> + + <div class="am-g"> + <#-- 新增销售合同流程 --> + + <div class="am-u-sm-12 am-u-md-12" v-if="isSalesContractProcessMode"> + <el-form :inline="true" ref="saleContractProcessForm" :model="processForm" label-position="right" label-width="100px"> + + <div class="am-form-inline"> + <el-form-item label="项目编号"> + <span>{{processForm.projectNo}}</span> + </el-form-item> + + <el-tooltip :disabled="!projectSelected" effect="light" :content="projectTitle" placement="top-start"> + <el-form-item label="项目标题"> + <span>{{projectTitle}}</span> + </el-form-item> + </el-tooltip> + + <el-form-item label="申请时间"> + <span>{{processForm.applyDate}}</span> + </el-form-item> + + <el-form-item label="项目类型"> + <span>{{processForm.projectType}}</span> + </el-form-item> + + <el-form-item label="合作类型"> + <span>{{processForm.cooperationType}}</span> + </el-form-item> + + </div> + + <div> + + <el-form-item label="申请部门" :rules="[{ required: true, message: '申请部门电话不能为空'}]" prop="applyDept"> + <el-cascader :options="applySectorOptions" clearable v-model="processForm.applyDept"></el-cascader> + </el-form-item> + + <el-form-item label="申请人"> + <span>{{processForm.applyPersonName}}</span> + </el-form-item> + + <el-form-item label="申请部门领导"> + <span>{{processForm.applyDeptLeaderName}}</span> + </el-form-item> + + <el-form-item label="申请人电话" :rules="[{ required: true, message: '申请人电话不能为空'}]" prop="applyPersonPhone"> + <el-input placeholder="请输入内容" v-model="processForm.applyPersonPhone"></el-input> + </el-form-item> + + </div> + + <div> + + <el-form-item label="合同编号" :rules="[{ required: true, message: '合同编号不能为空'}]" prop="contractNo"> + <el-input placeholder="请输入合同编号" v-model="processForm.contractNo"></el-input> + </el-form-item> + + <el-form-item label="合同名称" :rules="[{ required: true, message: '合同名称不能为空'}]" prop="contractName"> + <el-input placeholder="请输入合同名称" v-model="processForm.contractName"></el-input> + </el-form-item> + + <el-form-item label="合同金额"> + <span>{{processForm.contractAmount}}元</span> + </el-form-item> + + </div> + + <div> + <el-form-item label="客户名称" :rules="[{ required: true, message: '客户名称不能为空'}]"> + <el-input placeholder="请输入客户名称" v-model="processForm.clientName"></el-input> + </el-form-item> + + <el-form-item label="最终用户名称"> + <#--TODO 最终用户名称--> + <span>{{processForm.terminalCustomer}}</span> + </el-form-item> + </div> + + <div> + <el-form-item label="用印类型" :rules="[{ required: true, message: '用印类型不能为空'}]"> + <el-checkbox-group v-model="processForm.sealTypes"> + <#list sealTypes as sealType> + <el-checkbox label="${sealType.name()}" key="key-${sealType.name()}">${sealType.description}</el-checkbox> + </#list> + </el-checkbox-group> + </el-form-item> + </div> + + <div> + + <el-form-item label="税率" :rules="[{ required: true, message: '税率不能为空'}]" prop="taxRate"> + <el-select v-model="processForm.taxRate" placeholder="请选择税率"> + <#list taxRate as rate> + <el-option label="${rate}%" value="${rate}"></el-option> + </#list> + </el-select> + </el-form-item> + + <el-form-item label="是否垫资"> + <span>{{processForm.isPrepaid}}</span> + </el-form-item> + + <el-form-item label="垫资金额"> + <span>{{processForm.repaidAmount}}</span> + </el-form-item> + + <el-form-item label="预算毛利率"> + <span>{{processForm.budgetGrossMargin}}</span> + </el-form-item> + + </div> + + <div> + <el-form-item label="收款条件" :rules="[{ required: true, message: '收款条件不能为空'}]" prop="paymentTerms"> + <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 10}" cols="90" maxlength="5000" show-word-limit + v-model="processForm.paymentTerms" placeholder="请输入收款条件(限制5000字)"></el-input> + </el-form-item> + </div> + + <div> + <el-form-item label="备注"> + <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 10}" maxlength="5000" show-word-limit + v-model="processForm.remark" placeholder="请输入备注(限制5000字)" cols="90"></el-input> + </el-form-item> + </div> + + <div> + <el-form-item label="上传附件" :rules="[{ required: true, message: '未上传附件'}]"> + <el-upload class="upload-demo" + action="${base}/file/upload" + name="files[]" + :on-remove="handleRemove" + :before-remove="beforeRemove" + :on-success="handleFileUploaded" + :limit="10" :file-list="fileList" + :on-exceed="handleExceed"> + <el-button size="small" type="primary">点击上传</el-button> + <div slot="tip" class="el-upload__tip">只能上传PDF、excel、word、图片、压缩包,且不超过50MB</div> + </el-upload> + </el-form-item> + </div> + + <div> + <el-form-item label="合同清单明细"> + <el-button type="text" @click="goToSaleContractDetail">详细清单</el-button> + </el-form-item> + </div> + + </el-form> + + <#-- <el-row justify="space-around" type="flex" class="row-bg">--> + <el-row> + <el-button type="info" @click="goToHome">返回上一级</el-button> + <el-button type="primary" @click="saveDraft">保存草稿</el-button> + <el-button type="success" @click="submitForm">提交</el-button> + </el-row> + + + </div> + + <#-- 销售合同清单明细 --> + + <div class="am-u-sm-12 am-u-md-12" v-if="isSaleContractDetailMode"> + <el-table border :data="incomeDetails"> + <el-table-column type="index" :index="1" label="序号" fixed></el-table-column> + <el-table-column prop="name" label="名称" fixed width="120"></el-table-column> + <el-table-column prop="type" label="类别"></el-table-column> + <el-table-column prop="spec" label="规格型号"></el-table-column> + <el-table-column prop="param" label="参数"></el-table-column> + <el-table-column prop="unit" label="单位"></el-table-column> + <el-table-column prop="amount" label="数量"></el-table-column> + <el-table-column prop="price" label="单价" width="120"></el-table-column> + <el-table-column prop="taxRate" label="税率"></el-table-column> + <el-table-column prop="totalTaxInclude" label="含税金额" width="120"></el-table-column> + <el-table-column prop="totalTaxExclude" label="不含税金额" width="120"></el-table-column> + <el-table-column prop="totalTax" label="税金"></el-table-column> + + <el-table-column prop="expirationDate" label="质保期" fixed="right" width="150"> + <template slot-scope="scope"> + <el-input maxlength="5" size="mini" placeholder="请输入质保期" + v-model="scope.row.expirationDate"></el-input> + </template> + </el-table-column> + + </el-table> + + <el-row style="margin: 20px 0"> + <el-button type="info" @click="goToSaleContractProcess">返回上一级</el-button> + <el-button type="primary" @click="submitToSaleContractProcess">保存并返回上一级</el-button> + </el-row> + + </div> + + <#-- 新增业务采购合同流程 --> + + <div class="am-u-sm-12 am-u-md-12" id="newBusinessProcurementContractProcess" v-if="isBusinessProcurementContractProcessMode"> + <form role="form" id="newBusinessProcurementContractProcessForm"> + <div class="am-form-inline"> + + <div class="am-form-group"> + <label class="am-u-md-3 am-form-label">项目编号</label> + + <el-autocomplete v-model="processForm.serialNumber" :fetch-suggestions="queryProject" + placeholder="请输入内容" @select="handleSelectProject"></el-autocomplete> + + </div> + + <div class="am-form-group"> + <label class="am-u-md-3 am-form-label">项目标题</label> + <el-input placeholder="请输入内容" v-model="processForm.title" clearable></el-input> + </div> + <div class="am-form-group"> + <label class="am-u-md-3 am-form-label">申请时间</label> + <input type="text" class="am-form-field am-u-sm-2" :value="processForm.time"/> + </div> + <div class="am-form-group"> + <label class="am-u-sm-3 am-form-label">采购模式</label> + <input type="text" class="am-form-field" :value="processForm.time"/> + </div> + <div class="am-form-group"> + <label class="am-u-md-3 am-form-label">合作类型</label> + <input type="text" class="am-form-field" :value="processForm.time"/> + </div> + + </div> + + <div class="am-form-inline"> + + <div class="am-form-group am-u-md-4"> + <label class="am-form-label">申请部门</label> + <input type="text" class="am-form-field" :value="processForm.title"/> + <input type="text" class="am-form-field" :value="processForm.time"/> + <input type="text" class="am-form-field" :value="processForm.time"/> + </div> + + <div class="am-form-group am-u-md-4"> + <label class="am-form-label">申请人</label> + <input type="text" class="am-form-field" value="周瑾"/> + </div> + + <div class="am-form-group am-u-md-4"> + <label class="am-form-label">申请部门领导</label> + <input type="text" class="am-form-field" value="尹浩"/> + </div> + + </div> + + <div class="am-form-inline"> + + </div> + + <div class="am-form-inline"> + <button type="submit" class="am-btn am-btn-default">登录</button> + </div> + + </form> + + </div> + + <#-- 选择 业务采购清单明细 --> + + <div class="am-u-sm-12 am-u-md-12" v-if="isBusinessProcurementContractProcessMode"> + <el-table style="width: 100%" border> + + <el-table-column prop="fee" label="费用项目" width="180"></el-table-column> + <el-table-column prop="fee" label="采购类别" width="180"></el-table-column> + <el-table-column prop="fee" label="产品名称" width="180"></el-table-column> + <el-table-column prop="fee" label="单位" width="180"></el-table-column> + <el-table-column prop="fee" label="数量" width="180"></el-table-column> + <el-table-column prop="fee" label="预算单价" width="180"></el-table-column> + <el-table-column prop="fee" label="税率(%)" width="180"></el-table-column> + <el-table-column prop="fee" label="含税总金额(元)" width="180"></el-table-column> + <el-table-column prop="fee" label="不含税金额(元)" width="180"></el-table-column> + <el-table-column prop="fee" label="税金(元)" width="180"></el-table-column> + <el-table-column prop="fee" label="是否垫资" width="180"></el-table-column> + <el-table-column prop="fee" label="支出时间" width="180"></el-table-column> + <el-table-column prop="fee" label="支出金额(元)" width="180"></el-table-column> + <el-table-column prop="fee" label="已采购数量" width="180"></el-table-column> + <el-table-column prop="fee" label="本次采购数量" width="180"></el-table-column> + <el-table-column prop="fee" label="未采购数量" width="180"></el-table-column> + + + <el-table-column prop="fee" label="供应商名称"></el-table-column> + <el-table-column prop="fee" label="设备厂商名称"></el-table-column> + <el-table-column prop="fee" label="对应采购清单"></el-table-column> + <el-table-column prop="fee" label="规格型号"></el-table-column> + <el-table-column prop="fee" label="对应采购数目"></el-table-column> + <el-table-column prop="fee" label="采购单价"></el-table-column> + <el-table-column prop="fee" label="含税总金额(元)"></el-table-column> + + </el-table> + </div> + + </div> + + </div> + + </div> + + <script src="https://unpkg.com/vue@2/dist/vue.js"></script> + <script src="https://unpkg.com/element-ui/lib/index.js"></script> + + <script> + + const newBusinessProcurementContractProcess = "newBusinessProcurementContractProcess" + const saleContractProcess = "saleContractProcess" + const saleContractDetail = "saleContractDetail" + const BUTTON = "btn" + + const isEmpty = (obj) => { + return !obj || (obj.length && obj.length === 0) + } + + const isNotEmpty = (obj) => { + return !isEmpty(obj) + } + + const isBlank = (obj) => { + return isEmpty(obj) || (obj.trim && isEmpty(obj.trim())) + } + + const hasText = (obj) => { + return !isBlank(obj) + } + + const data = () => { + return { + mode: "btn", // btn , newBusinessProcurementContractProcess + processForm: { + sealTypes: [], + }, + projectSelected: false, + applySectorOptions: [ + { + value: 'zhinan', + label: '指南', + children: [{ + value: 'shejiyuanze', + label: '设计原则', + children: [{ + value: 'yizhi', + label: '一致' + }, { + value: 'fankui', + label: '反馈' + }, { + value: 'xiaolv', + label: '效率' + }, { + value: 'kekong', + label: '可控' + }] + }, { + value: 'daohang', + label: '导航', + children: [{ + value: 'cexiangdaohang', + label: '侧向导航' + }, { + value: 'dingbudaohang', + label: '顶部导航' + }] + }] + }, + { + value: 'zujian', + label: '组件', + children: [{ + value: 'basic', + label: 'Basic', + children: [{ + value: 'layout', + label: 'Layout 布局' + }, { + value: 'color', + label: 'Color 色彩' + }, { + value: 'typography', + label: 'Typography 字体' + }, { + value: 'icon', + label: 'Icon 图标' + }, { + value: 'button', + label: 'Button 按钮' + }] + }, { + value: 'form', + label: 'Form', + children: [{ + value: 'radio', + label: 'Radio 单选框' + }, { + value: 'checkbox', + label: 'Checkbox 多选框' + }, { + value: 'input', + label: 'Input 输入框' + }, { + value: 'input-number', + label: 'InputNumber 计数器' + }, { + value: 'select', + label: 'Select 选择器' + }, { + value: 'cascader', + label: 'Cascader 级联选择器' + }, { + value: 'switch', + label: 'Switch 开关' + }, { + value: 'slider', + label: 'Slider 滑块' + }, { + value: 'time-picker', + label: 'TimePicker 时间选择器' + }, { + value: 'date-picker', + label: 'DatePicker 日期选择器' + }, { + value: 'datetime-picker', + label: 'DateTimePicker 日期时间选择器' + }, { + value: 'upload', + label: 'Upload 上传' + }, { + value: 'rate', + label: 'Rate 评分' + }, { + value: 'form', + label: 'Form 表单' + }] + }, { + value: 'data', + label: 'Data', + children: [{ + value: 'table', + label: 'Table 表格' + }, { + value: 'tag', + label: 'Tag 标签' + }, { + value: 'progress', + label: 'Progress 进度条' + }, { + value: 'tree', + label: 'Tree 树形控件' + }, { + value: 'pagination', + label: 'Pagination 分页' + }, { + value: 'badge', + label: 'Badge 标记' + }] + }, { + value: 'notice', + label: 'Notice', + children: [{ + value: 'alert', + label: 'Alert 警告' + }, { + value: 'loading', + label: 'Loading 加载' + }, { + value: 'message', + label: 'Message 消息提示' + }, { + value: 'message-box', + label: 'MessageBox 弹框' + }, { + value: 'notification', + label: 'Notification 通知' + }] + }, { + value: 'navigation', + label: 'Navigation', + children: [{ + value: 'menu', + label: 'NavMenu 导航菜单' + }, { + value: 'tabs', + label: 'Tabs 标签页' + }, { + value: 'breadcrumb', + label: 'Breadcrumb 面包屑' + }, { + value: 'dropdown', + label: 'Dropdown 下拉菜单' + }, { + value: 'steps', + label: 'Steps 步骤条' + }] + }, { + value: 'others', + label: 'Others', + children: [{ + value: 'dialog', + label: 'Dialog 对话框' + }, { + value: 'tooltip', + label: 'Tooltip 文字提示' + }, { + value: 'popover', + label: 'Popover 弹出框' + }, { + value: 'card', + label: 'Card 卡片' + }, { + value: 'carousel', + label: 'Carousel 走马灯' + }, { + value: 'collapse', + label: 'Collapse 折叠面板' + }] + }] + }, + { + value: 'ziyuan', + label: '资源', + children: [{ + value: 'axure', + label: 'Axure Components' + }, { + value: 'sketch', + label: 'Sketch Templates' + }, { + value: 'jiaohu', + label: '组件交互文档' + }] + } + ], + fileList: [], + // 销售合同收入明细 + incomeDetails: [], + } + } + + const methods = { + changeMode(mode) { + this.mode = mode + }, + + businessProcurementProcessClick() { + const that = this + }, + + goToHome() { + this.changeMode(BUTTON) + }, + + goToSaleContractProcess() { + this.changeMode(saleContractProcess) + }, + goToSaleContractDetail() { + const { projectId } = this.processForm + if (projectId) { + this.changeMode(saleContractDetail) + } + else { + this.$message.warning("项目还未选择") + } + }, + + render(obj) { + console.log(obj) + }, + + initForm(form) { + this.processForm = { ...form } + console.log(form) + }, + + queryProject(q, callback) { + if (isBlank(q)) { + return + } + fetch("${base}/process/projects?q=" + q) + .then(res => res.json()) + .then(data => { + if (data.length === 0) { + callback([{ + name: '未搜索到结果' + }]) + } + else { + callback(data) + } + }) + .catch(err => { + this.$message.error('项目搜索失败'); + }) + }, + handleSelectProject(id) { + const loading = this.$loading({ + lock: true, + text: '正在加载项目', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }) + + fetch("${base}/process/" + id) + .then(res => res.json()) + .then(data => { + const { incomeDetails, process, contract, ...form } = data + // 转换数据 + // @formatter:off + const computeType = (type) => { + switch (type) { + case 1: return '设备' + case 2: return '工程' + case 3: return '服务' + default: return '未知' + } + } + // @formatter:on + + const applyDept = process.applyDept.split(',') + this.initForm({ ...form, ...process, ...contract, applyDept }) + this.projectSelected = true + this.incomeDetails = incomeDetails.map(detail => ({ + ...detail, type: computeType(detail.type) + })) + this.fileList = JSON.parse(process.attachmentUri).map(item => ({ + name: item.name, url: item.uri + })) + + if (process.processType === 'sale_contract') { + this.changeMode(saleContractProcess) + } + else if (process.processType === 'business_procurement') { + this.changeMode(newBusinessProcurementContractProcess) + } + + }) + .catch(err => { + this.$message.error("项目'" + name + "'加载失败"); + }) + .finally(() => loading.close()) + }, + clearProjectProcess() { + this.projectSelected = false + this.initForm({}) + this.incomeDetails = [] + }, + + saveDraft() { + this.processForm.status = 'draft' + this.submit() + }, + + submitForm() { + this.processForm.status = 'to_be_audit' + this.submit() + }, + + submit() { + this.$refs["saleContractProcessForm"].validate((valid) => { + if (valid) { + const fileList = this.fileList + if (fileList.length === 0) { + this.$message.error("未上传附件"); + return false + } + const loading = this.$loading({ + lock: true, + text: '正在提交', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }) + + const form = { + ...this.processForm, + attachments: fileList.map(file => { + if (file.url) { + return { + uri: file.url, + name: file.name + } + } + return { + uri: file.response.data.url, + name: file.name + } + }), + incomeDetails: this.incomeDetails.map(detail => ({ + id: detail.id, expirationDate: detail.expirationDate + })) + } + + fetch("${base}/process", { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(form), + }).then(response => { + if (response.ok) { + this.$message({ + showClose: true, + message: '提交成功', + type: 'success' + }) + } + else { + return Promise.reject("失败") + } + }).catch(err => { + this.$message.error("项目提交失败"); + }).finally(() => loading.close()) + } + else { + return false; + } + }) + }, + + submitToSaleContractProcess() { + this.goToSaleContractProcess() + }, + + handleRemove(file, fileList) { + this.fileList = fileList + }, + + handleExceed(files, fileList) { + this.$message.warning("当前限制选择只能选择10个文件"); + this.fileList = fileList + }, + + beforeRemove(file, fileList) { + return this.$confirm("确定移除 " + file.name + "?"); + }, + + handleFileUploaded(response, file, fileList) { + if (response.success) { + this.fileList = fileList + } + else { + this.$message.warning("上传失败"); + } + }, + + indexMethod(index) { + return index * 1; + } + } + + new Vue({ + el: '#app', + data, + computed: { + projectTitle() { + const { projectNo, projectName, applyPersonName, applyDate } = this.processForm + if (projectNo && projectName) { + return projectNo.trim() + "-" + projectName.trim() + "-" + applyPersonName + "-" + applyDate + } + return "" + }, + isButtonMode() { + return this.mode === BUTTON + }, + isBusinessProcurementContractProcessMode() { + return this.mode === newBusinessProcurementContractProcess + }, + isSalesContractProcessMode() { + return this.mode === saleContractProcess + }, + isSaleContractDetailMode() { + return this.mode === saleContractDetail + }, + subTitle() { + switch (this.mode) { + case BUTTON: + return "新增流程" + case saleContractProcess: + return "编辑销售合同流程" + case saleContractDetail: + return "销售合同清单明细" + case newBusinessProcurementContractProcess: + return "编辑业务采购合同流程" + } + } + }, + + methods, + + mounted() { + const processId = ${processId} + this.handleSelectProject(processId) + }, + }) + + </script> + +</@defaultLayout.layout> + diff --git a/src/main/resources/templates/admin/business/process-new.ftl b/src/main/resources/templates/admin/business/process-new.ftl index e9a9450..8cfc765 100644 --- a/src/main/resources/templates/admin/business/process-new.ftl +++ b/src/main/resources/templates/admin/business/process-new.ftl @@ -36,7 +36,8 @@ .el-upload-list__item-name [class^="el-icon"] { height: unset; } - .el-checkbox{ + + .el-checkbox { margin-right: 10px; } </style> @@ -244,7 +245,7 @@ <el-row> <el-button type="info" @click="goToHome">返回上一级</el-button> <el-button type="primary" @click="saveDraft">保存草稿</el-button> - <el-button type="success" @click="submit">提交</el-button> + <el-button type="success" @click="submitForm">提交</el-button> </el-row> @@ -734,6 +735,11 @@ this.submit() }, + submitForm() { + this.processForm.status = 'to_be_audit' + this.submit() + }, + submit() { this.$refs["saleContractProcessForm"].validate((valid) => { if (valid) {