From 88022fc18a3dddb2e1181eaf08e0e155cdb3094b Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 26 五月 2026 13:39:53 +0800
Subject: [PATCH] 库存管理完善
---
src/pages/inventoryManagement/stockManagement/Record.vue | 567 +++++++++++++++++++++++++++++++++++--------------------
1 files changed, 359 insertions(+), 208 deletions(-)
diff --git a/src/pages/inventoryManagement/stockManagement/Record.vue b/src/pages/inventoryManagement/stockManagement/Record.vue
index e4542a8..e5d5ca2 100644
--- a/src/pages/inventoryManagement/stockManagement/Record.vue
+++ b/src/pages/inventoryManagement/stockManagement/Record.vue
@@ -3,33 +3,38 @@
<view class="search-section">
<view class="search-bar">
<view class="search-input">
- <up-input
- class="search-text"
- placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
- v-model="searchForm.productName"
- @confirm="handleQuery"
- clearable
- />
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
+ v-model="searchForm.productName"
+ @confirm="handleQuery"
+ clearable />
</view>
- <view class="filter-button" @click="handleQuery">
- <up-icon name="search" size="24" color="#999"></up-icon>
+ <view class="filter-button"
+ @click="handleQuery">
+ <up-icon name="search"
+ size="24"
+ color="#999"></up-icon>
</view>
</view>
</view>
-
- <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore">
- <view v-for="item in tableData" :key="item.id" class="ledger-item">
+ <scroll-view scroll-y
+ class="ledger-list"
+ v-if="tableData.length > 0"
+ @scrolltolower="loadMore">
+ <view v-for="item in tableData"
+ :key="item.id"
+ class="ledger-item">
<view class="item-header">
<view class="item-left">
<view class="document-icon">
- <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
+ <up-icon name="file-text"
+ size="16"
+ color="#ffffff"></up-icon>
</view>
<text class="item-id">{{ item.productName }}</text>
</view>
</view>
-
<up-divider></up-divider>
-
<view class="item-details">
<view class="detail-row">
<text class="detail-label">瑙勬牸鍨嬪彿</text>
@@ -39,11 +44,20 @@
<text class="detail-label">鍗曚綅</text>
<text class="detail-value">{{ item.unit }}</text>
</view>
- <view class="detail-row">
+ <view class="detail-row"
+ @click="handleShowBatch(item.batchNo)">
<text class="detail-label">鎵瑰彿</text>
- <text class="detail-value">{{ item.batchNo }}</text>
+ <view class="detail-value batch-no-wrapper">
+ <text class="batch-no-text"
+ :class="{ 'clickable': isBatchClickable(item.batchNo) }">
+ {{ formatBatchNo(item.batchNo) }}
+ </text>
+ <up-icon v-if="isBatchClickable(item.batchNo)"
+ name="arrow-right"
+ size="14"
+ color="#2979ff"></up-icon>
+ </view>
</view>
-
<view class="quantity-section">
<view class="quantity-box qualified">
<text class="q-label">鍚堟牸搴撳瓨</text>
@@ -54,7 +68,6 @@
<text class="q-value">{{ item.unQualifiedQuantity }}</text>
</view>
</view>
-
<view class="quantity-section">
<view class="quantity-box locked">
<text class="q-label">鍚堟牸鍐荤粨</text>
@@ -65,7 +78,6 @@
<text class="q-value">{{ item.unQualifiedLockedQuantity }}</text>
</view>
</view>
-
<view class="detail-row">
<text class="detail-label">搴撳瓨棰勮</text>
<text class="detail-value">{{ item.warnNum }}</text>
@@ -82,211 +94,350 @@
</view>
<up-loadmore :status="loadStatus" />
</scroll-view>
- <view v-else-if="!loading" class="no-data">
- <up-empty mode="data" text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
+ <view v-else-if="!loading"
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
</view>
+ <!-- 鎵瑰彿鍒楄〃寮圭獥 -->
+ <up-popup v-model:show="showBatchPopup"
+ @close="showBatchPopup = false"
+ mode="bottom"
+ round="20"
+ closeable>
+ <view class="batch-popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鎵瑰彿璇︽儏</text>
+ </view>
+ <scroll-view scroll-y
+ class="batch-list-scroll">
+ <view class="batch-list">
+ <view v-for="(batch, index) in currentBatchList"
+ :key="index"
+ class="batch-item">
+ <view class="batch-index-box">
+ <text class="batch-index">{{ index + 1 }}</text>
+ </view>
+ <text class="batch-text">{{ batch }}</text>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+ </up-popup>
</view>
</template>
<script setup>
-import { ref, reactive, onMounted } from 'vue';
-import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
+ import { ref, reactive, onMounted } from "vue";
+ import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
-const props = defineProps({
- productId: {
- type: Number,
- required: true
- }
-});
-
-const tableData = ref([]);
-const loading = ref(false);
-const loadStatus = ref('loadmore');
-const page = reactive({ current: 1, size: 10 });
-const total = ref(0);
-const searchForm = reactive({
- productName: '',
- topParentProductId: props.productId
-});
-
-const handleQuery = () => {
- page.current = 1;
- tableData.value = [];
- getList();
-};
-
-const getList = () => {
- if (loading.value) return;
- loading.value = true;
- loadStatus.value = 'loading';
-
- getStockInventoryListPageCombined({
- ...searchForm,
- current: page.current,
- size: page.size
- }).then(res => {
- loading.value = false;
- const records = res.data.records || [];
- tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
- total.value = res.data.total;
- loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore';
- }).catch(() => {
- loading.value = false;
- loadStatus.value = 'loadmore';
+ const props = defineProps({
+ productId: {
+ type: Number,
+ required: true,
+ },
});
-};
-const loadMore = () => {
- if (loadStatus.value === 'loadmore') {
- page.current++;
+ const tableData = ref([]);
+ const loading = ref(false);
+ const loadStatus = ref("loadmore");
+ const page = reactive({ current: 1, size: 10 });
+ const total = ref(0);
+ const searchForm = reactive({
+ productName: "",
+ topParentProductId: props.productId,
+ });
+
+ const showBatchPopup = ref(false);
+ const currentBatchList = ref([]);
+
+ const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
getList();
- }
-};
+ };
-onMounted(() => {
- getList();
-});
+ const getList = () => {
+ if (loading.value) return;
+ loading.value = true;
+ loadStatus.value = "loading";
+
+ getStockInventoryListPageCombined({
+ ...searchForm,
+ current: page.current,
+ size: page.size,
+ })
+ .then(res => {
+ loading.value = false;
+ const records = res.data.records || [];
+ tableData.value =
+ page.current === 1 ? records : [...tableData.value, ...records];
+ total.value = res.data.total;
+ loadStatus.value =
+ tableData.value.length >= total.value ? "nomore" : "loadmore";
+ })
+ .catch(() => {
+ loading.value = false;
+ loadStatus.value = "loadmore";
+ });
+ };
+
+ const loadMore = () => {
+ if (loadStatus.value === "loadmore") {
+ page.current++;
+ getList();
+ }
+ };
+
+ const handleShowBatch = batchNo => {
+ if (!batchNo) return;
+ // 鏀寔閫楀彿銆佺┖鏍兼垨鎹㈣鍒嗛殧
+ currentBatchList.value = batchNo
+ .split(/[,锛孿s\n]+/)
+ .filter(item => item.trim() !== "");
+ if (currentBatchList.value.length > 0) {
+ showBatchPopup.value = true;
+ }
+ };
+
+ const formatBatchNo = batchNo => {
+ if (!batchNo) return "-";
+ if (batchNo.length > 25) {
+ return batchNo.substring(0, 25) + "...";
+ }
+ return batchNo;
+ };
+
+ const isBatchClickable = batchNo => {
+ if (!batchNo) return false;
+ return batchNo.length > 25 || batchNo.includes(",") || batchNo.includes("锛�");
+ };
+
+ onMounted(() => {
+ getList();
+ });
</script>
<style scoped lang="scss">
-.record-container {
- height: 100%;
- display: flex;
- flex-direction: column;
- background-color: #f5f7fa;
-}
-
-.search-section {
- padding: 20rpx;
- background-color: #ffffff;
- position: sticky;
- top: 0;
- z-index: 10;
-}
-
-.search-bar {
- display: flex;
- align-items: center;
- background-color: #f2f2f2;
- border-radius: 40rpx;
- padding: 0 30rpx;
- height: 80rpx;
-}
-
-.search-input {
- flex: 1;
-}
-
-.search-text {
- font-size: 28rpx;
-}
-
-.filter-button {
- padding-left: 20rpx;
-}
-
-.ledger-list {
- flex: 1;
- padding: 20rpx;
- box-sizing: border-box;
-}
-
-.ledger-item {
- background-color: #ffffff;
- border-radius: 16rpx;
- padding: 30rpx;
- margin-bottom: 20rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.item-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20rpx;
-}
-
-.item-left {
- display: flex;
- align-items: center;
-}
-
-.document-icon {
- width: 40rpx;
- height: 40rpx;
- background: linear-gradient(135deg, #2979ff, #1565c0);
- border-radius: 8rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 16rpx;
-}
-
-.item-id {
- font-size: 30rpx;
- font-weight: bold;
- color: #303133;
-}
-
-.item-details {
- .detail-row {
- display: flex;
- justify-content: space-between;
- margin-bottom: 16rpx;
- font-size: 26rpx;
-
- .detail-label {
- color: #909399;
- }
-
- .detail-value {
- color: #303133;
- font-weight: 500;
- }
- }
-}
-
-.quantity-section {
- display: flex;
- gap: 20rpx;
- margin: 20rpx 0;
-
- .quantity-box {
- flex: 1;
- padding: 16rpx;
- border-radius: 8rpx;
+ .record-container {
+ height: 100%;
display: flex;
flex-direction: column;
+ background-color: #f5f7fa;
+ }
+
+ .search-section {
+ padding: 20rpx;
+ background-color: #ffffff;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ }
+
+ .search-bar {
+ display: flex;
align-items: center;
-
- .q-label {
- font-size: 22rpx;
- margin-bottom: 8rpx;
- }
-
- .q-value {
- font-size: 32rpx;
- font-weight: bold;
- }
-
- &.qualified {
- background-color: #ecf5ff;
- color: #409eff;
- }
-
- &.unqualified {
- background-color: #fef0f0;
- color: #f56c6c;
- }
-
- &.locked {
- background-color: #f4f4f5;
- color: #909399;
+ background-color: #f2f2f2;
+ border-radius: 40rpx;
+ padding: 0 30rpx;
+ height: 80rpx;
+ }
+
+ .search-input {
+ flex: 1;
+ }
+
+ .search-text {
+ font-size: 28rpx;
+ }
+
+ .filter-button {
+ padding-left: 20rpx;
+ }
+
+ .ledger-list {
+ padding: 20rpx;
+ box-sizing: border-box;
+ overflow: auto;
+ }
+
+ .ledger-item {
+ background-color: #ffffff;
+ border-radius: 16rpx;
+ padding: 30rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+ }
+
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+ }
+
+ .item-left {
+ display: flex;
+ align-items: center;
+ }
+
+ .document-icon {
+ width: 40rpx;
+ height: 40rpx;
+ background: linear-gradient(135deg, #2979ff, #1565c0);
+ border-radius: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 16rpx;
+ }
+
+ .item-id {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #303133;
+ }
+
+ .item-details {
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+ font-size: 26rpx;
+
+ .detail-label {
+ color: #909399;
+ }
+
+ .detail-value {
+ color: #303133;
+ font-weight: 500;
+ }
+
+ .batch-no-wrapper {
+ display: flex;
+ align-items: center;
+ max-width: 70%;
+
+ .batch-no-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ &.clickable {
+ color: #2979ff;
+ text-decoration: underline;
+ }
+ }
+ }
}
}
-}
-.no-data {
- padding-top: 200rpx;
-}
+ .batch-popup-content {
+ background-color: #fff;
+ padding: 30rpx;
+ max-height: 70vh;
+ display: flex;
+ flex-direction: column;
+
+ .popup-header {
+ padding-bottom: 30rpx;
+ border-bottom: 1rpx solid #ebeef5;
+ margin-bottom: 20rpx;
+ text-align: center;
+
+ .popup-title {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #303133;
+ }
+ }
+
+ .batch-list-scroll {
+ flex: 1;
+ overflow: hidden;
+ }
+
+ .batch-list {
+ padding: 10rpx 0;
+
+ .batch-item {
+ display: flex;
+ align-items: center;
+ padding: 24rpx 0;
+ border-bottom: 1rpx solid #f2f6fc;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .batch-index-box {
+ width: 40rpx;
+ height: 40rpx;
+ background-color: #f0f2f5;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 20rpx;
+
+ .batch-index {
+ font-size: 20rpx;
+ color: #909399;
+ }
+ }
+
+ .batch-text {
+ font-size: 28rpx;
+ color: #303133;
+ flex: 1;
+ word-break: break-all;
+ }
+ }
+ }
+ }
+
+ .quantity-section {
+ display: flex;
+ gap: 20rpx;
+ margin: 20rpx 0;
+
+ .quantity-box {
+ flex: 1;
+ padding: 16rpx;
+ border-radius: 8rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ .q-label {
+ font-size: 22rpx;
+ margin-bottom: 8rpx;
+ }
+
+ .q-value {
+ font-size: 32rpx;
+ font-weight: bold;
+ }
+
+ &.qualified {
+ background-color: #ecf5ff;
+ color: #409eff;
+ }
+
+ &.unqualified {
+ background-color: #fef0f0;
+ color: #f56c6c;
+ }
+
+ &.locked {
+ background-color: #f4f4f5;
+ color: #909399;
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 200rpx;
+ }
</style>
--
Gitblit v1.9.3