Commit dda34c8e by qinjianhui

feat: 入库申请单功能开发

parent 590631b9
import axios from './axios' import axios from '../axios'
import { BasePaginationData, BaseRespData } from '@/types/api' import { BasePaginationData, BaseRespData } from '@/types/api'
import { import {
SearchForm, SearchForm,
......
import axios from '@/api/axios'
import { BasePaginationData, BaseRespData } from '@/types/api'
import {
LogListData,
SubmitWarehousingData,
} from '@/types/api/supply/stockingOrder'
import {
SearchForm,
TableData,
TreeData,
StockingApplyOrderDetailList,
RelatedDocumentList,
StockingApplyOrderDetailData,
} from '@/types/api/warehouse/stockingApplyOrder'
export function getStockingApplyOrderListApi(
data: SearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<TableData>>(
'factory/supply/stockingUpWarehouseApply/list_page',
{ ...data, currentPage, pageSize },
)
}
export function getStockingApplyOrderTreeApi() {
return axios.get<never, BaseRespData<TreeData[]>>(
'factory/supply/stockingUpWarehouseApply/getStatusTree',
)
}
export function getStockingApplyOrderDetailListByIdApi(id: number) {
return axios.get<never, BaseRespData<StockingApplyOrderDetailList[]>>(
`factory/supply/stockingUpWarehouseApply/getWarehouseApplyList?id=${id}`,
)
}
export function getStockingApplyOrderRelatedDocumentListByIdApi(
warehouseApplyNo: string,
) {
return axios.get<never, BaseRespData<RelatedDocumentList[]>>(
`factory/supply/stockingUpWarehouseApply/getWarehouseApplyList?warehouseApplyNo=${warehouseApplyNo}`,
)
}
export function getStockingApplyOrderLogListByIdApi(id: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
`factory/supply/stockingUpWarehouseApplyLog/getWarehouseApplyLog?warehouseApplyId=${id}`,
)
}
export function warehouseReceiptApi(id: number) {
return axios.get<never, BaseRespData<void>>(
`factory/supply/stockingUpWarehouseApply/receiving?id=${id}`,
)
}
export function submitWarehousingApi(data: SubmitWarehousingData) {
return axios.post<never, BaseRespData<void>>(
'factory/supply/stockingUpWarehouseApply/submitWarehousing',
data,
)
}
export function getStockingApplyOrderDetailById(id: number) {
return axios.get<never, BaseRespData<StockingApplyOrderDetailData>>(
`factory/supply/stockingUpWarehouseApply/get?id=${id}`,
)
}
...@@ -167,7 +167,7 @@ const router = createRouter({ ...@@ -167,7 +167,7 @@ const router = createRouter({
meta: { meta: {
title: '缺货统计', title: '缺货统计',
}, },
component: () => import('@/views/supply/OutOfStockStatistics.vue') component: () => import('@/views/supply/OutOfStockStatistics.vue'),
}, },
{ {
path: '/supply/stocking-order', path: '/supply/stocking-order',
...@@ -303,6 +303,14 @@ const router = createRouter({ ...@@ -303,6 +303,14 @@ const router = createRouter({
component: stockingPlan, component: stockingPlan,
}, },
{ {
path: '/warehouse/stocking-application',
meta: {
title: '入库申请单',
},
component: () =>
import('@/views/warehouse/stockingApplicationOrder/index.vue'),
},
{
path: '/warehouse/warning', path: '/warehouse/warning',
meta: { meta: {
title: '仓库预警', title: '仓库预警',
......
...@@ -69,6 +69,11 @@ const menu: MenuItem[] = [ ...@@ -69,6 +69,11 @@ const menu: MenuItem[] = [
label: '库存', label: '库存',
children: [ children: [
{ {
index: '/warehouse/stocking-application',
id: 126,
label: '入库申请单',
},
{
index: '/warehouse/warning', index: '/warehouse/warning',
id: 125, id: 125,
label: '仓库预警', label: '仓库预警',
...@@ -155,7 +160,7 @@ const menu: MenuItem[] = [ ...@@ -155,7 +160,7 @@ const menu: MenuItem[] = [
}, },
{ {
label: '备货订单', label: '备货订单',
index:'/supply/stocking-order', index: '/supply/stocking-order',
id: 2, id: 2,
}, },
{ {
......
...@@ -118,3 +118,18 @@ export interface LogListData { ...@@ -118,3 +118,18 @@ export interface LogListData {
description?: string description?: string
updateTime?: string updateTime?: string
} }
// 提交入库
export interface SubmitWarehousingItem {
id: number
buyStored: number
rejectsAmount: number
locationId?: number
}
export interface SubmitWarehousingData {
warehouseApplyId?: number
inspectorId?: number
remark?: string
itemList: SubmitWarehousingItem[]
}
export interface SearchForm {
warehouseApplyNo?: string
manageNo?: string
warehouseId?: string | number[]
warehouseSku?: string | number[]
warehouseSkuName?: string
currentPage: number
pageSize: number
storeStatus?: string
}
export interface TreeData {
id?: number
code?: string
name?: string
countQuantity?: number
children?: TreeData[]
leaf?: boolean
}
export interface TableData {
id: number
factoryId?: number
factoryCode?: string
manageId?: number
manageNo?: string
warehouseApplyNo: string
warehouseId?: number
warehouseName?: string
skuTotal?: number
total?: number
buyStored?: number
rejectsAmount?: number
storeStatus: string
expectDeliveryTime?: string
createUserId?: number
createUserName?: string
createTime?: string
updateTime?: string
dataVersion?: number
}
export interface StockingApplyOrderDetailList {
id: number
manageId?: number
warehouseApplyId?: number
warehouseApplyNo?: string
warehouseSku?: string
productNo?: string
warehouseSkuName?: string
warehouseSkuImage?: string
shipmentQuantity?: number
buyStored?: number
rejectsAmount?: number
price?: number
createTime?: string
updateTime?: string
}
export interface RelatedDocumentList {}
export interface StockingApplyOrderDetailData {
id: number
warehouseApplyNo?: string
warehouseName?: string
warehouseId?: number
inspectorId?: number
remark?: string
detailsList: StockingApplyOrderDetailList[]
}
...@@ -274,7 +274,7 @@ import { ...@@ -274,7 +274,7 @@ import {
getProductBySkuApi, getProductBySkuApi,
getStockingOrderDetailByIdApi, getStockingOrderDetailByIdApi,
rejectedStockingOrderApi, rejectedStockingOrderApi,
} from '@/api/stockingOrder' } from '@/api/supplier/stockingOrder'
import ImageView from '@/components/ImageView.vue' import ImageView from '@/components/ImageView.vue'
import { BigNumber } from 'bignumber.js' import { BigNumber } from 'bignumber.js'
......
...@@ -52,7 +52,7 @@ import { ...@@ -52,7 +52,7 @@ import {
getStockingOrderInternalMemoListByIdApi, getStockingOrderInternalMemoListByIdApi,
getStockingOrderLogListByIdApi, getStockingOrderLogListByIdApi,
getStockingOrderRelatedDocumentListByIdApi, getStockingOrderRelatedDocumentListByIdApi,
} from '@/api/stockingOrder' } from '@/api/supplier/stockingOrder'
import LogList from '@/components/LogList.vue' import LogList from '@/components/LogList.vue'
const relatedDocumentsColumns = computed(() => { const relatedDocumentsColumns = computed(() => {
return [ return [
...@@ -67,7 +67,7 @@ const relatedDocumentsColumns = computed(() => { ...@@ -67,7 +67,7 @@ const relatedDocumentsColumns = computed(() => {
{ {
label: '制单人', label: '制单人',
width: 120, width: 120,
prop:"createUserName", prop: 'createUserName',
}, },
{ {
label: '制单时间', label: '制单时间',
...@@ -86,6 +86,7 @@ const relatedDocumentsColumns = computed(() => { ...@@ -86,6 +86,7 @@ const relatedDocumentsColumns = computed(() => {
{ {
label: '申请数量', label: '申请数量',
width: 120, width: 120,
prop: 'shipmentQuantity',
align: 'right', align: 'right',
}, },
{ {
......
...@@ -124,7 +124,7 @@ import type { WarehouseListData } from '@/types' ...@@ -124,7 +124,7 @@ import type { WarehouseListData } from '@/types'
import { import {
getStockingOrderDetailByIdApi, getStockingOrderDetailByIdApi,
supplierDispatchApi, supplierDispatchApi,
} from '@/api/stockingOrder' } from '@/api/supplier/stockingOrder'
import ImageView from '@/components/ImageView.vue' import ImageView from '@/components/ImageView.vue'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import { BigNumber } from 'bignumber.js' import { BigNumber } from 'bignumber.js'
......
...@@ -230,7 +230,7 @@ import { ...@@ -230,7 +230,7 @@ import {
getStockingOrderListApi, getStockingOrderListApi,
stockingCompleteApi, stockingCompleteApi,
submitStockingOrderAuditApi, submitStockingOrderAuditApi,
} from '@/api/stockingOrder' } from '@/api/supplier/stockingOrder'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import StockingOrderDetailTabs from './StockingOrderDetailTabs.vue' import StockingOrderDetailTabs from './StockingOrderDetailTabs.vue'
import AddStockingOrderDialog from './AddStockingOrderDialog.vue' import AddStockingOrderDialog from './AddStockingOrderDialog.vue'
...@@ -249,7 +249,7 @@ import { ...@@ -249,7 +249,7 @@ import {
getSupplierListApi, getSupplierListApi,
loadWarehouseListApi, loadWarehouseListApi,
} from '@/api/common' } from '@/api/common'
import { getStockingOrderStatusTreeApi } from '@/api/stockingOrder' import { getStockingOrderStatusTreeApi } from '@/api/supplier/stockingOrder'
import { userData } from '@/types/api/user' import { userData } from '@/types/api/user'
import { ElButton, ElTag } from 'element-plus' import { ElButton, ElTag } from 'element-plus'
const tableColumns = computed(() => { const tableColumns = computed(() => {
...@@ -441,7 +441,7 @@ const tableColumns = computed(() => { ...@@ -441,7 +441,7 @@ const tableColumns = computed(() => {
}) })
const treeData = ref<TreeData[]>([]) const treeData = ref<TreeData[]>([])
const status = ref<string>('-1') const status = ref<string>('PENDING_SUBMIT')
const selectedRow = ref<TableData | null>(null) const selectedRow = ref<TableData | null>(null)
const warehouseList = ref<WarehouseListData[]>([]) const warehouseList = ref<WarehouseListData[]>([])
......
<template>
<ElTabs
v-model="activeTab"
v-loading="loading"
class="detail-tabs"
@tab-click="handleTabClick"
>
<ElTabPane name="stockProducts" label="入库商品">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="stockProductsColumns"
:paginated-data="stockProductsData"
/>
</div>
</ElTabPane>
<ElTabPane name="relatedDocuments" label="关联单据">
<div class="detail-table-content">
<TableView
:serial-numberable="true"
:columns="relatedDocumentsColumns"
:paginated-data="relatedDocumentsData"
/>
</div>
</ElTabPane>
<ElTabPane name="operationLog" label="操作日志">
<div class="detail-table-content">
<LogList :log-list="operationLogData" />
<div class="empty-content">暂无数据</div>
</div>
</ElTabPane>
</ElTabs>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { TabsPaneContext } from 'element-plus'
import TableView from '@/components/TableView.vue'
import {
RelatedDocumentList,
StockingApplyOrderDetailList,
TableData,
} from '@/types/api/warehouse/stockingApplyOrder'
import ImageView from '@/components/ImageView.vue'
import {
getStockingApplyOrderDetailListByIdApi,
getStockingApplyOrderLogListByIdApi,
getStockingApplyOrderRelatedDocumentListByIdApi,
} from '@/api/warehouse/stockingApplyOrder'
import LogList from '@/components/LogList.vue'
import { LogListData } from '@/types/api/supply/stockingOrder'
const relatedDocumentsColumns = computed(() => {
return [
{
label: '关联单据',
width: 120,
prop: 'documentNo',
},
{
label: '关联单号',
},
{
label: '制单人',
width: 120,
prop: 'createUserName',
},
{
label: '制单时间',
width: 160,
},
{
label: 'SKU个数',
width: 120,
align: 'right',
},
{
label: '入库数量',
width: 120,
align: 'right',
},
]
})
const stockProductsColumns = computed(() => {
return [
{
label: 'SKU图片',
prop: 'skuImage',
width: 100,
align: 'center',
render: (item: StockingApplyOrderDetailList) => (
<ImageView src={item.warehouseSkuImage} width="50px" height="50px" />
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
minWidth: 200,
align: 'left',
},
{
label: '款号',
prop: 'productNo',
width: 120,
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '申请数量',
prop: 'shipmentQuantity',
width: 100,
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 120,
align: 'right',
},
{
label: '不良品数',
prop: 'rejectsAmount',
width: 120,
align: 'right',
},
{
label: '实际质检数',
prop: '',
width: 120,
align: 'right',
},
{
label: '入库待审核数',
prop: '',
width: 120,
align: 'right',
},
{
label: '币种',
prop: 'currencyCode',
width: 100,
align: 'right',
},
{
label: '入库单价',
prop: 'price',
width: 100,
align: 'right',
},
]
})
const props = defineProps<{
selectedRow: TableData | null
}>()
const stockProductsData = ref<StockingApplyOrderDetailList[]>([])
const relatedDocumentsData = ref<RelatedDocumentList[]>([])
const activeTab = ref('stockProducts')
const loading = ref(false)
const operationLogData = ref<LogListData[]>([])
watch(
() => props.selectedRow,
(newVal) => {
if (newVal) {
if (activeTab.value === 'stockProducts') {
loadStockProducts(newVal)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(newVal)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(newVal)
}
} else {
stockProductsData.value = []
relatedDocumentsData.value = []
}
},
)
const loadStockProducts = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingApplyOrderDetailListByIdApi(row.id)
if (res.code !== 200) return
stockProductsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadRelatedDocuments = async (row: TableData) => {
loading.value = true
try {
const res = await getStockingApplyOrderRelatedDocumentListByIdApi(
row.warehouseApplyNo,
)
if (res.code !== 200) return
relatedDocumentsData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const loadOperationLog = async (row: TableData) => {
try {
const res = await getStockingApplyOrderLogListByIdApi(row.id)
if (res.code !== 200) return
operationLogData.value = res.data || []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const handleTabClick = (tab: TabsPaneContext) => {
activeTab.value = tab.paneName as string
if (activeTab.value === 'stockProducts') {
loadStockProducts(props.selectedRow as TableData)
} else if (activeTab.value === 'relatedDocuments') {
loadRelatedDocuments(props.selectedRow as TableData)
} else if (activeTab.value === 'operationLog') {
loadOperationLog(props.selectedRow as TableData)
}
}
</script>
<style lang="scss" scoped>
.detail-tabs {
height: 100%;
display: flex;
:deep(.el-tab-pane) {
height: 100%;
overflow: hidden;
}
}
.detail-table-content {
height: 100%;
overflow: hidden;
}
.empty-content {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #909399;
font-size: 14px;
}
</style>
<template>
<ElDialog
v-model="visible"
title="提交入库"
width="1200px"
top="10vh"
:close-on-click-modal="false"
destroy-on-close
>
<div class="submit-warehousing">
<ElForm
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
inline
>
<div class="form-row">
<ElFormItem label="入库申请单" class="form-item">
<ElInput
v-model="formData.warehouseApplyNo"
disabled
placeholder="自动带出"
/>
</ElFormItem>
<ElFormItem label="入库仓库" class="form-item">
<ElInput
v-model="formData.warehouseName"
disabled
placeholder="自动带出"
/>
</ElFormItem>
<ElFormItem label="质检员" prop="inspectorId" class="form-item">
<ElSelect
v-model="formData.inspectorId"
placeholder="请选择"
filterable
clearable
>
<ElOption
v-for="item in userList"
:key="item.id"
:label="item.account"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="备注" class="form-item remark-item">
<ElInput v-model="formData.remark" placeholder="请输入" clearable />
</ElFormItem>
</div>
</ElForm>
<div class="table-section">
<TableView
:selection-able="true"
:serial-numberable="true"
:data="formData.detailsList"
:columns="tableColumns"
>
<template #buyStored="{ row }">
<ElInput
v-model="row.buyStored"
clearable
size="small"
style="width: 70px"
/>
</template>
<template #rejectsAmount="{ row }">
<ElInput
v-model="row.rejectsAmount"
clearable
size="small"
style="width: 70px"
/>
</template>
<template #locationName="{ row }">
<ElSelect
v-model="row.locationId"
placeholder="请选择"
clearable
style="width: 100px"
>
<ElOption
v-for="item in locationList"
:key="item.id"
:label="item.locationCode"
:value="item.id"
/>
</ElSelect>
</template>
</TableView>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="handleCancel">取消</ElButton>
<ElButton type="primary" :loading="submitLoading" @click="handleSubmit">
入库
</ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import ImageView from '@/components/ImageView.vue'
import { getUserListApi } from '@/api/common'
import { LocationInfoGetAll, loactionData } from '@/api/warehouse'
import {
getStockingApplyOrderDetailById,
submitWarehousingApi,
} from '@/api/warehouse/stockingApplyOrder'
import {
TableData,
StockingApplyOrderDetailData,
StockingApplyOrderDetailList,
} from '@/types/api/warehouse/stockingApplyOrder'
import { userData } from '@/types/api/user'
const emit = defineEmits<{
(e: 'refresh'): void
}>()
const visible = ref(false)
const submitLoading = ref(false)
const formRef = ref<FormInstance>()
const currentRow = ref<TableData | null>(null)
const userList = ref<userData[]>([])
const locationList = ref<loactionData[]>([])
const formData = ref<StockingApplyOrderDetailData>({
detailsList: [],
id: 0,
warehouseApplyNo: '',
warehouseName: '',
warehouseId: undefined,
inspectorId: undefined,
remark: '',
})
const tableColumns = ref([
{
label: 'SKU图片',
prop: 'warehouseSkuImage',
width: 90,
align: 'center',
render: (row: StockingApplyOrderDetailList) => (
<ImageView src={row.warehouseSkuImage} width="50px" height="50px" />
),
},
{
label: '商品名称',
prop: 'warehouseSkuName',
align: 'left',
},
{
label: '款号',
width: 140,
align: 'center',
},
{
label: '库存SKU',
prop: 'warehouseSku',
width: 180,
align: 'center',
},
{
label: '申请数量',
width: 120,
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 100,
align: 'right',
slot: 'buyStored',
},
{
label: '不良品数',
prop: 'rejectsAmount',
width: 100,
align: 'right',
slot: 'rejectsAmount',
},
{
label: '实际质检数',
prop: '',
width: 100,
align: 'right',
},
{
label: '库位',
prop: 'locationName',
width: 140,
align: 'right',
slot: 'locationName',
},
])
const formRules: FormRules = {
inspectorId: [{ required: true, message: '请选择质检员', trigger: 'change' }],
}
const loadUserList = async () => {
try {
const res = await getUserListApi()
if (res.code === 200) {
userList.value = res.data || []
}
} catch (e) {
console.error(e)
}
}
const loadLocationList = async (warehouseId: number | undefined) => {
if (!warehouseId) return
try {
const res = await LocationInfoGetAll(warehouseId)
if (res.code === 200) {
locationList.value = res.data || []
}
} catch (e) {
console.error(e)
}
}
const loadDetailList = async (id: number) => {
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await getStockingApplyOrderDetailById(id)
if (res.code !== 200) return
formData.value = res.data
visible.value = true
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const open = async (row: TableData) => {
loadUserList()
await loadLocationList(row.warehouseId)
loadDetailList(row.id)
}
const handleCancel = () => {
visible.value = false
}
// 提交入库
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch {
return
}
const invalidItem = formData.value.detailsList.find(
(item) => (item.buyStored || 0) > (item.shipmentQuantity || 0),
)
if (invalidItem) {
ElMessage.error('入库数量不能大于申请数量')
return
}
submitLoading.value = true
try {
const submitData = {
warehouseApplyId: currentRow.value?.id,
inspectorId: formData.value.inspectorId,
remark: formData.value.remark,
itemList: formData.value.detailsList.map((item) => ({
id: item.id,
buyStored: item.buyStored || 0,
rejectsAmount: item.rejectsAmount || 0,
locationId: item.locationId,
})),
}
const res = await submitWarehousingApi(submitData)
if (res.code === 200) {
ElMessage.success('入库成功')
visible.value = false
emit('refresh')
}
} catch (e) {
console.error(e)
} finally {
submitLoading.value = false
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.submit-warehousing {
height: 600px;
display: flex;
flex-direction: column;
overflow: hidden;
.form-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-start;
}
.table-section {
flex: 1;
overflow: hidden;
}
.form-item {
flex: 0 0 auto;
margin-right: 20px;
:deep(.el-input),
:deep(.el-select) {
width: 180px;
}
}
.remark-item {
flex: 1;
:deep(.el-input) {
width: 100%;
}
}
}
.dialog-footer {
text-align: center;
}
</style>
<template>
<div class="stocking-application-order-page card h-100 flex overflow-hidden">
<div class="order-status">
<ElTree
ref="treeRef"
default-expand-all
:expand-on-click-node="false"
:default-expanded-keys="[]"
:highlight-current="true"
node-key="code"
:data="treeData"
:props="{ children: 'children', label: 'name' }"
@node-click="nodeClick"
>
<template #default="{ data }">
<div class="tree-node">
<div class="tree-node-label">{{ data.name }}</div>
<div
v-if="data.countQuantity || data.countQuantity === 0"
class="tree-node-count"
>
{{ `(${data.countQuantity})` }}
</div>
</div>
</template>
</ElTree>
</div>
<div class="order-content flex-1 flex-column overflow-hidden">
<div class="header">
<div class="header-search-form">
<ElForm
ref="searchFormRef"
:model="searchForm"
inline
label-width="100px"
class="search-form"
>
<ElFormItem label="入库申请单号" prop="warehouseApplyNo">
<ElInput
v-model="searchForm.warehouseApplyNo"
clearable
placeholder="入库申请单号"
/>
</ElFormItem>
<ElFormItem label="备货仓库" prop="warehouseId">
<ElSelect
v-model="searchForm.warehouseId"
placeholder="请选择"
filterable
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="关联单号" prop="manageNo">
<ElInput
v-model="searchForm.manageNo"
clearable
placeholder="关联单号"
/>
</ElFormItem>
<ElFormItem label="库存SKU" prop="warehouseSku">
<ElInput
v-model="searchForm.warehouseSku"
clearable
placeholder="库存SKU"
/>
</ElFormItem>
<ElFormItem label="商品名称" prop="warehouseSkuName">
<ElInput
v-model="searchForm.warehouseSkuName"
clearable
placeholder="商品名称"
/>
</ElFormItem>
<ElFormItem class="form-item-buttons">
<ElButton type="primary" @click="() => search()">查询</ElButton>
<ElButton @click="() => resetSearchForm()">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="header-operation"></div>
</div>
<div class="table-content">
<SplitDiv size="60">
<template #top>
<div class="table-list flex-1 overflow-hidden">
<TableView
ref="tableRef"
highlight-current-row
:selectionable="true"
:serial-numberable="true"
:columns="tableColumns"
:paginated-data="tableData"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
>
<template #action="{ row }">
<ElButton
v-if="status === 'TO_BE_RECEIPT'"
link
type="primary"
@click="warehouseReceipt(row)"
>仓库收货</ElButton
>
<ElButton
v-if="status === 'TO_BE_INSPECTED'"
link
type="primary"
@click="submitWarehousing(row)"
>提交入库</ElButton
>
</template>
</TableView>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[50, 100, 200, 300, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</template>
<template #bottom>
<StockingApplyOrderDetailTabs :selected-row="selectedRow" />
</template>
</SplitDiv>
</div>
</div>
<!-- 提交入库弹框 -->
<SubmitWarehousingDialog ref="submitWarehousingDialogRef" @refresh="onRefresh" />
</div>
</template>
<script setup lang="tsx">
import { ref } from 'vue'
import { TreeData } from '@/types/api/supply/stockingOrder'
import { loadWarehouseListApi } from '@/api/common'
import { WarehouseListData } from '@/types'
import usePageList from '@/utils/hooks/usePageList'
import {
getStockingApplyOrderListApi,
getStockingApplyOrderTreeApi,
warehouseReceiptApi,
} from '@/api/warehouse/stockingApplyOrder'
import { TableData, SearchForm } from '@/types/api/warehouse/stockingApplyOrder'
import { useValue } from '@/utils/hooks/useValue'
import { ElTag, ElMessageBox, ElMessage } from 'element-plus'
import StockingApplyOrderDetailTabs from './StockingApplyOrderDetailTabs.vue'
import SubmitWarehousingDialog from './SubmitWarehousingDialog.vue'
const treeData = ref<TreeData[]>([])
const treeRef = ref()
const tableRef = ref()
const selectedRow = ref<TableData | null>(null)
const submitWarehousingDialogRef = ref<InstanceType<typeof SubmitWarehousingDialog>>()
const [searchForm, resetSearchForm] = useValue<SearchForm>({} as SearchForm)
const status = ref<string>('TO_BE_RECEIPT')
const warehouseList = ref<WarehouseListData[]>([])
const getStockingApplyOrderStatusName = (status: string) => {
const statusText = treeData.value[0]?.children?.find(
(item) => item.code === status,
)?.name
return statusText || ''
}
const tableColumns = computed(() => [
{
label: '入库申请单号',
prop: 'warehouseApplyNo',
width: 160,
align: 'center',
},
{
label: '关联单号',
prop: 'manageNo',
width: 160,
align: 'center',
},
{
label: '单据状态',
prop: 'storeStatus',
width: 120,
align: 'center',
render: (row: TableData) => {
return (
<ElTag type="primary">
{getStockingApplyOrderStatusName(row.storeStatus)}
</ElTag>
)
},
},
{
label: '入库仓库',
prop: 'warehouseName',
width: 160,
align: 'center',
},
{
label: 'SKU个数',
prop: 'skuTotal',
width: 100,
align: 'right',
},
{
label: '申请数量',
prop: 'total',
width: 100,
align: 'right',
},
{
label: '入库数量',
prop: 'buyStored',
width: 100,
align: 'right',
},
{
label: '预计到货日期',
prop: 'expectDeliveryTime',
width: 160,
align: 'center',
},
{
label: '实际到货日期',
prop: '',
width: 160,
align: 'center',
},
{
label: '制单人',
prop: 'createUserName',
width: 100,
align: 'center',
},
{
label: '制单时间',
prop: 'createTime',
width: 160,
align: 'center',
},
{
label: '操作',
prop: 'action',
fixed: 'right',
width: 100,
align: 'center',
slot: 'action',
},
])
const selection = ref<TableData[]>([])
const loadWarehouseList = async () => {
try {
const res = await loadWarehouseListApi()
if (res.code !== 200) return
warehouseList.value = res.data || []
} catch (e) {
console.error(e)
}
}
const loadTreeData = async () => {
try {
const res = await getStockingApplyOrderTreeApi()
if (res.code !== 200) return
treeData.value = [
{ code: '-1', name: '全部', children: res.data || [] } as TreeData,
]
await nextTick(() => {
treeRef.value!.setCurrentKey(status.value, true)
})
} catch (e) {
console.error(e)
}
}
const nodeClick = (data: TreeData) => {
status.value = data.code
search()
}
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList<TableData>({
query: (page, pageSize) =>
getStockingApplyOrderListApi(
{
...searchForm.value,
warehouseId: Array.isArray(searchForm.value.warehouseId)
? searchForm.value.warehouseId.join(',')
: (searchForm.value.warehouseId as string | undefined),
storeStatus: status.value === '-1' ? undefined : status.value,
},
page,
pageSize,
).then(async (res) => {
const { records } = res.data
await nextTick(() => {
tableRef.value.setCurrentRow(records[0])
selectedRow.value = (records as never)[0]
})
return res.data
}),
initLoad: true,
})
const handleRowClick = (row: TableData) => {
if (row) {
selectedRow.value = row
} else {
selectedRow.value = null
}
}
const handleSelectionChange = (val: TableData[]) => {
selection.value = val
}
const warehouseReceipt = async (row: TableData) => {
try {
await ElMessageBox.confirm(
`<div>
<p style="margin-bottom: 10px;">SKU个数:<span style="color: #F56C6C; font-weight: bold;">${row.skuTotal}</span>,预计到货数量:<span style="color:#F56C6C; font-weight: bold;">${row.total}</span></p>
<p>确认仓库是否收到货?</p>
</div>`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
},
)
} catch {
return
}
try {
const res = await warehouseReceiptApi(row.id)
if (res.code !== 200) return
ElMessage.success('收货成功')
search()
loadTreeData()
} catch (e) {
console.error(e)
}
}
const submitWarehousing = (row: TableData) => {
submitWarehousingDialogRef.value?.open(row)
}
const onRefresh = () => {
search()
loadTreeData()
}
onMounted(async () => {
await loadWarehouseList()
await loadTreeData()
})
</script>
<style lang="scss" scoped>
.order-status {
width: 160px;
:deep(.el-tree-node__content) {
height: 30px;
line-height: 30px;
}
:deep(.el-tree-node__label) {
font-size: 13px;
cursor: pointer;
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.is-current) {
.tree-node-label,
.tree-node-count {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.tree-node-label,
.tree-node-count {
background-color: transparent !important;
color: black !important;
}
}
}
:deep(.el-tree-node__label) {
font-size: 13px;
color: rgb(96, 98, 102);
cursor: pointer;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.el-tree-node__label) {
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.is-current) {
.el-tree-node__label {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.el-tree-node__label {
background-color: transparent !important;
color: black !important;
}
}
}
}
.tree-node {
display: flex;
color: #333;
font-weight: 500;
}
.table-content {
flex: 1;
margin-top: 10px;
overflow: hidden;
:deep(#top) {
height: 100%;
}
}
.search-form {
display: grid;
grid-template-columns: repeat(6, 1fr);
align-items: flex-start;
:deep(.el-form-item) {
margin-right: 10px;
margin-bottom: 10px;
}
}
</style>
...@@ -60,11 +60,11 @@ ...@@ -60,11 +60,11 @@
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem label="备货单号"> <ElFormItem label="备货计划单号">
<ElInput <ElInput
v-model.trim="searchForm.inNo" v-model.trim="searchForm.inNo"
clearable clearable
placeholder="请输入备货单号" placeholder="请输入备货计划单号"
style="width: 160px" style="width: 160px"
/> />
</ElFormItem> </ElFormItem>
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
align="center" align="center"
/> />
<ElTableColumn <ElTableColumn
label="备货单号" label="备货计划单号"
show-overflow-tooltip show-overflow-tooltip
prop="inNo" prop="inNo"
width="130" width="130"
...@@ -553,7 +553,7 @@ ...@@ -553,7 +553,7 @@
inline inline
label-width="90px" label-width="90px"
> >
<ElFormItem label="备货单号" prop="account"> <ElFormItem label="备货计划单号" prop="account">
<ElInput v-model.trim="editForm.inNo" clearable disabled /> <ElInput v-model.trim="editForm.inNo" clearable disabled />
</ElFormItem> </ElFormItem>
<ElFormItem label="工厂编号:" prop="factoryCode"> <ElFormItem label="工厂编号:" prop="factoryCode">
...@@ -943,7 +943,7 @@ ...@@ -943,7 +943,7 @@
width="500px" width="500px"
> >
<ElTable :data="updateShipmentNumberForm" border> <ElTable :data="updateShipmentNumberForm" border>
<ElTableColumn prop="inNo" label="备货单号" /> <ElTableColumn prop="inNo" label="备货计划单号" />
<ElTableColumn prop="shipmentNumber" label="物流单号"> <ElTableColumn prop="shipmentNumber" label="物流单号">
<template #default="{ row }"> <template #default="{ row }">
<el-input <el-input
...@@ -968,7 +968,7 @@ ...@@ -968,7 +968,7 @@
<el-input <el-input
ref="scanInputRef" ref="scanInputRef"
v-model="scanInput" v-model="scanInput"
placeholder="请输入备货单号" placeholder="请输入备货计划单号"
style="width: 90%" style="width: 90%"
clearable clearable
@keyup.enter="scan" @keyup.enter="scan"
...@@ -980,7 +980,7 @@ ...@@ -980,7 +980,7 @@
<el-descriptions-item label="备货仓库"> <el-descriptions-item label="备货仓库">
{{ scanData.warehouseName || '-' }} {{ scanData.warehouseName || '-' }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="备货单号"> <el-descriptions-item label="备货计划单号">
{{ scanData.inNo || '-' }} {{ scanData.inNo || '-' }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="物流单号"> <el-descriptions-item label="物流单号">
...@@ -2063,7 +2063,7 @@ const scan = async () => { ...@@ -2063,7 +2063,7 @@ const scan = async () => {
console.log(scanInput.value, 'scanInput.value') console.log(scanInput.value, 'scanInput.value')
if (!scanInput.value) { if (!scanInput.value) {
return ElMessage.warning('请输入备货单号') return ElMessage.warning('请输入备货计划单号')
} }
scanInput.value = scanInput.value.trim() scanInput.value = scanInput.value.trim()
// 将中文破折号转换为下划线 // 将中文破折号转换为下划线
......
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