This commit is contained in:
Your Name
2026-04-10 14:43:58 +08:00
parent fdf714f833
commit 4909ec6daa
38 changed files with 6549 additions and 735 deletions
+321
View File
@@ -0,0 +1,321 @@
# 处方组件重复请求修复
## 问题描述
在预约列表页面点击"开处方"或"查看病历"按钮时,会出现重复请求 `/tcm.prescription/detail` 接口的问题,导致错误。
## 问题原因
### 1. 快速连续点击
用户可能快速连续点击按钮,导致 `open()``openById()` 方法被多次调用。
### 2. 异步操作未加锁
`open()``openById()` 方法是异步的,但没有加锁机制,导致:
- 第一次点击开始执行
- 第二次点击也开始执行
- 两次请求同时发送
### 3. 代码流程
```javascript
const open = async (data: any) => {
// 没有防重复检查
await loadDictOptions()
if (appointmentId) {
const existing = await prescriptionGetByAppointment(...) // 请求1
if (existing?.id) {
const detail = await prescriptionDetail(...) // 请求2
}
}
}
```
## 解决方案
### 添加防重复打开标志
使用 `isOpening` 标志来防止重复调用:
```typescript
// 防止重复打开
const isOpening = ref(false)
const open = async (data: any, options?: { templateId?: number; stationName?: string }) => {
// 防止重复打开
if (isOpening.value) {
console.warn('处方正在打开中,请勿重复点击')
return
}
isOpening.value = true
try {
// 原有逻辑...
} finally {
// 延迟重置,避免快速连续点击
setTimeout(() => {
isOpening.value = false
}, 500)
}
}
```
### 关键点
1. **检查标志**:方法开始时检查 `isOpening.value`,如果为 true 则直接返回
2. **设置标志**:开始执行时设置 `isOpening.value = true`
3. **finally 重置**:无论成功还是失败,都在 finally 中重置标志
4. **延迟重置**:使用 `setTimeout` 延迟 500ms 重置,防止快速连续点击
## 修改的文件
**文件**`admin/src/components/tcm-prescription/index.vue`
### 1. 添加防重复标志
```typescript
// 防止重复打开
const isOpening = ref(false)
```
### 2. 修改 open 方法
```typescript
const open = async (data: any, options?: { templateId?: number; stationName?: string }) => {
// 防止重复打开
if (isOpening.value) {
console.warn('处方正在打开中,请勿重复点击')
return
}
isOpening.value = true
try {
// 原有逻辑...
} finally {
setTimeout(() => {
isOpening.value = false
}, 500)
}
}
```
### 3. 修改 openById 方法
```typescript
const openById = async (prescriptionId: number) => {
// 防止重复打开
if (isOpening.value) {
console.warn('处方正在打开中,请勿重复点击')
return
}
isOpening.value = true
try {
const res = await prescriptionDetail({ id: prescriptionId })
savedPrescription.value = res
if (res?.case_record && Object.keys(res.case_record).length > 0) {
await loadDictOptions()
}
visible.value = true
} catch (e) {
feedback.msgError('加载处方失败')
} finally {
setTimeout(() => {
isOpening.value = false
}, 500)
}
}
```
## 工作流程
### 修复前
```
用户点击"开处方"按钮
调用 open() 方法
用户再次点击(快速连续点击)
再次调用 open() 方法
两次请求同时发送
❌ 重复请求错误
```
### 修复后
```
用户点击"开处方"按钮
调用 open() 方法
设置 isOpening = true
用户再次点击(快速连续点击)
检查 isOpening = true
✓ 直接返回,不执行
第一次请求完成
500ms 后重置 isOpening = false
```
## 测试场景
### 场景1:快速连续点击
**操作**:快速连续点击"开处方"按钮 2-3 次
**预期结果**
- 只发送一次请求
- 控制台显示警告:"处方正在打开中,请勿重复点击"
- 处方正常打开
---
### 场景2:正常点击
**操作**:正常点击"开处方"按钮
**预期结果**
- 发送请求
- 处方正常打开
- 无警告信息
---
### 场景3:间隔点击
**操作**:点击"开处方",等待 1 秒后再次点击
**预期结果**
- 第一次点击正常打开
- 第二次点击也正常打开(因为已过 500ms)
---
### 场景4:查看已有处方
**操作**:点击"查看病历"按钮
**预期结果**
- 调用 `prescriptionGetByAppointment` 查询
- 如果有处方,调用 `prescriptionDetail` 获取详情
- 只发送一次 detail 请求
---
### 场景5:请求失败
**操作**:点击"开处方",但请求失败
**预期结果**
- 显示错误提示
- 500ms 后 `isOpening` 重置
- 可以再次点击
## 其他优化建议
### 1. 按钮禁用状态
可以在按钮上添加 loading 状态:
```vue
<el-button
:loading="isOpening"
@click="handlePrescription(row)"
>
{{ prescriptionActionLabel(row) }}
</el-button>
```
但这需要将 `isOpening` 暴露给父组件,可能不太合适。
### 2. 防抖处理
可以使用 lodash 的 debounce 函数:
```typescript
import { debounce } from 'lodash-es'
const debouncedOpen = debounce(open, 500, { leading: true, trailing: false })
```
但当前的解决方案已经足够简单有效。
### 3. 请求取消
可以使用 AbortController 取消重复请求:
```typescript
let abortController: AbortController | null = null
const open = async (data: any) => {
if (abortController) {
abortController.abort()
}
abortController = new AbortController()
// 在请求中使用 signal
await fetch(url, { signal: abortController.signal })
}
```
但这需要修改 API 调用层,改动较大。
## 注意事项
### 1. 延迟时间
当前设置为 500ms,可以根据实际情况调整:
- 太短(< 300ms):可能无法有效防止快速连续点击
- 太长(> 1000ms):用户体验不好,需要等待较长时间
### 2. 控制台警告
`console.warn` 只在开发环境有用,生产环境可以考虑:
- 移除警告
- 或者改为用户提示:`feedback.msgWarning('请勿重复点击')`
### 3. 多个入口
如果有多个地方调用 `open()` 方法,都会受到这个锁的保护。
## 相关文件
### 修改的文件
- `admin/src/components/tcm-prescription/index.vue` - 处方组件
### 调用的文件
- `admin/src/views/tcm/appointment/list.vue` - 预约列表
- 其他可能调用处方组件的页面
## 总结
✅ 添加 `isOpening` 标志防止重复打开
✅ 使用 try-finally 确保标志正确重置
✅ 延迟 500ms 重置,防止快速连续点击
✅ 同时修复 `open()``openById()` 方法
✅ 添加控制台警告提示
**效果**
- 防止重复请求
- 避免接口错误
- 提升用户体验
- 代码简单易维护
**下一步**
1. 测试快速连续点击
2. 测试正常点击流程
3. 测试请求失败情况
4. 考虑是否需要用户提示