From c0ac58d8d1e836787cc73eca9bd02411229836fb Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 01 四月 2026 17:00:46 +0800
Subject: [PATCH] fix: 备件管理可以跟设备维修、设备保养做联动
---
src/pages/equipmentManagement/upkeep/maintain.vue | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 302 insertions(+), 56 deletions(-)
diff --git a/src/pages/equipmentManagement/upkeep/maintain.vue b/src/pages/equipmentManagement/upkeep/maintain.vue
index 9edeff4..b11a585 100644
--- a/src/pages/equipmentManagement/upkeep/maintain.vue
+++ b/src/pages/equipmentManagement/upkeep/maintain.vue
@@ -41,7 +41,7 @@
clearable />
</u-form-item>
<u-form-item label="淇濆吇鐘舵��"
- prop="status"
+ prop="maintenancestatusText"
required
border-bottom>
<u-input v-model="maintenancestatusText"
@@ -79,6 +79,25 @@
@click="showSparePartPicker" />
</template>
</u-form-item>
+
+ <u-form-item v-if="selectedSpareParts.length"
+ label="棰嗙敤鏁伴噺"
+ border-bottom>
+ <view class="spare-qty-list">
+ <view v-for="item in selectedSpareParts"
+ :key="String(item.id || item.value)"
+ class="spare-qty-row">
+ <view class="spare-qty-name">
+ <text class="spare-name">{{ item.name }}</text>
+ <text v-if="item.quantity !== null && item.quantity !== undefined"
+ class="spare-stock">锛堝簱瀛橈細{{ item.quantity }}锛�</text>
+ </view>
+ <up-number-box v-model="sparePartQtyMap[item.id || item.value]"
+ :min="1"
+ :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined" />
+ </view>
+ </view>
+ </u-form-item>
<!-- 涓婁紶闄勪欢 -->
<u-form-item v-if="form.status == '1'"
label="淇濆吇闄勪欢"
@@ -96,7 +115,7 @@
style="margin-right: 5px;"></u-icon>
{{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
</u-button>
- <u-button type="success"
+ <!-- <u-button type="success"
@click="chooseMedia('video')"
:loading="uploading"
:disabled="uploadFiles.length >= uploadConfig.limit"
@@ -107,7 +126,7 @@
color="#fff"
style="margin-right: 5px;"></uni-icons>
{{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
- </u-button>
+ </u-button> -->
</view>
<!-- 涓婁紶杩涘害 -->
<view v-if="uploading"
@@ -123,8 +142,9 @@
:key="index"
class="file-item">
<view class="file-preview-container">
+ <!-- {{formatFileUrl(file.url)}} -->
<image v-if="file.type === 'image' || isImageFile(file)"
- :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
+ :src="formatFileUrl(file.url || file.tempFilePath || file.path || file.downloadUrl)"
class="file-preview"
mode="aspectFill" />
<view v-else-if="file.type === 'video'"
@@ -153,7 +173,7 @@
</view>
<view v-if="uploadFiles.length === 0"
class="empty-state">
- <text>璇烽�夋嫨瑕佷笂浼犵殑淇濆吇鍥剧墖鎴栬棰�</text>
+ <text>璇烽�夋嫨瑕佷笂浼犵殑淇濆吇鍥剧墖</text>
</view>
</view>
</u-form-item>
@@ -190,9 +210,6 @@
<view class="spare-part-popup">
<view class="popup-header">
<text class="popup-title">閫夋嫨璁惧澶囦欢</text>
- <up-button type="primary"
- size="small"
- @click="confirmSparePartSelection">纭畾</up-button>
</view>
<view class="spare-part-options">
<view v-for="(item, index) in sparePartOptions"
@@ -206,19 +223,23 @@
color="#2c7be5" />
</view>
</view>
+ <up-button type="primary"
+ size="small"
+ :customStyle="{ borderRadius: '6px', padding: '4px 12px' }"
+ @click="confirmSparePartSelection">纭畾</up-button>
</view>
</up-popup>
</view>
</template>
<script setup>
- import { ref, onMounted } from "vue";
+ import { ref, onMounted, reactive } from "vue";
import { onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
import {
addMaintenance,
- getSparePartsOptions,
} from "@/api/equipmentManagement/upkeep";
+ import { getSparePartsList } from "@/api/equipmentManagement/spareParts";
import useUserStore from "@/store/modules/user";
import dayjs from "dayjs";
import { formatDateToYMD } from "@/utils/ruoyi";
@@ -253,6 +274,8 @@
const maintenancestatusText = ref("");
const selectedSpareParts = ref([]);
const tempSelectedSpareParts = ref([]);
+ const sparePartQtyMap = reactive({});
+ const sparePartsQtyRaw = ref("");
// 鏂囦欢涓婁紶鐩稿叧
const uploadFiles = ref([]);
@@ -314,6 +337,8 @@
maintenancestatusText.value = "";
selectedSpareParts.value = [];
tempSelectedSpareParts.value = [];
+ Object.keys(sparePartQtyMap).forEach((k) => delete sparePartQtyMap[k]);
+ sparePartsQtyRaw.value = "";
};
const resetFormAndValidate = () => {
@@ -338,6 +363,7 @@
// 鎻愪氦琛ㄥ崟
const sendForm = async () => {
+ console.log(form.value.sparePartsIds, "form.value.sparePartsIds");
try {
// 鎵嬪姩楠岃瘉琛ㄥ崟
let isValid = true;
@@ -379,11 +405,34 @@
loading.value = false;
return;
}
- // 鍑嗗鎻愪氦鏁版嵁锛宮aintenanceActuallyTime 鍔犱笂褰撳墠鏃跺垎绉�
+ // 棰嗙敤鏁伴噺鏍¢獙
+ const spareIds = Array.isArray(form.value.sparePartsIds) ? form.value.sparePartsIds : [];
+ if (spareIds.length > 0) {
+ for (const partId of spareIds) {
+ const qty = Number(sparePartQtyMap?.[partId]);
+ if (!Number.isFinite(qty) || qty <= 0) {
+ showToast("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
+ loading.value = false;
+ return;
+ }
+ const part = sparePartOptions.value.find((p) => String(p.id || p.value) === String(partId));
+ const stock = part?.quantity;
+ if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) {
+ if (qty > Number(stock)) {
+ showToast(`澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦);
+ loading.value = false;
+ return;
+ }
+ }
+ }
+ }
+
const submitData = {
...form.value,
imagesFile: form.value.status == "1" ? uploadFiles.value : [],
- sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
+ sparePartsIds: spareIds.length ? spareIds.join(",") : "",
+ sparePartsQty: spareIds.length ? spareIds.map((pid) => sparePartQtyMap?.[pid] ?? 1).join(",") : "",
+ sparePartsUseList: spareIds.length ? spareIds.map((pid) => ({ id: pid, quantity: sparePartQtyMap?.[pid] ?? 1 })) : [],
};
const { code } = await addMaintenance({ id: id, ...submitData });
@@ -416,6 +465,7 @@
return uni.getStorageSync("repairId");
};
+ const dataform = ref({});
// 鑾峰彇璁惧淇℃伅
const getUpkeepItemData = () => {
try {
@@ -423,6 +473,9 @@
if (!dataStr) {
return null;
}
+ dataform.value = JSON.parse(dataStr);
+ sparePartsQtyRaw.value = dataform.value?.sparePartsQty || "";
+ fetchSparePartOptions();
return JSON.parse(dataStr);
} catch (e) {
console.error("瑙f瀽璁惧鏁版嵁澶辫触:", e);
@@ -459,39 +512,64 @@
// 鏄剧ず璁惧澶囦欢閫夋嫨鍣�
const showSparePartPicker = () => {
tempSelectedSpareParts.value = [...selectedSpareParts.value];
+ tempSelectedSpareParts.value.forEach((p) => {
+ const pid = p?.id ?? p?.value;
+ if (pid !== null && pid !== undefined) {
+ if (!Number.isFinite(Number(sparePartQtyMap[pid])) || Number(sparePartQtyMap[pid]) <= 0) {
+ sparePartQtyMap[pid] = 1;
+ }
+ }
+ });
showSparePart.value = true;
};
// 妫�鏌ュ浠舵槸鍚﹀凡閫変腑
- const isSparePartSelected = value => {
- return tempSelectedSpareParts.value.some(item => item.value === value);
+ const isSparePartSelected = id => {
+ return tempSelectedSpareParts.value.some(
+ item => item.id === id || item.value === id
+ );
};
// 鍒囨崲澶囦欢閫変腑鐘舵��
const toggleSparePartSelection = item => {
+ const itemId = item.id || item.value;
const index = tempSelectedSpareParts.value.findIndex(
- selected => selected.value === item.value
+ selected => selected.id === itemId || selected.value === itemId
);
if (index > -1) {
tempSelectedSpareParts.value.splice(index, 1);
+ delete sparePartQtyMap[itemId];
} else {
tempSelectedSpareParts.value.push(item);
+ if (!Number.isFinite(Number(sparePartQtyMap[itemId])) || Number(sparePartQtyMap[itemId]) <= 0) {
+ sparePartQtyMap[itemId] = 1;
+ }
}
};
// 纭澶囦欢閫夋嫨
const confirmSparePartSelection = () => {
selectedSpareParts.value = [...tempSelectedSpareParts.value];
- form.value.sparePartsIds = selectedSpareParts.value.map(item => item.value);
+ form.value.sparePartsIds = selectedSpareParts.value.map(item => item.id || item.value);
+ selectedSpareParts.value.forEach((p) => {
+ const pid = p?.id ?? p?.value;
+ if (pid !== null && pid !== undefined) {
+ if (!Number.isFinite(Number(sparePartQtyMap[pid])) || Number(sparePartQtyMap[pid]) <= 0) {
+ sparePartQtyMap[pid] = 1;
+ }
+ }
+ });
showSparePart.value = false;
};
// 绉婚櫎宸查�夊浠�
const removeSparePart = index => {
- selectedSpareParts.value.splice(index, 1);
- form.value.sparePartsIds = selectedSpareParts.value.map(item => item.value);
+ const removed = selectedSpareParts.value.splice(index, 1)[0];
+ form.value.sparePartsIds = selectedSpareParts.value.map(item => item.id || item.value);
+ const rid = removed?.id ?? removed?.value;
+ if (rid !== null && rid !== undefined) delete sparePartQtyMap[rid];
};
-
+ const sparePartsIds = ref([]);
// 鍒濆鍖栬〃鍗曟暟鎹�
const initForm = () => {
// 鑾峰彇璁惧淇℃伅
@@ -545,18 +623,10 @@
maintenancestatusText.value = statusMap[itemData.status] || "";
}
// 濉厖澶囦欢鏁版嵁
- if (itemData.spareParts && itemData.spareParts.length > 0) {
- selectedSpareParts.value = itemData.spareParts.map(sparePart => ({
- id: sparePart.id || sparePart.sparePartId || sparePart.value,
- name: sparePart.name || sparePart.sparePartName,
- code: sparePart.code || sparePart.sparePartCode,
- value: sparePart.id || sparePart.sparePartId || sparePart.value,
- }));
- // 璁剧疆澶囦欢IDs
- form.value.sparePartsIds = selectedSpareParts.value
- .map(item => item.value)
- .join(",");
- }
+
+ // 澶勭悊瀛楃涓叉牸寮忕殑澶囦欢IDs
+ sparePartsIds.value = itemData.sparePartsIds;
+
// 濉厖闄勪欢鏁版嵁
if (itemData.files && itemData.files.length > 0) {
uploadFiles.value = itemData.files.map(file => ({
@@ -582,41 +652,102 @@
}
};
- onShow(() => {
+ onShow(async () => {
+ // 鍏堣幏鍙栧浠堕�夐」锛屽啀鍒濆鍖栬〃鍗�
+ const pageId = getPageId();
+ if (pageId) {
+ await fetchSparePartOptions();
+ }
// 椤甸潰鏄剧ず鏃跺垵濮嬪寲琛ㄥ崟
- initForm();
- fetchSparePartOptions(getPageId());
});
const sparePartOptions = ref([]);
- const fetchSparePartOptions = deviceLedgerId => {
- getSparePartsOptions({ deviceLedgerId: deviceLedgerId }).then(res => {
- if (res.code == 200) {
- sparePartOptions.value = res.data || [];
- }
+ // 澶囦欢鍒楄〃鎺ュ彛瀵归綈缁翠慨椤�/PC锛�/spareParts/listPage 鈫� res.data.records
+ const fetchSparePartOptions = () => {
+ return new Promise((resolve, reject) => {
+ getSparePartsList({ current: 1, size: 1000 })
+ .then(res => {
+ if (res.code == 200) {
+ sparePartOptions.value = res?.data?.records || [];
+ const idArray =
+ typeof sparePartsIds.value === "string"
+ ? sparePartsIds.value.split(",")
+ : (Array.isArray(sparePartsIds.value) ? sparePartsIds.value : []);
+
+ if (idArray.length > 0) {
+ selectedSpareParts.value = sparePartOptions.value
+ .filter(
+ option =>
+ idArray.includes(option.id.toString()) ||
+ idArray.includes(option.value?.toString())
+ )
+ .map(option => ({
+ id: option.id || option.value,
+ name: option.name,
+ code: option.code,
+ value: option.id || option.value,
+ quantity: option.quantity,
+ }));
+ // 璁剧疆澶囦欢IDs锛堜繚鎸佹暟缁勶紝鎻愪氦鏃跺啀 join锛�
+ form.value.sparePartsIds = idArray.map((v) => {
+ const n = Number(String(v).trim());
+ return Number.isFinite(n) ? n : String(v).trim();
+ });
+
+ // 鍥炴樉棰嗙敤鏁伴噺锛堣嫢鏈� sparePartsQty锛�
+ if (typeof sparePartsQtyRaw.value === "string" && sparePartsQtyRaw.value.trim()) {
+ const qtyArr = sparePartsQtyRaw.value.split(",").map((s) => Number(String(s).trim()));
+ selectedSpareParts.value.forEach((p, idx) => {
+ const pid = p?.id ?? p?.value;
+ const q = qtyArr[idx];
+ if (pid !== null && pid !== undefined && Number.isFinite(q) && q > 0) {
+ sparePartQtyMap[pid] = q;
+ }
+ });
+ }
+
+ // 榛樿鏁伴噺鍏滃簳
+ selectedSpareParts.value.forEach((p) => {
+ const pid = p?.id ?? p?.value;
+ if (pid !== null && pid !== undefined) {
+ if (!Number.isFinite(Number(sparePartQtyMap[pid])) || Number(sparePartQtyMap[pid]) <= 0) {
+ sparePartQtyMap[pid] = 1;
+ }
+ }
+ });
+ }
+ resolve(res?.data?.records || []);
+ } else {
+ resolve([]);
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇澶囦欢閫夐」澶辫触:", err);
+ resolve([]);
+ });
});
};
// 鏍煎紡鍖栨枃浠禪RL
- const formatFileUrl = url => {
- if (!url) return "";
+ // const formatFileUrl = url => {
+ // if (!url) return "";
- // 濡傛灉宸茬粡鏄畬鏁寸殑URL锛坔ttp鎴杊ttps寮�澶达級锛岀洿鎺ヨ繑鍥�
- if (url.startsWith("http://") || url.startsWith("https://")) {
- return url;
- }
+ // // 濡傛灉宸茬粡鏄畬鏁寸殑URL锛坔ttp鎴杊ttps寮�澶达級锛岀洿鎺ヨ繑鍥�
+ // if (url.startsWith("http://") || url.startsWith("https://")) {
+ // return url;
+ // }
- // 濡傛灉鏄湰鍦拌矾寰勶紙濡� D:\\ruoyi\\prod\\uploads...锛夛紝闇�瑕佽浆鎹负缃戠粶URL
- // 浠庤矾寰勪腑鎻愬彇uploads鍚庨潰鐨勯儴鍒�
- const uploadsIndex = url.indexOf("uploads");
- if (uploadsIndex !== -1) {
- const relativePath = url.substring(uploadsIndex);
- // 浣跨敤baseUrl + /profile/ + 鐩稿璺緞
- return `http://192.168.1.35:8888/profile/${relativePath}`;
- }
+ // // 濡傛灉鏄湰鍦拌矾寰勶紙濡� D:\\ruoyi\\prod\\uploads...锛夛紝闇�瑕佽浆鎹负缃戠粶URL
+ // // 浠庤矾寰勪腑鎻愬彇uploads鍚庨潰鐨勯儴鍒�
+ // const uploadsIndex = url.indexOf("uploads");
+ // if (uploadsIndex !== -1) {
+ // const relativePath = url.substring(uploadsIndex);
+ // // 浣跨敤baseUrl + /profile/ + 鐩稿璺緞
+ // return `http://192.168.1.35:8888/profile/${relativePath}`;
+ // }
- // 鍏朵粬鎯呭喌锛屽皾璇曠洿鎺ユ嫾鎺�
- return `http://192.168.1.35:8888/profile/${url}`;
- };
+ // // 鍏朵粬鎯呭喌锛屽皾璇曠洿鎺ユ嫾鎺�
+ // return `http://192.168.1.35:8888/profile/${url}`;
+ // };
// 鏍煎紡鍖栨枃浠跺ぇ灏�
const formatFileSize = size => {
@@ -883,6 +1014,18 @@
uploadProgress.value = res.progress;
});
}
+ }; // 鏍煎紡鍖栨枃浠禪RL
+ const formatFileUrl = url => {
+ if (!url) return "";
+ if (url.startsWith("http://") || url.startsWith("https://")) {
+ return url;
+ }
+ // const uploadsIndex = url.indexOf("uploads");
+ // if (uploadsIndex !== -1) {
+ // const relativePath = url.substring(uploadsIndex);
+ // return `${config.fileUrl}/${relativePath}`;
+ // }
+ return `${config.fileUrl}/${url}`;
};
// 涓婁紶鎴愬姛澶勭悊
@@ -1026,6 +1169,46 @@
font-size: 14px;
}
+ /* 棰嗙敤鏁伴噺鏍峰紡 */
+ .spare-qty-list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 4px 0;
+ }
+
+ .spare-qty-row {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ }
+
+ .spare-qty-name {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 6px;
+ }
+
+ .spare-name {
+ max-width: 220px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ color: #303133;
+ font-size: 14px;
+ }
+
+ .spare-stock {
+ color: #909399;
+ font-size: 12px;
+ }
+
/* 澶囦欢閫夋嫨寮圭獥鏍峰紡 */
.spare-part-popup {
padding: 16px;
@@ -1069,6 +1252,69 @@
font-weight: 500;
}
+ /* 澶囦欢閫夋嫨寮圭獥鏍峰紡 */
+ .spare-part-popup {
+ width: 100%;
+ max-height: 80vh;
+ background: #fff;
+ border-radius: 16px 16px 0 0;
+ overflow: hidden;
+ }
+
+ .popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #f8f9fa;
+ }
+
+ .popup-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .spare-part-options {
+ padding: 10px 0;
+ max-height: 60vh;
+ overflow-y: auto;
+ }
+
+ .spare-part-option {
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 14px 20px;
+ border-bottom: 1px solid #f0f0f0;
+ transition: all 0.2s ease;
+ }
+
+ .spare-part-option:last-child {
+ border-bottom: none;
+ }
+
+ .spare-part-option:hover {
+ background: #f8f9fa;
+ }
+
+ .spare-part-option.selected {
+ background: #e6f7ff;
+ color: #1890ff;
+ }
+
+ .spare-part-option.selected::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 4px;
+ background: #1890ff;
+ }
+
/* 鏂囦欢涓婁紶鏍峰紡 */
.simple-upload-area {
width: 100%;
--
Gitblit v1.9.3