Browse Source

no message

kllay 6 days ago
parent
commit
79ff4aa062

+ 27 - 0
src/views/system/borrow/approval/ItemsList/index.vue

@@ -0,0 +1,27 @@
+<template>
+    <div id="testxxxxx2">
+      <div v-for="item in itemss" :key="item.id">
+        {{ item.device_category_name }} × {{ item.quantity }}
+      </div>
+    </div>
+  </template>
+  
+  <script lang="ts" setup name="ItemsList">
+   import { onMounted } from 'vue';
+  const { itemss } = defineProps<{ itemss: any[] }>();
+   console.log('items:', itemss);
+
+  console.log('ItemsList组件加载成功');
+
+  watch(() => itemss, (val) => {
+  console.log('📦 itemss changed:', val);
+});
+
+
+onMounted(() => {
+  console.log('✅ ItemsList 已挂载');
+  console.log('📦 itemss:', itemss);
+});
+
+  </script>
+  

+ 2 - 0
src/views/system/borrow/approval/RefuseNotification/index.vue

@@ -27,6 +27,7 @@
   const rejectReason = inject('rejectReason') as Ref<string>;
   const currentFormId = inject('currentFormId') as Ref<number | null>;
   const crudExpose = inject('crudExpose') as any;
+  const closeDialog = inject('closeDialog') as () => void;
   const handleReject = async () => {
     if (!rejectReason.value.trim()) {
       ElMessage.warning("请填写拒绝原因");
@@ -35,6 +36,7 @@
     await api.BorrowingReview(currentFormId.value, "reject", rejectReason.value);
     ElMessage.success("已拒绝申请");
     showRejectDialog.value = false;
+    closeDialog();
     crudExpose?.doRefresh();
   };
   </script>

+ 97 - 0
src/views/system/borrow/approval/SpecialBorrowApp/SelectDeviceDialogApp/index.vue

@@ -0,0 +1,97 @@
+<template>
+	<el-dialog v-model="visible" title="选择设备" width="800px" :close-on-click-modal="false" @close="onCancel">
+		<div style="margin-bottom: 12px">
+			<el-input 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="category_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 } 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>>({});
+
+
+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, page], (v) => {
+	if (visible.value) fetchData();
+});
+</script>

+ 538 - 0
src/views/system/borrow/approval/SpecialBorrowApp/index.vue

@@ -0,0 +1,538 @@
+<template>
+	<el-dialog v-model="visible" title="借用申请单" 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="borrower_type">
+					<el-select v-model="form.borrower_type" placeholder="请选择借用人角色"  :disabled="isView" >
+						<el-option label="管理员用户" :value=0 />
+						<el-option label="应用用户" :value=1 />
+						<el-option label="外部用户" :value=2 />
+					</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8" >
+					<el-form-item label="借用人" prop="admin_borrower" v-if="form.borrower_type == 0">
+					<el-select v-model="form.admin_borrower" placeholder="请选择借用人"  :disabled="isView" >
+						<el-option  :label="adminList.name" :value="adminList.id" />
+					</el-select>
+					</el-form-item>
+					<el-form-item label="借用人" prop="app_user_borrower" v-if="form.borrower_type == 1">
+					<el-select v-model="form.app_user_borrower" placeholder="请选择借用人"  :disabled="isView" >
+						<el-option v-for="item in allUserList"  :key="item.id" :label="item.name" :value="item.id" />
+					</el-select>
+					</el-form-item>
+					<el-form-item label="借用人" prop="external_borrower_name" v-if="form.borrower_type == 2">
+					<el-input v-model="form.external_borrower_name" placeholder="请输入借用人"  :disabled="isView"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8" v-if="form.borrower_type !== 2">
+					<el-form-item label="学号/工号" prop="user_code" >
+						<el-input v-model="form.user_code" placeholder="请输入学号/工号"  :disabled="isView"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="8">
+					<el-form-item label="联系电话" prop="mobile" v-if="form.borrower_type !== 2">
+						<el-input v-model="form.mobile" placeholder="请输入联系电话" :disabled="isView" />
+					</el-form-item>
+					<el-form-item label="联系电话" prop="external_borrower_phone" v-if="form.borrower_type == 2">
+						<el-input v-model="form.external_borrower_phone" placeholder="请输入联系电话"  :disabled="isView"/>
+					</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="isView" />
+					</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="isView"/>
+					</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="isView">
+							<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="isView"/>
+					</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="isView"/>
+					</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="isView" />
+					</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="isView" >
+							<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="isView"/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<div style="margin: 16px 0 8px 0; font-weight: bold">借用列表</div>
+			<!-- {{ isEdit }} -->
+			<el-table :data="!isEdit ? form.items : form.items.filter(item => item.device === null)" border style="width: 100%; margin-bottom: 12px">
+				<el-table-column type="index" label="序号" width="60" />
+				<el-table-column prop="device_category_name" label="设备分类" />
+				<!-- <el-table-column prop="quantity" label="借用数量" /> -->
+				<el-table-column label="借用数量">
+				<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="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.accessories" placeholder="请输入配件信息"  :disabled="isView"/>
+
+			<div style="margin: 16px 0 8px 0; font-weight: bold">附件</div>
+			<div>
+			<el-upload
+			list-type="picture-card"
+			:show-file-list="true"
+			:http-request="handleCustomUpload"
+			:file-list="fileList"
+			style="margin-top: 10px;"
+			>
+			<el-icon><Plus /></el-icon>
+			</el-upload>
+		    </div>
+
+		</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="steps.length" align-center>
+			<el-step
+				v-for="(step, index) in steps"
+				:key="index"
+				:title="step.title"
+				:description="step.description"
+				icon="CircleCheck"
+				:status="step.status"
+			/>
+			</el-steps>
+
+		</div>
+
+		<template #footer>
+			<el-button @click="onSucess">通过</el-button>
+			<el-button @click="onDanger(form.id)">拒绝</el-button>
+			<el-button @click="onCancel">关闭</el-button>
+			<el-button type="primary" @click="onSubmit" v-if="!isView">提交</el-button>
+		</template>
+	</el-dialog>
+	<SelectDeviceDialogApp v-model:visible="showSelectDeviceDialog" @confirm="onDeviceSelected" />
+	<!-- <SelectCatgory v-model:visible="showSelectDeviceDialog" @confirm="onDeviceSelected" /> -->
+	<RefuseNotification/>
+</template>
+
+<script setup lang="ts">
+import { ref, inject,provide,watch, defineProps, defineEmits, onMounted,computed } from 'vue';
+import SelectDeviceDialogApp from './SelectDeviceDialogApp/index.vue';
+import RefuseNotification from '../RefuseNotification/index.vue';
+import dayjs from 'dayjs';
+import { ElMessage } from 'element-plus';
+import * as api from '../api';
+import * as apis from '../../api';
+import Cookies from 'js-cookie';
+import { request } from '/@/utils/service';
+import { Plus } from '@element-plus/icons-vue';
+// import SelectDeviceDialog from './SelectDeviceDialogApp/index.vue';
+import { getUserInfo,getAdminUserInfo } from '../../../login/api';
+
+const showRejectDialog = ref(false);
+const rejectReason = ref('');
+const currentFormId = ref<number | null>(null);
+
+provide('showRejectDialog', showRejectDialog);
+provide('rejectReason', rejectReason);
+provide('currentFormId', currentFormId);
+provide('closeDialog', onCancel);
+
+const crudExpose = inject('crudExpose') as any;
+provide('crudExpose', crudExpose); 
+
+
+/* 获取管理员列表 */
+const adminList = ref([]);
+
+async function getAdminList() {
+	const res = await getUserInfo();
+	adminList.value = res.data;
+	console.log("adminList.value:::",adminList.value)	
+}
+
+/* 获取所有用户 */
+const allUserList = ref([]);
+async function getAllUserList() {
+	const res = await apis.GetAllUser();
+	allUserList.value = res.data||[];
+	const adminRes = await getAdminUserInfo();
+	const adminUser = adminRes.data;
+	// // 设置默认选择第一个用户
+	// if (allUserList.value.length > 0) {
+	// 	form.value.app_user_borrower = allUserList.value[0].id;
+	// }
+
+    // 检查是否已存在该用户,避免重复添加
+    const exists = allUserList.value.some(user => user.id === adminUser.id);
+    if (!exists) {
+      allUserList.value.push(adminUser);
+    }
+	console.log("allUserList.value:::",allUserList.value)	
+}
+
+
+
+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 emit = defineEmits(['update:visible', 'update:modelValue', 'submit']);
+const visible = ref(props.visible);
+const formRef = ref();
+
+
+const fileList = ref([]);
+
+// const fileList = ref([]);
+const handleCustomUpload = async ({ file, onProgress, onSuccess, onError }: any) => {
+  const token = Cookies.get('token'); 
+  const formData = new FormData();
+  formData.append('file', file);
+//   console.log("token::::",token);
+
+  try {
+    const res = await request({
+		url: `/api/system/upload/`,
+		method: 'post',
+		headers: {
+			'Content-Type': 'multipart/form-data',
+			Authorization: 'JWT '+ token,
+		},
+		data: formData,
+		timeout: 60000,
+		onUploadProgress: (p: any) => {
+                            onProgress({percent: Math.round((p.loaded / p.total) * 100)});
+		}
+    });
+
+	// 构造 Element Plus 需要的 file 对象
+	const fileObj = {
+      name: res.data.name,
+      url: res.data.url,
+      status: 'success',
+      response: res.data
+    };
+
+    // 添加到附件列表
+	form.value.attachments = fileObj.url; 
+
+    ElMessage.success('上传成功');
+    onSuccess(fileObj); 
+  } catch (error) {
+    ElMessage.error('上传失败');
+    onError(error);
+  }
+};
+
+// import { ElSteps } from 'element-plus';
+const steps = ref<any[]>([]);
+const timeline = ref({
+	setpstatus:0,
+	create: '',
+	approve: '',
+	approver: '',
+	approvemsg:''
+});
+
+
+function buildSteps(data: any) {
+  steps.value = [
+    {
+      title: '创建申请',
+      description: `创建时间:${data.create_datetime}`,
+      status: 'finish'
+    },
+    {
+      title: '待审批',
+      description: '待超级管理员审批',
+      status: data.status >= 2 ? 'finish' : 'process'
+    }
+  ];
+
+  if (data.status >= 2 &&data.status!=3) {
+    steps.value.push({
+      title: '审批通过',
+      description: `审批人:${data.approver_info.name},时间:${data.approve_time}`,
+      status: 'finish'
+    });
+  } else if (data.status === 3) {
+    steps.value.push({
+      title: '审批拒绝',
+      description: `审批人:${data.approver_info.name},拒绝原因: ${data.approve_remark},时间:${data.approve_time}`,
+      status: 'error'
+    });
+  }
+
+  if (data.status >= 4) {
+    steps.value.push({
+      title: '流程完成',
+      description: '流程已结束',
+      status: 'finish'
+    });
+  }
+}
+
+// const activeStep = ref(1);
+
+
+
+const form = ref({
+	borrower_type: 1,
+	application_no: '',
+	external_borrower_name:"",
+	external_borrower_phone:"",
+	emergency_contact: '111',
+	emergency_phone: '11111',
+	app_user_borrower: 1,
+	team_type: 0,
+	team_members_count: 2,
+	team_members: '',
+	expected_start_time:'',
+	expected_end_time: '',
+	borrower_dept: '',
+	purpose: '',
+	applicant_remark_type: "1",
+	applicant_remark: '',
+	items: [],
+	accessories: '',
+	mobile:'',
+	user_code:'',
+	borrow_type: 2,
+	return_location:'',
+	attachments:'',
+	admin_borrower:''
+});
+const showSelectDeviceDialog = ref(false);
+
+
+const rules = {
+   external_borrower_name: [
+    { required: true, message: '请输入借用人', trigger: 'change' },
+  ],
+  app_user_borrower: [
+    { required: true, message: '请输入学号或工号', trigger: 'change' },
+  ],
+  external_borrower_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 {
+		getAllUserList()
+		const bortype=props.modelValue.borrower_type;
+		if(bortype==0){
+			getAdminList()
+			
+		}else if(bortype==1){
+			const userInfo = props.modelValue.borrower_info;
+			if (userInfo) {
+				form.value.borrower_type = 1;
+				form.value.app_user_borrower = userInfo.id || '';
+				// form.value.app_user_borrower = Number(userInfo.dept_info?.dept_id )|| 1;
+				form.value.user_code=userInfo.user_code||'';
+				form.value.mobile=userInfo.mobile||'';
+				form.value.borrower_dept = userInfo.dept_info?.dept_name || '';
+			}
+			console.log("form.value1111:",form.value);
+		}else if(bortype==2){
+			const userInfo = props.modelValue.borrower_info;
+			if (userInfo) {
+				form.value.borrower_type =2;
+				form.value.external_borrower_name = userInfo.id || '';
+				// form.value.app_user_borrower = Number(userInfo.dept_info?.dept_id )|| 1;
+				form.value.user_code=userInfo.user_code||'';
+				form.value.external_borrower_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("val+++",val && val.id && isView.value,val&& isEdit.value);
+  if (val && val.id && isView.value) {
+			try {
+			const res = await apis.GetApplicationDetail(val.id);
+			if (res.code === 2000 && res.data) {
+				const data = res.data;
+				Object.assign(form.value, data);
+				buildSteps(data);
+				form.value.app_user_borrower = res.data.app_user_borrower?.id || '';
+
+       
+			}
+			} catch (e) {
+			  ElMessage.error('获取审批信息失败');
+			}
+	}else if(val&& isEdit.value){
+		try {
+			const res = await apis.GetApplicationDetail(val.id);
+	
+			if (res.code === 2000 && res.data) {
+				const data = res.data;
+				Object.assign(form.value, data);
+				form.value.app_user_borrower = res.data.app_user_borrower?.id || '';
+			}
+			} catch (e) {
+			  ElMessage.error('获取审批信息失败');
+			}
+	}
+}, { immediate: true });
+// 监听借用人选择变化
+watch(() => form.value.app_user_borrower, (newValue) => {
+  if (newValue && allUserList.value.length > 0) {
+    const selectedUser = allUserList.value.find(user => user.id === newValue);
+    if (selectedUser) {
+      form.value.user_code = selectedUser.user_code || '';
+	  form.value.mobile = selectedUser.mobile || '';
+    }
+  }
+});
+
+// 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) {
+	console.log("devices:::",devices);
+	form.value.items.push(
+		...devices.map((d) => ({
+			device_category: d.id,
+			remark: d.name,
+			quantity: 1,
+			location: d.warehouse,
+		}))
+	);
+}
+function removeItem(index: number) {
+	form.value.items.splice(index, 1);
+}
+
+async function onDanger(formId: number){
+	currentFormId.value = formId;
+	showRejectDialog.value = true;
+}
+
+async function onSucess(){
+	const ids=props.modelValue.id;
+	await api.BorrowingReview(ids, "approve", "同意借用");
+	ElMessage.success("审批通过");
+	emit('update:visible', false);
+	crudExpose?.doRefresh();
+}
+
+
+
+
+function onCancel() {
+	emit('update:visible', false);
+}
+function onSubmit() {
+	
+	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.value.team_members_count)||1;
+			form.value.mode = isEdit.value ? 'edit' : 'add';
+			console.log(form.value);
+			// return
+			emit('submit', { ...form.value });
+			emit('update:visible', false);
+			// ElMessage.success("添加成功!");
+		}
+	});
+	
+}
+
+</script>

+ 50 - 2
src/views/system/borrow/approval/curd.tsx

@@ -15,17 +15,24 @@ import * as api from './api';
 import { BorrowingReview, Switchspost } from './api';
 import { ElMessage , ElInput, ElDialog, ElButton } from 'element-plus';
 import dayjs from 'dayjs';
-import { h, ref, computed } from "vue";
+import { h, ref, computed ,inject,defineAsyncComponent} from "vue";
+// import ItemsList from './ItemsList/index.vue';
 
-export const createCrudOptions = function ({ crudExpose ,context, showRejectDialog, rejectReason, currentFormId }: CreateCrudOptionsProps& {
+export const createCrudOptions = function ({ crudExpose ,context, showRejectDialog, rejectReason, currentFormId,expose }: CreateCrudOptionsProps& {
 	showRejectDialog: Ref<boolean>,
 	rejectReason: Ref<string>,
 	currentFormId: Ref<number | null>
   }): CreateCrudOptionsRet {
 
+
+
+
+
+
 	const pageRequest = async (query: UserPageQuery) => {
 		try {
 			const res = await api.GetList(query);
+			console.log("res::::",res)
 			return {
 				...res,
 				records: res.data,
@@ -110,6 +117,12 @@ export const createCrudOptions = function ({ crudExpose ,context, showRejectDial
 				buttons: {
 					view: { 
 						show: true,
+						order:1,
+						click: ({ row }) => {
+							// eslint-disable-next-line no-console
+							// console.log("row:::",row);
+							window.dispatchEvent(new CustomEvent('borrow-view', { detail: row }));
+						},
 					},
 					edit: { show: false },
 					remove: { show: false },
@@ -159,6 +172,8 @@ export const createCrudOptions = function ({ crudExpose ,context, showRejectDial
 							let index = context.index ?? 1;
 							let pagination = crudExpose!.crudBinding.value.pagination;
 							return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
+
+
 						},
 					},
 				},
@@ -314,7 +329,40 @@ export const createCrudOptions = function ({ crudExpose ,context, showRejectDial
 						rules: [{ required: false, message: '请输入当前审批人' }],
 					},
 				},
+				"items[0].device_category_name": {
+					title: '设备分类',
+					search: { show: false },
+					type: 'input',
+					column: { show:false,minWidth: 120 },
+					form: {
+						show: true,
+						component: { placeholder: '' },
+						// rules: [{ required: false, message: '请输入当前审批人' }],
+					},
+				},
+				// "items[0].quantity": {
+				// 	title: '借用数量',
+				// 	search: { show: false },
+				// 	type: 'input',
+				// 	column: {show:false, minWidth: 120 },
+				// 	form: {
+				// 		show: true,
+				// 		component: { placeholder: '' },
+				// 		// rules: [{ required: false, message: '请输入当前审批人' }],
+				// 	},
+				// },
+				// items: {
+				// 	title: '设备列表',
+				// 	type: 'input', // 必须是 component 类型
+				// 	form: {
+				// 	  show: true, // 确保表单中显示
+				// 	  slot: 'ItemsList',
+				// 	}
+				//   },
 				
+				
+				  
+			
 		
 		
 		

+ 72 - 7
src/views/system/borrow/approval/index.vue

@@ -3,28 +3,32 @@
 
 		<fs-crud ref="crudRef" v-bind="crudBinding">
 			</fs-crud>
+      <!-- <ItemsList v-if="formData?.items" :itemss="formData.items" /> -->
 			<RefuseNotification/>
-
-		<!-- <BorrowApprovalDialog ref="approvalDialogRef" @approved="crudExpose.doRefresh()" /> -->
+      <SpecialBorrowApp v-if="showSpecialBorrowDialogapp" v-model:visible="showSpecialBorrowDialogapp" :modelValue="borrowForm" @submit="handleBorrowSubmit" />
 	</fs-page>
 </template>
 
 <script lang="ts" setup name="approval">
-import { onMounted,ref ,provide} from 'vue';
-import { useFs } from '@fast-crud/fast-crud';
+import { onMounted,ref,onBeforeUnmount ,provide,watch,nextTick} from 'vue';
+import { useFs , useCrud, useExpose } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './curd';
 import { ElMessage } from 'element-plus';
 import { BorrowingReview } from './api';
 // import BorrowApprovalDialog from './BorrowApprovalDialog/index.vue';
 import RefuseNotification from './RefuseNotification/index.vue';
-
-
+import ItemsList from './ItemsList/index.vue';
+import SpecialBorrowApp from './SpecialBorrowApp/index.vue';
 // const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
 const showRejectDialog = ref(false);
 const rejectReason = ref('');
 const currentFormId = ref(null);
+const showSpecialBorrowDialogapp = ref(false);
+const borrowForm = ref({});
+
+
 
-const { crudBinding, crudRef, crudExpose } = useFs({
+const { crudBinding, crudRef, crudExpose ,crudOptions} = useFs({
   createCrudOptions: (props) =>
     createCrudOptions({
       ...props,
@@ -43,11 +47,72 @@ provide('currentFormId', currentFormId);
 provide('crudExpose', crudExpose);
 
 
+function onBorrowTypeSelected(type: number, mode: 'view' | 'edit' | 'add' |'collect',record?: any) {
+	nextTick(() => {
+		borrowForm.value = { ...(record || {}), borrow_type: type, mode };
+	});
+	if (type != 1) {
+    showSpecialBorrowDialogapp.value = true;
+	} 
+	
+}
+
+async function handleView(e: CustomEvent) {
+	const row = e.detail;
+	onBorrowTypeSelected(row.borrow_type, 'view',row);
+}
+
+async function handleBorrowSubmit(formData: any) {
+	try {
+		if (formData.mode === 'edit') {
+			// await api.UpdateObj(formData);
+			ElMessage.success('编辑成功');
+		} else {
+			// await api.AddObj(formData);
+			ElMessage.success('添加成功');
+		}
+		
+		// 刷新列表
+		crudRef.value && crudRef.value.doRefresh && crudRef.value.doRefresh();	
+		crudExpose.doRefresh();
+
+	} catch (e) {
+		// 错误处理
+		console.error(formData.mode === 'edit' ? '编辑失败' : '提交失败', e);
+		ElMessage.error(formData.mode === 'edit' ? '编辑失败' : '提交失败');
+	}
+}
+
 
 
+
+// const formRef = crudExpose.getFormRef();
+
+const formData = ref<any>(null);
+
 onMounted(() => {
 	crudExpose.doRefresh();
+  window.addEventListener('borrow-view', handleView);
+ 
+  // watch(
+  //   () => formRef?.visible,
+  //   (visible) => {
+  //     if (visible) {
+  //       formData.value = formRef?.formModel;
+  //       console.log('✅ 表单打开,form 数据:', formRef?.formModel);
+  //     } else {
+  //       formData.value = null;
+  //     }
+  //   }
+  // );
+
 });
+
+onBeforeUnmount(() => {
+	window.removeEventListener('borrow-view', handleView);
+});
+
+
 </script>
 
 <style scoped lang="scss"></style>

+ 63 - 7
src/views/system/borrow/component/CollectEquipment/index.vue

@@ -139,7 +139,7 @@
                     </el-upload>
                 </el-form>
                 <!-- 审批信息流程展示 -->
-                <div v-if="isView"  style="margin-top: 24px; padding: 0 12px">
+                <!-- <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
@@ -167,6 +167,20 @@
                         :status="activeStep >= 4 ? 'finish' : 'wait'"
                         />
                     </el-steps>
+                </div> -->
+                <div v-if="isView"  style="margin-top: 24px; padding: 0 12px">
+                <div style="font-weight: bold; margin-bottom: 12px">审批流程进度</div>
+                <el-steps :active="steps.length" align-center>
+                  <el-step
+                    v-for="(step, index) in steps"
+                    :key="index"
+                    :title="step.title"
+                    :description="step.description"
+                    icon="CircleCheck"
+                    :status="step.status"
+                  />
+                  </el-steps>
+
                 </div>
             </el-tab-pane>
         <el-tab-pane v-if="isCollect" label="设备清单" name="second">
@@ -506,7 +520,7 @@ const timeline = ref({
   approve: '',
   approver: ''
 });
-const activeStep = ref(1);
+// const activeStep = ref(1);
 
 const activeName = ref('first');
 const returnStatus = ref('未归还'); // 添加独立的状态变量
@@ -524,6 +538,8 @@ const returnAccessoryInfo = ref('');
 const returnRemark = ref('');
 const selectedDevices = ref<DeviceListItem[]>([]); // 添加选中设备列表
 
+//审批步骤
+const steps = ref<any[]>([]);
 
 // 添加结算单相关的状态
 const showSettlementDialog = ref(false);
@@ -540,6 +556,45 @@ const currentAbnormalData = ref<AbnormalData>({
   photos: []
 });
 
+//审批
+function buildSteps(data: any) {
+  steps.value = [
+    {
+      title: '创建申请',
+      description: `创建时间:${data.create_datetime}`,
+      status: 'finish'
+    },
+    {
+      title: '待审批',
+      description: '待超级管理员审批',
+      status: data.status >= 2 ? 'finish' : 'process'
+    }
+  ];
+
+  if (data.status >= 2&&data.status!=3 && data.borrow_type!=1) {
+    steps.value.push({
+      title: '审批通过',
+      description: `审批人:${data.approver_info.name},时间:${data.approve_time}`,
+      status: 'finish'
+    });
+  } else if (data.status === 3) {
+    // console.log("data.approver_name:::",data.approver_info.name)
+    steps.value.push({
+      title: '审批拒绝',
+      description: `审批人:${data.approver_info.name},拒绝原因: ${data.approve_remark},时间:${data.approve_time}`,
+      status: 'error'
+    });
+  }
+
+  if (data.status >= 4 ||data.borrow_type==1) {
+    steps.value.push({
+      title: '流程完成',
+      description: '流程已结束',
+      status: 'finish'
+    });
+  }
+}
+
 // 处理异常操作
 const handleAbnormal = (index: number) => {
   currentAbnormalIndex.value = index;
@@ -886,14 +941,15 @@ watch(() => props.modelValue, async (val) => {
 			if (res.code === 2000 && res.data) {
 				const data = res.data;
 				Object.assign(form.value, data);
+        buildSteps(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; // 完成
+                // 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; // 完成
 
                 // 同步 form.items 到 returnDeviceList
                 if (form.value.items && form.value.items.length > 0) {

+ 4 - 4
src/views/system/borrow/component/SpecialBorrow/index.vue

@@ -269,16 +269,16 @@ function buildSteps(data: any) {
     }
   ];
 
-  if (data.status === 2) {
+  if (data.status >= 2&&data.status!=3) {
     steps.value.push({
       title: '审批通过',
-      description: `审批人:${data.approver_name},时间:${data.approve_time}`,
+      description: `审批人:${data.approver_info.name},时间:${data.approve_time}`,
       status: 'finish'
     });
   } else if (data.status === 3) {
     steps.value.push({
       title: '审批拒绝',
-      description: `审批人:${data.approver_name},拒绝原因: ${data.approve_remark},时间:${data.approve_time}`,
+      description: `审批人:${data.approver_info.name},拒绝原因: ${data.approve_remark},时间:${data.approve_time}`,
       status: 'error'
     });
   }
@@ -385,7 +385,7 @@ watch(() => props.modelValue, async (val) => {
 				const data = res.data;
 				Object.assign(form.value, data);
 				buildSteps(data);
-				console.log("data:::",data);
+				// console.log("data:::",data);
 
 			}
 			} catch (e) {

+ 5 - 5
src/views/system/borrow/workflow/WorkflowDialog/index.vue

@@ -12,14 +12,11 @@
 						<el-input v-model="form.name" placeholder="请输入" />
 					</el-form-item>
 				</el-col>
-				<el-col :span="12">
+				<!-- <el-col :span="12">
 					<el-form-item label="适用场景" prop="scene">
 						<el-input v-model="form.scene" placeholder="请输入" />
 					</el-form-item>
-				</el-col>
-				
-			</el-row>
-			<el-row :gutter="20">
+				</el-col> -->
 				<el-col :span="12">
 					<el-form-item label="流程类型" prop="workflow_type">
 						<el-select v-model="form.workflow_type" placeholder="请选择流程类型" :disabled="props.mode === 'view'">
@@ -31,6 +28,9 @@
 						</el-select>
 					</el-form-item>
 				</el-col>
+			</el-row>
+			<el-row :gutter="20">
+				
 				<el-col :span="12">
 					<el-form-item label="流程描述" prop="description">
 						<el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入流程描述" :disabled="props.mode === 'view'" />

+ 3 - 0
src/views/system/devicelabel/index.vue

@@ -1,6 +1,9 @@
 <template>
 	<fs-page>
 		<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
+		<!-- <template #form-header-right>
+        <el-tag color="danger">form-header-right插槽</el-tag>
+      </template> -->
 	</fs-page>
 </template>
 

+ 7 - 0
src/views/system/login/api.ts

@@ -28,3 +28,10 @@ export function getUserInfo() {
         method: 'get',
     });
 }
+
+export function getAdminUserInfo() {
+    return request({
+        url: '/api/system/app_user/1/',
+        method: 'get',
+    });
+}