xiongzhu 5 lat temu
rodzic
commit
05fbc1dbd7
23 zmienionych plików z 422 dodań i 1896 usunięć
  1. 3 0
      db/migrate005.sql
  2. 25 2
      src/main/java/com/izouma/zhumj/domain/sale/ContractViolation.java
  3. 13 16
      src/main/java/com/izouma/zhumj/dto/report/RevocationReport.java
  4. 6 0
      src/main/java/com/izouma/zhumj/repo/ContractViolationRepo.java
  5. 105 0
      src/main/java/com/izouma/zhumj/service/ContractViolationService.java
  6. 125 2
      src/main/java/com/izouma/zhumj/service/FinancialService.java
  7. 20 20
      src/main/java/com/izouma/zhumj/service/report/SaleReportService.java
  8. 7 1
      src/main/java/com/izouma/zhumj/service/sale/ContractService.java
  9. 18 17
      src/main/java/com/izouma/zhumj/service/sale/SaleWorkService.java
  10. 4 8
      src/main/java/com/izouma/zhumj/web/ContractViolationController.java
  11. 0 746
      src/main/vue/src/components/ResidenceData.vue
  12. 0 5
      src/main/vue/src/components/operation/ContractList.vue
  13. 1 17
      src/main/vue/src/router.js
  14. 8 0
      src/main/vue/src/views/Admin.vue
  15. 4 0
      src/main/vue/src/views/ContractViolationEdit.vue
  16. 18 12
      src/main/vue/src/views/ContractViolationList.vue
  17. 0 170
      src/main/vue/src/views/ResidenceRoomTypeList.vue
  18. 33 14
      src/main/vue/src/views/sale/ContractEdit.vue
  19. 14 85
      src/main/vue/src/views/sale/ContractList.vue
  20. 1 325
      src/main/vue/src/views/sale/RecoveryContractList.vue
  21. 0 173
      src/main/vue/src/views/sale/ResidenceEdit.vue
  22. 0 283
      src/main/vue/src/views/sale/ResidenceList.vue
  23. 17 0
      src/test/java/com/izouma/zhumj/service/ContractViolationServiceTest.java

+ 3 - 0
db/migrate005.sql

@@ -19,6 +19,9 @@ alter table contract_store add constraint FKpq2qnjfvfws7t5tkg0rmaav5s foreign ke
 drop index FK6vc2qkse1t8gnh0k5589foko5 on contract_room_type;
 drop index FKiq6khr7imvc2bgbq6pgbmxnvb on contract_room_type;
 
+update menu set path = '/contractViolationList' where id = 23871;
+delete from menu where id = 637;
+
 
 
 

+ 25 - 2
src/main/java/com/izouma/zhumj/domain/sale/ContractViolation.java

@@ -1,6 +1,7 @@
 package com.izouma.zhumj.domain.sale;
 
 import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.zhumj.domain.BaseEntity;
 import com.izouma.zhumj.enums.PenaltyType;
 import com.izouma.zhumj.enums.ViolationType;
@@ -12,6 +13,7 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.*;
+import javax.validation.constraints.NotNull;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.List;
@@ -27,9 +29,19 @@ public class ContractViolation extends BaseEntity {
     @ApiModelProperty("合同ID")
     private Long contractId;
 
+    @ApiModelProperty(value = "销售员ID", name = "saleId")
+    private Long saleId;
+
+    @ApiModelProperty(value = "销售员", name = "saleName")
+    private String saleName;
+
     @ApiModelProperty("合同编号")
     private String contractNumber;
 
+    private String coFullName;
+
+    private String coSimpleName;
+
     @Enumerated(EnumType.STRING)
     @ApiModelProperty("违约类型")
     private ViolationType violationType;
@@ -40,6 +52,9 @@ public class ContractViolation extends BaseEntity {
     @ApiModelProperty("剩余天数")
     private int days;
 
+    @ApiModelProperty("押金")
+    private BigDecimal deposit;
+
     @ApiModelProperty("剩余房费")
     private BigDecimal restRent;
 
@@ -62,8 +77,16 @@ public class ContractViolation extends BaseEntity {
     @ApiModelProperty("是否流失")
     private boolean customerLost;
 
-    @OneToMany(fetch = FetchType.LAZY)
-    @JoinColumn(name = "residenceId", insertable = false, updatable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
+    @ApiModelProperty("门店")
+    @Column(columnDefinition = "TEXT")
+    private String originalStoreName;
+
+    @ApiModelProperty("房型")
+    @Column(columnDefinition = "TEXT")
+    private String originalRoomType;
+
+    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+    @JoinColumn(name = "violationId", insertable = false, updatable = false)
     @ExcelIgnore
     private List<ViolationRoomType> violationRoomTypes;
 

+ 13 - 16
src/main/java/com/izouma/zhumj/dto/report/RevocationReport.java

@@ -2,11 +2,9 @@ package com.izouma.zhumj.dto.report;
 
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
-import com.izouma.zhumj.domain.sale.Contract;
-import com.izouma.zhumj.domain.sale.Customer;
-import com.izouma.zhumj.domain.sale.Residence;
-import com.izouma.zhumj.domain.sale.ResidenceRoomType;
+import com.izouma.zhumj.domain.sale.*;
 import com.izouma.zhumj.enums.CustomerLoss;
+import com.izouma.zhumj.enums.PenaltyType;
 import io.swagger.annotations.ApiModel;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
@@ -81,25 +79,24 @@ public class RevocationReport {
     @ExcelProperty("销售员")
     private String saleName;
 
-    public static RevocationReport from(Contract contract, Residence residence) {
+    public static RevocationReport from(Contract contract, ContractViolation violation) {
         return builder()
                 .customerNumber(contract.getContractNumber())
                 .contractNumber(contract.getContractNumber())
                 .coFullName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoFullName).orElse(null))
                 .coSimpleName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoSimpleName).orElse(null))
-                .storeName(residence.getOriginalStoreName())
-                .roomType(residence.getOriginalRoomType())
-                .revocationBedsAmount(residence.getResidenceRoomTypes().stream()
-                        .filter(residenceRoomType -> residenceRoomType.getResidenceBeds() != null)
-                        .mapToInt(ResidenceRoomType::getResidenceBeds).sum())
+                .storeName(violation.getOriginalStoreName())
+                .roomType(violation.getOriginalRoomType())
+                .revocationBedsAmount(violation.getViolationRoomTypes().stream()
+                        .mapToInt(ViolationRoomType::getBeds).sum())
                 .contractBeginTime(contract.getContractBeginTime())
                 .contractEndTime(contract.getContractEndTime())
-                .revocationTime(residence.getResidenceTime())
-                .revocationReason(residence.getResidenceReason())
-                .deposit(residence.getDeposit())
-                .restTotalRent(residence.getSurplusRoomFee())
-                .takeDeposit(residence.getFalsifyType() == 2)
-                .customerLost(residence.getCustomerLoss() == CustomerLoss.YES)
+                .revocationTime(violation.getViolationTime())
+                .revocationReason(violation.getReason())
+                .deposit(violation.getDeposit())
+                .restTotalRent(violation.getRestRent())
+                .takeDeposit(violation.getPenaltyType() == PenaltyType.DEPOSIT)
+                .customerLost(violation.isCustomerLost())
                 .customerIndustry(contract.getCustomerIndustry())
                 .note(contract.getNote())
                 .saleName(contract.getSaleName())

+ 6 - 0
src/main/java/com/izouma/zhumj/repo/ContractViolationRepo.java

@@ -4,5 +4,11 @@ import com.izouma.zhumj.domain.sale.ContractViolation;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
+import java.time.LocalDateTime;
+import java.util.List;
+
 public interface ContractViolationRepo extends JpaRepository<ContractViolation, Long>, JpaSpecificationExecutor<ContractViolation> {
+    List<ContractViolation> findByViolationTimeBetween(LocalDateTime start, LocalDateTime end);
+
+    List<ContractViolation> findBySaleIdAndCreatedAtBetween(Long saleId, LocalDateTime start, LocalDateTime end);
 }

+ 105 - 0
src/main/java/com/izouma/zhumj/service/ContractViolationService.java

@@ -1,13 +1,118 @@
 package com.izouma.zhumj.service;
 
+import com.alibaba.druid.wall.Violation;
+import com.alibaba.fastjson.JSONObject;
+import com.izouma.zhumj.domain.StoreNotice;
+import com.izouma.zhumj.domain.sale.*;
+import com.izouma.zhumj.enums.*;
+import com.izouma.zhumj.exception.BusinessException;
 import com.izouma.zhumj.repo.ContractViolationRepo;
+import com.izouma.zhumj.repo.StoreNoticeRepo;
+import com.izouma.zhumj.repo.sale.ContractRepo;
+import com.izouma.zhumj.repo.sale.ContractRoomRepo;
+import com.izouma.zhumj.repo.sale.ContractRoomTypeRepo;
+import com.izouma.zhumj.repo.sale.ResidenceRepo;
+import com.izouma.zhumj.utils.ObjUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
 
 @Service
 @AllArgsConstructor
 public class ContractViolationService {
 
     private ContractViolationRepo contractViolationRepo;
+    private ContractRepo          contractRepo;
+    private StoreNoticeRepo       storeNoticeRepo;
+    private ResidenceRepo         residenceRepo;
+    private ContractRoomTypeRepo  contractRoomTypeRepo;
+
+    public ContractViolation save(ContractViolation record) {
+        if (record.getId() != null) {
+            ContractViolation orig = contractViolationRepo.findById(record.getId())
+                    .orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return contractViolationRepo.save(orig);
+        }
+
+        Contract contract = contractRepo.findByContractNumber(record.getContractNumber());
+        if (contract == null) {
+            throw new BusinessException("合同不存在");
+        }
+        record.setSaleId(contract.getSaleId());
+        record.setSaleName(contract.getSaleName());
+        record.setOriginalStoreName(contract.getStoreName());
+        record.setOriginalRoomType(contract.getRoomTypeDesc());
+        record.setDeposit(contract.getFlowBet());
+        record.setCoFullName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoFullName).orElse(null));
+        record.setCoSimpleName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoSimpleName).orElse(null));
+        Map<Long, Long> storeId = new HashMap<>();
+        record = contractViolationRepo.save(record);
+        if (ViolationType.PART == record.getViolationType()) {
+            for (ViolationRoomType roomType : record.getViolationRoomTypes()) {
+                if (ObjectUtils.isEmpty(storeId.get(roomType.getStoreId()))) {
+                    StoreNotice storeNotice = new StoreNotice();
+                    storeNotice.setContractNumbers(record.getContractNumber());
+                    storeNotice.setStoreId(roomType.getStoreId());
+                    storeNotice.setStoreNoticeType(StoreNoticeType.BREACH);
+                    storeNotice.setRemark("退宿提醒,合同号:" + record.getContractNumber());
+                    storeNotice.setNoticeTime(record.getViolationTime());
+                    storeNotice.setStatus(StoreNoticeStatus.UNREAD);
+                    JSONObject jsonObject = new JSONObject();
+                    jsonObject.put("contractNumber", record.getContractNumber());
+                    storeNotice.setAttach(jsonObject);
+                    storeId.put(roomType.getStoreId(), roomType.getStoreId());
+                    storeNoticeRepo.save(storeNotice);
+                }
+            }
+        } else if (ViolationType.ALL == record.getViolationType()) {
+            contract.setStatus(ContractStatus.RETREAT);
+            contractRepo.save(contract);
+        }
+        return record;
+    }
 
+    public void migrate() {
+        for (Residence residence : residenceRepo.findAll()) {
+            Contract contract = contractRepo.findById(residence.getContractId()).orElse(null);
+            if (contract == null) continue;
+            ContractViolation violation = ContractViolation.builder()
+                    .contractId(residence.getContractId())
+                    .coFullName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoFullName).orElse(null))
+                    .coSimpleName(Optional.ofNullable(contract.getCustomer()).map(Customer::getCoSimpleName).orElse(null))
+                    .saleId(residence.getSaleId())
+                    .saleName(residence.getSaleName())
+                    .contractNumber(residence.getContractNumber())
+                    .penaltyType(residence.getFalsifyType() == 1 ? PenaltyType.RENT : PenaltyType.DEPOSIT)
+                    .restRent(residence.getSurplusRoomFee())
+                    .deposit(residence.getDeposit())
+                    .penalty(residence.getRent())
+                    .attachNumber(residence.getInformAgreementNo())
+                    .violationTime(residence.getResidenceTime())
+                    .violationType(residence.getResidenceType() == ResidenceType.AllRETIREMENT ? ViolationType.ALL : ViolationType.PART)
+                    .reason(residence.getResidenceReason())
+                    .customerLost(residence.getCustomerLoss() == CustomerLoss.YES)
+                    .attach(residence.getAttach())
+                    .violationRoomTypes(new ArrayList<>())
+                    .build();
+            for (ResidenceRoomType residenceRoomType : residence.getResidenceRoomTypes()) {
+                ContractRoomType contractRoomType = contractRoomTypeRepo.findById(residenceRoomType.getContractRoomId())
+                        .orElse(null);
+                if (contractRoomType == null) continue;
+                violation.getViolationRoomTypes().add(ViolationRoomType.builder()
+                        .storeId(residenceRoomType.getStoreId())
+                        .storeName(contractRoomType.getStoreName())
+                        .roomTypeId(contractRoomType.getRoomTypeId())
+                        .roomTypeName(contractRoomType.getRoomTypeName())
+                        .beds(Optional.ofNullable(residenceRoomType.getResidenceBeds()).orElse(0))
+                        .build());
+            }
+            contractViolationRepo.save(violation);
+        }
+    }
 }

+ 125 - 2
src/main/java/com/izouma/zhumj/service/FinancialService.java

@@ -299,8 +299,8 @@ public class FinancialService {
         head.addAll(feeNames);
         head.addAll(Arrays.asList("其他费用", "总收入", "总床位数", "出租率", "平均床价"));
         businessReport.add(head);
-        for (StoreInfo storeInfo : storeInfoList) {
 
+        storeInfoList.stream().parallel().forEach(storeInfo -> {
             List<String> list = new ArrayList<>();
             list.add(storeInfo.getStoreName());
             BigDecimal total = BigDecimal.ZERO;
@@ -421,7 +421,130 @@ public class FinancialService {
                 list.add(rate.divide(BigDecimal.valueOf(roomRateList.size()), 2, RoundingMode.HALF_UP).toString());
             }
             businessReport.add(list);
-        }
+        });
+//        for (StoreInfo storeInfo : storeInfoList) {
+//
+//            List<String> list = new ArrayList<>();
+//            list.add(storeInfo.getStoreName());
+//            BigDecimal total = BigDecimal.ZERO;
+//
+//            //个人押金
+//            BigDecimal deposit = BigDecimal.ZERO;
+//            for (DepositRecord depositRecord : depositRecordRepo
+//                    .findByCheckinInfoStoreIdAndCreatedAtBetween(storeInfo.getId(), start, end)) {
+//                deposit = deposit.add(depositRecord.getAmount());
+//            }
+//            list.add(deposit.toString());
+//            total = total.add(deposit);
+//
+//            //团队押金
+//            BigDecimal contractDeposit = BigDecimal.ZERO;
+//            for (Contract contract : contractRepo.findStoreContract(storeInfo.getId(), start, end)) {
+//                contractDeposit = contractDeposit.add(contract.getFlowBet());
+//            }
+//            list.add(contractDeposit.toString());
+//            total = total.add(contractDeposit);
+//
+//            List<RoomRate> roomRates = roomRateRepo.findByStoreIdAndDateBetween(storeInfo.getId(),
+//                    start.toLocalDate(), end.toLocalDate());
+//            //团队房费
+//            BigDecimal teamRent = roomRates.stream()
+//                    .filter(roomRate -> roomRate.getCheckInType() != CheckInType.INDIVIDUAL)
+//                    .map(RoomRate::getDayRate)
+//                    .filter(Objects::nonNull)
+//                    .reduce(BigDecimal::add)
+//                    .orElse(BigDecimal.ZERO);
+//            list.add(teamRent.toString());
+//            total = total.add(teamRent);
+//
+//            //散客房费
+//            BigDecimal individualRent = roomRates.stream()
+//                    .filter(roomRate -> roomRate.getCheckInType() == CheckInType.INDIVIDUAL)
+//                    .map(RoomRate::getDayRate)
+//                    .filter(Objects::nonNull)
+//                    .reduce(BigDecimal::add)
+//                    .orElse(BigDecimal.ZERO);
+//            list.add(individualRent.toString());
+//            total = total.add(individualRent);
+//
+//            List<FeeType> feeTypeList = feeTypeRepo.findByStoreId(storeInfo.getId());
+//            List<RoomFee> roomFeeList = roomFeeRepo.findByStoreIdAndSettleTimeBetween(storeInfo.getId(), start, end);
+//            List<PersonalFeeType> personalFeeTypeList = personalFeeTypeRepo.findByStoreId(storeInfo.getId());
+//            List<PersonalFee> personalFeeList = personalFeeRepo
+//                    .findByStoreIdAndSettleTimeBetween(storeInfo.getId(), start, end);
+//            for (String feeName : feeNames) {
+//                Set<Long> feeTypeIds = feeTypeList.stream().filter(feeType -> feeType.getName().equals(feeName))
+//                        .map(FeeType::getId).collect(Collectors.toSet());
+//                Set<Long> personalFeeTypeIds = personalFeeTypeList.stream()
+//                        .filter(personalFeeType -> personalFeeType.getName().equals(feeName))
+//                        .map(PersonalFeeType::getId).collect(Collectors.toSet());
+//                BigDecimal fee = roomFeeList.stream()
+//                        .filter(roomFee -> feeTypeIds.stream()
+//                                .anyMatch(longVal -> longVal.equals(roomFee.getFeeTypeId())))
+//                        .map(RoomFee::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
+//                        .add(personalFeeList.stream()
+//                                .filter(personalFee -> personalFeeTypeIds.stream().anyMatch(longVal ->
+//                                        longVal.equals(personalFee.getPersonalFeeTypeId())))
+//                                .map(PersonalFee::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
+//
+//                list.add(fee.toString());
+//                total = total.add(fee);
+//            }
+//
+//            //其他费用
+//            Set<Long> otherFeeTypeIds = feeTypeList.stream()
+//                    .filter(feeType -> feeNames.stream().noneMatch(name -> feeType.getName().equals(name)))
+//                    .map(FeeType::getId).collect(Collectors.toSet());
+//            Set<Long> otherPersonalFeeTypeIds = personalFeeTypeList.stream()
+//                    .filter(personalFeeType -> feeNames.stream()
+//                            .noneMatch(name -> personalFeeType.getName().equals(name)))
+//                    .map(PersonalFeeType::getId).collect(Collectors.toSet());
+//            BigDecimal otherFee = roomFeeList.stream()
+//                    .filter(roomFee -> otherFeeTypeIds.stream()
+//                            .anyMatch(longVal -> longVal.equals(roomFee.getFeeTypeId())))
+//                    .map(RoomFee::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
+//                    .add(personalFeeList.stream()
+//                            .filter(personalFee -> otherPersonalFeeTypeIds.stream().anyMatch(longVal ->
+//                                    longVal.equals(personalFee.getPersonalFeeTypeId())))
+//                            .map(PersonalFee::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
+//            list.add(otherFee.toString());
+//            total = total.add(otherFee);
+//
+//            //总收入
+//            list.add(total.toString());
+//
+//            //总床位数
+//            Long bedNum = bedInfoRepo.countAllByStoreId(storeInfo.getId());
+//            list.add(bedNum.toString());
+//
+//            //出租率
+//            list.add(BigDecimal.valueOf(roomRateRepo
+//                    .countByStoreIdAndDateBetween(storeInfo.getId(), start.toLocalDate(), end.toLocalDate()))
+//                    .divide(BigDecimal.valueOf(bedNum * ChronoUnit.DAYS
+//                            .between(start.toLocalDate(), end.toLocalDate()) + 1), 4, BigDecimal.ROUND_HALF_UP)
+//                    .multiply(BigDecimal.valueOf(100))
+//                    .setScale(2, RoundingMode.HALF_UP)
+//                    .toString() + "%");
+//
+//            //平均床价
+//            List<RoomRate> roomRateList = roomRateRepo
+//                    .findByStoreIdAndDateBetween(storeInfo.getId(), start.toLocalDate(), end.toLocalDate());
+//            BigDecimal rate = roomRateList.stream().map(roomRate -> {
+//                if (roomRate.getDayRate() != null) {
+//                    return roomRate.getDayRate();
+//                }
+//                if (roomRate.getMonthRate() != null) {
+//                    return roomRate.getMonthRate().divide(BigDecimal.valueOf(30), 2, RoundingMode.HALF_UP);
+//                }
+//                return BigDecimal.ZERO;
+//            }).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
+//            if (roomRateList.size() == 0) {
+//                list.add("0");
+//            } else {
+//                list.add(rate.divide(BigDecimal.valueOf(roomRateList.size()), 2, RoundingMode.HALF_UP).toString());
+//            }
+//            businessReport.add(list);
+//        }
         return businessReport;
     }
 

+ 20 - 20
src/main/java/com/izouma/zhumj/service/report/SaleReportService.java

@@ -29,17 +29,17 @@ import java.util.stream.Collectors;
 @Service
 @AllArgsConstructor
 public class SaleReportService {
-    private ContractRepo        contractRepo;
-    private StoreInfoRepo       storeInfoRepo;
-    private RoomTypeInfoRepo    roomTypeInfoRepo;
-    private PersonalFeeTypeRepo personalFeeTypeRepo;
-    private ContractBillRepo    contractBillRepo;
-    private CustomerRepo        customerRepo;
-    private BillDetailRepo      billDetailRepo;
-    private ResidenceRepo       residenceRepo;
-    private UserService         userService;
-    private CheckinInfoRepo     checkinInfoRepo;
-    private SaleParamConfigRepo saleParamConfigRepo;
+    private ContractRepo          contractRepo;
+    private StoreInfoRepo         storeInfoRepo;
+    private RoomTypeInfoRepo      roomTypeInfoRepo;
+    private PersonalFeeTypeRepo   personalFeeTypeRepo;
+    private ContractBillRepo      contractBillRepo;
+    private CustomerRepo          customerRepo;
+    private BillDetailRepo        billDetailRepo;
+    private ContractViolationRepo contractViolationRepo;
+    private UserService           userService;
+    private CheckinInfoRepo       checkinInfoRepo;
+    private SaleParamConfigRepo   saleParamConfigRepo;
 
     @Cacheable("contractReport")
     public List<ContractReport> contractReport(LocalDate start, LocalDate end) {
@@ -205,12 +205,12 @@ public class SaleReportService {
         LocalDateTime endTime = end.atTime(Constants.TIME_MAX);
         AtomicInteger idx = new AtomicInteger();
         List<RevocationReport> revocationReports = new ArrayList<>();
-        for (Residence residence : residenceRepo.findByResidenceTimeBetween(startTime, endTime)) {
-            Contract contract = contractRepo.findByContractNumber(residence.getContractNumber());
+        for (ContractViolation violation : contractViolationRepo.findByViolationTimeBetween(startTime, endTime)) {
+            Contract contract = contractRepo.findByContractNumber(violation.getContractNumber());
             if (contract == null) {
                 return null;
             }
-            RevocationReport revocationReport = RevocationReport.from(contract, residence);
+            RevocationReport revocationReport = RevocationReport.from(contract, violation);
             revocationReport.setIdx(idx.incrementAndGet());
             revocationReports.add(revocationReport);
         }
@@ -240,15 +240,15 @@ public class SaleReportService {
         LocalDateTime start = date.atStartOfDay();
         LocalDateTime end = date.atTime(Constants.TIME_MAX);
         return userService.allSales(false).stream().map(user -> {
-            List<Residence> residenceList = residenceRepo.findAllBySaleIdAndCreatedAtBetween(user.getId(), start, end);
-            String reason = residenceList.stream()
-                    .map(Residence::getResidenceReason)
+            List<ContractViolation> violationList = contractViolationRepo.findBySaleIdAndCreatedAtBetween(user.getId(), start, end);
+            String reason = violationList.stream()
+                    .map(ContractViolation::getReason)
                     .filter(StringUtils::isNotBlank)
                     .collect(Collectors.joining(";"));
-            int residenceBeds = residenceList
+            int residenceBeds = violationList
                     .stream()
-                    .flatMap(residence -> residence.getResidenceRoomTypes().stream())
-                    .mapToInt(ResidenceRoomType::getResidenceBeds)
+                    .flatMap(violation -> violation.getViolationRoomTypes().stream())
+                    .mapToInt(ViolationRoomType::getBeds)
                     .sum();
             residenceBeds += contractRepo.findBySaleIdAndContractEndTimeBetween(user.getId(), start, end)
                     .stream()

+ 7 - 1
src/main/java/com/izouma/zhumj/service/sale/ContractService.java

@@ -196,7 +196,8 @@ public class ContractService {
 
     @Transactional(rollbackOn = {Exception.class})
     public Contract save(Contract contract, boolean saveHistory, String remark) {
-
+        if (contract.getContractNumber() == null) throw new BusinessException("缺少合同编号");
+        contract.setContractNumber(contract.getContractNumber().trim());
         boolean insert = true;
         boolean renewal = false;
         if (contract.getCheckInType().equals(CheckInType.INDIVIDUAL) || contract.getCheckInType()
@@ -224,6 +225,11 @@ public class ContractService {
             ObjUtils.merge(orig, contract);
             insert = false;
         } else {
+            Contract c = contractRepo.findByContractNumber(contract.getContractNumber());
+            if (c != null) {
+                throw new BusinessException("合同编号已存在");
+            }
+
             if (StringUtils.isNotEmpty(contract.getFirstContractNumber())) {
                 renewal = true;
                 contract.setContractSource(ContractSource.RENEWAL);

+ 18 - 17
src/main/java/com/izouma/zhumj/service/sale/SaleWorkService.java

@@ -7,6 +7,7 @@ import com.izouma.zhumj.domain.sale.*;
 import com.izouma.zhumj.dto.sale.*;
 import com.izouma.zhumj.enums.BillStatus;
 import com.izouma.zhumj.repo.CheckinInfoRepo;
+import com.izouma.zhumj.repo.ContractViolationRepo;
 import com.izouma.zhumj.repo.UserRepo;
 import com.izouma.zhumj.repo.sale.*;
 import com.izouma.zhumj.service.DepartmentService;
@@ -31,17 +32,17 @@ import java.util.concurrent.atomic.AtomicReference;
 @Service
 @AllArgsConstructor
 public class SaleWorkService {
-    private ContractRepo        contractRepo;
-    private CustomerRepo        customerRepo;
-    private RenewalRepo         renewalRepo;
-    private ResidenceRepo       residenceRepo;
-    private UserService         userService;
-    private UserRepo            userRepo;
-    private CheckinInfoRepo     checkinInfoRepo;
-    private ContractStoreRepo   contractStoreRepo;
-    private DepartmentService   departmentService;
-    private SaleParamConfigRepo configRepo;
-    private ContractBillRepo    contractBillRepo;
+    private ContractRepo          contractRepo;
+    private CustomerRepo          customerRepo;
+    private RenewalRepo           renewalRepo;
+    private UserService           userService;
+    private ContractViolationRepo contractViolationRepo;
+    private UserRepo              userRepo;
+    private CheckinInfoRepo       checkinInfoRepo;
+    private ContractStoreRepo     contractStoreRepo;
+    private DepartmentService     departmentService;
+    private SaleParamConfigRepo   configRepo;
+    private ContractBillRepo      contractBillRepo;
 
     /**
      * 首页销售数据
@@ -104,8 +105,8 @@ public class SaleWorkService {
         //退宿床位数
         AtomicInteger residenceBeds = new AtomicInteger();
 
-        List<Residence> residences = residenceRepo.findAllBySaleIdAndCreatedAtBetween(saleId, begin, end);
-        residences.forEach(residence -> {
+        List<ContractViolation> violationList = contractViolationRepo.findBySaleIdAndCreatedAtBetween(saleId, begin, end);
+        violationList.forEach(residence -> {
 //            residenceBeds.addAndGet(residence.getResidenceBeds());
         });
 
@@ -181,8 +182,8 @@ public class SaleWorkService {
         //退宿床位数
         AtomicInteger residenceBeds = new AtomicInteger();
 
-        List<Residence> residences = residenceRepo.findAllBySaleIdAndCreatedAtBetween(saleId, begin, end);
-        residences.forEach(residence -> {
+        List<ContractViolation> violationList = contractViolationRepo.findBySaleIdAndCreatedAtBetween(saleId, begin, end);
+        violationList.forEach(residence -> {
 //            residenceBeds.addAndGet(residence.getResidenceBeds());
         });
 
@@ -271,10 +272,10 @@ public class SaleWorkService {
                     renewalBeds.set(renewalBeds.get() + room.getBeds());
                 });
             }
-            List<Residence> residences = residenceRepo.findAllBySaleIdAndCreatedAtBetween(user.getId(), begin, end);
+            List<ContractViolation> violationList = contractViolationRepo.findBySaleIdAndCreatedAtBetween(user.getId(), begin, end);
             AtomicReference<Integer> residenceBeds = new AtomicReference<>(0);
 
-            residences.forEach(residence -> {
+            violationList.forEach(residence -> {
 //                residenceBeds.set(residenceBeds.get() + residence.getResidenceBeds());
             });
             saleAmountReportDTOS.add(

+ 4 - 8
src/main/java/com/izouma/zhumj/web/ContractViolationController.java

@@ -1,4 +1,5 @@
 package com.izouma.zhumj.web;
+
 import com.izouma.zhumj.domain.sale.ContractViolation;
 import com.izouma.zhumj.service.ContractViolationService;
 import com.izouma.zhumj.dto.PageQuery;
@@ -19,22 +20,17 @@ import java.util.List;
 @AllArgsConstructor
 public class ContractViolationController extends BaseController {
     private ContractViolationService contractViolationService;
-    private ContractViolationRepo contractViolationRepo;
+    private ContractViolationRepo    contractViolationRepo;
 
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public ContractViolation save(@RequestBody ContractViolation record) {
-        if (record.getId() != null) {
-            ContractViolation orig = contractViolationRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
-            ObjUtils.merge(orig, record);
-            return contractViolationRepo.save(orig);
-        }
-        return contractViolationRepo.save(record);
+       return contractViolationService.save(record);
     }
 
     @GetMapping("/all")
     public Page<ContractViolation> all(PageQuery pageQuery) {
-        return contractViolationRepo.findAll(toSpecification(pageQuery,ContractViolation.class), toPageRequest(pageQuery));
+        return contractViolationRepo.findAll(toSpecification(pageQuery, ContractViolation.class), toPageRequest(pageQuery));
     }
 
     @GetMapping("/get/{id}")

+ 0 - 746
src/main/vue/src/components/ResidenceData.vue

@@ -1,746 +0,0 @@
-<template>
-    <div class>
-        <el-form
-            :model="formData"
-            :rules="rules"
-            ref="form"
-            label-width="100px"
-            label-position="right"
-            size="small"
-            style="max-width:810px;"
-        >
-            <el-form-item prop="contractNumber" label="退宿方式">
-                <el-radio-group v-model="info.residenceType">
-                    <el-radio-button v-for="(item, index) in residenceTypeOptions" :key="index" :label="item.value"
-                        >{{ item.label }}
-                    </el-radio-button>
-                </el-radio-group>
-            </el-form-item>
-
-            <el-form-item prop="contractNumber" label="合同编号">
-                <el-input disabled v-model="info.contractNumber" style="width:215px"></el-input>
-            </el-form-item>
-
-            <el-form-item
-                prop="InformAgreementNo"
-                :label="info.residenceType == 'RETIREMENT' ? '补充协议编号' : '告知函编号'"
-            >
-                <el-input v-model="info.InformAgreementNo"></el-input>
-            </el-form-item>
-
-            <el-form-item prop="residenceTime" label="退宿时间">
-                <el-date-picker
-                    v-model="info.residenceTime"
-                    type="datetime"
-                    value-format="yyyy-MM-dd HH:mm:ss"
-                    placeholder="选择日期时间"
-                ></el-date-picker>
-            </el-form-item>
-
-            <el-form-item label="退宿房型" v-if="info.residenceType == 'RETIREMENT'">
-                <div
-                    class="stepInfo"
-                    v-for="(contractStore, index) in formData.contractStoreList"
-                    :key="index"
-                    style="width:360px"
-                >
-                    <div class="step-title">门店{{ index + 1 }}</div>
-
-                    <el-form label-width="80px" label-position="left">
-                        <el-form-item class="form-item" label="门店名称">
-                            <el-select
-                                filterable
-                                v-model="contractStore.storeId"
-                                @change="getRoomTyeInfo(contractStore, index)"
-                                disabled
-                            >
-                                <el-option
-                                    v-for="(item, index) in storeInfo"
-                                    :key="index"
-                                    :label="item.storeName"
-                                    :value="item.id"
-                                ></el-option>
-                            </el-select>
-                        </el-form-item>
-
-                        <el-form-item
-                            prop="monthlyRent"
-                            label="月租金"
-                            v-if="formData.checkInType == 'INDIVIDUAL' || formData.checkInType == 'TEAM_POST_PAID'"
-                        >
-                            <el-input-number type="number" v-model="contractStore.monthlyRent"></el-input-number>
-                        </el-form-item>
-
-                        <el-form-item
-                            prop="monthlyRent"
-                            label="日租金"
-                            v-if="formData.checkInType == 'INDIVIDUAL' || formData.checkInType == 'TEAM_POST_PAID'"
-                        >
-                            <el-input-number type="number" v-model="contractStore.dayRent"></el-input-number>
-                        </el-form-item>
-
-                        <el-form-item
-                            prop="monthlyRent"
-                            label="日租金最大天数"
-                            v-if="formData.checkInType == 'INDIVIDUAL' || formData.checkInType == 'TEAM_POST_PAID'"
-                        >
-                            <el-input-number type="number" v-model="contractStore.daySize"></el-input-number>
-                        </el-form-item>
-
-                        <el-form-item
-                            label="房型信息"
-                            v-if="formData.checkInType == 'TEAM' || formData.checkInType == 'SCATTERED_BEDS'"
-                        >
-                            <div class="houseInfo" v-for="(room, index) in contractStore.roomTypes" :key="index">
-                                <div class="step-title">房型{{ index + 1 }}</div>
-                                <el-form-item label="房间类型" class="form-item">
-                                    <el-select
-                                        filterable
-                                        v-model="room.roomTypeId"
-                                        @change="setRoomBeds(contractStore, room)"
-                                        disabled
-                                    >
-                                        <el-option
-                                            v-for="(item, index) in contractStore.roomTypeInfo"
-                                            :key="index"
-                                            :label="item.typeName"
-                                            :value="item.id"
-                                        ></el-option>
-                                    </el-select>
-                                </el-form-item>
-
-                                <el-form-item label="床位数" class="form-item">
-                                    <el-input-number
-                                        type="number"
-                                        v-model="room.beds"
-                                        @change="getMoney"
-                                        :max="room.maxBeds"
-                                        :min="0"
-                                    ></el-input-number>
-                                </el-form-item>
-
-                                <el-form-item label="单价" class="form-item">
-                                    <el-input-number disabled type="number" v-model="room.price"></el-input-number>
-                                </el-form-item>
-                            </div>
-                        </el-form-item>
-                    </el-form>
-                </div>
-            </el-form-item>
-
-            <el-form-item prop="surplusRoomFee" label="剩余房费">
-                <el-input type="number" v-model="info.surplusRoomFee" disabled></el-input>
-            </el-form-item>
-
-            <el-form-item prop="deposit" label="押金">
-                <el-input type="number" v-model="info.deposit" disabled></el-input>
-            </el-form-item>
-
-            <el-form-item prop="falsifyType" label="违约金方式">
-                <!-- <el-input type="number" v-model="info.falsifyType"></el-input> -->
-                <el-select v-model="info.falsifyType" placeholder="请选择违约金方式">
-                    <el-option
-                        v-for="item in falsifyTypes"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    ></el-option>
-                </el-select>
-            </el-form-item>
-
-            <el-form-item prop="rent" label="金额">
-                <el-input type="number" v-model="info.rent"></el-input>
-            </el-form-item>
-
-            <el-form-item prop="residenceReason" label="退宿原因">
-                <el-input v-model="info.residenceReason"></el-input>
-            </el-form-item>
-
-            <el-form-item prop="customerLoss" label="客户是否流失">
-                <el-select v-model="info.customerLoss" clearable filterable placeholder="请选择">
-                    <el-option
-                        v-for="item in customerLossOptions"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    ></el-option>
-                </el-select>
-            </el-form-item>
-
-            <el-form-item prop="attach" :label="info.residenceType == 'RETIREMENT' ? '上传补充协议' : '上传告知函'">
-                <el-upload
-                    drag
-                    class="single-upload"
-                    :action="uploadUrl"
-                    :show-file-list="true"
-                    :on-success="onSuccess"
-                    :before-upload="beforeUpload"
-                    :limit="1"
-                    multiple
-                >
-                    <div></div>
-                    <i class="el-icon-upload"></i>
-                    <div class="el-upload__text">
-                        将文件拖到此处,或
-                        <em>点击上传</em>
-                    </div>
-                </el-upload>
-            </el-form-item>
-            <el-form-item>
-                <el-button @click="onSave" :loading="$store.state.fetchingData" type="primary">保存</el-button>
-                <el-button @click="close">取消</el-button>
-            </el-form-item>
-        </el-form>
-    </div>
-</template>
-<script>
-import resolveUrl from 'resolve-url';
-import { addSeconds, parse } from 'date-fns';
-
-export default {
-    name: 'ContractEdit',
-    created() {
-        this.uploadUrl = resolveUrl(this.$baseUrl, 'upload/file');
-
-        this.getStoreInfo();
-
-        this.info = {
-            ...this.residenceData,
-            residenceTime: '',
-            falsifyType: 1,
-            attach: ''
-        };
-        this.getMoney();
-    },
-    props: {
-        residenceData: {
-            type: Object,
-            default: () => {
-                return {};
-            }
-        }
-    },
-    watch: {
-        residenceData() {
-            this.info = {
-                ...this.residenceData,
-                residenceTime: '',
-                falsifyType: 1,
-                attach: ''
-            };
-            this.getMoney();
-        },
-        'info.residenceType'() {
-            this.getMoney();
-        },
-        'info.residenceTime'() {
-            this.getMoney();
-        }
-    },
-    data() {
-        return {
-            saving: false,
-            showCycle: false,
-            uploadUrl: '',
-            loading: false,
-            imageUrl: '',
-            text: '',
-            textarea: '',
-            saleEdit: false, //disables 为false
-            formData: {
-                bedsAmount: 0,
-                bet: 1,
-                checkInType: 'TEAM',
-                bills: [],
-                contactList: [],
-                contractStoreList: [
-                    {
-                        fixedFeeTypes: [],
-                        storeId: '',
-                        freeFeeTypes: [],
-                        roomTypeInfo: [],
-                        roomTypes: [],
-                        reserveRooms: []
-                    }
-                ],
-                monthlyRent: 0,
-                deposit: 0,
-                contractMonthly: 0,
-                contractDays: 0
-            },
-            rules: {
-                residenceTime: [{ required: true, message: '请填写退宿时间', trigger: 'blur' }]
-            },
-            CustomerCooperationType: [
-                {
-                    value: 'TEAM',
-                    label: '团队包房'
-                },
-                {
-                    value: 'SCATTERED_BEDS',
-                    label: '团队床位'
-                },
-                {
-                    value: 'TEAM_POST_PAID',
-                    label: '团散'
-                },
-                {
-                    value: 'INDIVIDUAL',
-                    label: '散客'
-                }
-            ],
-            saleUser: [],
-            storeInfo: [],
-            roomTyeInfo: [],
-            personalFeeTypes: [],
-            contractId: 0,
-            residenceTypeOptions: [
-                { value: 'AllRETIREMENT', label: '全部退宿' },
-                { value: 'RETIREMENT', label: '部分退宿' }
-                // { value: 'BREAK_STOP', label: '违约' }
-            ],
-            falsifyTypes: [
-                { value: 1, label: '房费' },
-                { value: 2, label: '押金' }
-            ],
-            info: {
-                contractNumber: '',
-                residenceRoomType: '',
-                residenceTime: '',
-                residenceType: 'AllRETIREMENT',
-                residenceReason: '',
-                customerLoss: ''
-            },
-            customerLossOptions: [
-                { value: 'YES', label: '是' },
-                { value: 'NO', label: '否' }
-            ]
-        };
-    },
-    computed: {
-        days() {
-            var day = 0;
-            if (this.info.residenceTime) {
-                var bills = [...this.bills];
-                for (var i = 0; i < bills.length; i++) {
-                    var time1 = addSeconds(parse(bills[i].endTime, 'yyyy-MM-dd HH:mm:ss', new Date()), 1).getTime();
-                    var time2 = parse(this.info.residenceTime, 'yyyy-MM-dd HH:mm:ss', new Date()).getTime();
-                    console.log(time1);
-                    console.log(time2);
-                    if (time1 > time2) {
-                        day = (time1 - time2) / 3600 / 24 / 1000;
-                        break;
-                    }
-                }
-            }
-            return day;
-        },
-        bills() {
-            return this.formData.bills || [];
-        }
-    },
-    methods: {
-        getMoney() {
-            var money = 0;
-            var list = [...this.formData.contractStoreList];
-            list.forEach(item => {
-                console.log(item);
-                if (item.roomTypes) {
-                    item.roomTypes.forEach(room => {
-                        var unitPrice = this.Div(room.price, 30);
-                        var price = this.Mul(unitPrice, this.days);
-                        if (this.info.residenceType == 'RETIREMENT') {
-                            money = this.Add(money, this.Mul(price, room.beds));
-                        } else {
-                            money = this.Add(money, this.Mul(price, room.maxBeds));
-                        }
-                    });
-                }
-            });
-            // var payMoney = 0;
-            // if (this.formData.pay) {
-            //   payMoney = this.Mul(this.formData.pay, this.formData.monthlyRent);
-            // }
-            // money = this.Sub(payMoney, money);
-
-            this.info.surplusRoomFee = Number(money.toFixed(2));
-        },
-        refreash() {
-            if (this.residenceData.contractId) {
-                this.$http
-                    .get(`/contract/get/${this.residenceData.contractId}`)
-                    .then(res => {
-                        this.getUser();
-                        if (!res.contactList) {
-                            res.push({
-                                contactList: [
-                                    {
-                                        name: '',
-                                        mobile: ''
-                                    }
-                                ]
-                            });
-                        }
-                        if (!res.contractStoreList) {
-                            res.push({
-                                contractStoreList: [
-                                    {
-                                        storeId: '',
-                                        personalFeeTypes: [],
-                                        roomTypes: [
-                                            {
-                                                price: '',
-                                                beds: '',
-                                                storeId: '',
-                                                roomTypeId: '',
-                                                reserveRooms: [],
-                                                maxBeds: 0
-                                            }
-                                        ]
-                                    }
-                                ]
-                            });
-                        } else {
-                            for (var i = res.contractStoreList.length - 1; i >= 0; i--) {
-                                let index = i;
-                                if (!res.contractStoreList[index].storeId) {
-                                    continue;
-                                }
-                                let query = {
-                                    query: {
-                                        storeId: res.contractStoreList[index].storeId
-                                    }
-                                };
-
-                                this.$http
-                                    .get(`/roomTypeInfo/getChooseAll`, {
-                                        size: 1000,
-                                        query: { storeId: res.contractStoreList[index].storeId }
-                                    })
-                                    .then(room => {
-                                        res.contractStoreList[index].roomTypeInfo = room.content;
-                                    })
-                                    .catch(e => {
-                                        this.$message.error(e.error);
-                                    });
-
-                                //减免项信息
-                                this.$http
-                                    .get(`/personalFeeType/all`, query)
-                                    .then(per => {
-                                        res.contractStoreList[index].personalFeeTypesData = per.content;
-                                    })
-                                    .catch(e => {
-                                        this.$message.error(e.error);
-                                    });
-                            }
-                        }
-
-                        res.contractStoreList.forEach(item => {
-                            if (item.roomTypes) {
-                                item.roomTypes.forEach(room => {
-                                    room.maxBeds = room.beds;
-                                });
-                            }
-                        });
-
-                        this.formData = res;
-
-                        this.getMoney();
-                        if (this.$route.query.firstContractNumber) {
-                            this.formData.contractNumber = '';
-                            this.$delete(this.formData, 'id');
-                            this.formData.firstContractNumber = this.$route.query.firstContractNumber;
-                            this.showCycle = false;
-                        }
-                    })
-                    .catch(e => {
-                        console.log(e);
-                        this.$message.error(e.error);
-                    });
-            }
-        },
-        getUser() {
-            this.$http
-                .get('/user/my')
-                .then(res => {
-                    if (res.position != '销售经理') {
-                        this.saleEdit = true;
-                    }
-                })
-                .catch(e => {
-                    this.$message.error(e.error);
-                });
-        },
-        setRoomBeds(contractStore, room) {
-            console.log('setRoomBeds');
-            var index = this.formData.contractStoreList.indexOf(contractStore);
-
-            var roomIndex = this.formData.contractStoreList[index].roomTypes.indexOf(room);
-
-            var roomType = {};
-
-            var storeList = [...contractStore.roomTypeInfo];
-            storeList.forEach(item => {
-                if (item.id == room.roomTypeId) {
-                    roomType = item;
-                }
-            });
-            this.formData.contractStoreList[index].roomTypes[roomIndex].beds = roomType.bedCount;
-        },
-        getRoomTyeInfo(room, index) {
-            //房型信息
-            let query = {
-                size: 1000,
-                query: { storeId: room.storeId }
-            };
-            this.$http
-                .get(`/roomTypeInfo/getChooseAll`, query)
-                .then(res => {
-                    var index = this.formData.contractStoreList.indexOf(room);
-                    this.formData.contractStoreList[index].roomTypeInfo = res.content;
-                    console.log(this.formData.contractStoreList[index].roomTypeInfo);
-                })
-                .catch(e => {
-                    this.$message.error(e.error);
-                });
-            this.formData.contractStoreList[index].personalFeeTypes = [];
-            this.formData.contractStoreList[index].reserveRooms = [];
-            this.formData.contractStoreList[index].roomTypes = [];
-        },
-        getRoomInfo(room, index) {
-            //房型信息
-            let query = {
-                query: { storeId: room.storeId }
-            };
-            this.$http
-                .get(`/roomTypeInfo/getChooseAll`, query)
-                .then(res => {
-                    var index = this.formData.contractStoreList.indexOf(room);
-                    this.formData.contractStoreList[index].roomTypeInfo = res.content;
-                })
-                .catch(e => {
-                    this.$message.error(e.error);
-                });
-            this.formData.contractStoreList[index].personalFeeTypes = [];
-            this.formData.contractStoreList[index].reserveRooms = [];
-        },
-        getStoreInfo() {
-            this.$http
-                .get(`/storeInfo/all`, { size: 1000 })
-                .then(res => {
-                    this.storeInfo = res.content;
-                })
-                .catch(e => {
-                    this.$message.error(e.error);
-                });
-        },
-        onSave() {
-            this.$refs.form.validate(valid => {
-                if (valid) {
-                    this.submit();
-                } else {
-                    return false;
-                }
-            });
-        },
-        submit() {
-            this.$store.commit('updateFetchingData', true);
-            let data = { ...this.info, residenceRoomTypes: [], contractStoreList: [], roomTypes: [] };
-            if (this.info.residenceType == 'RETIREMENT') {
-                var list = [...this.formData.contractStoreList];
-                var residenceRoomTypes = [];
-                list.forEach(item => {
-                    if (item.roomTypes) {
-                        item.roomTypes.forEach(room => {
-                            residenceRoomTypes.push({
-                                contractRoomId: room.id,
-                                residenceBeds: room.beds,
-                                storeId: room.storeId
-                            });
-                        });
-                    }
-                });
-                data.residenceRoomTypes = residenceRoomTypes;
-            }
-            console.log(data);
-            this.$http
-                .post('/residence/save', data, { body: 'json' })
-                .then(res => {
-                    this.$store.commit('updateFetchingData', false);
-                    this.$message.success('成功');
-                    this.$emit('close');
-                })
-                .catch(e => {
-                    this.$store.commit('updateFetchingData', false);
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        },
-        close() {
-            this.$emit('close');
-        },
-        onSuccess(res, file) {
-            this.info.attach = res;
-            this.loading = false;
-            this.imageUrl = URL.createObjectURL(file.raw);
-            var newVal = '';
-            if (res instanceof Array) {
-                newVal = res[0];
-            } else {
-                newVal = res;
-            }
-            this.$emit('input', newVal);
-        },
-        onError(err, file, fileList) {
-            this.loading = false;
-        },
-        beforeUpload(file) {
-            this.loading = true;
-            return true;
-        },
-        Add(arg1, arg2) {
-            (arg1 = arg1.toString()), (arg2 = arg2.toString());
-            var arg1Arr = arg1.split('.'),
-                arg2Arr = arg2.split('.'),
-                d1 = arg1Arr.length == 2 ? arg1Arr[1] : '',
-                d2 = arg2Arr.length == 2 ? arg2Arr[1] : '';
-            var maxLen = Math.max(d1.length, d2.length);
-            var m = Math.pow(10, maxLen);
-            var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));
-            var d = arguments[2];
-            return typeof d === 'number' ? Number(result.toFixed(d)) : result;
-        },
-        /*
-                函数:减法函数,用来得到精确的减法结果
-                说明:函数返回较为精确的减法结果。
-                参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数
-                调用:Calc.Sub(arg1,arg2)
-                返回值:两数相减的结果
-                */
-        Sub(arg1, arg2) {
-            return this.Add(arg1, -Number(arg2), arguments[2]);
-        },
-        /*
-                函数:乘法函数,用来得到精确的乘法结果
-                说明:函数返回较为精确的乘法结果。
-                参数:arg1:第一个乘数;arg2第二个乘数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
-                调用:Calc.Mul(arg1,arg2)
-                返回值:两数相乘的结果
-                */
-        Mul(arg1, arg2) {
-            var r1 = arg1.toString(),
-                r2 = arg2.toString(),
-                m,
-                resultVal,
-                d = arguments[2];
-            m = (r1.split('.')[1] ? r1.split('.')[1].length : 0) + (r2.split('.')[1] ? r2.split('.')[1].length : 0);
-            resultVal = (Number(r1.replace('.', '')) * Number(r2.replace('.', ''))) / Math.pow(10, m);
-            return typeof d !== 'number' ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
-        },
-        /*
-                函数:除法函数,用来得到精确的除法结果
-                说明:函数返回较为精确的除法结果。
-                参数:arg1:除数;arg2被除数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
-                调用:Calc.Div(arg1,arg2)
-                返回值:arg1除于arg2的结果
-                */
-        Div(arg1, arg2) {
-            var r1 = arg1.toString(),
-                r2 = arg2.toString(),
-                m,
-                resultVal,
-                d = arguments[2];
-            m = (r2.split('.')[1] ? r2.split('.')[1].length : 0) - (r1.split('.')[1] ? r1.split('.')[1].length : 0);
-            resultVal = (Number(r1.replace('.', '')) / Number(r2.replace('.', ''))) * Math.pow(10, m);
-            return typeof d !== 'number' ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
-        }
-    }
-};
-</script>
-<style lang="less" scoped>
-.stepInfo {
-    //   background: rgba(242, 244, 245, 1);
-    border: 1px solid #f2f4f5;
-    border-radius: 4px;
-    padding: 20px;
-    margin-bottom: 15px;
-
-    .step-title {
-        font-size: 14px;
-        font-weight: bold;
-        color: rgba(72, 87, 106, 1);
-        line-height: 16px;
-        margin-bottom: 15px;
-    }
-
-    .btnList {
-        display: flex;
-        margin-bottom: 15px;
-    }
-
-    .houseInfo {
-        padding: 15px;
-        border: 1px solid #ccc;
-        border-radius: 3px;
-        margin-bottom: 15px;
-    }
-
-    .loading {
-        position: absolute;
-        top: 0;
-        bottom: 0;
-        left: 0;
-        right: 0;
-        margin: auto;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        background: rgba(255, 255, 255, 0.6);
-        color: @text1;
-        font-size: 24px;
-    }
-
-    .avatar-uploader-icon {
-        font-size: 28px;
-        color: #8c939d;
-        width: 178px;
-        height: 178px;
-        line-height: 178px;
-        text-align: center;
-        border: 1px dashed #d9d9d9;
-        border-radius: 6px;
-        cursor: pointer;
-        position: relative;
-        overflow: hidden;
-        background-color: #fbfdff;
-
-        &:hover {
-            border-color: #409eff;
-        }
-    }
-
-    .avatar {
-        width: 178px;
-        height: 178px;
-        display: block;
-        border: 1px dashed #d9d9d9;
-        border-radius: 6px;
-        cursor: pointer;
-        position: relative;
-        overflow: hidden;
-
-        &:hover {
-            border-color: #409eff;
-        }
-    }
-
-    .wrapper {
-        position: relative;
-    }
-
-    .single-upload .el-upload {
-        position: relative;
-    }
-}
-</style>

+ 0 - 5
src/main/vue/src/components/operation/ContractList.vue

@@ -145,11 +145,6 @@ export default {
                 { value: 'STAY_IN', label: '在住' },
                 { value: 'RETREAT', label: '已退' }
             ],
-            residenceTypeOptions: [
-                { value: 'RETIREMENT', label: '部分退宿' },
-                { value: 'AllRETIREMENT', label: '全部退宿' },
-                { value: 'BREAK_STOP', label: '违约' }
-            ],
             customerLossOptions: [
                 { value: 'YES', label: '是' },
                 { value: 'NO', label: '否' }

+ 1 - 17
src/main/vue/src/router.js

@@ -260,26 +260,10 @@ const router = new Router({
                         title: '门店列表'
                     }
                 },
-                {
-                    path: '/residenceEdit',
-                    name: 'ResidenceEdit',
-                    component: () => import(/* webpackChunkName: "residenceList" */ '@/views/sale/ResidenceEdit.vue'),
-                    meta: {
-                        title: '退宿编辑'
-                    }
-                },
-                {
-                    path: '/residenceList',
-                    name: 'ResidenceList',
-                    component: () => import(/* webpackChunkName: "residenceList" */ '@/views/sale/ResidenceList.vue'),
-                    meta: {
-                        title: '退宿'
-                    }
-                },
                 {
                     path: '/saleWork',
                     name: 'SaleWork',
-                    component: () => import(/* webpackChunkName: "residenceEdit" */ '@/views/sale/SaleWork.vue')
+                    component: () => import(/* webpackChunkName: "saleWork" */ '@/views/sale/SaleWork.vue')
                 },
                 {
                     path: '/buildingInfoEdit',

+ 8 - 0
src/main/vue/src/views/Admin.vue

@@ -208,10 +208,12 @@ export default {
         this.getNightAuditStatus();
         eventBus.$on('nightAuditDone', this.onNightAuditDone);
         eventBus.$on('shiftHandoverDone', this.logout);
+        eventBus.$on('closeTab', this.closeTabHandler);
     },
     beforeDestroy() {
         eventBus.$off('nightAuditDone', this.onNightAuditDone);
         eventBus.$off('shiftHandoverDone', this.logout);
+        eventBus.$off('closeTab', this.closeTabHandler);
     },
     data() {
         return {
@@ -405,6 +407,12 @@ export default {
             this.hasMore = true;
             this.getStoreNotice();
             this.getNightAuditStatus();
+        },
+        closeTabHandler(name) {
+            let index = this.tabs.findIndex(i => i.name === name);
+            if (index > -1) {
+                this.tabs.splice(index, 1);
+            }
         }
     },
     watch: {

+ 4 - 0
src/main/vue/src/views/ContractViolationEdit.vue

@@ -99,6 +99,7 @@
 </template>
 <script>
 import { parse, format, differenceInDays, startOfDay, isAfter, isBefore, isEqual } from 'date-fns';
+import eventBus from '../eventBus';
 const parseTime = time => {
     return parse(time, 'yyyy-MM-dd HH:mm:ss', new Date());
 };
@@ -310,6 +311,9 @@ export default {
                 });
             }
         }
+    },
+    beforeDestroy() {
+        eventBus.$emit('closeTab', 'ContractViolationEdit');
     }
 };
 </script>

+ 18 - 12
src/main/vue/src/views/ContractViolationList.vue

@@ -26,18 +26,24 @@
             :height="tableHeight"
         >
             <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
-            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
-            <el-table-column prop="contractId" label="合同ID"> </el-table-column>
-            <el-table-column prop="contractNumber" label="合同编号"> </el-table-column>
-            <el-table-column prop="violationType" label="违约类型" :formatter="violationTypeFormatter">
+            <el-table-column label="#" type="index" width="50"> </el-table-column>
+            <el-table-column prop="contractNumber" label="合同编号" min-width="180" show-overflow-tooltip>
             </el-table-column>
-            <el-table-column prop="restRent" label="剩余房费"> </el-table-column>
-            <el-table-column prop="penalty" label="违约金"> </el-table-column>
-            <el-table-column prop="penaltyType" label="违约金方式" :formatter="penaltyTypeFormatter"> </el-table-column>
-            <el-table-column label="操作" v-if="canEdit" align="center" fixed="right" min-width="150">
-                <template slot-scope="{ row }">
-                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
-                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+            <el-table-column prop="coSimpleName" label="公司简称"> </el-table-column>
+            <el-table-column prop="violationType" label="违约类型" width="80" :formatter="violationTypeFormatter">
+            </el-table-column>
+            <el-table-column prop="restRent" label="剩余房费" width="100"> </el-table-column>
+            <el-table-column prop="penalty" label="违约金" width="100"> </el-table-column>
+            <el-table-column prop="penaltyType" label="违约金方式" width="100" :formatter="penaltyTypeFormatter">
+            </el-table-column>
+            <el-table-column prop="attachNumber" label="补充协议/告知函编号" width="150" show-overflow-tooltip>
+            </el-table-column>
+            <el-table-column prop="violationTime" label="退宿时间" width="150"> </el-table-column>
+            <el-table-column prop="reason" label="退宿原因" min-width="200" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="saleName" label="销售员" width="80px"></el-table-column>
+            <el-table-column label="附件" width="80px">
+                <template v-slot="{ row }">
+                    <el-link :href="row.attach" v-if="row.attach" style="font-size:12px;">下载</el-link>
                 </template>
             </el-table-column>
         </el-table>
@@ -82,7 +88,7 @@ export default {
             downloading: false,
             violationTypeOptions: [
                 { label: '部分', value: 'PART' },
-                { label: '全部', value: 'TOTAL' }
+                { label: '全部', value: 'ALL' }
             ],
             penaltyTypeOptions: [
                 { label: '房费', value: 'RENT' },

+ 0 - 170
src/main/vue/src/views/ResidenceRoomTypeList.vue

@@ -1,170 +0,0 @@
-<template>
-    <div class="list-view">
-        <div class="filters-container">
-            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
-            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
-            <el-button @click="addRow" v-if="canEdit" type="primary" icon="el-icon-plus" class="filter-item"
-                >添加
-            </el-button>
-            <el-button
-                @click="download"
-                type="primary"
-                icon="el-icon-download"
-                :loading="downloading"
-                class="filter-item"
-                >导出EXCEL
-            </el-button>
-        </div>
-        <el-table
-            :data="tableData"
-            row-key="id"
-            ref="table"
-            header-row-class-name="table-header-row"
-            header-cell-class-name="table-header-cell"
-            row-class-name="table-row"
-            cell-class-name="table-cell"
-            :height="tableHeight"
-        >
-            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
-            <el-table-column prop="id" label="ID" width="100">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="contractRoomId" label="原合同房型ID">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="residenceBeds" label="退宿床位数">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column label="操作" v-if="canEdit" align="center" fixed="right" min-width="150">
-                <template v-slot="{ row }">
-                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
-                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
-                </template>
-            </el-table-column>
-        </el-table>
-        <div class="pagination-wrapper">
-            <el-pagination
-                background
-                @size-change="onSizeChange"
-                @current-change="onCurrentChange"
-                :current-page="page"
-                :page-sizes="[10, 20, 30, 40, 50]"
-                :page-size="pageSize"
-                layout="total, sizes, prev, pager, next, jumper"
-                :total="totalElements"
-            >
-            </el-pagination>
-        </div>
-    </div>
-</template>
-<script>
-import { mapState } from 'vuex';
-import pageableTable from '@/mixins/pageableTable';
-
-export default {
-    name: 'ResidenceRoomTypeList',
-    mixins: [pageableTable],
-    data() {
-        return {
-            multipleMode: false,
-            search: '',
-            url: '/residenceRoomType/all',
-            downloading: false
-        };
-    },
-    computed: {
-        selection() {
-            return this.$refs.table.selection.map(i => i.id);
-        }
-    },
-    methods: {
-        beforeGetData() {
-            if (this.search) {
-                return { search: this.search };
-            }
-        },
-        toggleMultipleMode(multipleMode) {
-            this.multipleMode = multipleMode;
-            if (!multipleMode) {
-                this.$refs.table.clearSelection();
-            }
-        },
-        addRow() {
-            this.$router.push({
-                path: '/residenceRoomTypeEdit',
-                query: {
-                    ...this.$route.query
-                }
-            });
-        },
-        editRow(row) {
-            this.$router.push({
-                path: '/residenceRoomTypeEdit',
-                query: {
-                    id: row.id
-                }
-            });
-        },
-        download() {
-            this.downloading = true;
-            this.$axios
-                .get('/residenceRoomType/excel', {
-                    responseType: 'blob',
-                    params: { size: 10000 }
-                })
-                .then(res => {
-                    console.log(res);
-                    this.downloading = false;
-                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
-                    const link = document.createElement('a');
-                    link.href = downloadUrl;
-                    link.setAttribute(
-                        'download',
-                        decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])
-                    );
-                    document.body.appendChild(link);
-                    link.click();
-                    link.remove();
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.downloading = false;
-                    this.$message.error(e.error);
-                });
-        },
-        operation1() {
-            this.$notify({
-                title: '提示',
-                message: this.selection
-            });
-        },
-        operation2() {
-            this.$message('操作2');
-        },
-        deleteRow(row) {
-            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
-                .then(() => {
-                    return this.$http.post(`/residenceRoomType/del/${row.id}`);
-                })
-                .then(() => {
-                    this.$message.success('删除成功');
-                    this.getData();
-                })
-                .catch(action => {
-                    if (action === 'cancel') {
-                        this.$message.info('删除取消');
-                    } else {
-                        this.$message.error('删除失败');
-                    }
-                });
-        }
-    }
-};
-</script>
-<style lang="less" scoped></style>

+ 33 - 14
src/main/vue/src/views/sale/ContractEdit.vue

@@ -78,6 +78,17 @@
                 >
                     <el-input-number type="number" v-model="formData.bedsAmount"> </el-input-number>
                 </el-form-item>
+
+                <el-form-item prop="status" label="在住状态">
+                    <el-select v-model="formData.status">
+                        <el-option
+                            v-for="item in statusData"
+                            :label="item.label"
+                            :value="item.value"
+                            :key="item.value"
+                        ></el-option>
+                    </el-select>
+                </el-form-item>
             </el-card>
 
             <el-card shadow="never" class="phases">
@@ -806,6 +817,7 @@ export default {
             // },
             lastFlowBet: 0,
             rules: {
+                status: [{ required: true, message: '请选择在住状态', trigger: 'blur' }],
                 saleId: [{ required: true, message: '请选择销售员', trigger: 'blur' }],
                 customerSource: [{ required: true, message: '请选择客户来源', trigger: 'blur' }],
                 checkInType: [{ required: true, message: '请选择合作模式', trigger: 'blur' }],
@@ -833,8 +845,9 @@ export default {
                                 this.formData.checkInType === 'SCATTERED_BEDS'
                             ) {
                                 if (
+                                    !this.formData.firstContractNumber &&
                                     value.map(i => i.money).reduce((a, b) => Number(a) + Number(b), 0) !==
-                                    this.formData.contractTotalRent + this.formData.flowBet - this.lastFlowBet
+                                        this.formData.contractTotalRent + this.formData.flowBet - this.lastFlowBet
                                 ) {
                                     callback(new Error('周期总金额与合同总金额不一致'));
                                     return;
@@ -1161,20 +1174,26 @@ export default {
                 }
                 this.$set(this.formData, 'bills', bills);
             } else {
-                let startTime = startOfDay(startOfMonth(contractBeginTime));
-                for (let i = 0; i < this.formData.contractMonthly + 1; i++) {
-                    bills.push({
-                        idx: i + 1,
-                        startTime: formatDate(startTime),
-                        endTime: formatDate(endOfDay(endOfMonth(startTime))),
-                        dueTime: formatDate(addMonths(startTime, 1)),
-                        money: null,
-                        flowBet: i === 0 ? this.formData.flowBet - this.lastFlowBet : null,
-                        status: 'PENDING',
-                        type: 'POST_PAID'
-                    });
+                for (let i = 0; i < this.formData.phases.length; i++) {
+                    const phase = this.formData.phases[i];
+                    let totalMonth = phase.months;
+                    const contractBeginTime = parseDate(phase.startTime);
+                    const contractEndTime = parseDate(phase.endTime);
+                    let startTime = startOfDay(startOfMonth(contractBeginTime));
+                    for (let i = 0; i < this.formData.contractMonthly + 1; i++) {
+                        bills.push({
+                            idx: i + 1,
+                            startTime: formatDate(startTime),
+                            endTime: formatDate(endOfDay(endOfMonth(startTime))),
+                            dueTime: formatDate(addMonths(startTime, 1)),
+                            money: null,
+                            flowBet: i === 0 ? this.formData.flowBet - this.lastFlowBet : null,
+                            status: 'PENDING',
+                            type: 'POST_PAID'
+                        });
+                        startTime = addMonths(startTime, 1);
+                    }
                 }
-                startTime = addMonths(startTime, 1);
                 this.$set(this.formData, 'bills', bills);
             }
         },

+ 14 - 85
src/main/vue/src/views/sale/ContractList.vue

@@ -152,7 +152,7 @@
             <!--      </el-table-column>-->
             <el-table-column label="退宿信息" width="100">
                 <template v-slot="{ row }">
-                    <el-button @click="residenceList(row.contractNumber)" size="mini" plain>查看</el-button>
+                    <el-button @click="violationList(row)" size="mini" plain>查看</el-button>
                 </template>
             </el-table-column>
             <el-table-column prop="contractRenewals" label="续约次数" width="80"> </el-table-column>
@@ -203,7 +203,7 @@
                     <!-- <el-button @click="detail(row)" type="success" size="mini" plain>详情</el-button> -->
                     <template v-if="showMoreEdit">
                         <el-button @click="renewalRow(row)" type="success" size="mini" plain>续约</el-button>
-                        <el-button @click="residenceDialog(row)" type="warning" size="mini" plain>退宿</el-button>
+                        <el-button @click="violationEdit(row)" type="warning" size="mini" plain>退宿</el-button>
                         <el-button @click="staffDialog(row)" type="warning" size="mini" plain>人员</el-button>
                         <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
                         <el-button @click="history(row)" type="primary" size="mini" plain>历史</el-button>
@@ -214,7 +214,7 @@
                         </span>
                         <el-dropdown-menu slot="dropdown">
                             <el-dropdown-item command="renewalRow">续约</el-dropdown-item>
-                            <el-dropdown-item command="residenceDialog">退宿</el-dropdown-item>
+                            <el-dropdown-item command="violationEdit">退宿</el-dropdown-item>
                             <el-dropdown-item command="staffDialog">人员</el-dropdown-item>
                             <el-dropdown-item command="editRow">编辑</el-dropdown-item>
                             <el-dropdown-item command="history">历史</el-dropdown-item>
@@ -237,21 +237,6 @@
             ></el-pagination>
         </div>
 
-        <el-dialog
-            class="renewalData"
-            title="退宿"
-            :visible.sync="residenceDataDialog"
-            :before-close="handleClose"
-            width="540px"
-            :modal-append-to-body="false"
-        >
-            <residence-data
-                ref="residence"
-                :residenceData="residenceData"
-                @close="residenceDataDialog = false"
-            ></residence-data>
-        </el-dialog>
-
         <el-dialog title="联系人" :visible.sync="showContractsDialog" width="60%" :modal-append-to-body="false">
             <el-table :data="contactsData" stripe style="width: 100%">
                 <el-table-column prop="name" label="姓名"></el-table-column>
@@ -309,7 +294,6 @@
 <script>
 import { mapState } from 'vuex';
 import pageableTable from '@/mixins/pageableTable';
-import ResidenceData from '../../components/ResidenceData';
 import resolveUrl from 'resolve-url';
 import { parse, format, differenceInDays, startOfDay } from 'date-fns';
 
@@ -324,8 +308,6 @@ export default {
             saleId: undefined,
             multipleMode: false,
             renewalDataDialog: false,
-            residenceDataDialogurl: false,
-            residenceDataDialog: false,
             showContractsDialog: false,
             contactsData: [],
             contractBills: [],
@@ -431,11 +413,6 @@ export default {
                 { value: 'STAY_IN', label: '在住' },
                 { value: 'RETREAT', label: '已退' }
             ],
-            residenceTypeOptions: [
-                { value: 'RETIREMENT', label: '部分退宿' },
-                { value: 'AllRETIREMENT', label: '全部退宿' }
-                // { value: 'BREAK_STOP', label: '违约' }
-            ],
             customerLossOptions: [
                 { value: 'YES', label: '是' },
                 { value: 'NO', label: '否' }
@@ -450,14 +427,6 @@ export default {
                 { value: '付四押一', label: '付四押一' }
             ],
             renewalData: {},
-            residenceData: {
-                contractNumber: '',
-                residenceRoomType: '',
-                residenceTime: '',
-                residenceType: 'AllRETIREMENT',
-                residenceReason: '',
-                customerLoss: ''
-            },
             excelTime: '',
             pickerOptions: {
                 shortcuts: [
@@ -602,60 +571,22 @@ export default {
                 }
             });
         },
-        residenceList(contractNumber) {
+        violationList(row) {
             this.$router.push({
-                path: '/residenceList',
+                path: '/contractViolationList',
                 query: {
-                    contractNumber: contractNumber
+                    contractId: row.id
                 }
             });
         },
         //退宿弹出框
-        residenceDialog(row) {
-            this.residenceData = {
-                contractNumber: row.contractNumber,
-                contractId: row.id,
-                residenceType: 'AllRETIREMENT',
-                deposit: row.flowBet,
-                saleName: row.saleName,
-                saleId: row.saleId
-            };
-
-            this.contactsRoomData = row.roomTypes;
-            this.residenceDataDialog = true;
-            setTimeout(() => {
-                this.$refs.residence.refreash();
-            }, 500);
-        },
-        //查询退宿信息
-        residenceByContractNumber(renewalContractNumber) {
-            this.$http
-                .get(`/residence/getContractNumber/` + renewalContractNumber)
-                .then(res => {
-                    this.residenceData = res;
-                })
-                .catch(e => {
-                    console.log(e);
-                    // this.$message.error(e.error);
-                });
-        },
-        handleClose() {
-            this.residenceDataDialog = false;
-            this.residenceData = {};
-        },
-        //退宿保存
-        residenceSubmit() {
-            let data = { ...this.residenceData };
-            this.$http
-                .post('/residence/save', data, { body: 'json' })
-                .then(res => {
-                    this.$message.success('成功');
-                    this.$router.go(-1);
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
+        violationEdit(row) {
+            this.$router.push({
+                path: '/contractViolationEdit',
+                query: {
+                    contractId: row.id
+                }
+            });
         },
         customerSourceFormatter(row, column, cellValue, index) {
             var valueStr = '';
@@ -824,9 +755,7 @@ export default {
             this.getData();
         }
     },
-    components: {
-        ResidenceData
-    }
+    components: {}
 };
 </script>
 <style lang="less" scoped>

+ 1 - 325
src/main/vue/src/views/sale/RecoveryContractList.vue

@@ -3,7 +3,6 @@
         <div class="filters-container">
             <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
             <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
-            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
             <el-button
                 @click="download"
                 type="primary"
@@ -23,7 +22,6 @@
             row-class-name="table-row"
             cell-class-name="table-cell"
         >
-            <el-table-column align="center" v-if="multipleMode" type="selection" width="50"> </el-table-column>
             <el-table-column type="index" width="50"> </el-table-column>
             <el-table-column align="center" prop="contractNumber" label="合同编号" width="150">
                 <template slot="header" slot-scope="{ column }">
@@ -121,27 +119,6 @@
                     {{ statusLabel(row.status) }}
                 </template>
             </el-table-column>
-            <el-table-column label="续约订单" align="center" fixed="right" min-width="320">
-                <template v-slot="{ row }">
-                    <el-button @click="renewalList(row.contractNumber)" type="success" size="mini" plain
-                        >续约订单
-                    </el-button>
-                    <el-button @click="openContactsDataDialog(row.contactList)" type="success" size="mini" plain
-                        >联系人
-                    </el-button>
-                    <el-button @click="openRoomDataDialog(row.roomTypes)" type="success" size="mini" plain
-                        >房型床位
-                    </el-button>
-                </template>
-            </el-table-column>
-            <el-table-column label="操作" align="center" v-if="canEdit" fixed="right" min-width="320">
-                <template v-slot="{ row }">
-                    <el-button @click="renewalDialog(row)" type="success" size="mini" plain>续约 </el-button>
-                    <el-button @click="residenceDialog(row.contractNumber)" type="warning" size="mini" plain
-                        >退宿
-                    </el-button>
-                </template>
-            </el-table-column>
         </el-table>
         <div class="pagination-wrapper">
             <el-pagination
@@ -155,149 +132,6 @@
                 :total="totalElements"
             >
             </el-pagination>
-            <template>
-                <el-dialog
-                    class="renewalData"
-                    title="续约订单"
-                    :visible.sync="renewalDataDialog"
-                    width="30%"
-                    :modal-append-to-body="false"
-                >
-                    <el-form ref="form" :model="renewalData" label-width="100px">
-                        <el-form-item prop="contractNumber" label="合同编号">
-                            <el-input v-model="renewalData.contractNumber" :disabled="true"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalNumber" label="续约合同编号">
-                            <el-input v-model="renewalData.renewalNumber"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalRoomType" label="续约房型">
-                            <el-input v-model="renewalData.renewalRoomType"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalStore" label="续约门店">
-                            <el-input v-model="renewalData.renewalStore"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalPrice" label="续约单价">
-                            <el-input v-model="renewalData.renewalPrice" @change="sumRent"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalMonthly" label="合同期(月)">
-                            <el-input v-model="renewalData.renewalMonthly" @change="sumRent"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="contractPrice" label="原合同单价">
-                            <el-input v-model="renewalData.contractPrice" :disabled="true"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="gainPrice" label="涨幅价格">
-                            <el-input v-model="renewalData.gainPrice" :disabled="true"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalTotalRent" label="续约总租金">
-                            <el-input v-model="renewalData.renewalTotalRent"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="renewalBeginTime" label="合同开始时间">
-                            <el-date-picker
-                                v-model="renewalData.renewalBeginTime"
-                                type="datetime"
-                                value-format="yyyy-MM-dd HH:mm:ss"
-                                placeholder="选择日期时间"
-                            >
-                            </el-date-picker>
-                        </el-form-item>
-                        <el-form-item prop="renewalEndTime" label="合同结束时间">
-                            <el-date-picker
-                                v-model="renewalData.renewalEndTime"
-                                type="datetime"
-                                value-format="yyyy-MM-dd HH:mm:ss"
-                                placeholder="选择日期时间"
-                            >
-                            </el-date-picker>
-                        </el-form-item>
-                    </el-form>
-                    <span slot="footer" class="dialog-footer">
-                        <!--              <el-button @click=handleClose()>取 消</el-button>-->
-                        <el-button type="primary" @click="renewalSubmit()">确 定</el-button>
-                    </span>
-                </el-dialog>
-
-                <el-dialog
-                    class="renewalData"
-                    title="退宿"
-                    :visible.sync="residenceDataDialog"
-                    :before-close="handleClose"
-                    width="30%"
-                    :modal-append-to-body="false"
-                >
-                    <el-form ref="form" :model="residenceData" label-width="100px">
-                        <el-form-item prop="contractNumber" label="合同编号">
-                            <el-input v-model="residenceData.contractNumber" :disabled="true"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="residenceRoomType" label="退宿房型">
-                            <el-input v-model="residenceData.residenceRoomType"></el-input>
-                        </el-form-item>
-                        <el-form-item prop="residenceType" label="退宿类型">
-                            <el-select v-model="residenceData.residenceType" clearable filterable placeholder="请选择">
-                                <el-option
-                                    v-for="item in residenceTypeOptions"
-                                    :key="item.value"
-                                    :label="item.label"
-                                    :value="item.value"
-                                >
-                                </el-option>
-                            </el-select>
-                        </el-form-item>
-
-                        <el-form-item prop="residenceTime" label="退宿时间">
-                            <el-date-picker
-                                v-model="residenceData.residenceTime"
-                                type="datetime"
-                                value-format="yyyy-MM-dd HH:mm:ss"
-                                placeholder="选择日期时间"
-                            >
-                            </el-date-picker>
-                        </el-form-item>
-
-                        <el-form-item prop="customerLoss" label="客户是否流失">
-                            <el-select v-model="residenceData.customerLoss" clearable filterable placeholder="请选择">
-                                <el-option
-                                    v-for="item in customerLossOptions"
-                                    :key="item.value"
-                                    :label="item.label"
-                                    :value="item.value"
-                                >
-                                </el-option>
-                            </el-select>
-                        </el-form-item>
-                    </el-form>
-                    <span slot="footer" class="dialog-footer">
-                        <!--              <el-button @click=handleClose()>取 消</el-button>-->
-                        <el-button type="primary" @click="residenceSubmit()">确 定</el-button>
-                    </span>
-                </el-dialog>
-
-                <el-dialog
-                    class="contactsData"
-                    title="联系人"
-                    :visible.sync="contactsDataDialog"
-                    width="60%"
-                    :modal-append-to-body="false"
-                >
-                    <el-table :data="contactsData" stripe style="width: 100%">
-                        <el-table-column prop="name" label="姓名"> </el-table-column>
-                        <el-table-column prop="mobile" label="地址"> </el-table-column>
-                    </el-table>
-                </el-dialog>
-
-                <el-dialog
-                    class="roomData"
-                    title="房型床位"
-                    :visible.sync="roomDataDialog"
-                    width="60%"
-                    :modal-append-to-body="false"
-                >
-                    <el-table :data="roomData" stripe style="width: 100%">
-                        <el-table-column prop="roomType" label="房型"> </el-table-column>
-                        <el-table-column prop="beds" label="床位数"> </el-table-column>
-                        <el-table-column prop="price" label="单价"> </el-table-column>
-                    </el-table>
-                </el-dialog>
-            </template>
         </div>
     </div>
 </template>
@@ -315,13 +149,6 @@ export default {
     data() {
         return {
             saleId: '',
-            multipleMode: false,
-            renewalDataDialog: false,
-            residenceDataDialog: false,
-            contactsDataDialog: false,
-            contactsData: [],
-            roomDataDialog: false,
-            roomData: [],
             search: '',
             url: '/contract/all',
             downloading: false,
@@ -343,10 +170,6 @@ export default {
                 { value: 'STAY_IN', label: '在住' },
                 { value: 'RETREAT', label: '已退' }
             ],
-            residenceTypeOptions: [
-                { value: 'RETIREMENT', label: '退宿' },
-                { value: 'BREAK_STOP', label: '违约' }
-            ],
             customerLossOptions: [
                 { value: 'YES', label: '是' },
                 { value: 'NO', label: '否' }
@@ -359,29 +182,7 @@ export default {
                     label: '付三押一'
                 },
                 { value: '付四押一', label: '付四押一' }
-            ],
-            renewalData: {
-                contractNumber: '',
-                renewalNumber: '',
-                renewalRoomType: '',
-                renewalStore: '',
-                renewalPrice: '',
-                gainPrice: 0,
-                renewalBeginTime: '',
-                renewalEndTime: '',
-                contractPrice: '',
-                renewalMonthly: 0,
-                renewalTotalRent: 0,
-                beds: ''
-            },
-            residenceData: {
-                contractNumber: '',
-                residenceRoomType: '',
-                residenceTime: '',
-                residenceType: '',
-                residenceReason: '',
-                customerLoss: ''
-            }
+            ]
         };
     },
     computed: {
@@ -390,100 +191,6 @@ export default {
         }
     },
     methods: {
-        //房型信息弹出框
-        openRoomDataDialog(data) {
-            this.roomData = data;
-            this.roomDataDialog = true;
-        },
-        //联系人弹出框
-        openContactsDataDialog(data) {
-            this.contactsData = data;
-            this.contactsDataDialog = true;
-        },
-        //计算续约订单金额
-        sumRent() {
-            this.renewalData.gainPrice = this.renewalData.renewalPrice - this.renewalData.contractPrice;
-            this.renewalData.renewalTotalRent = this.renewalData.renewalMonthly * this.renewalData.renewalPrice;
-        },
-        //查询合同续约订单信息
-        renewalList(contractNumber) {
-            this.$router.push({
-                path: '/renewalList',
-                query: {
-                    contractNumber: contractNumber
-                }
-            });
-        },
-        residenceList(contractNumber) {
-            this.$router.push({
-                path: '/residenceList',
-                query: {
-                    contractNumber: contractNumber
-                }
-            });
-        },
-        //续约订单保存
-        renewalSubmit() {
-            let data = { ...this.renewalData };
-            this.$http
-                .post('/renewal/save', data, { body: 'json' })
-                .then(res => {
-                    this.$message.success('成功');
-                    this.$router.go(-1);
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        },
-        //续约弹出框
-        renewalDialog(row) {
-            this.renewalData = {};
-            this.renewalData.gainPrice = 0;
-            this.renewalData.renewalMonthly = 0;
-            this.renewalData.renewalTotalRent = 0;
-
-            this.renewalData.contractNumber = row.contractNumber;
-            this.renewalData.contractPrice = row.price;
-            this.renewalData.beds = row.beds;
-            this.renewalDataDialog = true;
-        },
-        //退宿弹出框
-        residenceDialog(renewalContractNumber) {
-            this.residenceData.contractNumber = renewalContractNumber;
-            this.residenceByContractNumber(renewalContractNumber);
-            this.residenceDataDialog = true;
-        },
-        //查询退宿信息
-        residenceByContractNumber(renewalContractNumber) {
-            this.$http
-                .get(`/residence/getContractNumber/` + renewalContractNumber)
-                .then(res => {
-                    this.residenceData = res;
-                })
-                .catch(e => {
-                    console.log(e);
-                    // this.$message.error(e.error);
-                });
-        },
-        handleClose() {
-            this.residenceDataDialog = false;
-            this.residenceData = {};
-        },
-        //退宿保存
-        residenceSubmit() {
-            let data = { ...this.residenceData };
-            this.$http
-                .post('/residence/save', data, { body: 'json' })
-                .then(res => {
-                    this.$message.success('成功');
-                    this.$router.go(-1);
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        },
         sourceLabel(cellValue) {
             var valueStr = '未知';
             this.sourceData.forEach(item => {
@@ -523,28 +230,6 @@ export default {
             }
             return data;
         },
-        toggleMultipleMode(multipleMode) {
-            this.multipleMode = multipleMode;
-            if (!multipleMode) {
-                this.$refs.table.clearSelection();
-            }
-        },
-        addRow() {
-            this.$router.push({
-                path: '/contractEdit',
-                query: {
-                    ...this.$route.query
-                }
-            });
-        },
-        editRow(row) {
-            this.$router.push({
-                path: '/contractEdit',
-                query: {
-                    id: row.id
-                }
-            });
-        },
         download() {
             this.downloading = true;
             this.$axios
@@ -569,15 +254,6 @@ export default {
                     this.$message.error(e.error);
                 });
         },
-        operation1() {
-            this.$notify({
-                title: '提示',
-                message: this.selection
-            });
-        },
-        operation2() {
-            this.$message('操作2');
-        },
         deleteRow(row) {
             this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
                 .then(() => {

+ 0 - 173
src/main/vue/src/views/sale/ResidenceEdit.vue

@@ -1,173 +0,0 @@
-<template>
-    <div class="edit-view">
-        <el-form
-            :model="formData"
-            :rules="rules"
-            ref="form"
-            label-width="186px"
-            label-position="right"
-            size="small"
-            style="max-width: 500px;"
-        >
-            <el-form-item prop="contractNumber" label="合同编号">
-                <el-input v-model="formData.contractNumber"></el-input>
-            </el-form-item>
-            <el-form-item prop="contractId" label="合同编号">
-                <el-input-number type="number" v-model="formData.contractId"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="falsifyType" label="违约金方式 1 房费 2 押金">
-                <el-input-number type="number" v-model="formData.falsifyType"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="surplusRoomFee" label="剩余房费">
-                <el-input-number type="number" v-model="formData.surplusRoomFee"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="surplusFlowBet" label="押金">
-                <el-input-number type="number" v-model="formData.surplusFlowBet"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="rent" label="金额">
-                <el-input-number type="number" v-model="formData.rent"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="InformAgreementNo" label="补充协议编号/告知函编号">
-                <el-input v-model="formData.InformAgreementNo"></el-input>
-            </el-form-item>
-            <el-form-item prop="residenceTime" label="退宿时间">
-                <el-date-picker
-                    v-model="formData.residenceTime"
-                    type="datetime"
-                    value-format="yyyy-MM-dd HH:mm:ss"
-                    placeholder="选择日期时间"
-                >
-                </el-date-picker>
-            </el-form-item>
-            <el-form-item prop="residenceRoomTypes" label="residenceRoomTypes">
-                <el-input v-model="formData.residenceRoomTypes"></el-input>
-            </el-form-item>
-            <el-form-item prop="residenceType" label="退宿类型">
-                <el-select v-model="formData.residenceType" clearable filterable placeholder="请选择">
-                    <el-option
-                        v-for="item in residenceTypeOptions"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    >
-                    </el-option>
-                </el-select>
-            </el-form-item>
-            <el-form-item prop="residenceReason" label="退宿原因">
-                <el-input v-model="formData.residenceReason"></el-input>
-            </el-form-item>
-            <el-form-item prop="customerLoss" label="客户是否流失">
-                <el-select v-model="formData.customerLoss" clearable filterable placeholder="请选择">
-                    <el-option
-                        v-for="item in customerLossOptions"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    >
-                    </el-option>
-                </el-select>
-            </el-form-item>
-            <el-form-item prop="attach" label="附件">
-                <el-input v-model="formData.attach"></el-input>
-            </el-form-item>
-            <el-form-item prop="enabled" label="enabled">
-                <el-switch v-model="formData.enabled"></el-switch>
-            </el-form-item>
-            <el-form-item prop="deposit" label="押金">
-                <el-input-number type="number" v-model="formData.deposit"></el-input-number>
-            </el-form-item>
-            <el-form-item prop="saleName" label="销售员">
-                <el-input v-model="formData.saleName"></el-input>
-            </el-form-item>
-            <el-form-item prop="saleId" label="销售员ID">
-                <el-input-number type="number" v-model="formData.saleId"></el-input-number>
-            </el-form-item>
-            <el-form-item>
-                <el-button @click="onSave" :loading="$store.state.fetchingData" type="primary">保存</el-button>
-                <el-button @click="onDelete" :loading="$store.state.fetchingData" type="danger" v-if="formData.id"
-                    >删除
-                </el-button>
-                <el-button @click="$router.go(-1)">取消</el-button>
-            </el-form-item>
-        </el-form>
-    </div>
-</template>
-<script>
-export default {
-    name: 'ResidenceEdit',
-    created() {
-        if (this.$route.query.id) {
-            this.$http
-                .get('residence/get/' + this.$route.query.id)
-                .then(res => {
-                    this.formData = res;
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        }
-    },
-    data() {
-        return {
-            saving: false,
-            formData: {},
-            rules: {},
-            residenceTypeOptions: [
-                { label: 'AllRETIREMENT', value: 'AllRETIREMENT' },
-                { label: 'RETIREMENT', value: 'RETIREMENT' },
-                { label: 'BREAK_STOP', value: 'BREAK_STOP' }
-            ],
-            customerLossOptions: [
-                { label: 'YES', value: 'YES' },
-                { label: 'NO', value: 'NO' }
-            ]
-        };
-    },
-    methods: {
-        onSave() {
-            this.$refs.form.validate(valid => {
-                if (valid) {
-                    this.submit();
-                } else {
-                    return false;
-                }
-            });
-        },
-        submit() {
-            let data = { ...this.formData };
-
-            this.$store.commit('updateFetchingData', true);
-            this.$http
-                .post('/residence/save', data, { body: 'json' })
-                .then(res => {
-                    this.$store.commit('updateFetchingData', false);
-                    this.$message.success('成功');
-                    this.$router.go(-1);
-                })
-                .catch(e => {
-                    this.$store.commit('updateFetchingData', false);
-                    console.log(e);
-                    this.$message.error(e.error);
-                });
-        },
-        onDelete() {
-            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
-                .then(() => {
-                    return this.$http.post(`/residence/del/${this.formData.id}`);
-                })
-                .then(() => {
-                    this.$message.success('删除成功');
-                    this.$router.go(-1);
-                })
-                .catch(e => {
-                    if (e !== 'cancel') {
-                        console.log(e);
-                        this.$message.error(e.error);
-                    }
-                });
-        }
-    }
-};
-</script>
-<style lang="less" scoped></style>

+ 0 - 283
src/main/vue/src/views/sale/ResidenceList.vue

@@ -1,283 +0,0 @@
-<template>
-    <div class="list-view">
-        <div class="filters-container">
-            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
-            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
-            <el-button @click="addRow" v-if="canEdit" type="primary" icon="el-icon-plus" class="filter-item"
-                >添加
-            </el-button>
-            <el-button
-                @click="download"
-                type="primary"
-                icon="el-icon-download"
-                :loading="downloading"
-                class="filter-item"
-                >导出EXCEL
-            </el-button>
-        </div>
-        <el-table
-            :data="tableData"
-            row-key="id"
-            ref="table"
-            header-row-class-name="table-header-row"
-            header-cell-class-name="table-header-cell"
-            row-class-name="table-row"
-            cell-class-name="table-cell"
-            :height="tableHeight"
-        >
-            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
-            <el-table-column prop="id" label="ID" width="30">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="contractNumber" label="合同编号" width="200px">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="contractId" label="合同编号">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="coSimpleName" label="公司简称">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="falsifyType" label="违约金方式 1 房费 2 押金">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="surplusRoomFee" label="剩余房费">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="surplusFlowBet" label="押金">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="rent" label="金额">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="InformAgreementNo" label="补充协议编号/告知函编号">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="residenceTime" label="退宿时间">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <!--            <el-table-column prop="residenceRoomTypes" label="residenceRoomTypes"-->
-            <!--            >-->
-            <!--                <template slot="header" slot-scope="{column}">-->
-            <!--                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort">-->
-            <!--                    </sortable-header>-->
-            <!--                </template>-->
-            <!--            </el-table-column>-->
-            <el-table-column prop="residenceType" label="退宿类型" :formatter="residenceTypeFormatter">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="residenceReason" label="退宿原因">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="customerLoss" label="客户是否流失" :formatter="customerLossFormatter">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="attach" label="附件">
-                <template v-slot="{ row }">
-                    <a :href="row.attach">下载</a>
-                </template>
-            </el-table-column>
-            <el-table-column prop="deposit" label="押金">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="saleName" label="销售员">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <!--            <el-table-column-->
-            <!--                label="操作"-->
-            <!--                v-if="canEdit"-->
-            <!--                align="center"-->
-            <!--                fixed="right"-->
-            <!--                min-width="150">-->
-            <!--                <template slot-scope="{row}">-->
-            <!--                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>-->
-            <!--                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>-->
-            <!--                </template>-->
-            <!--            </el-table-column>-->
-        </el-table>
-        <div class="pagination-wrapper">
-            <!--            <div class="multiple-mode-wrapper">-->
-            <!--                <el-button v-if="!multipleMode"-->
-            <!--                           @click="toggleMultipleMode(true)">批量编辑-->
-            <!--                </el-button>-->
-            <!--                <el-button-group v-else>-->
-            <!--                    <el-button @click="operation1">批量操作1</el-button>-->
-            <!--                    <el-button @click="operation2">批量操作2</el-button>-->
-            <!--                    <el-button @click="toggleMultipleMode(false)">取消</el-button>-->
-            <!--                </el-button-group>-->
-            <!--            </div>-->
-            <el-pagination
-                background
-                @size-change="onSizeChange"
-                @current-change="onCurrentChange"
-                :current-page="page"
-                :page-sizes="[10, 20, 30, 40, 50]"
-                :page-size="pageSize"
-                layout="total, sizes, prev, pager, next, jumper"
-                :total="totalElements"
-            >
-            </el-pagination>
-        </div>
-    </div>
-</template>
-<script>
-import { mapState } from 'vuex';
-import pageableTable from '@/mixins/pageableTable';
-
-export default {
-    name: 'ResidenceList',
-    mixins: [pageableTable],
-    data() {
-        return {
-            multipleMode: false,
-            search: '',
-            url: '/residence/all',
-            downloading: false,
-            residenceTypeOptions: [
-                { label: '全部退宿', value: 'AllRETIREMENT' },
-                {
-                    label: '部分退宿',
-                    value: 'RETIREMENT'
-                },
-                { label: 'BREAK_STOP', value: 'BREAK_STOP' }
-            ],
-            customerLossOptions: [
-                { label: '是', value: 'YES' },
-                { label: '否', value: 'NO' }
-            ]
-        };
-    },
-    computed: {
-        selection() {
-            return this.$refs.table.selection.map(i => i.id);
-        }
-    },
-    methods: {
-        residenceTypeFormatter(row, column, cellValue, index) {
-            let selectedOption = this.residenceTypeOptions.find(i => i.value === cellValue);
-            if (selectedOption) {
-                return selectedOption.label;
-            }
-            return '';
-        },
-        customerLossFormatter(row, column, cellValue, index) {
-            let selectedOption = this.customerLossOptions.find(i => i.value === cellValue);
-            if (selectedOption) {
-                return selectedOption.label;
-            }
-            return '';
-        },
-        beforeGetData() {
-            if (this.search) {
-                return { search: this.search };
-            }
-        },
-        toggleMultipleMode(multipleMode) {
-            this.multipleMode = multipleMode;
-            if (!multipleMode) {
-                this.$refs.table.clearSelection();
-            }
-        },
-        addRow() {
-            this.$router.push({
-                path: '/residenceEdit',
-                query: {
-                    ...this.$route.query
-                }
-            });
-        },
-        editRow(row) {
-            this.$router.push({
-                path: '/residenceEdit',
-                query: {
-                    id: row.id
-                }
-            });
-        },
-        download() {
-            this.downloading = true;
-            this.$axios
-                .get('/residence/excel', {
-                    responseType: 'blob',
-                    params: { size: 10000 }
-                })
-                .then(res => {
-                    console.log(res);
-                    this.downloading = false;
-                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
-                    const link = document.createElement('a');
-                    link.href = downloadUrl;
-                    link.setAttribute(
-                        'download',
-                        decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])
-                    );
-                    document.body.appendChild(link);
-                    link.click();
-                    link.remove();
-                })
-                .catch(e => {
-                    console.log(e);
-                    this.downloading = false;
-                    this.$message.error(e.error);
-                });
-        },
-        operation1() {
-            this.$notify({
-                title: '提示',
-                message: this.selection
-            });
-        },
-        operation2() {
-            this.$message('操作2');
-        },
-        deleteRow(row) {
-            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
-                .then(() => {
-                    return this.$http.post(`/residence/del/${row.id}`);
-                })
-                .then(() => {
-                    this.$message.success('删除成功');
-                    this.getData();
-                })
-                .catch(action => {
-                    if (action === 'cancel') {
-                        this.$message.info('删除取消');
-                    } else {
-                        this.$message.error('删除失败');
-                    }
-                });
-        }
-    }
-};
-</script>
-<style lang="less" scoped></style>

+ 17 - 0
src/test/java/com/izouma/zhumj/service/ContractViolationServiceTest.java

@@ -0,0 +1,17 @@
+package com.izouma.zhumj.service;
+
+import com.izouma.zhumj.ZhumjApplicationTests;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.Assert.*;
+
+public class ContractViolationServiceTest extends ZhumjApplicationTests {
+    @Autowired
+    private ContractViolationService contractViolationService;
+
+    @Test
+    public void migrate() {
+        contractViolationService.migrate();
+    }
+}