|
|
@@ -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);
|
|
|
}
|
|
|
|