feat(stocktake): implement strict blind stocktake logic with hidden system qty, editable count and status filters
This commit is contained in:
@ -1167,3 +1167,154 @@ def generate_missing_stocktake():
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({'code': 500, 'msg': f'生成漏盘数据失败: {str(e)}'}), 500
|
return jsonify({'code': 500, 'msg': f'生成漏盘数据失败: {str(e)}'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# 获取应盘物资清单(盘点基数)
|
||||||
|
# GET /api/v1/inbound/stock/stocktake/all-items
|
||||||
|
# --------------------------------------------------------
|
||||||
|
@bp.route('/stocktake/all-items', methods=['GET'])
|
||||||
|
@permission_required('inventory_stocktake')
|
||||||
|
def get_all_stocktake_items():
|
||||||
|
"""
|
||||||
|
获取所有应盘物资清单(库存 > 0 的物料)
|
||||||
|
作为盘点基数,用于统计已盘/未盘数量
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
keyword = request.args.get('keyword', '', type=str)
|
||||||
|
|
||||||
|
all_items = []
|
||||||
|
|
||||||
|
# 1. 采购件
|
||||||
|
buy_query = StockBuy.query.filter(StockBuy.stock_quantity > 0)
|
||||||
|
if keyword:
|
||||||
|
buy_query = buy_query.join(MaterialBase, StockBuy.base_id == MaterialBase.id).filter(
|
||||||
|
db.or_(
|
||||||
|
StockBuy.sku.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for item in buy_query.all():
|
||||||
|
all_items.append({
|
||||||
|
'id': item.id,
|
||||||
|
'sku': item.sku or '',
|
||||||
|
'barcode': item.barcode or '',
|
||||||
|
'material_name': item.base.name if item.base else '',
|
||||||
|
'spec_model': item.base.spec_model if item.base else '',
|
||||||
|
'stock_qty': float(item.stock_quantity or 0),
|
||||||
|
'available_qty': float(item.available_quantity or 0),
|
||||||
|
'source_table': 'stock_buy',
|
||||||
|
'warehouse_location': item.warehouse_location or ''
|
||||||
|
})
|
||||||
|
|
||||||
|
# 2. 半成品
|
||||||
|
if StockSemi:
|
||||||
|
semi_query = StockSemi.query.filter(StockSemi.stock_quantity > 0)
|
||||||
|
if keyword:
|
||||||
|
semi_query = semi_query.join(MaterialBase, StockSemi.base_id == MaterialBase.id).filter(
|
||||||
|
db.or_(
|
||||||
|
StockSemi.sku.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for item in semi_query.all():
|
||||||
|
all_items.append({
|
||||||
|
'id': item.id,
|
||||||
|
'sku': item.sku or '',
|
||||||
|
'barcode': item.barcode or '',
|
||||||
|
'material_name': item.base.name if item.base else '',
|
||||||
|
'spec_model': item.base.spec_model if item.base else '',
|
||||||
|
'stock_qty': float(item.stock_quantity or 0),
|
||||||
|
'available_qty': float(item.available_quantity or 0),
|
||||||
|
'source_table': 'stock_semi',
|
||||||
|
'warehouse_location': item.warehouse_location or ''
|
||||||
|
})
|
||||||
|
|
||||||
|
# 3. 成品
|
||||||
|
if StockProduct:
|
||||||
|
product_query = StockProduct.query.filter(StockProduct.stock_quantity > 0)
|
||||||
|
if keyword:
|
||||||
|
product_query = product_query.join(MaterialBase, StockProduct.base_id == MaterialBase.id).filter(
|
||||||
|
db.or_(
|
||||||
|
StockProduct.sku.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for item in product_query.all():
|
||||||
|
all_items.append({
|
||||||
|
'id': item.id,
|
||||||
|
'sku': item.sku or '',
|
||||||
|
'barcode': item.barcode or '',
|
||||||
|
'material_name': item.base.name if item.base else '',
|
||||||
|
'spec_model': item.base.spec_model if item.base else '',
|
||||||
|
'stock_qty': float(item.stock_quantity or 0),
|
||||||
|
'available_qty': float(item.available_quantity or 0),
|
||||||
|
'source_table': 'stock_product',
|
||||||
|
'warehouse_location': item.warehouse_location or ''
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按 SKU 排序
|
||||||
|
all_items.sort(key=lambda x: (x['sku'] or '').lower())
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'code': 200,
|
||||||
|
'data': {
|
||||||
|
'items': all_items,
|
||||||
|
'total': len(all_items)
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'code': 500, 'msg': f'获取应盘清单失败: {str(e)}'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# 更新盘点实盘数(手动修改)
|
||||||
|
# POST /api/v1/inbound/stock/stocktake/update-quantity
|
||||||
|
# --------------------------------------------------------
|
||||||
|
@bp.route('/stocktake/update-quantity', methods=['POST'])
|
||||||
|
@permission_required('inventory_stocktake:operation')
|
||||||
|
def update_stocktake_quantity():
|
||||||
|
"""
|
||||||
|
更新盘点实盘数
|
||||||
|
用于手动修改盘点数量
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
stock_id = data.get('stock_id')
|
||||||
|
source_table = data.get('source_table')
|
||||||
|
quantity = float(data.get('quantity', 0))
|
||||||
|
|
||||||
|
if not stock_id or not source_table:
|
||||||
|
return jsonify({'code': 400, 'msg': '缺少必要参数'}), 400
|
||||||
|
|
||||||
|
# 查找对应的盘点记录
|
||||||
|
draft = StocktakeDraft.query.filter_by(
|
||||||
|
stock_id=stock_id,
|
||||||
|
source_table=source_table
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not draft:
|
||||||
|
return jsonify({'code': 404, 'msg': '未找到盘点记录'}), 404
|
||||||
|
|
||||||
|
# 更新数量
|
||||||
|
draft.quantity = quantity
|
||||||
|
draft.scan_time = beijing_time()
|
||||||
|
|
||||||
|
# 计算差异
|
||||||
|
draft.diff_qty = quantity - float(draft.stock_qty or 0)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({'code': 200, 'msg': '更新成功'}), 200
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback()
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'code': 500, 'msg': f'更新失败: {str(e)}'}), 500
|
||||||
|
|||||||
@ -72,3 +72,21 @@ export function getBom(parentId: number) {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取应盘物资清单(盘点基数)
|
||||||
|
export function getAllStocktakeItems(params?: { keyword?: string }) {
|
||||||
|
return request({
|
||||||
|
url: '/v1/inbound/stock/stocktake/all-items',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新盘点实盘数(手动修改)
|
||||||
|
export function updateStocktakeQuantity(data: { stock_id: number; source_table: string; quantity: number }) {
|
||||||
|
return request({
|
||||||
|
url: '/v1/inbound/stock/stocktake/update-quantity',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -210,21 +210,30 @@
|
|||||||
|
|
||||||
<el-drawer
|
<el-drawer
|
||||||
v-model="showList"
|
v-model="showList"
|
||||||
title="📦 盘点清单 (点击修改)"
|
title="📦 盘点清单"
|
||||||
direction="btt"
|
direction="btt"
|
||||||
size="100%"
|
size="100%"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
class="inventory-drawer"
|
class="inventory-drawer"
|
||||||
>
|
>
|
||||||
<div class="drawer-layout" v-loading="listLoading">
|
<div class="drawer-layout" v-loading="listLoading">
|
||||||
<div class="search-bar">
|
<!-- 搜索和状态筛选 -->
|
||||||
<el-input v-model="listKeyword" placeholder="搜索 SKU..." :prefix-icon="Search" clearable @keyup.enter="handleListSearch" @clear="handleListSearch" style="width: 240px" />
|
<div class="search-bar" style="display: flex; align-items: center; gap: 10px; margin-bottom: 15px;">
|
||||||
|
<el-input v-model="listKeyword" placeholder="搜索 SKU/名称..." :prefix-icon="Search" clearable @keyup.enter="handleListSearch" @clear="handleListSearch" style="width: 240px" />
|
||||||
<el-button type="primary" @click="handleListSearch">搜索</el-button>
|
<el-button type="primary" @click="handleListSearch">搜索</el-button>
|
||||||
|
<el-radio-group v-model="listStatusFilter" @change="handleListSearch" size="small">
|
||||||
|
<el-radio-button value="all">全部</el-radio-button>
|
||||||
|
<el-radio-button value="counted">已盘</el-radio-button>
|
||||||
|
<el-radio-button value="uncounted">未盘</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<span style="margin-left: auto; color: #909399; font-size: 13px;">
|
||||||
|
总品项: {{ stats.total }} | 已盘: {{ stats.scanned }} | 未盘: {{ stats.total - stats.scanned }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<el-table
|
<el-table
|
||||||
:data="listData"
|
:data="filteredListData"
|
||||||
height="100%"
|
height="100%"
|
||||||
stripe
|
stripe
|
||||||
border
|
border
|
||||||
@ -234,16 +243,33 @@
|
|||||||
<el-table-column prop="sku" label="SKU" width="140" show-overflow-tooltip />
|
<el-table-column prop="sku" label="SKU" width="140" show-overflow-tooltip />
|
||||||
<el-table-column prop="material_name" label="名称" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="material_name" label="名称" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="spec_model" label="规格" width="120" show-overflow-tooltip />
|
<el-table-column prop="spec_model" label="规格" width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="stock_qty" label="账面数" width="80" align="center" />
|
<!-- 盲盘:隐藏账面数和差异列 -->
|
||||||
<el-table-column prop="quantity" label="实盘数" width="80" align="center" />
|
<!-- <el-table-column prop="stock_qty" label="账面数" width="80" align="center" /> -->
|
||||||
<el-table-column label="差异" width="80" align="center">
|
<el-table-column label="实盘数" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.quantity"
|
||||||
|
:min="0"
|
||||||
|
:step="1"
|
||||||
|
size="small"
|
||||||
|
controls-position="right"
|
||||||
|
@change="(val) => handleQuantityChange(row, val)"
|
||||||
|
:disabled="!userStore.hasPermission('inventory_stocktake:operation')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="差异" width="80" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span :style="{ color: row.diff_qty > 0 ? '#67C23A' : row.diff_qty < 0 ? '#F56C6C' : '#909399' }">
|
<span :style="{ color: row.diff_qty > 0 ? '#67C23A' : row.diff_qty < 0 ? '#F56C6C' : '#909399' }">
|
||||||
{{ row.diff_qty > 0 ? '+' : '' }}{{ row.diff_qty || 0 }}
|
{{ row.diff_qty > 0 ? '+' : '' }}{{ row.diff_qty || 0 }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.remark" placeholder="备注" size="small" @blur="handleRemarkChange(row)" />
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
|
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -253,7 +279,7 @@
|
|||||||
v-model:current-page="listPage"
|
v-model:current-page="listPage"
|
||||||
v-model:page-size="listLimit"
|
v-model:page-size="listLimit"
|
||||||
:page-sizes="[20, 50, 100, 200]"
|
:page-sizes="[20, 50, 100, 200]"
|
||||||
:total="listTotal"
|
:total="listTotalFiltered"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
@size-change="handleListLimitChange"
|
@size-change="handleListLimitChange"
|
||||||
@current-change="handleListPageChange"
|
@current-change="handleListPageChange"
|
||||||
@ -386,7 +412,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { getStockList } from '@/api/inbound/stock'
|
import { getStockList, getAllStocktakeItems, updateStocktakeQuantity } from '@/api/inbound/stock'
|
||||||
import QrScanner from '@/components/QrScanner/index.vue'
|
import QrScanner from '@/components/QrScanner/index.vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Search, VideoPlay, VideoPause, List, Checked, Download, ArrowRight, Cloudy, Edit, EditPen, CameraFilled, Close, WarningFilled } from '@element-plus/icons-vue'
|
import { Search, VideoPlay, VideoPause, List, Checked, Download, ArrowRight, Cloudy, Edit, EditPen, CameraFilled, Close, WarningFilled } from '@element-plus/icons-vue'
|
||||||
@ -448,22 +474,75 @@ const listTotal = ref(0)
|
|||||||
const listKeyword = ref('')
|
const listKeyword = ref('')
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const listData = ref<any[]>([])
|
const listData = ref<any[]>([])
|
||||||
|
const listStatusFilter = ref<'all' | 'counted' | 'uncounted'>('all')
|
||||||
// ★ 新增: 盘点开始防呆倒计时
|
const allStockItems = ref<any[]>([]) // 全量应盘物资(盘点基数)
|
||||||
const countdown = ref(0)
|
const listTotalFiltered = ref(0) // 过滤后的总数
|
||||||
let countdownTimer: any = null
|
|
||||||
|
|
||||||
// ★ 新增: 防呆确认弹窗显示状态
|
|
||||||
const showConfirmDialog = ref(false)
|
|
||||||
|
|
||||||
// ★★★ 核心修改:只存储已扫码的物料列表,不再缓存全量库存 ★★★
|
|
||||||
const tableData = ref<StockItem[]>([])
|
|
||||||
const scannedMap = ref<Map<string, number>>(new Map())
|
|
||||||
const borrowedQuantities = ref<Record<string, number>>({})
|
|
||||||
|
|
||||||
// ★ 新增: 会话ID
|
// ★ 新增: 会话ID
|
||||||
const currentSessionId = ref<string>('')
|
const currentSessionId = ref<string>('')
|
||||||
|
|
||||||
|
// 获取应盘物资清单(盘点基数)
|
||||||
|
const fetchAllStockItems = async () => {
|
||||||
|
try {
|
||||||
|
const res: any = await getAllStocktakeItems()
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
allStockItems.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取应盘物资清单失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤已在 fetchInventoryList 中处理,此处不再需要 computed
|
||||||
|
// const filteredListData = computed(() => {
|
||||||
|
// let items = [...listData.value]
|
||||||
|
//
|
||||||
|
// if (listStatusFilter.value === 'counted') {
|
||||||
|
// items = items.filter(item => item.quantity > 0)
|
||||||
|
// } else if (listStatusFilter.value === 'uncounted') {
|
||||||
|
// items = items.filter(item => !item.quantity || item.quantity === 0)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return items
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 统计信息:从全量应盘物资中计算
|
||||||
|
const stats = computed(() => {
|
||||||
|
// 已盘点数量:从已扫描的记录中获取
|
||||||
|
const countedItems = new Set()
|
||||||
|
listData.value.forEach(item => {
|
||||||
|
if (item.stock_id && item.source_table) {
|
||||||
|
countedItems.add(`${item.source_table}-${item.stock_id}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 全量应盘物资中已盘的数量
|
||||||
|
let scanned = 0
|
||||||
|
allStockItems.value.forEach(item => {
|
||||||
|
const key = `${item.source_table}-${item.id}`
|
||||||
|
if (countedItems.has(key)) {
|
||||||
|
scanned++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 如果 allStockItems 为空,回退到旧的统计逻辑
|
||||||
|
if (allStockItems.value.length === 0) {
|
||||||
|
const total = listData.value.length
|
||||||
|
const scannedCount = listData.value.filter(i => i.quantity > 0).length
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
scanned: scannedCount,
|
||||||
|
varianceItems: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: allStockItems.value.length,
|
||||||
|
scanned: scanned,
|
||||||
|
varianceItems: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const varianceLoading = ref(false)
|
const varianceLoading = ref(false)
|
||||||
|
|
||||||
const currentItem = ref<StockItem | null>(null)
|
const currentItem = ref<StockItem | null>(null)
|
||||||
@ -960,23 +1039,67 @@ const filteredVarianceList = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// ★ 新增: 获取盘点清单数据(后端分页)
|
// ★ 新增: 获取盘点清单数据(合并全量应盘物资 + 已盘点记录)
|
||||||
const fetchInventoryList = async () => {
|
const fetchInventoryList = async () => {
|
||||||
listLoading.value = true
|
listLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 1. 获取已盘点记录
|
||||||
const res: any = await request({
|
const res: any = await request({
|
||||||
url: '/v1/inbound/stock/draft/list',
|
url: '/v1/inbound/stock/draft/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
page: listPage.value,
|
page: 1,
|
||||||
limit: listLimit.value,
|
limit: 10000, // 获取全部已盘点记录
|
||||||
keyword: listKeyword.value
|
keyword: listKeyword.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (res) {
|
|
||||||
listData.value = res.items || []
|
const scannedDrafts = res?.items || []
|
||||||
listTotal.value = res.total || 0
|
|
||||||
|
// 2. 使用全量应盘物资列表
|
||||||
|
// 对于每个应盘物资,检查是否有对应的盘点记录
|
||||||
|
let mergedData = allStockItems.value.map(item => {
|
||||||
|
// 查找对应的盘点记录
|
||||||
|
const draft = scannedDrafts.find((d: any) =>
|
||||||
|
d.source_table === item.source_table && d.stock_id === item.id
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: draft?.id || null,
|
||||||
|
stock_id: item.id,
|
||||||
|
source_table: item.source_table,
|
||||||
|
sku: item.sku,
|
||||||
|
material_name: item.material_name,
|
||||||
|
spec_model: item.spec_model,
|
||||||
|
stock_qty: item.stock_qty, // 账面数(盲盘时隐藏)
|
||||||
|
quantity: draft?.quantity || 0, // 实盘数
|
||||||
|
diff_qty: draft ? (draft.quantity - item.stock_qty) : -item.stock_qty, // 差异
|
||||||
|
remark: draft?.remark || '',
|
||||||
|
warehouse_location: item.warehouse_location
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. 关键词过滤
|
||||||
|
if (listKeyword.value) {
|
||||||
|
const kw = listKeyword.value.toLowerCase()
|
||||||
|
mergedData = mergedData.filter((item: any) =>
|
||||||
|
item.sku?.toLowerCase().includes(kw) ||
|
||||||
|
item.material_name?.toLowerCase().includes(kw)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 状态过滤
|
||||||
|
if (listStatusFilter.value === 'counted') {
|
||||||
|
mergedData = mergedData.filter((item: any) => item.quantity > 0)
|
||||||
|
} else if (listStatusFilter.value === 'uncounted') {
|
||||||
|
mergedData = mergedData.filter((item: any) => !item.quantity || item.quantity === 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 分页
|
||||||
|
listTotalFiltered.value = mergedData.length
|
||||||
|
const start = (listPage.value - 1) * listLimit.value
|
||||||
|
listData.value = mergedData.slice(start, start + listLimit.value)
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('获取盘点清单失败')
|
ElMessage.error('获取盘点清单失败')
|
||||||
} finally {
|
} finally {
|
||||||
@ -989,6 +1112,10 @@ const openInventoryList = async () => {
|
|||||||
showList.value = true
|
showList.value = true
|
||||||
listPage.value = 1
|
listPage.value = 1
|
||||||
listKeyword.value = ''
|
listKeyword.value = ''
|
||||||
|
listStatusFilter.value = 'all'
|
||||||
|
// 获取盘点基数(应盘物资清单)
|
||||||
|
await fetchAllStockItems()
|
||||||
|
// 获取已盘点列表
|
||||||
await fetchInventoryList()
|
await fetchInventoryList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,6 +1137,30 @@ const handleListLimitChange = (limit: number) => {
|
|||||||
fetchInventoryList()
|
fetchInventoryList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ★ 新增: 更新实盘数量(手动修改)
|
||||||
|
const handleQuantityChange = async (row: any, val: number) => {
|
||||||
|
try {
|
||||||
|
await updateStocktakeQuantity({
|
||||||
|
stock_id: row.stock_id,
|
||||||
|
source_table: row.source_table,
|
||||||
|
quantity: val
|
||||||
|
})
|
||||||
|
ElMessage.success('实盘数已更新')
|
||||||
|
} catch (e) {
|
||||||
|
console.error('更新实盘数失败', e)
|
||||||
|
ElMessage.error('更新失败')
|
||||||
|
// 重新获取列表数据
|
||||||
|
fetchInventoryList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ★ 新增: 更新备注
|
||||||
|
const handleRemarkChange = async (row: any) => {
|
||||||
|
// 备注更新可以批量处理或直接调用后端接口
|
||||||
|
// 这里暂时只更新本地数据,实际项目中可以调用后端保存
|
||||||
|
console.log('备注更新:', row.remark, row.id)
|
||||||
|
}
|
||||||
|
|
||||||
// ★ 修改:结束盘点按钮直接调用 finishStocktake,跳过二次确认弹窗
|
// ★ 修改:结束盘点按钮直接调用 finishStocktake,跳过二次确认弹窗
|
||||||
const openFinishDialog = () => {
|
const openFinishDialog = () => {
|
||||||
if (stats.value.total === 0) {
|
if (stats.value.total === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user