490 lines
13 KiB
Markdown
490 lines
13 KiB
Markdown
|
|
# 中介库导入失败记录查看功能设计
|
||
|
|
|
||
|
|
## 1. 需求背景
|
||
|
|
|
||
|
|
当前中介库导入功能在导入失败后,只显示通知消息,但没有提供查看失败记录的入口,用户无法了解具体哪些数据导入失败以及失败原因。
|
||
|
|
|
||
|
|
## 2. 功能描述
|
||
|
|
|
||
|
|
为中介库管理页面添加**导入失败记录查看**功能,支持个人中介和实体中介两种类型的失败记录查看。
|
||
|
|
|
||
|
|
### 2.1 核心功能
|
||
|
|
|
||
|
|
1. **双按钮独立管理**
|
||
|
|
- "查看个人导入失败记录"按钮 - 仅在个人中介导入存在失败记录时显示
|
||
|
|
- "查看实体导入失败记录"按钮 - 仅在实体中介导入存在失败记录时显示
|
||
|
|
- 按钮带tooltip提示上次导入时间
|
||
|
|
|
||
|
|
2. **localStorage持久化存储**
|
||
|
|
- 分别存储个人中介和实体中介的导入任务信息
|
||
|
|
- 存储期限:7天,过期自动清除
|
||
|
|
- 存储内容:任务ID、导入时间、成功数、失败数、hasFailures标志
|
||
|
|
|
||
|
|
3. **失败记录对话框**
|
||
|
|
- 显示导入统计摘要(总数/成功/失败)
|
||
|
|
- 表格展示所有失败记录,支持分页(每页10条)
|
||
|
|
- 提供清除历史记录按钮
|
||
|
|
- 记录过期时自动提示并清除
|
||
|
|
|
||
|
|
## 3. 技术设计
|
||
|
|
|
||
|
|
### 3.1 组件结构
|
||
|
|
|
||
|
|
```
|
||
|
|
index.vue (中介库管理页面)
|
||
|
|
├── 工具栏按钮区域
|
||
|
|
│ ├── 新增按钮
|
||
|
|
│ ├── 导入按钮
|
||
|
|
│ ├── 查看个人导入失败记录按钮 (条件显示)
|
||
|
|
│ └── 查看实体导入失败记录按钮 (条件显示)
|
||
|
|
├── 数据表格
|
||
|
|
├── 个人中介导入失败记录对话框
|
||
|
|
└── 实体中介导入失败记录对话框
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 数据流程
|
||
|
|
|
||
|
|
```
|
||
|
|
用户选择文件上传
|
||
|
|
↓
|
||
|
|
ImportDialog 组件提交导入
|
||
|
|
↓
|
||
|
|
后端返回 taskId (异步处理)
|
||
|
|
↓
|
||
|
|
前端开始轮询导入状态
|
||
|
|
↓
|
||
|
|
导入完成,ImportDialog 触发 @import-complete 事件
|
||
|
|
↓
|
||
|
|
index.vue 接收事件,根据 importType 判断类型
|
||
|
|
↓
|
||
|
|
保存任务信息到 localStorage (person 或 entity)
|
||
|
|
↓
|
||
|
|
更新对应的失败记录按钮显示状态
|
||
|
|
↓
|
||
|
|
用户点击"查看失败记录"按钮
|
||
|
|
↓
|
||
|
|
调用后端接口获取失败记录列表 (支持分页)
|
||
|
|
↓
|
||
|
|
在对话框中展示失败记录和错误原因
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.3 localStorage存储设计
|
||
|
|
|
||
|
|
#### 3.3.1 个人中介导入任务
|
||
|
|
|
||
|
|
**Key**: `intermediary_person_import_last_task`
|
||
|
|
|
||
|
|
**数据结构**:
|
||
|
|
```javascript
|
||
|
|
{
|
||
|
|
taskId: "uuid", // 任务ID
|
||
|
|
saveTime: 1234567890, // 保存时间戳
|
||
|
|
hasFailures: true, // 是否有失败记录
|
||
|
|
totalCount: 100, // 总数
|
||
|
|
successCount: 95, // 成功数
|
||
|
|
failureCount: 5 // 失败数
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 3.3.2 实体中介导入任务
|
||
|
|
|
||
|
|
**Key**: `intermediary_entity_import_last_task`
|
||
|
|
|
||
|
|
**数据结构**: 同个人中介
|
||
|
|
|
||
|
|
### 3.4 页面状态管理
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
// 按钮显示状态
|
||
|
|
showPersonFailureButton: false,
|
||
|
|
showEntityFailureButton: false,
|
||
|
|
|
||
|
|
// 当前任务ID
|
||
|
|
currentPersonTaskId: null,
|
||
|
|
currentEntityTaskId: null,
|
||
|
|
|
||
|
|
// 个人失败记录对话框
|
||
|
|
personFailureDialogVisible: false,
|
||
|
|
personFailureList: [],
|
||
|
|
personFailureLoading: false,
|
||
|
|
personFailureTotal: 0,
|
||
|
|
personFailureQueryParams: {
|
||
|
|
pageNum: 1,
|
||
|
|
pageSize: 10
|
||
|
|
},
|
||
|
|
|
||
|
|
// 实体失败记录对话框
|
||
|
|
entityFailureDialogVisible: false,
|
||
|
|
entityFailureList: [],
|
||
|
|
entityFailureLoading: false,
|
||
|
|
entityFailureTotal: 0,
|
||
|
|
entityFailureQueryParams: {
|
||
|
|
pageNum: 1,
|
||
|
|
pageSize: 10
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 4. 接口依赖
|
||
|
|
|
||
|
|
### 4.1 已有后端接口
|
||
|
|
|
||
|
|
#### 4.1.1 查询个人中介导入失败记录
|
||
|
|
|
||
|
|
**接口**: `GET /ccdi/intermediary/importPersonFailures/{taskId}`
|
||
|
|
|
||
|
|
**参数**:
|
||
|
|
- `taskId`: 任务ID (路径参数)
|
||
|
|
- `pageNum`: 页码 (默认1)
|
||
|
|
- `pageSize`: 每页大小 (默认10)
|
||
|
|
|
||
|
|
**返回**: `IntermediaryPersonImportFailureVO[]`
|
||
|
|
|
||
|
|
**字段**:
|
||
|
|
- `name`: 姓名
|
||
|
|
- `personId`: 证件号码
|
||
|
|
- `personType`: 人员类型
|
||
|
|
- `gender`: 性别
|
||
|
|
- `mobile`: 手机号码
|
||
|
|
- `company`: 所在公司
|
||
|
|
- `errorMessage`: 错误信息
|
||
|
|
|
||
|
|
#### 4.1.2 查询实体中介导入失败记录
|
||
|
|
|
||
|
|
**接口**: `GET /ccdi/intermediary/importEntityFailures/{taskId}`
|
||
|
|
|
||
|
|
**参数**:
|
||
|
|
- `taskId`: 任务ID (路径参数)
|
||
|
|
- `pageNum`: 页码 (默认1)
|
||
|
|
- `pageSize`: 每页大小 (默认10)
|
||
|
|
|
||
|
|
**返回**: `IntermediaryEntityImportFailureVO[]`
|
||
|
|
|
||
|
|
**字段**:
|
||
|
|
- `enterpriseName`: 机构名称
|
||
|
|
- `socialCreditCode`: 统一社会信用代码
|
||
|
|
- `enterpriseType`: 主体类型
|
||
|
|
- `enterpriseNature`: 企业性质
|
||
|
|
- `legalRepresentative`: 法定代表人
|
||
|
|
- `establishDate`: 成立日期
|
||
|
|
- `errorMessage`: 错误信息
|
||
|
|
|
||
|
|
### 4.2 前端API方法
|
||
|
|
|
||
|
|
已有API方法 (位于 `@/api/ccdiIntermediary.js`):
|
||
|
|
- `getPersonImportFailures(taskId, pageNum, pageSize)` - 查询个人导入失败记录
|
||
|
|
- `getEntityImportFailures(taskId, pageNum, pageSize)` - 查询实体导入失败记录
|
||
|
|
|
||
|
|
## 5. UI设计
|
||
|
|
|
||
|
|
### 5.1 工具栏按钮
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<el-col :span="1.5" v-if="showPersonFailureButton">
|
||
|
|
<el-tooltip :content="getPersonImportTooltip()" placement="top">
|
||
|
|
<el-button
|
||
|
|
type="warning"
|
||
|
|
plain
|
||
|
|
icon="el-icon-warning"
|
||
|
|
size="mini"
|
||
|
|
@click="viewPersonImportFailures"
|
||
|
|
>查看个人导入失败记录</el-button>
|
||
|
|
</el-tooltip>
|
||
|
|
</el-col>
|
||
|
|
|
||
|
|
<el-col :span="1.5" v-if="showEntityFailureButton">
|
||
|
|
<el-tooltip :content="getEntityImportTooltip()" placement="top">
|
||
|
|
<el-button
|
||
|
|
type="warning"
|
||
|
|
plain
|
||
|
|
icon="el-icon-warning"
|
||
|
|
size="mini"
|
||
|
|
@click="viewEntityImportFailures"
|
||
|
|
>查看实体导入失败记录</el-button>
|
||
|
|
</el-tooltip>
|
||
|
|
</el-col>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 失败记录对话框
|
||
|
|
|
||
|
|
**个人中介失败记录对话框**:
|
||
|
|
- 标题: "个人中介导入失败记录"
|
||
|
|
- 顶部提示: 显示导入统计信息
|
||
|
|
- 表格列: 姓名、证件号码、人员类型、性别、手机号码、所在公司、**失败原因**(最小宽度200px,溢出显示tooltip)
|
||
|
|
- 分页组件: 支持翻页
|
||
|
|
- 底部按钮: "关闭"、"清除历史记录"
|
||
|
|
|
||
|
|
**实体中介失败记录对话框**:
|
||
|
|
- 标题: "实体中介导入失败记录"
|
||
|
|
- 顶部提示: 显示导入统计信息
|
||
|
|
- 表格列: 机构名称、统一社会信用代码、主体类型、企业性质、法定代表人、成立日期、**失败原因**(最小宽度200px,溢出显示tooltip)
|
||
|
|
- 分页组件: 支持翻页
|
||
|
|
- 底部按钮: "关闭"、"清除历史记录"
|
||
|
|
|
||
|
|
## 6. 核心方法设计
|
||
|
|
|
||
|
|
### 6.1 localStorage管理方法
|
||
|
|
|
||
|
|
#### 6.1.1 个人中介导入任务
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
/** 保存个人导入任务到localStorage */
|
||
|
|
savePersonImportTaskToStorage(taskData) {
|
||
|
|
const data = {
|
||
|
|
...taskData,
|
||
|
|
saveTime: Date.now()
|
||
|
|
}
|
||
|
|
localStorage.setItem('intermediary_person_import_last_task', JSON.stringify(data))
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 从localStorage读取个人导入任务 */
|
||
|
|
getPersonImportTaskFromStorage() {
|
||
|
|
try {
|
||
|
|
const data = localStorage.getItem('intermediary_person_import_last_task')
|
||
|
|
if (!data) return null
|
||
|
|
|
||
|
|
const task = JSON.parse(data)
|
||
|
|
|
||
|
|
// 7天过期检查
|
||
|
|
const sevenDays = 7 * 24 * 60 * 60 * 1000
|
||
|
|
if (Date.now() - task.saveTime > sevenDays) {
|
||
|
|
this.clearPersonImportTaskFromStorage()
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
return task
|
||
|
|
} catch (error) {
|
||
|
|
console.error('读取个人导入任务失败:', error)
|
||
|
|
this.clearPersonImportTaskFromStorage()
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 清除个人导入任务 */
|
||
|
|
clearPersonImportTaskFromStorage() {
|
||
|
|
localStorage.removeItem('intermediary_person_import_last_task')
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 6.1.2 实体中介导入任务
|
||
|
|
|
||
|
|
结构同个人中介,方法名为:
|
||
|
|
- `saveEntityImportTaskToStorage(taskData)`
|
||
|
|
- `getEntityImportTaskFromStorage()`
|
||
|
|
- `clearEntityImportTaskFromStorage()`
|
||
|
|
|
||
|
|
### 6.2 导入完成处理
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
/** 处理导入完成 */
|
||
|
|
handleImportComplete(importData) {
|
||
|
|
const { taskId, hasFailures, importType, totalCount, successCount, failureCount } = importData
|
||
|
|
|
||
|
|
if (importType === 'person') {
|
||
|
|
// 保存个人导入任务
|
||
|
|
this.savePersonImportTaskToStorage({
|
||
|
|
taskId,
|
||
|
|
hasFailures,
|
||
|
|
totalCount,
|
||
|
|
successCount,
|
||
|
|
failureCount
|
||
|
|
})
|
||
|
|
|
||
|
|
// 更新按钮显示
|
||
|
|
this.showPersonFailureButton = hasFailures
|
||
|
|
this.currentPersonTaskId = taskId
|
||
|
|
|
||
|
|
} else if (importType === 'entity') {
|
||
|
|
// 保存实体导入任务
|
||
|
|
this.saveEntityImportTaskToStorage({
|
||
|
|
taskId,
|
||
|
|
hasFailures,
|
||
|
|
totalCount,
|
||
|
|
successCount,
|
||
|
|
failureCount
|
||
|
|
})
|
||
|
|
|
||
|
|
// 更新按钮显示
|
||
|
|
this.showEntityFailureButton = hasFailures
|
||
|
|
this.currentEntityTaskId = taskId
|
||
|
|
}
|
||
|
|
|
||
|
|
// 刷新列表
|
||
|
|
this.getList()
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.3 查看失败记录
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
/** 查看个人导入失败记录 */
|
||
|
|
viewPersonImportFailures() {
|
||
|
|
this.personFailureDialogVisible = true
|
||
|
|
this.getPersonFailureList()
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 查询个人失败记录列表 */
|
||
|
|
getPersonFailureList() {
|
||
|
|
this.personFailureLoading = true
|
||
|
|
getPersonImportFailures(
|
||
|
|
this.currentPersonTaskId,
|
||
|
|
this.personFailureQueryParams.pageNum,
|
||
|
|
this.personFailureQueryParams.pageSize
|
||
|
|
).then(response => {
|
||
|
|
this.personFailureList = response.rows
|
||
|
|
this.personFailureTotal = response.total
|
||
|
|
this.personFailureLoading = false
|
||
|
|
}).catch(error => {
|
||
|
|
this.personFailureLoading = false
|
||
|
|
// 错误处理: 404表示记录已过期
|
||
|
|
if (error.response?.status === 404) {
|
||
|
|
this.$modal.msgWarning('导入记录已过期,无法查看失败记录')
|
||
|
|
this.clearPersonImportTaskFromStorage()
|
||
|
|
this.showPersonFailureButton = false
|
||
|
|
this.personFailureDialogVisible = false
|
||
|
|
} else {
|
||
|
|
this.$modal.msgError('查询失败记录失败')
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.4 清除历史记录
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
/** 清除个人导入历史记录 */
|
||
|
|
clearPersonImportHistory() {
|
||
|
|
this.$confirm('确认清除上次导入记录?', '提示', {
|
||
|
|
confirmButtonText: '确定',
|
||
|
|
cancelButtonText: '取消',
|
||
|
|
type: 'warning'
|
||
|
|
}).then(() => {
|
||
|
|
this.clearPersonImportTaskFromStorage()
|
||
|
|
this.showPersonFailureButton = false
|
||
|
|
this.currentPersonTaskId = null
|
||
|
|
this.personFailureDialogVisible = false
|
||
|
|
this.$message.success('已清除')
|
||
|
|
}).catch(() => {})
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 7. 生命周期管理
|
||
|
|
|
||
|
|
### 7.1 created钩子
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
created() {
|
||
|
|
this.getList()
|
||
|
|
this.loadEnumOptions()
|
||
|
|
this.restoreImportState() // 恢复导入状态
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.2 恢复导入状态
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
/** 恢复导入状态 */
|
||
|
|
restoreImportState() {
|
||
|
|
// 恢复个人中介导入状态
|
||
|
|
const personTask = this.getPersonImportTaskFromStorage()
|
||
|
|
if (personTask && personTask.hasFailures && personTask.taskId) {
|
||
|
|
this.currentPersonTaskId = personTask.taskId
|
||
|
|
this.showPersonFailureButton = true
|
||
|
|
}
|
||
|
|
|
||
|
|
// 恢复实体中介导入状态
|
||
|
|
const entityTask = this.getEntityImportTaskFromStorage()
|
||
|
|
if (entityTask && entityTask.hasFailures && entityTask.taskId) {
|
||
|
|
this.currentEntityTaskId = entityTask.taskId
|
||
|
|
this.showEntityFailureButton = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 8. 边界情况处理
|
||
|
|
|
||
|
|
### 8.1 记录过期
|
||
|
|
|
||
|
|
- localStorage中存储的记录超过7天,自动清除
|
||
|
|
- 后端接口返回404时,提示用户"导入记录已过期",并清除本地存储
|
||
|
|
- 清除后隐藏对应的"查看失败记录"按钮
|
||
|
|
|
||
|
|
### 8.2 并发导入
|
||
|
|
|
||
|
|
- 每次新导入开始前,清除旧的导入记录
|
||
|
|
- 同一类型的导入进行时,取消之前的轮询
|
||
|
|
- 只保留最近一次的导入任务信息
|
||
|
|
|
||
|
|
### 8.3 网络错误
|
||
|
|
|
||
|
|
- 查询失败记录时网络错误,显示友好的错误提示
|
||
|
|
- 不影响页面其他功能的正常使用
|
||
|
|
|
||
|
|
## 9. 测试要点
|
||
|
|
|
||
|
|
### 9.1 功能测试
|
||
|
|
|
||
|
|
1. **个人中介导入失败场景**
|
||
|
|
- 导入包含错误数据的Excel文件
|
||
|
|
- 验证失败记录按钮是否显示
|
||
|
|
- 点击按钮查看失败记录
|
||
|
|
- 验证失败原因是否正确显示
|
||
|
|
|
||
|
|
2. **实体中介导入失败场景**
|
||
|
|
- 导入包含错误数据的Excel文件
|
||
|
|
- 验证失败记录按钮是否显示
|
||
|
|
- 点击按钮查看失败记录
|
||
|
|
- 验证失败原因是否正确显示
|
||
|
|
|
||
|
|
3. **localStorage持久化**
|
||
|
|
- 导入失败后刷新页面
|
||
|
|
- 验证"查看失败记录"按钮是否仍然显示
|
||
|
|
- 验证点击后能否正常查看失败记录
|
||
|
|
|
||
|
|
4. **分页功能**
|
||
|
|
- 失败记录超过10条时
|
||
|
|
- 验证分页组件是否正常工作
|
||
|
|
- 验证翻页后数据是否正确
|
||
|
|
|
||
|
|
5. **清除历史记录**
|
||
|
|
- 点击"清除历史记录"按钮
|
||
|
|
- 验证localStorage是否清除
|
||
|
|
- 验证按钮是否隐藏
|
||
|
|
- 再次点击导入,验证新记录是否正常
|
||
|
|
|
||
|
|
6. **记录过期处理**
|
||
|
|
- 手动修改localStorage中的saveTime模拟过期
|
||
|
|
- 刷新页面,验证按钮是否隐藏
|
||
|
|
- 或点击查看,验证是否提示"记录已过期"
|
||
|
|
|
||
|
|
### 9.2 兼容性测试
|
||
|
|
|
||
|
|
1. **浏览器兼容性**
|
||
|
|
- Chrome
|
||
|
|
- Firefox
|
||
|
|
- Edge
|
||
|
|
- Safari
|
||
|
|
|
||
|
|
2. **数据量大时性能测试**
|
||
|
|
- 导入1000条数据,其中100条失败
|
||
|
|
- 验证查询速度和渲染性能
|
||
|
|
|
||
|
|
## 10. 参考实现
|
||
|
|
|
||
|
|
本设计参考了员工管理页面 (`ccdiEmployee/index.vue`) 的导入失败记录查看功能的实现,主要参考点:
|
||
|
|
|
||
|
|
1. localStorage存储模式
|
||
|
|
2. 失败记录对话框布局
|
||
|
|
3. 分页查询逻辑
|
||
|
|
4. 错误处理机制
|
||
|
|
5. 过期记录清理逻辑
|
||
|
|
|
||
|
|
## 11. 变更历史
|
||
|
|
|
||
|
|
| 日期 | 版本 | 变更内容 | 作者 |
|
||
|
|
|------|------|----------|------|
|
||
|
|
| 2026-02-08 | 1.0 | 初始设计 | Claude |
|