中扬CRM客户关系管理系统
#
Junjie
2023-08-31 bfb6b1df256df56c7a387682ffdf8feb3897bbb1
#
22个文件已添加
2422 ■■■■■ 已修改文件
src/main/java/com/zy/crm/manager/controller/ContractController.java 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/controller/ContractSalesController.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/controller/WordController.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/entity/Contract.java 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/entity/ContractSales.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/mapper/ContractMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/mapper/ContractSalesMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/service/ContractSalesService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/service/ContractService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/service/impl/ContractSalesServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/service/impl/ContractServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/utils/ChineseNumberUtils.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/utils/WordUtils.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/contractTemplate/jcxm_1.docx 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/contractTemplate/upload/lock 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/contractTemplate/zjxm_1.docx 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ContractMapper.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ContractSalesMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/contract/contract.js 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/contractSales/contractSales.js 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/contract/contract.html 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/contractSales/contractSales.html 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/crm/manager/controller/ContractController.java
New file
@@ -0,0 +1,296 @@
package com.zy.crm.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.common.DateUtils;
import com.zy.crm.common.service.OssService;
import com.zy.crm.manager.entity.Contract;
import com.zy.crm.manager.entity.ContractSales;
import com.zy.crm.manager.entity.PriOnline;
import com.zy.crm.manager.service.ContractSalesService;
import com.zy.crm.manager.service.ContractService;
import com.core.annotations.ManagerAuth;
import com.core.common.BaseRes;
import com.core.common.Cools;
import com.core.common.R;
import com.core.domain.KeyValueVo;
import com.zy.crm.common.web.BaseController;
import com.zy.crm.manager.utils.ChineseNumberUtils;
import com.zy.crm.manager.utils.WordUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
public class ContractController extends BaseController {
    @Autowired
    private ContractService contractService;
    @Autowired
    private ContractSalesService contractSalesService;
    @Autowired
    private OssService ossService;
    @RequestMapping(value = "/contract/{id}/auth")
    @ManagerAuth
    public R get(@PathVariable("id") String id) {
        return R.ok(contractService.selectById(String.valueOf(id)));
    }
    @RequestMapping(value = "/contract/list/auth")
    @ManagerAuth
    public R list(@RequestParam(defaultValue = "1")Integer curr,
                  @RequestParam(defaultValue = "10")Integer limit,
                  @RequestParam(required = false)String orderByField,
                  @RequestParam(required = false)String orderByType,
                  @RequestParam(required = false)String condition,
                  @RequestParam Map<String, Object> param){
        EntityWrapper<Contract> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        convert(param, wrapper);
        allLike(Contract.class, param.keySet(), wrapper, condition);
        if (!Cools.isEmpty(orderByField)){wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));}
        return R.ok(contractService.selectPage(new Page<>(curr, limit), wrapper));
    }
    private <T> void convert(Map<String, Object> map, EntityWrapper<T> wrapper){
        for (Map.Entry<String, Object> entry : map.entrySet()){
            String val = String.valueOf(entry.getValue());
            if (val.contains(RANGE_TIME_LINK)){
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else {
                wrapper.like(entry.getKey(), val);
            }
        }
    }
    @RequestMapping(value = "/contract/add/auth")
    @ManagerAuth
    public R add(Contract contract) {
        contract.setCreateBy(getUserId());
        contract.setCreateTime(new Date());
        contractService.insert(contract);
        return R.ok();
    }
    @RequestMapping(value = "/contract/update/auth")
    @ManagerAuth
    public R update(Contract contract){
        if (Cools.isEmpty(contract) || null==contract.getId()){
            return R.error();
        }
        contract.setUpdateBy(getUserId());
        contract.setUpdateTime(new Date());
        contractService.updateById(contract);
        return R.ok();
    }
    @RequestMapping(value = "/contract/delete/auth")
    @ManagerAuth
    public R delete(@RequestParam(value="ids[]") Long[] ids){
         for (Long id : ids){
            contractService.deleteById(id);
        }
        return R.ok();
    }
    @RequestMapping(value = "/contract/generate/auth")
    @ManagerAuth
    public ResponseEntity<InputStreamResource> generate(@RequestParam Integer id,
                                                        @RequestParam String contractTemplate){
        try {
            /////////////////////////生成合同数据/////////////////////////
            Contract contract = contractService.selectById(id);
            if (contract == null) {
                return null;
            }
            HashMap<String, Object> map = new HashMap<>();
            map.put("{{customer}}", contract.getCustomer());
            map.put("{{address}}", contract.getAddress());
            map.put("{{company}}", contract.getCompany());
            map.put("{{companyAddress}}", contract.getCompanyAddress());
            map.put("{{taxNum}}", contract.getTaxNum());
            map.put("{{bank}}", contract.getBank());
            map.put("{{bankNum}}", contract.getBankNum());
            map.put("{{city}}", contract.getCity());
            map.put("{{shippingAddress}}", contract.getShippingAddress());
            map.put("{{shippingName}}", contract.getShippingName());
            map.put("{{shippingPhone}}", contract.getShippingPhone());
            map.put("{{email}}", contract.getEmail());
            map.put("{{boss}}", contract.getBoss());
            map.put("{{priceChinese}}", ChineseNumberUtils.numberToChinese(contract.getPrice()));
            map.put("{{priceSci}}", WordUtils.formatNumberForAccounting(contract.getPrice()));
            SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");
            map.put("{{now}}", format.format(new Date()));
            /////////////////////////生成合同数据/////////////////////////
            /////////////////////////生成Tab表格数据/////////////////////////
            List<ContractSales> contractSales = contractSalesService.selectByContractId(contract.getId());
            List<List<String>> tabParam = new ArrayList<>();
            ArrayList<String> tabTitle = new ArrayList<>();
            tabTitle.add("序号");
            tabTitle.add("品名");
            tabTitle.add("数量");
            tabTitle.add("单位");
            tabTitle.add("单价(元)");
            tabTitle.add("合计(元)");
            tabTitle.add("备注");
            tabParam.add(tabTitle);
            int idx = 1;//tab序号
            double totalPrice = 0D;
            for (ContractSales contractSale : contractSales) {
                ArrayList<String> list = new ArrayList<>();
                list.add(String.valueOf(idx));
                list.add(contractSale.getName());
                list.add(String.valueOf(contractSale.getNum()));
                list.add(contractSale.getUnit());
                list.add(WordUtils.formatNumberForAccounting(contractSale.getUnitPrice()));
                list.add(WordUtils.formatNumberForAccounting(contractSale.getTotalPrice()));
                list.add(contractSale.getMemo());
                tabParam.add(list);
                totalPrice += contractSale.getTotalPrice();
                idx++;//序号+1
            }
            //tab最后合计栏
            ArrayList<String> tabFooter = new ArrayList<>();
            tabFooter.add("合计(大写金额):" + ChineseNumberUtils.numberToChinese(totalPrice));
            tabFooter.add("");//合并单元格
            tabFooter.add("");//合并单元格
            tabFooter.add("");//合并单元格
            tabFooter.add("合计:" + WordUtils.formatNumberForAccounting(totalPrice));
            tabFooter.add("");//合并单元格
            tabFooter.add("");//合并单元格
            tabParam.add(tabFooter);
            /////////////////////////生成Tab表格数据/////////////////////////
            String fileName = this.getClass().getClassLoader().getResource("contractTemplate/" + contractTemplate + ".docx").getPath();//获取文件路径
            return WordUtils.generate(fileName, map, tabParam);
        } catch (Exception e) {
            return null;
        }
    }
    @RequestMapping(value = "/contract/upload/auth")
    @ManagerAuth
    public R upload(@RequestParam("id") Integer id,
                         @RequestParam("file") MultipartFile[] files) throws IOException {
        Contract contract = contractService.selectById(id);
        if (contract == null) {
            return R.error();
        }
        MultipartFile file = files[0];
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String path =  ClassUtils.getDefaultClassLoader().getResource("contractTemplate/upload").getPath();
        //文件后缀名
        String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        //上传文件名
        String filename = format.format(new Date()) + suffix;
        //最终文件路径
        String filepath = path + "/" + filename;
        //OSS文件存储路径
        String ossPath = "contract/" + filename;
        //服务器端保存的文件对象
        File serverFile = new File(filepath);
        if(!serverFile.exists()) {
            try {
                //创建文件
                serverFile.createNewFile();
                //将上传的文件写入到服务器端文件内
                file.transferTo(serverFile);
                //上传至OSS
                ossService.uploadFile(ossPath, serverFile);
                contract.setFilepath(ossPath);
                contractService.updateById(contract);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return R.ok();
    }
    @RequestMapping(value = "/contract/download/auth")
    @ManagerAuth
    public ResponseEntity<InputStreamResource> download(@RequestParam("id") Integer id) {
        Contract contract = contractService.selectById(id);
        if (contract == null) {
            return null;
        }
        if (Cools.isEmpty(contract.getFilepath())) {
            return null;
        }
        try {
            return ossService.downloadFile(contract.getFilepath());//从OSS中下载文件
        } catch (Exception e) {
            return null;
        }
    }
    @RequestMapping(value = "/contract/export/auth")
    @ManagerAuth
    public R export(@RequestBody JSONObject param){
        EntityWrapper<Contract> wrapper = new EntityWrapper<>();
        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
        Map<String, Object> map = excludeTrash(param.getJSONObject("contract"));
        convert(map, wrapper);
        List<Contract> list = contractService.selectList(wrapper);
        return R.ok(exportSupport(list, fields));
    }
    @RequestMapping(value = "/contractQuery/auth")
    @ManagerAuth
    public R query(String condition) {
        EntityWrapper<Contract> wrapper = new EntityWrapper<>();
        wrapper.like("id", condition);
        Page<Contract> page = contractService.selectPage(new Page<>(0, 10), wrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (Contract contract : page.getRecords()){
            Map<String, Object> map = new HashMap<>();
            map.put("id", contract.getId());
            map.put("value", contract.getId());
            result.add(map);
        }
        return R.ok(result);
    }
    @RequestMapping(value = "/contract/check/column/auth")
    @ManagerAuth
    public R query(@RequestBody JSONObject param) {
        Wrapper<Contract> wrapper = new EntityWrapper<Contract>().eq(humpToLine(String.valueOf(param.get("key"))), param.get("val"));
        if (null != contractService.selectOne(wrapper)){
            return R.parse(BaseRes.REPEAT).add(getComment(Contract.class, String.valueOf(param.get("key"))));
        }
        return R.ok();
    }
    @RequestMapping("/contract/all/get/kv")
    @ManagerAuth
    public R getDataKV(@RequestParam(required = false) String condition) {
        List<KeyValueVo> vos = new ArrayList<>();
        Wrapper<Contract> wrapper = new EntityWrapper<Contract>().andNew().like("id", condition).orderBy("create_time", false);
        contractService.selectPage(new Page<>(1, 30), wrapper).getRecords().forEach(item -> vos.add(new KeyValueVo(String.valueOf(item.getId()), item.getId())));
        return R.ok().add(vos);
    }
}
src/main/java/com/zy/crm/manager/controller/ContractSalesController.java
New file
@@ -0,0 +1,135 @@
package com.zy.crm.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.common.DateUtils;
import com.zy.crm.manager.entity.ContractSales;
import com.zy.crm.manager.service.ContractSalesService;
import com.core.annotations.ManagerAuth;
import com.core.common.BaseRes;
import com.core.common.Cools;
import com.core.common.R;
import com.core.domain.KeyValueVo;
import com.zy.crm.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
public class ContractSalesController extends BaseController {
    @Autowired
    private ContractSalesService contractSalesService;
    @RequestMapping(value = "/contractSales/{id}/auth")
    @ManagerAuth
    public R get(@PathVariable("id") String id) {
        return R.ok(contractSalesService.selectById(String.valueOf(id)));
    }
    @RequestMapping(value = "/contractSales/list/auth")
    @ManagerAuth
    public R list(@RequestParam(defaultValue = "1")Integer curr,
                  @RequestParam(defaultValue = "10")Integer limit,
                  @RequestParam(required = false)String orderByField,
                  @RequestParam(required = false)String orderByType,
                  @RequestParam(required = false)String condition,
                  @RequestParam Map<String, Object> param){
        EntityWrapper<ContractSales> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        convert(param, wrapper);
        allLike(ContractSales.class, param.keySet(), wrapper, condition);
        if (!Cools.isEmpty(orderByField)){wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));}
        return R.ok(contractSalesService.selectPage(new Page<>(curr, limit), wrapper));
    }
    private <T> void convert(Map<String, Object> map, EntityWrapper<T> wrapper){
        for (Map.Entry<String, Object> entry : map.entrySet()){
            String val = String.valueOf(entry.getValue());
            if (val.contains(RANGE_TIME_LINK)){
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else {
                wrapper.like(entry.getKey(), val);
            }
        }
    }
    @RequestMapping(value = "/contractSales/add/auth")
    @ManagerAuth
    public R add(ContractSales contractSales) {
        contractSalesService.insert(contractSales);
        return R.ok();
    }
    @RequestMapping(value = "/contractSales/update/auth")
    @ManagerAuth
    public R update(ContractSales contractSales){
        if (Cools.isEmpty(contractSales) || null==contractSales.getId()){
            return R.error();
        }
        contractSalesService.updateById(contractSales);
        return R.ok();
    }
    @RequestMapping(value = "/contractSales/delete/auth")
    @ManagerAuth
    public R delete(@RequestParam(value="ids[]") Long[] ids){
         for (Long id : ids){
            contractSalesService.deleteById(id);
        }
        return R.ok();
    }
    @RequestMapping(value = "/contractSales/export/auth")
    @ManagerAuth
    public R export(@RequestBody JSONObject param){
        EntityWrapper<ContractSales> wrapper = new EntityWrapper<>();
        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
        Map<String, Object> map = excludeTrash(param.getJSONObject("contractSales"));
        convert(map, wrapper);
        List<ContractSales> list = contractSalesService.selectList(wrapper);
        return R.ok(exportSupport(list, fields));
    }
    @RequestMapping(value = "/contractSalesQuery/auth")
    @ManagerAuth
    public R query(String condition) {
        EntityWrapper<ContractSales> wrapper = new EntityWrapper<>();
        wrapper.like("id", condition);
        Page<ContractSales> page = contractSalesService.selectPage(new Page<>(0, 10), wrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (ContractSales contractSales : page.getRecords()){
            Map<String, Object> map = new HashMap<>();
            map.put("id", contractSales.getId());
            map.put("value", contractSales.getId());
            result.add(map);
        }
        return R.ok(result);
    }
    @RequestMapping(value = "/contractSales/check/column/auth")
    @ManagerAuth
    public R query(@RequestBody JSONObject param) {
        Wrapper<ContractSales> wrapper = new EntityWrapper<ContractSales>().eq(humpToLine(String.valueOf(param.get("key"))), param.get("val"));
        if (null != contractSalesService.selectOne(wrapper)){
            return R.parse(BaseRes.REPEAT).add(getComment(ContractSales.class, String.valueOf(param.get("key"))));
        }
        return R.ok();
    }
    @RequestMapping("/contractSales/all/get/kv")
    @ManagerAuth
    public R getDataKV(@RequestParam(required = false) String condition) {
        List<KeyValueVo> vos = new ArrayList<>();
        Wrapper<ContractSales> wrapper = new EntityWrapper<ContractSales>().andNew().like("id", condition).orderBy("create_time", false);
        contractSalesService.selectPage(new Page<>(1, 30), wrapper).getRecords().forEach(item -> vos.add(new KeyValueVo(String.valueOf(item.getId()), item.getId())));
        return R.ok().add(vos);
    }
}
src/main/java/com/zy/crm/manager/controller/WordController.java
New file
@@ -0,0 +1,155 @@
package com.zy.crm.manager.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class WordController {
    @GetMapping("/generateWord")
    public ResponseEntity<InputStreamResource> generateWord() throws IOException {
        // 读取 Word 模板文件
        String fileName = this.getClass().getClassLoader().getResource("word1.docx").getPath();//获取文件路径
        File file = new File(fileName);
        FileInputStream inputStream = new FileInputStream(file);
        XWPFDocument document = new XWPFDocument(inputStream);
        insertTab("${table}", document);
        HashMap<String, Object> map = new HashMap<>();
        map.put("${table}", "");
        map.put("${name}", "张三");
        processParagraphs(document.getParagraphs(), map, document);
        // 将文档写入输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        document.write(outputStream);
        outputStream.close();
        // 创建响应实体,将输出流作为文件内容返回
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", "output.docx");
        InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(outputStream.toByteArray()));
        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }
    /**
     * 处理段落中文本,替换文本中定义的变量;
     * @param paragraphList 段落列表
     * @param param 需要替换的变量及变量值
     * @param doc 需要替换的DOC
     */
    public static void processParagraphs(List<XWPFParagraph> paragraphList, HashMap<String, Object> param, XWPFDocument doc) {
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    String text = run.getText(0);
                    if (text != null) {
                        boolean isSetText = false;
                        for (Map.Entry<String, Object> entry : param.entrySet()) {
                            String key = entry.getKey();
                            if (text.indexOf(key) != -1) {
                                isSetText = true;
                                Object value = entry.getValue();
                                if (value instanceof String) {// 文本替换
                                    text = text.replace(key, value.toString());
                                }
                            }
                        }
                        if (isSetText) {
                            run.setText(text, 0);
                        }
                    }
                }
            }
        }
    }
    /**
     * 在定位的位置插入表格
     */
    public static void insertTab(String key, XWPFDocument doc2) {
        List<XWPFParagraph> paragraphList = doc2.getParagraphs();
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    String text = run.getText(0);
                    if (text != null) {
                        if (text.indexOf(key) >= 0) {
                            XmlCursor cursor = paragraph.getCTP().newCursor();
                            XWPFTable tableOne = doc2.insertNewTbl(cursor);// ---这个是关键
                            String test = "[\n" +
                                    "  [\"序号\",\"品名\",\"数量\",\"单位\",\"单价(元)\",\"合计(元)\",\"备注\"],\n" +
                                    "  [\"1\",\"自动化立体仓库设备\",\"1\",\"套\",\"130000\",\"130000\",\"备注\"],\n" +
                                    "  [\"合计(大写金额):\",\"\",\"\",\"\",\"合计:\",\"\",\"\"]\n" +
                                    "]";
                            JSONArray row = JSON.parseArray(test);
                            ArrayList<List<String>> list = new ArrayList<>();
                            for (Object o : row) {
                                ArrayList<String> arrayList = new ArrayList<>();
                                JSONArray objects = JSON.parseArray(o.toString());
                                for (Object object : objects) {
                                    arrayList.add(object.toString());
                                }
                                list.add(arrayList);
                            }
                            int rows = list.size();
                            int cols = list.get(0).size();
                            for (int i = 0; i < rows; i++) {
                                XWPFTableRow tableRow = null;
                                if (i == 0) {
                                    tableRow = tableOne.getRow(i);
                                }else {
                                    tableRow = tableOne.createRow();
                                }
                                for (int j = 0; j < cols; j++) {
                                    if (i == 0 && j == 0) {
                                        tableRow.getCell(j).setText(list.get(i).get(j));
                                    }else {
                                        XWPFTableCell cell = tableRow.getCell(j);
                                        if (cell == null) {
                                            tableRow.addNewTableCell().setText(list.get(i).get(j));
                                        }else {
                                            cell.setText(list.get(i).get(j));
                                        }
                                    }
                                }
                            }
                            //合并最后一行单元格
                            tableOne.getRow(rows - 1).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                            tableOne.getRow(rows - 1).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                            tableOne.getRow(rows - 1).getCell(2).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                            tableOne.getRow(rows - 1).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                            tableOne.getRow(rows - 1).getCell(5).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                        }
                    }
                }
            }
        }
    }
}
src/main/java/com/zy/crm/manager/entity/Contract.java
New file
@@ -0,0 +1,271 @@
package com.zy.crm.manager.entity;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.annotations.TableField;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.core.common.SpringUtils;
import com.zy.crm.system.entity.User;
import com.zy.crm.system.service.UserService;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.baomidou.mybatisplus.annotations.TableName;
import java.io.Serializable;
@Data
@TableName("man_contract")
public class Contract implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value= "")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 甲方
     */
    @ApiModelProperty(value= "甲方")
    private String customer;
    /**
     * 甲方地址
     */
    @ApiModelProperty(value= "甲方地址")
    private String address;
    /**
     * 公司名称
     */
    @ApiModelProperty(value= "公司名称")
    private String company;
    /**
     * 公司地址
     */
    @ApiModelProperty(value= "公司地址")
    @TableField("company_address")
    private String companyAddress;
    /**
     * 公司税号
     */
    @ApiModelProperty(value= "公司税号")
    @TableField("tax_num")
    private String taxNum;
    /**
     * 开户银行
     */
    @ApiModelProperty(value= "开户银行")
    private String bank;
    /**
     * 银行账号
     */
    @ApiModelProperty(value= "银行账号")
    @TableField("bank_num")
    private String bankNum;
    /**
     * 创建时间
     */
    @ApiModelProperty(value= "创建时间")
    @TableField("create_time")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 更新时间
     */
    @ApiModelProperty(value= "更新时间")
    @TableField("update_time")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 创建人员
     */
    @ApiModelProperty(value= "创建人员")
    @TableField("create_by")
    private Long createBy;
    /**
     * 更新人员
     */
    @ApiModelProperty(value= "更新人员")
    @TableField("update_by")
    private Long updateBy;
    /**
     * 上传合同地址
     */
    @ApiModelProperty(value= "上传合同地址")
    private String filepath;
    /**
     * 城市
     */
    @ApiModelProperty(value= "城市")
    private String city;
    /**
     * 收货地址
     */
    @ApiModelProperty(value= "收货地址")
    @TableField("shipping_address")
    private String shippingAddress;
    /**
     * 收货人
     */
    @ApiModelProperty(value= "收货人")
    @TableField("shipping_name")
    private String shippingName;
    /**
     * 收货人电话
     */
    @ApiModelProperty(value= "收货人电话")
    @TableField("shipping_phone")
    private String shippingPhone;
    /**
     * 合同总金额
     */
    @ApiModelProperty(value= "合同总金额")
    private Double price;
    /**
     * 电子邮箱
     */
    @ApiModelProperty(value= "电子邮箱")
    private String email;
    /**
     * 合同名称
     */
    @ApiModelProperty(value= "合同名称")
    private String name;
    /**
     * 合同状态{1:正常,0:禁止}
     */
    @ApiModelProperty(value= "合同状态{1:正常,0:禁止}")
    private Integer status;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    /**
     * 法人或授权代表
     */
    @ApiModelProperty(value= "法人或授权代表")
    private String boss;
    public Contract() {}
    public Contract(Long id, String customer, String address, String company, String companyAddress, String taxNum, String bank, String bankNum, Date createTime, Date updateTime, Long createBy, Long updateBy, String filepath, String city, String shippingAddress, String shippingName, String shippingPhone, Double price, String email, String name, Integer status, String memo, String boss) {
        this.id = id;
        this.customer = customer;
        this.address = address;
        this.company = company;
        this.companyAddress = companyAddress;
        this.taxNum = taxNum;
        this.bank = bank;
        this.bankNum = bankNum;
        this.createTime = createTime;
        this.updateTime = updateTime;
        this.createBy = createBy;
        this.updateBy = updateBy;
        this.filepath = filepath;
        this.city = city;
        this.shippingAddress = shippingAddress;
        this.shippingName = shippingName;
        this.shippingPhone = shippingPhone;
        this.price = price;
        this.email = email;
        this.name = name;
        this.status = status;
        this.memo = memo;
        this.boss = boss;
    }
    //    Contract contract = new Contract(
//            null,    // [非空]
//            null,    // 甲方
//            null,    // 甲方地址
//            null,    // 公司名称
//            null,    // 公司地址
//            null,    // 公司税号
//            null,    // 开户银行
//            null,    // 银行账号
//            null,    // 创建时间
//            null,    // 更新时间
//            null,    // 创建人员
//            null,    // 更新人员
//            null,    // 上传合同地址
//            null,    // 城市
//            null,    // 收货地址
//            null,    // 收货人
//            null,    // 收货人电话
//            null,    // 合同总金额
//            null    // 电子邮箱
//    );
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public String getCreateBy$() {
        UserService userService = SpringUtils.getBean(UserService.class);
        User user = userService.selectById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateBy$() {
        UserService userService = SpringUtils.getBean(UserService.class);
        User user = userService.selectById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getStatus$() {
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "禁用";
            default:
                return String.valueOf(this.status);
        }
    }
}
src/main/java/com/zy/crm/manager/entity/ContractSales.java
New file
@@ -0,0 +1,89 @@
package com.zy.crm.manager.entity;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.annotations.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.baomidou.mybatisplus.annotations.TableName;
import java.io.Serializable;
@Data
@TableName("man_contract_sales")
public class ContractSales implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value= "")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 品名
     */
    @ApiModelProperty(value= "品名")
    private String name;
    /**
     * 数量
     */
    @ApiModelProperty(value= "数量")
    private Double num;
    /**
     * 单位
     */
    @ApiModelProperty(value= "单位")
    private String unit;
    /**
     * 单价
     */
    @ApiModelProperty(value= "单价")
    @TableField("unit_price")
    private Double unitPrice;
    /**
     * 总价
     */
    @ApiModelProperty(value= "总价")
    @TableField("total_price")
    private Double totalPrice;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    /**
     * 合同ID
     */
    @ApiModelProperty(value= "合同ID")
    private Long contractId;
    public ContractSales() {}
    public ContractSales(Long id, String name, Double num, String unit, Double unitPrice, Double totalPrice, String memo, Long contractId) {
        this.id = id;
        this.name = name;
        this.num = num;
        this.unit = unit;
        this.unitPrice = unitPrice;
        this.totalPrice = totalPrice;
        this.memo = memo;
        this.contractId = contractId;
    }
    //    ContractSales contractSales = new ContractSales(
//            null,    // 品名
//            null,    // 数量
//            null,    // 单位
//            null,    // 单价
//            null,    // 总价
//            null    // 备注
//    );
}
src/main/java/com/zy/crm/manager/mapper/ContractMapper.java
New file
@@ -0,0 +1,12 @@
package com.zy.crm.manager.mapper;
import com.zy.crm.manager.entity.Contract;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface ContractMapper extends BaseMapper<Contract> {
}
src/main/java/com/zy/crm/manager/mapper/ContractSalesMapper.java
New file
@@ -0,0 +1,16 @@
package com.zy.crm.manager.mapper;
import com.zy.crm.manager.entity.ContractSales;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface ContractSalesMapper extends BaseMapper<ContractSales> {
    List<ContractSales> selectByContractId(Long contractId);
}
src/main/java/com/zy/crm/manager/service/ContractSalesService.java
New file
@@ -0,0 +1,12 @@
package com.zy.crm.manager.service;
import com.zy.crm.manager.entity.ContractSales;
import com.baomidou.mybatisplus.service.IService;
import java.util.List;
public interface ContractSalesService extends IService<ContractSales> {
    List<ContractSales> selectByContractId(Long contractId);
}
src/main/java/com/zy/crm/manager/service/ContractService.java
New file
@@ -0,0 +1,8 @@
package com.zy.crm.manager.service;
import com.zy.crm.manager.entity.Contract;
import com.baomidou.mybatisplus.service.IService;
public interface ContractService extends IService<Contract> {
}
src/main/java/com/zy/crm/manager/service/impl/ContractSalesServiceImpl.java
New file
@@ -0,0 +1,18 @@
package com.zy.crm.manager.service.impl;
import com.zy.crm.manager.mapper.ContractSalesMapper;
import com.zy.crm.manager.entity.ContractSales;
import com.zy.crm.manager.service.ContractSalesService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("contractSalesService")
public class ContractSalesServiceImpl extends ServiceImpl<ContractSalesMapper, ContractSales> implements ContractSalesService {
    @Override
    public List<ContractSales> selectByContractId(Long contractId) {
        return this.baseMapper.selectByContractId(contractId);
    }
}
src/main/java/com/zy/crm/manager/service/impl/ContractServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.zy.crm.manager.service.impl;
import com.zy.crm.manager.mapper.ContractMapper;
import com.zy.crm.manager.entity.Contract;
import com.zy.crm.manager.service.ContractService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("contractService")
public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> implements ContractService {
}
src/main/java/com/zy/crm/manager/utils/ChineseNumberUtils.java
New file
@@ -0,0 +1,111 @@
package com.zy.crm.manager.utils;
//数字中文大写工具类
public class ChineseNumberUtils {
    private static final String[] CN_NUMERIC = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
    private static final String[] CN_CARRY = {"", "拾", "佰", "仟"};
    private static final String[] CN_UNITS = {"", "万", "亿", "兆"};
    private static final String CN_FULL = "整";
    private static final String CN_NEGATIVE = "负";
    public static String numberToChinese(double number) {
        if (number == 0) {
            return CN_NUMERIC[0];
        }
        long temp = (long) (number * 100);
        int numFen = (int) (temp % 10);
        temp = temp / 10;
        int numJiao = (int) (temp % 10);
        temp = temp / 10;
        int[] parts = new int[20];
        int numParts = 0;
        for (int i = 0; temp != 0; i++) {
            int partNum = (int) (temp % 10000);
            parts[i] = partNum;
            temp = temp / 10000;
            numParts++;
        }
        boolean lastIsZero = true;
        String chineseStr = "";
        for (int i = 0; i < numParts; i++) {
            if (parts[i] == 0) {
                lastIsZero = true;
                if (i == numParts - 1) {
                    chineseStr = CN_UNITS[i] + chineseStr;
                }
                continue;
            }
            if (lastIsZero) {
                chineseStr = CN_NUMERIC[0] + chineseStr;
            }
            String partChinese = partTranslate(parts[i]);
            partChinese += CN_UNITS[i];
            chineseStr = partChinese + chineseStr;
            lastIsZero = false;
        }
        if (numFen == 0 && numJiao == 0) {
            chineseStr += CN_FULL;
        } else if (numFen == 0) {
            chineseStr += CN_NUMERIC[numJiao] + "角" + CN_FULL;
        } else {
            if (lastIsZero) {
                chineseStr += CN_NUMERIC[0];
            }
            chineseStr += CN_NUMERIC[numJiao] + "角" + CN_NUMERIC[numFen] + "分";
        }
        return chineseStr;
    }
    private static String partTranslate(int partNum) {
        if (partNum < 0 || partNum > 10000) {
            return "";
        }
        String[] chNum = new String[4];
        int unitPos = 0;
        boolean zero = true;
        while (partNum > 0) {
            int num = partNum % 10;
            if (num == 0) {
                if (!zero) {
                    zero = true;
                    chNum[unitPos++] = CN_NUMERIC[num];
                }
            } else {
                zero = false;
                chNum[unitPos++] = CN_NUMERIC[num] + CN_CARRY[unitPos - 1];
            }
            partNum = partNum / 10;
        }
        StringBuilder strBuf = new StringBuilder();
        boolean hasValue = false;
        for (int i = 0; i < 4; i++) {
            if (chNum[i] != null) {
                if (i > 0 && !hasValue) {
                    strBuf.insert(0, CN_NUMERIC[0]);
                }
                strBuf.insert(0, chNum[i]);
                hasValue = true;
            }
        }
        return strBuf.toString();
    }
    public static void main(String[] args) {
        double number = 123456789.23;
        String chineseNumber = numberToChinese(number);
        System.out.println(chineseNumber);
    }
}
src/main/java/com/zy/crm/manager/utils/WordUtils.java
New file
@@ -0,0 +1,175 @@
package com.zy.crm.manager.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.io.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//Word操作工具类
public class WordUtils {
    //生成word合同
    public static ResponseEntity<InputStreamResource> generate(String fileName, HashMap<String, Object> map, List<List<String>> tabParam) throws IOException {
        // 读取 Word 模板文件
        File file = new File(fileName);
        FileInputStream inputStream = new FileInputStream(file);
        XWPFDocument document = new XWPFDocument(inputStream);
        insertTab("{{table}}", document, tabParam);
        map.put("{{table}}", "");
        processParagraphs(map, document);
        // 将文档写入输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        document.write(outputStream);
        outputStream.close();
        // 创建响应实体,将输出流作为文件内容返回
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", "output.docx");
        InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(outputStream.toByteArray()));
        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }
    /**
     * 处理段落中文本,替换文本中定义的变量;
     * @param param 需要替换的变量及变量值
     * @param document 需要替换的DOC
     */
    public static void processParagraphs(HashMap<String, Object> param, XWPFDocument document) {
        // 遍历所有段落
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                // 替换段落文本
                replaceTextInParagraph(paragraph, key, value);
            }
        }
        // 遍历所有表格
        for (XWPFTable table : document.getTables()) {
            // 遍历表格中的每个单元格
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (Map.Entry<String, Object> entry : param.entrySet()) {
                        String key = entry.getKey();
                        String value = entry.getValue().toString();
                        // 替换表格单元格的文本
                        replaceTextInParagraph(cell.getParagraphs().get(0), key, value);
                    }
                }
            }
        }
    }
    private static void replaceTextInParagraph(XWPFParagraph paragraph, String searchString, String replacementString) {
        for (XWPFRun run : paragraph.getRuns()) {
            // 获取段落中的文本
            String text = run.getText(0);
            if (text != null && text.contains(searchString)) {
                // 使用正则表达式进行替换
                Pattern pattern = Pattern.compile(Pattern.quote(searchString));
                Matcher matcher = pattern.matcher(text);
                if (matcher.find()) {
                    String replacedText = matcher.replaceAll(replacementString);
                    // 替换文本
                    run.setText(replacedText, 0);
                }
            }
        }
    }
    /**
     * 在定位的位置插入表格
     */
    public static void insertTab(String key, XWPFDocument document, List<List<String>> param) {
        List<XWPFParagraph> paragraphList = document.getParagraphs();
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    String text = run.getText(0);
                    if (text != null) {
                        if (text.indexOf(key) >= 0) {
                            XmlCursor cursor = paragraph.getCTP().newCursor();
                            XWPFTable tableOne = document.insertNewTbl(cursor);// ---这个是关键
//                            String test = "[\n" +
//                                    "  [\"序号\",\"品名\",\"数量\",\"单位\",\"单价(元)\",\"合计(元)\",\"备注\"],\n" +
//                                    "  [\"1\",\"自动化立体仓库设备\",\"1\",\"套\",\"130000\",\"130000\",\"备注\"],\n" +
//                                    "  [\"合计(大写金额):\",\"\",\"\",\"\",\"合计:\",\"\",\"\"]\n" +
//                                    "]";
//
//                            JSONArray row = JSON.parseArray(test);
//                            ArrayList<List<String>> list = new ArrayList<>();
//                            for (Object o : row) {
//                                ArrayList<String> arrayList = new ArrayList<>();
//                                JSONArray objects = JSON.parseArray(o.toString());
//                                for (Object object : objects) {
//                                    arrayList.add(object.toString());
//                                }
//                                list.add(arrayList);
//                            }
                            int rows = param.size();
                            int cols = param.get(0).size();
                            for (int i = 0; i < rows; i++) {
                                XWPFTableRow tableRow = null;
                                if (i == 0) {
                                    tableRow = tableOne.getRow(i);
                                } else {
                                    tableRow = tableOne.createRow();
                                }
                                for (int j = 0; j < cols; j++) {
                                    if (i == 0 && j == 0) {
                                        tableRow.getCell(j).setText(param.get(i).get(j));
                                    } else {
                                        XWPFTableCell cell = tableRow.getCell(j);
                                        if (cell == null) {
                                            tableRow.addNewTableCell().setText(param.get(i).get(j));
                                        } else {
                                            cell.setText(param.get(i).get(j));
                                        }
                                    }
                                }
                            }
                            //合并最后一行单元格
                            tableOne.getRow(rows - 1).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                            tableOne.getRow(rows - 1).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                            tableOne.getRow(rows - 1).getCell(2).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                            tableOne.getRow(rows - 1).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                            tableOne.getRow(rows - 1).getCell(5).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                        }
                    }
                }
            }
        }
    }
    public static String formatNumberForAccounting(double number) {
        NumberFormat formatter = new DecimalFormat("#,###.00");
        return formatter.format(number);
    }
}
src/main/resources/contractTemplate/jcxm_1.docx
Binary files differ
src/main/resources/contractTemplate/upload/lock
src/main/resources/contractTemplate/zjxm_1.docx
Binary files differ
src/main/resources/mapper/ContractMapper.xml
New file
@@ -0,0 +1,33 @@
<?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.zy.crm.manager.mapper.ContractMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zy.crm.manager.entity.Contract">
        <result column="id" property="id" />
        <result column="customer" property="customer" />
        <result column="address" property="address" />
        <result column="company" property="company" />
        <result column="company_address" property="companyAddress" />
        <result column="tax_num" property="taxNum" />
        <result column="bank" property="bank" />
        <result column="bank_num" property="bankNum" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="create_by" property="createBy" />
        <result column="update_by" property="updateBy" />
        <result column="filepath" property="filepath" />
        <result column="city" property="city" />
        <result column="shipping_address" property="shippingAddress" />
        <result column="shipping_name" property="shippingName" />
        <result column="shipping_phone" property="shippingPhone" />
        <result column="price" property="price" />
        <result column="email" property="email" />
        <result column="name" property="name" />
        <result column="status" property="status" />
        <result column="memo" property="memo" />
        <result column="boss" property="boss" />
    </resultMap>
</mapper>
src/main/resources/mapper/ContractSalesMapper.xml
New file
@@ -0,0 +1,23 @@
<?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.zy.crm.manager.mapper.ContractSalesMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zy.crm.manager.entity.ContractSales">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="num" property="num" />
        <result column="unit" property="unit" />
        <result column="unit_price" property="unitPrice" />
        <result column="total_price" property="totalPrice" />
        <result column="memo" property="memo" />
        <result column="contract_id" property="contractId" />
    </resultMap>
    <select id="selectByContractId" resultMap="BaseResultMap">
        select * from man_contract_sales
        where contract_id = #{contractId}
    </select>
</mapper>
src/main/webapp/static/js/contract/contract.js
New file
@@ -0,0 +1,431 @@
var pageCurr;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).use(['table','laydate', 'form', 'admin'], function(){
    var table = layui.table;
    var $ = layui.jquery;
    var layer = layui.layer;
    var layDate = layui.laydate;
    var form = layui.form;
    var admin = layui.admin;
    // 数据渲染
    tableIns = table.render({
        elem: '#contract',
        headers: {token: localStorage.getItem('token')},
        url: baseUrl+'/contract/list/auth',
        page: true,
        limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
        toolbar: '#toolbar',
        cellMinWidth: 50,
        height: 'full-120',
        cols: [[
            {type: 'checkbox'}
            ,{field: 'id', align: 'center',title: '#ID'}
            ,{field: 'name', align: 'center',title: '合同名称'}
            ,{field: 'customer', align: 'center',title: '甲方'}
            ,{field: 'company', align: 'center',title: '公司名称'}
            ,{field: 'status$', align: 'center',title: '状态'}
            ,{field: 'createBy$', align: 'center',title: '添加人员'}
            ,{field: 'createTime$', align: 'center',title: '添加时间'}
            ,{field: 'updateBy$', align: 'center',title: '修改人员'}
            ,{field: 'updateTime$', align: 'center',title: '修改时间'}
            ,{field: 'memo', align: 'center',title: '备注'}
            ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width:400}
        ]],
        request: {
            pageName: 'curr',
            pageSize: 'limit'
        },
        parseData: function (res) {
            return {
                'code': res.code,
                'msg': res.msg,
                'count': res.data.total,
                'data': res.data.records
            }
        },
        response: {
            statusCode: 200
        },
        done: function(res, curr, count) {
            if (res.code === 403) {
                top.location.href = baseUrl+"/";
            }
            pageCurr=curr;
            limit();
        }
    });
    // 监听排序事件
    table.on('sort(contract)', function (obj) {
        var searchData = {};
        $.each($('#search-box [name]').serializeArray(), function() {
            searchData[this.name] = this.value;
        });
        searchData['orderByField'] = obj.field;
        searchData['orderByType'] = obj.type;
        tableIns.reload({
            where: searchData,
            page: {curr: 1}
        });
    });
    // 监听头工具栏事件
    table.on('toolbar(contract)', function (obj) {
        var checkStatus = table.checkStatus(obj.config.id).data;
        switch(obj.event) {
            case 'addData':
                showEditModel();
                break;
            case 'deleteData':
               if (checkStatus.length === 0) {
                   layer.msg('请选择要删除的数据', {icon: 2});
                   return;
               }
               del(checkStatus.map(function (d) {
                   return d.id;
               }));
               break;
            case 'exportData':
                admin.confirm('确定导出Excel吗', {shadeClose: true}, function(){
                    var titles=[];
                    var fields=[];
                    obj.config.cols[0].map(function (col) {
                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
                            titles.push(col.title);
                            fields.push(col.field);
                        }
                    });
                    var exportData = {};
                    $.each($('#search-box [name]').serializeArray(), function() {
                        exportData[this.name] = this.value;
                    });
                    var param = {
                        'contract': exportData,
                        'fields': fields
                    };
                    $.ajax({
                        url: baseUrl+"/contract/export/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: JSON.stringify(param),
                        dataType:'json',
                        contentType:'application/json;charset=UTF-8',
                        method: 'POST',
                        success: function (res) {
                            layer.closeAll();
                            if (res.code === 200) {
                                table.exportFile(titles,res.data,'xls');
                            } else if (res.code === 403) {
                                top.location.href = baseUrl+"/";
                            } else {
                                layer.msg(res.msg, {icon: 2})
                            }
                        }
                    });
                });
                break;
        }
    });
    // 监听行工具事件
    table.on('tool(contract)', function(obj){
        var data = obj.data;
        switch (obj.event) {
            case 'edit':
                showEditModel(data);
                break;
            case "del":
                del([data.id]);
                break;
            case "generate":
                //生成合同
                generate(data)
                break
            case "sales":
                //合同明细
                sales(data);
                break
            case "upload":
                //上传合同
                upload(data)
                break
            case "download":
                //下载合同
                download(data)
                break
        }
    });
    /* 弹窗 - 新增、修改 */
    function showEditModel(mData) {
        admin.open({
            type: 1,
            area: '800px',
            title: (mData ? '修改' : '添加') + '合同',
            content: $('#editDialog').html(),
            success: function (layero, dIndex) {
                layDateRender(mData);
                form.val('detail', mData);
                form.on('submit(editSubmit)', function (data) {
                    var loadIndex = layer.load(2);
                    $.ajax({
                        url: baseUrl+"/contract/"+(mData?'update':'add')+"/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: data.field,
                        method: 'POST',
                        success: function (res) {
                            layer.close(loadIndex);
                            if (res.code === 200){
                                layer.close(dIndex);
                                layer.msg(res.msg, {icon: 1});
                                tableReload();
                            } else if (res.code === 403){
                                top.location.href = baseUrl+"/";
                            }else {
                                layer.msg(res.msg, {icon: 2});
                            }
                        }
                    })
                    return false;
                });
                $(layero).children('.layui-layer-content').css('overflow', 'visible');
                layui.form.render('select');
            }
        });
    }
    /* 生成合同 */
    function generate(mData) {
        admin.open({
            type: 1,
            area: '600px',
            title: '生成合同',
            content: $('#generateDialog').html(),
            success: function (layero, dIndex) {
                layDateRender(mData);
                form.val('detail', mData);
                form.on('submit(generateSubmit)', function (data) {
                    var loadIndex = layer.load(2);
                    $.ajax({
                        url: baseUrl + "/contract/generate/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: data.field,
                        method: 'GET',
                        xhrFields: {
                            responseType: "blob" // 设置响应类型为二进制数据
                        },
                        success: function (res) {
                            // 创建一个临时的下载链接
                            const url = window.URL.createObjectURL(res);
                            // 创建一个隐藏的 <a> 元素并设置下载链接
                            const a = document.createElement("a");
                            a.style.display = "none";
                            a.href = url;
                            a.download = data.field.name + ".docx"; // 指定下载的文件名
                            document.body.appendChild(a);
                            // 触发点击事件以开始下载
                            a.click();
                            // 清理临时资源
                            setTimeout(function() {
                                window.URL.revokeObjectURL(url);
                                document.body.removeChild(a);
                            }, 100);
                            layer.close(loadIndex);
                            layer.close(dIndex);
                        }
                    })
                    return false;
                });
                $(layero).children('.layui-layer-content').css('overflow', 'visible');
                layui.form.render('select');
            }
        });
    }
    /* 合同明细 */
    function sales(mData) {
        layer.open({
            type: 2,
            title: '合同明细',
            maxmin: true,
            area: [top.detailWidth,top.detailHeight],
            shadeClose: false,
            content: '../contractSales/contractSales.html?contractId=' + mData.id,
            success: function(layero, index){
            }
        });
    }
    //上传合同
    function upload(data) {
        if (data.filepath == '' || data.filepath == null) {
            layer.confirm('是否上传合同?', function(){
                $("#uploadQuote").click()
            });
        }else {
            layer.confirm('已上传合同,是否继续覆盖上传?', function(){
                $("#uploadQuote").click()
            });
        }
        $("#uploadQuote").on("change",(evt) => {
            var files = evt.target.files;
            if(files==null || files.length==0){
                alert("No files wait for import");
                return;
            }
            let name = files[0].name;
            let suffixArr = name.split("."), suffix = suffixArr[suffixArr.length-1];
            // if(suffix!="xlsx"){
            //     alert("Currently only supports the import of xlsx files");
            //     return;
            // }
            let formData = new FormData($("#uploadFile")[0]);
            formData.append("id", data.id);
            $.ajax({
                url: baseUrl + "/contract/upload/auth",
                headers: {'token': localStorage.getItem('token')},
                data: formData,
                method: 'POST',
                cache: false,
                processData: false,
                contentType: false,
                success: function (res) {
                    if (res.code == 200) {
                        layer.msg('上传成功',{time:1000},() => {
                            parent.location.reload()
                        })
                    }else{
                        layer.msg(res.msg,{time:1000},() => {
                            parent.location.reload()
                        })
                    }
                }
            })
        })
    }
    //下载合同
    function download(data) {
        $.ajax({
            url: baseUrl+"/contract/download/auth",
            headers: {'token': localStorage.getItem('token')},
            data: data,
            method: 'GET',
            xhrFields: {
                responseType: "blob" // 设置响应类型为二进制数据
            },
            success: function (res) {
                // 创建一个临时的下载链接
                const url = window.URL.createObjectURL(res);
                // 创建一个隐藏的 <a> 元素并设置下载链接
                const a = document.createElement("a");
                a.style.display = "none";
                a.href = url;
                let list = data.filepath.split(".")
                let suffix = "." + list[list.length - 1]//获取后缀名
                a.download = data.name + suffix; // 指定下载的文件名
                document.body.appendChild(a);
                // 触发点击事件以开始下载
                a.click();
                // 清理临时资源
                setTimeout(function() {
                    window.URL.revokeObjectURL(url);
                    document.body.removeChild(a);
                }, 100);
            }
        });
    }
    /* 删除 */
    function del(ids) {
        layer.confirm('确定要删除选中数据吗?', {
            skin: 'layui-layer-admin',
            shade: .1
        }, function (i) {
            layer.close(i);
            var loadIndex = layer.load(2);
            $.ajax({
                url: baseUrl+"/contract/delete/auth",
                headers: {'token': localStorage.getItem('token')},
                data: {ids: ids},
                method: 'POST',
                success: function (res) {
                    layer.close(loadIndex);
                    if (res.code === 200){
                        layer.msg(res.msg, {icon: 1});
                        tableReload();
                    } else if (res.code === 403){
                        top.location.href = baseUrl+"/";
                    } else {
                        layer.msg(res.msg, {icon: 2});
                    }
                }
            })
        });
    }
    // 搜索
    form.on('submit(search)', function (data) {
        pageCurr = 1;
        tableReload(false);
    });
    // 重置
    form.on('submit(reset)', function (data) {
        pageCurr = 1;
        clearFormVal($('#search-box'));
        tableReload(false);
    });
    // 时间选择器
    function layDateRender(data) {
        setTimeout(function () {
            layDate.render({
                elem: '.layui-laydate-range'
                ,type: 'datetime'
                ,range: true
            });
            layDate.render({
                elem: '#createTime\\$',
                type: 'datetime',
                value: data!==undefined?data['createTime\\$']:null
            });
            layDate.render({
                elem: '#updateTime\\$',
                type: 'datetime',
                value: data!==undefined?data['updateTime\\$']:null
            });
        }, 300);
    }
    layDateRender();
});
// 关闭动作
$(document).on('click','#data-detail-close', function () {
    parent.layer.closeAll();
});
function tableReload(child) {
    var searchData = {};
    $.each($('#search-box [name]').serializeArray(), function() {
        searchData[this.name] = this.value;
    });
    tableIns.reload({
        where: searchData,
        page: {curr: pageCurr}
     });
}
src/main/webapp/static/js/contractSales/contractSales.js
New file
@@ -0,0 +1,262 @@
var pageCurr;
var pageCount = 0;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).use(['table','laydate', 'form', 'admin', 'xmSelect'], function(){
    var table = layui.table;
    var $ = layui.jquery;
    var layer = layui.layer;
    var layDate = layui.laydate;
    var form = layui.form;
    var admin = layui.admin;
    var xmSelect = layui.xmSelect;
    var contractId = getQueryVariable("contractId")
    if (contractId != false) {
        $("#contract_id").val(contractId)
    }
    // 数据渲染
    tableIns = table.render({
        elem: '#contractSales',
        headers: {token: localStorage.getItem('token')},
        url: baseUrl+'/contractSales/list/auth',
        page: true,
        limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
        toolbar: '#toolbar',
        cellMinWidth: 50,
        height: 'full-120',
        where: {contract_id: $('#contract_id').val()},
        cols: [[
            {type: 'checkbox'}
            // ,{field: 'id', align: 'center',title: ''}
            ,{field: 'name', align: 'center',title: '品名'}
            ,{field: 'num', align: 'center',title: '数量'}
            ,{field: 'unit', align: 'center',title: '单位'}
            ,{field: 'unitPrice', align: 'center',title: '单价'}
            ,{field: 'totalPrice', align: 'center',title: '总价'}
            ,{field: 'memo', align: 'center',title: '备注'}
            ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width:120}
        ]],
        request: {
            pageName: 'curr',
            pageSize: 'limit'
        },
        parseData: function (res) {
            return {
                'code': res.code,
                'msg': res.msg,
                'count': res.data.total,
                'data': res.data.records
            }
        },
        response: {
            statusCode: 200
        },
        done: function(res, curr, count) {
            if (res.code === 403) {
                top.location.href = baseUrl+"/";
            }
            pageCurr=curr;pageCount=count;
            limit();
        }
    });
    // 监听排序事件
    table.on('sort(contractSales)', function (obj) {
        var searchData = {};
        $.each($('#search-box [name]').serializeArray(), function() {
            searchData[this.name] = this.value;
        });
        searchData['orderByField'] = obj.field;
        searchData['orderByType'] = obj.type;
        tableIns.reload({
            where: searchData,
            page: {curr: 1}
        });
    });
    // 监听头工具栏事件
    table.on('toolbar(contractSales)', function (obj) {
        var checkStatus = table.checkStatus(obj.config.id).data;
        switch(obj.event) {
            case 'addData':
                showEditModel();
                break;
            case 'deleteData':
               if (checkStatus.length === 0) {
                   layer.msg('请选择要删除的数据', {icon: 2});
                   return;
               }
               del(checkStatus.map(function (d) {
                   return d.id;
               }));
               break;
            case 'exportData':
                admin.confirm('确定导出Excel吗', {shadeClose: true}, function(){
                    var titles=[];
                    var fields=[];
                    obj.config.cols[0].map(function (col) {
                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
                            titles.push(col.title);
                            fields.push(col.field);
                        }
                    });
                    var exportData = {};
                    $.each($('#search-box [name]').serializeArray(), function() {
                        exportData[this.name] = this.value;
                    });
                    var param = {
                        'contractSales': exportData,
                        'fields': fields
                    };
                    $.ajax({
                        url: baseUrl+"/contractSales/export/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: JSON.stringify(param),
                        dataType:'json',
                        contentType:'application/json;charset=UTF-8',
                        method: 'POST',
                        success: function (res) {
                            layer.closeAll();
                            if (res.code === 200) {
                                table.exportFile(titles,res.data,'xls');
                            } else if (res.code === 403) {
                                top.location.href = baseUrl+"/";
                            } else {
                                layer.msg(res.msg, {icon: 2})
                            }
                        }
                    });
                });
                break;
        }
    });
    // 监听行工具事件
    table.on('tool(contractSales)', function(obj){
        var data = obj.data;
        switch (obj.event) {
            case 'edit':
                showEditModel(data);
                break;
            case "del":
                del([data.id]);
                break;
        }
    });
    /* 弹窗 - 新增、修改 */
    function showEditModel(mData) {
        admin.open({
            type: 1,
            area: '600px',
            title: (mData ? '修改' : '添加') + '',
            content: $('#editDialog').html(),
            success: function (layero, dIndex) {
                layDateRender(mData);
                form.val('detail', mData);
                form.on('submit(editSubmit)', function (data) {
                    var loadIndex = layer.load(2);
                    data.field.contractId = $("#contract_id").val()
                    $.ajax({
                        url: baseUrl+"/contractSales/"+(mData?'update':'add')+"/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: data.field,
                        method: 'POST',
                        success: function (res) {
                            layer.close(loadIndex);
                            if (res.code === 200){
                                layer.close(dIndex);
                                layer.msg(res.msg, {icon: 1});
                                tableReload();
                            } else if (res.code === 403){
                                top.location.href = baseUrl+"/";
                            }else {
                                layer.msg(res.msg, {icon: 2});
                            }
                        }
                    })
                    return false;
                });
                $(layero).children('.layui-layer-content').css('overflow', 'visible');
                layui.form.render('select');
            }
        });
    }
    /* 删除 */
    function del(ids) {
        layer.confirm('确定要删除选中数据吗?', {
            skin: 'layui-layer-admin',
            shade: .1
        }, function (i) {
            layer.close(i);
            var loadIndex = layer.load(2);
            $.ajax({
                url: baseUrl+"/contractSales/delete/auth",
                headers: {'token': localStorage.getItem('token')},
                data: {ids: ids},
                method: 'POST',
                success: function (res) {
                    layer.close(loadIndex);
                    if (res.code === 200){
                        layer.msg(res.msg, {icon: 1});
                        tableReload();
                    } else if (res.code === 403){
                        top.location.href = baseUrl+"/";
                    } else {
                        layer.msg(res.msg, {icon: 2});
                    }
                }
            })
        });
    }
    // 搜索
    form.on('submit(search)', function (data) {
        pageCurr = 1;
        tableReload();
    });
    // 重置
    form.on('submit(reset)', function (data) {
        pageCurr = 1;
        clearFormVal($('#search-box'));
        tableReload();
    });
    // 时间选择器
    function layDateRender(data) {
        setTimeout(function () {
            layDate.render({
                elem: '.layui-laydate-range'
                ,type: 'datetime'
                ,range: true
            });
        }, 300);
    }
    layDateRender();
});
// 关闭动作
$(document).on('click','#data-detail-close', function () {
    parent.layer.closeAll();
});
function tableReload() {
    var searchData = {};
    $.each($('#search-box [name]').serializeArray(), function() {
        searchData[this.name] = this.value;
    });
    tableIns.reload({
        where: searchData,
        page: {curr: pageCurr}
    });
}
src/main/webapp/views/contract/contract.html
New file
@@ -0,0 +1,236 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
</head>
<body>
<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <div class="layui-form toolbar" id="search-box">
                <div class="layui-form-item">
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input class="layui-input" type="text" name="id" placeholder="编号" autocomplete="off">
                        </div>
                    </div>
                     <div class="layui-inline" style="width: 300px">
                        <div class="layui-input-inline">
                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="起始时间 - 终止时间" autocomplete="off" style="width: 300px">
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input class="layui-input" type="text" name="condition" placeholder="请输入" autocomplete="off">
                        </div>
                    </div>
                    <div class="layui-inline">&emsp;
                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
                            <i class="layui-icon">&#xe615;</i>搜索
                        </button>
                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
                            <i class="layui-icon">&#xe666;</i>重置
                        </button>
                    </div>
                </div>
            </div>
            <table class="layui-hide" id="contract" lay-filter="contract"></table>
        </div>
    </div>
</div>
<form id="uploadFile" enctype="multipart/form-data" style="display: none;" >
    <input type="file" name="file" id="uploadQuote">
    <input type="button" onclick="upload()" value="上传"/>
</form>
<script type="text/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">新增</button>
        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button>
        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">导出</button>
    </div>
</script>
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">修改</a>
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="sales">合同明细</a>
    <a class="layui-btn layui-btn-xs btn-edit" lay-event="generate">生成合同</a>
    <a class="layui-btn layui-btn-xs btn-edit" lay-event="upload">上传合同</a>
    <a class="layui-btn layui-btn-xs btn-edit" lay-event="download">下载</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">删除</a>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/contract/contract.js" charset="utf-8"></script>
</body>
<!-- 表单弹窗 -->
<script type="text/html" id="editDialog">
    <div id="detail" lay-filter="detail" class="layui-form admin-form model-form">
        <input name="id" type="hidden">
        <div class="layui-row">
            <div class="layui-col-md6">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">合同名称: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="name" placeholder="请输入合同名称" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">甲方: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="customer" placeholder="请输入甲方" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">法人或授权代表: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="boss" placeholder="请输入法人或授权代表" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">甲方地址: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="address" placeholder="请输入甲方地址" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">公司名称: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="company" placeholder="请输入公司名称" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">公司地址: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="companyAddress" placeholder="请输入公司地址" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">公司税号: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="taxNum" placeholder="请输入公司税号" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">收货人: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="shippingName" placeholder="请输入收货人" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">收货人电话: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="shippingPhone" placeholder="请输入收货人电话" lay-verify="required">
                    </div>
                </div>
             </div>
            <div class="layui-col-md6">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">开户银行: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="bank" placeholder="请输入开户银行" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">银行账号: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="bankNum" placeholder="请输入银行账号" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">收货地址:</label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="shippingAddress" placeholder="请输入收货地址" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">合同总金额: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="price" placeholder="请输入合同总金额" lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">电子邮箱: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="email" placeholder="请输入电子邮箱">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">城市: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="city" placeholder="请输入城市">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">合同状态: </label>
                    <div class="layui-input-block">
                        <select name="status" lay-vertype="tips" lay-verify="required">
                            <option value="">请选择状态</option>
                            <option value="1">正常</option>
                            <option value="0">禁止</option>
                        </select>
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">备注: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="memo" placeholder="请输入备注">
                    </div>
                </div>
            </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </div>
</script>
<!-- 表单弹窗 -->
<script type="text/html" id="generateDialog">
    <div id="detail1" lay-filter="detail" class="layui-form admin-form model-form">
        <input name="id" type="hidden">
        <div class="layui-row">
            <div class="layui-col-md12">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">合同名称: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="name" placeholder="请输入合同名称" disabled lay-verify="required">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">合同模板: </label>
                    <div class="layui-input-block">
                        <input type="radio" name="contractTemplate" value="jcxm_1" title="集成项目销售合同模板" checked>
                        <input type="radio" name="contractTemplate" value="zjxm_1" title="载具销售合同模板">
                    </div>
                </div>
            </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="generateSubmit" lay-submit="">生成</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </div>
</script>
</html>
src/main/webapp/views/contractSales/contractSales.html
New file
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
</head>
<body>
<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <div class="layui-form toolbar" id="search-box">
                <div class="layui-form-item">
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input class="layui-input" type="text" name="id" placeholder="编号" autocomplete="off">
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input class="layui-input" type="text" name="contract_id" id="contract_id" placeholder="合同ID" autocomplete="off">
                        </div>
                    </div>
                     <div class="layui-inline" style="width: 300px">
                        <div class="layui-input-inline">
                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="起始时间 - 终止时间" autocomplete="off" style="width: 300px">
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input class="layui-input" type="text" name="condition" placeholder="请输入" autocomplete="off">
                        </div>
                    </div>
                    <div class="layui-inline">&emsp;
                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
                            <i class="layui-icon">&#xe615;</i>搜索
                        </button>
                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
                            <i class="layui-icon">&#xe666;</i>重置
                        </button>
                    </div>
                </div>
            </div>
            <table class="layui-hide" id="contractSales" lay-filter="contractSales"></table>
        </div>
    </div>
</div>
<script type="text/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">新增</button>
        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">删除</button>
        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">导出</button>
    </div>
</script>
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">修改</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">删除</a>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/contractSales/contractSales.js" charset="utf-8"></script>
</body>
<!-- 表单弹窗 -->
<script type="text/html" id="editDialog">
    <div id="detail" lay-filter="detail" class="layui-form admin-form model-form">
        <input name="id" type="hidden">
        <div class="layui-row">
            <div class="layui-col-md12">
                <div class="layui-form-item">
                    <label class="layui-form-label">品名: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="name" placeholder="请输入品名">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">数量: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="num" placeholder="请输入数量">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">单位: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="unit" placeholder="请输入单位">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">单价: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="unitPrice" placeholder="请输入单价">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">总价: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="totalPrice" placeholder="请输入总价">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">备注: </label>
                    <div class="layui-input-block">
                        <input class="layui-input" name="memo" placeholder="请输入备注">
                    </div>
                </div>
             </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </div>
</script>
</html>