From 73d112ddc9bdfeb7eaaf3b7308cfb4dbeb470c89 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 07 八月 2026 17:28:10 +0800
Subject: [PATCH] 天津_意博凯信 1.备件管理-新增独立备件出库功能入口,支持直接完成备件领用出库操作。 2.设备保养-支持按日期分组折叠展示,分组条目可展开查看明细;单条明细行末尾增加【保养完成】操作按钮,可单独办结单条保养任务;折叠汇总状态下,新增【一键保养完成】批量操作按钮,快速办结当前分组全部保养项。 3.设备巡检-按日期分组折叠展示,分组支持展开查看明细;每条巡检明细后增设【巡检完成】按钮,支持单独办结单条巡检记录;分组折叠未展开状态下,提供【一键巡检完成】批量操作,统一办结该分组下所有巡检任务。 4.增值税对比-优化数据展示逻辑,列表展示各订单增值税明细;页面右侧新增柱状可视化图表,直观对比进销项增值税数据。 5.管理驾驶舱-指标修改:将顶部「销售产品数」调整为「销售订单数」;时间筛选:页面左上角新增日期范围下拉筛选控件,所有统计指标标注当前筛选时段;

---
 src/views/equipmentManagement/spareParts/index.vue |  931 +++++++++++++++++++++++++++++++++------------------------
 1 files changed, 535 insertions(+), 396 deletions(-)

diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 8abe35d..c25736f 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -1,26 +1,26 @@
 <template>
-  <div class="spare-part-category">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
-      <el-tab-pane label="澶囦欢鍒楄〃" name="list">
-        <div class="search_form">
-          <el-form :inline="true" :model="queryParams" class="search-form">
-            <el-form-item label="澶囦欢鍚嶇О">
-              <el-input
-                v-model="queryParams.name"
-                placeholder="璇疯緭鍏ュ浠跺悕绉�"
-                clearable
-                style="width: 240px"
-              />
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
-              <el-button @click="resetQuery">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-          <div>
-            <el-button type="primary" @click="addCategory">鏂板</el-button>
-          </div>
-        </div>
+	<div class="spare-part-category">
+		<el-tabs v-model="activeTab" @tab-change="handleTabChange">
+			<el-tab-pane label="澶囦欢鍒楄〃" name="list">
+				<div class="search_form">
+					<el-form :inline="true" :model="queryParams" class="search-form">
+						<el-form-item label="澶囦欢鍚嶇О">
+							<el-input
+								v-model="queryParams.name"
+								placeholder="璇疯緭鍏ュ浠跺悕绉�"
+								clearable
+								style="width: 240px"
+							/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+							<el-button @click="resetQuery">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+					<div>
+						<el-button type="primary" @click="addCategory">鏂板</el-button>
+					</div>
+				</div>
 				<div class="table_list">
 					<PIMTable
 						rowKey="id"
@@ -36,83 +36,87 @@
 						</template>
 					</PIMTable>
 				</div>
-
-        <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
-          <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-            <el-form-item label="璁惧" prop="deviceLedgerIds">
-              <el-select
-                v-model="form.deviceLedgerIds"
-                placeholder="璇烽�夋嫨璁惧"
-                filterable
-                default-first-option
-                :reserve-keyword="false"
-                multiple
-                style="width: 100%"
-              >
-                <el-option
-                  v-for="(item, index) in deviceOptions"
-                  :key="index"
-                  :label="item.deviceName"
-                  :value="item.id"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="澶囦欢鍚嶇О" prop="name">
-              <el-input v-model="form.name"></el-input>
-            </el-form-item>
-            <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
-              <el-input v-model="form.sparePartsNo"></el-input>
-            </el-form-item>
-            <el-form-item label="鏁伴噺" prop="quantity">
-              <el-input type="number" v-model="form.quantity"></el-input>
-            </el-form-item>
-            <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
-                <el-option label="姝e父" value="姝e父"></el-option>
-                <el-option label="绂佺敤" value="绂佺敤"></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="鎻忚堪" prop="description">
-              <el-input v-model="form.description"></el-input>
-            </el-form-item>
-            <el-form-item label="浠锋牸" prop="price">
-              <el-input-number
-                v-model="form.price"
-                placeholder="璇疯緭鍏ヤ环鏍�"
-                :min="0"
-                :step="0.01"
-                :precision="2"
-                style="width: 100%"
-              ></el-input-number>
-            </el-form-item>
-          </el-form>
-          <template #footer>
+				
+				<el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
+					<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+						<el-form-item label="璁惧" prop="deviceLedgerIds">
+							<el-select
+								v-model="form.deviceLedgerIds"
+								placeholder="璇烽�夋嫨璁惧"
+								filterable
+								default-first-option
+								:reserve-keyword="false"
+								multiple
+								style="width: 100%"
+							>
+								<el-option
+									v-for="(item, index) in deviceOptions"
+									:key="index"
+									:label="item.deviceName"
+									:value="item.id"
+								></el-option>
+							</el-select>
+						</el-form-item>
+						<el-form-item label="澶囦欢鍚嶇О" prop="name">
+							<el-input v-model="form.name"></el-input>
+						</el-form-item>
+						<el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
+							<el-input v-model="form.sparePartsNo"></el-input>
+						</el-form-item>
+						<el-form-item label="鏁伴噺" prop="quantity">
+							<el-input type="number" v-model="form.quantity"></el-input>
+						</el-form-item>
+						<el-form-item label="鐘舵��" prop="status">
+							<el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
+								<el-option label="姝e父" value="姝e父"></el-option>
+								<el-option label="绂佺敤" value="绂佺敤"></el-option>
+							</el-select>
+						</el-form-item>
+						<el-form-item label="鎻忚堪" prop="description">
+							<el-input v-model="form.description"></el-input>
+						</el-form-item>
+						<el-form-item label="浠锋牸" prop="price">
+							<el-input-number
+								v-model="form.price"
+								placeholder="璇疯緭鍏ヤ环鏍�"
+								:min="0"
+								:step="0.01"
+								:precision="2"
+								style="width: 100%"
+							></el-input-number>
+						</el-form-item>
+					</el-form>
+					<template #footer>
             <span class="dialog-footer">
               <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
               <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
             </span>
-          </template>
-        </el-dialog>
-      </el-tab-pane>
-
-      <el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
-        <div class="search_form">
-          <el-form :inline="true" :model="usageQuery" class="search-form">
-            <el-form-item label="澶囦欢鍚嶇О">
-              <el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
-            </el-form-item>
-            <el-form-item label="鏉ユ簮">
-              <el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
-                <el-option label="缁翠慨" :value="0" />
-                <el-option label="淇濆吇" :value="1" />
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
-              <el-button @click="resetUsageQuery">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
+					</template>
+				</el-dialog>
+			</el-tab-pane>
+			
+			<el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
+				<div class="search_form">
+					<el-form :inline="true" :model="usageQuery" class="search-form">
+						<el-form-item label="澶囦欢鍚嶇О">
+							<el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
+						</el-form-item>
+						<el-form-item label="鏉ユ簮">
+							<el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
+								<el-option label="缁翠慨" :value="0" />
+								<el-option label="淇濆吇" :value="1" />
+								<el-option label="鐩存帴鍑哄簱" :value="2" />
+							</el-select>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
+							<el-button @click="resetUsageQuery">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+					<div>
+						<el-button type="primary" @click="openRequisitionDialog">鐩存帴鍑哄簱</el-button>
+					</div>
+				</div>
 				<div class="table_list">
 					<PIMTable
 						rowKey="rowKey"
@@ -124,9 +128,63 @@
 						@pagination="handleUsagePageChange"
 					/>
 				</div>
-      </el-tab-pane>
-    </el-tabs>
-  </div>
+			</el-tab-pane>
+		</el-tabs>
+		
+		<!-- 鐩存帴鍑哄簱寮圭獥 -->
+		<el-dialog title="鐩存帴鍑哄簱" v-model="requisitionDialogVisible" width="500px">
+			<el-form :model="requisitionForm" :rules="requisitionRules" ref="requisitionFormRef" label-width="100px">
+				<el-form-item label="璁惧閫夋嫨" prop="deviceLedgerId">
+					<el-select
+						v-model="requisitionForm.deviceLedgerId"
+						placeholder="璇烽�夋嫨璁惧"
+						filterable
+						clearable
+						style="width: 100%"
+					>
+						<el-option
+							v-for="item in requisitionDeviceOptions"
+							:key="item.id"
+							:label="item.deviceName"
+							:value="item.id"
+						/>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="澶囦欢閫夋嫨" prop="sparePartsId">
+					<el-select
+						v-model="requisitionForm.sparePartsId"
+						placeholder="璇烽�夋嫨澶囦欢"
+						filterable
+						clearable
+						style="width: 100%"
+						@change="onRequisitionSparePartChange"
+					>
+						<el-option
+							v-for="item in requisitionSparePartsOptions"
+							:key="item.id"
+							:label="`${item.name}锛堝簱瀛�: ${item.quantity}锛塦"
+							:value="item.id"
+						/>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="棰嗙敤鏁伴噺" prop="quantity">
+					<el-input-number
+						v-model="requisitionForm.quantity"
+						:min="1"
+						:max="selectedSparePartMaxQty"
+						style="width: 100%"
+					/>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="requisitionForm.remark" placeholder="璇疯緭鍏ュ娉�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<el-button type="primary" @click="submitRequisition" :loading="requisitionLoading">纭畾</el-button>
+				<el-button @click="requisitionDialogVisible = false">鍙栨秷</el-button>
+			</template>
+		</el-dialog>
+	</div>
 </template>
 
 <script setup>
@@ -135,7 +193,7 @@
 import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
-import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage";
+import { getSparePartsUsagePage, outboundSpareParts } from "@/api/equipmentManagement/sparePartsUsage";
 
 // 鍔犺浇鐘舵��
 const loading = ref(false);
@@ -157,375 +215,456 @@
 const formRef = ref(null);
 // 鏌ヨ鍙傛暟
 const queryParams = reactive({
-  name: ''
+	name: ''
 });
 // 鍒嗛〉鍙傛暟
 const pagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0
+	current: 1,
+	size: 10,
+	total: 0
 });
 
 // 澶囦欢棰嗙敤璁板綍
 const usageLoading = ref(false);
 const usageQuery = reactive({
-  sparePartsName: "",
-  sourceType: "",
+	sparePartsName: "",
+	sourceType: "",
 });
 const usagePagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0,
+	current: 1,
+	size: 10,
+	total: 0,
 });
 const usageTableData = ref([]);
 const usageColumns = ref([
-  { label: "鏉ユ簮", prop: "sourceText" },
-  { label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
-  { label: "璁惧鍚嶇О", prop: "deviceName" },
-  { label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
-  { label: "棰嗙敤鏁伴噺", prop: "quantity" },
-  { label: "鎿嶄綔浜�", prop: "operator" },
-  { label: "鏃堕棿", prop: "createTime" },
+	{ label: "鏉ユ簮", prop: "sourceText" },
+	{ label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
+	{ label: "璁惧鍚嶇О", prop: "deviceName" },
+	{ label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
+	{ label: "棰嗙敤鏁伴噺", prop: "quantity" },
+	{ label: "鎿嶄綔浜�", prop: "operator" },
+	{ label: "鏃堕棿", prop: "createTime" },
 ]);
 
 const handleTabChange = async (name) => {
-  if (name === "usage") {
-    usagePagination.current = 1;
-    await fetchUsageData();
-  }
+	if (name === "usage") {
+		usagePagination.current = 1;
+		await fetchUsageData();
+	}
 };
 const columns = ref([
-  {
-    label: "璁惧鍚嶇О",
-    prop: "deviceNameStr",
-  },
-  {
-    label: "澶囦欢鍚嶇О",
-    prop: "name",
-  },
-  {
-    label: "澶囦欢缂栧彿",
-    prop: "sparePartsNo",
-  },
-  {
-    label: "鐘舵��",
-    prop: "status",
-    slot: "status",
-    dataType: "slot",
-  },
-  {
-    label: "浠锋牸",
-    prop: "price",
-  },
-  {
-    label: "鏁伴噺",
-    prop: "quantity",
-  },
-  {
-    label: "鎻忚堪",
-    prop: "description",
-  },
-  {
-    label: "鎿嶄綔",
-    prop: "operation",
-    width: 150,
-    fixed: 'right',
-    align: "center",
-    dataType: "action",
-    operation: [
-      {
-        name: "缂栬緫",
-        clickFun: (row) => {
-          editCategory(row)
-        },
-      },
-      {
-        name: "鍒犻櫎",
-        clickFun: (row) => {
-          deleteCategory(row.id)
-        },
-      },
-    ],
-  },
+	{
+		label: "璁惧鍚嶇О",
+		prop: "deviceNameStr",
+	},
+	{
+		label: "澶囦欢鍚嶇О",
+		prop: "name",
+	},
+	{
+		label: "澶囦欢缂栧彿",
+		prop: "sparePartsNo",
+	},
+	{
+		label: "鐘舵��",
+		prop: "status",
+		slot: "status",
+		dataType: "slot",
+	},
+	{
+		label: "浠锋牸",
+		prop: "price",
+	},
+	{
+		label: "鏁伴噺",
+		prop: "quantity",
+	},
+	{
+		label: "鎻忚堪",
+		prop: "description",
+	},
+	{
+		label: "鎿嶄綔",
+		prop: "operation",
+		width: 150,
+		fixed: 'right',
+		align: "center",
+		dataType: "action",
+		operation: [
+			{
+				name: "缂栬緫",
+				clickFun: (row) => {
+					editCategory(row)
+				},
+			},
+			{
+				name: "鍒犻櫎",
+				clickFun: (row) => {
+					deleteCategory(row.id)
+				},
+			},
+		],
+	},
 ]);
 // 琛ㄥ崟鏁版嵁
 const form = reactive({
-  id:'',
-  name: '',
-  sparePartsNo: '',
-  status: '',
-  description: '',
-  deviceLedgerIds: [],
-  price: null
+	id:'',
+	name: '',
+	sparePartsNo: '',
+	status: '',
+	description: '',
+	deviceLedgerIds: [],
+	price: null
 });
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const rules = reactive({
-  name: [
-    { required: true, message: '璇疯緭鍏ュ浠跺悕绉�', trigger: 'blur' }
-  ],
-  sparePartsNo: [
-    { required: true, message: '璇疯緭鍏ュ浠剁紪鍙�', trigger: 'blur' }
-  ],
-  quantity:[
-    { required: true, message: '璇疯緭鍏ユ暟閲�', trigger: 'blur' }
-  ],
-  status: [
-    { required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }
-  ],
-  deviceLedgerIds: [
-    { 
-      required: true, 
-      message: '璇烽�夋嫨璁惧', 
-      trigger: 'change',
-      validator: (rule, value, callback) => {
-        if (operationType.value === 'add' && (!value || value.length === 0)) {
-          callback(new Error('璇烽�夋嫨璁惧'));
-        } else {
-          callback();
-        }
-      }
-    }
-  ]
+	name: [
+		{ required: true, message: '璇疯緭鍏ュ浠跺悕绉�', trigger: 'blur' }
+	],
+	sparePartsNo: [
+		{ required: true, message: '璇疯緭鍏ュ浠剁紪鍙�', trigger: 'blur' }
+	],
+	quantity:[
+		{ required: true, message: '璇疯緭鍏ユ暟閲�', trigger: 'blur' }
+	],
+	status: [
+		{ required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }
+	],
+	deviceLedgerIds: [
+		{
+			required: true,
+			message: '璇烽�夋嫨璁惧',
+			trigger: 'change',
+			validator: (rule, value, callback) => {
+				if (operationType.value === 'add' && (!value || value.length === 0)) {
+					callback(new Error('璇烽�夋嫨璁惧'));
+				} else {
+					callback();
+				}
+			}
+		}
+	]
 });
 // 鑾峰彇缂╄繘閲�
 const getIndentation = (row) => {
-  // 杩欓噷绠�鍗曡繑鍥� 20锛屽彲鏍规嵁瀹為檯闇�姹傚疄鐜板眰绾х缉杩涢�昏緫
-  return 20; 
+	// 杩欓噷绠�鍗曡繑鍥� 20锛屽彲鏍规嵁瀹為檯闇�姹傚疄鐜板眰绾х缉杩涢�昏緫
+	return 20;
 };
 // 瀹氫箟 buildTree 鍑芥暟
 const buildTree = (flatData) => {
-  const map = {};
-  const result = [];
-  if(flatData){
-    return result;
-  }
-  flatData.forEach(item => {
-    map[item.id] = { ...item, children: [] };
-  });
-  flatData.forEach(item => {
-    if (item.parentId === null || !map[item.parentId]) {
-      result.push(map[item.id]);
-    } else {
-      map[item.parentId].children.push(map[item.id]);
-    }
-  });
-  return result;
+	const map = {};
+	const result = [];
+	if(flatData){
+		return result;
+	}
+	flatData.forEach(item => {
+		map[item.id] = { ...item, children: [] };
+	});
+	flatData.forEach(item => {
+		if (item.parentId === null || !map[item.parentId]) {
+			result.push(map[item.id]);
+		} else {
+			map[item.parentId].children.push(map[item.id]);
+		}
+	});
+	return result;
 };
 // 鑾峰彇鍒楄〃鏁版嵁
 const fetchListData = async () => {
-  loading.value = true;
-  try {
-    const params = {
-      current: pagination.current,
-      size: pagination.size
-    };
-    if (queryParams.name) {
-      params.name = queryParams.name;
-    }
-    const res = await getSparePartsList(params);
-    if (res.code === 200) {
-      renderTableData.value = res.data.records || [];
-      categories.value = res.data.records || [];
-      pagination.total = res.data.total || 0;
-    }
-  } catch (error) {
+	loading.value = true;
+	try {
+		const params = {
+			current: pagination.current,
+			size: pagination.size
+		};
+		if (queryParams.name) {
+			params.name = queryParams.name;
+		}
+		const res = await getSparePartsList(params);
+		if (res.code === 200) {
+			renderTableData.value = res.data.records || [];
+			categories.value = res.data.records || [];
+			pagination.total = res.data.total || 0;
+		}
+	} catch (error) {
 		loading.value = false;
-  } finally {
-    loading.value = false;
-  }
+	} finally {
+		loading.value = false;
+	}
 }
 
 const fetchUsageData = async () => {
-  usageLoading.value = true;
-  try {
-    const res = await getSparePartsUsagePage({
-      current: usagePagination.current,
-      size: usagePagination.size,
-      sparePartsName: usageQuery.sparePartsName || undefined,
-      sourceType: usageQuery.sourceType || undefined,
-    });
-    if (res?.code === 200) {
-      const records = res?.data?.records || [];
-      usagePagination.total = res?.data?.total || 0;
-      usageTableData.value = records.map((r, idx) => ({
-        rowKey: r.id ?? `${usagePagination.current}-${idx}`,
-        ...r,
-        sourceText: r.sourceText === "" ? "-" : r.sourceText,
-      }));
-    } else {
-      usagePagination.total = 0;
-      usageTableData.value = [];
-    }
-  } finally {
-    usageLoading.value = false;
-  }
+	usageLoading.value = true;
+	try {
+		const res = await getSparePartsUsagePage({
+			current: usagePagination.current,
+			size: usagePagination.size,
+			sparePartsName: usageQuery.sparePartsName || undefined,
+			sourceType: usageQuery.sourceType || undefined,
+		});
+		if (res?.code === 200) {
+			const records = res?.data?.records || [];
+			usagePagination.total = res?.data?.total || 0;
+			usageTableData.value = records.map((r, idx) => ({
+				rowKey: r.id ?? `${usagePagination.current}-${idx}`,
+				...r,
+				sourceText: r.sourceText === "" ? "-" : r.sourceText,
+			}));
+		} else {
+			usagePagination.total = 0;
+			usageTableData.value = [];
+		}
+	} finally {
+		usageLoading.value = false;
+	}
 };
 
 const handleUsageQuery = () => {
-  usagePagination.current = 1;
-  fetchUsageData();
+	usagePagination.current = 1;
+	fetchUsageData();
 };
 const resetUsageQuery = () => {
-  usageQuery.sparePartsName = "";
-  usageQuery.sourceType = "";
-  usagePagination.current = 1;
-  fetchUsageData();
+	usageQuery.sparePartsName = "";
+	usageQuery.sourceType = "";
+	usagePagination.current = 1;
+	fetchUsageData();
 };
 const handleUsagePageChange = (obj) => {
-  usagePagination.current = obj.page;
-  usagePagination.size = obj.limit;
-  fetchUsageData();
+	usagePagination.current = obj.page;
+	usagePagination.size = obj.limit;
+	fetchUsageData();
 };
 
 // 鏌ヨ
 const handleQuery = () => {
-  pagination.current = 1;
-  fetchListData();
+	pagination.current = 1;
+	fetchListData();
 }
 
 // 閲嶇疆鏌ヨ
 const resetQuery = () => {
-  queryParams.name = '';
-  pagination.current = 1;
-  fetchListData();
+	queryParams.name = '';
+	pagination.current = 1;
+	fetchListData();
 }
 
-// 鍒嗛〉澶у皬鏀瑰彉
-const handleSizeChange = (size) => {
-  pagination.size = size;
-  pagination.current = 1;
-  fetchListData();
-}
-
-// 褰撳墠椤垫敼鍙�
-const handleCurrentChange = (current) => {
-  pagination.current = current;
-  fetchListData();
+// 鍒嗛〉鍙樺寲
+const handleSizeChange = ({ page, limit }) => {
+	pagination.current = page;
+	pagination.size = limit;
+	fetchListData();
 }
 
 // 鍔犺浇璁惧鍒楄〃锛堝湪鎵撳紑寮规鏃惰皟鐢級
 const loadDeviceName = async () => {
-  try {
-    const { data } = await getDeviceLedger();
-    deviceOptions.value = data || [];
-  } catch (error) {
-    ElMessage.error('鑾峰彇璁惧鍒楄〃澶辫触');
-  }
+	try {
+		const { data } = await getDeviceLedger();
+		deviceOptions.value = data || [];
+	} catch (error) {
+		ElMessage.error('鑾峰彇璁惧鍒楄〃澶辫触');
+	}
 };
 
 // 鏂板鍒嗙被
 const addCategory = async () => {
-  await loadDeviceName();
-  form.id = '';
-  form.name = '';
-  form.sparePartsNo = '';
-  form.status = '';
-  form.description = '';
-  form.deviceLedgerIds = [];
-  form.quantity = undefined;
-  form.price = null;
-  operationType.value = 'add'
-  dialogVisible.value = true;
+	await loadDeviceName();
+	form.id = '';
+	form.name = '';
+	form.sparePartsNo = '';
+	form.status = '';
+	form.description = '';
+	form.deviceLedgerIds = [];
+	form.quantity = undefined;
+	form.price = null;
+	operationType.value = 'add'
+	dialogVisible.value = true;
 };
 
 // 缂栬緫鍒嗙被
 const editCategory = async (row) => {
-  await loadDeviceName();
-  Object.assign(form, row);
-  // 濡傛灉鍚庣杩斿洖鐨勬槸 deviceIds 瀛楃涓诧紝闇�瑕佽浆鎹负鏁扮粍
-  if (row.deviceIds && typeof row.deviceIds === 'string') {
-    // 纭繚ID绫诲瀷涓庤澶囬�夐」涓殑ID绫诲瀷涓�鑷�
-    const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id);
-    // 濡傛灉璁惧閫夐」涓殑ID鏄暟瀛楃被鍨嬶紝鍒欒浆鎹负鏁板瓧
-    if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') {
-      form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id));
-    } else {
-      form.deviceLedgerIds = deviceIdsArray;
-    }
-  } else if (row.deviceIds && Array.isArray(row.deviceIds)) {
-    form.deviceLedgerIds = row.deviceIds;
-  } else {
-    form.deviceLedgerIds = [];
-  }
-  operationType.value = 'edit'
-  dialogVisible.value = true;
+	await loadDeviceName();
+	Object.assign(form, row);
+	// 濡傛灉鍚庣杩斿洖鐨勬槸 deviceIds 瀛楃涓诧紝闇�瑕佽浆鎹负鏁扮粍
+	if (row.deviceIds && typeof row.deviceIds === 'string') {
+		// 纭繚ID绫诲瀷涓庤澶囬�夐」涓殑ID绫诲瀷涓�鑷�
+		const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id);
+		// 濡傛灉璁惧閫夐」涓殑ID鏄暟瀛楃被鍨嬶紝鍒欒浆鎹负鏁板瓧
+		if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') {
+			form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id));
+		} else {
+			form.deviceLedgerIds = deviceIdsArray;
+		}
+	} else if (row.deviceIds && Array.isArray(row.deviceIds)) {
+		form.deviceLedgerIds = row.deviceIds;
+	} else {
+		form.deviceLedgerIds = [];
+	}
+	operationType.value = 'edit'
+	dialogVisible.value = true;
 };
 
 // 鍒犻櫎鍒嗙被
 const deleteCategory = async (id) => {
-  try {
-    await ElMessageBox.confirm('姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ュ垎绫伙紝鏄惁缁х画?', '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning'
-    });
-    loading.value = true;
-    const res = await delSparePart(id);
-    if (res.code === 200) {
-      ElMessage.success('鍒犻櫎鎴愬姛');
-      fetchListData();
-    } else {
-      ElMessage.error(res.message || '鍒犻櫎澶辫触');
-    }
-  } catch (error) {
-    if (error !== 'cancel') {
-      ElMessage.error('鍒犻櫎澶辫触');
-    }
-  } finally {
-    loading.value = false;
-  }
+	try {
+		await ElMessageBox.confirm('姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ュ垎绫伙紝鏄惁缁х画?', '鎻愮ず', {
+			confirmButtonText: '纭畾',
+			cancelButtonText: '鍙栨秷',
+			type: 'warning'
+		});
+		loading.value = true;
+		const res = await delSparePart(id);
+		if (res.code === 200) {
+			ElMessage.success('鍒犻櫎鎴愬姛');
+			fetchListData();
+		} else {
+			ElMessage.error(res.message || '鍒犻櫎澶辫触');
+		}
+	} catch (error) {
+		if (error !== 'cancel') {
+			ElMessage.error('鍒犻櫎澶辫触');
+		}
+	} finally {
+		loading.value = false;
+	}
 };
 
 // 鎻愪氦琛ㄥ崟
 const submitForm = async () => {
-  if (!formRef.value) return;
-  try {
-    await formRef.value.validate();
-    formLoading.value = true;
-    
-    // 鏋勫缓鎻愪氦鏁版嵁
-    const submitData = {
-      ...form,
-      deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0 
-        ? form.deviceLedgerIds.join(',') 
-        : ''
-    };
-    
-    // 鍒犻櫎涓嶉渶瑕佺殑瀛楁
-    delete submitData.deviceLedgerIds;
-    
-    if (operationType.value === 'edit') {
-      let res = await editSparePart(submitData);
-      if (res.code === 200) {
-        ElMessage.success('缂栬緫鎴愬姛');
-        dialogVisible.value = false;
-        fetchListData();
-      }
-    } else {
-      let res = await addSparePart(submitData);
-      if (res.code === 200) {
-        ElMessage.success('鏂板鎴愬姛');
-        dialogVisible.value = false;
-        fetchListData();
-      }
-    }
-  } catch (error) {
-    ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�');
-  } finally {
-    formLoading.value = false;
-  }
+	if (!formRef.value) return;
+	try {
+		await formRef.value.validate();
+		formLoading.value = true;
+		
+		// 鏋勫缓鎻愪氦鏁版嵁
+		const submitData = {
+			...form,
+			deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0
+				? form.deviceLedgerIds.join(',')
+				: ''
+		};
+		
+		// 鍒犻櫎涓嶉渶瑕佺殑瀛楁
+		delete submitData.deviceLedgerIds;
+		
+		if (operationType.value === 'edit') {
+			let res = await editSparePart(submitData);
+			if (res.code === 200) {
+				ElMessage.success('缂栬緫鎴愬姛');
+				dialogVisible.value = false;
+				fetchListData();
+			}
+		} else {
+			let res = await addSparePart(submitData);
+			if (res.code === 200) {
+				ElMessage.success('鏂板鎴愬姛');
+				dialogVisible.value = false;
+				fetchListData();
+			}
+		}
+	} catch (error) {
+		ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�');
+	} finally {
+		formLoading.value = false;
+	}
+};
+
+// 鐩存帴鍑哄簱
+const requisitionDialogVisible = ref(false);
+const requisitionLoading = ref(false);
+const requisitionFormRef = ref(null);
+const requisitionDeviceOptions = ref([]);
+const requisitionSparePartsOptions = ref([]);
+const selectedSparePartMaxQty = ref(1);
+const requisitionForm = reactive({
+	deviceLedgerId: null,
+	sparePartsId: null,
+	quantity: 1,
+	remark: '',
+});
+const requisitionRules = {
+	sparePartsId: [{ required: true, message: '璇烽�夋嫨澶囦欢', trigger: 'change' }],
+	quantity: [{ required: true, message: '璇疯緭鍏ラ鐢ㄦ暟閲�', trigger: 'blur' }],
+};
+
+const loadRequisitionOptions = async () => {
+	try {
+		const [deviceRes, spareRes] = await Promise.all([
+			getDeviceLedger(),
+			getSparePartsList({ current: 1, size: 9999 }),
+		]);
+		requisitionDeviceOptions.value = deviceRes.data || [];
+		requisitionSparePartsOptions.value = spareRes.data?.records || [];
+	} catch (e) {
+		ElMessage.error('鍔犺浇閫夐」澶辫触');
+	}
+};
+
+const openRequisitionDialog = async () => {
+	requisitionForm.deviceLedgerId = null;
+	requisitionForm.sparePartsId = null;
+	requisitionForm.quantity = 1;
+	requisitionForm.remark = '';
+	selectedSparePartMaxQty.value = 1;
+	await loadRequisitionOptions();
+	requisitionDialogVisible.value = true;
+};
+
+const onRequisitionSparePartChange = (val) => {
+	const selected = requisitionSparePartsOptions.value.find(item => item.id === val);
+	selectedSparePartMaxQty.value = selected ? selected.quantity : 1;
+	if (requisitionForm.quantity > selectedSparePartMaxQty.value) {
+		requisitionForm.quantity = selectedSparePartMaxQty.value;
+	}
+};
+
+const submitRequisition = async () => {
+	if (!requisitionFormRef.value) return;
+	try {
+		await requisitionFormRef.value.validate();
+	} catch {
+		return;
+	}
+	if (requisitionForm.quantity <= 0) {
+		ElMessage.warning('棰嗙敤鏁伴噺蹇呴』澶т簬0');
+		return;
+	}
+	const selected = requisitionSparePartsOptions.value.find(item => item.id === requisitionForm.sparePartsId);
+	if (selected && requisitionForm.quantity > selected.quantity) {
+		ElMessage.warning(`搴撳瓨涓嶈冻锛屽綋鍓嶅簱瀛樹负 ${selected.quantity}`);
+		return;
+	}
+	requisitionLoading.value = true;
+	try {
+		const res = await outboundSpareParts({
+			deviceLedgerId: requisitionForm.deviceLedgerId || undefined,
+			sparePartsId: requisitionForm.sparePartsId,
+			quantity: requisitionForm.quantity,
+			remark: requisitionForm.remark || undefined,
+		});
+		if (res.code === 200) {
+			ElMessage.success('棰嗙敤鎴愬姛');
+			requisitionDialogVisible.value = false;
+			fetchUsageData();
+		} else {
+			ElMessage.error(res.msg || '棰嗙敤澶辫触');
+		}
+	} catch {
+		ElMessage.error('棰嗙敤澶辫触');
+	} finally {
+		requisitionLoading.value = false;
+	}
 };
 
 // 缁勪欢鎸傝浇鏃惰幏鍙栧垪琛ㄦ暟鎹�
 onMounted(() => {
-  fetchListData();
+	fetchListData();
 });
 </script>
 
 <style scoped>
 .spare-part-category {
-  padding: 20px;
+	padding: 20px;
 }
 .search_form {
 	display: flex;
@@ -533,38 +672,38 @@
 	justify-content: space-between;
 }
 .table_list {
-  margin-top: unset;
+	margin-top: unset;
 }
 
 .pagination-container {
-  margin-top: 20px;
-  display: flex;
-  justify-content: flex-end;
+	margin-top: 20px;
+	display: flex;
+	justify-content: flex-end;
 }
 
 .el-table__header-wrapper th {
-  background-color: #f5f7fa;
-  font-weight: 600;
+	background-color: #f5f7fa;
+	font-weight: 600;
 }
 
 .el-table__row:hover > td {
-  background-color: #fafafa;
+	background-color: #fafafa;
 }
 
 /* 鎸夐挳缁勬牱寮� */
 .actions > div {
-  display: flex;
-  gap: 10px;
+	display: flex;
+	gap: 10px;
 }
 
 /* 纭繚琛ㄦ牸涓殑鎿嶄綔鎸夐挳涓嶄細琚埅鏂� */
 .el-table-column--fixed-right .el-button {
-  margin: 0 2px;
+	margin: 0 2px;
 }
 
 /* 鏍戝舰鑺傜偣鍐呭鏍峰紡 */
 .nested-tree .el-tree-node__expand-icon {
-  font-size: 12px;
-  margin-right: 4px;
+	font-size: 12px;
+	margin-right: 4px;
 }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3