qaz 3 дней назад
Родитель
Сommit
e91e090952

+ 2 - 2
.env.development

@@ -5,8 +5,8 @@ ENV = 'development'
 #线下:http://192.168.100.187:8083
 # 本地环境接口地址 121.36.251.245
 
-VITE_API_URL = 'http://192.168.100.179:8086'
-#VITE_API_URL = 'http://1.94.168.85:8086'
+#VITE_API_URL = 'http://192.168.100.179:8086'
+VITE_API_URL = 'http://1.94.168.85:8086'
 # VITE_API_URL = 'https://backend.qicai321.com'
 VITE_API_WX_URL='https://api.weixin.qq.com/'
 VITE_API_URL_MINLONG='http://117.185.80.170:7861'

+ 25 - 10
src/views/system/borrow/component/CollectEquipment/index.vue

@@ -3266,23 +3266,34 @@ onMounted(() => {
 	}
 });
 
+/** 领取/归还不含查看时,默认先展示设备清单,便于扫码;查看仍默认借用单信息 */
+function applyDefaultTabForInteractionMode() {
+	if (isView.value) return;
+	if (shouldHideDeviceList.value) return;
+	if (isCollect.value) {
+		activeName.value = 'second';
+	} else if (isReturn.value) {
+		activeName.value = 'third';
+	}
+}
+
 watch(
 	() => props.visible,
 	(v) => {
 		visible.value = v;
-		// 当对话框打开且当前标签页是设备清单时,自动聚焦输入框
-		if (v && activeName.value === 'second' && isCollect.value) {
+		if (v) {
+			// 同步切页,避免首帧仍渲染「借用单信息」;聚焦放到 nextTick 等 Tab 内容挂载
+			applyDefaultTabForInteractionMode();
 			nextTick(() => {
-				if (searchInputRef.value) {
-					searchInputRef.value.focus();
-					searchInputRef.value.select();
+				if (activeName.value === 'second' && isCollect.value) {
+					if (searchInputRef.value) {
+						searchInputRef.value.focus();
+						searchInputRef.value.select();
+					}
+				} else if (activeName.value === 'third' && !isView.value) {
+					returnSearchInputRef.value?.focus?.();
 				}
 			});
-		} else if (v && activeName.value === 'third' && !isView.value) {
-			// 当对话框打开且当前标签页是归还设备清单时,自动聚焦输入框
-			nextTick(() => {
-				returnSearchInputRef.value?.focus?.();
-			});
 		}
 		// 清理RFID缓冲状态
 		if (v) {
@@ -3396,6 +3407,10 @@ watch(() => props.modelValue, async (val) => {
                         }).filter(Boolean);
                     }
                 }
+                // 领取/归还:默认进设备清单;父级 collectInitialTab 与之一致,无需单独判断
+                nextTick(() => {
+                  applyDefaultTabForInteractionMode();
+                });
 			}
 			} catch (e) {
         console.log("审判失败:::",e)

+ 199 - 7
src/views/system/borrow/index.vue

@@ -54,6 +54,181 @@ const showSettlementDialog = ref(false);
 const lastFormSnapshot = ref({});
 // provide('lastFormSnapshot', lastFormSnapshot);
 
+/** 在响应中递归查找「疑似借用单」主键;expectedBorrowType 为 1 课堂 / 2 特殊 等,与列表性质一致,避免误取 user.id */
+function findBorrowApplicationIdInResponseTree(
+	res: any,
+	expectedBorrowType: number
+): string | number | null {
+	const s = new WeakSet<object>();
+	const want = Number(expectedBorrowType);
+	function walk(n: any, depth: number): string | number | null {
+		if (n == null || depth > 8) return null;
+		if (typeof n !== 'object') return null;
+		if (s.has(n as object) && !Array.isArray(n)) return null;
+		s.add(n as object);
+		if (Array.isArray(n)) {
+			for (const item of n) {
+				const f = walk(item, depth + 1);
+				if (f != null) return f;
+			}
+			return null;
+		}
+		const o = n as any;
+		if (o.id != null) {
+			const hasNo = o.application_no != null && String(o.application_no).trim() !== '';
+			const hasBt = o.borrow_type != null;
+			const hasPurpose = o.purpose != null && String(o.purpose).trim() !== '';
+			const nbt = Number(o.borrow_type);
+			if (
+				hasNo ||
+				(hasBt &&
+					hasPurpose &&
+					!Number.isNaN(nbt) &&
+					nbt === want)
+			) {
+				return o.id;
+			}
+		}
+		for (const v of Object.values(n)) {
+			if (v && typeof v === 'object') {
+				const f = walk(v, depth + 1);
+				if (f != null) return f;
+			}
+		}
+		return null;
+	}
+	return walk(res, 0);
+}
+
+/** 从「新建借用单」POST 标准返回中尽量解析出新建单 id(兼容 data 为对象/数字/数组及常见字段名) */
+function parseNewBorrowApplicationId(res: any): string | number | null {
+	if (!res) return null;
+	if (res.id != null) return res.id;
+	const d = res.data;
+	if (d == null) return null;
+	if (typeof d === 'number') return d;
+	if (typeof d === 'string' && d.trim() !== '' && !Number.isNaN(Number(d))) {
+		return Number(d);
+	}
+	if (Array.isArray(d) && d.length > 0) {
+		const r0 = d[0] as any;
+		if (r0?.id != null) return r0.id;
+	}
+	if (typeof d === 'object') {
+		const id =
+			d.id ?? d.pk ?? d.application_id ?? d.applicationId ?? d.bid;
+		if (id != null) return id;
+		if (Array.isArray((d as any).results) && (d as any).results[0]?.id != null) {
+			return (d as any).results[0].id;
+		}
+		const inner = d.data;
+		if (inner && typeof inner === 'object' && (inner as any).id != null) return (inner as any).id;
+	}
+	return null;
+}
+
+function listResponseRecords(lr: any): any[] {
+	const d = lr?.data;
+	if (!d) return [];
+	if (Array.isArray(d.items)) return d.items;
+	if (Array.isArray(d.results)) return d.results;
+	if (Array.isArray(d.records)) return d.records;
+	if (Array.isArray(d)) return d;
+	return [];
+}
+
+/** 用关键字搜索 + 单号 / 用途 等在列表中匹配新建单;borrowType 与借用性质(1 课堂 2 特殊…)一致 */
+async function searchBorrowIdByList(params: {
+	search?: string;
+	formPurpose?: string;
+	formExpectedStart?: string;
+	borrowType: number;
+}): Promise<string | number | null> {
+	const { search, formPurpose, formExpectedStart, borrowType } = params;
+	const base = {
+		page: 1,
+		limit: 30,
+		borrow_type: Number(borrowType),
+		ordering: '-create_datetime',
+	} as Record<string, any>;
+	try {
+		if (search && String(search).trim() !== '') {
+			const lr: any = await api.GetList({
+				...base,
+				search: String(search).trim(),
+			} as any);
+			const rows = listResponseRecords(lr);
+			if (search) {
+				const t = String(search).trim();
+				const byNo = rows.find(
+					(r: any) => r && String(r.application_no) === t
+				);
+				if (byNo?.id != null) return byNo.id;
+			}
+		}
+		// 无单号时:取同性质借用最近一页,用「用途」+ 预计开始日(若有)弱匹配
+		const lr2: any = await api.GetList(base as any);
+		const rows = listResponseRecords(lr2);
+		if (!rows.length) return null;
+		if (formPurpose) {
+			const p = String(formPurpose).trim();
+			const m = rows.find((r: any) => r && String(r.purpose) === p);
+			if (m?.id != null) return m.id;
+		}
+		if (formExpectedStart) {
+			const day = String(formExpectedStart).slice(0, 10);
+			const m = rows.find(
+				(r: any) =>
+					r &&
+					r.expected_start_time &&
+					String(r.expected_start_time).slice(0, 10) === day
+			);
+			if (m?.id != null) return m.id;
+		}
+		// 新建立即查询时,多并发下非 100% 可靠;与后端约定在 POST 中回 id 可彻底避免
+		return rows[0]?.id ?? null;
+	} catch {
+		return null;
+	}
+}
+
+/**
+ * 从 POST 返回 + 表单快照解析新建单主键,供直接打开「领取」(课堂/特殊等,由 borrow_type 区分)
+ */
+async function resolveNewBorrowApplicationIdAfterAdd(
+	res: any,
+	submitted: Record<string, any>
+): Promise<string | number | null> {
+	const borrowType = Number(submitted?.borrow_type);
+	let id = parseNewBorrowApplicationId(res);
+	if (id != null) return id;
+	if (!Number.isNaN(borrowType)) {
+		id = findBorrowApplicationIdInResponseTree(res, borrowType);
+		if (id != null) return id;
+	}
+	const d = res?.data;
+	const appNoFromRes =
+		d && typeof d === 'object' ? d.application_no ?? d.applicationNo : null;
+	if (appNoFromRes && String(appNoFromRes).trim() !== '' && !Number.isNaN(borrowType)) {
+		id = await searchBorrowIdByList({
+			search: String(appNoFromRes).trim(),
+			borrowType,
+		});
+		if (id != null) return id;
+	}
+	if (submitted?.application_no && !Number.isNaN(borrowType)) {
+		id = await searchBorrowIdByList({
+			search: String(submitted.application_no),
+			borrowType,
+		});
+		if (id != null) return id;
+	}
+	return await searchBorrowIdByList({
+		formPurpose: submitted?.purpose,
+		formExpectedStart: submitted?.expected_start_time,
+		borrowType: Number.isNaN(borrowType) ? 1 : borrowType,
+	});
+}
 
 function onBorrowTypeSelected(type: number, mode: 'view' | 'edit' | 'add' |'collect',record?: any) {
 	showBorrowTypeDialog.value = false;
@@ -86,8 +261,29 @@ async function handleBorrowSubmit(formData: any) {
 			await api.UpdateObj(formData);
 			ElMessage.success('编辑成功');
 		} else {
-			await api.AddObj(formData);
+			const res: any = await api.AddObj(formData);
 			ElMessage.success('添加成功');
+			// 课堂(1) / 特殊(2) 借用:新增成功后直接打开「领取」弹窗,并进入设备清单
+			const addBorrowType = Number(formData.borrow_type);
+			if (addBorrowType === 1 || addBorrowType === 2) {
+				const newId = await resolveNewBorrowApplicationIdAfterAdd(res, formData);
+				if (newId != null) {
+					// 先写入 modelValue 再打开弹窗,领取页首帧即 mode=collect,避免先显示「借用单信息」再切页
+					borrowForm.value = {
+						id: newId,
+						borrow_type: addBorrowType,
+						mode: 'collect',
+						collectInitialTab: 'second',
+					};
+					showCollectDialog.value = true;
+				} else {
+					console.warn(
+						'[borrow] 新建借用后仍无法确定单主键,请让 POST /application/ 在 data 中返回 id;可暂从列表点击「领取」',
+						res
+					);
+					ElMessage.info('已新建成功,请从下方列表中点击该单的「领取」');
+				}
+			}
 		}
 		
 		// 刷新列表
@@ -112,21 +308,17 @@ async function handleCollectSubmit(formData: any) {
 async function handleCollect(e: CustomEvent) {
 	const row = e.detail;
 	console.log("row:::",row);
+	borrowForm.value = { ...row, mode: 'collect' };
 	showCollectDialog.value = true;
 	console.log("showCollectDialog:::",showCollectDialog.value);
-	nextTick(() => {
-		borrowForm.value = { ...row, mode: 'collect' };
-	});
 }
 
 
 async function handleReturn(e: CustomEvent) {
 	const row = e.detail;
 	console.log("row:::",row);
+	borrowForm.value = { ...row, mode: 'return' };
 	showCollectDialog.value = true;
-	nextTick(() => {
-		borrowForm.value = { ...row, mode: 'return' };
-	});
 	console.log("showReturnDialog:::",showCollectDialog.value);
 }