功能:新增业务接口与扫码组件

This commit is contained in:
焦龙言
2026-07-27 03:17:52 +08:00
parent c08b5a5d3d
commit 1f23d92ffb
20 changed files with 1095 additions and 0 deletions
@@ -0,0 +1,43 @@
import request from '@/utils/request'
// 查询托盘装填记录列表
export function listFilling(query) {
return request({
url: '/business/filling/list',
method: 'get',
params: query
})
}
// 查询装填菌种选项
export function getFillingStrainOptions() {
return request({
url: '/business/filling/strain-options',
method: 'get'
})
}
// 扫描待装填托盘
export function scanFillingPallet(qrToken) {
return request({
url: '/business/filling/scan/' + qrToken,
method: 'get'
})
}
// 查询托盘装填记录详情
export function getFilling(fillingId) {
return request({
url: '/business/filling/' + fillingId,
method: 'get'
})
}
// 记录托盘装填
export function addFilling(data) {
return request({
url: '/business/filling',
method: 'post',
data: data
})
}
@@ -0,0 +1,70 @@
import request from '@/utils/request'
// 查询首次入库单列表
export function listInbound(query) {
return request({
url: '/business/inbound/list',
method: 'get',
params: query
})
}
// 查询首次入库仓库选项
export function getInboundWarehouseOptions() {
return request({
url: '/business/inbound/warehouse-options',
method: 'get'
})
}
// 扫描首次入库托盘
export function scanInboundPallet(qrToken, query) {
return request({
url: '/business/inbound/scan/' + qrToken,
method: 'get',
params: query
})
}
// 查询首次入库单详情
export function getInbound(inboundId) {
return request({
url: '/business/inbound/' + inboundId,
method: 'get'
})
}
// 新增首次入库草稿
export function addInbound(data) {
return request({
url: '/business/inbound',
method: 'post',
data: data
})
}
// 修改首次入库草稿
export function updateInbound(data) {
return request({
url: '/business/inbound',
method: 'put',
data: data
})
}
// 删除首次入库草稿
export function delInbound(inboundIds) {
return request({
url: '/business/inbound/' + inboundIds,
method: 'delete'
})
}
// 确认首次入库单
export function confirmInbound(inboundId, data) {
return request({
url: '/business/inbound/' + inboundId + '/confirm',
method: 'put',
data: data
})
}
@@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询托盘退回单列表
export function listReturn(query) {
return request({
url: '/business/return/list',
method: 'get',
params: query
})
}
// 查询退回目标仓库选项
export function getReturnWarehouseOptions() {
return request({
url: '/business/return/warehouse-options',
method: 'get'
})
}
// 扫描退回托盘
export function scanReturnPallet(qrToken, query) {
return request({
url: '/business/return/scan/' + qrToken,
method: 'get',
params: query
})
}
// 发起托盘退回申请
export function requestReturn(data) {
return request({
url: '/business/return/request',
method: 'post',
data: data
})
}
// 查询托盘退回单详情
export function getReturn(returnId) {
return request({
url: '/business/return/' + returnId,
method: 'get'
})
}
// 确认托盘退回
export function confirmReturn(returnId, data) {
return request({
url: '/business/return/' + returnId + '/confirm',
method: 'put',
data: data
})
}
@@ -0,0 +1,78 @@
import request from '@/utils/request'
// 查询整托发货单列表
export function listShipment(query) {
return request({
url: '/business/shipment/list',
method: 'get',
params: query
})
}
// 查询发货来源仓库选项
export function getShipmentWarehouseOptions() {
return request({
url: '/business/shipment/warehouse-options',
method: 'get'
})
}
// 查询发货目标现场选项
export function getShipmentSiteOptions() {
return request({
url: '/business/shipment/site-options',
method: 'get'
})
}
// 扫描整托发货托盘
export function scanShipmentPallet(qrToken, query) {
return request({
url: '/business/shipment/scan/' + qrToken,
method: 'get',
params: query
})
}
// 查询整托发货单详情
export function getShipment(shipmentId) {
return request({
url: '/business/shipment/' + shipmentId,
method: 'get'
})
}
// 新增整托发货草稿
export function addShipment(data) {
return request({
url: '/business/shipment',
method: 'post',
data: data
})
}
// 修改整托发货草稿
export function updateShipment(data) {
return request({
url: '/business/shipment',
method: 'put',
data: data
})
}
// 删除整托发货草稿
export function delShipment(shipmentIds) {
return request({
url: '/business/shipment/' + shipmentIds,
method: 'delete'
})
}
// 确认整托发货单
export function confirmShipment(shipmentId, data) {
return request({
url: '/business/shipment/' + shipmentId + '/confirm',
method: 'put',
data: data
})
}
@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询客户现场使用记录列表
export function listUsage(query) {
return request({
url: '/business/usage/list',
method: 'get',
params: query
})
}
// 查询客户现场启用产线选项
export function getUsageLineOptions(siteId) {
return request({
url: '/business/usage/line-options',
method: 'get',
params: { siteId }
})
}
// 扫描客户现场待使用托盘
export function scanUsagePallet(qrToken) {
return request({
url: '/business/usage/scan/' + qrToken,
method: 'get'
})
}
// 查询客户现场使用记录详情
export function getUsage(usageId) {
return request({
url: '/business/usage/' + usageId,
method: 'get'
})
}
// 记录客户现场使用
export function addUsage(data) {
return request({
url: '/business/usage',
method: 'post',
data: data
})
}
@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询客户列表
export function listCustomer(query) {
return request({
url: '/master/customer/list',
method: 'get',
params: query
})
}
// 查询客户详情
export function getCustomer(customerId) {
return request({
url: '/master/customer/' + customerId,
method: 'get'
})
}
// 新增客户
export function addCustomer(data) {
return request({
url: '/master/customer',
method: 'post',
data: data
})
}
// 修改客户
export function updateCustomer(data) {
return request({
url: '/master/customer',
method: 'put',
data: data
})
}
// 删除客户
export function delCustomer(customerIds) {
return request({
url: '/master/customer/' + customerIds,
method: 'delete'
})
}
@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询生产线列表
export function listLine(query) {
return request({
url: '/master/line/list',
method: 'get',
params: query
})
}
// 查询生产线详情
export function getLine(lineId) {
return request({
url: '/master/line/' + lineId,
method: 'get'
})
}
// 新增生产线
export function addLine(data) {
return request({
url: '/master/line',
method: 'post',
data: data
})
}
// 修改生产线
export function updateLine(data) {
return request({
url: '/master/line',
method: 'put',
data: data
})
}
// 删除生产线
export function delLine(lineIds) {
return request({
url: '/master/line/' + lineIds,
method: 'delete'
})
}
@@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询托盘列表
export function listPallet(query) {
return request({
url: '/master/pallet/list',
method: 'get',
params: query
})
}
// 查询托盘详情
export function getPallet(palletId) {
return request({
url: '/master/pallet/' + palletId,
method: 'get'
})
}
// 获取托盘二维码图片
export function getPalletQrcode(palletId) {
return request({
url: '/master/pallet/' + palletId + '/qrcode',
method: 'get',
responseType: 'blob'
})
}
// 新增托盘
export function addPallet(data) {
return request({
url: '/master/pallet',
method: 'post',
data: data
})
}
// 修改托盘
export function updatePallet(data) {
return request({
url: '/master/pallet',
method: 'put',
data: data
})
}
// 删除托盘
export function delPallet(palletIds) {
return request({
url: '/master/pallet/' + palletIds,
method: 'delete'
})
}
@@ -0,0 +1,61 @@
import request from '@/utils/request'
// 查询客户现场列表
export function listSite(query) {
return request({
url: '/master/site/list',
method: 'get',
params: query
})
}
// 查询客户现场详情
export function getSite(siteId) {
return request({
url: '/master/site/' + siteId,
method: 'get'
})
}
// 新增客户现场
export function addSite(data) {
return request({
url: '/master/site',
method: 'post',
data: data
})
}
// 修改客户现场
export function updateSite(data) {
return request({
url: '/master/site',
method: 'put',
data: data
})
}
// 删除客户现场
export function delSite(siteIds) {
return request({
url: '/master/site/' + siteIds,
method: 'delete'
})
}
// 查询客户现场可分配用户
export function getSiteUsers(siteId) {
return request({
url: '/master/site/' + siteId + '/users',
method: 'get'
})
}
// 分配客户现场用户
export function assignSiteUsers(siteId, data) {
return request({
url: '/master/site/' + siteId + '/users',
method: 'put',
data: data
})
}
@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询菌种列表
export function listStrain(query) {
return request({
url: '/master/strain/list',
method: 'get',
params: query
})
}
// 查询菌种详情
export function getStrain(strainId) {
return request({
url: '/master/strain/' + strainId,
method: 'get'
})
}
// 新增菌种
export function addStrain(data) {
return request({
url: '/master/strain',
method: 'post',
data: data
})
}
// 修改菌种
export function updateStrain(data) {
return request({
url: '/master/strain',
method: 'put',
data: data
})
}
// 删除菌种
export function delStrain(strainIds) {
return request({
url: '/master/strain/' + strainIds,
method: 'delete'
})
}
@@ -0,0 +1,61 @@
import request from '@/utils/request'
// 查询仓库列表
export function listWarehouse(query) {
return request({
url: '/master/warehouse/list',
method: 'get',
params: query
})
}
// 查询仓库详情
export function getWarehouse(warehouseId) {
return request({
url: '/master/warehouse/' + warehouseId,
method: 'get'
})
}
// 新增仓库
export function addWarehouse(data) {
return request({
url: '/master/warehouse',
method: 'post',
data: data
})
}
// 修改仓库
export function updateWarehouse(data) {
return request({
url: '/master/warehouse',
method: 'put',
data: data
})
}
// 删除仓库
export function delWarehouse(warehouseIds) {
return request({
url: '/master/warehouse/' + warehouseIds,
method: 'delete'
})
}
// 查询仓库可分配用户
export function getWarehouseUsers(warehouseId) {
return request({
url: '/master/warehouse/' + warehouseId + '/users',
method: 'get'
})
}
// 分配仓库用户
export function assignWarehouseUsers(warehouseId, data) {
return request({
url: '/master/warehouse/' + warehouseId + '/users',
method: 'put',
data: data
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询产线消耗报表
export function listConsumptionReport(query) {
return request({
url: '/report/consumption/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询装填记录报表
export function listFillingReport(query) {
return request({
url: '/report/filling/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询托盘库存报表
export function listPalletInventory(query) {
return request({
url: '/report/pallet-inventory/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询托盘追溯报表
export function listPalletTrace(query) {
return request({
url: '/report/pallet-trace/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询退回仓库报表
export function listReturnReport(query) {
return request({
url: '/report/return/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询发货出库报表
export function listShipmentReport(query) {
return request({
url: '/report/shipment/list',
method: 'get',
params: query
})
}
@@ -0,0 +1,173 @@
<template>
<div
class="pallet-scan-input"
:class="{
'is-loading': loading,
'is-error': errorMessage,
'is-disabled': disabled
}"
>
<el-input
ref="inputRef"
v-model="scanValue"
size="large"
clearable
:disabled="disabled"
:readonly="loading"
:aria-busy="loading"
:aria-invalid="Boolean(errorMessage)"
placeholder="请扫描或输入托盘二维码后按 Enter"
@input="clearError"
@keyup.enter="handleSubmit"
>
<template #suffix>
<el-icon v-if="loading" class="pallet-scan-input__loading is-loading">
<Loading />
</el-icon>
</template>
</el-input>
<div v-if="errorMessage" class="pallet-scan-input__error" role="alert">
{{ errorMessage }}
</div>
</div>
</template>
<script setup>
import { Loading } from '@element-plus/icons-vue'
import { isDuplicateScan, normalizeScanValue } from '@/utils/palletScan'
const props = defineProps({
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
minLength: {
type: Number,
default: 8,
validator: (value) => Number.isInteger(value) && value > 0
},
duplicateWindowMs: {
type: Number,
default: 500,
validator: (value) => Number.isFinite(value) && value >= 0
}
})
const emit = defineEmits(['scan', 'invalid', 'duplicate'])
const inputRef = ref(null)
const scanValue = ref('')
const errorMessage = ref('')
let lastScanValue = ''
let lastScanAt = 0
function focusInput() {
nextTick(() => {
if (!props.disabled) {
inputRef.value?.focus()
}
})
}
function clearError() {
errorMessage.value = ''
}
function handleSubmit() {
if (props.disabled || props.loading) {
return
}
let normalizedValue
try {
normalizedValue = normalizeScanValue(scanValue.value)
if (normalizedValue.length < props.minLength) {
throw new Error(`二维码长度不能少于 ${props.minLength} 个字符`)
}
} catch (error) {
errorMessage.value = error.message
emit('invalid', error.message)
focusInput()
return
}
const scannedAt = Date.now()
if (isDuplicateScan(
normalizedValue,
lastScanValue,
scannedAt,
lastScanAt,
props.duplicateWindowMs
)) {
scanValue.value = ''
emit('duplicate', normalizedValue)
focusInput()
return
}
lastScanValue = normalizedValue
lastScanAt = scannedAt
scanValue.value = ''
errorMessage.value = ''
emit('scan', normalizedValue)
focusInput()
}
onMounted(focusInput)
watch(() => props.loading, (loading) => {
if (!loading) {
focusInput()
}
})
</script>
<style lang="scss" scoped>
.pallet-scan-input {
width: 100%;
min-height: 48px;
:deep(.el-input__wrapper) {
min-height: 48px;
transition: box-shadow var(--el-transition-duration-fast), background-color var(--el-transition-duration-fast);
}
&:not(.is-disabled):not(.is-error):focus-within :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
}
&.is-loading :deep(.el-input__wrapper) {
background-color: var(--el-fill-color-light);
cursor: progress;
}
&.is-error :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px var(--el-color-danger) inset;
}
&.is-disabled :deep(.el-input__wrapper) {
background-color: var(--el-disabled-bg-color);
cursor: not-allowed;
}
}
.pallet-scan-input__loading {
color: var(--el-color-primary);
}
.pallet-scan-input__error {
padding-top: 4px;
color: var(--el-color-danger);
font-size: 12px;
line-height: 1.4;
}
@media (max-width: 767px) {
.pallet-scan-input {
width: 100%;
}
}
</style>
@@ -0,0 +1,17 @@
export function normalizeScanValue(value) {
const normalizedValue = String(value ?? '').trim()
if (!normalizedValue) {
throw new Error('二维码不能为空')
}
return normalizedValue
}
export function isDuplicateScan(value, lastValue, scannedAt, lastScannedAt, duplicateWindowMs) {
const elapsed = scannedAt - lastScannedAt
return value === lastValue && elapsed >= 0 && elapsed <= duplicateWindowMs
}
export function createIdempotencyKey() {
return globalThis.crypto.randomUUID()
}