diff --git a/inventory-backend/app/api/v1/inbound/stock.py b/inventory-backend/app/api/v1/inbound/stock.py
index 0b612b4..64f78e9 100644
--- a/inventory-backend/app/api/v1/inbound/stock.py
+++ b/inventory-backend/app/api/v1/inbound/stock.py
@@ -2079,23 +2079,32 @@ def get_draft_merged_list():
# 否则工人扫到货架上账面为 0 的实物(盘盈)时,「已盘」计数会 +1,
# 明细里却找不到这一行,看起来像系统丢了数据。
# 注意条件必须限定在**本会话**的草稿,不能是库容里存在过草稿就放行。
+ # ★ 批次/序列号:stock_product 表**没有 batch_number 列**(只有 serial_number),
+ # 所以第三个分支必须给 batch_number 补一个空串占位,否则 UNION 两边
+ # 列数不齐 / 报 column does not exist。前端据此显示为 '-'。
union_sql = """
SELECT id, 'stock_buy' AS source_table, sku,
- stock_quantity AS stock_qty, warehouse_location, base_id
+ stock_quantity AS stock_qty, warehouse_location, base_id,
+ COALESCE(batch_number, '') AS batch_number,
+ COALESCE(serial_number, '') AS serial_number
FROM stock_buy
WHERE stock_quantity > 0
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
WHERE sd.source_table = 'stock_buy' AND sd.session_id = :sid)
UNION ALL
SELECT id, 'stock_semi', sku,
- stock_quantity, warehouse_location, base_id
+ stock_quantity, warehouse_location, base_id,
+ COALESCE(batch_number, ''),
+ COALESCE(serial_number, '')
FROM stock_semi
WHERE stock_quantity > 0
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
WHERE sd.source_table = 'stock_semi' AND sd.session_id = :sid)
UNION ALL
SELECT id, 'stock_product', sku,
- stock_quantity, warehouse_location, base_id
+ stock_quantity, warehouse_location, base_id,
+ '' AS batch_number, -- stock_product 无 batch_number 列
+ COALESCE(serial_number, '')
FROM stock_product
WHERE stock_quantity > 0
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
@@ -2156,6 +2165,7 @@ def get_draft_merged_list():
data_sql = f"""
SELECT
cs.id AS stock_id, cs.source_table, cs.sku,
+ cs.batch_number, cs.serial_number,
cs.stock_qty, cs.warehouse_location,
mb.name AS mat_name, mb.spec_model AS mat_spec,
sd.id AS draft_id, sd.quantity AS draft_qty,
@@ -2193,6 +2203,8 @@ def get_draft_merged_list():
'source_table': row.source_table,
'uniqueKey': f"{row.source_table}_{row.stock_id}",
'sku': row.sku or '',
+ 'batch_number': row.batch_number or '',
+ 'serial_number': row.serial_number or '',
'material_name': row.mat_name or '',
'spec_model': row.mat_spec or '',
# ★ 盲盘:账面数置空,真实值不得出现在 HTTP 响应里
@@ -2371,6 +2383,8 @@ def update_stocktake_quantity():
source_table = data.get('source_table')
session_id = data.get('session_id') # ★ 修复:按会话隔离,防止跨会话误改
quantity = float(data.get('quantity', 0))
+ # 备注为**可选**字段:只有显式传入时才覆盖,避免改实盘数时把备注清空
+ remark = data.get('remark', None)
if not stock_id or not source_table:
return jsonify({'code': 400, 'msg': '缺少必要参数'}), 400
@@ -2406,6 +2420,9 @@ def update_stocktake_quantity():
# 计算差异(★ 这是写库计算,不是报表输出,绝不能用 _qty —— 那会在盲盘时
# 返回占位符 '—' 导致减法报错。账面数在这里始终按真实值参与计算。)
draft.diff_qty = quantity - float(draft.stock_qty or 0)
+
+ if remark is not None:
+ draft.remark = remark.strip() if isinstance(remark, str) else remark
db.session.commit()
diff --git a/inventory-web/src/api/inbound/stock.ts b/inventory-web/src/api/inbound/stock.ts
index 0226a47..44ee6c6 100644
--- a/inventory-web/src/api/inbound/stock.ts
+++ b/inventory-web/src/api/inbound/stock.ts
@@ -125,7 +125,7 @@ export function getAllStocktakeItems(params?: {
// 更新盘点实盘数(手动修改)
// company 走 query string(后端 get_current_company_filter 只读 request.args,不读 body)
export function updateStocktakeQuantity(
- data: { stock_id: number; source_table: string; quantity: number; session_id?: string },
+ data: { stock_id: number; source_table: string; quantity: number; session_id?: string; remark?: string },
company?: string
) {
return request({
diff --git a/inventory-web/src/views/stock/stocktake/index.vue b/inventory-web/src/views/stock/stocktake/index.vue
index dddd2dc..1a7ecea 100644
--- a/inventory-web/src/views/stock/stocktake/index.vue
+++ b/inventory-web/src/views/stock/stocktake/index.vue
@@ -443,17 +443,25 @@
style="width: 100%"
:row-class-name="(row) => row._justScanned ? 'just-scanned-row' : ''"
>
-
+
+
+ {{ row.batch_number || '-' }}
+
+
+ {{ row.serial_number || '-' }}
+
-
-
-
+
+
+
-
+
+
—
{{ row.stock_qty }}
@@ -479,7 +487,7 @@
/>
-
+
—
@@ -487,9 +495,24 @@
-
+
+
-
+ handleRemarkChange(row)"
+ >
+
+
@@ -774,6 +797,9 @@ const creatingNew = ref(false) // 已有活跃会话时,是否展开创建
const newMode = ref<'open' | 'blind'>('open') // 盘点模式,默认明盘
const newScopeType = ref<'full' | 'active'>('full') // 盘点范围,默认全仓
+// 备注高频选项:可直接点选,也可在输入框里创建新条目(el-select allow-create)
+const REMARK_OPTIONS = ['包装破损', '找不到实物', '标签丢失', '账实不符', '实物变质']
+
const MODE_LABELS: Record = { open: '明盘', blind: '盲盘' }
const SCOPE_LABELS: Record = { full: '全仓盘点', active: '活跃库位抽盘' }
const modeLabel = (m?: string | null) => (m ? MODE_LABELS[m] || m : '')
@@ -1680,10 +1706,24 @@ const handleQuantityChange = async (row: any, val: number) => {
}
// ★ 新增: 更新备注
+// ★ 备注保存。原实现只 console.log,备注**根本没落库** —— 填了等于没填。
+// 现复用 update-quantity(后端已支持可选 remark),quantity 必填故一并带上。
const handleRemarkChange = async (row: any) => {
- // 备注更新可以批量处理或直接调用后端接口
- // 这里暂时只更新本地数据,实际项目中可以调用后端保存
- console.log('备注更新:', row.remark, row.id)
+ if (!row.draft_id) return // 未扫过的行没有草稿记录可挂
+ try {
+ await updateStocktakeQuantity({
+ stock_id: row.stock_id,
+ source_table: row.source_table,
+ quantity: Number(row.quantity || 0),
+ session_id: currentSessionId.value,
+ remark: row.remark || ''
+ }, selectedCompany.value || undefined)
+ ElMessage.success('备注已保存')
+ } catch (e: any) {
+ console.error('保存备注失败', e)
+ ElMessage.error(e?.response?.data?.msg || e?.msg || '备注保存失败,已还原')
+ fetchInventoryList(true)
+ }
}
// ★ 修改:结束盘点按钮直接调用 finishStocktake,跳过二次确认弹窗