|
|
@@ -0,0 +1,611 @@
|
|
|
+<template>
|
|
|
+ <el-container>
|
|
|
+ <el-header
|
|
|
+ class="gva-search-box"
|
|
|
+ style="line-height: 60px"
|
|
|
+ >
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="18">
|
|
|
+ <el-form
|
|
|
+ :inline="true"
|
|
|
+ :model="searchGoods"
|
|
|
+ style="margin-top: 10px"
|
|
|
+ >
|
|
|
+ <el-form-item label="物品名称">
|
|
|
+ <el-select
|
|
|
+ v-model="searchGoods.commodityId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择商品"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in commodityData"
|
|
|
+ :key="item.ID"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.ID"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="位置">
|
|
|
+ <el-select
|
|
|
+ v-model="searchGoods.warehouseId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择仓库"
|
|
|
+ clearable
|
|
|
+ @change="warehouseChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in warehouseData"
|
|
|
+ :key="item.ID"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.ID"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-select
|
|
|
+ v-model="searchGoods.storageAreaId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择库区"
|
|
|
+ clearable
|
|
|
+ @change="storageAreaChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in storageAreaByWarehouseData"
|
|
|
+ :key="item.ID"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.ID"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-select
|
|
|
+ v-model="searchGoods.placeId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择库位"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in placeByStorageAreaData"
|
|
|
+ :key="item.ID"
|
|
|
+ :label="item.code"
|
|
|
+ :value="item.ID"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="getData"
|
|
|
+ >查询</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-button @click="inboundDialog = true">
|
|
|
+ 入库
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="outboundDialog = true">
|
|
|
+ 出库
|
|
|
+ </el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-header>
|
|
|
+ <el-main class="gva-table-box">
|
|
|
+ <el-table
|
|
|
+ :data="goodsList"
|
|
|
+ height="600"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ prop="commodity.name"
|
|
|
+ label="物品名称"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ prop="number"
|
|
|
+ label="数量"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ prop="warehouse.name"
|
|
|
+ label="仓库"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ prop="storageArea.name"
|
|
|
+ label="库区"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ prop="place.shelfNumber"
|
|
|
+ label="货架号"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ prop="place.layerNumber"
|
|
|
+ label="层号"
|
|
|
+ />
|
|
|
+ </el-table>
|
|
|
+ <!-- 分页-->
|
|
|
+ <div class="gva-pagination">
|
|
|
+ <el-pagination
|
|
|
+ :current-page="searchGoods.pageInfo.page"
|
|
|
+ :page-size="searchGoods.pageInfo.pageSize"
|
|
|
+ :page-sizes="[10, 30, 50, 100]"
|
|
|
+ :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <el-drawer
|
|
|
+ v-model="inboundDialog"
|
|
|
+ title="新建入库单"
|
|
|
+ direction="rtl"
|
|
|
+ size="50%"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <el-header
|
|
|
+ class="gva-search-box"
|
|
|
+ style="line-height: 60px"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ style="margin-top: 12px;"
|
|
|
+ @click="addInboundCargo"
|
|
|
+ >添加商品</el-button>
|
|
|
+ </el-header>
|
|
|
+ <el-main>
|
|
|
+ <el-table
|
|
|
+ :data="manifestInboundData.cargos"
|
|
|
+ height="500"
|
|
|
+ border
|
|
|
+ style="width: 100%;margin-bottom: 20px"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ label="商品"
|
|
|
+ width="300"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-model="row.commodityId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择商品"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in commodityData"
|
|
|
+ :key="item.ID"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.ID"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ label="数量"
|
|
|
+ width="200"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-model="row.number"
|
|
|
+ :min="1"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="仓库/库区/库位">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-cascader
|
|
|
+ v-model="row.location"
|
|
|
+ :options="warehouseOptions"
|
|
|
+ :props="{
|
|
|
+ value: 'value',
|
|
|
+ label: 'label',
|
|
|
+ children: 'children'
|
|
|
+ }"
|
|
|
+ placeholder="仓库/库区/库位"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ label="操作"
|
|
|
+ width="100"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ @click="removeInboundCargo(scope.$index)"
|
|
|
+ >删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-form-item
|
|
|
+ label="标题"
|
|
|
+ prop="title"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="manifestInboundData.title"
|
|
|
+ style="width: 200px;"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ label="管理人"
|
|
|
+ prop="custodian"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="manifestInboundData.custodian"
|
|
|
+ style="width: 200px;"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="submitInboundForm"
|
|
|
+ >提交入库</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-main>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+ <el-drawer
|
|
|
+ v-model="outboundDialog"
|
|
|
+ title="新建出库单"
|
|
|
+ direction="rtl"
|
|
|
+ size="50%"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <el-header
|
|
|
+ class="gva-search-box"
|
|
|
+ style="line-height: 60px"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ icon="Plus"
|
|
|
+ class="mt-2"
|
|
|
+ @click="addOutboundCargo"
|
|
|
+ >添加商品</el-button>
|
|
|
+ </el-header>
|
|
|
+ <el-main>
|
|
|
+ <el-form
|
|
|
+ ref="outboundForm"
|
|
|
+ :model="manifestOutboundData"
|
|
|
+ >
|
|
|
+ <!-- 商品出库表格 -->
|
|
|
+ <el-table
|
|
|
+ :data="manifestOutboundData.cargos"
|
|
|
+ height="500"
|
|
|
+ border
|
|
|
+ style="width: 100%;margin-bottom: 20px"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ label="商品"
|
|
|
+ width="250"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-model.number="row.commodityId"
|
|
|
+ filterable
|
|
|
+ placeholder="选择商品"
|
|
|
+ @change="updateCommodityInfo(row)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in goodsList"
|
|
|
+ :key="item.commodity.ID"
|
|
|
+ :label="`${item.commodity.name} (库存: ${item.number})`"
|
|
|
+ :value="item.commodity.ID"
|
|
|
+ :disabled="isCommoditySelected(item.commodity.ID)"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column
|
|
|
+ label="出库数量"
|
|
|
+ width="200"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-model="row.number"
|
|
|
+ :min="1"
|
|
|
+ :max="row.maxNumber || 0"
|
|
|
+ :disabled="!row.commodityId"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column label="出库位置">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-cascader
|
|
|
+ v-model="row.location"
|
|
|
+ disabled
|
|
|
+ :options="warehouseOptions"
|
|
|
+ :props="cascaderProps"
|
|
|
+ placeholder="仓库/库区/库位"
|
|
|
+ style="width: 320px;"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <el-table-column
|
|
|
+ label="操作"
|
|
|
+ width="80"
|
|
|
+ >
|
|
|
+ <template #default="{ $index }">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ icon="Delete"
|
|
|
+ size="small"
|
|
|
+ @click="removeOutboundCargo($index)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-form-item
|
|
|
+ label="标题"
|
|
|
+ prop="title"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="manifestOutboundData.title"
|
|
|
+ style="width: 200px;"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ label="管理人"
|
|
|
+ prop="custodian"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="manifestOutboundData.custodian"
|
|
|
+ placeholder="请输入负责人姓名"
|
|
|
+ style="width: 200px;"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="submitForm"
|
|
|
+ >提交出库</el-button>
|
|
|
+ <el-button @click="resetForm">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-main>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+ </el-main>
|
|
|
+ </el-container>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { queryAllCommodity } from '@/api/inventory'
|
|
|
+import {
|
|
|
+ createInboundManifest, createOutboundManifest, queryAllGoods,
|
|
|
+ queryAllPlace,
|
|
|
+ queryAllStorageArea,
|
|
|
+ queryAllWarehouse,
|
|
|
+ queryGoodsList
|
|
|
+} from '@/api/storehouse'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+// 数据
|
|
|
+const commodityData = ref()
|
|
|
+const warehouseData = ref()
|
|
|
+const storageAreaData = ref()
|
|
|
+const storageAreaByWarehouseData = ref()
|
|
|
+const placeData = ref()
|
|
|
+const placeByStorageAreaData = ref()
|
|
|
+const warehouseOptions = ref()
|
|
|
+const goodsList = ref()
|
|
|
+const goodsData = ref()
|
|
|
+
|
|
|
+const warehouseChange = (val) => {
|
|
|
+ if (val === undefined) {
|
|
|
+ searchGoods.value.placeId = undefined
|
|
|
+ placeByStorageAreaData.value = undefined
|
|
|
+ searchGoods.value.storageAreaId = undefined
|
|
|
+ storageAreaByWarehouseData.value = undefined
|
|
|
+ }
|
|
|
+ for (const i in warehouseData.value) {
|
|
|
+ if (warehouseData.value[i].ID === val) {
|
|
|
+ storageAreaByWarehouseData.value = warehouseData.value[i].storageAreas
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const storageAreaChange = (val) => {
|
|
|
+ if (val === undefined) {
|
|
|
+ searchGoods.value.placeId = undefined
|
|
|
+ placeByStorageAreaData.value = undefined
|
|
|
+ }
|
|
|
+ for (const i in storageAreaByWarehouseData.value) {
|
|
|
+ if (storageAreaByWarehouseData.value[i].ID === val) {
|
|
|
+ placeByStorageAreaData.value = storageAreaByWarehouseData.value[i].places
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const searchGoods = ref({
|
|
|
+ pageInfo: {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 10
|
|
|
+ },
|
|
|
+ commodityId: undefined,
|
|
|
+ warehouseId: undefined,
|
|
|
+ storageAreaId: undefined,
|
|
|
+ placeId: undefined,
|
|
|
+})
|
|
|
+
|
|
|
+const total = ref(0)
|
|
|
+
|
|
|
+const getData = async() => {
|
|
|
+ await queryAllCommodity().then(res => {
|
|
|
+ commodityData.value = res.data
|
|
|
+ })
|
|
|
+ await queryAllWarehouse().then(res => {
|
|
|
+ warehouseData.value = res.data
|
|
|
+ warehouseOptions.value = res.data.map(warehouse => ({
|
|
|
+ value: warehouse.ID,
|
|
|
+ label: warehouse.name,
|
|
|
+ children: warehouse.storageAreas.map(area => ({
|
|
|
+ value: area.ID,
|
|
|
+ label: area.name,
|
|
|
+ children: area.places.map(place => ({
|
|
|
+ value: place.ID,
|
|
|
+ label: `${place.shelfNumber}架-${place.layerNumber}层 (${place.code})`
|
|
|
+ }))
|
|
|
+ }))
|
|
|
+ }))
|
|
|
+ })
|
|
|
+ await queryAllStorageArea().then(res => {
|
|
|
+ storageAreaData.value = res.data
|
|
|
+ })
|
|
|
+ await queryAllPlace().then(res => {
|
|
|
+ placeData.value = res.data
|
|
|
+ })
|
|
|
+ await queryGoodsList(searchGoods.value).then(res => {
|
|
|
+ goodsList.value = res.data.list
|
|
|
+ total.value = res.data.total
|
|
|
+ })
|
|
|
+ await queryAllGoods().then(res => {
|
|
|
+ goodsData.value = res.data
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 分页
|
|
|
+const handleSizeChange = (val) => {
|
|
|
+ searchGoods.value.pageSize = val
|
|
|
+ getData()
|
|
|
+}
|
|
|
+
|
|
|
+const handleCurrentChange = (val) => {
|
|
|
+ searchGoods.value.page = val
|
|
|
+ getData()
|
|
|
+}
|
|
|
+
|
|
|
+// 入库
|
|
|
+const manifestInboundData = ref({
|
|
|
+ manifestGenre: '入库',
|
|
|
+ title: '',
|
|
|
+ custodian: '',
|
|
|
+ cargos: [
|
|
|
+ { commodityId: null, number: 1, location: [] },
|
|
|
+ ],
|
|
|
+})
|
|
|
+
|
|
|
+const inboundDialog = ref(false)
|
|
|
+
|
|
|
+// 添加商品行
|
|
|
+const addInboundCargo = () => {
|
|
|
+ manifestInboundData.value.cargos.push({ commodityId: null, number: 1, location: [] })
|
|
|
+}
|
|
|
+
|
|
|
+// 删除商品行
|
|
|
+const removeInboundCargo = (index) => {
|
|
|
+ manifestInboundData.value.cargos.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+const submitInboundForm = async() => {
|
|
|
+ await createInboundManifest({
|
|
|
+ ...manifestInboundData.value,
|
|
|
+ cargos: manifestInboundData.value.cargos.map(cargo => ({
|
|
|
+ commodityId: cargo.commodityId,
|
|
|
+ number: cargo.number,
|
|
|
+ warehouseId: cargo.location[0],
|
|
|
+ storageAreaId: cargo.location[1],
|
|
|
+ placeId: cargo.location[2]
|
|
|
+ }))
|
|
|
+ }).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ ElMessage.success('入库单提交成功!')
|
|
|
+ manifestInboundData.value.cargos = [{ commodityId: null, number: 1, location: [] }]
|
|
|
+ }
|
|
|
+ inboundDialog.value = false
|
|
|
+ getData()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 出库
|
|
|
+const manifestOutboundData = ref({
|
|
|
+ manifestGenre: '出库',
|
|
|
+ title: '',
|
|
|
+ custodian: '',
|
|
|
+ cargos: [
|
|
|
+ {
|
|
|
+ commodityId: null,
|
|
|
+ commodityName: '',
|
|
|
+ number: 0,
|
|
|
+ maxNumber: 1, // 当前最大可出库数量
|
|
|
+ location: [] // [warehouseId, storageAreaId, placeId]
|
|
|
+ },
|
|
|
+ ],
|
|
|
+})
|
|
|
+
|
|
|
+const outboundDialog = ref(false)
|
|
|
+
|
|
|
+const cascaderProps = {
|
|
|
+ value: 'value',
|
|
|
+ label: 'label',
|
|
|
+ children: 'children',
|
|
|
+}
|
|
|
+
|
|
|
+// 添加商品行
|
|
|
+const addOutboundCargo = () => {
|
|
|
+ manifestOutboundData.value.cargos.push({
|
|
|
+ commodityId: null,
|
|
|
+ number: 0,
|
|
|
+ maxNumber: 1,
|
|
|
+ location: []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 删除商品行
|
|
|
+const removeOutboundCargo = (index) => {
|
|
|
+ manifestOutboundData.value.cargos.splice(index, 1)
|
|
|
+ console.log(manifestOutboundData.value.cargos)
|
|
|
+}
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+const submitForm = async() => {
|
|
|
+ try {
|
|
|
+ await createOutboundManifest({
|
|
|
+ ...manifestOutboundData.value,
|
|
|
+ cargos: manifestOutboundData.value.cargos.map(cargo => ({
|
|
|
+ commodityId: cargo.commodityId,
|
|
|
+ number: cargo.number,
|
|
|
+ warehouseId: cargo.location[0],
|
|
|
+ storageAreaId: cargo.location[1],
|
|
|
+ placeId: cargo.location[2]
|
|
|
+ }))
|
|
|
+ }).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ ElMessage.success('出库单提交成功!')
|
|
|
+ }
|
|
|
+ outboundDialog.value = false
|
|
|
+ getData()
|
|
|
+ })
|
|
|
+ resetForm()
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(`提交失败: ${error.message}`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 更新商品信息
|
|
|
+const updateCommodityInfo = (row) => {
|
|
|
+ const goods = goodsList.value.find(item => item.commodityId === row.commodityId)
|
|
|
+ if (goods) {
|
|
|
+ row.commodityName = goods.commodity.name
|
|
|
+ row.maxNumber = goods.number
|
|
|
+ row.location = [goods.warehouseId, goods.storageAreaId, goods.placeId]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const isCommoditySelected = (id) => {
|
|
|
+ return manifestOutboundData.value.cargos.some(cargo => cargo.commodityId === id)
|
|
|
+}
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+const resetForm = () => {
|
|
|
+ manifestOutboundData.value.cargos = [{ commodityId: null, number: 1, location: [] }]
|
|
|
+ manifestOutboundData.value.custodian = ''
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getData()
|
|
|
+})
|
|
|
+</script>
|