1. 新增PageHelp通用帮助组件,实现页面左下角悬浮帮助按钮 2. 为WMS、MES、工位终端多个业务页面接入帮助系统 3. 替换项目中旧的打包静态资源文件,更新资源引用路径 4. 新增各模块的帮助文档配置数据
238 lines
9.6 KiB
Vue
238 lines
9.6 KiB
Vue
<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> |