Compare commits
15 Commits
5996173abd
...
23ff1b0b1d
| Author | SHA1 | Date | |
|---|---|---|---|
| 23ff1b0b1d | |||
| 44fd20316d | |||
| e3e26574c6 | |||
| 76c68cd544 | |||
| d897e12335 | |||
| 3eafd1b9e2 | |||
| 67e14363e3 | |||
| b15cc4fdcb | |||
| 4ec1544660 | |||
| c432b04075 | |||
| 8d2e5386ac | |||
| 7efc23dfd2 | |||
| 3c27b192a4 | |||
| aeda8f2fda | |||
| 1fb945a40f |
308
docs/plans/2026-02-28-featured-area-customer-view-design.md
Normal file
308
docs/plans/2026-02-28-featured-area-customer-view-design.md
Normal file
@@ -0,0 +1,308 @@
|
||||
# 特色区域详情窗口添加查看客户功能设计文档
|
||||
|
||||
## 文档信息
|
||||
|
||||
- **日期**: 2026-02-28
|
||||
- **作者**: Claude Code
|
||||
- **模块**: 区域绘制 - 特色区域
|
||||
- **需求来源**: 用户需求
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
数字支行辅助管理系统的区域绘制功能包含两种区域类型:
|
||||
1. **行政区域**(btnType=1):使用 index.vue 页面,区域详情窗口已有"查看客户"按钮
|
||||
2. **特色区域**(btnType=2):使用 BMapPolygonEditor.vue 组件,区域详情窗口缺少"查看客户"功能
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
在特色区域的区域详情窗口中,添加"查看客户"按钮,使功能与行政区域保持一致,方便用户快速查看该区域内的客户信息。
|
||||
|
||||
### 1.3 范围
|
||||
|
||||
**涉及模块:**
|
||||
- 前端:`ruoyi-ui/src/map/BMapPolygonEditor.vue`
|
||||
- 复用组件:`ruoyi-ui/src/views/grid/map/draw-area/customer-modal.vue`
|
||||
|
||||
**不涉及:**
|
||||
- 后端 API 修改
|
||||
- 数据库修改
|
||||
- 新增文件
|
||||
|
||||
## 2. 需求分析
|
||||
|
||||
### 2.1 用户需求
|
||||
|
||||
在区域绘制的特色区域详情窗口内,添加一个查看客户的按钮,功能与特色区域列表(menulist-modal)中的查看客户功能保持一致。
|
||||
|
||||
### 2.2 功能需求
|
||||
|
||||
#### 必须实现(MVP)
|
||||
- ✅ 在区域详情窗口的按钮区域添加"查看客户"图标按钮
|
||||
- ✅ 点击按钮后打开客户查看模态框
|
||||
- ✅ 模态框中显示该特色区域的企业、个人、商户客户列表
|
||||
- ✅ 支持客户类型切换(企业/个人/商户)
|
||||
- ✅ 支持分页查询
|
||||
- ✅ 支持点击客户名称跳转到客户详情页
|
||||
|
||||
#### 可选功能
|
||||
- 无
|
||||
|
||||
### 2.3 非功能需求
|
||||
|
||||
- **性能**: 不影响页面加载速度
|
||||
- **易用性**: 图标按钮风格与现有按钮保持一致
|
||||
- **兼容性**: 不影响现有功能
|
||||
- **可维护性**: 代码结构清晰,复用现有组件
|
||||
|
||||
## 3. 技术设计
|
||||
|
||||
### 3.1 整体架构
|
||||
|
||||
采用组件复用架构:
|
||||
- **展示层**: BMapPolygonEditor.vue(特色区域地图编辑器)
|
||||
- **组件层**: CustomerModal.vue(客户查看模态框,已存在)
|
||||
- **数据层**: API 接口(shapeCustList,已存在)
|
||||
|
||||
### 3.2 方案选择
|
||||
|
||||
经过方案对比分析,选择**方案1:添加图标按钮**
|
||||
|
||||
**方案对比:**
|
||||
|
||||
| 方案 | 优点 | 缺点 | 评分 |
|
||||
|------|------|------|------|
|
||||
| 方案1:图标按钮 | UI风格一致、改动最小、实现简单 | 图标不如文字明显 | ★★★★★ |
|
||||
| 方案2:文字按钮 | 按钮明显、文字清晰 | 可能破坏布局、需要调整样式 | ★★★☆☆ |
|
||||
| 方案3:替换布局 | 与行政区域一致 | 改动大、影响用户体验 | ★★☆☆☆ |
|
||||
|
||||
### 3.3 数据流程
|
||||
|
||||
```
|
||||
用户操作流程:
|
||||
用户点击特色区域
|
||||
↓
|
||||
显示区域详情窗口(area-info-modal)
|
||||
↓
|
||||
用户点击"查看客户"图标按钮
|
||||
↓
|
||||
触发 previewCustomer() 方法
|
||||
↓
|
||||
调用 this.$refs.customerModal.onOpen()
|
||||
↓
|
||||
CustomerModal 组件接收参数:
|
||||
- cardType="featured" (标识特色区域)
|
||||
- :detailInfo="areaForm" (包含 shapeId)
|
||||
- :btnType="'2'" (特色区域类型)
|
||||
↓
|
||||
CustomerModal 调用 shapeCustList API
|
||||
↓
|
||||
获取并展示客户列表数据
|
||||
↓
|
||||
用户可切换客户类型(企业/个人/商户)
|
||||
↓
|
||||
用户可点击客户名称查看详情
|
||||
```
|
||||
|
||||
### 3.4 接口设计
|
||||
|
||||
**使用现有接口:**
|
||||
|
||||
#### 获取特色区域客户列表
|
||||
- **接口**: `shapeCustList`
|
||||
- **文件**: `@/api/grid/draw-area.js`
|
||||
- **参数**:
|
||||
- shapeId: 区域ID
|
||||
- pageNum: 页码
|
||||
- pageSize: 每页条数
|
||||
- custType: 客户类型(0=个人,1=商户,2=企业)
|
||||
- **返回**: 客户列表数据
|
||||
|
||||
**无需新增接口。**
|
||||
|
||||
### 3.5 组件设计
|
||||
|
||||
#### BMapPolygonEditor.vue 修改
|
||||
|
||||
**1. 引入组件**
|
||||
|
||||
```javascript
|
||||
import CustomerModal from "@/views/grid/map/draw-area/customer-modal.vue"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// ... 现有组件
|
||||
CustomerModal,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**2. 添加组件引用**
|
||||
|
||||
在 template 末尾(约第273行之后)添加:
|
||||
|
||||
```vue
|
||||
<customer-modal
|
||||
ref="customerModal"
|
||||
cardType="featured"
|
||||
:detailInfo="areaForm"
|
||||
:btnType="'2'"
|
||||
/>
|
||||
```
|
||||
|
||||
**3. 添加图标按钮**
|
||||
|
||||
在区域详情窗口的按钮区域(第221-270行),建议位置在"修改信息"按钮之后:
|
||||
|
||||
```vue
|
||||
<el-tooltip placement="top" effect="light" content="查看客户">
|
||||
<i
|
||||
class="el-icon-user icon-area"
|
||||
@click.stop="previewCustomer"
|
||||
/>
|
||||
</el-tooltip>
|
||||
```
|
||||
|
||||
**4. 添加方法**
|
||||
|
||||
```javascript
|
||||
methods: {
|
||||
// ... 现有方法
|
||||
|
||||
/**
|
||||
* 查看客户
|
||||
*/
|
||||
previewCustomer() {
|
||||
this.$refs.customerModal.onOpen()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### CustomerModal.vue(无需修改)
|
||||
|
||||
该组件已实现完整的客户查看功能:
|
||||
- 支持行政区域(通过 code 查询)
|
||||
- 支持特色区域(通过 shapeId 查询)
|
||||
- 支持三种客户类型切换
|
||||
- 支持分页
|
||||
- 支持跳转客户详情
|
||||
|
||||
## 4. 实施计划
|
||||
|
||||
### 4.1 开发任务
|
||||
|
||||
| 任务 | 文件 | 预估时间 | 负责人 |
|
||||
|------|------|---------|--------|
|
||||
| 添加"查看客户"功能 | BMapPolygonEditor.vue | 0.5h | 前端开发 |
|
||||
| 功能测试 | - | 0.5h | 测试人员 |
|
||||
|
||||
**总计**: 1小时
|
||||
|
||||
### 4.2 测试计划
|
||||
|
||||
#### 单元测试
|
||||
- 测试 previewCustomer() 方法是否正确调用
|
||||
- 测试组件引用是否正确传递参数
|
||||
|
||||
#### 集成测试
|
||||
- 测试点击按钮后模态框是否正常打开
|
||||
- 测试客户列表是否正确加载
|
||||
- 测试客户类型切换功能
|
||||
- 测试分页功能
|
||||
|
||||
#### UI测试
|
||||
- 测试图标样式是否一致
|
||||
- 测试 tooltip 是否正确显示
|
||||
- 测试响应式布局
|
||||
|
||||
#### 回归测试
|
||||
- 验证现有4个图标按钮功能正常
|
||||
- 验证行政区域的查看客户功能正常
|
||||
- 验证页面无报错
|
||||
|
||||
### 4.3 部署计划
|
||||
|
||||
- **开发环境**: 开发完成后立即部署测试
|
||||
- **测试环境**: 通过代码审查后部署
|
||||
- **生产环境**: 测试通过后部署
|
||||
|
||||
## 5. 风险评估
|
||||
|
||||
### 5.1 技术风险
|
||||
|
||||
| 风险 | 等级 | 影响 | 缓解措施 |
|
||||
|------|------|------|----------|
|
||||
| 组件引入导致页面加载变慢 | 低 | 轻微 | 组件已在使用,无额外性能影响 |
|
||||
| 图标样式不一致 | 低 | 轻微 | 使用现有 icon-area 样式类 |
|
||||
| 数据传递错误 | 中 | 中等 | 充分测试,确保 shapeId 正确传递 |
|
||||
|
||||
### 5.2 业务风险
|
||||
|
||||
| 风险 | 等级 | 影响 | 缓解措施 |
|
||||
|------|------|------|----------|
|
||||
| 用户体验变化 | 低 | 轻微 | 仅新增功能,不影响现有操作 |
|
||||
| 功能误解 | 低 | 轻微 | tooltip 提示清晰 |
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
### 6.1 功能验收
|
||||
|
||||
- [ ] 区域详情窗口中显示"查看客户"图标按钮
|
||||
- [ ] 点击按钮后成功打开客户查看模态框
|
||||
- [ ] 模态框顶部显示正确的区域名称和规模
|
||||
- [ ] 客户列表正确加载(企业/个人/商户三种类型)
|
||||
- [ ] 客户类型切换功能正常
|
||||
- [ ] 分页功能正常
|
||||
- [ ] 点击客户名称可跳转到客户详情页
|
||||
|
||||
### 6.2 UI验收
|
||||
|
||||
- [ ] 图标样式与现有图标按钮一致
|
||||
- [ ] 图标大小、颜色、间距合理
|
||||
- [ ] Tooltip 正确显示"查看客户"
|
||||
- [ ] 图标位置合理,不拥挤
|
||||
|
||||
### 6.3 性能验收
|
||||
|
||||
- [ ] 页面加载速度无明显变化
|
||||
- [ ] 模态框打开速度正常(<1秒)
|
||||
- [ ] 客户列表加载速度正常(<2秒)
|
||||
|
||||
### 6.4 兼容性验收
|
||||
|
||||
- [ ] 现有4个图标按钮功能正常
|
||||
- [ ] 行政区域的查看客户功能正常
|
||||
- [ ] 浏览器控制台无报错
|
||||
|
||||
## 7. 后续优化
|
||||
|
||||
### 7.1 短期优化(可选)
|
||||
|
||||
- 无
|
||||
|
||||
### 7.2 长期优化(可选)
|
||||
|
||||
1. **统一按钮风格**: 考虑将所有图标按钮改为文字按钮,提高可读性
|
||||
2. **权限控制**: 根据用户角色控制"查看客户"按钮的显示/隐藏
|
||||
3. **数据缓存**: 对频繁查看的区域客户数据进行缓存,提升加载速度
|
||||
|
||||
## 8. 附录
|
||||
|
||||
### 8.1 相关文件
|
||||
|
||||
- `ruoyi-ui/src/map/BMapPolygonEditor.vue` - 特色区域地图编辑器
|
||||
- `ruoyi-ui/src/views/grid/map/draw-area/customer-modal.vue` - 客户查看模态框
|
||||
- `ruoyi-ui/src/views/grid/map/draw-area/components/menulist-modal.vue` - 特色区域列表
|
||||
- `ruoyi-ui/src/views/grid/map/draw-area/index.vue` - 行政区域页面
|
||||
|
||||
### 8.2 参考资料
|
||||
|
||||
- Element UI 文档: https://element.eleme.cn/
|
||||
- 若依框架文档: http://doc.ruoyi.vip/
|
||||
|
||||
### 8.3 变更记录
|
||||
|
||||
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||
|------|------|--------|----------|
|
||||
| 1.0 | 2026-02-28 | Claude Code | 初始版本 |
|
||||
389
docs/plans/2026-02-28-featured-area-customer-view.md
Normal file
389
docs/plans/2026-02-28-featured-area-customer-view.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# 特色区域查看客户功能实施计划
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**目标:** 在特色区域详情窗口添加"查看客户"图标按钮,复用现有的 customer-modal 组件,实现客户列表查看功能。
|
||||
|
||||
**架构:** 组件复用架构 - BMapPolygonEditor.vue 引入 CustomerModal.vue,通过 refs 调用模态框的 onOpen() 方法。
|
||||
|
||||
**技术栈:** Vue 2.x, Element UI, 若依框架
|
||||
|
||||
**设计文档:** `docs/plans/2026-02-28-featured-area-customer-view-design.md`
|
||||
|
||||
---
|
||||
|
||||
## 任务 1: 引入 CustomerModal 组件
|
||||
|
||||
**文件:**
|
||||
- 修改: `ruoyi-ui/src/map/BMapPolygonEditor.vue:20-30`(import 区域)
|
||||
|
||||
**步骤 1: 在 script 标签内添加 import 语句**
|
||||
|
||||
在第22行(MenuEdit 导入语句之后)添加:
|
||||
|
||||
```javascript
|
||||
import CustomerModal from "@/views/grid/map/draw-area/customer-modal.vue"
|
||||
```
|
||||
|
||||
**步骤 2: 在 components 中注册组件**
|
||||
|
||||
在第26-30行的 components 对象中添加 CustomerModal:
|
||||
|
||||
```javascript
|
||||
components: {
|
||||
MenuEdit,
|
||||
BMapPolygonEditor,
|
||||
MenuEdit,
|
||||
CustomerModal, // 新增
|
||||
},
|
||||
```
|
||||
|
||||
**步骤 3: 验证 import 路径正确**
|
||||
|
||||
运行前端项目验证无报错:
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
预期: 浏览器控制台无 "Failed to mount component" 错误
|
||||
|
||||
**步骤 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/map/BMapPolygonEditor.vue
|
||||
git commit -m "feat(featured-areas): 引入 CustomerModal 组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务 2: 在 template 中添加组件引用
|
||||
|
||||
**文件:**
|
||||
- 修改: `ruoyi-ui/src/map/BMapPolygonEditor.vue:270-275`(template 末尾)
|
||||
|
||||
**步骤 1: 在 template 末尾添加组件标签**
|
||||
|
||||
在第273行(`</transition>` 之后,`<div class="search-box">` 之前)添加:
|
||||
|
||||
```vue
|
||||
</transition>
|
||||
<!-- 查看客户模态框 -->
|
||||
<customer-modal
|
||||
ref="customerModal"
|
||||
cardType="featured"
|
||||
:detailInfo="areaForm"
|
||||
:btnType="'2'"
|
||||
/>
|
||||
<div class="search-box">
|
||||
```
|
||||
|
||||
**步骤 2: 验证组件引用**
|
||||
|
||||
在浏览器中打开特色区域页面,验证控制台无报错。
|
||||
|
||||
**步骤 3: 验证 props 传递**
|
||||
|
||||
在 Vue DevTools 中检查 customer-modal 组件的 props:
|
||||
- cardType 应该是 "featured"
|
||||
- detailInfo 应该是 areaForm 对象
|
||||
- btnType 应该是 "2"
|
||||
|
||||
**步骤 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/map/BMapPolygonEditor.vue
|
||||
git commit -m "feat(featured-areas): 添加 CustomerModal 组件引用"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务 3: 在区域详情窗口添加查看客户图标按钮
|
||||
|
||||
**文件:**
|
||||
- 修改: `ruoyi-ui/src/map/BMapPolygonEditor.vue:221-240`(edit-operate 按钮区域)
|
||||
|
||||
**步骤 1: 定位按钮插入位置**
|
||||
|
||||
找到第221-240行的代码区域,这是 `infoType === 'SHOW'` 的按钮区域:
|
||||
- 第229行:修改信息按钮结束
|
||||
- 第240行:删除区域按钮开始
|
||||
|
||||
我们需要在修改信息按钮之后插入查看客户按钮。
|
||||
|
||||
**步骤 2: 添加查看客户图标按钮**
|
||||
|
||||
在第239行(修改信息 tooltip 结束)之后,第240行(删除区域 tooltip 开始)之前插入:
|
||||
|
||||
```vue
|
||||
</el-tooltip>
|
||||
<!-- 查看客户 -->
|
||||
<el-tooltip placement="top" effect="light" content="查看客户">
|
||||
<i
|
||||
class="el-icon-user icon-area"
|
||||
@click.stop="previewCustomer"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<!-- 删除区域 -->
|
||||
<el-tooltip placement="top" effect="light" content="删除区域">
|
||||
```
|
||||
|
||||
**步骤 3: 验证图标样式**
|
||||
|
||||
在浏览器中:
|
||||
1. 打开特色区域页面
|
||||
2. 点击一个已绘制的区域
|
||||
3. 查看区域详情窗口底部的图标按钮区域
|
||||
4. 验证新添加的"用户图标"样式与其他图标一致
|
||||
|
||||
**步骤 4: 验证 tooltip 显示**
|
||||
|
||||
1. 鼠标悬停在新添加的用户图标上
|
||||
2. 验证 tooltip 显示"查看客户"
|
||||
3. 验证 tooltip 样式与其他 tooltip 一致
|
||||
|
||||
**步骤 5: 提交代码**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/map/BMapPolygonEditor.vue
|
||||
git commit -m "feat(featured-areas): 添加查看客户图标按钮"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务 4: 实现 previewCustomer 方法
|
||||
|
||||
**文件:**
|
||||
- 修改: `ruoyi-ui/src/map/BMapPolygonEditor.vue:900-1000`(methods 区域)
|
||||
|
||||
**步骤 1: 定位 methods 区域**
|
||||
|
||||
找到 methods 对象的最后,准备添加新方法。建议添加在 `updateAreaShape()` 方法之后。
|
||||
|
||||
**步骤 2: 添加 previewCustomer 方法**
|
||||
|
||||
在 methods 对象中添加:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* 调整边界
|
||||
*/
|
||||
updateAreaShape() {
|
||||
// ... 现有代码
|
||||
},
|
||||
/**
|
||||
* 查看客户
|
||||
*/
|
||||
previewCustomer() {
|
||||
this.$refs.customerModal.onOpen()
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 3: 验证方法调用**
|
||||
|
||||
在浏览器控制台中测试:
|
||||
1. 打开特色区域页面
|
||||
2. 点击一个区域,打开区域详情窗口
|
||||
3. 打开 Vue DevTools
|
||||
4. 找到 BMapPolygonEditor 组件
|
||||
5. 在控制台执行:`$vm0.previewCustomer()`
|
||||
6. 验证 customer-modal 模态框成功打开
|
||||
|
||||
**步骤 4: 提交代码**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/map/BMapPolygonEditor.vue
|
||||
git commit -m "feat(featured-areas): 实现 previewCustomer 方法"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务 5: 功能测试
|
||||
|
||||
**文件:**
|
||||
- 无需修改文件
|
||||
|
||||
**步骤 1: 启动前端项目**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
预期: 项目成功启动在 http://localhost:80
|
||||
|
||||
**步骤 2: 登录系统**
|
||||
|
||||
1. 访问 http://localhost:80
|
||||
2. 使用测试账号登录:
|
||||
- 用户名: admin
|
||||
- 密码: admin123
|
||||
|
||||
**步骤 3: 进入特色区域页面**
|
||||
|
||||
1. 导航到"网格管理" > "区域绘制"
|
||||
2. 点击"特色区域"标签(btnType=2)
|
||||
3. 等待地图加载完成
|
||||
|
||||
**步骤 4: 测试查看客户按钮**
|
||||
|
||||
1. 在左侧菜单中选择一个特色区域图层
|
||||
2. 在地图上点击一个已绘制的特色区域
|
||||
3. 验证右上角弹出区域详情窗口
|
||||
4. 查看窗口底部的图标按钮区域
|
||||
5. 验证显示"用户图标"(查看客户按钮)
|
||||
|
||||
**步骤 5: 测试点击功能**
|
||||
|
||||
1. 点击"查看客户"图标
|
||||
2. 验证:
|
||||
- customer-modal 对话框成功打开
|
||||
- 对话框标题显示"查看客户"
|
||||
- 顶部显示区域名称和规模信息
|
||||
- 客户列表成功加载
|
||||
- 显示三种客户类型标签(企业/个人/商户)
|
||||
|
||||
**步骤 6: 测试客户类型切换**
|
||||
|
||||
1. 点击"个人"标签
|
||||
2. 验证客户列表刷新,显示个人客户
|
||||
3. 点击"商户"标签
|
||||
4. 验证客户列表刷新,显示商户客户
|
||||
5. 点击"企业"标签
|
||||
6. 验证客户列表刷新,显示企业客户
|
||||
|
||||
**步骤 7: 测试分页功能**
|
||||
|
||||
1. 如果客户数量超过10条,验证分页器显示
|
||||
2. 点击下一页
|
||||
3. 验证客户列表刷新,显示第二页数据
|
||||
4. 修改每页显示条数
|
||||
5. 验证列表重新加载
|
||||
|
||||
**步骤 8: 测试客户详情跳转**
|
||||
|
||||
1. 点击表格中的客户名称(el-button)
|
||||
2. 验证路由跳转到客户详情页面
|
||||
3. 验证详情页面显示正确的客户信息
|
||||
|
||||
**步骤 9: 测试现有功能兼容性**
|
||||
|
||||
1. 关闭客户模态框
|
||||
2. 依次点击其他4个图标按钮:
|
||||
- 重新申请
|
||||
- 修改信息
|
||||
- 删除区域
|
||||
- 调整边界
|
||||
3. 验证这些按钮功能正常,无报错
|
||||
|
||||
**步骤 10: 测试行政区域兼容性**
|
||||
|
||||
1. 切换到"行政区域"标签(btnType=1)
|
||||
2. 点击一个行政区域
|
||||
3. 验证行政区域的"查看客户"按钮功能正常
|
||||
4. 验证两个区域类型的查看客户功能互不影响
|
||||
|
||||
**步骤 11: 检查浏览器控制台**
|
||||
|
||||
1. 打开浏览器开发者工具
|
||||
2. 切换到 Console 标签
|
||||
3. 执行所有测试步骤
|
||||
4. 验证控制台无 JavaScript 报错
|
||||
5. 验证控制台无 Vue 警告
|
||||
|
||||
**步骤 12: 最终提交**
|
||||
|
||||
如果所有测试通过:
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
预期: 无未提交的文件
|
||||
|
||||
---
|
||||
|
||||
## 任务 6: 更新文档(可选)
|
||||
|
||||
**文件:**
|
||||
- 修改: `docs/plans/2026-02-28-featured-area-customer-view-design.md`
|
||||
|
||||
**步骤 1: 更新验收标准**
|
||||
|
||||
在验收标准的各项前打勾:
|
||||
|
||||
```markdown
|
||||
### 6.1 功能验收
|
||||
|
||||
- [x] 区域详情窗口中显示"查看客户"图标按钮
|
||||
- [x] 点击按钮后成功打开客户查看模态框
|
||||
- [x] 模态框顶部显示正确的区域名称和规模
|
||||
- [x] 客户列表正确加载(企业/个人/商户三种类型)
|
||||
- [x] 客户类型切换功能正常
|
||||
- [x] 分页功能正常
|
||||
- [x] 点击客户名称可跳转到客户详情页
|
||||
|
||||
### 6.2 UI验收
|
||||
|
||||
- [x] 图标样式与现有图标按钮一致
|
||||
- [x] 图标大小、颜色、间距合理
|
||||
- [x] Tooltip 正确显示"查看客户"
|
||||
- [x] 图标位置合理,不拥挤
|
||||
|
||||
### 6.3 性能验收
|
||||
|
||||
- [x] 页面加载速度无明显变化
|
||||
- [x] 模态框打开速度正常(<1秒)
|
||||
- [x] 客户列表加载速度正常(<2秒)
|
||||
|
||||
### 6.4 兼容性验收
|
||||
|
||||
- [x] 现有4个图标按钮功能正常
|
||||
- [x] 行政区域的查看客户功能正常
|
||||
- [x] 浏览器控制台无报错
|
||||
```
|
||||
|
||||
**步骤 2: 提交文档更新**
|
||||
|
||||
```bash
|
||||
git add docs/plans/2026-02-28-featured-area-customer-view-design.md
|
||||
git commit -m "docs: 更新特色区域查看客户功能验收状态"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完成检查清单
|
||||
|
||||
实施完成后,验证以下内容:
|
||||
|
||||
- [ ] 所有6个任务已完成
|
||||
- [ ] 所有 git commits 已提交
|
||||
- [ ] 前端项目无编译错误
|
||||
- [ ] 浏览器控制台无 JavaScript 错误
|
||||
- [ ] 功能测试全部通过
|
||||
- [ ] 代码已推送到远程仓库(如需要)
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果实施过程中遇到问题,可以回滚:
|
||||
|
||||
```bash
|
||||
# 查看提交历史
|
||||
git log --oneline
|
||||
|
||||
# 回滚到指定提交(替换 <commit-hash> 为实际的 commit hash)
|
||||
git reset --hard <commit-hash>
|
||||
|
||||
# 或者回滚所有提交(回到初始状态)
|
||||
git reset --hard HEAD~4
|
||||
```
|
||||
|
||||
## 相关资源
|
||||
|
||||
- **设计文档**: `docs/plans/2026-02-28-featured-area-customer-view-design.md`
|
||||
- **修改文件**: `ruoyi-ui/src/map/BMapPolygonEditor.vue`
|
||||
- **复用组件**: `ruoyi-ui/src/views/grid/map/draw-area/customer-modal.vue`
|
||||
- **参考实现**: `ruoyi-ui/src/views/grid/map/draw-area/components/menulist-modal.vue:413-416`
|
||||
- **Element UI 文档**: https://element.eleme.cn/#/zh-CN/component/tooltip
|
||||
- **Vue 2.x 文档**: https://v2.cn.vuejs.org/
|
||||
@@ -18,8 +18,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
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));
|
||||
}
|
||||
|
||||
@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.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.CustInfoDeleteFromAnchor;
|
||||
import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor;
|
||||
@@ -242,4 +243,10 @@ public interface CustInfoBusinessMapper extends BaseMapper<CustInfoBusiness>
|
||||
public int insertCustomersToBusinessByScCode(List<SysGroupCustomer> sysGroupCustomers);
|
||||
|
||||
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;
|
||||
|
||||
import com.ruoyi.ibs.customerselect.domain.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,4 +42,8 @@ public interface IMyCustomerService {
|
||||
public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata);
|
||||
|
||||
MerchantMcspInfo selectmerchantMessage(String custId);
|
||||
|
||||
String importBusinessCustLevelAsync(MultipartFile file);
|
||||
|
||||
BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.ruoyi.ibs.customerselect.service.Impl;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.annotation.DataScope;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.entity.CustInfoBusiness;
|
||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
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.vo.GridRelateVO;
|
||||
import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper;
|
||||
@@ -32,9 +38,16 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
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.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -45,6 +58,10 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
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
|
||||
private CustInfoBusinessMapper custInfoBusinessMapper;
|
||||
|
||||
@@ -80,6 +97,12 @@ public class MyCustomerServiceImpl implements IMyCustomerService {
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Resource
|
||||
private NotificationService notificationService;
|
||||
|
||||
@Resource(name = "excelImportExecutor")
|
||||
private ExecutorService excelImportExecutor;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
startPage();
|
||||
notice.setCurrentHeadDeptId(SecurityUtils.getHeadId() + "000");
|
||||
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@@ -23,44 +23,58 @@ public class Ent9vPortraitOrc implements Serializable {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ruoyi.ibs.list.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -21,206 +22,294 @@ public class NineVFinalInfoOrc implements Serializable {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -148,15 +148,15 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
|
||||
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));
|
||||
// }
|
||||
if ("965".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;
|
||||
}
|
||||
|
||||
@@ -857,6 +857,28 @@
|
||||
select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode}
|
||||
</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 INTO cust_info_business
|
||||
(cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit)
|
||||
@@ -866,4 +888,4 @@
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
@@ -100,6 +100,50 @@ public class SysLoginService
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试登录
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @return 结果
|
||||
*/
|
||||
public String login(String username, String password)
|
||||
{
|
||||
// 登录前置校验
|
||||
loginPreCheck(username, password);
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
try
|
||||
{
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof BadCredentialsException)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AuthenticationContextHolder.clearContext();
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*
|
||||
|
||||
@@ -31,6 +31,12 @@ public class SysNotice extends BaseEntity
|
||||
/** 公告状态(0正常 1关闭) */
|
||||
private String status;
|
||||
|
||||
/** 可见总行部门ID,多选逗号分隔 */
|
||||
private String deptIds;
|
||||
|
||||
/** 当前登录用户所属总行部门ID,仅用于查询过滤 */
|
||||
private String currentHeadDeptId;
|
||||
|
||||
public Long getNoticeId()
|
||||
{
|
||||
return noticeId;
|
||||
@@ -84,6 +90,26 @@ public class SysNotice extends BaseEntity
|
||||
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
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@@ -92,6 +118,7 @@ public class SysNotice extends BaseEntity
|
||||
.append("noticeType", getNoticeType())
|
||||
.append("noticeContent", getNoticeContent())
|
||||
.append("status", getStatus())
|
||||
.append("deptIds", getDeptIds())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
|
||||
@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="noticeType" column="notice_type" />
|
||||
<result property="noticeContent" column="notice_content" />
|
||||
<result property="status" column="status" />
|
||||
<result property="deptIds" column="dept_ids" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
@@ -18,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<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
|
||||
</sql>
|
||||
|
||||
@@ -39,6 +40,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="createBy != null and createBy != ''">
|
||||
AND create_by like concat('%', #{createBy}, '%')
|
||||
</if>
|
||||
<if test="currentHeadDeptId != null and currentHeadDeptId != ''">
|
||||
AND (dept_ids is null or dept_ids = '' or find_in_set(#{currentHeadDeptId}, dept_ids))
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@@ -48,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != '' ">notice_type, </if>
|
||||
<if test="noticeContent != null and noticeContent != '' ">notice_content, </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="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
@@ -56,6 +61,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
|
||||
<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </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="createBy != null and createBy != ''">#{createBy},</if>
|
||||
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="noticeContent != null">notice_content = #{noticeContent}, </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>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
@@ -86,4 +93,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = UAT支行数智管理平台系统
|
||||
VUE_APP_TITLE = DEV支行数智管理平台系统
|
||||
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = PRE支行数智管理平台系统
|
||||
VUE_APP_TITLE = UAT支行数智管理平台系统
|
||||
|
||||
NODE_ENV = staging
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script>
|
||||
const result = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script>
|
||||
const result = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script type="text/javascript" src="./script/getscript.js?type=webgl&v=1.0&services=&t=20230529114224"></script>
|
||||
<!-- 引入logisticsgl的sdk -->
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<script>
|
||||
const sdk = new QuHuaSdk({
|
||||
ak: 'L7KaAZUYPVSD40nYT09rWWgIdZKUesiX',
|
||||
webAk: 't6k6UC2IZR40Un8kkqM4RXlaQb4FulyM',
|
||||
webAk: 'mokVj0S4sGE9av6NBwy8WHY0xnQsucbE',
|
||||
domId: 'box',
|
||||
defaultCenterCity: "杭州市", // 非必填
|
||||
_baseUrl: window.NODE_ENV === "production" ? "http://64.202.32.20:5001/logisticsWeb-quhua-intranet" : "http://158.234.96.76:5001/logisticsWeb-quhua-intranet", // 固定格式,必填
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script>
|
||||
const result = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script>
|
||||
const result = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<script>
|
||||
// 这里一定要配置ak 否则无法使用
|
||||
window.BMAP_AUTHENTIC_KEY = "t6k6UC2IZR40Un8kkqM4RXlaQb4FulyM"
|
||||
window.BMAP_AUTHENTIC_KEY = "mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"
|
||||
</script>
|
||||
<script type="text/javascript" src="<%= VUE_APP_BAIDU_PATH %>"></script>
|
||||
<!-- <script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=mokVj0S4sGE9av6NBwy8WHY0xnQsucbE"></script> -->
|
||||
<link rel="stylesheet" type="text/css" href="<%= VUE_APP_BAIDU_CSS_PATH %>" />
|
||||
<script type="text/javascript" src="\baidu\script\index.umd.min.js"></script>
|
||||
|
||||
|
||||
@@ -91,4 +91,20 @@ export function uploadTag(data) {
|
||||
data: data,
|
||||
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'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,4 +41,12 @@ export function delNotice(noticeId) {
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 查询总行列表
|
||||
export function getHeadList() {
|
||||
return request({
|
||||
url: '/system/dept/headList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -168,7 +168,8 @@ export default {
|
||||
notReadCount: 0,
|
||||
noticeCenterList: [],
|
||||
open2: false,
|
||||
downCenterList: []
|
||||
downCenterList: [],
|
||||
noticeCenterTimer: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -192,6 +193,12 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.getCenterList();
|
||||
this.startNoticePolling();
|
||||
window.addEventListener('notice-center-refresh', this.getCenterList);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearNoticePolling();
|
||||
window.removeEventListener('notice-center-refresh', this.getCenterList);
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
@@ -223,6 +230,18 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
startNoticePolling() {
|
||||
this.clearNoticePolling();
|
||||
this.noticeCenterTimer = setInterval(() => {
|
||||
this.getCenterList();
|
||||
}, 10000);
|
||||
},
|
||||
clearNoticePolling() {
|
||||
if (this.noticeCenterTimer) {
|
||||
clearInterval(this.noticeCenterTimer);
|
||||
this.noticeCenterTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 下载中心列表
|
||||
getDownCenterList() {
|
||||
|
||||
@@ -237,6 +237,14 @@
|
||||
@click.stop="updateAreaInfo"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<!-- 查看客户 -->
|
||||
<el-tooltip placement="top" effect="light" content="查看客户">
|
||||
<i
|
||||
class="el-icon-user icon-area"
|
||||
@click.stop="previewCustomer"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<!-- 删除区域 -->
|
||||
<el-tooltip placement="top" effect="light" content="删除区域">
|
||||
<el-popconfirm
|
||||
title="确定删除吗?"
|
||||
@@ -271,6 +279,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- 查看客户模态框 -->
|
||||
<customer-modal
|
||||
ref="customerModal"
|
||||
cardType="featured"
|
||||
:detailInfo="areaForm"
|
||||
:btnType="'2'"
|
||||
/>
|
||||
<div class="search-box">
|
||||
<el-form size="small" class="myForm">
|
||||
<el-form-item style="width: 130px">
|
||||
@@ -337,6 +352,7 @@ import { businessBelongList } from '@/views/grid/create/utils'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { Message } from 'element-ui'
|
||||
import { isEmpty } from 'lodash'
|
||||
import CustomerModal from '@/views/grid/map/draw-area/customer-modal.vue'
|
||||
const polygonOptions = {
|
||||
strokeColor: '#5E87DB',
|
||||
strokeWeight: 3,
|
||||
@@ -355,6 +371,9 @@ const labelOptions = {
|
||||
}
|
||||
export default {
|
||||
name: 'BMapPolygonEditor',
|
||||
components: {
|
||||
CustomerModal
|
||||
},
|
||||
props: ['layerInfo'],
|
||||
data() {
|
||||
return {
|
||||
@@ -1207,6 +1226,12 @@ export default {
|
||||
this.$store.dispatch('setIsDrawing', true)
|
||||
this.currentPolygon.enableEditing()
|
||||
},
|
||||
/**
|
||||
* 查看客户
|
||||
*/
|
||||
previewCustomer() {
|
||||
this.$refs.customerModal.onOpen()
|
||||
},
|
||||
setForm(data) {
|
||||
this.areaForm = data
|
||||
this.visible = true
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
initSdk(){
|
||||
this.sdk = new QuHuaSdk({
|
||||
ak: 'L7KaAZUYPVSD40nYT09rWWgIdZKUesiX',
|
||||
webAk: 't6k6UC2IZR40Un8kkqM4RXlaQb4FulyM',
|
||||
webAk: 'mokVj0S4sGE9av6NBwy8WHY0xnQsucbE',
|
||||
domId: 'box',
|
||||
defaultCenterCity: "杭州市", // 非必填
|
||||
_baseUrl: "http://158.234.96.76:5001/logisticsWeb-quhua-intranet", // 固定格式,必填
|
||||
@@ -137,7 +137,7 @@ export default {
|
||||
onChange(){
|
||||
this.sdk = new QuHuaSdk({
|
||||
ak: 'L7KaAZUYPVSD40nYT09rWWgIdZKUesiX',
|
||||
webAk: 't6k6UC2IZR40Un8kkqM4RXlaQb4FulyM',
|
||||
webAk: 'mokVj0S4sGE9av6NBwy8WHY0xnQsucbE',
|
||||
domId: 'box',
|
||||
defaultCenterCity: "杭州市", // 非必填
|
||||
_baseUrl: "http://158.234.96.76:5001/logisticsWeb-quhua-intranet", // 固定格式,必填
|
||||
|
||||
@@ -1024,6 +1024,77 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</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-dialog
|
||||
title="该客户尚未建档,请先进行营销建档"
|
||||
@@ -1215,7 +1286,120 @@ import { downloadFiles } from '@/utils'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { cloneDeep, isEmpty } from 'lodash'
|
||||
import _ from 'lodash'
|
||||
import * as echarts from 'echarts'
|
||||
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 {
|
||||
data() {
|
||||
var validatePhone = (rule, value, callback) => {
|
||||
@@ -1347,6 +1531,12 @@ export default {
|
||||
showTagsList:[],
|
||||
addTagName:"",
|
||||
newCustTag:[],
|
||||
// 企业九维数据
|
||||
ent9vPortrait: null,
|
||||
nineVFinalInfo: null,
|
||||
nineDimensionRadarChart: null, // 九维画像雷达图实例
|
||||
nineDimensionRadarResizeObserver: null,
|
||||
nineDimensionRadarRenderTimer: null,
|
||||
regAddress: {
|
||||
lazy: true,
|
||||
lazyLoad(node, resolve) {
|
||||
@@ -1403,13 +1593,40 @@ export default {
|
||||
this.managerOptions = []
|
||||
this.authUser = ''
|
||||
}
|
||||
},
|
||||
ent9vPortrait: {
|
||||
handler(newVal) {
|
||||
if (!newVal) {
|
||||
this.destroyNineDimensionRadar()
|
||||
return
|
||||
}
|
||||
this.scheduleNineDimensionRadarRender()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Custom
|
||||
},
|
||||
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() {
|
||||
return this.roles.includes('headAdmin')
|
||||
},
|
||||
@@ -1443,6 +1660,9 @@ export default {
|
||||
// 海宁
|
||||
is875() {
|
||||
return this.userName.slice(0, 3) === '875'
|
||||
},
|
||||
is825() {
|
||||
return String(this.deptId || '').substring(0, 3) === '825'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -1464,6 +1684,16 @@ export default {
|
||||
// systemUserAllTreeUser().then(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: {
|
||||
handleAddTag(){
|
||||
@@ -1591,6 +1821,9 @@ export default {
|
||||
this.showTagsList=this.tagsList[0].children[0].children||[];
|
||||
}
|
||||
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;
|
||||
});
|
||||
},
|
||||
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>
|
||||
@@ -2405,4 +3013,210 @@ export default {
|
||||
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>
|
||||
|
||||
@@ -142,6 +142,29 @@
|
||||
<el-dropdown-item @click.native="handleExportAll">导出前1000条<i class="quesiton"></i></el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<template v-if="selectedTab === '2' && is825">
|
||||
<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>
|
||||
</div>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="searchColoumns"
|
||||
@@ -180,7 +203,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 GroupCheckMore from './components/group-check-more'
|
||||
import UploadTag from './components/uploadTag.vue'
|
||||
@@ -288,6 +320,8 @@ export default {
|
||||
showMoreIndc: true,
|
||||
contiKeys: [],
|
||||
iscollapsed: false,
|
||||
businessImportLoading: false,
|
||||
businessImportTimer: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -342,6 +376,9 @@ export default {
|
||||
isOps() {
|
||||
return this.roles.includes('headOps')
|
||||
},
|
||||
is825() {
|
||||
return String(this.deptId || '').substring(0, 3) === '825'
|
||||
},
|
||||
// 客户经理
|
||||
isCommonManager() {
|
||||
return this.roles.includes('commonManager')
|
||||
@@ -780,6 +817,63 @@ 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() {
|
||||
const { query } = this.$route;
|
||||
@@ -800,6 +894,9 @@ export default {
|
||||
this.initGetSecondRegionList()
|
||||
this.initgetDrawList()
|
||||
this.initGetVirtualList()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearBusinessImportTimer()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1062,6 +1159,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 {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
|
||||
@@ -870,10 +870,6 @@ export default {
|
||||
},
|
||||
// headId为875时,便捷操作只显示快速入门
|
||||
filteredOptArr() {
|
||||
// 当deptId以875开头时,只保留快速入门
|
||||
if (this.deptId && String(this.deptId).startsWith('875')) {
|
||||
return this.optArr.filter(item => item.name === '快速入门')
|
||||
}
|
||||
return this.optArr
|
||||
}
|
||||
},
|
||||
@@ -1424,10 +1420,9 @@ export default {
|
||||
handleSaveSetting() {
|
||||
let arr = this.optArr.map(item => {
|
||||
return item.name
|
||||
})
|
||||
}).filter(name => name && name.trim() !== '') // 过滤掉空值
|
||||
console.log(arr, 'arrarrarr')
|
||||
updateQuickSelect(arr).then(res => {
|
||||
|
||||
if (res.code == 200) {
|
||||
Message.success(res.data)
|
||||
this.handleSetting()
|
||||
|
||||
@@ -83,6 +83,11 @@
|
||||
<dict-tag :options="dict.type.sys_notice_type" :value="scope.row.noticeType"/>
|
||||
</template>
|
||||
</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">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.sys_notice_status" :value="scope.row.status"/>
|
||||
@@ -154,6 +159,25 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</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-form-item label="内容">
|
||||
<editor v-model="form.noticeContent" :min-height="192"/>
|
||||
@@ -170,7 +194,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
|
||||
import { listNotice, getNotice, delNotice, addNotice, updateNotice, getHeadList } from "@/api/system/notice";
|
||||
|
||||
export default {
|
||||
name: "Notice",
|
||||
@@ -191,6 +215,10 @@ export default {
|
||||
total: 0,
|
||||
// 公告表格数据
|
||||
noticeList: [],
|
||||
// 总行选项
|
||||
headOptions: [],
|
||||
// 已选总行
|
||||
headList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
@@ -218,6 +246,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getHeadOptions();
|
||||
},
|
||||
methods: {
|
||||
/** 查询公告列表 */
|
||||
@@ -229,6 +258,16 @@ export default {
|
||||
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() {
|
||||
this.open = false;
|
||||
@@ -241,8 +280,10 @@ export default {
|
||||
noticeTitle: undefined,
|
||||
noticeType: undefined,
|
||||
noticeContent: undefined,
|
||||
deptIds: undefined,
|
||||
status: "0"
|
||||
};
|
||||
this.headList = [];
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
@@ -273,6 +314,7 @@ export default {
|
||||
const noticeId = row.noticeId || this.ids
|
||||
getNotice(noticeId).then(response => {
|
||||
this.form = response.data;
|
||||
this.headList = response.data.deptIds ? response.data.deptIds.split(',').filter(Boolean) : [];
|
||||
this.open = true;
|
||||
this.title = "修改公告";
|
||||
});
|
||||
@@ -281,6 +323,7 @@ export default {
|
||||
submitForm: function() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.form.deptIds = Array.isArray(this.headList) && this.headList.length > 0 ? this.headList.join(',') : '';
|
||||
if (this.form.noticeId != undefined) {
|
||||
updateNotice(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
@@ -306,6 +349,20 @@ export default {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).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('、');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user