Commit 9f92bf42 by qinjianhui

feat: 实现订单库存明细弹框页面

parent a851c9e6
...@@ -5,6 +5,7 @@ import type { ...@@ -5,6 +5,7 @@ import type {
FactoryOrderNewListData, FactoryOrderNewListData,
LogListData, LogListData,
operateOrderListData, operateOrderListData,
OrderInventoryData,
PickCompleteData, PickCompleteData,
ProductListData, ProductListData,
RestockData, RestockData,
...@@ -661,3 +662,21 @@ export function interceptSuccessApi( ...@@ -661,3 +662,21 @@ export function interceptSuccessApi(
data, data,
) )
} }
export function getOrderInventoryListApi(
data: Record<string, unknown>,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<OrderInventoryData>>(
'factory/podOrderOperation/order-inventory-list',
{ ...data, currentPage, pageSize },
)
}
export function exportOrderInventoryApi(data: Record<string, unknown>) {
return axios.post<never, BaseRespData<never>>(
'factory/podOrderOperation/export-order-inventory',
data,
)
}
...@@ -278,3 +278,19 @@ export type CardLayoutListFetcher = ( ...@@ -278,3 +278,19 @@ export type CardLayoutListFetcher = (
currentPage: number, currentPage: number,
pageSize: number, pageSize: number,
) => Promise<PaginationData<operateOrderListData>> ) => Promise<PaginationData<operateOrderListData>>
export interface OrderInventoryData {
id?: number
image?: string
warehouseName?: string
locationCode?: string
warehouseSku?: string
productNo?: string
skuName?: string
shortageQuantity?: number
demandQuantity?: number
usableQuantity?: number
inventory?: number
occupyInventory?: number
freezeInventory?: number
}
<template>
<ElDialog
v-model="visible"
title="订单库存明细"
width="1500px"
:close-on-click-modal="false"
>
<div class="inventory-dialog-content">
<div class="search-bar">
<ElForm
:inline="true"
:model="filterForm"
size="default"
class="search-form"
>
<ElFormItem label="仓库">
<ElSelect
v-model="filterForm.warehouseId"
placeholder="请选择仓库"
clearable
style="width: 150px"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="库存SKU">
<ElInput
v-model="filterForm.warehouseSku"
placeholder="请输入库存SKU"
clearable
style="width: 160px"
/>
</ElFormItem>
<ElFormItem label="款号">
<ElInput
v-model="filterForm.productNo"
placeholder="请输入款号"
clearable
style="width: 150px"
/>
</ElFormItem>
<ElFormItem label="库存状况">
<ElSelect
v-model="filterForm.stockStatus"
placeholder="请选择库存状况"
clearable
style="width: 160px"
>
<ElOption
v-for="item in stockStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="handleSearch">查询</ElButton>
<ElButton type="success" @click="handleExport"> 导出 </ElButton>
</ElFormItem>
</ElForm>
</div>
<div v-loading="loading" class="inventory-table">
<TableView
:paginated-data="data"
:columns="columns"
serial-numberable
selectionable
/>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[20, 50, 100, 200]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0"
@size-change="onPageSizeChange"
@current-change="onCurrentPageChange"
/>
</div>
</ElDialog>
</template>
<script setup lang="tsx">
import { ElMessage } from 'element-plus'
import type { WarehouseListData } from '@/types'
import type {
FactoryOrderNewListData,
OrderInventoryData,
} from '@/types/api/factoryOrderNew'
import TableView from '@/components/TableView.vue'
import usePageList from '@/utils/hooks/usePageList'
import {
getOrderInventoryListApi,
exportOrderInventoryApi,
} from '@/api/factoryOrderNew'
const props = defineProps<{
visible: boolean
selectedRows: FactoryOrderNewListData[]
warehouseList: WarehouseListData[]
}>()
const stockStatusOptions = [
{ label: '缺货商品', value: 'OUT_OF_STOCK' },
{ label: '不缺货商品', value: 'IN_STOCK' },
{ label: '未入过库的商品', value: 'NEVER_STOCKED' },
]
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void
}>()
const visible = computed({
get() {
return props.visible
},
set(value: boolean) {
emit('update:visible', value)
},
})
watch(visible, (newVal) => {
if (newVal) {
filterForm.value = {
warehouseId: undefined,
warehouseSku: '',
productNo: '',
stockStatus: undefined,
}
refresh()
}
})
const filterForm = ref({
warehouseId: undefined as number | undefined,
warehouseSku: '',
productNo: '',
stockStatus: undefined as string | undefined,
})
const buildQueryParams = () => ({
orderIds: props.selectedRows.map((row) => row.id),
warehouseId: filterForm.value.warehouseId || undefined,
warehouseSku: filterForm.value.warehouseSku || undefined,
productNo: filterForm.value.productNo || undefined,
stockStatus: filterForm.value.stockStatus || undefined,
})
const {
loading,
currentPage,
pageSize,
total,
data,
onCurrentPageChange,
onPageSizeChange,
refresh,
} = usePageList<OrderInventoryData>({
initPageSize: 20,
initLoad: false,
query: async (current, size) => {
const res = await getOrderInventoryListApi(
buildQueryParams(),
current,
size,
)
return res.data
},
})
const columns = [
{
label: '图片',
width: 70,
align: 'center',
render: (row: OrderInventoryData) => (
<el-image
src={row.image ?? ''}
style={{ width: '40px', height: '40px' }}
preview-src-list={row.image ? [row.image] : []}
preview-teleported
fit="cover"
/>
),
},
{
prop: 'warehouseName',
label: '仓库名称',
minWidth: 120,
},
{
prop: 'locationCode',
label: '库位',
width: 100,
align: 'center',
},
{
prop: 'warehouseSku',
label: '库存SKU',
minWidth: 180,
align: 'center',
},
{
prop: 'productNo',
label: '款号',
width: 100,
align: 'center',
},
{
prop: 'skuName',
label: '商品名称',
minWidth: 120,
showOverflowTooltip: true,
},
{
label: '缺货数量',
width: 100,
align: 'right',
render: (row: OrderInventoryData) => {
const v = row.shortageQuantity ?? 0
if (v > 0) {
return <span style="color: #f56c6c; font-weight: bold">{v}</span>
}
return <span>{v}</span>
},
},
{
prop: 'demandQuantity',
label: '需求数量',
width: 100,
align: 'right',
},
{
prop: 'usableQuantity',
label: '可用数量',
width: 100,
align: 'right',
},
{
prop: 'inventory',
label: '库存数量',
width: 100,
align: 'right',
},
{
prop: 'occupyInventory',
label: '占用数量',
width: 100,
align: 'right',
},
{
prop: 'freezeInventory',
label: '冻结数量',
width: 100,
align: 'right',
},
]
const handleSearch = () => {
refresh()
}
const handleExport = async () => {
try {
await exportOrderInventoryApi(buildQueryParams())
ElMessage.success('导出成功')
} catch (e) {
console.error(e)
}
}
</script>
<style scoped lang="scss">
.inventory-dialog-content {
display: flex;
flex-direction: column;
height: 68vh;
}
.search-bar {
flex-shrink: 0;
padding-bottom: 10px;
}
.search-form {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0;
:deep(.el-form-item) {
margin-right: 10px;
margin-bottom: 10px;
}
}
.inventory-table {
flex: 1;
overflow: hidden;
}
</style>
...@@ -512,11 +512,6 @@ ...@@ -512,11 +512,6 @@
转至待排单 转至待排单
</ElButton> </ElButton>
</span> </span>
<!-- <span v-if="status === 'PENDING_RECEIVE'" class="item">
<ElButton type="warning" @click="handleTransferOldFlow"
>转旧流程</ElButton
>
</span> -->
<span v-if="status === 'PENDING_SCHEDULE'" class="item"> <span v-if="status === 'PENDING_SCHEDULE'" class="item">
<ElButton type="primary" @click="handleArrange">排单</ElButton> <ElButton type="primary" @click="handleArrange">排单</ElButton>
</span> </span>
...@@ -705,6 +700,17 @@ ...@@ -705,6 +700,17 @@
>刷新商品信息</ElButton >刷新商品信息</ElButton
> >
</span> </span>
<span
v-if="
status === 'PENDING_RECEIVE' &&
pendingAcceptSubTab === 'ACCEPT_FAIL_OUT_OF_STOCK'
"
class="item"
>
<ElButton type="success" @click="handleGetOrderInventoryDetail"
>订单库存明细</ElButton
>
</span>
</div> </div>
<div v-if="status === 'SUSPEND'" class="status-subtabs"> <div v-if="status === 'SUSPEND'" class="status-subtabs">
<div <div
...@@ -1150,6 +1156,11 @@ ...@@ -1150,6 +1156,11 @@
</span> </span>
</template> </template>
</ElDialog> </ElDialog>
<OrderInventoryDetailDialog
v-model:visible="orderInventoryDetailVisible"
:warehouse-list="warehouseList"
:selected-rows="selectedRows"
/>
</div> </div>
</template> </template>
...@@ -1243,7 +1254,8 @@ import { useOrderSearchForm } from './hooks/useOrderSearchForm' ...@@ -1243,7 +1254,8 @@ import { useOrderSearchForm } from './hooks/useOrderSearchForm'
import { useOrderStatusTree } from './hooks/useOrderStatusTree' import { useOrderStatusTree } from './hooks/useOrderStatusTree'
import { useOrderListAndDetail } from './hooks/useOrderListAndDetail' import { useOrderListAndDetail } from './hooks/useOrderListAndDetail'
import { useOrderBatchActions } from './hooks/useOrderBatchActions' import { useOrderBatchActions } from './hooks/useOrderBatchActions'
import { ElTag } from 'element-plus' import { ElButton, ElTag } from 'element-plus'
import OrderInventoryDetailDialog from './component/OrderInventoryDialog.vue'
const resultInfo = ref<ResultInfoDataItem[]>([]) const resultInfo = ref<ResultInfoDataItem[]>([])
const resultRefs = ref() const resultRefs = ref()
...@@ -1252,7 +1264,7 @@ const statusSidebarCollapsed = ref(false) ...@@ -1252,7 +1264,7 @@ const statusSidebarCollapsed = ref(false)
const toggleStatusSidebar = () => { const toggleStatusSidebar = () => {
statusSidebarCollapsed.value = !statusSidebarCollapsed.value statusSidebarCollapsed.value = !statusSidebarCollapsed.value
} }
const orderInventoryDetailVisible = ref(false)
const batchManageRef = ref<InstanceType<typeof BatchManageTable>>() const batchManageRef = ref<InstanceType<typeof BatchManageTable>>()
const waitingRestockRef = ref<InstanceType<typeof WaitingRestockTable>>() const waitingRestockRef = ref<InstanceType<typeof WaitingRestockTable>>()
const cardLayoutRef = ref<InstanceType<typeof CardLayout>>() const cardLayoutRef = ref<InstanceType<typeof CardLayout>>()
...@@ -3116,6 +3128,9 @@ const handleInterceptionFail = async (row?: FactoryOrderNewListData) => { ...@@ -3116,6 +3128,9 @@ const handleInterceptionFail = async (row?: FactoryOrderNewListData) => {
}, },
}) })
} }
const handleGetOrderInventoryDetail = async () => {
orderInventoryDetailVisible.value = true
}
onMounted(() => { onMounted(() => {
loadStatusTreeCounts() loadStatusTreeCounts()
loadAllDictionaries() loadAllDictionaries()
......
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