Compare commits
8 Commits
dev
...
15891708de
| Author | SHA1 | Date | |
|---|---|---|---|
| 15891708de | |||
| 59e05e85b1 | |||
| 7f5a045cab | |||
| 4345c2193f | |||
| 23ff1b0b1d | |||
| 44fd20316d | |||
| e3e26574c6 | |||
| 5996173abd |
313
CLAUDE.md
313
CLAUDE.md
@@ -2,215 +2,186 @@
|
|||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
## 项目概述
|
## Project Overview
|
||||||
|
|
||||||
**数字支行辅助管理系统(IBS)** - 基于 若依框架 v3.8.8 的前后端分离全栈项目,专注于银行支行的网格化营销、客户管理和走访业务。
|
This is a full-stack banking management system (数字支行辅助管理系统) built on the RuoYi framework v3.8.8. It provides grid-based customer relationship management, customer grouping, visit tracking, and performance statistics for banking institutions.
|
||||||
|
|
||||||
## 常用命令
|
- **Backend**: Spring Boot 2.5.14 + MyBatis + MySQL + Redis + JWT
|
||||||
|
- **Frontend**: Vue 2.6.12 + Element UI 2.15.14
|
||||||
|
- **Java Version**: 1.8
|
||||||
|
|
||||||
### 后端开发
|
## Architecture
|
||||||
|
|
||||||
```bash
|
### Backend Module Structure
|
||||||
# Maven 打包(跳过测试)
|
|
||||||
mvn clean package -Dmaven.test.skip=true
|
|
||||||
|
|
||||||
# 运行已打包的 JAR
|
The backend is a multi-module Maven project with the following modules:
|
||||||
cd ruoyi-admin/target
|
|
||||||
java -jar -Xms256m -Xmx1024m ruoyi-admin.jar
|
|
||||||
|
|
||||||
# 后端服务地址
|
```
|
||||||
http://localhost:8080
|
ruoyi/
|
||||||
|
├── ruoyi-admin/ # Web entry point, main application (RuoYiApplication)
|
||||||
# Swagger API 文档
|
├── ruoyi-framework/ # Core framework (security, config, interceptors)
|
||||||
http://localhost:8080/swagger-ui/index.html
|
├── ruoyi-system/ # System management (users, roles, menus, depts)
|
||||||
|
├── ruoyi-common/ # Common utilities, annotations, domain classes
|
||||||
# 测试登录接口获取 token
|
├── ruoyi-quartz/ # Scheduled task management
|
||||||
POST /login/test?username=admin&password=admin123
|
├── ruoyi-generator/ # Code generation tools
|
||||||
|
├── ibs/ # Main business module: grid customer management (网格营销)
|
||||||
|
└── ibs-group/ # Customer group management module (客户分组)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 前端开发
|
### Business Domain Structure
|
||||||
|
|
||||||
|
Each business module (e.g., `ibs`, `ibs-group`) follows this package structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
com.ruoyi.<module>/
|
||||||
|
├── controller/ # REST controllers (handle HTTP requests)
|
||||||
|
├── service/ # Business logic layer
|
||||||
|
├── mapper/ # MyBatis mappers (database access)
|
||||||
|
├── domain/
|
||||||
|
│ ├── entity/ # Database entities
|
||||||
|
│ ├── dto/ # Data Transfer Objects (request)
|
||||||
|
│ └── vo/ # View Objects (response)
|
||||||
|
└── handler/ # Custom MyBatis type handlers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ruoyi-ui/
|
||||||
|
├── src/
|
||||||
|
│ ├── api/ # API request modules (organized by feature)
|
||||||
|
│ ├── views/ # Page components
|
||||||
|
│ ├── components/ # Reusable components
|
||||||
|
│ ├── store/ # Vuex state management
|
||||||
|
│ ├── router/ # Vue Router configuration
|
||||||
|
│ ├── utils/ # Utility functions
|
||||||
|
│ └── directive/ # Custom Vue directives
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build and Run Commands
|
||||||
|
|
||||||
|
### Backend (Maven)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clean build artifacts
|
||||||
|
cd bin && clean.bat
|
||||||
|
|
||||||
|
# Package the project (creates JAR in ruoyi-admin/target/)
|
||||||
|
cd bin && package.bat
|
||||||
|
|
||||||
|
# Run the backend server (requires packaged JAR)
|
||||||
|
cd bin && run.bat
|
||||||
|
|
||||||
|
# Or run directly with Maven from ruoyi-admin/
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# The main class is: com.ruoyi.RuoYiApplication
|
||||||
|
# Default port: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend (npm/Vue CLI)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ruoyi-ui
|
cd ruoyi-ui
|
||||||
|
|
||||||
# 安装依赖
|
# Development server (runs on port 80)
|
||||||
npm install
|
|
||||||
|
|
||||||
# 开发环境运行(端口 80,代理到 localhost:8080)
|
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# 生产环境构建
|
# For older Node.js versions with OpenSSL issues
|
||||||
|
npm run dev_t
|
||||||
|
|
||||||
|
# Build for production
|
||||||
npm run build:prod
|
npm run build:prod
|
||||||
|
|
||||||
# 代码检查
|
# Build for staging
|
||||||
|
npm run build:stage
|
||||||
|
|
||||||
|
# Build for pre-production
|
||||||
|
npm run build:pre
|
||||||
|
|
||||||
|
# Lint code
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### 数据库连接
|
## Development Configuration
|
||||||
|
|
||||||
```bash
|
### Application Profiles
|
||||||
# 通过 MCP MySQL 工具连接
|
|
||||||
# 地址: 116.62.17.81:3306
|
|
||||||
# 数据库: ibs
|
|
||||||
# 用户名: root
|
|
||||||
```
|
|
||||||
|
|
||||||
## 核心架构
|
Backend uses Spring profiles located in `ruoyi-admin/src/main/resources/`:
|
||||||
|
- `application.yml` - Base configuration
|
||||||
|
- `application-dev.yml` - Development environment
|
||||||
|
- `application-uat.yml` - UAT environment
|
||||||
|
- `application-pre.yml` - Pre-production
|
||||||
|
- `application-pro.yml` - Production
|
||||||
|
|
||||||
### 后端模块结构
|
Active profile is set in `application.yml` (default: `dev`).
|
||||||
|
|
||||||
```
|
### Frontend API Proxy
|
||||||
ruoyi-admin/ # 主入口模块,包含启动类和配置文件
|
|
||||||
ruoyi-framework/ # 框架核心:安全配置、缓存、数据源等
|
|
||||||
ruoyi-system/ # 系统管理:用户、角色、菜单、字典等
|
|
||||||
ruoyi-common/ # 通用工具:工具类、注解、常量等
|
|
||||||
ruoyi-quartz/ # 定时任务模块
|
|
||||||
ruoyi-generator/ # 代码生成器
|
|
||||||
ibs/ # ★ 业务模块:数字支行核心业务 ★
|
|
||||||
```
|
|
||||||
|
|
||||||
### IBS 业务模块 (核心业务)
|
The Vue dev server proxies API requests to `http://localhost:8080`:
|
||||||
|
- Frontend dev server: `http://localhost:80`
|
||||||
|
- API requests: `/dev-api/*` → `http://localhost:8080/*`
|
||||||
|
|
||||||
位置: `ibs/src/main/java/com/ruoyi/ibs/`
|
## Key Patterns and Conventions
|
||||||
|
|
||||||
**主要业务包:**
|
### Backend Code Patterns
|
||||||
|
|
||||||
| 包名 | 功能 | 说明 |
|
1. **Controller-Service-Mapper Layering**
|
||||||
|------|------|------|
|
- Controllers handle HTTP requests/responses, use `@RestController`
|
||||||
| `grid` | 网格管理 | 支行网格划分、分配、统计 |
|
- Services contain business logic, use `@Service`
|
||||||
| `cmpm` | 客户经理管理 | 客户经理信息维护 |
|
- Mappers use MyBatis annotations or XML files in `resources/mapper/`
|
||||||
| `list` | 客户列表管理 | 零售/商户/企业客户管理 |
|
|
||||||
| `visit` | 走访管理 | 走访任务、记录、轨迹 |
|
|
||||||
| `task` | 任务管理 | 营销任务分配和跟踪 |
|
|
||||||
| `draw` | 绘图/网格绘制 | 基于百度地图的网格绘制 |
|
|
||||||
| `custmap` | 客户地图 | 客户地理分布可视化 |
|
|
||||||
| `dashboard` | 仪表盘 | 数据统计和展示 |
|
|
||||||
| `datavisual` | 数据可视化 | 报表和图表 |
|
|
||||||
| `rules` | 规则配置 | 业务规则配置 |
|
|
||||||
| `qxhy` | 青县惠银接口 | 外部系统对接 |
|
|
||||||
| `websocket` | WebSocket通信 | 实时通信支持 |
|
|
||||||
|
|
||||||
**业务模块命名规范:**
|
2. **DTO/VO Pattern**
|
||||||
- 新建模块命名: `ibs` + 主要功能(如 `ibs-grid`, `ibs-customer`)
|
- DTOs (Data Transfer Objects) for incoming requests
|
||||||
- Controller 放在新建模块中,不与若依框架混合
|
- VOs (View Objects) for outgoing responses
|
||||||
- Entity 使用 `@Data` 注解
|
- Entities map directly to database tables
|
||||||
- Service 使用 `@Resource` 注解,不继承 `ServiceImpl`
|
|
||||||
- DAO 使用 MyBatis Plus,复杂操作在 XML 中编写 SQL
|
|
||||||
|
|
||||||
### 前端结构
|
3. **Security & Authentication**
|
||||||
|
- JWT-based authentication via `TokenService`
|
||||||
|
- Role-based access: `head`, `branch`, `outlet`, `manager`
|
||||||
|
- Use `SecurityUtils` for current user context: `SecurityUtils.getUsername()`, `SecurityUtils.getDeptId()`, `SecurityUtils.userRole()`
|
||||||
|
|
||||||
```
|
4. **Pagination**
|
||||||
ruoyi-ui/src/
|
- Uses `PageHelper` for database pagination
|
||||||
├── api/ # API 接口定义
|
- Controllers return `TableDataInfo` with `total` and `rows`
|
||||||
├── views/ # 页面视图
|
|
||||||
│ ├── grid/ # 网格管理相关页面
|
|
||||||
│ ├── customer/ # 客户管理
|
|
||||||
│ ├── taskManage/ # 任务管理
|
|
||||||
│ ├── dashboard/ # 仪表盘
|
|
||||||
│ └── ...
|
|
||||||
├── components/ # 公共组件
|
|
||||||
├── map/ # 地图相关(百度地图集成)
|
|
||||||
├── store/ # Vuex 状态管理
|
|
||||||
└── router/ # 路由配置
|
|
||||||
```
|
|
||||||
|
|
||||||
### 数据库表命名规范
|
5. **Caching**
|
||||||
|
- Uses `RedisCache` for Redis operations
|
||||||
|
- Cache keys often follow pattern: `{module}:{feature}:{key}`
|
||||||
|
|
||||||
- 新建表需加项目前缀: `ibs_` + 表名
|
### Frontend Code Patterns
|
||||||
- 示例: `ibs_grid`, `ibs_customer`, `ibs_visit_record`
|
|
||||||
|
|
||||||
## 关键技术点
|
1. **API Modules**
|
||||||
|
- Each feature has a dedicated API file in `src/api/`
|
||||||
|
- Uses `request()` wrapper around axios
|
||||||
|
- API base URL is configured in `src/utils/request.js`
|
||||||
|
|
||||||
### 1. 地图集成
|
2. **Vuex Store**
|
||||||
|
- Modules in `src/store/modules/`: user, app, permission, settings, tagsView
|
||||||
|
- State persists via `vuex-persistedstate`
|
||||||
|
|
||||||
项目深度集成百度地图 API,用于:
|
3. **Permission Directives**
|
||||||
- 网格绘制和编辑
|
- `v-hasPermi` for button-level permissions
|
||||||
- 客户地理位置标注
|
- `v-hasRole` for role-based display
|
||||||
- 走访轨迹记录
|
|
||||||
- 客户分布热力图
|
|
||||||
|
|
||||||
**相关配置:**
|
## Common Business Concepts
|
||||||
- 百度地图 AK 在前端配置
|
|
||||||
- 使用 JTS 库进行地理空间计算
|
|
||||||
|
|
||||||
### 2. 批量导入优化
|
- **网格**: Grid-based territory management for customer assignment
|
||||||
|
- **客户经理**: Relationship managers assigned to customers
|
||||||
|
- **客户星级**: Customer rating levels (5星, 4星, 3星, 2星, 1星, 基础, 长尾)
|
||||||
|
- **AUM (Asset Under Management)**: Customer assets, tracked with monthly averages
|
||||||
|
- **PAD走访**: Mobile visit records for customer interactions
|
||||||
|
|
||||||
设计批量导入功能时:
|
## MyBatis Configuration
|
||||||
- 使用批量操作提高响应速度
|
|
||||||
- 导入结果只展示失败数据,不展示成功数据
|
|
||||||
- 使用 EasyExcel 处理 Excel
|
|
||||||
|
|
||||||
### 3. 多端支持
|
- Mapper XML files: `classpath*:mapper/**/*Mapper.xml`
|
||||||
|
- Type aliases: `com.ruoyi.**.domain`
|
||||||
|
- Custom type handlers: `com.ruoyi.ibs.handler`
|
||||||
|
- Pagination: `pagehelper` with MySQL dialect
|
||||||
|
|
||||||
- PC 端: 主要管理和配置功能
|
## Notes
|
||||||
- PAD 端: 走访记录功能(移动端)
|
|
||||||
|
|
||||||
### 4. 外部系统对接
|
- The project uses Chinese comments and variable names in many places
|
||||||
|
- Scheduled tasks use Quartz via `ruoyi-quartz` module
|
||||||
| 系统 | 地址 | 用途 |
|
- File upload path configured in `application.yml`: `ruoyi.profile`
|
||||||
|------|------|------|
|
- Swagger/Knife4j API docs available when running
|
||||||
| 青县惠银 | http://158.234.96.76:5002 | 业务数据对接 |
|
|
||||||
| BI 系统 | http://158.220.52.42:9388/bi | 数据分析和报表 |
|
|
||||||
| 阿里云 OSS | - | 文件存储 |
|
|
||||||
|
|
||||||
## 开发规范
|
|
||||||
|
|
||||||
### 代码分层
|
|
||||||
|
|
||||||
- **Entity**: 实体类,不继承 BaseEntity,单独添加审计字段
|
|
||||||
- **DTO**: 接口传参专用类
|
|
||||||
- **VO**: 返回数据专用类
|
|
||||||
- **Service**: 业务逻辑,使用 `@Resource` 注入
|
|
||||||
- **Mapper**: MyBatis Plus + XML 混合使用
|
|
||||||
|
|
||||||
### 审计字段
|
|
||||||
|
|
||||||
通过注解实现自动填充:
|
|
||||||
```java
|
|
||||||
@TableField(fill = FieldFill.INSERT)
|
|
||||||
private String createBy;
|
|
||||||
|
|
||||||
@TableField(fill = FieldFill.INSERT)
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
|
||||||
private String updateBy;
|
|
||||||
|
|
||||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
|
||||||
private Date updateTime;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 分页
|
|
||||||
|
|
||||||
使用 MyBatis Plus 分页插件自动处理,传入 `Page` 对象即可。
|
|
||||||
|
|
||||||
### 前端菜单配置
|
|
||||||
|
|
||||||
添加新页面后,需要在数据库 `sys_menu` 表中配置菜单权限。
|
|
||||||
|
|
||||||
## 重要配置文件
|
|
||||||
|
|
||||||
| 文件 | 位置 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| 后端配置 | `ruoyi-admin/src/main/resources/application-dev.yml` | 数据库、Redis、第三方服务配置 |
|
|
||||||
| 前端配置 | `ruoyi-ui/vue.config.js` | 开发服务器、代理配置 |
|
|
||||||
| 菜单数据 | 数据库 `sys_menu` 表 | 菜单权限配置 |
|
|
||||||
|
|
||||||
## 测试账号
|
|
||||||
|
|
||||||
- 用户名: `admin`
|
|
||||||
- 密码: `admin123`
|
|
||||||
|
|
||||||
## Swagger 接口文档
|
|
||||||
|
|
||||||
启动后端后访问: `http://localhost:8080/swagger-ui/index.html`
|
|
||||||
|
|
||||||
## 开发流程建议
|
|
||||||
|
|
||||||
1. **数据库设计**: 新建表需加 `ibs_` 前缀
|
|
||||||
2. **后端实体类**: 使用 `@Data` 注解,添加审计字段注解
|
|
||||||
3. **后端业务层**: 在 `ibs` 模块下开发,简单 CRUD 用 MyBatis Plus,复杂操作用 XML
|
|
||||||
4. **后端测试**: 使用 `/login/test` 获取 token 后测试接口
|
|
||||||
5. **前端开发**: 在 `ruoyi-ui/views/` 下对应业务模块开发
|
|
||||||
6. **菜单配置**: 在 `sys_menu` 表添加菜单项
|
|
||||||
7. **API 文档**: 生成后保存在 `doc/api/` 目录
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ public class CustGroupController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取客群详情
|
* 根据ID查询客群详情
|
||||||
*/
|
*/
|
||||||
@ApiOperation("获取客群详情")
|
@ApiOperation("根据ID查询客群详情")
|
||||||
@Log(title = "客群管理-获取客群详情")
|
@Log(title = "客群管理-查询客群详情")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public AjaxResult getCustGroup(@PathVariable Long id) {
|
public AjaxResult getCustGroup(@PathVariable Long id) {
|
||||||
CustGroupVO vo = custGroupService.getCustGroup(id);
|
CustGroupVO custGroup = custGroupService.getCustGroup(id);
|
||||||
return AjaxResult.success(vo);
|
return AjaxResult.success(custGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,17 +83,6 @@ public class CustGroupController extends BaseController {
|
|||||||
return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file));
|
return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新客群
|
|
||||||
*/
|
|
||||||
@ApiOperation("更新客群")
|
|
||||||
@Log(title = "客群管理-更新客群", businessType = BusinessType.UPDATE)
|
|
||||||
@PostMapping("/update")
|
|
||||||
public AjaxResult updateCustGroup(@RequestBody @Valid CustGroup custGroup) {
|
|
||||||
String result = custGroupService.updateCustGroup(custGroup);
|
|
||||||
return AjaxResult.success(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新客群(网格导入)
|
* 更新客群(网格导入)
|
||||||
*/
|
*/
|
||||||
@@ -150,15 +139,4 @@ public class CustGroupController extends BaseController {
|
|||||||
return AjaxResult.success(result);
|
return AjaxResult.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动移除客群客户
|
|
||||||
*/
|
|
||||||
@ApiOperation("手动移除客群客户")
|
|
||||||
@Log(title = "客群管理-手动移除客户", businessType = BusinessType.DELETE)
|
|
||||||
@PostMapping("/removeMembers")
|
|
||||||
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) {
|
|
||||||
String result = custGroupService.removeMembers(groupId, memberIds);
|
|
||||||
return AjaxResult.success(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.ruoyi.group.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||||
|
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||||
|
import com.ruoyi.group.service.ICustGroupMemberService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客群客户Controller
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Api(tags = "客群客户接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/group/member")
|
||||||
|
public class CustGroupMemberController extends BaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICustGroupMemberService custGroupMemberService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询客群客户列表
|
||||||
|
*/
|
||||||
|
@ApiOperation("分页查询客群客户列表")
|
||||||
|
@Log(title = "客群客户-查询客户列表")
|
||||||
|
@GetMapping("/list/{groupId}")
|
||||||
|
public TableDataInfo listCustGroupMembers(@PathVariable Long groupId,
|
||||||
|
CustGroupMemberQueryDTO dto) {
|
||||||
|
startPage();
|
||||||
|
List<CustGroupMemberVO> list = custGroupMemberService.listCustGroupMembers(groupId, dto);
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动移除客群客户
|
||||||
|
*/
|
||||||
|
@ApiOperation("手动移除客群客户")
|
||||||
|
@Log(title = "客群客户-手动移除客户", businessType = BusinessType.DELETE)
|
||||||
|
@PostMapping("/remove")
|
||||||
|
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) {
|
||||||
|
String result = custGroupMemberService.removeMembers(groupId, memberIds);
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.ruoyi.group.domain.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客群客户查询DTO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(description = "客群客户查询条件")
|
||||||
|
public class CustGroupMemberQueryDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户类型:0=个人, 1=商户, 2=企业
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "客户类型")
|
||||||
|
private String custType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户姓名
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "客户姓名")
|
||||||
|
private String custName;
|
||||||
|
}
|
||||||
@@ -39,6 +39,12 @@ public class CustGroupQueryDTO implements Serializable {
|
|||||||
@ApiModelProperty(value = "客群状态", name = "groupStatus")
|
@ApiModelProperty(value = "客群状态", name = "groupStatus")
|
||||||
private String groupStatus;
|
private String groupStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视图类型:mine=我创建的,sharedToMe=下发给我的
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "视图类型", name = "viewType")
|
||||||
|
private String viewType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页码
|
* 页码
|
||||||
*/
|
*/
|
||||||
@@ -50,4 +56,4 @@ public class CustGroupQueryDTO implements Serializable {
|
|||||||
*/
|
*/
|
||||||
@ApiModelProperty(value = "每页大小", name = "pageSize")
|
@ApiModelProperty(value = "每页大小", name = "pageSize")
|
||||||
private Integer pageSize = 10;
|
private Integer pageSize = 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ public class CustGroup {
|
|||||||
* 所属机构ID
|
* 所属机构ID
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty(value = "所属机构ID", name = "deptId")
|
@ApiModelProperty(value = "所属机构ID", name = "deptId")
|
||||||
@TableField(fill = FieldFill.INSERT)
|
|
||||||
private Long deptId;
|
private Long deptId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,13 +73,6 @@ public class CustGroup {
|
|||||||
@ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
|
@ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
|
||||||
private String shareDeptIds;
|
private String shareDeptIds;
|
||||||
|
|
||||||
/**
|
|
||||||
* 共享部门ID列表(非表字段,用于接收前端传参)
|
|
||||||
*/
|
|
||||||
@ApiModelProperty(value = "共享部门ID列表", name = "shareDeptIdList")
|
|
||||||
@TableField(exist = false)
|
|
||||||
private List<Long> shareDeptIdList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客群状态:0=正常, 1=已禁用
|
* 客群状态:0=正常, 1=已禁用
|
||||||
*/
|
*/
|
||||||
@@ -109,14 +101,12 @@ public class CustGroup {
|
|||||||
/**
|
/**
|
||||||
* 更新者
|
* 更新者
|
||||||
*/
|
*/
|
||||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
|
||||||
private String updateBy;
|
private String updateBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新时间
|
* 更新时间
|
||||||
*/
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
|
||||||
private Date updateTime;
|
private Date updateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,14 +63,12 @@ public class CustGroupMember {
|
|||||||
/**
|
/**
|
||||||
* 创建者
|
* 创建者
|
||||||
*/
|
*/
|
||||||
@TableField(fill = FieldFill.INSERT)
|
|
||||||
private String createBy;
|
private String createBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*/
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@TableField(fill = FieldFill.INSERT)
|
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ public class CustGroupVO {
|
|||||||
private Integer shareEnabled;
|
private Integer shareEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可见部门ID列表
|
* 可见部门ID列表(逗号分隔)
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty(value = "可见部门ID列表", name = "shareDeptIds")
|
@ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
|
||||||
private List<Long> shareDeptIds;
|
private String shareDeptIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客群状态:0=正常, 1=已禁用
|
* 客群状态:0=正常, 1=已禁用
|
||||||
@@ -114,6 +114,49 @@ public class CustGroupVO {
|
|||||||
@ApiModelProperty(value = "备注", name = "remark")
|
@ApiModelProperty(value = "备注", name = "remark")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效期截止时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@ApiModelProperty(value = "有效期截止时间", name = "validTime")
|
||||||
|
private Date validTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建状态:0=创建中, 1=创建成功, 2=创建失败
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "创建状态:0=创建中, 1=创建成功, 2=创建失败", name = "createStatus")
|
||||||
|
private String createStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网格类型:0=绩效网格, 1=地理网格, 2=绘制网格
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "网格类型", name = "gridType")
|
||||||
|
private String gridType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绩效业务类型:retail=零售, corporate=公司
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType")
|
||||||
|
private String cmpmBizType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户经理列表(逗号分隔)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "客户经理列表", name = "gridUserNames")
|
||||||
|
private String gridUserNames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地理网格ID列表(逗号分隔)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "地理网格ID列表", name = "regionGridIds")
|
||||||
|
private String regionGridIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制网格ID列表(逗号分隔)
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "绘制网格ID列表", name = "drawGridIds")
|
||||||
|
private String drawGridIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户列表
|
* 客户列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
||||||
import com.ruoyi.group.domain.entity.CustGroup;
|
import com.ruoyi.group.domain.entity.CustGroup;
|
||||||
import com.ruoyi.group.domain.vo.CustGroupVO;
|
import com.ruoyi.group.domain.vo.CustGroupVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,6 +14,7 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
|
@Mapper
|
||||||
public interface CustGroupMapper extends BaseMapper<CustGroup> {
|
public interface CustGroupMapper extends BaseMapper<CustGroup> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,5 +32,29 @@ public interface CustGroupMapper extends BaseMapper<CustGroup> {
|
|||||||
* @param dto 查询条件
|
* @param dto 查询条件
|
||||||
* @return 客群VO列表
|
* @return 客群VO列表
|
||||||
*/
|
*/
|
||||||
List<CustGroupVO> selectCustGroupList(@Param("dto") CustGroupQueryDTO dto);
|
List<CustGroupVO> selectCustGroupList(@Param("dto") CustGroupQueryDTO dto,
|
||||||
}
|
@Param("userName") String userName,
|
||||||
|
@Param("deptId") String deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询客群详情
|
||||||
|
*
|
||||||
|
* @param id 客群ID
|
||||||
|
* @return 客群VO
|
||||||
|
*/
|
||||||
|
CustGroupVO selectCustGroupById(@Param("id") Long id,
|
||||||
|
@Param("userName") String userName,
|
||||||
|
@Param("deptId") String deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前用户是否有客群查看权限
|
||||||
|
*
|
||||||
|
* @param id 客群ID
|
||||||
|
* @param userName 当前用户名
|
||||||
|
* @param deptId 当前部门ID
|
||||||
|
* @return 可查看数量
|
||||||
|
*/
|
||||||
|
Long countVisibleCustGroup(@Param("id") Long id,
|
||||||
|
@Param("userName") String userName,
|
||||||
|
@Param("deptId") String deptId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,36 @@
|
|||||||
package com.ruoyi.group.mapper;
|
package com.ruoyi.group.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||||
import com.ruoyi.group.domain.entity.CustGroupMember;
|
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||||
|
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客群客户关联Mapper接口
|
* 客群客户关联Mapper接口
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
|
@Mapper
|
||||||
public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> {
|
public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询客群客户数量
|
* 分页查询客群客户列表
|
||||||
*
|
*
|
||||||
* @param groupId 客群ID
|
* @param groupId 客群ID
|
||||||
* @return 数量
|
* @param dto 查询条件
|
||||||
|
* @return 客户列表
|
||||||
*/
|
*/
|
||||||
Long countByGroupId(@Param("groupId") Long groupId);
|
List<CustGroupMemberVO> selectCustGroupMemberList(@Param("groupId") Long groupId,
|
||||||
|
@Param("dto") CustGroupMemberQueryDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量插入客群客户(INSERT IGNORE,遇到重复键自动跳过)
|
||||||
|
*
|
||||||
|
* @param memberList 客户列表
|
||||||
|
*/
|
||||||
|
void batchInsertMembers(@Param("list") List<CustGroupMember> memberList);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.ruoyi.group.service;
|
||||||
|
|
||||||
|
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||||
|
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客群客户服务接口
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public interface ICustGroupMemberService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询客群客户列表
|
||||||
|
*
|
||||||
|
* @param groupId 客群ID
|
||||||
|
* @param dto 查询条件
|
||||||
|
* @return 客户列表
|
||||||
|
*/
|
||||||
|
List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动移除客群客户
|
||||||
|
*
|
||||||
|
* @param groupId 客群ID
|
||||||
|
* @param memberIds 客户ID列表
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
String removeMembers(Long groupId, List<Long> memberIds);
|
||||||
|
}
|
||||||
@@ -23,6 +23,21 @@ public interface ICustGroupService {
|
|||||||
*/
|
*/
|
||||||
List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto);
|
List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询客群详情
|
||||||
|
*
|
||||||
|
* @param id 客群ID
|
||||||
|
* @return 客群VO
|
||||||
|
*/
|
||||||
|
CustGroupVO getCustGroup(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前用户是否有客群查看权限
|
||||||
|
*
|
||||||
|
* @param id 客群ID
|
||||||
|
*/
|
||||||
|
void checkCustGroupViewPermission(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步创建客群(模板导入)
|
* 异步创建客群(模板导入)
|
||||||
*
|
*
|
||||||
@@ -57,14 +72,6 @@ public interface ICustGroupService {
|
|||||||
*/
|
*/
|
||||||
String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
|
String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新客群
|
|
||||||
*
|
|
||||||
* @param custGroup 客群实体
|
|
||||||
* @return 结果消息
|
|
||||||
*/
|
|
||||||
String updateCustGroup(CustGroup custGroup);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除客群
|
* 删除客群
|
||||||
*
|
*
|
||||||
@@ -73,14 +80,6 @@ public interface ICustGroupService {
|
|||||||
*/
|
*/
|
||||||
String deleteCustGroup(List<Long> idList);
|
String deleteCustGroup(List<Long> idList);
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取客群详情
|
|
||||||
*
|
|
||||||
* @param id 客群ID
|
|
||||||
* @return 客群VO
|
|
||||||
*/
|
|
||||||
CustGroupVO getCustGroup(Long id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查客群名称是否存在
|
* 检查客群名称是否存在
|
||||||
*
|
*
|
||||||
@@ -97,15 +96,6 @@ public interface ICustGroupService {
|
|||||||
*/
|
*/
|
||||||
String getCreateStatus(Long id);
|
String getCreateStatus(Long id);
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动移除客群客户
|
|
||||||
*
|
|
||||||
* @param groupId 客群ID
|
|
||||||
* @param memberIds 客群成员ID列表
|
|
||||||
* @return 结果消息
|
|
||||||
*/
|
|
||||||
String removeMembers(Long groupId, List<Long> memberIds);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新动态客群(定时任务调用)
|
* 更新动态客群(定时任务调用)
|
||||||
* 根据原始导入条件重新查询并更新客户列表
|
* 根据原始导入条件重新查询并更新客户列表
|
||||||
@@ -117,4 +107,4 @@ public interface ICustGroupService {
|
|||||||
*/
|
*/
|
||||||
void checkAndDisableExpiredGroups();
|
void checkAndDisableExpiredGroups();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.ruoyi.group.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||||
|
import com.ruoyi.group.domain.entity.CustGroup;
|
||||||
|
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||||
|
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||||
|
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 org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客群客户服务实现类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class CustGroupMemberServiceImpl implements ICustGroupMemberService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CustGroupMemberMapper custGroupMemberMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CustGroupMapper custGroupMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICustGroupService custGroupService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto) {
|
||||||
|
custGroupService.checkCustGroupViewPermission(groupId);
|
||||||
|
return custGroupMemberMapper.selectCustGroupMemberList(groupId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public String removeMembers(Long groupId, List<Long> memberIds) {
|
||||||
|
// 检查客群是否存在
|
||||||
|
CustGroup custGroup = custGroupMapper.selectById(groupId);
|
||||||
|
if (custGroup == null) {
|
||||||
|
return "客群不存在";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除客户关联
|
||||||
|
memberIds.forEach(memberId -> {
|
||||||
|
CustGroupMember member = custGroupMemberMapper.selectById(memberId);
|
||||||
|
if (member != null && member.getGroupId().equals(groupId)) {
|
||||||
|
// 设置手动移除标识
|
||||||
|
member.setManualRemove(1);
|
||||||
|
// 逻辑删除
|
||||||
|
custGroupMemberMapper.deleteById(memberId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return "移除成功";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,36 +2,28 @@ package com.ruoyi.group.service.impl;
|
|||||||
|
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
|
||||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.system.mapper.SysDeptMapper;
|
import com.ruoyi.system.mapper.SysDeptMapper;
|
||||||
import com.ruoyi.ibs.cmpm.domain.entity.GridCmpm;
|
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
|
||||||
import com.ruoyi.ibs.cmpm.mapper.GridCmpmMapper;
|
import com.ruoyi.ibs.cmpm.service.GridCmpmService;
|
||||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
|
import com.ruoyi.ibs.draw.mapper.DrawGridCustUserUnbindMapper;
|
||||||
import com.ruoyi.ibs.draw.domain.entity.DrawShapeCust;
|
|
||||||
import com.ruoyi.ibs.draw.mapper.DrawGridShapeRelateMapper;
|
|
||||||
import com.ruoyi.ibs.draw.mapper.DrawShapeCustMapper;
|
|
||||||
import com.ruoyi.ibs.grid.domain.vo.CustVO;
|
|
||||||
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
||||||
import com.ruoyi.group.domain.dto.CustGroupMemberTemplate;
|
import com.ruoyi.group.domain.dto.CustGroupMemberTemplate;
|
||||||
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
||||||
import com.ruoyi.group.domain.dto.GridImportDTO;
|
import com.ruoyi.group.domain.dto.GridImportDTO;
|
||||||
import com.ruoyi.group.domain.entity.CustGroup;
|
import com.ruoyi.group.domain.entity.CustGroup;
|
||||||
import com.ruoyi.group.domain.entity.CustGroupMember;
|
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
|
||||||
import com.ruoyi.group.domain.vo.CustGroupVO;
|
import com.ruoyi.group.domain.vo.CustGroupVO;
|
||||||
import com.ruoyi.group.mapper.CustGroupMapper;
|
import com.ruoyi.group.mapper.CustGroupMapper;
|
||||||
import com.ruoyi.group.mapper.CustGroupMemberMapper;
|
import com.ruoyi.group.mapper.CustGroupMemberMapper;
|
||||||
import com.ruoyi.group.service.ICustGroupService;
|
import com.ruoyi.group.service.ICustGroupService;
|
||||||
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
||||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
import com.ruoyi.ibs.handler.DynamicTableNameHelper;
|
||||||
import com.ruoyi.ibs.grid.mapper.RegionCustUserMapper;
|
|
||||||
import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import lombok.val;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -61,29 +53,37 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
private ExecutorService executorService;
|
private ExecutorService executorService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private GridCmpmMapper gridCmpmMapper;
|
private GridCmpmService gridCmpmService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RegionCustUserMapper regionCustUserMapper;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RegionGridMapper regionGridMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RegionGridListService regionGridListService;
|
private RegionGridListService regionGridListService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DrawShapeCustMapper drawShapeCustMapper;
|
private DrawGridCustUserUnbindMapper drawGridCustUserUnbindMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DrawGridShapeRelateMapper drawGridShapeRelateMapper;
|
private TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private SysDeptMapper sysDeptMapper;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto) {
|
public List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto) {
|
||||||
return custGroupMapper.selectCustGroupList(dto);
|
return custGroupMapper.selectCustGroupList(dto, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustGroupVO getCustGroup(Long id) {
|
||||||
|
CustGroupVO custGroup = custGroupMapper.selectCustGroupById(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId()));
|
||||||
|
if (custGroup == null) {
|
||||||
|
throw new ServiceException("客群不存在");
|
||||||
|
}
|
||||||
|
return custGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCustGroupViewPermission(Long id) {
|
||||||
|
Long count = custGroupMapper.countVisibleCustGroup(id, SecurityUtils.getUsername(), String.valueOf(SecurityUtils.getDeptId()));
|
||||||
|
if (count == null || count <= 0) {
|
||||||
|
throw new ServiceException("客群不存在或无查看权限");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -176,21 +176,6 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
return String.valueOf(custGroup.getId());
|
return String.valueOf(custGroup.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public String updateCustGroup(CustGroup custGroup) {
|
|
||||||
CustGroup existGroup = custGroupMapper.selectById(custGroup.getId());
|
|
||||||
if (existGroup == null) {
|
|
||||||
throw new ServiceException("客群不存在");
|
|
||||||
}
|
|
||||||
// 检查客群名称是否存在(排除自己)
|
|
||||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
|
||||||
throw new ServiceException("客群名称已存在");
|
|
||||||
}
|
|
||||||
custGroupMapper.updateById(custGroup);
|
|
||||||
return "客群更新成功";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public String updateCustGroupByGrid(GridImportDTO gridImportDTO) {
|
public String updateCustGroupByGrid(GridImportDTO gridImportDTO) {
|
||||||
@@ -200,10 +185,17 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
if (existGroup == null) {
|
if (existGroup == null) {
|
||||||
throw new ServiceException("客群不存在");
|
throw new ServiceException("客群不存在");
|
||||||
}
|
}
|
||||||
|
// 检查客群是否正在创建或更新
|
||||||
|
if ("0".equals(existGroup.getCreateStatus())) {
|
||||||
|
throw new ServiceException("客群正在处理中,请稍后再试");
|
||||||
|
}
|
||||||
// 更新客群基本信息
|
// 更新客群基本信息
|
||||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
||||||
throw new ServiceException("客群名称已存在");
|
throw new ServiceException("客群名称已存在");
|
||||||
}
|
}
|
||||||
|
// 检查网格条件是否发生变化
|
||||||
|
boolean gridConditionChanged = isGridConditionChanged(existGroup, gridImportDTO);
|
||||||
|
|
||||||
// 重新查询数据库,获取最新状态
|
// 重新查询数据库,获取最新状态
|
||||||
CustGroup latestGroup = custGroupMapper.selectById(custGroup.getId());
|
CustGroup latestGroup = custGroupMapper.selectById(custGroup.getId());
|
||||||
latestGroup.setGroupName(custGroup.getGroupName());
|
latestGroup.setGroupName(custGroup.getGroupName());
|
||||||
@@ -212,7 +204,48 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
latestGroup.setValidTime(custGroup.getValidTime());
|
latestGroup.setValidTime(custGroup.getValidTime());
|
||||||
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
||||||
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
||||||
|
|
||||||
|
// 保存新的网格导入条件
|
||||||
|
String gridType = gridImportDTO.getGridType();
|
||||||
|
latestGroup.setGridType(gridType);
|
||||||
|
if ("0".equals(gridType)) {
|
||||||
|
latestGroup.setCmpmBizType(gridImportDTO.getCmpmBizType());
|
||||||
|
if (gridImportDTO.getUserNames() != null && !gridImportDTO.getUserNames().isEmpty()) {
|
||||||
|
latestGroup.setGridUserNames(String.join(",", gridImportDTO.getUserNames()));
|
||||||
|
} else {
|
||||||
|
latestGroup.setGridUserNames(null);
|
||||||
|
}
|
||||||
|
} else if ("1".equals(gridType)) {
|
||||||
|
if (gridImportDTO.getRegionGridIds() != null && !gridImportDTO.getRegionGridIds().isEmpty()) {
|
||||||
|
latestGroup.setRegionGridIds(gridImportDTO.getRegionGridIds().stream()
|
||||||
|
.map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
|
} else {
|
||||||
|
latestGroup.setRegionGridIds(null);
|
||||||
|
}
|
||||||
|
} else if ("2".equals(gridType)) {
|
||||||
|
if (gridImportDTO.getDrawGridIds() != null && !gridImportDTO.getDrawGridIds().isEmpty()) {
|
||||||
|
latestGroup.setDrawGridIds(gridImportDTO.getDrawGridIds().stream()
|
||||||
|
.map(String::valueOf).collect(Collectors.joining(",")));
|
||||||
|
} else {
|
||||||
|
latestGroup.setDrawGridIds(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新数据库
|
// 更新数据库
|
||||||
|
latestGroup.setUpdateBy(SecurityUtils.getUsername());
|
||||||
|
latestGroup.setUpdateTime(new Date());
|
||||||
|
custGroupMapper.updateById(latestGroup);
|
||||||
|
|
||||||
|
// 如果网格条件没有变化,直接返回成功
|
||||||
|
if (!gridConditionChanged) {
|
||||||
|
log.info("客群网格条件未变化,跳过客户重新导入,客群ID:{}", custGroup.getId());
|
||||||
|
return "客群更新成功(客户列表无需变更)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网格条件发生变化,需要重新导入客户
|
||||||
|
log.info("客群网格条件已变化,开始重新导入客户,客群ID:{}", custGroup.getId());
|
||||||
|
// 设置更新状态为"更新中"
|
||||||
|
latestGroup.setCreateStatus("0");
|
||||||
custGroupMapper.updateById(latestGroup);
|
custGroupMapper.updateById(latestGroup);
|
||||||
// 重新设置回DTO(确保异步线程能获取到正确的ID和状态)
|
// 重新设置回DTO(确保异步线程能获取到正确的ID和状态)
|
||||||
gridImportDTO.setCustGroup(latestGroup);
|
gridImportDTO.setCustGroup(latestGroup);
|
||||||
@@ -223,6 +256,80 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
return "客群更新中";
|
return "客群更新中";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查网格条件是否发生变化
|
||||||
|
*/
|
||||||
|
private boolean isGridConditionChanged(CustGroup existGroup, GridImportDTO gridImportDTO) {
|
||||||
|
// 首先检查网格类型是否变化
|
||||||
|
String newGridType = gridImportDTO.getGridType();
|
||||||
|
String oldGridType = existGroup.getGridType();
|
||||||
|
|
||||||
|
// 如果旧记录没有网格类型信息,说明需要导入
|
||||||
|
if (oldGridType == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网格类型变化
|
||||||
|
if (!newGridType.equals(oldGridType)) {
|
||||||
|
log.info("网格类型变化:旧={}, 新={}", oldGridType, newGridType);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据网格类型检查具体条件
|
||||||
|
if ("0".equals(newGridType)) {
|
||||||
|
// 绩效网格:比较业务类型和客户经理列表
|
||||||
|
String oldBizType = existGroup.getCmpmBizType();
|
||||||
|
String newBizType = gridImportDTO.getCmpmBizType();
|
||||||
|
List<String> newUserNames = gridImportDTO.getUserNames();
|
||||||
|
|
||||||
|
if (!Objects.equals(oldBizType, newBizType)) {
|
||||||
|
log.info("绩效业务类型变化:旧={}, 新={}", oldBizType, newBizType);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较客户经理列表
|
||||||
|
String oldUserNames = existGroup.getGridUserNames();
|
||||||
|
String newUserNamesStr = newUserNames == null || newUserNames.isEmpty()
|
||||||
|
? null : String.join(",", newUserNames);
|
||||||
|
if (!Objects.equals(oldUserNames, newUserNamesStr)) {
|
||||||
|
log.info("客户经理列表变化:旧={}, 新={}", oldUserNames, newUserNamesStr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if ("1".equals(newGridType)) {
|
||||||
|
// 地理网格:比较网格ID列表
|
||||||
|
String oldRegionIds = existGroup.getRegionGridIds();
|
||||||
|
List<Long> newRegionIds = gridImportDTO.getRegionGridIds();
|
||||||
|
String newRegionIdsStr = newRegionIds == null || newRegionIds.isEmpty()
|
||||||
|
? null : newRegionIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
// 归一化比较(排序后比较)
|
||||||
|
String oldRegionIdsSorted = oldRegionIds == null ? null :
|
||||||
|
Arrays.stream(oldRegionIds.split(",")).sorted().collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
if (!Objects.equals(oldRegionIdsSorted, newRegionIdsStr)) {
|
||||||
|
log.info("地理网格ID列表变化:旧={}, 新={}", oldRegionIdsSorted, newRegionIdsStr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if ("2".equals(newGridType)) {
|
||||||
|
// 绘制网格:比较网格ID列表
|
||||||
|
String oldDrawIds = existGroup.getDrawGridIds();
|
||||||
|
List<Long> newDrawIds = gridImportDTO.getDrawGridIds();
|
||||||
|
String newDrawIdsStr = newDrawIds == null || newDrawIds.isEmpty()
|
||||||
|
? null : newDrawIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
// 归一化比较(排序后比较)
|
||||||
|
String oldDrawIdsSorted = oldDrawIds == null ? null :
|
||||||
|
Arrays.stream(oldDrawIds.split(",")).sorted().collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
if (!Objects.equals(oldDrawIdsSorted, newDrawIdsStr)) {
|
||||||
|
log.info("绘制网格ID列表变化:旧={}, 新={}", oldDrawIdsSorted, newDrawIdsStr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file) {
|
public String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file) {
|
||||||
@@ -231,6 +338,10 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
if (existGroup == null) {
|
if (existGroup == null) {
|
||||||
throw new ServiceException("客群不存在");
|
throw new ServiceException("客群不存在");
|
||||||
}
|
}
|
||||||
|
// 检查客群是否正在创建或更新
|
||||||
|
if ("0".equals(existGroup.getCreateStatus())) {
|
||||||
|
throw new ServiceException("客群正在处理中,请稍后再试");
|
||||||
|
}
|
||||||
// 更新客群基本信息
|
// 更新客群基本信息
|
||||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
||||||
throw new ServiceException("客群名称已存在");
|
throw new ServiceException("客群名称已存在");
|
||||||
@@ -243,7 +354,11 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
latestGroup.setValidTime(custGroup.getValidTime());
|
latestGroup.setValidTime(custGroup.getValidTime());
|
||||||
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
||||||
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
||||||
|
// 设置更新状态为"更新中"
|
||||||
|
latestGroup.setCreateStatus("0");
|
||||||
// 更新数据库
|
// 更新数据库
|
||||||
|
latestGroup.setUpdateBy(SecurityUtils.getUsername());
|
||||||
|
latestGroup.setUpdateTime(new Date());
|
||||||
custGroupMapper.updateById(latestGroup);
|
custGroupMapper.updateById(latestGroup);
|
||||||
// 获取当前用户部门编码(异步线程中无法获取)
|
// 获取当前用户部门编码(异步线程中无法获取)
|
||||||
String headId = SecurityUtils.getHeadId();
|
String headId = SecurityUtils.getHeadId();
|
||||||
@@ -269,82 +384,6 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
return "客群删除成功";
|
return "客群删除成功";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public String removeMembers(Long groupId, List<Long> memberIds) {
|
|
||||||
if (memberIds == null || memberIds.isEmpty()) {
|
|
||||||
throw new ServiceException("请选择要移除的客户");
|
|
||||||
}
|
|
||||||
// 检查客群是否存在
|
|
||||||
CustGroup custGroup = custGroupMapper.selectById(groupId);
|
|
||||||
if (custGroup == null) {
|
|
||||||
throw new ServiceException("客群不存在");
|
|
||||||
}
|
|
||||||
// 标记为手动移除(软删除)
|
|
||||||
for (Long memberId : memberIds) {
|
|
||||||
CustGroupMember member = custGroupMemberMapper.selectById(memberId);
|
|
||||||
if (member != null && member.getGroupId().equals(groupId)) {
|
|
||||||
member.setManualRemove(1);
|
|
||||||
member.setDelFlag(1);
|
|
||||||
custGroupMemberMapper.updateById(member);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "成功移除 " + memberIds.size() + " 个客户";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CustGroupVO getCustGroup(Long id) {
|
|
||||||
CustGroup custGroup = custGroupMapper.selectById(id);
|
|
||||||
if (custGroup == null) {
|
|
||||||
throw new ServiceException("客群不存在");
|
|
||||||
}
|
|
||||||
CustGroupVO vo = new CustGroupVO();
|
|
||||||
vo.setId(custGroup.getId());
|
|
||||||
vo.setGroupName(custGroup.getGroupName());
|
|
||||||
vo.setGroupMode(custGroup.getGroupMode());
|
|
||||||
vo.setCreateMode(custGroup.getCreateMode());
|
|
||||||
vo.setUserName(custGroup.getUserName());
|
|
||||||
vo.setNickName(custGroup.getNickName());
|
|
||||||
vo.setDeptId(custGroup.getDeptId());
|
|
||||||
vo.setShareEnabled(custGroup.getShareEnabled());
|
|
||||||
vo.setGroupStatus(custGroup.getGroupStatus());
|
|
||||||
vo.setRemark(custGroup.getRemark());
|
|
||||||
vo.setCreateBy(custGroup.getCreateBy());
|
|
||||||
vo.setCreateTime(custGroup.getCreateTime());
|
|
||||||
vo.setUpdateBy(custGroup.getUpdateBy());
|
|
||||||
vo.setUpdateTime(custGroup.getUpdateTime());
|
|
||||||
// 处理共享部门ID列表
|
|
||||||
if (StringUtils.isNotEmpty(custGroup.getShareDeptIds())) {
|
|
||||||
List<Long> deptIds = new ArrayList<>();
|
|
||||||
for (String idStr : custGroup.getShareDeptIds().split(",")) {
|
|
||||||
if (StringUtils.isNotEmpty(idStr)) {
|
|
||||||
deptIds.add(Long.valueOf(idStr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vo.setShareDeptIds(deptIds);
|
|
||||||
}
|
|
||||||
// 查询客户列表
|
|
||||||
LambdaQueryWrapper<CustGroupMember> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.eq(CustGroupMember::getGroupId, id);
|
|
||||||
List<CustGroupMember> memberList = custGroupMemberMapper.selectList(wrapper);
|
|
||||||
List<CustGroupMemberVO> memberVOList = new ArrayList<>();
|
|
||||||
for (CustGroupMember member : memberList) {
|
|
||||||
CustGroupMemberVO memberVO = new CustGroupMemberVO();
|
|
||||||
memberVO.setId(member.getId());
|
|
||||||
memberVO.setGroupId(member.getGroupId());
|
|
||||||
memberVO.setCustType(member.getCustType());
|
|
||||||
memberVO.setCustId(member.getCustId());
|
|
||||||
memberVO.setCustName(member.getCustName());
|
|
||||||
memberVO.setCustIdc(member.getCustIdc());
|
|
||||||
memberVO.setSocialCreditCode(member.getSocialCreditCode());
|
|
||||||
memberVO.setCreateTime(member.getCreateTime());
|
|
||||||
memberVOList.add(memberVO);
|
|
||||||
}
|
|
||||||
vo.setCustList(memberVOList);
|
|
||||||
vo.setCustCount(memberVOList.size());
|
|
||||||
return vo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkGroupNameExist(String groupName) {
|
public boolean checkGroupNameExist(String groupName) {
|
||||||
LambdaQueryWrapper<CustGroup> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<CustGroup> wrapper = new LambdaQueryWrapper<>();
|
||||||
@@ -486,14 +525,14 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
gridImportDTO.setRegionGridIds(Arrays.stream(custGroup.getRegionGridIds().split(","))
|
gridImportDTO.setRegionGridIds(Arrays.stream(custGroup.getRegionGridIds().split(","))
|
||||||
.map(Long::valueOf).collect(Collectors.toList()));
|
.map(Long::valueOf).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
newMemberList.addAll(importFromRegionGrid(custGroup, gridImportDTO));
|
newMemberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId));
|
||||||
} else if ("2".equals(gridType)) {
|
} else if ("2".equals(gridType)) {
|
||||||
// 绘制网格
|
// 绘制网格
|
||||||
if (StringUtils.isNotEmpty(custGroup.getDrawGridIds())) {
|
if (StringUtils.isNotEmpty(custGroup.getDrawGridIds())) {
|
||||||
gridImportDTO.setDrawGridIds(Arrays.stream(custGroup.getDrawGridIds().split(","))
|
gridImportDTO.setDrawGridIds(Arrays.stream(custGroup.getDrawGridIds().split(","))
|
||||||
.map(Long::valueOf).collect(Collectors.toList()));
|
.map(Long::valueOf).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
newMemberList.addAll(importFromDrawGrid(custGroup, gridImportDTO));
|
newMemberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算差异
|
// 计算差异
|
||||||
@@ -601,51 +640,71 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
}
|
}
|
||||||
memberList.add(member);
|
memberList.add(member);
|
||||||
}
|
}
|
||||||
// 批量插入
|
|
||||||
int batchSize = 1000;
|
// 使用编程式事务:先删除旧客户,再插入新客户
|
||||||
int successCount = 0;
|
transactionTemplate.executeWithoutResult(status -> {
|
||||||
int skippedCount = 0;
|
// 删除该客群的所有旧客户
|
||||||
int restoredCount = 0;
|
log.info("开始删除客群旧客户(模板导入),客群ID:{}", custGroup.getId());
|
||||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
LambdaQueryWrapper<CustGroupMember> memberWrapper = new LambdaQueryWrapper<>();
|
||||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId());
|
||||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
custGroupMemberMapper.delete(memberWrapper);
|
||||||
for (CustGroupMember member : batchList) {
|
log.info("客群旧客户删除完成(模板导入),客群ID:{}", custGroup.getId());
|
||||||
try {
|
|
||||||
custGroupMemberMapper.insert(member);
|
// 批量插入新客户
|
||||||
successCount++;
|
log.info("开始批量插入客户(模板导入),客群ID:{},客户总数:{}", custGroup.getId(), memberList.size());
|
||||||
} catch (DuplicateKeyException e) {
|
int batchSize = 1000;
|
||||||
// 客户已存在,检查是否是被手动移除的
|
int successCount = 0;
|
||||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
int skippedCount = 0;
|
||||||
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
int restoredCount = 0;
|
||||||
.eq(CustGroupMember::getCustId, member.getCustId())
|
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||||
.eq(CustGroupMember::getCustType, member.getCustType());
|
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||||
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||||
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1, (memberList.size() + batchSize - 1) / batchSize, batchList.size());
|
||||||
// 是被手动移除的客户,清除标记并恢复
|
for (CustGroupMember member : batchList) {
|
||||||
existMember.setManualRemove(0);
|
try {
|
||||||
existMember.setDelFlag(0);
|
custGroupMemberMapper.insert(member);
|
||||||
existMember.setCustName(member.getCustName());
|
successCount++;
|
||||||
custGroupMemberMapper.updateById(existMember);
|
} catch (DuplicateKeyException e) {
|
||||||
restoredCount++;
|
// 客户已存在,检查是否是被手动移除的
|
||||||
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
} else {
|
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
||||||
// 正常存在的客户,跳过
|
.eq(CustGroupMember::getCustId, member.getCustId())
|
||||||
skippedCount++;
|
.eq(CustGroupMember::getCustType, member.getCustType());
|
||||||
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
||||||
|
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
||||||
|
// 是被手动移除的客户,清除标记并恢复
|
||||||
|
existMember.setManualRemove(0);
|
||||||
|
existMember.setDelFlag(0);
|
||||||
|
existMember.setCustName(member.getCustName());
|
||||||
|
custGroupMemberMapper.updateById(existMember);
|
||||||
|
restoredCount++;
|
||||||
|
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||||
|
} else {
|
||||||
|
// 正常存在的客户,跳过
|
||||||
|
skippedCount++;
|
||||||
|
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
||||||
log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
custGroup.getId(), successCount, skippedCount, restoredCount);
|
||||||
custGroup.getId(), successCount, skippedCount, restoredCount);
|
// 更新创建状态为成功
|
||||||
// 更新创建状态为成功
|
custGroup.setCreateStatus("1");
|
||||||
custGroup.setCreateStatus("1");
|
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||||
custGroupMapper.updateById(custGroup);
|
custGroup.setUpdateTime(new Date());
|
||||||
|
custGroupMapper.updateById(custGroup);
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.error("客群客户导入失败,客群ID:{},异常:{}", custGroup.getId(), e.getMessage(), e);
|
||||||
|
|
||||||
|
// 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理
|
||||||
|
|
||||||
// 更新创建状态为失败
|
// 更新创建状态为失败
|
||||||
custGroup.setCreateStatus("2");
|
custGroup.setCreateStatus("2");
|
||||||
|
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||||
|
custGroup.setUpdateTime(new Date());
|
||||||
custGroupMapper.updateById(custGroup);
|
custGroupMapper.updateById(custGroup);
|
||||||
log.error("客群客户导入失败,客群ID:{}", custGroup.getId(), e);
|
|
||||||
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,63 +723,122 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
memberList.addAll(importFromCmpmGrid(custGroup, gridImportDTO, headId));
|
memberList.addAll(importFromCmpmGrid(custGroup, gridImportDTO, headId));
|
||||||
} else if ("1".equals(gridType)) {
|
} else if ("1".equals(gridType)) {
|
||||||
// 地理网格
|
// 地理网格
|
||||||
memberList.addAll(importFromRegionGrid(custGroup, gridImportDTO));
|
memberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId));
|
||||||
} else if ("2".equals(gridType)) {
|
} else if ("2".equals(gridType)) {
|
||||||
// 绘制网格
|
// 绘制网格
|
||||||
memberList.addAll(importFromDrawGrid(custGroup, gridImportDTO));
|
memberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId));
|
||||||
}
|
}
|
||||||
if (memberList.isEmpty()) {
|
if (memberList.isEmpty()) {
|
||||||
throw new ServiceException("未查询到任何客户");
|
throw new ServiceException("未查询到任何客户");
|
||||||
}
|
}
|
||||||
// 批量插入
|
|
||||||
int batchSize = 1000;
|
// 使用编程式事务:先删除旧客户,再插入新客户
|
||||||
int successCount = 0;
|
transactionTemplate.executeWithoutResult(status -> {
|
||||||
int skippedCount = 0;
|
// 删除该客群的所有旧客户
|
||||||
int restoredCount = 0;
|
log.info("开始删除客群旧客户,客群ID:{}", custGroup.getId());
|
||||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
LambdaQueryWrapper<CustGroupMember> memberWrapper = new LambdaQueryWrapper<>();
|
||||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId());
|
||||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
custGroupMemberMapper.delete(memberWrapper);
|
||||||
for (CustGroupMember member : batchList) {
|
log.info("客群旧客户删除完成,客群ID:{}", custGroup.getId());
|
||||||
try {
|
|
||||||
custGroupMemberMapper.insert(member);
|
// 批量插入新客户
|
||||||
successCount++;
|
log.info("开始批量插入客户,客群ID:{},客户总数:{}", custGroup.getId(), memberList.size());
|
||||||
} catch (DuplicateKeyException e) {
|
|
||||||
// 客户已存在,检查是否是被手动移除的
|
// 分批批量插入(每批1000条)
|
||||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
int batchSize = 1000;
|
||||||
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
int totalInserted = 0;
|
||||||
.eq(CustGroupMember::getCustId, member.getCustId())
|
int totalRestored = 0;
|
||||||
.eq(CustGroupMember::getCustType, member.getCustType());
|
int totalSkipped = 0;
|
||||||
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
|
||||||
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||||
// 是被手动移除的客户,清除标记并恢复
|
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||||
existMember.setManualRemove(0);
|
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||||
existMember.setDelFlag(0);
|
log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1,
|
||||||
existMember.setCustName(member.getCustName());
|
(memberList.size() + batchSize - 1) / batchSize, batchList.size());
|
||||||
custGroupMemberMapper.updateById(existMember);
|
|
||||||
restoredCount++;
|
// SQL层面的批量插入
|
||||||
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
custGroupMemberMapper.batchInsertMembers(batchList);
|
||||||
} else {
|
|
||||||
// 正常存在的客户,跳过
|
// 查询本批中被手动移除的客户,需要恢复
|
||||||
skippedCount++;
|
List<CustGroupMember> toRestore = findManualRemovedToRestore(custGroup.getId(), batchList);
|
||||||
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
if (!toRestore.isEmpty()) {
|
||||||
|
// 恢复被手动移除的客户
|
||||||
|
for (CustGroupMember m : toRestore) {
|
||||||
|
batchList.stream()
|
||||||
|
.filter(b -> b.getCustId().equals(m.getCustId()) && b.getCustType().equals(m.getCustType()))
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(origin -> m.setCustName(origin.getCustName()));
|
||||||
|
m.setManualRemove(0);
|
||||||
|
custGroupMemberMapper.updateById(m);
|
||||||
}
|
}
|
||||||
|
log.info("本批恢复被手动移除的客户:{} 条", toRestore.size());
|
||||||
|
totalRestored += toRestore.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalInserted += batchList.size() - toRestore.size();
|
||||||
|
totalSkipped += toRestore.size();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
log.info("客群客户导入完成(网格),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
log.info("客群客户导入完成(网格),客群ID:{},插入:{},恢复:{},跳过:{}",
|
||||||
custGroup.getId(), successCount, skippedCount, restoredCount);
|
custGroup.getId(), totalInserted, totalRestored, totalSkipped);
|
||||||
// 更新创建状态为成功
|
|
||||||
custGroup.setCreateStatus("1");
|
// 更新创建状态为成功
|
||||||
custGroupMapper.updateById(custGroup);
|
custGroup.setCreateStatus("1");
|
||||||
|
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||||
|
custGroup.setUpdateTime(new Date());
|
||||||
|
log.info("准备更新客群状态为成功,客群ID:{}", custGroup.getId());
|
||||||
|
custGroupMapper.updateById(custGroup);
|
||||||
|
log.info("客群状态更新成功完成,客群ID:{}", custGroup.getId());
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// 先记录原始异常(必须第一时间记录,避免后续异常覆盖)
|
||||||
|
log.error("==========客群客户导入异常========== 客群ID:{},异常类型:{},异常消息:{}",
|
||||||
|
custGroup.getId(), e.getClass().getName(), e.getMessage(), e);
|
||||||
|
|
||||||
|
// 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理
|
||||||
|
|
||||||
// 更新创建状态为失败
|
// 更新创建状态为失败
|
||||||
custGroup.setCreateStatus("2");
|
try {
|
||||||
custGroupMapper.updateById(custGroup);
|
custGroup.setCreateStatus("2");
|
||||||
log.error("客群客户导入失败,客群ID:{}", custGroup.getId(), e);
|
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||||
|
custGroup.setUpdateTime(new Date());
|
||||||
|
log.info("准备更新客群状态为失败,客群ID:{}", custGroup.getId());
|
||||||
|
custGroupMapper.updateById(custGroup);
|
||||||
|
log.info("客群状态更新为失败完成,客群ID:{}", custGroup.getId());
|
||||||
|
} catch (Exception updateException) {
|
||||||
|
log.error("==========更新客群状态为失败也异常了========== 客群ID:{},异常类型:{},异常消息:{}",
|
||||||
|
custGroup.getId(), updateException.getClass().getName(), updateException.getMessage(), updateException);
|
||||||
|
}
|
||||||
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找需要恢复的被手动移除的客户
|
||||||
|
*
|
||||||
|
* @param groupId 客群ID
|
||||||
|
* @param batchList 本批导入的客户列表
|
||||||
|
* @return 需要恢复的客户列表
|
||||||
|
*/
|
||||||
|
private List<CustGroupMember> findManualRemovedToRestore(Long groupId, List<CustGroupMember> batchList) {
|
||||||
|
// 构建本批客户的 (custId, custType) 集合
|
||||||
|
Set<String> batchKeys = batchList.stream()
|
||||||
|
.map(m -> m.getCustId() + "|" + m.getCustType())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 查询该客群所有被手动移除的客户
|
||||||
|
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(CustGroupMember::getGroupId, groupId)
|
||||||
|
.eq(CustGroupMember::getManualRemove, 1)
|
||||||
|
.eq(CustGroupMember::getDelFlag, 0);
|
||||||
|
List<CustGroupMember> manualRemovedList = custGroupMemberMapper.selectList(queryWrapper);
|
||||||
|
|
||||||
|
// 筛选出本批中需要恢复的
|
||||||
|
return manualRemovedList.stream()
|
||||||
|
.filter(m -> batchKeys.contains(m.getCustId() + "|" + m.getCustType()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从绩效网格导入客户
|
* 从绩效网格导入客户
|
||||||
*/
|
*/
|
||||||
@@ -735,13 +853,13 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
gridTypes.add("corporate");
|
gridTypes.add("corporate");
|
||||||
gridTypes.add("corporate_account");
|
gridTypes.add("corporate_account");
|
||||||
} else {
|
} else {
|
||||||
throw new ServiceException("请选择绩效网格业务类型(零售/公司)");
|
throw new ServiceException("请选择绩效网格业务类型(零售/对公/对公账户)");
|
||||||
}
|
}
|
||||||
// 查询客户
|
// 查询客户
|
||||||
for (String userName : gridImportDTO.getUserNames()) {
|
for (String userName : gridImportDTO.getUserNames()) {
|
||||||
for (String gridType : gridTypes) {
|
for (String gridType : gridTypes) {
|
||||||
List<GridCmpm> cmpmList = gridCmpmMapper.getGridCmpmByUserName(userName, headId, gridType);
|
List<GridCmpmVO> cmpmList = gridCmpmService.selectManageListForImport(gridType, userName, headId);
|
||||||
for (GridCmpm cmpm : cmpmList) {
|
for (GridCmpmVO cmpm : cmpmList) {
|
||||||
CustGroupMember member = new CustGroupMember();
|
CustGroupMember member = new CustGroupMember();
|
||||||
member.setGroupId(custGroup.getId());
|
member.setGroupId(custGroup.getId());
|
||||||
member.setCustId(cmpm.getCustId());
|
member.setCustId(cmpm.getCustId());
|
||||||
@@ -760,25 +878,21 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
/**
|
/**
|
||||||
* 从地理网格导入客户
|
* 从地理网格导入客户
|
||||||
*/
|
*/
|
||||||
private List<CustGroupMember> importFromRegionGrid(CustGroup custGroup, GridImportDTO gridImportDTO) {
|
private List<CustGroupMember> importFromRegionGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) {
|
||||||
List<CustGroupMember> memberList = new ArrayList<>();
|
List<CustGroupMember> memberList = new ArrayList<>();
|
||||||
// 查询地理网格获取编码
|
|
||||||
if (gridImportDTO.getRegionGridIds() == null || gridImportDTO.getRegionGridIds().isEmpty()) {
|
if (gridImportDTO.getRegionGridIds() == null || gridImportDTO.getRegionGridIds().isEmpty()) {
|
||||||
throw new ServiceException("请选择地理网格");
|
throw new ServiceException("请选择地理网格");
|
||||||
}
|
}
|
||||||
List<RegionGrid> regionGrids = regionGridMapper.selectBatchIds(gridImportDTO.getRegionGridIds());
|
// 直接根据网格ID列表批量查询所有客户(SQL中直接拼接headId,绕过MyBatis-Plus拦截器)
|
||||||
// 使用 selectAllCustFromGrid 方法查询所有客户(不限制客户类型)
|
List<RegionCustUser> custUsers = regionGridListService.selectAllCustByGridIds(gridImportDTO.getRegionGridIds(), headId);
|
||||||
for (RegionGrid regionGrid : regionGrids) {
|
for (RegionCustUser custUser : custUsers) {
|
||||||
List<RegionCustUser> custUsers = regionGridListService.selectAllCustFromGrid(regionGrid);
|
CustGroupMember member = new CustGroupMember();
|
||||||
for (RegionCustUser custUser : custUsers) {
|
member.setGroupId(custGroup.getId());
|
||||||
CustGroupMember member = new CustGroupMember();
|
member.setCustId(custUser.getCustId());
|
||||||
member.setGroupId(custGroup.getId());
|
member.setCustName(custUser.getCustName());
|
||||||
member.setCustId(custUser.getCustId());
|
member.setCustType(custUser.getCustType());
|
||||||
member.setCustName(custUser.getCustName());
|
member.setCreateTime(new Date());
|
||||||
member.setCustType(custUser.getCustType());
|
memberList.add(member);
|
||||||
member.setCreateTime(new Date());
|
|
||||||
memberList.add(member);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return memberList;
|
return memberList;
|
||||||
}
|
}
|
||||||
@@ -786,27 +900,21 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
/**
|
/**
|
||||||
* 从绘制网格导入客户
|
* 从绘制网格导入客户
|
||||||
*/
|
*/
|
||||||
private List<CustGroupMember> importFromDrawGrid(CustGroup custGroup, GridImportDTO gridImportDTO) {
|
private List<CustGroupMember> importFromDrawGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) {
|
||||||
List<CustGroupMember> memberList = new ArrayList<>();
|
List<CustGroupMember> memberList = new ArrayList<>();
|
||||||
if (gridImportDTO.getDrawGridIds() == null || gridImportDTO.getDrawGridIds().isEmpty()) {
|
if (gridImportDTO.getDrawGridIds() == null || gridImportDTO.getDrawGridIds().isEmpty()) {
|
||||||
throw new ServiceException("请选择绘制网格");
|
throw new ServiceException("请选择绘制网格");
|
||||||
}
|
}
|
||||||
// 查询绘制网格关联的图形ID
|
// 使用 selectCustByDrawGridId 方法(直接在SQL中拼接headId,绕过拦截器)
|
||||||
for (Long gridId : gridImportDTO.getDrawGridIds()) {
|
for (Long gridId : gridImportDTO.getDrawGridIds()) {
|
||||||
LambdaQueryWrapper<DrawGridShapeRelate> relateWrapper = new LambdaQueryWrapper<>();
|
List<RegionCustUser> custUsers = drawGridCustUserUnbindMapper.selectCustByDrawGridId(gridId, headId);
|
||||||
relateWrapper.eq(DrawGridShapeRelate::getGridId, gridId);
|
if (custUsers != null && !custUsers.isEmpty()) {
|
||||||
List<DrawGridShapeRelate> relates = drawGridShapeRelateMapper.selectList(relateWrapper);
|
for (RegionCustUser custUser : custUsers) {
|
||||||
for (DrawGridShapeRelate relate : relates) {
|
|
||||||
// 根据图形ID查询客户
|
|
||||||
LambdaQueryWrapper<DrawShapeCust> custWrapper = new LambdaQueryWrapper<>();
|
|
||||||
custWrapper.eq(DrawShapeCust::getShapeId, relate.getShapeId());
|
|
||||||
List<DrawShapeCust> shapeCusts = drawShapeCustMapper.selectList(custWrapper);
|
|
||||||
for (DrawShapeCust shapeCust : shapeCusts) {
|
|
||||||
CustGroupMember member = new CustGroupMember();
|
CustGroupMember member = new CustGroupMember();
|
||||||
member.setGroupId(custGroup.getId());
|
member.setGroupId(custGroup.getId());
|
||||||
member.setCustId(shapeCust.getCustId());
|
member.setCustId(custUser.getCustId());
|
||||||
member.setCustName(shapeCust.getCustName());
|
member.setCustName(custUser.getCustName());
|
||||||
member.setCustType(shapeCust.getCustType());
|
member.setCustType(custUser.getCustType());
|
||||||
member.setCreateTime(new Date());
|
member.setCreateTime(new Date());
|
||||||
memberList.add(member);
|
memberList.add(member);
|
||||||
}
|
}
|
||||||
@@ -814,4 +922,4 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
|||||||
}
|
}
|
||||||
return memberList;
|
return memberList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,37 @@
|
|||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.ruoyi.group.mapper.CustGroupMapper">
|
<mapper namespace="com.ruoyi.group.mapper.CustGroupMapper">
|
||||||
|
|
||||||
|
<sql id="custGroupVisibleBaseCondition">
|
||||||
|
AND (
|
||||||
|
cg.user_name = #{userName}
|
||||||
|
OR (
|
||||||
|
cg.share_enabled = 1
|
||||||
|
AND cg.group_status = '0'
|
||||||
|
AND cg.share_dept_ids IS NOT NULL
|
||||||
|
AND cg.share_dept_ids != ''
|
||||||
|
AND find_in_set(#{deptId}, cg.share_dept_ids)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="custGroupVisibleCondition">
|
||||||
|
<choose>
|
||||||
|
<when test="dto != null and dto.viewType == 'mine'">
|
||||||
|
AND cg.user_name = #{userName}
|
||||||
|
</when>
|
||||||
|
<when test="dto != null and dto.viewType == 'sharedToMe'">
|
||||||
|
AND cg.share_enabled = 1
|
||||||
|
AND cg.group_status = '0'
|
||||||
|
AND cg.share_dept_ids IS NOT NULL
|
||||||
|
AND cg.share_dept_ids != ''
|
||||||
|
AND find_in_set(#{deptId}, cg.share_dept_ids)
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
<include refid="custGroupVisibleBaseCondition"/>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="selectCustGroupList" resultType="CustGroupVO">
|
<select id="selectCustGroupList" resultType="CustGroupVO">
|
||||||
SELECT
|
SELECT
|
||||||
cg.id,
|
cg.id,
|
||||||
@@ -26,7 +57,8 @@
|
|||||||
FROM ibs_cust_group cg
|
FROM ibs_cust_group cg
|
||||||
<where>
|
<where>
|
||||||
cg.del_flag = '0'
|
cg.del_flag = '0'
|
||||||
and create_status = '1'
|
AND cg.create_status = '1'
|
||||||
|
<include refid="custGroupVisibleCondition"/>
|
||||||
<if test="dto.groupName != null and dto.groupName != ''">
|
<if test="dto.groupName != null and dto.groupName != ''">
|
||||||
AND cg.group_name LIKE CONCAT('%', #{dto.groupName}, '%')
|
AND cg.group_name LIKE CONCAT('%', #{dto.groupName}, '%')
|
||||||
</if>
|
</if>
|
||||||
@@ -43,4 +75,45 @@
|
|||||||
ORDER BY cg.create_time DESC
|
ORDER BY cg.create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectCustGroupById" resultType="CustGroupVO">
|
||||||
|
SELECT
|
||||||
|
cg.id,
|
||||||
|
cg.group_name,
|
||||||
|
cg.group_mode,
|
||||||
|
cg.create_mode,
|
||||||
|
cg.user_name,
|
||||||
|
cg.nick_name,
|
||||||
|
cg.dept_id,
|
||||||
|
cg.share_enabled,
|
||||||
|
cg.share_dept_ids,
|
||||||
|
cg.group_status,
|
||||||
|
cg.valid_time,
|
||||||
|
cg.create_by,
|
||||||
|
cg.create_time,
|
||||||
|
cg.update_by,
|
||||||
|
cg.update_time,
|
||||||
|
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
|
||||||
|
FROM ibs_cust_group cg
|
||||||
|
WHERE cg.id = #{id}
|
||||||
|
AND cg.del_flag = '0'
|
||||||
|
AND cg.create_status = '1'
|
||||||
|
<include refid="custGroupVisibleBaseCondition"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="countVisibleCustGroup" resultType="java.lang.Long">
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM ibs_cust_group cg
|
||||||
|
WHERE cg.id = #{id}
|
||||||
|
AND cg.del_flag = '0'
|
||||||
|
AND cg.create_status = '1'
|
||||||
|
<include refid="custGroupVisibleBaseCondition"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?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.CustGroupMemberMapper">
|
||||||
|
|
||||||
|
<select id="selectCustGroupMemberList" resultType="CustGroupMemberVO">
|
||||||
|
SELECT
|
||||||
|
cgm.id,
|
||||||
|
cgm.group_id,
|
||||||
|
cgm.cust_type,
|
||||||
|
cgm.cust_id,
|
||||||
|
cgm.cust_name,
|
||||||
|
cgm.cust_idc,
|
||||||
|
cgm.social_credit_code,
|
||||||
|
cgm.create_time
|
||||||
|
FROM ibs_cust_group_member cgm
|
||||||
|
<where>
|
||||||
|
cgm.group_id = #{groupId}
|
||||||
|
AND cgm.del_flag = '0'
|
||||||
|
<if test="dto != null and dto.custType != null and dto.custType != ''">
|
||||||
|
AND cgm.cust_type = #{dto.custType}
|
||||||
|
</if>
|
||||||
|
<if test="dto != null and dto.custName != null and dto.custName != ''">
|
||||||
|
AND cgm.cust_name LIKE CONCAT('%', #{dto.custName}, '%')
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY cgm.create_time ASC
|
||||||
|
</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)
|
||||||
|
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')
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -117,4 +117,14 @@ public class GridCmpmController extends BaseController {
|
|||||||
public AjaxResult selectCustBaseInfoList(@RequestBody CustBaseInfo custBaseInfo) {
|
public AjaxResult selectCustBaseInfoList(@RequestBody CustBaseInfo custBaseInfo) {
|
||||||
return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ;
|
return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格类型获取客户经理列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/managerList")
|
||||||
|
@Log(title = "绩效网格-获取客户经理列表")
|
||||||
|
@ApiOperation("获取客户经理列表")
|
||||||
|
public AjaxResult getManagerListByGridType(@RequestParam String gridType) {
|
||||||
|
return AjaxResult.success(gridCmpmService.getManagerListByGridType(gridType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ import com.ruoyi.ibs.cmpm.domain.entity.GridCmpm;
|
|||||||
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO;
|
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO;
|
||||||
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO;
|
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO;
|
||||||
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmClaimVO;
|
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmClaimVO;
|
||||||
|
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
|
||||||
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
|
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.cursor.Cursor;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author 吴凯程
|
* @Author 吴凯程
|
||||||
@@ -60,6 +64,32 @@ public interface GridCmpmMapper {
|
|||||||
|
|
||||||
List<String> selectManagerList();
|
List<String> selectManagerList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格类型和总行ID查询客户经理列表
|
||||||
|
* @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account)
|
||||||
|
* @param headId 总行ID
|
||||||
|
* @return 客户经理列表(user_name, nick_name)
|
||||||
|
*/
|
||||||
|
List<Map<String, String>> getManagerListByGridType(@Param("gridType") String gridType, @Param("headId") String headId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格类型、总行ID和客户经理查询客户列表(分表查询,适用于客群导入)
|
||||||
|
* @param gridType 网格类型
|
||||||
|
* @param userName 客户经理柜员号
|
||||||
|
* @param headId 总行ID
|
||||||
|
* @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<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo);
|
// List<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -302,4 +302,26 @@ public class GridCmpmService {
|
|||||||
return changesMap;
|
return changesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格类型和总行ID查询客户经理列表
|
||||||
|
* @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account)
|
||||||
|
* @return 客户经理列表
|
||||||
|
*/
|
||||||
|
public List<Map<String, String>> getManagerListByGridType(String gridType) {
|
||||||
|
String headId = SecurityUtils.getHeadId();
|
||||||
|
return gridCmpmMapper.getManagerListByGridType(gridType, headId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数查询绩效网格客户列表(不依赖SecurityUtils,适用于异步线程)
|
||||||
|
* 所有参数由调用者传入,不在方法内部获取用户上下文
|
||||||
|
* @param gridType 网格类型
|
||||||
|
* @param userName 客户经理柜员号
|
||||||
|
* @param headId 总行ID
|
||||||
|
* @return 客户列表
|
||||||
|
*/
|
||||||
|
public List<GridCmpmVO> selectManageListForImport(String gridType, String userName, String headId) {
|
||||||
|
return gridCmpmMapper.getCustomerListForImport(gridType, userName, headId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.rmi.ServerException;
|
import java.rmi.ServerException;
|
||||||
@@ -220,4 +222,18 @@ public class MyCustomerController extends BaseController {
|
|||||||
}
|
}
|
||||||
return AjaxResult.success(iSysCampaignGroupCustomerService.appointCustCamp( custId, custName, custIdc, custPhone, custIsn,socialCreditCode,lpName, campaignId, custType));
|
return AjaxResult.success(iSysCampaignGroupCustomerService.appointCustCamp( custId, custName, custIdc, custPhone, custIsn,socialCreditCode,lpName, campaignId, custType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Log(title = "我的客户-异步导入企业客户价值分层", businessType = com.ruoyi.common.enums.BusinessType.IMPORT)
|
||||||
|
@PostMapping(value = "/importBusinessCustLevelAsync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
@ApiOperation("异步导入企业客户价值分层")
|
||||||
|
public AjaxResult importBusinessCustLevelAsync(@RequestPart("file") MultipartFile file) {
|
||||||
|
return AjaxResult.success("导入任务已提交,后台正在处理", myCustomerService.importBusinessCustLevelAsync(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "我的客户-查询企业客户价值分层导入状态")
|
||||||
|
@GetMapping("/importBusinessCustLevelStatus/{taskId}")
|
||||||
|
@ApiOperation("查询企业客户价值分层导入状态")
|
||||||
|
public AjaxResult importBusinessCustLevelStatus(@PathVariable String taskId) {
|
||||||
|
return AjaxResult.success(myCustomerService.getBusinessCustLevelImportStatus(taskId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.ibs.customerselect.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BusinessCustLevelImportExcelDTO {
|
||||||
|
|
||||||
|
@ExcelProperty("统信码")
|
||||||
|
private String socialCreditCode;
|
||||||
|
|
||||||
|
@ExcelProperty("价值分层")
|
||||||
|
private String custLevel;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.ibs.customerselect.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BusinessCustLevelImportTaskVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0-处理中 1-成功 2-失败
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
private Integer totalCount;
|
||||||
|
|
||||||
|
private Integer successCount;
|
||||||
|
|
||||||
|
private Integer ignoredCount;
|
||||||
|
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
private String createTime;
|
||||||
|
|
||||||
|
private String finishTime;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.ruoyi.ibs.customerselect.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
|
import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
|
||||||
|
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO;
|
||||||
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
|
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
|
||||||
import com.ruoyi.ibs.customerselect.domain.CustInfoDeleteFromAnchor;
|
import com.ruoyi.ibs.customerselect.domain.CustInfoDeleteFromAnchor;
|
||||||
import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor;
|
import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor;
|
||||||
@@ -242,4 +243,10 @@ public interface CustInfoBusinessMapper extends BaseMapper<CustInfoBusiness>
|
|||||||
public int insertCustomersToBusinessByScCode(List<SysGroupCustomer> sysGroupCustomers);
|
public int insertCustomersToBusinessByScCode(List<SysGroupCustomer> sysGroupCustomers);
|
||||||
|
|
||||||
List<CustInfoBusiness> selectRecord(String socialCreditCode);
|
List<CustInfoBusiness> selectRecord(String socialCreditCode);
|
||||||
|
|
||||||
|
List<String> selectExistingSocialCreditCodes(@Param("socialCreditCodes") List<String> socialCreditCodes,
|
||||||
|
@Param("deptCode") String deptCode);
|
||||||
|
|
||||||
|
int batchUpdateCustLevelBySocialCreditCode(@Param("list") List<BusinessCustLevelImportExcelDTO> list,
|
||||||
|
@Param("deptCode") String deptCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ruoyi.ibs.customerselect.service;
|
package com.ruoyi.ibs.customerselect.service;
|
||||||
|
|
||||||
import com.ruoyi.ibs.customerselect.domain.*;
|
import com.ruoyi.ibs.customerselect.domain.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -41,4 +42,8 @@ public interface IMyCustomerService {
|
|||||||
public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata);
|
public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata);
|
||||||
|
|
||||||
MerchantMcspInfo selectmerchantMessage(String custId);
|
MerchantMcspInfo selectmerchantMessage(String custId);
|
||||||
|
|
||||||
|
String importBusinessCustLevelAsync(MultipartFile file);
|
||||||
|
|
||||||
|
BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
package com.ruoyi.ibs.customerselect.service.Impl;
|
package com.ruoyi.ibs.customerselect.service.Impl;
|
||||||
|
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.ruoyi.common.annotation.DataScope;
|
import com.ruoyi.common.annotation.DataScope;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
|
import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
|
||||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||||
import com.ruoyi.common.core.redis.RedisCache;
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||||
|
import com.ruoyi.ibs.dashboard.service.NotificationService;
|
||||||
|
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO;
|
||||||
|
import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportTaskVO;
|
||||||
import com.ruoyi.ibs.customerselect.domain.*;
|
import com.ruoyi.ibs.customerselect.domain.*;
|
||||||
import com.ruoyi.ibs.customerselect.domain.vo.GridRelateVO;
|
import com.ruoyi.ibs.customerselect.domain.vo.GridRelateVO;
|
||||||
import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper;
|
import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper;
|
||||||
@@ -32,9 +38,16 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,6 +58,10 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class MyCustomerServiceImpl implements IMyCustomerService {
|
public class MyCustomerServiceImpl implements IMyCustomerService {
|
||||||
|
|
||||||
|
private static final String BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY = "BUSINESS_CUST_LEVEL_IMPORT_TASK_";
|
||||||
|
private static final int BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE = 1000;
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CustInfoBusinessMapper custInfoBusinessMapper;
|
private CustInfoBusinessMapper custInfoBusinessMapper;
|
||||||
|
|
||||||
@@ -80,6 +97,12 @@ public class MyCustomerServiceImpl implements IMyCustomerService {
|
|||||||
@Resource
|
@Resource
|
||||||
private RedisCache redisCache;
|
private RedisCache redisCache;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private NotificationService notificationService;
|
||||||
|
|
||||||
|
@Resource(name = "excelImportExecutor")
|
||||||
|
private ExecutorService excelImportExecutor;
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(MyCustomerServiceImpl.class);
|
private static Logger logger = LoggerFactory.getLogger(MyCustomerServiceImpl.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -467,5 +490,154 @@ public class MyCustomerServiceImpl implements IMyCustomerService {
|
|||||||
return merchantMcspInfoMapper.selectOne(new LambdaQueryWrapper<MerchantMcspInfo>().eq(MerchantMcspInfo::getCustId,custId));
|
return merchantMcspInfoMapper.selectOne(new LambdaQueryWrapper<MerchantMcspInfo>().eq(MerchantMcspInfo::getCustId,custId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String importBusinessCustLevelAsync(MultipartFile file) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new ServiceException("导入文件不能为空");
|
||||||
|
}
|
||||||
|
byte[] fileBytes;
|
||||||
|
try {
|
||||||
|
fileBytes = file.getBytes();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServiceException("读取导入文件失败");
|
||||||
|
}
|
||||||
|
String taskId = IdUtils.fastSimpleUUID();
|
||||||
|
String userName = SecurityUtils.getUsername();
|
||||||
|
String deptCode = SecurityUtils.getHeadId();
|
||||||
|
BusinessCustLevelImportTaskVO taskVO = new BusinessCustLevelImportTaskVO();
|
||||||
|
taskVO.setStatus("0");
|
||||||
|
taskVO.setMessage("导入任务已提交,后台正在处理");
|
||||||
|
taskVO.setTotalCount(0);
|
||||||
|
taskVO.setSuccessCount(0);
|
||||||
|
taskVO.setIgnoredCount(0);
|
||||||
|
taskVO.setUserName(userName);
|
||||||
|
taskVO.setCreateTime(formatNow());
|
||||||
|
cacheBusinessCustLevelImportTask(taskId, taskVO);
|
||||||
|
excelImportExecutor.submit(() -> doImportBusinessCustLevel(taskId, userName, deptCode, fileBytes));
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId) {
|
||||||
|
BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId));
|
||||||
|
if (taskVO == null) {
|
||||||
|
throw new ServiceException("导入任务不存在或已过期");
|
||||||
|
}
|
||||||
|
return taskVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doImportBusinessCustLevel(String taskId, String userName, String deptCode, byte[] fileBytes) {
|
||||||
|
BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId));
|
||||||
|
if (taskVO == null) {
|
||||||
|
taskVO = new BusinessCustLevelImportTaskVO();
|
||||||
|
taskVO.setUserName(userName);
|
||||||
|
taskVO.setCreateTime(formatNow());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<BusinessCustLevelImportExcelDTO> importRows = EasyExcel.read(new ByteArrayInputStream(fileBytes))
|
||||||
|
.head(BusinessCustLevelImportExcelDTO.class)
|
||||||
|
.sheet()
|
||||||
|
.doReadSync();
|
||||||
|
|
||||||
|
Map<String, String> levelMap = new LinkedHashMap<>();
|
||||||
|
if (importRows != null) {
|
||||||
|
for (BusinessCustLevelImportExcelDTO row : importRows) {
|
||||||
|
String socialCreditCode = normalizeImportCell(row.getSocialCreditCode());
|
||||||
|
String custLevel = normalizeImportCell(row.getCustLevel());
|
||||||
|
if (StringUtils.isEmpty(socialCreditCode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
levelMap.put(socialCreditCode, custLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (levelMap.isEmpty()) {
|
||||||
|
throw new ServiceException("Excel中未识别到有效的统信码和价值分层数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> existingSocialCreditCodes = getExistingBusinessSocialCreditCodes(new ArrayList<>(levelMap.keySet()), deptCode);
|
||||||
|
Set<String> existingCodeSet = new HashSet<>(existingSocialCreditCodes);
|
||||||
|
List<BusinessCustLevelImportExcelDTO> updateList = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, String> entry : levelMap.entrySet()) {
|
||||||
|
if (!existingCodeSet.contains(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BusinessCustLevelImportExcelDTO dto = new BusinessCustLevelImportExcelDTO();
|
||||||
|
dto.setSocialCreditCode(entry.getKey());
|
||||||
|
dto.setCustLevel(entry.getValue());
|
||||||
|
updateList.add(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
batchUpdateBusinessCustLevel(updateList, deptCode);
|
||||||
|
|
||||||
|
int totalCount = levelMap.size();
|
||||||
|
int successCount = updateList.size();
|
||||||
|
int ignoredCount = totalCount - successCount;
|
||||||
|
String message = String.format("公司客户视图分层分类数据导入完成,成功更新%d条,忽略%d条", successCount, ignoredCount);
|
||||||
|
|
||||||
|
taskVO.setStatus("1");
|
||||||
|
taskVO.setMessage(message);
|
||||||
|
taskVO.setTotalCount(totalCount);
|
||||||
|
taskVO.setSuccessCount(successCount);
|
||||||
|
taskVO.setIgnoredCount(ignoredCount);
|
||||||
|
taskVO.setFinishTime(formatNow());
|
||||||
|
cacheBusinessCustLevelImportTask(taskId, taskVO);
|
||||||
|
notificationService.sendNotification(userName, message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String errorMsg = StringUtils.isNotEmpty(e.getMessage()) ? e.getMessage() : "导入失败,请检查文件内容";
|
||||||
|
taskVO.setStatus("2");
|
||||||
|
taskVO.setMessage(errorMsg);
|
||||||
|
taskVO.setFinishTime(formatNow());
|
||||||
|
cacheBusinessCustLevelImportTask(taskId, taskVO);
|
||||||
|
notificationService.sendNotification(userName, "公司客户视图分层分类数据导入失败:" + errorMsg);
|
||||||
|
logger.error("公司客户视图分层分类数据导入失败,taskId={}", taskId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void batchUpdateBusinessCustLevel(List<BusinessCustLevelImportExcelDTO> updateList, String deptCode) {
|
||||||
|
if (updateList == null || updateList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < updateList.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) {
|
||||||
|
int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, updateList.size());
|
||||||
|
custInfoBusinessMapper.batchUpdateCustLevelBySocialCreditCode(updateList.subList(i, endIndex), deptCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getExistingBusinessSocialCreditCodes(List<String> socialCreditCodes, String deptCode) {
|
||||||
|
if (socialCreditCodes == null || socialCreditCodes.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<String> existingSocialCreditCodes = new ArrayList<>();
|
||||||
|
for (int i = 0; i < socialCreditCodes.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) {
|
||||||
|
int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, socialCreditCodes.size());
|
||||||
|
List<String> batchCodes = socialCreditCodes.subList(i, endIndex);
|
||||||
|
List<String> batchResult = custInfoBusinessMapper.selectExistingSocialCreditCodes(batchCodes, deptCode);
|
||||||
|
if (batchResult != null && !batchResult.isEmpty()) {
|
||||||
|
existingSocialCreditCodes.addAll(batchResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return existingSocialCreditCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cacheBusinessCustLevelImportTask(String taskId, BusinessCustLevelImportTaskVO taskVO) {
|
||||||
|
redisCache.setCacheObject(getBusinessCustLevelImportTaskKey(taskId), taskVO, 24, TimeUnit.HOURS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBusinessCustLevelImportTaskKey(String taskId) {
|
||||||
|
return BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY + taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeImportCell(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmedValue = value.trim();
|
||||||
|
return trimmedValue.isEmpty() ? null : trimmedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatNow() {
|
||||||
|
return LocalDateTime.now().format(DATE_TIME_FORMATTER);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ public class DashboardController extends BaseController {
|
|||||||
public TableDataInfo list(SysNotice notice)
|
public TableDataInfo list(SysNotice notice)
|
||||||
{
|
{
|
||||||
startPage();
|
startPage();
|
||||||
|
notice.setCurrentHeadDeptId(SecurityUtils.getHeadId() + "000");
|
||||||
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,16 @@ public class DrawGridController extends BaseController {
|
|||||||
return getDataTable(gridList, page);
|
return getDataTable(gridList, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("获取网格列表(用于客群创建,简化查询)")
|
||||||
|
@Log(title = "自定义绘制网格--获取网格列表(客群创建用)")
|
||||||
|
@GetMapping("/simpleList")
|
||||||
|
public R<List<DrawGridListVO>> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
|
||||||
|
drawGridListDTO.setUserName(SecurityUtils.getUsername());
|
||||||
|
drawGridListDTO.setDeptId(SecurityUtils.getDeptId());
|
||||||
|
List<DrawGridListVO> gridList = drawGridService.getSimpleGridList(drawGridListDTO);
|
||||||
|
return R.ok(gridList);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation("分页获取网格内客户列表")
|
@ApiOperation("分页获取网格内客户列表")
|
||||||
@Log(title = "自定义绘制网格--分页获取网格内客户列表")
|
@Log(title = "自定义绘制网格--分页获取网格内客户列表")
|
||||||
@GetMapping("/cust/list")
|
@GetMapping("/cust/list")
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
import com.ruoyi.ibs.draw.domain.dto.grid.DrawGridCustListDTO;
|
import com.ruoyi.ibs.draw.domain.dto.grid.DrawGridCustListDTO;
|
||||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridCustUserUnbind;
|
import com.ruoyi.ibs.draw.domain.entity.DrawGridCustUserUnbind;
|
||||||
import com.ruoyi.ibs.draw.domain.vo.DrawGridCustVO;
|
import com.ruoyi.ibs.draw.domain.vo.DrawGridCustVO;
|
||||||
|
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -18,5 +20,13 @@ public interface DrawGridCustUserUnbindMapper extends BaseMapper<DrawGridCustUse
|
|||||||
|
|
||||||
List<DrawGridCustVO> getCustListByManager(DrawGridCustListDTO drawGridCustListDTO);
|
List<DrawGridCustVO> getCustListByManager(DrawGridCustListDTO drawGridCustListDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据绘制网格ID查询所有客户(用于客群导入,拼接headId绕过拦截器)
|
||||||
|
* @param gridId 绘制网格ID
|
||||||
|
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||||
|
* @return 客户列表
|
||||||
|
*/
|
||||||
|
List<RegionCustUser> selectCustByDrawGridId(@Param("gridId") Long gridId, @Param("headId") String headId);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package com.ruoyi.ibs.draw.mapper;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
|
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author 吴凯程
|
* @Author 吴凯程
|
||||||
@@ -10,4 +14,19 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
**/
|
**/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface DrawGridShapeRelateMapper extends BaseMapper<DrawGridShapeRelate> {
|
public interface DrawGridShapeRelateMapper extends BaseMapper<DrawGridShapeRelate> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格ID查询关联的图形(用于异步导入客群,原生SQL绕过拦截器)
|
||||||
|
* @param gridId 网格ID
|
||||||
|
* @return 图形关联列表
|
||||||
|
*/
|
||||||
|
List<DrawGridShapeRelate> selectByGridId(@Param("gridId") Long gridId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器)
|
||||||
|
* @param gridId 网格ID
|
||||||
|
* @param headId 部门代码(用于拼接动态表名)
|
||||||
|
* @return 客户列表,包含 custId, custName, custType
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectCustListByGridId(@Param("gridId") Long gridId, @Param("headId") String headId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ public interface DrawGridService {
|
|||||||
|
|
||||||
List<DrawGridListVO> getGridList(DrawGridListDTO drawGridListDTO);
|
List<DrawGridListVO> getGridList(DrawGridListDTO drawGridListDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网格列表(用于客群创建,简化查询不统计客户数量)
|
||||||
|
* @param drawGridListDTO 查询条件
|
||||||
|
* @return 网格列表(不含客户数量)
|
||||||
|
*/
|
||||||
|
List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -334,6 +334,15 @@ public class DrawGridServiceImpl implements DrawGridService {
|
|||||||
return gridList;
|
return gridList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
|
||||||
|
if (SecurityUtils.userRole().equals("manager")) {
|
||||||
|
return drawGridMapper.getGridListByManager(drawGridListDTO);
|
||||||
|
} else {
|
||||||
|
return drawGridMapper.getGridList(drawGridListDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private CustCountDTO getCustCountByGrid(Long gridId) {
|
private CustCountDTO getCustCountByGrid(Long gridId) {
|
||||||
CustCountDTO custCountDTO = new CustCountDTO();
|
CustCountDTO custCountDTO = new CustCountDTO();
|
||||||
LambdaQueryWrapper<DrawGridShapeRelate> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<DrawGridShapeRelate> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
|
|||||||
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
||||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||||
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||||
import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
|
import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
|
||||||
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
||||||
@@ -108,6 +109,14 @@ public class RegionGridListController extends BaseController {
|
|||||||
return AjaxResult.success("开始更新行社所有网格");
|
return AjaxResult.success("开始更新行社所有网格");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询地理网格列表(专用于客群创建)
|
||||||
|
*/
|
||||||
|
@ApiOperation("查询地理网格列表(专用于客群创建)")
|
||||||
|
@Log(title = "地理网格-查询地理网格列表(客群创建用)")
|
||||||
|
@GetMapping("/groupList")
|
||||||
|
public AjaxResult getRegionGridListForGroup(RegionGridListDTO dto) {
|
||||||
|
return AjaxResult.success(regionGridListService.getRegionGridListForGroup(dto));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ruoyi.ibs.grid.domain.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地理网格选择VO - 专用于客群创建时的网格选择
|
||||||
|
* 只包含必要字段,提升查询效率
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RegionGridGroupVO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网格主键
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "网格主键")
|
||||||
|
private Long gridId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网格名称
|
||||||
|
*/
|
||||||
|
@ApiModelProperty(value = "网格名称")
|
||||||
|
private String gridName;
|
||||||
|
}
|
||||||
@@ -40,6 +40,13 @@ public interface RegionCustUserMapper extends BaseMapper<RegionCustUser> {
|
|||||||
|
|
||||||
Long countCustInSecGrid(@Param("gridId") Long gridId, @Param("custType") String custType);
|
Long countCustInSecGrid(@Param("gridId") Long gridId, @Param("custType") String custType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格ID列表查询所有客户(用于异步导入客群,直接根据gridIds查询无需区分等级)
|
||||||
|
* @param gridIds 网格ID列表
|
||||||
|
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||||
|
*/
|
||||||
|
List<RegionCustUser> selectAllCustByGridIds(@Param("gridIds") List<Long> gridIds, @Param("headId") String headId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
|
|||||||
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
||||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||||
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
@@ -69,4 +70,10 @@ public interface RegionGridMapper extends BaseMapper<RegionGrid> {
|
|||||||
|
|
||||||
List<Long> getSecGridIdByTopGridId(@Param("gridId") Long gridId);
|
List<Long> getSecGridIdByTopGridId(@Param("gridId") Long gridId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||||
|
* 只返回gridId和gridName,避免N+1查询问题
|
||||||
|
*/
|
||||||
|
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
|||||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||||
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||||
|
|
||||||
@@ -36,11 +37,20 @@ public interface RegionGridListService {
|
|||||||
List<RegionGridListVO> getSecGridListByManager(RegionGridListDTO regionGridListDTO);
|
List<RegionGridListVO> getSecGridListByManager(RegionGridListDTO regionGridListDTO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询网格内所有客户(不限制客户类型,用于导入客群)
|
* 根据网格ID列表批量查询所有客户(用于导入客群,优化版)
|
||||||
* @param regionGrid 地理网格对象
|
* @param gridIds 网格ID列表
|
||||||
* @return 网格内所有客户列表
|
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||||
|
* @return 客户列表(去重)
|
||||||
*/
|
*/
|
||||||
List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid);
|
List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||||
|
* 只返回gridId和gridName,避免N+1查询问题
|
||||||
|
* @param regionGridListDTO 查询条件
|
||||||
|
* @return 网格列表(仅包含ID和名称)
|
||||||
|
*/
|
||||||
|
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.UnbindDTO;
|
|||||||
import com.ruoyi.ibs.grid.domain.entity.*;
|
import com.ruoyi.ibs.grid.domain.entity.*;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||||
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||||
import com.ruoyi.ibs.grid.mapper.*;
|
import com.ruoyi.ibs.grid.mapper.*;
|
||||||
@@ -432,23 +433,33 @@ public class RegionGridListServiceImpl implements RegionGridListService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询网格内所有客户(不限制客户类型,用于导入客群)
|
* 根据网格ID列表批量查询所有客户(用于导入客群,优化版)
|
||||||
* @param regionGrid 地理网格对象
|
* @param gridIds 网格ID列表
|
||||||
* @return 网格内所有客户列表
|
* @return 客户列表(去重)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid) {
|
public List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId) {
|
||||||
LambdaQueryWrapper<RegionCustUser> queryWrapper = new LambdaQueryWrapper<>();
|
return regionCustUserMapper.selectAllCustByGridIds(gridIds, headId);
|
||||||
// 根据网格等级判断使用一级还是二级网格ID查询
|
}
|
||||||
if (regionGrid.getGridLevel().equals("1")) {
|
|
||||||
queryWrapper.eq(RegionCustUser::getTopGridId, regionGrid.getGridId());
|
/**
|
||||||
} else if (regionGrid.getGridLevel().equals("2")) {
|
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||||
queryWrapper.eq(RegionCustUser::getSecGridId, regionGrid.getGridId());
|
* 只返回gridId和gridName,避免N+1查询问题
|
||||||
} else {
|
* @param regionGridListDTO 查询条件
|
||||||
throw new ServiceException("无效的网格等级: " + regionGrid.getGridLevel());
|
* @return 网格列表(仅包含ID和名称)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO) {
|
||||||
|
// 设置当前用户部门ID用于权限过滤
|
||||||
|
regionGridListDTO.setDeptId(SecurityUtils.getDeptId());
|
||||||
|
|
||||||
|
// 如果是总行用户,设置归属部室
|
||||||
|
if (SecurityUtils.isHead()) {
|
||||||
|
regionGridListDTO.setOpsDept(SecurityUtils.getOpsDept());
|
||||||
}
|
}
|
||||||
// 不限制客户类型,查询所有类型的客户
|
|
||||||
return regionCustUserMapper.selectList(queryWrapper);
|
// 直接查询,一次SQL完成,无N+1问题
|
||||||
|
return regionGridMapper.getRegionGridListForGroup(regionGridListDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,6 +312,13 @@ public class SysCampaignController extends BaseController {
|
|||||||
.stream().collect(HashMap::new, (m, v) -> m.put(v.getUuid(),v.getModelName()), HashMap::putAll)));
|
.stream().collect(HashMap::new, (m, v) -> m.put(v.getUuid(),v.getModelName()), HashMap::putAll)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/updateVisitInfoFeedback")
|
||||||
|
@ApiOperation("更新PAD走访反馈")
|
||||||
|
@Log(title ="pad走访记录-更新走访反馈", businessType = BusinessType.UPDATE)
|
||||||
|
public AjaxResult updateVisitInfoFeedback(@RequestBody VisitInfoFeedbackUpdateDTO updateDTO) {
|
||||||
|
return toAjax(sysCampaignService.updateVisitInfoFeedback(updateDTO));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
@ApiOperation("根据campaignId删除任务")
|
@ApiOperation("根据campaignId删除任务")
|
||||||
@Log(title = "走访-根据campaignId删除任务")
|
@Log(title = "走访-根据campaignId删除任务")
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ public class CustInfoBusinessVo {
|
|||||||
@ApiModelProperty(value = "手动打标")
|
@ApiModelProperty(value = "手动打标")
|
||||||
private List<TreeNode> tagManual;
|
private List<TreeNode> tagManual;
|
||||||
|
|
||||||
|
/** 九维画像分数 */
|
||||||
|
@ApiModelProperty(value = "九维画像分数")
|
||||||
|
private Ent9vPortraitOrc ent9vPortrait;
|
||||||
|
|
||||||
|
/** 九维画像详细信息 */
|
||||||
|
@ApiModelProperty(value = "九维画像详细信息")
|
||||||
|
private NineVFinalInfoOrc nineVFinalInfo;
|
||||||
|
|
||||||
public List<TreeNode> getTagManual() {
|
public List<TreeNode> getTagManual() {
|
||||||
return tagManual;
|
return tagManual;
|
||||||
}
|
}
|
||||||
@@ -187,4 +195,20 @@ public class CustInfoBusinessVo {
|
|||||||
public void setTabEnmuVos(List<TreeNode> tabEnmuVos) {
|
public void setTabEnmuVos(List<TreeNode> tabEnmuVos) {
|
||||||
this.tabEnmuVos = tabEnmuVos;
|
this.tabEnmuVos = tabEnmuVos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Ent9vPortraitOrc getEnt9vPortrait() {
|
||||||
|
return ent9vPortrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnt9vPortrait(Ent9vPortraitOrc ent9vPortrait) {
|
||||||
|
this.ent9vPortrait = ent9vPortrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NineVFinalInfoOrc getNineVFinalInfo() {
|
||||||
|
return nineVFinalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNineVFinalInfo(NineVFinalInfoOrc nineVFinalInfo) {
|
||||||
|
this.nineVFinalInfo = nineVFinalInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.ruoyi.ibs.list.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业九维画像分数对象 ent_9v_portrait_orc_825
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("ent_9v_portrait_orc_825")
|
||||||
|
public class Ent9vPortraitOrc implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 统一社会信用代码 */
|
||||||
|
private String uniscid;
|
||||||
|
|
||||||
|
/** 客户内码 */
|
||||||
|
@TableField("cst_id")
|
||||||
|
private String cstId;
|
||||||
|
|
||||||
|
/** 机构号 */
|
||||||
|
@TableField("org_no")
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** score_1合规经营 */
|
||||||
|
@TableField("score_1")
|
||||||
|
private BigDecimal score1;
|
||||||
|
|
||||||
|
/** score_2风险准入 */
|
||||||
|
@TableField("score_2")
|
||||||
|
private BigDecimal score2;
|
||||||
|
|
||||||
|
/** score_3高管信用评价 */
|
||||||
|
@TableField("score_3")
|
||||||
|
private String score3;
|
||||||
|
|
||||||
|
/** score_4股东信用评价 */
|
||||||
|
@TableField("score_4")
|
||||||
|
private BigDecimal score4;
|
||||||
|
|
||||||
|
/** score_5社会贡献度 */
|
||||||
|
@TableField("score_5")
|
||||||
|
private BigDecimal score5;
|
||||||
|
|
||||||
|
/** score_6稳定经营 */
|
||||||
|
@TableField("score_6")
|
||||||
|
private BigDecimal score6;
|
||||||
|
|
||||||
|
/** score_7经营能力 */
|
||||||
|
@TableField("score_7")
|
||||||
|
private BigDecimal score7;
|
||||||
|
|
||||||
|
/** score_8偿债能力 */
|
||||||
|
@TableField("score_8")
|
||||||
|
private BigDecimal score8;
|
||||||
|
|
||||||
|
/** score_9潜在代偿资源 */
|
||||||
|
@TableField("score_9")
|
||||||
|
private BigDecimal score9;
|
||||||
|
|
||||||
|
/** 九维总分 */
|
||||||
|
@TableField("score_all")
|
||||||
|
private BigDecimal scoreAll;
|
||||||
|
|
||||||
|
/** 九维总分排名 */
|
||||||
|
@TableField("score_all_rank")
|
||||||
|
private BigDecimal scoreAllRank;
|
||||||
|
|
||||||
|
/** 会计日期 */
|
||||||
|
@TableField("dat_dt")
|
||||||
|
private String datDt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
package com.ruoyi.ibs.list.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业九维画像详细信息对象 9v_final_info_orc_825
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("9v_final_info_orc_825")
|
||||||
|
public class NineVFinalInfoOrc implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 统一社会信用代码 */
|
||||||
|
private String uniscid;
|
||||||
|
|
||||||
|
/** 机构号 */
|
||||||
|
@TableField("org_no")
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 一、企业合规经营模块 */
|
||||||
|
|
||||||
|
/** 是否存在经营异常名录信息 */
|
||||||
|
@TableField("score_1_1")
|
||||||
|
private String score11;
|
||||||
|
|
||||||
|
/** 是否存在严重违法失信企业名单信息 */
|
||||||
|
@TableField("score_1_2")
|
||||||
|
private String score12;
|
||||||
|
|
||||||
|
/** 企业环境行为信仰登记是否为"E"或D */
|
||||||
|
@TableField("score_1_3")
|
||||||
|
private String score13;
|
||||||
|
|
||||||
|
/** 是否存在税务重大税收违法黑名单信息 */
|
||||||
|
@TableField("score_1_4")
|
||||||
|
private String score14;
|
||||||
|
|
||||||
|
/** 是否存在拖欠工资黑名单 */
|
||||||
|
@TableField("score_1_5")
|
||||||
|
private String score15;
|
||||||
|
|
||||||
|
/** 是否存在工商吊销企业信息 */
|
||||||
|
@TableField("score_1_6")
|
||||||
|
private String score16;
|
||||||
|
|
||||||
|
/** 二、企业风险准入模块 */
|
||||||
|
|
||||||
|
/** 是否存在注销企业信息 */
|
||||||
|
@TableField("score_2_1")
|
||||||
|
private String score21;
|
||||||
|
|
||||||
|
/** 是否存在执行案件信息 */
|
||||||
|
@TableField("score_2_2")
|
||||||
|
private String score22;
|
||||||
|
|
||||||
|
/** 是否存在查封信息 */
|
||||||
|
@TableField("score_2_3")
|
||||||
|
private String score23;
|
||||||
|
|
||||||
|
/** 是否存在单位未履行生效裁判信息 */
|
||||||
|
@TableField("score_2_4")
|
||||||
|
private String score24;
|
||||||
|
|
||||||
|
/** 是否存在企业破产清算信息 */
|
||||||
|
@TableField("score_2_5")
|
||||||
|
private String score25;
|
||||||
|
|
||||||
|
/** 是否失信被执行人 */
|
||||||
|
@TableField("score_2_6")
|
||||||
|
private String score26;
|
||||||
|
|
||||||
|
/** 是否为诉讼案件被告 */
|
||||||
|
@TableField("score_2_7")
|
||||||
|
private String score27;
|
||||||
|
|
||||||
|
/** 三、高管信用评价模块 */
|
||||||
|
|
||||||
|
/** 是否存在查封信息(score_3) */
|
||||||
|
@TableField("score_3_1")
|
||||||
|
private String score31;
|
||||||
|
|
||||||
|
/** 是否存在执行案件信息(score_3) */
|
||||||
|
@TableField("score_3_2")
|
||||||
|
private String score32;
|
||||||
|
|
||||||
|
/** 是否存在个人未履行生效裁判信息 */
|
||||||
|
@TableField("score_3_3")
|
||||||
|
private String score33;
|
||||||
|
|
||||||
|
/** 是否存在拖欠工资黑名单(score_3) */
|
||||||
|
@TableField("score_3_4")
|
||||||
|
private String score34;
|
||||||
|
|
||||||
|
/** 是否失信被执行人(score_3) */
|
||||||
|
@TableField("score_3_5")
|
||||||
|
private String score35;
|
||||||
|
|
||||||
|
/** 是否存在刑事案件被告人生效判决信息 */
|
||||||
|
@TableField("score_3_6")
|
||||||
|
private String score36;
|
||||||
|
|
||||||
|
/** 是否为诉讼案件被告(score_3) */
|
||||||
|
@TableField("score_3_7")
|
||||||
|
private String score37;
|
||||||
|
|
||||||
|
/** 四、股东信用评价模块 */
|
||||||
|
|
||||||
|
/** 是否存在查封信息(score_4) */
|
||||||
|
@TableField("score_4_1")
|
||||||
|
private String score41;
|
||||||
|
|
||||||
|
/** 是否存在执行案件信息(score_4) */
|
||||||
|
@TableField("score_4_2")
|
||||||
|
private String score42;
|
||||||
|
|
||||||
|
/** 是否存在个人未履行生效裁判信息(score_4) */
|
||||||
|
@TableField("score_4_3")
|
||||||
|
private String score43;
|
||||||
|
|
||||||
|
/** 是否存在拖欠工资黑名单(score_4) */
|
||||||
|
@TableField("score_4_4")
|
||||||
|
private String score44;
|
||||||
|
|
||||||
|
/** 是否失信被执行人(score_4) */
|
||||||
|
@TableField("score_4_5")
|
||||||
|
private String score45;
|
||||||
|
|
||||||
|
/** 是否存在刑事案件被告人生效判决信息(score_4) */
|
||||||
|
@TableField("score_4_6")
|
||||||
|
private String score46;
|
||||||
|
|
||||||
|
/** 是否为诉讼案件被告(score_4) */
|
||||||
|
@TableField("score_4_7")
|
||||||
|
private String score47;
|
||||||
|
|
||||||
|
/** 是否存在企业未履行生效裁判信息 */
|
||||||
|
@TableField("score_4_8")
|
||||||
|
private String score48;
|
||||||
|
|
||||||
|
/** 五、企业社会贡献度模块 */
|
||||||
|
|
||||||
|
/** 前12个月纳税金额 */
|
||||||
|
@TableField("score_5_1")
|
||||||
|
private String score51;
|
||||||
|
|
||||||
|
/** 纳税等级 */
|
||||||
|
@TableField("score_5_2")
|
||||||
|
private String score52;
|
||||||
|
|
||||||
|
/** 缴纳社保人数 */
|
||||||
|
@TableField("score_5_3")
|
||||||
|
private String score53;
|
||||||
|
|
||||||
|
/** 公积金缴纳人数 */
|
||||||
|
@TableField("score_5_4")
|
||||||
|
private String score54;
|
||||||
|
|
||||||
|
/** 是否为出口退税生产清单企业 */
|
||||||
|
@TableField("score_5_5")
|
||||||
|
private String score55;
|
||||||
|
|
||||||
|
/** 六、企业稳定经营模块 */
|
||||||
|
|
||||||
|
/** 市场主体经营年限 */
|
||||||
|
@TableField("score_6_1")
|
||||||
|
private String score61;
|
||||||
|
|
||||||
|
/** 股东(或发起人)或投资人信息认缴出资人数 */
|
||||||
|
@TableField("score_6_2")
|
||||||
|
private String score62;
|
||||||
|
|
||||||
|
/** 最大股东持股占比 */
|
||||||
|
@TableField("score_6_3")
|
||||||
|
private String score63;
|
||||||
|
|
||||||
|
/** 近三年法定代表人变更次数 */
|
||||||
|
@TableField("score_6_4")
|
||||||
|
private String score64;
|
||||||
|
|
||||||
|
/** 近三年股东变更次数 */
|
||||||
|
@TableField("score_6_5")
|
||||||
|
private String score65;
|
||||||
|
|
||||||
|
/** 近三年经营范围变更次数 */
|
||||||
|
@TableField("score_6_6")
|
||||||
|
private String score66;
|
||||||
|
|
||||||
|
/** 近三年经营地址变更次数 */
|
||||||
|
@TableField("score_6_7")
|
||||||
|
private String score67;
|
||||||
|
|
||||||
|
/** 近三年缴税年数 */
|
||||||
|
@TableField("score_6_8")
|
||||||
|
private String score68;
|
||||||
|
|
||||||
|
/** 法人户籍 */
|
||||||
|
@TableField("score_6_9")
|
||||||
|
private String score69;
|
||||||
|
|
||||||
|
/** 法人婚姻状况 */
|
||||||
|
@TableField("score_6_10")
|
||||||
|
private String score610;
|
||||||
|
|
||||||
|
/** 七、企业经营能力模块 */
|
||||||
|
|
||||||
|
/** 上年增值税金额 */
|
||||||
|
@TableField("score_7_1")
|
||||||
|
private String score71;
|
||||||
|
|
||||||
|
/** 今年增值税同比变动 */
|
||||||
|
@TableField("score_7_2")
|
||||||
|
private String score72;
|
||||||
|
|
||||||
|
/** 上年企业所得税金额 */
|
||||||
|
@TableField("score_7_3")
|
||||||
|
private String score73;
|
||||||
|
|
||||||
|
/** 今年所得税同比变动 */
|
||||||
|
@TableField("score_7_4")
|
||||||
|
private String score74;
|
||||||
|
|
||||||
|
/** 缴纳社保人数同比变动 */
|
||||||
|
@TableField("score_7_5")
|
||||||
|
private String score75;
|
||||||
|
|
||||||
|
/** 公积金缴纳人数同比变动 */
|
||||||
|
@TableField("score_7_6")
|
||||||
|
private String score76;
|
||||||
|
|
||||||
|
/** 当年纳税金额同比变动 */
|
||||||
|
@TableField("score_7_7")
|
||||||
|
private String score77;
|
||||||
|
|
||||||
|
/** 上年出口退税金额 */
|
||||||
|
@TableField("score_7_8")
|
||||||
|
private String score78;
|
||||||
|
|
||||||
|
/** 八、企业偿债能力模块 */
|
||||||
|
|
||||||
|
/** 房产套数 */
|
||||||
|
@TableField("score_8_1")
|
||||||
|
private String score81;
|
||||||
|
|
||||||
|
/** 房产面积 */
|
||||||
|
@TableField("score_8_2")
|
||||||
|
private String score82;
|
||||||
|
|
||||||
|
/** 未抵押房产套数 */
|
||||||
|
@TableField("score_8_3")
|
||||||
|
private String score83;
|
||||||
|
|
||||||
|
/** 未抵押房产面积 */
|
||||||
|
@TableField("score_8_4")
|
||||||
|
private String score84;
|
||||||
|
|
||||||
|
/** 已抵押房产被担保债权数额 */
|
||||||
|
@TableField("score_8_5")
|
||||||
|
private String score85;
|
||||||
|
|
||||||
|
/** 企业车产金额 */
|
||||||
|
@TableField("score_8_6")
|
||||||
|
private String score86;
|
||||||
|
|
||||||
|
/** 九、潜在代偿资源模块 */
|
||||||
|
|
||||||
|
/** 法人及股东房产面积合计 */
|
||||||
|
@TableField("score_9_1")
|
||||||
|
private String score91;
|
||||||
|
|
||||||
|
/** 法人及股东房产套数合计 */
|
||||||
|
@TableField("score_9_2")
|
||||||
|
private String score92;
|
||||||
|
|
||||||
|
/** 法人及股东未抵押房产套数合计 */
|
||||||
|
@TableField("score_9_3")
|
||||||
|
private String score93;
|
||||||
|
|
||||||
|
/** 法人及股东未抵押房产面积 */
|
||||||
|
@TableField("score_9_4")
|
||||||
|
private String score94;
|
||||||
|
|
||||||
|
/** 法人及股东已抵押房产被担保债权数额合计 */
|
||||||
|
@TableField("score_9_5")
|
||||||
|
private String score95;
|
||||||
|
|
||||||
|
/** 法人代表车产金额 */
|
||||||
|
@TableField("score_9_6")
|
||||||
|
private String score96;
|
||||||
|
|
||||||
|
/** 十、企业环境信用模块 */
|
||||||
|
|
||||||
|
/** 生态信用扣分 */
|
||||||
|
@TableField("score_b015")
|
||||||
|
private String scoreB015;
|
||||||
|
|
||||||
|
/** 列入环境失信黑名单时间 */
|
||||||
|
@TableField("score_b016_time")
|
||||||
|
private String scoreB016Time;
|
||||||
|
|
||||||
|
/** 列入环境失信黑名单原因 */
|
||||||
|
@TableField("score_b016_reason")
|
||||||
|
private String scoreB016Reason;
|
||||||
|
|
||||||
|
/** 环境行为信用评价等级 */
|
||||||
|
@TableField("score_a020")
|
||||||
|
private String scoreA020;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ruoyi.ibs.list.domain;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class VisitFeedbackItemDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "反馈类型")
|
||||||
|
private String feedbackType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "反馈产品列表")
|
||||||
|
private List<String> products;
|
||||||
|
}
|
||||||
@@ -117,4 +117,40 @@ public class VisitInfo {
|
|||||||
@ApiModelProperty(value = "走访备注")
|
@ApiModelProperty(value = "走访备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "互动地址")
|
||||||
|
private String interAddr;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "协同员工名称")
|
||||||
|
private String colStafName;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "后续备注")
|
||||||
|
private String laterNote;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "意向产品值")
|
||||||
|
private String intentionProductValue;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "反馈状态")
|
||||||
|
private String feedbackStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访来源")
|
||||||
|
private String sourceOfInterview;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "文件名")
|
||||||
|
private String filename;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "外呼状态")
|
||||||
|
private String outCallStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "外呼意向")
|
||||||
|
private String outCallIntention;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "来源")
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分析值")
|
||||||
|
private String analysisValue;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "设备")
|
||||||
|
private String facility;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ public class VisitInfoDTO {
|
|||||||
@ApiModelProperty(value = "柜员名称")
|
@ApiModelProperty(value = "柜员名称")
|
||||||
private String visName;
|
private String visName;
|
||||||
|
|
||||||
|
/** 柜员号 */
|
||||||
|
@ApiModelProperty(value = "柜员号")
|
||||||
|
private String visId;
|
||||||
|
|
||||||
/** 走访时间 */
|
/** 走访时间 */
|
||||||
@ApiModelProperty(value = "走访时间")
|
@ApiModelProperty(value = "走访时间")
|
||||||
private String visTime;
|
private String visTime;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.ruoyi.ibs.list.domain;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class VisitInfoFeedbackUpdateDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访记录ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访渠道")
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客户意愿结构化数据")
|
||||||
|
private List<VisitFeedbackItemDTO> feedbackItems;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客户意愿拼接值")
|
||||||
|
private String intentionProductValue;
|
||||||
|
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
private String deptId;
|
||||||
|
}
|
||||||
@@ -128,4 +128,40 @@ public class VisitInfoVO {
|
|||||||
@ApiModelProperty(value = "走访结果")
|
@ApiModelProperty(value = "走访结果")
|
||||||
private String interRes;
|
private String interRes;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "实地拜访地址")
|
||||||
|
private String interAddr;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "协同走访客户经理")
|
||||||
|
private String colStafName;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事后备注")
|
||||||
|
private String laterNote;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访反馈")
|
||||||
|
private String intentionProductValue;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "反馈状态")
|
||||||
|
private String feedbackStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访来源")
|
||||||
|
private String sourceOfInterview;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "批量导入文件名")
|
||||||
|
private String filename;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "外呼状态")
|
||||||
|
private String outCallStatus;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客户意愿")
|
||||||
|
private String outCallIntention;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "走访来源:1null:pad 2:企业微信 3:pc")
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "nlp模型提取")
|
||||||
|
private String analysisValue;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "预授信额度")
|
||||||
|
private String facility;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.ruoyi.ibs.list.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.ibs.list.domain.Ent9vPortraitOrc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业九维画像分数Mapper接口
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-18
|
||||||
|
*/
|
||||||
|
public interface Ent9vPortraitOrcMapper extends BaseMapper<Ent9vPortraitOrc> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.ruoyi.ibs.list.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.ibs.list.domain.NineVFinalInfoOrc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业九维画像详细信息Mapper接口
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-18
|
||||||
|
*/
|
||||||
|
public interface NineVFinalInfoOrcMapper extends BaseMapper<NineVFinalInfoOrc> {
|
||||||
|
}
|
||||||
@@ -156,6 +156,8 @@ public interface SysCampaignMapper extends BaseMapper<SysCampaign> {
|
|||||||
|
|
||||||
List<VisitInfoVO> selectVisitInfoList(VisitInfoDTO visitInfoDTO);
|
List<VisitInfoVO> selectVisitInfoList(VisitInfoDTO visitInfoDTO);
|
||||||
|
|
||||||
|
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
|
||||||
|
|
||||||
@Update("UPDATE sys_campaign SET del_flag = '2' where campaign_id = #{campaignId}")
|
@Update("UPDATE sys_campaign SET del_flag = '2' where campaign_id = #{campaignId}")
|
||||||
int deleteSysCampaignByCampaignId(@Param("campaignId") String campaignId);
|
int deleteSysCampaignByCampaignId(@Param("campaignId") String campaignId);
|
||||||
|
|
||||||
|
|||||||
@@ -115,5 +115,7 @@ public interface ISysCampaignService {
|
|||||||
|
|
||||||
public List<VisitInfoVO> selectVisitInfoVoList(VisitInfoDTO visitInfoDTO);
|
public List<VisitInfoVO> selectVisitInfoVoList(VisitInfoDTO visitInfoDTO);
|
||||||
|
|
||||||
|
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
|
||||||
|
|
||||||
public int deleteSysCampaign(String campaignId);
|
public int deleteSysCampaign(String campaignId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import com.ruoyi.ibs.grid.util.CustExcelUtil;
|
|||||||
import com.ruoyi.ibs.list.domain.*;
|
import com.ruoyi.ibs.list.domain.*;
|
||||||
import com.ruoyi.ibs.list.mapper.CorporateShareholderMapper;
|
import com.ruoyi.ibs.list.mapper.CorporateShareholderMapper;
|
||||||
import com.ruoyi.ibs.list.mapper.CustInfoRetailMapper;
|
import com.ruoyi.ibs.list.mapper.CustInfoRetailMapper;
|
||||||
|
import com.ruoyi.ibs.list.mapper.Ent9vPortraitOrcMapper;
|
||||||
import com.ruoyi.ibs.list.mapper.FamilyMembersMapper;
|
import com.ruoyi.ibs.list.mapper.FamilyMembersMapper;
|
||||||
|
import com.ruoyi.ibs.list.mapper.NineVFinalInfoOrcMapper;
|
||||||
import com.ruoyi.ibs.list.mapper.SignedProductsMapper;
|
import com.ruoyi.ibs.list.mapper.SignedProductsMapper;
|
||||||
import com.ruoyi.ibs.list.service.ICustInfoBusinessService;
|
import com.ruoyi.ibs.list.service.ICustInfoBusinessService;
|
||||||
import com.ruoyi.system.mapper.SysIndustryMapper;
|
import com.ruoyi.system.mapper.SysIndustryMapper;
|
||||||
@@ -75,6 +77,12 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FamilyMembersMapper familyMembersMapper;
|
private FamilyMembersMapper familyMembersMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Ent9vPortraitOrcMapper ent9vPortraitOrcMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NineVFinalInfoOrcMapper nineVFinalInfoOrcMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CustMapMapper custMapMapper;
|
private CustMapMapper custMapMapper;
|
||||||
|
|
||||||
@@ -138,6 +146,17 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
|
|||||||
//查询手动标签列表
|
//查询手动标签列表
|
||||||
List<CustManualTagVO> tagCreateVos = familyMembersMapper.selectManualTag(params);
|
List<CustManualTagVO> tagCreateVos = familyMembersMapper.selectManualTag(params);
|
||||||
custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos));
|
custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos));
|
||||||
|
|
||||||
|
// 仅当登录机构为825时,查询九维画像数据
|
||||||
|
if ("825".equals(getHeadId())) {
|
||||||
|
LambdaQueryWrapper<Ent9vPortraitOrc> portraitWrapper = new LambdaQueryWrapper<>();
|
||||||
|
portraitWrapper.eq(Ent9vPortraitOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
|
||||||
|
custInfoBusinessVo.setEnt9vPortrait(ent9vPortraitOrcMapper.selectOne(portraitWrapper));
|
||||||
|
|
||||||
|
LambdaQueryWrapper<NineVFinalInfoOrc> finalInfoWrapper = new LambdaQueryWrapper<>();
|
||||||
|
finalInfoWrapper.eq(NineVFinalInfoOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
|
||||||
|
custInfoBusinessVo.setNineVFinalInfo(nineVFinalInfoOrcMapper.selectOne(finalInfoWrapper));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return custInfoBusinessVo;
|
return custInfoBusinessVo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2178,6 +2178,23 @@ public class SysCampaignServiceImpl implements ISysCampaignService
|
|||||||
return sysCampaignMapper.selectVisitInfoList(visitInfoDTO);
|
return sysCampaignMapper.selectVisitInfoList(visitInfoDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO) {
|
||||||
|
if (updateDTO == null || updateDTO.getId() == null) {
|
||||||
|
throw new ServiceException("走访记录ID不能为空");
|
||||||
|
}
|
||||||
|
updateDTO.setUserName(SecurityUtils.getUsername());
|
||||||
|
updateDTO.setUserRole(SecurityUtils.userRole());
|
||||||
|
updateDTO.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
|
||||||
|
updateDTO.setSource(StringUtils.trimToNull(updateDTO.getSource()));
|
||||||
|
updateDTO.setIntentionProductValue(buildIntentionProductValue(updateDTO.getFeedbackItems()));
|
||||||
|
int rows = sysCampaignMapper.updateVisitInfoFeedback(updateDTO);
|
||||||
|
if (rows <= 0) {
|
||||||
|
throw new ServiceException("走访记录不存在或无权限修改");
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int deleteSysCampaign(String campaignId) {
|
public int deleteSysCampaign(String campaignId) {
|
||||||
SysCampaign sysCampaign = sysCampaignMapper.selectSysCampaignByCampaignId(campaignId);
|
SysCampaign sysCampaign = sysCampaignMapper.selectSysCampaignByCampaignId(campaignId);
|
||||||
@@ -2186,4 +2203,28 @@ public class SysCampaignServiceImpl implements ISysCampaignService
|
|||||||
}
|
}
|
||||||
return sysCampaignMapper.deleteSysCampaignByCampaignId(campaignId);
|
return sysCampaignMapper.deleteSysCampaignByCampaignId(campaignId);
|
||||||
}
|
}
|
||||||
|
private String buildIntentionProductValue(List<VisitFeedbackItemDTO> feedbackItems) {
|
||||||
|
if (feedbackItems == null || feedbackItems.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String joinedValue = feedbackItems.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(item -> {
|
||||||
|
String feedbackType = StringUtils.trimToNull(item.getFeedbackType());
|
||||||
|
List<String> products = Optional.ofNullable(item.getProducts())
|
||||||
|
.orElse(Collections.emptyList())
|
||||||
|
.stream()
|
||||||
|
.map(StringUtils::trimToNull)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (feedbackType == null || products.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return feedbackType + ":" + String.join(",", products);
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
return StringUtils.trimToNull(joinedValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,4 +131,15 @@ public class WorkRecordController extends BaseController {
|
|||||||
return toAjax(workRecordService.updateReadTime(id));
|
return toAjax(workRecordService.updateReadTime(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有预警类型
|
||||||
|
*/
|
||||||
|
@GetMapping("/alter/types")
|
||||||
|
@Log(title = "工作台-查询预警类型")
|
||||||
|
@ApiOperation("查询所有预警类型")
|
||||||
|
public AjaxResult getAlterTypes() {
|
||||||
|
List<String> types = workRecordService.getAlterTypes();
|
||||||
|
return AjaxResult.success(types);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,4 +112,12 @@ public interface WorkRecordMapper extends BaseMapper<WorkRecord> {
|
|||||||
List<AlterConfigVO> selectAlterConfigList(String alterType);
|
List<AlterConfigVO> selectAlterConfigList(String alterType);
|
||||||
|
|
||||||
int updateAlterConfig(AlterConfig alterConfig);
|
int updateAlterConfig(AlterConfig alterConfig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有预警类型
|
||||||
|
*
|
||||||
|
* @param headId 总部ID(部门ID前三位)
|
||||||
|
* @return 预警类型列表
|
||||||
|
*/
|
||||||
|
List<String> selectAlterTypes(@Param("headId") String headId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,4 +85,11 @@ public interface WorkRecordService{
|
|||||||
int updateReadTime(Long id);
|
int updateReadTime(Long id);
|
||||||
|
|
||||||
AlterCountVO getAlterCount(Date reportTime);
|
AlterCountVO getAlterCount(Date reportTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有预警类型
|
||||||
|
*
|
||||||
|
* @return 预警类型列表
|
||||||
|
*/
|
||||||
|
List<String> getAlterTypes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.stream.IntStream;
|
|||||||
|
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.ruoyi.common.core.page.TableDataPageInfo;
|
import com.ruoyi.common.core.page.TableDataPageInfo;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
import com.ruoyi.common.utils.PageUtils;
|
import com.ruoyi.common.utils.PageUtils;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
|
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
|
||||||
@@ -39,6 +40,8 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@Service
|
@Service
|
||||||
public class WorkRecordServiceImpl implements WorkRecordService {
|
public class WorkRecordServiceImpl implements WorkRecordService {
|
||||||
|
|
||||||
|
private final static String alterTypesRedisKey = "work:record:alter:types";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WorkRecordMapper workRecordMapper;
|
private WorkRecordMapper workRecordMapper;
|
||||||
|
|
||||||
@@ -48,6 +51,9 @@ public class WorkRecordServiceImpl implements WorkRecordService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ISysDeptService sysDeptService;
|
private ISysDeptService sysDeptService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询我的工作清单
|
* 查询我的工作清单
|
||||||
*
|
*
|
||||||
@@ -253,6 +259,19 @@ public class WorkRecordServiceImpl implements WorkRecordService {
|
|||||||
return alterCountVO;
|
return alterCountVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAlterTypes() {
|
||||||
|
String headId = SecurityUtils.getHeadId();
|
||||||
|
String cacheKey = alterTypesRedisKey + ":" + headId;
|
||||||
|
List<String> cachedTypes = redisCache.getCacheObject(cacheKey);
|
||||||
|
if (cachedTypes != null) {
|
||||||
|
return cachedTypes;
|
||||||
|
}
|
||||||
|
List<String> types = workRecordMapper.selectAlterTypes(headId);
|
||||||
|
redisCache.setCacheObjectToEndDay(cacheKey, types);
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前登录用户权限范围内所有post_ids
|
* 获取当前登录用户权限范围内所有post_ids
|
||||||
* @return
|
* @return
|
||||||
|
|||||||
@@ -857,6 +857,28 @@
|
|||||||
select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode}
|
select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectExistingSocialCreditCodes" resultType="java.lang.String">
|
||||||
|
select social_credit_code
|
||||||
|
from cust_info_business_${deptCode}
|
||||||
|
where social_credit_code in
|
||||||
|
<foreach item="socialCreditCode" collection="socialCreditCodes" open="(" separator="," close=")">
|
||||||
|
#{socialCreditCode}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<update id="batchUpdateCustLevelBySocialCreditCode">
|
||||||
|
update cust_info_business_${deptCode}
|
||||||
|
set cust_level = case social_credit_code
|
||||||
|
<foreach item="item" collection="list">
|
||||||
|
when #{item.socialCreditCode} then #{item.custLevel}
|
||||||
|
</foreach>
|
||||||
|
end
|
||||||
|
where social_credit_code in
|
||||||
|
<foreach item="item" collection="list" open="(" separator="," close=")">
|
||||||
|
#{item.socialCreditCode}
|
||||||
|
</foreach>
|
||||||
|
</update>
|
||||||
|
|
||||||
<insert id="insertCustomersToBusinessByScCode" parameterType="java.util.List">
|
<insert id="insertCustomersToBusinessByScCode" parameterType="java.util.List">
|
||||||
INSERT INTO cust_info_business
|
INSERT INTO cust_info_business
|
||||||
(cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit)
|
(cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit)
|
||||||
@@ -866,4 +888,4 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and status = #{status}
|
and status = #{status}
|
||||||
</if>
|
</if>
|
||||||
<if test="alterType != null and alterType != ''">
|
<if test="alterType != null and alterType != ''">
|
||||||
and alter_type LIKE CONCAT('%', #{alterType}, '%')
|
and alter_type = #{alterType}
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
order by create_time desc, status asc
|
order by create_time desc, status asc
|
||||||
@@ -232,7 +232,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and wr.status = #{status}
|
and wr.status = #{status}
|
||||||
</if>
|
</if>
|
||||||
<if test="alterType != null and alterType != ''">
|
<if test="alterType != null and alterType != ''">
|
||||||
and wr.alter_type LIKE CONCAT('%', #{alterType}, '%')
|
and wr.alter_type = #{alterType}
|
||||||
</if>
|
</if>
|
||||||
<!-- "走访异常提醒"类型直接通过用户名匹配,其他类型按角色权限处理 -->
|
<!-- "走访异常提醒"类型直接通过用户名匹配,其他类型按角色权限处理 -->
|
||||||
and ((wr.alter_type = '走访异常提醒' and wr.user_name = #{username}) or
|
and ((wr.alter_type = '走访异常提醒' and wr.user_name = #{username}) or
|
||||||
@@ -268,4 +268,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<select id="selectAlterTypes" resultType="string">
|
||||||
|
select distinct alter_type
|
||||||
|
from work_record
|
||||||
|
where is_alter = 1
|
||||||
|
and alter_type is not null
|
||||||
|
and alter_type != ''
|
||||||
|
and left(user_name, 3) = #{headId}
|
||||||
|
order by alter_type
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -505,4 +505,26 @@
|
|||||||
where manager_id is not null
|
where manager_id is not null
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据网格类型和总行ID查询客户经理列表 -->
|
||||||
|
<select id="getManagerListByGridType" resultType="java.util.HashMap">
|
||||||
|
select distinct user_name as userName, nick_name as nickName
|
||||||
|
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
|
||||||
|
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}
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -55,8 +55,17 @@
|
|||||||
<if test="custName != null and custName != ''">AND b.cust_name like concat('%', #{custName}, '%')</if>
|
<if test="custName != null and custName != ''">AND b.cust_name like concat('%', #{custName}, '%')</if>
|
||||||
<if test="custType != null and custType != ''">AND b.cust_type = #{custType}</if>
|
<if test="custType != null and custType != ''">AND b.cust_type = #{custType}</if>
|
||||||
</select>
|
</select>
|
||||||
<select id="">
|
|
||||||
|
|
||||||
|
<!-- 根据绘制网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器) -->
|
||||||
|
<select id="selectCustByDrawGridId" resultType="com.ruoyi.ibs.grid.domain.entity.RegionCustUser">
|
||||||
|
SELECT DISTINCT
|
||||||
|
sc.cust_id,
|
||||||
|
sc.cust_name,
|
||||||
|
sc.cust_type
|
||||||
|
FROM grid_draw_shape_relate sr
|
||||||
|
INNER JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
|
||||||
|
WHERE sr.grid_id = #{gridId}
|
||||||
|
AND sr.delete_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -121,4 +121,30 @@
|
|||||||
WHERE sec_grid_id = #{gridId}
|
WHERE sec_grid_id = #{gridId}
|
||||||
AND cust_type = #{custType}
|
AND cust_type = #{custType}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据网格ID列表查询所有客户(一级或二级网格) -->
|
||||||
|
<select id="selectAllCustByGridIds" resultType="RegionCustUser">
|
||||||
|
SELECT DISTINCT cust_id, cust_name, cust_type
|
||||||
|
FROM grid_region_cust_user_${headId}
|
||||||
|
WHERE (top_grid_id IN
|
||||||
|
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
|
||||||
|
#{gridId}
|
||||||
|
</foreach>
|
||||||
|
OR sec_grid_id IN
|
||||||
|
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
|
||||||
|
#{gridId}
|
||||||
|
</foreach>)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据绘制网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器) -->
|
||||||
|
<select id="selectCustByDrawGridId" resultType="RegionCustUser">
|
||||||
|
SELECT DISTINCT
|
||||||
|
sc.cust_id,
|
||||||
|
sc.cust_name,
|
||||||
|
sc.cust_type
|
||||||
|
FROM grid_draw_shape_relate sr
|
||||||
|
LEFT JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
|
||||||
|
WHERE sr.grid_id = #{gridId}
|
||||||
|
AND sr.delete_flag = '0'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -339,4 +339,26 @@
|
|||||||
<select id="getSecGridIdByTopGridId" resultType="Long">
|
<select id="getSecGridIdByTopGridId" resultType="Long">
|
||||||
select grid_id from grid_region_grid where parent_grid_id = #{gridId} and grid_level = '2' and delete_flag = '0'
|
select grid_id from grid_region_grid where parent_grid_id = #{gridId} and grid_level = '2' and delete_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询地理网格列表(简化版)- 专用于客群创建 -->
|
||||||
|
<select id="getRegionGridListForGroup" parameterType="RegionGridListDTO" resultType="RegionGridGroupVO">
|
||||||
|
select distinct a.grid_id, a.grid_name
|
||||||
|
from grid_region_grid a
|
||||||
|
left join grid_region_user_relate c on c.grid_id = a.grid_id
|
||||||
|
where a.delete_flag = '0'
|
||||||
|
and c.delete_flag = '0'
|
||||||
|
and a.grid_level = #{gridLevel}
|
||||||
|
and (a.dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors))
|
||||||
|
or c.relate_dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors)))
|
||||||
|
<if test="opsDept != null and opsDept != ''">
|
||||||
|
and a.ops_dept = #{opsDept}
|
||||||
|
</if>
|
||||||
|
<if test="gridDutyType != null and gridDutyType != ''">
|
||||||
|
and a.grid_duty_type = #{gridDutyType}
|
||||||
|
</if>
|
||||||
|
<if test="gridName != null and gridName != ''">
|
||||||
|
and a.grid_name like concat('%', #{gridName}, '%')
|
||||||
|
</if>
|
||||||
|
ORDER BY a.grid_name
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -127,6 +127,18 @@
|
|||||||
<result property="remark" column="remark" />
|
<result property="remark" column="remark" />
|
||||||
<result property="custType" column="cust_type" />
|
<result property="custType" column="cust_type" />
|
||||||
<result property="deptName" column="dept_name" />
|
<result property="deptName" column="dept_name" />
|
||||||
|
<result property="interAddr" column="inter_addr" />
|
||||||
|
<result property="colStafName" column="col_staf_name" />
|
||||||
|
<result property="laterNote" column="later_note" />
|
||||||
|
<result property="intentionProductValue" column="intention_product_value" />
|
||||||
|
<result property="feedbackStatus" column="feedback_status" />
|
||||||
|
<result property="sourceOfInterview" column="source_of_interview" />
|
||||||
|
<result property="filename" column="filename" />
|
||||||
|
<result property="outCallStatus" column="out_call_status" />
|
||||||
|
<result property="outCallIntention" column="out_call_intention" />
|
||||||
|
<result property="source" column="source" />
|
||||||
|
<result property="analysisValue" column="analysis_value" />
|
||||||
|
<result property="facility" column="facility" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectSysCampaignVo">
|
<sql id="selectSysCampaignVo">
|
||||||
@@ -1384,12 +1396,15 @@
|
|||||||
<select id="selectVisitInfoList" parameterType="VisitInfoDTO" resultType="VisitInfoVO">
|
<select id="selectVisitInfoList" parameterType="VisitInfoDTO" resultType="VisitInfoVO">
|
||||||
select vi.id,vi.campaign_id,vi.campaign_name,vi.vis_name,vi.vis_id,vi.dept_id,d.dept_name,vi.vis_time,vi.cust_name,vi.cust_type,vi.cust_idc,
|
select vi.id,vi.campaign_id,vi.campaign_name,vi.vis_name,vi.vis_id,vi.dept_id,d.dept_name,vi.vis_time,vi.cust_name,vi.cust_type,vi.cust_idc,
|
||||||
vi.social_credit_code,vi.create_by,vi.create_time,vi.update_by,vi.update_time,vi.remark,vi.sign_in_time,vi.sign_out_time,vi.sign_in_address,
|
vi.social_credit_code,vi.create_by,vi.create_time,vi.update_by,vi.update_time,vi.remark,vi.sign_in_time,vi.sign_out_time,vi.sign_in_address,
|
||||||
vi.sign_out_address,vi.abnormal_visit_tag,vi.abnormal_visit_info,vi.sign_in_coordinate,vi.sign_out_coordinate,vi.is_valid_cust,vi.marketing_way,vi.inter_res
|
vi.sign_out_address,vi.abnormal_visit_tag,vi.abnormal_visit_info,vi.sign_in_coordinate,vi.sign_out_coordinate,vi.is_valid_cust,vi.marketing_way,vi.inter_res,
|
||||||
|
vi.inter_addr,vi.col_staf_name,vi.later_note,vi.intention_product_value,vi.feedback_status,vi.source_of_interview,vi.filename,
|
||||||
|
vi.out_call_status,vi.out_call_intention,vi.source,vi.analysis_value,vi.facility
|
||||||
from visit_info vi
|
from visit_info vi
|
||||||
left join sys_dept d on vi.dept_id = d.dept_id
|
left join sys_dept d on vi.dept_id = d.dept_id
|
||||||
<where>
|
<where>
|
||||||
<if test="custType != null and custType != ''"> and vi.cust_type = #{custType}</if>
|
<if test="custType != null and custType != ''"> and vi.cust_type = #{custType}</if>
|
||||||
<if test="visName != null and visName != ''"> and vi.vis_name like concat('%', #{visName}, '%')</if>
|
<if test="visName != null and visName != ''"> and vi.vis_name like concat('%', #{visName}, '%')</if>
|
||||||
|
<if test="visId != null and visId != ''"> and vi.vis_id = #{visId}</if>
|
||||||
<if test="custIdc != null and custIdc != ''"> and vi.cust_idc = #{custIdc}</if>
|
<if test="custIdc != null and custIdc != ''"> and vi.cust_idc = #{custIdc}</if>
|
||||||
<if test="socialCreditCode != null and socialCreditCode != ''"> and vi.social_credit_code = #{socialCreditCode}</if>
|
<if test="socialCreditCode != null and socialCreditCode != ''"> and vi.social_credit_code = #{socialCreditCode}</if>
|
||||||
<if test="abnormalVisitTag != null and abnormalVisitTag != ''"> and vi.abnormal_visit_tag = #{abnormalVisitTag}</if>
|
<if test="abnormalVisitTag != null and abnormalVisitTag != ''"> and vi.abnormal_visit_tag = #{abnormalVisitTag}</if>
|
||||||
@@ -1410,4 +1425,22 @@
|
|||||||
order by vi.sign_in_time desc
|
order by vi.sign_in_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
<update id="updateVisitInfoFeedback" parameterType="VisitInfoFeedbackUpdateDTO">
|
||||||
|
update visit_info vi
|
||||||
|
left join sys_dept d on vi.dept_id = d.dept_id
|
||||||
|
set vi.source = #{source},
|
||||||
|
vi.intention_product_value = #{intentionProductValue},
|
||||||
|
vi.update_by = #{userName},
|
||||||
|
vi.update_time = sysdate()
|
||||||
|
where vi.id = #{id}
|
||||||
|
<if test="userRole != null">
|
||||||
|
<choose>
|
||||||
|
<when test="userRole == 'manager'"> and vi.vis_id = #{userName} </when>
|
||||||
|
<when test="userRole == 'outlet'"> and d.dept_id = #{deptId} </when>
|
||||||
|
<when test="userRole == 'branch'"> and (d.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when>
|
||||||
|
<when test="userRole in {'head', 'ops', 'public', 'private'}"> and left(d.dept_id,3) = left(#{deptId},3) </when>
|
||||||
|
</choose>
|
||||||
|
</if>
|
||||||
|
</update>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
|
|||||||
7
pom.xml
7
pom.xml
@@ -215,6 +215,13 @@
|
|||||||
<version>${ruoyi.version}</version>
|
<version>${ruoyi.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 数字支行-客群模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ibs-group</artifactId>
|
||||||
|
<version>3.8.8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ spring:
|
|||||||
# 地址
|
# 地址
|
||||||
host: 116.62.17.81
|
host: 116.62.17.81
|
||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 6380
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 0
|
database: 0
|
||||||
# 密码
|
# 密码
|
||||||
|
|||||||
@@ -38,6 +38,10 @@
|
|||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<artifactId>ibs</artifactId>
|
<artifactId>ibs</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ibs-group</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ruoyi.quartz.task;
|
package com.ruoyi.quartz.task;
|
||||||
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.group.service.ICustGroupService;
|
||||||
import com.ruoyi.ibs.cmpm.service.GridCmpmService;
|
import com.ruoyi.ibs.cmpm.service.GridCmpmService;
|
||||||
import com.ruoyi.ibs.dashboard.service.FileOptService;
|
import com.ruoyi.ibs.dashboard.service.FileOptService;
|
||||||
import com.ruoyi.ibs.draw.service.DrawGridCustService;
|
import com.ruoyi.ibs.draw.service.DrawGridCustService;
|
||||||
@@ -47,6 +48,9 @@ public class RyTask
|
|||||||
@Resource
|
@Resource
|
||||||
private AddressAnalyseService addressAnalyseService;
|
private AddressAnalyseService addressAnalyseService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICustGroupService custGroupService;
|
||||||
|
|
||||||
|
|
||||||
public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
|
public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
|
||||||
{
|
{
|
||||||
@@ -111,4 +115,12 @@ public class RyTask
|
|||||||
addressAnalyseService.pointInGeometryScheduled();
|
addressAnalyseService.pointInGeometryScheduled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateDynamicCustGroups() {
|
||||||
|
custGroupService.updateDynamicCustGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAndDisableExpiredGroups() {
|
||||||
|
custGroupService.checkAndDisableExpiredGroups();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ public class SysNotice extends BaseEntity
|
|||||||
/** 公告状态(0正常 1关闭) */
|
/** 公告状态(0正常 1关闭) */
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
/** 可见总行部门ID,多选逗号分隔 */
|
||||||
|
private String deptIds;
|
||||||
|
|
||||||
|
/** 当前登录用户所属总行部门ID,仅用于查询过滤 */
|
||||||
|
private String currentHeadDeptId;
|
||||||
|
|
||||||
public Long getNoticeId()
|
public Long getNoticeId()
|
||||||
{
|
{
|
||||||
return noticeId;
|
return noticeId;
|
||||||
@@ -84,6 +90,26 @@ public class SysNotice extends BaseEntity
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDeptIds()
|
||||||
|
{
|
||||||
|
return deptIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeptIds(String deptIds)
|
||||||
|
{
|
||||||
|
this.deptIds = deptIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentHeadDeptId()
|
||||||
|
{
|
||||||
|
return currentHeadDeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentHeadDeptId(String currentHeadDeptId)
|
||||||
|
{
|
||||||
|
this.currentHeadDeptId = currentHeadDeptId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||||
@@ -92,6 +118,7 @@ public class SysNotice extends BaseEntity
|
|||||||
.append("noticeType", getNoticeType())
|
.append("noticeType", getNoticeType())
|
||||||
.append("noticeContent", getNoticeContent())
|
.append("noticeContent", getNoticeContent())
|
||||||
.append("status", getStatus())
|
.append("status", getStatus())
|
||||||
|
.append("deptIds", getDeptIds())
|
||||||
.append("createBy", getCreateBy())
|
.append("createBy", getCreateBy())
|
||||||
.append("createTime", getCreateTime())
|
.append("createTime", getCreateTime())
|
||||||
.append("updateBy", getUpdateBy())
|
.append("updateBy", getUpdateBy())
|
||||||
|
|||||||
@@ -133,8 +133,13 @@ public class SysDeptServiceImpl implements ISysDeptService
|
|||||||
@Override
|
@Override
|
||||||
public List<TreeSelect> selectDeptTreeListForTopGrid(SysDept dept) {
|
public List<TreeSelect> selectDeptTreeListForTopGrid(SysDept dept) {
|
||||||
List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
|
List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
|
||||||
List<SysDept> branchs = depts.stream().filter(sysDept -> !sysDept.getDeptType().equals("outlet"))
|
if (depts == null || depts.isEmpty()) {
|
||||||
.filter(sysDept -> !sysDept.getDeptType().equals("head"))
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<SysDept> branchs = depts.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(sysDept -> !"outlet".equals(sysDept.getDeptType()))
|
||||||
|
.filter(sysDept -> !"head".equals(sysDept.getDeptType()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return buildDeptTreeSelect(branchs);
|
return buildDeptTreeSelect(branchs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="noticeType" column="notice_type" />
|
<result property="noticeType" column="notice_type" />
|
||||||
<result property="noticeContent" column="notice_content" />
|
<result property="noticeContent" column="notice_content" />
|
||||||
<result property="status" column="status" />
|
<result property="status" column="status" />
|
||||||
|
<result property="deptIds" column="dept_ids" />
|
||||||
<result property="createBy" column="create_by" />
|
<result property="createBy" column="create_by" />
|
||||||
<result property="createTime" column="create_time" />
|
<result property="createTime" column="create_time" />
|
||||||
<result property="updateBy" column="update_by" />
|
<result property="updateBy" column="update_by" />
|
||||||
@@ -18,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectNoticeVo">
|
<sql id="selectNoticeVo">
|
||||||
select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark
|
select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, dept_ids, create_by, create_time, update_by, update_time, remark
|
||||||
from sys_notice
|
from sys_notice
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
@@ -39,6 +40,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="createBy != null and createBy != ''">
|
<if test="createBy != null and createBy != ''">
|
||||||
AND create_by like concat('%', #{createBy}, '%')
|
AND create_by like concat('%', #{createBy}, '%')
|
||||||
</if>
|
</if>
|
||||||
|
<if test="currentHeadDeptId != null and currentHeadDeptId != ''">
|
||||||
|
AND (dept_ids is null or dept_ids = '' or find_in_set(#{currentHeadDeptId}, dept_ids))
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -48,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="noticeType != null and noticeType != '' ">notice_type, </if>
|
<if test="noticeType != null and noticeType != '' ">notice_type, </if>
|
||||||
<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
|
<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
|
||||||
<if test="status != null and status != '' ">status, </if>
|
<if test="status != null and status != '' ">status, </if>
|
||||||
|
<if test="deptIds != null">dept_ids, </if>
|
||||||
<if test="remark != null and remark != ''">remark,</if>
|
<if test="remark != null and remark != ''">remark,</if>
|
||||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||||
create_time
|
create_time
|
||||||
@@ -56,6 +61,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
|
<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
|
||||||
<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
|
<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
|
||||||
<if test="status != null and status != ''">#{status}, </if>
|
<if test="status != null and status != ''">#{status}, </if>
|
||||||
|
<if test="deptIds != null">#{deptIds}, </if>
|
||||||
<if test="remark != null and remark != ''">#{remark},</if>
|
<if test="remark != null and remark != ''">#{remark},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
sysdate()
|
sysdate()
|
||||||
@@ -69,6 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if>
|
<if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if>
|
||||||
<if test="noticeContent != null">notice_content = #{noticeContent}, </if>
|
<if test="noticeContent != null">notice_content = #{noticeContent}, </if>
|
||||||
<if test="status != null and status != ''">status = #{status}, </if>
|
<if test="status != null and status != ''">status = #{status}, </if>
|
||||||
|
<if test="deptIds != null">dept_ids = #{deptIds}, </if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = sysdate()
|
update_time = sysdate()
|
||||||
</set>
|
</set>
|
||||||
@@ -86,4 +93,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
168
ruoyi-ui/CLAUDE.md
Normal file
168
ruoyi-ui/CLAUDE.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
**数字支行辅助管理系统** - 基于 RuoYi-Vue 框架的银行客户网格化管理平台。
|
||||||
|
|
||||||
|
这是一个全栈项目:
|
||||||
|
- **前端**: `ruoyi-ui/` - Vue 2.6 + Element UI
|
||||||
|
- **后端**: `../ibs/` - Java Spring Boot (若依框架)
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
### 开发
|
||||||
|
```bash
|
||||||
|
# 安装依赖(建议使用国内镜像)
|
||||||
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 启动开发服务器 (默认端口80)
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Node.js 版本兼容性问题的启动方式
|
||||||
|
npm run dev_t
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构建
|
||||||
|
```bash
|
||||||
|
# 构建生产环境
|
||||||
|
npm run build:prod
|
||||||
|
|
||||||
|
# 构建测试环境
|
||||||
|
npm run build:stage
|
||||||
|
|
||||||
|
# 构建预发布环境
|
||||||
|
npm run build:pre
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码质量
|
||||||
|
```bash
|
||||||
|
# ESLint 检查
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock 服务
|
||||||
|
```bash
|
||||||
|
# 启动 Mock 服务器
|
||||||
|
npm run mock
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目架构
|
||||||
|
|
||||||
|
### 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ruoyi-ui/
|
||||||
|
├── src/
|
||||||
|
│ ├── api/ # API 接口层,按业务模块分组
|
||||||
|
│ ├── assets/ # 静态资源(图片、样式、图标SVG)
|
||||||
|
│ ├── components/ # 全局通用组件
|
||||||
|
│ ├── directive/ # Vue 自定义指令
|
||||||
|
│ ├── layout/ # 布局组件(侧边栏、头部等)
|
||||||
|
│ ├── map/ # 地图相关工具/配置
|
||||||
|
│ ├── plugins/ # 插件配置
|
||||||
|
│ ├── router/ # 路由配置
|
||||||
|
│ ├── store/ # Vuex 状态管理
|
||||||
|
│ ├── utils/ # 工具函数
|
||||||
|
│ ├── views/ # 页面组件
|
||||||
|
│ ├── App.vue # 根组件
|
||||||
|
│ ├── main.js # 入口文件
|
||||||
|
│ └── permission.js # 路由权限控制
|
||||||
|
├── public/
|
||||||
|
│ ├── baidu/ # 百度地图静态资源
|
||||||
|
│ └── index.html
|
||||||
|
├── mock/ # Mock 数据
|
||||||
|
├── .env.development # 开发环境配置
|
||||||
|
├── .env.production # 生产环境配置
|
||||||
|
└── .env.staging # 测试环境配置
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心业务模块
|
||||||
|
|
||||||
|
| 目录 | 功能 |
|
||||||
|
|------|------|
|
||||||
|
| `views/grid/` | 网格管理(创建、列表、地图、走访、绩效) |
|
||||||
|
| `views/customer/` | 客户管理(360视图、建档、客群、标签) |
|
||||||
|
| `views/system/` | 系统管理(用户、角色、菜单、字典) |
|
||||||
|
| `views/monitor/` | 系统监控(日志、在线用户、服务监控) |
|
||||||
|
| `views/configure/` | 配置管理(级别配置、参数设置、模板) |
|
||||||
|
| `views/approveCenter/` | 审批中心 |
|
||||||
|
| `views/gridSearch/` | 网格搜索(管户、绩效、公海池) |
|
||||||
|
|
||||||
|
### API 层组织
|
||||||
|
|
||||||
|
API 请求按业务模块组织在 `src/api/` 目录:
|
||||||
|
- 每个模块导出具体的请求函数
|
||||||
|
- 使用 `src/utils/request.js` 中的 axios 实例
|
||||||
|
- 默认添加 Bearer Token 认证
|
||||||
|
- 统一错误处理(401/500/601等状态码)
|
||||||
|
|
||||||
|
### 状态管理 (Vuex)
|
||||||
|
|
||||||
|
Store 模块位于 `src/store/modules/`:
|
||||||
|
- `app` - 应用状态(侧边栏、设备类型)
|
||||||
|
- `user` - 用户信息
|
||||||
|
- `permission` - 权限路由
|
||||||
|
- `settings` - 系统设置
|
||||||
|
- `dict` - 字典数据
|
||||||
|
- `tagsView` - 标签页视图
|
||||||
|
- `featuredAreas` - 特色区域
|
||||||
|
- `rate` - 汇率相关
|
||||||
|
|
||||||
|
使用 `vuex-persistedstate` 将 settings 持久化到 localStorage。
|
||||||
|
|
||||||
|
### 路由系统
|
||||||
|
|
||||||
|
- 路由分为 `constantRoutes`(静态路由)和 `dynamicRoutes`(动态路由)
|
||||||
|
- 动态路由基于用户权限(permissions)动态加载
|
||||||
|
- 使用 `src/permission.js` 进行路由守卫和权限控制
|
||||||
|
- 支持嵌套路由和路由元信息配置
|
||||||
|
|
||||||
|
### 百度地图集成
|
||||||
|
|
||||||
|
项目深度集成百度地图 API:
|
||||||
|
- 静态资源位于 `public/baidu/`
|
||||||
|
- 地图相关 API 在 `src/api/grid/` 下
|
||||||
|
- 支持 WebGL 渲染、绘制管理器、热力图等
|
||||||
|
|
||||||
|
## 环境变量配置
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `VUE_APP_BASE_API` | 后端 API 基础路径 |
|
||||||
|
| `VUE_APP_MOCK_API` | Mock API 路径 |
|
||||||
|
| `VUE_APP_MOCK` | 是否启用 Mock |
|
||||||
|
| `VUE_APP_STAGE_URL` | 测试环境地址 |
|
||||||
|
| `VUE_APP_BAIDU_URL` | 百度地图服务地址 |
|
||||||
|
| `VUE_APP_SHARP_URL` | SHARP 服务地址 |
|
||||||
|
|
||||||
|
## 代码规范
|
||||||
|
|
||||||
|
- ESLint 配置基于 `plugin:vue/recommended`
|
||||||
|
- 使用单引号、2空格缩进
|
||||||
|
- 组件名使用 PascalCase
|
||||||
|
- pre-commit 钩子自动执行 lint-staged
|
||||||
|
|
||||||
|
## 全局组件
|
||||||
|
|
||||||
|
以下组件已全局注册,可直接使用:
|
||||||
|
- `Pagination` - 分页组件
|
||||||
|
- `RightToolbar` - 表格工具栏
|
||||||
|
- `Editor` - 富文本编辑器
|
||||||
|
- `FileUpload` - 文件上传
|
||||||
|
- `ImageUpload` - 图片上传
|
||||||
|
- `ImagePreview` - 图片预览
|
||||||
|
- `DictTag` - 字典标签
|
||||||
|
- `treeselect` - 树形选择器
|
||||||
|
- `DownloadBtn` - 下载按钮
|
||||||
|
|
||||||
|
## 全局方法
|
||||||
|
|
||||||
|
通过 `Vue.prototype` 挂载的全局方法:
|
||||||
|
- `getDicts()` - 获取字典数据
|
||||||
|
- `getConfigKey()` - 获取配置项
|
||||||
|
- `parseTime()` - 时间格式化
|
||||||
|
- `resetForm()` - 重置表单
|
||||||
|
- `download()` - 文件下载
|
||||||
|
- `handleTree()` - 处理树形数据
|
||||||
@@ -287,6 +287,15 @@ export function getFeaturegrid(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取网格列表(用于客群创建,简化查询)
|
||||||
|
export function getSimpleGridList(data) {
|
||||||
|
return request({
|
||||||
|
url: `/draw/grid/simpleList`,
|
||||||
|
method: 'get',
|
||||||
|
params: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 删除网格
|
// 删除网格
|
||||||
export function featureGridDelete(data) {
|
export function featureGridDelete(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
@@ -91,4 +91,20 @@ export function uploadTag(data) {
|
|||||||
data: data,
|
data: data,
|
||||||
isUpload: true
|
isUpload: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function importBusinessCustLevelAsync(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/custBaseInfo/importBusinessCustLevelAsync',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
isUpload: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBusinessCustLevelImportStatus(taskId) {
|
||||||
|
return request({
|
||||||
|
url: `/system/custBaseInfo/importBusinessCustLevelStatus/${taskId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
137
ruoyi-ui/src/api/group/custGroup.js
Normal file
137
ruoyi-ui/src/api/group/custGroup.js
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询客群列表
|
||||||
|
export function listCustGroup(query) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取客群详情
|
||||||
|
export function getCustGroup(id) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步创建客群(网格导入)
|
||||||
|
export function createCustGroupByGrid(data) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/createByGrid',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步创建客群(模板导入)
|
||||||
|
export function createCustGroupByTemplate(data) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/createByTemplate',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-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({
|
||||||
|
url: '/group/cust/updateByTemplate',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮询客群创建状态
|
||||||
|
export function getCreateStatus(id) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/createStatus/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户信息模板下载
|
||||||
|
export function downloadTemplate() {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/download',
|
||||||
|
method: 'post',
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除客群
|
||||||
|
export function deleteCustGroup(idList) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: idList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动移除客群客户
|
||||||
|
export function removeMembers(groupId, memberIds) {
|
||||||
|
return request({
|
||||||
|
url: '/group/cust/removeMembers',
|
||||||
|
method: 'post',
|
||||||
|
params: { groupId: groupId },
|
||||||
|
data: 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({
|
||||||
|
url: '/group/member/list/' + groupId,
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询地理网格列表(专用于客群创建)
|
||||||
|
export function getRegionGridListForGroup(query) {
|
||||||
|
return request({
|
||||||
|
url: '/grid/region/groupList',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -257,4 +257,12 @@ export function warningCardNum(query) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有预警类型
|
||||||
|
export function getAlterTypes() {
|
||||||
|
return request({
|
||||||
|
url: '/work/record/alter/types',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -41,4 +41,12 @@ export function delNotice(noticeId) {
|
|||||||
url: '/system/notice/' + noticeId,
|
url: '/system/notice/' + noticeId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询总行列表
|
||||||
|
export function getHeadList() {
|
||||||
|
return request({
|
||||||
|
url: '/system/dept/headList',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,4 +6,12 @@ export function getPADVisitRecord(query) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updatePADVisitFeedback(data) {
|
||||||
|
return request({
|
||||||
|
url: `/system/campaign/updateVisitInfoFeedback`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -192,6 +192,10 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getCenterList();
|
this.getCenterList();
|
||||||
|
window.addEventListener('notice-center-refresh', this.getCenterList);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('notice-center-refresh', this.getCenterList);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openModal() {
|
openModal() {
|
||||||
|
|||||||
@@ -227,6 +227,17 @@ export const constantRoutes = [
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/group/custGroup/detail',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
children: [{
|
||||||
|
path: '/group/custGroup/detail',
|
||||||
|
name: 'CustGroupDetail',
|
||||||
|
meta: { title: '客群详情', activeMenu: '/group/custGroup' },
|
||||||
|
component: () => import('@/views/group/custGroup/detail')
|
||||||
|
}]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/checklist/customerlist',
|
path: '/checklist/customerlist',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const user = {
|
|||||||
permissions: [],
|
permissions: [],
|
||||||
nickName: '',
|
nickName: '',
|
||||||
userName: '',
|
userName: '',
|
||||||
|
deptId: '',
|
||||||
groupErr:{},
|
groupErr:{},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1024,6 +1024,77 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
|
||||||
|
<el-collapse-item v-if="is825" name="11">
|
||||||
|
<template slot="title">
|
||||||
|
<p class="common-title">企业九维数据</p>
|
||||||
|
</template>
|
||||||
|
<div v-if="ent9vPortrait || nineVFinalInfo" class="nine-dimension-data">
|
||||||
|
<!-- 企业九维画像 -->
|
||||||
|
<div v-if="ent9vPortrait" class="nine-dimension-section">
|
||||||
|
<h4 class="section-title">1.企业九维画像</h4>
|
||||||
|
<div class="nine-dimension-chart-container">
|
||||||
|
<div class="radar-chart-wrapper">
|
||||||
|
<div ref="nineDimensionRadar" class="nine-dimension-radar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="nine-dimension-summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">综合评分</span>
|
||||||
|
<div class="summary-content">
|
||||||
|
<span class="summary-value">{{ formatPortraitDisplay(ent9vPortrait.scoreAll) }}</span>
|
||||||
|
<span class="summary-unit">分</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">总分排名</span>
|
||||||
|
<div class="summary-content">
|
||||||
|
<span class="summary-value is-rank">{{ formatPortraitDisplay(ent9vPortrait.scoreAllRank) }}</span>
|
||||||
|
<span class="summary-unit">名</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 九维细项指标 -->
|
||||||
|
<div v-if="nineVFinalInfo" class="nine-dimension-section">
|
||||||
|
<h4 class="section-title">2.九维细项指标</h4>
|
||||||
|
<div class="nine-dimension-detail-table">
|
||||||
|
<table class="detail-table">
|
||||||
|
<tbody>
|
||||||
|
<template v-for="group in nineDimensionDetailGroups">
|
||||||
|
<tr
|
||||||
|
v-for="(row, rowIndex) in group.rows"
|
||||||
|
:key="`${group.title}-${rowIndex}`"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
v-if="rowIndex === 0"
|
||||||
|
class="category-cell"
|
||||||
|
:rowspan="group.rows.length"
|
||||||
|
>
|
||||||
|
{{ group.title }}
|
||||||
|
</td>
|
||||||
|
<template v-for="metric in row">
|
||||||
|
<td :key="`${group.title}-${metric.key}-label`" class="metric-label-cell">
|
||||||
|
{{ metric.label }}
|
||||||
|
</td>
|
||||||
|
<td :key="`${group.title}-${metric.key}-value`" class="metric-value-cell">
|
||||||
|
{{ formatNineDimensionValue(metric) }}
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
<template v-if="row.length === 1">
|
||||||
|
<td class="metric-label-cell is-empty"></td>
|
||||||
|
<td class="metric-value-cell is-empty"></td>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="暂无企业九维数据"></el-empty>
|
||||||
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="该客户尚未建档,请先进行营销建档"
|
title="该客户尚未建档,请先进行营销建档"
|
||||||
@@ -1215,7 +1286,120 @@ import { downloadFiles } from '@/utils'
|
|||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import { cloneDeep, isEmpty } from 'lodash'
|
import { cloneDeep, isEmpty } from 'lodash'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
import Custom from '../custom.vue'
|
import Custom from '../custom.vue'
|
||||||
|
|
||||||
|
const NINE_DIMENSION_DETAIL_CONFIG = [
|
||||||
|
{
|
||||||
|
title: '企业合规经营模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score11', label: '是否存在经营异常名录信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score12', label: '是否存在严重违法失信企业名单信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score13', label: '企业环境行为信用等级是否为“E”或“D”', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score14', label: '是否存在税务重大税收违法黑名单信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score15', label: '是否存在拖欠工资黑名单', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score16', label: '是否存在工商吊销企业信息', valueMap: { '1': '是', '2': '否' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '企业风险准入模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score21', label: '是否存在注销企业信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score22', label: '是否存在执行案件信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score23', label: '是否存在查封信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score24', label: '是否存在单位未履行生效裁判信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score25', label: '是否存在企业破产清算信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score26', label: '是否失信被执行人', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score27', label: '是否为诉讼案件被告', valueMap: { '1': '是', '2': '否' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '高管信用评价模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score31', label: '是否存在查封信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score32', label: '是否存在执行案件信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score33', label: '是否存在个人未履行生效裁判信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score34', label: '是否存在拖欠工资黑名单', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score35', label: '是否失信被执行人', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score36', label: '是否存在刑事案件被告人生效判决信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score37', label: '是否为诉讼案件被告', valueMap: { '1': '是', '2': '否' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '股东信用评价模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score41', label: '是否存在查封信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score42', label: '是否存在执行案件信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score43', label: '是否存在个人未履行生效裁判信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score44', label: '是否存在拖欠工资黑名单', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score45', label: '是否失信被执行人', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score46', label: '是否存在刑事案件被告人生效判决信息', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score47', label: '是否为诉讼案件被告', valueMap: { '1': '是', '2': '否' } },
|
||||||
|
{ key: 'score48', label: '是否存在企业未履行生效裁判信息', valueMap: { '1': '是', '2': '否' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '企业社会贡献模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score51', label: '前12个月纳税金额', valueMap: { '1': '0', '2': '0-5(含)万元', '3': '5-10(含)万元', '4': '10-50(含)万元', '5': '50-100(含)万元', '6': '100-500(含)万元', '7': '500-1000(含)万元', '8': '1000万元以上' } },
|
||||||
|
{ key: 'score52', label: '纳税等级', valueMap: { '1': 'A', '2': 'B', '3': 'M', '4': 'C、D', '5': '未评' } },
|
||||||
|
{ key: 'score53', label: '缴纳社保人数', valueMap: { '1': '0', '2': '3(含)人以内', '3': '3-10(含)人', '4': '10-30(含)人', '5': '30-50(含)人', '6': '50-100(含)人', '7': '100-500(含)人', '8': '500人以上' } },
|
||||||
|
{ key: 'score54', label: '公积金缴纳人数', valueMap: { '1': '0', '2': '3(含)人以内', '3': '3-10(含)人', '4': '10-30(含)人', '5': '30-50(含)人', '6': '50-100(含)人', '7': '100-500(含)人', '8': '500人以上' } },
|
||||||
|
{ key: 'score55', label: '是否为出口退税生产清单企业', valueMap: { '1': '是', '2': '否' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '企业稳定经营模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score61', label: '市场主体经营年限', valueMap: { '1': '1年以内', '2': '1-3年', '3': '3-5年', '4': '5-10年', '5': '10年以上' } },
|
||||||
|
{ key: 'score62', label: '股东(或发起人)或投资人信息认缴出资人数', valueMap: { '1': '1人独资', '2': '2-5人', '3': '5人以上' } },
|
||||||
|
{ key: 'score63', label: '股东(或发起人)或投资人信息最大股东持股占比', valueMap: { '1': '10%以内(含)', '2': '10%-30%(含)', '3': '30%-50%(含)', '4': '50%-66.6%(含)', '5': '66.6%以上' } },
|
||||||
|
{ key: 'score64', label: '近三年法定代表人变更次数', valueMap: { '1': '0次', '2': '1次', '3': '2次', '4': '3次及以上' } },
|
||||||
|
{ key: 'score65', label: '近三年股东变更次数', valueMap: { '1': '0次', '2': '1次', '3': '2次', '4': '3次及以上' } },
|
||||||
|
{ key: 'score66', label: '近三年经营范围变更次数', valueMap: { '1': '0次', '2': '1次', '3': '2次', '4': '3次及以上' } },
|
||||||
|
{ key: 'score67', label: '近三年经营地址变更次数', valueMap: { '1': '0次', '2': '1次', '3': '2次', '4': '3次及以上' } },
|
||||||
|
{ key: 'score68', label: '近三年缴税年数', valueMap: { '1': '0', '2': '1年', '3': '2年', '4': '3年' } },
|
||||||
|
{ key: 'score69', label: '法人户籍', valueMap: { '1': '户籍本地、原籍本地', '2': '户籍本地、原籍浙江其他地区', '3': '户籍本地、原籍未浙江', '4': '非本地户籍、浙江籍', '5': '非本地户籍、未浙江籍' } },
|
||||||
|
{ key: 'score610', label: '法人婚姻状况', valueMap: { '1': '未婚', '2': '已婚', '3': '丧偶', '4': '离婚' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '企业经营能力模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score71', label: '上年增值税金额', valueMap: { '1': '0', '2': '0-5(含)万元', '3': '5-10(含)万元', '4': '10-30(含)万元', '5': '30-50(含)万元', '6': '50-100(含)万元', '7': '100-500(含)万元', '8': '500-1000(含)万元', '9': '1000万元以上' } },
|
||||||
|
{ key: 'score72', label: '今年增值税同比变动', valueMap: { '1': '0及以下', '2': '0-5%(含)', '3': '5%-10%(含)', '4': '10%-20%(含)', '5': '20%以上' } },
|
||||||
|
{ key: 'score73', label: '上年企业所得税金额', valueMap: { '1': '0', '2': '0-5(含)万元', '3': '5-10(含)万元', '4': '10-30(含)万元', '5': '30-50(含)万元', '6': '50-100(含)万元', '7': '100-500(含)万元', '8': '500-1000(含)万元', '9': '1000万元以上' } },
|
||||||
|
{ key: 'score74', label: '今年所得税同比变动', valueMap: { '1': '0及以下', '2': '0-5%(含)', '3': '5%-10%(含)', '4': '10%-20%(含)', '5': '20%以上' } },
|
||||||
|
{ key: 'score75', label: '缴纳社保人数同比变动', valueMap: { '1': '0及以下', '2': '0-5%(含)', '3': '5%-10%(含)', '4': '10%-20%(含)', '5': '20%以上' } },
|
||||||
|
{ key: 'score76', label: '公积金缴纳人数同比变动', valueMap: { '1': '0及以下', '2': '0-5%(含)', '3': '5%-10%(含)', '4': '10%-20%(含)', '5': '20%以上' } },
|
||||||
|
{ key: 'score77', label: '当年纳税金额同比变动', valueMap: { '1': '0及以下', '2': '0-5%(含)', '3': '5%-10%(含)', '4': '10%-20%(含)', '5': '20%以上' } },
|
||||||
|
{ key: 'score78', label: '上年出口退税金额', valueMap: { '1': '0', '2': '0-5(含)万元', '3': '5-10(含)万元', '4': '10-50(含)万元', '5': '50-100(含)万元', '6': '100万元以上' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '企业偿债能力模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score81', label: '房产套数', valueMap: { '1': '0', '2': '1套', '3': '2套', '4': '3套', '5': '4套及以上' } },
|
||||||
|
{ key: 'score82', label: '房产面积', valueMap: { '1': '0', '2': '0-500(含)平方米', '3': '500-1000(含)平方米以下', '4': '1000-3000(含)平方米', '5': '3000-5000(含)平方米', '6': '5000-10000(含)平方米', '7': '10000平方米及以上' } },
|
||||||
|
{ key: 'score83', label: '未抵押房产套数', valueMap: { '1': '0', '2': '1套', '3': '2套', '4': '3套', '5': '4套及以上' } },
|
||||||
|
{ key: 'score84', label: '未抵押房产面积', valueMap: { '1': '0', '2': '0-500(含)平方米', '3': '500-1000(含)平方米以下', '4': '1000-3000(含)平方米', '5': '3000-5000(含)平方米', '6': '5000-10000(含)平方米', '7': '10000平方米及以上' } },
|
||||||
|
{ key: 'score85', label: '已抵押房产被担保债权数额', valueMap: { '1': '0', '2': '0-100(含)万元', '3': '100-500(含)万元', '4': '500-1000(含)万元', '5': '1000-3000(含)万元', '6': '3000-5000(含)万元', '7': '5000-10000(含)万元', '8': '10000万元以上' } },
|
||||||
|
{ key: 'score86', label: '企业车产金额', valueMap: { '1': '0', '2': '0-20(含)万元', '3': '20-40(含)万元', '4': '40-60(含)万元', '5': '60-80(含)万元', '6': '80-100(含)万元', '7': '100万元以上' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '潜在代偿资源模块',
|
||||||
|
metrics: [
|
||||||
|
{ key: 'score91', label: '法人代表及股东房产面积合计', valueMap: { '1': '0', '2': '0-500(含)平方米', '3': '500-1000(含)平方米以下', '4': '1000-3000(含)平方米', '5': '3000-5000(含)平方米', '6': '5000-10000(含)平方米', '7': '10000平方米及以上' } },
|
||||||
|
{ key: 'score92', label: '法人代表及股东房产套数合计', valueMap: { '1': '0', '2': '1套', '3': '2套', '4': '3套', '5': '4套及以上' } },
|
||||||
|
{ key: 'score93', label: '法人代表及股东未抵押房产套数合计', valueMap: { '1': '0', '2': '1套', '3': '2套', '4': '3套', '5': '4套及以上' } },
|
||||||
|
{ key: 'score94', label: '法人代表及股东未抵押房产面积', valueMap: { '1': '0', '2': '0-500(含)平方米', '3': '500-1000(含)平方米以下', '4': '1000-3000(含)平方米', '5': '3000-5000(含)平方米', '6': '5000-10000(含)平方米', '7': '10000平方米及以上' } },
|
||||||
|
{ key: 'score95', label: '法人代表及股东已抵押房产被担保债权数额合计', valueMap: { '1': '0', '2': '0-100(含)万元', '3': '100-500(含)万元', '4': '500-1000(含)万元', '5': '1000-3000(含)万元', '6': '3000-5000(含)万元', '7': '5000-10000(含)万元', '8': '10000万元以上' } },
|
||||||
|
{ key: 'score96', label: '法人代表车产金额', valueMap: { '1': '0', '2': '0-20(含)万元', '3': '20-40(含)万元', '4': '40-60(含)万元', '5': '60-80(含)万元', '6': '80-100(含)万元', '7': '100万元以上' } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
var validatePhone = (rule, value, callback) => {
|
var validatePhone = (rule, value, callback) => {
|
||||||
@@ -1347,6 +1531,12 @@ export default {
|
|||||||
showTagsList:[],
|
showTagsList:[],
|
||||||
addTagName:"",
|
addTagName:"",
|
||||||
newCustTag:[],
|
newCustTag:[],
|
||||||
|
// 企业九维数据
|
||||||
|
ent9vPortrait: null,
|
||||||
|
nineVFinalInfo: null,
|
||||||
|
nineDimensionRadarChart: null, // 九维画像雷达图实例
|
||||||
|
nineDimensionRadarResizeObserver: null,
|
||||||
|
nineDimensionRadarRenderTimer: null,
|
||||||
regAddress: {
|
regAddress: {
|
||||||
lazy: true,
|
lazy: true,
|
||||||
lazyLoad(node, resolve) {
|
lazyLoad(node, resolve) {
|
||||||
@@ -1403,13 +1593,40 @@ export default {
|
|||||||
this.managerOptions = []
|
this.managerOptions = []
|
||||||
this.authUser = ''
|
this.authUser = ''
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ent9vPortrait: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (!newVal) {
|
||||||
|
this.destroyNineDimensionRadar()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.scheduleNineDimensionRadarRender()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Custom
|
Custom
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['roles', 'userName']),
|
...mapGetters(['roles', 'userName', 'deptId']),
|
||||||
|
nineDimensionDetailGroups() {
|
||||||
|
return NINE_DIMENSION_DETAIL_CONFIG.map(group => ({
|
||||||
|
title: group.title,
|
||||||
|
rows: this.buildNineDimensionRows(group.metrics)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
nineDimensionScores() {
|
||||||
|
if (!this.ent9vPortrait) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return [1, 2, 3, 4, 5, 6, 7, 8, 9].map(index =>
|
||||||
|
this.normalizePortraitScore(this.ent9vPortrait[`score${index}`])
|
||||||
|
)
|
||||||
|
},
|
||||||
|
nineDimensionRadarScale() {
|
||||||
|
return this.getAdaptiveRadarScale(this.nineDimensionScores)
|
||||||
|
},
|
||||||
isHeadAdmin() {
|
isHeadAdmin() {
|
||||||
return this.roles.includes('headAdmin')
|
return this.roles.includes('headAdmin')
|
||||||
},
|
},
|
||||||
@@ -1443,6 +1660,9 @@ export default {
|
|||||||
// 海宁
|
// 海宁
|
||||||
is875() {
|
is875() {
|
||||||
return this.userName.slice(0, 3) === '875'
|
return this.userName.slice(0, 3) === '875'
|
||||||
|
},
|
||||||
|
is825() {
|
||||||
|
return String(this.deptId || '').substring(0, 3) === '825'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -1464,6 +1684,16 @@ export default {
|
|||||||
// systemUserAllTreeUser().then(res => {
|
// systemUserAllTreeUser().then(res => {
|
||||||
// this.secoureOption = res
|
// this.secoureOption = res
|
||||||
// })
|
// })
|
||||||
|
window.addEventListener('resize', this.handleRadarResize);
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
this.scheduleNineDimensionRadarRender(120)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.clearNineDimensionRadarRenderTimer()
|
||||||
|
this.destroyNineDimensionRadarObserver()
|
||||||
|
this.destroyNineDimensionRadar()
|
||||||
|
window.removeEventListener('resize', this.handleRadarResize);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleAddTag(){
|
handleAddTag(){
|
||||||
@@ -1591,6 +1821,9 @@ export default {
|
|||||||
this.showTagsList=this.tagsList[0].children[0].children||[];
|
this.showTagsList=this.tagsList[0].children[0].children||[];
|
||||||
}
|
}
|
||||||
this.tagManualList = cloneDeep(res.data.tagManual) || []
|
this.tagManualList = cloneDeep(res.data.tagManual) || []
|
||||||
|
// 企业九维数据
|
||||||
|
this.ent9vPortrait = res.data.ent9vPortrait || null
|
||||||
|
this.nineVFinalInfo = res.data.nineVFinalInfo || null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -2014,6 +2247,381 @@ export default {
|
|||||||
this.managerOptions = response.data;
|
this.managerOptions = response.data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
normalizePortraitScore(value) {
|
||||||
|
const numericValue = Number(value)
|
||||||
|
return Number.isFinite(numericValue) ? numericValue : 0
|
||||||
|
},
|
||||||
|
getAdaptiveRadarScale(scores) {
|
||||||
|
const maxScore = Math.max(...scores, 0)
|
||||||
|
if (maxScore <= 0) {
|
||||||
|
return {
|
||||||
|
max: 10,
|
||||||
|
splitNumber: 5,
|
||||||
|
step: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitNumber = 5
|
||||||
|
const roughStep = maxScore / splitNumber
|
||||||
|
const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)))
|
||||||
|
const normalized = roughStep / magnitude
|
||||||
|
|
||||||
|
let niceFactor = 10
|
||||||
|
if (normalized <= 1) {
|
||||||
|
niceFactor = 1
|
||||||
|
} else if (normalized <= 2) {
|
||||||
|
niceFactor = 2
|
||||||
|
} else if (normalized <= 2.5) {
|
||||||
|
niceFactor = 2.5
|
||||||
|
} else if (normalized <= 5) {
|
||||||
|
niceFactor = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
const step = niceFactor * magnitude
|
||||||
|
let max = Math.ceil(maxScore / step) * step
|
||||||
|
if (max <= maxScore) {
|
||||||
|
max += step
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
max,
|
||||||
|
splitNumber,
|
||||||
|
step
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatPortraitDisplay(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericValue = Number(value)
|
||||||
|
if (!Number.isFinite(numericValue)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number.isInteger(numericValue) ? `${numericValue}` : numericValue.toFixed(2)
|
||||||
|
},
|
||||||
|
clearNineDimensionRadarRenderTimer() {
|
||||||
|
if (this.nineDimensionRadarRenderTimer) {
|
||||||
|
clearTimeout(this.nineDimensionRadarRenderTimer)
|
||||||
|
this.nineDimensionRadarRenderTimer = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scheduleNineDimensionRadarRender(delay = 80) {
|
||||||
|
this.clearNineDimensionRadarRenderTimer()
|
||||||
|
this.nineDimensionRadarRenderTimer = setTimeout(() => {
|
||||||
|
this.initNineDimensionRadar()
|
||||||
|
}, delay)
|
||||||
|
},
|
||||||
|
destroyNineDimensionRadar() {
|
||||||
|
if (this.nineDimensionRadarChart) {
|
||||||
|
this.nineDimensionRadarChart.dispose();
|
||||||
|
this.nineDimensionRadarChart = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyNineDimensionRadarObserver() {
|
||||||
|
if (this.nineDimensionRadarResizeObserver) {
|
||||||
|
this.nineDimensionRadarResizeObserver.disconnect()
|
||||||
|
this.nineDimensionRadarResizeObserver = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initNineDimensionRadarObserver(chartDom) {
|
||||||
|
if (!chartDom || typeof ResizeObserver === 'undefined' || this.nineDimensionRadarResizeObserver) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nineDimensionRadarResizeObserver = new ResizeObserver((entries) => {
|
||||||
|
const targetRect = entries && entries[0] ? entries[0].contentRect : null
|
||||||
|
if (this.nineDimensionRadarChart) {
|
||||||
|
this.handleRadarResize()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (targetRect && targetRect.width >= 360 && targetRect.height >= 260) {
|
||||||
|
this.scheduleNineDimensionRadarRender(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.nineDimensionRadarResizeObserver.observe(chartDom)
|
||||||
|
if (chartDom.parentElement) {
|
||||||
|
this.nineDimensionRadarResizeObserver.observe(chartDom.parentElement)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatRadarLabel(label, chunkSize = 7) {
|
||||||
|
if (!label || label.length <= chunkSize) {
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
const segments = []
|
||||||
|
for (let index = 0; index < label.length; index += chunkSize) {
|
||||||
|
segments.push(label.slice(index, index + chunkSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments.join('\n')
|
||||||
|
},
|
||||||
|
// 九维画像分数字段标签映射
|
||||||
|
getPortraitLabel(key) {
|
||||||
|
const labelMap = {
|
||||||
|
uniscid: '统一社会信用代码',
|
||||||
|
cstId: '客户内码',
|
||||||
|
orgNo: '机构号',
|
||||||
|
score1: '企业合规经营模块',
|
||||||
|
score2: '企业风险准入模块',
|
||||||
|
score3: '高管信用评价模块',
|
||||||
|
score4: '股东信用评价模块',
|
||||||
|
score5: '企业社会贡献模块',
|
||||||
|
score6: '企业稳定经营模块',
|
||||||
|
score7: '企业经营能力模块',
|
||||||
|
score8: '企业偿债能力模块',
|
||||||
|
score9: '潜在代偿资源模块',
|
||||||
|
scoreAll: '九维总分',
|
||||||
|
scoreAllRank: '九维总分排名',
|
||||||
|
datDt: '会计日期'
|
||||||
|
};
|
||||||
|
return labelMap[key] || key;
|
||||||
|
},
|
||||||
|
// 获取九维画像维度名称列表
|
||||||
|
getNineDimensionLabels() {
|
||||||
|
return [
|
||||||
|
'企业合规经营模块',
|
||||||
|
'企业风险准入模块',
|
||||||
|
'高管信用评价模块',
|
||||||
|
'股东信用评价模块',
|
||||||
|
'企业社会贡献模块',
|
||||||
|
'企业稳定经营模块',
|
||||||
|
'企业经营能力模块',
|
||||||
|
'企业偿债能力模块',
|
||||||
|
'潜在代偿资源模块'
|
||||||
|
];
|
||||||
|
},
|
||||||
|
// 初始化九维画像雷达图
|
||||||
|
initNineDimensionRadar() {
|
||||||
|
if (!this.ent9vPortrait) {
|
||||||
|
this.destroyNineDimensionRadar()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const chartDom = this.$refs.nineDimensionRadar;
|
||||||
|
if (!chartDom) return;
|
||||||
|
this.initNineDimensionRadarObserver(chartDom)
|
||||||
|
const chartWidth = chartDom.clientWidth || 0
|
||||||
|
const chartHeight = chartDom.clientHeight || 0
|
||||||
|
if (chartWidth < 360 || chartHeight < 260) {
|
||||||
|
this.scheduleNineDimensionRadarRender(140)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const scores = this.nineDimensionScores
|
||||||
|
const radarScale = this.nineDimensionRadarScale
|
||||||
|
const dimensionLabels = this.getNineDimensionLabels()
|
||||||
|
const isCompactChart = chartWidth > 0 && chartWidth < 760
|
||||||
|
|
||||||
|
const currentInstance = echarts.getInstanceByDom(chartDom)
|
||||||
|
if (currentInstance) {
|
||||||
|
this.nineDimensionRadarChart = currentInstance
|
||||||
|
} else {
|
||||||
|
this.destroyNineDimensionRadar()
|
||||||
|
this.nineDimensionRadarChart = echarts.init(chartDom);
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
backgroundColor: 'rgba(17, 24, 39, 0.92)',
|
||||||
|
borderColor: 'rgba(99, 102, 241, 0.35)',
|
||||||
|
borderWidth: 1,
|
||||||
|
padding: [10, 14],
|
||||||
|
textStyle: {
|
||||||
|
color: '#f8fafc',
|
||||||
|
fontSize: 13
|
||||||
|
},
|
||||||
|
formatter: (params) => {
|
||||||
|
const lines = dimensionLabels.map((name, index) => `${name}: ${this.formatPortraitDisplay(params.value[index])}`)
|
||||||
|
return lines.join('<br/>')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
radar: {
|
||||||
|
indicator: dimensionLabels.map(name => ({ name, max: radarScale.max })),
|
||||||
|
shape: 'polygon',
|
||||||
|
splitNumber: radarScale.splitNumber,
|
||||||
|
radius: isCompactChart ? '76%' : '82%',
|
||||||
|
center: ['50%', '54%'],
|
||||||
|
name: {
|
||||||
|
formatter: name => (isCompactChart ? this.formatRadarLabel(name, 6) : name),
|
||||||
|
textStyle: {
|
||||||
|
color: '#364152',
|
||||||
|
fontSize: isCompactChart ? 13 : 14,
|
||||||
|
fontWeight: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgba(122, 108, 255, 0.34)',
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitArea: {
|
||||||
|
show: true,
|
||||||
|
areaStyle: {
|
||||||
|
color: [
|
||||||
|
'rgba(107, 114, 128, 0.12)',
|
||||||
|
'rgba(148, 163, 184, 0.1)',
|
||||||
|
'rgba(203, 213, 225, 0.08)',
|
||||||
|
'rgba(226, 232, 240, 0.06)',
|
||||||
|
'rgba(241, 245, 249, 0.04)'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgba(148, 163, 184, 0.4)',
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisNameGap: isCompactChart ? 4 : 8
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '企业九维画像',
|
||||||
|
type: 'radar',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: scores,
|
||||||
|
name: '当前企业',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#ffbf2f'
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: 'rgba(255, 191, 47, 0.68)'
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
color: '#f5b700',
|
||||||
|
width: 2.5
|
||||||
|
},
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
animationDuration: 650
|
||||||
|
};
|
||||||
|
|
||||||
|
this.nineDimensionRadarChart.setOption(option);
|
||||||
|
this.handleRadarResize()
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleRadarResize()
|
||||||
|
}, 120)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 处理雷达图窗口大小变化
|
||||||
|
handleRadarResize() {
|
||||||
|
if (this.nineDimensionRadarChart) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.nineDimensionRadarChart.resize();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildNineDimensionRows(metrics) {
|
||||||
|
const rows = []
|
||||||
|
for (let index = 0; index < metrics.length; index += 2) {
|
||||||
|
const firstMetric = metrics[index]
|
||||||
|
const secondMetric = metrics[index + 1]
|
||||||
|
rows.push([
|
||||||
|
{
|
||||||
|
...firstMetric,
|
||||||
|
value: this.nineVFinalInfo ? this.nineVFinalInfo[firstMetric.key] : null
|
||||||
|
},
|
||||||
|
...(secondMetric
|
||||||
|
? [{
|
||||||
|
...secondMetric,
|
||||||
|
value: this.nineVFinalInfo ? this.nineVFinalInfo[secondMetric.key] : null
|
||||||
|
}]
|
||||||
|
: [])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
},
|
||||||
|
formatNineDimensionValue(metric) {
|
||||||
|
const value = metric ? metric.value : null
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawValue = `${value}`.trim()
|
||||||
|
const normalizedValue = /^-?\d+(\.0+)?$/.test(rawValue) ? `${Number(rawValue)}` : rawValue
|
||||||
|
|
||||||
|
if (metric && metric.valueMap) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(metric.valueMap, normalizedValue)) {
|
||||||
|
return metric.valueMap[normalizedValue]
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(metric.valueMap, rawValue)) {
|
||||||
|
return metric.valueMap[rawValue]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metric && metric.label && metric.label.includes('是否')) {
|
||||||
|
if (['1', 1, true, 'true', 'TRUE', '是'].includes(value)) {
|
||||||
|
return '是'
|
||||||
|
}
|
||||||
|
if (['2', 2, false, 'false', 'FALSE', '否'].includes(value)) {
|
||||||
|
return '否'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
// 九维详细信息字段标签映射(部分常用字段)
|
||||||
|
getFinalInfoLabel(key) {
|
||||||
|
const labelMap = {
|
||||||
|
uniscid: '统一社会信用代码',
|
||||||
|
orgNo: '机构号',
|
||||||
|
// 维度1 - 合规经营
|
||||||
|
score11: '是否存在经营异常名录信息',
|
||||||
|
score12: '是否存在严重违法失信企业名单信息',
|
||||||
|
score13: '企业环境行为信用等级是否为E或D',
|
||||||
|
score14: '是否存在税务重大税收违法黑名单信息',
|
||||||
|
score15: '是否存在拖欠工资黑名单',
|
||||||
|
score16: '是否存在工商吊销企业信息',
|
||||||
|
// 维度2 - 风险准入
|
||||||
|
score21: '是否存在注销企业信息',
|
||||||
|
score22: '是否存在执行案件信息',
|
||||||
|
score23: '是否存在查封信息',
|
||||||
|
score24: '是否存在单位未履行生效裁判信息',
|
||||||
|
score25: '是否存在企业破产清算信息',
|
||||||
|
score26: '是否失信被执行人',
|
||||||
|
score27: '是否为诉讼案件被告',
|
||||||
|
// 维度5 - 社会贡献度
|
||||||
|
score51: '前12个月纳税金额',
|
||||||
|
score52: '纳税等级',
|
||||||
|
score53: '缴纳社保人数',
|
||||||
|
score54: '公积金缴纳人数',
|
||||||
|
score55: '是否为出口退税生产清单企业',
|
||||||
|
// 维度6 - 稳定经营
|
||||||
|
score61: '市场主体经营年限',
|
||||||
|
score62: '认缴出资人数',
|
||||||
|
score63: '最大股东持股占比',
|
||||||
|
score64: '近三年法定代表人变更次数',
|
||||||
|
score65: '近三年股东变更次数',
|
||||||
|
score69: '法人户籍',
|
||||||
|
score610: '法人婚姻状况',
|
||||||
|
// 维度7 - 经营能力
|
||||||
|
score71: '上年增值税金额',
|
||||||
|
score72: '今年增值税同比变动',
|
||||||
|
score73: '上年企业所得税金额',
|
||||||
|
score74: '今年所得税同比变动',
|
||||||
|
score75: '缴纳社保人数同比变动',
|
||||||
|
score77: '当年纳税金额同比变动',
|
||||||
|
score78: '上年出口退税金额',
|
||||||
|
// 维度8 - 偿债能力
|
||||||
|
score81: '房产套数',
|
||||||
|
score82: '房产面积',
|
||||||
|
score83: '未抵押房产套数',
|
||||||
|
score84: '未抵押房产面积',
|
||||||
|
score85: '已抵押房产被担保债权数额',
|
||||||
|
score86: '企业车产金额'
|
||||||
|
};
|
||||||
|
return labelMap[key] || key;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -2405,4 +3013,210 @@ export default {
|
|||||||
width: 320px !important;
|
width: 320px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 企业九维数据样式
|
||||||
|
.nine-dimension-data {
|
||||||
|
padding: 20px 0;
|
||||||
|
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
|
||||||
|
border-radius: 14px;
|
||||||
|
|
||||||
|
.nine-dimension-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #202938;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #edf1f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nine-dimension-chart-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 30px;
|
||||||
|
padding: 26px 30px;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #fbfcff 100%);
|
||||||
|
border: 1px solid #e7edf7;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 8px 26px rgba(31, 55, 88, 0.06);
|
||||||
|
|
||||||
|
.radar-chart-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 4px 18px 4px 0;
|
||||||
|
border-right: 1px solid #edf1f7;
|
||||||
|
|
||||||
|
.nine-dimension-radar {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
height: 430px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nine-dimension-summary {
|
||||||
|
width: 236px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 18px 20px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #edf1f7;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 12px 28px rgba(34, 65, 120, 0.08);
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #edf1f7;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #202938;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
display: block;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f6feb;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
|
||||||
|
&.is-rank {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-unit {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #202938;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nine-dimension-detail-table {
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
.detail-table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 980px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 18px 16px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #303133;
|
||||||
|
vertical-align: middle;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-cell {
|
||||||
|
width: 184px;
|
||||||
|
background: #eef3f9;
|
||||||
|
color: #1f6feb;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-label-cell {
|
||||||
|
background: #f6f8fb;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value-cell {
|
||||||
|
width: 120px;
|
||||||
|
background: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-empty {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-item {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.data-label {
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1400px) {
|
||||||
|
.nine-dimension-data {
|
||||||
|
.nine-dimension-section {
|
||||||
|
.nine-dimension-chart-container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
.radar-chart-wrapper {
|
||||||
|
padding-right: 0;
|
||||||
|
border-right: 0;
|
||||||
|
border-bottom: 1px solid #edf1f7;
|
||||||
|
padding-bottom: 18px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
.nine-dimension-radar {
|
||||||
|
max-width: 100%;
|
||||||
|
height: 390px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nine-dimension-summary {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
<div class="customer-wrap">
|
<div class="customer-wrap">
|
||||||
<div>
|
<div>
|
||||||
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
||||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
<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>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||||
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
|
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||||
|
</template>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<div class="customerMain">
|
<div class="customerMain">
|
||||||
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
||||||
@@ -135,6 +142,29 @@
|
|||||||
<el-dropdown-item @click.native="handleExportAll">导出前1000条<i class="quesiton"></i></el-dropdown-item>
|
<el-dropdown-item @click.native="handleExportAll">导出前1000条<i class="quesiton"></i></el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
<template v-if="selectedTab === '2' && canSeeBusinessImport">
|
||||||
|
<div class="import-action">
|
||||||
|
<el-upload
|
||||||
|
ref="businessImportUploadRef"
|
||||||
|
class="business-import-upload"
|
||||||
|
action="#"
|
||||||
|
:show-file-list="false"
|
||||||
|
:http-request="requestBusinessCustLevelImport"
|
||||||
|
:before-upload="beforeBusinessCustLevelUpload"
|
||||||
|
:disabled="businessImportLoading"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-upload2"
|
||||||
|
type="primary"
|
||||||
|
style="margin-left: 10px"
|
||||||
|
:loading="businessImportLoading"
|
||||||
|
>导入</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-tooltip placement="top" trigger="hover" content="导入更新公司客户视图分层分类数据" width="200">
|
||||||
|
<span class="import-question"><i class="el-icon-question"></i></span>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<upload-tag style="margin-left: 10px" v-if="selectedTab === '0' && userName.indexOf('931') === 0"></upload-tag>
|
<upload-tag style="margin-left: 10px" v-if="selectedTab === '0' && userName.indexOf('931') === 0"></upload-tag>
|
||||||
</div>
|
</div>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="searchColoumns"
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="searchColoumns"
|
||||||
@@ -173,7 +203,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { getCustomerList, getCustAddressList, getPerIndcList, getGegionList, getDrawList, getVirtualList } from '@/api/grid/mycustomer.js'
|
import {
|
||||||
|
getCustomerList,
|
||||||
|
getCustAddressList,
|
||||||
|
getPerIndcList,
|
||||||
|
getGegionList,
|
||||||
|
getDrawList,
|
||||||
|
getVirtualList,
|
||||||
|
importBusinessCustLevelAsync,
|
||||||
|
getBusinessCustLevelImportStatus
|
||||||
|
} from '@/api/grid/mycustomer.js'
|
||||||
import GroupCheck from './components/group-check'
|
import GroupCheck from './components/group-check'
|
||||||
import GroupCheckMore from './components/group-check-more'
|
import GroupCheckMore from './components/group-check-more'
|
||||||
import UploadTag from './components/uploadTag.vue'
|
import UploadTag from './components/uploadTag.vue'
|
||||||
@@ -281,6 +320,8 @@ export default {
|
|||||||
showMoreIndc: true,
|
showMoreIndc: true,
|
||||||
contiKeys: [],
|
contiKeys: [],
|
||||||
iscollapsed: false,
|
iscollapsed: false,
|
||||||
|
businessImportLoading: false,
|
||||||
|
businessImportTimer: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -317,12 +358,18 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['roles', 'userName']),
|
...mapGetters(['roles', 'userName']),
|
||||||
|
deptId() {
|
||||||
|
return this.$store.state.user.deptId
|
||||||
|
},
|
||||||
filtereDate() {
|
filtereDate() {
|
||||||
if (this.searchQuery) {
|
if (this.searchQuery) {
|
||||||
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
||||||
}
|
}
|
||||||
return this.tableData
|
return this.tableData
|
||||||
},
|
},
|
||||||
|
isHeadAdmin() {
|
||||||
|
return this.roles.includes('headAdmin')
|
||||||
|
},
|
||||||
isPublic() {
|
isPublic() {
|
||||||
return this.roles.includes('headPublic')
|
return this.roles.includes('headPublic')
|
||||||
},
|
},
|
||||||
@@ -332,6 +379,12 @@ export default {
|
|||||||
isOps() {
|
isOps() {
|
||||||
return this.roles.includes('headOps')
|
return this.roles.includes('headOps')
|
||||||
},
|
},
|
||||||
|
is825() {
|
||||||
|
return String(this.deptId || '').substring(0, 3) === '825'
|
||||||
|
},
|
||||||
|
canSeeBusinessImport() {
|
||||||
|
return this.is825 && (this.isHeadAdmin || this.isPublic || this.isPrivate || this.isOps)
|
||||||
|
},
|
||||||
// 客户经理
|
// 客户经理
|
||||||
isCommonManager() {
|
isCommonManager() {
|
||||||
return this.roles.includes('commonManager')
|
return this.roles.includes('commonManager')
|
||||||
@@ -770,12 +823,75 @@ export default {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeBusinessCustLevelUpload(file) {
|
||||||
|
const fileName = file.name ? file.name.toLowerCase() : ''
|
||||||
|
const isExcel = fileName.endsWith('.xls') || fileName.endsWith('.xlsx')
|
||||||
|
if (!isExcel) {
|
||||||
|
this.$message.warning('请上传Excel文件')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
async requestBusinessCustLevelImport(fileOut) {
|
||||||
|
const { file } = fileOut
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
this.businessImportLoading = true
|
||||||
|
this.clearBusinessImportTimer()
|
||||||
|
try {
|
||||||
|
const res = await importBusinessCustLevelAsync(formData)
|
||||||
|
this.$message.success(res.msg || '导入任务已提交,后台正在处理')
|
||||||
|
if (res.data) {
|
||||||
|
this.pollBusinessCustLevelImportStatus(res.data)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.businessImportLoading = false
|
||||||
|
if (this.$refs.businessImportUploadRef) {
|
||||||
|
this.$refs.businessImportUploadRef.clearFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pollBusinessCustLevelImportStatus(taskId) {
|
||||||
|
this.clearBusinessImportTimer()
|
||||||
|
this.businessImportTimer = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const res = await getBusinessCustLevelImportStatus(taskId)
|
||||||
|
const task = res.data
|
||||||
|
if (!task || task.status === '0') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.clearBusinessImportTimer()
|
||||||
|
if (task.status === '1') {
|
||||||
|
window.dispatchEvent(new Event('notice-center-refresh'))
|
||||||
|
this.$message.success(task.message || '导入成功')
|
||||||
|
this.getList()
|
||||||
|
} else if (task.status === '2') {
|
||||||
|
window.dispatchEvent(new Event('notice-center-refresh'))
|
||||||
|
this.$message.error(task.message || '导入失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.clearBusinessImportTimer()
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
|
},
|
||||||
|
clearBusinessImportTimer() {
|
||||||
|
if (this.businessImportTimer) {
|
||||||
|
clearInterval(this.businessImportTimer)
|
||||||
|
this.businessImportTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
const { query } = this.$route;
|
const { query } = this.$route;
|
||||||
if (query.backUrl) {
|
if (query.backUrl) {
|
||||||
this.selectedTab = query.selectedTab
|
this.selectedTab = query.selectedTab
|
||||||
this.queryParams.custPattern = query.selectedTab
|
this.queryParams.custPattern = query.selectedTab
|
||||||
|
} else {
|
||||||
|
// 默认选中第一个tab
|
||||||
|
const deptId = this.$store.state.user.deptId
|
||||||
|
const deptIdStr = deptId ? String(deptId) : ''
|
||||||
|
this.selectedTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||||
|
this.queryParams.custPattern = this.selectedTab
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -784,6 +900,9 @@ export default {
|
|||||||
this.initGetSecondRegionList()
|
this.initGetSecondRegionList()
|
||||||
this.initgetDrawList()
|
this.initgetDrawList()
|
||||||
this.initGetVirtualList()
|
this.initGetVirtualList()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.clearBusinessImportTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1046,6 +1165,26 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.import-action {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-import-upload {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-question {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -14px;
|
||||||
|
color: #b9b9b9;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.iframe-wrap {
|
.iframe-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 500px;
|
height: 500px;
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
const commonCol = function (isCom, type) {
|
// 当headId=875时需要隐藏的字段列表
|
||||||
return [
|
const HIDDEN_PROPS_FOR_875 = [
|
||||||
|
'regionTopGridName', // 总行行政网格名称
|
||||||
|
'regionSecGridName', // 支行行政网格名称
|
||||||
|
'belongBranchName', // 行政网格归属支行
|
||||||
|
'belongOutletName', // 行政网格归属网点
|
||||||
|
'belongUserNameList', // 行政网格客户经理
|
||||||
|
'drawGridName', // 自绘地图网格名称
|
||||||
|
'drawBranchNames', // 自绘地图网格归属支行
|
||||||
|
'drawOutletNames', // 自绘地图网格归属网点
|
||||||
|
'drawUserNames', // 自绘地图网格客户经理
|
||||||
|
'virtualGridName', // 自建名单网格名称
|
||||||
|
'virtualBranchNames', // 自建名单网格归属支行
|
||||||
|
'virtualOutletNames', // 自建名单网格归属网点
|
||||||
|
'virtualUserNames', // 自建名单网格客户经理
|
||||||
|
]
|
||||||
|
|
||||||
|
const commonCol = function (isCom, type, headId) {
|
||||||
|
const allColumns = [
|
||||||
{
|
{
|
||||||
prop: 'regionTopGridName',
|
prop: 'regionTopGridName',
|
||||||
label: '总行行政网格名称',
|
label: '总行行政网格名称',
|
||||||
@@ -101,9 +118,16 @@ const commonCol = function (isCom, type) {
|
|||||||
showOverflowTooltip: true,
|
showOverflowTooltip: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 当headId=875时,过滤掉指定的列
|
||||||
|
if (String(headId).startsWith('875')) {
|
||||||
|
return allColumns.filter(col => !HIDDEN_PROPS_FOR_875.includes(col.prop))
|
||||||
|
}
|
||||||
|
|
||||||
|
return allColumns
|
||||||
}
|
}
|
||||||
|
|
||||||
export const placeholderMap = (type) => ({
|
export const placeholderMap = (type, headId) => ({
|
||||||
'2': {
|
'2': {
|
||||||
placeholder: '搜索企业名称/法人名字',
|
placeholder: '搜索企业名称/法人名字',
|
||||||
custPattern: '2',
|
custPattern: '2',
|
||||||
@@ -138,7 +162,7 @@ export const placeholderMap = (type) => ({
|
|||||||
type: 'myCustLevel',
|
type: 'myCustLevel',
|
||||||
// desc: '建档输入/取新华社数据',
|
// desc: '建档输入/取新华社数据',
|
||||||
},
|
},
|
||||||
...commonCol('2', type),
|
...commonCol('2', type, headId),
|
||||||
{
|
{
|
||||||
prop: 'lpName',
|
prop: 'lpName',
|
||||||
label: '法人姓名',
|
label: '法人姓名',
|
||||||
@@ -393,7 +417,7 @@ export const placeholderMap = (type) => ({
|
|||||||
type: 'myCustIdsn',
|
type: 'myCustIdsn',
|
||||||
// desc: '建档输入/取新华社数据',
|
// desc: '建档输入/取新华社数据',
|
||||||
},
|
},
|
||||||
...commonCol('1', type),
|
...commonCol('1', type, headId),
|
||||||
{
|
{
|
||||||
prop: 'lpName',
|
prop: 'lpName',
|
||||||
label: '经营者姓名',
|
label: '经营者姓名',
|
||||||
@@ -634,7 +658,7 @@ export const placeholderMap = (type) => ({
|
|||||||
type: 'myCustLevel',
|
type: 'myCustLevel',
|
||||||
// desc: '取大信贷数据',
|
// desc: '取大信贷数据',
|
||||||
},
|
},
|
||||||
...commonCol('0', type),
|
...commonCol('0', type, headId),
|
||||||
{
|
{
|
||||||
prop: 'custPhone',
|
prop: 'custPhone',
|
||||||
label: '联系方式',
|
label: '联系方式',
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
<div class="customer-wrap">
|
<div class="customer-wrap">
|
||||||
<div>
|
<div>
|
||||||
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
||||||
<el-radio-button label="2" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button> // :disabled="isPrivate"
|
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
<el-radio-button label="1" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
<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>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||||
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
|
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||||
|
</template>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<div class="customerMain">
|
<div class="customerMain">
|
||||||
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
||||||
@@ -283,8 +290,9 @@ export default {
|
|||||||
const type = this.roles.includes('headPublic') ? 'isPublic' :
|
const type = this.roles.includes('headPublic') ? 'isPublic' :
|
||||||
this.roles.includes('headPrivate') ? 'isPrivate' :
|
this.roles.includes('headPrivate') ? 'isPrivate' :
|
||||||
this.roles.includes('headOps') ? 'isOps' : ""
|
this.roles.includes('headOps') ? 'isOps' : ""
|
||||||
this.tableColoumns = placeholderMap(type)[val].tableColoumns
|
const headId = this.deptId
|
||||||
this.placeholder = placeholderMap(type)[val].placeholder
|
this.tableColoumns = placeholderMap(type, headId)[val].tableColoumns
|
||||||
|
this.placeholder = placeholderMap(type, headId)[val].placeholder
|
||||||
this.queryParams.custPattern = val
|
this.queryParams.custPattern = val
|
||||||
this.searchColoumns = this.getSearchColoumns()
|
this.searchColoumns = this.getSearchColoumns()
|
||||||
}
|
}
|
||||||
@@ -310,6 +318,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['roles']),
|
...mapGetters(['roles']),
|
||||||
|
deptId() {
|
||||||
|
return this.$store.state.user.deptId
|
||||||
|
},
|
||||||
filtereDate() {
|
filtereDate() {
|
||||||
if (this.searchQuery) {
|
if (this.searchQuery) {
|
||||||
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
||||||
@@ -777,6 +788,12 @@ export default {
|
|||||||
if (query.backUrl) {
|
if (query.backUrl) {
|
||||||
this.selectedTab = query.selectedTab
|
this.selectedTab = query.selectedTab
|
||||||
this.queryParams.custPattern = query.selectedTab
|
this.queryParams.custPattern = query.selectedTab
|
||||||
|
} else {
|
||||||
|
// 默认选中第一个tab
|
||||||
|
const deptId = this.$store.state.user.deptId
|
||||||
|
const deptIdStr = deptId ? String(deptId) : ''
|
||||||
|
this.selectedTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||||
|
this.queryParams.custPattern = this.selectedTab
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
680
ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue
Normal file
680
ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="isEdit ? '编辑客群' : '创建客群'"
|
||||||
|
:visible.sync="visible"
|
||||||
|
width="700px"
|
||||||
|
:before-close="handleClose"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-alert
|
||||||
|
v-if="isEdit && isProcessing"
|
||||||
|
title="客群正在处理中,请等待处理完成后再进行编辑"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom: 15px"
|
||||||
|
/>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="120px" v-loading="submitting">
|
||||||
|
<el-form-item label="客群名称" prop="groupName">
|
||||||
|
<el-input v-model="form.groupName" placeholder="请输入客群名称" maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客群模式" prop="groupMode">
|
||||||
|
<el-radio-group v-model="form.groupMode" :disabled="isEdit && form.createStatus === '1'">
|
||||||
|
<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
|
||||||
|
v-model="form.validTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择有效期截止时间"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">留空表示永久有效</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="2" />
|
||||||
|
</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"
|
||||||
|
multiple
|
||||||
|
placeholder="请选择可见部门"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dept in deptOptions"
|
||||||
|
:key="dept.deptId"
|
||||||
|
:label="dept.deptName"
|
||||||
|
:value="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>
|
||||||
|
|
||||||
|
<!-- 模板导入 -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- 绩效网格 -->
|
||||||
|
<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'">
|
||||||
|
<el-form-item label="网格级别">
|
||||||
|
<el-radio-group v-model="regionQuery.gridLevel">
|
||||||
|
<el-radio label="1">总行行政网格</el-radio>
|
||||||
|
<el-radio label="2">支行行政网格</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="责任类型">
|
||||||
|
<el-select v-model="regionQuery.gridDutyType" placeholder="请选择责任类型" style="width: 100%" clearable>
|
||||||
|
<el-option label="责任网格" value="1" />
|
||||||
|
<el-option label="竞争网格" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="网格名称">
|
||||||
|
<el-input v-model="regionQuery.gridName" placeholder="请输入网格名称(可选)" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" @click="loadRegionGridOptions" :loading="regionLoading">查询网格</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" @click="resetRegionQuery">重置条件</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择网格">
|
||||||
|
<el-select
|
||||||
|
v-model="gridForm.regionGridIds"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
collapse-tags
|
||||||
|
placeholder="请先查询网格,然后选择"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="grid in regionGridOptions"
|
||||||
|
:key="grid.gridId"
|
||||||
|
:label="grid.gridName"
|
||||||
|
:value="grid.gridId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 绘制网格 -->
|
||||||
|
<template v-if="form.createMode === '4'">
|
||||||
|
<el-form-item label="绘制网格" prop="drawGridIds">
|
||||||
|
<el-select
|
||||||
|
v-model="gridForm.drawGridIds"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
placeholder="请选择绘制网格"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="grid in drawGridOptions"
|
||||||
|
:key="grid.gridId"
|
||||||
|
:label="grid.gridName"
|
||||||
|
:value="grid.gridId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitting" :disabled="isProcessing" @click="handleSubmit">
|
||||||
|
{{ submitButtonText }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
createCustGroupByGrid,
|
||||||
|
createCustGroupByTemplate,
|
||||||
|
updateCustGroupByGrid,
|
||||||
|
updateCustGroupByTemplate,
|
||||||
|
downloadTemplate,
|
||||||
|
getManagerList,
|
||||||
|
getRegionGridListForGroup
|
||||||
|
} 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'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateDialog',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
groupData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
isEdit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 提交中状态
|
||||||
|
submitting: false,
|
||||||
|
// 上传地址
|
||||||
|
uploadUrl: process.env.VUE_APP_BASE_API + '/group/cust/createByTemplate',
|
||||||
|
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
|
||||||
|
// 处理中提示
|
||||||
|
processTipVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 是否正在处理中(创建或更新)
|
||||||
|
isProcessing() {
|
||||||
|
return this.form.createStatus === '0'
|
||||||
|
},
|
||||||
|
// 提交按钮文本
|
||||||
|
submitButtonText() {
|
||||||
|
if (this.submitting) return '提交中...'
|
||||||
|
if (this.isEdit && this.isProcessing) return '客群处理中,请稍后...'
|
||||||
|
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) {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
groupData: {
|
||||||
|
handler(val) {
|
||||||
|
if (val && Object.keys(val).length > 0) {
|
||||||
|
this.form = { ...val }
|
||||||
|
this.shareEnabled = val.shareEnabled === 1
|
||||||
|
// 解析 shareDeptIds 字符串为 shareDeptIdList 数组
|
||||||
|
if (val.shareDeptIds) {
|
||||||
|
this.form.shareDeptIdList = val.shareDeptIds.split(',').filter(id => id)
|
||||||
|
} else {
|
||||||
|
this.form.shareDeptIdList = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反显网格数据(需要在 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()
|
||||||
|
},
|
||||||
|
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
|
||||||
|
|
||||||
|
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.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 加载部门选项 */
|
||||||
|
loadDeptOptions() {
|
||||||
|
listDept().then(response => {
|
||||||
|
this.deptOptions = response.data || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.deptOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 加载用户选项 */
|
||||||
|
loadUserOptions() {
|
||||||
|
listUser().then(response => {
|
||||||
|
this.userOptions = response.rows || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.userOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 根据业务类型加载客户经理选项 */
|
||||||
|
loadManagerOptions() {
|
||||||
|
if (!this.gridForm.cmpmBizType) {
|
||||||
|
this.userOptions = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getManagerList(this.gridForm.cmpmBizType).then(response => {
|
||||||
|
this.userOptions = response.data || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.userOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 业务类型改变处理 */
|
||||||
|
handleCmpmBizTypeChange(val) {
|
||||||
|
// 清空已选择的客户经理
|
||||||
|
this.gridForm.userNames = []
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 加载地理网格选项 */
|
||||||
|
loadRegionGridOptions() {
|
||||||
|
this.regionLoading = true
|
||||||
|
const params = {
|
||||||
|
gridLevel: this.regionQuery.gridLevel
|
||||||
|
}
|
||||||
|
if (this.regionQuery.gridDutyType) {
|
||||||
|
params.gridDutyType = this.regionQuery.gridDutyType
|
||||||
|
}
|
||||||
|
if (this.regionQuery.gridName) {
|
||||||
|
params.gridName = this.regionQuery.gridName
|
||||||
|
}
|
||||||
|
|
||||||
|
getRegionGridListForGroup(params).then(response => {
|
||||||
|
this.regionGridOptions = response.data || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.regionGridOptions = []
|
||||||
|
}).finally(() => {
|
||||||
|
this.regionLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 重置地理网格查询条件 */
|
||||||
|
resetRegionQuery() {
|
||||||
|
this.regionQuery = {
|
||||||
|
gridLevel: '1',
|
||||||
|
gridDutyType: null,
|
||||||
|
gridName: null
|
||||||
|
}
|
||||||
|
this.gridForm.regionGridIds = []
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 加载绘制网格选项 */
|
||||||
|
loadDrawGridOptions() {
|
||||||
|
getSimpleGridList().then(response => {
|
||||||
|
this.drawGridOptions = response.data || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.drawGridOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 文件改变 */
|
||||||
|
handleFileChange(file) {
|
||||||
|
this.uploadFile = file.raw
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 上传成功 */
|
||||||
|
handleUploadSuccess(response) {
|
||||||
|
this.$modal.msgSuccess('创建成功')
|
||||||
|
this.submitting = false
|
||||||
|
this.$emit('submit', { id: response.data })
|
||||||
|
this.handleClose()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 上传失败 */
|
||||||
|
handleUploadError() {
|
||||||
|
this.$modal.msgError('创建失败')
|
||||||
|
this.submitting = false
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 下载模板 */
|
||||||
|
downloadTemplate() {
|
||||||
|
downloadTemplate().then(response => {
|
||||||
|
const url = window.URL.createObjectURL(new Blob([response]))
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.setAttribute('download', '客户信息模板.xlsx')
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
// 处理共享设置
|
||||||
|
this.form.shareEnabled = this.shareEnabled ? 1 : 0
|
||||||
|
// 转换 shareDeptIdList 数组为 shareDeptIds 逗号分隔字符串
|
||||||
|
this.form.shareDeptIds = this.form.shareDeptIdList.join(',')
|
||||||
|
|
||||||
|
// 根据创建方式处理提交数据
|
||||||
|
if (this.form.createMode === '1') {
|
||||||
|
this.handleTemplateSubmit()
|
||||||
|
} else {
|
||||||
|
this.handleGridSubmit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 模板导入提交 */
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
resetForm() {
|
||||||
|
this.form = {
|
||||||
|
id: null,
|
||||||
|
groupName: null,
|
||||||
|
groupMode: '0',
|
||||||
|
createMode: '1',
|
||||||
|
groupStatus: '0',
|
||||||
|
shareEnabled: 0,
|
||||||
|
shareDeptIdList: [],
|
||||||
|
remark: null,
|
||||||
|
validTime: null
|
||||||
|
}
|
||||||
|
this.shareEnabled = false
|
||||||
|
this.gridForm = {
|
||||||
|
gridType: '0',
|
||||||
|
cmpmBizType: null,
|
||||||
|
userNames: [],
|
||||||
|
regionGridIds: [],
|
||||||
|
drawGridIds: []
|
||||||
|
}
|
||||||
|
this.uploadFile = null
|
||||||
|
if (this.$refs.upload) {
|
||||||
|
this.$refs.upload.clearFiles()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.form && this.$refs.form.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
187
ruoyi-ui/src/views/group/custGroup/detail.vue
Normal file
187
ruoyi-ui/src/views/group/custGroup/detail.vue
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<div class="customer-wrap detail-wrap">
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button icon="el-icon-arrow-left" size="small" @click="goBack">返回</el-button>
|
||||||
|
<span class="page-title">客群客户列表</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<div class="search-area">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
|
||||||
|
<el-form-item label="客户类型" prop="custType">
|
||||||
|
<el-select v-model="queryParams.custType" placeholder="请选择" clearable style="width: 150px">
|
||||||
|
<el-option label="个人" value="0" />
|
||||||
|
<el-option label="商户" value="1" />
|
||||||
|
<el-option label="企业" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户姓名" prop="custName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.custName"
|
||||||
|
placeholder="请输入客户姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格区域 -->
|
||||||
|
<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">
|
||||||
|
<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="custIdc" show-overflow-tooltip />
|
||||||
|
<el-table-column label="统信码" prop="socialCreditCode" show-overflow-tooltip />
|
||||||
|
<el-table-column label="添加时间" prop="createTime" width="180" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
:total="total"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listCustGroupMembers } from '@/api/group/custGroup'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustGroupDetail',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
groupId: null,
|
||||||
|
memberList: [],
|
||||||
|
total: 0,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
custType: null,
|
||||||
|
custName: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.groupId = this.$route.query.groupId
|
||||||
|
if (this.groupId) {
|
||||||
|
this.getList()
|
||||||
|
} else {
|
||||||
|
this.$modal.msgError('缺少客群ID参数')
|
||||||
|
this.goBack()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query.groupId'(newGroupId) {
|
||||||
|
if (newGroupId && newGroupId !== this.groupId) {
|
||||||
|
this.groupId = newGroupId
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询客户列表 */
|
||||||
|
getList(param) {
|
||||||
|
if (param) {
|
||||||
|
this.queryParams.pageNum = param.page
|
||||||
|
this.queryParams.pageSize = param.limit
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
listCustGroupMembers(this.groupId, this.queryParams).then(response => {
|
||||||
|
this.memberList = response.rows
|
||||||
|
this.total = response.total
|
||||||
|
this.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 搜索 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 重置 */
|
||||||
|
resetQuery() {
|
||||||
|
this.resetForm('queryForm')
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 返回 */
|
||||||
|
goBack() {
|
||||||
|
this.$router.push({ path: '/group/custGroup' })
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 序号计算方法 */
|
||||||
|
indexMethod(index) {
|
||||||
|
return (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.customer-wrap {
|
||||||
|
background-color: #ffffff;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 3px 8px 0 #00000017;
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
padding: 24px 30px;
|
||||||
|
|
||||||
|
&.detail-wrap {
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #222222;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-area {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #ebebeb;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.el-form {
|
||||||
|
margin-bottom: -8px;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_table {
|
||||||
|
::v-deep .el-pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
447
ruoyi-ui/src/views/group/custGroup/index.vue
Normal file
447
ruoyi-ui/src/views/group/custGroup/index.vue
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
<template>
|
||||||
|
<div class="customer-wrap">
|
||||||
|
<el-radio-group v-model="activeTab" class="group-tab-radio" @input="handleTabChange">
|
||||||
|
<el-radio-button label="mine">我创建的</el-radio-button>
|
||||||
|
<el-radio-button label="sharedToMe">下发给我的</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
|
||||||
|
<div v-show="showSearch" class="search-area">
|
||||||
|
<el-form
|
||||||
|
ref="queryForm"
|
||||||
|
:model="queryParams"
|
||||||
|
size="small"
|
||||||
|
:inline="true"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item label="客群名称" prop="groupName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.groupName"
|
||||||
|
placeholder="请输入客群名称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客群模式" prop="groupMode">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.groupMode"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<el-option label="静态" value="0" />
|
||||||
|
<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"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<el-option label="正常" value="0" />
|
||||||
|
<el-option label="已禁用" value="1" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="operate-cnt">
|
||||||
|
<div class="operate-left">
|
||||||
|
<template v-if="isMineTab">
|
||||||
|
<el-button type="primary" icon="el-icon-plus" size="small" @click="handleAdd">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
size="small"
|
||||||
|
:disabled="multiple"
|
||||||
|
@click="handleDelete"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="main_table">
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="groupList"
|
||||||
|
style="width: 100%"
|
||||||
|
max-height="625"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
v-if="isMineTab"
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
<el-table-column label="客群名称" prop="groupName" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column label="客群模式" align="center" width="100">
|
||||||
|
<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">
|
||||||
|
<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="创建者" 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">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-view"
|
||||||
|
@click="handleView(scope.row)"
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<template v-if="isMineTab">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="handleUpdate(scope.row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
:total="total"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<create-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:group-data="form"
|
||||||
|
:is-edit="isEdit"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listCustGroup, getCustGroup, deleteCustGroup } from '@/api/group/custGroup'
|
||||||
|
import CreateDialog from './components/create-dialog'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustGroup',
|
||||||
|
components: { CreateDialog },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
showSearch: true,
|
||||||
|
activeTab: 'mine',
|
||||||
|
ids: [],
|
||||||
|
single: true,
|
||||||
|
multiple: true,
|
||||||
|
total: 0,
|
||||||
|
groupList: [],
|
||||||
|
dialogVisible: false,
|
||||||
|
isEdit: false,
|
||||||
|
form: {},
|
||||||
|
refreshTimer: null,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
groupName: null,
|
||||||
|
groupMode: null,
|
||||||
|
createMode: null,
|
||||||
|
groupStatus: null,
|
||||||
|
viewType: 'mine'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isMineTab() {
|
||||||
|
return this.activeTab === 'mine'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.clearRefreshTimer()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clearRefreshTimer() {
|
||||||
|
if (this.refreshTimer) {
|
||||||
|
clearTimeout(this.refreshTimer)
|
||||||
|
this.refreshTimer = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshList(delay = 0) {
|
||||||
|
this.clearRefreshTimer()
|
||||||
|
if (delay > 0) {
|
||||||
|
this.refreshTimer = setTimeout(() => {
|
||||||
|
this.getList()
|
||||||
|
this.refreshTimer = null
|
||||||
|
}, delay)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
|
||||||
|
getList() {
|
||||||
|
this.loading = true
|
||||||
|
this.queryParams.viewType = this.activeTab
|
||||||
|
listCustGroup(this.queryParams).then(response => {
|
||||||
|
this.groupList = response.rows
|
||||||
|
this.total = response.total
|
||||||
|
this.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTabChange() {
|
||||||
|
this.ids = []
|
||||||
|
this.single = true
|
||||||
|
this.multiple = true
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.queryParams.viewType = this.activeTab
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
|
||||||
|
resetQuery() {
|
||||||
|
this.resetForm('queryForm')
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.queryParams.pageSize = 10
|
||||||
|
this.queryParams.viewType = this.activeTab
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
if (!this.isMineTab) {
|
||||||
|
this.ids = []
|
||||||
|
this.single = true
|
||||||
|
this.multiple = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.ids = selection.map(item => item.id)
|
||||||
|
this.single = selection.length !== 1
|
||||||
|
this.multiple = !selection.length
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAdd() {
|
||||||
|
this.reset()
|
||||||
|
this.isEdit = false
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUpdate(row) {
|
||||||
|
this.reset()
|
||||||
|
const id = row.id || this.ids[0]
|
||||||
|
getCustGroup(id).then(response => {
|
||||||
|
this.form = response.data
|
||||||
|
this.isEdit = true
|
||||||
|
this.dialogVisible = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleView(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/group/custGroup/detail',
|
||||||
|
query: { groupId: row.id }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDelete(row) {
|
||||||
|
const ids = row.id ? [row.id] : this.ids
|
||||||
|
this.$modal.confirm('是否确认删除选中的客群?').then(() => {
|
||||||
|
return deleteCustGroup(ids)
|
||||||
|
}).then(() => {
|
||||||
|
this.refreshList()
|
||||||
|
this.$modal.msgSuccess('删除成功')
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.refreshList(800)
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.form = {
|
||||||
|
id: null,
|
||||||
|
groupName: null,
|
||||||
|
groupMode: '0',
|
||||||
|
createMode: null,
|
||||||
|
groupStatus: '0',
|
||||||
|
shareEnabled: 0,
|
||||||
|
shareDeptIdList: [],
|
||||||
|
remark: null,
|
||||||
|
validTime: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.customer-wrap {
|
||||||
|
background-color: #ffffff;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 3px 8px 0 #00000017;
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
padding: 24px 30px;
|
||||||
|
|
||||||
|
.group-tab-radio {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid #ebebeb;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.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;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
height: 21px;
|
||||||
|
width: 1px;
|
||||||
|
background: #ebebeb;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-area {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #ebebeb;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.el-form {
|
||||||
|
margin-bottom: -8px;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.operate-cnt {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 24px 0 16px 0;
|
||||||
|
|
||||||
|
.operate-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_table {
|
||||||
|
::v-deep .el-pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<span style="font-size: 14px;">较上月变动</span>
|
<span style="font-size: 14px;">较上月变动</span>
|
||||||
<span v-if="String(item.inc).includes('-')" style=" font-size: 14px;color: #00B453">{{ changeData(item.inc)
|
<span v-if="String(item.inc).includes('-')" style=" font-size: 14px;color: #00B453">{{ changeData(item.inc)
|
||||||
}}<i class="el-icon-caret-bottom"></i></span>
|
}}<i class="el-icon-caret-bottom"></i></span>
|
||||||
|
<span v-else-if="item.inc == 0 || item.inc === '0'" style=" font-size: 14px;color: #409EFF">{{ changeData(item.inc) }}</span>
|
||||||
<span v-else style=" font-size: 14px;color: #EF3F35">{{ changeData(item.inc) }}<i
|
<span v-else style=" font-size: 14px;color: #EF3F35">{{ changeData(item.inc) }}<i
|
||||||
class="el-icon-caret-top"></i></span>
|
class="el-icon-caret-top"></i></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,6 +44,11 @@
|
|||||||
@click="goToCustManager(item.itemNm, 'fall')">
|
@click="goToCustManager(item.itemNm, 'fall')">
|
||||||
{{ item.curAmt }}<i class="el-icon-caret-bottom"></i>
|
{{ item.curAmt }}<i class="el-icon-caret-bottom"></i>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="item.curAmt == 0 || item.curAmt === '0'"
|
||||||
|
style="font-size: 14px; color: #409EFF; cursor: pointer;"
|
||||||
|
@click="goToCustManager(item.itemNm, 'rise')">
|
||||||
|
{{ item.curAmt }}
|
||||||
|
</span>
|
||||||
<span v-else
|
<span v-else
|
||||||
style="font-size: 14px; color: #EF3F35; cursor: pointer;"
|
style="font-size: 14px; color: #EF3F35; cursor: pointer;"
|
||||||
@click="goToCustManager(item.itemNm, 'rise')">
|
@click="goToCustManager(item.itemNm, 'rise')">
|
||||||
@@ -60,7 +66,7 @@
|
|||||||
<el-radio-button label="4">预警任务</el-radio-button>
|
<el-radio-button label="4">预警任务</el-radio-button>
|
||||||
<el-radio-button label="5">二次走访提醒</el-radio-button>
|
<el-radio-button label="5">二次走访提醒</el-radio-button>
|
||||||
<el-radio-button label="6">走访资源提醒</el-radio-button>
|
<el-radio-button label="6">走访资源提醒</el-radio-button>
|
||||||
<el-radio-button label="7">营销任务</el-radio-button>
|
<el-radio-button label="7" v-if="!shouldHideFor875">营销任务</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<el-table v-if="selectedTab==='3'" key="3" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
<el-table v-if="selectedTab==='3'" key="3" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
||||||
@@ -152,7 +158,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-table v-if="selectedTab==='7'" key="7" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
<el-table v-if="selectedTab==='7' && !shouldHideFor875" key="7" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
||||||
<el-table-column label="营销任务" width="200" prop="marketTaskName" min-width="100" show-overflow-tooltip />
|
<el-table-column label="营销任务" width="200" prop="marketTaskName" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="客户姓名" prop="custName" min-width="100" show-overflow-tooltip />
|
<el-table-column label="客户姓名" prop="custName" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="客户号" width="200" prop="custId" min-width="140" show-overflow-tooltip />
|
<el-table-column label="客户号" width="200" prop="custId" min-width="140" show-overflow-tooltip />
|
||||||
@@ -194,9 +200,9 @@
|
|||||||
<el-table-column label="结束时间" width="200" prop="endTime" min-width="100" show-overflow-tooltip />
|
<el-table-column label="结束时间" width="200" prop="endTime" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination @size-change="handleAgentSizeChange" @current-change="handleAgentCurrentChange"
|
<el-pagination @current-change="handleAgentCurrentChange"
|
||||||
class="warnPagination" :page-sizes="[5, 10, 20, 30]" :page-size="agentPageSize"
|
class="warnPagination" :page-size="5"
|
||||||
layout="->,total,sizes,prev,pager,next" :total="agentTotal" :current-page="agentPageNum"></el-pagination>
|
layout="->,total,prev,pager,next" :total="agentTotal" :current-page="agentPageNum"></el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-vr">
|
<div class="page-vr">
|
||||||
@@ -210,7 +216,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<ul class="operate-cnt">
|
<ul class="operate-cnt">
|
||||||
<li v-for="(it, ind) in optArr" :key="ind" class="setLi" @mouseenter="onMouseOver(ind, true)"
|
<li v-for="(it, ind) in filteredOptArr" :key="ind" class="setLi" @mouseenter="onMouseOver(ind, true)"
|
||||||
@mouseleave="onMouseOver(ind, false)">
|
@mouseleave="onMouseOver(ind, false)">
|
||||||
<div class="noSetting" v-if="it.setShow">
|
<div class="noSetting" v-if="it.setShow">
|
||||||
<svg-icon :icon-class="getCurrentImg(ind)" :class="isSetting ? 'svg-icon-imgSetting' : 'svg-icon-img'"
|
<svg-icon :icon-class="getCurrentImg(ind)" :class="isSetting ? 'svg-icon-imgSetting' : 'svg-icon-img'"
|
||||||
@@ -230,7 +236,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-vr-middle page-common-wrap no-padding-cnt page-vr-top" id="yjxxMain">
|
<div class="page-vr-middle page-common-wrap no-padding-cnt page-vr-top" id="yjxxMain" v-if="!shouldHideFor875">
|
||||||
<p class="page-title page-btm">预警信息</p>
|
<p class="page-title page-btm">预警信息</p>
|
||||||
<ul class="common-ul">
|
<ul class="common-ul">
|
||||||
<li v-for="(it, ind) in warnArr" :key="ind" style="cursor: pointer;" @click="handleWarn(it)" class="yjxxLi">
|
<li v-for="(it, ind) in warnArr" :key="ind" style="cursor: pointer;" @click="handleWarn(it)" class="yjxxLi">
|
||||||
@@ -857,6 +863,23 @@ export default {
|
|||||||
},
|
},
|
||||||
showposition() {
|
showposition() {
|
||||||
return this.userName.slice(0, 3) === '875'
|
return this.userName.slice(0, 3) === '875'
|
||||||
|
},
|
||||||
|
// headId为875时隐藏预警信息和营销任务
|
||||||
|
shouldHideFor875() {
|
||||||
|
return this.userName && (this.userName + '').substring(0, 3) === '875'
|
||||||
|
},
|
||||||
|
// headId为875时,便捷操作只显示快速入门
|
||||||
|
filteredOptArr() {
|
||||||
|
return this.optArr
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedTab(newVal, oldVal) {
|
||||||
|
// 切换tab时重置分页为第1页
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.agentPageNum = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -1135,8 +1158,7 @@ export default {
|
|||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
handleAgentCurrentChange(val) {
|
handleAgentCurrentChange(val) {
|
||||||
// this.agentPageNum = val
|
this.agentPageNum = val
|
||||||
// this.initBranchList()
|
|
||||||
this.pageNum = val
|
this.pageNum = val
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
@@ -1398,10 +1420,9 @@ export default {
|
|||||||
handleSaveSetting() {
|
handleSaveSetting() {
|
||||||
let arr = this.optArr.map(item => {
|
let arr = this.optArr.map(item => {
|
||||||
return item.name
|
return item.name
|
||||||
})
|
}).filter(name => name && name.trim() !== '') // 过滤掉空值
|
||||||
console.log(arr, 'arrarrarr')
|
console.log(arr, 'arrarrarr')
|
||||||
updateQuickSelect(arr).then(res => {
|
updateQuickSelect(arr).then(res => {
|
||||||
|
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
Message.success(res.data)
|
Message.success(res.data)
|
||||||
this.handleSetting()
|
this.handleSetting()
|
||||||
@@ -1565,6 +1586,8 @@ p {
|
|||||||
.page-vr {
|
.page-vr {
|
||||||
width: 24%;
|
width: 24%;
|
||||||
margin-left: 1%;
|
margin-left: 1%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
@@ -1602,7 +1625,11 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-vr-middle {
|
.page-vr-middle {
|
||||||
margin: 22px 0;
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-vr-top {
|
||||||
|
margin-bottom: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-vl-top {
|
.page-vl-top {
|
||||||
@@ -1873,7 +1900,7 @@ p {
|
|||||||
.page-vr-btm {
|
.page-vr-btm {
|
||||||
height: 350px;
|
height: 350px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-badge__content {
|
::v-deep .el-badge__content {
|
||||||
@@ -1943,8 +1970,10 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.url-box {
|
.url-box {
|
||||||
max-height: 210px;
|
flex: 1;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yjxxLi {
|
.yjxxLi {
|
||||||
|
|||||||
@@ -83,6 +83,11 @@
|
|||||||
<dict-tag :options="dict.type.sys_notice_type" :value="scope.row.noticeType"/>
|
<dict-tag :options="dict.type.sys_notice_type" :value="scope.row.noticeType"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="可见总行" align="center" min-width="180" :show-overflow-tooltip="true">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatHeadNames(scope.row.deptIds) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.sys_notice_status" :value="scope.row.status"/>
|
<dict-tag :options="dict.type.sys_notice_status" :value="scope.row.status"/>
|
||||||
@@ -154,6 +159,25 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="可见总行" prop="deptIds">
|
||||||
|
<el-select
|
||||||
|
v-model="headList"
|
||||||
|
placeholder="请选择可见总行,不选则全员可见"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%;"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in headOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="内容">
|
<el-form-item label="内容">
|
||||||
<editor v-model="form.noticeContent" :min-height="192"/>
|
<editor v-model="form.noticeContent" :min-height="192"/>
|
||||||
@@ -170,7 +194,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
|
import { listNotice, getNotice, delNotice, addNotice, updateNotice, getHeadList } from "@/api/system/notice";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Notice",
|
name: "Notice",
|
||||||
@@ -191,6 +215,10 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
// 公告表格数据
|
// 公告表格数据
|
||||||
noticeList: [],
|
noticeList: [],
|
||||||
|
// 总行选项
|
||||||
|
headOptions: [],
|
||||||
|
// 已选总行
|
||||||
|
headList: [],
|
||||||
// 弹出层标题
|
// 弹出层标题
|
||||||
title: "",
|
title: "",
|
||||||
// 是否显示弹出层
|
// 是否显示弹出层
|
||||||
@@ -218,6 +246,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getList();
|
this.getList();
|
||||||
|
this.getHeadOptions();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** 查询公告列表 */
|
/** 查询公告列表 */
|
||||||
@@ -229,6 +258,16 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getHeadOptions() {
|
||||||
|
getHeadList().then(response => {
|
||||||
|
if (response.code === 200 && Array.isArray(response.data)) {
|
||||||
|
this.headOptions = response.data.map(item => ({
|
||||||
|
label: item.deptName,
|
||||||
|
value: String(item.deptId)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
cancel() {
|
cancel() {
|
||||||
this.open = false;
|
this.open = false;
|
||||||
@@ -241,8 +280,10 @@ export default {
|
|||||||
noticeTitle: undefined,
|
noticeTitle: undefined,
|
||||||
noticeType: undefined,
|
noticeType: undefined,
|
||||||
noticeContent: undefined,
|
noticeContent: undefined,
|
||||||
|
deptIds: undefined,
|
||||||
status: "0"
|
status: "0"
|
||||||
};
|
};
|
||||||
|
this.headList = [];
|
||||||
this.resetForm("form");
|
this.resetForm("form");
|
||||||
},
|
},
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
@@ -273,6 +314,7 @@ export default {
|
|||||||
const noticeId = row.noticeId || this.ids
|
const noticeId = row.noticeId || this.ids
|
||||||
getNotice(noticeId).then(response => {
|
getNotice(noticeId).then(response => {
|
||||||
this.form = response.data;
|
this.form = response.data;
|
||||||
|
this.headList = response.data.deptIds ? response.data.deptIds.split(',').filter(Boolean) : [];
|
||||||
this.open = true;
|
this.open = true;
|
||||||
this.title = "修改公告";
|
this.title = "修改公告";
|
||||||
});
|
});
|
||||||
@@ -281,6 +323,7 @@ export default {
|
|||||||
submitForm: function() {
|
submitForm: function() {
|
||||||
this.$refs["form"].validate(valid => {
|
this.$refs["form"].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
this.form.deptIds = Array.isArray(this.headList) && this.headList.length > 0 ? this.headList.join(',') : '';
|
||||||
if (this.form.noticeId != undefined) {
|
if (this.form.noticeId != undefined) {
|
||||||
updateNotice(this.form).then(response => {
|
updateNotice(this.form).then(response => {
|
||||||
this.$modal.msgSuccess("修改成功");
|
this.$modal.msgSuccess("修改成功");
|
||||||
@@ -306,6 +349,20 @@ export default {
|
|||||||
this.getList();
|
this.getList();
|
||||||
this.$modal.msgSuccess("删除成功");
|
this.$modal.msgSuccess("删除成功");
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
},
|
||||||
|
formatHeadNames(deptIds) {
|
||||||
|
if (!deptIds) {
|
||||||
|
return '全员可见';
|
||||||
|
}
|
||||||
|
const selectedIds = String(deptIds).split(',').filter(Boolean);
|
||||||
|
if (!selectedIds.length) {
|
||||||
|
return '全员可见';
|
||||||
|
}
|
||||||
|
const nameMap = this.headOptions.reduce((map, item) => {
|
||||||
|
map[item.value] = item.label;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
return selectedIds.map(id => nameMap[id] || id).join('、');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,24 +6,46 @@
|
|||||||
v-model="selectedTab"
|
v-model="selectedTab"
|
||||||
@input="handleChange"
|
@input="handleChange"
|
||||||
>
|
>
|
||||||
<el-radio-button
|
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||||
label="2"
|
<el-radio-button
|
||||||
:disabled="isPrivate"
|
label="0"
|
||||||
:class="{ 'btn-disabled': isPrivate }"
|
:disabled="isPublic"
|
||||||
>企业</el-radio-button
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
>
|
>个人</el-radio-button
|
||||||
<el-radio-button
|
>
|
||||||
label="0"
|
<el-radio-button
|
||||||
:disabled="isPublic"
|
label="1"
|
||||||
:class="{ 'btn-disabled': isPublic }"
|
:disabled="isPublic"
|
||||||
>个人</el-radio-button
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
>
|
>商户</el-radio-button
|
||||||
<el-radio-button
|
>
|
||||||
label="1"
|
<el-radio-button
|
||||||
:disabled="isPublic"
|
label="2"
|
||||||
:class="{ 'btn-disabled': isPublic }"
|
:disabled="isPrivate"
|
||||||
>商户</el-radio-button
|
:class="{ 'btn-disabled': isPrivate }"
|
||||||
>
|
>企业</el-radio-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-radio-button
|
||||||
|
label="2"
|
||||||
|
:disabled="isPrivate"
|
||||||
|
:class="{ 'btn-disabled': isPrivate }"
|
||||||
|
>企业</el-radio-button
|
||||||
|
>
|
||||||
|
<el-radio-button
|
||||||
|
label="0"
|
||||||
|
:disabled="isPublic"
|
||||||
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
|
>个人</el-radio-button
|
||||||
|
>
|
||||||
|
<el-radio-button
|
||||||
|
label="1"
|
||||||
|
:disabled="isPublic"
|
||||||
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
|
>商户</el-radio-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<div class="searchForm">
|
<div class="searchForm">
|
||||||
<el-form
|
<el-form
|
||||||
@@ -41,6 +63,15 @@
|
|||||||
@clear="handleSearch"
|
@clear="handleSearch"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="柜员号" class="staff-id-filter">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.visId"
|
||||||
|
placeholder="请输入"
|
||||||
|
@blur="handleSearch"
|
||||||
|
clearable
|
||||||
|
@clear="handleSearch"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="走访时间">
|
<el-form-item label="走访时间">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="searchForm.visTime"
|
v-model="searchForm.visTime"
|
||||||
@@ -340,6 +371,23 @@
|
|||||||
width="150px"
|
width="150px"
|
||||||
v-if="columns[16].visible"
|
v-if="columns[16].visible"
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
|
<el-table-column align="left" prop="interAddr" label="实地拜访地址" show-overflow-tooltip width="180px" v-if="columns[17].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="colStafName" label="协同走访客户经理" show-overflow-tooltip width="160px" v-if="columns[18].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="laterNote" label="事后备注" show-overflow-tooltip width="180px" v-if="columns[19].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="intentionProductValue" label="走访反馈" show-overflow-tooltip width="160px" v-if="columns[20].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="feedbackStatus" label="反馈状态" show-overflow-tooltip width="120px" v-if="columns[21].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="sourceOfInterview" label="走访来源" show-overflow-tooltip width="140px" v-if="columns[22].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="filename" label="批量导入文件名" show-overflow-tooltip width="180px" v-if="columns[23].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="outCallStatus" label="外呼状态" show-overflow-tooltip width="120px" v-if="columns[24].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="outCallIntention" label="客户意愿" show-overflow-tooltip width="140px" v-if="columns[25].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="source" label="走访渠道" show-overflow-tooltip width="120px" v-if="columns[26].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="analysisValue" label="nlp模型提取" show-overflow-tooltip width="140px" v-if="columns[27].visible"></el-table-column>
|
||||||
|
<el-table-column align="left" prop="facility" label="预授信额度" show-overflow-tooltip width="140px" v-if="columns[28].visible"></el-table-column>
|
||||||
|
<el-table-column align="center" label="操作" fixed="right" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="handleEditFeedback(scope.row)">编辑</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@@ -350,24 +398,90 @@
|
|||||||
:total="total"
|
:total="total"
|
||||||
:current-page="pageNum"
|
:current-page="pageNum"
|
||||||
></el-pagination>
|
></el-pagination>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
title="编辑走访反馈"
|
||||||
|
:visible.sync="feedbackDialogVisible"
|
||||||
|
width="960px"
|
||||||
|
custom-class="feedback-dialog"
|
||||||
|
append-to-body
|
||||||
|
@close="resetFeedbackForm"
|
||||||
|
>
|
||||||
|
<el-form ref="feedbackFormRef" :model="feedbackForm" label-width="100px" class="feedback-form">
|
||||||
|
<el-form-item label="走访渠道" required>
|
||||||
|
<el-radio-group v-model="feedbackForm.source">
|
||||||
|
<el-radio v-for="item in sourceOptions" :key="item" :label="item">{{ item }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户意愿" required>
|
||||||
|
<div class="feedback-groups">
|
||||||
|
<section v-for="type in feedbackTypeOptions" :key="type" class="feedback-group">
|
||||||
|
<div class="feedback-group__title">{{ type }}</div>
|
||||||
|
<el-checkbox-group v-model="feedbackForm.feedbackSelections[type]">
|
||||||
|
<el-checkbox
|
||||||
|
v-for="product in feedbackProductOptions"
|
||||||
|
:key="`${type}-${product}`"
|
||||||
|
:label="product"
|
||||||
|
>
|
||||||
|
{{ product }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预览结果">
|
||||||
|
<div class="feedback-preview">{{ feedbackPreview || "-" }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button @click="feedbackDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="feedbackSubmitting" @click="handleSubmitFeedback">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
import { getPADVisitRecord } from "@/api/task/PADvisitRecord.js";
|
import { getPADVisitRecord, updatePADVisitFeedback } from "@/api/task/PADvisitRecord.js";
|
||||||
import { Message } from "element-ui";
|
|
||||||
|
const SOURCE_OPTIONS = ["企业微信", "PAD"];
|
||||||
|
const FEEDBACK_TYPE_OPTIONS = ["拒绝", "考虑", "意愿", "其他", "愿意", "现场办理"];
|
||||||
|
const FEEDBACK_PRODUCT_OPTIONS = [
|
||||||
|
"丰收互联",
|
||||||
|
"贷款",
|
||||||
|
"电子社保卡签约",
|
||||||
|
"基金",
|
||||||
|
"贵金属",
|
||||||
|
"信用卡",
|
||||||
|
"医保电子凭证",
|
||||||
|
"社保卡",
|
||||||
|
"理财签约"
|
||||||
|
];
|
||||||
|
|
||||||
|
function createEmptyFeedbackSelections() {
|
||||||
|
return FEEDBACK_TYPE_OPTIONS.reduce((result, item) => {
|
||||||
|
result[item] = [];
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedTab: "0",
|
selectedTab: "0",
|
||||||
tableData: [],
|
tableData: [],
|
||||||
Loading: false,
|
Loading: false,
|
||||||
|
feedbackDialogVisible: false,
|
||||||
|
feedbackSubmitting: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
|
sourceOptions: SOURCE_OPTIONS,
|
||||||
|
feedbackTypeOptions: FEEDBACK_TYPE_OPTIONS,
|
||||||
|
feedbackProductOptions: FEEDBACK_PRODUCT_OPTIONS,
|
||||||
searchForm: {
|
searchForm: {
|
||||||
visName: "",
|
visName: "",
|
||||||
|
visId: "",
|
||||||
visTime: "",
|
visTime: "",
|
||||||
custIdc: "",
|
custIdc: "",
|
||||||
socialCreditCode: '',
|
socialCreditCode: '',
|
||||||
@@ -393,16 +507,36 @@ export default {
|
|||||||
{ key: 13, label: "签到坐标", visible: true },
|
{ key: 13, label: "签到坐标", visible: true },
|
||||||
{ key: 14, label: "签退坐标", visible: true },
|
{ key: 14, label: "签退坐标", visible: true },
|
||||||
{ key: 15, label: "是否为有效客户", visible: true },
|
{ key: 15, label: "是否为有效客户", visible: true },
|
||||||
{ key: 16, label: "走访备注", visible: true }
|
{ key: 16, label: "走访备注", visible: true },
|
||||||
|
{ key: 17, label: "实地拜访地址", visible: true },
|
||||||
|
{ key: 18, label: "协同走访客户经理", visible: true },
|
||||||
|
{ key: 19, label: "事后备注", visible: true },
|
||||||
|
{ key: 20, label: "走访反馈", visible: true },
|
||||||
|
{ key: 21, label: "反馈状态", visible: true },
|
||||||
|
{ key: 22, label: "走访来源", visible: true },
|
||||||
|
{ key: 23, label: "批量导入文件名", visible: true },
|
||||||
|
{ key: 24, label: "外呼状态", visible: true },
|
||||||
|
{ key: 25, label: "客户意愿", visible: true },
|
||||||
|
{ key: 26, label: "走访渠道", visible: true },
|
||||||
|
{ key: 27, label: "nlp模型提取", visible: true },
|
||||||
|
{ key: 28, label: "预授信额度", visible: true }
|
||||||
],
|
],
|
||||||
columns875: [
|
columns875: [
|
||||||
{ key: 17, label: "异常走访标签", visible: true },
|
{ key: 29, label: "异常走访标签", visible: true },
|
||||||
{ key: 18, label: "异常走访信息", visible: true },
|
{ key: 30, label: "异常走访信息", visible: true },
|
||||||
]
|
],
|
||||||
|
feedbackForm: {
|
||||||
|
id: null,
|
||||||
|
source: "",
|
||||||
|
feedbackSelections: createEmptyFeedbackSelections()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["roles", "userName"]),
|
...mapGetters(["roles", "userName"]),
|
||||||
|
deptId() {
|
||||||
|
return this.$store.state.user.deptId
|
||||||
|
},
|
||||||
//总行
|
//总行
|
||||||
isHeadAdmin() {
|
isHeadAdmin() {
|
||||||
return this.roles.includes("headAdmin");
|
return this.roles.includes("headAdmin");
|
||||||
@@ -434,6 +568,9 @@ export default {
|
|||||||
// 海宁
|
// 海宁
|
||||||
is875() {
|
is875() {
|
||||||
return this.userName.slice(0, 3) === '875'
|
return this.userName.slice(0, 3) === '875'
|
||||||
|
},
|
||||||
|
feedbackPreview() {
|
||||||
|
return this.buildFeedbackValue(this.feedbackForm.feedbackSelections)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -441,17 +578,93 @@ export default {
|
|||||||
if (selectedTab) {
|
if (selectedTab) {
|
||||||
this.selectedTab = selectedTab;
|
this.selectedTab = selectedTab;
|
||||||
} else {
|
} else {
|
||||||
|
// 默认选中第一个tab
|
||||||
|
const deptId = this.$store.state.user.deptId
|
||||||
|
const deptIdStr = deptId ? String(deptId) : ''
|
||||||
|
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||||
|
|
||||||
if (this.isPublic) {
|
if (this.isPublic) {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = '2'
|
||||||
} else if (this.isPrivate) {
|
} else if (this.isPrivate) {
|
||||||
this.selectedTab = '0'
|
this.selectedTab = defaultTab
|
||||||
} else {
|
} else {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = defaultTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.initVisitingTaskList();
|
this.initVisitingTaskList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
resetFeedbackForm() {
|
||||||
|
this.feedbackSubmitting = false;
|
||||||
|
this.feedbackForm = {
|
||||||
|
id: null,
|
||||||
|
source: "",
|
||||||
|
feedbackSelections: createEmptyFeedbackSelections()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parseFeedbackValue(value) {
|
||||||
|
const feedbackSelections = createEmptyFeedbackSelections();
|
||||||
|
if (!value) {
|
||||||
|
return feedbackSelections;
|
||||||
|
}
|
||||||
|
value.split(";").forEach((segment) => {
|
||||||
|
const [type, products] = segment.split(":");
|
||||||
|
if (!type || !feedbackSelections[type]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
feedbackSelections[type] = (products || "")
|
||||||
|
.split(",")
|
||||||
|
.map(item => item && item.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
});
|
||||||
|
return feedbackSelections;
|
||||||
|
},
|
||||||
|
buildFeedbackItems(feedbackSelections) {
|
||||||
|
return this.feedbackTypeOptions
|
||||||
|
.map((type) => ({
|
||||||
|
feedbackType: type,
|
||||||
|
products: (feedbackSelections[type] || []).filter(Boolean)
|
||||||
|
}))
|
||||||
|
.filter(item => item.products.length > 0);
|
||||||
|
},
|
||||||
|
buildFeedbackValue(feedbackSelections) {
|
||||||
|
return this.buildFeedbackItems(feedbackSelections)
|
||||||
|
.map(item => `${item.feedbackType}:${item.products.join(",")}`)
|
||||||
|
.join(";");
|
||||||
|
},
|
||||||
|
handleEditFeedback(row) {
|
||||||
|
this.feedbackForm = {
|
||||||
|
id: row.id,
|
||||||
|
source: row.source || "",
|
||||||
|
feedbackSelections: this.parseFeedbackValue(row.intentionProductValue)
|
||||||
|
};
|
||||||
|
this.feedbackDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleSubmitFeedback() {
|
||||||
|
const feedbackItems = this.buildFeedbackItems(this.feedbackForm.feedbackSelections);
|
||||||
|
if (!this.feedbackForm.source) {
|
||||||
|
this.$message.warning("请选择走访渠道");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!feedbackItems.length) {
|
||||||
|
this.$message.warning("请至少选择一组客户意愿和营销产品");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const payload = {
|
||||||
|
id: this.feedbackForm.id,
|
||||||
|
source: this.feedbackForm.source || null,
|
||||||
|
feedbackItems
|
||||||
|
};
|
||||||
|
this.feedbackSubmitting = true;
|
||||||
|
updatePADVisitFeedback(payload).then(() => {
|
||||||
|
this.$message.success("保存成功");
|
||||||
|
this.feedbackDialogVisible = false;
|
||||||
|
this.resetFeedbackForm();
|
||||||
|
this.initVisitingTaskList();
|
||||||
|
}).finally(() => {
|
||||||
|
this.feedbackSubmitting = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
handleChange(val) {
|
handleChange(val) {
|
||||||
this.pageSize = 10;
|
this.pageSize = 10;
|
||||||
this.pageNum = 1;
|
this.pageNum = 1;
|
||||||
@@ -462,6 +675,7 @@ export default {
|
|||||||
custType: this.selectedTab,
|
custType: this.selectedTab,
|
||||||
visTime: this.searchForm.visTime,
|
visTime: this.searchForm.visTime,
|
||||||
visName: this.searchForm.visName,
|
visName: this.searchForm.visName,
|
||||||
|
visId: this.searchForm.visId,
|
||||||
custIdc: this.searchForm.custIdc,
|
custIdc: this.searchForm.custIdc,
|
||||||
socialCreditCode: this.searchForm.socialCreditCode,
|
socialCreditCode: this.searchForm.socialCreditCode,
|
||||||
abnormalVisitTag: this.searchForm.abnormalVisitTag,
|
abnormalVisitTag: this.searchForm.abnormalVisitTag,
|
||||||
@@ -478,7 +692,16 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleRefersh() {
|
handleRefersh() {
|
||||||
this.searchForm = {};
|
this.searchForm = {
|
||||||
|
visName: "",
|
||||||
|
visId: "",
|
||||||
|
visTime: "",
|
||||||
|
custIdc: "",
|
||||||
|
socialCreditCode: '',
|
||||||
|
abnormalVisitTag: '',
|
||||||
|
marketingWay: '',
|
||||||
|
interRes: ''
|
||||||
|
};
|
||||||
this.initVisitingTaskList();
|
this.initVisitingTaskList();
|
||||||
},
|
},
|
||||||
handleSizeChange(newSize) {
|
handleSizeChange(newSize) {
|
||||||
@@ -493,7 +716,16 @@ export default {
|
|||||||
this.initVisitingTaskList();
|
this.initVisitingTaskList();
|
||||||
},
|
},
|
||||||
resetFilters() {
|
resetFilters() {
|
||||||
this.searchForm = {};
|
this.searchForm = {
|
||||||
|
visName: "",
|
||||||
|
visId: "",
|
||||||
|
visTime: "",
|
||||||
|
custIdc: "",
|
||||||
|
socialCreditCode: '',
|
||||||
|
abnormalVisitTag: '',
|
||||||
|
marketingWay: '',
|
||||||
|
interRes: ''
|
||||||
|
};
|
||||||
this.initVisitingTaskList();
|
this.initVisitingTaskList();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -535,67 +767,6 @@ export default {
|
|||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
height: 21px;
|
|
||||||
width: 1px;
|
|
||||||
background: #ebebeb;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
right: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-disabled {
|
.btn-disabled {
|
||||||
::v-deep .el-radio-button__inner {
|
::v-deep .el-radio-button__inner {
|
||||||
background-color: #e7e7e7;
|
background-color: #e7e7e7;
|
||||||
@@ -617,7 +788,6 @@ export default {
|
|||||||
|
|
||||||
.el-radio-button {
|
.el-radio-button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid #ccc;
|
|
||||||
|
|
||||||
::v-deep .el-radio-button__inner {
|
::v-deep .el-radio-button__inner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -695,6 +865,11 @@ export default {
|
|||||||
|
|
||||||
.searchForm {
|
.searchForm {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.staff-id-filter {
|
||||||
|
clear: left;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.operate-cnt {
|
.operate-cnt {
|
||||||
@@ -726,6 +901,67 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feedback-form {
|
||||||
|
::v-deep .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label::before {
|
||||||
|
color: #f56c6c;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-groups {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-group {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-group__title {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-checkbox {
|
||||||
|
margin-right: 0;
|
||||||
|
min-width: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-preview {
|
||||||
|
min-height: 20px;
|
||||||
|
color: #606266;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .feedback-dialog {
|
||||||
|
border-radius: 18px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.el-dialog__header {
|
||||||
|
padding: 20px 24px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 12px 24px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding: 10px 24px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.quesiton {
|
.quesiton {
|
||||||
color: #b9b9b9;
|
color: #b9b9b9;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,9 +64,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<el-radio-group class="header-radio" v-model="selectedTab" @input="handleChange">
|
<el-radio-group class="header-radio" v-model="selectedTab" @input="handleChange">
|
||||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
<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>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||||
|
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||||
|
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||||
|
</template>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<!-- <div class="taskTop">
|
<!-- <div class="taskTop">
|
||||||
<div class="taskTop_left">
|
<div class="taskTop_left">
|
||||||
@@ -292,6 +299,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['roles']),
|
...mapGetters(['roles']),
|
||||||
|
deptId() {
|
||||||
|
return this.$store.state.user.deptId
|
||||||
|
},
|
||||||
//总行
|
//总行
|
||||||
isHeadAdmin() {
|
isHeadAdmin() {
|
||||||
return this.roles.includes('headAdmin')
|
return this.roles.includes('headAdmin')
|
||||||
@@ -334,12 +344,17 @@ export default {
|
|||||||
delete this.$route.query[key];
|
delete this.$route.query[key];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// 默认选中第一个tab
|
||||||
|
const deptId = this.$store.state.user.deptId
|
||||||
|
const deptIdStr = deptId ? String(deptId) : ''
|
||||||
|
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||||
|
|
||||||
if (this.isPublic) {
|
if (this.isPublic) {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = '2'
|
||||||
} else if (this.isPrivate) {
|
} else if (this.isPrivate) {
|
||||||
this.selectedTab = '0'
|
this.selectedTab = defaultTab
|
||||||
} else {
|
} else {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = defaultTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.isBranchAdmin) {
|
if (this.isBranchAdmin) {
|
||||||
|
|||||||
@@ -6,21 +6,40 @@
|
|||||||
class="header-radio"
|
class="header-radio"
|
||||||
@input="handleChange"
|
@input="handleChange"
|
||||||
>
|
>
|
||||||
<el-radio-button
|
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||||
label="2"
|
<el-radio-button
|
||||||
:disabled="isPrivate"
|
label="0"
|
||||||
:class="{ 'btn-disabled': isPrivate }"
|
:disabled="isPublic"
|
||||||
>企业</el-radio-button>
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
<el-radio-button
|
>个人</el-radio-button>
|
||||||
label="0"
|
<el-radio-button
|
||||||
:disabled="isPublic"
|
label="1"
|
||||||
:class="{ 'btn-disabled': isPublic }"
|
:disabled="isPublic"
|
||||||
>个人</el-radio-button>
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
<el-radio-button
|
>商户</el-radio-button>
|
||||||
label="1"
|
<el-radio-button
|
||||||
:disabled="isPublic"
|
label="2"
|
||||||
:class="{ 'btn-disabled': isPublic }"
|
:disabled="isPrivate"
|
||||||
>商户</el-radio-button>
|
:class="{ 'btn-disabled': isPrivate }"
|
||||||
|
>企业</el-radio-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-radio-button
|
||||||
|
label="2"
|
||||||
|
:disabled="isPrivate"
|
||||||
|
:class="{ 'btn-disabled': isPrivate }"
|
||||||
|
>企业</el-radio-button>
|
||||||
|
<el-radio-button
|
||||||
|
label="0"
|
||||||
|
:disabled="isPublic"
|
||||||
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
|
>个人</el-radio-button>
|
||||||
|
<el-radio-button
|
||||||
|
label="1"
|
||||||
|
:disabled="isPublic"
|
||||||
|
:class="{ 'btn-disabled': isPublic }"
|
||||||
|
>商户</el-radio-button>
|
||||||
|
</template>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<div class="taskTop">
|
<div class="taskTop">
|
||||||
<div class="taskTop_left">
|
<div class="taskTop_left">
|
||||||
@@ -1180,6 +1199,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['roles']),
|
...mapGetters(['roles']),
|
||||||
|
deptId() {
|
||||||
|
return this.$store.state.user.deptId
|
||||||
|
},
|
||||||
// 总行
|
// 总行
|
||||||
isHeadAdmin() {
|
isHeadAdmin() {
|
||||||
return this.roles.includes('headAdmin')
|
return this.roles.includes('headAdmin')
|
||||||
@@ -1215,17 +1237,22 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
// getGroupInfoByGroupId({})
|
// getGroupInfoByGroupId({})
|
||||||
this.isUserType()
|
this.isUserType()
|
||||||
|
// 根据deptId动态设置默认tab:默认选中第一个tab
|
||||||
|
const deptId = this.$store.state.user.deptId
|
||||||
|
const deptIdStr = deptId ? String(deptId) : ''
|
||||||
|
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||||
|
|
||||||
if (this.isPublic) {
|
if (this.isPublic) {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = '2'
|
||||||
this.custTypeList = [{ label: '企业', value: '2' }]
|
this.custTypeList = [{ label: '企业', value: '2' }]
|
||||||
} else if (this.isPrivate) {
|
} else if (this.isPrivate) {
|
||||||
this.selectedTab = '0'
|
this.selectedTab = defaultTab
|
||||||
this.custTypeList = [
|
this.custTypeList = [
|
||||||
{ label: '个人', value: '0' },
|
{ label: '个人', value: '0' },
|
||||||
{ label: '商户', value: '1' }
|
{ label: '商户', value: '1' }
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
this.selectedTab = '2'
|
this.selectedTab = defaultTab
|
||||||
this.custTypeList = [
|
this.custTypeList = [
|
||||||
{ label: '个人', value: '0' },
|
{ label: '个人', value: '0' },
|
||||||
{ label: '商户', value: '1' },
|
{ label: '商户', value: '1' },
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container" style="padding: 0px !important;">
|
<div class="app-container" style="padding: 0px !important;">
|
||||||
<el-date-picker
|
<el-row :gutter="24" style="padding: 0 30px;">
|
||||||
v-model="reportTime"
|
|
||||||
placeholder="请选择日期"
|
|
||||||
@change="initCardList"
|
|
||||||
class="time-picker"
|
|
||||||
/>
|
|
||||||
<el-row :gutter="24" style="padding: 0 30px;margin-left: -15px;">
|
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-card class="box-card">
|
<el-card class="stat-card stat-card-blue">
|
||||||
<div class="my-span-checklist-title">
|
<div class="stat-icon">
|
||||||
总预警推送次数
|
<i class="el-icon-bell"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-span-checklist-main">
|
<div class="stat-content">
|
||||||
<span>{{ cardInfo.alterCount }}</span>
|
<div class="stat-title">总预警推送次数</div>
|
||||||
|
<div class="stat-value">{{ cardInfo.alterCount }}</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-card class="box-card1">
|
<el-card class="stat-card stat-card-blue2">
|
||||||
<div class="my-span-checklist-title">
|
<div class="stat-icon">
|
||||||
反馈完成率
|
<i class="el-icon-data-analysis"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-span-checklist-main">
|
<div class="stat-content">
|
||||||
<span>{{ cardInfo.completeRate + '%' }}</span>
|
<div class="stat-title">反馈完成率</div>
|
||||||
|
<div class="stat-value">{{ cardInfo.completeRate + '%' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -35,6 +31,14 @@
|
|||||||
:inline="true"
|
:inline="true"
|
||||||
style="margin-top:20px;"
|
style="margin-top:20px;"
|
||||||
>
|
>
|
||||||
|
<el-form-item label="日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="reportTime"
|
||||||
|
placeholder="请选择日期"
|
||||||
|
@change="initCardList"
|
||||||
|
style="width:100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="searchArray.status" placeholder="请选择状态" style="width: 100%" clearable>
|
<el-select v-model="searchArray.status" placeholder="请选择状态" style="width: 100%" clearable>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -46,20 +50,26 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="预警类型" prop="alterType">
|
<el-form-item label="预警类型" prop="alterType">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="searchArray.alterType"
|
v-model="searchArray.alterType"
|
||||||
placeholder="请输入预警类型"
|
placeholder="请选择预警类型"
|
||||||
clearable
|
clearable
|
||||||
style="width:100%"
|
style="width:100%"
|
||||||
>
|
>
|
||||||
</el-input>
|
<el-option
|
||||||
|
v-for="item in alterTypeOptions"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="searchFn">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="searchFn">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetFn">重置</el-button>
|
<el-button icon="el-icon-refresh" size="mini" @click="resetFn">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table v-loading="loading" :data="tableData" :height="dyHeight" :key="tableKey">
|
<el-table v-loading="loading" :data="tableData">
|
||||||
<template>
|
<template>
|
||||||
<el-table-column label="序号" prop="xh" width="80">
|
<el-table-column label="序号" prop="xh" width="80">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
@@ -91,17 +101,15 @@
|
|||||||
</el-table-column> -->
|
</el-table-column> -->
|
||||||
</template>
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="pagination_end">
|
<el-pagination
|
||||||
<el-pagination
|
:page-sizes="[10, 20, 30, 50]"
|
||||||
layout="total, prev, pager, next, jumper"
|
:page-size="pageSize"
|
||||||
:current-page="pageNum"
|
layout="->,total,prev,pager,next,sizes"
|
||||||
:page-size="pageSize"
|
:total="total"
|
||||||
:page-sizes="[10, 20, 30, 50]"
|
:current-page="pageNum"
|
||||||
@current-change="currentChangeFn"
|
@current-change="currentChangeFn"
|
||||||
:total="total"
|
@size-change="sizeChangeFn"
|
||||||
@size-change="sizeChangeFn"
|
></el-pagination>
|
||||||
></el-pagination>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:title="dialogTitle"
|
:title="dialogTitle"
|
||||||
@@ -199,7 +207,8 @@
|
|||||||
import {
|
import {
|
||||||
warningworkRecordList,
|
warningworkRecordList,
|
||||||
warningworkRecordSubmit,
|
warningworkRecordSubmit,
|
||||||
warningCardNum
|
warningCardNum,
|
||||||
|
getAlterTypes
|
||||||
} from "@/api/system/home";
|
} from "@/api/system/home";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -243,6 +252,8 @@ export default {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
alterTypeOptions: [],
|
||||||
|
|
||||||
searchArray: {
|
searchArray: {
|
||||||
status: "",
|
status: "",
|
||||||
alterType: ""
|
alterType: ""
|
||||||
@@ -252,8 +263,6 @@ export default {
|
|||||||
tableData: [],
|
tableData: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
dyHeight: {},
|
|
||||||
tableKey: false,
|
|
||||||
dialogTitle: '',
|
dialogTitle: '',
|
||||||
dialogForm: {
|
dialogForm: {
|
||||||
custName: '',
|
custName: '',
|
||||||
@@ -277,12 +286,9 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
this.dyHeight = Number(window.innerHeight) - 330;
|
|
||||||
});
|
|
||||||
this.dyHeight = Number(window.innerHeight) - 330;
|
|
||||||
this.resetFn();
|
this.resetFn();
|
||||||
this.initCardList()
|
this.initCardList();
|
||||||
|
this.getAlterTypeList();
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
formatFilter(v, type, list) {
|
formatFilter(v, type, list) {
|
||||||
@@ -293,6 +299,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getAlterTypeList() {
|
||||||
|
getAlterTypes().then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.alterTypeOptions = res.data || [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
initCardList() {
|
initCardList() {
|
||||||
warningCardNum({ reportTime: this.reportTime ? dayjs(this.reportTime).format('YYYY-MM-DD') + ' 23:59:59' : '' }).then(res => {
|
warningCardNum({ reportTime: this.reportTime ? dayjs(this.reportTime).format('YYYY-MM-DD') + ' 23:59:59' : '' }).then(res => {
|
||||||
this.cardInfo = res
|
this.cardInfo = res
|
||||||
@@ -326,7 +339,6 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$message.error(response.msg || "操作失败");
|
this.$message.error(response.msg || "操作失败");
|
||||||
}
|
}
|
||||||
this.tableKey = !this.tableKey;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
currentChangeFn(val) {
|
currentChangeFn(val) {
|
||||||
@@ -369,45 +381,79 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pagination_end {
|
::v-deep .el-pagination {
|
||||||
margin-top: 20px;
|
margin-top: 15px;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-picker {
|
// 统计卡片样式
|
||||||
margin: 20px 0 0 0px;
|
.stat-card {
|
||||||
}
|
|
||||||
|
|
||||||
.box-card {
|
|
||||||
height: 100px;
|
height: 100px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
margin-left: -30px;
|
border: none;
|
||||||
border: 3px solid #a8c2f5;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.box-card1 {
|
// 左侧装饰条
|
||||||
height: 100px;
|
&::before {
|
||||||
border-radius: 8px;
|
content: '';
|
||||||
margin-top: 25px;
|
position: absolute;
|
||||||
border: 3px solid #a8c2f5;
|
left: 0;
|
||||||
}
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
.my-span-checklist-title {
|
.stat-icon {
|
||||||
font-size: 14px;
|
width: 50px;
|
||||||
color: #666;
|
height: 50px;
|
||||||
letter-spacing: 0.5px;
|
border-radius: 50%;
|
||||||
margin-bottom: 10px;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
margin-right: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
.my-span-checklist-main {
|
i {
|
||||||
line-height: 40px;
|
font-size: 22px;
|
||||||
font-size: 30px;
|
color: #fff;
|
||||||
color: #222;
|
}
|
||||||
font-weight: bold;
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.stat-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 蓝色渐变卡片1(左上深→右下浅)
|
||||||
|
&.stat-card-blue {
|
||||||
|
background: linear-gradient(135deg, #1e7ee6 0%, #a0cfff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 蓝色渐变卡片2(左上浅→右下深)
|
||||||
|
&.stat-card-blue2 {
|
||||||
|
background: linear-gradient(135deg, #a0cfff 0%, #1e7ee6 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.header-radio {
|
.header-radio {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user