From d420578cb8bc076be2f0c7dd5a4dbfca335d7cdb Mon Sep 17 00:00:00 2001
From: zhang_nuo <zhang_12370@163.com>
Date: 星期一, 17 八月 2026 21:44:51 +0800
Subject: [PATCH] feat(personnel,production): 新增年龄自动计算及工单数量支持小数
---
src/views/equipmentManagement/spareParts/index.vue | 512 +++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 410 insertions(+), 102 deletions(-)
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index eb0bdd5..131e1d5 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -1,134 +1,204 @@
<template>
<div class="spare-part-category">
- <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-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"
+ :column="columns"
+ :tableData="renderTableData"
+ :tableLoading="loading"
+ :page="pagination"
+ :isShowPagination="true"
+ @pagination="handleSizeChange"
+ >
+ <template #status="{ row }">
+ <el-tag type="success" size="small">{{ row.status }}</el-tag>
+ </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>
+ <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-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"
+ :column="usageColumns"
+ :tableData="usageTableData"
+ :tableLoading="usageLoading"
+ :page="usagePagination"
+ :isShowPagination="true"
+ @pagination="handleUsagePageChange"
/>
- </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">
- <el-table
- v-loading="loading"
- :data="renderTableData"
- style="width: 100%; margin-top: 10px;"
- border
- row-key="id"
- >
- <el-table-column prop="deviceNameStr" label="璁惧鍚嶇О" width="300"></el-table-column>
- <el-table-column prop="name" label="澶囦欢鍚嶇О" width="200"></el-table-column>
- <el-table-column prop="sparePartsNo" label="澶囦欢缂栧彿" width="200"></el-table-column>
- <el-table-column prop="status" label="鐘舵��" width="100">
- <template #default="{ row }">
- <el-tag type="success" size="small">{{ row.status }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="price" label="浠锋牸" width="140"></el-table-column>
- <el-table-column prop="quantity" label="鏁伴噺" width="140"></el-table-column>
- <el-table-column prop="description" label="鎻忚堪" width="150"></el-table-column>
- <el-table-column label="鎿嶄綔" width="150" fixed="right" align="center">
- <template #default="{ row }">
- <el-button
- link
- type="primary"
- @click="() => editCategory(row)"
- :disabled="loading"
- >
- 缂栬緫
- </el-button>
- <el-button
- link
- @click="() => deleteCategory(row.id)"
- style="color: #f56c6c;"
- :disabled="loading"
- >
- 鍒犻櫎
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- </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">
+ </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="form.deviceLedgerIds"
+ v-model="requisitionForm.deviceLedgerId"
placeholder="璇烽�夋嫨璁惧"
filterable
- default-first-option
- :reserve-keyword="false"
- multiple
+ clearable
style="width: 100%"
>
<el-option
- v-for="(item, index) in deviceOptions"
- :key="index"
+ v-for="item in requisitionDeviceOptions"
+ :key="item.id"
: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"
+ <el-form-item label="澶囦欢閫夋嫨" prop="sparePartsId">
+ <el-select
+ v-model="requisitionForm.sparePartsId"
+ placeholder="璇烽�夋嫨澶囦欢"
+ filterable
+ clearable
style="width: 100%"
- ></el-input-number>
+ @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>
- <span class="dialog-footer">
- <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
- <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
- </span>
+ <el-button type="primary" @click="submitRequisition" :loading="requisitionLoading">纭畾</el-button>
+ <el-button @click="requisitionDialogVisible = false">鍙栨秷</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
-import { ref, computed, onMounted, reactive, watch } from 'vue';
+import { ref, computed, onMounted, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import { getSparePartsUsagePage, outboundSpareParts } from "@/api/equipmentManagement/sparePartsUsage";
// 鍔犺浇鐘舵��
const loading = ref(false);
const formLoading = ref(false);
+const activeTab = ref("list");
// 瀵硅瘽妗嗘樉绀虹姸鎬�
const dialogVisible = ref(false);
// 缂栬緫 ID
@@ -147,6 +217,95 @@
const queryParams = reactive({
name: ''
});
+// 鍒嗛〉鍙傛暟
+const pagination = reactive({
+ current: 1,
+ size: 10,
+ total: 0
+});
+
+// 澶囦欢棰嗙敤璁板綍
+const usageLoading = ref(false);
+const usageQuery = reactive({
+ sparePartsName: "",
+ sourceType: "",
+});
+const usagePagination = reactive({
+ 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" },
+]);
+
+const handleTabChange = async (name) => {
+ 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)
+ },
+ },
+ ],
+ },
+]);
// 琛ㄥ崟鏁版嵁
const form = reactive({
id:'',
@@ -215,7 +374,10 @@
const fetchListData = async () => {
loading.value = true;
try {
- const params = {};
+ const params = {
+ current: pagination.current,
+ size: pagination.size
+ };
if (queryParams.name) {
params.name = queryParams.name;
}
@@ -223,6 +385,7 @@
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;
@@ -231,14 +394,65 @@
}
}
+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;
+ }
+};
+
+const handleUsageQuery = () => {
+ usagePagination.current = 1;
+ fetchUsageData();
+};
+const resetUsageQuery = () => {
+ usageQuery.sparePartsName = "";
+ usageQuery.sourceType = "";
+ usagePagination.current = 1;
+ fetchUsageData();
+};
+const handleUsagePageChange = (obj) => {
+ usagePagination.current = obj.page;
+ usagePagination.size = obj.limit;
+ fetchUsageData();
+};
+
// 鏌ヨ
const handleQuery = () => {
+ pagination.current = 1;
fetchListData();
}
// 閲嶇疆鏌ヨ
const resetQuery = () => {
queryParams.name = '';
+ pagination.current = 1;
+ fetchListData();
+}
+
+// 鍒嗛〉鍙樺寲
+const handleSizeChange = ({ page, limit }) => {
+ pagination.current = page;
+ pagination.size = limit;
fetchListData();
}
@@ -261,6 +475,7 @@
form.status = '';
form.description = '';
form.deviceLedgerIds = [];
+ form.quantity = undefined;
form.price = null;
operationType.value = 'add'
dialogVisible.value = true;
@@ -354,6 +569,93 @@
}
};
+// 鐩存帴鍑哄簱
+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();
@@ -373,6 +675,12 @@
margin-top: unset;
}
+.pagination-container {
+ margin-top: 20px;
+ display: flex;
+ justify-content: flex-end;
+}
+
.el-table__header-wrapper th {
background-color: #f5f7fa;
font-weight: 600;
--
Gitblit v1.9.3