Explorar el Código

add:采购申请模版

zoie hace 5 días
padre
commit
50b2a3e8ef

BIN
public/采购申请模版.xlsx


+ 12 - 0
src/api/storehouse/index.ts

@@ -179,6 +179,9 @@ export const Storehouse_StockIn_Apply_del = (params: any) => $http.post('/storag
 // 出库列表
 export const Storehouse_StockOut_List = (params: any) => $http.post('/storage/StockOut/List', params)
 
+export const Storehouse_StockOut_Company_Name_List = (params: any) => $http.post('/storage/StockOut/Company_Name_List', params)
+export const Storehouse_StockOut_Project_List = (params: any) => $http.post('/storage/StockOut/Project_List', params)
+
 // 出库明细
 export const Storehouse_StockOut_ListProduct = (params: any) => $http.post('/storage/StockOut/List_Product', params)
 // 生成出库单号
@@ -232,6 +235,15 @@ export const Storehouse_Device_Check = (params: any) => $http.post('/storage/Dev
 export const Storehouse_Device_Take_Stock = (params: any) => $http.post('/storage/Device/Take_Stock', params)
 
 /**
+ * 公司设备管理
+ */
+// 设备列表
+export const Storehouse_Company_Device_Statistics = (params: any) => $http.post('/storage/Company/Device_Statistics', params)
+export const Storehouse_Company_Device_Statistics_Excel = (params: any) => $http.post('/storage/Company/Device_Statistics_Excel', params, {responseType: 'blob'})
+
+
+
+/**
  * 验证合同
  */
 // 客户列表

+ 1 - 0
src/hooks/useDepot.ts

@@ -101,6 +101,7 @@ export interface InStoreageFormType {
 	T_date: string
 	T_remark: string
 	T_project: string
+	T_company_name: string
 	T_return_user: string
 	T_return_user_name: string
 	T_batch_number: string

+ 85 - 0
src/hooks/useRemoteSearch.ts

@@ -0,0 +1,85 @@
+import { ref } from 'vue'
+import { GlobalStore } from '@/stores/index'
+import { Storehouse_StockOut_Company_Name_List, Storehouse_StockOut_Project_List } from '@/api/storehouse/index'
+
+/**
+ * 公司名称和退库项目的远程搜索
+ */
+export const useRemoteSearch = () => {
+	const globalStore = GlobalStore()
+
+	// 公司名称远程搜索
+	const stockOutCompanyNameOptions = ref<any[]>([])
+	const isStockOutCompanyNameLoading = ref(false)
+
+	// 退库项目远程搜索
+	const stockOutProjectOptions = ref<any[]>([])
+	const isStockOutProjectLoading = ref(false)
+
+	/**
+	 * 公司名称远程搜索
+	 */
+	const queryStockOutCompanyNameAsync = async (query: string) => {
+		if (!query) {
+			stockOutCompanyNameOptions.value = []
+			return
+		}
+		isStockOutCompanyNameLoading.value = true
+		try {
+			const res: any = await Storehouse_StockOut_Company_Name_List({
+				User_tokey: globalStore.GET_User_tokey,
+				T_name: query
+			})
+			if (res.Code === 200 && res.Data) {
+				stockOutCompanyNameOptions.value = res.Data.map((item: any) => ({
+					value: item,
+					label: item
+				}))
+			}
+		} catch (error) {
+			console.error('查询公司名称失败:', error)
+		} finally {
+			isStockOutCompanyNameLoading.value = false
+		}
+	}
+
+	/**
+	 * 退库项目远程搜索
+	 */
+	const queryStockOutProjectAsync = async (query: string) => {
+		if (!query) {
+			stockOutProjectOptions.value = []
+			return
+		}
+		isStockOutProjectLoading.value = true
+		try {
+			const res: any = await Storehouse_StockOut_Project_List({
+				User_tokey: globalStore.GET_User_tokey,
+				T_name: query
+			})
+			if (res.Code === 200 && res.Data) {
+				stockOutProjectOptions.value = res.Data.map((item: any) => ({
+					value: item,
+					label: item
+				}))
+			}
+		} catch (error) {
+			console.error('查询退库项目失败:', error)
+		} finally {
+			isStockOutProjectLoading.value = false
+		}
+	}
+
+	return {
+		// 公司名称相关
+		stockOutCompanyNameOptions,
+		isStockOutCompanyNameLoading,
+		queryStockOutCompanyNameAsync,
+		
+		// 退库项目相关
+		stockOutProjectOptions,
+		isStockOutProjectLoading,
+		queryStockOutProjectAsync
+	}
+}
+

+ 3 - 0
src/hooks/useTablePublic.ts

@@ -102,6 +102,9 @@ export interface PurchaseApproverInfoIn {
 	T_approver_name: string
 	T_remark: string
 	T_Detail: any
+	T_product_name?: string
+	T_product_quantity?: number
+	T_total_price?: number
 }
 
 export const Project_State = [

+ 3 - 0
src/views/purchase/MyPurchase.vue

@@ -18,6 +18,9 @@ const columns: ColumnProps[] = [
 	{prop: 'T_date', label: '申请时间'},
 	{prop: 'T_dept', label: '申请部门'},
 	{prop: 'T_uuid_name', label: '申请人'},
+	{prop: 'T_product_name', label: '产品名称'},
+	{prop: 'T_product_quantity', label: '产品数量', width: 100},
+	{prop: 'T_total_price', label: '产品总价', width: 120},
 	{prop: 'T_State', label: '状态', name: 'T_State'},
 	{prop: 'T_submit_name', label: '提交人'},
 	{prop: 'T_approver_name', label: '审批人'},

+ 3 - 0
src/views/purchase/Purchase.vue

@@ -19,6 +19,9 @@ const columns: ColumnProps[] = [
 	{prop: 'T_date', label: '申请时间'},
 	{prop: 'T_dept', label: '申请部门'},
 	{prop: 'T_uuid_name', label: '申请人'},
+	{prop: 'T_product_name', label: '产品名称'},
+	{prop: 'T_product_quantity', label: '产品数量', width: 100},
+	{prop: 'T_total_price', label: '产品总价', width: 120},
 	{prop: 'T_State', label: '状态', name: 'T_State'},
 	{prop: 'T_submit_name', label: '提交人'},
 	{prop: 'T_approver_name', label: '审批人'},

+ 31 - 9
src/views/purchase/PurchaseApprover.vue

@@ -99,6 +99,9 @@ const ApprovalPurchase = async (T_State: number) => {
 							<div>申请人: {{ userInfo.T_uuid_name }}</div>
 							<div>提交人: {{ userInfo.T_submit_name }}</div>
 							<div>审批人: {{ userInfo.T_approver_name }}</div>
+							<div>产品名称: {{ userInfo.T_product_name || '-' }}</div>
+							<div>产品数量: {{ userInfo.T_product_quantity || '-' }}</div>
+							<div>产品总价: {{ userInfo.T_total_price || '-' }}</div>
 							<div>
 								<span class="ml-3 w-35 text-gray-600 inline-flex items-center">备注:</span>
 								{{ userInfo.T_remark }}
@@ -108,15 +111,34 @@ const ApprovalPurchase = async (T_State: number) => {
 							<div>
 
 								<el-table :data="userInfo.T_Detail" border max-height="100%">
-									<el-table-column type="index" label="序号" width="100"/>
-									<el-table-column property="T_name" label="名称"/>
-									<el-table-column property="T_model" label="型号"/>
-									<el-table-column property="T_spec" label="规格"/>
-									<el-table-column property="T_quantity" label="数量"/>
-									<el-table-column property="T_reference_site" label="参考网址"/>
-									<el-table-column property="T_demand" label="需求"/>
-									<el-table-column property="T_remark" label="备注"/>
-									<el-table-column property="T_unit_price" label="采购单价"/>
+									<el-table-column type="index" label="序号" width="80" align="center"/>
+									<el-table-column property="T_name" label="名称" show-overflow-tooltip min-width="120"/>
+									<el-table-column property="T_bit_number" label="位号" show-overflow-tooltip width="100"/>
+									<el-table-column property="T_packaging" label="封装" show-overflow-tooltip width="100"/>
+									<el-table-column property="T_model" label="型号" show-overflow-tooltip min-width="120"/>
+									<el-table-column property="T_spec" label="规格" show-overflow-tooltip min-width="120"/>
+									<el-table-column property="T_quantity" label="数量" width="80" align="center"/>
+									<el-table-column property="T_demand" label="需求" show-overflow-tooltip min-width="140"/>
+									<el-table-column property="T_unit_price" label="采购单价" width="100" align="center"/>
+									<el-table-column property="T_amount" label="采购总价" width="100" align="center"/>
+									<el-table-column label="参考网址" width="140" show-overflow-tooltip align="center">
+										<template #default="{ row }">
+											<a 
+												v-if="row.T_reference_site" 
+												:href="row.T_reference_site" 
+												target="_blank" 
+												style="color: #409eff; text-decoration: underline;"
+											>
+												{{ row.T_reference_site_title || '参考网址' }}
+											</a>
+														<!-- 如果没有链接地址但有标题,显示纯文本 -->
+											<span v-else-if="row.T_reference_site_title">{{row.T_reference_site_title }}</span>
+											<!-- 两者都没有,显示 - -->
+											<span v-else>-</span>
+										</template>
+									</el-table-column>
+									<el-table-column property="T_remark" label="备注" show-overflow-tooltip min-width="140"/>
+									
 								</el-table>
 							</div>
 

+ 541 - 22
src/views/purchase/PurchaseDetail.vue

@@ -4,6 +4,8 @@ import Drawer from '@/components/Drawer/index.vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { ColumnProps } from '@/components/TableBase/interface/index'
 import { generateRandom } from '@/utils/common'
+import { ElMessage } from 'element-plus'
+import * as XLSX from 'xlsx'
 
 const isNew = ref(true)
 const tableData = ref<any[]>([])
@@ -12,36 +14,83 @@ const ruleFormRef = ref<FormInstance>()
 const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 
 const form = ref({
-  	id: '',
+  id: '',
 	T_name:'',
 	T_model:'',
 	T_spec:'',
-	T_quantity:'',
+	T_quantity: null as number | null,
 	T_demand:'',
 	T_remark:'',
 	T_state:1,
 	T_date:'',
-	T_unit_price:'',
-	T_amount:'',
+	T_unit_price: null as number | null,
+	T_amount: null as number | null,
 	T_reference_site:'',
+	T_reference_site_title:'',
+	T_bit_number:'',
+	T_packaging:'',
 })
+
+// 验证整数
+const validateInteger = (rule: any, value: any, callback: any) => {
+	if (value === '' || value === null || value === undefined) {
+		callback()
+		return
+	}
+	const num = Number(value)
+	if (isNaN(num) || !Number.isInteger(num)) {
+		callback(new Error('请输入整数'))
+	} else if (num < 0) {
+		callback(new Error('数量不能为负数'))
+	} else {
+		callback()
+	}
+}
+
+// 验证小数(正数)
+const validateDecimal = (rule: any, value: any, callback: any) => {
+	if (value === '' || value === null || value === undefined) {
+		callback()
+		return
+	}
+	const num = Number(value)
+	if (isNaN(num)) {
+		callback(new Error('请输入有效的数字'))
+	} else if (num < 0) {
+		callback(new Error('价格不能为负数'))
+	} else {
+		callback()
+	}
+}
+
 const rules = reactive<FormRules>({
 	T_name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
-	T_quantity: [{ required: true, message: '数量', trigger: 'blur' }]
+	T_quantity: [
+		{ required: true, message: '请输入数量', trigger: 'blur' },
+		{ validator: validateInteger, trigger: 'blur' }
+	],
+	T_unit_price: [
+		{ validator: validateDecimal, trigger: 'blur' }
+	],
+	T_amount: [
+		{ validator: validateDecimal, trigger: 'blur' }
+	]
 })
 const columns: ColumnProps[] = [
   { type: 'index', label: '序号', width: 80, align: 'center' },
   { prop: 'T_name', label: '名称', align: 'center' },
+  { prop: 'T_bit_number', label: '位号', align: 'center', width: 100 },
+  { prop: 'T_packaging', label: '封装', align: 'center', width: 100 },
   { prop: 'T_model', label: '型号', align: 'center', width: 100 },
   { prop: 'T_spec', label: '规格', align: 'center', width: 100 },
   { prop: 'T_quantity', label: '数量', align: 'center'},
-  { prop: 'T_reference_site', label: '参考网址', align: 'center', width: 140 },
   { prop: 'T_demand', label: '需求', align: 'center', width: 140 },
+  { prop: 'T_date', label: '采购时间', align: 'center', width: 150 },
+  { prop: 'T_unit_price', label: '采购单价', align: 'center', width: 120},
+  { prop: 'T_amount', label: '采购金额', align: 'center', width: 120},
+  { prop: 'T_reference_site', label: '参考网址', align: 'center', width: 140 },
   { prop: 'T_remark', label: '备注', align: 'center', width: 140 },
-  { prop: 'T_state', label: '状态', align: 'center', width: 80 },
-  { prop: 'T_date', label: '采购时间', align: 'center', width: 120 },
-  { prop: 'T_unit_price', label: '采购单价', align: 'center', width: 100},
-  { prop: 'T_amount', label: '采购金额', align: 'center', width: 100},
+  { prop: 'T_state', label: '状态', align: 'center', width: 120 },
   { prop: 'operation', label: '操作', width: 160, fixed: 'right', align: 'center' }
 ]
 
@@ -68,11 +117,19 @@ const addPurchaseDetail = (formEl: FormInstance | undefined) => {
   if (!formEl) return
   formEl.validate(valid => {
     if (valid) {
+      // 确保数据类型正确
+      const formattedData = {
+        ...form.value,
+        T_quantity: form.value.T_quantity ? parseInt(form.value.T_quantity.toString()) : 0,
+        T_unit_price: form.value.T_unit_price ? parseFloat(form.value.T_unit_price.toString()) : 0,
+        T_amount: form.value.T_amount ? parseFloat(form.value.T_amount.toString()) : 0
+      }
+      
       if (isNew.value) {
-        tableData.value.push({ ...form.value, id: generateRandom() })
+        tableData.value.push({ ...formattedData, id: generateRandom() })
       } else {
         const index = tableData.value.findIndex(item => item.id === form.value.id)
-        tableData.value[index] = { ...form.value }
+        tableData.value[index] = { ...formattedData }
       }
 
       nextTick(() => {
@@ -102,6 +159,197 @@ const props = defineProps<{ disabled: boolean; isShow?: string;typeTip?:string }
 const disabled = computed(() => props.disabled)
 const isShow = ref(props.isShow)
 const typeTip = ref(props.typeTip)
+
+// 导入Excel文件
+const importDialogVisible = ref(false)
+const fileInputRef = ref<HTMLInputElement | null>(null)
+const uploadFileList = ref<any[]>([])
+const tempImportData = ref<any[]>([]) // 临时存储解析的数据
+
+const handleImportExcel = () => {
+	importDialogVisible.value = true
+	uploadFileList.value = []
+	tempImportData.value = []
+}
+
+// 下载模板
+const handleDownloadTemplate = () => {
+	const link = document.createElement('a')
+	link.href = '/采购申请模版.xlsx'
+	link.download = '采购申请模版.xlsx'
+	link.click()
+	ElMessage.success('模板下载成功')
+}
+
+// 解析Excel文件
+const parseExcelFile = (file: File) => {
+	const reader = new FileReader()
+	reader.onload = (e) => {
+		try {
+			const data = e.target?.result
+			const workbook = XLSX.read(data, { type: 'array' })
+			
+			// 读取第一个sheet
+			const firstSheetName = workbook.SheetNames[0]
+			const worksheet = workbook.Sheets[firstSheetName]
+			
+			// 将sheet转换为JSON
+			const jsonData: any[] = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
+			
+			// 辅助函数:解码HTML实体
+			const decodeHTMLEntities = (text: string): string => {
+				const textarea = document.createElement('textarea')
+				textarea.innerHTML = text
+				return textarea.value
+			}
+			
+			// 辅助函数:获取单元格超链接
+			const getCellHyperlink = (worksheet: any, rowIndex: number, colIndex: number): string => {
+				// 将列索引转换为Excel列字母(0->A, 1->B, ..., 10->K)
+				const colLetter = String.fromCharCode(65 + colIndex)
+				// Excel行号从1开始,但我们的数据从第2行开始(索引1对应Excel的第2行)
+				const cellAddress = `${colLetter}${rowIndex + 1}`
+				const cell = worksheet[cellAddress]
+				
+				// 检查单元格是否有超链接
+				if (cell && cell.l && cell.l.Target) {
+					// 解码HTML实体,将 &amp; 转换回 &
+					return decodeHTMLEntities(cell.l.Target)
+				}
+				
+				// 如果没有超链接,返回单元格的文本值
+				// return cell ? (cell.v || '') : ''
+				return ''
+			}
+			
+			// 解析数据(从第2行开始,跳过标题行)
+			const parsedData: any[] = []
+			
+			for (let i = 1; i < jsonData.length; i++) {
+				const row = jsonData[i]
+				if (!row || row.length === 0) continue
+				
+			// 获取参考网址的超链接(第11列,索引为10)
+			const referenceSite = getCellHyperlink(worksheet, i, 10)
+			
+			// 数据类型转换
+			const quantity = row[5] ? parseInt(row[5].toString()) : 0
+			const unitPrice = row[8] ? parseFloat(row[8].toString()) : 0
+			const amount = row[9] ? parseFloat(row[9].toString()) : 0
+			
+			// 根据Excel列映射到表单字段
+			const item = {
+				id: generateRandom(),
+				T_name: row[1] || '', // 名称
+				T_bit_number: row[2] || '', // 位号
+				T_packaging: row[3] || '', // 封装
+				T_model: '', // 型号
+				T_spec: row[4] || '', // 规格
+				T_quantity: quantity, // 数量(整数)
+				T_demand: (row[6] !== undefined && row[6] !== null) ? row[6].toString() : '', // 需求(转换为字符串)
+				T_unit_price: unitPrice, // 采购单价(小数)
+				T_amount: amount, // 采购金额(小数)
+				T_reference_site: referenceSite, // 参考网址(超链接)
+				T_reference_site_title: row[10] || '', // 参考网址标题
+				T_remark: row[11] || '', // 备注
+				T_state: 1, // 状态:待采购
+				T_date: '' // 采购时间
+			}
+			
+			parsedData.push(item)
+			}
+			
+			if (parsedData.length > 0) {
+				tempImportData.value = parsedData
+				ElMessage.success(`文件解析成功,共 ${parsedData.length} 条有效数据`)
+			} else {
+				ElMessage.warning('未找到有效数据')
+			}
+		} catch (error) {
+			console.error('Excel解析错误:', error)
+			ElMessage.error('Excel文件解析失败,请检查文件格式')
+		}
+	}
+	
+	reader.onerror = () => {
+		ElMessage.error('文件读取失败')
+	}
+	
+	reader.readAsArrayBuffer(file)
+}
+
+// 文件列表改变
+const handleFileChange = (file: any, fileList: any[]) => {
+	uploadFileList.value = fileList
+	
+	// 当文件被选中时,立即解析
+	if (file && file.raw) {
+		const isExcel = file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || 
+		                file.raw.type === 'application/vnd.ms-excel'
+		if (!isExcel) {
+			ElMessage.error('只能上传Excel文件!')
+			uploadFileList.value = []
+			return
+		}
+		parseExcelFile(file.raw)
+	}
+}
+
+// 提交导入
+const handleSubmitImport = () => {
+	if (tempImportData.value.length === 0) {
+		ElMessage.warning('请先上传Excel文件')
+		return
+	}
+	
+	// 将解析的数据添加到表格
+	tableData.value.push(...tempImportData.value)
+	ElMessage.success(`成功导入 ${tempImportData.value.length} 条数据`)
+	emit('onPlanning', tableData.value)
+	
+	// 关闭对话框并清空数据
+	importDialogVisible.value = false
+	uploadFileList.value = []
+	tempImportData.value = []
+}
+
+// 批量设置采购时间
+const batchDateDialogVisible = ref(false)
+const batchDate = ref('')
+
+const handleBatchSetDate = () => {
+	batchDateDialogVisible.value = true
+	batchDate.value = ''
+}
+
+const confirmBatchSetDate = () => {
+	if (!batchDate.value) {
+		ElMessage.warning('请选择日期')
+		return
+	}
+	
+	tableData.value.forEach((item: any) => {
+		item.T_date = batchDate.value
+	})
+	
+	ElMessage.success('批量设置成功')
+	emit('onPlanning', tableData.value)
+	batchDateDialogVisible.value = false
+}
+
+// 处理表格内数据变化
+const handleCellValueChange = (row: any, field: string, value: any) => {
+	row[field] = value
+	emit('onPlanning', tableData.value)
+}
+
+// 格式化数字显示,0值显示为空
+const formatNumberDisplay = (value: any) => {
+	if (value === 0 || value === '0' || value === null || value === undefined) {
+		return ''
+	}
+	return value
+}
 </script>
 
 <template>
@@ -116,6 +364,7 @@ const typeTip = ref(props.typeTip)
   >
     <template v-for="item in columns" :key="item.prop">
 
+		<!-- 状态列 -->
 		<el-table-column
 			v-if="item.prop === 'T_state'"
 			:label="item.label"
@@ -123,18 +372,142 @@ const typeTip = ref(props.typeTip)
 			align="center"
 		>
 			<template v-slot="scope">
-				<el-text v-if="scope.row.T_state === 1" effect="dark">待采购</el-text>
-				<el-text v-else type="success" effect="dark">已采购</el-text>
+				<template v-if="isShow !== 'myPurchase' && typeTip !== 'view'">
+					<el-select 
+						v-model="scope.row.T_state" 
+						size="small"
+						style="width: 100%"
+						@change="handleCellValueChange(scope.row, 'T_state', scope.row.T_state)"
+					>
+						<el-option :label="'待采购'" :value="1" />
+						<el-option :label="'已采购'" :value="2" />
+					</el-select>
+				</template>
+				<template v-else>
+					<el-text v-if="scope.row.T_state === 1" effect="dark">待采购</el-text>
+					<el-text v-else type="success" effect="dark">已采购</el-text>
+				</template>
+			</template>
+		</el-table-column>
+		<el-table-column
+			v-else-if="item.prop === 'T_reference_site'"
+			:label="item.label"
+			:width="item.width"
+			align="center"
+		>
+			<template v-slot="scope">
+				<!-- 如果有链接地址,显示为可点击链接 -->
+				<a 
+					v-if="scope.row.T_reference_site" 
+					:href="scope.row.T_reference_site" 
+					target="_blank" 
+					style="color: #409eff; text-decoration: underline;"
+				>
+					{{ scope.row.T_reference_site_title || '参考网址' }}
+				</a>
+				<!-- 如果没有链接地址但有标题,显示纯文本 -->
+				<span v-else-if="scope.row.T_reference_site_title">{{ scope.row.T_reference_site_title }}</span>
+				<!-- 两者都没有,显示 - -->
+				<span v-else>-</span>
+			</template>
+		</el-table-column>
+		<!-- 采购时间可编辑列 -->
+		<el-table-column
+			v-else-if="item.prop === 'T_date' && isShow !== 'myPurchase'"
+			:width="item.width"
+			align="center"
+		>
+			<template #header>
+				<span style="cursor: pointer; color: #409eff" @click="handleBatchSetDate" title="点击批量设置时间">
+					{{ item.label }}
+				</span>
+			</template>
+			<template v-slot="scope">
+				<el-date-picker
+					v-model="scope.row.T_date"
+					type="date"
+					placeholder="采购时间"
+					format="YYYY-MM-DD"
+					value-format="YYYY-MM-DD"
+					size="small"
+					style="width: 100%"
+					:disabled="typeTip === 'view'"
+					@change="handleCellValueChange(scope.row, 'T_date', scope.row.T_date)"
+				/>
+			</template>
+		</el-table-column>
+		<!-- 采购单价可编辑列 -->
+		<el-table-column
+			v-else-if="item.prop === 'T_unit_price' && isShow !== 'myPurchase'"
+			:label="item.label"
+			:width="item.width"
+			align="center"
+		>
+			<template v-slot="scope">
+				<el-input-number
+					v-model="scope.row.T_unit_price"
+					:min="0"
+					:precision="2"
+					:controls="false"
+					size="small"
+					style="width: 100%; text-align: left"
+					:disabled="typeTip === 'view'"
+					:placeholder="scope.row.T_unit_price === 0 ? '' : '请输入单价'"
+					@change="handleCellValueChange(scope.row, 'T_unit_price', scope.row.T_unit_price)"
+					@blur="scope.row.T_unit_price === 0 && (scope.row.T_unit_price = null)"
+					@focus="scope.row.T_unit_price === null && (scope.row.T_unit_price = 0)"
+				/>
+			</template>
+		</el-table-column>
+		<!-- 采购金额可编辑列 -->
+		<el-table-column
+			v-else-if="item.prop === 'T_amount' && isShow !== 'myPurchase'"
+			:label="item.label"
+			:width="item.width"
+			align="center"
+		>
+			<template v-slot="scope">
+				<el-input-number
+					v-model="scope.row.T_amount"
+					:min="0"
+					:precision="2"
+					:controls="false"
+					size="small"
+					style="width: 100%; text-align: left"
+					:disabled="typeTip === 'view'"
+					:placeholder="scope.row.T_amount === 0 ? '' : '请输入金额'"
+					@change="handleCellValueChange(scope.row, 'T_amount', scope.row.T_amount)"
+					@blur="scope.row.T_amount === 0 && (scope.row.T_amount = null)"
+					@focus="scope.row.T_amount === null && (scope.row.T_amount = 0)"
+				/>
+			</template>
+		</el-table-column>
+		<!-- 备注可编辑列 -->
+		<el-table-column
+			v-else-if="item.prop === 'T_remark' && isShow !== 'myPurchase'"
+			:label="item.label"
+			:width="item.width"
+			align="center"
+		>
+			<template v-slot="scope">
+				<el-input
+					v-model="scope.row.T_remark"
+					type="text"
+					placeholder="请输入备注"
+					size="small"
+					:disabled="typeTip === 'view'"
+					@change="handleCellValueChange(scope.row, 'T_remark', scope.row.T_remark)"
+				/>
 			</template>
 		</el-table-column>
       <el-table-column show-overflow-tooltip v-bind="item" v-else-if="item.type === 'index'"></el-table-column>
       <el-table-column show-overflow-tooltip v-bind="item" v-else-if="item.fixed !== 'right' && item.type !== 'index'"> </el-table-column>
-      <el-table-column show-overflow-tooltip v-bind="item" v-else-if="item.fixed === 'right'">
-        <template #default="{ row }">
-          <el-button size="small" :disabled="typeTip === 'view'" type="primary" @click="openProductionDetailed('edit', row)"
+      <el-table-column show-overflow-tooltip v-bind="item" v-else-if="item.fixed === 'right' && isShow === 'myPurchase' && typeTip !== 'view'">
+        <template #default="{ row }" >
+          <el-button size="small" type="primary" @click="openProductionDetailed('edit', row)"
             >编辑</el-button
           >
-          <el-button size="small" :disabled="isShow !== 'myPurchase' || typeTip === 'view'" type="danger" @click="deletePurchaseDetail(row.id)"
+          <el-button size="small" type="danger" @click="deletePurchaseDetail(row.id)"
             >删除</el-button>
         </template>
       </el-table-column>
@@ -143,16 +516,104 @@ const typeTip = ref(props.typeTip)
       <el-button type="primary" :disabled="disabled" @click="openProductionDetailed('new')">
         <el-icon><Plus /></el-icon><span style="margin-left: 6px">添加</span>
       </el-button>
+      <el-button type="success" :disabled="disabled" @click="handleImportExcel" style="margin-left: 10px">
+        <el-icon><Plus /></el-icon><span style="margin-left: 6px">导入Excel</span>
+      </el-button>
     </template>
   </el-table>
+  
+  <!-- 导入Excel对话框 -->
+  <el-dialog
+    v-model="importDialogVisible"
+    title="导入Excel数据"
+    width="500px"
+    :close-on-click-modal="false"
+  >
+    <div style="margin-bottom: 20px">
+      <el-alert
+        title="请先下载模板,按照模板格式填写数据后上传"
+        type="info"
+        :closable="false"
+        show-icon
+      />
+    </div>
+    
+    <div style="margin-bottom: 20px; text-align: center">
+      <el-button type="primary" @click="handleDownloadTemplate">
+        <el-icon><Download /></el-icon>
+        <span style="margin-left: 6px">下载模板</span>
+      </el-button>
+    </div>
+    
+    <el-upload
+      drag
+      :file-list="uploadFileList"
+      :on-change="handleFileChange"
+      :auto-upload="false"
+      accept=".xlsx,.xls"
+      :limit="1"
+    >
+      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+      <div class="el-upload__text">
+        将文件拖到此处,或<em>点击上传</em>
+      </div>
+      <template #tip>
+        <div class="el-upload__tip">
+          只能上传 xlsx/xls 文件
+        </div>
+      </template>
+    </el-upload>
+    
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleSubmitImport">提交</el-button>
+        <el-button @click="importDialogVisible = false">取消</el-button>
+      </span>
+    </template>
+  </el-dialog>
+
+  <!-- 批量设置采购时间对话框 -->
+  <el-dialog
+    v-model="batchDateDialogVisible"
+    title="批量设置采购时间"
+    width="400px"
+    :close-on-click-modal="false"
+  >
+    <el-form label-width="100px">
+      <el-form-item label="采购时间:">
+        <el-date-picker
+          v-model="batchDate"
+          type="date"
+          placeholder="请选择采购时间"
+          format="YYYY-MM-DD"
+          value-format="YYYY-MM-DD"
+          style="width: 100%"
+        />
+      </el-form-item>
+    </el-form>
+    
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="confirmBatchSetDate">确定</el-button>
+        <el-button @click="batchDateDialogVisible = false">取消</el-button>
+      </span>
+    </template>
+  </el-dialog>
   <Drawer ref="drawerRef" :handleClose="callbackDrawer">
     <template #header="{ params }">
       <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 采购明细</h4>
     </template>
+
     <el-form ref="ruleFormRef" :model="form" :rules="rules">
       <el-form-item label="名称:" :label-width="formLabelWidth" prop="T_name">
         <el-input v-model="form.T_name" type="text" autocomplete="off" placeholder="请输入名称" />
       </el-form-item>
+      <el-form-item label="位号:" :label-width="formLabelWidth" prop="T_bit_number">
+        <el-input v-model="form.T_bit_number" type="text" autocomplete="off" placeholder="请输入位号" />
+      </el-form-item>
+      <el-form-item label="封装:" :label-width="formLabelWidth" prop="T_packaging">
+        <el-input v-model="form.T_packaging" type="text" autocomplete="off" placeholder="请输入封装" />
+      </el-form-item>
       <el-form-item label="型号:" :label-width="formLabelWidth" prop="T_model">
         <el-input v-model="form.T_model" type="text" autocomplete="off" placeholder="请输入型号" />
       </el-form-item>
@@ -160,7 +621,16 @@ const typeTip = ref(props.typeTip)
         <el-input v-model="form.T_spec" type="text" autocomplete="off" placeholder="请输入规格" />
       </el-form-item>
 		<el-form-item label="数量:" :label-width="formLabelWidth" prop="T_quantity">
-			<el-input v-model="form.T_quantity" type="text" autocomplete="off" placeholder="请输入数量" />
+			<el-input-number 
+				v-model="form.T_quantity" 
+				:min="0" 
+				:precision="0" 
+				:controls="false" 
+				placeholder="请输入数量" 
+				style="width: 100%; text-align: left"
+				@blur="form.T_quantity === 0 && (form.T_quantity = null)"
+				@focus="form.T_quantity === null && (form.T_quantity = 0)"
+			/>
 		</el-form-item>
 		<el-form-item label="参考网址:" :label-width="formLabelWidth" prop="T_reference_site">
 			<el-input v-model="form.T_reference_site" type="text" autocomplete="off" placeholder="请输入参考网址" />
@@ -189,10 +659,28 @@ const typeTip = ref(props.typeTip)
 			/>
 		</el-form-item>
 		<el-form-item label="采购单价:" :label-width="formLabelWidth" prop="T_unit_price">
-			<el-input v-model="form.T_unit_price" type="text" autocomplete="off" placeholder="请输入采购单价" />
+			<el-input-number 
+				v-model="form.T_unit_price" 
+				:min="0" 
+				:precision="2" 
+				:controls="false" 
+				placeholder="请输入采购单价" 
+				style="width: 100%; text-align: left"
+				@blur="form.T_unit_price === 0 && (form.T_unit_price = null)"
+				@focus="form.T_unit_price === null && (form.T_unit_price = 0)"
+			/>
 		</el-form-item>
-		<el-form-item v-if="isShow !== 'myPurchase'" label="采购金额:" :label-width="formLabelWidth" prop="T_amount">
-			<el-input v-model="form.T_amount" type="text" autocomplete="off" placeholder="请输入采购金额" />
+		<el-form-item  label="采购金额:" :label-width="formLabelWidth" prop="T_amount">
+			<el-input-number 
+				v-model="form.T_amount" 
+				:min="0" 
+				:precision="2" 
+				:controls="false" 
+				placeholder="请输入采购金额" 
+				style="width: 100%; text-align: left"
+				@blur="form.T_amount === 0 && (form.T_amount = null)"
+				@focus="form.T_amount === null && (form.T_amount = 0)"
+			/>
 		</el-form-item>
       <el-form-item :label-width="formLabelWidth">
         <el-button v-if="isNew" class="btn"  type="primary" @click="addPurchaseDetail(ruleFormRef)"
@@ -213,4 +701,35 @@ const typeTip = ref(props.typeTip)
 .el-form-item {
   margin-bottom: 18px;
 }
+
+// 数字输入框样式优化
+:deep(.el-input-number) {
+  .el-input__inner {
+    text-align: left !important;
+  }
+  
+  // 当值为0时隐藏显示
+  &.is-empty .el-input__inner {
+    color: transparent;
+  }
+}
+
+// 表格中的数字输入框
+:deep(.el-table .el-input-number) {
+  .el-input__inner {
+    text-align: left !important;
+    border: none;
+    background: transparent;
+    padding: 0 8px;
+  }
+  
+  &:hover .el-input__inner {
+    background: #f5f7fa;
+  }
+  
+  &.is-focus .el-input__inner {
+    background: #fff;
+    border: 1px solid #409eff;
+  }
+}
 </style>

+ 90 - 15
src/views/purchase/PurchaseForm.vue

@@ -30,10 +30,16 @@ const form = ref({
   T_State: '',
   T_approver: '',
   T_approver_name: '',
+  T_product_name: '',
+  T_product_quantity: '',
+  T_total_price: '',
 })
 
 const rules = reactive<FormRules>({
-  T_name: [{ required: true, message: '请输入采购申请名称', trigger: 'blur' }]
+  T_date: [{ required: true, message: '请选择申请日期', trigger: 'change' }],
+  T_dept: [{ required: true, message: '请选择申请部门', trigger: 'change' }],
+  T_uuid_name: [{ required: true, message: '请选择申请人', trigger: 'blur' }],
+  T_approver_name: [{ required: true, message: '请选择审批人', trigger: 'blur' }]
 })
 const resetForm = (formEl: FormInstance | undefined) => {
   if (!formEl) return
@@ -49,10 +55,28 @@ const addPurchase = (formEl: FormInstance | undefined) => {
   formEl.validate(async valid => {
     if (valid) {
       let res: any = ''
-      let detail = ''
-      purchaseDetailRef.value?.getDetailInfo().forEach((item: any) => {
-        detail += `${item.T_name},${item.T_model},${item.T_spec},${item.T_quantity},${item.T_demand},${item.T_remark},${item.T_state},${item.T_date},${item.T_unit_price},${item.T_amount},${item.T_reference_site}|`
-      })
+      // 获取采购明细数据
+      const detailData = purchaseDetailRef.value?.getDetailInfo() || []
+      // 构建符合后端要求的数据格式
+      const formattedDetail = detailData.map((item: any) => ({
+        T_name: item.T_name,
+        T_bit_number: item.T_bit_number,
+        T_packaging: item.T_packaging,
+        T_model: item.T_model,
+        T_spec: item.T_spec,
+        T_quantity: item.T_quantity,
+        T_demand: item.T_demand,
+        T_unit_price: item.T_unit_price,
+        T_amount: item.T_amount,
+        T_reference_site: item.T_reference_site,
+        T_reference_site_title: item.T_reference_site_title,
+        T_remark: item.T_remark,
+        T_state: item.T_state,
+        T_date: item.T_date
+      }))
+      // 转换为JSON字符串
+      const detail = JSON.stringify(formattedDetail)
+      
       const params = {
         User_tokey,
         ...form.value,
@@ -98,8 +122,25 @@ const purchaseFromOpen = (type: string, row?: any) => {
       // form.value.T_uuid = row.T_uuid_name
       const res: any = await Purchase_Get({ T_id: row.Id })
       if (res.Code === 200) {
-        purchaseDetailRef.value?.setDetailData(res.Data.T_Detail)
+        // 解析JSON字符串为数组对象
+        let detailData = res.Data.T_Detail
+        if (typeof detailData === 'string') {
+          try {
+            detailData = JSON.parse(detailData)
+          } catch (error) {
+            console.error('JSON解析失败:', error)
+            detailData = []
+          }
+        }
+        purchaseDetailRef.value?.setDetailData(detailData || [])
       }
+    } else {
+      // 新增时设置默认申请日期为当天
+      const today = new Date()
+      const year = today.getFullYear()
+      const month = String(today.getMonth() + 1).padStart(2, '0')
+      const day = String(today.getDate()).padStart(2, '0')
+      form.value.T_date = `${year}-${month}-${day}`
     }
   })
   drawerRef.value?.openDrawer()
@@ -162,15 +203,6 @@ defineExpose({
 				@focus="selectUser"
 			/>
 		</el-form-item>
-
-      <el-form-item label="采购明细:" :label-width="formLabelWidth" prop="T_detail">
-        <PurchaseDetail
-          ref="purchaseDetailRef"
-          :disabled="! (typeTip === 'new' || typeTip === 'edit')"
-          :isShow="purchaseName"
-          :typeTip="typeTip"
-        ></PurchaseDetail>
-      </el-form-item>
 		<el-form-item label="审批人:" :label-width="formLabelWidth" prop="T_approver_name">
 			<el-input
 				:key="Math.random()"
@@ -187,6 +219,49 @@ defineExpose({
 			<el-radio :label="2">已采购</el-radio>
 		</el-radio-group>
 	</el-form-item>
+
+	<el-form-item label="产品名称:" :label-width="formLabelWidth" prop="T_product_name">
+		<el-input
+			v-model="form.T_product_name"
+			:disabled="! (typeTip === 'new' || typeTip === 'edit')"
+			type="text"
+			placeholder="请输入产品名称"
+			class="w-50"
+		/>
+	</el-form-item>
+
+	<el-form-item label="产品数量:" :label-width="formLabelWidth" prop="T_product_quantity">
+		<el-input
+			v-model="form.T_product_quantity"
+			:disabled="! (typeTip === 'new' || typeTip === 'edit')"
+			:min="0"
+			:precision="0"
+			:controls="false"
+			placeholder="请输入产品数量"
+			class="w-50"
+		/>
+	</el-form-item>
+
+	<el-form-item label="产品总价:" :label-width="formLabelWidth" prop="T_total_price">
+		<el-input
+			v-model="form.T_total_price"
+			:disabled="! (typeTip === 'new' || typeTip === 'edit')"
+			:min="0"
+			:precision="2"
+			:controls="false"
+			placeholder="请输入产品总价"
+			class="w-50"
+		/>
+	</el-form-item>
+
+      <el-form-item label="采购明细:" :label-width="formLabelWidth" prop="T_detail">
+        <PurchaseDetail
+          ref="purchaseDetailRef"
+          :disabled="! (typeTip === 'new' || typeTip === 'edit')"
+          :isShow="purchaseName"
+          :typeTip="typeTip"
+        ></PurchaseDetail>
+      </el-form-item>
       <!-- my purchase -->
       <el-form-item label="备注:" :label-width="formLabelWidth" prop="T_remark">
         <el-input

+ 332 - 0
src/views/storehouse/CompanyDeviceStat.vue

@@ -0,0 +1,332 @@
+<script setup lang="ts">
+import {ref, reactive, computed, onMounted, nextTick} from 'vue'
+import {
+	Storehouse_StockOut_Company_Name_List,
+	Storehouse_Company_Device_Statistics,
+	Storehouse_Company_Device_Statistics_Excel
+} from '@/api/storehouse'
+import {useTablePublic} from '@/hooks/useTablePublic'
+import {ElMessage} from 'element-plus'
+
+// 左侧公司列表搜索与滚动
+const {globalStore} = useTablePublic()
+const searchKey = ref('')
+const companyAll = ref<string[]>([])
+
+const fetchCompanies = async () => {
+	const res: any = await Storehouse_StockOut_Company_Name_List({User_tokey: globalStore.GET_User_tokey, T_name: searchKey.value || ''})
+	companyAll.value = res?.Data || []
+}
+
+// 右侧公司设备统计
+interface RowItem {
+	seq?: number
+	out_number?: string
+	product_name?: string
+	device_sn?: string
+	num?: number
+	unit?: string
+	remark?: string
+	return_number?: string
+	return_device_sn?: string
+	return_remark?: string
+}
+
+interface DeviceStatItem {
+	out_total: number
+	return_total: number
+	rows: RowItem[]
+}
+
+const activeCompany = ref('')
+const deviceList = ref<DeviceStatItem[]>([])
+
+// 右表加载状态
+const rightLoading = ref(false)
+
+const flatRows = computed(() => {
+	// 拍平数据,同时将缺省 out_number/product_name 继承为上一条的值,便于合并
+	const result: any[] = []
+	deviceList.value.forEach((g) => {
+		let currentOut = ''
+		let currentProduct = ''
+		g.rows.forEach((r: any) => {
+			if (r.out_number) {
+				currentOut = r.out_number
+				if (r.product_name) currentProduct = r.product_name
+			} else {
+				r.out_number = currentOut
+				if (!r.product_name) r.product_name = currentProduct
+			}
+			if (!r.product_name && currentProduct) r.product_name = currentProduct
+			result.push({...r})
+		})
+	})
+	return result
+})
+
+// 计算单号合并跨度
+const outSpanMap = computed(() => {
+	const map = new Map<number, number>()
+	const list = flatRows.value
+	let i = 0
+	while (i < list.length) {
+		let j = i + 1
+		while (j < list.length && list[j].out_number === list[i].out_number) j++
+		map.set(i, j - i)
+		i = j
+	}
+	return map
+})
+
+// 计算品名在同一单号内的合并跨度
+const productSpanMap = computed(() => {
+	const map = new Map<number, number>()
+	const list = flatRows.value
+	let i = 0
+	while (i < list.length) {
+		let j = i + 1
+		while (j < list.length && list[j].out_number === list[i].out_number) j++ // [i, j) 为同一出库单
+		let start = i
+		while (start < j) {
+			let k = start + 1
+			while (k < j && list[k].product_name === list[start].product_name) k++
+			map.set(start, k - start)
+			start = k
+		}
+		i = j
+	}
+	return map
+})
+
+// 表格合并策略
+const spanMethod = ({ column, rowIndex }: any) => {
+	if (column?.property === 'out_number'|| column?.property === 'remark') {
+		const span = outSpanMap.value.get(rowIndex) || 0
+		return span > 0 ? { rowspan: span, colspan: 1 } : { rowspan: 0, colspan: 0 }
+	}
+	if (column?.property === 'product_name' || column?.property === 'num' || column?.property === 'unit') {
+		const span = productSpanMap.value.get(rowIndex) || 0
+		return span > 0 ? { rowspan: span, colspan: 1 } : { rowspan: 0, colspan: 0 }
+	}
+	return { rowspan: 1, colspan: 1 }
+}
+
+// 退回 SN 行:仅设备编号高亮,行本身不变
+
+const outTotal = computed(() => deviceList.value.reduce((s, i) => s + (i.out_total || 0), 0))
+const returnTotal = computed(() => deviceList.value.reduce((s, i) => s + (i.return_total || 0), 0))
+
+// 右侧滚动加载(对拍平后的行做前端增量渲染)
+const rightDisplayCount = ref(80)
+const rightPageSize = 80
+const rightVisibleRows = computed(() => flatRows.value.slice(0, rightDisplayCount.value))
+const canLoadMoreRight = computed(() => rightDisplayCount.value < flatRows.value.length)
+const onRightScroll = (e: Event) => {
+	const el = e.target as HTMLElement
+	if (!el) return
+	if (el.scrollTop + el.clientHeight >= el.scrollHeight - 10 && canLoadMoreRight.value) {
+		rightDisplayCount.value = Math.min(rightDisplayCount.value + rightPageSize, flatRows.value.length)
+	}
+}
+
+const fetchDeviceStats = async (company?: string) => {
+	if (!company) return
+	try {
+		rightLoading.value = true
+		const res: any = await Storehouse_Company_Device_Statistics({User_tokey: globalStore.GET_User_tokey, T_company_name: company})
+		deviceList.value = Array.isArray(res?.Data) ? res.Data : []
+		// 重置右侧滚动加载
+		rightDisplayCount.value = rightPageSize
+	} finally {
+		rightLoading.value = false
+	}
+}
+
+const onSelectCompany = (name: string) => {
+	activeCompany.value = name
+	fetchDeviceStats(name)
+}
+
+// 导出
+const exporting = ref(false)
+const exportExcel = async () => {
+	if (!activeCompany.value) {
+		ElMessage.warning('请选择公司后再导出')
+		return
+	}
+	try {
+		exporting.value = true
+		const response: any = await Storehouse_Company_Device_Statistics_Excel({
+			User_tokey: globalStore.GET_User_tokey,
+			T_company_name: activeCompany.value
+		})
+		// 兼容两种返回:1) blob 文件;2) json { Code, Data: 'url' }
+		if (response instanceof Blob) {
+			try {
+				const text = await response.text()
+				const json = JSON.parse(text)
+				if (json?.Data) {
+					window.open(json.Data)
+				} else {
+					// 非 JSON,按文件下载
+					const blob = new Blob([text], {type: 'application/vnd.ms-excel;charset=utf8'})
+					const url = window.URL.createObjectURL(blob)
+					const a = document.createElement('a')
+					a.href = url
+					const now = new Date()
+					const ts = `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}_${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}`
+					a.download = `公司设备借出退回统计_${activeCompany.value}_${ts}.xlsx`
+					document.body.appendChild(a)
+					a.click()
+					a.remove()
+					window.URL.revokeObjectURL(url)
+				}
+			} catch (_) {
+				const url = window.URL.createObjectURL(response)
+				const a = document.createElement('a')
+				a.href = url
+				const now = new Date()
+				const ts = `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}_${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}`
+				a.download = `公司设备借出退回统计_${activeCompany.value}_${ts}.xlsx`
+				document.body.appendChild(a)
+				a.click()
+				a.remove()
+				window.URL.revokeObjectURL(url)
+			}
+		} else if (response?.Code === 200 && response?.Data) {
+			window.open(response.Data)
+		} else {
+			ElMessage.error('导出失败:未知返回格式')
+		}
+		ElMessage.success('导出成功')
+	} catch (e) {
+		ElMessage.error('导出失败,请稍后重试')
+	} finally {
+		exporting.value = false
+	}
+}
+
+onMounted(() => {
+	fetchCompanies()
+})
+</script>
+
+<template>
+	<div class="company-device-stat">
+		<div class="left-list">
+			<div class="left-header">
+				<h3 class="title">公司名称</h3>
+				<el-input v-model="searchKey" placeholder="按公司名称搜索" clearable @change="fetchCompanies" />
+			</div>
+			<div class="company-scroll">
+				<el-empty v-if="companyAll.length === 0" description="无数据" />
+				<el-menu :default-active="activeCompany" class="company-menu" :collapse="false">
+					<el-menu-item v-for="name in companyAll" :key="name" :index="name" @click="onSelectCompany(name)">
+						{{ name.trim() }}
+					</el-menu-item>
+				</el-menu>
+			</div>
+		</div>
+
+		<div class="right-content" v-if="activeCompany">
+			<div class="toolbar">
+				<h3 class="title">{{ activeCompany }} 设备借出/退回统计</h3>
+				<div class="actions">
+					<el-button type="success" :loading="exporting" @click="exportExcel">导出</el-button>
+				</div>
+			</div>
+			<el-card class="box-card">
+				<div class="stat-info">
+					<span>借出总数:<b>{{ outTotal }}</b></span>
+					<span>退回总数:<b>{{ returnTotal }}</b></span>
+				</div>
+                <div class="right-table-scroll" @scroll="onRightScroll">
+                <el-table :data="rightVisibleRows" v-loading="rightLoading" style="width: 100%" :span-method="spanMethod" size="small">
+					<el-table-column type="index" label="序号" width="60" align="center" />
+					<el-table-column prop="out_number" label="出库单号" width="160" align="center" show-overflow-tooltip />
+					<el-table-column prop="product_name" label="品名" width="220" align="center" show-overflow-tooltip />
+                    <el-table-column prop="device_sn" label="设备SN" width="180" align="center" show-overflow-tooltip>
+                        <template #default="{ row }">
+                            <span :class="row.return_device_sn ? 'sn-danger' : ''">{{ row.device_sn }}</span>
+                        </template>
+                    </el-table-column>
+					<el-table-column prop="num" label="数量" width="80" align="center" />
+					<el-table-column prop="unit" label="单位" width="80" align="center" />
+					<el-table-column prop="remark" label="备注" width="160" align="center" show-overflow-tooltip />
+					<el-table-column prop="return_number" label="退回单号" width="160" align="center" show-overflow-tooltip />
+					<el-table-column prop="return_device_sn" label="退回SN" width="180" align="center" show-overflow-tooltip />
+					<el-table-column prop="return_remark" label="退回备注" width="160" align="center" show-overflow-tooltip />
+					<template #empty>
+						<el-empty description="暂无数据" />
+					</template>
+				</el-table>
+				<div v-if="canLoadMoreRight" class="right-loading-more">下拉加载更多...</div>
+				</div>
+			</el-card>
+		</div>
+	</div>
+</template>
+
+<style scoped lang="scss">
+@import '@/styles/var.scss';
+.company-device-stat {
+	height: 100%;
+	display: flex;
+	overflow: hidden;
+	.title {
+		width: 100%;
+		text-align: center;
+		line-height: 1.7em;
+		font-size: 20px;
+		color: #707b84;
+	}
+	.left-list {
+		@include f-direction;
+		width: 290px;
+		z-index: 1;
+		.left-header { padding: 2px; }
+			.company-scroll {
+			height: calc(100% - 90px);
+			overflow: auto;
+			.company-menu { width: 100%; border-right: none; }
+			:deep(.el-menu-item){
+				line-height: 40px;
+				height: 40px;
+				padding: 0 15px;
+				color: var(--el-text-color-regular);
+			}
+			:deep(.el-menu-item:hover){
+				background-color: var(--el-color-primary-light-9);
+			}
+			:deep(.el-menu-item.is-active){
+				// background-color: var(--el-color-primary-light-7);
+				color: var(--el-color-primary);
+				font-weight: 600;
+			}
+		}
+	}
+	.right-content {
+		@include f-direction;
+		z-index: 0;
+		margin-left: 12px;
+		width: calc(100% - 290px);
+		.toolbar {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin-bottom: 8px;
+		}
+		.box-card { border-radius: 8px; }
+		.stat-info { display: flex; gap: 16px; padding: 6px 0 12px 0; }
+		/* 右侧表格行距再小一些:对 tbody 行强制最小高度与内边距 */
+		:deep(.el-table){ --el-table-row-height: 26px; }
+		:deep(.el-table__body tr){ height: 24px; }
+		:deep(.el-table .cell){ padding: 2px 6px; line-height: 20px; }
+		.right-table-scroll { max-height: calc(100vh - 260px); overflow: auto; }
+		.right-loading-more { text-align: center; color: #999; padding: 8px 0; }
+        /* 仅退回SN的设备编号标红 */
+        :deep(.sn-danger){ color: var(--el-color-danger); }
+	}
+}
+</style>

+ 43 - 7
src/views/storehouse/inventory/InStorageEdit.vue

@@ -8,10 +8,10 @@ import { InStoreageFormType } from '@/hooks/useDepot'
 import InStorageProduct from './InStorageProduct.vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { Delete, CirclePlus } from '@element-plus/icons-vue'
-import { Storehouse_StockIn_Edit,Storehouse_StockIn_Get } from '@/api/storehouse/index'
+import { Storehouse_StockIn_Edit,Storehouse_StockIn_Get, Storehouse_StockOut_Company_Name_List, Storehouse_StockOut_Project_List } from '@/api/storehouse/index'
 import ImageCom from '@/components/Image/index.vue'
 import ReceiveUser from "@/views/storehouse/outStock/receiveUser.vue";
-
+import {useRemoteSearch} from '@/hooks/useRemoteSearch'
 
 const tableData = ref<any[]>([])
 const globalStore = GlobalStore()
@@ -21,6 +21,16 @@ const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 const drawerSnEditRef = ref<InstanceType<typeof InStorageEditSn> | null>(null)
 const drawerProductRef = ref<InstanceType<typeof InStorageProduct> | null>(null)
 
+// 使用远程搜索composable
+const {
+	stockOutCompanyNameOptions,
+	isStockOutCompanyNameLoading,
+	queryStockOutCompanyNameAsync,
+	stockOutProjectOptions,
+	isStockOutProjectLoading,
+	queryStockOutProjectAsync
+} = useRemoteSearch()
+
 const form = reactive<InStoreageFormType>({
   T_number: '',
   T_depot_id: '',
@@ -29,6 +39,7 @@ const form = reactive<InStoreageFormType>({
   T_date: '',
   T_remark: '',
   T_project: '',
+  T_company_name: '',
   T_return_user: '',
   T_return_user_name: '',
   T_batch_number: ''
@@ -49,7 +60,7 @@ const rules = reactive<FormRules>({
   T_product: [{ validator: validate_T_product, trigger: 'blur' }],
   T_depot_id: [{ required: true, message: '请选择仓库', trigger: 'blur' }],
   T_date: [{ required: true, message: '请选择入库日期', trigger: 'blur' }],
-  T_project: [{ required: true, message: '请选择退库项目', trigger: 'blur' }]
+	T_company_name: [{required: true, message: '请选择公司名称', trigger: 'blur'}],
 })
 
 const columns = [
@@ -277,6 +288,7 @@ const getReturnUserInfo = ({T_uuid, T_name}: { T_uuid: string; T_name: string })
 	form.T_return_user = T_uuid
 }
 
+
 defineExpose({
   openDrawer,getStorehouseContractGet,form
 })
@@ -381,13 +393,37 @@ defineExpose({
             </template>
           </el-table>
         </el-form-item>
+		  <el-form-item v-if="form.T_type===2" label="公司名称:" :label-width="formLabelWidth" prop="T_company_name">
+			  <el-select
+				  v-model="form.T_company_name"
+				  filterable
+				  clearable
+				  remote
+				  reserve-keyword
+				  placeholder="请输入公司名称搜索"
+				  remote-show-suffix
+				  :remote-method="queryStockOutCompanyNameAsync"
+				  :loading="isStockOutCompanyNameLoading"
+				  class="w-50"
+			  >
+				  <el-option v-for="item in stockOutCompanyNameOptions" :key="item.value" :label="item.label" :value="item.value" />
+			  </el-select>
+		  </el-form-item>
 		  <el-form-item v-if="form.T_type===2" label="退库项目:" :label-width="formLabelWidth" prop="T_project">
-			  <el-input
+			  <el-select
 				  v-model="form.T_project"
-				  type="text"
+				  filterable
+				  clearable
+				  remote
+				  reserve-keyword
+				  placeholder="请输入退库项目搜索"
+				  remote-show-suffix
+				  :remote-method="queryStockOutProjectAsync"
+				  :loading="isStockOutProjectLoading"
 				  class="w-50"
-				  placeholder="请输入退库项目"
-			  />
+			  >
+				  <el-option v-for="item in stockOutProjectOptions" :key="item.value" :label="item.label" :value="item.value" />
+			  </el-select>
 		  </el-form-item>
 		  <el-form-item v-if="form.T_type===2" label="退库人:" :label-width="formLabelWidth" prop="T_return_user_name">
 			  <el-input v-model="form.T_return_user_name" placeholder="请选择退库人" class="w-50"

+ 51 - 13
src/views/storehouse/inventory/InStorageForm.vue

@@ -8,9 +8,10 @@ import Drawer from '@/components/Drawer/index.vue'
 import {InStoreageFormType} from '@/hooks/useDepot'
 import InStorageProduct from './InStorageProduct.vue'
 import {CirclePlus, Delete} from '@element-plus/icons-vue'
-import {Storehouse_StockIn_Add,Storehouse_StockIn_Generate_Number} from '@/api/storehouse/index'
+import {Storehouse_StockIn_Add,Storehouse_StockIn_Generate_Number, Storehouse_StockOut_Company_Name_List, Storehouse_StockOut_Project_List} from '@/api/storehouse/index'
 import ImageCom from '@/components/Image/index.vue'
 import ReceiveUser from "@/views/storehouse/outStock/receiveUser.vue";
+import {useRemoteSearch} from '@/hooks/useRemoteSearch'
 
 const tableData = ref<any[]>([])
 const globalStore = GlobalStore()
@@ -25,6 +26,16 @@ const isSubmitting = ref(false)
 const isGeneratingNumber = ref(false)
 let generateNumberTimer: NodeJS.Timeout | null = null
 
+// 使用远程搜索composable
+const {
+	stockOutCompanyNameOptions,
+	isStockOutCompanyNameLoading,
+	queryStockOutCompanyNameAsync,
+	stockOutProjectOptions,
+	isStockOutProjectLoading,
+	queryStockOutProjectAsync
+} = useRemoteSearch()
+
 const data: any = reactive({
 	optionsType: [{id: 1, T_name: '入库'}, {id: 2, T_name: '退库'}]
 })
@@ -37,6 +48,7 @@ const form = reactive<InStoreageFormType>({
 	T_date: '',
 	T_remark: '',
 	T_project: '',
+	T_company_name: '',
 	T_return_user: '',
 	T_return_user_name: '',
 	T_batch_number: ''
@@ -57,7 +69,7 @@ const rules = reactive<FormRules>({
 	T_depot_id: [{required: true, message: '请选择仓库', trigger: 'blur'}],
 	T_date: [{required: true, message: '请选择入库日期', trigger: 'blur'}],
 	T_type: [{required: true, message: '请选择入库类型', trigger: 'blur'}],
-	T_project: [{required: true, message: '请选择退库项目', trigger: 'blur'}]
+	T_company_name: [{required: true, message: '请选择公司名称', trigger: 'blur'}]
 })
 
 const columns = [
@@ -302,6 +314,7 @@ const getReturnUserInfo = ({T_uuid, T_name}: { T_uuid: string; T_name: string })
 	form.T_return_user = T_uuid
 }
 
+
 /**
  * 入库调用
  */
@@ -445,17 +458,42 @@ defineExpose({
 								<span style="margin-left: 6px">添加产品</span>
 							</el-button>
 						</template>
-					</el-table>
-				</el-form-item>
-				<el-form-item v-if="form.T_type===2" label="退库项目:" :label-width="formLabelWidth" prop="T_project">
-					<el-input
-						v-model="form.T_project"
-						type="text"
-						class="w-50"
-						placeholder="请输入退库项目"
-					/>
-				</el-form-item>
-				<el-form-item v-if="form.T_type===2" label="退库人:" :label-width="formLabelWidth" prop="T_return_user_name">
+			</el-table>
+		</el-form-item>
+		
+		<el-form-item v-if="form.T_type===2" label="公司名称:" :label-width="formLabelWidth" prop="T_company_name">
+			<el-select
+				v-model="form.T_company_name"
+				filterable
+				clearable
+				remote
+				reserve-keyword
+				placeholder="请输入公司名称搜索"
+				remote-show-suffix
+				:remote-method="queryStockOutCompanyNameAsync"
+				:loading="isStockOutCompanyNameLoading"
+				class="w-50"
+			>
+				<el-option v-for="item in stockOutCompanyNameOptions" :key="item.value" :label="item.label" :value="item.value" />
+			</el-select>
+		</el-form-item>
+		<el-form-item v-if="form.T_type===2" label="退库项目:" :label-width="formLabelWidth" prop="T_project">
+			<el-select
+				v-model="form.T_project"
+				filterable
+				clearable
+				remote
+				reserve-keyword
+				placeholder="请输入退库项目搜索"
+				remote-show-suffix
+				:remote-method="queryStockOutProjectAsync"
+				:loading="isStockOutProjectLoading"
+				class="w-50"
+			>
+				<el-option v-for="item in stockOutProjectOptions" :key="item.value" :label="item.label" :value="item.value" />
+			</el-select>
+		</el-form-item>
+		<el-form-item v-if="form.T_type===2" label="退库人:" :label-width="formLabelWidth" prop="T_return_user_name">
 					<el-input v-model="form.T_return_user_name" placeholder="请选择退库人" class="w-50"
 							  @focus="selectReturnUser"/>
 				</el-form-item>

+ 41 - 6
src/views/storehouse/inventory/inStockApply/InStorageEdit.vue

@@ -11,6 +11,7 @@ import { Delete, CirclePlus } from '@element-plus/icons-vue'
 import { Storehouse_StockIn_Apply_Edit,Storehouse_StockIn_Get } from '@/api/storehouse/index'
 import ImageCom from '@/components/Image/index.vue'
 import ReceiveUser from "@/views/storehouse/outStock/receiveUser.vue";
+import {useRemoteSearch} from '@/hooks/useRemoteSearch'
 
 
 const tableData = ref<any[]>([])
@@ -21,6 +22,16 @@ const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 const drawerSnEditRef = ref<InstanceType<typeof InStorageEditSn> | null>(null)
 const drawerProductRef = ref<InstanceType<typeof InStorageProduct> | null>(null)
 
+// 使用远程搜索composable
+const {
+	stockOutCompanyNameOptions,
+	isStockOutCompanyNameLoading,
+	queryStockOutCompanyNameAsync,
+	stockOutProjectOptions,
+	isStockOutProjectLoading,
+	queryStockOutProjectAsync
+} = useRemoteSearch()
+
 const form = reactive<InStoreageFormType>({
   T_number: '',
   T_depot_id: '',
@@ -29,6 +40,7 @@ const form = reactive<InStoreageFormType>({
   T_date: '',
   T_remark: '',
   T_project: '',
+  T_company_name: '',
   T_return_user: '',
   T_return_user_name: '',
   T_batch_number: ''
@@ -58,7 +70,7 @@ const rules = reactive<FormRules>({
   T_product: [{ validator: validate_T_product, trigger: 'blur' }],
   T_depot_id: [{ required: true, message: '请选择仓库', trigger: 'blur' }],
   T_date: [{ required: true, message: '请选择退库日期', trigger: 'blur' }],
-  T_project: [{ required: true, message: '请选择退库项目', trigger: 'blur' }],
+	T_company_name: [{required: true, message: '请选择公司名称', trigger: 'blur'}],
   T_return_user: [{validator: validate_T_return_user, trigger: 'change'}]
 })
 
@@ -280,7 +292,6 @@ const getReturnUserInfo = ({T_uuid, T_name}: { T_uuid: string; T_name: string })
 	form.T_return_user_name = T_name
 	form.T_return_user = T_uuid
 }
-
 /**
  * 修改 openDrawer 接收参数
  */
@@ -372,13 +383,37 @@ defineExpose({
             </template>
           </el-table>
         </el-form-item>
+        <el-form-item label="公司名称:" :label-width="formLabelWidth" prop="T_company_name">
+          <el-select
+            v-model="form.T_company_name"
+            filterable
+            clearable
+            remote
+            reserve-keyword
+            placeholder="请输入公司名称搜索"
+            remote-show-suffix
+            :remote-method="queryStockOutCompanyNameAsync"
+            :loading="isStockOutCompanyNameLoading"
+            class="w-50"
+          >
+            <el-option v-for="item in stockOutCompanyNameOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
 		  <el-form-item label="退库项目:" :label-width="formLabelWidth" prop="T_project">
-			  <el-input
+			  <el-select
 				  v-model="form.T_project"
-				  type="text"
+				  filterable
+				  clearable
+				  remote
+				  reserve-keyword
+				  placeholder="请输入退库项目搜索"
+				  remote-show-suffix
+				  :remote-method="queryStockOutProjectAsync"
+				  :loading="isStockOutProjectLoading"
 				  class="w-50"
-				  placeholder="请输入退库项目"
-			  />
+			  >
+				  <el-option v-for="item in stockOutProjectOptions" :key="item.value" :label="item.label" :value="item.value" />
+			  </el-select>
 		  </el-form-item>
 		  <el-form-item v-if="showReturnUserField" label="退库人:" :label-width="formLabelWidth" prop="T_return_user">
 			  <el-input v-model="form.T_return_user_name" placeholder="请选择退库人" class="w-50"

+ 42 - 6
src/views/storehouse/inventory/inStockApply/InStorageForm.vue

@@ -11,6 +11,7 @@ import {CirclePlus, Delete} from '@element-plus/icons-vue'
 import {Storehouse_StockIn_Apply,Storehouse_StockIn_Generate_Number} from '@/api/storehouse/index'
 import ImageCom from '@/components/Image/index.vue'
 import ReceiveUser from "@/views/storehouse/outStock/receiveUser.vue";
+import {useRemoteSearch} from '@/hooks/useRemoteSearch'
 
 const tableData = ref<any[]>([])
 const globalStore = GlobalStore()
@@ -25,8 +26,18 @@ const isSubmitting = ref(false)
 const isGeneratingNumber = ref(false)
 let generateNumberTimer: NodeJS.Timeout | null = null
 
+// 使用远程搜索composable
+const {
+	stockOutCompanyNameOptions,
+	isStockOutCompanyNameLoading,
+	queryStockOutCompanyNameAsync,
+	stockOutProjectOptions,
+	isStockOutProjectLoading,
+	queryStockOutProjectAsync
+} = useRemoteSearch()
+
 const data: any = reactive({
-	optionsType: [{id: 1, T_name: '退库'}, {id: 2, T_name: '退库'}]
+	optionsType: [{id: 1, T_name: '库'}, {id: 2, T_name: '退库'}]
 })
 
 const form = reactive<InStoreageFormType>({
@@ -37,6 +48,7 @@ const form = reactive<InStoreageFormType>({
 	T_date: '',
 	T_remark: '',
 	T_project: '',
+	T_company_name: '',
 	T_return_user: '',
 	T_return_user_name: '',
 	T_batch_number: ''
@@ -64,7 +76,7 @@ const rules = reactive<FormRules>({
 	T_depot_id: [{required: true, message: '请选择仓库', trigger: 'blur'}],
 	T_date: [{required: true, message: '请选择退库日期', trigger: 'blur'}],
 	T_type: [{required: true, message: '请选择退库类型', trigger: 'blur'}],
-	T_project: [{required: true, message: '请选择退库项目', trigger: 'blur'}],
+	T_company_name: [{required: true, message: '请选择公司名称', trigger: 'blur'}],
 	T_return_user: [{validator: validate_T_return_user, trigger: 'change'}]
 })
 
@@ -407,13 +419,37 @@ defineExpose({
 						</template>
 					</el-table>
 				</el-form-item>
+				<el-form-item  label="公司名称:" :label-width="formLabelWidth" prop="T_company_name">
+					<el-select
+						v-model="form.T_company_name"
+						filterable
+						clearable
+						remote
+						reserve-keyword
+						placeholder="请输入公司名称搜索"
+						remote-show-suffix
+						:remote-method="queryStockOutCompanyNameAsync"
+						:loading="isStockOutCompanyNameLoading"
+						class="w-50"
+					>
+						<el-option v-for="item in stockOutCompanyNameOptions" :key="item.value" :label="item.label" :value="item.value" />
+					</el-select>
+				</el-form-item>
 				<el-form-item  label="退库项目:" :label-width="formLabelWidth" prop="T_project">
-					<el-input
+					<el-select
 						v-model="form.T_project"
-						type="text"
+						filterable
+						clearable
+						remote
+						reserve-keyword
+						placeholder="请输入退库项目搜索"
+						remote-show-suffix
+						:remote-method="queryStockOutProjectAsync"
+						:loading="isStockOutProjectLoading"
 						class="w-50"
-						placeholder="请输入退库项目"
-					/>
+					>
+						<el-option v-for="item in stockOutProjectOptions" :key="item.value" :label="item.label" :value="item.value" />
+					</el-select>
 				</el-form-item>
 				<el-form-item v-if="showReturnUserField" label="退库人:" :label-width="formLabelWidth" prop="T_return_user">
 					<el-input v-model="form.T_return_user_name" placeholder="请选择退库人" class="w-50"

+ 61 - 15
src/views/storehouse/inventory/inStockApply/InStorageWarehouse.vue

@@ -8,10 +8,10 @@ import { InStoreageFormType } from '@/hooks/useDepot'
 import InStorageProduct from './InStorageProduct.vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { Delete, CirclePlus } from '@element-plus/icons-vue'
-import { Storehouse_StockIn_Apply_Warehouse,Storehouse_StockIn_Get } from '@/api/storehouse/index'
+import { Storehouse_StockIn_Apply_Warehouse,Storehouse_StockIn_Get, Storehouse_StockOut_Company_Name_List, Storehouse_StockOut_Project_List } from '@/api/storehouse/index'
 import ImageCom from '@/components/Image/index.vue'
 import ReceiveUser from "@/views/storehouse/outStock/receiveUser.vue";
-
+import {useRemoteSearch} from '@/hooks/useRemoteSearch'
 
 const tableData = ref<any[]>([])
 const globalStore = GlobalStore()
@@ -21,6 +21,16 @@ const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 const drawerSnEditRef = ref<InstanceType<typeof InStorageEditSn> | null>(null)
 const drawerProductRef = ref<InstanceType<typeof InStorageProduct> | null>(null)
 
+// 使用远程搜索composable
+const {
+	stockOutCompanyNameOptions,
+	isStockOutCompanyNameLoading,
+	queryStockOutCompanyNameAsync,
+	stockOutProjectOptions,
+	isStockOutProjectLoading,
+	queryStockOutProjectAsync
+} = useRemoteSearch()
+
 // 获取今天的日期
 const getTodayDate = () => {
   const today = new Date()
@@ -40,7 +50,8 @@ const form = reactive<InStoreageFormType>({
   T_project: '',
   T_return_user: '',
   T_return_user_name: '',
-  T_batch_number: ''
+  T_batch_number: '',
+  T_company_name: ''
 })
 
 // 确保退库日期不为空时设置为今天
@@ -63,7 +74,7 @@ const rules = reactive<FormRules>({
   T_product: [{ validator: validate_T_product, trigger: 'blur' }],
   T_depot_id: [{ required: true, message: '请选择仓库', trigger: 'blur' }],
   T_date: [{ required: true, message: '请选择退库日期', trigger: 'blur' }],
-  T_project: [{ required: true, message: '请选择退库项目', trigger: 'blur' }]
+  T_company_name: [{required: true, message: '请选择公司名称', trigger: 'blur'}]
 })
 
 const columns = [
@@ -205,6 +216,7 @@ const getStorehouseContractGet = async (Id:any) => {
             T_img:item.T_product_img,
             T_model:item.T_number,
             T_name: item.T_product_name,
+            T_company_name: item.T_company_name,
             count: item.T_num,
             T_relation_sn:item.T_product_relation_sn,
             T_spec: item.T_product_spec,
@@ -213,6 +225,14 @@ const getStorehouseContractGet = async (Id:any) => {
         })
     })
     
+    // 填充表单数据
+    if (res.Data) {
+      form.T_company_name = res.Data.T_company_name || ''
+      form.T_project = res.Data.T_project || ''
+      form.T_remark = res.Data.T_remark || ''
+      form.T_type = res.Data.T_type || 2
+    }
+    
     // 确保退库日期不为空
     if (!form.T_date || form.T_date === '') {
       form.T_date = getTodayDate()
@@ -308,6 +328,7 @@ const getReturnUserInfo = ({T_uuid, T_name}: { T_uuid: string; T_name: string })
 	form.T_return_user = T_uuid
 }
 
+
 defineExpose({
   openDrawer,getStorehouseContractGet,form
 })
@@ -408,17 +429,42 @@ defineExpose({
                 <el-icon><Plus /></el-icon><span style="margin-left: 6px">添加产品</span>
               </el-button>
             </template>
-          </el-table>
-        </el-form-item>
-		  <el-form-item v-if="form.T_type===2" label="退库项目:" :label-width="formLabelWidth" prop="T_project">
-			  <el-input
-				  v-model="form.T_project"
-				  type="text"
-				  class="w-50"
-				  placeholder="请输入退库项目"
-			  />
-		  </el-form-item>
-		  <el-form-item v-if="form.T_type===2" label="退库人:" :label-width="formLabelWidth" prop="T_return_user_name">
+		  </el-table>
+		</el-form-item>
+		<el-form-item v-if="form.T_type===2" label="公司名称:" :label-width="formLabelWidth" prop="T_company_name">
+			<el-select
+				v-model="form.T_company_name"
+				filterable
+				clearable
+				remote
+				reserve-keyword
+				placeholder="请输入公司名称搜索"
+				remote-show-suffix
+				:remote-method="queryStockOutCompanyNameAsync"
+				:loading="isStockOutCompanyNameLoading"
+				class="w-50"
+			>
+				<el-option v-for="item in stockOutCompanyNameOptions" :key="item.value" :label="item.label" :value="item.value" />
+			</el-select>
+		</el-form-item>
+		<el-form-item v-if="form.T_type===2" label="退库项目:" :label-width="formLabelWidth" prop="T_project">
+			<el-select
+				v-model="form.T_project"
+				filterable
+				clearable
+				remote
+				reserve-keyword
+				placeholder="请输入退库项目搜索"
+				remote-show-suffix
+				:remote-method="queryStockOutProjectAsync"
+				:loading="isStockOutProjectLoading"
+				class="w-50"
+			>
+				<el-option v-for="item in stockOutProjectOptions" :key="item.value" :label="item.label" :value="item.value" />
+			</el-select>
+		</el-form-item>
+
+		<el-form-item v-if="form.T_type===2" label="退库人:" :label-width="formLabelWidth" prop="T_return_user_name">
 			  <el-input v-model="form.T_return_user_name" placeholder="请选择退库人" class="w-50"
 						@focus="selectReturnUser"/>
 		  </el-form-item>

+ 2 - 2
src/views/storehouse/outStock/modules/InStorageEdit.vue

@@ -316,8 +316,8 @@ defineExpose({
 					<el-input v-model="form.T_project" type="text" placeholder="关联项目" class="w-50"/>
 				</el-form-item>
 
-				<el-form-item label="经办人:" :label-width="formLabelWidth" prop="T_receive_name">
-					<el-input v-model="form.T_receive_name" placeholder="请选择经办人" class="w-50"
+				<el-form-item label="领取人:" :label-width="formLabelWidth" prop="T_receive_name">
+					<el-input v-model="form.T_receive_name" placeholder="请选择领取人" class="w-50"
 							  @focus="selectApprover"/>
 				</el-form-item>
 

BIN
采购申请模版.xlsx