admin-vue3/src/views/wms/order/shipment/index.vue

631 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- WMS 出库单 -->
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="80px"
>
<el-form-item label="出库单号" prop="no">
<el-input
v-model="queryParams.no"
class="!w-240px"
clearable
placeholder="请输入出库单号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="单据状态" prop="status">
<el-select
v-model="queryParams.status"
class="!w-240px"
clearable
placeholder="请选择单据状态"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.WMS_ORDER_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<WarehouseSelect
v-model="queryParams.warehouseId"
class="!w-240px"
@change="handleWarehouseChange"
/>
</el-form-item>
<el-form-item label="客户" prop="merchantId">
<MerchantSelect
v-model="queryParams.merchantId"
class="!w-240px"
placeholder="请选择客户"
customer
/>
</el-form-item>
<el-form-item label="单据日期" prop="orderTime">
<el-date-picker
v-model="queryParams.orderTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="数量" prop="totalQuantityMin">
<div class="flex w-240px items-center gap-8px">
<el-input-number
v-model="queryParams.totalQuantityMin"
:controls="false"
:min="0"
:precision="QUANTITY_PRECISION"
class="!w-105px"
placeholder="最小值"
/>
<span>至</span>
<el-input-number
v-model="queryParams.totalQuantityMax"
:controls="false"
:min="0"
:precision="QUANTITY_PRECISION"
class="!w-105px"
placeholder="最大值"
/>
</div>
</el-form-item>
<el-form-item label="总金额" prop="totalPriceMin">
<div class="flex w-240px items-center gap-8px">
<el-input-number
v-model="queryParams.totalPriceMin"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-105px"
placeholder="最小值"
/>
<span>至</span>
<el-input-number
v-model="queryParams.totalPriceMax"
:controls="false"
:min="0"
:precision="PRICE_PRECISION"
class="!w-105px"
placeholder="最大值"
/>
</div>
</el-form-item>
<el-form-item label="出库类型" prop="type">
<el-select
v-model="queryParams.type"
class="!w-240px"
clearable
placeholder="请选择出库类型"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.WMS_SHIPMENT_ORDER_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建用户" prop="creator">
<UserSelectV2
v-model="queryParams.creator"
class="!w-240px"
placeholder="请选择创建用户"
/>
</el-form-item>
<el-form-item label="更新用户" prop="updater">
<UserSelectV2
v-model="queryParams.updater"
class="!w-240px"
placeholder="请选择更新用户"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:shortcuts="defaultShortcuts"
class="!w-240px"
end-placeholder="结束时间"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-date-picker
v-model="queryParams.updateTime"
:shortcuts="defaultShortcuts"
class="!w-240px"
end-placeholder="结束时间"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</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-popover
popper-class="wms-shipment-order-table-setting-popover"
trigger="click"
width="520"
>
<template #reference>
<el-button>
<Icon class="mr-5px" icon="ep:setting" />
表格设置
</el-button>
</template>
<el-checkbox-group
v-model="checkedTableColumns"
class="wms-shipment-order-table-setting grid grid-cols-3 gap-y-14px rounded p-16px"
>
<el-checkbox
v-for="column in tableColumnOptions"
:key="column.value"
:label="column.value"
>
{{ column.label }}
</el-checkbox>
</el-checkbox-group>
</el-popover>
<el-button
v-hasPermi="['wms:shipment-order:create']"
plain
type="primary"
@click="openForm('create')"
>
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button
v-hasPermi="['wms:shipment-order:export']"
:loading="exportLoading"
plain
type="success"
@click="handleExport"
>
<Icon class="mr-5px" icon="ep:download" />
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 出库单列表 -->
<ContentWrap>
<el-table
v-loading="loading"
:cell-class-name="'wms-shipment-order-cell'"
:data="list"
:show-overflow-tooltip="true"
border
@expand-change="handleExpandChange"
>
<el-table-column type="expand" width="48">
<template #default="scope">
<el-table :data="detailMap[scope.row.id] || []" border>
<el-table-column label="商品信息" min-width="220">
<template #default="detailScope">
<div>{{ detailScope.row.itemName || '-' }}</div>
<div v-if="detailScope.row.itemCode" class="text-12px text-gray-500">
商品编号:{{ detailScope.row.itemCode }}
</div>
</template>
</el-table-column>
<el-table-column label="规格信息" min-width="220">
<template #default="detailScope">
<div>{{ detailScope.row.skuName || '-' }}</div>
<div v-if="detailScope.row.skuCode" class="text-12px text-gray-500">
规格编号:{{ detailScope.row.skuCode }}
</div>
</template>
</el-table-column>
<el-table-column align="right" label="出库数量" width="120">
<template #default="detailScope">
{{ formatQuantity(detailScope.row.quantity) }}
</template>
</el-table-column>
<el-table-column align="right" label="单价(元)" width="120">
<template #default="detailScope">
{{ formatPrice(detailScope.row.price) || '-' }}
</template>
</el-table-column>
<el-table-column align="right" label="金额(元)" width="120">
<template #default="detailScope">
{{ formatPrice(getDetailTotalPrice(detailScope.row)) || '-' }}
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
<el-table-column v-if="isTableColumnVisible('no')" fixed="left" label="单号/业务单号" width="290">
<template #default="scope">
<div>
单号:
<el-button link type="primary" @click="openDetail(scope.row.id)">
{{ scope.row.no }}
</el-button>
</div>
<div v-if="scope.row.bizOrderNo" class="text-12px text-gray-500">
业务:{{ scope.row.bizOrderNo }}
</div>
</template>
</el-table-column>
<el-table-column
v-if="isTableColumnVisible('status')"
align="center"
fixed="left"
label="出库状态"
width="110"
>
<template #default="scope">
<dict-tag :type="DICT_TYPE.WMS_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
v-if="isTableColumnVisible('type')"
align="center"
label="出库类型"
width="120"
>
<template #default="scope">
<dict-tag :type="DICT_TYPE.WMS_SHIPMENT_ORDER_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column
v-if="isTableColumnVisible('warehouse')"
label="仓库"
min-width="160"
>
<template #default="scope">
{{ scope.row.warehouseName || '-' }}
</template>
</el-table-column>
<el-table-column
v-if="isTableColumnVisible('quantityAmount')"
label="总数量/总金额(元)"
min-width="180"
>
<template #default="scope">
<div class="flex items-center justify-between">
<span>数量:</span>
<span>{{ formatQuantity(scope.row.totalQuantity) }}</span>
</div>
<div class="flex items-center justify-between">
<span>金额:</span>
<span>{{ formatPrice(scope.row.totalPrice) }}</span>
</div>
</template>
</el-table-column>
<el-table-column
v-if="isTableColumnVisible('merchant')"
label="客户"
min-width="160"
prop="merchantName"
/>
<el-table-column v-if="isTableColumnVisible('operateInfo')" label="操作信息" min-width="280">
<template #default="scope">
<div>
创建:{{ formatNullableDate(scope.row.createTime) }} /
{{ scope.row.creatorName || scope.row.creator || '-' }}
</div>
<div>
更新:{{ formatNullableDate(scope.row.updateTime) }} /
{{ scope.row.updaterName || scope.row.updater || '-' }}
</div>
</template>
</el-table-column>
<el-table-column v-if="isTableColumnVisible('remark')" label="备注" min-width="160" prop="remark" />
<el-table-column align="center" fixed="right" label="操作" width="180">
<template #default="scope">
<el-tooltip
:content="getShipmentOrderUpdateTip(scope.row.status)"
:disabled="canUpdateShipmentOrder(scope.row.status)"
placement="top"
>
<span>
<el-button
v-hasPermi="['wms:shipment-order:update']"
:disabled="!canUpdateShipmentOrder(scope.row.status)"
link
type="primary"
@click="openForm('update', scope.row.id)"
>
修改
</el-button>
</span>
</el-tooltip>
<el-tooltip
:content="getShipmentOrderDeleteTip(scope.row.status)"
:disabled="canDeleteShipmentOrder(scope.row.status)"
placement="top"
>
<span>
<el-button
v-hasPermi="['wms:shipment-order:delete']"
:disabled="!canDeleteShipmentOrder(scope.row.status)"
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
</span>
</el-tooltip>
<el-button
v-hasPermi="['wms:shipment-order:query']"
link
type="primary"
@click="handlePrint(scope.row.id)"
>
打印
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<ShipmentOrderForm ref="formRef" @success="getList" />
<ShipmentOrderDetail ref="detailRef" />
<ShipmentOrderPrint ref="printRef" />
</template>
<script lang="ts" setup>
import { defaultShortcuts, formatNullableDate } from '@/utils/formatTime'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ShipmentOrderApi, ShipmentOrderVO } from '@/api/wms/order/shipment'
import { ShipmentOrderDetailVO } from '@/api/wms/order/shipment/detail'
import MerchantSelect from '@/views/wms/md/merchant/components/MerchantSelect.vue'
import WarehouseSelect from '@/views/wms/md/warehouse/components/WarehouseSelect.vue'
import {
OrderDeleteStatusList,
OrderStatusEnum,
OrderUpdateStatusList
} from '@/views/wms/utils/constants'
import { formatPrice, formatQuantity, PRICE_PRECISION, QUANTITY_PRECISION } from '@/views/wms/utils/format'
import UserSelectV2 from '@/views/system/user/components/UserSelectV2.vue'
import ShipmentOrderDetail from './ShipmentOrderDetail.vue'
import ShipmentOrderForm from './ShipmentOrderForm.vue'
import ShipmentOrderPrint from './ShipmentOrderPrint.vue'
import download from '@/utils/download'
/** WMS 出库单 */
defineOptions({ name: 'WmsShipmentOrder' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
type TableColumnKey =
| 'no'
| 'status'
| 'type'
| 'warehouse'
| 'quantityAmount'
| 'merchant'
| 'operateInfo'
| 'remark'
const tableColumnOptions: Array<{ label: string; value: TableColumnKey }> = [
{ label: '单号/业务单号', value: 'no' },
{ label: '出库状态', value: 'status' },
{ label: '出库类型', value: 'type' },
{ label: '仓库', value: 'warehouse' },
{ label: '数量/金额(元)', value: 'quantityAmount' },
{ label: '客户', value: 'merchant' },
{ label: '操作信息', value: 'operateInfo' },
{ label: '备注', value: 'remark' }
]
const checkedTableColumns = ref<TableColumnKey[]>([
'no',
'status',
'type',
'warehouse',
'quantityAmount',
'merchant',
'operateInfo',
'remark'
])
const isTableColumnVisible = (column: TableColumnKey) => checkedTableColumns.value.includes(column)
const loading = ref(true) // 列表的加载中
const list = ref<ShipmentOrderVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const getDefaultQueryParams = () => ({
pageNo: 1,
pageSize: 10,
no: undefined as string | undefined,
status: undefined as number | undefined,
warehouseId: undefined as number | undefined,
merchantId: undefined as number | undefined,
orderTime: undefined as string[] | undefined,
totalQuantityMin: undefined as number | undefined,
totalQuantityMax: undefined as number | undefined,
totalPriceMin: undefined as number | undefined,
totalPriceMax: undefined as number | undefined,
type: undefined as number | undefined,
bizOrderNo: undefined as string | undefined,
creator: undefined as number | undefined,
updater: undefined as number | undefined,
createTime: undefined as string[] | undefined,
updateTime: undefined as string[] | undefined
})
const queryParams = reactive(getDefaultQueryParams())
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
const detailMap = reactive<Record<number, ShipmentOrderDetailVO[]>>({}) // 出库单明细缓存
/** 是否允许修改出库单 */
const canUpdateShipmentOrder = (status?: number) => {
return status !== undefined && OrderUpdateStatusList.includes(status)
}
/** 是否允许删除出库单 */
const canDeleteShipmentOrder = (status?: number) => {
return status !== undefined && OrderDeleteStatusList.includes(status)
}
/** 获得出库单修改禁用提示 */
const getShipmentOrderUpdateTip = (status?: number) => {
if (status === OrderStatusEnum.FINISHED) {
return '已出库,无法修改'
}
if (status === OrderStatusEnum.CANCELED) {
return '已作废,无法修改'
}
return '当前状态无法修改'
}
/** 获得出库单删除禁用提示 */
const getShipmentOrderDeleteTip = (status?: number) => {
if (status === OrderStatusEnum.FINISHED) {
return '已出库,无法删除'
}
return '当前状态无法删除'
}
/** 查询出库单列表 */
const getList = async () => {
loading.value = true
try {
const data = await ShipmentOrderApi.getShipmentOrderPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
Object.assign(queryParams, getDefaultQueryParams())
handleQuery()
}
/** 仓库变化 */
const handleWarehouseChange = () => {
handleQuery()
}
/** 计算明细金额 */
const getDetailTotalPrice = (detail: ShipmentOrderDetailVO) => {
if (!detail.quantity || detail.price === undefined || detail.price === null) {
return undefined
}
return Number(detail.quantity) * Number(detail.price)
}
/** 展开明细 */
const handleExpandChange = async (row: ShipmentOrderVO) => {
if (!row.id || detailMap[row.id]) {
return
}
detailMap[row.id] = await ShipmentOrderApi.getShipmentOrderDetailListByOrderId(row.id)
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 查看出库单详情 */
const detailRef = ref()
const openDetail = (id: number) => {
detailRef.value.open(id)
}
/** 打印出库单 */
const printRef = ref()
const handlePrint = (id: number) => {
printRef.value.print(id)
}
/** 删除出库单 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ShipmentOrderApi.deleteShipmentOrder(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ShipmentOrderApi.exportShipmentOrder(queryParams)
download.excel(data, '出库单.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 */
onMounted(async () => {
await getList()
})
</script>
<style scoped>
:deep(.wms-shipment-order-cell) {
vertical-align: top;
}
:global(.wms-shipment-order-table-setting-popover) {
padding: 12px;
}
:global(.wms-shipment-order-table-setting) {
background-color: var(--el-fill-color-light);
}
:global(.wms-shipment-order-table-setting .el-checkbox) {
height: 28px;
margin-right: 0;
}
:global(.wms-shipment-order-table-setting .el-checkbox__label) {
font-size: 16px;
font-weight: 600;
}
</style>