Commit 51fd2e02 by 杨子

feat: 重构WMS仓库管理系统并添加多个功能组件

refactor(仓库管理): 将"库位"统一改为"货架"并调整相关代码
feat(组件): 新增仓库、货架、客户、供应商等选择对话框组件
feat(退库单): 优化退库单页面布局和交互体验
style(主题): 更新系统主题颜色为军绿色调
docs: 更新系统名称和描述为WMS仓库管理系统
build: 添加vue-office相关依赖以支持文档处理
parent d5d551f4
# 页面标题
VITE_APP_TITLE = 若依管理系统
VITE_APP_TITLE = WMS仓库管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
# 若依管理系统/开发环境
# WMS仓库管理系统/开发环境
VITE_APP_BASE_API = '/dev-api'
# 页面标题
VITE_APP_TITLE = 若依管理系统
VITE_APP_TITLE = WMS仓库管理系统
# 生产环境配置
VITE_APP_ENV = 'production'
# 若依管理系统/生产环境
# WMS仓库管理系统/生产环境
VITE_APP_BASE_API = '/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
......
# 页面标题
VITE_APP_TITLE = 若依管理系统
VITE_APP_TITLE = WMS仓库管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'
# 若依管理系统/生产环境
# WMS仓库管理系统/生产环境
VITE_APP_BASE_API = '/stage-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
......
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html
......@@ -7,7 +7,7 @@
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<title>若依管理系统</title>
<title>WMS仓库管理系统</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,
......
{
"name": "ruoyi",
"version": "3.9.0",
"description": "若依管理系统",
"description": "WMS仓库管理系统",
"author": "若依",
"license": "MIT",
"type": "module",
......@@ -17,6 +17,9 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@vue-office/docx": "^1.6.3",
"@vue-office/excel": "^1.7.14",
"@vue-office/pdf": "^2.0.10",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0",
"axios": "1.9.0",
......@@ -34,6 +37,7 @@
"splitpanes": "4.0.4",
"vue": "3.5.16",
"vue-cropper": "1.1.1",
"vue-demi": "^0.14.10",
"vue-router": "4.5.1",
"vuedraggable": "4.1.0"
},
......
......@@ -11,6 +11,15 @@ importers:
'@element-plus/icons-vue':
specifier: 2.3.1
version: 2.3.1(vue@3.5.16)
'@vue-office/docx':
specifier: ^1.6.3
version: 1.6.3(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)
'@vue-office/excel':
specifier: ^1.7.14
version: 1.7.14(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)
'@vue-office/pdf':
specifier: ^2.0.10
version: 2.0.10(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)
'@vueup/vue-quill':
specifier: 1.2.0
version: 1.2.0(vue@3.5.16)
......@@ -62,6 +71,9 @@ importers:
vue-cropper:
specifier: 1.1.1
version: 1.1.1
vue-demi:
specifier: ^0.14.10
version: 0.14.10(vue@3.5.16)
vue-router:
specifier: 4.5.1
version: 4.5.1(vue@3.5.16)
......@@ -481,6 +493,36 @@ packages:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
'@vue-office/docx@1.6.3':
resolution: {integrity: sha512-Cs+3CAaRBOWOiW4XAhTwwxJ0dy8cPIf6DqfNvYcD3YACiLwO4kuawLF2IAXxyijhbuOeoFsfvoVbOc16A/4bZA==}
peerDependencies:
'@vue/composition-api': ^1.7.1
vue: ^2.0.0 || >=3.0.0
vue-demi: ^0.14.6
peerDependenciesMeta:
'@vue/composition-api':
optional: true
'@vue-office/excel@1.7.14':
resolution: {integrity: sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==}
peerDependencies:
'@vue/composition-api': ^1.7.1
vue: ^2.0.0 || >=3.0.0
vue-demi: ^0.14.6
peerDependenciesMeta:
'@vue/composition-api':
optional: true
'@vue-office/pdf@2.0.10':
resolution: {integrity: sha512-yHVLrMAKpMPBkhBwofFyGEtEeJF0Zd7oGmf56Pe5aj/xObdRq3E1CIZqTqhWJNgHV8oLQqaX0vs4p5T1zq+GIA==}
peerDependencies:
'@vue/composition-api': ^1.7.1
vue: ^2.0.0 || >=3.0.0
vue-demi: ^0.14.6
peerDependenciesMeta:
'@vue/composition-api':
optional: true
'@vue/compiler-core@3.5.16':
resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==}
......@@ -2549,6 +2591,21 @@ snapshots:
vite: 6.3.5(@types/node@25.0.2)(sass-embedded@1.89.1)
vue: 3.5.16
'@vue-office/docx@1.6.3(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)':
dependencies:
vue: 3.5.16
vue-demi: 0.14.10(vue@3.5.16)
'@vue-office/excel@1.7.14(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)':
dependencies:
vue: 3.5.16
vue-demi: 0.14.10(vue@3.5.16)
'@vue-office/pdf@2.0.10(vue-demi@0.14.10(vue@3.5.16))(vue@3.5.16)':
dependencies:
vue: 3.5.16
vue-demi: 0.14.10(vue@3.5.16)
'@vue/compiler-core@3.5.16':
dependencies:
'@babel/parser': 7.28.5
......
import request from '@/utils/request'
// 查询库位信息列表
// 查询货架信息列表
export function listWmsLocation(query) {
return request({
url: '/ware/wmsLocation/list',
......@@ -9,7 +9,7 @@ export function listWmsLocation(query) {
})
}
// 查询库位信息详细
// 查询货架信息详细
export function getWmsLocation(locationId) {
return request({
url: '/ware/wmsLocation/' + locationId,
......@@ -17,7 +17,7 @@ export function getWmsLocation(locationId) {
})
}
// 新增库位信息
// 新增货架信息
export function addWmsLocation(data) {
return request({
url: '/ware/wmsLocation',
......@@ -26,7 +26,7 @@ export function addWmsLocation(data) {
})
}
// 修改库位信息
// 修改货架信息
export function updateWmsLocation(data) {
return request({
url: '/ware/wmsLocation',
......@@ -35,7 +35,7 @@ export function updateWmsLocation(data) {
})
}
// 删除库位信息
// 删除货架信息
export function delWmsLocation(locationId) {
return request({
url: '/ware/wmsLocation/' + locationId,
......
......@@ -8,28 +8,28 @@ $tiffany: #4AB7BD;
$yellow: #FEC171;
$panGreen: #30B08F;
// 默认主题变量
$menuText: #bfcbd9;
$menuActiveText: #409eff;
$menuBg: #304156;
$menuHover: #263445;
// 默认主题变量 - 部队军绿色主题
$menuText: #e6e6e6;
$menuActiveText: #ffffff;
$menuBg: #2b580c;
$menuHover: #6f8a5d;
// 浅色主题theme-light
$menuLightBg: #ffffff;
$menuLightHover: #f0f1f5;
$menuLightText: #303133;
$menuLightActiveText: #409EFF;
$menuLightBg: #2b580c;
$menuLightHover: #6f8a5d;
$menuLightText: #e6e6e6;
$menuLightActiveText: #ffffff;
// 基础变量
$base-sidebar-width: 200px;
$sideBarWidth: 200px;
// 菜单暗色变量
$base-menu-color: #bfcbd9;
$base-menu-color-active: #f4f4f5;
$base-menu-background: #304156;
$base-sub-menu-background: #1f2d3d;
$base-sub-menu-hover: #001528;
$base-menu-color: #e6e6e6;
$base-menu-color-active: #ffffff;
$base-menu-background: #2b580c;
$base-sub-menu-background: #1e3e08;
$base-sub-menu-hover: #152d06;
// 组件变量
$--color-primary: #409EFF;
......
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1200px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="库区编码" prop="areaCode">
<el-input
v-model="queryParams.areaCode"
placeholder="请输入库区编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库区名称" prop="areaName">
<el-input
v-model="queryParams.areaName"
placeholder="请输入库区名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属仓库" prop="warehouseId">
<el-input
v-model="queryParams.warehouseId"
placeholder="请输入所属仓库ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="areaList"
default-expand-all
@selection-change="handleSelectionChange"
row-key="areaId"
:tree-props="{ children: 'children' }"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库区编码" align="center" prop="areaCode" />
<el-table-column label="库区名称" align="center" prop="areaName" />
<el-table-column label="所属仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库区类型" align="center" prop="areaType" />
<el-table-column label="温度范围" align="center" prop="temperatureRange" />
<el-table-column label="湿度范围" align="center" prop="humidityRange" />
<el-table-column label="库区容量" align="center" prop="capacity" />
<el-table-column label="状态" align="center" prop="status" />
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="AreaSelectTreeDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsArea } from "@/api/ware/wmsArea"
import { handleTree } from "@/utils/ruoyi"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择库区'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 库区列表
const areaList = ref([])
// 加载状态
const loading = ref(false)
// 选中的库区
const selectedAreas = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 100,
areaCode: null,
areaName: null,
warehouseId: null
})
// 加载库区列表
const handleQuery = () => {
loading.value = true
listWmsArea(queryParams).then(response => {
// 处理树形结构
areaList.value = handleTree(response.rows, 'areaId', 'parentId')
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.areaCode = null
queryParams.areaName = null
queryParams.warehouseId = null
queryParams.pageNum = 1
handleQuery()
}
// 库区选择变化
const handleSelectionChange = (selection) => {
selectedAreas.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedAreas.value.length === 0) {
return
}
emit('confirm', selectedAreas.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
const exposeMethods = {
handleQuery
}
defineExpose(exposeMethods)
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1000px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="客户编码" prop="customerCode">
<el-input
v-model="queryParams.customerCode"
placeholder="请输入客户编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="客户名称" prop="customerName">
<el-input
v-model="queryParams.customerName"
placeholder="请输入客户名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="联系人" prop="contactPerson">
<el-input
v-model="queryParams.contactPerson"
placeholder="请输入联系人"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="联系电话" prop="contactPhone">
<el-input
v-model="queryParams.contactPhone"
placeholder="请输入联系电话"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="客户编码" align="center" prop="customerCode" />
<el-table-column label="客户名称" align="center" prop="customerName" />
<el-table-column label="联系人" align="center" prop="contactPerson" />
<el-table-column label="联系电话" align="center" prop="contactPhone" />
<el-table-column label="邮箱" align="center" prop="email" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="状态" align="center" prop="status" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="CustomerSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsCustomer } from "@/api/ware/wmsCustomer"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择客户'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 客户列表
const customerList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的客户
const selectedCustomers = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
customerCode: null,
customerName: null,
contactPerson: null,
contactPhone: null
})
// 加载客户列表
const handleQuery = () => {
loading.value = true
listWmsCustomer(queryParams).then(response => {
customerList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.customerCode = null
queryParams.customerName = null
queryParams.contactPerson = null
queryParams.contactPhone = null
queryParams.pageNum = 1
handleQuery()
}
// 客户选择变化
const handleSelectionChange = (selection) => {
selectedCustomers.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedCustomers.value.length === 0) {
return
}
emit('confirm', selectedCustomers.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
defineExpose({
handleQuery
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog
:title="title"
v-model="dialogVisible"
width="1200px"
append-to-body
>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="操作类型" prop="operationType">
<el-input
v-model="queryParams.operationType"
placeholder="请输入操作类型"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="操作时间" prop="operationTime">
<el-date-picker clearable
v-model="queryParams.operationTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择操作时间">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="wmsInventoryFlowList">
<el-table-column label="流水ID" align="center" prop="flowId" />
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="操作类型" align="center" prop="operationType" />
<el-table-column label="变动数量" align="center" prop="changeQuantity" />
<el-table-column label="变动前数量" align="center" prop="beforeQuantity" />
<el-table-column label="变动后数量" align="center" prop="afterQuantity" />
<el-table-column label="操作人" align="center" prop="operator" />
<el-table-column label="操作时间" align="center" prop="operationTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.operationTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
</el-table>
<pagination
v-show="total>0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="InventoryFlowDialog">
import { ref, reactive, watch, toRefs } from 'vue'
import { listWmsInventoryFlow } from '@/api/ware/wmsInventoryFlow'
import { parseTime } from '@/utils/ruoyi'
import Pagination from '@/components/Pagination'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
inventoryId: {
type: String,
default: ''
},
title: {
type: String,
default: '库存履历'
}
})
const emit = defineEmits(['update:visible', 'update:inventoryId'])
const dialogVisible = ref(false)
const loading = ref(true)
const total = ref(0)
const wmsInventoryFlowList = ref([])
// 监听可见性变化
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 查询参数
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
inventoryId: props.inventoryId,
operationType: null,
operationTime: null
}
})
const { queryParams } = toRefs(data)
// 监听inventoryId变化,更新查询参数
watch(() => props.inventoryId, (newVal) => {
queryParams.value.inventoryId = newVal
getList()
})
// 监听查询参数中的inventoryId变化,通知父组件
watch(() => queryParams.value.inventoryId, (newVal) => {
if (newVal !== props.inventoryId) {
emit('update:inventoryId', newVal)
}
})
/** 查询库存流水列表 */
function getList() {
loading.value = true
listWmsInventoryFlow(queryParams.value).then(response => {
wmsInventoryFlowList.value = response.rows
total.value = response.total
loading.value = false
})
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
queryParams.value.operationType = null
queryParams.value.operationTime = null
queryParams.value.pageNum = 1
getList()
}
// 暴露方法供外部调用
defineExpose({
handleQuery
})
</script>
<style scoped>
.mb8 {
margin-bottom: 8px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1200px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="货架编码" prop="locationCode">
<el-input
v-model="queryParams.locationCode"
placeholder="请输入货架编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="货架名称" prop="locationName">
<el-input
v-model="queryParams.locationName"
placeholder="请输入货架名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属库区" prop="areaId">
<el-input
v-model="queryParams.areaId"
placeholder="请输入所属库区ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="X坐标" prop="xCoordinate">
<el-input
v-model="queryParams.xCoordinate"
placeholder="请输入X坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="Y坐标" prop="yCoordinate">
<el-input
v-model="queryParams.yCoordinate"
placeholder="请输入Y坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="locationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="货架编码" align="center" prop="locationCode" />
<el-table-column label="货架名称" align="center" prop="locationName" />
<el-table-column label="所属库区" align="center" prop="areaId" />
<el-table-column label="X坐标" align="center" prop="xCoordinate" />
<el-table-column label="Y坐标" align="center" prop="yCoordinate" />
<el-table-column label="Z坐标" align="center" prop="zCoordinate" />
<el-table-column label="货架状态" align="center" prop="status" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="LocationSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsLocation } from "@/api/ware/wmsLocation"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择货架'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 货架列表
const locationList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的货架
const selectedLocations = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
locationCode: null,
locationName: null,
areaId: null,
xCoordinate: null,
yCoordinate: null,
zCoordinate: null,
status: null
})
// 加载货架列表
const handleQuery = () => {
loading.value = true
listWmsLocation(queryParams).then(response => {
locationList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.locationCode = null
queryParams.locationName = null
queryParams.areaId = null
queryParams.xCoordinate = null
queryParams.yCoordinate = null
queryParams.zCoordinate = null
queryParams.status = null
queryParams.pageNum = 1
handleQuery()
}
// 货架选择变化
const handleSelectionChange = (selection) => {
selectedLocations.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedLocations.value.length === 0) {
return
}
emit('confirm', selectedLocations.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
const exposeMethods = {
handleQuery
}
defineExpose(exposeMethods)
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1200px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="物资编码" prop="materialCode">
<el-input
v-model="queryParams.materialCode"
placeholder="请输入物资编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="物资名称" prop="materialName">
<el-input
v-model="queryParams.materialName"
placeholder="请输入物资名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="物资分类" prop="categoryId">
<el-tree-select
v-model="queryParams.categoryId"
:data="materialCategoryOptions"
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
value-key="categoryId"
placeholder="请选择物资分类"
check-strictly
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="规格型号" prop="specification">
<el-input
v-model="queryParams.specification"
placeholder="请输入规格型号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="计量单位" prop="unit">
<el-input
v-model="queryParams.unit"
placeholder="请输入计量单位"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="materialList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="物资编码" align="center" prop="materialCode" />
<el-table-column label="物资名称" align="center" prop="materialName" />
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="计量单位" align="center" prop="unit" />
<el-table-column label="生产厂家" align="center" prop="manufacturer" />
<el-table-column label="安全库存" align="center" prop="safetyStock" />
<el-table-column label="状态" align="center" prop="status" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="MaterialSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsMaterial } from "@/api/ware/wmsMaterial"
import { listWmsMaterialCategory } from "@/api/ware/wmsMaterialCategory"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择物资'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 物资列表
const materialList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的物资
const selectedMaterials = ref([])
// 物资分类树选项
const materialCategoryOptions = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
materialCode: null,
materialName: null,
categoryId: null,
specification: null,
unit: null
})
// 加载物资列表
const handleQuery = () => {
loading.value = true
listWmsMaterial(queryParams).then(response => {
materialList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.materialCode = null
queryParams.materialName = null
queryParams.categoryId = null
queryParams.specification = null
queryParams.unit = null
queryParams.pageNum = 1
handleQuery()
}
// 物资选择变化
const handleSelectionChange = (selection) => {
selectedMaterials.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedMaterials.value.length === 0) {
return
}
emit('confirm', selectedMaterials.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 查询物资分类下拉树结构
const getTreeselect = () => {
return new Promise((resolve) => {
listWmsMaterialCategory().then(response => {
materialCategoryOptions.value = []
const data = { categoryId: 0, categoryName: '顶级节点', children: [] }
data.children = handleTree(response.data, "categoryId", "parentId")
materialCategoryOptions.value.push(data)
resolve()
})
})
}
// 处理树数据
const handleTree = (data, id, parentId, children) => {
children = children || 'children'
const result = []
const hash = {}
data.forEach((item) => {
hash[item[id]] = item
})
data.forEach((item) => {
const hashParent = hash[item[parentId]]
if (hashParent) {
!hashParent[children] && (hashParent[children] = [])
hashParent[children].push(item)
} else {
result.push(item)
}
})
return result
}
// 暴露方法
const exposeMethods = {
handleQuery,
getTreeselect
}
defineExpose(exposeMethods)
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1000px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="出库单号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
placeholder="请输入出库单号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="客户ID" prop="customerId">
<el-input
v-model="queryParams.customerId"
placeholder="请输入客户ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="单据状态" prop="orderStatus">
<el-input
v-model="queryParams.orderStatus"
placeholder="请输入单据状态"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="outboundOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="出库单号" align="center" prop="orderNo" />
<el-table-column label="原入库单号" align="center" prop="originalOrderNo" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="客户ID" align="center" prop="customerId" />
<el-table-column label="出库类型" align="center" prop="outboundType" />
<el-table-column label="总数量" align="center" prop="totalQuantity" />
<el-table-column label="总金额" align="center" prop="totalAmount" />
<el-table-column label="单据状态" align="center" prop="orderStatus" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="OutboundOrderSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsOutboundOrder } from "@/api/ware/wmsOutboundOrder"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择出库单'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 出库单列表
const outboundOrderList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的出库单
const selectedOutboundOrders = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
orderNo: null,
originalOrderNo: null,
warehouseId: null,
customerId: null,
outboundType: null,
orderStatus: null,
})
// 加载出库单列表
const handleQuery = () => {
loading.value = true
listWmsOutboundOrder(queryParams).then(response => {
outboundOrderList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.orderNo = null
queryParams.customerId = null
queryParams.orderStatus = null
queryParams.pageNum = 1
handleQuery()
}
// 出库单选择变化
const handleSelectionChange = (selection) => {
selectedOutboundOrders.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedOutboundOrders.value.length === 0) {
return
}
emit('confirm', selectedOutboundOrders.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
defineExpose({
handleQuery
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1200px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="100px">
<el-form-item label="供应商编码" prop="supplierCode">
<el-input
v-model="queryParams.supplierCode"
placeholder="请输入供应商编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="供应商名称" prop="supplierName">
<el-input
v-model="queryParams.supplierName"
placeholder="请输入供应商名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="联系人" prop="contactPerson">
<el-input
v-model="queryParams.contactPerson"
placeholder="请输入联系人"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="联系电话" prop="contactPhone">
<el-input
v-model="queryParams.contactPhone"
placeholder="请输入联系电话"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="supplierList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="供应商编码" align="center" prop="supplierCode" />
<el-table-column label="供应商名称" align="center" prop="supplierName" />
<el-table-column label="联系人" align="center" prop="contactPerson" />
<el-table-column label="联系电话" align="center" prop="contactPhone" />
<el-table-column label="邮箱" align="center" prop="email" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="状态" align="center" prop="status" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="SupplierSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsSupplier } from "@/api/ware/wmsSupplier"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择供应商'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 供应商列表
const supplierList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的供应商
const selectedSuppliers = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
supplierCode: null,
supplierName: null,
contactPerson: null,
contactPhone: null
})
// 加载供应商列表
const handleQuery = () => {
loading.value = true
listWmsSupplier(queryParams).then(response => {
supplierList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.supplierCode = null
queryParams.supplierName = null
queryParams.contactPerson = null
queryParams.contactPhone = null
queryParams.pageNum = 1
handleQuery()
}
// 供应商选择变化
const handleSelectionChange = (selection) => {
selectedSuppliers.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedSuppliers.value.length === 0) {
return
}
emit('confirm', selectedSuppliers.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
defineExpose({
handleQuery
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1000px" append-to-body>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="80px">
<el-form-item label="仓库编码" prop="warehouseCode">
<el-input
v-model="queryParams.warehouseCode"
placeholder="请输入仓库编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库名称" prop="warehouseName">
<el-input
v-model="queryParams.warehouseName"
placeholder="请输入仓库名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库类型" prop="warehouseType">
<el-input
v-model="queryParams.warehouseType"
placeholder="请输入仓库类型"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="warehouseList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="仓库编码" align="center" prop="warehouseCode" />
<el-table-column label="仓库名称" align="center" prop="warehouseName" />
<el-table-column label="仓库类型" align="center" prop="warehouseType" />
<el-table-column label="面积" align="center" prop="area" />
<el-table-column label="容量" align="center" prop="capacity" />
<el-table-column label="位置" align="center" prop="location" />
<el-table-column label="状态" align="center" prop="status" />
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleConfirm">确 定</el-button>
<el-button @click="handleCancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="WarehouseSelectDialog">
import { ref, reactive, watch } from 'vue'
import { listWmsWarehouse } from "@/api/ware/wmsWarehouse"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择仓库'
}
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 本地对话框可见性状态
const dialogVisible = ref(props.visible)
// 监听props变化,同步本地状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听本地状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
// 仓库列表
const warehouseList = ref([])
// 加载状态
const loading = ref(false)
// 总条数
const total = ref(0)
// 选中的仓库
const selectedWarehouses = ref([])
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
warehouseCode: null,
warehouseName: null,
warehouseType: null
})
// 加载仓库列表
const handleQuery = () => {
loading.value = true
listWmsWarehouse(queryParams).then(response => {
warehouseList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 重置查询参数
const resetQuery = () => {
queryParams.warehouseCode = null
queryParams.warehouseName = null
queryParams.warehouseType = null
queryParams.pageNum = 1
handleQuery()
}
// 仓库选择变化
const handleSelectionChange = (selection) => {
selectedWarehouses.value = selection
}
// 确认选择
const handleConfirm = () => {
if (selectedWarehouses.value.length === 0) {
return
}
emit('confirm', selectedWarehouses.value)
dialogVisible.value = false
}
// 取消选择
const handleCancel = () => {
emit('cancel')
dialogVisible.value = false
}
// 暴露方法
defineExpose({
handleQuery
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>
\ No newline at end of file
......@@ -14,7 +14,7 @@ const useSettingsStore = defineStore(
{
state: () => ({
title: '',
theme: storageSetting.theme || '#409EFF',
theme: storageSetting.theme || '#6f7a2fff',
sideTheme: storageSetting.sideTheme || sideTheme,
showSettings: showSettings,
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
......
......@@ -4,7 +4,7 @@
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>若依后台管理框架</h2>
<p>
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了WMS仓库管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
</p>
<p>
<b>当前版本:</b> <span>v{{ version }}</span>
......
<template>
<div class="inventory-alert">
<div class="content-wrapper">
<!-- 预警概览 -->
<div class="alert-overview">
<el-card shadow="hover" class="alert-card">
<div class="alert-content">
<div class="alert-info">
<div class="alert-count">{{ lowStockAlertCount }}</div>
<div class="alert-label">低库存预警</div>
</div>
<div class="alert-icon low-stock">
<el-icon-warning />
</div>
</div>
</el-card>
<el-card shadow="hover" class="alert-card">
<div class="alert-content">
<div class="alert-info">
<div class="alert-count">{{ highStockAlertCount }}</div>
<div class="alert-label">高库存预警</div>
</div>
<div class="alert-icon high-stock">
<el-icon-warning />
</div>
</div>
</el-card>
<el-card shadow="hover" class="alert-card">
<div class="alert-content">
<div class="alert-info">
<div class="alert-count">{{ nearExpiryAlertCount }}</div>
<div class="alert-label">即将过期预警</div>
</div>
<div class="alert-icon near-expiry">
<el-icon-clock />
</div>
</div>
</el-card>
<el-card shadow="hover" class="alert-card">
<div class="alert-content">
<div class="alert-info">
<div class="alert-count">{{ lowTurnoverAlertCount }}</div>
<div class="alert-label">低周转率预警</div>
</div>
<div class="alert-icon low-turnover">
<el-icon-data-analysis />
</div>
</div>
</el-card>
<el-card shadow="hover" class="alert-card">
<div class="alert-content">
<div class="alert-info">
<div class="alert-count">{{ totalAlertCount }}</div>
<div class="alert-label">总预警数</div>
</div>
<div class="alert-icon total">
<el-icon-bell />
</div>
</div>
</el-card>
</div>
<!-- 预警图表 -->
<div class="alert-charts">
<el-card shadow="hover" class="chart-card">
<template #header>
<div class="card-header">
<span>预警类型分布</span>
</div>
</template>
<div ref="alertTypeChartRef" class="chart-box"></div>
</el-card>
<el-card shadow="hover" class="chart-card">
<template #header>
<div class="card-header">
<span>预警处理进度</span>
</div>
</template>
<div ref="alertProgressChartRef" class="chart-box"></div>
</el-card>
</div>
<!-- 库存分析报表 -->
<div class="analysis-reports">
<el-tabs v-model="activeReportTab" class="report-tabs" @tab-click="handleTabClick">
<el-tab-pane label="库存结构分析" name="structure">
<div class="report-content">
<div ref="inventoryStructureChartRef" class="chart-box large"></div>
</div>
</el-tab-pane>
<el-tab-pane label="周转率分析" name="turnover">
<div class="report-content">
<div ref="turnoverAnalysisChartRef" class="chart-box large"></div>
</div>
</el-tab-pane>
<el-tab-pane label="库存占用资金分析" name="capital">
<div class="report-content">
<div ref="capitalOccupancyChartRef" class="chart-box large"></div>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 库存优化建议 -->
<div class="optimization-suggestions">
<el-card shadow="hover" class="suggestion-card">
<template #header>
<div class="card-header">
<span>库存优化建议</span>
</div>
</template>
<div class="suggestion-content">
<div v-for="(suggestion, index) in optimizationSuggestions" :key="index" class="suggestion-item">
<div class="suggestion-text">
<strong>{{ suggestion.title }}</strong>
<p>{{ suggestion.content }}</p>
</div>
</div>
</div>
</el-card>
</div>
<!-- 异常库存监控 -->
<div class="abnormal-inventory">
<el-card shadow="hover" class="abnormal-card">
<template #header>
<div class="card-header">
<span>异常库存监控</span>
</div>
</template>
<el-table :data="abnormalInventoryList" style="width: 100%" stripe border>
<el-table-column prop="materialCode" label="物资编码" width="120"></el-table-column>
<el-table-column prop="materialName" label="物资名称" min-width="150"></el-table-column>
<el-table-column prop="spec" label="规格型号" min-width="120"></el-table-column>
<el-table-column prop="abnormalType" label="异常类型" width="120">
<template #default="scope">
<el-tag :type="scope.row.abnormalType === 'long_term_overstock' ? 'danger' : 'warning'">
{{ getAbnormalTypeLabel(scope.row.abnormalType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="currentQuantity" label="当前库存" width="100"></el-table-column>
<el-table-column prop="abnormalDays" label="异常天数" width="100"></el-table-column>
<el-table-column prop="description" label="异常描述" min-width="200"></el-table-column>
</el-table>
</el-card>
</div>
<!-- 预警列表筛选 -->
<div class="alert-filter">
<el-input v-model="searchKeyword" placeholder="搜索物资编码或名称" style="width: 300px; margin-right: 10px;">
<template #prefix>
<el-icon-search />
</template>
</el-input>
<el-select v-model="filterAlertType" placeholder="选择预警类型" style="width: 150px; margin-right: 10px;">
<el-option label="全部" value="" />
<el-option label="低库存" value="low_stock" />
<el-option label="高库存" value="high_stock" />
<el-option label="即将过期" value="near_expiry" />
<el-option label="低周转率" value="low_turnover" />
</el-select>
<el-select v-model="filterAlertStatus" placeholder="选择预警状态" style="width: 150px; margin-right: 10px;">
<el-option label="全部" value="" />
<el-option label="未处理" value="pending" />
<el-option label="已处理" value="processed" />
</el-select>
<el-button type="primary" @click="handleSearch">
<el-icon-search /> 搜索
</el-button>
<el-button @click="resetSearch">
重置
</el-button>
</div>
<!-- 预警列表 -->
<div class="alert-list">
<div class="page-header">
<div class="header-actions">
<el-button type="primary" @click="openAlertRuleDialog">
<el-icon-setting /> 预警规则配置
</el-button>
<el-button type="success" @click="markAllAsProcessed" :disabled="filteredAlertList.length === 0">
<el-icon-check /> 批量标记已处理
</el-button>
</div>
</div>
<el-table :data="paginatedAlertList" style="width: 100%" stripe border
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="alertId" label="预警ID" width="120"></el-table-column>
<el-table-column prop="materialCode" label="物资编码" width="120"></el-table-column>
<el-table-column prop="materialName" label="物资名称" min-width="150"></el-table-column>
<el-table-column prop="spec" label="规格型号" min-width="120"></el-table-column>
<el-table-column prop="alertType" label="预警类型" width="120">
<template #default="scope">
<el-tag
:type="scope.row.alertType === 'low_stock' ? 'danger' : scope.row.alertType === 'high_stock' ? 'warning' : 'info'">
{{ getAlertTypeLabel(scope.row.alertType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="currentQuantity" label="当前数量" width="100"></el-table-column>
<el-table-column prop="threshold" label="预警阈值" width="100"></el-table-column>
<el-table-column prop="alertTime" label="预警时间" width="180">
<template #default="scope">
{{ formatDate(scope.row.alertTime) }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">
{{ scope.row.status === 'pending' ? '未处理' : '已处理' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="warehouseName" label="仓库" width="100"></el-table-column>
<el-table-column prop="location" label="货架" width="100"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="150"></el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="handleAlertProcess(scope.row)">
<el-icon-edit /> 处理
</el-button>
<el-button v-if="scope.row.status === 'pending'" type="success" size="small"
@click="markAsProcessed(scope.row)">
<el-icon-check /> 已处理
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100]"
:total="filteredAlertList.length" layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</div>
<!-- 预警规则配置对话框 -->
<el-dialog title="预警规则配置" v-model="alertRuleDialogVisible" width="600px">
<el-form :model="alertRuleForm" label-width="120px">
<el-form-item label="低库存阈值">
<el-input-number v-model="alertRuleForm.lowStockThreshold" :min="0" :max="1000" style="width: 200px;" />
</el-form-item>
<el-form-item label="高库存阈值">
<el-input-number v-model="alertRuleForm.highStockThreshold" :min="0" :max="10000" style="width: 200px;" />
</el-form-item>
<el-form-item label="过期预警天数">
<el-input-number v-model="alertRuleForm.expiryWarningDays" :min="1" :max="365" style="width: 200px;" />
</el-form-item>
<el-form-item label="预警通知方式">
<el-checkbox-group v-model="alertRuleForm.notificationMethods">
<el-checkbox label="系统消息">系统消息</el-checkbox>
<el-checkbox label="邮件">邮件</el-checkbox>
<el-checkbox label="短信">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="alertRuleDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveAlertRule">保存</el-button>
</span>
</template>
</el-dialog>
<!-- 预警处理对话框 -->
<el-dialog title="预警处理" v-model="alertProcessDialogVisible" width="500px">
<div v-if="selectedAlert" class="alert-process-form">
<div class="process-info">
<div class="info-item">
<label>预警ID:</label>
<span>{{ selectedAlert.alertId }}</span>
</div>
<div class="info-item">
<label>物资编码:</label>
<span>{{ selectedAlert.materialCode }}</span>
</div>
<div class="info-item">
<label>物资名称:</label>
<span>{{ selectedAlert.materialName }}</span>
</div>
<div class="info-item">
<label>预警类型:</label>
<el-tag
:type="selectedAlert.alertType === 'low_stock' ? 'danger' : selectedAlert.alertType === 'high_stock' ? 'warning' : 'info'">
{{ getAlertTypeLabel(selectedAlert.alertType) }}
</el-tag>
</div>
<div class="info-item">
<label>当前数量:</label>
<span>{{ selectedAlert.currentQuantity }}</span>
</div>
<div class="info-item">
<label>预警阈值:</label>
<span>{{ selectedAlert.threshold }}</span>
</div>
</div>
<el-form-item label="处理方式">
<el-radio-group v-model="alertProcessForm.processType">
<el-radio label="采购补货">采购补货</el-radio>
<el-radio label="促销处理">促销处理</el-radio>
<el-radio label="调整库存">调整库存</el-radio>
<el-radio label="忽略">忽略</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="处理备注">
<el-input v-model="alertProcessForm.remark" type="textarea" :rows="4" placeholder="请输入处理备注" />
</el-form-item>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="alertProcessDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveAlertProcess">保存处理</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus'
import * as echarts from 'echarts'
import {
Setting as ElIconSetting,
Check as ElIconCheck,
Warning as ElIconWarning,
Clock as ElIconClock,
Bell as ElIconBell,
Search as ElIconSearch,
Edit as ElIconEdit,
DataAnalysis as ElIconDataAnalysis,
InfoFilled as ElIconInfo
} from '@element-plus/icons-vue'
// 预警类型
interface Alert {
alertId: string
materialCode: string
materialName: string
spec: string
alertType: 'low_stock' | 'high_stock' | 'near_expiry' | 'low_turnover'
currentQuantity: number
threshold: number
alertTime: string
status: 'pending' | 'processed'
warehouseName: string
location: string
remark: string
}
// 库存优化建议
interface OptimizationSuggestion {
title: string
content: string
}
// 异常库存
interface AbnormalInventory {
materialCode: string
materialName: string
spec: string
abnormalType: 'long_term_overstock' | 'frequent_alert'
currentQuantity: number
abnormalDays: number
description: string
}
// 预警列表数据
const alertList = ref<Alert[]>([
{ alertId: 'A001', materialCode: 'M001', materialName: '智能手机', spec: '128GB', alertType: 'low_stock', currentQuantity: 10, threshold: 20, alertTime: '2025-12-10 09:30:00', status: 'pending', warehouseName: '主仓库', location: 'A1-01', remark: '库存低于安全库存' },
{ alertId: 'A002', materialCode: 'M002', materialName: '笔记本电脑', spec: 'i7/16GB/512GB', alertType: 'low_stock', currentQuantity: 5, threshold: 15, alertTime: '2025-12-09 14:20:00', status: 'pending', warehouseName: '主仓库', location: 'A2-01', remark: '库存低于安全库存' },
{ alertId: 'A003', materialCode: 'M004', materialName: '办公桌椅', spec: '木质', alertType: 'high_stock', currentQuantity: 50, threshold: 30, alertTime: '2025-12-08 11:00:00', status: 'processed', warehouseName: '家具库', location: 'C1-01', remark: '库存过高' },
{ alertId: 'A004', materialCode: 'M005', materialName: '打印纸', spec: 'A4', alertType: 'high_stock', currentQuantity: 1000, threshold: 800, alertTime: '2025-12-07 10:00:00', status: 'pending', warehouseName: '耗材库', location: 'D1-01', remark: '库存过高' },
{ alertId: 'A005', materialCode: 'M006', materialName: '电池', spec: 'AA', alertType: 'near_expiry', currentQuantity: 50, threshold: 30, alertTime: '2025-12-06 16:30:00', status: 'pending', warehouseName: '配件库', location: 'B2-01', remark: '即将过期' },
{ alertId: 'A006', materialCode: 'M007', materialName: '鼠标', spec: '无线', alertType: 'low_stock', currentQuantity: 8, threshold: 25, alertTime: '2025-12-05 09:15:00', status: 'processed', warehouseName: '配件库', location: 'B3-01', remark: '库存低于安全库存' },
{ alertId: 'A007', materialCode: 'M008', materialName: '键盘', spec: '机械', alertType: 'low_turnover', currentQuantity: 100, threshold: 0.5, alertTime: '2025-12-10 10:00:00', status: 'pending', warehouseName: '配件库', location: 'B4-01', remark: '周转率低于0.5' },
{ alertId: 'A008', materialCode: 'M009', materialName: '显示器', spec: '27寸', alertType: 'low_turnover', currentQuantity: 30, threshold: 0.5, alertTime: '2025-12-09 15:00:00', status: 'pending', warehouseName: '主仓库', location: 'A3-01', remark: '周转率低于0.5' }
])
// 库存优化建议数据
const optimizationSuggestions = ref<OptimizationSuggestion[]>([
{
title: '采购优化建议',
content: '对于低库存预警的物资(如智能手机、笔记本电脑),建议根据历史销售数据和当前需求,立即采购20-30件,以满足未来1个月的需求。'
},
{
title: '库存清理建议',
content: '对于高库存预警的物资(如办公桌椅、打印纸),建议调整采购计划,减少未来3个月的采购量,并考虑促销活动清理库存。'
},
{
title: '周转率提升建议',
content: '对于低周转率的物资(如键盘、显示器),建议分析销售不畅的原因,考虑调整定价策略或优化产品展示,以提升周转率。'
},
{
title: '近效期处理建议',
content: '对于即将过期的物资(如电池),建议优先使用或考虑退货,避免过期损失。'
}
])
// 异常库存数据
const abnormalInventoryList = ref<AbnormalInventory[]>([
{
materialCode: 'M008',
materialName: '键盘',
spec: '机械',
abnormalType: 'long_term_overstock',
currentQuantity: 100,
abnormalDays: 120,
description: '连续4个月库存周转低于0.5,建议优化采购或促销处理'
},
{
materialCode: 'M009',
materialName: '显示器',
spec: '27寸',
abnormalType: 'long_term_overstock',
currentQuantity: 30,
abnormalDays: 90,
description: '连续3个月库存周转低于0.5,建议优化采购或促销处理'
},
{
materialCode: 'M001',
materialName: '智能手机',
spec: '128GB',
abnormalType: 'frequent_alert',
currentQuantity: 10,
abnormalDays: 15,
description: '连续2周触发低库存预警,建议调整安全库存阈值'
}
])
// 搜索和筛选条件
const searchKeyword = ref('')
const filterAlertType = ref('')
const filterAlertStatus = ref('')
const selectedAlerts = ref<string[]>([])
// 分页
const currentPage = ref(1)
const pageSize = ref(20)
// 预警统计
const lowStockAlertCount = computed(() => {
return alertList.value.filter(alert => alert.alertType === 'low_stock' && alert.status === 'pending').length
})
const highStockAlertCount = computed(() => {
return alertList.value.filter(alert => alert.alertType === 'high_stock' && alert.status === 'pending').length
})
const nearExpiryAlertCount = computed(() => {
return alertList.value.filter(alert => alert.alertType === 'near_expiry' && alert.status === 'pending').length
})
const lowTurnoverAlertCount = computed(() => {
return alertList.value.filter(alert => alert.alertType === 'low_turnover' && alert.status === 'pending').length
})
const totalAlertCount = computed(() => {
return alertList.value.filter(alert => alert.status === 'pending').length
})
// 筛选后的预警列表
const filteredAlertList = computed(() => {
return alertList.value.filter(alert => {
// 搜索关键字筛选
const keywordMatch = !searchKeyword.value ||
alert.materialCode.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
alert.materialName.toLowerCase().includes(searchKeyword.value.toLowerCase())
// 预警类型筛选
const typeMatch = !filterAlertType.value || alert.alertType === filterAlertType.value
// 预警状态筛选
const statusMatch = !filterAlertStatus.value || alert.status === filterAlertStatus.value
return keywordMatch && typeMatch && statusMatch
})
})
// 分页处理
const paginatedAlertList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredAlertList.value.slice(start, end)
})
// 预警规则配置
const alertRuleDialogVisible = ref(false)
const alertRuleForm = ref({
lowStockThreshold: 20,
highStockThreshold: 100,
expiryWarningDays: 30,
notificationMethods: ['系统消息', '邮件']
})
// 预警处理
const alertProcessDialogVisible = ref(false)
const selectedAlert = ref<Alert | null>(null)
const alertProcessForm = ref({
processType: '采购补货',
remark: ''
})
// 标签页状态
const activeReportTab = ref('structure')
// 图表引用
const alertTypeChartRef = ref<HTMLElement | null>(null)
const alertProgressChartRef = ref<HTMLElement | null>(null)
const inventoryStructureChartRef = ref<HTMLElement | null>(null)
const turnoverAnalysisChartRef = ref<HTMLElement | null>(null)
const capitalOccupancyChartRef = ref<HTMLElement | null>(null)
let alertTypeChart: echarts.ECharts | null = null
let alertProgressChart: echarts.ECharts | null = null
let inventoryStructureChart: echarts.ECharts | null = null
let turnoverAnalysisChart: echarts.ECharts | null = null
let capitalOccupancyChart: echarts.ECharts | null = null
// 初始化图表
onMounted(() => {
initCharts()
// 窗口大小变化时重新调整图表大小
window.addEventListener('resize', handleResize)
})
// 初始化图表
const initCharts = () => {
// 预警类型分布图表
if (alertTypeChartRef.value) {
alertTypeChart = echarts.init(alertTypeChartRef.value)
const alertTypeOption = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: ['低库存', '高库存', '即将过期', '低周转率']
},
series: [
{
name: '预警类型',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: lowStockAlertCount.value, name: '低库存', itemStyle: { color: '#F56C6C' } },
{ value: highStockAlertCount.value, name: '高库存', itemStyle: { color: '#E6A23C' } },
{ value: nearExpiryAlertCount.value, name: '即将过期', itemStyle: { color: '#409EFF' } },
{ value: lowTurnoverAlertCount.value, name: '低周转率', itemStyle: { color: '#909399' } }
]
}
]
}
alertTypeChart.setOption(alertTypeOption)
}
// 预警处理进度图表
if (alertProgressChartRef.value) {
alertProgressChart = echarts.init(alertProgressChartRef.value)
const processedCount = alertList.value.filter(alert => alert.status === 'processed').length
const pendingCount = alertList.value.filter(alert => alert.status === 'pending').length
const alertProgressOption = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: ['已处理', '未处理']
},
series: [
{
name: '处理进度',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: processedCount, name: '已处理', itemStyle: { color: '#67C23A' } },
{ value: pendingCount, name: '未处理', itemStyle: { color: '#E6A23C' } }
]
}
]
}
alertProgressChart.setOption(alertProgressOption)
}
// 库存结构分析图表
if (inventoryStructureChartRef.value) {
inventoryStructureChart = echarts.init(inventoryStructureChartRef.value)
const inventoryStructureOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
legend: {
data: ['库存数量', '库存金额']
},
xAxis: {
type: 'category',
data: ['电子设备', '办公用品', '家具', '耗材', '配件']
},
yAxis: [
{
type: 'value',
name: '库存数量',
axisLabel: {
formatter: '{value} 件'
}
},
{
type: 'value',
name: '库存金额',
axisLabel: {
formatter: '{value} 元'
}
}
],
series: [
{
name: '库存数量',
type: 'bar',
data: [150, 800, 50, 1200, 200],
barMaxWidth: 20
},
{
name: '库存金额',
type: 'line',
yAxisIndex: 1,
data: [150000, 40000, 25000, 60000, 30000],
barMaxWidth: 20
}
]
}
inventoryStructureChart.setOption(inventoryStructureOption)
}
// 周转率分析图表
if (turnoverAnalysisChartRef.value) {
turnoverAnalysisChart = echarts.init(turnoverAnalysisChartRef.value)
const turnoverAnalysisOption = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['周转率']
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
},
yAxis: {
type: 'value',
name: '周转率',
axisLabel: {
formatter: '{value}'
}
},
series: [
{
name: '周转率',
type: 'line',
data: [0.8, 0.9, 1.2, 1.0, 0.7, 0.6, 0.8, 1.1, 1.3, 1.0, 0.7, 0.5]
}
]
}
turnoverAnalysisChart.setOption(turnoverAnalysisOption)
}
// 库存占用资金分析图表
if (capitalOccupancyChartRef.value) {
capitalOccupancyChart = echarts.init(capitalOccupancyChartRef.value)
const capitalOccupancyOption = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: ['电子设备', '办公用品', '家具', '耗材', '配件']
},
series: [
{
name: '库存占用资金',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 150000, name: '电子设备', itemStyle: { color: '#F56C6C' } },
{ value: 40000, name: '办公用品', itemStyle: { color: '#E6A23C' } },
{ value: 25000, name: '家具', itemStyle: { color: '#409EFF' } },
{ value: 60000, name: '耗材', itemStyle: { color: '#67C23A' } },
{ value: 30000, name: '配件', itemStyle: { color: '#909399' } }
]
}
]
}
capitalOccupancyChart.setOption(capitalOccupancyOption)
}
}
// 窗口大小变化时重新调整图表大小
const handleResize = () => {
alertTypeChart?.resize()
alertProgressChart?.resize()
inventoryStructureChart?.resize()
turnoverAnalysisChart?.resize()
capitalOccupancyChart?.resize()
}
// 处理选择
const handleSelectionChange = (selection: any[]) => {
selectedAlerts.value = selection.map(item => item.alertId)
}
// 打开预警规则配置对话框
const openAlertRuleDialog = () => {
alertRuleDialogVisible.value = true
}
// 保存预警规则
const saveAlertRule = () => {
// 实际项目中这里会调用API保存预警规则
ElMessage.success('预警规则保存成功')
alertRuleDialogVisible.value = false
}
// 打开预警处理对话框
const handleAlertProcess = (alert: Alert) => {
selectedAlert.value = alert
alertProcessForm.value = {
processType: '采购补货',
remark: ''
}
alertProcessDialogVisible.value = true
}
// 保存预警处理
const saveAlertProcess = () => {
if (!selectedAlert.value) return
// 实际项目中这里会调用API保存预警处理
const alertIndex = alertList.value.findIndex(alert => alert.alertId === selectedAlert.value?.alertId)
if (alertIndex !== -1) {
alertList.value[alertIndex].status = 'processed'
alertList.value[alertIndex].remark = alertProcessForm.value.remark
}
ElMessage.success('预警处理成功')
alertProcessDialogVisible.value = false
selectedAlert.value = null
// 更新图表
initCharts()
}
// 标记为已处理
const markAsProcessed = (alert: Alert) => {
// 实际项目中这里会调用API更新预警状态
const alertIndex = alertList.value.findIndex(item => item.alertId === alert.alertId)
if (alertIndex !== -1) {
alertList.value[alertIndex].status = 'processed'
}
ElMessage.success('已标记为已处理')
// 更新图表
initCharts()
}
// 批量标记为已处理
const markAllAsProcessed = () => {
// 实际项目中这里会调用API批量更新预警状态
alertList.value.forEach(alert => {
if (selectedAlerts.value.includes(alert.alertId)) {
alert.status = 'processed'
}
})
ElMessage.success('批量标记已处理成功')
selectedAlerts.value = []
// 更新图表
initCharts()
}
// 搜索
const handleSearch = () => {
currentPage.value = 1
}
// 重置搜索
const resetSearch = () => {
searchKeyword.value = ''
filterAlertType.value = ''
filterAlertStatus.value = ''
currentPage.value = 1
}
// 获取预警类型标签
const getAlertTypeLabel = (alertType: string) => {
switch (alertType) {
case 'low_stock':
return '低库存'
case 'high_stock':
return '高库存'
case 'near_expiry':
return '即将过期'
case 'low_turnover':
return '低周转率'
default:
return alertType
}
}
// 获取异常类型标签
const getAbnormalTypeLabel = (abnormalType: string) => {
switch (abnormalType) {
case 'long_term_overstock':
return '长期积压'
case 'frequent_alert':
return '频繁预警'
default:
return abnormalType
}
}
// 处理标签页切换
const handleTabClick = () => {
// 延迟执行,确保DOM已更新
setTimeout(() => {
// 根据当前激活的标签页,重新渲染对应图表
switch (activeReportTab.value) {
case 'structure':
if (inventoryStructureChart) {
inventoryStructureChart.resize()
}
break
case 'turnover':
if (turnoverAnalysisChart) {
turnoverAnalysisChart.resize()
}
break
case 'capital':
if (capitalOccupancyChart) {
capitalOccupancyChart.resize()
}
break
default:
break
}
}, 100)
}
// 格式化日期
const formatDate = (dateString: string) => {
const date = new Date(dateString)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
</script>
<style scoped>
.inventory-alert {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
font-size: 20px;
font-weight: 600;
}
.header-actions {
display: flex;
gap: 10px;
}
.content-wrapper {
background-color: #fff;
border-radius: 4px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.alert-overview {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.alert-card {
flex: 1;
min-width: 200px;
}
.alert-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.alert-info {
flex: 1;
}
.alert-count {
font-size: 32px;
font-weight: 600;
color: #303133;
}
.alert-label {
font-size: 14px;
color: #606266;
margin-top: 4px;
}
.alert-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
color: #fff;
}
.alert-icon.low-stock {
background-color: #F56C6C;
}
.alert-icon.high-stock {
background-color: #E6A23C;
}
.alert-icon.near-expiry {
background-color: #409EFF;
}
.alert-icon.low-turnover {
background-color: #909399;
}
.alert-icon.total {
background-color: #67C23A;
}
.alert-charts {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.chart-card {
flex: 1;
min-width: 350px;
}
.chart-box {
width: 100%;
height: 300px;
}
.chart-box.large {
height: 400px;
}
.alert-filter {
display: flex;
align-items: center;
margin-bottom: 20px;
gap: 10px;
flex-wrap: wrap;
}
.alert-list {
margin-bottom: 20px;
}
.pagination {
text-align: right;
margin-top: 20px;
}
.alert-process-form {
padding: 10px 0;
}
.process-info {
margin-bottom: 20px;
}
.info-item {
display: flex;
margin-bottom: 10px;
align-items: center;
}
.info-item label {
width: 80px;
font-weight: 500;
margin-right: 10px;
}
.info-item span {
flex: 1;
}
/* 库存分析报表样式 */
.analysis-reports {
margin-bottom: 20px;
}
.report-tabs {
margin-bottom: 20px;
}
.report-content {
padding: 20px;
background-color: #f5f7fa;
border-radius: 4px;
}
/* 库存优化建议样式 */
.optimization-suggestions {
margin-bottom: 20px;
}
.suggestion-card {
margin-bottom: 20px;
}
.suggestion-content {
padding: 10px 0;
}
.suggestion-item {
display: flex;
align-items: flex-start;
margin-bottom: 15px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
.suggestion-icon {
color: #409EFF;
font-size: 20px;
margin-right: 15px;
margin-top: 2px;
}
.suggestion-text {
flex: 1;
}
.suggestion-text strong {
display: block;
margin-bottom: 5px;
color: #303133;
}
.suggestion-text p {
margin: 0;
color: #606266;
line-height: 1.5;
}
/* 异常库存监控样式 */
.abnormal-inventory {
margin-bottom: 20px;
}
.abnormal-card {
margin-bottom: 20px;
}
</style>
\ No newline at end of file
<template>
<div class="reports-view">
<div class="view-header">
<div class="header-actions">
<el-button type="primary" @click="exportReport">
<el-icon><Download /></el-icon>
导出报表
</el-button>
<el-button @click="refreshData">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
</div>
<div class="view-content">
<!-- 过滤条件 -->
<el-card shadow="hover" class="filter-card">
<el-form :model="filterForm" :inline="true" label-width="100px">
<el-form-item label="报表类型">
<el-select v-model="filterForm.reportType" placeholder="请选择报表类型" clearable filterable style="width: 120px;">
<el-option label="出入库汇总" value="summary"></el-option>
<el-option label="周转率分析" value="turnover"></el-option>
<el-option label="效率分析" value="efficiency"></el-option>
<el-option label="异常统计" value="exception"></el-option>
</el-select>
</el-form-item>
<el-form-item label="时间范围">
<el-date-picker
v-model="filterForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
class="date-picker"
></el-date-picker>
</el-form-item>
<el-form-item label="物资类别">
<el-select v-model="filterForm.materialCategory" placeholder="请选择物资类别" clearable filterable style="width: 150px;">
<el-option label="钢材" value="steel"></el-option>
<el-option label="铝材" value="aluminum"></el-option>
<el-option label="五金配件" value="hardware"></el-option>
<el-option label="电子元件" value="electronics"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
</el-form-item>
<el-form-item label="操作类型">
<el-select v-model="filterForm.operationType" placeholder="请选择操作类型" clearable filterable style="width: 150px;">
<el-option label="入库" value="inbound"></el-option>
<el-option label="出库" value="outbound"></el-option>
<el-option label="全部" value="all"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchReport">
<el-icon><Search /></el-icon>
查询
</el-button>
<el-button @click="resetFilter">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 报表内容 -->
<div class="report-content">
<!-- 统计卡片 -->
<div class="stats-cards">
<el-card shadow="hover" class="stat-card">
<el-statistic title="总出入库量" :value="statistics.totalQuantity" suffix="件"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="入库量" :value="statistics.inboundQuantity" suffix="件" :value-style="{ color: '#67C23A' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="出库量" :value="statistics.outboundQuantity" suffix="件" :value-style="{ color: '#F56C6C' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="周转率" :value="statistics.turnoverRate" suffix="%" :value-style="{ color: '#E6A23C' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="平均库存" :value="statistics.averageInventory" suffix="件" :value-style="{ color: '#409EFF' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="库存周转天数" :value="statistics.turnoverDays" suffix="天" :value-style="{ color: '#909399' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="作业效率" :value="statistics.operationEfficiency" suffix="件/小时" :value-style="{ color: '#67C23A' }"></el-statistic>
</el-card>
<el-card shadow="hover" class="stat-card">
<el-statistic title="异常率" :value="statistics.exceptionRate" suffix="%" :value-style="{ color: '#F56C6C' }"></el-statistic>
</el-card>
</div>
<!-- 图表区域 -->
<div class="charts-section">
<!-- 趋势图 -->
<el-card shadow="hover" class="chart-card">
<template #header>
<div class="card-header">
<span>出入库趋势分析</span>
<el-select v-model="trendChartType" placeholder="选择图表类型" class="chart-type-select">
<el-option label="折线图" value="line"></el-option>
<el-option label="柱状图" value="bar"></el-option>
</el-select>
</div>
</template>
<div ref="trendChartRef" class="chart-container"></div>
</el-card>
<!-- 饼图 -->
<el-card shadow="hover" class="chart-card">
<template #header>
<div class="card-header">
<span>物资类别分布</span>
</div>
</template>
<div ref="distributionChartRef" class="chart-container"></div>
</el-card>
<!-- 周转率分析图 -->
<el-card shadow="hover" class="chart-card full-width">
<template #header>
<div class="card-header">
<span>周转率与效率分析</span>
</div>
</template>
<div ref="turnoverChartRef" class="chart-container"></div>
</el-card>
</div>
<!-- 详细报表数据 -->
<el-card shadow="hover" class="data-card">
<template #header>
<div class="card-header">
<span>{{ getReportTitle(filterForm.reportType) }} - 详细数据</span>
<el-button size="small" @click="expandAllRows">
<el-icon><Expand /></el-icon>
展开全部
</el-button>
</div>
</template>
<el-table :data="reportData" style="width: 100%" v-loading="loading">
<el-table-column prop="date" label="日期" width="150"></el-table-column>
<el-table-column prop="materialCategory" label="物资类别" width="120">
<template #default="scope">
<el-tag :type="getMaterialCategoryType(scope.row.materialCategory)">
{{ getMaterialCategoryName(scope.row.materialCategory) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="inboundQuantity" label="入库量" width="120" align="right"></el-table-column>
<el-table-column prop="outboundQuantity" label="出库量" width="120" align="right"></el-table-column>
<el-table-column prop="totalQuantity" label="总数量" width="120" align="right"></el-table-column>
<el-table-column prop="turnoverRate" label="周转率(%)" width="150" align="right">
<template #default="scope">
<el-progress :percentage="scope.row.turnoverRate" :color="scope.row.turnoverRate > 50 ? '#67C23A' : '#E6A23C'" :stroke-width="8" size="small"></el-progress>
</template>
</el-table-column>
<el-table-column prop="efficiency" label="效率(件/小时)" width="150" align="right"></el-table-column>
<el-table-column prop="exceptionCount" label="异常次数" width="120" align="right">
<template #default="scope">
<el-tag :type="scope.row.exceptionCount > 0 ? 'danger' : 'success'">
{{ scope.row.exceptionCount }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="200"></el-table-column>
</el-table>
<div class="table-pagination">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pagination.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="reportData.length"
></el-pagination>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch } from 'vue'
import { Download, Refresh, Search, Expand } from '@element-plus/icons-vue'
import { ElMessage as elMessage } from 'element-plus'
import * as echarts from 'echarts'
// 过滤条件
const filterForm = reactive({
reportType: 'summary',
dateRange: null,
materialCategory: '',
operationType: ''
})
// 加载状态
const loading = ref(false)
// 分页参数
const pagination = reactive({
currentPage: 1,
pageSize: 10
})
// 图表引用
const trendChartRef = ref<HTMLElement | null>(null)
const distributionChartRef = ref<HTMLElement | null>(null)
const turnoverChartRef = ref<HTMLElement | null>(null)
// 图表实例
let trendChart: echarts.ECharts | null = null
let distributionChart: echarts.ECharts | null = null
let turnoverChart: echarts.ECharts | null = null
// 图表类型
const trendChartType = ref('line')
// 统计数据
const statistics = reactive({
totalQuantity: 2568,
inboundQuantity: 1324,
outboundQuantity: 1244,
turnoverRate: 48.5,
averageInventory: 5200,
turnoverDays: 18.5,
operationEfficiency: 35.8,
exceptionRate: 1.2
})
// 报表数据
const reportData = ref([
{
date: '2024-12-01',
materialCategory: 'steel',
inboundQuantity: 56,
outboundQuantity: 48,
totalQuantity: 104,
turnoverRate: 46.2,
efficiency: 24.5,
exceptionCount: 2,
remark: '正常'
},
{
date: '2024-12-02',
materialCategory: 'aluminum',
inboundQuantity: 34,
outboundQuantity: 42,
totalQuantity: 76,
turnoverRate: 55.3,
efficiency: 28.3,
exceptionCount: 0,
remark: '正常'
},
{
date: '2024-12-03',
materialCategory: 'hardware',
inboundQuantity: 89,
outboundQuantity: 76,
totalQuantity: 165,
turnoverRate: 46.1,
efficiency: 26.8,
exceptionCount: 1,
remark: '少量异常'
},
{
date: '2024-12-04',
materialCategory: 'electronics',
inboundQuantity: 123,
outboundQuantity: 118,
totalQuantity: 241,
turnoverRate: 48.9,
efficiency: 31.2,
exceptionCount: 3,
remark: '部分物资质量问题'
},
{
date: '2024-12-05',
materialCategory: 'other',
inboundQuantity: 45,
outboundQuantity: 51,
totalQuantity: 96,
turnoverRate: 53.1,
efficiency: 22.7,
exceptionCount: 0,
remark: '正常'
},
{
date: '2024-12-06',
materialCategory: 'steel',
inboundQuantity: 78,
outboundQuantity: 82,
totalQuantity: 160,
turnoverRate: 51.2,
efficiency: 25.9,
exceptionCount: 1,
remark: '正常'
},
{
date: '2024-12-07',
materialCategory: 'aluminum',
inboundQuantity: 67,
outboundQuantity: 59,
totalQuantity: 126,
turnoverRate: 46.8,
efficiency: 23.4,
exceptionCount: 0,
remark: '正常'
},
{
date: '2024-12-08',
materialCategory: 'hardware',
inboundQuantity: 102,
outboundQuantity: 95,
totalQuantity: 197,
turnoverRate: 48.2,
efficiency: 27.6,
exceptionCount: 2,
remark: '标签问题'
},
{
date: '2024-12-09',
materialCategory: 'electronics',
inboundQuantity: 145,
outboundQuantity: 138,
totalQuantity: 283,
turnoverRate: 48.8,
efficiency: 32.1,
exceptionCount: 1,
remark: '正常'
},
{
date: '2024-12-10',
materialCategory: 'other',
inboundQuantity: 56,
outboundQuantity: 62,
totalQuantity: 118,
turnoverRate: 52.5,
efficiency: 24.3,
exceptionCount: 0,
remark: '正常'
}
])
// 导出报表
const exportReport = () => {
elMessage.success('报表导出成功')
}
// 刷新数据
const refreshData = () => {
loading.value = true
setTimeout(() => {
loading.value = false
elMessage.success('数据刷新成功')
initCharts()
}, 1000)
}
// 查询报表
const searchReport = () => {
loading.value = true
setTimeout(() => {
loading.value = false
elMessage.success('报表查询成功')
initCharts()
}, 1000)
}
// 重置过滤条件
const resetFilter = () => {
Object.assign(filterForm, {
reportType: 'summary',
dateRange: null,
materialCategory: '',
operationType: ''
})
searchReport()
}
// 展开全部行
const expandAllRows = () => {
elMessage.info('已展开全部行')
}
// 获取报表标题
const getReportTitle = (reportType: string) => {
const titleMap: Record<string, string> = {
'summary': '出入库汇总报表',
'turnover': '周转率分析报表',
'efficiency': '效率分析报表',
'exception': '异常统计报表'
}
return titleMap[reportType] || '报表'
}
// 获取物资类别名称
const getMaterialCategoryName = (category: string) => {
const categoryMap: Record<string, string> = {
'steel': '钢材',
'aluminum': '铝材',
'hardware': '五金配件',
'electronics': '电子元件',
'other': '其他'
}
return categoryMap[category] || category
}
// 获取物资类别类型
const getMaterialCategoryType = (category: string) => {
const typeMap: Record<string, string> = {
'steel': 'success',
'aluminum': 'info',
'hardware': 'warning',
'electronics': 'primary',
'other': 'danger'
}
return typeMap[category] || 'info'
}
// 分页处理
const handleSizeChange = (size: number) => {
pagination.pageSize = size
}
const handleCurrentChange = (current: number) => {
pagination.currentPage = current
}
// 初始化图表
const initCharts = () => {
// 销毁原有图表
if (trendChart) {
trendChart.dispose()
}
if (distributionChart) {
distributionChart.dispose()
}
if (turnoverChart) {
turnoverChart.dispose()
}
// 初始化趋势图
if (trendChartRef.value) {
trendChart = echarts.init(trendChartRef.value)
const trendOption = {
title: {
text: '出入库趋势分析',
textStyle: {
fontSize: 16,
fontWeight: 'normal'
},
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['入库量', '出库量'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: reportData.value.map(item => item.date)
},
yAxis: {
type: 'value'
},
series: [
{
name: '入库量',
type: trendChartType.value,
data: reportData.value.map(item => item.inboundQuantity),
itemStyle: {
color: '#67C23A'
}
},
{
name: '出库量',
type: trendChartType.value,
data: reportData.value.map(item => item.outboundQuantity),
itemStyle: {
color: '#F56C6C'
}
}
]
}
trendChart.setOption(trendOption)
}
// 初始化分布饼图
if (distributionChartRef.value) {
distributionChart = echarts.init(distributionChartRef.value)
// 统计物资类别分布
const categoryStats = {
steel: 0,
aluminum: 0,
hardware: 0,
electronics: 0,
other: 0
}
reportData.value.forEach(item => {
categoryStats[item.materialCategory as keyof typeof categoryStats] += item.totalQuantity
})
const distributionOption = {
title: {
text: '物资类别分布',
textStyle: {
fontSize: 16,
fontWeight: 'normal'
},
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '物资类别',
type: 'pie',
radius: '50%',
data: [
{ value: categoryStats.steel, name: '钢材' },
{ value: categoryStats.aluminum, name: '铝材' },
{ value: categoryStats.hardware, name: '五金配件' },
{ value: categoryStats.electronics, name: '电子元件' },
{ value: categoryStats.other, name: '其他' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 2
},
label: {
formatter: '{b}: {c} ({d}%)'
}
}
]
}
distributionChart.setOption(distributionOption)
}
// 初始化周转率分析图
if (turnoverChartRef.value) {
turnoverChart = echarts.init(turnoverChartRef.value)
const turnoverOption = {
title: {
text: '周转率与效率分析',
textStyle: {
fontSize: 16,
fontWeight: 'normal'
},
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['周转率', '作业效率', '异常率'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
data: reportData.value.map(item => item.date)
},
yAxis: [
{
type: 'value',
name: '周转率(%)',
position: 'left',
axisLabel: {
formatter: '{value}%'
}
},
{
type: 'value',
name: '作业效率(件/小时)',
position: 'right'
}
],
series: [
{
name: '周转率',
type: 'line',
data: reportData.value.map(item => item.turnoverRate),
itemStyle: {
color: '#E6A23C'
},
yAxisIndex: 0
},
{
name: '作业效率',
type: 'line',
data: reportData.value.map(item => item.efficiency),
itemStyle: {
color: '#67C23A'
},
yAxisIndex: 1
},
{
name: '异常率',
type: 'line',
data: reportData.value.map(item => item.exceptionCount),
itemStyle: {
color: '#F56C6C'
},
yAxisIndex: 0
}
]
}
turnoverChart.setOption(turnoverOption)
}
}
// 监听图表类型变化
watch(trendChartType, () => {
if (trendChart) {
initCharts()
}
})
// 窗口大小变化时重新调整图表大小
window.addEventListener('resize', () => {
if (trendChart) {
trendChart.resize()
}
if (distributionChart) {
distributionChart.resize()
}
if (turnoverChart) {
turnoverChart.resize()
}
})
// 组件挂载时初始化图表
onMounted(() => {
setTimeout(() => {
initCharts()
}, 100)
})
</script>
<style scoped>
.reports-view{
padding: 20px;
}
.view-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.view-header h3 {
margin: 0;
font-size: 18px;
color: var(--text-color);
}
.header-actions {
display: flex;
gap: 10px;
}
.filter-card {
margin-bottom: 20px;
}
.date-picker {
width: 300px;
}
.stats-cards {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.stat-card {
flex: 1;
min-width: 200px;
}
.charts-section {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.chart-card {
flex: 1;
min-width: 400px;
}
.chart-card.full-width {
flex: 0 0 100%;
min-width: 100%;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.chart-type-select {
width: 120px;
}
.chart-container {
height: 350px;
}
.data-card {
margin-bottom: 20px;
}
.table-pagination {
margin-top: 15px;
display: flex;
justify-content: flex-end;
}
</style>
\ No newline at end of file
......@@ -25,10 +25,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -136,7 +136,7 @@
<el-table-column label="预警类型" align="center" prop="alertType" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="当前值" align="center" prop="currentValue" />
<el-table-column label="阈值" align="center" prop="thresholdValue" />
<el-table-column label="预警级别" align="center" prop="alertLevel" />
......@@ -182,8 +182,8 @@
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="当前值" prop="currentValue">
<el-input v-model="form.currentValue" placeholder="请输入当前值" />
......
......@@ -17,7 +17,7 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属仓库ID" prop="warehouseId">
<el-form-item label="所属仓库" prop="warehouseId">
<el-input
v-model="queryParams.warehouseId"
placeholder="请输入所属仓库ID"
......@@ -25,46 +25,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="父库区ID" prop="parentId">
<el-input
v-model="queryParams.parentId"
placeholder="请输入父库区ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="温度范围" prop="temperatureRange">
<el-input
v-model="queryParams.temperatureRange"
placeholder="请输入温度范围"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="湿度范围" prop="humidityRange">
<el-input
v-model="queryParams.humidityRange"
placeholder="请输入湿度范围"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库区容量" prop="capacity">
<el-input
v-model="queryParams.capacity"
placeholder="请输入库区容量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="显示顺序" prop="sortOrder">
<el-input
v-model="queryParams.sortOrder"
placeholder="请输入显示顺序"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
......@@ -113,35 +73,37 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="wmsAreaList" @selection-change="handleSelectionChange">
<el-table
v-loading="loading"
:data="wmsAreaList"
default-expand-all
@selection-change="handleSelectionChange"
row-key="areaId"
:tree-props="{ children: 'children' }"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库区ID" align="center" prop="areaId" />
<el-table-column label="库区编码" align="center" prop="areaCode" />
<el-table-column label="库区名称" align="center" prop="areaName" />
<el-table-column label="所属仓库ID" align="center" prop="warehouseId" />
<el-table-column label="父库区ID" align="center" prop="parentId" />
<el-table-column label="库区类型" align="center" prop="areaType" />
<el-table-column label="温度范围" align="center" prop="temperatureRange" />
<el-table-column label="湿度范围" align="center" prop="humidityRange" />
<el-table-column label="库区容量" align="center" prop="capacity" />
<el-table-column label="显示顺序" align="center" prop="sortOrder" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="排序" align="center" prop="sortOrder" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsArea:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsArea:remove']">删除</el-button>
<el-button link type="primary" icon="Plus" @click="handleAddSub(scope.row)" v-hasPermi="['ware:wmsArea:add']">添加下级</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改库区信息对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsAreaRef" :model="form" :rules="rules" label-width="80px">
......@@ -151,11 +113,24 @@
<el-form-item label="库区名称" prop="areaName">
<el-input v-model="form.areaName" placeholder="请输入库区名称" />
</el-form-item>
<el-form-item label="所属仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入所属仓库ID" />
<el-form-item label="所属仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="父库区ID" prop="parentId">
<el-input v-model="form.parentId" placeholder="请输入父库区ID" />
<el-form-item label="父库区" prop="parentId">
<el-cascader
v-model="form.parentId"
:options="wmsAreaOptions"
:props="{ value: 'areaId', label: 'areaName', checkStrictly: true }"
placeholder="请选择父库区"
clearable
/>
</el-form-item>
<el-form-item label="温度范围" prop="temperatureRange">
<el-input v-model="form.temperatureRange" placeholder="请输入温度范围" />
......@@ -166,7 +141,7 @@
<el-form-item label="库区容量" prop="capacity">
<el-input v-model="form.capacity" placeholder="请输入库区容量" />
</el-form-item>
<el-form-item label="显示顺序" prop="sortOrder">
<el-form-item label="序" prop="sortOrder">
<el-input v-model="form.sortOrder" placeholder="请输入显示顺序" />
</el-form-item>
</el-form>
......@@ -177,14 +152,26 @@
</div>
</template>
</el-dialog>
<!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
</div>
</template>
<script setup name="WmsArea">
import { listWmsArea, getWmsArea, delWmsArea, addWmsArea, updateWmsArea } from "@/api/ware/wmsArea"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
import { handleTree } from "@/utils/ruoyi"
const { proxy } = getCurrentInstance()
const {sys_normal_disable} = proxy.useDict('sys_normal_disable')
const wmsAreaList = ref([])
const open = ref(false)
const loading = ref(true)
......@@ -195,11 +182,19 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 仓库选择对话框状态
const warehouseDialogVisible = ref(false)
// 仓库选择对话框实例引用
const warehouseSelectDialogRef = ref(null)
// 库区下拉树选项
const wmsAreaOptions = ref([])
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
pageSize: 100,
areaCode: null,
areaName: null,
warehouseId: null,
......@@ -219,7 +214,7 @@ const data = reactive({
{ required: true, message: "库区名称不能为空", trigger: "blur" }
],
warehouseId: [
{ required: true, message: "所属仓库ID不能为空", trigger: "blur" }
{ required: true, message: "所属仓库ID不能为空", trigger: "change" }
],
}
})
......@@ -230,7 +225,11 @@ const { queryParams, form, rules } = toRefs(data)
function getList() {
loading.value = true
listWmsArea(queryParams.value).then(response => {
wmsAreaList.value = response.rows
// 将扁平数据转换为树形结构
wmsAreaList.value = handleTree(response.rows, 'areaId', 'parentId')
wmsAreaOptions.value = [
{ areaId: 0, areaName: '顶级节点', children: wmsAreaList.value }
]
total.value = response.total
loading.value = false
})
......@@ -290,8 +289,17 @@ function handleAdd() {
title.value = "添加库区信息"
}
/** 添加下级按钮操作 */
function handleAddSub(row) {
reset()
// 设置父库区ID
form.value.parentId = row.areaId
open.value = true
title.value = "添加下级库区"
}
/** 修改按钮操作 */
function handleUpdate(row) {
async function handleUpdate(row) {
reset()
const _areaId = row.areaId || ids.value
getWmsArea(_areaId).then(response => {
......@@ -340,5 +348,22 @@ function handleExport() {
}, `wmsArea_${new Date().getTime()}.xlsx`)
}
/** 打开仓库选择对话框 */
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
/** 处理仓库选择结果 */
function handleWarehouseConfirm(selectedWarehouses) {
if (selectedWarehouses.length > 0) {
const warehouse = selectedWarehouses[0]
form.value.warehouseId = warehouse.warehouseId
// 可以根据需要添加更多仓库信息字段
}
}
getList()
</script>
</script>
\ No newline at end of file
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="借调单号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
......@@ -123,11 +123,10 @@
<el-table v-loading="loading" :data="wmsBorrowOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="借调单ID" align="center" prop="orderId" />
<el-table-column label="借调单号" align="center" prop="orderNo" />
<el-table-column label="借调类型" align="center" prop="borrowType" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="借调单号" align="center" prop="orderNo" width="150" />
<el-table-column label="借调类型" align="center" prop="borrowType" width="100" />
<el-table-column label="物资ID" align="center" prop="materialId" width="100" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" width="100" />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="借用人" align="center" prop="borrowerId" />
<el-table-column label="借用部门" align="center" prop="borrowDeptId" />
......@@ -149,10 +148,11 @@
<el-table-column label="单据状态" align="center" prop="orderStatus" />
<el-table-column label="审批状态" align="center" prop="approvalStatus" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsBorrowOrder:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsBorrowOrder:remove']">删除</el-button>
<el-button link type="success" icon="Check" @click="handleReturn(scope.row)" v-hasPermi="['ware:wmsBorrowOrder:return']">归还</el-button>
</template>
</el-table-column>
</el-table>
......@@ -167,15 +167,29 @@
<!-- 添加或修改借调管理对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsBorrowOrderRef" :model="form" :rules="rules" label-width="80px">
<el-form ref="wmsBorrowOrderRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="借调单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入借调单号" />
</el-form-item>
<el-form-item label="物资ID" prop="materialId">
<el-input v-model="form.materialId" placeholder="请输入物资ID" />
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.materialId" placeholder="请选择物资" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openMaterialSelect">选择物资</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input v-model="form.quantity" placeholder="请输入数量" />
......@@ -202,14 +216,6 @@
placeholder="请选择预计归还时间">
</el-date-picker>
</el-form-item>
<el-form-item label="实际归还时间" prop="actualReturnTime">
<el-date-picker clearable
v-model="form.actualReturnTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际归还时间">
</el-date-picker>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
......@@ -221,11 +227,29 @@
</div>
</template>
</el-dialog>
<!-- 物资选择对话框组件 -->
<MaterialSelectDialog
ref="materialSelectDialogRef"
v-model:visible="materialDialogVisible"
title="选择物资"
@confirm="handleMaterialConfirm"
/>
<!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
</div>
</template>
<script setup name="WmsBorrowOrder">
import { listWmsBorrowOrder, getWmsBorrowOrder, delWmsBorrowOrder, addWmsBorrowOrder, updateWmsBorrowOrder } from "@/api/ware/wmsBorrowOrder"
import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
const { proxy } = getCurrentInstance()
......@@ -239,6 +263,16 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 物资选择对话框状态
const materialDialogVisible = ref(false)
// 物资选择对话框实例引用
const materialSelectDialogRef = ref(null)
// 仓库选择对话框状态
const warehouseDialogVisible = ref(false)
// 仓库选择对话框实例引用
const warehouseSelectDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -265,10 +299,10 @@ const data = reactive({
{ required: true, message: "借调类型不能为空", trigger: "change" }
],
materialId: [
{ required: true, message: "物资ID不能为空", trigger: "blur" }
{ required: true, message: "物资ID不能为空", trigger: "change" }
],
warehouseId: [
{ required: true, message: "仓库ID不能为空", trigger: "blur" }
{ required: true, message: "仓库ID不能为空", trigger: "change" }
],
quantity: [
{ required: true, message: "数量不能为空", trigger: "blur" }
......@@ -302,7 +336,7 @@ function reset() {
form.value = {
orderId: null,
orderNo: null,
borrowType: null,
borrowType: '0',
materialId: null,
warehouseId: null,
quantity: null,
......@@ -398,5 +432,54 @@ function handleExport() {
}, `wmsBorrowOrder_${new Date().getTime()}.xlsx`)
}
/** 打开物资选择对话框 */
function openMaterialSelect() {
materialDialogVisible.value = true
if (materialSelectDialogRef.value) {
materialSelectDialogRef.value.handleQuery()
}
}
/** 处理物资选择结果 */
function handleMaterialConfirm(selectedMaterials) {
if (selectedMaterials.length > 0) {
const material = selectedMaterials[0]
form.value.materialId = material.materialId
// 可以根据需要添加更多物资信息字段
}
}
/** 打开仓库选择对话框 */
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
/** 处理仓库选择结果 */
function handleWarehouseConfirm(selectedWarehouses) {
if (selectedWarehouses.length > 0) {
const warehouse = selectedWarehouses[0]
form.value.warehouseId = warehouse.warehouseId
// 可以根据需要添加更多仓库信息字段
}
}
/** 归还按钮操作 */
function handleReturn(row) {
proxy.$modal.confirm('是否确认归还该借调单?').then(function() {
const updateData = {
orderId: row.orderId,
actualReturnTime: new Date().toISOString().split('T')[0], // 设置为当前日期
orderStatus: '已归还' // 假设"已归还"是订单状态的一个有效值
}
return updateWmsBorrowOrder(updateData)
}).then(() => {
getList()
proxy.$modal.msgSuccess("归还成功")
}).catch(() => {})
}
getList()
</script>
......@@ -97,7 +97,11 @@
<el-table-column label="联系人" align="center" prop="contactPerson" />
<el-table-column label="联系电话" align="center" prop="contactPhone" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
......@@ -152,6 +156,8 @@ import { listWmsCustomer, getWmsCustomer, delWmsCustomer, addWmsCustomer, update
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const wmsCustomerList = ref([])
const open = ref(false)
const loading = ref(true)
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="入库单号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
......@@ -33,22 +33,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总数量" prop="totalQuantity">
<el-input
v-model="queryParams.totalQuantity"
placeholder="请输入总数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总金额" prop="totalAmount">
<el-input
v-model="queryParams.totalAmount"
placeholder="请输入总金额"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="申请人" prop="applicantId">
<el-input
v-model="queryParams.applicantId"
......@@ -163,9 +147,12 @@
<el-table v-loading="loading" :data="wmsInboundOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="入库单ID" align="center" prop="orderId" />
<el-table-column label="入库单号" align="center" prop="orderNo" />
<el-table-column label="入库类型" align="center" prop="orderType" />
<el-table-column label="入库单号" align="center" prop="orderNo" width="180" fixed="left" />
<el-table-column label="入库类型" align="center" prop="orderType" >
<template #default="scope">
<dict-tag :options="inbound_type" :value="scope.row.orderType" />
</template>
</el-table-column>
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="供应商ID" align="center" prop="supplierId" />
<el-table-column label="关联单号" align="center" prop="relatedOrderNo" />
......@@ -203,10 +190,11 @@
</el-table-column>
<el-table-column label="审批状态" align="center" prop="approvalStatus" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsInboundOrder:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsInboundOrder:remove']">删除</el-button>
<el-button link type="primary" icon="Document" @click="handlePrint(scope.row)" v-hasPermi="['ware:wmsInboundOrder:print']">打印</el-button>
</template>
</el-table-column>
</el-table>
......@@ -220,78 +208,245 @@
/>
<!-- 添加或修改入库单主对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsInboundOrderRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="入库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入入库单号" />
</el-form-item>
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="供应商ID" prop="supplierId">
<el-input v-model="form.supplierId" placeholder="请输入供应商ID" />
</el-form-item>
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" />
</el-form-item>
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" />
</el-form-item>
<el-form-item label="申请人" prop="applicantId">
<el-input v-model="form.applicantId" placeholder="请输入申请人" />
</el-form-item>
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择申请时间">
</el-date-picker>
</el-form-item>
<el-form-item label="预计到货日期" prop="expectedArrivalDate">
<el-date-picker clearable
v-model="form.expectedArrivalDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计到货日期">
</el-date-picker>
</el-form-item>
<el-form-item label="实际到货日期" prop="actualArrivalDate">
<el-date-picker clearable
v-model="form.actualArrivalDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际到货日期">
</el-date-picker>
</el-form-item>
<el-form-item label="质检员" prop="qualityCheckerId">
<el-input v-model="form.qualityCheckerId" placeholder="请输入质检员" />
</el-form-item>
<el-form-item label="质检时间" prop="qualityCheckTime">
<el-date-picker clearable
v-model="form.qualityCheckTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择质检时间">
</el-date-picker>
</el-form-item>
<el-form-item label="上架员" prop="putawayPersonId">
<el-input v-model="form.putawayPersonId" placeholder="请输入上架员" />
</el-form-item>
<el-form-item label="上架时间" prop="putawayTime">
<el-date-picker clearable
v-model="form.putawayTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择上架时间">
</el-date-picker>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-dialog :title="title" v-model="open" width="1500px" append-to-body>
<el-form ref="wmsInboundOrderRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="入库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入入库单号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="供应商" prop="supplierId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.supplierId" placeholder="请选择供应商" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openSupplierSelect">选择供应商</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请人" prop="applicantId">
<el-input v-model="form.applicantId" placeholder="请输入申请人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择申请时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="预计到货日期" prop="expectedArrivalDate">
<el-date-picker clearable
v-model="form.expectedArrivalDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计到货日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际到货日期" prop="actualArrivalDate">
<el-date-picker clearable
v-model="form.actualArrivalDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际到货日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="质检员" prop="qualityCheckerId">
<el-input v-model="form.qualityCheckerId" placeholder="请输入质检员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="质检时间" prop="qualityCheckTime">
<el-date-picker clearable
v-model="form.qualityCheckTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择质检时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="上架员" prop="putawayPersonId">
<el-input v-model="form.putawayPersonId" placeholder="请输入上架员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上架时间" prop="putawayTime">
<el-date-picker clearable
v-model="form.putawayTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择上架时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 入库单明细 -->
<el-divider>入库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="addDetail">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.details" border style="width: 100%">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.planQuantity" placeholder="请输入计划数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="actualQuantity" label="实际数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.actualQuantity" placeholder="请输入实际数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="unit" label="单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入单位" size="small" />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.unitPrice" placeholder="请输入单价" size="small" />
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.amount" placeholder="请输入金额" size="small" />
</template>
</el-table-column>
<el-table-column prop="batchNo" label="批次号" width="120">
<template #default="scope">
<el-input v-model="scope.row.batchNo" placeholder="请输入批次号" size="small" />
</template>
</el-table-column>
<el-table-column prop="productionDate" label="生产日期" width="150">
<template #default="scope">
<el-date-picker clearable
v-model="scope.row.productionDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择生产日期"
size="small">
</el-date-picker>
</template>
</el-table-column>
<el-table-column prop="expirationDate" label="失效日期" width="150">
<template #default="scope">
<el-date-picker clearable
v-model="scope.row.expirationDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择失效日期"
size="small">
</el-date-picker>
</template>
</el-table-column>
<el-table-column prop="qualityStatus" label="质检状态" width="100">
<template #default="scope">
<el-input v-model="scope.row.qualityStatus" placeholder="请输入质检状态" size="small" />
</template>
</el-table-column>
<el-table-column prop="checkQuantity" label="质检数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.checkQuantity" placeholder="请输入质检数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="unqualifiedQuantity" label="不合格数量" width="120">
<template #default="scope">
<el-input v-model.number="scope.row.unqualifiedQuantity" placeholder="请输入不合格数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="unqualifiedReason" label="不合格原因" width="150">
<template #default="scope">
<el-input v-model="scope.row.unqualifiedReason" placeholder="请输入不合格原因" size="small" />
</template>
</el-table-column>
<el-table-column prop="storageLocation" label="存储货架" width="120">
<template #default="scope">
<el-input v-model="scope.row.storageLocation" placeholder="请输入存储货架" size="small" />
</template>
</el-table-column>
<el-table-column prop="putawayStatus" label="上架状态" width="100">
<template #default="scope">
<el-input v-model="scope.row.putawayStatus" placeholder="请输入上架状态" size="small" />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" width="150">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
......@@ -300,14 +455,44 @@
</div>
</template>
</el-dialog>
<!-- 物资选择对话框组件 -->
<MaterialSelectDialog
ref="materialSelectDialogRef"
v-model:visible="materialDialogVisible"
title="选择物资"
@confirm="handleMaterialConfirm"
/>
<!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
<!-- 供应商选择对话框组件 -->
<SupplierSelectDialog
ref="supplierSelectDialogRef"
v-model:visible="supplierDialogVisible"
title="选择供应商"
@confirm="handleSupplierConfirm"
/>
</div>
</template>
<script setup name="WmsInboundOrder">
import { listWmsInboundOrder, getWmsInboundOrder, delWmsInboundOrder, addWmsInboundOrder, updateWmsInboundOrder } from "@/api/ware/wmsInboundOrder"
import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
import SupplierSelectDialog from "@/components/SupplierSelectDialog.vue"
import { listWmsMaterial } from "@/api/ware/wmsMaterial"
const { proxy } = getCurrentInstance()
const {inbound_type} = proxy.useDict('inbound_type')
const wmsInboundOrderList = ref([])
const open = ref(false)
const loading = ref(true)
......@@ -318,6 +503,23 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 物资选择对话框状态
const materialDialogVisible = ref(false)
// 物资选择对话框实例引用
const materialSelectDialogRef = ref(null)
// 仓库选择对话框状态
const warehouseDialogVisible = ref(false)
// 仓库选择对话框实例引用
const warehouseSelectDialogRef = ref(null)
// 供应商选择对话框状态
const supplierDialogVisible = ref(false)
// 供应商选择对话框实例引用
const supplierSelectDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -378,7 +580,7 @@ function reset() {
form.value = {
orderId: null,
orderNo: null,
orderType: null,
orderType: '0',
warehouseId: null,
supplierId: null,
relatedOrderNo: null,
......@@ -399,7 +601,8 @@ function reset() {
createBy: null,
createTime: null,
updateBy: null,
updateTime: null
updateTime: null,
details: []
}
proxy.resetForm("wmsInboundOrderRef")
}
......@@ -430,6 +633,40 @@ function handleAdd() {
title.value = "添加入库单主"
}
/** 打开仓库选择对话框 */
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
/** 处理仓库选择结果 */
function handleWarehouseConfirm(selectedWarehouses) {
if (selectedWarehouses.length > 0) {
const warehouse = selectedWarehouses[0]
form.value.warehouseId = warehouse.warehouseId
// 可以根据需要添加更多仓库信息字段
}
}
/** 打开供应商选择对话框 */
function openSupplierSelect() {
supplierDialogVisible.value = true
if (supplierSelectDialogRef.value) {
supplierSelectDialogRef.value.handleQuery()
}
}
/** 处理供应商选择结果 */
function handleSupplierConfirm(selectedSuppliers) {
if (selectedSuppliers.length > 0) {
const supplier = selectedSuppliers[0]
form.value.supplierId = supplier.supplierId
// 可以根据需要添加更多供应商信息字段
}
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset()
......@@ -443,8 +680,34 @@ function handleUpdate(row) {
/** 提交按钮 */
function submitForm() {
// 表单基础验证
proxy.$refs["wmsInboundOrderRef"].validate(valid => {
if (valid) {
// 验证入库单明细
if (!form.value.details || form.value.details.length === 0) {
proxy.$modal.msgError("请至少添加一条入库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.details.length; i++) {
const detail = form.value.details[i]
if (!detail.materialId) {
proxy.$modal.msgError(`第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交数据
if (form.value.orderId != null) {
updateWmsInboundOrder(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
......@@ -462,6 +725,54 @@ function submitForm() {
})
}
/** 添加明细 */
function addDetail() {
if (!form.value.details) {
form.value.details = []
}
// 打开物资选择对话框
materialDialogVisible.value = true
// 调用组件方法加载物资分类和列表
materialSelectDialogRef.value.getTreeselect()
materialSelectDialogRef.value.handleQuery()
}
/** 处理物资选择确认 */
function handleMaterialConfirm(selectedMaterials) {
if (selectedMaterials.length === 0) {
proxy.$modal.msgWarning("请至少选择一个物资")
return
}
// 将选中的物资添加到明细列表
selectedMaterials.forEach(material => {
form.value.details.push({
materialId: material.materialId,
materialName: material.materialName,
planQuantity: null,
actualQuantity: null,
unit: material.unit,
unitPrice: null,
amount: null,
batchNo: null,
productionDate: null,
expirationDate: null,
qualityStatus: null,
checkQuantity: null,
unqualifiedQuantity: null,
unqualifiedReason: null,
storageLocation: null,
putawayStatus: null,
remark: null
})
})
}
/** 删除明细 */
function deleteDetail(index) {
form.value.details.splice(index, 1)
}
/** 删除按钮操作 */
function handleDelete(row) {
const _orderIds = row.orderId || ids.value
......@@ -480,5 +791,177 @@ function handleExport() {
}, `wmsInboundOrder_${new Date().getTime()}.xlsx`)
}
/** 打印按钮操作 */
function handlePrint(row) {
// 获取完整的入库单数据,包括明细
getWmsInboundOrder(row.orderId).then(response => {
const orderData = response.data
// 创建打印窗口
const printWindow = window.open('', '_blank')
// 构建打印内容
let printContent = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>入库单打印</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
padding: 20px;
border: 1px solid #ccc;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.order-info {
margin-bottom: 20px;
padding: 10px;
border: 1px solid #eee;
}
.info-row {
display: flex;
margin-bottom: 10px;
}
.info-label {
width: 120px;
font-weight: bold;
margin-right: 10px;
}
.table-container {
margin-top: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.footer {
margin-top: 30px;
text-align: right;
}
</style>
</head>
<body>
<h1>入库单</h1>
<div class="order-info">
<div class="info-row">
<span class="info-label">入库单号:</span>
<span>${orderData.orderNo}</span>
</div>
<div class="info-row">
<span class="info-label">仓库:</span>
<span>${orderData.warehouseId}</span>
</div>
<div class="info-row">
<span class="info-label">供应商:</span>
<span>${orderData.supplierId}</span>
</div>
<div class="info-row">
<span class="info-label">总数量:</span>
<span>${orderData.totalQuantity}</span>
</div>
<div class="info-row">
<span class="info-label">总金额:</span>
<span>${orderData.totalAmount}</span>
</div>
<div class="info-row">
<span class="info-label">申请人:</span>
<span>${orderData.applicantId}</span>
</div>
<div class="info-row">
<span class="info-label">申请时间:</span>
<span>${orderData.applyTime ? new Date(orderData.applyTime).toLocaleDateString() : ''}</span>
</div>
<div class="info-row">
<span class="info-label">备注:</span>
<span>${orderData.remark || ''}</span>
</div>
</div>
<div class="table-container">
<h3>入库单明细</h3>
<table>
<thead>
<tr>
<th>物资名称</th>
<th>计划数量</th>
<th>实际数量</th>
<th>单位</th>
<th>单价</th>
<th>金额</th>
<th>批次号</th>
<th>生产日期</th>
<th>失效日期</th>
<th>备注</th>
</tr>
</thead>
<tbody>
`
// 添加明细数据
if (orderData.details && orderData.details.length > 0) {
orderData.details.forEach(detail => {
printContent += `
<tr>
<td>${detail.materialName}</td>
<td>${detail.planQuantity}</td>
<td>${detail.actualQuantity}</td>
<td>${detail.unit}</td>
<td>${detail.unitPrice}</td>
<td>${detail.amount}</td>
<td>${detail.batchNo || ''}</td>
<td>${detail.productionDate ? new Date(detail.productionDate).toLocaleDateString() : ''}</td>
<td>${detail.expirationDate ? new Date(detail.expirationDate).toLocaleDateString() : ''}</td>
<td>${detail.remark || ''}</td>
</tr>
`
})
} else {
printContent += `
<tr>
<td colspan="10">无明细数据</td>
</tr>
`
}
// 完成打印内容
printContent += `
</tbody>
</table>
</div>
<div class="footer">
<p>打印时间:${new Date().toLocaleString()}</p>
</div>
</body>
</html>
`
// 写入打印窗口
printWindow.document.write(printContent)
printWindow.document.close()
// 执行打印
printWindow.print()
// 关闭打印窗口
printWindow.close()
})
}
getList()
</script>
......@@ -97,10 +97,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="存储库位" prop="storageLocation">
<el-form-item label="存储货架" prop="storageLocation">
<el-input
v-model="queryParams.storageLocation"
placeholder="请输入存储库位"
placeholder="请输入存储货架"
clearable
@keyup.enter="handleQuery"
/>
......@@ -178,7 +178,7 @@
<el-table-column label="质检数量" align="center" prop="checkQuantity" />
<el-table-column label="不合格数量" align="center" prop="unqualifiedQuantity" />
<el-table-column label="不合格原因" align="center" prop="unqualifiedReason" />
<el-table-column label="存储库位" align="center" prop="storageLocation" />
<el-table-column label="存储货架" align="center" prop="storageLocation" />
<el-table-column label="上架状态" align="center" prop="putawayStatus" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
......@@ -249,8 +249,8 @@
<el-form-item label="不合格原因" prop="unqualifiedReason">
<el-input v-model="form.unqualifiedReason" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="存储库位" prop="storageLocation">
<el-input v-model="form.storageLocation" placeholder="请输入存储库位" />
<el-form-item label="存储货架" prop="storageLocation">
<el-input v-model="form.storageLocation" placeholder="请输入存储货架" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="130px">
<el-form-item label="物资ID" prop="materialId">
<el-input
v-model="queryParams.materialId"
......@@ -17,10 +17,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -57,46 +57,6 @@
placeholder="请选择失效日期">
</el-date-picker>
</el-form-item>
<el-form-item label="库存数量" prop="quantity">
<el-input
v-model="queryParams.quantity"
placeholder="请输入库存数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="可用数量" prop="availableQuantity">
<el-input
v-model="queryParams.availableQuantity"
placeholder="请输入可用数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="锁定数量" prop="lockedQuantity">
<el-input
v-model="queryParams.lockedQuantity"
placeholder="请输入锁定数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="单位成本" prop="unitCost">
<el-input
v-model="queryParams.unitCost"
placeholder="请输入单位成本"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总成本" prop="totalCost">
<el-input
v-model="queryParams.totalCost"
placeholder="请输入总成本"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最后入库时间" prop="lastInboundTime">
<el-date-picker clearable
v-model="queryParams.lastInboundTime"
......@@ -121,14 +81,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="条形码" prop="barcode">
<el-input
v-model="queryParams.barcode"
placeholder="请输入条形码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="是否FIFO管理" prop="isFifo">
<el-input
v-model="queryParams.isFifo"
......@@ -137,14 +89,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="RFID绑定数量" prop="rfidBindingCount">
<el-input
v-model="queryParams.rfidBindingCount"
placeholder="请输入RFID绑定数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最后RFID检测时间" prop="lastRfidDetectTime">
<el-date-picker clearable
v-model="queryParams.lastRfidDetectTime"
......@@ -153,14 +97,6 @@
placeholder="请选择最后RFID检测时间">
</el-date-picker>
</el-form-item>
<el-form-item label="RFID覆盖率" prop="rfidCoverageRate">
<el-input
v-model="queryParams.rfidCoverageRate"
placeholder="请输入RFID覆盖率"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
......@@ -206,15 +142,25 @@
v-hasPermi="['ware:wmsInventory:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="View"
@click="handleLocationVisualization"
v-hasPermi="['ware:wmsInventory:visualization']"
>货架可视化</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="wmsInventoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="货架编码" align="center" prop="locationCode" />
<el-table-column label="货架名称" align="center" prop="locationName" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="批号" align="center" prop="lotNo" />
<el-table-column label="生产日期" align="center" prop="productionDate" width="180">
......@@ -255,10 +201,12 @@
</template>
</el-table-column>
<el-table-column label="RFID覆盖率" align="center" prop="rfidCoverageRate" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsInventory:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsInventory:remove']">删除</el-button>
<el-button link type="primary" icon="Document" @click="handleFlowQuery(scope.row)" v-hasPermi="['ware:wmsInventory:flow']">库存履历</el-button>
<el-button link type="primary" icon="View" @click="handleLocationVisualization(scope.row)" v-hasPermi="['ware:wmsInventory:visualization']">货架可视化</el-button>
</template>
</el-table-column>
</el-table>
......@@ -272,96 +220,210 @@
/>
<!-- 添加或修改库存对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsInventoryRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="物资ID" prop="materialId">
<el-input v-model="form.materialId" placeholder="请输入物资ID" />
</el-form-item>
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
</el-form-item>
<el-form-item label="批号" prop="lotNo">
<el-input v-model="form.lotNo" placeholder="请输入批号" />
</el-form-item>
<el-form-item label="生产日期" prop="productionDate">
<el-date-picker clearable
v-model="form.productionDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择生产日期">
</el-date-picker>
</el-form-item>
<el-form-item label="失效日期" prop="expirationDate">
<el-date-picker clearable
v-model="form.expirationDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择失效日期">
</el-date-picker>
</el-form-item>
<el-form-item label="库存数量" prop="quantity">
<el-input v-model="form.quantity" placeholder="请输入库存数量" />
</el-form-item>
<el-form-item label="可用数量" prop="availableQuantity">
<el-input v-model="form.availableQuantity" placeholder="请输入可用数量" />
</el-form-item>
<el-form-item label="锁定数量" prop="lockedQuantity">
<el-input v-model="form.lockedQuantity" placeholder="请输入锁定数量" />
</el-form-item>
<el-form-item label="单位成本" prop="unitCost">
<el-input v-model="form.unitCost" placeholder="请输入单位成本" />
</el-form-item>
<el-form-item label="总成本" prop="totalCost">
<el-input v-model="form.totalCost" placeholder="请输入总成本" />
</el-form-item>
<el-form-item label="最后入库时间" prop="lastInboundTime">
<el-date-picker clearable
v-model="form.lastInboundTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后入库时间">
</el-date-picker>
</el-form-item>
<el-form-item label="最后出库时间" prop="lastOutboundTime">
<el-date-picker clearable
v-model="form.lastOutboundTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后出库时间">
</el-date-picker>
</el-form-item>
<el-form-item label="RFID标签" prop="rfidTag">
<el-input v-model="form.rfidTag" placeholder="请输入RFID标签" />
</el-form-item>
<el-form-item label="条形码" prop="barcode">
<el-input v-model="form.barcode" placeholder="请输入条形码" />
</el-form-item>
<el-form-item label="是否FIFO管理" prop="isFifo">
<el-input v-model="form.isFifo" placeholder="请输入是否FIFO管理" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="RFID绑定数量" prop="rfidBindingCount">
<el-input v-model="form.rfidBindingCount" placeholder="请输入RFID绑定数量" />
</el-form-item>
<el-form-item label="最后RFID检测时间" prop="lastRfidDetectTime">
<el-date-picker clearable
v-model="form.lastRfidDetectTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后RFID检测时间">
</el-date-picker>
</el-form-item>
<el-form-item label="RFID覆盖率" prop="rfidCoverageRate">
<el-input v-model="form.rfidCoverageRate" placeholder="请输入RFID覆盖率" />
</el-form-item>
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-form ref="wmsInventoryRef" :model="form" :rules="rules" label-width="130px">
<!-- 第一行:物资ID -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="物资ID" prop="materialId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.materialId" placeholder="请选择物资" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openMaterialSelect">选择物资</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:仓库ID -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="仓库ID" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行:货架ID -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="货架ID" prop="locationId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.locationId" placeholder="请选择货架" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openLocationSelect">选择货架</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行:批次号、批号 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="批号" prop="lotNo">
<el-input v-model="form.lotNo" placeholder="请输入批号" />
</el-form-item>
</el-col>
</el-row>
<!-- 第五行:生产日期、失效日期 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="生产日期" prop="productionDate">
<el-date-picker clearable
v-model="form.productionDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择生产日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效日期" prop="expirationDate">
<el-date-picker clearable
v-model="form.expirationDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择失效日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第六行:库存数量、可用数量 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="库存数量" prop="quantity">
<el-input v-model="form.quantity" placeholder="请输入库存数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="可用数量" prop="availableQuantity">
<el-input v-model="form.availableQuantity" placeholder="请输入可用数量" />
</el-form-item>
</el-col>
</el-row>
<!-- 第七行:锁定数量、单位成本 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="锁定数量" prop="lockedQuantity">
<el-input v-model="form.lockedQuantity" placeholder="请输入锁定数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位成本" prop="unitCost">
<el-input v-model="form.unitCost" placeholder="请输入单位成本" />
</el-form-item>
</el-col>
</el-row>
<!-- 第八行:总成本、最后入库时间 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="总成本" prop="totalCost">
<el-input v-model="form.totalCost" placeholder="请输入总成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最后入库时间" prop="lastInboundTime">
<el-date-picker clearable
v-model="form.lastInboundTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后入库时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第九行:最后出库时间、RFID标签 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="最后出库时间" prop="lastOutboundTime">
<el-date-picker clearable
v-model="form.lastOutboundTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后出库时间">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="RFID标签" prop="rfidTag">
<el-input v-model="form.rfidTag" placeholder="请输入RFID标签" />
</el-form-item>
</el-col>
</el-row>
<!-- 第十行:条形码、是否FIFO管理 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="条形码" prop="barcode">
<el-input v-model="form.barcode" placeholder="请输入条形码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否FIFO管理" prop="isFifo">
<el-input v-model="form.isFifo" placeholder="请输入是否FIFO管理" />
</el-form-item>
</el-col>
</el-row>
<!-- 第十一行:备注 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 第十二行:RFID绑定数量、最后RFID检测时间 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RFID绑定数量" prop="rfidBindingCount">
<el-input v-model="form.rfidBindingCount" placeholder="请输入RFID绑定数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最后RFID检测时间" prop="lastRfidDetectTime">
<el-date-picker clearable
v-model="form.lastRfidDetectTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后RFID检测时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第十三行:RFID覆盖率 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RFID覆盖率" prop="rfidCoverageRate">
<el-input v-model="form.rfidCoverageRate" placeholder="请输入RFID覆盖率" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
......@@ -370,11 +432,163 @@
</div>
</template>
</el-dialog>
<!-- 物资选择对话框 -->
<MaterialSelectDialog
ref="materialSelectDialogRef"
v-model:visible="materialDialogVisible"
title="选择物资"
@confirm="handleMaterialConfirm"
@cancel="handleMaterialCancel"
/>
<!-- 仓库选择对话框 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
@cancel="handleWarehouseCancel"
/>
<!-- 货架选择对话框 -->
<LocationSelectDialog
ref="locationSelectDialogRef"
v-model:visible="locationDialogVisible"
title="选择货架"
@confirm="handleLocationConfirm"
@cancel="handleLocationCancel"
/>
<!-- 库存履历对话框 -->
<InventoryFlowDialog
ref="inventoryFlowDialogRef"
v-model:visible="inventoryFlowDialogVisible"
v-model:inventory-id="selectedInventoryId"
title="库存履历"
/>
<!-- 货架可视化对话框 -->
<el-dialog
title="货架可视化"
v-model="locationVisualDialogVisible"
width="1400px"
append-to-body
>
<div class="location-visual-container">
<!-- 第一列:货架信息 -->
<div class="visual-column location-info">
<h3>货架信息</h3>
<el-form :model="selectedLocationInfo" label-width="80px" class="three-col-form">
<div class="form-row">
<el-form-item label="货架ID">
<span>{{ selectedLocationInfo.locationId || '-' }}</span>
</el-form-item>
<el-form-item label="货架编码">
<span>{{ selectedLocationInfo.locationCode || '-' }}</span>
</el-form-item>
<el-form-item label="货架名称">
<span>{{ selectedLocationInfo.locationName || '-' }}</span>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="所属库区">
<span>{{ selectedLocationInfo.areaName || '-' }}</span>
</el-form-item>
<el-form-item label="货架状态">
<el-tag :type="selectedLocationInfo.status === '1' ? 'success' : 'info'">
{{ selectedLocationInfo.status === '1' ? '在用' : '停用' }}
</el-tag>
</el-form-item>
<el-form-item label="容纳能力">
<span>{{ selectedLocationInfo.capacity || '-' }}</span>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="已用容量">
<span>{{ selectedLocationInfo.usedCapacity || '0' }}</span>
</el-form-item>
<el-form-item label="物资数量">
<span>{{ selectedLocationInfo.materialCount || '0' }}</span>
</el-form-item>
<!-- 空列占位 -->
<el-form-item>
<span>-</span>
</el-form-item>
</div>
</el-form>
</div>
<!-- 第二列:可视化展示 -->
<div class="visual-column">
<h3>可视化展示</h3>
<div class="visualization-container">
<div class="warehouse-layout">
<!-- 货架行 -->
<div class="shelf-row" v-for="row in 4" :key="row">
<div class="shelf-label">货架 {{ row }}</div>
<!-- 货架列 -->
<div class="shelf-columns">
<div
v-for="col in 5"
:key="`${row}-${col}`"
class="shelf-column"
>
<div
class="location-cell"
:class="{
'selected': (row-1)*5 + col === 10,
'occupied': ((row-1)*5 + col) % 3 === 0,
'empty': ((row-1)*5 + col) % 3 !== 0
}"
>
<div class="cell-header">
<div class="cell-code">A{{ row }}{{ col }}</div>
</div>
<div class="cell-body">
<div class="cell-status" v-if="((row-1)*5 + col) % 3 === 0">
<el-icon class="status-icon"><CircleCheckFilled /></el-icon>
<span>已占用</span>
</div>
<div class="cell-status" v-else>
<el-icon class="status-icon"><CircleCloseFilled /></el-icon>
<span>空闲</span>
</div>
<div class="cell-capacity" v-if="((row-1)*5 + col) % 3 === 0">
容量: 80%
</div>
</div>
<div class="cell-footer">
<div class="cell-material-count" v-if="((row-1)*5 + col) % 3 === 0">
物资: 3
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="locationVisualDialogVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="WmsInventory">
import { listWmsInventory, getWmsInventory, delWmsInventory, addWmsInventory, updateWmsInventory } from "@/api/ware/wmsInventory"
import { getWmsLocation } from "@/api/ware/wmsLocation"
import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
import LocationSelectDialog from "@/components/LocationSelectDialog.vue"
import InventoryFlowDialog from "@/components/InventoryFlowDialog.vue"
import { View, CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue'
const { proxy } = getCurrentInstance()
......@@ -388,6 +602,29 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 选择对话框可见性
const materialDialogVisible = ref(false)
const warehouseDialogVisible = ref(false)
const locationDialogVisible = ref(false)
const inventoryFlowDialogVisible = ref(false)
const locationVisualDialogVisible = ref(false)
const selectedInventoryId = ref('')
const selectedLocationId = ref('')
const selectedLocationInfo = reactive({
locationId: '',
locationCode: '',
locationName: '',
areaName: '',
status: '1',
capacity: '',
usedCapacity: '0',
materialCount: '0'
})
const materialSelectDialogRef = ref(null)
const warehouseSelectDialogRef = ref(null)
const locationSelectDialogRef = ref(null)
const inventoryFlowDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -450,6 +687,71 @@ function cancel() {
reset()
}
// 打开物资选择对话框
function openMaterialSelect() {
materialDialogVisible.value = true
if (materialSelectDialogRef.value) {
materialSelectDialogRef.value.handleQuery()
}
}
// 打开仓库选择对话框
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
// 打开货架选择对话框
function openLocationSelect() {
locationDialogVisible.value = true
if (locationSelectDialogRef.value) {
locationSelectDialogRef.value.handleQuery()
}
}
// 确认选择物资
function handleMaterialConfirm(selection) {
if (selection && selection.length > 0) {
form.value.materialId = selection[0].materialId
}
materialDialogVisible.value = false
}
// 取消选择物资
function handleMaterialCancel() {
materialDialogVisible.value = false
}
// 确认选择仓库
function handleWarehouseConfirm(selection) {
if (selection && selection.length > 0) {
form.value.warehouseId = selection[0].warehouseId
}
warehouseDialogVisible.value = false
}
// 取消选择仓库
function handleWarehouseCancel() {
warehouseDialogVisible.value = false
}
// 确认选择货架
function handleLocationConfirm(selection) {
if (selection && selection.length > 0) {
form.value.locationId = selection[0].locationId
form.value.locationCode = selection[0].locationCode
form.value.locationName = selection[0].locationName
}
locationDialogVisible.value = false
}
// 取消选择货架
function handleLocationCancel() {
locationDialogVisible.value = false
}
// 表单重置
function reset() {
form.value = {
......@@ -457,6 +759,8 @@ function reset() {
materialId: null,
warehouseId: null,
locationId: null,
locationCode: null,
locationName: null,
batchNo: null,
lotNo: null,
productionDate: null,
......@@ -561,5 +865,296 @@ function handleExport() {
}, `wmsInventory_${new Date().getTime()}.xlsx`)
}
/** 库存履历查询 */
function handleFlowQuery(row) {
selectedInventoryId.value = row.inventoryId
inventoryFlowDialogVisible.value = true
if (inventoryFlowDialogRef.value) {
inventoryFlowDialogRef.value.handleQuery()
}
}
/** 货架可视化展示 */
function handleLocationVisualization(row) {
if (row) {
selectedLocationId.value = row.locationId
} else {
// 如果没有选中记录,默认显示第一个记录的货架
if (wmsInventoryList.value.length > 0) {
selectedLocationId.value = wmsInventoryList.value[0].locationId
}
}
// 获取货架详情信息
if (selectedLocationId.value) {
getWmsLocation(selectedLocationId.value).then(response => {
const locationData = response.data
Object.assign(selectedLocationInfo, {
locationId: locationData.locationId,
locationCode: locationData.locationCode,
locationName: locationData.locationName,
areaName: locationData.areaName || '',
status: locationData.status,
capacity: locationData.capacity || '',
usedCapacity: locationData.usedCapacity || '0',
materialCount: locationData.materialCount || '0'
})
})
}
locationVisualDialogVisible.value = true
}
getList()
</script>
<style scoped>
.location-visual-container {
display: flex;
gap: 20px;
height: 500px;
}
.visual-column {
flex: 1;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 15px;
background-color: #f9f9f9;
}
.visual-column h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
font-weight: bold;
color: #303133;
}
.visualization-container {
height: calc(100% - 40px);
background-color: #fff;
border-radius: 4px;
overflow: hidden;
border: 1px solid #e4e7ed;
}
.warehouse-layout {
padding: 20px;
background-color: #f0f2f5;
height: 100%;
overflow-y: auto;
}
.shelf-row {
display: flex;
align-items: flex-start;
margin-bottom: 30px;
position: relative;
}
.shelf-row:last-child {
margin-bottom: 0;
}
.shelf-label {
width: 80px;
padding: 10px;
background-color: #409eff;
color: white;
text-align: center;
font-weight: bold;
border-radius: 4px;
margin-right: 20px;
align-self: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.shelf-columns {
display: flex;
gap: 20px;
flex: 1;
}
.shelf-column {
flex: 1;
}
.location-cell {
background-color: #fff;
border: 2px solid #dcdfe6;
border-radius: 8px;
padding: 12px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
min-height: 120px;
}
.location-cell::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
border-radius: 8px 8px 0 0;
background-color: #dcdfe6;
transition: background-color 0.3s ease;
}
.location-cell.occupied {
border-color: #f56c6c;
background-color: #fef0f0;
}
.location-cell.occupied::before {
background-color: #f56c6c;
}
.location-cell.empty {
border-color: #67c23a;
background-color: #f0f9eb;
}
.location-cell.empty::before {
background-color: #67c23a;
}
.location-cell.selected {
border-color: #409eff;
background-color: #ecf5ff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
transform: translateY(-2px);
}
.location-cell.selected::before {
background-color: #409eff;
height: 6px;
}
.cell-header {
width: 100%;
text-align: center;
margin-bottom: 8px;
}
.cell-code {
font-size: 18px;
font-weight: bold;
color: #303133;
letter-spacing: 1px;
}
.cell-body {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
}
.cell-status {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
}
.cell-status span {
color: #303133;
}
.status-icon {
font-size: 16px;
}
.cell-status:nth-child(1) .status-icon {
color: #67c23a;
}
.cell-capacity, .cell-material-count {
font-size: 12px;
color: #606266;
text-align: center;
padding: 2px 8px;
background-color: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
.cell-footer {
width: 100%;
text-align: center;
}
.material-info {
height: calc(100% - 40px);
background-color: #fff;
border-radius: 4px;
padding: 20px;
overflow-y: auto;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.info-item:last-child {
border-bottom: none;
}
.info-item label {
font-weight: bold;
color: #606266;
}
.info-item span {
color: #303133;
}
/* 表单一行3列布局样式 */
.three-col-form {
width: 100%;
}
.form-row {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.form-row .el-form-item {
flex: 1;
margin-bottom: 0;
margin-right: 0;
}
.form-row .el-form-item__label {
width: 60px;
text-align: left;
padding-right: 10px;
}
.form-row .el-form-item__content {
margin-left: 70px !important;
flex: 1;
}
/* 调整最后一行空列的样式 */
.form-row .el-form-item:last-child .el-form-item__label {
visibility: hidden;
}
</style>
......@@ -25,10 +25,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -159,7 +159,7 @@
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="流水类型" align="center" prop="flowType" />
<el-table-column label="流水单号" align="center" prop="flowCode" />
<el-table-column label="批次号" align="center" prop="batchNo" />
......@@ -203,8 +203,8 @@
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="流水单号" prop="flowCode">
<el-input v-model="form.flowCode" placeholder="请输入流水单号" />
......
......@@ -17,10 +17,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -166,7 +166,7 @@
<el-table-column label="盘点结果ID" align="center" prop="resultId" />
<el-table-column label="盘点任务ID" align="center" prop="taskId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="系统数量" align="center" prop="systemQuantity" />
<el-table-column label="实际数量" align="center" prop="actualQuantity" />
......@@ -209,8 +209,8 @@
<el-form-item label="物资ID" prop="materialId">
<el-input v-model="form.materialId" placeholder="请输入物资ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
......
......@@ -127,7 +127,7 @@
<el-table-column label="盘点计划ID" align="center" prop="planId" />
<el-table-column label="盘点任务编号" align="center" prop="taskNo" />
<el-table-column label="库区ID" align="center" prop="areaId" />
<el-table-column label="库位范围" align="center" prop="locationRange" />
<el-table-column label="货架范围" align="center" prop="locationRange" />
<el-table-column label="负责人ID" align="center" prop="assigneeId" />
<el-table-column label="协助人IDs" align="center" prop="assistantIds" />
<el-table-column label="任务状态" align="center" prop="taskStatus" />
......@@ -173,7 +173,7 @@
<el-form-item label="库区ID" prop="areaId">
<el-input v-model="form.areaId" placeholder="请输入库区ID" />
</el-form-item>
<el-form-item label="库位范围" prop="locationRange">
<el-form-item label="货架范围" prop="locationRange">
<el-input v-model="form.locationRange" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="负责人ID" prop="assigneeId">
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="库位编码" prop="locationCode">
<el-form-item label="货架编码" prop="locationCode">
<el-input
v-model="queryParams.locationCode"
placeholder="请输入库位编码"
placeholder="请输入货架编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位名称" prop="locationName">
<el-form-item label="货架名称" prop="locationName">
<el-input
v-model="queryParams.locationName"
placeholder="请输入库位名称"
placeholder="请输入货架名称"
clearable
@keyup.enter="handleQuery"
/>
......@@ -25,78 +25,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="X坐标" prop="xCoordinate">
<el-input
v-model="queryParams.xCoordinate"
placeholder="请输入X坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="Y坐标" prop="yCoordinate">
<el-input
v-model="queryParams.yCoordinate"
placeholder="请输入Y坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="Z坐标" prop="zCoordinate">
<el-input
v-model="queryParams.zCoordinate"
placeholder="请输入Z坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最大容量" prop="maxCapacity">
<el-input
v-model="queryParams.maxCapacity"
placeholder="请输入最大容量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="当前容量" prop="currentCapacity">
<el-input
v-model="queryParams.currentCapacity"
placeholder="请输入当前容量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="承重限制" prop="weightLimit">
<el-input
v-model="queryParams.weightLimit"
placeholder="请输入承重限制"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="长度" prop="length">
<el-input
v-model="queryParams.length"
placeholder="请输入长度"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="宽度" prop="width">
<el-input
v-model="queryParams.width"
placeholder="请输入宽度"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="高度" prop="height">
<el-input
v-model="queryParams.height"
placeholder="请输入高度"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="RFID天线ID" prop="rfidAntennaId">
<el-input
v-model="queryParams.rfidAntennaId"
......@@ -105,14 +33,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最后盘点时间" prop="lastInventoryTime">
<el-date-picker clearable
v-model="queryParams.lastInventoryTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后盘点时间">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
......@@ -163,11 +83,10 @@
<el-table v-loading="loading" :data="wmsLocationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="库位编码" align="center" prop="locationCode" />
<el-table-column label="库位名称" align="center" prop="locationName" />
<el-table-column label="所属库区ID" align="center" prop="areaId" />
<el-table-column label="库位类型" align="center" prop="locationType" />
<el-table-column label="货架编码" align="center" prop="locationCode" width="180" />
<el-table-column label="货架名称" align="center" prop="locationName" />
<el-table-column label="所属库区" align="center" prop="areaId" />
<el-table-column label="货架类型" align="center" prop="locationType" />
<el-table-column label="X坐标" align="center" prop="xCoordinate" />
<el-table-column label="Y坐标" align="center" prop="yCoordinate" />
<el-table-column label="Z坐标" align="center" prop="zCoordinate" />
......@@ -179,13 +98,8 @@
<el-table-column label="高度" align="center" prop="height" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="RFID天线ID" align="center" prop="rfidAntennaId" />
<el-table-column label="最后盘点时间" align="center" prop="lastInventoryTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.lastInventoryTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsLocation:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsLocation:remove']">删除</el-button>
......@@ -201,17 +115,32 @@
@pagination="getList"
/>
<!-- 添加或修改库位信息对话框 -->
<!-- 库区选择对话框组件 -->
<AreaSelectTreeDialog
ref="areaSelectDialogRef"
v-model:visible="areaDialogVisible"
title="选择库区"
@confirm="handleAreaConfirm"
/>
<!-- 添加或修改货架信息对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsLocationRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="库位编码" prop="locationCode">
<el-input v-model="form.locationCode" placeholder="请输入库位编码" />
<el-form-item label="货架编码" prop="locationCode">
<el-input v-model="form.locationCode" placeholder="请输入货架编码" />
</el-form-item>
<el-form-item label="库位名称" prop="locationName">
<el-input v-model="form.locationName" placeholder="请输入库位名称" />
<el-form-item label="货架名称" prop="locationName">
<el-input v-model="form.locationName" placeholder="请输入货架名称" />
</el-form-item>
<el-form-item label="所属库区ID" prop="areaId">
<el-input v-model="form.areaId" placeholder="请输入所属库区ID" />
<el-form-item label="所属库区" prop="areaId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.areaId" placeholder="请选择所属库区" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openAreaSelect">选择库区</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="X坐标" prop="xCoordinate">
<el-input v-model="form.xCoordinate" placeholder="请输入X坐标" />
......@@ -243,14 +172,6 @@
<el-form-item label="RFID天线ID" prop="rfidAntennaId">
<el-input v-model="form.rfidAntennaId" placeholder="请输入RFID天线ID" />
</el-form-item>
<el-form-item label="最后盘点时间" prop="lastInventoryTime">
<el-date-picker clearable
v-model="form.lastInventoryTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择最后盘点时间">
</el-date-picker>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
......@@ -267,6 +188,7 @@
<script setup name="WmsLocation">
import { listWmsLocation, getWmsLocation, delWmsLocation, addWmsLocation, updateWmsLocation } from "@/api/ware/wmsLocation"
import AreaSelectTreeDialog from "@/components/AreaSelectTreeDialog.vue"
const { proxy } = getCurrentInstance()
......@@ -280,6 +202,11 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 库区选择对话框状态
const areaDialogVisible = ref(false)
// 库区选择对话框实例引用
const areaSelectDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -304,7 +231,7 @@ const data = reactive({
},
rules: {
locationCode: [
{ required: true, message: "库位编码不能为空", trigger: "blur" }
{ required: true, message: "货架编码不能为空", trigger: "blur" }
],
areaId: [
{ required: true, message: "所属库区ID不能为空", trigger: "blur" }
......@@ -314,7 +241,7 @@ const data = reactive({
const { queryParams, form, rules } = toRefs(data)
/** 查询库位信息列表 */
/** 查询货架信息列表 */
function getList() {
loading.value = true
listWmsLocation(queryParams.value).then(response => {
......@@ -382,7 +309,7 @@ function handleSelectionChange(selection) {
function handleAdd() {
reset()
open.value = true
title.value = "添加库位信息"
title.value = "添加货架信息"
}
/** 修改按钮操作 */
......@@ -392,7 +319,7 @@ function handleUpdate(row) {
getWmsLocation(_locationId).then(response => {
form.value = response.data
open.value = true
title.value = "修改库位信息"
title.value = "修改货架信息"
})
}
......@@ -420,7 +347,7 @@ function submitForm() {
/** 删除按钮操作 */
function handleDelete(row) {
const _locationIds = row.locationId || ids.value
proxy.$modal.confirm('是否确认删除库位信息编号为"' + _locationIds + '"的数据项?').then(function() {
proxy.$modal.confirm('是否确认删除货架信息编号为"' + _locationIds + '"的数据项?').then(function() {
return delWmsLocation(_locationIds)
}).then(() => {
getList()
......@@ -435,5 +362,21 @@ function handleExport() {
}, `wmsLocation_${new Date().getTime()}.xlsx`)
}
// 打开库区选择对话框
function openAreaSelect() {
areaDialogVisible.value = true
if (areaSelectDialogRef.value) {
areaSelectDialogRef.value.handleQuery()
}
}
// 处理库区选择确认
function handleAreaConfirm(areas) {
if (areas && areas.length > 0) {
form.value.areaId = areas[0].areaId
// 如果需要显示库区名称,可以添加一个areaName字段
}
}
getList()
</script>
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="物资编码" prop="materialCode">
<el-input
v-model="queryParams.materialCode"
......@@ -17,10 +17,14 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="分类ID" prop="categoryId">
<el-input
<el-form-item label="物资分类" prop="categoryId">
<el-tree-select
v-model="queryParams.categoryId"
placeholder="请输入分类ID"
:data="wmsMaterialCategoryOptions"
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
value-key="categoryId"
placeholder="请选择物资分类"
check-strictly
clearable
@keyup.enter="handleQuery"
/>
......@@ -49,93 +53,32 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="默认供应商" prop="supplierId">
<el-input
v-model="queryParams.supplierId"
placeholder="请输入默认供应商"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="安全库存" prop="safetyStock">
<el-input
v-model="queryParams.safetyStock"
placeholder="请输入安全库存"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最高库存" prop="maxStock">
<el-input
v-model="queryParams.maxStock"
placeholder="请输入最高库存"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最低库存" prop="minStock">
<el-input
v-model="queryParams.minStock"
placeholder="请输入最低库存"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="保质期天数" prop="shelfLifeDays">
<el-input
v-model="queryParams.shelfLifeDays"
placeholder="请输入保质期天数"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="存储条件" prop="storageCondition">
<el-input
v-model="queryParams.storageCondition"
placeholder="请输入存储条件"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="单件重量" prop="weight">
<el-input
v-model="queryParams.weight"
placeholder="请输入单件重量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="单件体积" prop="volume">
<el-input
v-model="queryParams.volume"
placeholder="请输入单件体积"
clearable
@keyup.enter="handleQuery"
/>
<el-form-item label="供应商" prop="supplierId">
<div class="input-with-select">
<el-input
v-model="currentSupplierName"
placeholder="请选择供应商"
readonly
clearable
@clear="handleSupplierClear('query')"
/>
<el-button
slot="append"
icon="Search"
@click="handleSupplierSelect('query')"
>选择</el-button>
</div>
</el-form-item>
<el-form-item label="是否RFID管理" prop="isRfidManaged">
<el-input
<el-form-item label="是否RFID" prop="isRfidManaged">
<el-select
v-model="queryParams.isRfidManaged"
placeholder="请输入是否RFID管理"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="RFID标准" prop="rfidStandard">
<el-input
v-model="queryParams.rfidStandard"
placeholder="请输入RFID标准"
placeholder="请选择是否RFID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="RFID粘贴位置" prop="rfidPosition">
<el-input
v-model="queryParams.rfidPosition"
placeholder="请输入RFID粘贴位置"
clearable
@keyup.enter="handleQuery"
/>
>
<el-option v-for="item in custom_yes_no" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
......@@ -187,15 +130,18 @@
<el-table v-loading="loading" :data="wmsMaterialList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="物资编码" align="center" prop="materialCode" />
<el-table-column label="物资编码" align="center" prop="materialCode" width="150" />
<el-table-column label="物资名称" align="center" prop="materialName" />
<el-table-column label="分类ID" align="center" prop="categoryId" />
<el-table-column label="物资分类" align="center" prop="categoryId" />
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="计量单位" align="center" prop="unit" />
<el-table-column label="物资类型" align="center" prop="materialType" />
<el-table-column label="生产厂家" align="center" prop="manufacturer" />
<el-table-column label="默认供应商" align="center" prop="supplierId" />
<el-table-column label="供应商" align="center" prop="supplierId">
<template #default="scope">
<span>{{ scope.row.supplierName || scope.row.supplierId }}</span>
</template>
</el-table-column>
<el-table-column label="安全库存" align="center" prop="safetyStock" />
<el-table-column label="最高库存" align="center" prop="maxStock" />
<el-table-column label="最低库存" align="center" prop="minStock" />
......@@ -203,14 +149,53 @@
<el-table-column label="存储条件" align="center" prop="storageCondition" />
<el-table-column label="单件重量" align="center" prop="weight" />
<el-table-column label="单件体积" align="center" prop="volume" />
<el-table-column label="图片URL" align="center" prop="imageUrl" />
<el-table-column label="文档URL" align="center" prop="documentUrl" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="图片" align="center" prop="imageUrl">
<template #default="scope">
<div v-if="scope.row.imageUrl" class="image-preview-container">
<el-button
:key="index"
link
type="primary"
@click="handleImagePreview(scope.row.imageUrl.split(','))"
class="preview-image-btn"
>
预览
</el-button>
</div>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="文档" align="center" prop="documentUrl">
<template #default="scope">
<div v-if="scope.row.documentUrl" class="document-preview-container">
<el-button
v-for="(url, index) in scope.row.documentUrl.split(',')"
:key="index"
link
type="primary"
@click="handleDocumentPreview(url)"
class="preview-document-btn"
>
预览
</el-button>
</div>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="是否RFID管理" align="center" prop="isRfidManaged" />
<el-table-column label="RFID标准" align="center" prop="rfidStandard" />
<el-table-column label="是否RFID管理" align="center" prop="isRfidManaged" width="100">
<template #default="scope">
<dict-tag :options="custom_yes_no" :value="scope.row.isRfidManaged" />
</template>
</el-table-column>
<el-table-column label="RFID标签" align="center" prop="rfidStandard" />
<el-table-column label="RFID粘贴位置" align="center" prop="rfidPosition" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsMaterial:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsMaterial:remove']">删除</el-button>
......@@ -227,69 +212,218 @@
/>
<!-- 添加或修改物资主数据对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsMaterialRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="物资编码" prop="materialCode">
<el-input v-model="form.materialCode" placeholder="请输入物资编码" />
</el-form-item>
<el-form-item label="物资名称" prop="materialName">
<el-input v-model="form.materialName" placeholder="请输入物资名称" />
</el-form-item>
<el-form-item label="分类ID" prop="categoryId">
<el-input v-model="form.categoryId" placeholder="请输入分类ID" />
</el-form-item>
<el-form-item label="规格型号" prop="specification">
<el-input v-model="form.specification" placeholder="请输入规格型号" />
</el-form-item>
<el-form-item label="计量单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入计量单位" />
</el-form-item>
<el-form-item label="生产厂家" prop="manufacturer">
<el-input v-model="form.manufacturer" placeholder="请输入生产厂家" />
</el-form-item>
<el-form-item label="默认供应商" prop="supplierId">
<el-input v-model="form.supplierId" placeholder="请输入默认供应商" />
</el-form-item>
<el-form-item label="安全库存" prop="safetyStock">
<el-input v-model="form.safetyStock" placeholder="请输入安全库存" />
</el-form-item>
<el-form-item label="最高库存" prop="maxStock">
<el-input v-model="form.maxStock" placeholder="请输入最高库存" />
</el-form-item>
<el-form-item label="最低库存" prop="minStock">
<el-input v-model="form.minStock" placeholder="请输入最低库存" />
</el-form-item>
<el-form-item label="保质期天数" prop="shelfLifeDays">
<el-input v-model="form.shelfLifeDays" placeholder="请输入保质期天数" />
</el-form-item>
<el-form-item label="存储条件" prop="storageCondition">
<el-input v-model="form.storageCondition" placeholder="请输入存储条件" />
</el-form-item>
<el-form-item label="单件重量" prop="weight">
<el-input v-model="form.weight" placeholder="请输入单件重量" />
</el-form-item>
<el-form-item label="单件体积" prop="volume">
<el-input v-model="form.volume" placeholder="请输入单件体积" />
</el-form-item>
<el-form-item label="图片URL" prop="imageUrl">
<el-input v-model="form.imageUrl" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="文档URL" prop="documentUrl">
<el-input v-model="form.documentUrl" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="是否RFID管理" prop="isRfidManaged">
<el-input v-model="form.isRfidManaged" placeholder="请输入是否RFID管理" />
</el-form-item>
<el-form-item label="RFID标准" prop="rfidStandard">
<el-input v-model="form.rfidStandard" placeholder="请输入RFID标准" />
</el-form-item>
<el-form-item label="RFID粘贴位置" prop="rfidPosition">
<el-input v-model="form.rfidPosition" placeholder="请输入RFID粘贴位置" />
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-form ref="wmsMaterialRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物资编码" prop="materialCode">
<el-input v-model="form.materialCode" placeholder="请输入物资编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物资名称" prop="materialName">
<el-input v-model="form.materialName" placeholder="请输入物资名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物资分类" prop="categoryId">
<el-tree-select
v-model="form.categoryId"
:data="wmsMaterialCategoryOptions"
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
value-key="categoryId"
placeholder="请选择物资分类"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格型号" prop="specification">
<el-input v-model="form.specification" placeholder="请输入规格型号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="计量单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入计量单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生产厂家" prop="manufacturer">
<el-input v-model="form.manufacturer" placeholder="请输入生产厂家" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="供应商" prop="supplierId">
<div class="input-with-select">
<el-input
v-model="currentSupplierName"
placeholder="请选择供应商"
readonly
clearable
@clear="handleSupplierClear('form')"
/>
<el-button
slot="append"
icon="Search"
@click="handleSupplierSelect('form')"
>选择</el-button>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="安全库存" prop="safetyStock">
<el-input v-model="form.safetyStock" placeholder="请输入安全库存" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="最高库存" prop="maxStock">
<el-input v-model="form.maxStock" placeholder="请输入最高库存" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最低库存" prop="minStock">
<el-input v-model="form.minStock" placeholder="请输入最低库存" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="保质期天数" prop="shelfLifeDays">
<el-input v-model="form.shelfLifeDays" placeholder="请输入保质期天数" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="存储条件" prop="storageCondition">
<el-input v-model="form.storageCondition" placeholder="请输入存储条件" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="单件重量" prop="weight">
<el-input v-model="form.weight" placeholder="请输入单件重量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单件体积" prop="volume">
<el-input v-model="form.volume" placeholder="请输入单件体积" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="物资图片">
<ImageUpload
v-model="form.imageUrl"
:limit="3"
:fileSize="5"
:fileType="['jpg', 'png', 'gif']"
:isShowTip="true"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="技术文档">
<FileUpload
v-model="form.documentUrl"
:limit="5"
:fileSize="20"
:fileType="['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt']"
:isShowTip="true"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否RFID管理" prop="isRfidManaged">
<el-input v-model="form.isRfidManaged" placeholder="请输入是否RFID管理" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="RFID标签" prop="rfidStandard">
<el-input v-model="form.rfidStandard" placeholder="请输入RFID标准" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RFID粘贴位置" prop="rfidPosition">
<el-input v-model="form.rfidPosition" placeholder="请输入RFID粘贴位置" />
</el-form-item>
</el-col>
</el-row>
<!-- 自定义属性扩展 -->
<el-divider>自定义属性</el-divider>
<el-form-item>
<el-button type="primary" plain size="small" @click="showCustomAttrDialog = true">添加属性</el-button>
<el-table :data="form.customAttributes" border style="margin-top: 10px;">
<el-table-column prop="attrName" label="属性名称" width="120" />
<el-table-column prop="attrValue" label="属性值" />
<el-table-column prop="attrType" label="属性类型" width="100">
<template #default="scope">
<el-tag size="small">{{ scope.row.attrType }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template #default="scope">
<el-button link size="small" @click="editCustomAttr(scope.row)">编辑</el-button>
<el-button link size="small" type="danger" @click="deleteCustomAttr(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<!-- 自定义属性编辑对话框 -->
<el-dialog
v-model="data.showCustomAttrDialog"
title="自定义属性编辑"
width="400px"
append-to-body
>
<el-form :model="data.customAttrForm" label-width="80px">
<el-form-item label="属性名称" required>
<el-input v-model="data.customAttrForm.attrName" placeholder="请输入属性名称" />
</el-form-item>
<el-form-item label="属性值" required>
<el-input v-model="data.customAttrForm.attrValue" placeholder="请输入属性值" />
</el-form-item>
<el-form-item label="属性类型">
<el-select v-model="data.customAttrForm.attrType" style="width: 100%;">
<el-option label="字符串" value="string" />
<el-option label="数字" value="number" />
<el-option label="日期" value="date" />
<el-option label="布尔值" value="boolean" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="data.showCustomAttrDialog = false">取消</el-button>
<el-button type="primary" @click="saveCustomAttr">确定</el-button>
</div>
</template>
</el-dialog>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
......@@ -297,14 +431,70 @@
</div>
</template>
</el-dialog>
<!-- 文档预览对话框 -->
<el-dialog
v-model="previewDialogVisible"
title="文档预览"
width="80%"
height="80%"
:close-on-click-modal="false"
>
<div class="document-preview">
<vue-office-docx
v-if="currentDocumentType === 'docx'"
:src="currentDocumentUrl"
style="height: 100%;"
/>
<vue-office-xlsx
v-else-if="currentDocumentType === 'xlsx'"
:src="currentDocumentUrl"
style="height: 100%;"
/>
<vue-office-pdf
v-else-if="currentDocumentType === 'pdf'"
:src="currentDocumentUrl"
style="height: 100%;"
/>
</div>
</el-dialog>
<!-- 图片预览组件 -->
<el-image-viewer
v-if="imageViewerVisible"
:url-list="imageList"
:initial-index="imageIndex"
@close="imageViewerVisible = false"
/>
<!-- 供应商选择对话框 -->
<SupplierSelectDialog
ref="supplierSelectDialogRef"
v-model:visible="supplierDialogVisible"
@confirm="handleSupplierConfirm"
@cancel="handleSupplierCancel"
/>
</div>
</template>
<script setup name="WmsMaterial">
import { listWmsMaterial, getWmsMaterial, delWmsMaterial, addWmsMaterial, updateWmsMaterial } from "@/api/ware/wmsMaterial"
import { listWmsMaterialCategory } from "@/api/ware/wmsMaterialCategory"
import ImageUpload from "@/components/ImageUpload/index.vue"
import FileUpload from "@/components/FileUpload/index.vue"
import VueOfficeDocx from '@vue-office/docx'
import VueOfficeXlsx from '@vue-office/excel'
import VueOfficePdf from '@vue-office/pdf'
import SupplierSelectDialog from "@/components/SupplierSelectDialog.vue"
//引入相关样式
import '@vue-office/docx/lib/index.css'
import '@vue-office/excel/lib/index.css'
import { ElImageViewer } from 'element-plus'
const { proxy } = getCurrentInstance()
const { sys_normal_disable, custom_yes_no } = proxy.useDict('sys_normal_disable','custom_yes_no')
const wmsMaterialList = ref([])
const open = ref(false)
const loading = ref(true)
......@@ -314,9 +504,15 @@ const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")
const isQuery = ref(false)
// 分类树数据
const wmsMaterialCategoryOptions = ref([])
const data = reactive({
form: {},
form: {
customAttributes: []
},
queryParams: {
pageNum: 1,
pageSize: 10,
......@@ -350,17 +546,41 @@ const data = reactive({
{ required: true, message: "物资名称不能为空", trigger: "blur" }
],
categoryId: [
{ required: true, message: "分类ID不能为空", trigger: "blur" }
{ required: true, message: "物资分类不能为空", trigger: ["blur", "change"] }
],
unit: [
{ required: true, message: "计量单位不能为空", trigger: "blur" }
],
}
},
// 自定义属性相关
customAttrForm: {
attrName: '',
attrValue: '',
attrType: 'string'
},
showCustomAttrDialog: false
})
const { queryParams, form, rules } = toRefs(data)
const { queryParams, form, rules, customAttrForm, showCustomAttrDialog } = toRefs(data)
// 文档预览
const previewDialogVisible = ref(false)
const currentDocumentUrl = ref('')
const currentDocumentType = ref('')
// 图片预览
const imageViewerVisible = ref(false)
const imageList = ref([])
const imageIndex = ref(0)
// 供应商选择
const supplierDialogVisible = ref(false)
const supplierSelectDialogRef = ref(null)
const currentSupplierId = ref(null)
const currentSupplierName = ref('')
const selectedSupplier = ref(null)
/** 查询物资主数据列表 */
// 初始化加载物资列表
function getList() {
loading.value = true
listWmsMaterial(queryParams.value).then(response => {
......@@ -376,6 +596,90 @@ function cancel() {
reset()
}
// 文档预览处理
function handleDocumentPreview(url) {
// 为URL增加API前缀(如果尚未包含)
if (url && !url.startsWith('http') && !url.startsWith(import.meta.env.VITE_APP_BASE_API)) {
currentDocumentUrl.value = import.meta.env.VITE_APP_BASE_API + url
} else {
currentDocumentUrl.value = url
}
// 根据文件扩展名确定文档类型
const ext = url.split('.').pop().toLowerCase()
if (['docx', 'doc'].includes(ext)) {
currentDocumentType.value = 'docx'
} else if (['xlsx', 'xls'].includes(ext)) {
currentDocumentType.value = 'xlsx'
} else if (['pdf'].includes(ext)) {
currentDocumentType.value = 'pdf'
} else {
// 不支持的文档类型
proxy.$modal.msgError('不支持的文档类型')
return
}
previewDialogVisible.value = true
}
// 图片预览处理
function handleImagePreview(urls) {
// 为所有URL增加API前缀(如果尚未包含)
imageList.value = urls.map(item => {
if (item && !item.startsWith('http') && !item.startsWith(import.meta.env.VITE_APP_BASE_API)) {
return import.meta.env.VITE_APP_BASE_API + item
}
return item
})
// 更新当前索引
imageIndex.value = 0;
imageViewerVisible.value = true
}
// 供应商选择处理
function handleSupplierSelect(type) {
isQuery.value = type === 'query'
currentSupplierId.value = type === 'query' ? queryParams.value.supplierId : form.value.supplierId
supplierDialogVisible.value = true
supplierSelectDialogRef.value?.handleQuery()
}
// 供应商选择确认
function handleSupplierConfirm(selected) {
if (selected && selected.length > 0) {
const supplier = selected[0]
selectedSupplier.value = supplier
currentSupplierId.value = supplier.supplierId
currentSupplierName.value = supplier.supplierName
// 根据调用类型设置不同的目标
if (isQuery.value) {
// 来自查询表单
queryParams.value.supplierId = supplier.supplierId
} else {
// 来自编辑表单
form.value.supplierId = supplier.supplierId
}
}
supplierDialogVisible.value = false
}
// 供应商选择取消
function handleSupplierCancel() {
supplierDialogVisible.value = false
}
// 供应商选择清除
function handleSupplierClear(type) {
currentSupplierName.value = ''
currentSupplierId.value = null
selectedSupplier.value = null
if (type === 'query') {
queryParams.value.supplierId = null
} else {
form.value.supplierId = null
}
}
// 表单重置
function reset() {
form.value = {
......@@ -405,9 +709,19 @@ function reset() {
updateTime: null,
isRfidManaged: null,
rfidStandard: null,
rfidPosition: null
rfidPosition: null,
customAttributes: []
}
proxy.resetForm("wmsMaterialRef")
// 重置自定义属性表单
Object.assign(data.customAttrForm, {
attrName: '',
attrValue: '',
attrType: 'string'
})
delete data.customAttrForm._editingIndex
data.showCustomAttrDialog = false
proxy.resetForm("wmsMaterialRef")
}
/** 搜索按钮操作 */
......@@ -429,16 +743,31 @@ function handleSelectionChange(selection) {
multiple.value = !selection.length
}
/** 查询物资分类下拉树结构 */
function getTreeselect() {
return new Promise((resolve) => {
listWmsMaterialCategory().then(response => {
wmsMaterialCategoryOptions.value = []
const data = { categoryId: 0, categoryName: '顶级节点', children: [] }
data.children = proxy.handleTree(response.data, "categoryId", "parentId")
wmsMaterialCategoryOptions.value.push(data)
resolve()
})
})
}
/** 新增按钮操作 */
function handleAdd() {
reset()
getTreeselect()
open.value = true
title.value = "添加物资主数据"
}
/** 修改按钮操作 */
function handleUpdate(row) {
async function handleUpdate(row) {
reset()
await getTreeselect()
const _materialId = row.materialId || ids.value
getWmsMaterial(_materialId).then(response => {
form.value = response.data
......@@ -447,6 +776,46 @@ function handleUpdate(row) {
})
}
// 保存自定义属性
function saveCustomAttr() {
if (!data.customAttrForm.attrName || !data.customAttrForm.attrValue) {
proxy.$modal.msgError("属性名称和属性值不能为空")
return
}
const editingIndex = data.customAttrForm._editingIndex
if (editingIndex !== undefined) {
// 编辑现有属性
form.value.customAttributes[editingIndex] = { ...data.customAttrForm }
delete data.customAttrForm._editingIndex
} else {
// 添加新属性
form.value.customAttributes.push({ ...data.customAttrForm })
}
// 重置表单
Object.assign(data.customAttrForm, {
attrName: '',
attrValue: '',
attrType: 'string'
})
data.showCustomAttrDialog = false
}
// 编辑自定义属性
function editCustomAttr(row) {
const index = form.value.customAttributes.findIndex(item => item.attrName === row.attrName)
data.customAttrForm = { ...row, _editingIndex: index }
data.showCustomAttrDialog = true
}
// 删除自定义属性
function deleteCustomAttr(index) {
form.value.customAttributes.splice(index, 1)
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["wmsMaterialRef"].validate(valid => {
......@@ -486,5 +855,42 @@ function handleExport() {
}, `wmsMaterial_${new Date().getTime()}.xlsx`)
}
// 初始化加载分类树数据
getTreeselect()
// 初始化加载物资列表
getList()
</script>
<style scoped>
.image-preview-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.preview-image {
width: 80px;
height: 80px;
cursor: pointer;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.document-preview-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.preview-document-btn {
padding: 0;
height: auto;
line-height: normal;
}
.document-preview {
height: 600px;
overflow: auto;
}
</style>
......@@ -17,38 +17,7 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="父分类ID" prop="parentId">
<el-input
v-model="queryParams.parentId"
placeholder="请输入父分类ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="祖级列表" prop="ancestors">
<el-input
v-model="queryParams.ancestors"
placeholder="请输入祖级列表"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="分类级别" prop="categoryLevel">
<el-input
v-model="queryParams.categoryLevel"
placeholder="请输入分类级别"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="显示顺序" prop="sortOrder">
<el-input
v-model="queryParams.sortOrder"
placeholder="请输入显示顺序"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
......@@ -86,11 +55,13 @@
>
<el-table-column label="分类编码" prop="categoryCode" />
<el-table-column label="分类名称" align="center" prop="categoryName" />
<el-table-column label="父分类ID" align="center" prop="parentId" />
<el-table-column label="祖级列表" align="center" prop="ancestors" />
<el-table-column label="分类级别" align="center" prop="categoryLevel" />
<el-table-column label="显示顺序" align="center" prop="sortOrder" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="排序" align="center" prop="sortOrder" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="属性模板" align="center" prop="attributeTemplate" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
......@@ -123,7 +94,7 @@
<el-form-item label="分类级别" prop="categoryLevel">
<el-input v-model="form.categoryLevel" placeholder="请输入分类级别" />
</el-form-item>
<el-form-item label="显示顺序" prop="sortOrder">
<el-form-item label="序" prop="sortOrder">
<el-input v-model="form.sortOrder" placeholder="请输入显示顺序" />
</el-form-item>
<el-form-item label="属性模板" prop="attributeTemplate">
......@@ -146,6 +117,8 @@ import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const wmsMaterialCategoryList = ref([])
const wmsMaterialCategoryOptions = ref([])
const open = ref(false)
......
......@@ -41,10 +41,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -315,7 +315,7 @@
<el-table-column label="业务单号" align="center" prop="businessNo" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="操作数量" align="center" prop="operationQuantity" />
<el-table-column label="操作时间" align="center" prop="operationTime" width="180">
......@@ -391,8 +391,8 @@
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="出库单号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
......@@ -33,22 +33,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总数量" prop="totalQuantity">
<el-input
v-model="queryParams.totalQuantity"
placeholder="请输入总数量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总金额" prop="totalAmount">
<el-input
v-model="queryParams.totalAmount"
placeholder="请输入总金额"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="申请人" prop="applicantId">
<el-input
v-model="queryParams.applicantId"
......@@ -187,9 +171,12 @@
<el-table v-loading="loading" :data="wmsOutboundOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="出库单ID" align="center" prop="orderId" />
<el-table-column label="出库单号" align="center" prop="orderNo" />
<el-table-column label="出库类型" align="center" prop="orderType" />
<el-table-column label="出库单号" align="center" prop="orderNo" width="180" fixed="left" />
<el-table-column label="出库类型" align="center" prop="orderType" >
<template #default="scope">
<dict-tag :options="outbound_type" :value="scope.row.orderType" />
</template>
</el-table-column>
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="客户ID" align="center" prop="customerId" />
<el-table-column label="关联单号" align="center" prop="relatedOrderNo" />
......@@ -233,10 +220,11 @@
<el-table-column label="审批状态" align="center" prop="approvalStatus" />
<el-table-column label="出库策略" align="center" prop="outboundStrategy" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:remove']">删除</el-button>
<el-button link type="primary" icon="Document" @click="handlePrint(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:print']">打印</el-button>
</template>
</el-table-column>
</el-table>
......@@ -250,92 +238,239 @@
/>
<!-- 添加或修改出库单主对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsOutboundOrderRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="出库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入出库单号" />
</el-form-item>
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
</el-form-item>
<el-form-item label="客户ID" prop="customerId">
<el-input v-model="form.customerId" placeholder="请输入客户ID" />
</el-form-item>
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" />
</el-form-item>
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" />
</el-form-item>
<el-form-item label="申请人" prop="applicantId">
<el-input v-model="form.applicantId" placeholder="请输入申请人" />
</el-form-item>
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择申请时间">
</el-date-picker>
</el-form-item>
<el-form-item label="预计发货日期" prop="expectedDeliveryDate">
<el-date-picker clearable
v-model="form.expectedDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计发货日期">
</el-date-picker>
</el-form-item>
<el-form-item label="实际发货日期" prop="actualDeliveryDate">
<el-date-picker clearable
v-model="form.actualDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际发货日期">
</el-date-picker>
</el-form-item>
<el-form-item label="拣货员" prop="pickingPersonId">
<el-input v-model="form.pickingPersonId" placeholder="请输入拣货员" />
</el-form-item>
<el-form-item label="拣货时间" prop="pickingTime">
<el-date-picker clearable
v-model="form.pickingTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择拣货时间">
</el-date-picker>
</el-form-item>
<el-form-item label="复核员" prop="checkerId">
<el-input v-model="form.checkerId" placeholder="请输入复核员" />
</el-form-item>
<el-form-item label="复核时间" prop="checkTime">
<el-date-picker clearable
v-model="form.checkTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择复核时间">
</el-date-picker>
</el-form-item>
<el-form-item label="发货员" prop="deliveryPersonId">
<el-input v-model="form.deliveryPersonId" placeholder="请输入发货员" />
</el-form-item>
<el-form-item label="发货时间" prop="deliveryTime">
<el-date-picker clearable
v-model="form.deliveryTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择发货时间">
</el-date-picker>
</el-form-item>
<el-form-item label="出库策略" prop="outboundStrategy">
<el-input v-model="form.outboundStrategy" placeholder="请输入出库策略" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-dialog :title="title" v-model="open" width="1500px" append-to-body>
<el-form ref="wmsOutboundOrderRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="出库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入出库单号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="客户" prop="customerId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.customerId" placeholder="请选择客户" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openCustomerSelect">选择客户</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请人" prop="applicantId">
<el-input v-model="form.applicantId" placeholder="请输入申请人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择申请时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="预计发货日期" prop="expectedDeliveryDate">
<el-date-picker clearable
v-model="form.expectedDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计发货日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际发货日期" prop="actualDeliveryDate">
<el-date-picker clearable
v-model="form.actualDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际发货日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="拣货员" prop="pickingPersonId">
<el-input v-model="form.pickingPersonId" placeholder="请输入拣货员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="拣货时间" prop="pickingTime">
<el-date-picker clearable
v-model="form.pickingTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择拣货时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="复核员" prop="checkerId">
<el-input v-model="form.checkerId" placeholder="请输入复核员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="复核时间" prop="checkTime">
<el-date-picker clearable
v-model="form.checkTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择复核时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="发货员" prop="deliveryPersonId">
<el-input v-model="form.deliveryPersonId" placeholder="请输入发货员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="发货时间" prop="deliveryTime">
<el-date-picker clearable
v-model="form.deliveryTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择发货时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="出库策略" prop="outboundStrategy">
<el-input v-model="form.outboundStrategy" placeholder="请输入出库策略" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 出库单明细 -->
<el-divider>出库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="addDetail">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.details" border style="width: 100%">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.planQuantity" placeholder="请输入计划数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="actualQuantity" label="实际数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.actualQuantity" placeholder="请输入实际数量" size="small" />
</template>
</el-table-column>
<el-table-column prop="unit" label="单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入单位" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.unitPrice" placeholder="请输入单价" size="small" />
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.amount" placeholder="请输入金额" size="small" />
</template>
</el-table-column>
<el-table-column prop="batchNo" label="批次号" width="120">
<template #default="scope">
<el-input v-model="scope.row.batchNo" placeholder="请输入批次号" size="small" />
</template>
</el-table-column>
<el-table-column prop="productionDate" label="生产日期" width="150">
<template #default="scope">
<el-date-picker clearable
v-model="scope.row.productionDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择生产日期"
size="small">
</el-date-picker>
</template>
</el-table-column>
<el-table-column prop="expirationDate" label="失效日期" width="150">
<template #default="scope">
<el-date-picker clearable
v-model="scope.row.expirationDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择失效日期"
size="small">
</el-date-picker>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
......@@ -344,14 +479,44 @@
</div>
</template>
</el-dialog>
<!-- 物资选择对话框组件 -->
<MaterialSelectDialog
ref="materialSelectDialogRef"
v-model:visible="materialDialogVisible"
title="选择物资"
@confirm="handleMaterialConfirm"
/>
<!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
<!-- 客户选择对话框组件 -->
<CustomerSelectDialog
ref="customerSelectDialogRef"
v-model:visible="customerDialogVisible"
title="选择客户"
@confirm="handleCustomerConfirm"
/>
</div>
</template>
<script setup name="WmsOutboundOrder">
import { listWmsOutboundOrder, getWmsOutboundOrder, delWmsOutboundOrder, addWmsOutboundOrder, updateWmsOutboundOrder } from "@/api/ware/wmsOutboundOrder"
import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
import CustomerSelectDialog from "@/components/CustomerSelectDialog.vue"
import { listWmsMaterial } from "@/api/ware/wmsMaterial"
const { proxy } = getCurrentInstance()
const {outbound_type} = proxy.useDict('outbound_type')
const wmsOutboundOrderList = ref([])
const open = ref(false)
const loading = ref(true)
......@@ -362,6 +527,21 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 物资选择对话框状态
const materialDialogVisible = ref(false)
// 物资选择对话框实例引用
const materialSelectDialogRef = ref(null)
// 仓库选择对话框状态
const warehouseDialogVisible = ref(false)
// 仓库选择对话框实例引用
const warehouseSelectDialogRef = ref(null)
// 客户选择对话框状态
const customerDialogVisible = ref(false)
// 客户选择对话框实例引用
const customerSelectDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -424,7 +604,7 @@ function reset() {
form.value = {
orderId: null,
orderNo: null,
orderType: null,
orderType: '0',
warehouseId: null,
customerId: null,
relatedOrderNo: null,
......@@ -447,7 +627,8 @@ function reset() {
createBy: null,
createTime: null,
updateBy: null,
updateTime: null
updateTime: null,
details: []
}
proxy.resetForm("wmsOutboundOrderRef")
}
......@@ -478,6 +659,40 @@ function handleAdd() {
title.value = "添加出库单主"
}
/** 打开仓库选择对话框 */
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
/** 处理仓库选择结果 */
function handleWarehouseConfirm(selectedWarehouses) {
if (selectedWarehouses.length > 0) {
const warehouse = selectedWarehouses[0]
form.value.warehouseId = warehouse.warehouseId
// 可以根据需要添加更多仓库信息字段
}
}
/** 打开客户选择对话框 */
function openCustomerSelect() {
customerDialogVisible.value = true
if (customerSelectDialogRef.value) {
customerSelectDialogRef.value.handleQuery()
}
}
/** 处理客户选择结果 */
function handleCustomerConfirm(selectedCustomers) {
if (selectedCustomers.length > 0) {
const customer = selectedCustomers[0]
form.value.customerId = customer.customerId
// 可以根据需要添加更多客户信息字段
}
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset()
......@@ -491,8 +706,34 @@ function handleUpdate(row) {
/** 提交按钮 */
function submitForm() {
// 表单基础验证
proxy.$refs["wmsOutboundOrderRef"].validate(valid => {
if (valid) {
// 验证出库单明细
if (!form.value.details || form.value.details.length === 0) {
proxy.$modal.msgError("请至少添加一条出库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.details.length; i++) {
const detail = form.value.details[i]
if (!detail.materialId) {
proxy.$modal.msgError(`第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交数据
if (form.value.orderId != null) {
updateWmsOutboundOrder(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
......@@ -510,6 +751,48 @@ function submitForm() {
})
}
/** 添加明细 */
function addDetail() {
if (!form.value.details) {
form.value.details = []
}
// 打开物资选择对话框
materialDialogVisible.value = true
// 调用组件方法加载物资分类和列表
materialSelectDialogRef.value.getTreeselect()
materialSelectDialogRef.value.handleQuery()
}
/** 处理物资选择确认 */
function handleMaterialConfirm(selectedMaterials) {
if (selectedMaterials.length === 0) {
proxy.$modal.msgWarning("请至少选择一个物资")
return
}
// 将选中的物资添加到明细列表
selectedMaterials.forEach(material => {
form.value.details.push({
materialId: material.materialId,
materialName: material.materialName,
planQuantity: null,
actualQuantity: null,
unit: material.unit,
unitPrice: null,
amount: null,
batchNo: null,
productionDate: null,
expirationDate: null,
remark: null
})
})
}
/** 删除明细 */
function deleteDetail(index) {
form.value.details.splice(index, 1)
}
/** 删除按钮操作 */
function handleDelete(row) {
const _orderIds = row.orderId || ids.value
......@@ -528,5 +811,177 @@ function handleExport() {
}, `wmsOutboundOrder_${new Date().getTime()}.xlsx`)
}
/** 打印按钮操作 */
function handlePrint(row) {
// 获取完整的出库单数据,包括明细
getWmsOutboundOrder(row.orderId).then(response => {
const orderData = response.data
// 创建打印窗口
const printWindow = window.open('', '_blank')
// 构建打印内容
let printContent = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>出库单打印</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
padding: 20px;
border: 1px solid #ccc;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.order-info {
margin-bottom: 20px;
padding: 10px;
border: 1px solid #eee;
}
.info-row {
display: flex;
margin-bottom: 10px;
}
.info-label {
width: 120px;
font-weight: bold;
margin-right: 10px;
}
.table-container {
margin-top: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.footer {
margin-top: 30px;
text-align: right;
}
</style>
</head>
<body>
<h1>出库单</h1>
<div class="order-info">
<div class="info-row">
<span class="info-label">出库单号:</span>
<span>${orderData.orderNo}</span>
</div>
<div class="info-row">
<span class="info-label">仓库:</span>
<span>${orderData.warehouseId}</span>
</div>
<div class="info-row">
<span class="info-label">客户:</span>
<span>${orderData.customerId}</span>
</div>
<div class="info-row">
<span class="info-label">总数量:</span>
<span>${orderData.totalQuantity}</span>
</div>
<div class="info-row">
<span class="info-label">总金额:</span>
<span>${orderData.totalAmount}</span>
</div>
<div class="info-row">
<span class="info-label">申请人:</span>
<span>${orderData.applicantId}</span>
</div>
<div class="info-row">
<span class="info-label">申请时间:</span>
<span>${orderData.applyTime ? new Date(orderData.applyTime).toLocaleDateString() : ''}</span>
</div>
<div class="info-row">
<span class="info-label">备注:</span>
<span>${orderData.remark || ''}</span>
</div>
</div>
<div class="table-container">
<h3>出库单明细</h3>
<table>
<thead>
<tr>
<th>物资名称</th>
<th>计划数量</th>
<th>实际数量</th>
<th>单位</th>
<th>单价</th>
<th>金额</th>
<th>批次号</th>
<th>生产日期</th>
<th>失效日期</th>
<th>备注</th>
</tr>
</thead>
<tbody>
`
// 添加明细数据
if (orderData.details && orderData.details.length > 0) {
orderData.details.forEach(detail => {
printContent += `
<tr>
<td>${detail.materialName}</td>
<td>${detail.planQuantity}</td>
<td>${detail.actualQuantity}</td>
<td>${detail.unit}</td>
<td>${detail.unitPrice}</td>
<td>${detail.amount}</td>
<td>${detail.batchNo || ''}</td>
<td>${detail.productionDate ? new Date(detail.productionDate).toLocaleDateString() : ''}</td>
<td>${detail.expirationDate ? new Date(detail.expirationDate).toLocaleDateString() : ''}</td>
<td>${detail.remark || ''}</td>
</tr>
`
})
} else {
printContent += `
<tr>
<td colspan="10">无明细数据</td>
</tr>
`
}
// 完成打印内容
printContent += `
</tbody>
</table>
</div>
<div class="footer">
<p>打印时间:${new Date().toLocaleString()}</p>
</div>
</body>
</html>
`
// 写入打印窗口
printWindow.document.write(printContent)
printWindow.document.close()
// 执行打印
printWindow.print()
// 关闭打印窗口
printWindow.close()
})
}
getList()
</script>
......@@ -65,10 +65,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="拣货库位" prop="pickingLocation">
<el-form-item label="拣货货架" prop="pickingLocation">
<el-input
v-model="queryParams.pickingLocation"
placeholder="请输入拣货库位"
placeholder="请输入拣货货架"
clearable
@keyup.enter="handleQuery"
/>
......@@ -156,7 +156,7 @@
<el-table-column label="单价" align="center" prop="unitPrice" />
<el-table-column label="金额" align="center" prop="amount" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="拣货库位" align="center" prop="pickingLocation" />
<el-table-column label="拣货货架" align="center" prop="pickingLocation" />
<el-table-column label="已拣数量" align="center" prop="pickingQuantity" />
<el-table-column label="拣货状态" align="center" prop="pickingStatus" />
<el-table-column label="复核数量" align="center" prop="checkQuantity" />
......@@ -207,8 +207,8 @@
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
</el-form-item>
<el-form-item label="拣货库位" prop="pickingLocation">
<el-input v-model="form.pickingLocation" placeholder="请输入拣货库位" />
<el-form-item label="拣货货架" prop="pickingLocation">
<el-input v-model="form.pickingLocation" placeholder="请输入拣货货架" />
</el-form-item>
<el-form-item label="已拣数量" prop="pickingQuantity">
<el-input v-model="form.pickingQuantity" placeholder="请输入已拣数量" />
......
......@@ -115,9 +115,8 @@
<el-table v-loading="loading" :data="wmsReturnOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="退库单ID" align="center" prop="orderId" />
<el-table-column label="退库单号" align="center" prop="orderNo" />
<el-table-column label="原出库单号" align="center" prop="outboundOrderNo" />
<el-table-column label="退库单号" align="center" prop="orderNo" width="150" />
<el-table-column label="原出库单号" align="center" prop="outboundOrderNo" width="150" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="退库原因" align="center" prop="returnReason" />
<el-table-column label="退库类型" align="center" prop="returnType" />
......@@ -133,7 +132,7 @@
<el-table-column label="质检状态" align="center" prop="qualityStatus" />
<el-table-column label="审批状态" align="center" prop="approvalStatus" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsReturnOrder:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsReturnOrder:remove']">删除</el-button>
......@@ -156,13 +155,27 @@
<el-input v-model="form.orderNo" placeholder="请输入退库单号" />
</el-form-item>
<el-form-item label="原出库单号" prop="outboundOrderNo">
<el-input v-model="form.outboundOrderNo" placeholder="请输入原出库单号" />
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.outboundOrderNo" placeholder="请选择原出库单号" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openOutboundOrderSelect">选择出库单</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="仓库ID" prop="warehouseId">
<el-input v-model="form.warehouseId" placeholder="请输入仓库ID" />
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseId" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="退库原因" prop="returnReason">
<el-input v-model="form.returnReason" placeholder="请输入退库原因" />
<el-input type="textarea" v-model="form.returnReason" placeholder="请输入退库原因" />
</el-form-item>
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" />
......@@ -192,11 +205,29 @@
</div>
</template>
</el-dialog>
<!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog
ref="warehouseSelectDialogRef"
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
<!-- 出库单选择对话框组件 -->
<OutboundOrderSelectDialog
ref="outboundOrderSelectDialogRef"
v-model:visible="outboundOrderDialogVisible"
title="选择出库单"
@confirm="handleOutboundOrderConfirm"
/>
</div>
</template>
<script setup name="WmsReturnOrder">
import { listWmsReturnOrder, getWmsReturnOrder, delWmsReturnOrder, addWmsReturnOrder, updateWmsReturnOrder } from "@/api/ware/wmsReturnOrder"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
import OutboundOrderSelectDialog from "@/components/OutboundOrderSelectDialog.vue"
const { proxy } = getCurrentInstance()
......@@ -210,6 +241,16 @@ const multiple = ref(true)
const total = ref(0)
const title = ref("")
// 仓库选择对话框状态
const warehouseDialogVisible = ref(false)
// 仓库选择对话框实例引用
const warehouseSelectDialogRef = ref(null)
// 出库单选择对话框状态
const outboundOrderDialogVisible = ref(false)
// 出库单选择对话框实例引用
const outboundOrderSelectDialogRef = ref(null)
const data = reactive({
form: {},
queryParams: {
......@@ -232,8 +273,11 @@ const data = reactive({
orderNo: [
{ required: true, message: "退库单号不能为空", trigger: "blur" }
],
outboundOrderNo: [
{ required: true, message: "原出库单号不能为空", trigger: "change" }
],
warehouseId: [
{ required: true, message: "仓库ID不能为空", trigger: "blur" }
{ required: true, message: "仓库ID不能为空", trigger: "change" }
],
returnReason: [
{ required: true, message: "退库原因不能为空", trigger: "blur" }
......@@ -360,5 +404,40 @@ function handleExport() {
}, `wmsReturnOrder_${new Date().getTime()}.xlsx`)
}
/** 打开仓库选择对话框 */
function openWarehouseSelect() {
warehouseDialogVisible.value = true
if (warehouseSelectDialogRef.value) {
warehouseSelectDialogRef.value.handleQuery()
}
}
/** 处理仓库选择结果 */
function handleWarehouseConfirm(selectedWarehouses) {
if (selectedWarehouses.length > 0) {
const warehouse = selectedWarehouses[0]
form.value.warehouseId = warehouse.warehouseId
// 可以根据需要添加更多仓库信息字段
}
}
/** 打开出库单选择对话框 */
function openOutboundOrderSelect() {
outboundOrderDialogVisible.value = true
if (outboundOrderSelectDialogRef.value) {
outboundOrderSelectDialogRef.value.handleQuery()
}
}
/** 处理出库单选择结果 */
function handleOutboundOrderConfirm(selectedOrders) {
if (selectedOrders.length > 0) {
const order = selectedOrders[0]
form.value.outboundOrderNo = order.orderNo
// 可以根据需要添加更多出库单信息字段,比如自动填充仓库ID
form.value.warehouseId = order.warehouseId
}
}
getList()
</script>
......@@ -33,10 +33,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -192,7 +192,7 @@
<el-table-column label="读写器ID" align="center" prop="readerId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库区ID" align="center" prop="areaId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="安装位置" align="center" prop="installationLocation" />
<el-table-column label="X坐标" align="center" prop="xCoordinate" />
<el-table-column label="Y坐标" align="center" prop="yCoordinate" />
......@@ -242,8 +242,8 @@
<el-form-item label="库区ID" prop="areaId">
<el-input v-model="form.areaId" placeholder="请输入库区ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="安装位置" prop="installationLocation">
<el-input v-model="form.installationLocation" placeholder="请输入安装位置" />
......
......@@ -33,10 +33,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-form-item label="货架ID" prop="locationId">
<el-input
v-model="queryParams.locationId"
placeholder="请输入库位ID"
placeholder="请输入货架ID"
clearable
@keyup.enter="handleQuery"
/>
......@@ -208,7 +208,7 @@
<el-table-column label="RFID卡ID" align="center" prop="cardId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="库区ID" align="center" prop="areaId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="检测时间" align="center" prop="detectTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.detectTime, '{y}-{m}-{d}') }}</span>
......@@ -260,8 +260,8 @@
<el-form-item label="库区ID" prop="areaId">
<el-input v-model="form.areaId" placeholder="请输入库区ID" />
</el-form-item>
<el-form-item label="库位ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入库位ID" />
<el-form-item label="货架ID" prop="locationId">
<el-input v-model="form.locationId" placeholder="请输入货架ID" />
</el-form-item>
<el-form-item label="检测时间" prop="detectTime">
<el-date-picker clearable
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="供应商编码" prop="supplierCode">
<el-input
v-model="queryParams.supplierCode"
......@@ -97,7 +97,11 @@
<el-table-column label="联系人" align="center" prop="contactPerson" />
<el-table-column label="联系电话" align="center" prop="contactPhone" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
......@@ -117,7 +121,7 @@
<!-- 添加或修改供应商对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsSupplierRef" :model="form" :rules="rules" label-width="80px">
<el-form ref="wmsSupplierRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="供应商编码" prop="supplierCode">
<el-input v-model="form.supplierCode" placeholder="请输入供应商编码" />
</el-form-item>
......@@ -152,6 +156,8 @@ import { listWmsSupplier, getWmsSupplier, delWmsSupplier, addWmsSupplier, update
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const wmsSupplierList = ref([])
const open = ref(false)
const loading = ref(true)
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="仓库编码" prop="warehouseCode">
<el-input
v-model="queryParams.warehouseCode"
......@@ -33,10 +33,10 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库位置" prop="location">
<el-form-item label="仓货架置" prop="location">
<el-input
v-model="queryParams.location"
placeholder="请输入仓库位置"
placeholder="请输入仓货架置"
clearable
@keyup.enter="handleQuery"
/>
......@@ -105,9 +105,13 @@
<el-table-column label="仓库类型" align="center" prop="warehouseType" />
<el-table-column label="仓库面积" align="center" prop="area" />
<el-table-column label="仓库容量" align="center" prop="capacity" />
<el-table-column label="仓库位置" align="center" prop="location" />
<el-table-column label="仓货架置" align="center" prop="location" />
<el-table-column label="仓库管理员" align="center" prop="managerId" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
......@@ -127,7 +131,7 @@
<!-- 添加或修改仓库信息对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="wmsWarehouseRef" :model="form" :rules="rules" label-width="80px">
<el-form ref="wmsWarehouseRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="仓库编码" prop="warehouseCode">
<el-input v-model="form.warehouseCode" placeholder="请输入仓库编码" />
</el-form-item>
......@@ -140,8 +144,8 @@
<el-form-item label="仓库容量" prop="capacity">
<el-input v-model="form.capacity" placeholder="请输入仓库容量" />
</el-form-item>
<el-form-item label="仓库位置" prop="location">
<el-input v-model="form.location" placeholder="请输入仓库位置" />
<el-form-item label="仓货架置" prop="location">
<el-input v-model="form.location" placeholder="请输入仓货架置" />
</el-form-item>
<el-form-item label="仓库管理员" prop="managerId">
<el-input v-model="form.managerId" placeholder="请输入仓库管理员" />
......@@ -165,6 +169,8 @@ import { listWmsWarehouse, getWmsWarehouse, delWmsWarehouse, addWmsWarehouse, up
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const wmsWarehouseList = ref([])
const open = ref(false)
const loading = ref(true)
......
......@@ -42,7 +42,7 @@ export default defineConfig(({ mode, command }) => {
},
// vite 相关配置
server: {
port: 80,
port: 8851,
host: true,
open: true,
proxy: {
......
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