diff --git a/src/api/supplyCollectionGoods.js b/src/api/supplyCollectionGoods.js
index 5c17d30..e9997d7 100755
--- a/src/api/supplyCollectionGoods.js
+++ b/src/api/supplyCollectionGoods.js
@@ -5,6 +5,7 @@ import { loadGoodsInScenes, sceneValuesObjectFromRaw } from '@/utils/rfSceneGood
import {
pickGoodsCategoryEntry,
pickGoodsDisplayName,
+ pickGoodsNameFromListedRow,
resolveGoodsCategoryCode,
} from '@/utils/goodsCategorys'
@@ -571,7 +572,15 @@ function parseStableSupply(v) {
/** @param {unknown} row */
export function normalizeCollectionGoodsRow(row) {
if (!row || typeof row !== 'object') return null
- const r = row
+ const embedded = row.raw && typeof row.raw === 'object' ? row.raw : null
+ const r = embedded
+ ? {
+ ...embedded,
+ ...row,
+ goods_categorys: row.goods_categorys ?? embedded.goods_categorys,
+ goods: row.goods ?? embedded.goods,
+ }
+ : row
const nested = r.goods && typeof r.goods === 'object' ? r.goods : null
const goods_code = String(
r.goods_code
@@ -600,7 +609,8 @@ export function normalizeCollectionGoodsRow(row) {
).trim()
const name = pickGoodsDisplayName(r, category_code, { allowTopLevelName: true })
|| String(
- r.goods_name
+ r.name
+ || r.goods_name
|| r.goodsName
|| r.product_name
|| r.material_name
@@ -671,15 +681,22 @@ export function pickCollectionGoodsPreferredTax(row) {
export function pickCollectionGoodsPickFields(row) {
const normalized = normalizeCollectionGoodsRow(row) || (row && typeof row === 'object' ? row : null)
if (!normalized) return null
- const raw = normalized.raw || row
+ const sourceRow = normalized.raw || row
const goods_code = String(normalized.goods_code || '').trim()
if (!goods_code) return null
- const payRaw = raw?.payment_price ?? ''
+ const payRaw = sourceRow?.payment_price ?? row?.payment_price ?? ''
const payment_price = payRaw !== '' && payRaw != null ? Number(payRaw) : null
- const bill_tax = pickCollectionGoodsPreferredTax(row)
+ const bill_tax = pickCollectionGoodsPreferredTax(sourceRow)
+ const nameCandidates = [
+ String(row?.name || '').trim(),
+ String(normalized.name || '').trim(),
+ pickGoodsNameFromListedRow(sourceRow),
+ pickGoodsNameFromListedRow(row),
+ ].filter(Boolean)
+ const name = nameCandidates.find((n) => n !== goods_code) || nameCandidates[0] || goods_code
return {
goods_code,
- name: String(normalized.name || goods_code).trim(),
+ name,
payment_price: Number.isFinite(payment_price) ? payment_price : null,
bill_tax,
raw: normalized,
diff --git a/src/api/warehouseAlloc.js b/src/api/warehouseAlloc.js
index 35749eb..366b481 100644
--- a/src/api/warehouseAlloc.js
+++ b/src/api/warehouseAlloc.js
@@ -186,6 +186,23 @@ export async function allocAttrListed(body = {}) {
}
}
+/** POST /api/warehouse/alloc/attr/search — 按商品/RDC/FDC 检索配货属性编码 */
+export async function allocAttrSearch(body = {}) {
+ const payload = buildListedPayload({
+ goods_code: str(body.goods_code) || undefined,
+ rdc_warehouse_code: str(body.rdc_warehouse_code) || undefined,
+ fdc_warehouse_code: str(body.fdc_warehouse_code) || undefined,
+ page: body.page,
+ limit: body.limit,
+ })
+ const res = await request.post('/api/warehouse/alloc/attr/search', payload)
+ const { list, total } = unwrapListed(res)
+ return {
+ list: list.map(normalizeAllocAttrRow).filter(Boolean),
+ total,
+ }
+}
+
/** POST /api/warehouse/alloc/logs/listed */
export async function allocLogsListed(body = {}) {
const res = await request.post('/api/warehouse/alloc/logs/listed', buildListedPayload(body))
diff --git a/src/api/warehouseWaste.js b/src/api/warehouseWaste.js
index 84ee437..b08c9a1 100644
--- a/src/api/warehouseWaste.js
+++ b/src/api/warehouseWaste.js
@@ -198,6 +198,83 @@ export async function wasteLogsAdd(body) {
return res?.data ?? res
}
+function normalizeWasteShareRow(raw) {
+ if (!raw || typeof raw !== 'object') return null
+ const wh = pickRdcFdcPair(raw)
+ const goods = pickGoodsFields(raw)
+ const goods_code = goods.goods_code
+ if (!goods_code && !wh.rdc_warehouse_code) return null
+ const waste_count = num(
+ raw.waste_count
+ ?? raw.rdc_waste_count
+ ?? raw.rdc_waste
+ ?? raw.waste_num,
+ )
+ const rdc_in_count = num(
+ raw.rdc_in_count
+ ?? raw.total_rdc_in
+ ?? raw.rdc_inbound_count
+ ?? raw.in_count,
+ )
+ const share_total_amount = num(
+ raw.total_amount
+ ?? raw.share_total_amount
+ ?? raw.share_amount,
+ )
+ return {
+ ...raw,
+ ...wh,
+ ...goods,
+ goods_display_name: pickGoodsNameFromListedRow(raw) || goods.goods_name,
+ waste_count,
+ rdc_in_count,
+ ratio: num(raw.ratio ?? raw.share_ratio ?? raw.waste_ratio),
+ pre_price: num(raw.pre_price ?? raw.price ?? raw.cost_price),
+ unit_amount: num(raw.unit_amount ?? raw.share_unit_amount ?? raw.unit_share_amount),
+ share_total_amount,
+ share_time: str(raw.share_time ?? raw.amortize_time ?? raw.ctime ?? raw.create_time),
+ day_id: raw.day_id != null && raw.day_id !== '' ? Number(raw.day_id) : undefined,
+ }
+}
+
+/** POST /api/warehouse/waste/share/listed */
+export async function wasteShareListed(body = {}) {
+ const payload = buildListedPayload({
+ goods_code: str(body.goods_code) || undefined,
+ rdc_warehouse_code: str(body.rdc_warehouse_code) || undefined,
+ day_id: body.day_id != null && body.day_id !== '' ? Number(body.day_id) : undefined,
+ page: body.page,
+ limit: body.limit,
+ })
+ const res = await request.post('/api/warehouse/waste/share/listed', payload)
+ const { list, total } = unwrapListed(res)
+ return {
+ list: list.map(normalizeWasteShareRow).filter(Boolean),
+ total,
+ }
+}
+
+/** POST /api/warehouse/waste/amortize */
+export async function wasteAmortize(body) {
+ const payload = {
+ start_time: str(body?.start_time),
+ end_time: str(body?.end_time),
+ }
+ if (!payload.start_time || !payload.end_time) throw new Error('请选择分摊时间段')
+ const res = await request.post('/api/warehouse/waste/amortize', payload, { skipErrorNotify: true })
+ return res?.data ?? res
+}
+
+export function isBenignWasteAmortizeError(err) {
+ const msg = pickApiErrorMessage(err, '')
+ return !msg || /没有|已分摊|无需分摊|无可分摊|暂无|为空/.test(msg)
+}
+
export function pickApiErrorMessage(err, fallback = '操作失败') {
- return String(err?.data?.message || err?.message || fallback).trim() || fallback
+ return String(
+ err?.data?.message
+ ?? err?.data?.data?.message
+ ?? err?.message
+ ?? fallback,
+ ).trim() || fallback
}
diff --git a/src/config/buildId.json b/src/config/buildId.json
index 64c368a..30a9bb1 100755
--- a/src/config/buildId.json
+++ b/src/config/buildId.json
@@ -1,4 +1,4 @@
{
- "buildId": "20260703007",
- "generatedAt": "2026-07-03T09:28:11.726Z"
+ "buildId": "20260706001",
+ "generatedAt": "2026-07-06T04:43:39.914Z"
}
\ No newline at end of file
diff --git a/src/data/appChangelog.js b/src/data/appChangelog.js
index 4293cf1..8d0d1ec 100755
--- a/src/data/appChangelog.js
+++ b/src/data/appChangelog.js
@@ -47,9 +47,49 @@ export const APP_VERSION = '0.7.029'
/** @type {ChangelogDay[]} */
export const APP_CHANGELOG = [
+ {
+ date: '2026-07-06',
+ items: [
+ {
+ icon: 'mdi:cart-outline',
+ kind: 'fix',
+ text: '「采购管理 → 新增采购订单」从统筹商品添加至明细后,物料列正确展示「名称(编码)」;修复二次归一化丢失 `goods_categorys` 物料名、明细仅显示编码的问题。',
+ },
+ {
+ icon: 'mdi:package-variant-closed-minus',
+ kind: 'change',
+ text: '「仓库管理 → 损耗管理 → 新增记录」配货属性编码旁增加「搜索」按钮,弹窗内按物料编码、RDC/FDC 仓查询 `alloc/attr/search` 后点选;主表单展示编码及「物料 · RDC · FDC」说明。',
+ },
+ ],
+ },
+ {
+ date: '2026-07-05',
+ items: [
+ {
+ icon: 'mdi:cog-outline',
+ kind: 'change',
+ text: '系统工程调整:变更说明按日归档,便于发版对照;仓库管理相关 API 封装与字段归一化逻辑补充,提升与后端联调时的数据一致性。',
+ },
+ ],
+ },
+ {
+ date: '2026-07-04',
+ items: [
+ {
+ icon: 'mdi:tune-variant',
+ kind: 'change',
+ text: '系统交互与展示优化:仓库运维及分摊预估类页面统一「进页跑批 / 手动查询 / 重新跑批」职责划分;列表 loading 与空态提示对齐;数字列间距、除物料外仅展示名称等展示规则在各模块间进一步收敛。',
+ },
+ ],
+ },
{
date: '2026-07-03',
items: [
+ {
+ icon: 'mdi:package-variant-closed-minus',
+ kind: 'add',
+ text: '「仓库管理 → 损耗分摊预估」新增页面(`WasteShareListed.vue`,路由 `/warehouse/waste-share`):进页自动 `waste/amortize` 跑批后拉 `waste/share/listed`;筛选分摊时间段、物料编码、RDC 仓库编码;展示损耗数、RDC 入库、占比、定价成本、分摊总额、单件分摊(`is-cols-share-waste`,9 列)。',
+ },
{
icon: 'mdi:help-circle-outline',
kind: 'change',
diff --git a/src/router/index.js b/src/router/index.js
index 57f1711..ff8b65c 100755
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -505,6 +505,16 @@ export const asyncRoutes = [
powerKey: 'warehouse',
},
},
+ {
+ path: 'waste-share',
+ name: 'WarehouseWasteShare',
+ component: () => import('@/views/base/WasteShareListed.vue'),
+ meta: {
+ title: '损耗分摊预估',
+ icon: 'mdi:package-variant-closed-minus',
+ powerKey: 'warehouse',
+ },
+ },
],
},
/*
diff --git a/src/styles/warehouseOpsFlexTable.css b/src/styles/warehouseOpsFlexTable.css
index 0f53058..9c217bd 100644
--- a/src/styles/warehouseOpsFlexTable.css
+++ b/src/styles/warehouseOpsFlexTable.css
@@ -81,6 +81,10 @@
.wops-flex-table.is-cols-share-deploy {
--wops-cols: 14fr 11fr 11fr 5fr 5fr 5fr 4fr 5fr 8fr;
}
+/* 损耗分摊预估(9 列) */
+.wops-flex-table.is-cols-share-waste {
+ --wops-cols: 14fr 11fr 5fr 5fr 4fr 5fr 5fr 5fr 8fr;
+}
/* 公关费用(10 列):费用编码略窄,集团 / 组织各加半份 */
.wops-flex-table.is-cols-pr-cost {
--wops-cols: 7fr 10.5fr 10.5fr 7fr 8fr 8fr 6fr 9fr 6fr 8fr;
@@ -191,6 +195,7 @@
.wops-flex-table.is-cols-share-pr .wops-flex-row,
.wops-flex-table.is-cols-share-alloc .wops-flex-row,
.wops-flex-table.is-cols-share-deploy .wops-flex-row,
+.wops-flex-table.is-cols-share-waste .wops-flex-row,
.wops-flex-table.is-cols-pr-cost .wops-flex-row,
.wops-flex-table.is-cols-collect-whp .wops-flex-row,
.wops-flex-table.is-cols-logs-whp .wops-flex-row,
@@ -211,6 +216,8 @@
.wops-flex-table.is-cols-share-alloc .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-share-deploy .wops-col--num,
.wops-flex-table.is-cols-share-deploy .wops-flex-head .wops-col--num,
+.wops-flex-table.is-cols-share-waste .wops-col--num,
+.wops-flex-table.is-cols-share-waste .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-pr-cost .wops-col--num,
.wops-flex-table.is-cols-pr-cost .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-collect-whp .wops-col--num,
diff --git a/src/views/base/WasteShareListed.vue b/src/views/base/WasteShareListed.vue
new file mode 100644
index 0000000..98d4b37
--- /dev/null
+++ b/src/views/base/WasteShareListed.vue
@@ -0,0 +1,419 @@
+
+ 损耗分摊预估
+ {{ !loading && queried ? `共 ${total} 条` : '' }}
+
{{ addFormAllocAttrLabel }}
未找到匹配的配货属性
+