| src/api/equipmentManagement/sparePartsUsage.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/equipmentManagement/repair/Modal/MaintainModal.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/equipmentManagement/spareParts/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/equipmentManagement/sparePartsUsage.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,36 @@ import request from "@/utils/request"; /** * å¤ä»¶é¢ç¨è®°å½ - å页æ¥è¯¢ * params: { current, size, sparePartId?, sparePartName?, source?, deviceId?, startTime?, endTime? } */ export const getSparePartsUsagePage = (params) => { return request({ url: "/sparePartsUsage/listPage", method: "get", params, }); }; /** * å¤ä»¶é¢ç¨è®°å½ - æ°å¢ * data 示ä¾ï¼ * { * source: "repair" | "upkeep" | "manual", * sourceId?: number | string, * deviceId?: number | string, * deviceName?: string, * operatorId?: number | string, * operator?: string, * useTime?: string, // YYYY-MM-DD HH:mm:ss * items: [{ sparePartId: number|string, qty: number }] * } */ export const addSparePartsUsage = (data) => { return request({ url: "/sparePartsUsage/add", method: "post", data, }); }; src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -32,23 +32,61 @@ style="width: 100%" /> </el-form-item> <el-form-item label="设å¤å¤ä»¶"> <el-select v-model="form.sparePartsIds" :loading="loadingSparePartOptions" placeholder="è¯·éæ©è®¾å¤å¤ä»¶" multiple filterable> <el-option v-for="item in sparePartOptions" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </el-form-item> <el-form-item v-if="selectedSpareParts.length" label="é¢ç¨æ°é"> <div style="width: 100%"> <div v-for="item in selectedSpareParts" :key="item.id" style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;" > <div style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> {{ item.name }} <span v-if="item.quantity !== null && item.quantity !== undefined" style="color: #909399;"> ï¼åºåï¼{{ item.quantity }}ï¼ </span> </div> <el-input-number v-model="sparePartQtyMap[item.id]" :min="1" :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined" :step="1" controls-position="right" style="width: 180px" /> </div> </div> </el-form-item> </el-form> </FormDialog> </template> <script setup> import { computed, getCurrentInstance, nextTick, ref } from "vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { addMaintain } from "@/api/equipmentManagement/repair"; import useFormData from "@/hooks/useFormData"; import useUserStore from "@/store/modules/user"; import dayjs from "dayjs"; import { ElMessage } from "element-plus"; import { getSparePartsList } from "@/api/equipmentManagement/spareParts"; defineOptions({ name: "ç»´ä¿®æ¨¡ææ¡", }); const emits = defineEmits(["ok"]); const { proxy } = getCurrentInstance(); // ä¿åæ¥ä¿®è®°å½çid const repairId = ref(); @@ -61,6 +99,16 @@ maintenanceResult: undefined, // ç»´ä¿®ç»æ maintenanceTime: undefined, // ç»´ä¿®æ¥æ status: 0, sparePartsIds: [], }); const sparePartOptions = ref([]) const loadingSparePartOptions = ref(true) const sparePartQtyMap = ref({}) const selectedSpareParts = computed(() => { const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : []; const set = new Set(ids.map((i) => String(i))); return (sparePartOptions.value || []).filter((p) => set.has(String(p.id))); }); const setForm = (data) => { @@ -71,16 +119,59 @@ ? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss") : dayjs().format("YYYY-MM-DD HH:mm:ss"); form.status = 1; // é»è®¤ç¶æä¸ºå®ç» // multiple éæ©å¨è¦æ±æ°ç»ï¼å端常è¿å "1,2,3" if (Array.isArray(data?.sparePartsIds)) { form.sparePartsIds = data.sparePartsIds.map((v) => Number(v)).filter((v) => Number.isFinite(v)); } else if (typeof data?.sparePartsIds === "string") { form.sparePartsIds = data.sparePartsIds .split(",") .map((s) => Number(String(s).trim())) .filter((v) => Number.isFinite(v)); } else if (typeof data?.sparePartsIds === "number") { form.sparePartsIds = [data.sparePartsIds]; } else { form.sparePartsIds = []; } }; const sendForm = async () => { loading.value = true; try { const { code } = await addMaintain({ id: repairId.value, ...form }); // é¢ç¨æ°éæ ¡éª if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) { for (const partId of form.sparePartsIds) { const qty = Number(sparePartQtyMap.value?.[partId]); if (!Number.isFinite(qty) || qty <= 0) { proxy?.$modal?.msgError?.("请填åå¤ä»¶é¢ç¨æ°é"); return; } const part = sparePartOptions.value.find((p) => String(p.id) === String(partId)); const stock = part?.quantity; if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) { if (qty > Number(stock)) { proxy?.$modal?.msgError?.(`å¤ä»¶ã${part?.name || ""}ãé¢ç¨æ°éä¸è½è¶ è¿åºåï¼${stock}ï¼`); return; } } } } const data = { id: repairId.value, ...form, sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "", sparePartsQty: form.sparePartsIds ? form.sparePartsIds.map((id) => sparePartQtyMap.value?.[id] ?? 1).join(",") : "", sparePartsUseList: form.sparePartsIds ? form.sparePartsIds.map((id) => ({ id, quantity: sparePartQtyMap.value?.[id] ?? 1 })) : [], } const { code } = await addMaintain(data); if (code == 200) { ElMessage.success("ç»´ä¿®æå"); emits("ok"); resetForm(); sparePartQtyMap.value = {}; visible.value = false; } } finally { @@ -88,13 +179,34 @@ } }; const fetchSparePartOptions = () => { loadingSparePartOptions.value = true; // åå¤ä»¶ç®¡ç页ä¸è´ï¼/spareParts/listPage â res.data.records getSparePartsList({ current: 1, size: 1000 }) .then((res) => { if (res.code === 200) { sparePartOptions.value = res?.data?.records || []; } else { sparePartOptions.value = []; } }) .catch(() => { sparePartOptions.value = []; }) .finally(() => { loadingSparePartOptions.value = false; }); } const handleCancel = () => { resetForm(); sparePartQtyMap.value = {}; visible.value = false; }; const handleClose = () => { resetForm(); sparePartQtyMap.value = {}; visible.value = false; }; @@ -103,6 +215,7 @@ visible.value = true; await nextTick(); setForm(row); fetchSparePartOptions() }; defineExpose({ src/views/equipmentManagement/spareParts/index.vue
@@ -1,5 +1,7 @@ <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="å¤ä»¶åç§°"> @@ -90,18 +92,53 @@ </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.sparePartName" placeholder="请è¾å ¥å¤ä»¶åç§°" clearable style="width: 240px" /> </el-form-item> <el-form-item label="æ¥æº"> <el-select v-model="usageQuery.source" placeholder="è¯·éæ©" clearable style="width: 200px"> <el-option label="ç»´ä¿®" value="repair" /> <el-option label="ä¿å »" value="upkeep" /> </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> <PIMTable rowKey="rowKey" :column="usageColumns" :tableData="usageTableData" :tableLoading="usageLoading" :page="usagePagination" :isShowPagination="true" @pagination="handleUsagePageChange" /> </el-tab-pane> </el-tabs> </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 } from "@/api/equipmentManagement/sparePartsUsage"; // å è½½ç¶æ const loading = ref(false); const formLoading = ref(false); const activeTab = ref("list"); // å¯¹è¯æ¡æ¾ç¤ºç¶æ const dialogVisible = ref(false); // ç¼è¾ ID @@ -126,6 +163,35 @@ size: 10, total: 0 }); // å¤ä»¶é¢ç¨è®°å½ const usageLoading = ref(false); const usageQuery = reactive({ sparePartName: "", source: "", }); 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: "sparePartName" }, { label: "é¢ç¨æ°é", prop: "qty" }, { label: "æä½äºº", prop: "operator" }, { label: "æ¶é´", prop: "time" }, ]); const handleTabChange = async (name) => { if (name === "usage") { usagePagination.current = 1; await fetchUsageData(); } }; const columns = ref([ { label: "设å¤åç§°", @@ -268,6 +334,52 @@ } } const fetchUsageData = async () => { usageLoading.value = true; try { const res = await getSparePartsUsagePage({ current: usagePagination.current, size: usagePagination.size, sparePartName: usageQuery.sparePartName || undefined, source: usageQuery.source || 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.source === "ç»´ä¿®" ? "ç»´ä¿®" : r.source === "ä¿å »" ? "ä¿å »" : r.source === "manual" ? "æå·¥" : (r.source || "-"), })); } else { usagePagination.total = 0; usageTableData.value = []; } } finally { usageLoading.value = false; } }; const handleUsageQuery = () => { usagePagination.current = 1; fetchUsageData(); }; const resetUsageQuery = () => { usageQuery.sparePartName = ""; usageQuery.source = ""; usagePagination.current = 1; fetchUsageData(); }; const handleUsagePageChange = (obj) => { usagePagination.current = obj.page; usagePagination.size = obj.limit; fetchUsageData(); }; // æ¥è¯¢ const handleQuery = () => { pagination.current = 1;