Files
bj_power/bj_power_wms_client/frontend/src/pages/Stocktake.vue
T
SunYF dd078038e2 feat: 为多个页面添加操作帮助功能,替换旧版静态资源文件
1. 新增PageHelp通用帮助组件,实现页面左下角悬浮帮助按钮
2. 为WMS、MES、工位终端多个业务页面接入帮助系统
3. 替换项目中旧的打包静态资源文件,更新资源引用路径
4. 新增各模块的帮助文档配置数据
2026-08-28 17:23:44 +08:00

238 lines
9.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { nextTick, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import request from '../utils/request'
import { fmtTime } from '../utils/format'
import { getRealName } from '../utils/auth'
import PageHelp from '../components/PageHelp.vue'
import { helpStocktake } from '../help'
/* ---------- 发起盘点(动态/静态/抽盘通过后端类型扩展,当前按全量快照) ---------- */
const operator = getRealName()
const starting = ref(false)
const active = reactive({ no: '', totalTargets: 0 })
async function startStocktake() {
starting.value = true
try {
const data = await request.post('/stocktake/start', { operator })
active.no = data?.stocktakeNo || ''
active.totalTargets = data?.totalTargets || 0
ElMessage.success(`盘点已发起:盘点单号 ${active.no},目标 ${active.totalTargets} 项`)
await loadHistory()
} finally {
starting.value = false
}
}
/* ---------- 扫码快速盘点 ---------- */
const recordForm = reactive({ targetId: '', scanQty: 1 })
const recording = ref(false)
const idRef = ref()
const records = ref([])
async function submitRecord() {
if (!active.no) {
ElMessage.warning('请先发起或填写盘点单号')
return
}
const targetId = recordForm.targetId.trim()
if (!targetId) {
ElMessage.warning('请扫入目标编号(批次号或 SN)')
return
}
const qty = Number(recordForm.scanQty)
if (!Number.isInteger(qty) || qty < 0) {
ElMessage.warning('实盘数量需为非负整数')
return
}
recording.value = true
try {
const data = await request.post('/stocktake/record', {
stocktakeNo: active.no,
targetId,
scanQty: qty
})
const hit = records.value.findIndex(r => r.targetId === data?.targetId)
const row = {
targetId: data?.targetId ?? targetId,
bookQty: data?.bookQty ?? '-',
scannedQty: data?.scannedQty ?? qty,
diffQty: data?.diffQty ?? qty - (data?.bookQty ?? 0)
}
if (hit >= 0) records.value.splice(hit, 1, row)
else records.value.unshift(row)
ElMessage.success(
`已记录 ${row.targetId}:账面 ${row.bookQty},实盘 ${row.scannedQty},差异 ${row.diffQty}`
)
recordForm.targetId = ''
nextTick(() => idRef.value?.focus())
} finally {
recording.value = false
}
}
/* ---------- 完成盘点 ---------- */
const adjust = ref(true)
const finishing = ref(false)
const finishResult = ref(null)
async function finishStocktake() {
if (!active.no) {
ElMessage.warning('请先发起盘点')
return
}
finishing.value = true
try {
const data = await request.post('/stocktake/finish', { stocktakeNo: active.no, adjust: adjust.value })
finishResult.value = data || {}
ElMessage.success(
`盘点完成:已盘 ${data?.counted ?? 0} 项,未盘 ${data?.uncounted ?? 0} 项` +
(adjust.value ? ',差异已写回库存' : '')
)
records.value = []
await loadHistory()
} finally {
finishing.value = false
}
}
/* ---------- 历史盘点单 ---------- */
const historyFilter = ref('')
const history = ref([])
const historyLoading = ref(false)
async function loadHistory() {
historyLoading.value = true
try {
const params = {}
if (historyFilter.value.trim()) params.stocktakeNo = historyFilter.value.trim()
const data = await request.get('/stocktake/query', { params })
history.value = data?.list || []
} finally {
historyLoading.value = false
}
}
</script>
<template>
<div>
<el-row :gutter="12">
<el-col :span="10">
<el-card shadow="never" class="mb12" header="发起盘点">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="操作人">{{ operator || '-' }}</el-descriptions-item>
<el-descriptions-item label="盘点单号">{{ active.no || '未发起' }}</el-descriptions-item>
<el-descriptions-item label="目标数量">{{ active.no ? `${active.totalTargets} 项` : '-' }}</el-descriptions-item>
</el-descriptions>
<el-button type="primary" class="mt12" :loading="starting"
:disabled="!!active.no && !finishResult" @click="startStocktake">
发起盘点冻结库存快照
</el-button>
</el-card>
<el-card shadow="never" header="完成盘点">
<el-form label-width="120px">
<el-form-item label="差异写回库存">
<el-switch v-model="adjust" />
<span class="tip">开启后按实盘差异修正库存</span>
</el-form-item>
<el-form-item>
<el-button type="warning" :loading="finishing" @click="finishStocktake">完成并生成差异</el-button>
</el-form-item>
</el-form>
</el-card>
</el-col>
<el-col :span="14">
<el-card shadow="never" class="mb12" header="扫码快速盘点(回车即提交并清空)">
<template v-if="!active.no">
<el-input v-model="active.no" placeholder="也可粘贴已有进行中的盘点单号" clearable>
<template #prepend>盘点单号</template>
</el-input>
</template>
<el-form inline @submit.prevent="submitRecord">
<el-form-item label="目标编号">
<el-input ref="idRef" v-model="recordForm.targetId" clearable autofocus placeholder="批次号 / SN"
style="width:220px" @keyup.enter="submitRecord" />
</el-form-item>
<el-form-item label="实盘数量">
<el-input-number v-model="recordForm.scanQty" :min="0" :step="1" step-strictly style="width:120px" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="recording" @click="submitRecord">提交</el-button>
</el-form-item>
</el-form>
<el-table :data="records" border size="small" max-height="300" empty-text="尚无扫码记录">
<el-table-column prop="targetId" label="目标编号" min-width="170" />
<el-table-column prop="bookQty" label="账面数" width="90" align="center" />
<el-table-column prop="scannedQty" label="实盘数" width="90" align="center" />
<el-table-column label="差异" width="90" align="center">
<template #default="{ row }">
<span :class="{ bad: row.diffQty !== 0 }">{{ row.diffQty }}</span>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
<el-card v-if="finishResult" shadow="never" class="mb12" header="差异明细(diffs">
<el-alert type="info" :closable="false"
:title="`盘点单 ${finishResult.stocktakeNo}:已盘 ${finishResult.counted} 项 / 未盘 ${finishResult.uncounted} 项${finishResult.adjusted ? ',差异已写回库存' : ''}`"
style="margin-bottom:10px" />
<el-table :data="finishResult.diffs || []" border size="small"
empty-text="无差异账实相符">
<el-table-column prop="targetId" label="目标编号" min-width="190" />
<el-table-column prop="type" label="类型" width="100" align="center">
<template #default="{ row }">{{ row.type === 'BATCH' ? '批次' : 'SN' }}</template>
</el-table-column>
<el-table-column prop="bookQty" label="账面数" width="100" align="center" />
<el-table-column prop="scannedQty" label="实盘数" width="100" align="center" />
<el-table-column prop="diffQty" label="差异" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.diffQty > 0 ? 'success' : 'danger'" size="small">{{ row.diffQty }}</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
<el-card shadow="never" header="历史盘点单查询">
<el-form inline @submit.prevent="loadHistory">
<el-form-item label="盘点单号">
<el-input v-model="historyFilter" placeholder="留空查最近全部" clearable style="width:240px"
@keyup.enter="loadHistory" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="historyLoading" @click="loadHistory">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="history" border size="small" empty-text="暂无盘点记录">
<el-table-column prop="stocktakeNo" label="盘点单号" min-width="200" />
<el-table-column prop="operator" label="操作人" width="110" align="center">
<template #default="{ row }">{{ row.operator || '-' }}</template>
</el-table-column>
<el-table-column prop="totalTargets" label="目标数" width="90" align="center" />
<el-table-column label="状态" width="110" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '已完成' ? 'success' : 'primary'" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="发起时间" width="160" align="center">
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="完成时间" width="160" align="center">
<template #default="{ row }">{{ fmtTime(row.finishedAt) }}</template>
</el-table-column>
</el-table>
</el-card>
</div>
<PageHelp :data="helpStocktake" />
</template>
<style scoped>
.mb12 { margin-bottom: 12px; }
.mt12 { margin-top: 12px; }
.tip { margin-left: 10px; color: #909399; font-size: 12px; }
.bad { color: #f56c6c; font-weight: 700; }
</style>