From 60530101d70fd5887ae1152962882919fceab502 Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期四, 16 四月 2026 16:44:47 +0800
Subject: [PATCH] feat(库存管理): 重构库存分类并新增原材料库存页面
---
src/pages/inventoryManagement/stockManagement/Qualified.vue | 50 +++++--
src/pages/works.vue | 3
src/pages/productionManagement/productionReport/index.vue | 91 +++++++++++++
src/pages/inventoryManagement/stockManagement/rawMaterialRecord.vue | 151 +++++++++++++++++++++
src/pages/inventoryManagement/stockManagement/index.vue | 9 +
src/pages/inventoryManagement/stockManagement/Unqualified.vue | 60 ++++++-
6 files changed, 333 insertions(+), 31 deletions(-)
diff --git a/src/pages/inventoryManagement/stockManagement/Qualified.vue b/src/pages/inventoryManagement/stockManagement/Qualified.vue
index 12b9060..dc2a06c 100644
--- a/src/pages/inventoryManagement/stockManagement/Qualified.vue
+++ b/src/pages/inventoryManagement/stockManagement/Qualified.vue
@@ -27,7 +27,7 @@
<text class="item-id">{{ item.productName }}</text>
</view>
<view class="item-right">
- <text class="item-tag tag-type">鍚堟牸搴撳瓨</text>
+ <text class="item-tag tag-type">鎴愬搧搴撳瓨</text>
</view>
</view>
@@ -35,28 +35,28 @@
<view class="item-details">
<view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.model }}</text>
+ <text class="detail-label">瀛樿揣/瑙勬牸</text>
+ <text class="detail-value">{{item.productName || "鏃�" }} / {{ item.model || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">搴撳瓨鏁伴噺</text>
- <text class="detail-value">{{ item.qualitity }} {{ item.unit }}</text>
+ <text class="detail-label">涓绘暟閲�/涓诲崟浣�</text>
+ <text class="detail-value">{{ item.qualitity || "鏃�" }} / {{ item.unit || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">閿佸畾/鍐荤粨</text>
- <text class="detail-value">{{ item.lockedQuantity }} {{ item.unit }}</text>
+ <text class="detail-label">杈呮暟閲�/杈呭崟浣�</text>
+ <text class="detail-value">{{ item.subQualitity || "鏃�" }} / {{ item.subUnit || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">鍙敤搴撳瓨</text>
- <text class="detail-value" style="color: #2979ff; font-weight: bold;">{{ item.unLockedQuantity }} {{ item.unit }}</text>
+ <text class="detail-label">浠撳簱缂栫爜/浠撳簱鍚嶇О</text>
+ <text class="detail-value">{{ item.warehouseCode || "鏃�" }} / {{ item.warehouseName || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">搴撳瓨棰勮</text>
- <text class="detail-value">{{ item.warnNum }}</text>
+ <text class="detail-label">瀛樿揣缂栫爜/搴撳瓨棰勮</text>
+ <text class="detail-value">{{ item.productCode || "鏃�" }} / {{ item.warnNum || "鏃�" }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鏇存柊鏃堕棿</text>
- <text class="detail-value">{{ item.updateTime }}</text>
+ <text class="detail-value">{{ item.updateTime || "鏃�" }}</text>
</view>
</view>
</view>
@@ -77,7 +77,7 @@
const loadStatus = ref('loadmore');
const page = reactive({ current: 1, size: 10 });
const total = ref(0);
-const searchForm = reactive({ productName: '' });
+const searchForm = reactive({ productName: '', rootName: '鎴愬搧' });
const handleQuery = () => {
page.current = 1;
@@ -89,7 +89,7 @@
if (loading.value) return;
loading.value = true;
loadStatus.value = 'loading';
- getStockInventoryListPage({ ...searchForm, ...page, type: 'qualified' }).then(res => {
+ getStockInventoryListPage({ ...searchForm, ...page }).then(res => {
loading.value = false;
const records = res.data.records || [];
tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
@@ -148,4 +148,26 @@
.no-data {
padding-top: 100px;
}
+
+.item-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 5px 0;
+}
+
+.fab-button {
+ position: fixed;
+ right: 20px;
+ bottom: 80px;
+ width: 50px;
+ height: 50px;
+ background-color: #2979ff;
+ border-radius: 25px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+ z-index: 100;
+}
</style>
diff --git a/src/pages/inventoryManagement/stockManagement/Unqualified.vue b/src/pages/inventoryManagement/stockManagement/Unqualified.vue
index 48dafc4..5b7205b 100644
--- a/src/pages/inventoryManagement/stockManagement/Unqualified.vue
+++ b/src/pages/inventoryManagement/stockManagement/Unqualified.vue
@@ -27,7 +27,7 @@
<text class="item-id">{{ item.productName }}</text>
</view>
<view class="item-right">
- <text class="item-tag tag-type" style="background-color: #fde2e2; color: #f56c6c;">涓嶅悎鏍煎簱瀛�</text>
+ <text class="item-tag tag-type">杈呮潗搴撳瓨</text>
</view>
</view>
@@ -35,24 +35,28 @@
<view class="item-details">
<view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.model }}</text>
+ <text class="detail-label">瀛樿揣/瑙勬牸</text>
+ <text class="detail-value">{{item.productName || "鏃�" }} / {{ item.model || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">搴撳瓨鏁伴噺</text>
- <text class="detail-value">{{ item.qualitity }} {{ item.unit }}</text>
+ <text class="detail-label">涓绘暟閲�/涓诲崟浣�</text>
+ <text class="detail-value">{{ item.qualitity || "鏃�" }} / {{ item.unit || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">閿佸畾/鍐荤粨</text>
- <text class="detail-value">{{ item.lockedQuantity }} {{ item.unit }}</text>
+ <text class="detail-label">杈呮暟閲�/杈呭崟浣�</text>
+ <text class="detail-value">{{ item.subQualitity || "鏃�" }} / {{ item.subUnit || "鏃�" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">鍙敤搴撳瓨</text>
- <text class="detail-value" style="color: #f56c6c; font-weight: bold;">{{ item.unLockedQuantity }} {{ item.unit }}</text>
+ <text class="detail-label">浠撳簱缂栫爜/浠撳簱鍚嶇О</text>
+ <text class="detail-value">{{ item.warehouseCode || "鏃�" }} / {{ item.warehouseName || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀛樿揣缂栫爜/搴撳瓨棰勮</text>
+ <text class="detail-value">{{ item.productCode || "鏃�" }} / {{ item.warnNum || "鏃�" }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鏇存柊鏃堕棿</text>
- <text class="detail-value">{{ item.updateTime }}</text>
+ <text class="detail-value">{{ item.updateTime || "鏃�" }}</text>
</view>
</view>
</view>
@@ -66,14 +70,14 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
-import { getStockUninventoryListPage } from "@/api/inventoryManagement/stockUninventory.js";
+import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js";
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: '' });
+const searchForm = reactive({ productName: '', rootName: '杈呮潗' });
const handleQuery = () => {
page.current = 1;
@@ -85,7 +89,7 @@
if (loading.value) return;
loading.value = true;
loadStatus.value = 'loading';
- getStockUninventoryListPage({ ...searchForm, ...page, type: 'unqualified' }).then(res => {
+ getStockInventoryListPage({ ...searchForm, ...page }).then(res => {
loading.value = false;
const records = res.data.records || [];
tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
@@ -131,4 +135,34 @@
.no-data {
padding-top: 100px;
}
+
+.tag-type {
+ background-color: #e8f5e9;
+ color: #4caf50;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+.item-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 5px 0;
+}
+
+.fab-button {
+ position: fixed;
+ right: 20px;
+ bottom: 80px;
+ width: 50px;
+ height: 50px;
+ background-color: #2979ff;
+ border-radius: 25px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+ z-index: 100;
+}
</style>
diff --git a/src/pages/inventoryManagement/stockManagement/index.vue b/src/pages/inventoryManagement/stockManagement/index.vue
index 98ebf44..ee298d6 100644
--- a/src/pages/inventoryManagement/stockManagement/index.vue
+++ b/src/pages/inventoryManagement/stockManagement/index.vue
@@ -9,6 +9,9 @@
<swiper-item class="swiper-item">
<unqualified-record />
</swiper-item>
+ <swiper-item class="swiper-item">
+ <raw-material-record />
+ </swiper-item>
</swiper>
</view>
</template>
@@ -18,11 +21,13 @@
import PageHeader from "@/components/PageHeader.vue";
import QualifiedRecord from "./Qualified.vue";
import UnqualifiedRecord from "./Unqualified.vue";
+import RawMaterialRecord from "./rawMaterialRecord.vue";
const activeTab = ref(0);
const tabs = ref([
- { name: '鍚堟牸搴撳瓨' },
- { name: '涓嶅悎鏍煎簱瀛�' }
+ { name: '鎴愬搧搴撳瓨' },
+ { name: '杈呮潗搴撳瓨' },
+ { name: '鍘熸潗鏂欏簱瀛�' }
]);
const handleTabClick = (item) => {
diff --git a/src/pages/inventoryManagement/stockManagement/rawMaterialRecord.vue b/src/pages/inventoryManagement/stockManagement/rawMaterialRecord.vue
new file mode 100644
index 0000000..aadfe9f
--- /dev/null
+++ b/src/pages/inventoryManagement/stockManagement/rawMaterialRecord.vue
@@ -0,0 +1,151 @@
+<template>
+ <view class="raw-material-record-container">
+ <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
+ />
+ </view>
+ <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" :class="{ 'low-stock': isLowStock(item) }">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.productName }}</text>
+ </view>
+ <view class="item-right">
+ <text class="item-tag tag-type">鍘熸潗鏂欏簱瀛�</text>
+ </view>
+ </view>
+
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">瀛樿揣/瑙勬牸</text>
+ <text class="detail-value">{{item.productName || "鏃�" }} / {{ item.model || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">涓绘暟閲�/涓诲崟浣�</text>
+ <text class="detail-value">{{ item.qualitity || "鏃�" }} / {{ item.unit || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">杈呮暟閲�/杈呭崟浣�</text>
+ <text class="detail-value">{{ item.subQualitity || "鏃�" }} / {{ item.subUnit || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">浠撳簱缂栫爜/浠撳簱鍚嶇О</text>
+ <text class="detail-value">{{ item.warehouseCode || "鏃�" }} / {{ item.warehouseName || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀛樿揣缂栫爜/搴撳瓨棰勮</text>
+ <text class="detail-value">{{ item.productCode || "鏃�" }} / {{ item.warnNum || "鏃�" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏇存柊鏃堕棿</text>
+ <text class="detail-value">{{ item.updateTime || "鏃�" }}</text>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus" />
+ </scroll-view>
+ <view v-else-if="!loading" class="no-data">
+ <up-empty mode="data" text="鏆傛棤鍘熸潗鏂欏簱瀛樻暟鎹�"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js";
+
+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: '', rootName: '鍘熸潗鏂�' });
+
+const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+};
+
+const getList = () => {
+ if (loading.value) return;
+ loading.value = true;
+ loadStatus.value = 'loading';
+ getStockInventoryListPage({ ...searchForm, ...page }).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 === 'nomore' || loading.value) return;
+ page.current++;
+ getList();
+};
+
+const isLowStock = (row) => {
+ const stock = Number(row?.unLockedQuantity ?? 0);
+ const warn = Number(row?.warnNum ?? 0);
+ return Number.isFinite(stock) && Number.isFinite(warn) && stock < warn;
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+@import '@/styles/sales-common.scss';
+
+.raw-material-record-container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.tag-type {
+ background-color: #e8f5e9;
+ color: #4caf50;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+.ledger-list {
+ flex: 1;
+ overflow-y: auto;
+}
+
+.low-stock {
+ background-color: #fde2e2;
+ color: #c45656;
+}
+
+.no-data {
+ padding-top: 100px;
+}
+</style>
diff --git a/src/pages/productionManagement/productionReport/index.vue b/src/pages/productionManagement/productionReport/index.vue
index ab3baf4..8d1e0e9 100644
--- a/src/pages/productionManagement/productionReport/index.vue
+++ b/src/pages/productionManagement/productionReport/index.vue
@@ -11,6 +11,31 @@
error-message-align="right">
<!-- 鍩烘湰淇℃伅 -->
<view class="form-section">
+ <u-form-item label="鏈哄彴"
+ prop="deviceName"
+ required>
+ <u-input v-model="form.deviceName"
+ disabled
+ placeholder="璇疯緭鍏ユ満鍙�" />
+ </u-form-item>
+ <u-form-item label="璁″垝寮�濮嬫椂闂�"
+ prop="planStartTime"
+ required>
+ <u-input v-model="form.planStartTime"
+ placeholder="璇烽�夋嫨璁″垝寮�濮嬫椂闂�"
+ readonly
+ @click="showStartTimePicker = true"
+ suffix-icon="calendar" />
+ </u-form-item>
+ <u-form-item label="璁″垝缁撴潫鏃堕棿"
+ prop="planEndTime"
+ required>
+ <u-input v-model="form.planEndTime"
+ placeholder="璇烽�夋嫨璁″垝缁撴潫鏃堕棿"
+ readonly
+ @click="showEndTimePicker = true"
+ suffix-icon="calendar" />
+ </u-form-item>
<u-form-item label="寰呯敓浜ф暟閲�"
prop="planQuantity"
required>
@@ -57,6 +82,20 @@
title="閫夋嫨鐢熶骇浜�"
@select="onProducerConfirm"
@close="showProducerPicker = false" />
+
+ <!-- 寮�濮嬫椂闂撮�夋嫨鍣� -->
+ <up-datetime-picker :show="showStartTimePicker"
+ v-model="startTimeValue"
+ mode="datetime"
+ @confirm="onStartTimeConfirm"
+ @cancel="showStartTimePicker = false" />
+
+ <!-- 缁撴潫鏃堕棿閫夋嫨鍣� -->
+ <up-datetime-picker :show="showEndTimePicker"
+ v-model="endTimeValue"
+ mode="datetime"
+ @confirm="onEndTimeConfirm"
+ @cancel="showEndTimePicker = false" />
</view>
</template>
@@ -80,6 +119,9 @@
// 琛ㄥ崟鏁版嵁
const form = ref({
+ deviceName: "",
+ planStartTime: "",
+ planEndTime: "",
planQuantity: "",
quantity: "",
scrapQty: "",
@@ -97,6 +139,12 @@
// 鐢熶骇浜洪�夋嫨鍣ㄧ姸鎬�
const showProducerPicker = ref(false);
+ // 鏃堕棿閫夋嫨鍣ㄧ姸鎬�
+ const showStartTimePicker = ref(false);
+ const showEndTimePicker = ref(false);
+ const startTimeValue = ref(Number(new Date()));
+ const endTimeValue = ref(Number(new Date()));
+
const producerList = ref([]);
const currentField = ref(""); // 褰撳墠閫夋嫨鐨勫瓧娈�
@@ -131,6 +179,30 @@
showProducerPicker.value = false;
};
+ // 鏍煎紡鍖栨棩鏈�
+ const formatDateTime = (timestamp) => {
+ const date = new Date(timestamp);
+ const y = date.getFullYear();
+ const m = (date.getMonth() + 1).toString().padStart(2, '0');
+ const d = date.getDate().toString().padStart(2, '0');
+ const h = date.getHours().toString().padStart(2, '0');
+ const min = date.getMinutes().toString().padStart(2, '0');
+ const s = date.getSeconds().toString().padStart(2, '0');
+ return `${y}-${m}-${d} ${h}:${min}:${s}`;
+ };
+
+ // 寮�濮嬫椂闂寸‘璁�
+ const onStartTimeConfirm = (e) => {
+ form.value.planStartTime = formatDateTime(e.value);
+ showStartTimePicker.value = false;
+ };
+
+ // 缁撴潫鏃堕棿纭
+ const onEndTimeConfirm = (e) => {
+ form.value.planEndTime = formatDateTime(e.value);
+ showEndTimePicker.value = false;
+ };
+
// 鎻愪氦鐘舵��
const submitting = ref(false);
@@ -142,6 +214,21 @@
const submitForm = async () => {
submitting.value = true;
// 鏍¢獙琛ㄥ崟
+ if (!form.value.deviceName) {
+ submitting.value = false;
+ showToast("璇疯緭鍏ユ満鍙�");
+ return;
+ }
+ if (!form.value.planStartTime) {
+ submitting.value = false;
+ showToast("璇烽�夋嫨璁″垝寮�濮嬫椂闂�");
+ return;
+ }
+ if (!form.value.planEndTime) {
+ submitting.value = false;
+ showToast("璇烽�夋嫨璁″垝缁撴潫鏃堕棿");
+ return;
+ }
if (!form.value.quantity) {
submitting.value = false;
showToast("璇疯緭鍏ユ湰娆$敓浜ф暟閲�");
@@ -202,10 +289,14 @@
onLoad(options => {
try {
const orderRow = JSON.parse(options.orderRow);
+ console.log(orderRow, "orderRow");
// 纭繚 planQuantity 杞崲涓哄瓧绗︿覆锛屼互渚垮湪 u-input 涓纭樉绀�
form.value.planQuantity = orderRow.planQuantity != null ? String(orderRow.planQuantity) : "";
form.value.productProcessRouteItemId = orderRow.productProcessRouteItemId || "";
form.value.workOrderId = orderRow.id || "";
+ form.value.deviceName = orderRow.deviceName || "";
+ form.value.planStartTime = orderRow.planStartTime || "";
+ form.value.planEndTime = orderRow.planEndTime || "";
getInfo().then(res => {
// 榛樿浣跨敤褰撳墠鐧诲綍鐢ㄦ埛锛屼絾鍏佽鐢ㄦ埛淇敼
form.value.userId.value = res.user.userId;
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 964bd07..e30a14e 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -958,13 +958,12 @@
orderRow = JSON.stringify({
id: workData.id || workOrderId,
planQuantity: workData.planQuantity - workData.completeQuantity,
+ deviceName:workData.deviceName,
productProcessRouteItemId:
workData.productProcessRouteItemId ||
workData.浜у搧宸ヨ壓璺嚎椤笽D ||
"",
});
-
- console.log("鏋勯�犵殑orderRow:", orderRow);
} else {
modal.msgError("鏈壘鍒板搴旂殑宸ュ崟淇℃伅");
return;
--
Gitblit v1.9.3