Commit db1677bd by wusiyi

feat: 播种墙配货抽组件

parent 24f36f38
......@@ -54,14 +54,15 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default']
NavMenu: typeof import('./src/components/NavMenu.vue')['default']
PodMakeOrder: typeof import('./src/components/PodMakeOrder/index.vue')['default']
RenderColumn: typeof import('./src/components/RenderColumn.vue')['default']
RightClickMenu: typeof import('./src/components/RightClickMenu.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
......
/**
* 播种墙相关接口
*/
import axios from './axios'
import { BaseRespData } from '@/types/api'
import { PodMakeOrderData } from '@/types/api/podMakeOrder'
// 获取箱子列表
export function getPodBoxListApi(
url: string,
factoryNo: number | string,
warehouseId: number | string,
) {
return axios.get<never, BaseRespData<PodMakeOrderData[]>>(url, {
params: { factoryNo, warehouseId },
})
}
// 打印普通拣货单
export function printNormalPdf(url: string, ids: string) {
return axios.get<never, BaseRespData<string>>(url, { params: { ids } })
}
// 获取装箱数据
export function getPackingDataApi(
url: string,
params: {
podJomallCnNo?: string
podJomallUsNo?: string
podOperationNo?: string
box?: number | null
factoryNo?: number
warehouseId?: number | string
},
) {
return axios.get<never, BaseRespData<PodMakeOrderData>>(url, {
params,
})
}
// 清空箱子
export function clearBoxApi(
url: string,
factoryNo: number,
box: number | null,
warehouseId: number | string,
) {
return axios.get<never, BaseRespData<never>>(url, {
params: { factoryNo, box, warehouseId },
})
}
// 清空所有箱子
export function clearAllBoxApi(
url: string,
warehouseId: string | number,
factoryNo: string | number | undefined,
) {
return axios.get<never, BaseRespData<never>>(url, {
params: { warehouseId, factoryNo },
})
}
// 提交打单 cn
export function submitInspectionCnApi(
url: string,
data: { id: number; version?: number }[],
warehouseId: number | string,
boxIndex?: number | null,
) {
const params = new URLSearchParams()
// 先放 warehouseId,再放 boxIndex
params.set('warehouseId', String(warehouseId))
if (boxIndex !== undefined && boxIndex !== null) {
params.set('box', String(boxIndex))
}
return axios.post<never, BaseRespData<never>>(`${url}?${params.toString()}`, {
orderParamList: data,
})
}
// 提交打单 us
export function submitInspectionUsApi(
url: string,
params: {
orderId?: number
orderParamList?: { id?: number }[]
},
boxIndex: number | null,
warehouseId: number | string,
) {
return axios.post<never, BaseRespData<never>>(
`${url}?box=${boxIndex}&warehouseId=${warehouseId}`,
params,
)
}
import type { CustomColumn } from '@/types/table'
import type { ProductList } from '@/types/api/podMakeOrder'
export type PodMakeOrderType = 'cn' | 'us' | 'new'
export const operationStatusMap: Record<string, string> = {
PENDING_PACKING: '待配货',
PENDING_SCHEDULE: '待排单',
PENDING_REPLENISH: '待补胚',
PENDING_PICK: '待拣胚',
IN_PRODUCTION: '生产中',
PACKING_COMPLETED: '配货完成',
COMPLETED: '已完成',
CANCELLED: '已取消',
ARCHIVE: '已归档',
}
export type OperationStatusTagConfig = {
type?: 'success' | 'warning' | 'info' | 'danger' | 'primary'
style?: { backgroundColor: string; color: string; borderColor: string }
}
export const operationStatusTagMap: Record<string, OperationStatusTagConfig> = {
PENDING_PACKING: { type: 'warning' },
PENDING_SCHEDULE: { type: 'info' },
PENDING_REPLENISH: {
style: {
backgroundColor: '#E6A23C',
color: '#FFFFFF',
borderColor: '#E6A23C',
},
},
PENDING_PICK: {
style: {
backgroundColor: '#409EFF',
color: '#FFFFFF',
borderColor: '#409EFF',
},
},
IN_PRODUCTION: { type: 'primary' },
PACKING_COMPLETED: { type: 'success' },
COMPLETED: { type: 'success' },
CANCELLED: { type: 'danger' },
ARCHIVE: {
style: {
backgroundColor: '#909399',
color: '#FFFFFF',
borderColor: '#909399',
},
},
}
export function getPodOrderDetailsColumns(options: {
orderNoName: string
type: PodMakeOrderType
}): CustomColumn<ProductList>[] {
const { orderNoName, type } = options
return [
{
label: '图片',
prop: 'image',
width: 250,
slot: 'image',
align: 'center',
fixed: 'left',
},
{
label: orderNoName,
align: 'center',
slot: 'productionOrderNo',
showOverflowTooltip: true,
width: type === 'new' ? 180 : 150,
},
{
label: '库存 SKU',
prop: 'thirdSkuCode',
width: 180,
align: 'center',
showOverflowTooltip: true,
},
{
label: 'variant SKU',
prop: 'variantSku',
width: 160,
align: 'center',
showOverflowTooltip: true,
},
{
label: '商品名称',
prop: 'productName',
minWidth: 100,
showOverflowTooltip: true,
},
{
label: '购买数量',
prop: 'purchaseNumber',
width: 90,
align: 'center',
fixed: 'right',
},
{
label: '拣货数量',
prop: 'count',
width: 90,
align: 'center',
fixed: 'right',
},
{
label: '验证结果',
slot: 'verifyResult',
width: 90,
align: 'center',
fixed: 'right',
},
]
}
<template>
<ElDialog
v-if="visible"
v-model="visible"
fullscreen
:close-on-click-modal="false"
:close-on-press-escape="false"
:before-close="handleClose"
style="top: 60px"
@opened="handleOpened"
@close="onClose"
>
<template #header>
<div class="title">
<span>{{ title }}</span>
<span v-if="socketConnect === 'online'" class="online">[在线]</span>
<span v-else class="offline">[离线]</span>
<ElButton
v-if="socketConnect === 'offline'"
type="success"
size="small"
:icon="Refresh"
@click="reconnectWebSocket"
>
刷新
</ElButton>
</div>
</template>
<div class="pod-make-order-content">
<div class="left-content">
<div class="head-form">
<div class="form-item">
<ElSelect
v-model="sheetPrinter"
placeholder="请选择打印机"
style="width: 200px"
@change="emit('set-printer', $event)"
>
<ElOption
v-for="item in printDeviceList"
:key="item"
:label="item"
:value="item"
/>
</ElSelect>
</div>
<div class="form-item">
<ElSelect
v-model="warehouseId"
placeholder="请选择仓库"
style="width: 200px"
@change="handleWarehouseChange"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</div>
<div class="form-item" style="flex: 1">
<ElInput
ref="productionOrderRef"
v-model="productionOrder"
:placeholder="`请输入${props.orderNoName}`"
clearable
style="width: 100%"
@keyup.enter="handleSearch"
/>
</div>
<div class="form-item">
<ElButton type="primary" @click="handleSearch">查询</ElButton>
</div>
<div v-if="isClearAll" class="form-item">
<ElButton type="danger" @click="clearAllBox">清空所有箱子</ElButton>
</div>
</div>
<div class="basic-info">
<div class="basic-info-item">
<span>物流跟踪号:</span>
<span>{{ podOrderDetailsData?.trackingNumber }}</span>
</div>
<div class="basic-info-item">
<span>店铺单号:</span>
<span>{{ podOrderDetailsData?.shopNumber }}</span>
</div>
<div class="basic-info-item">
<span>商品总数量:</span>
<span>{{ podOrderDetailsData?.purchaseNumber }}</span>
</div>
<div
v-for="item in basicInfoList"
:key="item.value"
class="basic-info-item"
>
<span>{{ item.label + ':' }}</span>
<span>{{ podOrderDetailsData?.[item.value] }}</span>
</div>
</div>
<div class="table-content">
<TableView
ref="tableRef"
:paginated-data="podOrderDetailsData?.productList || []"
:columns="podOrderDetailsColumns"
highlight-current-row
@row-click="handleRowClick"
>
<template #image="{ row }">
<div
v-if="type === 'cn'"
style="display: flex; flex-wrap: nowrap"
>
<div
v-for="img in row.productMark !== 'normal' &&
row.productMark !== 'custom_normal'
? row.previewImgs
: [{ url: row.variantImage }]"
:key="img"
style="cursor: pointer; margin-right: 5px; flex: 1"
@click.stop="handleCurrentChange(img.url)"
>
<img v-if="img.url" :src="img.url" alt="" />
</div>
</div>
<div
v-else-if="row.previewImgs?.length"
style="display: flex; flex-wrap: nowrap"
>
<div
v-for="img in row.previewImgs"
:key="img"
style="cursor: pointer; margin-right: 5px; flex: 1"
@click.stop="handleCurrentChange(img.url)"
>
<img v-if="img.url" :src="img.url" alt="" />
</div>
</div>
</template>
<template #productionOrderNo="{ row }">
<slot name="productionOrderNo" :row="row">
<template v-if="type === 'cn'">
{{ row.podJomallCnNo }}
</template>
<template v-else-if="type === 'us'">
{{ row.podJomallUsNo }}
</template>
<template v-else-if="type === 'new'">
<div
v-for="(parsed, index) in getOperationNoList(
row.operationNos,
)"
:key="index"
>
{{ parsed.operationNo }}
<ElTag
v-if="parsed.statusText"
size="small"
:type="operationStatusTagMap[parsed.status]?.type"
:style="{
marginLeft: '6px',
...operationStatusTagMap[parsed.status]?.style,
}"
>
{{ parsed.statusText }}
</ElTag>
</div>
</template>
</slot>
</template>
<template #verifyResult="{ row }">
<el-icon
v-if="row.power"
style="color: #00cc00; font-size: 24px; font-weight: 900"
>
<Check />
</el-icon>
</template>
</TableView>
</div>
</div>
<div class="middle-content">
<div class="box-top">
<template v-if="type === 'cn'">
<div class="box-top-item">
<span class="box-top-item-box-index">
{{ boxIndex }}
</span>
<span class="box-top-item-box-index-text">号箱</span>
<template v-if="pickFlag">
<strong
style="
font-size: 60px;
color: #00ff00;
display: inline-block;
text-align: center;
width: 90px;
"
>
{{ podOrderDetailsData?.purchaseNumber }}
</strong>
<span style="font-size: 30px">{{ '件已配齐' }}</span>
</template>
<template v-else>
<span style="font-size: 30px">放入第</span>
<div class="box-top-item-box-index-number">
{{ podOrderDetailsData?.pickingNumber }}
</div>
<span style="font-size: 30px">件商品</span>
</template>
</div>
</template>
<template v-else>
<div v-if="boxIndex !== 0" class="box-top-item">
<span class="box-top-item-box-index">
{{ boxIndex }}
</span>
<span class="box-top-item-box-index-text">号箱</span>
<span style="font-size: 30px">放入第</span>
<div
class="box-top-item-box-index-number"
:style="{
color:
podOrderDetailsData?.pickingNumber ===
podOrderDetailsData?.purchaseNumber
? 'rgb(0, 255, 0)'
: '',
}"
>
{{ podOrderDetailsData?.pickingNumber }}
</div>
<span style="font-size: 30px">件商品</span>
</div>
<div v-else class="box-top-item-box-index-text">
单件商品<span style="color: rgb(0, 255, 0)">(配齐)</span
>,不占用播种墙
</div>
</template>
<div class="box-top-item-status">
<span
v-if="
podOrderDetailsData?.pickingNumber &&
podOrderDetailsData?.purchaseNumber &&
podOrderDetailsData?.pickingNumber ===
podOrderDetailsData?.purchaseNumber
"
>
<span v-if="podOrderDetailsData?.printResult">
面单{{ renderPrintResult(podOrderDetailsData?.printResult) }}
</span>
<span v-else>面单打印中。。。</span>
</span>
<span v-else>验货中。。。</span>
</div>
<div class="box-top-item-btn">
<ElButton
type="primary"
@click="podOrderDetailsData && print(podOrderDetailsData, true)"
>
手动打印
</ElButton>
<ElButton v-if="type !== 'new'" type="primary" @click="printNormal">
普货拣货
</ElButton>
<ElButton type="success" @click="handlePrintFinish">
打单完成
</ElButton>
<ElButton
:disabled="props.type !== 'cn' && boxIndex === 0"
type="danger"
@click="handleClearBox"
>
清空箱子
</ElButton>
</div>
<div
v-if="
podOrderDetailsData?.productList?.length &&
podOrderDetailsData?.productList?.length > 1
"
class="multiple-title"
>
<span class="multiple-title-text"></span>
</div>
</div>
<div class="order-image">
<img :src="coverImage" alt="" />
</div>
</div>
<div class="right-content">
<div class="box-list">
<div
v-for="(item, index) in podBoxList"
:key="item.box"
class="box-list-item"
:class="{
active: item.box && boxIndex == item.box,
isNull: !item.data,
// badge: handleProduct(item.data || {}),
}"
@click="handleBoxClick(item)"
>
<span style="font-weight: bold" :title="index + 1 + '号箱'">
{{ index + 1 }}
</span>
<span
v-if="type === 'cn' ? item.data : item.data?.pickingNumber"
class="number"
>
{{ item.data?.pickingNumber }}/{{ item.data?.purchaseNumber }}
</span>
<div
v-if="
item.data &&
item.data.productList?.some(
(e) =>
e.productMark === 'custom_normal' ||
e.productMark === 'normal',
)
"
class="cb-product-mark"
>
<div class="red-circle"></div>
</div>
</div>
</div>
</div>
</div>
</ElDialog>
</template>
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import { Check, Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import useLodop from '@/utils/hooks/useLodop'
import TableView from '@/components/TableView.vue'
import {
OrderData,
PodMakeOrderData,
PodMakeOrderStore,
ProductList,
IorderItem,
} from '@/types/api/podMakeOrder'
import {
getPackingDataApi,
clearBoxApi,
getPodBoxListApi,
submitInspectionCnApi,
submitInspectionUsApi,
printNormalPdf,
clearAllBoxApi,
} from '@/api/podMakeOrder'
import useUserStore from '@/store/user'
import socket from './websocket'
import type { WebSocketMessage } from './websocket'
import {
getPodOrderDetailsColumns,
operationStatusMap,
operationStatusTagMap,
} from './config'
import { WarehouseListData } from '@/types/index'
import { filePath } from '@/api/axios.ts'
const { getCLodop } = useLodop()
const props = withDefaults(
defineProps<{
modelValue: boolean
printOrder: (data: OrderData, callback: (status: boolean) => void) => void
title?: string // 标题
type: 'cn' | 'us' | 'new'
warehouseList: WarehouseListData[]
orderNoName?: string // 订单号名称
basicInfoList?: { label: string; value: keyof OrderData }[]
orderStore: PodMakeOrderStore // 播种墙配货store
isClearAll?: boolean // 是否展示清空所有箱子按钮
// ws codes
wsCodes: {
open: string
close: string
}
// api url
apiUrls: {
getPodBoxList: string
clearBox: string
clearAllBox?: string
getPackingData: string
submitInspection: string
printNormalPdf: string
}
warehouseStorageKey: string // 仓库id存储key
}>(),
{
title: '播种墙配货',
orderNoName: '生产单号',
basicInfoList: () => [
{ label: '拣货数量', value: 'pickingNumber' },
{ label: '发货备注', value: 'remark' },
{ label: '订单号', value: 'factoryOrderNumber' },
],
isClearAll: false,
},
)
// new 获取操作单号
const getOperationNoList = (operationNos?: string) => {
if (!operationNos) return []
return operationNos.split(',').map((operationNoWithStatus) => {
const match = operationNoWithStatus.match(/^([^_]+)_(.+)$/)
if (!match)
return { operationNo: operationNoWithStatus, status: '', statusText: '' }
const [, operationNo, status] = match
const statusText = operationStatusMap[status] || status
return { operationNo, status, statusText }
})
}
const emit = defineEmits(['update:modelValue', 'set-printer', 'refresh'])
const userStore = useUserStore()
const visible = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
},
})
let renderLock = false
const printDeviceList = ref<string[]>([])
const sheetPrinter = ref<string>('')
const productionOrder = ref<string>('')
const podOrderDetailsData = ref<OrderData>()
const podOrderDetailsColumns = computed(() =>
getPodOrderDetailsColumns({
orderNoName: props.orderNoName,
type: props.type,
}),
)
const boxChange = ref<boolean>(false)
const boxIndex = ref<number | null>(null)
const isLock = ref<boolean>(false)
const productionOrderRef = ref()
const socketConnect = computed(() => props.orderStore.socketConnect)
const podBoxList = computed(() => props.orderStore.podBoxList)
const coverImage = ref<string>('')
let currentCode = ''
const tableRef = ref()
// 打开关闭弹窗 初始化 仓库id、ws、打印机
watch(visible, async (value: boolean) => {
if (value) {
podOrderDetailsData.value = {}
currentCode = ''
if (props.type === 'cn') {
currentItem.value = {}
}
initWarehouseId()
// 初始化ws
try {
await connectWebSocket()
} catch (error) {
console.error(error)
}
initOrderDetailBox()
initPrintDevice()
} else {
if (userStore.user?.factory.id) {
socket.send({
code: props.wsCodes.close,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
})
socket.close()
}
}
})
// 点击切换箱子
watch(boxIndex, (value: number | null) => {
const bool = !boxChange.value
boxChange.value = false
if (value && props.type === 'cn') {
const item = podBoxList.value?.find((item) => item.box === value)
currentItem.value = item?.data as OrderData
}
renderItemBox(bool)
})
// 箱子列表变化
watch(
podBoxList,
(value) => {
if (value) {
const item = value.find((item) => item.box === podBoxIndex.value)
if (props.type === 'cn') {
currentItem.value = item?.data as OrderData
}
if (item?.data) {
renderItemBox(true)
} else {
if (boxIndex.value === podBoxIndex.value) {
podOrderDetailsData.value = {}
boxIndex.value = null
}
}
}
},
{ deep: true },
)
// 订单详情数据变化 图片处理
watch(
() => podOrderDetailsData.value,
(val) => {
if (val && val.productList?.length)
val.productList.forEach((el) => {
if (!el.previewImgs) {
if (
el.productMark === 'custom_normal' ||
el.productMark === 'normal'
) {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
el.previewImgs = el.imageAry
? JSON.parse(el.imageAry)
: [{ url: el.variantImage }]
}
}
})
},
{ deep: true },
)
const podBoxIndex = computed(() => props.orderStore.podBoxIndex)
const pickFlag = computed(() => {
if (podOrderDetailsData.value?.productList) {
return podOrderDetailsData.value?.productList.every((item) => item.power)
}
return false
})
// 初始化仓库ID
const initWarehouseId = () => {
const localRaw = localStorage.getItem(props.warehouseStorageKey)
const localId = localRaw ? JSON.parse(localRaw) : ''
const hit = props.warehouseList.find((w) => w.id == localId)
warehouseId.value = hit ? localId : props.warehouseList[0]?.id
_warehouseId.value = hit ? localId : props.warehouseList[0]?.id
}
// 更新box列表
const updatePodBoxList = (params: {
boxList: PodMakeOrderData[] | OrderData | null | undefined
factoryNo: number | string
warehouseId?: number | string
box?: number
data?: OrderData
}) => {
if (props.type === 'cn') {
return props.orderStore.setPodBoxList(params)
}
return props.orderStore.setPodBoxList({
...params,
url: props.apiUrls.getPodBoxList,
fromUser: userStore.user?.id ?? 0,
})
}
// 处理扫码数据
const parseScanCode = (code: string) => {
const parts = code.split('_')
if (['us', 'new'].includes(props.type)) {
return parts.length > 3 && parts[3].startsWith('USPSC')
? parts[3]
: parts.length > 1
? parts[1]
: parts[0]
}
return parts.length > 3 && parts[3].startsWith('CNPSC')
? parts[3]
: parts.length > 1
? code
: parts[0]
}
// 匹配商品
const matchProductByCode = (product: ProductList, code: string) => {
if (['us', 'new'].includes(props.type)) {
return (
product.podJomallUsNo === code ||
product.thirdSkuCode === code ||
(product.operationNos && product.operationNos.includes(code))
)
}
return product.podJomallCnNo === code || product.thirdSkuCode === code
}
// 单件商品 不进播种墙
const handleUsSingleItemPacking = (data?: OrderData | null) => {
if (!data) return
podOrderDetailsData.value = data
podOrderDetailsData.value.fromUser = userStore.user?.id
const list = podOrderDetailsData.value.productList || []
for (const product of list) {
if (product.count === product.purchaseNumber) {
product.power = true
print(data, false, () => {
renderLock = false
})
}
}
if (list.length) {
nextTick(() => {
tableRef.value?.setCurrentRow(list[0])
})
}
}
// 渲染箱子对应的订单详情
const renderItemBox = (bool: boolean) => {
if (
!podBoxList.value ||
podBoxList.value.length === 0 ||
!boxIndex.value ||
(bool && boxIndex.value !== podBoxIndex.value)
)
return
if (renderLock) return
renderLock = true
let boxItem = podBoxList.value.find((item) => item.box === boxIndex.value)
if (!boxItem) boxItem = { data: { productList: [] } }
const { data } = boxItem
// 图片处理
data?.productList?.forEach((el) => {
if (!el.previewImgs || !el.previewImgs.length) {
if (el.productMark === 'custom_normal' || el.productMark === 'normal') {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
if (props.type === 'cn') {
try {
el.previewImgs = JSON.parse(el.imageAry || '[]')
} catch {
console.log('生产订单:', el)
// 只有是有效URL才创建对象
el.previewImgs = el.imageAry?.startsWith?.('http')
? [{ url: el.imageAry, sort: 1, title: '正' }]
: []
}
} else {
el.previewImgs = JSON.parse(el.imageAry || '[]')
}
}
}
})
if (!data) {
renderLock = false
currentCode = ''
podOrderDetailsData.value = {}
return
}
const { productList = [] } = data
// 计算拣货数量、验证结果
const pickingNumber = productList.reduce((prev, product) => {
return prev + (product.count || 0)
}, 0)
data.pickingNumber = pickingNumber
// coverImage.value = productList[0].previewImgs?.[0]?.url || ''
// 验证结果
for (const product of productList) {
if (product.count === product.purchaseNumber) {
product.power = true
}
}
if (currentCode) {
if (!currentCode.startsWith('JM')) {
currentCode = parseScanCode(currentCode)
}
for (const product of productList) {
if (matchProductByCode(product, currentCode)) {
coverImage.value = product.previewImgs?.[0]?.url || ''
nextTick(() => {
tableRef.value?.setCurrentRow(product)
})
break
}
}
currentCode = ''
}
podOrderDetailsData.value = data
if (productList.every((item) => item.power)) {
if (
['us', 'new'].includes(props.type) &&
userStore.user?.id !== boxItem.fromUser
) {
renderLock = false
return
}
print(data, false, () => {
renderLock = false
})
} else {
renderLock = false
}
}
// ws消息处理
const messageChange = (data: WebSocketMessage) => {
if (!data) return
const { code, ...more } = data
if (!code) return
if (
[
'FACTORY_POD_CN_PRINT_ORDER',
'FACTORY_POD_ORDER_PRINT_ORDER',
'POD_PRINT_ORDER',
].includes(code)
) {
// try {
// if (typeof more.txt === 'string') {
// console.log(
// '%conWebSocketMessage',
// 'font-size: 20px; color: red;',
// JSON.parse(more.txt),
// )
// }
// } catch (e) {
// console.error(e)
// }
setPodBoxListByWs(more, data.fromUser as number | undefined)
} else if (['POD_BOX_FLUSH', 'FACTORY_POD_ORDER_BOX_FLUSH'].includes(code)) {
initOrderDetailBox()
}
}
// ws消息更新box列表
const setPodBoxListByWs = (data: WebSocketMessage, fromUser?: number) => {
const obj = data.txt
if (obj && typeof obj === 'string') {
const parsedData = JSON.parse(obj)
if (props.type === 'us' || props.type === 'new') {
parsedData.fromUser = fromUser
props.orderStore.setPodBoxList({
url: props.apiUrls.getPodBoxList,
...parsedData,
})
return
}
props.orderStore.setPodBoxList(parsedData)
}
}
// 普货拣货
const printNormal = async () => {
const arr: (number | undefined)[] = []
;(podBoxList.value || []).forEach((item: PodMakeOrderData) => {
if (item.data?.productList?.length) {
if (['us', 'new'].includes(props.type)) {
item.data.productList.forEach((item1) => {
if (
item1.productMark == 'normal' ||
item1.productMark == 'custom_normal'
) {
arr.push(item1.id)
}
})
} else {
const flag = item.data.productList.some((item1) => {
return (
item1.productMark == 'normal' ||
item1.productMark == 'custom_normal'
)
})
if (flag) {
arr.push(item.data.id)
}
}
}
})
if (!arr.length) {
ElMessage.warning('暂无可打印的普货拣货单')
return
}
const res = await printNormalPdf(props.apiUrls.printNormalPdf, arr.join())
ElMessage.success('操作成功')
productionOrderRef.value.focus()
window.open(filePath + res.message)
}
// 初始化打印机
const initPrintDevice = () => {
const lodop = getCLodop(null, null)
if (!lodop) return
const arr = []
// 获取打印机数量
const length = lodop.GET_PRINTER_COUNT()
for (let i = 0; i < length; i++) {
// 根据设备序号获取设备名
const name = lodop.GET_PRINTER_NAME(i)
arr.push(name)
}
console.log('arr', arr, lodop.GET_PRINTER_NAME(1))
// 获取默认打印机
sheetPrinter.value = lodop.GET_PRINTER_NAME(0)
printDeviceList.value = arr
// 获取本地打印机
const locaclPrinter = localStorage.getItem('sheetPrinter')
if (locaclPrinter) sheetPrinter.value = JSON.parse(locaclPrinter)
emit('set-printer', sheetPrinter.value)
}
// 查询
const handleSearch = () => {
const code = productionOrder.value
if (!code) {
ElMessage.warning(`请输入${props.orderNoName}`)
productionOrderRef.value.focus()
return
}
if (isLock.value) {
ElMessage.warning('请稍后再试')
productionOrderRef.value.focus()
return
}
productionOrder.value = ''
isLock.value = true
const everyPower = podOrderDetailsData.value?.productList?.every(
(item) => item.power,
)
if (everyPower) {
/**
* printSuccess 打印成功
* printFail 打印失败
* printIng 打印中
* notPrintSuccess 未能获取打印状态
*/
if (podOrderDetailsData.value?.printResult === 'printSuccess') {
submitInspection(() => {
getPackingData(code)
})
} else {
ElMessage.warning('未获取到打印结果')
isLock.value = false
}
} else {
getPackingData(code)
}
}
// 获取装箱数据
const getPackingData = async (code: string) => {
const loading = ElLoading.service({
fullscreen: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
currentCode = code
try {
const factoryNo = userStore.user?.factory.id
if (!factoryNo) {
isLock.value = false
productionOrder.value = ''
return
}
if ((props.type === 'us' || props.type === 'new') && !warehouseId.value) {
return ElMessage.warning('请选择仓库')
}
const params: {
podJomallCnNo?: string
podJomallUsNo?: string
podOperationNo?: string
box?: number | null
factoryNo: number
warehouseId: number | string
} = {
box: boxIndex.value,
factoryNo,
warehouseId: warehouseId.value,
}
if (props.type === 'cn') {
params.podJomallCnNo = code
} else if (props.type === 'new') {
params.podOperationNo = code
} else {
params.podJomallUsNo = code
}
const res = await getPackingDataApi(props.apiUrls.getPackingData, params)
if (res.code !== 200) {
ElMessage.warning(res.message)
isLock.value = false
productionOrder.value = ''
return
}
if (props.type === 'cn') {
const { box } = res.data || {}
if (box) {
boxIndex.value = box
}
} else if (props.type === 'us' || props.type === 'new') {
boxIndex.value = res.data?.box as number
if (boxIndex.value === 0) {
handleUsSingleItemPacking(res.data?.data)
}
}
} catch (error) {
console.log(error)
} finally {
isLock.value = false
productionOrder.value = ''
loading.close()
productionOrderRef.value.focus()
}
}
// 提交打单
const submitInspection = async (callback: () => void) => {
const factoryNo = userStore.user?.factory.id
if (!factoryNo) {
return
}
try {
let res
if (props.type === 'cn') {
const data = (podOrderDetailsData.value?.orderParamList ?? []).map(
(item: IorderItem) => ({
id: item.id,
version: item.dataVersion,
}),
)
res = await submitInspectionCnApi(
props.apiUrls.submitInspection,
data,
warehouseId.value,
boxIndex.value,
)
} else {
const params =
props.type === 'new'
? { orderParamList: [{ id: podOrderDetailsData.value?.id }] }
: { orderId: podOrderDetailsData.value?.id }
res = await submitInspectionUsApi(
props.apiUrls.submitInspection,
params,
boxIndex.value,
warehouseId.value,
)
}
if (res.code !== 200) return
ElMessage.warning(res.message)
isLock.value = false
coverImage.value = ''
podOrderDetailsData.value = {}
productionOrderRef.value.focus()
callback && callback()
} catch (error) {
isLock.value = false
productionOrderRef.value.focus()
console.error(error)
}
}
const isBillLading = ref<boolean>(false)
// 初始化box列表
const initOrderDetailBox = async () => {
const factoryNo = userStore.user?.factory.id
if (!factoryNo) {
return
}
const loading = ElLoading.service({
fullscreen: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await getPodBoxListApi(
props.apiUrls.getPodBoxList,
factoryNo,
warehouseId.value,
)
if (res.code !== 200) {
ElMessage.warning(res.message)
return
}
// cn 处理图片正反
if (props.type === 'cn') {
res.data.forEach((r) => {
r.data?.productList?.forEach((d) => {
if (d.productMark === 'normal' || d.productMark === 'custom_normal') {
d.previewImgs = [{ url: d.variantImage || '' }]
} else {
if (!d.previewImgs) {
try {
d.previewImgs = JSON.parse(d.imageAry || '[]')
} catch {
console.log('生产订单:', d)
// 只有是有效URL才创建对象
d.previewImgs = d.imageAry?.startsWith?.('http')
? [{ url: d.imageAry, sort: 1, title: '正' }]
: []
}
}
}
})
})
}
updatePodBoxList({
boxList: res.data,
factoryNo,
warehouseId: warehouseId.value,
})
// box数据和列表拣货数量
const boxList = res.data.map((item) => {
if (item.data) {
if (!item.data.filePath) {
isBillLading.value = true
} else {
isBillLading.value = false
}
const productList = item?.data?.productList || []
const pickingNumber = productList.reduce((prev, product) => {
if (product.count === product.purchaseNumber) {
product.power = true
}
if (product.count) {
return prev + product.count
}
return prev
}, 0)
item.data.pickingNumber = pickingNumber
}
return item
})
podOrderDetailsData.value =
boxList.find((item) => item.data)?.data || undefined
boxIndex.value = boxList.find((item) => item.data)?.box || null
if (props.type === 'us' || props.type === 'new') {
podOrderDetailsData.value?.productList?.forEach((el) => {
if (!el.previewImgs) {
if (
el.productMark === 'custom_normal' ||
el.productMark === 'normal'
) {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
el.previewImgs = el.imageAry
? JSON.parse(el.imageAry)
: [{ url: el.variantImage }]
}
}
})
}
coverImage.value =
podOrderDetailsData.value?.productList?.[0]?.previewImgs?.[0].url || ''
if (
podOrderDetailsData.value &&
podOrderDetailsData.value.pickingNumber ===
podOrderDetailsData.value.purchaseNumber
) {
podOrderDetailsData.value.printResult = 'notPrintSuccess'
}
const pickFinished = boxList.filter((item) => {
return item.data?.productList?.every((product) => product.power)
})
const boxs = pickFinished.map((item) => item.box)
if (boxs.length > 0) {
nextTick(async () => {
try {
await ElMessageBox.alert(
`检测到${boxs.join(',')}号箱验货完成,请及时处理`,
'提示',
{
confirmButtonText: '确定',
},
)
productionOrderRef.value.focus()
} catch (error) {
productionOrderRef.value.focus()
console.error(error)
}
})
}
renderLock = false
productionOrder.value = ''
isLock.value = false
productionOrderRef.value.focus()
} catch (error) {
console.error(error)
} finally {
loading.close()
}
}
// 渲染打印结果
const renderPrintResult = (printResult: string) => {
switch (printResult) {
case 'printSuccess':
return '打印成功'
case 'printFail':
return '打印失败'
case 'printIng':
return '打印中'
case 'notPrintSuccess':
return '未能获取打印状态'
default:
return '未知'
}
}
const handleOpened = () => {
productionOrderRef.value.focus()
}
const handleClose = (done: () => void) => {
nextStep(
() => {
done()
},
props.type === 'cn' ? currentItem.value : undefined,
)
}
const onClose = () => {
// orderStore.clearPodBox()
emit('refresh')
}
// 下一步
const nextStep = async (callback: () => void, data?: OrderData) => {
const everyPicked = podOrderDetailsData.value?.productList?.every(
(item) => item.count === item.purchaseNumber,
)
if (
everyPicked &&
(podOrderDetailsData.value?.printResult === 'printSuccess' ||
podOrderDetailsData.value?.printResult === 'notPrintSuccess')
) {
const targetStatus =
props.type === 'cn'
? data?.replaceShipment == 1
? '待称重'
: '已完成'
: '已完成'
try {
await ElMessageBox.alert(
`当前订单验货完成并打印面单成功,是否转至${targetStatus}`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
},
)
submitInspection(() => {
callback && callback()
})
} catch (error) {
productionOrderRef.value.focus()
console.error(error)
}
} else {
callback && callback()
}
}
const currentItem = ref<OrderData>({})
// 点击切换箱子
const handleBoxClick = (item: PodMakeOrderData) => {
const { box, data } = item
if (props.type === 'cn') {
currentItem.value = item as OrderData
} else {
if (box == 0) return
}
isBillLading.value = !data?.filePath
nextStep(
() => {
if (!data) {
ElMessage.warning('暂无数据')
return
}
boxIndex.value = box || null
boxChange.value = true
productionOrderRef.value.focus()
},
props.type === 'cn' ? currentItem.value : undefined,
)
}
// 清空箱子
const handleClearBox = async () => {
try {
if (!boxIndex.value) {
ElMessage.warning('请选择箱子')
return
}
await ElMessageBox.alert('确定清空当前箱子吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
} catch {
return
}
const factoryNo = userStore.user?.factory.id
if (!factoryNo) {
return
}
try {
const res = await clearBoxApi(
props.apiUrls.clearBox,
factoryNo,
boxIndex.value ?? null,
warehouseId.value,
)
if (res.code !== 200) {
ElMessage.warning(res.message)
return
}
ElMessage.success('清空成功')
if (props.type === 'cn') {
props.orderStore.setPodBoxList({
boxList: null,
factoryNo,
box: boxIndex.value || undefined,
warehouseId: warehouseId.value,
})
}
boxIndex.value = null
podOrderDetailsData.value = {}
coverImage.value = ''
} catch (error) {
console.error(error)
} finally {
productionOrderRef.value.focus()
}
}
// 打单完成
const handlePrintFinish = async () => {
if (props.type === 'us' || props.type === 'new') {
if (podOrderDetailsData.value?.productList?.length) {
const { productList } = podOrderDetailsData.value
const bool = productList.some((el) => !el.power)
if (bool) {
try {
await ElMessageBox.alert(
'商品数量未配齐无法打单完成,请检查!',
'提示',
{
confirmButtonText: '确定',
},
)
} catch {
return
}
} else {
try {
await ElMessageBox.alert('确定打单完成吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
submitInspection(() => {
productionOrderRef.value.focus()
})
} catch {
productionOrderRef.value.focus()
return
}
}
} else {
try {
await ElMessageBox.alert('未检测到商品,请检查!', '提示', {
confirmButtonText: '确定',
})
} catch {
return
}
}
return
}
try {
await ElMessageBox.alert('确定打单完成吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
submitInspection(() => {
productionOrderRef.value.focus()
})
} catch {
productionOrderRef.value.focus()
return
}
}
// 打印拣货单
const print = (data: OrderData, forcePrint = false, callback?: () => void) => {
const _boxIndex = boxIndex.value
if (!forcePrint && data.printResult) {
callback && callback()
return
}
props.printOrder(data, (status: boolean) => {
callback && callback()
const item = podBoxList.value?.find((item) => item.box === _boxIndex)
if (item && item.data) {
if (status) {
item.data.printResult = 'printSuccess'
} else {
item.data.printResult = 'printFail'
}
}
if (_boxIndex === 0 && podOrderDetailsData.value) {
if (status) {
podOrderDetailsData.value.printResult = 'printSuccess'
} else {
podOrderDetailsData.value.printResult = 'printFail'
}
}
const factoryNo = userStore.user?.factory.id
if (!factoryNo) return
updatePodBoxList({
boxList: item ? item.data : null,
factoryNo,
box: _boxIndex ?? undefined,
warehouseId: warehouseId.value,
})
})
productionOrderRef.value?.focus()
}
// us 清空所有箱子
const clearAllBox = async () => {
if (!props.apiUrls.clearAllBox) return
try {
await ElMessageBox.alert('确定清空所有箱子吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
} catch {
productionOrderRef.value.focus()
return
}
try {
const res = await clearAllBoxApi(
props.apiUrls.clearAllBox,
warehouseId.value,
userStore.user?.factory.id,
)
if (res.code !== 200) return
if (props.type === 'us' || props.type === 'new') {
await initOrderDetailBox()
} else {
podOrderDetailsData.value = {}
coverImage.value = ''
boxIndex.value = null
}
productionOrderRef.value.focus()
} catch (error) {
productionOrderRef.value.focus()
console.error(error)
}
}
// 点击行数据,图片切换
const handleRowClick = (row: ProductList) => {
if (props.type === 'us' || props.type === 'new') {
coverImage.value = row.previewImgs?.[0]?.url || ''
} else {
const previewImages =
row.productMark !== 'normal' && row.productMark !== 'custom_normal'
? row.previewImgs
: [{ url: row.variantImage }]
coverImage.value = previewImages?.[0]?.url || ''
}
productionOrderRef.value.focus()
}
const handleCurrentChange = (url: string) => {
if (url) {
coverImage.value = url || ''
}
}
const warehouseId = ref<string | number>('')
const _warehouseId = ref<string | number>('')
const handleWarehouseChange = (value: string | number) => {
if (!value) return
if (_warehouseId.value !== warehouseId.value) {
socket.send({
code: props.wsCodes.close,
factoryNo: userStore.user?.factory.id,
warehouseId: _warehouseId.value,
})
}
warehouseId.value = value
localStorage.setItem(props.warehouseStorageKey, JSON.stringify(value))
socket.send({
code: props.wsCodes.open,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
})
_warehouseId.value = value
initOrderDetailBox()
}
// 连接 ws
const connectWebSocket = async () => {
if (!userStore.user?.factory.id) return
await socket.init(
{
account: userStore.user?.account.toString(),
factoryNo: userStore.user?.factory.id.toString(),
orderStore: props.orderStore,
},
messageChange,
)
socket.send({
code: props.wsCodes.open,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
})
}
// 重连ws
const reconnectWebSocket = async () => {
try {
await connectWebSocket()
} catch (error) {
console.error('WebSocket 重连失败:', error)
ElMessage.error('WebSocket 重连失败,请稍后重试')
}
}
</script>
<style scoped lang="scss">
.title {
display: flex;
align-items: center;
gap: 10px;
}
.online {
color: green;
font-size: 14px;
font-weight: 600;
}
.offline {
color: red;
font-size: 14px;
font-weight: 600;
}
.pod-make-order-content {
display: flex;
height: 100%;
overflow: hidden;
gap: 10px;
}
.left-content {
display: flex;
flex-direction: column;
gap: 10px;
width: calc(100% - 900px - 20px);
}
.head-form {
display: flex;
align-items: center;
gap: 10px;
}
.table-content {
flex: 1;
overflow: hidden;
}
.middle-content {
width: 500px;
display: flex;
flex-direction: column;
gap: 5px;
}
.right-content {
width: 400px;
overflow-y: auto;
overflow-x: hidden;
}
.basic-info {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
.basic-info-item {
display: flex;
align-items: center;
span:last-child {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.box-top {
position: relative;
border: 1px solid #ddd;
background: rgb(50 50 50);
border-radius: 5px;
padding: 10px 15px;
box-sizing: border-box;
font-size: 16px;
color: #fff;
overflow: hidden;
}
.multiple-title {
position: absolute;
top: 0;
right: 0;
&::after {
position: absolute;
top: -31px;
right: -30px;
content: '';
border-width: 30px;
border-style: solid;
border-color: transparent transparent red transparent;
transform: rotate(45deg);
}
.multiple-title-text {
position: absolute;
z-index: 2;
color: #fff;
left: -20px;
}
}
.box-top-item {
display: flex;
height: 80px;
align-items: center;
margin-bottom: 10px;
}
.box-top-item-box-index {
font-size: 60px;
color: red;
text-align: center;
width: 120px;
font-weight: 600;
}
.box-top-item-box-index-text {
margin-right: 15px;
font-size: 30px;
}
.box-top-item-box-index-number {
font-size: 60px;
color: red;
display: inline-block;
text-align: center;
width: 90px;
font-weight: 600;
}
.box-top-item-status {
margin-bottom: 15px;
}
.order-image {
flex: 1;
overflow: hidden;
border: 1px solid #ddd;
background: #eee;
}
.box-list {
display: grid;
grid-template-columns: repeat(7, 50px);
gap: 8px;
position: relative;
}
.box-list-item {
position: relative;
border: 1px solid #ddd;
cursor: pointer;
font-size: 18px;
line-height: 30px;
height: 46px;
text-align: center;
box-sizing: border-box;
border-radius: 5px;
&:not(.isNull) {
background: #819aff;
color: #fff;
}
&.active {
background: #ff9900;
color: #fff;
}
&.badge::after {
position: absolute;
content: '';
right: 4px;
top: 4px;
width: 7px;
height: 7px;
border-radius: 50%;
background-color: red;
}
.number {
position: absolute;
bottom: 3px;
right: 0;
left: 0;
text-align: center;
font-size: 12px;
line-height: 14px;
color: #ddd;
}
}
.cb-product-mark {
position: absolute;
top: 0;
right: 0;
.red-circle {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: red;
}
}
</style>
import { getWsUrl } from '@/api/axios'
import type { PodMakeOrderStore } from '@/types/api/podMakeOrder'
interface NotificationOptions {
body: string
icon: string
data: string
requireInteraction: boolean
}
export interface WebSocketMessage {
code?: string
fromUser?: number
content?: string
type?: string
data?: unknown
[key: string]: string | unknown | undefined
}
interface InitOptions {
account: string
factoryNo: string
orderStore: PodMakeOrderStore
}
type MessageCallback = (
data: WebSocketMessage,
sendSystemMessage: (msg: string) => void,
) => void
type OpenCallback = () => void
function sendSystemMessage(msg: string): void {
if (window.Notification && Notification.permission === 'granted') {
const notificationOptions: NotificationOptions = {
body: msg,
icon: './favicon.ico',
data: 'I like peas.',
requireInteraction: true,
}
const n = new Notification('消息通知', notificationOptions)
n.onclick = (event: Event) => {
event.preventDefault()
window.open('/home', '_blank')
}
setTimeout(() => {
n.close()
}, 10000)
}
}
const showReconnectingMsg = (): void => {
ElMessage.warning('消息服务已断开,正在重新连接,请稍候')
}
class Im {
private socket: WebSocket | null = null
private _wsUrl: string = ''
private _orderStore?: PodMakeOrderStore
private _onMessageCallback?: MessageCallback
private _onOpenCallback?: OpenCallback
private _heartbeatTimer: number | null = null
private _heartbeatTimeoutTimer: number | null = null
private _reconnectingTimer: number | null = null
private num: number = 0
constructor() {
this._onWsOpen = this._onWsOpen.bind(this)
this._onWsMessage = this._onWsMessage.bind(this)
this._onWsClose = this._onWsClose.bind(this)
this._onWsError = this._onWsError.bind(this)
}
private _onWsOpen(): void {
console.log('服务器连接成功')
localStorage.setItem('socket_connect', 'online')
this._onOpenCallback?.()
this._startHeartbeat()
}
private _onWsMessage(event: MessageEvent): void {
let data: WebSocketMessage = {}
if (typeof event.data === 'string') {
try {
data = JSON.parse(event.data)
} catch (error) {
data = {}
}
}
this._onHeartbeatMessage()
this._onMessageCallback?.(data, sendSystemMessage)
}
private _onWsClose(): void {
console.log('服务器关闭')
this._destroyWebSocket(true)
}
private _onWsError(): void {
console.log('连接出错')
this._destroyWebSocket(true)
}
private _sendHeartbeat(): void {
if (!this.socket) return
this.send({ code: 'HEALTH' })
if (this._heartbeatTimeoutTimer) {
window.clearTimeout(this._heartbeatTimeoutTimer)
}
this._heartbeatTimeoutTimer = window.setTimeout(() => {
this._destroyWebSocket(true)
}, 5 * 1000)
}
private _onHeartbeatMessage(): void {
console.log('心跳')
this._orderStore?.setSocketConnect('online')
if (this._heartbeatTimeoutTimer) {
window.clearTimeout(this._heartbeatTimeoutTimer)
}
}
private _startHeartbeat(): void {
this._stopHeartbeat()
this._sendHeartbeat()
this._heartbeatTimer = window.setInterval(
() => this._sendHeartbeat(),
10 * 1000,
)
}
private _stopHeartbeat(): void {
if (this._heartbeatTimer) {
window.clearInterval(this._heartbeatTimer)
}
if (this._heartbeatTimeoutTimer) {
window.clearTimeout(this._heartbeatTimeoutTimer)
}
}
private _scheduleReconnect(): void {
if (!this.num) this.num = 0
this.num++
if (this.num > 5) {
ElMessage.warning('尝试重连消息服务失败,请刷新重试')
return
}
showReconnectingMsg()
this._reconnectingTimer = window.setTimeout(() => {
this._createWebSocket()
}, 2000)
}
private _destroyWebSocket(reconnect?: boolean): void {
if (!this.socket) return
this._stopHeartbeat()
if (this._reconnectingTimer) {
window.clearTimeout(this._reconnectingTimer)
}
this.socket.removeEventListener('open', this._onWsOpen)
this.socket.removeEventListener('message', this._onWsMessage)
this.socket.removeEventListener('close', this._onWsClose)
this.socket.removeEventListener('error', this._onWsError)
this.socket.close(1000)
this.socket = null
localStorage.removeItem('socket_connect')
this._orderStore?.setSocketConnect('offline')
if (reconnect) this._scheduleReconnect()
}
private _createWebSocket(): void {
if (!this._wsUrl) return
const socket = new WebSocket(this._wsUrl)
socket.addEventListener('open', this._onWsOpen)
socket.addEventListener('message', this._onWsMessage)
socket.addEventListener('close', this._onWsClose)
socket.addEventListener('error', this._onWsError)
this.socket = socket
}
init(
options: InitOptions,
msgfunc?: MessageCallback,
openfunc?: OpenCallback,
): Promise<void> {
return new Promise((resolve, reject) => {
const { account, factoryNo, orderStore } = options
this._orderStore = orderStore
const socket_connect = localStorage.getItem('socket_connect')
if (socket_connect === 'online') {
resolve()
return
}
if (!window.WebSocket) {
reject(new Error('WebSocket is not supported'))
return
}
this._onMessageCallback = msgfunc
this._onOpenCallback = () => {
openfunc?.()
resolve()
}
this._destroyWebSocket()
this._wsUrl = `${getWsUrl()}/ws/websocket/${factoryNo}/${account}`
this._createWebSocket()
})
}
send(options: WebSocketMessage): void {
this.socket?.send(JSON.stringify(options))
}
close(): void {
this._destroyWebSocket()
}
}
export default new Im()
......@@ -61,6 +61,25 @@ export interface ProductList {
productName?: string
}
export interface PodMakeOrderSetPodBoxListParams {
boxList: PodMakeOrderData[] | OrderData | null | undefined
factoryNo: number | string
warehouseId?: number | string
box?: number
data?: OrderData
url?: string
fromUser?: number
}
// 播种墙配货store
export interface PodMakeOrderStore {
socketConnect?: string
podBoxList?: PodMakeOrderData[]
podBoxIndex?: number | null
setPodBoxList: (content: PodMakeOrderSetPodBoxListParams) => Promise<void>
setSocketConnect: (connect: string) => void
}
export interface LogisticBill {
expressSheet?: string
salesPlatform?: string
......
......@@ -897,14 +897,24 @@
<PodMakeOrder
v-model="podOrderVisible"
type="new"
title="配货打单"
:print-order="printOrder"
:warehouse-list="warehouseList"
:is-new-order="true"
ws-open-code="STARTORDERNEWPOD"
ws-close-code="ENDORDERNEWPOD"
init-url="factory/podOrderPacking/local/getPodBoxOrderDetails"
warehouse-storage-key="localNewWarehouseId"
order-no-name="操作单号"
:order-store="orderStore"
:is-clear-all="true"
:ws-codes="{ open: 'STARTORDERNEWPOD', close: 'ENDORDERNEWPOD' }"
:api-urls="{
getPodBoxList: 'factory/podOrderPacking/local/getPodBoxOrderDetails',
clearBox: 'factory/podOrderPacking/local/delPodBoxOrderDetailsByBox',
getPackingData: 'factory/podOrderPacking/local/putPackingSafe',
submitInspection: 'factory/podOrderPacking/podPrintOrderComplete',
printNormalPdf: 'factory/podJomallOrderUs/printNormalPickPdf',
clearAllBox: 'factory/podOrderPacking/local/delPodBoxOrderDetails',
}"
@set-printer="handlePrinterChange"
@set-warehouse-id="handleWarehouseIdChange"
@refresh="() => refreshCurrentView({ isRefreshTree: true })"
/>
<PodDistributionOrder
......@@ -1012,7 +1022,7 @@ import {
import { getLogisticsWayApi } from '@/api/podUsOrder'
import BigNumber from 'bignumber.js'
import { filePath } from '@/api/axios'
import { OrderData } from '@/types/api/podMakeOrder'
import { OrderData, type PodMakeOrderStore } from '@/types/api/podMakeOrder'
import useLodop, { LODOPObject } from '@/utils/hooks/useLodop'
import LogisticsWaySelect from '@/views/logistics/components/LogisticsWaySelect'
import ProductTypeFilter from './component/ProductTypeFilter.vue'
......@@ -1030,7 +1040,8 @@ import CreateLogisticDialog from '@/views/order/components/CreateLogisticDialog.
import UpdateCustomDeclarationInfoDialog from '@/views/order/components/UpdateCustomDeclarationInfoDialog.vue'
import WeightDialog from '@/views/order/components/WeightDialog.vue'
import FastProduction from '@/views/order/components/FastProduction.vue'
import PodMakeOrder from '@/views/order/podUs/PodMakeOrder.vue'
import PodMakeOrder from '@/components/PodMakeOrder/index.vue'
import useOrderStore from '@/store/order'
import PodDistributionOrder from '@/views/order/podCN/PodDistributionOrder.vue'
import PrintWarehouseSkuTag from '@/views/order/components/printWarehouseSkuTag.vue'
import UpdateAddress from '@/views/order/podCN/components/updateAddress.vue'
......@@ -2054,6 +2065,7 @@ const submitArrangeFinishApi = (params: {
const handleSingleConfirmOrder = (row: FactoryOrderNewListData) => {
confirmOrderDialogRef.value?.open([row.id])
}
const orderStore = useOrderStore() as unknown as PodMakeOrderStore
const podOrderVisible = ref(false)
const podDistributionOrderVisible = ref(false)
const sheetPrinter = ref('')
......@@ -2061,9 +2073,6 @@ const handlePrinterChange = (value: string) => {
sheetPrinter.value = value
localStorage.setItem('sheetPrinter', JSON.stringify(value))
}
const handleWarehouseIdChange = (value: string) => {
localStorage.setItem('localNewWarehouseId', JSON.stringify(value))
}
// 复用 podCN 的单件打单组件:为 factoryOrderNew 注入差异部分
const mapOrderParamListToSubmitItems = (
orderParamList: { id: number; dataVersion?: number }[],
......
......@@ -2650,10 +2650,20 @@
></FastProduction>
<PodMakeOrder
v-model="podOrderVisible"
type="cn"
:print-order="printOrder"
:warehouse-list="warehouseList"
warehouse-storage-key="locaclCnWarehouseId"
:order-store="orderStore"
:ws-codes="{ open: 'STARTORDERCN', close: 'ENDORDERCN' }"
:api-urls="{
getPodBoxList: 'factory/podJomallOrderCn/getPodBoxOrderDetails',
clearBox: 'factory/podJomallOrderCn/delPodBoxOrderDetailsByBox',
getPackingData: '/factory/podJomallOrderCn/getPodBoxDetailsBySkuOrNo',
submitInspection: 'factory/podJomallOrderCn/podPrintOrderComplete',
printNormalPdf: 'factory/podJomallOrderCn/printPickPdf',
}"
@set-printer="handlePrinterChange"
@set-warehouse-id="handleWarehouseIdChange"
@refresh="onFastRefresh"
/>
<!-- :print-order-one="printOrderOne" -->
......@@ -3050,10 +3060,11 @@ import { Column, ElFormItem, ElMessage } from 'element-plus'
import { computed, onMounted, ref, nextTick, reactive } from 'vue'
import FastProduction from '@/views/order/components/FastProduction.vue'
import { filePath } from '@/api/axios'
import PodMakeOrder from '@/views/order/components/PodMakeOrder.vue'
import PodMakeOrder from '@/components/PodMakeOrder/index.vue'
import useOrderStore from '@/store/cnOrder'
import PodDistributionOrder from './PodDistributionOrder.vue'
import SuperPodMakeOrder from './SuperPodMakeOrder.vue'
import { OrderData } from '@/types/api/podMakeOrder'
import { OrderData, type PodMakeOrderStore } from '@/types/api/podMakeOrder'
import useLodop, { LODOPObject } from '@/utils/hooks/useLodop'
import dayjs from 'dayjs'
import RightClickMenu from '@/components/RightClickMenu.vue'
......@@ -5169,6 +5180,7 @@ const fastClose = () => {
loadTabData()
detailVisible.value = false
}
const orderStore = useOrderStore() as PodMakeOrderStore
const podOrderVisible = ref(false)
const printPodOrder = async () => {
const lodop = getCLodop(null, null)
......@@ -5447,9 +5459,6 @@ const handlePrinterChange = (value: string) => {
sheetPrinter.value = value
localStorage.setItem('sheetPrinter', JSON.stringify(value))
}
const handleWarehouseIdChange = (value: string) => {
sessionStorage.setItem('locaclCnWarehouseId', JSON.stringify(value))
}
const { getCLodop } = useLodop()
const printOrder = async (
data: OrderData,
......
......@@ -982,7 +982,10 @@
</span>
</ElFormItem>
<ElFormItem
v-if="['WAIT_TRACK'].includes(status) && [1,2,5].includes(waitTrackStatus)"
v-if="
['WAIT_TRACK'].includes(status) &&
[1, 2, 5].includes(waitTrackStatus)
"
>
<span class="item">
<ElButton type="success" @click="reissueOrder">补发</ElButton>
......@@ -2776,10 +2779,22 @@
></FastProduction>
<PodMakeOrder
v-model="podOrderVisible"
type="us"
:print-order="printOrder"
:warehouse-list="warehouseList"
warehouse-storage-key="locaclWarehouseId"
:order-store="orderStore"
:is-clear-all="true"
:ws-codes="{ open: 'STARTORDER', close: 'ENDORDER' }"
:api-urls="{
getPodBoxList: 'factory/podJomallOrderUs/local/getPodBoxOrderDetails',
clearBox: 'factory/podJomallOrderUs/local/delPodBoxOrderDetailsByBox',
getPackingData: 'factory/podJomallOrderUs/local/putPackingSafe',
submitInspection: 'factory/podJomallOrderUs/podPrintOrderComplete',
printNormalPdf: 'factory/podJomallOrderUs/printNormalPickPdf',
clearAllBox: 'factory/podJomallOrderUs/local/delPodBoxOrderDetails',
}"
@set-printer="handlePrinterChange"
@set-warehouse-id="handleWarehouseIdChange"
@refresh="onFastRefresh"
/>
<InspPackagOrder
......@@ -3153,7 +3168,7 @@
<ReissueOrderComponent
ref="reissueOrderRef"
:selection="selection"
:trackRegisterSelect="waitTrackStatus"
:track-register-select="waitTrackStatus"
@success="handleSuccess"
></ReissueOrderComponent>
......@@ -3281,9 +3296,10 @@ import { Column, ElFormItem, ElMessage } from 'element-plus'
import { computed, onMounted, ref, nextTick, reactive, h } from 'vue'
import FastProduction from './FastProduction.vue'
import { filePath } from '@/api/axios'
import PodMakeOrder from './PodMakeOrder.vue'
import PodMakeOrder from '@/components/PodMakeOrder/index.vue'
import useOrderStore from '@/store/order'
import InspPackagOrder from './InspPackagOrder.vue'
import { OrderData } from '@/types/api/podMakeOrder'
import { OrderData, type PodMakeOrderStore } from '@/types/api/podMakeOrder'
import useLodop, { LODOPObject } from '@/utils/hooks/useLodop'
import dayjs from 'dayjs'
import RightClickMenu from '@/components/RightClickMenu.vue'
......@@ -5501,6 +5517,7 @@ const fastClose = () => {
loadTabData()
detailVisible.value = false
}
const orderStore = useOrderStore() as unknown as PodMakeOrderStore
const podOrderVisible = ref(false)
const printPodOrder = async () => {
const lodop = getCLodop(null, null)
......@@ -5981,9 +5998,6 @@ const handlePrinterChange = (value: string) => {
sheetPrinter.value = value
localStorage.setItem('sheetPrinter', JSON.stringify(value))
}
const handleWarehouseIdChange = (value: string) => {
localStorage.setItem('locaclWarehouseId', JSON.stringify(value))
}
const { getCLodop } = useLodop()
const printOrder = async (
data: OrderData,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment