Commit 43b046d8 by qinjianhui

feat: 胚衣入库功能开发

parent a96bb2d7
......@@ -302,7 +302,18 @@
</ElForm>
</div>
</div>
<div class="operation-list"></div>
<div class="operation-list">
<span v-if="currentStatus === 'processed'" class="item">
<ElButton type="primary" @click="handleEmbryoInStock"
>胚衣入库</ElButton
>
</span>
<span v-if="currentStatus === 'processed'" class="item">
<ElButton type="success" @click="handleProductionCompleteInStock"
>生产完成入库</ElButton
>
</span>
</div>
<div class="card-content">
<CardLayout
ref="cardLayoutRef"
......@@ -325,14 +336,42 @@
pending-order-label="操作单号"
please-scan-tip="请扫码操作单号"
search-input-audio-tip="请录入操作单号"
history-storage-key="historyFactoryOrderNewData"
tracking-placeholder="扫描枪输入操作单号,录入下一单本单自动生产完成,最后一单扫两次完成生产"
history-storage-key="historyFactoryNewOrderFastReceipt"
tracking-placeholder="扫描枪输入操作单号"
:query-api="getOperationByNo"
:complete-api="completeOperationById"
:download-api="downloadOperationById"
@on-success="handleFastProductionSuccess"
@close="fastClose"
/>
<ReceiptProductDialog
ref="receiptProductDialogRef"
v-model:visible="receiptDialogVisible"
v-model:user-mark="receiptUserMark"
v-model:select-sku="receiptSelectSku"
title="创建入库单"
:disable-warehouse="true"
:show-user-mark-filter="false"
:show-query-button="false"
:show-batch-add-button="false"
:show-import-button="false"
:edit-form="receiptEditForm"
:rules="receiptRules"
:warehouse-list="warehouseList"
:other-purchase-data="otherPurchaseData"
:location-list="locationList"
:user-mark-list="receiptUserMarkList"
:sku-data="skuData"
:filter-sku-data="filterSkuData"
@warehouse-change="handleReceiptWarehouseChange"
@selection-change="handleReceiptProductSelectionChange"
@set-cost-price="setCostPrice"
@location-change="handleReceiptLocationChange"
@update:remark="updateReceiptRemark"
@update:warehouse-id="updateReceiptWarehouseId"
@delete="deleteReceiptProducts"
@save="handleReceiptSave"
/>
<!-- 快捷入库 -->
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
......@@ -342,7 +381,6 @@ import { useOrderDictionaries } from '../factoryOrderNew/hooks/useOrderDictionar
import { operateOrderListData } from '@/types/api/factoryOrderNew'
import {
completeDeliveryApi,
downloadOperationMaterialApi,
getByOperationNoLogApi,
getFactoryOrderNewOperateDetailApi,
} from '@/api/factoryOrderNew'
......@@ -355,6 +393,23 @@ import { CaretBottom, CaretTop } from '@element-plus/icons-vue'
import { normalizeProductMarkListForQuery } from '../factoryOrderNew/utils/productMarkQuery'
import FastProduction from '../components/FastProduction.vue'
import { ResultInfoDataItem } from '@/types/api/order/common'
import { ElButton } from 'element-plus'
import ReceiptProductDialog from '@/views/warehouse/components/ReceiptProductDialog.vue'
import { useValue } from '@/utils/hooks/useValue'
import {
ILocation,
InterProductList,
InterskuList,
InterWarehouseDetail,
} from '@/types/api/warehouse'
import { useReceiptProductDialog } from '@/views/warehouse/hooks/useReceiptProductDialog'
import {
addInRecordApi,
getBySkuAndUserMarkApi,
getByWareHouseIdAndCodeApi,
warehouseInfo,
warehouseInfoGetAll,
} from '@/api/warehouse'
const cardLayoutRef = ref()
......@@ -459,8 +514,6 @@ const handleViewDetail = async (item: operateOrderListData) => {
const getOperationByNo = (operationNo: string) =>
getByOperationNoLogApi(operationNo) as Promise<{ data?: unknown }>
const completeOperationById = (ids: number[]) => completeDeliveryApi(ids)
const downloadOperationById = (ids: number[]) =>
downloadOperationMaterialApi(ids)
const handleFastProductionSuccess = (data: ResultInfoDataItem[]) => {
const result = data[0]
if (!result.status) {
......@@ -499,6 +552,228 @@ const handleOrderStatusClick = (status: 'processed' | 'unprocessed') => {
currentStatus.value = status
search()
}
/**胚衣入库 */
const receiptDialogVisible = ref(false)
const receiptUserMark = ref(0)
const receiptSelectSku = ref('')
const otherPurchaseData = ref<InterProductList[]>([])
const [receiptEditForm, resetReceiptEditForm] = useValue<InterWarehouseDetail>({
inNo: '',
warehouseId: '',
warehouseName: '',
remark: '',
factoryCode: '',
factoryId: 0,
productList: [],
})
const receiptRules = {
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
}
const batchUserMark = ref(0)
const importUserMark = ref(0)
const receiptUserMarkList = ref<
{ userId: number; userMark: string; userName: string }[]
>([{ userId: 0, userMark: '', userName: '' }])
const warehouseList = ref<warehouseInfo[]>([])
const locationList = ref<ILocation[]>([])
const otherReceiptSelection = ref<InterProductList[]>([])
const receiptProductDialogRef = ref()
const { skuData, setCostPrice, filterSkuData } = useReceiptProductDialog({
editForm: receiptEditForm,
otherPurchaseData,
userMark: receiptUserMark,
batchUserMark,
importUserMark,
selectSku: receiptSelectSku,
userMarkList: receiptUserMarkList,
})
function mapInterskuToProduct(val: InterskuList): InterProductList {
const warehouseSku = val.warehouseSku || val.sku || ''
const skuNameVal = val.skuName || val.productName || ''
let customerId: number | null = null
if (val.customerId != null && val.customerId !== '') {
const n = Number(val.customerId)
if (!Number.isNaN(n)) customerId = n
}
return {
skuImage: val.skuImage || val.image || '',
warehouseSku,
skuName: skuNameVal,
productNo: val.productNo ?? null,
locationCode: val.locationCode ?? '',
locationId: val.locationId ?? null,
costPrice: val.costPrice ?? null,
buyStored: null,
totalPrice: null,
currencyName: val.currencyName ?? null,
currencyCode: val.currencyCode ?? null,
customerId,
customerName: val.customerName ?? null,
userMark: val.userMark ?? null,
}
}
const handleEmbryoInStock = async () => {
if (cardSelectList.value.length !== 0) {
handleEmbryoInStockByCard()
} else {
handleEmbryoInFastStock()
}
}
const handleEmbryoInFastStock = async () => {
fastKey.value = 'fastReceipt'
detailVisible.value = true
detailData.value = {}
}
const handleEmbryoInStockByCard = async () => {
const firstWid = cardSelectList.value[0].warehouseId
if (cardSelectList.value.some((r) => r.warehouseId !== firstWid)) {
return ElMessage.error('请选择相同仓库的库存SKU!')
}
resetReceiptEditForm()
otherPurchaseData.value = []
receiptUserMark.value = 0
receiptSelectSku.value = ''
skuData.value = []
otherReceiptSelection.value = []
const userJson = localStorage.getItem('user')
if (userJson) {
try {
const userData = JSON.parse(userJson)
receiptEditForm.value.factoryCode = userData.factoryCode || ''
receiptEditForm.value.factoryId = userData.factoryId || 0
} catch {
// ignore
}
}
receiptEditForm.value.warehouseId = firstWid
receiptEditForm.value.warehouseName = cardSelectList.value[0]
.warehouseName as string
const wh = warehouseList.value.find((w) => w.id == firstWid)
if (wh) {
receiptEditForm.value.warehouseName = wh.name
}
const loading = ElLoading.service({
text: '加载中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const skus = cardSelectList.value.map((r) => r.thirdSkuCode as string)
const res = await getBySkuAndUserMarkApi(firstWid, skus.join(','), null)
if (res.code !== 200) return
otherPurchaseData.value = res.data.map((item: InterskuList) =>
mapInterskuToProduct(item),
)
await fetchReceiptLocationList('')
const listRes = await warehouseInfoGetAll()
warehouseList.value = listRes.data || []
receiptDialogVisible.value = true
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const fetchReceiptLocationList = async (query: string) => {
if (!receiptEditForm.value.warehouseId) return
try {
const res = await getByWareHouseIdAndCodeApi(
receiptEditForm.value.warehouseId,
query,
)
const result = res.data || []
locationList.value = result.map((item: ILocation) => ({
locationId: item.id,
locationCode: item.locationCode,
}))
} catch (e) {
console.error(e)
}
}
const handleReceiptWarehouseChange = (val: number | string | undefined) => {
const found = warehouseList.value.find(
(item: warehouseInfo) => item.id === val,
)
receiptEditForm.value.warehouseName = found ? found.name : ''
}
const handleReceiptProductSelectionChange = (v: InterProductList[]) => {
otherReceiptSelection.value = v
}
const handleReceiptLocationChange = (
val: number | null | undefined,
row: InterProductList,
) => {
const found = locationList.value.find(
(item: ILocation) => item.locationId === val,
)
row.locationCode = found ? found.locationCode : ''
}
const updateReceiptRemark = (value: string) => {
receiptEditForm.value.remark = value
}
const updateReceiptWarehouseId = (value: number | string | undefined) => {
receiptEditForm.value.warehouseId = value
}
const deleteReceiptProducts = () => {
const arr = otherReceiptSelection.value
if (arr.length === 0) return
const idList = arr.map((v: InterProductList) => v.warehouseSku)
otherPurchaseData.value = otherPurchaseData.value.filter(
(item: InterProductList) => !idList.includes(item.warehouseSku),
)
}
const handleReceiptSave = async () => {
try {
await receiptProductDialogRef.value?.validateForm()
} catch {
return
}
const arr = otherPurchaseData.value
if (arr.length === 0) {
ElMessage.error('请至少选择一条数据')
return
}
for (let i = 0; i < arr.length; i++) {
if (!arr[i].buyStored) {
ElMessage.error('请输入入库数量')
return
}
if (!arr[i].locationId) {
ElMessage.error('请选择库位')
return
}
const found = locationList.value.find(
(item: ILocation) => item.locationId === arr[i].locationId,
)
if (!arr[i].locationCode) {
arr[i].locationCode = found ? found?.locationCode : ''
}
}
const params = {
...receiptEditForm.value,
productList: otherPurchaseData.value,
}
const loading = ElLoading.service({
text: '保存中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
await addInRecordApi(params)
ElMessage.success('保存成功')
receiptDialogVisible.value = false
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const handleProductionCompleteInStock = () => {
console.log('生产完成入库')
}
/**胚衣入库完成 */
onMounted(() => {
void loadAllDictionaries()
search()
......@@ -521,7 +796,7 @@ onMounted(() => {
}
&.order-status-item-active {
background-color: #f5f7fa;
color: #409EFF;
color: #409eff;
}
}
.card-content {
......
<template>
<el-dialog
v-model="dialogVisible"
:title="fastKey === 'detail' ? '查看详情' : '快捷生产'"
:title="title"
top="140px"
:fullscreen="true"
:close-on-click-modal="false"
......@@ -44,7 +44,10 @@
</div>
</div>
<div class="right">
<div v-if="fastKey === 'fastProduction'" class="input">
<div
v-if="['fastProduction', 'fastReceipt'].includes(fastKey)"
class="input"
>
<el-input
ref="trackingNumberRef"
v-model="TrackingNumber"
......@@ -60,20 +63,36 @@
<div class="div-text">
<div class="div-content">
<div :title="String(detail?.userMark)" class="div-item">
<span style="font-size: 18px">客户</span>
<p style="color: red; font-size: 30px">
<span class="div-item-label" style="font-size: 18px"
>客户:</span
>
<div class="div-item-value" style="color: red; font-size: 30px">
{{ detail?.userMark }}
</p>
</div>
</div>
<div
:title="String(detail?.factoryOrderNumber)"
class="div-item"
style="margin-top: 14px"
>
<span style="font-size: 18px">订单号</span>
<p style="color: red; font-size: 22px">
<span class="div-item-label" style="font-size: 18px"
>订单号:</span
>
<div class="div-item-value" style="color: red; font-size: 22px">
{{ detail?.factoryOrderNumber }}
</p>
</div>
</div>
<div
:title="String(detail?.shopNumber)"
class="div-item"
style="margin-top: 14px"
>
<span class="div-item-label" style="font-size: 18px"
>店铺单号:</span
>
<div class="div-item-value" style="color: red; font-size: 22px">
{{ detail?.shopNumber }}
</div>
</div>
</div>
</div>
......@@ -86,77 +105,132 @@
:title="String(operationNoDisplay)"
class="div-item"
>
<span>操作单号</span>
<p>{{ operationNoDisplay }}</p>
<span class="div-item-label">操作单号:</span>
<div class="div-item-value">{{ operationNoDisplay }}</div>
</div>
<div
v-else
:title="detail?.factorySubOrderNumber"
class="div-item"
>
<span>生产单号</span>
<p>
<span class="div-item-label">生产单号:</span>
<div class="div-item-value">
{{ detail?.factorySubOrderNumber }}
</p>
</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="setProductMark(String(detail.productMark))"
class="div-item"
>
<span class="div-item-label">类型:</span>
<div
class="div-item-value"
style="font-weight: bold; color: red"
>
{{ setProductMark(String(detail.productMark)) }}
</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.productName) || ''"
class="div-item"
>
<span class="div-item-label">商品名称:</span>
<div class="div-item-value">{{ detail.productName || '' }}</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.statusName || '')"
class="div-item"
>
<span class="div-item-label">挂起前状态:</span>
<div class="div-item-value">
<ElTag v-if="detail.statusName" type="primary">{{ detail.statusName || '' }}</ElTag
>
</div>
</div>
<div
v-if="fastKey === 'fastReceipt'"
:title="String(detail.thirdSkuCode)"
class="div-item"
>
<span class="div-item-label">库存SKU:</span>
<div class="div-item-value">{{ detail.thirdSkuCode }}</div>
</div>
<div
v-if="!showOperationNoRow"
:title="detail?.thirdSubOrderNumber || ''"
class="div-item"
>
<span>第三方生产单号</span>
<p>
<span class="div-item-label">第三方生产单号:</span>
<div class="div-item-value">
{{ detail?.thirdSubOrderNumber }}
</p>
</div>
</div>
<div :title="String(detail?.craftName)" class="div-item">
<span>生产工艺</span>
<p>
<span class="div-item-label">生产工艺:</span>
<div class="div-item-value">
{{ detail?.craftName }}
</p>
</div>
<div :title="detail?.baseSku" class="div-item">
<span>基版</span>
<p>{{ detail?.baseSku }}</p>
</div>
<div
v-if="fastKey !== 'fastReceipt'"
:title="detail?.baseSku"
class="div-item"
>
<span class="div-item-label">基版:</span>
<div class="div-item-value">{{ detail?.baseSku }}</div>
</div>
<div :title="detail?.variantSku" class="div-item">
<span>变体SKU</span>
<p>{{ detail?.variantSku }}</p>
<span class="div-item-label">变体SKU:</span>
<div class="div-item-value">{{ detail?.variantSku }}</div>
</div>
<div
v-if="fastKey !== 'fastReceipt'"
:title="
String(showOperationNoRow ? detail?.quantity : detail?.num)
"
class="div-item"
>
<span>数量</span>
<p>{{ showOperationNoRow ? detail?.quantity : detail?.num }}</p>
<span class="div-item-label">数量:</span>
<div class="div-item-value">
{{ showOperationNoRow ? detail?.quantity : detail?.num }}
</div>
</div>
<div :title="String(detail?.size)" class="div-item">
<span>尺寸</span>
<p>{{ detail?.size }}</p>
<div
v-if="fastKey !== 'fastReceipt'"
:title="String(detail?.size)"
class="div-item"
>
<span class="div-item-label">尺寸:</span>
<div class="div-item-value">{{ detail?.size }}</div>
</div>
<div
v-if="detail?.sizeType"
v-if="detail?.sizeType && fastKey !== 'fastReceipt'"
:title="String(detail?.sizeType)"
class="div-item"
>
<span>尺码类型</span>
<p>
<span class="div-item-label">尺码类型:</span>
<div class="div-item-value">
{{ sizeList.find((i) => i.value === detail.sizeType)?.name }}
</p>
</div>
</div>
<div :title="detail?.shopNumber ?? ''" class="div-item">
<span>店铺单号</span>
<p>{{ detail?.shopNumber ?? '' }}</p>
<div
v-if="fastKey !== 'fastReceipt'"
:title="detail?.shopNumber ?? ''"
class="div-item"
>
<span class="div-item-label">店铺单号:</span>
<div class="div-item-value">{{ detail?.shopNumber ?? '' }}</div>
</div>
<div :title="detail?.createTime" class="div-item">
<span>创建时间</span>
<p>{{ detail?.createTime }}</p>
<span class="div-item-label">创建时间:</span>
<div class="div-item-value">{{ detail?.createTime }}</div>
</div>
</div>
</div>
......@@ -164,7 +238,10 @@
<div class="btn">
<div
:style="{
visibility: fastKey === 'fastProduction' ? 'visible' : 'hidden',
visibility:
fastKey === 'fastProduction' || fastKey === 'fastReceipt'
? 'visible'
: 'hidden',
}"
class="btn-sure"
>
......@@ -174,13 +251,13 @@
type="success"
@click="changeStatus"
>
生产完成
{{ fastKey === 'fastProduction' ? '生产完成' : '快捷入库' }}
</el-button>
<div class="check">
<el-checkbox v-model="isAutoSure"> 自动完成上一单 </el-checkbox>
</div>
</div>
<div class="btn-down">
<div v-if="fastKey !== 'fastReceipt'" class="btn-down">
<div class="check">
<el-checkbox v-model="isDownloadImage" size="large">
扫码下载素材
......@@ -243,12 +320,21 @@ interface HistoryDataItem {
orderNumber: string
finished: boolean
}
const title = computed(() => {
if (props.fastKey === 'fastProduction') {
return '快捷生产'
} else if (props.fastKey === 'fastReceipt') {
return '快捷入库'
} else {
return '查看详情'
}
})
export type FastProductionDetail = PodOrderRes & {
operationNo?: string | null
quantity?: number | null
podOrderNo?: string | null
podOrderProductId?: number | null
statusName?: string | null
}
const trackingNumberRef = ref()
const historyData = ref<HistoryDataItem[]>([])
......@@ -422,7 +508,10 @@ watch(
historyData.value = history ? JSON.parse(history) : []
const len = historyData.value
if (len.length > 0 && props.fastKey === 'fastProduction') {
if (
len.length > 0 &&
(props.fastKey === 'fastProduction' || props.fastKey === 'fastReceipt')
) {
confirmQuery(len, 0)
}
placeholderText.value = props.trackingPlaceholder
......@@ -452,7 +541,9 @@ watch(
const confirmQuery = (len: HistoryDataItem[], i: number) => {
const el = len[i]
showConfirm(
`${props.pendingOrderLabel} ${el.orderNumber} 未生产完成,取消则不提醒?`,
`${props.pendingOrderLabel} ${el.orderNumber} ${
props.fastKey === 'fastProduction' ? '未生产' : '未入库'
}完成,取消则不提醒?`,
{
confirmButtonText: '确定',
cancelButtonText: '取消',
......@@ -562,7 +653,7 @@ const download = async () => {
if (detail.value && detail.value?.id != -1) {
try {
const id = props.isNewOrder
? detail.value.podOrderProductId as number
? (detail.value.podOrderProductId as number)
: detail.value.id
if (id !== undefined) {
try {
......@@ -668,6 +759,12 @@ const trackCodeInput = async () => {
const onOpened = () => {
trackingNumberRef.value && trackingNumberRef.value.focus()
}
function setProductMark(productMark: string) {
if (!productMark) return ''
if (productMark === 'custom_normal') return 'CB'
if (productMark === 'normal') return 'G'
return ''
}
</script>
<style lang="scss" scoped>
.sure-btn {
......@@ -784,27 +881,27 @@ const onOpened = () => {
align-items: flex-start;
padding: 15px 10px;
box-sizing: border-box;
position: relative;
border: 1px solid #ececec;
.div-item {
width: 50%;
margin-bottom: 10px;
display: flex;
align-items: center;
height: 36px;
line-height: 36px;
p {
font-weight: 400;
color: black;
}
span {
.div-item-label {
display: inline-block;
text-align: right;
width: 120px;
}
span::after {
content: ':';
margin: 0 3px;
.div-item-value {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
......@@ -819,11 +916,6 @@ const onOpened = () => {
color: black;
z-index: 3;
}
.div-content {
position: relative;
border: 1px solid #ececec;
}
}
.input {
......
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