From 2291b7406cdc184abfa1b1986baeb55499771343 Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期一, 16 三月 2026 17:07:15 +0800
Subject: [PATCH] fix(设备管理): 修正添加维护接口的URL路径
---
src/pages/consumablesLogistics/stockManagement/index.vue | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 319 insertions(+), 0 deletions(-)
diff --git a/src/pages/consumablesLogistics/stockManagement/index.vue b/src/pages/consumablesLogistics/stockManagement/index.vue
new file mode 100644
index 0000000..85021f6
--- /dev/null
+++ b/src/pages/consumablesLogistics/stockManagement/index.vue
@@ -0,0 +1,319 @@
+<template>
+ <view class="stock-mgmt-page">
+ <PageHeader title="搴撳瓨绠$悊" @back="goBack" />
+
+ <view class="tabs-wrap">
+ <view
+ v-for="tab in tabs"
+ :key="tab.name"
+ class="tab-item"
+ :class="{ active: activeTab === tab.name }"
+ @click="activeTab = tab.name"
+ >
+ <text>{{ tab.label }}</text>
+ </view>
+ </view>
+
+ <view class="search-section">
+ <view class="search-row">
+ <view class="search-input-wrap">
+ <up-input
+ v-model="searchForm.productName"
+ placeholder="浜у搧澶х被"
+ clearable
+ />
+ </view>
+ <view class="btn-search" @click="handleQuery">
+ <view class="btn-search-inner">
+ <up-icon name="search" size="22" color="#fff"></up-icon>
+ <text>鎼滅储</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <view class="list-section">
+ <view v-if="tableData.length > 0">
+ <view
+ v-for="(item, index) in tableData"
+ :key="item.id || index"
+ class="card-item"
+ >
+ <view class="card-click" @click="goDetail(item)">
+ <view class="card-header">
+ <view class="header-main">
+ <text class="product-name">{{ item.productName }}</text>
+ <text class="sub-title">{{ item.model || item.updateTime }}</text>
+ </view>
+ </view>
+ <up-divider />
+ <view class="card-body">
+ <view class="row"><text class="l">瑙勬牸鍨嬪彿</text><text class="r">{{ item.model }}</text></view>
+ <view class="row"><text class="l">鍗曚綅</text><text class="r">{{ item.unit }}</text></view>
+ <view class="row"><text class="l">鎬诲簱瀛樻暟</text><text class="r highlight">{{ item.qualitity }}</text></view>
+ <view class="row"><text class="l">鍐荤粨鏁伴噺</text><text class="r">{{ item.lockedQuantity || 0 }}</text></view>
+ <view class="row"><text class="l">鍙敤搴撳瓨</text><text class="r">{{ item.unLockedQuantity ?? (item.qualitity - (item.lockedQuantity || 0)) }}</text></view>
+ <view class="row"><text class="l">鏈�杩戞洿鏂版椂闂�</text><text class="r">{{ item.updateTime }}</text></view>
+ </view>
+ </view>
+ <view class="card-actions">
+ <view
+ class="btn-link btn-link-primary"
+ :class="{ disabled: !(item.unLockedQuantity > 0) }"
+ @click="openSubtract(item)"
+ >鍑哄簱</view>
+ <view
+ class="btn-link btn-link-warn"
+ v-if="item.unLockedQuantity > 0"
+ @click="openFrozen(item)"
+ >鍐荤粨</view>
+ <view
+ class="btn-link btn-link-plain"
+ v-if="(item.lockedQuantity || 0) > 0"
+ @click="openThaw(item)"
+ >瑙e喕</view>
+ </view>
+ </view>
+ <view class="load-more-wrap">
+ <u-loadmore :status="loadStatus" @loadmore="loadMore" />
+ </view>
+ </view>
+ <view v-else class="no-data">鏆傛棤鏁版嵁</view>
+ </view>
+
+ <up-popup :show="showQuantityPopup" mode="center" round="16" @close="closeQuantityPopup">
+ <view class="popup-inner">
+ <view class="popup-title">{{ quantityTitle }}</view>
+ <view class="form-row">
+ <text class="form-label">鏁伴噺</text>
+ <up-input v-model="quantityForm.num" type="number" :placeholder="'鏈�澶�' + maxQuantity" />
+ </view>
+ <view class="popup-footer">
+ <view class="btn-cancel" @click="closeQuantityPopup">鍙栨秷</view>
+ <view class="btn-ok" @click="submitQuantity">纭畾</view>
+ </view>
+ </view>
+ </up-popup>
+
+ <view class="fab-button" @click="goAdd">
+ <up-icon name="plus" size="24" color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { computed, reactive, ref, toRefs, watch } from "vue";
+import { onReachBottom, onShow } from "@dcloudio/uni-app";
+import PageHeader from "@/components/PageHeader.vue";
+import { frozenConsumablesIn, getConsumablesInListPage, thawConsumablesIn } from "@/api/consumablesLogistics/consumablesIn.js";
+import { frozenConsumablesUninventory, getConsumablesUninventoryListPage, thawConsumablesUninventory } from "@/api/consumablesLogistics/consumablesUninventory.js";
+
+const activeTab = ref("qualified");
+const tabs = [
+ { label: "鍚堟牸搴撳瓨", name: "qualified" },
+ { label: "涓嶅悎鏍煎簱瀛�", name: "unqualified" },
+];
+const tableData = ref([]);
+const total = ref(0);
+const loadStatus = ref("loadmore");
+const showQuantityPopup = ref(false);
+const quantityOp = ref("");
+const currentRecord = ref(null);
+const page = reactive({ current: 1, size: 20 });
+const data = reactive({
+ searchForm: { productName: "" },
+ quantityForm: { num: "" },
+});
+const { searchForm, quantityForm } = toRefs(data);
+
+const isQualified = () => activeTab.value === "qualified";
+const getList = () => {
+ const isFirstPage = page.current === 1;
+ if (isFirstPage) {
+ uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
+ }
+ const params = { ...page, productName: searchForm.value.productName };
+ const api = isQualified() ? getConsumablesInListPage : getConsumablesUninventoryListPage;
+ api(params)
+ .then((res) => {
+ uni.hideLoading();
+ const records = res.data?.records || [];
+ const totalCount = res.data?.total || 0;
+ if (isFirstPage) {
+ tableData.value = records;
+ } else {
+ tableData.value = [...tableData.value, ...records];
+ }
+ total.value = totalCount;
+ loadStatus.value = tableData.value.length >= totalCount || totalCount === 0 ? "nomore" : "loadmore";
+ })
+ .catch(() => {
+ uni.hideLoading();
+ loadStatus.value = "error";
+ if (isFirstPage) {
+ uni.showToast({ title: "鍔犺浇澶辫触", icon: "none" });
+ }
+ });
+};
+
+const loadMore = () => {
+ if (loadStatus.value === "nomore" || loadStatus.value === "loading") return;
+ loadStatus.value = "loading";
+ page.current++;
+ getList();
+};
+
+watch(activeTab, () => {
+ page.current = 1;
+ loadStatus.value = "loadmore";
+ getList();
+});
+
+const handleQuery = () => {
+ page.current = 1;
+ loadStatus.value = "loadmore";
+ getList();
+};
+
+const goAdd = () => {
+ const type = isQualified() ? "0" : "1";
+ uni.navigateTo({
+ url: `/pages/consumablesLogistics/stockManagement/add?type=${type}`,
+ });
+};
+
+const quantityTitle = computed(() => {
+ if (quantityOp.value === "frozen") return "鍐荤粨搴撳瓨";
+ if (quantityOp.value === "thaw") return "瑙e喕搴撳瓨";
+ return "";
+});
+
+const maxQuantity = computed(() => {
+ if (!currentRecord.value) return 0;
+ if (quantityOp.value === "frozen") return currentRecord.value.unLockedQuantity || 0;
+ if (quantityOp.value === "thaw") return currentRecord.value.lockedQuantity || 0;
+ return 0;
+});
+
+const openSubtract = (row) => {
+ if (!(row.unLockedQuantity > 0)) return;
+ try {
+ uni.setStorageSync(
+ "stockSubtractRecord",
+ JSON.stringify({
+ item: row,
+ type: isQualified() ? "0" : "1",
+ })
+ );
+ } catch (e) {}
+ const typeParam = isQualified() ? "0" : "1";
+ uni.navigateTo({
+ url: `/pages/consumablesLogistics/stockManagement/subtract?type=${typeParam}&id=${row.id}`,
+ });
+};
+
+const openFrozen = (row) => {
+ quantityOp.value = "frozen";
+ currentRecord.value = row;
+ quantityForm.value.num = "";
+ showQuantityPopup.value = true;
+};
+
+const openThaw = (row) => {
+ quantityOp.value = "thaw";
+ currentRecord.value = row;
+ quantityForm.value.num = "";
+ showQuantityPopup.value = true;
+};
+
+const closeQuantityPopup = () => {
+ showQuantityPopup.value = false;
+ currentRecord.value = null;
+ quantityOp.value = "";
+};
+
+const submitQuantity = () => {
+ const num = Number(quantityForm.value.num);
+ if (!num || num <= 0 || num > maxQuantity.value) {
+ uni.showToast({ title: `璇疯緭鍏� 1~${maxQuantity.value} 涔嬮棿鐨勬暟閲廯, icon: "none" });
+ return;
+ }
+ const id = currentRecord.value?.id;
+ if (!id) return;
+ const base = { id, lockedQuantity: num };
+ let promise;
+ if (quantityOp.value === "frozen") {
+ promise = isQualified() ? frozenConsumablesIn(base) : frozenConsumablesUninventory(base);
+ } else {
+ promise = isQualified() ? thawConsumablesIn(base) : thawConsumablesUninventory(base);
+ }
+ promise
+ .then(() => {
+ uni.showToast({ title: "鎿嶄綔鎴愬姛", icon: "success" });
+ closeQuantityPopup();
+ getList();
+ })
+ .catch(() => uni.showToast({ title: "鎿嶄綔澶辫触", icon: "none" }));
+};
+
+const goDetail = (item) => {
+ if (!item) return;
+ try {
+ uni.setStorageSync(
+ "stockDetailItem",
+ JSON.stringify({
+ item,
+ type: isQualified() ? "0" : "1",
+ })
+ );
+ } catch (e) {}
+ if (!item.id) {
+ uni.navigateTo({ url: "/pages/consumablesLogistics/stockManagement/view" });
+ } else {
+ uni.navigateTo({ url: "/pages/consumablesLogistics/stockManagement/view?id=" + item.id });
+ }
+};
+
+const goBack = () => uni.navigateBack();
+onShow(() => getList());
+onReachBottom(() => {
+ loadMore();
+});
+</script>
+
+<style lang="scss" scoped>
+.stock-mgmt-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 120rpx; }
+.tabs-wrap { display: flex; background: #fff; padding: 24rpx; gap: 24rpx; }
+.tab-item { flex: 1; text-align: center; padding: 20rpx; border-radius: 12rpx; background: #f0f0f0; font-size: 28rpx; color: #666; }
+.tab-item.active { background: #2979ff; color: #fff; }
+.search-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
+.search-row { display: flex; align-items: center; }
+.search-input-wrap { flex: 1; margin-right: 20rpx; min-width: 0; }
+.btn-search { display: flex; align-items: center; justify-content: center; width: 180rpx; min-height: 72rpx; flex-shrink: 0; padding: 20rpx 24rpx; background: #2979ff; color: #fff; border-radius: 12rpx; font-size: 28rpx; box-sizing: border-box; text-align: center; }
+.btn-search-inner { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 8rpx; }
+.list-section { padding: 0 24rpx; }
+.card-item { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
+.card-header { padding: 8rpx 0; }
+.header-main { display: flex; flex-direction: column; gap: 8rpx; }
+.product-name { font-size: 30rpx; font-weight: 500; color: #333; }
+.sub-title { font-size: 24rpx; color: #999; }
+.card-body .row { display: flex; justify-content: space-between; padding: 12rpx 0; font-size: 26rpx; }
+.card-body .l { color: #666; }
+.card-body .r { color: #333; }
+.card-body .r.highlight { color: #2979ff; font-weight: 500; }
+.card-actions { display: flex; gap: 16rpx; margin-top: 16rpx; padding-top: 16rpx; border-top: 1rpx solid #eee; justify-content: flex-end; }
+.btn-link { min-width: 120rpx; padding: 10rpx 20rpx; border-radius: 24rpx; font-size: 26rpx; text-align: center; border-width: 1rpx; border-style: solid; }
+.btn-link-primary { color: #ffffff; background: #2979ff; border-color: #2979ff; }
+.btn-link-warn { color: #ff9f1a; background: rgba(255, 159, 26, 0.08); border-color: rgba(255, 159, 26, 0.6); }
+.btn-link-plain { color: #666666; background: #f5f5f5; border-color: #e0e0e0; }
+.btn-link.disabled { color: #cccccc; background: #f5f5f5; border-color: #e0e0e0; }
+.no-data { text-align: center; padding: 80rpx 0; color: #999; font-size: 28rpx; }
+.fab-button { position: fixed; bottom: calc(30px + env(safe-area-inset-bottom)); right: 30px; width: 56px; height: 56px; background: #2979ff; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); z-index: 1000; }
+.popup-inner { padding: 40rpx; min-width: 560rpx; background: #fff; }
+.popup-title { font-size: 32rpx; font-weight: 500; margin-bottom: 32rpx; }
+.form-row { margin-bottom: 24rpx; }
+.form-row .form-label { display: block; font-size: 26rpx; color: #666; margin-bottom: 12rpx; }
+.popup-footer { display: flex; gap: 24rpx; margin-top: 40rpx; }
+.btn-cancel { flex: 1; text-align: center; padding: 24rpx; background: #f0f0f0; border-radius: 12rpx; }
+.btn-ok { flex: 1; text-align: center; padding: 24rpx; background: #2979ff; color: #fff; border-radius: 12rpx; }
+</style>
--
Gitblit v1.9.3