Commit dda34c8e by qinjianhui

feat: 入库申请单功能开发

parent 590631b9
import axios from './axios'
import axios from '../axios'
import { BasePaginationData, BaseRespData } from '@/types/api'
import {
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({
meta: {
title: '缺货统计',
},
component: () => import('@/views/supply/OutOfStockStatistics.vue')
component: () => import('@/views/supply/OutOfStockStatistics.vue'),
},
{
path: '/supply/stocking-order',
......@@ -303,6 +303,14 @@ const router = createRouter({
component: stockingPlan,
},
{
path: '/warehouse/stocking-application',
meta: {
title: '入库申请单',
},
component: () =>
import('@/views/warehouse/stockingApplicationOrder/index.vue'),
},
{
path: '/warehouse/warning',
meta: {
title: '仓库预警',
......
......@@ -69,6 +69,11 @@ const menu: MenuItem[] = [
label: '库存',
children: [
{
index: '/warehouse/stocking-application',
id: 126,
label: '入库申请单',
},
{
index: '/warehouse/warning',
id: 125,
label: '仓库预警',
......@@ -155,7 +160,7 @@ const menu: MenuItem[] = [
},
{
label: '备货订单',
index:'/supply/stocking-order',
index: '/supply/stocking-order',
id: 2,
},
{
......
......@@ -118,3 +118,18 @@ export interface LogListData {
description?: 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 {
getProductBySkuApi,
getStockingOrderDetailByIdApi,
rejectedStockingOrderApi,
} from '@/api/stockingOrder'
} from '@/api/supplier/stockingOrder'
import ImageView from '@/components/ImageView.vue'
import { BigNumber } from 'bignumber.js'
......
......@@ -52,7 +52,7 @@ import {
getStockingOrderInternalMemoListByIdApi,
getStockingOrderLogListByIdApi,
getStockingOrderRelatedDocumentListByIdApi,
} from '@/api/stockingOrder'
} from '@/api/supplier/stockingOrder'
import LogList from '@/components/LogList.vue'
const relatedDocumentsColumns = computed(() => {
return [
......@@ -67,7 +67,7 @@ const relatedDocumentsColumns = computed(() => {
{
label: '制单人',
width: 120,
prop:"createUserName",
prop: 'createUserName',
},
{
label: '制单时间',
......@@ -86,6 +86,7 @@ const relatedDocumentsColumns = computed(() => {
{
label: '申请数量',
width: 120,
prop: 'shipmentQuantity',
align: 'right',
},
{
......
......@@ -124,7 +124,7 @@ import type { WarehouseListData } from '@/types'
import {
getStockingOrderDetailByIdApi,
supplierDispatchApi,
} from '@/api/stockingOrder'
} from '@/api/supplier/stockingOrder'
import ImageView from '@/components/ImageView.vue'
import TableView from '@/components/TableView.vue'
import { BigNumber } from 'bignumber.js'
......
......@@ -230,7 +230,7 @@ import {
getStockingOrderListApi,
stockingCompleteApi,
submitStockingOrderAuditApi,
} from '@/api/stockingOrder'
} from '@/api/supplier/stockingOrder'
import TableView from '@/components/TableView.vue'
import StockingOrderDetailTabs from './StockingOrderDetailTabs.vue'
import AddStockingOrderDialog from './AddStockingOrderDialog.vue'
......@@ -249,7 +249,7 @@ import {
getSupplierListApi,
loadWarehouseListApi,
} from '@/api/common'
import { getStockingOrderStatusTreeApi } from '@/api/stockingOrder'
import { getStockingOrderStatusTreeApi } from '@/api/supplier/stockingOrder'
import { userData } from '@/types/api/user'
import { ElButton, ElTag } from 'element-plus'
const tableColumns = computed(() => {
......@@ -441,7 +441,7 @@ const tableColumns = computed(() => {
})
const treeData = ref<TreeData[]>([])
const status = ref<string>('-1')
const status = ref<string>('PENDING_SUBMIT')
const selectedRow = ref<TableData | null>(null)
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>
......@@ -60,11 +60,11 @@
value-format="YYYY-MM-DD HH:mm:ss"
/>
</ElFormItem>
<ElFormItem label="备货单号">
<ElFormItem label="备货计划单号">
<ElInput
v-model.trim="searchForm.inNo"
clearable
placeholder="请输入备货单号"
placeholder="请输入备货计划单号"
style="width: 160px"
/>
</ElFormItem>
......@@ -206,7 +206,7 @@
align="center"
/>
<ElTableColumn
label="备货单号"
label="备货计划单号"
show-overflow-tooltip
prop="inNo"
width="130"
......@@ -553,7 +553,7 @@
inline
label-width="90px"
>
<ElFormItem label="备货单号" prop="account">
<ElFormItem label="备货计划单号" prop="account">
<ElInput v-model.trim="editForm.inNo" clearable disabled />
</ElFormItem>
<ElFormItem label="工厂编号:" prop="factoryCode">
......@@ -943,7 +943,7 @@
width="500px"
>
<ElTable :data="updateShipmentNumberForm" border>
<ElTableColumn prop="inNo" label="备货单号" />
<ElTableColumn prop="inNo" label="备货计划单号" />
<ElTableColumn prop="shipmentNumber" label="物流单号">
<template #default="{ row }">
<el-input
......@@ -968,7 +968,7 @@
<el-input
ref="scanInputRef"
v-model="scanInput"
placeholder="请输入备货单号"
placeholder="请输入备货计划单号"
style="width: 90%"
clearable
@keyup.enter="scan"
......@@ -980,7 +980,7 @@
<el-descriptions-item label="备货仓库">
{{ scanData.warehouseName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="备货单号">
<el-descriptions-item label="备货计划单号">
{{ scanData.inNo || '-' }}
</el-descriptions-item>
<el-descriptions-item label="物流单号">
......@@ -2063,7 +2063,7 @@ const scan = async () => {
console.log(scanInput.value, 'scanInput.value')
if (!scanInput.value) {
return ElMessage.warning('请输入备货单号')
return ElMessage.warning('请输入备货计划单号')
}
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