feat: add bit sample warehouse web pages

besure_bit
ck-chenkang 3 weeks ago
parent 62d26cdc12
commit 86058e36d1

@ -0,0 +1,62 @@
import request from '@/config/axios'
export interface BitWmsInboundReqVO {
productId: number
warehouseId: number
areaId?: number
count: number
supplierId?: number
remark?: string
}
export interface BitWmsOutboundReqVO {
productId: number
warehouseId: number
areaId?: number
count: number
outReason: string
remark?: string
}
export interface BitWmsMoveReqVO {
productId: number
fromWarehouseId: number
fromAreaId?: number
toWarehouseId: number
toAreaId?: number
count: number
remark?: string
}
export interface BitWmsLabelRespVO {
title?: string
printData: Record<string, any>
templateJson?: Record<string, any>
}
export const BitWmsApi = {
getSamplePage: async (params: any) => {
return await request.get({ url: '/erp/product/sample-page', params })
},
getSampleSimpleList: async (params?: any) => {
return await request.get({ url: '/erp/product/sample-simple-list', params })
},
inbound: async (data: BitWmsInboundReqVO) => {
return await request.post({ url: '/erp/bit-wms/inbound', data })
},
outbound: async (data: BitWmsOutboundReqVO) => {
return await request.post({ url: '/erp/bit-wms/outbound', data })
},
move: async (data: BitWmsMoveReqVO) => {
return await request.post({ url: '/erp/bit-wms/move', data })
},
scan: async (code: string) => {
return await request.get({ url: '/erp/bit-wms/scan', params: { code } })
},
getSampleLabel: async (productId: number) => {
return await request.get({ url: '/erp/bit-wms/sample-label', params: { productId } })
},
getLocationLabel: async (locationId: number) => {
return await request.get({ url: '/erp/bit-wms/location-label', params: { locationId } })
}
}

@ -0,0 +1,45 @@
<template>
<el-button size="small" type="primary" :loading="loading" @click="openPrint">
<Icon icon="ep:printer" class="mr-4px" />
{{ buttonText }}
</el-button>
<HiprintPreviewDialog ref="printDialogRef" />
</template>
<script setup lang="ts">
import HiprintPreviewDialog from '@/components/HiprintPreviewDialog/index.vue'
const props = withDefaults(
defineProps<{
title: string
buttonText?: string
printData?: Record<string, any>
templateJson?: Record<string, any>
loadLabel?: () => Promise<{ printData: Record<string, any>; templateJson?: Record<string, any> }>
}>(),
{
buttonText: '打印'
}
)
const printDialogRef = ref()
const loading = ref(false)
const openPrint = async () => {
loading.value = true
try {
const label = props.loadLabel ? await props.loadLabel() : undefined
const printData = label?.printData || props.printData || {}
const templateJson = label?.templateJson || props.templateJson
printDialogRef.value?.open({
title: props.title,
printData,
templateJson,
withDefaultQrcodeLayout: !templateJson,
paperSize: { width: 80, height: 60 }
})
} finally {
loading.value = false
}
}
</script>

@ -0,0 +1,119 @@
<template>
<ContentWrap>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px" class="bit-wms-form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="样品" prop="productId">
<el-select v-model="formData.productId" placeholder="请选择样品" filterable clearable class="!w-full">
<el-option v-for="item in sampleList" :key="item.id" :label="formatSampleLabel(item)" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商" prop="supplierId">
<el-select v-model="formData.supplierId" placeholder="请选择供应商" filterable clearable class="!w-full">
<el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="formData.warehouseId" placeholder="请选择仓库" filterable class="!w-full" @change="handleWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="库区" prop="areaId">
<el-select v-model="formData.areaId" placeholder="请选择库区" filterable clearable class="!w-full">
<el-option v-for="item in filteredAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="数量" prop="count">
<el-input-number v-model="formData.count" :min="1" :precision="0" class="!w-full" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" v-hasPermi="['erp:bit-wms-in:create']" @click="submitForm">
<Icon icon="ep:check" class="mr-5px" />确认入库
</el-button>
<el-button @click="resetForm"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { BitWmsApi } from '@/api/erp/bitwms'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
import { SupplierApi } from '@/api/erp/purchase/supplier'
defineOptions({ name: 'ErpBitWmsIn' })
const message = useMessage()
const formRef = ref()
const submitLoading = ref(false)
const sampleList = ref<any[]>([])
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const supplierList = ref<any[]>([])
const formData = reactive<any>({
productId: undefined,
warehouseId: undefined,
areaId: undefined,
count: 1,
supplierId: undefined,
remark: undefined
})
const formRules = reactive({
productId: [{ required: true, message: '请选择样品', trigger: 'change' }],
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
count: [{ required: true, message: '请输入数量', trigger: 'blur' }]
})
const filteredAreaList = computed(() => {
if (!formData.warehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === formData.warehouseId)
})
const formatSampleLabel = (item: any) => `${item.barCode || item.code || '-'} / ${item.name || '-'}`
const handleWarehouseChange = () => {
formData.areaId = undefined
}
const resetForm = () => {
formRef.value?.resetFields()
formData.count = 1
}
const submitForm = async () => {
await formRef.value.validate()
submitLoading.value = true
try {
await BitWmsApi.inbound(formData)
message.success('入库成功')
resetForm()
} finally {
submitLoading.value = false
}
}
onMounted(async () => {
const [samples, warehouses, areas, suppliers] = await Promise.all([
BitWmsApi.getSampleSimpleList(),
WarehouseApi.getWarehouseSimpleList(),
WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 }),
SupplierApi.getSupplierSimpleList()
])
sampleList.value = samples || []
warehouseList.value = warehouses || []
areaList.value = areas.list || []
supplierList.value = suppliers || []
})
</script>

@ -0,0 +1,150 @@
<template>
<div>
<template v-if="!formVisible">
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable filterable class="!w-220px" @change="handleWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="库区" prop="areaId">
<el-select v-model="queryParams.areaId" placeholder="请选择库区" clearable filterable class="!w-220px">
<el-option v-for="item in filteredAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="库位码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入库位码" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="库位名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入库位名称" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />查询</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
<el-button type="primary" plain v-hasPermi="['erp:bit-wms-location:create']" @click="openForm('create')">
<Icon icon="ep:plus" class="mr-5px" />新增库位
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<el-table-column label="仓库" align="center" min-width="140">
<template #default="{ row }">{{ getWarehouseName(row.warehouseId) }}</template>
</el-table-column>
<el-table-column label="库区" align="center" min-width="120">
<template #default="{ row }">{{ getAreaName(row.areaId) }}</template>
</el-table-column>
<el-table-column label="库位码" align="center" prop="code" min-width="140" />
<el-table-column label="库位名称" align="center" prop="name" min-width="140" />
<el-table-column label="面积" align="center" prop="areaSize" width="100" />
<el-table-column label="最大承重" align="center" prop="maxLoadWeight" width="110" />
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="210" fixed="right">
<template #default="{ row }">
<el-button link type="primary" v-hasPermi="['erp:bit-wms-location:update']" @click="openForm('update', row.id)"></el-button>
<BitWmsPrintButton
v-hasPermi="['erp:bit-wms-location:print']"
title="库位标签"
:load-label="() => loadLocationLabel(row.id)"
/>
</template>
</el-table-column>
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
</ContentWrap>
</template>
<WarehouseLocationForm v-else ref="formRef" @success="getList" @closed="formVisible = false" />
</div>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { WarehouseLocationApi } from '@/api/erp/stock/warehouselocation'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
import { BitWmsApi } from '@/api/erp/bitwms'
import WarehouseLocationForm from '@/views/erp/warehouselocation/WarehouseLocationForm.vue'
import BitWmsPrintButton from '../components/BitWmsPrintButton.vue'
defineOptions({ name: 'ErpBitWmsLocation' })
const loading = ref(true)
const list = ref<any[]>([])
const total = ref(0)
const queryFormRef = ref()
const formRef = ref()
const formVisible = ref(false)
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
warehouseId: undefined,
areaId: undefined,
code: undefined,
name: undefined
})
const filteredAreaList = computed(() => {
if (!queryParams.warehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === queryParams.warehouseId)
})
const buildQueryParams = () =>
Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value !== undefined && value !== null && value !== ''))
const getWarehouseName = (warehouseId: number) => warehouseList.value.find((item) => item.id === warehouseId)?.name || '-'
const getAreaName = (areaId: number) => areaList.value.find((item) => item.id === areaId)?.areaName || '-'
const getList = async () => {
loading.value = true
try {
const data = await WarehouseLocationApi.getWarehouseLocationPage(buildQueryParams())
list.value = data.list || []
total.value = data.total || 0
} finally {
loading.value = false
}
}
const loadBaseData = async () => {
warehouseList.value = await WarehouseApi.getWarehouseSimpleList()
const areaData = await WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 })
areaList.value = areaData.list || []
}
const handleWarehouseChange = () => {
queryParams.areaId = undefined
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const openForm = async (type: string, id?: number) => {
formVisible.value = true
await nextTick()
formRef.value.open(type, id)
}
const loadLocationLabel = async (id: number) => await BitWmsApi.getLocationLabel(id)
onMounted(async () => {
await loadBaseData()
await getList()
})
</script>

@ -0,0 +1,127 @@
<template>
<ContentWrap>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" class="bit-wms-form">
<el-form-item label="样品" prop="productId">
<el-select v-model="formData.productId" placeholder="请选择样品" filterable clearable class="!w-full">
<el-option v-for="item in sampleList" :key="item.id" :label="formatSampleLabel(item)" :value="item.id" />
</el-select>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="来源仓库" prop="fromWarehouseId">
<el-select v-model="formData.fromWarehouseId" placeholder="请选择来源仓库" filterable class="!w-full" @change="handleFromWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="来源库区" prop="fromAreaId">
<el-select v-model="formData.fromAreaId" placeholder="请选择来源库区" filterable clearable class="!w-full">
<el-option v-for="item in fromAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="目标仓库" prop="toWarehouseId">
<el-select v-model="formData.toWarehouseId" placeholder="请选择目标仓库" filterable class="!w-full" @change="handleToWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="目标库区" prop="toAreaId">
<el-select v-model="formData.toAreaId" placeholder="请选择目标库区" filterable clearable class="!w-full">
<el-option v-for="item in toAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="数量" prop="count">
<el-input-number v-model="formData.count" :min="1" :precision="0" class="!w-240px" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" v-hasPermi="['erp:bit-wms-move:create']" @click="submitForm">
<Icon icon="ep:check" class="mr-5px" />确认移库
</el-button>
<el-button @click="resetForm"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { BitWmsApi } from '@/api/erp/bitwms'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
defineOptions({ name: 'ErpBitWmsMove' })
const message = useMessage()
const formRef = ref()
const submitLoading = ref(false)
const sampleList = ref<any[]>([])
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const formData = reactive<any>({
productId: undefined,
fromWarehouseId: undefined,
fromAreaId: undefined,
toWarehouseId: undefined,
toAreaId: undefined,
count: 1,
remark: undefined
})
const formRules = reactive({
productId: [{ required: true, message: '请选择样品', trigger: 'change' }],
fromWarehouseId: [{ required: true, message: '请选择来源仓库', trigger: 'change' }],
toWarehouseId: [{ required: true, message: '请选择目标仓库', trigger: 'change' }],
count: [{ required: true, message: '请输入数量', trigger: 'blur' }]
})
const fromAreaList = computed(() => {
if (!formData.fromWarehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === formData.fromWarehouseId)
})
const toAreaList = computed(() => {
if (!formData.toWarehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === formData.toWarehouseId)
})
const formatSampleLabel = (item: any) => `${item.barCode || item.code || '-'} / ${item.name || '-'}`
const handleFromWarehouseChange = () => {
formData.fromAreaId = undefined
}
const handleToWarehouseChange = () => {
formData.toAreaId = undefined
}
const resetForm = () => {
formRef.value?.resetFields()
formData.count = 1
}
const submitForm = async () => {
await formRef.value.validate()
submitLoading.value = true
try {
await BitWmsApi.move(formData)
message.success('移库成功')
resetForm()
} finally {
submitLoading.value = false
}
}
onMounted(async () => {
const [samples, warehouses, areas] = await Promise.all([
BitWmsApi.getSampleSimpleList(),
WarehouseApi.getWarehouseSimpleList(),
WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 })
])
sampleList.value = samples || []
warehouseList.value = warehouses || []
areaList.value = areas.list || []
})
</script>

@ -0,0 +1,142 @@
<template>
<ContentWrap>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px" class="bit-wms-form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="样品" prop="productId">
<el-select v-model="formData.productId" placeholder="请选择样品" filterable clearable class="!w-full" @change="loadCurrentStock">
<el-option v-for="item in sampleList" :key="item.id" :label="formatSampleLabel(item)" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前库存">
<el-input :model-value="currentStockText" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="formData.warehouseId" placeholder="请选择仓库" filterable class="!w-full" @change="handleWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="库区" prop="areaId">
<el-select v-model="formData.areaId" placeholder="请选择库区" filterable clearable class="!w-full" @change="loadCurrentStock">
<el-option v-for="item in filteredAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="数量" prop="count">
<el-input-number v-model="formData.count" :min="1" :precision="0" class="!w-full" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="出库原因" prop="outReason">
<el-select v-model="formData.outReason" placeholder="请选择出库原因" class="!w-full">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.BIT_SAMPLE_OUT_REASON)" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" v-hasPermi="['erp:bit-wms-out:create']" @click="submitForm">
<Icon icon="ep:check" class="mr-5px" />确认出库
</el-button>
<el-button @click="resetForm"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import { BitWmsApi } from '@/api/erp/bitwms'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
import { StockApi } from '@/api/erp/stock/stock'
defineOptions({ name: 'ErpBitWmsOut' })
const message = useMessage()
const formRef = ref()
const submitLoading = ref(false)
const stockLoading = ref(false)
const currentStock = ref<number | undefined>(undefined)
const sampleList = ref<any[]>([])
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const formData = reactive<any>({
productId: undefined,
warehouseId: undefined,
areaId: undefined,
count: 1,
outReason: undefined,
remark: undefined
})
const formRules = reactive({
productId: [{ required: true, message: '请选择样品', trigger: 'change' }],
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
count: [{ required: true, message: '请输入数量', trigger: 'blur' }],
outReason: [{ required: true, message: '请选择出库原因', trigger: 'change' }]
})
const filteredAreaList = computed(() => {
if (!formData.warehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === formData.warehouseId)
})
const currentStockText = computed(() => {
if (stockLoading.value) return '查询中...'
return currentStock.value === undefined ? '-' : `${currentStock.value}`
})
const formatSampleLabel = (item: any) => `${item.barCode || item.code || '-'} / ${item.name || '-'}`
const handleWarehouseChange = () => {
formData.areaId = undefined
loadCurrentStock()
}
const loadCurrentStock = async () => {
if (!formData.productId || !formData.warehouseId) {
currentStock.value = undefined
return
}
stockLoading.value = true
try {
const data = await StockApi.getStock2(formData.productId, formData.warehouseId, formData.areaId)
currentStock.value = data?.count || 0
} finally {
stockLoading.value = false
}
}
const resetForm = () => {
formRef.value?.resetFields()
formData.count = 1
currentStock.value = undefined
}
const submitForm = async () => {
await formRef.value.validate()
submitLoading.value = true
try {
await BitWmsApi.outbound(formData)
message.success('出库成功')
await loadCurrentStock()
} finally {
submitLoading.value = false
}
}
onMounted(async () => {
const [samples, warehouses, areas] = await Promise.all([
BitWmsApi.getSampleSimpleList(),
WarehouseApi.getWarehouseSimpleList(),
WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 })
])
sampleList.value = samples || []
warehouseList.value = warehouses || []
areaList.value = areas.list || []
})
</script>

@ -0,0 +1,163 @@
<template>
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
<el-form-item label="样品" prop="productId">
<el-select v-model="queryParams.productId" placeholder="请选择样品" clearable filterable class="!w-240px">
<el-option v-for="item in sampleList" :key="item.id" :label="formatSampleLabel(item)" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable filterable class="!w-220px" @change="handleWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="库区" prop="areaId">
<el-select v-model="queryParams.areaId" placeholder="请选择库区" clearable filterable class="!w-220px">
<el-option v-for="item in filteredAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="业务单号" prop="bizNo">
<el-input v-model="queryParams.bizNo" placeholder="请输入业务单号" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />查询</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<el-table-column label="业务单号" align="center" prop="bizNo" min-width="170" />
<el-table-column label="方向" align="center" prop="bizDirection" width="100">
<template #default="{ row }">
<el-tag v-if="row.bizDirection" :type="getDirectionTagType(row.bizDirection)">{{ row.bizDirection }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="样品名称" align="left" prop="productName" min-width="180" />
<el-table-column label="分类" align="center" prop="categoryName" min-width="120" />
<el-table-column label="仓库" align="center" prop="warehouseName" min-width="140" />
<el-table-column label="库区" align="center" prop="areaName" min-width="120" />
<el-table-column label="变动数量" align="center" prop="count" min-width="120">
<template #default="{ row }">
<span :class="getDirectionClass(row.bizDirection)">{{ formatCount(row.count, row.unitName, true, row.bizDirection) }}</span>
</template>
</el-table-column>
<el-table-column label="结存" align="center" prop="totalCount" min-width="120">
<template #default="{ row }">{{ formatCount(row.totalCount, row.unitName) }}</template>
</el-table-column>
<el-table-column label="操作人" align="center" prop="creatorName" min-width="110" />
<el-table-column label="操作时间" align="center" prop="recordTime" :formatter="dateFormatter" width="180" />
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
</ContentWrap>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import { BitWmsApi } from '@/api/erp/bitwms'
import { StockRecordApi } from '@/api/erp/stock/record'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
defineOptions({ name: 'ErpBitWmsRecord' })
const loading = ref(true)
const list = ref<any[]>([])
const total = ref(0)
const queryFormRef = ref()
const sampleList = ref<any[]>([])
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const queryParams = reactive<any>({
pageNo: 1,
pageSize: 10,
productId: undefined,
warehouseId: undefined,
areaId: undefined,
bizNo: undefined,
isSample: true,
bizUnit: 'BIT'
})
const filteredAreaList = computed(() => {
if (!queryParams.warehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === queryParams.warehouseId)
})
const formatSampleLabel = (item: any) => `${item.barCode || item.code || '-'} / ${item.name || '-'}`
const isStockIn = (direction?: string) => direction === '入库'
const isStockOut = (direction?: string) => direction === '出库'
const getDirectionTagType = (direction?: string) => {
if (isStockIn(direction)) return 'success'
if (isStockOut(direction)) return 'danger'
return 'info'
}
const getDirectionClass = (direction?: string) => {
if (isStockIn(direction)) return 'bit-wms-record-in'
if (isStockOut(direction)) return 'bit-wms-record-out'
return ''
}
const formatNumber = (value: number | string | undefined) => {
if (value === undefined || value === null || value === '') return '-'
const num = Number(value)
return Number.isFinite(num) ? num.toLocaleString() : String(value)
}
const formatCount = (value: number | string | undefined, unit?: string, withSign = false, direction?: string) => {
const unitText = unit ? ` ${unit}` : ''
if (!withSign) return `${formatNumber(value)}${unitText}`
const num = Number(value)
if (!Number.isFinite(num)) return `${value ?? '-'}${unitText}`
const absValue = Math.abs(num).toLocaleString()
if (isStockIn(direction)) return `+${absValue}${unitText}`
if (isStockOut(direction)) return `-${absValue}${unitText}`
return `${formatNumber(value)}${unitText}`
}
const buildQueryParams = () =>
Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value !== undefined && value !== null && value !== ''))
const getList = async () => {
loading.value = true
try {
const data = await StockRecordApi.getStockRecordPage(buildQueryParams())
list.value = data.list || []
total.value = data.total || 0
} finally {
loading.value = false
}
}
const handleWarehouseChange = () => {
queryParams.areaId = undefined
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
queryParams.isSample = true
queryParams.bizUnit = 'BIT'
handleQuery()
}
onMounted(async () => {
const [samples, warehouses, areas] = await Promise.all([
BitWmsApi.getSampleSimpleList(),
WarehouseApi.getWarehouseSimpleList(),
WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 })
])
sampleList.value = samples || []
warehouseList.value = warehouses || []
areaList.value = areas.list || []
await getList()
})
</script>
<style scoped>
.bit-wms-record-in {
color: var(--el-color-success);
}
.bit-wms-record-out {
color: var(--el-color-danger);
}
</style>

@ -0,0 +1,127 @@
<template>
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
<el-form-item label="样品码" prop="barCode">
<el-input v-model="queryParams.barCode" placeholder="请输入样品码" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="样品名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入样品名称" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="样品类型" prop="categoryType">
<el-select v-model="queryParams.categoryType" placeholder="请选择样品类型" clearable class="!w-180px">
<el-option v-for="dict in getIntDictOptions(DICT_TYPE.MATERIAL_CLASSIFICATION_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="样品分类" prop="sampleCategory">
<el-select v-model="queryParams.sampleCategory" placeholder="请选择样品分类" clearable class="!w-180px">
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.BIT_SAMPLE_CATEGORY)" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="客户名称" prop="customerName">
<el-input v-model="queryParams.customerName" placeholder="请输入客户名称" clearable class="!w-220px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />查询</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
<el-button type="primary" plain v-hasPermi="['erp:bit-wms-sample:create']" @click="openProductCreate">
<Icon icon="ep:plus" class="mr-5px" />新增样品
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<el-table-column label="样品码" align="center" prop="barCode" min-width="160" />
<el-table-column label="样品名称" align="left" prop="name" min-width="180" />
<el-table-column label="样品类型" align="center" prop="categoryType" min-width="110">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.MATERIAL_CLASSIFICATION_TYPE" :value="row.categoryType" />
</template>
</el-table-column>
<el-table-column label="样品分类" align="center" prop="sampleCategory" min-width="120">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.BIT_SAMPLE_CATEGORY" :value="row.sampleCategory" />
</template>
</el-table-column>
<el-table-column label="规格" align="center" prop="standard" min-width="140" />
<el-table-column label="客户名称" align="center" prop="customerName" min-width="140" />
<el-table-column label="默认仓库" align="center" min-width="160">
<template #default="{ row }">
{{ row.defaultWarehouseName || row.warehouseName || '-' }}
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="210" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openProductDetail(row.id)"></el-button>
<el-button link type="primary" v-hasPermi="['erp:bit-wms-sample:update']" @click="openProductEdit(row.id)"></el-button>
<BitWmsPrintButton
v-hasPermi="['erp:bit-wms-sample:print']"
title="样品标签"
:load-label="() => loadSampleLabel(row.id)"
/>
</template>
</el-table-column>
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
</ContentWrap>
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import { BitWmsApi } from '@/api/erp/bitwms'
import BitWmsPrintButton from '../components/BitWmsPrintButton.vue'
defineOptions({ name: 'ErpBitWmsSample' })
const router = useRouter()
const loading = ref(true)
const list = ref<any[]>([])
const total = ref(0)
const queryFormRef = ref()
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
barCode: undefined,
name: undefined,
categoryType: undefined,
sampleCategory: undefined,
customerName: undefined
})
const buildQueryParams = () =>
Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value !== undefined && value !== null && value !== ''))
const getList = async () => {
loading.value = true
try {
const data = await BitWmsApi.getSamplePage(buildQueryParams())
list.value = data.list || []
total.value = data.total || 0
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const loadSampleLabel = async (id: number) => await BitWmsApi.getSampleLabel(id)
const openProductCreate = () => router.push({ path: '/erp/product/product' })
const openProductEdit = (id: number) => router.push({ path: '/erp/product/product', query: { id, type: 'update' } })
const openProductDetail = (id: number) => router.push({ path: '/erp/product/product', query: { id, type: 'detail' } })
onMounted(getList)
</script>

@ -0,0 +1,119 @@
<template>
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
<el-form-item label="样品" prop="productId">
<el-select v-model="queryParams.productId" placeholder="请选择样品" clearable filterable class="!w-240px">
<el-option v-for="item in sampleList" :key="item.id" :label="formatSampleLabel(item)" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable filterable class="!w-220px" @change="handleWarehouseChange">
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="库区" prop="areaId">
<el-select v-model="queryParams.areaId" placeholder="请选择库区" clearable filterable class="!w-220px">
<el-option v-for="item in filteredAreaList" :key="item.id" :label="item.areaName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />查询</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<el-table-column label="样品码" align="center" prop="barCode" min-width="150" />
<el-table-column label="样品名称" align="left" min-width="180">
<template #default="{ row }">{{ row.name || row.productName || '-' }}</template>
</el-table-column>
<el-table-column label="样品分类" align="center" prop="categoryName" min-width="130" />
<el-table-column label="仓库" align="center" prop="warehouseName" min-width="140" />
<el-table-column label="库区" align="center" prop="areaName" min-width="120" />
<el-table-column label="库存" align="center" prop="count" min-width="120">
<template #default="{ row }">{{ formatCount(row.count) }}</template>
</el-table-column>
<el-table-column label="单位" align="center" prop="unitName" width="90" />
<el-table-column label="库存展示" align="center" prop="stockDisplay" min-width="200">
<template #default="{ row }">{{ row.stockDisplay || '-' }}</template>
</el-table-column>
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
</ContentWrap>
</template>
<script setup lang="ts">
import { BitWmsApi } from '@/api/erp/bitwms'
import { StockApi } from '@/api/erp/stock/stock'
import { WarehouseApi } from '@/api/erp/stock/warehouse'
import { WarehouseAreaApi } from '@/api/erp/stock/warehousearea'
defineOptions({ name: 'ErpBitWmsStock' })
const loading = ref(true)
const list = ref<any[]>([])
const total = ref(0)
const queryFormRef = ref()
const sampleList = ref<any[]>([])
const warehouseList = ref<any[]>([])
const areaList = ref<any[]>([])
const queryParams = reactive<any>({
pageNo: 1,
pageSize: 10,
productId: undefined,
warehouseId: undefined,
areaId: undefined,
isSample: true,
bizUnit: 'BIT'
})
const filteredAreaList = computed(() => {
if (!queryParams.warehouseId) return areaList.value
return areaList.value.filter((item) => item.warehouseId === queryParams.warehouseId)
})
const formatSampleLabel = (item: any) => `${item.barCode || item.code || '-'} / ${item.name || '-'}`
const formatCount = (value: number | string | undefined) => {
if (value === undefined || value === null || value === '') return '-'
const num = Number(value)
return Number.isFinite(num) ? num.toLocaleString() : String(value)
}
const buildQueryParams = () =>
Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value !== undefined && value !== null && value !== ''))
const getList = async () => {
loading.value = true
try {
const data = await StockApi.getStockPage(buildQueryParams())
list.value = data.list || []
total.value = data.total || 0
} finally {
loading.value = false
}
}
const handleWarehouseChange = () => {
queryParams.areaId = undefined
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
queryParams.isSample = true
queryParams.bizUnit = 'BIT'
handleQuery()
}
onMounted(async () => {
const [samples, warehouses, areas] = await Promise.all([
BitWmsApi.getSampleSimpleList(),
WarehouseApi.getWarehouseSimpleList(),
WarehouseAreaApi.getWarehouseAreaPage({ pageNo: 1, pageSize: 100 })
])
sampleList.value = samples || []
warehouseList.value = warehouses || []
areaList.value = areas.list || []
await getList()
})
</script>
Loading…
Cancel
Save