From 882f447d5771a6e2dbd5815947e57172cd7f90c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B3=95=E6=8B=8951246?= <719602854@qq.com> Date: Thu, 5 Jun 2025 18:23:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=93=E5=8D=B0=E7=83=82=E7=9A=84=20?= =?UTF-8?q?=E5=A4=A7=E5=B1=8F=E6=B2=A1=E5=86=99=20=E8=85=BE=E8=AE=AF?= =?UTF-8?q?=E5=9C=B0=E5=9B=BEkey=E6=9B=BF=E6=8D=A2=E4=B8=BA=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-visit/pom.xml | 6 + .../customerinfo/CustomerInfoController.java | 2 + .../customerinfo/vo/CustomerInfoRespVO.java | 11 +- .../vo/CustomerInfoSaveReqVO.java | 4 +- .../controller/admin/info/InfoController.java | 20 ++ .../controller/admin/info/vo/InfoPrintVO.java | 55 +++++ .../controller/admin/info/vo/InfoRespVO.java | 11 +- .../admin/info/vo/InfoSaveReqVO.java | 7 +- .../visit/dal/dataobject/info/InfoDO.java | 6 + .../visit/dal/mysql/info/InfoMapper.java | 4 + .../visit/enums/ErrorCodeConstants.java | 2 + .../customerinfo/CustomerInfoServiceImpl.java | 46 ++++ .../visit/service/info/InfoService.java | 15 +- .../visit/service/info/InfoServiceImpl.java | 134 +++++++++++ .../yudao-ui-admin-vue3/package-lock.json | 12 +- yudao-ui/yudao-ui-admin-vue3/package.json | 3 +- .../src/api/visit/info/index.ts | 43 ++++ .../src/views/system/user/UserForm.vue | 1 - .../visit/customerinfo/CustomerInfoForm.vue | 6 +- .../src/views/visit/customerinfo/index.vue | 64 ++++- .../src/views/visit/info/InfoForm.vue | 20 +- .../src/views/visit/info/index.vue | 48 +++- .../src/views/visit/product/index.vue | 2 +- .../src/views/visit/util/MapPicker.vue | 2 +- .../src/views/visit/util/VisitPrint.vue | 219 ++++++++++++++++++ 25 files changed, 704 insertions(+), 39 deletions(-) create mode 100644 yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPrintVO.java create mode 100644 yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue diff --git a/yudao-module-visit/pom.xml b/yudao-module-visit/pom.xml index 46fd7df..bad052a 100644 --- a/yudao-module-visit/pom.xml +++ b/yudao-module-visit/pom.xml @@ -45,6 +45,12 @@ cn.iocoder.boot yudao-spring-boot-starter-biz-data-permission + + cn.iocoder.boot + yudao-module-infra-biz + 2.4.2-jdk8-SNAPSHOT + compile + diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java index 2129837..dfae132 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java @@ -129,4 +129,6 @@ public class CustomerInfoController { BeanUtils.toBean(list, CustomerInfoRespVO.class)); } + + } \ No newline at end of file diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java index db807ed..b4daae9 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java @@ -24,8 +24,8 @@ public class CustomerInfoRespVO { @ExcelProperty("客户姓名") private String customerName; - @Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("联系方式") + @Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("联系方式(客户手机号)") private String contact; @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED) @@ -69,12 +69,11 @@ public class CustomerInfoRespVO { private String productNames; @Schema(description = "客户所属部门", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("客户所属部门") + @ExcelProperty(value = "客户所属部门", converter = DictConvert.class) @DictFormat("customer_dept") private String department; @Schema(description = "静态图", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("静态图") private String locationImage; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @@ -84,4 +83,8 @@ public class CustomerInfoRespVO { @Schema(description = "产品信息List对象") private List productList; + @Schema(description = "业务经理") + @ExcelProperty("业务经理") + private String creator; + } \ No newline at end of file diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java index 2045786..cb79d9e 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java @@ -17,8 +17,8 @@ public class CustomerInfoSaveReqVO { @NotEmpty(message = "客户姓名不能为空") private String customerName; - @Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "联系方式不能为空") + @Schema(description = "联系方式") + @Pattern(regexp = "^$|^\\d{11}$", message = "联系方式必须是11位数字") private String contact; @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java index 324f5f5..a4fae27 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java @@ -71,6 +71,26 @@ public class InfoController { return success(BeanUtils.toBean(info, InfoRespVO.class)); } + @GetMapping("/getPrintListByIds") + @Operation(summary = "获得客户拜访打印信息根据Ids") + public CommonResult> getPrintListByIds(@RequestParam("ids") String ids) { + String[] split = ids.split(","); + //转为List + List idList = new ArrayList<>(); + for (String s : split) { + idList.add(Long.parseLong(s)); + } + List info = infoService.getInfoByIds(idList); + return success(info); + } + + @GetMapping("/getPrintListByCompanyName") + @Operation(summary = "获得客户拜访打印信息根据公司名称") + public CommonResult getPrintListByCompanyName(@RequestParam("companyName") String companyName) { + InfoPrintVO info = infoService.getInfoByCompanyName(companyName); + return success(info); + } + @GetMapping("/page") @Operation(summary = "获得客户拜访记录分页") @PreAuthorize("@ss.hasPermission('visit:info:query')") diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPrintVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPrintVO.java new file mode 100644 index 0000000..f55c659 --- /dev/null +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPrintVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.visit.controller.admin.info.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 客户拜访记录 Print VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class InfoPrintVO { + + @Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED) + private String contact; + + @Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String companyName; + + @Schema(description = "客户部门") + private String department; + + @Schema(description = "定位静态图", requiredMode = Schema.RequiredMode.REQUIRED) + private String locationImage; + + @Schema(description = "最近四次拜访日期", requiredMode = Schema.RequiredMode.REQUIRED) + private List visitDate; + + @Schema(description = "拜访品种") + private String visitProductNames; + + @Schema(description = "最近四次服务内容") + private List serviceContent; + + @Schema(description = "服务记录图片URL列表(JSON数组)") + private String serviceImages; + + @Schema(description = "业务员") + private String salesman; + + @Schema(description = "服务数量") + private Integer serviceCount; + + + + +} \ No newline at end of file diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java index c625723..36bae40 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java @@ -37,6 +37,11 @@ public class InfoRespVO { @ExcelProperty("客户公司名称") private String companyName; + @Schema(description = "客户所属部门", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty(value = "客户所属部门", converter = DictConvert.class) + @DictFormat("customer_dept") + private String department; + @Schema(description = "所在地区", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("所在地区") private String regionFullName; @@ -50,7 +55,8 @@ public class InfoRespVO { @Schema(description = "区名称", requiredMode = Schema.RequiredMode.REQUIRED) private String areaName; - @Schema(description = "定位地址文字描述", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "详细地址", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("详细地址") private String locationText; @Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED) @@ -59,7 +65,7 @@ public class InfoRespVO { @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED) private BigDecimal latitude; - @Schema(description = "定位静态图URL", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "定位静态图", requiredMode = Schema.RequiredMode.REQUIRED) private String locationImage; @Schema(description = "拜访日期", requiredMode = Schema.RequiredMode.REQUIRED) @@ -101,7 +107,6 @@ public class InfoRespVO { private String customerFeedback; @Schema(description = "服务记录图片URL列表(JSON数组)") - @ExcelProperty("服务记录图片URL列表(JSON数组)") private String serviceImages; diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java index c5e231d..9c2ad3e 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java @@ -20,8 +20,8 @@ public class InfoSaveReqVO { @NotEmpty(message = "客户姓名不能为空") private String customerName; - @Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "联系方式(客户手机号)不能为空") + @Schema(description = "联系方式(客户手机号)") + @Pattern(regexp = "^$|^\\d{11}$", message = "联系方式必须是11位数字") private String contact; @Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED) @@ -80,6 +80,9 @@ public class InfoSaveReqVO { @Schema(description = "拜访类型(字典)", example = "2") private Integer visitType; + @Schema(description = "客户所属部门(字典)") + private String department; + @Schema(description = "服务内容") private String serviceContent; diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java index d1d3e6d..87fff2e 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java @@ -115,6 +115,12 @@ public class InfoDO extends BaseDO { * 枚举 {@link TODO visit_type 对应的类} */ private Integer visitType; + /** + * 客户部门 + * + * 枚举 {@link TODO customer_dept 对应的类} + */ + private String department; /** * 服务内容 */ diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java index 9c8df46..78d91b3 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java @@ -31,4 +31,8 @@ public interface InfoMapper extends BaseMapperX { .orderByDesc(InfoDO::getId)); } + default List selectByCompanyName(String companyName) { + return selectList(new LambdaQueryWrapperX() + .eq(InfoDO::getCompanyName, companyName)); + } } \ No newline at end of file diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java index 9a65768..2078f40 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java @@ -16,6 +16,8 @@ public interface ErrorCodeConstants { // ========== 客户信息 ========== ErrorCode CUSTOMER_INFO_NOT_EXISTS = new ErrorCode(1002000001, "客户信息不存在"); + ErrorCode CUSTOMER_INFO_COMPANY_NAME_DUPLICATE = new ErrorCode(1002000002, "该公司名称已存在"); + // ========== 客户拜访记录 ========== ErrorCode INFO_NOT_EXISTS = new ErrorCode(1003000001, "客户拜访记录不存在"); diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java index 18a776a..5683a2a 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java @@ -1,10 +1,19 @@ package cn.iocoder.yudao.module.visit.service.customerinfo; +import cn.iocoder.yudao.module.infra.service.file.FileService; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.util.*; import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*; import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO; @@ -29,21 +38,58 @@ public class CustomerInfoServiceImpl implements CustomerInfoService { @Resource private CustomerInfoMapper customerInfoMapper; + @Resource + private FileService fileService; + @Override public Long createCustomerInfo(CustomerInfoSaveReqVO createReqVO) { // 插入 CustomerInfoDO customerInfo = BeanUtils.toBean(createReqVO, CustomerInfoDO.class); + //校验companyName是否重复 + CustomerInfoDO customerInfoDO = customerInfoMapper.selectByCompanyName(customerInfo.getCompanyName()); + if (customerInfoDO != null) { + throw exception(CUSTOMER_INFO_COMPANY_NAME_DUPLICATE); + } + String url = uploadMapImageByUrl(customerInfo.getLocationImage(), null); + customerInfo.setLocationImage(url); customerInfoMapper.insert(customerInfo); // 返回 return customerInfo.getId(); } + public String uploadMapImageByUrl(String imageUrl, String directory) { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + String fixedUrl = imageUrl.replace("|", "%7C"); + HttpGet request = new HttpGet(fixedUrl); + try (CloseableHttpResponse response = httpClient.execute(request)) { + HttpEntity entity = response.getEntity(); + byte[] content = EntityUtils.toByteArray(entity); + + // 文件名:你可以从 URL 中提取或自定义 + String name = "map_" + System.currentTimeMillis() + ".jpg"; + String type = ContentType.getOrDefault(entity).getMimeType(); + + // 调用你已有的 createFile 方法 + return fileService.createFile(content, name, directory, type); + } + } catch (IOException e) { + throw new RuntimeException("下载地图图片失败:" + imageUrl, e); + } + } @Override public void updateCustomerInfo(CustomerInfoSaveReqVO updateReqVO) { // 校验存在 validateCustomerInfoExists(updateReqVO.getId()); // 更新 CustomerInfoDO updateObj = BeanUtils.toBean(updateReqVO, CustomerInfoDO.class); + CustomerInfoDO customerInfoDO = customerInfoMapper.selectByCompanyName(updateObj.getCompanyName()); + if (customerInfoDO != null) { + throw exception(CUSTOMER_INFO_COMPANY_NAME_DUPLICATE); + } + if (updateObj.getLocationImage() != null&& updateObj.getLocationImage().startsWith("https://apis.map.qq.com/ws/staticmap/v2/")){ + String url = uploadMapImageByUrl(updateObj.getLocationImage(), null); + updateObj.setLocationImage(url); + } customerInfoMapper.updateById(updateObj); } diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java index 523e5cd..21ee919 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java @@ -51,5 +51,18 @@ public interface InfoService { * @return 客户拜访记录分页 */ PageResult getInfoPage(InfoPageReqVO pageReqVO); - + /** + * 获得客户拜访记录打印数据 根据ids + * + * @param ids 查询 + * @return 客户拜访记录 + */ + List getInfoByIds(List ids); + /** + * 获得客户拜访记录打印数据 根据客户名称 + * + * @param companyName 查询 + * @return 客户拜访记录 + */ + InfoPrintVO getInfoByCompanyName(String companyName); } \ No newline at end of file diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java index ba35c9a..dfc86bf 100644 --- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java +++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java @@ -2,21 +2,37 @@ package cn.iocoder.yudao.module.visit.service.info; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.infra.service.file.FileService; import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.CustomerInfoSaveReqVO; import cn.iocoder.yudao.module.visit.controller.admin.info.vo.InfoPageReqVO; +import cn.iocoder.yudao.module.visit.controller.admin.info.vo.InfoPrintVO; import cn.iocoder.yudao.module.visit.controller.admin.info.vo.InfoSaveReqVO; import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO; import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO; import cn.iocoder.yudao.module.visit.dal.mysql.customerinfo.CustomerInfoMapper; import cn.iocoder.yudao.module.visit.dal.mysql.info.InfoMapper; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.INFO_NOT_EXISTS; +import static java.nio.file.Files.createFile; /** * 客户拜访记录 Service 实现类 @@ -33,11 +49,16 @@ public class InfoServiceImpl implements InfoService { @Resource private CustomerInfoMapper customerInfoMapper; + @Resource + private FileService fileService; + @Override @Transactional(rollbackFor = Exception.class) public Long createInfo(InfoSaveReqVO createReqVO) { // 插入 InfoDO info = BeanUtils.toBean(createReqVO, InfoDO.class); + String url = uploadMapImageByUrl(info.getLocationImage(), null); + info.setLocationImage(url); int insert = infoMapper.insert(info); if (insert >0){ //拜访记录插入成功,根据【公司名称】判断该客户是否记录在案,如果没有,则插入一条客户信息 @@ -49,6 +70,7 @@ public class InfoServiceImpl implements InfoService { customerInfoSaveReqVO.setContact(info.getContact()); customerInfoSaveReqVO.setCompanyName(info.getCompanyName()); customerInfoSaveReqVO.setCustomerType(info.getCustomerStatus()); + customerInfoSaveReqVO.setDepartment(info.getDepartment()); customerInfoSaveReqVO.setProvinceName(info.getProvinceName()); customerInfoSaveReqVO.setCityName(info.getCityName()); customerInfoSaveReqVO.setAreaName(info.getAreaName()); @@ -67,12 +89,36 @@ public class InfoServiceImpl implements InfoService { return info.getId(); } + public String uploadMapImageByUrl(String imageUrl, String directory) { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + String fixedUrl = imageUrl.replace("|", "%7C"); + HttpGet request = new HttpGet(fixedUrl); + try (CloseableHttpResponse response = httpClient.execute(request)) { + HttpEntity entity = response.getEntity(); + byte[] content = EntityUtils.toByteArray(entity); + + // 文件名:你可以从 URL 中提取或自定义 + String name = "map_" + System.currentTimeMillis() + ".jpg"; + String type = ContentType.getOrDefault(entity).getMimeType(); + + // 调用你已有的 createFile 方法 + return fileService.createFile(content, name, directory, type); + } + } catch (IOException e) { + throw new RuntimeException("下载地图图片失败:" + imageUrl, e); + } + } + @Override public void updateInfo(InfoSaveReqVO updateReqVO) { // 校验存在 validateInfoExists(updateReqVO.getId()); // 更新 InfoDO updateObj = BeanUtils.toBean(updateReqVO, InfoDO.class); + if (updateObj.getLocationImage() != null&& updateObj.getLocationImage().startsWith("https://apis.map.qq.com/ws/staticmap/v2/")){ + String url = uploadMapImageByUrl(updateObj.getLocationImage(), null); + updateObj.setLocationImage(url); + } infoMapper.updateById(updateObj); } @@ -100,4 +146,92 @@ public class InfoServiceImpl implements InfoService { return infoMapper.selectPage(pageReqVO); } + @Override + public List getInfoByIds(List ids) { + List vos = new ArrayList<>(); + //先获取客户拜访记录 + List info = infoMapper.selectByIds(ids); + if (info != null && !info.isEmpty()){ + //获取客户拜访记录成功,获取客户信息 + info.forEach(infoDO -> { + InfoPrintVO infoPrintVO = new InfoPrintVO(); + infoPrintVO.setServiceContent(Collections.singletonList(infoDO.getServiceContent()));//本次服务内容 + //服务图片(最多四张) + String imagesStr = infoDO.getServiceImages(); + if (StringUtils.isNotBlank(imagesStr)) { + String limitedImagesStr = Arrays.stream(imagesStr.split(",")) + .limit(4) + .collect(Collectors.joining(",")); + infoPrintVO.setServiceImages(limitedImagesStr); + } else { + infoPrintVO.setServiceImages(""); + } + infoPrintVO.setVisitProductNames(infoDO.getVisitProductNames());//本次拜访品种 + infoPrintVO.setCompanyName(infoDO.getCompanyName());//公司名称 + infoPrintVO.setDepartment(infoDO.getDepartment());//部门 + infoPrintVO.setContact(infoDO.getContact());//联系方式 + infoPrintVO.setSalesman(infoDO.getCreator());//拜访记录的创建者就是该记录的业务员 + infoPrintVO.setLocationImage(infoDO.getLocationImage()); + infoPrintVO.setVisitDate(Collections.singletonList(infoDO.getVisitDate())); + //拜访次数按拜访id打印都是1 + infoPrintVO.setServiceCount(1); + vos.add(infoPrintVO); + }); + } + return vos; + } + + @Override + public InfoPrintVO getInfoByCompanyName(String companyName) { + InfoPrintVO infoPrintVO = new InfoPrintVO(); + //服务内容(最近四次) + List serviceContent = new ArrayList<>(); + //服务图片(最多四张) + List serviceImages = new ArrayList<>(); + //拜访时间记录 + List visitDateList = new ArrayList<>(); + //现根据客户名称获取历史拜访记录 + List info = infoMapper.selectByCompanyName(companyName); + if (info != null && !info.isEmpty()){ + //取最近四次 + info = info.stream().sorted(Comparator.comparing(InfoDO::getVisitDate).reversed()).limit(4).collect(Collectors.toList()); + //取最多4张服务图片 + int maxImages = 4; + for (InfoDO infoDO : info) { + if (serviceImages.size() >= maxImages) break; + + String imagesStr = infoDO.getServiceImages(); + if (StringUtils.isNotBlank(imagesStr)) { + List imageList = Arrays.asList(imagesStr.split(",")); + int remaining = maxImages - serviceImages.size(); + serviceImages.addAll(imageList.stream().limit(remaining).collect(Collectors.toList())); + } + } + //获取客户拜访记录成功,获取客户信息 + info.forEach(infoDO -> { + //服务内容记录 + serviceContent.add(infoDO.getServiceContent()); + //拜访时间记录 + visitDateList.add(infoDO.getVisitDate()); + }); + //下面获取客户信息 + CustomerInfoDO customerInfoDO = customerInfoMapper.selectByCompanyName(companyName); + infoPrintVO.setVisitProductNames(customerInfoDO.getProductNames());//本次拜访品种 + infoPrintVO.setCompanyName(customerInfoDO.getCompanyName());//公司名称 + infoPrintVO.setSalesman(customerInfoDO.getCreator());//拜访记录的创建者就是该记录的业务员 + infoPrintVO.setLocationImage(customerInfoDO.getLocationImage());//公司定位图 + infoPrintVO.setContact(customerInfoDO.getContact()); + infoPrintVO.setDepartment(customerInfoDO.getDepartment()); + //添加多次的信息 + infoPrintVO.setServiceContent(serviceContent); + infoPrintVO.setVisitDate(visitDateList); + infoPrintVO.setServiceImages(String.join(",", serviceImages)); + //拜访次数 + infoPrintVO.setServiceCount(info.size()); + }else { + return null; + } + return infoPrintVO; + } + } \ No newline at end of file diff --git a/yudao-ui/yudao-ui-admin-vue3/package-lock.json b/yudao-ui/yudao-ui-admin-vue3/package-lock.json index a2501b3..a7cbb3c 100644 --- a/yudao-ui/yudao-ui-admin-vue3/package-lock.json +++ b/yudao-ui/yudao-ui-admin-vue3/package-lock.json @@ -15,7 +15,7 @@ "@iconify/iconify": "^3.1.1", "@microsoft/fetch-event-source": "^2.0.1", "@videojs-player/vue": "^1.0.0", - "@vueuse/core": "^10.9.0", + "@vueuse/core": "^10.11.1", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.10", "@zxcvbn-ts/core": "^3.0.4", @@ -59,6 +59,7 @@ "vue-i18n": "9.10.2", "vue-router": "4.4.5", "vue-types": "^5.1.1", + "vue3-print-nb": "^0.1.4", "vue3-signature": "^0.2.4", "vuedraggable": "^4.1.0", "web-storage-cache": "^1.1.1", @@ -6908,7 +6909,6 @@ "version": "10.11.1", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", - "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "10.11.1", @@ -17077,6 +17077,14 @@ "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", "license": "MIT" }, + "node_modules/vue3-print-nb": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/vue3-print-nb/-/vue3-print-nb-0.1.4.tgz", + "integrity": "sha512-LExI7viEzplR6ZKQ2b+V4U0cwGYbVD4fut/XHvk3UPGlT5CcvIGs6VlwGp107aKgk6P8Pgx4rco3Rehv2lti3A==", + "dependencies": { + "vue": "^3.0.5" + } + }, "node_modules/vue3-signature": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/vue3-signature/-/vue3-signature-0.2.4.tgz", diff --git a/yudao-ui/yudao-ui-admin-vue3/package.json b/yudao-ui/yudao-ui-admin-vue3/package.json index 77e9df0..4d11e3d 100644 --- a/yudao-ui/yudao-ui-admin-vue3/package.json +++ b/yudao-ui/yudao-ui-admin-vue3/package.json @@ -31,7 +31,7 @@ "@iconify/iconify": "^3.1.1", "@microsoft/fetch-event-source": "^2.0.1", "@videojs-player/vue": "^1.0.0", - "@vueuse/core": "^10.9.0", + "@vueuse/core": "^10.11.1", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^5.1.10", "@zxcvbn-ts/core": "^3.0.4", @@ -75,6 +75,7 @@ "vue-i18n": "9.10.2", "vue-router": "4.4.5", "vue-types": "^5.1.1", + "vue3-print-nb": "^0.1.4", "vue3-signature": "^0.2.4", "vuedraggable": "^4.1.0", "web-storage-cache": "^1.1.1", diff --git a/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts index bb9d487..dab507c 100644 --- a/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts +++ b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts @@ -21,8 +21,40 @@ export interface InfoVO { serviceContent: string // 服务内容 customerFeedback: string // 客户反馈 serviceImages: string // 服务记录图片URL列表(JSON数组) + department: string // 客户部门 } +export interface InfoPrintVO { + /** 联系方式 */ + contact: string; + + /** 客户公司名称 */ + companyName: string; + + /** 客户部门 */ + department?: string; + + /** 定位静态图(URL) */ + locationImage: string; + + /** 最近四次拜访日期 */ + visitDate: string[]; // ISO 格式字符串,或可用 Date[] 视后端返回而定 + + /** 拜访品种 */ + visitProductNames?: string; + + /** 最近四次服务内容 */ + serviceContent: string[]; + + /** 服务记录图片URL列表(逗号分隔的字符串) */ + serviceImages: string; + + /** 业务员 */ + salesman: string; + + /** 服务数量 */ + serviceCount: number; +} // 客户拜访记录 API export const InfoApi = { // 查询客户拜访记录分页 @@ -35,6 +67,17 @@ export const InfoApi = { return await request.get({ url: `/visit/info/get?id=` + id }) }, + //查询打印客户拜访记录 + getPrintListByIds: async (ids: string) => { + return await request.get({ url: `/visit/info/getPrintListByIds`, params: { ids } }) + }, + + + //查询打印客户拜访记录 + getPrintListByCompanyName: async (companyName: string) => { + return await request.get({ url: `/visit/info/getPrintListByCompanyName`, params: { companyName } }) + }, + // 新增客户拜访记录 createInfo: async (data: InfoVO) => { return await request.post({ url: `/visit/info/create`, data }) diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/system/user/UserForm.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/system/user/UserForm.vue index 8087e48..694476f 100644 --- a/yudao-ui/yudao-ui-admin-vue3/src/views/system/user/UserForm.vue +++ b/yudao-ui/yudao-ui-admin-vue3/src/views/system/user/UserForm.vue @@ -96,7 +96,6 @@ diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue index 9c6f854..b7b82eb 100644 --- a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue +++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue @@ -182,7 +182,7 @@ const multipleSelection = ref([]) //存储多选内容 /** 多选操作**/ const handleSelectionChange = (val) => { multipleSelection.value = val.map(item=>item.id) - console.log(val,multipleSelection) + //console.log(val,multipleSelection) } /** 查询列表 */ const getList = async () => { diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/MapPicker.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/MapPicker.vue index 63c9571..9718059 100644 --- a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/MapPicker.vue +++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/MapPicker.vue @@ -26,7 +26,7 @@ import { ref, onMounted, onUnmounted } from 'vue' const visible = ref(false) // ✅ 腾讯地图 locationPicker 地址,替换成你的 key & 应用名 -const pickerUrl = `https://apis.map.qq.com/tools/locpicker?search=1&type=1&key=KHXBZ-OVYYZ-N4NXF-7JCZ2-PR4FT-RYF4E&referer=VISITKEY` +const pickerUrl = `https://apis.map.qq.com/tools/locpicker?search=1&type=1&key=2OZBZ-WUCE7-SLKXP-HJVOW-3P6RF-WVB7H&referer=VISITKEY` // ✅ 定义暴露 open() 方法,父组件通过 ref 调用 const open = () => { diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue new file mode 100644 index 0000000..ab3b86d --- /dev/null +++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue @@ -0,0 +1,219 @@ + + + + 渠道服务记录 + + + + + 业务员:{{ item.salesman }} + 服务数量:{{ item.serviceCount }} + 拜访时间记录 + + {{ formatDate(time) }} + + 主要服务内容 + + 内容{{ i + 1 }}:{{ content }} + + + + + 客户名称:{{ item.companyName }} + 客户部门:{{ getDictLabel(DICT_TYPE.CUSTOMER_DEPT, item.department) }} + 推广产品:{{ item.visitProductNames }} + 拜访签到实景图 + + + + + + + + + + + + + -- 2.22.0
业务员:{{ item.salesman }}
服务数量:{{ item.serviceCount }}
客户名称:{{ item.companyName }}
客户部门:{{ getDictLabel(DICT_TYPE.CUSTOMER_DEPT, item.department) }}
推广产品:{{ item.visitProductNames }}