浏览代码

完善借用归还

qaz 1 天之前
父节点
当前提交
1ac32191ab

+ 17 - 0
src/views/system/borrow/api.ts

@@ -82,4 +82,21 @@ export function returnDevice(id: number, remark?: string) {
 		method: 'post',
 		data: { remark }
 	});
+}
+
+export async function CollectEquipment(data: any) {
+	return await request({
+		url: `/api/system/borrow/application/${data.id}/borrow/`,
+		method: 'post',
+		data
+	});
+}
+
+/* 归还 */
+export async function ReturnEquipment(data: any) {
+	return await request({
+		url: `/api/system/borrow/application/${data.id}/return_device/`,
+		method: 'post',
+		data
+	});
 }

+ 103 - 0
src/views/system/borrow/component/CollectEquipment/SelectDeviceDialog/index.vue

@@ -0,0 +1,103 @@
+<template>
+	<el-dialog v-model="visible" title="选择设备" width="800px" :close-on-click-modal="false" @close="onCancel">
+		<div style="margin-bottom: 12px">
+			<el-input ref="searchInput" v-model="search" placeholder="输入关键字搜索" style="width: 240px; margin-right: 8px" @keyup.enter="fetchData(true)" />
+			<el-button type="primary" @click="fetchData(true)">搜索</el-button>
+		</div>
+		<el-table :data="tableData" border style="width: 100%" @selection-change="onSelectionChange" ref="tableRef">
+			<el-table-column type="selection" width="50" />
+			<el-table-column type="index" label="序号" width="60" />
+			<el-table-column prop="name" label="设备名称" />
+			<el-table-column prop="inventory.available_quantity" label="可用数量" />
+		<!-- 	<el-table-column prop="category_name" label="设备分类" /> -->
+			<el-table-column prop="warehouse" label="存放仓库" />
+		</el-table>
+		<el-pagination
+			style="margin-top: 12px; text-align: right"
+			:current-page="page"
+			:page-size="pageSize"
+			:total="total"
+			@current-change="onPageChange"
+		/>
+		<template #footer>
+			<el-button @click="onCancel">取消</el-button>
+			<el-button type="primary" @click="onConfirm">确定</el-button>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, defineProps, defineEmits, nextTick } from 'vue';
+import * as deviceApi from '../../../../device/api';
+import * as storeapi from '../../../../storelist/api';
+import { status } from 'nprogress';
+
+const props = defineProps<{ visible: boolean }>();
+const emit = defineEmits(['update:visible', 'confirm']);
+
+const visible = ref(props.visible);
+watch(
+	() => props.visible,
+	(v) => (visible.value = v)
+);
+
+const search = ref('');
+const page = ref(1);
+const pageSize = ref(10);
+const total = ref(0);
+const tableData = ref([]);
+const selectedRows = ref([]);
+const warehouseMap = ref<Record<number, string>>({});
+const searchInput = ref<HTMLInputElement | null>(null);
+
+
+function fetchWarehouseMap() {
+    storeapi.GetPermission().then((res) => {
+        const map: Record<number, string> = {};
+        res.data.forEach((item) => {
+            map[item.id] = item.name;
+        });
+        warehouseMap.value = map;
+    });
+}
+
+
+async function fetchData(resetPage = false) {
+	if (resetPage) page.value = 1;
+	await fetchWarehouseMap(); 
+	deviceApi.GetList({ page: page.value, limit: pageSize.value, code: search.value,status:1 })
+	.then((res) => {
+		const devices = res.data
+		.filter(device => device.available_quantity > 0)
+		.map((device) => ({
+            ...device,
+            warehouse: `${warehouseMap.value[device.warehouse] || `ID:${device.warehouse}`} (${device.available_quantity})`
+        }));
+		tableData.value = devices;
+		// tableData.value = [...res.data];
+		total.value = res.total;
+	});
+}
+function onSelectionChange(val) {
+	selectedRows.value = val;
+}
+function onPageChange(val) {
+	page.value = val;
+	fetchData();
+}
+function onConfirm() {
+	emit('confirm', selectedRows.value);
+	emit('update:visible', false);
+}
+function onCancel() {
+	emit('update:visible', false);
+}
+watch([visible], async (v) => {
+	if (visible.value) {
+		await fetchData();
+		nextTick(() => {
+			searchInput.value?.focus();
+		});
+	}
+});
+</script>

+ 381 - 0
src/views/system/borrow/component/CollectEquipment/index.vue

@@ -0,0 +1,381 @@
+<template>
+	<el-dialog v-model="visible" :title="isCollect ? '领取' : '归还'" width="75%" :close-on-click-modal="false" @close="onCancel">
+		<el-form :model="form" label-width="110px" label-position="top" :rules="rules" ref="formRef">
+			<el-row :gutter="16">
+				<el-col :span="8">
+					<el-form-item label="借用单编号" prop="application_no">
+						<el-input v-model="form.application_no" placeholder="自动生成" disabled />
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="借用人" prop="emergency_contact">
+						<el-input v-model="form.emergency_contact" placeholder="请输入借用人" :disabled="true" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="学号/工号" prop="app_user_borrower">
+						<el-input v-model="form.app_user_borrower" placeholder="请输入学号/工号"  :disabled="true"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="联系电话" prop="emergency_phone">
+						<el-input v-model="form.emergency_phone" placeholder="请输入联系电话" :disabled="true" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="借用部门" prop="borrower_dept">
+						<el-input v-model="form.borrower_dept" placeholder="请输入借用部门" :disabled="true" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="预计归还时间" prop="expected_end_time">
+						<el-date-picker v-model="form.expected_end_time" type="datetime" placeholder="请选择预计归还时间" style="width: 100%"  :disabled="true"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="4">
+					<el-form-item label="个人/团队" prop="team_type" >
+						<el-select v-model="form.team_type" placeholder="请选择" :disabled="true">
+							<el-option label="个人" :value="0" />
+							<el-option label="团队" :value="1" />
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="4" v-show="form.team_type===1">
+					<el-form-item label="团队人数" prop="team_members_count">
+						<el-input v-model="form.team_members_count" placeholder="请输入"  :disabled="true"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="16" v-show="form.team_type===1">
+					<el-form-item label="团队其他人员名单" prop="team_members">
+						<el-input v-model="form.team_members" placeholder="请输入团队其他人员名单"  :disabled="true"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="24">
+					<el-form-item label="借用原因" prop="purpose">
+						<el-input v-model="form.purpose" type="textarea" placeholder="请输入借用原因" :disabled="true" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="4">
+					<el-form-item label="审批人" prop="applicant_remark_type">
+						<el-select v-model="form.applicant_remark_type" placeholder="请选择" :disabled="true" >
+							<el-option label="管理员" value="1"/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="20">
+					<el-form-item label="备注" prop="applicant_remark">
+						<el-input v-model="form.applicant_remark" placeholder="请输入备注"  :disabled="true"/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<div style="margin: 16px 0 8px 0; font-weight: bold"> {{ isCollect ? '借用明细表' : '归还明细表' }}</div>
+			<el-table :data="form.items" border style="width: 100%; margin-bottom: 12px">
+				<el-table-column type="index" label="序号" width="60" />
+				<el-table-column prop="remark" label="设备分类" />
+				<!-- <el-table-column prop="quantity" label="借用数量" /> -->
+				<el-table-column :label="isCollect ? '借用数量' : '归还数量'">
+				<template #default="{ row }">
+				<el-input v-model="row.quantity" type="number" :min="1" prop="quantity" size="small" :disabled="isView" />
+				</template>
+				</el-table-column>
+                <el-table-column prop="condition" label="归还备注" v-if="isReturn">
+                    <template #default="{ row }">
+                        <el-input v-model="row.condition" placeholder="请输入备注"  :disabled="isView" />
+                    </template>
+                </el-table-column>
+				<el-table-column prop="location" label="存放仓库" />
+				<el-table-column label="操作" width="80">
+					<template #default="{ row, $index }">
+						<el-button type="text" style="color: red" @click="removeItem($index)" :disabled="isView">移除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-button type="primary" @click="addItem" v-if="!isView">添加设备</el-button>
+			<div style="margin: 16px 0 8px 0; font-weight: bold">配件信息</div>
+			<el-input v-model="form.accessory_info" placeholder="请输入配件信息"  :disabled="isView"/>
+
+			<div style="margin: 16px 0 8px 0; font-weight: bold">附件</div>
+			<el-upload
+			action="#"
+			drag
+			list-type="text"
+			accept=".bmp,.gif,.jpeg,.jpg,.png,‌.webp,.svg"
+			multiple
+			:show-file-list="true"
+			:on-success="handleUploadSuccess"
+			:on-error="handleUploadError"
+			>
+			<i class="el-icon-upload"></i>
+			<div class="el-upload__text">将文件拖到此处,或 <em>点击上传</em></div>
+			</el-upload>
+		</el-form>
+		<!-- 审批信息流程展示 -->
+		<div v-if="isView"  style="margin-top: 24px; padding: 0 12px">
+		<div style="font-weight: bold; margin-bottom: 12px">审批流程进度</div>
+		<el-steps :active="activeStep" align-center>
+			<el-step
+			title="创建申请"
+			:description="`创建时间:${ timeline.create }`"
+			icon="CircleCheck"
+			:status="activeStep >= 1 ? 'finish' : 'wait'"
+			/>
+			<el-step
+			title="待审批"
+			description="待超级管理员审批"
+			icon="CircleCheck"
+			:status="activeStep >= 2 ? 'finish' : 'wait'"
+			/>
+			<el-step
+			title="审批通过"
+			:description="`审批人:${timeline.approver},时间:${timeline.approve}`"
+			icon="CircleCheck"
+			:status="activeStep >= 3 ? 'finish' : 'wait'"
+			/>
+			<el-step
+			title="流程完成"
+			description="流程已结束"
+			icon="CircleCheck"
+			:status="activeStep >= 4 ? 'finish' : 'wait'"
+			/>
+		</el-steps>
+		</div>
+
+		<template #footer>
+			<el-button @click="onCancel">关闭</el-button>
+			<el-button type="primary" @click="onSubmit" v-if="!isView">提交</el-button>
+		</template>
+	</el-dialog>
+	<SelectDeviceDialog v-model:visible="showSelectDeviceDialog" @confirm="onDeviceSelected" />
+</template>
+
+<script setup lang="ts">
+import { ref, watch, defineProps, defineEmits, onMounted,computed } from 'vue';
+import SelectDeviceDialog from './SelectDeviceDialog/index.vue';
+import dayjs from 'dayjs';
+import { ElMessage } from 'element-plus';
+import * as api from '../../api';
+import { CollectEquipment,ReturnEquipment } from '../../api';
+
+const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
+
+
+
+
+const props = defineProps<{ visible: boolean; modelValue?: any }>();
+
+
+const isEdit = computed(() => props.modelValue?.mode === 'edit');
+const isView = computed(() => props.modelValue?.mode === 'view');
+const isAdd = computed(() => props.modelValue?.mode === 'add');
+const isCollect = computed(() => props.modelValue?.mode === 'collect');
+const isReturn = computed(() => props.modelValue?.mode === 'return');
+
+const emit = defineEmits(['update:visible', 'update:modelValue', 'submit']);
+const visible = ref(props.visible);
+const formRef = ref();
+
+// import { ElSteps } from 'element-plus';
+
+const timeline = ref({
+  create: '',
+  approve: '',
+  approver: ''
+});
+const activeStep = ref(1);
+
+function handleUploadSuccess(response, file, fileList) {
+	// eslint-disable-next-line no-console
+  console.log('上传成功:', file.name);
+}
+
+function handleUploadError(err, file, fileList) {
+	// eslint-disable-next-line no-console
+  console.error('上传失败:', file.name);
+}
+
+
+const form = ref({
+	application_no: '',
+	emergency_contact: '',
+	emergency_phone: '',
+	app_user_borrower: 1,
+	team_type: 0,
+	team_members_count: 1,
+	team_members: '',
+	expected_start_time:'',
+	expected_end_time: '',
+	borrower_dept: '',
+	purpose: '',
+	applicant_remark_type: "1",
+	applicant_remark: '',
+	items: [],
+	accessory_info: '',
+	borrow_type: 2,
+	return_location:''
+});
+const showSelectDeviceDialog = ref(false);
+
+
+const rules = {
+  emergency_contact: [
+    { required: true, message: '请输入借用人', trigger: 'change' },
+  ],
+  app_user_borrower: [
+    { required: true, message: '请输入学号或工号', trigger: 'change' },
+  ],
+  emergency_phone: [
+    { required: true, message: '请输入联系电话', trigger: 'change' },
+  ],
+  borrower_dept: [
+    { required: true, message: '请输入借用部门', trigger: 'change' },
+  ],
+  expected_end_time: [
+    { required: true, message: '请输选择归还时间', trigger: 'change' },
+  ],
+  team_type: [
+    { required: true, message: '请选择个人或者团队', trigger: 'change' },
+  ],
+  purpose: [
+    { required: true, message: '请输入借用原因', trigger: 'change' },
+  ],
+  applicant_remark_type: [
+    { required: true, message: '请选择审批人', trigger: 'change' },
+  ],
+  applicant_remark: [
+    { required: true, message: '请输入备注', trigger: 'change' },
+  ],
+};
+
+
+// 自动填充用户信息
+onMounted(() => {
+	try {
+		const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
+		if (userInfo) {
+			form.value.emergency_contact = userInfo.username || '';
+			form.value.app_user_borrower = Number(userInfo.dept_info?.dept_id )|| 1;
+			form.value.emergency_phone = userInfo.mobile || '';
+			form.value.borrower_dept = userInfo.dept_info?.dept_name || '';
+		}
+	} catch (e) {
+		/* empty */
+	}
+});
+
+watch(
+	() => props.visible,
+	(v) => (visible.value = v)
+);
+
+watch(() => props.modelValue, async (val) => {
+    console.log("props:::",props.modelValue);
+    console.log("val:::",val && val.id && isView.value);
+  if (val && val.id) {
+			try {
+			const res = await api.GetApplicationDetail(val.id);
+			if (res.code === 2000 && res.data) {
+				const data = res.data;
+				Object.assign(form.value, data);
+				timeline.value.create = data.create_datetime || '';
+				timeline.value.approve = data.approve_time || '';
+				timeline.value.approver = data.approver_info?.name || '审批人';
+                console.log("data:::",form.value);
+				activeStep.value = 1; // 创建
+				if (data.status >= 1) activeStep.value = 2; // 待审批
+				if (data.status >= 2) activeStep.value = 3; // 审批通过
+				if (data.status >= 3) activeStep.value = 4; // 完成
+			}
+			} catch (e) {
+			ElMessage.error('获取审批信息失败');
+			}
+	}else if(val&& isEdit.value){
+		try {
+			const res = await api.GetApplicationDetail(val.id);
+			if (res.code === 2000 && res.data) {
+				const data = res.data;
+				Object.assign(form.value, data);
+			}
+			} catch (e) {
+			ElMessage.error('获取审批信息失败');
+			}
+	}
+}, { immediate: true });
+
+
+// watch(
+// 	() => props.modelValue,
+// 	(v) => {
+// 		if (v) {
+// 			// eslint-disable-next-line no-console
+// 			console.log("val:::::",v);
+// 			Object.assign(form.value, v);
+// 		}
+// 	}, { immediate: true }
+// );
+
+function addItem() {
+	showSelectDeviceDialog.value = true;
+}
+function onDeviceSelected(devices) {
+	form.value.items.push(
+		...devices.map((d) => ({
+			device: d.id,
+			remark: d.category_name,
+			quantity: 1,
+			location: d.warehouse,
+		}))
+	);
+}
+function removeItem(index: number) {
+	form.value.items.splice(index, 1);
+}
+function onCancel() {
+	emit('update:visible', false);
+}
+function onSubmit() {
+    if(isCollect.value){
+        form.value.items = form.value.items.map(item => ({
+            ...item,
+            device_id: Number(item.device) || 0  
+        }));
+    CollectEquipment(form.value).then(res=>{
+        if(res.code===2000){
+            ElMessage.success("领取成功");
+            emit('update:visible', false);
+            emit('submit'); // 触发submit事件通知父组件刷新列表
+        }
+    });
+}else{
+    form.value.items = form.value.items.map(item => ({
+            ...item,
+            device_id: Number(item.device) || 0  
+        }));
+    ReturnEquipment(form.value).then(res=>{
+        if(res.code===2000){
+            ElMessage.success("归还成功");
+            emit('update:visible', false);
+            emit('submit'); // 触发submit事件通知父组件刷新列表
+        }
+    });
+}
+
+	/* formRef.value.validate((valid: boolean) => {
+		if (valid) {
+			form.value.items = form.value.items.map(item => ({
+                ...item,
+                quantity: Number(item.quantity) || 0  
+            }));
+			const firstLocation = form.value.items[0]?.location || '';
+            form.value.return_location = firstLocation;
+			form.value.expected_start_time = now;
+			form.value.expected_end_time = dayjs(form.value.expected_end_time).format('YYYY-MM-DD HH:mm:ss')|| now;
+			form.team_members_count=Number(form.team_members_count)||1;
+			emit('submit', { ...form.value });
+			emit('update:visible', false);
+			ElMessage.success("添加成功!");
+		}
+	}); */
+	
+}
+
+</script>

+ 5 - 2
src/views/system/borrow/component/SpecialBorrow/SelectDeviceDialog/index.vue

@@ -7,7 +7,9 @@
 		<el-table :data="tableData" border style="width: 100%" @selection-change="onSelectionChange" ref="tableRef">
 			<el-table-column type="selection" width="50" />
 			<el-table-column type="index" label="序号" width="60" />
-			<el-table-column prop="category_name" label="设备分类" />
+			<el-table-column prop="name" label="设备名称" />
+			<el-table-column prop="inventory.available_quantity" label="可用数量" />
+		<!-- 	<el-table-column prop="category_name" label="设备分类" /> -->
 			<el-table-column prop="warehouse" label="存放仓库" />
 		</el-table>
 		<el-pagination
@@ -28,6 +30,7 @@
 import { ref, watch, defineProps, defineEmits } from 'vue';
 import * as deviceApi from '../../../../device/api';
 import * as storeapi from '../../../../storelist/api';
+import { status } from 'nprogress';
 
 const props = defineProps<{ visible: boolean }>();
 const emit = defineEmits(['update:visible', 'confirm']);
@@ -61,7 +64,7 @@ function fetchWarehouseMap() {
 async function fetchData(resetPage = false) {
 	if (resetPage) page.value = 1;
 	await fetchWarehouseMap(); 
-	deviceApi.GetList({ page: page.value, limit: pageSize.value, category_name: search.value })
+	deviceApi.GetList({ page: page.value, limit: pageSize.value, code: search.value,status:1 })
 	.then((res) => {
 		const devices = res.data
 		.filter(device => device.available_quantity > 0)

+ 24 - 1
src/views/system/borrow/crud.tsx

@@ -51,10 +51,11 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 			},
 			rowHandle: {
 				fixed: 'right',
-				width: 220,
+				width: 300,
 				buttons: {
 					view: {
 						show: true,
+						order:1,
 						click: ({ row }) => {
 							// eslint-disable-next-line no-console
 							// console.log("row:::",row);
@@ -63,6 +64,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 					},
 					edit: {
 						show:true,	
+						order:4,
 						disabled:compute( ({row})=>{
 							return row.borrow_type === 0;
 						}), 
@@ -71,8 +73,29 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
 						}
 					},
 					remove: {
+						order:5,
 						show: true,
 					},
+					collectEquipment:{
+						order:2,
+						show:compute( ({row})=>{
+							return row.status === 2;
+						}), 
+						text: '领取',
+						click: ({ row }) => {
+							window.dispatchEvent(new CustomEvent('borrow-collect', { detail: row }));
+						}
+					},
+					returnEquipment:{
+						order:3,
+						show:compute( ({row})=>{
+							return row.status === 5;
+						}), 
+						text: '归还',
+						click: ({ row }) => {
+							window.dispatchEvent(new CustomEvent('borrow-return', { detail: row }));
+						}
+					}
 				},
 			},
 			search:{

+ 39 - 1
src/views/system/borrow/index.vue

@@ -10,6 +10,7 @@
 			@submit="handleBorrowSubmit"
 		/>
 		<SpecialBorrow v-if="showSpecialBorrowDialog" v-model:visible="showSpecialBorrowDialog" :modelValue="borrowForm" @submit="handleBorrowSubmit" />
+		<CollectEquipmentDialog  v-model:visible="showCollectDialog" :modelValue="borrowForm" @submit="handleCollectSubmit" />
 	</fs-page>
 </template>
 <script lang="ts" setup name="borrow">
@@ -22,6 +23,7 @@ import BorrowTypeSelect from './component/BorrowTypeSelect/index.vue';
 // import CommonBorrow from './component/CommonBorrow/index.vue';
 import ClassroomBorrow from './component/ClassroomBorrow/index.vue';
 import SpecialBorrow from './component/SpecialBorrow/index.vue';
+import CollectEquipmentDialog from './component/CollectEquipment/index.vue';
 import * as api from './api';
 import { ElMessage } from 'element-plus';
 
@@ -30,9 +32,10 @@ const showBorrowTypeDialog = ref(false);
 // const showCommonBorrowDialog = ref(false);
 const showClassroomBorrowDialog = ref(false);
 const showSpecialBorrowDialog = ref(false);
+const showCollectDialog = ref(false);
 const borrowForm = ref({});
 
-function onBorrowTypeSelected(type: number, mode: 'view' | 'edit' | 'add',record?: any) {
+function onBorrowTypeSelected(type: number, mode: 'view' | 'edit' | 'add' |'collect',record?: any) {
 	showBorrowTypeDialog.value = false;
 	// 初始化表单数据,带上所选借用性质
 	// borrowForm.value = { borrow_type: type, mode: mode };
@@ -44,6 +47,8 @@ function onBorrowTypeSelected(type: number, mode: 'view' | 'edit' | 'add',record
 		
 	} else if (type == 1) {
 		showClassroomBorrowDialog.value = true;
+	} else if (type == 2) {
+			showCollectDialog.value = true;
 	} else {
 		showSpecialBorrowDialog.value = true;
 	}
@@ -64,6 +69,35 @@ async function handleBorrowSubmit(formData: any) {
 	}
 }
 
+async function handleCollectSubmit(formData: any) {
+	 try {
+		crudExpose.doRefresh();
+	 } catch (e) {
+		console.error('提交失败', e);
+	}
+}
+
+async function handleCollect(e: CustomEvent) {
+	const row = e.detail;
+	console.log("row:::",row);
+	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);
+	showCollectDialog.value = true;
+	nextTick(() => {
+		borrowForm.value = { ...row, mode: 'return' };
+	});
+	console.log("showReturnDialog:::",showCollectDialog.value);
+}
+
 async function handleView(e: CustomEvent) {
 	const row = e.detail;
 	onBorrowTypeSelected(row.borrow_type, 'view',row);
@@ -89,6 +123,8 @@ onMounted(() => {
 	window.addEventListener('borrow-view', handleView);
 	window.addEventListener('borrow-edit', handleEdit);
 	window.addEventListener('borrow-remove', handleRemove);
+	window.addEventListener('borrow-collect', handleCollect);
+	window.addEventListener('borrow-return', handleReturn);
 	// 创建借用单按钮
 	function insertButton() {
 		const fsActionbar = document.querySelector('.borrow-page .fs-actionbar');
@@ -118,6 +154,8 @@ onBeforeUnmount(() => {
 		observer = null;
 	}
 	window.removeEventListener('borrow-view', handleView);
+	window.removeEventListener('borrow-collect', handleCollect);
+	window.removeEventListener('borrow-return', handleReturn);
 });
 </script>
 <style lang="scss" scoped></style>