feat(wms):修复只能删除作废的入库单的问题

pull/878/head
YunaiV 2026-05-13 00:42:29 +08:00
parent ac49ba5c6d
commit 765c8ea94f
7 changed files with 826 additions and 31 deletions

View File

@ -63,7 +63,7 @@ export const defaultShortcuts = [
* @description format + + "YYYY-MM-DD HH:mm:ss WWW QQQQ ZZZ"
* @returns
*/
export function formatDate(date: Date, format?: string): string {
export function formatDate(date: Date | string, format?: string): string {
// 日期不存在,则返回空
if (!date) {
return ''
@ -72,6 +72,25 @@ export function formatDate(date: Date, format?: string): string {
return date ? dayjs(date).format(format ?? 'YYYY-MM-DD HH:mm:ss') : ''
}
/**
*
*
* @param date new Date()
* @param format
* @param emptyText
* @returns
*/
export function formatNullableDate(
date?: Date | string | null,
format = 'YYYY-MM-DD HH:mm:ss',
emptyText = '-'
): string {
if (!date) {
return emptyText
}
return formatDate(date, format) || emptyText
}
/**
* +
*/

View File

@ -0,0 +1,295 @@
<!-- WMS 商品 SKU 选择器 -->
<template>
<Dialog
v-model="dialogVisible"
:append-to-body="true"
:scroll="true"
max-height="calc(100vh - 220px)"
title="商品选择"
width="80%"
>
<ContentWrap>
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="80px"
>
<el-form-item label="商品名称" prop="itemName">
<el-input
v-model="queryParams.itemName"
class="!w-240px"
clearable
placeholder="请输入商品名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="商品编号" prop="itemCode">
<el-input
v-model="queryParams.itemCode"
class="!w-240px"
clearable
placeholder="请输入商品编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="规格名称" prop="skuName">
<el-input
v-model="queryParams.skuName"
class="!w-240px"
clearable
placeholder="请输入规格名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="规格编号" prop="skuCode">
<el-input
v-model="queryParams.skuCode"
class="!w-240px"
clearable
placeholder="请输入规格编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap class="!mb-0">
<el-table
ref="tableRef"
v-loading="loading"
:data="list"
:show-overflow-tooltip="true"
:stripe="true"
border
max-height="calc(100vh - 430px)"
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleRowDblClick"
>
<el-table-column :reserve-selection="true" align="center" type="selection" width="50" />
<el-table-column label="商品信息" min-width="220">
<template #default="{ row }">
<div>{{ row.itemName || '-' }}</div>
<div v-if="row.itemCode" class="text-12px text-gray-500">
商品编号{{ row.itemCode }}
</div>
<div v-if="row.brandName" class="text-12px text-gray-500">{{ row.brandName }}</div>
</template>
</el-table-column>
<el-table-column label="规格信息" min-width="220">
<template #default="{ row }">
<div>{{ row.name || '-' }}</div>
<div v-if="row.code" class="text-12px text-gray-500">{{ row.code }}</div>
<div v-if="row.barCode" class="text-12px text-gray-500">{{ row.barCode }}</div>
</template>
</el-table-column>
<el-table-column label="金额(元)" min-width="160">
<template #default="{ row }">
<div v-if="row.costPrice !== undefined">{{ formatPrice(row.costPrice) }}</div>
<div v-if="row.sellingPrice !== undefined">{{ formatPrice(row.sellingPrice) }}</div>
</template>
</el-table-column>
<el-table-column label="重量(kg)" min-width="160">
<template #default="{ row }">
<div v-if="row.netWeight !== undefined">{{ formatWeight(row.netWeight) }}</div>
<div v-if="row.grossWeight !== undefined">{{ formatWeight(row.grossWeight) }}</div>
</template>
</el-table-column>
<el-table-column align="right" label="长宽高(cm)" min-width="180">
<template #default="{ row }">
{{ formatDimensionText(row.length, row.width, row.height) || '-' }}
</template>
</el-table-column>
</el-table>
<div class="overflow-hidden">
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</div>
</ContentWrap>
<template #footer>
<el-button type="primary" @click="handleConfirm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { ElTable } from 'element-plus'
import { ItemApi, ItemVO } from '@/api/wms/md/item'
import { ItemSkuVO } from '@/api/wms/md/item/sku'
import { formatDimensionText, formatPrice, formatWeight } from '@/views/wms/utils/format'
/** WMS 商品 SKU 选择器 */
defineOptions({ name: 'WmsItemSkuSelect' })
const message = useMessage() //
const loading = ref(false) //
const dialogVisible = ref(false) //
const allList = ref<ItemSkuVO[]>([]) // SKU
const filteredList = ref<ItemSkuVO[]>([]) // SKU
const list = ref<ItemSkuVO[]>([]) // SKU
const total = ref(0) //
const selectedList = ref<ItemSkuVO[]>([]) // SKU
const selectedMap = ref<Map<number, ItemSkuVO>>(new Map()) // SKU
const preSelectedIds = ref<number[]>([]) // SKU
const tableRef = ref<InstanceType<typeof ElTable>>() // Ref
const queryFormRef = ref() //
const getDefaultQueryParams = () => ({
pageNo: 1,
pageSize: 10,
itemName: undefined as string | undefined,
itemCode: undefined as string | undefined,
skuName: undefined as string | undefined,
skuCode: undefined as string | undefined
})
const queryParams = reactive(getDefaultQueryParams())
const emit = defineEmits<{
change: [list: ItemSkuVO[]]
}>()
/** 打开弹窗 */
const open = async (selectedIds?: number[]) => {
dialogVisible.value = true
Object.assign(queryParams, getDefaultQueryParams())
selectedList.value = []
selectedMap.value = new Map()
preSelectedIds.value = selectedIds || []
await nextTick()
tableRef.value?.clearSelection()
await loadSkuList()
}
defineExpose({ open })
/** 获得 SKU 列表 */
const loadSkuList = async () => {
loading.value = true
try {
const items = await ItemApi.getItemSimpleList()
allList.value = items.flatMap((item: ItemVO) =>
(item.skus || []).map((sku) => ({
...sku,
itemId: item.id,
itemCode: item.code,
itemName: item.name,
categoryId: item.categoryId,
categoryName: item.categoryName,
unit: item.unit,
brandId: item.brandId,
brandName: item.brandName
}))
)
initSelectedList()
getList()
} finally {
loading.value = false
}
}
/** 初始化已选 SKU */
const initSelectedList = () => {
if (preSelectedIds.value.length === 0) {
return
}
allList.value.forEach((sku) => {
if (sku.id && preSelectedIds.value.includes(sku.id)) {
selectedMap.value.set(sku.id, sku)
}
})
selectedList.value = Array.from(selectedMap.value.values())
}
/** 查询 SKU 列表 */
const getList = async () => {
filteredList.value = allList.value.filter((sku) => {
return (
includes(sku.itemName, queryParams.itemName) &&
includes(sku.itemCode, queryParams.itemCode) &&
includes(sku.name, queryParams.skuName) &&
includes(sku.code, queryParams.skuCode)
)
})
total.value = filteredList.value.length
const start = (queryParams.pageNo - 1) * queryParams.pageSize
list.value = filteredList.value.slice(start, start + queryParams.pageSize)
await nextTick()
applyPreSelection()
}
const includes = (value?: string, keyword?: string) => {
if (!keyword) {
return true
}
return (value || '').toLowerCase().includes(keyword.toLowerCase())
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
Object.assign(queryParams, getDefaultQueryParams())
queryFormRef.value?.resetFields()
getList()
}
/** 恢复预选状态(当前页可见范围内) */
const applyPreSelection = () => {
const table = tableRef.value
if (!table || selectedMap.value.size === 0) {
return
}
list.value.forEach((row) => {
if (row.id && selectedMap.value.has(row.id)) {
table.toggleRowSelection(row, true)
}
})
}
/** 选择变化 */
const handleSelectionChange = (rows: ItemSkuVO[]) => {
const currentPageIds = list.value.map((row) => row.id).filter((id): id is number => !!id)
currentPageIds.forEach((id) => selectedMap.value.delete(id))
rows.forEach((row) => {
if (row.id) {
selectedMap.value.set(row.id, row)
}
})
selectedList.value = Array.from(selectedMap.value.values())
}
/** 双击行:切换勾选 */
const handleRowDblClick = (row: ItemSkuVO) => {
tableRef.value?.toggleRowSelection(row)
}
/** 确认选择 */
const handleConfirm = () => {
if (selectedList.value.length === 0) {
message.warning('请至少选择一条数据')
return
}
emit('change', selectedList.value)
dialogVisible.value = false
}
</script>

View File

@ -40,13 +40,13 @@
{{ formatPrice(detailData.totalAmount) || '-' }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ formatNullableDate(detailData.createTime) || '-' }}
{{ formatNullableDate(detailData.createTime) }}
</el-descriptions-item>
<el-descriptions-item label="创建人">
{{ detailData.creatorName || detailData.creator || '-' }}
</el-descriptions-item>
<el-descriptions-item label="更新时间">
{{ formatNullableDate(detailData.updateTime) || '-' }}
{{ formatNullableDate(detailData.updateTime) }}
</el-descriptions-item>
<el-descriptions-item label="更新人">
{{ detailData.updaterName || detailData.updater || '-' }}
@ -78,12 +78,12 @@
<el-table-column v-if="BATCH_ENABLE" label="批号" min-width="140" prop="batchNo" />
<el-table-column v-if="BATCH_ENABLE" label="生产日期" width="140">
<template #default="scope">
{{ formatNullableDate(scope.row.productionDate, 'YYYY-MM-DD') || '-' }}
{{ formatNullableDate(scope.row.productionDate, 'YYYY-MM-DD') }}
</template>
</el-table-column>
<el-table-column v-if="BATCH_ENABLE" label="过期日期" width="140">
<template #default="scope">
{{ formatNullableDate(scope.row.expirationDate, 'YYYY-MM-DD') || '-' }}
{{ formatNullableDate(scope.row.expirationDate, 'YYYY-MM-DD') }}
</template>
</el-table-column>
<el-table-column align="right" label="数量" prop="quantity" width="120">
@ -108,7 +108,7 @@
</template>
<script lang="ts" setup>
import { formatDate } from '@/utils/formatTime'
import { formatNullableDate } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict'
import { ReceiptOrderApi, ReceiptOrderVO } from '@/api/wms/order/receipt'
import { ReceiptOrderDetailVO } from '@/api/wms/order/receipt/detail'
@ -140,11 +140,6 @@ const detailRows = computed<DetailRow[]>(() =>
}))
)
// TODO @AI date time ts
const formatNullableDate = (value?: Date | string, format?: string) => {
return value ? formatDate(value as Date, format) : ''
}
const getSummaries = ({ columns, data }: { columns: any[]; data: DetailRow[] }) =>
columns.map((column, index) => {
if (index === 0) {

View File

@ -0,0 +1,480 @@
<!-- WMS 入库单表单 -->
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1280px">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="92px"
>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="入库单号" prop="no">
<el-input v-model="formData.no" maxlength="64" placeholder="请输入入库单号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入库类型" prop="type">
<el-select v-model="formData.type" class="w-1/1" placeholder="请选择入库类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.WMS_RECEIPT_ORDER_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="供应商" prop="merchantId">
<MerchantSelect
v-model="formData.merchantId"
placeholder="请选择供应商"
supplier
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="业务单号" prop="bizOrderNo">
<el-input v-model="formData.bizOrderNo" maxlength="64" placeholder="请输入业务单号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<WarehouseSelect v-model="formData.warehouseId" @change="handleWarehouseChange" />
</el-form-item>
</el-col>
<el-col v-if="AREA_ENABLE" :span="8">
<el-form-item label="库区" prop="areaId">
<WarehouseAreaSelect
v-model="formData.areaId"
:warehouse-id="formData.warehouseId"
@change="handleAreaChange"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总数量">
<el-input :model-value="formatQuantity(totalQuantity)" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额">
<el-input-number
v-model="formData.totalAmount"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-1/1"
placeholder="请输入总金额"
/>
</el-form-item>
</el-col>
<el-col :span="AREA_ENABLE ? 8 : 16">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" maxlength="255" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
<div class="mb-12px flex items-center justify-between">
<span class="text-14px font-bold">入库明细</span>
<el-button :disabled="!formData.warehouseId" plain type="primary" @click="handleAddDetail">
<Icon class="mr-5px" icon="ep:plus" />
添加商品
</el-button>
</div>
<el-table :data="formData.details" border empty-text="">
<el-table-column label="商品信息" min-width="220">
<template #default="scope">
<div>{{ scope.row.itemName || '-' }}</div>
<div v-if="scope.row.itemCode" class="text-12px text-gray-500">
商品编号{{ scope.row.itemCode }}
</div>
</template>
</el-table-column>
<el-table-column label="规格信息" min-width="220">
<template #default="scope">
<div>{{ scope.row.skuName || '-' }}</div>
<div v-if="scope.row.skuCode" class="text-12px text-gray-500">
规格编号{{ scope.row.skuCode }}
</div>
</template>
</el-table-column>
<el-table-column v-if="AREA_ENABLE" label="库区" min-width="180">
<template #default="scope">
<WarehouseAreaSelect
v-model="scope.row.areaId"
:disabled="!formData.warehouseId || !!formData.areaId"
:warehouse-id="formData.warehouseId"
placeholder="请选择库区"
/>
</template>
</el-table-column>
<el-table-column v-if="BATCH_ENABLE" label="批号" min-width="160">
<template #default="scope">
<el-input v-model="scope.row.batchNo" maxlength="64" placeholder="请输入批号" />
</template>
</el-table-column>
<el-table-column v-if="BATCH_ENABLE" label="生产日期" width="180">
<template #default="scope">
<el-date-picker
v-model="scope.row.productionDate"
class="!w-1/1"
placeholder="请选择生产日期"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</template>
</el-table-column>
<el-table-column v-if="BATCH_ENABLE" label="过期日期" width="180">
<template #default="scope">
<el-date-picker
v-model="scope.row.expirationDate"
class="!w-1/1"
placeholder="请选择过期日期"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</template>
</el-table-column>
<el-table-column label="入库数量" width="160">
<template #default="scope">
<el-input-number
v-model="scope.row.quantity"
:controls="false"
:min="0"
:precision="QUANTITY_PRECISION"
class="!w-1/1"
placeholder="数量"
/>
</template>
</el-table-column>
<el-table-column label="金额(元)" width="160">
<template #default="scope">
<el-input-number
v-model="scope.row.amount"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-1/1"
placeholder="金额"
@change="handleDetailAmountChange"
/>
</template>
</el-table-column>
<el-table-column label="备注" min-width="180">
<template #default="scope">
<el-input v-model="scope.row.remark" maxlength="255" placeholder="请输入备注" />
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="80">
<template #default="scope">
<el-button link type="danger" @click="handleDeleteDetail(scope.$index)"></el-button>
</template>
</el-table-column>
</el-table>
<ItemSkuSelect ref="skuSelectRef" @change="handleSelectSku" />
</el-form>
<template #footer>
<div class="flex items-center justify-between">
<div>
<el-button
v-if="isSavedPrepareOrder"
v-hasPermi="['wms:receipt-order:complete']"
:disabled="formLoading"
type="success"
@click="handleComplete"
>
完成入库
</el-button>
<el-button
v-if="isSavedPrepareOrder"
v-hasPermi="['wms:receipt-order:cancel']"
:disabled="formLoading"
type="danger"
@click="handleCancel"
>
作废
</el-button>
</div>
<div>
<el-button v-if="isPrepareOrder" :disabled="formLoading" type="primary" @click="submitForm">
保存
</el-button>
<el-button @click="dialogVisible = false"> </el-button>
</div>
</div>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { FormRules } from 'element-plus'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ReceiptOrderApi, ReceiptOrderVO } from '@/api/wms/order/receipt'
import { ReceiptOrderDetailVO } from '@/api/wms/order/receipt/detail'
import { ItemSkuVO } from '@/api/wms/md/item/sku'
import ItemSkuSelect from '@/views/wms/md/item/sku/components/ItemSkuSelect.vue'
import MerchantSelect from '@/views/wms/md/merchant/components/MerchantSelect.vue'
import WarehouseAreaSelect from '@/views/wms/md/warehouse/components/WarehouseAreaSelect.vue'
import WarehouseSelect from '@/views/wms/md/warehouse/components/WarehouseSelect.vue'
import { AREA_ENABLE, BATCH_ENABLE } from '@/views/wms/utils/config'
import { OrderStatusEnum } from '@/views/wms/utils/constants'
import {
formatQuantity,
PRICE_PRECISION,
QUANTITY_PRECISION,
sumPrice,
sumQuantity
} from '@/views/wms/utils/format'
import { generateOrderNo } from '@/views/wms/utils/order'
/** WMS 入库单表单 */
defineOptions({ name: 'WmsReceiptOrderForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) //
const formType = ref('') // create - update -
const originalFormData = ref('') //
const formData = ref<ReceiptOrderVO>({
id: undefined,
no: undefined,
type: undefined,
status: OrderStatusEnum.PREPARE,
bizOrderNo: undefined,
merchantId: undefined,
warehouseId: undefined,
areaId: undefined,
totalQuantity: 0,
totalAmount: 0,
remark: undefined,
details: []
})
const formRules = reactive<FormRules>({
no: [{ required: true, message: '入库单号不能为空', trigger: 'blur' }],
type: [{ required: true, message: '入库类型不能为空', trigger: 'change' }],
warehouseId: [{ required: true, message: '仓库不能为空', trigger: 'change' }]
})
const formRef = ref() // Ref
const skuSelectRef = ref() // SKU Ref
const totalQuantity = computed(() => sumQuantity(formData.value.details || [], (detail) => detail.quantity))
const detailTotalAmount = computed(() => sumPrice(formData.value.details || [], (detail) => detail.amount))
const isPrepareOrder = computed(
() => !formData.value.id || formData.value.status === OrderStatusEnum.PREPARE
)
const isSavedPrepareOrder = computed(
() => !!formData.value.id && formData.value.status === OrderStatusEnum.PREPARE
)
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
const order = await ReceiptOrderApi.getReceiptOrder(id)
formData.value = {
...order,
details: order.details || []
}
} finally {
formLoading.value = false
}
}
originalFormData.value = JSON.stringify(buildSubmitData())
}
defineExpose({ open }) // open
/** 构建入库明细 */
const buildDetail = (sku: ItemSkuVO): ReceiptOrderDetailVO => ({
id: undefined,
itemId: sku.itemId,
itemCode: sku.itemCode,
itemName: sku.itemName,
unit: sku.unit,
skuId: sku.id,
skuCode: sku.code,
skuName: sku.name,
areaId: formData.value.areaId,
batchNo: undefined,
productionDate: undefined,
expirationDate: undefined,
quantity: undefined,
amount: undefined,
remark: undefined
})
/** 添加商品 */
const handleAddDetail = () => {
skuSelectRef.value?.open()
}
/** 选择商品 SKU */
const handleSelectSku = (skus: ItemSkuVO[]) => {
if (!skus.length) {
return
}
formData.value.details = formData.value.details || []
skus.forEach((sku) => formData.value.details!.push(buildDetail(sku)))
refreshTotalAmount()
}
/** 删除明细 */
const handleDeleteDetail = (index: number) => {
formData.value.details?.splice(index, 1)
refreshTotalAmount()
}
/** 仓库变化 */
const handleWarehouseChange = () => {
formData.value.areaId = undefined
formData.value.details?.forEach((detail) => (detail.areaId = undefined))
}
/** 库区变化 */
const handleAreaChange = () => {
formData.value.details?.forEach((detail) => (detail.areaId = formData.value.areaId))
}
/** 明细金额变化 */
const handleDetailAmountChange = () => {
refreshTotalAmount()
}
/** 汇总明细金额 */
const refreshTotalAmount = () => {
formData.value.totalAmount = detailTotalAmount.value
}
/** 校验明细 */
const validateDetails = (required: boolean) => {
if (!formData.value.details?.length) {
if (required) {
message.error('至少包含一条入库明细')
return false
}
return true
}
for (let i = 0; i < formData.value.details.length; i++) {
const detail = formData.value.details[i]
if (!detail.skuId) {
message.error(`${i + 1} 行明细请选择商品规格`)
return false
}
if (AREA_ENABLE && !detail.areaId) {
message.error(`${i + 1} 行明细请选择库区`)
return false
}
if (!detail.quantity || detail.quantity <= 0) {
message.error(`${i + 1} 行明细入库数量必须大于 0`)
return false
}
}
return true
}
/** 构建提交数据 */
const buildSubmitData = () =>
({
...formData.value,
totalQuantity: totalQuantity.value,
totalAmount: formData.value.totalAmount,
details: formData.value.details || []
}) as ReceiptOrderVO
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
if (!validateDetails(false)) {
return
}
//
formLoading.value = true
try {
const data = buildSubmitData()
if (formType.value === 'create') {
await ReceiptOrderApi.createReceiptOrder(data)
message.success(t('common.createSuccess'))
} else {
await ReceiptOrderApi.updateReceiptOrder(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 完成入库:表单修改过则先保存,再完成 */
const handleComplete = async () => {
await formRef.value.validate()
if (!validateDetails(true)) {
return
}
try {
await message.confirm('确认完成入库?完成后将更新库存。')
formLoading.value = true
const data = buildSubmitData()
if (JSON.stringify(data) !== originalFormData.value) {
await ReceiptOrderApi.updateReceiptOrder(data)
}
await ReceiptOrderApi.completeReceiptOrder(formData.value.id!)
message.success('入库成功')
dialogVisible.value = false
emit('success')
} catch {
} finally {
formLoading.value = false
}
}
/** 作废入库单 */
const handleCancel = async () => {
try {
await message.confirm('确认作废该入库单?作废后不可恢复。')
formLoading.value = true
await ReceiptOrderApi.cancelReceiptOrder(formData.value.id!)
message.success('作废成功')
dialogVisible.value = false
emit('success')
} catch {
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
no: generateOrderNo('RK'),
type: undefined,
status: OrderStatusEnum.PREPARE,
bizOrderNo: undefined,
merchantId: undefined,
warehouseId: undefined,
areaId: undefined,
totalQuantity: 0,
totalAmount: 0,
remark: undefined,
details: []
}
originalFormData.value = ''
nextTick(() => formRef.value?.clearValidate())
}
</script>

View File

@ -16,7 +16,7 @@
<div v-if="AREA_ENABLE">{{ printData.areaName || '-' }}</div>
<div>总数量{{ formatQuantity(printData.totalQuantity) || '-' }}</div>
<div>总金额{{ formatPrice(printData.totalAmount) || '-' }}</div>
<div>创建时间{{ formatNullableDate(printData.createTime) || '-' }}</div>
<div>创建时间{{ formatNullableDate(printData.createTime) }}</div>
<div class="col-span-3">备注{{ printData.remark || '-' }}</div>
</div>
<table class="w-full border-collapse text-13px">
@ -70,10 +70,10 @@
{{ detail.batchNo || '-' }}
</td>
<td v-if="BATCH_ENABLE" class="border border-solid border-#dcdfe6 p-8px">
{{ formatNullableDate(detail.productionDate, 'YYYY-MM-DD') || '-' }}
{{ formatNullableDate(detail.productionDate, 'YYYY-MM-DD') }}
</td>
<td v-if="BATCH_ENABLE" class="border border-solid border-#dcdfe6 p-8px">
{{ formatNullableDate(detail.expirationDate, 'YYYY-MM-DD') || '-' }}
{{ formatNullableDate(detail.expirationDate, 'YYYY-MM-DD') }}
</td>
<td class="border border-solid border-#dcdfe6 p-8px text-right">
{{ formatQuantity(detail.quantity) || '-' }}
@ -98,7 +98,7 @@
</template>
<script lang="ts" setup>
import { formatDate } from '@/utils/formatTime'
import { formatNullableDate } from '@/utils/formatTime'
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
import { ReceiptOrderApi, ReceiptOrderVO } from '@/api/wms/order/receipt'
import { AREA_ENABLE, BATCH_ENABLE } from '@/views/wms/utils/config'
@ -118,11 +118,6 @@ const printObj = ref({
zIndex: 20003
})
// TODO @AI date time ts
const formatNullableDate = (value?: Date | string, format?: string) => {
return value ? formatDate(value as Date, format) : ''
}
/** 打印入库单 */
const print = async (id: number) => {
printData.value = await ReceiptOrderApi.getReceiptOrder(id)

View File

@ -254,10 +254,10 @@
<el-table-column v-if="BATCH_ENABLE" label="生产日期/过期日期" min-width="180">
<template #default="detailScope">
<div v-if="detailScope.row.productionDate">
生产日期{{ formatDate(detailScope.row.productionDate, 'YYYY-MM-DD') }}
生产日期{{ formatNullableDate(detailScope.row.productionDate, 'YYYY-MM-DD') }}
</div>
<div v-if="detailScope.row.expirationDate">
过期日期{{ formatDate(detailScope.row.expirationDate, 'YYYY-MM-DD') }}
过期日期{{ formatNullableDate(detailScope.row.expirationDate, 'YYYY-MM-DD') }}
</div>
</template>
</el-table-column>
@ -352,8 +352,8 @@
min-width="160"
>
<template #default="scope">
<div>创建{{ formatNullableTime(scope.row.createTime) }}</div>
<div>更新{{ formatNullableTime(scope.row.updateTime) }}</div>
<div>创建{{ formatNullableDate(scope.row.createTime, 'MM-DD HH:mm') }}</div>
<div>更新{{ formatNullableDate(scope.row.updateTime, 'MM-DD HH:mm') }}</div>
</template>
</el-table-column>
<el-table-column v-if="isTableColumnVisible('operator')" label="操作人" min-width="120">
@ -375,7 +375,7 @@
修改
</el-button>
<el-button
v-hasPermi="['wms:receipt-order:print']"
v-hasPermi="['wms:receipt-order:query']"
link
type="primary"
@click="handlePrint(scope.row.id)"
@ -383,7 +383,7 @@
打印
</el-button>
<el-button
v-if="scope.row.status === OrderStatusEnum.PREPARE"
v-if="canDeleteReceiptOrder(scope.row.status)"
v-hasPermi="['wms:receipt-order:delete']"
link
type="danger"
@ -410,7 +410,7 @@
</template>
<script lang="ts" setup>
import { defaultShortcuts, formatDate } from '@/utils/formatTime'
import { defaultShortcuts, formatNullableDate } from '@/utils/formatTime'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ReceiptOrderApi, ReceiptOrderVO } from '@/api/wms/order/receipt'
import { ReceiptOrderDetailVO } from '@/api/wms/order/receipt/detail'
@ -493,10 +493,9 @@ const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) //
const detailMap = reactive<Record<number, ReceiptOrderDetailVO[]>>({}) //
/** 格式化列表时间 */
// TODO @AI date time ts
const formatNullableTime = (value?: Date | string) => {
return value ? formatDate(value as Date, 'MM-DD HH:mm') : '-'
/** 是否允许删除入库单 */
const canDeleteReceiptOrder = (status?: number) => {
return status !== undefined && [OrderStatusEnum.PREPARE, OrderStatusEnum.CANCELED].includes(status)
}
/** 查询入库单列表 */

View File

@ -0,0 +1,12 @@
/**
* WMS
*/
/** 生成业务单号:前缀 + 月日 + 4 位随机数 */
export const generateOrderNo = (prefix: string) => {
const now = new Date()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const randomNo = String(Math.floor(Math.random() * 10000)).padStart(4, '0')
return `${prefix}${month}${day}${randomNo}`
}