|
@@ -297,7 +297,20 @@
|
|
|
<el-row :gutter="16">
|
|
<el-row :gutter="16">
|
|
|
<el-col :span="24">
|
|
<el-col :span="24">
|
|
|
<div style="margin: 8px 0" v-if="!isView" >
|
|
<div style="margin: 8px 0" v-if="!isView" >
|
|
|
- <el-input ref="returnSearchInputRef" :disabled="isView" v-model="returnSearchText" placeholder="请扫描设备条码添加" style="width: 300px" @keyup.enter.prevent="handleReturnScanSearch" />
|
|
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ ref="returnSearchInputRef"
|
|
|
|
|
+ v-model="returnSearchText"
|
|
|
|
|
+ class="device-code-input"
|
|
|
|
|
+ placeholder="设备编号(支持扫码枪/RFID批量录入)"
|
|
|
|
|
+ :disabled="loading"
|
|
|
|
|
+ autocomplete="off"
|
|
|
|
|
+ rows="1"
|
|
|
|
|
+ style="width: 300px; vertical-align: top;"
|
|
|
|
|
+ @input="handleReturnDeviceInput"
|
|
|
|
|
+ @keydown="handleReturnRFIDKeydown"
|
|
|
|
|
+ @blur="handleReturnDeviceBlur"
|
|
|
|
|
+ @keydown.enter.prevent="handleReturnDeviceEnter"
|
|
|
|
|
+ ></textarea>
|
|
|
<el-button type="primary" style="margin-left: 16px" @click="handleAdd" :disabled="isView">手动归还</el-button>
|
|
<el-button type="primary" style="margin-left: 16px" @click="handleAdd" :disabled="isView">手动归还</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<el-table :data="returnDeviceList" border style="width: 100%" >
|
|
<el-table :data="returnDeviceList" border style="width: 100%" >
|
|
@@ -716,6 +729,13 @@ const returnDeviceList = ref<DeviceListItem[]>([]);
|
|
|
const returnAccessoryInfo = ref('');
|
|
const returnAccessoryInfo = ref('');
|
|
|
const returnRemark = ref('');
|
|
const returnRemark = ref('');
|
|
|
const selectedDevices = ref<DeviceListItem[]>([]); // 添加选中设备列表
|
|
const selectedDevices = ref<DeviceListItem[]>([]); // 添加选中设备列表
|
|
|
|
|
+// 归还流程 RFID高速扫码缓冲相关
|
|
|
|
|
+const returnRfidInputBuffer = ref('');
|
|
|
|
|
+let returnRfidInputTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
|
+// 记录归还流程最后一次输入时间,用于检测是否是高速扫码输入
|
|
|
|
|
+let returnLastInputTime = 0;
|
|
|
|
|
+// 记录归还流程连续输入次数,用于判断是否是扫码输入
|
|
|
|
|
+let returnConsecutiveInputCount = 0;
|
|
|
|
|
|
|
|
// 备注显示值计算属性:查看模式下显示 problem_remarks[0],否则显示 returnRemark
|
|
// 备注显示值计算属性:查看模式下显示 problem_remarks[0],否则显示 returnRemark
|
|
|
const displayReturnRemark = computed({
|
|
const displayReturnRemark = computed({
|
|
@@ -946,10 +966,30 @@ const clearRFIDInputTimer = () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 清理归还流程RFID输入定时器
|
|
|
|
|
+const clearReturnRFIDInputTimer = () => {
|
|
|
|
|
+ if (returnRfidInputTimer) {
|
|
|
|
|
+ clearTimeout(returnRfidInputTimer);
|
|
|
|
|
+ returnRfidInputTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 提交RFID缓冲区内容
|
|
// 提交RFID缓冲区内容
|
|
|
const submitRFIDBuffer = () => {
|
|
const submitRFIDBuffer = () => {
|
|
|
- if (!readRuleConfig.value.enableRFIDBuffer) return;
|
|
|
|
|
- if (!rfidInputBuffer.value) return;
|
|
|
|
|
|
|
+ if (!readRuleConfig.value.enableRFIDBuffer) {
|
|
|
|
|
+ // 如果未启用RFID缓冲,直接使用 searchText 进行搜索
|
|
|
|
|
+ if (searchText.value.trim()) {
|
|
|
|
|
+ handleScanSearch();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!rfidInputBuffer.value) {
|
|
|
|
|
+ // 如果没有缓冲内容,使用 searchText 进行搜索
|
|
|
|
|
+ if (searchText.value.trim()) {
|
|
|
|
|
+ handleScanSearch();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
const bufferedValue = rfidInputBuffer.value;
|
|
const bufferedValue = rfidInputBuffer.value;
|
|
|
rfidInputBuffer.value = '';
|
|
rfidInputBuffer.value = '';
|
|
|
clearRFIDInputTimer();
|
|
clearRFIDInputTimer();
|
|
@@ -969,57 +1009,33 @@ const scheduleRFIDBufferSubmit = () => {
|
|
|
}, delay);
|
|
}, delay);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 处理设备输入(用于同步 textarea 内容到缓冲区,作为备用方案防止第一个字符丢失)
|
|
|
|
|
|
|
+// 处理设备输入(用于同步 textarea 内容,仅在缓冲模式下使用)
|
|
|
const handleDeviceInput = () => {
|
|
const handleDeviceInput = () => {
|
|
|
- // 如果 textarea 有内容但缓冲区为空,可能是第一个字符通过默认行为输入了
|
|
|
|
|
- // 这种情况发生在:第一个字符没有被拦截(因为判断逻辑),但后续字符是高速输入
|
|
|
|
|
- if (rfidInputBuffer.value === '' && searchText.value && searchText.value.length > 0) {
|
|
|
|
|
- // 同步到缓冲区,确保内容不丢失
|
|
|
|
|
- rfidInputBuffer.value = searchText.value;
|
|
|
|
|
- // 如果内容长度大于1,说明已经有多个字符,可能是高速输入
|
|
|
|
|
- if (searchText.value.length > 1) {
|
|
|
|
|
- lastInputTime = Date.now();
|
|
|
|
|
- consecutiveInputCount = searchText.value.length;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 单个字符,初始化时间戳
|
|
|
|
|
- lastInputTime = Date.now();
|
|
|
|
|
- consecutiveInputCount = 1;
|
|
|
|
|
- }
|
|
|
|
|
- } else if (rfidInputBuffer.value && searchText.value !== rfidInputBuffer.value) {
|
|
|
|
|
- // 如果缓冲区有内容但 textarea 内容不同,同步缓冲区内容到 textarea
|
|
|
|
|
- // 这确保显示的内容与缓冲区一致
|
|
|
|
|
|
|
+ // 如果缓冲区有内容,确保 textarea 显示与缓冲区一致
|
|
|
|
|
+ // 这主要用于处理退格键等操作后的同步
|
|
|
|
|
+ if (rfidInputBuffer.value && searchText.value !== rfidInputBuffer.value) {
|
|
|
searchText.value = rfidInputBuffer.value;
|
|
searchText.value = rfidInputBuffer.value;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 注意:正常输入时,不干预 textarea 的内容,让默认行为处理
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 检测是否是高速扫码输入(通过输入速度判断)
|
|
// 检测是否是高速扫码输入(通过输入速度判断)
|
|
|
-const isHighSpeedInput = (): boolean => {
|
|
|
|
|
|
|
+// 返回值:{ isHighSpeed: boolean, isFirstInput: boolean, isNormalInput: boolean, timeSinceLastInput: number }
|
|
|
|
|
+const checkInputSpeed = () => {
|
|
|
const now = Date.now();
|
|
const now = Date.now();
|
|
|
const timeSinceLastInput = lastInputTime > 0 ? now - lastInputTime : 0;
|
|
const timeSinceLastInput = lastInputTime > 0 ? now - lastInputTime : 0;
|
|
|
- lastInputTime = now;
|
|
|
|
|
-
|
|
|
|
|
- // 如果是第一次输入(lastInputTime 之前为 0),先假设可能是高速输入
|
|
|
|
|
- // 等待第二个字符来判断
|
|
|
|
|
- if (timeSinceLastInput === 0) {
|
|
|
|
|
- return false; // 第一次输入,暂时返回 false,但会在 handleRFIDKeydown 中特殊处理
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const isFirstInput = lastInputTime === 0;
|
|
|
|
|
|
|
|
- // 如果输入间隔很短(小于配置的最小输入速度),认为是高速扫码
|
|
|
|
|
- if (timeSinceLastInput < readRuleConfig.value.minInputSpeed && timeSinceLastInput > 0) {
|
|
|
|
|
- consecutiveInputCount++;
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 如果输入间隔较长,重置计数
|
|
|
|
|
- if (timeSinceLastInput > 200) {
|
|
|
|
|
- consecutiveInputCount = 0;
|
|
|
|
|
- // 如果之前有缓冲内容,先提交
|
|
|
|
|
- if (rfidInputBuffer.value) {
|
|
|
|
|
- submitRFIDBuffer();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!isFirstInput) {
|
|
|
|
|
+ lastInputTime = now;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ isFirstInput,
|
|
|
|
|
+ timeSinceLastInput,
|
|
|
|
|
+ isHighSpeed: timeSinceLastInput > 0 && timeSinceLastInput < readRuleConfig.value.minInputSpeed,
|
|
|
|
|
+ isNormalInput: timeSinceLastInput > 200
|
|
|
|
|
+ };
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 处理RFID键盘输入(高速扫码)
|
|
// 处理RFID键盘输入(高速扫码)
|
|
@@ -1036,12 +1052,8 @@ const handleRFIDKeydown = (event: KeyboardEvent) => {
|
|
|
|
|
|
|
|
// 处理回车键
|
|
// 处理回车键
|
|
|
if (event.key === 'Enter') {
|
|
if (event.key === 'Enter') {
|
|
|
- // 如果有缓冲内容,提交缓冲
|
|
|
|
|
- if (rfidInputBuffer.value) {
|
|
|
|
|
- event.preventDefault();
|
|
|
|
|
- submitRFIDBuffer();
|
|
|
|
|
- }
|
|
|
|
|
- // 否则让默认行为处理(触发 handleDeviceEnter)
|
|
|
|
|
|
|
+ // 回车键由 @keydown.enter.prevent="handleDeviceEnter" 处理
|
|
|
|
|
+ // 这里不处理,让 handleDeviceEnter 统一处理
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1059,38 +1071,57 @@ const handleRFIDKeydown = (event: KeyboardEvent) => {
|
|
|
|
|
|
|
|
// 处理普通字符输入
|
|
// 处理普通字符输入
|
|
|
if (event.key.length === 1) {
|
|
if (event.key.length === 1) {
|
|
|
- // 检测是否是高速扫码输入
|
|
|
|
|
- const isHighSpeed = isHighSpeedInput();
|
|
|
|
|
|
|
+ const inputInfo = checkInputSpeed();
|
|
|
|
|
+ const { isFirstInput, isHighSpeed, isNormalInput, timeSinceLastInput } = inputInfo;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是第一次输入,不拦截,让默认行为处理,只记录时间戳
|
|
|
|
|
+ if (isFirstInput) {
|
|
|
|
|
+ lastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 判断是否是第一次输入(缓冲区为空且计数为0且时间戳为0)
|
|
|
|
|
- const isFirstInput = rfidInputBuffer.value === '' && consecutiveInputCount === 0;
|
|
|
|
|
|
|
+ // 如果检测到是正常输入(间隔 > 200ms),处理之前的缓冲内容
|
|
|
|
|
+ if (isNormalInput) {
|
|
|
|
|
+ // 如果之前有缓冲内容,先提交
|
|
|
|
|
+ if (rfidInputBuffer.value) {
|
|
|
|
|
+ submitRFIDBuffer();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 重置状态
|
|
|
|
|
+ consecutiveInputCount = 0;
|
|
|
|
|
+ lastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 如果是高速输入、已经有缓冲内容、或者第一次输入,使用缓冲模式
|
|
|
|
|
- // 第一次输入时,先捕获到缓冲区,等待第二个字符来判断是否是高速输入
|
|
|
|
|
- if (isHighSpeed || rfidInputBuffer.value || consecutiveInputCount > 0 || isFirstInput) {
|
|
|
|
|
|
|
+ // 如果检测到是高速输入(间隔 < 30ms),使用缓冲模式
|
|
|
|
|
+ if (isHighSpeed) {
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
|
event.stopPropagation(); // 阻止事件冒泡,避免影响其他元素
|
|
event.stopPropagation(); // 阻止事件冒泡,避免影响其他元素
|
|
|
- rfidInputBuffer.value += event.key;
|
|
|
|
|
- searchText.value = rfidInputBuffer.value;
|
|
|
|
|
|
|
|
|
|
- // 如果是第一次输入,初始化时间戳
|
|
|
|
|
- if (isFirstInput) {
|
|
|
|
|
- lastInputTime = Date.now();
|
|
|
|
|
- consecutiveInputCount = 1;
|
|
|
|
|
|
|
+ // 如果是第一次检测到高速输入(之前没有缓冲内容),需要将 textarea 中已有的内容也加入缓冲区
|
|
|
|
|
+ if (rfidInputBuffer.value === '' && searchText.value) {
|
|
|
|
|
+ // textarea 中已经有内容(第一个字符已经通过默认行为输入了)
|
|
|
|
|
+ // 将整个内容作为缓冲区的初始值,然后追加当前字符
|
|
|
|
|
+ rfidInputBuffer.value = searchText.value + event.key;
|
|
|
} else {
|
|
} else {
|
|
|
- consecutiveInputCount++;
|
|
|
|
|
|
|
+ // 否则直接追加当前字符
|
|
|
|
|
+ rfidInputBuffer.value += event.key;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ searchText.value = rfidInputBuffer.value;
|
|
|
|
|
+ consecutiveInputCount++;
|
|
|
scheduleRFIDBufferSubmit();
|
|
scheduleRFIDBufferSubmit();
|
|
|
- } else {
|
|
|
|
|
- // 如果是正常输入且之前有缓冲内容,先提交缓冲内容
|
|
|
|
|
- if (rfidInputBuffer.value) {
|
|
|
|
|
- submitRFIDBuffer();
|
|
|
|
|
- // 提交后,当前字符走默认行为(正常输入)
|
|
|
|
|
- // 注意:这里不阻止默认行为,让字符正常输入到 textarea
|
|
|
|
|
- }
|
|
|
|
|
- // 否则让默认行为处理(正常输入)
|
|
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果输入间隔在 30-200ms 之间,可能是正常输入,不拦截
|
|
|
|
|
+ // 但如果之前有缓冲内容,说明高速输入已结束,先提交
|
|
|
|
|
+ if (rfidInputBuffer.value) {
|
|
|
|
|
+ submitRFIDBuffer();
|
|
|
|
|
+ consecutiveInputCount = 0;
|
|
|
}
|
|
}
|
|
|
|
|
+ lastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -1123,13 +1154,10 @@ const handleDeviceBlur = (event: FocusEvent) => {
|
|
|
// 处理回车键
|
|
// 处理回车键
|
|
|
const handleDeviceEnter = (event: KeyboardEvent) => {
|
|
const handleDeviceEnter = (event: KeyboardEvent) => {
|
|
|
if (event.shiftKey) return;
|
|
if (event.shiftKey) return;
|
|
|
- if (readRuleConfig.value.enableRFIDBuffer) {
|
|
|
|
|
- event.preventDefault();
|
|
|
|
|
- submitRFIDBuffer();
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
|
- handleScanSearch();
|
|
|
|
|
|
|
+ // 无论是否启用RFID缓冲,都调用 submitRFIDBuffer
|
|
|
|
|
+ // submitRFIDBuffer 内部会判断是否有缓冲内容,如果没有则直接使用 searchText 搜索
|
|
|
|
|
+ submitRFIDBuffer();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 分割多个设备编号(支持分隔符)
|
|
// 分割多个设备编号(支持分隔符)
|
|
@@ -1160,7 +1188,7 @@ const splitDeviceCodes = (input: string): string[] => {
|
|
|
return [trimmed];
|
|
return [trimmed];
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 处理扫码搜索(支持批量)
|
|
|
|
|
|
|
+// 处理扫码搜索(支持批量,优化并发请求)
|
|
|
const handleScanSearch = async (event?: Event) => {
|
|
const handleScanSearch = async (event?: Event) => {
|
|
|
// 阻止默认行为,防止表单提交导致页面刷新
|
|
// 阻止默认行为,防止表单提交导致页面刷新
|
|
|
if (event) {
|
|
if (event) {
|
|
@@ -1182,73 +1210,114 @@ const handleScanSearch = async (event?: Event) => {
|
|
|
const codes = splitDeviceCodes(searchText.value);
|
|
const codes = splitDeviceCodes(searchText.value);
|
|
|
if (codes.length === 0) return;
|
|
if (codes.length === 0) return;
|
|
|
|
|
|
|
|
|
|
+ // 去重处理,避免重复请求同一个设备编号
|
|
|
|
|
+ const uniqueCodes = Array.from(new Set(codes.map(code => code.trim()))).filter(Boolean);
|
|
|
|
|
+ if (uniqueCodes.length === 0) return;
|
|
|
|
|
+
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
let successCount = 0;
|
|
let successCount = 0;
|
|
|
let failCount = 0;
|
|
let failCount = 0;
|
|
|
|
|
+ const errorMessages: string[] = [];
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // 批量处理设备编号
|
|
|
|
|
- for (const code of codes) {
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await deviceApi.GetList({
|
|
|
|
|
|
|
+ // 批量并发处理设备编号(使用 Promise.allSettled 确保所有请求都完成)
|
|
|
|
|
+ // 限制并发数量,避免同时发起过多请求
|
|
|
|
|
+ const batchSize = 10; // 每批处理10个
|
|
|
|
|
+ const batches: string[][] = [];
|
|
|
|
|
+ for (let i = 0; i < uniqueCodes.length; i += batchSize) {
|
|
|
|
|
+ batches.push(uniqueCodes.slice(i, i + batchSize));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 逐批处理,每批内部并发
|
|
|
|
|
+ for (const batch of batches) {
|
|
|
|
|
+ const promises = batch.map(async (code) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await deviceApi.GetList({
|
|
|
page: 1,
|
|
page: 1,
|
|
|
limit: 1,
|
|
limit: 1,
|
|
|
- code: code.trim(),
|
|
|
|
|
|
|
+ code: code,
|
|
|
status: 1
|
|
status: 1
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (res.data && res.data.length > 0) {
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (res.data && res.data.length > 0) {
|
|
|
const device = res.data[0];
|
|
const device = res.data[0];
|
|
|
if (device.available_quantity <= 0) {
|
|
if (device.available_quantity <= 0) {
|
|
|
- ElMessage.warning(`设备 ${code} 当前无可用数量`);
|
|
|
|
|
- failCount++;
|
|
|
|
|
- continue;
|
|
|
|
|
|
|
+ return { success: false, code, message: `设备 ${code} 当前无可用数量` };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 检查是否已存在
|
|
// 检查是否已存在
|
|
|
const existingDevice = deviceList.value.find(d => d.device_no === device.id);
|
|
const existingDevice = deviceList.value.find(d => d.device_no === device.id);
|
|
|
if (existingDevice) {
|
|
if (existingDevice) {
|
|
|
- existingDevice.borrow_count++;
|
|
|
|
|
- successCount++;
|
|
|
|
|
|
|
+ existingDevice.borrow_count++;
|
|
|
|
|
+ return { success: true, code, device: null };
|
|
|
} else {
|
|
} else {
|
|
|
- // 添加新设备到列表
|
|
|
|
|
- deviceList.value.push({
|
|
|
|
|
- device_no: device.id,
|
|
|
|
|
- device_code: device.code,
|
|
|
|
|
- device_type: device.category_name,
|
|
|
|
|
- device_name: device.name || '',
|
|
|
|
|
- borrow_count: 1,
|
|
|
|
|
- brand: device.brand ||device.device_brand || '',
|
|
|
|
|
- device_specification: device.specification,
|
|
|
|
|
- model: device.model || '',
|
|
|
|
|
- warehouse: device.warehouse,
|
|
|
|
|
- available_quantity:device.available_quantity
|
|
|
|
|
- });
|
|
|
|
|
- successCount++;
|
|
|
|
|
|
|
+ // 添加新设备到列表
|
|
|
|
|
+ const newDevice = {
|
|
|
|
|
+ device_no: device.id,
|
|
|
|
|
+ device_code: device.code,
|
|
|
|
|
+ device_type: device.category_name,
|
|
|
|
|
+ device_name: device.name || '',
|
|
|
|
|
+ borrow_count: 1,
|
|
|
|
|
+ brand: device.brand || device.device_brand || '',
|
|
|
|
|
+ device_specification: device.specification,
|
|
|
|
|
+ model: device.model || '',
|
|
|
|
|
+ warehouse: device.warehouse,
|
|
|
|
|
+ available_quantity: device.available_quantity
|
|
|
|
|
+ };
|
|
|
|
|
+ deviceList.value.push(newDevice);
|
|
|
|
|
+ return { success: true, code, device: newDevice };
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- ElMessage.warning(`未找到设备编号: ${code}`);
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return { success: false, code, message: `未找到设备编号: ${code}` };
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(`处理设备编号 ${code} 失败:`, error);
|
|
|
|
|
+ return { success: false, code, message: `处理设备编号 ${code} 失败` };
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 等待当前批次完成
|
|
|
|
|
+ const results = await Promise.allSettled(promises);
|
|
|
|
|
+
|
|
|
|
|
+ // 统计结果
|
|
|
|
|
+ results.forEach((result, index) => {
|
|
|
|
|
+ if (result.status === 'fulfilled') {
|
|
|
|
|
+ const { success, message } = result.value;
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ successCount++;
|
|
|
|
|
+ } else {
|
|
|
failCount++;
|
|
failCount++;
|
|
|
|
|
+ if (message) {
|
|
|
|
|
+ errorMessages.push(message);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ failCount++;
|
|
|
|
|
+ errorMessages.push(`处理设备编号 ${batch[index]} 时发生错误`);
|
|
|
}
|
|
}
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error(`处理设备编号 ${code} 失败:`, error);
|
|
|
|
|
- failCount++;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 显示批量处理结果
|
|
// 显示批量处理结果
|
|
|
- if (codes.length > 1) {
|
|
|
|
|
|
|
+ if (uniqueCodes.length > 1) {
|
|
|
if (successCount > 0 && failCount === 0) {
|
|
if (successCount > 0 && failCount === 0) {
|
|
|
ElMessage.success(`成功添加 ${successCount} 个设备`);
|
|
ElMessage.success(`成功添加 ${successCount} 个设备`);
|
|
|
} else if (successCount > 0 && failCount > 0) {
|
|
} else if (successCount > 0 && failCount > 0) {
|
|
|
ElMessage.warning(`成功添加 ${successCount} 个设备,失败 ${failCount} 个`);
|
|
ElMessage.warning(`成功添加 ${successCount} 个设备,失败 ${failCount} 个`);
|
|
|
|
|
+ // 只显示前3个错误信息,避免消息过多
|
|
|
|
|
+ if (errorMessages.length > 0) {
|
|
|
|
|
+ console.warn('批量处理错误详情:', errorMessages.slice(0, 3));
|
|
|
|
|
+ }
|
|
|
} else if (failCount > 0) {
|
|
} else if (failCount > 0) {
|
|
|
ElMessage.error(`添加失败,共 ${failCount} 个设备编号`);
|
|
ElMessage.error(`添加失败,共 ${failCount} 个设备编号`);
|
|
|
}
|
|
}
|
|
|
} else if (successCount > 0) {
|
|
} else if (successCount > 0) {
|
|
|
ElMessage.success('设备已添加到列表');
|
|
ElMessage.success('设备已添加到列表');
|
|
|
|
|
+ } else if (failCount > 0 && errorMessages.length > 0) {
|
|
|
|
|
+ ElMessage.warning(errorMessages[0]);
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ console.error('批量处理设备失败:', error);
|
|
|
ElMessage.error('搜索设备失败');
|
|
ElMessage.error('搜索设备失败');
|
|
|
} finally {
|
|
} finally {
|
|
|
searchText.value = ''; // 清空搜索框
|
|
searchText.value = ''; // 清空搜索框
|
|
@@ -1276,49 +1345,324 @@ const normalizeReturnStatus = (status?: { value?: number | string; label?: strin
|
|
|
};
|
|
};
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 处理归还扫码搜索
|
|
|
|
|
|
|
+// 提交归还流程RFID缓冲区内容
|
|
|
|
|
+const submitReturnRFIDBuffer = () => {
|
|
|
|
|
+ if (!readRuleConfig.value.enableRFIDBuffer) {
|
|
|
|
|
+ // 如果未启用RFID缓冲,直接使用 returnSearchText 进行搜索
|
|
|
|
|
+ if (returnSearchText.value.trim()) {
|
|
|
|
|
+ handleReturnScanSearch();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!returnRfidInputBuffer.value) {
|
|
|
|
|
+ // 如果没有缓冲内容,使用 returnSearchText 进行搜索
|
|
|
|
|
+ if (returnSearchText.value.trim()) {
|
|
|
|
|
+ handleReturnScanSearch();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const bufferedValue = returnRfidInputBuffer.value;
|
|
|
|
|
+ returnRfidInputBuffer.value = '';
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ returnConsecutiveInputCount = 0; // 重置计数
|
|
|
|
|
+ returnLastInputTime = 0; // 重置时间
|
|
|
|
|
+ returnSearchText.value = bufferedValue;
|
|
|
|
|
+ handleReturnScanSearch();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 调度归还流程RFID缓冲区提交(延迟提交)
|
|
|
|
|
+const scheduleReturnRFIDBufferSubmit = () => {
|
|
|
|
|
+ if (!readRuleConfig.value.enableRFIDBuffer) return;
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ const delay = Math.max(readRuleConfig.value.rfidInputTimeout || 120, 30);
|
|
|
|
|
+ returnRfidInputTimer = setTimeout(() => {
|
|
|
|
|
+ submitReturnRFIDBuffer();
|
|
|
|
|
+ }, delay);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 处理归还设备输入(用于同步 textarea 内容,仅在缓冲模式下使用)
|
|
|
|
|
+const handleReturnDeviceInput = () => {
|
|
|
|
|
+ // 如果缓冲区有内容,确保 textarea 显示与缓冲区一致
|
|
|
|
|
+ // 这主要用于处理退格键等操作后的同步
|
|
|
|
|
+ if (returnRfidInputBuffer.value && returnSearchText.value !== returnRfidInputBuffer.value) {
|
|
|
|
|
+ returnSearchText.value = returnRfidInputBuffer.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 注意:正常输入时,不干预 textarea 的内容,让默认行为处理
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 检测归还流程是否是高速扫码输入(通过输入速度判断)
|
|
|
|
|
+// 返回值:{ isHighSpeed: boolean, isFirstInput: boolean, timeSinceLastInput: number }
|
|
|
|
|
+const checkReturnInputSpeed = () => {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ const timeSinceLastInput = returnLastInputTime > 0 ? now - returnLastInputTime : 0;
|
|
|
|
|
+ const isFirstInput = returnLastInputTime === 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (!isFirstInput) {
|
|
|
|
|
+ returnLastInputTime = now;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ isFirstInput,
|
|
|
|
|
+ timeSinceLastInput,
|
|
|
|
|
+ isHighSpeed: timeSinceLastInput > 0 && timeSinceLastInput < readRuleConfig.value.minInputSpeed,
|
|
|
|
|
+ isNormalInput: timeSinceLastInput > 200
|
|
|
|
|
+ };
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 处理归还流程RFID键盘输入(高速扫码)
|
|
|
|
|
+const handleReturnRFIDKeydown = (event: KeyboardEvent) => {
|
|
|
|
|
+ // 如果未启用RFID缓冲,直接返回,不拦截
|
|
|
|
|
+ if (!readRuleConfig.value.enableRFIDBuffer) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 不拦截修饰键组合(如 Ctrl+C, Ctrl+V 等)
|
|
|
|
|
+ if (event.ctrlKey || event.metaKey || event.altKey) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理回车键
|
|
|
|
|
+ if (event.key === 'Enter') {
|
|
|
|
|
+ // 回车键由 @keydown.enter.prevent="handleReturnDeviceEnter" 处理
|
|
|
|
|
+ // 这里不处理,让 handleReturnDeviceEnter 统一处理
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理退格键
|
|
|
|
|
+ if (event.key === 'Backspace') {
|
|
|
|
|
+ // 如果有缓冲内容,处理缓冲
|
|
|
|
|
+ if (returnRfidInputBuffer.value) {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ returnRfidInputBuffer.value = returnRfidInputBuffer.value.slice(0, -1);
|
|
|
|
|
+ returnSearchText.value = returnRfidInputBuffer.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 否则让默认行为处理
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理普通字符输入
|
|
|
|
|
+ if (event.key.length === 1) {
|
|
|
|
|
+ const inputInfo = checkReturnInputSpeed();
|
|
|
|
|
+ const { isFirstInput, isHighSpeed, isNormalInput, timeSinceLastInput } = inputInfo;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是第一次输入,不拦截,让默认行为处理,只记录时间戳
|
|
|
|
|
+ if (isFirstInput) {
|
|
|
|
|
+ returnLastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果检测到是正常输入(间隔 > 200ms),处理之前的缓冲内容
|
|
|
|
|
+ if (isNormalInput) {
|
|
|
|
|
+ // 如果之前有缓冲内容,先提交
|
|
|
|
|
+ if (returnRfidInputBuffer.value) {
|
|
|
|
|
+ submitReturnRFIDBuffer();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 重置状态
|
|
|
|
|
+ returnConsecutiveInputCount = 0;
|
|
|
|
|
+ returnLastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果检测到是高速输入(间隔 < 30ms),使用缓冲模式
|
|
|
|
|
+ if (isHighSpeed) {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ event.stopPropagation(); // 阻止事件冒泡,避免影响其他元素
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是第一次检测到高速输入(之前没有缓冲内容),需要将 textarea 中已有的内容也加入缓冲区
|
|
|
|
|
+ if (returnRfidInputBuffer.value === '' && returnSearchText.value) {
|
|
|
|
|
+ // textarea 中已经有内容(第一个字符已经通过默认行为输入了)
|
|
|
|
|
+ // 将整个内容作为缓冲区的初始值,然后追加当前字符
|
|
|
|
|
+ returnRfidInputBuffer.value = returnSearchText.value + event.key;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 否则直接追加当前字符
|
|
|
|
|
+ returnRfidInputBuffer.value += event.key;
|
|
|
|
|
+ }
|
|
|
|
|
+ returnSearchText.value = returnRfidInputBuffer.value;
|
|
|
|
|
+ returnConsecutiveInputCount++;
|
|
|
|
|
+ scheduleReturnRFIDBufferSubmit();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果输入间隔在 30-200ms 之间,可能是正常输入,不拦截
|
|
|
|
|
+ // 但如果之前有缓冲内容,说明高速输入已结束,先提交
|
|
|
|
|
+ if (returnRfidInputBuffer.value) {
|
|
|
|
|
+ submitReturnRFIDBuffer();
|
|
|
|
|
+ returnConsecutiveInputCount = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ returnLastInputTime = Date.now();
|
|
|
|
|
+ // 不拦截,让字符正常输入到 textarea
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 处理归还输入框失焦
|
|
|
|
|
+const handleReturnDeviceBlur = (event: FocusEvent) => {
|
|
|
|
|
+ // 如果焦点转移到其他输入元素(如表单输入框),不自动聚焦
|
|
|
|
|
+ const relatedTarget = event.relatedTarget as HTMLElement;
|
|
|
|
|
+ if (relatedTarget && (
|
|
|
|
|
+ relatedTarget.tagName === 'INPUT' ||
|
|
|
|
|
+ relatedTarget.tagName === 'TEXTAREA' ||
|
|
|
|
|
+ relatedTarget.closest('.el-input') ||
|
|
|
|
|
+ relatedTarget.closest('.el-textarea') ||
|
|
|
|
|
+ relatedTarget.closest('form')
|
|
|
|
|
+ )) {
|
|
|
|
|
+ // 如果有缓冲内容,先提交
|
|
|
|
|
+ if (returnRfidInputBuffer.value) {
|
|
|
|
|
+ submitReturnRFIDBuffer();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 其他情况(如点击按钮等),延迟后自动聚焦
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (returnSearchInputRef.value && !loading.value) {
|
|
|
|
|
+ returnSearchInputRef.value.focus();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 10);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 处理归还回车键
|
|
|
|
|
+const handleReturnDeviceEnter = (event: KeyboardEvent) => {
|
|
|
|
|
+ if (event.shiftKey) return;
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ // 无论是否启用RFID缓冲,都调用 submitReturnRFIDBuffer
|
|
|
|
|
+ // submitReturnRFIDBuffer 内部会判断是否有缓冲内容,如果没有则直接使用 returnSearchText 搜索
|
|
|
|
|
+ submitReturnRFIDBuffer();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 处理归还扫码搜索(支持批量,优化并发请求)
|
|
|
const handleReturnScanSearch = async (event?: Event) => {
|
|
const handleReturnScanSearch = async (event?: Event) => {
|
|
|
// 阻止默认行为,防止表单提交导致页面刷新
|
|
// 阻止默认行为,防止表单提交导致页面刷新
|
|
|
if (event) {
|
|
if (event) {
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
|
event.stopPropagation();
|
|
event.stopPropagation();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 如果启用了RFID缓冲,先提交缓冲区
|
|
|
|
|
+ if (readRuleConfig.value.enableRFIDBuffer && returnRfidInputBuffer.value) {
|
|
|
|
|
+ const bufferedValue = returnRfidInputBuffer.value;
|
|
|
|
|
+ returnRfidInputBuffer.value = '';
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ returnSearchText.value = bufferedValue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (!returnSearchText.value.trim()) return;
|
|
if (!returnSearchText.value.trim()) return;
|
|
|
|
|
|
|
|
|
|
+ // 分割多个设备编号
|
|
|
|
|
+ const codes = splitDeviceCodes(returnSearchText.value);
|
|
|
|
|
+ if (codes.length === 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 去重处理,避免重复请求同一个设备编号
|
|
|
|
|
+ const uniqueCodes = Array.from(new Set(codes.map(code => code.trim()))).filter(Boolean);
|
|
|
|
|
+ if (uniqueCodes.length === 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ let successCount = 0;
|
|
|
|
|
+ let failCount = 0;
|
|
|
|
|
+ const errorMessages: string[] = [];
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- const res = await deviceApi.GetList({
|
|
|
|
|
- page: 1,
|
|
|
|
|
- limit: 1,
|
|
|
|
|
- code: returnSearchText.value.trim(),
|
|
|
|
|
- status: 1
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (res.data && res.data.length > 0) {
|
|
|
|
|
- const device = res.data[0];
|
|
|
|
|
- const normalizedStatus = normalizeReturnStatus(device.return_status);
|
|
|
|
|
|
|
+ // 批量并发处理设备编号(使用 Promise.allSettled 确保所有请求都完成)
|
|
|
|
|
+ // 限制并发数量,避免同时发起过多请求
|
|
|
|
|
+ const batchSize = 10; // 每批处理10个
|
|
|
|
|
+ const batches: string[][] = [];
|
|
|
|
|
+ for (let i = 0; i < uniqueCodes.length; i += batchSize) {
|
|
|
|
|
+ batches.push(uniqueCodes.slice(i, i + batchSize));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 逐批处理,每批内部并发
|
|
|
|
|
+ for (const batch of batches) {
|
|
|
|
|
+ const promises = batch.map(async (code) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await deviceApi.GetList({
|
|
|
|
|
+ page: 1,
|
|
|
|
|
+ limit: 1,
|
|
|
|
|
+ code: code,
|
|
|
|
|
+ status: 1
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- // 检查是否已存在
|
|
|
|
|
- const existingDeviceIndex = returnDeviceList.value.findIndex(d => d.device_no === device.id);
|
|
|
|
|
- if (existingDeviceIndex !== -1) {
|
|
|
|
|
|
|
+ if (res.data && res.data.length > 0) {
|
|
|
|
|
+ const device = res.data[0];
|
|
|
|
|
+ const normalizedStatus = normalizeReturnStatus(device.return_status);
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否已存在
|
|
|
|
|
+ const existingDeviceIndex = returnDeviceList.value.findIndex(d => d.device_no === device.id);
|
|
|
|
|
+ if (existingDeviceIndex !== -1) {
|
|
|
// 如果设备已存在,更新其状态
|
|
// 如果设备已存在,更新其状态
|
|
|
returnDeviceList.value[existingDeviceIndex].return_status = normalizedStatus;
|
|
returnDeviceList.value[existingDeviceIndex].return_status = normalizedStatus;
|
|
|
returnDeviceList.value[existingDeviceIndex].is_return = true;
|
|
returnDeviceList.value[existingDeviceIndex].is_return = true;
|
|
|
returnDeviceList.value[existingDeviceIndex].return_time = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
returnDeviceList.value[existingDeviceIndex].return_time = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
|
- ElMessage.success(`设备 ${device.category_name} 状态已更新为已归还`);
|
|
|
|
|
|
|
+ return { success: true, code };
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 设备不存在于归还列表中
|
|
|
|
|
+ return {
|
|
|
|
|
+ success: false,
|
|
|
|
|
+ code,
|
|
|
|
|
+ message: `设备 ${device.code || device.category_name} 不在当前归还列表中,请先通过手动归还添加该设备`
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return { success: false, code, message: `未找到设备编号: ${code}` };
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(`处理设备编号 ${code} 失败:`, error);
|
|
|
|
|
+ return { success: false, code, message: `处理设备编号 ${code} 失败` };
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 等待当前批次完成
|
|
|
|
|
+ const results = await Promise.allSettled(promises);
|
|
|
|
|
+
|
|
|
|
|
+ // 统计结果
|
|
|
|
|
+ results.forEach((result, index) => {
|
|
|
|
|
+ if (result.status === 'fulfilled') {
|
|
|
|
|
+ const { success, message } = result.value;
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ successCount++;
|
|
|
} else {
|
|
} else {
|
|
|
- // 设备不存在于归还列表中,提示用户
|
|
|
|
|
- ElMessage.warning(`设备 ${device.code || device.category_name} 不在当前归还列表中,请先通过手动归还添加该设备`);
|
|
|
|
|
|
|
+ failCount++;
|
|
|
|
|
+ if (message) {
|
|
|
|
|
+ errorMessages.push(message);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- ElMessage.warning('未找到对应的设备');
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ failCount++;
|
|
|
|
|
+ errorMessages.push(`处理设备编号 ${batch[index]} 时发生错误`);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 显示批量处理结果
|
|
|
|
|
+ if (uniqueCodes.length > 1) {
|
|
|
|
|
+ if (successCount > 0 && failCount === 0) {
|
|
|
|
|
+ ElMessage.success(`成功更新 ${successCount} 个设备状态为已归还`);
|
|
|
|
|
+ } else if (successCount > 0 && failCount > 0) {
|
|
|
|
|
+ ElMessage.warning(`成功更新 ${successCount} 个设备状态,失败 ${failCount} 个`);
|
|
|
|
|
+ // 只显示前3个错误信息,避免消息过多
|
|
|
|
|
+ if (errorMessages.length > 0) {
|
|
|
|
|
+ console.warn('批量处理错误详情:', errorMessages.slice(0, 3));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (failCount > 0) {
|
|
|
|
|
+ ElMessage.error(`更新失败,共 ${failCount} 个设备编号`);
|
|
|
}
|
|
}
|
|
|
|
|
+ } else if (successCount > 0) {
|
|
|
|
|
+ ElMessage.success('设备状态已更新为已归还');
|
|
|
|
|
+ } else if (failCount > 0 && errorMessages.length > 0) {
|
|
|
|
|
+ ElMessage.warning(errorMessages[0]);
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ console.error('批量处理设备失败:', error);
|
|
|
ElMessage.error('搜索设备失败');
|
|
ElMessage.error('搜索设备失败');
|
|
|
} finally {
|
|
} finally {
|
|
|
returnSearchText.value = ''; // 清空搜索框
|
|
returnSearchText.value = ''; // 清空搜索框
|
|
|
|
|
+ loading.value = false;
|
|
|
// 重新聚焦输入框,保持选中状态
|
|
// 重新聚焦输入框,保持选中状态
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
- returnSearchInputRef.value?.focus?.();
|
|
|
|
|
|
|
+ if (returnSearchInputRef.value) {
|
|
|
|
|
+ returnSearchInputRef.value.focus();
|
|
|
|
|
+ returnSearchInputRef.value.select();
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -1648,6 +1992,11 @@ watch(
|
|
|
clearRFIDInputTimer();
|
|
clearRFIDInputTimer();
|
|
|
consecutiveInputCount = 0;
|
|
consecutiveInputCount = 0;
|
|
|
lastInputTime = 0;
|
|
lastInputTime = 0;
|
|
|
|
|
+ // 清理归还流程RFID缓冲状态
|
|
|
|
|
+ returnRfidInputBuffer.value = '';
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ returnConsecutiveInputCount = 0;
|
|
|
|
|
+ returnLastInputTime = 0;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
);
|
|
);
|
|
@@ -1879,6 +2228,11 @@ function onCancel() {
|
|
|
clearRFIDInputTimer();
|
|
clearRFIDInputTimer();
|
|
|
consecutiveInputCount = 0;
|
|
consecutiveInputCount = 0;
|
|
|
lastInputTime = 0;
|
|
lastInputTime = 0;
|
|
|
|
|
+ // 清理归还流程RFID缓冲
|
|
|
|
|
+ returnRfidInputBuffer.value = '';
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ returnConsecutiveInputCount = 0;
|
|
|
|
|
+ returnLastInputTime = 0;
|
|
|
// 关闭所有对话框
|
|
// 关闭所有对话框
|
|
|
showSelectDeviceDialog.value = false;
|
|
showSelectDeviceDialog.value = false;
|
|
|
showSettlementDialog.value = false;
|
|
showSettlementDialog.value = false;
|
|
@@ -2033,6 +2387,11 @@ onBeforeUnmount(() => {
|
|
|
rfidInputBuffer.value = '';
|
|
rfidInputBuffer.value = '';
|
|
|
consecutiveInputCount = 0;
|
|
consecutiveInputCount = 0;
|
|
|
lastInputTime = 0;
|
|
lastInputTime = 0;
|
|
|
|
|
+ // 清理归还流程定时器和状态
|
|
|
|
|
+ clearReturnRFIDInputTimer();
|
|
|
|
|
+ returnRfidInputBuffer.value = '';
|
|
|
|
|
+ returnConsecutiveInputCount = 0;
|
|
|
|
|
+ returnLastInputTime = 0;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
/* formRef.value.validate((valid: boolean) => {
|
|
/* formRef.value.validate((valid: boolean) => {
|