feat: enhance stocktake UX with 10s delay, sku search, sku retention and remove adjust btn

This commit is contained in:
DXC
2026-03-19 11:10:00 +08:00
parent d1f54042bf
commit 44bd23ab77

View File

@ -111,6 +111,32 @@
</div> </div>
</el-card> </el-card>
<!-- 新增: 防呆确认弹窗 -->
<el-dialog
v-model="showConfirmDialog"
title="⚠️ 确认清除盘点数据"
width="400"
:close-on-click-modal="false"
:close-on-press-escape="false"
show-close
align-center
>
<div class="confirm-content">
<el-icon :size="48" color="#E6A23C"><WarningFilled /></el-icon>
<p class="confirm-text">存在未完成记录开始新盘点将清除它们确定吗</p>
</div>
<template #footer>
<el-button @click="cancelConfirm">取消</el-button>
<el-button
type="danger"
:disabled="countdown > 0"
@click="confirmClear"
>
{{ countdown > 0 ? `确认清除 (${countdown}s)` : '确认清除' }}
</el-button>
</template>
</el-dialog>
<el-dialog <el-dialog
v-model="showQtyDialog" v-model="showQtyDialog"
title="🔢 录入实盘数量" title="🔢 录入实盘数量"
@ -266,12 +292,24 @@
style="margin-bottom: 15px;" style="margin-bottom: 15px;"
> >
<template #default> <template #default>
以下列表显示所有已结束盘点但尚未平账的差异记录逐条核实后点击"确认平账"按钮调整系统库存 以下列表显示所有已结束盘点但尚未平账的差异记录请核实后进行后续处理
</template> </template>
</el-alert> </el-alert>
<!-- 新增: 差异列表搜索 -->
<el-input
v-model="searchSku"
placeholder="输入 SKU/条码/名称快速定位"
clearable
style="width: 250px; margin-bottom: 15px;"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-table <el-table
:data="varianceList" :data="filteredVarianceList"
height="500" height="500"
border border
stripe stripe
@ -315,22 +353,9 @@
<el-tag v-else type="warning">待审核</el-tag> <el-tag v-else type="warning">待审核</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="!scope.row.is_processed && userStore.hasPermission('inventory_stocktake:operation')"
type="primary"
size="small"
@click="handleAdjust(scope.row)"
>
确认平账
</el-button>
<span v-else class="text-gray">-</span>
</template>
</el-table-column>
</el-table> </el-table>
<el-empty v-if="varianceList.length === 0" description="暂无待审核的差异记录" /> <el-empty v-if="filteredVarianceList.length === 0" :description="searchSku ? '未找到匹配的差异记录' : '暂无待审核的差异记录'" />
</div> </div>
<template #footer> <template #footer>
@ -353,7 +378,7 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { getAllStock } from '@/api/inbound/stock' import { getAllStock } 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 } from '@element-plus/icons-vue' import { Search, VideoPlay, VideoPause, List, Checked, Download, ArrowRight, Cloudy, Edit, EditPen, CameraFilled, Close, WarningFilled } from '@element-plus/icons-vue'
import request from '@/utils/request' import request from '@/utils/request'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
@ -402,6 +427,16 @@ const showQtyDialog = ref(false)
// ★ 新增: 差异审核对话框 // ★ 新增: 差异审核对话框
const showVarianceDialog = ref(false) const showVarianceDialog = ref(false)
// ★ 新增: 差异列表搜索
const searchSku = ref('')
// ★ 新增: 盘点开始防呆倒计时
const countdown = ref(0)
let countdownTimer: any = null
// ★ 新增: 防呆确认弹窗显示状态
const showConfirmDialog = ref(false)
const allData = ref<StockItem[]>([]) const allData = ref<StockItem[]>([])
const scannedMap = ref<Map<string, number>>(new Map()) const scannedMap = ref<Map<string, number>>(new Map())
const borrowedQuantities = ref<Record<string, number>>({}) const borrowedQuantities = ref<Record<string, number>>({})
@ -492,19 +527,6 @@ const api = {
method: 'get', method: 'get',
params: {} params: {}
}), }),
// ★ 新增: 单条库存调整
adjustStock: (draftId: number, stockId: number, diffQty: number, sourceTable: string, remark: string) => request({
url: '/v1/inbound/stock/adjust',
method: 'post',
data: {
draft_id: draftId,
stock_id: stockId, // 库存项ID
diff_qty: diffQty, // 差异数量(支持无草稿模式)
source_table: sourceTable, // 必须stock_buy / stock_semi / stock_product
operator_name: currentUser,
remark: remark
}
}),
// ★ 保留清除功能(用于兼容性) // ★ 保留清除功能(用于兼容性)
clearDraft: () => request({ clearDraft: () => request({
url: '/v1/inbound/stock/draft/clear', url: '/v1/inbound/stock/draft/clear',
@ -571,11 +593,27 @@ const checkServerDraft = async () => {
// ★ 重写: 开始新盘点 - 使用新 API // ★ 重写: 开始新盘点 - 使用新 API
const startNewSession = async () => { const startNewSession = async () => {
try { // ★ 新增: 防呆确认弹窗
if (serverDraftCount.value > 0) { if (serverDraftCount.value > 0) {
await ElMessageBox.confirm('存在未完成记录,开始新盘点将清除它们,确定吗?', '警告', { type: 'warning' }) showConfirmDialog.value = true
countdown.value = 10
if (countdownTimer) clearInterval(countdownTimer)
countdownTimer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(countdownTimer!)
countdownTimer = null
} }
}, 1000)
return
}
await doStartNewSession()
}
const doStartNewSession = async () => {
showConfirmDialog.value = false
btnLoading.value = true btnLoading.value = true
try {
// 调用新 API 开始新会话 // 调用新 API 开始新会话
const res: any = await api.startNewSession() const res: any = await api.startNewSession()
currentSessionId.value = res.session_id || '' currentSessionId.value = res.session_id || ''
@ -592,6 +630,20 @@ const startNewSession = async () => {
} finally { btnLoading.value = false } } finally { btnLoading.value = false }
} }
const confirmClear = () => {
if (countdown.value > 0) return
doStartNewSession()
}
const cancelConfirm = () => {
showConfirmDialog.value = false
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
countdown.value = 0
}
// ★ 重写: 继续上次盘点 - 恢复扫码作业 // ★ 重写: 继续上次盘点 - 恢复扫码作业
const resumeSession = async () => { const resumeSession = async () => {
btnLoading.value = true btnLoading.value = true
@ -760,7 +812,6 @@ const handleManualInput = async () => {
ElMessage.error(`不在库条码: ${code}`) ElMessage.error(`不在库条码: ${code}`)
} }
} finally { } finally {
barcodeInput.value = ''
loading.value = false loading.value = false
} }
} }
@ -888,6 +939,18 @@ const varianceList = computed(() => {
})) }))
}) })
// ★ 新增: 本地搜索过滤后的差异列表
const filteredVarianceList = computed(() => {
if (!searchSku.value) return varianceList.value
const kw = searchSku.value.toLowerCase()
return varianceList.value.filter(i =>
(i.uuid && i.uuid.toLowerCase().includes(kw)) ||
(i.sku && i.sku.toLowerCase().includes(kw)) ||
(i.stock_name && i.stock_name.toLowerCase().includes(kw)) ||
(i.stock_spec && i.stock_spec.toLowerCase().includes(kw))
)
})
const openInventoryList = () => { showList.value = true } const openInventoryList = () => { showList.value = true }
// ★ 修改:结束盘点按钮直接调用 finishStocktake跳过二次确认弹窗 // ★ 修改:结束盘点按钮直接调用 finishStocktake跳过二次确认弹窗
@ -946,35 +1009,6 @@ const openVarianceDialog = async () => {
varianceLoading.value = false varianceLoading.value = false
} }
// ★ 新增: 确认平账
const handleAdjust = async (row: any) => {
try {
// ===== 调试代码 =====
console.warn('---- 准备平账参数检查 ----');
console.warn('当前点击行的完整数据:', row);
console.warn(`将要发送的 draftId: ${row.id}, stockId: ${row.stock_id}, sourceTable: ${row.source_table}`);
// ===== 调试结束 =====
await ElMessageBox.confirm(
`确定要对 "${row.uuid}" 进行平账调整吗?\n\n差异: ${row.diff_qty > 0 ? '盘盈 +' : '盘亏 '}${row.diff_qty}`,
'确认平账',
{ type: 'warning', confirmButtonText: '确认调整', cancelButtonText: '取消' }
)
const remark = `盘点差异调整 - ${row.diff_qty > 0 ? '盘盈入库' : '盘亏出库'}`
const res: any = await api.adjustStock(row.id, row.stock_id, row.diff_qty, row.source_table || 'stock_buy', remark)
ElMessage.success(res.message || '调整成功')
// 刷新数据并重新打开差异列表
await loadData()
await openVarianceDialog()
} catch (e: any) {
if (e !== 'cancel') ElMessage.error(e?.message || '操作失败')
}
}
// ★ 新增: 跳转到差异审核页面 // ★ 新增: 跳转到差异审核页面
const goToVarianceReview = () => { const goToVarianceReview = () => {
openVarianceDialog() openVarianceDialog()
@ -1239,4 +1273,14 @@ const goToVarianceReview = () => {
width: 100%; width: 100%;
} }
} }
.confirm-content {
text-align: center;
padding: 20px 0;
}
.confirm-content .confirm-text {
margin-top: 20px;
font-size: 16px;
color: #303133;
}
</style> </style>