b922b12d1dc650df75530cc52af5451a5bbfece1..3849263b31a16a91ff08acaa42786ecfde76f33c
2025-11-27 zhangback
提交
384926 对比 | 目录
2025-11-27 zhangback
提交
d457a0 对比 | 目录
30个文件已添加
35个文件已修改
5333 ■■■■ 已修改文件
admin/config/test/application-custom.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main/resources/application-custom.yml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main/resources/application.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main/resources/chqjkcns.pdf 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main/resources/fonts/simsun.ttc 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/main/resources/zjsf.pdf 补丁 | 查看 | 原始文档 | blame | 历史
api/src/main/java/com/ruoyi/api/third/controller/CarWxController.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/pom.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ruoyi/common/constant/Constants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ruoyi/common/enums/SystemDataNoEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/controller/TmsMessageNotifyController.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/controller/TmsQuoteItemController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/controller/TransportRouteViController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsContract.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsDispatchOrder.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsFinanceDetail.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsMessageNotify.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsQuoteDetail.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsQuoteItem.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TmsQuotePlan.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/TransportRouteVi.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/vo/FinanceDetailItem.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/domain/vo/QuoteDetailItem.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/mapper/TmsMessageNotifyMapper.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/mapper/TmsQuoteItemMapper.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/mapper/TransportRouteViMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/ITmsMessageNotifyService.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/ITmsQuoteItemService.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsFinanceDetailServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsFinanceServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsMessageNotifyServiceImpl.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuoteDetailServiceImpl.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuoteItemServiceImpl.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuotePlanServiceImpl.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/service/impl/TmsSettlementEntityServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/java/com/ruoyi/tms/task/ContractExpireNotifyTask.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsConsignorMapper.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsContractMapper.xml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsDispatchOrderMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsFinanceDetailMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsMessageNotifyMapper.xml 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsQuoteDetailMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TmsQuoteItemMapper.xml 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tms/src/main/resources/mapper/tms/TransportRouteViMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/common.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsContract.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsMessageNotify.ts 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsQuoteDetail.ts 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsQuotePlan.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsQuotePlanItem.ts 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/api/tms/tmsTransportRouteVi.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/assets/tongzhi.png 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/hooks/usePagePlus.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/layout/components/Navbar.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/router/index.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/collectionTmsQuoteDetail/index.vue 630 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/collectionTmsQuotePlan/index.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/paymentTmsQuotePlan/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsContract/index.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsDispatchOrder/index.vue 1263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsMessageNotify/index.vue 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsProject/index.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsSettlementEntity/index.vue 151 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ui/admin-ui3/src/views/tms/tmsVehicle/index.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/config/test/application-custom.yml
@@ -106,4 +106,8 @@
    # 接口超时设置(毫秒)
    timeout: 30000
    # 重试次数
    retryCount: 3
    retryCount: 3
  driver:
    template1: /data/cwgl/chqjkcns.pdf
    template2: /data/cwgl/zjsf.pdf
    font: /data/cwgl/simsun.ttc
admin/src/main/resources/application-custom.yml
@@ -81,6 +81,7 @@
      /**/*.js
    #允许匿名访问
    anonymousPaths:
      /app/car/download/resource
      /app/car/login
      /common/*
      /login
@@ -108,4 +109,8 @@
    # 接口超时设置(毫秒)
    timeout: 30000
    # 重试次数
    retryCount: 3
    retryCount: 3
  driver:
    template1: D:/temp/chqjkcns.pdf
    template2: D:/temp/zjsf.pdf
    font: D:/temp/simsun.ttc
admin/src/main/resources/application.yml
@@ -195,3 +195,5 @@
  excludes: /system/notice
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*
admin/src/main/resources/chqjkcns.pdf
Binary files differ
admin/src/main/resources/fonts/simsun.ttc
Binary files differ
admin/src/main/resources/zjsf.pdf
Binary files differ
api/src/main/java/com/ruoyi/api/third/controller/CarWxController.java
@@ -6,24 +6,33 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PdfTemplateUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.service.ISysDictTypeService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.tms.domain.TmsDriver;
import com.ruoyi.tms.domain.TmsFinanceDetail;
import com.ruoyi.tms.domain.TmsTrip;
import com.ruoyi.tms.service.ITmsDispatchOrderService;
import com.ruoyi.tms.service.ITmsDriverService;
import com.ruoyi.tms.service.ITmsFinanceDetailService;
import com.ruoyi.tms.service.ITmsTripService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@RestController
@@ -47,6 +56,19 @@
    @Value("${custom.upload.network-path}")
    private String networkPath;
    @Value("${custom.driver.template1}")
    private String template1;
    @Value("${custom.driver.template2}")
    private String template2;
    @Value("${custom.driver.font}")
    private String fontPath;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private ITmsDriverService tmsDriverService;
    /**
     * 登录方法[pda]
@@ -185,6 +207,52 @@
        }
        return AjaxResult.success(data);
    }
    @GetMapping("/resource/jkcns")
    public void jkcns( HttpServletResponse response) throws Exception {
        Integer deviceId = userService.getDeviceId(SecurityUtils.getUserId());
        if (deviceId == null){
            return;
        }
        TmsDriver byId = tmsDriverService.getById(deviceId);
        if (byId != null){
            HashMap<String, String> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put("date", DateUtils.getDate());
            objectObjectHashMap.put("name", byId.getDriverName());
            objectObjectHashMap.put("idCard", byId.getLicenseNumber());
            // 下载名称
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, "货车司机出车前健康承诺书_"+byId.getDriverName()+"pdf");
            FileUtils.writeBytes(template1, response.getOutputStream());
            PdfTemplateUtil.fillPdf(template2, response.getOutputStream(),objectObjectHashMap , fontPath);
        }
    }
    @GetMapping("/resource/zjsfcns")
    public void zjsfcns( HttpServletResponse response) throws Exception {
        // 下载名称
        Integer deviceId = userService.getDeviceId(SecurityUtils.getUserId());
        if (deviceId == null){
            return;
        }
        TmsDriver byId = tmsDriverService.getById(deviceId);
        if (byId != null){
            HashMap<String, String> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put("date", DateUtils.getDate());
            // 下载名称
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, "遵纪守法承诺书_"+byId.getDriverName()+"pdf");
            PdfTemplateUtil.fillPdf(template2, response.getOutputStream(),objectObjectHashMap , fontPath);
        }
    }
}
common/pom.xml
@@ -168,6 +168,21 @@
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.35</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-transcoder</artifactId>
            <version>1.16</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.16</version>
        </dependency>
    </dependencies>
</project>
common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -163,7 +163,7 @@
    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task","com.ruoyi.tms.task" };
    /**
     * 定时任务违规的字符
common/src/main/java/com/ruoyi/common/enums/SystemDataNoEnum.java
@@ -26,6 +26,7 @@
    OF("OF","OF","其他账单编号"),
    CUST("CUST","CUST","客户编号"),
    ZXH("ZXH","ZXH","装卸货点"),
    YY("YY","YY","运营编号")
common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java
New file
@@ -0,0 +1,239 @@
package com.ruoyi.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;
import java.util.Map;
@Slf4j
public class PdfTemplateUtil {
    /**
     * 填充 PDF 表单字段
     *
     * @param templatePdf 模板 PDF 路径
     * @param outputPdf   输出 PDF 路径
     * @param params      key=表单字段名, value=填充值
     * @param fontPath    中文字体 ttf 路径
     * @throws Exception
     */
    public static void fillPdf(String templatePdf, String outputPdf, Map<String, String> params, String fontPath) throws Exception {
        PDDocument document = PDDocument.load(new File(templatePdf));
        PDAcroForm form = document.getDocumentCatalog().getAcroForm();
        PDType0Font font = PDType0Font.load(document, new File(fontPath));
        // 注册字体资源
        PDResources resources = form.getDefaultResources();
        if (resources == null) {
            resources = new PDResources();
            form.setDefaultResources(resources);
        }
        COSName fontResName = resources.add(font);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            PDField field = form.getField(entry.getKey());
            // 文字字段
            if (field instanceof PDTextField) {
                PDTextField tf = (PDTextField) field;
                tf.setDefaultAppearance("/" + fontResName.getName() + " 10 Tf 0 0 0 rg");
                tf.getCOSObject().setInt(COSName.Q, 1); // 水平居中
                tf.setValue(entry.getValue());
            }
            // 图片按钮字段
            if (field instanceof PDPushButton) {
                PDPushButton button = (PDPushButton) field;
                String imgBase64 = entry.getValue();
                if (imgBase64.contains(",")) {
                    imgBase64 = imgBase64.split(",")[1];  // 去掉前缀
                }
                byte[] imageBytes;
                if (entry.getValue().startsWith("data:image/svg+xml;base64,")) {
                    // SVG 转 PNG
                    byte[] svgBytes = Base64.getDecoder().decode(imgBase64);
                    ByteArrayInputStream svgStream = new ByteArrayInputStream(svgBytes);
                    PNGTranscoder transcoder = new PNGTranscoder();
                    // 输出到 ByteArray
                    ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
                    TranscoderInput input = new TranscoderInput(svgStream);
                    TranscoderOutput output = new TranscoderOutput(pngOut);
                    transcoder.transcode(input, output);
                    pngOut.flush();
                    pngOut.close();
                    imageBytes = pngOut.toByteArray();
                    // BufferedImage 读取
                    BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
                    // 旋转 90°
                    int w = bufferedImage.getWidth();
                    int h = bufferedImage.getHeight();
                    BufferedImage rotated = new BufferedImage(h, w, bufferedImage.getType());
                    Graphics2D g2d = rotated.createGraphics();
                    g2d.translate(h / 2.0, w / 2.0);
                    g2d.rotate(Math.toRadians(90));
                    g2d.translate(-w / 2.0, -h / 2.0);
                    g2d.drawImage(bufferedImage, 0, 0, null);
                    g2d.dispose();
                    // 转回字节数组
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(rotated, "png", baos);
                    imageBytes = baos.toByteArray();
                } else {
                    // PNG/JPG 直接解码
                    imageBytes = Base64.getDecoder().decode(imgBase64);
                }
                // 创建 PDImageXObject 并绘制
                PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "img");
                PDAppearanceStream appearance = button.getWidgets().get(0).getNormalAppearanceStream();
                PDPageContentStream cs = new PDPageContentStream(document, appearance);
                cs.drawImage(pdImage, 0, 0, appearance.getBBox().getWidth(), appearance.getBBox().getHeight());
                cs.close();
            }
        }
        form.refreshAppearances();
        form.flatten();
        document.save(outputPdf);
        document.close();
    }
    public static void fillPdf(String templatePdf, OutputStream outputStream, Map<String, String> params, String fontPath) throws Exception {
        PDDocument document = PDDocument.load(new File(templatePdf));
        PDAcroForm form = document.getDocumentCatalog().getAcroForm();
        PDType0Font font = PDType0Font.load(document, new File(fontPath));
        // 注册字体资源
        PDResources resources = form.getDefaultResources();
        if (resources == null) {
            resources = new PDResources();
            form.setDefaultResources(resources);
        }
        COSName fontResName = resources.add(font);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            PDField field = form.getField(entry.getKey());
            // 文字字段
            if (field instanceof PDTextField) {
                PDTextField tf = (PDTextField) field;
                tf.setDefaultAppearance("/" + fontResName.getName() + " 10 Tf 0 0 0 rg");
                tf.getCOSObject().setInt(COSName.Q, 1); // 水平居中
                tf.setValue(entry.getValue());
            }
            // 图片按钮字段
            if (field instanceof PDPushButton) {
                PDPushButton button = (PDPushButton) field;
                String imgBase64 = entry.getValue();
                if (imgBase64.contains(",")) {
                    imgBase64 = imgBase64.split(",")[1];  // 去掉前缀
                }
                byte[] imageBytes;
                if (entry.getValue().startsWith("data:image/svg+xml;base64,")) {
                    // SVG 转 PNG
                    byte[] svgBytes = Base64.getDecoder().decode(imgBase64);
                    ByteArrayInputStream svgStream = new ByteArrayInputStream(svgBytes);
                    PNGTranscoder transcoder = new PNGTranscoder();
                    // 输出到 ByteArray
                    ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
                    TranscoderInput input = new TranscoderInput(svgStream);
                    TranscoderOutput output = new TranscoderOutput(pngOut);
                    transcoder.transcode(input, output);
                    pngOut.flush();
                    pngOut.close();
                    imageBytes = pngOut.toByteArray();
                    // BufferedImage 读取
                    BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
                    // 旋转 90°
                    int w = bufferedImage.getWidth();
                    int h = bufferedImage.getHeight();
                    BufferedImage rotated = new BufferedImage(h, w, bufferedImage.getType());
                    Graphics2D g2d = rotated.createGraphics();
                    g2d.translate(h / 2.0, w / 2.0);
                    g2d.rotate(Math.toRadians(90));
                    g2d.translate(-w / 2.0, -h / 2.0);
                    g2d.drawImage(bufferedImage, 0, 0, null);
                    g2d.dispose();
                    // 转回字节数组
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(rotated, "png", baos);
                    imageBytes = baos.toByteArray();
                } else {
                    // PNG/JPG 直接解码
                    imageBytes = Base64.getDecoder().decode(imgBase64);
                }
                // 创建 PDImageXObject 并绘制
                PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "img");
                PDAppearanceStream appearance = button.getWidgets().get(0).getNormalAppearanceStream();
                PDPageContentStream cs = new PDPageContentStream(document, appearance);
                cs.drawImage(pdImage, 0, 0, appearance.getBBox().getWidth(), appearance.getBBox().getHeight());
                cs.close();
            }
        }
        form.refreshAppearances();
        form.flatten();
        document.save(outputStream);
        document.close();
    }
    public static void main(String[] args) throws Exception {
        String template = "D:/temp/zjsf.pdf";
        String filled = "D:/temp/tes3.pdf";
        String finalPdf = "D:/temp/commit_final.pdf";
        // Java 8 写法:用 HashMap 初始化参数
        Map<String, String> map = new java.util.HashMap<String, String>();
        map.put("name", "张三");
        map.put("idCard", "440***********1234");
        map.put("date", "2025-11-27");
       map.put("signImage", "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iNjY0LjM5MDYyNSIgaGVpZ2h0PSIzMjgiPjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMTg1LjMgMjk2IEwgMTg1LjMgMjk2IEwgMTgzLjYgMjkwIEwgMTgwLjYgMjgyIEwgMTc3LjYgMjcyIEwgMTc0LjYgMjYwIEwgMTcxLjYgMjQxIEwgMTY3LjYgMjIzIEwgMTYzLjYgMjAyIEwgMTYwLjYgMTg0IEwgMTU4LjYgMTY2IEwgMTU1LjYgMTUwIEwgMTU0LjYgMTM3IEwgMTU0LjYgMTI5IEwgMTU0LjYgMTI0IEwgMTU0LjYgMTIyIEwgMTU1LjYgMTIxIEwgMTU3LjYgMTIxIEwgMTYwLjYgMTIyIEwgMTY5LjYgMTI5IEwgMTc3LjYgMTQwIEwgMTg5LjYgMTYxIEwgMTg5LjYgMTYxIi8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMjk2LjMgMzIzIEwgMjk2LjMgMzIzIEwgMjk2LjYgMzIyIEwgMjk2LjYgMzIwIEwgMjk2LjYgMzE2IEwgMjk1LjYgMzA5IEwgMjkzLjYgMzAzIEwgMjg4LjYgMjkzIEwgMjg0LjYgMjgyIEwgMjc5LjYgMjcwIEwgMjc2LjYgMjU4IEwgMjcyLjYgMjQ1IEwgMjY3LjYgMjM0IEwgMjY0LjYgMjI3IEwgMjYxLjYgMjIxIEwgMjU5LjYgMjE4IEwgMjU1LjYgMjE1IEwgMjUyLjYgMjE1IEwgMjQ4LjYgMjE1IEwgMjQxLjYgMjE1IEwgMjQxLjYgMjE1Ii8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMTcwLjMgMjQ1IEwgMTcwLjMgMjQ1IEwgMTc3LjYgMjQ3IEwgMTg1LjYgMjUxIEwgMTk1LjYgMjU1IEwgMjA5LjYgMjYwIEwgMjIxLjYgMjY2IEwgMjM3LjYgMjcwIEwgMjUxLjYgMjc1IEwgMjY0LjYgMjc4IEwgMjc1LjYgMjc4IEwgMjg0LjYgMjc4IEwgMjkxLjYgMjc4IEwgMjk2LjYgMjc2IEwgMzAwLjYgMjcyIEwgMzAyLjYgMjY5IEwgMzA0LjYgMjYzIEwgMzA1LjYgMjU2IEwgMzA1LjYgMjQ3IEwgMzA1LjYgMjM1IEwgMzAxLjYgMjE5IEwgMjk3LjYgMjA3IEwgMjkyLjYgMTkxIEwgMjg4LjYgMTc5IEwgMjgzLjYgMTcwIEwgMjgyLjYgMTY2IEwgMjgwLjYgMTY1IEwgMjgyLjYgMTY5IEwgMjg5LjYgMTc2IEwgMjk0LjYgMTg0IEwgMzAyLjYgMTk0IEwgMzA4LjYgMjAyIEwgMzE1LjYgMjExIEwgMzIyLjYgMjE5IEwgMzI3LjYgMjIzIEwgMzMzLjYgMjI3IEwgMzM4LjYgMjI5IEwgMzQxLjYgMjMxIEwgMzQzLjYgMjMxIEwgMzQ1LjYgMjMyIEwgMzQ2LjYgMjMyIEwgMzQ3LjYgMjMyIEwgMzQ4LjYgMjMxIEwgMzQ4LjYgMjMwIEwgMzQ4LjYgMjI5IEwgMzQ3LjYgMjI3IEwgMzQ2LjYgMjI2IEwgMzQzLjYgMjI1IEwgMzQxLjYgMjI1IEwgMzM4LjYgMjI2IEwgMzM1LjYgMjI4IEwgMzMyLjYgMjMwIEwgMzMwLjYgMjM1IEwgMzMwLjYgMjM1Ii8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMzE2LjMgMzExIEwgMzE2LjMgMzExIEwgMzE3LjYgMzEyIEwgMzE3LjYgMzEzIEwgMzE4LjYgMzEzIEwgMzE5LjYgMzEzIEwgMzIxLjYgMzEzIEwgMzIzLjYgMzExIEwgMzI4LjYgMzAzIEwgMzM0LjYgMjkzIEwgMzQwLjYgMjc1IEwgMzQ4LjYgMjUzIEwgMzU2LjYgMjI2IEwgMzYzLjYgMTkxIEwgMzcyLjYgMTU0IEwgMzc3LjYgMTE1IEwgMzc5LjYgNzkgTCAzODAuNiA0NiBMIDM4MC42IDE4IEwgMzgwLjYgLTQgTCAzODAuNiAtMjAgTCAzODAuNiAtMzMgTCAzODAuNiAtNDQgTCAzODAuNiAtNTMgTCAzNzkuNiAtNTkgTCAzNzkuNiAtNjIgTCAzNzkuNiAtNjMgTCAzNzguNiAtNjMgTCAzNzcuNiAtNjMgTCAzNzIuNiAtNTkgTCAzNjUuNiAtNTIgTCAzNTcuNiAtNDQgTCAzNTAuNiAtMzQgTCAzNTAuNiAtMzQiLz4KPHBhdGggc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZT0icmdiKDAsMCwwKSIgZmlsbD0ibm9uZSIgZD0iTSAyNDQuMyAxNDYgTCAyNDQuMyAxNDYgTCAyNDUuNiAxNDYgTCAyNDguNiAxNDQgTCAyNTIuNiAxNDEgTCAyNTguNiAxMzcgTCAyNjQuNiAxMzIgTCAyNzAuNiAxMjcgTCAyNzkuNiAxMTkgTCAyODYuNiAxMTIgTCAyOTUuNiAxMDMgTCAzMDMuNiA5NSBMIDMwOS42IDg3IEwgMzE1LjYgNzggTCAzMTguNiA3MSBMIDMyMS42IDYyIEwgMzIyLjYgNTYgTCAzMjIuNiA1MSBMIDMyMS42IDQ4IEwgMzE4LjYgNDYgTCAzMTIuNiA0MyBMIDMwNi42IDQxIEwgMjk2LjYgNDAgTCAyOTYuNiA0MCIvPgo8cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlPSJyZ2IoMCwwLDApIiBmaWxsPSJub25lIiBkPSJNIDI1Ni4zIDExMCBMIDI1Ni4zIDExMCBMIDI1OS42IDExNCBMIDI2NC42IDExOCBMIDI2OC42IDEyMSBMIDI3MS42IDEyMiBMIDI3My42IDEyNCBMIDI3NS42IDEyNSBMIDI3Ny42IDEyNSBMIDI3OC42IDEyNiBMIDI3OS42IDEyNiBMIDI3Ny42IDEyNiBMIDI3My42IDEyNiBMIDI3My42IDEyNiIvPjwvc3ZnPg==");
        // 1. 填充文字字段
        fillPdf(template, filled, map,"D:\\temp\\msyh.ttf");
    }
}
tms/src/main/java/com/ruoyi/tms/controller/TmsMessageNotifyController.java
New file
@@ -0,0 +1,144 @@
package com.ruoyi.tms.controller;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.utils.file.DownloadExportUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.tms.domain.TmsMessageNotify;
import com.ruoyi.tms.service.ITmsMessageNotifyService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
 * 消息通知Controller
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@RestController
@RequestMapping("/tms/tmsMessageNotify")
public class TmsMessageNotifyController extends BaseController
{
    @Autowired
    private ITmsMessageNotifyService tmsMessageNotifyService;
    @GetMapping("noCount")
    public AjaxResult noCount(){
        Long l = tmsMessageNotifyService.getBaseMapper().selectCount(new LambdaQueryWrapper<TmsMessageNotify>()
                .eq(TmsMessageNotify::getReadStatus, 0));
        List<TmsMessageNotify> messageNotifies = tmsMessageNotifyService.getBaseMapper().selectList(new LambdaQueryWrapper<TmsMessageNotify>()
                .eq(TmsMessageNotify::getReadStatus, 0)
                .orderByDesc(TmsMessageNotify::getId)
                .last("limit 5")
        );
        Map<String,Object> has = new HashMap<>();
        has.put("count",l);
        has.put("list",messageNotifies);
        return AjaxResult.success(has);
    }
    /**
     * 获取消息通知详细信息
     */
    @GetMapping(value = "/read/{id}")
    public AjaxResult read(@PathVariable("id") Integer id)
    {
        tmsMessageNotifyService.updateById(new TmsMessageNotify(){{
            setId(id);
            setReadStatus(1);
            setReadTime(new Date());
        }});
        return AjaxResult.success(tmsMessageNotifyService.selectTmsMessageNotifyById(id));
    }
    /**
     * 查询消息通知列表
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:list')")
    @GetMapping("/list")
    public TableDataInfo list(TmsMessageNotify tmsMessageNotify)
    {
        startPage();
        List<TmsMessageNotify> list = tmsMessageNotifyService.selectTmsMessageNotifyList(tmsMessageNotify);
        return getDataTable(list);
    }
    /**
     * 导出消息通知列表
     * @param tmsMessageNotify 查询条件对象
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:export')")
    @Log(title = "消息通知", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(TmsMessageNotify tmsMessageNotify,String exportKey)
    {
        tmsMessageNotifyService.export(tmsMessageNotify,exportKey);
        return AjaxResult.success("导出请求成功,请稍后点击下载...!");
    }
    /**
     * 获取消息通知详细信息
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Integer id)
    {
        return AjaxResult.success(tmsMessageNotifyService.selectTmsMessageNotifyById(id));
    }
    /**
     * 新增消息通知
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:add')")
    @Log(title = "消息通知", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody TmsMessageNotify tmsMessageNotify)
    {
        return toAjax(tmsMessageNotifyService.insertTmsMessageNotify(tmsMessageNotify));
    }
    /**
     * 修改消息通知
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:edit')")
    @Log(title = "消息通知", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody TmsMessageNotify tmsMessageNotify)
    {
        return toAjax(tmsMessageNotifyService.updateTmsMessageNotify(tmsMessageNotify));
    }
    /**
     * 删除消息通知
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsMessageNotify:remove')")
    @Log(title = "消息通知", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Integer[] ids)
    {
        return toAjax(tmsMessageNotifyService.deleteTmsMessageNotifyByIds(ids));
    }
}
tms/src/main/java/com/ruoyi/tms/controller/TmsQuoteItemController.java
New file
@@ -0,0 +1,108 @@
package com.ruoyi.tms.controller;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.utils.file.DownloadExportUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.tms.domain.TmsQuoteItem;
import com.ruoyi.tms.service.ITmsQuoteItemService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
 * 报价项目Controller
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@RestController
@RequestMapping("/tms/tmsQuoteItem")
public class TmsQuoteItemController extends BaseController
{
    @Autowired
    private ITmsQuoteItemService tmsQuoteItemService;
    /**
     * 查询报价项目列表
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:list')")
    @GetMapping("/list")
    public TableDataInfo list(TmsQuoteItem tmsQuoteItem)
    {
        startPage();
        List<TmsQuoteItem> list = tmsQuoteItemService.selectTmsQuoteItemList(tmsQuoteItem);
        return getDataTable(list);
    }
    /**
     * 导出报价项目列表
     * @param tmsQuoteItem 查询条件对象
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:export')")
    @Log(title = "报价项目", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(TmsQuoteItem tmsQuoteItem,String exportKey)
    {
        tmsQuoteItemService.export(tmsQuoteItem,exportKey);
        return AjaxResult.success("导出请求成功,请稍后点击下载...!");
    }
    /**
     * 获取报价项目详细信息
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Integer id)
    {
        return AjaxResult.success(tmsQuoteItemService.selectTmsQuoteItemById(id));
    }
    /**
     * 新增报价项目
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:add')")
    @Log(title = "报价项目", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody TmsQuoteItem tmsQuoteItem)
    {
        return toAjax(tmsQuoteItemService.insertTmsQuoteItem(tmsQuoteItem));
    }
    /**
     * 修改报价项目
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:edit')")
    @Log(title = "报价项目", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody TmsQuoteItem tmsQuoteItem)
    {
        return toAjax(tmsQuoteItemService.updateTmsQuoteItem(tmsQuoteItem));
    }
    /**
     * 删除报价项目
     */
    @PreAuthorize("@ss.hasPermi('tms:tmsQuoteItem:remove')")
    @Log(title = "报价项目", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Integer[] ids)
    {
        return toAjax(tmsQuoteItemService.deleteTmsQuoteItemByIds(ids));
    }
}
tms/src/main/java/com/ruoyi/tms/controller/TransportRouteViController.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.tms.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.tms.domain.TmsConsignor;
import com.ruoyi.tms.domain.TransportRouteVi;
import com.ruoyi.tms.mapper.TransportRouteViMapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/tms/transportRouteVi")
public class TransportRouteViController extends BaseController {
    @Resource
    TransportRouteViMapper transportRouteViMapper;
    /**
     * 查询收发货人管理列表
     */
    @GetMapping("/list")
    public TableDataInfo list(TransportRouteVi bo)
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Page<TransportRouteVi> pageParam = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
        Page<TransportRouteVi> page = transportRouteViMapper.selectPage(pageParam,
                new LambdaQueryWrapper<TransportRouteVi>()
                        .like(StringUtils.isNotEmpty(bo.getTransportRoute()),TransportRouteVi::getTransportRoute, bo.getTransportRoute())
                        .like(StringUtils.isNotEmpty(bo.getCustomerFullName()),TransportRouteVi::getCustomerFullName, bo.getCustomerFullName())
                        .like(StringUtils.isNotEmpty(bo.getProjectName()),TransportRouteVi::getProjectName, bo.getProjectName())
                        .like(StringUtils.isNotEmpty(bo.getContractName()),TransportRouteVi::getContractName, bo.getContractName())
                        .eq(bo.getVehicleType()!=null,TransportRouteVi::getVehicleType, bo.getVehicleType())
        );
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查询成功");
        rspData.setRows(page.getRecords());
        rspData.setTotal(page.getTotal());
        return rspData;
    }
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Integer id)
    {
        return AjaxResult.success(transportRouteViMapper.selectById(id));
    }
}
tms/src/main/java/com/ruoyi/tms/domain/TmsContract.java
@@ -294,5 +294,10 @@
    @TableField("remark")
    private String remark;
    /** 是否是临时合同 */
    @Excel(name = "是否是临时合同")
    @TableField("is_temp_contract")
    private Integer isTempContract;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsDispatchOrder.java
@@ -450,4 +450,22 @@
    @TableField(exist = false)
    private BigDecimal paymentPlanAmount;
    @TableField("is_customs")
    private Integer isCustoms;
    @TableField("start_region_code")
    private String startRegionCode;
    @TableField("end_region_code")
    private String endRegionCode;
    @TableField("quote_detail_id")
    private Integer quoteDetailId;
    @TableField("operation_mode")
    private Integer operationMode;
    @Excel(name = "关联报价方案ID")
    @TableField("quote_plan_id")
    private Integer quotePlanId;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsFinanceDetail.java
@@ -12,7 +12,9 @@
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
import java.util.List;
import com.ruoyi.tms.domain.vo.FinanceDetailItem;
import lombok.Data;
/**
@@ -37,7 +39,7 @@
     */
    @Excel(name = "费用类型")
    @TableField("fee_type")
    private Integer feeType;
    private String feeType;
    /**
@@ -129,6 +131,29 @@
    @TableField("update_by")
    private String updateBy;
    /**
     * 单价
     */
    @TableField("price")
    private BigDecimal price;
    /**
     * 计费单位
     */
    @TableField("unit")
    private String unit;
    /**
     * 币种
     */
    @TableField("currency")
    private String  currency;
    /**
     * 计费数量
     */
    @TableField("count")
    private Integer count;
    /**
     * 更新时间
@@ -140,5 +165,8 @@
    @TableField("status")
    private Integer status;
    @TableField(exist = false)
    private List<FinanceDetailItem> items;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsMessageNotify.java
New file
@@ -0,0 +1,131 @@
package com.ruoyi.tms.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
import lombok.Data;
/**
 * 消息通知对象 tms_message_notify
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@Data
public class TmsMessageNotify {
    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 消息标题
     */
    @Excel(name = "消息标题")
    @TableField("title")
    private String title;
    /**
     * 消息内容
     */
    @Excel(name = "消息内容")
    @TableField("content")
    private String content;
    /**
     * 消息类型:0系统通知 1业务通知 2告警 3营销
     */
    @Excel(name = "消息类型:0系统通知 1业务通知 2告警 3营销")
    @TableField("type")
    private Integer type;
    /**
     * 接收用户ID(NULL=全体)
     */
    @Excel(name = "接收用户ID", readConverterExp = "NULL=NULL=全体")
    @TableField("target_uid")
    private Integer targetUid;
    /**
     * 发送状态:0待发送 1成功 2失败
     */
    @Excel(name = "发送状态:0待发送 1成功 2失败")
    @TableField("status")
    private Integer status;
    /**
     * 阅读状态:0未读 1已读
     */
    @Excel(name = "阅读状态:0未读 1已读")
    @TableField("read_status")
    private Integer readStatus;
    /**
     * 读取时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "读取时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @TableField("read_time")
    private Date readTime;
    /**
     * 扩展信息(JSON文本)
     */
    @Excel(name = "扩展信息(JSON文本)")
    @TableField("extra_data")
    private String extraData;
    /**
     * 创建人
     */
    @Excel(name = "创建人")
    @TableField("create_uid")
    private Integer createUid;
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;
    /**
     * 更新人
     */
    @Excel(name = "更新人")
    @TableField("update_uid")
    private Integer updateUid;
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("update_time")
    private Date updateTime;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsQuoteDetail.java
@@ -10,7 +10,9 @@
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
import java.util.List;
import com.ruoyi.tms.domain.vo.QuoteDetailItem;
import lombok.Data;
/**
@@ -160,6 +162,10 @@
    @TableField("freight_price")
    private BigDecimal freightPrice;
    @Excel(name = "币种")
    @TableField("currency")
    private String currency;
    /**
     * 状态
@@ -210,4 +216,7 @@
    @TableField("plan_type")
    private Integer planType;
    @TableField(exist = false)
    private List<QuoteDetailItem> quoteItems;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsQuoteItem.java
New file
@@ -0,0 +1,89 @@
package com.ruoyi.tms.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Excel;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
import lombok.Data;
/**
 * 报价项目对象 tms_quote_item
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@Data
public class TmsQuoteItem {
    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 关联报价方案ID
     */
    @Excel(name = "关联报价方案ID")
    @TableField("quote_plan_id")
    private Integer quotePlanId;
    /**
     * 费用名称
     */
    @Excel(name = "费用名称")
    @TableField("free_name")
    private String freeName;
    /**
     * 单位
     */
    @Excel(name = "单位")
    @TableField("unit")
    private String unit;
    /**
     * 金额
     */
    @Excel(name = "金额")
    @TableField("price")
    private BigDecimal price;
    /**
     * 币制
     */
    @Excel(name = "币制")
    @TableField("currency")
    private String currency;
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;
    /**
     * 修改时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("update_time")
    private Date updateTime;
}
tms/src/main/java/com/ruoyi/tms/domain/TmsQuotePlan.java
@@ -11,6 +11,7 @@
import com.baomidou.mybatisplus.annotation.TableField;
import java.util.Date;
import java.util.List;
import lombok.Data;
@@ -163,4 +164,7 @@
    @TableField("plan_type")
    private Integer planType;
    @TableField(exist = false)
    private List<TmsQuoteItem> quoteItems;
}
tms/src/main/java/com/ruoyi/tms/domain/TransportRouteVi.java
New file
@@ -0,0 +1,53 @@
package com.ruoyi.tms.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("transport_route_vi")
public class TransportRouteVi {
    /**
     * 报价明细ID
     */
    @TableId
    private Integer quoteDetailId;
    private Integer quotePlanId;
    /**
     * 路线
     */
    private String transportRoute;
    private String startRegionCode;
    private String endRegionCode;
    /**
     * 客户ID
     */
    private Integer customerId;
    /**
     * 客户全称呼
     */
    private String customerFullName;
    private Integer projectId;
    private String projectName;
    private Integer contractId;
    private String contractName;
    private Integer vehicleType;
}
tms/src/main/java/com/ruoyi/tms/domain/vo/FinanceDetailItem.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.tms.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class FinanceDetailItem {
    private String feeType;
    private BigDecimal price;
    private Integer count;
    private String unit;
}
tms/src/main/java/com/ruoyi/tms/domain/vo/QuoteDetailItem.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.tms.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class QuoteDetailItem {
    private Integer vehicleType;
    private BigDecimal freightPrice;
    private String currency;
}
tms/src/main/java/com/ruoyi/tms/mapper/TmsMessageNotifyMapper.java
New file
@@ -0,0 +1,87 @@
package com.ruoyi.tms.mapper;
import java.util.List;
import com.ruoyi.tms.domain.TmsMessageNotify;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * 消息通知Mapper接口
 *
 * @author ruoyi
 * @date 2025-11-26
 */
public interface TmsMessageNotifyMapper  extends BaseMapper<TmsMessageNotify>
{
    /**
     * 查询消息通知
     *
     * @param id 消息通知ID
     * @return 消息通知
     */
    public TmsMessageNotify selectTmsMessageNotifyById(Integer id);
    /**
     * 查询消息通知 记录数
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知集合
     */
    public int selectTmsMessageNotifyCount(TmsMessageNotify tmsMessageNotify);
    /**
     * 查询消息通知列表
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知集合
     */
    public List<TmsMessageNotify> selectTmsMessageNotifyList(TmsMessageNotify tmsMessageNotify);
    /**
     * 新增消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    public int insertTmsMessageNotify(TmsMessageNotify tmsMessageNotify);
    /**
     * 新增消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    public int insertTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys);
    /**
     * 修改消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    public int updateTmsMessageNotify(TmsMessageNotify tmsMessageNotify);
    /**
     * 修改消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    public int updateTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys);
    /**
     * 删除消息通知
     *
     * @param id 消息通知ID
     * @return 结果
     */
    public int deleteTmsMessageNotifyById(Integer id);
    /**
     * 批量删除消息通知
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsMessageNotifyByIds(Integer[] ids);
}
tms/src/main/java/com/ruoyi/tms/mapper/TmsQuoteItemMapper.java
New file
@@ -0,0 +1,87 @@
package com.ruoyi.tms.mapper;
import java.util.List;
import com.ruoyi.tms.domain.TmsQuoteItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * 报价项目Mapper接口
 *
 * @author ruoyi
 * @date 2025-11-26
 */
public interface TmsQuoteItemMapper  extends BaseMapper<TmsQuoteItem>
{
    /**
     * 查询报价项目
     *
     * @param id 报价项目ID
     * @return 报价项目
     */
    public TmsQuoteItem selectTmsQuoteItemById(Integer id);
    /**
     * 查询报价项目 记录数
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目集合
     */
    public int selectTmsQuoteItemCount(TmsQuoteItem tmsQuoteItem);
    /**
     * 查询报价项目列表
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目集合
     */
    public List<TmsQuoteItem> selectTmsQuoteItemList(TmsQuoteItem tmsQuoteItem);
    /**
     * 新增报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    public int insertTmsQuoteItem(TmsQuoteItem tmsQuoteItem);
    /**
     * 新增报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    public int insertTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems);
    /**
     * 修改报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    public int updateTmsQuoteItem(TmsQuoteItem tmsQuoteItem);
    /**
     * 修改报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    public int updateTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems);
    /**
     * 删除报价项目
     *
     * @param id 报价项目ID
     * @return 结果
     */
    public int deleteTmsQuoteItemById(Integer id);
    /**
     * 批量删除报价项目
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsQuoteItemByIds(Integer[] ids);
}
tms/src/main/java/com/ruoyi/tms/mapper/TransportRouteViMapper.java
New file
@@ -0,0 +1,7 @@
package com.ruoyi.tms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.tms.domain.TransportRouteVi;
public interface TransportRouteViMapper extends BaseMapper<TransportRouteVi> {
}
tms/src/main/java/com/ruoyi/tms/service/ITmsMessageNotifyService.java
New file
@@ -0,0 +1,102 @@
package com.ruoyi.tms.service;
import java.util.List;
import com.ruoyi.tms.domain.TmsMessageNotify;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * 消息通知Service接口
 *
 * @author ruoyi
 * @date 2025-11-26
 */
public interface ITmsMessageNotifyService extends IService<TmsMessageNotify>
{
    /**
     * 查询消息通知
     *
     * @param id 消息通知ID
     * @return 消息通知
     */
    public TmsMessageNotify selectTmsMessageNotifyById(Integer id);
    /**
     * 查询消息通知 记录数
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知集合
     */
    public int selectTmsMessageNotifyCount(TmsMessageNotify tmsMessageNotify);
    /**
     * 查询消息通知列表
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知集合
     */
    public List<TmsMessageNotify> selectTmsMessageNotifyList(TmsMessageNotify tmsMessageNotify);
    /**
     * 查询消息通知列表 异步 导出
     *
     * @param tmsMessageNotify 消息通知
     * @param exportKey 导出功能的唯一标识
     * @return 消息通知集合
     */
    public void export(TmsMessageNotify tmsMessageNotify, String exportKey) ;
    /**
     * 新增消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    public int insertTmsMessageNotify(TmsMessageNotify tmsMessageNotify);
    /**
     * 新增消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    public int insertTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys);
    /**
     * 修改消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    public int updateTmsMessageNotify(TmsMessageNotify tmsMessageNotify);
    /**
     * 修改消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    public int updateTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys);
    /**
     * 批量删除消息通知
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsMessageNotifyByIds(String ids);
    /**
     * 批量删除消息通知
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsMessageNotifyByIds(Integer[] ids);
    /**
     * 删除消息通知信息
     *
     * @param id 消息通知ID
     * @return 结果
     */
    public int deleteTmsMessageNotifyById(Integer id);
}
tms/src/main/java/com/ruoyi/tms/service/ITmsQuoteItemService.java
New file
@@ -0,0 +1,102 @@
package com.ruoyi.tms.service;
import java.util.List;
import com.ruoyi.tms.domain.TmsQuoteItem;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * 报价项目Service接口
 *
 * @author ruoyi
 * @date 2025-11-26
 */
public interface ITmsQuoteItemService extends IService<TmsQuoteItem>
{
    /**
     * 查询报价项目
     *
     * @param id 报价项目ID
     * @return 报价项目
     */
    public TmsQuoteItem selectTmsQuoteItemById(Integer id);
    /**
     * 查询报价项目 记录数
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目集合
     */
    public int selectTmsQuoteItemCount(TmsQuoteItem tmsQuoteItem);
    /**
     * 查询报价项目列表
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目集合
     */
    public List<TmsQuoteItem> selectTmsQuoteItemList(TmsQuoteItem tmsQuoteItem);
    /**
     * 查询报价项目列表 异步 导出
     *
     * @param tmsQuoteItem 报价项目
     * @param exportKey 导出功能的唯一标识
     * @return 报价项目集合
     */
    public void export(TmsQuoteItem tmsQuoteItem, String exportKey) ;
    /**
     * 新增报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    public int insertTmsQuoteItem(TmsQuoteItem tmsQuoteItem);
    /**
     * 新增报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    public int insertTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems);
    /**
     * 修改报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    public int updateTmsQuoteItem(TmsQuoteItem tmsQuoteItem);
    /**
     * 修改报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    public int updateTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems);
    /**
     * 批量删除报价项目
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsQuoteItemByIds(String ids);
    /**
     * 批量删除报价项目
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    public int deleteTmsQuoteItemByIds(Integer[] ids);
    /**
     * 删除报价项目信息
     *
     * @param id 报价项目ID
     * @return 结果
     */
    public int deleteTmsQuoteItemById(Integer id);
}
tms/src/main/java/com/ruoyi/tms/service/impl/TmsFinanceDetailServiceImpl.java
@@ -13,10 +13,12 @@
import javax.annotation.Resource;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.ISystemDataNoService;
import com.ruoyi.tms.domain.TmsDispatchOrder;
import com.ruoyi.tms.domain.TmsFinance;
import com.ruoyi.tms.domain.vo.FinanceDetailItem;
import com.ruoyi.tms.mapper.TmsDispatchOrderMapper;
import com.ruoyi.tms.mapper.TmsFinanceMapper;
import org.springframework.beans.factory.annotation.Autowired;
@@ -158,6 +160,28 @@
                .ne(TmsFinance::getStatus, 2)
                .last("limit 1")
        );
        if (StringUtils.isNotEmpty(tmsFinanceDetail.getFeeType())){
            addItem(tmsFinance, tmsFinanceDetail);
        }
        // 1、查询当前调度单对应的费用
        List<FinanceDetailItem> items = tmsFinanceDetail.getItems();
        if (items != null && !items.isEmpty()){
            items.forEach(item->{
                TmsFinanceDetail tmsFinanceDetailNew = BeanUtil.copyProperties(tmsFinanceDetail, TmsFinanceDetail.class);
                BeanUtil.copyProperties(item, tmsFinanceDetailNew);
                tmsFinanceDetailNew.setActualFeeAmount(item.getPrice().multiply(BigDecimal.valueOf(item.getCount())));
                addItem(tmsFinance, tmsFinanceDetailNew);
            });
        }
        return 1;
    }
    public void addItem (TmsFinance tmsFinance, TmsFinanceDetail tmsFinanceDetail){
        if (tmsFinance == null){
            TmsDispatchOrder tmsDispatchOrder = tmsDispatchOrderMapper.selectTmsDispatchOrderById(tmsFinanceDetail.getDispatchOrderId());
@@ -189,7 +213,7 @@
        tmsFinanceDetail.setCreateId(SecurityUtils.getUserId());
        tmsFinanceDetail.setFeeCreateTime(DateUtils.getNowDate());
        tmsFinanceDetail.setCreateTime(DateUtils.getNowDate());
        return tmsFinanceDetailMapper.insertTmsFinanceDetail(tmsFinanceDetail);
        tmsFinanceDetailMapper.insertTmsFinanceDetail(tmsFinanceDetail);
    }
    /**
tms/src/main/java/com/ruoyi/tms/service/impl/TmsFinanceServiceImpl.java
@@ -153,7 +153,7 @@
            TmsFinanceDetail tmsFinanceDetail = new TmsFinanceDetail();
            tmsFinanceDetail.setFinanceId(item.getId());
            tmsFinanceDetail.setFinanceType(item.getType());
            tmsFinanceDetail.setFeeType(99);
            tmsFinanceDetail.setFeeType("99");
            tmsFinanceDetail.setInitialFeeAmount(tmsQuoteDetail.getFreightPrice());
            tmsFinanceDetail.setActualFeeAmount(tmsQuoteDetail.getFreightPrice());
            tmsFinanceDetail.setDispatchOrderId(item.getDispatchId());
tms/src/main/java/com/ruoyi/tms/service/impl/TmsMessageNotifyServiceImpl.java
New file
@@ -0,0 +1,182 @@
package com.ruoyi.tms.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import javax.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.core.service.BaseService;
import com.ruoyi.tms.mapper.TmsMessageNotifyMapper;
import com.ruoyi.tms.domain.TmsMessageNotify;
import com.ruoyi.tms.service.ITmsMessageNotifyService;
import com.ruoyi.common.core.text.Convert;
/**
 * 消息通知Service业务层处理
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class TmsMessageNotifyServiceImpl  extends BaseService<TmsMessageNotifyMapper, TmsMessageNotify> implements ITmsMessageNotifyService
{
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private TmsMessageNotifyMapper tmsMessageNotifyMapper;
    /**
     * 查询消息通知
     *
     * @param id 消息通知ID
     * @return 消息通知
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public TmsMessageNotify selectTmsMessageNotifyById(Integer id)
    {
        return tmsMessageNotifyMapper.selectTmsMessageNotifyById(id);
    }
    /**
     * 查询消息通知 记录数
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知集合
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public int selectTmsMessageNotifyCount(TmsMessageNotify tmsMessageNotify)
    {
        return tmsMessageNotifyMapper.selectTmsMessageNotifyCount(tmsMessageNotify);
    }
    /**
     * 查询消息通知列表
     *
     * @param tmsMessageNotify 消息通知
     * @return 消息通知
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public List<TmsMessageNotify> selectTmsMessageNotifyList(TmsMessageNotify tmsMessageNotify)
    {
        return tmsMessageNotifyMapper.selectTmsMessageNotifyList(tmsMessageNotify);
    }
    /**
     * 查询消息通知列表 异步 导出
     *
     * @param tmsMessageNotify 消息通知
     * @param exportKey 导出功能的唯一标识
     * @return 消息通知集合
     */
    @DataSource(DataSourceType.SLAVE)
    @Async
    @Override
    public void export(TmsMessageNotify tmsMessageNotify,String exportKey) {
        super.export(TmsMessageNotify.class,exportKey,"tmsMessageNotifyData",(pageNum)->{
            PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE);
            return selectTmsMessageNotifyList(tmsMessageNotify);
        });
    }
    /**
     * 新增消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    @Override
    public int insertTmsMessageNotify(TmsMessageNotify tmsMessageNotify)
    {
        tmsMessageNotify.setCreateTime(DateUtils.getNowDate());
        return tmsMessageNotifyMapper.insertTmsMessageNotify(tmsMessageNotify);
    }
    /**
     * 新增消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    @Override
    public int insertTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys)
    {
        int rows = tmsMessageNotifyMapper.insertTmsMessageNotifyBatch(tmsMessageNotifys);
        return rows;
    }
    /**
     * 修改消息通知
     *
     * @param tmsMessageNotify 消息通知
     * @return 结果
     */
    @Override
    public int updateTmsMessageNotify(TmsMessageNotify tmsMessageNotify)
    {
        tmsMessageNotify.setUpdateTime(DateUtils.getNowDate());
        return tmsMessageNotifyMapper.updateTmsMessageNotify(tmsMessageNotify);
    }
    /**
     * 修改消息通知[批量]
     *
     * @param tmsMessageNotifys 消息通知
     * @return 结果
     */
    @Override
    public int updateTmsMessageNotifyBatch(List<TmsMessageNotify> tmsMessageNotifys){
        return tmsMessageNotifyMapper.updateTmsMessageNotifyBatch(tmsMessageNotifys);
    }
    /**
     * 删除消息通知对象
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    @Override
    public int deleteTmsMessageNotifyByIds(String ids)
    {
        return deleteTmsMessageNotifyByIds(Convert.toIntArray(ids));
    }
    /**
     * 删除消息通知对象
     *
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    @Override
    public int deleteTmsMessageNotifyByIds(Integer[] ids)
    {
        return tmsMessageNotifyMapper.deleteTmsMessageNotifyByIds(ids);
    }
    /**
     * 删除消息通知信息
     *
     * @param id 消息通知ID
     * @return 结果
     */
    @Override
    public int deleteTmsMessageNotifyById(Integer id)
    {
        return tmsMessageNotifyMapper.deleteTmsMessageNotifyById(id);
    }
}
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuoteDetailServiceImpl.java
@@ -2,6 +2,7 @@
import java.util.List;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.enums.SystemDataNoEnum;
import com.ruoyi.common.utils.DateUtils;
@@ -10,6 +11,7 @@
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISystemDataNoService;
import com.ruoyi.tms.domain.TmsQuotePlan;
import com.ruoyi.tms.domain.vo.QuoteDetailItem;
import com.ruoyi.tms.mapper.TmsQuotePlanMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -111,16 +113,46 @@
     * @return 结果
     */
    @Override
    public int insertTmsQuoteDetail(TmsQuoteDetail tmsQuoteDetail)
    {
    public int insertTmsQuoteDetail(TmsQuoteDetail tmsQuoteDetail) {
        Integer quotePlanId = tmsQuoteDetail.getQuotePlanId();
        if (quotePlanId == null){
        if (quotePlanId == null) {
            throw new RuntimeException("请先选择报价方案不能为空");
        }
        TmsQuotePlan tmsQuotePlan = tmsQuotePlanMapper.selectTmsQuotePlanById(quotePlanId);
        if (tmsQuotePlan == null){
        if (tmsQuotePlan == null) {
            throw new RuntimeException("报价方案不存在");
        }
        // 车型报价类型
        if (tmsQuotePlan.getPlanType() == 0) {
            List<QuoteDetailItem> quoteItems = tmsQuoteDetail.getQuoteItems();
            if (quoteItems == null || quoteItems.isEmpty()) {
                throw new RuntimeException("请填写车型报价");
            }
            int count = 0;
            for (QuoteDetailItem item : quoteItems) {
                // 拷贝主对象固定属性
                TmsQuoteDetail newDetail = BeanUtil.copyProperties(tmsQuoteDetail, TmsQuoteDetail.class);
                // 拷贝 item 属性
                BeanUtil.copyProperties(item ,newDetail);
                // 插入
                addDetail(quotePlanId, tmsQuotePlan, newDetail);
                count++;
            }
            return count;
        }
        // 普通类型
        return addDetail(quotePlanId, tmsQuotePlan, tmsQuoteDetail);
    }
    public int addDetail(Integer quotePlanId, TmsQuotePlan tmsQuotePlan, TmsQuoteDetail tmsQuoteDetail){
        // 1、同一报价清单,不能添加相同【路线-车型】数据
        Long l = tmsQuoteDetailMapper.selectCount(new LambdaQueryWrapper<TmsQuoteDetail>()
                .eq(TmsQuoteDetail::getQuotePlanId, quotePlanId)
@@ -155,8 +187,11 @@
        tmsQuoteDetail.setPlanType(tmsQuotePlan.getPlanType());
        tmsQuoteDetail.setCustomerId(tmsQuotePlan.getCustomerId());
        return tmsQuoteDetailMapper.insertTmsQuoteDetail(tmsQuoteDetail);
    }
    /**
     * 新增报价明细[批量]
     *
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuoteItemServiceImpl.java
New file
@@ -0,0 +1,182 @@
package com.ruoyi.tms.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import javax.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.core.service.BaseService;
import com.ruoyi.tms.mapper.TmsQuoteItemMapper;
import com.ruoyi.tms.domain.TmsQuoteItem;
import com.ruoyi.tms.service.ITmsQuoteItemService;
import com.ruoyi.common.core.text.Convert;
/**
 * 报价项目Service业务层处理
 *
 * @author ruoyi
 * @date 2025-11-26
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class TmsQuoteItemServiceImpl  extends BaseService<TmsQuoteItemMapper, TmsQuoteItem> implements ITmsQuoteItemService
{
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private TmsQuoteItemMapper tmsQuoteItemMapper;
    /**
     * 查询报价项目
     *
     * @param id 报价项目ID
     * @return 报价项目
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public TmsQuoteItem selectTmsQuoteItemById(Integer id)
    {
        return tmsQuoteItemMapper.selectTmsQuoteItemById(id);
    }
    /**
     * 查询报价项目 记录数
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目集合
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public int selectTmsQuoteItemCount(TmsQuoteItem tmsQuoteItem)
    {
        return tmsQuoteItemMapper.selectTmsQuoteItemCount(tmsQuoteItem);
    }
    /**
     * 查询报价项目列表
     *
     * @param tmsQuoteItem 报价项目
     * @return 报价项目
     */
    @DataSource(DataSourceType.SLAVE)
    @Override
    public List<TmsQuoteItem> selectTmsQuoteItemList(TmsQuoteItem tmsQuoteItem)
    {
        return tmsQuoteItemMapper.selectTmsQuoteItemList(tmsQuoteItem);
    }
    /**
     * 查询报价项目列表 异步 导出
     *
     * @param tmsQuoteItem 报价项目
     * @param exportKey 导出功能的唯一标识
     * @return 报价项目集合
     */
    @DataSource(DataSourceType.SLAVE)
    @Async
    @Override
    public void export(TmsQuoteItem tmsQuoteItem,String exportKey) {
        super.export(TmsQuoteItem.class,exportKey,"tmsQuoteItemData",(pageNum)->{
            PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE);
            return selectTmsQuoteItemList(tmsQuoteItem);
        });
    }
    /**
     * 新增报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    @Override
    public int insertTmsQuoteItem(TmsQuoteItem tmsQuoteItem)
    {
        tmsQuoteItem.setCreateTime(DateUtils.getNowDate());
        return tmsQuoteItemMapper.insertTmsQuoteItem(tmsQuoteItem);
    }
    /**
     * 新增报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    @Override
    public int insertTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems)
    {
        int rows = tmsQuoteItemMapper.insertTmsQuoteItemBatch(tmsQuoteItems);
        return rows;
    }
    /**
     * 修改报价项目
     *
     * @param tmsQuoteItem 报价项目
     * @return 结果
     */
    @Override
    public int updateTmsQuoteItem(TmsQuoteItem tmsQuoteItem)
    {
        tmsQuoteItem.setUpdateTime(DateUtils.getNowDate());
        return tmsQuoteItemMapper.updateTmsQuoteItem(tmsQuoteItem);
    }
    /**
     * 修改报价项目[批量]
     *
     * @param tmsQuoteItems 报价项目
     * @return 结果
     */
    @Override
    public int updateTmsQuoteItemBatch(List<TmsQuoteItem> tmsQuoteItems){
        return tmsQuoteItemMapper.updateTmsQuoteItemBatch(tmsQuoteItems);
    }
    /**
     * 删除报价项目对象
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    @Override
    public int deleteTmsQuoteItemByIds(String ids)
    {
        return deleteTmsQuoteItemByIds(Convert.toIntArray(ids));
    }
    /**
     * 删除报价项目对象
     *
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    @Override
    public int deleteTmsQuoteItemByIds(Integer[] ids)
    {
        return tmsQuoteItemMapper.deleteTmsQuoteItemByIds(ids);
    }
    /**
     * 删除报价项目信息
     *
     * @param id 报价项目ID
     * @return 结果
     */
    @Override
    public int deleteTmsQuoteItemById(Integer id)
    {
        return tmsQuoteItemMapper.deleteTmsQuoteItemById(id);
    }
}
tms/src/main/java/com/ruoyi/tms/service/impl/TmsQuotePlanServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.tms.service.impl;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.enums.SystemDataNoEnum;
import com.ruoyi.common.utils.DateUtils;
@@ -8,6 +9,8 @@
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISystemDataNoService;
import com.ruoyi.tms.domain.TmsQuoteItem;
import com.ruoyi.tms.service.ITmsQuoteItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
@@ -38,6 +41,8 @@
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private TmsQuotePlanMapper tmsQuotePlanMapper;
    @Autowired
    private ITmsQuoteItemService tmsQuoteItemService;
    @Autowired
    ISystemDataNoService systemDataNoService;
@@ -51,7 +56,9 @@
    @Override
    public TmsQuotePlan selectTmsQuotePlanById(Integer id)
    {
        return tmsQuotePlanMapper.selectTmsQuotePlanById(id);
        TmsQuotePlan tmsQuotePlan = tmsQuotePlanMapper.selectTmsQuotePlanById(id);
        tmsQuotePlan.setQuoteItems(tmsQuoteItemService.selectTmsQuoteItemList(new TmsQuoteItem(){{setQuotePlanId(id);}}));
        return tmsQuotePlan;
    }
    /**
@@ -113,7 +120,22 @@
        tmsQuotePlan.setSystemCode(noByKey);
        tmsQuotePlan.setCreateBy(SecurityUtils.getUsername());
        tmsQuotePlan.setCreateTime(DateUtils.getNowDate());
        return tmsQuotePlanMapper.insertTmsQuotePlan(tmsQuotePlan);
        tmsQuotePlanMapper.insertTmsQuotePlan(tmsQuotePlan);
        if (tmsQuotePlan.getPlanType() == 0){
            List<TmsQuoteItem> quoteItems = tmsQuotePlan.getQuoteItems();
            if (quoteItems != null && !quoteItems.isEmpty()){
                quoteItems.forEach(tmsQuoteItem -> {
                    tmsQuoteItem.setQuotePlanId(tmsQuotePlan.getId());
                });
                tmsQuoteItemService.insertTmsQuoteItemBatch(quoteItems);
            }else{
                throw new RuntimeException("请至少选择一个报价方案");
            }
        }
        return 1;
    }
    /**
@@ -140,7 +162,44 @@
    {
        tmsQuotePlan.setUpdateBy(SecurityUtils.getUsername());
        tmsQuotePlan.setUpdateTime(DateUtils.getNowDate());
        return tmsQuotePlanMapper.updateTmsQuotePlan(tmsQuotePlan);
        int i = tmsQuotePlanMapper.updateTmsQuotePlan(tmsQuotePlan);
        if (tmsQuotePlan.getPlanType() == 0){
            List<TmsQuoteItem> quoteItems = tmsQuotePlan.getQuoteItems();
            if (quoteItems != null && !quoteItems.isEmpty()){
                List<TmsQuoteItem> tmsQuoteItems = tmsQuoteItemService.selectTmsQuoteItemList(new TmsQuoteItem() {{
                    setQuotePlanId(tmsQuotePlan.getId());
                }});
                // 1、删除本次提交没有的数据
                List<Integer> collect = quoteItems.stream().map(TmsQuoteItem::getId).collect(Collectors.toList());
                tmsQuoteItems.removeIf(tmsQuoteItem -> collect.contains(tmsQuoteItem.getId()));
                List<Integer> collect1 = tmsQuoteItems.stream().map(TmsQuoteItem::getId).collect(Collectors.toList());
                if (!collect1.isEmpty()){
                    tmsQuoteItemService.getBaseMapper().deleteBatchIds(collect1);
                }
                // 2、更新本次提交有的数据
                List<TmsQuoteItem> collect2 = quoteItems.stream().filter(tmsQuoteItem -> tmsQuoteItem.getId() != null).collect(Collectors.toList());
                if (!collect2.isEmpty()){
                    tmsQuoteItemService.updateTmsQuoteItemBatch(collect2);
                }
                // 3、新增本次提交没有的数据
                List<TmsQuoteItem> collect3 = quoteItems.stream().
                        filter(tmsQuoteItem -> tmsQuoteItem.getId() == null).collect(Collectors.toList());
                if (!collect3.isEmpty()){
                    collect3.forEach(tmsQuoteItem -> tmsQuoteItem.setQuotePlanId(tmsQuotePlan.getId()));
                    tmsQuoteItemService.insertTmsQuoteItemBatch(collect3);
                }
            }else{
                throw new RuntimeException("请至少选择一个报价方案");
            }
        }
        return i;
    }
    /**
tms/src/main/java/com/ruoyi/tms/service/impl/TmsSettlementEntityServiceImpl.java
@@ -2,8 +2,13 @@
import java.util.List;
import com.ruoyi.common.enums.SystemDataNoEnum;
import com.ruoyi.common.utils.DateUtils;
import javax.annotation.Resource;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISystemDataNoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
@@ -34,7 +39,8 @@
    @Resource
    private TmsSettlementEntityMapper tmsSettlementEntityMapper;
    @Autowired
    ISystemDataNoService systemDataNoService;
    /**
     * 查询结算主体
     *
@@ -102,7 +108,9 @@
    @Override
    public int insertTmsSettlementEntity(TmsSettlementEntity tmsSettlementEntity)
    {
        tmsSettlementEntity.setCustomerCode(systemDataNoService.getNoByKey(SystemDataNoEnum.YY));
        tmsSettlementEntity.setCreateTime(DateUtils.getNowDate());
        tmsSettlementEntity.setCreateBy(SecurityUtils.getUsername());
        return tmsSettlementEntityMapper.insertTmsSettlementEntity(tmsSettlementEntity);
    }
tms/src/main/java/com/ruoyi/tms/task/ContractExpireNotifyTask.java
New file
@@ -0,0 +1,105 @@
package com.ruoyi.tms.task;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.tms.domain.TmsContract;
import com.ruoyi.tms.domain.TmsMessageNotify;
import com.ruoyi.tms.service.ITmsContractService;
import com.ruoyi.tms.service.ITmsMessageNotifyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Component("contractExpireNotifyTask")
public class ContractExpireNotifyTask {
    @Autowired
    ITmsContractService tmsContractService;
    @Autowired
    ITmsMessageNotifyService tmsMessageNotifyService;
    /**
     * 检查合同即将到期
     */
    public void checkContractExpire() {
        LocalDate today = LocalDate.now();
        LocalDate targetDate = today.plusDays(90);
        List<TmsContract> tmsContracts = tmsContractService.getBaseMapper().selectList(new LambdaQueryWrapper<TmsContract>()
                        .eq(TmsContract::getContractStatus, 0)
                .between(TmsContract::getContractEndDate, today, targetDate)
        );
        if (tmsContracts.isEmpty()){
            return;
        }
        log.info("即将到期的合同有{}个", tmsContracts.size());
        // 提取合同ID
        List<Integer> ids = tmsContracts.stream()
                .map(TmsContract::getId)
                .collect(Collectors.toList());
        // 批量更新为“临期”
        tmsContractService.getBaseMapper().update(
                new LambdaUpdateWrapper<TmsContract>()
                        .in(TmsContract::getId, ids)
                        .set(TmsContract::getContractStatus, 1)  // 1 = 临期
        );
        List<TmsMessageNotify> messageNotifies = tmsContracts.stream().map(item -> {
            TmsMessageNotify notify = new TmsMessageNotify();
            notify.setTitle("合同到期提醒");
            notify.setContent("【合同编号" + item.getContractCode() + "】即将到期");
            notify.setType(0);
            notify.setStatus(0);
            notify.setReadStatus(0);
            notify.setExtraData("{\"contactStatus\":1,\"contractId\":\"" + item.getId() + "\"}");
            return notify;
        }).collect(Collectors.toList());
        tmsMessageNotifyService.insertTmsMessageNotifyBatch(messageNotifies);
    }
    /**
     * 检查合同已到期
     */
    public void handleExpiredContracts() {
        LocalDate today = LocalDate.now();
        List<TmsContract> tmsContracts = tmsContractService.getBaseMapper().selectList(new LambdaQueryWrapper<TmsContract>()
                .ne(TmsContract::getContractStatus, 2)
                .le(TmsContract::getContractEndDate, today)
        );
        if (tmsContracts.isEmpty()){
            return;
        }
        log.info("已到期的合同有{}个", tmsContracts.size());
        // 提取合同ID
        List<Integer> ids = tmsContracts.stream()
                .map(TmsContract::getId)
                .collect(Collectors.toList());
        // 批量更新为“临期”
        tmsContractService.getBaseMapper().update(
                new LambdaUpdateWrapper<TmsContract>()
                        .in(TmsContract::getId, ids)
                        .set(TmsContract::getContractStatus, 2)  // 1 = 临期
        );
        List<TmsMessageNotify> messageNotifies = tmsContracts.stream().map(item -> {
            TmsMessageNotify notify = new TmsMessageNotify();
            notify.setTitle("合同到期提醒");
            notify.setContent("【合同编号" + item.getContractCode() + "】已到期");
            notify.setType(0);
            notify.setStatus(0);
            notify.setReadStatus(0);
            notify.setExtraData("{\"contactStatus\":2, \"contractId\":\"" + item.getId() + "\"}");
            return notify;
        }).collect(Collectors.toList());
        tmsMessageNotifyService.insertTmsMessageNotifyBatch(messageNotifies);
    }
}
tms/src/main/resources/mapper/tms/TmsConsignorMapper.xml
@@ -48,11 +48,19 @@
        <if test="customerSysCode != null  and customerSysCode != ''"> and thisTab.customer_sys_code = #{customerSysCode}</if>
        <if test="consignorType != null "> and thisTab.consignor_type = #{consignorType}</if>
        <if test="contactName != null  and contactName != ''"> and  thisTab.contact_name like concat('%', #{contactName}, '%')</if>
        <if test="contactPhone != null  and contactPhone != ''"> and thisTab.contact_phone = #{contactPhone}</if>
        <if test="contactEmail != null  and contactEmail != ''"> and thisTab.contact_email = #{contactEmail}</if>
        <if test="countryId != null  and countryId != ''"> and thisTab.country_id = #{countryId}</if>
        <if test="provinceId != null  and provinceId != ''"> and thisTab.province_id = #{provinceId}</if>
        <if test="cityId != null  and cityId != ''"> and thisTab.city_id = #{cityId}</if>
        <if test="districtId != null  and districtId != ''"> and thisTab.district_id = #{districtId}</if>
        <if test="streetId != null  and streetId != ''"> and thisTab.street_id = #{streetId}</if>
        <if test="addressDetail != null  and addressDetail != ''"> and thisTab.address_detail = #{addressDetail}</if>
        <if test="dispatchTransportArea != null  and dispatchTransportArea != ''"> and thisTab.dispatch_transport_area = #{dispatchTransportArea}</if>
        <if test="mapLocation != null  and mapLocation != ''"> and thisTab.map_location = #{mapLocation}</if>
        <if test="status != null "> and thisTab.status = #{status}</if>
        <if test="updateBy != null  and updateBy != ''"> and thisTab.update_by = #{updateBy}</if>
        <if test="updateTime != null "> and thisTab.update_time = #{updateTime}</if>
        <if test="regionLabel != null  and regionLabel != ''"> and thisTab.region_label = #{regionLabel}</if>
        <if test="electronicFenceRange != null "> and thisTab.electronic_fence_range = #{electronicFenceRange}</if>
    </sql>
    <!--查询-->
tms/src/main/resources/mapper/tms/TmsContractMapper.xml
@@ -41,10 +41,12 @@
        <result property="updateBy"    column="update_by"    />
        <result property="updateTime"    column="update_time"    />
        <result property="remark"    column="remark"    />
        <result property="isTempContract"    column="is_temp_contract"    />
    </resultMap>
    <sql id="selectTmsContractVo">
        select thisTab.id, thisTab.system_code, thisTab.contract_code, thisTab.contract_name, thisTab.contract_type, thisTab.sign_date, thisTab.contract_start_date, thisTab.contract_end_date, thisTab.contract_status, thisTab.party_a_name, thisTab.party_a_id, thisTab.party_a_contact, thisTab.party_a_contact_info, thisTab.party_b_name, thisTab.party_b_id, thisTab.party_b_contact, thisTab.party_b_contact_info, thisTab.contract_amount, thisTab.payment_method, thisTab.payment_cycle, thisTab.paid_amount, thisTab.unpaid_amount, thisTab.invoice_status, thisTab.fulfillment_status, thisTab.fulfillment_progress, thisTab.acceptance_status, thisTab.attachment_name, thisTab.attachment_path, thisTab.uploaded_by, thisTab.upload_time, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark from tms_contract AS thisTab
        select thisTab.id, thisTab.system_code, thisTab.contract_code, thisTab.contract_name, thisTab.contract_type, thisTab.sign_date, thisTab.contract_start_date, thisTab.contract_end_date, thisTab.contract_status, thisTab.party_a_name, thisTab.party_a_id, thisTab.party_a_contact, thisTab.party_a_contact_info, thisTab.party_b_name, thisTab.party_b_id, thisTab.party_b_contact, thisTab.party_b_contact_info, thisTab.contract_amount, thisTab.payment_method, thisTab.payment_cycle, thisTab.paid_amount, thisTab.unpaid_amount, thisTab.invoice_status, thisTab.fulfillment_status, thisTab.fulfillment_progress, thisTab.acceptance_status, thisTab.attachment_name, thisTab.attachment_path, thisTab.uploaded_by, thisTab.upload_time, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark,thisTab.is_temp_contract from tms_contract AS thisTab
    </sql>
    <sql id="selectTmsContractVoCount">
        select count(0) from tms_contract as thisTab
@@ -70,6 +72,8 @@
        <if test="fulfillmentStatus != null "> and thisTab.fulfillment_status = #{fulfillmentStatus}</if>
        <if test="acceptanceStatus != null "> and thisTab.acceptance_status = #{acceptanceStatus}</if>
        <if test="status != null "> and thisTab.status = #{status}</if>
        <if test="isTempContract != null "> and thisTab.is_temp_contract = #{isTempContract}</if>
    </sql>
    <!--查询-->
@@ -132,6 +136,8 @@
            <if test="updateBy != null">update_by,</if>
            <if test="updateTime != null">update_time,</if>
            <if test="remark != null">remark,</if>
            <if test="isTempContract != null">is_temp_contract,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="systemCode != null and systemCode != ''">#{systemCode},</if>
@@ -169,17 +175,19 @@
            <if test="updateBy != null">#{updateBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
            <if test="remark != null">#{remark},</if>
            <if test="isTempContract != null">#{isTempContract},</if>
        </trim>
    </insert>
    <insert id="insertTmsContractBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_contract
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            id,system_code,contract_code,contract_name,contract_type,sign_date,contract_start_date,contract_end_date,contract_status,party_a_name,party_a_id,party_a_contact,party_a_contact_info,party_b_name,party_b_id,party_b_contact,party_b_contact_info,contract_amount,payment_method,payment_cycle,paid_amount,unpaid_amount,invoice_status,fulfillment_status,fulfillment_progress,acceptance_status,attachment_name,attachment_path,uploaded_by,upload_time,status,create_by,create_time,update_by,update_time,remark,
            id,system_code,contract_code,contract_name,contract_type,sign_date,contract_start_date,contract_end_date,contract_status,party_a_name,party_a_id,party_a_contact,party_a_contact_info,party_b_name,party_b_id,party_b_contact,party_b_contact_info,contract_amount,payment_method,payment_cycle,paid_amount,unpaid_amount,invoice_status,fulfillment_status,fulfillment_progress,acceptance_status,attachment_name,attachment_path,uploaded_by,upload_time,status,create_by,create_time,update_by,update_time,remark,is_temp_contract,
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
                #{item.id},#{item.systemCode},#{item.contractCode},#{item.contractName},#{item.contractType},#{item.signDate},#{item.contractStartDate},#{item.contractEndDate},#{item.contractStatus},#{item.partyAName},#{item.partyAId},#{item.partyAContact},#{item.partyAContactInfo},#{item.partyBName},#{item.partyBId},#{item.partyBContact},#{item.partyBContactInfo},#{item.contractAmount},#{item.paymentMethod},#{item.paymentCycle},#{item.paidAmount},#{item.unpaidAmount},#{item.invoiceStatus},#{item.fulfillmentStatus},#{item.fulfillmentProgress},#{item.acceptanceStatus},#{item.attachmentName},#{item.attachmentPath},#{item.uploadedBy},#{item.uploadTime},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},
                #{item.id},#{item.systemCode},#{item.contractCode},#{item.contractName},#{item.contractType},#{item.signDate},#{item.contractStartDate},#{item.contractEndDate},#{item.contractStatus},#{item.partyAName},#{item.partyAId},#{item.partyAContact},#{item.partyAContactInfo},#{item.partyBName},#{item.partyBId},#{item.partyBContact},#{item.partyBContactInfo},#{item.contractAmount},#{item.paymentMethod},#{item.paymentCycle},#{item.paidAmount},#{item.unpaidAmount},#{item.invoiceStatus},#{item.fulfillmentStatus},#{item.fulfillmentProgress},#{item.acceptanceStatus},#{item.attachmentName},#{item.attachmentPath},#{item.uploadedBy},#{item.uploadTime},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},#{item.isTempContract},
            </trim>
        </foreach>
    </insert>
@@ -223,6 +231,7 @@
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
            <if test="remark != null">remark = #{remark},</if>
            <if test="isTempContract != null">is_temp_contract = #{isTempContract},</if>
        </trim>
        where id = #{id}
    </update>
@@ -266,6 +275,7 @@
                <if test="item.updateBy != null">update_by = #{item.updateBy},</if>
                <if test="item.updateTime != null">update_time = #{item.updateTime},</if>
                <if test="item.remark != null">remark = #{item.remark},</if>
                <if test="item.isTempContract != null">is_temp_contract = #{item.isTempContract},</if>
            </trim>
            where id = #{item.id}
        </foreach>
tms/src/main/resources/mapper/tms/TmsDispatchOrderMapper.xml
@@ -81,10 +81,16 @@
        <result property="accountsPayableStatus"    column="accounts_payable_status"    />
        <result property="collectionPlanId"    column="collection_plan_id"    />
        <result property="paymentPlanId"    column="payment_plan_id"    />
        <result property="isCustoms"    column="is_customs"    />
        <result property="startRegionCode"    column="start_region_code"    />
        <result property="endRegionCode"    column="end_region_code"    />
        <result property="quoteDetailId"    column="quote_detail_id"    />
        <result property="operationMode"    column="operation_mode"    />
        <result property="quotePlanId"    column="quote_plan_id"    />
    </resultMap>
    <sql id="selectTmsDispatchOrderVo">
        select thisTab.id, thisTab.dispatch_no, thisTab.customer_id, thisTab.customer_name, thisTab.customer_code, thisTab.project_id, thisTab.project_name, thisTab.contract_id, thisTab.contract_name, thisTab.order_type, thisTab.transport_line, thisTab.sign_type, thisTab.vehicle_provider_id, thisTab.vehicle_provider_name, thisTab.loading_service_provider_id, thisTab.loading_service_provider_name, thisTab.customs_service_provider_id, thisTab.customs_service_provider_name, thisTab.is_urgent, thisTab.transport_type, thisTab.load_method, thisTab.main_driver_id, thisTab.main_driver_name, thisTab.assistant_driver_id, thisTab.required_vehicle_types, thisTab.assistant_driver_name, thisTab.vehicle_id, thisTab.container_no, thisTab.license_plate, thisTab.shipper_id, thisTab.earliest_departure, thisTab.shipper_name, thisTab.latest_departure, thisTab.shipper_mobile, thisTab.earliest_arrival, thisTab.shipper_address, thisTab.latest_arrival, thisTab.departure_address, thisTab.shipper_region_code, thisTab.receiver_id, thisTab.destination_address, thisTab.receiver_name, thisTab.actual_vehicle_type, thisTab.receiver_mobile, thisTab.actual_load_start, thisTab.shipper_region_label, thisTab.receiver_address, thisTab.actual_unload_end, thisTab.receiver_region_code, thisTab.actual_departure, thisTab.actual_arrival, thisTab.actual_quantity, thisTab.actual_weight, thisTab.receiver_region_label, thisTab.actual_volume, thisTab.reweigh_weight, thisTab.container_id, thisTab.electronic_lock, thisTab.empty_mileage, thisTab.shelf_id, thisTab.empty_fuel, thisTab.shelf_code, thisTab.loaded_mileage, thisTab.loaded_fuel, thisTab.shift_no, thisTab.line_no, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark, thisTab.accounts_receivable_status, thisTab.accounts_payable_status, thisTab.collection_plan_id, thisTab.payment_plan_id from tms_dispatch_order AS thisTab
        select thisTab.id, thisTab.dispatch_no, thisTab.customer_id, thisTab.customer_name, thisTab.customer_code, thisTab.project_id, thisTab.project_name, thisTab.contract_id, thisTab.contract_name, thisTab.order_type, thisTab.transport_line, thisTab.sign_type, thisTab.vehicle_provider_id, thisTab.vehicle_provider_name, thisTab.loading_service_provider_id, thisTab.loading_service_provider_name, thisTab.customs_service_provider_id, thisTab.customs_service_provider_name, thisTab.is_urgent, thisTab.transport_type, thisTab.load_method, thisTab.main_driver_id, thisTab.main_driver_name, thisTab.assistant_driver_id, thisTab.required_vehicle_types, thisTab.assistant_driver_name, thisTab.vehicle_id, thisTab.container_no, thisTab.license_plate, thisTab.shipper_id, thisTab.earliest_departure, thisTab.shipper_name, thisTab.latest_departure, thisTab.shipper_mobile, thisTab.earliest_arrival, thisTab.shipper_address, thisTab.latest_arrival, thisTab.departure_address, thisTab.shipper_region_code, thisTab.receiver_id, thisTab.destination_address, thisTab.receiver_name, thisTab.actual_vehicle_type, thisTab.receiver_mobile, thisTab.actual_load_start, thisTab.shipper_region_label, thisTab.receiver_address, thisTab.actual_unload_end, thisTab.receiver_region_code, thisTab.actual_departure, thisTab.actual_arrival, thisTab.actual_quantity, thisTab.actual_weight, thisTab.receiver_region_label, thisTab.actual_volume, thisTab.reweigh_weight, thisTab.container_id, thisTab.electronic_lock, thisTab.empty_mileage, thisTab.shelf_id, thisTab.empty_fuel, thisTab.shelf_code, thisTab.loaded_mileage, thisTab.loaded_fuel, thisTab.shift_no, thisTab.line_no, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark, thisTab.accounts_receivable_status, thisTab.accounts_payable_status, thisTab.collection_plan_id, thisTab.payment_plan_id , thisTab.is_customs, thisTab.start_region_code, thisTab.end_region_code, thisTab.quote_detail_id, thisTab.operation_mode, thisTab.quote_plan_id from tms_dispatch_order AS thisTab
    </sql>
    <sql id="selectTmsDispatchOrderVoCount">
        select count(0) from tms_dispatch_order as thisTab
@@ -138,6 +144,12 @@
        <if test="accountsPayableStatus != null "> and thisTab.accounts_payable_status = #{accountsPayableStatus}</if>
        <if test="collectionPlanId != null "> and thisTab.collection_plan_id = #{collectionPlanId}</if>
        <if test="paymentPlanId != null "> and thisTab.payment_plan_id = #{paymentPlanId}</if>
        <if test="isCustoms != null "> and thisTab.is_customs = #{isCustoms}</if>
        <if test="startRegionCode != null "> and thisTab.start_region_code = #{startRegionCode}</if>
        <if test="endRegionCode != null "> and thisTab.end_region_code = #{endRegionCode}</if>
        <if test="quoteDetailId != null "> and thisTab.quote_detail_id = #{quoteDetailId}</if>
        <if test="operationMode != null "> and thisTab.operation_mode = #{operationMode}</if>
        <if test="quotePlanId != null "> and thisTab.quote_plan_id = #{quotePlanId}</if>
    </sql>
    <!--查询-->
@@ -238,6 +250,12 @@
            <if test="accountsPayableStatus != null">accounts_payable_status,</if>
            <if test="collectionPlanId != null">collection_plan_id,</if>
            <if test="paymentPlanId != null">payment_plan_id,</if>
            <if test="isCustoms != null">is_customs,</if>
            <if test="startRegionCode != null">start_region_code,</if>
            <if test="endRegionCode != null">end_region_code,</if>
            <if test="quoteDetailId != null">quote_detail_id,</if>
            <if test="operationMode != null">operation_mode,</if>
            <if test="quotePlanId != null">quote_plan_id,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="dispatchNo != null and dispatchNo != ''">#{dispatchNo},</if>
@@ -315,17 +333,23 @@
            <if test="accountsPayableStatus != null">#{accountsPayableStatus},</if>
            <if test="collectionPlanId != null">#{collectionPlanId},</if>
            <if test="paymentPlanId != null">#{paymentPlanId},</if>
            <if test="isCustoms != null">#{isCustoms},</if>
            <if test="startRegionCode != null">#{startRegionCode},</if>
            <if test="endRegionCode != null">#{endRegionCode},</if>
            <if test="quoteDetailId != null">#{quoteDetailId},</if>
            <if test="operationMode != null">#{operationMode},</if>
            <if test="quotePlanId != null">#{quotePlanId},</if>
        </trim>
    </insert>
    <insert id="insertTmsDispatchOrderBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_dispatch_order
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            id,dispatch_no,customer_id,customer_name,customer_code,project_id,project_name,contract_id,contract_name,order_type,transport_line,sign_type,vehicle_provider_id,vehicle_provider_name,loading_service_provider_id,loading_service_provider_name,customs_service_provider_id,customs_service_provider_name,is_urgent,transport_type,load_method,main_driver_id,main_driver_name,assistant_driver_id,required_vehicle_types,assistant_driver_name,vehicle_id,container_no,license_plate,shipper_id,earliest_departure,shipper_name,latest_departure,shipper_mobile,earliest_arrival,shipper_address,latest_arrival,departure_address,shipper_region_code,receiver_id,destination_address,receiver_name,actual_vehicle_type,receiver_mobile,actual_load_start,shipper_region_label,receiver_address,actual_unload_end,receiver_region_code,actual_departure,actual_arrival,actual_quantity,actual_weight,receiver_region_label,actual_volume,reweigh_weight,container_id,electronic_lock,empty_mileage,shelf_id,empty_fuel,shelf_code,loaded_mileage,loaded_fuel,shift_no,line_no,status,create_by,create_time,update_by,update_time,remark,accounts_receivable_status,accounts_payable_status,collection_plan_id,payment_plan_id,
            id,dispatch_no,customer_id,customer_name,customer_code,project_id,project_name,contract_id,contract_name,order_type,transport_line,sign_type,vehicle_provider_id,vehicle_provider_name,loading_service_provider_id,loading_service_provider_name,customs_service_provider_id,customs_service_provider_name,is_urgent,transport_type,load_method,main_driver_id,main_driver_name,assistant_driver_id,required_vehicle_types,assistant_driver_name,vehicle_id,container_no,license_plate,shipper_id,earliest_departure,shipper_name,latest_departure,shipper_mobile,earliest_arrival,shipper_address,latest_arrival,departure_address,shipper_region_code,receiver_id,destination_address,receiver_name,actual_vehicle_type,receiver_mobile,actual_load_start,shipper_region_label,receiver_address,actual_unload_end,receiver_region_code,actual_departure,actual_arrival,actual_quantity,actual_weight,receiver_region_label,actual_volume,reweigh_weight,container_id,electronic_lock,empty_mileage,shelf_id,empty_fuel,shelf_code,loaded_mileage,loaded_fuel,shift_no,line_no,status,create_by,create_time,update_by,update_time,remark,accounts_receivable_status,accounts_payable_status,collection_plan_id,payment_plan_id,is_customs,start_region_code,end_region_code,quote_detail_id,operation_mode,quote_plan_id,
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
                #{item.id},#{item.dispatchNo},#{item.customerId},#{item.customerName},#{item.customerCode},#{item.projectId},#{item.projectName},#{item.contractId},#{item.contractName},#{item.orderType},#{item.transportLine},#{item.signType},#{item.vehicleProviderId},#{item.vehicleProviderName},#{item.loadingServiceProviderId},#{item.loadingServiceProviderName},#{item.customsServiceProviderId},#{item.customsServiceProviderName},#{item.isUrgent},#{item.transportType},#{item.loadMethod},#{item.mainDriverId},#{item.mainDriverName},#{item.assistantDriverId},#{item.requiredVehicleTypes},#{item.assistantDriverName},#{item.vehicleId},#{item.containerNo},#{item.licensePlate},#{item.shipperId},#{item.earliestDeparture},#{item.shipperName},#{item.latestDeparture},#{item.shipperMobile},#{item.earliestArrival},#{item.shipperAddress},#{item.latestArrival},#{item.departureAddress},#{item.shipperRegionCode},#{item.receiverId},#{item.destinationAddress},#{item.receiverName},#{item.actualVehicleType},#{item.receiverMobile},#{item.actualLoadStart},#{item.shipperRegionLabel},#{item.receiverAddress},#{item.actualUnloadEnd},#{item.receiverRegionCode},#{item.actualDeparture},#{item.actualArrival},#{item.actualQuantity},#{item.actualWeight},#{item.receiverRegionLabel},#{item.actualVolume},#{item.reweighWeight},#{item.containerId},#{item.electronicLock},#{item.emptyMileage},#{item.shelfId},#{item.emptyFuel},#{item.shelfCode},#{item.loadedMileage},#{item.loadedFuel},#{item.shiftNo},#{item.lineNo},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},#{item.accountsReceivableStatus},#{item.accountsPayableStatus},#{item.collectionPlanId},#{item.paymentPlanId},
                #{item.id},#{item.dispatchNo},#{item.customerId},#{item.customerName},#{item.customerCode},#{item.projectId},#{item.projectName},#{item.contractId},#{item.contractName},#{item.orderType},#{item.transportLine},#{item.signType},#{item.vehicleProviderId},#{item.vehicleProviderName},#{item.loadingServiceProviderId},#{item.loadingServiceProviderName},#{item.customsServiceProviderId},#{item.customsServiceProviderName},#{item.isUrgent},#{item.transportType},#{item.loadMethod},#{item.mainDriverId},#{item.mainDriverName},#{item.assistantDriverId},#{item.requiredVehicleTypes},#{item.assistantDriverName},#{item.vehicleId},#{item.containerNo},#{item.licensePlate},#{item.shipperId},#{item.earliestDeparture},#{item.shipperName},#{item.latestDeparture},#{item.shipperMobile},#{item.earliestArrival},#{item.shipperAddress},#{item.latestArrival},#{item.departureAddress},#{item.shipperRegionCode},#{item.receiverId},#{item.destinationAddress},#{item.receiverName},#{item.actualVehicleType},#{item.receiverMobile},#{item.actualLoadStart},#{item.shipperRegionLabel},#{item.receiverAddress},#{item.actualUnloadEnd},#{item.receiverRegionCode},#{item.actualDeparture},#{item.actualArrival},#{item.actualQuantity},#{item.actualWeight},#{item.receiverRegionLabel},#{item.actualVolume},#{item.reweighWeight},#{item.containerId},#{item.electronicLock},#{item.emptyMileage},#{item.shelfId},#{item.emptyFuel},#{item.shelfCode},#{item.loadedMileage},#{item.loadedFuel},#{item.shiftNo},#{item.lineNo},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},#{item.accountsReceivableStatus},#{item.accountsPayableStatus},#{item.collectionPlanId},#{item.paymentPlanId},#{item.isCustoms},#{item.startRegionCode},#{item.endRegionCode},#{item.quoteDetailId},#{item.operationMode},#{item.quotePlanId},
            </trim>
        </foreach>
    </insert>
@@ -409,6 +433,12 @@
            <if test="accountsPayableStatus != null">accounts_payable_status = #{accountsPayableStatus},</if>
            <if test="collectionPlanId != null">collection_plan_id = #{collectionPlanId},</if>
            <if test="paymentPlanId != null">payment_plan_id = #{paymentPlanId},</if>
            <if test="isCustoms != null">is_customs = #{isCustoms},</if>
            <if test="startRegionCode != null">start_region_code = #{startRegionCode},</if>
            <if test="endRegionCode != null">end_region_code = #{endRegionCode},</if>
            <if test="quoteDetailId != null">quote_detail_id = #{quoteDetailId},</if>
            <if test="operationMode != null">operation_mode = #{operationMode},</if>
            <if test="quotePlanId != null">quote_plan_id = #{quotePlanId},</if>
        </trim>
        where id = #{id}
    </update>
@@ -492,6 +522,12 @@
                <if test="item.accountsPayableStatus != null">accounts_payable_status = #{item.accountsPayableStatus},</if>
                <if test="item.collectionPlanId != null">collection_plan_id = #{item.collectionPlanId},</if>
                <if test="item.paymentPlanId != null">payment_plan_id = #{item.paymentPlanId},</if>
                <if test="item.isCustoms != null">is_customs = #{isCustoms},</if>
                <if test="item.startRegionCode != null">start_region_code = #{startRegionCode},</if>
                <if test="item.endRegionCode != null">end_region_code = #{endRegionCode},</if>
                <if test="item.quoteDetailId != null">quote_detail_id = #{quoteDetailId},</if>
                <if test="item.operationMode != null">operation_mode = #{operationMode},</if>
                <if test="item.quotePlanId != null">quote_plan_id = #{quotePlanId},</if>
            </trim>
            where id = #{item.id}
        </foreach>
tms/src/main/resources/mapper/tms/TmsFinanceDetailMapper.xml
@@ -21,10 +21,14 @@
        <result property="updateBy"    column="update_by"    />
        <result property="updateTime"    column="update_time"    />
        <result property="status"    column="status"    />
        <result property="price"    column="price"    />
        <result property="unit"    column="unit"    />
        <result property="currency"    column="currency"    />
        <result property="count"    column="count"    />
    </resultMap>
    <sql id="selectTmsFinanceDetailVo">
        select thisTab.id, thisTab.fee_type, thisTab.finance_id, thisTab.finance_type, thisTab.dispatch_order_id, thisTab.data_source, thisTab.create_id, thisTab.initial_fee_amount, thisTab.actual_fee_amount, thisTab.fee_voucher_url, thisTab.fee_create_time, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time , thisTab.status from tms_finance_detail AS thisTab
        select thisTab.id, thisTab.fee_type, thisTab.finance_id, thisTab.finance_type, thisTab.dispatch_order_id, thisTab.data_source, thisTab.create_id, thisTab.initial_fee_amount, thisTab.actual_fee_amount, thisTab.fee_voucher_url, thisTab.fee_create_time, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time , thisTab.status, thisTab.price, thisTab.unit, thisTab.currency, thisTab.count from tms_finance_detail AS thisTab
    </sql>
    <sql id="selectTmsFinanceDetailVoCount">
        select count(0) from tms_finance_detail as thisTab
@@ -42,6 +46,10 @@
        <if test="feeVoucherUrl != null  and feeVoucherUrl != ''"> and thisTab.fee_voucher_url = #{feeVoucherUrl}</if>
        <if test="feeCreateTime != null "> and thisTab.fee_create_time = #{feeCreateTime}</if>
        <if test="status != null "> and thisTab.status = #{status}</if>
        <if test="price != null "> and thisTab.price = #{price}</if>
        <if test="unit != null "> and thisTab.unit = #{unit}</if>
        <if test="currency != null "> and thisTab.currency = #{currency}</if>
        <if test="count != null "> and thisTab.count = #{count}</if>
    </sql>
    <!--查询-->
@@ -84,6 +92,10 @@
            <if test="updateBy != null and updateBy != ''">update_by,</if>
            <if test="updateTime != null">update_time,</if>
            <if test="status != null">status,</if>
            <if test="price != null">price,</if>
            <if test="unit != null">unit,</if>
            <if test="currency != null">currency,</if>
            <if test="count != null">count,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="feeType != null">#{feeType},</if>
@@ -101,17 +113,21 @@
            <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
            <if test="status != null">#{status},</if>
            <if test="price != null">#{price},</if>
            <if test="unit != null">#{unit},</if>
            <if test="currency != null">#{currency},</if>
            <if test="count != null">#{count},</if>
        </trim>
    </insert>
    <insert id="insertTmsFinanceDetailBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_finance_detail
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            fee_type,finance_id,finance_type,dispatch_order_id,data_source,create_id,initial_fee_amount,actual_fee_amount,fee_voucher_url,fee_create_time,create_by,create_time,status,
            fee_type,finance_id,finance_type,dispatch_order_id,data_source,create_id,initial_fee_amount,actual_fee_amount,fee_voucher_url,fee_create_time,create_by,create_time,status,price,unit,currency,count
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
               #{item.feeType},#{item.financeId},#{item.financeType},#{item.dispatchOrderId},#{item.dataSource},#{item.createId},#{item.initialFeeAmount},#{item.actualFeeAmount},#{item.feeVoucherUrl},#{item.feeCreateTime},#{item.createBy},#{item.createTime},#{item.status},
               #{item.feeType},#{item.financeId},#{item.financeType},#{item.dispatchOrderId},#{item.dataSource},#{item.createId},#{item.initialFeeAmount},#{item.actualFeeAmount},#{item.feeVoucherUrl},#{item.feeCreateTime},#{item.createBy},#{item.createTime},#{item.status},#{item.price},#{item.unit},#{item.currency},#{item.count}
            </trim>
        </foreach>
    </insert>
@@ -135,6 +151,10 @@
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
            <if test="status != null">status = #{status},</if>
            <if test="price != null">price = #{price},</if>
            <if test="unit != null">unit = #{unit},</if>
            <if test="currency != null">currency = #{currency},</if>
            <if test="count != null">count = #{count},</if>
        </trim>
        where id = #{id}
    </update>
@@ -158,6 +178,10 @@
                <if test="item.updateBy != null and item.updateBy != ''">update_by = #{item.updateBy},</if>
                <if test="item.updateTime != null">update_time = #{item.updateTime},</if>
                <if test="item.status != null">status = #{item.status},</if>
                <if test="item.count != null">count = #{item.count},</if>
                <if test="item.currency != null">currency = #{item.currency},</if>
                <if test="item.unit != null">unit = #{item.unit},</if>
                <if test="item.price != null">price = #{item.price},</if>
            </trim>
            where id = #{item.id}
        </foreach>
tms/src/main/resources/mapper/tms/TmsMessageNotifyMapper.xml
New file
@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.tms.mapper.TmsMessageNotifyMapper">
    <resultMap type="com.ruoyi.tms.domain.TmsMessageNotify" id="TmsMessageNotifyResult">
        <result property="id"    column="id"    />
        <result property="title"    column="title"    />
        <result property="content"    column="content"    />
        <result property="type"    column="type"    />
        <result property="targetUid"    column="target_uid"    />
        <result property="status"    column="status"    />
        <result property="readStatus"    column="read_status"    />
        <result property="readTime"    column="read_time"    />
        <result property="extraData"    column="extra_data"    />
        <result property="createUid"    column="create_uid"    />
        <result property="createTime"    column="create_time"    />
        <result property="updateUid"    column="update_uid"    />
        <result property="updateTime"    column="update_time"    />
    </resultMap>
    <sql id="selectTmsMessageNotifyVo">
        select thisTab.id, thisTab.title, thisTab.content, thisTab.type, thisTab.target_uid, thisTab.status, thisTab.read_status, thisTab.read_time, thisTab.extra_data, thisTab.create_uid, thisTab.create_time, thisTab.update_uid, thisTab.update_time from tms_message_notify AS thisTab
    </sql>
    <sql id="selectTmsMessageNotifyVoCount">
        select count(0) from tms_message_notify as thisTab
    </sql>
    <sql id="whereCondition">
        <if test="title != null  and title != ''"> and thisTab.title = #{title}</if>
        <if test="content != null  and content != ''"> and thisTab.content = #{content}</if>
        <if test="type != null "> and thisTab.type = #{type}</if>
        <if test="targetUid != null "> and thisTab.target_uid = #{targetUid}</if>
        <if test="status != null "> and thisTab.status = #{status}</if>
        <if test="readStatus != null "> and thisTab.read_status = #{readStatus}</if>
        <if test="readTime != null "> and thisTab.read_time = #{readTime}</if>
        <if test="extraData != null  and extraData != ''"> and thisTab.extra_data = #{extraData}</if>
        <if test="createUid != null "> and thisTab.create_uid = #{createUid}</if>
        <if test="updateUid != null "> and thisTab.update_uid = #{updateUid}</if>
    </sql>
    <!--查询-->
    <select id="selectTmsMessageNotifyById" parameterType="Integer" resultMap="TmsMessageNotifyResult">
        <include refid="selectTmsMessageNotifyVo"/>
        where id = #{id}
    </select>
    <select id="selectTmsMessageNotifyCount" parameterType="com.ruoyi.tms.domain.TmsMessageNotify" resultType="int">
        <include refid="selectTmsMessageNotifyVoCount"/>
        <where>
            <include refid="whereCondition"/>
        </where>
    </select>
    <select id="selectTmsMessageNotifyList" parameterType="com.ruoyi.tms.domain.TmsMessageNotify" resultMap="TmsMessageNotifyResult">
        <include refid="selectTmsMessageNotifyVo"/>
        <where>
            <include refid="whereCondition"/>
        </where>
        order by thisTab.id desc
    </select>
    <!-- 新增 -->
    <insert id="insertTmsMessageNotify" parameterType="com.ruoyi.tms.domain.TmsMessageNotify"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_message_notify
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="title != null and title != ''">title,</if>
            <if test="content != null and content != ''">content,</if>
            <if test="type != null">type,</if>
            <if test="targetUid != null">target_uid,</if>
            <if test="status != null">status,</if>
            <if test="readStatus != null">read_status,</if>
            <if test="readTime != null">read_time,</if>
            <if test="extraData != null">extra_data,</if>
            <if test="createUid != null">create_uid,</if>
            <if test="createTime != null">create_time,</if>
            <if test="updateUid != null">update_uid,</if>
            <if test="updateTime != null">update_time,</if>
         </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="title != null and title != ''">#{title},</if>
            <if test="content != null and content != ''">#{content},</if>
            <if test="type != null">#{type},</if>
            <if test="targetUid != null">#{targetUid},</if>
            <if test="status != null">#{status},</if>
            <if test="readStatus != null">#{readStatus},</if>
            <if test="readTime != null">#{readTime},</if>
            <if test="extraData != null">#{extraData},</if>
            <if test="createUid != null">#{createUid},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="updateUid != null">#{updateUid},</if>
            <if test="updateTime != null">#{updateTime},</if>
         </trim>
    </insert>
    <insert id="insertTmsMessageNotifyBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_message_notify
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            id,title,content,type,target_uid,status,read_status,read_time,extra_data,
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
                #{item.id},#{item.title},#{item.content},#{item.type},#{item.targetUid},#{item.status},#{item.readStatus},#{item.readTime},#{item.extraData},
            </trim>
        </foreach>
    </insert>
    <!-- 修改 -->
    <update id="updateTmsMessageNotify" parameterType="com.ruoyi.tms.domain.TmsMessageNotify">
        update tms_message_notify
        <trim prefix="SET" suffixOverrides=",">
            <if test="title != null and title != ''">title = #{title},</if>
            <if test="content != null and content != ''">content = #{content},</if>
            <if test="type != null">type = #{type},</if>
            <if test="targetUid != null">target_uid = #{targetUid},</if>
            <if test="status != null">status = #{status},</if>
            <if test="readStatus != null">read_status = #{readStatus},</if>
            <if test="readTime != null">read_time = #{readTime},</if>
            <if test="extraData != null">extra_data = #{extraData},</if>
            <if test="createUid != null">create_uid = #{createUid},</if>
            <if test="createTime != null">create_time = #{createTime},</if>
            <if test="updateUid != null">update_uid = #{updateUid},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
        </trim>
        where id = #{id}
    </update>
    <!-- 修改 -->
    <update id="updateTmsMessageNotifyBatch" parameterType="java.util.List">
        <foreach collection="list" item="item" index="index" separator=";">
            update tms_message_notify
            <trim prefix="SET" suffixOverrides=",">
                <if test="item.title != null and item.title != ''">title = #{item.title},</if>
                <if test="item.content != null and item.content != ''">content = #{item.content},</if>
                <if test="item.type != null">type = #{item.type},</if>
                <if test="item.targetUid != null">target_uid = #{item.targetUid},</if>
                <if test="item.status != null">status = #{item.status},</if>
                <if test="item.readStatus != null">read_status = #{item.readStatus},</if>
                <if test="item.readTime != null">read_time = #{item.readTime},</if>
                <if test="item.extraData != null">extra_data = #{item.extraData},</if>
                <if test="item.createUid != null">create_uid = #{item.createUid},</if>
                <if test="item.createTime != null">create_time = #{item.createTime},</if>
                <if test="item.updateUid != null">update_uid = #{item.updateUid},</if>
                <if test="item.updateTime != null">update_time = #{item.updateTime},</if>
            </trim>
        where id = #{item.id}
        </foreach>
    </update>
    <!--删除-->
    <delete id="deleteTmsMessageNotifyById" parameterType="Integer">
        delete from tms_message_notify where id = #{id}
    </delete>
    <delete id="deleteTmsMessageNotifyByIds" parameterType="Integer">
        delete from tms_message_notify where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
</mapper>
tms/src/main/resources/mapper/tms/TmsQuoteDetailMapper.xml
@@ -30,10 +30,11 @@
        <result property="remark"    column="remark"    />
        <result property="planType"    column="plan_type"    />
        <result property="customerId"    column="customer_id"    />
        <result property="currency"    column="currency"    />
    </resultMap>
    <sql id="selectTmsQuoteDetailVo">
        select thisTab.id, thisTab.system_code, thisTab.quote_plan_id, thisTab.quote_plan_code, thisTab.vehicle_provider_id, thisTab.vehicle_provider_name, thisTab.transport_route, thisTab.start_region_code, thisTab.start_region, thisTab.start_warehouse, thisTab.end_region_code, thisTab.end_region, thisTab.end_warehouse, thisTab.vehicle_type, thisTab.transport_mode, thisTab.route_type, thisTab.freight_price, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark,thisTab.plan_type, thisTab.customer_id from tms_quote_detail AS thisTab
        select thisTab.id, thisTab.system_code, thisTab.quote_plan_id, thisTab.quote_plan_code, thisTab.vehicle_provider_id, thisTab.vehicle_provider_name, thisTab.transport_route, thisTab.start_region_code, thisTab.start_region, thisTab.start_warehouse, thisTab.end_region_code, thisTab.end_region, thisTab.end_warehouse, thisTab.vehicle_type, thisTab.transport_mode, thisTab.route_type, thisTab.freight_price, thisTab.status, thisTab.create_by, thisTab.create_time, thisTab.update_by, thisTab.update_time, thisTab.remark,thisTab.plan_type, thisTab.customer_id , thisTab.currency from tms_quote_detail AS thisTab
    </sql>
    <sql id="selectTmsQuoteDetailVoCount">
        select count(0) from tms_quote_detail as thisTab
@@ -52,13 +53,14 @@
        <if test="endRegionCode != null  and endRegionCode != ''"> and thisTab.end_region_code = #{endRegionCode}</if>
        <if test="endRegion != null  and endRegion != ''"> and thisTab.end_region = #{endRegion}</if>
        <if test="endWarehouse != null "> and thisTab.end_warehouse = #{endWarehouse}</if>
        <if test="vehicleType != null "> and thisTab.vehicle_type = #{vehicleType}</if>
        <if test="vehicleType != null  and vehicleType != ''"> and thisTab.vehicle_type = #{vehicleType}</if>
        <if test="transportMode != null "> and thisTab.transport_mode = #{transportMode}</if>
        <if test="routeType != null "> and thisTab.route_type = #{routeType}</if>
        <if test="freightPrice != null "> and thisTab.freight_price = #{freightPrice}</if>
        <if test="status != null "> and thisTab.status = #{status}</if>
        <if test="status != null and status != ''"> and thisTab.status = #{status}</if>
        <if test="planType != null "> and thisTab.plan_type = #{planType}</if>
        <if test="customerId != null "> and thisTab.customer_id = #{customerId}</if>
        <if test="currency != null and currency != ''"> and thisTab.currency = #{currency}</if>
    </sql>
    <!--查询-->
@@ -123,6 +125,7 @@
            <if test="remark != null">remark,</if>
            <if test="planType != null">plan_type,</if>
            <if test="customerId != null">customer_id,</if>
            <if test="currency != null">currency,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="systemCode != null and systemCode != ''">#{systemCode},</if>
@@ -149,17 +152,18 @@
            <if test="remark != null">#{remark},</if>
            <if test="planType != null">#{planType},</if>
            <if test="customerId != null">#{customerId},</if>
            <if test="currency != null">#{currency},</if>
        </trim>
    </insert>
    <insert id="insertTmsQuoteDetailBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_quote_detail
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            id,system_code,quote_plan_id,quote_plan_code,vehicle_provider_id,vehicle_provider_name,transport_route,start_region_code,start_region,start_warehouse,end_region_code,end_region,end_warehouse,vehicle_type,transport_mode,route_type,freight_price,status,create_by,create_time,update_by,update_time,remark,plan_type,customer_id,
            id,system_code,quote_plan_id,quote_plan_code,vehicle_provider_id,vehicle_provider_name,transport_route,start_region_code,start_region,start_warehouse,end_region_code,end_region,end_warehouse,vehicle_type,transport_mode,route_type,freight_price,status,create_by,create_time,update_by,update_time,remark,plan_type,customer_id,currency,
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
                #{item.id},#{item.systemCode},#{item.quotePlanId},#{item.quotePlanCode},#{item.vehicleProviderId},#{item.vehicleProviderName},#{item.transportRoute},#{item.startRegionCode},#{item.startRegion},#{item.startWarehouse},#{item.endRegionCode},#{item.endRegion},#{item.endWarehouse},#{item.vehicleType},#{item.transportMode},#{item.routeType},#{item.freightPrice},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},#{item.planType},#{item.customerId},
                #{item.id},#{item.systemCode},#{item.quotePlanId},#{item.quotePlanCode},#{item.vehicleProviderId},#{item.vehicleProviderName},#{item.transportRoute},#{item.startRegionCode},#{item.startRegion},#{item.startWarehouse},#{item.endRegionCode},#{item.endRegion},#{item.endWarehouse},#{item.vehicleType},#{item.transportMode},#{item.routeType},#{item.freightPrice},#{item.status},#{item.createBy},#{item.createTime},#{item.updateBy},#{item.updateTime},#{item.remark},#{item.planType},#{item.customerId},#{item.currency},
            </trim>
        </foreach>
    </insert>
@@ -192,6 +196,7 @@
            <if test="remark != null">remark = #{remark},</if>
            <if test="planType != null">plan_type = #{planType},</if>
            <if test="customerId != null">customer_id = #{customerId},</if>
            <if test="currency != null">currency = #{currency},</if>
        </trim>
        where id = #{id}
    </update>
@@ -224,6 +229,7 @@
                <if test="item.remark != null">remark = #{item.remark},</if>
                <if test="item.planType != null">plan_type = #{item.planType},</if>
                <if test="item.customerId != null">customer_id = #{item.customerId},</if>
                <if test="item.currency != null">currency = #{item.currency},</if>
            </trim>
            where id = #{item.id}
        </foreach>
tms/src/main/resources/mapper/tms/TmsQuoteItemMapper.xml
New file
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.tms.mapper.TmsQuoteItemMapper">
    <resultMap type="com.ruoyi.tms.domain.TmsQuoteItem" id="TmsQuoteItemResult">
        <result property="id"    column="id"    />
        <result property="quotePlanId"    column="quote_plan_id"    />
        <result property="freeName"    column="free_name"    />
        <result property="unit"    column="unit"    />
        <result property="price"    column="price"    />
        <result property="currency"    column="currency"    />
        <result property="createTime"    column="create_time"    />
        <result property="updateTime"    column="update_time"    />
    </resultMap>
    <sql id="selectTmsQuoteItemVo">
        select thisTab.id, thisTab.quote_plan_id, thisTab.free_name, thisTab.unit, thisTab.price, thisTab.currency, thisTab.create_time, thisTab.update_time from tms_quote_item AS thisTab
    </sql>
    <sql id="selectTmsQuoteItemVoCount">
        select count(0) from tms_quote_item as thisTab
    </sql>
    <sql id="whereCondition">
        <if test="quotePlanId != null "> and thisTab.quote_plan_id = #{quotePlanId}</if>
        <if test="freeName != null  and freeName != ''"> and  thisTab.free_name like concat('%', #{freeName}, '%')</if>
        <if test="unit != null  and unit != ''"> and thisTab.unit = #{unit}</if>
        <if test="price != null "> and thisTab.price = #{price}</if>
        <if test="currency != null  and currency != ''"> and thisTab.currency = #{currency}</if>
    </sql>
    <!--查询-->
    <select id="selectTmsQuoteItemById" parameterType="Integer" resultMap="TmsQuoteItemResult">
        <include refid="selectTmsQuoteItemVo"/>
        where id = #{id}
    </select>
    <select id="selectTmsQuoteItemCount" parameterType="com.ruoyi.tms.domain.TmsQuoteItem" resultType="int">
        <include refid="selectTmsQuoteItemVoCount"/>
        <where>
            <include refid="whereCondition"/>
        </where>
    </select>
    <select id="selectTmsQuoteItemList" parameterType="com.ruoyi.tms.domain.TmsQuoteItem" resultMap="TmsQuoteItemResult">
        <include refid="selectTmsQuoteItemVo"/>
        <where>
            <include refid="whereCondition"/>
        </where>
        order by thisTab.id desc
    </select>
    <!-- 新增 -->
    <insert id="insertTmsQuoteItem" parameterType="com.ruoyi.tms.domain.TmsQuoteItem"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_quote_item
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="quotePlanId != null">quote_plan_id,</if>
            <if test="freeName != null and freeName != ''">free_name,</if>
            <if test="unit != null">unit,</if>
            <if test="price != null">price,</if>
            <if test="currency != null">currency,</if>
            <if test="createTime != null">create_time,</if>
            <if test="updateTime != null">update_time,</if>
         </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="quotePlanId != null">#{quotePlanId},</if>
            <if test="freeName != null and freeName != ''">#{freeName},</if>
            <if test="unit != null">#{unit},</if>
            <if test="price != null">#{price},</if>
            <if test="currency != null">#{currency},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="updateTime != null">#{updateTime},</if>
         </trim>
    </insert>
    <insert id="insertTmsQuoteItemBatch" parameterType="java.util.List"  useGeneratedKeys="true" keyProperty="id">
        insert into tms_quote_item
        <trim prefix="(" suffix=") values" suffixOverrides=",">
            id,quote_plan_id,free_name,unit,price,currency,
        </trim>
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=") " suffixOverrides=",">
                #{item.id},#{item.quotePlanId},#{item.freeName},#{item.unit},#{item.price},#{item.currency},
            </trim>
        </foreach>
    </insert>
    <!-- 修改 -->
    <update id="updateTmsQuoteItem" parameterType="com.ruoyi.tms.domain.TmsQuoteItem">
        update tms_quote_item
        <trim prefix="SET" suffixOverrides=",">
            <if test="quotePlanId != null">quote_plan_id = #{quotePlanId},</if>
            <if test="freeName != null and freeName != ''">free_name = #{freeName},</if>
            <if test="unit != null">unit = #{unit},</if>
            <if test="price != null">price = #{price},</if>
            <if test="currency != null">currency = #{currency},</if>
            <if test="createTime != null">create_time = #{createTime},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
        </trim>
        where id = #{id}
    </update>
    <!-- 修改 -->
    <update id="updateTmsQuoteItemBatch" parameterType="java.util.List">
        <foreach collection="list" item="item" index="index" separator=";">
            update tms_quote_item
            <trim prefix="SET" suffixOverrides=",">
                <if test="item.quotePlanId != null">quote_plan_id = #{item.quotePlanId},</if>
                <if test="item.freeName != null and item.freeName != ''">free_name = #{item.freeName},</if>
                <if test="item.unit != null">unit = #{item.unit},</if>
                <if test="item.price != null">price = #{item.price},</if>
                <if test="item.currency != null">currency = #{item.currency},</if>
                <if test="item.createTime != null">create_time = #{item.createTime},</if>
                <if test="item.updateTime != null">update_time = #{item.updateTime},</if>
            </trim>
        where id = #{item.id}
        </foreach>
    </update>
    <!--删除-->
    <delete id="deleteTmsQuoteItemById" parameterType="Integer">
        delete from tms_quote_item where id = #{id}
    </delete>
    <delete id="deleteTmsQuoteItemByIds" parameterType="Integer">
        delete from tms_quote_item where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
</mapper>
tms/src/main/resources/mapper/tms/TransportRouteViMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.tms.mapper.TransportRouteViMapper">
</mapper>
ui/admin-ui3/src/api/common.ts
New file
@@ -0,0 +1,17 @@
import request, {requestType} from "@/utils/request";
export const getNoCountApi: requestType = () => {
    return request({
        url: '/tms/tmsMessageNotify/noCount',
        method: 'get'
    })
}
export const getReadApi: requestType = (id) => {
    return request({
        url: '/tms/tmsMessageNotify/read/'+id,
        method: 'get'
    })
}
ui/admin-ui3/src/api/tms/tmsContract.ts
@@ -10,7 +10,7 @@
    signDate?: string,
    contractStartDate?: string,
    contractEndDate?: string,
    contractStatus?: number,
    contractStatus?: string,
    partyAId?: number,
    partyAName?: string,
    partyAContact?: string,
ui/admin-ui3/src/api/tms/tmsMessageNotify.ts
New file
@@ -0,0 +1,67 @@
import request,{download,requestType} from "@/utils/request";
import {BaseEntityInterface} from "@/utils/globalInterface";
export interface TmsMessageNotifyI extends BaseEntityInterface{
            id ?:  number   ,            title ?:  string   ,            content ?:  string   ,            type ?:  number   ,            targetUid ?:  number   ,            status ?:  number   ,            readStatus ?:  number   ,            readTime ?:  string   ,            extraData ?:  string   ,            createUid ?:  number   ,            createTime ?:  string   ,            updateUid ?:  number   ,            updateTime ?:  string       }
/**
 * 查询消息通知列表
 */
export const listTmsMessageNotify:requestType = (query) => {
    return request({
        url: '/tms/tmsMessageNotify/list',
        method:'get',
        params:query
    })
}
/**
 * 查询消息通知详细
 */
export const getTmsMessageNotify:requestType = (id) => {
    return request({
        url: '/tms/tmsMessageNotify/' + id,
        method:'get'
    })
}
/**
 * 新增消息通知
 */
export const addTmsMessageNotify:requestType = (data) => {
    return request({
        url: '/tms/tmsMessageNotify',
        method: 'post',
        data
    })
}
/**
 * 修改消息通知
 */
export const updateTmsMessageNotify:requestType = (data) => {
    return request({
        url: '/tms/tmsMessageNotify',
        method: 'put',
        data
    })
}
/**
 * 删除消息通知
 */
export const delTmsMessageNotify:requestType = (id) => {
    return request({
        url: '/tms/tmsMessageNotify/' + id,
        method: 'delete'
    })
}
/**
 * 导出消息通知
 */
export const exportTmsMessageNotify:requestType = (query) => {
    return new Promise<any>(()=>{
        download('/tms/tmsMessageNotify/export',query);
    })
}
ui/admin-ui3/src/api/tms/tmsQuoteDetail.ts
@@ -1,33 +1,59 @@
import request,{download,requestType} from "@/utils/request";
import request, {download, requestType} from "@/utils/request";
import {BaseEntityInterface} from "@/utils/globalInterface";
export interface TmsQuoteDetailI extends BaseEntityInterface{
            id ?:  number   ,            systemCode ?:  string   ,            quotePlanId ?:  number   ,            quotePlanCode ?:  string   ,            vehicleProviderId ?:  number   ,            vehicleProviderName ?:  string   ,            transportRoute ?:  string   ,            startRegionCode ?:  string   ,            startRegion ?:  string   ,            startWarehouse ?:  number   ,            endRegionCode ?:  string   ,            endRegion ?:  string   ,            endWarehouse ?:  number   ,            vehicleType ?:  number   ,            transportMode ?:  number   ,            routeType ?:  number   ,            freightPrice ?:  string   ,            status ?:  number   ,            createBy ?:  string   ,            createTime ?:  string   ,            updateBy ?:  string   ,            updateTime ?:  string   ,            remark ?:  string       }
export interface TmsQuoteDetailI extends BaseEntityInterface {
    id?: number,
    systemCode?: string,
    quotePlanId?: number,
    quotePlanCode?: string,
    vehicleProviderId?: number,
    vehicleProviderName?: string,
    transportRoute?: string,
    startRegionCode?: string,
    startRegion?: string,
    startWarehouse?: number,
    endRegionCode?: string,
    endRegion?: string,
    endWarehouse?: number,
    vehicleType?: number,
    transportMode?: number,
    routeType?: number,
    freightPrice?: string,
    status?: number,
    createBy?: string,
    createTime?: string,
    updateBy?: string,
    updateTime?: string,
    remark?: string,
    items?: any
}
/**
 * 查询报价明细列表
 */
export const listTmsQuoteDetail:requestType = (query) => {
export const listTmsQuoteDetail: requestType = (query) => {
    return request({
        url: '/tms/tmsQuoteDetail/list',
        method:'get',
        params:query
        method: 'get',
        params: query
    })
}
/**
 * 查询报价明细详细
 */
export const getTmsQuoteDetail:requestType = (id) => {
export const getTmsQuoteDetail: requestType = (id) => {
    return request({
        url: '/tms/tmsQuoteDetail/' + id,
        method:'get'
        method: 'get'
    })
}
/**
 * 新增报价明细
 */
export const addTmsQuoteDetail:requestType = (data) => {
export const addTmsQuoteDetail: requestType = (data) => {
    return request({
        url: '/tms/tmsQuoteDetail',
        method: 'post',
@@ -38,7 +64,7 @@
/**
 * 修改报价明细
 */
export const updateTmsQuoteDetail:requestType = (data) => {
export const updateTmsQuoteDetail: requestType = (data) => {
    return request({
        url: '/tms/tmsQuoteDetail',
        method: 'put',
@@ -49,7 +75,7 @@
/**
 * 删除报价明细
 */
export const delTmsQuoteDetail:requestType = (id) => {
export const delTmsQuoteDetail: requestType = (id) => {
    return request({
        url: '/tms/tmsQuoteDetail/' + id,
        method: 'delete'
@@ -60,8 +86,8 @@
/**
 * 导出报价明细
 */
export const exportTmsQuoteDetail:requestType = (query) => {
    return new Promise<any>(()=>{
        download('/tms/tmsQuoteDetail/export',query);
export const exportTmsQuoteDetail: requestType = (query) => {
    return new Promise<any>(() => {
        download('/tms/tmsQuoteDetail/export', query);
    })
}
ui/admin-ui3/src/api/tms/tmsQuotePlan.ts
@@ -1,6 +1,18 @@
import request, {download, requestType} from "@/utils/request";
import {BaseEntityInterface} from "@/utils/globalInterface";
export interface TmsQuoteItemI extends BaseEntityInterface {
    id?: number,
    quotePlanId?: number,
    freeName?: string,
    unit?: string,
    price?: string,
    currency?: string,
    createTime?: string,
    updateTime?: string
}
export interface TmsQuotePlanI extends BaseEntityInterface {
    id?: number,
    systemCode?: string,
@@ -20,6 +32,7 @@
    updateTime?: string,
    remark?: string,
    planType?: string,
    items?: TmsQuoteItemI[],
}
ui/admin-ui3/src/api/tms/tmsQuotePlanItem.ts
New file
@@ -0,0 +1,76 @@
import request, {download, requestType} from "@/utils/request";
import {BaseEntityInterface} from "@/utils/globalInterface";
export interface TmsQuoteItemI extends BaseEntityInterface {
    id?: number,
    quotePlanId?: number,
    freeName?: string,
    unit?: string,
    price?: string,
    currency?: string,
    createTime?: string,
    updateTime?: string
}
/**
 * 查询报价方案管理列表
 */
export const listTmsQuoteItem: requestType = (query) => {
    return request({
        url: '/tms/tmsQuoteItem/list',
        method: 'get',
        params: query
    })
}
/**
 * 查询报价方案管理详细
 */
export const getTmsQuoteItem: requestType = (id) => {
    return request({
        url: '/tms/tmsQuoteItem/' + id,
        method: 'get'
    })
}
/**
 * 新增报价方案管理
 */
export const addTmsQuoteItem: requestType = (data) => {
    return request({
        url: '/tms/tmsQuoteItem',
        method: 'post',
        data
    })
}
/**
 * 修改报价方案管理
 */
export const updateTmsQuoteItem: requestType = (data) => {
    return request({
        url: '/tms/tmsQuoteItem',
        method: 'put',
        data
    })
}
/**
 * 删除报价方案管理
 */
export const delTmsQuoteItem: requestType = (id) => {
    return request({
        url: '/tms/tmsQuoteItem/' + id,
        method: 'delete'
    })
}
/**
 * 导出报价方案管理
 */
export const exportTmsQuoteItem: requestType = (query) => {
    return new Promise<any>(() => {
        download('/tms/tmsQuoteItem/export', query);
    })
}
ui/admin-ui3/src/api/tms/tmsTransportRouteVi.ts
New file
@@ -0,0 +1,22 @@
import request, {download, requestType} from "@/utils/request";
/**
 * 查询合同管理列表
 */
export const listTransportRouteVi: requestType = (query) => {
    return request({
        url: '/tms/transportRouteVi/list',
        method: 'get',
        params: query
    })
}
/**
 * 查询合同管理详细
 */
export const getTransportRouteVi: requestType = (id) => {
    return request({
        url: '/tms/transportRouteVi/' + id,
        method: 'get'
    })
}
ui/admin-ui3/src/assets/tongzhi.png
ui/admin-ui3/src/hooks/usePagePlus.ts
@@ -101,7 +101,7 @@
    /** 新增操作 */
    const rowSave = (row:any, done:any, loading:any) => {
        if (opts.rowSaveBegin){
            opts.rowSaveBegin!(row);
            opts.rowSaveBegin!(row,loading);
        }
        opts.addApi!(row).then(()=>{
            ElMessage({
@@ -118,7 +118,7 @@
    /** 修改操作 */
    const rowUpdate = (row:any, index:any, done:any, loading:any) => {
        if (opts.rowUpdateBegin){
            opts.rowUpdateBegin!(row);
            opts.rowUpdateBegin!(row,loading);
        }
        opts.updateApi!(row).then((response:any) => {
            ElMessage({
ui/admin-ui3/src/layout/components/Navbar.vue
@@ -5,6 +5,36 @@
    <top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
    <div class="right-menu">
      <el-dropdown>
        <el-badge :value="noCount" class="item" :hidden="noCount == 0" :offset="[-1, 15]">
          <el-image style="width: 30px;height: 30px;" :src="tongzhi"></el-image>
          <!--        <el-button>comments</el-button>-->
        </el-badge>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item disabled v-if="noCount == 0" >
              <div style="margin: 0 auto;width: 325px;">
                <el-empty :image-size="30" description="暂无通知" />
              </div>
            </el-dropdown-item>
            <el-dropdown-item v-for="(item,index) in noList"
      @click="itemClick(item)"
                              :key="index" :divided="index > 0">
              <div style="width: 200px;">
                <div style="line-height: 16px;font-size: 14px;margin-top: 10px;">{{ item.createTime }}</div>
                <div>
                  <div style="line-height: 24px;font-size: 16px;font-weight: 700;">{{ item.title }}</div>
                  <div
                      style="line-height: 16px;display: block;font-size: 10px;  width: 100%;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;">
                    {{ item.content }}</div>
                </div>
              </div>
            </el-dropdown-item>
            <el-dropdown-item v-if="noCount != 0">查看更多消息 》</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
      <template v-if="device !== 'mobile'">
        <screenfull id="screenfull" class="right-menu-item hover-effect" />
@@ -32,6 +62,19 @@
        </el-dropdown>
      </div>
    </div>
    <el-dialog :title="onDetail.title" v-model="open" class="avue-dialog avue-dialog--top" width="50%">
      <div style="font-size: 14px">
        {{onDetail.content}}
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"   @click="goToDetail">
            查看详情
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -42,11 +85,13 @@
import Hamburger from '@/components/Hamburger/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import {computed} from "vue";
import {computed,ref} from "vue";
import {useAppStore} from "@/store/modules/app";
import {useSettingsStore} from "@/store/modules/settings";
import {useUserStore} from "@/store/modules/user";
import tongzhi from "@/assets/tongzhi.png"
import {getNoCountApi, getReadApi} from "@/api/common";
import router from "@/router";
const userStore = useUserStore();
const appStore = useAppStore();
const settingsStore = useSettingsStore();
@@ -88,6 +133,39 @@
function setLayout() {
  emits('setLayout');
}
const noCount = ref(0);
const noList = ref([]);
const onDetail = ref<any>({});
const open = ref(false);
/**
 * 查询未读
 */
const getMessage = () => {
  getNoCountApi().then(res=>{
    let resData = res.data || {};
    noCount.value = resData.count ;
    noList.value  = resData.list;
  })
}
getMessage();
const itemClick = (item:any)=>{
  getReadApi(item.id).then(res=>{
    open.value = true;
    onDetail.value = res.data || {};
    getMessage();
  })
}
const goToDetail = () => {
  open.value =false;
  let extraData:any = {};
  if (onDetail.value.extraData){
    extraData = JSON.parse(onDetail.value.extraData)
  }
  router.push("/customer/tmsContract?contactStatus=" + extraData.contactStatus);
}
</script>
<style lang='scss' scoped>
@@ -178,4 +256,9 @@
    }
  }
}
.item {
  display: flex;
  align-items: center;
  //margin-right: 20px;
}
</style>
ui/admin-ui3/src/router/index.ts
@@ -97,6 +97,19 @@
        ]
    },
    {
        path:'/collectionTmsQuotePlan',
        component:Layout,
        hidden:true,
        children:[
            {
                path: "collectionTmsQuotePlanItem",
                component:()=> import("@/views/tms/collectionTmsQuoteDetail/index.vue"),
                name: "CollectionTmsQuotePlanItem",
                meta: {title: '运费报价',activeMenu:'/Quotation/collectionTmsQuotePlan'}
            }
        ]
    },
    {
        path:'/monitor/job-log',
        component:Layout,
        hidden:true,
ui/admin-ui3/src/views/tms/collectionTmsQuoteDetail/index.vue
New file
@@ -0,0 +1,630 @@
<template>
  <basicContainer>
    <avue-crud
        :option="option"
        :table-loading="pageF.loading"
        :data="tableData"
        :page="page"
        :permission="permissionList"
        :before-open="beforeOpen"
        v-model="form" v-model:search="queryParams"
        ref="crudRef"
        @row-update="rowUpdate"
        @row-save="rowSave"
        @refresh-change="refreshChange"
        @row-del="rowDel"
        @search-change="searchChange"
        @search-reset="searchReset"
        @selection-change="selectionChange"
        @current-change="currentChange"
        @size-change="sizeChange"
        @on-load="onLoad"
    >
      <template #menu-left>
        <el-button
            type="success"
            icon="Edit"
            :disabled="pageF.single"
            v-hasPermi="['tms:tmsQuoteDetail:edit']"
            @click="handleUpdate">修改
        </el-button>
        <el-button
            type="danger"
            icon="Delete"
            :disabled="pageF.multiple"
            @click="handleDelete"
            v-hasPermi="['tms:tmsQuoteDetail:remove']"
        >删除
        </el-button>
        <el-button
            type="warning"
            plain
            icon="Download"
            @click="handleExport"
            v-hasPermi="['tms:tmsQuoteDetail:export']"
        >导出
        </el-button>
        <el-button
            type="default"
            plain
            icon="Back"
            @click="handleBack"
        >返回
        </el-button>
      </template>
      <template #items-form="scope">
        <avue-crud
            :option="{...itemsTableOption,selection: !scope.disabled}"          @selection-change="selectionChange2"
            :data="form.items" ref="itemsCrudRef"
        >
          <template #freightPrice="{row}">
            <el-input v-model="row.freightPrice" :min="1" :disabled="scope.disabled || !rowKeys.includes(row.rowKey)" type="number" placeholder="请输入金额"></el-input>
          </template>
          <template #currency="{row}">
            <el-radio-group v-model="row.currency"  :disabled="scope.disabled || !rowKeys.includes(row.rowKey)">
              <el-radio
                  v-for="dict in sys_currency"
                  :key="dict.value"
                  :label="dict.value"
              >{{ dict.label }}</el-radio>
            </el-radio-group>
          </template>
        </avue-crud>
      </template>
    </avue-crud>
  </basicContainer>
</template>
<script setup name="tmsQuoteDetail" lang="ts">
import {
  TmsQuoteDetailI,
  addTmsQuoteDetail,
  delTmsQuoteDetail,
  exportTmsQuoteDetail,
  getTmsQuoteDetail,
  listTmsQuoteDetail,
  updateTmsQuoteDetail
} from "@/api/tms/tmsQuoteDetail";
import useCurrentInstance from "@/utils/useCurrentInstance";
import {computed, reactive, ref, toRefs,onBeforeMount} from "vue";
import {PagesInterface, PageQueryInterface} from "@/utils/globalInterface";
import {usePagePlus} from "@/hooks/usePagePlus";
import {hasPermission} from "@/utils/permissionUtils";
import {getTmsServiceProvider, listTmsServiceProvider} from "@/api/tms/tmsServiceProvider";
import {getArea, getCity, getProvince, getStreet} from "@/api/tms/tmsRegion";
import {useRoute,useRouter } from "vue-router";
import {randomId} from "@smallwei/avue";
const {proxy} = useCurrentInstance();
const crudRef = ref();
const route = useRoute();
const router  = useRouter();
const {vehicle_type,sys_currency} =
    proxy.useDict("vehicle_type","sys_currency");
const permissionList = computed(() => {
  return {
    addBtn: hasPermission(["tms:tmsQuoteDetail:add"]),
    delBtn: hasPermission(["tms:tmsQuoteDetail:remove"]),
    editBtn: hasPermission(["tms:tmsQuoteDetail:edit"]),
    viewBtn: hasPermission(["tms:tmsQuoteDetail:query"]),
  }
})
const data = reactive({
  form: <TmsQuoteDetailI>{},
  queryParams: <TmsQuoteDetailI & PageQueryInterface>{},
  page: <PagesInterface>{
    pageSize: 10,
    total: 0,
    currentPage: 1,
  },
  selectionList: [],
  selectionList2:<any>[]
})
const {queryParams, form, page, selectionList,selectionList2} = toRefs(data);
const option = ref({
  pageKey: 'TmsQuoteDetail',
  rowKey: 'id',
  labelWidth: 120,
  group:[
    {
      label:"基础信息",
      prop: 'jcxx',
      column:{
        systemCode: {
          label: '系统编号',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
        },
        quotePlanCode: {
          label: '关联报价方案编号',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
        },
        startRegionCode: {
          label: '起点行政区域',
          addDisplay: true,
          editDisplay: true,dataType: 'string',
          viewDisplay: true,checkStrictly:true,
          type:'cascader',
          rules: [
            {
              required: true,
              message: "起点行政区域不能为空", trigger: "blur"
            }
          ],
          lazy: true,
          props: {
            label: 'name',
            value: 'code'
          },
          change:({value=[]}:{value:any})=>{
            if (value && value.length>0){
              const cascader = crudRef.value?.getPropRef?.('startRegionCode')?.$refs?.temp;
              console.log('cascader.getCheckedNodes()',cascader.getCheckedNodes())
              if (cascader.getCheckedNodes() && cascader.getCheckedNodes().length >0){
                form.value.startRegionCode= cascader.getCheckedNodes()[0].pathValues.toString();
                form.value.startRegion= cascader.getCheckedNodes()[0].text!;
                form.value.transportRoute = `${form.value.startRegion}->${form.value.endRegion}`;
              }
            }
          },
          lazyLoad (node:any, resolve:any) {
            const stopLevel = 3;
            const level = node.level;
            const data = node.data || {};
            const code = data.code;
            let list:any = [];
            const callback = () => {
              resolve((list || []).map((ele:any) => ({
                ...ele,
                leaf: level >= stopLevel
              })));
            };
            if (level === 0) {
              getProvince().then(res => {
                list = res.data || [];
                callback();
              });
            } else if (level === 1) {
              getCity(code).then(res => {
                list = res.data ||[];
                callback();
              });
            } else if (level === 2) {
              getArea(code).then(res => {
                list = res.data ||  [];
                callback();
              });
            }else if (level === 3) {
              getStreet(code).then(res => {
                list = res.data || [];
                callback();
              });
            } else {
              callback();
            }
          }
        },
        // startWarehouse: {
        //   label: '起点仓库',
        //   addDisplay: true,
        //   editDisplay: true,
        //   viewDisplay: true,
        //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_warehouse',
        //
        // },
        endRegionCode: {
          label: '终点行政区域',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,dataType: 'string',
          type:'cascader',checkStrictly:true,
          rules: [
            {
              required: true,
              message: "终点行政区域不能为空", trigger: "blur"
            }
          ],
          lazy: true,
          props: {
            label: 'name',
            value: 'code'
          },
          change:({value=[]}:{value:any})=>{
            if (value && value.length>0){
              const cascader = crudRef.value?.getPropRef?.('endRegionCode')?.$refs?.temp;
              if (cascader.getCheckedNodes() && cascader.getCheckedNodes().length >0){
                form.value.endRegionCode= cascader.getCheckedNodes()[0].pathValues.toString();
                form.value.endRegion= cascader.getCheckedNodes()[0].text!;
                form.value.transportRoute = `${form.value.startRegion}->${form.value.endRegion}`;
              }
            }
          },
          lazyLoad (node:any, resolve:any) {
            const stopLevel = 3;
            const level = node.level;
            const data = node.data || {};
            const code = data.code;
            let list:any = [];
            const callback = () => {
              resolve((list || []).map((ele:any) => ({
                ...ele,
                leaf: level >= stopLevel
              })));
            };
            if (level === 0) {
              getProvince().then(res => {
                list = res.data || [];
                callback();
              });
            } else if (level === 1) {
              getCity(code).then(res => {
                list = res.data ||[];
                callback();
              });
            } else if (level === 2) {
              getArea(code).then(res => {
                list = res.data ||  [];
                callback();
              });
            }else if (level === 3) {
              getStreet(code).then(res => {
                list = res.data || [];
                callback();
              });
            } else {
              callback();
            }
          }
        },
        // endWarehouse: {
        //   label: '终点仓库',
        //   addDisplay: true,
        //   editDisplay: true,
        //   viewDisplay: true,
        //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_warehouse',
        //
        // },
        transportRoute: {
          label: '运输线路',span:24,
          disabled: true,
          // type: 'textarea', minRows: 3, maxRows: 5,
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          hide: false,
          search: true,
          rules: [
            {
              required: true,
              message: "运输线路不能为空", trigger: "blur"
            }
          ],
        },
        vehicleType: {
          label: '车型',
          addDisplay: false,
          editDisplay: true,
          viewDisplay: true,
          type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/vehicle_type',
          rules: [
            {
              required: true,
              message: "车型不能为空", trigger: "change"
            }
          ],
        },
        // transportMode: {
        //   label: '运输方式',
        //   addDisplay: true,
        //   editDisplay: true,
        //   viewDisplay: true,
        //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/transport_mode',
        //
        // },
        // routeType: {
        //   label: '线路类型',
        //   addDisplay: true,
        //   editDisplay: true,
        //   viewDisplay: true,
        //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/route_type',
        //
        // },
        freightPrice: {
          label: '运费报价',
          addDisplay: false,
          editDisplay: true,
          viewDisplay: true,type: 'number',
          rules: [
            {
              required: true,
              message: "运费报价不能为空", trigger: "blur"
            }
          ],
        },
      }
    },
    {
      label: '车型报价',
      prop: 'cxbj', addDisplay: true,editDisplay: false,viewDisplay: false,
      column: {
        items:{
          label: '',labelWidth:0,span:24,
        }
      }
    },
    {
      label: '其他信息',
      prop: 'qtxx',
      column: {
        status: {
          label: '状态',
          type: 'radio', dicUrl: '/system/dict/data/type/sys_normal_disable',
          addDisplay: false,dataType:'string',
          editDisplay: true,
          viewDisplay: true,
          hide: false,
          search: true,
          rules: [
            {
              required: true,
              message: "状态不能为空", trigger: "blur"
            }
          ],
        },
        createBy: {
          label: '创建人',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
          hide: true,
          search: false,
        },
        createTime: {
          label: '创建时间',
          type: 'date', valueFormat: 'YYYY-MM-DD',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
          hide: true,
          search: false,
        },
        updateBy: {
          label: '更新人',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
          hide: false,
          search: false,
        },
        updateTime: {
          label: '更新时间',
          type: 'date', valueFormat: 'YYYY-MM-DD',
          addDisplay: false,
          editDisplay: false,
          viewDisplay: true,
          hide: false,
          search: false,
        },
        remark: {
          label: '备注',
          type: 'textarea', minRows: 3, maxRows: 5,
          addDisplay: true,span:24,
          editDisplay: true,
          viewDisplay: true,
          hide: true,
          search: false,
        },
      }
    }
  ],
  column: {
    systemCode: {
      label: '系统编号',minWidth:150,
      display: false,
      hide: false,
      search: true,
    },
    quotePlanCode: {
      label: '报价方案编号',minWidth:150,
      display: false,
    },
    // vehicleProviderName: {
    //   label: '车辆服务商',minWidth:150,
    //   display: false,
    //   hide: quotePlanType.value != 1,
    //   search: quotePlanType.value == 1,
    // },
    transportRoute: {
      label: '运输线路',minWidth:150,
      display: false,overHidden: true,
      hide: false,
    },
    startRegion: {
      label: '起点行政区域',
      display: false,minWidth:150,
      hide: false,overHidden: true,
      search: false,
    },
    // startWarehouse: {
    //   label: '起点仓库',   display: false,
    //   hide: false,minWidth:150,
    //   search: true,
    //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_warehouse',
    //
    // },
    endRegion: {
      label: '终点行政区域',overHidden: true,
      display: false,minWidth:150,
      hide: false,
    },
    // endWarehouse: {
    //   label: '终点仓库',
    //   display: false,minWidth:150,
    //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_warehouse',
    //   search: true,
    // },
    vehicleType: {
      label: '车型',
      display: false,minWidth:150,
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/vehicle_type',
      search: true,
    },
    // transportMode: {
    //   label: '运输方式',minWidth:150,
    //   display: false,
    //   hide: false,
    //   search: true,
    //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/transport_mode',
    //
    // },
    // routeType: {
    //   label: '线路类型',
    //   display: false,
    //   hide: false,minWidth:150,
    //   search: true,
    //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/route_type',
    //
    // },
    freightPrice: {
      label: '运费报价',
      display: false,
      hide: false,minWidth:150,
    },
    currency: {
      label: '币制',
      display: false,
      hide: false,minWidth:150,
      search: true,
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_currency',
    },
    status: {
      label: '状态',
      type: 'radio',dataType:'string',  dicUrl: '/system/dict/data/type/sys_normal_disable',
      display: false,
      hide: false,
      search: true,minWidth:150,
    },
    updateBy: {
      label: '更新人',
      display: false,minWidth:150,
    },
    updateTime: {
      label: '更新时间',
      display: false,minWidth:180,
    },
  }
})
const itemsTableOption = ref({
  pageKey: 'itemsTable',
  rowKey: 'rowKey',
  header: false,
  addBtn: false,menu: false,
  column:{
    vehicleType:{
      label: '车型',
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/vehicle_type',
    },
    freightPrice:{
      label: '*运费报价',
    },
    currency:{
      label: '币制',
    }
  }
})
const itemsCrudRef =ref()
const {
  tableData,
  pageF,
  rowSave,
  rowUpdate,
  rowDel,
  beforeOpen,
  searchChange,
  searchReset,
  selectionChange,
  onLoad,
  currentChange,
  sizeChange,
  handleDelete,
  handleExport,
  handleUpdate,
  refreshChange
} = usePagePlus({
  form: form,
  option: option,
  queryParams: queryParams,
  idKey: 'id',
  page: page.value,
  getListApi: listTmsQuoteDetail,
  getDetailApi: getTmsQuoteDetail,
  exportApi: exportTmsQuoteDetail,
  deleteApi: delTmsQuoteDetail,
  addApi: addTmsQuoteDetail,
  updateApi: updateTmsQuoteDetail,
  handleUpdateFunc: () => {
    crudRef.value.rowEdit(selectionList.value[0]);
  },
  handleSelectionChangeFunc: (selection: any) => {
    selectionList.value = selection;
  },
  getBeginListFunc(params:any = {}){
    params.quotePlanId = (route.query?.quotePlanId||'') as string;
    return params;
  },
  rowSaveBegin:(row:any,loading:any)=>{
    row.quotePlanId = (route.query?.quotePlanId||'') as string;
    if (selectionList2.value.length == 0){
      proxy.$modal.msgError("请至少选择一条车型报价");
      loading();
      throw new Error("请至少选择一条车型报价");
    }
    let filter = selectionList2.value.filter((item:any)=>{
      return !item.freightPrice || !item.currency
    });
    if (filter.length > 0){
      proxy.$modal.msgError("请填写所有必填项");
      loading();
      throw new Error("请填写所有必填项")
    }
    row.quoteItems = selectionList2.value;
  },
  handleBeforeOpenFunc:(type:string)=>{
    form.value.items = [];
    if (type === 'add'){
      form.value.items = vehicle_type.value.map((item:any)=>{
        return { rowKey: randomId() ,vehicleType: item.value,}
      })
    }
  },
})
const handleBack = () => {
  router.back()
}
const rowKeys = ref<any>([]);
const selectionChange2 = (selection?: any[]) => {
  selectionList2.value = selection;
  rowKeys.value = selection?.map((item:any)=>item.rowKey);
}
</script>
ui/admin-ui3/src/views/tms/collectionTmsQuotePlan/index.vue
@@ -46,8 +46,40 @@
        </el-button>
      </template>
      <template #menu-before="{row}">
        <el-link size="small" type="primary" @click="goToDetail(row)"  class="link-btn" :underline="false" icon="el-icon-d-arrow-right">报价明细</el-link>
        <el-link size="small" type="primary" @click="goToDetail(row)"  class="link-btn" :underline="false" icon="el-icon-d-arrow-right">运费报价</el-link>
      </template>
      <template #items-form="scope">
        <avue-crud
            :option="{...itemsTableOption,selection: !scope.disabled}"          @selection-change="selectionChange2"
            :data="form.items" ref="itemsCrudRef"
        >
          <template #unit="{row}">
            <el-select  v-model="row.unit" :disabled="scope.disabled || !rowKeys.includes(row.rowKey)" placeholder="请选择计费单位">
              <el-option
                  v-for="dict in sys_unit"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
              ></el-option>
            </el-select>
          </template>
          <template #price="{row}">
            <el-input v-model="row.price" :min="1" :disabled="scope.disabled || !rowKeys.includes(row.rowKey)" type="number" placeholder="请输入金额"></el-input>
          </template>
          <template #currency="{row}">
            <el-radio-group v-model="row.currency"  :disabled="scope.disabled || !rowKeys.includes(row.rowKey)">
              <el-radio
                  v-for="dict in sys_currency"
                  :key="dict.value"
                  :label="dict.value"
              >{{ dict.label }}</el-radio>
            </el-radio-group>
          </template>
        </avue-crud>
      </template>
    </avue-crud>
  </basicContainer>
</template>
@@ -63,16 +95,19 @@
  updateTmsQuotePlan
} from "@/api/tms/tmsQuotePlan";
import useCurrentInstance from "@/utils/useCurrentInstance";
import {computed, reactive, ref, toRefs} from "vue";
import {computed, reactive, ref, toRefs,nextTick} from "vue";
import {PagesInterface, PageQueryInterface} from "@/utils/globalInterface";
import {usePagePlus} from "@/hooks/usePagePlus";
import {hasPermission} from "@/utils/permissionUtils";
import {getTmsProject, listTmsProject} from "@/api/tms/tmsProject";
import router from "@/router";
import {getDicts} from "@/api/system/dict/data";
import { randomId } from "@smallwei/avue";
const {proxy} = useCurrentInstance();
const crudRef = ref();
const {sys_quotation_items,sys_unit,sys_currency} =
    proxy.useDict("sys_quotation_items","sys_unit","sys_currency");
const permissionList = computed(() => {
  return {
    addBtn: hasPermission(["tms:tmsQuotePlan:add"]),
@@ -91,8 +126,10 @@
    currentPage: 1,
  },
  selectionList: [],
  selectionList2: <any>[],
})
const {queryParams, form, page, selectionList} = toRefs(data);
const {queryParams, form, page, selectionList,selectionList2} = toRefs(data);
const option = ref({
  pageKey: 'TmsQuotePlan',
  rowKey: 'id',
@@ -236,6 +273,15 @@
      }
    },
    {
      label: '应收费用报价',
      prop: 'bjxm',
      column:{
        items:{
          label: '', labelWidth:0,span:24,
        }
      }
    },
    {
      label: '其他信息',
      prop: 'qtxx',
      column:{
@@ -339,6 +385,30 @@
  }
})
const itemsTableOption = ref({
  pageKey: 'itemsTable',
  rowKey: 'rowKey',
  header: false,
  addBtn: false,menu: false,
  column:{
    freeName:{
      label: '费用名称',
    },
    unit:{
      label: '*计量单位',
    },
    price:{
      label: '*计费金额',
    },
    currency:{
      label: '币制',
    }
  }
})
const itemsCrudRef =ref()
const {
  tableData,
  pageF,
@@ -376,11 +446,87 @@
  },
  getBeginListFunc:()=>{
    queryParams.value.planType = '0'
  },
  handleBeforeOpenFunc:(type:string)=>{
    form.value.items = [];
    if (type === 'add'){
      form.value.items = sys_quotation_items.value.map((item:any)=>{
        return { rowKey: randomId() ,freeName: item.label,unit: '次'}
      })
    }
  },
  rowSaveBegin:(row:any,loading:any)=>{
      if (selectionList2.value.length == 0){
        proxy.$modal.msgError("请至少选择一条应收费用报价项");
        loading();
        throw new Error("请至少选择一条应收费用报价项");
      }
      let filter = selectionList2.value.filter((item:any)=>{
         return !item.price || !item.currency
       });
      if (filter.length > 0){
        proxy.$modal.msgError("请填写所有必填项");
        loading();
        throw new Error("请填写所有必填项")
      }
      row.quoteItems = selectionList2.value;
  },
  rowUpdateBegin(row:any,loading:any){
    if (selectionList2.value.length == 0){
      proxy.$modal.msgError("请至少选择一条应收费用报价项");
      loading();
      throw new Error("请至少选择一条应收费用报价项");
    }
    let filter = selectionList2.value.filter((item:any)=>{
      return !item.price || !item.currency
    });
    if (filter.length > 0){
      proxy.$modal.msgError("请填写所有必填项");
      loading();
      throw new Error("请填写所有必填项")
    }
    row.quoteItems = selectionList2.value;
  },
  handleEndOpenFunc:(type:string,res:any)=>{
    if (type === 'edit'){
      selectionList2.value = (res.data.quoteItems || []).map((item:any)=>{
        item.rowKey =randomId()
        return item;
      });
      form.value.items = sys_quotation_items.value.map((item:any)=>{
        let find = selectionList2.value.find((ele:any)=>
          ele.freeName === item.label
        );
        if ( find){
          return find;
        }else{
          return { rowKey: randomId() ,freeName: item.label,unit: '次'}
        }
      })
      nextTick( ()=>{
        selectionList2.value.map((find:any)=>{
          itemsCrudRef.value.toggleRowSelection(find,true)
        })
      })
    }else{
      form.value.items = res.data.quoteItems || [];
    }
  }
})
const goToDetail = (row?:any) => {
  router.push("/basic/tmsQuoteDetail?quotePlanId=" + row.id);
  router.push("/collectionTmsQuotePlan/collectionTmsQuotePlanItem?quotePlanType=0&quotePlanId=" + row.id);
}
const rowKeys = ref<any>([]);
const selectionChange2 = (selection?: any[]) => {
  selectionList2.value = selection;
  rowKeys.value = selection?.map((item:any)=>item.rowKey);
}
</script>
ui/admin-ui3/src/views/tms/paymentTmsQuotePlan/index.vue
@@ -380,7 +380,7 @@
})
const goToDetail = (row?:any) => {
  router.push("/basic/tmsQuoteDetail?quotePlanId=" + row.id);
  router.push("/basic/tmsQuoteDetail?quotePlanType=1&quotePlanId=" + row.id);
}
</script>
ui/admin-ui3/src/views/tms/tmsContract/index.vue
@@ -68,6 +68,7 @@
import dayjs from 'dayjs';
import {formatDate} from "@/utils/ruoyi";
import {getTmsSettlementEntity, listTmsSettlementEntity} from "@/api/tms/tmsSettlementEntity";
import router from "@/router";
const {proxy} = useCurrentInstance();
const crudRef = ref();
@@ -110,6 +111,19 @@
            {
              required: true,
              message: "系统编号不能为空", trigger: "blur"
            }
          ],
        },
        isTempContract: {
          label: '临时合同',
          type: 'radio', dataType: 'string', dicUrl: '/system/dict/data/type/sys_number_is',
          addDisplay: true,value:'1',
          editDisplay: true,
          viewDisplay: true,
          rules: [
            {
              required: true,
              message: "是否临时合同不能为空", trigger: "change"
            }
          ],
        },
@@ -156,12 +170,6 @@
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          rules: [
            {
              required: true,
              message: "签约日期不能为空", trigger: "blur"
            }
          ],
        },
        contractDate: {
          label: '合同期限',
@@ -629,6 +637,13 @@
      hide: false,
      search: true,
    },
    isTempContract: {
      label: '临时合同',
      type: 'radio', dataType: 'string', dicUrl: '/system/dict/data/type/sys_number_is',
      display: false,minWidth:150,
      hide: false,
      search: true,
    },
    contractType: {
      label: '合同类型',
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/contract_type',
@@ -779,6 +794,9 @@
  deleteApi: delTmsContract,
  addApi: addTmsContract,
  updateApi: updateTmsContract,
  getBeginListFunc:()=>{
    queryParams.value.contractStatus = <any>router.currentRoute.value.query.contactStatus || undefined;
  },
  handleUpdateFunc: () => {
    crudRef.value.rowEdit(selectionList.value[0]);
  },
@@ -786,7 +804,7 @@
    selectionList.value = selection;
  },
})
console.log(router.currentRoute.value.query.contactStatus)
</script>
ui/admin-ui3/src/views/tms/tmsDispatchOrder/index.vue
@@ -103,6 +103,17 @@
      </template>
    </avue-crud>
    <el-dialog :title="pageF.title" v-model="pageF.open" class="avue-dialog avue-dialog--top" width="80%">
      <h2 v-if="optionType == 'addFinance'">报价费用</h2>
      <avue-crud v-if="optionType == 'addFinance'"
          :option="itemsTableOption"
          :data="itemsTableData"
                 @selection-change="selectionChange2"
      >
        <template #count="{row}">
          <el-input-number v-model="row.count" @change="countChange(row)" placeholder="请输入计费数量" min="0"></el-input-number>
        </template>
      </avue-crud>
      <h2 v-if="optionType == 'addFinance'">实报实销费用</h2>
      <avue-form v-if="optionType == 'addItinerary' || optionType == 'addFinance'" v-model="boxForm" ref="boxFormRef" :option="boxFormOption">
      </avue-form>
@@ -236,6 +247,8 @@
import {addTmsFinanceDetail, listTmsFinanceDetail, updateTmsFinanceDetail} from "@/api/tms/tmsFinanceDetail";
import {getTmsProductInfo, listTmsProductInfo} from "@/api/tms/tmsProductInfo";
import {addTmsGoodsDetail, delTmsGoodsDetail, listTmsGoodsDetail, updateTmsGoodsDetail} from "@/api/tms/tmsGoodsDetail";
import {getTransportRouteVi, listTransportRouteVi} from "@/api/tms/tmsTransportRouteVi";
import {listTmsQuoteItem} from "@/api/tms/tmsQuotePlanItem";
const {proxy} = useCurrentInstance();
const crudRef = ref();
@@ -275,6 +288,7 @@
  open: false,
  open2:false,
  goodsTableData: <any>[],
  selectionList2: <any>[],
  goodsForm:<any>{},
  open3: false,
  actualForm:<any>{},
@@ -282,7 +296,7 @@
const {queryParams, form, page, selectionList,open3,
  boxTableData, optionType,
  boxFormOption,boxForm,boxTableOption,title,open,open2,
  goodsTableData,goodsForm,actualForm
  goodsTableData,goodsForm,actualForm,selectionList2
} = toRefs(data);
const option = ref({
  pageKey: 'TmsDispatchOrder',
@@ -296,77 +310,75 @@
      label: '基本信息',
      prop: 'jbxx',
      column: {
        projectId: {
          label: '关联项目',
        quoteDetailId: {
          label: '运输线路', span: 24,
          display: true,
          rules: [
            {
              required: true,
              message: "关联项目不能为空", trigger: "change"
              message: "请选择运输路线", trigger: "change"
            }
          ],
          type: 'table', suffixIcon: 'search',
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('projectId')?.$refs?.temp;
            const table = crudRef.value?.getPropRef?.('quoteDetailId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              Object.assign(form.value, {
                projectId: active.id,
                quoteDetailId: active.quoteDetailId,
                transportLine: active.transportRoute,
                startRegionCode: active.startRegionCode,
                endRegionCode: active.endRegionCode,
                projectId: active.projectId,
                projectName: active.projectName,
                customerId: active.relatedCustomerId,
                customerName: active.relatedCustomerName,
                customerCode: active.relatedCustomerCode,
                contractId: active.relatedContractId,
                contractName: active.relatedContractName,
                customerId: active.customerId,
                customerName: active.customerFullName,
                customerCode: active.customerCode,
                contractId: active.contractId,
                contractName: active.contractName,
                requiredVehicleTypes: active.vehicleType,
                quotePlanId: active.quotePlanId,
              });
            }
          },
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchMenuSpan: 5,
            column: {
              transportRoute: {
                label: '运输路线', minWidth: 130,
                search: true,
              },
              customerFullName: {
                label: '客户全称', minWidth: 120,
                search: true,
              },
              projectName: {
                label: '项目名称', minWidth: 130,
                search: true,
              },
              projectCode: {
                label: '项目编号', minWidth: 120,
                search: true,
              },
              relatedContractName: {
                label: '关联合同',
                label: '关联项目',
                display: false, minWidth: 150,
                search: true,
              },
              relatedCustomerName: {
                label: '关联客户',
              contractName: {
                label: '关联合同',
                display: false, minWidth: 150,
                search: true,
              },
              status: {
                label: '状态', dataType: 'string',
                type: 'radio', dicUrl: '/system/dict/data/type/data_status',
                addDisplay: false, minWidth: 150,
                editDisplay: false,
                viewDisplay: true,
              vehicleType: {
                label: '车型', dataType: 'string',
                type: 'select', dicUrl: '/system/dict/data/type/vehicle_type', minWidth: 150,
                hide: false,
                search: true,
                rules: [
                  {
                    required: true,
                    message: "状态不能为空", trigger: "blur"
                  }
                ],
              },
            },
          },
          props: {
            label: 'projectName',
            value: 'id'
            label: 'transportRoute',
            value: 'quoteDetailId'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
@@ -374,11 +386,11 @@
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsProject(id).then(res => {
              getTransportRouteVi(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              listTmsProject({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
              listTransportRouteVi({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
@@ -387,10 +399,13 @@
            }
          }
        },
        customerName: {
          label: '客户名称',
          display: true, disabled: true
        },
        projectName: {
          label: '关联项目',
          display: true, disabled: true
        },
        // customerCode: {
@@ -398,10 +413,19 @@
        //   display: true,disabled:true
        // },
        contractName: {
          label: '关联合同名称',
          label: '关联合同',
          display: true, disabled: true
        },
        requiredVehicleTypes: {
          label: '下单车型',disabled: true,
          display: true, type: 'select', dicUrl: '/system/dict/data/type/vehicle_type', dataType: 'string',
          rules: [
            {
              required: true,
              message: "下单车型不能为空", trigger: "blur"
            }
          ],
        },
        orderType: {
          label: '订单类型',
          display: true,
@@ -413,18 +437,258 @@
              message: "订单类型不能为空", trigger: "change"
            }
          ],
          change:({value}: any)=>{
              option.value.group.forEach((item:any) =>{
                if (item.prop == 'pcxx'){
                  item.column.shipperId.rules.forEach((cItem:any)=>{
                    cItem.required = !(value == 1);
                  }) ;
                  item.column.receiverId.rules.forEach((cItem:any)=>{
                    cItem.required = !(value == 1);
                  }) ;
                }
              })
          }
        },
        signType: {
          label: '签收类型',
        // signType: {
        //   label: '签收类型',
        //   display: true,
        //   type: 'select', dataType: 'string',
        //   dicUrl: '/system/dict/data/type/sign_type',
        //   rules: [
        //     {
        //       required: true,
        //       message: "签收类型不能为空", trigger: "change"
        //     }
        //   ],
        // },
        // loadingServiceProviderId: {
        //   label: '装货服务商',
        //   display: true,
        //   rules: [
        //     {
        //       required: true,
        //       message: "装货服务商不能为空", trigger: "change"
        //     }
        //   ],
        //   change: (val: any) => {
        //     const table = crudRef.value?.getPropRef?.('loadingServiceProviderId')?.$refs?.temp;
        //     if (!table) return;
        //     let active = table.active;
        //     if (Array.isArray(active)) active = active[0];
        //     if (active) {
        //       Object.assign(form.value, {
        //         loadingServiceProviderId: active.id,
        //         loadingServiceProviderName: active.serviceShortName,
        //       });
        //     }
        //   },
        //   type: 'table', suffixIcon: 'search',
        //   children: {
        //     border: true,
        //     searchLabelWidth: 100,
        //     searchMenuSpan: 5,
        //     column: {
        //       serviceCode: {
        //         label: '服务商编码', minWidth: 130,
        //         search: true,
        //       },
        //       serviceShortName: {
        //         label: '服务商简称', minWidth: 120,
        //         search: true,
        //       },
        //       serviceType: {
        //         label: '服务类型', multiple: true,
        //         type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/service_type',
        //         minWidth: 150,
        //       },
        //       contactName: {
        //         label: '联系人姓名', minWidth: 120,
        //       },
        //     },
        //
        //   },
        //   props: {
        //     label: 'serviceShortName',
        //     value: 'id'
        //   },
        //   onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
        //     if (value) {
        //       let id = value;
        //       if (Array.isArray(value)) {
        //         id = value[0]
        //       }
        //       getTmsLoadingServiceProvider(id).then(res => {
        //         return callback(res.data || {})
        //       })
        //     } else {
        //       listTmsLoadingServiceProvider({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
        //         return callback({
        //           total: res.total,
        //           data: res.rows || [],
        //         })
        //       })
        //     }
        //
        //   }
        //
        // },
        // customsServiceProviderId: {
        //   label: '报关服务商',
        //   display: true,
        //   rules: [
        //     {
        //       required: true,
        //       message: "报关服务商不能为空", trigger: "change"
        //     }
        //   ],
        //   change: (val: any) => {
        //     const table = crudRef.value?.getPropRef?.('customsServiceProviderId')?.$refs?.temp;
        //     if (!table) return;
        //     let active = table.active;
        //     if (Array.isArray(active)) active = active[0];
        //     if (active) {
        //       Object.assign(form.value, {
        //         customsServiceProviderId: active.id,
        //         customsServiceProviderName: active.serviceShortName,
        //       });
        //     }
        //   },
        //   type: 'table', suffixIcon: 'search',
        //   children: {
        //     border: true,
        //     searchLabelWidth: 100,
        //     searchMenuSpan: 5,
        //     column: {
        //       serviceCode: {
        //         label: '服务商编码', minWidth: 130,
        //         search: true,
        //       },
        //       serviceShortName: {
        //         label: '服务商简称', minWidth: 120,
        //         search: true,
        //       },
        //       serviceType: {
        //         label: '服务类型', multiple: true,
        //         type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/service_type',
        //         minWidth: 150,
        //       },
        //       contactName: {
        //         label: '联系人姓名', minWidth: 120,
        //       },
        //     },
        //
        //   },
        //   props: {
        //     label: 'serviceShortName',
        //     value: 'id'
        //   },
        //   onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
        //     if (value) {
        //       let id = value;
        //       if (Array.isArray(value)) {
        //         id = value[0]
        //       }
        //       getTmsCustomsServiceProvider(id).then(res => {
        //         return callback(res.data || {})
        //       })
        //     } else {
        //       listTmsCustomsServiceProvider({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
        //         return callback({
        //           total: res.total,
        //           data: res.rows || [],
        //         })
        //       })
        //     }
        //
        //   }
        // },
        isCustoms: {
          label: '委托报关',
          display: true,
          type: 'select', dataType: 'string',
          dicUrl: '/system/dict/data/type/sign_type',
          type: 'radio', dicUrl: '/system/dict/data/type/sys_number_is', dataType: 'string',
          rules: [
            {
              required: true,
              message: "签收类型不能为空", trigger: "change"
              message: "委托报关不能为空", trigger: "blur"
            }
          ],
        },
        isUrgent: {
          label: '是否紧急',
          display: true,
          type: 'radio', dicUrl: '/system/dict/data/type/sys_number_is', dataType: 'string',
          rules: [
            {
              required: true,
              message: "是否紧急不能为空", trigger: "blur"
            }
          ],
        },
        // transportType: {
        //   label: '运输方式',
        //   display: true, type: 'select', dicUrl: '/system/dict/data/type/dispatch_transport_mode', dataType: 'string',
        //
        // },
        // loadMethod: {
        //   label: '配载方式',
        //   display: true, type: 'select', dicUrl: '/system/dict/data/type/load_method', dataType: 'string',
        // },
        latestDeparture: {
          label: '要求最晚出发时间',
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss', labelWidth: 150,
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
          rules: [
            {
              required: true,
              message: "要求最晚出发时间不能为空", trigger: "blur"
            }
          ],
        },
        latestArrival: {
          label: '要求最晚到达时间',labelWidth: 150,
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss',
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
          rules: [
            {
              required: true,
              message: "要求最晚到达时间不能为空", trigger: "blur"
            }
          ],
        },
      }
    },
    {
      label: '派车信息',
      prop: 'pcxx',
      column: {
        operationMode:{
          label: '是否是自营车队',value:0,
          type: 'radio', dataType: 'string', dicUrl: '/system/dict/data/type/sys_number_is',
          rules: [
            {
              required: true,
              message: "是否是自营车队不能为空", trigger: "change"
            }
          ],
          change: ({value}:any) => {
            console.log(value)
            option.value.group.forEach((item:any) =>{
              if (item.prop == 'pcxx'){
                item.column.vehicleProviderId.rules.forEach((cItem:any)=>{
                  cItem.required = value != 1;
                }) ;
              }
            })
          },
        },
        vehicleProviderId: {
          label: '车辆服务商',
@@ -512,167 +776,6 @@
          }
        },
        loadingServiceProviderId: {
          label: '装货服务商',
          display: true,
          rules: [
            {
              required: true,
              message: "装货服务商不能为空", trigger: "change"
            }
          ],
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('loadingServiceProviderId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              Object.assign(form.value, {
                loadingServiceProviderId: active.id,
                loadingServiceProviderName: active.serviceShortName,
              });
            }
          },
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              serviceCode: {
                label: '服务商编码', minWidth: 130,
                search: true,
              },
              serviceShortName: {
                label: '服务商简称', minWidth: 120,
                search: true,
              },
              serviceType: {
                label: '服务类型', multiple: true,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/service_type',
                minWidth: 150,
              },
              contactName: {
                label: '联系人姓名', minWidth: 120,
              },
            },
          },
          props: {
            label: 'serviceShortName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsLoadingServiceProvider(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              listTmsLoadingServiceProvider({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        customsServiceProviderId: {
          label: '报关服务商',
          display: true,
          rules: [
            {
              required: true,
              message: "报关服务商不能为空", trigger: "change"
            }
          ],
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('customsServiceProviderId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              Object.assign(form.value, {
                customsServiceProviderId: active.id,
                customsServiceProviderName: active.serviceShortName,
              });
            }
          },
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              serviceCode: {
                label: '服务商编码', minWidth: 130,
                search: true,
              },
              serviceShortName: {
                label: '服务商简称', minWidth: 120,
                search: true,
              },
              serviceType: {
                label: '服务类型', multiple: true,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/service_type',
                minWidth: 150,
              },
              contactName: {
                label: '联系人姓名', minWidth: 120,
              },
            },
          },
          props: {
            label: 'serviceShortName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsCustomsServiceProvider(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              listTmsCustomsServiceProvider({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        isUrgent: {
          label: '是否紧急',
          display: true,
          type: 'select', dicUrl: '/system/dict/data/type/sys_number_is', dataType: 'string',
          rules: [
            {
              required: true,
              message: "是否紧急不能为空", trigger: "blur"
            }
          ],
        },
        transportType: {
          label: '运输方式',
          display: true, type: 'select', dicUrl: '/system/dict/data/type/dispatch_transport_mode', dataType: 'string',
        },
        loadMethod: {
          label: '配载方式',
          display: true, type: 'select', dicUrl: '/system/dict/data/type/load_method', dataType: 'string',
        },
        mainDriverId: {
          label: '主驾驶员',
          display: true,dataType: 'string',
@@ -729,7 +832,7 @@
                return callback(res.data || {})
              })
            } else {
              listTmsDriver({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
              listTmsDriver({pageSize: page.pageSize, pageNum: page.currentPage, ...data,carrierType:form.value.operationMode}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
@@ -791,7 +894,7 @@
                return callback(res.data || {})
              })
            } else {
              listTmsDriver({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
              listTmsDriver({pageSize: page.pageSize, pageNum: page.currentPage, ...data,carrierType:form.value.operationMode}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
@@ -801,20 +904,6 @@
          }
        },
        requiredVehicleTypes: {
          label: '要求车型',
          display: true, type: 'select', dicUrl: '/system/dict/data/type/vehicle_type', dataType: 'string',
          rules: [
            {
              required: true,
              message: "要求车型不能为空", trigger: "blur"
            }
          ],
        },
        actualVehicleType: {
          label: '实际运输工具类型',
          display: true, type: 'select', dicUrl: '/system/dict/data/type/vehicle_type', dataType: 'string',
        },
        vehicleId: {
          label: '车牌号',dataType: 'string',
@@ -884,7 +973,7 @@
                return callback(res.data || {})
              })
            } else {
              listTmsVehicle({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
              listTmsVehicle({pageSize: page.pageSize, pageNum: page.currentPage, ...data,carrierType:form.value.operationMode}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
@@ -894,6 +983,269 @@
          }
        },
        actualVehicleType: {
          label: '派出车型',
          display: true, type: 'select', dicUrl: '/system/dict/data/type/vehicle_type', dataType: 'string',
        },
        shipperId: {
          label: '装货点',dataType:'string',
          display: true,
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('shipperId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              let shipperRegionCode = '';
              if (active.provinceId){
                shipperRegionCode = active.provinceId;
              }
              if (active.cityId){
                shipperRegionCode =shipperRegionCode+ ","+active.cityId;
              }
              if (active.districtId){
                shipperRegionCode =shipperRegionCode+ ","+active.districtId;
              }
              if (active.streetId){
                shipperRegionCode =shipperRegionCode+ ","+active.streetId;
              }
              Object.assign(form.value, {
                shipperId: active.id,
                shipperName: active.consignorName,
                shipperRegionLabel: active.regionLabel,
                shipperAddress: active.addressDetail,
                shipperMobile: active.contactPhone,
                shipperRegionCode:shipperRegionCode
              });
             }
          },
          rules: [
            {
              required: false,
              message: "装货点不能为空", trigger: "change"
            }
          ],
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              consignorCode: {
                label: '收装货点编码', minWidth: 130,
                search: true,
              },
              consignorName: {
                label: '收装货点名称', minWidth: 130,
                search: true,
              },
              consignorType: {
                label: '收装货点类型', minWidth: 80,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/consignor_type',
                display: false,
                hide: false,
                search: true,
              },
              contactName: {
                label: '联系人姓名', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              contactPhone: {
                label: '联系人电话', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              regionLabel: {
                label: '行政区域', minWidth: 180,
                display: false,
                hide: false,
                search: true,
              },
            },
          },
          props: {
            label: 'consignorName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsConsignor(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              if (form.value.startRegionCode) {
                const split = form.value.startRegionCode.split(',');
                const [provinceId, cityId, districtId, streetId] = split;
                if (provinceId) data.provinceId = provinceId;
                if (cityId) data.cityId = cityId;
                if (districtId) data.districtId = districtId;
                if (streetId) data.streetId = streetId;
              }
              console.log(form.value.startRegionCode)
              listTmsConsignor({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        shipperRegionLabel: {
          label: '装货点行政区域',
          display: true, disabled: true
        },
        shipperAddress: {
          label: '装货点详细地址',
          display: true, disabled: true
        },
        shipperMobile: {
          label: '装货点联系方式',
          display: true, disabled: true
        },
        receiverId: {
          label: '卸货点',
          display: true, dataType:'string',
          rules: [
            {
              required: true,
              message: "卸货点不能为空", trigger: "change"
            }
          ],
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('receiverId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              let receiverRegionCode = '';
              if (active.provinceId){
                receiverRegionCode = active.provinceId;
              }
              if (active.cityId){
                receiverRegionCode =receiverRegionCode+ ","+active.cityId;
              }
              if (active.districtId){
                receiverRegionCode =receiverRegionCode+ ","+active.districtId;
              }
              if (active.streetId){
                receiverRegionCode =receiverRegionCode+ ","+active.streetId;
              }
              Object.assign(form.value, {
                receiverId: active.id,
                receiverName: active.consignorName,
                receiverRegionLabel: active.regionLabel,
                receiverAddress: active.addressDetail,
                receiverMobile: active.contactPhone,
                receiverRegionCode:receiverRegionCode,
              });
            }
          },
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              consignorCode: {
                label: '收装货点编码', minWidth: 130,
                search: true,
              },
              consignorName: {
                label: '收装货点名称', minWidth: 130,
                search: true,
              },
              consignorType: {
                label: '收装货点类型', minWidth: 80,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/consignor_type',
                display: false,
                hide: false,
                search: true,
              },
              contactName: {
                label: '联系人姓名', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              contactPhone: {
                label: '联系人电话', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              regionLabel: {
                label: '行政区域', minWidth: 180,
                display: false,
                hide: false,
                search: true,
              },
            },
          },
          props: {
            label: 'consignorName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsConsignor(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              if (form.value.endRegionCode) {
                const split = form.value.endRegionCode.split(',');
                const [provinceId, cityId, districtId, streetId] = split;
                if (provinceId) data.provinceId = provinceId;
                if (cityId) data.cityId = cityId;
                if (districtId) data.districtId = districtId;
                if (streetId) data.streetId = streetId;
              }
              listTmsConsignor({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        receiverRegionLabel: {
          label: '卸货点行政区域',
          display: true, disabled: true
        },
        receiverAddress: {
          label: '卸货点详细地址',
          display: true, disabled: true
        },
        receiverMobile: {
          label: '卸货点联系方式',
          display: true, disabled: true
        },
        containerId: {
          label: '关联集装箱信息',
@@ -1022,338 +1374,83 @@
          }
        },
      }
    },
    {
      label: '收装货点信息',
      prop: 'shffxrxx',
      column: {
        shipperId: {
          label: '装货点',dataType:'string',
          display: true,
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('shipperId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              let shipperRegionCode = '';
              if (active.provinceId){
                shipperRegionCode = active.provinceId;
              }
              if (active.cityId){
                shipperRegionCode =shipperRegionCode+ ","+active.cityId;
              }
              if (active.districtId){
                shipperRegionCode =shipperRegionCode+ ","+active.districtId;
              }
              if (active.streetId){
                shipperRegionCode =shipperRegionCode+ ","+active.streetId;
              }
              Object.assign(form.value, {
                shipperId: active.id,
                shipperName: active.consignorName,
                shipperRegionLabel: active.regionLabel,
                shipperAddress: active.addressDetail,
                shipperMobile: active.contactPhone,
                shipperRegionCode:shipperRegionCode
              });
              form.value.transportLine = form.value.shipperRegionLabel
                  + (form.value.receiverRegionLabel ? '>' + form.value.receiverRegionLabel : '');
            }
          },
          rules: [
            {
              required: true,
              message: "装货点不能为空", trigger: "change"
            }
          ],
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              consignorCode: {
                label: '收装货点编码', minWidth: 130,
                search: true,
              },
              consignorName: {
                label: '收装货点名称', minWidth: 130,
                search: true,
              },
              consignorType: {
                label: '收装货点类型', minWidth: 80,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/consignor_type',
                display: false,
                hide: false,
                search: true,
              },
              contactName: {
                label: '联系人姓名', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              contactPhone: {
                label: '联系人电话', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              regionLabel: {
                label: '行政区域', minWidth: 180,
                display: false,
                hide: false,
                search: true,
              },
            },
          },
          props: {
            label: 'consignorName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsConsignor(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              listTmsConsignor({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        shipperRegionLabel: {
          label: '装货点行政区域',
          display: true, disabled: true
        },
        shipperAddress: {
          label: '装货点详细地址',
          display: true, disabled: true
        },
        shipperMobile: {
          label: '装货点联系方式',
          display: true, disabled: true
        },
        receiverId: {
          label: '卸货点',
          display: true, dataType:'string',
          rules: [
            {
              required: true,
              message: "卸货点不能为空", trigger: "change"
            }
          ],
          change: (val: any) => {
            const table = crudRef.value?.getPropRef?.('receiverId')?.$refs?.temp;
            if (!table) return;
            let active = table.active;
            if (Array.isArray(active)) active = active[0];
            if (active) {
              let receiverRegionCode = '';
              if (active.provinceId){
                receiverRegionCode = active.provinceId;
              }
              if (active.cityId){
                receiverRegionCode =receiverRegionCode+ ","+active.cityId;
              }
              if (active.districtId){
                receiverRegionCode =receiverRegionCode+ ","+active.districtId;
              }
              if (active.streetId){
                receiverRegionCode =receiverRegionCode+ ","+active.streetId;
              }
              Object.assign(form.value, {
                receiverId: active.id,
                receiverName: active.consignorName,
                receiverRegionLabel: active.regionLabel,
                receiverAddress: active.addressDetail,
                receiverMobile: active.contactPhone,
                receiverRegionCode:receiverRegionCode,
              });
              form.value.transportLine =
                  (form.value.shipperRegionLabel ? form.value.shipperRegionLabel + '>' : '') + form.value.receiverRegionLabel;
            }
          },
          type: 'table', suffixIcon: 'search',
          children: {
            border: true,
            searchLabelWidth: 100,
            searchMenuSpan: 5,
            column: {
              consignorCode: {
                label: '收装货点编码', minWidth: 130,
                search: true,
              },
              consignorName: {
                label: '收装货点名称', minWidth: 130,
                search: true,
              },
              consignorType: {
                label: '收装货点类型', minWidth: 80,
                type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/consignor_type',
                display: false,
                hide: false,
                search: true,
              },
              contactName: {
                label: '联系人姓名', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              contactPhone: {
                label: '联系人电话', minWidth: 110,
                display: false,
                hide: false,
                search: true,
              },
              regionLabel: {
                label: '行政区域', minWidth: 180,
                display: false,
                hide: false,
                search: true,
              },
            },
          },
          props: {
            label: 'consignorName',
            value: 'id'
          },
          onLoad: ({page, value, data}: { page: any, value: any, data: any }, callback: any) => {
            if (value) {
              let id = value;
              if (Array.isArray(value)) {
                id = value[0]
              }
              getTmsConsignor(id).then(res => {
                return callback(res.data || {})
              })
            } else {
              listTmsConsignor({pageSize: page.pageSize, pageNum: page.currentPage, ...data}).then(res => {
                return callback({
                  total: res.total,
                  data: res.rows || [],
                })
              })
            }
          }
        },
        receiverRegionLabel: {
          label: '卸货点行政区域',
          display: true, disabled: true
        },
        receiverAddress: {
          label: '卸货点详细地址',
          display: true, disabled: true
        },
        receiverMobile: {
          label: '卸货点联系方式',
          display: true, disabled: true
        },
        transportLine: {
          label: '运输线路', span: 24,
          display: true, disabled: true
        },
      }
    },
    {
      label: '运输要求信息',
      prop: 'ysyqxx',
      column: {
        earliestDeparture: {
          label: '要求最早出发时间',
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss',
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
        },
        latestDeparture: {
          label: '要求最晚出发时间',
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss',
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
        },
        earliestArrival: {
          label: '要求最早到达时间',
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss',
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
        },
        latestArrival: {
          label: '要求最晚到达时间',
          type: 'datetime',  // 改为 datetime 类型
          format: 'YYYY-MM-DD HH:mm:ss',
          valueFormat: 'YYYY-MM-DD HH:mm:ss',
          display: true,
        },
      }
    },
    {
      label: '车辆信息',
      prop: 'clxx',
      column: {
        emptyMileage: {
          label: '空载里程', append: 'Km',
          display: true,
        },
        emptyFuel: {
          label: '空载油耗', append: 'L',
          display: true,
        },
        loadedMileage: {
          label: '重载里程', append: 'Km',
          display: true,
        },
        loadedFuel: {
          label: '重载油耗', append: 'L',
          display: true,
        },
      }
    },
    {
      label: '其他信息',
      prop: 'qtyxx',
      column: {
        shiftNo: {
          label: '班次号',
          display: true,
        },
        lineNo: {
          label: '线路号',
          display: true,
        },
        remark: {
          label: '备注', span: 24,
          type: 'textarea', minRows: 3, maxRows: 5,
          display: true,
        },
      }
    }
      },
    },
    // {
    //   label: '运输要求信息',
    //   prop: 'ysyqxx',
    //   column: {
    //     earliestDeparture: {
    //       label: '要求最早出发时间',
    //       type: 'datetime',  // 改为 datetime 类型
    //       format: 'YYYY-MM-DD HH:mm:ss',
    //       valueFormat: 'YYYY-MM-DD HH:mm:ss',
    //       display: true,
    //     },
    //     latestDeparture: {
    //       label: '要求最晚出发时间',
    //       type: 'datetime',  // 改为 datetime 类型
    //       format: 'YYYY-MM-DD HH:mm:ss',
    //       valueFormat: 'YYYY-MM-DD HH:mm:ss',
    //       display: true,
    //     },
    //     earliestArrival: {
    //       label: '要求最早到达时间',
    //       type: 'datetime',  // 改为 datetime 类型
    //       format: 'YYYY-MM-DD HH:mm:ss',
    //       valueFormat: 'YYYY-MM-DD HH:mm:ss',
    //       display: true,
    //     },
    //
    //   }
    // },
    // {
    //   label: '车辆信息',
    //   prop: 'clxx',
    //   column: {
    //     emptyMileage: {
    //       label: '空载里程', append: 'Km',
    //       display: true,
    //     },
    //     emptyFuel: {
    //       label: '空载油耗', append: 'L',
    //       display: true,
    //     },
    //     loadedMileage: {
    //       label: '重载里程', append: 'Km',
    //       display: true,
    //     },
    //     loadedFuel: {
    //       label: '重载油耗', append: 'L',
    //       display: true,
    //     },
    //   }
    // },
    // {
    //   label: '其他信息',
    //   prop: 'qtyxx',
    //   column: {
    //     shiftNo: {
    //       label: '班次号',
    //       display: true,
    //     },
    //     lineNo: {
    //       label: '线路号',
    //       display: true,
    //     },
    //     remark: {
    //       label: '备注', span: 24,
    //       type: 'textarea', minRows: 3, maxRows: 5,
    //       display: true,
    //     },
    //   }
    // }
  ],
  column: {
@@ -2119,6 +2216,35 @@
})
const itemsTableOption = ref({
  pageKey: 'itemsTable',
  rowKey: 'rowKey',
  header: false,
  addBtn: false,menu: false,
  column:{
    feeType:{
      label: '费用名称',
    },
    unit:{
      label: '*计量单位',
    },
    price:{
      label: '*计费金额',
    },
    currency:{
      label: '币制',
    },
    count:{
      label: '计费数量',minWidth:130,
    },
    sum:{
      label: '总金额',
    }
  }
})
const itemsCrudRef =ref()
const itemsTableData = ref<any>([])
const {
  tableData,
  pageF,
@@ -2248,6 +2374,14 @@
    if (Array.isArray(boxForm.value.feeVoucherUrl)) {
      boxForm.value.feeVoucherUrl = boxForm.value.feeVoucherUrl.toString();
    }
    let filter = selectionList2.value.filter((item:any)=>{
      return !item.count
    });
    if (filter.length > 0){
      proxy.$modal.msgError("勾选报价费用请填写计费数量");
      return;
    }
    boxForm.value.items = selectionList2.value;
    addTmsFinanceDetail(boxForm.value).then(res=>{
      ElMessage({
        message: "操作成功!",
@@ -2276,13 +2410,29 @@
  })
}
const handleAddFinance = (row:any)=>{
const handleAddFinance = async (row:any)=>{
  optionType.value = 'addFinance';
  boxFormOption.value = financeOption.value;
  boxTableOption.value = financeTableOption.value;
  const quotePlanItemRes = await listTmsQuoteItem({quotePlanId:row.quotePlanId,pageNum: 1, pageSize: 999});
  let items = quotePlanItemRes.rows || [];
  itemsTableData.value = items.map((item:any) => {
    return {
      rowKey: item.id,
      feeType: item.freeName,
      unit: item.unit,
      price: item.price,
      currency: item.currency,
      count:  0,
      sum: 0,
    }
  })
  listTmsFinanceDetail({
    dispatchOrderId: row.id,financeType:2,
    pageNum: 1, pageSize: 999}).then(res => {
    boxTableData.value = res.rows || [];
    pageF.open = true;
    pageF.title = '费用登记';
@@ -2440,6 +2590,13 @@
    onLoad(page.value)
  })
}
const countChange = (row:any) => {
  row.sum = Number(row.price) * Number(row.count);
}
const selectionChange2 = (selection?: any[]) => {
  selectionList2.value = selection;
}
</script>
ui/admin-ui3/src/views/tms/tmsMessageNotify/index.vue
New file
@@ -0,0 +1,249 @@
<template>
  <basicContainer >
    <avue-crud
        :option="option"
        :table-loading="pageF.loading"
        :data="tableData"
        :page="page"
        :permission="permissionList"
        :before-open="beforeOpen"
        v-model="form" v-model:search="queryParams"
        ref="crudRef"
        @row-update="rowUpdate"
        @row-save="rowSave"
        @refresh-change="refreshChange"
        @row-del="rowDel"
        @search-change="searchChange"
        @search-reset="searchReset"
        @selection-change="selectionChange"
        @current-change="currentChange"
        @size-change="sizeChange"
        @on-load="onLoad"
    >
      <template #menu-left>
        <el-button
            type="success"
            icon="Edit"
            :disabled="pageF.single"
            v-hasPermi="['tms:tmsMessageNotify:edit']"
            @click="handleUpdate">修改
        </el-button>
        <el-button
            type="danger"
            icon="Delete"
            :disabled="pageF.multiple"
            @click="handleDelete"
            v-hasPermi="['tms:tmsMessageNotify:remove']"
        >删除
        </el-button>
        <el-button
            type="warning"
            plain
            icon="Download"
            @click="handleExport"
            v-hasPermi="['tms:tmsMessageNotify:export']"
        >导出
        </el-button>
      </template>
    </avue-crud>
  </basicContainer>
</template>
<script setup name="tmsMessageNotify" lang="ts">
  import {TmsMessageNotifyI,addTmsMessageNotify, delTmsMessageNotify, exportTmsMessageNotify, getTmsMessageNotify, listTmsMessageNotify, updateTmsMessageNotify} from "@/api/tms/tmsMessageNotify";
  import useCurrentInstance from "@/utils/useCurrentInstance";
  import {computed,reactive, ref, toRefs} from "vue";
  import {PagesInterface, PageQueryInterface} from "@/utils/globalInterface";
  import {usePagePlus} from "@/hooks/usePagePlus";
  import {hasPermission} from "@/utils/permissionUtils";
  const { proxy } = useCurrentInstance();
  const crudRef = ref();
  const permissionList = computed(()=>{
    return {
      addBtn: hasPermission(["tms:tmsMessageNotify:add"]),
      delBtn: hasPermission(["tms:tmsMessageNotify:remove"]),
      editBtn: hasPermission(["tms:tmsMessageNotify:edit"]),
      viewBtn: hasPermission(["tms:tmsMessageNotify:query"]),
    }
  })
  const data = reactive({
    form:<TmsMessageNotifyI>{},
    queryParams:<TmsMessageNotifyI&PageQueryInterface>{},
    page: <PagesInterface>{
      pageSize: 10,
      total: 0,
      currentPage: 1,
    },
    selectionList:[],
  })
  const {queryParams,form,page,selectionList} = toRefs(data);
  const option = ref({
    pageKey: 'TmsMessageNotify',
    rowKey: 'id',
    column: {
                                id: {
          label: '主键ID',
                  addDisplay :true,
        editDisplay : false,
        viewDisplay : false,
        hide : true,
        search : false,
                  },
                                title: {
          label: '消息标题',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                      rules: [
              {
                required: true,
                message: "消息标题不能为空", trigger: "blur" }
            ],                  },
                                content: {
          label: '消息内容',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                      rules: [
              {
                required: true,
                message: "消息内容不能为空", trigger: "blur" }
            ],                  },
                                type: {
          label: '消息类型:0系统通知 1业务通知 2告警 3营销',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                      rules: [
              {
                required: true,
                message: "消息类型:0系统通知 1业务通知 2告警 3营销不能为空", trigger: "change"
                 }
            ],                  },
                                targetUid: {
          label: '接收用户ID',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                  },
                                status: {
          label: '发送状态:0待发送 1成功 2失败',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                      rules: [
              {
                required: true,
                message: "发送状态:0待发送 1成功 2失败不能为空", trigger: "blur" }
            ],                  },
                                readStatus: {
          label: '阅读状态:0未读 1已读',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                      rules: [
              {
                required: true,
                message: "阅读状态:0未读 1已读不能为空", trigger: "blur" }
            ],                  },
                                readTime: {
          label: '读取时间',
                      type: 'date', valueFormat: 'YYYY-MM-DD',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                  },
                                extraData: {
          label: '扩展信息(JSON文本)',
                      type: 'textarea', minRows: 3, maxRows: 5,
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                  },
                                createUid: {
          label: '创建人',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                  },
                                createTime: {
          label: '创建时间',
                      type: 'date', valueFormat: 'YYYY-MM-DD',
                  addDisplay :true,
        editDisplay : false,
        viewDisplay : false,
        hide : true,
        search : false,
                      rules: [
              {
                required: true,
                message: "创建时间不能为空", trigger: "blur" }
            ],                  },
                                updateUid: {
          label: '更新人',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay :true,
        hide :false,
        search :true,
                  },
                                updateTime: {
          label: '更新时间',
                      type: 'date', valueFormat: 'YYYY-MM-DD',
                  addDisplay :true,
        editDisplay :true,
        viewDisplay : false,
        hide : true,
        search : false,
                      rules: [
              {
                required: true,
                message: "更新时间不能为空", trigger: "blur" }
            ]                  },
          }
  })
  const { tableData,pageF,rowSave,rowUpdate,rowDel,beforeOpen,searchChange,
    searchReset,selectionChange,onLoad,currentChange,sizeChange,handleDelete,handleExport,handleUpdate,refreshChange} = usePagePlus({
    form:form,
    option:option,
    queryParams:queryParams,
    idKey:'id',
    page:page.value,
    getListApi:listTmsMessageNotify,
    getDetailApi:getTmsMessageNotify,
    exportApi:exportTmsMessageNotify,
    deleteApi:delTmsMessageNotify,
    addApi:addTmsMessageNotify,
    updateApi:updateTmsMessageNotify,
    handleUpdateFunc:()=>{
      crudRef.value.rowEdit(selectionList.value[0]);
    },
    handleSelectionChangeFunc:(selection:any)=>{
      selectionList.value = selection;
    }
  })
</script>
ui/admin-ui3/src/views/tms/tmsProject/index.vue
@@ -111,12 +111,7 @@
        projectCode: {
          label: '项目编号',
          display: true,
          rules: [
            {
              required: true,
              message: "项目编号不能为空", trigger: "blur"
            }
          ],
        },
        relatedCustomerId: {
          label: '关联客户',
@@ -283,7 +278,7 @@
      column:{
        status: {
          label: '状态',dataType: 'string',
          type: 'radio', dicUrl: '/system/dict/data/type/data_status',
          type: 'radio', dicUrl: '/system/dict/data/type/project_status',
          addDisplay: false,minWidth: 150,
          editDisplay: false,
          viewDisplay: true,
@@ -353,7 +348,7 @@
    },
    status: {
      label: '状态',dataType: 'string',
      type: 'radio', dicUrl: '/system/dict/data/type/data_status',
      type: 'radio', dicUrl: '/system/dict/data/type/project_status',
      display: false,minWidth: 150,
      search: true,
    },
ui/admin-ui3/src/views/tms/tmsSettlementEntity/index.vue
@@ -98,10 +98,24 @@
      label: '基本信息',
      prop: 'jbxx',
      column:{
        customerType: {
          label: '客户类型',
          type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/customer_type',
          addDisplay: true,
        // customerType: {
        //   label: '运营主体类型',
        //   type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/customer_type',
        //   addDisplay: true,
        //   editDisplay: true,
        //   viewDisplay: true,
        //   hide: false,minWidth:150,
        //   search: true,
        //   rules: [
        //     {
        //       required: true,
        //       message: "运营主体类型不能为空", trigger: "change"
        //     }
        //   ],
        // },
        customerCode: {
          label: '运营主体编号',
          addDisplay: false, disabled: true,
          editDisplay: true,
          viewDisplay: true,
          hide: false,minWidth:150,
@@ -109,12 +123,12 @@
          rules: [
            {
              required: true,
              message: "客户类型不能为空", trigger: "change"
              message: "运营主体编号不能为空", trigger: "blur"
            }
          ],
        },
        customerShortName: {
          label: '客户简称',
          label: '运营主体简称',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
@@ -123,26 +137,13 @@
          rules: [
            {
              required: true,
              message: "客户简称不能为空", trigger: "blur"
              message: "运营主体简称不能为空", trigger: "blur"
            }
          ],
        },
        customerCode: {
          label: '客户编号',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          hide: false,minWidth:150,
          search: true,
          rules: [
            {
              required: true,
              message: "客户编号不能为空", trigger: "blur"
            }
          ],
        },
        customerFullName: {
          label: '客户全称',
          label: '运营主体全称',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
@@ -204,42 +205,42 @@
        },
      }
    },
    {
      label: '商务对接信息',
      prop: 'swjdxx',
      column: {
        businessContactName: {
          label: '商务对接人名称',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          hide: false,minWidth:150,labelWidth:140,
          search: true,
          rules: [
            {
              required: true,
              message: "商务对接人名称不能为空", trigger: "blur"
            }
          ],
        },
        businessContactPhone: {
          label: '商务对接人电话',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          hide: true,
          search: false,
        },
        businessContactEmail: {
          label: '商务对接人邮箱',
          addDisplay: true,
          editDisplay: true,
          viewDisplay: true,
          hide: true,
          search: false,
        },
      }
    },
    // {
    //   label: '商务对接信息',
    //   prop: 'swjdxx',
    //   column: {
    //     businessContactName: {
    //       label: '商务对接人名称',
    //       addDisplay: true,
    //       editDisplay: true,
    //       viewDisplay: true,
    //       hide: false,minWidth:150,labelWidth:140,
    //       search: true,
    //       rules: [
    //         {
    //           required: true,
    //           message: "商务对接人名称不能为空", trigger: "blur"
    //         }
    //       ],
    //     },
    //     businessContactPhone: {
    //       label: '商务对接人电话',
    //       addDisplay: true,
    //       editDisplay: true,
    //       viewDisplay: true,
    //       hide: true,
    //       search: false,
    //     },
    //     businessContactEmail: {
    //       label: '商务对接人邮箱',
    //       addDisplay: true,
    //       editDisplay: true,
    //       viewDisplay: true,
    //       hide: true,
    //       search: false,
    //     },
    //   }
    // },
    {
      label: '信用信息',
      prop: 'xyxx',
@@ -248,7 +249,7 @@
      viewDisplay: true,
      column: {
        creditRating: {
          label: '客户信用评级',
          label: '运营主体信用评级',
          type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_credit_rating',
          addDisplay: false,
          editDisplay: false,
@@ -372,20 +373,20 @@
  column: {
    customerType: {
      label: '客户类型',
      label: '运营主体类型',
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/customer_type',
      display: false,
      hide: false,minWidth:150,
      search: true,
    },
    customerShortName: {
      label: '客户简称',
      label: '运营主体简称',
      display: false,
      hide: false,minWidth:150,
      search: true,
    },
    customerCode: {
      label: '客户编号',
      label: '运营主体编号',
      display: false,
      hide: false,minWidth:150,
      search: true,
@@ -396,20 +397,20 @@
      hide: false,minWidth:150,
      search: true,
    },
    businessContactName: {
      label: '商务对接人名称',
      display: false,
      hide: false,minWidth:150,labelWidth:140,
      search: true,
      rules: [
        {
          required: true,
          message: "商务对接人名称不能为空", trigger: "blur"
        }
      ],
    },
    // businessContactName: {
    //   label: '商务对接人名称',
    //   display: false,
    //   hide: false,minWidth:150,labelWidth:140,
    //   search: true,
    //   rules: [
    //     {
    //       required: true,
    //       message: "商务对接人名称不能为空", trigger: "blur"
    //     }
    //   ],
    // },
    creditRating: {
      label: '客户信用评级',
      label: '运营主体信用评级',
      type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/sys_credit_rating',
      display: false,
      hide: false,minWidth:150,
ui/admin-ui3/src/views/tms/tmsVehicle/index.vue
@@ -115,19 +115,26 @@
        carrierType:{
          label: '车辆服务商类型',
          display: true,
          type: 'radio', dataType: 'string', dicUrl: '/system/dict/data/type/carrier_type',
          type: 'radio', dataType: 'string', dicUrl: '/system/dict/data/type/carrier_type',value: '0',
          rules: [
            {
              required: true,
              message: "车辆服务商类型不能为空", trigger: "blur"
            }
          ],
          change: ({value}: any) => {
           option.value.group.forEach((item:any) =>{
             if (item.prop == 'jbxx'){
               item.column.serviceProviderId.display = value == 1
             }
           })
            console.log(value)
          }
        },
        serviceProviderId: {
          label: '车辆服务商',
          addDisplay: true, minWidth: 150,
          editDisplay: true,
          viewDisplay: true,
          minWidth: 150,
          display: true,
          hide: false,
          search: true,
          rules: [
@@ -207,12 +214,7 @@
          editDisplay: true,
          viewDisplay: true,
          type: 'select', dataType: 'string', dicUrl: '/system/dict/data/type/vehicle_type',
          rules: [
            {
              required: true,
              message: "车辆类型不能为空", trigger: "change"
            }
          ],
        },
        internalCode: {
          label: '车辆内部编码',
@@ -299,7 +301,7 @@
          ],
        },
        isTrailer: {
          label: '是否挂车', minWidth: 150, dataType: 'string',
          label: '是否挂车', minWidth: 150, dataType: 'string',value: '1',
          type: 'radio', dicUrl: '/system/dict/data/type/sys_number_is',
          addDisplay: true,
          editDisplay: true,
@@ -326,12 +328,12 @@
          viewDisplay: true,
          hide: false,
          search: false,
          rules: [
            {
              required: true,
              message: "主驾驶员不能为空", trigger: "blur"
            }
          ],
          // rules: [
          //   {
          //     required: true,
          //     message: "主驾驶员不能为空", trigger: "blur"
          //   }
          // ],
        },
        mainDriverPhone: {
          label: '主驾驶员联系方式',