feat(inventory): 新增发货记录和产品库存管理功能- 新增发货记录模块,支持查询、新增、修改、删除、撤回及导出功能- 新增产品库存管理页面,支持库存查询、入库出库记录查看- 新增发货记录详情抽屉组件,展示发货单详情及关联SN码列表

- 新增订单执行跟踪路由及权限配置
- 优化 GenerateDeliveryForm 组件导入逻辑及选择控制
- 新增产品库存列表实时库存颜色标识及排序功能- 新增入库记录和出库记录弹窗展示功能- 完善库存信息相关API接口封装
dev_1.0.0
chenhao 2025-11-18 11:42:47 +08:00
parent 1b3b23a63e
commit 253cbdb5fb
18 changed files with 2004 additions and 18 deletions

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
// 获取所有仓库列表
export function listAllWarehouse() {
return request({
url: '/warehouse/info/vue/listAll',
method: 'get'
})
}

View File

@ -0,0 +1,95 @@
import request from '@/utils/request'
// 查询发货记录列表
export function listDelivery(query) {
return request({
url: '/inventory/delivery/vue/list',
method: 'get',
params: query
})
}
// 查询发货记录详细
export function getDelivery(id) {
return request({
url: '/inventory/delivery/vue/' + id,
method: 'get'
})
}
// 新增发货记录
export function addDelivery(data) {
return request({
url: '/inventory/delivery/vue',
method: 'post',
data: data
})
}
// 修改发货记录
export function updateDelivery(data) {
return request({
url: '/inventory/delivery/vue',
method: 'put',
data: data
})
}
// 删除发货记录
export function delDelivery(id) {
return request({
url: '/inventory/delivery/vue/' + id,
method: 'delete'
})
}
// 撤回发货记录
export function recallDelivery(id) {
return request({
url: '/inventory/delivery/vue/recall',
method: 'post',
data: {id}
})
}
// 导出采购合同
export function exportDelivery(query) {
return request({
url: '/inventory/delivery/vue/export',
method: 'post',
data: query
})
}
// 导出采购合同详情
export function exportDeliveryDetail(id) {
return request({
url: '/inventory/delivery/vue/detail/export',
method: 'post',
data: {id}
})
}
// 查询关联的SN码列表
export function listProductSn(query) {
return request({
url: '/inventory/info/vue/list',
method: 'get',
params: query
})
}
export function removeDelivery(id) {
return request({
url: '/inventory/delivery/vue/' + id,
method: 'delete'
})
}
export function updateDeliveryStatus(data) {
return request({
url: '/inventory/delivery/vue/status' ,
method: 'put',
data: data
})
}

View File

@ -0,0 +1,107 @@
import request from '@/utils/request'
// 查询订单执行列表
export function listExecution(query) {
return request({
url: '/inventory/execution/vue/list',
method: 'get',
params: query
})
}
// 查询订单执行详细
export function getExecution(id) {
return request({
url: '/inventory/execution/vue/' + id,
method: 'get'
})
}
// 新增订单执行
export function addExecution(data) {
return request({
url: '/inventory/execution/vue',
method: 'post',
data: data
})
}
// 修改订单执行
export function updateExecution(data) {
return request({
url: '/inventory/execution/vue',
method: 'put',
data: data
})
}
// 删除订单执行
export function delExecution(id) {
return request({
url: '/inventory/execution/vue/' + id,
method: 'delete'
})
}
// 订单执行签收
export function signExecution(data) {
return request({
url: '/inventory/execution/vue/sign',
method: 'post',
data: data
})
}
// 下载签收文件
export function downloadSignFile(orderId, versionCode) {
return request({
url: `/inventory/execution/vue/sign/download`,
method: 'get',
params: { orderId, versionCode },
responseType: 'blob'
})
}
// 撤单
export function recallExecution(id) {
return request({
url: `/inventory/execution/vue/recall`,
method: 'post',
data: { id }
})
}
// 查询出库预览信息
export function getCheckOutPreview(data) {
return request({
url: '/inventory/execution/vue/checkOut/preview',
method: 'post',
data: data
})
}
// 新增出库单
export function addOuter(data) {
return request({
url: '/inventory/outer/vue',
method: 'post',
data: data
})
}
// 撤销出库单
export function removeOuter(id) {
return request({
url: '/inventory/outer/vue/' + id,
method: 'delete'
})
}
// 确认出库
export function confirmOuterStatus(id, orderCode) {
return request({
url: '/inventory/outer/vue/status',
method: 'post',
data: { id, outerStatus: '2', orderCode }
})
}

View File

@ -0,0 +1,87 @@
import request from '@/utils/request'
// 查询产品库存列表
export function listInfo(query) {
return request({
url: '/inventory/info/vue/group/list',
method: 'get',
params: query
})
}
// 查询产品库存详细
export function getInfo(id) {
return request({
url: '/inventory/info/vue/' + id,
method: 'get'
})
}
// 新增产品库存
export function addInfo(data) {
return request({
url: '/inventory/info/vue',
method: 'post',
data: data
})
}
// 修改产品库存
export function updateInfo(data) {
return request({
url: '/inventory/info/vue',
method: 'put',
data: data
})
}
// 删除产品库存
export function delInfo(id) {
return request({
url: '/inventory/info/vue/' + id,
method: 'delete'
})
}
// 导出产品库存
export function exportInfo(query) {
return request({
url: '/inventory/info/vue/export',
method: 'post',
data: query
})
}
// 获取所有制造商列表
export function listAllVendors() {
return request({
url: '/inventory/info/vue/listAllVendors',
method: 'get'
})
}
// 获取所有正常状态的仓库列表
export function listAllWarehouses() {
return request({
url: '/inventory/info/vue/listAllWarehouses',
method: 'get'
})
}
// 查询入库日志
export function getInnerLog(query) {
return request({
url: '/inventory/info/vue/inner-log',
method: 'get',
params: query
})
}
// 查询出库日志
export function getOuterLog(query) {
return request({
url: '/inventory/info/vue/outer-log',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,74 @@
import request from '@/utils/request'
const VUE_APP_API_URL = '/inventory/inner/vue';
// 查询入库单信息列表
export function listInner(query) {
return request({
url: `${VUE_APP_API_URL}/list`,
method: 'get',
params: query
})
}
// 查询入库单信息详细
export function getInner(id) {
return request({
url: `${VUE_APP_API_URL}/${id}`,
method: 'get'
})
}
// 新增入库单信息
export function addInner(data) {
return request({
url: VUE_APP_API_URL,
method: 'post',
data: data
})
}
// 修改入库单信息
export function updateInner(data) {
return request({
url: VUE_APP_API_URL,
method: 'put',
data: data
})
}
// 删除入库单信息
export function delInner(id) {
return request({
url: `${VUE_APP_API_URL}/${id}`,
method: 'delete'
})
}
// 导出入库单信息
export function exportInner(query) {
return request({
url: `${VUE_APP_API_URL}/export`,
method: 'post',
data: query
})
}
// 导入产品数据
export function importProductData(data) {
return request({
url: `${VUE_APP_API_URL}/importData`,
method: 'post',
data: data
// Content-Type 会被自动设置为 multipart/form-data
})
}
// 下载导入模板
export function downloadTemplate() {
return request({
url: `${VUE_APP_API_URL}/export/template`,
method: 'post',
responseType: 'blob'
})
}

View File

@ -0,0 +1,85 @@
import request from '@/utils/request'
// 查询出库单列表
export function listOuter(query) {
return request({
url: '/inventory/outer/vue',
method: 'get',
params: query
})
}
// 查询出库单详细
export function getOuter(id) {
return request({
url: '/inventory/outer/vue/' + id,
method: 'get'
})
}
// 新增出库单
export function addOuter(data) {
return request({
url: '/inventory/outer/vue',
method: 'post',
data: data
})
}
// 修改出库单
export function updateOuter(data) {
return request({
url: '/inventory/outer/vue',
method: 'put',
data: data
})
}
// 删除出库单
export function delOuter(id) {
return request({
url: '/inventory/outer/vue/' + id,
method: 'delete'
})
}
// 导出出库单
export function exportOuter(query) {
return request({
url: '/inventory/outer/vue/export',
method: 'post',
params: query
})
}
// 修改出库单状态
export function changeOuterStatus(id, outerStatus, orderCode) {
const data = {
id,
outerStatus,
orderCode
}
return request({
url: '/inventory/outer/vue/status',
method: 'post',
data: data
})
}
// 查询出库单详细(用于刷新产品和发货列表)
export function queryInfo(id) {
return request({
url: '/inventory/outer/vue/' + id,
method: 'get'
})
}
export function importSnData(formData) {
return request({
url: '/inventory/outer/vue/importData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData
})
}

View File

@ -121,6 +121,20 @@ export const dynamicRoutes = [
name: 'Execution',
meta: { title: '订单执行跟踪', icon: 'execution' },
permissions: ['inventory:execution:list']
},
{
path: 'info',
component: () => import('@/views/inventory/info/index'),
name: 'InventoryInfo',
meta: { title: '产品库存', icon: 'product' },
permissions: ['inventory:info:list']
},
{
path: 'delivery',
component: () => import('@/views/inventory/delivery/index'),
name: 'Delivery',
meta: { title: '发货记录', icon: 'logistics' },
permissions: ['inventory:delivery:list']
}
]
},

View File

@ -0,0 +1,212 @@
<template>
<el-drawer title="发货单详情" :visible.sync="visible" direction="rtl" size="60%" @close="handleClose" append-to-body>
<div class="app-container">
<el-form :model="detail" label-width="100px" disabled>
<div
style="display: flex; justify-content: space-between; padding: 10px 20px; font-family: '微软雅黑', Arial, sans-serif;">
<div style="font-weight: bold; font-size: 24px;">发货单</div>
<div style="font-size: 16px;">
<span>要求发货时间: </span>
<span>{{ parseTime(detail.deliveryTime, '{y}-{m}-{d}') }}</span>
</div>
</div>
<el-row>
<el-col :span="6">
<el-form-item label="物流单号" prop="logisticsCode">
<el-input v-model="detail.logisticsCode"/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发货方式" prop="deliveryType">
<el-select v-model="detail.deliveryType" placeholder="请选择发货方式">
<el-option v-for="item in dict.type.delivery_type" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="物流公司" prop="logisticsCompany">
<el-select v-model="detail.logisticsCompany" placeholder="请选择发货方式">
<el-option v-for="item in dict.type.logistics_company" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item label="发货人" prop="createByName">
<el-input v-model="detail.createByName"/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发货时间" prop="deliveryTime">
<el-input v-model="detail.deliveryTime"/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="备注" prop="remark">
<el-input v-model="detail.remark"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExportDetail"
v-hasPermi="['inventory:delivery:export']">导出
</el-button>
</el-col>
</el-row>
<h4>关联SN码列表</h4>
<el-table v-loading="snLoading" :data="snList">
<el-table-column label="SN码" align="center" prop="productSn"/>
<el-table-column label="产品编码" align="center" prop="productCode"/>
<el-table-column label="产品型号" align="center" prop="model"/>
<el-table-column label="描述" align="center" prop="productDesc"/>
<el-table-column label="仓库" align="center" prop="warehouseName"/>
</el-table>
<pagination v-show="snTotal>0" :total="snTotal" :page.sync="snQueryParams.pageNum"
:limit.sync="snQueryParams.pageSize" @pagination="getSnList"/>
</div>
</el-drawer>
</template>
<script>
import {getDelivery, exportDeliveryDetail, listProductSn} from "@/api/inventory/delivery";
export default {
name: "DeliveryDetail",
dicts: ['delivery_type', 'logistics_company'],
props: {
deliveryId: {
type: Number,
default: null
},
visible: {
type: Boolean,
default: false
}
},
data() {
return {
//
snLoading: false,
//
detail: {},
// SN
snList: [],
// SN
snTotal: 0,
snQueryParams: {
pageNum: 1,
pageSize: 10,
deliveryId: null,
inventoryStatus: '1',
outerCode: null,
warehouseId: null,
productSnList: []
},
};
},
watch: {
deliveryId(newVal) {
if (newVal) {
this.handleView(newVal);
}
}
},
methods: {
handleView(id) {
this.detail = {};
this.snList = [];
this.snTotal = 0;
this.snQueryParams = {
pageNum: 1,
pageSize: 10,
deliveryId: id,
inventoryStatus: '1',
outerCode: null,
warehouseId: null,
productSnList: []
};
getDelivery(id).then(response => {
this.detail = response.data;
// Update snQueryParams with data from the detail response
this.snQueryParams.outerCode = response.data.outerCode;
this.snQueryParams.warehouseId = response.data.warehouseId;
this.snQueryParams.productSnList = response.data.productSnList;
// Fetch the first page of SN list
this.getSnList();
});
},
/** 查询关联SN码列表 */
getSnList() {
this.snLoading = true;
listProductSn(this.snQueryParams).then(snResponse => {
this.snList = snResponse.rows;
this.snTotal = snResponse.total;
this.snLoading = false;
});
},
/** 导出详情按钮操作 */
handleExportDetail() {
const id = this.detail.id;
this.$confirm('是否确认导出该发货单详情?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
return exportDeliveryDetail(id);
}).then(response => {
const fileName = response.msg;
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(fileName) + "&delete=" + true;
});
},
handleClose() {
this.$emit('update:visible', false);
},
parseTime(time, pattern) {
if (time === undefined || time === null || time === '') {
return ''
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
}
};
</script>

View File

@ -0,0 +1,288 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="出库单号" prop="outerCode">
<el-input v-model="queryParams.outerCode" placeholder="请输入出库单号" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="物流单号" prop="logisticsCode">
<el-input v-model="queryParams.logisticsCode" placeholder="请输入物流单号" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="合同编号" prop="orderCode">
<el-input v-model="queryParams.orderCode" placeholder="请输入合同编号" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="发货方式" prop="deliveryType">
<el-select v-model="queryParams.deliveryType" placeholder="请选择发货方式" clearable size="small">
<el-option v-for="item in dict.type.delivery_type" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">-->
<!-- <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['inventory:delivery:add']"></el-button>-->
<!-- </el-col>-->
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['inventory:delivery:export']">导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="deliveryList">
<el-table-column label="序号" type="index" width="50" align="center"/>
<el-table-column label="出库单号" align="center" prop="outerCode" show-overflow-tooltip/>
<el-table-column label="物流单号" align="center" prop="logisticsCode" show-overflow-tooltip/>
<el-table-column label="项目编号" align="center" prop="projectCode" show-overflow-tooltip/>
<el-table-column label="合同编号" align="center" prop="orderCode" show-overflow-tooltip/>
<el-table-column label="项目名称" align="center" prop="projectName" show-overflow-tooltip/>
<el-table-column label="产品编码" align="center" prop="productCode" show-overflow-tooltip/>
<el-table-column label="产品型号" align="center" prop="model" show-overflow-tooltip/>
<el-table-column label="发货数量" align="center" prop="quantity"/>
<el-table-column label="发货方式" align="center" prop="deliveryType">
<template slot-scope="scope">
<dict-tag :options="dict.type.delivery_type" :value="scope.row.deliveryType"/>
</template>
</el-table-column>
<el-table-column label="发货时间" align="center" prop="deliveryTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.deliveryTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-document" @click="handleView(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-refresh-left" @click="handleRecall(scope.row)"
v-hasPermi="['inventory:delivery:recall']">撤回
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 新增/修改对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="出库单号" prop="outerCode">
<el-input v-model="form.outerCode" placeholder="请输入出库单号"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属仓库" prop="warehouseId">
<!-- To-Do: Should be a warehouse selector component -->
<el-input v-model="form.warehouseId" placeholder="请输入所属仓库ID"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="物流公司" prop="logisticsCompany">
<el-input v-model="form.logisticsCompany" placeholder="请输入物流公司"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物流单号" prop="logisticsCode">
<el-input v-model="form.logisticsCode" placeholder="请输入物流单号"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="发货时间" prop="deliveryTime">
<el-date-picker clearable size="small" v-model="form.deliveryTime" type="date" value-format="yyyy-MM-dd"
placeholder="选择发货时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 查看详情抽屉 -->
<delivery-detail :delivery-id="deliveryId" :visible.sync="viewOpen"/>
</div>
</template>
<script>
import {
listDelivery,
addDelivery,
updateDelivery,
recallDelivery,
exportDelivery,
} from "@/api/inventory/delivery";
import DeliveryDetail from "./Detail";
export default {
name: "Delivery",
components: {DeliveryDetail},
dicts: ['delivery_type', 'logistics_company'],
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
deliveryList: [],
//
title: "",
//
open: false,
//
viewOpen: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
outerCode: null,
logisticsCode: null,
projectCode: null,
orderCode: null,
projectName: null,
deliveryType: null,
},
//
form: {},
//
deliveryId: null,
//
rules: {},
};
},
created() {
this.getList();
},
methods: {
/** 查询发货记录列表 */
getList() {
this.loading = true;
listDelivery(this.queryParams).then(response => {
this.deliveryList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加发货记录";
},
/** 查看详情按钮操作 */
handleView(row) {
this.deliveryId = row.id;
this.viewOpen = true;
},
/** 撤回按钮操作 */
handleRecall(row) {
const id = row.id;
this.$confirm('撤回该发货记录后无法恢复,操作不可逆转,确认无误后再执行!', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
return recallDelivery(id);
}).then(() => {
this.getList();
this.msgSuccess("撤回成功");
})
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有发货记录数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
return exportDelivery(queryParams);
}).then(response => {
const fileName = response.msg;
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(fileName) + "&delete=" + true;
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateDelivery(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addDelivery(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
outerCode: null,
warehouseId: null,
logisticsCompany: null,
logisticsCode: null,
deliveryTime: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
remark: null
};
this.resetForm("form");
}
}
};
</script>

View File

@ -0,0 +1,459 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
<el-form-item label="产品编码" prop="productCode">
<el-input v-model="queryParams.productCode" placeholder="请输入产品编码" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="产品型号" prop="model">
<el-input v-model="queryParams.model" placeholder="请输入产品型号" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable size="small" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="制造商" prop="vendorCode">
<el-select v-model="queryParams.vendorCode" placeholder="请选择制造商" clearable size="small">
<el-option v-for="vendor in vendorOptions" :key="vendor.vendorCode" :label="vendor.vendorName" :value="vendor.vendorCode" />
</el-select>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable size="small">
<el-option v-for="warehouse in warehouseOptions" :key="warehouse.id" :label="warehouse.warehouseName" :value="warehouse.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">-->
<!-- <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['inventory:info:add']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['inventory:info:edit']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['inventory:info:remove']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['inventory:info:export']"></el-button>-->
<!-- </el-col>-->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="infoList" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="产品编码" align="center" prop="productCode" show-overflow-tooltip />
<el-table-column label="产品型号" align="center" prop="model" show-overflow-tooltip />
<el-table-column label="产品名称" align="center" prop="productName" show-overflow-tooltip />
<el-table-column label="仓库" align="center" prop="warehouseName" />
<el-table-column label="实时库存" align="center" prop="availableCount" sortable="custom" >
<template slot-scope="scope">
<span :style="getStockColor(scope.row.availableCount)">{{ scope.row.availableCount || scope.row.inventoryCount || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="累计发货" align="center" prop="cumulativeCount">
<template slot-scope="scope">
<span>{{ scope.row.cumulativeCount || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="制造商" align="center" prop="vendorName" show-overflow-tooltip/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-document" @click="handleInnerLog(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-document" @click="handleOuterLog(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 新增/修改对话框 -->
<el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="产品编码" prop="productCode">
<el-input v-model="form.productCode" placeholder="请输入产品编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品序列号" prop="productSn">
<el-input v-model="form.productSn" placeholder="请输入产品序列号" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="入库单号" prop="innerCode">
<el-input v-model="form.innerCode" placeholder="请输入入库单号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库单号" prop="outerCode">
<el-input v-model="form.outerCode" placeholder="请输入出库单号" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="所属仓库" prop="warehouseId">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" style="width:100%">
<el-option v-for="warehouse in warehouseOptions" :key="warehouse.id" :label="warehouse.warehouseName" :value="warehouse.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="制造商" prop="vendorCode">
<el-select v-model="form.vendorCode" placeholder="请选择制造商" style="width:100%">
<el-option v-for="vendor in vendorOptions" :key="vendor.vendorCode" :label="vendor.vendorName" :value="vendor.vendorCode" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="入库价" prop="innerPrice">
<el-input-number v-model="form.innerPrice" :precision="2" :step="0.1" placeholder="请输入入库价" style="width:100%"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库价" prop="outerPrice">
<el-input-number v-model="form.outerPrice" :precision="2" :step="0.1" placeholder="请输入出库价" style="width:100%"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 详情抽屉 -->
<el-drawer :title="title" :visible.sync="drawerVisible" direction="rtl" size="50%">
<div class="detail-container">
<el-form ref="formDetail" :model="form" label-width="100px" disabled>
<el-row>
<el-col :span="12">
<el-form-item label="产品编码" prop="productCode">
<el-input v-model="form.productCode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品名称" prop="productName">
<el-input v-model="form.productName" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="产品型号" prop="model">
<el-input v-model="form.model" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品序列号" prop="productSn">
<el-input v-model="form.productSn" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="所属仓库" prop="warehouseId">
<el-select v-model="form.warehouseId" style="width:100%">
<el-option v-for="warehouse in warehouseOptions" :key="warehouse.id" :label="warehouse.warehouseName" :value="warehouse.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="制造商" prop="vendorCode">
<el-select v-model="form.vendorCode" style="width:100%">
<el-option v-for="vendor in vendorOptions" :key="vendor.vendorCode" :label="vendor.vendorName" :value="vendor.vendorCode" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="入库价" prop="innerPrice">
<el-input-number v-model="form.innerPrice" :precision="2" style="width:100%"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出库价" prop="outerPrice">
<el-input-number v-model="form.outerPrice" :precision="2" style="width:100%"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</el-drawer>
<!-- 入库记录对话框 -->
<el-dialog title="入库记录" :visible.sync="innerLogVisible" width="70%" append-to-body>
<inner-log v-if="innerLogVisible" :product-code="currentLogProductCode" :warehouse-id="currentLogWarehouseId" />
</el-dialog>
<!-- 出库记录对话框 -->
<el-dialog title="出库记录" :visible.sync="outerLogVisible" width="70%" append-to-body>
<outer-log v-if="outerLogVisible" :product-code="currentLogProductCode" :warehouse-id="currentLogWarehouseId" />
</el-dialog>
</div>
</template>
<script>
import { listInfo, getInfo, addInfo, updateInfo, delInfo, listAllVendors, listAllWarehouses } from "@/api/inventory/info";
import InnerLog from './innerLog';
import OuterLog from './outerLog';
export default {
name: "InventoryInfo",
components: {
InnerLog,
OuterLog
},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
infoList: [],
//
title: "",
//
open: false,
//
drawerVisible: false,
//
vendorOptions: [],
//
warehouseOptions: [],
//
innerLogVisible: false,
outerLogVisible: false,
currentLogProductCode: null,
currentLogWarehouseId: null,
//
queryParams: {
pageNum: 1,
pageSize: 10,
productCode: null,
model: null,
productName: null,
vendorCode: null,
warehouseId: null,
},
//
form: {},
//
rules: {
productCode: [
{ required: true, message: "产品编码不能为空", trigger: "blur" }
],
warehouseId: [
{ required: true, message: "所属仓库不能为空", trigger: "change" }
],
}
};
},
created() {
this.getList();
this.getOptions();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
listInfo(this.queryParams).then(response => {
this.infoList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleSortChange(column) {
this.queryParams.orderByColumn = column.prop;
if (column.order === 'ascending') {
this.queryParams.isAsc = 'asc';
} else if (column.order === 'descending') {
this.queryParams.isAsc = 'desc';
} else {
this.queryParams.isAsc = null;
this.queryParams.orderByColumn = null;
}
this.getList();
},
/** 获取下拉框选项 */
getOptions() {
listAllVendors().then(response => {
this.vendorOptions = response.data;
});
listAllWarehouses().then(response => {
this.warehouseOptions = response.data;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
productCode: null,
productSn: null,
innerCode: null,
outerCode: null,
warehouseId: null,
vendorCode: null,
innerPrice: 0.00,
outerPrice: 0.00,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加产品库存";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids[0];
getInfo(id).then(response => {
this.form = response.data;
if (this.form.warehouseId) {
this.form.warehouseId = Number(this.form.warehouseId);
}
this.open = true;
this.title = "修改产品库存";
});
},
/** 详情按钮操作 */
handleView(row) {
this.reset();
const id = row.id;
getInfo(id).then(response => {
this.form = response.data;
if (this.form.warehouseId) {
this.form.warehouseId = Number(this.form.warehouseId);
}
this.drawerVisible = true;
this.title = "库存详情";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateInfo(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addInfo(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除产品库存ID为"' + ids + '"的数据项?').then(function() {
return delInfo(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有产品库存数据项?').then(() => {
this.exportLoading = true;
return this.download('inventory/info/vue/export', {
...queryParams
}, `inventory_info_${new Date().getTime()}.xlsx`)
}).then(response => {
this.exportLoading = false;
}).catch(() => {});
},
/** 库存颜色 */
getStockColor(stock) {
const numStock = Number(stock) || 0;
if (numStock > 200) {
return { color: '#1ab394' }; // Green
} else if (numStock <= 0) {
return { color: 'red' };
} else {
return { color: '#f59a4d' }; // Orange
}
},
/** 处理入库记录 */
handleInnerLog(row) {
if (!row.warehouseId) {
this.$modal.msgError("仓库信息不存在,无法查看记录");
return;
}
this.currentLogProductCode = row.productCode;
this.currentLogWarehouseId = row.warehouseId;
this.innerLogVisible = true;
},
/** 处理出库记录 */
handleOuterLog(row) {
if (!row.warehouseId) {
this.$modal.msgError("仓库信息不存在,无法查看记录");
return;
}
this.currentLogProductCode = row.productCode;
this.currentLogWarehouseId = row.warehouseId;
this.outerLogVisible = true;
}
}
};</script>
<style scoped>
.detail-container {
padding: 20px;
}
</style>

View File

@ -0,0 +1,83 @@
<template>
<div>
<el-table v-loading="loading" :data="logList">
<el-table-column label="入库单号" align="center" prop="innerCode" />
<el-table-column label="产品编码" align="center" prop="productCode" />
<el-table-column label="产品型号" align="center" prop="model" />
<el-table-column label="入库数量" align="center" prop="quantity" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<el-table-column label="入库仓" align="center" prop="warehouseName" />
<el-table-column label="经办人" align="center" prop="createByName" />
<el-table-column label="入库时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="fetchLog" />
</div>
</template>
<script>
import { getInnerLog } from "@/api/inventory/info";
export default {
name: "InnerLog",
props: {
productCode: {
type: String,
required: true
},
warehouseId: {
type: [String, Number],
required: true
}
},
data() {
return {
loading: true,
logList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
productCode: null,
warehouseId: null,
}
};
},
watch: {
productCode: {
immediate: true,
handler(val) {
if (val) {
this.queryParams.productCode = val;
this.fetchLog();
}
}
},
warehouseId: {
immediate: true,
handler(val) {
if (val) {
this.queryParams.warehouseId = val;
this.fetchLog();
}
}
}
},
methods: {
fetchLog() {
if (!this.queryParams.productCode || !this.queryParams.warehouseId) {
return;
}
this.loading = true;
getInnerLog(this.queryParams).then(response => {
this.logList = response.rows;
this.total = response.total;
this.loading = false;
});
}
}
};
</script>

View File

@ -0,0 +1,92 @@
<template>
<div>
<el-table v-loading="loading" :data="logList">
<el-table-column label="出库单号" align="center" prop="outerCode" />
<el-table-column label="合同编号" align="center" prop="orderCode" />
<el-table-column label="项目名称" align="center" prop="projectName" show-overflow-tooltip />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="出库状态" align="center" prop="outerStatus">
<template slot-scope="scope">
<dict-tag :options="outerStatusOptions" :value="scope.row.outerStatus"/>
</template>
</el-table-column>
<el-table-column label="经办人" align="center" prop="createByName" />
<el-table-column label="出库时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="fetchLog" />
</div>
</template>
<script>
import { getOuterLog } from "@/api/inventory/info";
export default {
name: "OuterLog",
props: {
productCode: {
type: String,
required: true
},
warehouseId: {
type: [String, Number],
required: true
}
},
data() {
return {
loading: true,
logList: [],
total: 0,
outerStatusOptions: [],
queryParams: {
pageNum: 1,
pageSize: 10,
productCode: null,
warehouseId: null,
}
};
},
created() {
this.getDicts("outer_outer_status").then(response => {
this.outerStatusOptions = response.data;
});
},
watch: {
productCode: {
immediate: true,
handler(val) {
if (val) {
this.queryParams.productCode = val;
this.fetchLog();
}
}
},
warehouseId: {
immediate: true,
handler(val) {
if (val) {
this.queryParams.warehouseId = val;
this.fetchLog();
}
}
}
},
methods: {
fetchLog() {
if (!this.queryParams.productCode || !this.queryParams.warehouseId) {
return;
}
this.loading = true;
getOuterLog(this.queryParams).then(response => {
this.logList = response.rows;
this.total = response.total;
this.loading = false;
});
}
}
};
</script>

View File

@ -79,8 +79,8 @@
</template>
<script>
import { addDelivery, importSnData, listProductSn } from '@/api/inventory/delivery';
import { addDelivery, listProductSn } from '@/api/inventory/delivery';
import {importSnData} from '@/api/inventory/outer'
export default {
name: "GenerateDeliveryForm",
dicts: ['delivery_type', 'logistics_company'],
@ -148,7 +148,7 @@ export default {
});
},
handleSelectionChange(selection) {
if (this.isImported) {
if (this.isImported && selection.length!=(this.productData.quantity - this.productData.deliveryGenerateQuantity - this.productData.deliveryConfirmQuantity)) {
this.$message.error("导入数据不允许取消勾选");
this.$refs.snTable.toggleAllSelection();
return;
@ -172,12 +172,12 @@ export default {
importSnData(formData).then(response => {
this.$message.success('导入成功');
this.isImported = true;
this.snList = response.data.productSnDataList;
this.queryParams.warehouseId = response.data.warehouseId;
this.total = this.snList.length;
this.queryParams.pageNum = 1;
this.queryParams.pageSize = this.total;
this.isImported = true;
this.$nextTick(() => {
if (this.$refs.snTable) {
this.$refs.snTable.toggleAllSelection();

View File

@ -78,6 +78,8 @@
<!-- 发货弹窗 -->
<generate-delivery-form ref="deliveryForm" @success="refreshTables"></generate-delivery-form>
<!-- 查看详情弹窗 -->
<delivery-detail :delivery-id="deliveryId" :visible.sync="viewOpen" />
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel"> </el-button>
@ -87,12 +89,13 @@
<script>
import { getOuter, queryInfo } from '@/api/inventory/outer';
import { removeDelivery, updateDeliveryStatus, getDelivery } from '@/api/inventory/delivery';
import { removeDelivery, updateDeliveryStatus } from '@/api/inventory/delivery';
import GenerateDeliveryForm from './GenerateDeliveryForm.vue';
import DeliveryDetail from '@/views/inventory/delivery/Detail.vue';
export default {
name: "OuterForm",
components: { GenerateDeliveryForm },
components: { GenerateDeliveryForm, DeliveryDetail },
data() {
return {
visible: false,
@ -100,16 +103,19 @@ export default {
form: {},
productList: [],
deliveryList: [],
deliveryId: null,
viewOpen: false,
};
},
methods: {
open(id) {
this.reset();
this.visible = true;
if (id) {
getOuter(id).then(response => {
this.form = response.data;
this.productList = response.data.productList || [];
this.visible = true;
this.form = response.data.inventoryOuter;
this.productList = response.data.productVoList || [];
this.deliveryList = response.data.deliveryList || [];
});
}
@ -122,11 +128,13 @@ export default {
this.form = {};
this.productList = [];
this.deliveryList = [];
this.deliveryId = null;
this.viewOpen = false;
},
//
refreshTables() {
queryInfo(this.form.id).then(res => {
this.productList = res.data.productList || [];
this.productList = res.data.productVoList || [];
this.deliveryList = res.data.deliveryList || [];
});
},
@ -168,10 +176,8 @@ export default {
},
//
handleViewDelivery(deliveryId) {
// This could open another component for viewing, for now, we just log it.
// Or maybe reuse the GenerateDeliveryForm in a read-only mode.
console.log("TODO: View delivery details:", deliveryId);
this.$router.push({ path: '/inventory/delivery/view/' + deliveryId });
this.deliveryId = deliveryId;
this.viewOpen = true;
}
}
};

View File

@ -56,7 +56,7 @@
<el-table-column label="生成时间" prop="createTime" width="180" align="center"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="220">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
<!-- <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>-->
<template v-if="scope.row.outerStatus === '3'">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['inventory:outer:edit']"></el-button>
</template>

View File

@ -0,0 +1,148 @@
package com.ruoyi.sip.controller.vue;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.InventoryDelivery;
import com.ruoyi.sip.domain.OmsInventoryDeliveryDetail;
import com.ruoyi.sip.dto.inventory.InventoryDeliveryDetailExcelDto;
import com.ruoyi.sip.service.IInventoryAuthService;
import com.ruoyi.sip.service.IInventoryDeliveryService;
import com.ruoyi.sip.service.IOmsInventoryDeliveryDetailService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import cn.hutool.core.collection.CollUtil;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* (Vue)Controller
*
* @author ruoyi
* @date 2025-11-18
*/
@RestController
@RequestMapping("/inventory/delivery/vue")
public class VueDeliveryController extends BaseController {
@Autowired
private IInventoryDeliveryService inventoryDeliveryService;
@Autowired
private IOmsInventoryDeliveryDetailService deliveryDetailService;
@Autowired
private IInventoryAuthService inventoryAuthService;
/**
*
*/
@RequiresPermissions("inventory:delivery:list")
@GetMapping("/list")
public TableDataInfo list(InventoryDelivery inventoryDelivery) {
inventoryDelivery.setDeliveryStatus(InventoryDelivery.DeliveryStatusEnum.CONFIRM_DELIVERY.getCode());
if (!inventoryAuthService.authAll()) {
List<String> productCodeList = inventoryAuthService.authProductCode();
if (CollUtil.isEmpty(productCodeList)) {
return getDataTable(Collections.emptyList());
}
inventoryDelivery.setProductCodeList(productCodeList);
}
startPage();
List<InventoryDelivery> list = inventoryDeliveryService.selectInventoryDeliveryList(inventoryDelivery);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("inventory:delivery:list")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
InventoryDelivery inventoryDelivery = inventoryDeliveryService.selectInventoryDeliveryById(id);
List<String> productSnList = deliveryDetailService.selectOmsInventoryDeliveryDetailByDeliveryId(id)
.stream()
.map(OmsInventoryDeliveryDetail::getProductSn)
.collect(Collectors.toList());
inventoryDelivery.setProductSnList(productSnList);
return AjaxResult.success(inventoryDelivery);
}
/**
*
*/
@RequiresPermissions("inventory:delivery:add")
@Log(title = "发货记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody InventoryDelivery inventoryDelivery) {
return toAjax(inventoryDeliveryService.insertInventoryDelivery(inventoryDelivery));
}
/**
*
*/
@RequiresPermissions("inventory:delivery:edit")
@Log(title = "发货记录", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody InventoryDelivery inventoryDelivery) {
return toAjax(inventoryDeliveryService.updateInventoryDelivery(inventoryDelivery));
}
/**
*
*/
@RequiresPermissions("inventory:delivery:remove")
@Log(title = "发货记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{id}")
public AjaxResult remove(@PathVariable(value = "id") Long id) {
return toAjax(inventoryDeliveryService.deleteInventoryOuterById(id));
}
/**
*
*/
@RequiresPermissions("inventory:delivery:recall")
@Log(title = "发货记录", businessType = BusinessType.UPDATE)
@PostMapping("/recall")
public AjaxResult recall(@RequestBody InventoryDelivery delivery) {
inventoryDeliveryService.recall(delivery.getId());
return AjaxResult.success();
}
/**
*
*/
@RequiresPermissions("inventory:delivery:export")
@Log(title = "发货记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public AjaxResult export(@RequestBody InventoryDelivery inventoryDelivery) {
List<InventoryDelivery> list = inventoryDeliveryService.selectInventoryDeliveryList(inventoryDelivery);
ExcelUtil<InventoryDelivery> util = new ExcelUtil<>(InventoryDelivery.class);
return util.exportExcel(list, "发货记录数据");
}
@Log(title = "发货记录", businessType = BusinessType.UPDATE)
@PutMapping("/status")
@ResponseBody
public AjaxResult status(@RequestBody InventoryDelivery inventoryDelivery)
{
inventoryDeliveryService.statusUpdate(inventoryDelivery);
return AjaxResult.success();
}
/**
*
*/
@RequiresPermissions("inventory:delivery:export")
@Log(title = "发货记录", businessType = BusinessType.EXPORT)
@PostMapping("/detail/export")
public AjaxResult detailExport(@RequestBody InventoryDelivery inventoryDelivery) {
List<InventoryDeliveryDetailExcelDto> list = inventoryDeliveryService.detailExport(inventoryDelivery);
ExcelUtil<InventoryDeliveryDetailExcelDto> util = new ExcelUtil<>(InventoryDeliveryDetailExcelDto.class);
return util.exportExcel(list, "发货详情记录数据");
}
}

View File

@ -0,0 +1,196 @@
package com.ruoyi.sip.controller.vue;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.InventoryInfo;
import com.ruoyi.sip.domain.OmsWarehouseInfo;
import com.ruoyi.sip.domain.ProductInfo;
import com.ruoyi.sip.service.IInventoryAuthService;
import com.ruoyi.sip.service.IInventoryInfoService;
import com.ruoyi.sip.service.IOmsWarehouseInfoService;
import com.ruoyi.sip.service.IProductInfoService;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.sip.domain.OmsInventoryInner;
import com.ruoyi.sip.service.IOmsInventoryInnerService;
import com.ruoyi.sip.domain.InventoryOuter;
import com.ruoyi.sip.service.IInventoryOuterService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* (Vue) Controller
*
* @author ch
* @date 2025-08-07
*/
@RestController
@RequestMapping("/inventory/info/vue")
public class VueInventoryInfoController extends BaseController {
@Autowired
private IInventoryInfoService inventoryInfoService;
@Autowired
private IProductInfoService productInfoService;
@Autowired
private IInventoryAuthService inventoryAuthService;
@Autowired
private IOmsWarehouseInfoService warehouseInfoService;
@Autowired
private IOmsInventoryInnerService omsInventoryInnerService;
@Autowired
private IInventoryOuterService inventoryOuterService;
/**
*
*/
@RequiresPermissions("inventory:info:list")
@GetMapping("/group/list")
public TableDataInfo list(ProductInfo info) {
if (!inventoryAuthService.authAll()) {
List<String> productCodeList = inventoryAuthService.authProductCode();
if (CollUtil.isEmpty(productCodeList)) {
return getDataTable(Collections.emptyList());
}
info.setProductCodeList(productCodeList);
}
startPage();
List<ProductInfo> list = productInfoService.listInventory(info);
return getDataTable(list);
}
@GetMapping("/list")
@ResponseBody
public TableDataInfo list(InventoryInfo inventoryInfo)
{
if (!inventoryAuthService.authAll()) {
List<String> productCodeList = inventoryAuthService.authProductCode();
if (CollUtil.isEmpty(productCodeList)) {
return getDataTable(Collections.emptyList());
}
inventoryInfo.setProductCodeList(productCodeList);
}
startPage();
List<InventoryInfo> list = inventoryInfoService.selectInventoryInfoList(inventoryInfo);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("inventory:info:query")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return AjaxResult.success(inventoryInfoService.selectInventoryInfoById(id));
}
/**
*
*/
@RequiresPermissions("inventory:info:add")
@Log(title = "产品库存", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody InventoryInfo inventoryInfo) {
return toAjax(inventoryInfoService.insertInventoryInfo(inventoryInfo));
}
/**
*
*/
@RequiresPermissions("inventory:info:edit")
@Log(title = "产品库存", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody InventoryInfo inventoryInfo) {
return toAjax(inventoryInfoService.updateInventoryInfo(inventoryInfo));
}
/**
*
*/
@RequiresPermissions("inventory:info:remove")
@Log(title = "产品库存", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String ids) {
return toAjax(inventoryInfoService.deleteInventoryInfoByIds(ids));
}
/**
*
*/
@RequiresPermissions("inventory:info:export")
@Log(title = "产品库存", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public AjaxResult export(@RequestBody ProductInfo info) {
if (!inventoryAuthService.authAll()) {
List<String> productCodeList = inventoryAuthService.authProductCode();
if (CollUtil.isEmpty(productCodeList)) {
List<ProductInfo> emptyList = Collections.emptyList();
ExcelUtil<ProductInfo> util = new ExcelUtil<>(ProductInfo.class);
return util.exportExcel(emptyList, "产品库存数据");
}
info.setProductCodeList(productCodeList);
}
List<ProductInfo> list = productInfoService.listInventory(info);
ExcelUtil<ProductInfo> util = new ExcelUtil<>(ProductInfo.class);
return util.exportExcel(list, "产品库存数据");
}
/**
*
*/
@GetMapping("/listAllVendors")
public AjaxResult listAllVendors() {
return AjaxResult.success(inventoryAuthService.currentVendor());
}
/**
*
*/
@GetMapping("/listAllWarehouses")
public AjaxResult listAllWarehouses() {
OmsWarehouseInfo queryWarehouseParam = new OmsWarehouseInfo();
queryWarehouseParam.setWarehouseStatus(OmsWarehouseInfo.WarehouseStatusEnum.NORMAL.getValue());
return AjaxResult.success(warehouseInfoService.selectOmsWarehouseInfoList(queryWarehouseParam));
}
/**
*
*/
@GetMapping("/inner-log")
public TableDataInfo getInnerLog(OmsInventoryInner omsInventoryInner) {
startPage();
List<OmsInventoryInner> list = omsInventoryInnerService.selectOmsInventoryInnerList(omsInventoryInner);
return getDataTable(list);
}
/**
*
*/
@GetMapping("/outer-log")
public TableDataInfo getOuterLog(InventoryOuter inventoryOuter) {
inventoryOuter.setOuterStatusList(Arrays.asList(InventoryOuter.OuterStatusEnum.WAIT_RECEIVE.getCode(), InventoryOuter.OuterStatusEnum.RECEIVED.getCode()));
if (!inventoryAuthService.authAll()){
List<String> productCodeList = inventoryAuthService.authProductCode();
if (CollUtil.isEmpty(productCodeList)){
return getDataTable(Collections.emptyList());
}
inventoryOuter.setProductCodeList(productCodeList);
}
startPage();
List<InventoryOuter> list = inventoryOuterService.selectInventoryOuterList(inventoryOuter);
return getDataTable(list);
}
}

View File

@ -4,13 +4,20 @@ import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.InventoryOuter;
import com.ruoyi.sip.dto.inventory.InventoryInfoExcelDto;
import com.ruoyi.sip.service.IInventoryAuthService;
import com.ruoyi.sip.service.IInventoryOuterService;
import com.ruoyi.sip.service.IOmsInventoryInnerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -28,6 +35,8 @@ public class VueInventoryOuterController extends BaseController
@Autowired
private IInventoryOuterService inventoryOuterService;
@Autowired
private IOmsInventoryInnerService innerService;
@Autowired
private IInventoryAuthService inventoryAuthService;
@GetMapping()
@ResponseBody
@ -83,6 +92,28 @@ public class VueInventoryOuterController extends BaseController
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(inventoryOuterService.selectInventoryOuterById(id));
return success(inventoryOuterService.selectBaseInfoById(id));
}
@PostMapping("/importData")
@ResponseBody
public AjaxResult importTemplate(@RequestPart("file") MultipartFile file, @RequestParam(value = "productCode") String productCode, @RequestParam(value = "quantity") Long quantity) {
ExcelUtil<InventoryInfoExcelDto> util = new ExcelUtil<InventoryInfoExcelDto>(InventoryInfoExcelDto.class);
try (InputStream inputStream = file.getInputStream()) {
List<InventoryInfoExcelDto> inventoryInfoExcelDtoList = util.importExcel(inputStream);
if (CollUtil.isEmpty(inventoryInfoExcelDtoList)){
return AjaxResult.error("导入数据为空");
}
if (inventoryInfoExcelDtoList.size()!=quantity.intValue()){
return AjaxResult.error("导入数据应等于发货数量");
}
return AjaxResult.success(innerService.getInventoryInfoList(inventoryInfoExcelDtoList, productCode));
} catch (IOException e) {
throw new ServiceException("读取excel错误,请联系管理员");
} catch (ServiceException e) {
return AjaxResult.error(e.getMessage());
}
}
}