Commit 20a8a2b4 authored by 法拉51246's avatar 法拉51246

产品、客户、拜访记录三个业务模块创建

关闭租户
parent 90358580
......@@ -3,18 +3,18 @@
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao</artifactId>
<version>${revision}</version>
<version>${revision}</version> <!-- 1. 修改 version 为 ${revision} -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-visit</artifactId>
<packaging>jar</packaging>
<packaging>jar</packaging> <!-- 2. 新增 packaging 为 jar -->
<name>${project.artifactId}</name>
<description>
visit 模块,主要实现 拜访系统主要业务逻辑
<name>${project.artifactId}</name> <!-- 3. 新增 name 为 ${project.artifactId} -->
<description> <!-- 4. 新增 description 为该模块的描述 -->
visit 模块,主要实现 拜访 等功能
</description>
<dependencies>
<dependencies> <!-- 5. 新增依赖,这里引入的都是比较常用的业务组件、技术组件 -->
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
......@@ -37,6 +37,10 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
</project>
package cn.iocoder.yudao.module.visit.controller.admin;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - Test")
@RestController
@RequestMapping("/demo/test")
@Validated
public class DemoTestController {
// 这个构造方法,只是方便大家,验证 Controller 有生效
public DemoTestController() {
System.out.println(getClass() + "生效啦!!!");
}
@GetMapping("/get")
@Operation(summary = "获取 test 信息")
public CommonResult<String> get() {
return success("true");
}
}
package cn.iocoder.yudao.module.visit.controller.admin.customerinfo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
import cn.iocoder.yudao.module.visit.service.customerinfo.CustomerInfoService;
@Tag(name = "管理后台 - 客户信息")
@RestController
@RequestMapping("/visit/customer-info")
@Validated
public class CustomerInfoController {
@Resource
private CustomerInfoService customerInfoService;
@PostMapping("/create")
@Operation(summary = "创建客户信息")
@PreAuthorize("@ss.hasPermission('visit:customer-info:create')")
public CommonResult<Long> createCustomerInfo(@Valid @RequestBody CustomerInfoSaveReqVO createReqVO) {
return success(customerInfoService.createCustomerInfo(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户信息")
@PreAuthorize("@ss.hasPermission('visit:customer-info:update')")
public CommonResult<Boolean> updateCustomerInfo(@Valid @RequestBody CustomerInfoSaveReqVO updateReqVO) {
customerInfoService.updateCustomerInfo(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客户信息")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('visit:customer-info:delete')")
public CommonResult<Boolean> deleteCustomerInfo(@RequestParam("id") Long id) {
customerInfoService.deleteCustomerInfo(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('visit:customer-info:query')")
public CommonResult<CustomerInfoRespVO> getCustomerInfo(@RequestParam("id") Long id) {
CustomerInfoDO customerInfo = customerInfoService.getCustomerInfo(id);
return success(BeanUtils.toBean(customerInfo, CustomerInfoRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户信息分页")
@PreAuthorize("@ss.hasPermission('visit:customer-info:query')")
public CommonResult<PageResult<CustomerInfoRespVO>> getCustomerInfoPage(@Valid CustomerInfoPageReqVO pageReqVO) {
PageResult<CustomerInfoDO> pageResult = customerInfoService.getCustomerInfoPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerInfoRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出客户信息 Excel")
@PreAuthorize("@ss.hasPermission('visit:customer-info:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportCustomerInfoExcel(@Valid CustomerInfoPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CustomerInfoDO> list = customerInfoService.getCustomerInfoPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "客户信息.xls", "数据", CustomerInfoRespVO.class,
BeanUtils.toBean(list, CustomerInfoRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客户信息分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CustomerInfoPageReqVO extends PageParam {
@Schema(description = "客户姓名")
private String customerName;
@Schema(description = "联系方式")
private String contact;
@Schema(description = "公司名称")
private String companyName;
@Schema(description = "性质等级")
private Integer customerType;
@Schema(description = "市级行政区编码")
private String cityCode;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 客户信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CustomerInfoRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("编号")
private Long id;
@Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("客户姓名")
private String customerName;
@Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("联系方式")
private String contact;
@Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("公司名称")
private String companyName;
@Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "性质等级", converter = DictConvert.class)
@DictFormat("customer_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer customerType;
@Schema(description = "所在地区", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("所在地区")
private String regionFullName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 客户信息新增/修改 Request VO")
@Data
public class CustomerInfoSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "客户姓名不能为空")
private String customerName;
@Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "联系方式不能为空")
private String contact;
@Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "公司名称不能为空")
private String companyName;
@Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "性质等级不能为空")
private Integer customerType;
@Schema(description = "省名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "省名称不能为空")
private String provinceName;
@Schema(description = "市名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "市名称不能为空")
private String cityName;
@Schema(description = "区名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "区名称不能为空")
private String areaName;
@Schema(description = "所在地区", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "所在地区不能为空")
private String regionFullName;
@Schema(description = "详细地址", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "详细地址不能为空")
private String locationText;
@Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "经度不能为空")
private BigDecimal longitude;
@Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "纬度不能为空")
private BigDecimal latitude;
@Schema(description = "定位静态图 URL", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "定位静态图 URL不能为空")
private String locationImage;
@Schema(description = "产品信息")
private String productIds;
@Schema(description = "客户部门")
private String department;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.info;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
import cn.iocoder.yudao.module.visit.service.info.InfoService;
@Tag(name = "管理后台 - 客户拜访记录")
@RestController
@RequestMapping("/visit/info")
@Validated
public class InfoController {
@Resource
private InfoService infoService;
@PostMapping("/create")
@Operation(summary = "创建客户拜访记录")
@PreAuthorize("@ss.hasPermission('visit:info:create')")
public CommonResult<Long> createInfo(@Valid @RequestBody InfoSaveReqVO createReqVO) {
return success(infoService.createInfo(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户拜访记录")
@PreAuthorize("@ss.hasPermission('visit:info:update')")
public CommonResult<Boolean> updateInfo(@Valid @RequestBody InfoSaveReqVO updateReqVO) {
infoService.updateInfo(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客户拜访记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('visit:info:delete')")
public CommonResult<Boolean> deleteInfo(@RequestParam("id") Long id) {
infoService.deleteInfo(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户拜访记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('visit:info:query')")
public CommonResult<InfoRespVO> getInfo(@RequestParam("id") Long id) {
InfoDO info = infoService.getInfo(id);
return success(BeanUtils.toBean(info, InfoRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户拜访记录分页")
@PreAuthorize("@ss.hasPermission('visit:info:query')")
public CommonResult<PageResult<InfoRespVO>> getInfoPage(@Valid InfoPageReqVO pageReqVO) {
PageResult<InfoDO> pageResult = infoService.getInfoPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, InfoRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出客户拜访记录 Excel")
@PreAuthorize("@ss.hasPermission('visit:info:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportInfoExcel(@Valid InfoPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InfoDO> list = infoService.getInfoPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "客户拜访记录.xls", "数据", InfoRespVO.class,
BeanUtils.toBean(list, InfoRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
import lombok.*;
import java.time.LocalDate;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客户拜访记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class InfoPageReqVO extends PageParam {
@Schema(description = "客户姓名")
private String customerName;
@Schema(description = "联系方式(客户手机号)")
private String contact;
@Schema(description = "客户公司名称")
private String companyName;
@Schema(description = "拜访日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDate[] visitDate;
@Schema(description = "性质等级", example = "1")
private Integer customerStatus;
@Schema(description = "拜访品种(多选,逗号分隔)")
private String visitProductIds;
@Schema(description = "拜访方式(字典)")
private Integer visitMethod;
@Schema(description = "拜访类型(字典)", example = "2")
private Integer visitType;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDate;
import java.util.*;
import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 客户拜访记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class InfoRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("客户姓名")
private String customerName;
@Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("联系方式(客户手机号)")
private String contact;
@Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("客户公司名称")
private String companyName;
@Schema(description = "拜访日期", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("拜访日期")
private LocalDate visitDate;
@Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("性质等级")
private Integer customerStatus;
@Schema(description = "拜访品种(多选,逗号分隔)")
@ExcelProperty("拜访品种(多选,逗号分隔)")
private String visitProductIds;
@Schema(description = "拜访方式(字典)")
@ExcelProperty(value = "拜访方式(字典)", converter = DictConvert.class)
@DictFormat("visit_method") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer visitMethod;
@Schema(description = "拜访类型(字典)", example = "2")
@ExcelProperty(value = "拜访类型(字典)", converter = DictConvert.class)
@DictFormat("visit_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer visitType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDate;
import java.util.*;
import javax.validation.constraints.*;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 客户拜访记录新增/修改 Request VO")
@Data
public class InfoSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "客户姓名不能为空")
private String customerName;
@Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "联系方式(客户手机号)不能为空")
private String contact;
@Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "客户公司名称不能为空")
private String companyName;
@Schema(description = "省名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "省名称不能为空")
private String provinceName;
@Schema(description = "市名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "市名称不能为空")
private String cityName;
@Schema(description = "区名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "区名称不能为空")
private String areaName;
@Schema(description = "定位地址文字描述", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "定位地址文字描述不能为空")
private String locationText;
@Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "经度不能为空")
private BigDecimal longitude;
@Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "纬度不能为空")
private BigDecimal latitude;
@Schema(description = "定位静态图URL", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "定位静态图URL不能为空")
private String locationImage;
@Schema(description = "拜访日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "拜访日期不能为空")
private LocalDate visitDate;
@Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "性质等级不能为空")
private Integer customerStatus;
@Schema(description = "拜访品种(多选,逗号分隔)")
private String visitProductIds;
@Schema(description = "拜访方式(字典)")
private Integer visitMethod;
@Schema(description = "拜访类型(字典)", example = "2")
private Integer visitType;
@Schema(description = "服务内容")
private String serviceContent;
@Schema(description = "客户反馈")
private String customerFeedback;
@Schema(description = "服务记录图片URL列表(JSON数组)")
private String serviceImages;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.product;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
import cn.iocoder.yudao.module.visit.service.product.ProductService;
@Tag(name = "管理后台 - 产品")
@RestController
@RequestMapping("/visit/product")
@Validated
public class ProductController {
@Resource
private ProductService productService;
public ProductController(){
System.out.println("生效");
}
@PostMapping("/create")
@Operation(summary = "创建产品")
@PreAuthorize("@ss.hasPermission('visit:product:create')")
public CommonResult<Long> createProduct(@Valid @RequestBody ProductSaveReqVO createReqVO) {
return success(productService.createProduct(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新产品")
@PreAuthorize("@ss.hasPermission('visit:product:update')")
public CommonResult<Boolean> updateProduct(@Valid @RequestBody ProductSaveReqVO updateReqVO) {
productService.updateProduct(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除产品")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('visit:product:delete')")
public CommonResult<Boolean> deleteProduct(@RequestParam("id") Long id) {
productService.deleteProduct(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得产品")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('visit:product:query')")
public CommonResult<ProductRespVO> getProduct(@RequestParam("id") Long id) {
ProductDO product = productService.getProduct(id);
return success(BeanUtils.toBean(product, ProductRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得产品分页")
@PreAuthorize("@ss.hasPermission('visit:product:query')")
public CommonResult<PageResult<ProductRespVO>> getProductPage(@Valid ProductPageReqVO pageReqVO) {
PageResult<ProductDO> pageResult = productService.getProductPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ProductRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出产品 Excel")
@PreAuthorize("@ss.hasPermission('visit:product:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportProductExcel(@Valid ProductPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ProductDO> list = productService.getProductPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "产品.xls", "数据", ProductRespVO.class,
BeanUtils.toBean(list, ProductRespVO.class));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 产品分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductPageReqVO extends PageParam {
@Schema(description = "品种名称", example = "阿莫西林胶囊")
private String productName;
@Schema(description = "包装信息", example = "盒装")
private String packageName;
@Schema(description = "规格", example = "10x20mg")
private String specification;
@Schema(description = "状态(1:正常,0:禁用)", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 产品 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ProductRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("编号")
private Long id;
@Schema(description = "品种名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿莫西林胶囊")
@ExcelProperty("品种名称")
private String productName;
@Schema(description = "包装信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒装")
@ExcelProperty("包装信息")
private String packageName;
@Schema(description = "规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "10x20mg")
@ExcelProperty("规格")
private String specification;
@Schema(description = "状态(1:正常,0:禁用)", example = "1")
@ExcelProperty(value = "状态(1:正常,0:禁用)", converter = DictConvert.class)
@DictFormat("common_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 产品新增/修改 Request VO")
@Data
public class ProductSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "品种名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿莫西林胶囊")
@NotEmpty(message = "品种名称不能为空")
private String productName;
@Schema(description = "包装信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒装")
@NotEmpty(message = "包装信息不能为空")
private String packageName;
@Schema(description = "规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "10x20mg")
@NotEmpty(message = "规格不能为空")
private String specification;
@Schema(description = "状态(1:正常,0:禁用)", example = "1")
private Integer status;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo;
import lombok.*;
import java.util.*;
import java.math.BigDecimal;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 客户信息 DO
*
* @author 超级管理员
*/
@TableName("visit_customer_info")
@KeySequence("visit_customer_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerInfoDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 客户姓名
*/
private String customerName;
/**
* 联系方式
*/
private String contact;
/**
* 公司名称
*/
private String companyName;
/**
* 性质等级
*
* 枚举 {@link TODO customer_type 对应的类}
*/
private Integer customerType;
/**
* 省级行政区编码
*/
private String provinceCode;
/**
* 省名称
*/
private String provinceName;
/**
* 市级行政区编码
*/
private String cityCode;
/**
* 市名称
*/
private String cityName;
/**
* 区行政区编码
*/
private String areaCode;
/**
* 区名称
*/
private String areaName;
/**
* 所在地区
*/
private String regionFullName;
/**
* 详细地址
*/
private String locationText;
/**
* 经度
*/
private BigDecimal longitude;
/**
* 纬度
*/
private BigDecimal latitude;
/**
* 定位静态图 URL
*/
private String locationImage;
/**
* 产品信息
*/
private String productIds;
/**
* 客户部门
*
* 枚举 {@link TODO customer_dept 对应的类}
*/
private String department;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.dataobject.info;
import lombok.*;
import java.time.LocalDate;
import java.util.*;
import java.math.BigDecimal;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 客户拜访记录 DO
*
* @author 超级管理员
*/
@TableName("visit_info")
@KeySequence("visit_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InfoDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 客户姓名
*/
private String customerName;
/**
* 联系方式(客户手机号)
*/
private String contact;
/**
* 客户公司名称
*/
private String companyName;
/**
* 省编码
*/
private String provinceCode;
/**
* 省名称
*/
private String provinceName;
/**
* 市编码
*/
private String cityCode;
/**
* 市名称
*/
private String cityName;
/**
* 区编码
*/
private String areaCode;
/**
* 区名称
*/
private String areaName;
/**
* 定位地址文字描述
*/
private String locationText;
/**
* 经度
*/
private BigDecimal longitude;
/**
* 纬度
*/
private BigDecimal latitude;
/**
* 定位静态图URL
*/
private String locationImage;
/**
* 拜访日期
*/
private LocalDate visitDate;
/**
* 性质等级
*/
private Integer customerStatus;
/**
* 拜访品种(多选,逗号分隔)
*/
private String visitProductIds;
/**
* 拜访方式(字典)
*
* 枚举 {@link TODO visit_method 对应的类}
*/
private Integer visitMethod;
/**
* 拜访类型(字典)
*
* 枚举 {@link TODO visit_type 对应的类}
*/
private Integer visitType;
/**
* 服务内容
*/
private String serviceContent;
/**
* 客户反馈
*/
private String customerFeedback;
/**
* 服务记录图片URL列表(JSON数组)
*/
private String serviceImages;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.dataobject.product;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 产品 DO
*
* @author 超级管理员
*/
@TableName("visit_product")
@KeySequence("visit_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 品种名称
*/
private String productName;
/**
* 包装信息
*/
private String packageName;
/**
* 规格
*/
private String specification;
/**
* 状态(1:正常,0:禁用)
*
* 枚举
*/
private Integer status;
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.mysql.customerinfo;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
/**
* 客户信息 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface CustomerInfoMapper extends BaseMapperX<CustomerInfoDO> {
default PageResult<CustomerInfoDO> selectPage(CustomerInfoPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerInfoDO>()
.likeIfPresent(CustomerInfoDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(CustomerInfoDO::getContact, reqVO.getContact())
.likeIfPresent(CustomerInfoDO::getCompanyName, reqVO.getCompanyName())
.eqIfPresent(CustomerInfoDO::getCustomerType, reqVO.getCustomerType())
.eqIfPresent(CustomerInfoDO::getCityCode, reqVO.getCityCode())
.betweenIfPresent(CustomerInfoDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CustomerInfoDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.mysql.info;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
/**
* 客户拜访记录 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface InfoMapper extends BaseMapperX<InfoDO> {
default PageResult<InfoDO> selectPage(InfoPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<InfoDO>()
.likeIfPresent(InfoDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(InfoDO::getContact, reqVO.getContact())
.likeIfPresent(InfoDO::getCompanyName, reqVO.getCompanyName())
.betweenIfPresent(InfoDO::getVisitDate, reqVO.getVisitDate())
.eqIfPresent(InfoDO::getCustomerStatus, reqVO.getCustomerStatus())
.eqIfPresent(InfoDO::getVisitProductIds, reqVO.getVisitProductIds())
.eqIfPresent(InfoDO::getVisitMethod, reqVO.getVisitMethod())
.eqIfPresent(InfoDO::getVisitType, reqVO.getVisitType())
.betweenIfPresent(InfoDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(InfoDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.dal.mysql.product;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
/**
* 产品 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface ProductMapper extends BaseMapperX<ProductDO> {
default PageResult<ProductDO> selectPage(ProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ProductDO>()
.likeIfPresent(ProductDO::getProductName, reqVO.getProductName())
.eqIfPresent(ProductDO::getPackageName, reqVO.getPackageName())
.eqIfPresent(ProductDO::getSpecification, reqVO.getSpecification())
.eqIfPresent(ProductDO::getStatus, reqVO.getStatus())
.betweenIfPresent(ProductDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProductDO::getId));
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/*
* System 错误码枚举类
*
* system 系统,使用 1-002-000-000 段
*/
public interface ErrorCodeConstants {
// ========== 产品 ==========
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1001000001, "产品不存在");
// ========== 客户信息 ==========
ErrorCode CUSTOMER_INFO_NOT_EXISTS = new ErrorCode(1002000001, "客户信息不存在");
// ========== 客户拜访记录 ==========
ErrorCode INFO_NOT_EXISTS = new ErrorCode(1003000001, "客户拜访记录不存在");
}
package cn.iocoder.yudao.module.visit.service.customerinfo;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 客户信息 Service 接口
*
* @author 超级管理员
*/
public interface CustomerInfoService {
/**
* 创建客户信息
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCustomerInfo(@Valid CustomerInfoSaveReqVO createReqVO);
/**
* 更新客户信息
*
* @param updateReqVO 更新信息
*/
void updateCustomerInfo(@Valid CustomerInfoSaveReqVO updateReqVO);
/**
* 删除客户信息
*
* @param id 编号
*/
void deleteCustomerInfo(Long id);
/**
* 获得客户信息
*
* @param id 编号
* @return 客户信息
*/
CustomerInfoDO getCustomerInfo(Long id);
/**
* 获得客户信息分页
*
* @param pageReqVO 分页查询
* @return 客户信息分页
*/
PageResult<CustomerInfoDO> getCustomerInfoPage(CustomerInfoPageReqVO pageReqVO);
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.customerinfo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.visit.dal.mysql.customerinfo.CustomerInfoMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
/**
* 客户信息 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class CustomerInfoServiceImpl implements CustomerInfoService {
@Resource
private CustomerInfoMapper customerInfoMapper;
@Override
public Long createCustomerInfo(CustomerInfoSaveReqVO createReqVO) {
// 插入
CustomerInfoDO customerInfo = BeanUtils.toBean(createReqVO, CustomerInfoDO.class);
customerInfoMapper.insert(customerInfo);
// 返回
return customerInfo.getId();
}
@Override
public void updateCustomerInfo(CustomerInfoSaveReqVO updateReqVO) {
// 校验存在
validateCustomerInfoExists(updateReqVO.getId());
// 更新
CustomerInfoDO updateObj = BeanUtils.toBean(updateReqVO, CustomerInfoDO.class);
customerInfoMapper.updateById(updateObj);
}
@Override
public void deleteCustomerInfo(Long id) {
// 校验存在
validateCustomerInfoExists(id);
// 删除
customerInfoMapper.deleteById(id);
}
private void validateCustomerInfoExists(Long id) {
if (customerInfoMapper.selectById(id) == null) {
throw exception(CUSTOMER_INFO_NOT_EXISTS);
}
}
@Override
public CustomerInfoDO getCustomerInfo(Long id) {
return customerInfoMapper.selectById(id);
}
@Override
public PageResult<CustomerInfoDO> getCustomerInfoPage(CustomerInfoPageReqVO pageReqVO) {
return customerInfoMapper.selectPage(pageReqVO);
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.info;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 客户拜访记录 Service 接口
*
* @author 超级管理员
*/
public interface InfoService {
/**
* 创建客户拜访记录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createInfo(@Valid InfoSaveReqVO createReqVO);
/**
* 更新客户拜访记录
*
* @param updateReqVO 更新信息
*/
void updateInfo(@Valid InfoSaveReqVO updateReqVO);
/**
* 删除客户拜访记录
*
* @param id 编号
*/
void deleteInfo(Long id);
/**
* 获得客户拜访记录
*
* @param id 编号
* @return 客户拜访记录
*/
InfoDO getInfo(Long id);
/**
* 获得客户拜访记录分页
*
* @param pageReqVO 分页查询
* @return 客户拜访记录分页
*/
PageResult<InfoDO> getInfoPage(InfoPageReqVO pageReqVO);
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.info;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.visit.dal.mysql.info.InfoMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
/**
* 客户拜访记录 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class InfoServiceImpl implements InfoService {
@Resource
private InfoMapper infoMapper;
@Override
public Long createInfo(InfoSaveReqVO createReqVO) {
// 插入
InfoDO info = BeanUtils.toBean(createReqVO, InfoDO.class);
infoMapper.insert(info);
// 返回
return info.getId();
}
@Override
public void updateInfo(InfoSaveReqVO updateReqVO) {
// 校验存在
validateInfoExists(updateReqVO.getId());
// 更新
InfoDO updateObj = BeanUtils.toBean(updateReqVO, InfoDO.class);
infoMapper.updateById(updateObj);
}
@Override
public void deleteInfo(Long id) {
// 校验存在
validateInfoExists(id);
// 删除
infoMapper.deleteById(id);
}
private void validateInfoExists(Long id) {
if (infoMapper.selectById(id) == null) {
throw exception(INFO_NOT_EXISTS);
}
}
@Override
public InfoDO getInfo(Long id) {
return infoMapper.selectById(id);
}
@Override
public PageResult<InfoDO> getInfoPage(InfoPageReqVO pageReqVO) {
return infoMapper.selectPage(pageReqVO);
}
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.product;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 产品 Service 接口
*
* @author 超级管理员
*/
public interface ProductService {
/**
* 创建产品
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createProduct(@Valid ProductSaveReqVO createReqVO);
/**
* 更新产品
*
* @param updateReqVO 更新信息
*/
void updateProduct(@Valid ProductSaveReqVO updateReqVO);
/**
* 删除产品
*
* @param id 编号
*/
void deleteProduct(Long id);
/**
* 获得产品
*
* @param id 编号
* @return 产品
*/
ProductDO getProduct(Long id);
/**
* 获得产品分页
*
* @param pageReqVO 分页查询
* @return 产品分页
*/
PageResult<ProductDO> getProductPage(ProductPageReqVO pageReqVO);
}
\ No newline at end of file
package cn.iocoder.yudao.module.visit.service.product;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.visit.dal.mysql.product.ProductMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
/**
* 产品 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class ProductServiceImpl implements ProductService {
@Resource
private ProductMapper productMapper;
@Override
public Long createProduct(ProductSaveReqVO createReqVO) {
// 插入
ProductDO product = BeanUtils.toBean(createReqVO, ProductDO.class);
productMapper.insert(product);
// 返回
return product.getId();
}
@Override
public void updateProduct(ProductSaveReqVO updateReqVO) {
// 校验存在
validateProductExists(updateReqVO.getId());
// 更新
ProductDO updateObj = BeanUtils.toBean(updateReqVO, ProductDO.class);
productMapper.updateById(updateObj);
}
@Override
public void deleteProduct(Long id) {
// 校验存在
validateProductExists(id);
// 删除
productMapper.deleteById(id);
}
private void validateProductExists(Long id) {
if (productMapper.selectById(id) == null) {
throw exception(PRODUCT_NOT_EXISTS);
}
}
@Override
public ProductDO getProduct(Long id) {
return productMapper.selectById(id);
}
@Override
public PageResult<ProductDO> getProductPage(ProductPageReqVO pageReqVO) {
return productMapper.selectPage(pageReqVO);
}
}
\ No newline at end of file
<?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="cn.iocoder.yudao.module.visit.dal.mysql.customerinfo.CustomerInfoMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>
\ No newline at end of file
<?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="cn.iocoder.yudao.module.visit.dal.mysql.info.InfoMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>
\ No newline at end of file
<?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="cn.iocoder.yudao.module.visit.dal.mysql.product.ProductMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>
\ No newline at end of file
......@@ -271,7 +271,7 @@ yudao:
front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
unit-test-enable: false # 是否生成单元测试
tenant: # 多租户相关配置项
enable: true
enable: false
ignore-urls:
- /jmreport/* # 积木报表,无法携带租户编号
ignore-visit-urls:
......
......@@ -8,7 +8,7 @@ VITE_PORT=80
VITE_OPEN=false
# 租户开关
VITE_APP_TENANT_ENABLE=true
VITE_APP_TENANT_ENABLE=false
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
......
import request from '@/config/axios'
// 客户信息 VO
export interface CustomerInfoVO {
id: number // 编号
customerName: string // 客户姓名
contact: string // 联系方式
companyName: string // 公司名称
customerType: number // 性质等级
provinceName: string // 省名称
cityName: string // 市名称
areaName: string // 区名称
regionFullName: string // 所在地区
locationText: string // 详细地址
longitude: number // 经度
latitude: number // 纬度
locationImage: string // 定位静态图 URL
productIds: string // 产品信息
department: string // 客户部门
}
// 客户信息 API
export const CustomerInfoApi = {
// 查询客户信息分页
getCustomerInfoPage: async (params: any) => {
return await request.get({ url: `/visit/customer-info/page`, params })
},
// 查询客户信息详情
getCustomerInfo: async (id: number) => {
return await request.get({ url: `/visit/customer-info/get?id=` + id })
},
// 新增客户信息
createCustomerInfo: async (data: CustomerInfoVO) => {
return await request.post({ url: `/visit/customer-info/create`, data })
},
// 修改客户信息
updateCustomerInfo: async (data: CustomerInfoVO) => {
return await request.put({ url: `/visit/customer-info/update`, data })
},
// 删除客户信息
deleteCustomerInfo: async (id: number) => {
return await request.delete({ url: `/visit/customer-info/delete?id=` + id })
},
// 导出客户信息 Excel
exportCustomerInfo: async (params) => {
return await request.download({ url: `/visit/customer-info/export-excel`, params })
},
}
\ No newline at end of file
import request from '@/config/axios'
// 客户拜访记录 VO
export interface InfoVO {
id: number // 主键ID
customerName: string // 客户姓名
contact: string // 联系方式(客户手机号)
companyName: string // 客户公司名称
provinceName: string // 省名称
cityName: string // 市名称
areaName: string // 区名称
locationText: string // 定位地址文字描述
longitude: number // 经度
latitude: number // 纬度
locationImage: string // 定位静态图URL
visitDate: Date // 拜访日期
customerStatus: number // 性质等级
visitProductIds: string // 拜访品种(多选,逗号分隔)
visitMethod: number // 拜访方式(字典)
visitType: number // 拜访类型(字典)
serviceContent: string // 服务内容
customerFeedback: string // 客户反馈
serviceImages: string // 服务记录图片URL列表(JSON数组)
}
// 客户拜访记录 API
export const InfoApi = {
// 查询客户拜访记录分页
getInfoPage: async (params: any) => {
return await request.get({ url: `/visit/info/page`, params })
},
// 查询客户拜访记录详情
getInfo: async (id: number) => {
return await request.get({ url: `/visit/info/get?id=` + id })
},
// 新增客户拜访记录
createInfo: async (data: InfoVO) => {
return await request.post({ url: `/visit/info/create`, data })
},
// 修改客户拜访记录
updateInfo: async (data: InfoVO) => {
return await request.put({ url: `/visit/info/update`, data })
},
// 删除客户拜访记录
deleteInfo: async (id: number) => {
return await request.delete({ url: `/visit/info/delete?id=` + id })
},
// 导出客户拜访记录 Excel
exportInfo: async (params) => {
return await request.download({ url: `/visit/info/export-excel`, params })
},
}
\ No newline at end of file
import request from '@/config/axios'
// 产品 VO
export interface ProductVO {
id: number // 编号
productName: string // 品种名称
packageName: string // 包装信息
specification: string // 规格
status: number // 状态(1:正常,0:禁用)
}
// 产品 API
export const ProductApi = {
// 查询产品分页
getProductPage: async (params: any) => {
return await request.get({ url: `/visit/product/page`, params })
},
// 查询产品详情
getProduct: async (id: number) => {
return await request.get({ url: `/visit/product/get?id=` + id })
},
// 新增产品
createProduct: async (data: ProductVO) => {
return await request.post({ url: `/visit/product/create`, data })
},
// 修改产品
updateProduct: async (data: ProductVO) => {
return await request.put({ url: `/visit/product/update`, data })
},
// 删除产品
deleteProduct: async (id: number) => {
return await request.delete({ url: `/visit/product/delete?id=` + id })
},
// 导出产品 Excel
exportProduct: async (params) => {
return await request.download({ url: `/visit/product/export-excel`, params })
},
}
......@@ -113,6 +113,12 @@ export enum DICT_TYPE {
TERMINAL = 'terminal', // 终端
DATE_INTERVAL = 'date_interval', // 数据间隔
// ========== VISIT 模块 ==========
CUSTOMER_TYPE = 'customer_type',
CUSTOMER_DEPT = 'customer_dept',
VISIT_METHOD = 'visit_method',
VISIT_TYPE = 'visit_type',
// ========== SYSTEM 模块 ==========
SYSTEM_USER_SEX = 'system_user_sex',
SYSTEM_MENU_TYPE = 'system_menu_type',
......
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="客户姓名" prop="customerName">
<el-input v-model="formData.customerName" placeholder="请输入客户姓名" />
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input v-model="formData.contact" placeholder="请输入联系方式" />
</el-form-item>
<el-form-item label="公司名称" prop="companyName">
<el-input v-model="formData.companyName" placeholder="请输入公司名称" />
</el-form-item>
<el-form-item label="性质等级" prop="customerType">
<el-select v-model="formData.customerType" placeholder="请选择性质等级">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CUSTOMER_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="省名称" prop="provinceName">
<el-input v-model="formData.provinceName" placeholder="请输入省名称" />
</el-form-item>
<el-form-item label="市名称" prop="cityName">
<el-input v-model="formData.cityName" placeholder="请输入市名称" />
</el-form-item>
<el-form-item label="区名称" prop="areaName">
<el-input v-model="formData.areaName" placeholder="请输入区名称" />
</el-form-item>
<el-form-item label="所在地区" prop="regionFullName">
<el-input v-model="formData.regionFullName" placeholder="请输入所在地区" />
</el-form-item>
<el-form-item label="详细地址" prop="locationText">
<el-input v-model="formData.locationText" placeholder="请输入详细地址" />
</el-form-item>
<el-form-item label="经度" prop="longitude">
<el-input v-model="formData.longitude" placeholder="请输入经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude">
<el-input v-model="formData.latitude" placeholder="请输入纬度" />
</el-form-item>
<el-form-item label="定位静态图 URL" prop="locationImage">
<UploadImg v-model="formData.locationImage" />
</el-form-item>
<el-form-item label="产品信息" prop="productIds">
<el-input v-model="formData.productIds" placeholder="请输入产品信息" />
</el-form-item>
<el-form-item label="客户部门" prop="department">
<el-select v-model="formData.department" placeholder="请选择客户部门">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.CUSTOMER_DEPT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { CustomerInfoApi, CustomerInfoVO } from '@/api/visit/customerinfo'
/** 客户信息 表单 */
defineOptions({ name: 'CustomerInfoForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
customerName: undefined,
contact: undefined,
companyName: undefined,
customerType: undefined,
provinceName: undefined,
cityName: undefined,
areaName: undefined,
regionFullName: undefined,
locationText: undefined,
longitude: undefined,
latitude: undefined,
locationImage: undefined,
productIds: undefined,
department: undefined,
})
const formRules = reactive({
customerName: [{ required: true, message: '客户姓名不能为空', trigger: 'blur' }],
contact: [{ required: true, message: '联系方式不能为空', trigger: 'blur' }],
companyName: [{ required: true, message: '公司名称不能为空', trigger: 'blur' }],
customerType: [{ required: true, message: '性质等级不能为空', trigger: 'change' }],
provinceName: [{ required: true, message: '省名称不能为空', trigger: 'blur' }],
cityName: [{ required: true, message: '市名称不能为空', trigger: 'blur' }],
areaName: [{ required: true, message: '区名称不能为空', trigger: 'blur' }],
regionFullName: [{ required: true, message: '所在地区不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }],
longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图 URL不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await CustomerInfoApi.getCustomerInfo(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as CustomerInfoVO
if (formType.value === 'create') {
await CustomerInfoApi.createCustomerInfo(data)
message.success(t('common.createSuccess'))
} else {
await CustomerInfoApi.updateCustomerInfo(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
customerName: undefined,
contact: undefined,
companyName: undefined,
customerType: undefined,
provinceName: undefined,
cityName: undefined,
areaName: undefined,
regionFullName: undefined,
locationText: undefined,
longitude: undefined,
latitude: undefined,
locationImage: undefined,
productIds: undefined,
department: undefined,
}
formRef.value?.resetFields()
}
</script>
\ No newline at end of file
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="客户姓名" prop="customerName">
<el-input
v-model="queryParams.customerName"
placeholder="请输入客户姓名"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input
v-model="queryParams.contact"
placeholder="请输入联系方式"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="公司名称" prop="companyName">
<el-input
v-model="queryParams.companyName"
placeholder="请输入公司名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="性质等级" prop="customerType">
<el-select
v-model="queryParams.customerType"
placeholder="请选择性质等级"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CUSTOMER_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['visit:customer-info:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['visit:customer-info:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="客户姓名" align="center" prop="customerName" />
<el-table-column label="联系方式" align="center" prop="contact" />
<el-table-column label="公司名称" align="center" prop="companyName" />
<el-table-column label="性质等级" align="center" prop="customerType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CUSTOMER_TYPE" :value="scope.row.customerType" />
</template>
</el-table-column>
<el-table-column label="所在地区" align="center" prop="regionFullName" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['visit:customer-info:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['visit:customer-info:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<CustomerInfoForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CustomerInfoApi, CustomerInfoVO } from '@/api/visit/customerinfo'
import CustomerInfoForm from './CustomerInfoForm.vue'
/** 客户信息 列表 */
defineOptions({ name: 'CustomerInfo' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<CustomerInfoVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
customerName: undefined,
contact: undefined,
companyName: undefined,
customerType: undefined,
cityCode: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CustomerInfoApi.getCustomerInfoPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await CustomerInfoApi.deleteCustomerInfo(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await CustomerInfoApi.exportCustomerInfo(queryParams)
download.excel(data, '客户信息.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="拜访人姓名" prop="customerName">
<el-input v-model="formData.customerName" placeholder="请输入拜访人姓名" />
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input v-model="formData.contact" placeholder="请输入联系方式" />
</el-form-item>
<el-form-item label="客户公司名称" prop="companyName">
<el-input v-model="formData.companyName" placeholder="请输入客户公司名称" />
</el-form-item>
<el-form-item label="省名称" prop="provinceName">
<el-input v-model="formData.provinceName" placeholder="请输入省名称" />
</el-form-item>
<el-form-item label="市名称" prop="cityName">
<el-input v-model="formData.cityName" placeholder="请输入市名称" />
</el-form-item>
<el-form-item label="区名称" prop="areaName">
<el-input v-model="formData.areaName" placeholder="请输入区名称" />
</el-form-item>
<el-form-item label="定位地址文字描述" prop="locationText">
<el-input v-model="formData.locationText" placeholder="请输入定位地址文字描述" />
</el-form-item>
<el-form-item label="经度" prop="longitude">
<el-input v-model="formData.longitude" placeholder="请输入经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude">
<el-input v-model="formData.latitude" placeholder="请输入纬度" />
</el-form-item>
<el-form-item label="定位静态图URL" prop="locationImage">
<UploadImg v-model="formData.locationImage" />
</el-form-item>
<el-form-item label="拜访日期" prop="visitDate">
<el-date-picker
v-model="formData.visitDate"
type="date"
value-format="x"
placeholder="选择拜访日期"
/>
</el-form-item>
<el-form-item label="性质等级" prop="customerStatus">
<el-radio-group v-model="formData.customerStatus">
<el-radio value="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="拜访品种" prop="visitProductIds">
<el-input v-model="formData.visitProductIds" placeholder="请输入拜访品种" />
</el-form-item>
<el-form-item label="拜访方式" prop="visitMethod">
<el-select v-model="formData.visitMethod" placeholder="请选择拜访方式">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.VISIT_METHOD)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="拜访类型" prop="visitType">
<el-select v-model="formData.visitType" placeholder="请选择拜访类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.VISIT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="服务内容" prop="serviceContent">
<Editor v-model="formData.serviceContent" height="150px" />
</el-form-item>
<el-form-item label="客户反馈" prop="customerFeedback">
<el-input v-model="formData.customerFeedback" placeholder="请输入客户反馈" />
</el-form-item>
<el-form-item label="服务记录图片URL列表" prop="serviceImages">
<UploadImg v-model="formData.serviceImages" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { InfoApi, InfoVO } from '@/api/visit/info'
/** 客户拜访记录 表单 */
defineOptions({ name: 'InfoForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
customerName: undefined,
contact: undefined,
companyName: undefined,
provinceName: undefined,
cityName: undefined,
areaName: undefined,
locationText: undefined,
longitude: undefined,
latitude: undefined,
locationImage: undefined,
visitDate: undefined,
customerStatus: undefined,
visitProductIds: undefined,
visitMethod: undefined,
visitType: undefined,
serviceContent: undefined,
customerFeedback: undefined,
serviceImages: undefined,
})
const formRules = reactive({
customerName: [{ required: true, message: '拜访人姓名不能为空', trigger: 'blur' }],
contact: [{ required: true, message: '联系方式不能为空', trigger: 'blur' }],
companyName: [{ required: true, message: '客户公司名称不能为空', trigger: 'blur' }],
provinceName: [{ required: true, message: '省名称不能为空', trigger: 'blur' }],
cityName: [{ required: true, message: '市名称不能为空', trigger: 'blur' }],
areaName: [{ required: true, message: '区名称不能为空', trigger: 'blur' }],
locationText: [{ required: true, message: '定位地址文字描述不能为空', trigger: 'blur' }],
longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }],
locationImage: [{ required: true, message: '定位静态图URL不能为空', trigger: 'blur' }],
visitDate: [{ required: true, message: '拜访日期不能为空', trigger: 'blur' }],
customerStatus: [{ required: true, message: '性质等级不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await InfoApi.getInfo(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as InfoVO
if (formType.value === 'create') {
await InfoApi.createInfo(data)
message.success(t('common.createSuccess'))
} else {
await InfoApi.updateInfo(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
customerName: undefined,
contact: undefined,
companyName: undefined,
provinceName: undefined,
cityName: undefined,
areaName: undefined,
locationText: undefined,
longitude: undefined,
latitude: undefined,
locationImage: undefined,
visitDate: undefined,
customerStatus: undefined,
visitProductIds: undefined,
visitMethod: undefined,
visitType: undefined,
serviceContent: undefined,
customerFeedback: undefined,
serviceImages: undefined,
}
formRef.value?.resetFields()
}
</script>
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="98px"
>
<el-form-item label="拜访人姓名" prop="customerName">
<el-input
v-model="queryParams.customerName"
placeholder="请输入拜访人姓名"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="联系方式" prop="contact">
<el-input
v-model="queryParams.contact"
placeholder="请输入联系方式"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="客户公司名称" prop="companyName">
<el-input
v-model="queryParams.companyName"
placeholder="请输入客户公司名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="拜访日期" prop="visitDate">
<el-date-picker
v-model="queryParams.visitDate"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item label="性质等级" prop="customerStatus">
<el-select
v-model="queryParams.customerStatus"
placeholder="请选择性质等级"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="拜访品种" prop="visitProductIds">
<el-input
v-model="queryParams.visitProductIds"
placeholder="请输入拜访品种"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="拜访方式" prop="visitMethod">
<el-select
v-model="queryParams.visitMethod"
placeholder="请选择拜访方式"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.VISIT_METHOD)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="拜访类型" prop="visitType">
<el-select
v-model="queryParams.visitType"
placeholder="请选择拜访类型"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.VISIT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['visit:info:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['visit:info:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="主键ID" align="center" prop="id" />
<el-table-column label="拜访人姓名" align="center" prop="customerName" width="100px"/>
<el-table-column label="联系方式" align="center" prop="contact" />
<el-table-column label="客户公司名称" align="center" prop="companyName" width="120px" />
<el-table-column label="拜访日期" align="center" prop="visitDate" />
<el-table-column label="性质等级" align="center" prop="customerStatus" />
<el-table-column label="拜访品种" align="center" prop="visitProductIds" />
<el-table-column label="拜访方式" align="center" prop="visitMethod">
<template #default="scope">
<dict-tag :type="DICT_TYPE.VISIT_METHOD" :value="scope.row.visitMethod" />
</template>
</el-table-column>
<el-table-column label="拜访类型" align="center" prop="visitType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.VISIT_TYPE" :value="scope.row.visitType" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['visit:info:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['visit:info:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<InfoForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { InfoApi, InfoVO } from '@/api/visit/info'
import InfoForm from './InfoForm.vue'
/** 客户拜访记录 列表 */
defineOptions({ name: 'Info' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<InfoVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
customerName: undefined,
contact: undefined,
companyName: undefined,
visitDate: [],
customerStatus: undefined,
visitProductIds: undefined,
visitMethod: undefined,
visitType: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await InfoApi.getInfoPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await InfoApi.deleteInfo(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await InfoApi.exportInfo(queryParams)
download.excel(data, '客户拜访记录.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="品种名称" prop="productName">
<el-input v-model="formData.productName" placeholder="请输入品种名称" />
</el-form-item>
<el-form-item label="包装信息" prop="packageName">
<el-input v-model="formData.packageName" placeholder="请输入包装信息" />
</el-form-item>
<el-form-item label="规格" prop="specification">
<el-input v-model="formData.specification" placeholder="请输入规格" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { ProductApi, ProductVO } from '@/api/visit/product'
/** 产品 表单 */
defineOptions({ name: 'ProductForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
productName: undefined,
packageName: undefined,
specification: undefined,
status: undefined,
})
const formRules = reactive({
productName: [{ required: true, message: '品种名称不能为空', trigger: 'blur' }],
packageName: [{ required: true, message: '包装信息不能为空', trigger: 'blur' }],
specification: [{ required: true, message: '规格不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProductApi.getProduct(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProductVO
if (formType.value === 'create') {
await ProductApi.createProduct(data)
message.success(t('common.createSuccess'))
} else {
await ProductApi.updateProduct(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
productName: undefined,
packageName: undefined,
specification: undefined,
status: undefined,
}
formRef.value?.resetFields()
}
</script>
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="品种名称" prop="productName">
<el-input
v-model="queryParams.productName"
placeholder="请输入品种名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="包装信息" prop="packageName">
<el-input
v-model="queryParams.packageName"
placeholder="请输入包装信息"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="规格" prop="specification">
<el-input
v-model="queryParams.specification"
placeholder="请输入规格"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['visit:product:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['visit:product:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="品种名称" align="center" prop="productName" />
<el-table-column label="包装信息" align="center" prop="packageName" />
<el-table-column label="规格" align="center" prop="specification" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['visit:product:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['visit:product:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ProductForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProductApi, ProductVO } from '@/api/visit/product'
import ProductForm from './ProductForm.vue'
/** 产品 列表 */
defineOptions({ name: 'Product' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<ProductVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
productName: undefined,
packageName: undefined,
specification: undefined,
status: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ProductApi.getProductPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ProductApi.deleteProduct(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ProductApi.exportProduct(queryParams)
download.excel(data, '产品.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment