0407-北仑客群+客群业绩统计+网格整体业绩修改+青田贷款客户经理

This commit is contained in:
2026-04-07 18:58:11 +08:00
parent 3938004783
commit 803dbf2aa5
45 changed files with 3457 additions and 1119 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"kiroAgent.configureMCP": "Disabled"
}

View File

@@ -9,7 +9,6 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.group.domain.dto.CustGroupMemberTemplate;
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
import com.ruoyi.group.domain.dto.GridImportDTO;
import com.ruoyi.group.domain.entity.CustGroup;
import com.ruoyi.group.domain.vo.CustGroupVO;
import com.ruoyi.group.service.ICustGroupService;
@@ -60,48 +59,31 @@ public class CustGroupController extends BaseController {
return AjaxResult.success(custGroup);
}
/**
* 异步创建客群(网格导入)
*/
@ApiOperation("异步创建客群(网格导入)")
@Log(title = "客群管理-网格导入创建客群", businessType = BusinessType.INSERT)
@PostMapping("/createByGrid")
public AjaxResult createCustGroupByGrid(@RequestBody @Valid GridImportDTO gridImportDTO) {
String id = custGroupService.createCustGroupByGrid(gridImportDTO);
return AjaxResult.success(id);
}
/**
* 异步创建客群(模板导入)
* gridType、regionGridIds、drawGridIds 包含在 dto 中
*/
@ApiOperation("异步创建客群(模板导入)")
@Log(title = "客群管理-异步创建客群", businessType = BusinessType.INSERT)
@PostMapping(value = "/createByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult createCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson,
@RequestPart("file") MultipartFile file) {
public AjaxResult createCustGroupByTemplate(
@RequestPart("dto") @Valid String dtoJson,
@RequestPart("file") MultipartFile file) {
CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class);
return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file));
}
/**
* 更新客群(网格导入)
*/
@ApiOperation("更新客群(网格导入)")
@Log(title = "客群管理-更新客群(网格导入)", businessType = BusinessType.UPDATE)
@PostMapping("/updateByGrid")
public AjaxResult updateCustGroupByGrid(@RequestBody @Valid GridImportDTO gridImportDTO) {
String result = custGroupService.updateCustGroupByGrid(gridImportDTO);
return AjaxResult.success(result);
return AjaxResult.success("操作成功", custGroupService.createCustGroupByTemplate(custGroup, file));
}
/**
* 更新客群(模板导入)
* gridType、regionGridIds、drawGridIds 包含在 dto 中
* file 参数可选:不传文件则只更新客群信息,传文件则追加客户
*/
@ApiOperation("更新客群(模板导入)")
@Log(title = "客群管理-更新客群(模板导入)", businessType = BusinessType.UPDATE)
@PostMapping(value = "/updateByTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult updateCustGroupByTemplate(@RequestPart("dto") @Valid String dtoJson,
@RequestPart("file") MultipartFile file) {
public AjaxResult updateCustGroupByTemplate(
@RequestPart("dto") @Valid String dtoJson,
@RequestPart(value = "file", required = false) MultipartFile file) {
CustGroup custGroup = JSON.parseObject(dtoJson, CustGroup.class);
return AjaxResult.success(custGroupService.updateCustGroupByTemplate(custGroup, file));
}
@@ -114,7 +96,7 @@ public class CustGroupController extends BaseController {
@GetMapping("/createStatus/{id}")
public AjaxResult getCreateStatus(@PathVariable Long id) {
String status = custGroupService.getCreateStatus(id);
return AjaxResult.success(status);
return AjaxResult.success("操作成功", status);
}
/**
@@ -139,4 +121,15 @@ public class CustGroupController extends BaseController {
return AjaxResult.success(result);
}
/**
* 获取所有已有的客群标签列表
*/
@ApiOperation("获取所有客群标签")
@Log(title = "客群管理-获取客群标签")
@GetMapping("/tags")
public AjaxResult getAllGroupTags() {
List<String> tags = custGroupService.getAllGroupTags();
return AjaxResult.success(tags);
}
}

View File

@@ -36,7 +36,7 @@ public class CustGroupMemberController extends BaseController {
@GetMapping("/list/{groupId}")
public TableDataInfo listCustGroupMembers(@PathVariable Long groupId,
CustGroupMemberQueryDTO dto) {
startPage();
// 注意:startPage()在Service内部调用因为权限检查会先执行SQL导致分页失效
List<CustGroupMemberVO> list = custGroupMemberService.listCustGroupMembers(groupId, dto);
return getDataTable(list);
}

View File

@@ -0,0 +1,125 @@
package com.ruoyi.group.controller;
import com.github.pagehelper.Page;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825;
import com.ruoyi.group.domain.entity.GroupCustCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCustCountLingshou825;
import com.ruoyi.group.service.IGroupPerformanceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Api(tags = "客群业绩统计")
@RestController
@RequestMapping("/group/performance")
public class GroupPerformanceController extends BaseController {
@Resource
private IGroupPerformanceService groupPerformanceService;
@ApiOperation("查询零售客群业绩汇总列表")
@GetMapping("/ls/list")
public TableDataPageInfo<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName) {
Page<Object> page = startPage();
List<GroupCmpmCountLingshou825> list = groupPerformanceService.selectLsCountList(dt, groupName);
return getDataTable(list, page);
}
@ApiOperation("查询公司客群业绩汇总列表")
@GetMapping("/gs/list")
public TableDataPageInfo<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName) {
Page<Object> page = startPage();
List<GroupCmpmCountGongsi825> list = groupPerformanceService.selectGsCountList(dt, groupName);
return getDataTable(list, page);
}
@ApiOperation("查询零售客群客户明细")
@GetMapping("/ls/custList")
public TableDataPageInfo<GroupCustCountLingshou825> selectLsCustList(@RequestParam String groupId,
String custName,
String custIdc,
String dt) {
Page<Object> page = startPage();
List<GroupCustCountLingshou825> list = groupPerformanceService.selectLsCustList(groupId, custName, custIdc, dt);
return getDataTable(list, page);
}
@ApiOperation("查询公司客群客户明细")
@GetMapping("/gs/custList")
public TableDataPageInfo<GroupCustCountGongsi825> selectGsCustList(@RequestParam String groupId,
String custName,
String socialCreditCode,
String dt) {
Page<Object> page = startPage();
List<GroupCustCountGongsi825> list = groupPerformanceService.selectGsCustList(groupId, custName, socialCreditCode, dt);
return getDataTable(list, page);
}
@Log(title = "导出零售客群业绩汇总")
@ApiOperation(value = "导出零售客群业绩汇总", produces = "application/octet-stream")
@GetMapping("/exportLs")
public void exportLs(HttpServletResponse response, String dt, String groupName) {
try {
ExcelUtil<GroupCmpmCountLingshou825> util = new ExcelUtil<>(GroupCmpmCountLingshou825.class);
util.exportExcel(response, groupPerformanceService.selectLsCountList(dt, groupName), "零售客群业绩汇总");
} catch (Exception e) {
logger.error("导出零售客群业绩汇总失败", e);
}
}
@Log(title = "导出公司客群业绩汇总")
@ApiOperation(value = "导出公司客群业绩汇总", produces = "application/octet-stream")
@GetMapping("/exportGs")
public void exportGs(HttpServletResponse response, String dt, String groupName) {
try {
ExcelUtil<GroupCmpmCountGongsi825> util = new ExcelUtil<>(GroupCmpmCountGongsi825.class);
util.exportExcel(response, groupPerformanceService.selectGsCountList(dt, groupName), "公司客群业绩汇总");
} catch (Exception e) {
logger.error("导出公司客群业绩汇总失败", e);
}
}
@Log(title = "导出零售客群客户明细")
@ApiOperation(value = "导出零售客群客户明细", produces = "application/octet-stream")
@GetMapping("/exportLsCust")
public void exportLsCust(HttpServletResponse response,
@RequestParam String groupId,
String custName,
String custIdc,
String dt) {
try {
ExcelUtil<GroupCustCountLingshou825> util = new ExcelUtil<>(GroupCustCountLingshou825.class);
util.exportExcel(response, groupPerformanceService.selectLsCustList(groupId, custName, custIdc, dt), "零售客群客户明细");
} catch (Exception e) {
logger.error("导出零售客群客户明细失败", e);
}
}
@Log(title = "导出公司客群客户明细")
@ApiOperation(value = "导出公司客群客户明细", produces = "application/octet-stream")
@GetMapping("/exportGsCust")
public void exportGsCust(HttpServletResponse response,
@RequestParam String groupId,
String custName,
String socialCreditCode,
String dt) {
try {
ExcelUtil<GroupCustCountGongsi825> util = new ExcelUtil<>(GroupCustCountGongsi825.class);
util.exportExcel(response, groupPerformanceService.selectGsCustList(groupId, custName, socialCreditCode, dt), "公司客群客户明细");
} catch (Exception e) {
logger.error("导出公司客群客户明细失败", e);
}
}
}

View File

@@ -13,6 +13,18 @@ import lombok.Data;
@ApiModel(description = "客群客户查询条件")
public class CustGroupMemberQueryDTO {
/**
* 页码
*/
@ApiModelProperty(value = "页码", hidden = true)
private Integer pageNum;
/**
* 每页数量
*/
@ApiModelProperty(value = "每页数量", hidden = true)
private Integer pageSize;
/**
* 客户类型0=个人, 1=商户, 2=企业
*/
@@ -24,4 +36,16 @@ public class CustGroupMemberQueryDTO {
*/
@ApiModelProperty(value = "客户姓名")
private String custName;
/**
* 客户经理柜员号
*/
@ApiModelProperty(value = "客户经理柜员号")
private String userName;
/**
* 客户经理姓名
*/
@ApiModelProperty(value = "客户经理姓名")
private String nickName;
}

View File

@@ -39,6 +39,12 @@ public class CustGroupQueryDTO implements Serializable {
@ApiModelProperty(value = "客群状态", name = "groupStatus")
private String groupStatus;
/**
* 客群标签(模糊匹配)
*/
@ApiModelProperty(value = "客群标签", name = "groupTags")
private String groupTags;
/**
* 视图类型mine=我创建的sharedToMe=下发给我的
*/

View File

@@ -38,10 +38,10 @@ public class CustGroup {
private String groupMode;
/**
* 创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格
* 客群类型0=零售类(个人+商户), 1=公司类(企业)
*/
@ApiModelProperty(value = "创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode")
private String createMode;
@ApiModelProperty(value = "客群类型0=零售类(个人+商户), 1=公司类(企业)", name = "groupType")
private String groupType;
/**
* 柜员号
@@ -123,23 +123,18 @@ public class CustGroup {
private String remark;
/**
* 网格类型0=绩效网格, 1=地理网格, 2=绘制网格(动态客群使用
* 客群标签(多个标签用逗号分隔
*/
@ApiModelProperty(value = "网格类型", name = "gridType")
@ApiModelProperty(value = "客群标签(多个标签用逗号分隔)", name = "groupTags")
@TableField("group_tags")
private String groupTags;
/**
* 网格类型0=绩效网格, 1=地理网格, 2=绘制网格(管户关系来源)
*/
@ApiModelProperty(value = "网格类型管户关系来源0=绩效网格, 1=地理网格, 2=绘制网格", name = "gridType")
private String gridType;
/**
* 绩效业务类型retail=零售, corporate=公司(动态客群使用)
*/
@ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType")
private String cmpmBizType;
/**
* 客户经理列表(逗号分隔,动态客群使用)
*/
@ApiModelProperty(value = "客户经理列表", name = "gridUserNames")
private String gridUserNames;
/**
* 地理网格ID列表逗号分隔动态客群使用
*/

View File

@@ -60,6 +60,48 @@ public class CustGroupMember {
@ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode")
private String socialCreditCode;
/**
* 客户经理柜员号
*/
@ApiModelProperty(value = "客户经理柜员号", name = "userName")
@TableField("user_name")
private String userName;
/**
* 客户经理姓名
*/
@ApiModelProperty(value = "客户经理姓名", name = "nickName")
@TableField("nick_name")
private String nickName;
/**
* 网点ID
*/
@ApiModelProperty(value = "网点ID", name = "outletId")
@TableField("outlet_id")
private Long outletId;
/**
* 网点名称
*/
@ApiModelProperty(value = "网点名称", name = "outletName")
@TableField("outlet_name")
private String outletName;
/**
* 支行ID
*/
@ApiModelProperty(value = "支行ID", name = "branchId")
@TableField("branch_id")
private Long branchId;
/**
* 支行名称
*/
@ApiModelProperty(value = "支行名称", name = "branchName")
@TableField("branch_name")
private String branchName;
/**
* 创建者
*/
@@ -71,6 +113,12 @@ public class CustGroupMember {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 删除标识0=正常, 1=删除
*/

View File

@@ -0,0 +1,199 @@
package com.ruoyi.group.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 客群业绩汇总统计_公司825
*/
@Data
public class GroupCmpmCountGongsi825 implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "统计日期")
private String dt;
private String groupId;
@Excel(name = "客群名称")
private String groupName;
@Excel(name = "客群模式")
private String groupMode;
@Excel(name = "归属支行")
private String deptId;
@Excel(name = "归属支行名称")
private String deptName;
@Excel(name = "归属网点")
private String outletsId;
@Excel(name = "归属网点名称")
private String outletsName;
@Excel(name = "归属客户经理")
private String userName;
@Excel(name = "入格客户数")
private Integer custNum;
@Excel(name = "活期存款余额")
private String hqCurBalance;
@Excel(name = "保证金存款余额")
private String bzCurBalance;
@Excel(name = "贷款余额")
private String loanBalanceCny;
@Excel(name = "贴现余额")
private String financeProd711Balance;
@Excel(name = "承兑汇票余额")
private String financeProd716Balance;
@Excel(name = "贷款年日均")
private String loanYearDailyaverage;
@Excel(name = "签发承兑汇票率")
private String qfcdRat;
@Excel(name = "贴现业务率")
private String txRat;
@Excel(name = "保函业务率")
private String bhRat;
@Excel(name = "有效代发工资率")
private String yxdfgzRat;
@Excel(name = "代扣电费率")
private String dkdfRat;
@Excel(name = "代扣水费率")
private String dksfRat;
@Excel(name = "代扣税费率")
private String dkshfRat;
@Excel(name = "票据宝签约率")
private String pjbRat;
@Excel(name = "财资宝签约率")
private String czbRat;
@Excel(name = "收付宝签约率")
private String sfbRat;
@Excel(name = "贸融宝签约率")
private String mrbRat;
@Excel(name = "数字生态产品签约率")
private String szstRat;
@Excel(name = "开户率")
private String khRat;
@Excel(name = "国际结算业务率")
private String gjjsywRat;
@Excel(name = "远期结算汇业务率")
private String yqjshRat;
@Excel(name = "签发承兑汇票数")
private Integer qfcdNum;
@Excel(name = "贴现业务数")
private Integer txNum;
@Excel(name = "保函业务数")
private Integer bhNum;
@Excel(name = "有效代发工资数")
private Integer yxdfgzNum;
@Excel(name = "月均代发工资笔数")
private String ustrCountPerM;
@Excel(name = "月均代发工资金额(元)")
private String ustrBalM;
@Excel(name = "代扣电费数")
private Integer dkdfNum;
@Excel(name = "代扣水费数")
private Integer dksfNum;
@Excel(name = "代扣税费数")
private Integer dkshfNum;
@Excel(name = "票据宝签约数")
private Integer pjbNum;
@Excel(name = "财资宝签约数")
private Integer czbNum;
@Excel(name = "收付宝签约数")
private Integer sfbNum;
@Excel(name = "贸融宝签约数")
private Integer mrbNum;
@Excel(name = "数字生态产品签约数")
private Integer szstNum;
@Excel(name = "开户数")
private Integer khNum;
@Excel(name = "国际结算业务数")
private Integer gjjsywNum;
@Excel(name = "远期结算汇业务数")
private Integer yqjshNum;
private String regionCode;
private String opsDept;
@Excel(name = "合同签约率")
private String htqyRat;
@Excel(name = "合同签约数")
private Integer htqyNum;
private String deptType;
@Excel(name = "近365天已走访人数")
private String zf365cnt;
@Excel(name = "近180天已走访人数")
private String zf180cnt;
@Excel(name = "近90天已走访人数")
private String zf90cnt;
@Excel(name = "近30天已走访人数")
private String zf30cnt;
@Excel(name = "近365天走访率")
private String zf365rt;
@Excel(name = "近180天走访率")
private String zf180rt;
@Excel(name = "近90天走访率")
private String zf90rt;
@Excel(name = "近30天走访率")
private String zf30rt;
@Excel(name = "建档率")
private String phRat;
@Excel(name = "建档数")
private String phNum;
}

View File

@@ -0,0 +1,158 @@
package com.ruoyi.group.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 客群业绩汇总统计_零售825
*/
@Data
public class GroupCmpmCountLingshou825 implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "统计日期")
private String dt;
private String groupId;
@Excel(name = "客群名称")
private String groupName;
@Excel(name = "客群模式")
private String groupMode;
@Excel(name = "归属支行机构号")
private String deptId;
@Excel(name = "归属支行名称")
private String deptName;
@Excel(name = "归属网点机构号")
private String outletsId;
@Excel(name = "归属网点名称")
private String outletsName;
@Excel(name = "归属客户经理")
private String userName;
@Excel(name = "入格客户数")
private Integer custNum;
@Excel(name = "近365天已走访人数")
private String zf365cnt;
@Excel(name = "近180天已走访人数")
private String zf180cnt;
@Excel(name = "近90天已走访人数")
private String zf90cnt;
@Excel(name = "近30天已走访人数")
private String zf30cnt;
@Excel(name = "近365天走访率")
private String zf365rt;
@Excel(name = "近180天走访率")
private String zf180rt;
@Excel(name = "近90天走访率")
private String zf90rt;
@Excel(name = "近30天走访率")
private String zf30rt;
@Excel(name = "活期存款余额(元)")
private String curBalD;
@Excel(name = "授信率(%")
private String sxRat;
@Excel(name = "用信覆盖率")
private String yxRat;
@Excel(name = "授信户数")
private Integer sxNum;
@Excel(name = "用信户数")
private Integer yxNum;
@Excel(name = "授信金额(合同)")
private String sxBal;
@Excel(name = "贷款余额(元)")
private String balLoan;
@Excel(name = "贷款年日均(元)")
private String loanAve;
@Excel(name = "合同签约率(%")
private String yxhtRat;
@Excel(name = "代扣电费覆盖率(%")
private String dianRat;
@Excel(name = "代扣水费率(%")
private String shuiRat;
@Excel(name = "代扣税费率(%")
private String taxRat;
@Excel(name = "开户率(%")
private String openRat;
@Excel(name = "合同签约客户数")
private Integer yxhtNum;
@Excel(name = "代扣电费客户数")
private Integer dianNum;
@Excel(name = "代扣水费客户数")
private Integer shuiNum;
@Excel(name = "代扣税费数")
private Integer taxNum;
@Excel(name = "开户数")
private Integer openNum;
@Excel(name = "存款余额(元)")
private String depBal;
@Excel(name = "财富余额(元)")
private String finBal;
@Excel(name = "个人核心客户数")
private Integer grhxNum;
@Excel(name = "财富有效客户数")
private Integer cfyxNum;
@Excel(name = "有效信用卡数")
private Integer yxxykNum;
@Excel(name = "有效社保卡户数")
private Integer yxsbkNum;
@Excel(name = "二换三社保卡户数")
private Integer twoTo3SbkNum;
@Excel(name = "养老金迁移至社保卡户数")
private Integer yljToSbkNum;
@Excel(name = "丰收互联客户数")
private Integer fshlNum;
@Excel(name = "有效收单商户")
private Integer yxsdNum;
@Excel(name = "核心收单户数")
private String hxsdNum;
private String regionCode;
private String opsDept;
}

View File

@@ -0,0 +1,138 @@
package com.ruoyi.group.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 客群客户明细统计_公司825
*/
@Data
public class GroupCustCountGongsi825 implements Serializable {
private static final long serialVersionUID = 1L;
private String groupId;
@Excel(name = "客户名称")
private String custName;
@Excel(name = "客户证件号")
private String socialCreditCode;
@Excel(name = "客户内码")
private String custIsn;
@Excel(name = "活期存款余额")
private String hqCurBalance;
@Excel(name = "保证金存款余额")
private String bzCurBalance;
@Excel(name = "是否授信")
private String isCredit;
@Excel(name = "贷款余额")
private String loanBalanceCny;
@Excel(name = "贷款年日均")
private String loanYearDailyaverage;
@Excel(name = "是否有签发承兑汇票")
private String financeProd716OpenFlag;
@Excel(name = "承兑汇票余额")
private String financeProd716Balance;
@Excel(name = "是否有贴现业务")
private String financeProd711OpenFlag;
@Excel(name = "贴现金额")
private String financeProd711Balance;
@Excel(name = "是否有保函业务")
private String intlBussinessJcbhOpenFlag;
@Excel(name = "是否为有效代发工资客户")
private String isUstr;
@Excel(name = "月均代发工资笔数")
private String ustrCountPerM;
@Excel(name = "月均代发工资金额(元)")
private String ustrBalM;
@Excel(name = "是否代扣电费")
private String elecchargeSignFlag;
@Excel(name = "是否代扣水费")
private String waterchargeSignFlag;
@Excel(name = "是否代扣税费")
private String taxdeductionSignFlag;
@Excel(name = "是否票据宝签约")
private String pjb;
@Excel(name = "是否财资宝签约")
private String czb;
@Excel(name = "是否收付宝签约")
private String sfb;
@Excel(name = "是否贸融宝签约")
private String mrb;
@Excel(name = "是否数字生态产品签约")
private String szst;
@Excel(name = "是否开户")
private String isOpenSts;
@Excel(name = "是否国际结算业务")
private String intlBussinessOpenFlag;
@Excel(name = "是否有远期结算汇业务")
private String intlBussiness325OpenFlag;
private String regionCode;
private String opsDept;
private String custType;
@Excel(name = "是否合同签约")
private String isHtqy;
@Excel(name = "归属支行名称")
private String deptName;
@Excel(name = "归属网点id")
private String outletsId;
@Excel(name = "归属网点名称")
private String outletsName;
@Excel(name = "归属客户经理")
private String userName;
@Excel(name = "归属支行id")
private String deptId;
@Excel(name = "近365天有无走访")
private String is365zf;
@Excel(name = "近180天有无走访")
private String is180zf;
@Excel(name = "近90天有无走访")
private String is90zf;
@Excel(name = "近30天有无走访")
private String is30zf;
private String dt;
@Excel(name = "是否建档")
private String isPh;
}

View File

@@ -0,0 +1,111 @@
package com.ruoyi.group.domain.entity;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
/**
* 客群客户明细统计_零售825
*/
@Data
public class GroupCustCountLingshou825 implements Serializable {
private static final long serialVersionUID = 1L;
private String groupId;
@Excel(name = "客户名称")
private String custName;
@Excel(name = "客户证件号")
private String custIdc;
@Excel(name = "客户内码")
private String custIsn;
@Excel(name = "活期存款余额")
private String curBalD;
@Excel(name = "贷款余额")
private String balLoan;
@Excel(name = "贷款年日均")
private String loanAve;
@Excel(name = "是否授信")
private String isSx;
@Excel(name = "是否用信")
private String isYx;
@Excel(name = "授信金额")
private String sxBal;
@Excel(name = "是否合同签约")
private String isYxht;
@Excel(name = "是否持有信用卡")
private String isXyk;
@Excel(name = "是否开通丰收互联")
private String fshl;
@Excel(name = "是否办理收单")
private String isSd;
@Excel(name = "是否代扣电费")
private String dian;
@Excel(name = "是否代扣水费")
private String shui;
@Excel(name = "是否代扣税费")
private String tax;
@Excel(name = "开户数")
private String openNum;
@Excel(name = "存款余额")
private String depBal;
@Excel(name = "财富余额")
private String finBal;
@Excel(name = "是否个人核心客户")
private String isGrhx;
@Excel(name = "是否财富有效客户")
private String isCfyx;
@Excel(name = "是否有效社保卡客户")
private String isYxsbk;
@Excel(name = "是否二换三社保卡")
private String is2to3Sbk;
@Excel(name = "是否养老金迁移至社保卡")
private String isYljToSbk;
private String regionCode;
private String opsDept;
private String custType;
@Excel(name = "近365天有无走访")
private String is365zf;
@Excel(name = "近180天有无走访")
private String is180zf;
@Excel(name = "近90天有无走访")
private String is90zf;
@Excel(name = "近30天有无走访")
private String is30zf;
private String dt;
@Excel(name = "是否核心收单")
private String isHxsd;
}

View File

@@ -57,10 +57,52 @@ public class CustGroupMemberVO {
@ApiModelProperty(value = "统信码(商户/企业有)", name = "socialCreditCode")
private String socialCreditCode;
/**
* 客户经理柜员号
*/
@ApiModelProperty(value = "客户经理柜员号", name = "userName")
private String userName;
/**
* 客户经理姓名
*/
@ApiModelProperty(value = "客户经理姓名", name = "nickName")
private String nickName;
/**
* 网点ID
*/
@ApiModelProperty(value = "网点ID", name = "outletId")
private Long outletId;
/**
* 网点名称
*/
@ApiModelProperty(value = "网点名称", name = "outletName")
private String outletName;
/**
* 支行ID
*/
@ApiModelProperty(value = "支行ID", name = "branchId")
private Long branchId;
/**
* 支行名称
*/
@ApiModelProperty(value = "支行名称", name = "branchName")
private String branchName;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间", name = "createTime")
private Date createTime;
/**
* 存量状态true=存量客户系统存在false=非存量客户(系统不存在)
*/
@ApiModelProperty(value = "存量状态true=存量客户false=非存量客户", name = "isExisting")
private Boolean isExisting;
}

View File

@@ -35,10 +35,10 @@ public class CustGroupVO {
private String groupMode;
/**
* 创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格
* 客群类型0=零售类(个人+商户), 1=公司类(企业)
*/
@ApiModelProperty(value = "创建方式1=模板导入, 2=绩效网格, 3=地理网格, 4=自定义网格", name = "createMode")
private String createMode;
@ApiModelProperty(value = "客群类型0=零售类(个人+商户), 1=公司类(企业)", name = "groupType")
private String groupType;
/**
* 柜员号
@@ -114,6 +114,12 @@ public class CustGroupVO {
@ApiModelProperty(value = "备注", name = "remark")
private String remark;
/**
* 客群标签(多个标签用逗号分隔)
*/
@ApiModelProperty(value = "客群标签(多个标签用逗号分隔)", name = "groupTags")
private String groupTags;
/**
* 有效期截止时间
*/
@@ -128,23 +134,11 @@ public class CustGroupVO {
private String createStatus;
/**
* 网格类型0=绩效网格, 1=地理网格, 2=绘制网格
* 网格类型(管户关系来源)0=绩效网格, 1=地理网格, 2=绘制网格
*/
@ApiModelProperty(value = "网格类型", name = "gridType")
@ApiModelProperty(value = "网格类型管户关系来源0=绩效网格, 1=地理网格, 2=绘制网格", name = "gridType")
private String gridType;
/**
* 绩效业务类型retail=零售, corporate=公司
*/
@ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType")
private String cmpmBizType;
/**
* 客户经理列表(逗号分隔)
*/
@ApiModelProperty(value = "客户经理列表", name = "gridUserNames")
private String gridUserNames;
/**
* 地理网格ID列表逗号分隔
*/

View File

@@ -57,4 +57,11 @@ public interface CustGroupMapper extends BaseMapper<CustGroup> {
Long countVisibleCustGroup(@Param("id") Long id,
@Param("userName") String userName,
@Param("deptId") String deptId);
/**
* 查询所有已有的客群标签
*
* @return 标签列表
*/
List<String> selectAllGroupTags();
}

View File

@@ -33,4 +33,29 @@ public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> {
* @param memberList 客户列表
*/
void batchInsertMembers(@Param("list") List<CustGroupMember> memberList);
/**
* 批量查询个人客户是否存在(根据客户号+身份证号)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingRetailCustIds(@Param("list") List<String> custIds);
/**
* 批量查询商户客户是否存在(根据客户号+统信码)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingMerchantCustIds(@Param("list") List<String> custIds);
/**
* 批量查询企业客户是否存在(根据客户号+统信码)
*
* @param custIds 客户号列表
* @return 存在的客户号列表
*/
List<String> selectExistingBusinessCustIds(@Param("list") List<String> custIds);
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.group.mapper;
import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825;
import com.ruoyi.group.domain.entity.GroupCustCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCustCountLingshou825;
import org.apache.ibatis.annotations.Mapper;
import java.util.HashMap;
import java.util.List;
@Mapper
public interface GroupPerformanceMapper {
List<GroupCmpmCountLingshou825> selectLsCountList(HashMap<String, Object> paramMap);
List<GroupCmpmCountGongsi825> selectGsCountList(HashMap<String, Object> paramMap);
List<GroupCustCountLingshou825> selectLsCustList(HashMap<String, Object> paramMap);
List<GroupCustCountGongsi825> selectGsCustList(HashMap<String, Object> paramMap);
}

View File

@@ -1,7 +1,6 @@
package com.ruoyi.group.service;
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
import com.ruoyi.group.domain.dto.GridImportDTO;
import com.ruoyi.group.domain.entity.CustGroup;
import com.ruoyi.group.domain.vo.CustGroupVO;
import org.springframework.web.multipart.MultipartFile;
@@ -17,88 +16,56 @@ public interface ICustGroupService {
/**
* 查询客群列表
*
* @param dto 查询条件
* @return 客群VO列表
*/
List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto);
/**
* 根据ID查询客群详情
*
* @param id 客群ID
* @return 客群VO
*/
CustGroupVO getCustGroup(Long id);
/**
* 校验当前用户是否有客群查看权限
*
* @param id 客群ID
*/
void checkCustGroupViewPermission(Long id);
/**
* 异步创建客群(模板导入)
* gridType、regionGridIds、drawGridIds 从 custGroup 中获取
*
* @param custGroup 客群实体
* @param file Excel文件
* @param custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds
* @param file Excel文件
* @return 客群ID
*/
String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
/**
* 异步创建客群(网格导入)
*
* @param gridImportDTO 网格导入条件
* @return 客群ID
*/
String createCustGroupByGrid(GridImportDTO gridImportDTO);
/**
* 更新客群(网格导入)
*
* @param gridImportDTO 网格导入条件
* @return 结果消息
*/
String updateCustGroupByGrid(GridImportDTO gridImportDTO);
/**
* 更新客群(模板导入)
* gridType、regionGridIds、drawGridIds 从 custGroup 中获取
*
* @param custGroup 客群实体
* @param file Excel文件
* @param custGroup 客群实体(包含 gridType、regionGridIds、drawGridIds
* @param file Excel文件
* @return 结果消息
*/
String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
/**
* 删除客群
*
* @param idList 客群ID列表
* @return 结果消息
*/
String deleteCustGroup(List<Long> idList);
/**
* 检查客群名称是否存在
*
* @param groupName 客群名称
* @return true=存在, false=不存在
*/
boolean checkGroupNameExist(String groupName);
/**
* 查询客群创建状态
*
* @param id 客群ID
* @return 创建状态0=创建中, 1=创建成功, 2=创建失败
*/
String getCreateStatus(Long id);
/**
* 更新动态客群(定时任务调用)
* 根据原始导入条件重新查询并更新客户列表
*/
void updateDynamicCustGroups();
@@ -107,4 +74,11 @@ public interface ICustGroupService {
*/
void checkAndDisableExpiredGroups();
/**
* 获取所有已有的客群标签列表
*
* @return 标签列表
*/
List<String> getAllGroupTags();
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.group.service;
import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825;
import com.ruoyi.group.domain.entity.GroupCustCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCustCountLingshou825;
import java.util.List;
public interface IGroupPerformanceService {
List<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName);
List<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName);
List<GroupCustCountLingshou825> selectLsCustList(String groupId, String custName, String custIdc, String dt);
List<GroupCustCountGongsi825> selectGsCustList(String groupId, String custName, String socialCreditCode, String dt);
}

View File

@@ -10,11 +10,16 @@ import com.ruoyi.group.mapper.CustGroupMapper;
import com.ruoyi.group.mapper.CustGroupMemberMapper;
import com.ruoyi.group.service.ICustGroupService;
import com.ruoyi.group.service.ICustGroupMemberService;
import com.github.pagehelper.PageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 客群客户服务实现类
@@ -36,7 +41,64 @@ public class CustGroupMemberServiceImpl implements ICustGroupMemberService {
@Override
public List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto) {
custGroupService.checkCustGroupViewPermission(groupId);
return custGroupMemberMapper.selectCustGroupMemberList(groupId, dto);
// 在权限检查之后启动分页避免权限检查SQL消耗分页设置
int pageNum = dto.getPageNum() != null ? dto.getPageNum() : 1;
int pageSize = dto.getPageSize() != null ? dto.getPageSize() : 10;
PageHelper.startPage(pageNum, pageSize);
// 1. 查询客群成员列表(分页)
List<CustGroupMemberVO> memberList = custGroupMemberMapper.selectCustGroupMemberList(groupId, dto);
if (memberList == null || memberList.isEmpty()) {
return memberList;
}
// 2. 按客户类型分组,批量查询存量状态
// 个人客户
List<String> retailCustIds = memberList.stream()
.filter(m -> "0".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 商户客户
List<String> merchantCustIds = memberList.stream()
.filter(m -> "1".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 企业客户
List<String> businessCustIds = memberList.stream()
.filter(m -> "2".equals(m.getCustType()))
.map(CustGroupMemberVO::getCustId)
.collect(Collectors.toList());
// 3. 批量查询各类型存量客户
Set<String> existingRetailCustIds = new HashSet<>();
Set<String> existingMerchantCustIds = new HashSet<>();
Set<String> existingBusinessCustIds = new HashSet<>();
if (!retailCustIds.isEmpty()) {
existingRetailCustIds.addAll(custGroupMemberMapper.selectExistingRetailCustIds(retailCustIds));
}
if (!merchantCustIds.isEmpty()) {
existingMerchantCustIds.addAll(custGroupMemberMapper.selectExistingMerchantCustIds(merchantCustIds));
}
if (!businessCustIds.isEmpty()) {
existingBusinessCustIds.addAll(custGroupMemberMapper.selectExistingBusinessCustIds(businessCustIds));
}
// 4. 填充存量状态
for (CustGroupMemberVO member : memberList) {
String custType = member.getCustType();
String custId = member.getCustId();
if ("0".equals(custType)) {
member.setIsExisting(existingRetailCustIds.contains(custId));
} else if ("1".equals(custType)) {
member.setIsExisting(existingMerchantCustIds.contains(custId));
} else if ("2".equals(custType)) {
member.setIsExisting(existingBusinessCustIds.contains(custId));
} else {
member.setIsExisting(false);
}
}
return memberList;
}
@Override

View File

@@ -0,0 +1,104 @@
package com.ruoyi.group.service.impl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825;
import com.ruoyi.group.domain.entity.GroupCustCountGongsi825;
import com.ruoyi.group.domain.entity.GroupCustCountLingshou825;
import com.ruoyi.group.mapper.GroupPerformanceMapper;
import com.ruoyi.group.service.IGroupPerformanceService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
@Service
public class GroupPerformanceServiceImpl implements IGroupPerformanceService {
@Resource
private GroupPerformanceMapper groupPerformanceMapper;
@Override
public List<GroupCmpmCountLingshou825> selectLsCountList(String dt, String groupName) {
return groupPerformanceMapper.selectLsCountList(buildLsCountParams(dt, groupName));
}
private HashMap<String, Object> buildLsCountParams(String dt, String groupName) {
HashMap<String, Object> paramMap = new HashMap<>();
if (SecurityUtils.userRole().equals("branch")) {
paramMap.put("isBranch", true);
}
if (SecurityUtils.userRole().equals("outlet")) {
paramMap.put("isOutlet", true);
}
if (SecurityUtils.userRole().equals("manager")) {
paramMap.put("isManager", true);
}
paramMap.put("deptId", SecurityUtils.getDeptId());
paramMap.put("userName", SecurityUtils.getUsername());
paramMap.put("dt", dt);
paramMap.put("groupName", groupName);
return paramMap;
}
@Override
public List<GroupCmpmCountGongsi825> selectGsCountList(String dt, String groupName) {
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("userRole", "head");
if (SecurityUtils.userRole().equals("branch")) {
paramMap.put("userRole", "branch");
paramMap.put("isBranch", true);
}
if (SecurityUtils.userRole().equals("outlet")) {
paramMap.put("userRole", "outlet");
paramMap.put("isOutlet", true);
}
if (SecurityUtils.userRole().equals("manager")) {
paramMap.put("userRole", "manager");
paramMap.put("isManager", true);
}
paramMap.put("deptId", SecurityUtils.getDeptId());
paramMap.put("userName", SecurityUtils.getUsername());
paramMap.put("dt", dt);
paramMap.put("groupName", groupName);
return groupPerformanceMapper.selectGsCountList(paramMap);
}
@Override
public List<GroupCustCountLingshou825> selectLsCustList(String groupId, String custName, String custIdc, String dt) {
return groupPerformanceMapper.selectLsCustList(buildLsCustParams(groupId, custName, custIdc, dt));
}
private HashMap<String, Object> buildLsCustParams(String groupId, String custName, String custIdc, String dt) {
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("deptId", SecurityUtils.getDeptId());
paramMap.put("userName", SecurityUtils.getUsername());
paramMap.put("groupId", groupId);
paramMap.put("custName", custName);
paramMap.put("custIdc", custIdc);
paramMap.put("dt", dt);
return paramMap;
}
@Override
public List<GroupCustCountGongsi825> selectGsCustList(String groupId, String custName, String socialCreditCode, String dt) {
HashMap<String, Object> paramMap = new HashMap<>();
if (SecurityUtils.userRole().equals("branch")) {
paramMap.put("isBranch", true);
}
if (SecurityUtils.userRole().equals("outlet")) {
paramMap.put("isOutlet", true);
}
if (SecurityUtils.userRole().equals("manager")) {
paramMap.put("isManager", true);
}
paramMap.put("deptId", SecurityUtils.getDeptId());
paramMap.put("userName", SecurityUtils.getUsername());
paramMap.put("groupId", groupId);
paramMap.put("custName", custName);
paramMap.put("socialCreditCode", socialCreditCode);
paramMap.put("dt", dt);
return groupPerformanceMapper.selectGsCustList(paramMap);
}
}

View File

@@ -40,13 +40,14 @@
cg.id,
cg.group_name,
cg.group_mode,
cg.create_mode,
cg.group_type,
cg.user_name,
cg.nick_name,
cg.dept_id,
cg.share_enabled,
cg.share_dept_ids,
cg.group_status,
cg.group_tags,
cg.create_by,
cg.create_time,
cg.update_by,
@@ -65,12 +66,12 @@
<if test="dto.groupMode != null and dto.groupMode != ''">
AND cg.group_mode = #{dto.groupMode}
</if>
<if test="dto.createMode != null and dto.createMode != ''">
AND cg.create_mode = #{dto.createMode}
</if>
<if test="dto.groupStatus != null and dto.groupStatus != ''">
AND cg.group_status = #{dto.groupStatus}
</if>
<if test="dto.groupTags != null and dto.groupTags != ''">
AND cg.group_tags LIKE CONCAT('%', #{dto.groupTags}, '%')
</if>
</where>
ORDER BY cg.create_time DESC
</select>
@@ -80,13 +81,14 @@
cg.id,
cg.group_name,
cg.group_mode,
cg.create_mode,
cg.group_type,
cg.user_name,
cg.nick_name,
cg.dept_id,
cg.share_enabled,
cg.share_dept_ids,
cg.group_status,
cg.group_tags,
cg.valid_time,
cg.create_by,
cg.create_time,
@@ -95,8 +97,6 @@
cg.remark,
cg.create_status,
cg.grid_type,
cg.cmpm_biz_type,
cg.grid_user_names,
cg.region_grid_ids,
cg.draw_grid_ids,
(SELECT COUNT(*) FROM ibs_cust_group_member cgm WHERE cgm.group_id = cg.id AND cgm.del_flag = '0') AS cust_count
@@ -116,4 +116,14 @@
<include refid="custGroupVisibleBaseCondition"/>
</select>
<select id="selectAllGroupTags" resultType="java.lang.String">
SELECT DISTINCT group_tags
FROM ibs_cust_group
WHERE del_flag = '0'
AND create_status = '1'
AND group_tags IS NOT NULL
AND group_tags != ''
ORDER BY group_tags
</select>
</mapper>

View File

@@ -13,6 +13,12 @@
cgm.cust_name,
cgm.cust_idc,
cgm.social_credit_code,
cgm.user_name,
cgm.nick_name,
cgm.outlet_id,
cgm.outlet_name,
cgm.branch_id,
cgm.branch_name,
cgm.create_time
FROM ibs_cust_group_member cgm
<where>
@@ -24,17 +30,66 @@
<if test="dto != null and dto.custName != null and dto.custName != ''">
AND cgm.cust_name LIKE CONCAT('%', #{dto.custName}, '%')
</if>
<if test="dto != null and dto.userName != null and dto.userName != ''">
AND cgm.user_name = #{dto.userName}
</if>
<if test="dto != null and dto.nickName != null and dto.nickName != ''">
AND cgm.nick_name LIKE CONCAT('%', #{dto.nickName}, '%')
</if>
</where>
ORDER BY cgm.create_time ASC
</select>
<!-- 批量查询个人客户是否存在 -->
<select id="selectExistingRetailCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_retail c ON t.cust_id = c.cust_id AND t.cust_idc = c.cust_idc
WHERE t.del_flag = '0'
AND t.cust_type = '0'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量查询商户客户是否存在 -->
<select id="selectExistingMerchantCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_merchant c ON t.cust_id = c.cust_id AND t.social_credit_code = c.social_credit_code
WHERE t.del_flag = '0'
AND t.cust_type = '1'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量查询企业客户是否存在 -->
<select id="selectExistingBusinessCustIds" resultType="String">
SELECT DISTINCT t.cust_id
FROM ibs_cust_group_member t
INNER JOIN cust_info_business c ON t.cust_id = c.cust_id AND t.social_credit_code = c.social_credit_code
WHERE t.del_flag = '0'
AND t.cust_type = '2'
AND t.cust_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 批量插入客群客户INSERT IGNORE遇到重复键自动跳过 -->
<insert id="batchInsertMembers">
INSERT IGNORE INTO ibs_cust_group_member
(group_id, cust_type, cust_id, cust_name, cust_idc, social_credit_code, create_by, create_time, del_flag, manual_remove)
(group_id, cust_type, cust_id, cust_name, cust_idc, social_credit_code,
user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name,
create_by, create_time, del_flag, manual_remove)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(#{item.groupId}, #{item.custType}, #{item.custId}, #{item.custName}, #{item.custIdc}, #{item.socialCreditCode}, #{item.createBy}, NOW(), '0', '0')
(#{item.groupId}, #{item.custType}, #{item.custId}, #{item.custName}, #{item.custIdc}, #{item.socialCreditCode},
#{item.userName}, #{item.nickName}, #{item.outletId}, #{item.outletName}, #{item.branchId}, #{item.branchName},
#{item.createBy}, NOW(), '0', '0')
</foreach>
</insert>

View File

@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.group.mapper.GroupPerformanceMapper">
<select id="selectLsCountList" resultType="com.ruoyi.group.domain.entity.GroupCmpmCountLingshou825">
SELECT
dt,
group_id,
group_name,
group_mode,
dept_id,
dept_name,
outlets_id,
outlets_name,
user_name,
cust_num,
zf_365cnt,
zf_180cnt,
zf_90cnt,
zf_30cnt,
zf_365rt,
zf_180rt,
zf_90rt,
zf_30rt,
cur_bal_d,
sx_rat,
yx_rat,
sx_num,
yx_num,
sx_bal,
bal_loan,
loan_ave,
yxht_rat,
dian_rat,
shui_rat,
tax_rat,
open_rat,
yxht_num,
dian_num,
shui_num,
tax_num,
open_num,
dep_bal,
fin_bal,
grhx_num,
cfyx_num,
yxxyk_num,
yxsbk_num,
`2to3_sbk_num` as twoTo3SbkNum,
ylj_to_sbk_num,
fshl_num,
yxsd_num,
hxsd_num,
region_code,
ops_dept
FROM group_cmpm_count_lingshou_825
<where>
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="groupName != null and groupName != ''">and group_name like concat('%', #{groupName}, '%')</if>
<if test="isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
</where>
</select>
<select id="selectGsCountList" resultType="com.ruoyi.group.domain.entity.GroupCmpmCountGongsi825">
SELECT
dt,
group_id,
group_name,
group_mode,
dept_id,
dept_name,
outlets_id,
outlets_name,
user_name,
cust_num,
hq_cur_balance,
bz_cur_balance,
loan_balance_cny,
finance_prod_711_balance,
finance_prod_716_balance,
loan_year_dailyaverage,
qfcd_rat,
tx_rat,
bh_rat,
yxdfgz_rat,
dkdf_rat,
dksf_rat,
dkshf_rat,
pjb_rat,
czb_rat,
sfb_rat,
mrb_rat,
szst_rat,
kh_rat,
gjjsyw_rat,
yqjsh_rat,
qfcd_num,
tx_num,
bh_num,
yxdfgz_num,
ustr_count_per_m,
ustr_bal_m,
dkdf_num,
dksf_num,
dkshf_num,
pjb_num,
czb_num,
sfb_num,
mrb_num,
szst_num,
kh_num,
gjjsyw_num,
yqjsh_num,
region_code,
ops_dept,
htqy_rat,
htqy_num,
dept_type,
zf_365cnt,
zf_180cnt,
zf_90cnt,
zf_30cnt,
zf_365rt,
zf_180rt,
zf_90rt,
zf_30rt,
ph_rat,
ph_num
FROM group_cmpm_count_gongsi_825
<where>
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="groupName != null and groupName != ''">and group_name like concat('%', #{groupName}, '%')</if>
<if test=" isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test=" isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
</where>
</select>
<select id="selectLsCustList" resultType="com.ruoyi.group.domain.entity.GroupCustCountLingshou825">
SELECT
group_id,
cust_name,
cust_idc,
cust_isn,
cur_bal_d,
bal_loan,
loan_ave,
is_sx,
is_yx,
sx_bal,
is_yxht,
is_xyk,
fshl,
is_sd,
dian,
shui,
tax,
open_num,
dep_bal,
fin_bal,
is_grhx,
is_cfyx,
is_yxsbk,
is_2to3_sbk,
is_ylj_to_sbk,
region_code,
ops_dept,
cust_type,
is_365zf,
is_180zf,
is_90zf,
is_30zf,
dt,
is_hxsd
FROM group_cust_count_lingshou_825
<where>
group_id = #{groupId}
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="custName != null and custName != ''">and cust_name like concat('%', #{custName}, '%')</if>
<if test="custIdc != null and custIdc != ''">and cust_idc like concat('%', #{custIdc}, '%')</if>
</where>
</select>
<select id="selectGsCustList" resultType="com.ruoyi.group.domain.entity.GroupCustCountGongsi825">
SELECT
group_id,
cust_name,
social_credit_code,
cust_isn,
hq_cur_balance,
bz_cur_balance,
is_credit,
loan_balance_cny,
loan_year_dailyaverage,
finance_prod_716_open_flag,
finance_prod_716_balance,
finance_prod_711_open_flag,
finance_prod_711_balance,
intl_bussiness_jcbh_open_flag,
is_ustr,
ustr_count_per_m,
ustr_bal_m,
eleccharge_sign_flag,
watercharge_sign_flag,
taxdeduction_sign_flag,
pjb,
czb,
sfb,
mrb,
szst,
is_open_sts,
intl_bussiness_open_flag,
intl_bussiness_325_open_flag,
region_code,
ops_dept,
cust_type,
is_htqy,
dept_name,
outlets_id,
outlets_name,
user_name,
dept_id,
is_365zf,
is_180zf,
is_90zf,
is_30zf,
dt,
is_ph
FROM group_cust_count_gongsi_825
<where>
group_id = #{groupId}
<if test="dt != null and dt != ''">and dt = #{dt}</if>
<if test="isBranch == true">and dept_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isOutlet == true">and outlets_id like concat('%',concat(#{deptId},'%'))</if>
<if test="isManager == true">and user_name like concat('%',concat(#{userName},'%'))</if>
<if test="custName != null and custName != ''">and cust_name like concat('%', #{custName}, '%')</if>
<if test="socialCreditCode != null and socialCreditCode != ''">and social_credit_code like concat('%', #{socialCreditCode}, '%')</if>
</where>
</select>
</mapper>

View File

@@ -118,13 +118,4 @@ public class GridCmpmController extends BaseController {
return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ;
}
/**
* 根据网格类型获取客户经理列表
*/
@GetMapping("/managerList")
@Log(title = "绩效网格-获取客户经理列表")
@ApiOperation("获取客户经理列表")
public AjaxResult getManagerListByGridType(@RequestParam String gridType) {
return AjaxResult.success(gridCmpmService.getManagerListByGridType(gridType));
}
}

View File

@@ -65,30 +65,21 @@ public interface GridCmpmMapper {
List<String> selectManagerList();
/**
* 根据网格类型和总行ID查询客户经理列表
* @param gridType 网格类型零售retail、对公corporate、对公账户corporate_account
* 全量查询绩效网格客户列表(用于客群管户关系匹配,不按客户经理过滤)
* @param gridType 网格类型
* @param headId 总行ID
* @return 客户经理列表(user_name, nick_name
* @return 客户列表(包含管户关系信息
*/
List<Map<String, String>> getManagerListByGridType(@Param("gridType") String gridType, @Param("headId") String headId);
List<GridCmpmVO> getAllCustomerListForImport(@Param("gridType") String gridType, @Param("headId") String headId);
/**
* 根据网格类型、总行ID和客户经理查询客户列表分表查询适用于客群导入
* @param gridType 网格类型
* @param userName 客户经理柜员号
* 根据客户ID和类型列表查询管户关系用于动态客群更新管户关系
* @param gridType 网格类型retail/corporate
* @param headId 总行ID
* @return 客户列表
* @param custInfoList 客户信息列表包含custId和custType
* @return 客户管户关系列表
*/
List<GridCmpmVO> getCustomerListForImport(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId);
/**
* 根据网格类型、总行ID和客户经理流式查询客户列表使用Cursor适用于大数据量场景
* @param gridType 网格类型
* @param userName 客户经理柜员号
* @param headId 总行ID
* @return 客户列表游标
*/
Cursor<GridCmpmVO> getCustomerListForImportCursor(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId);
List<GridCmpmVO> getRelationshipsByCustList(@Param("gridType") String gridType, @Param("headId") String headId, @Param("custInfoList") List<Map<String, String>> custInfoList);
// List<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo);

View File

@@ -303,25 +303,27 @@ public class GridCmpmService {
}
/**
* 根据网格类型和总行ID查询客户经理列表
* @param gridType 网格类型零售retail、对公corporate、对公账户corporate_account
* @return 客户经理列表
* 全量查询绩效网格客户列表用于客群管户关系匹配不依赖SecurityUtils
* @param gridType 网格类型
* @param headId 总行ID
* @return 客户列表(包含管户关系信息)
*/
public List<Map<String, String>> getManagerListByGridType(String gridType) {
String headId = SecurityUtils.getHeadId();
return gridCmpmMapper.getManagerListByGridType(gridType, headId);
public List<GridCmpmVO> selectAllForImport(String gridType, String headId) {
return gridCmpmMapper.getAllCustomerListForImport(gridType, headId);
}
/**
* 根据参数查询绩效网格客户列表不依赖SecurityUtils适用于异步线程
* 所有参数由调用者传入,不在方法内部获取用户上下文
* @param gridType 网格类型
* @param userName 客户经理柜员号
* 根据客户ID列表查询管户关系用于动态客群更新管户关系
* @param gridType 网格类型retail/corporate
* @param headId 总行ID
* @return 客户列表
* @param custInfoList 客户信息列表包含custId和custType
* @return 客户管户关系列表
*/
public List<GridCmpmVO> selectManageListForImport(String gridType, String userName, String headId) {
return gridCmpmMapper.getCustomerListForImport(gridType, userName, headId);
public List<GridCmpmVO> getRelationshipsByCustList(String gridType, String headId, List<Map<String, String>> custInfoList) {
if (custInfoList == null || custInfoList.isEmpty()) {
return new ArrayList<>();
}
return gridCmpmMapper.getRelationshipsByCustList(gridType, headId, custInfoList);
}
}

View File

@@ -100,7 +100,7 @@ public class GridCmpmCountLingshouNew825 implements Serializable {
private Integer yxxykNum;
@Excel(name = "有效社保卡户数")
private Integer yxsbkNum;
@Excel(name = "二换三社保卡户数")
@Excel(name = "有效社保卡新增")
private Integer twoTo3SbkNum;
@Excel(name = "养老金迁移至社保卡户数")
private Integer yljToSbkNum;
@@ -108,6 +108,8 @@ public class GridCmpmCountLingshouNew825 implements Serializable {
private Integer fshlNum;
@Excel(name = "有效收单商户")
private Integer yxsdNum;
@Excel(name = "核心收单户数")
private Integer hxsdNum;
private String regionCode;
private String opsDept;
}

View File

@@ -56,10 +56,12 @@ public class GridCustCountLingshouNew825 implements Serializable {
private String isCfyx;
@Excel(name = "是否有效社保卡客户")
private String isYxsbk;
@Excel(name = "是否二换三社保卡")
@Excel(name = "是否有效社保卡新增")
private String is2to3Sbk;
@Excel(name = "是否养老金迁移至社保卡")
private String isYljToSbk;
@Excel(name = "是否核心收单")
private String isHxsd;
private String regionCode;
private String opsDept;
private String custType;

View File

@@ -505,26 +505,20 @@
where manager_id is not null
</select>
<!-- 根据网格类型和总行ID查询客户经理列表 -->
<select id="getManagerListByGridType" resultType="java.util.HashMap">
select distinct user_name as userName, nick_name as nickName
<!-- 全量查询绩效网格客户列表(用于客群管户关系匹配,不按客户经理过滤)-->
<select id="getAllCustomerListForImport" resultType="GridCmpmVO">
select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name
from grid_cmpm_${gridType}_${headId}
where user_name is not null
order by user_name
</select>
<!-- 根据网格类型、总行ID和客户经理查询客户列表分表查询适用于客群导入-->
<select id="getCustomerListForImport" resultType="GridCmpmVO">
select cust_id, cust_name, cust_type, cust_idc, usci
<!-- 根据客户ID列表查询管户关系用于动态客群更新管户关系-->
<select id="getRelationshipsByCustList" resultType="GridCmpmVO">
select cust_id, cust_type, user_name, nick_name, outlet_id, outlet_name, branch_id, branch_name
from grid_cmpm_${gridType}_${headId}
where user_name = #{userName}
</select>
<!-- 根据网格类型、总行ID和客户经理流式查询客户列表使用Cursor适用于大数据量场景-->
<select id="getCustomerListForImportCursor" resultType="GridCmpmVO" fetchSize="1000">
select cust_id, cust_name, cust_type, cust_idc, usci
from grid_cmpm_${gridType}_${headId}
where user_name = #{userName}
where
<foreach item="custInfo" collection="custInfoList" separator=" or " open="(" close=")">
(cust_id = #{custInfo.custId} and cust_type = #{custInfo.custType})
</foreach>
</select>
</mapper>

View File

@@ -27,7 +27,7 @@
zf_30rt, cur_bal_d, sx_rat, yx_rat, sx_num, yx_num, sx_bal, bal_loan, loan_ave, yxht_rat, dian_rat,
shui_rat, tax_rat, open_rat, yxht_num, dian_num, shui_num, tax_num, open_num, dep_bal, fin_bal,
grhx_num, cfyx_num, yxxyk_num, yxsbk_num, `2to3_sbk_num` as twoTo3SbkNum, ylj_to_sbk_num,
fshl_num, yxsd_num, region_code, ops_dept
fshl_num, yxsd_num, hxsd_num, region_code, ops_dept
FROM grid_cmpm_count_lingshou_new_825
<where>
<if test=" dt != null and dt != ''">and dt = #{dt}</if>
@@ -102,6 +102,7 @@
is_yxsbk,
is_2to3_sbk,
is_ylj_to_sbk,
is_hxsd,
region_code,
ops_dept,
cust_type,

View File

@@ -17,15 +17,6 @@ export function getCustGroup(id) {
})
}
// 异步创建客群(网格导入)
export function createCustGroupByGrid(data) {
return request({
url: '/group/cust/createByGrid',
method: 'post',
data: data
})
}
// 异步创建客群(模板导入)
export function createCustGroupByTemplate(data) {
return request({
@@ -36,24 +27,6 @@ export function createCustGroupByTemplate(data) {
})
}
// 更新客群
export function updateCustGroup(data) {
return request({
url: '/group/cust/update',
method: 'post',
data: data
})
}
// 更新客群(网格导入)
export function updateCustGroupByGrid(data) {
return request({
url: '/group/cust/updateByGrid',
method: 'post',
data: data
})
}
// 更新客群(模板导入)
export function updateCustGroupByTemplate(data) {
return request({
@@ -100,24 +73,6 @@ export function removeMembers(groupId, memberIds) {
})
}
// 检查客群名称是否存在
export function checkGroupNameExist(groupName) {
return request({
url: '/group/cust/checkName',
method: 'get',
params: { groupName: groupName }
})
}
// 根据网格类型获取客户经理列表
export function getManagerList(gridType) {
return request({
url: '/grid/cmpm/managerList',
method: 'get',
params: { gridType: gridType }
})
}
// 分页查询客群成员列表
export function listCustGroupMembers(groupId, query) {
return request({
@@ -135,3 +90,11 @@ export function getRegionGridListForGroup(query) {
params: query
})
}
// 获取所有客群标签列表
export function getAllGroupTags() {
return request({
url: '/group/cust/tags',
method: 'get'
})
}

View File

@@ -0,0 +1,69 @@
import request from '@/utils/request';
export function getGroupPerformanceLsList(params) {
return request({
url: '/group/performance/ls/list',
method: 'get',
params
});
}
export function getGroupPerformanceGsList(params) {
return request({
url: '/group/performance/gs/list',
method: 'get',
params
});
}
export function getGroupPerformanceLsCustList(params) {
return request({
url: '/group/performance/ls/custList',
method: 'get',
params
});
}
export function getGroupPerformanceGsCustList(params) {
return request({
url: '/group/performance/gs/custList',
method: 'get',
params
});
}
export function exportGroupPerformanceLs(params) {
return request({
url: '/group/performance/exportLs',
method: 'get',
params,
responseType: 'blob'
});
}
export function exportGroupPerformanceGs(params) {
return request({
url: '/group/performance/exportGs',
method: 'get',
params,
responseType: 'blob'
});
}
export function exportGroupPerformanceLsCust(params) {
return request({
url: '/group/performance/exportLsCust',
method: 'get',
params,
responseType: 'blob'
});
}
export function exportGroupPerformanceGsCust(params) {
return request({
url: '/group/performance/exportGsCust',
method: 'get',
params,
responseType: 'blob'
});
}

View File

@@ -238,6 +238,28 @@ export const constantRoutes = [
component: () => import('@/views/group/custGroup/detail')
}]
},
{
path: '/center/groupPerformance/list',
component: Layout,
hidden: true,
children: [{
path: '/center/groupPerformance/list',
name: 'GroupPerformanceList',
meta: { title: '客群业绩统计', activeMenu: '/center/groupPerformance/list' },
component: () => import('@/views/group/performance/list')
}]
},
{
path: '/center/groupPerformance/custom',
component: Layout,
hidden: true,
children: [{
path: '/center/groupPerformance/custom',
name: 'GroupPerformanceCustom',
meta: { title: '客群客户明细', activeMenu: '/center/groupPerformance/list' },
component: () => import('@/views/group/performance/custom')
}]
},
{
path: '/checklist/customerlist',
component: Layout,
@@ -353,8 +375,8 @@ export const constantRoutes = [
meta: { title: '个人中心', icon: 'user' }
}
]
},
];
}
];
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [

View File

@@ -432,7 +432,7 @@
v-if="item.cmpmType == '贷款客户经理'"
:label="`${item.cmpmType}`"
>
<span>{{ item.cmpmUserList }}</span>
<span>{{ is932Dept ? `${baseForm.belongUserName}-${baseForm.belongUserId}` : item.cmpmUserList }}</span>
</el-form-item>
</el-col>
</el-row>
@@ -1242,6 +1242,7 @@ export default {
businessScope: '',
gridUserName: '',
belongUserName: '',
belongUserId: '',
updateTime: ''
},
registerLocationNum: '',
@@ -1374,7 +1375,7 @@ export default {
Custom
},
computed: {
...mapGetters(['roles', 'userName']),
...mapGetters(['roles', 'userName', 'deptId']),
isHeadAdmin() {
return this.roles.includes('headAdmin')
},
@@ -1386,6 +1387,10 @@ export default {
isPrivate() {
return this.roles.includes('headPrivate')
},
// 是否932开头部门
is932Dept() {
return String(this.deptId || '').substring(0, 3) === '932'
},
// 运管部
isHeadOps() {
return this.roles.includes('headOps')

View File

@@ -437,7 +437,7 @@
v-if="item.cmpmType == '贷款客户经理'"
:label="`${item.cmpmType}`"
>
<span>{{ item.cmpmUserList }}</span>
<span>{{ is932Dept ? `${baseForm.belongUserName}-${baseForm.belongUserId}` : item.cmpmUserList }}</span>
</el-form-item>
</el-col>
</el-row>
@@ -1460,6 +1460,7 @@ export default {
tel: '',
gridUserName: '',
belongUserName: '',
belongUserId: '',
updateTime: ''
},
registerLocationNum: '',
@@ -1663,6 +1664,9 @@ export default {
},
is825() {
return String(this.deptId || '').substring(0, 3) === '825'
},
is932Dept() {
return String(this.deptId || '').substring(0, 3) === '932'
}
},
created() {

View File

@@ -364,7 +364,7 @@
v-if="item.cmpmType == '贷款客户经理'"
:label="`${item.cmpmType}`"
>
<span>{{ item.cmpmUserList }}</span>
<span>{{ is932Dept ? `${profile.belongUserName}-${profile.belongUserId}` : item.cmpmUserList }}</span>
</el-form-item>
</el-col>
</el-row>
@@ -1663,7 +1663,7 @@ export default {
CustContact
},
computed: {
...mapGetters(['roles', 'userName']),
...mapGetters(['roles', 'userName', 'deptId']),
isHeadAdmin() {
return this.roles.includes('headAdmin')
},
@@ -1675,6 +1675,10 @@ export default {
isPrivate() {
return this.roles.includes('headPrivate')
},
// 是否932开头部门
is932Dept() {
return String(this.deptId || '').substring(0, 3) === '932'
},
// 运管部
isHeadOps() {
return this.roles.includes('headOps')

View File

@@ -235,10 +235,11 @@ export const privateColumnsNew825 = [
{ prop: 'cfyxNum', label: '财富有效客户数', width: 150 },
{ prop: 'yxxykNum', label: '有效信用卡数', width: 140 },
{ prop: 'yxsbkNum', label: '有效社保卡户数', width: 150 },
{ prop: 'twoTo3SbkNum', label: '二换三社保卡户数', width: 160 },
{ prop: 'twoTo3SbkNum', label: '有效社保卡新增', width: 160 },
{ prop: 'yljToSbkNum', label: '养老金迁移至社保卡户数', width: 180 },
{ prop: 'fshlNum', label: '丰收互联客户数', width: 150 },
{ prop: 'yxsdNum', label: '有效收单商户', width: 140 },
{ prop: 'hxsdNum', label: '核心收单户数', width: 140 },
{ prop: 'zf365rt', label: '近365天走访率', width: 140 },
{ prop: 'zf180rt', label: '近180天走访率', width: 140 },
{ prop: 'zf90rt', label: '近90天走访率', width: 140 },
@@ -638,8 +639,9 @@ export const privateTotalColumnsNew825 = [
{ prop: 'isGrhx', label: '是否个人核心客户', width: 140 },
{ prop: 'isCfyx', label: '是否财富有效客户', width: 140 },
{ prop: 'isYxsbk', label: '是否有效社保卡客户', width: 150 },
{ prop: 'is2to3Sbk', label: '是否二换三社保卡', width: 150 },
{ prop: 'is2to3Sbk', label: '是否有效社保卡新增', width: 150 },
{ prop: 'isYljToSbk', label: '是否养老金迁移至社保卡', width: 180 },
{ prop: 'isHxsd', label: '是否核心收单', width: 120 },
{ prop: 'is365zf', label: '近365天有无走访', width: 140 },
{ prop: 'is180zf', label: '近180天有无走访', width: 140 },
{ prop: 'is90zf', label: '近90天有无走访', width: 140 },

View File

@@ -22,11 +22,22 @@
<el-radio label="0">静态客群</el-radio>
<el-radio label="1">动态客群</el-radio>
</el-radio-group>
<div class="form-tip">
静态客群创建后客户列表固定除非手动更新
<!-- <div class="form-tip">
静态客群创建后客户列表固定
<br />
动态客群系统定期根据原始条件自动刷新客户列表
</div>
</div> -->
</el-form-item>
<el-form-item label="客群类型" prop="groupType">
<el-radio-group v-model="form.groupType" :disabled="isEdit">
<el-radio label="0">零售类</el-radio>
<el-radio label="1">公司类</el-radio>
</el-radio-group>
<!-- <div class="form-tip">
零售类只能导入个人和商户客户
<br />
公司类只能导入企业客户
</div> -->
</el-form-item>
<el-form-item label="有效期" prop="validTime">
<el-date-picker
@@ -42,89 +53,103 @@
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="2" />
</el-form-item>
<el-form-item label="客群标签">
<div class="tag-input-wrapper">
<el-input
v-model="tagInput"
placeholder="输入标签后点击加号添加"
style="width: calc(100% - 40px)"
@keyup.enter.native="addTag"
/>
<el-button type="primary" icon="el-icon-plus" @click="addTag" style="margin-left: 8px" />
</div>
<div class="tag-list" v-if="allTags.length > 0">
<span class="tag-label">已有标签</span>
<el-tag
v-for="tag in allTags"
:key="tag"
:type="selectedTags.includes(tag) ? 'primary' : 'info'"
:effect="selectedTags.includes(tag) ? 'dark' : 'plain'"
@click="toggleTag(tag)"
style="cursor: pointer; margin-right: 8px; margin-bottom: 8px"
>
{{ tag }}
</el-tag>
</div>
<div class="selected-tags" v-if="selectedTags.length > 0">
<span class="tag-label">已选标签</span>
<el-tag
v-for="tag in selectedTags"
:key="tag"
closable
@close="removeTag(tag)"
style="margin-right: 8px; margin-bottom: 8px"
>
{{ tag }}
</el-tag>
</div>
</el-form-item>
<el-form-item label="开启共享">
<el-switch v-model="shareEnabled" />
</el-form-item>
<el-form-item label="可见部门" v-if="shareEnabled">
<el-select
v-model="form.shareDeptIdList"
v-model="shareDeptList"
multiple
filterable
placeholder="请选择可见部门"
style="width: 100%"
popper-class="dept-select-popper"
>
<el-option
v-for="dept in deptOptions"
:key="dept.deptId"
:label="dept.deptName"
:value="dept.deptId"
:value="String(dept.deptId)"
/>
</el-select>
</el-form-item>
<el-form-item label="创建方式" prop="createMode">
<el-radio-group v-model="form.createMode" :disabled="isEdit">
<el-radio label="1">模板导入</el-radio>
<el-radio label="2">绩效网格</el-radio>
<el-radio label="3">地理网格</el-radio>
<el-radio label="4">绘制网格</el-radio>
</el-radio-group>
<!-- 模板导入 -->
<el-form-item label="客户文件" prop="file">
<el-upload
ref="upload"
:action="uploadUrl"
:headers="uploadHeaders"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-change="handleFileChange"
:auto-upload="false"
:show-file-list="true"
:limit="1"
accept=".xlsx,.xls"
>
<el-button slot="trigger" size="small" icon="el-icon-upload">选择文件</el-button>
<el-button size="small" type="text" @click.stop="downloadTemplate" style="margin-left: 15px">
下载模板
</el-button>
<div slot="tip" class="el-upload__tip">
仅支持Excel文件文件大小不超过10MB
</div>
</el-upload>
</el-form-item>
<!-- 模板导入 -->
<template v-if="form.createMode === '1'">
<el-form-item label="客户文件" prop="file">
<el-upload
ref="upload"
:action="uploadUrl"
:headers="uploadHeaders"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-change="handleFileChange"
:auto-upload="false"
:show-file-list="true"
:limit="1"
accept=".xlsx,.xls"
>
<el-button slot="trigger" size="small" icon="el-icon-upload">选择文件</el-button>
<el-button size="small" type="text" @click.stop="downloadTemplate" style="margin-left: 15px">
下载模板
</el-button>
<div slot="tip" class="el-upload__tip">
仅支持Excel文件文件大小不超过10MB
</div>
</el-upload>
</el-form-item>
</template>
<el-form-item label="匹配网格类型" prop="gridType">
<el-radio-group v-model="form.gridType" @change="handleGridTypeChange">
<el-radio label="0">绩效网格</el-radio>
<!-- 暂时隐藏后续版本开放
<el-radio label="1">地理网格</el-radio>
<el-radio label="2">绘制网格</el-radio>
-->
</el-radio-group>
<div class="form-tip">
系统将根据客户信息从所选网格匹配管户关系匹配不上则保存空的管户关系
</div>
</el-form-item>
<!-- 绩效网格 -->
<template v-if="form.createMode === '2'">
<el-form-item label="业务类型" prop="cmpmBizType">
<el-select v-model="gridForm.cmpmBizType" placeholder="请选择业务类型" style="width: 100%" @change="handleCmpmBizTypeChange">
<el-option label="零售" value="retail" />
<el-option label="对公" value="corporate" />
<el-option label="对公账户" value="corporate_account" />
</el-select>
</el-form-item>
<el-form-item label="客户经理" prop="userNames">
<el-select
v-model="gridForm.userNames"
multiple
filterable
placeholder="请选择客户经理"
style="width: 100%"
>
<el-option
v-for="user in userOptions"
:key="user.userName"
:label="user.nickName"
:value="user.userName"
/>
</el-select>
</el-form-item>
</template>
<!-- 地理网格 -->
<template v-if="form.createMode === '3'">
<!-- 地理网格筛选 - 暂时隐藏后续版本开放
<template v-if="form.gridType === '1'">
<el-form-item label="网格级别">
<el-radio-group v-model="regionQuery.gridLevel">
<el-radio label="1">总行行政网格</el-radio>
@@ -146,11 +171,11 @@
</el-form-item>
<el-form-item label="选择网格">
<el-select
v-model="gridForm.regionGridIds"
v-model="form.regionGridIds"
multiple
filterable
collapse-tags
placeholder="请先查询网格,然后选择"
placeholder="请选择网格"
style="width: 100%"
>
<el-option
@@ -162,15 +187,16 @@
</el-select>
</el-form-item>
</template>
-->
<!-- 绘制网格 -->
<template v-if="form.createMode === '4'">
<el-form-item label="绘制网格" prop="drawGridIds">
<!-- 绘制网格筛选 - 暂时隐藏后续版本开放
<template v-if="form.gridType === '2'">
<el-form-item label="绘制网格">
<el-select
v-model="gridForm.drawGridIds"
v-model="form.drawGridIds"
multiple
filterable
placeholder="请选择绘制网格"
placeholder="请选择网格"
style="width: 100%"
>
<el-option
@@ -182,6 +208,7 @@
</el-select>
</el-form-item>
</template>
-->
</el-form>
<div slot="footer" class="dialog-footer">
@@ -195,16 +222,14 @@
<script>
import {
createCustGroupByGrid,
createCustGroupByTemplate,
updateCustGroupByGrid,
updateCustGroupByTemplate,
downloadTemplate,
getManagerList,
getRegionGridListForGroup
getRegionGridListForGroup,
getCreateStatus,
getAllGroupTags
} from '@/api/group/custGroup'
import { getToken } from '@/utils/auth'
import { listUser } from '@/api/system/user'
import { listDept } from '@/api/system/dept'
import { getSimpleGridList } from '@/api/grid/list/gridlist'
@@ -231,8 +256,61 @@ export default {
// 上传地址
uploadUrl: process.env.VUE_APP_BASE_API + '/group/cust/createByTemplate',
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
// 处理中提示
processTipVisible: false
// 表单数据
form: {
id: null,
groupName: null,
groupMode: '0',
groupType: '0',
groupStatus: '0',
gridType: '0',
regionGridIds: [],
drawGridIds: [],
shareEnabled: 0,
shareDeptIdList: [],
remark: null,
validTime: null
},
// 共享开关
shareEnabled: false,
// 已选部门列表字符串数组用于el-select绑定
shareDeptList: [],
// 上传文件
uploadFile: null,
// 表单验证规则
rules: {
groupName: [
{ required: true, message: '请输入客群名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
groupMode: [{ required: true, message: '请选择客群模式', trigger: 'change' }],
groupType: [{ required: true, message: '请选择客群类型', trigger: 'change' }],
gridType: [{ required: true, message: '请选择匹配网格类型', trigger: 'change' }]
},
// 部门选项
deptOptions: [],
// 地理网格选项
regionGridOptions: [],
// 地理网格查询条件
regionQuery: {
gridLevel: '1',
gridDutyType: null,
gridName: null
},
// 地理网格加载状态
regionLoading: false,
// 绘制网格选项
drawGridOptions: [],
// 轮询定时器
pollTimer: null,
// 标签输入框内容
tagInput: '',
// 所有已有标签列表
allTags: [],
// 已选标签列表
selectedTags: [],
// 关闭弹窗时是否保留状态轮询
keepPollingOnClose: false
}
},
computed: {
@@ -247,66 +325,6 @@ export default {
return '确 定'
}
},
data() {
return {
// 提交中状态
submitting: false,
// 上传地址
uploadUrl: process.env.VUE_APP_BASE_API + '/group/cust/createByTemplate',
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
// 处理中提示
processTipVisible: false,
// 表单数据
form: {
id: null,
groupName: null,
groupMode: '0',
createMode: '1',
groupStatus: '0',
shareEnabled: 0,
shareDeptIdList: [],
remark: null,
validTime: null
},
// 共享开关
shareEnabled: false,
// 网格表单数据
gridForm: {
gridType: '0',
cmpmBizType: null,
userNames: [],
regionGridIds: [],
drawGridIds: []
},
// 上传文件
uploadFile: null,
// 表单验证规则
rules: {
groupName: [
{ required: true, message: '请输入客群名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
groupMode: [{ required: true, message: '请选择客群模式', trigger: 'change' }],
createMode: [{ required: true, message: '请选择创建方式', trigger: 'change' }]
},
// 部门选项
deptOptions: [],
// 用户选项
userOptions: [],
// 地理网格选项
regionGridOptions: [],
// 地理网格查询条件
regionQuery: {
gridLevel: '1',
gridDutyType: null,
gridName: null
},
// 地理网格加载状态
regionLoading: false,
// 绘制网格选项
drawGridOptions: []
}
},
watch: {
visible(val) {
if (val) {
@@ -318,143 +336,142 @@ export default {
if (val && Object.keys(val).length > 0) {
this.form = { ...val }
this.shareEnabled = val.shareEnabled === 1
// 解析 shareDeptIds 字符串为 shareDeptIdList 数组
// 解析 shareDeptIds 字符串为 shareDeptList 数组保持字符串类型以匹配el-option的value
if (val.shareDeptIds) {
this.form.shareDeptIdList = val.shareDeptIds.split(',').filter(id => id)
this.shareDeptList = val.shareDeptIds.split(',').filter(id => id)
} else {
this.form.shareDeptIdList = []
this.shareDeptList = []
}
// 解析网格ID
if (val.regionGridIds) {
this.form.regionGridIds = val.regionGridIds.split(',').map(id => parseInt(id)).filter(id => id)
} else {
this.form.regionGridIds = []
}
if (val.drawGridIds) {
this.form.drawGridIds = val.drawGridIds.split(',').map(id => parseInt(id)).filter(id => id)
} else {
this.form.drawGridIds = []
}
// 解析标签
if (val.groupTags) {
this.selectedTags = val.groupTags.split(',').map(tag => tag.trim()).filter(tag => tag)
} else {
this.selectedTags = []
}
// 反显网格数据(需要在 form.createMode watch 之后执行)
// 反显网格数据
this.$nextTick(() => {
this.restoreGridData(val)
})
}
},
immediate: true
},
'form.createMode'(val) {
// 重置网格表单
this.gridForm = {
gridType: val === '2' ? '0' : val === '3' ? '1' : '2',
regionGridIds: [],
drawGridIds: []
}
// 切换到地理网格模式时,重置查询条件
if (val === '3') {
this.resetRegionQuery()
}
// 切换到绘制网格模式时,加载绘制网格列表
if (val === '4') {
this.loadDrawGridOptions()
}
},
'gridForm.cmpmBizType'(val) {
// 当业务类型改变时,清空已选择的客户经理
if (val) {
this.gridForm.userNames = []
this.loadManagerOptions()
}
},
'form.createStatus'(val) {
// 监听创建状态变化,显示提示
if (this.isEdit && val === '0') {
this.processTipVisible = true
} else if (this.processTipVisible) {
this.processTipVisible = false
}
}
},
created() {
this.loadDeptOptions()
this.loadAllTags()
},
beforeDestroy() {
this.clearPollTimer()
},
methods: {
/** 初始化 */
init() {
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
// 编辑模式下,恢复网格数据
if (this.isEdit && this.groupData && Object.keys(this.groupData).length > 0) {
this.restoreGridData(this.groupData)
}
})
},
/** 反显网格数据 */
restoreGridData(data) {
if (!this.isEdit) return
if (!this.isEdit || !data.gridType) return
const createMode = String(data.createMode)
// 根据创建方式反显网格数据
if (createMode === '2' && data.gridType === '0') {
// 绩效网格
this.gridForm.gridType = '0'
this.gridForm.cmpmBizType = data.cmpmBizType
if (data.gridUserNames) {
this.gridForm.userNames = data.gridUserNames.split(',').filter(n => n)
// 加载客户经理选项
this.loadManagerOptions()
}
} else if (createMode === '3' && data.gridType === '1') {
// 地理网格 - 需要查询所有网格以便正确反显
this.gridForm.gridType = '1'
// 设置默认查询条件(获取所有网格)
this.regionQuery = {
gridLevel: '1',
gridDutyType: null,
gridName: null
}
if (data.gridType === '1') {
this.resetRegionQuery()
if (data.regionGridIds) {
this.gridForm.regionGridIds = data.regionGridIds.split(',').map(id => parseInt(id)).filter(id => id)
// 加载地理网格选项(无查询条件,获取全部)
this.loadRegionGridOptions()
}
} else if (createMode === '4' && data.gridType === '2') {
// 绘制网格
this.gridForm.gridType = '2'
if (data.drawGridIds) {
this.gridForm.drawGridIds = data.drawGridIds.split(',').map(id => parseInt(id)).filter(id => id)
// 加载绘制网格选项
this.loadDrawGridOptions()
}
} else if (data.gridType === '2') {
this.loadDrawGridOptions()
}
},
/** 加载部门选项 */
loadDeptOptions() {
listDept().then(response => {
this.deptOptions = response.data || []
// 将树形结构扁平化以便el-select遍历
this.deptOptions = this.flattenDeptList(response.data || [])
}).catch(() => {
this.deptOptions = []
})
},
/** 加载用户选项 */
loadUserOptions() {
listUser().then(response => {
this.userOptions = response.rows || []
/** 扁平化部门树 */
flattenDeptList(list, result = []) {
list.forEach(item => {
result.push({ deptId: item.deptId, deptName: item.deptName })
if (item.children && item.children.length > 0) {
this.flattenDeptList(item.children, result)
}
})
return result
},
/** 加载所有已有标签 */
loadAllTags() {
getAllGroupTags().then(response => {
this.allTags = response.data || []
}).catch(() => {
this.userOptions = []
this.allTags = []
})
},
/** 根据业务类型加载客户经理选项 */
loadManagerOptions() {
if (!this.gridForm.cmpmBizType) {
this.userOptions = []
/** 添加标签 */
addTag() {
const tag = this.tagInput.trim()
if (!tag) return
if (this.selectedTags.includes(tag)) {
this.$message.warning('该标签已添加')
return
}
getManagerList(this.gridForm.cmpmBizType).then(response => {
this.userOptions = response.data || []
}).catch(() => {
this.userOptions = []
})
this.selectedTags.push(tag)
// 如果是新标签添加到allTags中
if (!this.allTags.includes(tag)) {
this.allTags.push(tag)
}
this.tagInput = ''
},
/** 业务类型改变处理 */
handleCmpmBizTypeChange(val) {
// 清空已选择的客户经理
this.gridForm.userNames = []
/** 切换标签选中状态 */
toggleTag(tag) {
const index = this.selectedTags.indexOf(tag)
if (index > -1) {
this.selectedTags.splice(index, 1)
} else {
this.selectedTags.push(tag)
}
},
/** 移除已选标签 */
removeTag(tag) {
const index = this.selectedTags.indexOf(tag)
if (index > -1) {
this.selectedTags.splice(index, 1)
}
},
/** 网格类型改变处理 */
handleGridTypeChange(val) {
if (val === '1') {
this.resetRegionQuery()
} else if (val === '2') {
this.loadDrawGridOptions()
}
// 清空网格选择
this.form.regionGridIds = []
this.form.drawGridIds = []
},
/** 加载地理网格选项 */
@@ -486,7 +503,7 @@ export default {
gridDutyType: null,
gridName: null
}
this.gridForm.regionGridIds = []
this.form.regionGridIds = []
},
/** 加载绘制网格选项 */
@@ -534,130 +551,138 @@ export default {
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
if (!this.uploadFile && !this.isEdit) {
this.$modal.msgWarning('请选择要上传的文件')
return
}
// 处理共享设置
this.form.shareEnabled = this.shareEnabled ? 1 : 0
// 转换 shareDeptIdList 数组为 shareDeptIds 逗号分隔字符串
this.form.shareDeptIds = this.form.shareDeptIdList.join(',')
this.form.shareDeptIds = this.shareDeptList.join(',')
// 转换选中的标签为逗号分隔字符串
this.form.groupTags = this.selectedTags.join(',')
// 转换网格ID数组为逗号分隔字符串
this.form.regionGridIds = this.form.regionGridIds.join(',')
this.form.drawGridIds = this.form.drawGridIds.join(',')
// 根据创建方式处理提交数据
if (this.form.createMode === '1') {
this.handleTemplateSubmit()
this.submitting = true
// 构建表单数据
const formData = new FormData()
formData.append('dto', JSON.stringify(this.form))
if (this.uploadFile) {
formData.append('file', this.uploadFile)
}
if (this.isEdit) {
// 编辑模式
updateCustGroupByTemplate(formData).then(response => {
this.submitting = false
if (response.msg === '客群更新成功') {
// 没有上传文件,直接更新成功
this.$modal.msgSuccess('客群更新成功')
this.$emit('submit', { id: this.form.id, refresh: true })
} else {
// 有上传文件,后台正在处理
this.$modal.msgSuccess('客群更新任务已提交,后台正在处理')
this.keepPollingOnClose = true
this.startPollingStatus(this.form.id)
}
this.handleClose()
}).catch(() => {
this.submitting = false
})
} else {
this.handleGridSubmit()
// 新增模式
createCustGroupByTemplate(formData).then(response => {
const groupId = response && response.data ? response.data : null
if (!groupId) {
this.$modal.msgError('客群创建成功但未获取到任务ID请稍后在列表中查看状态')
this.submitting = false
this.handleClose()
return
}
this.$modal.msgSuccess('客群创建任务已提交,后台正在处理')
this.submitting = false
this.keepPollingOnClose = true
this.startPollingStatus(groupId)
this.handleClose()
}).catch(() => {
this.submitting = false
})
}
}
})
},
/** 模板导入提交 */
handleTemplateSubmit() {
if (!this.uploadFile && !this.isEdit) {
this.$modal.msgWarning('请选择要上传的文件')
return
}
this.submitting = true
// 构建表单数据
const formData = new FormData()
formData.append('dto', JSON.stringify(this.form))
if (this.uploadFile) {
formData.append('file', this.uploadFile)
}
if (this.isEdit) {
// 编辑模式:重新导入模板文件
updateCustGroupByTemplate(formData).then(response => {
this.$modal.msgSuccess('客群更新中,请稍后刷新查看')
this.submitting = false
this.$emit('submit', { id: this.form.id })
this.handleClose()
}).catch(() => {
this.submitting = false
})
} else {
// 新增模式
createCustGroupByTemplate(formData).then(response => {
this.$modal.msgSuccess('客群创建中,请稍后刷新查看')
this.submitting = false
this.$emit('submit', { id: response.data })
this.handleClose()
}).catch(() => {
this.submitting = false
})
}
},
/** 网格导入提交 */
handleGridSubmit() {
this.submitting = true
// 构建提交数据
const submitData = {
custGroup: { ...this.form },
gridType: this.form.createMode === '2' ? '0' : this.form.createMode === '3' ? '1' : '2',
cmpmBizType: this.gridForm.cmpmBizType,
userNames: this.gridForm.userNames,
regionGridIds: this.gridForm.regionGridIds,
drawGridIds: this.gridForm.drawGridIds
}
if (this.isEdit) {
// 编辑模式:重新导入网格
updateCustGroupByGrid(submitData).then(response => {
// 使用后端返回的消息
const msg = response.msg || '客群更新成功'
if (msg.includes('更新中')) {
this.$modal.msgSuccess('客群更新中,请稍后刷新查看')
} else {
this.$modal.msgSuccess(msg)
}
this.submitting = false
this.$emit('submit', { id: this.form.id })
this.handleClose()
}).catch(() => {
this.submitting = false
})
} else {
// 新增模式
createCustGroupByGrid(submitData).then(response => {
this.$modal.msgSuccess('客群创建中,请稍后刷新查看')
this.submitting = false
this.$emit('submit', { id: response.data })
this.handleClose()
}).catch(() => {
this.submitting = false
})
}
},
/** 关闭弹窗 */
handleClose() {
this.$emit('update:visible', false)
this.resetForm()
},
/** 开始轮询创建状态 */
startPollingStatus(groupId) {
if (!groupId || groupId === 'undefined' || groupId === 'null') {
return
}
this.clearPollTimer()
// 每30秒轮询一次
this.pollTimer = setInterval(() => {
getCreateStatus(groupId).then(res => {
const status = res.data
if (status === '1') {
// 创建成功
this.clearPollTimer()
this.keepPollingOnClose = false
window.dispatchEvent(new Event('notice-center-refresh'))
this.$modal.msgSuccess('客群导入完成')
this.$emit('submit', { id: groupId, refresh: true })
} else if (status === '2') {
// 创建失败
this.clearPollTimer()
this.keepPollingOnClose = false
window.dispatchEvent(new Event('notice-center-refresh'))
this.$modal.msgError('客群导入失败,请查看消息中心了解详情')
}
}).catch(() => {
// 查询失败,继续轮询
})
}, 30000)
},
/** 清除轮询定时器 */
clearPollTimer() {
if (this.pollTimer) {
clearInterval(this.pollTimer)
this.pollTimer = null
}
},
/** 重置表单 */
resetForm() {
if (!this.keepPollingOnClose) {
this.clearPollTimer()
}
this.form = {
id: null,
groupName: null,
groupMode: '0',
createMode: '1',
groupType: '0',
groupStatus: '0',
gridType: '0',
regionGridIds: [],
drawGridIds: [],
shareEnabled: 0,
shareDeptIdList: [],
remark: null,
validTime: null
}
this.shareEnabled = false
this.gridForm = {
gridType: '0',
cmpmBizType: null,
userNames: [],
regionGridIds: [],
drawGridIds: []
}
this.shareDeptList = []
this.tagInput = ''
this.selectedTags = []
this.uploadFile = null
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
@@ -677,4 +702,31 @@ export default {
line-height: 1.5;
margin-top: 5px;
}
.tag-input-wrapper {
display: flex;
align-items: center;
}
.tag-list,
.selected-tags {
margin-top: 10px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
.tag-label {
display: block;
font-size: 12px;
color: #909399;
margin-bottom: 8px;
}
</style>
<style>
/* 解决弹窗内下拉框被遮挡的问题 */
.dept-select-popper {
z-index: 9999 !important;
}
</style>

View File

@@ -36,19 +36,51 @@
<div class="main_table">
<el-table v-loading="loading" :data="memberList">
<el-table-column label="序号" type="index" width="60" align="center" :index="indexMethod" />
<el-table-column label="客户类型" align="center" width="100">
<el-table-column label="存量状态" align="center" width="90">
<template slot-scope="scope">
<el-tag v-if="scope.row.isExisting" size="small" type="success">存量</el-tag>
<el-tag v-else size="small" type="danger">非存量</el-tag>
</template>
</el-table-column>
<el-table-column label="客户类型" align="center" width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.custType === '0'" size="small">个人</el-tag>
<el-tag v-else-if="scope.row.custType === '1'" size="small" type="warning">商户</el-tag>
<el-tag v-else-if="scope.row.custType === '2'" size="small" type="success">企业</el-tag>
</template>
</el-table-column>
<el-table-column label="客户号" prop="custId" width="150" />
<el-table-column label="客户姓名" prop="custName" width="120" />
<el-table-column label="客户号" prop="custId" width="150">
<template slot-scope="scope">
<el-button type="text" @click="handleCustClick(scope.row)">{{ scope.row.custId }}</el-button>
</template>
</el-table-column>
<el-table-column label="客户姓名" prop="custName" width="120">
<template slot-scope="scope">
<el-button type="text" @click="handleCustClick(scope.row)">{{ scope.row.custName || '-' }}</el-button>
</template>
</el-table-column>
<el-table-column label="身份证号" prop="custIdc" show-overflow-tooltip />
<el-table-column label="统信码" prop="socialCreditCode" show-overflow-tooltip />
<el-table-column label="客户经理" prop="nickName" width="100">
<template slot-scope="scope">
<span v-if="scope.row.nickName">{{ scope.row.nickName }}</span>
<span v-else class="text-muted">-</span>
</template>
</el-table-column>
<el-table-column label="所属网点" prop="outletName" width="150">
<template slot-scope="scope">
<span v-if="scope.row.outletName">{{ scope.row.outletName }}</span>
<span v-else class="text-muted">-</span>
</template>
</el-table-column>
<el-table-column label="所属支行" prop="branchName" width="150">
<template slot-scope="scope">
<span v-if="scope.row.branchName">{{ scope.row.branchName }}</span>
<span v-else class="text-muted">-</span>
</template>
</el-table-column>
<el-table-column label="添加时间" prop="createTime" width="180" />
<el-table-column v-if="isMineView" label="操作" align="center" width="100" fixed="right">
<el-table-column v-if="isMineView" label="操作" align="center" width="80" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleRemove(scope.row)">移除</el-button>
</template>
@@ -155,11 +187,45 @@ export default {
/** 返回 */
goBack() {
const fromPerformance = this.$route.query.fromPerformance
if (fromPerformance === '1') {
// 从网格业绩统计页进入,返回网格业绩统计页的客群报表
this.$router.push({
path: '/center/performance/list',
query: { isActive: '8' }
})
} else {
// 从客群列表进入,返回客群列表
this.$router.push({
path: '/group/custGroup',
query: {
tab: this.viewType,
refresh: Date.now()
}
})
}
},
/** 点击客户号/姓名跳转360视图 */
handleCustClick(row) {
const custType = row.custType
let path = ''
if (custType === '0') {
// 个人客户
path = '/360charts/indexcharts'
} else if (custType === '1') {
// 商户
path = '/360charts/commercial/'
} else if (custType === '2') {
// 企业
path = '/360charts/firm/'
}
this.$router.push({
path: '/group/custGroup',
path: path,
query: {
tab: this.viewType,
refresh: Date.now()
custId: row.custId,
selectedTab: custType,
backUrl: '/group/custGroup'
}
})
},
@@ -214,5 +280,9 @@ export default {
margin-top: 15px;
}
}
.text-muted {
color: #909399;
}
}
</style>

View File

@@ -35,19 +35,6 @@
<el-option label="动态" value="1" />
</el-select>
</el-form-item>
<el-form-item label="创建方式" prop="createMode">
<el-select
v-model="queryParams.createMode"
placeholder="请选择"
clearable
style="width: 150px"
>
<el-option label="模板导入" value="1" />
<el-option label="绩效网格" value="2" />
<el-option label="地理网格" value="3" />
<el-option label="绘制网格" value="4" />
</el-select>
</el-form-item>
<el-form-item label="客群状态" prop="groupStatus">
<el-select
v-model="queryParams.groupStatus"
@@ -59,6 +46,22 @@
<el-option label="已禁用" value="1" />
</el-select>
</el-form-item>
<el-form-item label="客群标签" prop="groupTags">
<el-select
v-model="queryParams.groupTags"
placeholder="请选择"
clearable
filterable
style="width: 150px"
>
<el-option
v-for="tag in allTags"
:key="tag"
:label="tag"
:value="tag"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">
搜索
@@ -104,31 +107,38 @@
width="55"
align="center"
/>
<el-table-column label="客群名称" prop="groupName" min-width="180" show-overflow-tooltip />
<el-table-column label="客群模式" align="center" width="100">
<el-table-column label="客群名称" prop="groupName" width="120" show-overflow-tooltip />
<el-table-column label="客群模式" align="center" width="120">
<template slot-scope="scope">
<el-tag v-if="scope.row.groupMode === '0'" type="info" size="small">静态</el-tag>
<el-tag v-else-if="scope.row.groupMode === '1'" type="success" size="small">动态</el-tag>
</template>
</el-table-column>
<el-table-column label="创建方式" align="center" width="120">
<template slot-scope="scope">
<span v-if="scope.row.createMode === '1'">模板导入</span>
<span v-else-if="scope.row.createMode === '2'">绩效网格</span>
<span v-else-if="scope.row.createMode === '3'">地理网格</span>
<span v-else-if="scope.row.createMode === '4'">绘制网格</span>
</template>
</el-table-column>
<el-table-column label="客户数量" align="center" prop="custCount" width="100" />
<el-table-column label="客群状态" align="center" width="100">
<el-table-column label="客户数量" align="center" prop="custCount" width="120" />
<el-table-column label="客群状态" align="center" width="120">
<template slot-scope="scope">
<el-tag v-if="scope.row.groupStatus === '0'" type="success" size="small">正常</el-tag>
<el-tag v-else-if="scope.row.groupStatus === '1'" type="danger" size="small">已禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="客群标签" min-width="150">
<template slot-scope="scope">
<template v-if="scope.row.groupTags">
<el-tag
v-for="tag in scope.row.groupTags.split(',')"
:key="tag"
size="small"
style="margin-right: 5px; margin-bottom: 3px"
>
{{ tag.trim() }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="创建者" prop="nickName" width="120" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="180" fixed="right">
<el-table-column label="备注" prop="remark" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="200" fixed="right">
<template slot-scope="scope">
<el-button
size="mini"
@@ -179,7 +189,7 @@
</template>
<script>
import { listCustGroup, getCustGroup, deleteCustGroup } from '@/api/group/custGroup'
import { listCustGroup, getCustGroup, deleteCustGroup, getAllGroupTags } from '@/api/group/custGroup'
import CreateDialog from './components/create-dialog'
export default {
@@ -204,10 +214,12 @@ export default {
pageSize: 10,
groupName: null,
groupMode: null,
createMode: null,
groupStatus: null,
groupTags: null,
viewType: 'mine'
}
},
// 所有标签列表
allTags: []
}
},
computed: {
@@ -218,6 +230,7 @@ export default {
created() {
this.activeTab = this.$route.query.tab || 'mine'
this.getList()
this.loadAllTags()
},
watch: {
'$route.query.refresh'() {
@@ -261,6 +274,15 @@ export default {
})
},
/** 加载所有标签列表 */
loadAllTags() {
getAllGroupTags().then(response => {
this.allTags = response.data || []
}).catch(() => {
this.allTags = []
})
},
handleTabChange() {
this.ids = []
this.single = true
@@ -323,6 +345,12 @@ export default {
this.$modal.confirm('是否确认删除选中的客群?').then(() => {
return deleteCustGroup(ids)
}).then(() => {
if (this.groupList.length <= ids.length && this.queryParams.pageNum > 1) {
this.queryParams.pageNum -= 1
}
this.ids = []
this.single = true
this.multiple = true
this.refreshList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {})
@@ -339,7 +367,6 @@ export default {
id: null,
groupName: null,
groupMode: '0',
createMode: null,
groupStatus: '0',
shareEnabled: 0,
shareDeptIdList: [],

View File

@@ -0,0 +1,275 @@
<template>
<div class="customer-wrap">
<div class="content-box">
<div class="form-box">
<el-form ref="searchForm" :model="searchForm">
<el-row>
<el-col :span="6">
<el-form-item label="客户名称" prop="custName" label-width="80px">
<el-input
v-model="searchForm.custName"
clearable
@keyup.enter.native="handleSearch"
@clear="handleSearch"
/>
</el-form-item>
</el-col>
<el-col v-if="selectedTab === '1'" :span="6">
<el-form-item label="证件号" prop="custIdc" label-width="80px">
<el-input
v-model="searchForm.custIdc"
clearable
@keyup.enter.native="handleSearch"
@clear="handleSearch"
/>
</el-form-item>
</el-col>
<el-col v-else :span="6">
<el-form-item label="统一信用码" prop="socialCreditCode" label-width="100px">
<el-input
v-model="searchForm.socialCreditCode"
clearable
@keyup.enter.native="handleSearch"
@clear="handleSearch"
/>
</el-form-item>
</el-col>
<el-col :span="5" :offset="1" style="text-align: left">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-col>
</el-row>
</el-form>
</div>
<div class="operate-btn">
<el-button @click="returnLastPage">返回上一级</el-button>
<el-button type="primary" @click="handleDownload">导出</el-button>
</div>
<div class="remark">
<span>统计日期<span class="span-value">{{ dt }}</span></span>
<span>客群名称<span class="span-value">{{ groupName }}</span></span>
<span>客群模式<span class="span-value">{{ groupModeText }}</span></span>
</div>
<div class="main_table">
<el-table v-loading="loading" :data="tableData" style="width: 100%">
<el-table-column
v-for="(item, index) in tableColumns"
:key="index"
align="center"
:prop="item.prop"
:label="item.label"
show-overflow-tooltip
:width="item.width || 120"
>
<template slot-scope="scope">
<el-button
v-if="item.prop === 'custName'"
type="text"
class="special-btn"
@click="openCustomer(scope.row)"
>
{{ scope.row[item.prop] || '-' }}
</el-button>
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
</template>
</el-table-column>
</el-table>
<el-pagination
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="->,total,prev,pager,next,sizes"
:total="total"
:current-page="pageNum"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<script>
import {
getGroupPerformanceLsCustList,
getGroupPerformanceGsCustList,
exportGroupPerformanceLsCust,
exportGroupPerformanceGsCust
} from '@/api/group/performance';
import { retailDetailColumns, publicDetailColumns, groupModeDict } from '../list/list';
import { downloadFiles } from '@/utils';
export default {
name: 'GroupPerformanceCustom',
data() {
return {
loading: false,
tableData: [],
total: 0,
pageSize: 10,
pageNum: 1,
tableColumns: [],
selectedTab: '1',
dt: '',
groupId: '',
groupName: '',
groupMode: '',
searchForm: {
custName: '',
custIdc: '',
socialCreditCode: ''
}
};
},
computed: {
groupModeText() {
return groupModeDict[this.groupMode] || '-';
}
},
created() {
const { type, dt, groupId, groupName, groupMode } = this.$route.query;
this.selectedTab = type || '1';
this.dt = dt || '';
this.groupId = groupId || '';
this.groupName = groupName || '';
this.groupMode = groupMode || '';
this.tableColumns = this.selectedTab === '1' ? retailDetailColumns : publicDetailColumns;
this.getList();
},
methods: {
getList() {
this.loading = true;
const api = this.selectedTab === '1' ? getGroupPerformanceLsCustList : getGroupPerformanceGsCustList;
const params = {
groupId: this.groupId,
dt: this.dt,
custName: this.searchForm.custName,
custIdc: this.searchForm.custIdc,
socialCreditCode: this.searchForm.socialCreditCode,
pageNum: this.pageNum,
pageSize: this.pageSize
};
api(params).then(res => {
this.tableData = res.rows;
this.total = res.total;
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
handleSearch() {
this.pageNum = 1;
this.getList();
},
handleReset() {
this.searchForm = {
custName: '',
custIdc: '',
socialCreditCode: ''
};
this.pageNum = 1;
this.getList();
},
handleSizeChange(size) {
this.pageSize = size;
this.getList();
},
handleCurrentChange(page) {
this.pageNum = page;
this.getList();
},
handleDownload() {
const api = this.selectedTab === '1' ? exportGroupPerformanceLsCust : exportGroupPerformanceGsCust;
const fileName = this.selectedTab === '1' ? '零售客群客户明细' : '公司客群客户明细';
api({
groupId: this.groupId,
dt: this.dt,
custName: this.searchForm.custName,
custIdc: this.searchForm.custIdc,
socialCreditCode: this.searchForm.socialCreditCode
}).then(res => {
downloadFiles(res, `${fileName}_${new Date().getTime()}.xlsx`);
});
},
returnLastPage() {
this.$router.replace({
path: '/center/groupPerformance/list'
});
},
openCustomer(row) {
const custType = row.custType;
let path = '';
const rawId = this.selectedTab === '1' ? row.custIdc : row.socialCreditCode;
const custId = rawId ? (custType === '0' ? `101${rawId}` : `202${rawId}`) : '';
if (custType === '0') {
path = '/360charts/indexcharts';
} else if (custType === '1') {
path = '/360charts/commercial/';
} else if (custType === '2') {
path = '/360charts/firm/';
}
if (!path || !custId) {
return;
}
this.$router.push({
path: path,
query: {
custId,
selectedTab: custType,
backUrl: '/center/groupPerformance/list'
}
});
}
}
};
</script>
<style lang="scss" scoped>
.customer-wrap {
background-color: #ffffff;
overflow: hidden;
box-shadow: 0 3px 8px 0 #00000017;
}
.content-box {
padding: 20px 24px 24px;
}
.operate-btn {
margin-bottom: 12px;
.el-button + .el-button {
margin-left: 12px;
}
}
.remark {
margin-bottom: 12px;
color: #666666;
span {
display: inline-block;
margin-right: 20px;
margin-bottom: 8px;
}
.span-value {
color: #333333;
font-weight: 500;
}
}
.main_table {
margin-top: 15px;
}
::v-deep .el-input {
width: 100%;
}
::v-deep .el-pagination {
margin-top: 15px;
}
</style>

View File

@@ -0,0 +1,294 @@
<template>
<div class="customer-wrap">
<el-radio-group
v-model="selectedTab"
class="header-radio"
@input="handleTabChange"
>
<el-radio-button
label="1"
:disabled="isPublic"
:class="{ 'btn-disabled': isPublic }"
>零售部</el-radio-button>
<el-radio-button
label="2"
:disabled="isPrivate"
:class="{ 'btn-disabled': isPrivate }"
>公司部</el-radio-button>
</el-radio-group>
<div class="content-box">
<div class="form-box">
<el-form ref="searchForm" :model="searchForm">
<el-row>
<el-col :span="6">
<el-form-item label="客群名称" prop="groupName" label-width="80px">
<el-input
v-model="searchForm.groupName"
clearable
@keyup.enter.native="handleSearch"
@clear="handleSearch"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="统计日期" prop="dt" label-width="80px">
<el-date-picker
v-model="searchForm.dt"
type="date"
value-format="yyyy-MM-dd"
:clearable="false"
:picker-options="pickerOptions"
@change="handleSearch"
/>
</el-form-item>
</el-col>
<el-col :span="4" style="text-align: right">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-col>
</el-row>
</el-form>
</div>
<div>
<el-button type="primary" @click="handleDownload">导出</el-button>
</div>
<div class="main_table">
<el-table
:key="tableKey"
v-loading="loading"
:data="tableData"
style="width: 100%"
max-height="650"
>
<el-table-column
v-for="(item, index) in tableColumns"
:key="index"
align="center"
:prop="item.prop"
:label="item.label"
show-overflow-tooltip
:width="item.width || 120"
>
<template slot-scope="scope">
<span v-if="item.dict">{{ item.dict[scope.row[item.prop]] || '-' }}</span>
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="100" fixed="right">
<template slot-scope="scope">
<el-button type="text" @click="handleView(scope.row)">客户明细</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="->,total,prev,pager,next,sizes"
:total="total"
:current-page="pageNum"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<script>
import {
getGroupPerformanceLsList,
getGroupPerformanceGsList,
exportGroupPerformanceLs,
exportGroupPerformanceGs
} from '@/api/group/performance';
import { retailColumns, publicColumns } from './list';
import { mapGetters } from 'vuex';
import { downloadFiles } from '@/utils';
export default {
name: 'GroupPerformanceList',
data() {
return {
pickerOptions: {
disabledDate(time) {
const today = new Date();
today.setHours(0, 0, 0, 0);
return time.getTime() >= today.getTime();
}
},
selectedTab: '1',
loading: false,
tableData: [],
total: 0,
pageSize: 10,
pageNum: 1,
tableColumns: [],
tableKey: 'group-performance-1',
searchForm: {
groupName: '',
dt: ''
}
};
},
computed: {
...mapGetters(['roles']),
isPublic() {
return this.roles.includes('headPublic');
},
isPrivate() {
return this.roles.includes('headPrivate');
}
},
created() {
const date = new Date();
date.setDate(date.getDate() - 1);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
this.searchForm.dt = `${year}-${month}-${day}`;
if (this.isPublic) {
this.selectedTab = '2';
} else if (this.isPrivate) {
this.selectedTab = '1';
}
this.updateColumns();
this.getList();
},
methods: {
updateColumns() {
this.tableColumns = this.selectedTab === '1' ? retailColumns : publicColumns;
this.tableKey = `group-performance-${this.selectedTab}`;
},
getList() {
this.loading = true;
const api = this.selectedTab === '1' ? getGroupPerformanceLsList : getGroupPerformanceGsList;
api({
...this.searchForm,
pageNum: this.pageNum,
pageSize: this.pageSize
}).then(res => {
this.tableData = res.rows;
this.total = res.total;
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
handleTabChange() {
this.updateColumns();
this.pageNum = 1;
this.getList();
},
handleSearch() {
this.pageNum = 1;
this.getList();
},
handleReset() {
const date = new Date();
date.setDate(date.getDate() - 1);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
this.searchForm = {
groupName: '',
dt: `${year}-${month}-${day}`
};
this.pageNum = 1;
this.getList();
},
handleSizeChange(size) {
this.pageSize = size;
this.getList();
},
handleCurrentChange(page) {
this.pageNum = page;
this.getList();
},
handleView(row) {
this.$router.push({
path: '/center/groupPerformance/custom',
query: {
type: this.selectedTab,
dt: row.dt,
groupId: row.groupId,
groupName: row.groupName,
groupMode: row.groupMode
}
});
},
handleDownload() {
const api = this.selectedTab === '1' ? exportGroupPerformanceLs : exportGroupPerformanceGs;
const fileName = this.selectedTab === '1' ? '零售客群业绩汇总' : '公司客群业绩汇总';
api(this.searchForm).then(res => {
downloadFiles(res, `${fileName}_${new Date().getTime()}.xlsx`);
});
}
}
};
</script>
<style lang="scss" scoped>
.customer-wrap {
background-color: #ffffff;
overflow: hidden;
border-radius: 16px 16px 0 0;
box-shadow: 0 3px 8px 0 #00000017;
.header-radio {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #ebebeb;
.btn-disabled {
::v-deep .el-radio-button__inner {
background-color: #e7e7e7;
}
}
.el-radio-button {
flex: 1;
::v-deep .el-radio-button__inner {
width: 100%;
border: none;
font-weight: 400;
letter-spacing: 0.44px;
line-height: 25px;
font-size: 16px;
color: #666666;
padding: 11px 0 12px 0;
}
::v-deep .el-radio-button__orig-radio:checked + .el-radio-button__inner {
background-color: #4886f8;
font-weight: 400;
color: #ffffff;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
}
}
.content-box {
padding: 20px 24px 24px;
}
.main_table {
margin-top: 15px;
}
::v-deep .el-date-editor,
::v-deep .el-input,
::v-deep .el-select {
width: 100%;
}
::v-deep .el-pagination {
margin-top: 15px;
}
</style>

View File

@@ -0,0 +1,194 @@
export const groupModeDict = {
'0': '静态',
'1': '动态'
};
export const retailColumns = [
{ prop: 'dt', label: '统计日期', width: 140 },
{ prop: 'groupName', label: '客群名称', width: 180 },
{ prop: 'groupMode', label: '客群模式', width: 100, dict: groupModeDict },
{ prop: 'deptId', label: '归属支行机构号', width: 140 },
{ prop: 'deptName', label: '归属支行名称', width: 160 },
{ prop: 'outletsId', label: '归属网点机构号', width: 140 },
{ prop: 'outletsName', label: '归属网点名称', width: 160 },
{ prop: 'userName', label: '归属客户经理', width: 140 },
{ prop: 'custNum', label: '入格客户数', width: 120 },
{ prop: 'zf365cnt', label: '近365天已走访人数', width: 150 },
{ prop: 'zf180cnt', label: '近180天已走访人数', width: 150 },
{ prop: 'zf90cnt', label: '近90天已走访人数', width: 150 },
{ prop: 'zf30cnt', label: '近30天已走访人数', width: 150 },
{ prop: 'zf365rt', label: '近365天走访率', width: 140 },
{ prop: 'zf180rt', label: '近180天走访率', width: 140 },
{ prop: 'zf90rt', label: '近90天走访率', width: 140 },
{ prop: 'zf30rt', label: '近30天走访率', width: 140 },
{ prop: 'curBalD', label: '活期存款余额(元)', width: 160 },
{ prop: 'sxRat', label: '授信率(%', width: 120 },
{ prop: 'yxRat', label: '用信覆盖率', width: 120 },
{ prop: 'sxNum', label: '授信户数', width: 120 },
{ prop: 'yxNum', label: '用信户数', width: 120 },
{ prop: 'sxBal', label: '授信金额(合同)', width: 160 },
{ prop: 'balLoan', label: '贷款余额(元)', width: 150 },
{ prop: 'loanAve', label: '贷款年日均(元)', width: 150 },
{ prop: 'yxhtRat', label: '合同签约率(%', width: 140 },
{ prop: 'dianRat', label: '代扣电费覆盖率(%', width: 150 },
{ prop: 'shuiRat', label: '代扣水费率(%', width: 140 },
{ prop: 'taxRat', label: '代扣税费率(%', width: 140 },
{ prop: 'openRat', label: '开户率(%', width: 120 },
{ prop: 'yxhtNum', label: '合同签约客户数', width: 150 },
{ prop: 'dianNum', label: '代扣电费客户数', width: 150 },
{ prop: 'shuiNum', label: '代扣水费客户数', width: 150 },
{ prop: 'taxNum', label: '代扣税费数', width: 140 },
{ prop: 'openNum', label: '开户数', width: 120 },
{ prop: 'depBal', label: '存款余额(元)', width: 150 },
{ prop: 'finBal', label: '财富余额(元)', width: 150 },
{ prop: 'grhxNum', label: '个人核心客户数', width: 150 },
{ prop: 'cfyxNum', label: '财富有效客户数', width: 150 },
{ prop: 'yxxykNum', label: '有效信用卡数', width: 140 },
{ prop: 'yxsbkNum', label: '有效社保卡户数', width: 150 },
{ prop: 'twoTo3SbkNum', label: '二换三社保卡户数', width: 160 },
{ prop: 'yljToSbkNum', label: '养老金迁移至社保卡户数', width: 180 },
{ prop: 'fshlNum', label: '丰收互联客户数', width: 150 },
{ prop: 'yxsdNum', label: '有效收单商户', width: 140 },
{ prop: 'hxsdNum', label: '核心收单户数', width: 140 },
{ prop: 'regionCode', label: '归属行政区划编码', width: 160 }
];
export const publicColumns = [
{ prop: 'dt', label: '统计日期', width: 140 },
{ prop: 'groupName', label: '客群名称', width: 180 },
{ prop: 'groupMode', label: '客群模式', width: 100, dict: groupModeDict },
{ prop: 'deptId', label: '归属支行', width: 120 },
{ prop: 'deptName', label: '归属支行名称', width: 140 },
{ prop: 'outletsId', label: '归属网点', width: 120 },
{ prop: 'outletsName', label: '归属网点名称', width: 140 },
{ prop: 'userName', label: '归属客户经理', width: 140 },
{ prop: 'custNum', label: '入格客户数', width: 120 },
{ prop: 'hqCurBalance', label: '活期存款余额', width: 140 },
{ prop: 'bzCurBalance', label: '保证金存款余额', width: 150 },
{ prop: 'loanBalanceCny', label: '贷款余额', width: 140 },
{ prop: 'financeProd711Balance', label: '贴现余额', width: 140 },
{ prop: 'financeProd716Balance', label: '承兑汇票余额', width: 150 },
{ prop: 'loanYearDailyaverage', label: '贷款年日均', width: 140 },
{ prop: 'qfcdRat', label: '签发承兑汇票率', width: 140 },
{ prop: 'txRat', label: '贴现业务率', width: 120 },
{ prop: 'bhRat', label: '保函业务率', width: 120 },
{ prop: 'yxdfgzRat', label: '有效代发工资率', width: 150 },
{ prop: 'dkdfRat', label: '代扣电费率', width: 120 },
{ prop: 'dksfRat', label: '代扣水费率', width: 120 },
{ prop: 'dkshfRat', label: '代扣税费率', width: 120 },
{ prop: 'pjbRat', label: '票据宝签约率', width: 130 },
{ prop: 'czbRat', label: '财资宝签约率', width: 130 },
{ prop: 'sfbRat', label: '收付宝签约率', width: 130 },
{ prop: 'mrbRat', label: '贸融宝签约率', width: 130 },
{ prop: 'szstRat', label: '数字生态产品签约率', width: 170 },
{ prop: 'khRat', label: '开户率', width: 120 },
{ prop: 'gjjsywRat', label: '国际结算业务率', width: 150 },
{ prop: 'yqjshRat', label: '远期结算汇业务率', width: 160 },
{ prop: 'qfcdNum', label: '签发承兑汇票数', width: 150 },
{ prop: 'txNum', label: '贴现业务数', width: 120 },
{ prop: 'bhNum', label: '保函业务数', width: 120 },
{ prop: 'yxdfgzNum', label: '有效代发工资数', width: 150 },
{ prop: 'ustrCountPerM', label: '月均代发工资笔数', width: 150 },
{ prop: 'ustrBalM', label: '月均代发工资金额(元)', width: 170 },
{ prop: 'dkdfNum', label: '代扣电费数', width: 120 },
{ prop: 'dksfNum', label: '代扣水费数', width: 120 },
{ prop: 'dkshfNum', label: '代扣税费数', width: 120 },
{ prop: 'pjbNum', label: '票据宝签约数', width: 130 },
{ prop: 'czbNum', label: '财资宝签约数', width: 130 },
{ prop: 'sfbNum', label: '收付宝签约数', width: 130 },
{ prop: 'mrbNum', label: '贸融宝签约数', width: 130 },
{ prop: 'szstNum', label: '数字生态产品签约数', width: 170 },
{ prop: 'khNum', label: '开户数', width: 120 },
{ prop: 'gjjsywNum', label: '国际结算业务数', width: 150 },
{ prop: 'yqjshNum', label: '远期结算汇业务数', width: 160 },
{ prop: 'htqyRat', label: '合同签约率', width: 130 },
{ prop: 'htqyNum', label: '合同签约数', width: 130 },
{ prop: 'zf365cnt', label: '近365天已走访人数', width: 150 },
{ prop: 'zf180cnt', label: '近180天已走访人数', width: 150 },
{ prop: 'zf90cnt', label: '近90天已走访人数', width: 150 },
{ prop: 'zf30cnt', label: '近30天已走访人数', width: 150 },
{ prop: 'zf365rt', label: '近365天走访率', width: 140 },
{ prop: 'zf180rt', label: '近180天走访率', width: 140 },
{ prop: 'zf90rt', label: '近90天走访率', width: 140 },
{ prop: 'zf30rt', label: '近30天走访率', width: 140 },
{ prop: 'phRat', label: '建档率', width: 120 },
{ prop: 'phNum', label: '建档数', width: 120 },
{ prop: 'regionCode', label: '归属行政区划编码', width: 160 }
];
export const retailDetailColumns = [
{ prop: 'custName', label: '客户名称', width: 160 },
{ prop: 'custIdc', label: '客户证件号', width: 180 },
{ prop: 'custIsn', label: '客户内码', width: 140 },
{ prop: 'curBalD', label: '活期存款余额', width: 140 },
{ prop: 'balLoan', label: '贷款余额', width: 140 },
{ prop: 'loanAve', label: '贷款年日均', width: 140 },
{ prop: 'isSx', label: '是否授信', width: 100 },
{ prop: 'isYx', label: '是否用信', width: 100 },
{ prop: 'sxBal', label: '授信金额', width: 120 },
{ prop: 'isYxht', label: '是否合同签约', width: 120 },
{ prop: 'isXyk', label: '是否持有信用卡', width: 130 },
{ prop: 'fshl', label: '是否开通丰收互联', width: 140 },
{ prop: 'isSd', label: '是否办理收单', width: 120 },
{ prop: 'dian', label: '是否代扣电费', width: 120 },
{ prop: 'shui', label: '是否代扣水费', width: 120 },
{ prop: 'tax', label: '是否代扣税费', width: 120 },
{ prop: 'openNum', label: '开户数', width: 100 },
{ prop: 'depBal', label: '存款余额', width: 120 },
{ prop: 'finBal', label: '财富余额', width: 120 },
{ prop: 'isGrhx', label: '是否个人核心客户', width: 140 },
{ prop: 'isCfyx', label: '是否财富有效客户', width: 140 },
{ prop: 'isYxsbk', label: '是否有效社保卡客户', width: 150 },
{ prop: 'is2to3Sbk', label: '是否二换三社保卡', width: 140 },
{ prop: 'isYljToSbk', label: '是否养老金迁移至社保卡', width: 180 },
{ prop: 'is365zf', label: '近365天有无走访', width: 140 },
{ prop: 'is180zf', label: '近180天有无走访', width: 140 },
{ prop: 'is90zf', label: '近90天有无走访', width: 140 },
{ prop: 'is30zf', label: '近30天有无走访', width: 140 },
{ prop: 'isHxsd', label: '是否核心收单', width: 120 },
{ prop: 'custType', label: '客户类型', width: 100 },
{ prop: 'regionCode', label: '归属行政区划编码', width: 160 }
];
export const publicDetailColumns = [
{ prop: 'custName', label: '客户名称', width: 160 },
{ prop: 'socialCreditCode', label: '客户证件号', width: 200 },
{ prop: 'custIsn', label: '客户内码', width: 140 },
{ prop: 'hqCurBalance', label: '活期存款余额', width: 140 },
{ prop: 'bzCurBalance', label: '保证金存款余额', width: 150 },
{ prop: 'isCredit', label: '是否授信', width: 100 },
{ prop: 'loanBalanceCny', label: '贷款余额', width: 140 },
{ prop: 'loanYearDailyaverage', label: '贷款年日均', width: 140 },
{ prop: 'financeProd716OpenFlag', label: '是否有签发承兑汇票', width: 160 },
{ prop: 'financeProd716Balance', label: '承兑汇票余额', width: 150 },
{ prop: 'financeProd711OpenFlag', label: '是否有贴现业务', width: 140 },
{ prop: 'financeProd711Balance', label: '贴现金额', width: 120 },
{ prop: 'intlBussinessJcbhOpenFlag', label: '是否有保函业务', width: 140 },
{ prop: 'isUstr', label: '是否为有效代发工资客户', width: 170 },
{ prop: 'ustrCountPerM', label: '月均代发工资笔数', width: 150 },
{ prop: 'ustrBalM', label: '月均代发工资金额(元)', width: 170 },
{ prop: 'elecchargeSignFlag', label: '是否代扣电费', width: 120 },
{ prop: 'waterchargeSignFlag', label: '是否代扣水费', width: 120 },
{ prop: 'taxdeductionSignFlag', label: '是否代扣税费', width: 120 },
{ prop: 'pjb', label: '是否票据宝签约', width: 130 },
{ prop: 'czb', label: '是否财资宝签约', width: 130 },
{ prop: 'sfb', label: '是否收付宝签约', width: 130 },
{ prop: 'mrb', label: '是否贸融宝签约', width: 130 },
{ prop: 'szst', label: '是否数字生态产品签约', width: 170 },
{ prop: 'isOpenSts', label: '是否开户', width: 100 },
{ prop: 'intlBussinessOpenFlag', label: '是否国际结算业务', width: 150 },
{ prop: 'intlBussiness325OpenFlag', label: '是否有远期结算汇业务', width: 170 },
{ prop: 'isHtqy', label: '是否合同签约', width: 120 },
{ prop: 'deptName', label: '归属支行名称', width: 140 },
{ prop: 'outletsId', label: '归属网点id', width: 120 },
{ prop: 'outletsName', label: '归属网点名称', width: 140 },
{ prop: 'userName', label: '归属客户经理', width: 140 },
{ prop: 'deptId', label: '归属支行id', width: 120 },
{ prop: 'is365zf', label: '近365天有无走访', width: 140 },
{ prop: 'is180zf', label: '近180天有无走访', width: 140 },
{ prop: 'is90zf', label: '近90天有无走访', width: 140 },
{ prop: 'is30zf', label: '近30天有无走访', width: 140 },
{ prop: 'isPh', label: '是否建档', width: 100 },
{ prop: 'custType', label: '客户类型', width: 100 },
{ prop: 'regionCode', label: '归属行政区划编码', width: 160 }
];