diff --git a/src/api/mes/productInventory.js b/src/api/mes/productInventory.js index 2806c0b..964a90e 100644 --- a/src/api/mes/productInventory.js +++ b/src/api/mes/productInventory.js @@ -9,4 +9,11 @@ export function getProductInventoryPage(params = {}) { ...params } }) -} \ No newline at end of file +} +export function getProductDetail(id) { + return request({ + url: '/admin-api/erp/product/get', + method: 'get', + params: { id } + }) +} diff --git a/src/pages_function/pages/productInventory/detail.vue b/src/pages_function/pages/productInventory/detail.vue index ed7d96a..ada1f50 100644 --- a/src/pages_function/pages/productInventory/detail.vue +++ b/src/pages_function/pages/productInventory/detail.vue @@ -4,6 +4,29 @@ + + + + + 📦 + + + + {{ t('productInventory.productName') }} + {{ textValue(detail.name || detail.productName) }} + + + {{ t('productInventory.barCode') }} + {{ textValue(detail.barCode || detail.productBarCode) }} + + + {{ t('productInventory.category') }} + {{ textValue(detail.categoryName || detail.subCategoryName) }} + + + + + @@ -13,18 +36,6 @@ - - {{ t('productInventory.barCode') }} - {{ textValue(detail.barCode || detail.productBarCode) }} - - - {{ t('productInventory.productName') }} - {{ textValue(detail.name || detail.productName) }} - - - {{ t('productInventory.category') }} - {{ textValue(detail.categoryName || detail.subCategoryName) }} - {{ t('productInventory.packagingRule') }} {{ textValue(detail.packagingRule) }} @@ -69,6 +80,7 @@ + {{ t('functionCommon.loading') }} @@ -82,7 +94,7 @@ import { computed, ref } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { useI18n } from 'vue-i18n' import NavBar from '@/components/common/NavBar.vue' -import { getProductInventoryPage } from '@/api/mes/productInventory' +import { getProductDetail, getProductInventoryPage } from '@/api/mes/productInventory' const { t } = useI18n() const detail = ref(null) @@ -90,6 +102,12 @@ const loading = ref(true) const stockDisplayList = computed(() => formatStockDisplay(detail.value?.stockDisplay)) const areaStockDisplayList = computed(() => getAreaStockDisplayList(detail.value?.areaStocks)) +const detailImage = computed(() => { + const images = detail.value?.images || detail.value?.image || detail.value?.picUrl + if (!images) return '' + if (Array.isArray(images) && images.length) return String(images[0]) + return String(images).split(',')[0]?.trim() || '' +}) function textValue(value) { if (value === 0) return '0' @@ -158,16 +176,48 @@ function normalizePageData(res) { return Array.isArray(candidateList) ? candidateList : [] } -function applyStockItem(item) { +function normalizeDetailData(res) { + const root = res && res.data !== undefined ? res.data : res + return root?.data || root || {} +} + +function applyStockItem(item, productDetail = {}) { detail.value = { - name: item?.name || item?.productName, - barCode: item?.barCode || item?.productBarCode, - categoryName: item?.categoryName || item?.subCategoryName, + name: item?.name || item?.productName || productDetail?.name, + barCode: item?.barCode || item?.productBarCode || productDetail?.barCode, + categoryName: item?.categoryName || productDetail?.categoryName || productDetail?.subCategoryName, + standard: item?.standard || productDetail?.standard, + remark: item?.remark || productDetail?.remark, + images: productDetail?.images || item?.images, + image: productDetail?.image || item?.image, + picUrl: productDetail?.picUrl || item?.picUrl, + ...productDetail, ...item, stockDisplay: item?.stockDisplay || String(item?.count || '') } } +async function enrichProductDetail(productId) { + try { + const res = await getProductDetail(productId) + const product = normalizeDetailData(res) + if (detail.value) { + detail.value = { + ...product, + ...detail.value, + name: detail.value.name || detail.value.productName || product.name, + barCode: detail.value.barCode || detail.value.productBarCode || product.barCode, + categoryName: detail.value.categoryName || product.categoryName || product.subCategoryName, + standard: detail.value.standard || product.standard, + remark: detail.value.remark || product.remark, + images: detail.value.images || product.images, + image: detail.value.image || product.image, + picUrl: detail.value.picUrl || product.picUrl + } + } + } catch (e) {} +} + onLoad(async (options) => { const id = options?.id || options?.productId const warehouseId = options?.warehouseId @@ -182,22 +232,30 @@ onLoad(async (options) => { if (cached && String(cached.productId || cached.id) === String(id)) { applyStockItem(cached) getApp().globalData._productInventoryDetail = null + await enrichProductDetail(id) loading.value = false return } - const pageRes = await getProductInventoryPage({ - pageNo: 1, - pageSize: 1, - productId: id, - warehouseId: warehouseId || undefined, - areaId: areaId || undefined - }) + const [pageRes, productRes] = await Promise.all([ + getProductInventoryPage({ + pageNo: 1, + pageSize: 1, + productId: id, + warehouseId: warehouseId || undefined, + areaId: areaId || undefined + }), + getProductDetail(id) + ]) const rows = normalizePageData(pageRes) + const product = normalizeDetailData(productRes) if (rows[0]) { - applyStockItem(rows[0]) + applyStockItem(rows[0], product) } else { - detail.value = { stockDisplay: '-' } + detail.value = { + ...product, + stockDisplay: '-' + } } } catch (e) { uni.showToast({ title: t('functionCommon.loadFailed'), icon: 'none' }) @@ -211,7 +269,17 @@ onLoad(async (options) => { .page-container { min-height: 100vh; background: #f5f7fb; } .detail-scroll { height: calc(100vh - 120rpx); } .content-section { padding: 20rpx 24rpx 40rpx; } -.section-card { padding: 24rpx; background: #ffffff; border: 1rpx solid #eef2f7; border-radius: 20rpx; box-shadow: 0 6rpx 18rpx rgba(15, 23, 42, 0.04); } +.detail-card { padding: 28rpx; margin-bottom: 20rpx; background: #ffffff; border-radius: 16rpx; box-shadow: 0 2rpx 12rpx rgba(15, 23, 42, 0.04); } +.info-row-top { display: flex; align-items: center; } +.item-image-wrap { width: 130rpx; height: 130rpx; border-radius: 12rpx; overflow: hidden; background: #f8fafc; border: 1rpx solid #f0f0f0; flex-shrink: 0; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; } +.item-image { width: 100%; height: 100%; } +.item-image-placeholder { font-size: 48rpx; opacity: 0.2; } +.item-info-col { flex: 1; min-width: 0; } +.item-row { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; padding: 8rpx 0; } +.item-row:last-child { padding-bottom: 0; } +.item-label { font-size: 28rpx; color: #9ca3af; flex-shrink: 0; } +.item-value { font-size: 28rpx; color: #374151; text-align: right; word-break: break-all; } +.section-card { padding: 24rpx; margin-bottom: 20rpx; background: #ffffff; border: 1rpx solid #eef2f7; border-radius: 20rpx; box-shadow: 0 6rpx 18rpx rgba(15, 23, 42, 0.04); } .section-header { display: flex; align-items: center; gap: 12rpx; margin-bottom: 22rpx; padding-bottom: 18rpx; border-bottom: 1rpx solid #f1f5f9; } .section-icon { width: 40rpx; height: 40rpx; border-radius: 10rpx; background: #eff6ff; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .section-title { font-size: 32rpx; font-weight: 600; color: #1f2937; } @@ -221,7 +289,6 @@ onLoad(async (options) => { .info-row.block-row { flex-direction: column; gap: 12rpx; } .info-label { width: 190rpx; font-size: 26rpx; color: #94a3b8; flex-shrink: 0; } .info-value { flex: 1; text-align: right; font-size: 27rpx; color: #334155; line-height: 1.5; word-break: break-all; } -.info-value.strong { color: #0f172a; font-weight: 700; } .info-value.highlight { color: #1f4b79; font-weight: 700; } .tag-wrap { width: 100%; display: flex; flex-wrap: wrap; gap: 12rpx; justify-content: flex-end; } .stock-tag { max-width: 100%; padding: 8rpx 16rpx; border-radius: 999rpx; color: #1d4ed8; background: #dbeafe; font-size: 24rpx; line-height: 1.35; word-break: break-all; } diff --git a/src/pages_function/pages/productInventory/index.vue b/src/pages_function/pages/productInventory/index.vue index 59ce481..b22d5e2 100644 --- a/src/pages_function/pages/productInventory/index.vue +++ b/src/pages_function/pages/productInventory/index.vue @@ -31,12 +31,12 @@ - {{ textValue(item.barCode || item.productBarCode) }} + {{ textValue(item.name || item.productName) }} - {{ t('productInventory.productName') }} - {{ textValue(item.name || item.productName) }} + {{ t('productInventory.barCode') }} + {{ textValue(item.barCode || item.productBarCode) }} {{ t('productInventory.stockDisplay') }} @@ -46,14 +46,6 @@ {{ t('productInventory.count') }} {{ formatStockCount(item.count) }}{{ textUnit(item.unitName) }} - - {{ t('productInventory.latestInTime') }} - {{ formatDateTime(getLatestInTime(item)) }} - - - {{ t('productInventory.latestOutTime') }} - {{ formatDateTime(getLatestOutTime(item)) }} - @@ -121,37 +113,6 @@ function formatStockCount(value) { return Number.isFinite(num) ? num.toLocaleString() : String(value) } -function formatDateTime(value) { - if (!value) return '-' - if (Array.isArray(value) && value.length >= 3) { - const [year, month, day, hour = 0, minute = 0, second = 0] = value - const pad = (n) => String(n).padStart(2, '0') - return `${year}-${pad(month)}-${pad(day)} ${pad(hour)}:${pad(minute)}:${pad(second)}` - } - const text = String(value).trim() - if (!text) return '-' - const numeric = Number(text) - if (Number.isFinite(numeric)) { - const timestamp = text.length === 10 ? numeric * 1000 : numeric - const date = new Date(timestamp) - if (!Number.isNaN(date.getTime())) return formatDate(date) - } - const date = new Date(text) - return Number.isNaN(date.getTime()) ? text : formatDate(date) -} - -function formatDate(date) { - const pad = (n) => String(n).padStart(2, '0') - return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` -} - -function getLatestInTime(row) { - return row?.latestInTime ?? row?.lastInTime ?? row?.recentInTime ?? row?.latestStockInTime ?? row?.lastStockInTime -} - -function getLatestOutTime(row) { - return row?.latestOutTime ?? row?.lastOutTime ?? row?.recentOutTime ?? row?.latestStockOutTime ?? row?.lastStockOutTime -} function itemKey(item) { return [item?.id, item?.productId, item?.warehouseId, item?.areaId].filter((value) => value !== undefined && value !== null).join('_')